From ca03288fa9c3137da3f3ccd70d6361a456850d68 Mon Sep 17 00:00:00 2001 From: Toon Verwerft Date: Wed, 21 Feb 2024 16:06:57 +0100 Subject: [PATCH 01/10] Add spec compliance --- src/Xml/Dom/Assert/assert_attribute.php | 8 +-- src/Xml/Dom/Assert/assert_cdata.php | 8 +-- src/Xml/Dom/Assert/assert_document.php | 8 +-- src/Xml/Dom/Assert/assert_dom_node_list.php | 8 +-- src/Xml/Dom/Assert/assert_element.php | 8 +-- src/Xml/Dom/Builder/Builder.php | 4 +- src/Xml/Dom/Builder/attribute.php | 6 +- src/Xml/Dom/Builder/attributes.php | 8 +-- src/Xml/Dom/Builder/cdata.php | 10 +-- src/Xml/Dom/Builder/children.php | 8 +-- src/Xml/Dom/Builder/element.php | 10 +-- src/Xml/Dom/Builder/escaped_value.php | 6 +- src/Xml/Dom/Builder/namespaced_attribute.php | 6 +- src/Xml/Dom/Builder/namespaced_attributes.php | 8 +-- src/Xml/Dom/Builder/namespaced_element.php | 10 +-- src/Xml/Dom/Builder/nodes.php | 18 +++--- src/Xml/Dom/Builder/value.php | 6 +- src/Xml/Dom/Builder/xmlns_attribute.php | 6 +- src/Xml/Dom/Builder/xmlns_attributes.php | 8 +-- src/Xml/Dom/Collection/NodeList.php | 50 +++++++-------- src/Xml/Dom/Configurator/Configurator.php | 4 +- src/Xml/Dom/Configurator/canonicalize.php | 18 ++---- src/Xml/Dom/Configurator/comparable.php | 4 +- src/Xml/Dom/Configurator/document_uri.php | 6 +- src/Xml/Dom/Configurator/loader.php | 8 +-- src/Xml/Dom/Configurator/normalize.php | 9 +-- .../Dom/Configurator/optimize_namespaces.php | 6 +- src/Xml/Dom/Configurator/pretty_print.php | 6 +- src/Xml/Dom/Configurator/traverse.php | 2 +- src/Xml/Dom/Configurator/trim_spaces.php | 6 +- src/Xml/Dom/Configurator/utf8.php | 6 +- src/Xml/Dom/Configurator/validator.php | 8 +-- src/Xml/Dom/Document.php | 62 +++++++++---------- src/Xml/Dom/Loader/Loader.php | 4 +- src/Xml/Dom/Loader/xml_file_loader.php | 6 +- src/Xml/Dom/Loader/xml_node_loader.php | 10 +-- src/Xml/Dom/Loader/xml_string_loader.php | 6 +- .../Dom/Locator/Attribute/attributes_list.php | 8 +-- .../Attribute/xmlns_attributes_list.php | 10 +-- src/Xml/Dom/Locator/Element/ancestors.php | 12 ++-- src/Xml/Dom/Locator/Element/children.php | 12 ++-- .../Element/locate_by_namespaced_tag_name.php | 6 +- .../Locator/Element/locate_by_tag_name.php | 6 +- .../Dom/Locator/Element/parent_element.php | 6 +- src/Xml/Dom/Locator/Element/siblings.php | 12 ++-- src/Xml/Dom/Locator/Node/children.php | 6 +- src/Xml/Dom/Locator/Node/detect_document.php | 8 +-- src/Xml/Dom/Locator/Node/value.php | 4 +- .../Dom/Locator/Xmlns/linked_namespaces.php | 10 +-- .../Xmlns/recursive_linked_namespaces.php | 10 +-- .../Locator/Xsd/locate_all_xsd_schemas.php | 4 +- .../Xsd/locate_namespaced_xsd_schemas.php | 4 +- .../Xsd/locate_no_namespaced_xsd_schemas.php | 4 +- src/Xml/Dom/Locator/document_element.php | 8 +-- .../elements_with_namespaced_tagname.php | 10 +-- src/Xml/Dom/Locator/elements_with_tagname.php | 10 +-- src/Xml/Dom/Locator/root_namespace.php | 6 +- src/Xml/Dom/Manipulator/Attribute/rename.php | 4 +- .../Document/optimize_namespaces.php | 10 +-- .../Element/copy_named_xmlns_attributes.php | 8 +-- src/Xml/Dom/Manipulator/Element/rename.php | 12 ++-- .../Manipulator/Node/append_external_node.php | 4 +- .../Manipulator/Node/import_node_deeply.php | 6 +- src/Xml/Dom/Manipulator/Node/remove.php | 6 +- .../Dom/Manipulator/Node/remove_namespace.php | 8 +-- src/Xml/Dom/Manipulator/Node/rename.php | 4 +- .../Node/replace_by_external_node.php | 6 +- .../Node/replace_by_external_nodes.php | 12 ++-- src/Xml/Dom/Manipulator/Xmlns/rename.php | 24 +++---- src/Xml/Dom/Manipulator/append.php | 8 +-- src/Xml/Dom/Mapper/Mapper.php | 4 +- src/Xml/Dom/Mapper/xml_string.php | 6 +- src/Xml/Dom/Mapper/xslt_template.php | 4 +- src/Xml/Dom/Predicate/is_attribute.php | 10 +-- src/Xml/Dom/Predicate/is_cdata.php | 10 +-- .../Predicate/is_default_xmlns_attribute.php | 6 +- src/Xml/Dom/Predicate/is_document.php | 10 +-- src/Xml/Dom/Predicate/is_document_element.php | 6 +- src/Xml/Dom/Predicate/is_element.php | 10 +-- src/Xml/Dom/Predicate/is_non_empty_text.php | 4 +- src/Xml/Dom/Predicate/is_text.php | 10 +-- src/Xml/Dom/Predicate/is_whitespace.php | 4 +- src/Xml/Dom/Predicate/is_xmlns_attribute.php | 10 +-- src/Xml/Dom/Traverser/Action.php | 4 +- src/Xml/Dom/Traverser/Action/Noop.php | 4 +- src/Xml/Dom/Traverser/Action/RemoveNode.php | 4 +- src/Xml/Dom/Traverser/Action/RenameNode.php | 4 +- src/Xml/Dom/Traverser/Action/ReplaceNode.php | 6 +- src/Xml/Dom/Traverser/Traverser.php | 8 +-- src/Xml/Dom/Traverser/Visitor.php | 6 +- .../Dom/Traverser/Visitor/AbstractVisitor.php | 6 +- .../Traverser/Visitor/RemoveNamespaces.php | 18 +++--- .../Dom/Traverser/Visitor/SortAttributes.php | 10 +-- src/Xml/Dom/Validator/Validator.php | 4 +- .../Dom/Validator/internal_xsd_validator.php | 8 +-- src/Xml/Dom/Validator/validator_chain.php | 10 +-- src/Xml/Dom/Validator/xsd_validator.php | 6 +- src/Xml/Dom/Xpath.php | 16 ++--- .../Dom/Xpath/Configurator/Configurator.php | 4 +- .../Dom/Xpath/Configurator/all_functions.php | 6 +- src/Xml/Dom/Xpath/Configurator/functions.php | 6 +- src/Xml/Dom/Xpath/Configurator/namespaces.php | 6 +- .../Dom/Xpath/Configurator/php_namespace.php | 6 +- src/Xml/Dom/Xpath/Locator/Locator.php | 4 +- src/Xml/Dom/Xpath/Locator/evaluate.php | 10 +-- src/Xml/Dom/Xpath/Locator/query.php | 12 ++-- src/Xml/Dom/Xpath/Locator/query_single.php | 14 ++--- .../Internal/Decoder/Builder/attribute.php | 4 +- .../Internal/Decoder/Builder/attributes.php | 8 +-- .../Internal/Decoder/Builder/element.php | 4 +- .../Decoder/Builder/group_child_elements.php | 6 +- .../Decoder/Builder/grouped_children.php | 10 +-- .../Internal/Decoder/Builder/name.php | 4 +- .../Internal/Decoder/Builder/namespaces.php | 8 +-- .../Internal/Encoder/Builder/children.php | 6 +- .../Internal/Encoder/Builder/element.php | 8 +-- .../Internal/Encoder/Builder/parent_node.php | 6 +- .../Internal/Encoder/Builder/root.php | 6 +- src/Xml/Encoding/document_encode.php | 2 +- src/Xml/Encoding/element_decode.php | 6 +- src/Xml/Encoding/element_encode.php | 2 +- src/Xml/Encoding/typed.php | 4 +- src/Xml/Encoding/xml_decode.php | 2 +- src/Xml/Encoding/xml_encode.php | 2 +- src/Xml/Reader/MatchingNode.php | 2 +- tests/Xml/Dom/Assert/AssertCDataTest.php | 4 +- tests/Xml/Dom/Assert/AssertDocumentTest.php | 4 +- tests/Xml/Dom/Assert/AssertElementTest.php | 4 +- tests/Xml/Dom/Builder/AttributesTest.php | 5 +- tests/Xml/Dom/Builder/CdataTest.php | 13 ++-- tests/Xml/Dom/Builder/ChildrenTest.php | 9 +-- tests/Xml/Dom/Builder/ElementTest.php | 9 +-- tests/Xml/Dom/Builder/EscapedValueTest.php | 5 +- .../Dom/Builder/NamespacedAttributeTest.php | 5 +- .../Xml/Dom/Builder/NamespacedElementTest.php | 23 +++---- tests/Xml/Dom/Builder/NodesTest.php | 7 ++- tests/Xml/Dom/Builder/ValueTest.php | 5 +- tests/Xml/Dom/Builder/XmlnsAttributesTest.php | 5 +- tests/Xml/Dom/Collection/NodeListTest.php | 34 +++++----- .../Xml/Dom/Configurator/DocumentUriTest.php | 5 +- tests/Xml/Dom/Configurator/LoaderTest.php | 13 ++-- .../Xml/Dom/Configurator/PrettyPrintTest.php | 6 +- tests/Xml/Dom/Configurator/TraverseTest.php | 4 +- tests/Xml/Dom/Configurator/TrimSpacesTest.php | 6 +- tests/Xml/Dom/Configurator/Utf8Test.php | 6 +- tests/Xml/Dom/Configurator/ValidatorTest.php | 15 ++--- tests/Xml/Dom/DocumentTest.php | 21 +++---- tests/Xml/Dom/Loader/XmlFileLoaderTest.php | 10 +-- tests/Xml/Dom/Loader/XmlNodeLoaderTest.php | 15 +++-- tests/Xml/Dom/Loader/XmlStringLoaderTest.php | 8 +-- .../Locator/Attribute/AttributesListTest.php | 4 +- .../Attribute/XmlnsAttributesListTest.php | 4 +- .../Dom/Locator/Element/ParentElementTest.php | 2 +- .../Dom/Locator/Node/DetectDocumentTest.php | 6 +- .../Locator/Xmlns/LinkedNamespacesTest.php | 4 +- .../Xmlns/RecursiveLinkedNamespacesTest.php | 4 +- .../Dom/Locator/Xsd/LocateXsdSchemasTest.php | 14 ++--- .../Node/AppendExternalNodeTest.php | 30 +++++---- .../Manipulator/Node/ImportNodeDeeplyTest.php | 38 +++++------- tests/Xml/Dom/Manipulator/Node/RemoveTest.php | 2 +- tests/Xml/Dom/Manipulator/Node/RenameTest.php | 2 +- .../Node/ReplaceByExternalNodeTest.php | 35 +++++------ .../Node/ReplaceByExternalNodesTest.php | 59 ++++++++---------- tests/Xml/Dom/Mapper/XmlStringTest.php | 6 +- tests/Xml/Dom/Predicate/IsAttributeTest.php | 4 +- tests/Xml/Dom/Predicate/IsCDataTest.php | 4 +- .../Predicate/IsDefaultXmlnsAttributeTest.php | 6 +- .../Dom/Predicate/IsDocumentElementTest.php | 4 +- tests/Xml/Dom/Predicate/IsDocumentTest.php | 4 +- tests/Xml/Dom/Predicate/IsElementTest.php | 4 +- .../Xml/Dom/Predicate/IsNonEmptyTextTest.php | 4 +- tests/Xml/Dom/Predicate/IsTextTest.php | 4 +- tests/Xml/Dom/Predicate/IsWhitespaceTest.php | 4 +- .../Dom/Predicate/IsXmlnsAttributeTest.php | 6 +- tests/Xml/Dom/Traverser/TraverserTest.php | 14 ++--- .../Xml/Dom/Validator/ValidatorChainTest.php | 2 +- .../Xml/Dom/Xpath/Locator/QuerySingleTest.php | 6 +- 177 files changed, 753 insertions(+), 773 deletions(-) diff --git a/src/Xml/Dom/Assert/assert_attribute.php b/src/Xml/Dom/Assert/assert_attribute.php index 5742eeb..83234f1 100644 --- a/src/Xml/Dom/Assert/assert_attribute.php +++ b/src/Xml/Dom/Assert/assert_attribute.php @@ -4,15 +4,15 @@ namespace VeeWee\Xml\Dom\Assert; -use DOMAttr; +use \DOM\Attr; use Psl\Type\Exception\AssertException; use function Psl\Type\instance_of; /** - * @psalm-assert DOMElement $node + * @psalm-assert \DOM\Element $node * @throws AssertException */ -function assert_attribute(mixed $node): DOMAttr +function assert_attribute(mixed $node): \DOM\Attr { - return instance_of(DOMAttr::class)->assert($node); + return instance_of(\DOM\Attr::class)->assert($node); } diff --git a/src/Xml/Dom/Assert/assert_cdata.php b/src/Xml/Dom/Assert/assert_cdata.php index de96d00..21ca62a 100644 --- a/src/Xml/Dom/Assert/assert_cdata.php +++ b/src/Xml/Dom/Assert/assert_cdata.php @@ -4,15 +4,15 @@ namespace VeeWee\Xml\Dom\Assert; -use DOMCdataSection; +use \DOM\CdataSection; use Psl\Type\Exception\AssertException; use function Psl\Type\instance_of; /** - * @psalm-assert DOMCdataSection $node + * @psalm-assert \DOM\CdataSection $node * @throws AssertException */ -function assert_cdata(mixed $node): DOMCdataSection +function assert_cdata(mixed $node): \DOM\CdataSection { - return instance_of(DOMCdataSection::class)->assert($node); + return instance_of(\DOM\CdataSection::class)->assert($node); } diff --git a/src/Xml/Dom/Assert/assert_document.php b/src/Xml/Dom/Assert/assert_document.php index b3f09ef..ad53687 100644 --- a/src/Xml/Dom/Assert/assert_document.php +++ b/src/Xml/Dom/Assert/assert_document.php @@ -4,15 +4,15 @@ namespace VeeWee\Xml\Dom\Assert; -use DOMDocument; +use \DOM\XMLDocument; use Psl\Type\Exception\AssertException; use function Psl\Type\instance_of; /** - * @psalm-assert DOMDocument $node + * @psalm-assert \DOM\XMLDocument $node * @throws AssertException */ -function assert_document(mixed $node): DOMDocument +function assert_document(mixed $node): \DOM\XMLDocument { - return instance_of(DOMDocument::class)->assert($node); + return instance_of(\DOM\XMLDocument::class)->assert($node); } diff --git a/src/Xml/Dom/Assert/assert_dom_node_list.php b/src/Xml/Dom/Assert/assert_dom_node_list.php index 6287064..91ecfe6 100644 --- a/src/Xml/Dom/Assert/assert_dom_node_list.php +++ b/src/Xml/Dom/Assert/assert_dom_node_list.php @@ -4,15 +4,15 @@ namespace VeeWee\Xml\Dom\Assert; -use DOMNodeList; +use \DOM\NodeList; use Psl\Type\Exception\AssertException; use function Psl\Type\instance_of; /** - * @psalm-assert DOMNodeList $node + * @psalm-assert \DOM\NodeList $node * @throws AssertException */ -function assert_dom_node_list(mixed $node): DOMNodeList +function assert_dom_node_list(mixed $node): \DOM\NodeList { - return instance_of(DOMNodeList::class)->assert($node); + return instance_of(\DOM\NodeList::class)->assert($node); } diff --git a/src/Xml/Dom/Assert/assert_element.php b/src/Xml/Dom/Assert/assert_element.php index 2aec5ba..50e2a5b 100644 --- a/src/Xml/Dom/Assert/assert_element.php +++ b/src/Xml/Dom/Assert/assert_element.php @@ -4,15 +4,15 @@ namespace VeeWee\Xml\Dom\Assert; -use DOMElement; +use \DOM\Element; use Psl\Type\Exception\AssertException; use function Psl\Type\instance_of; /** - * @psalm-assert DOMElement $node + * @psalm-assert \DOM\Element $node * @throws AssertException */ -function assert_element(mixed $node): DOMElement +function assert_element(mixed $node): \DOM\Element { - return instance_of(DOMElement::class)->assert($node); + return instance_of(\DOM\Element::class)->assert($node); } diff --git a/src/Xml/Dom/Builder/Builder.php b/src/Xml/Dom/Builder/Builder.php index 904b161..06801d1 100644 --- a/src/Xml/Dom/Builder/Builder.php +++ b/src/Xml/Dom/Builder/Builder.php @@ -4,9 +4,9 @@ namespace VeeWee\Xml\Dom\Builder; -use DOMNode; +use \DOM\Node; interface Builder { - public function __invoke(DOMNode $node): DOMNode; + public function __invoke(\DOM\Node $node): \DOM\Node; } diff --git a/src/Xml/Dom/Builder/attribute.php b/src/Xml/Dom/Builder/attribute.php index dee76a3..d7c1a02 100644 --- a/src/Xml/Dom/Builder/attribute.php +++ b/src/Xml/Dom/Builder/attribute.php @@ -5,14 +5,14 @@ namespace VeeWee\Xml\Dom\Builder; use Closure; -use DOMElement; +use \DOM\Element; /** - * @return Closure(DOMElement): DOMElement + * @return Closure(\DOM\Element): \DOM\Element */ function attribute(string $name, string $value): Closure { - return static function (DOMElement $node) use ($name, $value): DOMElement { + return static function (\DOM\Element $node) use ($name, $value): \DOM\Element { $node->setAttribute($name, $value); return $node; diff --git a/src/Xml/Dom/Builder/attributes.php b/src/Xml/Dom/Builder/attributes.php index aaf9dad..e52f153 100644 --- a/src/Xml/Dom/Builder/attributes.php +++ b/src/Xml/Dom/Builder/attributes.php @@ -5,19 +5,19 @@ namespace VeeWee\Xml\Dom\Builder; use Closure; -use DOMElement; +use \DOM\Element; use function Psl\Iter\reduce_with_keys; /** * @param array $attributes - * @return Closure(DOMElement): DOMElement + * @return Closure(\DOM\Element): \DOM\Element */ function attributes(array $attributes): Closure { - return static function (DOMElement $node) use ($attributes): DOMElement { + return static function (\DOM\Element $node) use ($attributes): \DOM\Element { return reduce_with_keys( $attributes, - static fn (DOMElement $node, string $name, string $value) + static fn (\DOM\Element $node, string $name, string $value) => attribute($name, $value)($node), $node ); diff --git a/src/Xml/Dom/Builder/cdata.php b/src/Xml/Dom/Builder/cdata.php index c875906..bcfd39b 100644 --- a/src/Xml/Dom/Builder/cdata.php +++ b/src/Xml/Dom/Builder/cdata.php @@ -5,20 +5,20 @@ namespace VeeWee\Xml\Dom\Builder; use Closure; -use DOMCdataSection; -use DOMNode; +use \DOM\CdataSection; +use \DOM\Node; use function VeeWee\Xml\Dom\Assert\assert_cdata; use function VeeWee\Xml\Dom\Locator\Node\detect_document; use function VeeWee\Xml\Internal\configure; /** - * @param list $configurators + * @param list $configurators * - * @return Closure(DOMNode): DOMCdataSection + * @return Closure(\DOM\Node): \DOM\CdataSection */ function cdata(string $data, ...$configurators): Closure { - return static function (DOMNode $node) use ($data, $configurators): DOMCdataSection { + return static function (\DOM\Node $node) use ($data, $configurators): \DOM\CdataSection { $document = detect_document($node); return assert_cdata( diff --git a/src/Xml/Dom/Builder/children.php b/src/Xml/Dom/Builder/children.php index 335eaea..8d35131 100644 --- a/src/Xml/Dom/Builder/children.php +++ b/src/Xml/Dom/Builder/children.php @@ -5,18 +5,18 @@ namespace VeeWee\Xml\Dom\Builder; use Closure; -use DOMNode; +use \DOM\Node; /** - * @template T of DOMNode + * @template T of \DOM\Node * - * @param list $builders + * @param list $builders * * @return Closure(T): T */ function children(callable ...$builders): Closure { - return static function (DOMNode $node) use ($builders): DOMNode { + return static function (\DOM\Node $node) use ($builders): \DOM\Node { foreach ($builders as $builder) { $node->appendChild($builder($node)); } diff --git a/src/Xml/Dom/Builder/element.php b/src/Xml/Dom/Builder/element.php index 32fedad..0c8e98d 100644 --- a/src/Xml/Dom/Builder/element.php +++ b/src/Xml/Dom/Builder/element.php @@ -5,20 +5,20 @@ namespace VeeWee\Xml\Dom\Builder; use Closure; -use DOMElement; -use DOMNode; +use \DOM\Element; +use \DOM\Node; use function VeeWee\Xml\Dom\Assert\assert_element; use function VeeWee\Xml\Dom\Locator\Node\detect_document; use function VeeWee\Xml\Internal\configure; /** - * @param list $configurators + * @param list $configurators * - * @return Closure(DOMNode): DOMElement + * @return Closure(\DOM\Node): \DOM\Element */ function element(string $name, callable ...$configurators): Closure { - return static function (DOMNode $node) use ($name, $configurators): DOMElement { + return static function (\DOM\Node $node) use ($name, $configurators): \DOM\Element { $document = detect_document($node); return assert_element( diff --git a/src/Xml/Dom/Builder/escaped_value.php b/src/Xml/Dom/Builder/escaped_value.php index d2f91ac..2d63b83 100644 --- a/src/Xml/Dom/Builder/escaped_value.php +++ b/src/Xml/Dom/Builder/escaped_value.php @@ -5,14 +5,14 @@ namespace VeeWee\Xml\Dom\Builder; use Closure; -use DOMElement; +use \DOM\Element; /** - * @return Closure(DOMElement): DOMElement + * @return Closure(\DOM\Element): \DOM\Element */ function escaped_value(string $value): Closure { - return static function (DOMElement $node) use ($value): DOMElement { + return static function (\DOM\Element $node) use ($value): \DOM\Element { $node->nodeValue = htmlspecialchars($value, ENT_XML1|ENT_QUOTES); return $node; diff --git a/src/Xml/Dom/Builder/namespaced_attribute.php b/src/Xml/Dom/Builder/namespaced_attribute.php index bb2b65d..b4d6c6a 100644 --- a/src/Xml/Dom/Builder/namespaced_attribute.php +++ b/src/Xml/Dom/Builder/namespaced_attribute.php @@ -5,15 +5,15 @@ namespace VeeWee\Xml\Dom\Builder; use Closure; -use DOMElement; +use \DOM\Element; use function VeeWee\Xml\Assertion\assert_strict_prefixed_name; /** - * @return Closure(DOMElement): DOMElement + * @return Closure(\DOM\Element): \DOM\Element */ function namespaced_attribute(string $namespace, string $qualifiedName, string $value): Closure { - return static function (DOMElement $node) use ($namespace, $qualifiedName, $value): DOMElement { + return static function (\DOM\Element $node) use ($namespace, $qualifiedName, $value): \DOM\Element { assert_strict_prefixed_name($qualifiedName); $node->setAttributeNS($namespace, $qualifiedName, $value); diff --git a/src/Xml/Dom/Builder/namespaced_attributes.php b/src/Xml/Dom/Builder/namespaced_attributes.php index d607154..27b3364 100644 --- a/src/Xml/Dom/Builder/namespaced_attributes.php +++ b/src/Xml/Dom/Builder/namespaced_attributes.php @@ -5,19 +5,19 @@ namespace VeeWee\Xml\Dom\Builder; use Closure; -use DOMElement; +use \DOM\Element; use function Psl\Iter\reduce_with_keys; /** * @param array $attributes - * @return Closure(DOMElement): DOMElement + * @return Closure(\DOM\Element): \DOM\Element */ function namespaced_attributes(string $namespace, array $attributes): Closure { - return static function (DOMElement $node) use ($namespace, $attributes): DOMElement { + return static function (\DOM\Element $node) use ($namespace, $attributes): \DOM\Element { return reduce_with_keys( $attributes, - static fn (DOMElement $node, string $name, string $value) + static fn (\DOM\Element $node, string $name, string $value) => namespaced_attribute($namespace, $name, $value)($node), $node ); diff --git a/src/Xml/Dom/Builder/namespaced_element.php b/src/Xml/Dom/Builder/namespaced_element.php index 15dc351..ed22f65 100644 --- a/src/Xml/Dom/Builder/namespaced_element.php +++ b/src/Xml/Dom/Builder/namespaced_element.php @@ -5,20 +5,20 @@ namespace VeeWee\Xml\Dom\Builder; use Closure; -use DOMElement; -use DOMNode; +use \DOM\Element; +use \DOM\Node; use function VeeWee\Xml\Dom\Assert\assert_element; use function VeeWee\Xml\Dom\Locator\Node\detect_document; use function VeeWee\Xml\Internal\configure; /** - * @param list $configurators + * @param list $configurators * - * @return Closure(DOMNode): DOMElement + * @return Closure(\DOM\Node): \DOM\Element */ function namespaced_element(string $namespace, string $qualifiedName, callable ...$configurators): Closure { - return static function (DOMNode $node) use ($namespace, $qualifiedName, $configurators): DOMElement { + return static function (\DOM\Node $node) use ($namespace, $qualifiedName, $configurators): \DOM\Element { $document = detect_document($node); return assert_element( diff --git a/src/Xml/Dom/Builder/nodes.php b/src/Xml/Dom/Builder/nodes.php index 49ff522..bdc4097 100644 --- a/src/Xml/Dom/Builder/nodes.php +++ b/src/Xml/Dom/Builder/nodes.php @@ -5,30 +5,30 @@ namespace VeeWee\Xml\Dom\Builder; use Closure; -use DOMDocument; -use DOMNode; +use \DOM\XMLDocument; +use \DOM\Node; use function is_array; use function Psl\Iter\reduce; use function VeeWee\Xml\Dom\Locator\Node\detect_document; /** - * @param list|DOMNode)> $builders + * @param list|\DOM\Node)> $builders * - * @return Closure(DOMDocument): list + * @return Closure(\DOM\XMLDocument): list<\DOM\Node> */ function nodes(callable ... $builders): Closure { return /** - * @return list + * @return list<\DOM\Node> */ - static fn (DOMNode $node): array + static fn (\DOM\Node $node): array => reduce( $builders, /** - * @param list $builds - * @param callable(DOMDocument): (DOMNode|list) $builder - * @return list + * @param list<\DOM\Node> $builds + * @param callable(\DOM\XMLDocument): (\DOM\Node|list<\DOM\Node>) $builder + * @return list<\DOM\Node> */ static function (array $builds, callable $builder) use ($node): array { $result = $builder(detect_document($node)); diff --git a/src/Xml/Dom/Builder/value.php b/src/Xml/Dom/Builder/value.php index 28482d6..48c3eb5 100644 --- a/src/Xml/Dom/Builder/value.php +++ b/src/Xml/Dom/Builder/value.php @@ -5,14 +5,14 @@ namespace VeeWee\Xml\Dom\Builder; use Closure; -use DOMElement; +use \DOM\Element; /** - * @return Closure(DOMElement): DOMElement + * @return Closure(\DOM\Element): \DOM\Element */ function value(string $value): Closure { - return static function (DOMElement $node) use ($value): DOMElement { + return static function (\DOM\Element $node) use ($value): \DOM\Element { $node->nodeValue = $value; return $node; diff --git a/src/Xml/Dom/Builder/xmlns_attribute.php b/src/Xml/Dom/Builder/xmlns_attribute.php index e7ae6bd..6371364 100644 --- a/src/Xml/Dom/Builder/xmlns_attribute.php +++ b/src/Xml/Dom/Builder/xmlns_attribute.php @@ -5,16 +5,16 @@ namespace VeeWee\Xml\Dom\Builder; use Closure; -use DOMElement; +use \DOM\Element; use VeeWee\Xml\Xmlns\Xmlns; use function VeeWee\Xml\Assertion\assert_strict_prefixed_name; /** - * @return Closure(DOMElement): DOMElement + * @return Closure(\DOM\Element): \DOM\Element */ function xmlns_attribute(string $prefix, string $namespaceURI): Closure { - return static function (DOMElement $node) use ($prefix, $namespaceURI): DOMElement { + return static function (\DOM\Element $node) use ($prefix, $namespaceURI): \DOM\Element { $prefixed = 'xmlns:'.$prefix; assert_strict_prefixed_name($prefixed); diff --git a/src/Xml/Dom/Builder/xmlns_attributes.php b/src/Xml/Dom/Builder/xmlns_attributes.php index cbf6f4d..4634b2c 100644 --- a/src/Xml/Dom/Builder/xmlns_attributes.php +++ b/src/Xml/Dom/Builder/xmlns_attributes.php @@ -5,19 +5,19 @@ namespace VeeWee\Xml\Dom\Builder; use Closure; -use DOMElement; +use \DOM\Element; use function Psl\Iter\reduce_with_keys; /** * @param array $attributes - A map of namespace prefix with namespace URI - * @return Closure(DOMElement): DOMElement + * @return Closure(\DOM\Element): \DOM\Element */ function xmlns_attributes(array $attributes): Closure { - return static function (DOMElement $node) use ($attributes): DOMElement { + return static function (\DOM\Element $node) use ($attributes): \DOM\Element { return reduce_with_keys( $attributes, - static fn (DOMElement $node, string $name, string $value) + static fn (\DOM\Element $node, string $name, string $value) => xmlns_attribute($name, $value)($node), $node ); diff --git a/src/Xml/Dom/Collection/NodeList.php b/src/Xml/Dom/Collection/NodeList.php index e9cfd65..8ead96a 100644 --- a/src/Xml/Dom/Collection/NodeList.php +++ b/src/Xml/Dom/Collection/NodeList.php @@ -5,10 +5,10 @@ namespace VeeWee\Xml\Dom\Collection; use Countable; -use DOMElement; -use DOMNode; -use DOMNodeList; -use DOMXPath; +use \DOM\Element; +use \DOM\Node; +use \DOM\NodeList as DOMNodeList; +use DOMXpath as DOMXpath; use Generator; use InvalidArgumentException; use IteratorAggregate; @@ -29,7 +29,7 @@ use function VeeWee\Xml\Dom\Locator\Element\siblings; /** - * @template T of DOMNode + * @template T of \DOM\Node * @implements IteratorAggregate */ final class NodeList implements Countable, IteratorAggregate @@ -49,7 +49,7 @@ public function __construct(...$nodes) } /** - * @template X of DOMNode + * @template X of \DOM\Node * @return self * * @psalm-suppress InvalidReturnType, InvalidReturnStatement - It is empty alright! @@ -60,7 +60,7 @@ public static function empty(): self } /** - * @template X of DOMNode + * @template X of \DOM\Node * @param DOMNodeList $list * @return NodeList */ @@ -70,7 +70,7 @@ public static function fromDOMNodeList(DOMNodeList $list): self } /** - * @template X of DOMNode + * @template X of \DOM\Node * @param class-string $type * @return NodeList * @throws InvalidArgumentException @@ -136,7 +136,7 @@ public function forEach(callable $mapper): void } /** - * @template X of DOMNode + * @template X of \DOM\Node * @param callable(T): iterable $mapper * * @return NodeList @@ -184,32 +184,32 @@ public function reduce(callable $reducer, mixed $initial): mixed } /** - * @param list $configurators + * @param list $configurators * @throws RuntimeException - * @return NodeList + * @return NodeList<\DOM\Node> */ public function query(string $xpath, callable ... $configurators): self { return $this->detect( /** * @param T $node - * @return NodeList + * @return NodeList<\DOM\Node> */ - static fn (DOMNode $node): NodeList + static fn (\DOM\Node $node): NodeList => Xpath::fromUnsafeNode($node, ...$configurators)->query($xpath, $node) ); } /** * @template X - * @param list $configurators + * @param list $configurators * @param TypeInterface $type * @return list */ public function evaluate(string $expression, TypeInterface $type, callable ... $configurators): array { return $this->map( - static fn (DOMNode $node): mixed + static fn (\DOM\Node $node): mixed => Xpath::fromUnsafeNode($node, ...$configurators)->evaluate($expression, $type, $node) ); } @@ -263,46 +263,46 @@ public function expectLast(string $message = '') } /** - * @return NodeList + * @return NodeList<\DOM\Element> */ public function siblings(): self { return $this->detect( /** - * @return iterable + * @return iterable<\DOM\Element> */ - static fn (DOMNode $node): NodeList => siblings($node) + static fn (\DOM\Node $node): NodeList => siblings($node) ); } /** - * @return NodeList + * @return NodeList<\DOM\Element> */ public function ancestors(): self { return $this->detect( /** - * @return iterable + * @return iterable<\DOM\Element> */ - static fn (DOMNode $node): NodeList => ancestors($node) + static fn (\DOM\Node $node): NodeList => ancestors($node) ); } /** - * @return NodeList + * @return NodeList<\DOM\Element> */ public function children(): self { return $this->detect( /** - * @return iterable + * @return iterable<\DOM\Element> */ - static fn (DOMNode $node): NodeList => children($node) + static fn (\DOM\Node $node): NodeList => children($node) ); } /** - * @template X of DOMNode + * @template X of \DOM\Node * @param class-string $type * @return NodeList * @throws InvalidArgumentException diff --git a/src/Xml/Dom/Configurator/Configurator.php b/src/Xml/Dom/Configurator/Configurator.php index 6c734ad..253f2a2 100644 --- a/src/Xml/Dom/Configurator/Configurator.php +++ b/src/Xml/Dom/Configurator/Configurator.php @@ -4,9 +4,9 @@ namespace VeeWee\Xml\Dom\Configurator; -use DOMDocument; +use \DOM\XMLDocument; interface Configurator { - public function __invoke(DOMDocument $document): DOMDocument; + public function __invoke(\DOM\XMLDocument $document): \DOM\XMLDocument; } diff --git a/src/Xml/Dom/Configurator/canonicalize.php b/src/Xml/Dom/Configurator/canonicalize.php index 4ccb45e..42c9205 100644 --- a/src/Xml/Dom/Configurator/canonicalize.php +++ b/src/Xml/Dom/Configurator/canonicalize.php @@ -5,11 +5,9 @@ namespace VeeWee\Xml\Dom\Configurator; use Closure; -use DOMDocument; +use \DOM\XMLDocument as DOMDocument; use VeeWee\Xml\Dom\Document; use function Psl\Type\non_empty_string; -use function VeeWee\Xml\Dom\Loader\xml_string_loader; -use function VeeWee\Xml\ErrorHandling\disallow_libxml_false_returns; /** * @return Closure(DOMDocument): DOMDocument @@ -17,17 +15,9 @@ function canonicalize(): Closure { return static fn (DOMDocument $document): DOMDocument - => Document::configure( - pretty_print(), - loader( - xml_string_loader( - disallow_libxml_false_returns( - non_empty_string()->assert($document->C14N()), - 'Could not canonicalize XML input.' - ), - LIBXML_NSCLEAN + LIBXML_NOCDATA - ) - ), + => Document::fromXmlString( + non_empty_string()->assert($document->C14N()), // TODO : LIBXML_NSCLEAN + LIBXML_NOCDATA + // pretty_print() : TODO normalize() )->toUnsafeDocument(); } diff --git a/src/Xml/Dom/Configurator/comparable.php b/src/Xml/Dom/Configurator/comparable.php index c15bf47..07e4044 100644 --- a/src/Xml/Dom/Configurator/comparable.php +++ b/src/Xml/Dom/Configurator/comparable.php @@ -5,12 +5,12 @@ namespace VeeWee\Xml\Dom\Configurator; use Closure; -use DOMDocument; +use \DOM\XMLDocument; use VeeWee\Xml\Dom\Traverser\Visitor\SortAttributes; use function VeeWee\Xml\Internal\configure; /** - * @return Closure(DOMDocument): DOMDocument + * @return Closure(\DOM\XMLDocument): \DOM\XMLDocument */ function comparable(): Closure { diff --git a/src/Xml/Dom/Configurator/document_uri.php b/src/Xml/Dom/Configurator/document_uri.php index 27a20b3..7510866 100644 --- a/src/Xml/Dom/Configurator/document_uri.php +++ b/src/Xml/Dom/Configurator/document_uri.php @@ -5,15 +5,15 @@ namespace VeeWee\Xml\Dom\Configurator; use Closure; -use DOMDocument; +use \DOM\XMLDocument; /** * @param non-empty-string $documentUri - * @return Closure(DOMDocument): DOMDocument + * @return Closure(\DOM\XMLDocument): \DOM\XMLDocument */ function document_uri(string $documentUri): Closure { - return static function (DOMDocument $document) use ($documentUri) : DOMDocument { + return static function (\DOM\XMLDocument $document) use ($documentUri) : \DOM\XMLDocument { $document->documentURI = $documentUri; return $document; diff --git a/src/Xml/Dom/Configurator/loader.php b/src/Xml/Dom/Configurator/loader.php index ba469da..fe5bb56 100644 --- a/src/Xml/Dom/Configurator/loader.php +++ b/src/Xml/Dom/Configurator/loader.php @@ -5,16 +5,16 @@ namespace VeeWee\Xml\Dom\Configurator; use Closure; -use DOMDocument; +use \DOM\XMLDocument; /** - * @param callable(DOMDocument): void $loader + * @param callable(\DOM\XMLDocument): void $loader * - * @return Closure(DOMDocument): DOMDocument + * @return Closure(\DOM\XMLDocument): \DOM\XMLDocument */ function loader(callable $loader): Closure { - return static function (DOMDocument $document) use ($loader): DOMDocument { + return static function (\DOM\XMLDocument $document) use ($loader): \DOM\XMLDocument { $loader($document); return $document; }; diff --git a/src/Xml/Dom/Configurator/normalize.php b/src/Xml/Dom/Configurator/normalize.php index 502a985..81eee21 100644 --- a/src/Xml/Dom/Configurator/normalize.php +++ b/src/Xml/Dom/Configurator/normalize.php @@ -5,15 +5,16 @@ namespace VeeWee\Xml\Dom\Configurator; use Closure; -use DOMDocument; +use \DOM\XMLDocument; /** - * @return Closure(DOMDocument): DOMDocument + * @return Closure(\DOM\XMLDocument): \DOM\XMLDocument */ function normalize(): Closure { - return static function (DOMDocument $document): DOMDocument { - $document->normalizeDocument(); + return static function (\DOM\XMLDocument $document): \DOM\XMLDocument { + // TODO : does not exist anmyore + // $document->normalizeDocument(); return $document; }; diff --git a/src/Xml/Dom/Configurator/optimize_namespaces.php b/src/Xml/Dom/Configurator/optimize_namespaces.php index 49f2a88..312fc27 100644 --- a/src/Xml/Dom/Configurator/optimize_namespaces.php +++ b/src/Xml/Dom/Configurator/optimize_namespaces.php @@ -5,15 +5,15 @@ namespace VeeWee\Xml\Dom\Configurator; use Closure; -use DOMDocument; +use \DOM\XMLDocument; use function VeeWee\Xml\Dom\Manipulator\Document\optimize_namespaces as optimize_namespaces_manipulator; /** - * @return Closure(DOMDocument): DOMDocument + * @return Closure(\DOM\XMLDocument): \DOM\XMLDocument */ function optimize_namespaces(string $prefix = 'ns'): Closure { - return static function (DOMDocument $document) use ($prefix) : DOMDocument { + return static function (\DOM\XMLDocument $document) use ($prefix) : \DOM\XMLDocument { optimize_namespaces_manipulator($document, $prefix); return $document; diff --git a/src/Xml/Dom/Configurator/pretty_print.php b/src/Xml/Dom/Configurator/pretty_print.php index 9fd845f..a697700 100644 --- a/src/Xml/Dom/Configurator/pretty_print.php +++ b/src/Xml/Dom/Configurator/pretty_print.php @@ -5,14 +5,14 @@ namespace VeeWee\Xml\Dom\Configurator; use Closure; -use DOMDocument; +use \DOM\XMLDocument; /** - * @return Closure(DOMDocument): DOMDocument + * @return Closure(\DOM\XMLDocument): \DOM\XMLDocument */ function pretty_print(): Closure { - return static function (DOMDocument $document): DOMDocument { + return static function (\DOM\XMLDocument $document): \DOM\XMLDocument { $document->preserveWhiteSpace = false; $document->formatOutput = true; diff --git a/src/Xml/Dom/Configurator/traverse.php b/src/Xml/Dom/Configurator/traverse.php index 2416eac..032a433 100644 --- a/src/Xml/Dom/Configurator/traverse.php +++ b/src/Xml/Dom/Configurator/traverse.php @@ -5,7 +5,7 @@ namespace VeeWee\Xml\Dom\Configurator; use Closure; -use DOMDocument; +use \DOM\XMLDocument as DOMDocument; use VeeWee\Xml\Dom\Document; use VeeWee\Xml\Dom\Traverser\Visitor; diff --git a/src/Xml/Dom/Configurator/trim_spaces.php b/src/Xml/Dom/Configurator/trim_spaces.php index 48cfc9a..6009749 100644 --- a/src/Xml/Dom/Configurator/trim_spaces.php +++ b/src/Xml/Dom/Configurator/trim_spaces.php @@ -5,14 +5,14 @@ namespace VeeWee\Xml\Dom\Configurator; use Closure; -use DOMDocument; +use \DOM\XMLDocument; /** - * @return Closure(DOMDocument): DOMDocument + * @return Closure(\DOM\XMLDocument): \DOM\XMLDocument */ function trim_spaces(): Closure { - return static function (DOMDocument $document): DOMDocument { + return static function (\DOM\XMLDocument $document): \DOM\XMLDocument { $document->preserveWhiteSpace = false; $document->formatOutput = false; diff --git a/src/Xml/Dom/Configurator/utf8.php b/src/Xml/Dom/Configurator/utf8.php index 627feef..688aaf2 100644 --- a/src/Xml/Dom/Configurator/utf8.php +++ b/src/Xml/Dom/Configurator/utf8.php @@ -5,14 +5,14 @@ namespace VeeWee\Xml\Dom\Configurator; use Closure; -use DOMDocument; +use \DOM\XMLDocument; /** - * @return Closure(DOMDocument): DOMDocument + * @return Closure(\DOM\XMLDocument): \DOM\XMLDocument */ function utf8(): Closure { - return static function (DOMDocument $document): DOMDocument { + return static function (\DOM\XMLDocument $document): \DOM\XMLDocument { $document->encoding = 'UTF-8'; return $document; diff --git a/src/Xml/Dom/Configurator/validator.php b/src/Xml/Dom/Configurator/validator.php index b263ef8..af1b70e 100644 --- a/src/Xml/Dom/Configurator/validator.php +++ b/src/Xml/Dom/Configurator/validator.php @@ -5,16 +5,16 @@ namespace VeeWee\Xml\Dom\Configurator; use Closure; -use DOMDocument; +use \DOM\XMLDocument; use VeeWee\Xml\ErrorHandling\Issue\Issue; use VeeWee\Xml\ErrorHandling\Issue\IssueCollection; use VeeWee\Xml\ErrorHandling\Issue\Level; use VeeWee\Xml\Exception\RuntimeException; /** - * @param callable(DOMDocument): IssueCollection $validator + * @param callable(\DOM\XMLDocument): IssueCollection $validator * - * @return Closure(DOMDocument): DOMDocument + * @return Closure(\DOM\XMLDocument): \DOM\XMLDocument */ function validator(callable $validator, ?Level $minimumLevel = null): Closure { @@ -24,7 +24,7 @@ function validator(callable $validator, ?Level $minimumLevel = null): Closure /** * @throws RuntimeException */ - static function (DOMDocument $document) use ($validator, $minimumLevel): DOMDocument { + static function (\DOM\XMLDocument $document) use ($validator, $minimumLevel): \DOM\XMLDocument { $issues = $validator($document) ->filter(static fn (Issue $issue): bool => $issue->level()->value() >= $minimumLevel->value()); diff --git a/src/Xml/Dom/Document.php b/src/Xml/Dom/Document.php index 448d39f..61786d1 100644 --- a/src/Xml/Dom/Document.php +++ b/src/Xml/Dom/Document.php @@ -5,10 +5,10 @@ namespace VeeWee\Xml\Dom; use Closure; -use DOMDocument; -use DOMElement; -use DOMNode; -use DOMXPath; +use \DOM\XMLDocument; +use \DOM\Element; +use \DOM\Node; +use \DOM\XPath as DOMXPath; use VeeWee\Xml\Dom\Traverser\Traverser; use VeeWee\Xml\Dom\Traverser\Visitor; use VeeWee\Xml\ErrorHandling\Issue\IssueCollection; @@ -25,60 +25,60 @@ final class Document { private function __construct( - private DOMDocument $document + private XMLDocument $document ) { } public static function empty(): self { - return new self(new DOMDocument()); + return new self(XMLDocument::createEmpty()); } /** - * @param list $configurators + * @param list $configurators * * @throws RuntimeException */ public static function configure(callable ... $configurators): self { - $document = configure(...$configurators)(new DOMDocument()); + $document = configure(...$configurators)(XMLDocument::createEmpty()); return new self($document); } /** - * @param list $configurators + * @param list $configurators * * @throws RuntimeException */ public static function fromXmlFile(string $file, callable ...$configurators): self { - return self::configure( - loader(xml_file_loader($file)), - ...$configurators + return new self( + configure(...$configurators)(XMLDocument::createFromFile($file)) + // TODO : What with loader(xml_file_loader($file)) ); } /** * @param non-empty-string $xml - * @param list $configurators + * @param list $configurators * * @throws RuntimeException */ public static function fromXmlString(string $xml, callable ...$configurators): self { - return self::configure( - loader(xml_string_loader($xml)), - ...$configurators + return new self( + configure(...$configurators)(XMLDocument::createFromString($xml)) + // TODO : What with loader(xml_string_loader($file)) ); } /** - * @param list $configurators + * @param list $configurators * * @throws RuntimeException */ - public static function fromXmlNode(DOMNode $node, callable ...$configurators): self + public static function fromXmlNode(\DOM\Node $node, callable ...$configurators): self { return self::configure( loader(xml_node_loader($node)), @@ -87,25 +87,25 @@ public static function fromXmlNode(DOMNode $node, callable ...$configurators): s } /** - * @param list $configurators + * @param list $configurators * * @throws RuntimeException */ - public static function fromUnsafeDocument(DOMDocument $document, callable ...$configurators): self + public static function fromUnsafeDocument(XMLDocument $document, callable ...$configurators): self { return new self( configure(...$configurators)($document) ); } - public function toUnsafeDocument(): DOMDocument + public function toUnsafeDocument(): XMLDocument { return $this->document; } /** * @template T - * @param callable(DOMDocument): T $locator + * @param callable(XMLDocument): T $locator * * @return T */ @@ -114,13 +114,13 @@ public function locate(callable $locator) return $locator($this->document); } - public function locateDocumentElement(): DOMElement + public function locateDocumentElement(): \DOM\Element { return $this->locate(Locator\document_element()); } /** - * @param callable(DOMDocument): mixed $manipulator + * @param callable(XMLDocument): mixed $manipulator * * @return $this */ @@ -132,9 +132,9 @@ public function manipulate(callable $manipulator): self } /** - * @param list|DOMNode)> $builders + * @param list|\DOM\Node)> $builders * - * @return list + * @return list<\DOM\Node> */ public function build(callable ... $builders): array { @@ -145,7 +145,7 @@ public function build(callable ... $builders): array } /** - * @param callable(DOMDocument): IssueCollection $validator + * @param callable(XMLDocument): IssueCollection $validator */ public function validate(callable $validator): IssueCollection { @@ -162,7 +162,7 @@ public function xpath(callable ...$configurators): Xpath /** * @template T - * @param callable(DOMDocument): T $mapper + * @param callable(XMLDocument): T $mapper * * @return T */ @@ -172,7 +172,7 @@ public function map(callable $mapper) } /** - * @param list $configurators + * @param list $configurators * * @throws RuntimeException */ @@ -184,7 +184,7 @@ public function reconfigure(callable ... $configurators): self /** * @no-named-arguments */ - public function traverse(Visitor ... $visitors): DOMNode + public function traverse(Visitor ... $visitors): \DOM\Node { $traverser = new Traverser(...$visitors); return $traverser->traverse($this->map(document_element())); @@ -209,7 +209,7 @@ public function stringifyDocumentElement(): string /** * @return non-empty-string */ - public function stringifyNode(DOMNode $node): string + public function stringifyNode(\DOM\Node $node): string { return xml_string()($node); } diff --git a/src/Xml/Dom/Loader/Loader.php b/src/Xml/Dom/Loader/Loader.php index 8481318..98b1e5e 100644 --- a/src/Xml/Dom/Loader/Loader.php +++ b/src/Xml/Dom/Loader/Loader.php @@ -4,9 +4,9 @@ namespace VeeWee\Xml\Dom\Loader; -use DOMDocument; +use \DOM\XMLDocument; interface Loader { - public function __invoke(DOMDocument $document): void; + public function __invoke(\DOM\XMLDocument $document): void; } diff --git a/src/Xml/Dom/Loader/xml_file_loader.php b/src/Xml/Dom/Loader/xml_file_loader.php index ce76dab..a7126ca 100644 --- a/src/Xml/Dom/Loader/xml_file_loader.php +++ b/src/Xml/Dom/Loader/xml_file_loader.php @@ -5,16 +5,16 @@ namespace VeeWee\Xml\Dom\Loader; use Closure; -use DOMDocument; +use \DOM\XMLDocument; use Webmozart\Assert\Assert; /** * @param int $options - bitmask of LIBXML_* constants https://www.php.net/manual/en/libxml.constants.php - * @return Closure(DOMDocument): void + * @return Closure(\DOM\XMLDocument): void */ function xml_file_loader(string $file, int $options = 0): Closure { - return static function (DOMDocument $document) use ($file, $options): void { + return static function (\DOM\XMLDocument $document) use ($file, $options): void { load( static function () use ($document, $file, $options): bool { Assert::fileExists($file); diff --git a/src/Xml/Dom/Loader/xml_node_loader.php b/src/Xml/Dom/Loader/xml_node_loader.php index 01c254a..7a5f1ef 100644 --- a/src/Xml/Dom/Loader/xml_node_loader.php +++ b/src/Xml/Dom/Loader/xml_node_loader.php @@ -5,17 +5,17 @@ namespace VeeWee\Xml\Dom\Loader; use Closure; -use DOMDocument; +use \DOM\XMLDocument; -use DOMNode; +use \DOM\Node; use function VeeWee\Xml\Dom\Manipulator\Node\append_external_node; /** - * @return Closure(DOMDocument): void + * @return Closure(\DOM\XMLDocument): void */ -function xml_node_loader(DOMNode $importedNode): Closure +function xml_node_loader(\DOM\Node $importedNode): Closure { - return static function (DOMDocument $document) use ($importedNode): void { + return static function (\DOM\XMLDocument $document) use ($importedNode): void { load(static fn (): bool => (bool) append_external_node($document, $importedNode)); }; } diff --git a/src/Xml/Dom/Loader/xml_string_loader.php b/src/Xml/Dom/Loader/xml_string_loader.php index f80f183..8191be2 100644 --- a/src/Xml/Dom/Loader/xml_string_loader.php +++ b/src/Xml/Dom/Loader/xml_string_loader.php @@ -5,16 +5,16 @@ namespace VeeWee\Xml\Dom\Loader; use Closure; -use DOMDocument; +use \DOM\XMLDocument; /** * @param non-empty-string $xml * @param int $options - bitmask of LIBXML_* constants https://www.php.net/manual/en/libxml.constants.php - * @return Closure(DOMDocument): void + * @return Closure(\DOM\XMLDocument): void */ function xml_string_loader(string $xml, int $options = 0): Closure { - return static function (DOMDocument $document) use ($xml, $options): void { + return static function (\DOM\XMLDocument $document) use ($xml, $options): void { load(static fn (): bool => $document->loadXML($xml, $options)); }; } diff --git a/src/Xml/Dom/Locator/Attribute/attributes_list.php b/src/Xml/Dom/Locator/Attribute/attributes_list.php index 8538411..a8221ad 100644 --- a/src/Xml/Dom/Locator/Attribute/attributes_list.php +++ b/src/Xml/Dom/Locator/Attribute/attributes_list.php @@ -4,16 +4,16 @@ namespace VeeWee\Xml\Dom\Locator\Attribute; -use DOMAttr; -use DOMNode; +use \DOM\Attr; +use \DOM\Node; use VeeWee\Xml\Dom\Collection\NodeList; use function Psl\Vec\values; use function VeeWee\Xml\Dom\Predicate\is_element; /** - * @return NodeList + * @return NodeList<\DOM\Attr> */ -function attributes_list(DOMNode $node): NodeList +function attributes_list(\DOM\Node $node): NodeList { if (!is_element($node)) { return NodeList::empty(); diff --git a/src/Xml/Dom/Locator/Attribute/xmlns_attributes_list.php b/src/Xml/Dom/Locator/Attribute/xmlns_attributes_list.php index 9a5f51c..e82545b 100644 --- a/src/Xml/Dom/Locator/Attribute/xmlns_attributes_list.php +++ b/src/Xml/Dom/Locator/Attribute/xmlns_attributes_list.php @@ -4,23 +4,23 @@ namespace VeeWee\Xml\Dom\Locator\Attribute; -use DOMNameSpaceNode; -use DOMNode; +use \DOM\NameSpaceNode; +use \DOM\Node; use VeeWee\Xml\Dom\Collection\NodeList; use VeeWee\Xml\Exception\RuntimeException; use function VeeWee\Xml\Dom\Locator\Xmlns\linked_namespaces; use function VeeWee\Xml\Dom\Predicate\is_element; /** - * @return NodeList + * @return NodeList<\DOM\NameSpaceNode> * @throws RuntimeException */ -function xmlns_attributes_list(DOMNode $node): NodeList +function xmlns_attributes_list(\DOM\Node $node): NodeList { if (! is_element($node)) { return NodeList::empty(); } return linked_namespaces($node) - ->filter(static fn (DOMNameSpaceNode $namespace): bool => $node->hasAttribute($namespace->nodeName)); + ->filter(static fn (\DOM\NameSpaceNode $namespace): bool => $node->hasAttribute($namespace->nodeName)); } diff --git a/src/Xml/Dom/Locator/Element/ancestors.php b/src/Xml/Dom/Locator/Element/ancestors.php index 6510c5b..3787aa5 100644 --- a/src/Xml/Dom/Locator/Element/ancestors.php +++ b/src/Xml/Dom/Locator/Element/ancestors.php @@ -4,23 +4,23 @@ namespace VeeWee\Xml\Dom\Locator\Element; -use DOMElement; -use DOMNode; +use \DOM\Element; +use \DOM\Node; use Generator; use VeeWee\Xml\Dom\Collection\NodeList; use function VeeWee\Xml\Dom\Predicate\is_element; /** - * @return NodeList + * @return NodeList<\DOM\Element> */ -function ancestors(DOMNode $node): NodeList +function ancestors(\DOM\Node $node): NodeList { return new NodeList( ...( /** - * @return Generator + * @return Generator */ - static function (DOMNode $next) { + static function (\DOM\Node $next) { while (($parent = $next->parentNode) !== null) { if (is_element($parent)) { yield $parent; diff --git a/src/Xml/Dom/Locator/Element/children.php b/src/Xml/Dom/Locator/Element/children.php index 592824d..8b7a9fb 100644 --- a/src/Xml/Dom/Locator/Element/children.php +++ b/src/Xml/Dom/Locator/Element/children.php @@ -4,21 +4,21 @@ namespace VeeWee\Xml\Dom\Locator\Element; -use DOMElement; -use DOMNode; +use \DOM\Element; +use \DOM\Node; use VeeWee\Xml\Dom\Collection\NodeList; use function Psl\Vec\filter; use function VeeWee\Xml\Dom\Predicate\is_element; /** - * @return NodeList + * @return NodeList<\DOM\Element> */ -function children(DOMNode $node): NodeList +function children(\DOM\Node $node): NodeList { - /** @var list $children */ + /** @var list<\DOM\Element> $children */ $children = filter( $node->childNodes, - static fn (DOMNode $node): bool => is_element($node) + static fn (\DOM\Node $node): bool => is_element($node) ); return new NodeList(...$children); diff --git a/src/Xml/Dom/Locator/Element/locate_by_namespaced_tag_name.php b/src/Xml/Dom/Locator/Element/locate_by_namespaced_tag_name.php index a70caaa..5124bb5 100644 --- a/src/Xml/Dom/Locator/Element/locate_by_namespaced_tag_name.php +++ b/src/Xml/Dom/Locator/Element/locate_by_namespaced_tag_name.php @@ -4,13 +4,13 @@ namespace VeeWee\Xml\Dom\Locator\Element; -use DOMElement; +use \DOM\Element; use VeeWee\Xml\Dom\Collection\NodeList; /** - * @return NodeList + * @return NodeList<\DOM\Element> */ -function locate_by_namespaced_tag_name(DOMElement $node, string $namespace, string $localTagName): NodeList +function locate_by_namespaced_tag_name(\DOM\Element $node, string $namespace, string $localTagName): NodeList { return NodeList::fromDOMNodeList($node->getElementsByTagNameNS($namespace, $localTagName)); } diff --git a/src/Xml/Dom/Locator/Element/locate_by_tag_name.php b/src/Xml/Dom/Locator/Element/locate_by_tag_name.php index e091a61..b134ca2 100644 --- a/src/Xml/Dom/Locator/Element/locate_by_tag_name.php +++ b/src/Xml/Dom/Locator/Element/locate_by_tag_name.php @@ -4,13 +4,13 @@ namespace VeeWee\Xml\Dom\Locator\Element; -use DOMElement; +use \DOM\Element; use VeeWee\Xml\Dom\Collection\NodeList; /** - * @return NodeList + * @return NodeList<\DOM\Element> */ -function locate_by_tag_name(DOMElement $node, string $tag): NodeList +function locate_by_tag_name(\DOM\Element $node, string $tag): NodeList { return NodeList::fromDOMNodeList($node->getElementsByTagName($tag)); } diff --git a/src/Xml/Dom/Locator/Element/parent_element.php b/src/Xml/Dom/Locator/Element/parent_element.php index 95c4cf4..4e6ebcd 100644 --- a/src/Xml/Dom/Locator/Element/parent_element.php +++ b/src/Xml/Dom/Locator/Element/parent_element.php @@ -4,15 +4,15 @@ namespace VeeWee\Xml\Dom\Locator\Element; -use DOMElement; -use DOMNode; +use \DOM\Element; +use \DOM\Node; use VeeWee\Xml\Exception\RuntimeException; use function VeeWee\Xml\Dom\Predicate\is_element; /** * @throws RuntimeException */ -function parent_element(DOMNode $child): DOMElement +function parent_element(\DOM\Node $child): \DOM\Element { $parent = $child->parentNode; if (!$parent|| !is_element($parent)) { diff --git a/src/Xml/Dom/Locator/Element/siblings.php b/src/Xml/Dom/Locator/Element/siblings.php index fc6d809..9cba8c0 100644 --- a/src/Xml/Dom/Locator/Element/siblings.php +++ b/src/Xml/Dom/Locator/Element/siblings.php @@ -4,21 +4,21 @@ namespace VeeWee\Xml\Dom\Locator\Element; -use DOMElement; -use DOMNode; +use \DOM\Element; +use \DOM\Node; use VeeWee\Xml\Dom\Collection\NodeList; use function Psl\Vec\filter; use function VeeWee\Xml\Dom\Predicate\is_element; /** - * @return NodeList + * @return NodeList<\DOM\Element> */ -function siblings(DOMNode $node): NodeList +function siblings(\DOM\Node $node): NodeList { - /** @var NodeList $siblings */ + /** @var NodeList<\DOM\Element> $siblings */ $siblings = new NodeList(...filter( $node->parentNode?->childNodes?->getIterator() ?? [], - static fn (DOMNode $sibling): bool => is_element($sibling) && $sibling !== $node + static fn (\DOM\Node $sibling): bool => is_element($sibling) && $sibling !== $node )); return $siblings; diff --git a/src/Xml/Dom/Locator/Node/children.php b/src/Xml/Dom/Locator/Node/children.php index 88cb9b6..d0d3430 100644 --- a/src/Xml/Dom/Locator/Node/children.php +++ b/src/Xml/Dom/Locator/Node/children.php @@ -4,13 +4,13 @@ namespace VeeWee\Xml\Dom\Locator\Node; -use DOMNode; +use \DOM\Node; use VeeWee\Xml\Dom\Collection\NodeList; /** - * @return NodeList + * @return NodeList<\DOM\Node> */ -function children(DOMNode $node): NodeList +function children(\DOM\Node $node): NodeList { return NodeList::fromDOMNodeList($node->childNodes); } diff --git a/src/Xml/Dom/Locator/Node/detect_document.php b/src/Xml/Dom/Locator/Node/detect_document.php index fdf005b..6511250 100644 --- a/src/Xml/Dom/Locator/Node/detect_document.php +++ b/src/Xml/Dom/Locator/Node/detect_document.php @@ -4,8 +4,8 @@ namespace VeeWee\Xml\Dom\Locator\Node; -use DOMDocument; -use DOMNode; +use \DOM\XMLDocument; +use \DOM\Node; use InvalidArgumentException; use Webmozart\Assert\Assert; use function VeeWee\Xml\Dom\Predicate\is_document; @@ -14,10 +14,10 @@ * @throws InvalidArgumentException * @psalm-suppress RedundantCondition - node->ownerDocument can also be null... */ -function detect_document(DOMNode $node): DOMDocument +function detect_document(\DOM\Node $node): \DOM\XMLDocument { $document = is_document($node) ? $node : $node->ownerDocument; - Assert::notNull($document, 'Expected to find an ownerDocument on provided DOMNode.'); + Assert::notNull($document, 'Expected to find an ownerDocument on provided \DOM\Node.'); return $document; } diff --git a/src/Xml/Dom/Locator/Node/value.php b/src/Xml/Dom/Locator/Node/value.php index 6cc981b..dc51ad8 100644 --- a/src/Xml/Dom/Locator/Node/value.php +++ b/src/Xml/Dom/Locator/Node/value.php @@ -4,7 +4,7 @@ namespace VeeWee\Xml\Dom\Locator\Node; -use DOMNode; +use \DOM\Node; use Psl\Type\Exception\CoercionException; use Psl\Type\TypeInterface; @@ -17,7 +17,7 @@ * * @throws CoercionException */ -function value(DOMNode $node, TypeInterface $type) +function value(\DOM\Node $node, TypeInterface $type) { return $type->coerce($node->nodeValue); } diff --git a/src/Xml/Dom/Locator/Xmlns/linked_namespaces.php b/src/Xml/Dom/Locator/Xmlns/linked_namespaces.php index a5fdb79..8b7ef71 100644 --- a/src/Xml/Dom/Locator/Xmlns/linked_namespaces.php +++ b/src/Xml/Dom/Locator/Xmlns/linked_namespaces.php @@ -4,22 +4,22 @@ namespace VeeWee\Xml\Dom\Locator\Xmlns; -use DOMNameSpaceNode; -use DOMNode; +use \DOM\NameSpaceNode; +use \DOM\Node; use InvalidArgumentException; use VeeWee\Xml\Dom\Collection\NodeList; use VeeWee\Xml\Dom\Xpath; use VeeWee\Xml\Exception\RuntimeException; /** - * @return NodeList + * @return NodeList<\DOM\NameSpaceNode> * * @throws RuntimeException * @throws InvalidArgumentException */ -function linked_namespaces(DOMNode $node): NodeList +function linked_namespaces(\DOM\Node $node): NodeList { $xpath = Xpath::fromUnsafeNode($node); - return $xpath->query('./namespace::*', $node)->expectAllOfType(DOMNameSpaceNode::class); + return $xpath->query('./namespace::*', $node)->expectAllOfType(\DOM\NameSpaceNode::class); } diff --git a/src/Xml/Dom/Locator/Xmlns/recursive_linked_namespaces.php b/src/Xml/Dom/Locator/Xmlns/recursive_linked_namespaces.php index 509ce24..2f3c766 100644 --- a/src/Xml/Dom/Locator/Xmlns/recursive_linked_namespaces.php +++ b/src/Xml/Dom/Locator/Xmlns/recursive_linked_namespaces.php @@ -4,22 +4,22 @@ namespace VeeWee\Xml\Dom\Locator\Xmlns; -use DOMNameSpaceNode; -use DOMNode; +use \DOM\NameSpaceNode; +use \DOM\Node; use InvalidArgumentException; use VeeWee\Xml\Dom\Collection\NodeList; use VeeWee\Xml\Dom\Xpath; use VeeWee\Xml\Exception\RuntimeException; /** - * @return NodeList + * @return NodeList<\DOM\NameSpaceNode> * * @throws RuntimeException * @throws InvalidArgumentException */ -function recursive_linked_namespaces(DOMNode $node): NodeList +function recursive_linked_namespaces(\DOM\Node $node): NodeList { $xpath = Xpath::fromUnsafeNode($node); - return $xpath->query('.//namespace::*', $node)->expectAllOfType(DOMNameSpaceNode::class); + return $xpath->query('.//namespace::*', $node)->expectAllOfType(\DOM\NameSpaceNode::class); } diff --git a/src/Xml/Dom/Locator/Xsd/locate_all_xsd_schemas.php b/src/Xml/Dom/Locator/Xsd/locate_all_xsd_schemas.php index 8e41820..e632e62 100644 --- a/src/Xml/Dom/Locator/Xsd/locate_all_xsd_schemas.php +++ b/src/Xml/Dom/Locator/Xsd/locate_all_xsd_schemas.php @@ -4,14 +4,14 @@ namespace VeeWee\Xml\Dom\Locator\Xsd; -use DOMDocument; +use \DOM\XMLDocument; use Psl\Regex\Exception\RuntimeException; use VeeWee\Xml\Xsd\Schema\SchemaCollection; /** * @throws RuntimeException */ -function locate_all_xsd_schemas(DOMDocument $document): SchemaCollection +function locate_all_xsd_schemas(\DOM\XMLDocument $document): SchemaCollection { return new SchemaCollection( ...iterator_to_array(locate_namespaced_xsd_schemas($document)), diff --git a/src/Xml/Dom/Locator/Xsd/locate_namespaced_xsd_schemas.php b/src/Xml/Dom/Locator/Xsd/locate_namespaced_xsd_schemas.php index 1eb4b09..47d45e4 100644 --- a/src/Xml/Dom/Locator/Xsd/locate_namespaced_xsd_schemas.php +++ b/src/Xml/Dom/Locator/Xsd/locate_namespaced_xsd_schemas.php @@ -4,7 +4,7 @@ namespace VeeWee\Xml\Dom\Locator\Xsd; -use DOMDocument; +use \DOM\XMLDocument; use Psl\Regex\Exception\RuntimeException; use VeeWee\Xml\Xmlns\Xmlns; use VeeWee\Xml\Xsd\Schema\Schema; @@ -14,7 +14,7 @@ /** * @throws RuntimeException */ -function locate_namespaced_xsd_schemas(DOMDocument $document): SchemaCollection +function locate_namespaced_xsd_schemas(\DOM\XMLDocument $document): SchemaCollection { $schemaNs = Xmlns::xsi()->value(); $attributes = $document->documentElement->attributes; diff --git a/src/Xml/Dom/Locator/Xsd/locate_no_namespaced_xsd_schemas.php b/src/Xml/Dom/Locator/Xsd/locate_no_namespaced_xsd_schemas.php index e7d0ab0..2a9600f 100644 --- a/src/Xml/Dom/Locator/Xsd/locate_no_namespaced_xsd_schemas.php +++ b/src/Xml/Dom/Locator/Xsd/locate_no_namespaced_xsd_schemas.php @@ -4,7 +4,7 @@ namespace VeeWee\Xml\Dom\Locator\Xsd; -use DOMDocument; +use \DOM\XMLDocument; use Psl\Regex\Exception\RuntimeException; use VeeWee\Xml\Xmlns\Xmlns; use VeeWee\Xml\Xsd\Schema\Schema; @@ -15,7 +15,7 @@ /** * @throws RuntimeException */ -function locate_no_namespaced_xsd_schemas(DOMDocument $document): SchemaCollection +function locate_no_namespaced_xsd_schemas(\DOM\XMLDocument $document): SchemaCollection { $schemaNs = Xmlns::xsi()->value(); $attributes = $document->documentElement->attributes; diff --git a/src/Xml/Dom/Locator/document_element.php b/src/Xml/Dom/Locator/document_element.php index 4df07f8..821b997 100644 --- a/src/Xml/Dom/Locator/document_element.php +++ b/src/Xml/Dom/Locator/document_element.php @@ -5,13 +5,13 @@ namespace VeeWee\Xml\Dom\Locator; use Closure; -use DOMDocument; -use DOMElement; +use \DOM\XMLDocument; +use \DOM\Element; /** - * @return Closure(DOMDocument): DOMElement + * @return Closure(\DOM\XMLDocument): \DOM\Element */ function document_element(): Closure { - return static fn (DOMDocument $document): DOMElement => $document->documentElement; + return static fn (\DOM\XMLDocument $document): \DOM\Element => $document->documentElement; } diff --git a/src/Xml/Dom/Locator/elements_with_namespaced_tagname.php b/src/Xml/Dom/Locator/elements_with_namespaced_tagname.php index c996e81..951f9a7 100644 --- a/src/Xml/Dom/Locator/elements_with_namespaced_tagname.php +++ b/src/Xml/Dom/Locator/elements_with_namespaced_tagname.php @@ -5,20 +5,20 @@ namespace VeeWee\Xml\Dom\Locator; use Closure; -use DOMDocument; -use DOMElement; +use \DOM\XMLDocument; +use \DOM\Element; use VeeWee\Xml\Dom\Collection\NodeList; use function VeeWee\Xml\Dom\Locator\Element\locate_by_namespaced_tag_name; /** - * @return Closure(DOMDocument): NodeList + * @return Closure(\DOM\XMLDocument): NodeList<\DOM\Element> */ function elements_with_namespaced_tagname(string $namespace, string $localTagName): Closure { return /** - * @return NodeList + * @return NodeList<\DOM\Element> */ - static fn (DOMDocument $document): NodeList + static fn (\DOM\XMLDocument $document): NodeList => locate_by_namespaced_tag_name($document->documentElement, $namespace, $localTagName); } diff --git a/src/Xml/Dom/Locator/elements_with_tagname.php b/src/Xml/Dom/Locator/elements_with_tagname.php index f16bab3..c2773fb 100644 --- a/src/Xml/Dom/Locator/elements_with_tagname.php +++ b/src/Xml/Dom/Locator/elements_with_tagname.php @@ -5,20 +5,20 @@ namespace VeeWee\Xml\Dom\Locator; use Closure; -use DOMDocument; -use DOMElement; +use \DOM\XMLDocument; +use \DOM\Element; use VeeWee\Xml\Dom\Collection\NodeList; use function VeeWee\Xml\Dom\Locator\Element\locate_by_tag_name; /** - * @return Closure(DOMDocument): NodeList + * @return Closure(\DOM\XMLDocument): NodeList<\DOM\Element> */ function elements_with_tagname(string $tagName): Closure { return /** - * @return NodeList + * @return NodeList<\DOM\Element> */ - static fn (DOMDocument $document): NodeList + static fn (\DOM\XMLDocument $document): NodeList => locate_by_tag_name($document->documentElement, $tagName); } diff --git a/src/Xml/Dom/Locator/root_namespace.php b/src/Xml/Dom/Locator/root_namespace.php index 63b8ccf..d182f0a 100644 --- a/src/Xml/Dom/Locator/root_namespace.php +++ b/src/Xml/Dom/Locator/root_namespace.php @@ -5,12 +5,12 @@ namespace VeeWee\Xml\Dom\Locator; use Closure; -use DOMDocument; +use \DOM\XMLDocument; /** - * @return Closure(DOMDocument): ?string + * @return Closure(\DOM\XMLDocument): ?string */ function root_namespace_uri(): Closure { - return static fn (DOMDocument $document): ?string => $document->documentElement->namespaceURI; + return static fn (\DOM\XMLDocument $document): ?string => $document->documentElement->namespaceURI; } diff --git a/src/Xml/Dom/Manipulator/Attribute/rename.php b/src/Xml/Dom/Manipulator/Attribute/rename.php index 5cc2eb7..9fa2cc1 100644 --- a/src/Xml/Dom/Manipulator/Attribute/rename.php +++ b/src/Xml/Dom/Manipulator/Attribute/rename.php @@ -4,7 +4,7 @@ namespace VeeWee\Xml\Dom\Manipulator\Attribute; -use DOMAttr; +use \DOM\Attr; use VeeWee\Xml\Exception\RuntimeException; use function VeeWee\Xml\Dom\Builder\attribute; use function VeeWee\Xml\Dom\Builder\namespaced_attribute; @@ -15,7 +15,7 @@ /** * @throws RuntimeException */ -function rename(DOMAttr $target, string $newQName, ?string $newNamespaceURI = null): DOMAttr +function rename(\DOM\Attr $target, string $newQName, ?string $newNamespaceURI = null): \DOM\Attr { $element = parent_element($target); $namespace = $newNamespaceURI ?? $target->namespaceURI; diff --git a/src/Xml/Dom/Manipulator/Document/optimize_namespaces.php b/src/Xml/Dom/Manipulator/Document/optimize_namespaces.php index 1dcbe87..4710e3f 100644 --- a/src/Xml/Dom/Manipulator/Document/optimize_namespaces.php +++ b/src/Xml/Dom/Manipulator/Document/optimize_namespaces.php @@ -4,8 +4,8 @@ namespace VeeWee\Xml\Dom\Manipulator\Document; -use DOMDocument; -use DOMNameSpaceNode; +use \DOM\XMLDocument; +use \DOM\NameSpaceNode; use VeeWee\Xml\Exception\RuntimeException; use VeeWee\Xml\Xmlns\Xmlns; use function Psl\Dict\unique; @@ -17,16 +17,16 @@ /** * @throws RuntimeException */ -function optimize_namespaces(DOMDocument $document, string $prefix = 'ns'): void +function optimize_namespaces(\DOM\XMLDocument $document, string $prefix = 'ns'): void { $namespaceURIs = recursive_linked_namespaces($document) - ->filter(static fn (DOMNameSpaceNode $node): bool => $node->namespaceURI !== Xmlns::xml()->value()) + ->filter(static fn (\DOM\NameSpaceNode $node): bool => $node->namespaceURI !== Xmlns::xml()->value()) ->reduce( /** * @param list $grouped * @return list */ - static fn (array $grouped, DOMNameSpaceNode $node): array + static fn (array $grouped, \DOM\NameSpaceNode $node): array => values(unique([...$grouped, $node->namespaceURI])), [] ); diff --git a/src/Xml/Dom/Manipulator/Element/copy_named_xmlns_attributes.php b/src/Xml/Dom/Manipulator/Element/copy_named_xmlns_attributes.php index 0b251a1..4392123 100644 --- a/src/Xml/Dom/Manipulator/Element/copy_named_xmlns_attributes.php +++ b/src/Xml/Dom/Manipulator/Element/copy_named_xmlns_attributes.php @@ -4,8 +4,8 @@ namespace VeeWee\Xml\Dom\Manipulator\Element; -use DOMElement; -use DOMNameSpaceNode; +use \DOM\Element; +use \DOM\NameSpaceNode; use VeeWee\Xml\Exception\RuntimeException; use function VeeWee\Xml\Dom\Builder\xmlns_attribute; use function VeeWee\Xml\Dom\Locator\Xmlns\linked_namespaces; @@ -13,9 +13,9 @@ /** * @throws RuntimeException */ -function copy_named_xmlns_attributes(DOMElement $target, DOMElement $source): void +function copy_named_xmlns_attributes(\DOM\Element $target, \DOM\Element $source): void { - linked_namespaces($source)->forEach(static function (DOMNameSpaceNode $xmlns) use ($target) { + linked_namespaces($source)->forEach(static function (\DOM\NameSpaceNode $xmlns) use ($target) { if ($xmlns->prefix && !$target->hasAttribute($xmlns->nodeName)) { xmlns_attribute($xmlns->prefix, $xmlns->namespaceURI)($target); } diff --git a/src/Xml/Dom/Manipulator/Element/rename.php b/src/Xml/Dom/Manipulator/Element/rename.php index cab0076..ae6e8aa 100644 --- a/src/Xml/Dom/Manipulator/Element/rename.php +++ b/src/Xml/Dom/Manipulator/Element/rename.php @@ -4,9 +4,9 @@ namespace VeeWee\Xml\Dom\Manipulator\Element; -use DOMAttr; -use DOMElement; -use DOMNameSpaceNode; +use \DOM\Attr; +use \DOM\Element; +use \DOM\NameSpaceNode; use VeeWee\Xml\Exception\RuntimeException; use function VeeWee\Xml\Dom\Builder\element; use function VeeWee\Xml\Dom\Builder\namespaced_element; @@ -21,7 +21,7 @@ /** * @throws RuntimeException */ -function rename(DOMElement $target, string $newQName, ?string $newNamespaceURI = null): DOMElement +function rename(\DOM\Element $target, string $newQName, ?string $newNamespaceURI = null): \DOM\Element { $isRootElement = $target === $target->ownerDocument->documentElement; $parent = $isRootElement ? $target->ownerDocument : parent_element($target); @@ -35,7 +35,7 @@ function rename(DOMElement $target, string $newQName, ?string $newNamespaceURI = append(...children($target))($newElement); xmlns_attributes_list($target)->forEach( - static function (DOMNameSpaceNode $attribute) use ($target, $newElement): void { + static function (\DOM\NameSpaceNode $attribute) use ($target, $newElement): void { if (is_default_xmlns_attribute($attribute) || $target->prefix === $attribute->prefix) { return; } @@ -44,7 +44,7 @@ static function (DOMNameSpaceNode $attribute) use ($target, $newElement): void { ); attributes_list($target)->forEach( - static function (DOMAttr $attribute) use ($newElement): void { + static function (\DOM\Attr $attribute) use ($newElement): void { $newElement->setAttributeNode($attribute); } ); diff --git a/src/Xml/Dom/Manipulator/Node/append_external_node.php b/src/Xml/Dom/Manipulator/Node/append_external_node.php index aa39beb..87ef41e 100644 --- a/src/Xml/Dom/Manipulator/Node/append_external_node.php +++ b/src/Xml/Dom/Manipulator/Node/append_external_node.php @@ -4,13 +4,13 @@ namespace VeeWee\Xml\Dom\Manipulator\Node; -use DOMNode; +use \DOM\Node; use VeeWee\Xml\Exception\RuntimeException; /** * @throws RuntimeException */ -function append_external_node(DOMNode $target, DOMNode $source): DOMNode +function append_external_node(\DOM\Node $target, \DOM\Node $source): \DOM\Node { $copy = import_node_deeply($target, $source); $target->appendChild($copy); diff --git a/src/Xml/Dom/Manipulator/Node/import_node_deeply.php b/src/Xml/Dom/Manipulator/Node/import_node_deeply.php index 7f4d229..e923e14 100644 --- a/src/Xml/Dom/Manipulator/Node/import_node_deeply.php +++ b/src/Xml/Dom/Manipulator/Node/import_node_deeply.php @@ -4,7 +4,7 @@ namespace VeeWee\Xml\Dom\Manipulator\Node; -use DOMNode; +use \DOM\Node; use VeeWee\Xml\Exception\RuntimeException; use function VeeWee\Xml\Dom\Locator\Node\detect_document; use function VeeWee\Xml\ErrorHandling\disallow_issues; @@ -13,10 +13,10 @@ /** * @throws RuntimeException */ -function import_node_deeply(DOMNode $target, DOMNode $source): DOMNode +function import_node_deeply(\DOM\Node $target, \DOM\Node $source): \DOM\Node { return disallow_issues( - static function () use ($target, $source): DOMNode { + static function () use ($target, $source): \DOM\Node { $document = detect_document($target); return disallow_libxml_false_returns( diff --git a/src/Xml/Dom/Manipulator/Node/remove.php b/src/Xml/Dom/Manipulator/Node/remove.php index 4844ad1..f399411 100644 --- a/src/Xml/Dom/Manipulator/Node/remove.php +++ b/src/Xml/Dom/Manipulator/Node/remove.php @@ -4,7 +4,7 @@ namespace VeeWee\Xml\Dom\Manipulator\Node; -use DOMNode; +use \DOM\Node; use VeeWee\Xml\Exception\RuntimeException; use function VeeWee\Xml\Dom\Locator\Element\parent_element; use function VeeWee\Xml\Dom\Predicate\is_attribute; @@ -14,13 +14,13 @@ /** * @throws RuntimeException */ -function remove(DOMNode $target): DOMNode +function remove(\DOM\Node $target): \DOM\Node { return disallow_issues( /** * @throws RuntimeException */ - static function () use ($target): DOMNode { + static function () use ($target): \DOM\Node { $parent = parent_element($target); if (is_attribute($target)) { diff --git a/src/Xml/Dom/Manipulator/Node/remove_namespace.php b/src/Xml/Dom/Manipulator/Node/remove_namespace.php index aec60bf..169a8d1 100644 --- a/src/Xml/Dom/Manipulator/Node/remove_namespace.php +++ b/src/Xml/Dom/Manipulator/Node/remove_namespace.php @@ -4,8 +4,8 @@ namespace VeeWee\Xml\Dom\Manipulator\Node; -use DOMElement; -use DOMNameSpaceNode; +use \DOM\Element; +use \DOM\NameSpaceNode; use VeeWee\Xml\Exception\RuntimeException; use function VeeWee\Xml\ErrorHandling\disallow_issues; use function VeeWee\Xml\ErrorHandling\disallow_libxml_false_returns; @@ -13,13 +13,13 @@ /** * @throws RuntimeException */ -function remove_namespace(DOMNameSpaceNode $target, DOMElement $parent): DOMNameSpaceNode +function remove_namespace(\DOM\NameSpaceNode $target, \DOM\Element $parent): \DOM\NameSpaceNode { return disallow_issues( /** * @throws RuntimeException */ - static function () use ($target, $parent): DOMNameSpaceNode { + static function () use ($target, $parent): \DOM\NameSpaceNode { disallow_libxml_false_returns( $parent->removeAttributeNS($target->namespaceURI, $target->prefix), 'Could not remove xmlns attribute from dom element' diff --git a/src/Xml/Dom/Manipulator/Node/rename.php b/src/Xml/Dom/Manipulator/Node/rename.php index 99c9ad1..1265175 100644 --- a/src/Xml/Dom/Manipulator/Node/rename.php +++ b/src/Xml/Dom/Manipulator/Node/rename.php @@ -4,7 +4,7 @@ namespace VeeWee\Xml\Dom\Manipulator\Node; -use DOMNode; +use \DOM\Node; use VeeWee\Xml\Exception\RuntimeException; use function VeeWee\Xml\Dom\Manipulator\Attribute\rename as rename_attribute; use function VeeWee\Xml\Dom\Manipulator\Element\rename as rename_element; @@ -16,7 +16,7 @@ * * @throws RuntimeException */ -function rename(DOMNode $target, string $newQName, ?string $newNamespaceURI = null): DOMNode +function rename(\DOM\Node $target, string $newQName, ?string $newNamespaceURI = null): \DOM\Node { if (is_attribute($target)) { return rename_attribute($target, $newQName, $newNamespaceURI); diff --git a/src/Xml/Dom/Manipulator/Node/replace_by_external_node.php b/src/Xml/Dom/Manipulator/Node/replace_by_external_node.php index 66321bc..012ebfa 100644 --- a/src/Xml/Dom/Manipulator/Node/replace_by_external_node.php +++ b/src/Xml/Dom/Manipulator/Node/replace_by_external_node.php @@ -4,7 +4,7 @@ namespace VeeWee\Xml\Dom\Manipulator\Node; -use DOMNode; +use \DOM\Node; use VeeWee\Xml\Exception\RuntimeException; use Webmozart\Assert\Assert; use function get_class; @@ -14,10 +14,10 @@ /** * @throws RuntimeException */ -function replace_by_external_node(DOMNode $target, DOMNode $source): DOMNode +function replace_by_external_node(\DOM\Node $target, \DOM\Node $source): \DOM\Node { return disallow_issues( - static function () use ($target, $source) : DOMNode { + static function () use ($target, $source) : \DOM\Node { $parentNode = $target->parentNode; Assert::notNull($parentNode, 'Could not replace a node without parent node. ('.get_class($target).')'); $copy = import_node_deeply($target, $source); diff --git a/src/Xml/Dom/Manipulator/Node/replace_by_external_nodes.php b/src/Xml/Dom/Manipulator/Node/replace_by_external_nodes.php index 850cd0e..e9a92a7 100644 --- a/src/Xml/Dom/Manipulator/Node/replace_by_external_nodes.php +++ b/src/Xml/Dom/Manipulator/Node/replace_by_external_nodes.php @@ -4,7 +4,7 @@ namespace VeeWee\Xml\Dom\Manipulator\Node; -use DOMNode; +use \DOM\Node; use VeeWee\Xml\Exception\RuntimeException; use Webmozart\Assert\Assert; use function get_class; @@ -13,21 +13,21 @@ /** * @throws RuntimeException - * @param iterable $sources - * @return array + * @param iterable $sources + * @return array */ -function replace_by_external_nodes(DOMNode $target, iterable $sources): array +function replace_by_external_nodes(\DOM\Node $target, iterable $sources): array { return disallow_issues( /** - * @return array + * @return array */ static function () use ($target, $sources) : array { $parentNode = $target->parentNode; Assert::notNull($parentNode, 'Could not replace a node without parent node. ('.get_class($target).')'); $copies = map( $sources, - static fn (DOMNode $source): DOMNode => import_node_deeply($target, $source) + static fn (\DOM\Node $source): \DOM\Node => import_node_deeply($target, $source) ); foreach ($copies as $copy) { diff --git a/src/Xml/Dom/Manipulator/Xmlns/rename.php b/src/Xml/Dom/Manipulator/Xmlns/rename.php index 21f7b9f..086a455 100644 --- a/src/Xml/Dom/Manipulator/Xmlns/rename.php +++ b/src/Xml/Dom/Manipulator/Xmlns/rename.php @@ -4,10 +4,10 @@ namespace VeeWee\Xml\Dom\Manipulator\Xmlns; -use DOMDocument; -use DOMElement; -use DOMNameSpaceNode; -use DOMNode; +use \DOM\XMLDocument; +use \DOM\Element; +use \DOM\NameSpaceNode; +use \DOM\Node; use VeeWee\Xml\Dom\Collection\NodeList; use VeeWee\Xml\Dom\Xpath; use VeeWee\Xml\Exception\RuntimeException; @@ -25,7 +25,7 @@ /** * @throws RuntimeException */ -function rename(DOMDocument $document, string $namespaceURI, string $newPrefix): void +function rename(\DOM\XMLDocument $document, string $namespaceURI, string $newPrefix): void { // Check for prefix collisions $existingUri = $document->lookupNamespaceURI($newPrefix); @@ -41,7 +41,7 @@ function rename(DOMDocument $document, string $namespaceURI, string $newPrefix): } $xpath = Xpath::fromUnsafeNode($document); - $predicate = static fn (DOMNode $node): bool + $predicate = static fn (\DOM\Node $node): bool => $node->namespaceURI === $namespaceURI && $node->prefix !== $newPrefix; // Fetch all nodes (attributes and elements) linked to the given namespace and the nodes that declare namespaces. @@ -51,8 +51,8 @@ function rename(DOMDocument $document, string $namespaceURI, string $newPrefix): // otherwise XMLNS namespace will be removed again after dealing with the elements that declare the xmlns. $linkedNodes = $xpath->query( sprintf('//*[namespace-uri()=\'%1$s\' or @*[namespace-uri()=\'%1$s\'] or namespace::*]', $namespaceURI) - )->expectAllOfType(DOMElement::class)->reduce( - static fn (NodeList $list, DOMElement $element): NodeList + )->expectAllOfType(\DOM\Element::class)->reduce( + static fn (NodeList $list, \DOM\Element $element): NodeList => new NodeList( ...[$element], ...$list, @@ -68,9 +68,9 @@ function rename(DOMDocument $document, string $namespaceURI, string $newPrefix): // Go through the linked nodes and remove all matching xmlns attributes // Finally rename the node in order to use the new prefix. $linkedNodes->forEach( - static function (DOMNode $node) use ($namespaceURI, $newPrefix, $predicate, $root): void { + static function (\DOM\Node $node) use ($namespaceURI, $newPrefix, $predicate, $root): void { // Wrapped in a closure so that psalm knows it all... - $newQname = static fn (DOMNode $node): string => $newPrefix.':'.non_empty_string()->assert($node->localName); + $newQname = static fn (\DOM\Node $node): string => $newPrefix.':'.non_empty_string()->assert($node->localName); if (is_attribute($node)) { rename_node($node, $newQname($node), $namespaceURI); @@ -86,7 +86,7 @@ static function (DOMNode $node) use ($namespaceURI, $newPrefix, $predicate, $roo // Remove old xmlns declarations: $namespaceNodes = xmlns_attributes_list($node) ->filter( - static fn (DOMNameSpaceNode $xmlns): bool + static fn (\DOM\NameSpaceNode $xmlns): bool => $xmlns->namespaceURI === $namespaceURI && $xmlns->prefix !== $newPrefix ); @@ -95,7 +95,7 @@ static function (DOMNode $node) use ($namespaceURI, $newPrefix, $predicate, $roo // We need to make sure to rename it to the new namespace // Otherwise the namespace will be lost! if ($node === $root && $predicate($node)) { - // The root node renaming can result in a new DOMNode. + // The root node renaming can result in a new \DOM\Node. // Make sure to use this new node to avoid issues with e.g. duplicate namespace declarations. $node = rename_node($node, $newQname($node), $namespaceURI); invariant(is_element($node), 'Expected the root node to be a DOM element'); diff --git a/src/Xml/Dom/Manipulator/append.php b/src/Xml/Dom/Manipulator/append.php index c5bd011..922af7c 100644 --- a/src/Xml/Dom/Manipulator/append.php +++ b/src/Xml/Dom/Manipulator/append.php @@ -5,18 +5,18 @@ namespace VeeWee\Xml\Dom\Manipulator; use Closure; -use DOMNode; +use \DOM\Node; use VeeWee\Xml\Exception\RuntimeException; use function VeeWee\Xml\ErrorHandling\disallow_issues; /** * @no-named-arguments * @throws RuntimeException - * @return Closure(DOMNode): DOMNode + * @return Closure(\DOM\Node): \DOM\Node */ -function append(DOMNode ... $nodes): Closure +function append(\DOM\Node ... $nodes): Closure { - return static fn (DOMNode $target): DOMNode => disallow_issues( + return static fn (\DOM\Node $target): \DOM\Node => disallow_issues( static function () use ($target, $nodes) { foreach ($nodes as $node) { $target->appendChild($node); diff --git a/src/Xml/Dom/Mapper/Mapper.php b/src/Xml/Dom/Mapper/Mapper.php index ca3f148..3677784 100644 --- a/src/Xml/Dom/Mapper/Mapper.php +++ b/src/Xml/Dom/Mapper/Mapper.php @@ -4,7 +4,7 @@ namespace VeeWee\Xml\Dom\Mapper; -use DOMDocument; +use \DOM\XMLDocument; /** * @template R @@ -14,5 +14,5 @@ interface Mapper /** * @return R */ - public function __invoke(DOMDocument $document): mixed; + public function __invoke(\DOM\XMLDocument $document): mixed; } diff --git a/src/Xml/Dom/Mapper/xml_string.php b/src/Xml/Dom/Mapper/xml_string.php index 529ae90..d41a66e 100644 --- a/src/Xml/Dom/Mapper/xml_string.php +++ b/src/Xml/Dom/Mapper/xml_string.php @@ -5,7 +5,7 @@ namespace VeeWee\Xml\Dom\Mapper; use Closure; -use DOMNode; +use \DOM\Node; use function Psl\Type\non_empty_string; use function VeeWee\Xml\Dom\Locator\Node\detect_document; use function VeeWee\Xml\Dom\Predicate\is_document; @@ -13,11 +13,11 @@ use function VeeWee\Xml\ErrorHandling\disallow_libxml_false_returns; /** - * @return Closure(DOMNode): non-empty-string + * @return Closure(\DOM\Node): non-empty-string */ function xml_string(): Closure { - return static fn (DOMNode $node): string => disallow_issues( + return static fn (\DOM\Node $node): string => disallow_issues( static function () use ($node): string { $document = detect_document($node); $node = is_document($node) ? null : $node; diff --git a/src/Xml/Dom/Mapper/xslt_template.php b/src/Xml/Dom/Mapper/xslt_template.php index 89b510a..9862e39 100644 --- a/src/Xml/Dom/Mapper/xslt_template.php +++ b/src/Xml/Dom/Mapper/xslt_template.php @@ -5,8 +5,8 @@ namespace VeeWee\Xml\Dom\Mapper; use Closure; -use DOMDocument; -use VeeWee\Xml\Dom\Document; +use DOMDocument as DOMDocument; +use VeeWee\XmlDOMDocument; use VeeWee\Xml\Xslt\Processor; use XSLTProcessor; diff --git a/src/Xml/Dom/Predicate/is_attribute.php b/src/Xml/Dom/Predicate/is_attribute.php index ca18933..8eaf5d3 100644 --- a/src/Xml/Dom/Predicate/is_attribute.php +++ b/src/Xml/Dom/Predicate/is_attribute.php @@ -4,13 +4,13 @@ namespace VeeWee\Xml\Dom\Predicate; -use DOMAttr; -use DOMNode; +use \DOM\Attr; +use \DOM\Node; /** - * @psalm-assert-if-true DOMAttr $node + * @psalm-assert-if-true \DOM\Attr $node */ -function is_attribute(DOMNode $node): bool +function is_attribute(\DOM\Node $node): bool { - return $node instanceof DOMAttr; + return $node instanceof \DOM\Attr; } diff --git a/src/Xml/Dom/Predicate/is_cdata.php b/src/Xml/Dom/Predicate/is_cdata.php index ee4884d..60b7ac2 100644 --- a/src/Xml/Dom/Predicate/is_cdata.php +++ b/src/Xml/Dom/Predicate/is_cdata.php @@ -4,13 +4,13 @@ namespace VeeWee\Xml\Dom\Predicate; -use DOMCdataSection; -use DOMNode; +use \DOM\CdataSection; +use \DOM\Node; /** - * @psalm-assert-if-true DOMCdataSection $node + * @psalm-assert-if-true \DOM\CdataSection $node */ -function is_cdata(DOMNode $node): bool +function is_cdata(\DOM\Node $node): bool { - return $node instanceof DOMCdataSection; + return $node instanceof \DOM\CdataSection; } diff --git a/src/Xml/Dom/Predicate/is_default_xmlns_attribute.php b/src/Xml/Dom/Predicate/is_default_xmlns_attribute.php index 836ebd6..7aac7fe 100644 --- a/src/Xml/Dom/Predicate/is_default_xmlns_attribute.php +++ b/src/Xml/Dom/Predicate/is_default_xmlns_attribute.php @@ -4,10 +4,10 @@ namespace VeeWee\Xml\Dom\Predicate; -use DOMNameSpaceNode; -use DOMNode; +use \DOM\NameSpaceNode; +use \DOM\Node; -function is_default_xmlns_attribute(DOMNode|DOMNameSpaceNode $node): bool +function is_default_xmlns_attribute(\DOM\Node|\DOM\NameSpaceNode $node): bool { return is_xmlns_attribute($node) && $node->prefix === ''; } diff --git a/src/Xml/Dom/Predicate/is_document.php b/src/Xml/Dom/Predicate/is_document.php index f7ccd7f..42eb991 100644 --- a/src/Xml/Dom/Predicate/is_document.php +++ b/src/Xml/Dom/Predicate/is_document.php @@ -4,13 +4,13 @@ namespace VeeWee\Xml\Dom\Predicate; -use DOMDocument; -use DOMNode; +use \DOM\XMLDocument; +use \DOM\Node; /** - * @psalm-assert-if-true DOMDocument $node + * @psalm-assert-if-true \DOM\XMLDocument $node */ -function is_document(DOMNode $node): bool +function is_document(\DOM\Node $node): bool { - return $node instanceof DOMDocument; + return $node instanceof \DOM\XMLDocument; } diff --git a/src/Xml/Dom/Predicate/is_document_element.php b/src/Xml/Dom/Predicate/is_document_element.php index b1b363b..1b5b22c 100644 --- a/src/Xml/Dom/Predicate/is_document_element.php +++ b/src/Xml/Dom/Predicate/is_document_element.php @@ -4,11 +4,11 @@ namespace VeeWee\Xml\Dom\Predicate; -use DOMNameSpaceNode; -use DOMNode; +use \DOM\NameSpaceNode; +use \DOM\Node; use function VeeWee\Xml\Dom\Locator\Node\detect_document; -function is_document_element(DOMNode|DOMNameSpaceNode $node): bool +function is_document_element(\DOM\Node|\DOM\NameSpaceNode $node): bool { return is_element($node) && detect_document($node)->documentElement === $node; } diff --git a/src/Xml/Dom/Predicate/is_element.php b/src/Xml/Dom/Predicate/is_element.php index a68aacc..e0c6af0 100644 --- a/src/Xml/Dom/Predicate/is_element.php +++ b/src/Xml/Dom/Predicate/is_element.php @@ -4,13 +4,13 @@ namespace VeeWee\Xml\Dom\Predicate; -use DOMElement; -use DOMNode; +use \DOM\Element; +use \DOM\Node; /** - * @psalm-assert-if-true DOMElement $node + * @psalm-assert-if-true \DOM\Element $node */ -function is_element(DOMNode $node): bool +function is_element(\DOM\Node $node): bool { - return $node instanceof DOMElement; + return $node instanceof \DOM\Element; } diff --git a/src/Xml/Dom/Predicate/is_non_empty_text.php b/src/Xml/Dom/Predicate/is_non_empty_text.php index 0609d7b..8dd327a 100644 --- a/src/Xml/Dom/Predicate/is_non_empty_text.php +++ b/src/Xml/Dom/Predicate/is_non_empty_text.php @@ -4,9 +4,9 @@ namespace VeeWee\Xml\Dom\Predicate; -use DOMNode; +use \DOM\Node; -function is_non_empty_text(DOMNode $node): bool +function is_non_empty_text(\DOM\Node $node): bool { return is_text($node) && trim($node->nodeValue ?? '') !== ''; } diff --git a/src/Xml/Dom/Predicate/is_text.php b/src/Xml/Dom/Predicate/is_text.php index a1b5c0b..865d9f8 100644 --- a/src/Xml/Dom/Predicate/is_text.php +++ b/src/Xml/Dom/Predicate/is_text.php @@ -4,13 +4,13 @@ namespace VeeWee\Xml\Dom\Predicate; -use DOMNode; -use DOMText; +use \DOM\Node; +use \DOM\Text; /** - * @psalm-assert-if-true DOMText $node + * @psalm-assert-if-true \DOM\Text $node */ -function is_text(DOMNode $node): bool +function is_text(\DOM\Node $node): bool { - return $node instanceof DOMText; + return $node instanceof \DOM\Text; } diff --git a/src/Xml/Dom/Predicate/is_whitespace.php b/src/Xml/Dom/Predicate/is_whitespace.php index 2f35b2f..69fa6b7 100644 --- a/src/Xml/Dom/Predicate/is_whitespace.php +++ b/src/Xml/Dom/Predicate/is_whitespace.php @@ -4,9 +4,9 @@ namespace VeeWee\Xml\Dom\Predicate; -use DOMNode; +use \DOM\Node; -function is_whitespace(DOMNode $node): bool +function is_whitespace(\DOM\Node $node): bool { return is_text($node) && trim($node->nodeValue ?? '') === ''; } diff --git a/src/Xml/Dom/Predicate/is_xmlns_attribute.php b/src/Xml/Dom/Predicate/is_xmlns_attribute.php index 5be81b8..422ad07 100644 --- a/src/Xml/Dom/Predicate/is_xmlns_attribute.php +++ b/src/Xml/Dom/Predicate/is_xmlns_attribute.php @@ -4,13 +4,13 @@ namespace VeeWee\Xml\Dom\Predicate; -use DOMNameSpaceNode; -use DOMNode; +use \DOM\NameSpaceNode; +use \DOM\Node; /** - * @psalm-assert-if-true DOMNameSpaceNode $node + * @psalm-assert-if-true \DOM\NameSpaceNode $node */ -function is_xmlns_attribute(DOMNode|DOMNameSpaceNode $node): bool +function is_xmlns_attribute(\DOM\Node|\DOM\NameSpaceNode $node): bool { - return $node instanceof DOMNameSpaceNode; + return $node instanceof \DOM\NameSpaceNode; } diff --git a/src/Xml/Dom/Traverser/Action.php b/src/Xml/Dom/Traverser/Action.php index 98fd39f..198e01d 100644 --- a/src/Xml/Dom/Traverser/Action.php +++ b/src/Xml/Dom/Traverser/Action.php @@ -4,9 +4,9 @@ namespace VeeWee\Xml\Dom\Traverser; -use DOMNode; +use \DOM\Node; interface Action { - public function __invoke(DOMNode $currentNode): void; + public function __invoke(\DOM\Node $currentNode): void; } diff --git a/src/Xml/Dom/Traverser/Action/Noop.php b/src/Xml/Dom/Traverser/Action/Noop.php index 2443ddb..c31b0b2 100644 --- a/src/Xml/Dom/Traverser/Action/Noop.php +++ b/src/Xml/Dom/Traverser/Action/Noop.php @@ -4,12 +4,12 @@ namespace VeeWee\Xml\Dom\Traverser\Action; -use DOMNode; +use \DOM\Node; use VeeWee\Xml\Dom\Traverser\Action; final class Noop implements Action { - public function __invoke(DOMNode $currentNode): void + public function __invoke(\DOM\Node $currentNode): void { } } diff --git a/src/Xml/Dom/Traverser/Action/RemoveNode.php b/src/Xml/Dom/Traverser/Action/RemoveNode.php index d7b0d5e..11d0c91 100644 --- a/src/Xml/Dom/Traverser/Action/RemoveNode.php +++ b/src/Xml/Dom/Traverser/Action/RemoveNode.php @@ -4,7 +4,7 @@ namespace VeeWee\Xml\Dom\Traverser\Action; -use DOMNode; +use \DOM\Node; use VeeWee\Xml\Dom\Traverser\Action; use VeeWee\Xml\Exception\RuntimeException; use function VeeWee\Xml\Dom\Manipulator\Node\remove; @@ -14,7 +14,7 @@ final class RemoveNode implements Action /** * @throws RuntimeException */ - public function __invoke(DOMNode $currentNode): void + public function __invoke(\DOM\Node $currentNode): void { remove($currentNode); } diff --git a/src/Xml/Dom/Traverser/Action/RenameNode.php b/src/Xml/Dom/Traverser/Action/RenameNode.php index 0484c1c..919685d 100644 --- a/src/Xml/Dom/Traverser/Action/RenameNode.php +++ b/src/Xml/Dom/Traverser/Action/RenameNode.php @@ -4,7 +4,7 @@ namespace VeeWee\Xml\Dom\Traverser\Action; -use DOMNode; +use \DOM\Node; use VeeWee\Xml\Dom\Traverser\Action; use VeeWee\Xml\Exception\RuntimeException; use function VeeWee\Xml\Dom\Manipulator\Node\rename; @@ -19,7 +19,7 @@ public function __construct( /** * @throws RuntimeException */ - public function __invoke(DOMNode $currentNode): void + public function __invoke(\DOM\Node $currentNode): void { rename($currentNode, $this->newQName); } diff --git a/src/Xml/Dom/Traverser/Action/ReplaceNode.php b/src/Xml/Dom/Traverser/Action/ReplaceNode.php index fd8eed7..a78dbde 100644 --- a/src/Xml/Dom/Traverser/Action/ReplaceNode.php +++ b/src/Xml/Dom/Traverser/Action/ReplaceNode.php @@ -4,7 +4,7 @@ namespace VeeWee\Xml\Dom\Traverser\Action; -use DOMNode; +use \DOM\Node; use VeeWee\Xml\Dom\Traverser\Action; use VeeWee\Xml\Exception\RuntimeException; use function VeeWee\Xml\Dom\Manipulator\Node\replace_by_external_node; @@ -12,14 +12,14 @@ final class ReplaceNode implements Action { public function __construct( - private DOMNode $newNode + private \DOM\Node $newNode ) { } /** * @throws RuntimeException */ - public function __invoke(DOMNode $currentNode): void + public function __invoke(\DOM\Node $currentNode): void { replace_by_external_node($currentNode, $this->newNode); } diff --git a/src/Xml/Dom/Traverser/Traverser.php b/src/Xml/Dom/Traverser/Traverser.php index 46a8a2a..bd34f45 100644 --- a/src/Xml/Dom/Traverser/Traverser.php +++ b/src/Xml/Dom/Traverser/Traverser.php @@ -4,7 +4,7 @@ namespace VeeWee\Xml\Dom\Traverser; -use DOMNode; +use \DOM\Node; use function VeeWee\Xml\Dom\Locator\Attribute\attributes_list; use function VeeWee\Xml\Dom\Locator\Node\children; @@ -23,7 +23,7 @@ public function __construct(Visitor ... $visitors) $this->visitors = $visitors; } - public function traverse(DOMNode $node): DOMNode + public function traverse(\DOM\Node $node): \DOM\Node { $this->enterNode($node); @@ -40,14 +40,14 @@ public function traverse(DOMNode $node): DOMNode return $node; } - private function enterNode(DOMNode $node): void + private function enterNode(\DOM\Node $node): void { foreach ($this->visitors as $visitor) { $visitor->onNodeEnter($node)($node); } } - private function leaveNode(DOMNode $node): void + private function leaveNode(\DOM\Node $node): void { foreach ($this->visitors as $visitor) { $visitor->onNodeLeave($node)($node); diff --git a/src/Xml/Dom/Traverser/Visitor.php b/src/Xml/Dom/Traverser/Visitor.php index 02eb42e..bd87e50 100644 --- a/src/Xml/Dom/Traverser/Visitor.php +++ b/src/Xml/Dom/Traverser/Visitor.php @@ -4,10 +4,10 @@ namespace VeeWee\Xml\Dom\Traverser; -use DOMNode; +use \DOM\Node; interface Visitor { - public function onNodeEnter(DOMNode $node): Action; - public function onNodeLeave(DOMNode $node): Action; + public function onNodeEnter(\DOM\Node $node): Action; + public function onNodeLeave(\DOM\Node $node): Action; } diff --git a/src/Xml/Dom/Traverser/Visitor/AbstractVisitor.php b/src/Xml/Dom/Traverser/Visitor/AbstractVisitor.php index a43488c..cd15f08 100644 --- a/src/Xml/Dom/Traverser/Visitor/AbstractVisitor.php +++ b/src/Xml/Dom/Traverser/Visitor/AbstractVisitor.php @@ -4,18 +4,18 @@ namespace VeeWee\Xml\Dom\Traverser\Visitor; -use DOMNode; +use \DOM\Node; use VeeWee\Xml\Dom\Traverser\Action; use VeeWee\Xml\Dom\Traverser\Visitor; abstract class AbstractVisitor implements Visitor { - public function onNodeEnter(DOMNode $node): Action + public function onNodeEnter(\DOM\Node $node): Action { return new Action\Noop(); } - public function onNodeLeave(DOMNode $node): Action + public function onNodeLeave(\DOM\Node $node): Action { return new Action\Noop(); } diff --git a/src/Xml/Dom/Traverser/Visitor/RemoveNamespaces.php b/src/Xml/Dom/Traverser/Visitor/RemoveNamespaces.php index e9cad27..4551ad0 100644 --- a/src/Xml/Dom/Traverser/Visitor/RemoveNamespaces.php +++ b/src/Xml/Dom/Traverser/Visitor/RemoveNamespaces.php @@ -3,8 +3,8 @@ namespace VeeWee\Xml\Dom\Traverser\Visitor; -use DOMNameSpaceNode; -use DOMNode; +use \DOM\NameSpaceNode; +use \DOM\Node; use VeeWee\Xml\Dom\Traverser\Action; use VeeWee\Xml\Exception\RuntimeException; use function Psl\Iter\contains; @@ -14,12 +14,12 @@ final class RemoveNamespaces extends AbstractVisitor { /** - * @var null | callable(DOMNameSpaceNode): bool + * @var null | callable(\DOM\NameSpaceNode): bool */ private $filter; /** - * @param null | callable(DOMNameSpaceNode): bool $filter + * @param null | callable(\DOM\NameSpaceNode): bool $filter */ public function __construct( ?callable $filter = null @@ -35,14 +35,14 @@ public static function all(): self public static function prefixed(): self { return new self( - static fn (DOMNameSpaceNode $node): bool => $node->prefix !== '' + static fn (\DOM\NameSpaceNode $node): bool => $node->prefix !== '' ); } public static function unprefixed(): self { return new self( - static fn (DOMNameSpaceNode $node): bool => $node->prefix === '' + static fn (\DOM\NameSpaceNode $node): bool => $node->prefix === '' ); } @@ -52,7 +52,7 @@ public static function unprefixed(): self public static function byPrefixNames(array $prefixes): self { return new self( - static fn (DOMNameSpaceNode $node): bool => contains($prefixes, $node->prefix) + static fn (\DOM\NameSpaceNode $node): bool => contains($prefixes, $node->prefix) ); } @@ -62,14 +62,14 @@ public static function byPrefixNames(array $prefixes): self public static function byNamespaceURIs(array $URIs): self { return new self( - static fn (DOMNameSpaceNode $node): bool => contains($URIs, $node->namespaceURI) + static fn (\DOM\NameSpaceNode $node): bool => contains($URIs, $node->namespaceURI) ); } /** * @throws RuntimeException */ - public function onNodeLeave(DOMNode $node): Action + public function onNodeLeave(\DOM\Node $node): Action { if (!is_element($node)) { return new Action\Noop(); diff --git a/src/Xml/Dom/Traverser/Visitor/SortAttributes.php b/src/Xml/Dom/Traverser/Visitor/SortAttributes.php index 15aeb61..20b02ce 100644 --- a/src/Xml/Dom/Traverser/Visitor/SortAttributes.php +++ b/src/Xml/Dom/Traverser/Visitor/SortAttributes.php @@ -4,8 +4,8 @@ namespace VeeWee\Xml\Dom\Traverser\Visitor; -use DOMAttr; -use DOMNode; +use \DOM\Attr; +use \DOM\Node; use VeeWee\Xml\Dom\Traverser\Action; use function VeeWee\Xml\Dom\Locator\Attribute\attributes_list; use function VeeWee\Xml\Dom\Manipulator\append; @@ -13,16 +13,16 @@ final class SortAttributes extends AbstractVisitor { - public function onNodeEnter(DOMNode $node): Action + public function onNodeEnter(\DOM\Node $node): Action { if (!is_element($node)) { return new Action\Noop(); } attributes_list($node) - ->sort(static fn (DOMAttr $a, DOMAttr $b): int => $a->nodeName <=> $b->nodeName) + ->sort(static fn (\DOM\Attr $a, \DOM\Attr $b): int => $a->nodeName <=> $b->nodeName) ->forEach( - static function (DOMAttr $attr) use ($node): void { + static function (\DOM\Attr $attr) use ($node): void { append($attr)($node); } ); diff --git a/src/Xml/Dom/Validator/Validator.php b/src/Xml/Dom/Validator/Validator.php index 8f62bfb..121f376 100644 --- a/src/Xml/Dom/Validator/Validator.php +++ b/src/Xml/Dom/Validator/Validator.php @@ -4,10 +4,10 @@ namespace VeeWee\Xml\Dom\Validator; -use DOMDocument; +use \DOM\XMLDocument; use VeeWee\Xml\ErrorHandling\Issue\IssueCollection; interface Validator { - public function __invoke(DOMDocument $document): IssueCollection; + public function __invoke(\DOM\XMLDocument $document): IssueCollection; } diff --git a/src/Xml/Dom/Validator/internal_xsd_validator.php b/src/Xml/Dom/Validator/internal_xsd_validator.php index 29fe45d..1dac61f 100644 --- a/src/Xml/Dom/Validator/internal_xsd_validator.php +++ b/src/Xml/Dom/Validator/internal_xsd_validator.php @@ -5,7 +5,7 @@ namespace VeeWee\Xml\Dom\Validator; use Closure; -use DOMDocument; +use \DOM\XMLDocument; use VeeWee\Xml\ErrorHandling\Issue\IssueCollection; use VeeWee\Xml\Xsd\Schema\Schema; use VeeWee\Xml\Xsd\Schema\SchemaCollection; @@ -14,17 +14,17 @@ /** * @param list $schemaManipulators - * @return Closure(DOMDocument): IssueCollection + * @return Closure(\DOM\XMLDocument): IssueCollection */ function internal_xsd_validator(callable ... $schemaManipulators): Closure { - return static function (DOMDocument $document) use ($schemaManipulators) : IssueCollection { + return static function (\DOM\XMLDocument $document) use ($schemaManipulators) : IssueCollection { $schemas = configure(...$schemaManipulators)(locate_all_xsd_schemas($document)); return validator_chain( ...$schemas->map( /** - * @return Closure(DOMDocument): IssueCollection + * @return Closure(\DOM\XMLDocument): IssueCollection */ static fn (Schema $schema): Closure => xsd_validator($schema->location()) ) diff --git a/src/Xml/Dom/Validator/validator_chain.php b/src/Xml/Dom/Validator/validator_chain.php index 8c26f0b..dbf3c92 100644 --- a/src/Xml/Dom/Validator/validator_chain.php +++ b/src/Xml/Dom/Validator/validator_chain.php @@ -5,21 +5,21 @@ namespace VeeWee\Xml\Dom\Validator; use Closure; -use DOMDocument; +use \DOM\XMLDocument; use VeeWee\Xml\ErrorHandling\Issue\IssueCollection; use function Psl\Iter\reduce; /** - * @param list $validators - * @return Closure(DOMDocument): IssueCollection + * @param list $validators + * @return Closure(\DOM\XMLDocument): IssueCollection */ function validator_chain(callable ... $validators): Closure { - return static fn (DOMDocument $document): IssueCollection => + return static fn (\DOM\XMLDocument $document): IssueCollection => reduce( $validators, /** - * @param callable(DOMDocument): IssueCollection $validator + * @param callable(\DOM\XMLDocument): IssueCollection $validator */ static fn (IssueCollection $issues, callable $validator): IssueCollection => new IssueCollection( diff --git a/src/Xml/Dom/Validator/xsd_validator.php b/src/Xml/Dom/Validator/xsd_validator.php index 8c06d27..6d4e97a 100644 --- a/src/Xml/Dom/Validator/xsd_validator.php +++ b/src/Xml/Dom/Validator/xsd_validator.php @@ -5,16 +5,16 @@ namespace VeeWee\Xml\Dom\Validator; use Closure; -use DOMDocument; +use \DOM\XMLDocument; use VeeWee\Xml\ErrorHandling\Issue\IssueCollection; use function VeeWee\Xml\ErrorHandling\detect_issues; /** - * @return Closure(DOMDocument): IssueCollection + * @return Closure(\DOM\XMLDocument): IssueCollection */ function xsd_validator(string $xsd): Closure { - return static function (DOMDocument $document) use ($xsd): IssueCollection { + return static function (\DOM\XMLDocument $document) use ($xsd): IssueCollection { [$_, $issues] = detect_issues(static fn () => $document->schemaValidate($xsd)); return $issues; diff --git a/src/Xml/Dom/Xpath.php b/src/Xml/Dom/Xpath.php index ff3907a..ab1baee 100644 --- a/src/Xml/Dom/Xpath.php +++ b/src/Xml/Dom/Xpath.php @@ -4,8 +4,8 @@ namespace VeeWee\Xml\Dom; -use DOMNode; -use DOMXPath; +use \DOM\Node; +use \DOM\XPath as DOMXPath; use InvalidArgumentException; use Psl\Type\TypeInterface; use VeeWee\Xml\Dom\Collection\NodeList; @@ -38,7 +38,7 @@ public static function fromDocument(Document $document, callable ... $configurat * @throws RuntimeException * @throws InvalidArgumentException */ - public static function fromUnsafeNode(DOMNode $node, callable ... $configurators): self + public static function fromUnsafeNode(\DOM\Node $node, callable ... $configurators): self { return self::fromDocument( Document::fromUnsafeDocument( @@ -50,7 +50,7 @@ public static function fromUnsafeNode(DOMNode $node, callable ... $configurators /** * @template T - * @param callable(DOMXpath): T $locator + * @param callable(DOMXPath): T $locator * * @return T * @throws RuntimeException @@ -62,9 +62,9 @@ public function locate(callable $locator) /** * @throws RuntimeException - * @return NodeList + * @return NodeList<\DOM\Node> */ - public function query(string $expression, DOMNode $contextNode = null): NodeList + public function query(string $expression, \DOM\Node $contextNode = null): NodeList { return $this->locate(query($expression, $contextNode)); } @@ -73,7 +73,7 @@ public function query(string $expression, DOMNode $contextNode = null): NodeList * @throws RuntimeException * @throws InvalidArgumentException */ - public function querySingle(string $expression, DOMNode $contextNode = null): DOMNode + public function querySingle(string $expression, \DOM\Node $contextNode = null): \DOM\Node { return $this->locate(query_single($expression, $contextNode)); } @@ -86,7 +86,7 @@ public function querySingle(string $expression, DOMNode $contextNode = null): DO * @return T * @throws RuntimeException */ - public function evaluate(string $expression, TypeInterface $type, DOMNode $contextNode = null) + public function evaluate(string $expression, TypeInterface $type, \DOM\Node $contextNode = null) { return $this->locate(evaluate($expression, $type, $contextNode)); } diff --git a/src/Xml/Dom/Xpath/Configurator/Configurator.php b/src/Xml/Dom/Xpath/Configurator/Configurator.php index 15f4342..23d3c2f 100644 --- a/src/Xml/Dom/Xpath/Configurator/Configurator.php +++ b/src/Xml/Dom/Xpath/Configurator/Configurator.php @@ -4,9 +4,9 @@ namespace VeeWee\Xml\Dom\Xpath\Configurator; -use DOMXPath; +use \DOM\XPath; interface Configurator { - public function __invoke(DOMXPath $xpath): DOMXPath; + public function __invoke(\DOM\XPath $xpath): \DOM\XPath; } diff --git a/src/Xml/Dom/Xpath/Configurator/all_functions.php b/src/Xml/Dom/Xpath/Configurator/all_functions.php index 0dbf2a8..4ab487e 100644 --- a/src/Xml/Dom/Xpath/Configurator/all_functions.php +++ b/src/Xml/Dom/Xpath/Configurator/all_functions.php @@ -5,14 +5,14 @@ namespace VeeWee\Xml\Dom\Xpath\Configurator; use Closure; -use DOMXPath; +use \DOM\XPath; /** - * @return Closure(DOMXPath): DOMXPath + * @return Closure(\DOM\XPath): \DOM\XPath */ function all_functions(): Closure { - return static function (DOMXPath $xpath): DOMXPath { + return static function (\DOM\XPath $xpath): \DOM\XPath { php_namespace()($xpath); $xpath->registerPhpFunctions(); diff --git a/src/Xml/Dom/Xpath/Configurator/functions.php b/src/Xml/Dom/Xpath/Configurator/functions.php index 3a1e02f..ad43c69 100644 --- a/src/Xml/Dom/Xpath/Configurator/functions.php +++ b/src/Xml/Dom/Xpath/Configurator/functions.php @@ -5,16 +5,16 @@ namespace VeeWee\Xml\Dom\Xpath\Configurator; use Closure; -use DOMXPath; +use \DOM\XPath; /** * @param non-empty-list $functions * - * @return Closure(DOMXPath): DOMXPath + * @return Closure(\DOM\XPath): \DOM\XPath */ function functions(array $functions): Closure { - return static function (DOMXPath $xpath) use ($functions) : DOMXPath { + return static function (\DOM\XPath $xpath) use ($functions) : \DOM\XPath { php_namespace()($xpath); $xpath->registerPhpFunctions($functions); diff --git a/src/Xml/Dom/Xpath/Configurator/namespaces.php b/src/Xml/Dom/Xpath/Configurator/namespaces.php index 869169d..e5d9a22 100644 --- a/src/Xml/Dom/Xpath/Configurator/namespaces.php +++ b/src/Xml/Dom/Xpath/Configurator/namespaces.php @@ -5,16 +5,16 @@ namespace VeeWee\Xml\Dom\Xpath\Configurator; use Closure; -use DOMXPath; +use \DOM\XPath; /** * @param array $namespaces * - * @return Closure(DOMXPath): DOMXPath + * @return Closure(\DOM\XPath): \DOM\XPath */ function namespaces(array $namespaces): Closure { - return static function (DOMXPath $xpath) use ($namespaces) : DOMXPath { + return static function (\DOM\XPath $xpath) use ($namespaces) : \DOM\XPath { foreach ($namespaces as $prefix => $namespaceURI) { $xpath->registerNamespace($prefix, $namespaceURI); } diff --git a/src/Xml/Dom/Xpath/Configurator/php_namespace.php b/src/Xml/Dom/Xpath/Configurator/php_namespace.php index 2c46818..91eedd8 100644 --- a/src/Xml/Dom/Xpath/Configurator/php_namespace.php +++ b/src/Xml/Dom/Xpath/Configurator/php_namespace.php @@ -5,15 +5,15 @@ namespace VeeWee\Xml\Dom\Xpath\Configurator; use Closure; -use DOMXPath; +use \DOM\XPath; use VeeWee\Xml\Xmlns\Xmlns; /** - * @return Closure(DOMXPath): DOMXPath + * @return Closure(\DOM\XPath): \DOM\XPath */ function php_namespace(): Closure { - return static function (DOMXPath $xpath): DOMXPath { + return static function (\DOM\XPath $xpath): \DOM\XPath { namespaces(['php' => Xmlns::phpXpath()->value()])($xpath); return $xpath; diff --git a/src/Xml/Dom/Xpath/Locator/Locator.php b/src/Xml/Dom/Xpath/Locator/Locator.php index b17c909..c59dacc 100644 --- a/src/Xml/Dom/Xpath/Locator/Locator.php +++ b/src/Xml/Dom/Xpath/Locator/Locator.php @@ -4,7 +4,7 @@ namespace VeeWee\Xml\Dom\Xpath\Locator; -use DOMXPath; +use \DOM\XPath; /** * @template T @@ -14,5 +14,5 @@ interface Locator /** * @return T */ - public function __invoke(DOMXPath $xpath): mixed; + public function __invoke(\DOM\XPath $xpath): mixed; } diff --git a/src/Xml/Dom/Xpath/Locator/evaluate.php b/src/Xml/Dom/Xpath/Locator/evaluate.php index 4ba61be..9f9e6a3 100644 --- a/src/Xml/Dom/Xpath/Locator/evaluate.php +++ b/src/Xml/Dom/Xpath/Locator/evaluate.php @@ -4,8 +4,8 @@ namespace VeeWee\Xml\Dom\Xpath\Locator; use Closure; -use DOMNode; -use DOMXPath; +use \DOM\Node; +use \DOM\XPath; use Psl\Type\TypeInterface; use function VeeWee\Xml\ErrorHandling\disallow_issues; use function VeeWee\Xml\ErrorHandling\disallow_libxml_false_returns; @@ -15,15 +15,15 @@ * * @param TypeInterface $type * - * @return Closure(DOMXPath): T + * @return Closure(\DOM\XPath): T */ -function evaluate(string $query, TypeInterface $type, DOMNode $node = null): Closure +function evaluate(string $query, TypeInterface $type, \DOM\Node $node = null): Closure { return /** * @return T */ - static function (DOMXPath $xpath) use ($query, $node, $type) { + static function (\DOM\XPath $xpath) use ($query, $node, $type) { $node = $node ?? $xpath->document->documentElement; return disallow_issues( diff --git a/src/Xml/Dom/Xpath/Locator/query.php b/src/Xml/Dom/Xpath/Locator/query.php index 35d1ac2..a60562e 100644 --- a/src/Xml/Dom/Xpath/Locator/query.php +++ b/src/Xml/Dom/Xpath/Locator/query.php @@ -4,20 +4,20 @@ namespace VeeWee\Xml\Dom\Xpath\Locator; use Closure; -use DOMNode; -use DOMNodeList; -use DOMXPath; +use \DOM\Node; +use \DOM\NodeList as DOMNodeList; +use \DOM\XPath; use VeeWee\Xml\Dom\Collection\NodeList; use function VeeWee\Xml\Dom\Assert\assert_dom_node_list; use function VeeWee\Xml\ErrorHandling\disallow_issues; use function VeeWee\Xml\ErrorHandling\disallow_libxml_false_returns; /** - * @return Closure(DOMXPath): NodeList + * @return Closure(\DOM\XPath): NodeList<\DOM\Node> */ -function query(string $query, DOMNode $node = null): Closure +function query(string $query, \DOM\Node $node = null): Closure { - return static function (DOMXPath $xpath) use ($query, $node): NodeList { + return static function (\DOM\XPath $xpath) use ($query, $node): NodeList { $node = $node ?? $xpath->document->documentElement; $list = disallow_issues( diff --git a/src/Xml/Dom/Xpath/Locator/query_single.php b/src/Xml/Dom/Xpath/Locator/query_single.php index 4e8213b..649c86a 100644 --- a/src/Xml/Dom/Xpath/Locator/query_single.php +++ b/src/Xml/Dom/Xpath/Locator/query_single.php @@ -4,9 +4,9 @@ namespace VeeWee\Xml\Dom\Xpath\Locator; use Closure; -use DOMNode; -use DOMNodeList; -use DOMXPath; +use \DOM\Node; +use \DOM\NodeList; +use \DOM\XPath; use InvalidArgumentException; use VeeWee\Xml\Exception\RuntimeException; use Webmozart\Assert\Assert; @@ -16,19 +16,19 @@ use function VeeWee\Xml\ErrorHandling\disallow_libxml_false_returns; /** - * @return Closure(DOMXPath): DOMNode + * @return Closure(\DOM\XPath): \DOM\Node */ -function query_single(string $query, DOMNode $node = null): Closure +function query_single(string $query, \DOM\Node $node = null): Closure { return /** * @throws InvalidArgumentException * @throws RuntimeException */ - static function (DOMXPath $xpath) use ($query, $node): DOMNode { + static function (\DOM\XPath $xpath) use ($query, $node): \DOM\Node { $node = $node ?? $xpath->document->documentElement; $list = disallow_issues( - static fn (): DOMNodeList => assert_dom_node_list( + static fn (): \DOM\NodeList => assert_dom_node_list( disallow_libxml_false_returns( $xpath->query($query, $node), 'Failed querying XPath query: '.$query diff --git a/src/Xml/Encoding/Internal/Decoder/Builder/attribute.php b/src/Xml/Encoding/Internal/Decoder/Builder/attribute.php index 7e87c21..6fa766c 100644 --- a/src/Xml/Encoding/Internal/Decoder/Builder/attribute.php +++ b/src/Xml/Encoding/Internal/Decoder/Builder/attribute.php @@ -4,12 +4,12 @@ namespace VeeWee\Xml\Encoding\Internal\Decoder\Builder; -use DOMAttr; +use \DOM\Attr; /** * @psalm-internal VeeWee\Xml\Encoding */ -function attribute(DOMAttr $attribute): array +function attribute(\DOM\Attr $attribute): array { return [ name($attribute) => $attribute->nodeValue, diff --git a/src/Xml/Encoding/Internal/Decoder/Builder/attributes.php b/src/Xml/Encoding/Internal/Decoder/Builder/attributes.php index 8dbd55e..0eaa804 100644 --- a/src/Xml/Encoding/Internal/Decoder/Builder/attributes.php +++ b/src/Xml/Encoding/Internal/Decoder/Builder/attributes.php @@ -4,8 +4,8 @@ namespace VeeWee\Xml\Encoding\Internal\Decoder\Builder; -use DOMAttr; -use DOMElement; +use \DOM\Attr; +use \DOM\Element; use function Psl\Dict\filter; use function Psl\Dict\merge; use function Psl\Iter\reduce; @@ -13,12 +13,12 @@ /** * @psalm-internal VeeWee\Xml\Encoding */ -function attributes(DOMElement $element): array +function attributes(\DOM\Element $element): array { return filter([ '@attributes' => reduce( $element->attributes, - static fn (array $attributes, DOMAttr $attr): array + static fn (array $attributes, \DOM\Attr $attr): array => merge($attributes, attribute($attr)), [] ) diff --git a/src/Xml/Encoding/Internal/Decoder/Builder/element.php b/src/Xml/Encoding/Internal/Decoder/Builder/element.php index 76f57e0..d9bf991 100644 --- a/src/Xml/Encoding/Internal/Decoder/Builder/element.php +++ b/src/Xml/Encoding/Internal/Decoder/Builder/element.php @@ -4,7 +4,7 @@ namespace VeeWee\Xml\Encoding\Internal\Decoder\Builder; -use DOMElement; +use \DOM\Element; use VeeWee\Xml\Exception\RuntimeException; use function Psl\Dict\filter; use function Psl\Dict\merge; @@ -14,7 +14,7 @@ * @return array * @throws RuntimeException */ -function element(DOMElement $element): array +function element(\DOM\Element $element): array { $name = name($element); $children = grouped_children($element); diff --git a/src/Xml/Encoding/Internal/Decoder/Builder/group_child_elements.php b/src/Xml/Encoding/Internal/Decoder/Builder/group_child_elements.php index 61d5511..e1567f5 100644 --- a/src/Xml/Encoding/Internal/Decoder/Builder/group_child_elements.php +++ b/src/Xml/Encoding/Internal/Decoder/Builder/group_child_elements.php @@ -4,15 +4,15 @@ namespace VeeWee\Xml\Encoding\Internal\Decoder\Builder; -use DOMElement; +use \DOM\Element; use function VeeWee\Xml\Dom\Locator\Element\children; /** - * @psalm-type GroupedElements=array> + * @psalm-type GroupedElements=array> * @psalm-internal VeeWee\Xml\Encoding * @return GroupedElements */ -function group_child_elements(DOMElement $element): array +function group_child_elements(\DOM\Element $element): array { /** @var GroupedElements $grouped */ $grouped = []; diff --git a/src/Xml/Encoding/Internal/Decoder/Builder/grouped_children.php b/src/Xml/Encoding/Internal/Decoder/Builder/grouped_children.php index b885ee7..03c2be2 100644 --- a/src/Xml/Encoding/Internal/Decoder/Builder/grouped_children.php +++ b/src/Xml/Encoding/Internal/Decoder/Builder/grouped_children.php @@ -4,7 +4,7 @@ namespace VeeWee\Xml\Encoding\Internal\Decoder\Builder; -use DOMElement; +use \DOM\Element; use function Psl\Dict\map; use function Psl\Dict\merge; use function Psl\Iter\reduce_with_keys; @@ -13,21 +13,21 @@ * @psalm-internal VeeWee\Xml\Encoding * */ -function grouped_children(DOMElement $element): array +function grouped_children(\DOM\Element $element): array { return reduce_with_keys( group_child_elements($element), /** * @param array $children - * @param DOMElement|list $child + * @param \DOM\Element|list<\DOM\Element> $child * @return array */ - static fn (array $children, string $name, DOMElement|array $child): array + static fn (array $children, string $name, \DOM\Element|array $child): array => merge( $children, [ $name => is_array($child) - ? [...map($child, static fn (DOMElement $child): array|string + ? [...map($child, static fn (\DOM\Element $child): array|string => unwrap_element(element($child)))] : unwrap_element(element($child)) ] diff --git a/src/Xml/Encoding/Internal/Decoder/Builder/name.php b/src/Xml/Encoding/Internal/Decoder/Builder/name.php index bc8154e..9c13b1b 100644 --- a/src/Xml/Encoding/Internal/Decoder/Builder/name.php +++ b/src/Xml/Encoding/Internal/Decoder/Builder/name.php @@ -4,12 +4,12 @@ namespace VeeWee\Xml\Encoding\Internal\Decoder\Builder; -use DOMNode; +use \DOM\Node; /** * @psalm-internal VeeWee\Xml\Encoding */ -function name(DOMNode $node): string +function name(\DOM\Node $node): string { return $node->nodeName; } diff --git a/src/Xml/Encoding/Internal/Decoder/Builder/namespaces.php b/src/Xml/Encoding/Internal/Decoder/Builder/namespaces.php index deca682..54dd1c8 100644 --- a/src/Xml/Encoding/Internal/Decoder/Builder/namespaces.php +++ b/src/Xml/Encoding/Internal/Decoder/Builder/namespaces.php @@ -4,8 +4,8 @@ namespace VeeWee\Xml\Encoding\Internal\Decoder\Builder; -use DOMElement; -use DOMNameSpaceNode; +use \DOM\Element; +use \DOM\NameSpaceNode; use VeeWee\Xml\Exception\RuntimeException; use function Psl\Dict\filter; use function Psl\Dict\merge; @@ -16,11 +16,11 @@ * @psalm-suppress RedundantCast * @throws RuntimeException */ -function namespaces(DOMElement $element): array +function namespaces(\DOM\Element $element): array { return filter([ '@namespaces' => xmlns_attributes_list($element)->reduce( - static fn (array $namespaces, DOMNameSpaceNode $node) + static fn (array $namespaces, \DOM\NameSpaceNode $node) => $node->namespaceURI ? merge($namespaces, [(string) $node->prefix => $node->namespaceURI]) : $namespaces, diff --git a/src/Xml/Encoding/Internal/Encoder/Builder/children.php b/src/Xml/Encoding/Internal/Encoder/Builder/children.php index 56cb864..9664b06 100644 --- a/src/Xml/Encoding/Internal/Encoder/Builder/children.php +++ b/src/Xml/Encoding/Internal/Encoder/Builder/children.php @@ -5,7 +5,7 @@ namespace VeeWee\Xml\Encoding\Internal\Encoder\Builder; use Closure; -use DOMElement; +use \DOM\Element; use function Psl\Dict\map; use function VeeWee\Xml\Dom\Builder\children as buildChildren; use function VeeWee\Xml\Dom\Builder\element as elementBuilder; @@ -16,7 +16,7 @@ * * @psalm-suppress LessSpecificReturnStatement, MoreSpecificReturnType * - * @return Closure(DOMElement): DOMElement + * @return Closure(\DOM\Element): \DOM\Element */ function children(string $name, array $children): Closure { @@ -24,7 +24,7 @@ function children(string $name, array $children): Closure ...map( $children, /** - * @return Closure(DOMElement): DOMElement + * @return Closure(\DOM\Element): \DOM\Element */ static fn (array|string $data): Closure => is_array($data) ? element($name, $data) diff --git a/src/Xml/Encoding/Internal/Encoder/Builder/element.php b/src/Xml/Encoding/Internal/Encoder/Builder/element.php index e7c6277..f09da83 100644 --- a/src/Xml/Encoding/Internal/Encoder/Builder/element.php +++ b/src/Xml/Encoding/Internal/Encoder/Builder/element.php @@ -5,7 +5,7 @@ namespace VeeWee\Xml\Encoding\Internal\Encoder\Builder; use Closure; -use DOMElement; +use \DOM\Element; use Psl\Exception\InvariantViolationException; use Psl\Type\Exception\AssertException; use function Psl\Dict\filter_keys; @@ -28,7 +28,7 @@ * @psalm-internal VeeWee\Xml\Encoding * @psalm-suppress LessSpecificReturnStatement, MoreSpecificReturnType * - * @return Closure(DOMElement): DOMElement + * @return Closure(\DOM\Element): \DOM\Element * * @throws AssertException * @throws InvariantViolationException @@ -49,7 +49,7 @@ function element(string $name, array $data): Closure $currentNamespace = $namespaces[''] ?? null; $namedNamespaces = filter_keys($namespaces ?? []); - /** @var list $children */ + /** @var list $children */ $children = filter_nulls([ $attributes !== null ? attributes($attributes) : null, $namedNamespaces ? xmlns_attributes($namedNamespaces) : null, @@ -59,7 +59,7 @@ function element(string $name, array $data): Closure $element, /** * @param string|array $value - * @return Closure(DOMElement): DOMElement + * @return Closure(\DOM\Element): \DOM\Element */ static fn (string $name, string|array $value): Closure => parent_node($name, $value) diff --git a/src/Xml/Encoding/Internal/Encoder/Builder/parent_node.php b/src/Xml/Encoding/Internal/Encoder/Builder/parent_node.php index 8127a25..d44a568 100644 --- a/src/Xml/Encoding/Internal/Encoder/Builder/parent_node.php +++ b/src/Xml/Encoding/Internal/Encoder/Builder/parent_node.php @@ -5,8 +5,8 @@ namespace VeeWee\Xml\Encoding\Internal\Encoder\Builder; use Closure; -use DOMElement; -use DOMNode; +use \DOM\Element; +use \DOM\Node; use Psl\Exception\InvariantViolationException; use Psl\Type\Exception\AssertException; use function VeeWee\Xml\Dom\Builder\children as buildChildren; @@ -17,7 +17,7 @@ * @psalm-internal VeeWee\Xml\Encoding * @psalm-suppress LessSpecificReturnStatement, MoreSpecificReturnType * - * @return Closure(DOMNode): DOMElement + * @return Closure(\DOM\Node): \DOM\Element * * @throws AssertException * @throws InvariantViolationException diff --git a/src/Xml/Encoding/Internal/Encoder/Builder/root.php b/src/Xml/Encoding/Internal/Encoder/Builder/root.php index aaed37f..14fd996 100644 --- a/src/Xml/Encoding/Internal/Encoder/Builder/root.php +++ b/src/Xml/Encoding/Internal/Encoder/Builder/root.php @@ -5,8 +5,8 @@ namespace VeeWee\Xml\Encoding\Internal\Encoder\Builder; use Closure; -use DOMDocument; -use DOMNode; +use \DOM\XMLDocument; +use \DOM\Node; use Psl\Exception\InvariantViolationException; use VeeWee\Xml\Encoding\Exception\EncodingException; use function Psl\Dict\map_with_key; @@ -14,7 +14,7 @@ /** * @psalm-internal VeeWee\Xml\Encoding - * @return Closure(DOMDocument): list + * @return Closure(\DOM\XMLDocument): list<\DOM\Node> * * @throws EncodingException * @throws InvariantViolationException diff --git a/src/Xml/Encoding/document_encode.php b/src/Xml/Encoding/document_encode.php index 2d114aa..ca633f9 100644 --- a/src/Xml/Encoding/document_encode.php +++ b/src/Xml/Encoding/document_encode.php @@ -4,7 +4,7 @@ namespace VeeWee\Xml\Encoding; -use DOMDocument; +use \DOM\XMLDocument as DOMDocument; use VeeWee\Xml\Dom\Document; use VeeWee\Xml\Encoding\Exception\EncodingException; use function VeeWee\Xml\Encoding\Internal\Encoder\Builder\normalize_data; diff --git a/src/Xml/Encoding/element_decode.php b/src/Xml/Encoding/element_decode.php index 490f768..f905258 100644 --- a/src/Xml/Encoding/element_decode.php +++ b/src/Xml/Encoding/element_decode.php @@ -4,8 +4,8 @@ namespace VeeWee\Xml\Encoding; -use DOMDocument; -use DOMElement; +use \DOM\XMLDocument as DOMDocument; +use \DOM\Element; use VeeWee\Xml\Dom\Document; use VeeWee\Xml\Encoding\Exception\EncodingException; use function VeeWee\Xml\Dom\Locator\document_element; @@ -17,7 +17,7 @@ * * @throws EncodingException */ -function element_decode(DOMElement $element, callable ... $configurators): array +function element_decode(\DOM\Element $element, callable ... $configurators): array { return wrap_exception( static function () use ($element, $configurators): array { diff --git a/src/Xml/Encoding/element_encode.php b/src/Xml/Encoding/element_encode.php index 8a6dea7..072a289 100644 --- a/src/Xml/Encoding/element_encode.php +++ b/src/Xml/Encoding/element_encode.php @@ -4,7 +4,7 @@ namespace VeeWee\Xml\Encoding; -use DOMDocument; +use \DOM\XMLDocument as DOMDocument; use VeeWee\Xml\Encoding\Exception\EncodingException; use function VeeWee\Xml\Dom\Locator\document_element; use function VeeWee\Xml\Dom\Mapper\xml_string; diff --git a/src/Xml/Encoding/typed.php b/src/Xml/Encoding/typed.php index a07e859..aee6199 100644 --- a/src/Xml/Encoding/typed.php +++ b/src/Xml/Encoding/typed.php @@ -4,7 +4,7 @@ namespace VeeWee\Xml\Encoding; -use DOMDocument; +use \DOM\XMLDocument; use Psl\Type\Exception\CoercionException; use Psl\Type\TypeInterface; use VeeWee\Xml\Encoding\Exception\EncodingException; @@ -14,7 +14,7 @@ * * @psalm-param non-empty-string $xml * @psalm-param TypeInterface $type - * @param list $configurators + * @param list $configurators * * @return T * diff --git a/src/Xml/Encoding/xml_decode.php b/src/Xml/Encoding/xml_decode.php index 7a3a6c9..a3d6702 100644 --- a/src/Xml/Encoding/xml_decode.php +++ b/src/Xml/Encoding/xml_decode.php @@ -4,7 +4,7 @@ namespace VeeWee\Xml\Encoding; -use DOMDocument; +use \DOM\XMLDocument as DOMDocument; use VeeWee\Xml\Dom\Document; use VeeWee\Xml\Encoding\Exception\EncodingException; use function VeeWee\Xml\Dom\Locator\document_element; diff --git a/src/Xml/Encoding/xml_encode.php b/src/Xml/Encoding/xml_encode.php index a1da5a9..2da3896 100644 --- a/src/Xml/Encoding/xml_encode.php +++ b/src/Xml/Encoding/xml_encode.php @@ -4,7 +4,7 @@ namespace VeeWee\Xml\Encoding; -use DOMDocument; +use \DOM\XMLDocument as DOMDocument; use VeeWee\Xml\Encoding\Exception\EncodingException; use function VeeWee\Xml\Encoding\Internal\wrap_exception; diff --git a/src/Xml/Reader/MatchingNode.php b/src/Xml/Reader/MatchingNode.php index 49b5a9c..5c32bc6 100644 --- a/src/Xml/Reader/MatchingNode.php +++ b/src/Xml/Reader/MatchingNode.php @@ -3,7 +3,7 @@ namespace VeeWee\Xml\Reader; -use DOMDocument; +use \DOM\XMLDocument as DOMDocument; use VeeWee\Xml\Dom\Document; use VeeWee\Xml\Encoding\Exception\EncodingException; use VeeWee\Xml\Exception\RuntimeException; diff --git a/tests/Xml/Dom/Assert/AssertCDataTest.php b/tests/Xml/Dom/Assert/AssertCDataTest.php index 283e1c0..a0739e5 100644 --- a/tests/Xml/Dom/Assert/AssertCDataTest.php +++ b/tests/Xml/Dom/Assert/AssertCDataTest.php @@ -4,7 +4,7 @@ namespace VeeWee\Tests\Xml\Dom\Assert; -use DOMNode; +use \DOM\Node; use PHPUnit\Framework\TestCase; use Psl\Type\Exception\AssertException; use VeeWee\Xml\Dom\Document; @@ -16,7 +16,7 @@ final class AssertCDataTest extends TestCase * * @dataProvider provideTestCases */ - public function test_it_knows_cdata(?DOMNode $node, bool $expected): void + public function test_it_knows_cdata(?\DOM\Node $node, bool $expected): void { if (!$expected) { $this->expectException(AssertException::class); diff --git a/tests/Xml/Dom/Assert/AssertDocumentTest.php b/tests/Xml/Dom/Assert/AssertDocumentTest.php index 39c2482..0bd1fd8 100644 --- a/tests/Xml/Dom/Assert/AssertDocumentTest.php +++ b/tests/Xml/Dom/Assert/AssertDocumentTest.php @@ -4,7 +4,7 @@ namespace VeeWee\Tests\Xml\Dom\Assert; -use DOMNode; +use \DOM\Node; use PHPUnit\Framework\TestCase; use Psl\Type\Exception\AssertException; use VeeWee\Xml\Dom\Document; @@ -16,7 +16,7 @@ final class AssertDocumentTest extends TestCase * * @dataProvider provideTestCases */ - public function test_it_knows_documents(?DOMNode $node, bool $expected): void + public function test_it_knows_documents(?\DOM\Node $node, bool $expected): void { if (!$expected) { $this->expectException(AssertException::class); diff --git a/tests/Xml/Dom/Assert/AssertElementTest.php b/tests/Xml/Dom/Assert/AssertElementTest.php index f98100f..4bd31e4 100644 --- a/tests/Xml/Dom/Assert/AssertElementTest.php +++ b/tests/Xml/Dom/Assert/AssertElementTest.php @@ -4,7 +4,7 @@ namespace VeeWee\Tests\Xml\Dom\Assert; -use DOMNode; +use \DOM\Node; use PHPUnit\Framework\TestCase; use Psl\Type\Exception\AssertException; use VeeWee\Xml\Dom\Document; @@ -16,7 +16,7 @@ final class AssertElementTest extends TestCase * * @dataProvider provideTestCases */ - public function test_it_knows_elements(?DOMNode $node, bool $expected): void + public function test_it_knows_elements(?\DOM\Node $node, bool $expected): void { if (!$expected) { $this->expectException(AssertException::class); diff --git a/tests/Xml/Dom/Builder/AttributesTest.php b/tests/Xml/Dom/Builder/AttributesTest.php index 111a2ea..3701fd2 100644 --- a/tests/Xml/Dom/Builder/AttributesTest.php +++ b/tests/Xml/Dom/Builder/AttributesTest.php @@ -4,8 +4,9 @@ namespace VeeWee\Tests\Xml\Dom\Builder; -use DOMDocument; +use \DOM\XMLDocument; use PHPUnit\Framework\TestCase; +use VeeWee\Xml\Dom\Document; use function VeeWee\Xml\Dom\Builder\attribute; use function VeeWee\Xml\Dom\Builder\attributes; use function VeeWee\Xml\Dom\Builder\element; @@ -16,7 +17,7 @@ final class AttributesTest extends TestCase { public function test_it_can_build_an_element_with_attributes(): void { - $doc = new DOMDocument(); + $doc = Document::empty()->toUnsafeDocument(); $ns = 'https://namespace.com'; $node = element( diff --git a/tests/Xml/Dom/Builder/CdataTest.php b/tests/Xml/Dom/Builder/CdataTest.php index e2915d6..9b4ad5a 100644 --- a/tests/Xml/Dom/Builder/CdataTest.php +++ b/tests/Xml/Dom/Builder/CdataTest.php @@ -4,9 +4,10 @@ namespace VeeWee\Tests\Xml\Dom\Builder; -use DOMCdataSection; -use DOMDocument; +use \DOM\CdataSection; +use \DOM\XMLDocument; use PHPUnit\Framework\TestCase; +use VeeWee\Xml\Dom\Document; use function Psl\Fun\identity; use function VeeWee\Xml\Dom\Builder\cdata; use function VeeWee\Xml\Dom\Mapper\xml_string; @@ -15,20 +16,20 @@ final class CdataTest extends TestCase { public function test_it_can_build_cdata(): void { - $doc = new DOMDocument(); + $doc = Document::empty()->toUnsafeDocument(); $node = cdata($data = 'hello')($doc); - static::assertInstanceOf(DOMCdataSection::class, $node); + static::assertInstanceOf(\DOM\CdataSection::class, $node); static::assertSame($data, $node->textContent); static::assertSame(xml_string()($node), ''); } public function test_it_can_build_cdata_with_configurators(): void { - $doc = new DOMDocument(); + $doc = Document::empty()->toUnsafeDocument(); $node = cdata($data = 'hello', identity())($doc); - static::assertInstanceOf(DOMCdataSection::class, $node); + static::assertInstanceOf(\DOM\CdataSection::class, $node); static::assertSame($data, $node->textContent); } } diff --git a/tests/Xml/Dom/Builder/ChildrenTest.php b/tests/Xml/Dom/Builder/ChildrenTest.php index 6f24d5c..c97ebc0 100644 --- a/tests/Xml/Dom/Builder/ChildrenTest.php +++ b/tests/Xml/Dom/Builder/ChildrenTest.php @@ -4,8 +4,9 @@ namespace VeeWee\Tests\Xml\Dom\Builder; -use DOMDocument; +use \DOM\XMLDocument; use PHPUnit\Framework\TestCase; +use VeeWee\Xml\Dom\Document; use function VeeWee\Xml\Dom\Builder\cdata; use function VeeWee\Xml\Dom\Builder\children; use function VeeWee\Xml\Dom\Builder\element; @@ -15,7 +16,7 @@ final class ChildrenTest extends TestCase { public function test_it_can_build_document_children(): void { - $doc = new DOMDocument(); + $doc = Document::empty()->toUnsafeDocument(); $actual = children( element('world1'), element('world2') @@ -31,7 +32,7 @@ public function test_it_can_build_document_children(): void public function test_it_can_build_an_element_with_children(): void { - $doc = new DOMDocument(); + $doc = Document::empty()->toUnsafeDocument(); $node = element( 'hello', children( @@ -52,7 +53,7 @@ public function test_it_can_build_an_element_with_children(): void public function test_it_can_add_cdata(): void { - $doc = new DOMDocument(); + $doc = Document::empty()->toUnsafeDocument(); $node = element( 'hello', children( diff --git a/tests/Xml/Dom/Builder/ElementTest.php b/tests/Xml/Dom/Builder/ElementTest.php index 5f7c4f9..c1629a3 100644 --- a/tests/Xml/Dom/Builder/ElementTest.php +++ b/tests/Xml/Dom/Builder/ElementTest.php @@ -4,8 +4,9 @@ namespace VeeWee\Tests\Xml\Dom\Builder; -use DOMDocument; +use \DOM\XMLDocument; use PHPUnit\Framework\TestCase; +use VeeWee\Xml\Dom\Document; use function Psl\Fun\identity; use function VeeWee\Xml\Dom\Builder\element; use function VeeWee\Xml\Dom\Builder\value; @@ -14,7 +15,7 @@ final class ElementTest extends TestCase { public function test_it_can_build_an_element(): void { - $doc = new DOMDocument(); + $doc = Document::empty()->toUnsafeDocument(); $node = element('hello')($doc); static::assertSame('hello', $node->nodeName); @@ -24,7 +25,7 @@ public function test_it_can_build_an_element(): void public function test_it_can_build_an_element_with_configurators(): void { - $doc = new DOMDocument(); + $doc = Document::empty()->toUnsafeDocument(); $node = element('hello', identity())($doc); static::assertSame('hello', $node->nodeName); @@ -34,7 +35,7 @@ public function test_it_can_build_an_element_with_configurators(): void public function test_it_can_build_an_element_with_value(): void { - $doc = new DOMDocument(); + $doc = Document::empty()->toUnsafeDocument(); $node = element('hello', value('world'))($doc); static::assertSame('hello', $node->nodeName); diff --git a/tests/Xml/Dom/Builder/EscapedValueTest.php b/tests/Xml/Dom/Builder/EscapedValueTest.php index 0305162..96957b5 100644 --- a/tests/Xml/Dom/Builder/EscapedValueTest.php +++ b/tests/Xml/Dom/Builder/EscapedValueTest.php @@ -4,8 +4,9 @@ namespace VeeWee\Tests\Xml\Dom\Builder; -use DOMDocument; +use \DOM\XMLDocument; use PHPUnit\Framework\TestCase; +use VeeWee\Xml\Dom\Document; use function VeeWee\Xml\Dom\Builder\element; use function VeeWee\Xml\Dom\Builder\escaped_value; use function VeeWee\Xml\Dom\Mapper\xml_string; @@ -14,7 +15,7 @@ final class EscapedValueTest extends TestCase { public function test_it_can_build_an_element_with_html_value(): void { - $doc = new DOMDocument(); + $doc = Document::empty()->toUnsafeDocument(); $node = element('hello', escaped_value(''))($doc); static::assertSame('', $node->nodeValue); diff --git a/tests/Xml/Dom/Builder/NamespacedAttributeTest.php b/tests/Xml/Dom/Builder/NamespacedAttributeTest.php index 695d6df..6b78883 100644 --- a/tests/Xml/Dom/Builder/NamespacedAttributeTest.php +++ b/tests/Xml/Dom/Builder/NamespacedAttributeTest.php @@ -4,9 +4,10 @@ namespace VeeWee\Tests\Xml\Dom\Builder; -use DOMDocument; +use \DOM\XMLDocument; use InvalidArgumentException; use PHPUnit\Framework\TestCase; +use VeeWee\Xml\Dom\Document; use function VeeWee\Xml\Dom\Builder\element; use function VeeWee\Xml\Dom\Builder\namespaced_attribute; @@ -14,7 +15,7 @@ final class NamespacedAttributeTest extends TestCase { public function test_it_throws_exception_if_the_attribute_name_is_not_qualified(): void { - $doc = new DOMDocument(); + $doc = Document::empty()->toUnsafeDocument(); $ns = 'https://namespace.com'; $this->expectException(InvalidArgumentException::class); diff --git a/tests/Xml/Dom/Builder/NamespacedElementTest.php b/tests/Xml/Dom/Builder/NamespacedElementTest.php index 3e6432e..29be4bf 100644 --- a/tests/Xml/Dom/Builder/NamespacedElementTest.php +++ b/tests/Xml/Dom/Builder/NamespacedElementTest.php @@ -4,9 +4,10 @@ namespace VeeWee\Tests\Xml\Dom\Builder; -use DOMDocument; -use DOMElement; +use \DOM\XMLDocument; +use \DOM\Element; use PHPUnit\Framework\TestCase; +use VeeWee\Xml\Dom\Document; use function Psl\Fun\identity; use function VeeWee\Xml\Dom\Builder\namespaced_element; use function VeeWee\Xml\Dom\Builder\value; @@ -15,9 +16,9 @@ final class NamespacedElementTest extends TestCase { public function test_it_can_build_an_element_with_alias(): void { - $doc = new DOMDocument(); + $doc = Document::empty()->toUnsafeDocument(); $ns = 'https://namespace.com'; - /** @var DOMElement $node */ + /** @var \DOM\Element $node */ $node = namespaced_element($ns, 'ns:hello')($doc); static::assertSame($ns, $node->namespaceURI); @@ -27,12 +28,12 @@ public function test_it_can_build_an_element_with_alias(): void static::assertSame($doc, $node->ownerDocument); } - + public function test_it_can_build_an_element_without_alias(): void { - $doc = new DOMDocument(); + $doc = Document::empty()->toUnsafeDocument(); $ns = 'https://namespace.com'; - /** @var DOMElement $node */ + /** @var \DOM\Element $node */ $node = namespaced_element($ns, 'hello')($doc); static::assertSame($ns, $node->namespaceURI); @@ -41,10 +42,10 @@ public function test_it_can_build_an_element_without_alias(): void static::assertSame($doc, $node->ownerDocument); } - + public function test_it_can_build_an_element_with_configurators(): void { - $doc = new DOMDocument(); + $doc = Document::empty()->toUnsafeDocument(); $ns = 'https://namespace.com'; $node = namespaced_element($ns, 'ns:hello', identity())($doc); @@ -54,10 +55,10 @@ public function test_it_can_build_an_element_with_configurators(): void static::assertSame($doc, $node->ownerDocument); } - + public function test_it_can_build_an_element_with_value(): void { - $doc = new DOMDocument(); + $doc = Document::empty()->toUnsafeDocument(); $ns = 'https://namespace.com'; $node = namespaced_element($ns, 'ns:hello', value('world'))($doc); diff --git a/tests/Xml/Dom/Builder/NodesTest.php b/tests/Xml/Dom/Builder/NodesTest.php index 810cf40..98bc22f 100644 --- a/tests/Xml/Dom/Builder/NodesTest.php +++ b/tests/Xml/Dom/Builder/NodesTest.php @@ -4,8 +4,9 @@ namespace VeeWee\Tests\Xml\Dom\Builder; -use DOMDocument; +use \DOM\XMLDocument; use PHPUnit\Framework\TestCase; +use VeeWee\Xml\Dom\Document; use function VeeWee\Xml\Dom\Builder\element; use function VeeWee\Xml\Dom\Builder\nodes; @@ -13,11 +14,11 @@ final class NodesTest extends TestCase { public function test_it_can_build_nodes(): void { - $doc = new DOMDocument(); + $doc = Document::empty()->toUnsafeDocument(); $nodes = nodes( element('hello'), element('world'), - static fn (DOMDocument $doc): array => [ + static fn (\DOM\XMLDocument $doc): array => [ element('many1')($doc), element('many2')($doc), ], diff --git a/tests/Xml/Dom/Builder/ValueTest.php b/tests/Xml/Dom/Builder/ValueTest.php index 0590ae5..70f679b 100644 --- a/tests/Xml/Dom/Builder/ValueTest.php +++ b/tests/Xml/Dom/Builder/ValueTest.php @@ -4,8 +4,9 @@ namespace VeeWee\Tests\Xml\Dom\Builder; -use DOMDocument; +use \DOM\XMLDocument; use PHPUnit\Framework\TestCase; +use VeeWee\Xml\Dom\Document; use function VeeWee\Xml\Dom\Builder\element; use function VeeWee\Xml\Dom\Builder\value; use function VeeWee\Xml\Dom\Mapper\xml_string; @@ -14,7 +15,7 @@ final class ValueTest extends TestCase { public function test_it_can_build_an_element_with_html_value(): void { - $doc = new DOMDocument(); + $doc = Document::empty()->toUnsafeDocument(); $node = element('hello', value(''))($doc); static::assertSame('', $node->nodeValue); diff --git a/tests/Xml/Dom/Builder/XmlnsAttributesTest.php b/tests/Xml/Dom/Builder/XmlnsAttributesTest.php index 59350f3..27c90c1 100644 --- a/tests/Xml/Dom/Builder/XmlnsAttributesTest.php +++ b/tests/Xml/Dom/Builder/XmlnsAttributesTest.php @@ -4,8 +4,9 @@ namespace VeeWee\Tests\Xml\Dom\Builder; -use DOMDocument; +use \DOM\XMLDocument; use PHPUnit\Framework\TestCase; +use VeeWee\Xml\Dom\Document; use function VeeWee\Xml\Dom\Builder\element; use function VeeWee\Xml\Dom\Builder\xmlns_attribute; use function VeeWee\Xml\Dom\Builder\xmlns_attributes; @@ -14,7 +15,7 @@ final class XmlnsAttributesTest extends TestCase { public function test_it_can_build_an_element_with_attributes(): void { - $doc = new DOMDocument(); + $doc = Document::empty()->toUnsafeDocument(); $node = element( 'hello', diff --git a/tests/Xml/Dom/Collection/NodeListTest.php b/tests/Xml/Dom/Collection/NodeListTest.php index 253aca4..92a6aeb 100644 --- a/tests/Xml/Dom/Collection/NodeListTest.php +++ b/tests/Xml/Dom/Collection/NodeListTest.php @@ -4,9 +4,9 @@ namespace VeeWee\Tests\Xml\Dom\Collection; -use DOMAttr; -use DOMElement; -use DOMNode; +use \DOM\Attr; +use \DOM\Element; +use \DOM\Node; use InvalidArgumentException; use PHPUnit\Framework\TestCase; use Psl\Collection\MutableVector; @@ -66,20 +66,20 @@ public function test_it_can_be_iterated(): void static::assertCount(4, $items); static::assertIsIterable($items); foreach ($items as $item) { - static::assertInstanceOf(DOMElement::class, $item); + static::assertInstanceOf(\DOM\Element::class, $item); } } public function test_it_can_be_created_typed(): void { $items = NodeList::typed( - DOMElement::class, + \DOM\Element::class, $items = $this->loadProducts() ); static::assertCount(4, $items); foreach ($items as $item) { - static::assertInstanceOf(DOMElement::class, $item); + static::assertInstanceOf(\DOM\Element::class, $item); } } @@ -87,7 +87,7 @@ public function test_it_will_trigger_error_when_created_with_invalid_type(): voi { $this->expectException(InvalidArgumentException::class); NodeList::typed( - DOMElement::class, + \DOM\Element::class, $this->loadProducts()->eq(0)->first()->attributes ); } @@ -120,7 +120,7 @@ public function test_it_can_expect_items() public function test_it_can_map(): void { $items = $this->loadProducts()->map( - static fn (DOMElement $element): string => $element->nodeValue + static fn (\DOM\Element $element): string => $element->nodeValue ); static::assertSame( @@ -138,7 +138,7 @@ public function test_it_can_loop_over_all_items(): void { $x = new MutableVector([]); $this->loadProducts()->forEach( - static function (DOMElement $element) use ($x) : void { + static function (\DOM\Element $element) use ($x) : void { $x->add($element->nodeValue); } ); @@ -158,7 +158,7 @@ public function test_it_can_filter(): void { $all = $this->loadProducts(); $filtered = $all->filter( - static fn (DOMElement $element): bool => (bool) ((int)($element->getAttribute('id'))%2) + static fn (\DOM\Element $element): bool => (bool) ((int)($element->getAttribute('id'))%2) ); static::assertCount(2, $filtered); @@ -170,7 +170,7 @@ public function test_it_can_filter(): void public function test_it_can_reduce(): void { $total = $this->loadPrices()->reduce( - static fn (int $total, DOMElement $element): int => $total + (int) $element->nodeValue, + static fn (int $total, \DOM\Element $element): int => $total + (int) $element->nodeValue, 0 ); @@ -180,9 +180,9 @@ public function test_it_can_reduce(): void public function test_it_can_detect(): void { $list = $this->root()->detect( - static fn (DOMElement $element) => filter( + static fn (\DOM\Element $element) => filter( $element->childNodes, - static fn (DOMNode $current) => is_element($current) + static fn (\DOM\Node $current) => is_element($current) ) ); @@ -261,7 +261,7 @@ public function test_it_can_expect_single_on_a_two_item_list(): void $prices = $this->loadPrices(); $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage('Expected single element, got 2 elements.'); - $prices->filter(static fn (DOMElement $price): bool => (int)$price->textContent >= 2)->expectSingle('Expected single element, got %s elements.'); + $prices->filter(static fn (\DOM\Element $price): bool => (int)$price->textContent >= 2)->expectSingle('Expected single element, got %s elements.'); } public function test_it_can_get_last(): void @@ -316,7 +316,7 @@ public function test_it_can_search_siblings(): void public function test_it_can_validate_types(): void { $prices = $this->loadPrices(); - $result = $prices->expectAllOfType(DOMElement::class); + $result = $prices->expectAllOfType(\DOM\Element::class); static::assertSame([...$prices], [...$result]); } @@ -325,13 +325,13 @@ public function test_it_can_validate_types_and_fail(): void { $this->expectException(InvalidArgumentException::class); $prices = $this->loadPrices(); - $prices->expectAllOfType(DOMAttr::class); + $prices->expectAllOfType(\DOM\Attr::class); } public function test_it_can_sort(): void { $prices = $this->loadPrices(); - $sorted = $prices->sort(static fn (DOMNode $a, DOMNode $b) => $b->nodeValue <=> $a->nodeValue); + $sorted = $prices->sort(static fn (\DOM\Node $a, \DOM\Node $b) => $b->nodeValue <=> $a->nodeValue); static::assertSame( [ diff --git a/tests/Xml/Dom/Configurator/DocumentUriTest.php b/tests/Xml/Dom/Configurator/DocumentUriTest.php index 89f550f..492134a 100644 --- a/tests/Xml/Dom/Configurator/DocumentUriTest.php +++ b/tests/Xml/Dom/Configurator/DocumentUriTest.php @@ -4,7 +4,7 @@ namespace VeeWee\Tests\Xml\Dom\Configurator; -use DOMDocument; +use DOM\XMLDocument as DOMDocument; use PHPUnit\Framework\TestCase; use VeeWee\Xml\Dom\Document; use function VeeWee\Xml\Dom\Configurator\document_uri; @@ -14,8 +14,7 @@ final class DocumentUriTest extends TestCase { public function test_it_can_use_document_uri(): void { - $doc = new DOMDocument(); - $doc->loadXML($xml = ''); + $doc = Document::fromXmlString('')->toUnsafeDocument(); static::assertStringStartsWith(getcwd(), $doc->documentURI); $configurator = document_uri($documentUri = 'myfile.wsdl'); diff --git a/tests/Xml/Dom/Configurator/LoaderTest.php b/tests/Xml/Dom/Configurator/LoaderTest.php index b274c3b..788cd87 100644 --- a/tests/Xml/Dom/Configurator/LoaderTest.php +++ b/tests/Xml/Dom/Configurator/LoaderTest.php @@ -4,19 +4,20 @@ namespace VeeWee\Tests\Xml\Dom\Configurator; -use DOMDocument; +use \DOM\XMLDocument; use Exception; use PHPUnit\Framework\TestCase; +use VeeWee\Xml\Dom\Document; use function VeeWee\Xml\Dom\Configurator\loader; final class LoaderTest extends TestCase { public function test_it_can_load_xml(): void { - $doc = new DOMDocument(); + $doc = Document::empty()->toUnsafeDocument(); $xml = ''; - $loader = loader(static function (DOMDocument $doc) use ($xml): void { + $loader = loader(static function (\DOM\XMLDocument $doc) use ($xml): void { $doc->loadXML($xml); }); @@ -25,12 +26,12 @@ public function test_it_can_load_xml(): void static::assertXmlStringEqualsXmlString($xml, $doc->saveXML()); } - + public function test_it_can_mark_xml_loading_as_failed(): void { - $doc = new DOMDocument(); + $doc = Document::empty()->toUnsafeDocument(); $exception = new Exception('Could not load the XML document'); - $loader = loader(static function (DOMDocument $doc) use ($exception): void { + $loader = loader(static function (\DOM\XMLDocument $doc) use ($exception): void { throw $exception; }); diff --git a/tests/Xml/Dom/Configurator/PrettyPrintTest.php b/tests/Xml/Dom/Configurator/PrettyPrintTest.php index cb72849..79ed80c 100644 --- a/tests/Xml/Dom/Configurator/PrettyPrintTest.php +++ b/tests/Xml/Dom/Configurator/PrettyPrintTest.php @@ -4,8 +4,9 @@ namespace VeeWee\Tests\Xml\Dom\Configurator; -use DOMDocument; +use \DOM\XMLDocument; use PHPUnit\Framework\TestCase; +use VeeWee\Xml\Dom\Document; use function VeeWee\Xml\Dom\Configurator\pretty_print; use function VeeWee\Xml\Dom\Mapper\xml_string; @@ -15,9 +16,8 @@ public function test_it_can_trim_contents(): void { $configurator = pretty_print(); - $doc = new DOMDocument(); + $doc = Document::fromXmlString($xml = ' ')->toUnsafeDocument(); $result = $configurator($doc); - $doc->loadXML($xml = ' '); $expected = << diff --git a/tests/Xml/Dom/Configurator/TraverseTest.php b/tests/Xml/Dom/Configurator/TraverseTest.php index d9e3452..aff13a6 100644 --- a/tests/Xml/Dom/Configurator/TraverseTest.php +++ b/tests/Xml/Dom/Configurator/TraverseTest.php @@ -4,7 +4,7 @@ namespace VeeWee\Tests\Xml\Dom\Configurator; -use DOMNode; +use \DOM\Node; use PHPUnit\Framework\TestCase; use VeeWee\Xml\Dom\Document; use VeeWee\Xml\Dom\Traverser\Action; @@ -18,7 +18,7 @@ public function test_it_can_traverse(): void { $doc = Document::fromXmlString('world', traverse( new class() extends AbstractVisitor { - public function onNodeLeave(DOMNode $node): Action + public function onNodeLeave(\DOM\Node $node): Action { return is_text($node) ? new Action\RemoveNode() diff --git a/tests/Xml/Dom/Configurator/TrimSpacesTest.php b/tests/Xml/Dom/Configurator/TrimSpacesTest.php index b9718c2..f12fff1 100644 --- a/tests/Xml/Dom/Configurator/TrimSpacesTest.php +++ b/tests/Xml/Dom/Configurator/TrimSpacesTest.php @@ -4,8 +4,9 @@ namespace VeeWee\Tests\Xml\Dom\Configurator; -use DOMDocument; +use \DOM\XMLDocument; use PHPUnit\Framework\TestCase; +use VeeWee\Xml\Dom\Document; use function VeeWee\Xml\Dom\Configurator\trim_spaces; use function VeeWee\Xml\Dom\Mapper\xml_string; @@ -13,10 +14,9 @@ final class TrimSpacesTest extends TestCase { public function test_it_can_trim_contents(): void { - $doc = new DOMDocument(); + $doc = Document::fromXmlString(' ')->toUnsafeDocument(); $configurator = trim_spaces(); $result = $configurator($doc); - $doc->loadXML($xml = ' '); static::assertSame($doc, $result); static::assertFalse($doc->preserveWhiteSpace); diff --git a/tests/Xml/Dom/Configurator/Utf8Test.php b/tests/Xml/Dom/Configurator/Utf8Test.php index fc903e8..ac45857 100644 --- a/tests/Xml/Dom/Configurator/Utf8Test.php +++ b/tests/Xml/Dom/Configurator/Utf8Test.php @@ -4,16 +4,16 @@ namespace VeeWee\Tests\Xml\Dom\Configurator; -use DOMDocument; +use \DOM\XMLDocument; use PHPUnit\Framework\TestCase; +use VeeWee\Xml\Dom\Document; use function VeeWee\Xml\Dom\Configurator\utf8; final class Utf8Test extends TestCase { public function test_it_can_convert_to_utf8(): void { - $doc = new DOMDocument(); - $doc->loadXML($xml = ''); + $doc = Document::fromXmlString($xml = '')->toUnsafeDocument(); $configurator = utf8(); diff --git a/tests/Xml/Dom/Configurator/ValidatorTest.php b/tests/Xml/Dom/Configurator/ValidatorTest.php index ee237a3..eef307b 100644 --- a/tests/Xml/Dom/Configurator/ValidatorTest.php +++ b/tests/Xml/Dom/Configurator/ValidatorTest.php @@ -4,9 +4,10 @@ namespace VeeWee\Tests\Xml\Dom\Configurator; -use DOMDocument; +use \DOM\XMLDocument; use PHPUnit\Framework\TestCase; use VeeWee\Tests\Xml\ErrorHandling\Issue\UseIssueTrait; +use VeeWee\Xml\Dom\Document; use VeeWee\Xml\ErrorHandling\Issue\IssueCollection; use VeeWee\Xml\ErrorHandling\Issue\Level; use VeeWee\Xml\Exception\RuntimeException; @@ -16,21 +17,21 @@ final class ValidatorTest extends TestCase { use UseIssueTrait; - + public function test_it_can_configure_xml_with_valid_validation_result(): void { - $doc = new DOMDocument(); - $validator = validator(static fn (DOMDocument $doc): IssueCollection => new IssueCollection()); + $doc = Document::empty()->toUnsafeDocument(); + $validator = validator(static fn (\DOM\XMLDocument $doc): IssueCollection => new IssueCollection()); $result = $validator($doc); static::assertSame($doc, $result); } - + public function test_it_can_configure_xml_with_invalid_validation_result(): void { - $doc = new DOMDocument(); - $validator = validator(fn (DOMDocument $doc): IssueCollection => new IssueCollection( + $doc = Document::empty()->toUnsafeDocument(); + $validator = validator(fn (\DOM\XMLDocument $doc): IssueCollection => new IssueCollection( $this->createIssue(Level::fatal()) )); diff --git a/tests/Xml/Dom/DocumentTest.php b/tests/Xml/Dom/DocumentTest.php index 04f9c9d..09a4d29 100644 --- a/tests/Xml/Dom/DocumentTest.php +++ b/tests/Xml/Dom/DocumentTest.php @@ -4,8 +4,8 @@ namespace VeeWee\Tests\Xml\Dom; -use DOMDocument; -use DOMNode; +use \DOM\XMLDocument; +use \DOM\Node; use PHPUnit\Framework\TestCase; use VeeWee\Tests\Xml\Helper\FillFileTrait; use VeeWee\Xml\Dom\Document; @@ -24,7 +24,7 @@ final class DocumentTest extends TestCase public function test_it_can_create_a_document_from_dom(): void { - $document = new DOMDocument(); + $document = XMLDocument::createEmpty(); $doc = Document::fromUnsafeDocument($document, identity()); static::assertSame($document, $doc->toUnsafeDocument()); @@ -33,7 +33,7 @@ public function test_it_can_create_a_document_from_dom(): void public function test_it_can_create_an_empty_document(): void { - $document = new DOMDocument(); + $document = XMLDocument::createEmpty(); $doc = Document::empty(); static::assertEquals($document, $doc->toUnsafeDocument()); @@ -42,7 +42,7 @@ public function test_it_can_create_an_empty_document(): void public function test_it_can_create_a_configured_document(): void { - $document = new DOMDocument(); + $document = XMLDocument::createEmpty(); $doc = Document::configure(identity()); static::assertEquals($document, $doc->toUnsafeDocument()); @@ -67,8 +67,7 @@ public function test_it_can_add_various_configurators(): void public function test_it_can_create_a_document_from_xml_node(): void { - $source = new DOMDocument(); - $source->loadXML($xml = ''); + $source = XMLDocument::createFromString($xml = ''); $doc = Document::fromXmlNode( $source->documentElement, @@ -107,7 +106,7 @@ public function test_it_can_create_a_document_from_xml_string(): void public function test_it_can_map(): void { - $doc = new DOMDocument(); + $doc = XMLDocument::createEmpty(); $wrapper = Document::fromUnsafeDocument($doc); $mapped = $wrapper->map(identity()); @@ -119,7 +118,7 @@ public function test_it_can_traverse(): void $doc = Document::fromXmlString('world'); $result = $doc->traverse( new class() extends AbstractVisitor { - public function onNodeLeave(DOMNode $node): Action + public function onNodeLeave(\DOM\Node $node): Action { return is_text($node) ? new Action\RemoveNode() @@ -167,11 +166,11 @@ public function test_it_can_stringify_parts(): void $expected = ''; static::assertSame( - ''.PHP_EOL.$expected.PHP_EOL, + ''.PHP_EOL.$expected, $full ); static::assertSame($expected, $documentElement); static::assertSame($expected, $node); - static::assertSame(' value="world"', $attr); + static::assertSame('value="world"', $attr); } } diff --git a/tests/Xml/Dom/Loader/XmlFileLoaderTest.php b/tests/Xml/Dom/Loader/XmlFileLoaderTest.php index 7a9fdb8..9f3f68d 100644 --- a/tests/Xml/Dom/Loader/XmlFileLoaderTest.php +++ b/tests/Xml/Dom/Loader/XmlFileLoaderTest.php @@ -4,7 +4,7 @@ namespace VeeWee\Tests\Xml\Dom\Loader; -use DOMDocument; +use \DOM\XMLDocument; use PHPUnit\Framework\TestCase; use VeeWee\Tests\Xml\Helper\FillFileTrait; use VeeWee\Xml\Exception\RuntimeException; @@ -16,7 +16,7 @@ final class XmlFileLoaderTest extends TestCase public function test_it_can_load_xml_file(): void { - $doc = new DOMDocument(); + $doc = new \DOM\XMLDocument(); $xml = ''; [$file, $handle] = $this->fillFile($xml); $loader = xml_file_loader($file); @@ -29,7 +29,7 @@ public function test_it_can_load_xml_file(): void public function test_it_can_load_with_options(): void { - $doc = new DOMDocument(); + $doc = new \DOM\XMLDocument(); $xml = ''; [$file, $handle] = $this->fillFile($xml); $loader = xml_file_loader($file, LIBXML_NOCDATA); @@ -42,7 +42,7 @@ public function test_it_can_load_with_options(): void public function test_it_cannot_load_invalid_xml_file(): void { - $doc = new DOMDocument(); + $doc = new \DOM\XMLDocument(); $xml = 'fillFile($xml); $loader = xml_file_loader($file); @@ -56,7 +56,7 @@ public function test_it_cannot_load_invalid_xml_file(): void public function test_it_throws_exception_on_invalid_file(): void { - $doc = new DOMDocument(); + $doc = new \DOM\XMLDocument(); $loader = xml_file_loader('invalid-file'); $this->expectException(RuntimeException::class); diff --git a/tests/Xml/Dom/Loader/XmlNodeLoaderTest.php b/tests/Xml/Dom/Loader/XmlNodeLoaderTest.php index 863f5e8..c0a115e 100644 --- a/tests/Xml/Dom/Loader/XmlNodeLoaderTest.php +++ b/tests/Xml/Dom/Loader/XmlNodeLoaderTest.php @@ -4,8 +4,9 @@ namespace VeeWee\Tests\Xml\Dom\Loader; -use DOMDocument; +use \DOM\XMLDocument; use PHPUnit\Framework\TestCase; +use VeeWee\Xml\Dom\Document; use VeeWee\Xml\Exception\RuntimeException; use function VeeWee\Xml\Dom\Loader\xml_node_loader; @@ -13,23 +14,21 @@ final class XmlNodeLoaderTest extends TestCase { public function test_it_can_load_xml_node(): void { - $source = new DOMDocument(); - $source->loadXML($xml = ''); + $source = Document::fromXmlString($xml = '')->toUnsafeDocument(); + $doc = Document::empty()->toUnsafeDocument(); - $doc = new DOMDocument(); $loader = xml_node_loader($source->documentElement); $loader($doc); static::assertXmlStringEqualsXmlString($xml, $doc->saveXML()); } - + public function test_it_can_not_load_invalid_xml_node(): void { - $source = new DOMDocument(); - $source->loadXML($xml = ''); + $source = Document::fromXmlString($xml = '')->toUnsafeDocument(); + $doc = Document::empty()->toUnsafeDocument(); - $doc = new DOMDocument(); $loader = xml_node_loader($source); $this->expectException(RuntimeException::class); diff --git a/tests/Xml/Dom/Loader/XmlStringLoaderTest.php b/tests/Xml/Dom/Loader/XmlStringLoaderTest.php index 3111169..ebed914 100644 --- a/tests/Xml/Dom/Loader/XmlStringLoaderTest.php +++ b/tests/Xml/Dom/Loader/XmlStringLoaderTest.php @@ -4,7 +4,7 @@ namespace VeeWee\Tests\Xml\Dom\Loader; -use DOMDocument; +use \DOM\XMLDocument; use PHPUnit\Framework\TestCase; use VeeWee\Xml\Exception\RuntimeException; use function VeeWee\Xml\Dom\Loader\xml_string_loader; @@ -13,7 +13,7 @@ final class XmlStringLoaderTest extends TestCase { public function test_it_can_load_xml_string(): void { - $doc = new DOMDocument(); + $doc = new \DOM\XMLDocument(); $xml = ''; $loader = xml_string_loader($xml); @@ -23,7 +23,7 @@ public function test_it_can_load_xml_string(): void public function test_it_can_not_load_invalid_xml_string(): void { - $doc = new DOMDocument(); + $doc = new \DOM\XMLDocument(); $xml = 'documentElement); $this->expectException(RuntimeException::class); - $this->expectExceptionMessage('Can not find parent element for DOMElement hello'); + $this->expectExceptionMessage('Can not find parent element for \DOM\Element hello'); parent_element($hello); } } diff --git a/tests/Xml/Dom/Locator/Node/DetectDocumentTest.php b/tests/Xml/Dom/Locator/Node/DetectDocumentTest.php index d304228..4c7e101 100644 --- a/tests/Xml/Dom/Locator/Node/DetectDocumentTest.php +++ b/tests/Xml/Dom/Locator/Node/DetectDocumentTest.php @@ -4,7 +4,7 @@ namespace VeeWee\Tests\Xml\Dom\Locator\Node; -use DOMElement; +use \DOM\Element; use InvalidArgumentException; use PHPUnit\Framework\TestCase; use VeeWee\Xml\Dom\Document; @@ -24,10 +24,10 @@ public function test_it_can_detect_document(): void public function test_it_throws_exception_on_unlinked_node(): void { - $element = new DOMElement('name'); + $element = new \DOM\Element('name'); $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage('Expected to find an ownerDocument on provided DOMNode.'); + $this->expectExceptionMessage('Expected to find an ownerDocument on provided \DOM\Node.'); detect_document($element); } diff --git a/tests/Xml/Dom/Locator/Xmlns/LinkedNamespacesTest.php b/tests/Xml/Dom/Locator/Xmlns/LinkedNamespacesTest.php index 29a961c..60fa809 100644 --- a/tests/Xml/Dom/Locator/Xmlns/LinkedNamespacesTest.php +++ b/tests/Xml/Dom/Locator/Xmlns/LinkedNamespacesTest.php @@ -4,7 +4,7 @@ namespace VeeWee\Tests\Xml\Dom\Locator\Xmlns; -use DOMNameSpaceNode; +use \DOM\NameSpaceNode; use PHPUnit\Framework\TestCase; use VeeWee\Xml\Dom\Collection\NodeList; use VeeWee\Xml\Dom\Document; @@ -26,7 +26,7 @@ public function test_it_can_detect_linked_namespaces(): void $parse = static fn (NodeList $list): array => reduce( [...$list], - static fn (array $map, DOMNameSpaceNode $node) => merge($map, [$node->localName => $node->namespaceURI]), + static fn (array $map, \DOM\NameSpaceNode $node) => merge($map, [$node->localName => $node->namespaceURI]), [] ); diff --git a/tests/Xml/Dom/Locator/Xmlns/RecursiveLinkedNamespacesTest.php b/tests/Xml/Dom/Locator/Xmlns/RecursiveLinkedNamespacesTest.php index 1ab23f6..35fab90 100644 --- a/tests/Xml/Dom/Locator/Xmlns/RecursiveLinkedNamespacesTest.php +++ b/tests/Xml/Dom/Locator/Xmlns/RecursiveLinkedNamespacesTest.php @@ -4,7 +4,7 @@ namespace VeeWee\Tests\Xml\Dom\Locator\Xmlns; -use DOMNameSpaceNode; +use \DOM\NameSpaceNode; use PHPUnit\Framework\TestCase; use VeeWee\Xml\Dom\Collection\NodeList; use VeeWee\Xml\Dom\Document; @@ -26,7 +26,7 @@ public function test_it_can_detect_recursively_linked_namespaces(): void $parse = static fn (NodeList $list): array => reduce( [...$list], - static fn (array $map, DOMNameSpaceNode $node) => merge($map, [$node->localName => $node->namespaceURI]), + static fn (array $map, \DOM\NameSpaceNode $node) => merge($map, [$node->localName => $node->namespaceURI]), [] ); diff --git a/tests/Xml/Dom/Locator/Xsd/LocateXsdSchemasTest.php b/tests/Xml/Dom/Locator/Xsd/LocateXsdSchemasTest.php index c71df25..a0d6bfd 100644 --- a/tests/Xml/Dom/Locator/Xsd/LocateXsdSchemasTest.php +++ b/tests/Xml/Dom/Locator/Xsd/LocateXsdSchemasTest.php @@ -4,8 +4,9 @@ namespace VeeWee\Tests\Xml\Dom\Locator\Xsd; -use DOMDocument; +use \DOM\XMLDocument; use PHPUnit\Framework\TestCase; +use VeeWee\Xml\Dom\Document; use VeeWee\Xml\Xsd\Schema\Schema; use function VeeWee\Xml\Dom\Locator\Xsd\locate_all_xsd_schemas; use function VeeWee\Xml\Dom\Locator\Xsd\locate_namespaced_xsd_schemas; @@ -27,7 +28,7 @@ public function test_it_can_locate_namespaced_xsd_schemas(): void ); } - + public function test_it_can_locate_no_namespaced_xsd_schemas(): void { $document = $this->loadXsdContainer(); @@ -42,7 +43,7 @@ public function test_it_can_locate_no_namespaced_xsd_schemas(): void ); } - + public function test_it_can_locate_all_xsd_schemas(): void { $document = $this->loadXsdContainer(); @@ -59,14 +60,11 @@ public function test_it_can_locate_all_xsd_schemas(): void ); } - private function loadXsdContainer(): DOMDocument + private function loadXsdContainer(): \DOM\XMLDocument { $file = FIXTURE_DIR.'/dom/locator/xsd/xsdcontainer.xml'; static::assertFileExists($file); - $document = new DOMDocument(); - $document->load($file); - - return $document; + return Document::fromXmlFile($file)->toUnsafeDocument(); } } diff --git a/tests/Xml/Dom/Manipulator/Node/AppendExternalNodeTest.php b/tests/Xml/Dom/Manipulator/Node/AppendExternalNodeTest.php index de3f193..2de46ba 100644 --- a/tests/Xml/Dom/Manipulator/Node/AppendExternalNodeTest.php +++ b/tests/Xml/Dom/Manipulator/Node/AppendExternalNodeTest.php @@ -4,50 +4,48 @@ namespace VeeWee\Tests\Xml\Dom\Manipulator\Node; -use DOMDocument; -use DOMElement; +use \DOM\XMLDocument; +use \DOM\Element; +use Infected\PhpParser\Comment\Doc; use PHPUnit\Framework\TestCase; use RuntimeException; +use VeeWee\Xml\Dom\Document; use function VeeWee\Xml\Dom\Manipulator\Node\append_external_node; final class AppendExternalNodeTest extends TestCase { public function test_it_can_import_a_node_into_a_document_root(): void { - $source = new DOMDocument(); - $source->loadXML(''); - $target = new DOMDocument(); + $source = Document::fromXmlString('')->toUnsafeDocument(); + $target = Document::empty()->toUnsafeDocument(); $result = append_external_node($target, $source->documentElement); - static::assertInstanceOf(DOMElement::class, $result); + static::assertInstanceOf(\DOM\Element::class, $result); static::assertSame('hello', $result->nodeName); static::assertXmlStringEqualsXmlString($source->saveXML(), $target->saveXML()); } - + public function test_it_can_not_import_a_document_into_a_document(): void { - $source = new DOMDocument(); - $source->loadXML(''); - $target = new DOMDocument(); + $source = Document::fromXmlString('')->toUnsafeDocument(); + $target = Document::empty()->toUnsafeDocument(); $this->expectException(RuntimeException::class); $this->expectExceptionMessage('Cannot import node: Node Type Not Supported'); append_external_node($target, $source); } - + public function test_it_can_recursively_import_a_node_into_another_document_node(): void { - $source = new DOMDocument(); - $source->loadXML('VeeWee'); - $target = new DOMDocument(); - $target->loadXML(''); + $source = Document::fromXmlString('VeeWee')->toUnsafeDocument(); + $target = Document::fromXmlString('')->toUnsafeDocument(); $result = append_external_node($target->documentElement, $source->documentElement->firstChild); - static::assertInstanceOf(DOMElement::class, $result); + static::assertInstanceOf(\DOM\Element::class, $result); static::assertSame('world', $result->nodeName); static::assertXmlStringEqualsXmlString($source->saveXML(), $target->saveXML()); } diff --git a/tests/Xml/Dom/Manipulator/Node/ImportNodeDeeplyTest.php b/tests/Xml/Dom/Manipulator/Node/ImportNodeDeeplyTest.php index fda12f6..000cfde 100644 --- a/tests/Xml/Dom/Manipulator/Node/ImportNodeDeeplyTest.php +++ b/tests/Xml/Dom/Manipulator/Node/ImportNodeDeeplyTest.php @@ -4,9 +4,10 @@ namespace VeeWee\Tests\Xml\Dom\Manipulator\Node; -use DOMDocument; -use DOMElement; +use \DOM\XMLDocument; +use \DOM\Element; use PHPUnit\Framework\TestCase; +use VeeWee\Xml\Dom\Document; use VeeWee\Xml\Exception\RuntimeException; use function VeeWee\Xml\Dom\Manipulator\Node\import_node_deeply; @@ -14,52 +15,47 @@ final class ImportNodeDeeplyTest extends TestCase { public function test_it_can_import_a_node(): void { - $source = new DOMDocument(); - $source->loadXML(''); - $target = new DOMDocument(); + $source = Document::fromXmlString('')->toUnsafeDocument(); + $target = Document::empty()->toUnsafeDocument(); $result = import_node_deeply($target, $source->documentElement); - static::assertInstanceOf(DOMElement::class, $result); + static::assertInstanceOf(\DOM\Element::class, $result); static::assertSame('hello', $result->nodeName); } - + public function test_it_can_import_a_node_into_a_nodes_document(): void { - $source = new DOMDocument(); - $source->loadXML(''); - $target = new DOMDocument(); - $target->loadXML(''); + $source = Document::fromXmlString('')->toUnsafeDocument(); + $target = Document::fromXmlString('')->toUnsafeDocument(); $result = import_node_deeply($target->documentElement, $source->documentElement); - static::assertInstanceOf(DOMElement::class, $result); + static::assertInstanceOf(\DOM\Element::class, $result); static::assertSame('hello', $result->nodeName); } - + public function test_it_can_not_import_an_invalid_node(): void { - $source = new DOMDocument(); - $source->loadXML(''); - $target = new DOMDocument(); + $source = Document::fromXmlString('')->toUnsafeDocument(); + $target = Document::empty()->toUnsafeDocument(); $this->expectException(RuntimeException::class); $this->expectExceptionMessage('Cannot import node: Node Type Not Supported'); import_node_deeply($target, $source); } - + public function test_it_can_recursively_import_a_node_based_on_a_target_document_node(): void { - $source = new DOMDocument(); - $source->loadXML('VeeWee'); - $target = new DOMDocument(); + $source = Document::fromXmlString('VeeWee')->toUnsafeDocument(); + $target = Document::empty()->toUnsafeDocument(); $result = import_node_deeply($target, $source->documentElement->firstChild); - static::assertInstanceOf(DOMElement::class, $result); + static::assertInstanceOf(\DOM\Element::class, $result); static::assertSame('world', $result->nodeName); static::assertSame('myvalue', $result->attributes->getNamedItem('myattrib')->nodeValue); static::assertSame('name', $result->firstChild->nodeName); diff --git a/tests/Xml/Dom/Manipulator/Node/RemoveTest.php b/tests/Xml/Dom/Manipulator/Node/RemoveTest.php index 314a870..f6e68d4 100644 --- a/tests/Xml/Dom/Manipulator/Node/RemoveTest.php +++ b/tests/Xml/Dom/Manipulator/Node/RemoveTest.php @@ -4,7 +4,7 @@ namespace VeeWee\Tests\Xml\Dom\Manipulator\Node; -use DOMDocument; +use \DOM\XMLDocument as DOMDocument; use PHPUnit\Framework\TestCase; use VeeWee\Xml\Dom\Document; use VeeWee\Xml\Exception\RuntimeException; diff --git a/tests/Xml/Dom/Manipulator/Node/RenameTest.php b/tests/Xml/Dom/Manipulator/Node/RenameTest.php index 975434c..310e98c 100644 --- a/tests/Xml/Dom/Manipulator/Node/RenameTest.php +++ b/tests/Xml/Dom/Manipulator/Node/RenameTest.php @@ -4,7 +4,7 @@ namespace VeeWee\Tests\Xml\Dom\Manipulator\Node; -use DOMDocument; +use \DOM\XMLDocument as DOMDocument; use PHPUnit\Framework\TestCase; use VeeWee\Xml\Dom\Document; use VeeWee\Xml\Exception\RuntimeException; diff --git a/tests/Xml/Dom/Manipulator/Node/ReplaceByExternalNodeTest.php b/tests/Xml/Dom/Manipulator/Node/ReplaceByExternalNodeTest.php index ac23778..39610c7 100644 --- a/tests/Xml/Dom/Manipulator/Node/ReplaceByExternalNodeTest.php +++ b/tests/Xml/Dom/Manipulator/Node/ReplaceByExternalNodeTest.php @@ -4,9 +4,10 @@ namespace VeeWee\Tests\Xml\Dom\Manipulator\Node; -use DOMDocument; -use DOMElement; +use \DOM\XMLDocument; +use \DOM\Element; use PHPUnit\Framework\TestCase; +use VeeWee\Xml\Dom\Document; use VeeWee\Xml\Exception\RuntimeException; use function VeeWee\Xml\Dom\Manipulator\Node\replace_by_external_node; @@ -14,43 +15,37 @@ final class ReplaceByExternalNodeTest extends TestCase { public function test_it_can_replace_a_node(): void { - $source = new DOMDocument(); - $source->loadXML(''); - $target = new DOMDocument(); - $target->loadXML(''); + $source = Document::fromXmlString('')->toUnsafeDocument(); + $target = Document::fromXmlString('')->toUnsafeDocument(); $result = replace_by_external_node($target->documentElement, $source->documentElement); - static::assertInstanceOf(DOMElement::class, $result); + static::assertInstanceOf(\DOM\Element::class, $result); static::assertSame('hello', $result->nodeName); static::assertXmlStringEqualsXmlString($target->saveXML(), $source->saveXML()); } - + public function test_it_can_not_replace_a_document_into_a_document(): void { - $source = new DOMDocument(); - $source->loadXML(''); - $target = new DOMDocument(); + $source = Document::fromXmlString('')->toUnsafeDocument(); + $target = Document::empty()->toUnsafeDocument(); $this->expectException(RuntimeException::class); - $this->expectExceptionMessage('Could not replace a node without parent node. (DOMDocument)'); + $this->expectExceptionMessage('Could not replace a node without parent node. (\DOM\XMLDocument)'); replace_by_external_node($target, $source); } - + public function test_it_can_recursively_replace_a_node_with_another_external_node(): void { - $source = new DOMDocument(); - $source->loadXML('VeeWee'); - $target = new DOMDocument(); - $target->loadXML(''); - $expected = new DOMDocument(); - $expected->loadXML('VeeWee'); + $source = Document::fromXmlString('VeeWee')->toUnsafeDocument(); + $target = Document::fromXmlString('')->toUnsafeDocument(); + $expected = Document::fromXmlString('VeeWee')->toUnsafeDocument(); $result = replace_by_external_node($target->documentElement, $source->documentElement->firstChild); - static::assertInstanceOf(DOMElement::class, $result); + static::assertInstanceOf(\DOM\Element::class, $result); static::assertSame('world', $result->nodeName); static::assertXmlStringEqualsXmlString($expected->saveXML(), $target->saveXML()); } diff --git a/tests/Xml/Dom/Manipulator/Node/ReplaceByExternalNodesTest.php b/tests/Xml/Dom/Manipulator/Node/ReplaceByExternalNodesTest.php index c332e7d..b5358ab 100644 --- a/tests/Xml/Dom/Manipulator/Node/ReplaceByExternalNodesTest.php +++ b/tests/Xml/Dom/Manipulator/Node/ReplaceByExternalNodesTest.php @@ -4,9 +4,10 @@ namespace VeeWee\Tests\Xml\Dom\Manipulator\Node; -use DOMDocument; -use DOMElement; +use \DOM\XMLDocument; +use \DOM\Element; use PHPUnit\Framework\TestCase; +use VeeWee\Xml\Dom\Document; use VeeWee\Xml\Exception\RuntimeException; use function VeeWee\Xml\Dom\Locator\Element\children; use function VeeWee\Xml\Dom\Manipulator\Node\replace_by_external_nodes; @@ -15,63 +16,55 @@ final class ReplaceByExternalNodesTest extends TestCase { public function test_it_can_replace_a_node(): void { - $source = new DOMDocument(); - $source->loadXML(''); - $target = new DOMDocument(); - $target->loadXML(''); + $source = Document::fromXmlString(''); + $target = Document::fromXmlString(''); - $results = replace_by_external_nodes($target->documentElement, [$source->documentElement]); + $results = replace_by_external_nodes($target->locateDocumentElement(), [$source->locateDocumentElement()]); $result = $results[0]; - static::assertInstanceOf(DOMElement::class, $result); + static::assertInstanceOf(\DOM\Element::class, $result); static::assertSame('hello', $result->nodeName); - static::assertXmlStringEqualsXmlString($target->saveXML(), $source->saveXML()); + static::assertXmlStringEqualsXmlString($target->toXmlString(), $source->toXmlString()); } public function test_it_can_replace_many_nodes(): void { - $source = new DOMDocument(); - $source->loadXML(''); - $target = new DOMDocument(); - $target->loadXML(''); - $items = children($source->documentElement); + $source = Document::fromXmlString(''); + $target = Document::fromXmlString(''); + $items = children($source->locateDocumentElement()); - $results = replace_by_external_nodes($target->documentElement->childNodes->item(0), $items); + $results = replace_by_external_nodes($target->locateDocumentElement()->childNodes->item(0), $items); - static::assertInstanceOf(DOMElement::class, $results[0]); + static::assertInstanceOf(\DOM\Element::class, $results[0]); static::assertSame('world', $results[0]->nodeName); - static::assertInstanceOf(DOMElement::class, $results[1]); + static::assertInstanceOf(\DOM\Element::class, $results[1]); static::assertSame('toon', $results[1]->nodeName); - static::assertXmlStringEqualsXmlString($target->saveXML(), $source->saveXML()); + static::assertXmlStringEqualsXmlString($target->toXmlString(), $source->toXmlString()); } public function test_it_can_not_replace_a_document_into_a_document(): void { - $source = new DOMDocument(); - $source->loadXML(''); - $target = new DOMDocument(); + $source = Document::fromXmlString('')->toUnsafeDocument(); + $target = Document::empty()->toUnsafeDocument(); $this->expectException(RuntimeException::class); - $this->expectExceptionMessage('Could not replace a node without parent node. (DOMDocument)'); + $this->expectExceptionMessage('Could not replace a node without parent node. (\DOM\XMLDocument)'); replace_by_external_nodes($target, [$source]); } - + public function test_it_can_recursively_replace_a_node_with_another_external_node(): void { - $source = new DOMDocument(); - $source->loadXML('VeeWee'); - $target = new DOMDocument(); - $target->loadXML(''); - $expected = new DOMDocument(); - $expected->loadXML('VeeWee'); - - $results = replace_by_external_nodes($target->documentElement, [$source->documentElement->firstChild]); + $source = Document::fromXmlString('VeeWee'); + $target = Document::fromXmlString(''); + $expected = Document::fromXmlString('VeeWee'); + + $results = replace_by_external_nodes($target->locateDocumentElement(), [$source->locateDocumentElement()->firstChild]); $result = $results[0]; - static::assertInstanceOf(DOMElement::class, $result); + static::assertInstanceOf(\DOM\Element::class, $result); static::assertSame('world', $result->nodeName); - static::assertXmlStringEqualsXmlString($expected->saveXML(), $target->saveXML()); + static::assertXmlStringEqualsXmlString($expected->toXmlString(), $target->toXmlString()); } } diff --git a/tests/Xml/Dom/Mapper/XmlStringTest.php b/tests/Xml/Dom/Mapper/XmlStringTest.php index 7d66e9c..8bc865d 100644 --- a/tests/Xml/Dom/Mapper/XmlStringTest.php +++ b/tests/Xml/Dom/Mapper/XmlStringTest.php @@ -4,7 +4,7 @@ namespace VeeWee\Tests\Xml\Mapper; -use DOMElement; +use \DOM\Element; use PHPUnit\Framework\TestCase; use VeeWee\Xml\Dom\Document; use VeeWee\Xml\Exception\RuntimeException; @@ -31,7 +31,7 @@ public function test_it_can_map_node_to_xml(): void public function test_it_throws_exception_on_invalid_node(): void { $this->expectException(RuntimeException::class); - $this->expectExceptionMessage('Expected to find an ownerDocument on provided DOMNode'); - xml_string()(new DOMElement('hello')); + $this->expectExceptionMessage('Expected to find an ownerDocument on provided \DOM\Node'); + xml_string()(new \DOM\Element('hello')); } } diff --git a/tests/Xml/Dom/Predicate/IsAttributeTest.php b/tests/Xml/Dom/Predicate/IsAttributeTest.php index 6044f79..a1689be 100644 --- a/tests/Xml/Dom/Predicate/IsAttributeTest.php +++ b/tests/Xml/Dom/Predicate/IsAttributeTest.php @@ -4,7 +4,7 @@ namespace VeeWee\Tests\Xml\Dom\Predicate; -use DOMNode; +use \DOM\Node; use PHPUnit\Framework\TestCase; use VeeWee\Xml\Dom\Document; use function VeeWee\Xml\Dom\Predicate\is_attribute; @@ -15,7 +15,7 @@ final class IsAttributeTest extends TestCase * * @dataProvider provideTestCases */ - public function test_it_knows_attributes(DOMNode $node, bool $expected): void + public function test_it_knows_attributes(\DOM\Node $node, bool $expected): void { static::assertSame($expected, is_attribute($node)); } diff --git a/tests/Xml/Dom/Predicate/IsCDataTest.php b/tests/Xml/Dom/Predicate/IsCDataTest.php index 779d834..b7bfc91 100644 --- a/tests/Xml/Dom/Predicate/IsCDataTest.php +++ b/tests/Xml/Dom/Predicate/IsCDataTest.php @@ -4,7 +4,7 @@ namespace VeeWee\Tests\Xml\Dom\Predicate; -use DOMNode; +use \DOM\Node; use PHPUnit\Framework\TestCase; use VeeWee\Xml\Dom\Document; use function VeeWee\Xml\Dom\Predicate\is_cdata; @@ -15,7 +15,7 @@ final class IsCDataTest extends TestCase * * @dataProvider provideTestCases */ - public function test_it_knows_cdata(?DOMNode $node, bool $expected): void + public function test_it_knows_cdata(?\DOM\Node $node, bool $expected): void { $actual = is_cdata($node); static::assertSame($expected, $actual); diff --git a/tests/Xml/Dom/Predicate/IsDefaultXmlnsAttributeTest.php b/tests/Xml/Dom/Predicate/IsDefaultXmlnsAttributeTest.php index caaf170..86f3e31 100644 --- a/tests/Xml/Dom/Predicate/IsDefaultXmlnsAttributeTest.php +++ b/tests/Xml/Dom/Predicate/IsDefaultXmlnsAttributeTest.php @@ -4,8 +4,8 @@ namespace VeeWee\Tests\Xml\Dom\Predicate; -use DOMNameSpaceNode; -use DOMNode; +use \DOM\NameSpaceNode; +use \DOM\Node; use PHPUnit\Framework\TestCase; use VeeWee\Xml\Dom\Document; use function VeeWee\Xml\Dom\Predicate\is_default_xmlns_attribute; @@ -16,7 +16,7 @@ final class IsDefaultXmlnsAttributeTest extends TestCase * * @dataProvider provideTestCases */ - public function test_it_knows_default_xmlns_attribute(DOMNode|DOMNameSpaceNode $node, bool $expected): void + public function test_it_knows_default_xmlns_attribute(\DOM\Node|\DOM\NameSpaceNode $node, bool $expected): void { static::assertSame($expected, is_default_xmlns_attribute($node)); } diff --git a/tests/Xml/Dom/Predicate/IsDocumentElementTest.php b/tests/Xml/Dom/Predicate/IsDocumentElementTest.php index 018cd06..5872cef 100644 --- a/tests/Xml/Dom/Predicate/IsDocumentElementTest.php +++ b/tests/Xml/Dom/Predicate/IsDocumentElementTest.php @@ -4,7 +4,7 @@ namespace VeeWee\Tests\Xml\Dom\Predicate; -use DOMNode; +use \DOM\Node; use PHPUnit\Framework\TestCase; use VeeWee\Xml\Dom\Document; use function VeeWee\Xml\Dom\Predicate\is_document_element; @@ -15,7 +15,7 @@ final class IsDocumentElementTest extends TestCase * * @dataProvider provideTestCases */ - public function test_it_knows_document_elements(DOMNode $node, bool $expected): void + public function test_it_knows_document_elements(\DOM\Node $node, bool $expected): void { static::assertSame($expected, is_document_element($node)); } diff --git a/tests/Xml/Dom/Predicate/IsDocumentTest.php b/tests/Xml/Dom/Predicate/IsDocumentTest.php index 8ae4435..3030afb 100644 --- a/tests/Xml/Dom/Predicate/IsDocumentTest.php +++ b/tests/Xml/Dom/Predicate/IsDocumentTest.php @@ -4,7 +4,7 @@ namespace VeeWee\Tests\Xml\Dom\Predicate; -use DOMNode; +use \DOM\Node; use PHPUnit\Framework\TestCase; use VeeWee\Xml\Dom\Document; use function VeeWee\Xml\Dom\Predicate\is_document; @@ -15,7 +15,7 @@ final class IsDocumentTest extends TestCase * * @dataProvider provideTestCases */ - public function test_it_knows_documents(DOMNode $node, bool $expected): void + public function test_it_knows_documents(\DOM\Node $node, bool $expected): void { static::assertSame($expected, is_document($node)); } diff --git a/tests/Xml/Dom/Predicate/IsElementTest.php b/tests/Xml/Dom/Predicate/IsElementTest.php index 0f94a3b..b6bbbe7 100644 --- a/tests/Xml/Dom/Predicate/IsElementTest.php +++ b/tests/Xml/Dom/Predicate/IsElementTest.php @@ -4,7 +4,7 @@ namespace VeeWee\Tests\Xml\Dom\Predicate; -use DOMNode; +use \DOM\Node; use PHPUnit\Framework\TestCase; use VeeWee\Xml\Dom\Document; use function VeeWee\Xml\Dom\Predicate\is_element; @@ -15,7 +15,7 @@ final class IsElementTest extends TestCase * * @dataProvider provideTestCases */ - public function test_it_knows_elements(DOMNode $node, bool $expected): void + public function test_it_knows_elements(\DOM\Node $node, bool $expected): void { static::assertSame($expected, is_element($node)); } diff --git a/tests/Xml/Dom/Predicate/IsNonEmptyTextTest.php b/tests/Xml/Dom/Predicate/IsNonEmptyTextTest.php index 95cc823..55f4143 100644 --- a/tests/Xml/Dom/Predicate/IsNonEmptyTextTest.php +++ b/tests/Xml/Dom/Predicate/IsNonEmptyTextTest.php @@ -4,7 +4,7 @@ namespace VeeWee\Tests\Xml\Dom\Predicate; -use DOMNode; +use \DOM\Node; use PHPUnit\Framework\TestCase; use VeeWee\Xml\Dom\Document; use function VeeWee\Xml\Dom\Predicate\is_non_empty_text; @@ -15,7 +15,7 @@ final class IsNonEmptyTextTest extends TestCase * * @dataProvider provideTestCases */ - public function test_it_knows_text(DOMNode $node, bool $expected): void + public function test_it_knows_text(\DOM\Node $node, bool $expected): void { static::assertSame($expected, is_non_empty_text($node)); } diff --git a/tests/Xml/Dom/Predicate/IsTextTest.php b/tests/Xml/Dom/Predicate/IsTextTest.php index bbddf78..f3902a0 100644 --- a/tests/Xml/Dom/Predicate/IsTextTest.php +++ b/tests/Xml/Dom/Predicate/IsTextTest.php @@ -4,7 +4,7 @@ namespace VeeWee\Tests\Xml\Dom\Predicate; -use DOMNode; +use \DOM\Node; use PHPUnit\Framework\TestCase; use VeeWee\Xml\Dom\Document; use function VeeWee\Xml\Dom\Predicate\is_text; @@ -15,7 +15,7 @@ final class IsTextTest extends TestCase * * @dataProvider provideTestCases */ - public function test_it_knows_text(DOMNode $node, bool $expected): void + public function test_it_knows_text(\DOM\Node $node, bool $expected): void { static::assertSame($expected, is_text($node)); } diff --git a/tests/Xml/Dom/Predicate/IsWhitespaceTest.php b/tests/Xml/Dom/Predicate/IsWhitespaceTest.php index 5723c5e..6be5f42 100644 --- a/tests/Xml/Dom/Predicate/IsWhitespaceTest.php +++ b/tests/Xml/Dom/Predicate/IsWhitespaceTest.php @@ -4,7 +4,7 @@ namespace VeeWee\Tests\Xml\Dom\Predicate; -use DOMNode; +use \DOM\Node; use PHPUnit\Framework\TestCase; use VeeWee\Xml\Dom\Document; use function VeeWee\Xml\Dom\Predicate\is_whitespace; @@ -15,7 +15,7 @@ final class IsWhitespaceTest extends TestCase * * @dataProvider provideTestCases */ - public function test_it_knows_whitespaces(DOMNode $node, bool $expected): void + public function test_it_knows_whitespaces(\DOM\Node $node, bool $expected): void { static::assertSame($expected, is_whitespace($node)); } diff --git a/tests/Xml/Dom/Predicate/IsXmlnsAttributeTest.php b/tests/Xml/Dom/Predicate/IsXmlnsAttributeTest.php index 78ca961..32ce19b 100644 --- a/tests/Xml/Dom/Predicate/IsXmlnsAttributeTest.php +++ b/tests/Xml/Dom/Predicate/IsXmlnsAttributeTest.php @@ -4,8 +4,8 @@ namespace VeeWee\Tests\Xml\Dom\Predicate; -use DOMNameSpaceNode; -use DOMNode; +use \DOM\NameSpaceNode; +use \DOM\Node; use PHPUnit\Framework\TestCase; use VeeWee\Xml\Dom\Document; use function VeeWee\Xml\Dom\Predicate\is_xmlns_attribute; @@ -16,7 +16,7 @@ final class IsXmlnsAttributeTest extends TestCase * * @dataProvider provideTestCases */ - public function test_it_knows_xmlns_attributes(DOMNode|DOMNameSpaceNode $node, bool $expected): void + public function test_it_knows_xmlns_attributes(\DOM\Node|\DOM\NameSpaceNode $node, bool $expected): void { static::assertSame($expected, is_xmlns_attribute($node)); } diff --git a/tests/Xml/Dom/Traverser/TraverserTest.php b/tests/Xml/Dom/Traverser/TraverserTest.php index c343ff5..77c134a 100644 --- a/tests/Xml/Dom/Traverser/TraverserTest.php +++ b/tests/Xml/Dom/Traverser/TraverserTest.php @@ -4,7 +4,7 @@ namespace VeeWee\Tests\Xml\Dom\Traverser; -use DOMNode; +use \DOM\Node; use PHPUnit\Framework\TestCase; use VeeWee\Xml\Dom\Document; use VeeWee\Xml\Dom\Traverser\Action; @@ -37,7 +37,7 @@ public function test_it_can_traverse_single_node(): void $traverser = new Traverser( new class() extends AbstractVisitor { - public function onNodeLeave(DOMNode $node): Action + public function onNodeLeave(\DOM\Node $node): Action { attribute('who', 'Jos')($node); return new Action\Noop(); @@ -55,7 +55,7 @@ public function test_it_can_traverse_attributes(): void $doc = Document::fromXmlString($actual = ''); $doc->traverse( new class() extends AbstractVisitor { - public function onNodeLeave(DOMNode $node): Action + public function onNodeLeave(\DOM\Node $node): Action { if (!is_attribute($node)) { return new Action\Noop(); @@ -77,7 +77,7 @@ public function test_it_can_traverse_child_nodes(): void $doc = Document::fromXmlString($actual = ' '); $doc->traverse( new class() extends AbstractVisitor { - public function onNodeLeave(DOMNode $node): Action + public function onNodeLeave(\DOM\Node $node): Action { if (is_non_empty_text($node)) { $node->nodeValue = 'Yo'; @@ -107,7 +107,7 @@ public function test_it_can_handle_node_enter_and_leave(): void $doc = Document::fromXmlString($actual = ''); $doc->traverse( new class() extends AbstractVisitor { - public function onNodeEnter(DOMNode $node): Action + public function onNodeEnter(\DOM\Node $node): Action { if (!is_element($node)) { return new Action\Noop(); @@ -117,7 +117,7 @@ public function onNodeEnter(DOMNode $node): Action return new Action\Noop(); } - public function onNodeLeave(DOMNode $node): Action + public function onNodeLeave(\DOM\Node $node): Action { if (!is_element($node)) { return new Action\Noop(); @@ -153,7 +153,7 @@ public function test_it_can_recursively_remove_empty_nodes(): void ); $transformedNode = $doc->traverse(new class extends AbstractVisitor { - public function onNodeLeave(DOMNode $node): Action + public function onNodeLeave(\DOM\Node $node): Action { if (!is_element($node)) { return new Action\Noop(); diff --git a/tests/Xml/Dom/Validator/ValidatorChainTest.php b/tests/Xml/Dom/Validator/ValidatorChainTest.php index 06abfe2..5ca3831 100644 --- a/tests/Xml/Dom/Validator/ValidatorChainTest.php +++ b/tests/Xml/Dom/Validator/ValidatorChainTest.php @@ -4,7 +4,7 @@ namespace VeeWee\Tests\Xml\Dom\Validator; -use DOMDocument; +use \DOM\XMLDocument as DOMDocument; use PHPUnit\Framework\TestCase; use VeeWee\Tests\Xml\ErrorHandling\Issue\UseIssueTrait; use VeeWee\Xml\Dom\Document; diff --git a/tests/Xml/Dom/Xpath/Locator/QuerySingleTest.php b/tests/Xml/Dom/Xpath/Locator/QuerySingleTest.php index ee2d577..2dbe8cb 100644 --- a/tests/Xml/Dom/Xpath/Locator/QuerySingleTest.php +++ b/tests/Xml/Dom/Xpath/Locator/QuerySingleTest.php @@ -2,7 +2,7 @@ namespace VeeWee\Tests\Xml\Dom\Xpath\Locator; -use DOMElement; +use \DOM\Element; use PHPUnit\Framework\TestCase; use VeeWee\Xml\Dom\Document; use VeeWee\Xml\Exception\RuntimeException; @@ -36,7 +36,7 @@ public function test_it_can_find_single_xpath_element(): void $xpath = $this->provideXml()->xpath(); $actual = $xpath->querySingle('//item'); - static::assertInstanceOf(DOMElement::class, $actual); + static::assertInstanceOf(\DOM\Element::class, $actual); } @@ -48,7 +48,7 @@ public function test_it_can_find_single_xpath_element_with_node_context(): void $xpath = $doc->xpath(); $actual = $xpath->querySingle('./world', $hello); - static::assertInstanceOf(DOMElement::class, $actual); + static::assertInstanceOf(\DOM\Element::class, $actual); } private function provideXml(): Document From 74328c7b0bdffb16d4d3f171c017f6967969e105 Mon Sep 17 00:00:00 2001 From: Toon Verwerft Date: Fri, 23 Feb 2024 07:40:03 +0100 Subject: [PATCH 02/10] Differences between legacy and new API --- src/Xml/Dom/Assert/assert_cdata.php | 8 +- src/Xml/Dom/Builder/cdata.php | 8 +- src/Xml/Dom/Builder/escaped_value.php | 5 +- src/Xml/Dom/Builder/value.php | 5 +- src/Xml/Dom/Collection/NodeList.php | 9 + src/Xml/Dom/Configurator/canonicalize.php | 10 +- src/Xml/Dom/Configurator/loader.php | 21 - src/Xml/Dom/Configurator/normalize.php | 3 +- src/Xml/Dom/Configurator/pretty_print.php | 3 +- src/Xml/Dom/Configurator/trim_spaces.php | 3 +- src/Xml/Dom/Configurator/utf8.php | 2 +- src/Xml/Dom/Document.php | 35 +- src/Xml/Dom/Loader/Loader.php | 2 +- src/Xml/Dom/Loader/load.php | 25 - src/Xml/Dom/Loader/xml_file_loader.php | 18 +- src/Xml/Dom/Loader/xml_node_loader.php | 14 +- src/Xml/Dom/Loader/xml_string_loader.php | 9 +- .../Element/locate_by_namespaced_tag_name.php | 2 +- .../Locator/Element/locate_by_tag_name.php | 2 +- src/Xml/Dom/Locator/Node/detect_document.php | 5 +- src/Xml/Dom/Locator/Node/value.php | 5 +- src/Xml/Dom/Manipulator/Element/rename.php | 5 +- .../Node/replace_by_external_nodes.php | 13 +- src/Xml/Dom/Mapper/xslt_template.php | 10 +- src/Xml/Dom/Predicate/is_cdata.php | 6 +- .../Dom/Traverser/Visitor/SortAttributes.php | 9 +- src/Xml/Xslt/Configurator/functions.php | 2 + src/bootstrap.php | 2 - stubs/DOM.phpstub | 1662 ++++++++++++++++- tests/Xml/Dom/Builder/CdataTest.php | 6 +- tests/Xml/Dom/Builder/ChildrenTest.php | 4 +- tests/Xml/Dom/Builder/ElementTest.php | 2 +- tests/Xml/Dom/Builder/EscapedValueTest.php | 2 +- tests/Xml/Dom/Builder/ValueTest.php | 2 +- tests/Xml/Dom/Collection/NodeListTest.php | 18 +- tests/Xml/Dom/Configurator/LoaderTest.php | 41 - .../Xml/Dom/Configurator/PrettyPrintTest.php | 2 +- tests/Xml/Dom/Configurator/TrimSpacesTest.php | 2 +- tests/Xml/Dom/Configurator/Utf8Test.php | 2 +- tests/Xml/Dom/DocumentTest.php | 4 +- tests/Xml/Dom/Loader/XmlFileLoaderTest.php | 16 +- tests/Xml/Dom/Loader/XmlNodeLoaderTest.php | 8 +- tests/Xml/Dom/Loader/XmlStringLoaderTest.php | 11 +- .../Xml/Dom/Locator/Element/SiblingsTest.php | 6 +- tests/Xml/Dom/Locator/Node/ChildrenTest.php | 2 +- .../Dom/Locator/Node/DetectDocumentTest.php | 12 - .../Dom/Locator/Xsd/LocateXsdSchemasTest.php | 2 +- .../Manipulator/Node/ImportNodeDeeplyTest.php | 6 +- .../Node/ReplaceByExternalNodeTest.php | 2 +- .../Node/ReplaceByExternalNodesTest.php | 2 +- tests/Xml/Dom/Mapper/XmlStringTest.php | 11 +- tests/Xml/Dom/Traverser/TraverserTest.php | 16 +- .../Traverser/Visitor/SortAttributesTest.php | 2 +- tests/Xml/Dom/XpathTest.php | 2 +- 54 files changed, 1821 insertions(+), 265 deletions(-) delete mode 100644 src/Xml/Dom/Configurator/loader.php delete mode 100644 src/Xml/Dom/Loader/load.php delete mode 100644 tests/Xml/Dom/Configurator/LoaderTest.php diff --git a/src/Xml/Dom/Assert/assert_cdata.php b/src/Xml/Dom/Assert/assert_cdata.php index 21ca62a..b9c2167 100644 --- a/src/Xml/Dom/Assert/assert_cdata.php +++ b/src/Xml/Dom/Assert/assert_cdata.php @@ -4,15 +4,15 @@ namespace VeeWee\Xml\Dom\Assert; -use \DOM\CdataSection; +use \DOM\CDATASection; use Psl\Type\Exception\AssertException; use function Psl\Type\instance_of; /** - * @psalm-assert \DOM\CdataSection $node + * @psalm-assert \DOM\CDATASection $node * @throws AssertException */ -function assert_cdata(mixed $node): \DOM\CdataSection +function assert_cdata(mixed $node): \DOM\CDATASection { - return instance_of(\DOM\CdataSection::class)->assert($node); + return instance_of(\DOM\CDATASection::class)->assert($node); } diff --git a/src/Xml/Dom/Builder/cdata.php b/src/Xml/Dom/Builder/cdata.php index bcfd39b..a8e2085 100644 --- a/src/Xml/Dom/Builder/cdata.php +++ b/src/Xml/Dom/Builder/cdata.php @@ -5,20 +5,20 @@ namespace VeeWee\Xml\Dom\Builder; use Closure; -use \DOM\CdataSection; +use \DOM\CDATASection; use \DOM\Node; use function VeeWee\Xml\Dom\Assert\assert_cdata; use function VeeWee\Xml\Dom\Locator\Node\detect_document; use function VeeWee\Xml\Internal\configure; /** - * @param list $configurators + * @param list $configurators * - * @return Closure(\DOM\Node): \DOM\CdataSection + * @return Closure(\DOM\Node): \DOM\CDATASection */ function cdata(string $data, ...$configurators): Closure { - return static function (\DOM\Node $node) use ($data, $configurators): \DOM\CdataSection { + return static function (\DOM\Node $node) use ($data, $configurators): \DOM\CDATASection { $document = detect_document($node); return assert_cdata( diff --git a/src/Xml/Dom/Builder/escaped_value.php b/src/Xml/Dom/Builder/escaped_value.php index 2d63b83..1529770 100644 --- a/src/Xml/Dom/Builder/escaped_value.php +++ b/src/Xml/Dom/Builder/escaped_value.php @@ -5,7 +5,6 @@ namespace VeeWee\Xml\Dom\Builder; use Closure; -use \DOM\Element; /** * @return Closure(\DOM\Element): \DOM\Element @@ -13,8 +12,6 @@ function escaped_value(string $value): Closure { return static function (\DOM\Element $node) use ($value): \DOM\Element { - $node->nodeValue = htmlspecialchars($value, ENT_XML1|ENT_QUOTES); - - return $node; + return value(htmlspecialchars($value, ENT_XML1|ENT_QUOTES))($node); }; } diff --git a/src/Xml/Dom/Builder/value.php b/src/Xml/Dom/Builder/value.php index 48c3eb5..7533d0c 100644 --- a/src/Xml/Dom/Builder/value.php +++ b/src/Xml/Dom/Builder/value.php @@ -6,6 +6,7 @@ use Closure; use \DOM\Element; +use function VeeWee\Xml\Dom\Locator\Node\detect_document; /** * @return Closure(\DOM\Element): \DOM\Element @@ -13,7 +14,9 @@ function value(string $value): Closure { return static function (\DOM\Element $node) use ($value): \DOM\Element { - $node->nodeValue = $value; + $document = detect_document($node); + $text = $document->createTextNode($value); + $node->appendChild($text); return $node; }; diff --git a/src/Xml/Dom/Collection/NodeList.php b/src/Xml/Dom/Collection/NodeList.php index 8ead96a..fa8191e 100644 --- a/src/Xml/Dom/Collection/NodeList.php +++ b/src/Xml/Dom/Collection/NodeList.php @@ -59,6 +59,15 @@ public static function empty(): self return new self(); } + /** + * @param \DOM\HTMLCollection $list + * @return NodeList<\DOM\Element> + */ + public static function fromDOMHTMLCollection(\DOM\HTMLCollection $list): self + { + return new self(...values($list->getIterator())); + } + /** * @template X of \DOM\Node * @param DOMNodeList $list diff --git a/src/Xml/Dom/Configurator/canonicalize.php b/src/Xml/Dom/Configurator/canonicalize.php index 42c9205..15075b6 100644 --- a/src/Xml/Dom/Configurator/canonicalize.php +++ b/src/Xml/Dom/Configurator/canonicalize.php @@ -8,6 +8,7 @@ use \DOM\XMLDocument as DOMDocument; use VeeWee\Xml\Dom\Document; use function Psl\Type\non_empty_string; +use function VeeWee\Xml\Dom\Loader\xml_string_loader; /** * @return Closure(DOMDocument): DOMDocument @@ -15,9 +16,12 @@ function canonicalize(): Closure { return static fn (DOMDocument $document): DOMDocument - => Document::fromXmlString( - non_empty_string()->assert($document->C14N()), // TODO : LIBXML_NSCLEAN + LIBXML_NOCDATA - // pretty_print() : TODO + => Document::fromLoader( + xml_string_loader( + non_empty_string()->assert($document->C14N()), + LIBXML_NSCLEAN + LIBXML_NOCDATA + ), + pretty_print(), normalize() )->toUnsafeDocument(); } diff --git a/src/Xml/Dom/Configurator/loader.php b/src/Xml/Dom/Configurator/loader.php deleted file mode 100644 index fe5bb56..0000000 --- a/src/Xml/Dom/Configurator/loader.php +++ /dev/null @@ -1,21 +0,0 @@ -normalizeDocument(); + $document->normalize(); return $document; }; diff --git a/src/Xml/Dom/Configurator/pretty_print.php b/src/Xml/Dom/Configurator/pretty_print.php index a697700..7138440 100644 --- a/src/Xml/Dom/Configurator/pretty_print.php +++ b/src/Xml/Dom/Configurator/pretty_print.php @@ -13,7 +13,8 @@ function pretty_print(): Closure { return static function (\DOM\XMLDocument $document): \DOM\XMLDocument { - $document->preserveWhiteSpace = false; + // TODO : not fully implemented yet in the new API + //$document->preserveWhiteSpace = false; $document->formatOutput = true; return $document; diff --git a/src/Xml/Dom/Configurator/trim_spaces.php b/src/Xml/Dom/Configurator/trim_spaces.php index 6009749..9b23089 100644 --- a/src/Xml/Dom/Configurator/trim_spaces.php +++ b/src/Xml/Dom/Configurator/trim_spaces.php @@ -13,7 +13,8 @@ function trim_spaces(): Closure { return static function (\DOM\XMLDocument $document): \DOM\XMLDocument { - $document->preserveWhiteSpace = false; + // TODO : not fully implemented yet in the new API + //$document->preserveWhiteSpace = false; $document->formatOutput = false; return $document; diff --git a/src/Xml/Dom/Configurator/utf8.php b/src/Xml/Dom/Configurator/utf8.php index 688aaf2..afdeaa2 100644 --- a/src/Xml/Dom/Configurator/utf8.php +++ b/src/Xml/Dom/Configurator/utf8.php @@ -13,7 +13,7 @@ function utf8(): Closure { return static function (\DOM\XMLDocument $document): \DOM\XMLDocument { - $document->encoding = 'UTF-8'; + $document->charset = 'UTF-8'; return $document; }; diff --git a/src/Xml/Dom/Document.php b/src/Xml/Dom/Document.php index 61786d1..bb6b096 100644 --- a/src/Xml/Dom/Document.php +++ b/src/Xml/Dom/Document.php @@ -14,10 +14,7 @@ use VeeWee\Xml\ErrorHandling\Issue\IssueCollection; use VeeWee\Xml\Exception\RuntimeException; use function Psl\Vec\map; -use function VeeWee\Xml\Dom\Configurator\loader; -use function VeeWee\Xml\Dom\Loader\xml_file_loader; -use function VeeWee\Xml\Dom\Loader\xml_node_loader; -use function VeeWee\Xml\Dom\Loader\xml_string_loader; +use function VeeWee\Xml\Dom\Loader; use function VeeWee\Xml\Dom\Locator\document_element; use function VeeWee\Xml\Dom\Mapper\xml_string; use function VeeWee\Xml\Internal\configure; @@ -47,18 +44,28 @@ public static function configure(callable ... $configurators): self } /** + * @param callable(): XMLDocument $loader * @param list $configurators * * @throws RuntimeException */ - public static function fromXmlFile(string $file, callable ...$configurators): self + public static function fromLoader(callable $loader, callable ...$configurators): self { return new self( - configure(...$configurators)(XMLDocument::createFromFile($file)) - // TODO : What with loader(xml_file_loader($file)) + configure(...$configurators)($loader()) ); } + /** + * @param list $configurators + * + * @throws RuntimeException + */ + public static function fromXmlFile(string $file, callable ...$configurators): self + { + return self::fromLoader(Loader\xml_file_loader($file), ...$configurators); + } + /** * @param non-empty-string $xml * @param list $configurators @@ -67,10 +74,7 @@ public static function fromXmlFile(string $file, callable ...$configurators): se */ public static function fromXmlString(string $xml, callable ...$configurators): self { - return new self( - configure(...$configurators)(XMLDocument::createFromString($xml)) - // TODO : What with loader(xml_string_loader($file)) - ); + return self::fromLoader(Loader\xml_string_loader($xml), ...$configurators); } /** @@ -80,10 +84,7 @@ public static function fromXmlString(string $xml, callable ...$configurators): s */ public static function fromXmlNode(\DOM\Node $node, callable ...$configurators): self { - return self::configure( - loader(xml_node_loader($node)), - ...$configurators - ); + return self::fromLoader(Loader\xml_node_loader($node), ...$configurators); } /** @@ -93,9 +94,7 @@ public static function fromXmlNode(\DOM\Node $node, callable ...$configurators): */ public static function fromUnsafeDocument(XMLDocument $document, callable ...$configurators): self { - return new self( - configure(...$configurators)($document) - ); + return self::fromLoader(static fn () => $document, ...$configurators); } public function toUnsafeDocument(): XMLDocument diff --git a/src/Xml/Dom/Loader/Loader.php b/src/Xml/Dom/Loader/Loader.php index 98b1e5e..f0c34c9 100644 --- a/src/Xml/Dom/Loader/Loader.php +++ b/src/Xml/Dom/Loader/Loader.php @@ -8,5 +8,5 @@ interface Loader { - public function __invoke(\DOM\XMLDocument $document): void; + public function __invoke(): XMLDocument; } diff --git a/src/Xml/Dom/Loader/load.php b/src/Xml/Dom/Loader/load.php deleted file mode 100644 index 8f1bc64..0000000 --- a/src/Xml/Dom/Loader/load.php +++ /dev/null @@ -1,25 +0,0 @@ -load($file, $options); - } - ); - }; + return static fn () => disallow_issues(static function () use ($file, $options): XMLDocument { + Assert::fileExists($file); + + return XMLDocument::createFromFile($file, $options); + }); } diff --git a/src/Xml/Dom/Loader/xml_node_loader.php b/src/Xml/Dom/Loader/xml_node_loader.php index 7a5f1ef..9222cd1 100644 --- a/src/Xml/Dom/Loader/xml_node_loader.php +++ b/src/Xml/Dom/Loader/xml_node_loader.php @@ -6,16 +6,20 @@ use Closure; use \DOM\XMLDocument; - use \DOM\Node; use function VeeWee\Xml\Dom\Manipulator\Node\append_external_node; +use function VeeWee\Xml\ErrorHandling\disallow_issues; /** - * @return Closure(\DOM\XMLDocument): void + * @return Closure(): XMLDocument */ function xml_node_loader(\DOM\Node $importedNode): Closure { - return static function (\DOM\XMLDocument $document) use ($importedNode): void { - load(static fn (): bool => (bool) append_external_node($document, $importedNode)); - }; + return static fn () => disallow_issues(static function () use ($importedNode): XMLDocument { + $document = XMLDocument::createEmpty(); + + append_external_node($document, $importedNode); + + return $document; + }); } diff --git a/src/Xml/Dom/Loader/xml_string_loader.php b/src/Xml/Dom/Loader/xml_string_loader.php index 8191be2..c7dd097 100644 --- a/src/Xml/Dom/Loader/xml_string_loader.php +++ b/src/Xml/Dom/Loader/xml_string_loader.php @@ -6,15 +6,16 @@ use Closure; use \DOM\XMLDocument; +use function VeeWee\Xml\ErrorHandling\disallow_issues; /** * @param non-empty-string $xml * @param int $options - bitmask of LIBXML_* constants https://www.php.net/manual/en/libxml.constants.php - * @return Closure(\DOM\XMLDocument): void + * @return Closure(): XMLDocument */ function xml_string_loader(string $xml, int $options = 0): Closure { - return static function (\DOM\XMLDocument $document) use ($xml, $options): void { - load(static fn (): bool => $document->loadXML($xml, $options)); - }; + return static fn () => disallow_issues(static function () use ($xml, $options): XMLDocument { + return XMLDocument::createFromString($xml, $options); + }); } diff --git a/src/Xml/Dom/Locator/Element/locate_by_namespaced_tag_name.php b/src/Xml/Dom/Locator/Element/locate_by_namespaced_tag_name.php index 5124bb5..55dd221 100644 --- a/src/Xml/Dom/Locator/Element/locate_by_namespaced_tag_name.php +++ b/src/Xml/Dom/Locator/Element/locate_by_namespaced_tag_name.php @@ -12,5 +12,5 @@ */ function locate_by_namespaced_tag_name(\DOM\Element $node, string $namespace, string $localTagName): NodeList { - return NodeList::fromDOMNodeList($node->getElementsByTagNameNS($namespace, $localTagName)); + return NodeList::fromDOMHTMLCollection($node->getElementsByTagNameNS($namespace, $localTagName)); } diff --git a/src/Xml/Dom/Locator/Element/locate_by_tag_name.php b/src/Xml/Dom/Locator/Element/locate_by_tag_name.php index b134ca2..f299118 100644 --- a/src/Xml/Dom/Locator/Element/locate_by_tag_name.php +++ b/src/Xml/Dom/Locator/Element/locate_by_tag_name.php @@ -12,5 +12,5 @@ */ function locate_by_tag_name(\DOM\Element $node, string $tag): NodeList { - return NodeList::fromDOMNodeList($node->getElementsByTagName($tag)); + return NodeList::fromDOMHTMLCollection($node->getElementsByTagName($tag)); } diff --git a/src/Xml/Dom/Locator/Node/detect_document.php b/src/Xml/Dom/Locator/Node/detect_document.php index 6511250..86a5c7a 100644 --- a/src/Xml/Dom/Locator/Node/detect_document.php +++ b/src/Xml/Dom/Locator/Node/detect_document.php @@ -16,8 +16,5 @@ */ function detect_document(\DOM\Node $node): \DOM\XMLDocument { - $document = is_document($node) ? $node : $node->ownerDocument; - Assert::notNull($document, 'Expected to find an ownerDocument on provided \DOM\Node.'); - - return $document; + return is_document($node) ? $node : $node->ownerDocument; } diff --git a/src/Xml/Dom/Locator/Node/value.php b/src/Xml/Dom/Locator/Node/value.php index dc51ad8..c82e77d 100644 --- a/src/Xml/Dom/Locator/Node/value.php +++ b/src/Xml/Dom/Locator/Node/value.php @@ -19,5 +19,8 @@ */ function value(\DOM\Node $node, TypeInterface $type) { - return $type->coerce($node->nodeValue); + // TODO : nodeValue did entity substitution + // TODO : nodeValue returns null for elements + // TODO : How to best deal with this? + return $type->coerce($node->textContent ?? ''); } diff --git a/src/Xml/Dom/Manipulator/Element/rename.php b/src/Xml/Dom/Manipulator/Element/rename.php index ae6e8aa..dca0f63 100644 --- a/src/Xml/Dom/Manipulator/Element/rename.php +++ b/src/Xml/Dom/Manipulator/Element/rename.php @@ -4,8 +4,6 @@ namespace VeeWee\Xml\Dom\Manipulator\Element; -use \DOM\Attr; -use \DOM\Element; use \DOM\NameSpaceNode; use VeeWee\Xml\Exception\RuntimeException; use function VeeWee\Xml\Dom\Builder\element; @@ -44,7 +42,8 @@ static function (\DOM\NameSpaceNode $attribute) use ($target, $newElement): void ); attributes_list($target)->forEach( - static function (\DOM\Attr $attribute) use ($newElement): void { + static function (\DOM\Attr $attribute) use ($target, $newElement): void { + $target->removeAttributeNode($attribute); $newElement->setAttributeNode($attribute); } ); diff --git a/src/Xml/Dom/Manipulator/Node/replace_by_external_nodes.php b/src/Xml/Dom/Manipulator/Node/replace_by_external_nodes.php index e9a92a7..8ac2ec1 100644 --- a/src/Xml/Dom/Manipulator/Node/replace_by_external_nodes.php +++ b/src/Xml/Dom/Manipulator/Node/replace_by_external_nodes.php @@ -4,11 +4,11 @@ namespace VeeWee\Xml\Dom\Manipulator\Node; -use \DOM\Node; use VeeWee\Xml\Exception\RuntimeException; use Webmozart\Assert\Assert; use function get_class; use function Psl\Dict\map; +use function VeeWee\Xml\Dom\Predicate\is_document_element; use function VeeWee\Xml\ErrorHandling\disallow_issues; /** @@ -30,11 +30,20 @@ static function () use ($target, $sources) : array { static fn (\DOM\Node $source): \DOM\Node => import_node_deeply($target, $source) ); + // Documents can only contain one element, so in case of documentElement, we remove it first to avoid errors. + if (is_document_element($target)) { + $parentNode->removeChild($target); + $target = null; + } + foreach ($copies as $copy) { $parentNode->insertBefore($copy, $target); } - $parentNode->removeChild($target); + // In case of all other elements : the target only gets removed after replacement. + if ($target) { + $parentNode->removeChild($target); + } return $copies; } diff --git a/src/Xml/Dom/Mapper/xslt_template.php b/src/Xml/Dom/Mapper/xslt_template.php index 9862e39..8600b18 100644 --- a/src/Xml/Dom/Mapper/xslt_template.php +++ b/src/Xml/Dom/Mapper/xslt_template.php @@ -5,19 +5,19 @@ namespace VeeWee\Xml\Dom\Mapper; use Closure; -use DOMDocument as DOMDocument; -use VeeWee\XmlDOMDocument; +use VeeWee\Xml\Dom\Document; +use DOM\XMLDocument; use VeeWee\Xml\Xslt\Processor; use XSLTProcessor; /** * @param list $configurators - * @return Closure(DOMDocument): string + * @return Closure(XMLDocument): string */ function xslt_template(Document $template, callable ... $configurators): Closure { - return static fn (DOMDocument $document): string - => Processor::fromTemplateDocument($template, ...$configurators)->transformDocumentToString( + return static fn (XMLDocument $document): string => + Processor::fromTemplateDocument($template, ...$configurators)->transformDocumentToString( Document::fromUnsafeDocument($document) ); } diff --git a/src/Xml/Dom/Predicate/is_cdata.php b/src/Xml/Dom/Predicate/is_cdata.php index 60b7ac2..3fbd1ef 100644 --- a/src/Xml/Dom/Predicate/is_cdata.php +++ b/src/Xml/Dom/Predicate/is_cdata.php @@ -4,13 +4,13 @@ namespace VeeWee\Xml\Dom\Predicate; -use \DOM\CdataSection; +use \DOM\CDATASection; use \DOM\Node; /** - * @psalm-assert-if-true \DOM\CdataSection $node + * @psalm-assert-if-true \DOM\CDATASection $node */ function is_cdata(\DOM\Node $node): bool { - return $node instanceof \DOM\CdataSection; + return $node instanceof \DOM\CDATASection; } diff --git a/src/Xml/Dom/Traverser/Visitor/SortAttributes.php b/src/Xml/Dom/Traverser/Visitor/SortAttributes.php index 20b02ce..ef166ab 100644 --- a/src/Xml/Dom/Traverser/Visitor/SortAttributes.php +++ b/src/Xml/Dom/Traverser/Visitor/SortAttributes.php @@ -4,12 +4,10 @@ namespace VeeWee\Xml\Dom\Traverser\Visitor; -use \DOM\Attr; -use \DOM\Node; use VeeWee\Xml\Dom\Traverser\Action; use function VeeWee\Xml\Dom\Locator\Attribute\attributes_list; -use function VeeWee\Xml\Dom\Manipulator\append; use function VeeWee\Xml\Dom\Predicate\is_element; +use function VeeWee\Xml\ErrorHandling\disallow_issues; final class SortAttributes extends AbstractVisitor { @@ -23,7 +21,10 @@ public function onNodeEnter(\DOM\Node $node): Action ->sort(static fn (\DOM\Attr $a, \DOM\Attr $b): int => $a->nodeName <=> $b->nodeName) ->forEach( static function (\DOM\Attr $attr) use ($node): void { - append($attr)($node); + disallow_issues(static function () use ($node, $attr) { + $node->removeAttributeNode($attr); + $node->setAttributeNode($attr); + }); } ); diff --git a/src/Xml/Xslt/Configurator/functions.php b/src/Xml/Xslt/Configurator/functions.php index cb6e117..210b170 100644 --- a/src/Xml/Xslt/Configurator/functions.php +++ b/src/Xml/Xslt/Configurator/functions.php @@ -8,6 +8,8 @@ use XSLTProcessor; /** + * TODO : Add support for callables : https://wiki.php.net/rfc/improve_callbacks_dom_and_xsl (either here or through a separate configurator) + * * @param non-empty-list $functions * * @return Closure(XSLTProcessor): XSLTProcessor diff --git a/src/bootstrap.php b/src/bootstrap.php index a6e0898..ce099e0 100644 --- a/src/bootstrap.php +++ b/src/bootstrap.php @@ -24,7 +24,6 @@ 'Xml\Dom\Configurator\canonicalize' => __DIR__.'/Xml/Dom/Configurator/canonicalize.php', 'Xml\Dom\Configurator\comparable' => __DIR__.'/Xml/Dom/Configurator/comparable.php', 'Xml\Dom\Configurator\document_uri' => __DIR__.'/Xml/Dom/Configurator/document_uri.php', - 'Xml\Dom\Configurator\loader' => __DIR__.'/Xml/Dom/Configurator/loader.php', 'Xml\Dom\Configurator\normalize' => __DIR__.'/Xml/Dom/Configurator/normalize.php', 'Xml\Dom\Configurator\optimize_namespaces' => __DIR__.'/Xml/Dom/Configurator/optimize_namespaces.php', 'Xml\Dom\Configurator\pretty_print' => __DIR__.'/Xml/Dom/Configurator/pretty_print.php', @@ -32,7 +31,6 @@ 'Xml\Dom\Configurator\trim_spaces' => __DIR__.'/Xml/Dom/Configurator/trim_spaces.php', 'Xml\Dom\Configurator\utf8' => __DIR__.'/Xml/Dom/Configurator/utf8.php', 'Xml\Dom\Configurator\validator' => __DIR__.'/Xml/Dom/Configurator/validator.php', - 'Xml\Dom\Loader\load' => __DIR__.'/Xml/Dom/Loader/load.php', 'Xml\Dom\Loader\xml_file_loader' => __DIR__.'/Xml/Dom/Loader/xml_file_loader.php', 'Xml\Dom\Loader\xml_node_loader' => __DIR__.'/Xml/Dom/Loader/xml_node_loader.php', 'Xml\Dom\Loader\xml_string_loader' => __DIR__.'/Xml/Dom/Loader/xml_string_loader.php', diff --git a/stubs/DOM.phpstub b/stubs/DOM.phpstub index 520be9c..9fe7fc4 100644 --- a/stubs/DOM.phpstub +++ b/stubs/DOM.phpstub @@ -1,11 +1,1661 @@ toUnsafeDocument(); $node = cdata($data = 'hello')($doc); - static::assertInstanceOf(\DOM\CdataSection::class, $node); + static::assertInstanceOf(\DOM\CDATASection::class, $node); static::assertSame($data, $node->textContent); static::assertSame(xml_string()($node), ''); } @@ -29,7 +29,7 @@ public function test_it_can_build_cdata_with_configurators(): void $doc = Document::empty()->toUnsafeDocument(); $node = cdata($data = 'hello', identity())($doc); - static::assertInstanceOf(\DOM\CdataSection::class, $node); + static::assertInstanceOf(\DOM\CDATASection::class, $node); static::assertSame($data, $node->textContent); } } diff --git a/tests/Xml/Dom/Builder/ChildrenTest.php b/tests/Xml/Dom/Builder/ChildrenTest.php index c97ebc0..ef58f2f 100644 --- a/tests/Xml/Dom/Builder/ChildrenTest.php +++ b/tests/Xml/Dom/Builder/ChildrenTest.php @@ -19,15 +19,13 @@ public function test_it_can_build_document_children(): void $doc = Document::empty()->toUnsafeDocument(); $actual = children( element('world1'), - element('world2') )($doc); static::assertSame($doc, $actual); $children = $doc->childNodes; - static::assertSame(2, $children->count()); + static::assertSame(1, $children->count()); static::assertSame('world1', $children->item(0)->nodeName); - static::assertSame('world2', $children->item(1)->nodeName); } public function test_it_can_build_an_element_with_children(): void diff --git a/tests/Xml/Dom/Builder/ElementTest.php b/tests/Xml/Dom/Builder/ElementTest.php index c1629a3..bd6d9c9 100644 --- a/tests/Xml/Dom/Builder/ElementTest.php +++ b/tests/Xml/Dom/Builder/ElementTest.php @@ -40,7 +40,7 @@ public function test_it_can_build_an_element_with_value(): void static::assertSame('hello', $node->nodeName); static::assertSame('hello', $node->localName); - static::assertSame('world', $node->nodeValue); + static::assertSame('world', $node->textContent); static::assertSame($doc, $node->ownerDocument); } } diff --git a/tests/Xml/Dom/Builder/EscapedValueTest.php b/tests/Xml/Dom/Builder/EscapedValueTest.php index 96957b5..c4efa44 100644 --- a/tests/Xml/Dom/Builder/EscapedValueTest.php +++ b/tests/Xml/Dom/Builder/EscapedValueTest.php @@ -18,7 +18,7 @@ public function test_it_can_build_an_element_with_html_value(): void $doc = Document::empty()->toUnsafeDocument(); $node = element('hello', escaped_value(''))($doc); - static::assertSame('', $node->nodeValue); + static::assertSame('', $node->textContent); // TODO : nodeValue did entity substitution static::assertSame(xml_string()($node), '<wor " ld>'); } } diff --git a/tests/Xml/Dom/Builder/ValueTest.php b/tests/Xml/Dom/Builder/ValueTest.php index 70f679b..00453ea 100644 --- a/tests/Xml/Dom/Builder/ValueTest.php +++ b/tests/Xml/Dom/Builder/ValueTest.php @@ -18,7 +18,7 @@ public function test_it_can_build_an_element_with_html_value(): void $doc = Document::empty()->toUnsafeDocument(); $node = element('hello', value(''))($doc); - static::assertSame('', $node->nodeValue); + static::assertSame('', $node->textContent); static::assertSame(xml_string()($node), '<world>'); } } diff --git a/tests/Xml/Dom/Collection/NodeListTest.php b/tests/Xml/Dom/Collection/NodeListTest.php index 92a6aeb..c1dd517 100644 --- a/tests/Xml/Dom/Collection/NodeListTest.php +++ b/tests/Xml/Dom/Collection/NodeListTest.php @@ -120,7 +120,7 @@ public function test_it_can_expect_items() public function test_it_can_map(): void { $items = $this->loadProducts()->map( - static fn (\DOM\Element $element): string => $element->nodeValue + static fn (\DOM\Element $element): string => $element->textContent ); static::assertSame( @@ -139,7 +139,7 @@ public function test_it_can_loop_over_all_items(): void $x = new MutableVector([]); $this->loadProducts()->forEach( static function (\DOM\Element $element) use ($x) : void { - $x->add($element->nodeValue); + $x->add($element->textContent); } ); @@ -166,11 +166,11 @@ public function test_it_can_filter(): void static::assertSame($all->item(3), $filtered->item(1)); } - + public function test_it_can_reduce(): void { $total = $this->loadPrices()->reduce( - static fn (int $total, \DOM\Element $element): int => $total + (int) $element->nodeValue, + static fn (int $total, \DOM\Element $element): int => $total + (int) $element->textContent, 0 ); @@ -189,7 +189,7 @@ public function test_it_can_detect(): void static::assertCount(2, $list); } - + public function test_it_can_query_xpath(): void { $items = $this->root()->query('./prices/price', identity()); @@ -199,7 +199,7 @@ public function test_it_can_query_xpath(): void } } - + public function test_it_can_evaluate_xpath(): void { $evaluated = $this->loadPrices()->evaluate('number(.)', Type\int(), identity()); @@ -217,7 +217,7 @@ public function test_it_can_equal(): void static::assertSame([$prices->item(3)], [...$prices->eq(3)]); static::assertSame([], [...$prices->eq(4)]); } - + public function test_it_can_get_first(): void { $prices = $this->loadPrices(); @@ -282,7 +282,7 @@ public function test_it_can_expect_last(): void $this->expectExceptionMessage('No last item was found'); NodeList::empty()->expectFirst('No last item was found'); } - + public function test_it_can_search_ancestors(): void { $prices = $this->loadPrices(); @@ -331,7 +331,7 @@ public function test_it_can_validate_types_and_fail(): void public function test_it_can_sort(): void { $prices = $this->loadPrices(); - $sorted = $prices->sort(static fn (\DOM\Node $a, \DOM\Node $b) => $b->nodeValue <=> $a->nodeValue); + $sorted = $prices->sort(static fn (\DOM\Node $a, \DOM\Node $b) => $b->textContent <=> $a->textContent); static::assertSame( [ diff --git a/tests/Xml/Dom/Configurator/LoaderTest.php b/tests/Xml/Dom/Configurator/LoaderTest.php deleted file mode 100644 index 788cd87..0000000 --- a/tests/Xml/Dom/Configurator/LoaderTest.php +++ /dev/null @@ -1,41 +0,0 @@ -toUnsafeDocument(); - $xml = ''; - - $loader = loader(static function (\DOM\XMLDocument $doc) use ($xml): void { - $doc->loadXML($xml); - }); - - $result = $loader($doc); - static::assertSame($doc, $result); - static::assertXmlStringEqualsXmlString($xml, $doc->saveXML()); - } - - - public function test_it_can_mark_xml_loading_as_failed(): void - { - $doc = Document::empty()->toUnsafeDocument(); - $exception = new Exception('Could not load the XML document'); - $loader = loader(static function (\DOM\XMLDocument $doc) use ($exception): void { - throw $exception; - }); - - $this->expectExceptionObject($exception); - $loader($doc); - } -} diff --git a/tests/Xml/Dom/Configurator/PrettyPrintTest.php b/tests/Xml/Dom/Configurator/PrettyPrintTest.php index 79ed80c..2933760 100644 --- a/tests/Xml/Dom/Configurator/PrettyPrintTest.php +++ b/tests/Xml/Dom/Configurator/PrettyPrintTest.php @@ -26,7 +26,7 @@ public function test_it_can_trim_contents(): void EOXML; static::assertSame($doc, $result); - static::assertFalse($doc->preserveWhiteSpace); + // TODO : static::assertFalse($doc->preserveWhiteSpace); static::assertTrue($doc->formatOutput); static::assertSame($expected, xml_string()($doc->documentElement)); } diff --git a/tests/Xml/Dom/Configurator/TrimSpacesTest.php b/tests/Xml/Dom/Configurator/TrimSpacesTest.php index f12fff1..9fd1e13 100644 --- a/tests/Xml/Dom/Configurator/TrimSpacesTest.php +++ b/tests/Xml/Dom/Configurator/TrimSpacesTest.php @@ -19,7 +19,7 @@ public function test_it_can_trim_contents(): void $result = $configurator($doc); static::assertSame($doc, $result); - static::assertFalse($doc->preserveWhiteSpace); + // TODO : static::assertFalse($doc->preserveWhiteSpace); static::assertFalse($doc->formatOutput); static::assertSame('', xml_string()($doc->documentElement)); } diff --git a/tests/Xml/Dom/Configurator/Utf8Test.php b/tests/Xml/Dom/Configurator/Utf8Test.php index ac45857..de470e6 100644 --- a/tests/Xml/Dom/Configurator/Utf8Test.php +++ b/tests/Xml/Dom/Configurator/Utf8Test.php @@ -19,6 +19,6 @@ public function test_it_can_convert_to_utf8(): void $result = $configurator($doc); static::assertSame($doc, $result); - static::assertSame('UTF-8', $doc->encoding); + static::assertSame('UTF-8', $doc->charset); } } diff --git a/tests/Xml/Dom/DocumentTest.php b/tests/Xml/Dom/DocumentTest.php index 09a4d29..ee4be47 100644 --- a/tests/Xml/Dom/DocumentTest.php +++ b/tests/Xml/Dom/DocumentTest.php @@ -58,9 +58,9 @@ public function test_it_can_add_various_configurators(): void ); $document = $doc->toUnsafeDocument(); - static::assertFalse($document->preserveWhiteSpace); + // TODO : Not implemented yet static::assertFalse($document->preserveWhiteSpace); static::assertFalse($document->formatOutput); - static::assertSame('UTF-8', $document->encoding); + static::assertSame('UTF-8', $document->charset); static::assertXmlStringEqualsXmlString($xml, $doc->toXmlString()); } diff --git a/tests/Xml/Dom/Loader/XmlFileLoaderTest.php b/tests/Xml/Dom/Loader/XmlFileLoaderTest.php index 9f3f68d..8dd63a0 100644 --- a/tests/Xml/Dom/Loader/XmlFileLoaderTest.php +++ b/tests/Xml/Dom/Loader/XmlFileLoaderTest.php @@ -16,12 +16,11 @@ final class XmlFileLoaderTest extends TestCase public function test_it_can_load_xml_file(): void { - $doc = new \DOM\XMLDocument(); $xml = ''; [$file, $handle] = $this->fillFile($xml); $loader = xml_file_loader($file); - $loader($doc); + $doc = $loader(); fclose($handle); static::assertXmlStringEqualsXmlString($xml, $doc->saveXML()); @@ -29,39 +28,36 @@ public function test_it_can_load_xml_file(): void public function test_it_can_load_with_options(): void { - $doc = new \DOM\XMLDocument(); $xml = ''; [$file, $handle] = $this->fillFile($xml); $loader = xml_file_loader($file, LIBXML_NOCDATA); - $loader($doc); + $doc = $loader(); fclose($handle); static::assertSame('HELLO', $doc->saveXML($doc->documentElement)); } - + public function test_it_cannot_load_invalid_xml_file(): void { - $doc = new \DOM\XMLDocument(); $xml = 'fillFile($xml); $loader = xml_file_loader($file); $this->expectException(RuntimeException::class); - $this->expectExceptionMessage('Could not load the DOM Document'); + $this->expectExceptionMessage('XML document is malformed'); - $loader($doc); + $doc = $loader(); fclose($handle); } public function test_it_throws_exception_on_invalid_file(): void { - $doc = new \DOM\XMLDocument(); $loader = xml_file_loader('invalid-file'); $this->expectException(RuntimeException::class); $this->expectExceptionMessage('The file "invalid-file" does not exist'); - $loader($doc); + $loader(); } } diff --git a/tests/Xml/Dom/Loader/XmlNodeLoaderTest.php b/tests/Xml/Dom/Loader/XmlNodeLoaderTest.php index c0a115e..ca3cc5a 100644 --- a/tests/Xml/Dom/Loader/XmlNodeLoaderTest.php +++ b/tests/Xml/Dom/Loader/XmlNodeLoaderTest.php @@ -15,11 +15,10 @@ final class XmlNodeLoaderTest extends TestCase public function test_it_can_load_xml_node(): void { $source = Document::fromXmlString($xml = '')->toUnsafeDocument(); - $doc = Document::empty()->toUnsafeDocument(); $loader = xml_node_loader($source->documentElement); - $loader($doc); + $doc = $loader(); static::assertXmlStringEqualsXmlString($xml, $doc->saveXML()); } @@ -27,13 +26,12 @@ public function test_it_can_load_xml_node(): void public function test_it_can_not_load_invalid_xml_node(): void { $source = Document::fromXmlString($xml = '')->toUnsafeDocument(); - $doc = Document::empty()->toUnsafeDocument(); $loader = xml_node_loader($source); $this->expectException(RuntimeException::class); - $this->expectExceptionMessage('Cannot import node: Node Type Not Supported'); + $this->expectExceptionMessage('Not Supported Error'); - $loader($doc); + $loader(); } } diff --git a/tests/Xml/Dom/Loader/XmlStringLoaderTest.php b/tests/Xml/Dom/Loader/XmlStringLoaderTest.php index ebed914..8233ce0 100644 --- a/tests/Xml/Dom/Loader/XmlStringLoaderTest.php +++ b/tests/Xml/Dom/Loader/XmlStringLoaderTest.php @@ -13,33 +13,30 @@ final class XmlStringLoaderTest extends TestCase { public function test_it_can_load_xml_string(): void { - $doc = new \DOM\XMLDocument(); $xml = ''; $loader = xml_string_loader($xml); + $doc = $loader(); - $loader($doc); static::assertXmlStringEqualsXmlString($xml, $doc->saveXML()); } public function test_it_can_not_load_invalid_xml_string(): void { - $doc = new \DOM\XMLDocument(); $xml = 'expectException(RuntimeException::class); - $this->expectExceptionMessage('Could not load the DOM Document'); + $this->expectExceptionMessage('XML document is malformed'); - $loader($doc); + $loader(); } public function test_it_can_load_with_options(): void { - $doc = new \DOM\XMLDocument(); $xml = ''; $loader = xml_string_loader($xml, LIBXML_NOCDATA); + $doc = $loader(); - $loader($doc); static::assertSame('HELLO', $doc->saveXML($doc->documentElement)); } } diff --git a/tests/Xml/Dom/Locator/Element/SiblingsTest.php b/tests/Xml/Dom/Locator/Element/SiblingsTest.php index d55909c..26c1a77 100644 --- a/tests/Xml/Dom/Locator/Element/SiblingsTest.php +++ b/tests/Xml/Dom/Locator/Element/SiblingsTest.php @@ -29,8 +29,8 @@ public function test_it_can_detect_siblings(): void $siblings = siblings($domdoc->documentElement->firstElementChild); static::assertCount(3, $siblings); - static::assertSame('1', $siblings->item(0)->nodeValue); - static::assertSame('2', $siblings->item(1)->nodeValue); - static::assertSame('3', $siblings->item(2)->nodeValue); + static::assertSame('1', $siblings->item(0)->textContent); + static::assertSame('2', $siblings->item(1)->textContent); + static::assertSame('3', $siblings->item(2)->textContent); } } diff --git a/tests/Xml/Dom/Locator/Node/ChildrenTest.php b/tests/Xml/Dom/Locator/Node/ChildrenTest.php index d9d0e5d..c554b35 100644 --- a/tests/Xml/Dom/Locator/Node/ChildrenTest.php +++ b/tests/Xml/Dom/Locator/Node/ChildrenTest.php @@ -23,6 +23,6 @@ public function test_it_can_detect_children(): void static::assertCount(3, $children); static::assertSame('world', $children->item(0)->nodeName); static::assertSame('moon', $children->item(1)->nodeName); - static::assertSame('Comment', $children->item(2)->nodeValue); + static::assertSame('Comment', $children->item(2)->textContent); } } diff --git a/tests/Xml/Dom/Locator/Node/DetectDocumentTest.php b/tests/Xml/Dom/Locator/Node/DetectDocumentTest.php index 4c7e101..2d5ef0d 100644 --- a/tests/Xml/Dom/Locator/Node/DetectDocumentTest.php +++ b/tests/Xml/Dom/Locator/Node/DetectDocumentTest.php @@ -5,7 +5,6 @@ namespace VeeWee\Tests\Xml\Dom\Locator\Node; use \DOM\Element; -use InvalidArgumentException; use PHPUnit\Framework\TestCase; use VeeWee\Xml\Dom\Document; use function VeeWee\Xml\Dom\Locator\Node\detect_document; @@ -20,15 +19,4 @@ public function test_it_can_detect_document(): void static::assertSame($domdoc, detect_document($domdoc)); static::assertSame($domdoc, detect_document($domdoc->documentElement)); } - - - public function test_it_throws_exception_on_unlinked_node(): void - { - $element = new \DOM\Element('name'); - - $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage('Expected to find an ownerDocument on provided \DOM\Node.'); - - detect_document($element); - } } diff --git a/tests/Xml/Dom/Locator/Xsd/LocateXsdSchemasTest.php b/tests/Xml/Dom/Locator/Xsd/LocateXsdSchemasTest.php index a0d6bfd..3289e5d 100644 --- a/tests/Xml/Dom/Locator/Xsd/LocateXsdSchemasTest.php +++ b/tests/Xml/Dom/Locator/Xsd/LocateXsdSchemasTest.php @@ -12,7 +12,7 @@ use function VeeWee\Xml\Dom\Locator\Xsd\locate_namespaced_xsd_schemas; use function VeeWee\Xml\Dom\Locator\Xsd\locate_no_namespaced_xsd_schemas; -final class LocateAllXsdSchemasTest extends TestCase +final class LocateXsdSchemasTest extends TestCase { public function test_it_can_locate_namespaced_xsd_schemas(): void { diff --git a/tests/Xml/Dom/Manipulator/Node/ImportNodeDeeplyTest.php b/tests/Xml/Dom/Manipulator/Node/ImportNodeDeeplyTest.php index 000cfde..4397871 100644 --- a/tests/Xml/Dom/Manipulator/Node/ImportNodeDeeplyTest.php +++ b/tests/Xml/Dom/Manipulator/Node/ImportNodeDeeplyTest.php @@ -43,7 +43,7 @@ public function test_it_can_not_import_an_invalid_node(): void $target = Document::empty()->toUnsafeDocument(); $this->expectException(RuntimeException::class); - $this->expectExceptionMessage('Cannot import node: Node Type Not Supported'); + $this->expectExceptionMessage('Not Supported Error'); import_node_deeply($target, $source); } @@ -57,8 +57,8 @@ public function test_it_can_recursively_import_a_node_based_on_a_target_document static::assertInstanceOf(\DOM\Element::class, $result); static::assertSame('world', $result->nodeName); - static::assertSame('myvalue', $result->attributes->getNamedItem('myattrib')->nodeValue); + static::assertSame('myvalue', $result->attributes->getNamedItem('myattrib')->textContent); static::assertSame('name', $result->firstChild->nodeName); - static::assertSame('VeeWee', $result->firstChild->nodeValue); + static::assertSame('VeeWee', $result->firstChild->textContent); } } diff --git a/tests/Xml/Dom/Manipulator/Node/ReplaceByExternalNodeTest.php b/tests/Xml/Dom/Manipulator/Node/ReplaceByExternalNodeTest.php index 39610c7..450e882 100644 --- a/tests/Xml/Dom/Manipulator/Node/ReplaceByExternalNodeTest.php +++ b/tests/Xml/Dom/Manipulator/Node/ReplaceByExternalNodeTest.php @@ -32,7 +32,7 @@ public function test_it_can_not_replace_a_document_into_a_document(): void $target = Document::empty()->toUnsafeDocument(); $this->expectException(RuntimeException::class); - $this->expectExceptionMessage('Could not replace a node without parent node. (\DOM\XMLDocument)'); + $this->expectExceptionMessage('Could not replace a node without parent node. (DOM\XMLDocument)'); replace_by_external_node($target, $source); } diff --git a/tests/Xml/Dom/Manipulator/Node/ReplaceByExternalNodesTest.php b/tests/Xml/Dom/Manipulator/Node/ReplaceByExternalNodesTest.php index b5358ab..a16e822 100644 --- a/tests/Xml/Dom/Manipulator/Node/ReplaceByExternalNodesTest.php +++ b/tests/Xml/Dom/Manipulator/Node/ReplaceByExternalNodesTest.php @@ -49,7 +49,7 @@ public function test_it_can_not_replace_a_document_into_a_document(): void $target = Document::empty()->toUnsafeDocument(); $this->expectException(RuntimeException::class); - $this->expectExceptionMessage('Could not replace a node without parent node. (\DOM\XMLDocument)'); + $this->expectExceptionMessage('Could not replace a node without parent node. (DOM\XMLDocument)'); replace_by_external_nodes($target, [$source]); } diff --git a/tests/Xml/Dom/Mapper/XmlStringTest.php b/tests/Xml/Dom/Mapper/XmlStringTest.php index 8bc865d..d1c6f82 100644 --- a/tests/Xml/Dom/Mapper/XmlStringTest.php +++ b/tests/Xml/Dom/Mapper/XmlStringTest.php @@ -14,11 +14,10 @@ final class XmlStringTest extends TestCase { public function test_it_can_map_doc_to_xml(): void { - $doc = Document::fromXmlString($xml = ''.PHP_EOL.''.PHP_EOL); + $doc = Document::fromXmlString($xml = ''.PHP_EOL.''); static::assertSame($xml, xml_string()($doc->toUnsafeDocument())); } - public function test_it_can_map_node_to_xml(): void { $doc = Document::fromXmlString($xml = ''); @@ -26,12 +25,4 @@ public function test_it_can_map_node_to_xml(): void static::assertSame('', xml_string()($node)); } - - - public function test_it_throws_exception_on_invalid_node(): void - { - $this->expectException(RuntimeException::class); - $this->expectExceptionMessage('Expected to find an ownerDocument on provided \DOM\Node'); - xml_string()(new \DOM\Element('hello')); - } } diff --git a/tests/Xml/Dom/Traverser/TraverserTest.php b/tests/Xml/Dom/Traverser/TraverserTest.php index 77c134a..5367c96 100644 --- a/tests/Xml/Dom/Traverser/TraverserTest.php +++ b/tests/Xml/Dom/Traverser/TraverserTest.php @@ -29,7 +29,7 @@ public function test_it_can_traverse_without_visitors(): void static::assertXmlStringEqualsXmlString($doc->toXmlString(), $actual); } - + public function test_it_can_traverse_single_node(): void { $doc = Document::fromXmlString($actual = ''); @@ -49,7 +49,7 @@ public function onNodeLeave(\DOM\Node $node): Action static::assertXmlStringEqualsXmlString(xml_string()($newRoot), ''); } - + public function test_it_can_traverse_attributes(): void { $doc = Document::fromXmlString($actual = ''); @@ -61,7 +61,7 @@ public function onNodeLeave(\DOM\Node $node): Action return new Action\Noop(); } - $node->nodeValue = 'Jos'; + $node->textContent = 'Jos'; return new Action\Noop(); } @@ -71,7 +71,7 @@ public function onNodeLeave(\DOM\Node $node): Action static::assertXmlStringEqualsXmlString($doc->toXmlString(), ''); } - + public function test_it_can_traverse_child_nodes(): void { $doc = Document::fromXmlString($actual = ' '); @@ -80,11 +80,11 @@ public function test_it_can_traverse_child_nodes(): void public function onNodeLeave(\DOM\Node $node): Action { if (is_non_empty_text($node)) { - $node->nodeValue = 'Yo'; + $node->textContent = 'Yo'; return new Action\Noop(); } if (is_whitespace($node)) { - $node->nodeValue = ''; + $node->textContent = ''; return new Action\Noop(); } @@ -101,7 +101,7 @@ public function onNodeLeave(\DOM\Node $node): Action static::assertXmlStringEqualsXmlString($doc->toXmlString(), 'Yo'); } - + public function test_it_can_handle_node_enter_and_leave(): void { $doc = Document::fromXmlString($actual = ''); @@ -132,7 +132,7 @@ public function onNodeLeave(\DOM\Node $node): Action static::assertXmlStringEqualsXmlString($doc->toXmlString(), ''); } - + public function test_it_can_recursively_remove_empty_nodes(): void { $doc = Document::fromXmlString( diff --git a/tests/Xml/Dom/Traverser/Visitor/SortAttributesTest.php b/tests/Xml/Dom/Traverser/Visitor/SortAttributesTest.php index 6659a8b..44a4323 100644 --- a/tests/Xml/Dom/Traverser/Visitor/SortAttributesTest.php +++ b/tests/Xml/Dom/Traverser/Visitor/SortAttributesTest.php @@ -22,7 +22,7 @@ public function test_it_can_sort_attributes(): void EOXML; $expected = << + Jos Jaak Jul diff --git a/tests/Xml/Dom/XpathTest.php b/tests/Xml/Dom/XpathTest.php index 8a09ce3..bedb23a 100644 --- a/tests/Xml/Dom/XpathTest.php +++ b/tests/Xml/Dom/XpathTest.php @@ -47,6 +47,6 @@ public function test_it_can_locate_stuff(): void $result = $xpath->locate(identity()); - static::assertInstanceOf(DOMXPath::class, $result); + static::assertInstanceOf(\DOM\XPath::class, $result); } } From 518eac4432d8f0ea42284921ebb9df9a0768d8b0 Mon Sep 17 00:00:00 2001 From: Toon Verwerft Date: Fri, 23 Feb 2024 15:39:26 +0100 Subject: [PATCH 03/10] New namspaces system --- docs/dom.md | 4 +- src/Xml/Dom/Builder/value.php | 5 +- src/Xml/Dom/Configurator/pretty_print.php | 16 ++- src/Xml/Dom/Configurator/trim_spaces.php | 15 ++- .../Attribute/xmlns_attributes_list.php | 15 +-- src/Xml/Dom/Locator/Node/value.php | 5 +- .../Dom/Locator/Xmlns/linked_namespaces.php | 18 +-- .../Xmlns/recursive_linked_namespaces.php | 18 +-- src/Xml/Dom/Manipulator/Attribute/rename.php | 38 ++----- .../Document/optimize_namespaces.php | 24 ++-- .../Element/copy_named_xmlns_attributes.php | 8 +- src/Xml/Dom/Manipulator/Element/rename.php | 60 ++++------ .../Dom/Manipulator/Node/remove_namespace.php | 8 +- src/Xml/Dom/Manipulator/Node/rename.php | 14 +-- src/Xml/Dom/Manipulator/Xmlns/rename.php | 105 +----------------- .../Xmlns/rename_element_namespace.php | 33 ++++++ src/Xml/Dom/Manipulator/append.php | 10 ++ .../Predicate/is_default_xmlns_attribute.php | 5 +- src/Xml/Dom/Predicate/is_document_element.php | 3 +- src/Xml/Dom/Predicate/is_xmlns_attribute.php | 8 +- src/Xml/Dom/Traverser/Action/RenameNode.php | 5 +- .../Traverser/Visitor/RemoveNamespaces.php | 65 +++++++---- .../Dom/Traverser/Visitor/SortAttributes.php | 6 +- .../Internal/Decoder/Builder/attributes.php | 4 +- .../Internal/Decoder/Builder/namespaces.php | 9 +- src/bootstrap.php | 1 + stubs/DOM.phpstub | 81 ++++++++++---- .../Xml/Dom/Configurator/PrettyPrintTest.php | 7 +- tests/Xml/Dom/Configurator/TrimSpacesTest.php | 7 +- .../Locator/Attribute/AttributesListTest.php | 2 + .../Dom/Locator/Element/ParentElementTest.php | 2 +- .../Locator/Xmlns/LinkedNamespacesTest.php | 36 ++++-- .../Xmlns/RecursiveLinkedNamespacesTest.php | 34 +++--- .../Node/AppendExternalNodeTest.php | 2 +- tests/Xml/Dom/Manipulator/Node/RenameTest.php | 50 ++++----- .../Xml/Dom/Manipulator/Xmlns/RenameTest.php | 41 +++---- tests/Xml/Encoding/EncodingTest.php | 6 +- 37 files changed, 356 insertions(+), 414 deletions(-) create mode 100644 src/Xml/Dom/Manipulator/Xmlns/rename_element_namespace.php diff --git a/docs/dom.md b/docs/dom.md index 56bcc62..8742683 100644 --- a/docs/dom.md +++ b/docs/dom.md @@ -896,7 +896,7 @@ This function returns a list of all namespaces that are linked to a specific DOM use VeeWee\Xml\Dom\Collection\NodeList; use function VeeWee\Xml\Dom\Locator\Xmlns\linked_namespaces; -/** @var NodeList $namespaces */ +/** @var array $namespaces - A lookup of prefix -> namespace */ $namespaces = linked_namespaces($element); ``` @@ -908,7 +908,7 @@ This function returns a list of all namespaces that are linked to a specific DOM use VeeWee\Xml\Dom\Collection\NodeList; use function VeeWee\Xml\Dom\Locator\Xmlns\recursive_linked_namespaces; -/** @var NodeList $namespaces */ +/** @var array $namespaces - A lookup of prefix -> namespace */ $namespaces = recursive_linked_namespaces($element); ``` diff --git a/src/Xml/Dom/Builder/value.php b/src/Xml/Dom/Builder/value.php index 7533d0c..50c7167 100644 --- a/src/Xml/Dom/Builder/value.php +++ b/src/Xml/Dom/Builder/value.php @@ -6,7 +6,6 @@ use Closure; use \DOM\Element; -use function VeeWee\Xml\Dom\Locator\Node\detect_document; /** * @return Closure(\DOM\Element): \DOM\Element @@ -14,9 +13,7 @@ function value(string $value): Closure { return static function (\DOM\Element $node) use ($value): \DOM\Element { - $document = detect_document($node); - $text = $document->createTextNode($value); - $node->appendChild($text); + $node->substitutedNodeValue = $value; return $node; }; diff --git a/src/Xml/Dom/Configurator/pretty_print.php b/src/Xml/Dom/Configurator/pretty_print.php index 7138440..f149301 100644 --- a/src/Xml/Dom/Configurator/pretty_print.php +++ b/src/Xml/Dom/Configurator/pretty_print.php @@ -5,7 +5,8 @@ namespace VeeWee\Xml\Dom\Configurator; use Closure; -use \DOM\XMLDocument; +use VeeWee\Xml\Dom\Document; +use function VeeWee\Xml\Dom\Loader\xml_string_loader; /** * @return Closure(\DOM\XMLDocument): \DOM\XMLDocument @@ -13,10 +14,15 @@ function pretty_print(): Closure { return static function (\DOM\XMLDocument $document): \DOM\XMLDocument { - // TODO : not fully implemented yet in the new API - //$document->preserveWhiteSpace = false; - $document->formatOutput = true; + $trimmed = Document::fromLoader( + xml_string_loader( + Document::fromUnsafeDocument($document)->toXmlString(), + LIBXML_NOBLANKS + ) + )->toUnsafeDocument(); - return $document; + $trimmed->formatOutput = true; + + return $trimmed; }; } diff --git a/src/Xml/Dom/Configurator/trim_spaces.php b/src/Xml/Dom/Configurator/trim_spaces.php index 9b23089..ca22522 100644 --- a/src/Xml/Dom/Configurator/trim_spaces.php +++ b/src/Xml/Dom/Configurator/trim_spaces.php @@ -6,6 +6,8 @@ use Closure; use \DOM\XMLDocument; +use VeeWee\Xml\Dom\Document; +use function VeeWee\Xml\Dom\Loader\xml_string_loader; /** * @return Closure(\DOM\XMLDocument): \DOM\XMLDocument @@ -13,10 +15,15 @@ function trim_spaces(): Closure { return static function (\DOM\XMLDocument $document): \DOM\XMLDocument { - // TODO : not fully implemented yet in the new API - //$document->preserveWhiteSpace = false; - $document->formatOutput = false; + $trimmed = Document::fromLoader( + xml_string_loader( + Document::fromUnsafeDocument($document)->toXmlString(), + LIBXML_NOBLANKS + ) + )->toUnsafeDocument(); - return $document; + $trimmed->formatOutput = false; + + return $trimmed; }; } diff --git a/src/Xml/Dom/Locator/Attribute/xmlns_attributes_list.php b/src/Xml/Dom/Locator/Attribute/xmlns_attributes_list.php index e82545b..5ac1d14 100644 --- a/src/Xml/Dom/Locator/Attribute/xmlns_attributes_list.php +++ b/src/Xml/Dom/Locator/Attribute/xmlns_attributes_list.php @@ -4,23 +4,16 @@ namespace VeeWee\Xml\Dom\Locator\Attribute; -use \DOM\NameSpaceNode; -use \DOM\Node; use VeeWee\Xml\Dom\Collection\NodeList; use VeeWee\Xml\Exception\RuntimeException; -use function VeeWee\Xml\Dom\Locator\Xmlns\linked_namespaces; -use function VeeWee\Xml\Dom\Predicate\is_element; +use function VeeWee\Xml\Dom\Predicate\is_xmlns_attribute; /** - * @return NodeList<\DOM\NameSpaceNode> + * @return NodeList<\DOM\Attr> * @throws RuntimeException */ function xmlns_attributes_list(\DOM\Node $node): NodeList { - if (! is_element($node)) { - return NodeList::empty(); - } - - return linked_namespaces($node) - ->filter(static fn (\DOM\NameSpaceNode $namespace): bool => $node->hasAttribute($namespace->nodeName)); + return attributes_list($node) + ->filter(static fn (\DOM\Attr $attribute): bool => is_xmlns_attribute($attribute)); } diff --git a/src/Xml/Dom/Locator/Node/value.php b/src/Xml/Dom/Locator/Node/value.php index c82e77d..9ee53e9 100644 --- a/src/Xml/Dom/Locator/Node/value.php +++ b/src/Xml/Dom/Locator/Node/value.php @@ -19,8 +19,5 @@ */ function value(\DOM\Node $node, TypeInterface $type) { - // TODO : nodeValue did entity substitution - // TODO : nodeValue returns null for elements - // TODO : How to best deal with this? - return $type->coerce($node->textContent ?? ''); + return $type->coerce($node->substitutedNodeValue ?? ''); } diff --git a/src/Xml/Dom/Locator/Xmlns/linked_namespaces.php b/src/Xml/Dom/Locator/Xmlns/linked_namespaces.php index 8b7ef71..5e66574 100644 --- a/src/Xml/Dom/Locator/Xmlns/linked_namespaces.php +++ b/src/Xml/Dom/Locator/Xmlns/linked_namespaces.php @@ -4,22 +4,10 @@ namespace VeeWee\Xml\Dom\Locator\Xmlns; -use \DOM\NameSpaceNode; -use \DOM\Node; -use InvalidArgumentException; -use VeeWee\Xml\Dom\Collection\NodeList; -use VeeWee\Xml\Dom\Xpath; -use VeeWee\Xml\Exception\RuntimeException; - /** - * @return NodeList<\DOM\NameSpaceNode> - * - * @throws RuntimeException - * @throws InvalidArgumentException + * @return list<\DOM\NamespaceInfo> */ -function linked_namespaces(\DOM\Node $node): NodeList +function linked_namespaces(\DOM\Element $node): array { - $xpath = Xpath::fromUnsafeNode($node); - - return $xpath->query('./namespace::*', $node)->expectAllOfType(\DOM\NameSpaceNode::class); + return $node->getInScopeNamespaces(); } diff --git a/src/Xml/Dom/Locator/Xmlns/recursive_linked_namespaces.php b/src/Xml/Dom/Locator/Xmlns/recursive_linked_namespaces.php index 2f3c766..af2e016 100644 --- a/src/Xml/Dom/Locator/Xmlns/recursive_linked_namespaces.php +++ b/src/Xml/Dom/Locator/Xmlns/recursive_linked_namespaces.php @@ -4,22 +4,10 @@ namespace VeeWee\Xml\Dom\Locator\Xmlns; -use \DOM\NameSpaceNode; -use \DOM\Node; -use InvalidArgumentException; -use VeeWee\Xml\Dom\Collection\NodeList; -use VeeWee\Xml\Dom\Xpath; -use VeeWee\Xml\Exception\RuntimeException; - /** - * @return NodeList<\DOM\NameSpaceNode> - * - * @throws RuntimeException - * @throws InvalidArgumentException + * @return list<\DOM\NamespaceInfo> */ -function recursive_linked_namespaces(\DOM\Node $node): NodeList +function recursive_linked_namespaces(\DOM\Element $node): array { - $xpath = Xpath::fromUnsafeNode($node); - - return $xpath->query('.//namespace::*', $node)->expectAllOfType(\DOM\NameSpaceNode::class); + return $node->getDescendantNamespaces(); } diff --git a/src/Xml/Dom/Manipulator/Attribute/rename.php b/src/Xml/Dom/Manipulator/Attribute/rename.php index 9fa2cc1..e3b2ea7 100644 --- a/src/Xml/Dom/Manipulator/Attribute/rename.php +++ b/src/Xml/Dom/Manipulator/Attribute/rename.php @@ -6,40 +6,18 @@ use \DOM\Attr; use VeeWee\Xml\Exception\RuntimeException; -use function VeeWee\Xml\Dom\Builder\attribute; -use function VeeWee\Xml\Dom\Builder\namespaced_attribute; -use function VeeWee\Xml\Dom\Locator\Element\parent_element; -use function VeeWee\Xml\Dom\Manipulator\Node\remove; -use function VeeWee\Xml\Dom\Predicate\is_attribute; +use function Psl\Fun\tap; +use function VeeWee\Xml\Dom\Manipulator\Xmlns\rename as rename_xmlns_attribute; +use function VeeWee\Xml\Dom\Predicate\is_xmlns_attribute; +use function VeeWee\Xml\ErrorHandling\disallow_issues; /** * @throws RuntimeException */ function rename(\DOM\Attr $target, string $newQName, ?string $newNamespaceURI = null): \DOM\Attr { - $element = parent_element($target); - $namespace = $newNamespaceURI ?? $target->namespaceURI; - $value = $target->nodeValue ?? ''; - - $builder = $namespace !== null - ? namespaced_attribute($namespace, $newQName, $value) - : attribute($newQName, $value); - - remove($target); - $builder($element); - - // If the namespace prefix of the target still exists, PHP will fallback into using that prefix. - // In that case it is not possible to fully rename an attribute. - // If you want to rename a prefix, you'll have to remove the xmlns first - // or make sure the new prefix is found first for the given namespace URI. - $result = $element->getAttributeNode($newQName); - - /** @psalm-suppress TypeDoesNotContainType - It can actually be null if the exact node name is not found. */ - if (!$result || !is_attribute($result)) { - throw RuntimeException::withMessage( - 'Unable to rename attribute '.$target->nodeName.' into '.$newQName.'. You might need to swap xmlns prefix first!' - ); - } - - return $result; + return disallow_issues(static fn (): \DOM\Attr => match(true) { + is_xmlns_attribute($target) => rename_xmlns_attribute($target, $newQName), + default => tap(fn () => $target->rename($newNamespaceURI, $newQName))($target) + }); } diff --git a/src/Xml/Dom/Manipulator/Document/optimize_namespaces.php b/src/Xml/Dom/Manipulator/Document/optimize_namespaces.php index 4710e3f..a1c1df7 100644 --- a/src/Xml/Dom/Manipulator/Document/optimize_namespaces.php +++ b/src/Xml/Dom/Manipulator/Document/optimize_namespaces.php @@ -4,34 +4,26 @@ namespace VeeWee\Xml\Dom\Manipulator\Document; -use \DOM\XMLDocument; -use \DOM\NameSpaceNode; use VeeWee\Xml\Exception\RuntimeException; -use VeeWee\Xml\Xmlns\Xmlns; use function Psl\Dict\unique; +use function Psl\Vec\map; use function Psl\Vec\sort; use function Psl\Vec\values; use function VeeWee\Xml\Dom\Locator\Xmlns\recursive_linked_namespaces; -use function VeeWee\Xml\Dom\Manipulator\Xmlns\rename; +use function VeeWee\Xml\Dom\Manipulator\Xmlns\rename_element_namespace; /** * @throws RuntimeException */ function optimize_namespaces(\DOM\XMLDocument $document, string $prefix = 'ns'): void { - $namespaceURIs = recursive_linked_namespaces($document) - ->filter(static fn (\DOM\NameSpaceNode $node): bool => $node->namespaceURI !== Xmlns::xml()->value()) - ->reduce( - /** - * @param list $grouped - * @return list - */ - static fn (array $grouped, \DOM\NameSpaceNode $node): array - => values(unique([...$grouped, $node->namespaceURI])), - [] - ); + $documentElement = $document->documentElement; + $namespaceURIs = values(unique(map( + recursive_linked_namespaces($documentElement), + static fn (\DOM\NamespaceInfo $info): string => $info->namespaceURI + ))); foreach (sort($namespaceURIs) as $index => $namespaceURI) { - rename($document, $namespaceURI, $prefix . ((string) ($index+1))); + rename_element_namespace($documentElement, $namespaceURI, $prefix . ((string) ($index+1))); } } diff --git a/src/Xml/Dom/Manipulator/Element/copy_named_xmlns_attributes.php b/src/Xml/Dom/Manipulator/Element/copy_named_xmlns_attributes.php index 4392123..b2a32f7 100644 --- a/src/Xml/Dom/Manipulator/Element/copy_named_xmlns_attributes.php +++ b/src/Xml/Dom/Manipulator/Element/copy_named_xmlns_attributes.php @@ -4,20 +4,18 @@ namespace VeeWee\Xml\Dom\Manipulator\Element; -use \DOM\Element; -use \DOM\NameSpaceNode; use VeeWee\Xml\Exception\RuntimeException; use function VeeWee\Xml\Dom\Builder\xmlns_attribute; -use function VeeWee\Xml\Dom\Locator\Xmlns\linked_namespaces; +use function VeeWee\Xml\Dom\Locator\Attribute\xmlns_attributes_list; /** * @throws RuntimeException */ function copy_named_xmlns_attributes(\DOM\Element $target, \DOM\Element $source): void { - linked_namespaces($source)->forEach(static function (\DOM\NameSpaceNode $xmlns) use ($target) { + xmlns_attributes_list($source)->forEach(static function (\DOM\Attr $xmlns) use ($target) { if ($xmlns->prefix && !$target->hasAttribute($xmlns->nodeName)) { - xmlns_attribute($xmlns->prefix, $xmlns->namespaceURI)($target); + xmlns_attribute($xmlns->localName, $xmlns->value)($target); } }); } diff --git a/src/Xml/Dom/Manipulator/Element/rename.php b/src/Xml/Dom/Manipulator/Element/rename.php index dca0f63..58b4aff 100644 --- a/src/Xml/Dom/Manipulator/Element/rename.php +++ b/src/Xml/Dom/Manipulator/Element/rename.php @@ -4,51 +4,31 @@ namespace VeeWee\Xml\Dom\Manipulator\Element; -use \DOM\NameSpaceNode; use VeeWee\Xml\Exception\RuntimeException; -use function VeeWee\Xml\Dom\Builder\element; -use function VeeWee\Xml\Dom\Builder\namespaced_element; -use function VeeWee\Xml\Dom\Builder\xmlns_attribute; -use function VeeWee\Xml\Dom\Locator\Attribute\attributes_list; -use function VeeWee\Xml\Dom\Locator\Attribute\xmlns_attributes_list; -use function VeeWee\Xml\Dom\Locator\Element\parent_element; -use function VeeWee\Xml\Dom\Locator\Node\children; -use function VeeWee\Xml\Dom\Manipulator\append; -use function VeeWee\Xml\Dom\Predicate\is_default_xmlns_attribute; +use function VeeWee\Xml\ErrorHandling\disallow_issues; /** * @throws RuntimeException */ function rename(\DOM\Element $target, string $newQName, ?string $newNamespaceURI = null): \DOM\Element { - $isRootElement = $target === $target->ownerDocument->documentElement; - $parent = $isRootElement ? $target->ownerDocument : parent_element($target); - $namespace = $newNamespaceURI ?? $target->namespaceURI; - $builder = $namespace !== null - ? namespaced_element($namespace, $newQName) - : element($newQName); - - $newElement = $builder($parent); - - append(...children($target))($newElement); - - xmlns_attributes_list($target)->forEach( - static function (\DOM\NameSpaceNode $attribute) use ($target, $newElement): void { - if (is_default_xmlns_attribute($attribute) || $target->prefix === $attribute->prefix) { - return; - } - xmlns_attribute($attribute->prefix, $attribute->namespaceURI)($newElement); - } - ); - - attributes_list($target)->forEach( - static function (\DOM\Attr $attribute) use ($target, $newElement): void { - $target->removeAttributeNode($attribute); - $newElement->setAttributeNode($attribute); - } - ); - - $parent->replaceChild($newElement, $target); - - return $newElement; + $parts = explode(':', $newQName, 2); + $newPrefix = $parts[0] ?? ''; + + /* + * To make sure the new namespace prefix is being used, we need to apply an additional xmlns declaration chech: + * This is due to a particular rule in the XML serialization spec, + * that enforces that a namespaceURI on an element is only associated with exactly one prefix. + * See the note of bullet point 2 of https://www.w3.org/TR/DOM-Parsing/#dfn-concept-serialize-xml. + * + * If you rename a:xx to b:xx an xmlns:b="xx" attribute gets added at the end, but prefix a: will still be serialized. + * So in this case, we need to remove the xmlns declaration first. + */ + if ($newPrefix && $newPrefix !== $target->prefix && $target->hasAttribute('xmlns:'.$target->prefix)) { + $target->removeAttribute('xmlns:'.$target->prefix); + } + + disallow_issues(static fn () => $target->rename($newNamespaceURI, $newQName)); + + return $target; } diff --git a/src/Xml/Dom/Manipulator/Node/remove_namespace.php b/src/Xml/Dom/Manipulator/Node/remove_namespace.php index 169a8d1..a9c7f75 100644 --- a/src/Xml/Dom/Manipulator/Node/remove_namespace.php +++ b/src/Xml/Dom/Manipulator/Node/remove_namespace.php @@ -4,8 +4,6 @@ namespace VeeWee\Xml\Dom\Manipulator\Node; -use \DOM\Element; -use \DOM\NameSpaceNode; use VeeWee\Xml\Exception\RuntimeException; use function VeeWee\Xml\ErrorHandling\disallow_issues; use function VeeWee\Xml\ErrorHandling\disallow_libxml_false_returns; @@ -13,15 +11,15 @@ /** * @throws RuntimeException */ -function remove_namespace(\DOM\NameSpaceNode $target, \DOM\Element $parent): \DOM\NameSpaceNode +function remove_namespace(\DOM\Attr $target, \DOM\Element $parent): \DOM\Attr { return disallow_issues( /** * @throws RuntimeException */ - static function () use ($target, $parent): \DOM\NameSpaceNode { + static function () use ($target, $parent): \DOM\Attr { disallow_libxml_false_returns( - $parent->removeAttributeNS($target->namespaceURI, $target->prefix), + $parent->removeAttributeNode($target), 'Could not remove xmlns attribute from dom element' ); diff --git a/src/Xml/Dom/Manipulator/Node/rename.php b/src/Xml/Dom/Manipulator/Node/rename.php index 1265175..a5694d8 100644 --- a/src/Xml/Dom/Manipulator/Node/rename.php +++ b/src/Xml/Dom/Manipulator/Node/rename.php @@ -18,13 +18,9 @@ */ function rename(\DOM\Node $target, string $newQName, ?string $newNamespaceURI = null): \DOM\Node { - if (is_attribute($target)) { - return rename_attribute($target, $newQName, $newNamespaceURI); - } - - if (is_element($target)) { - return rename_element($target, $newQName, $newNamespaceURI); - } - - throw RuntimeException::withMessage('Can not rename dom node with type ' . get_class($target)); + return match(true) { + is_attribute($target) => rename_attribute($target, $newQName, $newNamespaceURI), + is_element($target) => rename_element($target, $newQName, $newNamespaceURI), + default => throw RuntimeException::withMessage('Can not rename dom node with type ' . get_class($target)) + }; } diff --git a/src/Xml/Dom/Manipulator/Xmlns/rename.php b/src/Xml/Dom/Manipulator/Xmlns/rename.php index 086a455..1580aeb 100644 --- a/src/Xml/Dom/Manipulator/Xmlns/rename.php +++ b/src/Xml/Dom/Manipulator/Xmlns/rename.php @@ -4,111 +4,16 @@ namespace VeeWee\Xml\Dom\Manipulator\Xmlns; -use \DOM\XMLDocument; -use \DOM\Element; -use \DOM\NameSpaceNode; -use \DOM\Node; -use VeeWee\Xml\Dom\Collection\NodeList; -use VeeWee\Xml\Dom\Xpath; use VeeWee\Xml\Exception\RuntimeException; -use function Psl\invariant; -use function Psl\Type\non_empty_string; -use function sprintf; -use function VeeWee\Xml\Dom\Builder\xmlns_attribute; -use function VeeWee\Xml\Dom\Locator\Attribute\attributes_list; -use function VeeWee\Xml\Dom\Locator\Attribute\xmlns_attributes_list; -use function VeeWee\Xml\Dom\Manipulator\Node\remove_namespace; -use function VeeWee\Xml\Dom\Manipulator\Node\rename as rename_node; -use function VeeWee\Xml\Dom\Predicate\is_attribute; -use function VeeWee\Xml\Dom\Predicate\is_element; +use VeeWee\Xml\Xmlns\Xmlns; +use function VeeWee\Xml\ErrorHandling\disallow_issues; /** * @throws RuntimeException */ -function rename(\DOM\XMLDocument $document, string $namespaceURI, string $newPrefix): void +function rename(\DOM\Attr $target, string $newQName): \DOM\Attr { - // Check for prefix collisions - $existingUri = $document->lookupNamespaceURI($newPrefix); - if ($existingUri !== null && $existingUri !== $namespaceURI) { - throw RuntimeException::withMessage( - sprintf( - 'Cannot rename the namespace uri %s because the prefix %s is already linked to uri %s', - $namespaceURI, - $newPrefix, - $existingUri - ) - ); - } + disallow_issues(static fn () => $target->rename(Xmlns::xmlns()->value(), $newQName)); - $xpath = Xpath::fromUnsafeNode($document); - $predicate = static fn (\DOM\Node $node): bool - => $node->namespaceURI === $namespaceURI && $node->prefix !== $newPrefix; - - // Fetch all nodes (attributes and elements) linked to the given namespace and the nodes that declare namespaces. - // The sort order is important: - // Make sure to set the deepest DOM element first in the list - // The attributes need to be dealt with as last, - // otherwise XMLNS namespace will be removed again after dealing with the elements that declare the xmlns. - $linkedNodes = $xpath->query( - sprintf('//*[namespace-uri()=\'%1$s\' or @*[namespace-uri()=\'%1$s\'] or namespace::*]', $namespaceURI) - )->expectAllOfType(\DOM\Element::class)->reduce( - static fn (NodeList $list, \DOM\Element $element): NodeList - => new NodeList( - ...[$element], - ...$list, - ...attributes_list($element)->filter($predicate), - ), - new NodeList() - ); - - // Add new xmlns to root node - $root = $document->documentElement; - xmlns_attribute($newPrefix, $namespaceURI)($root); - - // Go through the linked nodes and remove all matching xmlns attributes - // Finally rename the node in order to use the new prefix. - $linkedNodes->forEach( - static function (\DOM\Node $node) use ($namespaceURI, $newPrefix, $predicate, $root): void { - // Wrapped in a closure so that psalm knows it all... - $newQname = static fn (\DOM\Node $node): string => $newPrefix.':'.non_empty_string()->assert($node->localName); - - if (is_attribute($node)) { - rename_node($node, $newQname($node), $namespaceURI); - return; - } - - // @codeCoverageIgnoreStart - if (!is_element($node)) { - return; - } - // @codeCoverageIgnoreEnd - - // Remove old xmlns declarations: - $namespaceNodes = xmlns_attributes_list($node) - ->filter( - static fn (\DOM\NameSpaceNode $xmlns): bool - => $xmlns->namespaceURI === $namespaceURI && $xmlns->prefix !== $newPrefix - ); - - foreach ($namespaceNodes as $xmlns) { - // Before removing the default xmlns on the root node - // We need to make sure to rename it to the new namespace - // Otherwise the namespace will be lost! - if ($node === $root && $predicate($node)) { - // The root node renaming can result in a new \DOM\Node. - // Make sure to use this new node to avoid issues with e.g. duplicate namespace declarations. - $node = rename_node($node, $newQname($node), $namespaceURI); - invariant(is_element($node), 'Expected the root node to be a DOM element'); - } - - remove_namespace($xmlns, $node); - } - - // If the DOM element is part of the namespace URI, rename it! - // (Remember: this function also accepts regular DOM elements with xmlns declarations that are not linked to the namespace) - if ($predicate($node)) { - rename_node($node, $newQname($node), $namespaceURI); - } - } - ); + return $target; } diff --git a/src/Xml/Dom/Manipulator/Xmlns/rename_element_namespace.php b/src/Xml/Dom/Manipulator/Xmlns/rename_element_namespace.php new file mode 100644 index 0000000..27444be --- /dev/null +++ b/src/Xml/Dom/Manipulator/Xmlns/rename_element_namespace.php @@ -0,0 +1,33 @@ +childNodes as $child) { + if (is_element($child)) { + rename_element_namespace($child, $namespaceURI, $newPrefix); + } + } + + foreach ($root->attributes as $attr) { + match (true) { + $attr->namespaceURI === $namespaceURI => $attr->rename($namespaceURI, $newPrefix . ':' . $attr->localName), + is_xmlns_attribute($attr) && $attr->value === $namespaceURI => $attr->rename($attr->namespaceURI, 'xmlns:' . $newPrefix), + default => null, + }; + } + + if ($root->namespaceURI === $namespaceURI) { + $root->rename($namespaceURI, $newPrefix . ':' . $root->localName); + } +} diff --git a/src/Xml/Dom/Manipulator/append.php b/src/Xml/Dom/Manipulator/append.php index 922af7c..e25c5f4 100644 --- a/src/Xml/Dom/Manipulator/append.php +++ b/src/Xml/Dom/Manipulator/append.php @@ -7,7 +7,10 @@ use Closure; use \DOM\Node; use VeeWee\Xml\Exception\RuntimeException; +use function VeeWee\Xml\Dom\Predicate\is_attribute; +use function VeeWee\Xml\Dom\Predicate\is_element; use function VeeWee\Xml\ErrorHandling\disallow_issues; +use function VeeWee\Xml\ErrorHandling\disallow_libxml_false_returns; /** * @no-named-arguments @@ -19,6 +22,13 @@ function append(\DOM\Node ... $nodes): Closure return static fn (\DOM\Node $target): \DOM\Node => disallow_issues( static function () use ($target, $nodes) { foreach ($nodes as $node) { + // Attributes cannot be appended with appendChild. + // Setting the attribute node to the element is the correct way to append an attribute. + if (is_attribute($node) && is_element($target)) { + $target->setAttributeNode($node); + continue; + } + $target->appendChild($node); } diff --git a/src/Xml/Dom/Predicate/is_default_xmlns_attribute.php b/src/Xml/Dom/Predicate/is_default_xmlns_attribute.php index 7aac7fe..3deffed 100644 --- a/src/Xml/Dom/Predicate/is_default_xmlns_attribute.php +++ b/src/Xml/Dom/Predicate/is_default_xmlns_attribute.php @@ -4,10 +4,9 @@ namespace VeeWee\Xml\Dom\Predicate; -use \DOM\NameSpaceNode; use \DOM\Node; -function is_default_xmlns_attribute(\DOM\Node|\DOM\NameSpaceNode $node): bool +function is_default_xmlns_attribute(\DOM\Node $node): bool { - return is_xmlns_attribute($node) && $node->prefix === ''; + return is_xmlns_attribute($node) && $node->prefix === null; } diff --git a/src/Xml/Dom/Predicate/is_document_element.php b/src/Xml/Dom/Predicate/is_document_element.php index 1b5b22c..be8694a 100644 --- a/src/Xml/Dom/Predicate/is_document_element.php +++ b/src/Xml/Dom/Predicate/is_document_element.php @@ -4,11 +4,10 @@ namespace VeeWee\Xml\Dom\Predicate; -use \DOM\NameSpaceNode; use \DOM\Node; use function VeeWee\Xml\Dom\Locator\Node\detect_document; -function is_document_element(\DOM\Node|\DOM\NameSpaceNode $node): bool +function is_document_element(\DOM\Node $node): bool { return is_element($node) && detect_document($node)->documentElement === $node; } diff --git a/src/Xml/Dom/Predicate/is_xmlns_attribute.php b/src/Xml/Dom/Predicate/is_xmlns_attribute.php index 422ad07..ac752ca 100644 --- a/src/Xml/Dom/Predicate/is_xmlns_attribute.php +++ b/src/Xml/Dom/Predicate/is_xmlns_attribute.php @@ -4,13 +4,13 @@ namespace VeeWee\Xml\Dom\Predicate; -use \DOM\NameSpaceNode; use \DOM\Node; +use VeeWee\Xml\Xmlns\Xmlns; /** - * @psalm-assert-if-true \DOM\NameSpaceNode $node + * @psalm-assert-if-true \DOM\Attr $node */ -function is_xmlns_attribute(\DOM\Node|\DOM\NameSpaceNode $node): bool +function is_xmlns_attribute(\DOM\Node $node): bool { - return $node instanceof \DOM\NameSpaceNode; + return is_attribute($node) && $node->namespaceURI === Xmlns::xmlns()->value(); } diff --git a/src/Xml/Dom/Traverser/Action/RenameNode.php b/src/Xml/Dom/Traverser/Action/RenameNode.php index 919685d..03bc1e6 100644 --- a/src/Xml/Dom/Traverser/Action/RenameNode.php +++ b/src/Xml/Dom/Traverser/Action/RenameNode.php @@ -12,7 +12,8 @@ final class RenameNode implements Action { public function __construct( - private string $newQName + private string $newQName, + private ?string $newNamespaceURI = null, ) { } @@ -21,6 +22,6 @@ public function __construct( */ public function __invoke(\DOM\Node $currentNode): void { - rename($currentNode, $this->newQName); + rename($currentNode, $this->newQName, $this->newNamespaceURI); } } diff --git a/src/Xml/Dom/Traverser/Visitor/RemoveNamespaces.php b/src/Xml/Dom/Traverser/Visitor/RemoveNamespaces.php index 4551ad0..0da7c9a 100644 --- a/src/Xml/Dom/Traverser/Visitor/RemoveNamespaces.php +++ b/src/Xml/Dom/Traverser/Visitor/RemoveNamespaces.php @@ -3,23 +3,22 @@ namespace VeeWee\Xml\Dom\Traverser\Visitor; -use \DOM\NameSpaceNode; -use \DOM\Node; use VeeWee\Xml\Dom\Traverser\Action; use VeeWee\Xml\Exception\RuntimeException; use function Psl\Iter\contains; -use function VeeWee\Xml\Dom\Locator\Attribute\xmlns_attributes_list; +use function VeeWee\Xml\Dom\Predicate\is_attribute; use function VeeWee\Xml\Dom\Predicate\is_element; +use function VeeWee\Xml\Dom\Predicate\is_xmlns_attribute; final class RemoveNamespaces extends AbstractVisitor { /** - * @var null | callable(\DOM\NameSpaceNode): bool + * @var null | callable(\DOM\Attr | \DOM\Element): bool */ private $filter; /** - * @param null | callable(\DOM\NameSpaceNode): bool $filter + * @param null | callable(\DOM\Attr): bool $filter */ public function __construct( ?callable $filter = null @@ -35,14 +34,14 @@ public static function all(): self public static function prefixed(): self { return new self( - static fn (\DOM\NameSpaceNode $node): bool => $node->prefix !== '' + static fn (\DOM\Attr | \DOM\Element $node): bool => $node->prefix !== null ); } public static function unprefixed(): self { return new self( - static fn (\DOM\NameSpaceNode $node): bool => $node->prefix === '' + static fn (\DOM\Attr | \DOM\Element $node): bool => $node->prefix === null ); } @@ -52,7 +51,10 @@ public static function unprefixed(): self public static function byPrefixNames(array $prefixes): self { return new self( - static fn (\DOM\NameSpaceNode $node): bool => contains($prefixes, $node->prefix) + static fn (\DOM\Attr | \DOM\Element $node): bool => match(true) { + is_xmlns_attribute($node) => contains($prefixes, $node->prefix !== null ? $node->localName : ''), + default => contains($prefixes, $node->prefix ?? '') + } ); } @@ -62,31 +64,56 @@ public static function byPrefixNames(array $prefixes): self public static function byNamespaceURIs(array $URIs): self { return new self( - static fn (\DOM\NameSpaceNode $node): bool => contains($URIs, $node->namespaceURI) + static fn (\DOM\Attr | \DOM\Element $node): bool => match(true) { + is_xmlns_attribute($node) => contains($URIs, $node->value), + default => contains($URIs, $node->namespaceURI), + } ); } + public function onNodeEnter(\DOM\Node $node): Action + { + if (is_xmlns_attribute($node)) { + return new Action\Noop(); + } + + if (!$this->shouldDealWithNode($node)) { + return new Action\Noop(); + } + + return new Action\RenameNode($node->localName, null); + } + /** * @throws RuntimeException */ public function onNodeLeave(\DOM\Node $node): Action { - if (!is_element($node)) { + if (!is_xmlns_attribute($node)) { return new Action\Noop(); } - $namespaces = xmlns_attributes_list($node); - if ($this->filter !== null) { - $namespaces = $namespaces->filter($this->filter); + if (!$this->shouldDealWithNode($node)) { + return new Action\Noop(); + } + + return new Action\RemoveNode(); + } + + private function shouldDealWithNode(\DOM\Node $node): bool + { + if (!is_element($node) && !is_attribute($node)) { + return false; + } + + if ($node->namespaceURI === null) { + return false; } - foreach ($namespaces as $namespace) { - $node->removeAttributeNS( - $namespace->namespaceURI, - $namespace->prefix - ); + if ($this->filter === null) { + return true; } - return new Action\Noop(); + return ($this->filter)($node); } } diff --git a/src/Xml/Dom/Traverser/Visitor/SortAttributes.php b/src/Xml/Dom/Traverser/Visitor/SortAttributes.php index ef166ab..95066f3 100644 --- a/src/Xml/Dom/Traverser/Visitor/SortAttributes.php +++ b/src/Xml/Dom/Traverser/Visitor/SortAttributes.php @@ -6,6 +6,8 @@ use VeeWee\Xml\Dom\Traverser\Action; use function VeeWee\Xml\Dom\Locator\Attribute\attributes_list; +use function VeeWee\Xml\Dom\Manipulator\append; +use function VeeWee\Xml\Dom\Manipulator\Node\remove; use function VeeWee\Xml\Dom\Predicate\is_element; use function VeeWee\Xml\ErrorHandling\disallow_issues; @@ -22,8 +24,8 @@ public function onNodeEnter(\DOM\Node $node): Action ->forEach( static function (\DOM\Attr $attr) use ($node): void { disallow_issues(static function () use ($node, $attr) { - $node->removeAttributeNode($attr); - $node->setAttributeNode($attr); + remove($attr); + append($attr)($node); }); } ); diff --git a/src/Xml/Encoding/Internal/Decoder/Builder/attributes.php b/src/Xml/Encoding/Internal/Decoder/Builder/attributes.php index 0eaa804..af1f859 100644 --- a/src/Xml/Encoding/Internal/Decoder/Builder/attributes.php +++ b/src/Xml/Encoding/Internal/Decoder/Builder/attributes.php @@ -9,6 +9,8 @@ use function Psl\Dict\filter; use function Psl\Dict\merge; use function Psl\Iter\reduce; +use function VeeWee\Xml\Dom\Locator\Attribute\attributes_list; +use function VeeWee\Xml\Dom\Predicate\is_xmlns_attribute; /** * @psalm-internal VeeWee\Xml\Encoding @@ -17,7 +19,7 @@ function attributes(\DOM\Element $element): array { return filter([ '@attributes' => reduce( - $element->attributes, + attributes_list($element)->filter(static fn(\DOM\Attr $attr): bool => !is_xmlns_attribute($attr)), static fn (array $attributes, \DOM\Attr $attr): array => merge($attributes, attribute($attr)), [] diff --git a/src/Xml/Encoding/Internal/Decoder/Builder/namespaces.php b/src/Xml/Encoding/Internal/Decoder/Builder/namespaces.php index 54dd1c8..ddf5a5a 100644 --- a/src/Xml/Encoding/Internal/Decoder/Builder/namespaces.php +++ b/src/Xml/Encoding/Internal/Decoder/Builder/namespaces.php @@ -5,7 +5,6 @@ namespace VeeWee\Xml\Encoding\Internal\Decoder\Builder; use \DOM\Element; -use \DOM\NameSpaceNode; use VeeWee\Xml\Exception\RuntimeException; use function Psl\Dict\filter; use function Psl\Dict\merge; @@ -20,9 +19,11 @@ function namespaces(\DOM\Element $element): array { return filter([ '@namespaces' => xmlns_attributes_list($element)->reduce( - static fn (array $namespaces, \DOM\NameSpaceNode $node) - => $node->namespaceURI - ? merge($namespaces, [(string) $node->prefix => $node->namespaceURI]) + static fn (array $namespaces, \DOM\Attr $node) + => $node->value + ? merge($namespaces, [ + ($node->prefix ? $node->localName : '') => $node->value + ]) : $namespaces, [] ), diff --git a/src/bootstrap.php b/src/bootstrap.php index ce099e0..c11c580 100644 --- a/src/bootstrap.php +++ b/src/bootstrap.php @@ -66,6 +66,7 @@ 'Xml\Dom\Manipulator\Node\replace_by_external_node' => __DIR__.'/Xml/Dom/Manipulator/Node/replace_by_external_node.php', 'Xml\Dom\Manipulator\Node\replace_by_external_nodes' => __DIR__.'/Xml/Dom/Manipulator/Node/replace_by_external_nodes.php', 'Xml\Dom\Manipulator\Xmlns\rename' => __DIR__.'/Xml/Dom/Manipulator/Xmlns/rename.php', + 'Xml\Dom\Manipulator\Xmlns\rename_element_namespace' => __DIR__.'/Xml/Dom/Manipulator/Xmlns/rename_element_namespace.php', 'Xml\Dom\Manipulator\append' => __DIR__.'/Xml/Dom/Manipulator/append.php', 'Xml\Dom\Mapper\xml_string' => __DIR__.'/Xml/Dom/Mapper/xml_string.php', 'Xml\Dom\Mapper\xslt_template' => __DIR__.'/Xml/Dom/Mapper/xslt_template.php', diff --git a/stubs/DOM.phpstub b/stubs/DOM.phpstub index 9fe7fc4..80923b0 100644 --- a/stubs/DOM.phpstub +++ b/stubs/DOM.phpstub @@ -509,7 +509,7 @@ namespace public function replaceChildren(...$nodes): void {} } - class DOMNodeList implements \IteratorAggregate, \Countable + class DOMNodeList implements IteratorAggregate, Countable { /** @readonly */ public int $length; @@ -919,7 +919,7 @@ namespace public function splitText(int $offset) {} } - class DOMNamedNodeMap implements \IteratorAggregate, \Countable + class DOMNamedNodeMap implements IteratorAggregate, Countable { /** @readonly */ public int $length; @@ -1017,6 +1017,8 @@ namespace public function registerPhpFunctions(string|array|null $restrict = null): void {} public function registerPhpFunctionNS(string $namespaceURI, string $name, callable $callable): void {} + + public static function quote(string $str): string {} } #endif @@ -1122,6 +1124,19 @@ namespace DOM public function replaceWith(Node|string ...$nodes): void; } + /** + * @strict-properties + * @not-serializable + */ + class Implementation + { + public function createDocumentType(string $qualifiedName, string $publicId, string $systemId): DocumentType {} + + public function createDocument(?string $namespace, string $qualifiedName, ?DocumentType $doctype = null): XMLDocument {} + + public function createHTMLDocument(?string $title = null): HTMLDocument {} + } + /** @strict-properties */ class Node { @@ -1203,15 +1218,12 @@ namespace DOM public function __wakeup(): void {} } - class NodeList implements \IteratorAggregate, \Countable + class NodeList implements IteratorAggregate, Countable { /** @readonly */ public int $length; - /** - * @tentative-return-type - * @implementation-alias DOMNodeList::count - */ + /** @implementation-alias DOMNodeList::count */ public function count(): int {} /** @implementation-alias DOMNodeList::getIterator */ @@ -1221,7 +1233,7 @@ namespace DOM public function item(int $index): ?Node {} } - class NamedNodeMap implements \IteratorAggregate, \Countable + class NamedNodeMap implements IteratorAggregate, Countable { /** @readonly */ public int $length; @@ -1233,17 +1245,14 @@ namespace DOM /** @implementation-alias DOMNamedNodeMap::getNamedItemNS */ public function getNamedItemNS(?string $namespace, string $localName): ?Attr {} - /** - * @tentative-return-type - * @implementation-alias DOMNamedNodeMap::count - */ + /** @implementation-alias DOMNamedNodeMap::count */ public function count(): int {} /** @implementation-alias DOMNamedNodeMap::getIterator */ public function getIterator(): \Iterator {} } - class DTDNamedNodeMap implements \IteratorAggregate, \Countable + class DTDNamedNodeMap implements IteratorAggregate, Countable { /** @readonly */ public int $length; @@ -1255,17 +1264,14 @@ namespace DOM /** @implementation-alias DOMNamedNodeMap::getNamedItemNS */ public function getNamedItemNS(?string $namespace, string $localName): Entity|Notation|null {} - /** - * @tentative-return-type - * @implementation-alias DOMNamedNodeMap::count - */ + /** @implementation-alias DOMNamedNodeMap::count */ public function count(): int {} /** @implementation-alias DOMNamedNodeMap::getIterator */ public function getIterator(): \Iterator {} } - class HTMLCollection implements \IteratorAggregate, \Countable + class HTMLCollection implements IteratorAggregate, Countable { /** @readonly */ public int $length; @@ -1275,10 +1281,7 @@ namespace DOM /* TODO: implement namedItem */ - /** - * @tentative-return-type - * @implementation-alias DOMNodeList::count - */ + /** @implementation-alias DOMNodeList::count */ public function count(): int {} /** @implementation-alias DOMNodeList::getIterator */ @@ -1370,6 +1373,16 @@ namespace DOM public function prepend(Node|string ...$nodes): void {} /** @implementation-alias DOMElement::replaceChildren */ public function replaceChildren(Node|string ...$nodes): void {} + + public string $substitutedNodeValue; + + /** @return list */ + public function getInScopeNamespaces(): array {} + + /** @return list */ + public function getDescendantNamespaces(): array {} + + public function rename(?string $namespaceURI, string $qualifiedName): void {} } class Attr extends Node @@ -1392,6 +1405,9 @@ namespace DOM /** @implementation-alias DOMAttr::isId */ public function isId(): bool {} + + /** @implementation-alias DOM\Element::rename */ + public function rename(?string $namespaceURI, string $qualifiedName): void {} } class CharacterData extends Node implements ChildNode @@ -1510,6 +1526,8 @@ namespace DOM abstract class Document extends Node implements ParentNode { + /** @readonly */ + public Implementation $implementation; /** @readonly */ public string $URL; /** @readonly */ @@ -1594,7 +1612,7 @@ namespace DOM /** @implementation-alias DOMDocument::save */ public function saveXMLFile(string $filename, int $options = 0): int|false {} - public function saveHTML(?Node $node = null): string|false {} + public function saveHTML(?Node $node = null): string {} public function saveHTMLFile(string $filename): int|false {} } @@ -1631,6 +1649,20 @@ namespace DOM public function saveXMLFile(string $filename, int $options = 0): int|false {} } + /** + * @not-serializable + * @strict-properties + */ + final class NamespaceInfo + { + public readonly ?string $prefix; + public readonly ?string $namespaceURI; + public readonly Element $element; + + /** @implementation-alias DOM\Node::__construct */ + private function __construct() {} + } + #ifdef LIBXML_XPATH_ENABLED /** @not-serializable */ final class XPath @@ -1654,6 +1686,9 @@ namespace DOM /** @implementation-alias DOMXPath::registerPhpFunctionNS */ public function registerPhpFunctionNS(string $namespaceURI, string $name, callable $callable): void {} + + /** @implementation-alias DOMXPath::quote */ + public static function quote(string $str): string {} } #endif diff --git a/tests/Xml/Dom/Configurator/PrettyPrintTest.php b/tests/Xml/Dom/Configurator/PrettyPrintTest.php index 2933760..05a87d6 100644 --- a/tests/Xml/Dom/Configurator/PrettyPrintTest.php +++ b/tests/Xml/Dom/Configurator/PrettyPrintTest.php @@ -25,9 +25,8 @@ public function test_it_can_trim_contents(): void EOXML; - static::assertSame($doc, $result); - // TODO : static::assertFalse($doc->preserveWhiteSpace); - static::assertTrue($doc->formatOutput); - static::assertSame($expected, xml_string()($doc->documentElement)); + static::assertNotSame($doc, $result); + static::assertTrue($result->formatOutput); + static::assertSame($expected, xml_string()($result->documentElement)); } } diff --git a/tests/Xml/Dom/Configurator/TrimSpacesTest.php b/tests/Xml/Dom/Configurator/TrimSpacesTest.php index 9fd1e13..960e437 100644 --- a/tests/Xml/Dom/Configurator/TrimSpacesTest.php +++ b/tests/Xml/Dom/Configurator/TrimSpacesTest.php @@ -18,9 +18,8 @@ public function test_it_can_trim_contents(): void $configurator = trim_spaces(); $result = $configurator($doc); - static::assertSame($doc, $result); - // TODO : static::assertFalse($doc->preserveWhiteSpace); - static::assertFalse($doc->formatOutput); - static::assertSame('', xml_string()($doc->documentElement)); + static::assertNotSame($doc, $result); + static::assertFalse($result->formatOutput); + static::assertSame('', xml_string()($result->documentElement)); } } diff --git a/tests/Xml/Dom/Locator/Attribute/AttributesListTest.php b/tests/Xml/Dom/Locator/Attribute/AttributesListTest.php index 87c2503..7e6d3b4 100644 --- a/tests/Xml/Dom/Locator/Attribute/AttributesListTest.php +++ b/tests/Xml/Dom/Locator/Attribute/AttributesListTest.php @@ -35,6 +35,8 @@ public function provideTestCases() yield [$doc->documentElement, [ $doc->documentElement->getAttributeNode('attr1'), $doc->documentElement->getAttributeNode('attr2'), + $doc->documentElement->getAttributeNode('xmlns'), + $doc->documentElement->getAttributeNode('xmlns:yo'), ]]; yield [$doc->documentElement->firstElementChild, [ $doc->documentElement->firstElementChild->getAttributeNode('attr'), diff --git a/tests/Xml/Dom/Locator/Element/ParentElementTest.php b/tests/Xml/Dom/Locator/Element/ParentElementTest.php index 664e741..4536dac 100644 --- a/tests/Xml/Dom/Locator/Element/ParentElementTest.php +++ b/tests/Xml/Dom/Locator/Element/ParentElementTest.php @@ -34,7 +34,7 @@ public function test_it_can_detect_parents(): void static::assertSame($hello, $domdoc->documentElement); $this->expectException(RuntimeException::class); - $this->expectExceptionMessage('Can not find parent element for \DOM\Element hello'); + $this->expectExceptionMessage('Can not find parent element for DOM\Element hello'); parent_element($hello); } } diff --git a/tests/Xml/Dom/Locator/Xmlns/LinkedNamespacesTest.php b/tests/Xml/Dom/Locator/Xmlns/LinkedNamespacesTest.php index 60fa809..5523e00 100644 --- a/tests/Xml/Dom/Locator/Xmlns/LinkedNamespacesTest.php +++ b/tests/Xml/Dom/Locator/Xmlns/LinkedNamespacesTest.php @@ -6,11 +6,9 @@ use \DOM\NameSpaceNode; use PHPUnit\Framework\TestCase; -use VeeWee\Xml\Dom\Collection\NodeList; use VeeWee\Xml\Dom\Document; use function Psl\Dict\merge; use function Psl\Iter\reduce; -use function VeeWee\Xml\Dom\Locator\document_element; use function VeeWee\Xml\Dom\Locator\Xmlns\linked_namespaces; final class LinkedNamespacesTest extends TestCase @@ -22,22 +20,36 @@ public function test_it_can_detect_linked_namespaces(): void 1 XML; - $element = Document::fromXmlString($xml)->locate(document_element()); + $element = Document::fromXmlString($xml)->locateDocumentElement(); - $parse = static fn (NodeList $list): array => reduce( - [...$list], - static fn (array $map, \DOM\NameSpaceNode $node) => merge($map, [$node->localName => $node->namespaceURI]), - [] + static::assertSame( + [ + '' => 'http://hello.com', + 'world' => 'http://world.com', + ], + $this->parseLinkedNamespaces($element) ); - static::assertSame( [ - 'xml' => 'http://www.w3.org/XML/1998/namespace', + '' => 'http://hello.com', 'world' => 'http://world.com', - 'xmlns' => 'http://hello.com', ], - $parse(linked_namespaces($element)) + $this->parseLinkedNamespaces($element->firstElementChild) + ); + } + + /** + * @return array - Key : prefix, Value : namespace + */ + private function parseLinkedNamespaces(\DOM\Element $element): array + { + return reduce( + linked_namespaces($element), + static fn (array $result, \DOM\NamespaceInfo $info) => merge( + $result, + [(string) $info->prefix => $info->namespaceURI] + ), + [] ); - static::assertSame([], $parse(linked_namespaces($element->childNodes->item(0)))); } } diff --git a/tests/Xml/Dom/Locator/Xmlns/RecursiveLinkedNamespacesTest.php b/tests/Xml/Dom/Locator/Xmlns/RecursiveLinkedNamespacesTest.php index 35fab90..536e56a 100644 --- a/tests/Xml/Dom/Locator/Xmlns/RecursiveLinkedNamespacesTest.php +++ b/tests/Xml/Dom/Locator/Xmlns/RecursiveLinkedNamespacesTest.php @@ -11,6 +11,7 @@ use function Psl\Dict\merge; use function Psl\Iter\reduce; use function VeeWee\Xml\Dom\Locator\document_element; +use function VeeWee\Xml\Dom\Locator\Xmlns\linked_namespaces; use function VeeWee\Xml\Dom\Locator\Xmlns\recursive_linked_namespaces; final class RecursiveLinkedNamespacesTest extends TestCase @@ -22,29 +23,36 @@ public function test_it_can_detect_recursively_linked_namespaces(): void 1 XML; - $element = Document::fromXmlString($xml)->locate(document_element()); - - $parse = static fn (NodeList $list): array => reduce( - [...$list], - static fn (array $map, \DOM\NameSpaceNode $node) => merge($map, [$node->localName => $node->namespaceURI]), - [] - ); + $element = Document::fromXmlString($xml)->locateDocumentElement(); static::assertSame( [ - 'xml' => 'http://www.w3.org/XML/1998/namespace', - 'xmlns' => 'http://hello.com', + '' => 'http://hello.com', 'world' => 'http://world.com', ], - $parse(recursive_linked_namespaces($element)) + $this->parseRecursiveLinkedNamespaces($element) ); static::assertSame( [ - 'xml' => 'http://www.w3.org/XML/1998/namespace', - 'xmlns' => 'http://hello.com', + '' => 'http://hello.com', 'world' => 'http://world.com', ], - $parse(recursive_linked_namespaces($element->firstElementChild)) + $this->parseRecursiveLinkedNamespaces($element->firstElementChild) + ); + } + + /** + * @return array - Key : prefix, Value : namespace + */ + private function parseRecursiveLinkedNamespaces(\DOM\Element $element): array + { + return reduce( + recursive_linked_namespaces($element), + static fn (array $result, \DOM\NamespaceInfo $info) => merge( + $result, + [(string) $info->prefix => $info->namespaceURI] + ), + [] ); } } diff --git a/tests/Xml/Dom/Manipulator/Node/AppendExternalNodeTest.php b/tests/Xml/Dom/Manipulator/Node/AppendExternalNodeTest.php index 2de46ba..b3f6bf2 100644 --- a/tests/Xml/Dom/Manipulator/Node/AppendExternalNodeTest.php +++ b/tests/Xml/Dom/Manipulator/Node/AppendExternalNodeTest.php @@ -33,7 +33,7 @@ public function test_it_can_not_import_a_document_into_a_document(): void $target = Document::empty()->toUnsafeDocument(); $this->expectException(RuntimeException::class); - $this->expectExceptionMessage('Cannot import node: Node Type Not Supported'); + $this->expectExceptionMessage('Not Supported Error'); append_external_node($target, $source); } diff --git a/tests/Xml/Dom/Manipulator/Node/RenameTest.php b/tests/Xml/Dom/Manipulator/Node/RenameTest.php index 310e98c..7c912f7 100644 --- a/tests/Xml/Dom/Manipulator/Node/RenameTest.php +++ b/tests/Xml/Dom/Manipulator/Node/RenameTest.php @@ -64,11 +64,14 @@ public function test_it_can_rename_an_element_with_attributes(): void public function test_it_can_rename_an_element_with_namespace(): void { $doc = Document::fromXmlString(''); - $node = $doc->xpath(namespaces(['ok' => 'http://ok']))->querySingle('//ok:item'); + $node = $doc->locateDocumentElement()->firstElementChild; - $result = rename($node, 'thing'); + $result = rename($node, 'thing', $node->namespaceURI); - static::assertXmlStringEqualsXmlString($doc->toXmlString(), ''); + static::assertXmlStringEqualsXmlString( + '', + $doc->toXmlString() + ); static::assertSame($doc->xpath(namespaces(['ok' => 'http://ok']))->querySingle('//ok:thing'), $result); } @@ -88,7 +91,7 @@ public function test_it_can_rename_an_element_with_prefixed_namespace(): void $doc = Document::fromXmlString(''); $node = $doc->xpath(namespaces(['a' => 'http://ok']))->querySingle('//a:item'); - $result = rename($node, 'a:thing'); + $result = rename($node, 'a:thing', $node->namespaceURI); static::assertXmlStringEqualsXmlString($doc->toXmlString(), ''); static::assertSame($doc->xpath(namespaces(['a' => 'http://ok']))->querySingle('//a:thing'), $result); @@ -108,28 +111,28 @@ public function test_it_can_rename_an_element_with_prefixed_namespace_and_new_ur public function test_it_can_rename_an_element_and_drop_prefix(): void { $doc = Document::fromXmlString(''); - $node = $doc->xpath(namespaces(['a' => 'http://ok']))->querySingle('//a:item'); + $node = $doc->locateDocumentElement()->firstElementChild; $result = rename($node, 'thing'); static::assertSame( - $doc->reconfigure(comparable())->toXmlString(), - Document::fromXmlString( - '', - comparable() - )->toXmlString(), + '', + $doc->stringifyDocumentElement(), ); - static::assertSame($doc->xpath(namespaces(['a' => 'http://ok']))->querySingle('//a:thing'), $result); + static::assertSame($doc->xpath()->querySingle('thing'), $result); } public function test_it_can_rename_an_element_prefix(): void { $doc = Document::fromXmlString(''); - $node = $doc->xpath(namespaces(['a' => 'http://ok']))->querySingle('//a:item'); + $node = $doc->locateDocumentElement()->firstElementChild; - $result = rename($node, 'b:thing'); + $result = rename($node, 'b:thing', $node->namespaceURI); - static::assertXmlStringEqualsXmlString($doc->toXmlString(), ''); + static::assertSame( + '', + $doc->stringifyDocumentElement(), + ); static::assertSame($doc->xpath(namespaces(['b' => 'http://ok']))->querySingle('//b:thing'), $result); } @@ -151,7 +154,7 @@ public function test_it_can_rename_namespaced_attributes(): void $root = $doc->map(document_element()); $node = $root->getAttributeNode('a:who'); - $result = rename($node, 'a:you'); + $result = rename($node, 'a:you', $node->namespaceURI); static::assertXmlStringEqualsXmlString($doc->toXmlString(), ''); static::assertSame($root->getAttributeNode('a:you'), $result); @@ -168,7 +171,7 @@ public function test_it_can_not_rename_namespaced_attribute_prefix_when_the_xmln $node = $root->getAttributeNode('a:who'); $this->expectException(RuntimeException::class); - $this->expectExceptionMessage('Unable to rename attribute a:who into b:you'); + $this->expectExceptionMessage('Namespace Error'); rename($node, 'b:you'); } @@ -178,13 +181,10 @@ public function test_it_can_rename_namespaced_attribute_prefix(): void $doc = Document::fromXmlString(''); $root = $doc->map(document_element()); $node = $root->getAttributeNode('a:who'); - - // You'll need to manually rename the namespace $ns = $root->getAttributeNode('xmlns:a'); - remove_namespace($ns, $root); - xmlns_attribute('b', $ns->namespaceURI)($root); - $result = rename($node, 'b:you'); + rename($ns, 'xmlns:b', $ns->value); + $result = rename($node, 'b:you', $node->namespaceURI); static::assertXmlStringEqualsXmlString($doc->toXmlString(), ''); static::assertSame($root->getAttributeNode('b:you'), $result); @@ -223,7 +223,7 @@ public function test_it_can_rename_root_element(): void static::assertXmlStringEqualsXmlString($doc->toXmlString(), ''); static::assertSame($doc->map(document_element()), $result); - static::assertNotSame($root, $result); + static::assertSame($root, $result); } public function test_it_can_rename_root_element_with_namespace(): void @@ -235,7 +235,7 @@ public function test_it_can_rename_root_element_with_namespace(): void static::assertXmlStringEqualsXmlString($doc->toXmlString(), ''); static::assertSame($doc->map(document_element()), $result); - static::assertNotSame($root, $result); + static::assertSame($root, $result); } public function test_it_can_rename_root_element_with_default_namespace(): void @@ -245,8 +245,8 @@ public function test_it_can_rename_root_element_with_default_namespace(): void $result = rename($root, 'goodbye', 'https://foo'); - static::assertXmlStringEqualsXmlString($doc->toXmlString(), ''); + static::assertXmlStringEqualsXmlString($doc->toXmlString(), ''); static::assertSame($doc->map(document_element()), $result); - static::assertNotSame($root, $result); + static::assertSame($root, $result); } } diff --git a/tests/Xml/Dom/Manipulator/Xmlns/RenameTest.php b/tests/Xml/Dom/Manipulator/Xmlns/RenameTest.php index 09b161a..c94c0f0 100644 --- a/tests/Xml/Dom/Manipulator/Xmlns/RenameTest.php +++ b/tests/Xml/Dom/Manipulator/Xmlns/RenameTest.php @@ -6,41 +6,30 @@ use PHPUnit\Framework\TestCase; use VeeWee\Xml\Dom\Document; -use VeeWee\Xml\Exception\RuntimeException; -use function VeeWee\Xml\Dom\Manipulator\Xmlns\rename; -use function VeeWee\Xml\Dom\Mapper\xml_string; +use function VeeWee\Xml\Dom\Manipulator\Xmlns\rename_element_namespace; final class RenameTest extends TestCase { /** * @dataProvider provideXmls + * @param callable(): \Dom\Attr $targetLocator */ public function test_it_can_rename_namespaces(string $input, string $expected): void { - $document = Document::fromXmlString($input)->toUnsafeDocument(); - rename($document, 'http://replace', 'foo'); + $document = Document::fromXmlString($input); + $target = $document->locateDocumentElement(); + rename_element_namespace($target, 'http://replace', 'foo'); - $actual = xml_string()($document->documentElement); + $actual = $document->stringifyDocumentElement(); static::assertSame($expected, $actual); } - - public function test_it_can_not_rename_existing_prefix_to_other_uri(): void - { - $document = Document::fromXmlString('')->toUnsafeDocument(); - - $this->expectException(RuntimeException::class); - $this->expectExceptionMessage('Cannot rename the namespace uri http://replace because the prefix ns1 is already linked to uri http://ok'); - rename($document, 'http://replace', 'ns1'); - } - public function provideXmls() { - yield 'no-action' => [ - '', + yield 'simple' => [ + '', '', ]; - yield 'namespaced-root-and-child' => [ << @@ -52,7 +41,7 @@ public function provideXmls() << - + EOXML, @@ -69,7 +58,7 @@ public function provideXmls() << - + EOXML, @@ -84,8 +73,8 @@ public function provideXmls() EOXML, << - + + @@ -99,8 +88,8 @@ public function provideXmls() EOXML, << - + + EOXML, ]; @@ -128,7 +117,7 @@ public function provideXmls() EOXML, << - + diff --git a/tests/Xml/Encoding/EncodingTest.php b/tests/Xml/Encoding/EncodingTest.php index 3b923c0..ff373ad 100644 --- a/tests/Xml/Encoding/EncodingTest.php +++ b/tests/Xml/Encoding/EncodingTest.php @@ -19,7 +19,7 @@ final class EncodingTest extends TestCase { - private const XML_HEADER = ''; + private const XML_HEADER = ''; /** * @dataProvider provideBidirectionalCases @@ -256,15 +256,15 @@ public function provideRiskyBidirectionalCases() yield 'namespaced' => [ 'xml' => << - + 1 EOXML, 'data' => ['root' => [ '@namespaces' => [ - 'test' => 'http://testy.test', '' => 'http://rooty.root', + 'test' => 'http://testy.test', ], 'test:item' => [ 'id:int' => [ From 6d4a1afd4324a01dbfefa771f88770d683ec2f49 Mon Sep 17 00:00:00 2001 From: Toon Verwerft Date: Thu, 28 Mar 2024 07:19:56 +0100 Subject: [PATCH 04/10] PHP84 explicit nullable types --- .php-cs-fixer.dist.php | 1 + src/Xml/Dom/Xpath.php | 6 +++--- src/Xml/Dom/Xpath/Locator/evaluate.php | 2 +- src/Xml/Dom/Xpath/Locator/query.php | 2 +- src/Xml/Dom/Xpath/Locator/query_single.php | 2 +- src/Xml/Encoding/Exception/EncodingException.php | 2 +- src/Xml/Exception/RuntimeException.php | 2 +- 7 files changed, 9 insertions(+), 8 deletions(-) diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php index f538f79..d9fbaa5 100644 --- a/.php-cs-fixer.dist.php +++ b/.php-cs-fixer.dist.php @@ -55,5 +55,6 @@ 'static_lambda' => true, 'strict_comparison' => true, 'strict_param' => true, + 'nullable_type_declaration_for_default_null_value' => true, ]) ; diff --git a/src/Xml/Dom/Xpath.php b/src/Xml/Dom/Xpath.php index ab1baee..f75467b 100644 --- a/src/Xml/Dom/Xpath.php +++ b/src/Xml/Dom/Xpath.php @@ -64,7 +64,7 @@ public function locate(callable $locator) * @throws RuntimeException * @return NodeList<\DOM\Node> */ - public function query(string $expression, \DOM\Node $contextNode = null): NodeList + public function query(string $expression, ?\DOM\Node $contextNode = null): NodeList { return $this->locate(query($expression, $contextNode)); } @@ -73,7 +73,7 @@ public function query(string $expression, \DOM\Node $contextNode = null): NodeLi * @throws RuntimeException * @throws InvalidArgumentException */ - public function querySingle(string $expression, \DOM\Node $contextNode = null): \DOM\Node + public function querySingle(string $expression, ?\DOM\Node $contextNode = null): \DOM\Node { return $this->locate(query_single($expression, $contextNode)); } @@ -86,7 +86,7 @@ public function querySingle(string $expression, \DOM\Node $contextNode = null): * @return T * @throws RuntimeException */ - public function evaluate(string $expression, TypeInterface $type, \DOM\Node $contextNode = null) + public function evaluate(string $expression, TypeInterface $type, ?\DOM\Node $contextNode = null) { return $this->locate(evaluate($expression, $type, $contextNode)); } diff --git a/src/Xml/Dom/Xpath/Locator/evaluate.php b/src/Xml/Dom/Xpath/Locator/evaluate.php index 9f9e6a3..bc6b194 100644 --- a/src/Xml/Dom/Xpath/Locator/evaluate.php +++ b/src/Xml/Dom/Xpath/Locator/evaluate.php @@ -17,7 +17,7 @@ * * @return Closure(\DOM\XPath): T */ -function evaluate(string $query, TypeInterface $type, \DOM\Node $node = null): Closure +function evaluate(string $query, TypeInterface $type, ?\DOM\Node $node = null): Closure { return /** diff --git a/src/Xml/Dom/Xpath/Locator/query.php b/src/Xml/Dom/Xpath/Locator/query.php index a60562e..f7450a2 100644 --- a/src/Xml/Dom/Xpath/Locator/query.php +++ b/src/Xml/Dom/Xpath/Locator/query.php @@ -15,7 +15,7 @@ /** * @return Closure(\DOM\XPath): NodeList<\DOM\Node> */ -function query(string $query, \DOM\Node $node = null): Closure +function query(string $query, ?\DOM\Node $node = null): Closure { return static function (\DOM\XPath $xpath) use ($query, $node): NodeList { $node = $node ?? $xpath->document->documentElement; diff --git a/src/Xml/Dom/Xpath/Locator/query_single.php b/src/Xml/Dom/Xpath/Locator/query_single.php index 649c86a..7899f72 100644 --- a/src/Xml/Dom/Xpath/Locator/query_single.php +++ b/src/Xml/Dom/Xpath/Locator/query_single.php @@ -18,7 +18,7 @@ /** * @return Closure(\DOM\XPath): \DOM\Node */ -function query_single(string $query, \DOM\Node $node = null): Closure +function query_single(string $query, ?\DOM\Node $node = null): Closure { return /** diff --git a/src/Xml/Encoding/Exception/EncodingException.php b/src/Xml/Encoding/Exception/EncodingException.php index 3ffbda8..a9494c5 100644 --- a/src/Xml/Encoding/Exception/EncodingException.php +++ b/src/Xml/Encoding/Exception/EncodingException.php @@ -9,7 +9,7 @@ final class EncodingException extends Exception implements ExceptionInterface { - private function __construct(string $message, Exception $previous = null) + private function __construct(string $message, ?Exception $previous = null) { parent::__construct( $message, diff --git a/src/Xml/Exception/RuntimeException.php b/src/Xml/Exception/RuntimeException.php index 61b54ae..689ed12 100644 --- a/src/Xml/Exception/RuntimeException.php +++ b/src/Xml/Exception/RuntimeException.php @@ -9,7 +9,7 @@ final class RuntimeException extends \RuntimeException implements ExceptionInterface { - private function __construct(string $message, Throwable $previous = null) + private function __construct(string $message, ?Throwable $previous = null) { parent::__construct( $message, From e066614ccdf04c1d28c5b3bbce17cced97a81c0c Mon Sep 17 00:00:00 2001 From: Toon Verwerft Date: Thu, 28 Mar 2024 07:21:00 +0100 Subject: [PATCH 05/10] Change CI settings --- .github/workflows/analyzers.yaml | 2 +- .github/workflows/autoloader.yaml | 2 +- .github/workflows/code-style.yaml | 2 +- .github/workflows/stress.yaml | 2 +- .github/workflows/tests.yaml | 2 +- composer.json | 2 +- .../Xmlns/rename_element_namespace.php | 45 ++++++++++++------- tests/Xml/Dom/Configurator/ComparableTest.php | 2 +- .../Configurator/OptimizeNamespacesTest.php | 2 +- .../Document/OptimizeNamespacesTest.php | 2 +- 10 files changed, 38 insertions(+), 25 deletions(-) diff --git a/.github/workflows/analyzers.yaml b/.github/workflows/analyzers.yaml index 85890d1..7ade8b2 100644 --- a/.github/workflows/analyzers.yaml +++ b/.github/workflows/analyzers.yaml @@ -12,7 +12,7 @@ jobs: strategy: matrix: operating-system: [ubuntu-latest] - php-versions: ['8.1', '8.2', '8.3'] + php-versions: ['8.4'] fail-fast: false name: PHP ${{ matrix.php-versions }} @ ${{ matrix.operating-system }} steps: diff --git a/.github/workflows/autoloader.yaml b/.github/workflows/autoloader.yaml index d24c9ef..39e7ec9 100644 --- a/.github/workflows/autoloader.yaml +++ b/.github/workflows/autoloader.yaml @@ -7,7 +7,7 @@ jobs: strategy: matrix: operating-system: [ubuntu-latest] - php-versions: ['8.1', '8.2', '8.3'] + php-versions: ['8.4'] fail-fast: false name: PHP ${{ matrix.php-versions }} @ ${{ matrix.operating-system }} steps: diff --git a/.github/workflows/code-style.yaml b/.github/workflows/code-style.yaml index 619334f..dadf1e6 100644 --- a/.github/workflows/code-style.yaml +++ b/.github/workflows/code-style.yaml @@ -11,7 +11,7 @@ jobs: strategy: matrix: operating-system: [ubuntu-latest] - php-versions: ['8.1', '8.2', '8.3'] + php-versions: ['8.4'] fail-fast: false name: PHP ${{ matrix.php-versions }} @ ${{ matrix.operating-system }} steps: diff --git a/.github/workflows/stress.yaml b/.github/workflows/stress.yaml index 225e921..4141f4f 100644 --- a/.github/workflows/stress.yaml +++ b/.github/workflows/stress.yaml @@ -7,7 +7,7 @@ jobs: strategy: matrix: operating-system: [ubuntu-latest] - php-versions: ['8.1', '8.2', '8.3'] + php-versions: ['8.4'] fail-fast: false name: PHP ${{ matrix.php-versions }} @ ${{ matrix.operating-system }} steps: diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index c0502fb..013c038 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -12,7 +12,7 @@ jobs: strategy: matrix: operating-system: [ubuntu-latest] - php-versions: ['8.1', '8.2', '8.3'] + php-versions: ['8.4'] experimental: [false] fail-fast: false name: PHP ${{ matrix.php-versions }} @ ${{ matrix.operating-system }} diff --git a/composer.json b/composer.json index 855257f..2b0d207 100644 --- a/composer.json +++ b/composer.json @@ -7,7 +7,7 @@ ], "type": "library", "require": { - "php": "~8.1.0 || ~8.2.0 || ~8.3.0", + "php": "~8.4.0", "ext-dom": "*", "ext-libxml": "*", "ext-xml": "*", diff --git a/src/Xml/Dom/Manipulator/Xmlns/rename_element_namespace.php b/src/Xml/Dom/Manipulator/Xmlns/rename_element_namespace.php index 27444be..ab21156 100644 --- a/src/Xml/Dom/Manipulator/Xmlns/rename_element_namespace.php +++ b/src/Xml/Dom/Manipulator/Xmlns/rename_element_namespace.php @@ -5,29 +5,42 @@ namespace VeeWee\Xml\Dom\Manipulator\Xmlns; use VeeWee\Xml\Exception\RuntimeException; +use function VeeWee\Xml\Dom\Builder\xmlns_attribute; use function VeeWee\Xml\Dom\Predicate\is_element; use function VeeWee\Xml\Dom\Predicate\is_xmlns_attribute; /** * @throws RuntimeException + * @param non-empty-string $newPrefix */ function rename_element_namespace(\DOM\Element $root, string $namespaceURI, string $newPrefix): void { - foreach ($root->childNodes as $child) { - if (is_element($child)) { - rename_element_namespace($child, $namespaceURI, $newPrefix); + + $recurse = function (\DOM\Element $element) use (&$recurse, $namespaceURI, $newPrefix): void { + foreach ($element->childNodes as $child) { + if (is_element($child)) { + $recurse($child); + } + } + + $hasXmlnsAttribute = false; + foreach ($element->attributes as $attr) { + if ($attr->namespaceURI === $namespaceURI) { + $attr->rename($namespaceURI, $newPrefix . ':' . $attr->localName); + } + + if (is_xmlns_attribute($attr) && $attr->value === $namespaceURI) { + $attr->rename($attr->namespaceURI, 'xmlns:' . $newPrefix); + } } - } - - foreach ($root->attributes as $attr) { - match (true) { - $attr->namespaceURI === $namespaceURI => $attr->rename($namespaceURI, $newPrefix . ':' . $attr->localName), - is_xmlns_attribute($attr) && $attr->value === $namespaceURI => $attr->rename($attr->namespaceURI, 'xmlns:' . $newPrefix), - default => null, - }; - } - - if ($root->namespaceURI === $namespaceURI) { - $root->rename($namespaceURI, $newPrefix . ':' . $root->localName); - } + + if ($element->namespaceURI === $namespaceURI) { + $element->rename($namespaceURI, $newPrefix . ':' . $element->localName); + } + }; + + $recurse( + // TODO - should become : xmlns_attribute($newPrefix, $namespaceURI)($root), + $root + ); } diff --git a/tests/Xml/Dom/Configurator/ComparableTest.php b/tests/Xml/Dom/Configurator/ComparableTest.php index cc77704..0765626 100644 --- a/tests/Xml/Dom/Configurator/ComparableTest.php +++ b/tests/Xml/Dom/Configurator/ComparableTest.php @@ -96,7 +96,7 @@ public function provideXmls() EOXML, << + Jos Jaak Jul diff --git a/tests/Xml/Dom/Configurator/OptimizeNamespacesTest.php b/tests/Xml/Dom/Configurator/OptimizeNamespacesTest.php index 25e8ca6..f38d409 100644 --- a/tests/Xml/Dom/Configurator/OptimizeNamespacesTest.php +++ b/tests/Xml/Dom/Configurator/OptimizeNamespacesTest.php @@ -40,7 +40,7 @@ public function provideXmls() << - + EOXML, diff --git a/tests/Xml/Dom/Manipulator/Document/OptimizeNamespacesTest.php b/tests/Xml/Dom/Manipulator/Document/OptimizeNamespacesTest.php index 8244cab..01ffb87 100644 --- a/tests/Xml/Dom/Manipulator/Document/OptimizeNamespacesTest.php +++ b/tests/Xml/Dom/Manipulator/Document/OptimizeNamespacesTest.php @@ -43,7 +43,7 @@ public function provideXmls() << - + EOXML, From e76451667a756edea9d4eb5224d7eda44a6d1a3d Mon Sep 17 00:00:00 2001 From: Toon Verwerft Date: Mon, 1 Apr 2024 20:09:29 +0200 Subject: [PATCH 06/10] Optimize namespaces --- .../Document/optimize_namespaces.php | 3 ++ .../Xmlns/rename_element_namespace.php | 48 +++++++++---------- .../Configurator/OptimizeNamespacesTest.php | 4 +- .../Document/OptimizeNamespacesTest.php | 2 +- 4 files changed, 30 insertions(+), 27 deletions(-) diff --git a/src/Xml/Dom/Manipulator/Document/optimize_namespaces.php b/src/Xml/Dom/Manipulator/Document/optimize_namespaces.php index a1c1df7..1f351f2 100644 --- a/src/Xml/Dom/Manipulator/Document/optimize_namespaces.php +++ b/src/Xml/Dom/Manipulator/Document/optimize_namespaces.php @@ -9,6 +9,7 @@ use function Psl\Vec\map; use function Psl\Vec\sort; use function Psl\Vec\values; +use function VeeWee\Xml\Dom\Builder\xmlns_attribute; use function VeeWee\Xml\Dom\Locator\Xmlns\recursive_linked_namespaces; use function VeeWee\Xml\Dom\Manipulator\Xmlns\rename_element_namespace; @@ -24,6 +25,8 @@ function optimize_namespaces(\DOM\XMLDocument $document, string $prefix = 'ns'): ))); foreach (sort($namespaceURIs) as $index => $namespaceURI) { + $currentPrefix = $prefix . ((string) ($index+1)); + xmlns_attribute($currentPrefix, $namespaceURI)($documentElement); rename_element_namespace($documentElement, $namespaceURI, $prefix . ((string) ($index+1))); } } diff --git a/src/Xml/Dom/Manipulator/Xmlns/rename_element_namespace.php b/src/Xml/Dom/Manipulator/Xmlns/rename_element_namespace.php index ab21156..0229941 100644 --- a/src/Xml/Dom/Manipulator/Xmlns/rename_element_namespace.php +++ b/src/Xml/Dom/Manipulator/Xmlns/rename_element_namespace.php @@ -5,42 +5,42 @@ namespace VeeWee\Xml\Dom\Manipulator\Xmlns; use VeeWee\Xml\Exception\RuntimeException; -use function VeeWee\Xml\Dom\Builder\xmlns_attribute; -use function VeeWee\Xml\Dom\Predicate\is_element; +use function VeeWee\Xml\Dom\Locator\Attribute\attributes_list; +use function VeeWee\Xml\Dom\Locator\Element\children; use function VeeWee\Xml\Dom\Predicate\is_xmlns_attribute; /** * @throws RuntimeException * @param non-empty-string $newPrefix */ -function rename_element_namespace(\DOM\Element $root, string $namespaceURI, string $newPrefix): void +function rename_element_namespace(\DOM\Element $element, string $namespaceURI, string $newPrefix): void { + children($element)->forEach( + static fn (\DOM\Element $child) => rename_element_namespace($child, $namespaceURI, $newPrefix) + ); - $recurse = function (\DOM\Element $element) use (&$recurse, $namespaceURI, $newPrefix): void { - foreach ($element->childNodes as $child) { - if (is_element($child)) { - $recurse($child); - } + attributes_list($element)->forEach(static function (\DOM\Attr $attr) use ($namespaceURI, $newPrefix, $element) { + if ($attr->namespaceURI === $namespaceURI) { + $attr->rename($namespaceURI, $newPrefix . ':' . $attr->localName); } - $hasXmlnsAttribute = false; - foreach ($element->attributes as $attr) { - if ($attr->namespaceURI === $namespaceURI) { - $attr->rename($namespaceURI, $newPrefix . ':' . $attr->localName); - } - - if (is_xmlns_attribute($attr) && $attr->value === $namespaceURI) { + if (is_xmlns_attribute($attr) && $attr->value === $namespaceURI) { + try { $attr->rename($attr->namespaceURI, 'xmlns:' . $newPrefix); - } - } - if ($element->namespaceURI === $namespaceURI) { - $element->rename($namespaceURI, $newPrefix . ':' . $element->localName); + } catch (\DOMException $e) { + if ($e->getCode() === \DOM\INVALID_MODIFICATION_ERR) { + // Remove the attribute that would become a duplicate + $element->removeAttributeNode($attr); + } else { + throw $e; + } + } + $attr->rename($attr->namespaceURI, 'xmlns:' . $newPrefix); } - }; + }); - $recurse( - // TODO - should become : xmlns_attribute($newPrefix, $namespaceURI)($root), - $root - ); + if ($element->namespaceURI === $namespaceURI) { + $element->rename($namespaceURI, $newPrefix . ':' . $element->localName); + } } diff --git a/tests/Xml/Dom/Configurator/OptimizeNamespacesTest.php b/tests/Xml/Dom/Configurator/OptimizeNamespacesTest.php index f38d409..8f2224e 100644 --- a/tests/Xml/Dom/Configurator/OptimizeNamespacesTest.php +++ b/tests/Xml/Dom/Configurator/OptimizeNamespacesTest.php @@ -52,7 +52,7 @@ public function provideXmls() EOXML, << + EOXML, @@ -64,7 +64,7 @@ public function provideXmls() EOXML, << + EOXML, diff --git a/tests/Xml/Dom/Manipulator/Document/OptimizeNamespacesTest.php b/tests/Xml/Dom/Manipulator/Document/OptimizeNamespacesTest.php index 01ffb87..a775cb7 100644 --- a/tests/Xml/Dom/Manipulator/Document/OptimizeNamespacesTest.php +++ b/tests/Xml/Dom/Manipulator/Document/OptimizeNamespacesTest.php @@ -106,7 +106,7 @@ public function provideXmls() EOXML, << + Jos Jaak Jul From 4a980ec8ab41fa0eaa2d739cdfabfa1604eb0d33 Mon Sep 17 00:00:00 2001 From: Toon Verwerft Date: Tue, 16 Apr 2024 16:05:05 +0200 Subject: [PATCH 07/10] Fix static analysis --- .github/workflows/analyzers.yaml | 1 + .github/workflows/autoloader.yaml | 1 + .github/workflows/code-style.yaml | 1 + .github/workflows/stress.yaml | 3 +- .github/workflows/tests.yaml | 4 +- psalm.xml | 1 + src/Xml/Dom/Collection/NodeList.php | 6 +- src/Xml/Dom/Locator/Node/detect_document.php | 6 +- .../Xsd/locate_namespaced_xsd_schemas.php | 7 +- .../Xsd/locate_no_namespaced_xsd_schemas.php | 7 +- src/Xml/Dom/Locator/document_element.php | 3 +- .../elements_with_namespaced_tagname.php | 6 +- src/Xml/Dom/Locator/elements_with_tagname.php | 5 +- src/Xml/Dom/Locator/root_namespace.php | 2 +- src/Xml/Dom/Manipulator/Attribute/rename.php | 5 +- .../Document/optimize_namespaces.php | 5 +- .../Element/copy_named_xmlns_attributes.php | 2 +- src/Xml/Dom/Manipulator/Element/rename.php | 4 +- .../Traverser/Visitor/RemoveNamespaces.php | 3 +- .../Internal/Decoder/Builder/element.php | 4 +- .../Internal/Decoder/Builder/namespaces.php | 2 +- stubs/DOM.phpstub | 48 +++++-- stubs/php_xsl.stub.phpstub | 131 ++++++++++++++++++ .../Document/OptimizeNamespacesTest.php | 24 ++++ 24 files changed, 240 insertions(+), 41 deletions(-) create mode 100644 stubs/php_xsl.stub.phpstub diff --git a/.github/workflows/analyzers.yaml b/.github/workflows/analyzers.yaml index 7ade8b2..3735eea 100644 --- a/.github/workflows/analyzers.yaml +++ b/.github/workflows/analyzers.yaml @@ -13,6 +13,7 @@ jobs: matrix: operating-system: [ubuntu-latest] php-versions: ['8.4'] + composer-options: ['--ignore-platform-req=php+'] fail-fast: false name: PHP ${{ matrix.php-versions }} @ ${{ matrix.operating-system }} steps: diff --git a/.github/workflows/autoloader.yaml b/.github/workflows/autoloader.yaml index 39e7ec9..db62f8f 100644 --- a/.github/workflows/autoloader.yaml +++ b/.github/workflows/autoloader.yaml @@ -8,6 +8,7 @@ jobs: matrix: operating-system: [ubuntu-latest] php-versions: ['8.4'] + composer-options: ['--ignore-platform-req=php+'] fail-fast: false name: PHP ${{ matrix.php-versions }} @ ${{ matrix.operating-system }} steps: diff --git a/.github/workflows/code-style.yaml b/.github/workflows/code-style.yaml index dadf1e6..f2ea74d 100644 --- a/.github/workflows/code-style.yaml +++ b/.github/workflows/code-style.yaml @@ -12,6 +12,7 @@ jobs: matrix: operating-system: [ubuntu-latest] php-versions: ['8.4'] + composer-options: ['--ignore-platform-req=php+'] fail-fast: false name: PHP ${{ matrix.php-versions }} @ ${{ matrix.operating-system }} steps: diff --git a/.github/workflows/stress.yaml b/.github/workflows/stress.yaml index 4141f4f..54a919c 100644 --- a/.github/workflows/stress.yaml +++ b/.github/workflows/stress.yaml @@ -8,6 +8,7 @@ jobs: matrix: operating-system: [ubuntu-latest] php-versions: ['8.4'] + composer-options: ['--ignore-platform-req=php+'] fail-fast: false name: PHP ${{ matrix.php-versions }} @ ${{ matrix.operating-system }} steps: @@ -21,6 +22,6 @@ jobs: tools: 'composer:v2' extensions: pcov, mbstring, posix, dom, libxml, xml, xsl, xmlreader, xmlwriter - name: Install dependencies - run: composer update --prefer-dist --no-progress --no-suggest + run: composer update --prefer-dist --no-progress --no-suggest ${{ matrix.composer-options }} - name: Run the stress tests run: composer run stress diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 013c038..3962f3b 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -13,7 +13,7 @@ jobs: matrix: operating-system: [ubuntu-latest] php-versions: ['8.4'] - experimental: [false] + composer-options: ['--ignore-platform-req=php+'] fail-fast: false name: PHP ${{ matrix.php-versions }} @ ${{ matrix.operating-system }} steps: @@ -27,7 +27,7 @@ jobs: ini-values: error_reporting=E_ALL extensions: pcov, mbstring, posix, dom, libxml, xml, xsl, xmlreader, xmlwriter - name: Install dependencies - run: composer update --prefer-dist --no-progress --no-suggest + run: composer update --prefer-dist --no-progress --no-suggest ${{ matrix.composer-options }} - name: Run the tests run: composer run tests - name: Check tests quality diff --git a/psalm.xml b/psalm.xml index 95604c8..8cf9405 100644 --- a/psalm.xml +++ b/psalm.xml @@ -45,6 +45,7 @@ + diff --git a/src/Xml/Dom/Collection/NodeList.php b/src/Xml/Dom/Collection/NodeList.php index fa8191e..a78ddc1 100644 --- a/src/Xml/Dom/Collection/NodeList.php +++ b/src/Xml/Dom/Collection/NodeList.php @@ -8,7 +8,7 @@ use \DOM\Element; use \DOM\Node; use \DOM\NodeList as DOMNodeList; -use DOMXpath as DOMXpath; +use \DOM\XPath as DOMXPath; use Generator; use InvalidArgumentException; use IteratorAggregate; @@ -193,7 +193,7 @@ public function reduce(callable $reducer, mixed $initial): mixed } /** - * @param list $configurators + * @param list $configurators * @throws RuntimeException * @return NodeList<\DOM\Node> */ @@ -211,7 +211,7 @@ public function query(string $xpath, callable ... $configurators): self /** * @template X - * @param list $configurators + * @param list $configurators * @param TypeInterface $type * @return list */ diff --git a/src/Xml/Dom/Locator/Node/detect_document.php b/src/Xml/Dom/Locator/Node/detect_document.php index 86a5c7a..d3a3188 100644 --- a/src/Xml/Dom/Locator/Node/detect_document.php +++ b/src/Xml/Dom/Locator/Node/detect_document.php @@ -5,16 +5,14 @@ namespace VeeWee\Xml\Dom\Locator\Node; use \DOM\XMLDocument; -use \DOM\Node; use InvalidArgumentException; -use Webmozart\Assert\Assert; +use function VeeWee\Xml\Dom\Assert\assert_document; use function VeeWee\Xml\Dom\Predicate\is_document; /** * @throws InvalidArgumentException - * @psalm-suppress RedundantCondition - node->ownerDocument can also be null... */ function detect_document(\DOM\Node $node): \DOM\XMLDocument { - return is_document($node) ? $node : $node->ownerDocument; + return is_document($node) ? $node : assert_document($node->ownerDocument); } diff --git a/src/Xml/Dom/Locator/Xsd/locate_namespaced_xsd_schemas.php b/src/Xml/Dom/Locator/Xsd/locate_namespaced_xsd_schemas.php index 47d45e4..e4b2ffe 100644 --- a/src/Xml/Dom/Locator/Xsd/locate_namespaced_xsd_schemas.php +++ b/src/Xml/Dom/Locator/Xsd/locate_namespaced_xsd_schemas.php @@ -10,6 +10,7 @@ use VeeWee\Xml\Xsd\Schema\Schema; use VeeWee\Xml\Xsd\Schema\SchemaCollection; use function Psl\Regex\split; +use function VeeWee\Xml\Dom\Locator\document_element; /** * @throws RuntimeException @@ -17,15 +18,15 @@ function locate_namespaced_xsd_schemas(\DOM\XMLDocument $document): SchemaCollection { $schemaNs = Xmlns::xsi()->value(); - $attributes = $document->documentElement->attributes; + $documentElement = document_element()($document); + $attributes = $documentElement->attributes; $collection = new SchemaCollection(); if (!$schemaLocation = $attributes->getNamedItemNS($schemaNs, 'schemaLocation')) { return $collection; } - /** @psalm-suppress MissingThrowsDocblock - Covered the runtime exception! */ - $parts = split(trim($schemaLocation->textContent), '/\s+/'); + $parts = split(trim($schemaLocation->textContent ?? ''), '/\s+/'); $partsCount = count($parts); for ($k = 0; $k < $partsCount; $k += 2) { $collection = $collection->add(Schema::withNamespace($parts[$k], $parts[$k + 1])); diff --git a/src/Xml/Dom/Locator/Xsd/locate_no_namespaced_xsd_schemas.php b/src/Xml/Dom/Locator/Xsd/locate_no_namespaced_xsd_schemas.php index 2a9600f..b03abf5 100644 --- a/src/Xml/Dom/Locator/Xsd/locate_no_namespaced_xsd_schemas.php +++ b/src/Xml/Dom/Locator/Xsd/locate_no_namespaced_xsd_schemas.php @@ -11,6 +11,7 @@ use VeeWee\Xml\Xsd\Schema\SchemaCollection; use function Psl\Dict\map; use function Psl\Regex\split; +use function VeeWee\Xml\Dom\Locator\document_element; /** * @throws RuntimeException @@ -18,13 +19,13 @@ function locate_no_namespaced_xsd_schemas(\DOM\XMLDocument $document): SchemaCollection { $schemaNs = Xmlns::xsi()->value(); - $attributes = $document->documentElement->attributes; + $documentElement = document_element()($document); + $attributes = $documentElement->attributes; if (!$schemaLocNoNamespace = $attributes->getNamedItemNS($schemaNs, 'noNamespaceSchemaLocation')) { return new SchemaCollection(); } - /** @psalm-suppress MissingThrowsDocblock - Covered the runtime exception! */ - $parts = split(trim($schemaLocNoNamespace->textContent), '/\s+/'); + $parts = split(trim($schemaLocNoNamespace->textContent ?? ''), '/\s+/'); return new SchemaCollection( ...map( diff --git a/src/Xml/Dom/Locator/document_element.php b/src/Xml/Dom/Locator/document_element.php index 821b997..ace573b 100644 --- a/src/Xml/Dom/Locator/document_element.php +++ b/src/Xml/Dom/Locator/document_element.php @@ -7,11 +7,12 @@ use Closure; use \DOM\XMLDocument; use \DOM\Element; +use function VeeWee\Xml\Dom\Assert\assert_element; /** * @return Closure(\DOM\XMLDocument): \DOM\Element */ function document_element(): Closure { - return static fn (\DOM\XMLDocument $document): \DOM\Element => $document->documentElement; + return static fn (\DOM\XMLDocument $document): \DOM\Element => assert_element($document->documentElement); } diff --git a/src/Xml/Dom/Locator/elements_with_namespaced_tagname.php b/src/Xml/Dom/Locator/elements_with_namespaced_tagname.php index 951f9a7..eefbf3e 100644 --- a/src/Xml/Dom/Locator/elements_with_namespaced_tagname.php +++ b/src/Xml/Dom/Locator/elements_with_namespaced_tagname.php @@ -20,5 +20,9 @@ function elements_with_namespaced_tagname(string $namespace, string $localTagNam * @return NodeList<\DOM\Element> */ static fn (\DOM\XMLDocument $document): NodeList - => locate_by_namespaced_tag_name($document->documentElement, $namespace, $localTagName); + => locate_by_namespaced_tag_name( + document_element()($document), + $namespace, + $localTagName + ); } diff --git a/src/Xml/Dom/Locator/elements_with_tagname.php b/src/Xml/Dom/Locator/elements_with_tagname.php index c2773fb..6159b73 100644 --- a/src/Xml/Dom/Locator/elements_with_tagname.php +++ b/src/Xml/Dom/Locator/elements_with_tagname.php @@ -20,5 +20,8 @@ function elements_with_tagname(string $tagName): Closure * @return NodeList<\DOM\Element> */ static fn (\DOM\XMLDocument $document): NodeList - => locate_by_tag_name($document->documentElement, $tagName); + => locate_by_tag_name( + document_element()($document), + $tagName + ); } diff --git a/src/Xml/Dom/Locator/root_namespace.php b/src/Xml/Dom/Locator/root_namespace.php index d182f0a..719c6bb 100644 --- a/src/Xml/Dom/Locator/root_namespace.php +++ b/src/Xml/Dom/Locator/root_namespace.php @@ -12,5 +12,5 @@ */ function root_namespace_uri(): Closure { - return static fn (\DOM\XMLDocument $document): ?string => $document->documentElement->namespaceURI; + return static fn (\DOM\XMLDocument $document): ?string => document_element()($document)->namespaceURI; } diff --git a/src/Xml/Dom/Manipulator/Attribute/rename.php b/src/Xml/Dom/Manipulator/Attribute/rename.php index e3b2ea7..cac7cbd 100644 --- a/src/Xml/Dom/Manipulator/Attribute/rename.php +++ b/src/Xml/Dom/Manipulator/Attribute/rename.php @@ -18,6 +18,9 @@ function rename(\DOM\Attr $target, string $newQName, ?string $newNamespaceURI = { return disallow_issues(static fn (): \DOM\Attr => match(true) { is_xmlns_attribute($target) => rename_xmlns_attribute($target, $newQName), - default => tap(fn () => $target->rename($newNamespaceURI, $newQName))($target) + default => (function() use ($target, $newNamespaceURI, $newQName): \DOM\Attr { + $target->rename($newNamespaceURI, $newQName); + return $target; + })() }); } diff --git a/src/Xml/Dom/Manipulator/Document/optimize_namespaces.php b/src/Xml/Dom/Manipulator/Document/optimize_namespaces.php index 1f351f2..0f1a95e 100644 --- a/src/Xml/Dom/Manipulator/Document/optimize_namespaces.php +++ b/src/Xml/Dom/Manipulator/Document/optimize_namespaces.php @@ -10,6 +10,7 @@ use function Psl\Vec\sort; use function Psl\Vec\values; use function VeeWee\Xml\Dom\Builder\xmlns_attribute; +use function VeeWee\Xml\Dom\Locator\document_element; use function VeeWee\Xml\Dom\Locator\Xmlns\recursive_linked_namespaces; use function VeeWee\Xml\Dom\Manipulator\Xmlns\rename_element_namespace; @@ -18,10 +19,10 @@ */ function optimize_namespaces(\DOM\XMLDocument $document, string $prefix = 'ns'): void { - $documentElement = $document->documentElement; + $documentElement = document_element()($document); $namespaceURIs = values(unique(map( recursive_linked_namespaces($documentElement), - static fn (\DOM\NamespaceInfo $info): string => $info->namespaceURI + static fn (\DOM\NamespaceInfo $info): string => $info->namespaceURI ?? '' ))); foreach (sort($namespaceURIs) as $index => $namespaceURI) { diff --git a/src/Xml/Dom/Manipulator/Element/copy_named_xmlns_attributes.php b/src/Xml/Dom/Manipulator/Element/copy_named_xmlns_attributes.php index b2a32f7..9eb8136 100644 --- a/src/Xml/Dom/Manipulator/Element/copy_named_xmlns_attributes.php +++ b/src/Xml/Dom/Manipulator/Element/copy_named_xmlns_attributes.php @@ -14,7 +14,7 @@ function copy_named_xmlns_attributes(\DOM\Element $target, \DOM\Element $source): void { xmlns_attributes_list($source)->forEach(static function (\DOM\Attr $xmlns) use ($target) { - if ($xmlns->prefix && !$target->hasAttribute($xmlns->nodeName)) { + if ($xmlns->prefix !== null && !$target->hasAttribute($xmlns->nodeName)) { xmlns_attribute($xmlns->localName, $xmlns->value)($target); } }); diff --git a/src/Xml/Dom/Manipulator/Element/rename.php b/src/Xml/Dom/Manipulator/Element/rename.php index 58b4aff..b220cd3 100644 --- a/src/Xml/Dom/Manipulator/Element/rename.php +++ b/src/Xml/Dom/Manipulator/Element/rename.php @@ -16,7 +16,7 @@ function rename(\DOM\Element $target, string $newQName, ?string $newNamespaceURI $newPrefix = $parts[0] ?? ''; /* - * To make sure the new namespace prefix is being used, we need to apply an additional xmlns declaration chech: + * To make sure the new namespace prefix is being used, we need to apply an additional xmlns declaration check: * This is due to a particular rule in the XML serialization spec, * that enforces that a namespaceURI on an element is only associated with exactly one prefix. * See the note of bullet point 2 of https://www.w3.org/TR/DOM-Parsing/#dfn-concept-serialize-xml. @@ -24,7 +24,7 @@ function rename(\DOM\Element $target, string $newQName, ?string $newNamespaceURI * If you rename a:xx to b:xx an xmlns:b="xx" attribute gets added at the end, but prefix a: will still be serialized. * So in this case, we need to remove the xmlns declaration first. */ - if ($newPrefix && $newPrefix !== $target->prefix && $target->hasAttribute('xmlns:'.$target->prefix)) { + if ($newPrefix && $target->prefix !== null && $newPrefix !== $target->prefix && $target->hasAttribute('xmlns:'.$target->prefix)) { $target->removeAttribute('xmlns:'.$target->prefix); } diff --git a/src/Xml/Dom/Traverser/Visitor/RemoveNamespaces.php b/src/Xml/Dom/Traverser/Visitor/RemoveNamespaces.php index 0da7c9a..adfd2b6 100644 --- a/src/Xml/Dom/Traverser/Visitor/RemoveNamespaces.php +++ b/src/Xml/Dom/Traverser/Visitor/RemoveNamespaces.php @@ -18,7 +18,7 @@ final class RemoveNamespaces extends AbstractVisitor private $filter; /** - * @param null | callable(\DOM\Attr): bool $filter + * @param null | callable(\DOM\Attr | \DOM\Element): bool $filter */ public function __construct( ?callable $filter = null @@ -81,6 +81,7 @@ public function onNodeEnter(\DOM\Node $node): Action return new Action\Noop(); } + /** @var \DOM\Element | \DOM\Attr $node */ return new Action\RenameNode($node->localName, null); } diff --git a/src/Xml/Encoding/Internal/Decoder/Builder/element.php b/src/Xml/Encoding/Internal/Decoder/Builder/element.php index d9bf991..c79047c 100644 --- a/src/Xml/Encoding/Internal/Decoder/Builder/element.php +++ b/src/Xml/Encoding/Internal/Decoder/Builder/element.php @@ -22,7 +22,7 @@ function element(\DOM\Element $element): array $namespaces = namespaces($element); if (!count($children) && !count($attributes) && !count($namespaces)) { - return [$name => $element->textContent]; + return [$name => $element->textContent ?? '']; } return [ @@ -30,7 +30,7 @@ function element(\DOM\Element $element): array merge( $namespaces, $attributes, - $children ?: ['@value' => $element->textContent] + $children ?: ['@value' => $element->textContent ?? ''] ), static fn (mixed $data): bool => $data !== [] ) diff --git a/src/Xml/Encoding/Internal/Decoder/Builder/namespaces.php b/src/Xml/Encoding/Internal/Decoder/Builder/namespaces.php index ddf5a5a..05232af 100644 --- a/src/Xml/Encoding/Internal/Decoder/Builder/namespaces.php +++ b/src/Xml/Encoding/Internal/Decoder/Builder/namespaces.php @@ -22,7 +22,7 @@ function namespaces(\DOM\Element $element): array static fn (array $namespaces, \DOM\Attr $node) => $node->value ? merge($namespaces, [ - ($node->prefix ? $node->localName : '') => $node->value + ($node->prefix !== null ? $node->localName : '') => $node->value ]) : $namespaces, [] diff --git a/stubs/DOM.phpstub b/stubs/DOM.phpstub index 80923b0..1208b34 100644 --- a/stubs/DOM.phpstub +++ b/stubs/DOM.phpstub @@ -1027,6 +1027,9 @@ namespace namespace DOM { + + use Psl\Iter\Iterator; + /** * @var int * @cvalue INDEX_SIZE_ERR @@ -1163,7 +1166,10 @@ namespace DOM public ?Element $parentElement; /** @implementation-alias DOMNode::hasChildNodes */ public function hasChildNodes(): bool {} - /** @readonly */ + /** + * @readonly + * @var NodeList + */ public NodeList $childNodes; /** @readonly */ public ?Node $firstChild; @@ -1218,7 +1224,11 @@ namespace DOM public function __wakeup(): void {} } - class NodeList implements IteratorAggregate, Countable + /** + * @template-covariant TNode as \DOM\Node + * @template-implements \IteratorAggregate + */ + class NodeList implements \IteratorAggregate, \Countable { /** @readonly */ public int $length; @@ -1226,14 +1236,23 @@ namespace DOM /** @implementation-alias DOMNodeList::count */ public function count(): int {} - /** @implementation-alias DOMNodeList::getIterator */ + /** + * @implementation-alias DOMNodeList::getIterator + * @psalm-return iterable + */ public function getIterator(): \Iterator {} - /** @implementation-alias DOMNodeList::item */ + /** + * @implementation-alias DOMNodeList::item + * @psalm-return TNode + */ public function item(int $index): ?Node {} } - class NamedNodeMap implements IteratorAggregate, Countable + /** + * @implements \IteratorAggregate + */ + class NamedNodeMap implements \IteratorAggregate, \Countable { /** @readonly */ public int $length; @@ -1248,11 +1267,14 @@ namespace DOM /** @implementation-alias DOMNamedNodeMap::count */ public function count(): int {} - /** @implementation-alias DOMNamedNodeMap::getIterator */ + /** + * @implementation-alias DOMNamedNodeMap::getIterator + * @psalm-return iterable + */ public function getIterator(): \Iterator {} } - class DTDNamedNodeMap implements IteratorAggregate, Countable + class DTDNamedNodeMap implements \IteratorAggregate, \Countable { /** @readonly */ public int $length; @@ -1271,7 +1293,10 @@ namespace DOM public function getIterator(): \Iterator {} } - class HTMLCollection implements IteratorAggregate, Countable + /** + * @implements \IteratorAggregate + */ + class HTMLCollection implements \IteratorAggregate, \Countable { /** @readonly */ public int $length; @@ -1284,7 +1309,10 @@ namespace DOM /** @implementation-alias DOMNodeList::count */ public function count(): int {} - /** @implementation-alias DOMNodeList::getIterator */ + /** + * @implementation-alias DOMNodeList::getIterator + * @psalm-return iterable + */ public function getIterator(): \Iterator {} } @@ -1528,9 +1556,7 @@ namespace DOM { /** @readonly */ public Implementation $implementation; - /** @readonly */ public string $URL; - /** @readonly */ public string $documentURI; public string $characterSet; public string $charset; diff --git a/stubs/php_xsl.stub.phpstub b/stubs/php_xsl.stub.phpstub new file mode 100644 index 0000000..c01e218 --- /dev/null +++ b/stubs/php_xsl.stub.phpstub @@ -0,0 +1,131 @@ + EOXML, ]; + yield 'only-inline-namespace' => [ + << + + + EOXML, + << + + + EOXML, + ]; + yield 'empty-namespace' => [ + << + + + EOXML, + << + + + EOXML, + ]; } } From 9e5c0edc3db661549af3201036b053b2fd012fb0 Mon Sep 17 00:00:00 2001 From: Toon Verwerft Date: Tue, 9 Jul 2024 13:36:44 +0200 Subject: [PATCH 08/10] Make compatible with alpha --- src/Xml/Dom/Assert/assert_attribute.php | 8 +- src/Xml/Dom/Assert/assert_cdata.php | 8 +- src/Xml/Dom/Assert/assert_document.php | 8 +- src/Xml/Dom/Assert/assert_dom_node_list.php | 8 +- src/Xml/Dom/Assert/assert_element.php | 8 +- src/Xml/Dom/Builder/Builder.php | 4 +- src/Xml/Dom/Builder/attribute.php | 6 +- src/Xml/Dom/Builder/attributes.php | 8 +- src/Xml/Dom/Builder/cdata.php | 10 +- src/Xml/Dom/Builder/children.php | 8 +- src/Xml/Dom/Builder/element.php | 10 +- src/Xml/Dom/Builder/escaped_value.php | 4 +- src/Xml/Dom/Builder/namespaced_attribute.php | 6 +- src/Xml/Dom/Builder/namespaced_attributes.php | 8 +- src/Xml/Dom/Builder/namespaced_element.php | 10 +- src/Xml/Dom/Builder/nodes.php | 18 +-- src/Xml/Dom/Builder/value.php | 6 +- src/Xml/Dom/Builder/xmlns_attribute.php | 6 +- src/Xml/Dom/Builder/xmlns_attributes.php | 8 +- src/Xml/Dom/Collection/NodeList.php | 52 +++---- src/Xml/Dom/Configurator/Configurator.php | 4 +- src/Xml/Dom/Configurator/canonicalize.php | 2 +- src/Xml/Dom/Configurator/comparable.php | 4 +- src/Xml/Dom/Configurator/document_uri.php | 6 +- src/Xml/Dom/Configurator/normalize.php | 6 +- .../Dom/Configurator/optimize_namespaces.php | 6 +- src/Xml/Dom/Configurator/pretty_print.php | 4 +- src/Xml/Dom/Configurator/traverse.php | 2 +- src/Xml/Dom/Configurator/trim_spaces.php | 6 +- src/Xml/Dom/Configurator/utf8.php | 6 +- src/Xml/Dom/Configurator/validator.php | 8 +- src/Xml/Dom/Document.php | 20 +-- src/Xml/Dom/Loader/Loader.php | 2 +- src/Xml/Dom/Loader/xml_file_loader.php | 2 +- src/Xml/Dom/Loader/xml_node_loader.php | 6 +- src/Xml/Dom/Loader/xml_string_loader.php | 2 +- .../Dom/Locator/Attribute/attributes_list.php | 8 +- .../Attribute/xmlns_attributes_list.php | 6 +- src/Xml/Dom/Locator/Element/ancestors.php | 12 +- src/Xml/Dom/Locator/Element/children.php | 12 +- .../Element/locate_by_namespaced_tag_name.php | 6 +- .../Locator/Element/locate_by_tag_name.php | 6 +- .../Dom/Locator/Element/parent_element.php | 6 +- src/Xml/Dom/Locator/Element/siblings.php | 12 +- src/Xml/Dom/Locator/Node/children.php | 6 +- src/Xml/Dom/Locator/Node/detect_document.php | 4 +- src/Xml/Dom/Locator/Node/value.php | 4 +- .../Dom/Locator/Xmlns/linked_namespaces.php | 4 +- .../Xmlns/recursive_linked_namespaces.php | 4 +- .../Locator/Xsd/locate_all_xsd_schemas.php | 4 +- .../Xsd/locate_namespaced_xsd_schemas.php | 4 +- .../Xsd/locate_no_namespaced_xsd_schemas.php | 4 +- src/Xml/Dom/Locator/document_element.php | 8 +- .../elements_with_namespaced_tagname.php | 10 +- src/Xml/Dom/Locator/elements_with_tagname.php | 10 +- src/Xml/Dom/Locator/root_namespace.php | 6 +- src/Xml/Dom/Manipulator/Attribute/rename.php | 8 +- .../Document/optimize_namespaces.php | 4 +- .../Element/copy_named_xmlns_attributes.php | 4 +- src/Xml/Dom/Manipulator/Element/rename.php | 2 +- .../Manipulator/Node/append_external_node.php | 4 +- .../Manipulator/Node/import_node_deeply.php | 6 +- src/Xml/Dom/Manipulator/Node/remove.php | 6 +- .../Dom/Manipulator/Node/remove_namespace.php | 4 +- src/Xml/Dom/Manipulator/Node/rename.php | 4 +- .../Node/replace_by_external_node.php | 6 +- .../Node/replace_by_external_nodes.php | 10 +- src/Xml/Dom/Manipulator/Xmlns/rename.php | 2 +- .../Xmlns/rename_element_namespace.php | 8 +- src/Xml/Dom/Manipulator/append.php | 8 +- src/Xml/Dom/Mapper/Mapper.php | 4 +- src/Xml/Dom/Mapper/xml_string.php | 6 +- src/Xml/Dom/Mapper/xslt_template.php | 2 +- src/Xml/Dom/Predicate/is_attribute.php | 10 +- src/Xml/Dom/Predicate/is_cdata.php | 10 +- .../Predicate/is_default_xmlns_attribute.php | 4 +- src/Xml/Dom/Predicate/is_document.php | 10 +- src/Xml/Dom/Predicate/is_document_element.php | 4 +- src/Xml/Dom/Predicate/is_element.php | 10 +- src/Xml/Dom/Predicate/is_non_empty_text.php | 4 +- src/Xml/Dom/Predicate/is_text.php | 10 +- src/Xml/Dom/Predicate/is_whitespace.php | 4 +- src/Xml/Dom/Predicate/is_xmlns_attribute.php | 6 +- src/Xml/Dom/Traverser/Action.php | 4 +- src/Xml/Dom/Traverser/Action/Noop.php | 4 +- src/Xml/Dom/Traverser/Action/RemoveNode.php | 4 +- src/Xml/Dom/Traverser/Action/RenameNode.php | 4 +- src/Xml/Dom/Traverser/Action/ReplaceNode.php | 6 +- src/Xml/Dom/Traverser/Traverser.php | 8 +- src/Xml/Dom/Traverser/Visitor.php | 6 +- .../Dom/Traverser/Visitor/AbstractVisitor.php | 6 +- .../Traverser/Visitor/RemoveNamespaces.php | 20 +-- .../Dom/Traverser/Visitor/SortAttributes.php | 6 +- src/Xml/Dom/Validator/Validator.php | 4 +- .../Dom/Validator/internal_xsd_validator.php | 8 +- src/Xml/Dom/Validator/validator_chain.php | 10 +- src/Xml/Dom/Validator/xsd_validator.php | 6 +- src/Xml/Dom/Xpath.php | 14 +- .../Dom/Xpath/Configurator/Configurator.php | 4 +- .../Dom/Xpath/Configurator/all_functions.php | 6 +- src/Xml/Dom/Xpath/Configurator/functions.php | 6 +- src/Xml/Dom/Xpath/Configurator/namespaces.php | 6 +- .../Dom/Xpath/Configurator/php_namespace.php | 6 +- src/Xml/Dom/Xpath/Locator/Locator.php | 4 +- src/Xml/Dom/Xpath/Locator/evaluate.php | 10 +- src/Xml/Dom/Xpath/Locator/query.php | 12 +- src/Xml/Dom/Xpath/Locator/query_single.php | 14 +- .../Internal/Decoder/Builder/attribute.php | 4 +- .../Internal/Decoder/Builder/attributes.php | 10 +- .../Internal/Decoder/Builder/element.php | 4 +- .../Decoder/Builder/group_child_elements.php | 6 +- .../Decoder/Builder/grouped_children.php | 10 +- .../Internal/Decoder/Builder/name.php | 4 +- .../Internal/Decoder/Builder/namespaces.php | 6 +- .../Internal/Encoder/Builder/children.php | 6 +- .../Internal/Encoder/Builder/element.php | 8 +- .../Internal/Encoder/Builder/parent_node.php | 6 +- .../Internal/Encoder/Builder/root.php | 6 +- src/Xml/Encoding/document_encode.php | 2 +- src/Xml/Encoding/element_decode.php | 6 +- src/Xml/Encoding/element_encode.php | 2 +- src/Xml/Encoding/typed.php | 4 +- src/Xml/Encoding/xml_decode.php | 2 +- src/Xml/Encoding/xml_encode.php | 2 +- src/Xml/Reader/MatchingNode.php | 2 +- stubs/DOM.phpstub | 138 ++++++++++++------ tests/Xml/Dom/Loader/XmlFileLoaderTest.php | 2 +- tests/Xml/Dom/Loader/XmlStringLoaderTest.php | 2 +- .../Dom/Locator/Element/ParentElementTest.php | 2 +- .../Node/ReplaceByExternalNodeTest.php | 2 +- .../Node/ReplaceByExternalNodesTest.php | 2 +- 131 files changed, 525 insertions(+), 477 deletions(-) diff --git a/src/Xml/Dom/Assert/assert_attribute.php b/src/Xml/Dom/Assert/assert_attribute.php index 83234f1..25150f6 100644 --- a/src/Xml/Dom/Assert/assert_attribute.php +++ b/src/Xml/Dom/Assert/assert_attribute.php @@ -4,15 +4,15 @@ namespace VeeWee\Xml\Dom\Assert; -use \DOM\Attr; +use \Dom\Attr; use Psl\Type\Exception\AssertException; use function Psl\Type\instance_of; /** - * @psalm-assert \DOM\Element $node + * @psalm-assert \Dom\Element $node * @throws AssertException */ -function assert_attribute(mixed $node): \DOM\Attr +function assert_attribute(mixed $node): \Dom\Attr { - return instance_of(\DOM\Attr::class)->assert($node); + return instance_of(\Dom\Attr::class)->assert($node); } diff --git a/src/Xml/Dom/Assert/assert_cdata.php b/src/Xml/Dom/Assert/assert_cdata.php index b9c2167..9d3b426 100644 --- a/src/Xml/Dom/Assert/assert_cdata.php +++ b/src/Xml/Dom/Assert/assert_cdata.php @@ -4,15 +4,15 @@ namespace VeeWee\Xml\Dom\Assert; -use \DOM\CDATASection; +use \Dom\CDATASection; use Psl\Type\Exception\AssertException; use function Psl\Type\instance_of; /** - * @psalm-assert \DOM\CDATASection $node + * @psalm-assert \Dom\CDATASection $node * @throws AssertException */ -function assert_cdata(mixed $node): \DOM\CDATASection +function assert_cdata(mixed $node): \Dom\CDATASection { - return instance_of(\DOM\CDATASection::class)->assert($node); + return instance_of(\Dom\CDATASection::class)->assert($node); } diff --git a/src/Xml/Dom/Assert/assert_document.php b/src/Xml/Dom/Assert/assert_document.php index ad53687..f4b272b 100644 --- a/src/Xml/Dom/Assert/assert_document.php +++ b/src/Xml/Dom/Assert/assert_document.php @@ -4,15 +4,15 @@ namespace VeeWee\Xml\Dom\Assert; -use \DOM\XMLDocument; +use \Dom\XMLDocument; use Psl\Type\Exception\AssertException; use function Psl\Type\instance_of; /** - * @psalm-assert \DOM\XMLDocument $node + * @psalm-assert \Dom\XMLDocument $node * @throws AssertException */ -function assert_document(mixed $node): \DOM\XMLDocument +function assert_document(mixed $node): \Dom\XMLDocument { - return instance_of(\DOM\XMLDocument::class)->assert($node); + return instance_of(\Dom\XMLDocument::class)->assert($node); } diff --git a/src/Xml/Dom/Assert/assert_dom_node_list.php b/src/Xml/Dom/Assert/assert_dom_node_list.php index 91ecfe6..164a5a0 100644 --- a/src/Xml/Dom/Assert/assert_dom_node_list.php +++ b/src/Xml/Dom/Assert/assert_dom_node_list.php @@ -4,15 +4,15 @@ namespace VeeWee\Xml\Dom\Assert; -use \DOM\NodeList; +use \Dom\NodeList; use Psl\Type\Exception\AssertException; use function Psl\Type\instance_of; /** - * @psalm-assert \DOM\NodeList $node + * @psalm-assert \Dom\NodeList $node * @throws AssertException */ -function assert_dom_node_list(mixed $node): \DOM\NodeList +function assert_dom_node_list(mixed $node): \Dom\NodeList { - return instance_of(\DOM\NodeList::class)->assert($node); + return instance_of(\Dom\NodeList::class)->assert($node); } diff --git a/src/Xml/Dom/Assert/assert_element.php b/src/Xml/Dom/Assert/assert_element.php index 50e2a5b..ba22edc 100644 --- a/src/Xml/Dom/Assert/assert_element.php +++ b/src/Xml/Dom/Assert/assert_element.php @@ -4,15 +4,15 @@ namespace VeeWee\Xml\Dom\Assert; -use \DOM\Element; +use \Dom\Element; use Psl\Type\Exception\AssertException; use function Psl\Type\instance_of; /** - * @psalm-assert \DOM\Element $node + * @psalm-assert \Dom\Element $node * @throws AssertException */ -function assert_element(mixed $node): \DOM\Element +function assert_element(mixed $node): \Dom\Element { - return instance_of(\DOM\Element::class)->assert($node); + return instance_of(\Dom\Element::class)->assert($node); } diff --git a/src/Xml/Dom/Builder/Builder.php b/src/Xml/Dom/Builder/Builder.php index 06801d1..5bee80c 100644 --- a/src/Xml/Dom/Builder/Builder.php +++ b/src/Xml/Dom/Builder/Builder.php @@ -4,9 +4,9 @@ namespace VeeWee\Xml\Dom\Builder; -use \DOM\Node; +use \Dom\Node; interface Builder { - public function __invoke(\DOM\Node $node): \DOM\Node; + public function __invoke(\Dom\Node $node): \Dom\Node; } diff --git a/src/Xml/Dom/Builder/attribute.php b/src/Xml/Dom/Builder/attribute.php index d7c1a02..6ff19ca 100644 --- a/src/Xml/Dom/Builder/attribute.php +++ b/src/Xml/Dom/Builder/attribute.php @@ -5,14 +5,14 @@ namespace VeeWee\Xml\Dom\Builder; use Closure; -use \DOM\Element; +use \Dom\Element; /** - * @return Closure(\DOM\Element): \DOM\Element + * @return Closure(\Dom\Element): \Dom\Element */ function attribute(string $name, string $value): Closure { - return static function (\DOM\Element $node) use ($name, $value): \DOM\Element { + return static function (\Dom\Element $node) use ($name, $value): \Dom\Element { $node->setAttribute($name, $value); return $node; diff --git a/src/Xml/Dom/Builder/attributes.php b/src/Xml/Dom/Builder/attributes.php index e52f153..4c7d1e5 100644 --- a/src/Xml/Dom/Builder/attributes.php +++ b/src/Xml/Dom/Builder/attributes.php @@ -5,19 +5,19 @@ namespace VeeWee\Xml\Dom\Builder; use Closure; -use \DOM\Element; +use \Dom\Element; use function Psl\Iter\reduce_with_keys; /** * @param array $attributes - * @return Closure(\DOM\Element): \DOM\Element + * @return Closure(\Dom\Element): \Dom\Element */ function attributes(array $attributes): Closure { - return static function (\DOM\Element $node) use ($attributes): \DOM\Element { + return static function (\Dom\Element $node) use ($attributes): \Dom\Element { return reduce_with_keys( $attributes, - static fn (\DOM\Element $node, string $name, string $value) + static fn (\Dom\Element $node, string $name, string $value) => attribute($name, $value)($node), $node ); diff --git a/src/Xml/Dom/Builder/cdata.php b/src/Xml/Dom/Builder/cdata.php index a8e2085..fa71380 100644 --- a/src/Xml/Dom/Builder/cdata.php +++ b/src/Xml/Dom/Builder/cdata.php @@ -5,20 +5,20 @@ namespace VeeWee\Xml\Dom\Builder; use Closure; -use \DOM\CDATASection; -use \DOM\Node; +use \Dom\CDATASection; +use \Dom\Node; use function VeeWee\Xml\Dom\Assert\assert_cdata; use function VeeWee\Xml\Dom\Locator\Node\detect_document; use function VeeWee\Xml\Internal\configure; /** - * @param list $configurators + * @param list $configurators * - * @return Closure(\DOM\Node): \DOM\CDATASection + * @return Closure(\Dom\Node): \Dom\CDATASection */ function cdata(string $data, ...$configurators): Closure { - return static function (\DOM\Node $node) use ($data, $configurators): \DOM\CDATASection { + return static function (\Dom\Node $node) use ($data, $configurators): \Dom\CDATASection { $document = detect_document($node); return assert_cdata( diff --git a/src/Xml/Dom/Builder/children.php b/src/Xml/Dom/Builder/children.php index 8d35131..c30ec1e 100644 --- a/src/Xml/Dom/Builder/children.php +++ b/src/Xml/Dom/Builder/children.php @@ -5,18 +5,18 @@ namespace VeeWee\Xml\Dom\Builder; use Closure; -use \DOM\Node; +use \Dom\Node; /** - * @template T of \DOM\Node + * @template T of \Dom\Node * - * @param list $builders + * @param list $builders * * @return Closure(T): T */ function children(callable ...$builders): Closure { - return static function (\DOM\Node $node) use ($builders): \DOM\Node { + return static function (\Dom\Node $node) use ($builders): \Dom\Node { foreach ($builders as $builder) { $node->appendChild($builder($node)); } diff --git a/src/Xml/Dom/Builder/element.php b/src/Xml/Dom/Builder/element.php index 0c8e98d..7ccab1c 100644 --- a/src/Xml/Dom/Builder/element.php +++ b/src/Xml/Dom/Builder/element.php @@ -5,20 +5,20 @@ namespace VeeWee\Xml\Dom\Builder; use Closure; -use \DOM\Element; -use \DOM\Node; +use \Dom\Element; +use \Dom\Node; use function VeeWee\Xml\Dom\Assert\assert_element; use function VeeWee\Xml\Dom\Locator\Node\detect_document; use function VeeWee\Xml\Internal\configure; /** - * @param list $configurators + * @param list $configurators * - * @return Closure(\DOM\Node): \DOM\Element + * @return Closure(\Dom\Node): \Dom\Element */ function element(string $name, callable ...$configurators): Closure { - return static function (\DOM\Node $node) use ($name, $configurators): \DOM\Element { + return static function (\Dom\Node $node) use ($name, $configurators): \Dom\Element { $document = detect_document($node); return assert_element( diff --git a/src/Xml/Dom/Builder/escaped_value.php b/src/Xml/Dom/Builder/escaped_value.php index 1529770..7e4d440 100644 --- a/src/Xml/Dom/Builder/escaped_value.php +++ b/src/Xml/Dom/Builder/escaped_value.php @@ -7,11 +7,11 @@ use Closure; /** - * @return Closure(\DOM\Element): \DOM\Element + * @return Closure(\Dom\Element): \Dom\Element */ function escaped_value(string $value): Closure { - return static function (\DOM\Element $node) use ($value): \DOM\Element { + return static function (\Dom\Element $node) use ($value): \Dom\Element { return value(htmlspecialchars($value, ENT_XML1|ENT_QUOTES))($node); }; } diff --git a/src/Xml/Dom/Builder/namespaced_attribute.php b/src/Xml/Dom/Builder/namespaced_attribute.php index b4d6c6a..0de1d6e 100644 --- a/src/Xml/Dom/Builder/namespaced_attribute.php +++ b/src/Xml/Dom/Builder/namespaced_attribute.php @@ -5,15 +5,15 @@ namespace VeeWee\Xml\Dom\Builder; use Closure; -use \DOM\Element; +use \Dom\Element; use function VeeWee\Xml\Assertion\assert_strict_prefixed_name; /** - * @return Closure(\DOM\Element): \DOM\Element + * @return Closure(\Dom\Element): \Dom\Element */ function namespaced_attribute(string $namespace, string $qualifiedName, string $value): Closure { - return static function (\DOM\Element $node) use ($namespace, $qualifiedName, $value): \DOM\Element { + return static function (\Dom\Element $node) use ($namespace, $qualifiedName, $value): \Dom\Element { assert_strict_prefixed_name($qualifiedName); $node->setAttributeNS($namespace, $qualifiedName, $value); diff --git a/src/Xml/Dom/Builder/namespaced_attributes.php b/src/Xml/Dom/Builder/namespaced_attributes.php index 27b3364..fe3e2e4 100644 --- a/src/Xml/Dom/Builder/namespaced_attributes.php +++ b/src/Xml/Dom/Builder/namespaced_attributes.php @@ -5,19 +5,19 @@ namespace VeeWee\Xml\Dom\Builder; use Closure; -use \DOM\Element; +use \Dom\Element; use function Psl\Iter\reduce_with_keys; /** * @param array $attributes - * @return Closure(\DOM\Element): \DOM\Element + * @return Closure(\Dom\Element): \Dom\Element */ function namespaced_attributes(string $namespace, array $attributes): Closure { - return static function (\DOM\Element $node) use ($namespace, $attributes): \DOM\Element { + return static function (\Dom\Element $node) use ($namespace, $attributes): \Dom\Element { return reduce_with_keys( $attributes, - static fn (\DOM\Element $node, string $name, string $value) + static fn (\Dom\Element $node, string $name, string $value) => namespaced_attribute($namespace, $name, $value)($node), $node ); diff --git a/src/Xml/Dom/Builder/namespaced_element.php b/src/Xml/Dom/Builder/namespaced_element.php index ed22f65..ebd5f49 100644 --- a/src/Xml/Dom/Builder/namespaced_element.php +++ b/src/Xml/Dom/Builder/namespaced_element.php @@ -5,20 +5,20 @@ namespace VeeWee\Xml\Dom\Builder; use Closure; -use \DOM\Element; -use \DOM\Node; +use \Dom\Element; +use \Dom\Node; use function VeeWee\Xml\Dom\Assert\assert_element; use function VeeWee\Xml\Dom\Locator\Node\detect_document; use function VeeWee\Xml\Internal\configure; /** - * @param list $configurators + * @param list $configurators * - * @return Closure(\DOM\Node): \DOM\Element + * @return Closure(\Dom\Node): \Dom\Element */ function namespaced_element(string $namespace, string $qualifiedName, callable ...$configurators): Closure { - return static function (\DOM\Node $node) use ($namespace, $qualifiedName, $configurators): \DOM\Element { + return static function (\Dom\Node $node) use ($namespace, $qualifiedName, $configurators): \Dom\Element { $document = detect_document($node); return assert_element( diff --git a/src/Xml/Dom/Builder/nodes.php b/src/Xml/Dom/Builder/nodes.php index bdc4097..2904243 100644 --- a/src/Xml/Dom/Builder/nodes.php +++ b/src/Xml/Dom/Builder/nodes.php @@ -5,30 +5,30 @@ namespace VeeWee\Xml\Dom\Builder; use Closure; -use \DOM\XMLDocument; -use \DOM\Node; +use \Dom\XMLDocument; +use \Dom\Node; use function is_array; use function Psl\Iter\reduce; use function VeeWee\Xml\Dom\Locator\Node\detect_document; /** - * @param list|\DOM\Node)> $builders + * @param list|\Dom\Node)> $builders * - * @return Closure(\DOM\XMLDocument): list<\DOM\Node> + * @return Closure(\Dom\XMLDocument): list<\Dom\Node> */ function nodes(callable ... $builders): Closure { return /** - * @return list<\DOM\Node> + * @return list<\Dom\Node> */ - static fn (\DOM\Node $node): array + static fn (\Dom\Node $node): array => reduce( $builders, /** - * @param list<\DOM\Node> $builds - * @param callable(\DOM\XMLDocument): (\DOM\Node|list<\DOM\Node>) $builder - * @return list<\DOM\Node> + * @param list<\Dom\Node> $builds + * @param callable(\Dom\XMLDocument): (\Dom\Node|list<\Dom\Node>) $builder + * @return list<\Dom\Node> */ static function (array $builds, callable $builder) use ($node): array { $result = $builder(detect_document($node)); diff --git a/src/Xml/Dom/Builder/value.php b/src/Xml/Dom/Builder/value.php index 50c7167..edde041 100644 --- a/src/Xml/Dom/Builder/value.php +++ b/src/Xml/Dom/Builder/value.php @@ -5,14 +5,14 @@ namespace VeeWee\Xml\Dom\Builder; use Closure; -use \DOM\Element; +use \Dom\Element; /** - * @return Closure(\DOM\Element): \DOM\Element + * @return Closure(\Dom\Element): \Dom\Element */ function value(string $value): Closure { - return static function (\DOM\Element $node) use ($value): \DOM\Element { + return static function (\Dom\Element $node) use ($value): \Dom\Element { $node->substitutedNodeValue = $value; return $node; diff --git a/src/Xml/Dom/Builder/xmlns_attribute.php b/src/Xml/Dom/Builder/xmlns_attribute.php index 6371364..9f37c2d 100644 --- a/src/Xml/Dom/Builder/xmlns_attribute.php +++ b/src/Xml/Dom/Builder/xmlns_attribute.php @@ -5,16 +5,16 @@ namespace VeeWee\Xml\Dom\Builder; use Closure; -use \DOM\Element; +use \Dom\Element; use VeeWee\Xml\Xmlns\Xmlns; use function VeeWee\Xml\Assertion\assert_strict_prefixed_name; /** - * @return Closure(\DOM\Element): \DOM\Element + * @return Closure(\Dom\Element): \Dom\Element */ function xmlns_attribute(string $prefix, string $namespaceURI): Closure { - return static function (\DOM\Element $node) use ($prefix, $namespaceURI): \DOM\Element { + return static function (\Dom\Element $node) use ($prefix, $namespaceURI): \Dom\Element { $prefixed = 'xmlns:'.$prefix; assert_strict_prefixed_name($prefixed); diff --git a/src/Xml/Dom/Builder/xmlns_attributes.php b/src/Xml/Dom/Builder/xmlns_attributes.php index 4634b2c..a3e5895 100644 --- a/src/Xml/Dom/Builder/xmlns_attributes.php +++ b/src/Xml/Dom/Builder/xmlns_attributes.php @@ -5,19 +5,19 @@ namespace VeeWee\Xml\Dom\Builder; use Closure; -use \DOM\Element; +use \Dom\Element; use function Psl\Iter\reduce_with_keys; /** * @param array $attributes - A map of namespace prefix with namespace URI - * @return Closure(\DOM\Element): \DOM\Element + * @return Closure(\Dom\Element): \Dom\Element */ function xmlns_attributes(array $attributes): Closure { - return static function (\DOM\Element $node) use ($attributes): \DOM\Element { + return static function (\Dom\Element $node) use ($attributes): \Dom\Element { return reduce_with_keys( $attributes, - static fn (\DOM\Element $node, string $name, string $value) + static fn (\Dom\Element $node, string $name, string $value) => xmlns_attribute($name, $value)($node), $node ); diff --git a/src/Xml/Dom/Collection/NodeList.php b/src/Xml/Dom/Collection/NodeList.php index a78ddc1..52de224 100644 --- a/src/Xml/Dom/Collection/NodeList.php +++ b/src/Xml/Dom/Collection/NodeList.php @@ -5,10 +5,10 @@ namespace VeeWee\Xml\Dom\Collection; use Countable; -use \DOM\Element; -use \DOM\Node; -use \DOM\NodeList as DOMNodeList; -use \DOM\XPath as DOMXPath; +use \Dom\Element; +use \Dom\Node; +use \Dom\NodeList as DOMNodeList; +use \Dom\XPath as DOMXPath; use Generator; use InvalidArgumentException; use IteratorAggregate; @@ -29,7 +29,7 @@ use function VeeWee\Xml\Dom\Locator\Element\siblings; /** - * @template T of \DOM\Node + * @template T of \Dom\Node * @implements IteratorAggregate */ final class NodeList implements Countable, IteratorAggregate @@ -49,7 +49,7 @@ public function __construct(...$nodes) } /** - * @template X of \DOM\Node + * @template X of \Dom\Node * @return self * * @psalm-suppress InvalidReturnType, InvalidReturnStatement - It is empty alright! @@ -60,16 +60,16 @@ public static function empty(): self } /** - * @param \DOM\HTMLCollection $list - * @return NodeList<\DOM\Element> + * @param \Dom\HTMLCollection $list + * @return NodeList<\Dom\Element> */ - public static function fromDOMHTMLCollection(\DOM\HTMLCollection $list): self + public static function fromDOMHTMLCollection(\Dom\HTMLCollection $list): self { return new self(...values($list->getIterator())); } /** - * @template X of \DOM\Node + * @template X of \Dom\Node * @param DOMNodeList $list * @return NodeList */ @@ -79,7 +79,7 @@ public static function fromDOMNodeList(DOMNodeList $list): self } /** - * @template X of \DOM\Node + * @template X of \Dom\Node * @param class-string $type * @return NodeList * @throws InvalidArgumentException @@ -145,7 +145,7 @@ public function forEach(callable $mapper): void } /** - * @template X of \DOM\Node + * @template X of \Dom\Node * @param callable(T): iterable $mapper * * @return NodeList @@ -195,16 +195,16 @@ public function reduce(callable $reducer, mixed $initial): mixed /** * @param list $configurators * @throws RuntimeException - * @return NodeList<\DOM\Node> + * @return NodeList<\Dom\Node> */ public function query(string $xpath, callable ... $configurators): self { return $this->detect( /** * @param T $node - * @return NodeList<\DOM\Node> + * @return NodeList<\Dom\Node> */ - static fn (\DOM\Node $node): NodeList + static fn (\Dom\Node $node): NodeList => Xpath::fromUnsafeNode($node, ...$configurators)->query($xpath, $node) ); } @@ -218,7 +218,7 @@ public function query(string $xpath, callable ... $configurators): self public function evaluate(string $expression, TypeInterface $type, callable ... $configurators): array { return $this->map( - static fn (\DOM\Node $node): mixed + static fn (\Dom\Node $node): mixed => Xpath::fromUnsafeNode($node, ...$configurators)->evaluate($expression, $type, $node) ); } @@ -272,46 +272,46 @@ public function expectLast(string $message = '') } /** - * @return NodeList<\DOM\Element> + * @return NodeList<\Dom\Element> */ public function siblings(): self { return $this->detect( /** - * @return iterable<\DOM\Element> + * @return iterable<\Dom\Element> */ - static fn (\DOM\Node $node): NodeList => siblings($node) + static fn (\Dom\Node $node): NodeList => siblings($node) ); } /** - * @return NodeList<\DOM\Element> + * @return NodeList<\Dom\Element> */ public function ancestors(): self { return $this->detect( /** - * @return iterable<\DOM\Element> + * @return iterable<\Dom\Element> */ - static fn (\DOM\Node $node): NodeList => ancestors($node) + static fn (\Dom\Node $node): NodeList => ancestors($node) ); } /** - * @return NodeList<\DOM\Element> + * @return NodeList<\Dom\Element> */ public function children(): self { return $this->detect( /** - * @return iterable<\DOM\Element> + * @return iterable<\Dom\Element> */ - static fn (\DOM\Node $node): NodeList => children($node) + static fn (\Dom\Node $node): NodeList => children($node) ); } /** - * @template X of \DOM\Node + * @template X of \Dom\Node * @param class-string $type * @return NodeList * @throws InvalidArgumentException diff --git a/src/Xml/Dom/Configurator/Configurator.php b/src/Xml/Dom/Configurator/Configurator.php index 253f2a2..1f0cc79 100644 --- a/src/Xml/Dom/Configurator/Configurator.php +++ b/src/Xml/Dom/Configurator/Configurator.php @@ -4,9 +4,9 @@ namespace VeeWee\Xml\Dom\Configurator; -use \DOM\XMLDocument; +use \Dom\XMLDocument; interface Configurator { - public function __invoke(\DOM\XMLDocument $document): \DOM\XMLDocument; + public function __invoke(\Dom\XMLDocument $document): \Dom\XMLDocument; } diff --git a/src/Xml/Dom/Configurator/canonicalize.php b/src/Xml/Dom/Configurator/canonicalize.php index 15075b6..cb4b1a2 100644 --- a/src/Xml/Dom/Configurator/canonicalize.php +++ b/src/Xml/Dom/Configurator/canonicalize.php @@ -5,7 +5,7 @@ namespace VeeWee\Xml\Dom\Configurator; use Closure; -use \DOM\XMLDocument as DOMDocument; +use \Dom\XMLDocument as DOMDocument; use VeeWee\Xml\Dom\Document; use function Psl\Type\non_empty_string; use function VeeWee\Xml\Dom\Loader\xml_string_loader; diff --git a/src/Xml/Dom/Configurator/comparable.php b/src/Xml/Dom/Configurator/comparable.php index 07e4044..5c24aca 100644 --- a/src/Xml/Dom/Configurator/comparable.php +++ b/src/Xml/Dom/Configurator/comparable.php @@ -5,12 +5,12 @@ namespace VeeWee\Xml\Dom\Configurator; use Closure; -use \DOM\XMLDocument; +use \Dom\XMLDocument; use VeeWee\Xml\Dom\Traverser\Visitor\SortAttributes; use function VeeWee\Xml\Internal\configure; /** - * @return Closure(\DOM\XMLDocument): \DOM\XMLDocument + * @return Closure(\Dom\XMLDocument): \Dom\XMLDocument */ function comparable(): Closure { diff --git a/src/Xml/Dom/Configurator/document_uri.php b/src/Xml/Dom/Configurator/document_uri.php index 7510866..8c39e0e 100644 --- a/src/Xml/Dom/Configurator/document_uri.php +++ b/src/Xml/Dom/Configurator/document_uri.php @@ -5,15 +5,15 @@ namespace VeeWee\Xml\Dom\Configurator; use Closure; -use \DOM\XMLDocument; +use \Dom\XMLDocument; /** * @param non-empty-string $documentUri - * @return Closure(\DOM\XMLDocument): \DOM\XMLDocument + * @return Closure(\Dom\XMLDocument): \Dom\XMLDocument */ function document_uri(string $documentUri): Closure { - return static function (\DOM\XMLDocument $document) use ($documentUri) : \DOM\XMLDocument { + return static function (\Dom\XMLDocument $document) use ($documentUri) : \Dom\XMLDocument { $document->documentURI = $documentUri; return $document; diff --git a/src/Xml/Dom/Configurator/normalize.php b/src/Xml/Dom/Configurator/normalize.php index 0ee7740..25c41e1 100644 --- a/src/Xml/Dom/Configurator/normalize.php +++ b/src/Xml/Dom/Configurator/normalize.php @@ -5,14 +5,14 @@ namespace VeeWee\Xml\Dom\Configurator; use Closure; -use \DOM\XMLDocument; +use \Dom\XMLDocument; /** - * @return Closure(\DOM\XMLDocument): \DOM\XMLDocument + * @return Closure(\Dom\XMLDocument): \Dom\XMLDocument */ function normalize(): Closure { - return static function (\DOM\XMLDocument $document): \DOM\XMLDocument { + return static function (\Dom\XMLDocument $document): \Dom\XMLDocument { $document->normalize(); return $document; diff --git a/src/Xml/Dom/Configurator/optimize_namespaces.php b/src/Xml/Dom/Configurator/optimize_namespaces.php index 312fc27..9cba72d 100644 --- a/src/Xml/Dom/Configurator/optimize_namespaces.php +++ b/src/Xml/Dom/Configurator/optimize_namespaces.php @@ -5,15 +5,15 @@ namespace VeeWee\Xml\Dom\Configurator; use Closure; -use \DOM\XMLDocument; +use \Dom\XMLDocument; use function VeeWee\Xml\Dom\Manipulator\Document\optimize_namespaces as optimize_namespaces_manipulator; /** - * @return Closure(\DOM\XMLDocument): \DOM\XMLDocument + * @return Closure(\Dom\XMLDocument): \Dom\XMLDocument */ function optimize_namespaces(string $prefix = 'ns'): Closure { - return static function (\DOM\XMLDocument $document) use ($prefix) : \DOM\XMLDocument { + return static function (\Dom\XMLDocument $document) use ($prefix) : \Dom\XMLDocument { optimize_namespaces_manipulator($document, $prefix); return $document; diff --git a/src/Xml/Dom/Configurator/pretty_print.php b/src/Xml/Dom/Configurator/pretty_print.php index f149301..462c104 100644 --- a/src/Xml/Dom/Configurator/pretty_print.php +++ b/src/Xml/Dom/Configurator/pretty_print.php @@ -9,11 +9,11 @@ use function VeeWee\Xml\Dom\Loader\xml_string_loader; /** - * @return Closure(\DOM\XMLDocument): \DOM\XMLDocument + * @return Closure(\Dom\XMLDocument): \Dom\XMLDocument */ function pretty_print(): Closure { - return static function (\DOM\XMLDocument $document): \DOM\XMLDocument { + return static function (\Dom\XMLDocument $document): \Dom\XMLDocument { $trimmed = Document::fromLoader( xml_string_loader( Document::fromUnsafeDocument($document)->toXmlString(), diff --git a/src/Xml/Dom/Configurator/traverse.php b/src/Xml/Dom/Configurator/traverse.php index 032a433..5abde59 100644 --- a/src/Xml/Dom/Configurator/traverse.php +++ b/src/Xml/Dom/Configurator/traverse.php @@ -5,7 +5,7 @@ namespace VeeWee\Xml\Dom\Configurator; use Closure; -use \DOM\XMLDocument as DOMDocument; +use \Dom\XMLDocument as DOMDocument; use VeeWee\Xml\Dom\Document; use VeeWee\Xml\Dom\Traverser\Visitor; diff --git a/src/Xml/Dom/Configurator/trim_spaces.php b/src/Xml/Dom/Configurator/trim_spaces.php index ca22522..e58d558 100644 --- a/src/Xml/Dom/Configurator/trim_spaces.php +++ b/src/Xml/Dom/Configurator/trim_spaces.php @@ -5,16 +5,16 @@ namespace VeeWee\Xml\Dom\Configurator; use Closure; -use \DOM\XMLDocument; +use \Dom\XMLDocument; use VeeWee\Xml\Dom\Document; use function VeeWee\Xml\Dom\Loader\xml_string_loader; /** - * @return Closure(\DOM\XMLDocument): \DOM\XMLDocument + * @return Closure(\Dom\XMLDocument): \Dom\XMLDocument */ function trim_spaces(): Closure { - return static function (\DOM\XMLDocument $document): \DOM\XMLDocument { + return static function (\Dom\XMLDocument $document): \Dom\XMLDocument { $trimmed = Document::fromLoader( xml_string_loader( Document::fromUnsafeDocument($document)->toXmlString(), diff --git a/src/Xml/Dom/Configurator/utf8.php b/src/Xml/Dom/Configurator/utf8.php index afdeaa2..708f7c5 100644 --- a/src/Xml/Dom/Configurator/utf8.php +++ b/src/Xml/Dom/Configurator/utf8.php @@ -5,14 +5,14 @@ namespace VeeWee\Xml\Dom\Configurator; use Closure; -use \DOM\XMLDocument; +use \Dom\XMLDocument; /** - * @return Closure(\DOM\XMLDocument): \DOM\XMLDocument + * @return Closure(\Dom\XMLDocument): \Dom\XMLDocument */ function utf8(): Closure { - return static function (\DOM\XMLDocument $document): \DOM\XMLDocument { + return static function (\Dom\XMLDocument $document): \Dom\XMLDocument { $document->charset = 'UTF-8'; return $document; diff --git a/src/Xml/Dom/Configurator/validator.php b/src/Xml/Dom/Configurator/validator.php index af1b70e..5eb5966 100644 --- a/src/Xml/Dom/Configurator/validator.php +++ b/src/Xml/Dom/Configurator/validator.php @@ -5,16 +5,16 @@ namespace VeeWee\Xml\Dom\Configurator; use Closure; -use \DOM\XMLDocument; +use \Dom\XMLDocument; use VeeWee\Xml\ErrorHandling\Issue\Issue; use VeeWee\Xml\ErrorHandling\Issue\IssueCollection; use VeeWee\Xml\ErrorHandling\Issue\Level; use VeeWee\Xml\Exception\RuntimeException; /** - * @param callable(\DOM\XMLDocument): IssueCollection $validator + * @param callable(\Dom\XMLDocument): IssueCollection $validator * - * @return Closure(\DOM\XMLDocument): \DOM\XMLDocument + * @return Closure(\Dom\XMLDocument): \Dom\XMLDocument */ function validator(callable $validator, ?Level $minimumLevel = null): Closure { @@ -24,7 +24,7 @@ function validator(callable $validator, ?Level $minimumLevel = null): Closure /** * @throws RuntimeException */ - static function (\DOM\XMLDocument $document) use ($validator, $minimumLevel): \DOM\XMLDocument { + static function (\Dom\XMLDocument $document) use ($validator, $minimumLevel): \Dom\XMLDocument { $issues = $validator($document) ->filter(static fn (Issue $issue): bool => $issue->level()->value() >= $minimumLevel->value()); diff --git a/src/Xml/Dom/Document.php b/src/Xml/Dom/Document.php index bb6b096..d029f00 100644 --- a/src/Xml/Dom/Document.php +++ b/src/Xml/Dom/Document.php @@ -5,10 +5,10 @@ namespace VeeWee\Xml\Dom; use Closure; -use \DOM\XMLDocument; -use \DOM\Element; -use \DOM\Node; -use \DOM\XPath as DOMXPath; +use \Dom\XMLDocument; +use \Dom\Element; +use \Dom\Node; +use \Dom\XPath as DOMXPath; use VeeWee\Xml\Dom\Traverser\Traverser; use VeeWee\Xml\Dom\Traverser\Visitor; use VeeWee\Xml\ErrorHandling\Issue\IssueCollection; @@ -82,7 +82,7 @@ public static function fromXmlString(string $xml, callable ...$configurators): s * * @throws RuntimeException */ - public static function fromXmlNode(\DOM\Node $node, callable ...$configurators): self + public static function fromXmlNode(\Dom\Node $node, callable ...$configurators): self { return self::fromLoader(Loader\xml_node_loader($node), ...$configurators); } @@ -113,7 +113,7 @@ public function locate(callable $locator) return $locator($this->document); } - public function locateDocumentElement(): \DOM\Element + public function locateDocumentElement(): \Dom\Element { return $this->locate(Locator\document_element()); } @@ -131,9 +131,9 @@ public function manipulate(callable $manipulator): self } /** - * @param list|\DOM\Node)> $builders + * @param list|\Dom\Node)> $builders * - * @return list<\DOM\Node> + * @return list<\Dom\Node> */ public function build(callable ... $builders): array { @@ -183,7 +183,7 @@ public function reconfigure(callable ... $configurators): self /** * @no-named-arguments */ - public function traverse(Visitor ... $visitors): \DOM\Node + public function traverse(Visitor ... $visitors): \Dom\Node { $traverser = new Traverser(...$visitors); return $traverser->traverse($this->map(document_element())); @@ -208,7 +208,7 @@ public function stringifyDocumentElement(): string /** * @return non-empty-string */ - public function stringifyNode(\DOM\Node $node): string + public function stringifyNode(\Dom\Node $node): string { return xml_string()($node); } diff --git a/src/Xml/Dom/Loader/Loader.php b/src/Xml/Dom/Loader/Loader.php index f0c34c9..73e1eb7 100644 --- a/src/Xml/Dom/Loader/Loader.php +++ b/src/Xml/Dom/Loader/Loader.php @@ -4,7 +4,7 @@ namespace VeeWee\Xml\Dom\Loader; -use \DOM\XMLDocument; +use \Dom\XMLDocument; interface Loader { diff --git a/src/Xml/Dom/Loader/xml_file_loader.php b/src/Xml/Dom/Loader/xml_file_loader.php index 4d71911..1baff7c 100644 --- a/src/Xml/Dom/Loader/xml_file_loader.php +++ b/src/Xml/Dom/Loader/xml_file_loader.php @@ -5,7 +5,7 @@ namespace VeeWee\Xml\Dom\Loader; use Closure; -use DOM\XMLDocument; +use Dom\XMLDocument; use Webmozart\Assert\Assert; use function VeeWee\Xml\ErrorHandling\disallow_issues; diff --git a/src/Xml/Dom/Loader/xml_node_loader.php b/src/Xml/Dom/Loader/xml_node_loader.php index 9222cd1..760cf85 100644 --- a/src/Xml/Dom/Loader/xml_node_loader.php +++ b/src/Xml/Dom/Loader/xml_node_loader.php @@ -5,15 +5,15 @@ namespace VeeWee\Xml\Dom\Loader; use Closure; -use \DOM\XMLDocument; -use \DOM\Node; +use \Dom\XMLDocument; +use \Dom\Node; use function VeeWee\Xml\Dom\Manipulator\Node\append_external_node; use function VeeWee\Xml\ErrorHandling\disallow_issues; /** * @return Closure(): XMLDocument */ -function xml_node_loader(\DOM\Node $importedNode): Closure +function xml_node_loader(\Dom\Node $importedNode): Closure { return static fn () => disallow_issues(static function () use ($importedNode): XMLDocument { $document = XMLDocument::createEmpty(); diff --git a/src/Xml/Dom/Loader/xml_string_loader.php b/src/Xml/Dom/Loader/xml_string_loader.php index c7dd097..b53e746 100644 --- a/src/Xml/Dom/Loader/xml_string_loader.php +++ b/src/Xml/Dom/Loader/xml_string_loader.php @@ -5,7 +5,7 @@ namespace VeeWee\Xml\Dom\Loader; use Closure; -use \DOM\XMLDocument; +use \Dom\XMLDocument; use function VeeWee\Xml\ErrorHandling\disallow_issues; /** diff --git a/src/Xml/Dom/Locator/Attribute/attributes_list.php b/src/Xml/Dom/Locator/Attribute/attributes_list.php index a8221ad..93788ec 100644 --- a/src/Xml/Dom/Locator/Attribute/attributes_list.php +++ b/src/Xml/Dom/Locator/Attribute/attributes_list.php @@ -4,16 +4,16 @@ namespace VeeWee\Xml\Dom\Locator\Attribute; -use \DOM\Attr; -use \DOM\Node; +use \Dom\Attr; +use \Dom\Node; use VeeWee\Xml\Dom\Collection\NodeList; use function Psl\Vec\values; use function VeeWee\Xml\Dom\Predicate\is_element; /** - * @return NodeList<\DOM\Attr> + * @return NodeList<\Dom\Attr> */ -function attributes_list(\DOM\Node $node): NodeList +function attributes_list(\Dom\Node $node): NodeList { if (!is_element($node)) { return NodeList::empty(); diff --git a/src/Xml/Dom/Locator/Attribute/xmlns_attributes_list.php b/src/Xml/Dom/Locator/Attribute/xmlns_attributes_list.php index 5ac1d14..64ff4e2 100644 --- a/src/Xml/Dom/Locator/Attribute/xmlns_attributes_list.php +++ b/src/Xml/Dom/Locator/Attribute/xmlns_attributes_list.php @@ -9,11 +9,11 @@ use function VeeWee\Xml\Dom\Predicate\is_xmlns_attribute; /** - * @return NodeList<\DOM\Attr> + * @return NodeList<\Dom\Attr> * @throws RuntimeException */ -function xmlns_attributes_list(\DOM\Node $node): NodeList +function xmlns_attributes_list(\Dom\Node $node): NodeList { return attributes_list($node) - ->filter(static fn (\DOM\Attr $attribute): bool => is_xmlns_attribute($attribute)); + ->filter(static fn (\Dom\Attr $attribute): bool => is_xmlns_attribute($attribute)); } diff --git a/src/Xml/Dom/Locator/Element/ancestors.php b/src/Xml/Dom/Locator/Element/ancestors.php index 3787aa5..5292249 100644 --- a/src/Xml/Dom/Locator/Element/ancestors.php +++ b/src/Xml/Dom/Locator/Element/ancestors.php @@ -4,23 +4,23 @@ namespace VeeWee\Xml\Dom\Locator\Element; -use \DOM\Element; -use \DOM\Node; +use \Dom\Element; +use \Dom\Node; use Generator; use VeeWee\Xml\Dom\Collection\NodeList; use function VeeWee\Xml\Dom\Predicate\is_element; /** - * @return NodeList<\DOM\Element> + * @return NodeList<\Dom\Element> */ -function ancestors(\DOM\Node $node): NodeList +function ancestors(\Dom\Node $node): NodeList { return new NodeList( ...( /** - * @return Generator + * @return Generator */ - static function (\DOM\Node $next) { + static function (\Dom\Node $next) { while (($parent = $next->parentNode) !== null) { if (is_element($parent)) { yield $parent; diff --git a/src/Xml/Dom/Locator/Element/children.php b/src/Xml/Dom/Locator/Element/children.php index 8b7a9fb..e5e7715 100644 --- a/src/Xml/Dom/Locator/Element/children.php +++ b/src/Xml/Dom/Locator/Element/children.php @@ -4,21 +4,21 @@ namespace VeeWee\Xml\Dom\Locator\Element; -use \DOM\Element; -use \DOM\Node; +use \Dom\Element; +use \Dom\Node; use VeeWee\Xml\Dom\Collection\NodeList; use function Psl\Vec\filter; use function VeeWee\Xml\Dom\Predicate\is_element; /** - * @return NodeList<\DOM\Element> + * @return NodeList<\Dom\Element> */ -function children(\DOM\Node $node): NodeList +function children(\Dom\Node $node): NodeList { - /** @var list<\DOM\Element> $children */ + /** @var list<\Dom\Element> $children */ $children = filter( $node->childNodes, - static fn (\DOM\Node $node): bool => is_element($node) + static fn (\Dom\Node $node): bool => is_element($node) ); return new NodeList(...$children); diff --git a/src/Xml/Dom/Locator/Element/locate_by_namespaced_tag_name.php b/src/Xml/Dom/Locator/Element/locate_by_namespaced_tag_name.php index 55dd221..0cf9c4e 100644 --- a/src/Xml/Dom/Locator/Element/locate_by_namespaced_tag_name.php +++ b/src/Xml/Dom/Locator/Element/locate_by_namespaced_tag_name.php @@ -4,13 +4,13 @@ namespace VeeWee\Xml\Dom\Locator\Element; -use \DOM\Element; +use \Dom\Element; use VeeWee\Xml\Dom\Collection\NodeList; /** - * @return NodeList<\DOM\Element> + * @return NodeList<\Dom\Element> */ -function locate_by_namespaced_tag_name(\DOM\Element $node, string $namespace, string $localTagName): NodeList +function locate_by_namespaced_tag_name(\Dom\Element $node, string $namespace, string $localTagName): NodeList { return NodeList::fromDOMHTMLCollection($node->getElementsByTagNameNS($namespace, $localTagName)); } diff --git a/src/Xml/Dom/Locator/Element/locate_by_tag_name.php b/src/Xml/Dom/Locator/Element/locate_by_tag_name.php index f299118..d740f65 100644 --- a/src/Xml/Dom/Locator/Element/locate_by_tag_name.php +++ b/src/Xml/Dom/Locator/Element/locate_by_tag_name.php @@ -4,13 +4,13 @@ namespace VeeWee\Xml\Dom\Locator\Element; -use \DOM\Element; +use \Dom\Element; use VeeWee\Xml\Dom\Collection\NodeList; /** - * @return NodeList<\DOM\Element> + * @return NodeList<\Dom\Element> */ -function locate_by_tag_name(\DOM\Element $node, string $tag): NodeList +function locate_by_tag_name(\Dom\Element $node, string $tag): NodeList { return NodeList::fromDOMHTMLCollection($node->getElementsByTagName($tag)); } diff --git a/src/Xml/Dom/Locator/Element/parent_element.php b/src/Xml/Dom/Locator/Element/parent_element.php index 4e6ebcd..fb9fbd7 100644 --- a/src/Xml/Dom/Locator/Element/parent_element.php +++ b/src/Xml/Dom/Locator/Element/parent_element.php @@ -4,15 +4,15 @@ namespace VeeWee\Xml\Dom\Locator\Element; -use \DOM\Element; -use \DOM\Node; +use \Dom\Element; +use \Dom\Node; use VeeWee\Xml\Exception\RuntimeException; use function VeeWee\Xml\Dom\Predicate\is_element; /** * @throws RuntimeException */ -function parent_element(\DOM\Node $child): \DOM\Element +function parent_element(\Dom\Node $child): \Dom\Element { $parent = $child->parentNode; if (!$parent|| !is_element($parent)) { diff --git a/src/Xml/Dom/Locator/Element/siblings.php b/src/Xml/Dom/Locator/Element/siblings.php index 9cba8c0..1b4c479 100644 --- a/src/Xml/Dom/Locator/Element/siblings.php +++ b/src/Xml/Dom/Locator/Element/siblings.php @@ -4,21 +4,21 @@ namespace VeeWee\Xml\Dom\Locator\Element; -use \DOM\Element; -use \DOM\Node; +use \Dom\Element; +use \Dom\Node; use VeeWee\Xml\Dom\Collection\NodeList; use function Psl\Vec\filter; use function VeeWee\Xml\Dom\Predicate\is_element; /** - * @return NodeList<\DOM\Element> + * @return NodeList<\Dom\Element> */ -function siblings(\DOM\Node $node): NodeList +function siblings(\Dom\Node $node): NodeList { - /** @var NodeList<\DOM\Element> $siblings */ + /** @var NodeList<\Dom\Element> $siblings */ $siblings = new NodeList(...filter( $node->parentNode?->childNodes?->getIterator() ?? [], - static fn (\DOM\Node $sibling): bool => is_element($sibling) && $sibling !== $node + static fn (\Dom\Node $sibling): bool => is_element($sibling) && $sibling !== $node )); return $siblings; diff --git a/src/Xml/Dom/Locator/Node/children.php b/src/Xml/Dom/Locator/Node/children.php index d0d3430..87690e9 100644 --- a/src/Xml/Dom/Locator/Node/children.php +++ b/src/Xml/Dom/Locator/Node/children.php @@ -4,13 +4,13 @@ namespace VeeWee\Xml\Dom\Locator\Node; -use \DOM\Node; +use \Dom\Node; use VeeWee\Xml\Dom\Collection\NodeList; /** - * @return NodeList<\DOM\Node> + * @return NodeList<\Dom\Node> */ -function children(\DOM\Node $node): NodeList +function children(\Dom\Node $node): NodeList { return NodeList::fromDOMNodeList($node->childNodes); } diff --git a/src/Xml/Dom/Locator/Node/detect_document.php b/src/Xml/Dom/Locator/Node/detect_document.php index d3a3188..45be27d 100644 --- a/src/Xml/Dom/Locator/Node/detect_document.php +++ b/src/Xml/Dom/Locator/Node/detect_document.php @@ -4,7 +4,7 @@ namespace VeeWee\Xml\Dom\Locator\Node; -use \DOM\XMLDocument; +use \Dom\XMLDocument; use InvalidArgumentException; use function VeeWee\Xml\Dom\Assert\assert_document; use function VeeWee\Xml\Dom\Predicate\is_document; @@ -12,7 +12,7 @@ /** * @throws InvalidArgumentException */ -function detect_document(\DOM\Node $node): \DOM\XMLDocument +function detect_document(\Dom\Node $node): \Dom\XMLDocument { return is_document($node) ? $node : assert_document($node->ownerDocument); } diff --git a/src/Xml/Dom/Locator/Node/value.php b/src/Xml/Dom/Locator/Node/value.php index 9ee53e9..ff3ed06 100644 --- a/src/Xml/Dom/Locator/Node/value.php +++ b/src/Xml/Dom/Locator/Node/value.php @@ -4,7 +4,7 @@ namespace VeeWee\Xml\Dom\Locator\Node; -use \DOM\Node; +use \Dom\Node; use Psl\Type\Exception\CoercionException; use Psl\Type\TypeInterface; @@ -17,7 +17,7 @@ * * @throws CoercionException */ -function value(\DOM\Node $node, TypeInterface $type) +function value(\Dom\Node $node, TypeInterface $type) { return $type->coerce($node->substitutedNodeValue ?? ''); } diff --git a/src/Xml/Dom/Locator/Xmlns/linked_namespaces.php b/src/Xml/Dom/Locator/Xmlns/linked_namespaces.php index 5e66574..109d88d 100644 --- a/src/Xml/Dom/Locator/Xmlns/linked_namespaces.php +++ b/src/Xml/Dom/Locator/Xmlns/linked_namespaces.php @@ -5,9 +5,9 @@ namespace VeeWee\Xml\Dom\Locator\Xmlns; /** - * @return list<\DOM\NamespaceInfo> + * @return list<\Dom\NamespaceInfo> */ -function linked_namespaces(\DOM\Element $node): array +function linked_namespaces(\Dom\Element $node): array { return $node->getInScopeNamespaces(); } diff --git a/src/Xml/Dom/Locator/Xmlns/recursive_linked_namespaces.php b/src/Xml/Dom/Locator/Xmlns/recursive_linked_namespaces.php index af2e016..e77bf53 100644 --- a/src/Xml/Dom/Locator/Xmlns/recursive_linked_namespaces.php +++ b/src/Xml/Dom/Locator/Xmlns/recursive_linked_namespaces.php @@ -5,9 +5,9 @@ namespace VeeWee\Xml\Dom\Locator\Xmlns; /** - * @return list<\DOM\NamespaceInfo> + * @return list<\Dom\NamespaceInfo> */ -function recursive_linked_namespaces(\DOM\Element $node): array +function recursive_linked_namespaces(\Dom\Element $node): array { return $node->getDescendantNamespaces(); } diff --git a/src/Xml/Dom/Locator/Xsd/locate_all_xsd_schemas.php b/src/Xml/Dom/Locator/Xsd/locate_all_xsd_schemas.php index e632e62..32425a9 100644 --- a/src/Xml/Dom/Locator/Xsd/locate_all_xsd_schemas.php +++ b/src/Xml/Dom/Locator/Xsd/locate_all_xsd_schemas.php @@ -4,14 +4,14 @@ namespace VeeWee\Xml\Dom\Locator\Xsd; -use \DOM\XMLDocument; +use \Dom\XMLDocument; use Psl\Regex\Exception\RuntimeException; use VeeWee\Xml\Xsd\Schema\SchemaCollection; /** * @throws RuntimeException */ -function locate_all_xsd_schemas(\DOM\XMLDocument $document): SchemaCollection +function locate_all_xsd_schemas(\Dom\XMLDocument $document): SchemaCollection { return new SchemaCollection( ...iterator_to_array(locate_namespaced_xsd_schemas($document)), diff --git a/src/Xml/Dom/Locator/Xsd/locate_namespaced_xsd_schemas.php b/src/Xml/Dom/Locator/Xsd/locate_namespaced_xsd_schemas.php index e4b2ffe..7511f30 100644 --- a/src/Xml/Dom/Locator/Xsd/locate_namespaced_xsd_schemas.php +++ b/src/Xml/Dom/Locator/Xsd/locate_namespaced_xsd_schemas.php @@ -4,7 +4,7 @@ namespace VeeWee\Xml\Dom\Locator\Xsd; -use \DOM\XMLDocument; +use \Dom\XMLDocument; use Psl\Regex\Exception\RuntimeException; use VeeWee\Xml\Xmlns\Xmlns; use VeeWee\Xml\Xsd\Schema\Schema; @@ -15,7 +15,7 @@ /** * @throws RuntimeException */ -function locate_namespaced_xsd_schemas(\DOM\XMLDocument $document): SchemaCollection +function locate_namespaced_xsd_schemas(\Dom\XMLDocument $document): SchemaCollection { $schemaNs = Xmlns::xsi()->value(); $documentElement = document_element()($document); diff --git a/src/Xml/Dom/Locator/Xsd/locate_no_namespaced_xsd_schemas.php b/src/Xml/Dom/Locator/Xsd/locate_no_namespaced_xsd_schemas.php index b03abf5..195ab41 100644 --- a/src/Xml/Dom/Locator/Xsd/locate_no_namespaced_xsd_schemas.php +++ b/src/Xml/Dom/Locator/Xsd/locate_no_namespaced_xsd_schemas.php @@ -4,7 +4,7 @@ namespace VeeWee\Xml\Dom\Locator\Xsd; -use \DOM\XMLDocument; +use \Dom\XMLDocument; use Psl\Regex\Exception\RuntimeException; use VeeWee\Xml\Xmlns\Xmlns; use VeeWee\Xml\Xsd\Schema\Schema; @@ -16,7 +16,7 @@ /** * @throws RuntimeException */ -function locate_no_namespaced_xsd_schemas(\DOM\XMLDocument $document): SchemaCollection +function locate_no_namespaced_xsd_schemas(\Dom\XMLDocument $document): SchemaCollection { $schemaNs = Xmlns::xsi()->value(); $documentElement = document_element()($document); diff --git a/src/Xml/Dom/Locator/document_element.php b/src/Xml/Dom/Locator/document_element.php index ace573b..0c259ab 100644 --- a/src/Xml/Dom/Locator/document_element.php +++ b/src/Xml/Dom/Locator/document_element.php @@ -5,14 +5,14 @@ namespace VeeWee\Xml\Dom\Locator; use Closure; -use \DOM\XMLDocument; -use \DOM\Element; +use \Dom\XMLDocument; +use \Dom\Element; use function VeeWee\Xml\Dom\Assert\assert_element; /** - * @return Closure(\DOM\XMLDocument): \DOM\Element + * @return Closure(\Dom\XMLDocument): \Dom\Element */ function document_element(): Closure { - return static fn (\DOM\XMLDocument $document): \DOM\Element => assert_element($document->documentElement); + return static fn (\Dom\XMLDocument $document): \Dom\Element => assert_element($document->documentElement); } diff --git a/src/Xml/Dom/Locator/elements_with_namespaced_tagname.php b/src/Xml/Dom/Locator/elements_with_namespaced_tagname.php index eefbf3e..35f7e3a 100644 --- a/src/Xml/Dom/Locator/elements_with_namespaced_tagname.php +++ b/src/Xml/Dom/Locator/elements_with_namespaced_tagname.php @@ -5,21 +5,21 @@ namespace VeeWee\Xml\Dom\Locator; use Closure; -use \DOM\XMLDocument; -use \DOM\Element; +use \Dom\XMLDocument; +use \Dom\Element; use VeeWee\Xml\Dom\Collection\NodeList; use function VeeWee\Xml\Dom\Locator\Element\locate_by_namespaced_tag_name; /** - * @return Closure(\DOM\XMLDocument): NodeList<\DOM\Element> + * @return Closure(\Dom\XMLDocument): NodeList<\Dom\Element> */ function elements_with_namespaced_tagname(string $namespace, string $localTagName): Closure { return /** - * @return NodeList<\DOM\Element> + * @return NodeList<\Dom\Element> */ - static fn (\DOM\XMLDocument $document): NodeList + static fn (\Dom\XMLDocument $document): NodeList => locate_by_namespaced_tag_name( document_element()($document), $namespace, diff --git a/src/Xml/Dom/Locator/elements_with_tagname.php b/src/Xml/Dom/Locator/elements_with_tagname.php index 6159b73..20daa90 100644 --- a/src/Xml/Dom/Locator/elements_with_tagname.php +++ b/src/Xml/Dom/Locator/elements_with_tagname.php @@ -5,21 +5,21 @@ namespace VeeWee\Xml\Dom\Locator; use Closure; -use \DOM\XMLDocument; -use \DOM\Element; +use \Dom\XMLDocument; +use \Dom\Element; use VeeWee\Xml\Dom\Collection\NodeList; use function VeeWee\Xml\Dom\Locator\Element\locate_by_tag_name; /** - * @return Closure(\DOM\XMLDocument): NodeList<\DOM\Element> + * @return Closure(\Dom\XMLDocument): NodeList<\Dom\Element> */ function elements_with_tagname(string $tagName): Closure { return /** - * @return NodeList<\DOM\Element> + * @return NodeList<\Dom\Element> */ - static fn (\DOM\XMLDocument $document): NodeList + static fn (\Dom\XMLDocument $document): NodeList => locate_by_tag_name( document_element()($document), $tagName diff --git a/src/Xml/Dom/Locator/root_namespace.php b/src/Xml/Dom/Locator/root_namespace.php index 719c6bb..608d3df 100644 --- a/src/Xml/Dom/Locator/root_namespace.php +++ b/src/Xml/Dom/Locator/root_namespace.php @@ -5,12 +5,12 @@ namespace VeeWee\Xml\Dom\Locator; use Closure; -use \DOM\XMLDocument; +use \Dom\XMLDocument; /** - * @return Closure(\DOM\XMLDocument): ?string + * @return Closure(\Dom\XMLDocument): ?string */ function root_namespace_uri(): Closure { - return static fn (\DOM\XMLDocument $document): ?string => document_element()($document)->namespaceURI; + return static fn (\Dom\XMLDocument $document): ?string => document_element()($document)->namespaceURI; } diff --git a/src/Xml/Dom/Manipulator/Attribute/rename.php b/src/Xml/Dom/Manipulator/Attribute/rename.php index cac7cbd..09058dc 100644 --- a/src/Xml/Dom/Manipulator/Attribute/rename.php +++ b/src/Xml/Dom/Manipulator/Attribute/rename.php @@ -4,7 +4,7 @@ namespace VeeWee\Xml\Dom\Manipulator\Attribute; -use \DOM\Attr; +use \Dom\Attr; use VeeWee\Xml\Exception\RuntimeException; use function Psl\Fun\tap; use function VeeWee\Xml\Dom\Manipulator\Xmlns\rename as rename_xmlns_attribute; @@ -14,11 +14,11 @@ /** * @throws RuntimeException */ -function rename(\DOM\Attr $target, string $newQName, ?string $newNamespaceURI = null): \DOM\Attr +function rename(\Dom\Attr $target, string $newQName, ?string $newNamespaceURI = null): \Dom\Attr { - return disallow_issues(static fn (): \DOM\Attr => match(true) { + return disallow_issues(static fn (): \Dom\Attr => match(true) { is_xmlns_attribute($target) => rename_xmlns_attribute($target, $newQName), - default => (function() use ($target, $newNamespaceURI, $newQName): \DOM\Attr { + default => (function() use ($target, $newNamespaceURI, $newQName): \Dom\Attr { $target->rename($newNamespaceURI, $newQName); return $target; })() diff --git a/src/Xml/Dom/Manipulator/Document/optimize_namespaces.php b/src/Xml/Dom/Manipulator/Document/optimize_namespaces.php index 0f1a95e..33eeb18 100644 --- a/src/Xml/Dom/Manipulator/Document/optimize_namespaces.php +++ b/src/Xml/Dom/Manipulator/Document/optimize_namespaces.php @@ -17,12 +17,12 @@ /** * @throws RuntimeException */ -function optimize_namespaces(\DOM\XMLDocument $document, string $prefix = 'ns'): void +function optimize_namespaces(\Dom\XMLDocument $document, string $prefix = 'ns'): void { $documentElement = document_element()($document); $namespaceURIs = values(unique(map( recursive_linked_namespaces($documentElement), - static fn (\DOM\NamespaceInfo $info): string => $info->namespaceURI ?? '' + static fn (\Dom\NamespaceInfo $info): string => $info->namespaceURI ?? '' ))); foreach (sort($namespaceURIs) as $index => $namespaceURI) { diff --git a/src/Xml/Dom/Manipulator/Element/copy_named_xmlns_attributes.php b/src/Xml/Dom/Manipulator/Element/copy_named_xmlns_attributes.php index 9eb8136..c8524b9 100644 --- a/src/Xml/Dom/Manipulator/Element/copy_named_xmlns_attributes.php +++ b/src/Xml/Dom/Manipulator/Element/copy_named_xmlns_attributes.php @@ -11,9 +11,9 @@ /** * @throws RuntimeException */ -function copy_named_xmlns_attributes(\DOM\Element $target, \DOM\Element $source): void +function copy_named_xmlns_attributes(\Dom\Element $target, \Dom\Element $source): void { - xmlns_attributes_list($source)->forEach(static function (\DOM\Attr $xmlns) use ($target) { + xmlns_attributes_list($source)->forEach(static function (\Dom\Attr $xmlns) use ($target) { if ($xmlns->prefix !== null && !$target->hasAttribute($xmlns->nodeName)) { xmlns_attribute($xmlns->localName, $xmlns->value)($target); } diff --git a/src/Xml/Dom/Manipulator/Element/rename.php b/src/Xml/Dom/Manipulator/Element/rename.php index b220cd3..743da4b 100644 --- a/src/Xml/Dom/Manipulator/Element/rename.php +++ b/src/Xml/Dom/Manipulator/Element/rename.php @@ -10,7 +10,7 @@ /** * @throws RuntimeException */ -function rename(\DOM\Element $target, string $newQName, ?string $newNamespaceURI = null): \DOM\Element +function rename(\Dom\Element $target, string $newQName, ?string $newNamespaceURI = null): \Dom\Element { $parts = explode(':', $newQName, 2); $newPrefix = $parts[0] ?? ''; diff --git a/src/Xml/Dom/Manipulator/Node/append_external_node.php b/src/Xml/Dom/Manipulator/Node/append_external_node.php index 87ef41e..f9764ca 100644 --- a/src/Xml/Dom/Manipulator/Node/append_external_node.php +++ b/src/Xml/Dom/Manipulator/Node/append_external_node.php @@ -4,13 +4,13 @@ namespace VeeWee\Xml\Dom\Manipulator\Node; -use \DOM\Node; +use \Dom\Node; use VeeWee\Xml\Exception\RuntimeException; /** * @throws RuntimeException */ -function append_external_node(\DOM\Node $target, \DOM\Node $source): \DOM\Node +function append_external_node(\Dom\Node $target, \Dom\Node $source): \Dom\Node { $copy = import_node_deeply($target, $source); $target->appendChild($copy); diff --git a/src/Xml/Dom/Manipulator/Node/import_node_deeply.php b/src/Xml/Dom/Manipulator/Node/import_node_deeply.php index e923e14..6e44828 100644 --- a/src/Xml/Dom/Manipulator/Node/import_node_deeply.php +++ b/src/Xml/Dom/Manipulator/Node/import_node_deeply.php @@ -4,7 +4,7 @@ namespace VeeWee\Xml\Dom\Manipulator\Node; -use \DOM\Node; +use \Dom\Node; use VeeWee\Xml\Exception\RuntimeException; use function VeeWee\Xml\Dom\Locator\Node\detect_document; use function VeeWee\Xml\ErrorHandling\disallow_issues; @@ -13,10 +13,10 @@ /** * @throws RuntimeException */ -function import_node_deeply(\DOM\Node $target, \DOM\Node $source): \DOM\Node +function import_node_deeply(\Dom\Node $target, \Dom\Node $source): \Dom\Node { return disallow_issues( - static function () use ($target, $source): \DOM\Node { + static function () use ($target, $source): \Dom\Node { $document = detect_document($target); return disallow_libxml_false_returns( diff --git a/src/Xml/Dom/Manipulator/Node/remove.php b/src/Xml/Dom/Manipulator/Node/remove.php index f399411..6e108ce 100644 --- a/src/Xml/Dom/Manipulator/Node/remove.php +++ b/src/Xml/Dom/Manipulator/Node/remove.php @@ -4,7 +4,7 @@ namespace VeeWee\Xml\Dom\Manipulator\Node; -use \DOM\Node; +use \Dom\Node; use VeeWee\Xml\Exception\RuntimeException; use function VeeWee\Xml\Dom\Locator\Element\parent_element; use function VeeWee\Xml\Dom\Predicate\is_attribute; @@ -14,13 +14,13 @@ /** * @throws RuntimeException */ -function remove(\DOM\Node $target): \DOM\Node +function remove(\Dom\Node $target): \Dom\Node { return disallow_issues( /** * @throws RuntimeException */ - static function () use ($target): \DOM\Node { + static function () use ($target): \Dom\Node { $parent = parent_element($target); if (is_attribute($target)) { diff --git a/src/Xml/Dom/Manipulator/Node/remove_namespace.php b/src/Xml/Dom/Manipulator/Node/remove_namespace.php index a9c7f75..1bfce93 100644 --- a/src/Xml/Dom/Manipulator/Node/remove_namespace.php +++ b/src/Xml/Dom/Manipulator/Node/remove_namespace.php @@ -11,13 +11,13 @@ /** * @throws RuntimeException */ -function remove_namespace(\DOM\Attr $target, \DOM\Element $parent): \DOM\Attr +function remove_namespace(\Dom\Attr $target, \Dom\Element $parent): \Dom\Attr { return disallow_issues( /** * @throws RuntimeException */ - static function () use ($target, $parent): \DOM\Attr { + static function () use ($target, $parent): \Dom\Attr { disallow_libxml_false_returns( $parent->removeAttributeNode($target), 'Could not remove xmlns attribute from dom element' diff --git a/src/Xml/Dom/Manipulator/Node/rename.php b/src/Xml/Dom/Manipulator/Node/rename.php index a5694d8..a8cd3de 100644 --- a/src/Xml/Dom/Manipulator/Node/rename.php +++ b/src/Xml/Dom/Manipulator/Node/rename.php @@ -4,7 +4,7 @@ namespace VeeWee\Xml\Dom\Manipulator\Node; -use \DOM\Node; +use \Dom\Node; use VeeWee\Xml\Exception\RuntimeException; use function VeeWee\Xml\Dom\Manipulator\Attribute\rename as rename_attribute; use function VeeWee\Xml\Dom\Manipulator\Element\rename as rename_element; @@ -16,7 +16,7 @@ * * @throws RuntimeException */ -function rename(\DOM\Node $target, string $newQName, ?string $newNamespaceURI = null): \DOM\Node +function rename(\Dom\Node $target, string $newQName, ?string $newNamespaceURI = null): \Dom\Node { return match(true) { is_attribute($target) => rename_attribute($target, $newQName, $newNamespaceURI), diff --git a/src/Xml/Dom/Manipulator/Node/replace_by_external_node.php b/src/Xml/Dom/Manipulator/Node/replace_by_external_node.php index 012ebfa..f6f4e6f 100644 --- a/src/Xml/Dom/Manipulator/Node/replace_by_external_node.php +++ b/src/Xml/Dom/Manipulator/Node/replace_by_external_node.php @@ -4,7 +4,7 @@ namespace VeeWee\Xml\Dom\Manipulator\Node; -use \DOM\Node; +use \Dom\Node; use VeeWee\Xml\Exception\RuntimeException; use Webmozart\Assert\Assert; use function get_class; @@ -14,10 +14,10 @@ /** * @throws RuntimeException */ -function replace_by_external_node(\DOM\Node $target, \DOM\Node $source): \DOM\Node +function replace_by_external_node(\Dom\Node $target, \Dom\Node $source): \Dom\Node { return disallow_issues( - static function () use ($target, $source) : \DOM\Node { + static function () use ($target, $source) : \Dom\Node { $parentNode = $target->parentNode; Assert::notNull($parentNode, 'Could not replace a node without parent node. ('.get_class($target).')'); $copy = import_node_deeply($target, $source); diff --git a/src/Xml/Dom/Manipulator/Node/replace_by_external_nodes.php b/src/Xml/Dom/Manipulator/Node/replace_by_external_nodes.php index 8ac2ec1..faa61d4 100644 --- a/src/Xml/Dom/Manipulator/Node/replace_by_external_nodes.php +++ b/src/Xml/Dom/Manipulator/Node/replace_by_external_nodes.php @@ -13,21 +13,21 @@ /** * @throws RuntimeException - * @param iterable $sources - * @return array + * @param iterable $sources + * @return array */ -function replace_by_external_nodes(\DOM\Node $target, iterable $sources): array +function replace_by_external_nodes(\Dom\Node $target, iterable $sources): array { return disallow_issues( /** - * @return array + * @return array */ static function () use ($target, $sources) : array { $parentNode = $target->parentNode; Assert::notNull($parentNode, 'Could not replace a node without parent node. ('.get_class($target).')'); $copies = map( $sources, - static fn (\DOM\Node $source): \DOM\Node => import_node_deeply($target, $source) + static fn (\Dom\Node $source): \Dom\Node => import_node_deeply($target, $source) ); // Documents can only contain one element, so in case of documentElement, we remove it first to avoid errors. diff --git a/src/Xml/Dom/Manipulator/Xmlns/rename.php b/src/Xml/Dom/Manipulator/Xmlns/rename.php index 1580aeb..f23004f 100644 --- a/src/Xml/Dom/Manipulator/Xmlns/rename.php +++ b/src/Xml/Dom/Manipulator/Xmlns/rename.php @@ -11,7 +11,7 @@ /** * @throws RuntimeException */ -function rename(\DOM\Attr $target, string $newQName): \DOM\Attr +function rename(\Dom\Attr $target, string $newQName): \Dom\Attr { disallow_issues(static fn () => $target->rename(Xmlns::xmlns()->value(), $newQName)); diff --git a/src/Xml/Dom/Manipulator/Xmlns/rename_element_namespace.php b/src/Xml/Dom/Manipulator/Xmlns/rename_element_namespace.php index 0229941..653937a 100644 --- a/src/Xml/Dom/Manipulator/Xmlns/rename_element_namespace.php +++ b/src/Xml/Dom/Manipulator/Xmlns/rename_element_namespace.php @@ -13,13 +13,13 @@ * @throws RuntimeException * @param non-empty-string $newPrefix */ -function rename_element_namespace(\DOM\Element $element, string $namespaceURI, string $newPrefix): void +function rename_element_namespace(\Dom\Element $element, string $namespaceURI, string $newPrefix): void { children($element)->forEach( - static fn (\DOM\Element $child) => rename_element_namespace($child, $namespaceURI, $newPrefix) + static fn (\Dom\Element $child) => rename_element_namespace($child, $namespaceURI, $newPrefix) ); - attributes_list($element)->forEach(static function (\DOM\Attr $attr) use ($namespaceURI, $newPrefix, $element) { + attributes_list($element)->forEach(static function (\Dom\Attr $attr) use ($namespaceURI, $newPrefix, $element) { if ($attr->namespaceURI === $namespaceURI) { $attr->rename($namespaceURI, $newPrefix . ':' . $attr->localName); } @@ -29,7 +29,7 @@ function rename_element_namespace(\DOM\Element $element, string $namespaceURI, s $attr->rename($attr->namespaceURI, 'xmlns:' . $newPrefix); } catch (\DOMException $e) { - if ($e->getCode() === \DOM\INVALID_MODIFICATION_ERR) { + if ($e->getCode() === \Dom\INVALID_MODIFICATION_ERR) { // Remove the attribute that would become a duplicate $element->removeAttributeNode($attr); } else { diff --git a/src/Xml/Dom/Manipulator/append.php b/src/Xml/Dom/Manipulator/append.php index e25c5f4..b9db447 100644 --- a/src/Xml/Dom/Manipulator/append.php +++ b/src/Xml/Dom/Manipulator/append.php @@ -5,7 +5,7 @@ namespace VeeWee\Xml\Dom\Manipulator; use Closure; -use \DOM\Node; +use \Dom\Node; use VeeWee\Xml\Exception\RuntimeException; use function VeeWee\Xml\Dom\Predicate\is_attribute; use function VeeWee\Xml\Dom\Predicate\is_element; @@ -15,11 +15,11 @@ /** * @no-named-arguments * @throws RuntimeException - * @return Closure(\DOM\Node): \DOM\Node + * @return Closure(\Dom\Node): \Dom\Node */ -function append(\DOM\Node ... $nodes): Closure +function append(\Dom\Node ... $nodes): Closure { - return static fn (\DOM\Node $target): \DOM\Node => disallow_issues( + return static fn (\Dom\Node $target): \Dom\Node => disallow_issues( static function () use ($target, $nodes) { foreach ($nodes as $node) { // Attributes cannot be appended with appendChild. diff --git a/src/Xml/Dom/Mapper/Mapper.php b/src/Xml/Dom/Mapper/Mapper.php index 3677784..971f6be 100644 --- a/src/Xml/Dom/Mapper/Mapper.php +++ b/src/Xml/Dom/Mapper/Mapper.php @@ -4,7 +4,7 @@ namespace VeeWee\Xml\Dom\Mapper; -use \DOM\XMLDocument; +use \Dom\XMLDocument; /** * @template R @@ -14,5 +14,5 @@ interface Mapper /** * @return R */ - public function __invoke(\DOM\XMLDocument $document): mixed; + public function __invoke(\Dom\XMLDocument $document): mixed; } diff --git a/src/Xml/Dom/Mapper/xml_string.php b/src/Xml/Dom/Mapper/xml_string.php index d41a66e..b8c08c8 100644 --- a/src/Xml/Dom/Mapper/xml_string.php +++ b/src/Xml/Dom/Mapper/xml_string.php @@ -5,7 +5,7 @@ namespace VeeWee\Xml\Dom\Mapper; use Closure; -use \DOM\Node; +use \Dom\Node; use function Psl\Type\non_empty_string; use function VeeWee\Xml\Dom\Locator\Node\detect_document; use function VeeWee\Xml\Dom\Predicate\is_document; @@ -13,11 +13,11 @@ use function VeeWee\Xml\ErrorHandling\disallow_libxml_false_returns; /** - * @return Closure(\DOM\Node): non-empty-string + * @return Closure(\Dom\Node): non-empty-string */ function xml_string(): Closure { - return static fn (\DOM\Node $node): string => disallow_issues( + return static fn (\Dom\Node $node): string => disallow_issues( static function () use ($node): string { $document = detect_document($node); $node = is_document($node) ? null : $node; diff --git a/src/Xml/Dom/Mapper/xslt_template.php b/src/Xml/Dom/Mapper/xslt_template.php index 8600b18..a8d19f8 100644 --- a/src/Xml/Dom/Mapper/xslt_template.php +++ b/src/Xml/Dom/Mapper/xslt_template.php @@ -6,7 +6,7 @@ use Closure; use VeeWee\Xml\Dom\Document; -use DOM\XMLDocument; +use Dom\XMLDocument; use VeeWee\Xml\Xslt\Processor; use XSLTProcessor; diff --git a/src/Xml/Dom/Predicate/is_attribute.php b/src/Xml/Dom/Predicate/is_attribute.php index 8eaf5d3..0ccb537 100644 --- a/src/Xml/Dom/Predicate/is_attribute.php +++ b/src/Xml/Dom/Predicate/is_attribute.php @@ -4,13 +4,13 @@ namespace VeeWee\Xml\Dom\Predicate; -use \DOM\Attr; -use \DOM\Node; +use \Dom\Attr; +use \Dom\Node; /** - * @psalm-assert-if-true \DOM\Attr $node + * @psalm-assert-if-true \Dom\Attr $node */ -function is_attribute(\DOM\Node $node): bool +function is_attribute(\Dom\Node $node): bool { - return $node instanceof \DOM\Attr; + return $node instanceof \Dom\Attr; } diff --git a/src/Xml/Dom/Predicate/is_cdata.php b/src/Xml/Dom/Predicate/is_cdata.php index 3fbd1ef..df920d2 100644 --- a/src/Xml/Dom/Predicate/is_cdata.php +++ b/src/Xml/Dom/Predicate/is_cdata.php @@ -4,13 +4,13 @@ namespace VeeWee\Xml\Dom\Predicate; -use \DOM\CDATASection; -use \DOM\Node; +use \Dom\CDATASection; +use \Dom\Node; /** - * @psalm-assert-if-true \DOM\CDATASection $node + * @psalm-assert-if-true \Dom\CDATASection $node */ -function is_cdata(\DOM\Node $node): bool +function is_cdata(\Dom\Node $node): bool { - return $node instanceof \DOM\CDATASection; + return $node instanceof \Dom\CDATASection; } diff --git a/src/Xml/Dom/Predicate/is_default_xmlns_attribute.php b/src/Xml/Dom/Predicate/is_default_xmlns_attribute.php index 3deffed..897a459 100644 --- a/src/Xml/Dom/Predicate/is_default_xmlns_attribute.php +++ b/src/Xml/Dom/Predicate/is_default_xmlns_attribute.php @@ -4,9 +4,9 @@ namespace VeeWee\Xml\Dom\Predicate; -use \DOM\Node; +use \Dom\Node; -function is_default_xmlns_attribute(\DOM\Node $node): bool +function is_default_xmlns_attribute(\Dom\Node $node): bool { return is_xmlns_attribute($node) && $node->prefix === null; } diff --git a/src/Xml/Dom/Predicate/is_document.php b/src/Xml/Dom/Predicate/is_document.php index 42eb991..77f7159 100644 --- a/src/Xml/Dom/Predicate/is_document.php +++ b/src/Xml/Dom/Predicate/is_document.php @@ -4,13 +4,13 @@ namespace VeeWee\Xml\Dom\Predicate; -use \DOM\XMLDocument; -use \DOM\Node; +use \Dom\XMLDocument; +use \Dom\Node; /** - * @psalm-assert-if-true \DOM\XMLDocument $node + * @psalm-assert-if-true \Dom\XMLDocument $node */ -function is_document(\DOM\Node $node): bool +function is_document(\Dom\Node $node): bool { - return $node instanceof \DOM\XMLDocument; + return $node instanceof \Dom\XMLDocument; } diff --git a/src/Xml/Dom/Predicate/is_document_element.php b/src/Xml/Dom/Predicate/is_document_element.php index be8694a..eefeb4d 100644 --- a/src/Xml/Dom/Predicate/is_document_element.php +++ b/src/Xml/Dom/Predicate/is_document_element.php @@ -4,10 +4,10 @@ namespace VeeWee\Xml\Dom\Predicate; -use \DOM\Node; +use \Dom\Node; use function VeeWee\Xml\Dom\Locator\Node\detect_document; -function is_document_element(\DOM\Node $node): bool +function is_document_element(\Dom\Node $node): bool { return is_element($node) && detect_document($node)->documentElement === $node; } diff --git a/src/Xml/Dom/Predicate/is_element.php b/src/Xml/Dom/Predicate/is_element.php index e0c6af0..b038d1a 100644 --- a/src/Xml/Dom/Predicate/is_element.php +++ b/src/Xml/Dom/Predicate/is_element.php @@ -4,13 +4,13 @@ namespace VeeWee\Xml\Dom\Predicate; -use \DOM\Element; -use \DOM\Node; +use \Dom\Element; +use \Dom\Node; /** - * @psalm-assert-if-true \DOM\Element $node + * @psalm-assert-if-true \Dom\Element $node */ -function is_element(\DOM\Node $node): bool +function is_element(\Dom\Node $node): bool { - return $node instanceof \DOM\Element; + return $node instanceof \Dom\Element; } diff --git a/src/Xml/Dom/Predicate/is_non_empty_text.php b/src/Xml/Dom/Predicate/is_non_empty_text.php index 8dd327a..9acf3b2 100644 --- a/src/Xml/Dom/Predicate/is_non_empty_text.php +++ b/src/Xml/Dom/Predicate/is_non_empty_text.php @@ -4,9 +4,9 @@ namespace VeeWee\Xml\Dom\Predicate; -use \DOM\Node; +use \Dom\Node; -function is_non_empty_text(\DOM\Node $node): bool +function is_non_empty_text(\Dom\Node $node): bool { return is_text($node) && trim($node->nodeValue ?? '') !== ''; } diff --git a/src/Xml/Dom/Predicate/is_text.php b/src/Xml/Dom/Predicate/is_text.php index 865d9f8..5135a09 100644 --- a/src/Xml/Dom/Predicate/is_text.php +++ b/src/Xml/Dom/Predicate/is_text.php @@ -4,13 +4,13 @@ namespace VeeWee\Xml\Dom\Predicate; -use \DOM\Node; -use \DOM\Text; +use \Dom\Node; +use \Dom\Text; /** - * @psalm-assert-if-true \DOM\Text $node + * @psalm-assert-if-true \Dom\Text $node */ -function is_text(\DOM\Node $node): bool +function is_text(\Dom\Node $node): bool { - return $node instanceof \DOM\Text; + return $node instanceof \Dom\Text; } diff --git a/src/Xml/Dom/Predicate/is_whitespace.php b/src/Xml/Dom/Predicate/is_whitespace.php index 69fa6b7..9ece97c 100644 --- a/src/Xml/Dom/Predicate/is_whitespace.php +++ b/src/Xml/Dom/Predicate/is_whitespace.php @@ -4,9 +4,9 @@ namespace VeeWee\Xml\Dom\Predicate; -use \DOM\Node; +use \Dom\Node; -function is_whitespace(\DOM\Node $node): bool +function is_whitespace(\Dom\Node $node): bool { return is_text($node) && trim($node->nodeValue ?? '') === ''; } diff --git a/src/Xml/Dom/Predicate/is_xmlns_attribute.php b/src/Xml/Dom/Predicate/is_xmlns_attribute.php index ac752ca..09f6f0f 100644 --- a/src/Xml/Dom/Predicate/is_xmlns_attribute.php +++ b/src/Xml/Dom/Predicate/is_xmlns_attribute.php @@ -4,13 +4,13 @@ namespace VeeWee\Xml\Dom\Predicate; -use \DOM\Node; +use \Dom\Node; use VeeWee\Xml\Xmlns\Xmlns; /** - * @psalm-assert-if-true \DOM\Attr $node + * @psalm-assert-if-true \Dom\Attr $node */ -function is_xmlns_attribute(\DOM\Node $node): bool +function is_xmlns_attribute(\Dom\Node $node): bool { return is_attribute($node) && $node->namespaceURI === Xmlns::xmlns()->value(); } diff --git a/src/Xml/Dom/Traverser/Action.php b/src/Xml/Dom/Traverser/Action.php index 198e01d..ca24e33 100644 --- a/src/Xml/Dom/Traverser/Action.php +++ b/src/Xml/Dom/Traverser/Action.php @@ -4,9 +4,9 @@ namespace VeeWee\Xml\Dom\Traverser; -use \DOM\Node; +use \Dom\Node; interface Action { - public function __invoke(\DOM\Node $currentNode): void; + public function __invoke(\Dom\Node $currentNode): void; } diff --git a/src/Xml/Dom/Traverser/Action/Noop.php b/src/Xml/Dom/Traverser/Action/Noop.php index c31b0b2..ec7f7e9 100644 --- a/src/Xml/Dom/Traverser/Action/Noop.php +++ b/src/Xml/Dom/Traverser/Action/Noop.php @@ -4,12 +4,12 @@ namespace VeeWee\Xml\Dom\Traverser\Action; -use \DOM\Node; +use \Dom\Node; use VeeWee\Xml\Dom\Traverser\Action; final class Noop implements Action { - public function __invoke(\DOM\Node $currentNode): void + public function __invoke(\Dom\Node $currentNode): void { } } diff --git a/src/Xml/Dom/Traverser/Action/RemoveNode.php b/src/Xml/Dom/Traverser/Action/RemoveNode.php index 11d0c91..fd18b44 100644 --- a/src/Xml/Dom/Traverser/Action/RemoveNode.php +++ b/src/Xml/Dom/Traverser/Action/RemoveNode.php @@ -4,7 +4,7 @@ namespace VeeWee\Xml\Dom\Traverser\Action; -use \DOM\Node; +use \Dom\Node; use VeeWee\Xml\Dom\Traverser\Action; use VeeWee\Xml\Exception\RuntimeException; use function VeeWee\Xml\Dom\Manipulator\Node\remove; @@ -14,7 +14,7 @@ final class RemoveNode implements Action /** * @throws RuntimeException */ - public function __invoke(\DOM\Node $currentNode): void + public function __invoke(\Dom\Node $currentNode): void { remove($currentNode); } diff --git a/src/Xml/Dom/Traverser/Action/RenameNode.php b/src/Xml/Dom/Traverser/Action/RenameNode.php index 03bc1e6..cb28f21 100644 --- a/src/Xml/Dom/Traverser/Action/RenameNode.php +++ b/src/Xml/Dom/Traverser/Action/RenameNode.php @@ -4,7 +4,7 @@ namespace VeeWee\Xml\Dom\Traverser\Action; -use \DOM\Node; +use \Dom\Node; use VeeWee\Xml\Dom\Traverser\Action; use VeeWee\Xml\Exception\RuntimeException; use function VeeWee\Xml\Dom\Manipulator\Node\rename; @@ -20,7 +20,7 @@ public function __construct( /** * @throws RuntimeException */ - public function __invoke(\DOM\Node $currentNode): void + public function __invoke(\Dom\Node $currentNode): void { rename($currentNode, $this->newQName, $this->newNamespaceURI); } diff --git a/src/Xml/Dom/Traverser/Action/ReplaceNode.php b/src/Xml/Dom/Traverser/Action/ReplaceNode.php index a78dbde..bdaf96d 100644 --- a/src/Xml/Dom/Traverser/Action/ReplaceNode.php +++ b/src/Xml/Dom/Traverser/Action/ReplaceNode.php @@ -4,7 +4,7 @@ namespace VeeWee\Xml\Dom\Traverser\Action; -use \DOM\Node; +use \Dom\Node; use VeeWee\Xml\Dom\Traverser\Action; use VeeWee\Xml\Exception\RuntimeException; use function VeeWee\Xml\Dom\Manipulator\Node\replace_by_external_node; @@ -12,14 +12,14 @@ final class ReplaceNode implements Action { public function __construct( - private \DOM\Node $newNode + private \Dom\Node $newNode ) { } /** * @throws RuntimeException */ - public function __invoke(\DOM\Node $currentNode): void + public function __invoke(\Dom\Node $currentNode): void { replace_by_external_node($currentNode, $this->newNode); } diff --git a/src/Xml/Dom/Traverser/Traverser.php b/src/Xml/Dom/Traverser/Traverser.php index bd34f45..2df73f8 100644 --- a/src/Xml/Dom/Traverser/Traverser.php +++ b/src/Xml/Dom/Traverser/Traverser.php @@ -4,7 +4,7 @@ namespace VeeWee\Xml\Dom\Traverser; -use \DOM\Node; +use \Dom\Node; use function VeeWee\Xml\Dom\Locator\Attribute\attributes_list; use function VeeWee\Xml\Dom\Locator\Node\children; @@ -23,7 +23,7 @@ public function __construct(Visitor ... $visitors) $this->visitors = $visitors; } - public function traverse(\DOM\Node $node): \DOM\Node + public function traverse(\Dom\Node $node): \Dom\Node { $this->enterNode($node); @@ -40,14 +40,14 @@ public function traverse(\DOM\Node $node): \DOM\Node return $node; } - private function enterNode(\DOM\Node $node): void + private function enterNode(\Dom\Node $node): void { foreach ($this->visitors as $visitor) { $visitor->onNodeEnter($node)($node); } } - private function leaveNode(\DOM\Node $node): void + private function leaveNode(\Dom\Node $node): void { foreach ($this->visitors as $visitor) { $visitor->onNodeLeave($node)($node); diff --git a/src/Xml/Dom/Traverser/Visitor.php b/src/Xml/Dom/Traverser/Visitor.php index bd87e50..0f2b440 100644 --- a/src/Xml/Dom/Traverser/Visitor.php +++ b/src/Xml/Dom/Traverser/Visitor.php @@ -4,10 +4,10 @@ namespace VeeWee\Xml\Dom\Traverser; -use \DOM\Node; +use \Dom\Node; interface Visitor { - public function onNodeEnter(\DOM\Node $node): Action; - public function onNodeLeave(\DOM\Node $node): Action; + public function onNodeEnter(\Dom\Node $node): Action; + public function onNodeLeave(\Dom\Node $node): Action; } diff --git a/src/Xml/Dom/Traverser/Visitor/AbstractVisitor.php b/src/Xml/Dom/Traverser/Visitor/AbstractVisitor.php index cd15f08..606f297 100644 --- a/src/Xml/Dom/Traverser/Visitor/AbstractVisitor.php +++ b/src/Xml/Dom/Traverser/Visitor/AbstractVisitor.php @@ -4,18 +4,18 @@ namespace VeeWee\Xml\Dom\Traverser\Visitor; -use \DOM\Node; +use \Dom\Node; use VeeWee\Xml\Dom\Traverser\Action; use VeeWee\Xml\Dom\Traverser\Visitor; abstract class AbstractVisitor implements Visitor { - public function onNodeEnter(\DOM\Node $node): Action + public function onNodeEnter(\Dom\Node $node): Action { return new Action\Noop(); } - public function onNodeLeave(\DOM\Node $node): Action + public function onNodeLeave(\Dom\Node $node): Action { return new Action\Noop(); } diff --git a/src/Xml/Dom/Traverser/Visitor/RemoveNamespaces.php b/src/Xml/Dom/Traverser/Visitor/RemoveNamespaces.php index adfd2b6..c2b0396 100644 --- a/src/Xml/Dom/Traverser/Visitor/RemoveNamespaces.php +++ b/src/Xml/Dom/Traverser/Visitor/RemoveNamespaces.php @@ -13,12 +13,12 @@ final class RemoveNamespaces extends AbstractVisitor { /** - * @var null | callable(\DOM\Attr | \DOM\Element): bool + * @var null | callable(\Dom\Attr | \Dom\Element): bool */ private $filter; /** - * @param null | callable(\DOM\Attr | \DOM\Element): bool $filter + * @param null | callable(\Dom\Attr | \Dom\Element): bool $filter */ public function __construct( ?callable $filter = null @@ -34,14 +34,14 @@ public static function all(): self public static function prefixed(): self { return new self( - static fn (\DOM\Attr | \DOM\Element $node): bool => $node->prefix !== null + static fn (\Dom\Attr | \Dom\Element $node): bool => $node->prefix !== null ); } public static function unprefixed(): self { return new self( - static fn (\DOM\Attr | \DOM\Element $node): bool => $node->prefix === null + static fn (\Dom\Attr | \Dom\Element $node): bool => $node->prefix === null ); } @@ -51,7 +51,7 @@ public static function unprefixed(): self public static function byPrefixNames(array $prefixes): self { return new self( - static fn (\DOM\Attr | \DOM\Element $node): bool => match(true) { + static fn (\Dom\Attr | \Dom\Element $node): bool => match(true) { is_xmlns_attribute($node) => contains($prefixes, $node->prefix !== null ? $node->localName : ''), default => contains($prefixes, $node->prefix ?? '') } @@ -64,14 +64,14 @@ public static function byPrefixNames(array $prefixes): self public static function byNamespaceURIs(array $URIs): self { return new self( - static fn (\DOM\Attr | \DOM\Element $node): bool => match(true) { + static fn (\Dom\Attr | \Dom\Element $node): bool => match(true) { is_xmlns_attribute($node) => contains($URIs, $node->value), default => contains($URIs, $node->namespaceURI), } ); } - public function onNodeEnter(\DOM\Node $node): Action + public function onNodeEnter(\Dom\Node $node): Action { if (is_xmlns_attribute($node)) { return new Action\Noop(); @@ -81,14 +81,14 @@ public function onNodeEnter(\DOM\Node $node): Action return new Action\Noop(); } - /** @var \DOM\Element | \DOM\Attr $node */ + /** @var \Dom\Element | \Dom\Attr $node */ return new Action\RenameNode($node->localName, null); } /** * @throws RuntimeException */ - public function onNodeLeave(\DOM\Node $node): Action + public function onNodeLeave(\Dom\Node $node): Action { if (!is_xmlns_attribute($node)) { return new Action\Noop(); @@ -101,7 +101,7 @@ public function onNodeLeave(\DOM\Node $node): Action return new Action\RemoveNode(); } - private function shouldDealWithNode(\DOM\Node $node): bool + private function shouldDealWithNode(\Dom\Node $node): bool { if (!is_element($node) && !is_attribute($node)) { return false; diff --git a/src/Xml/Dom/Traverser/Visitor/SortAttributes.php b/src/Xml/Dom/Traverser/Visitor/SortAttributes.php index 95066f3..4d729bf 100644 --- a/src/Xml/Dom/Traverser/Visitor/SortAttributes.php +++ b/src/Xml/Dom/Traverser/Visitor/SortAttributes.php @@ -13,16 +13,16 @@ final class SortAttributes extends AbstractVisitor { - public function onNodeEnter(\DOM\Node $node): Action + public function onNodeEnter(\Dom\Node $node): Action { if (!is_element($node)) { return new Action\Noop(); } attributes_list($node) - ->sort(static fn (\DOM\Attr $a, \DOM\Attr $b): int => $a->nodeName <=> $b->nodeName) + ->sort(static fn (\Dom\Attr $a, \Dom\Attr $b): int => $a->nodeName <=> $b->nodeName) ->forEach( - static function (\DOM\Attr $attr) use ($node): void { + static function (\Dom\Attr $attr) use ($node): void { disallow_issues(static function () use ($node, $attr) { remove($attr); append($attr)($node); diff --git a/src/Xml/Dom/Validator/Validator.php b/src/Xml/Dom/Validator/Validator.php index 121f376..c31b26b 100644 --- a/src/Xml/Dom/Validator/Validator.php +++ b/src/Xml/Dom/Validator/Validator.php @@ -4,10 +4,10 @@ namespace VeeWee\Xml\Dom\Validator; -use \DOM\XMLDocument; +use \Dom\XMLDocument; use VeeWee\Xml\ErrorHandling\Issue\IssueCollection; interface Validator { - public function __invoke(\DOM\XMLDocument $document): IssueCollection; + public function __invoke(\Dom\XMLDocument $document): IssueCollection; } diff --git a/src/Xml/Dom/Validator/internal_xsd_validator.php b/src/Xml/Dom/Validator/internal_xsd_validator.php index 1dac61f..b3b8ef0 100644 --- a/src/Xml/Dom/Validator/internal_xsd_validator.php +++ b/src/Xml/Dom/Validator/internal_xsd_validator.php @@ -5,7 +5,7 @@ namespace VeeWee\Xml\Dom\Validator; use Closure; -use \DOM\XMLDocument; +use \Dom\XMLDocument; use VeeWee\Xml\ErrorHandling\Issue\IssueCollection; use VeeWee\Xml\Xsd\Schema\Schema; use VeeWee\Xml\Xsd\Schema\SchemaCollection; @@ -14,17 +14,17 @@ /** * @param list $schemaManipulators - * @return Closure(\DOM\XMLDocument): IssueCollection + * @return Closure(\Dom\XMLDocument): IssueCollection */ function internal_xsd_validator(callable ... $schemaManipulators): Closure { - return static function (\DOM\XMLDocument $document) use ($schemaManipulators) : IssueCollection { + return static function (\Dom\XMLDocument $document) use ($schemaManipulators) : IssueCollection { $schemas = configure(...$schemaManipulators)(locate_all_xsd_schemas($document)); return validator_chain( ...$schemas->map( /** - * @return Closure(\DOM\XMLDocument): IssueCollection + * @return Closure(\Dom\XMLDocument): IssueCollection */ static fn (Schema $schema): Closure => xsd_validator($schema->location()) ) diff --git a/src/Xml/Dom/Validator/validator_chain.php b/src/Xml/Dom/Validator/validator_chain.php index dbf3c92..773de1c 100644 --- a/src/Xml/Dom/Validator/validator_chain.php +++ b/src/Xml/Dom/Validator/validator_chain.php @@ -5,21 +5,21 @@ namespace VeeWee\Xml\Dom\Validator; use Closure; -use \DOM\XMLDocument; +use \Dom\XMLDocument; use VeeWee\Xml\ErrorHandling\Issue\IssueCollection; use function Psl\Iter\reduce; /** - * @param list $validators - * @return Closure(\DOM\XMLDocument): IssueCollection + * @param list $validators + * @return Closure(\Dom\XMLDocument): IssueCollection */ function validator_chain(callable ... $validators): Closure { - return static fn (\DOM\XMLDocument $document): IssueCollection => + return static fn (\Dom\XMLDocument $document): IssueCollection => reduce( $validators, /** - * @param callable(\DOM\XMLDocument): IssueCollection $validator + * @param callable(\Dom\XMLDocument): IssueCollection $validator */ static fn (IssueCollection $issues, callable $validator): IssueCollection => new IssueCollection( diff --git a/src/Xml/Dom/Validator/xsd_validator.php b/src/Xml/Dom/Validator/xsd_validator.php index 6d4e97a..6b87250 100644 --- a/src/Xml/Dom/Validator/xsd_validator.php +++ b/src/Xml/Dom/Validator/xsd_validator.php @@ -5,16 +5,16 @@ namespace VeeWee\Xml\Dom\Validator; use Closure; -use \DOM\XMLDocument; +use \Dom\XMLDocument; use VeeWee\Xml\ErrorHandling\Issue\IssueCollection; use function VeeWee\Xml\ErrorHandling\detect_issues; /** - * @return Closure(\DOM\XMLDocument): IssueCollection + * @return Closure(\Dom\XMLDocument): IssueCollection */ function xsd_validator(string $xsd): Closure { - return static function (\DOM\XMLDocument $document) use ($xsd): IssueCollection { + return static function (\Dom\XMLDocument $document) use ($xsd): IssueCollection { [$_, $issues] = detect_issues(static fn () => $document->schemaValidate($xsd)); return $issues; diff --git a/src/Xml/Dom/Xpath.php b/src/Xml/Dom/Xpath.php index f75467b..6684181 100644 --- a/src/Xml/Dom/Xpath.php +++ b/src/Xml/Dom/Xpath.php @@ -4,8 +4,8 @@ namespace VeeWee\Xml\Dom; -use \DOM\Node; -use \DOM\XPath as DOMXPath; +use \Dom\Node; +use \Dom\XPath as DOMXPath; use InvalidArgumentException; use Psl\Type\TypeInterface; use VeeWee\Xml\Dom\Collection\NodeList; @@ -38,7 +38,7 @@ public static function fromDocument(Document $document, callable ... $configurat * @throws RuntimeException * @throws InvalidArgumentException */ - public static function fromUnsafeNode(\DOM\Node $node, callable ... $configurators): self + public static function fromUnsafeNode(\Dom\Node $node, callable ... $configurators): self { return self::fromDocument( Document::fromUnsafeDocument( @@ -62,9 +62,9 @@ public function locate(callable $locator) /** * @throws RuntimeException - * @return NodeList<\DOM\Node> + * @return NodeList<\Dom\Node> */ - public function query(string $expression, ?\DOM\Node $contextNode = null): NodeList + public function query(string $expression, ?\Dom\Node $contextNode = null): NodeList { return $this->locate(query($expression, $contextNode)); } @@ -73,7 +73,7 @@ public function query(string $expression, ?\DOM\Node $contextNode = null): NodeL * @throws RuntimeException * @throws InvalidArgumentException */ - public function querySingle(string $expression, ?\DOM\Node $contextNode = null): \DOM\Node + public function querySingle(string $expression, ?\Dom\Node $contextNode = null): \Dom\Node { return $this->locate(query_single($expression, $contextNode)); } @@ -86,7 +86,7 @@ public function querySingle(string $expression, ?\DOM\Node $contextNode = null): * @return T * @throws RuntimeException */ - public function evaluate(string $expression, TypeInterface $type, ?\DOM\Node $contextNode = null) + public function evaluate(string $expression, TypeInterface $type, ?\Dom\Node $contextNode = null) { return $this->locate(evaluate($expression, $type, $contextNode)); } diff --git a/src/Xml/Dom/Xpath/Configurator/Configurator.php b/src/Xml/Dom/Xpath/Configurator/Configurator.php index 23d3c2f..704a9d3 100644 --- a/src/Xml/Dom/Xpath/Configurator/Configurator.php +++ b/src/Xml/Dom/Xpath/Configurator/Configurator.php @@ -4,9 +4,9 @@ namespace VeeWee\Xml\Dom\Xpath\Configurator; -use \DOM\XPath; +use \Dom\XPath; interface Configurator { - public function __invoke(\DOM\XPath $xpath): \DOM\XPath; + public function __invoke(\Dom\XPath $xpath): \Dom\XPath; } diff --git a/src/Xml/Dom/Xpath/Configurator/all_functions.php b/src/Xml/Dom/Xpath/Configurator/all_functions.php index 4ab487e..b400a51 100644 --- a/src/Xml/Dom/Xpath/Configurator/all_functions.php +++ b/src/Xml/Dom/Xpath/Configurator/all_functions.php @@ -5,14 +5,14 @@ namespace VeeWee\Xml\Dom\Xpath\Configurator; use Closure; -use \DOM\XPath; +use \Dom\XPath; /** - * @return Closure(\DOM\XPath): \DOM\XPath + * @return Closure(\Dom\XPath): \Dom\XPath */ function all_functions(): Closure { - return static function (\DOM\XPath $xpath): \DOM\XPath { + return static function (\Dom\XPath $xpath): \Dom\XPath { php_namespace()($xpath); $xpath->registerPhpFunctions(); diff --git a/src/Xml/Dom/Xpath/Configurator/functions.php b/src/Xml/Dom/Xpath/Configurator/functions.php index ad43c69..9157bd2 100644 --- a/src/Xml/Dom/Xpath/Configurator/functions.php +++ b/src/Xml/Dom/Xpath/Configurator/functions.php @@ -5,16 +5,16 @@ namespace VeeWee\Xml\Dom\Xpath\Configurator; use Closure; -use \DOM\XPath; +use \Dom\XPath; /** * @param non-empty-list $functions * - * @return Closure(\DOM\XPath): \DOM\XPath + * @return Closure(\Dom\XPath): \Dom\XPath */ function functions(array $functions): Closure { - return static function (\DOM\XPath $xpath) use ($functions) : \DOM\XPath { + return static function (\Dom\XPath $xpath) use ($functions) : \Dom\XPath { php_namespace()($xpath); $xpath->registerPhpFunctions($functions); diff --git a/src/Xml/Dom/Xpath/Configurator/namespaces.php b/src/Xml/Dom/Xpath/Configurator/namespaces.php index e5d9a22..99f4500 100644 --- a/src/Xml/Dom/Xpath/Configurator/namespaces.php +++ b/src/Xml/Dom/Xpath/Configurator/namespaces.php @@ -5,16 +5,16 @@ namespace VeeWee\Xml\Dom\Xpath\Configurator; use Closure; -use \DOM\XPath; +use \Dom\XPath; /** * @param array $namespaces * - * @return Closure(\DOM\XPath): \DOM\XPath + * @return Closure(\Dom\XPath): \Dom\XPath */ function namespaces(array $namespaces): Closure { - return static function (\DOM\XPath $xpath) use ($namespaces) : \DOM\XPath { + return static function (\Dom\XPath $xpath) use ($namespaces) : \Dom\XPath { foreach ($namespaces as $prefix => $namespaceURI) { $xpath->registerNamespace($prefix, $namespaceURI); } diff --git a/src/Xml/Dom/Xpath/Configurator/php_namespace.php b/src/Xml/Dom/Xpath/Configurator/php_namespace.php index 91eedd8..ed9d06d 100644 --- a/src/Xml/Dom/Xpath/Configurator/php_namespace.php +++ b/src/Xml/Dom/Xpath/Configurator/php_namespace.php @@ -5,15 +5,15 @@ namespace VeeWee\Xml\Dom\Xpath\Configurator; use Closure; -use \DOM\XPath; +use \Dom\XPath; use VeeWee\Xml\Xmlns\Xmlns; /** - * @return Closure(\DOM\XPath): \DOM\XPath + * @return Closure(\Dom\XPath): \Dom\XPath */ function php_namespace(): Closure { - return static function (\DOM\XPath $xpath): \DOM\XPath { + return static function (\Dom\XPath $xpath): \Dom\XPath { namespaces(['php' => Xmlns::phpXpath()->value()])($xpath); return $xpath; diff --git a/src/Xml/Dom/Xpath/Locator/Locator.php b/src/Xml/Dom/Xpath/Locator/Locator.php index c59dacc..5590d2f 100644 --- a/src/Xml/Dom/Xpath/Locator/Locator.php +++ b/src/Xml/Dom/Xpath/Locator/Locator.php @@ -4,7 +4,7 @@ namespace VeeWee\Xml\Dom\Xpath\Locator; -use \DOM\XPath; +use \Dom\XPath; /** * @template T @@ -14,5 +14,5 @@ interface Locator /** * @return T */ - public function __invoke(\DOM\XPath $xpath): mixed; + public function __invoke(\Dom\XPath $xpath): mixed; } diff --git a/src/Xml/Dom/Xpath/Locator/evaluate.php b/src/Xml/Dom/Xpath/Locator/evaluate.php index bc6b194..91bf702 100644 --- a/src/Xml/Dom/Xpath/Locator/evaluate.php +++ b/src/Xml/Dom/Xpath/Locator/evaluate.php @@ -4,8 +4,8 @@ namespace VeeWee\Xml\Dom\Xpath\Locator; use Closure; -use \DOM\Node; -use \DOM\XPath; +use \Dom\Node; +use \Dom\XPath; use Psl\Type\TypeInterface; use function VeeWee\Xml\ErrorHandling\disallow_issues; use function VeeWee\Xml\ErrorHandling\disallow_libxml_false_returns; @@ -15,15 +15,15 @@ * * @param TypeInterface $type * - * @return Closure(\DOM\XPath): T + * @return Closure(\Dom\XPath): T */ -function evaluate(string $query, TypeInterface $type, ?\DOM\Node $node = null): Closure +function evaluate(string $query, TypeInterface $type, ?\Dom\Node $node = null): Closure { return /** * @return T */ - static function (\DOM\XPath $xpath) use ($query, $node, $type) { + static function (\Dom\XPath $xpath) use ($query, $node, $type) { $node = $node ?? $xpath->document->documentElement; return disallow_issues( diff --git a/src/Xml/Dom/Xpath/Locator/query.php b/src/Xml/Dom/Xpath/Locator/query.php index f7450a2..4fd8e49 100644 --- a/src/Xml/Dom/Xpath/Locator/query.php +++ b/src/Xml/Dom/Xpath/Locator/query.php @@ -4,20 +4,20 @@ namespace VeeWee\Xml\Dom\Xpath\Locator; use Closure; -use \DOM\Node; -use \DOM\NodeList as DOMNodeList; -use \DOM\XPath; +use \Dom\Node; +use \Dom\NodeList as DOMNodeList; +use \Dom\XPath; use VeeWee\Xml\Dom\Collection\NodeList; use function VeeWee\Xml\Dom\Assert\assert_dom_node_list; use function VeeWee\Xml\ErrorHandling\disallow_issues; use function VeeWee\Xml\ErrorHandling\disallow_libxml_false_returns; /** - * @return Closure(\DOM\XPath): NodeList<\DOM\Node> + * @return Closure(\Dom\XPath): NodeList<\Dom\Node> */ -function query(string $query, ?\DOM\Node $node = null): Closure +function query(string $query, ?\Dom\Node $node = null): Closure { - return static function (\DOM\XPath $xpath) use ($query, $node): NodeList { + return static function (\Dom\XPath $xpath) use ($query, $node): NodeList { $node = $node ?? $xpath->document->documentElement; $list = disallow_issues( diff --git a/src/Xml/Dom/Xpath/Locator/query_single.php b/src/Xml/Dom/Xpath/Locator/query_single.php index 7899f72..96657f3 100644 --- a/src/Xml/Dom/Xpath/Locator/query_single.php +++ b/src/Xml/Dom/Xpath/Locator/query_single.php @@ -4,9 +4,9 @@ namespace VeeWee\Xml\Dom\Xpath\Locator; use Closure; -use \DOM\Node; -use \DOM\NodeList; -use \DOM\XPath; +use \Dom\Node; +use \Dom\NodeList; +use \Dom\XPath; use InvalidArgumentException; use VeeWee\Xml\Exception\RuntimeException; use Webmozart\Assert\Assert; @@ -16,19 +16,19 @@ use function VeeWee\Xml\ErrorHandling\disallow_libxml_false_returns; /** - * @return Closure(\DOM\XPath): \DOM\Node + * @return Closure(\Dom\XPath): \Dom\Node */ -function query_single(string $query, ?\DOM\Node $node = null): Closure +function query_single(string $query, ?\Dom\Node $node = null): Closure { return /** * @throws InvalidArgumentException * @throws RuntimeException */ - static function (\DOM\XPath $xpath) use ($query, $node): \DOM\Node { + static function (\Dom\XPath $xpath) use ($query, $node): \Dom\Node { $node = $node ?? $xpath->document->documentElement; $list = disallow_issues( - static fn (): \DOM\NodeList => assert_dom_node_list( + static fn (): \Dom\NodeList => assert_dom_node_list( disallow_libxml_false_returns( $xpath->query($query, $node), 'Failed querying XPath query: '.$query diff --git a/src/Xml/Encoding/Internal/Decoder/Builder/attribute.php b/src/Xml/Encoding/Internal/Decoder/Builder/attribute.php index 6fa766c..9ce2aa0 100644 --- a/src/Xml/Encoding/Internal/Decoder/Builder/attribute.php +++ b/src/Xml/Encoding/Internal/Decoder/Builder/attribute.php @@ -4,12 +4,12 @@ namespace VeeWee\Xml\Encoding\Internal\Decoder\Builder; -use \DOM\Attr; +use \Dom\Attr; /** * @psalm-internal VeeWee\Xml\Encoding */ -function attribute(\DOM\Attr $attribute): array +function attribute(\Dom\Attr $attribute): array { return [ name($attribute) => $attribute->nodeValue, diff --git a/src/Xml/Encoding/Internal/Decoder/Builder/attributes.php b/src/Xml/Encoding/Internal/Decoder/Builder/attributes.php index af1f859..8c6fd3e 100644 --- a/src/Xml/Encoding/Internal/Decoder/Builder/attributes.php +++ b/src/Xml/Encoding/Internal/Decoder/Builder/attributes.php @@ -4,8 +4,8 @@ namespace VeeWee\Xml\Encoding\Internal\Decoder\Builder; -use \DOM\Attr; -use \DOM\Element; +use \Dom\Attr; +use \Dom\Element; use function Psl\Dict\filter; use function Psl\Dict\merge; use function Psl\Iter\reduce; @@ -15,12 +15,12 @@ /** * @psalm-internal VeeWee\Xml\Encoding */ -function attributes(\DOM\Element $element): array +function attributes(\Dom\Element $element): array { return filter([ '@attributes' => reduce( - attributes_list($element)->filter(static fn(\DOM\Attr $attr): bool => !is_xmlns_attribute($attr)), - static fn (array $attributes, \DOM\Attr $attr): array + attributes_list($element)->filter(static fn(\Dom\Attr $attr): bool => !is_xmlns_attribute($attr)), + static fn (array $attributes, \Dom\Attr $attr): array => merge($attributes, attribute($attr)), [] ) diff --git a/src/Xml/Encoding/Internal/Decoder/Builder/element.php b/src/Xml/Encoding/Internal/Decoder/Builder/element.php index c79047c..557bb56 100644 --- a/src/Xml/Encoding/Internal/Decoder/Builder/element.php +++ b/src/Xml/Encoding/Internal/Decoder/Builder/element.php @@ -4,7 +4,7 @@ namespace VeeWee\Xml\Encoding\Internal\Decoder\Builder; -use \DOM\Element; +use \Dom\Element; use VeeWee\Xml\Exception\RuntimeException; use function Psl\Dict\filter; use function Psl\Dict\merge; @@ -14,7 +14,7 @@ * @return array * @throws RuntimeException */ -function element(\DOM\Element $element): array +function element(\Dom\Element $element): array { $name = name($element); $children = grouped_children($element); diff --git a/src/Xml/Encoding/Internal/Decoder/Builder/group_child_elements.php b/src/Xml/Encoding/Internal/Decoder/Builder/group_child_elements.php index e1567f5..2ad94de 100644 --- a/src/Xml/Encoding/Internal/Decoder/Builder/group_child_elements.php +++ b/src/Xml/Encoding/Internal/Decoder/Builder/group_child_elements.php @@ -4,15 +4,15 @@ namespace VeeWee\Xml\Encoding\Internal\Decoder\Builder; -use \DOM\Element; +use \Dom\Element; use function VeeWee\Xml\Dom\Locator\Element\children; /** - * @psalm-type GroupedElements=array> + * @psalm-type GroupedElements=array> * @psalm-internal VeeWee\Xml\Encoding * @return GroupedElements */ -function group_child_elements(\DOM\Element $element): array +function group_child_elements(\Dom\Element $element): array { /** @var GroupedElements $grouped */ $grouped = []; diff --git a/src/Xml/Encoding/Internal/Decoder/Builder/grouped_children.php b/src/Xml/Encoding/Internal/Decoder/Builder/grouped_children.php index 03c2be2..cb083ba 100644 --- a/src/Xml/Encoding/Internal/Decoder/Builder/grouped_children.php +++ b/src/Xml/Encoding/Internal/Decoder/Builder/grouped_children.php @@ -4,7 +4,7 @@ namespace VeeWee\Xml\Encoding\Internal\Decoder\Builder; -use \DOM\Element; +use \Dom\Element; use function Psl\Dict\map; use function Psl\Dict\merge; use function Psl\Iter\reduce_with_keys; @@ -13,21 +13,21 @@ * @psalm-internal VeeWee\Xml\Encoding * */ -function grouped_children(\DOM\Element $element): array +function grouped_children(\Dom\Element $element): array { return reduce_with_keys( group_child_elements($element), /** * @param array $children - * @param \DOM\Element|list<\DOM\Element> $child + * @param \Dom\Element|list<\Dom\Element> $child * @return array */ - static fn (array $children, string $name, \DOM\Element|array $child): array + static fn (array $children, string $name, \Dom\Element|array $child): array => merge( $children, [ $name => is_array($child) - ? [...map($child, static fn (\DOM\Element $child): array|string + ? [...map($child, static fn (\Dom\Element $child): array|string => unwrap_element(element($child)))] : unwrap_element(element($child)) ] diff --git a/src/Xml/Encoding/Internal/Decoder/Builder/name.php b/src/Xml/Encoding/Internal/Decoder/Builder/name.php index 9c13b1b..3daa510 100644 --- a/src/Xml/Encoding/Internal/Decoder/Builder/name.php +++ b/src/Xml/Encoding/Internal/Decoder/Builder/name.php @@ -4,12 +4,12 @@ namespace VeeWee\Xml\Encoding\Internal\Decoder\Builder; -use \DOM\Node; +use \Dom\Node; /** * @psalm-internal VeeWee\Xml\Encoding */ -function name(\DOM\Node $node): string +function name(\Dom\Node $node): string { return $node->nodeName; } diff --git a/src/Xml/Encoding/Internal/Decoder/Builder/namespaces.php b/src/Xml/Encoding/Internal/Decoder/Builder/namespaces.php index 05232af..d216d12 100644 --- a/src/Xml/Encoding/Internal/Decoder/Builder/namespaces.php +++ b/src/Xml/Encoding/Internal/Decoder/Builder/namespaces.php @@ -4,7 +4,7 @@ namespace VeeWee\Xml\Encoding\Internal\Decoder\Builder; -use \DOM\Element; +use \Dom\Element; use VeeWee\Xml\Exception\RuntimeException; use function Psl\Dict\filter; use function Psl\Dict\merge; @@ -15,11 +15,11 @@ * @psalm-suppress RedundantCast * @throws RuntimeException */ -function namespaces(\DOM\Element $element): array +function namespaces(\Dom\Element $element): array { return filter([ '@namespaces' => xmlns_attributes_list($element)->reduce( - static fn (array $namespaces, \DOM\Attr $node) + static fn (array $namespaces, \Dom\Attr $node) => $node->value ? merge($namespaces, [ ($node->prefix !== null ? $node->localName : '') => $node->value diff --git a/src/Xml/Encoding/Internal/Encoder/Builder/children.php b/src/Xml/Encoding/Internal/Encoder/Builder/children.php index 9664b06..0053ac9 100644 --- a/src/Xml/Encoding/Internal/Encoder/Builder/children.php +++ b/src/Xml/Encoding/Internal/Encoder/Builder/children.php @@ -5,7 +5,7 @@ namespace VeeWee\Xml\Encoding\Internal\Encoder\Builder; use Closure; -use \DOM\Element; +use \Dom\Element; use function Psl\Dict\map; use function VeeWee\Xml\Dom\Builder\children as buildChildren; use function VeeWee\Xml\Dom\Builder\element as elementBuilder; @@ -16,7 +16,7 @@ * * @psalm-suppress LessSpecificReturnStatement, MoreSpecificReturnType * - * @return Closure(\DOM\Element): \DOM\Element + * @return Closure(\Dom\Element): \Dom\Element */ function children(string $name, array $children): Closure { @@ -24,7 +24,7 @@ function children(string $name, array $children): Closure ...map( $children, /** - * @return Closure(\DOM\Element): \DOM\Element + * @return Closure(\Dom\Element): \Dom\Element */ static fn (array|string $data): Closure => is_array($data) ? element($name, $data) diff --git a/src/Xml/Encoding/Internal/Encoder/Builder/element.php b/src/Xml/Encoding/Internal/Encoder/Builder/element.php index f09da83..40a29f2 100644 --- a/src/Xml/Encoding/Internal/Encoder/Builder/element.php +++ b/src/Xml/Encoding/Internal/Encoder/Builder/element.php @@ -5,7 +5,7 @@ namespace VeeWee\Xml\Encoding\Internal\Encoder\Builder; use Closure; -use \DOM\Element; +use \Dom\Element; use Psl\Exception\InvariantViolationException; use Psl\Type\Exception\AssertException; use function Psl\Dict\filter_keys; @@ -28,7 +28,7 @@ * @psalm-internal VeeWee\Xml\Encoding * @psalm-suppress LessSpecificReturnStatement, MoreSpecificReturnType * - * @return Closure(\DOM\Element): \DOM\Element + * @return Closure(\Dom\Element): \Dom\Element * * @throws AssertException * @throws InvariantViolationException @@ -49,7 +49,7 @@ function element(string $name, array $data): Closure $currentNamespace = $namespaces[''] ?? null; $namedNamespaces = filter_keys($namespaces ?? []); - /** @var list $children */ + /** @var list $children */ $children = filter_nulls([ $attributes !== null ? attributes($attributes) : null, $namedNamespaces ? xmlns_attributes($namedNamespaces) : null, @@ -59,7 +59,7 @@ function element(string $name, array $data): Closure $element, /** * @param string|array $value - * @return Closure(\DOM\Element): \DOM\Element + * @return Closure(\Dom\Element): \Dom\Element */ static fn (string $name, string|array $value): Closure => parent_node($name, $value) diff --git a/src/Xml/Encoding/Internal/Encoder/Builder/parent_node.php b/src/Xml/Encoding/Internal/Encoder/Builder/parent_node.php index d44a568..58eb5fc 100644 --- a/src/Xml/Encoding/Internal/Encoder/Builder/parent_node.php +++ b/src/Xml/Encoding/Internal/Encoder/Builder/parent_node.php @@ -5,8 +5,8 @@ namespace VeeWee\Xml\Encoding\Internal\Encoder\Builder; use Closure; -use \DOM\Element; -use \DOM\Node; +use \Dom\Element; +use \Dom\Node; use Psl\Exception\InvariantViolationException; use Psl\Type\Exception\AssertException; use function VeeWee\Xml\Dom\Builder\children as buildChildren; @@ -17,7 +17,7 @@ * @psalm-internal VeeWee\Xml\Encoding * @psalm-suppress LessSpecificReturnStatement, MoreSpecificReturnType * - * @return Closure(\DOM\Node): \DOM\Element + * @return Closure(\Dom\Node): \Dom\Element * * @throws AssertException * @throws InvariantViolationException diff --git a/src/Xml/Encoding/Internal/Encoder/Builder/root.php b/src/Xml/Encoding/Internal/Encoder/Builder/root.php index 14fd996..4a8068a 100644 --- a/src/Xml/Encoding/Internal/Encoder/Builder/root.php +++ b/src/Xml/Encoding/Internal/Encoder/Builder/root.php @@ -5,8 +5,8 @@ namespace VeeWee\Xml\Encoding\Internal\Encoder\Builder; use Closure; -use \DOM\XMLDocument; -use \DOM\Node; +use \Dom\XMLDocument; +use \Dom\Node; use Psl\Exception\InvariantViolationException; use VeeWee\Xml\Encoding\Exception\EncodingException; use function Psl\Dict\map_with_key; @@ -14,7 +14,7 @@ /** * @psalm-internal VeeWee\Xml\Encoding - * @return Closure(\DOM\XMLDocument): list<\DOM\Node> + * @return Closure(\Dom\XMLDocument): list<\Dom\Node> * * @throws EncodingException * @throws InvariantViolationException diff --git a/src/Xml/Encoding/document_encode.php b/src/Xml/Encoding/document_encode.php index ca633f9..17ed93b 100644 --- a/src/Xml/Encoding/document_encode.php +++ b/src/Xml/Encoding/document_encode.php @@ -4,7 +4,7 @@ namespace VeeWee\Xml\Encoding; -use \DOM\XMLDocument as DOMDocument; +use \Dom\XMLDocument as DOMDocument; use VeeWee\Xml\Dom\Document; use VeeWee\Xml\Encoding\Exception\EncodingException; use function VeeWee\Xml\Encoding\Internal\Encoder\Builder\normalize_data; diff --git a/src/Xml/Encoding/element_decode.php b/src/Xml/Encoding/element_decode.php index f905258..82af963 100644 --- a/src/Xml/Encoding/element_decode.php +++ b/src/Xml/Encoding/element_decode.php @@ -4,8 +4,8 @@ namespace VeeWee\Xml\Encoding; -use \DOM\XMLDocument as DOMDocument; -use \DOM\Element; +use \Dom\XMLDocument as DOMDocument; +use \Dom\Element; use VeeWee\Xml\Dom\Document; use VeeWee\Xml\Encoding\Exception\EncodingException; use function VeeWee\Xml\Dom\Locator\document_element; @@ -17,7 +17,7 @@ * * @throws EncodingException */ -function element_decode(\DOM\Element $element, callable ... $configurators): array +function element_decode(\Dom\Element $element, callable ... $configurators): array { return wrap_exception( static function () use ($element, $configurators): array { diff --git a/src/Xml/Encoding/element_encode.php b/src/Xml/Encoding/element_encode.php index 072a289..81866c5 100644 --- a/src/Xml/Encoding/element_encode.php +++ b/src/Xml/Encoding/element_encode.php @@ -4,7 +4,7 @@ namespace VeeWee\Xml\Encoding; -use \DOM\XMLDocument as DOMDocument; +use \Dom\XMLDocument as DOMDocument; use VeeWee\Xml\Encoding\Exception\EncodingException; use function VeeWee\Xml\Dom\Locator\document_element; use function VeeWee\Xml\Dom\Mapper\xml_string; diff --git a/src/Xml/Encoding/typed.php b/src/Xml/Encoding/typed.php index aee6199..feefb7f 100644 --- a/src/Xml/Encoding/typed.php +++ b/src/Xml/Encoding/typed.php @@ -4,7 +4,7 @@ namespace VeeWee\Xml\Encoding; -use \DOM\XMLDocument; +use \Dom\XMLDocument; use Psl\Type\Exception\CoercionException; use Psl\Type\TypeInterface; use VeeWee\Xml\Encoding\Exception\EncodingException; @@ -14,7 +14,7 @@ * * @psalm-param non-empty-string $xml * @psalm-param TypeInterface $type - * @param list $configurators + * @param list $configurators * * @return T * diff --git a/src/Xml/Encoding/xml_decode.php b/src/Xml/Encoding/xml_decode.php index a3d6702..937e335 100644 --- a/src/Xml/Encoding/xml_decode.php +++ b/src/Xml/Encoding/xml_decode.php @@ -4,7 +4,7 @@ namespace VeeWee\Xml\Encoding; -use \DOM\XMLDocument as DOMDocument; +use \Dom\XMLDocument as DOMDocument; use VeeWee\Xml\Dom\Document; use VeeWee\Xml\Encoding\Exception\EncodingException; use function VeeWee\Xml\Dom\Locator\document_element; diff --git a/src/Xml/Encoding/xml_encode.php b/src/Xml/Encoding/xml_encode.php index 2da3896..8fe6a07 100644 --- a/src/Xml/Encoding/xml_encode.php +++ b/src/Xml/Encoding/xml_encode.php @@ -4,7 +4,7 @@ namespace VeeWee\Xml\Encoding; -use \DOM\XMLDocument as DOMDocument; +use \Dom\XMLDocument as DOMDocument; use VeeWee\Xml\Encoding\Exception\EncodingException; use function VeeWee\Xml\Encoding\Internal\wrap_exception; diff --git a/src/Xml/Reader/MatchingNode.php b/src/Xml/Reader/MatchingNode.php index 5c32bc6..c51b5c5 100644 --- a/src/Xml/Reader/MatchingNode.php +++ b/src/Xml/Reader/MatchingNode.php @@ -3,7 +3,7 @@ namespace VeeWee\Xml\Reader; -use \DOM\XMLDocument as DOMDocument; +use \Dom\XMLDocument as DOMDocument; use VeeWee\Xml\Dom\Document; use VeeWee\Xml\Encoding\Exception\EncodingException; use VeeWee\Xml\Exception\RuntimeException; diff --git a/stubs/DOM.phpstub b/stubs/DOM.phpstub index 1208b34..8f43cce 100644 --- a/stubs/DOM.phpstub +++ b/stubs/DOM.phpstub @@ -94,14 +94,6 @@ namespace * @cvalue XML_NAMESPACE_DECL */ const XML_NAMESPACE_DECL_NODE = UNKNOWN; -#ifdef XML_GLOBAL_NAMESPACE - /** - * @var int - * @cvalue XML_GLOBAL_NAMESPACE - */ - const XML_GLOBAL_NAMESPACE = UNKNOWN; -#endif - /** * @var int * @cvalue XML_LOCAL_NAMESPACE @@ -889,7 +881,7 @@ namespace public function replaceChildren(...$nodes): void {} } - /** @alias DOM\DOMException */ + /** @alias Dom\DOMException */ final class DOMException extends Exception { /** @@ -1025,11 +1017,8 @@ namespace function dom_import_simplexml(object $node): DOMElement {} } -namespace DOM +namespace Dom { - - use Psl\Iter\Iterator; - /** * @var int * @cvalue INDEX_SIZE_ERR @@ -1117,6 +1106,9 @@ namespace DOM public function append(Node|string ...$nodes): void; public function prepend(Node|string ...$nodes): void; public function replaceChildren(Node|string ...$nodes): void; + + public function querySelector(string $selectors): ?Element; + public function querySelectorAll(string $selectors): NodeList; } interface ChildNode @@ -1267,14 +1259,11 @@ namespace DOM /** @implementation-alias DOMNamedNodeMap::count */ public function count(): int {} - /** - * @implementation-alias DOMNamedNodeMap::getIterator - * @psalm-return iterable - */ + /** @implementation-alias DOMNamedNodeMap::getIterator */ public function getIterator(): \Iterator {} } - class DTDNamedNodeMap implements \IteratorAggregate, \Countable + class DtdNamedNodeMap implements \IteratorAggregate, \Countable { /** @readonly */ public int $length; @@ -1304,7 +1293,7 @@ namespace DOM /** @implementation-alias DOMNodeList::item */ public function item(int $index): ?Element {} - /* TODO: implement namedItem */ + public function namedItem(string $key): ?Element {} /** @implementation-alias DOMNodeList::count */ public function count(): int {} @@ -1316,6 +1305,14 @@ namespace DOM public function getIterator(): \Iterator {} } + enum AdjacentPosition : string + { + case BeforeBegin = "beforebegin"; + case AfterBegin = "afterbegin"; + case BeforeEnd = "beforeend"; + case AfterEnd = "afterend"; + } + class Element extends Node implements ParentNode, ChildNode { /** @readonly */ @@ -1329,6 +1326,8 @@ namespace DOM public string $id; public string $className; + /** @readonly */ + public TokenList $classList; /** @implementation-alias DOMNode::hasAttributes */ public function hasAttributes(): bool {} @@ -1358,7 +1357,7 @@ namespace DOM public function getAttributeNode(string $qualifiedName): ?Attr {} /** @implementation-alias DOMElement::getAttributeNodeNS */ public function getAttributeNodeNS(?string $namespace, string $localName): ?Attr {} - /** @implementation-alias DOM\Element::setAttributeNodeNS */ + /** @implementation-alias Dom\Element::setAttributeNodeNS */ public function setAttributeNode(Attr $attr) : ?Attr {} public function setAttributeNodeNS(Attr $attr) : ?Attr {} public function removeAttributeNode(Attr $attr) : Attr {} @@ -1366,9 +1365,8 @@ namespace DOM public function getElementsByTagName(string $qualifiedName): HTMLCollection {} public function getElementsByTagNameNS(?string $namespace, string $localName): HTMLCollection {} - public function insertAdjacentElement(string $where, Element $element): ?Element {} - /** @implementation-alias DOMElement::insertAdjacentText */ - public function insertAdjacentText(string $where, string $data): void {} + public function insertAdjacentElement(AdjacentPosition $where, Element $element): ?Element {} + public function insertAdjacentText(AdjacentPosition $where, string $data): void {} /** @readonly */ public ?Element $firstElementChild; @@ -1402,6 +1400,13 @@ namespace DOM /** @implementation-alias DOMElement::replaceChildren */ public function replaceChildren(Node|string ...$nodes): void {} + public function querySelector(string $selectors): ?Element {} + public function querySelectorAll(string $selectors): NodeList {} + public function closest(string $selectors): ?Element {} + public function matches(string $selectors): bool {} + + public string $innerHTML; + public string $substitutedNodeValue; /** @return list */ @@ -1413,6 +1418,10 @@ namespace DOM public function rename(?string $namespaceURI, string $qualifiedName): void {} } + class HTMLElement extends Element + { + } + class Attr extends Node { /** @readonly */ @@ -1434,7 +1443,7 @@ namespace DOM /** @implementation-alias DOMAttr::isId */ public function isId(): bool {} - /** @implementation-alias DOM\Element::rename */ + /** @implementation-alias Dom\Element::rename */ public function rename(?string $namespaceURI, string $qualifiedName): void {} } @@ -1493,9 +1502,9 @@ namespace DOM /** @readonly */ public string $name; /** @readonly */ - public DTDNamedNodeMap $entities; + public DtdNamedNodeMap $entities; /** @readonly */ - public DTDNamedNodeMap $notations; + public DtdNamedNodeMap $notations; /** @readonly */ public string $publicId; /** @readonly */ @@ -1523,13 +1532,18 @@ namespace DOM public int $childElementCount; /** @implementation-alias DOMDocumentFragment::appendXML */ - public function appendXML(string $data): bool {} + public function appendXml(string $data): bool {} /** @implementation-alias DOMElement::append */ public function append(Node|string ...$nodes): void {} /** @implementation-alias DOMElement::prepend */ public function prepend(Node|string ...$nodes): void {} /** @implementation-alias DOMElement::replaceChildren */ public function replaceChildren(Node|string ...$nodes): void {} + + /** @implementation-alias Dom\Element::querySelector */ + public function querySelector(string $selectors): ?Element {} + /** @implementation-alias Dom\Element::querySelectorAll */ + public function querySelectorAll(string $selectors): NodeList {} } class Entity extends Node @@ -1566,9 +1580,9 @@ namespace DOM public ?DocumentType $doctype; /** @readonly */ public ?Element $documentElement; - /** @implementation-alias DOM\Element::getElementsByTagName */ + /** @implementation-alias Dom\Element::getElementsByTagName */ public function getElementsByTagName(string $qualifiedName): HTMLCollection {} - /** @implementation-alias DOM\Element::getElementsByTagNameNS */ + /** @implementation-alias Dom\Element::getElementsByTagNameNS */ public function getElementsByTagNameNS(?string $namespace, string $localName): HTMLCollection {} public function createElement(string $localName): Element {} @@ -1609,9 +1623,9 @@ namespace DOM /** @implementation-alias DOMDocument::schemaValidateSource */ public function schemaValidateSource(string $source, int $flags = 0): bool {} /** @implementation-alias DOMDocument::relaxNGValidate */ - public function relaxNGValidate(string $filename): bool {} + public function relaxNgValidate(string $filename): bool {} /** @implementation-alias DOMDocument::relaxNGValidateSource */ - public function relaxNGValidateSource(string $source): bool {} + public function relaxNgValidateSource(string $source): bool {} #endif /** @implementation-alias DOMElement::append */ @@ -1622,6 +1636,16 @@ namespace DOM public function replaceChildren(Node|string ...$nodes): void {} public function importLegacyNode(\DOMNode $node, bool $deep = false): Node {} + + /** @implementation-alias Dom\Element::querySelector */ + public function querySelector(string $selectors): ?Element {} + /** @implementation-alias Dom\Element::querySelectorAll */ + public function querySelectorAll(string $selectors): NodeList {} + + public ?HTMLElement $body; + /** @readonly */ + public ?HTMLElement $head; + public string $title; } final class HTMLDocument extends Document @@ -1632,15 +1656,15 @@ namespace DOM public static function createFromString(string $source, int $options = 0, ?string $overrideEncoding = null): HTMLDocument {} - /** @implementation-alias DOM\XMLDocument::saveXML */ - public function saveXML(?Node $node = null, int $options = 0): string|false {} + /** @implementation-alias Dom\XMLDocument::saveXml */ + public function saveXml(?Node $node = null, int $options = 0): string|false {} /** @implementation-alias DOMDocument::save */ - public function saveXMLFile(string $filename, int $options = 0): int|false {} + public function saveXmlFile(string $filename, int $options = 0): int|false {} - public function saveHTML(?Node $node = null): string {} + public function saveHtml(?Node $node = null): string {} - public function saveHTMLFile(string $filename): int|false {} + public function saveHtmlFile(string $filename): int|false {} } final class XMLDocument extends Document @@ -1666,26 +1690,50 @@ namespace DOM /** @implementation-alias DOMDocument::validate */ public function validate(): bool {} - /** @implementation-alias DOMDocument::xinclude */ - public function xinclude(int $options = 0): int|false {} + public function xinclude(int $options = 0): int {} - public function saveXML(?Node $node = null, int $options = 0): string|false {} + public function saveXml(?Node $node = null, int $options = 0): string|false {} /** @implementation-alias DOMDocument::save */ - public function saveXMLFile(string $filename, int $options = 0): int|false {} + public function saveXmlFile(string $filename, int $options = 0): int|false {} + } + + /** + * @not-serializable + * @strict-properties + */ + final class TokenList implements IteratorAggregate, Countable + { + /** @implementation-alias Dom\Node::__construct */ + private function __construct() {} + + /** @readonly */ + public int $length; + public function item(int $index): ?string {} + public function contains(string $token): bool {} + public function add(string ...$tokens): void {} + public function remove(string ...$tokens): void {} + public function toggle(string $token, ?bool $force = null): bool {} + public function replace(string $token, string $newToken): bool {} + public function supports(string $token): bool {} + public string $value; + + public function count(): int {} + + public function getIterator(): \Iterator {} } /** * @not-serializable * @strict-properties */ - final class NamespaceInfo + readonly final class NamespaceInfo { - public readonly ?string $prefix; - public readonly ?string $namespaceURI; - public readonly Element $element; + public ?string $prefix; + public ?string $namespaceURI; + public Element $element; - /** @implementation-alias DOM\Node::__construct */ + /** @implementation-alias Dom\Node::__construct */ private function __construct() {} } diff --git a/tests/Xml/Dom/Loader/XmlFileLoaderTest.php b/tests/Xml/Dom/Loader/XmlFileLoaderTest.php index 8dd63a0..89f0a34 100644 --- a/tests/Xml/Dom/Loader/XmlFileLoaderTest.php +++ b/tests/Xml/Dom/Loader/XmlFileLoaderTest.php @@ -45,7 +45,7 @@ public function test_it_cannot_load_invalid_xml_file(): void $loader = xml_file_loader($file); $this->expectException(RuntimeException::class); - $this->expectExceptionMessage('XML document is malformed'); + $this->expectExceptionMessage('XML fragment is not well-formed'); $doc = $loader(); fclose($handle); diff --git a/tests/Xml/Dom/Loader/XmlStringLoaderTest.php b/tests/Xml/Dom/Loader/XmlStringLoaderTest.php index 8233ce0..36bc1bd 100644 --- a/tests/Xml/Dom/Loader/XmlStringLoaderTest.php +++ b/tests/Xml/Dom/Loader/XmlStringLoaderTest.php @@ -26,7 +26,7 @@ public function test_it_can_not_load_invalid_xml_string(): void $loader = xml_string_loader($xml); $this->expectException(RuntimeException::class); - $this->expectExceptionMessage('XML document is malformed'); + $this->expectExceptionMessage('XML fragment is not well-formed'); $loader(); } diff --git a/tests/Xml/Dom/Locator/Element/ParentElementTest.php b/tests/Xml/Dom/Locator/Element/ParentElementTest.php index 4536dac..d5f165b 100644 --- a/tests/Xml/Dom/Locator/Element/ParentElementTest.php +++ b/tests/Xml/Dom/Locator/Element/ParentElementTest.php @@ -34,7 +34,7 @@ public function test_it_can_detect_parents(): void static::assertSame($hello, $domdoc->documentElement); $this->expectException(RuntimeException::class); - $this->expectExceptionMessage('Can not find parent element for DOM\Element hello'); + $this->expectExceptionMessage('Can not find parent element for Dom\Element hello'); parent_element($hello); } } diff --git a/tests/Xml/Dom/Manipulator/Node/ReplaceByExternalNodeTest.php b/tests/Xml/Dom/Manipulator/Node/ReplaceByExternalNodeTest.php index 450e882..c13d694 100644 --- a/tests/Xml/Dom/Manipulator/Node/ReplaceByExternalNodeTest.php +++ b/tests/Xml/Dom/Manipulator/Node/ReplaceByExternalNodeTest.php @@ -32,7 +32,7 @@ public function test_it_can_not_replace_a_document_into_a_document(): void $target = Document::empty()->toUnsafeDocument(); $this->expectException(RuntimeException::class); - $this->expectExceptionMessage('Could not replace a node without parent node. (DOM\XMLDocument)'); + $this->expectExceptionMessage('Could not replace a node without parent node. (Dom\XMLDocument)'); replace_by_external_node($target, $source); } diff --git a/tests/Xml/Dom/Manipulator/Node/ReplaceByExternalNodesTest.php b/tests/Xml/Dom/Manipulator/Node/ReplaceByExternalNodesTest.php index a16e822..3c1036e 100644 --- a/tests/Xml/Dom/Manipulator/Node/ReplaceByExternalNodesTest.php +++ b/tests/Xml/Dom/Manipulator/Node/ReplaceByExternalNodesTest.php @@ -49,7 +49,7 @@ public function test_it_can_not_replace_a_document_into_a_document(): void $target = Document::empty()->toUnsafeDocument(); $this->expectException(RuntimeException::class); - $this->expectExceptionMessage('Could not replace a node without parent node. (DOM\XMLDocument)'); + $this->expectExceptionMessage('Could not replace a node without parent node. (Dom\XMLDocument)'); replace_by_external_nodes($target, [$source]); } From ee6e5838b25be81e8ae81e2ab3d922072104a6ae Mon Sep 17 00:00:00 2001 From: Toon Verwerft Date: Tue, 9 Jul 2024 13:44:07 +0200 Subject: [PATCH 09/10] Bumps phive --- .phive/phars.xml | 6 +- tools/infection.phar | Bin 1011925 -> 1065467 bytes tools/php-cs-fixer.phar | Bin 2762141 -> 3192463 bytes tools/phpunit.phar | 77251 ++++++++++++++++++++++---------------- 4 files changed, 44041 insertions(+), 33216 deletions(-) diff --git a/.phive/phars.xml b/.phive/phars.xml index 1862966..3c109e8 100644 --- a/.phive/phars.xml +++ b/.phive/phars.xml @@ -1,6 +1,6 @@ - - - + + + diff --git a/tools/infection.phar b/tools/infection.phar index 257191b97d9f90200b965e27752494e0f12255e3..b9003745a383ad67d42260333063455f3f9614ff 100755 GIT binary patch delta 909410 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�!)SIj*zu>%JGjNL9mr|Zm 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 !%g_c2+I0@3lo@sq16z4 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

~&B}$V~|D!?$Sz-@8By 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�a|uDwH!G_tq|;(R&nPDzG+-E2fLO}t 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

&vL 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

{o}jP^PINnn^p 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⪼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

{{qK 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≧$El>XBA|6NSK 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�oZsIO;E= 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®TdH=Ptgz@5$bu~fx_Mg$COv7%8 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

qFo5saMh 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

(1C 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%0D~gR(~y%2PF);MUTL4g=B}!8%Q<&UXWl3k84z> zw515F#^9kcGj0(7I4aw<5^?%hACyuT9o>DqU9gr4`ddoK z*&NWMLdj&Me`#0n#x4qfH2;yFM!yIt_P$W@56#J-nE;mcr4X^vh{y|VUx%oAPO5$5 z;BO^C)ouwWAa2dbPas&qv|pjw!P~nKQ8YEg&bh#Xwe+@()17CyNz&pLL zg8Ca@cx#Oq9913C7i(#@iH{8k{85^)-@sGe+?!~9w}kl2y-mo6)j@>B{qP5`XEuo` z`RPKlwgad&jb#I-GC&oNOkuq5e@yWkrq^d;fyj78)Z2yZ#(|f3`_V$Mm7xfH3+2YU zjWhR9JvmW$k!AA@@uBvS_N_7E?dthiX8oQ~H$Ci94_&209u)Yo{j&W+oTatnh{uci z>$oO0xAdoa`gT|UTsfI_^6zbl!g4@A8r#G-O&s9$_#<>{(&ziIg@^GarRZLG>hs^J z$>W*C9wEz#%B2L~5WWM}{;W(>JEviy<_a&m7Q58ydi8vci;6gQ^^57;eJYK2P5PB% z9$ySe2D9?oopgjTj4+I9&bO$CEdFdiEVv4T#aV)?_BB$820=$t-DuWs8!3>cY#3!gMZ&9$NURk!<;XQ)z6_M?Yssn;paxh|Rzlz?GGFA4$eTTs?U&f2 zAGelj+I?V)_^a+lkU37Y;+rvn;To1Yo0z6-z~uRa%!C_~t{teboL`yWc+$T#QlWTE zqrDn^9>B@B*j7P_twXL*R=Q@n){1?`I;wtuA^${$NZDX*WjBox4O76IrG`l}cXtrs5N!Tthh`i3zO{PF+DXWm&I;q5>2mp+;&}9%BKE$upQ|rKBC?Xqh>f61qc<< zzBDu>Dkz7B?+L!Gox+5fXH&4?BEoj`LpiYdpm=8Dg2f zzJ-w#Is(IeDn%ECVB(*+`gr*QLY~0JTKHgNEQW#{DtZSKGFQ~GYXkCe)A_hbPpg?K z@^5N2`;0~c&?~N2zGb|3>#GjfT+9(OW3SG#k*%t}=eG7)?BEY`atlVaO(!uB#0)EP z^c(RR$|Nf2D^H>%Uol?J9uB(Qc1q$mIeKM@Ab~^v>n28Bj3|P zMv?4_NH7G~xXh@gXGa|Zx*Jf~<}>$CY}1qfFJ$6~mB=PiH)vE>S~UnYNF5JLmMvAD z;CBbx9m#{Y9Q`B0wzfaAR<(+h)n6~NWyLSed=$}?+dNwhghfN+Ours0FKlb|vlix@ zC8`Z(3Kb&@h9|R_f{S;mKp6O+!%%h0<#dd>sySgd|Hu~3Ukhe#-*$ zZTMTrz&G|6k6S07PxJeb?(E$3jay6$xOcS!@YbbjgkQu1QWO>99xLljA27+P3jeiJ zb{%WU*R5{lKRPsN(21cyl?B3RQCL`dK&(+vH5jRUc38W=8qWo}%6Q&wPiDw46*t#M zSsN6z=#fmi1q8CpTmiM*jPL0(($?{!)9ZE1TmfsDv(9V|ldD!dW?KIgOI>F|$*C}( zh#ZQ@mBOoun_z%f;ijbL^T@zvhvYF?Kev0=AslMdT zE$No4rda(kbnE5)k69(hf`1+rF|*P1?{0cRM`O(<6*gGQ-y?RcpLERjn=zCW&(L4? z1bo{Dh^k1~jDdJqPRK!X-=;|#kS9`Fs2anA6FIX(#d)|SyH~m0VtZ@`L=cCceBhD3 zU*s{*7BUv0!kXER1m%v_yE=W&js?h#b*xyQAzPP=lAONYCu%Z3$3=(Zwwh6dDqN%` z@xL1U$T4Yhq&>vsE!Xc@ty_(*&!zf__k&0-fHC`Q6B3A1f6Ow5@J46&Thvp;{Lb1a zg5iosqt;0{kaIri{FaaKX&dd{l){a+-$9BNJ2}3Q^q%PHqo(P-*#=llKENB0tk4fZ@9glUwAc59P*Rsl+^PSm*YRr#7wF8LxEmLMv#l z62u!xXCq`qGc60fiiAo4$QEimzDb#y#q6M0xkMpQ+4yq-6}{1cv_`@ma)BD1BS&!# z!IxK=I)`NbTlNU5Qcq5AGU8=1z~G z$S4&jf;SKA3SlKYy>JA_~SNPQjbF14da-%&dc{vBLj#rVHw?e}a$XO=$AqcZI&sujrmj7B)U z5f1iazd1gOE|HFAugDt0DS9ZxEbe_`X`K44iGDjQt(pu*N~sgE&mwTMu`rfA%mqZF zn9WmNdXKluyhF=x6{d?*T?|~rX_nO0!$eUsOa8I=rxQUY;TtfsWMS-#*H53hB+{Ah z(cZThP&RDqV*6Y3RUuTXI*8((soJ3{`CK*Y3pJX!iTJEcFS8uk`B+ zf90x0lL^Jxx#WV2;}UU@(8-0F;MBG;M~kCLE}VyT`{=58VrN9vw~Om&U>_hA-Yhn@ z`I#_Osy2*9WbVJ2d+j%MKoZ~Pkuzvbrh<1ZDovdHWTniSSej*SuStK7YzRb@HPrPW zQzVZnQ`N1A9wIO1^!>G`a&$nQ2g|AQ928dC72w~F(>do21GSBDD{uWbYO4BS1pGyn zGCNIFI|4N8^s^{!UNM7JHl-TXx$oN zHw}1NHi(a-<%GHPg0#$p|IXFTJmPtKt{CBPX57u|uXdz8)2abXn>T*3ZR9RGs4qnd znh%-D{+dvGW6D1fGP@1_y_U;9{pC2}=`315O*1eEoW+Y~x3z6@7yI!<7%4U4jN&l2 zK&Fz#&g-F(0L5$s2nM>&`7AQ4{RIg?s#I%|}C3 z$H5&;OGfLe5yx_;4iGF_dFm@?L+pN~g*+#POWIDHbHL>D#*K2Q9jFgFrwQ+ikFF9K zIO$+^#)Iy#YlppmmucT=TZR*@AJ{Dv<57){sQdb3uWsf@^4Z{(lhz~R&^VbOFG#WT zBNZq9AopZ@1nPw;fWl!qSyZvao6*KcQFn8Z6!%Pt?Qv{d|L(SNS7!C$`~j*Aruf%g z35{d8T%X!I{i}{%Ye~R+i^@Hpk)$v6QLSVQP=_a^wuZEPR~N$TjnE}r)+Hn&N2)mF zQt5g+;FW3L<{F(CmPyB++vl_Nn)r(%>};pBx=RUn0p#SY#d|fJYxuegaj@bB?t26b zH_2Kl42QTw;wBWoZVQR8#zs+f8y&DMwecTVSX-nbCIKp(ZdB1Brc%v6!j(U>TCL|{ zuqMZEH`9EM^|lVJr&EWc^kM>y$}!NhO+NH2!sjT7GZ?tj8;>!{OzVBm#`3w{M^|%q zEL-mX0A)sE9$@5Y0La53cT^eD8gTfmcIv%PpKIJf44{)DXFt{H$;+E07tigaN|Gzm3Ex=9t@BNSGi&m<7{m^ zIc`L}|8n!C+j@QgDJ25=(+jF5nV3D`n)JA^ANm)#Kg7FhUy3GAHRw1MRhUoSX`C=g z{Q$6R{}LC&;eMVu<=2vmHo8t@KmGdX$-9G0CdZ9lXa8zsrSa+LhCXZiH0HXEK;zWY z1YW>YX>K?iaH0CGXw8;4`yyu>(ihO-|EgmHA#cTz0Hq~+D@9_j$TwZUW{H1ZEq_N0lbvg+iA zZN~ROI(PeMPg+*&-~h?Rlop0Cfe97#Y&-{gBF}ICZnThZ>O=NV`={|vZ+FB?{=Mz7 zWB-CaKa1|@)X0@hanHd8|AfnK?gik6q{v7M>D6q z=nUnYZ?5`)J)4I-W9?yJ*rpaMUjT3Sk3|BfOms=cjd&7Y&`tVb!37H4d;=tUk495b z1wq$mUaYMO_o&tJSpye0&h2!Ggn7RTpv)|HUvDlwZRzbv&QZAFK<;Wm|! ztAxT)zhS)@Hdeo=+$Ov~lfv08o7S&%u%+vh49Or@Wy|?jirgNa7=1XL@)%}Jktc1k zo>02J73u1rJ7t>CrP8YFwR73((2!@mnd&E_Q@(rxOdvPbaHW@IYp&*k??G1c} zl&g2kLCzzt3wou5KL|sk+JYHx4(>pZ;YmQt!5yyM;1mMNp$~&46vh_d4APp^Z%IBT z)K;;y|A4R$K7^I)1w#i1$&gqR#9LUx+NDO6!(-L1aUN6;f&;mSW6SGU35sME{7`WTDXeE#(TQZU z^r5DYZ^-~g5IYCnAYXX%kbqfa3Iph_%2HjVB!HR{C!Xb>{Y(XuRw;XlY%hzdn&;Pq zA?HD_=ku)l5rPNKZk2ynBEu4!D_wBZH;v^@%o{Xl%x8>_K2wg@mjR&(CG9csFGd}8 zd^(nQO|CTtKP8`(LNL;tpsQoSMX8z&)F!^oqcZ~e7unBY`#l~$YjDeX78N;g-M&qg zAYgd9*%bdW9pVkAdtFMg&BqakXoMSOTUX6N&+hKQMj8@o>-e-*uxW7D+R;2WG$hW! z7VfUWd1A@vFPWRNTFmv3TMm$&sN@oViL2Uk^l^Gy<)7k@feL!GGxE}Rwfa+`++%Et zbM_7BTsGTECgUjK#>}%~fFdc_dbOBhQl3$Jy1KH(){gbFWP#xf2_0B!>eB9N83hgs zHbv2+G??p>U1<}4P?vq=i*ZvYT^>`N;ss@$W$AgSZcRLFsOS95PEqlZ1IH`&5Id-{ z`f-1D870u#PDfQhsPFTc@}%IRwOZ5A@zxJa<`CXV{*o9)CH{q3()_>mzOD1h_^3Zu z`~+v4^d(OtIN$^2)tcnvWstPl70viBX9s~fS+P{k-|hUq-$8)8`d$ehZx23gd3A;{ zM&?msv%<69uOa?M-+4(~a>1@k-iZb|53(eFP#U+SzWRmpCZGw|RTgMYcq;)xtV428 zsG8@a@U-aQRA?zyvd#e^=#VIoSH9DK31sZv8%3&k(p1sKa9K%>*$904erN1@du|1( zc{dLj&Ve77E*U_@=%xKyC(itIfv0n{`bb!c5ZjG(`GBLG-BPYfUBE;0zk z7Jol^UHnp=ZQ)Hy7!3mcZL-hntd5^X$CgEBg$+!~q0Vdci!I=v45^ltS(=V% zF~*a?#3QhUC34ZQO!gQ4p}uP|o|P~^n z$XjM9c4d~7{Ir>pJ(LB!r?ln$FVz~Yr6Jw#h$pa%di8Tb=*m}AWQ zf5!hp@HIDVjk<((nLg5vVa+F}z;VZz=8tncLwf(VreJ;yz;LkHO{Z?^J-i04Hd^Shz;>HUmn<|C|14nycqE({7%M@LKlQMTq(Xi}1hmLt%gq{Dt2Rb{3W zbV>5b_yk;d9`^B4(&+)#4n2f{pcF|tcmDZgJ@A{)n7J#YZIz63Tnz{=@UpleXP2{Q z=zhp&KgWSGh0sL9XqH-4*{U~6_*!UBBb*iCYahJa23e%^Z<6y0G0kSjpkl8A z_IxiNaTswR%Zv(bEn#R$#;mp>c>h*1*J{YJo1qpBs9TOoip#G-{+EaKU zO9f@@F+h{m(2Kgc1xORmIY!f)iC#G%DiE;16;n}PUWcLud8U_{HI_s=&n&vF;NHBm zsiGn*>ZNN1=m26{CFbgRuY*?$-R_z(Zm;X;_I!(0{ep^FFnf;G%qD-(VO;ZV^^$tx zB2r%%iPzr`LEES%tQdd)^~AZ<>^`oIOzHLfKH=l%JDw1QO8&UZP8z@z1s;xi_heF2 zN&9c|iru!CB*zvb=l}tddbXXFwCxjx6jNMcj~P5qFa|hQyF)$WB*Ich{Z1NRc9RXg zsG3SR>86vu6nc+X*r`js6AYqveW{?B&$dGp$eMky@NJ(KR%0w1j~2H_+Hxr3B+_S! z=XA^$^xGCx-pi!nvr(Z0xFA&g4u1@?Ub#+SamY0nn_8OLC*@NoRP?uEfBxo6;KdFJfR$u#wjbERh^pw!pgK zXh~^kra`}Q1ThNv&>*uX-TP?*0J=efVAYK0D5>(nO>bBnLvjJSsb#=I ze=Z{ft=k(_OJEd^6n+!^Y-b7`1KQ>>O2HZZr$W+U1_5Omq8H{EB4e6f0tS=l(Qr8- zM`wef(o&62bsSS)G{u)unrRzb<)%C0h}qD=TKB1eqz!B%# zsY~1SE8zm%6VY=m)+Tmw2(3Fifjh7u8|kdw)N98Nhdg(4_bhi$jdF}bXzJtm=rA}b zB;GY^Cdr@b2%AR44(G77NOQ>fd9^27YGFG59U?m3{=ae0$j-4EfqY?dxE=F30D?G| z1Ovb_%ts-|%sxv+DKldBZ!1NpRD*h>>*U96&+F&a)s>L0Ww3O5{us>wQCvGc2_D9YHPiyiZ-n1MnIN;+i7MIY7kBS6OK29Zht zcAAYcRwb=qi>I>S}>DtVA#SS$8bFq;FFr<0qCHFTD~AWIU@GC zlaUF(spHfV!T1dJ|B{RLMSu=5!3PWU25ltg!_*>zA0}ZHsN@@%mtP_)Bae<`GKuVw zr}W7z!!E%`G@^B87NGabhua57&OrT|F1y&D{O`K9z6t8XId-ti(MAyW!x``N1b*z2 z3q?*hz+r!Wkqzz-E7dUM^uJis!!UYIudAzbF;Z5!62p+xrm-x2g!@f@p?~vRi8)o- zqoVMw7${mqDNu9=5z71jUd2ycdWv342`^-yid#i1eOeBzi0mPG_6aliY0r?CcgOL2-vnWsADNR_F>p02n^I{#R zf3OV1kpxO?C9a^jEg;GWtL6SBQpp`}@+g^6WXT^Gij}tJQ(fkbvNL(JUzg_R_~kUP zTWwb5W@IaByT7vG$heCY3|SbDcNAqGs1I;Vu)1?Il^ zlmEDT%VX;9PkSM<&xjw4Dc%$R!`(;zZ|?rU2h^{5lDuiv3pXU&1Shc(AJsdBN-j^X zUr!bak*>ILlnY8t*PeC^8^XK6@fGPZkq}jf+sO5ncp*t# zQVc&%4ozloi5Y^DGPhH?!N*D;ho@SmJ+zz3er}{hlc3~qSA*HWDnb9t`o_j+Cd0+{ z(c=^Wh^(%ExJe3kx(mAOl&V(e+>PB6$r7cO5LEaQXaC?-GDJa z7~H%uKqji}vg6^A)5AX<d11479Es8q*IrkbjEd&(qfhr{`y{O`*yXz=@ZjwQI4Dl_Sebt`h zOze39>A9?Q+q%VpGkOW9O_oq}O`7%j#l2q6|UQ!x{WP~*&ms-tH zxu+S@wp1Z6!b3J~?l#G9MB}UU&BG5m#tyzi1Mo-v**rsQE`3f4|Ijol z1pJk zk;-@dgX%nH17=k$VqxIdH@2+COp~-LNaNU4Scp}veWQXYf&;>!J)d9nqd-?t3SbcNx=F(|5TM039rQa_!t)-PB9gss-ZSXrE#++r{rAoa(Mq9D2 z$wjtXZ1PnfE~wL}ONU8E3>EsfwprFk_!aHSY9_!u&q0lm9MdWHGV}xz)l(ProV%os%$x zM#M7|3@HPLY!f+FA@ZmqFf+t`y1yL`#yr-M%iiN}t6kzekryAn-J7w9^iK8RKIKL7 zUR-8s6_UsNlO#niFtNa_C23)e<>DQJuXNUa7pSV)<3O!E4-ZF(s|}R))?8pi8{KKT zWvf$e>1i`)nl8x$gTD5X9yeXP%uzT-`6?;>n#?11z0$8P2OUrq%w$`U@@Rjrr7bJ;AGxV$S~LB#MLQ6yRwf?uT9u-SGc@T z1pabYIA_pFFpRF6rT%>zk(pzjwq1_8tB6Ll<h=sf9KIqIR(uPcUac`L%iMoP}f!SyFITC z_xpF7Uq^W2`h9&g&1U*4GdU_39}iP{4W4NySWX!XQ@xItC3mZ{AA9J+ze#v=^jeYU zkX9p#A(RNnjdZIu9!s2($VZ!~mGt}z%?CAz*T+areD(*}?rkol%bW1K46q8$ zh)`mPR^U`If!cQlenvm?U_Z?C<~L;$B@5~5MGdEsbvI%#YT{rw3}_g5>r`$7KZJcj zkO7(SctkMTs`VMjCLD?WDPi~L^70<$?fcF{;Z17$%;TD4k-EZvyK7$&F=B_hqE&}U zOBd#Hbono7pVK0#OtJ&{KnW8mV6&RL#=mPfA?#8SEV<)-v!mxJpMv=71Dl^v0sEBe z5eUIB$$3tx^~6f%#Y-5kl@qe`>M?$abP@|xt$T_Y)~I2Dx(GYxE&&#X-+Te3stB*@ zH~im-HM(dD{VO&5WvYyXaK17;o4x+k9MwN804x2){th{XQA9Bf94KX3AU~uX7mHDH zpRa)nHHftBV=R?0B*_69z~1qhI?EHWv1s@92w}=Sc3%3bpX^2Ux__gSz)e^rtV7qR z0+{W9YvJ%lf?w$+T4XjDy-yrFpP%`2UeH_4iovGq1;KtjrD35L1C!ZLmha zi_KH&u04C-;pKqnj96F)l*+eiKkwM`Ln7qn<;&#{Xy`C|S#g4vUvE2>B$rq{_*{@c z@zUJXKEZrCEJ@;0Eo?gJ&zkobi%0GNbwhWjuq)ztM<3<X&T9;H-}aUB^#rlh@;&0D_2R?IfDv8)-EHxy;~- zEk1Jq4%1q@8KxTW`!6z%zD)p048;NaNH_uoBtb{!4sxlcOT8%ifH+yUG4Hv1J$gnT zsy{xY!+Yb4N*wNd#Bc%&yb2~e9Yu}FX&FM^&!FPsxpEp`t9BnxwIC0Ye0-U{1q>3{ zKxor-@9GS(akj|<$P|JRY4#gE=h|I;o8S(iR$_^NkxQi=p)KJe`LP^~@i`#pzU6C8_A6^p`0`Pre*Kg>bsIw| zV;7&9p3u!H&fLvHLBzf;Q` z(3t&=H1n;}m+i$ZG5Y~&F%P*TsujsAts0w!Iy_Xu9WXKb(|$kz-Zg|wK_=B@LGt7d zR0Qd2?dy2Ko*c6uI7~$itSt`#Pv}>bC(J6-eWmyg5u?D7E)JvQwul%t!sU##gr^Rq zV(j9a-lU5BIUtK3u6gY4v7)Bf1}6yX;Aaun78iK zoyjzOU|j)eF4-mS=Y&;QZZ{jGdA|?zn07+#@Ukjr%Q8JFp(`j+ywM&W!vRPM%4(zw zdB(LpQtlX*lwU1f3M7WLbN1j)tMLNW?;%Bk7v;FG+A<>?&cB=Pt>&~73#{t#S2b)7I=ayRCf#xqhva186k`N5D0>zCURVg(JQ7|vDlJ`HF5yRS93umK) zwSUb%WIbE`zEY)hDZNyWB61a$52swE7GRCcJI)dK-FUI#tHySP4+UiLHU;(_#`J8@vsFp?;$>Why z8w59&frFR){=Rt>UTlA_a2!HK5tA*;IGal2iJ@IP&*70d?rSCYn#oTV86EJ*sm?o2 zjO8KH%PpAzolS3oe*q4h7KXW?EC0gENjScT-aM(*{(>b6I)24s*tPgj9KU^~tNqJt z->D8Xm29O!PJlK%FK^#>(%tn>QK!HQId=mjk+vrY5(_9(M ziQ;Ob8mb?y^ZOY7zV9V5U{k*d8rspazj+qD*ZQ?j(n`hJq(z&=2l7MvbX9tXuNsUy zKYfs#>YryET#5rHXA;gGgo6k@dBx@A_vsE;qZd>i{hc7@WP51l-Z8ujJ|Ycd982+S zsvW1y=%}YeVv)|5wwp@oT|Xeqqr2e0$_Jod{UUr&Ah1p16-|7moS%_V>V?g;q)~Ua z)UE9-jVxN%C&CI@Hf1I7it?b3-ZtCHE}o0+=;B6WHRyqxcYYPMCv-UZ?U+v-U6gzW zm~grx7D~@EfMzJ)LxC)(q~7%ye!mkr;b_$jn~kq#^A#=m=x9pmH!$}tWda!Z5D-;d z3RHtZ^hMG9MLmuo&Bs$^waOm~GlRx;cGv0Yv}f;TtVxg~K~n3OJimKG~XGFxhz&4wD0*J)*F@bZtED`yiwHtHQbz^SJ5J z9gmE+mw9WKiWG@J=r0t3lv9s}I|-|L9}5Bf902eSq|<2mb}ng8K$S-TxG^<~^d%=j z8`MVlbn#gJLMNbv6|Vr1Az;l2tc@XC2#P8%B2W#!Ae@lXrGa+RXfSNyl-O& zNDllrpVP;D-RSW&e`|ko;*A`Q-;hnHG2*tBIp=~JMb%tiSpLdlez%aZY%cQ5Exh!*15=2gEw)9KGwC{uC?x7r!C7 zr(ZHFhbjbHuYPBw+K_p`SETY4lj+lRi` zr~Rj^nw3)Q5+vGbH(aOI0zy0kZy>dg25(xO+X+GDM<fI%c!7K6AD)HZ-S-gD zaeZIJ#DDrocw0OjdR8K%AZHPRR?=qEB3ijEPStbCI)seb?X20NC3Vz+6dL!BYv5DP ztCSXBk@U)_B7QIq-7M_U+KV<05@AD0u{#o}%;On;(B#@(UoOVRtZEtPlQR5R6$=D_ zDxRN2&9Lo25uAi!*cFX)32gxlF7qHRmINwKZ+I}ms;#JXICNB02K+DL&Z#@oXlv84 zZ6_7mwr$(CoxHJa+jc6pZQFJ#Nd;Z|JKEjHJ;wJBo;99>HRhW4eO*OlOG0frOGwcU zv7`%DiY2*+vVZF;Q_{|=IYYydI{`yBgdlVn>FY5=)TCm}TPaXIoy%}Mq9Abt`$N*$ z4vZZK6Fb-Ljv4i@l(^3?@3={@!w7y&K~ z0m1sZP*RNfTWW@Y;?~lsyD;|OPsmlbb68?T`pz>jDv2bmh82ky-P`BU36c2 zzd`y8nZB)ra4vbd3>tG>+(qx{qUs5+7d+DX1(x2~hT-9bkyv5~NSx&)F~V{R3lJ;m zY37-IqKE;mOf&A`oy?$o_U=mFY2T=}p7AJIlI`);IU_F5UBsm~J|cGi?b;Ayb9ZnJ zTsiC>FFq5jmijk!=_roB0#DGB9~+sPOwVn$SY<+Huz+;H2ge{l-ix=)i{C3kFas`+ zr!2-v4sATn?^b0yUMTFb8KF(8F#6HJx2-0C)EYYdsc({Qy(*ekHq2pm8>2ErY9+W z>Y}kf!$iNR2bWc;9X&$+(Wqve-LW&tPAQe9gQkP-E%56VkUiN`CbCk)F!6L&f|0O5 zsj}v+#(`u!x2H!fu5o@N;4S3Q;?zvro)!&y{1#!cD;LeSze~&6_?9!WXIeH;4R%g{ zwlEj-r3srvR%He|gMKYs{a1?-_m+**RaHB+>2&dsHMR`MgahhHY^fmko`+?!Yo4>*7UP+@H&aJv4|IK>{OHog^JP*uQGNI;4}tMfn2s(C=_FYC zd}X%__!fumjJovD;`oXW5W5*pFzmwA%BZC+CXb#yK&52>hbNsMhmf9Ah$dFq7)J)3 zpFg6jRD_%_&Vr1#nYOTDK~=oc0u-XQ3X)Wi4jd3TTen3oaOx)!S6O!cNiPYv$W0N) zj2%0gZ_gkQ-hA;#s(dDQ665%-WpddHi%O;2Yj`S!tmuJJm-X}E@+v6CTDzux1p?EPLDAQFk{my0pikRqd_jmUiBsT*c5c5nSS3KplU>zp@|u3%|-p1_l@J z3bTCH#5{G@Qn_#br7z&`;}L*W;}m#1e{Hg8uUIU8w)hEiL_^2nkQ43S6RIcaW(b`M z(-YLvh8pmGZW~o=SEZ@sZ2M= za3|FIJf1<;+UnFsrUe%&r>Qn~4FlxmHxs2Qiip=?(akWlXq&J^es3z4k(1lO0|d3g zWe%by``h9|jw1TyR1b5bLDe|Lg65P>pr4DQ&kb;WFAmXPeqi0`LW|dTd4UQITDw^SdyKvAq*w{vVawx z08z_MwaXMBB}eK~c!W$@9Q=Uzt?}_|>^y-e5PjTiKqYPOs$sn|Kh;a(vZTro?0=jXe|z zjbaME_u{;p^wekJWWa`~?w6R;>iI zYxht1p#3F9RGvvn`qi!!0oVkikG%IxsUVHqM7m7ka||!E!lUVe%5#uj?eyvngFe$j zpLZcvkzX)127ad1TUoN#8ebh+2Fu35MuioGi|=?8+u6FvgZhFWQNqpE_3KE;Ivz;# zk^drOT)nnE$q{mhI9F*5y6nE#KhR9z?&%mzv3_+HYsFL?+-N1p2k4|50&QRMMWUYuO-(!=!Jwd4Xs<_LTq?X=JZ^qFzW9}_YW%tNXYIx@*x4-*m@m)c zFYke2Rga*&lbLd%a_$UbqB=+h@P8d!-wJ~b}K@}+ImCuzR#KZNhm zYwhs$nR@FyKPtCR1T?*P%!^i6%tkI^054MTJuNDdz~!2*4l_yy?lFe4io$#}Pe+y! z)Vq`j*-Xn6WTCw*w{NtKt)Q8v)v16;pB zL`+^%6ERl{?3=B!=3nhgdce(aDY3SBSq!U}n>A|Ht~hcvbZoYyk^I-{Gtk=82DqNN z_~kdl`k6wNq0UZJ2Wn0d>8S6bdY0MjzW-+GO?1}zUI+)Z@?>Q?(C@{4`;WJKAvv~* zq&8z24WP!b0qTm+hUk$$k@%wY=sBJ7{Zfg=ON;8Q;;b>i#PvwOpIJtA%Y!6fzk|&# zK8m+9@TF8Cn*(Z7(r8zaEM%$PH2Jw@5^f*iZeU@yM}urqpt*2AVFJ|c)wOVjC_NTk z-d>q$Ud=v&twNM0U5W)VNrj}wjvDWNH@8oUe&Vl@0tl?E8eF91FDa-(bJ*}D;o|A= zH0ezSpaUb_G%fQvI|lW-7QqH7)np>2v9OPn;QW8+zXDTO83~FdiL{Xn%R+NlRoB@T zw^Vm_F&U!VuM|rABk?tdQ4qKC5)Pq)7oeCLO&W){(9zQ7*s7+Avm*Xm1n_=zkN)# zUZE4a22;r?Djh*Q8c>L@0%~QZ!ulB3E8D_oalMLwxVYkQt1Ri5kmO`MZy?#!&SmJi z4tECLaU9ZvSNbEw@U+cog)|wJlE0JtdOT_fz5i+4Hr=N>R*mZsRMd}MeF$->E}V(J z0K4RW%BGSl+9up(ako#SZNcAoXI@7t>oa&M?9+Fp8zJL-usS7%Bp1*}+)(27y44)^ z6O7x+jEw~!Ap%j#5V?Z*at5P{C_UW+2Ei{W_c*n>ZbH_TWfx^GPb%j z_8tx#+_7Ja_LE6dXTv9im&SARvjn0i#e82{m(45wWMs?3T^_F0&)g@y@_lVu;%O_a z&Jsc3L+F#`dC%`Chf_`^G5ENA_Odj=1bTo&0Es(A(-NZPPi2`}e(o3hOoFm4Kw|!v zfVfRrCM+qLjh+&2s-UySpSQU_1Ac~l%pX4ZXM^Ocnj)2m8|}ozJB`-INL8I+P=B?G zIH}e-iYHG|7jP^AwRMiK%bQN#o9~Qgk0|o8t>+}3VzvhZXJo_!d~=?&6QtUARWGUA zMX0y&`)hDpi}mcM#(;2yV`rp7fYIeYy%~n?%gH=;$EI@F-%;khvyB-`0l^uH%bGZi z_|dO?4V}(?7FQB~NfnR*-u1%Yzm1JrRI+c^Z{MdIj3r3w2Yy21{s$-?_75oTaz78w za7b*7o~SoMd~S>^1d>#iykp4CLRzUf{A1rv?5W7z?%}x`|0J^!scZ`1mNljo7DKQb zRbLZ1%Yf)Pl+Q?L+7)ffNZR!rP^CVhGI8};dam>@`A}rcdzQP$pjDY{==AIC^Qo;) zxA`4VoP4xo_Kxe!w{e|m80{51AE&RULZa?IR{YDlb$(d|H%n!Aw+SUFecQehyA2;L1Fvv7FzZ77sRHg6&o~rsk zBs%}Odd@<5snUSgmhh&a`B5;GrK(guWYf}FvPQ@2asGN$)E|5vD-r0O;m^ajTi-uB zXLpb9-L+ek%3i^3GhBnM)QPAV8_r89+ICG0omQ>FVjF{&ae&o4@xPd;-Es@ft_YM& zN&*K1B340=#}4@5J--%S!KcHr$v+>0WZpbm_>D zhxZ;3%q-76V{pw?1e+zb>}}8;|CdfEtV+kIK(<_&k6NDo3&CHwk*cUJ<&8t@fi%qM zD-O~WVdDU?^%nhPDRYiAHAz}5&0oHERKvd(D)MQ!O#!vV_ub2Z7fGdEYxrZhH6diI z)2neXJ?gd;iKOn*FfT~ue0iYLJ3ElQ!7j)D`nGJ25z*`%1lzW43_}S_H>&CG!ZHSE z9myLQLrc9SbSEwK*Nf-oTE!{;aap?Py^wgMvp!p0Z2ZVoZmf87GL=t>mt!d9o9Axj z8*59qgn-5uwo2K4R>@ZSfHP^UAoLFgs>T4fSeP7mEWhK0NC5a~ufeYV z*V|-9czjdNOy@>E0kN6kP_qdov{bOJU*ES)>QnfT+}_TQk6|Uo>)SM})6|XJzB_l? z9W3b4o*K%JGCsm0lK5yi@f%e@dL(xwBEr8Y3&2l61h+>S0wS--qDM{V-=6C~bHi_C zRaB~p-7Pn|D`vLEmZA~{HN9VBcqpul(_t-Y*+0D|^zcZFwhSc7DEAK@jeREk24z>v zO{DQc<+?zb6F{O6{5svkLc|8^ZvRFNuuLWmGLRPjc3Nbo+Q=^X@;Rc%7zUCz0%^h& zz>76Wao@B|aDm}B0rfLe~DURutVtk)#;5c~V zy=dp>YlG?fKgOn}2}3d|aj;?(<(~m1npj1+j>0KCh+%^L%ZvwfQz8y_HwWFjtLt)> zzVacX;gqrz8r?dW%(&``R>VWxC`K#7K*=Z{-=|2w*}Iu?(6h7Qta zD|i@6E|Qi!*4_ChzVR}B-D%o)GZvB&|FbNlsC;On0&oSP`d?AMwbiI4La=?7wg`Ko zem0E#sK%YipkX~rj0|4wyR;Rc3}1kkNh0i8Zg$D_up#{`5M+J=3g9)+aSHS{0ClGQ zQ@(1y*x!eSPng`3>xwt4Y(^iSTfF_X9CWbCl@5br+l!rTEtMC>gBr)&f3acFrVwaE z^`(Hqj^)OVgQ8bE;_YJ+DH0XmA}i>r7#uT<-mg6))}E_EVBW{$Z% zI3Tqzsu(PfZ(=|DzIZCUFQtAS-m^{t{%Q5FzveOw5nhHwx8t7zC$WqdvGY_2R#4LH<;Sh z1^~_Q+39SzHSImo4C^Pogpmm@KDQm;D6Y5gN(I7}_<6fu@SAJE@_XLY-h?`Ce*WQM zNwVEYy4EA&3rB=rFJ7=@&BBf~KVG{tPj8lpCk9L|WeObH3S`|YjgAE;Z(Wea*+X_c zxME1~PC`gX@g7=zTrL!Q0Xm`f3;uWwTWBFk1b>}xA_hOmJ2T~jh<+cgJXY|JVh)`1 zLmemo@ke@m043svrYHU}Qp|-=;pflUixwDrq6B4~D&~YS*ZuAyG~>+-;H2GnQ$h1B z+e@?kV}lfXl X@q3chJnHkoueY{(8Se)f;=3d2=&yF?&&-eBk6@amN8B z^IM>|7C7W5PQk|qD$Cp-WT+mP{|5i38_+;XYzG@M~?WhgS}oBSZ@0GnfEZQLg#?;i<`)UkB`PMSTz;(=jA6@5Ewg>rvZgy3;c z7nVBSg9WyNi+nW#$OYRxwY-IxqsMAq$cGt2J&|CQC$hmY2tbt~-lNJD?l;%C_`~lg zbK-E{r1uTpRwZ84E66mn99h3YJZT`N#NU`D-&~Zen0Je|wkY6;MqV2{teK=xvR<4U zyQ`QP1BXh&usew0e-J2=Q!F-#8w6jINab<*o3B^dI1Ysl5m!tv4zKCgO6e;|9+?7Z<$vBO~GS~_%MsAb! z35^Zz>P!2^uGk?*i&4r9O5W~5-j=-ClIDN!MY^Wi&esUNz&=<_S$RD_a`L|MN-E}# zpo<3@pS+OFg;I>0&-PHDG~eD}OvUMG*&mB3XR&^!B>+z`sQW)C`*mRb?a7`5u@^fY zFwmX8Xwxv}j-SX~D)8f+iSqT^DOt}uFj$sn z+l=;i1R$U(W8nM^Om#YArU3Xkls4zlDrdXt?bE;zPbWmA;oc8x2u%!A_IV~195N+> z8$=CAk3z_u>x47)0Zxt(2#PeQhY%!#kfEK_kY=D1YQO!yO~f#0DPK9ShUT#CJk;<3 zy05@YbXgzL>70hZui2{2SDpcE4*)*N8Rrcl44ASHkj*fGFlAxQWTX#5Um_|=C-YAL zNqf>c4fkgz1L8t;29gX-zZWE}&O+P2 z&56&CYTJ``LQ~LURGNQQcYFfNlf)j4oZx(8fR+{Vlo8y!XbD@+D`jQ%rsMnoJwJ{H z1Ca4iEX*M#3u2@CtdmBT!>uH(!4zRAy*axyzH&r86mgxKZ|F}?l^=tUr^J3uTYR&5 z_Ec;e>1*P|*KyY7q#LWd$MZ9>iqpcC1tJOZV|;p2m4UNi#@z3zCpcWIL}MlXJx>Q2 zSQAQ`0R|!rHoXFn2RLAm`pSZQJ(g;_0FsGH3NMMkevw~LB8+6bwW?w=pr4J=_Q);B zSmvju9GDU;Y+4Nc^1`Wr;T4ej+}dUnJdkS8d4>|y5PxF4W`-uw1$mI<1bNTDBmW)a zQjIXzw{xwyH^?+Xn84;>RuF*|)G5=vY;*LCV5BB-BQFs=nt#n4>7Y66py~B<1ZY)Y z@Vdo<<={Fn0=mQ(Pbax<%APWGod`;KwwHplKk^!k^XDecg{mnM`%|;ym5O>#_G8D^ z1-JOpp7jUpNCnTwECBfkCSDpIAA4lC+pBnp&ug$dP?bjiZ+=g3&%uSlIVzlj`|h7j z{MR*u8a}L+${j-o!tIr%p`*Pp3P4YB?h*dmilJ6y!~po*KF1t)RZbat3H0e|AdGLM zzUvLLD7*)dEuq*TYSIGJ8wk*1^HWMjhG1B}B9C|R`}DnIuV#0kFMh!Xm(W(Pj%;l@ zLqL5Wamr9|tl&7fm;O(UOs*SrpYXOBdz}>6%wXodZJsFl*>kiAPsy({7C^nUT_X-; zfrJ&1VRnro!|qlL95?}1(~?6Ik5 zII#N=*z90(`yhmQ7Rq+FfdI2-YwYA{62w}l%4y#A_^Iw+O-f~__}Dw=o$o>n150j{_dbM^@Xuc!~ zJZ&ehe+vs*)>fxI(bch-w#+AEjZgJnQeTWg{x7JXj+h}P$bqF_@VVNr&zC*pp_CcG z(EfvF>)M^NgbQX><-wzpvwb@gpcI@MY81>7OVN~A7FD!N8{F1C zrZ1Q!!jgaEy811aPj&J@y zCJvk-2jmERaDZ!p$6!&!u){f02s(tWUysL``AsV{+YEI$+EMCMUM#{i`bTgRkfcIF zCF+ct zwCYQ50N*LyT-4+<1l4bNeKWT%_x6(l0qOuJT(a@tiR*xhAYf@e4hiID8HGvpe^>I2 z&To6RVnFWPXafrp>jb#zgZ4kmy!1`*)s91N3+nl+(~rGDgCa?)u$^t?*0hgNNSdzFai82F+xjght(+Iy@U$w zDd3k)U+yFtvf@l=v<2vJGb|7UryWTh;TxNMf2?f^c>8{X5j8$d7y1;%d}N_mR{>+r znA6)I>vW#Wf2hI-gF=RgDY|r}j-@z*jA~~^O&zL1t}g}+B4QDty~vAN#}J-{40+y>ydALedtk9jO6)Kifn=2##1CcVPi@Ba_-~`y%(Fqzgv56_6?y ztN8|U>M=UDdNg6XG(P>3&TPlwsHXCT1l$9&+dBC1KvVS|UY3=HnUI02u`A6J`e`V! z5+V+?e<#%;3&(WFyQ}#6LN4{N~m|wLUCn=0cf5-N}liQ z4+!*&aUwDGnUkvsK`kge4Q-QZ;A?FCP&T z4v7iK1-jS(PWmp}0+1-P0k|9otb)pfH29oeO!(Rx3p$^;>DA0V)FDW5ax zZk>=73sfC+%FS;^hrV3C&cRZ@_yWP+N=Qey8%r9R_bcJPW7kvv0{FGF1b7{5xFN+x z`?z=!BCM)OiT<#BtM|tIU;wU!AiEou+$7Xqh@yY0I}KJ#6~|1jyop|y>mWgmnO3)* zl*R|)(*u^rq|@VHto8C9=U(B>@+PiM)TBzc3xdqoh^Y^8S0~Ur+x{_|&%P9^jQ*;) zqK|MS)@W4j;p9zM0vJu05%snP<@jhiME&tkWdyzgpCrY0cVK_x0SnArq3ObQ@LIe; z!osse=1#oL1t)4mQDR+CTdJ(JyN0b2c+#RqaDuz0G$Q0!ob`LxaA$#|g$zi3d~`;H z)Km*sSinh;n3!o_Lty2AeP+euA4Hx)k6##w3W|D7>`@ao0g6hccoSN}G2%xmY)tbo zYMLmCbhtx%AcHTgd)X{2Vm3O5fC>OoqT;+YPnFl&PV4497aWn}E0_eKWoUIq*O9K~-IxAY6wvcIf za60E*vhh!)WK7Zr>rPTl$iMWQm*ix%s{#_`Z{2;=#!J&k$jEMY>oCVc<-)sHs&Vl~ zD+}tCiO-OZ2ZJUcRp}5R-yEH(h<{R&&kU2~PQ$y0r#JYd)s2jB_$Abh;q9zhdQ_y66zKRT3}2 zbchNhF~}RV&MnU4R`H^i)UdF=(RT4Uqc?6M2V1w_9p#g@X-ZQn&|!qxP3y6A=rp4aH9Cu@4@QcWh~jY?k9jKQ zSq+&LbD4Q6m=WA*DW`=59gi#UGYnu&@~0ui$%Ptm-0TiQ_)X2#uqnyGa$2aR8@tjt zzzAp9P`*#O0RHVlqeN?jztU~moGvYIhr)G#seHA?e3_N1g1!#B$(8n65&g5hS9RUh zG4F*_$F74z{udnzRPr#jt!vm{8)Y>t9`^?cQ zOLFVm?4eCuR>YJhNcSX3_X_>ZJq5M`;4xI9sYwy2*_kI(B@-`-o4WKvPZXsb-=4z8_EhXh~yI$~k=e&%QoK`5bIqwjahZuGNpfgIw z5M3<5vj>|qV}E)eU%&h%CKpY=9LHi1Izt-F3a*ox;Wen1en`iE8kinMUP(jI5K@&v z3a?hLZv|GNDz2`_Q%EmMJXB9^G|`SaddFDofsV0q&q2Y^ ztYDogzEe)E-a^yW+Q&`hFAT>zfONl=dx-rMB8?zb^Kzfe7EX|zj+Zo=c$YgzrTB)A zx@(G(j;p+l#1Fc2csLN0;k4*e#tnwl;B9%DHxK&@Jx+pTG41TgrMjU&4&i zt7C%qBv0w(guadON?<~2I$B?PoMv**`cMpyVs4Px9RhY?2c0U&{W4NzSxyc<-qZ{^ zwmq&tRZ62}wRl4vt*xU2K%&yeDye?)EGI6Yy1KE~5bR7)TI5Zx7o8~!V$+&r@N^v& z)G{(>-g-OpsCrmyJdA%e(Xyh^QR&g4BVes*D$#dpf2906i&OP+L|d+@HENbvrdwvV z7(cn|SWym>;zFC|gTY_{%s9|(wh-5`%7Ha}CZg0)pZ_*a&^_=C82BqP<-fV3ltq?O zbruZ$2Z`(CHgr3Fp1d`7InBKX)Mx@{+Qfl~ocE&Xk$Dc%$Kq;1lPA^Uh8CmOLk(dH zm=z6PquCtaw~7E#bxuH6!bV#?aGd3w(;0+4zw(&bXCyPM0>y2HhEpSYexWS;qE3M> zB3lm)W|MK@#0hHw@HFz~!L-!hu6ziqby$II46AC& zY|0nlL~)lb)#!m<>_vILBAUu&M>dl$c}nVv1%yn=FW|h+qE(} zv|DV};1tUWCa1THja9(GpmzxZ>eq$L)XZ$MDR-pq-8m7{gbor~x16DcL91J7*5#n#k>PpQ znqXZ3-zWq2F^yJ~Mt<*@V&`l#6jqnivO`%4$4{ircb-PFw%to&qZ326_n!n5DQ<z$tFpxcPxp+F;()hqDU9Yzqp`{1tnNYI?>sFM^u(cV%C6e=+0% zT*XV@<}1h?2~xUILHul05;}o)mxXveDCJF*W0L;qtq}Pf96Ih_rGn7GDWD3hKwp0l zT7x**97u(h0t0N^^K_+z6M!-ng-j`gQnT8IYr90}SU#KUBzShd*rRZ$Z&&YV=pKGd z-hb{6e=u^v+XArOq6G<$1p1a_CIi|4@EmvYidx!i4VvA+pSwvF-u&<9$5yyHJ*ic>1)5^3gI^2_C^`D!Xa&o zC4O!jWA&Vj+{E}J{Epj+$U6tLuL>iMIT3d5Z|!*1MxYdE`R?0o(c@H5lE7fR^Sd*AO?7FNyea>X*oX8nCat)l4%Tqu zx;_3NBU3F_=Eu?t#lAuY!`Fk3_Pu=EB=R;8IT5k!t zP6(^P$s3#RD$zv-ixHv@>J5RX+^p&Iw-;{Ecj2QOo`K#NB~!=m?{!=)Hd)&!>ui zAY)bK91j1d{y7c25}9%WnPR53++?jF)^DT%FHD{pP&yoGTMmHPeB4``-@|j z_6Pafm{-u~t5q;wwE?jTNt_v0B&|4vsymtKR8p7dh)-=~D?a!!ci5DN%ESC`#5

weT0Zp=7h96<(fLBCMK!28~dDCS_DxPuhTR1K;SzZNMbCRFrRfISMFw z#F6VE%mhm1eY{Ln9L^NYy$4ei=qACbRSKAQQ;vha`xRisxi`yUSDhX%s0-g}_Be^M z;zYkLJW}wIaNw4@5JkK)Pq(_Z`R@f&$5Mbh_6p{zW11eLQ*w3ziy^6#p@))qa<-M! z2-uR<2C=7SfG{}w>%WBvHi#PeTSAubLali9Pql`R-pP+$i9PWnVs-}MaNvfCg_I4R zy;b$mBM=~2-bW5N@(fvzwf6kot<`g}2A~bhVb1ENbv$~DIf>E7al9#yzZz|d8-opZ zrU%J&@9Af`Gkdd;Za-Gv3Uai1Dg0Zy0-pZE806)+u3E%A$24!WjT>jQr*&zn6nTuU z1R(0z?t?qx-7D~OfaiR8J ziW&NGkQ&6Hu(lzBLn1|VZVV!sAW{yN&CkEbI=&O7N7-VJ_X*+8Hx~FdA*6Es){{Vc z6Hpn!Zf^->iAh?VDv|0aC9nYOk^W6+;mWW4e#h_hKzH4Vd;ZioiUvSNLG}Wa-$5is z0^kiTTAU*S+PZdZrP9X{!ZHX~0O1Y~>oVoNa|3H#5%L_|$YJx|;wO|LHHTu;%rnZ2 zdbs53n5)WzE{!jIk?70kToxvk#t$XufHIY~Jc>qlumb5>3QrX#swPR+KArybb*P-Y zB97s}Ln7K1-a}A2%TJKNC<$|L>7wP56kyrm!m_VSLh-jEUv*K;)bn9i-_V|kMvxz&zR_*jiJbs*5PYEV(d{DV+4v${>1NbnEEGRJ zzjEKEUgQ_IR;~XuVc*YZo3`GXUtYGBP)H6Apwvb*G}MK1M%7#JME0Qoe0|ib9RPI~ z9Wf?5L`XkOTjmTpRV>E7fBEGX?bsaAVP|s75!Yg(Z@{B+W0MKX&xx6UJHZ*jMu4DG zv@)7v0K7rIklM08t!(nCcN$X3#HzEAAra?#DUaZJv-JD9iv+u(g6rCZS0*b_X`Bl4M9j17GXY0^k$?3J551``%Fz;H)-QHj{MQd+96jgqKg+RP$*Q z`T4&ja{vEPEP(%FvHWkKSOEXUV)@@du>k&y#qz&_VgdXYi{*a<#RB**7R&#OVyR7@ z`%j%}^k1Ed1%5#)7}D?)S(ryvx6E#vT20GQLh(}7sfnquhv%YmP!KeXC|&Yp&VYaG zWyI}8^B;m&w5~*0vQqHm;a7Ie>+du$t6#vELEMJdeOaXTD8+t5P6Nea8B`iN4-@<)@O2XN6IJ-_wva%-i~ zwQ5YK4}8OC=d;D0^e+ZOBnDlonRLV3wWD)3a>gxvwKME)=jEWHw6KU``Xrr5Ek*W9 zKI_nj-JL)Lose?)e6f?NP$Wspi3N9x4|@Fm{pkI^A9zP=+2UH3g7@yi9eXj&-={YX z`m@RphcVS(?r+=Y0>Id9hD20XGNbWvWGS6mhxh%vV%;d~&!_WrqCydKp5=g;OY`X? zX7S_Ljqdkqc*&&%R_|4(K7VOX2TUfp)7E21~r zV=H;o$Xf#^0u){uoKTSxcxZa~Ha$dDaO`cds8so3gXxF2BnH2}$|KIsLM9IgAVQW8 z{fl}+e{ykuj}2rOYa}0ZEdhJcDqizWc_frqA>2;<0pQ{x&;*hmvkzr-^K)f)^KsGz zs^uW!{~k&{dO8?z!d3=nw$Vam@Q~l+ge-YD6m$CK229qV^;?v6I5ONE-U@wK?Nhxv z>oEU&!vu%EDv^a~ckdjA7CB4fwGVZb7r9iR+9iyAku71g2~?L7V1Mqie9F(IwW5vg z796~{;Ia;3)eq%wtohVD*-|iyQk3K#h7cv!V71qYmf@e~NAO4SEhxm~WT{Su+||}a z__Sga0Ym{sMNdHk6&2ojqEg?sqnkb~ax@{#BF4wn#34*@cF%ZR5vMjg>NEy(Iz}~B zf~3-&uY_`Rit(GW+%LqIuZgnawQf84$9T%&Gor4JZ_@A@>4mdudpWiJarwA{q2L!2 zIwuN~p2XzxJ#KAXaeh&t7rFdY_8nIGY(a=B0Klb5e(pgHVdwG$3~{sOaOa_7+#eB# zYciMidlH)W!CiDsGno4LU@ulnNv0FLQQFyTwG~q!{#DT4f>g&WDV4dl3|0Oog)i^_ z@f=w*x=(z2CYkuau;T$yIURdyb30hdCUapLf8K<~f5Q;Jk3vm4Ew0$$cwS|zYNBh} z0+2OTs^_Z539OV;a>f%5WhCc!8o}@d9jW>jx(j>e^eoDQ11+c`yE1zSVS0M~k|5oo z1aT{|#J+($SBv^cgRaNX20-kZGt+3X?fvo@v>2~;D^wMQ-dTdU16i3t&=aAbc&>Pz}M~EMpL~2GjdvLYRuVtAUs@$L#%k zlPMsc3`nb)t*plK3v`^rmG|k>^cg`_U{70V7Pc)1Zw(N&eMcopjkDs{Qf&NPZPivh z;8~oc9tv>_2XkJYq(udS+|cnS1)_gV9~uC86|``SQv~O7%hg!HkE@Lr0WzyW7Y4)9 z5I~~OZ2}GHS{VaO`(0AOj^^Nj1gC0ybXGunrcf_pKo%FCw!&&m_j`mF%A5KRZ@VVr8u(6^ZaD z3NgWqn3JJx4*t<5WSLxy(6}(5`jbe&1g#X|qU1~te}cSj?lirRH-N0Ib@=C_yo4X0 zDy7dG6tY$+zK>?DgCKRd(joHRtr1e|YYPQ7ezO)$F%zj(6oX$BzpT)Q-Mr=W z%de*Z9S?3W0peG#z6f7xuG9LaOmy92MP`oK6VVgIZ&R&AEc4%l=u#+8xy zF;;^eW~)B`mCaEFv7Hv$wUn0as#@Y#Z=nG5EY7Qdpzkzy5|81FvH z#d;fxCavH5MR{qe{5T2bcf>?9Du1?X@utb-kL|3+WHfl)`GAfL*0FnNgcwsums*%7 z0)b;cCd8lGgX+kJ*au5mks-(2$CtY+bjUkw!pQX}%ROHkfS;N!e z8aey9^)(j)u5xe>no>S2;A_1l(|@_9>K97rEjaofW^!aoV@814!0T-iDD;RYhWw45g#A1>Cixs z<~66gVT0JvrqqU*Q}-_@RZG5r*=m3S(x^O?n+5!wrdaPy);KIEv2vf?BrjOi(uvi6 zX(+{#(u}r@NWu`Y(K{4^wvF*E6%OkbxtXGP_B96bmyB)HV79s@G)J2$y#5G>l6&25xbrJ)=1Tz!$7# za&_MZn0ea8P^R-i)vLo^jztcT)GoecbY78oK6p}!nBS<5FP5A7jeFOmsTNs4aj?|| zbi1*8@^KCw?;SrIa^mISm#=8){!wwsnWW5Lv?n#d(sEqFiBF~kc3>(NmfB$d{3NH|5pR+K}Fl-#V+Lx&D&yfT_SAjl#h8){ho)P~8;OP-F+7I3G z(hismIkT#wlIy2pDV6Cy?QbCMn>{73$;sO0W9<;l(w&vg(yewvs_@!wC3d^2#clKe zX9t_V(NRlOR-%ncG3U3`hM}~D4p_SsD^dOtE4!@A(lm3Wp64gbs-7ZOJ5sbJ+?1Uo zy_E{W%;r(?Y8lm|abHvfaLLP%a)C86YH`(314NIPaE*;Ml_bj$U!+9baF^9{kEyD8 zCwaAb80_%r((-uROqN-G)5dFguH{2S{~zAYA-I#U;kU7EXJXqrTc1cR|%7d!u1I zC%REQ9h<14qPaC+Ri)qvpol?9v}%en^i=7P7Rx|8liK@8J%LP6;Rlmw1nVaz&3;W= z{FjO3mLMp)A6R>%SOWZeqxcL-Jrvn@A0ENS7^xp~96&@7s!Di1T7??-LshEs`AU zs2j{yJrg}TMDoXN7_Wq??!z_oX1_V4pXwm;IqJ>1DwOy#7W%bOcb$kp0sI>+O|FdJ z%CAFmkxFFrZmoQw0U&UABJH-c`82w;1_!7$d*^#aK0)CM$l5!=5UO7Bcel0pffaM<2>O<#f=#Y6|=k817b9swxTD#A*>ML&VBAzoeDP=o8NjB{^wl>y#0Q z<20NxGYR07O1bqr^ihQ=vC3Y*XE#NDe_4k6X-Zi63V|?{N;SiT`tGoaX-5A6FwJ{Sdpt8H%vyn9(m}n$rek z-Qc(VCZ(J@p=zo7;P0|VlOmmOu%wep4f#)(`!Nn&qYf_sWpglGno+~moze~Yi^P#y z6K7wRLK$+{dG%KpmUa?;Q5@U5PL&LeZXg30U6Q|1>?~_-<7Fy(8l?eiI|)MQb-xa1 zT!~t;u$4)kpSDVnqSPK7Yod`A>n=t;t9HQQQp8``Xz<4y-C)#8jFLj@*4|k;q;h&0 zhFwLK`C%@=9I<_k&d!*Pba=Q_fjvpPv}E>0=h!@F?eIU>sS$xO9l8nt zPe9}twt9q)3_Vx(xG7>64R&1X1gQ(6^?H^MJNyEux6sVBiUldZQch?2#J0xe8C{?L zlCF=@s9*c;nf$IiGf+9ZtH40&fzp)ccPj0)B_`_J(fR!XO!G|rOKm*GrfVWt*zK3* zmr;n3*hwFABYSD%kKGq7e+yw_H;%xIOwGqSUUQ;4OJm+umGh(7kpiQ4lD>U|0)q-1 zJZ%Fo%Jh0^dcJX%LJKS+F-r3ln9N~})>hQjV;*A7B=@_R-{WXT<5FO#Or3QEe4EVf zPPE1Td`8U%1rD(upWTpg@bis_$rmtD(T|W{cEI1HDIHz;M2OptJZ|=N{&PwQxjzRK zc=*-4earqlDr+aBhAg5UBi|=C=>+7|?$ixvgX||Oo6^#DT{c1R^_H@OM9;D7d~9D? z+kswxFP%;I_2pVW`!r?VUO#VYp({=#EiUI0CWBu;VC1W{Czd;-AtjP+Cg}DqQgpmh znMEkMjgosj0Y0TrI$v+#7*=F?&93698Z(3yUosZY){va@OAnb^V?9H92o1oVu~`M2 zM?bIi!@n@apRhxzFM3)6?|mQCmPZr)xR?6gaHR@vqNWHmMRxtQpPd zRi< z!SCGu>&cHQGWY6N)piTfg6U{QV28P|nhVK3=$_quc41Cwi7x$UtiAbF$i3{~vXXl* ziy&WNHn^%gri(YeS6MD$ZugD%;&(B8Ge?d3@PLO#u-(Otg%BccUn%cA6(3>P63$<~ zEug9~DrFMyIw3EgJY-Xgo~90{sCC+0{8X_S_d*IW;ACGDDlQlrC7m7brWBB>J*Y^)T%J_pr_xaE# zeluBfOY{&?WtQZE63__ zEQ_P}dZCdvniIOR^HkopHG=SbkD(T1VByq88tBK)wT}0#opIP@&#OQ5J@}%LXbA4u zaAjpp-#ZP4F=bxIe~g9p(uHmwR&2|8YJ~|pBzpBVRp9l;v^Zx~W2lq{TFK=DJg?QH z=RWlx@w}8%g6MWK3`224$dJU7HlhikP^*tgndIAwAR1~IHH)7*0k-- zi;iKmKiPLE^|@h<44yYO-VIfU+A*R>5i%7w``+$%f0%h+_PWj`n7nmYgtNJZg$E9u zBGM$!@5xY^ktilH8`7w(EQwLSdB{hQo+axSNamW-yKLxUZi4_w^Y{StECTV|IfgHJ z)D5ohY6U}*XaC0Rv{yauL*&=Rkb;v!#nItT?7h$w6km(8s41jx_V=VpYQowrjSmXF zvsVrLeaA&ow<*q>=}289ohbT~wv)0?sOQt$v({>O_nqJa5L{X3sG z?^gfL{i{2VPcOoL#>*(_3CP~bY%Zdl9)(+Pmwb;=z&jv?dvB8TT$!I+vXP)g8wgj2 z2L9Q3)_kV=%hC&g_!Sj$`*I()8*h~@^Zlzn|7)0Ux%ywN(VJ2zh8F=a!wbu$bIQcE zaUs_o4N@$J*TUIFWi*zWe4Xycb*cd1Sf4!J3;fbc`yi&xPz=rBB3w+=h3EZ+;&RT; zr`-eSUJ>A2vuGkcOslQGeZ6UrDrX-fjb6?0)1f@x3j7AHPTsLwXG9yhzDE-eqYqd4 zRcAG6VEQ}BF0rJXrn0I0CsiPa%EqZ^BwP{Dl-WH>-@39|cp-T#Xd&#__2(yv`u*y& z!$sln@g{pHnjtoyM2b8Bwym6MAVxlO<9QzPunss2U8F1uVm_hCfWVhM7#EP|3Pei` zdnIy}Q^<J@BdSNq4?aZ!uj-O;yBCky@L$w`spQEqC(R?-1ah;D9eu91Sx7{^9v&DW-26Mh4RTfEPji4OZ2U7ozz+XkffrX z`_Fb)n?6$1(5zxDyc>O(*% z+H0QRm-joBsa(<&165)Q%RjrpbZcVz-AyWUU!rlAd1*>rXQ$YmhP zWh2F0i(3=LkSMP~=fYIvAeRDj=%u#$SVV|ay7lQOQ7P)YY^?=nY28)1ABQYDwU=bE zubVA81F)o<9F~zbhh|Q69m;2S69wR%9!t$idk)_S|>v@4)KP_jek02i1L1?jgLCY7Pr;F zc+xhb+9_9Dr2cQpx_)({?thr_|CkwiPDQC*l@fd{tXfp*IZ<)-L0Q;P012YuiA4VO zb@Ldiuh)%4#^ER#dVhawK=%56W)0q!LViW!LP$$fyJsjGG@?(F>XOfw=&0Hxrm2q8 zh^5P5SZYqe6(XCfLcb*{(%(mr32DKuhq92+C=vN#U^74{1ai!Z=J;0`{RW}=1G+EZ zD@+cN+i&ux3d#VBC?H1$4PdbG9Oh|rP2pk_xhv}Tx8`iOX8YwhDro96u8mP)A(rBY z>9>YT?Cb}n4MT3xt?u9#?NA2>Da0SzqS{qE8$oCZH};a~W`xNXCcxkMs39sTUFCsU zd?tqD(~Ug4WURs#Uv+~H(I+YH%7z0W;pl?}8APIIbq(?z8e`c|0z{Pt-soL$YQ@-& zT77T693yFtsZ>}l)ycRa?slXfdkBgDUgpvcj7)}cx!3d0l0?Ti)1oZBHML%JO(Vd> zU2?SYpWn%|9rtwM`loJ9otfcUfT;hK+hD$mb_Otz%wbr`4D)Ga(ki z!Ab2f>b&EbFy$QLAK={)4DL!Ob)M!hghQ z$6tCw0E1yl_-|?Uh~4b#wNW6sf-=t4Nz9yNK}J?r9EZ|YP%XE$OA;r(_%!^==%T2C&Ar(g1-ub5sHPE|4>^n&9Ja(G?b}I zP`*B!O)NYD5Uyr00bKE9!#7F&iRg!Yw6Utvt*SM;-MF-e=}qId<2Pknaw6@;OfO&c zWf8)RDSp#|N3$xA5L?=Th@6_cx=(CAmW71HD%wBU3bY?f#|<@7I2gJQopfmgbG5R# zVk5sy1X}tKuza}fnjFLW%eC{bA#!qf3y<(a=T~~b$!czI9h);Ah%+fx6mlB{RRo#T zz%HLWl?P%et9jBd;z51J*?~3fsI;LZ89a)aJft5JsNf@$pgl^wh+k=dMAkpQ`%&O? zW4vC$aKYGQ({c5z{PFreQ1WTNUujIFxTSbKUxL5;miW6h%s3$|h4$%kDI=(m6EN#t zmJkyG=%+`W0YrTw8Ge0`g_T6BrK*1CNoblSvuuytXkR+cZ!oEvRDOk+GO&u^-E`I2 zZ+HL3*)R$-1Ibfx)r>@Eokk`8x*%3FD>Z4Fs@c7nnV{1uF7p$O=*mN)H>?NzvtFF5 z$a`|;{d-rrFg;&h6$keRZ>vNAZ#*C|q%77G0KHYo)Q<5_o^hfjPj$3_KsPk$I7)j7 z3;#0teaDqsTeIDof@)EEE#GFN-tmz?$#vkab)sTBJR>;W9gk&&qsYS=ZZ#+c7OjL4 zb%aUIpQjBEXh)q%k#^~$N}9Rjju-Ti(nZ)LOkNt%mwojkt zj<+|Le|ws@HoCnVii003cNj-1E;;y9M6vbFmFRjW!5K+6;1?vOkK>-@m`|Y^FWaac zTC?sn1g`nP2WYm+rQ8N1+GlQQ8>5Ia04)3|wIK#NV!7O?>9SmIVK=649LfXceS#pgj@>qOI)%X!Y%^O*FVa3IB<3n-NVoj$M>EV46&e zvvP5wePbDIv*~~UU}{rP8K~4J`)qL3aog7fM8;($9_?ytGug%ZhQ%EmwR}Zpr?h4B z<6YE~Ay!p7!#y?}#DhBPO0E=Szn`C|cM9#G8Gjx6aYsZ5T2pQpRuX-sta6Qn9FG;VqqXO`=)kpVoYf+VKwA&$KQ zjAT1zCITT&xWJyh232{0!6W$9yxh=d(e(C``$iI4LIn8{nXr$%SiUZQsI_smfU0?c zN^WePhg6NmJl{P6DwMa3FRi;hCY5anQclTf$$t6lq<{Z;qegq5sb(7Llggtqc!Hx< zi`U6zaSJ6ncPpd=LF0d-IoUB0e=cCJ`$unacqej9dhkF4>tO-$-oD?f^|Sh={JJ}o z2oX}j;-KV_lx8;x6trqKv46w@Nl!@DCm=;q1Hva1!5@^9 zK53(gX4+NiwtTmJcdWZD>^QV}E-$xdGW9wat?Zijpuswm2$uh`a3>V;@Dt?bM$O8P z3iSA_9MEbwmxcpeu&6>t+q*Agw!3kaaFzwdXZ!ks`tWc$Q4aB(2-^L0MCgf9<7rYR z#Ow~{Mux=v^_hw?`AE}}4b%cOq0Pwt&kfC(s!7lViSZ!;$~|i4_iE5RDz3cj-((j~ z*p60pXVl17X>9whWqcQ*?zDTO?MQG2jBX7l0I}044<+Okql*My(9U>QAGoeILl@{i zKGLdP<=nG9)z&|1!25yoUiU}QOACr`^}nqC)UqB7D8x9b(R|6~ubAW& zZPg|3fvK8izFW2Al#1)P@gnI>MkWE>fH@NK>KWOoOO+f_LpRSisgcSfCi80DDw(e@ z!7-I}DLKcbf~F~r?Zs+2#ceBy{q?%_;PD-IPV9s-tjf(S*TXDlZA&NSGkWKY zv7vaA0wWl^v7GF5;-hV2onRwf+5Vqb6tm%%aLZNQBJ}IF=5bclt(&Ym0F|R?Fy;tl zW>2^6(Q@u}9{BaepMscT&UIA^kb$!2Zd0}$EWJ|Shmal|Sf`;ua6k-2m(L@EMr#0O zA3Y}ZIy_o0#4yz!1WWQvKv)mm1!?dAJca!(ren?qy}YNEvB|6TSDZyR)1LD1(?*!y zK++Ijf!N|#HWI{+Z7LRW9V&Do=H_)+~yPmz9a%qqH|)u~;- zSHQKwJbORX&723!k%xHprPWKOu<$V&0*;uly53!$4IKfWqth-uY2G@UviyCT0iPFe z`#&xY9V9cwSqNtoFq2XYfPX1PS$hAlRfMTo63bRttesE&qH91+^s1>X&VgsdSZ~rH zaRBP#UgQ{Si1n|BMC!UMBnL52oHND#@NCtemfRnHDQ8!N32#P9m56;NLP!Pmh?bBf zXmt@%U%W4#J>2dAF#Z+_>+SWTaU#49{WAnH^|{ZG{$yl|AxoSrK$MszP(pxmV)pSU z|1(3+U(M8gu|`AF1?S6xcJ9u17oJ=?JM(M%!_kl9+VRW%w^ZY}`bSZjI6!@5R zuyo-vN{F3<+YLQ1ashJ{9tzHCQf7FYGtcD}&1INF^-npEuRc^?BN|nlt0PxV`ZLW68r4R$PWCy*Rv*ji{LqoN zGqzx(zvA)K8%41_|EevLqV%Y(RXR3lHWrZo@lNdIl#_*=14Oe?x)>~Fy`@_tZWr-Z zmf))k2ntIwMgS$|K@@)lvHo63TYiO3RqgRr< z-KAGBnxzSc`T`F+R#aZK$~*OnKklD1E=aqcmluKx07r}uD4oho1&*Xuz(YROO3g=7 zblSc2NhZxk%4kr+wQ1t!8aC%-1PwAKJ0R2peq;}%-w^IsACSRuvoHhyStt5L;PJ}L zVcuN#Cz^{QK@=|A(70G?F82TQ2kh?a;QMu8#-Dw9i#iKwvXMw>9^|NlSdPlDa?gsZ zouq96o(;*3K|^P{YUJG$Ml>Qdk~j)to{06zyZ7X>g10D0lS>`(QFXg=yl1T07aOpM zh3JR=Xx7qkrV|M#+>~J z?FIaoI*b3?wij^t-{~y=Z`)qLf2p(he{Xv)`99S*MjCnnC$&g~(HE=abGe=pe!^vo zqt<>p{OfM9qTWGtf(#9ZG~=@F(BJPHYYf#zYfBS?cAnbV?0!w_+37#mS~`CBcIM7Z zE??xQX593rI{Qj?*ZopgP=~AP4WL14K#|JO;u}9j_ zwwJ&($SdH4rm}VR6ZhqtJnidPJaTubQS#6{UAR*!*T>^H&bp^9Vj!GFa-Y_1<{5z9 zyCWb#m_xwB6SJ}A_09ck|3_Uc=h@P9@Ltajk={%9S@m=Jhv!1@#m~jR=-m*&|6XzU zKH0NuE~QO-FVCeot2y!c?AQqQYBa&o3l++U_X2d+=5YM_mfuSy_~zObDCJuQ@4v?k0Me7AJy=6T!IdGgdf5koS$O zAO=W=Kn6IvXnHVe7EuWiY;*Mx!$WAlLHIeN69(|E+dna7&0_b#(Ko3k$$U_D@HAS% z33o#eyoAjEwjC%r_e7TWE6?U)b(f~x8ox{GQi$um4sw+Hiu%Bn8m0Hib!61pYhKj6 z9k798a5=Qzs#$=j`Cymj=W@ImrQhT1HplLtLHCh+v1Fivu8 z_7u8&NIiM!hZ4holmi~!<77UtcH3a^PS-#Z9zWQpAUX?>i8O-)si4ku!OK|3FfD1f zOBUyEcZY9f<6^@lj($17sQK_yP$SK_Vu8B-c%TE(7z)Z@KDu+5NR<_sn)jft*8#C% z8JlZsj$Ix&n^ESz{Oy-*4EpqbK~043;f}NRpeH|hj%&O(qkaN@WDo(@J23JvUf9x^ z;n$6Rc}DrbYX0Fy{ox%}TlBpmc{BAv_uoxqM1sjqe&?|>&ZWYX3$9&^2!xcR@q`pleboUE+AUT;ffUI z!=33QYoq#+^mq-Hf40yB$3?B%UG+Ry%h_A1Te2bSK4zVaIpL^FA|80DEIThFhjFTi z;Lqjnas71UVV4{{$Xu#&9=6LN51~=&-;qg4WV+@2Cu|7X6n`c8vyo(9sNkK`ez%CA z(eZB$6wr*}nL&bM!kOQH)Bq6ladnzrk{ml~<Q#{u)kIsg1rzdRtSjgKlNPaK&#UiMvE}U`mnFA_g`%IaE&Ls#& ze(h~+n9$7%8!3JerX|?$M;CHLHxQVW_Osp0(Ms3{7#Bn%5h0oUfvm}y=#$q((zUm* zELmEI+|PCGODC3cl+rc4xopr=cdYZ=y4rrjSvAm;LVJeW+o_q0#h#!{Y7I(472L#T z8W@8Cx{@M=`^O>P&xl`b%z7U&Ezgpq`<#vjjVOd%C%bLq6 z+;CrSIXjwN`bP8h-S8Elyj0bRL7^C?`ItrV!xK60zD1E<#{jm=3U3egzQf@BEM}1Lx?cMj%93eV_RJ%)Fv4y9-VO0&ZNG zfjR|5XzUXEFn_>vec+?4ie(Ocon?`*U;I#znB%2~J)04xm1Z#9{Ucc=C$Oeq8ClUm zY^{Fy9?<$4OC5NV)u^a71U)m6EggyL9gAB|{Knb_iG`-37}EjNPL?%oJB0TuCkE$= z4F|i$H*~n7Uuj_=gq28bl4t^DYD{N=dQ*@sqcWIGOe~;(t46v=QbVP5P(S3y`!YH_ z{(5tAyY_mvcgt{C{_gtFf4iwV`@Zi{I2n!UrF#;5V(ZrDqDYs@XVekr#gO(&%uCE{ z`WLR&sk;O3yd+9)1089xoFp`|F)zKTCYWp%?Kk6awY7-|t}O{6Wmdig!36O%wQ6BH zrTytyLlpq(nJWNdyoe;TO$iYwwXEZkM8?I?&q}OW$12^w5dCq08K*ua)g_iF=MUxI zjD9`=+0V{f-gGyIjf!~`N|g-BV{Wev8j^iDlhkekC`H5o_8)o!%KoZ)%Kn7G63)D{ zlfuMVWXL%fh9Y$)=6>n^&aSq#$yrD>vO|7q1s}lG4u)CcyRv$B7<`+SP`40NmfyqT zq(0tOd38=kf6dk^nsF0*-ReM6f8G*?j4oZ@h5cl572z;{e!cV@a`Yb5a;V$StnxNP z?t55@epe_x1D3XMmQ;RJ1*sJe1JP-MPR(AMkt&wmKkUgJX-Gmps(}QEmA&;eu#R4| zKm}}kTmV(C(j#ud88#waH652AbP+I^3E{}Omky0_nQ`ctXMW@ifwEJ^LlZY3SO#&C z%m`pe5^%bW6T5<>(T9agjubHjiwuZL{FD5|z`=_`>Gh}1+ItCcO8v6s33`8H<50{) zRiG8KPJ7v<@$@nSSFSl~b9JkRGEPn*S^(^qjbBjO1NncJ>>a+Pl=sy4>uw)j-N9C) zo3qA2NsZE4QFhViZ~bFW9bY#1J-95$ENzUlPIjL2yrZ7FBt{|?&cr;JQ-uda^p79* zBS{8#y}4l<=3l!bYa4CB2&5smCKC8roMS-Jl}@zp82$J_+@cBc3AbXdkT-VGEG)pB zmQ%<#Q~($l(PbQ8V?2skzyf#mS@djI@7TXXQfJMImIJUM8kC_O0re4cSemZD(RiUL_* zqfn8o>1WI!5PVZ8G|%r_&5v57y8_S!HtC$St&zQ1pM!waW6r#%+Ew!6l-5uDn-H zEj-o_O6_}IvZ@gs-Wsl zl=-OGV4iHLic3K-*8>c(DkM={?9Ot?)&CVI@z8Ri!l7ch3!J^+kM@wA)sT@(<|C`S zS%Fcb#(N&_a|8&5>~Fsy$A(?8)?wJ2&#F2k%d@U#DJX>NzbOB~{@B^^K0OfysAmaK z%g*YnS%?E*ZNu-PXl91m{m9GF;lD99>}gs8t^lI#OA^IASOAOIhbGLqLeZqqdnmcz z@)%8MfwzJKs3qx&&R#fEi$J{oXQIbDemYLQ#Wk%J<{U03nTKcaU6Czd?*udUWzfb` zy``DdI_CZUvVA_K31zjj#ETV@ZAFa{))M?$xXr72E>w82PK(sG{<5v5R@lo2l z#*Gp^utJsC_Q=YxV(Kno(295=xA_|XXmgu&{PCrDkBqVg9pix=MK7SeJac}WRK*Y4 zO3(Dc>0;=^WH1ZA!itlKFl|yBW>wemOu(0_ew^5NJ0NkdJS=9{j%S+1SYy78WaQ6W zk!Ojv5i8kXdVdB-o|6zFmPTk&ZX6zQNNHWUX$FG8mtmuEcY!iu62)bbAD>E)7olKu zG7MW8uY1lUXvW81f`hbvwR=r$XbgdD)ia~SMOVE2fpB6M(KSd2EX&Z;}uC{~<+U$W+lBkd+kv3nEEYXUf_f5S?!tL}x8 z+u(e@5xbL^K75Mz+OzH*e0|$`K>nGs`K1%f5Wu+u;qo0sbaYYQm1-MFEGqSqgF$XI zqO*f68hHgZ9nBUww;+?Hcs>gQXC>Cy!W+j}w6>!Wj(b#pueDoe1CI$tlA@^!7cdRF zu`0=RB50R3!TM3I^e0j{oRw*baRJ?w*40lw+>-gQvI=g+d1-B_i5e4o@W7|GLWkNb z4gkJ{1xQvBxMB9fIckCL(mWcslIc<^$?sTGzB7Kni-<$lX#9hFG^a<`15u(yCr` zB#yfH5pC!xR7fjxQdJ+G2N8b|0iw7nhH+Kvgv0^AJE^=iX0|8Dk4^>~w}cph z;gUI5Z9=8Uhm`{Jr;Zp@T@s}q)$|~H1<*nqWZ2O(Q6$M)tx%toR7~RHQEjWqD!tV& z2;(vTs3cU|!i};t;wOZ9y9^A|vZ6(EuU^?Fs*jb^#fIepFWT`77RbxH&+7U-5ZYS< z|I&daRxz{7lKCJUOA2T8svw=kN+qJz5W$<*a$HfSkCggYH%6t^^_PB-9Mv9z2EYIo z5BLD7`H#wakNVUnbzJhW8*(TwNefaZ$Rne1JFLbw?P0k(k39?rda^&-??qWqxR^i9 z8!G)drO@?j<~u`b;%((qq7i?___iYtDYx@v!VS?R~CbMcW|lRUrvo`H-O?rVn4#1~8rv8;oPqTU8Z z52~@z@Yf9}nqqpbs03mgEVR+6j^?RF_2@u$qIFP&P=Q1kEru~gY&nqJnU2^<`mM+Z z>>J!@P*R7#ty>)p=j4rL2;r(Omh^9;Z3WE^` zs2ODR3l!vxzC^^4$B@`oO~mgG?(Fv)KsPGVgjW-{D$egh#D{GVSkYD}!3usS*`N z-X)2w`t5%UIdAzLK{dT41uA}~PSmZPyZIm_dW!wVPPP52t#|?zpa6mV(ME9e*Ocvi z1Z?RD*%?XBz%XL8&y?5`$R<#RCtpj^Ym9T1wYG$Pi>AtJmZknS?Q~OrD#mrnOx02S ztB_@Dv^yB<*TnavC~&U$HdM?n9mRuYL_;}wT_CuI-63oC(ZsllA<;2Z!cP=1#SS~g zF><7-k=ksZd{(Eo^SCku&~_IQgjSeOdUFdC{Z$2oAP~fHuA5(9MjoYT-0K%e|MMl8 z1evEb_*+>{v)W8kUUz=A`h9#cR@mIWHC(-NW48D;N4cog>>m8 zs<&FAUo=tcCk!UaU4Q9 zx0JRf*cPGDbnVCA>ZtnHnJ-u)5BSv0cjU7;_mxSXbZ`)d$}RODdPl|5BKb=zaRq1j zas`y9HJqCDWXlCI03AzZt6*PJy=+R#xSLzWhcH+S0x~SV@ASDTKO~*HEw<<)nTzrw zVXGg#pK0KI*~N!IF>bGA`aat?n02{aenQWk51d>kj0Q*i!Ah z#S$x+YWb;3v4c!|f30%LBmP*MxGXWhO7U(c@m64Hj36jCVCn0~p1Dp82TSChrEqLSpxM3cfxpf5iVAE`jeHwj73D<1pj6U#;K?UY zt~-`3Pb$f5rswvEsr>=CChe(Pg$|jlVcpV+Pi0^-fU0#+y9|Dn^s59k@4&+vm*HtF zY-?M~jD`Gkgs%R(QxN66lF?DXYsv?7n*z_0n4$t%WtF@^6OQNH zujNB~qYz_>!tKAe+}%Arohl^b@^t-^e|}s7qI?g#1UT`zAD%)=q+-bb31YuZfA!a% zlpf~Zb`BN}I`cpsC$g@#-BA$UCw;KQ3}~kDJOFX1)M#TQE$;OXpRMhUJ6bC&05zyV zdGTRT+CKpZ^s{JVcZ6w;RF>bU{#Z$z>F-D`!>c%JUbJ6p?%lAfIUjKOtOg;7dQ&2Ri@W8 zTu{i?ei6Gz5a6c=GzDnE?g=d|kRL;%sJ$Wf^qb2uCVuIt^1h8zvZ$}k*^L()yV3yS z=Z$=Q>R%_Ivc$rWzvr)Q=%5hm_pCAadWn#RzamL{oRL;Vv5r8A8|VI=!xRmN(ozw$ zAe9#rod>;fq~x+&$V|*(0&}-<@(6I@BvSDuqWiHvfce$Y+aiUsWvdIJpF%VNpcY*Q z{cAH9uiNOt8o;t41UV~lDAk4Tg{0%3G^THT!4fRN&}Id|_q_EEM{pWgeqERBa~OD( zhB4@MY4MVdP9d^J=c1u&prCv~_(TT8Cq|WEj32#~LyBhv(OPLq6t^6UBtAI$AY*G7t z{nUl*_1o`+lGz^3<#JIFqAWTj=?SSgesJTzk81@YMdbtaH=4p$<(v_=D*o9Ia>?Yx? z!rR%D50rVh5~~+e5{`3l)9V4Wqj?br#v*3qsR9Wwj4(zRAHKN9LhR2JsU8V1#DG>= z#txrA(e^`XI(k^p3gj0C+rGsdGan$J!eXE@e0p615K*}AY?h^)emLlwPP1QF!%MHX z^-&{l`{fj^t^g$| z*R{NWkKE`a!#uMUD~P$M=3!gXLm;>-SGN3U<`OX+`{F+b^k884mxq4MSHO$w5!ijC z7>OZIZVv(|#+!@hR<7^;3$w?0$0lqba`Zsj;>*5)hh-h3cbm?kGHbc_1*GD@cSeNc z-G>aeuMs58d3OgZisxJZ`faxdhp(L4jfn37XoZ+$UpKl>FHAhxE6@D_Ny8`5L>thQ zw}|@-e`+YSxZ>-{XUM2`AfWfi=hn2g49F|~f``KFTB11)gZbT`K4(}T6Mn@Fi=5m?n}pS25X>3T$^tKxX|mbNI#k&S1yGEfN>4xFesnW2 z+&)UYv!)a32JPWn-pbFOPF zmupWtwwb5%;(9W&03h~nec8KP)h~epVN=(AR#Il#N}?QJOcg6V>J4^s0jO&2it|3} zO$HP=1e#H+NaQD!NVu^zU-mvnET+C&2c9k69_XaAQ z5f26`2KLq$M4R=SKjpE~%WJ1myc@9nEZ&XF$ga77c1fB{1E49at;wA_BU&E=xVk0x zUE%exY9`*qnOIGZ3>jHD|5TeAI8J^3$#s0UT|y`S650%Mln5bSMPqxD`yA`)>bijM zWoMj|X3z-AR#s=8#8%sXxYN-gJrfP!9cV!TX+xkQ+?L0|d!Xw|yG4<#lkLk^A4fNj z9uZFK8-Na51ppgfGM8iAfa5`)6aNwb)6p-Bu*89~w*A%yT4<2Lz`z*-= zpT~IoLOVr=^1$*)ts|&Yrc~b`Yiq`}!XH)qF^STW3X%?47GSO_3;7dOwS^w51T-?v zL8cdk_0n4pg!S&Qh}`?H3@d9`^NFsQn@X6bS31A_Lj?WjgOGUerH?T>qirZT+B zf_s!W_Wsg7qxLDjbc<4N@NvcWRnEs@5}zz!7F^w!m5?PWy)ac$fO0K{8=Ei6M7QwF zCRtNTCCdex2go|-Q;Yw#)KybpNS=>AFG8fz_-^_Q9}(30tK^o}(>72?S6B7I_e~_G zq+qb_3{W}B$>QqMZW=D0AxqZ(GMMW4LY&am9Lfd`Adp6kQHCHR02O(|-LJxzze=Bi z%}X1V!T7hC{1aF|(~0lR{k)WcKdVN=bJFCit0-NvAtYcakpS!%pFir=J^d&gkt`D! zNaZ_3;l3u|v%f{TjzSs0wD~7H{>6;&y}$YL0li!uk)jK#IA+>i&y$$A) z0VJ-xGZErI?0+3ohg2j63`O56=qJM};MIwAwI#gvvzSk9KzkVBtC&aB9{5H}FR)gW zk(}mV@`_msi#bb&v|?KEGF&hxDC3GaGeaxxwSu@GL9`i5l2Ekjl7$>mCv)qKBW3c( zmqzt#`VOKFr9Ckhij1)b7V2n_R|0V)8*V z62B5(dd_g8jw{2+2mB`^ebzH&ixr!B_BCmb`6>8J3VvN>3kZ3jj46+j^4S~64mL`> zsLbQijGsp@<$L$t#9ia)?o_<);PJ0fRS0IR!)-nCSXJ>JuO}PTZ=~w!8@QF$@MtTN10} z=iVi(WtGp+bpeg6S%H)=1#=MtO3?AF)?7L_BJ{@~8ndmbwZp7%#fK`;q7rLltH`SA zCzy>G2-MqTcW~O;0>kLmD-5+Kp%c|>7Jh@*DB=G(3_X7j+%sA$WV|HjKQmRs&n z(|oKtl7Yn0<%n2Xt{HIpN1TbfIqUdqalW_hHjREBo4t3VQ+CS^$hV1yQ#e9Vawd6@ z@C?Wzu5mNKAQ*e@4=`C70G4a`K74^hgK@CTl_}`Ao@t?~ljh0aliA{mrB2X2w$j?u z2KNSG&%${J5&GGqmG8n6Q?KBdDTh+f3a8HYiKupSZXcrq3<(l(^;PN{PMCB}^x=cN za|=To`w=Issqd_h#PViZQ(a!_$!{fpQ}0HSN)>9>8uy<)zy zcD{y0N*n!4$=?Q8mb0@iP9kF?FvqSYQJL6GFgA=KWnwX4`|Ns53WPi#m8fKRx@ymBhX}>^rRil_Tb(z0u zw(wI#5zBAoc27m)1ClNepn6rx%j%LbV@AHTvg%!AQa60dDuE#^GA5@#k1KWcbOg-; zsgp!coR9fL{V|gxfyZ2QOXhE*?woaoDd{onegMhdJ=V|J-Dwsxz5>1grKc3ux$aeka(3$}nG zk%_!N&(NR6Vp7bGq7jyDR-84Nmmg6$ybHJAGjoMwN~B6er5ZxWJNjBofEupcXi`` zO%e!vbX_9)Mgh}oY}Hq7=sm03+aN-=B3i9a>b7!Hg78zPQVqj!V|(3KJ@ZfhIm!jq zdA;WQTlGt%8{2O+$cQ9z{T3k$ zr2%q)C;=mB;c{9{IxTAu)crpIjzDq0Rz#^RB-IoW|9duWYFAo&s^j$x&6+N(U1KtYPSFJC*!aJ!Uu4UG`)E*RZ7Uq7YC4pUUV!4bc-y>iCmo2Fa2rLCN}jlE?ZW-;eTj{~-@z zGtrG~WOrRrUvJtl5P#37IPug<8LNN#xHprSVlBjxW6F3mi6NdsjAAp?Z@;@h+jSBO zy7T@1-DUp#Ho9XM;h}ki=cnhVZ}@qJrP(_>K;@$y8fV8OsqA=iAviyP3((oo?%&`z znjzXYg~Mnq_yaVZ8ILxF=)nwcaJFMWfZs>s1~(kRK!b`2PR0&tf zjWfsxt#2+TJ46#n@8EhH;30MvX7wF0dz?gCYx=|uKyU9)aCNcsmk6T`adZuFq~M08 zzqI7x{keB1cUK_zw1$u%=_OzS@|nWPx9&jyc8Ybr?0Xlw6k12r`%A=Tpk`wSLSc^Y zeuQBAK0yYDzNf68?xq>Cex`qg#`}r_b*`O1eNBQZI$Xv9C2dR2T8}H66o1<$5{AYP zzVH2&qBVYK9VH4sl0@T^+510s%5uB%;3HbSS)c=)Kd*X!3Z3bD*xUQU@EEvW^G}?S z6JJZA2kCWd2G3Thi;!w+_-ZI5O)KDq;DsrB~jMZ4rQrHT;k{f>wIH6dncOaL* z${qX^Wu5~6RViNAAXOsPRUtU`M41(vT$C@cKwK#`6k;tjl4=Pgc&8G)CbBhG*$PXx z5QWgYG+7E=66aDXU{EooMYbuJf@-5GS#y-nk+u}&QlSc8^HR@I6??#6v4DETih`sP zwn6ub{6QwGof0oA4J&_HSokwqmmc5?^Y6{i;I zG5lp($dY1YveXL86jLhw0o{t4r@)k`X(3A`*J(oQM1-735LfaW3#}TyTm(m`-qieq z5OU56&ufD)6vyxRE8f9_w%gFzb)b{7!(e~C6rnwBj*^B114#@? zJjn2WU$k{y-Mc)<`+R@oWut42kiwE$jR}Q)Ct0Jqs%%8ZxDZuY>*q8_h;fAl5P_|Q z@eO?=@T1$4-~_D0&ac34<6a#UkUXX7YW=v5z4|}MT16ah6x7CXNy7l!T`Rq;Vpv!j zjgoW1FoCkeO>%!?nvuU?`IQ&}+n%g5p~vqaNg}uhO9jiasaHO8OyX%CVpu@PM1x&4 z9Iqs2aH$vlX*78(iy?Y(9xZNF&`|ZaY6h#3m*o+n#gj~&2do&7~LGS`2|)zOyrD&hG79p61@}vKnDt zWQi2TVk&=mkuc5|(=@;70o9O4p2rdh7t^OHc^%K@lb2^Q8NqM>H{D*h+kziYp|MIt z-ny1vBv!zTIG_B1olzre^GAHai~j{lqyAwEm^Igeg!79 zp&~|^z~iRCX*@$Mt%~u%iub=XbC%^C-!eYtn4y0#5v<)fr>9AkVOcYW!cWT-66wkS zA*0ERkWO$_#n*8;2a%W8VfOpJ>!!@TfdPa}m=$@ z)+Chc{cN^%kHoEW?yEe>2Non@N}Gf2IG|zm4Sw|~dnZyMjZ+BgU*!q$of-y1INH1t zI46Jq^1|sEDGpAI zdozhytcEyp%uqGIx6R_RvSTYwzLPm#^PH;pYb| znw>QxR4$sawZCtM^KP)APoc9!y*o#225PouBoyZO>^uZB3<)w=^gU&Lb2rV9^%E^L-c}T- zbLrjjV-j4^{_IDTG#xqX9Ik9q{A1ck7#iQZVQ?3U*1EB?lqmd65{*yI&iyr0mg|*A z7t!j~0v+J=cGcTc=$m1How+RxkAZ*dZQjN4Bp)Ik-ZlfAoS*dmkNJT5OAfVM=nYdG zL=BaaPa@|z%vg=}EQO8GOS#s76N;633vvOh+`=zW<|**smEv^`QYB(l6@p_=lv%OP zMfnJG#FbJ*Ayz^osg^*3H!8tvB3p5lEwN;CQ3$JAsbMLLoMUs&(HWZ; z{3?Z^vVw_K3OQS`M?Rq>vZ#c{uI^y726qph< zEo7nODotpeh>#Nr;!1vHp;dpwhl}6{)$5wS5<<>dfn0c=^27Nal7BtRu?mAg5Jk~` zzQX@dNFf&1Nx99szD2=(Gq?j$g8!bRvAZ0&-NP0WVJxT?6Pl43$uWoLS+epBy%tk~ z{wr495QZbcd{wq0D;<0OkGGCD;c#%SO?WN4zT>n(K61$yWsNZk0x^FOMf*9$+(W@a zEVk7)P00RWATt@95q1&pZmMnU9{<6A=Zig!NVC8#7x1DLC9tN)E_LXhu0w1nG$yH! zj&$1!EZWc$QR&&8?{AGJI0R!0=<1yQOcU#ndFOnLiw0hG*6BlE`T<3b%?iRW5QOjh z6nPH?5$T~P4_EbOv)cT=t1<3ISk z^F`lEgh|+R${c?p3TNjzJvS*GTe^194u%hc^~UM8W>&8qC!*XYvv99w6|V?g{IG#n zXUrBJ3%m;dMHOu<P?Sw_fW^DIsZ@FuMyfylM_*_!E8kDP)oWl&leAK7aZ0ON#^{aQCKVf$lMMm=~(i3Lm&Fm z54}`xkD4$Tf6u3Q?xFz_F1c;l8yp&j=Ej?8)V&&uK`h#&wxlgy%;I;yQb3S7bC+e; zgg_sj{{EHnWt8nQ!=UjVi7ZYKgfvBoM4NwTaonej9;1NmGe!}W>WKGg7bqj7bpUx)pWjI;94b*=D z)2(_x>6ufn49n5fOZ^E`;QNFsW$%%^bq?Z?-ZA1uS^ z_l0R+p+EB?&Kb9qOH8-WQ{S10x5$5ix{zXmlnC1Ip06jBciZ?308 z*w!*NpUS_X(i~0+>}a%`f}dkfm9%%1TrvKO=nY!u?r#09uxIYf#!|<77i3qhKq0Ca5TBd*N_C;|O zL6shBPP;|W9JZ!*x_;vSMgoOAuP>Y;nBuPD@VeZ!?Q)|oJvVrsa&FUuz}rv-BnZvDL53B z;$CP4l=8slZN)uF+lmgUS)u@v^_3Jafyt4?aOEXsu2!6OG%iWghiiY$$`-0f7HBYC z-wUtN@SN(hZDVMOP`+Yky>?^UOcGD%`d*M%$2k(wKJ8S}vLPQR+JKdE6}e z$|}f;5Hp+xY%oJnW-Y5HXe-F@Vx-QIB&+64DAD*!l3cjPIohjfg8u*9eL<7{g&Ez4 z(+%=5@E_zl=DG?+A(HLpvv3cZ3T=_`5Q{y%ie$TH^Ss2+gVLN>c3rxD~v`lxyPTAc)U zByJtam1J0^@ZYPeE!%Qp==Q03h%Y+lJ4fd(zd0#Z#b|`%HDX1aAaI5kDB&oLE*{nk zf*+!{WL*%9Fc*J!n2{Vsr-&sqD>$KtqfyBQ@yv=M&ytvD1RoCN5pUKpPVIFt-!CXH z5H**FX25!iwtMa+NZ;PUObEQyyTKqU(PT`bRm3rI7;%EG4LU# zf2QOf*Zu1?TB*2MbBJgyhX#Mx2(Y zZ3nBfMyY?ykcmlhzxf|vizRK9-SgQnIi66Lu-~a>_%;b29_*@iiCUmgLn#3F!5IS-{Z(cZrQ&T%m5lvn)|7u5@BuL>nK6L|ZKpy}#_~bTSpZqce<;6;vEh+hC z86AJ?!$)CwxiAKOXu{Iar$Jw87ko=;9x1;D-szs$KZd5PR6t&8&n+7Nk;iw5Q^et1vbe5F{|r# zTUJM&j^UOVyF*;&Ir#4ANXhf`IypdSFn=qo)##K7uQGlq9_5}!{-hXuJ#(m!C zdiG^DeA?n;qs3qkS6VCo>e&2mylc9Z-?yf%Sp{C=R}JxOuWrq8b3zHsRZfKJW_W)> z8-h#7WJ7{i;6(HaJ?n1FS>1??ICnzRL)nf~?@-JGDEps27>V&SOZh7BFo)oK@MGBQ zX)aZ}scl&7wkjEcpIN*_jiwnXLkTt0GC@sgvpkC#VzK~Nh8Glq;X!oCQbPGcfg)Z~ zQC=0H?gQFbio|7I=kCiO`S9foO>}^)DJ_yH)wyvbY6Ym zIs$dAt-gBcdMmrqVVl`@B#iDsBfsk$Y3k(>+Q|Kk=cw**(hoKFC%M?b>nj@urC3nAal@Vnoobl((w6cQ><%>C|egfN$ttO*15O{+|s18RT?q`B|{Ax zZ)jE#D`=v1jYi+i$SsUt5M|~=!hk^BV_=WT;PT7&EY$d$(#|$$ zKddj>3&<(O1ajM3X- zg=317#fSTRRTlS)*JWOn1uiH&6{O7Y;sTSnPAgj0CzDA-M#-F4RhGsPP0QkBtPG6g zQIW`hN6Wn}s)mZc=k>bDVeT=q7kHHx=}sDJ(wCKxjI)Rk^f`ael-CgcN^y}8q=qJc zPZ0d7W(i(GtEPyVG+JY7sd7=q+@R1JM~6xGe1u-2#l_`2xt2qV4cPB6DRXp+3jD=b z6$US!9|uzue7yQ)_Cs)VVhGG$MO6h#zuu%-5^9@$e)k)Ii6wE3BZ_~{u)tG`f+GV? z7q)<-_Q1#hZnuBNfL)caD&8Pnx!Ei5po{?$9-)Y!Zi^$$CtNSONr_aa^NOL-R4$SS zBeDC-b=4lm*vY|5H~cNVKCBlsHxdb|I?nY1g+5LbUAxgdth%%-dHZ}9AS-rN+Sc6zDrR6G-n0J0-a{+tx}_QM=v9 z#!ypE?+oqsO|@+;M>=(6j(9fU3HMQU>xT|gE$WqKzh~y1Q0toxMLTF`OdC4+^gU;6 zIY#H6%H|#t$K6@FbiQVDHx5wc3%6L86E}aIKV0_v^xH!xZEWFhA050Zv~@r|Y%L-_ z_NhzCZvYeli4a<1<6|&1-qZ)@6!C^hev1+C!}ZOPO)4U(zSKaEDxY@!>Kv!A_?8Q` zXQ|uXR2}|Y9H*w%nhe}}>`i4?r=(r3j8(m@iH=X%r|UUTZ(G#sfEQj!+_N*;&5VE5 zTw}O_R_f_ixLP-hMmuX%apV?>o^1zX(>tgQ%2bQjSo_9`20E;w8V1<&6sgfPuK*t> z@A<54rmCXmIw(o3JTV9&xK_UQs7`HbRW^O1{8Hdr>J0re-%QlH4q3JvJ;L=k!aN(j zjpgO9ccQ$~g28!2FB=Nk!kT5aD^%SQ5O8te?4vZ67~^vhCEZ?*O1AxZ_gp|>&0DE-{P+<-+h1UWa8g2 zP~Uz4<(Xn)P{4in%EDS!!`hc#1=D=#_VRWo_is^@=$L4bs_k^6;HyDaf;uu%-_AbgAtnVwvKe6H6`GTVvJS1UL!XY0|ML8sJT4mS-{XcM#c4S^A z;#WV;BkFg1Vmatuz(0xg5-!)BEx55Z-+$9%;#VxUXQ>Lo}ayx&%FWY6?YLHp08LO2?c+^xI}K%Y!NmpOF*+KtM}~hrHzccl-IgBB@hDj0IO}L$Dz1luS2~ z5@b1AQ;L|NlkdF@0{s0f2=X9E=Jfg{;W-SFLvUh|r48>G{Ma{*kZC|%OGY?QvqCS0 z$Hlm}ow9#~=bo=!OF5nMLl6wlVHl!JL>bG9Dsv=BYu?z1YFT3?_$E1H*Zk~^`Anr7 zuiY+f3{Z^WP0x!vcPa5tHf-hexk__;EN)Qg-E0cK-0_hn{JX1Qzbn?%-$OsC2+HagRGp z3j6kOS4%ZJ*lAP|o-puWZm*Ku-w?7WixFTx#Q?@}dXJWUE#c{$cu&R6s^jsEduFA$ z?u37#Ms9RT08IsRlzgto4uLTQ!#mI*maPyW2M|~Fr2#KByMhh;$JZVWx|H$04fPoG zxNT~hVZ?AX&5CPO$MO1vIt9_7G-#gfG)(_Dy2a8Qka2&{(r3q{R?#7tI>m-_-j!i&);Y6n&gr03 zGwP@|7Zh*6Ie2Xo&=~A&URBEGl+$0BCPkE#sQGsLq!7{QY(1jt3%^R-ZXN!w&3C)c z|Hot6b}J=fo4^-;K`-PN-Rg!Q9-cF+we1S6I0Y+68dJ?qk$tML(jZfU8a1~&7HfZe zUk#0$>PQ1ddr!^&4q{zE)H0fn7_QOIaj2-RMbE6}pn{GiB5b2lH<-93pTh!JM( zpZ=T~!0?dfKuPcRMcg(I8oNxR5pgKYNaS-(*3q^&&d{uZXfwQ2Xf}h=fxKh|_?&*h z?G{%@SL@)O7{8jg?nt9#`r$*Kh6sOSw4_~B!xfB!R$q3(R?vFLf0aN`u}&@c&r?=* z{%nmv`PiBO0P>d&(jIc%9laB3HF)&3YtqS8H{D=YN?hKk9cX$q7KgSKsv-VR%}-t6S$ zMBMXzA@vq!zA71-bNZmfq$`pmunjRsMVTjZ0YMN7<(SkUO^EI-wbFn18$Do6u$jw> zPS~l>SHExF&9#(>y>M0ASNRO1nC@l*749S=Kyin_Apu;VY%$sfiv@19vH_>nz&5)& z^)0TYrk0j6hTU$n&L+(#K257AF6h3yW73V->JV01Xo=Epq>(L4EYE0Yys(?)QoGN- z0vB(`D0^o!8vXMA_UC`e`^C-Vx2x%g$>kMqa5WR7rQ5i!i+(Gu2UIMvb)=z_o@R6j zD7z~xfwdn_?lUs;VBp?#S^aRr5Eh zoqx5uq~-iVo?K9yI-FXVqd9qU!`NDz_FB9|pJ9P2tf;uiV2FR|)Iw5TI)1vKtA5Jr zfSryJz1gCGYgVe5ZaQ6+9X7cs+gqTLFTe}?DMLAuh<9D{dj09fbzbCX3T0^1Vm)(SYQkXQRM+@GF3wiD`9#?JQcJ_`Al< zwxX|Z-txp$)b)Q62n#$yXSI+CiSUAJsFb!GwmbKfB(Hf~E_I0Gf0v5d-aOMqk(BgL zY&({pnYwo5wGoKJRTuaQ*)uA5-ZmO8r#lQZX1%r*;sxYKqQ+zY27lo+S)e4O(Nvvw z2ER^kZx$bKuBMlh4_9v%)Ay6&Rckb~A2ru2jUzT{C&jO9;UO!P`$UHo z?)NBJGa-MkiRo-8hG*+A;W%{O_jk@*Rx!9fxxjAZ$DygP-=-S0P`wZ+w_m z201m??5VXO<#?=rLScOUL!wBFnwIhFBHQ<(+P@^w#9DXIRk>6=GN88y{{pR6-%r~x z5Pr{JaUe!gsZ81{U8y?8en^Z>;<2eJlQ`5OwWEL78C0YH`|Ko6?bx9(EibL?@9w_4 z`|gsDvtnHY0Yw|k3zT5^PBWYcoW{4?O~&XpzGRz%QA~yWM>)%J{2BA4B!yt*BnT@0 zDw}yxPn(E(I+g&{+xKNR_GSVhzBViF`d;v5WCIw|=#_!EG9 zit2w(UP2|2rV(c@ zP+6jFk21+1Qdz=uI(10#k%2Vm5XuXwMWdF{XwP}q{VTDkO6s~jK)V(1+em9+{8!4h z+71zpObIFO?YW2j5#bz*s265YTP{a%dI~n8)wc%plN9TcJp#p#Ff{m4%;m69kwt%D z3xVTz{1{S(IZ!45KM^k8hvSz08;aACg4qnbY^~z81KU;4*BAd(>2Pua7R%v5a_*7R ziDxtw%{+4EaOQ<(MAlSe93$0hrAMP^87nbL)}XPRm3v()P9s3vS~eH^<>qL(hV>G& zqjc5LUQLI6f1L_7;OwH^7INgCpW%PVT{}{ddobb6Y2*k|KXTy;LMuFRZg*ZLJ|gvS zH>2baGN8k0$J7%Yu-w}(`?J;ivODLbY1Lw>_qlDe^R1@6bQgO)Cq+#91?syEL zPS?0u_J?&~*ens&i`CSef7k9~r7WN6O(?7F_N>j`QsNy#Lv=978O>Ah1{Qy$T#s&Y%cAwO%M@%Yz<0 z%eo#bn^>QF{GgiSplsj01}+Bbqqy0Ad~cANl$+%cF%NQR0eWPrlvE4FSgp5M_&TTq znlN|V`=zLRV>CsX9d3+%i`swA^jmTl`~{6pyAHxI4BY({9vDFC9~8s@OD6_K%8;gx z2x@IBkD)^RJE7tsG2!LzY~R`ItzHy>%}7y=C2ZJ{P1Lu?X>`I0ku?7Z__Xke$THsO+`zkjvvslO%>yz5>PZY z?SY`maW1VPu`S!7RA~PlJBgd5TUpi(Kg4JE?)$#$`R?7NT+1Nfq(ChRMVRvpslhb< zx-BxnxAA*XNWqaCYjJ-qa*VG~Q^lkaDhh&94~m&dnKMcZ6FeFkV{Zz=Q@;<6`|z8h zbWHJ_{~|d{XKGaz)?@d>V5m8lrK!Ij0&8 zfJlB=9DA}eWRGc(2ffN|6dhoc}q*8Ob&Nv#rlAYP1%n4w`h4E@6LftW2ki+nX=9H_Ok9>rbtJS6gq}HWYsMuiy#;ls0?a^E}5078K0}xW$5Y50()y z5^b}PDUE-W(gv;n`*@W|iL%u+ez0ZoobP-*Hy-`5Di7skLeUZP5~Ud2(j87KoF#Wp z$6Z06l0S=MSx`(X@kDu%|V+k0qe^ zGjPbI`q(2b0ZnlLYS;g?f`tS9-f7cNFv|M^&QpTkp0!1ysHqa4xgcA2H)HNIm<* z93Hhwm_~!=GdT^pYXfaaG@kv}>4W{G8Ss~zv&&GjIe`OMMDhZRrj@8_x_?A@jd_1R z`5JJMe&NjMbqr}{u=L3zs&HHQt(qxnt?XBLuef(P+HL^Q7zfIypRd@R+d_c;)qt}0p98|*+ z2Gs$BSMvdGViCRqdKq@Zq>#759sWVbozj)KGaEXz3LxPXYt!nRwEb-kX9H?7CSkWV z@_Of^+_1?WaHBIzD$#Czc&>GXU!p*qlZ`A7Pa{wqq5W~C(U~L9jIBfEFePq3J2#_P zB*n8)Ff)owEUz8)t~B4yMrnWegUh+spWsDA+HNqKxDoHtHZLzh(ne*Fa^NMXKe~*2 z`{^5nwjYf)-k(5yx1IWTlXWxK_2P$0q_w$qon1rkVwSqYX#f30cW%{hF81waAa!n1 z1LU`_Ze@E?Ud47sTTLW5i-BLH;hLj;!18rY5SQc9_R>7oXdN-zWAA?zqj~l0&E$h5 z%pj+*qK!xC?uiy9Cz8|5C;LIFbLpt?8KegfI4}Fhe9sCo6mp!s72C$smbI2X|Bma2 zp*e5txve`;A4ryA>W$N~gx;!UcQCitx>U1Cd7AtWg_6ro!!Qs<_xXxhM2#Z_b>C1B zf=xF_JeCoW>rPrrjvaq&2U=+U9p}|1Z2@l*d+yA+Gxqguu_-)H(i}}e8Nxzl$Skgs zmtCGIxl8UG6zjuEHBUmaWz+YiTOJQLS zNnJC7UU))qf?)c#oAMxRSgoIiO10~6HXG6gn4S>EhYa6txVgGHCtPl8kYdn2DmN>^ zzfhCzaE#b z$U-O$k{%az1;c-Vm3r|ENWx88-{B$iR5ZYJl6VxaM!Qb3Ft5(CYk<_XLr4l|Aa$>k zZAj}S>ZM_P?kS_fxa|4-c<=wZg2`k||I22V8U!t*tFYhyR-x*jLFN4bwO8A2+c*$? z_g72;Z7uI=Q}oqYCkV2AD9{31Y|$4h4QOg)5tT%hq?~^Qh5x-Ibuo3bU7*!VWNI$w z9L@}xe{PGUn9XvMp)3eRc$ddW6-MFzFImj`@|mP1!sQ?G5|{*5 zQD~gXP0A?5Wlk;P$49D3D^XJA>pv{tg7>XP9{>@8ytJ?b@FI+fMys>rWtuK*$cm$R zSPcF}W4&-bm`5doKS7R95WeSIMq4fYd8HyWc_Du*rGkX)5Qu^liT?Els<1$Igr#JY z;?;a{-6q(#Zv_#0qn1mTn8T=H;P`qfVe;>r8n&j^-6qFGqWWyKB#H~aUfZJjw+eaq zv=~O$u6L#4RBvO73WePx>9=B44*StP(kUv1q~BLupejY)qi`&i#_{1$ZvKYzv{Qin z1a*JpZi%`MzeFUL#xe9vy5NtCF~B$}?tiwqPK2vE@PxE=q^6N7p9x9ZmiFAxKcj|D z_||FtHmy4kCX)9?M#ts%?r1%XcEUB(s_bPj$cC0Cr zcT#eCFsm2(UF`60Y5HCj4xHCT*u}d*2(Ii?9&5eawiW zi9dh+W&I_1_{+at2KHs~r}uguKwUTsXfYZX#zj*ai)^05IG;Kr{ZQ)N&Q6u+o~}w_6|LbCL20{dq8ozZZ88sE^bUA|s*K?b<^x$Z!LiD)=N=mYHJpN9BKt zxj;e_rv{pIM1uDfRyW}7rgZN6ti7)fMgdJZ^AF5-C#CHEa*M|y-{X87`OchSW4Jwm zQimUl*o{hE9q`2}QLRnMVRf&s0;|>P2#7wIwUNza83G#0GrnSyhm@DOzEadt7y3T- z=2!H{{8NH@I`SQhFanaxClu!TMzVhm+m^hPS5V#SWWvidf(?Scv;vRqMejByGB@RAZ+?#wiO_?i5;=g8Ip<1qF*U6^`q}F(T$sJ9(w(t+&ZTT! zFOAfdC1qRPBw|vZTmHe#a(I}WXH(fKx3g=eO<(g|9eBd4SMZad96;9coK1fmPyRc` zc-4C6Rn~CaRQ=eTskhD)uSqYPx07@D#6j5HMTQuTMXtRGXVGQ4KcV@hVQD5D%ghw?R}1f@AAmKwBL5yP?pAUZNX zly4i}=FTbu!gI#r0-|KuK2yD;>iwr$-be+UdqNFT+tov-SzFL`RHIO<1f5GBDjz}n zTT1YQ~R*M3bc9ZW1dK%~ojT!dqYmwq3 z43%{-;!1^7`cdu(QE7^z3bM0mVo%mSdMCvb;_!spJs_GbQB}Dv4GHF4Xk@#K-T`Xi zf0)G$H;0f+*8`@$pKqbD?i5GSdDChYBeXi?lHRl?tUEnk^(}w5;dI|j87+M-?=dr! zzw&TOJZ+^F;m&Kv&TA{&?nU!In$2t#*d8>(QI~-WYW^3!(sp07(v^$}RVyqdeu-+K zFa@ji#w)6pN(tgN7>knvVwHj1LR7sxz>9J+^ID{Q_mrLYe7R^F;gPE}*1r3uADNtQ z%xRnNb6iTakq>`fT_7+AMp2@b&(xd|m=h2wO70|n1gR{c-4tD8vV-c#c&qhubmoDL z`ckS~ENY8u6OtnI=CoaD(GKstIu_%2goe;X&xN*whn^Y_v(hEMgNje7x~6ta$}B8| zjl^J=8JJ6sDJ1gYTyQ`}PzJIvE z-%F$BHqd{QK#hR)D7-wJ=#yu{uuGwy3Q-DO-0b|}s5~%>Mr;_jKRwXJbQ7b5h@9*C zBuyzZmD)a6OOQF5ne)56z|7*oeoEC{eRn!;fOl7|k&rd8P?K>WkQ6 z@(;~c+iu%95PkPo(0~z1NE$mq3oPt)jlj)f1GIlh5w}|urYk5CZ4;14fuwRlyokuy^9j0QYvL;s;Kft4=Ija2c@%R-n1WAC z@{mk=JwNoML|=%ADD%SJ&mMv&7WCdz1WDy7>Z8E}4b*nhy0j&9vsLYDQmLnGCAkaX z%{qT!zM`C=4N>1TrtPH5CaZo-Dl|nHwh(r2Tl1TbUq4+hEN4<|qHAR1wHHc)`qOC^ zk}yP50F)$LkH_t>W{oncKG29T3EfD$6*4QJ z42EW7^vB3--gOc4cgkp!8N+m$nvYl4moI-3V?qvLCfj)^HSNIvm7A^QT*5L!0kEA)JEy||j+TmzjzLW)OZgWA2&idkWloJTCrR+FxX!b4O_qU<8%?aD#3 zI?me_=&JVayoS9)1Wq{BhDib9{6~MCjYf+kj=2CEqoX5KEY)<|-COmXzDmkAI=1Yl zYWhOx_8=(YP_BDmI2-w&Kq!#-a8~zd%h3VEHlymtu{VLK{(vFU+W+nVxi?Rvby5^| zt-D)#F?Am(3;2U{?=_Mu4V0B0EbiU_gP6yJ*%tgNkB3940`NCz-g*FizuteUB??+R zn@)rUxQT7bXRi0pd6-1n>i=$C0Zp75k4{c~6PJGn?ghNL{AhE5F*q zBrEt-H&cAcn)&UTc$JvzCI&6fJR!WN`b~kdH7{f!h!^}Nc=TCtpGeSj1i>gBv(<}3 z@f&aApPx~`O6{BMImTGbFlK+J$FqE(w|>_XKE-;0=FE9I_Y3!gWrO;YCbg?WOjS39 z=1`_uXdek39Hu|OZ#?vjHiE<`lb3anuj^nYJsS8tiaZu{m(+0si2=veqo(s3(#7r| zAkr5!R$R!K_#m0(8JfOFODm=^F|!JoK5x7$$#@W(HuSx;sKm~jIjMi=fEOA^o}@+P zf0vdXngU>4A_VNTbV|4#BxF@nX&+PR!cLwDpV)c!S9hLS1Ow5;i(HU}>vIOz5Mb0) z2#PAD#%n+1k{q&jURg^y$=9=D8`rwDoLoD*-Y?Bo-*4kK41UjFp%uZ|#&UP>$$CZC z^{@clQZ%^&hjS4qHr;<{=SZF;H_e9p@1rCqu^l@<4t-c29NQv4k{@Z3zg%VWtkdCi z0V<;rkReYX(h!H^)glpm72b$NCOB}7cU*}S!U-r1a?t7Isw98^`~4Trbof~!)>6pt z;k8mA^`I)&4m;#ouHALW(LjRkoUA;`wb3pf-P5_s?(c_fkF|dUx;WFctpkvN7al#XG-}$#g@Rv#ULN$=CD9lz~GQ*`Jm*m@1?Ifa^&LudP z)sV-K0baOHS12APG*!?9R(q#3)w#M6@kThGr7R*z&LhnPC(odNV=7DO%AX05k}el= zK~we*Y^?-V(M5k_ek>tA&(oA#US5)}o-_6SlM5~=NtvIXOR*#zmLz=0xn>KveT^V9 zhJDYafpDP-!>VM~Xxt+)i^)pl1YRM^F$6sa5;?}n3=$!MNccNPq8Wd-r?Cs*$_oZt z^Xu(hC}GKX>{mQ}{|N(k-1$VKvN-3n{`>ZVgRD^q=77wKSPCT=yl<*0r z3XCVj(MOoF*{3g*rF518SVgPs zgecG-&Mkj~GCRTB6~;-h|GQGL&E2mT!v@0zS?V*bIGex^0+m~0E7Ew|!S>LaI?=A- zoz;z;6>KyyyhTe=JGhp%RAr*xrkmNIc3d}%A0Mm3nC{|^py;#tsy#0teexn$TqDiR z{C?koP3BbldrtH$O-+V^-quKP$W>L8lJcpPSiyhAq^oDJTv28W7Gi@&nkx2+0k<`1 z5>^ylVbQgOHj_u@tHqdpht(8bnZhu<*5>2&cDBg7ciV?=2UMP_E#LtE+bmrKASnDaqu=wv)$QIm=gVaFuSv( zFr_-G!Z5Wv+C!0$q;{jPFcm(y*GTqg_7~n%G3y|I*iCVI-%DgvYSRwtur`*>x$n8} z+dB+ngSxhjl;N`Dhg#-vg32RJbEx{;tFYBd!9A7OI$GVgr#C6i0l&cwtgJ=?oZ21% zM!t!F63nRt)KwY@IIrB-4U@x6yYj?}(6BCA?~$S9oV2TRvlsH`of|tZ%%VV}Ic|K# zyrZgrFj4PoVX~A=W6;i0uMi_2$Y%9ByyH5xV1JCdgUujacm4yVlFx3!Fbu};d5Rp6 zS|qv$fWZ#ih0AsT?LraCf=i>dPLw}vXyVYft?L|> zv;RXZ+-~Pv@HaCdykN%A#@Z$4)Fb&K)E}kAo>`y+9y+L_?yV4yq zeY`2aPwpUQTC)Qki0UbZbWH2-z5;hz`=^-W7Cyay zK+2^<8#hIhbDqd(97S|G^(lbckT7has*~0l8zt2>;z)W~4oxnyAU5iCo6L4#pZ&tc z!xm(~yCe3NdqW?OGcC)ddx;Ic)m21l9%F1HCjoK=ugtB8j2UN2xgNc&VCE;D-Dxn|K5C;x0Bjou{DNW z!f})EnpsPtPS-~AFCqSECA-3lVZuY(qBi1>{okP{V)`FADNsdBEay)Wxh{kcG zQToP61EaHOp9P+UUFY#D$tKc&*I5Jj3)~@4)=okeqMc<$;xDCJjP+9c!_-Ywgx(2j z0cpqDFQeV^kdpg*B3)gRyDTGg!L;_KEEr~!KG*pPa615K8rWk_fc%6?NYNqI`I*_R zIl0EKgx-8TbLcN55*qWCMHuMVfH?sj8YNji*Uc|~;b?kTMSi-uiC(9FVfO|S{*d7^ z&=15he4YEyp3wknsD-+Ec;Nnw5KCjIO-CAS;xX{JnEg^nf2){ZUM-3{sb4 zu6l$D>S_O{xF-Jrl~BunPQx$|yyq+SP?RD8AJ7si)KU=%Aw(#bhJ$f7)WWeX?}mpE z|BjRNiGT`UydKZa?Cj~tEnE~CQ6V`Y5mHm4WGwRRy(+DF&&RfM)}Y}~8L}GlJ0wQm zKG_Rpco;=BZMEK9-pq{ReCmVjtM$)Y+?}?auHOvYq(gt9i0l{LUNW3S9p{}2X1ZQ1fVc5^&(?G40=fYt`_ zu>Jq+G`;0E!A76N{=p!djmH;R21$}Y*SN=}6iU|~CrHvO z*Fal_e})Mpkex4o#Bc&VUMM;~+iYWxvnUGH6hcq`JwvY&&IK3}n6I->&(o{f?FA%f z5HGVb*0klE_U+4-X!M z0}{A`F^@0lXV%tA3Q|TFul6jo^;FKB6Q?#Tn5+dNPrSM@*f-_7Pfc#X^Bp=h!+%LDav+)vp{^zq8DfHeK6UT&5O6g|4AOiRcw+dZ=%wO4IV>oyer-d}MpD@!uLUCTQZxZ)zT zi^RqRXkVHNIf;`-q)C*drPaoN-?8Jo*h#tqT2z$S=lD6#d9jZ<{}xZ$UT>amqG;YMZ5n5O6q>i;f&{*Em3Z?R zYLtRe&-FtRfE|!Dh3hDYlHT7vz$Z@p1<5Gbh?T+h{oVb;BV52(vjSqjXmD`}gB{Qk z>hSL&etQX}As_AGXhnk{dga4?99qGg^1%Ur4ratd7KD!vacr+hzVHYKuTwwERgZDx zP?yYujE_$zqZV%8A8vp9@$_7y=p|%TGPDxkn`XOpqGC$sD<)Oi`K-iV%&AU@X~`Ec zS97V=g{!}=Ztt$X-`zal|M_%#|A&Zl6y%)8T1vB${X8s2dwUZ;c4(5?Q9>2v16lch zV8YYjhcAlWp{#c};dxyksET+jYaTOA^uVShP&7|u&6A1Zci%<6UJ@aBMPOlhwF)9$ zu_+5p0X9c>Y;fnH%|!70%)^d-~bb zPj@dROfeS|3>V)(dd;RWQrAjmTh>;8@*ObI64u* z1A0mW_w1}uV`H&$VyJ7`lTDN86+-$7U1x`GrkI;Bb9GIqKn@6LOl{xwsdENf+v(O< zxLL;htO7irAVOx_sqqy8!G!mhI6@e;! z#i0LJI4aZ}@vXJiNE`>NvdS{L%nUda#idkDLbG`i7K~U%4EUO_;cq_8H;XP@YB!m$ z>@d_VkB3} z&!t9Y3kL>pf&5JyS!8+%(AXA#B9EHZh9iXKp4c*%LuTkFRlzMYn|GNFb6bJ@Z`rMzkn$_~SefA3sb3r;HXH4L z6-9y4piGwpDebHVks;=44)QMV`0AhP%g2LBBLQ5%sHAfXN$3UJp>hs?Gqn9Y>Zlgb zP-wQcnG&_M%Z|k6_psx8Px@Zh5E!i^Uc(#E`cU^{y$(1g?`;Bo9Vd~Mde?_C&OL-n1nrw|)=E@5$qQNwfEFK&cI96U)*? z{k;T+1J2;I#-J6ijmRf|vpE9Vb$=0Cp#AzXqlriL=3OQyY_wf-B$tfpj?;03v{wcC zQJw-_-J{xaaEbp`9Kp|c@jtBHRy|4Kq#?H|xgz4H3A?#+Y2G$Wk7hJn=oE_{g ztuygCVWX&g2U{0$sN!QEqu%%M&uT4t^}NW03VaI@Fd+tV4tj6?$HJ%w+wkN&{vfg)m*JT(B*u@pT6x)x4l{t&W z$YFnh;b4H=`imleSZ}?50dWL*tyHa*MCsa2S|u<` zoilvz9VUd_Z7*YZY3K7z`u_5~$yaabdL8Wu6VzHrZJSnqQ(2Ht0&Qu(7o0)qC39R! z$aB`-aK>v%AD{ zUH7+lcEJYxBDGf%$*{BY_B=DQ+k;q#JHaeK8 zBDbtaX3=T|SNFHKpYOgzcb{*sVZHWzSXqEi4}L{oj4X1>2*eUN%a-|ip3jGsO{ zuWhS#jY%U6=zzxZJxqx*I0>S46G7(+CS*ntC#sAF&EV+?98WFh1o{e`%7YK15!l9x z4bRUP+6prY`l+LEJhNe=1Ak$XDfF+wmO)1{e{#LSnePVSd0_j-N=B`%dw2)^4D5O@ zy4&$4h$;nx^NwN#2Qmh8LEd1&MJgfDKSms3=8=XqGLmSrj0DM`&tNz@tkt$!(Lr?N zUK5ucZ3OtioFVpWiWzY%yXu{E%%1TR1nmbS}9KW2ljj>Yi9@EogRBQQAI zj*;7%+tA&^xr#+ApM-8pw^{FX)=9o^b)UVqRr`q5_Bs;W z^TPW1(?E96d5g^-kj_5sU|YLOIU`o5f6q2*n=)-ykS=G{vZ@J%kC&(C{*L#`ltBk% zIh_)zTYQ%-v{ed2m(|K#j}VFTC*QI@Swc#(xXhIar?CfmUd0q&COA=Lj&%v%&CgeN z{&D(Uq#sUCb?@r}Zwh<#H-zDS$2Z}IZX3J*TwuL!RzqMo4bznBOb{$0lX#9Bf51yQ z=FB(_^&}xUwp`spRVDZxi!CIO8QpEtbX${^?l!19%yCa%^&gJaCR~KT-hN$}V(`DC z%zLO=PFlIupWCE#a3MJG0`_;L&`~jJSUWNZ!Vv$%aD7znXt{G-{-WhC$K|$`+xzl= z4{1kr7hXwrP{firg5duoUPri{f3^7SnlSxc##Q z_nxphp0GS=)jK#a@D7p=?6boyeR=v3NHXIG7u!f4?D^e;83)*AKlE?lfABR4_Lr=G z0F_Z&Yr-%Te($e1gW_7Ku)XS3hWOyq#y0j;keHs%Kuk(5UKsxOrLlFj&dd7HBq!&) zrjwOB^@G6bo7!DvNkmq! z{abNUQCPG8PlZ91J9{^Se_yOA(fg$V&zT?`C?{G&)jLs2DxV2(V^CzjH)S#S4#0hd zVk=@0LM+?+t=fi}vCOb4+c{?xax17|Qow9R9iLH_n&>zVgseO#(DDhP^o0b{kA;$J zGUv5&0@XW0NUO~NgylT;+IzF!1yRm@uh&$sA<03w5{ml=Txb?de|Lvo%uzSB$3>Dl5sd7XQC(mkQO`BIag)7~&Oys>^E}jomku z+5*>*zG2SME4?aNpfcO2X)%F&>X7%%hgaR9GxbauUHZ$zsw0m^e-Nci&m7=LzB_c3d%74p9e9Ad!2r&5!07Bv4;~N3cfk*hkiSmE zFbu}`JcWl2hjcu_0XG0cCkEC^r;xg|QtL#CbK->V-AR)a^(s)CA%7hE=kN2w(X>Vg zk^?#;6+%f3D#p5czYLA0OZBV=qX~(_jhybWdOe5NK?v_Qf2D`WAgP|t!`8ZzjOVnM z#Aur!Z*%dR=dn5+>*d5&Dt5`*OBY7Lszp0lRlRf$GCRfqzZj`@5MSb3Kv>3Vj$|m* zWH!aSvt)##8_g_YI!eqz|Iv2}8c&KlO>jD?(37oKygTOk{j&FPJDZH(!@~5`;x4vG zO2<`j+qwIve@uhNh3HvIOnHrX$O z@QhU#P7;!HFwrS&Bd&-;|J^A8jYmOBBb-hmTp;TO9VXw9h?r!s^aP0->I_z5A<<(a z0~M65e`wQ@HYytha2urN`6KZ^;mj~yOp7}%p|f&Nugl}`vhDJ=>9v*gxmZ4=kPn(y z60Ia}ahq|ze`9~GkFid}Fc5}!KgA6l+JT{BBLxIahDsGk32c!n$382ToU;|%RjaD+ zj!8;EC==o)`~J`OomY>pb&L`3&^s;=Hl$Dlf0T7~?3ATry|$gRgcQ<=yfs)C`*h0~ zQ<}H|VYv5j$r9Xgk^=#F2uSjp4NZ1pkjllzXaOQ60m|a-y?oiNU-M#@Z>p!~ZIQ!0 zESI<02={u=1Hw#<)2r4-Bg;q$szVh2aUC6V8<;!tH%&~ ze}Y+PwZD0oGAM5RMRA@KA^nTz`zOZi6rs-Tf7JU`QnT1JK71 zWnk*ijz|$Tm&OuYTlO_dROR1mo*^g$9tX*>D+!e+$x0KPPBLiO zdda(SXo%!+h~aS{h*WovVbv5O-2*YG&}k0b_kly3KDw}>UXm;$?@^QY=(SI0e_h09 zMNH=$qE^Nh*;`usPo6jN z52aOIZ<{a_edkx)qD+P+rRnw-nxSfos=l;ZKcq>RMU*-LjTmH`er(Nu-!%b3LfTa= zQiOed&hfd|_~zKlTKxL`65esgng!R0(ojmcQX=8=QXbf&V*E}5g73?S@z(%+ z^5Qm4B8X62!R6$1@^gB1eR+0%3S%Ia|BF%?^gY}1MmT!5yyHEJk8Q4pf0R7;Y~sG8 zc60vY=Hhzty(6p1TKZ*bm*|a5B%395zw|_@(**2&^hHZ~vJI&th@oU_&so7ET{oCc z%k4!GDNB~cgI=PGWtZ?(#Y$o*u;8Ono8Skw+W>khPUOK?$<%fBn{jDX0i|cjA7)j$ z6=k`yBa-`2v9+HDy=ewUe-H{0K5d!mm2ioL@`IL>l<~QWF-lPdC-v4XNK^=!34{#Y zEj5hsB2Cxfhd41zM+9 z68xh8#j5dVJ=Pyzl1CC7Q_Q5Lr&Gc56nmuQ6Lg825=zX~Yc(}Z65tDmR2H|$uqwqb z7(N=n!I5p-?wd;Sf8V@6mUSn)4ZFj&-S@fO?x7#{+nmxaKRcKz7TE)E_5d&a=U~p* zzPOrM(at*sjyD_U9P`}mJ>T0)$ zYtkDo>1&sBEhLyK1h2t$|6UsZ0Bwvh3IZ_@Mf*8L3JX2Me+m{>skcp&&HiX0nS{** zT*SK@S6C4I%?$J2{PU&U3lZCdTC9W8?BN8n8Q0`9ujZOl$qbgham^7%b7?XV1r{v+ z^EAo=o9q;^F}(vk1Uy&ud!{F;p3)m#*3om*Dl~PSl z!!QiJ=U3#=e?yV@2LmC*hqTiWTzg4dr?ZqgQIkwei2sh8u49J<+ME*S`T64^DX(lQ_&H+|Eo8%W@N*@NKP8Q=%W#B#oXB(0q`B|)~!lEXZ|B`*0!!prV z!2e5Le_jE7+FHf0Lv|c2;}SJ33I0Yn?OXf+%~)M;+cp$^_pe|Jt&#vcSo;{)DT;Ol z3Jl%Q1beXzgOO;PuuN(smDE`C-*-vsTa>H@9WXt`()~F1oXe}LynUA)ve}H`6OkE? z3Hr=-BvvGeZqKJ3XXoexKV_T|rr<9oc}k+se=N(@ayHB5B=g_C#UxXdGjM#sDNWvs zeSQLq>cR5aq5d$x8iC>zONrhKfzJjeBHt8YiA3ht?4KEeD--kqD}uZX3n*L>A(!?+ zexK49?Q$0DaA>=YA+r*BtOBfKHeKd6=0~!lj2d$ASxHEKvEml zMumYUdyJCZwI6H zBy55hZ0tc1_2IyIsw(mRFS>RF32H@-==*onP`#_y2(x9imn9qRsUK5yT-iQ=e`sp3q|WgkCbvzS3U}jdw~F7|A=T49D|=(TnJjl}LjGs-(c1$}0IXyDdV}5}f90cV ze`^ z8#r_1H}LoydnQ7SX$bE0G1^L9z-;def-AFW)dvKuB>B-j;YiNoTS8;hk@N6rzc|F; z{JFt3F z<;?neJnLxGYMHa4++jLhQOa$%-Ae2ZMmwF5C-uEJS5@#%H<%@*090?HiyK(2ZVT6- z3ew|99RzJC%^P^NZ)e|I40<>D9l5^?hgB^+MPjO%5dN_C~%y3XTT3Y{I` zfDQ|VefWtBM-9f{3$%RDe-&<+*+#;WSp9MJ3{p$j+Pz2Th~F2o$0MH4661mzHqqEQ z_N=oeQ;+OEa8{tcvK_JTvXSJv5pq+lc5RRDd}(w@QH&Ey)fPP%ns)uEQLJc# zpGP+O;)cqKz7*7~kotPraC zEjN6Q>UsCfutE77H0la33cDTkmxjtb{NE}mxp0@YXFPunmMe(zslPzR3I zPJ5%m+FB%b7CS%%{oiLNCUKk}1h$7QLMV;T-+g!Y-8uO(k9YB4 zz(@olCIO%Y+dv>;f9+rGqYY_-U$8iplffVrz0AKq1`tcinc8?y zLb^UrwrQj+iiOGBp)OMVQCn88AcwPzUT&F{TxlFQ`_9LZ1gph zJ1WMrDjqx_$0PP8#kK>2P}Q>$XGPf>+wLY!R+&tQgbM1Q{O_hBL+!|c8@zGTR0(Uj zUd6VA)FfgVf5s#cMIZ;K+J$I~P%7qboMm3zfU0y{iWaG@(iJ>Ug#VS?t1|H!4HGh5kLHy58P4a@o0BOhBdyLr10CB|K80 zToj-U7$+yFjhjVgJfq?gex)>lbwzg8z3v%5;Hlo|f9A$RGxXUdEO!Zi$lAr7SJ)SE zqFQ}}PcOJcz<9ddp;D08;Gd!658~MSbUH%59%g(ESlz3?Is>m~w@|StXG~2>Cdoz? z)-;-QKnjpH=8E&MGRayRusZ98J)Kp>)lA<=B!XnCH0Jh0MFeVOkTld#qgpd~J$Yc& z#;O)*f2a@Arcry;z+p#3Da34JyMEe^PD@AEJIKURs0gh&2*k9!beCui88@y@>UA$N zaFcDhMe})mtT?@tVQ0@wN^xiUL`(V%ByW1`^*HoaRdKAJmEUi#szWt>#PeA6K`ADd6%Hv zSbFM4w|}kbWj5Vc=YL-X%{A>g<9c*_=Y*@|06jmqm^r2FA_XEKF0aL{q5CG?gi)SH&uADiR%nJunWkgG!4lTy%c(xF;Cxqfmu!lVpBhy^4vSqvCpSVbMYZ~wsm5@;zBbh`M3jVHBpzA8 z4N+-Naq)puR=Nv5j!JbQOqd&qe{>lc-h!#V zqD7P!boG=JI}Iu@a&2OW|7S+Fg3yr)fc#2<`PEV_p7jFlVVC7aJKNJB*WV!|0iT#-Q74+RMu)9I9%XJWR^8gE$a|JCD_)x{XEsdOj;kqR|Gwb|rs(2g znE1?n*2~_*Wa&2+CULd~S(!xp$JH7`N?fT`6OXY-{WX-~Q|$8We+d!%gzDV@b6JKL zDK=s7VTaNA1a&cA?4MHRj z(weRO3972QvD;HHQjHO69-Mk%P;$(Hj^V{i*^f_d62_jHe=!`f@R|z+A-jlg-kzmP z2Z+^{l3L$sh;XX%oX(K|%$Ir^=yIuuC7muQXjm73ijk2`jQfbJ??fG@TDW7`L+4O_rAlK~ zcMoFxsrJAIsKg%jtUM?CLz=B|7@VVXDA(FVR4U^sMvax{2+>_(S@)YU^|V#8i8&TdvuW_QO%3Pn@HT2Wd88RS>>yb5 zXz%TOZi-B#ydN?SwQq0)ZS_T!1o9`SB}N7mAAx}(H1Y&NXz>+ulW2v%<6L0-DJIA zBKT|3ex2!hSwea107uo0jK21gi|G4zWT*hEJ`KE8N)0&tRL^qKG2Z~Cjxo^kky&F1 zS>tm;Io|j9pEAku+TXLC=sLL|N{@^*k@G46f7QTXxw@2FL`sx4^qP9mw6D(Fz;hWl z+<@v#02;~up7ZNBGTO&|hs_F9dy8Q?;vMwndMI&*#6;E=Pi|t35su=W9S}(rtsqhJ@^2U|OxJVc&Tl8bP2IepiF?Dn#p#b>q#hmW)B2#cp_)qlQOd0Sca$@% z3Y@Ew&V!t)SFMbs&wLBjB0G^RPO#s_f6L=Yt@H}$=1?|RS1(yjk3NRdK45M5DsiOf zAV^~~H%xJ)uECw>FWEQn=P$UA7Ww=V@&{ZR)iS1~pM2%4Iwck@fvoU~W!saR+SHha z6drwC+*fQocs3^cMmK^N?5Io&?LI%&vxde)cH8Un6?#)OYvN(tt(yL4XR+INf5CsE zgDOaVoQrz>smm?8wRFYeIQ^V>_$jVmtCjj;O`rb{abcU$9~rAvkv;I~+iiff=SoZL zqeG|5FBwljc1Lpg)K4MZ9xs;^OA2LA$;RNHFX zKoEV`S4=``$>0W33WYdI5s4oRA!(iVq144}q>;TKuhhLHF^&JdtJQ5KTdu1ITg(}q zIeTViuRdq1%yAf5qsT~zFl8}@5~KO!W*w(&GrvjKS;~+}{l-L^;C#w5e0+aaivhr$N=WGEJfjLi+c@3Z7?XCRm3NE^Qa>dgZVE6)k zgW=7Ec8ST@b1MuC+vz)T3RD0~C74go^8*^uLrYNob|zO;yuZ#(f71uBx@Cdo>8!q4 zVB5~!mRYF+RtQ7X=C>4#XTnBi|lIYxa%McbMU&~5fQ zv4QQyLD5tMoRiJrTFt&Rwd&aMwe}c#V1`Rp+YcBFOgfzSe!T|$)7ZXSJokpMe9p8C zAW#X=Q>PHnWI>$rfA-zetcRJ`b9e7lw&a$6c4f8KvPZj_zQ&rLzs4=NS+%U}q-8+cgq8LdELXkQ|K#gj z0b6J5`~}TfU2oeq6n*!vU<#*_(Ad~>-6ROI4GXYkMH37Ie{92GB-$n*i<&4k3D*4g zk(4O$Q{EizIR{hVXXq#lpm}aE}pZAS(T>FxmwNmCc-4?!O0DQ zw&rpae+Ha5+&?%uKUe0OeCf#8nd4lPznuKC66sb){d^frFBIHvG3@L*(!-QdBQ!UA z*{+#g>`-~jx_lU|BQue5Uyw(Tp+}&%)o?{wTgu997+kT)WFU{Y+yj1MfOn_#9e^o= zEqMm$3Dc6!gEQdr8YyVJcy`9ao>zo>4YQ1te;iBzfh2dAK^^zDLf()ip%YE1Ui$tTnlV%_g$`ODN3f+*l* ze-n*BP^Wg|1VZiDD@-@I4GUa?l_<##3!*~Q>S_`AXaU*zC{S4ynRECA1y(i&L6$56(5(cN^L3^SMd^QIgHizW%FP}FzxA&XhuKr~CuVP`)f@Wkm zU)1UxNYzovgZ7!kg;nZ6aa!-GSGAhrf2vO|G`5CIUOaC?3Z2y>Gp;QT2u3L|8>yrU zagSJZYjr7E<@)5)5aN70FawfnEWif>3zavJ*4L0i zlW!%>d`+EkHE_}nFgC-Kj#>9QRaYAlaxU@eIm?^>VAs-Y$?!i8Se<*bLeQDH-c$=;9)}sTi|FU#Z z550MVB+X^df4+S^pm%X9(GOo7XsKX&%L@lSq{yL)MNy%ITV~FJjLZTsA9U((L5e*$ zW8SbX6<;P%UR>Q-X&`Et10eVHf9;(-5JOtB;CEn*aF!>;8_DL`jKk9ac9d+^rB1hn zBLwb(Xne_Xe<;FR(|Rdiu0%*Z>mkYA+)%YYFuVAEENg(a!-92KEM z&kxyI*LDWgs0g=Jx91*7$Zs^-R2V<|#N$UNh%NtAADYn9e0-bDn6T6F>XP^Q&`8u7 zTb@#0RQzwPwilP}vu5+uLK`PvT&2ue>Edf1Da5b&y{1)PEt< zxaAf<2@UPInPHEE3j^!17ucGUIrB1~&jTNr((uX1t7|iZrO2Rp;NXV9jt8;s4ME=v z+n&{FSNPDpQ31}aZujT4PuXr?CjS6Ei?I&EFbqU@e}xAI_=X}?h$%B`mXJCkQe#_< zBUDxVJD~_}INd$nf1O8tC;*2-RYN3noG2Pi{Wy&T%Q<_L63!1$9c#y<{ee~y)MohJF=41}@ZP8f$JXUQYx z+pty~OeVp2Y=PycXhK-bs+Hhtuhpaj1S4k>P;x9^D;*FHIt22QT741+I81rf6ZmL(k0=xdGft{@_fVelMS zdA2-qf30!&nG{fzXp^vlU>*iP!Z>~&Pi|g>teA*`*cnq~GDR+@1EQ4iNrnkWrD0@! zJpJbTEXU;H^~KQIF;oz4_&U;hCDST9X|Fiy_F{-+7Z+xRSw#X6VMt2S*lQ2m4w%bJ z@D4mb*CNFqKhXs-6O)AUEDZFZ1xnVNU)URfe;JVeE5^4n56Yd+t~lje%qG`$2U}y# zB3k+NhMonggJX?9i|Edv{mIuPpoU`>skRw|voml~Kf+Xc{vDDEg+Dbl=3&*gfAcsP+_UQk1$kd(d5hN_H zf4yM`+-WKS@y~j9gv}DE;V+#VY=iiHT`-})W=h1XlKM4vK-xmo=K)EkguA6UVIP!) znL;hmKNT*~S|m@ltV7eS2T3>>hCM;uYVBEJZXYp@!cNMW-FnC9H^v^;Z_X-Te@l6d z{<@zJ>ou2}ueYVVMz7s@7hsj=l?um7CHL`{e3qKf<7lAA<;uAYq&9Lua-CtWTqyGS{ zmTPa?FcgNr=T~?YZGsA-pxsnPe`!ahu3L9e={}JPrOt6<$=H$YK+)9yzLN_O80*&1 zd?B%Y-se4z$vGeTaTGg_3?^XWfC9Oc45)?BAI>LC$+TF?v;U59ZQ2tq|*3>hTaDDC`m2wt(~)4&3$sVtlV~g%`JGof5DBDmU(;rL;G&DAD-;glQ{fV~a2vxr_@)n}uxH z+IiMExVJdBb!ztH6wcLR9%HRtrTVy*^s5cCo4FM4VuT;k_U$&2nnB!G6#}N|n+Sx! zEduJf={RZLyCw9Nc}@d~udm)3eI&=aPngF=Sd!=!}*fi#esfwego}T z-EZ4A5P$by!4@7w1CCc<*o)I7NYeoekaocm?8OcQMxtyEG9{2wT5s|HzT<~%lA@i| z-NPQHhgi{lf8YJ?j?(*!dRY$!oUEvjzGk%6ch~%hlq~NOwGl-xOZPsA;VSKUAA#{e4%tSk}9BHXpk}(QI!DNKp;d8yVoCY}) zUZu4mIqZHUzJR4ABORZ~t-4r&@H_M#%~h-^Z&xW13yDV~heustVzHni%_-Q07J#ha zL>g55Xd?hulAk9(sUIkK!4|Fd>+KKnEn*UNf1)&>085%)-CW+?rRnH&YiP5*8#YTO zPtNz|_#hnm3QYcb@NgJ|R7U{gF!vH21<@u7rCL-Ol@fBZ5Q3!j0#$`3>8Myf?vI`^3f+87z%C-R*mP?8fV5?!rVI|$_$nOne>weH zmsL)2@~nL92p1P9NqRkDJhgLePJ|%qR4!QoP_}{*tPh#|mE~|6P)jF4XJ==grYPXQ z-O{FKm$zm4Dr*KkZtpUAt0yxaq3355lFT@d(3V6x)5RbAd*Ro47d;+K%!WvH%##F7 z6pH!)v3j;m4a4P9N#LDw(@mOWe`5l>6oX=Nyb}x5S=-d znP7D{?6)J&d7eBdh^i9%np*A-))2t{Qk%KJh!z^2&>jSZVfaFOe*qG~RR9O~ zU%-G-{0eCFK;|6_KG9}b(R{(q+U&T#%KXNsWDdgqFdJNn_{g$C*i(0Ku?jxF6Fjat_@r7e)eUeE82L}M>OD)qJ#$GXn(FI70Y zK}OLK4(yuUnu)};w-nABb1XdvU!xU-VxRN8oOuU{gD+QMilZOQrqi8oTZhNJ#Eiz_ za&glc9D}y+Yq)o`V!4{V&Z%Y8y_#5&Y5WV|X=wOHge<|!{@YXo9f^X&5Yg~y*EFm`dMcg@(ozcOJH*pP!d$D zJ4d_vGBVIci7oIY$B`Jh8KjZybf{_)5x4 z#jRyaiE9N7p)0MGe=Ir48X3G2U}aeo%Kl}{FKy(O6R>;>#)h!! zVm(D!ULQ7^c;G0JdK9H#MMJX@V33;xbjFciCx8jMxS%e_YOP?`F6618&ya!NdKx zhj#&ZRmFopqVO98cYlhS!8mDc6zzUWC~jBQUNSyD=uhd|qnG;eVUj;zE2 zTQZW|b`<~jNtW!$ZbCoU48iiXj_$el9Lf1{QKZFSK;s;XB7a60mUNGi#7T7bC*L#r zC%R&J!6>Fuji_K5jusE<>6!?MDdy9`pcD@5x8Tt=JLum5^Cgv-@721~?yU2R+k2fu zyctzbZ?T*+UPnHN|2TAwc}{37-?&CS>nBm2mng;DJl~Z&fjHT9x`(n@2&gM-Iu9f- zGMwu^!8zdId4B-vDmZyiq+poUs!UC=++_s8zNAPJMq#@}stCzTB!ixM4G?BP%Pb3P zyjfabLzxmWp0{&4X{wXS7KW2>5F4oayo5e)sZdM zSv1W$#dvqpAar|Cw-XBZAl~W?Ib5y z)3wV4;k{GhD_uIrzHXIb$=M^d#Cb`nrjA&i$23u7?WpqJUxJ02pws_%RQHPI+D#gq z+_nGJdVfzuUa^+ZQA0<#5(Y=jnCKBV;UH52@z0r)*-rQ9<7*rY_iYzGRchVzv6U(it~? z_wZ9x$6vpFzaK#lv0kihmbaJdMGw{5wL#u@n19=|{seVbcQ;q#c$mP2s9*OFc-zTa z^*5nwT`Niu!o|sO{iI=mUS02>qu@r8?s_%F{yvCuEK``3L z?0-x*Rikz__zPW8OOM(x5WeSEj5uwjbh|=5^=1;YSPgOHn57&;Vu-5{Bij`1zwb<7 zce|A$MV`m^n#`SFy*>639@;N3e|q}Cwph0^(lssAm}Ba1Jaqo$#wQXe>2%SU-p9yeF~i===~*PF@I38 zH6v}Ik8jRHFvE}_f<@hv*AI2m3`svzpz$72pvtv(r_W9>qQk|HNNGA!);Wx9V*G8| zNINusaKqrPWUX~$XUS3ckt7;h&EEYnlb73-M;B4*O#&U@{BhNLEA-7Uz~0;g!((8) z%|CWL(T9kKx6J@&=O@1ZZ9XFZihn~bmwL+-2T?<%>tw8%CEQ&4SGC2NlKIl`8rTqGF@O0BjaA=^+b|4%&tIV(DRF4l9mk%U-W_Z(HW=H(JvQZV zQ>8U?oDe&#W%Pfa;{3ccEf*-273q^c$x>F&`5|{4#Z%Ndj}d;T1Y?7{=;f3qnL0&Z zvoy~XDnpXenZ)Qj${dT(aZ3Fz`)^s1a$~TFewQXMO?M8RSaPkQQ-9RRfJcLBr=dPN z{~VwvFSk;}kd!JmB2$o9rx&y*rcEK#9$a0Y185b*OY=SU3T;jj;SoHclFaihb{(O5 z3N&S*AksNmn}m6*nSNT&^k(&m1ej?rXdr??uujuN^Fs@GzLEGBnZ!3&Z1+dLB z39jVDzDhPqxYRgKR(~YGK14#l@}l6U9?bU#M%B~3A*gkC*o2+YxszOpimdL#fkvp> zbKpQEkjDq;g96t*|10qcWON0VOVlyXk&R0CHu!o|)u8Q3Q^z($p$ zP{P(2gw|+SL(&x+KZN<*6<7Ob3(=dy0w#mPM|i`s$DgEA-J_bu?ZDFnrx2$|c2Mu( zS&@4jjp0n&A@nZi&Dyu>$%NXu7HKZC9kRPf-3|P24y-9i;s)8pPNCdv4ff_R7~nEV z#If2ii^bw%!GE|h?Z=W9=1=Qp+gVGap4Bh|GPX-2?m(PXXiiFS8Z?OHVporU%>|H#Q}W1eNLJd zTQ9>ExUEB5959>@hGNr+Hb?d(xot4?zmIyc9j9^DZh!l-e5mz8isbWse zBZIRyp7K`pUu2P>Qqkfm!BX==)GI$So@3f9zxp8moeh$lDFuG&FadnhC{hJ(I{0${ z^ed}n&VLigDv=mVAS)5jflE(^u=mX`MnOs8KKw?Ew6UElv2)5!jz>0$!YIt1&h^Vs}&2Ej>WgN*Ts zE3E=AIGx(n$z(^`{PbEDk09_7qR+*OCk?j2_4IID}X*VK})Tjej zV8pmknnK3(4DRLDs0f44&zyz$;`~=u!$ga(-nU}aW)Pij(=L*f4cb?$02m{Erf{hi5)ze z-0?p5GHil#g=8hae)kP7FKxZN2z2BV#(&fkMe7Ya1G9s;?qUjN2g*m(_6t!b~_ zHCPEnS>d-P`&Ro2yA}^D=jr#Jm9`jL{=5iy3V}R%;3=k{3(w%xmSw%K)kgM&(O0Q}iwwJS;;fmcLQ&?dCu~xjW77ynkDX zU+RtECC#K!G)G&N(`PP_ zOdoN*_JXUACYcZTxzpdh*+UDI-Rw7*e}pW(9~4`m zak#O?)m@dCVOHhA1TJChAp%JWq<@;dd^@IVsLjh$1+`#G>1i$ilP?{G5IWc|o;cy3 z8sKNIr@SbSGwy>5dt>S+`J@+yckgU(fRp*$U+~A%G~X@RbE|N1s?I;S(YPlu-NA?b zlc*~xnxLG>^TD@@>EO5j`Fo}R)v?Ql+m+ zA~)SjAx}j_o3X?)r$nLFLw~Ji97X11m5=2+as4y>OBwu~g|SC?Tp#v4_zTS&ZByGg z`a8cu?U0SLBn5ico0~vjmX_Xjh8`THyVoJTjB%9US;x-GHZQx&f4@hsmSkBraI@EZ z39Uo#gKU`n0H#X96h4Xb7Ve}zg;Hbd!>8JbEB1`Y5Z?n}pOMh`%z+Y*eC3yNH zPS%+0ZETczr}b}3l4r2KG&InG+VqL1A{MU!Hz+?deJ`u|& zSPne!hmXbaR2D8US;c9X>}@RQ7mAWF&(RslmIUYdQAjp^-$3wLmsd#~p$NbV^#1hx z;|~|JpN`LuPCp%^34iL}U~-k^xWDIH{KbN}pS6R%J3jjS54wJiugaxo;Xmou30;|H zWECcIDT!~w0y`N8n2$gFTVsEgW~=b71@em%T*=aogNalDGr`)SxO^2Dm(mHhc@>hM z<9Trn>t6r8{_gE{NUW!LwJz?D_$!KiWTRXQi;xQr>;((TPk&)OjHx|>S-p3?BzR83 z+xGM#lQ?y12EuC|E-@A1@~bTa zSSM2{yqp%Kw12rNR2Z;!9pA)RnSbP{t+$Z~y81&9w*c1^tODBH_dYlLc~Pb;0;1WB znLa6_BCrCR+>Rjv+hY2rni`@-l7)rW0(Q|hdi`d=2=bUlKk)(u0wrJs?$QWn3*-*$ z0}YUVB;Z8;H9A=0;+T+(aL8b=rvM(*Uz^2soR1D{;D0@PL5_=$V5VRwc|au{)KGE3>K{t|-u}@x+a%PR(eOS?+$G_o*7=NgBu2Ucr><-29c60CUpOQ#B)3n3$30io(-p-;SY^TJ{~8zrz1GL7?DMAI&X2o&N0-7ofBe>O%t z1FeDvBf?>HjjAOm%+cmA_#RCT(0}Gln3T0~NpK(IWNt5VHtga2hE8I<^Ap-cpG~SZEL8Yz{a?RjqH)woXPj z*Qg|<2IvPdEZkB?&B~4Pg9pR`vN)@jj7^UL-(oY>8#cLwE+HZrb2v7d){0Tbu-~!O ztP63~!ET3rjaI>`+sdI#a)6oBG>knMFwrV{`kYv(7~iFmvoo=!S7`%2FZx| zq>_WX6HEC!L*$-Zs~*|5C4TZd9^bNZUQ|E>!-vP{ufAv@^MYqY)-@v769Si+XkIfh zWPu3u!VY=e&7?Utx3!RJ+nW_&ookH8{7Cb|#h2N?K7TmA&_&TsHh+F%N1SnSU!TYO z?cE+0$RTD^EZ%d&akJ$6y)N_JIcoXqK=THx*qfOs>-A+r^m3ZM^lx%y4%U#{;|;3mHI9(Efh*|F z(J&+Nk{)d;G}dt3qT1+!W_~{x;eB5oZ`E6lFe~IH(^+5 zX3q_s=zA(QQ^(i9t;J+!s88GH9$KlFyk4ZC}hz0Hzxl?J;W;Vn2O5YMxnVJNN7>m~rN z9v{W-V^}q*R)4>=hEXdD4;*ddN(Hg$*^g(l2;HhcFg}rfYD{BXf(17rKZC!C3 zJ0J7gczciK<(Uk9@o@F!IjpgZiBI5(-_aa99=f=g<9|h1CdCuzh%26B*HfF)gTo^2 z3kjav9zY}{B)qq*Q?Pj1zY0m8&M+0Ofbzs{R}O6z%llomFc4*{bppHNh>4W=fETELhNta)|mk3D-HEH(5`^ zQLa=N&)pY1e5;dN$+UJkV_;Gx@=UoaUEZom#MS)_mpv5MB)er-;mq6gLH~2gP6moh z4kchMMv56kMWCeUQ*iirRj!~TTwP&;psuF*Gk-^Mj?%24edeov7qvT7rR30#)-45L zd2irbP>^EgPgjt3eZo@E3S5=kKg(=CG8Dm-qnZJ^S;;Og@k+!ZNJpYLEgngcIrBQ4 z2Q{ZRqKI~(2S}m*5KTW_oX^gFcsDyaet%)h9^O5Y4ToWvD8(fGvC@eK#g?tQLG(S^ z-GAB9X_*(gJeg2klWWO+u9OTy^ifajSZ})r3hSZE9aAFy)85n zfeoVmiKH`8)Duy6{I|Tuf%V@UP8Qk_e0eTY+dbv;<53HZr*kU)ndX1PBIhsAKL&@rhxny_ zU=}9HRT%vOaFsu9g1FH&t{t$j;z`~2l_v#k%GYv}Sz^50{pzl*3Vf#x`Do$6a1YJ) z%#QP_^^9{`H_94XUG1!>wvhDzx=1=u$p|PRcWl6#CI!zD;I*H{X%0q_7K@wmMh zS%D4tnohMDU8qG#%@F9og_410dr8Yex}P&?9RWA4VP!8vbF4D3Fy(4kHf2F|whGsQ zW?*@FScsZ3ErszPpsCKpzGOHuq<`A73GUgs?|C-yu<@|*U!_!CZ<{a_edkx)Rhcjn z+YeaQR@FYVOxpIaO_~%?3SslW59+pTPu3Lf0tu3)q}{e0=V~?{DQwdLE}G zYDp7>8P6~=m_|RhWhVGG`XEXvIC5j(xE2LQU$Bsnv+y5LCyGfU)PLZ0#to{> z?l`@G=BpOGpI*Z7jjD%KtY66T3peIv41dbv0)kJQ1f^kuM>GCN3zpugyeciC`*1eZ zkIKl(w2whkl7eaty*Af$um?t-YH(BUuLpKW#U80an4}$C!!^v~sry_l3zk4u@uZdp zi$!8LH>yeu2^xf>smH{un16nAqcCUMY9YSa7(WD#YdAEU1i=utDT@|%;OG(R-sYx- z9Y*K~SEeoMG$t15PPH37n{!}^-P&o>H|N`lEB(i) zfiFr_Ig+`r=z3wSF+N=1K$Ou!V+ejGR7u@hV_+P16s`Jtunfp6rhhdW(%L@g-+W&% z-?PPW5DYZc!>p88H%2{`LlSA54sJ5y|K_@qQYf28aFlTMT3@~OdvX0{W26e&`mrjDST%Fh|`y9Yn3IQ+FST0$~Q*<8r5r0|Q@3>%V;R(2uj*aaN zcB7csJHfg(%f0AF6Sw3&O}KITP68hAdP`UIp(`i9{E%h;EJ=1hl;-Q|3^Tj{om0zh z+b|Hk>npZ_8&ZH9px3xbP#6W8Yn_{E7&NuAiBcrNM_i%szn79FIdbcy?Or5u$(fzm z<);U`vstEjg@4|1f$*qH6afpisH@WGn$1mRjYb{PpY}!}`-;kPdhw$4TV7u+63^?TH`{(LMV+=X5V!?o$q6Pa zt4x8EuzzONnA6HiWu3$xMzW>%7zT06t|O})uCL+UZ`&YKBe)i5%2T0>Cifq@Te&T8 zrv<9$18fku9*7_@U5%-(Zpa_yj9YPPWrc2wXN{T;^U^^bE{t9GMD2}9z$4im+Q!U9@P7;1xQN}r$PUM`-0Yqc#z?x4tc#nqq^qY+ zat-qu*>O7Eg{uj$h{w?9G2tPWz$-#ipOsR_(e@-r{P5>8*q%Jah`}YPwnu&vkYsZT zj&gAP{#3_`2w^XU6~ZK0RlZ432uI}fC_jE*Jq)SbktZR4lp92N7GOLZ2+7h3U4J&H zcFPzVPX*pCgM4s(@7+Dze*E-scl~+gHDX1Opb$g_4okWqhwN75j#hEG7vVza3iL(nLKuw47J$Qi(dfdB%SW^DIvI$w5R&)w5& z7Nh*_shZ_vAA}Nxtf08osJ}iB5`W*S#C4W3jAD2UlTYTVOPnIF<=wQBIzKKz`vFxc zkdwhhlFY#e4v|EfD~%81DZGVVn$-rme^ju7uCys=JqJXwYJy64&papW(5Zr{j{S7 zSqWl|ivAf$55~q9ZL0T3$$y_<q$bev1w+)NyMs4c{`E>KbVj{~g}&Au%oz1zAKogvJ+ zzI3piTqFEYW#w5bwgBI|_VwoS%3EmNtn_~V)m#1G?~E33Zf-_Rw{x>;Gt#xDjN5+) zE`Md!eH|be0>Zsi-G9-%k)4@ayV#~dZ^<6oy;hTJ?OcTgBi2g4Z>j2J9gw5&y^9)K zd)zbBMtxMr`*;gHa4pm*9yEnl`qhvt?cSY|g?8W2KD38!u$*W=PyPXIkGpO{F${+H zJcS2_bB4Y^=|vSwSt(;A6jGlMDRCm(p+Xh!-q0heiq?}a|9|h#et52jY7DuIQk_Rw z(}Z5}-M*djROn<^CD%eo+B1@p(7s}-$V+2G$jo+quDbr|6u?3UdTSuRiAFc`Hl!EnnOhw(}{n=h?YVQ<v^lP~ck+vb^MQp1x}nPBmZiGQ3GQ4o~sP5H}G=cTTO3XI+^J- z@`LNT+zZlLju+ezxfU*3{t%p^w3JlyOrj*AV5=@ls&UKF1MwYlY7ew%fMh8|b>ew= zP*9(ThJVs_Q>~8PaMc@HSjh1@Ya}=luggY)WAQq!7Eck5jhPAi&6QTziSPGKJPaF( z8kn_B0P4)aogFDc)JI?tmp@x|ruHb+lRg?jajO~0o{vsq>)*jeCvp01ai4shf0;cd z~d~U6R!sDu1RYnaB!-J@0!%uBMD;1q{dvwt?P;a#^%@19!g;0)mncvbqV zcz?#i8vsrItB71B);DY8b;u80^wF@x1~2Ea*DNKa&{xsw>5Ji0CUs&b=fZ$gYkzTM zg5&S$2awBM_KUUVr1j;h1z~^ALR%Eo@cZB&b&oL)!Y~X)cb~!o1Dv5C1{S)obj?zN zLn9@Q)i^^{ad%QxR1Dzh`TM`Y$FmFqz<+(DEWIUc*^yOxm~M(L@nkM3N@8NI#5n~r zR|-O71K?R{bc?k{?PrG>u#S{_@D^QRt3EMi(zV!;?l;2Su;B^0gm7qn>d$Zef9Fmtm=SA%cZ?rWudsGO2PC(QQn~cr+s4cwS~`HxwC- z3CpNb(1?6O4ZB?k78)hhg`6je#D9cpB(DU_C?$V3Uoc2(n1U&+)|106=H%C_)t`(r zh%3RY5C}tNRBKr=?NKS|=8(#-LCslq#Dzr4Qt-Mcs$gI|nrS+7Sbq;;$3UlCUC$J1 zhr5D+tB?z`P=kHA1yN-gd}-PINyo4yyXUcdOW9;>Axw&5TM69}#ugVzCx5R#;MhBd%92VTw8=A%Cg zDOH{`YdhlDq(ogw0bD6Zw+ti~tLV z^T|KvocssLhxzZ3`(YLQs$-U$5`Xh7yGyA^aDB>)yZzpnX-~366@Sd_?bSsd{`{`I zx8xt4j=K)RFbqZae1!)F82Ezn5MrS-YnL>2MWn=uVpmY1{yTjM%7*X|+3~%{cXy1p zs1#|B9JN8nsYWAM71!3+f!g96d<=vneMcM|78i68Q>IkIKk0LdjZE+9t`p`YnCkvm z_6K2M)+I~QM!-1D41b>kQ$WCnAXN?J%vl%@TZ>W{rEN>6v4)$N*PIc-9gv6JJ= zj=f6k+$G`?pAvKTu)D0rv%R}RW@nc9NNl)Bh-V%^N(4e65Pu*cAs%_;*W53m`g6K_ zx_f7x1cBHGk7v5Ox~jUqy1HiW4OWZQjT>3C#N{fAF?yWMa9rW(==s%hmSysDBs8$HfmMrks=U(D+z^pFjQn}d2*XH~Mq zn}hf?FP2eN;eTRuR#&UKlK0nu@Kq75R?$U@4N#9W_%j1`c819Sh}-AT9cW??H-*72 z3JP%=l_feSc;5Kg4Ftbck-Us5jA9sBq2~|3xc}__`Ps9{`Pmof2<;uN>oj?Ac7A^L z1p4m#`#yPm3Z3rwJ4s-7ZT%jcJv)8)Y;yL)^XHFGAAh>g%D;u%@T58H+lu0z-ZUt$jL>$uIfcsuoGPbIck6yv7ppOJ*q8DsehH zIN-STmEq*4H3A5BpJYkJ_!aaUXs^wQOU!x&y(ykWbz1d!@7F4YoH>$EG3pf0$UN{U z0J4*84hs%8IAm%{yKs)0bblzav{6Hf&6u!N5q~N#Wph7e|0yyE+@u6H2R4HG<|GBK z>LRleZ-ZVfK=VDH`j@+C*I$C8A6-9K# zp_LsFQ9K6u&GG_A@d5=L0*Xp>D`aGW}vDM<>sv;IQLd)nfS=LL$NI(>C zVukW7y+UQZTIB@Mx7a@G2^zDlZ(k5%h;tQsKWttw$Rz z9c?O3is%ru+$O;pjr8s#(q~!29)IZqqX_7ve9sF|c%24G-EPmPl#(VxBXs_E=K5*{ zI|Rx2u9j6@3EQ68%_%c)pjb~f6qSp-PN$q(Vjct&QTs?OW*ns{v6;d;+E9=NaWZ-G z@chx)>11Lzc2v4VJi~TN4sjATG){3;h%tamT&J+v0w$j1KwaRqsdjbI=6?^av5w4? z1l&T0sL^hZMG*$Qtsxs*a=L#H24crikz0o}X$F*zqXA`;0N4+M1w$xx64(OK~Q{rgzmVoVzYEc zIjhsOD}VGS&7E~hA#&X3Epai&hM0vQE?msVp=#y45w0-aPxWk{7Jo+x1U@VBdQ~>N z--|j)rPZSC4ZC2#>&0?n^W6B9`IzKekyWcd&*BPLPgF8w(#PB8YXKunnR-o zQHf*N207^SfsU0>m1~W}d9=hXfKK)c+5P|Eu&%2<9(Py^2T#{UJ1)*rg%8_Q5|0bb zf;K7jbl;O?WY*~&y>gUKd4mOgF zZ#T{&9j7SPIOifSM4rlI=mXkxvWSY(+yiWhaU`aswqD%d;rA`UDe{u!F$uU}69i*E z(2Al8wS}Z*6@OTmKvv1bV5sb?K)6yos|Zl|cbQE2e?>B1(5Gzk(U2S}Xb!)p-|p$R zpN!S4p$yV(7WYHoYGAXk-x`$M?To9e5%};Zf8U_Mq-ej5^asP`yZY^S^xJQb?Fr&# zRJ?%5_NbX!3$c;N($HE8ams=2A*S_*XnUJKYsK3f@_#OvFt96=0ox&l|Cc67w?K|5%l_!G%7;x;%VUFkGp-q6;O$f~r{Y2@kWIGa}sU;?XWIyj)G z34ypP@G^giPjQ@s{UwRQTk>e6u@v@GjS$JcQyCSAG^1*XLxG^_=r~6lDDqZg42vcd z%8ubaO@EdNHRKw=9rTGc5nwQejc&VOPTlnx7`oWe>$O;+Ot_w%V(zlO00HHVA=(Q$ zEkL=B{0_R?N4u!EyQ^irb&+aV^Sp`*!p;%Q4LifYWUI=+5Z$q?5Q`3}e28`02p%b&60Q4rIK1cV#E?{Orsb#Ab$+5g(?l9hN?-qEA~?+1>6Ob#e93( z41}%by6sxWsjS`h;^hwZgd)Bwk_x9vR^JhIDB&?zrhbQjjMoWxVlus2jiy~`0A?mc zHxG+=3bs38HiQ}nN=&NoI{RZ{Q^nogaTivQsJ}__H*sbR0ld&g@oYvmQ75v&5q}am zCx75Yuo`*V`EKR45^*ypqHuRkk0;dRn%X^$N~+Y<-w5wm_KD}QC8q@RCY@||m((De zv_tU9g6vy~EB!2(uxoMwpopE8IA<~8ch=-h=ZT$Dv=XIGxW+1SzEfVvq<|r>2l``H z-|5S!nub6@sFQ{h+Euw$3PEf_?$&V9M}KC~#W~kt04`3EJXV)+)GcZlOGM>9*#WnFYg{PZ*;W=#7 z^0X2+1d8*ga1=1BXnM~4xib0-_ONwO*--;|?Vq(LZL^$fJk8m#>4U5Dh{){LXjS0( z1n`a*LGSjD?ggXKtL?Ap-=iV?4S&bmhlBpd{o~#3d!x~9wVC{(ZG+=mw|i}>B|+xA zR)vmLN8X}RvRg2@9nH(q7G^jD)ttJspOg!uMOCdP^x$jqlNuLSfgJ(B{fw4(lw_5A zKy&ZVjPLCP_3rgZ;7X>0ZNc$J<-Oa-z4kzRGM6Y34Q;tJ!n9<#n<`amm46VPj$(L= zE-vJm#_hvz>kWLOdfIO&xl4*?+|CUVa*f<|-El6M?2?!6kk$jv`&DQJGQH!8Iym2 zuXq}c-`tbi5O$nTvT>}*RR$vtFOoEU3K3FM3lIvRhO+2lFyx(Bd5lQE9djpPbpR{| z6uM;hfCFpLc%bg6RDt7FD&D%^F|DV)MzV=V3lIRWoxo-8crY)4l7GV@^6&j@N=l<; z$mfTTK9<~DYrq6&wi9o$99IM~LWgM;8u~f2LrTG*G1b8TGU4Xiw zQS~ir8+&Z@c@ZU~Ao4*JzbK()(Sty7gfCTad=YHfJ;7a{G|9R#wmaomhiKU24DCsw z)n#r>&l!1t%jAGJeSf{kKv#2X?9ok!GXiI=QrMoOQ8+dkQ8e-&RsLi4zqe>iXq;sS z*P=zOv17aV@udDcCI>BV~1-_c#%cQJI zVN65a?Do;uUn5&@j=s@dBI_-aqqJFv4Fz1q!NvLNN*s|pO@H!w)_0k-D~mgaPObPy zN*47p(y4-FuS+aR)K}mDjle#<#6?L4x)KmtNFo%&Q99IAZ;`VzZ>T~~j-bPV>L!U= z@G!$t-Ke;u&19pY#X4?Xd8kI2tiZfJY1dnP@(ZpKoKBUxJ7>D3W9-_LrUSMRKbz5& zi0F8v#3P_l0)MWCP&%n{@fw7+^3h+FX{$IkDl|2>q*mpK_jvRbgOE(c*mVji0y$+O zu;6>wfF{bX+x;Az*MdPM#^w1;=5P*JmA<(c{;*Yx90{CGaie}H8}k&&mOq2Sas(hm zQ6UmYFdIf4RT7LlAZpvDnsdC!fmT16m&$l6r)sKU@dGM^M zebULd3Z23LriFW<>*z(d^Xnzjde-#5jE9OiGtSdwHk0Q+Vrab=f@n3_zt8{a;Otah%FFcSlGkBtjElqy!W@-TkKDT@*2nJ zL@t|`yJfhvX{AJ$N@W5i6WV+aICP{sfAd=o;>fxkc328e58GYD6kX5LH!R3g->_>$NZml% zH6G$gsKIU5$#*O$7y#TPTh}jCP@#E0dFOZZ;$={Ci9I@Icz z+9ZwY;^)g%6jy$fcqbn|K6HH8@HVUA>U~t|$WlY`S)EL=AK#{*59OVAVWGrYd)pc? z?&)iJNj}P9qT2VN_05}~`+pGo=FKm}?AmLy(C{x(0Qk+DU$Vh2GExY`a zUEZ+E?@gRf^ZJ5)PGmF8-}1@)1ibw<>$QfCw+y?t4D+|YVV6I%%OBX~Z|w3rcKIW_ z{E1!u&MtrHwawO0eodH!FD8vJtkFTXiEi$pk!ypf@Sry&$;>_(zJJ5K*_=op>(WRg zg?uSMUb5SrDdo0&%9ua)+C0lASw&qPeP-Ammdu7;?Hz%wGFoO{V1(<~^?&f|zpron z8`V~AZ`(Ey{_bCK2;5q39ly18i#T?GwOdmZK|iF$K3JY1OO!=Gq6Cs!8m#{BJ&~eK z$yVYH9nd*IL{iT^cYpWXiR9%~7GZbwGvuVp) z3&}lSf**TAfQ}1jlEoxJE3>k5)B2|c_{(@Il_O|J3UEs#%zuF8aXi(4d>vCC!kqb% zrVIp;*9Pe9Mu*m2Wv!hQzqX91IG@|Fy^VIYJ?d~{JFK&EB6H3T$dB*|>B&kSlE;KO zS3>CrZT+nCc1!+H$+Bh0B28x(>((!z}TU_ z9t+@(#xbl3hJT%;f8!cuP}UbF{b3)zVC8kP7Z-Olkdf1cl<$@>;Eh_NOi1Ddee}6n z-TeOf`qR7BsynUAB1jw>B9q_=(rAOaGH`)zWxc8;=;{iL2P|x-W5q&4a9mVjv%*IA zBd0SM0<=S(Cgo6z(Xcyl4SRZ2%qK(jI-yL1$Hg3H1z<8P zmR?MT8WV8^c|BJ1h!>FJWwH2X^0Gt)-o0jeJ$|E>aG^jd#B#Y;5c4CRA7KUU3_JmC z$^!BgW^>d}t=5ZGw1AQX<}-s?43~A7fl`5oa(~6kqIB^bHR!_Ar=Zy`p$UZS@a@|! zGVC-z%~_SS3+FI87K4dnY#WxDHQ}wsG6{|>?d;$7=&I?;Fiulmsx{bgjWO*$bnvhL zAb*v6TkHN4+V=)ea(?fqFF#S^dK;@^>Q_ZMN5k<;cxFEuKh+FBG8sCZC;d?f@9A&F zHh*0b4KzXJ(YXB{_kNn&n`XDC`g`0_03VOId2lnbjV;rYHga_e{7LbumdkgzwUieW2}7s)iqI%54dazb_f3nS|1So43XPZl#D3J%(6V6TW{>^hz-HL za*sK-4T1*?S{>v*IL+F@GoLY~X$!tzCDq_$ygHO9p3H^8H=wkfte0 zJK@9fm2{>W+tzorR>^MLFc7`#E9T%tH0m^WuV@no36LNuT%c$pBT(g$O++G9l4@cU z{`XSUzPNCf@?dkB<;|NRN8?$vi7bn`A}!SdR|1km_ZBS4^w=8tcL8Ut*Msa^{?EIQ1}J zzCxcfpC;h-;`LTuthrFz&>yHI`M1m{h5d4^V z$uT?$s_;%|zlto*Us;;nIMz^=H?6 zlp6hp6qZh=YgD-d((jK-n=AC)I7rOarO(rSQjw)TR83NOqXiO|s7+RImRLVdz>$%l zO!>qH5((6t9!XFfJkAtlG4}0&7T!>4yw175*l?k|Sk++eikW<(%G&^Olz-654X>W7 z6VBowfXQT1#5SzSRGF-R8&l@<>5&|tn#s_>(oz)=`S>Z&eH^tN7u*14Qn}1S?n75e zzX%YJEy`2I`x!F0AA39ub^RK&`FvR*9+XL)fvuHo7=q2X{pL_=w;PU+p$Xqb}$RW|zXo8os$;{O1FKz_euqR3Fr z!4hV(3Lyva@rHNeEzmnamUWc%g_Egi9swiG*QghnhN98gFa>+hGrd2#trvg0?a;i+ zz5!m>&9hy&(Pfy0ZZ|nEan>s)>xTyfPcjpk=W8ti{>l&$!a?jK1vb?z?j9&Q&87oTpfuD>@qND+fw+=eqH zf)78+8v$yA42t729xc~@&LvJt`7MyXO8CjiZ0CcvhbSV`SgCxW}CUCt&vQpI9AyWawGz^8!kSHH` zPA@;JjNn7U5DeEK$YInV!SDin{=0e%L=c0KcP365X_M*b0K&7 z;AjWdwqSoUXN2gE*qP$ff-8;Ym(pWQ zIyZD`C50i8FlvQCuOh7461`Jzo`e@C3W zxtJL%Ilf*P7Kli-dUYCmV^eZm*4-5TvmG+IGu3|>QuSmCmh6PeTJNc*d;;UK?oC=q zl?TBmA{(VeL_)-_^G>TlhXDv6A@G^V#wvfZMmW{V=-jG7t`QW6t&(d~iQ5~V8l&6? zqm7E~yuXJZzO&7x&|6Hmg+J@53xf-MTTH1m(XRV=_4(rVZgT&p|Hs4R>OO5Q%h@0u z_^5x!9xT>-mD`MsoSAe&IbO`r+-E3O{_s@@pJDK~bMUq6OyTiAo_Fj&9rc~D>v^s- z{i`eg^vB&-d@$G;=Nw9p1dDpdtIKoYFtfqi5&fLu^z^A_oDq@@@3en0!dEui+>^6D zF&phcr5BYkm4&9iJ*KuP0l3-hQwcPG!BT&0(Py=bV*ohXgKu`%wu z^&x(^uON;TsefV0WI_KkU~ zHp_NoItL!}zMMW!&we|oBe6V2z~IQx=A>(W^eT*+Hw|1;d;cj3!nCiwn*PNZK|Ova>fX0FU|%*Z33NI z*YaVaTOZ_W9Qx(j*2{wO&5G9AH^n@=U3(Mr^b*%~)3SS?#!!!N;J75Y81nj6%hY9F zMRqG;H+rW;kN))~5dB1t=qZRyIZuB%l3qLi0j(QpZ`;WAyMM*l&@O32itTJ$w7ycW z<3$X_o77IXC`5yxsZm6jB3TY4+szvN>wR;Pb5fGM0(K)(^WMDsn(^`bYF2G+70Dcv zDoHWAET%YZ@FcoQZyh<^Mp=N)YJ>?TQSLsOTvVl0JTfGZgH2x?=1@nxes-_ z659`vp(Zpa*^6Pz3mW`KeA{KXIT!SkO;IS8qpSd$iWHYq0^+Me5-_R(pkavtuQThzE&)IfhT%Lv2%0Z)@f-kcTW0oP&2mv1A! zkII|RP0^1(!g9iXr7Q^oVNVVL#90F1WGPla0f}M{IzdhSWMJU3kX*3ceoOLfk~C#) zOT(7Pq&x$=Xcg{~hfaS2BRiu!FEb1x1cF-=3wIHCjSJf!M#oW)c9)!SdOnUU z-Yk3s=hMT(HuNz`YA8hn?2s|zc|{Zxf`6#927jp`v^V{xI50fccH{SlD7yLj;qq4n zC|NXR3dnN|#GfGDYtNJ)>vN$UHU4SFqvQJhceJevjXujnc6c~T2o>|$GRbl(b2}^y zK!b*))m;@rGxC2in@n)w4lQ7rY#~ zH{>k&pwN4oQnPtz5VngBOR`5ZWSfm`c9fJI&)wB)1G^ezH2|<5@dvtP&21{prUasZ(OXlxOJC@0P z#)JD?imByv$$4D*xU*eC6Nhw*vSAH#=NM{n^_o&`jtNP=*#(pYwSvP73{1u_Msr?- zQBv2*Q#{A@Hyn&FP9@X_SurhxU9*PqvM8kI4y78+p~SJfLMwfg z>TiG597Wj71Gr7T`Se4*b9}2*zw_krU)iLYQICM{txS_D9LMqbr?Z=zI0l(K&_w~r zQY^T~Paw3FF0VI|z-Bzf*xdpZ-9;%iVvTkN3pOTl53j88RY=lEl6|B)3_|>)1ra z?{tw`z|y6zArDJB+b3G#&PG}{7%Y;`KAb;{PNDq>gQ*;<26pqISi2hoJZl)FfBl@7 zP&jjv*{Um!iRPSuy$!6C*x|&sodwXn>8t~yv#D{P(SO9;7{U(W_nn|?gXLlSd>MUT{k~)Wqnu>L-E>;8K zgAo}Wgl1_{fUZXv!k0#~GA3=$DX5DOI%Ir;)E~@p2v}65uG5S#xXYp03}~30fsqDk z^ny2FJ>hmVg$${)YWl2L+KLdof1kSAjodWLX|>`At3%}ok;93D#Ga9{{K&&6C8vYAvK9`y z{kkFXLq=OQhIWX0!Z`gr_i?mgm~Y|wGmbwVt>JSVzix$T4=sOp5WVj=3R#<{BX?x< zCOwgf{<^<|j{8{cNk(IUPXQj9`-35bs>g}?fce8a_AuoSKi!ULy(iw>u2o&EBWGrR z)R|MqZ-&1+mb`i6s=fyFIQyE<`rGldDZ(|zT&zjoXbfuZzCqwzOQNfc%b&@+36C94 z*s#YrDp zEpn19@t2Zh=Ipdv*rp?)Y~{q}B?;A~X3evL<5|weFh&b|c!)`wRQPM1WI1G?pP`i{ zc|xGkkl9`<8io$qcf}ls?%-rrNTmiZ0Tc5H#x)LKzYTwEFJZJ|>75XHvWdw5yxQ8V4Mb8hs2833{8=DWI^X?fQiM$ioMj zRK@cy8li(*`_hXfuS3;0Db<+ppV}GM5WJH(J@wp`WeyQT9=*t`dcCR$qKH>3(TB_H zi}SCaum67?-&}k-yFP=j)_4@`39!a7tc_!8ynAoo?g5$Qq6QJ|-DSldzp%jQdJD?~ zJ3a&zIKrXWRX9r=|8n{1g3jEbP5iTaF4N4bi$^|9dm?NYMuTHK$!b0jU9mNDi?pns zLdwQa;&VA3XHzWb9sUqlkeUO&9X_@NBtX@U>DqrhCNuo4rl9Jrx%DxG?3|2!GeFI$ zZeSu;+Frq)b4au(AZFc%ZV|XIX&ISBKfP5i{6M(h=)TVeV^T)Dy~dnODhImaGL(8R z)4n=l(bSE^%v&{K@Xvy2tSJ$g8~i)NAR_Lk+JsDrPEz5~6pWS__2QrQ|MnLnJiS7i z>&Jh6U4iOc(pjy%Xi8`t@wfJF*+45HaZv0%aq9N~hpl>ItaUxJJF(F2X};!gm>tj1 zGY!bB%Gf*+TUy75SIJ{bjy(!juG>ij&|9_-qme08{h*AWr8&cDcioVm&2`y@1NB7* z_bofA2j4Xe?J}%&ZEn*mZSD(cEf)Jk(ZGMfv;D$@ z9^Z`^&Sl>9ZP1k?7JZ*UE=;1>lswlpsb9!A99X?PC5^{aPIq#H*D*RlCp zG87p~)DXP0qRp<9X&QPcv~nYS@)rNE+?n91q*smiZCRG+cWW}U>AaPp7fXQ()g=r!(*V5}KR)K8e3!Q*L ziRXctecRiHRgb9lS2(yX3=jn3Qe?*L@@jDuoK|*0>)?j2v-=*!{=^?j@dJPL?uE1d z;Y%H|P0M20C%B=;)asdxlq_4{E5QCJVAh#D8(u-Z@BzvNF%Vl(Mkp>$FuoV#0t3}J zkiYSQ!wemRId9u6F#4W@1ofR7PM~&it=CE#Uyy@Eo{*+Psbe83$M!E{L`tTJhMaaI zpt7XNH*19gCbS)8Ma+(kLI!^|L}SR~L&h8Ze-qe8$AfY@B_gXppOX8ePd`zOseSb> z5gMiAi?}IcVw=TKW7mYAQija+oJP-51(|E9ZPp5aH16lE{{yvFZEu?}5dNNDaf=cG zDqXd&%}b?qoi?dzrgZ%jqKc4nibiZ?n|0Be|GqOXApuGnZSxllcea0@d+y=R!>jly z?sT}nMiu)3!dK2P(75#OH)|&N#=92lSa9T;W?YE~y)PKWC{6lU? z7Eu@gOL(9|!6990D=kC5f?l|Ep_mh4im;R)Vu>vLMSEG(hdawjx69%-qo+1Hti#Jon^Pt(xJqRSm-WI}BCP7Cp`*0YZ< z{m!qrNnIgQ)*q0pt!SIH(Xu8==%y+}h`>S6jE!v5Hk$h1XB?nF(`C~9fq;GY?%ll? zf4$7MnPE^rMUnXd!h}W`NDSTOE{z!7xz{Ys7)5_7)f*LzpnHcTLq0T&T>Mk~=q12R zCXBih`s9-&w0WGTDxy9Z8UgWz0LR}DyhcKna!1hkZGc)?z9C5ffkKmTGaB0uQ^&KX zliPbZ2a{kV&EYQX*#~o@pG;R3=K4^ghW5VObDgH~ z;rxFB+@-Vd=Jt)ZnB3ku3M5b+Aa9>t&73=R#FKd2$(oMFPN&>SNu#1y#G4^Teokb` zwYyajFZO9ph}u#Ejj7%cNTR`@H6cxtkTK(_FYBy|$9}L;PJ;p!JWmgR=Yh0*&i!4B z;ku`j_L>wciNHDulPFTBu8#MLFROh2g*$&?RRFy{w6^6o5nnE=`IN z!$*#>T%pEDWn`Jo7CXj)kf8v|B#1oA7?%nVA!3BE=Y+;k)*XOreip8IrY7qqX+~Ix zmgyE>rh4e!6t`hb?X}f=A_U61!&NE7x(BDH@ae5v4_^`?#rwd1DOl~d{~NWOV zsMW%%NLV)qtwgaG3;WRu}gK6D6{=lvv!@vO;y)=HU0pNQprxkFc7`_D~775t_1u*SPqCvl~6An395`U zAr{HlvM00=>c8Wp3*BK(){NicH@fR^xKU0i9K@d2}}sca$n`OgS-vT34BD{IRTS{yf{xk z+`8Tt7v9c*a0aLLB-0R6CeGzxbp?8tp$D0b(U4`pxe0tTxLSEyz2Ns1U8YMTO`GL$ zXUTp{Yl!?Sl1clW<%zJ@qIP@!t%ON ze5Qg!To89QV70Q7+NgpXzaO~8I{~H?E8C1g#ke=(&d?-6PiM^t-1U5QU%o&3IDV2>#OTno6N56!o?;ow*dfCA?QmAHk*gF4Cp=wW84ak^+oICrnlA z`j07_X5s!o|BRbu_1&z#usk8qFz>uyl~BD3!Y~lt?Wme_|h)kW#rQ@`M%f zip-Uj!C3<2_>XPfa;xwB&=`_qTAdxI3uiJPLV}}&v#t5R!nG?5PY`87zE4gIn@Lxn z7%(+4&5j1Ks}=0!PuZyA7Fqe$)gGI|zu*ns8rg2!%JqNUUopOLLaL>l1$|Pyz;TpV zsA9Q_lH0;G1ezRL15>0fXJlFJ^+yy1`hoqDp4rIZYDo!h7e83!EN9>543AD1v&G(C z?9DMmUN@#{YM`!7)fGx z5I=Yk3C@4gyX72KWE1bU2Q^;CjLh-&AU-DP++z%c1m))Uky>o)o zIf*^689(wUMQWu89tb}&90wFhnTWByzwIITTP$xQ;v*lRF!cHC&(q=HvOgX6FF#E# z&@p=R_VqitE+r2h!{~s-LaK$aM2WgE_)E!g3X==dFAFmfiSZD}cWhRgxD{H(!N^1= zsSkgbvCmPV=~OV1E`4Sf?O74-ppuDc;yVJKlnMVkU^7CyCt?%G!DQQ*F46?+V!T2% zR%p8)2IKzuWHkQE^h^KhYa)&g#$#J{%>AiYxW4pTtP zQWD<*BkKj`3R*@{K1hXklt&jS4#^`7TgY(6TC99M$9K~9U zeu}}5-k0Sg1mh@CIGTAUx>g5)fdLx;OqGdvflpK`K1W?RQF zAY)puC!E06Zz-N95BLN2lT<{1E%~CCI=K+w?`B7-QY?P|kL23@uaIj*AZf*Sw(v?Y z{Rnf4>GN`(K#nblRd404FMWLOE$j@Dtrv6*u>3E@Y?;P}*wF9L3u-wTL<4`xlVgzB z8~~b^Z05n7waqQ8B8k=@*c<~3A<&3`VFHoNpP@X8A&=(DFNw1x3q!0@sF}?eQf-li zGU4aMQA|UQN-*-jbrjwm2RQVW5qo!=J)WRfuaNQ!m15*?dmLE$J|Nka9&7YRz+(X+ zw-Ie*^kg<`LyUjZG)cEc+aHfdV}*G&1C?;hP(W>tcjcuqchGGy|9m#S z99(`XbG?RRR3ciA6<3SonbG)DHJE_Jxx|k5m;6=;^1pTaZ53~keh#P4MmQQ0fa39 zbx)ww40y`eEga60l>9Y`nHNod9MKs}TgvLUVFf}Di3xukLf*3v4-XG`0<4g&@IwH< zHcL^Up){1oW)4ThWf9fOQJI9WM9hGhQ|84!PC``dUzGQllw9BN^MC;GuDS^5YFz+F z3`p|jnWqYcJX^^EOA&wDd6p(|+|vCUwaY=*mY+m);l&m@K0$BI;-o?}PZGP6yl-UK zW|G;AtB%v!)FZU5Z$dkCmMo(H-D2d$(z5kA$w5iV2etlCMS+J_r;Xf#+}wMdQC!@V z3C8u8X)1DWdD&Ch_{%PuJLfeAchl+=9=*@XYTd-h4~HsSXNP}A--{xSLl_$gINxfn z;zb&mG85h6u?;L5yy?$+RyhARjl|CJ8H-i%sEaasUq$j9?-wKpa9ksSAIWJWe+s*q zFn)6(__BjnBLaEm&rP8A(BF)n%c8D5SZe|8B#I_w4TUQC$a(6N?X^rR4;g$M?H?cK zsLLg^EX?zROc;NWg{^Uv-8Dz3_Fe0t8+3Y_&DVMFGmHbw&7o8?v9TGfSD@U8y0hSc$5lxmWq85+u?+;mQP?B| z3Ik8=7CSe>_Yq0~IJ<9*Ayn%dOOLm$vM8jw1}m>fxK)2wMc}%l_^g_^4~RA__kr3z z=C8jZ>temYsU=#PF31AM54JVwk3UbZ#-mT;v(Hu~(H;~Pk6C}dU~70epFw`~dDFuB z*FXPWPLh>vnsj5^vwO-|{$bUoOYLx#c6_^~3;-&~)F%8_WCZ=>AVWhU;J_hXPnPtx zCKxJrUKAKkn@I%Gj0Z5iSKy}^FZfI+` zJr$tus~yGbk_NEFMJl`2UR2g2Jn&+=!bS`$0u(W~z^@}0I;bvl?<$xC*7%Nr?UgD79B``Y?nQX7T{Wl01o<#$BJ^~nFcXidQ? z_I40)^VT*gYY!1Z^pj=G>TB2_4Rq@Puw9_Y6azt zJSu;2o!5mDQAr38>T2|vLf34SKrWfoB$9*thOlsBXml3pKTklX#T`6#%4-#lsxV~; zRbLv)yI4U`uLn#Ck$lKHDz-&sVD5?y-Mb|Tu$hJ>Ad#+lvp_a`_d5Lq5RDd)JrpLB0z-l zeku|*F%AyfYm^G+R@SNs7?^uVNKm#NAIcV3Qc~V&w}Z;%O@n`| z1fMv_9j!qSB@qab^6yWkE5c^B2BS&aSRhYrJec9PbSY{isi6qPS#D}#v@LEHWBafY z+Z*UJuix{!`9JWTg1l1##>H8_%sGhz$SK*k9|MX0H(VmM$-2ZKx6OYDb24kza)Jh8y{Ecag&S*u!-Q8GzZy!Ad%Who z;HFYU)Yj_5i@Kh(+6E*wT*~M7IITU{Ak$QXVsiq!KkRzGBxW@}tbSxv`TD3^GQ)03 zp}((kltt|vObY=b72wB3lmuq@cUMUotWs}bx_^akSeNe*t-%teiY-W#MoE9l2Wk~A zoI~Df7)zhM!BDv%|E%Z)y+JQuO8YxP)%h;eneF&3s|l--okEgQHdOxeSd1jXwsoN5 zw5TePDjUK<&C#&}@J*2l)tX$!bVfpEOL`qtZI#qBQs=KxrX0du^`#i7bJX?#m0{(& zE3?5j5mAToD0Zk8_NuW*O|^eIvqqv4L+uv1=&&Y~xpVKa*5$s_?S<6f;dR*?gdNB- zwMAsc_d_pe_UTaey_$gjyg}+JQs`7C>ZiSb1FcnGZ<{a>f9F$p5)VY8()QYHowl`8 zD`iWzwtGTC#yL_;#zywh4o&&)b8sLyZGcYW0deMjf9}rU_aDoLG6;VpEx?qN0lASm zFobn-TNk;Kb@D|OrIH}gK1rhlB)3=#mx}b=llVycvg{*_{X=YGUhJ*=f3~}$9J`rA&0t!pgE_bk)G#OnS z{0WG?O3fcB0tr=OE z&%b?9MZ(j9ONh8c5;9fE9q!#A@!7?b<_J+@&7~Sjpco~q>s5NST*P~l*=$IZX^Vm0 zjGcTze_Qdl$Ko2W(v4Hnhj^ai20bY1Oo#R>FmwYZCYe%#gqD9+*alePB&WjIku_eh zE_Z7NM1Fq0yuVxB-=*J|KfiriOzKTfrd5a?>9|KB`|$tZ#wQ$iVE})D&XnO;yU7T6 z1TiAzq4tO&l}$hY#LSN*e8@-%m%x;^+iV@VL9g3*yp993hZ>UO<^Y*iiia4fr2Cyl zr+%*_%-gl`>JEQ&J=Dmg4w%W4>OqEE_oL!~liRk>9;S4=1SXSq`J=S;KTy{b=`SIx zHm}$}O}0m=6*lLaH@hpXm3BMl89C2M^u|9nxIJ{JWT0~L@=-eL;ZGTycNyF9o#Gj6 z>)8f>0hN<6Ps1<}h4=gl4;_Nk0p@}bLPAtx>zd{2TpE81=PsPjZ6m~g$7xf9P@)J~k9kmWv@?t37hs1st5G)KX}yy6E@;AX5P{At&|{P4 z4HLBDQS*N}|7dLj<}egtnS%}>)K52q5i8Cy7UC(E8BAfDk{;5i5lWP>G{au_FqI61 z_n*W+B8I-WG?z+=6-2M*!BF&mnTGMcH!_Pocvph53}@5rUtL)ujhOtqnPsY+G$XCH z%>MVZg-VP zk=j<`NJNOclOnZS2k_!={r7x0@tu`IGeKZuL08y=73kU~O+8_1FElYBpyY&s9JE*R zlM#ObUn(_X-u*1rQ8>!lHIf`uORHW==izvBqt$&xD>wN$~5n=lZ) z^A$cs3P_u3d!|WLt+rB5E0yE2id-`URAYZEdz@WV@$a<>Nq_*G9_kaqGw;25o|$i3 znMuzRv;ZwB1F{n-FoYzIsv<2#6+e}QECmR(UqY80;vc0dC?crP^NhYL<@tkw#Jm*o zP8=!Elke(a3L9~`2&bR~D#}pD5vXI%ApQv&a3?H%YW0R5<5X+%Vo(~qD2uS-5FvkM zze!$54vtEbeyaD!Blbv@A1MM^XqThI{!b_*Rt;KvVSe$Pk<Xqy+PfP1^kcATserPK_-&SgBPb31%m)(N&a-?}lSM8P_#o?z6Jh%vl4wke zlY3IR+E6KZI|d@YpxN=m*wni8~~q;MG&_G@;}x8a7&Ivv5^)hx4$<@XuDG!s_{Sbfi zqHyt!F0(c4C>G9pe&jKRZZe)`ybyuHbs7<|lcB$hZO#PEM70Ofm6Jp`YW zhTl9+khLIwLg8s~UVqQ{HX;m-(R5~7x#OPyRWD=@QRt%}i+!Oan$P_REW?gmRpP|Q z;;WDyUzX&+T<&bIoEJnvLW$?E5U%cF$YaR*O15Zxi7db>BThw7Qy80yy|YrBq$p+V zdgWhj4C5ZJ+C-S|r38O7PQ!Q!%*lqg&4ZU9!%Zn!&M8SFKpWd9c>2YK&+Pjb2Tw09 zXU>T|K(=)%URuAxv*K2&LkZ7lj2MZ6voj_5o_b*fl79zm7=*wISBVbLyv?9y3OYmT zcf+PIK-h(qIf)l+{upvC7z}V-+i8Iwkys!A6!3(bhSgA_Aa8$NO^ zANCtyD~X0B74djMB(*zkXy-J;pyC0!!vJwL(jj*_&3uj@k|e5Rlh8cyB1Q(tqtx5h z5^7IoREe8;S&eDJ?*uPw6G{^bP{ic1Srhb*$5qIQh&M_WcGcJxuP_dhPF71wNZifC zPu8Leb+Xx#C`x}GyZQW+5heicWb^jxqXmt zHpPgkn(b3ou47ZS7NG=^|HwVwjHRCl7qgI%l`@)4iFGy%|*Cu-;`EvHaW?QBLN zU(w`IM$x_r0ID(c;g|>5{v0nz5XRtssr?!1|HAr1Q5Y5P&cgPL)m=sh(y@4w+YCby z^OpNy)}j>AyLchy6P(iw(5MsNI#pBR009uldJGqQWdc3Uq6m%0<5I;2+*(E3Hs*H{ z1P~z*ccXtwrAlOM!^m82xT_m;P0Nj73_>K*?(!;>@B`Jv~(Jwo4+cpf@$tAsF* zFw&x&evu@#jTxm72Oq)TA4$CAtGZrl<^Zhx)!d6B+<$$6@l#E&odYVIz{{GHdH7?Uk|6YKrM-CW~z-E3JIlsOQZ!7={QP zyD(5k+xN-=Dtld)JwPy4;#fz^0BQO9w=>B34RoLm48Cj*F-7D_W=Ea#lt4uKSwzo? zEwO(Q+oom&Ebc7}c)?$%v#Ax%S~%|zfzjmv`rmSTG?~j+`-k zpTjpk|8B_xs%F)02BH((-2QQOyH6#Nkj93Zr{~_0zN@pywqk53t~_7O*(waU-rj#K zsChHd$H`*Y$=@4lsvRtKW6AfJa!r~fj}2f7S<)nn7uX;Iit*lhU_ZP}k~qTuq@)!k zfvrcPE@2ss+7}s5Vt!FK7&~=?VGqhS-VoZ*`7aQR1>60)C33!H2dLKyVu;;=2wu>~ zjEJa8ioHoqy(BEIh0=bKGCFfw5uJbP*`#p;jT<$}=LBv3I|E1tw#J03)3-NFq?HdPuzZI1iVa79?oEZ#&Hpgm6v2Y_RoNj;4%3Gs> z9LKeT#As^ZHAHKz zo3-hOykw1MPd2P+ONYqbIj)P|3f_I!It2^cw}$5XNP&hx1y=@THQ`oB5BI%zC0NRr z1)fFz9G|vtQngx=YPdiCtO$SP%F;zPX_PF)6{*S0vSKL$QLn^POK#hoqsoP@BrzO7 z6yg_SV{jQ7%rnQ?x{ZxgFl2O7hRIrWBsJMw;wa8C<@w>+>R>>lqw>1qzcdvONr}bq zy6gQ1b&fF$!Y~ws_xCF@bWrdgR45_{og5rox}?cV8)#l0$qQP<|89Tk;N~rN!*|>r zJDwSUbimBopz6t?5wyc?4KC6;oMYffNKzB!xZrS!DQFQ)Er6%$ZY$oCX4d0^*CJfx z2lYP!pMqqw`Uk)ZR27$T@kY5qMtmgYzTk=Mwy^86Bu|rS>fx*>Jg9m>H4krP-na0E z55-hbZ`v>re$THkRn>naQjuU!QyElU#Wp%hBw%}K5Hi7mXpSA(4r@gD@3WI&k|?xv zLz-U_-F?+{2J_;KU+|h0Zb&2}U3YBaA{BQ|})T`R9L2g2vVW@eNjs^67)jDV-0!g)ET{GlYO^_@x;@_m_E0c8m7~>?z#0wmFz>kTi zl7ac$lPLv@Jz>dDKvqR&pbLnMy#{;Nftz+g#$ck!N%PapPD^DPsBsx+vhR zkZ1QF#L?1ltNfOnb(#P6d4N%b6FoG6o<)_G(q7#?&{fLk+g$eoS10lsR?;&cIzs5jri`2CeE-w|Jff; z2F^(OUtWKJ6%b*AS1>*Bq1A4;TD6Oz+ieuuRhL&*eqZ-eOWEdy``lj4K6X0WLb9DQ zy6!dw?e>Vwcu~iSArUiDT9IZin_V`v{fu|UA9awi3WG2ZhW9*01_uj$0}Bo<1*Z;O zU1GdwAl?OY4pRE=je-_hlO;Ls`~Ukdm(urM2vmOq1g|v63Jqw6wtmioA)M_0vSL^U0~>%#ZAtsCFgAUE=3XQyW=D+g%Y~p z!LrZ3@Bi=Y`!ei;QlvZNplgIR8LS1HYBO}k(@;J6E_gzcv?BHns}~O(;qA=@*-Shva$A(msG6U^V?Rdfh3Dzg+4!PX zCOgZwk5d&;mXQAlJOJVQQm1D9qPS*rr+L}t)_b??rZUJ`i&1~h zun`1VbJ~yWy|>Lb_Med7Ro+~GisP?Y)HtY5t&hJ>12GK7_dJCMhVlv&gai^&7dmvk zCFGnE;n-2)AR^RvCwC`K3`L?6o<9HkefI5rdQHZ#jaZU(2s`%Z6vyGvq8Dz%qr@bP ztX**t!r@7Bw5pi5#`wUNz>zHlo-2RRzG|wvP8_rmLWqelc0_@5{T+AZySe!^&?D6s z3J!c_r$HEOO4K$*bKoOfDSqfR+`y903(MLR8MS6!hPHF{bQW1qAFO*jW~n@`XeT5Y z?tuFvlf}D)@rS{$sQwQA6KbC(wPIvWF9(#3&DpkZK>0JF@TTGJ($(0B(oW$pyPD)$x zTtA0sD_Fd{G-h~%iiPvGB5r@;ZOXhcS?%Q8k$m#^FSNuleuq~1JIUNTHE!N=d$`ItjiN@HpTbXJmuWa<0pov`X&eeFuq4Umo)H|c zAeDbW4~u;8wG)72W}54Q0iY;aN{K3C;_}8X*YkhQ;Aq&B6rY?EU1=6H_q-5K5KBoc6%Pi&;Hjj58Rf&DvlGiq z#rt;1@k~xYNbr*DZSS?RS>ypd$$eNgUD5O})dr_EN?#*$$ zfL1eu-lBLHKJ5~`uzcDELg`>2*>!TwV1gpF3t}dtCtX@&+P{BXA$F(lK#d140d)Ef z7!HRPKTn>k=@-vKPqm%g=b2Ui$$Fk~bsl=&M#%y}XDABCkahw-yWk^uaQKen%+3wD znIOYrHLJelwE%V1uE{QZ5FMwS$Yg!^0J@npOs`Tx6Y_;11YO%HX@DX$o1B}Y3frDP z=ZE?89yfzfWmJDvW3Io|JJ&Ux%U=7JK`724sqlB5lM}gxciEIHOyKDfOVB+gGFtI~ z(Ez}%$H$z{hXG;Kf5L1H&1!S{ea8hO;L&u>DXrblqHP?f#)ADJF5h~5RPJz+QmcEi zQIDnOA&j?@NSWivBzkX_+#plqM*}7(Yf|S6mhz>vz2ios8<8YCQ0Mt#FOFk;XgkLtGF^MWMIie!K z>nqq_kI>+7#o;-YiD{RL!|C1|x*QT8AN*=#3iRK8>{JtfoS{};>!BZy^Oa#SlmJ$u zdkgO6aI((Nedy9qq2?x6F*-^|nY0!= z`}2Pc=plV{y;Vw67ijvduI_r>+o;PdMFqV?ziFRa?7OL6QoxH87_^YCW8KiYMy=O| z%p73m>5`*TL(jw642(v=0ae^-Hm!bAtb+`fLBpa^!>Lk7Yag=Jgw(3NUnhixDbt0? zE9!pTyu}(hbxWF4Ze5+*4LbAEU|v8=i|v2Q1eaa2GuvySCA+rIdNG+Sr}>ADT2Nuu zTP+;X#n=_Y)+Bk7pIf}I$qI)>_d(}M2?1D2!IZ!3f(S280A_MDf;r;+h$mw+M+78G zpg!MZUuF`%6eFA7Bh=C)1I7>#c6QNCFdzb*LTWo%%zIk4ZwPAG`IB!yPP0D&10ekL1# ziB7DhN0AbJC@E^i-TI}y-1R9oo886-pRV?ZSrJ(bL0w7%7HoP3tN zf_`A&{2KH+{B9X5b#D0Hfd3%Q`3n==0f!3d+<1#Fu^ml#>%7u``s-J? ziu16=MINqX5;nNQtN6;m1D=(Vziz`I5XN_&;uKkmEyaC+x=p2a%90F`y3`$reYTM> zAOoqQ%I{wApTu=CB)4!J-+jLm@bacAMTnp^YDF`IjVLfPn5X+wT}W|CKV+?>Kw(@a zwB*=TI2tBJy0b=ElMrI{qpg>JbZ^TNwa;{NuTc*kmW?pj6mI&2WSnXZ?^IV8|6T;l z?`vnGVSaWtAio4$D0|>6gTji;i{PhW2E&^ZL!d9470mn~kX<2yXE;ihckVsIM{uOv zqK4OhF7diV^XX3}oG?nxZG~)*W)*~2T{|@!FN}b?8D&?GCg=(N>)Q=~u#Dp+JdMiE zZ0kP76zhW(jyh_X-eO4&J#eR+o8JHkc^9K4*V%B7@w$5oF2dA5r_3>2q++oeNzq`{ zl9v1djm|v_!Y~j9;61&aYSplKqmwci}(H`uSt;j3IB zU#3o+z!nzzgKUvQODp*oSQKot+4+}zf8pg0;sNbFYjfL1lHdJ*E5<510ArGtoZY%R z+On3h=vZ~KMP59h zKi#Sa0Jw;YMUq8TydjJ)lX%I|iE(g}S8wta;DSHc0+jfFv^j}LAjkPlGI_`>PPjDt z7E3x?WmU3>HwW=1FBVY+Xc(QYDxh@ny)gtVd7%4Ye7(96z3Gpe!gyf!hx9s+n-eAL z_`b*|aanFR>dz}G5jN~iq|siAEjD3ZweRPsh&N|91;SxG zOR{7m+E#jhKWOOF%~*;u68}oSJ*e~MlV?!2?M|irS$qS^y|^>JHiuH(;eY9K@?~vT zqNd)0m`{O4)GywiSye=ns-(*1Fad#DP3E|>{(U_TIH2>mxJ@SU=qxT{6?Ky|DoZq2 zF4JTZVR}Fb*6)}hN~AY_^!po|Bj9LKh44R^8r>-95_~xM&y&+%PJ#~NPuPB`58DJRB_jOSs^&@A zeZ^oXo31>RU~~#lnsCHIn=Jl}m_?R`ZL6h@Ydp0SFcZjj$qa=fvgPqK49<^!Iy$)s z+UUFQkS;6w{iHl!Eth!#%oeuW%A%Cvbo8@->FI#fNm@N!do!Yw>LC2#VtDqBe)@2J zwkJM4zmh=jAIX2s4F%qok|gv=7L}wuJ186AeKI+~JGkc7gl;FtU~$g`v^IDS4!Pi- z#!XzAJA(SgCK+R5B6;lGVv`Q4d69o6yKdUCKwNZ-5!eqX%PUlJa_XUX(We-`!-Hyn z9#!y9jQ+WtWL5f46o0AWj4(t}q6D}xO4E3XFg265JEYzC`yH`DIRfkp_DQGc`VQUY zs{$?O%N_`iQBa~C_HZn^?Lhkilu2?M_ky-bQ24Kit5uP?iG9t^pF8P0On}CFy`D!j zdG_ARD;_X)Qv;UI?tukal+?* z1?}(ev&CEDO`rrQTiEPj18@{s7CebV0H~RWZlJ9q&Va84H$hLah2iJ|a~&psX4Hz? z2+X3;GcKMRADi$}Hi`2Ya@(k8XN3akY{al(EAH?$5p)xSkFUqrw0jhTM&Amy2}vSK zS7@*;NhH6}vj<0vV|5q8Ve;oIozSM*r< z;d1v%#k^`6JMkSW4RnJ|$0u2Tw$WH07UTwO3}4FIrdS}eXaYzb+`mz_9?NFQ4M_f@ z(~xqRYqzm6VQ>(L7Eq^CVn;EZoP*A(0dkLseGL2}*{EevxvJ|7d)kE>s_5qhquFlCvM{+~vY2!PI8F zPxQBk6f|&;H$YD#S)oE-i!_(?AakTl%H3I~t`k)D3O)bpU)OO7G^yib+*0AWItx)} zG`HzR<(1R;@>*R^a|`Q#1jTsizM4nn`=|it5_;2+S9lq8yUd*gmo?~q46Zs9swBN~L#ah~0Lq);rq&X&IGLW@Iwv#DMu*;E-o#s+m`jf-wO;BSapG*Z6BlW>w9k0TIJHVYUQCy(}j!syJ=8)ERkaJ(Nu<;w7pUZ!P|pK7aq(m!x1sR#-JN-V1w0WH)^wX z2MH_^1Y#6xaq&<*?&uHt{nPgsN2e!);|}r-AUn6&O3F!*P}+f^DZ;MYu84zAm{F|7 z4w>&|;AJ8nvQ9I9!dqlKmldRY4G!W3S*Lpm?sGdFEPAXr^`D3lgLsc zV6H5}es?MDpMml#jyDlInsH@P684XXuWkpU_yUL*Ms$}MwDH?E%#&8!GI`)?+Lj$; z8FI$9$r0t}kPgxj@bR~@-yfcxot}BX;nnO?!9+S!CyVKS7&ij=pDmWvKifQ^Xh8Z% z7zNF&n@0Aqvg3Fll>oqIFvz;Z=M6lRs2oAmSo*RbD3~;#d;&{wS%3*V3wK65F$0Zu zo|K~<99AN+A{qnjjj|oiq#YIk35f9(xST#d3UOnKg&Ze1%yGyGVY!N`9LHG@jJHF$ zB#x#B2xNC?R}pDSYK^7tl%d#1 z)!y(#bUcEcLr_Bb9#@xx=RG>e&k@bk$R^fnfe*?;)gJZ6KE=6wC_X8tNuD65c#cSY{e);tPc@4ns=#xSv$rNxoT$k<7m$~?aF;!3r! zYLKN2@5YwjfWzoAmg5hZirBVt{^t(E9y$gzSMBRzSd4BL#_Y6ypPM0l!tx74BPUJp ztuxUKG@1N@k7{o7Wa`*318Bd0@%Q(`@!;&I5ATL27vqnE;}1j4x1kb>^|`oPg7C$r zoMIY(`x+Au%Jv&4(!Wwq$obzaFX9MWp4@RyQy4KSxx4U6K=5fmDP5EDZ8Dw4Ku_2( z)TAz*995J)?+gEd%C3^kbaeDxb}sGm$`tf;NE?t`vD$i&CQ*rvTCsMo{3q#ts=3+qR#VXIHZpAqe32pman4~In1hsZiJrK=#STW>+m|yQ`Ex$#*t5L*vTTi zL)~CWIbiX&VMfkY*Q|p_=hiHM18p>OC4r^Mlm&yT&BzuVRXnvh8gK-Jt@)4B%9^0 zo|G@2kq_9jGKTLs!Pof@7PFf|-m(P(<^iD#pvj=#cE&jaAaU!6$>5{uG^Ab}Gx|xF zdZ9C&vesottCoTgR6c{n0_ivyVY{LGOJ*!|JGZeW-su^jhOcA-j-J-8L?$lgG2%02Q4g3g zjZRFW3nak%>K%M>F_o& zn$x(7*Gh}3S9mC#2ANVkdaP}_f@T3&t5a@7)&lu4H9LEIALqd6_ znjVHjYSTO$1r^qKC_>X@HiMpjSw-5S+ay9FXEQGl{I|n3cY9|IR_u_Gffap6&65)k zHTv>`MYFlQ@TGasAw3&1ByFa>YH1Z;#_sw2*vE5Q-`QrGV) zJ1K35W^y*iXlzHR>kKGwEu=Fu_?mLMX>zpnBs z#t)zmZQJMaj7|oqriUnh0Odqkr`8bxbyLIu_4j{}bV)J==y*?0B$NdhDvVeIp@uzE z(Ee!__c#W3B+2PFUYiqJZF>M zV>Yqs-agtHWuxpcnG#L40M8WRnH`KW+;GPcx$&LLuLqoGF#-#J6R=mtH3Qg;sX!HU zx5EJFhf;o)#DCHz@SpdFd^A7cdzBkNG!S;kcrK;7fs*og(uGc-eAg5TMX%5vJD|p} z{bo(g66E0_Hf-eGb$NgoEv}QBRlX{F^?UTB?Cq%QP~FnO^>q=0{bGcFLtWPCva5q? zp4O>(is%DnDyUC?GSgxmed}c7HiiHd_G@)#6HoL(3d*v|X-hAcZ{S?mh}XY4k3RgK zffb9evpO^t#NB?&kY(1?0n$@te5MlNiBMA6r}(Zkx2KJdBvx99-MG+&}UmvKpdJq{9J3tP*aOdEK%fRB`j zItVb^b|(-^r7CTpQMtMX0KD@q%mQH5$I_f;0e5+5R)n5}A-4K~n%~$1=OPVw#*v8n zw4>a)s6Dq~{QB5pSk=ULNDEIg?*CgPn_DE_D222~4ArS4*2k`V8zjHJvKdcY#!Jeo zp$z6^mnO@9wena-v*j%M=1(vDVynho674KnKP?7Sk>cw`->EB9 ztLg`XX&y@7D{^&`T&k1!c3rqbU9@MU9=0N4=qi+d$T`q6+twobavbB6Dg%i0jBsq%gu-mG4uS&P7?i^4LK#gyH|(>X=KoEQmnjl{Q`ZFI^5 z=vV8)Mfh)(5rbaXtZc4j92Gco55TBIV&;tVKOS^(1TSsODA-}P#&{V``B;3x0Sbr_ zhlo{w<5-)SIPK*Q_bvj}YD9V<+Hv!RHlzlO+Oms&AT#UrdI6TIw4Ffm~F8% zVkv?)CFenl9j$QNw~HrTKYu!8r!EfXG%TR zvW^zy@qpjaw&u-x>xAcWP3*P1(+TY5+ho2`WMp0L9N`3oi1y7$ymajt-!czxYzEV) zr-+_{@N!Bo2hN;rW&;uK;r8l!5tO8RuZC?h$z;WR3RWhp>r% z+|)~Yc{c{yL7SA?XMj)m#21Um&#%D#L)|X^>~`d((P4!sQp=uGXMD*%T={Y=>(I$y z)X8(Rq=9zz%753>o$G5|&kOX$hjEn2MOsYG# zAJU@r*7oi6A(t3yRY{fVfkK2XpOjC)UnW@w>9Ht7EMcr^O*Z?i7^JDXiyj^K~}zT5o|?a}v7 zm4Wfdz3&f3quz493=jIdUk<*1_AjB!)zhb=QLBwVM%~$<`v!h^7H9#Y#hOUyi0m3K z2h>_z?p}%OZm?NlNN8gj;Y6M>X-gZy!#)~)itj=OL00Nx4ceqhK%P>UQZ9-3Zwu>2 zA=#Ubz-PUm|G7bo<6vC`1fP(+fBSxXbaF9%H~23@?4(Ine_tYC39_7j01g-dl*<&1 z{@~eYRKT`ncu&NlU`SAASl43kuI({RDFW@BcpX39(aDp(9gc)9`b)bOqF4>M_%{S= z22;_FPtjl8aR!PBqJuB_&=QOTJavc7{R|pfbv-p z%qYqf8*;!3M4$4{O;l=saFn4ZCBn#p|C8gl>!^&e*~68_n(j^Qt+v)n1CJ0xbNWF#&ah_5irvFxximtp;B?;Q_hnaPQEONdwLmg z6;8@vU~@N^0pruEBB~%z(~24qYA7 z*_*px_|I{a@Ia?bhkB1b&+!g~Ero5~+ztfboR7dwoCgma5v=f-kN??SFb<5Be>}WNL1M zQJ;UCQMVa?`iE~Y^x^Qw4?i^wA^%^SvEtU0W-qF{ zXG##)>4Ja$LHG9dHsF&cw>#<@;|g^!cD(cez{3hYM150=u@UxzD`M0D zNn7CPxz^|TpN*q63~i(8`F5z<0#kdN_i^_4bFkWf7@^*BNV?$Z?om?F`#)TtZd>`= zP_MLvw=p^AY3X=)^6}Er&cw6SyDMiO+lTJNcBS?Z zas6p|A?JxNeWXUK9B=P&k|^!Tb{?0Uv`S^xQJ3ouwEyjJ_*_(0{&7~dH_N-6udZ=g z&~-e2WhM1PJ9)YJ`wH-1-8&0Dkh|l!fLqDOC0__oMCzB7|I1%rpzje!4}WJ!zYlWo zysGX7w^2g&$P?Bmq0Kvb*fJt~0xTX+!T|q{x;L(q6+y)IJ3MG;nxb*dg$sQHaea7J zJb`uZSf|z@%e>=Lt}BmduZ7AV-qr0@yE3(Z>jKM4dP9BHJ}W5iW{L*LbCmv5Uj64U z8rKn!ePNS^yP_Nq@t;~iYbU@FY=6iU>o#YLvV%ZhCxc_p-1$~KmJ{(0E*dM@wR|C$ zjyKTM>3#E$RFdAUnI-;n*XHy;*_0^VI>sSi*N|Idinx?S7toUDbcnzzR_swOGNEmM z9WNQVE+l`T?$rR|lcNH!fI%|#l{z6cHHx+B*1kF)4=r%*U~#5k*YLP@C(Eh==`BLM z2_L>befMr~a_G9i!>WF=$(_&hRXW9$(AU1ipy_T>gf@8bfCFj${@L*2!`aFB_37bo ze0X#|`0;pn=#M8hDW<%68CIEhGs;RUmfnowHE2GsD+` zq6_Usv$Yz_QImfQOku<1=wxuAx?6hsleM*Wxnsv&TW^(HuWw-#t9SYm_4T6k_vncX z1>=2iebu*plN6<_V%}hiz7n7AiHR!fO4G|tO0S;{fBtZEHarCFp=M%(IgYJ=H}EC{ zT(Ox0ln%O5tiPi^13w$r#2lZTo($nJwy@IZ4&mnHj0&6aH(*U?e5fA!We(qD6$5P3 z(-u=#XL_!pypqbmeh&rgyoCewtUwI`)-E9@egk{KQ&mfwl>aVz0Wkymwm6#qmmO{!DxMGyhS4sdH#ymcCKcmnW^> z(P0_W{B912Tza#^y`%tt#uCGM4VJ;sredTTTS@BbJ(3Ql8+<;yNPO?_Yb~RqBH5bu z#rqEG?%4&MwBYA3nqSH|hI7Cq-poIB?^GAMV$=SH2dH8Bz*t*|stP8UPvA!mG zn#QCL#x)tzTp}p#xSZ!HzE8BbySv*{&Th=>;A$F}0{cC~k~`5|wj-_+OwLM>G+|wm zR#!ISNoSXNA%=66NTB1AERAw??Mb({W=mH^>uvDgPWb&2vSB`d6TKwZU{@-%_c<7# zTnQoSb&MrL2D^wsL3@K7wIiXY`^sLseeIee(DLP5*3`a9N$Hf{RYh$YZ#FIwZ?--j#`mq?IBZ*{7Oz#UKI9R(Wbme{WzdE%4 zOZR^LZJEuu*|nK}Y$pu{_HLlId&X8_7~*o_x*`@*lsNg3JZ#$=MLTu<+vD`xwyd6! z;ybz|2q-qm7s4TU0t51U=Z305uhFM6FDi4?(fR4w#TeYW4`+j8^p$;ob3FKI{Cc1r zt?9+(XfYc{>500xI4Gu-&`{cyd|KZ+DbJnnIrqQeL4S*Xw-otb@wsD4?|Wzi!IO4d z%YCP3tNO^Enw2v(ZgpZ`ICDCg){Dd3Z%e2{qA@%2W@?PF=B{q75#dO2=bJ?T&kQ7z zlIt@cl&U%`Hj+*9DF%YG(67hS7@M?X{%a^e)0YXcAhCzq3E$Jae$kIj$9V?2s%HOA z7sN&fnE{S}j%)Y;wUfOL$JJC z@(WfAo-v6^&~mj^a=VR+1l!-GWCw-fC~Qm6(%)-2)>$o7t2yU0@|*iHP0G10g=LIC zISXyswFjG2MIFVwm^&|6*u&R`F&W~Kng+GaTC0w}HDX!zuH6h;W56K}l^O&|lqSE$ z9%b%-6k%3hTalQjqOLH;!wc5mKl&eyQekV`FcAIjUvZR_*x-hKj+>Qs%ho`;ls0S( z491a9iNKbObV?IO{(I%Pah$k?-UnlyPVe4(cQ?1?s`NaW7HGc-WI?Y9Prjg0>FDV9tlbKjY{7*~?^q z{xA7Gdw#r|hOkg7hhBzEY1cM$l`&6qgCTq-6?*)TbJf!3d9deK>#9O<@Q-ZZW(3zk z>$(bBY4r*cU*WUi__1+bNaB9~;?nrRh4Zm=gEEkUQ!NybQJb#$|NRqJSP7p*5m0j~!rrLFX9}e5D9t{jz7KJSXT2MV&33K)BD0|eI?e4=l zawN9$&pqrp8g6Z`4_G$!ghvoV3@9_WOQhkg2~~5c-v^a_MNJG3gQy z%i*S!3yB?uC;bPb=3R8U2#4oq8_;6~kgja@jl{z*QBYq;uv%po|4 z-+6D1lTk~tCcXOA&=Y)tT1C>V!P*vPf$vewDkmD{xBj)B$0CO-O5Imm;5p;>gRGvxFvSsp-<0kGzHJzrM`^*YW8s z?e+(4*wmXp4JXK5^jf((auDoa9s>{QZtuDkA3LMq5xL&ge|36+{~Y-HBD-d0K785Q zn1ScX(48XkxBRpeiDpvJO~EwS7Jjyh)}T5xmXcFTz=kscs0-7GE9g*v{10l$d@EA8 zeSX&B$Hk%JiAn`}2}LXw)0O?B;qU?6K(7yJ1H;s;WT4II+5uXD>L@gZxLljkc&jla zvWT!F?D}n2&AP?0QJ7tKJ!6}Ch+1j0XMV)J4KiQk8;w@mj?*v@eeYKctHP#`6_>|` zf=~egT4|9M#KQ)y+{D>`S|oO4JM31_e`n%Lnz(6c9@^R+pE)ygW}Me=>vcVua8glK zlZ?V=UeHX_JiXpjh2WcXDXLm<$~FGtN|ZFcYINP`WHM>gN%kLwtcccBx|7}dGslkt zy(XtZeWGQJIQHx0Q{Skp7$;>i$x5OWxczH9#{{>efIeu-a|PFbT2fNk&B>n$;8RQX zn`jEtOdw;fVF5Qkli^<_<5b}vFM@q#ov2`2ca+R_vnp8zMZ=9T+}@%mrDc=pXsQgM z7>JZ)15Y}{7#LES{-$yz6w{j+Rzj38&1ohiI>}==ly8_8es5+7*fI0&F6od`L-@9T;cCr_7Np}n)oZ4DmS%nN#v8!mNtm~umCqdJn$vsmwT&F!b~K?` zNr{Y1zI>s0Qz!^NNp&Onj`VhbZJmc_ zT%uHslxg5Oh6{HJp2DkHFKJt#CCYyY)kWt}%rY684Dfq@QXbOn1Hi??At!q}$;A)J zbVs`uwQZoK#sAJVE0>LIja|R_EX0r&1VcDlIDv}0w+?VZDRM`{n)!H4h4)2e4wrm) z{Ft{psvYlW26I5!GkzENvlWJvRdp$H8l9iVK~9glAbIXxSa_H+j!hbdg&jpnwuUIB z57tro_0!jX+YeWlu|EMs*|IFXO8<;i+LrLy0@94K(iaLtea}#GWOGURonA*nq~~F~ znG-))j+Q9;w{v)TOjb8PVKLzLUt=F#s704WDYTjLyF_~7`7FBn`YDLBp>;pj;U_in zp1wQ$4mvrp^QTl2MSau9)EnK4XnZYfC5b_f-vjnl zbUwc~qtUM&$ z?zzXlJKkO7n_SaykO9ks5YQElA>Y@rUhtWw73@X! zUkJ?t&Ve?b?fVrLKe329)50`h3^g9L8G++^0#1;sRF&XuU2yI4weMCYU3A3-q3_ zgztnCgEV%Wo5k{z>s_sG*FW6l_mAIfbWcc%^aw&iMbJoC;v!9LG^PU5J_{(UB;W4+UOO-0eNLD?JE#5u}%ro=vQTwVS2{_v%#bJ>&y)$7foj zB6c~BhzPtRevsbjX+HxcPfOM4dGO}4`ih$ta`pw&yYb=35 zk|n#@YbrVQQkvFxEI&y@rQ*iQr?30aO;m)Rb{HkrmqFe=JAM#4qW6pp`+COI7r2RDos)u76Ml}{4 zq%1I1s&E70?`nAZd)GBQC5$uE5FZ!oqWripEOdeV_SH0)QQ{efjeK$7fVZaMdaK*( zg(2F2fBp4(?Rs~!k&I46M!uMTNluvQm!AYA5|}fA*3c0kMn&70&hc=6Y%1xM9sQ(H z@9==t#@^B_ajW#5voliR3vR}vanQA8uL$AqHbLTrR1l2u8Un_)X(x@2Q>h;)b zN5|^H_1+N@k0*-GVzo=`6PH3LTgM3ZNox$)<~HCLBefCoReTE=%LnsOGlFcUVc5cE zQ)VSK)+o@46dOmNkyTe*Oj>47C6%MmmPb%>XX4duHY%5ib$jK1f2x3-0?j5_7|y{Q zPw!n4wC*h!#v*bsbO1U2Oso%h35|0WB;ltugnttL+JC*Bxh00tP|WxW{THwHPy6@B z-OGXx=h%bz0i{;!ZrU&u{-3AtB(w$zUH}THUD<@xm0qCLv`okeC)Ap;qu6PMR^EM1 zLK4R$5U78kVEcT37yJ0wm!se{uq=kxAOh?GnlK+c2}|c`v-UaLIN$g>;0%~lUrcZc z&NPxiB>R>XiAM9k^Kgx&gm4#`|L=bqVZzjpuXOh<>fb7j`(@b(#cLTox&S5O2cI0 zk+m!=9BRZ4F-b~Nm9+wlqUTeqsudRHm7rag7601Xw{VJ zW_&&WaVp1qNF-2Zx75WW@Crl+B{{`|cv$4Mb2^`Y-Op#cT2+&436_rJsCukwX$E#M zt@nb{qpE5hB@DF6e7Zd1Bm0X(#6mHI*rggxyRFSjtYjk!`gi{4Pf=aBn(IvfH4@NA zx%ctv#q7Gjy_($h%V_Z-O;NuV9!I?@1}!q%Xriex57kG_wNo*WWozZgq2VL}D|D5v zKvH9WuKqrZw@ghooxXRM7?Oy`mxu5El8yn>gZ~$YgT7FXmv}Jggp1 z#-qmAtKMFao@!`NaUGQTslW%B+-`3p9$O&MftgROFBMvYRyTkAiK zRAF!0FcAHoUvZUC6C_r3Utw%xjBQ9QK>=;5mML<>1uP|YWIIr__P_5)LLfBJ>JO2V zJimMQ?#>@RrJK~U1WQn-EJT=!2t$J_f3Z&@Dfa$YCaDxC4DE!LG5T(1(##AjE7Na( z!cU$;ObwU9pNcJu`D&!rSwe{F$Kb70-=s>IF{nxr)%#S?BccQbRv0s_VPO=qq(A{a z85AoG#jEw#0$Qo!TV@bCI)@I5kJ)3)Lx?gF9)v*;V@BI$KnaQggG1 zA!v-Fe*fph`{FL9%ZFg*diSHd9z05a8AE4~> zweVRWlCca5W?&k)(GYQ^4X~2s#_kTPcrVqiV>VpB`&gbF)(de=_Fw`GDV>vljhE_c z2A8B8rpGdtYLTU>RK{r(kTON;`u$pwtGf2ih`-MC&OmZ$Or_f|d)RV3; zDQWWdKas8EFU1Wy1mncmcq-T!2N_NqJ$IdH9!>Jcm^9!=J7d*%jX_s;d~KY6eF5sT^4 zICeUUt#g9B>hRM2;!sn@hlutM8WLsa?N)IvC*FN9_uTK^=)Rqonx~c4|2p+17JlJuuk~0cQJgmW zMzh6q?9S}sE}>1`3A%(QNp=902)b2O(qLEfxF0hsLiRSo2u1DL24W()q(w%*`%>Mil(x2I_d@h?_q+dH4(}fG zWlo5QGQd1afcheZM8iCP92Z$CMG?NpESCa=wr>I@hcGZYH`*h_;92pnQe~0Wpu&OS zyvpAByvr$j1Yg)kYPEYL;SnOOt5BBZ95RbRT6uQC zNHmjzBGd)lV*(nKPD!b1Yz`6^O=2^AYb>R%qn8YCA8KUdd}|7Sv{8cYhk3i84QUCb z_f)`|9=H{^WNE3r*zwRU!W zITK*1m9oo=ZO9LGk1-CyFbqX^pCSW8dj*QbfEYS4bnQ}72O>3glsFO<;_kGBB2j@C zTmQc=+q=ELdI0JiM&D^vH5t@`ecO!AFpcejoo7OlX`;w~1Gc9mK1l^&Nidi>^|rE8 ze`LG~QXkbyhdZdLd^!(SE0aizWl|TEV+b5FU6j>NDt=Szl^dMRzu8wWO0`{1ZnCue z^nfRoR&9^kI1v8Mub5k9n?&92sohVx7J6%q@lEZ0*-5b_2Gau@8#HoHYz{(J4b zVS=-iNJznd^UQeWnR)EoeN0zrt3}Wn^Av>`1|-6vz>DBvyN)Q?27S6tDZxa@H^OOx zgCSimF}rQGHeAF1&6%3Pp)Bf(GOXCGn!imYcN0+;PN-bPdKTW zn^PQbM7pW|=KlsnU>3<5&y8~A9wvx$C{THwfPV;oOcopt%Wu83KtAc_F^NM6rH6pQ z=*t*7V8Gf~V z9}Px-U(fa%jb)bJ3Ag;s)BW-Wp7Jufe)c~7b1KmcGplttqHK-CZ?u==Br1qBHnPF4 zx2yqDHK)O@wIN3+p3Hok?Io{fYen7OTqh|L3|>fff(RuXABZd3kWd{lFrQ~SW1CPo zwKkOUx&p))+BOJlR+*2xGIO&%bJ?rM604Yhzw734CE`H98o;!ZVf(j@_@jtN$<@<+ zYewdQc1G?Jx^%2W0k6Q4R|ZE*T(Kc(74jG~(hx`B{1J1Gh0|2PG!Hy;UCmEDV#Vk) z5c~{H*M<{tIRr#Rjy+jnl_g}c;Np^Bz=C29gbH~^$6Q?7GWE>rx_i!cd-nFN(pz_b zdr*od0qAr(W+JPCYW1i9hhq`Tc|;_{Gy?OLRS!{1G=F>7U1H%W4dmXAR^MTb+LUOT zk&?hBMzVahExwhVb6n_l8V9ne{3_Hliv>>f=G|Ssw|IAY`N85dxV*9W-JjWtYT0WH zmmRHOu_7*|i98mZ{hXE)>bh5_v#v9LVHU&+aW7}ksgFFvV#7#{=L2yS=T;R=&srW( zF%!M}O5R=dXX;P=0KytD)0_+$2NL^)`1;{7~hSNk&q0$`B@X)|KvwnvG(Q-1ZymRR&+7X zl+3erS=D?l=tD-vad}7iPcL3xRDQeN#SHQcYBB*V?~ZKfRd$*-gU_-vlX#6^*X4=b z${}{DaP7ALrGImC^H*K4RVH;V&fV9Z=H6h#H3U+_^Xwb8Aod#UnQhwo>|M(6Vw>Vu zf*WS67(+F=g6W#o_j616>{`En0Hsx1PunmQe$THs4@GgM0DCTNCp3yy3DIp_qzNWu z5~uZ+*pcl3)ztsK|_ocx!Gt8zYP+OG6}-iFSYjukJA zRQIPvk@1*X{rVJ_dt3UE9&q`xe z-CxUboKda8=I?t0*o$Ez(ct&N?*Q;C6n~_j#cpYT#*+%biBeK&(m&}6YGy96xknon4+@L=avt7X7a3hv5;i!XwUV!{Z38kK4#UapJPd3mLoQM|KJa2kwp0hh z_^w|$HkFS!)z(x(L_7&eP0pHrJGfOtGV1VjwTrFn zg~g>|WOE)tkK26&opAv@he+2uPl4EGh0$o#38mi>+a9QYJD4-m8tMf55s97<7l^yw z&hmb1aIDt#SX2`o?OSej2mQvdagvAQq!M~$#3vbbVvXRgo&+!&Lo2e*oIpZb0zVj> zq`O2d!I7h{X5P9kww~*2#Z0LLe0Zx}V)wc-(s`*HG`Bv#(9`Qp9|rIwd9nrHI*Gj0 z#;eze;4CkHnLd~56*OJ&lLiQn%=U+z>}ZNsy()99$Z>wTjSGg{Q>Ezg;TKzK(sEnb z^kl*}r)$xbd0Rv6D1pPXRe-t&UZuWwL;fwZreQIYb2kUcYZ9!2RqzL;RoiabKoEWR zSIk2quO(>uObAuf5EUtcwz)`EsF1P8u#LTI?XH`Dpo)L*tk*V%kc6gwfW6F|nK^UW z{-=DId!7h0)Oi>oj6{Nw!FYPT$`UD7)6X)?r9fe5CA3V@o@AkjPd%^DhbrVL8ZJd^ zITCqcPX7zyx>zhw+m${T=mK@5cy6Scp67YWqtNQtr?fm6=?kVgsurL-I9R_V!2O=9 zMUl~e-FtLL!k|hhb?;H0{9DMA!dO-J*D@TXq1JH4wW(?Y@X4TvHB@`=j|cSSN}AFJ zB!!5qX^5mq_@YqAf>Oi@+V99Lx$X`$nlV);^HYNNgYw`46E3*bXZHdhVT(lOH`VNS zwy%1<$=93FWPEn%8i@55L`xz7H=dM=Nlr2h2Q~4m@R~`b$gE0jVI11H$v^ zX0JuPl;Z3Q&qRjzDTuztVq4=0y@ zzGq|UbCJlwaV7V`GJo7ZDG#@p(-5X<1K0DYGImrG3TD>U_q-pnfG8BCswKo+=V`bC zLsn}X%OQlIbBuVxqfH6CZU78Tn*CO)+*l*_+GfaBHWwi2OjSwKFkeBTEumJjZ%q)B zgKpQ|_fP5*kS;N=`%~+kQ5iITjf}5%Z(*>qTz(-zLk zQr{iIta73y*&S3RjjV#~)FX!YX}txE+ULIRP&gY@o|R%d{Y#Q|KFkb_yO%_N?fkHE zo1wHLxNmM*1=q1Jpt?(H!cCkCO`!yQ+@imjQ&{b3TfUvhcHZHJ{k@MX9O=y_fFAso zJl@F%Pk_~aW`#DDdL7(dm=>rnrP_)zXaSU|ikm!EiQWH3vlkNAVxxp>xlOakZJZ}; z8Y&g8W)Ha1tYx;u9M>Ldg51G>bS!Vq)^9Q3GS-T}uAOJWA6(II#nx0j#d7I}&lWf9 zszTP@U)>sOZ`;W6yMM*nkdIW)ank!#D6!qtRuBPhY*>kbgXt3}awRQFlfo`($JhG5 z-|UmSQ{?+IQDIOc7VmzXX<*slOG$sgm4q#xW(+B# z-uw3+f@fXL!9`F4TT3+kc78sdPSFW^_r5**>Eq<`+szne|LU2Ye7XI2Ik|w@U(_ru zN|RaSlhpWm?L0<(k?#kE{X9^(?DL2SFUGm~3?VAT-7PcUO&NtrdL zz?fr(8IXH--9#p0!30;}4`EIlh7Evh^b%q%hY39Zp^DAr1=;?F>u z+lksY8j0WU%{*CupfI}rd_B7z-%KZ0UuIxDrzhxd@8Cf{{e;3ntV1yX33L2}q8q%( zu)u!KfrS#saQNmFh%u3yg(h?>ti$2ZqKrQjmle%XjpOd!{hL!jG67?b^91;TpT?y4 z^HQ@$fuaUM*R%kcmCuT%BNSNX9t>+R8nY75$q9gab1Hv-of%=-&VoiL2P=^vUba(- zDDc^Sg|w0-`5#LoiO-Ma3q0Sm^Jnw z5V#GdD5f-jex9)vS(KrM*2ZJ;%W(Nw1m7?MQ8jUhg}t!d^)D_v2E|9Y+PCecEzW%{ z&X#-(Kq0`UR9sL>Hb$?;cDkY3uH9Ce>-cA9aN>B8D|ldi3$m;z)+XaOQ~yEKj`)ko}`@O?m*iIR=?CqIuahrsH(^)T~Sv@thWsESK&3<_1WK|sW zXvR$pob{cus|hl1)@j}$E}N$2(eKCPvfhBIqh$m526(pC`ysZ}*;Z-2Y{Z^T9&*Fi;OMdtP7jh(gD-;GKj z!*}pMo%T*2coYt@A$Fq(mbl-(!Prn-*r>+&QU)D`BT3Lk<-9+9$DvR}lA$01=L|J^U19;;@0nX}!vF)r$yg;yY`RLRvaI0K;$R3-s~n zR4(P1*NEpv+0EZLbPrdJtFB5iG;Ie`aXxoxbm?xe}k{z>l=$gV{spVV9$1u zZ`M+TM7sOko+;n^HM!_`OYni~ok&!mvrT0xz%$+iB4WLSmw@`xV&930q}cEHSiY^_ z0OrZIMh8ZT!dPQ|^H)dq7>q=b@Wpw-E($RO$^#ssFVx(HkH1gUEZ_+97A2yOyo%Gn zxCvfU$hgK9C`YR*OX#d#pUqLEp0wR76D@605T0 zgVUbD7O7aBW&4ao6+%%h)I)^~Y+ngnWTV;7 zK*$o-k-q1q0SxK4LU0=bK}|Dw$~3-p3XWRR9lorG4s= zUb7>0A0C3j$iWol>a8>l&D8I)+V)$mtg~C@%!Tk_d%EEU;qyNbHS;}rNw7e>IsYA8 zh)*LhH;^&HQEwGlAON9zs-Z!)$826pQfIW$)Ce=rUNJwXH&0?z)Vt*Wc^Ba%&?!B<|;2bOP~A%M9F*;*}aF&Z>2Z}z6S>oSui8{oJ+UObjTTK&5c zMJcVfJ=q56?074Kyo*;JhU9#izn}JdCv+`(TI=~d-YpF-dFL5(?rC80X;8jB9jFb% zh-TRQZ;`iiYJN-#5X!PR$=+$gBwOmpAGh8i}74 zr1Cm|6DnOkoO`g-GPtJ7qhm63``$F`wt5Tb7sJPN386+#fnYaizwA%mF|$M5t1qyf z=GmPLAjEYnV8gw|d{eCvNf5tU_C3BpM7>Qs1;c}ecPO*iyIbpZH z+@5t%W3B*ST)_fwzAtG5>zN1go9@2ZmlX?Q@cB_L#LthChi2Di*9f+;4e*1Eu%hmO z5Qxw_7}SV>3g=aN`LpH(WD05JVranaH7P1PZ;p?jd+*jBHc&9{v1Fzr;!x)eZ%h}5 z(9LJ2l_9j$Jmp-bj$x5x9ZZ#k&b9!7~o@{o}4<*16T*6NgM8*5zDxE z-pG)XeAtJQzY7V7SSPW<83CVX^RMP=^k2Xo+!aF84(PzmJc};5zS*^Ehmr^=ZV3mD zigrH!s;}ptcaSm@#|(J*Jql02f50P&B6=WS6C|jp`$xePAq7})A&hhSu0NZ?b{J-m z$=iu0dBuI233leDNZ3K!Bq+u`96tl&9h*Ogxk~-SgNZ-#5oN+vAOZczer{60(I>bP z#zhVgq8wM+jYKkWM$Ol&*`X^>P<&%?udtf(A`FG?e`g6u;_0SM3>Y1RV$vIFpEFL) zGTIgp!I7U4QaR}db^-4;^1s{4-Ze#b-CgW?h37C&io;yyP1Xmw5hTchmZtifTWh3$ zPh75I@076Lm`R#@h63bZ{{7B~7i4I`09|O36%n+UtD`2rd5vGKQM*;@}oOE3UD6wGAk`eFY`M^5N21A-lbg zF8X73>pmDV4CHrof!XZ>`bVA!B+FZt7DNg912UveO=`BC(iN&4)`@<=lmaT1vuFbX z8f%FVdDo)*ke8G^+5_n|V#aDOP;>7ZqC8b&&W{*fTUZ265P1O0{nYHp1)PdRKXdfp z>dct6+nEbAZ5A+N$m;SZr>+fvjs7w>vU4|z)s=s;(+${fH;^cjDHHIK0sDyl%#;*ntq4tURpk;CHuXy^tH!1qFv=xUpd3~B%{^-(j^19) zS;}ZxwvHCYLz(ac`6{9KM9@q;)hyn&jqErX;V|3CNq}eNIS3aoZByCv9$^J4)9v&S z!woNDh_W~o@K;{BB-y{P#6~Fx8Pk;PXCLA`Xw^6l9h zDu@=2ZUJezFR8;u3nVqen%#TZk?vZYHdDU1HG8G5G8laIO9r?*oF_8aMgQJrr z6|&Voj5Xt*ql8y=*9ay;?J#~c*~C|X2=RVw(weRL-i&gRWk`gzN>_!-KDs|fh9$zh-pvLxa9Nji_mUCw+dF_ekZ!@^*1&uqHOpzD}c0s%xGg zWeUj9?xS;HA3Ev*?+9aG&YTd}R4DuHsb8<8$~jh0KD=Scs?C}YAQkgZ-YZ9CC2eO| zUdXiF-q7Q@I?+zvHTs^UBKaMaW1kPh+G1{#d1qtNUYHv3s+A06H)M4L!d_c%T7gN* zib-QkAmZ(O`l(w}mbO?)wgMHD_8|8$RSf8OBCBz4_6n!HX~r`ufy>wK*73~`UGPOK zjRAZ!{2gyc`lp9fIOs*;u8DFG>ig%Em;&vwMju~w8x1P=m_8#cRX^Bm#-wJp(ovHO zD4$Hg(pq)b2gXt-*mqf{yLn;^S~$tcT0-T?r<=sG+Wu5LGP0(bzExa7DcO`YssYf3 z%x|Ws`j*$`?ANA~L;WiJ?~JZ{i8jl3XVZKZwuv{YKuR&O6CpWzP9;qOC1JO0$(VG|#^Y1XF++b^hm9mpPcw<64^k$Lc zw^U&vnb_=KYC6c1BV|%(WSTTyzlQ!BSPMW(=X>SJ zzx00WO{sLUC>+Ni6Dz~`Rq;8E%>Ic=;IjH9*ax~9RUL@!Eu9pjxo(!)5RsvtGhv%Y zMpw$pf?}-g)(!XU+B)wl`LY4Ye4ULqtEvmI3HEylzCY>|jftuBE-<_=t30CiRSCjI zgK!iv32R>xH>nlug)Qk}6zzadF{a~I-GNES8r_8D`S^>qK(TQ8OD5mojv$T&bKi^J zZ;_dT4`BE%f5<)=b3<}W&y&c`?le=61YzTG)BDGe(5uc7Dy)GwqH=RecgDqY`_)>O z&ft8M60AYHA`aDdr!0d*F1zoyHLTpURrfmYZ{QLQTl>AAyV6{P-F<*28P{!N9#u&g zv|568xpW(Mg{o2}XS|AoQ>zHZqgUUj`2{H_)urpRB3Q&_hNt;a29~==sx9-B@mFf( zGy-KtLpXS1Xw{(~PKiw)w?doNBxTaguxO^D?!Px$-iB&jA9X^R@@N%v-dWAmsif}` zj#%XY>uR$aG$+?g6q`l*Z@N{u2zH0JY!H zqCd3KSFWbg^hYpg1OsTs!TLpfY#>IkUs`fVAnNr>l-N$vttvH%sHDLtkYyO_-M#2+ zfcHW2U!jt5{#Q+7VX=M_G*Q%_$Bq&SC-YacG{XFtwCk%1FhII68_cSSQz8ePLW*ZJ z51vb!bpbsLBMo+(K1QU(perJelQ>p=0ngT%2VsZvAnXNrn zPfy+!|6h(Y4#16%tC0YHbu)->z2xrM1O}>3gozcCB3trz@7|IEvw__i@PUmYj}_(I zEDSY+Wg^$mu0v;g^A+LqP`cRZi40JA7-cW)JBc!FJfb8qzcJMALmlrCYqP0zJ>>(J zKw5?43Y60G)DeoO@ox@fYcqjxHs=(~eoGhhid7R!N4%7@p+b=&V(qZuA# zO#%>MUUkyLMBQ%eUNG`Q3NvL0K)e?e2>|6T@ z9_+*5S5(HX2AJJ|8twLu%)+l7wRBL!AN-p7BZP$&`#3DFrEF~W;YY_-sAqQQDT1}B z6l+%%4A_Nt_*B^8o+PM7PyDtdIvhq23B)Cln(jxcNEYTGu`C{1e=FM-8Rq>>g&9;- z`X+hSEBkJ5tNujpee;cyJeQ4`)=&6XPL{05Rqh?aiL<%w3T+&ACx5P{B;<?q+=6Fw9@9c8Nqq{nl2tJI)KByX~f58ndnJR&C@9_Wn8Q>D5@vb`>MuiU~(* zXZV^#@_EDhkDoED9(~rLxB`93zKXG+)dJ|-yH6`cytx~v!oRIITpdE& z7}sX9vK*I4!CP7PVhdj2*g^I(B4FY0DSxTJ$3>9G6fH;;TX_teg|0;@F67*A7?A!C zH`0!sYcib~ep1ar)hTX~Hh_`~;b{tn%Rtv4?ij_Mzc)8=Rii$pN)bWkgs%POFA)M9 zyapDI1(xocs*H?kPJ<$!!$-L{x=Fy$`{8m&}Y2F56E z_dp?0X#S)<15`t}P$18RnD~m6 z#4#t_^lgKgQ5DLISdZCk>Pj_IM;BMg*H8^m42Z-ReI`90UQ2Np2Xu6uVECpQQeT#)&)!isTG6gC!1-D$JjZM$1gXmy89 zFN(ywK5zPNZ>Ifm_d9Ny226q%r9tn`uaqO(RjREn&AKgPta@0?lXw0*fxVLFR&2W8VX< z%wmnNX7>+(skX2p!K6PI^lr?B#nlRJs7{V!_U&iIQbjV&zjh;_k6?u>tbsj4qZ=4? z(>lIi{Gzii#0@5i!#1-55kA)by_PPxo?gu4tBec&9FZi1SN43{N~ys2OfpG}W`_KTFEL;dYwNjM5Fimxp1qPFs@6JB;@yU6yKSYv)z9c zFY69{-Db9rU&sm&4d*{SNFa*T6sFUmLCK9XF})nx`GMLq`4(X)E5-Ucs{6DipGj0j zj814@i^a9yL{2L45_0w9e|ShuGZBS6A#NME0R)4Xk@sQj0KI$R3ArjUPL1K80gL?a za6KC9c1u5&o$Wft1!>ZSI}o}O;gWD^$arX8HDNnuDLT)~aW`u8CBRtPuw(a+_m0=b z#@_sBdYPq;p(@O($;bntaS$sXM_sU#u&;~n0%jK8UBkz~5nT2ZEh#&Y>z_$#o;`bu zuINo`CFMjtfH*scp@fM76;`Ug+eoeED|WrQ=jNajD0Y6}Qguv>gVwZ+st&(9Rb7YAq!EK$$zAUH!TD@+edEaY~&|3wl=P>jf zyS48Y57}Mjt~d0hr*GY7bY!NPxYEPEMCm-Gk_GiVfMHO&nJuAJL=)0SP)8OjmC(KX zlLI0HN*U`6uk$z?8;d|PH)v~HPl?`ozQ8k%Wn{!CjDB4Khw}t@)3PC6TT>qG-ewsj zp>9i3F^@HRI8f^Ht41WTIew!eDcb|~>Ol{J849WFxFH|BEEGkE|BX2qKkLzQuD#e) zx8FSmz|ofBN(YL;EH#hibwEn6(ha)I$vNK{-H?uKZL?hBj9lHq6Ep*6%vM73TQVSV zTvQYS+8pZ!SWWXpQaE+w^w`DOH&AB zi-+udtcVKs8yn1w;>cvAe5--jpr<=0(n0P}fQpmYOrl@TqLkqi4=NgC^Wjtf4vDPX z;JFABvvw(65|wO57c*f*EkT^kjl7V~KiidqLX2ak&}ehu`^e*X({yDMYZYrJ#zwL^ zSy#;{Mtwq9vV?E7o9=9p*pYqA(K{zY##t_4u50PKXu*mNeqD& zgohf1lpZLQO*$F&eBy9nNr)Lyb0ka}fL&?A)GegkI+yFO$z z$BYC|Z^Q0HQFgJd(PXL{uu^3Ckev)}Kj)EytYI?@)0TfV)39R&M0^unsX^!6m%2^z zyQyA6kO3!Z$sV0rh!UAWBsmF3R4wwg1-K_x2GMOTm*wajt-j$s1&u={J!sMLfLmI$ zmPJcsc>SXLfHrB5Oz>iHG8L`)sHmv^a6Ob9{FYM^xHrPfMb-WlkQ*EK3ru*#Y| zzu(P$!?h|ye3HpgLSMFL7HUgX0g=_7Qg%+Wd|}Sc&x(MhUy#!kl}rVU$Yf-hS5X(^ zGE}i6;&`K^ru1jJ z@ky&NN@A)*=kn$1qrL#oRi_;P`<&m>r=yp&aZ2EHI}H;CfP&LOs%Y$q&}h)Y97LJ~8e>z2cTQCI@pl;hnH}=SFM;*o}UboYK3F zoJK4|A;j;pL$f4fT}%;xJbgZWS6>$tRzG~&RdYBTn1i<9d*!pVAvNZxBjAnl3P11m zg74sb{2Ecyv;L{(@x88V2Y_V2Jq%nbaSTVC;JXyU<3cL4%{Qi}l83mS0Sw-d1gzKL zE}#Prj-Il;ujK4<&t@m*;q%6rU_aR!G6YW7mnhInk~GLqiZ3%~q3f8^akDc^wLl-b zz{t^uV0t$2S>JNh+t_SlB!VpOFMBwsNkDPv?j+q9^8>Bc=+P?I0hoP0(b_|-%{_5I z2%f*Paz+VZ81I;Nm_VZuBgT#f8~>pKv~(SMx}%+m;Re}`i3e5f9C~Y3I~?d*gBKZUtCrwJ*{W$%c zu{huF)T%I)PUi(q0GzaX$V6xw-H^cR4277mkf%y;Zq%2nNvLz2?0u=l9z0A^lf#Vq ziar?-RJz(u73!=JmCx*AE`{uUvRn(MlL-n()2&p==IMXwuR_Et*i~Pxi8;3ME<`jVr94zp=AjTo4iEol?Rp6D=kb?H~G{J7>q(wRI5CAtyS=j`25( z-Vb$JXS(+(0kDJBJ_p(!qvs-}a%vs% zzytGzPzMMYBy*hW}_5F)lfk*v#{LxchxpC9bp6CyK~#6 zB@L%%1rWVReF^9~=8zb3b@Dcu>t298nzynw6rzjh3HYPDye{QyOUoL}A|T%D^ZPH` zaq9$2!2Oo3+b%$vdbp*!(Q+1!o60@kF-@jPihic;o;Kkem!d9@G_!0WKgxkZ*y0sg zEyyb}v{zBf2ru~)%xz}N#+5_f-OOcu=;F-G?U_|)a$U)nhsME~#C20buT`nRX5kAP z?z7T;`A89cO>xNPB9*mDcv5|Wj+Li^p+(CUpt3ZM{-c^6_o55a5(0A9!0@38nJZFsODsi#NFJ54Sz7(~11ZRA0T--UP)E5YznS~G75 zu%JbQ!GBj7>fUU05|4-?cNT=IJmwKvG8SaM!i z?O$3&5Zm8P&VzDmdj=I20Qd6Zi6bz1VxEzk1}c5q`SP94#p}{eUs>Cqk>hdmy)Hxc z8Q75`$idPz*aeOK2}0(K_%fyG)Em?M zqZ)vGHcVO2poXaAGYjl*JlL-f85tFocg@%nOq}+=-Wt1p&!1O2O4 zK4Uw$BCfDgTJbqxLt3agVw;{*JoaqIHJ9m;CYiH|2ef9{?t%C8m)*zN&z1LpEhpN4 z4X_qkE~%~-A@1s|79(!_l(P_v4P;}poCILUCTm)U*=oyLE!k}Uv{?L60v3(3!yMs8 zZQG0c$xz5i)5m`?{K^ZJf+tr?Z#I+;r2;7xOJ5P%5tOE#`^VJ(2613uv0y>aMK$M)62ipb&}}`c z`gKrbp?-WsQ1!{7?zr75yE$OjW*+wbWPv3^%m`E1wCnYS{gF@vq%ZM6I=^_}2)JI# z2PI(C%{18z<$~GpxbV3Ea9lWJXt*4=iNR^ioJ2dK*q-p7Xr3?>X4$L`(!*4EWuDn9 z4r}5aSUNK2T2kScn3{l7k9?-M7Q{nZ6UQN+XP?4##H?KV1K}uLX!4= zS%rihZczcCDq#b_yTbt5Rwn?#!Tt5e(BUBMX@KdP>45JGX@*~Xlrl}mb)-9%eg!5~ zZkqu5AUt+cYj3Vr_fLWSJ>M%=*q!s*x{j0RSexfw7wvq4#r1fTdo#f-5?haBiBNjhMvjwSGcn$$`6j7{X>^Jn6rzg7`EM1)O2BVVGmnP~O}_ZVK?S7A z);jvGt9e_8Hp_0)z%;zV-uUU*{5?v1!$3%!F+*DPAEw^sADcg}ZD^&jDdF$bm{OsM zLA$>>cW3M3J?XPyNs(`ZY1}ArpH7n=Dlle3=|>xnd%$^Cxsg%-0n_^HPalbzGM~>dFP}|5XYk;CxG*6a#VPiJ@N! z7KVLYDYwxKm zqIS)=*37*<->GCZHbo#ON>mHG?yjuUx2ubUWBA}e<<{0raZ^7HVjh|w;&V*ANXhFp zO?Dm{7vfe+H)TKg6 z-JcW_x*R8#r(@MRw7W?c=RS7Ne^34}H7k$8#({X|PJ1QNUNo)!{AaCtw+nw z3tm%Sr*z)tuCCkH75X5rEp90W{LH+_F?j__0fAhY@}z?p!cu4XHs~EC@+Z&s%(yYb zNm^-W3(ypRw5p|Ut&}e>nid=FlD;k3E_Pq1kY>er*G#;^wkne?>s>odk*=MaQ-=7& zIs%`p<*V<)4T1%_XS||PC!Fk|6{EAyL#xBm4$H|Ft&3`bZ8dCVYu%6y<(;a+Vri7K z*qoxPvAFDH0mVCVnPjQdl*MI?s*mIwF^+iNrDqM2mEc@#GeL%0&Bp8zj56Vx?6l4{ z$%+RBsyuC2?otp#Wi{;fyCYsCy(v*(t38)7ZqHD%}%CoVM3hrhp7C%($A zWqaz*70YjsSeDlRE&C?x`~5~js%G9vMSHy}8vDjiGgyS?2L?!{ zRrvXw19ee_44rxFQkGk6LhI5g_ikH8d3AA5C8W`x{zTmc>($GbO(xofK9EwTEK?=y=EuyWdsV{@W%LaTH= zi^3KP?Y26m?F^^>>)s?jX)jzlIWG0PoB zlZE=7&W;>9`fP`}5mMM{GPbk&9F|OohMDNHY4rPDAL*mP*qZfD*sNnsPEs2({0M%%^|l zUy!e0uq7FFRMM1swoZ)$1ZFMU6c1f1%}L_pzuC?%qeozU(njUghQ=wvv+5TK3X~DF z)CCPyJb!7qS|sCk85V_8QAv_Y)HO@fl}|NJ#Fu!%N=4;hv?x2IQM4(D4@3jo6AiR} zFW>AEa|%Cp0*>l}=d39D2hJ2-qh7t1?9u-~Hql8-2o3KG{ibsnuFICxA`Q zir0l9_H}o?v4QAa?V3%I6c(=wFv=FKH@@3XHA$F35Nifoxsqr$FxD(2jYMbo5(?|| zwxn=YU%gKJJFKRug0x8g;GzAt2*elN06p}dG|Ja1Wb6q?`DBi&NrR!4xGYqhJy933 z;Cl;oVZ+Jc`pWM4$>tX>#sYf6#(2iuZ)iU~r$aVg>MT)KxZDH1cyRwg@kGEQ$vq#H zv=r$+!2r0O#`&q*T!q*=ox3>nB` zX<2%zSSWcq>M2?I7)M$vs!3UDI;obUJ>^(&2R1}mnkvLGLig2p4}(=i1OP?|HW;lF zkkA)Utq@T{W`nL}hGzs9^aNML$OyDhMMz18SOt~tvTlp+ubAiot02Lj_>Fs1kV_#0 zB1^~x=_C;4`Y!9Q@D2w|Sf;BI6J@(9 z<8GIp=w)k$GdRm~=qC*sU9-w9b|p_Z#G$@;vZ%kHnOaXJ9O|n#50CX^aqcbDO+()L zD+Hb$?>;hdb6QOB6B&p%e-`Mq+ks{|hDWsq>7gqw!Ok-QUQlNwmW?*+9ony>$1HK} zLJ_ki?oX?X1E2ya>Uuwy)2P138XFauWC{KRzhIWFnn~2O)rwl}ON48kRjQ$Wf(M%C zBfooB$>R}l|IQ?tyl*-CT!np(`8LprlT5h%h8{)2Hx1n|?)0m5%Sc>)3rkdIv733r zdenE&jTSEfSlWJ=HE$0G53iimF~o+TN%KoaahuHEL~a%}3aC*n#{bJao=%7JgEEox z0W;!%=1JP(T z!wAPyGtwDxry3;R?bz0oWe`X6W8N|AOm9+koxg!mGDnH*K7D)}&9PSj$=K3imNosPb1ZD3$c>4T)L;GdH}* zq@dL(!>)cKq%hxJ8MNV)8s2i2PFuX1dm^#})ZtHu(09h6W0*tqXKP=?3)~5&HVi9h zs(>G!pnYl4Y#OubC7dbnzrnN1(-+vW@a}E$%>QQ8n;%#%^DMqm{J7c#qKs5Z?yueg z4#XxbDpuFZ+9r9u@0}=j6T8@`{sSU#2yP@MzT0QMBzGrR#_!_w%Eb_-ty~3$T(O%4 zpjaZLw6ru)RyISlviAQT%6w1L)|Ib*KWB?$t>T#7TtZWuz{ z$?G-mF57h;5Nxfag|i^PMjM-4e08IvjQ-18qof2r%;9-+11Ra#(eMm8Pi#qWq;^~_ z($R$@fG6{$P(F6p`vP2M2f$nz!HF*fNDq)HbGL?tDBryij%Qm#%CxgIj5iKoF2Oed zGQ$C>I9L*}P+Xh+!sjT3{{zC54{n4&paoa5|_bAlaEM_Ez>q=?>~jPwkKqXg9f0$@$C%fZ+QG zL+*1rW^dsSc$-&e>~akO9HwCVxv@dmAMgt;=3-|p489#VbtQ3IG6F&H36u^?-IfL$ zb^>#pzmn0(aS!{aht2nyMR)xtG8Ov~nM%C;2d%zh0rAlOS&dA z-@PP{jGvPrGCI))qPJHb2wR^XA7+Zvk@6Dt-l||xNTI|dnkLAx7|)jU=)0cu&87|N zyO@XDyrYZqWvcM|DT%2Ojsdeal&q7s*-h!mYVtUIJi4<;D=SnpXCr z)T_$O+vbuRR9+=>tc%BYna=C+Od_Q;Wt$|xZwor4H^i-?+(W5ggZ70%Ikk=YCW?Ay&fRE%rbqAqci#f~TNF`~XnW&zyCYv}9OJeJl=nCsv4 z5mXnN=T#2dXoupedW{RWyl2Vt#3;*>aCVN@9$N|)*;)rBZh5=hs4CAtI-yYNXl+>4 z-7&v8YPWJX1`Gke$PGSLnJZcXsv2gLp?6p7c4+&uD0OUcg16%(sh}nZprK-AL-K`WRINdBW}F;4{t}(FkzBl^~<5TX>z=JoXJIN z2l_|80@fg?=cTJ&;Q~R{{0O?!p$bi5(({evV6(1vodhi1NgQVJA+Wi`$C$x z-elx|V1z*|hK4<^hXY}_>5xovJKGtF=^{!Hbg!t7!_99ei;_wfp7dhnbZpexb?RxS zj;%!1+IRpG8q=3>UJe(uJ;MgBUBQsh__JXxec)02`-8vyVGPISV1TP;;a>0_2$uW6 zYFcz5V#4U!qGx8c` z`($Y|y`yM;Hyax_xLJdA{WPKf6YhqM%!Ob@+wiOdU4S0|V8^w(k0RKeZ#`W=yS@+K zuoK|OpI_HKUhDDgpPf{fWGQQTQ#4~e<>_4G%>Wi->pXVTyD#MwqcTGqpr%ER4fK?B z?6UwrcK>4y+{cO(&DBH8wJZ>MlNdwOY;#Z29Z&^OiPom_Z@=UCW<3fbqP!`0o(!HI zFYgnQjWz^E@3*JVsS8W?AhD(<^WaG_me!RQ(u!z-HNYMFPgtI@Cb|XMgzv6)^!TWthT&`N#h7lK&SfW=zjrw&2(w zs9aL>-H*dZ1V>h)n?v|}x&LDdKp-T#tAf73k{k=^r)a>aj%WaN+7_Mw&p`N-g#dBM zsif91V`Ic^nfX0k`bme{V$zi1D}BL9(;+UcTC1!#`x;TXaw0RB(TCUlSRK0mob$4cOc6 zR5ro-lP%!ciq%Ux@i2ym=zfvR3IX4hsCCxy)JRp8h`82;(~++9lNRaw`c!PF#$>F4 zR#2iILCD?Y^=KOZkn?Qlp$pn98xcE6r$bcx^NppWZ1fnxmbKLyk0kR*Y^WwfgwcEV zO8i9CtAPLm5dSfSXi7bRtx}`-|3HQQ|Aq?9Y-iQ~phCHz;(wqbLqP{wNmbZH!?j+^ zi5iJu=s&11{SPW$Wamp3uB&jA?orc`*}xhVol?kADeT6fK%b@k#0tuH7sVWcx6U*B zK!j(t0yI5x?-pP2llo?~bbfXQ)E}YD4gk8S6^KOgIlUTkjR*lmn^u$O!UzjgyO6k$ zl}b?+x#$!?Ky@asT)a0Jkw=@m?oQ9kTjt;+f`n11S@+aMY5sukh(v_lBnn;4j&tKX?*I%l2EcZL zM^MoIMxlcOlKHF$(`Z>PF{JxO*929beWHW#Kv+6EG9J$mCf0k0=R_teBec)yY_A`v z6!Nd4sn@n0&=#;0*9=YNNZT3P3*+I2O7x*z+vl4^ltttiGO4d#CtQN07wp+_8d04O zsQdPCQ2&zMXVo0#^!^vcG7dv}2IziRoFt-C9v-VWZ%}*?WE4xhIZEAL;7AOv_$4EY zBAPPk3=ov9-uJiBiPwRt^n0@Ab&?wV%gPCiqAt|@!0;q_71)gtC z!bBh61VLMFw66r-mI)st^)MWAsiiltCFATh@kC>L9 zoH4MvI)?7qtC$S?+u;yj0f?v)JoD4VI5Tz<@M>H~W2NE+Z(9&bFZ?nhOQS`KH+W?cZii>>GPN;rAPt%kvTe=B>6 zW+s!4^RF%W==B0qJgUXSiZ0jW_|edCEog`V+QFY=~DW%&#`Yo*Td)io>N$7uL&Y|tN0Ily_v zYKGo^Y(&$}zBkv3lkGkw73x{{0)*Q@0hpz}%!rRd?7vtT2zh5OfRuuN+8UrIPJsh+ zkOt}~NMDAT9ND7NE-)#qux2aHz{V;Ur`2lpU#+wcPq`2A0LVzCR4n`Rik;!%BDEX3 zLc;3%JdOZ29p4@phl)7}Mik;WO{zQPbWHDI6Na$2%A=(P5P4Dan$pNSmhGbEK@$mG z{E_P7RKuqVqxGGP+=t6xHP8Am?f@n5isUAnJ0f4>8#J?B0yBxVQgQ7PhT;V#R}8P8 z-D3ZPL6y%8@JV;l7t|YuJA~J;`G`f!m+j2oVLN~?Pzc(sCoOk5^zUDW7rn4fc?9`M zmCk*H{JLGt+u)FgaRqDc^yWK$S@I|ed!OrD6?k9e(Q;UH_C((pf#EV_8BJdqTer+e z_lDM5lD6Mg%Rpn=&hwK3%qCgpCCZiP-(xv^r34fkza}|ky~1{>yLxub z$pU(| zJ$)7?_FmrC#~4|yl+KNv@W(lUfq>rn2%9C0U(N70#3k>#-M*yT^(WdgehgKNed5Ja zyRT&(V2th$X8n5T<-dO+#fZ(SjsN5lpcyuwXX zQ&QS*gvZ%FuC_P>y?zCT!k2=;^THl{}2j07YFY|zpuid(x zX9PgouWf)r-*FwVyHF;%!R1-Z*=y#QorFdhXxQFy!& zo-RH<0Z6cYU95U5?GWRiTYKbV@Fb$txFk*&UYYI1M-yD+C-)ao#=wBB1hD?DIAj1~ zS3un8D!b904(@2HV1s^CsuCSMDCr&G4<+mQzFwpCAa+|=QM?vSvcrxBp-@@%XhE_);in(#N8c2 z<>WbxMC!n0B;uKxVeUvEP)cTBh+|SGWtfVgG?(4|k96I6T8BaesdUymvgdT39?(+EzMywl>wBLwpGE9@ADJ`)h3pa&dL_ z*Vr>I+3~$km8HhCe9g9?ow92yd}{y3T(h|9iSq%E^YilX3A9Vs?4pv*Vzu)61rlf< zO-{B((&D8NP-9QE$)9kq;>Vtu%XCDeYiMX#7h4x=PifzPYa)k1&6b}4l-4c#oVz(h zlY>a3H`;Smfws3oaUXM%(^k?IFS@3_fJr&DA#Ix2AD1K-V@GS}}s8O9U|5 zI>;P^nOw04loaeskl<1S;8S>SbqV4a@y+RmK@-qS96CgO{}*fT;N(g0Zhf|G+qP{^ z+qP|MzHLt1wr$(CZQI?`-8;X#8}V*zY{a`a_WlEz^<-64Rp$Ae^K50};%#uj1=ZPG zIav~MimXDPbLxtp5>SlbBtc5a?xkctk$zBuI=LMcJcgvb`wW%Bz_ym z3cK;AcbfrYSzk-1vj|X>!V(4rwf;pKj+i5&iHy8?q?o@zfnyD}S{qS{39##Cj&TUe zkG}mCjy`WizZnxGgA=MP?zdd0AR`32-wB;gC4x#x=ES6guq5K-Uqk!>lfF*zQY4Az z>noVgfHVc$K|Hz2$)YkZO|)U^m@6HCeo7Wy-jZMrWIr5IP58Lu;ZW6 zzB2|JzVHMgd@!2OP@5kO@d09IDk>@bi!V)Pgv}#7TdWfe?5n*A63~tQ#8qZLeJu)k z4rmJA_`+9lcxYlb3og(Y(X_p9{P=c%jPh=6f9UF6@&t?mcxelR&{4A4ah|_QqoT9@ zpcYvG3YY>F(dmnjG^|0|WQjz35<|O7Hi!0|yF~WQ@_)60f(_ncszm3z;S>Zf3fWK`fTHb?o$b7K{}=r}j@ZAf zNk9Epxo#h`;a`o!UQy{Ls5c^wKhPB6W}M@-zL8ZJ2UPb!Jyo!`_Iija23<0em_uny zan0c!n|gh|QHDJVwkPfq-l>8HM@i*I@*-Pw;&cJF{4TUNEV#8NhL&(bxn=bUS>jLw zM%jUCp^b9~0Cq%reT)3z%XE>b)s49WH!Dje+^lE`TS$2b2I|9&zHHycjsXfa)yj1G zP0Fs!xMITCinp{tpgVn_wYa6Q5PGJc(K(n9boM)sj( z5IhJ+0wshXHWPchJuy0R6rmkX%@3w(;VO?yIE3d~(GDz96V03Vt32v6Di2_!t_lMas;4tc$NuA&?R#iASB ztkc!zDH^> zUG`Z%0Oz{MujL|4c+xO|w(nx{oHjP)2TZ7m1OpJaG_2__?8bCN)UqYhjjZViw2RyA zk|M6|7aOTval9Zf7*lD;FO>1LRE?o?e;(q(4o=cUfLKpWV0fn10HQa(qCgJE3^$gw0NWc` z_w$7k7BDuUq&SQ~>?smCBVb0Mwj^p=3gXi3?a1t?s0Flqeh!FFr9|0-AsL>(mEQ6y zAy|X$85}ncL5^3R|46X9F8=~)u#$l1?`~ZO5)ewcS+i}&Uu0-9N^nQS^bi|IfzC)- zfK_bKZ>y6e6<=J4+ql@>{2fMo#(w+^jqw*o$bC7M(8ZBi?Oz9sIFw3o<7klGkb=2wc81JnhW`3@QRAW78$@6Uo$G5hxM% z;~YG$qjCI{nFfd#l2v9h`@)637J4AOIzixRJkwa<#O`HQv;D3lJS}^*A%t^$L5Mm7 zRYI+8!K?x&32f~mo$OKL+ieQ|fY|USC=9hA**9HOLF{Aw)wUoft$*3mmq!^s-x*%YKVJGXN*=?+Tf!CE@4b#_0ISsX5(aSv zR=nWRv&IWn|J=HPG)K3DsWyUqyWT6R&eu(6nas5dGpd>OG<2>6oR(GNS|GbWY1F+C5ZMb|iZ>&zFO1ca#&xqWSi89G?`vC?aj{%ogmtpe@` zNtcQ|6Uh;j?UOtlX=;J%m623joy$yIgAAuduL_)bFm1yAiSRaV0CoA)M(RqcRx}I# z7DJ>cPF_Eo1q_zD|Kab6A@UWbOWcG9Ns1vC|jsXXi*HxV4t@docC7 zQjot~s_CgXiK2=qRW++?v{d}vvVL4|e710uNM8iWJ|}flht6BRJ&%K&p5d%Dp!ErV zp_2+^Ckb(582@TE;STAXaH*UrVDC&bTvf%zh1!pE+50vAff#&|3+TCj=9 z8p*29Tv(|o$Bz;cPN(st+OJ$X3Xaaf6>{L-JV#yqmy7$e#^t^&jZsc(7M)N#;^Cja zgvvf^Xf1Sg7my|V$BG+}tSV`g%AhR!d(XtKT3vt^OlUSwVBi=boV& zyBOPQ5;pwuXlVgLeZn@G)o?`x@AyuFGI45z5vX)e8LSWfn zLma+MI$@M}sHnWX%$@HZ)m~&_+gd?1%#~ssY9n<(2T-Q5zcSe-mG9`v3-N7=m`sjO z)K~QNaFd7UjL?B|r0{$W;gU7MN@~!VdGO^5Wf0Gi`7OlaAoH z^atmQs_=ToqOfK&?AXqmL%e=L{+6h*DjK**sF_A+`P{c?E3EQHKqU{gr>YFQx=*d; zGU=W@5>TlXU~1gTkZ2=RfRM*6GFfI?sYSjlYLJu(*)AB6e@w?P{vz95SzK0Z93kCc zbv{g5$RyKac(MK@iPFr=sRe*qWxFgGiun?&Josrh`l3p4n+aX^&B|NDI-fwn`Y40S zI+e32=_-ax6m87YdVON%& z|N1e6%KPg;3S}{8<)0tm%X|Tpw;LGDFO4?u@nuoh`8UTI9&20u=(C*^0kbCyYT4rbx4mA!y%kBI$RiEypt3DrIB} zwU_W9A#AGE-4cx%HD*Vu{?&B^ADB)B2HIc2jxd_+20zf{hI!K7RL6T5c+nc`x$E=j|ogaO-gxjS(Gr@oA?2f@_T zn|3|lzYYl7x+?h%}LNLD-5C1nXWr}jnA29=5p$UO5)s+iWOu*)r5(f9Y z_=^C*sk&$+b*3dy^Hi^*PKL>&b-Q$0r>dPkWA*q%E`{mZK3w9E8H8@^qRr53p~ZgT z`8*N9Jl!=eXon#v2m9Pepku;-Aj|q$w$mv(18`Nnku-oA>ion)`Sq(7_+;zbo3;H?GX@bnly`uvY#+Y%KXUa# z{E`92n^)U+$-iAlxN&B1suW-E_n3i6E5%o|87y5`19_L)!9&ZfJ5F@aP)yq5&cSEr zqXes%<|u<RQ4i7PeA(tDS|JNV!86c*8#wonQRkO{Q zQ)Lt6+Q?pXd%7xNx_>r}?-jQq*)J%@)bjQ47Py>Vqqj%(YXCTA1!Wf>Mbjo}xQWuM zho?OoOQBTs?`Z_C@rq9ie|tKCTvC4irAgPC&bv3-A};wyr`D6hCAH64b!|+os|A_? zelJhko13`rnv^8A6#}7r`jdU+&{!BHqQW<3YfCCL7(uIH9JD%QYn3Z>6E5JNGvPln z?)QyBKl%tAXuzqfFfQ3+q#b-6kRNk``==r%2)z=9QpOS$O$75ldx`0KgKa#C)zG)0 zW>@EF4)0C`(ZjV_=3Q&qlgn4M2JZ*Ea-2cA{gjcF}NGZK@{K02sRwyUJ~!n59y`CyQE+B zRk`zE_kqknqPQhm_y^0&^L2wZPxuGSp{S^Hr27dBQqqpHro1^R6M&V*^vd-i5evD_ zOnI5&_;9LW7E0^HNfuU=M?T^QrfH&sqj)8G!@XhInKxLh0zQhaCW1^+7suMiP8ZEj zrz{+Ox`-lVEX8J)nVB5*pam3*yK7xjeao0cD`=roGgu%AS1bRGPV~mHI47nrax{-~^2$KPtXTH>)I?7XPZx!DL zh5GAVrFpi^udDGOgjL8$26*5MVskWe^}~0?!uW+7lGV*MYi{!S)|zBn1%f9+CfczY z@a;*hutTLWW+k!>vW+Sf zlN~{`CFbR z>CwfU<x&ohCA)^^-sKa$pA{r+Z8r8XLmMW3-*Lk!riHK zq~>W^R|N2!aV>pGk)CrShYhQkw*(6_T#sil7{i&zK*+*iXQe$f*${Ow?{%A7TJaX9 zXS`$)Zhs&)YHD(~B#UYZ5z#Gnqrl3 zbI7hP+s_vu$@1yJg8yY#VpDM>g`AVBn%>78Ap@vLHWP1jZv4C?r+_{#-uM(8?EP-* zg%(sO<@89lC+9`W&cDC&&P+>r?~MJ)n4+D>=DB71as52sj^61)$<9x;c_jS*L9YV- zTlnh#JiQ9|Z{e%|^Ykj<&(o`b{}#UbKTod${#*F!|C3(jpZpKKiUhIul{4^a znIF`&XFIC1@CFHba>E449)8QfP($cDn>sB)mY=5`eg+{G9uqODo*;dwA5|`tCFmt= z2Uu2m>lRP5NQP9x7|p2hxpYM32$O9-yzROW{EIOs51DNjJ8k{1Uuy8JcO?H?oIfl- z~ObX^Gsy9w!T zY|XQ4lEMzS!Y{hq=wVmcKk_vQIH0%+G63QxHR}G4V+%-6!y<|R-^rnoeNScvP1oV8 zR71Hx_4MZlGD7J{)B4OJ=x833k2DU|^K<44LG$K?{NyoOk5zo3?uyAUQrC45qHxJC z@B{o?D@1}FXM)QsFEPlkBd4kiN!8W+G&h9|lkah!66d7{GewzUld;fCKqbcIjQ~yA z0EvWv0s@m$i~aLmdETSd1OCrU!g!Y=>XGGRvL0YRrOE%)-*riwvWEgGd(K^cp3fF1 z<6{{cN;FxiNmLt5-poAwqfaZp#V7qr!pxhoj?XW!zQH2VC2Y|yv=%3}Ur)2sDS>kl zX|bp&`m0BiHoC@zAoIYCan9QeU^$#Nx5XVkt#P|P27LxW$1^>qpplpJQ-zyX-u<}G-XW!RP=g`$)F~IPk$LNib6RFQ=lKfKEatzE zW^fO`ke|W;gyN8@>x*Ho*&)O60%hWor`<$qnN?mc`;LnWu>`CVYSJm?ID8pjcm_R* zF1Hm1q}bRE!$CZ}TOB~C_q8+e1buqQsbm+6JaQekX+6@9teM?jq2+#XBiGI`RvzP* zC8SwYHw*|f62zxpB>gkCd>v9qC7SKx&=w}6<2s!=B18D?je=9O$_!IycZ@WdKw$;q zo<2#Sk>lz=Qu_pf+y*(kT_r0+-~FD$z6HPg4Bl;~{y*~u@Bhvvxy>gcfzjA3fo_k0XvjR~mlq$#$A*AP6COT^AgW?^CpixtjVL5!2;1DFKYHY&!1)+|5b`T|R^#7oZ@ zW)_B>ymXG?%CxvVF`6e~=GnqpTVL+#_loFA50nuYc}*}IBTJXR z(H#fYW|gJuZWl9^OCc{4!jKjOp|v^QY2ECEkXe_wlh#PA%K#j9hU=)|Xwi7KC7fb1 zz{~_`z9A5265)J9MHi+=rPgjiH4Du5;<0H(0HR&1TW$#Bl$?$QgJF;;a!qRtbl$eu zL&to-+O4-MC{w^l40E&VU0|MNMw9LL@C%G2nN~lZem@C=$;b^jMLE*GqBj{mtZb4b z#HYh9#HM^-nH3eeNf4eD@I zF-!s5hV;IM&vcgnzX|OJwZ{Z=-DBIdUqLW{DnAxHYgDT~a*#u|G1<@{cH2k(%02Cl z;K9eCjv(}Mb(o2>5mhSIM;s2Xpp%K~{b7>rOu;7d#L)1<7|Cf=0 zb)SH+hq4FK(0Cv$mtspqPL=1cDjp!CzALAX$j6_<04N2qw4B>yJNSxoeEl5mm}jAf z{%SK$(juRE?1X_Zk1>pwpxF!r{9JSPp%yq?x6j)o3A=8F`e`@p?sOFc3HtLJ>^To+ z4d+hhVQ`R};r7vn@Uh9@sE|lT1P*$vmb_fV4stZd7%fO|Pe!0dCaYCF%I?J8r_|Tj zlp@k?AQ7;;I^cso!&bPtM|OSta0{L+gkQvWX~XpS@R>n2H^342*AGaZ8JdQwCh2H(VssOpmXeS*+~gJ&6N) zR+hmipfH~hs-{PF+cZpr}vr@Gwv7T3VQBdL@Qd=ymBp>Oqwd@ z5Y2Xf)p9|fZOYxz0%pa-qNL4R1Y-s+bhtyt`+atAW#p0J&L`}m(#fjTSd{LR;#o^z zMiSX1b7>+Gc~W43U}uI)VbwC$&5=`O>NifT5@%vj^5BMei?_+O>dgb?_D7irIlzT$vI1B$9{L-a_2t|i5SPEYWH9bo%ssvrpick@(qFY6WMpJz3~i~SuPbkF(Yx)Cf+kgF zI&j5?FwmNvfsT;JKSIyng9yYT`r|G(SNWdiFKE(%sQ(ktm2*dJ-sp8oHdt7WaXwl< z#ceDJ{f0&&K~%$u8U@OI&9$)qh|X)|=i#ct=fK5aH_wp;Af_6U_h#glcf?WK1(SI( z>M-7EfNG%U)mWZDcowPMb=V8Rotki4{^-l>=4+KVn?4cuJ6`lSWUiIY4Nz z(BN+f&m_xXz3?`Oe>wgGF}ks+qj!m7)tvR6p6YU7`PmT3f{kDQs9%pEf_hP>P!??x zkL+y9Cw-&}ND^P*(-+EH14D}sQ3p1#AK85 z0*i{#O6&~ncUc5NGtEd;*K+h;(C_7n!*=34%wvmH0I?*jte}y%(tz2AA^5Bk!J?RF zoEA=Vw^JvjL$pehKVkv5`fhi3ebEG+*v^v-w*`uh{uA3yJY%QfEiD zQQ-h9V5-A!1$2*Z^p{quu`NqFR6;PT$B~>9T`pQ`byDhTKz2isx#2oghL#nJr8sKp_y_8&T5WMy742Fcvxo!SLB zmh<7V5mAYuvhj?Ho_g2nJSdRWz4FqfcoJhsP_OhcEs9z`jKFJ^r)hIq2^`^tU6aS1u)+dTtkId$6nKaXNZD;KXuf(7kRiPWy$`cK>kvR?8=V%c0bvm6K zfTjTQwQ56AM{%iyANQIG)9hDebe!EY_^QkEOek}E)l~Xr3ymPH?9MY6N%IWOs0Mm8 zt~YyTWgUnQ^=uE>Qf04NF}~z9zKULvGp8EzKGQHCaw?p&;4e$RZ^87e~ zB5}(AYGmlb-kA2b{QGOs*@Cv>>epin)@q&vK~8}zjHqm5jeeE%f`u!n z16EY zQMIM{z4T)kgIzf}a-$HS;xU#qc0M=6i{4d|DU9oGY2>-J=N~0#c)4$_$JW=EDN+i-vwCmnm zSwoM`WD@b5(btTrY=dt87i5ABMkavw$G6)zTkc@Px(=(zaRIiDjcZYD5w1ble(p+3QMRWpsC^*d2wK zgU-OO{+MgW^IwBHK6R8n7J1Lqp>afKy|#FG2v;bsmG4Ab3BRZ1S@&a${!2 zdUjH~{8)z>Q2^6m98sY7cH%0c+ajElpi167fl}T?vq$NyJX68oaO^KD9_1D8Keony z?KXb#vi)`(+^aFIb~CXUS9*&l!_k@E`C?8?-AtCmRJxD@!QcDqwCqFsG|kkvvjsY6 zV%Rk;=*~AE_Fo4F7N?XwKXw8nKaryJsosaLv%d!u8Sh^du z9ZdaZuaxQ$?X6b&z5}8hrM?EHzn)^EYVD}NHx&FPSy_~LMTE30MJU*Vs^11g$0fP? zZXBZk$vc%oCSPViR~j6Srle_%16)PXPLGA90!-noQ>$GYl?iW~+3Ei=Ht7L$N}AWILVn-b9WpPR{180=bD^ zigM5VmxbO`ii1VVI%F12kLIhV1D#zVfLcj~NQQjXO2p|PG)u^__J|WL5=TG}O!BO| z{GFHRC<{G2T6Yw3GU(j4=-dI}a^izn>VJy-p*Pe3chYv`zkiNe*7Xt|`z6s}e1YNJnx zBz)A-M*_0VjHcYLIs(-e6AC2c1{@oCC5i9Db@q4hWkE_|Pt}dDV&MP#*;t3z+`-sR z;G^{erEmcGY0>X~fN)rX84rG6q1p8vTp6hYdf_-Bw2a#?&A?cvmwBgmN!TJx^`CCa zc-!ej;u)%%fHJzOtevb~V~-UnB4HRjfUAo$M?dtC(u(fNkD2a|vxC#>0HjS%otR@9 z;QUcrEb@JK|7Mnl0l)#`PW__cK<(DNv#yEx{Oc+-9Z_j**vaagZxg_SUB%zcW@M%%5~F-CL*b?lP!I|z0>Jo z=D;_rjvB{TR@~fAeG=(T0p$_D4YLMtoBG(fg%YdDUBd_=zR1yrAe36thKC?mvxv!< zJQL92JOv<}HjFt;c~I7V*%u*i6#p?PkR6OpYX#-(Q1oe?BPsGI8)BVUh}=~Z2J5A{ zG4M{Ld0l(aaX+UnDcBwI(9j415PBa=c+W@O{SBMz{OxCOkCMg`0BCMy4?cbsP6j`) zn9hI!vYEIi%P=Fa?R4XJi_t40uT7yBlC4Xa6Nr~B6!;^U;3MaD1EP6&ZpkxioLMS0 zdDNAbIJG0L)85)h>WnP900hg^hqk!J07k%`v z@1R8_j5vY`kYIengI|f}LqJYEoasFQFHv60kYQ9rzpU4e0P18`-9O4i++m@>ot!8C zWQKW^8nN>qoXfF`hf2ck58gPGc4FSfBE+oAynIYcv!O1c&Qp+iB2)w*WZy5P;_qlz z3PKe7Tx^Ax@O^|&&UuMss#D4#*wm%3k%Vb^>NeDxV4+|Tr=5#SJ(z+N7XEHnn3wG! znVY{pN(j`50+1v_x(EtC-$Y}dQMe@27nG9)B?+*N_!k+YE}>YN7*0>~G}hV;H|VK-oOr0&scw{;CD z=i*)Dw4nbxe4I+tR8|iLS80J%W3HTYZ>f|tPI(ZdpoOxTA(bC60tqe|OwHDrlz=r~ z88F8yw-C#JK<%kzG9?~Kio*b+v4R`R&w@Gf8D;_LgGp)83wa7z+G){wht6v`7}d_F zb`*K$0;n#bA{coHgw^sAlyubC`#KKmIiPlXcs7|Xs_+rLT;>x*kbZ-cOVGu_Cv;XN|ikvmW#3XxYOAr@w*h}EQ@S<-wSkj$J>ae$# zcK{j$f>I<;fyUOU+Y7=Eb_Es_rdCaXUru4t+pf*Mv{tY1sht3O^QzMZ=X-#%biP%r zid8PR;irVl3qvCSw-I5&{oAsAkR4c=nM9u5#0Rc0TUa>m*1|WPl7L0zD)^?VH62Kz zvD>5<@+xkC`B?WiT>nVIjvtM^(mk{^7hpN{aRl-8wRiRR?ud(P%R;ce*RC&&2`-Gd z?-(`wK*+!)gr1Bw5%w?qXX)CA%9lizubML7TG0F*4e8)?el6b~`AOnXYo2ATGCmcd zPTolMin>|e6^9yJ6M}%jJ>g-p#W0S&3H8d*%SXMMs0BU43{XUiwwx!c_S$936@cwK z#Sa%k(lUpXD0EQ()uvTx-b#6G^V8qp>7({+E(h{yUh+}tbrJ_Dd5J>aKOu1AwhA5; zouoiGC0Ji*M4aW#<rq&nd~tH=P7$aR6(SWqN!D zk~;xju;e8xP^}>G!dx;zS0>uysw7KfPf!$K3JV5_Y%}(qllEX9!Q`6c%e?Jd0em?} z74Gox;Iys0fyvctbs%abeYE6JV3n^hg*HruEAbjNtGTs>REzCtr{v=)(wi|;0)k-kZkLjb6$uh$dG?+#}k zGdHUQ<@3E(IdDHK!DQ#HoUC%~4$<;f;D|)1P+Cf^nDnkicXdtDp>=*nlrUf07hUbd z-|&uZ`Ax>swJ~On&@@V_W==dZcM-SrS6DXpQOBT+6FrdqlmSaqOlW%!N$D0KR;h zC3KQ?xn#ZkB%ms3Q7RTVtI8KcB8wuPAV_=ufdj4MV}ygtEud5Xq_JoNGGsQ^6Wkq9 zwm41Z!2nWpRiO6lF{N7~|E^_dru^CYF>*uy+^)9V4o@x50~9I3imIe-s|}|P-<_KQ zbCvuglmH-IcA#n!fa@7Oq${Ww*Fq9i$j z>%`+7PQhZ}8cWPZ9~6cj)QB|YF9degSTsF5I^Vc!D%|{J^*ift~40n7YT57cXfCJ8Kgc-fW6TU8B-O*)weX`UET4=FN z2hBRI8t~ZkjgsPi%Ms_=mw{VkSPIX;uKl&rviZexG)uq(vhEWyEYgE;g@8-x5^dIO zQP496%KWe)Ph-a;W%4_W`t;=d8jYV`kXk&xCv;e5{wuNp;=!vi?S6?{K0Meaxc$qa z7ag%9>9%C93)%AzMldKfvdD`o5{GtW)u|u&3 z2Y}6-^c5X$r13+VBmJ;wwGlj|4!zlSpgOrl#^yX^S)Efl<&`9R_YIlO-`|6!M~Y>Z_yUbBh@J666tPaff1g*>vx?HQ8E65GgpE2`P%&RP!NF%H;|%R+YC}*igh4 zo1JT!mSjI>X5~nm#+Z#q;I1djfxHF-_9KU&i;=|L$eN!bf=+6&;~0q}jl z<1O8YzHQ9qDjb>fYxlr#<`Uv{+c3LGP7I8<7f_kR9thvpT6X#71%WBREzUT+@oJ5% zUvLBscLY{UkKezu+H_d`qZ z_l)R~x=b6pYT-6&6U%0s+xiO(0O|;;%ga%9a0T+Jc8ATo7bEcc7GMZ`IRj{zQ|r$p z^2^YAOQfzR|&?FUM=+ZXXJEg@$t4Yu0v{0orqi;PsSfa3h z4wJ}z|1t*f)q->Zjw)fX*W}{X^3T5hb%tu8-s)--rh`53c(og)1<v>C>wymO zHq{mP^F%b!T|dDMInjoD5(`Xe-U*3SMxsK{ENCfOqBc8pMs)G#3>8hd3IyE?_mqJ|d@UHB%4fK; zrqR|?(@^4@IrVrI?1+D=M{9bFdR_$TnwA`0#`$1-o*-Ca+H zPtYe8OTg&C&u)fH8W?_Bab>nsC#I!-^2i`(t;Spc$JRRy0Pxr}edE6joO7j|EwrBM zClAUe7uXX=gZF?*HcF87UHJHTpxhM{5My5ZOJBg5`i>%}Ur zHpYP1P?fxu0H5$XeToK9c)-#$(qg#y**?v;AG}d_OELNFhY7u1>9%@JNTdW4X3-Xq z7g%sT-1q5F&wI4Rk#R!HGDpQFbJEB%o-@pRvdFH;Kp1mRtl7~>XuN(uq8vEUD@qB3 zio;auzfIiOYxXmaDGyPWK-9Rv2To}J60PxW@Czb>02+>4#aN?Xyk|c*_{5NW!9O1s zE#ahFCSdFX<_%!2O%#F_FdbO(m5XumA0I}d#X;HEs!z5V8Nb0pRP`J-nEsRf-i8=| zVW}E;ZH5D`%G{);0t{Uu5J$t(4_rACW89cr{rB~q6p2zXrF8A}W6P!H_I{ga_ei+! zK4lCKPzq-LX-DxUkOR{)OJ#EJnh3D>2qH4ml%A_>_aw$UJiL2>oZYHU%=ozG<3iX@*5p4SptYdC70XPDC{y@|X}jOoDu z`f;>bm5{RRfrxd{JP8BjAn1tyhOEc1C8zHUB~fzVwr63`$#+}6cjfAI7==Js6?lT! zinL0n%d47eDuZJ?2sljvpRc^0odL zP!4W*v*1T3zJIkQF4!z3h(TKzO1#PS0Cz`BWs;f6t9bGntfComz`;KT`ANuss%^{^=5_H5ky# zVN2(tJ|2%sMBC6PeoCCa$T`g-z)GA;<0SGfV*p|l;hcM!ZoKyTjaXn0D0SWkkY%uD z{bgW;%{FSO;NT3`xc=GeB|oWedm(GP7;F%N{>be+Er@p+B;pLQXz5|~ zq-vR+)S=NIWNS+cq9Nmj#kHRs6eQxqDwqx7G$=*A6`B zeW|izOo4Q`zVXVNTZ(4RrRW$VT&s(qVfocS5! z3Up&V;~3SRi2oEDcyt<2tp6L+YM!A2px09Y6C4*D7L;gCwd_QCMv9I>{a>8fuPK>v zDY<(&I;j~)dYTDFOhZe746mmNI?Ie#sf@D(Mr)v%gD;BK`~HIOvxz7 zPDmYBKv>aH$xV#Qs{f*$mSGs5npFvpWgHJHo1bessQ?%G-;b@RsG{;;S42BAH5)ZM zt{^KVH?PPzJ}W;xStU)cyt?#%76bTCHDgpzz!9QLrD(KZ8Yd;kr)nc$N2jVKW+z8O zKx&#;|5CP`)LfQ9i2wJ~{@<2JW{O(Q?f-pciW8^p1BK8+UipRrcE(^Rm@Tx0cG1xs z?9A3nZ6xVLx{k_O#H>N|%soE!Z$ajB;EkOwH{7Q|eKwr>qa(*;C`LKXNT&U?&$ zBS=Q||Dp3KH0o^yV8z9bo0@JY)(&nb3p?0ugzfoOl9d9>N38ecfk8i_9sk*5OqSL* z-&?dJ_?@ulXaaTQ#sjaj|y0H!O}EIhOTUnZltXDq)!)puVzVPm_G<$GPV_jPwP? zT7`ZZ4{zY9L|n=C zYe0zgt#y_+xKX3&MUCdawxE`L9Qv1scMM2mIfllYgX05nH}@CfLtkI^4rss2jDvp(09ysZo1S-JI}0K*3p!DD`)I%c+n2=vbx&-#|)zB%K!-cf0Il?{4y+0Dt_&azd2NEPkIN{*C8R z1+NSboUb#0CBl|Fx`*MU!J|N9gc0! zGUJ#_!OYp1QFB$!7aoMrjAhd$(Z}86a8IV2XmT2<>0Do%&0cD`xU){x@V3zyS}NHWL^ziyjo7j* zN%eOgz{M7i;=Y@2F<{8d>=@fRZ9O|aY)x?Zp*FsgEA9Md9fZ}&E#*t=w=YD>nr>l-;91UXrc}%{I>^$k~CTb6Qno6SM7RM z?Hp~qDoiHvj!IW-?2MJ(NoQu}=YEx3A6@gY92+}QBUUimUc$L9%qfh{8@C=4v14U5 zD%9S(nNEKhL^8*8V06(Xz(VCO>P}2gtTwqH#r(n^16@GlAMCuJVzB79g@V$-%dqn= zz?uF3LDx422NJy7#6xmprsq4~Iexm~G%;{RRSk+$*N8e-maXAQXvAw7&z>pF-L_-|6S~VBQ~9(GYzVQz z%NNkd$l5qE#xRO%J^c1F3S;9{Rmh9qM6Ca8K&O3lmVK?=aSoIY_SYEJT0hl1u7AXK zkP74rXeU1_;u7qxDt##*M_hoQsiZ7RpLP)OUQLn;zmvRE|9I$A;*Bh@6y-`~po5A(RSWApt&JOyJz4GLK^9y}>JGsPnF zZ>A5M_9%{AQ!4)wLfqQ9=&z#%fKNyWOT!s%7_YXh;XS=vP%KXO2ptJadz__&>Ea&T zeToI{pLBEBj(*9tfG%~B+i2EeRNcWpo^!vdoao8M8RKgGryug>17!t>6R|oh>c${$ zf${B~V%yCnQA{CA{mZJ)&X7>c50j!BNaCeDUd}6Jx%Q_632?&R9g9&JAX^ACtZ-MT zjBj@^Tp*6^I*92VvP+Pd^3U(atbZ&L4C*xF14Q|4cwRJHJ-92RWfCic)=;Gc-) zA{TZ?L%+Ifm^K8Bi|aYgkhL%OHa@DcI^I4<4RxF+S&;na`;P~k>LhMvTK(|XbKb1* zeN4Ap>*QkO@KByCEX^t4fFo_oMk4xiwcWO{`JZymM&KFQVa8dlp2cF}Roj$!1HoP< z&eGYVMP|`zDD)!;Vs6*cb=uL_;6s+mKRfFBsQe>u4j$_@OD{pk*fzCA$9RY6#I2EJ>PH#L;>!M9a>+X_Y;6% z=JFECIv3gP`o+q_JClfXLC0IfNX=SoFSGLj?4-LTw^j0f$Ln0MR>)oc zEuqX!m((fgmIFx9Ce86fMviC83V8?1EmyD9%p2c2tCJ^~wtvqzn%xG7<7Zj< z*OcDNVmO`gYIr24jA6a9G`A8_G&s(1N}E%`n^~zQjeYOM`#x}j_&|UBo-zuF?HLsz zWpXwgdIFvgl>KeIfb7NGL5Jz~yz}b}h*6cl%4=$Z8*4C-lze=;WL%Tnir;}0%f4Hw zh!2QOAZ@Z$Xw?5cAhS8fuwg4(c6c($STSL4j_>q7NU$3L_EMA3J?0hfduG)jt*HXz z8(Dfg^o|1K*Rd+b^^-R?c=gcd^!pP;bN~<9W7%>MAU>YTk15$erbIyn@#6FuAzZZUSNMp^_n1KJ`$Q+eg;&1EHRg4>u zivj~8;iu5HKItZ*7}ohG62|i5hVE_ySh96W22JCK{=f1 z)_5tZGb&L!E)%6j}e2yIBT|HwBJebY~UMuzLLZ6(v;;H;6^TqRzV3vJGz>#7D2 zW|-P5;m878m{L;=yaadTTSWH(|$ zkf7~X{DG?;5(@611Pe*8MzhP>9^@SnGSle+*R6L;H)_oeCx3vdM6Rs`MHW#EgN<;Z z!Df*VHpm?@v9@VI+o49^e~Rc(B8gKc3VM6TQqXP0Wg>>Sct>o*?-?7%)8^afYZ8oTgwnxnnyq{J!K@V_U3(Q7q$Aa^5WKv>ATC zxi2Apbiqqbu{P=>wLPaJy+iU@W6``n{8(dGSkGY_97z4CaxE?}(lHX0lcUT3`D{X(86fh zKrIs2wCOU~mpQ|}(E8|4zL62Z0JF z@;h0#8|mCpC{i`l(p_`4`U8%Go&6ZmCsDC(?>Pni?<{kHbcS*>JK>=f4G8m=*9s?R zX+QP4;cK#$gsV#S3Y=EbOS|;jxW!nRe-3~cKykx#8i6blWIYH^b0vc!;>>&fOhn@> zaw^WTzD^4n*7qJyg$WzX7|rC&9d`04+Ww_nGc~QdF|hYIo9E8M-NP%O@LkJVERLX) zb1z2+J(cYnD%h-?kEcwPHvsPP%_Otci@N=-SWavsPb9QLV6p|Q^1-{!J1RptWlQ1I z^QWM%D3s}E>EN`uGITfatGc zHB3mixLOIYXqryxhi|Go+YnP3#BJ#f!MrY6bAb^@geG<5k_;4s17ycmuNV9b@TS>; z?aTpidLD+Y2p(d-S>$*RN8I>3UyM7!bOoCz{86!(=8j`@-bK`4t^j$BXz&q=UYv z@h!?~*qCso{+ugq0WgwI3_!uTE5^7;>#lKJ538!AyhSd%(ERxky>*V+y z-w;7@sw1Rf31E_AKFVlm2xBOV=W2ZrkfjcdJXPGKLDk8LnkIGHONMyP2>UKI~;~Zq$X^K=W)oZ)Qmzl3x2)%jokl@KEVcYqFK8 z<{Dm@s>R!5uW^oCCpH|qE6g$xAYWObg5}5({n@W# zCq~=VWFA5E{BwS*K1lqm=VY2IsMwe3hS7##*;sqP;U!BZ8eVQ%->MdSwJA4m5x@?q zdf|yRG&q_%TA-~o0VleS&NI$skLk@{6W%Ct&o;l1TCF%5l>{n575cV3-FXW1`33}B z?iYEf0z(Szub6a)t<(wdnWxrk60k~BLybAVoabXor5`X@O2_5UFt_TkZBcYA+itS$ z6x+Z=8BQ2R_Br>5B{&uAC8M4PF#0w*T?Ok}{L{1!szy_9+;S8i z9bhkXxWLM7GKOW$Lz=FK!HOi_cBK#TAgM9+0OrQBb!t~A!;JjbsWzlBMZC)j!l3nO05L=R-MsCX{ii*a zwp&ckPGn!nUiWDB={p>j6n7N9lPpl*F#R`#r8`_VMih^;*F|K9+Ol!_RXO(u-~Hvl zD{#1^m(mD(ax{)^cB6;wkEU0u`Z`tS6eDW`{w^$9O2EPF{iK+w{TG%-^IFlkSv%Ss z!@;TdRUWITOu>P&OzEHc*qGN(n;3YSibcy0p}_C`YrGZpCnJW>xs1uMUoi2gOxoD! z%t(}(TBK{Z$C0008K;wAtg!@X9IW72(tu{4HsUf&bVLl|-c+rYA^Fi!>0AiMPv@U$ zpm}RDnJ>tc{9eZ65_@0M`5WFMYh+u%ET0qTmG|m^0Iuk7Dpit%il)Eu_{J0P^4)o5 z{RXRNATr;aEX2*85Yk(C@k*})nvD7F9BW(3w|cJb5(9upre6rB=5>Z$zhH2G8vQdA zY~fb$)_q}W(SSny00rh|aggk#d}&%Qcifq}q^DEJA6`v>B_octDB*9*gbs>{n;k1U z&hC+N%f9Zvcr4x1kg3H9s4RVosI+_@7bUK><~aXs-?vZ4q9%X;y)GOH>AHPCJ*%J% zEsbuU@iHePi~5mFuK(K6&bMU0C_6^hk0ZMl-ld+{wTZ}5@z14=S&pIxrbT`XV+TVl zgHgJ}!800EH8hzqopNoVK>)>2w%!u6&;}vC@F$z5OUCu)g5Wy(?6^of*=mtOr$Nbd+iStzb1D-u+_JUojv786$qr1;gJ;7S6{js}@kJ2!`^gOx1 zI)(o1De*-EK8~2o>M$|lDwuOw;VbSv;o3pbj`8WR+;EQD`Pdj|iK6g{!W<5#^%+vB z+wV30Y8_tb^-{d^we|ws1Go)FK0SDUJl(Ya#iww-~db#-Nf z$yz}X!+K$P{_;K36Bga;#i}WizU=E{D*^I7L|Tt$pMS=laDG$y^YxU= zpktt9dYfkO__9qk>CPi0x?yP$pq6Opn}L%suqS3;VC=GZraMUMYTH!b?Q;U9$O++( zC{XS|5M$ESf(m!iwBZ*ti)n#Fh#v98h5KjTJ_v+-sXBb^YKvO)x41AtR5}y%v=Ygl z*qUrDj!t>m>cn#@r^kUA_};`Be>-)Kj7$IMn4+|C=ljSNBq~{Xn9lp!``jV}j(A@; z`(C;JZfbm0U;bX>jLBbUvB1PBl;yd+y@MP_%}h0?8R@5`zwtpOAZb?T-`xFp{(PS@>>;M2~0LT}kW?*TNoT8ZkavI>cFC(&X%h-60=5X<>W5$vMAbl}79EAMP;( zJYavkZ4N06-sE!SMx>%H>|w0`OUp^6Yi28wD=TNzZ1QxgemMUq;qh91aQf(@Yvh{{ z?K}VG2vO6C+D$-a6;~@jd9kC{0;l$V-VU(tJ$P2nJiK0N`7I=`O?R6=y*>`^SeFCGyuJ#Exbe4`ZVP_|COMK6s{WH? z@{$FzA?5sy+)u}$w+T%tDs4-ir|P+FLmsOtLyDZ0>$LgE=bq2o8$cpep&FoQK@2-` z#B>A|piWHurw7D3g@;FoGX>8oQ?Rr07B@Pemj3-_k0C({pjg`a{Jh%|5)uq}f6T4< z`h355s+k7fpO=@ffPjy?)VMgI?$7tLIHKIG9;Wrz3)iZHo`AP^LfoVEqYwM>qXfs? z2fjjuHFE1za-}4}>7qA2GeBW!zn!UVSH@7Kv7V;g5QcTIn(B&aG}Di|p1Rmj^Ov-8 zq@gTqd^M>=Pa~yXp2o{1)A7QOOQz8`p;Qvv`h2Sl1eH4%Afn#7(d=kqiqW%~(Syw| z=kP8bv(gOus8)_meT>Xv$OPRGMF~gAX0F{jWhBzkHJmJiEZi2i0{}fz^P%8~sJcBP z+wLeX(sC#1E)N+9=L_K2w^PJbWYhcwtn+}sEEa0v{_GCD;)M*aUFLCk~s0ruY6Um;(rOe$XrczOasIM zOa;XR|Dk*6hCbS|?o0G*=xCW=$shg|>&Vgg6YIh5cy*z}Hy~y5&bH~Z=~wjEGO6P= z3E#>XXuzKP=j8KxxYV>kl_lGjWcTFF<;nD2ZQYlW{Ab9WtI2NmxmU!|-nJk2Q-P@v z@6i^^m*5rrL+{t$uOQ%qi1QAUd%Eudwo<7UfXG1>BofH&yTjp*D`PV(s}hQ_Gs?Xw zdU~fE$^8&)9f7{7R=J6O+p7Iyv+R5__us3c@{Bved-&`?yiX=EB3XVY4t{dPoyG4G zJiDAfbqs%6)cpup_r0t(HF&$D@AK}G%wDz$tsIKHQeLjcP=gzF^`*D%lWah`+H zFy1#nJl!zC@PB}7h^mCEM78;+MSHqsWl-xJ`oM>7Nv~})@gbcfbY-l{@^YCKqH;ghk<(`qjo%0HXc_B`>E2Kj&}80Y z7}S$A*p|3_VYTF!;l0C$KQuIa_TZ{$E|*-f^vVDtB%Wwfb47X&>DOSRe<%I#e24t$y`+^x}rQa_qCwiRLL^URQ0n`uRRJ z{pao@KG)x?3TOI$`~{GeGfmHgntZ|9wAMcO`zHsw$%Sz0y-AsX@bZr>gV+1RcH!R| z$3vGt+TEr-OX>;l=cB7#CvOi~KV7wb+Q~Ymb-Gi9fvQbYPFoc${vh~tsVAhs$g z18h_+l>wA)f^XxzdpFB!83Qg;+fA?Me?4}8X*jDcNmkd>YE=UQZ`Zm4)s^EAIkqQW z=MQdKe3LV3OOpXY>s*TFdR+B{$-eF%uM;C*Aj$C}{}D!!c;X$xfJ@1b@7f?jlNBzP4~JV-iAc{2 zqnD^j*Y4(QGZxU_^v`oMbF}`hakwiA-@Kgm&Dw-sjvI!15Wkgl$8t`Fr+T4>u?OIsA(;f`L8!)M!PqnzgeoeY^t*_RwI@d zS6YrT|EokWRnAVRmu;8IrQMeX|1{Z4+l5?Icc}*tVYg0e|HT@nuE~m!AK_1+mi#Lv z1vQ9xI-@?Ld24si-m)2XpUE)o4=oKzQCT<*J^^ry$qRW?T6_dER$|^&5W>H9v|L-$ zZk+2lFd#^c{Ius*F)WrxC(o&enN^|LujXayQa} zo^m%L#r^A}Ep`4?NCnZ%6tbqK)m0|ZtmTil$BD*H@CV<(E5e1Z7rFB%&R|cJ&Zunx zh%2&Zg201il+K`S1&9@YbI`shm?=^(*ghVJBh>H0fdw_+#jja|4N0Ckg13mDJt2S^ zVlPb4x%yjPFmmeyG_XRu0|tm_;eAKQ7d??r&G?>VWR!QTz6hB3ljJxQzvyjrh+NWV zguWvfMzUwHzEqe`@V+b5chEk7^ck*?2=yJd4N7;4C#5ERrVBm;k)=LE8*dM- zXUvb6quxh@tG^Go2Sb*nPC<(-x(CYav5{p*m0)pcNXRJ2OG6=&l@t>A~*^^z;Q!Gdn3d2 zJaKt}fd{}tY<9r8=iL&v&m=3IPeBozlrC3SdV+MdR^0y7cv&d-v(ON1p-rtC$JRWUU>I(&+tF#h zrq}aIVc?&@$}xwZt`D>Hp166(rF-hOxTbsTcUk!T_7QR*@wi=s@cFZ=_WdFF?W3UW z{MP*tnu+Z>GAy~xe)dB!DF{#vVvSO|h7Vwq@96CD>KWZOhcZ^}=;_b>n%#08k(7Mh zdwh+cTMVZ{{N8!0Krp#%YohbTh4i<(2Yh}1z9#1(l(O1FuZbwor_m? z9SK302j&1pu#f54p4~Y0OS>J}Z9~ZO3#=cYXYoSZrh9*34YOU23kK3QJN~HwPer9x zt*Ln$Bl5Umi^dM>Yd7b%!Jj?2!uUAlkohy}Twj9K=sYui5V-kXxkGIk$H%s9<9-Od z5me|OR&kp@3DylatkIgL6H zSH@>r5d0_pb+IZ?MARX)=)l9w^J)>ECjxsAs#T%H7(%jBg3egEg>lb& z5Q3C(_m!c@7()i^xV((t<|?hi&2;_e?o&!oeDqi|fAcG=rQ z#p#XDJ45U4(1xevaQK*ZAbRdOz!1KtGCS$__qxM^cT;2yrdE@-h8y96jy{Q?wlR>l zNJKwIj=G_yGm_3rn6>|q$7ZRcF)>jSN5x2ZBE2;jj4u8`N^L@`Hke%agH8rj3rPIU zM<90deTYAd!g4u2!gh3k;}2pr3Egshvh6lF7cpiVD4MU=qv`*Ioo;a_Oi=FC0oNW8 zkdT0^@(ZO{r_p z;4RW;l)fvJUZX9BKsZ81z~~9`ygP6MyY>yt!?5=fe#L)>4B}sCgeQGN0cCFkUGm(+ z;}lMy&kMvd5WD6L=0YJ(f*;)n8>Rx@@IVpnJ}@OITt0XIJHB4SC0>&TH^4BGdg1of zpuFStWx{NVUNZ(ip>)P=gOl&NLpTz*25-{_n?umS$VxLp0XAB-Tgk|%fll3q!`con z|Mp1?V%rXj*9F_~I1ojh6;*l{HTojK-W|n!!SDBi3?saW+#~Gl=bS&~GDTXH6R_49 zhc(bBjsKg1(kM}Wf}Md~H*LO?fj_kP8bNdmy)m$t_N@9Ri8L&UQScfwLOH0IuD}UUs);lkX+sw?4nplka61|Gy^1x%IGg4SBsdRQ%C_wS+sMvh>w!9 znX~S(gDe$KH#?KrrfDQLbQ`I?J(5fL1?fn7EoNjpu&^PKrH1qu%Zg>dn(xBmgt(;$ zoBNE)F2TeateHNgq-}7<57}vF6>4sJ$TiJW01U!4jH*>wWMJAU2GfkOi@k+0^QALW zCsV1=EhCC)8v1%0tZZHKBw}rwHS?g9q6N#mx)Kbqy#{m5BXJv`Kbq47Pm^xt0wYGs z!!BF`;2{u>s{%pBQGWqowL^%Cn)u`2zwyr&v1|IQ$AU+A&z#QN_ZlLDkt{Vw^?3yH zy*y=utm8&E3G{Uf^{tWo9Wtu4=jx@xiEg?uzj&-af_Y-bu`M8$g=aBKo%Co5!k(P$ zJoSeh`uu1-KMm$px1Oy}FmZ@Y5uWE}ptQOHxJybGW7c@`HvZOFJT>ROB(0x2U?9!2 zqV0TS@<`Zc*_8eyWj1o%8JwQ=N+BBfB~L-Yp3z>2r!(}+eQd!kgi%z4p=hk~y|dDm z!$7E@(0q9o&Nq{$l*Y$|+{}$(xuWO*RY668Odc-3si`R;2MZT9UyOO*4(fI91m(jP zV86yGQ-cY6yeYv|NG)1uL>cZ5HiJ=);`W&nq96+`Xz(o%#kQs8U!oDWTraq1+O@yH zuW3?4QJd#|j68R3%EaEUB++Z7aoOton31qlQ!z$bhu7Edzx**{)3s{bvnulAZ+aPv zT{LTLU6A>5Z=J_i0*%7b^*0K8eKWJafY~`BIoXmyQmg`6dsTb!goTI}q=w)a0_UGn z3;&~R%1L`2R@F$f6gPcd3ii{m%IHnCf#|U}bxk$%U55F{y6KzP&U{4Ob_5Ug^K%P< zht@zCF)H|SmMXJ9%!$HU)$+XiMtHe#N;8`}{aI^Zp?oMRA!rQEiMN#|D9Z?KfMs~$ zT?KymE&dU8qE9)bq)ld)D#6~Lx4A-#*b=eN{aDXZa@i?4nsGnHxj0XG6#aun+|}8g z{l>&(Sa5Mk9UY2{C>RD&gm?Y?qqhCFgp%n@5e5PRC$Dd1yC8pMSC|?b*=a>S2RTHl zR;g#e?S=8}v7}4<1^ppB2g)_?01b9KDTTS%Qlk=|im_(%!?cc&Ix>f_eZs_y#HMIs z%I`jpM_CzE7b|lItl+fgdAQ|1d7G6M825o~<9PXuvOSmg6lq;EtE)6Cw`8hZO_d(1 zOFD*zC$mB-2PK~}L(;d7))5={1jkV@8w3zSR*SPCS^`!6^!Z0a(MUv7nTXY2wDg;WE40fYootySR2nZ2%)xchn+1qCEkH zQH5dC$qheLfW*xcerJt=w+mfIN-9$Phg+;k4v~1eM7HH>d5vu~1?R>JjOgd-K0$!r zWe99W4Gl&$>-6Y@nTg`rnhZ>7aCL41WM&~sc{*&w@WNiXhbm?kAT|R6zrS{!HY_Hf&M&q;gjC?4B!f1T?>7F`a)o>M*k6X)h2ALSx}#8BXy>6SOYlV0n|YJ)PUBQBkGgM+Werd2BvnbmG6SXufl|`c zq@%`B#K%FH4Z{oyI2j){(HM^)1^Mehug%0b22(MZJRCu6VLpyTXwb+NCfyAgbBqQ&h-{?m>;u(o_+LyXjEl-1{qW z*2M1{ORM}ll7C+qTs%3LAYQ(1(M-7+lxBMOe8%UMr;ASQk&AC>n`I?;Q;=(Eab3%?c;UxT)K}+>O*H{cJ zN)ar^0E0>oTwVa1q&#teubd5r(a9!BL4lm(BwR1ns+PxO`5v^ZQ340eHA%+uT-I!AEGcQGFh~ZS*dd|v>aiT5UpOX zTuec!phDxUO!lwGldkHt)hfFTA|(fdT*v-xqRa^ELvSaJR#5V&C_9%Fjv#xYU%QYX zFSj2%&x+JxoWN}MEVX}=zWf9(t4PrR$(8$=*;U|lO3(;Acp_HGD1&Hwg`=BV&GmQu zr$1twz|CL9!z7;TvnkD`-f9P*>;WwxfbNu%yLvb2M}2Yn!p%7L4@ezfO>U@3xP;%5 z^|5Kt4Ul+$=6W4nT`Op4bizSW5n_|+$7{C>8-s0h_3WVZ-Oo9zma3%;CipVkT0Ocu zT?)@=Z(?iZ7$$=^x-iFX(>Q#*6j{~g0|*Vwf!Z?|8?o~k5U4(gb=~#4Gb@BiSGlD& zJgjMlzk5~2sTl8-zS)HZRt#sP{VA4rlVFvQ>6PpfC7aZ5vFHN+D6N7ZoOmp|g?w{K z()S~Alo5OWAI`>88If=3&*Y@%wUQ+86;&tXI-@6eXI}fOaORqY@~FALe8*08yOv?I zfSFc`!skoJ$k5cn$}@7IoRkH(6zL9LZQ=<$L>x{#)50WEd8zF1q($Eh6UpCfsKdB< zD2hTk>ONgic(d7Nl!9`Nrc!bD;TbQ9mzfF5xtArZ+v%|W!6vY8 zgRzg}d=iBtONr!vlj4t!Q5jA4^7&=#0hwk=?A1L{%hVZeHQV_<87Ry?gng*7PcJ|2 zDOLzu;E`Kr-m``c_4*Tw7N1viND@&%%mR;xAxr&_HJWUbDy`lL?q)mq{VCNq!j@iD z8x+n^6OBE-ewUyh8LeT3j4h9ab#bdT!ryGBqgZ@w3lEn?Vyc#RKhk`)JQhyO{n z`z#4TLqu)O5tD58!KsZC^V|ms+ETq*Ig`s8pFrW^ZhoOJg1ZPc9w4>_14#0ASPBXT z$jYQc;^}aTnQ7pc(k5}1;nc>XE0;EUU8O){5Ck|(w_<3f)4`MFEFX|CC*D^CG3W8s z%9UWCrKL||d9p>D9wBAq(6sWSQHit?YS2|qpe1zAZVI{4BZdUwGI6`f7o6Du+jH}9J_p!{zAr=YJI|rnN-=3VDu@lu2OCK(|Ma0uqb5)wlL~!9uHt!-|+}7 zk&jm)9mTii=FvWL6$Z6cG1gn0^x$y^7(7hsr>%VO6b+3F%w1CwG+f{|KI(7W(J#={ z>x^#f-~Gl7KESd3DFdZ=4Q>nbdsivV$BLaY` z1!cD9`>XybR#1CjW*pJ6#pni#UqqyP2p7%iciyZX824?r!X(XQg_LD%OslBm8o?m`VU;B>(r)Y(;NXlj*>ob+6hk+AIajN`3cC9SPwZ%xy%ahA1tv~V$2B5zr zwJc#0YiphyX!Vh;Z`G68>kr)s4m@Ys>G%dyCzrC$u{+;@=m>nsxIvr6md0%B4uzDa z&gs=NU&ZkVvF3+(Y;GE<%Gd2R>+?UJnWD9En*FapGYxGwtQ|6UZ6;mQYK9qNEgJmrbv_GEddcw zz_7s`D(e^Em@b8V`%)z+D*5YDyN37QR|enLH?jwZ5FCwVmUE!Tl;BkR%Zzd+pVMz8 zKvgDlp1x5EI8i|?Qc|=&S1jLSj8SEfK2}7o%Lc3L9#EhX^HItCgcpK(n)$Zgq<+CY zzOnfe=Pm;@_4S*0SKpCG%yeseDW}mJEZi~nvU?ZcJw3QX1@=$O({tR4btyArBr0FT z^Ydco+#lN;D?3^ZI+H$pEBvzkpSyK}*SCB|k3iuq0aO)yY?yBid^ z53dda=nqLW;FAKvR4Eg}WU2GDxdFV>hR?I`uatb2ff0vgepQuT(wY~O;QYlfzUMi+ zn2(+&dx1j}o{ug>CAA6L*M&ZL!_kZrm9PXgkP)twC(m;>PibKb4x$h?{c)-ZmR$Ml zNsn1J8hSZz$C9KC^kbFuTqzW6=lpLZ6(|XSlkPu(6LAhbc8TL6Dva+f?gkKAv?u>^ z8tRevvdK?>`V>e7@XMu)a}pnfq(}waswfjGN~_eYYBq1|7Lv=X(KQ%~%_t_U@7`dE z=6SF73OqKKnoyeq4s=?HRM?~cMi1#yR#?CKcs0B(7){m%I7pu$JdnAhq_C-y*lOAW zK4d2#xbzAk@TZxAFT%Zlhb?93a82m$+Q9>Tup@~s7@aqw2Jex9wMe0uZGxl2w@nc73ni$o_;)F*)DNi@f(OSQ-;$0lLx}h}bu|@u zmnz6aS!>IVha{ho8DG`_a5)t%wH_59eZFz<6mep@r!tpT=gYPaxuONJ3vDdGim}b>VPOf;t0@nqNmr^Myr|RXz?>Zq`A}i#k+6Hc<0R4UhUYtDE zxcr=-phhm3g?29Dr+x?C$PUZ$HH&{zCDtioDrp<1;}o;1>iY-p2fgF1 z-gi)u#rc;>ZjcX0(g*y&=G83l`1bcwBw;DTIB?;P45Yx$&n-`o{reGjkdZW@MDUe1 z^+4#Eo~nn$VoFQW!+@*IvPnna&GF*fh0FTzO;65lB`cJ+3hPCBBktHQ$%GSeT%9H9 z`&I&xp}>8WPzXX4d}1AicSirX&trMR(wdnrrfnzg_&k%67^-)YposOZu8+;Gy@?RB z96P&sqabTEd1hWpHvX~eg$^(6KPI=^1Vd5elxb$05-H*PK8~hjB@Bmv_W&|_mYK2q zc!_D^jvLg(c~#6_+FwW&ZUOn4J4ktIgUYxQekz^TUc7K4`3CkhGb=R6U%$m(G=zUU zC5We0$s~wlMU(v%+eBuk#O6i}s_tpXkICgk@k{i>4?}1F2|GEn`f|12yb==OdjGB?@jx~F& zvCOV(K&Hn0@yH$Gi~GP&1GD$jJZ2ep!SR)?DBu1B)*7)&RcA$jMe?>-)glM#+Y`Qp zvT-7Y32O^ea_5jl7Nqk;`IFaAOkH5kthcRiK7G(*E$zzgE*_Ef_acX{!yt#~C@|CED4U-x)Sr>u+HZ9M<*T@YVKsTr+_x%6ghS zn7#J%$l(Vv8de-0VOF-pG||j$Q-|&FRUJ%yyl~}UKA0X_HZwB94Td^Vcz-vKW(nJR z9@wq6C?|qH6Crbz)45T52q?;_7(^0{ST$b8r=}Evh{Fb*XA$eM91ZA^w6QNwuE$JI z(_AIcH-X0f1XzmKjU<2AoO7UD3K_RJBE7;|%2bv~z2KAvQW{xc*(&juu+5at?}mF2 zeFIRRbcD$0=?9lYh$Ovbp4Wo;ia%G)cLPaAL1fFP?hHvsJZ+e7nCGDa0!)u~o~udR zk-?$B?$aGJxXlhN|su@aLHQAs|%p53=UjyUV3<~PNL*u4{2A*0e;5qVX*%8iwSVr`oL zgpiFUl=iW^K%OM6og&o6Ulodr%8^x_ej)9QcL<0v?z2o9ImEok*j(_z#A1J^-=J(-a!@tNQ|EEee>tm4r=*$V9~xi< zCRkMQGuaj9Fr|}hG3L!#izwGyOVXg42dWN7_@`LOcjld^%sp5zos6~4K8W6vkwg! zE*CAb1?k``+$`j9VI;}k1^A8DG!Im)TbKDr?l%?RK{GdtVrtABl~b!dFiBwo>+0-tDOvC-d{{h$O2p{a5e?L>7F0_975YnZbG*0YX#wBB zH*_WRSaA#tu8$!OOyx{KLyO*WQL?3-g>XgF^KJB(ZIrHO0qxgfOn(UxUw%HYEYj9KSZe{zJn(xj9{0 zAEn$f-;76YPl9HZ-hoD`pnI+^TTV^~8W&!)waPehF6a!R<`=sdy^mN->{xItM9amp z;`45i*0!Ay9yyJ6?r*ru`qHdj>hHDGWhixyXmLB?7&dxeHEYNFC{?6gu6?DT*}2f3 z##y3^&@B4ZII0Pw)D_ggCEgAV@|!ZTqQtdj(1s1FuDQzLY7?{mHOK{mcCy33e$dea zOTlU`G=yltX$Cg25ru_doD&pCVO5!c?AJu*V z3i3A{p{TC!!jJ}>q>qjUI>x!AM7PyJ3ToG_5K2Q;*@0#Y2kr{M)0_+?jYoCL#(t@^ zB3Ve;B04)XN$R!-EffdZZef)>|xv2~#SvWOAtE;duWKYDe59)?VrDM#p5bB5sz zOrufm%dawKNovUu2SRq&H<-I)mON({4^&q&B5+j%BRz7rD9wTxOU!y1311%NPx|>K z=N6*xn$fL!27MEN0r92IueUhGZM1{V^rn?kfb&pZ*zOW;QvXB$#|uIu2kKI6xvmpt zDE-6;14D<0JtarFmQ}sR^MSfm5&gkZT8N6C!#RibA9}j_aLH22;^`%q@qYULYVK0V zkN~0*HQlUP#<&xm_Y1M`gXDhJ2|P3>LQ!LlYsRDW?D9I`oWM5izS$cx6C15a=fDeD zpVyo7Eu28Q@5}!4+Ts57+fg}H>yo*?6Hbs;rGQGX=7vJLv{lsSyK6EO;k!0zX~Ga> z#aUB()q8B{pO|sEi9lHh4i#UX>6`=lE<)@QOH9`6C*_WjC%3nks2W3Vj!Eji_$CO> z7{B+gEd2#Q%0}-V%M@#+uRF+O%xC!zi(65AR^>lEapps^mY;SDW!|JkL`wWam_z^! z%|5o`sB}4e$#@Y`HQ%{UXZ2+Yna2TPbhd`ahYomSKL6u({E$@Mo+Zn(xp_}s_i$Pl zN&o!_MDsnKV*y;N3py6S^+$0t89Dn;$S`EP906^Cc+H`Acik+`+Bu*%7h2Nj*+aQL|k* zTXFrt3N->=&e!A-Ig`F^rck5!`v+xTC9cC8|4Kj4dePS{eKYqKJ6SLX1;frCzrwO+ z4X(cc1+kfDWgWV)3ANxEKDJ}y_u3jQmGe(`wA%HEa4s#&>JI}%nH`9x+cghjJ!^rn zjH>p?pWbCecI)9Ly4nKbUq#KHeKk<66^^{x)m6;vdRI%b9dsmFW8oK=ZUHRKT1Q!n zkCC>-w`Elqm`-bBY(%=|zG$`C7uH;9YpXc`$Z@_sx`a~mv0(DY1A{GpKNN>)PzmwC zmvZW(ClTF!C4Nv);6v+fiF zsSfo$pQxC%v`O5G7Fb=vgGR=giA`y2#kc% zcgj4vEuD6nqj8Jn_GF4ky;aknmjg7^VVxP1h|lpe*O)nj*1c3#OGbozDbl-c<3QUG zVoU)Z4C7*)LJA^3By}bzsA!<7}?d#RTgt#Xv^JM29yIUjv9u(GTXfjL_Uf4&_VoKMmX@MH{F9Tg}>!~2bzLOUy)bReZ-6V za~rImT68h=&OgwqX;hg*N=+|~yP_9cFT;)-B;6`Y(u>gxmG-Mt2pqzY0aTK=)75=UY^Hp_+XIk*mzD0*&J_LRTjs^yEM zMEzF)FEY^0O78TOyFCl8li<$qStD>keA+VWh*?K$xd@4PF$w_FHC8ocbt$MvI* z$H#o!*UF`Wf>N!B=c?pwByDhfhHxgNnF?;e)t(rIx+-}rTyA;JuwA7;1;b9=I5#NMA@zP-M9b+tWY&uQ%d?dFz|%w+EG1h{27j z5_cXAFRTCp2ESSbqDU#Ga(|YDQ$D20a$%MWm!_7Mr^~;HsdBj>l$=Rg1)Qa%5v58M z1`AkZ2kg}dRVT+wB@&Jr^c%J6&QaJ=sobgk=ZTqjM`i^d(ClwuHt<`J^UmB1zrsxJ_)WSn&tc2Q5uX!PFKnq zOiSgPgN`{pP6k{(?516BPXj#vbHG|Tv9H-7{wO>Ft-apYsBJcWh23v>A%ENoYxM?fZJzDjH@)tBGmhFg zs|p@^e*D^>R0z2pvf}0C*{^?&mGO0upi`eFJIS>nTcU_~{*s3b40=77imBIMCzlJJ6N zB+jT(QVliJ!rU3KP0Hc^(^O!YA z0Dl7L8LTEWQvpiU%0U!kfO(E!gu`&m=%`lBYQb`=q`RKe!U5ze<7tfA8>c*5Iow9{ zOtJS!Y+Bb*Yr&C?4k2&;>Hydc9L`CH>PIA0S;CnU%$K>?p}3vMmjP}qw7)Ng{)%;r zQgs!x67{%*v~jVk7)z`_T~H!K)gI6lV1IrXV6NH|N(p1+(9ZYL19S)3z5-f{&M9{am`2E<7*%DHJ2-Ede5wt?7;AR z=Sd4OM?BO9N8Dm?`}J`~Yh!|e zDQre-CxfAt%e<3UD>PwNR~~^8)hyD^_!30fquIZ?+ens`!c@{QB}*n1Fy~@%gfQ>L z>G^M$XRqG{+zYu~ap`!d&oQy&z~o$5)}mRxH1+ZV!4_;;XVeD4k=rm|47o{G+`+~` zSIR>yvtx8+nB5_hF=1;Dany$uvwx;OkGmz`Ta>CrW>GzrS=(>`l;fa00Es@&`^0P8 z650V(YmE>itQZJcqe9vMS;j!DHH^{1Dzpr$5M%rvVl-t~Op?$_p*E|E1}N0%BysEb z1d*u7xz==q7!?zzPER@#;TE7<2Tr=sY-~@->)R$37{c(;z?DQHl*EgBW`C>OxK%O% zgO~NJ5F8cCHP|9b6j25UwEaXXK-}h-NMnW`H;Yr@0w^R)QBcaH2Pv9W6pb>iU$sv2zl}?s`sYf#`WS zW9}YpAe%=CJNYjjloPVkZ^4cUK+&4B2H!eYjv#QJP4HdlvaF|p;$G3)U*s9j0R5Q=6@~{6H(J8@@Am4 zb1h^QOYMva?v&51OY`!K6<1YiH)O1s_Cy6cctWOg zUc5JU_HhS9xBtV#Ab+~{F!uKYfAj?dQ!NWJvlc8`4LAN*0z60KluR3`!51%8WG|;> zU+A8ylZ0=;5xqKl97qk8y|)JRq!0Q4E04SHu3_3@WNP=%_C74g@$qp#;QD>vB7Hr2 zwD;j?@Ky&>>(X*UyD$j>{$=;f#?ZzS`0TAt)t@4!nLb-nIDh29adXV2P}7JqE;RjN zo(xS6O?hB~_jExXKI|yE%^-a(;YdG@i@v#s(q~5Lga2GB{ub0;{|xQZR7<^tNV?KG zAde4v74=>3Y;hB`$cs+qK3|L=6E0PCS;7RP^~cq=WbbSJ7}PhM9w>e?c7ja>D_6l@ z_n<^OiXCeyO@Ego*6GXFY~YlxR{wNTRC0UQd9~_h3mp>|^Y1}!5Q0}^UdQHnI#6&p z(D^x8XKAXn2N!}1@ErF~%bOpCh?Ju0QZ_qy1K*BEPf;ige z9H7_f{Jws#yBOeow+lpt+uZAsj(36X!g^kS{G6`m?0>jV-|PIp;alo_K@7fD@9Uo5 z7wvw1Z~pIp_wIl0RGJRhtFf=y16Ah?zkC;LIPEL-K&$K<>44UnZ@&NCk0Jhj2XuPC zsmOop-AryKe*;}nTW{Jh6n@XIIO4R~f0;eaX0T{>){IcOXvWqWpCpy>M;n55BiI1F z@n-i52j7g*bSdn;G2r&l_KiQ76rzJ>e1((o0Te*0UBtLF~P~$BTR7ncxk-B z;SQP*Tx%P|gU+?*qZwlpiEnQQ6W~7f2JY(H#(&+zMADgNNbCsIduzeP#@?MHcoU+x zEn%c!$96b(q~WbS*rUBM(E1cZND%ZK&;jX8;pjTMr@xtOozA<#hCYSP67=pIu^6b> znvu59$5-bem|;i|!J_WT>xa5&hNPb;(0Gd|Q03CQ<7X!r(f;g5q%<8V>l{WlG5$7f zq<lg&jT^fpZvLYIWOkO~-7Olgs=3#Oo2t4h`!>2riFMY&MO!dJZ1kI0HO;4k=q zddZ4{s1mkD^@{XCCabLy&r1zUS>zmxbB@Z`yx>QR7|-3uh<)oLEDBXo zs2<5(I>We3vX9g`EpP&e2&6p{CM2d5W`}6P*NEqgDJQ5OYrg_G;_xLW9MIEd553@| zx9ypnksB8x%N2#CNs()%UKy8)l6$M*y@)b)!%7>8X>gIfWbrDT&qRH*}0`)7PNO9V1Ka_o#TT133%Oa zYHhrSDThagzAh6~MXqTqtf_a9@ubdK6r2xugBPhox3)eIo}^^sFfz-?IJkzE*3h*& zw^>$qzqHl3&RCMs8Y{Dha>L0Q@5n1*bH^f;*^bWhf_|HedQF72bEnR*!HCY+oOW~-CGt~b@_(2~n zbA=>RKqim@vJOzjZH`K;j}r0~*r6(M6kT6T-ru~9q5%pMkWzW8MIQtwRb<=h=|HAL z3{RU?Y&&sBynj|*w<2d}`yz$*w)I2>SQq{QHNVYc>WYs#fwh}XD$`Ay9AwT}tLZ0W zRrf*FcE#@iRqwFErhip%-q71IsN0wUI!3K4LNjaHNYp88=gHK@$eE(D+e=fbi~R!Q zM?3r9zoXZN5US|ZRa#1$>mc%ipxM|h{&p9J<9UmK3V+c4!?Kd)BI4CeMD8niX57lI z_@Q&(Oi54{DOSKQVN#Q-TC8mjOo8^A`xygXN1Bf2FF&D)GRm1q$z1x4G<4G__;p%N z4RD~hEO(FxT}5$Qdnc|`cN+C*w(W?JyN#(!tI@00t-fvlAQ+xvy*)KE3F@8az-hBS z`+c>nAb-0atu{myM9O_5_cp(WJ!_7|ZmmXDOvAR>Q4_AAdZbW`&X123z`G+!RDbq+V>kY`3|Aeqh|PLy_yfZ)fZE01dSV3d}eRDxlf+ z{sWDUJr2S!425@}!UIx4f(uX(LQI`2wtp;X>Xt~U6D7`2A?{A0{7guAdGhDyXYUT- z5&&2al!7*d6?bHWT2+rj=Xq2oKLpRjqH2kjdsVyK_Y_Njoc{42n+bYoiPF$%;}40D?)G+{-Dhv_<<&4+M6FifEMXiuE(CkQ47v%NPEM|uvoN@x z?1alG3}BEq=^M0$9#x>eaMt@f>j1n&lvZuWufoO(@={J_K7kqVmZ@P`<=@K`P zANnoVcj6eF2A&&EA?pX=RRV))47M-g1UYV^eQkYf0sM>73*U3WEDc;D2-vn82Do|Z zCRWVEtnvQ`pxy4!POK~WttAUDUVklHOo9+Cv5J2}WP7nK|C(A{N(T(3z<({RxqOsO z`*Ij>2CT`<@iAP3!9X9iqo_lE$pVF!Ab^*k3@xk8zkt*6+x3@#$?3I~0dHE_za~UZ z6k%8eI=xRuD4Y2SxjFIbO?=l>30Xr|gvyVtcJ}ph;rS3)&wrmid-4ptDzlOF z?BTAH$)|6$>5U?SHsE%x4*>mMHoOqh#*r=h^g#Qj^Ehqy>(Pbc7~{{Xk+Tp3=`L{C zMF`IAIPyKr7If9s;p&3*%`TW69gp7GCrA5xV|#bBzjt8oY#)tJk4NCU_U-V4JvulV zf9?P$=HupoG000B8Gq0)h0o42i)3`_0Eysb9jfA&B=_IEWML@@=pqdLg2?49fQNIt z6Sy#(8L29766-J!Pr^P$WdJd^Da&ylp`6Jy`TiXgj5!%2=PN|9bK%31pp6`aS+lj3 zGtL~iRAEbyg1;(^LL4ef+%`ykU*(fG$9WCzI9U7;Ob%tb+ke_Re7j4MtNv=IURtGU zblIRHu?lU=a;nzH$f_a-a3L6LEzfcn6vE3ImL-=Q3MEoD=tz_0g-x)I!q}S3krOT8 z656=xAYo-0l$+oOOm*o_{N~;9_R;&%!N?wc8XwyS+n?Ti9*<6xrh1NB&&lY>39bt^ zx}Z&`h80r+$A6uue=jOm68eu6duJSG#Zm>KS+;3FXqleaQ(Z!HX_U~pF`E$DG;SrK zg~AGM~=pOag1WIc{&?RklNQ} z83)5GRH}mC3#Rbu!^~O>Ae{QJ3us{$&So)8FsvJd41YuXu80h*O9L|S4g>L2lMJje zBSU`724u(?%MD#g3aNNT3ij&WNg*v!NFlW?A%)(jWR~MXj9E^&q7Y_ca1kI0x;6pz zKnW~zb|~Y7={f?O+Nn+-)!nX>(AxE*8@yw;D4V){1HS{s*(KsAjaJw%WXj}UA*{giP(HJ(8)(9I<8vB?5%5{^h-&=TIy_< z4+q^4Dk|egp{=!249_vOvkR(j-oKNS@O$V57NwoQEA9fZE$Cz3yu+!A$93K9FTp-4 zJCiEK-N#l{%?%^ARbdfJ^Epu9oQp!O+^Q0v>3>PHF3%Y;^k;RK4_1usvhZ}jLAoXO)|3SG0yNU(7r54oLojVQUG>Sr$ zz^U|vL;XUA)OX|*%^G2|V&kYjO#K2eN^TjfDiBshdW4|4$&Fv#@g}#vsV(FW0e>vt z)PLqpJ7oK?c>4Ic$t^sI9qBtRl!rMp6fUzIo`iqPp%pNbaX7x@u^d?1uw*?g#eBM^>FE8D{c&`BviIT8-rEJQz{?H%V;g6$#ps0J)ukWqxPQ(X z7nA|qfU>ocI%^7X1)0Fyw8K)n5)M3<{<8V>#nYEho5Ab)Xc@K-b6na2uSs(&saSW*CgFx}rAffJ%HVA90B1P9>S#!I}fWQQ%m zvjVDdF+rModo830;KcwgqvX0`T)L6LDno+ciakGmCm!A`9m>XmCbQF0eAz=|(u+;OeV>+DT>Wrap=t$!+8fqvD3 zyg{+O>V}63T4{}~W-+DGE({j(TVH2}*Qgtel6#f9^{=7Ose#|s$TkVe&aGLC2B!qP z!$3UMJS7mCkW+&Em<>(|3d~z|HXv2b&IZ`4dp{dsYUo>aQrnVDrS~a08{k5WvjO3R z;zR0tzly@uE4|mzk$X+GPk+7zn2qk2tkk25eztw-e1U0XnRj#f&S`A3@r*jSd`6kP zai#|_S%lLa$M=;9WycA~1d;|ZL>>(eKyL8a_RjwH$%$<@oi+8koW^3sbz&tVbwjGb zp}DOPn{vW6*Z{v%8;rz|%L4vS+kf(`OC z2oY|RC58eQ-fXv7vSjC7vrWnpOoTTRS(L0U27@pTIEVMa_nlsXXp_dcAsG&ZR4|Wm z2;N_uB{R&TAdbGvrGLSX0njhaqHQ1$tZfkIc%j$wc^rknMIh+kGEYJgu>_VG+gt~6 zEVUggq^Efhei_2q*_jn&t6ZJx!U#CVOJztOA%D>loFgyde%LbL!pR97IdU_|vS8QP zi8x1bSbIEwQp@H7L<#wtgvgfQUB`^)8A_So;BpLJ$nwN6;D5TV;g|f0b;iCzf?vUV z*rhnhGL|_Wr_3Z`>DYf3p}}iNDcQIH!Ql86cidyxUtV7G4ZtgJR7V zPY@GCr=`$P>L0@@+G28M55f08Odj6dz4d(pYK6jI;5=XXg1o6Yeo^_oub!;<0RC7- zp>cBzX+!4pQhxzWqD^=m7jllyiGl3N0ay8=G!?-5xOy5T#7Q2Kj!^|YZLUL& zcw6az={!zZ3^Lt!ygCxt{4=rVFLhesWM`Yn$YjN8vwZ;-3DzkbjcSbfsC9<2?gZ1x zv7i3io_2Hs+!~$#GV)Op#0UJlfF$(ijKvM~i$i3Zv>%oD&!1&+8dNRoMDBFt6|TV3 z%<1E|h<^+2bR|!^p$=0OP+{_~Bg>Gpcv~xJWw<*mGo>}-mA!*&j@!`~W?sZP;stDd zMP$l!6^Y1pFDuHLs2ygO(4KBB5w6Lz2ib_KZ7giMqg=1rb2QQ;$CyZ7HIRCBq#E70 zzJGK3e&YLT+rraJlDLVeZaQh*l5IyGdiX7k*?$5ZYEb$s^gbZx!Ah!juw$vo^Esu| z@zmttrz~~UmOF&=A)I=&7h~esMQU(icUsGeF7u|ij8TDkQ-E5}-t!!9WyEMR$h7Y9x?rW#CY8p(bJ8@}QVmT@a)Bw?{eP?^$YHze^Y6Os*$ ztADD~0%BlUe@wc4St9eIqHIVf^$*5se^i?Y>qTVgtE4dknmmXxN9pUKn>#)!T24Pp8$Gj~%E&UH^VFOy z87gJOpF|z<%Z~`~N2rh6+v?sta;oGM{8G3x3t5WSihbMPG$ufW>E{~jaD;m3QhzX# z*fOXS*o$fJ7fs&@U6c8~)koU?8Q7zdP5%n@vX}OQe*ooJZExE)5dQ98!2&@f1CHZj z$S^cjjUdgo0BLJ9F18^G3`M4FViKi_RO774f8X&rgMd&yHm7stI`#yYHX&sIK8IEiU7 zI;R|`0jN3NS;jg2=7nT_k}k3c7UUDhJwNmqL#Oc^Phvv3+KKRrV>)N(I+e5HuGZ&%QK(bOo`dk{~csmeYr_(6@Mf^wtr=zSB{e} z+uuj8+$6<|S>$nlXL2HV z9`ey1UlMlkE;m`LRZAagUG|a$U^3nghr>OV1p!(0(VHAPjoG^MLN=)ILpU#`_;_tD zH)H6a*XW&tTtChzw{!Vpq<@E3qiZxC1HPcrB~cQ>F#)avGhcF5Al`(pQTWmzrj$TwjMj3o{U4M9#Q*#|3!(6qYmAodIvJD(?)4v-~d)#_ijIU=rrKL}sirL=Yv$6-zU=@p2; z4d789$3Z0lZk~`K}bcb1tk;^BTDNl09l335Qe5Q*ckD``M)fP7FD z(Ql>e*9@?l#(%ac7~1Hnf@BA}R?32|bk))%T{m>@Lf5KuTj^>HF447W@s?t&GrRIr z(wfv6*Ycv^0OTd{F^Spc`Rl=`NK{*j3F&I}@@*pvp>w?Y6xe$d<9Ub&sFbJyw2>gs z7{1k!M{U`GJbHs;Mjgqc7Ax{>>fC`m8_t>=X&Z@h!+#4BsZZN-o-w`FESiO3Z3#k< zvq?I*$=bM5aj9U8D2Ua*owjXUt||j7w+I*`tQR*i=|HawkMaK%#nv7EF=_T_p8Xd@ zQ+2v6tk|TI5vNP;2P<`dXr)PcTI`RQmymF0vD23Iq`IW1eirS?n_ZzcBLp`Vq++tE9Bq0Xl?wIrhI+x$uD5V~e^5%iGp2~|y-$Z$5 zMI@T%3}%^cWQAW-*?|oMqq`*;ZuKjXj+xld7=H@hXz!lpZDZ0XDSGjSWIN)@Jv|ub z^>#D(qXK^eyTD;olA42LMqT7K9;$3usE^HD(NDoV2r9DI4z{96TnX;v_sQ)1<8=1n zksOVPDaY!PPsTaFp=77QM2Xk@thda7#IXI!2WsSId7cS7N+f2Qt2og?j-}9ELQPK-*zRw*RQT=;Tl6dhW4M>b$!N` ztIP|!omUj6P0b&*Vu#0--r$%~$H$eiM1MT4Hg)dsxTnuexQE`q?ObhBBR3NMD=O#eD!IdE_fx9& zQx*tWCu`s^B)N-Ilsu!c6*SVUUNBB-|NC`I8Z&@jKysz(uJUEqjMR_1)vwaH{eMUA zZ>E^Dn)crH=GHdm-Q;j`_?uC(AD$1Co=px*O)J}f^Y!E~JpH0~(6h^i0hOBm#tI96 z)3W52D@nzLRt?jZGt+xlQDN9YPqR;sYgYE&{n9ha>4NosJ?J%^iGMtB@<)qptmbmo z`wWZmnki>HW75@AreWQZnL_ghmw$%GS93U`&5!+B`xLBpg5#bqGZ~T z1-Ek&SSE!k8Cfbv7F1ed6{(%2mZ6M1V6NFnC;2DG;+j@F2G7`iAzZl^{(qDv6&K7r z;w$2N=79|y)2$GZ@U={ThNnW!%sRaR&t0v&eU-W_ySLAIcq;n`S5rweqa~dRMv#aA zJ6`sIw<=fHR4>;fRbYV;XdLFPh!4nO&WkxIxoHGl8e-9&Yvo`kRpm7-ts%fLDL~gV zrI!cf%OAK9tUMs!YOS>YV1EkQ;D;-4fs&kq6a1Z=DNByk9fKKP4af*)f@fGJOPEoC z;_wd*dqf(i8)caBl3*;@DaQBX^Rs6%6s>`93Gg+=xr9#9Bhh4{EC+;wSu=P*gS62w zs4SyFm%-BUfn~;$O4FJxl)fbZ)i^sK*M?P2cp;oY3xkD@hv4-34u6djOmGVHUQ>#I zzGr}d{1m{)5`d{#{tlaAy})x@i=6_0xEg%~{WnV6XDSndM0rMsQMd-!XS9wqIHROyK2=nggI&q_aRdxvVLsLjHlgj%nKJ^0!Jq*R$ z_bITnWuwrmk>ifl{x5AJ;WG(AYh?IOV4Y{Gby4#IGaY);ko~w4ZIZx5_B+$+ zA#r%-p-6*O*46W?`uq)iruX6V+c$5Xg^w_gtis6H$J?PSA+N($k}rXkuP_-E47L=4 zuusYZGJl>cCrT^{oB{vgom!z~EtW}p;xMAY|4_#4pWTqd)|SFsjdnvh?;ScCz7I55 zh5-MvoBPqz!01D8d20VNp{7*ygDtH&R6Hx^H`o3cC%b54UmW5_9xX4~jNNC!AM`?D z3Qx2L!}Y$Q8f>;^5YD#5bohfplIh3Y?*|`6eSdt@I!F>R9#f;rZl+jU#ZyEcl7w2! zavD^f0tV@viZs&7Ngugy45*M=r?(XsTW}!bOJB05kw@=MqDA9w3)9zA>()+FNNaVM zDzRp;+49DRku(Iey{_3pDU@mw&6b+PHJw~&nz$+);F&aSrh7eo97-a7Z_qPpuv?n2S#F=i4dtdE|MoM4y$_o!T)*I)CFiklYp+^T(z=F zDwmJ%OWPyRx~sugT%_#j1o@{O9ou8`tB>MO#FjUKq34$_u!tYJF^uYBzHY|ncz?h6 zZ_Y%S=CPhMJ?4DpujusEJn<0Rp+DE&M6Xnu(=lU`xoC;=SV;G<&Mur~9^cn(Xf9@6 z9BMZ=my2gg$_2~{irL}$!cCz<{1HwCQ!cnoD_J1ESco~S%*0upjE2%eh)c^{t20Ip z$hl(79 z(y5@cG}+Fa5Mx@gSo|!H!9uctM$FDtnGCZ!*IhnbvHn%Y?zN2dm)C6Fntx2<8?#?} z71stq1@-@-F0pbIyngh;mdDi5dGq#mRp$EOdL&eFdzo=rei~m=pgrZ_cwc$1>nx0w zW#^zA3nf_=fRCl;`07KJIY8q%1Tv7vWU=EN77BsnyBJ?RKzW#68+MH(u^94(oW;Z$ zTbu`HOv~y#Eq;$1S}-%`X@Bom#tnXJEk}C+A@fy90LilDrL^?kf6p?_gdl>`)M5~b z(+E%0PjfEPDjQFkTK5|JzP-puqCSEIoJ^d4TtMn!qKzSKhJzycIONF`@@xMN)NlGR z9E)e{2e!n>n-AvXZ8nG!u1<@3k5K9 zo6pfH7dG+G-$fCLd8dPcxmTN1w_FFb7tOEG_MrY1)_!c@0d5b50OF+;9vzHP;eyz#)9X|Hg5KT za$W8r_jr)KgdmT!pMQ{X&G!{$ACUGVBj0aqy!JONxhVb>TTWPvH?0{6AB$6e36p^U z)oR+AF3?|Q(nGa8)Y+j6P#|!z!~;VYV;_v%b9m(zQ_oSqgixr5R9f`7%dz}}@zV;K4#>gi1}*X#8FW8n51#~KRd z$W07wYiAfH%bS|=4VHs5tS;OJGYG@HQR{lQ9>%vaV6|X!+wcx_y=`-Fd>4DW7TCeP zRiaH{o2R%oL4U2E;?vJffgSieCT$#sa1KWpdsD=Vxb)soQAqzzBF48H?G4FT)7ZELo%@PCJELT^5c$D2!ig;cg()A!9`SXU;t&H3z0#T9IlxtKTSp8K`o20itc#Cvk=FqXl2G0ce6rIu*$6&%Pu7s{!fv67BEyeNPhmUB2u#IGg46q}=`RxFbGPw0 z{9elXTIjC|DCVw^z@M~~?^OE;cay*3M+v-|eCt^7&(QJ-87a2(PI{BWN$-Gg8}vKH zj&Er|!`SIc39#$%M=97n__Q%z+xSZ}6uZI=MpYfiY}iCxQ)A=r_}np}8vk_wv?Ta2 zIea^r3?`HEpOZuXr)HpV!}^1N#0M4g9fgz0qF`=JMxGAm%8BLWS_B=~PT~kBhltzoOqP$vMMBn4vucc4 zHI>zd`n=l7_T+!2Awt~MbkE&A75ZtnLVt9Pv7!~}r9>KW^FPT`6w2vsQtqqhAPSu1 zlHE{K(?~*ZmdZ5}IpW*vGLFb9-E~Kgh_#~gJ@A5Nf_RVjupouUK5g>ofW`bmb8W>S zV-t=e4p}yJ9MMvKUy(vI_g09G2DPZ2VJR|9Hd^Yl#|oo=>z9HA`;~lP`q$=d@w-HbXUJMk`9OG){;Eqyx)uWMwo?T%rbxrPka$ zid1p5T-pm2Kumb|;}33<#UV3V+p3OOMBI9m zeWtK@OSV)7oo}kGXwmz4B8~WI(~}bw(wVo?`{Jp_x&l){@u2b=V|8P*Xp2Y$h^t~c zuY0ZuTETy}yf84a7&^`;W!fg zNa7x`Vz$Lo5O2CgGe-a0S{?5r#t|Fx%pnTnH&(|K8?om%R9Cn1n?pcB&~GB%m0Oy3 z^Zqy2R8`wNczRbs2l1bQBaYrYi^yRPPg5Y3LDeucSuDy0wCfjP{EoY1-6K%jT=x)ibES@~Z*8BtOUyu@S_8-^=}= zdl9lvV5p#!5ZF};j29&kuf4G0bNN@_2>Du&J6m+O%9U;Qan`6k$YA7`0#kX*Dc#^RRxL!aq><_VX9|$eH8)<>YX^C^jy9 znE3H>IE~^j;ly*-h%|A_57^d8n6JjHzHKh57Bi>0(lrM}G z&AHdONb129t>mS#2GN{rT!r{i9XCq4D@wGB*g+q*Oy1TjTwWL#%=Ufgo@P8k#~N9a7{;hnstr!&P(#82v627|lsm+$NpE=|&j8;I?XKO4%qjnD4J z*n~P4FEwf^3Q)e>sYozWpey8ETnu-PjvP5eR>fmeypoT!goK9Jz3hXZWol@#m4_3k@VNvcSeRJAyZIBaIN%4f zU~yY=sC+!pkKgzM6yRs$QG-0K3z$K2N4Z~8h@d(;sZ#vdnp7=*Bq!f4ek@C_6F+=Y zhI1;v0QthdpUvh}w#G_q-IFdl$#!92*}I`8E@QeY1z?!Gha`x&WYgjG^8SG!!QO6c z!dFf{(tD)Xh;EIp;j&i&zbo(W>LEYSHH0@k3vPDV+-{0|&U!Oc>ft8F8!_h0=FfS{ zK$q8y>lP%qPw&=8{FvRX7pJuols9|Q{MU(=#4=`qN{=8WAL}mM-V6w0AHLb4JJ++6 z-je)a_h><9U3U>Pwt;E*r9AyS_?8ax%5JXm@ow&NzaCMd)Px=*cx$VG?x$k*}Z3hs8iaL^x@5(V|nEvz^d1{$1A93lAP0v#;v>;IjpyPQ4H|vJ)BzZ z-B0{j)?2V}4`|?fTP#cEYcVkTy?xoT15T0Z2Nb=`>ysL~l0=Ev{jIpwN)i$)KiV%q z?$_6y9Xsu$k$xt-N1Q)-!v=W*3`cDr|FA-X*;vaLLH;2ZjCk?j>qEFPKoL2wuY0H~ zWeAAQL#OXlnu&*ILfO(^z&rdNECv;E;QI@mxp$b`*G=Bj*H7vUD@JJHi0g-X{Ufi- z443hyluEHaIkkjKP8Hg&oi^NMDjz;kLL+ZVCA&VJO0g%YU%rsl+g0&;)^F=nPgw&&P6WX(zPyv z3gWOehbG9|B6u%Y#(5`S>1>?1i+E-GQ$4xA0WO%&87HmUqKsO~043xPpg{HIfI*TQ zX))r$sTWp53MT|qm=+?P@ez~}=XCP^M2?yeF3V}1gaqD88!di>q>teu)BugHxob-x z@Q2GM()`!}<`N{WCI%2^-u}xyTs7gY{BL>?uhs}R)^EK12OgkKq=m9Izc~e}k12fD zW)w&hNevJ`?yhc-4-{exp)Bz?CrSMsB*NT{hh?Tq?^4o*c=XGFALKpb!sWVbHQV~5 zQ+syXk4|3dxoRr^VrHh)!%O^u=rL-?2`&I@`8!TI`Klu+S(Z}=4(1#$hK^Q^_}Ww6 z0D%a~%IgRENEy^H;uE{24dAr8N`KKs#JU}0Lao$a`oSq!dIB5*GLHD3dBH9&fx7Ha z`H?J2>DFYC-u<0LCOv?a2(jyr3`c_Lf)U{#^#&`Q3jB2m4D9ZB|SG;u!s2X^va1~S>ze32lW+0 z|I(mT$rVyN>;q`ZH2HdG280a`pi` zhd&><1gyW~V?zQYH4O{mg%?&N!7##gwOla7L+TPC8blnl;jM$5Y{o{2>UWNH@sxte z=OFG|x$SScc63L^Z(knk;-^OWaBcZ_NQTq_l4<+{G56)=$&j`1vZ3mZVN`5Uu9zyM z1Gx>41?+}F7vy-TdXD%+{Q8^P4ZQQ%m|Qs2SIEdPRAjaHfE4!iU7IeS7!WR>zs+4f zFf>g1om2pE%aNGLFq|FPqKPhUa_q2R=?D!6V)fL!?*zRUcUj%LPF`$zs+T)kIl#q5 z@*{a7KJfFOZ}5H~8Fu9zCqJp0ZX-6PzAb?v6P>7uF8=DX%BZL0^_M;-YeHPHyU$E; zC(UiT$*<=5N{>?!#BGynGkB+YYrvMT^eEj%EIoQ|5;%RLkWuF1tsbbT;@IH>r4=+- zi1&1f_K@EL-*Nsn4|!3(n#~;mayT(Og3Ix-{0ZFn20t-4bUF-yB#9V7EDc}o%?Bof#v1XHWA7Y+(V!V}#E9Y2EH}{v;}^e zXgCo6n!95Lm}*B}?Cek%e|h2vcgd52BH|}0^S`u~KtfLzK<@f1(U9jAkzYSpBy!gU zP$#;RR|C(w1OwfLB<2?vN%xY%A?AH`@kLvF^?JpH#8xtakE<4?tE!$*_MCy&nK`tXYcQ77*7LT(#CGg>_&fK1n8 zjPTcA0jt^Sx7>L2M~Rrmy0H_JU3}E9(2&Y2#*m}^b&POGUqhWwmh@ZCb9_-JQOIe< z*n!d#ij0VE%WHlBW$Z8ckjxOL^_+Y=_e&rdH@3n>C8+~R5OULPLj|S1w+-T5=n8jY zvp3zryTB`)d(tUPy@(o-r;ejEZ^yVaX(*{OVmptoXK?G<_KK66+`rUYT0nhA93Aqm z8vJQ8JN~+pTCFSu%Zp2CB+r!QNT*1yh!cz6FNN}y49*>&C6!Wcf++2nmC4--Z|ll< z%9evCnCJNW0J+dAa<*J?0ax6gT&ooW$Dsb z(oDq7_cmlgxar8CF*U0#ba9sg=r-bt{=1J`trhuU>xx_gqzbYL#P)msy1SW|mo|VG zEJ_s3Bc6J(t5IpdO^zJjo*U)@>M!E<$GevBL<we^MX^ToKY#}?n~kBsbbAl zu-ZhnXb`Y_goK7TY|ipvxoef4t$)TzEq_%h60|$Pp-eHqcbIoXs&IcY$HhxdFnCE< zC~hEz{1o?;h_tJmTzP%AdzF(U?IQAs>kppf$c1uksfi~clfKyE4cd0G^B`UrzF%WM8T?Wg-PV^5vYRwMT}Yd$(ul`34Ve=;s3P5BNoB2 zv-OG9wY7Dexk0cnUTbbd8uyYCA;wL(&w5xO{sts+X4dCS&jW%;vU4$Z@^DU>R}s`w=s4w(oo}V1yMqI+7vc116_o)36JU+2FmlQ_xpI z&?q;=Im3HjRhR(3e_f@*#_b3NQbb4W*lTQ)PN5Wg6O?cuYJ4!^orseco?Int_| zJ!plaM-%+eC4W_^P`QypDY~f$wGX69C>O*|hf+E#bb8#NHzmf3smX&H>YEzO2K+su z!O*}x!4C2Si2LKdcUK6u>Rv^8#S{ZK+${WMC**QcR>axQY@DDFiEGJKh0aPP(`+tp zjk8|yKG7xPPYEEEz8h@(C3wJ0ofYsrEG zazzG9ZG*Xj$Ray;U>4y4{NRs@`9AX;^HXFfuWW;#6~qtX?A`Yby%~NKa;^C}jX|IZ z_`x5;b$?3=xvtmgUUoxWwPB_MSuKM~C~U+q-SOcHu~uVlBdd}r=G$r;CQlLMP)4iB zwIROcHS#fqiuO7zjjT6TW8HIPm549BQju)q7H!(hs;#N3wHWJd2Id5%U5E#l^!m=m zwb@h@J#Q_>v1C1nS1*0|io$X$uTp2Otub1Jy2A!D{XIPih5(MiN}I# zh;tP9X>#g_Jqn6dO8ur*CTdF{+fZdNn+<%C*Ve{gzIWtH3Wc66huf9_7aByTg|Mb=}w9**VP+euLuC^ocBIyHS$v@lkamO;_IyAD% zO%-rHr5p#v>I2ObFA?X>EZcyLP@uByOwP;-LmgI3&NG-M*H5wjjRKxP1BB?3KFviD zPv7f_$4VjDs_M$6Koe4Zn7lJ$^uznVMRgAl-K#WNxKAvCBLU`0Ik``uG=_LU+}=2K zrj1o8r3aQ*78}gd4Q8bs-b^u7L|p!O+-<1MBmkviR;{VZI2rgvK-Mxy@`zcTy);UP zTRD*y6o%AV>MeteVjAX6J_PaTg=KFjMD*?2^|g=l3KdYG`r3-x>g2+5vqhvkuhC*6 z;-i`0EVr?%R<#BiP37iBI9OOG>Xy>#A!6K5+OrA+aUco1#>K z5aqWElrxm_+k-sGAK2F%4u`wSCv%(DQ0Ea}3S(c|_%fvqs3@G%wTjI4Ac_;!My&hb z@z)j3TV(?Fv}_H)P6sNu4rokO_~VOHQ@_QK*WX^(1Un@abuc?IS?X%d^|l-xg#8x` zD~OM-y5{OY{RDw5NYZR8&LLGoyz8ku_O>YqrIMX5euy(}szThqYFex!h*p+oF9?zJ z8mAi^_T_`y$R~UN_|U75cT=d^%#a+(W>9%`^`!EOsS2m4B3Tsd5C?UJJ*MW|#-62- zm5v2Fn3NRp)XOtFD!jBx9_`5$8Q}1!Eh7)Y3M~#0U{1Tqeb=v02(^kL_JsJTVNcdm zQiB*fWak48ER9qI=0UX$cG~d+4Km`=y)P|Q+P;B6acaORk?%z|M$?ER_Ho|!R79J6 zV18*dO|}(+GYJ9l;k;!B6h7MYTbM>vgU3v^nXvYxxh7)Po&zYzEMTTxyUy@`hknCK zMfrgp=|X7);>C}?U=B=9DGn}2uwkeBKS6UiqKLsx9Z%UfZcA;&RD)Os0H+cn=C}6md`LVhz8=c$5;1IXka4MWY>QiB7=-G4V5F! zMmtJl$bTX-&HGmCZKc~bghURSW2!JW@_3BBdWtxDN;3j7q*<@|C@i(oFB?M5g0Yio zf8q)8bj4-ZP_|CmZ7LVTa}xN70++22%Rw4X#C~zbE_jlK;;3Ck48X|r2V@h7)5b+v zk3hDvN1NJ%D1c-A0bXMG5nq2~;9#ZCwnNz@4j?vb1GsNHrfrG5-Er4NM?_lE5PcV! z1TwM#QaIE@#Hh--P6`b$s?;`>YNW|H(~gr^a+HW=8$VxblT_OX0IM~(yv$v+jA%Gl zQVVOQ4n#hrysoY>r_nGF+_R#|U=9NtjMa1`vznFy5$C45uT^>wcO9z$Nj5X`1&uvm zQ@f=rhipPN^<FJSQlt07+O(i}(akP`k85>U6ZNJD(OM>OdX9DGkoMCnm)Q6adN(X7F0Z}uc>YRMxT%f$t;seaN$$QP- zQb{M#JYv|qZ8~{cqiEj85+k+mEp8(A+j3{2oM}mwz9o`~mnSV+Pb6bQpYs)^5wATN zVO@zCC`;3{Z2c7HIJLo36p=gEli(Zc2TMB~FT5z@blb<^LKwI3q?0rKdXL6U)7 z9%3)~-4XKkzXD}oA31cFU!CbCg_9f+PajJ;!{u^fW_M2W1=ub$)woWwc5Ls@(6Rd| zGeKM*Tk;hQG;JPlZ6y2foF$GfZv>{o-|~D{uKN)7`(}7E=d-G4=^>g|Aug)=@CmMu zD7U|VfIaE1#It;17Hu&Pon0dJ9x9}XIDY%z+d=))q`jo^h%YSO4M#&^>?Yjl%T6qE zj*zxcGl=1Ljr`eq_xvSQgmgMm5JZg8|EQ8P=jqtFD=L5W8PY!a`#b17@US^~(ll}? zh&vDdGEJU$Ay6KFryo1_uPRWj94nH4nKNAKMFK)RKYMi%m+6E%g-m7ebz@6T!chp~ zb=L2|&2%t%G=0Qg<=K1X^LOS+50iZ$E=rs9EgxF!-scOPL!||jC?Kl!Q>TDLu=9TG z*hQ6^<Lm#e+KSB2y6Ktsu-z}^Z+qnNUafuT-@Du8K@ok zTjei(3)+A@BKE(db2$uZkbG@!H)#)@l|X#{=Vx|-C{v}oNC=3Ye`nOl56!zRG>n1< z;Z_R)TS@=<(mKAh{(IYmj!s5!)o_w5ds zyWW#7Z6diLX8pb6IT+2H&_ptB#K~j+U8rgKB0FE2ByU(1ByXAT#m?+<_L1HoB|tp!cF%5H@wUu&!!COiFOJFgg2C-4m6OyEf2(`zS*1n!-2ML2LQ;7| zWACux{HEyvZ?=1%v!8UCGzW2c&5N<}mlt*lWnDJn^q$S-(obvJEc|0ndc#s|n@q8#v z`kjvIAs%b;hvyRR>lmE}PrsHl)){c#$frv$Q{ICZ<~r^V-ay?$;nGCv3gRn$OKLz7 zJIY%gijY>*1PSrO{g)!-4eLT>?E(!uvEMmZeJ}APCp_F&nw=QJ zV6cS8iBNA5&+At{&JB6(f_O1LM;7$p!_W`XuRe>DKjE(F=t4i{?c(Ie(w*Vx;>1VP zqHOge;!MdY;AZc=7msqQhqChTkAzB(kZB>Ve!%xZ2u%#ES6ayFDy?8v2J33^*ZK-pB^bSb@ z@q>PcMpD_lyH;$?QU^ou}$V}}D$U=@#CWJWf_+9DTM;v>iz1&5PkbFt;5c4e` zotC}c36_sP;mT&er}9&$kXw}($f?q8h_6N--N!WqTksy?94Ea` z&JwY>K481j59U?m9HEXP1(NT0B17ogwkLXrCX;_ed`2ssRJ?txJY;?3SnJ7_1tbMT z&CUnrT8qK_0Z*^+VNd3BG1x-lf7wmU?xT4I;%95OZ;{=e6p4Ek=z7hut4&-zUVhS# z8J=<0s-GeW$PYg`R`B}MlQcOR!o>0S-$0+|J+q0OUFp#5Wo55pLnje zNV2!5Q;ZZ#gM&EamG%>1a6)C%Q!!jGil9obMH5DX*A12zF7}kSKIJblGHk@RM)jJ( z4f~PBUhJ1j=MbqYFwV^$F!pGlEak3TxR}mm)-9$(W7`+!h)!spPL+bGYlx5DcGt&z zvQx06JxkdG`Qt034~Qw#*HSMXTST(Ujj^K3sZ;{y-cSbZ2*-5GJnBOOF6kI0}g8-D-M4T7_x5L>bxVOay}(mV6?t&xz+7Hk zY^WVV_+qcNmclT;PJ@gX_0!r^t3;B=Imb#R#3Ev+4<0zg z`A^!Qk@r3q!3F6IODvun=YP!u$<-Hp)iBI32jw(jR9yZprj;ez(PSR`$rn1tAQk8S}M@|hR?Iv1E>1=vuTSr6?^ zf)^jb-ZZsTsuh}#nD_oWW%7k5gIMl)RUDgpP9+|5tS3n!1}*9TlDu$rvGg7Z7;$SQ zOtc`?!FoNU*+dbs;mw$>%AGj&&D}8d@FgA*uMYjBi0A7`Yb1HkO95O%%0RI?bX@~S zyus&eeaSNlg;Y^*<=B-xJ>uE5t0wZHOb%A$!GR>TJw;i> zr?g{~P4aeqMBKsmDBF zu?x`4j?!(EJt02$<{!!2MB#3S;jmC_m6VAjg}CFIUok{l(b0G*U z(xpi>n25bLj=T!inkl_WWDq|Z`R;u%BTAK2Bs0W^{9k>PhhyLMB9D1781@!tdN1P_ zN3M^Ql%-(AzqT3Dxfe*<;K6ns1tpQm3CercQ%Wdpp!Kc^8*;cB<4RcNpJxtJMNhW= zJLf2A0wq(3w@<4+$Gtz2os|J|?%vcH#D%FjySRltT_mpktrl0?Mf|N#zgxFdA(Q|wdSG=y1_LF->%+8ziAfN3aD~ofT0;FWR zi}>`#XTIgWEB|${@l2;6dHfq{59H@rFye~E{p3gQ4wmP>>A`#kImM`J6ya=sW03SY zc~is%t47BOJr;8GXNm4k5?l1Bvx{_uDj^Q~z7^xZJ;)dS4x$pG&)LFyQ4ml@F zBdJS>>e~)~#0~8DMp_Z@cspCNeWrH%ho3^UyfcUafnL%Giou9`{6Cw=lO;~NrZ0$i z43U;y-{~dw2i?WNKr9+CbR{PCa)(WB@|1UCq{}14O~lOn*BA3qUjI&%6w*c9Ks^12 zI-biFYOxbnArE<|zqpGSGVlu{w~LH-Bc+%WaRYJ7z~19aPI}QvEA@Z2E8ZX1VXH z0;EPtvJrF3)28rh{WeovzkM?q;oF;wrCBsOh(o&GKN7-Lki2w@#3D+Z6Qz~ph7dO_ zGcVw?U2JB{N2)0G-K72UuJ^

sw)FSg^RVf~zkUOa2J*ztz+PqUzNQPwvZ>ZlT4l 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�}cTD+G%!8f2( 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 aa=q@O28mZrjnxT4Ve6O}7#Q50e4PM+4')) { fwrite( STDERR, sprintf( - 'PHPUnit 9.6.13 by Sebastian Bergmann and contributors.' . PHP_EOL . PHP_EOL . + 'PHPUnit 9.6.19 by Sebastian Bergmann and contributors.' . PHP_EOL . PHP_EOL . 'This version of PHPUnit requires PHP >= 7.3.' . PHP_EOL . 'You are using PHP %s (%s).' . PHP_EOL, PHP_VERSION, @@ -61,13 +61,15 @@ if (__FILE__ === realpath($_SERVER['SCRIPT_NAME'])) { $execute = false; } -$options = getopt('', array('prepend:', 'manifest', 'sbom')); +$options = getopt('', array('prepend:', 'composer-lock', 'manifest', 'sbom')); if (isset($options['prepend'])) { require $options['prepend']; } -if (isset($options['manifest'])) { +if (isset($options['composer-lock'])) { + $printComposerLock = true; +} elseif (isset($options['manifest'])) { $printManifest = true; } elseif (isset($options['sbom'])) { $printSbom = true; @@ -76,45 +78,770 @@ if (isset($options['manifest'])) { unset($options); define('__PHPUNIT_PHAR__', str_replace(DIRECTORY_SEPARATOR, '/', __FILE__)); -define('__PHPUNIT_PHAR_ROOT__', 'phar://phpunit-9.6.13.phar'); +define('__PHPUNIT_PHAR_ROOT__', 'phar://phpunit-9.6.19.phar'); -Phar::mapPhar('phpunit-9.6.13.phar'); +Phar::mapPhar('phpunit-9.6.19.phar'); spl_autoload_register( function ($class) { static $classes = null; if ($classes === null) { - $classes = ['PHPUnit\\DeepCopy\\DeepCopy' => '/myclabs-deep-copy/DeepCopy/DeepCopy.php', - 'PHPUnit\\DeepCopy\\Exception\\CloneException' => '/myclabs-deep-copy/DeepCopy/Exception/CloneException.php', - 'PHPUnit\\DeepCopy\\Exception\\PropertyException' => '/myclabs-deep-copy/DeepCopy/Exception/PropertyException.php', - 'PHPUnit\\DeepCopy\\Filter\\ChainableFilter' => '/myclabs-deep-copy/DeepCopy/Filter/ChainableFilter.php', - 'PHPUnit\\DeepCopy\\Filter\\Doctrine\\DoctrineCollectionFilter' => '/myclabs-deep-copy/DeepCopy/Filter/Doctrine/DoctrineCollectionFilter.php', - 'PHPUnit\\DeepCopy\\Filter\\Doctrine\\DoctrineEmptyCollectionFilter' => '/myclabs-deep-copy/DeepCopy/Filter/Doctrine/DoctrineEmptyCollectionFilter.php', - 'PHPUnit\\DeepCopy\\Filter\\Doctrine\\DoctrineProxyFilter' => '/myclabs-deep-copy/DeepCopy/Filter/Doctrine/DoctrineProxyFilter.php', - 'PHPUnit\\DeepCopy\\Filter\\Filter' => '/myclabs-deep-copy/DeepCopy/Filter/Filter.php', - 'PHPUnit\\DeepCopy\\Filter\\KeepFilter' => '/myclabs-deep-copy/DeepCopy/Filter/KeepFilter.php', - 'PHPUnit\\DeepCopy\\Filter\\ReplaceFilter' => '/myclabs-deep-copy/DeepCopy/Filter/ReplaceFilter.php', - 'PHPUnit\\DeepCopy\\Filter\\SetNullFilter' => '/myclabs-deep-copy/DeepCopy/Filter/SetNullFilter.php', - 'PHPUnit\\DeepCopy\\Matcher\\Doctrine\\DoctrineProxyMatcher' => '/myclabs-deep-copy/DeepCopy/Matcher/Doctrine/DoctrineProxyMatcher.php', - 'PHPUnit\\DeepCopy\\Matcher\\Matcher' => '/myclabs-deep-copy/DeepCopy/Matcher/Matcher.php', - 'PHPUnit\\DeepCopy\\Matcher\\PropertyMatcher' => '/myclabs-deep-copy/DeepCopy/Matcher/PropertyMatcher.php', - 'PHPUnit\\DeepCopy\\Matcher\\PropertyNameMatcher' => '/myclabs-deep-copy/DeepCopy/Matcher/PropertyNameMatcher.php', - 'PHPUnit\\DeepCopy\\Matcher\\PropertyTypeMatcher' => '/myclabs-deep-copy/DeepCopy/Matcher/PropertyTypeMatcher.php', - 'PHPUnit\\DeepCopy\\Reflection\\ReflectionHelper' => '/myclabs-deep-copy/DeepCopy/Reflection/ReflectionHelper.php', - 'PHPUnit\\DeepCopy\\TypeFilter\\Date\\DateIntervalFilter' => '/myclabs-deep-copy/DeepCopy/TypeFilter/Date/DateIntervalFilter.php', - 'PHPUnit\\DeepCopy\\TypeFilter\\ReplaceFilter' => '/myclabs-deep-copy/DeepCopy/TypeFilter/ReplaceFilter.php', - 'PHPUnit\\DeepCopy\\TypeFilter\\ShallowCopyFilter' => '/myclabs-deep-copy/DeepCopy/TypeFilter/ShallowCopyFilter.php', - 'PHPUnit\\DeepCopy\\TypeFilter\\Spl\\ArrayObjectFilter' => '/myclabs-deep-copy/DeepCopy/TypeFilter/Spl/ArrayObjectFilter.php', - 'PHPUnit\\DeepCopy\\TypeFilter\\Spl\\SplDoublyLinkedList' => '/myclabs-deep-copy/DeepCopy/TypeFilter/Spl/SplDoublyLinkedList.php', - 'PHPUnit\\DeepCopy\\TypeFilter\\Spl\\SplDoublyLinkedListFilter' => '/myclabs-deep-copy/DeepCopy/TypeFilter/Spl/SplDoublyLinkedListFilter.php', - 'PHPUnit\\DeepCopy\\TypeFilter\\TypeFilter' => '/myclabs-deep-copy/DeepCopy/TypeFilter/TypeFilter.php', - 'PHPUnit\\DeepCopy\\TypeMatcher\\TypeMatcher' => '/myclabs-deep-copy/DeepCopy/TypeMatcher/TypeMatcher.php', - 'PHPUnit\\Doctrine\\Instantiator\\Exception\\ExceptionInterface' => '/doctrine-instantiator/Doctrine/Instantiator/Exception/ExceptionInterface.php', - 'PHPUnit\\Doctrine\\Instantiator\\Exception\\InvalidArgumentException' => '/doctrine-instantiator/Doctrine/Instantiator/Exception/InvalidArgumentException.php', - 'PHPUnit\\Doctrine\\Instantiator\\Exception\\UnexpectedValueException' => '/doctrine-instantiator/Doctrine/Instantiator/Exception/UnexpectedValueException.php', - 'PHPUnit\\Doctrine\\Instantiator\\Instantiator' => '/doctrine-instantiator/Doctrine/Instantiator/Instantiator.php', - 'PHPUnit\\Doctrine\\Instantiator\\InstantiatorInterface' => '/doctrine-instantiator/Doctrine/Instantiator/InstantiatorInterface.php', + $classes = ['Doctrine\\Deprecations\\PHPUnit\\VerifyDeprecations' => '/doctrine-deprecations/Doctrine/Deprecations/PHPUnit/VerifyDeprecations.php', + 'PHPUnitPHAR\\DeepCopy\\DeepCopy' => '/myclabs-deep-copy/DeepCopy/DeepCopy.php', + 'PHPUnitPHAR\\DeepCopy\\Exception\\CloneException' => '/myclabs-deep-copy/DeepCopy/Exception/CloneException.php', + 'PHPUnitPHAR\\DeepCopy\\Exception\\PropertyException' => '/myclabs-deep-copy/DeepCopy/Exception/PropertyException.php', + 'PHPUnitPHAR\\DeepCopy\\Filter\\ChainableFilter' => '/myclabs-deep-copy/DeepCopy/Filter/ChainableFilter.php', + 'PHPUnitPHAR\\DeepCopy\\Filter\\Doctrine\\DoctrineCollectionFilter' => '/myclabs-deep-copy/DeepCopy/Filter/Doctrine/DoctrineCollectionFilter.php', + 'PHPUnitPHAR\\DeepCopy\\Filter\\Doctrine\\DoctrineEmptyCollectionFilter' => '/myclabs-deep-copy/DeepCopy/Filter/Doctrine/DoctrineEmptyCollectionFilter.php', + 'PHPUnitPHAR\\DeepCopy\\Filter\\Doctrine\\DoctrineProxyFilter' => '/myclabs-deep-copy/DeepCopy/Filter/Doctrine/DoctrineProxyFilter.php', + 'PHPUnitPHAR\\DeepCopy\\Filter\\Filter' => '/myclabs-deep-copy/DeepCopy/Filter/Filter.php', + 'PHPUnitPHAR\\DeepCopy\\Filter\\KeepFilter' => '/myclabs-deep-copy/DeepCopy/Filter/KeepFilter.php', + 'PHPUnitPHAR\\DeepCopy\\Filter\\ReplaceFilter' => '/myclabs-deep-copy/DeepCopy/Filter/ReplaceFilter.php', + 'PHPUnitPHAR\\DeepCopy\\Filter\\SetNullFilter' => '/myclabs-deep-copy/DeepCopy/Filter/SetNullFilter.php', + 'PHPUnitPHAR\\DeepCopy\\Matcher\\Doctrine\\DoctrineProxyMatcher' => '/myclabs-deep-copy/DeepCopy/Matcher/Doctrine/DoctrineProxyMatcher.php', + 'PHPUnitPHAR\\DeepCopy\\Matcher\\Matcher' => '/myclabs-deep-copy/DeepCopy/Matcher/Matcher.php', + 'PHPUnitPHAR\\DeepCopy\\Matcher\\PropertyMatcher' => '/myclabs-deep-copy/DeepCopy/Matcher/PropertyMatcher.php', + 'PHPUnitPHAR\\DeepCopy\\Matcher\\PropertyNameMatcher' => '/myclabs-deep-copy/DeepCopy/Matcher/PropertyNameMatcher.php', + 'PHPUnitPHAR\\DeepCopy\\Matcher\\PropertyTypeMatcher' => '/myclabs-deep-copy/DeepCopy/Matcher/PropertyTypeMatcher.php', + 'PHPUnitPHAR\\DeepCopy\\Reflection\\ReflectionHelper' => '/myclabs-deep-copy/DeepCopy/Reflection/ReflectionHelper.php', + 'PHPUnitPHAR\\DeepCopy\\TypeFilter\\Date\\DateIntervalFilter' => '/myclabs-deep-copy/DeepCopy/TypeFilter/Date/DateIntervalFilter.php', + 'PHPUnitPHAR\\DeepCopy\\TypeFilter\\ReplaceFilter' => '/myclabs-deep-copy/DeepCopy/TypeFilter/ReplaceFilter.php', + 'PHPUnitPHAR\\DeepCopy\\TypeFilter\\ShallowCopyFilter' => '/myclabs-deep-copy/DeepCopy/TypeFilter/ShallowCopyFilter.php', + 'PHPUnitPHAR\\DeepCopy\\TypeFilter\\Spl\\ArrayObjectFilter' => '/myclabs-deep-copy/DeepCopy/TypeFilter/Spl/ArrayObjectFilter.php', + 'PHPUnitPHAR\\DeepCopy\\TypeFilter\\Spl\\SplDoublyLinkedList' => '/myclabs-deep-copy/DeepCopy/TypeFilter/Spl/SplDoublyLinkedList.php', + 'PHPUnitPHAR\\DeepCopy\\TypeFilter\\Spl\\SplDoublyLinkedListFilter' => '/myclabs-deep-copy/DeepCopy/TypeFilter/Spl/SplDoublyLinkedListFilter.php', + 'PHPUnitPHAR\\DeepCopy\\TypeFilter\\TypeFilter' => '/myclabs-deep-copy/DeepCopy/TypeFilter/TypeFilter.php', + 'PHPUnitPHAR\\DeepCopy\\TypeMatcher\\TypeMatcher' => '/myclabs-deep-copy/DeepCopy/TypeMatcher/TypeMatcher.php', + 'PHPUnitPHAR\\Doctrine\\Deprecations\\Deprecation' => '/doctrine-deprecations/Doctrine/Deprecations/Deprecation.php', + 'PHPUnitPHAR\\Doctrine\\Instantiator\\Exception\\ExceptionInterface' => '/doctrine-instantiator/Doctrine/Instantiator/Exception/ExceptionInterface.php', + 'PHPUnitPHAR\\Doctrine\\Instantiator\\Exception\\InvalidArgumentException' => '/doctrine-instantiator/Doctrine/Instantiator/Exception/InvalidArgumentException.php', + 'PHPUnitPHAR\\Doctrine\\Instantiator\\Exception\\UnexpectedValueException' => '/doctrine-instantiator/Doctrine/Instantiator/Exception/UnexpectedValueException.php', + 'PHPUnitPHAR\\Doctrine\\Instantiator\\Instantiator' => '/doctrine-instantiator/Doctrine/Instantiator/Instantiator.php', + 'PHPUnitPHAR\\Doctrine\\Instantiator\\InstantiatorInterface' => '/doctrine-instantiator/Doctrine/Instantiator/InstantiatorInterface.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\AbstractNodeVisitor' => '/phpstan-phpdoc-parser/Ast/AbstractNodeVisitor.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\Attribute' => '/phpstan-phpdoc-parser/Ast/Attribute.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\ConstExpr\\ConstExprArrayItemNode' => '/phpstan-phpdoc-parser/Ast/ConstExpr/ConstExprArrayItemNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\ConstExpr\\ConstExprArrayNode' => '/phpstan-phpdoc-parser/Ast/ConstExpr/ConstExprArrayNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\ConstExpr\\ConstExprFalseNode' => '/phpstan-phpdoc-parser/Ast/ConstExpr/ConstExprFalseNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\ConstExpr\\ConstExprFloatNode' => '/phpstan-phpdoc-parser/Ast/ConstExpr/ConstExprFloatNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\ConstExpr\\ConstExprIntegerNode' => '/phpstan-phpdoc-parser/Ast/ConstExpr/ConstExprIntegerNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\ConstExpr\\ConstExprNode' => '/phpstan-phpdoc-parser/Ast/ConstExpr/ConstExprNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\ConstExpr\\ConstExprNullNode' => '/phpstan-phpdoc-parser/Ast/ConstExpr/ConstExprNullNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\ConstExpr\\ConstExprStringNode' => '/phpstan-phpdoc-parser/Ast/ConstExpr/ConstExprStringNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\ConstExpr\\ConstExprTrueNode' => '/phpstan-phpdoc-parser/Ast/ConstExpr/ConstExprTrueNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\ConstExpr\\ConstFetchNode' => '/phpstan-phpdoc-parser/Ast/ConstExpr/ConstFetchNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\ConstExpr\\DoctrineConstExprStringNode' => '/phpstan-phpdoc-parser/Ast/ConstExpr/DoctrineConstExprStringNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\ConstExpr\\QuoteAwareConstExprStringNode' => '/phpstan-phpdoc-parser/Ast/ConstExpr/QuoteAwareConstExprStringNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\Node' => '/phpstan-phpdoc-parser/Ast/Node.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\NodeAttributes' => '/phpstan-phpdoc-parser/Ast/NodeAttributes.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\NodeTraverser' => '/phpstan-phpdoc-parser/Ast/NodeTraverser.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\NodeVisitor' => '/phpstan-phpdoc-parser/Ast/NodeVisitor.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\NodeVisitor\\CloningVisitor' => '/phpstan-phpdoc-parser/Ast/NodeVisitor/CloningVisitor.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\AssertTagMethodValueNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/AssertTagMethodValueNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\AssertTagPropertyValueNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/AssertTagPropertyValueNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\AssertTagValueNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/AssertTagValueNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\DeprecatedTagValueNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/DeprecatedTagValueNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\Doctrine\\DoctrineAnnotation' => '/phpstan-phpdoc-parser/Ast/PhpDoc/Doctrine/DoctrineAnnotation.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\Doctrine\\DoctrineArgument' => '/phpstan-phpdoc-parser/Ast/PhpDoc/Doctrine/DoctrineArgument.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\Doctrine\\DoctrineArray' => '/phpstan-phpdoc-parser/Ast/PhpDoc/Doctrine/DoctrineArray.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\Doctrine\\DoctrineArrayItem' => '/phpstan-phpdoc-parser/Ast/PhpDoc/Doctrine/DoctrineArrayItem.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\Doctrine\\DoctrineTagValueNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/Doctrine/DoctrineTagValueNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\ExtendsTagValueNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/ExtendsTagValueNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\GenericTagValueNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/GenericTagValueNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\ImplementsTagValueNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/ImplementsTagValueNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\InvalidTagValueNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/InvalidTagValueNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\MethodTagValueNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/MethodTagValueNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\MethodTagValueParameterNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/MethodTagValueParameterNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\MixinTagValueNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/MixinTagValueNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\ParamClosureThisTagValueNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/ParamClosureThisTagValueNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\ParamImmediatelyInvokedCallableTagValueNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/ParamImmediatelyInvokedCallableTagValueNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\ParamLaterInvokedCallableTagValueNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/ParamLaterInvokedCallableTagValueNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\ParamOutTagValueNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/ParamOutTagValueNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\ParamTagValueNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/ParamTagValueNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\PhpDocChildNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/PhpDocChildNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\PhpDocNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/PhpDocNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\PhpDocTagNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/PhpDocTagNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\PhpDocTagValueNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/PhpDocTagValueNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\PhpDocTextNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/PhpDocTextNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\PropertyTagValueNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/PropertyTagValueNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\RequireExtendsTagValueNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/RequireExtendsTagValueNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\RequireImplementsTagValueNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/RequireImplementsTagValueNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\ReturnTagValueNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/ReturnTagValueNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\SelfOutTagValueNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/SelfOutTagValueNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\TemplateTagValueNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/TemplateTagValueNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\ThrowsTagValueNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/ThrowsTagValueNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\TypeAliasImportTagValueNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/TypeAliasImportTagValueNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\TypeAliasTagValueNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/TypeAliasTagValueNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\TypelessParamTagValueNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/TypelessParamTagValueNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\UsesTagValueNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/UsesTagValueNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\VarTagValueNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/VarTagValueNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\Type\\ArrayShapeItemNode' => '/phpstan-phpdoc-parser/Ast/Type/ArrayShapeItemNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\Type\\ArrayShapeNode' => '/phpstan-phpdoc-parser/Ast/Type/ArrayShapeNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\Type\\ArrayTypeNode' => '/phpstan-phpdoc-parser/Ast/Type/ArrayTypeNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\Type\\CallableTypeNode' => '/phpstan-phpdoc-parser/Ast/Type/CallableTypeNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\Type\\CallableTypeParameterNode' => '/phpstan-phpdoc-parser/Ast/Type/CallableTypeParameterNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\Type\\ConditionalTypeForParameterNode' => '/phpstan-phpdoc-parser/Ast/Type/ConditionalTypeForParameterNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\Type\\ConditionalTypeNode' => '/phpstan-phpdoc-parser/Ast/Type/ConditionalTypeNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\Type\\ConstTypeNode' => '/phpstan-phpdoc-parser/Ast/Type/ConstTypeNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\Type\\GenericTypeNode' => '/phpstan-phpdoc-parser/Ast/Type/GenericTypeNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\Type\\IdentifierTypeNode' => '/phpstan-phpdoc-parser/Ast/Type/IdentifierTypeNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\Type\\IntersectionTypeNode' => '/phpstan-phpdoc-parser/Ast/Type/IntersectionTypeNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\Type\\InvalidTypeNode' => '/phpstan-phpdoc-parser/Ast/Type/InvalidTypeNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\Type\\NullableTypeNode' => '/phpstan-phpdoc-parser/Ast/Type/NullableTypeNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\Type\\ObjectShapeItemNode' => '/phpstan-phpdoc-parser/Ast/Type/ObjectShapeItemNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\Type\\ObjectShapeNode' => '/phpstan-phpdoc-parser/Ast/Type/ObjectShapeNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\Type\\OffsetAccessTypeNode' => '/phpstan-phpdoc-parser/Ast/Type/OffsetAccessTypeNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\Type\\ThisTypeNode' => '/phpstan-phpdoc-parser/Ast/Type/ThisTypeNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\Type\\TypeNode' => '/phpstan-phpdoc-parser/Ast/Type/TypeNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\Type\\UnionTypeNode' => '/phpstan-phpdoc-parser/Ast/Type/UnionTypeNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Lexer\\Lexer' => '/phpstan-phpdoc-parser/Lexer/Lexer.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Parser\\ConstExprParser' => '/phpstan-phpdoc-parser/Parser/ConstExprParser.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Parser\\ParserException' => '/phpstan-phpdoc-parser/Parser/ParserException.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Parser\\PhpDocParser' => '/phpstan-phpdoc-parser/Parser/PhpDocParser.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Parser\\StringUnescaper' => '/phpstan-phpdoc-parser/Parser/StringUnescaper.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Parser\\TokenIterator' => '/phpstan-phpdoc-parser/Parser/TokenIterator.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Parser\\TypeParser' => '/phpstan-phpdoc-parser/Parser/TypeParser.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Printer\\DiffElem' => '/phpstan-phpdoc-parser/Printer/DiffElem.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Printer\\Differ' => '/phpstan-phpdoc-parser/Printer/Differ.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Printer\\Printer' => '/phpstan-phpdoc-parser/Printer/Printer.php', + 'PHPUnitPHAR\\PharIo\\Manifest\\Application' => '/phar-io-manifest/values/Application.php', + 'PHPUnitPHAR\\PharIo\\Manifest\\ApplicationName' => '/phar-io-manifest/values/ApplicationName.php', + 'PHPUnitPHAR\\PharIo\\Manifest\\Author' => '/phar-io-manifest/values/Author.php', + 'PHPUnitPHAR\\PharIo\\Manifest\\AuthorCollection' => '/phar-io-manifest/values/AuthorCollection.php', + 'PHPUnitPHAR\\PharIo\\Manifest\\AuthorCollectionIterator' => '/phar-io-manifest/values/AuthorCollectionIterator.php', + 'PHPUnitPHAR\\PharIo\\Manifest\\AuthorElement' => '/phar-io-manifest/xml/AuthorElement.php', + 'PHPUnitPHAR\\PharIo\\Manifest\\AuthorElementCollection' => '/phar-io-manifest/xml/AuthorElementCollection.php', + 'PHPUnitPHAR\\PharIo\\Manifest\\BundledComponent' => '/phar-io-manifest/values/BundledComponent.php', + 'PHPUnitPHAR\\PharIo\\Manifest\\BundledComponentCollection' => '/phar-io-manifest/values/BundledComponentCollection.php', + 'PHPUnitPHAR\\PharIo\\Manifest\\BundledComponentCollectionIterator' => '/phar-io-manifest/values/BundledComponentCollectionIterator.php', + 'PHPUnitPHAR\\PharIo\\Manifest\\BundlesElement' => '/phar-io-manifest/xml/BundlesElement.php', + 'PHPUnitPHAR\\PharIo\\Manifest\\ComponentElement' => '/phar-io-manifest/xml/ComponentElement.php', + 'PHPUnitPHAR\\PharIo\\Manifest\\ComponentElementCollection' => '/phar-io-manifest/xml/ComponentElementCollection.php', + 'PHPUnitPHAR\\PharIo\\Manifest\\ContainsElement' => '/phar-io-manifest/xml/ContainsElement.php', + 'PHPUnitPHAR\\PharIo\\Manifest\\CopyrightElement' => '/phar-io-manifest/xml/CopyrightElement.php', + 'PHPUnitPHAR\\PharIo\\Manifest\\CopyrightInformation' => '/phar-io-manifest/values/CopyrightInformation.php', + 'PHPUnitPHAR\\PharIo\\Manifest\\ElementCollection' => '/phar-io-manifest/xml/ElementCollection.php', + 'PHPUnitPHAR\\PharIo\\Manifest\\ElementCollectionException' => '/phar-io-manifest/exceptions/ElementCollectionException.php', + 'PHPUnitPHAR\\PharIo\\Manifest\\Email' => '/phar-io-manifest/values/Email.php', + 'PHPUnitPHAR\\PharIo\\Manifest\\Exception' => '/phar-io-manifest/exceptions/Exception.php', + 'PHPUnitPHAR\\PharIo\\Manifest\\ExtElement' => '/phar-io-manifest/xml/ExtElement.php', + 'PHPUnitPHAR\\PharIo\\Manifest\\ExtElementCollection' => '/phar-io-manifest/xml/ExtElementCollection.php', + 'PHPUnitPHAR\\PharIo\\Manifest\\Extension' => '/phar-io-manifest/values/Extension.php', + 'PHPUnitPHAR\\PharIo\\Manifest\\ExtensionElement' => '/phar-io-manifest/xml/ExtensionElement.php', + 'PHPUnitPHAR\\PharIo\\Manifest\\InvalidApplicationNameException' => '/phar-io-manifest/exceptions/InvalidApplicationNameException.php', + 'PHPUnitPHAR\\PharIo\\Manifest\\InvalidEmailException' => '/phar-io-manifest/exceptions/InvalidEmailException.php', + 'PHPUnitPHAR\\PharIo\\Manifest\\InvalidUrlException' => '/phar-io-manifest/exceptions/InvalidUrlException.php', + 'PHPUnitPHAR\\PharIo\\Manifest\\Library' => '/phar-io-manifest/values/Library.php', + 'PHPUnitPHAR\\PharIo\\Manifest\\License' => '/phar-io-manifest/values/License.php', + 'PHPUnitPHAR\\PharIo\\Manifest\\LicenseElement' => '/phar-io-manifest/xml/LicenseElement.php', + 'PHPUnitPHAR\\PharIo\\Manifest\\Manifest' => '/phar-io-manifest/values/Manifest.php', + 'PHPUnitPHAR\\PharIo\\Manifest\\ManifestDocument' => '/phar-io-manifest/xml/ManifestDocument.php', + 'PHPUnitPHAR\\PharIo\\Manifest\\ManifestDocumentException' => '/phar-io-manifest/exceptions/ManifestDocumentException.php', + 'PHPUnitPHAR\\PharIo\\Manifest\\ManifestDocumentLoadingException' => '/phar-io-manifest/exceptions/ManifestDocumentLoadingException.php', + 'PHPUnitPHAR\\PharIo\\Manifest\\ManifestDocumentMapper' => '/phar-io-manifest/ManifestDocumentMapper.php', + 'PHPUnitPHAR\\PharIo\\Manifest\\ManifestDocumentMapperException' => '/phar-io-manifest/exceptions/ManifestDocumentMapperException.php', + 'PHPUnitPHAR\\PharIo\\Manifest\\ManifestElement' => '/phar-io-manifest/xml/ManifestElement.php', + 'PHPUnitPHAR\\PharIo\\Manifest\\ManifestElementException' => '/phar-io-manifest/exceptions/ManifestElementException.php', + 'PHPUnitPHAR\\PharIo\\Manifest\\ManifestLoader' => '/phar-io-manifest/ManifestLoader.php', + 'PHPUnitPHAR\\PharIo\\Manifest\\ManifestLoaderException' => '/phar-io-manifest/exceptions/ManifestLoaderException.php', + 'PHPUnitPHAR\\PharIo\\Manifest\\ManifestSerializer' => '/phar-io-manifest/ManifestSerializer.php', + 'PHPUnitPHAR\\PharIo\\Manifest\\NoEmailAddressException' => '/phar-io-manifest/exceptions/NoEmailAddressException.php', + 'PHPUnitPHAR\\PharIo\\Manifest\\PhpElement' => '/phar-io-manifest/xml/PhpElement.php', + 'PHPUnitPHAR\\PharIo\\Manifest\\PhpExtensionRequirement' => '/phar-io-manifest/values/PhpExtensionRequirement.php', + 'PHPUnitPHAR\\PharIo\\Manifest\\PhpVersionRequirement' => '/phar-io-manifest/values/PhpVersionRequirement.php', + 'PHPUnitPHAR\\PharIo\\Manifest\\Requirement' => '/phar-io-manifest/values/Requirement.php', + 'PHPUnitPHAR\\PharIo\\Manifest\\RequirementCollection' => '/phar-io-manifest/values/RequirementCollection.php', + 'PHPUnitPHAR\\PharIo\\Manifest\\RequirementCollectionIterator' => '/phar-io-manifest/values/RequirementCollectionIterator.php', + 'PHPUnitPHAR\\PharIo\\Manifest\\RequiresElement' => '/phar-io-manifest/xml/RequiresElement.php', + 'PHPUnitPHAR\\PharIo\\Manifest\\Type' => '/phar-io-manifest/values/Type.php', + 'PHPUnitPHAR\\PharIo\\Manifest\\Url' => '/phar-io-manifest/values/Url.php', + 'PHPUnitPHAR\\PharIo\\Version\\AbstractVersionConstraint' => '/phar-io-version/constraints/AbstractVersionConstraint.php', + 'PHPUnitPHAR\\PharIo\\Version\\AndVersionConstraintGroup' => '/phar-io-version/constraints/AndVersionConstraintGroup.php', + 'PHPUnitPHAR\\PharIo\\Version\\AnyVersionConstraint' => '/phar-io-version/constraints/AnyVersionConstraint.php', + 'PHPUnitPHAR\\PharIo\\Version\\BuildMetaData' => '/phar-io-version/BuildMetaData.php', + 'PHPUnitPHAR\\PharIo\\Version\\ExactVersionConstraint' => '/phar-io-version/constraints/ExactVersionConstraint.php', + 'PHPUnitPHAR\\PharIo\\Version\\Exception' => '/phar-io-version/exceptions/Exception.php', + 'PHPUnitPHAR\\PharIo\\Version\\GreaterThanOrEqualToVersionConstraint' => '/phar-io-version/constraints/GreaterThanOrEqualToVersionConstraint.php', + 'PHPUnitPHAR\\PharIo\\Version\\InvalidPreReleaseSuffixException' => '/phar-io-version/exceptions/InvalidPreReleaseSuffixException.php', + 'PHPUnitPHAR\\PharIo\\Version\\InvalidVersionException' => '/phar-io-version/exceptions/InvalidVersionException.php', + 'PHPUnitPHAR\\PharIo\\Version\\NoBuildMetaDataException' => '/phar-io-version/exceptions/NoBuildMetaDataException.php', + 'PHPUnitPHAR\\PharIo\\Version\\NoPreReleaseSuffixException' => '/phar-io-version/exceptions/NoPreReleaseSuffixException.php', + 'PHPUnitPHAR\\PharIo\\Version\\OrVersionConstraintGroup' => '/phar-io-version/constraints/OrVersionConstraintGroup.php', + 'PHPUnitPHAR\\PharIo\\Version\\PreReleaseSuffix' => '/phar-io-version/PreReleaseSuffix.php', + 'PHPUnitPHAR\\PharIo\\Version\\SpecificMajorAndMinorVersionConstraint' => '/phar-io-version/constraints/SpecificMajorAndMinorVersionConstraint.php', + 'PHPUnitPHAR\\PharIo\\Version\\SpecificMajorVersionConstraint' => '/phar-io-version/constraints/SpecificMajorVersionConstraint.php', + 'PHPUnitPHAR\\PharIo\\Version\\UnsupportedVersionConstraintException' => '/phar-io-version/exceptions/UnsupportedVersionConstraintException.php', + 'PHPUnitPHAR\\PharIo\\Version\\Version' => '/phar-io-version/Version.php', + 'PHPUnitPHAR\\PharIo\\Version\\VersionConstraint' => '/phar-io-version/constraints/VersionConstraint.php', + 'PHPUnitPHAR\\PharIo\\Version\\VersionConstraintParser' => '/phar-io-version/VersionConstraintParser.php', + 'PHPUnitPHAR\\PharIo\\Version\\VersionConstraintValue' => '/phar-io-version/VersionConstraintValue.php', + 'PHPUnitPHAR\\PharIo\\Version\\VersionNumber' => '/phar-io-version/VersionNumber.php', + 'PHPUnitPHAR\\PhpParser\\Builder' => '/nikic-php-parser/PhpParser/Builder.php', + 'PHPUnitPHAR\\PhpParser\\BuilderFactory' => '/nikic-php-parser/PhpParser/BuilderFactory.php', + 'PHPUnitPHAR\\PhpParser\\BuilderHelpers' => '/nikic-php-parser/PhpParser/BuilderHelpers.php', + 'PHPUnitPHAR\\PhpParser\\Builder\\ClassConst' => '/nikic-php-parser/PhpParser/Builder/ClassConst.php', + 'PHPUnitPHAR\\PhpParser\\Builder\\Class_' => '/nikic-php-parser/PhpParser/Builder/Class_.php', + 'PHPUnitPHAR\\PhpParser\\Builder\\Declaration' => '/nikic-php-parser/PhpParser/Builder/Declaration.php', + 'PHPUnitPHAR\\PhpParser\\Builder\\EnumCase' => '/nikic-php-parser/PhpParser/Builder/EnumCase.php', + 'PHPUnitPHAR\\PhpParser\\Builder\\Enum_' => '/nikic-php-parser/PhpParser/Builder/Enum_.php', + 'PHPUnitPHAR\\PhpParser\\Builder\\FunctionLike' => '/nikic-php-parser/PhpParser/Builder/FunctionLike.php', + 'PHPUnitPHAR\\PhpParser\\Builder\\Function_' => '/nikic-php-parser/PhpParser/Builder/Function_.php', + 'PHPUnitPHAR\\PhpParser\\Builder\\Interface_' => '/nikic-php-parser/PhpParser/Builder/Interface_.php', + 'PHPUnitPHAR\\PhpParser\\Builder\\Method' => '/nikic-php-parser/PhpParser/Builder/Method.php', + 'PHPUnitPHAR\\PhpParser\\Builder\\Namespace_' => '/nikic-php-parser/PhpParser/Builder/Namespace_.php', + 'PHPUnitPHAR\\PhpParser\\Builder\\Param' => '/nikic-php-parser/PhpParser/Builder/Param.php', + 'PHPUnitPHAR\\PhpParser\\Builder\\Property' => '/nikic-php-parser/PhpParser/Builder/Property.php', + 'PHPUnitPHAR\\PhpParser\\Builder\\TraitUse' => '/nikic-php-parser/PhpParser/Builder/TraitUse.php', + 'PHPUnitPHAR\\PhpParser\\Builder\\TraitUseAdaptation' => '/nikic-php-parser/PhpParser/Builder/TraitUseAdaptation.php', + 'PHPUnitPHAR\\PhpParser\\Builder\\Trait_' => '/nikic-php-parser/PhpParser/Builder/Trait_.php', + 'PHPUnitPHAR\\PhpParser\\Builder\\Use_' => '/nikic-php-parser/PhpParser/Builder/Use_.php', + 'PHPUnitPHAR\\PhpParser\\Comment' => '/nikic-php-parser/PhpParser/Comment.php', + 'PHPUnitPHAR\\PhpParser\\Comment\\Doc' => '/nikic-php-parser/PhpParser/Comment/Doc.php', + 'PHPUnitPHAR\\PhpParser\\ConstExprEvaluationException' => '/nikic-php-parser/PhpParser/ConstExprEvaluationException.php', + 'PHPUnitPHAR\\PhpParser\\ConstExprEvaluator' => '/nikic-php-parser/PhpParser/ConstExprEvaluator.php', + 'PHPUnitPHAR\\PhpParser\\Error' => '/nikic-php-parser/PhpParser/Error.php', + 'PHPUnitPHAR\\PhpParser\\ErrorHandler' => '/nikic-php-parser/PhpParser/ErrorHandler.php', + 'PHPUnitPHAR\\PhpParser\\ErrorHandler\\Collecting' => '/nikic-php-parser/PhpParser/ErrorHandler/Collecting.php', + 'PHPUnitPHAR\\PhpParser\\ErrorHandler\\Throwing' => '/nikic-php-parser/PhpParser/ErrorHandler/Throwing.php', + 'PHPUnitPHAR\\PhpParser\\Internal\\DiffElem' => '/nikic-php-parser/PhpParser/Internal/DiffElem.php', + 'PHPUnitPHAR\\PhpParser\\Internal\\Differ' => '/nikic-php-parser/PhpParser/Internal/Differ.php', + 'PHPUnitPHAR\\PhpParser\\Internal\\PrintableNewAnonClassNode' => '/nikic-php-parser/PhpParser/Internal/PrintableNewAnonClassNode.php', + 'PHPUnitPHAR\\PhpParser\\Internal\\TokenStream' => '/nikic-php-parser/PhpParser/Internal/TokenStream.php', + 'PHPUnitPHAR\\PhpParser\\JsonDecoder' => '/nikic-php-parser/PhpParser/JsonDecoder.php', + 'PHPUnitPHAR\\PhpParser\\Lexer' => '/nikic-php-parser/PhpParser/Lexer.php', + 'PHPUnitPHAR\\PhpParser\\Lexer\\Emulative' => '/nikic-php-parser/PhpParser/Lexer/Emulative.php', + 'PHPUnitPHAR\\PhpParser\\Lexer\\TokenEmulator\\AttributeEmulator' => '/nikic-php-parser/PhpParser/Lexer/TokenEmulator/AttributeEmulator.php', + 'PHPUnitPHAR\\PhpParser\\Lexer\\TokenEmulator\\CoaleseEqualTokenEmulator' => '/nikic-php-parser/PhpParser/Lexer/TokenEmulator/CoaleseEqualTokenEmulator.php', + 'PHPUnitPHAR\\PhpParser\\Lexer\\TokenEmulator\\EnumTokenEmulator' => '/nikic-php-parser/PhpParser/Lexer/TokenEmulator/EnumTokenEmulator.php', + 'PHPUnitPHAR\\PhpParser\\Lexer\\TokenEmulator\\ExplicitOctalEmulator' => '/nikic-php-parser/PhpParser/Lexer/TokenEmulator/ExplicitOctalEmulator.php', + 'PHPUnitPHAR\\PhpParser\\Lexer\\TokenEmulator\\FlexibleDocStringEmulator' => '/nikic-php-parser/PhpParser/Lexer/TokenEmulator/FlexibleDocStringEmulator.php', + 'PHPUnitPHAR\\PhpParser\\Lexer\\TokenEmulator\\FnTokenEmulator' => '/nikic-php-parser/PhpParser/Lexer/TokenEmulator/FnTokenEmulator.php', + 'PHPUnitPHAR\\PhpParser\\Lexer\\TokenEmulator\\KeywordEmulator' => '/nikic-php-parser/PhpParser/Lexer/TokenEmulator/KeywordEmulator.php', + 'PHPUnitPHAR\\PhpParser\\Lexer\\TokenEmulator\\MatchTokenEmulator' => '/nikic-php-parser/PhpParser/Lexer/TokenEmulator/MatchTokenEmulator.php', + 'PHPUnitPHAR\\PhpParser\\Lexer\\TokenEmulator\\NullsafeTokenEmulator' => '/nikic-php-parser/PhpParser/Lexer/TokenEmulator/NullsafeTokenEmulator.php', + 'PHPUnitPHAR\\PhpParser\\Lexer\\TokenEmulator\\NumericLiteralSeparatorEmulator' => '/nikic-php-parser/PhpParser/Lexer/TokenEmulator/NumericLiteralSeparatorEmulator.php', + 'PHPUnitPHAR\\PhpParser\\Lexer\\TokenEmulator\\ReadonlyFunctionTokenEmulator' => '/nikic-php-parser/PhpParser/Lexer/TokenEmulator/ReadonlyFunctionTokenEmulator.php', + 'PHPUnitPHAR\\PhpParser\\Lexer\\TokenEmulator\\ReadonlyTokenEmulator' => '/nikic-php-parser/PhpParser/Lexer/TokenEmulator/ReadonlyTokenEmulator.php', + 'PHPUnitPHAR\\PhpParser\\Lexer\\TokenEmulator\\ReverseEmulator' => '/nikic-php-parser/PhpParser/Lexer/TokenEmulator/ReverseEmulator.php', + 'PHPUnitPHAR\\PhpParser\\Lexer\\TokenEmulator\\TokenEmulator' => '/nikic-php-parser/PhpParser/Lexer/TokenEmulator/TokenEmulator.php', + 'PHPUnitPHAR\\PhpParser\\NameContext' => '/nikic-php-parser/PhpParser/NameContext.php', + 'PHPUnitPHAR\\PhpParser\\Node' => '/nikic-php-parser/PhpParser/Node.php', + 'PHPUnitPHAR\\PhpParser\\NodeAbstract' => '/nikic-php-parser/PhpParser/NodeAbstract.php', + 'PHPUnitPHAR\\PhpParser\\NodeDumper' => '/nikic-php-parser/PhpParser/NodeDumper.php', + 'PHPUnitPHAR\\PhpParser\\NodeFinder' => '/nikic-php-parser/PhpParser/NodeFinder.php', + 'PHPUnitPHAR\\PhpParser\\NodeTraverser' => '/nikic-php-parser/PhpParser/NodeTraverser.php', + 'PHPUnitPHAR\\PhpParser\\NodeTraverserInterface' => '/nikic-php-parser/PhpParser/NodeTraverserInterface.php', + 'PHPUnitPHAR\\PhpParser\\NodeVisitor' => '/nikic-php-parser/PhpParser/NodeVisitor.php', + 'PHPUnitPHAR\\PhpParser\\NodeVisitorAbstract' => '/nikic-php-parser/PhpParser/NodeVisitorAbstract.php', + 'PHPUnitPHAR\\PhpParser\\NodeVisitor\\CloningVisitor' => '/nikic-php-parser/PhpParser/NodeVisitor/CloningVisitor.php', + 'PHPUnitPHAR\\PhpParser\\NodeVisitor\\FindingVisitor' => '/nikic-php-parser/PhpParser/NodeVisitor/FindingVisitor.php', + 'PHPUnitPHAR\\PhpParser\\NodeVisitor\\FirstFindingVisitor' => '/nikic-php-parser/PhpParser/NodeVisitor/FirstFindingVisitor.php', + 'PHPUnitPHAR\\PhpParser\\NodeVisitor\\NameResolver' => '/nikic-php-parser/PhpParser/NodeVisitor/NameResolver.php', + 'PHPUnitPHAR\\PhpParser\\NodeVisitor\\NodeConnectingVisitor' => '/nikic-php-parser/PhpParser/NodeVisitor/NodeConnectingVisitor.php', + 'PHPUnitPHAR\\PhpParser\\NodeVisitor\\ParentConnectingVisitor' => '/nikic-php-parser/PhpParser/NodeVisitor/ParentConnectingVisitor.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Arg' => '/nikic-php-parser/PhpParser/Node/Arg.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Attribute' => '/nikic-php-parser/PhpParser/Node/Attribute.php', + 'PHPUnitPHAR\\PhpParser\\Node\\AttributeGroup' => '/nikic-php-parser/PhpParser/Node/AttributeGroup.php', + 'PHPUnitPHAR\\PhpParser\\Node\\ComplexType' => '/nikic-php-parser/PhpParser/Node/ComplexType.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Const_' => '/nikic-php-parser/PhpParser/Node/Const_.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr' => '/nikic-php-parser/PhpParser/Node/Expr.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\ArrayDimFetch' => '/nikic-php-parser/PhpParser/Node/Expr/ArrayDimFetch.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\ArrayItem' => '/nikic-php-parser/PhpParser/Node/Expr/ArrayItem.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\Array_' => '/nikic-php-parser/PhpParser/Node/Expr/Array_.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\ArrowFunction' => '/nikic-php-parser/PhpParser/Node/Expr/ArrowFunction.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\Assign' => '/nikic-php-parser/PhpParser/Node/Expr/Assign.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\AssignOp' => '/nikic-php-parser/PhpParser/Node/Expr/AssignOp.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\AssignOp\\BitwiseAnd' => '/nikic-php-parser/PhpParser/Node/Expr/AssignOp/BitwiseAnd.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\AssignOp\\BitwiseOr' => '/nikic-php-parser/PhpParser/Node/Expr/AssignOp/BitwiseOr.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\AssignOp\\BitwiseXor' => '/nikic-php-parser/PhpParser/Node/Expr/AssignOp/BitwiseXor.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\AssignOp\\Coalesce' => '/nikic-php-parser/PhpParser/Node/Expr/AssignOp/Coalesce.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\AssignOp\\Concat' => '/nikic-php-parser/PhpParser/Node/Expr/AssignOp/Concat.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\AssignOp\\Div' => '/nikic-php-parser/PhpParser/Node/Expr/AssignOp/Div.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\AssignOp\\Minus' => '/nikic-php-parser/PhpParser/Node/Expr/AssignOp/Minus.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\AssignOp\\Mod' => '/nikic-php-parser/PhpParser/Node/Expr/AssignOp/Mod.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\AssignOp\\Mul' => '/nikic-php-parser/PhpParser/Node/Expr/AssignOp/Mul.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\AssignOp\\Plus' => '/nikic-php-parser/PhpParser/Node/Expr/AssignOp/Plus.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\AssignOp\\Pow' => '/nikic-php-parser/PhpParser/Node/Expr/AssignOp/Pow.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\AssignOp\\ShiftLeft' => '/nikic-php-parser/PhpParser/Node/Expr/AssignOp/ShiftLeft.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\AssignOp\\ShiftRight' => '/nikic-php-parser/PhpParser/Node/Expr/AssignOp/ShiftRight.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\AssignRef' => '/nikic-php-parser/PhpParser/Node/Expr/AssignRef.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\BinaryOp' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\BinaryOp\\BitwiseAnd' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/BitwiseAnd.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\BinaryOp\\BitwiseOr' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/BitwiseOr.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\BinaryOp\\BitwiseXor' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/BitwiseXor.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\BinaryOp\\BooleanAnd' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/BooleanAnd.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\BinaryOp\\BooleanOr' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/BooleanOr.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\BinaryOp\\Coalesce' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Coalesce.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\BinaryOp\\Concat' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Concat.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\BinaryOp\\Div' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Div.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\BinaryOp\\Equal' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Equal.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\BinaryOp\\Greater' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Greater.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\BinaryOp\\GreaterOrEqual' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/GreaterOrEqual.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\BinaryOp\\Identical' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Identical.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\BinaryOp\\LogicalAnd' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/LogicalAnd.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\BinaryOp\\LogicalOr' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/LogicalOr.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\BinaryOp\\LogicalXor' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/LogicalXor.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\BinaryOp\\Minus' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Minus.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\BinaryOp\\Mod' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Mod.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\BinaryOp\\Mul' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Mul.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\BinaryOp\\NotEqual' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/NotEqual.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\BinaryOp\\NotIdentical' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/NotIdentical.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\BinaryOp\\Plus' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Plus.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\BinaryOp\\Pow' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Pow.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\BinaryOp\\ShiftLeft' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/ShiftLeft.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\BinaryOp\\ShiftRight' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/ShiftRight.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\BinaryOp\\Smaller' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Smaller.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\BinaryOp\\SmallerOrEqual' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/SmallerOrEqual.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\BinaryOp\\Spaceship' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Spaceship.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\BitwiseNot' => '/nikic-php-parser/PhpParser/Node/Expr/BitwiseNot.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\BooleanNot' => '/nikic-php-parser/PhpParser/Node/Expr/BooleanNot.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\CallLike' => '/nikic-php-parser/PhpParser/Node/Expr/CallLike.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\Cast' => '/nikic-php-parser/PhpParser/Node/Expr/Cast.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\Cast\\Array_' => '/nikic-php-parser/PhpParser/Node/Expr/Cast/Array_.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\Cast\\Bool_' => '/nikic-php-parser/PhpParser/Node/Expr/Cast/Bool_.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\Cast\\Double' => '/nikic-php-parser/PhpParser/Node/Expr/Cast/Double.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\Cast\\Int_' => '/nikic-php-parser/PhpParser/Node/Expr/Cast/Int_.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\Cast\\Object_' => '/nikic-php-parser/PhpParser/Node/Expr/Cast/Object_.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\Cast\\String_' => '/nikic-php-parser/PhpParser/Node/Expr/Cast/String_.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\Cast\\Unset_' => '/nikic-php-parser/PhpParser/Node/Expr/Cast/Unset_.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\ClassConstFetch' => '/nikic-php-parser/PhpParser/Node/Expr/ClassConstFetch.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\Clone_' => '/nikic-php-parser/PhpParser/Node/Expr/Clone_.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\Closure' => '/nikic-php-parser/PhpParser/Node/Expr/Closure.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\ClosureUse' => '/nikic-php-parser/PhpParser/Node/Expr/ClosureUse.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\ConstFetch' => '/nikic-php-parser/PhpParser/Node/Expr/ConstFetch.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\Empty_' => '/nikic-php-parser/PhpParser/Node/Expr/Empty_.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\Error' => '/nikic-php-parser/PhpParser/Node/Expr/Error.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\ErrorSuppress' => '/nikic-php-parser/PhpParser/Node/Expr/ErrorSuppress.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\Eval_' => '/nikic-php-parser/PhpParser/Node/Expr/Eval_.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\Exit_' => '/nikic-php-parser/PhpParser/Node/Expr/Exit_.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\FuncCall' => '/nikic-php-parser/PhpParser/Node/Expr/FuncCall.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\Include_' => '/nikic-php-parser/PhpParser/Node/Expr/Include_.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\Instanceof_' => '/nikic-php-parser/PhpParser/Node/Expr/Instanceof_.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\Isset_' => '/nikic-php-parser/PhpParser/Node/Expr/Isset_.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\List_' => '/nikic-php-parser/PhpParser/Node/Expr/List_.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\Match_' => '/nikic-php-parser/PhpParser/Node/Expr/Match_.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\MethodCall' => '/nikic-php-parser/PhpParser/Node/Expr/MethodCall.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\New_' => '/nikic-php-parser/PhpParser/Node/Expr/New_.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\NullsafeMethodCall' => '/nikic-php-parser/PhpParser/Node/Expr/NullsafeMethodCall.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\NullsafePropertyFetch' => '/nikic-php-parser/PhpParser/Node/Expr/NullsafePropertyFetch.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\PostDec' => '/nikic-php-parser/PhpParser/Node/Expr/PostDec.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\PostInc' => '/nikic-php-parser/PhpParser/Node/Expr/PostInc.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\PreDec' => '/nikic-php-parser/PhpParser/Node/Expr/PreDec.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\PreInc' => '/nikic-php-parser/PhpParser/Node/Expr/PreInc.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\Print_' => '/nikic-php-parser/PhpParser/Node/Expr/Print_.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\PropertyFetch' => '/nikic-php-parser/PhpParser/Node/Expr/PropertyFetch.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\ShellExec' => '/nikic-php-parser/PhpParser/Node/Expr/ShellExec.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\StaticCall' => '/nikic-php-parser/PhpParser/Node/Expr/StaticCall.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\StaticPropertyFetch' => '/nikic-php-parser/PhpParser/Node/Expr/StaticPropertyFetch.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\Ternary' => '/nikic-php-parser/PhpParser/Node/Expr/Ternary.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\Throw_' => '/nikic-php-parser/PhpParser/Node/Expr/Throw_.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\UnaryMinus' => '/nikic-php-parser/PhpParser/Node/Expr/UnaryMinus.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\UnaryPlus' => '/nikic-php-parser/PhpParser/Node/Expr/UnaryPlus.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\Variable' => '/nikic-php-parser/PhpParser/Node/Expr/Variable.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\YieldFrom' => '/nikic-php-parser/PhpParser/Node/Expr/YieldFrom.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\Yield_' => '/nikic-php-parser/PhpParser/Node/Expr/Yield_.php', + 'PHPUnitPHAR\\PhpParser\\Node\\FunctionLike' => '/nikic-php-parser/PhpParser/Node/FunctionLike.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Identifier' => '/nikic-php-parser/PhpParser/Node/Identifier.php', + 'PHPUnitPHAR\\PhpParser\\Node\\IntersectionType' => '/nikic-php-parser/PhpParser/Node/IntersectionType.php', + 'PHPUnitPHAR\\PhpParser\\Node\\MatchArm' => '/nikic-php-parser/PhpParser/Node/MatchArm.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Name' => '/nikic-php-parser/PhpParser/Node/Name.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Name\\FullyQualified' => '/nikic-php-parser/PhpParser/Node/Name/FullyQualified.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Name\\Relative' => '/nikic-php-parser/PhpParser/Node/Name/Relative.php', + 'PHPUnitPHAR\\PhpParser\\Node\\NullableType' => '/nikic-php-parser/PhpParser/Node/NullableType.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Param' => '/nikic-php-parser/PhpParser/Node/Param.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Scalar' => '/nikic-php-parser/PhpParser/Node/Scalar.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Scalar\\DNumber' => '/nikic-php-parser/PhpParser/Node/Scalar/DNumber.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Scalar\\Encapsed' => '/nikic-php-parser/PhpParser/Node/Scalar/Encapsed.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Scalar\\EncapsedStringPart' => '/nikic-php-parser/PhpParser/Node/Scalar/EncapsedStringPart.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Scalar\\LNumber' => '/nikic-php-parser/PhpParser/Node/Scalar/LNumber.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Scalar\\MagicConst' => '/nikic-php-parser/PhpParser/Node/Scalar/MagicConst.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Scalar\\MagicConst\\Class_' => '/nikic-php-parser/PhpParser/Node/Scalar/MagicConst/Class_.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Scalar\\MagicConst\\Dir' => '/nikic-php-parser/PhpParser/Node/Scalar/MagicConst/Dir.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Scalar\\MagicConst\\File' => '/nikic-php-parser/PhpParser/Node/Scalar/MagicConst/File.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Scalar\\MagicConst\\Function_' => '/nikic-php-parser/PhpParser/Node/Scalar/MagicConst/Function_.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Scalar\\MagicConst\\Line' => '/nikic-php-parser/PhpParser/Node/Scalar/MagicConst/Line.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Scalar\\MagicConst\\Method' => '/nikic-php-parser/PhpParser/Node/Scalar/MagicConst/Method.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Scalar\\MagicConst\\Namespace_' => '/nikic-php-parser/PhpParser/Node/Scalar/MagicConst/Namespace_.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Scalar\\MagicConst\\Trait_' => '/nikic-php-parser/PhpParser/Node/Scalar/MagicConst/Trait_.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Scalar\\String_' => '/nikic-php-parser/PhpParser/Node/Scalar/String_.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Stmt' => '/nikic-php-parser/PhpParser/Node/Stmt.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\Break_' => '/nikic-php-parser/PhpParser/Node/Stmt/Break_.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\Case_' => '/nikic-php-parser/PhpParser/Node/Stmt/Case_.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\Catch_' => '/nikic-php-parser/PhpParser/Node/Stmt/Catch_.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\ClassConst' => '/nikic-php-parser/PhpParser/Node/Stmt/ClassConst.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\ClassLike' => '/nikic-php-parser/PhpParser/Node/Stmt/ClassLike.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\ClassMethod' => '/nikic-php-parser/PhpParser/Node/Stmt/ClassMethod.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\Class_' => '/nikic-php-parser/PhpParser/Node/Stmt/Class_.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\Const_' => '/nikic-php-parser/PhpParser/Node/Stmt/Const_.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\Continue_' => '/nikic-php-parser/PhpParser/Node/Stmt/Continue_.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\DeclareDeclare' => '/nikic-php-parser/PhpParser/Node/Stmt/DeclareDeclare.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\Declare_' => '/nikic-php-parser/PhpParser/Node/Stmt/Declare_.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\Do_' => '/nikic-php-parser/PhpParser/Node/Stmt/Do_.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\Echo_' => '/nikic-php-parser/PhpParser/Node/Stmt/Echo_.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\ElseIf_' => '/nikic-php-parser/PhpParser/Node/Stmt/ElseIf_.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\Else_' => '/nikic-php-parser/PhpParser/Node/Stmt/Else_.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\EnumCase' => '/nikic-php-parser/PhpParser/Node/Stmt/EnumCase.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\Enum_' => '/nikic-php-parser/PhpParser/Node/Stmt/Enum_.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\Expression' => '/nikic-php-parser/PhpParser/Node/Stmt/Expression.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\Finally_' => '/nikic-php-parser/PhpParser/Node/Stmt/Finally_.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\For_' => '/nikic-php-parser/PhpParser/Node/Stmt/For_.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\Foreach_' => '/nikic-php-parser/PhpParser/Node/Stmt/Foreach_.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\Function_' => '/nikic-php-parser/PhpParser/Node/Stmt/Function_.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\Global_' => '/nikic-php-parser/PhpParser/Node/Stmt/Global_.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\Goto_' => '/nikic-php-parser/PhpParser/Node/Stmt/Goto_.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\GroupUse' => '/nikic-php-parser/PhpParser/Node/Stmt/GroupUse.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\HaltCompiler' => '/nikic-php-parser/PhpParser/Node/Stmt/HaltCompiler.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\If_' => '/nikic-php-parser/PhpParser/Node/Stmt/If_.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\InlineHTML' => '/nikic-php-parser/PhpParser/Node/Stmt/InlineHTML.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\Interface_' => '/nikic-php-parser/PhpParser/Node/Stmt/Interface_.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\Label' => '/nikic-php-parser/PhpParser/Node/Stmt/Label.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\Namespace_' => '/nikic-php-parser/PhpParser/Node/Stmt/Namespace_.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\Nop' => '/nikic-php-parser/PhpParser/Node/Stmt/Nop.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\Property' => '/nikic-php-parser/PhpParser/Node/Stmt/Property.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\PropertyProperty' => '/nikic-php-parser/PhpParser/Node/Stmt/PropertyProperty.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\Return_' => '/nikic-php-parser/PhpParser/Node/Stmt/Return_.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\StaticVar' => '/nikic-php-parser/PhpParser/Node/Stmt/StaticVar.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\Static_' => '/nikic-php-parser/PhpParser/Node/Stmt/Static_.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\Switch_' => '/nikic-php-parser/PhpParser/Node/Stmt/Switch_.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\Throw_' => '/nikic-php-parser/PhpParser/Node/Stmt/Throw_.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\TraitUse' => '/nikic-php-parser/PhpParser/Node/Stmt/TraitUse.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\TraitUseAdaptation' => '/nikic-php-parser/PhpParser/Node/Stmt/TraitUseAdaptation.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\TraitUseAdaptation\\Alias' => '/nikic-php-parser/PhpParser/Node/Stmt/TraitUseAdaptation/Alias.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\TraitUseAdaptation\\Precedence' => '/nikic-php-parser/PhpParser/Node/Stmt/TraitUseAdaptation/Precedence.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\Trait_' => '/nikic-php-parser/PhpParser/Node/Stmt/Trait_.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\TryCatch' => '/nikic-php-parser/PhpParser/Node/Stmt/TryCatch.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\Unset_' => '/nikic-php-parser/PhpParser/Node/Stmt/Unset_.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\UseUse' => '/nikic-php-parser/PhpParser/Node/Stmt/UseUse.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\Use_' => '/nikic-php-parser/PhpParser/Node/Stmt/Use_.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\While_' => '/nikic-php-parser/PhpParser/Node/Stmt/While_.php', + 'PHPUnitPHAR\\PhpParser\\Node\\UnionType' => '/nikic-php-parser/PhpParser/Node/UnionType.php', + 'PHPUnitPHAR\\PhpParser\\Node\\VarLikeIdentifier' => '/nikic-php-parser/PhpParser/Node/VarLikeIdentifier.php', + 'PHPUnitPHAR\\PhpParser\\Node\\VariadicPlaceholder' => '/nikic-php-parser/PhpParser/Node/VariadicPlaceholder.php', + 'PHPUnitPHAR\\PhpParser\\Parser' => '/nikic-php-parser/PhpParser/Parser.php', + 'PHPUnitPHAR\\PhpParser\\ParserAbstract' => '/nikic-php-parser/PhpParser/ParserAbstract.php', + 'PHPUnitPHAR\\PhpParser\\ParserFactory' => '/nikic-php-parser/PhpParser/ParserFactory.php', + 'PHPUnitPHAR\\PhpParser\\Parser\\Multiple' => '/nikic-php-parser/PhpParser/Parser/Multiple.php', + 'PHPUnitPHAR\\PhpParser\\Parser\\Php5' => '/nikic-php-parser/PhpParser/Parser/Php5.php', + 'PHPUnitPHAR\\PhpParser\\Parser\\Php7' => '/nikic-php-parser/PhpParser/Parser/Php7.php', + 'PHPUnitPHAR\\PhpParser\\Parser\\Tokens' => '/nikic-php-parser/PhpParser/Parser/Tokens.php', + 'PHPUnitPHAR\\PhpParser\\PrettyPrinterAbstract' => '/nikic-php-parser/PhpParser/PrettyPrinterAbstract.php', + 'PHPUnitPHAR\\PhpParser\\PrettyPrinter\\Standard' => '/nikic-php-parser/PhpParser/PrettyPrinter/Standard.php', + 'PHPUnitPHAR\\SebastianBergmann\\CliParser\\AmbiguousOptionException' => '/sebastian-cli-parser/exceptions/AmbiguousOptionException.php', + 'PHPUnitPHAR\\SebastianBergmann\\CliParser\\Exception' => '/sebastian-cli-parser/exceptions/Exception.php', + 'PHPUnitPHAR\\SebastianBergmann\\CliParser\\OptionDoesNotAllowArgumentException' => '/sebastian-cli-parser/exceptions/OptionDoesNotAllowArgumentException.php', + 'PHPUnitPHAR\\SebastianBergmann\\CliParser\\Parser' => '/sebastian-cli-parser/Parser.php', + 'PHPUnitPHAR\\SebastianBergmann\\CliParser\\RequiredOptionArgumentMissingException' => '/sebastian-cli-parser/exceptions/RequiredOptionArgumentMissingException.php', + 'PHPUnitPHAR\\SebastianBergmann\\CliParser\\UnknownOptionException' => '/sebastian-cli-parser/exceptions/UnknownOptionException.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\BranchAndPathCoverageNotSupportedException' => '/php-code-coverage/Exception/BranchAndPathCoverageNotSupportedException.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\CodeCoverage' => '/php-code-coverage/CodeCoverage.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\DeadCodeDetectionNotSupportedException' => '/php-code-coverage/Exception/DeadCodeDetectionNotSupportedException.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Driver\\Driver' => '/php-code-coverage/Driver/Driver.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Driver\\PathExistsButIsNotDirectoryException' => '/php-code-coverage/Exception/PathExistsButIsNotDirectoryException.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Driver\\PcovDriver' => '/php-code-coverage/Driver/PcovDriver.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Driver\\PcovNotAvailableException' => '/php-code-coverage/Exception/PcovNotAvailableException.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Driver\\PhpdbgDriver' => '/php-code-coverage/Driver/PhpdbgDriver.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Driver\\PhpdbgNotAvailableException' => '/php-code-coverage/Exception/PhpdbgNotAvailableException.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Driver\\Selector' => '/php-code-coverage/Driver/Selector.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Driver\\WriteOperationFailedException' => '/php-code-coverage/Exception/WriteOperationFailedException.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Driver\\WrongXdebugVersionException' => '/php-code-coverage/Exception/WrongXdebugVersionException.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Driver\\Xdebug2Driver' => '/php-code-coverage/Driver/Xdebug2Driver.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Driver\\Xdebug2NotEnabledException' => '/php-code-coverage/Exception/Xdebug2NotEnabledException.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Driver\\Xdebug3Driver' => '/php-code-coverage/Driver/Xdebug3Driver.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Driver\\Xdebug3NotEnabledException' => '/php-code-coverage/Exception/Xdebug3NotEnabledException.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Driver\\XdebugNotAvailableException' => '/php-code-coverage/Exception/XdebugNotAvailableException.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Exception' => '/php-code-coverage/Exception/Exception.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Filter' => '/php-code-coverage/Filter.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\InvalidArgumentException' => '/php-code-coverage/Exception/InvalidArgumentException.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\NoCodeCoverageDriverAvailableException' => '/php-code-coverage/Exception/NoCodeCoverageDriverAvailableException.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\NoCodeCoverageDriverWithPathCoverageSupportAvailableException' => '/php-code-coverage/Exception/NoCodeCoverageDriverWithPathCoverageSupportAvailableException.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Node\\AbstractNode' => '/php-code-coverage/Node/AbstractNode.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Node\\Builder' => '/php-code-coverage/Node/Builder.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Node\\CrapIndex' => '/php-code-coverage/Node/CrapIndex.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Node\\Directory' => '/php-code-coverage/Node/Directory.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Node\\File' => '/php-code-coverage/Node/File.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Node\\Iterator' => '/php-code-coverage/Node/Iterator.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\ParserException' => '/php-code-coverage/Exception/ParserException.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\ProcessedCodeCoverageData' => '/php-code-coverage/ProcessedCodeCoverageData.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\RawCodeCoverageData' => '/php-code-coverage/RawCodeCoverageData.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\ReflectionException' => '/php-code-coverage/Exception/ReflectionException.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\ReportAlreadyFinalizedException' => '/php-code-coverage/Exception/ReportAlreadyFinalizedException.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Report\\Clover' => '/php-code-coverage/Report/Clover.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Report\\Cobertura' => '/php-code-coverage/Report/Cobertura.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Report\\Crap4j' => '/php-code-coverage/Report/Crap4j.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Report\\Html\\Dashboard' => '/php-code-coverage/Report/Html/Renderer/Dashboard.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Report\\Html\\Directory' => '/php-code-coverage/Report/Html/Renderer/Directory.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Report\\Html\\Facade' => '/php-code-coverage/Report/Html/Facade.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Report\\Html\\File' => '/php-code-coverage/Report/Html/Renderer/File.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Report\\Html\\Renderer' => '/php-code-coverage/Report/Html/Renderer.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Report\\PHP' => '/php-code-coverage/Report/PHP.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Report\\Text' => '/php-code-coverage/Report/Text.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Report\\Xml\\BuildInformation' => '/php-code-coverage/Report/Xml/BuildInformation.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Report\\Xml\\Coverage' => '/php-code-coverage/Report/Xml/Coverage.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Report\\Xml\\Directory' => '/php-code-coverage/Report/Xml/Directory.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Report\\Xml\\Facade' => '/php-code-coverage/Report/Xml/Facade.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Report\\Xml\\File' => '/php-code-coverage/Report/Xml/File.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Report\\Xml\\Method' => '/php-code-coverage/Report/Xml/Method.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Report\\Xml\\Node' => '/php-code-coverage/Report/Xml/Node.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Report\\Xml\\Project' => '/php-code-coverage/Report/Xml/Project.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Report\\Xml\\Report' => '/php-code-coverage/Report/Xml/Report.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Report\\Xml\\Source' => '/php-code-coverage/Report/Xml/Source.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Report\\Xml\\Tests' => '/php-code-coverage/Report/Xml/Tests.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Report\\Xml\\Totals' => '/php-code-coverage/Report/Xml/Totals.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Report\\Xml\\Unit' => '/php-code-coverage/Report/Xml/Unit.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\StaticAnalysisCacheNotConfiguredException' => '/php-code-coverage/Exception/StaticAnalysisCacheNotConfiguredException.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\StaticAnalysis\\CacheWarmer' => '/php-code-coverage/StaticAnalysis/CacheWarmer.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\StaticAnalysis\\CachingFileAnalyser' => '/php-code-coverage/StaticAnalysis/CachingFileAnalyser.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\StaticAnalysis\\CodeUnitFindingVisitor' => '/php-code-coverage/StaticAnalysis/CodeUnitFindingVisitor.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\StaticAnalysis\\ExecutableLinesFindingVisitor' => '/php-code-coverage/StaticAnalysis/ExecutableLinesFindingVisitor.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\StaticAnalysis\\FileAnalyser' => '/php-code-coverage/StaticAnalysis/FileAnalyser.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\StaticAnalysis\\IgnoredLinesFindingVisitor' => '/php-code-coverage/StaticAnalysis/IgnoredLinesFindingVisitor.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\StaticAnalysis\\ParsingFileAnalyser' => '/php-code-coverage/StaticAnalysis/ParsingFileAnalyser.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\TestIdMissingException' => '/php-code-coverage/Exception/TestIdMissingException.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\UnintentionallyCoveredCodeException' => '/php-code-coverage/Exception/UnintentionallyCoveredCodeException.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Util\\DirectoryCouldNotBeCreatedException' => '/php-code-coverage/Exception/DirectoryCouldNotBeCreatedException.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Util\\Filesystem' => '/php-code-coverage/Util/Filesystem.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Util\\Percentage' => '/php-code-coverage/Util/Percentage.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Version' => '/php-code-coverage/Version.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\XmlException' => '/php-code-coverage/Exception/XmlException.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeUnitReverseLookup\\Wizard' => '/sebastian-code-unit-reverse-lookup/Wizard.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeUnit\\ClassMethodUnit' => '/sebastian-code-unit/ClassMethodUnit.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeUnit\\ClassUnit' => '/sebastian-code-unit/ClassUnit.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeUnit\\CodeUnit' => '/sebastian-code-unit/CodeUnit.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeUnit\\CodeUnitCollection' => '/sebastian-code-unit/CodeUnitCollection.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeUnit\\CodeUnitCollectionIterator' => '/sebastian-code-unit/CodeUnitCollectionIterator.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeUnit\\Exception' => '/sebastian-code-unit/exceptions/Exception.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeUnit\\FunctionUnit' => '/sebastian-code-unit/FunctionUnit.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeUnit\\InterfaceMethodUnit' => '/sebastian-code-unit/InterfaceMethodUnit.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeUnit\\InterfaceUnit' => '/sebastian-code-unit/InterfaceUnit.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeUnit\\InvalidCodeUnitException' => '/sebastian-code-unit/exceptions/InvalidCodeUnitException.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeUnit\\Mapper' => '/sebastian-code-unit/Mapper.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeUnit\\NoTraitException' => '/sebastian-code-unit/exceptions/NoTraitException.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeUnit\\ReflectionException' => '/sebastian-code-unit/exceptions/ReflectionException.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeUnit\\TraitMethodUnit' => '/sebastian-code-unit/TraitMethodUnit.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeUnit\\TraitUnit' => '/sebastian-code-unit/TraitUnit.php', + 'PHPUnitPHAR\\SebastianBergmann\\Comparator\\ArrayComparator' => '/sebastian-comparator/ArrayComparator.php', + 'PHPUnitPHAR\\SebastianBergmann\\Comparator\\Comparator' => '/sebastian-comparator/Comparator.php', + 'PHPUnitPHAR\\SebastianBergmann\\Comparator\\ComparisonFailure' => '/sebastian-comparator/ComparisonFailure.php', + 'PHPUnitPHAR\\SebastianBergmann\\Comparator\\DOMNodeComparator' => '/sebastian-comparator/DOMNodeComparator.php', + 'PHPUnitPHAR\\SebastianBergmann\\Comparator\\DateTimeComparator' => '/sebastian-comparator/DateTimeComparator.php', + 'PHPUnitPHAR\\SebastianBergmann\\Comparator\\DoubleComparator' => '/sebastian-comparator/DoubleComparator.php', + 'PHPUnitPHAR\\SebastianBergmann\\Comparator\\Exception' => '/sebastian-comparator/exceptions/Exception.php', + 'PHPUnitPHAR\\SebastianBergmann\\Comparator\\ExceptionComparator' => '/sebastian-comparator/ExceptionComparator.php', + 'PHPUnitPHAR\\SebastianBergmann\\Comparator\\Factory' => '/sebastian-comparator/Factory.php', + 'PHPUnitPHAR\\SebastianBergmann\\Comparator\\MockObjectComparator' => '/sebastian-comparator/MockObjectComparator.php', + 'PHPUnitPHAR\\SebastianBergmann\\Comparator\\NumericComparator' => '/sebastian-comparator/NumericComparator.php', + 'PHPUnitPHAR\\SebastianBergmann\\Comparator\\ObjectComparator' => '/sebastian-comparator/ObjectComparator.php', + 'PHPUnitPHAR\\SebastianBergmann\\Comparator\\ResourceComparator' => '/sebastian-comparator/ResourceComparator.php', + 'PHPUnitPHAR\\SebastianBergmann\\Comparator\\RuntimeException' => '/sebastian-comparator/exceptions/RuntimeException.php', + 'PHPUnitPHAR\\SebastianBergmann\\Comparator\\ScalarComparator' => '/sebastian-comparator/ScalarComparator.php', + 'PHPUnitPHAR\\SebastianBergmann\\Comparator\\SplObjectStorageComparator' => '/sebastian-comparator/SplObjectStorageComparator.php', + 'PHPUnitPHAR\\SebastianBergmann\\Comparator\\TypeComparator' => '/sebastian-comparator/TypeComparator.php', + 'PHPUnitPHAR\\SebastianBergmann\\Complexity\\Calculator' => '/sebastian-complexity/Calculator.php', + 'PHPUnitPHAR\\SebastianBergmann\\Complexity\\Complexity' => '/sebastian-complexity/Complexity/Complexity.php', + 'PHPUnitPHAR\\SebastianBergmann\\Complexity\\ComplexityCalculatingVisitor' => '/sebastian-complexity/Visitor/ComplexityCalculatingVisitor.php', + 'PHPUnitPHAR\\SebastianBergmann\\Complexity\\ComplexityCollection' => '/sebastian-complexity/Complexity/ComplexityCollection.php', + 'PHPUnitPHAR\\SebastianBergmann\\Complexity\\ComplexityCollectionIterator' => '/sebastian-complexity/Complexity/ComplexityCollectionIterator.php', + 'PHPUnitPHAR\\SebastianBergmann\\Complexity\\CyclomaticComplexityCalculatingVisitor' => '/sebastian-complexity/Visitor/CyclomaticComplexityCalculatingVisitor.php', + 'PHPUnitPHAR\\SebastianBergmann\\Complexity\\Exception' => '/sebastian-complexity/Exception/Exception.php', + 'PHPUnitPHAR\\SebastianBergmann\\Complexity\\RuntimeException' => '/sebastian-complexity/Exception/RuntimeException.php', + 'PHPUnitPHAR\\SebastianBergmann\\Diff\\Chunk' => '/sebastian-diff/Chunk.php', + 'PHPUnitPHAR\\SebastianBergmann\\Diff\\ConfigurationException' => '/sebastian-diff/Exception/ConfigurationException.php', + 'PHPUnitPHAR\\SebastianBergmann\\Diff\\Diff' => '/sebastian-diff/Diff.php', + 'PHPUnitPHAR\\SebastianBergmann\\Diff\\Differ' => '/sebastian-diff/Differ.php', + 'PHPUnitPHAR\\SebastianBergmann\\Diff\\Exception' => '/sebastian-diff/Exception/Exception.php', + 'PHPUnitPHAR\\SebastianBergmann\\Diff\\InvalidArgumentException' => '/sebastian-diff/Exception/InvalidArgumentException.php', + 'PHPUnitPHAR\\SebastianBergmann\\Diff\\Line' => '/sebastian-diff/Line.php', + 'PHPUnitPHAR\\SebastianBergmann\\Diff\\LongestCommonSubsequenceCalculator' => '/sebastian-diff/LongestCommonSubsequenceCalculator.php', + 'PHPUnitPHAR\\SebastianBergmann\\Diff\\MemoryEfficientLongestCommonSubsequenceCalculator' => '/sebastian-diff/MemoryEfficientLongestCommonSubsequenceCalculator.php', + 'PHPUnitPHAR\\SebastianBergmann\\Diff\\Output\\AbstractChunkOutputBuilder' => '/sebastian-diff/Output/AbstractChunkOutputBuilder.php', + 'PHPUnitPHAR\\SebastianBergmann\\Diff\\Output\\DiffOnlyOutputBuilder' => '/sebastian-diff/Output/DiffOnlyOutputBuilder.php', + 'PHPUnitPHAR\\SebastianBergmann\\Diff\\Output\\DiffOutputBuilderInterface' => '/sebastian-diff/Output/DiffOutputBuilderInterface.php', + 'PHPUnitPHAR\\SebastianBergmann\\Diff\\Output\\StrictUnifiedDiffOutputBuilder' => '/sebastian-diff/Output/StrictUnifiedDiffOutputBuilder.php', + 'PHPUnitPHAR\\SebastianBergmann\\Diff\\Output\\UnifiedDiffOutputBuilder' => '/sebastian-diff/Output/UnifiedDiffOutputBuilder.php', + 'PHPUnitPHAR\\SebastianBergmann\\Diff\\Parser' => '/sebastian-diff/Parser.php', + 'PHPUnitPHAR\\SebastianBergmann\\Diff\\TimeEfficientLongestCommonSubsequenceCalculator' => '/sebastian-diff/TimeEfficientLongestCommonSubsequenceCalculator.php', + 'PHPUnitPHAR\\SebastianBergmann\\Environment\\Console' => '/sebastian-environment/Console.php', + 'PHPUnitPHAR\\SebastianBergmann\\Environment\\OperatingSystem' => '/sebastian-environment/OperatingSystem.php', + 'PHPUnitPHAR\\SebastianBergmann\\Environment\\Runtime' => '/sebastian-environment/Runtime.php', + 'PHPUnitPHAR\\SebastianBergmann\\Exporter\\Exporter' => '/sebastian-exporter/Exporter.php', + 'PHPUnitPHAR\\SebastianBergmann\\FileIterator\\Facade' => '/php-file-iterator/Facade.php', + 'PHPUnitPHAR\\SebastianBergmann\\FileIterator\\Factory' => '/php-file-iterator/Factory.php', + 'PHPUnitPHAR\\SebastianBergmann\\FileIterator\\Iterator' => '/php-file-iterator/Iterator.php', + 'PHPUnitPHAR\\SebastianBergmann\\GlobalState\\CodeExporter' => '/sebastian-global-state/CodeExporter.php', + 'PHPUnitPHAR\\SebastianBergmann\\GlobalState\\Exception' => '/sebastian-global-state/exceptions/Exception.php', + 'PHPUnitPHAR\\SebastianBergmann\\GlobalState\\ExcludeList' => '/sebastian-global-state/ExcludeList.php', + 'PHPUnitPHAR\\SebastianBergmann\\GlobalState\\Restorer' => '/sebastian-global-state/Restorer.php', + 'PHPUnitPHAR\\SebastianBergmann\\GlobalState\\RuntimeException' => '/sebastian-global-state/exceptions/RuntimeException.php', + 'PHPUnitPHAR\\SebastianBergmann\\GlobalState\\Snapshot' => '/sebastian-global-state/Snapshot.php', + 'PHPUnitPHAR\\SebastianBergmann\\Invoker\\Exception' => '/php-invoker/exceptions/Exception.php', + 'PHPUnitPHAR\\SebastianBergmann\\Invoker\\Invoker' => '/php-invoker/Invoker.php', + 'PHPUnitPHAR\\SebastianBergmann\\Invoker\\ProcessControlExtensionNotLoadedException' => '/php-invoker/exceptions/ProcessControlExtensionNotLoadedException.php', + 'PHPUnitPHAR\\SebastianBergmann\\Invoker\\TimeoutException' => '/php-invoker/exceptions/TimeoutException.php', + 'PHPUnitPHAR\\SebastianBergmann\\LinesOfCode\\Counter' => '/sebastian-lines-of-code/Counter.php', + 'PHPUnitPHAR\\SebastianBergmann\\LinesOfCode\\Exception' => '/sebastian-lines-of-code/Exception/Exception.php', + 'PHPUnitPHAR\\SebastianBergmann\\LinesOfCode\\IllogicalValuesException' => '/sebastian-lines-of-code/Exception/IllogicalValuesException.php', + 'PHPUnitPHAR\\SebastianBergmann\\LinesOfCode\\LineCountingVisitor' => '/sebastian-lines-of-code/LineCountingVisitor.php', + 'PHPUnitPHAR\\SebastianBergmann\\LinesOfCode\\LinesOfCode' => '/sebastian-lines-of-code/LinesOfCode.php', + 'PHPUnitPHAR\\SebastianBergmann\\LinesOfCode\\NegativeValueException' => '/sebastian-lines-of-code/Exception/NegativeValueException.php', + 'PHPUnitPHAR\\SebastianBergmann\\LinesOfCode\\RuntimeException' => '/sebastian-lines-of-code/Exception/RuntimeException.php', + 'PHPUnitPHAR\\SebastianBergmann\\ObjectEnumerator\\Enumerator' => '/sebastian-object-enumerator/Enumerator.php', + 'PHPUnitPHAR\\SebastianBergmann\\ObjectEnumerator\\Exception' => '/sebastian-object-enumerator/Exception.php', + 'PHPUnitPHAR\\SebastianBergmann\\ObjectEnumerator\\InvalidArgumentException' => '/sebastian-object-enumerator/InvalidArgumentException.php', + 'PHPUnitPHAR\\SebastianBergmann\\ObjectReflector\\Exception' => '/sebastian-object-reflector/Exception.php', + 'PHPUnitPHAR\\SebastianBergmann\\ObjectReflector\\InvalidArgumentException' => '/sebastian-object-reflector/InvalidArgumentException.php', + 'PHPUnitPHAR\\SebastianBergmann\\ObjectReflector\\ObjectReflector' => '/sebastian-object-reflector/ObjectReflector.php', + 'PHPUnitPHAR\\SebastianBergmann\\RecursionContext\\Context' => '/sebastian-recursion-context/Context.php', + 'PHPUnitPHAR\\SebastianBergmann\\RecursionContext\\Exception' => '/sebastian-recursion-context/Exception.php', + 'PHPUnitPHAR\\SebastianBergmann\\RecursionContext\\InvalidArgumentException' => '/sebastian-recursion-context/InvalidArgumentException.php', + 'PHPUnitPHAR\\SebastianBergmann\\ResourceOperations\\ResourceOperations' => '/sebastian-resource-operations/ResourceOperations.php', + 'PHPUnitPHAR\\SebastianBergmann\\Template\\Exception' => '/php-text-template/exceptions/Exception.php', + 'PHPUnitPHAR\\SebastianBergmann\\Template\\InvalidArgumentException' => '/php-text-template/exceptions/InvalidArgumentException.php', + 'PHPUnitPHAR\\SebastianBergmann\\Template\\RuntimeException' => '/php-text-template/exceptions/RuntimeException.php', + 'PHPUnitPHAR\\SebastianBergmann\\Template\\Template' => '/php-text-template/Template.php', + 'PHPUnitPHAR\\SebastianBergmann\\Timer\\Duration' => '/php-timer/Duration.php', + 'PHPUnitPHAR\\SebastianBergmann\\Timer\\Exception' => '/php-timer/exceptions/Exception.php', + 'PHPUnitPHAR\\SebastianBergmann\\Timer\\NoActiveTimerException' => '/php-timer/exceptions/NoActiveTimerException.php', + 'PHPUnitPHAR\\SebastianBergmann\\Timer\\ResourceUsageFormatter' => '/php-timer/ResourceUsageFormatter.php', + 'PHPUnitPHAR\\SebastianBergmann\\Timer\\TimeSinceStartOfRequestNotAvailableException' => '/php-timer/exceptions/TimeSinceStartOfRequestNotAvailableException.php', + 'PHPUnitPHAR\\SebastianBergmann\\Timer\\Timer' => '/php-timer/Timer.php', + 'PHPUnitPHAR\\SebastianBergmann\\Type\\CallableType' => '/sebastian-type/type/CallableType.php', + 'PHPUnitPHAR\\SebastianBergmann\\Type\\Exception' => '/sebastian-type/exception/Exception.php', + 'PHPUnitPHAR\\SebastianBergmann\\Type\\FalseType' => '/sebastian-type/type/FalseType.php', + 'PHPUnitPHAR\\SebastianBergmann\\Type\\GenericObjectType' => '/sebastian-type/type/GenericObjectType.php', + 'PHPUnitPHAR\\SebastianBergmann\\Type\\IntersectionType' => '/sebastian-type/type/IntersectionType.php', + 'PHPUnitPHAR\\SebastianBergmann\\Type\\IterableType' => '/sebastian-type/type/IterableType.php', + 'PHPUnitPHAR\\SebastianBergmann\\Type\\MixedType' => '/sebastian-type/type/MixedType.php', + 'PHPUnitPHAR\\SebastianBergmann\\Type\\NeverType' => '/sebastian-type/type/NeverType.php', + 'PHPUnitPHAR\\SebastianBergmann\\Type\\NullType' => '/sebastian-type/type/NullType.php', + 'PHPUnitPHAR\\SebastianBergmann\\Type\\ObjectType' => '/sebastian-type/type/ObjectType.php', + 'PHPUnitPHAR\\SebastianBergmann\\Type\\Parameter' => '/sebastian-type/Parameter.php', + 'PHPUnitPHAR\\SebastianBergmann\\Type\\ReflectionMapper' => '/sebastian-type/ReflectionMapper.php', + 'PHPUnitPHAR\\SebastianBergmann\\Type\\RuntimeException' => '/sebastian-type/exception/RuntimeException.php', + 'PHPUnitPHAR\\SebastianBergmann\\Type\\SimpleType' => '/sebastian-type/type/SimpleType.php', + 'PHPUnitPHAR\\SebastianBergmann\\Type\\StaticType' => '/sebastian-type/type/StaticType.php', + 'PHPUnitPHAR\\SebastianBergmann\\Type\\TrueType' => '/sebastian-type/type/TrueType.php', + 'PHPUnitPHAR\\SebastianBergmann\\Type\\Type' => '/sebastian-type/type/Type.php', + 'PHPUnitPHAR\\SebastianBergmann\\Type\\TypeName' => '/sebastian-type/TypeName.php', + 'PHPUnitPHAR\\SebastianBergmann\\Type\\UnionType' => '/sebastian-type/type/UnionType.php', + 'PHPUnitPHAR\\SebastianBergmann\\Type\\UnknownType' => '/sebastian-type/type/UnknownType.php', + 'PHPUnitPHAR\\SebastianBergmann\\Type\\VoidType' => '/sebastian-type/type/VoidType.php', + 'PHPUnitPHAR\\SebastianBergmann\\Version' => '/sebastian-version/Version.php', + 'PHPUnitPHAR\\TheSeer\\Tokenizer\\Exception' => '/theseer-tokenizer/Exception.php', + 'PHPUnitPHAR\\TheSeer\\Tokenizer\\NamespaceUri' => '/theseer-tokenizer/NamespaceUri.php', + 'PHPUnitPHAR\\TheSeer\\Tokenizer\\NamespaceUriException' => '/theseer-tokenizer/NamespaceUriException.php', + 'PHPUnitPHAR\\TheSeer\\Tokenizer\\Token' => '/theseer-tokenizer/Token.php', + 'PHPUnitPHAR\\TheSeer\\Tokenizer\\TokenCollection' => '/theseer-tokenizer/TokenCollection.php', + 'PHPUnitPHAR\\TheSeer\\Tokenizer\\TokenCollectionException' => '/theseer-tokenizer/TokenCollectionException.php', + 'PHPUnitPHAR\\TheSeer\\Tokenizer\\Tokenizer' => '/theseer-tokenizer/Tokenizer.php', + 'PHPUnitPHAR\\TheSeer\\Tokenizer\\XMLSerializer' => '/theseer-tokenizer/XMLSerializer.php', + 'PHPUnitPHAR\\Webmozart\\Assert\\Assert' => '/webmozart-assert/Assert.php', + 'PHPUnitPHAR\\Webmozart\\Assert\\InvalidArgumentException' => '/webmozart-assert/InvalidArgumentException.php', + 'PHPUnitPHAR\\Webmozart\\Assert\\Mixin' => '/webmozart-assert/Mixin.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock' => '/phpdocumentor-reflection-docblock/DocBlock.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlockFactory' => '/phpdocumentor-reflection-docblock/DocBlockFactory.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlockFactoryInterface' => '/phpdocumentor-reflection-docblock/DocBlockFactoryInterface.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Description' => '/phpdocumentor-reflection-docblock/DocBlock/Description.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\DescriptionFactory' => '/phpdocumentor-reflection-docblock/DocBlock/DescriptionFactory.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\ExampleFinder' => '/phpdocumentor-reflection-docblock/DocBlock/ExampleFinder.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Serializer' => '/phpdocumentor-reflection-docblock/DocBlock/Serializer.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\StandardTagFactory' => '/phpdocumentor-reflection-docblock/DocBlock/StandardTagFactory.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Tag' => '/phpdocumentor-reflection-docblock/DocBlock/Tag.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\TagFactory' => '/phpdocumentor-reflection-docblock/DocBlock/TagFactory.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Author' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Author.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Tags\\BaseTag' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/BaseTag.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Covers' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Covers.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Deprecated' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Deprecated.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Example' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Example.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Factory\\StaticMethod' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Factory/StaticMethod.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Formatter' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Formatter.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Formatter\\AlignFormatter' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Formatter/AlignFormatter.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Formatter\\PassthroughFormatter' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Formatter/PassthroughFormatter.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Generic' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Generic.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Tags\\InvalidTag' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/InvalidTag.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Link' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Link.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Method' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Method.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Param' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Param.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Property' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Property.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Tags\\PropertyRead' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/PropertyRead.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Tags\\PropertyWrite' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/PropertyWrite.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Reference\\Fqsen' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Reference/Fqsen.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Reference\\Reference' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Reference/Reference.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Reference\\Url' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Reference/Url.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Return_' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Return_.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Tags\\See' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/See.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Since' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Since.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Source' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Source.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Tags\\TagWithType' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/TagWithType.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Throws' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Throws.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Uses' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Uses.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Var_' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Var_.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Version' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Version.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Element' => '/phpdocumentor-reflection-common/Element.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Exception\\PcreException' => '/phpdocumentor-reflection-docblock/Exception/PcreException.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\File' => '/phpdocumentor-reflection-common/File.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Fqsen' => '/phpdocumentor-reflection-common/Fqsen.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\FqsenResolver' => '/phpdocumentor-type-resolver/FqsenResolver.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Location' => '/phpdocumentor-reflection-common/Location.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Project' => '/phpdocumentor-reflection-common/Project.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\ProjectFactory' => '/phpdocumentor-reflection-common/ProjectFactory.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\PseudoType' => '/phpdocumentor-type-resolver/PseudoType.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\PseudoTypes\\ArrayShape' => '/phpdocumentor-type-resolver/PseudoTypes/ArrayShape.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\PseudoTypes\\ArrayShapeItem' => '/phpdocumentor-type-resolver/PseudoTypes/ArrayShapeItem.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\PseudoTypes\\CallableString' => '/phpdocumentor-type-resolver/PseudoTypes/CallableString.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\PseudoTypes\\ConstExpression' => '/phpdocumentor-type-resolver/PseudoTypes/ConstExpression.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\PseudoTypes\\False_' => '/phpdocumentor-type-resolver/PseudoTypes/False_.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\PseudoTypes\\FloatValue' => '/phpdocumentor-type-resolver/PseudoTypes/FloatValue.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\PseudoTypes\\HtmlEscapedString' => '/phpdocumentor-type-resolver/PseudoTypes/HtmlEscapedString.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\PseudoTypes\\IntegerRange' => '/phpdocumentor-type-resolver/PseudoTypes/IntegerRange.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\PseudoTypes\\IntegerValue' => '/phpdocumentor-type-resolver/PseudoTypes/IntegerValue.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\PseudoTypes\\List_' => '/phpdocumentor-type-resolver/PseudoTypes/List_.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\PseudoTypes\\LiteralString' => '/phpdocumentor-type-resolver/PseudoTypes/LiteralString.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\PseudoTypes\\LowercaseString' => '/phpdocumentor-type-resolver/PseudoTypes/LowercaseString.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\PseudoTypes\\NegativeInteger' => '/phpdocumentor-type-resolver/PseudoTypes/NegativeInteger.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\PseudoTypes\\NonEmptyList' => '/phpdocumentor-type-resolver/PseudoTypes/NonEmptyList.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\PseudoTypes\\NonEmptyLowercaseString' => '/phpdocumentor-type-resolver/PseudoTypes/NonEmptyLowercaseString.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\PseudoTypes\\NonEmptyString' => '/phpdocumentor-type-resolver/PseudoTypes/NonEmptyString.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\PseudoTypes\\NumericString' => '/phpdocumentor-type-resolver/PseudoTypes/NumericString.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\PseudoTypes\\Numeric_' => '/phpdocumentor-type-resolver/PseudoTypes/Numeric_.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\PseudoTypes\\PositiveInteger' => '/phpdocumentor-type-resolver/PseudoTypes/PositiveInteger.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\PseudoTypes\\StringValue' => '/phpdocumentor-type-resolver/PseudoTypes/StringValue.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\PseudoTypes\\TraitString' => '/phpdocumentor-type-resolver/PseudoTypes/TraitString.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\PseudoTypes\\True_' => '/phpdocumentor-type-resolver/PseudoTypes/True_.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Type' => '/phpdocumentor-type-resolver/Type.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\TypeResolver' => '/phpdocumentor-type-resolver/TypeResolver.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\AbstractList' => '/phpdocumentor-type-resolver/Types/AbstractList.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\AggregatedType' => '/phpdocumentor-type-resolver/Types/AggregatedType.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\ArrayKey' => '/phpdocumentor-type-resolver/Types/ArrayKey.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\Array_' => '/phpdocumentor-type-resolver/Types/Array_.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\Boolean' => '/phpdocumentor-type-resolver/Types/Boolean.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\CallableParameter' => '/phpdocumentor-type-resolver/Types/CallableParameter.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\Callable_' => '/phpdocumentor-type-resolver/Types/Callable_.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\ClassString' => '/phpdocumentor-type-resolver/Types/ClassString.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\Collection' => '/phpdocumentor-type-resolver/Types/Collection.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\Compound' => '/phpdocumentor-type-resolver/Types/Compound.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\Context' => '/phpdocumentor-type-resolver/Types/Context.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\ContextFactory' => '/phpdocumentor-type-resolver/Types/ContextFactory.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\Expression' => '/phpdocumentor-type-resolver/Types/Expression.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\Float_' => '/phpdocumentor-type-resolver/Types/Float_.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\Integer' => '/phpdocumentor-type-resolver/Types/Integer.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\InterfaceString' => '/phpdocumentor-type-resolver/Types/InterfaceString.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\Intersection' => '/phpdocumentor-type-resolver/Types/Intersection.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\Iterable_' => '/phpdocumentor-type-resolver/Types/Iterable_.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\Mixed_' => '/phpdocumentor-type-resolver/Types/Mixed_.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\Never_' => '/phpdocumentor-type-resolver/Types/Never_.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\Null_' => '/phpdocumentor-type-resolver/Types/Null_.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\Nullable' => '/phpdocumentor-type-resolver/Types/Nullable.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\Object_' => '/phpdocumentor-type-resolver/Types/Object_.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\Parent_' => '/phpdocumentor-type-resolver/Types/Parent_.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\Resource_' => '/phpdocumentor-type-resolver/Types/Resource_.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\Scalar' => '/phpdocumentor-type-resolver/Types/Scalar.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\Self_' => '/phpdocumentor-type-resolver/Types/Self_.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\Static_' => '/phpdocumentor-type-resolver/Types/Static_.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\String_' => '/phpdocumentor-type-resolver/Types/String_.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\This' => '/phpdocumentor-type-resolver/Types/This.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\Void_' => '/phpdocumentor-type-resolver/Types/Void_.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Utils' => '/phpdocumentor-reflection-docblock/Utils.php', 'PHPUnit\\Exception' => '/phpunit/Exception.php', 'PHPUnit\\Framework\\ActualValueIsNotAnObjectException' => '/phpunit/Framework/Exception/ActualValueIsNotAnObjectException.php', 'PHPUnit\\Framework\\Assert' => '/phpunit/Framework/Assert.php', @@ -292,327 +1019,6 @@ spl_autoload_register( 'PHPUnit\\Framework\\UnintentionallyCoveredCodeError' => '/phpunit/Framework/Exception/UnintentionallyCoveredCodeError.php', 'PHPUnit\\Framework\\Warning' => '/phpunit/Framework/Exception/Warning.php', 'PHPUnit\\Framework\\WarningTestCase' => '/phpunit/Framework/WarningTestCase.php', - 'PHPUnit\\PharIo\\Manifest\\Application' => '/phar-io-manifest/values/Application.php', - 'PHPUnit\\PharIo\\Manifest\\ApplicationName' => '/phar-io-manifest/values/ApplicationName.php', - 'PHPUnit\\PharIo\\Manifest\\Author' => '/phar-io-manifest/values/Author.php', - 'PHPUnit\\PharIo\\Manifest\\AuthorCollection' => '/phar-io-manifest/values/AuthorCollection.php', - 'PHPUnit\\PharIo\\Manifest\\AuthorCollectionIterator' => '/phar-io-manifest/values/AuthorCollectionIterator.php', - 'PHPUnit\\PharIo\\Manifest\\AuthorElement' => '/phar-io-manifest/xml/AuthorElement.php', - 'PHPUnit\\PharIo\\Manifest\\AuthorElementCollection' => '/phar-io-manifest/xml/AuthorElementCollection.php', - 'PHPUnit\\PharIo\\Manifest\\BundledComponent' => '/phar-io-manifest/values/BundledComponent.php', - 'PHPUnit\\PharIo\\Manifest\\BundledComponentCollection' => '/phar-io-manifest/values/BundledComponentCollection.php', - 'PHPUnit\\PharIo\\Manifest\\BundledComponentCollectionIterator' => '/phar-io-manifest/values/BundledComponentCollectionIterator.php', - 'PHPUnit\\PharIo\\Manifest\\BundlesElement' => '/phar-io-manifest/xml/BundlesElement.php', - 'PHPUnit\\PharIo\\Manifest\\ComponentElement' => '/phar-io-manifest/xml/ComponentElement.php', - 'PHPUnit\\PharIo\\Manifest\\ComponentElementCollection' => '/phar-io-manifest/xml/ComponentElementCollection.php', - 'PHPUnit\\PharIo\\Manifest\\ContainsElement' => '/phar-io-manifest/xml/ContainsElement.php', - 'PHPUnit\\PharIo\\Manifest\\CopyrightElement' => '/phar-io-manifest/xml/CopyrightElement.php', - 'PHPUnit\\PharIo\\Manifest\\CopyrightInformation' => '/phar-io-manifest/values/CopyrightInformation.php', - 'PHPUnit\\PharIo\\Manifest\\ElementCollection' => '/phar-io-manifest/xml/ElementCollection.php', - 'PHPUnit\\PharIo\\Manifest\\ElementCollectionException' => '/phar-io-manifest/exceptions/ElementCollectionException.php', - 'PHPUnit\\PharIo\\Manifest\\Email' => '/phar-io-manifest/values/Email.php', - 'PHPUnit\\PharIo\\Manifest\\Exception' => '/phar-io-manifest/exceptions/Exception.php', - 'PHPUnit\\PharIo\\Manifest\\ExtElement' => '/phar-io-manifest/xml/ExtElement.php', - 'PHPUnit\\PharIo\\Manifest\\ExtElementCollection' => '/phar-io-manifest/xml/ExtElementCollection.php', - 'PHPUnit\\PharIo\\Manifest\\Extension' => '/phar-io-manifest/values/Extension.php', - 'PHPUnit\\PharIo\\Manifest\\ExtensionElement' => '/phar-io-manifest/xml/ExtensionElement.php', - 'PHPUnit\\PharIo\\Manifest\\InvalidApplicationNameException' => '/phar-io-manifest/exceptions/InvalidApplicationNameException.php', - 'PHPUnit\\PharIo\\Manifest\\InvalidEmailException' => '/phar-io-manifest/exceptions/InvalidEmailException.php', - 'PHPUnit\\PharIo\\Manifest\\InvalidUrlException' => '/phar-io-manifest/exceptions/InvalidUrlException.php', - 'PHPUnit\\PharIo\\Manifest\\Library' => '/phar-io-manifest/values/Library.php', - 'PHPUnit\\PharIo\\Manifest\\License' => '/phar-io-manifest/values/License.php', - 'PHPUnit\\PharIo\\Manifest\\LicenseElement' => '/phar-io-manifest/xml/LicenseElement.php', - 'PHPUnit\\PharIo\\Manifest\\Manifest' => '/phar-io-manifest/values/Manifest.php', - 'PHPUnit\\PharIo\\Manifest\\ManifestDocument' => '/phar-io-manifest/xml/ManifestDocument.php', - 'PHPUnit\\PharIo\\Manifest\\ManifestDocumentException' => '/phar-io-manifest/exceptions/ManifestDocumentException.php', - 'PHPUnit\\PharIo\\Manifest\\ManifestDocumentLoadingException' => '/phar-io-manifest/exceptions/ManifestDocumentLoadingException.php', - 'PHPUnit\\PharIo\\Manifest\\ManifestDocumentMapper' => '/phar-io-manifest/ManifestDocumentMapper.php', - 'PHPUnit\\PharIo\\Manifest\\ManifestDocumentMapperException' => '/phar-io-manifest/exceptions/ManifestDocumentMapperException.php', - 'PHPUnit\\PharIo\\Manifest\\ManifestElement' => '/phar-io-manifest/xml/ManifestElement.php', - 'PHPUnit\\PharIo\\Manifest\\ManifestElementException' => '/phar-io-manifest/exceptions/ManifestElementException.php', - 'PHPUnit\\PharIo\\Manifest\\ManifestLoader' => '/phar-io-manifest/ManifestLoader.php', - 'PHPUnit\\PharIo\\Manifest\\ManifestLoaderException' => '/phar-io-manifest/exceptions/ManifestLoaderException.php', - 'PHPUnit\\PharIo\\Manifest\\ManifestSerializer' => '/phar-io-manifest/ManifestSerializer.php', - 'PHPUnit\\PharIo\\Manifest\\PhpElement' => '/phar-io-manifest/xml/PhpElement.php', - 'PHPUnit\\PharIo\\Manifest\\PhpExtensionRequirement' => '/phar-io-manifest/values/PhpExtensionRequirement.php', - 'PHPUnit\\PharIo\\Manifest\\PhpVersionRequirement' => '/phar-io-manifest/values/PhpVersionRequirement.php', - 'PHPUnit\\PharIo\\Manifest\\Requirement' => '/phar-io-manifest/values/Requirement.php', - 'PHPUnit\\PharIo\\Manifest\\RequirementCollection' => '/phar-io-manifest/values/RequirementCollection.php', - 'PHPUnit\\PharIo\\Manifest\\RequirementCollectionIterator' => '/phar-io-manifest/values/RequirementCollectionIterator.php', - 'PHPUnit\\PharIo\\Manifest\\RequiresElement' => '/phar-io-manifest/xml/RequiresElement.php', - 'PHPUnit\\PharIo\\Manifest\\Type' => '/phar-io-manifest/values/Type.php', - 'PHPUnit\\PharIo\\Manifest\\Url' => '/phar-io-manifest/values/Url.php', - 'PHPUnit\\PharIo\\Version\\AbstractVersionConstraint' => '/phar-io-version/constraints/AbstractVersionConstraint.php', - 'PHPUnit\\PharIo\\Version\\AndVersionConstraintGroup' => '/phar-io-version/constraints/AndVersionConstraintGroup.php', - 'PHPUnit\\PharIo\\Version\\AnyVersionConstraint' => '/phar-io-version/constraints/AnyVersionConstraint.php', - 'PHPUnit\\PharIo\\Version\\BuildMetaData' => '/phar-io-version/BuildMetaData.php', - 'PHPUnit\\PharIo\\Version\\ExactVersionConstraint' => '/phar-io-version/constraints/ExactVersionConstraint.php', - 'PHPUnit\\PharIo\\Version\\Exception' => '/phar-io-version/exceptions/Exception.php', - 'PHPUnit\\PharIo\\Version\\GreaterThanOrEqualToVersionConstraint' => '/phar-io-version/constraints/GreaterThanOrEqualToVersionConstraint.php', - 'PHPUnit\\PharIo\\Version\\InvalidPreReleaseSuffixException' => '/phar-io-version/exceptions/InvalidPreReleaseSuffixException.php', - 'PHPUnit\\PharIo\\Version\\InvalidVersionException' => '/phar-io-version/exceptions/InvalidVersionException.php', - 'PHPUnit\\PharIo\\Version\\NoBuildMetaDataException' => '/phar-io-version/exceptions/NoBuildMetaDataException.php', - 'PHPUnit\\PharIo\\Version\\NoPreReleaseSuffixException' => '/phar-io-version/exceptions/NoPreReleaseSuffixException.php', - 'PHPUnit\\PharIo\\Version\\OrVersionConstraintGroup' => '/phar-io-version/constraints/OrVersionConstraintGroup.php', - 'PHPUnit\\PharIo\\Version\\PreReleaseSuffix' => '/phar-io-version/PreReleaseSuffix.php', - 'PHPUnit\\PharIo\\Version\\SpecificMajorAndMinorVersionConstraint' => '/phar-io-version/constraints/SpecificMajorAndMinorVersionConstraint.php', - 'PHPUnit\\PharIo\\Version\\SpecificMajorVersionConstraint' => '/phar-io-version/constraints/SpecificMajorVersionConstraint.php', - 'PHPUnit\\PharIo\\Version\\UnsupportedVersionConstraintException' => '/phar-io-version/exceptions/UnsupportedVersionConstraintException.php', - 'PHPUnit\\PharIo\\Version\\Version' => '/phar-io-version/Version.php', - 'PHPUnit\\PharIo\\Version\\VersionConstraint' => '/phar-io-version/constraints/VersionConstraint.php', - 'PHPUnit\\PharIo\\Version\\VersionConstraintParser' => '/phar-io-version/VersionConstraintParser.php', - 'PHPUnit\\PharIo\\Version\\VersionConstraintValue' => '/phar-io-version/VersionConstraintValue.php', - 'PHPUnit\\PharIo\\Version\\VersionNumber' => '/phar-io-version/VersionNumber.php', - 'PHPUnit\\PhpParser\\Builder' => '/nikic-php-parser/PhpParser/Builder.php', - 'PHPUnit\\PhpParser\\BuilderFactory' => '/nikic-php-parser/PhpParser/BuilderFactory.php', - 'PHPUnit\\PhpParser\\BuilderHelpers' => '/nikic-php-parser/PhpParser/BuilderHelpers.php', - 'PHPUnit\\PhpParser\\Builder\\ClassConst' => '/nikic-php-parser/PhpParser/Builder/ClassConst.php', - 'PHPUnit\\PhpParser\\Builder\\Class_' => '/nikic-php-parser/PhpParser/Builder/Class_.php', - 'PHPUnit\\PhpParser\\Builder\\Declaration' => '/nikic-php-parser/PhpParser/Builder/Declaration.php', - 'PHPUnit\\PhpParser\\Builder\\EnumCase' => '/nikic-php-parser/PhpParser/Builder/EnumCase.php', - 'PHPUnit\\PhpParser\\Builder\\Enum_' => '/nikic-php-parser/PhpParser/Builder/Enum_.php', - 'PHPUnit\\PhpParser\\Builder\\FunctionLike' => '/nikic-php-parser/PhpParser/Builder/FunctionLike.php', - 'PHPUnit\\PhpParser\\Builder\\Function_' => '/nikic-php-parser/PhpParser/Builder/Function_.php', - 'PHPUnit\\PhpParser\\Builder\\Interface_' => '/nikic-php-parser/PhpParser/Builder/Interface_.php', - 'PHPUnit\\PhpParser\\Builder\\Method' => '/nikic-php-parser/PhpParser/Builder/Method.php', - 'PHPUnit\\PhpParser\\Builder\\Namespace_' => '/nikic-php-parser/PhpParser/Builder/Namespace_.php', - 'PHPUnit\\PhpParser\\Builder\\Param' => '/nikic-php-parser/PhpParser/Builder/Param.php', - 'PHPUnit\\PhpParser\\Builder\\Property' => '/nikic-php-parser/PhpParser/Builder/Property.php', - 'PHPUnit\\PhpParser\\Builder\\TraitUse' => '/nikic-php-parser/PhpParser/Builder/TraitUse.php', - 'PHPUnit\\PhpParser\\Builder\\TraitUseAdaptation' => '/nikic-php-parser/PhpParser/Builder/TraitUseAdaptation.php', - 'PHPUnit\\PhpParser\\Builder\\Trait_' => '/nikic-php-parser/PhpParser/Builder/Trait_.php', - 'PHPUnit\\PhpParser\\Builder\\Use_' => '/nikic-php-parser/PhpParser/Builder/Use_.php', - 'PHPUnit\\PhpParser\\Comment' => '/nikic-php-parser/PhpParser/Comment.php', - 'PHPUnit\\PhpParser\\Comment\\Doc' => '/nikic-php-parser/PhpParser/Comment/Doc.php', - 'PHPUnit\\PhpParser\\ConstExprEvaluationException' => '/nikic-php-parser/PhpParser/ConstExprEvaluationException.php', - 'PHPUnit\\PhpParser\\ConstExprEvaluator' => '/nikic-php-parser/PhpParser/ConstExprEvaluator.php', - 'PHPUnit\\PhpParser\\Error' => '/nikic-php-parser/PhpParser/Error.php', - 'PHPUnit\\PhpParser\\ErrorHandler' => '/nikic-php-parser/PhpParser/ErrorHandler.php', - 'PHPUnit\\PhpParser\\ErrorHandler\\Collecting' => '/nikic-php-parser/PhpParser/ErrorHandler/Collecting.php', - 'PHPUnit\\PhpParser\\ErrorHandler\\Throwing' => '/nikic-php-parser/PhpParser/ErrorHandler/Throwing.php', - 'PHPUnit\\PhpParser\\Internal\\DiffElem' => '/nikic-php-parser/PhpParser/Internal/DiffElem.php', - 'PHPUnit\\PhpParser\\Internal\\Differ' => '/nikic-php-parser/PhpParser/Internal/Differ.php', - 'PHPUnit\\PhpParser\\Internal\\PrintableNewAnonClassNode' => '/nikic-php-parser/PhpParser/Internal/PrintableNewAnonClassNode.php', - 'PHPUnit\\PhpParser\\Internal\\TokenStream' => '/nikic-php-parser/PhpParser/Internal/TokenStream.php', - 'PHPUnit\\PhpParser\\JsonDecoder' => '/nikic-php-parser/PhpParser/JsonDecoder.php', - 'PHPUnit\\PhpParser\\Lexer' => '/nikic-php-parser/PhpParser/Lexer.php', - 'PHPUnit\\PhpParser\\Lexer\\Emulative' => '/nikic-php-parser/PhpParser/Lexer/Emulative.php', - 'PHPUnit\\PhpParser\\Lexer\\TokenEmulator\\AttributeEmulator' => '/nikic-php-parser/PhpParser/Lexer/TokenEmulator/AttributeEmulator.php', - 'PHPUnit\\PhpParser\\Lexer\\TokenEmulator\\CoaleseEqualTokenEmulator' => '/nikic-php-parser/PhpParser/Lexer/TokenEmulator/CoaleseEqualTokenEmulator.php', - 'PHPUnit\\PhpParser\\Lexer\\TokenEmulator\\EnumTokenEmulator' => '/nikic-php-parser/PhpParser/Lexer/TokenEmulator/EnumTokenEmulator.php', - 'PHPUnit\\PhpParser\\Lexer\\TokenEmulator\\ExplicitOctalEmulator' => '/nikic-php-parser/PhpParser/Lexer/TokenEmulator/ExplicitOctalEmulator.php', - 'PHPUnit\\PhpParser\\Lexer\\TokenEmulator\\FlexibleDocStringEmulator' => '/nikic-php-parser/PhpParser/Lexer/TokenEmulator/FlexibleDocStringEmulator.php', - 'PHPUnit\\PhpParser\\Lexer\\TokenEmulator\\FnTokenEmulator' => '/nikic-php-parser/PhpParser/Lexer/TokenEmulator/FnTokenEmulator.php', - 'PHPUnit\\PhpParser\\Lexer\\TokenEmulator\\KeywordEmulator' => '/nikic-php-parser/PhpParser/Lexer/TokenEmulator/KeywordEmulator.php', - 'PHPUnit\\PhpParser\\Lexer\\TokenEmulator\\MatchTokenEmulator' => '/nikic-php-parser/PhpParser/Lexer/TokenEmulator/MatchTokenEmulator.php', - 'PHPUnit\\PhpParser\\Lexer\\TokenEmulator\\NullsafeTokenEmulator' => '/nikic-php-parser/PhpParser/Lexer/TokenEmulator/NullsafeTokenEmulator.php', - 'PHPUnit\\PhpParser\\Lexer\\TokenEmulator\\NumericLiteralSeparatorEmulator' => '/nikic-php-parser/PhpParser/Lexer/TokenEmulator/NumericLiteralSeparatorEmulator.php', - 'PHPUnit\\PhpParser\\Lexer\\TokenEmulator\\ReadonlyFunctionTokenEmulator' => '/nikic-php-parser/PhpParser/Lexer/TokenEmulator/ReadonlyFunctionTokenEmulator.php', - 'PHPUnit\\PhpParser\\Lexer\\TokenEmulator\\ReadonlyTokenEmulator' => '/nikic-php-parser/PhpParser/Lexer/TokenEmulator/ReadonlyTokenEmulator.php', - 'PHPUnit\\PhpParser\\Lexer\\TokenEmulator\\ReverseEmulator' => '/nikic-php-parser/PhpParser/Lexer/TokenEmulator/ReverseEmulator.php', - 'PHPUnit\\PhpParser\\Lexer\\TokenEmulator\\TokenEmulator' => '/nikic-php-parser/PhpParser/Lexer/TokenEmulator/TokenEmulator.php', - 'PHPUnit\\PhpParser\\NameContext' => '/nikic-php-parser/PhpParser/NameContext.php', - 'PHPUnit\\PhpParser\\Node' => '/nikic-php-parser/PhpParser/Node.php', - 'PHPUnit\\PhpParser\\NodeAbstract' => '/nikic-php-parser/PhpParser/NodeAbstract.php', - 'PHPUnit\\PhpParser\\NodeDumper' => '/nikic-php-parser/PhpParser/NodeDumper.php', - 'PHPUnit\\PhpParser\\NodeFinder' => '/nikic-php-parser/PhpParser/NodeFinder.php', - 'PHPUnit\\PhpParser\\NodeTraverser' => '/nikic-php-parser/PhpParser/NodeTraverser.php', - 'PHPUnit\\PhpParser\\NodeTraverserInterface' => '/nikic-php-parser/PhpParser/NodeTraverserInterface.php', - 'PHPUnit\\PhpParser\\NodeVisitor' => '/nikic-php-parser/PhpParser/NodeVisitor.php', - 'PHPUnit\\PhpParser\\NodeVisitorAbstract' => '/nikic-php-parser/PhpParser/NodeVisitorAbstract.php', - 'PHPUnit\\PhpParser\\NodeVisitor\\CloningVisitor' => '/nikic-php-parser/PhpParser/NodeVisitor/CloningVisitor.php', - 'PHPUnit\\PhpParser\\NodeVisitor\\FindingVisitor' => '/nikic-php-parser/PhpParser/NodeVisitor/FindingVisitor.php', - 'PHPUnit\\PhpParser\\NodeVisitor\\FirstFindingVisitor' => '/nikic-php-parser/PhpParser/NodeVisitor/FirstFindingVisitor.php', - 'PHPUnit\\PhpParser\\NodeVisitor\\NameResolver' => '/nikic-php-parser/PhpParser/NodeVisitor/NameResolver.php', - 'PHPUnit\\PhpParser\\NodeVisitor\\NodeConnectingVisitor' => '/nikic-php-parser/PhpParser/NodeVisitor/NodeConnectingVisitor.php', - 'PHPUnit\\PhpParser\\NodeVisitor\\ParentConnectingVisitor' => '/nikic-php-parser/PhpParser/NodeVisitor/ParentConnectingVisitor.php', - 'PHPUnit\\PhpParser\\Node\\Arg' => '/nikic-php-parser/PhpParser/Node/Arg.php', - 'PHPUnit\\PhpParser\\Node\\Attribute' => '/nikic-php-parser/PhpParser/Node/Attribute.php', - 'PHPUnit\\PhpParser\\Node\\AttributeGroup' => '/nikic-php-parser/PhpParser/Node/AttributeGroup.php', - 'PHPUnit\\PhpParser\\Node\\ComplexType' => '/nikic-php-parser/PhpParser/Node/ComplexType.php', - 'PHPUnit\\PhpParser\\Node\\Const_' => '/nikic-php-parser/PhpParser/Node/Const_.php', - 'PHPUnit\\PhpParser\\Node\\Expr' => '/nikic-php-parser/PhpParser/Node/Expr.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\ArrayDimFetch' => '/nikic-php-parser/PhpParser/Node/Expr/ArrayDimFetch.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\ArrayItem' => '/nikic-php-parser/PhpParser/Node/Expr/ArrayItem.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\Array_' => '/nikic-php-parser/PhpParser/Node/Expr/Array_.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\ArrowFunction' => '/nikic-php-parser/PhpParser/Node/Expr/ArrowFunction.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\Assign' => '/nikic-php-parser/PhpParser/Node/Expr/Assign.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\AssignOp' => '/nikic-php-parser/PhpParser/Node/Expr/AssignOp.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\AssignOp\\BitwiseAnd' => '/nikic-php-parser/PhpParser/Node/Expr/AssignOp/BitwiseAnd.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\AssignOp\\BitwiseOr' => '/nikic-php-parser/PhpParser/Node/Expr/AssignOp/BitwiseOr.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\AssignOp\\BitwiseXor' => '/nikic-php-parser/PhpParser/Node/Expr/AssignOp/BitwiseXor.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\AssignOp\\Coalesce' => '/nikic-php-parser/PhpParser/Node/Expr/AssignOp/Coalesce.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\AssignOp\\Concat' => '/nikic-php-parser/PhpParser/Node/Expr/AssignOp/Concat.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\AssignOp\\Div' => '/nikic-php-parser/PhpParser/Node/Expr/AssignOp/Div.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\AssignOp\\Minus' => '/nikic-php-parser/PhpParser/Node/Expr/AssignOp/Minus.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\AssignOp\\Mod' => '/nikic-php-parser/PhpParser/Node/Expr/AssignOp/Mod.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\AssignOp\\Mul' => '/nikic-php-parser/PhpParser/Node/Expr/AssignOp/Mul.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\AssignOp\\Plus' => '/nikic-php-parser/PhpParser/Node/Expr/AssignOp/Plus.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\AssignOp\\Pow' => '/nikic-php-parser/PhpParser/Node/Expr/AssignOp/Pow.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\AssignOp\\ShiftLeft' => '/nikic-php-parser/PhpParser/Node/Expr/AssignOp/ShiftLeft.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\AssignOp\\ShiftRight' => '/nikic-php-parser/PhpParser/Node/Expr/AssignOp/ShiftRight.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\AssignRef' => '/nikic-php-parser/PhpParser/Node/Expr/AssignRef.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\BinaryOp' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\BinaryOp\\BitwiseAnd' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/BitwiseAnd.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\BinaryOp\\BitwiseOr' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/BitwiseOr.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\BinaryOp\\BitwiseXor' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/BitwiseXor.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\BinaryOp\\BooleanAnd' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/BooleanAnd.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\BinaryOp\\BooleanOr' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/BooleanOr.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\BinaryOp\\Coalesce' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Coalesce.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\BinaryOp\\Concat' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Concat.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\BinaryOp\\Div' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Div.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\BinaryOp\\Equal' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Equal.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\BinaryOp\\Greater' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Greater.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\BinaryOp\\GreaterOrEqual' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/GreaterOrEqual.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\BinaryOp\\Identical' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Identical.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\BinaryOp\\LogicalAnd' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/LogicalAnd.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\BinaryOp\\LogicalOr' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/LogicalOr.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\BinaryOp\\LogicalXor' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/LogicalXor.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\BinaryOp\\Minus' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Minus.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\BinaryOp\\Mod' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Mod.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\BinaryOp\\Mul' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Mul.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\BinaryOp\\NotEqual' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/NotEqual.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\BinaryOp\\NotIdentical' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/NotIdentical.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\BinaryOp\\Plus' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Plus.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\BinaryOp\\Pow' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Pow.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\BinaryOp\\ShiftLeft' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/ShiftLeft.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\BinaryOp\\ShiftRight' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/ShiftRight.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\BinaryOp\\Smaller' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Smaller.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\BinaryOp\\SmallerOrEqual' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/SmallerOrEqual.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\BinaryOp\\Spaceship' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Spaceship.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\BitwiseNot' => '/nikic-php-parser/PhpParser/Node/Expr/BitwiseNot.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\BooleanNot' => '/nikic-php-parser/PhpParser/Node/Expr/BooleanNot.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\CallLike' => '/nikic-php-parser/PhpParser/Node/Expr/CallLike.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\Cast' => '/nikic-php-parser/PhpParser/Node/Expr/Cast.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\Cast\\Array_' => '/nikic-php-parser/PhpParser/Node/Expr/Cast/Array_.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\Cast\\Bool_' => '/nikic-php-parser/PhpParser/Node/Expr/Cast/Bool_.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\Cast\\Double' => '/nikic-php-parser/PhpParser/Node/Expr/Cast/Double.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\Cast\\Int_' => '/nikic-php-parser/PhpParser/Node/Expr/Cast/Int_.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\Cast\\Object_' => '/nikic-php-parser/PhpParser/Node/Expr/Cast/Object_.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\Cast\\String_' => '/nikic-php-parser/PhpParser/Node/Expr/Cast/String_.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\Cast\\Unset_' => '/nikic-php-parser/PhpParser/Node/Expr/Cast/Unset_.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\ClassConstFetch' => '/nikic-php-parser/PhpParser/Node/Expr/ClassConstFetch.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\Clone_' => '/nikic-php-parser/PhpParser/Node/Expr/Clone_.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\Closure' => '/nikic-php-parser/PhpParser/Node/Expr/Closure.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\ClosureUse' => '/nikic-php-parser/PhpParser/Node/Expr/ClosureUse.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\ConstFetch' => '/nikic-php-parser/PhpParser/Node/Expr/ConstFetch.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\Empty_' => '/nikic-php-parser/PhpParser/Node/Expr/Empty_.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\Error' => '/nikic-php-parser/PhpParser/Node/Expr/Error.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\ErrorSuppress' => '/nikic-php-parser/PhpParser/Node/Expr/ErrorSuppress.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\Eval_' => '/nikic-php-parser/PhpParser/Node/Expr/Eval_.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\Exit_' => '/nikic-php-parser/PhpParser/Node/Expr/Exit_.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\FuncCall' => '/nikic-php-parser/PhpParser/Node/Expr/FuncCall.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\Include_' => '/nikic-php-parser/PhpParser/Node/Expr/Include_.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\Instanceof_' => '/nikic-php-parser/PhpParser/Node/Expr/Instanceof_.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\Isset_' => '/nikic-php-parser/PhpParser/Node/Expr/Isset_.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\List_' => '/nikic-php-parser/PhpParser/Node/Expr/List_.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\Match_' => '/nikic-php-parser/PhpParser/Node/Expr/Match_.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\MethodCall' => '/nikic-php-parser/PhpParser/Node/Expr/MethodCall.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\New_' => '/nikic-php-parser/PhpParser/Node/Expr/New_.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\NullsafeMethodCall' => '/nikic-php-parser/PhpParser/Node/Expr/NullsafeMethodCall.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\NullsafePropertyFetch' => '/nikic-php-parser/PhpParser/Node/Expr/NullsafePropertyFetch.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\PostDec' => '/nikic-php-parser/PhpParser/Node/Expr/PostDec.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\PostInc' => '/nikic-php-parser/PhpParser/Node/Expr/PostInc.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\PreDec' => '/nikic-php-parser/PhpParser/Node/Expr/PreDec.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\PreInc' => '/nikic-php-parser/PhpParser/Node/Expr/PreInc.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\Print_' => '/nikic-php-parser/PhpParser/Node/Expr/Print_.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\PropertyFetch' => '/nikic-php-parser/PhpParser/Node/Expr/PropertyFetch.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\ShellExec' => '/nikic-php-parser/PhpParser/Node/Expr/ShellExec.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\StaticCall' => '/nikic-php-parser/PhpParser/Node/Expr/StaticCall.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\StaticPropertyFetch' => '/nikic-php-parser/PhpParser/Node/Expr/StaticPropertyFetch.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\Ternary' => '/nikic-php-parser/PhpParser/Node/Expr/Ternary.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\Throw_' => '/nikic-php-parser/PhpParser/Node/Expr/Throw_.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\UnaryMinus' => '/nikic-php-parser/PhpParser/Node/Expr/UnaryMinus.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\UnaryPlus' => '/nikic-php-parser/PhpParser/Node/Expr/UnaryPlus.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\Variable' => '/nikic-php-parser/PhpParser/Node/Expr/Variable.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\YieldFrom' => '/nikic-php-parser/PhpParser/Node/Expr/YieldFrom.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\Yield_' => '/nikic-php-parser/PhpParser/Node/Expr/Yield_.php', - 'PHPUnit\\PhpParser\\Node\\FunctionLike' => '/nikic-php-parser/PhpParser/Node/FunctionLike.php', - 'PHPUnit\\PhpParser\\Node\\Identifier' => '/nikic-php-parser/PhpParser/Node/Identifier.php', - 'PHPUnit\\PhpParser\\Node\\IntersectionType' => '/nikic-php-parser/PhpParser/Node/IntersectionType.php', - 'PHPUnit\\PhpParser\\Node\\MatchArm' => '/nikic-php-parser/PhpParser/Node/MatchArm.php', - 'PHPUnit\\PhpParser\\Node\\Name' => '/nikic-php-parser/PhpParser/Node/Name.php', - 'PHPUnit\\PhpParser\\Node\\Name\\FullyQualified' => '/nikic-php-parser/PhpParser/Node/Name/FullyQualified.php', - 'PHPUnit\\PhpParser\\Node\\Name\\Relative' => '/nikic-php-parser/PhpParser/Node/Name/Relative.php', - 'PHPUnit\\PhpParser\\Node\\NullableType' => '/nikic-php-parser/PhpParser/Node/NullableType.php', - 'PHPUnit\\PhpParser\\Node\\Param' => '/nikic-php-parser/PhpParser/Node/Param.php', - 'PHPUnit\\PhpParser\\Node\\Scalar' => '/nikic-php-parser/PhpParser/Node/Scalar.php', - 'PHPUnit\\PhpParser\\Node\\Scalar\\DNumber' => '/nikic-php-parser/PhpParser/Node/Scalar/DNumber.php', - 'PHPUnit\\PhpParser\\Node\\Scalar\\Encapsed' => '/nikic-php-parser/PhpParser/Node/Scalar/Encapsed.php', - 'PHPUnit\\PhpParser\\Node\\Scalar\\EncapsedStringPart' => '/nikic-php-parser/PhpParser/Node/Scalar/EncapsedStringPart.php', - 'PHPUnit\\PhpParser\\Node\\Scalar\\LNumber' => '/nikic-php-parser/PhpParser/Node/Scalar/LNumber.php', - 'PHPUnit\\PhpParser\\Node\\Scalar\\MagicConst' => '/nikic-php-parser/PhpParser/Node/Scalar/MagicConst.php', - 'PHPUnit\\PhpParser\\Node\\Scalar\\MagicConst\\Class_' => '/nikic-php-parser/PhpParser/Node/Scalar/MagicConst/Class_.php', - 'PHPUnit\\PhpParser\\Node\\Scalar\\MagicConst\\Dir' => '/nikic-php-parser/PhpParser/Node/Scalar/MagicConst/Dir.php', - 'PHPUnit\\PhpParser\\Node\\Scalar\\MagicConst\\File' => '/nikic-php-parser/PhpParser/Node/Scalar/MagicConst/File.php', - 'PHPUnit\\PhpParser\\Node\\Scalar\\MagicConst\\Function_' => '/nikic-php-parser/PhpParser/Node/Scalar/MagicConst/Function_.php', - 'PHPUnit\\PhpParser\\Node\\Scalar\\MagicConst\\Line' => '/nikic-php-parser/PhpParser/Node/Scalar/MagicConst/Line.php', - 'PHPUnit\\PhpParser\\Node\\Scalar\\MagicConst\\Method' => '/nikic-php-parser/PhpParser/Node/Scalar/MagicConst/Method.php', - 'PHPUnit\\PhpParser\\Node\\Scalar\\MagicConst\\Namespace_' => '/nikic-php-parser/PhpParser/Node/Scalar/MagicConst/Namespace_.php', - 'PHPUnit\\PhpParser\\Node\\Scalar\\MagicConst\\Trait_' => '/nikic-php-parser/PhpParser/Node/Scalar/MagicConst/Trait_.php', - 'PHPUnit\\PhpParser\\Node\\Scalar\\String_' => '/nikic-php-parser/PhpParser/Node/Scalar/String_.php', - 'PHPUnit\\PhpParser\\Node\\Stmt' => '/nikic-php-parser/PhpParser/Node/Stmt.php', - 'PHPUnit\\PhpParser\\Node\\Stmt\\Break_' => '/nikic-php-parser/PhpParser/Node/Stmt/Break_.php', - 'PHPUnit\\PhpParser\\Node\\Stmt\\Case_' => '/nikic-php-parser/PhpParser/Node/Stmt/Case_.php', - 'PHPUnit\\PhpParser\\Node\\Stmt\\Catch_' => '/nikic-php-parser/PhpParser/Node/Stmt/Catch_.php', - 'PHPUnit\\PhpParser\\Node\\Stmt\\ClassConst' => '/nikic-php-parser/PhpParser/Node/Stmt/ClassConst.php', - 'PHPUnit\\PhpParser\\Node\\Stmt\\ClassLike' => '/nikic-php-parser/PhpParser/Node/Stmt/ClassLike.php', - 'PHPUnit\\PhpParser\\Node\\Stmt\\ClassMethod' => '/nikic-php-parser/PhpParser/Node/Stmt/ClassMethod.php', - 'PHPUnit\\PhpParser\\Node\\Stmt\\Class_' => '/nikic-php-parser/PhpParser/Node/Stmt/Class_.php', - 'PHPUnit\\PhpParser\\Node\\Stmt\\Const_' => '/nikic-php-parser/PhpParser/Node/Stmt/Const_.php', - 'PHPUnit\\PhpParser\\Node\\Stmt\\Continue_' => '/nikic-php-parser/PhpParser/Node/Stmt/Continue_.php', - 'PHPUnit\\PhpParser\\Node\\Stmt\\DeclareDeclare' => '/nikic-php-parser/PhpParser/Node/Stmt/DeclareDeclare.php', - 'PHPUnit\\PhpParser\\Node\\Stmt\\Declare_' => '/nikic-php-parser/PhpParser/Node/Stmt/Declare_.php', - 'PHPUnit\\PhpParser\\Node\\Stmt\\Do_' => '/nikic-php-parser/PhpParser/Node/Stmt/Do_.php', - 'PHPUnit\\PhpParser\\Node\\Stmt\\Echo_' => '/nikic-php-parser/PhpParser/Node/Stmt/Echo_.php', - 'PHPUnit\\PhpParser\\Node\\Stmt\\ElseIf_' => '/nikic-php-parser/PhpParser/Node/Stmt/ElseIf_.php', - 'PHPUnit\\PhpParser\\Node\\Stmt\\Else_' => '/nikic-php-parser/PhpParser/Node/Stmt/Else_.php', - 'PHPUnit\\PhpParser\\Node\\Stmt\\EnumCase' => '/nikic-php-parser/PhpParser/Node/Stmt/EnumCase.php', - 'PHPUnit\\PhpParser\\Node\\Stmt\\Enum_' => '/nikic-php-parser/PhpParser/Node/Stmt/Enum_.php', - 'PHPUnit\\PhpParser\\Node\\Stmt\\Expression' => '/nikic-php-parser/PhpParser/Node/Stmt/Expression.php', - 'PHPUnit\\PhpParser\\Node\\Stmt\\Finally_' => '/nikic-php-parser/PhpParser/Node/Stmt/Finally_.php', - 'PHPUnit\\PhpParser\\Node\\Stmt\\For_' => '/nikic-php-parser/PhpParser/Node/Stmt/For_.php', - 'PHPUnit\\PhpParser\\Node\\Stmt\\Foreach_' => '/nikic-php-parser/PhpParser/Node/Stmt/Foreach_.php', - 'PHPUnit\\PhpParser\\Node\\Stmt\\Function_' => '/nikic-php-parser/PhpParser/Node/Stmt/Function_.php', - 'PHPUnit\\PhpParser\\Node\\Stmt\\Global_' => '/nikic-php-parser/PhpParser/Node/Stmt/Global_.php', - 'PHPUnit\\PhpParser\\Node\\Stmt\\Goto_' => '/nikic-php-parser/PhpParser/Node/Stmt/Goto_.php', - 'PHPUnit\\PhpParser\\Node\\Stmt\\GroupUse' => '/nikic-php-parser/PhpParser/Node/Stmt/GroupUse.php', - 'PHPUnit\\PhpParser\\Node\\Stmt\\HaltCompiler' => '/nikic-php-parser/PhpParser/Node/Stmt/HaltCompiler.php', - 'PHPUnit\\PhpParser\\Node\\Stmt\\If_' => '/nikic-php-parser/PhpParser/Node/Stmt/If_.php', - 'PHPUnit\\PhpParser\\Node\\Stmt\\InlineHTML' => '/nikic-php-parser/PhpParser/Node/Stmt/InlineHTML.php', - 'PHPUnit\\PhpParser\\Node\\Stmt\\Interface_' => '/nikic-php-parser/PhpParser/Node/Stmt/Interface_.php', - 'PHPUnit\\PhpParser\\Node\\Stmt\\Label' => '/nikic-php-parser/PhpParser/Node/Stmt/Label.php', - 'PHPUnit\\PhpParser\\Node\\Stmt\\Namespace_' => '/nikic-php-parser/PhpParser/Node/Stmt/Namespace_.php', - 'PHPUnit\\PhpParser\\Node\\Stmt\\Nop' => '/nikic-php-parser/PhpParser/Node/Stmt/Nop.php', - 'PHPUnit\\PhpParser\\Node\\Stmt\\Property' => '/nikic-php-parser/PhpParser/Node/Stmt/Property.php', - 'PHPUnit\\PhpParser\\Node\\Stmt\\PropertyProperty' => '/nikic-php-parser/PhpParser/Node/Stmt/PropertyProperty.php', - 'PHPUnit\\PhpParser\\Node\\Stmt\\Return_' => '/nikic-php-parser/PhpParser/Node/Stmt/Return_.php', - 'PHPUnit\\PhpParser\\Node\\Stmt\\StaticVar' => '/nikic-php-parser/PhpParser/Node/Stmt/StaticVar.php', - 'PHPUnit\\PhpParser\\Node\\Stmt\\Static_' => '/nikic-php-parser/PhpParser/Node/Stmt/Static_.php', - 'PHPUnit\\PhpParser\\Node\\Stmt\\Switch_' => '/nikic-php-parser/PhpParser/Node/Stmt/Switch_.php', - 'PHPUnit\\PhpParser\\Node\\Stmt\\Throw_' => '/nikic-php-parser/PhpParser/Node/Stmt/Throw_.php', - 'PHPUnit\\PhpParser\\Node\\Stmt\\TraitUse' => '/nikic-php-parser/PhpParser/Node/Stmt/TraitUse.php', - 'PHPUnit\\PhpParser\\Node\\Stmt\\TraitUseAdaptation' => '/nikic-php-parser/PhpParser/Node/Stmt/TraitUseAdaptation.php', - 'PHPUnit\\PhpParser\\Node\\Stmt\\TraitUseAdaptation\\Alias' => '/nikic-php-parser/PhpParser/Node/Stmt/TraitUseAdaptation/Alias.php', - 'PHPUnit\\PhpParser\\Node\\Stmt\\TraitUseAdaptation\\Precedence' => '/nikic-php-parser/PhpParser/Node/Stmt/TraitUseAdaptation/Precedence.php', - 'PHPUnit\\PhpParser\\Node\\Stmt\\Trait_' => '/nikic-php-parser/PhpParser/Node/Stmt/Trait_.php', - 'PHPUnit\\PhpParser\\Node\\Stmt\\TryCatch' => '/nikic-php-parser/PhpParser/Node/Stmt/TryCatch.php', - 'PHPUnit\\PhpParser\\Node\\Stmt\\Unset_' => '/nikic-php-parser/PhpParser/Node/Stmt/Unset_.php', - 'PHPUnit\\PhpParser\\Node\\Stmt\\UseUse' => '/nikic-php-parser/PhpParser/Node/Stmt/UseUse.php', - 'PHPUnit\\PhpParser\\Node\\Stmt\\Use_' => '/nikic-php-parser/PhpParser/Node/Stmt/Use_.php', - 'PHPUnit\\PhpParser\\Node\\Stmt\\While_' => '/nikic-php-parser/PhpParser/Node/Stmt/While_.php', - 'PHPUnit\\PhpParser\\Node\\UnionType' => '/nikic-php-parser/PhpParser/Node/UnionType.php', - 'PHPUnit\\PhpParser\\Node\\VarLikeIdentifier' => '/nikic-php-parser/PhpParser/Node/VarLikeIdentifier.php', - 'PHPUnit\\PhpParser\\Node\\VariadicPlaceholder' => '/nikic-php-parser/PhpParser/Node/VariadicPlaceholder.php', - 'PHPUnit\\PhpParser\\Parser' => '/nikic-php-parser/PhpParser/Parser.php', - 'PHPUnit\\PhpParser\\ParserAbstract' => '/nikic-php-parser/PhpParser/ParserAbstract.php', - 'PHPUnit\\PhpParser\\ParserFactory' => '/nikic-php-parser/PhpParser/ParserFactory.php', - 'PHPUnit\\PhpParser\\Parser\\Multiple' => '/nikic-php-parser/PhpParser/Parser/Multiple.php', - 'PHPUnit\\PhpParser\\Parser\\Php5' => '/nikic-php-parser/PhpParser/Parser/Php5.php', - 'PHPUnit\\PhpParser\\Parser\\Php7' => '/nikic-php-parser/PhpParser/Parser/Php7.php', - 'PHPUnit\\PhpParser\\Parser\\Tokens' => '/nikic-php-parser/PhpParser/Parser/Tokens.php', - 'PHPUnit\\PhpParser\\PrettyPrinterAbstract' => '/nikic-php-parser/PhpParser/PrettyPrinterAbstract.php', - 'PHPUnit\\PhpParser\\PrettyPrinter\\Standard' => '/nikic-php-parser/PhpParser/PrettyPrinter/Standard.php', 'PHPUnit\\Runner\\AfterIncompleteTestHook' => '/phpunit/Runner/Hook/AfterIncompleteTestHook.php', 'PHPUnit\\Runner\\AfterLastTestHook' => '/phpunit/Runner/Hook/AfterLastTestHook.php', 'PHPUnit\\Runner\\AfterRiskyTestHook' => '/phpunit/Runner/Hook/AfterRiskyTestHook.php', @@ -645,206 +1051,6 @@ spl_autoload_register( 'PHPUnit\\Runner\\TestSuiteLoader' => '/phpunit/Runner/TestSuiteLoader.php', 'PHPUnit\\Runner\\TestSuiteSorter' => '/phpunit/Runner/TestSuiteSorter.php', 'PHPUnit\\Runner\\Version' => '/phpunit/Runner/Version.php', - 'PHPUnit\\SebastianBergmann\\CliParser\\AmbiguousOptionException' => '/sebastian-cli-parser/exceptions/AmbiguousOptionException.php', - 'PHPUnit\\SebastianBergmann\\CliParser\\Exception' => '/sebastian-cli-parser/exceptions/Exception.php', - 'PHPUnit\\SebastianBergmann\\CliParser\\OptionDoesNotAllowArgumentException' => '/sebastian-cli-parser/exceptions/OptionDoesNotAllowArgumentException.php', - 'PHPUnit\\SebastianBergmann\\CliParser\\Parser' => '/sebastian-cli-parser/Parser.php', - 'PHPUnit\\SebastianBergmann\\CliParser\\RequiredOptionArgumentMissingException' => '/sebastian-cli-parser/exceptions/RequiredOptionArgumentMissingException.php', - 'PHPUnit\\SebastianBergmann\\CliParser\\UnknownOptionException' => '/sebastian-cli-parser/exceptions/UnknownOptionException.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\BranchAndPathCoverageNotSupportedException' => '/php-code-coverage/Exception/BranchAndPathCoverageNotSupportedException.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\CodeCoverage' => '/php-code-coverage/CodeCoverage.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\DeadCodeDetectionNotSupportedException' => '/php-code-coverage/Exception/DeadCodeDetectionNotSupportedException.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Driver\\Driver' => '/php-code-coverage/Driver/Driver.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Driver\\PathExistsButIsNotDirectoryException' => '/php-code-coverage/Exception/PathExistsButIsNotDirectoryException.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Driver\\PcovDriver' => '/php-code-coverage/Driver/PcovDriver.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Driver\\PcovNotAvailableException' => '/php-code-coverage/Exception/PcovNotAvailableException.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Driver\\PhpdbgDriver' => '/php-code-coverage/Driver/PhpdbgDriver.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Driver\\PhpdbgNotAvailableException' => '/php-code-coverage/Exception/PhpdbgNotAvailableException.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Driver\\Selector' => '/php-code-coverage/Driver/Selector.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Driver\\WriteOperationFailedException' => '/php-code-coverage/Exception/WriteOperationFailedException.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Driver\\WrongXdebugVersionException' => '/php-code-coverage/Exception/WrongXdebugVersionException.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Driver\\Xdebug2Driver' => '/php-code-coverage/Driver/Xdebug2Driver.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Driver\\Xdebug2NotEnabledException' => '/php-code-coverage/Exception/Xdebug2NotEnabledException.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Driver\\Xdebug3Driver' => '/php-code-coverage/Driver/Xdebug3Driver.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Driver\\Xdebug3NotEnabledException' => '/php-code-coverage/Exception/Xdebug3NotEnabledException.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Driver\\XdebugNotAvailableException' => '/php-code-coverage/Exception/XdebugNotAvailableException.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Exception' => '/php-code-coverage/Exception/Exception.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Filter' => '/php-code-coverage/Filter.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\InvalidArgumentException' => '/php-code-coverage/Exception/InvalidArgumentException.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\NoCodeCoverageDriverAvailableException' => '/php-code-coverage/Exception/NoCodeCoverageDriverAvailableException.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\NoCodeCoverageDriverWithPathCoverageSupportAvailableException' => '/php-code-coverage/Exception/NoCodeCoverageDriverWithPathCoverageSupportAvailableException.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Node\\AbstractNode' => '/php-code-coverage/Node/AbstractNode.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Node\\Builder' => '/php-code-coverage/Node/Builder.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Node\\CrapIndex' => '/php-code-coverage/Node/CrapIndex.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Node\\Directory' => '/php-code-coverage/Node/Directory.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Node\\File' => '/php-code-coverage/Node/File.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Node\\Iterator' => '/php-code-coverage/Node/Iterator.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\ParserException' => '/php-code-coverage/Exception/ParserException.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\ProcessedCodeCoverageData' => '/php-code-coverage/ProcessedCodeCoverageData.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\RawCodeCoverageData' => '/php-code-coverage/RawCodeCoverageData.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\ReflectionException' => '/php-code-coverage/Exception/ReflectionException.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\ReportAlreadyFinalizedException' => '/php-code-coverage/Exception/ReportAlreadyFinalizedException.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Report\\Clover' => '/php-code-coverage/Report/Clover.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Report\\Cobertura' => '/php-code-coverage/Report/Cobertura.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Report\\Crap4j' => '/php-code-coverage/Report/Crap4j.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Report\\Html\\Dashboard' => '/php-code-coverage/Report/Html/Renderer/Dashboard.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Report\\Html\\Directory' => '/php-code-coverage/Report/Html/Renderer/Directory.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Report\\Html\\Facade' => '/php-code-coverage/Report/Html/Facade.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Report\\Html\\File' => '/php-code-coverage/Report/Html/Renderer/File.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Report\\Html\\Renderer' => '/php-code-coverage/Report/Html/Renderer.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Report\\PHP' => '/php-code-coverage/Report/PHP.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Report\\Text' => '/php-code-coverage/Report/Text.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Report\\Xml\\BuildInformation' => '/php-code-coverage/Report/Xml/BuildInformation.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Report\\Xml\\Coverage' => '/php-code-coverage/Report/Xml/Coverage.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Report\\Xml\\Directory' => '/php-code-coverage/Report/Xml/Directory.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Report\\Xml\\Facade' => '/php-code-coverage/Report/Xml/Facade.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Report\\Xml\\File' => '/php-code-coverage/Report/Xml/File.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Report\\Xml\\Method' => '/php-code-coverage/Report/Xml/Method.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Report\\Xml\\Node' => '/php-code-coverage/Report/Xml/Node.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Report\\Xml\\Project' => '/php-code-coverage/Report/Xml/Project.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Report\\Xml\\Report' => '/php-code-coverage/Report/Xml/Report.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Report\\Xml\\Source' => '/php-code-coverage/Report/Xml/Source.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Report\\Xml\\Tests' => '/php-code-coverage/Report/Xml/Tests.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Report\\Xml\\Totals' => '/php-code-coverage/Report/Xml/Totals.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Report\\Xml\\Unit' => '/php-code-coverage/Report/Xml/Unit.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\StaticAnalysisCacheNotConfiguredException' => '/php-code-coverage/Exception/StaticAnalysisCacheNotConfiguredException.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\StaticAnalysis\\CacheWarmer' => '/php-code-coverage/StaticAnalysis/CacheWarmer.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\StaticAnalysis\\CachingFileAnalyser' => '/php-code-coverage/StaticAnalysis/CachingFileAnalyser.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\StaticAnalysis\\CodeUnitFindingVisitor' => '/php-code-coverage/StaticAnalysis/CodeUnitFindingVisitor.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\StaticAnalysis\\ExecutableLinesFindingVisitor' => '/php-code-coverage/StaticAnalysis/ExecutableLinesFindingVisitor.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\StaticAnalysis\\FileAnalyser' => '/php-code-coverage/StaticAnalysis/FileAnalyser.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\StaticAnalysis\\IgnoredLinesFindingVisitor' => '/php-code-coverage/StaticAnalysis/IgnoredLinesFindingVisitor.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\StaticAnalysis\\ParsingFileAnalyser' => '/php-code-coverage/StaticAnalysis/ParsingFileAnalyser.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\TestIdMissingException' => '/php-code-coverage/Exception/TestIdMissingException.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\UnintentionallyCoveredCodeException' => '/php-code-coverage/Exception/UnintentionallyCoveredCodeException.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Util\\DirectoryCouldNotBeCreatedException' => '/php-code-coverage/Exception/DirectoryCouldNotBeCreatedException.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Util\\Filesystem' => '/php-code-coverage/Util/Filesystem.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Util\\Percentage' => '/php-code-coverage/Util/Percentage.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Version' => '/php-code-coverage/Version.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\XmlException' => '/php-code-coverage/Exception/XmlException.php', - 'PHPUnit\\SebastianBergmann\\CodeUnitReverseLookup\\Wizard' => '/sebastian-code-unit-reverse-lookup/Wizard.php', - 'PHPUnit\\SebastianBergmann\\CodeUnit\\ClassMethodUnit' => '/sebastian-code-unit/ClassMethodUnit.php', - 'PHPUnit\\SebastianBergmann\\CodeUnit\\ClassUnit' => '/sebastian-code-unit/ClassUnit.php', - 'PHPUnit\\SebastianBergmann\\CodeUnit\\CodeUnit' => '/sebastian-code-unit/CodeUnit.php', - 'PHPUnit\\SebastianBergmann\\CodeUnit\\CodeUnitCollection' => '/sebastian-code-unit/CodeUnitCollection.php', - 'PHPUnit\\SebastianBergmann\\CodeUnit\\CodeUnitCollectionIterator' => '/sebastian-code-unit/CodeUnitCollectionIterator.php', - 'PHPUnit\\SebastianBergmann\\CodeUnit\\Exception' => '/sebastian-code-unit/exceptions/Exception.php', - 'PHPUnit\\SebastianBergmann\\CodeUnit\\FunctionUnit' => '/sebastian-code-unit/FunctionUnit.php', - 'PHPUnit\\SebastianBergmann\\CodeUnit\\InterfaceMethodUnit' => '/sebastian-code-unit/InterfaceMethodUnit.php', - 'PHPUnit\\SebastianBergmann\\CodeUnit\\InterfaceUnit' => '/sebastian-code-unit/InterfaceUnit.php', - 'PHPUnit\\SebastianBergmann\\CodeUnit\\InvalidCodeUnitException' => '/sebastian-code-unit/exceptions/InvalidCodeUnitException.php', - 'PHPUnit\\SebastianBergmann\\CodeUnit\\Mapper' => '/sebastian-code-unit/Mapper.php', - 'PHPUnit\\SebastianBergmann\\CodeUnit\\NoTraitException' => '/sebastian-code-unit/exceptions/NoTraitException.php', - 'PHPUnit\\SebastianBergmann\\CodeUnit\\ReflectionException' => '/sebastian-code-unit/exceptions/ReflectionException.php', - 'PHPUnit\\SebastianBergmann\\CodeUnit\\TraitMethodUnit' => '/sebastian-code-unit/TraitMethodUnit.php', - 'PHPUnit\\SebastianBergmann\\CodeUnit\\TraitUnit' => '/sebastian-code-unit/TraitUnit.php', - 'PHPUnit\\SebastianBergmann\\Comparator\\ArrayComparator' => '/sebastian-comparator/ArrayComparator.php', - 'PHPUnit\\SebastianBergmann\\Comparator\\Comparator' => '/sebastian-comparator/Comparator.php', - 'PHPUnit\\SebastianBergmann\\Comparator\\ComparisonFailure' => '/sebastian-comparator/ComparisonFailure.php', - 'PHPUnit\\SebastianBergmann\\Comparator\\DOMNodeComparator' => '/sebastian-comparator/DOMNodeComparator.php', - 'PHPUnit\\SebastianBergmann\\Comparator\\DateTimeComparator' => '/sebastian-comparator/DateTimeComparator.php', - 'PHPUnit\\SebastianBergmann\\Comparator\\DoubleComparator' => '/sebastian-comparator/DoubleComparator.php', - 'PHPUnit\\SebastianBergmann\\Comparator\\Exception' => '/sebastian-comparator/exceptions/Exception.php', - 'PHPUnit\\SebastianBergmann\\Comparator\\ExceptionComparator' => '/sebastian-comparator/ExceptionComparator.php', - 'PHPUnit\\SebastianBergmann\\Comparator\\Factory' => '/sebastian-comparator/Factory.php', - 'PHPUnit\\SebastianBergmann\\Comparator\\MockObjectComparator' => '/sebastian-comparator/MockObjectComparator.php', - 'PHPUnit\\SebastianBergmann\\Comparator\\NumericComparator' => '/sebastian-comparator/NumericComparator.php', - 'PHPUnit\\SebastianBergmann\\Comparator\\ObjectComparator' => '/sebastian-comparator/ObjectComparator.php', - 'PHPUnit\\SebastianBergmann\\Comparator\\ResourceComparator' => '/sebastian-comparator/ResourceComparator.php', - 'PHPUnit\\SebastianBergmann\\Comparator\\RuntimeException' => '/sebastian-comparator/exceptions/RuntimeException.php', - 'PHPUnit\\SebastianBergmann\\Comparator\\ScalarComparator' => '/sebastian-comparator/ScalarComparator.php', - 'PHPUnit\\SebastianBergmann\\Comparator\\SplObjectStorageComparator' => '/sebastian-comparator/SplObjectStorageComparator.php', - 'PHPUnit\\SebastianBergmann\\Comparator\\TypeComparator' => '/sebastian-comparator/TypeComparator.php', - 'PHPUnit\\SebastianBergmann\\Complexity\\Calculator' => '/sebastian-complexity/Calculator.php', - 'PHPUnit\\SebastianBergmann\\Complexity\\Complexity' => '/sebastian-complexity/Complexity/Complexity.php', - 'PHPUnit\\SebastianBergmann\\Complexity\\ComplexityCalculatingVisitor' => '/sebastian-complexity/Visitor/ComplexityCalculatingVisitor.php', - 'PHPUnit\\SebastianBergmann\\Complexity\\ComplexityCollection' => '/sebastian-complexity/Complexity/ComplexityCollection.php', - 'PHPUnit\\SebastianBergmann\\Complexity\\ComplexityCollectionIterator' => '/sebastian-complexity/Complexity/ComplexityCollectionIterator.php', - 'PHPUnit\\SebastianBergmann\\Complexity\\CyclomaticComplexityCalculatingVisitor' => '/sebastian-complexity/Visitor/CyclomaticComplexityCalculatingVisitor.php', - 'PHPUnit\\SebastianBergmann\\Complexity\\Exception' => '/sebastian-complexity/Exception/Exception.php', - 'PHPUnit\\SebastianBergmann\\Complexity\\RuntimeException' => '/sebastian-complexity/Exception/RuntimeException.php', - 'PHPUnit\\SebastianBergmann\\Diff\\Chunk' => '/sebastian-diff/Chunk.php', - 'PHPUnit\\SebastianBergmann\\Diff\\ConfigurationException' => '/sebastian-diff/Exception/ConfigurationException.php', - 'PHPUnit\\SebastianBergmann\\Diff\\Diff' => '/sebastian-diff/Diff.php', - 'PHPUnit\\SebastianBergmann\\Diff\\Differ' => '/sebastian-diff/Differ.php', - 'PHPUnit\\SebastianBergmann\\Diff\\Exception' => '/sebastian-diff/Exception/Exception.php', - 'PHPUnit\\SebastianBergmann\\Diff\\InvalidArgumentException' => '/sebastian-diff/Exception/InvalidArgumentException.php', - 'PHPUnit\\SebastianBergmann\\Diff\\Line' => '/sebastian-diff/Line.php', - 'PHPUnit\\SebastianBergmann\\Diff\\LongestCommonSubsequenceCalculator' => '/sebastian-diff/LongestCommonSubsequenceCalculator.php', - 'PHPUnit\\SebastianBergmann\\Diff\\MemoryEfficientLongestCommonSubsequenceCalculator' => '/sebastian-diff/MemoryEfficientLongestCommonSubsequenceCalculator.php', - 'PHPUnit\\SebastianBergmann\\Diff\\Output\\AbstractChunkOutputBuilder' => '/sebastian-diff/Output/AbstractChunkOutputBuilder.php', - 'PHPUnit\\SebastianBergmann\\Diff\\Output\\DiffOnlyOutputBuilder' => '/sebastian-diff/Output/DiffOnlyOutputBuilder.php', - 'PHPUnit\\SebastianBergmann\\Diff\\Output\\DiffOutputBuilderInterface' => '/sebastian-diff/Output/DiffOutputBuilderInterface.php', - 'PHPUnit\\SebastianBergmann\\Diff\\Output\\StrictUnifiedDiffOutputBuilder' => '/sebastian-diff/Output/StrictUnifiedDiffOutputBuilder.php', - 'PHPUnit\\SebastianBergmann\\Diff\\Output\\UnifiedDiffOutputBuilder' => '/sebastian-diff/Output/UnifiedDiffOutputBuilder.php', - 'PHPUnit\\SebastianBergmann\\Diff\\Parser' => '/sebastian-diff/Parser.php', - 'PHPUnit\\SebastianBergmann\\Diff\\TimeEfficientLongestCommonSubsequenceCalculator' => '/sebastian-diff/TimeEfficientLongestCommonSubsequenceCalculator.php', - 'PHPUnit\\SebastianBergmann\\Environment\\Console' => '/sebastian-environment/Console.php', - 'PHPUnit\\SebastianBergmann\\Environment\\OperatingSystem' => '/sebastian-environment/OperatingSystem.php', - 'PHPUnit\\SebastianBergmann\\Environment\\Runtime' => '/sebastian-environment/Runtime.php', - 'PHPUnit\\SebastianBergmann\\Exporter\\Exporter' => '/sebastian-exporter/Exporter.php', - 'PHPUnit\\SebastianBergmann\\FileIterator\\Facade' => '/php-file-iterator/Facade.php', - 'PHPUnit\\SebastianBergmann\\FileIterator\\Factory' => '/php-file-iterator/Factory.php', - 'PHPUnit\\SebastianBergmann\\FileIterator\\Iterator' => '/php-file-iterator/Iterator.php', - 'PHPUnit\\SebastianBergmann\\GlobalState\\CodeExporter' => '/sebastian-global-state/CodeExporter.php', - 'PHPUnit\\SebastianBergmann\\GlobalState\\Exception' => '/sebastian-global-state/exceptions/Exception.php', - 'PHPUnit\\SebastianBergmann\\GlobalState\\ExcludeList' => '/sebastian-global-state/ExcludeList.php', - 'PHPUnit\\SebastianBergmann\\GlobalState\\Restorer' => '/sebastian-global-state/Restorer.php', - 'PHPUnit\\SebastianBergmann\\GlobalState\\RuntimeException' => '/sebastian-global-state/exceptions/RuntimeException.php', - 'PHPUnit\\SebastianBergmann\\GlobalState\\Snapshot' => '/sebastian-global-state/Snapshot.php', - 'PHPUnit\\SebastianBergmann\\Invoker\\Exception' => '/php-invoker/exceptions/Exception.php', - 'PHPUnit\\SebastianBergmann\\Invoker\\Invoker' => '/php-invoker/Invoker.php', - 'PHPUnit\\SebastianBergmann\\Invoker\\ProcessControlExtensionNotLoadedException' => '/php-invoker/exceptions/ProcessControlExtensionNotLoadedException.php', - 'PHPUnit\\SebastianBergmann\\Invoker\\TimeoutException' => '/php-invoker/exceptions/TimeoutException.php', - 'PHPUnit\\SebastianBergmann\\LinesOfCode\\Counter' => '/sebastian-lines-of-code/Counter.php', - 'PHPUnit\\SebastianBergmann\\LinesOfCode\\Exception' => '/sebastian-lines-of-code/Exception/Exception.php', - 'PHPUnit\\SebastianBergmann\\LinesOfCode\\IllogicalValuesException' => '/sebastian-lines-of-code/Exception/IllogicalValuesException.php', - 'PHPUnit\\SebastianBergmann\\LinesOfCode\\LineCountingVisitor' => '/sebastian-lines-of-code/LineCountingVisitor.php', - 'PHPUnit\\SebastianBergmann\\LinesOfCode\\LinesOfCode' => '/sebastian-lines-of-code/LinesOfCode.php', - 'PHPUnit\\SebastianBergmann\\LinesOfCode\\NegativeValueException' => '/sebastian-lines-of-code/Exception/NegativeValueException.php', - 'PHPUnit\\SebastianBergmann\\LinesOfCode\\RuntimeException' => '/sebastian-lines-of-code/Exception/RuntimeException.php', - 'PHPUnit\\SebastianBergmann\\ObjectEnumerator\\Enumerator' => '/sebastian-object-enumerator/Enumerator.php', - 'PHPUnit\\SebastianBergmann\\ObjectEnumerator\\Exception' => '/sebastian-object-enumerator/Exception.php', - 'PHPUnit\\SebastianBergmann\\ObjectEnumerator\\InvalidArgumentException' => '/sebastian-object-enumerator/InvalidArgumentException.php', - 'PHPUnit\\SebastianBergmann\\ObjectReflector\\Exception' => '/sebastian-object-reflector/Exception.php', - 'PHPUnit\\SebastianBergmann\\ObjectReflector\\InvalidArgumentException' => '/sebastian-object-reflector/InvalidArgumentException.php', - 'PHPUnit\\SebastianBergmann\\ObjectReflector\\ObjectReflector' => '/sebastian-object-reflector/ObjectReflector.php', - 'PHPUnit\\SebastianBergmann\\RecursionContext\\Context' => '/sebastian-recursion-context/Context.php', - 'PHPUnit\\SebastianBergmann\\RecursionContext\\Exception' => '/sebastian-recursion-context/Exception.php', - 'PHPUnit\\SebastianBergmann\\RecursionContext\\InvalidArgumentException' => '/sebastian-recursion-context/InvalidArgumentException.php', - 'PHPUnit\\SebastianBergmann\\ResourceOperations\\ResourceOperations' => '/sebastian-resource-operations/ResourceOperations.php', - 'PHPUnit\\SebastianBergmann\\Template\\Exception' => '/php-text-template/exceptions/Exception.php', - 'PHPUnit\\SebastianBergmann\\Template\\InvalidArgumentException' => '/php-text-template/exceptions/InvalidArgumentException.php', - 'PHPUnit\\SebastianBergmann\\Template\\RuntimeException' => '/php-text-template/exceptions/RuntimeException.php', - 'PHPUnit\\SebastianBergmann\\Template\\Template' => '/php-text-template/Template.php', - 'PHPUnit\\SebastianBergmann\\Timer\\Duration' => '/php-timer/Duration.php', - 'PHPUnit\\SebastianBergmann\\Timer\\Exception' => '/php-timer/exceptions/Exception.php', - 'PHPUnit\\SebastianBergmann\\Timer\\NoActiveTimerException' => '/php-timer/exceptions/NoActiveTimerException.php', - 'PHPUnit\\SebastianBergmann\\Timer\\ResourceUsageFormatter' => '/php-timer/ResourceUsageFormatter.php', - 'PHPUnit\\SebastianBergmann\\Timer\\TimeSinceStartOfRequestNotAvailableException' => '/php-timer/exceptions/TimeSinceStartOfRequestNotAvailableException.php', - 'PHPUnit\\SebastianBergmann\\Timer\\Timer' => '/php-timer/Timer.php', - 'PHPUnit\\SebastianBergmann\\Type\\CallableType' => '/sebastian-type/type/CallableType.php', - 'PHPUnit\\SebastianBergmann\\Type\\Exception' => '/sebastian-type/exception/Exception.php', - 'PHPUnit\\SebastianBergmann\\Type\\FalseType' => '/sebastian-type/type/FalseType.php', - 'PHPUnit\\SebastianBergmann\\Type\\GenericObjectType' => '/sebastian-type/type/GenericObjectType.php', - 'PHPUnit\\SebastianBergmann\\Type\\IntersectionType' => '/sebastian-type/type/IntersectionType.php', - 'PHPUnit\\SebastianBergmann\\Type\\IterableType' => '/sebastian-type/type/IterableType.php', - 'PHPUnit\\SebastianBergmann\\Type\\MixedType' => '/sebastian-type/type/MixedType.php', - 'PHPUnit\\SebastianBergmann\\Type\\NeverType' => '/sebastian-type/type/NeverType.php', - 'PHPUnit\\SebastianBergmann\\Type\\NullType' => '/sebastian-type/type/NullType.php', - 'PHPUnit\\SebastianBergmann\\Type\\ObjectType' => '/sebastian-type/type/ObjectType.php', - 'PHPUnit\\SebastianBergmann\\Type\\Parameter' => '/sebastian-type/Parameter.php', - 'PHPUnit\\SebastianBergmann\\Type\\ReflectionMapper' => '/sebastian-type/ReflectionMapper.php', - 'PHPUnit\\SebastianBergmann\\Type\\RuntimeException' => '/sebastian-type/exception/RuntimeException.php', - 'PHPUnit\\SebastianBergmann\\Type\\SimpleType' => '/sebastian-type/type/SimpleType.php', - 'PHPUnit\\SebastianBergmann\\Type\\StaticType' => '/sebastian-type/type/StaticType.php', - 'PHPUnit\\SebastianBergmann\\Type\\TrueType' => '/sebastian-type/type/TrueType.php', - 'PHPUnit\\SebastianBergmann\\Type\\Type' => '/sebastian-type/type/Type.php', - 'PHPUnit\\SebastianBergmann\\Type\\TypeName' => '/sebastian-type/TypeName.php', - 'PHPUnit\\SebastianBergmann\\Type\\UnionType' => '/sebastian-type/type/UnionType.php', - 'PHPUnit\\SebastianBergmann\\Type\\UnknownType' => '/sebastian-type/type/UnknownType.php', - 'PHPUnit\\SebastianBergmann\\Type\\VoidType' => '/sebastian-type/type/VoidType.php', - 'PHPUnit\\SebastianBergmann\\Version' => '/sebastian-version/Version.php', 'PHPUnit\\TextUI\\CliArguments\\Builder' => '/phpunit/TextUI/CliArguments/Builder.php', 'PHPUnit\\TextUI\\CliArguments\\Configuration' => '/phpunit/TextUI/CliArguments/Configuration.php', 'PHPUnit\\TextUI\\CliArguments\\Exception' => '/phpunit/TextUI/CliArguments/Exception.php', @@ -939,14 +1145,6 @@ spl_autoload_register( 'PHPUnit\\TextUI\\XmlConfiguration\\Variable' => '/phpunit/TextUI/XmlConfiguration/PHP/Variable.php', 'PHPUnit\\TextUI\\XmlConfiguration\\VariableCollection' => '/phpunit/TextUI/XmlConfiguration/PHP/VariableCollection.php', 'PHPUnit\\TextUI\\XmlConfiguration\\VariableCollectionIterator' => '/phpunit/TextUI/XmlConfiguration/PHP/VariableCollectionIterator.php', - 'PHPUnit\\TheSeer\\Tokenizer\\Exception' => '/theseer-tokenizer/Exception.php', - 'PHPUnit\\TheSeer\\Tokenizer\\NamespaceUri' => '/theseer-tokenizer/NamespaceUri.php', - 'PHPUnit\\TheSeer\\Tokenizer\\NamespaceUriException' => '/theseer-tokenizer/NamespaceUriException.php', - 'PHPUnit\\TheSeer\\Tokenizer\\Token' => '/theseer-tokenizer/Token.php', - 'PHPUnit\\TheSeer\\Tokenizer\\TokenCollection' => '/theseer-tokenizer/TokenCollection.php', - 'PHPUnit\\TheSeer\\Tokenizer\\TokenCollectionException' => '/theseer-tokenizer/TokenCollectionException.php', - 'PHPUnit\\TheSeer\\Tokenizer\\Tokenizer' => '/theseer-tokenizer/Tokenizer.php', - 'PHPUnit\\TheSeer\\Tokenizer\\XMLSerializer' => '/theseer-tokenizer/XMLSerializer.php', 'PHPUnit\\Util\\Annotation\\DocBlock' => '/phpunit/Util/Annotation/DocBlock.php', 'PHPUnit\\Util\\Annotation\\Registry' => '/phpunit/Util/Annotation/Registry.php', 'PHPUnit\\Util\\Blacklist' => '/phpunit/Util/Blacklist.php', @@ -993,105 +1191,6 @@ spl_autoload_register( 'PHPUnit\\Util\\Xml\\SuccessfulSchemaDetectionResult' => '/phpunit/Util/Xml/SuccessfulSchemaDetectionResult.php', 'PHPUnit\\Util\\Xml\\ValidationResult' => '/phpunit/Util/Xml/ValidationResult.php', 'PHPUnit\\Util\\Xml\\Validator' => '/phpunit/Util/Xml/Validator.php', - 'PHPUnit\\Webmozart\\Assert\\Assert' => '/webmozart-assert/Assert.php', - 'PHPUnit\\Webmozart\\Assert\\InvalidArgumentException' => '/webmozart-assert/InvalidArgumentException.php', - 'PHPUnit\\Webmozart\\Assert\\Mixin' => '/webmozart-assert/Mixin.php', - 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock' => '/phpdocumentor-reflection-docblock/DocBlock.php', - 'PHPUnit\\phpDocumentor\\Reflection\\DocBlockFactory' => '/phpdocumentor-reflection-docblock/DocBlockFactory.php', - 'PHPUnit\\phpDocumentor\\Reflection\\DocBlockFactoryInterface' => '/phpdocumentor-reflection-docblock/DocBlockFactoryInterface.php', - 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Description' => '/phpdocumentor-reflection-docblock/DocBlock/Description.php', - 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\DescriptionFactory' => '/phpdocumentor-reflection-docblock/DocBlock/DescriptionFactory.php', - 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\ExampleFinder' => '/phpdocumentor-reflection-docblock/DocBlock/ExampleFinder.php', - 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Serializer' => '/phpdocumentor-reflection-docblock/DocBlock/Serializer.php', - 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\StandardTagFactory' => '/phpdocumentor-reflection-docblock/DocBlock/StandardTagFactory.php', - 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Tag' => '/phpdocumentor-reflection-docblock/DocBlock/Tag.php', - 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\TagFactory' => '/phpdocumentor-reflection-docblock/DocBlock/TagFactory.php', - 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Author' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Author.php', - 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Tags\\BaseTag' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/BaseTag.php', - 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Covers' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Covers.php', - 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Deprecated' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Deprecated.php', - 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Example' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Example.php', - 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Factory\\StaticMethod' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Factory/StaticMethod.php', - 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Formatter' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Formatter.php', - 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Formatter\\AlignFormatter' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Formatter/AlignFormatter.php', - 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Formatter\\PassthroughFormatter' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Formatter/PassthroughFormatter.php', - 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Generic' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Generic.php', - 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Tags\\InvalidTag' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/InvalidTag.php', - 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Link' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Link.php', - 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Method' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Method.php', - 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Param' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Param.php', - 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Property' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Property.php', - 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Tags\\PropertyRead' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/PropertyRead.php', - 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Tags\\PropertyWrite' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/PropertyWrite.php', - 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Reference\\Fqsen' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Reference/Fqsen.php', - 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Reference\\Reference' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Reference/Reference.php', - 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Reference\\Url' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Reference/Url.php', - 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Return_' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Return_.php', - 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Tags\\See' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/See.php', - 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Since' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Since.php', - 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Source' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Source.php', - 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Tags\\TagWithType' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/TagWithType.php', - 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Throws' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Throws.php', - 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Uses' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Uses.php', - 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Var_' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Var_.php', - 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Version' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Version.php', - 'PHPUnit\\phpDocumentor\\Reflection\\Element' => '/phpdocumentor-reflection-common/Element.php', - 'PHPUnit\\phpDocumentor\\Reflection\\Exception\\PcreException' => '/phpdocumentor-reflection-docblock/Exception/PcreException.php', - 'PHPUnit\\phpDocumentor\\Reflection\\File' => '/phpdocumentor-reflection-common/File.php', - 'PHPUnit\\phpDocumentor\\Reflection\\Fqsen' => '/phpdocumentor-reflection-common/Fqsen.php', - 'PHPUnit\\phpDocumentor\\Reflection\\FqsenResolver' => '/phpdocumentor-type-resolver/FqsenResolver.php', - 'PHPUnit\\phpDocumentor\\Reflection\\Location' => '/phpdocumentor-reflection-common/Location.php', - 'PHPUnit\\phpDocumentor\\Reflection\\Project' => '/phpdocumentor-reflection-common/Project.php', - 'PHPUnit\\phpDocumentor\\Reflection\\ProjectFactory' => '/phpdocumentor-reflection-common/ProjectFactory.php', - 'PHPUnit\\phpDocumentor\\Reflection\\PseudoType' => '/phpdocumentor-type-resolver/PseudoType.php', - 'PHPUnit\\phpDocumentor\\Reflection\\PseudoTypes\\CallableString' => '/phpdocumentor-type-resolver/PseudoTypes/CallableString.php', - 'PHPUnit\\phpDocumentor\\Reflection\\PseudoTypes\\False_' => '/phpdocumentor-type-resolver/PseudoTypes/False_.php', - 'PHPUnit\\phpDocumentor\\Reflection\\PseudoTypes\\HtmlEscapedString' => '/phpdocumentor-type-resolver/PseudoTypes/HtmlEscapedString.php', - 'PHPUnit\\phpDocumentor\\Reflection\\PseudoTypes\\IntegerRange' => '/phpdocumentor-type-resolver/PseudoTypes/IntegerRange.php', - 'PHPUnit\\phpDocumentor\\Reflection\\PseudoTypes\\List_' => '/phpdocumentor-type-resolver/PseudoTypes/List_.php', - 'PHPUnit\\phpDocumentor\\Reflection\\PseudoTypes\\LiteralString' => '/phpdocumentor-type-resolver/PseudoTypes/LiteralString.php', - 'PHPUnit\\phpDocumentor\\Reflection\\PseudoTypes\\LowercaseString' => '/phpdocumentor-type-resolver/PseudoTypes/LowercaseString.php', - 'PHPUnit\\phpDocumentor\\Reflection\\PseudoTypes\\NegativeInteger' => '/phpdocumentor-type-resolver/PseudoTypes/NegativeInteger.php', - 'PHPUnit\\phpDocumentor\\Reflection\\PseudoTypes\\NonEmptyLowercaseString' => '/phpdocumentor-type-resolver/PseudoTypes/NonEmptyLowercaseString.php', - 'PHPUnit\\phpDocumentor\\Reflection\\PseudoTypes\\NonEmptyString' => '/phpdocumentor-type-resolver/PseudoTypes/NonEmptyString.php', - 'PHPUnit\\phpDocumentor\\Reflection\\PseudoTypes\\NumericString' => '/phpdocumentor-type-resolver/PseudoTypes/NumericString.php', - 'PHPUnit\\phpDocumentor\\Reflection\\PseudoTypes\\Numeric_' => '/phpdocumentor-type-resolver/PseudoTypes/Numeric_.php', - 'PHPUnit\\phpDocumentor\\Reflection\\PseudoTypes\\PositiveInteger' => '/phpdocumentor-type-resolver/PseudoTypes/PositiveInteger.php', - 'PHPUnit\\phpDocumentor\\Reflection\\PseudoTypes\\TraitString' => '/phpdocumentor-type-resolver/PseudoTypes/TraitString.php', - 'PHPUnit\\phpDocumentor\\Reflection\\PseudoTypes\\True_' => '/phpdocumentor-type-resolver/PseudoTypes/True_.php', - 'PHPUnit\\phpDocumentor\\Reflection\\Type' => '/phpdocumentor-type-resolver/Type.php', - 'PHPUnit\\phpDocumentor\\Reflection\\TypeResolver' => '/phpdocumentor-type-resolver/TypeResolver.php', - 'PHPUnit\\phpDocumentor\\Reflection\\Types\\AbstractList' => '/phpdocumentor-type-resolver/Types/AbstractList.php', - 'PHPUnit\\phpDocumentor\\Reflection\\Types\\AggregatedType' => '/phpdocumentor-type-resolver/Types/AggregatedType.php', - 'PHPUnit\\phpDocumentor\\Reflection\\Types\\ArrayKey' => '/phpdocumentor-type-resolver/Types/ArrayKey.php', - 'PHPUnit\\phpDocumentor\\Reflection\\Types\\Array_' => '/phpdocumentor-type-resolver/Types/Array_.php', - 'PHPUnit\\phpDocumentor\\Reflection\\Types\\Boolean' => '/phpdocumentor-type-resolver/Types/Boolean.php', - 'PHPUnit\\phpDocumentor\\Reflection\\Types\\Callable_' => '/phpdocumentor-type-resolver/Types/Callable_.php', - 'PHPUnit\\phpDocumentor\\Reflection\\Types\\ClassString' => '/phpdocumentor-type-resolver/Types/ClassString.php', - 'PHPUnit\\phpDocumentor\\Reflection\\Types\\Collection' => '/phpdocumentor-type-resolver/Types/Collection.php', - 'PHPUnit\\phpDocumentor\\Reflection\\Types\\Compound' => '/phpdocumentor-type-resolver/Types/Compound.php', - 'PHPUnit\\phpDocumentor\\Reflection\\Types\\Context' => '/phpdocumentor-type-resolver/Types/Context.php', - 'PHPUnit\\phpDocumentor\\Reflection\\Types\\ContextFactory' => '/phpdocumentor-type-resolver/Types/ContextFactory.php', - 'PHPUnit\\phpDocumentor\\Reflection\\Types\\Expression' => '/phpdocumentor-type-resolver/Types/Expression.php', - 'PHPUnit\\phpDocumentor\\Reflection\\Types\\Float_' => '/phpdocumentor-type-resolver/Types/Float_.php', - 'PHPUnit\\phpDocumentor\\Reflection\\Types\\Integer' => '/phpdocumentor-type-resolver/Types/Integer.php', - 'PHPUnit\\phpDocumentor\\Reflection\\Types\\InterfaceString' => '/phpdocumentor-type-resolver/Types/InterfaceString.php', - 'PHPUnit\\phpDocumentor\\Reflection\\Types\\Intersection' => '/phpdocumentor-type-resolver/Types/Intersection.php', - 'PHPUnit\\phpDocumentor\\Reflection\\Types\\Iterable_' => '/phpdocumentor-type-resolver/Types/Iterable_.php', - 'PHPUnit\\phpDocumentor\\Reflection\\Types\\Mixed_' => '/phpdocumentor-type-resolver/Types/Mixed_.php', - 'PHPUnit\\phpDocumentor\\Reflection\\Types\\Never_' => '/phpdocumentor-type-resolver/Types/Never_.php', - 'PHPUnit\\phpDocumentor\\Reflection\\Types\\Null_' => '/phpdocumentor-type-resolver/Types/Null_.php', - 'PHPUnit\\phpDocumentor\\Reflection\\Types\\Nullable' => '/phpdocumentor-type-resolver/Types/Nullable.php', - 'PHPUnit\\phpDocumentor\\Reflection\\Types\\Object_' => '/phpdocumentor-type-resolver/Types/Object_.php', - 'PHPUnit\\phpDocumentor\\Reflection\\Types\\Parent_' => '/phpdocumentor-type-resolver/Types/Parent_.php', - 'PHPUnit\\phpDocumentor\\Reflection\\Types\\Resource_' => '/phpdocumentor-type-resolver/Types/Resource_.php', - 'PHPUnit\\phpDocumentor\\Reflection\\Types\\Scalar' => '/phpdocumentor-type-resolver/Types/Scalar.php', - 'PHPUnit\\phpDocumentor\\Reflection\\Types\\Self_' => '/phpdocumentor-type-resolver/Types/Self_.php', - 'PHPUnit\\phpDocumentor\\Reflection\\Types\\Static_' => '/phpdocumentor-type-resolver/Types/Static_.php', - 'PHPUnit\\phpDocumentor\\Reflection\\Types\\String_' => '/phpdocumentor-type-resolver/Types/String_.php', - 'PHPUnit\\phpDocumentor\\Reflection\\Types\\This' => '/phpdocumentor-type-resolver/Types/This.php', - 'PHPUnit\\phpDocumentor\\Reflection\\Types\\Void_' => '/phpdocumentor-type-resolver/Types/Void_.php', - 'PHPUnit\\phpDocumentor\\Reflection\\Utils' => '/phpdocumentor-reflection-docblock/Utils.php', 'Prophecy\\Argument' => '/phpspec-prophecy/Prophecy/Argument.php', 'Prophecy\\Argument\\ArgumentsWildcard' => '/phpspec-prophecy/Prophecy/Argument/ArgumentsWildcard.php', 'Prophecy\\Argument\\Token\\AnyValueToken' => '/phpspec-prophecy/Prophecy/Argument/Token/AnyValueToken.php', @@ -1188,43 +1287,768 @@ spl_autoload_register( } if (isset($classes[$class])) { - require_once 'phar://phpunit-9.6.13.phar' . $classes[$class]; + require_once 'phar://phpunit-9.6.19.phar' . $classes[$class]; } }, true, false ); -foreach (['PHPUnit\\DeepCopy\\DeepCopy' => '/myclabs-deep-copy/DeepCopy/DeepCopy.php', - 'PHPUnit\\DeepCopy\\Exception\\CloneException' => '/myclabs-deep-copy/DeepCopy/Exception/CloneException.php', - 'PHPUnit\\DeepCopy\\Exception\\PropertyException' => '/myclabs-deep-copy/DeepCopy/Exception/PropertyException.php', - 'PHPUnit\\DeepCopy\\Filter\\ChainableFilter' => '/myclabs-deep-copy/DeepCopy/Filter/ChainableFilter.php', - 'PHPUnit\\DeepCopy\\Filter\\Doctrine\\DoctrineCollectionFilter' => '/myclabs-deep-copy/DeepCopy/Filter/Doctrine/DoctrineCollectionFilter.php', - 'PHPUnit\\DeepCopy\\Filter\\Doctrine\\DoctrineEmptyCollectionFilter' => '/myclabs-deep-copy/DeepCopy/Filter/Doctrine/DoctrineEmptyCollectionFilter.php', - 'PHPUnit\\DeepCopy\\Filter\\Doctrine\\DoctrineProxyFilter' => '/myclabs-deep-copy/DeepCopy/Filter/Doctrine/DoctrineProxyFilter.php', - 'PHPUnit\\DeepCopy\\Filter\\Filter' => '/myclabs-deep-copy/DeepCopy/Filter/Filter.php', - 'PHPUnit\\DeepCopy\\Filter\\KeepFilter' => '/myclabs-deep-copy/DeepCopy/Filter/KeepFilter.php', - 'PHPUnit\\DeepCopy\\Filter\\ReplaceFilter' => '/myclabs-deep-copy/DeepCopy/Filter/ReplaceFilter.php', - 'PHPUnit\\DeepCopy\\Filter\\SetNullFilter' => '/myclabs-deep-copy/DeepCopy/Filter/SetNullFilter.php', - 'PHPUnit\\DeepCopy\\Matcher\\Doctrine\\DoctrineProxyMatcher' => '/myclabs-deep-copy/DeepCopy/Matcher/Doctrine/DoctrineProxyMatcher.php', - 'PHPUnit\\DeepCopy\\Matcher\\Matcher' => '/myclabs-deep-copy/DeepCopy/Matcher/Matcher.php', - 'PHPUnit\\DeepCopy\\Matcher\\PropertyMatcher' => '/myclabs-deep-copy/DeepCopy/Matcher/PropertyMatcher.php', - 'PHPUnit\\DeepCopy\\Matcher\\PropertyNameMatcher' => '/myclabs-deep-copy/DeepCopy/Matcher/PropertyNameMatcher.php', - 'PHPUnit\\DeepCopy\\Matcher\\PropertyTypeMatcher' => '/myclabs-deep-copy/DeepCopy/Matcher/PropertyTypeMatcher.php', - 'PHPUnit\\DeepCopy\\Reflection\\ReflectionHelper' => '/myclabs-deep-copy/DeepCopy/Reflection/ReflectionHelper.php', - 'PHPUnit\\DeepCopy\\TypeFilter\\Date\\DateIntervalFilter' => '/myclabs-deep-copy/DeepCopy/TypeFilter/Date/DateIntervalFilter.php', - 'PHPUnit\\DeepCopy\\TypeFilter\\ReplaceFilter' => '/myclabs-deep-copy/DeepCopy/TypeFilter/ReplaceFilter.php', - 'PHPUnit\\DeepCopy\\TypeFilter\\ShallowCopyFilter' => '/myclabs-deep-copy/DeepCopy/TypeFilter/ShallowCopyFilter.php', - 'PHPUnit\\DeepCopy\\TypeFilter\\Spl\\ArrayObjectFilter' => '/myclabs-deep-copy/DeepCopy/TypeFilter/Spl/ArrayObjectFilter.php', - 'PHPUnit\\DeepCopy\\TypeFilter\\Spl\\SplDoublyLinkedList' => '/myclabs-deep-copy/DeepCopy/TypeFilter/Spl/SplDoublyLinkedList.php', - 'PHPUnit\\DeepCopy\\TypeFilter\\Spl\\SplDoublyLinkedListFilter' => '/myclabs-deep-copy/DeepCopy/TypeFilter/Spl/SplDoublyLinkedListFilter.php', - 'PHPUnit\\DeepCopy\\TypeFilter\\TypeFilter' => '/myclabs-deep-copy/DeepCopy/TypeFilter/TypeFilter.php', - 'PHPUnit\\DeepCopy\\TypeMatcher\\TypeMatcher' => '/myclabs-deep-copy/DeepCopy/TypeMatcher/TypeMatcher.php', - 'PHPUnit\\Doctrine\\Instantiator\\Exception\\ExceptionInterface' => '/doctrine-instantiator/Doctrine/Instantiator/Exception/ExceptionInterface.php', - 'PHPUnit\\Doctrine\\Instantiator\\Exception\\InvalidArgumentException' => '/doctrine-instantiator/Doctrine/Instantiator/Exception/InvalidArgumentException.php', - 'PHPUnit\\Doctrine\\Instantiator\\Exception\\UnexpectedValueException' => '/doctrine-instantiator/Doctrine/Instantiator/Exception/UnexpectedValueException.php', - 'PHPUnit\\Doctrine\\Instantiator\\Instantiator' => '/doctrine-instantiator/Doctrine/Instantiator/Instantiator.php', - 'PHPUnit\\Doctrine\\Instantiator\\InstantiatorInterface' => '/doctrine-instantiator/Doctrine/Instantiator/InstantiatorInterface.php', +foreach (['Doctrine\\Deprecations\\PHPUnit\\VerifyDeprecations' => '/doctrine-deprecations/Doctrine/Deprecations/PHPUnit/VerifyDeprecations.php', + 'PHPUnitPHAR\\DeepCopy\\DeepCopy' => '/myclabs-deep-copy/DeepCopy/DeepCopy.php', + 'PHPUnitPHAR\\DeepCopy\\Exception\\CloneException' => '/myclabs-deep-copy/DeepCopy/Exception/CloneException.php', + 'PHPUnitPHAR\\DeepCopy\\Exception\\PropertyException' => '/myclabs-deep-copy/DeepCopy/Exception/PropertyException.php', + 'PHPUnitPHAR\\DeepCopy\\Filter\\ChainableFilter' => '/myclabs-deep-copy/DeepCopy/Filter/ChainableFilter.php', + 'PHPUnitPHAR\\DeepCopy\\Filter\\Doctrine\\DoctrineCollectionFilter' => '/myclabs-deep-copy/DeepCopy/Filter/Doctrine/DoctrineCollectionFilter.php', + 'PHPUnitPHAR\\DeepCopy\\Filter\\Doctrine\\DoctrineEmptyCollectionFilter' => '/myclabs-deep-copy/DeepCopy/Filter/Doctrine/DoctrineEmptyCollectionFilter.php', + 'PHPUnitPHAR\\DeepCopy\\Filter\\Doctrine\\DoctrineProxyFilter' => '/myclabs-deep-copy/DeepCopy/Filter/Doctrine/DoctrineProxyFilter.php', + 'PHPUnitPHAR\\DeepCopy\\Filter\\Filter' => '/myclabs-deep-copy/DeepCopy/Filter/Filter.php', + 'PHPUnitPHAR\\DeepCopy\\Filter\\KeepFilter' => '/myclabs-deep-copy/DeepCopy/Filter/KeepFilter.php', + 'PHPUnitPHAR\\DeepCopy\\Filter\\ReplaceFilter' => '/myclabs-deep-copy/DeepCopy/Filter/ReplaceFilter.php', + 'PHPUnitPHAR\\DeepCopy\\Filter\\SetNullFilter' => '/myclabs-deep-copy/DeepCopy/Filter/SetNullFilter.php', + 'PHPUnitPHAR\\DeepCopy\\Matcher\\Doctrine\\DoctrineProxyMatcher' => '/myclabs-deep-copy/DeepCopy/Matcher/Doctrine/DoctrineProxyMatcher.php', + 'PHPUnitPHAR\\DeepCopy\\Matcher\\Matcher' => '/myclabs-deep-copy/DeepCopy/Matcher/Matcher.php', + 'PHPUnitPHAR\\DeepCopy\\Matcher\\PropertyMatcher' => '/myclabs-deep-copy/DeepCopy/Matcher/PropertyMatcher.php', + 'PHPUnitPHAR\\DeepCopy\\Matcher\\PropertyNameMatcher' => '/myclabs-deep-copy/DeepCopy/Matcher/PropertyNameMatcher.php', + 'PHPUnitPHAR\\DeepCopy\\Matcher\\PropertyTypeMatcher' => '/myclabs-deep-copy/DeepCopy/Matcher/PropertyTypeMatcher.php', + 'PHPUnitPHAR\\DeepCopy\\Reflection\\ReflectionHelper' => '/myclabs-deep-copy/DeepCopy/Reflection/ReflectionHelper.php', + 'PHPUnitPHAR\\DeepCopy\\TypeFilter\\Date\\DateIntervalFilter' => '/myclabs-deep-copy/DeepCopy/TypeFilter/Date/DateIntervalFilter.php', + 'PHPUnitPHAR\\DeepCopy\\TypeFilter\\ReplaceFilter' => '/myclabs-deep-copy/DeepCopy/TypeFilter/ReplaceFilter.php', + 'PHPUnitPHAR\\DeepCopy\\TypeFilter\\ShallowCopyFilter' => '/myclabs-deep-copy/DeepCopy/TypeFilter/ShallowCopyFilter.php', + 'PHPUnitPHAR\\DeepCopy\\TypeFilter\\Spl\\ArrayObjectFilter' => '/myclabs-deep-copy/DeepCopy/TypeFilter/Spl/ArrayObjectFilter.php', + 'PHPUnitPHAR\\DeepCopy\\TypeFilter\\Spl\\SplDoublyLinkedList' => '/myclabs-deep-copy/DeepCopy/TypeFilter/Spl/SplDoublyLinkedList.php', + 'PHPUnitPHAR\\DeepCopy\\TypeFilter\\Spl\\SplDoublyLinkedListFilter' => '/myclabs-deep-copy/DeepCopy/TypeFilter/Spl/SplDoublyLinkedListFilter.php', + 'PHPUnitPHAR\\DeepCopy\\TypeFilter\\TypeFilter' => '/myclabs-deep-copy/DeepCopy/TypeFilter/TypeFilter.php', + 'PHPUnitPHAR\\DeepCopy\\TypeMatcher\\TypeMatcher' => '/myclabs-deep-copy/DeepCopy/TypeMatcher/TypeMatcher.php', + 'PHPUnitPHAR\\Doctrine\\Deprecations\\Deprecation' => '/doctrine-deprecations/Doctrine/Deprecations/Deprecation.php', + 'PHPUnitPHAR\\Doctrine\\Instantiator\\Exception\\ExceptionInterface' => '/doctrine-instantiator/Doctrine/Instantiator/Exception/ExceptionInterface.php', + 'PHPUnitPHAR\\Doctrine\\Instantiator\\Exception\\InvalidArgumentException' => '/doctrine-instantiator/Doctrine/Instantiator/Exception/InvalidArgumentException.php', + 'PHPUnitPHAR\\Doctrine\\Instantiator\\Exception\\UnexpectedValueException' => '/doctrine-instantiator/Doctrine/Instantiator/Exception/UnexpectedValueException.php', + 'PHPUnitPHAR\\Doctrine\\Instantiator\\Instantiator' => '/doctrine-instantiator/Doctrine/Instantiator/Instantiator.php', + 'PHPUnitPHAR\\Doctrine\\Instantiator\\InstantiatorInterface' => '/doctrine-instantiator/Doctrine/Instantiator/InstantiatorInterface.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\AbstractNodeVisitor' => '/phpstan-phpdoc-parser/Ast/AbstractNodeVisitor.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\Attribute' => '/phpstan-phpdoc-parser/Ast/Attribute.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\ConstExpr\\ConstExprArrayItemNode' => '/phpstan-phpdoc-parser/Ast/ConstExpr/ConstExprArrayItemNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\ConstExpr\\ConstExprArrayNode' => '/phpstan-phpdoc-parser/Ast/ConstExpr/ConstExprArrayNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\ConstExpr\\ConstExprFalseNode' => '/phpstan-phpdoc-parser/Ast/ConstExpr/ConstExprFalseNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\ConstExpr\\ConstExprFloatNode' => '/phpstan-phpdoc-parser/Ast/ConstExpr/ConstExprFloatNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\ConstExpr\\ConstExprIntegerNode' => '/phpstan-phpdoc-parser/Ast/ConstExpr/ConstExprIntegerNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\ConstExpr\\ConstExprNode' => '/phpstan-phpdoc-parser/Ast/ConstExpr/ConstExprNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\ConstExpr\\ConstExprNullNode' => '/phpstan-phpdoc-parser/Ast/ConstExpr/ConstExprNullNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\ConstExpr\\ConstExprStringNode' => '/phpstan-phpdoc-parser/Ast/ConstExpr/ConstExprStringNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\ConstExpr\\ConstExprTrueNode' => '/phpstan-phpdoc-parser/Ast/ConstExpr/ConstExprTrueNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\ConstExpr\\ConstFetchNode' => '/phpstan-phpdoc-parser/Ast/ConstExpr/ConstFetchNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\ConstExpr\\DoctrineConstExprStringNode' => '/phpstan-phpdoc-parser/Ast/ConstExpr/DoctrineConstExprStringNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\ConstExpr\\QuoteAwareConstExprStringNode' => '/phpstan-phpdoc-parser/Ast/ConstExpr/QuoteAwareConstExprStringNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\Node' => '/phpstan-phpdoc-parser/Ast/Node.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\NodeAttributes' => '/phpstan-phpdoc-parser/Ast/NodeAttributes.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\NodeTraverser' => '/phpstan-phpdoc-parser/Ast/NodeTraverser.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\NodeVisitor' => '/phpstan-phpdoc-parser/Ast/NodeVisitor.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\NodeVisitor\\CloningVisitor' => '/phpstan-phpdoc-parser/Ast/NodeVisitor/CloningVisitor.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\AssertTagMethodValueNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/AssertTagMethodValueNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\AssertTagPropertyValueNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/AssertTagPropertyValueNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\AssertTagValueNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/AssertTagValueNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\DeprecatedTagValueNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/DeprecatedTagValueNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\Doctrine\\DoctrineAnnotation' => '/phpstan-phpdoc-parser/Ast/PhpDoc/Doctrine/DoctrineAnnotation.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\Doctrine\\DoctrineArgument' => '/phpstan-phpdoc-parser/Ast/PhpDoc/Doctrine/DoctrineArgument.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\Doctrine\\DoctrineArray' => '/phpstan-phpdoc-parser/Ast/PhpDoc/Doctrine/DoctrineArray.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\Doctrine\\DoctrineArrayItem' => '/phpstan-phpdoc-parser/Ast/PhpDoc/Doctrine/DoctrineArrayItem.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\Doctrine\\DoctrineTagValueNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/Doctrine/DoctrineTagValueNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\ExtendsTagValueNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/ExtendsTagValueNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\GenericTagValueNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/GenericTagValueNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\ImplementsTagValueNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/ImplementsTagValueNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\InvalidTagValueNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/InvalidTagValueNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\MethodTagValueNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/MethodTagValueNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\MethodTagValueParameterNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/MethodTagValueParameterNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\MixinTagValueNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/MixinTagValueNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\ParamClosureThisTagValueNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/ParamClosureThisTagValueNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\ParamImmediatelyInvokedCallableTagValueNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/ParamImmediatelyInvokedCallableTagValueNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\ParamLaterInvokedCallableTagValueNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/ParamLaterInvokedCallableTagValueNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\ParamOutTagValueNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/ParamOutTagValueNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\ParamTagValueNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/ParamTagValueNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\PhpDocChildNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/PhpDocChildNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\PhpDocNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/PhpDocNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\PhpDocTagNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/PhpDocTagNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\PhpDocTagValueNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/PhpDocTagValueNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\PhpDocTextNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/PhpDocTextNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\PropertyTagValueNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/PropertyTagValueNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\RequireExtendsTagValueNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/RequireExtendsTagValueNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\RequireImplementsTagValueNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/RequireImplementsTagValueNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\ReturnTagValueNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/ReturnTagValueNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\SelfOutTagValueNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/SelfOutTagValueNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\TemplateTagValueNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/TemplateTagValueNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\ThrowsTagValueNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/ThrowsTagValueNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\TypeAliasImportTagValueNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/TypeAliasImportTagValueNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\TypeAliasTagValueNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/TypeAliasTagValueNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\TypelessParamTagValueNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/TypelessParamTagValueNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\UsesTagValueNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/UsesTagValueNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\VarTagValueNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/VarTagValueNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\Type\\ArrayShapeItemNode' => '/phpstan-phpdoc-parser/Ast/Type/ArrayShapeItemNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\Type\\ArrayShapeNode' => '/phpstan-phpdoc-parser/Ast/Type/ArrayShapeNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\Type\\ArrayTypeNode' => '/phpstan-phpdoc-parser/Ast/Type/ArrayTypeNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\Type\\CallableTypeNode' => '/phpstan-phpdoc-parser/Ast/Type/CallableTypeNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\Type\\CallableTypeParameterNode' => '/phpstan-phpdoc-parser/Ast/Type/CallableTypeParameterNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\Type\\ConditionalTypeForParameterNode' => '/phpstan-phpdoc-parser/Ast/Type/ConditionalTypeForParameterNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\Type\\ConditionalTypeNode' => '/phpstan-phpdoc-parser/Ast/Type/ConditionalTypeNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\Type\\ConstTypeNode' => '/phpstan-phpdoc-parser/Ast/Type/ConstTypeNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\Type\\GenericTypeNode' => '/phpstan-phpdoc-parser/Ast/Type/GenericTypeNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\Type\\IdentifierTypeNode' => '/phpstan-phpdoc-parser/Ast/Type/IdentifierTypeNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\Type\\IntersectionTypeNode' => '/phpstan-phpdoc-parser/Ast/Type/IntersectionTypeNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\Type\\InvalidTypeNode' => '/phpstan-phpdoc-parser/Ast/Type/InvalidTypeNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\Type\\NullableTypeNode' => '/phpstan-phpdoc-parser/Ast/Type/NullableTypeNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\Type\\ObjectShapeItemNode' => '/phpstan-phpdoc-parser/Ast/Type/ObjectShapeItemNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\Type\\ObjectShapeNode' => '/phpstan-phpdoc-parser/Ast/Type/ObjectShapeNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\Type\\OffsetAccessTypeNode' => '/phpstan-phpdoc-parser/Ast/Type/OffsetAccessTypeNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\Type\\ThisTypeNode' => '/phpstan-phpdoc-parser/Ast/Type/ThisTypeNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\Type\\TypeNode' => '/phpstan-phpdoc-parser/Ast/Type/TypeNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\Type\\UnionTypeNode' => '/phpstan-phpdoc-parser/Ast/Type/UnionTypeNode.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Lexer\\Lexer' => '/phpstan-phpdoc-parser/Lexer/Lexer.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Parser\\ConstExprParser' => '/phpstan-phpdoc-parser/Parser/ConstExprParser.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Parser\\ParserException' => '/phpstan-phpdoc-parser/Parser/ParserException.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Parser\\PhpDocParser' => '/phpstan-phpdoc-parser/Parser/PhpDocParser.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Parser\\StringUnescaper' => '/phpstan-phpdoc-parser/Parser/StringUnescaper.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Parser\\TokenIterator' => '/phpstan-phpdoc-parser/Parser/TokenIterator.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Parser\\TypeParser' => '/phpstan-phpdoc-parser/Parser/TypeParser.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Printer\\DiffElem' => '/phpstan-phpdoc-parser/Printer/DiffElem.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Printer\\Differ' => '/phpstan-phpdoc-parser/Printer/Differ.php', + 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Printer\\Printer' => '/phpstan-phpdoc-parser/Printer/Printer.php', + 'PHPUnitPHAR\\PharIo\\Manifest\\Application' => '/phar-io-manifest/values/Application.php', + 'PHPUnitPHAR\\PharIo\\Manifest\\ApplicationName' => '/phar-io-manifest/values/ApplicationName.php', + 'PHPUnitPHAR\\PharIo\\Manifest\\Author' => '/phar-io-manifest/values/Author.php', + 'PHPUnitPHAR\\PharIo\\Manifest\\AuthorCollection' => '/phar-io-manifest/values/AuthorCollection.php', + 'PHPUnitPHAR\\PharIo\\Manifest\\AuthorCollectionIterator' => '/phar-io-manifest/values/AuthorCollectionIterator.php', + 'PHPUnitPHAR\\PharIo\\Manifest\\AuthorElement' => '/phar-io-manifest/xml/AuthorElement.php', + 'PHPUnitPHAR\\PharIo\\Manifest\\AuthorElementCollection' => '/phar-io-manifest/xml/AuthorElementCollection.php', + 'PHPUnitPHAR\\PharIo\\Manifest\\BundledComponent' => '/phar-io-manifest/values/BundledComponent.php', + 'PHPUnitPHAR\\PharIo\\Manifest\\BundledComponentCollection' => '/phar-io-manifest/values/BundledComponentCollection.php', + 'PHPUnitPHAR\\PharIo\\Manifest\\BundledComponentCollectionIterator' => '/phar-io-manifest/values/BundledComponentCollectionIterator.php', + 'PHPUnitPHAR\\PharIo\\Manifest\\BundlesElement' => '/phar-io-manifest/xml/BundlesElement.php', + 'PHPUnitPHAR\\PharIo\\Manifest\\ComponentElement' => '/phar-io-manifest/xml/ComponentElement.php', + 'PHPUnitPHAR\\PharIo\\Manifest\\ComponentElementCollection' => '/phar-io-manifest/xml/ComponentElementCollection.php', + 'PHPUnitPHAR\\PharIo\\Manifest\\ContainsElement' => '/phar-io-manifest/xml/ContainsElement.php', + 'PHPUnitPHAR\\PharIo\\Manifest\\CopyrightElement' => '/phar-io-manifest/xml/CopyrightElement.php', + 'PHPUnitPHAR\\PharIo\\Manifest\\CopyrightInformation' => '/phar-io-manifest/values/CopyrightInformation.php', + 'PHPUnitPHAR\\PharIo\\Manifest\\ElementCollection' => '/phar-io-manifest/xml/ElementCollection.php', + 'PHPUnitPHAR\\PharIo\\Manifest\\ElementCollectionException' => '/phar-io-manifest/exceptions/ElementCollectionException.php', + 'PHPUnitPHAR\\PharIo\\Manifest\\Email' => '/phar-io-manifest/values/Email.php', + 'PHPUnitPHAR\\PharIo\\Manifest\\Exception' => '/phar-io-manifest/exceptions/Exception.php', + 'PHPUnitPHAR\\PharIo\\Manifest\\ExtElement' => '/phar-io-manifest/xml/ExtElement.php', + 'PHPUnitPHAR\\PharIo\\Manifest\\ExtElementCollection' => '/phar-io-manifest/xml/ExtElementCollection.php', + 'PHPUnitPHAR\\PharIo\\Manifest\\Extension' => '/phar-io-manifest/values/Extension.php', + 'PHPUnitPHAR\\PharIo\\Manifest\\ExtensionElement' => '/phar-io-manifest/xml/ExtensionElement.php', + 'PHPUnitPHAR\\PharIo\\Manifest\\InvalidApplicationNameException' => '/phar-io-manifest/exceptions/InvalidApplicationNameException.php', + 'PHPUnitPHAR\\PharIo\\Manifest\\InvalidEmailException' => '/phar-io-manifest/exceptions/InvalidEmailException.php', + 'PHPUnitPHAR\\PharIo\\Manifest\\InvalidUrlException' => '/phar-io-manifest/exceptions/InvalidUrlException.php', + 'PHPUnitPHAR\\PharIo\\Manifest\\Library' => '/phar-io-manifest/values/Library.php', + 'PHPUnitPHAR\\PharIo\\Manifest\\License' => '/phar-io-manifest/values/License.php', + 'PHPUnitPHAR\\PharIo\\Manifest\\LicenseElement' => '/phar-io-manifest/xml/LicenseElement.php', + 'PHPUnitPHAR\\PharIo\\Manifest\\Manifest' => '/phar-io-manifest/values/Manifest.php', + 'PHPUnitPHAR\\PharIo\\Manifest\\ManifestDocument' => '/phar-io-manifest/xml/ManifestDocument.php', + 'PHPUnitPHAR\\PharIo\\Manifest\\ManifestDocumentException' => '/phar-io-manifest/exceptions/ManifestDocumentException.php', + 'PHPUnitPHAR\\PharIo\\Manifest\\ManifestDocumentLoadingException' => '/phar-io-manifest/exceptions/ManifestDocumentLoadingException.php', + 'PHPUnitPHAR\\PharIo\\Manifest\\ManifestDocumentMapper' => '/phar-io-manifest/ManifestDocumentMapper.php', + 'PHPUnitPHAR\\PharIo\\Manifest\\ManifestDocumentMapperException' => '/phar-io-manifest/exceptions/ManifestDocumentMapperException.php', + 'PHPUnitPHAR\\PharIo\\Manifest\\ManifestElement' => '/phar-io-manifest/xml/ManifestElement.php', + 'PHPUnitPHAR\\PharIo\\Manifest\\ManifestElementException' => '/phar-io-manifest/exceptions/ManifestElementException.php', + 'PHPUnitPHAR\\PharIo\\Manifest\\ManifestLoader' => '/phar-io-manifest/ManifestLoader.php', + 'PHPUnitPHAR\\PharIo\\Manifest\\ManifestLoaderException' => '/phar-io-manifest/exceptions/ManifestLoaderException.php', + 'PHPUnitPHAR\\PharIo\\Manifest\\ManifestSerializer' => '/phar-io-manifest/ManifestSerializer.php', + 'PHPUnitPHAR\\PharIo\\Manifest\\NoEmailAddressException' => '/phar-io-manifest/exceptions/NoEmailAddressException.php', + 'PHPUnitPHAR\\PharIo\\Manifest\\PhpElement' => '/phar-io-manifest/xml/PhpElement.php', + 'PHPUnitPHAR\\PharIo\\Manifest\\PhpExtensionRequirement' => '/phar-io-manifest/values/PhpExtensionRequirement.php', + 'PHPUnitPHAR\\PharIo\\Manifest\\PhpVersionRequirement' => '/phar-io-manifest/values/PhpVersionRequirement.php', + 'PHPUnitPHAR\\PharIo\\Manifest\\Requirement' => '/phar-io-manifest/values/Requirement.php', + 'PHPUnitPHAR\\PharIo\\Manifest\\RequirementCollection' => '/phar-io-manifest/values/RequirementCollection.php', + 'PHPUnitPHAR\\PharIo\\Manifest\\RequirementCollectionIterator' => '/phar-io-manifest/values/RequirementCollectionIterator.php', + 'PHPUnitPHAR\\PharIo\\Manifest\\RequiresElement' => '/phar-io-manifest/xml/RequiresElement.php', + 'PHPUnitPHAR\\PharIo\\Manifest\\Type' => '/phar-io-manifest/values/Type.php', + 'PHPUnitPHAR\\PharIo\\Manifest\\Url' => '/phar-io-manifest/values/Url.php', + 'PHPUnitPHAR\\PharIo\\Version\\AbstractVersionConstraint' => '/phar-io-version/constraints/AbstractVersionConstraint.php', + 'PHPUnitPHAR\\PharIo\\Version\\AndVersionConstraintGroup' => '/phar-io-version/constraints/AndVersionConstraintGroup.php', + 'PHPUnitPHAR\\PharIo\\Version\\AnyVersionConstraint' => '/phar-io-version/constraints/AnyVersionConstraint.php', + 'PHPUnitPHAR\\PharIo\\Version\\BuildMetaData' => '/phar-io-version/BuildMetaData.php', + 'PHPUnitPHAR\\PharIo\\Version\\ExactVersionConstraint' => '/phar-io-version/constraints/ExactVersionConstraint.php', + 'PHPUnitPHAR\\PharIo\\Version\\Exception' => '/phar-io-version/exceptions/Exception.php', + 'PHPUnitPHAR\\PharIo\\Version\\GreaterThanOrEqualToVersionConstraint' => '/phar-io-version/constraints/GreaterThanOrEqualToVersionConstraint.php', + 'PHPUnitPHAR\\PharIo\\Version\\InvalidPreReleaseSuffixException' => '/phar-io-version/exceptions/InvalidPreReleaseSuffixException.php', + 'PHPUnitPHAR\\PharIo\\Version\\InvalidVersionException' => '/phar-io-version/exceptions/InvalidVersionException.php', + 'PHPUnitPHAR\\PharIo\\Version\\NoBuildMetaDataException' => '/phar-io-version/exceptions/NoBuildMetaDataException.php', + 'PHPUnitPHAR\\PharIo\\Version\\NoPreReleaseSuffixException' => '/phar-io-version/exceptions/NoPreReleaseSuffixException.php', + 'PHPUnitPHAR\\PharIo\\Version\\OrVersionConstraintGroup' => '/phar-io-version/constraints/OrVersionConstraintGroup.php', + 'PHPUnitPHAR\\PharIo\\Version\\PreReleaseSuffix' => '/phar-io-version/PreReleaseSuffix.php', + 'PHPUnitPHAR\\PharIo\\Version\\SpecificMajorAndMinorVersionConstraint' => '/phar-io-version/constraints/SpecificMajorAndMinorVersionConstraint.php', + 'PHPUnitPHAR\\PharIo\\Version\\SpecificMajorVersionConstraint' => '/phar-io-version/constraints/SpecificMajorVersionConstraint.php', + 'PHPUnitPHAR\\PharIo\\Version\\UnsupportedVersionConstraintException' => '/phar-io-version/exceptions/UnsupportedVersionConstraintException.php', + 'PHPUnitPHAR\\PharIo\\Version\\Version' => '/phar-io-version/Version.php', + 'PHPUnitPHAR\\PharIo\\Version\\VersionConstraint' => '/phar-io-version/constraints/VersionConstraint.php', + 'PHPUnitPHAR\\PharIo\\Version\\VersionConstraintParser' => '/phar-io-version/VersionConstraintParser.php', + 'PHPUnitPHAR\\PharIo\\Version\\VersionConstraintValue' => '/phar-io-version/VersionConstraintValue.php', + 'PHPUnitPHAR\\PharIo\\Version\\VersionNumber' => '/phar-io-version/VersionNumber.php', + 'PHPUnitPHAR\\PhpParser\\Builder' => '/nikic-php-parser/PhpParser/Builder.php', + 'PHPUnitPHAR\\PhpParser\\BuilderFactory' => '/nikic-php-parser/PhpParser/BuilderFactory.php', + 'PHPUnitPHAR\\PhpParser\\BuilderHelpers' => '/nikic-php-parser/PhpParser/BuilderHelpers.php', + 'PHPUnitPHAR\\PhpParser\\Builder\\ClassConst' => '/nikic-php-parser/PhpParser/Builder/ClassConst.php', + 'PHPUnitPHAR\\PhpParser\\Builder\\Class_' => '/nikic-php-parser/PhpParser/Builder/Class_.php', + 'PHPUnitPHAR\\PhpParser\\Builder\\Declaration' => '/nikic-php-parser/PhpParser/Builder/Declaration.php', + 'PHPUnitPHAR\\PhpParser\\Builder\\EnumCase' => '/nikic-php-parser/PhpParser/Builder/EnumCase.php', + 'PHPUnitPHAR\\PhpParser\\Builder\\Enum_' => '/nikic-php-parser/PhpParser/Builder/Enum_.php', + 'PHPUnitPHAR\\PhpParser\\Builder\\FunctionLike' => '/nikic-php-parser/PhpParser/Builder/FunctionLike.php', + 'PHPUnitPHAR\\PhpParser\\Builder\\Function_' => '/nikic-php-parser/PhpParser/Builder/Function_.php', + 'PHPUnitPHAR\\PhpParser\\Builder\\Interface_' => '/nikic-php-parser/PhpParser/Builder/Interface_.php', + 'PHPUnitPHAR\\PhpParser\\Builder\\Method' => '/nikic-php-parser/PhpParser/Builder/Method.php', + 'PHPUnitPHAR\\PhpParser\\Builder\\Namespace_' => '/nikic-php-parser/PhpParser/Builder/Namespace_.php', + 'PHPUnitPHAR\\PhpParser\\Builder\\Param' => '/nikic-php-parser/PhpParser/Builder/Param.php', + 'PHPUnitPHAR\\PhpParser\\Builder\\Property' => '/nikic-php-parser/PhpParser/Builder/Property.php', + 'PHPUnitPHAR\\PhpParser\\Builder\\TraitUse' => '/nikic-php-parser/PhpParser/Builder/TraitUse.php', + 'PHPUnitPHAR\\PhpParser\\Builder\\TraitUseAdaptation' => '/nikic-php-parser/PhpParser/Builder/TraitUseAdaptation.php', + 'PHPUnitPHAR\\PhpParser\\Builder\\Trait_' => '/nikic-php-parser/PhpParser/Builder/Trait_.php', + 'PHPUnitPHAR\\PhpParser\\Builder\\Use_' => '/nikic-php-parser/PhpParser/Builder/Use_.php', + 'PHPUnitPHAR\\PhpParser\\Comment' => '/nikic-php-parser/PhpParser/Comment.php', + 'PHPUnitPHAR\\PhpParser\\Comment\\Doc' => '/nikic-php-parser/PhpParser/Comment/Doc.php', + 'PHPUnitPHAR\\PhpParser\\ConstExprEvaluationException' => '/nikic-php-parser/PhpParser/ConstExprEvaluationException.php', + 'PHPUnitPHAR\\PhpParser\\ConstExprEvaluator' => '/nikic-php-parser/PhpParser/ConstExprEvaluator.php', + 'PHPUnitPHAR\\PhpParser\\Error' => '/nikic-php-parser/PhpParser/Error.php', + 'PHPUnitPHAR\\PhpParser\\ErrorHandler' => '/nikic-php-parser/PhpParser/ErrorHandler.php', + 'PHPUnitPHAR\\PhpParser\\ErrorHandler\\Collecting' => '/nikic-php-parser/PhpParser/ErrorHandler/Collecting.php', + 'PHPUnitPHAR\\PhpParser\\ErrorHandler\\Throwing' => '/nikic-php-parser/PhpParser/ErrorHandler/Throwing.php', + 'PHPUnitPHAR\\PhpParser\\Internal\\DiffElem' => '/nikic-php-parser/PhpParser/Internal/DiffElem.php', + 'PHPUnitPHAR\\PhpParser\\Internal\\Differ' => '/nikic-php-parser/PhpParser/Internal/Differ.php', + 'PHPUnitPHAR\\PhpParser\\Internal\\PrintableNewAnonClassNode' => '/nikic-php-parser/PhpParser/Internal/PrintableNewAnonClassNode.php', + 'PHPUnitPHAR\\PhpParser\\Internal\\TokenStream' => '/nikic-php-parser/PhpParser/Internal/TokenStream.php', + 'PHPUnitPHAR\\PhpParser\\JsonDecoder' => '/nikic-php-parser/PhpParser/JsonDecoder.php', + 'PHPUnitPHAR\\PhpParser\\Lexer' => '/nikic-php-parser/PhpParser/Lexer.php', + 'PHPUnitPHAR\\PhpParser\\Lexer\\Emulative' => '/nikic-php-parser/PhpParser/Lexer/Emulative.php', + 'PHPUnitPHAR\\PhpParser\\Lexer\\TokenEmulator\\AttributeEmulator' => '/nikic-php-parser/PhpParser/Lexer/TokenEmulator/AttributeEmulator.php', + 'PHPUnitPHAR\\PhpParser\\Lexer\\TokenEmulator\\CoaleseEqualTokenEmulator' => '/nikic-php-parser/PhpParser/Lexer/TokenEmulator/CoaleseEqualTokenEmulator.php', + 'PHPUnitPHAR\\PhpParser\\Lexer\\TokenEmulator\\EnumTokenEmulator' => '/nikic-php-parser/PhpParser/Lexer/TokenEmulator/EnumTokenEmulator.php', + 'PHPUnitPHAR\\PhpParser\\Lexer\\TokenEmulator\\ExplicitOctalEmulator' => '/nikic-php-parser/PhpParser/Lexer/TokenEmulator/ExplicitOctalEmulator.php', + 'PHPUnitPHAR\\PhpParser\\Lexer\\TokenEmulator\\FlexibleDocStringEmulator' => '/nikic-php-parser/PhpParser/Lexer/TokenEmulator/FlexibleDocStringEmulator.php', + 'PHPUnitPHAR\\PhpParser\\Lexer\\TokenEmulator\\FnTokenEmulator' => '/nikic-php-parser/PhpParser/Lexer/TokenEmulator/FnTokenEmulator.php', + 'PHPUnitPHAR\\PhpParser\\Lexer\\TokenEmulator\\KeywordEmulator' => '/nikic-php-parser/PhpParser/Lexer/TokenEmulator/KeywordEmulator.php', + 'PHPUnitPHAR\\PhpParser\\Lexer\\TokenEmulator\\MatchTokenEmulator' => '/nikic-php-parser/PhpParser/Lexer/TokenEmulator/MatchTokenEmulator.php', + 'PHPUnitPHAR\\PhpParser\\Lexer\\TokenEmulator\\NullsafeTokenEmulator' => '/nikic-php-parser/PhpParser/Lexer/TokenEmulator/NullsafeTokenEmulator.php', + 'PHPUnitPHAR\\PhpParser\\Lexer\\TokenEmulator\\NumericLiteralSeparatorEmulator' => '/nikic-php-parser/PhpParser/Lexer/TokenEmulator/NumericLiteralSeparatorEmulator.php', + 'PHPUnitPHAR\\PhpParser\\Lexer\\TokenEmulator\\ReadonlyFunctionTokenEmulator' => '/nikic-php-parser/PhpParser/Lexer/TokenEmulator/ReadonlyFunctionTokenEmulator.php', + 'PHPUnitPHAR\\PhpParser\\Lexer\\TokenEmulator\\ReadonlyTokenEmulator' => '/nikic-php-parser/PhpParser/Lexer/TokenEmulator/ReadonlyTokenEmulator.php', + 'PHPUnitPHAR\\PhpParser\\Lexer\\TokenEmulator\\ReverseEmulator' => '/nikic-php-parser/PhpParser/Lexer/TokenEmulator/ReverseEmulator.php', + 'PHPUnitPHAR\\PhpParser\\Lexer\\TokenEmulator\\TokenEmulator' => '/nikic-php-parser/PhpParser/Lexer/TokenEmulator/TokenEmulator.php', + 'PHPUnitPHAR\\PhpParser\\NameContext' => '/nikic-php-parser/PhpParser/NameContext.php', + 'PHPUnitPHAR\\PhpParser\\Node' => '/nikic-php-parser/PhpParser/Node.php', + 'PHPUnitPHAR\\PhpParser\\NodeAbstract' => '/nikic-php-parser/PhpParser/NodeAbstract.php', + 'PHPUnitPHAR\\PhpParser\\NodeDumper' => '/nikic-php-parser/PhpParser/NodeDumper.php', + 'PHPUnitPHAR\\PhpParser\\NodeFinder' => '/nikic-php-parser/PhpParser/NodeFinder.php', + 'PHPUnitPHAR\\PhpParser\\NodeTraverser' => '/nikic-php-parser/PhpParser/NodeTraverser.php', + 'PHPUnitPHAR\\PhpParser\\NodeTraverserInterface' => '/nikic-php-parser/PhpParser/NodeTraverserInterface.php', + 'PHPUnitPHAR\\PhpParser\\NodeVisitor' => '/nikic-php-parser/PhpParser/NodeVisitor.php', + 'PHPUnitPHAR\\PhpParser\\NodeVisitorAbstract' => '/nikic-php-parser/PhpParser/NodeVisitorAbstract.php', + 'PHPUnitPHAR\\PhpParser\\NodeVisitor\\CloningVisitor' => '/nikic-php-parser/PhpParser/NodeVisitor/CloningVisitor.php', + 'PHPUnitPHAR\\PhpParser\\NodeVisitor\\FindingVisitor' => '/nikic-php-parser/PhpParser/NodeVisitor/FindingVisitor.php', + 'PHPUnitPHAR\\PhpParser\\NodeVisitor\\FirstFindingVisitor' => '/nikic-php-parser/PhpParser/NodeVisitor/FirstFindingVisitor.php', + 'PHPUnitPHAR\\PhpParser\\NodeVisitor\\NameResolver' => '/nikic-php-parser/PhpParser/NodeVisitor/NameResolver.php', + 'PHPUnitPHAR\\PhpParser\\NodeVisitor\\NodeConnectingVisitor' => '/nikic-php-parser/PhpParser/NodeVisitor/NodeConnectingVisitor.php', + 'PHPUnitPHAR\\PhpParser\\NodeVisitor\\ParentConnectingVisitor' => '/nikic-php-parser/PhpParser/NodeVisitor/ParentConnectingVisitor.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Arg' => '/nikic-php-parser/PhpParser/Node/Arg.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Attribute' => '/nikic-php-parser/PhpParser/Node/Attribute.php', + 'PHPUnitPHAR\\PhpParser\\Node\\AttributeGroup' => '/nikic-php-parser/PhpParser/Node/AttributeGroup.php', + 'PHPUnitPHAR\\PhpParser\\Node\\ComplexType' => '/nikic-php-parser/PhpParser/Node/ComplexType.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Const_' => '/nikic-php-parser/PhpParser/Node/Const_.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr' => '/nikic-php-parser/PhpParser/Node/Expr.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\ArrayDimFetch' => '/nikic-php-parser/PhpParser/Node/Expr/ArrayDimFetch.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\ArrayItem' => '/nikic-php-parser/PhpParser/Node/Expr/ArrayItem.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\Array_' => '/nikic-php-parser/PhpParser/Node/Expr/Array_.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\ArrowFunction' => '/nikic-php-parser/PhpParser/Node/Expr/ArrowFunction.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\Assign' => '/nikic-php-parser/PhpParser/Node/Expr/Assign.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\AssignOp' => '/nikic-php-parser/PhpParser/Node/Expr/AssignOp.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\AssignOp\\BitwiseAnd' => '/nikic-php-parser/PhpParser/Node/Expr/AssignOp/BitwiseAnd.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\AssignOp\\BitwiseOr' => '/nikic-php-parser/PhpParser/Node/Expr/AssignOp/BitwiseOr.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\AssignOp\\BitwiseXor' => '/nikic-php-parser/PhpParser/Node/Expr/AssignOp/BitwiseXor.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\AssignOp\\Coalesce' => '/nikic-php-parser/PhpParser/Node/Expr/AssignOp/Coalesce.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\AssignOp\\Concat' => '/nikic-php-parser/PhpParser/Node/Expr/AssignOp/Concat.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\AssignOp\\Div' => '/nikic-php-parser/PhpParser/Node/Expr/AssignOp/Div.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\AssignOp\\Minus' => '/nikic-php-parser/PhpParser/Node/Expr/AssignOp/Minus.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\AssignOp\\Mod' => '/nikic-php-parser/PhpParser/Node/Expr/AssignOp/Mod.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\AssignOp\\Mul' => '/nikic-php-parser/PhpParser/Node/Expr/AssignOp/Mul.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\AssignOp\\Plus' => '/nikic-php-parser/PhpParser/Node/Expr/AssignOp/Plus.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\AssignOp\\Pow' => '/nikic-php-parser/PhpParser/Node/Expr/AssignOp/Pow.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\AssignOp\\ShiftLeft' => '/nikic-php-parser/PhpParser/Node/Expr/AssignOp/ShiftLeft.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\AssignOp\\ShiftRight' => '/nikic-php-parser/PhpParser/Node/Expr/AssignOp/ShiftRight.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\AssignRef' => '/nikic-php-parser/PhpParser/Node/Expr/AssignRef.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\BinaryOp' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\BinaryOp\\BitwiseAnd' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/BitwiseAnd.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\BinaryOp\\BitwiseOr' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/BitwiseOr.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\BinaryOp\\BitwiseXor' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/BitwiseXor.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\BinaryOp\\BooleanAnd' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/BooleanAnd.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\BinaryOp\\BooleanOr' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/BooleanOr.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\BinaryOp\\Coalesce' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Coalesce.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\BinaryOp\\Concat' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Concat.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\BinaryOp\\Div' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Div.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\BinaryOp\\Equal' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Equal.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\BinaryOp\\Greater' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Greater.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\BinaryOp\\GreaterOrEqual' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/GreaterOrEqual.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\BinaryOp\\Identical' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Identical.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\BinaryOp\\LogicalAnd' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/LogicalAnd.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\BinaryOp\\LogicalOr' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/LogicalOr.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\BinaryOp\\LogicalXor' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/LogicalXor.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\BinaryOp\\Minus' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Minus.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\BinaryOp\\Mod' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Mod.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\BinaryOp\\Mul' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Mul.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\BinaryOp\\NotEqual' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/NotEqual.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\BinaryOp\\NotIdentical' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/NotIdentical.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\BinaryOp\\Plus' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Plus.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\BinaryOp\\Pow' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Pow.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\BinaryOp\\ShiftLeft' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/ShiftLeft.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\BinaryOp\\ShiftRight' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/ShiftRight.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\BinaryOp\\Smaller' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Smaller.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\BinaryOp\\SmallerOrEqual' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/SmallerOrEqual.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\BinaryOp\\Spaceship' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Spaceship.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\BitwiseNot' => '/nikic-php-parser/PhpParser/Node/Expr/BitwiseNot.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\BooleanNot' => '/nikic-php-parser/PhpParser/Node/Expr/BooleanNot.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\CallLike' => '/nikic-php-parser/PhpParser/Node/Expr/CallLike.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\Cast' => '/nikic-php-parser/PhpParser/Node/Expr/Cast.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\Cast\\Array_' => '/nikic-php-parser/PhpParser/Node/Expr/Cast/Array_.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\Cast\\Bool_' => '/nikic-php-parser/PhpParser/Node/Expr/Cast/Bool_.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\Cast\\Double' => '/nikic-php-parser/PhpParser/Node/Expr/Cast/Double.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\Cast\\Int_' => '/nikic-php-parser/PhpParser/Node/Expr/Cast/Int_.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\Cast\\Object_' => '/nikic-php-parser/PhpParser/Node/Expr/Cast/Object_.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\Cast\\String_' => '/nikic-php-parser/PhpParser/Node/Expr/Cast/String_.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\Cast\\Unset_' => '/nikic-php-parser/PhpParser/Node/Expr/Cast/Unset_.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\ClassConstFetch' => '/nikic-php-parser/PhpParser/Node/Expr/ClassConstFetch.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\Clone_' => '/nikic-php-parser/PhpParser/Node/Expr/Clone_.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\Closure' => '/nikic-php-parser/PhpParser/Node/Expr/Closure.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\ClosureUse' => '/nikic-php-parser/PhpParser/Node/Expr/ClosureUse.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\ConstFetch' => '/nikic-php-parser/PhpParser/Node/Expr/ConstFetch.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\Empty_' => '/nikic-php-parser/PhpParser/Node/Expr/Empty_.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\Error' => '/nikic-php-parser/PhpParser/Node/Expr/Error.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\ErrorSuppress' => '/nikic-php-parser/PhpParser/Node/Expr/ErrorSuppress.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\Eval_' => '/nikic-php-parser/PhpParser/Node/Expr/Eval_.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\Exit_' => '/nikic-php-parser/PhpParser/Node/Expr/Exit_.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\FuncCall' => '/nikic-php-parser/PhpParser/Node/Expr/FuncCall.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\Include_' => '/nikic-php-parser/PhpParser/Node/Expr/Include_.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\Instanceof_' => '/nikic-php-parser/PhpParser/Node/Expr/Instanceof_.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\Isset_' => '/nikic-php-parser/PhpParser/Node/Expr/Isset_.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\List_' => '/nikic-php-parser/PhpParser/Node/Expr/List_.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\Match_' => '/nikic-php-parser/PhpParser/Node/Expr/Match_.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\MethodCall' => '/nikic-php-parser/PhpParser/Node/Expr/MethodCall.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\New_' => '/nikic-php-parser/PhpParser/Node/Expr/New_.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\NullsafeMethodCall' => '/nikic-php-parser/PhpParser/Node/Expr/NullsafeMethodCall.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\NullsafePropertyFetch' => '/nikic-php-parser/PhpParser/Node/Expr/NullsafePropertyFetch.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\PostDec' => '/nikic-php-parser/PhpParser/Node/Expr/PostDec.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\PostInc' => '/nikic-php-parser/PhpParser/Node/Expr/PostInc.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\PreDec' => '/nikic-php-parser/PhpParser/Node/Expr/PreDec.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\PreInc' => '/nikic-php-parser/PhpParser/Node/Expr/PreInc.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\Print_' => '/nikic-php-parser/PhpParser/Node/Expr/Print_.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\PropertyFetch' => '/nikic-php-parser/PhpParser/Node/Expr/PropertyFetch.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\ShellExec' => '/nikic-php-parser/PhpParser/Node/Expr/ShellExec.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\StaticCall' => '/nikic-php-parser/PhpParser/Node/Expr/StaticCall.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\StaticPropertyFetch' => '/nikic-php-parser/PhpParser/Node/Expr/StaticPropertyFetch.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\Ternary' => '/nikic-php-parser/PhpParser/Node/Expr/Ternary.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\Throw_' => '/nikic-php-parser/PhpParser/Node/Expr/Throw_.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\UnaryMinus' => '/nikic-php-parser/PhpParser/Node/Expr/UnaryMinus.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\UnaryPlus' => '/nikic-php-parser/PhpParser/Node/Expr/UnaryPlus.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\Variable' => '/nikic-php-parser/PhpParser/Node/Expr/Variable.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\YieldFrom' => '/nikic-php-parser/PhpParser/Node/Expr/YieldFrom.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\Yield_' => '/nikic-php-parser/PhpParser/Node/Expr/Yield_.php', + 'PHPUnitPHAR\\PhpParser\\Node\\FunctionLike' => '/nikic-php-parser/PhpParser/Node/FunctionLike.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Identifier' => '/nikic-php-parser/PhpParser/Node/Identifier.php', + 'PHPUnitPHAR\\PhpParser\\Node\\IntersectionType' => '/nikic-php-parser/PhpParser/Node/IntersectionType.php', + 'PHPUnitPHAR\\PhpParser\\Node\\MatchArm' => '/nikic-php-parser/PhpParser/Node/MatchArm.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Name' => '/nikic-php-parser/PhpParser/Node/Name.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Name\\FullyQualified' => '/nikic-php-parser/PhpParser/Node/Name/FullyQualified.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Name\\Relative' => '/nikic-php-parser/PhpParser/Node/Name/Relative.php', + 'PHPUnitPHAR\\PhpParser\\Node\\NullableType' => '/nikic-php-parser/PhpParser/Node/NullableType.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Param' => '/nikic-php-parser/PhpParser/Node/Param.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Scalar' => '/nikic-php-parser/PhpParser/Node/Scalar.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Scalar\\DNumber' => '/nikic-php-parser/PhpParser/Node/Scalar/DNumber.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Scalar\\Encapsed' => '/nikic-php-parser/PhpParser/Node/Scalar/Encapsed.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Scalar\\EncapsedStringPart' => '/nikic-php-parser/PhpParser/Node/Scalar/EncapsedStringPart.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Scalar\\LNumber' => '/nikic-php-parser/PhpParser/Node/Scalar/LNumber.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Scalar\\MagicConst' => '/nikic-php-parser/PhpParser/Node/Scalar/MagicConst.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Scalar\\MagicConst\\Class_' => '/nikic-php-parser/PhpParser/Node/Scalar/MagicConst/Class_.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Scalar\\MagicConst\\Dir' => '/nikic-php-parser/PhpParser/Node/Scalar/MagicConst/Dir.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Scalar\\MagicConst\\File' => '/nikic-php-parser/PhpParser/Node/Scalar/MagicConst/File.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Scalar\\MagicConst\\Function_' => '/nikic-php-parser/PhpParser/Node/Scalar/MagicConst/Function_.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Scalar\\MagicConst\\Line' => '/nikic-php-parser/PhpParser/Node/Scalar/MagicConst/Line.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Scalar\\MagicConst\\Method' => '/nikic-php-parser/PhpParser/Node/Scalar/MagicConst/Method.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Scalar\\MagicConst\\Namespace_' => '/nikic-php-parser/PhpParser/Node/Scalar/MagicConst/Namespace_.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Scalar\\MagicConst\\Trait_' => '/nikic-php-parser/PhpParser/Node/Scalar/MagicConst/Trait_.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Scalar\\String_' => '/nikic-php-parser/PhpParser/Node/Scalar/String_.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Stmt' => '/nikic-php-parser/PhpParser/Node/Stmt.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\Break_' => '/nikic-php-parser/PhpParser/Node/Stmt/Break_.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\Case_' => '/nikic-php-parser/PhpParser/Node/Stmt/Case_.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\Catch_' => '/nikic-php-parser/PhpParser/Node/Stmt/Catch_.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\ClassConst' => '/nikic-php-parser/PhpParser/Node/Stmt/ClassConst.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\ClassLike' => '/nikic-php-parser/PhpParser/Node/Stmt/ClassLike.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\ClassMethod' => '/nikic-php-parser/PhpParser/Node/Stmt/ClassMethod.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\Class_' => '/nikic-php-parser/PhpParser/Node/Stmt/Class_.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\Const_' => '/nikic-php-parser/PhpParser/Node/Stmt/Const_.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\Continue_' => '/nikic-php-parser/PhpParser/Node/Stmt/Continue_.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\DeclareDeclare' => '/nikic-php-parser/PhpParser/Node/Stmt/DeclareDeclare.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\Declare_' => '/nikic-php-parser/PhpParser/Node/Stmt/Declare_.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\Do_' => '/nikic-php-parser/PhpParser/Node/Stmt/Do_.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\Echo_' => '/nikic-php-parser/PhpParser/Node/Stmt/Echo_.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\ElseIf_' => '/nikic-php-parser/PhpParser/Node/Stmt/ElseIf_.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\Else_' => '/nikic-php-parser/PhpParser/Node/Stmt/Else_.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\EnumCase' => '/nikic-php-parser/PhpParser/Node/Stmt/EnumCase.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\Enum_' => '/nikic-php-parser/PhpParser/Node/Stmt/Enum_.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\Expression' => '/nikic-php-parser/PhpParser/Node/Stmt/Expression.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\Finally_' => '/nikic-php-parser/PhpParser/Node/Stmt/Finally_.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\For_' => '/nikic-php-parser/PhpParser/Node/Stmt/For_.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\Foreach_' => '/nikic-php-parser/PhpParser/Node/Stmt/Foreach_.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\Function_' => '/nikic-php-parser/PhpParser/Node/Stmt/Function_.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\Global_' => '/nikic-php-parser/PhpParser/Node/Stmt/Global_.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\Goto_' => '/nikic-php-parser/PhpParser/Node/Stmt/Goto_.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\GroupUse' => '/nikic-php-parser/PhpParser/Node/Stmt/GroupUse.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\HaltCompiler' => '/nikic-php-parser/PhpParser/Node/Stmt/HaltCompiler.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\If_' => '/nikic-php-parser/PhpParser/Node/Stmt/If_.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\InlineHTML' => '/nikic-php-parser/PhpParser/Node/Stmt/InlineHTML.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\Interface_' => '/nikic-php-parser/PhpParser/Node/Stmt/Interface_.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\Label' => '/nikic-php-parser/PhpParser/Node/Stmt/Label.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\Namespace_' => '/nikic-php-parser/PhpParser/Node/Stmt/Namespace_.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\Nop' => '/nikic-php-parser/PhpParser/Node/Stmt/Nop.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\Property' => '/nikic-php-parser/PhpParser/Node/Stmt/Property.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\PropertyProperty' => '/nikic-php-parser/PhpParser/Node/Stmt/PropertyProperty.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\Return_' => '/nikic-php-parser/PhpParser/Node/Stmt/Return_.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\StaticVar' => '/nikic-php-parser/PhpParser/Node/Stmt/StaticVar.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\Static_' => '/nikic-php-parser/PhpParser/Node/Stmt/Static_.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\Switch_' => '/nikic-php-parser/PhpParser/Node/Stmt/Switch_.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\Throw_' => '/nikic-php-parser/PhpParser/Node/Stmt/Throw_.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\TraitUse' => '/nikic-php-parser/PhpParser/Node/Stmt/TraitUse.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\TraitUseAdaptation' => '/nikic-php-parser/PhpParser/Node/Stmt/TraitUseAdaptation.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\TraitUseAdaptation\\Alias' => '/nikic-php-parser/PhpParser/Node/Stmt/TraitUseAdaptation/Alias.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\TraitUseAdaptation\\Precedence' => '/nikic-php-parser/PhpParser/Node/Stmt/TraitUseAdaptation/Precedence.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\Trait_' => '/nikic-php-parser/PhpParser/Node/Stmt/Trait_.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\TryCatch' => '/nikic-php-parser/PhpParser/Node/Stmt/TryCatch.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\Unset_' => '/nikic-php-parser/PhpParser/Node/Stmt/Unset_.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\UseUse' => '/nikic-php-parser/PhpParser/Node/Stmt/UseUse.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\Use_' => '/nikic-php-parser/PhpParser/Node/Stmt/Use_.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\While_' => '/nikic-php-parser/PhpParser/Node/Stmt/While_.php', + 'PHPUnitPHAR\\PhpParser\\Node\\UnionType' => '/nikic-php-parser/PhpParser/Node/UnionType.php', + 'PHPUnitPHAR\\PhpParser\\Node\\VarLikeIdentifier' => '/nikic-php-parser/PhpParser/Node/VarLikeIdentifier.php', + 'PHPUnitPHAR\\PhpParser\\Node\\VariadicPlaceholder' => '/nikic-php-parser/PhpParser/Node/VariadicPlaceholder.php', + 'PHPUnitPHAR\\PhpParser\\Parser' => '/nikic-php-parser/PhpParser/Parser.php', + 'PHPUnitPHAR\\PhpParser\\ParserAbstract' => '/nikic-php-parser/PhpParser/ParserAbstract.php', + 'PHPUnitPHAR\\PhpParser\\ParserFactory' => '/nikic-php-parser/PhpParser/ParserFactory.php', + 'PHPUnitPHAR\\PhpParser\\Parser\\Multiple' => '/nikic-php-parser/PhpParser/Parser/Multiple.php', + 'PHPUnitPHAR\\PhpParser\\Parser\\Php5' => '/nikic-php-parser/PhpParser/Parser/Php5.php', + 'PHPUnitPHAR\\PhpParser\\Parser\\Php7' => '/nikic-php-parser/PhpParser/Parser/Php7.php', + 'PHPUnitPHAR\\PhpParser\\Parser\\Tokens' => '/nikic-php-parser/PhpParser/Parser/Tokens.php', + 'PHPUnitPHAR\\PhpParser\\PrettyPrinterAbstract' => '/nikic-php-parser/PhpParser/PrettyPrinterAbstract.php', + 'PHPUnitPHAR\\PhpParser\\PrettyPrinter\\Standard' => '/nikic-php-parser/PhpParser/PrettyPrinter/Standard.php', + 'PHPUnitPHAR\\SebastianBergmann\\CliParser\\AmbiguousOptionException' => '/sebastian-cli-parser/exceptions/AmbiguousOptionException.php', + 'PHPUnitPHAR\\SebastianBergmann\\CliParser\\Exception' => '/sebastian-cli-parser/exceptions/Exception.php', + 'PHPUnitPHAR\\SebastianBergmann\\CliParser\\OptionDoesNotAllowArgumentException' => '/sebastian-cli-parser/exceptions/OptionDoesNotAllowArgumentException.php', + 'PHPUnitPHAR\\SebastianBergmann\\CliParser\\Parser' => '/sebastian-cli-parser/Parser.php', + 'PHPUnitPHAR\\SebastianBergmann\\CliParser\\RequiredOptionArgumentMissingException' => '/sebastian-cli-parser/exceptions/RequiredOptionArgumentMissingException.php', + 'PHPUnitPHAR\\SebastianBergmann\\CliParser\\UnknownOptionException' => '/sebastian-cli-parser/exceptions/UnknownOptionException.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\BranchAndPathCoverageNotSupportedException' => '/php-code-coverage/Exception/BranchAndPathCoverageNotSupportedException.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\CodeCoverage' => '/php-code-coverage/CodeCoverage.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\DeadCodeDetectionNotSupportedException' => '/php-code-coverage/Exception/DeadCodeDetectionNotSupportedException.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Driver\\Driver' => '/php-code-coverage/Driver/Driver.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Driver\\PathExistsButIsNotDirectoryException' => '/php-code-coverage/Exception/PathExistsButIsNotDirectoryException.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Driver\\PcovDriver' => '/php-code-coverage/Driver/PcovDriver.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Driver\\PcovNotAvailableException' => '/php-code-coverage/Exception/PcovNotAvailableException.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Driver\\PhpdbgDriver' => '/php-code-coverage/Driver/PhpdbgDriver.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Driver\\PhpdbgNotAvailableException' => '/php-code-coverage/Exception/PhpdbgNotAvailableException.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Driver\\Selector' => '/php-code-coverage/Driver/Selector.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Driver\\WriteOperationFailedException' => '/php-code-coverage/Exception/WriteOperationFailedException.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Driver\\WrongXdebugVersionException' => '/php-code-coverage/Exception/WrongXdebugVersionException.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Driver\\Xdebug2Driver' => '/php-code-coverage/Driver/Xdebug2Driver.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Driver\\Xdebug2NotEnabledException' => '/php-code-coverage/Exception/Xdebug2NotEnabledException.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Driver\\Xdebug3Driver' => '/php-code-coverage/Driver/Xdebug3Driver.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Driver\\Xdebug3NotEnabledException' => '/php-code-coverage/Exception/Xdebug3NotEnabledException.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Driver\\XdebugNotAvailableException' => '/php-code-coverage/Exception/XdebugNotAvailableException.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Exception' => '/php-code-coverage/Exception/Exception.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Filter' => '/php-code-coverage/Filter.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\InvalidArgumentException' => '/php-code-coverage/Exception/InvalidArgumentException.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\NoCodeCoverageDriverAvailableException' => '/php-code-coverage/Exception/NoCodeCoverageDriverAvailableException.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\NoCodeCoverageDriverWithPathCoverageSupportAvailableException' => '/php-code-coverage/Exception/NoCodeCoverageDriverWithPathCoverageSupportAvailableException.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Node\\AbstractNode' => '/php-code-coverage/Node/AbstractNode.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Node\\Builder' => '/php-code-coverage/Node/Builder.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Node\\CrapIndex' => '/php-code-coverage/Node/CrapIndex.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Node\\Directory' => '/php-code-coverage/Node/Directory.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Node\\File' => '/php-code-coverage/Node/File.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Node\\Iterator' => '/php-code-coverage/Node/Iterator.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\ParserException' => '/php-code-coverage/Exception/ParserException.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\ProcessedCodeCoverageData' => '/php-code-coverage/ProcessedCodeCoverageData.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\RawCodeCoverageData' => '/php-code-coverage/RawCodeCoverageData.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\ReflectionException' => '/php-code-coverage/Exception/ReflectionException.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\ReportAlreadyFinalizedException' => '/php-code-coverage/Exception/ReportAlreadyFinalizedException.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Report\\Clover' => '/php-code-coverage/Report/Clover.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Report\\Cobertura' => '/php-code-coverage/Report/Cobertura.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Report\\Crap4j' => '/php-code-coverage/Report/Crap4j.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Report\\Html\\Dashboard' => '/php-code-coverage/Report/Html/Renderer/Dashboard.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Report\\Html\\Directory' => '/php-code-coverage/Report/Html/Renderer/Directory.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Report\\Html\\Facade' => '/php-code-coverage/Report/Html/Facade.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Report\\Html\\File' => '/php-code-coverage/Report/Html/Renderer/File.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Report\\Html\\Renderer' => '/php-code-coverage/Report/Html/Renderer.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Report\\PHP' => '/php-code-coverage/Report/PHP.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Report\\Text' => '/php-code-coverage/Report/Text.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Report\\Xml\\BuildInformation' => '/php-code-coverage/Report/Xml/BuildInformation.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Report\\Xml\\Coverage' => '/php-code-coverage/Report/Xml/Coverage.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Report\\Xml\\Directory' => '/php-code-coverage/Report/Xml/Directory.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Report\\Xml\\Facade' => '/php-code-coverage/Report/Xml/Facade.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Report\\Xml\\File' => '/php-code-coverage/Report/Xml/File.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Report\\Xml\\Method' => '/php-code-coverage/Report/Xml/Method.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Report\\Xml\\Node' => '/php-code-coverage/Report/Xml/Node.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Report\\Xml\\Project' => '/php-code-coverage/Report/Xml/Project.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Report\\Xml\\Report' => '/php-code-coverage/Report/Xml/Report.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Report\\Xml\\Source' => '/php-code-coverage/Report/Xml/Source.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Report\\Xml\\Tests' => '/php-code-coverage/Report/Xml/Tests.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Report\\Xml\\Totals' => '/php-code-coverage/Report/Xml/Totals.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Report\\Xml\\Unit' => '/php-code-coverage/Report/Xml/Unit.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\StaticAnalysisCacheNotConfiguredException' => '/php-code-coverage/Exception/StaticAnalysisCacheNotConfiguredException.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\StaticAnalysis\\CacheWarmer' => '/php-code-coverage/StaticAnalysis/CacheWarmer.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\StaticAnalysis\\CachingFileAnalyser' => '/php-code-coverage/StaticAnalysis/CachingFileAnalyser.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\StaticAnalysis\\CodeUnitFindingVisitor' => '/php-code-coverage/StaticAnalysis/CodeUnitFindingVisitor.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\StaticAnalysis\\ExecutableLinesFindingVisitor' => '/php-code-coverage/StaticAnalysis/ExecutableLinesFindingVisitor.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\StaticAnalysis\\FileAnalyser' => '/php-code-coverage/StaticAnalysis/FileAnalyser.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\StaticAnalysis\\IgnoredLinesFindingVisitor' => '/php-code-coverage/StaticAnalysis/IgnoredLinesFindingVisitor.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\StaticAnalysis\\ParsingFileAnalyser' => '/php-code-coverage/StaticAnalysis/ParsingFileAnalyser.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\TestIdMissingException' => '/php-code-coverage/Exception/TestIdMissingException.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\UnintentionallyCoveredCodeException' => '/php-code-coverage/Exception/UnintentionallyCoveredCodeException.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Util\\DirectoryCouldNotBeCreatedException' => '/php-code-coverage/Exception/DirectoryCouldNotBeCreatedException.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Util\\Filesystem' => '/php-code-coverage/Util/Filesystem.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Util\\Percentage' => '/php-code-coverage/Util/Percentage.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Version' => '/php-code-coverage/Version.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\XmlException' => '/php-code-coverage/Exception/XmlException.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeUnitReverseLookup\\Wizard' => '/sebastian-code-unit-reverse-lookup/Wizard.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeUnit\\ClassMethodUnit' => '/sebastian-code-unit/ClassMethodUnit.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeUnit\\ClassUnit' => '/sebastian-code-unit/ClassUnit.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeUnit\\CodeUnit' => '/sebastian-code-unit/CodeUnit.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeUnit\\CodeUnitCollection' => '/sebastian-code-unit/CodeUnitCollection.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeUnit\\CodeUnitCollectionIterator' => '/sebastian-code-unit/CodeUnitCollectionIterator.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeUnit\\Exception' => '/sebastian-code-unit/exceptions/Exception.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeUnit\\FunctionUnit' => '/sebastian-code-unit/FunctionUnit.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeUnit\\InterfaceMethodUnit' => '/sebastian-code-unit/InterfaceMethodUnit.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeUnit\\InterfaceUnit' => '/sebastian-code-unit/InterfaceUnit.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeUnit\\InvalidCodeUnitException' => '/sebastian-code-unit/exceptions/InvalidCodeUnitException.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeUnit\\Mapper' => '/sebastian-code-unit/Mapper.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeUnit\\NoTraitException' => '/sebastian-code-unit/exceptions/NoTraitException.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeUnit\\ReflectionException' => '/sebastian-code-unit/exceptions/ReflectionException.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeUnit\\TraitMethodUnit' => '/sebastian-code-unit/TraitMethodUnit.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeUnit\\TraitUnit' => '/sebastian-code-unit/TraitUnit.php', + 'PHPUnitPHAR\\SebastianBergmann\\Comparator\\ArrayComparator' => '/sebastian-comparator/ArrayComparator.php', + 'PHPUnitPHAR\\SebastianBergmann\\Comparator\\Comparator' => '/sebastian-comparator/Comparator.php', + 'PHPUnitPHAR\\SebastianBergmann\\Comparator\\ComparisonFailure' => '/sebastian-comparator/ComparisonFailure.php', + 'PHPUnitPHAR\\SebastianBergmann\\Comparator\\DOMNodeComparator' => '/sebastian-comparator/DOMNodeComparator.php', + 'PHPUnitPHAR\\SebastianBergmann\\Comparator\\DateTimeComparator' => '/sebastian-comparator/DateTimeComparator.php', + 'PHPUnitPHAR\\SebastianBergmann\\Comparator\\DoubleComparator' => '/sebastian-comparator/DoubleComparator.php', + 'PHPUnitPHAR\\SebastianBergmann\\Comparator\\Exception' => '/sebastian-comparator/exceptions/Exception.php', + 'PHPUnitPHAR\\SebastianBergmann\\Comparator\\ExceptionComparator' => '/sebastian-comparator/ExceptionComparator.php', + 'PHPUnitPHAR\\SebastianBergmann\\Comparator\\Factory' => '/sebastian-comparator/Factory.php', + 'PHPUnitPHAR\\SebastianBergmann\\Comparator\\MockObjectComparator' => '/sebastian-comparator/MockObjectComparator.php', + 'PHPUnitPHAR\\SebastianBergmann\\Comparator\\NumericComparator' => '/sebastian-comparator/NumericComparator.php', + 'PHPUnitPHAR\\SebastianBergmann\\Comparator\\ObjectComparator' => '/sebastian-comparator/ObjectComparator.php', + 'PHPUnitPHAR\\SebastianBergmann\\Comparator\\ResourceComparator' => '/sebastian-comparator/ResourceComparator.php', + 'PHPUnitPHAR\\SebastianBergmann\\Comparator\\RuntimeException' => '/sebastian-comparator/exceptions/RuntimeException.php', + 'PHPUnitPHAR\\SebastianBergmann\\Comparator\\ScalarComparator' => '/sebastian-comparator/ScalarComparator.php', + 'PHPUnitPHAR\\SebastianBergmann\\Comparator\\SplObjectStorageComparator' => '/sebastian-comparator/SplObjectStorageComparator.php', + 'PHPUnitPHAR\\SebastianBergmann\\Comparator\\TypeComparator' => '/sebastian-comparator/TypeComparator.php', + 'PHPUnitPHAR\\SebastianBergmann\\Complexity\\Calculator' => '/sebastian-complexity/Calculator.php', + 'PHPUnitPHAR\\SebastianBergmann\\Complexity\\Complexity' => '/sebastian-complexity/Complexity/Complexity.php', + 'PHPUnitPHAR\\SebastianBergmann\\Complexity\\ComplexityCalculatingVisitor' => '/sebastian-complexity/Visitor/ComplexityCalculatingVisitor.php', + 'PHPUnitPHAR\\SebastianBergmann\\Complexity\\ComplexityCollection' => '/sebastian-complexity/Complexity/ComplexityCollection.php', + 'PHPUnitPHAR\\SebastianBergmann\\Complexity\\ComplexityCollectionIterator' => '/sebastian-complexity/Complexity/ComplexityCollectionIterator.php', + 'PHPUnitPHAR\\SebastianBergmann\\Complexity\\CyclomaticComplexityCalculatingVisitor' => '/sebastian-complexity/Visitor/CyclomaticComplexityCalculatingVisitor.php', + 'PHPUnitPHAR\\SebastianBergmann\\Complexity\\Exception' => '/sebastian-complexity/Exception/Exception.php', + 'PHPUnitPHAR\\SebastianBergmann\\Complexity\\RuntimeException' => '/sebastian-complexity/Exception/RuntimeException.php', + 'PHPUnitPHAR\\SebastianBergmann\\Diff\\Chunk' => '/sebastian-diff/Chunk.php', + 'PHPUnitPHAR\\SebastianBergmann\\Diff\\ConfigurationException' => '/sebastian-diff/Exception/ConfigurationException.php', + 'PHPUnitPHAR\\SebastianBergmann\\Diff\\Diff' => '/sebastian-diff/Diff.php', + 'PHPUnitPHAR\\SebastianBergmann\\Diff\\Differ' => '/sebastian-diff/Differ.php', + 'PHPUnitPHAR\\SebastianBergmann\\Diff\\Exception' => '/sebastian-diff/Exception/Exception.php', + 'PHPUnitPHAR\\SebastianBergmann\\Diff\\InvalidArgumentException' => '/sebastian-diff/Exception/InvalidArgumentException.php', + 'PHPUnitPHAR\\SebastianBergmann\\Diff\\Line' => '/sebastian-diff/Line.php', + 'PHPUnitPHAR\\SebastianBergmann\\Diff\\LongestCommonSubsequenceCalculator' => '/sebastian-diff/LongestCommonSubsequenceCalculator.php', + 'PHPUnitPHAR\\SebastianBergmann\\Diff\\MemoryEfficientLongestCommonSubsequenceCalculator' => '/sebastian-diff/MemoryEfficientLongestCommonSubsequenceCalculator.php', + 'PHPUnitPHAR\\SebastianBergmann\\Diff\\Output\\AbstractChunkOutputBuilder' => '/sebastian-diff/Output/AbstractChunkOutputBuilder.php', + 'PHPUnitPHAR\\SebastianBergmann\\Diff\\Output\\DiffOnlyOutputBuilder' => '/sebastian-diff/Output/DiffOnlyOutputBuilder.php', + 'PHPUnitPHAR\\SebastianBergmann\\Diff\\Output\\DiffOutputBuilderInterface' => '/sebastian-diff/Output/DiffOutputBuilderInterface.php', + 'PHPUnitPHAR\\SebastianBergmann\\Diff\\Output\\StrictUnifiedDiffOutputBuilder' => '/sebastian-diff/Output/StrictUnifiedDiffOutputBuilder.php', + 'PHPUnitPHAR\\SebastianBergmann\\Diff\\Output\\UnifiedDiffOutputBuilder' => '/sebastian-diff/Output/UnifiedDiffOutputBuilder.php', + 'PHPUnitPHAR\\SebastianBergmann\\Diff\\Parser' => '/sebastian-diff/Parser.php', + 'PHPUnitPHAR\\SebastianBergmann\\Diff\\TimeEfficientLongestCommonSubsequenceCalculator' => '/sebastian-diff/TimeEfficientLongestCommonSubsequenceCalculator.php', + 'PHPUnitPHAR\\SebastianBergmann\\Environment\\Console' => '/sebastian-environment/Console.php', + 'PHPUnitPHAR\\SebastianBergmann\\Environment\\OperatingSystem' => '/sebastian-environment/OperatingSystem.php', + 'PHPUnitPHAR\\SebastianBergmann\\Environment\\Runtime' => '/sebastian-environment/Runtime.php', + 'PHPUnitPHAR\\SebastianBergmann\\Exporter\\Exporter' => '/sebastian-exporter/Exporter.php', + 'PHPUnitPHAR\\SebastianBergmann\\FileIterator\\Facade' => '/php-file-iterator/Facade.php', + 'PHPUnitPHAR\\SebastianBergmann\\FileIterator\\Factory' => '/php-file-iterator/Factory.php', + 'PHPUnitPHAR\\SebastianBergmann\\FileIterator\\Iterator' => '/php-file-iterator/Iterator.php', + 'PHPUnitPHAR\\SebastianBergmann\\GlobalState\\CodeExporter' => '/sebastian-global-state/CodeExporter.php', + 'PHPUnitPHAR\\SebastianBergmann\\GlobalState\\Exception' => '/sebastian-global-state/exceptions/Exception.php', + 'PHPUnitPHAR\\SebastianBergmann\\GlobalState\\ExcludeList' => '/sebastian-global-state/ExcludeList.php', + 'PHPUnitPHAR\\SebastianBergmann\\GlobalState\\Restorer' => '/sebastian-global-state/Restorer.php', + 'PHPUnitPHAR\\SebastianBergmann\\GlobalState\\RuntimeException' => '/sebastian-global-state/exceptions/RuntimeException.php', + 'PHPUnitPHAR\\SebastianBergmann\\GlobalState\\Snapshot' => '/sebastian-global-state/Snapshot.php', + 'PHPUnitPHAR\\SebastianBergmann\\Invoker\\Exception' => '/php-invoker/exceptions/Exception.php', + 'PHPUnitPHAR\\SebastianBergmann\\Invoker\\Invoker' => '/php-invoker/Invoker.php', + 'PHPUnitPHAR\\SebastianBergmann\\Invoker\\ProcessControlExtensionNotLoadedException' => '/php-invoker/exceptions/ProcessControlExtensionNotLoadedException.php', + 'PHPUnitPHAR\\SebastianBergmann\\Invoker\\TimeoutException' => '/php-invoker/exceptions/TimeoutException.php', + 'PHPUnitPHAR\\SebastianBergmann\\LinesOfCode\\Counter' => '/sebastian-lines-of-code/Counter.php', + 'PHPUnitPHAR\\SebastianBergmann\\LinesOfCode\\Exception' => '/sebastian-lines-of-code/Exception/Exception.php', + 'PHPUnitPHAR\\SebastianBergmann\\LinesOfCode\\IllogicalValuesException' => '/sebastian-lines-of-code/Exception/IllogicalValuesException.php', + 'PHPUnitPHAR\\SebastianBergmann\\LinesOfCode\\LineCountingVisitor' => '/sebastian-lines-of-code/LineCountingVisitor.php', + 'PHPUnitPHAR\\SebastianBergmann\\LinesOfCode\\LinesOfCode' => '/sebastian-lines-of-code/LinesOfCode.php', + 'PHPUnitPHAR\\SebastianBergmann\\LinesOfCode\\NegativeValueException' => '/sebastian-lines-of-code/Exception/NegativeValueException.php', + 'PHPUnitPHAR\\SebastianBergmann\\LinesOfCode\\RuntimeException' => '/sebastian-lines-of-code/Exception/RuntimeException.php', + 'PHPUnitPHAR\\SebastianBergmann\\ObjectEnumerator\\Enumerator' => '/sebastian-object-enumerator/Enumerator.php', + 'PHPUnitPHAR\\SebastianBergmann\\ObjectEnumerator\\Exception' => '/sebastian-object-enumerator/Exception.php', + 'PHPUnitPHAR\\SebastianBergmann\\ObjectEnumerator\\InvalidArgumentException' => '/sebastian-object-enumerator/InvalidArgumentException.php', + 'PHPUnitPHAR\\SebastianBergmann\\ObjectReflector\\Exception' => '/sebastian-object-reflector/Exception.php', + 'PHPUnitPHAR\\SebastianBergmann\\ObjectReflector\\InvalidArgumentException' => '/sebastian-object-reflector/InvalidArgumentException.php', + 'PHPUnitPHAR\\SebastianBergmann\\ObjectReflector\\ObjectReflector' => '/sebastian-object-reflector/ObjectReflector.php', + 'PHPUnitPHAR\\SebastianBergmann\\RecursionContext\\Context' => '/sebastian-recursion-context/Context.php', + 'PHPUnitPHAR\\SebastianBergmann\\RecursionContext\\Exception' => '/sebastian-recursion-context/Exception.php', + 'PHPUnitPHAR\\SebastianBergmann\\RecursionContext\\InvalidArgumentException' => '/sebastian-recursion-context/InvalidArgumentException.php', + 'PHPUnitPHAR\\SebastianBergmann\\ResourceOperations\\ResourceOperations' => '/sebastian-resource-operations/ResourceOperations.php', + 'PHPUnitPHAR\\SebastianBergmann\\Template\\Exception' => '/php-text-template/exceptions/Exception.php', + 'PHPUnitPHAR\\SebastianBergmann\\Template\\InvalidArgumentException' => '/php-text-template/exceptions/InvalidArgumentException.php', + 'PHPUnitPHAR\\SebastianBergmann\\Template\\RuntimeException' => '/php-text-template/exceptions/RuntimeException.php', + 'PHPUnitPHAR\\SebastianBergmann\\Template\\Template' => '/php-text-template/Template.php', + 'PHPUnitPHAR\\SebastianBergmann\\Timer\\Duration' => '/php-timer/Duration.php', + 'PHPUnitPHAR\\SebastianBergmann\\Timer\\Exception' => '/php-timer/exceptions/Exception.php', + 'PHPUnitPHAR\\SebastianBergmann\\Timer\\NoActiveTimerException' => '/php-timer/exceptions/NoActiveTimerException.php', + 'PHPUnitPHAR\\SebastianBergmann\\Timer\\ResourceUsageFormatter' => '/php-timer/ResourceUsageFormatter.php', + 'PHPUnitPHAR\\SebastianBergmann\\Timer\\TimeSinceStartOfRequestNotAvailableException' => '/php-timer/exceptions/TimeSinceStartOfRequestNotAvailableException.php', + 'PHPUnitPHAR\\SebastianBergmann\\Timer\\Timer' => '/php-timer/Timer.php', + 'PHPUnitPHAR\\SebastianBergmann\\Type\\CallableType' => '/sebastian-type/type/CallableType.php', + 'PHPUnitPHAR\\SebastianBergmann\\Type\\Exception' => '/sebastian-type/exception/Exception.php', + 'PHPUnitPHAR\\SebastianBergmann\\Type\\FalseType' => '/sebastian-type/type/FalseType.php', + 'PHPUnitPHAR\\SebastianBergmann\\Type\\GenericObjectType' => '/sebastian-type/type/GenericObjectType.php', + 'PHPUnitPHAR\\SebastianBergmann\\Type\\IntersectionType' => '/sebastian-type/type/IntersectionType.php', + 'PHPUnitPHAR\\SebastianBergmann\\Type\\IterableType' => '/sebastian-type/type/IterableType.php', + 'PHPUnitPHAR\\SebastianBergmann\\Type\\MixedType' => '/sebastian-type/type/MixedType.php', + 'PHPUnitPHAR\\SebastianBergmann\\Type\\NeverType' => '/sebastian-type/type/NeverType.php', + 'PHPUnitPHAR\\SebastianBergmann\\Type\\NullType' => '/sebastian-type/type/NullType.php', + 'PHPUnitPHAR\\SebastianBergmann\\Type\\ObjectType' => '/sebastian-type/type/ObjectType.php', + 'PHPUnitPHAR\\SebastianBergmann\\Type\\Parameter' => '/sebastian-type/Parameter.php', + 'PHPUnitPHAR\\SebastianBergmann\\Type\\ReflectionMapper' => '/sebastian-type/ReflectionMapper.php', + 'PHPUnitPHAR\\SebastianBergmann\\Type\\RuntimeException' => '/sebastian-type/exception/RuntimeException.php', + 'PHPUnitPHAR\\SebastianBergmann\\Type\\SimpleType' => '/sebastian-type/type/SimpleType.php', + 'PHPUnitPHAR\\SebastianBergmann\\Type\\StaticType' => '/sebastian-type/type/StaticType.php', + 'PHPUnitPHAR\\SebastianBergmann\\Type\\TrueType' => '/sebastian-type/type/TrueType.php', + 'PHPUnitPHAR\\SebastianBergmann\\Type\\Type' => '/sebastian-type/type/Type.php', + 'PHPUnitPHAR\\SebastianBergmann\\Type\\TypeName' => '/sebastian-type/TypeName.php', + 'PHPUnitPHAR\\SebastianBergmann\\Type\\UnionType' => '/sebastian-type/type/UnionType.php', + 'PHPUnitPHAR\\SebastianBergmann\\Type\\UnknownType' => '/sebastian-type/type/UnknownType.php', + 'PHPUnitPHAR\\SebastianBergmann\\Type\\VoidType' => '/sebastian-type/type/VoidType.php', + 'PHPUnitPHAR\\SebastianBergmann\\Version' => '/sebastian-version/Version.php', + 'PHPUnitPHAR\\TheSeer\\Tokenizer\\Exception' => '/theseer-tokenizer/Exception.php', + 'PHPUnitPHAR\\TheSeer\\Tokenizer\\NamespaceUri' => '/theseer-tokenizer/NamespaceUri.php', + 'PHPUnitPHAR\\TheSeer\\Tokenizer\\NamespaceUriException' => '/theseer-tokenizer/NamespaceUriException.php', + 'PHPUnitPHAR\\TheSeer\\Tokenizer\\Token' => '/theseer-tokenizer/Token.php', + 'PHPUnitPHAR\\TheSeer\\Tokenizer\\TokenCollection' => '/theseer-tokenizer/TokenCollection.php', + 'PHPUnitPHAR\\TheSeer\\Tokenizer\\TokenCollectionException' => '/theseer-tokenizer/TokenCollectionException.php', + 'PHPUnitPHAR\\TheSeer\\Tokenizer\\Tokenizer' => '/theseer-tokenizer/Tokenizer.php', + 'PHPUnitPHAR\\TheSeer\\Tokenizer\\XMLSerializer' => '/theseer-tokenizer/XMLSerializer.php', + 'PHPUnitPHAR\\Webmozart\\Assert\\Assert' => '/webmozart-assert/Assert.php', + 'PHPUnitPHAR\\Webmozart\\Assert\\InvalidArgumentException' => '/webmozart-assert/InvalidArgumentException.php', + 'PHPUnitPHAR\\Webmozart\\Assert\\Mixin' => '/webmozart-assert/Mixin.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock' => '/phpdocumentor-reflection-docblock/DocBlock.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlockFactory' => '/phpdocumentor-reflection-docblock/DocBlockFactory.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlockFactoryInterface' => '/phpdocumentor-reflection-docblock/DocBlockFactoryInterface.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Description' => '/phpdocumentor-reflection-docblock/DocBlock/Description.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\DescriptionFactory' => '/phpdocumentor-reflection-docblock/DocBlock/DescriptionFactory.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\ExampleFinder' => '/phpdocumentor-reflection-docblock/DocBlock/ExampleFinder.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Serializer' => '/phpdocumentor-reflection-docblock/DocBlock/Serializer.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\StandardTagFactory' => '/phpdocumentor-reflection-docblock/DocBlock/StandardTagFactory.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Tag' => '/phpdocumentor-reflection-docblock/DocBlock/Tag.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\TagFactory' => '/phpdocumentor-reflection-docblock/DocBlock/TagFactory.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Author' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Author.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Tags\\BaseTag' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/BaseTag.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Covers' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Covers.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Deprecated' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Deprecated.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Example' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Example.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Factory\\StaticMethod' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Factory/StaticMethod.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Formatter' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Formatter.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Formatter\\AlignFormatter' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Formatter/AlignFormatter.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Formatter\\PassthroughFormatter' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Formatter/PassthroughFormatter.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Generic' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Generic.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Tags\\InvalidTag' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/InvalidTag.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Link' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Link.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Method' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Method.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Param' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Param.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Property' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Property.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Tags\\PropertyRead' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/PropertyRead.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Tags\\PropertyWrite' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/PropertyWrite.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Reference\\Fqsen' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Reference/Fqsen.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Reference\\Reference' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Reference/Reference.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Reference\\Url' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Reference/Url.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Return_' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Return_.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Tags\\See' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/See.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Since' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Since.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Source' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Source.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Tags\\TagWithType' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/TagWithType.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Throws' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Throws.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Uses' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Uses.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Var_' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Var_.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Version' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Version.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Element' => '/phpdocumentor-reflection-common/Element.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Exception\\PcreException' => '/phpdocumentor-reflection-docblock/Exception/PcreException.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\File' => '/phpdocumentor-reflection-common/File.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Fqsen' => '/phpdocumentor-reflection-common/Fqsen.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\FqsenResolver' => '/phpdocumentor-type-resolver/FqsenResolver.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Location' => '/phpdocumentor-reflection-common/Location.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Project' => '/phpdocumentor-reflection-common/Project.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\ProjectFactory' => '/phpdocumentor-reflection-common/ProjectFactory.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\PseudoType' => '/phpdocumentor-type-resolver/PseudoType.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\PseudoTypes\\ArrayShape' => '/phpdocumentor-type-resolver/PseudoTypes/ArrayShape.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\PseudoTypes\\ArrayShapeItem' => '/phpdocumentor-type-resolver/PseudoTypes/ArrayShapeItem.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\PseudoTypes\\CallableString' => '/phpdocumentor-type-resolver/PseudoTypes/CallableString.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\PseudoTypes\\ConstExpression' => '/phpdocumentor-type-resolver/PseudoTypes/ConstExpression.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\PseudoTypes\\False_' => '/phpdocumentor-type-resolver/PseudoTypes/False_.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\PseudoTypes\\FloatValue' => '/phpdocumentor-type-resolver/PseudoTypes/FloatValue.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\PseudoTypes\\HtmlEscapedString' => '/phpdocumentor-type-resolver/PseudoTypes/HtmlEscapedString.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\PseudoTypes\\IntegerRange' => '/phpdocumentor-type-resolver/PseudoTypes/IntegerRange.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\PseudoTypes\\IntegerValue' => '/phpdocumentor-type-resolver/PseudoTypes/IntegerValue.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\PseudoTypes\\List_' => '/phpdocumentor-type-resolver/PseudoTypes/List_.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\PseudoTypes\\LiteralString' => '/phpdocumentor-type-resolver/PseudoTypes/LiteralString.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\PseudoTypes\\LowercaseString' => '/phpdocumentor-type-resolver/PseudoTypes/LowercaseString.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\PseudoTypes\\NegativeInteger' => '/phpdocumentor-type-resolver/PseudoTypes/NegativeInteger.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\PseudoTypes\\NonEmptyList' => '/phpdocumentor-type-resolver/PseudoTypes/NonEmptyList.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\PseudoTypes\\NonEmptyLowercaseString' => '/phpdocumentor-type-resolver/PseudoTypes/NonEmptyLowercaseString.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\PseudoTypes\\NonEmptyString' => '/phpdocumentor-type-resolver/PseudoTypes/NonEmptyString.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\PseudoTypes\\NumericString' => '/phpdocumentor-type-resolver/PseudoTypes/NumericString.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\PseudoTypes\\Numeric_' => '/phpdocumentor-type-resolver/PseudoTypes/Numeric_.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\PseudoTypes\\PositiveInteger' => '/phpdocumentor-type-resolver/PseudoTypes/PositiveInteger.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\PseudoTypes\\StringValue' => '/phpdocumentor-type-resolver/PseudoTypes/StringValue.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\PseudoTypes\\TraitString' => '/phpdocumentor-type-resolver/PseudoTypes/TraitString.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\PseudoTypes\\True_' => '/phpdocumentor-type-resolver/PseudoTypes/True_.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Type' => '/phpdocumentor-type-resolver/Type.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\TypeResolver' => '/phpdocumentor-type-resolver/TypeResolver.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\AbstractList' => '/phpdocumentor-type-resolver/Types/AbstractList.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\AggregatedType' => '/phpdocumentor-type-resolver/Types/AggregatedType.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\ArrayKey' => '/phpdocumentor-type-resolver/Types/ArrayKey.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\Array_' => '/phpdocumentor-type-resolver/Types/Array_.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\Boolean' => '/phpdocumentor-type-resolver/Types/Boolean.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\CallableParameter' => '/phpdocumentor-type-resolver/Types/CallableParameter.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\Callable_' => '/phpdocumentor-type-resolver/Types/Callable_.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\ClassString' => '/phpdocumentor-type-resolver/Types/ClassString.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\Collection' => '/phpdocumentor-type-resolver/Types/Collection.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\Compound' => '/phpdocumentor-type-resolver/Types/Compound.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\Context' => '/phpdocumentor-type-resolver/Types/Context.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\ContextFactory' => '/phpdocumentor-type-resolver/Types/ContextFactory.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\Expression' => '/phpdocumentor-type-resolver/Types/Expression.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\Float_' => '/phpdocumentor-type-resolver/Types/Float_.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\Integer' => '/phpdocumentor-type-resolver/Types/Integer.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\InterfaceString' => '/phpdocumentor-type-resolver/Types/InterfaceString.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\Intersection' => '/phpdocumentor-type-resolver/Types/Intersection.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\Iterable_' => '/phpdocumentor-type-resolver/Types/Iterable_.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\Mixed_' => '/phpdocumentor-type-resolver/Types/Mixed_.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\Never_' => '/phpdocumentor-type-resolver/Types/Never_.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\Null_' => '/phpdocumentor-type-resolver/Types/Null_.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\Nullable' => '/phpdocumentor-type-resolver/Types/Nullable.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\Object_' => '/phpdocumentor-type-resolver/Types/Object_.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\Parent_' => '/phpdocumentor-type-resolver/Types/Parent_.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\Resource_' => '/phpdocumentor-type-resolver/Types/Resource_.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\Scalar' => '/phpdocumentor-type-resolver/Types/Scalar.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\Self_' => '/phpdocumentor-type-resolver/Types/Self_.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\Static_' => '/phpdocumentor-type-resolver/Types/Static_.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\String_' => '/phpdocumentor-type-resolver/Types/String_.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\This' => '/phpdocumentor-type-resolver/Types/This.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\Void_' => '/phpdocumentor-type-resolver/Types/Void_.php', + 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Utils' => '/phpdocumentor-reflection-docblock/Utils.php', 'PHPUnit\\Exception' => '/phpunit/Exception.php', 'PHPUnit\\Framework\\ActualValueIsNotAnObjectException' => '/phpunit/Framework/Exception/ActualValueIsNotAnObjectException.php', 'PHPUnit\\Framework\\Assert' => '/phpunit/Framework/Assert.php', @@ -1402,327 +2226,6 @@ foreach (['PHPUnit\\DeepCopy\\DeepCopy' => '/myclabs-deep-copy/DeepCopy/DeepCopy 'PHPUnit\\Framework\\UnintentionallyCoveredCodeError' => '/phpunit/Framework/Exception/UnintentionallyCoveredCodeError.php', 'PHPUnit\\Framework\\Warning' => '/phpunit/Framework/Exception/Warning.php', 'PHPUnit\\Framework\\WarningTestCase' => '/phpunit/Framework/WarningTestCase.php', - 'PHPUnit\\PharIo\\Manifest\\Application' => '/phar-io-manifest/values/Application.php', - 'PHPUnit\\PharIo\\Manifest\\ApplicationName' => '/phar-io-manifest/values/ApplicationName.php', - 'PHPUnit\\PharIo\\Manifest\\Author' => '/phar-io-manifest/values/Author.php', - 'PHPUnit\\PharIo\\Manifest\\AuthorCollection' => '/phar-io-manifest/values/AuthorCollection.php', - 'PHPUnit\\PharIo\\Manifest\\AuthorCollectionIterator' => '/phar-io-manifest/values/AuthorCollectionIterator.php', - 'PHPUnit\\PharIo\\Manifest\\AuthorElement' => '/phar-io-manifest/xml/AuthorElement.php', - 'PHPUnit\\PharIo\\Manifest\\AuthorElementCollection' => '/phar-io-manifest/xml/AuthorElementCollection.php', - 'PHPUnit\\PharIo\\Manifest\\BundledComponent' => '/phar-io-manifest/values/BundledComponent.php', - 'PHPUnit\\PharIo\\Manifest\\BundledComponentCollection' => '/phar-io-manifest/values/BundledComponentCollection.php', - 'PHPUnit\\PharIo\\Manifest\\BundledComponentCollectionIterator' => '/phar-io-manifest/values/BundledComponentCollectionIterator.php', - 'PHPUnit\\PharIo\\Manifest\\BundlesElement' => '/phar-io-manifest/xml/BundlesElement.php', - 'PHPUnit\\PharIo\\Manifest\\ComponentElement' => '/phar-io-manifest/xml/ComponentElement.php', - 'PHPUnit\\PharIo\\Manifest\\ComponentElementCollection' => '/phar-io-manifest/xml/ComponentElementCollection.php', - 'PHPUnit\\PharIo\\Manifest\\ContainsElement' => '/phar-io-manifest/xml/ContainsElement.php', - 'PHPUnit\\PharIo\\Manifest\\CopyrightElement' => '/phar-io-manifest/xml/CopyrightElement.php', - 'PHPUnit\\PharIo\\Manifest\\CopyrightInformation' => '/phar-io-manifest/values/CopyrightInformation.php', - 'PHPUnit\\PharIo\\Manifest\\ElementCollection' => '/phar-io-manifest/xml/ElementCollection.php', - 'PHPUnit\\PharIo\\Manifest\\ElementCollectionException' => '/phar-io-manifest/exceptions/ElementCollectionException.php', - 'PHPUnit\\PharIo\\Manifest\\Email' => '/phar-io-manifest/values/Email.php', - 'PHPUnit\\PharIo\\Manifest\\Exception' => '/phar-io-manifest/exceptions/Exception.php', - 'PHPUnit\\PharIo\\Manifest\\ExtElement' => '/phar-io-manifest/xml/ExtElement.php', - 'PHPUnit\\PharIo\\Manifest\\ExtElementCollection' => '/phar-io-manifest/xml/ExtElementCollection.php', - 'PHPUnit\\PharIo\\Manifest\\Extension' => '/phar-io-manifest/values/Extension.php', - 'PHPUnit\\PharIo\\Manifest\\ExtensionElement' => '/phar-io-manifest/xml/ExtensionElement.php', - 'PHPUnit\\PharIo\\Manifest\\InvalidApplicationNameException' => '/phar-io-manifest/exceptions/InvalidApplicationNameException.php', - 'PHPUnit\\PharIo\\Manifest\\InvalidEmailException' => '/phar-io-manifest/exceptions/InvalidEmailException.php', - 'PHPUnit\\PharIo\\Manifest\\InvalidUrlException' => '/phar-io-manifest/exceptions/InvalidUrlException.php', - 'PHPUnit\\PharIo\\Manifest\\Library' => '/phar-io-manifest/values/Library.php', - 'PHPUnit\\PharIo\\Manifest\\License' => '/phar-io-manifest/values/License.php', - 'PHPUnit\\PharIo\\Manifest\\LicenseElement' => '/phar-io-manifest/xml/LicenseElement.php', - 'PHPUnit\\PharIo\\Manifest\\Manifest' => '/phar-io-manifest/values/Manifest.php', - 'PHPUnit\\PharIo\\Manifest\\ManifestDocument' => '/phar-io-manifest/xml/ManifestDocument.php', - 'PHPUnit\\PharIo\\Manifest\\ManifestDocumentException' => '/phar-io-manifest/exceptions/ManifestDocumentException.php', - 'PHPUnit\\PharIo\\Manifest\\ManifestDocumentLoadingException' => '/phar-io-manifest/exceptions/ManifestDocumentLoadingException.php', - 'PHPUnit\\PharIo\\Manifest\\ManifestDocumentMapper' => '/phar-io-manifest/ManifestDocumentMapper.php', - 'PHPUnit\\PharIo\\Manifest\\ManifestDocumentMapperException' => '/phar-io-manifest/exceptions/ManifestDocumentMapperException.php', - 'PHPUnit\\PharIo\\Manifest\\ManifestElement' => '/phar-io-manifest/xml/ManifestElement.php', - 'PHPUnit\\PharIo\\Manifest\\ManifestElementException' => '/phar-io-manifest/exceptions/ManifestElementException.php', - 'PHPUnit\\PharIo\\Manifest\\ManifestLoader' => '/phar-io-manifest/ManifestLoader.php', - 'PHPUnit\\PharIo\\Manifest\\ManifestLoaderException' => '/phar-io-manifest/exceptions/ManifestLoaderException.php', - 'PHPUnit\\PharIo\\Manifest\\ManifestSerializer' => '/phar-io-manifest/ManifestSerializer.php', - 'PHPUnit\\PharIo\\Manifest\\PhpElement' => '/phar-io-manifest/xml/PhpElement.php', - 'PHPUnit\\PharIo\\Manifest\\PhpExtensionRequirement' => '/phar-io-manifest/values/PhpExtensionRequirement.php', - 'PHPUnit\\PharIo\\Manifest\\PhpVersionRequirement' => '/phar-io-manifest/values/PhpVersionRequirement.php', - 'PHPUnit\\PharIo\\Manifest\\Requirement' => '/phar-io-manifest/values/Requirement.php', - 'PHPUnit\\PharIo\\Manifest\\RequirementCollection' => '/phar-io-manifest/values/RequirementCollection.php', - 'PHPUnit\\PharIo\\Manifest\\RequirementCollectionIterator' => '/phar-io-manifest/values/RequirementCollectionIterator.php', - 'PHPUnit\\PharIo\\Manifest\\RequiresElement' => '/phar-io-manifest/xml/RequiresElement.php', - 'PHPUnit\\PharIo\\Manifest\\Type' => '/phar-io-manifest/values/Type.php', - 'PHPUnit\\PharIo\\Manifest\\Url' => '/phar-io-manifest/values/Url.php', - 'PHPUnit\\PharIo\\Version\\AbstractVersionConstraint' => '/phar-io-version/constraints/AbstractVersionConstraint.php', - 'PHPUnit\\PharIo\\Version\\AndVersionConstraintGroup' => '/phar-io-version/constraints/AndVersionConstraintGroup.php', - 'PHPUnit\\PharIo\\Version\\AnyVersionConstraint' => '/phar-io-version/constraints/AnyVersionConstraint.php', - 'PHPUnit\\PharIo\\Version\\BuildMetaData' => '/phar-io-version/BuildMetaData.php', - 'PHPUnit\\PharIo\\Version\\ExactVersionConstraint' => '/phar-io-version/constraints/ExactVersionConstraint.php', - 'PHPUnit\\PharIo\\Version\\Exception' => '/phar-io-version/exceptions/Exception.php', - 'PHPUnit\\PharIo\\Version\\GreaterThanOrEqualToVersionConstraint' => '/phar-io-version/constraints/GreaterThanOrEqualToVersionConstraint.php', - 'PHPUnit\\PharIo\\Version\\InvalidPreReleaseSuffixException' => '/phar-io-version/exceptions/InvalidPreReleaseSuffixException.php', - 'PHPUnit\\PharIo\\Version\\InvalidVersionException' => '/phar-io-version/exceptions/InvalidVersionException.php', - 'PHPUnit\\PharIo\\Version\\NoBuildMetaDataException' => '/phar-io-version/exceptions/NoBuildMetaDataException.php', - 'PHPUnit\\PharIo\\Version\\NoPreReleaseSuffixException' => '/phar-io-version/exceptions/NoPreReleaseSuffixException.php', - 'PHPUnit\\PharIo\\Version\\OrVersionConstraintGroup' => '/phar-io-version/constraints/OrVersionConstraintGroup.php', - 'PHPUnit\\PharIo\\Version\\PreReleaseSuffix' => '/phar-io-version/PreReleaseSuffix.php', - 'PHPUnit\\PharIo\\Version\\SpecificMajorAndMinorVersionConstraint' => '/phar-io-version/constraints/SpecificMajorAndMinorVersionConstraint.php', - 'PHPUnit\\PharIo\\Version\\SpecificMajorVersionConstraint' => '/phar-io-version/constraints/SpecificMajorVersionConstraint.php', - 'PHPUnit\\PharIo\\Version\\UnsupportedVersionConstraintException' => '/phar-io-version/exceptions/UnsupportedVersionConstraintException.php', - 'PHPUnit\\PharIo\\Version\\Version' => '/phar-io-version/Version.php', - 'PHPUnit\\PharIo\\Version\\VersionConstraint' => '/phar-io-version/constraints/VersionConstraint.php', - 'PHPUnit\\PharIo\\Version\\VersionConstraintParser' => '/phar-io-version/VersionConstraintParser.php', - 'PHPUnit\\PharIo\\Version\\VersionConstraintValue' => '/phar-io-version/VersionConstraintValue.php', - 'PHPUnit\\PharIo\\Version\\VersionNumber' => '/phar-io-version/VersionNumber.php', - 'PHPUnit\\PhpParser\\Builder' => '/nikic-php-parser/PhpParser/Builder.php', - 'PHPUnit\\PhpParser\\BuilderFactory' => '/nikic-php-parser/PhpParser/BuilderFactory.php', - 'PHPUnit\\PhpParser\\BuilderHelpers' => '/nikic-php-parser/PhpParser/BuilderHelpers.php', - 'PHPUnit\\PhpParser\\Builder\\ClassConst' => '/nikic-php-parser/PhpParser/Builder/ClassConst.php', - 'PHPUnit\\PhpParser\\Builder\\Class_' => '/nikic-php-parser/PhpParser/Builder/Class_.php', - 'PHPUnit\\PhpParser\\Builder\\Declaration' => '/nikic-php-parser/PhpParser/Builder/Declaration.php', - 'PHPUnit\\PhpParser\\Builder\\EnumCase' => '/nikic-php-parser/PhpParser/Builder/EnumCase.php', - 'PHPUnit\\PhpParser\\Builder\\Enum_' => '/nikic-php-parser/PhpParser/Builder/Enum_.php', - 'PHPUnit\\PhpParser\\Builder\\FunctionLike' => '/nikic-php-parser/PhpParser/Builder/FunctionLike.php', - 'PHPUnit\\PhpParser\\Builder\\Function_' => '/nikic-php-parser/PhpParser/Builder/Function_.php', - 'PHPUnit\\PhpParser\\Builder\\Interface_' => '/nikic-php-parser/PhpParser/Builder/Interface_.php', - 'PHPUnit\\PhpParser\\Builder\\Method' => '/nikic-php-parser/PhpParser/Builder/Method.php', - 'PHPUnit\\PhpParser\\Builder\\Namespace_' => '/nikic-php-parser/PhpParser/Builder/Namespace_.php', - 'PHPUnit\\PhpParser\\Builder\\Param' => '/nikic-php-parser/PhpParser/Builder/Param.php', - 'PHPUnit\\PhpParser\\Builder\\Property' => '/nikic-php-parser/PhpParser/Builder/Property.php', - 'PHPUnit\\PhpParser\\Builder\\TraitUse' => '/nikic-php-parser/PhpParser/Builder/TraitUse.php', - 'PHPUnit\\PhpParser\\Builder\\TraitUseAdaptation' => '/nikic-php-parser/PhpParser/Builder/TraitUseAdaptation.php', - 'PHPUnit\\PhpParser\\Builder\\Trait_' => '/nikic-php-parser/PhpParser/Builder/Trait_.php', - 'PHPUnit\\PhpParser\\Builder\\Use_' => '/nikic-php-parser/PhpParser/Builder/Use_.php', - 'PHPUnit\\PhpParser\\Comment' => '/nikic-php-parser/PhpParser/Comment.php', - 'PHPUnit\\PhpParser\\Comment\\Doc' => '/nikic-php-parser/PhpParser/Comment/Doc.php', - 'PHPUnit\\PhpParser\\ConstExprEvaluationException' => '/nikic-php-parser/PhpParser/ConstExprEvaluationException.php', - 'PHPUnit\\PhpParser\\ConstExprEvaluator' => '/nikic-php-parser/PhpParser/ConstExprEvaluator.php', - 'PHPUnit\\PhpParser\\Error' => '/nikic-php-parser/PhpParser/Error.php', - 'PHPUnit\\PhpParser\\ErrorHandler' => '/nikic-php-parser/PhpParser/ErrorHandler.php', - 'PHPUnit\\PhpParser\\ErrorHandler\\Collecting' => '/nikic-php-parser/PhpParser/ErrorHandler/Collecting.php', - 'PHPUnit\\PhpParser\\ErrorHandler\\Throwing' => '/nikic-php-parser/PhpParser/ErrorHandler/Throwing.php', - 'PHPUnit\\PhpParser\\Internal\\DiffElem' => '/nikic-php-parser/PhpParser/Internal/DiffElem.php', - 'PHPUnit\\PhpParser\\Internal\\Differ' => '/nikic-php-parser/PhpParser/Internal/Differ.php', - 'PHPUnit\\PhpParser\\Internal\\PrintableNewAnonClassNode' => '/nikic-php-parser/PhpParser/Internal/PrintableNewAnonClassNode.php', - 'PHPUnit\\PhpParser\\Internal\\TokenStream' => '/nikic-php-parser/PhpParser/Internal/TokenStream.php', - 'PHPUnit\\PhpParser\\JsonDecoder' => '/nikic-php-parser/PhpParser/JsonDecoder.php', - 'PHPUnit\\PhpParser\\Lexer' => '/nikic-php-parser/PhpParser/Lexer.php', - 'PHPUnit\\PhpParser\\Lexer\\Emulative' => '/nikic-php-parser/PhpParser/Lexer/Emulative.php', - 'PHPUnit\\PhpParser\\Lexer\\TokenEmulator\\AttributeEmulator' => '/nikic-php-parser/PhpParser/Lexer/TokenEmulator/AttributeEmulator.php', - 'PHPUnit\\PhpParser\\Lexer\\TokenEmulator\\CoaleseEqualTokenEmulator' => '/nikic-php-parser/PhpParser/Lexer/TokenEmulator/CoaleseEqualTokenEmulator.php', - 'PHPUnit\\PhpParser\\Lexer\\TokenEmulator\\EnumTokenEmulator' => '/nikic-php-parser/PhpParser/Lexer/TokenEmulator/EnumTokenEmulator.php', - 'PHPUnit\\PhpParser\\Lexer\\TokenEmulator\\ExplicitOctalEmulator' => '/nikic-php-parser/PhpParser/Lexer/TokenEmulator/ExplicitOctalEmulator.php', - 'PHPUnit\\PhpParser\\Lexer\\TokenEmulator\\FlexibleDocStringEmulator' => '/nikic-php-parser/PhpParser/Lexer/TokenEmulator/FlexibleDocStringEmulator.php', - 'PHPUnit\\PhpParser\\Lexer\\TokenEmulator\\FnTokenEmulator' => '/nikic-php-parser/PhpParser/Lexer/TokenEmulator/FnTokenEmulator.php', - 'PHPUnit\\PhpParser\\Lexer\\TokenEmulator\\KeywordEmulator' => '/nikic-php-parser/PhpParser/Lexer/TokenEmulator/KeywordEmulator.php', - 'PHPUnit\\PhpParser\\Lexer\\TokenEmulator\\MatchTokenEmulator' => '/nikic-php-parser/PhpParser/Lexer/TokenEmulator/MatchTokenEmulator.php', - 'PHPUnit\\PhpParser\\Lexer\\TokenEmulator\\NullsafeTokenEmulator' => '/nikic-php-parser/PhpParser/Lexer/TokenEmulator/NullsafeTokenEmulator.php', - 'PHPUnit\\PhpParser\\Lexer\\TokenEmulator\\NumericLiteralSeparatorEmulator' => '/nikic-php-parser/PhpParser/Lexer/TokenEmulator/NumericLiteralSeparatorEmulator.php', - 'PHPUnit\\PhpParser\\Lexer\\TokenEmulator\\ReadonlyFunctionTokenEmulator' => '/nikic-php-parser/PhpParser/Lexer/TokenEmulator/ReadonlyFunctionTokenEmulator.php', - 'PHPUnit\\PhpParser\\Lexer\\TokenEmulator\\ReadonlyTokenEmulator' => '/nikic-php-parser/PhpParser/Lexer/TokenEmulator/ReadonlyTokenEmulator.php', - 'PHPUnit\\PhpParser\\Lexer\\TokenEmulator\\ReverseEmulator' => '/nikic-php-parser/PhpParser/Lexer/TokenEmulator/ReverseEmulator.php', - 'PHPUnit\\PhpParser\\Lexer\\TokenEmulator\\TokenEmulator' => '/nikic-php-parser/PhpParser/Lexer/TokenEmulator/TokenEmulator.php', - 'PHPUnit\\PhpParser\\NameContext' => '/nikic-php-parser/PhpParser/NameContext.php', - 'PHPUnit\\PhpParser\\Node' => '/nikic-php-parser/PhpParser/Node.php', - 'PHPUnit\\PhpParser\\NodeAbstract' => '/nikic-php-parser/PhpParser/NodeAbstract.php', - 'PHPUnit\\PhpParser\\NodeDumper' => '/nikic-php-parser/PhpParser/NodeDumper.php', - 'PHPUnit\\PhpParser\\NodeFinder' => '/nikic-php-parser/PhpParser/NodeFinder.php', - 'PHPUnit\\PhpParser\\NodeTraverser' => '/nikic-php-parser/PhpParser/NodeTraverser.php', - 'PHPUnit\\PhpParser\\NodeTraverserInterface' => '/nikic-php-parser/PhpParser/NodeTraverserInterface.php', - 'PHPUnit\\PhpParser\\NodeVisitor' => '/nikic-php-parser/PhpParser/NodeVisitor.php', - 'PHPUnit\\PhpParser\\NodeVisitorAbstract' => '/nikic-php-parser/PhpParser/NodeVisitorAbstract.php', - 'PHPUnit\\PhpParser\\NodeVisitor\\CloningVisitor' => '/nikic-php-parser/PhpParser/NodeVisitor/CloningVisitor.php', - 'PHPUnit\\PhpParser\\NodeVisitor\\FindingVisitor' => '/nikic-php-parser/PhpParser/NodeVisitor/FindingVisitor.php', - 'PHPUnit\\PhpParser\\NodeVisitor\\FirstFindingVisitor' => '/nikic-php-parser/PhpParser/NodeVisitor/FirstFindingVisitor.php', - 'PHPUnit\\PhpParser\\NodeVisitor\\NameResolver' => '/nikic-php-parser/PhpParser/NodeVisitor/NameResolver.php', - 'PHPUnit\\PhpParser\\NodeVisitor\\NodeConnectingVisitor' => '/nikic-php-parser/PhpParser/NodeVisitor/NodeConnectingVisitor.php', - 'PHPUnit\\PhpParser\\NodeVisitor\\ParentConnectingVisitor' => '/nikic-php-parser/PhpParser/NodeVisitor/ParentConnectingVisitor.php', - 'PHPUnit\\PhpParser\\Node\\Arg' => '/nikic-php-parser/PhpParser/Node/Arg.php', - 'PHPUnit\\PhpParser\\Node\\Attribute' => '/nikic-php-parser/PhpParser/Node/Attribute.php', - 'PHPUnit\\PhpParser\\Node\\AttributeGroup' => '/nikic-php-parser/PhpParser/Node/AttributeGroup.php', - 'PHPUnit\\PhpParser\\Node\\ComplexType' => '/nikic-php-parser/PhpParser/Node/ComplexType.php', - 'PHPUnit\\PhpParser\\Node\\Const_' => '/nikic-php-parser/PhpParser/Node/Const_.php', - 'PHPUnit\\PhpParser\\Node\\Expr' => '/nikic-php-parser/PhpParser/Node/Expr.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\ArrayDimFetch' => '/nikic-php-parser/PhpParser/Node/Expr/ArrayDimFetch.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\ArrayItem' => '/nikic-php-parser/PhpParser/Node/Expr/ArrayItem.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\Array_' => '/nikic-php-parser/PhpParser/Node/Expr/Array_.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\ArrowFunction' => '/nikic-php-parser/PhpParser/Node/Expr/ArrowFunction.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\Assign' => '/nikic-php-parser/PhpParser/Node/Expr/Assign.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\AssignOp' => '/nikic-php-parser/PhpParser/Node/Expr/AssignOp.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\AssignOp\\BitwiseAnd' => '/nikic-php-parser/PhpParser/Node/Expr/AssignOp/BitwiseAnd.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\AssignOp\\BitwiseOr' => '/nikic-php-parser/PhpParser/Node/Expr/AssignOp/BitwiseOr.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\AssignOp\\BitwiseXor' => '/nikic-php-parser/PhpParser/Node/Expr/AssignOp/BitwiseXor.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\AssignOp\\Coalesce' => '/nikic-php-parser/PhpParser/Node/Expr/AssignOp/Coalesce.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\AssignOp\\Concat' => '/nikic-php-parser/PhpParser/Node/Expr/AssignOp/Concat.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\AssignOp\\Div' => '/nikic-php-parser/PhpParser/Node/Expr/AssignOp/Div.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\AssignOp\\Minus' => '/nikic-php-parser/PhpParser/Node/Expr/AssignOp/Minus.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\AssignOp\\Mod' => '/nikic-php-parser/PhpParser/Node/Expr/AssignOp/Mod.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\AssignOp\\Mul' => '/nikic-php-parser/PhpParser/Node/Expr/AssignOp/Mul.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\AssignOp\\Plus' => '/nikic-php-parser/PhpParser/Node/Expr/AssignOp/Plus.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\AssignOp\\Pow' => '/nikic-php-parser/PhpParser/Node/Expr/AssignOp/Pow.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\AssignOp\\ShiftLeft' => '/nikic-php-parser/PhpParser/Node/Expr/AssignOp/ShiftLeft.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\AssignOp\\ShiftRight' => '/nikic-php-parser/PhpParser/Node/Expr/AssignOp/ShiftRight.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\AssignRef' => '/nikic-php-parser/PhpParser/Node/Expr/AssignRef.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\BinaryOp' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\BinaryOp\\BitwiseAnd' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/BitwiseAnd.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\BinaryOp\\BitwiseOr' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/BitwiseOr.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\BinaryOp\\BitwiseXor' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/BitwiseXor.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\BinaryOp\\BooleanAnd' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/BooleanAnd.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\BinaryOp\\BooleanOr' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/BooleanOr.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\BinaryOp\\Coalesce' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Coalesce.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\BinaryOp\\Concat' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Concat.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\BinaryOp\\Div' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Div.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\BinaryOp\\Equal' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Equal.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\BinaryOp\\Greater' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Greater.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\BinaryOp\\GreaterOrEqual' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/GreaterOrEqual.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\BinaryOp\\Identical' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Identical.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\BinaryOp\\LogicalAnd' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/LogicalAnd.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\BinaryOp\\LogicalOr' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/LogicalOr.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\BinaryOp\\LogicalXor' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/LogicalXor.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\BinaryOp\\Minus' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Minus.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\BinaryOp\\Mod' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Mod.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\BinaryOp\\Mul' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Mul.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\BinaryOp\\NotEqual' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/NotEqual.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\BinaryOp\\NotIdentical' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/NotIdentical.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\BinaryOp\\Plus' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Plus.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\BinaryOp\\Pow' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Pow.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\BinaryOp\\ShiftLeft' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/ShiftLeft.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\BinaryOp\\ShiftRight' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/ShiftRight.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\BinaryOp\\Smaller' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Smaller.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\BinaryOp\\SmallerOrEqual' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/SmallerOrEqual.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\BinaryOp\\Spaceship' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Spaceship.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\BitwiseNot' => '/nikic-php-parser/PhpParser/Node/Expr/BitwiseNot.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\BooleanNot' => '/nikic-php-parser/PhpParser/Node/Expr/BooleanNot.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\CallLike' => '/nikic-php-parser/PhpParser/Node/Expr/CallLike.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\Cast' => '/nikic-php-parser/PhpParser/Node/Expr/Cast.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\Cast\\Array_' => '/nikic-php-parser/PhpParser/Node/Expr/Cast/Array_.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\Cast\\Bool_' => '/nikic-php-parser/PhpParser/Node/Expr/Cast/Bool_.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\Cast\\Double' => '/nikic-php-parser/PhpParser/Node/Expr/Cast/Double.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\Cast\\Int_' => '/nikic-php-parser/PhpParser/Node/Expr/Cast/Int_.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\Cast\\Object_' => '/nikic-php-parser/PhpParser/Node/Expr/Cast/Object_.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\Cast\\String_' => '/nikic-php-parser/PhpParser/Node/Expr/Cast/String_.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\Cast\\Unset_' => '/nikic-php-parser/PhpParser/Node/Expr/Cast/Unset_.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\ClassConstFetch' => '/nikic-php-parser/PhpParser/Node/Expr/ClassConstFetch.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\Clone_' => '/nikic-php-parser/PhpParser/Node/Expr/Clone_.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\Closure' => '/nikic-php-parser/PhpParser/Node/Expr/Closure.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\ClosureUse' => '/nikic-php-parser/PhpParser/Node/Expr/ClosureUse.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\ConstFetch' => '/nikic-php-parser/PhpParser/Node/Expr/ConstFetch.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\Empty_' => '/nikic-php-parser/PhpParser/Node/Expr/Empty_.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\Error' => '/nikic-php-parser/PhpParser/Node/Expr/Error.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\ErrorSuppress' => '/nikic-php-parser/PhpParser/Node/Expr/ErrorSuppress.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\Eval_' => '/nikic-php-parser/PhpParser/Node/Expr/Eval_.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\Exit_' => '/nikic-php-parser/PhpParser/Node/Expr/Exit_.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\FuncCall' => '/nikic-php-parser/PhpParser/Node/Expr/FuncCall.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\Include_' => '/nikic-php-parser/PhpParser/Node/Expr/Include_.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\Instanceof_' => '/nikic-php-parser/PhpParser/Node/Expr/Instanceof_.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\Isset_' => '/nikic-php-parser/PhpParser/Node/Expr/Isset_.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\List_' => '/nikic-php-parser/PhpParser/Node/Expr/List_.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\Match_' => '/nikic-php-parser/PhpParser/Node/Expr/Match_.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\MethodCall' => '/nikic-php-parser/PhpParser/Node/Expr/MethodCall.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\New_' => '/nikic-php-parser/PhpParser/Node/Expr/New_.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\NullsafeMethodCall' => '/nikic-php-parser/PhpParser/Node/Expr/NullsafeMethodCall.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\NullsafePropertyFetch' => '/nikic-php-parser/PhpParser/Node/Expr/NullsafePropertyFetch.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\PostDec' => '/nikic-php-parser/PhpParser/Node/Expr/PostDec.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\PostInc' => '/nikic-php-parser/PhpParser/Node/Expr/PostInc.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\PreDec' => '/nikic-php-parser/PhpParser/Node/Expr/PreDec.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\PreInc' => '/nikic-php-parser/PhpParser/Node/Expr/PreInc.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\Print_' => '/nikic-php-parser/PhpParser/Node/Expr/Print_.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\PropertyFetch' => '/nikic-php-parser/PhpParser/Node/Expr/PropertyFetch.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\ShellExec' => '/nikic-php-parser/PhpParser/Node/Expr/ShellExec.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\StaticCall' => '/nikic-php-parser/PhpParser/Node/Expr/StaticCall.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\StaticPropertyFetch' => '/nikic-php-parser/PhpParser/Node/Expr/StaticPropertyFetch.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\Ternary' => '/nikic-php-parser/PhpParser/Node/Expr/Ternary.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\Throw_' => '/nikic-php-parser/PhpParser/Node/Expr/Throw_.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\UnaryMinus' => '/nikic-php-parser/PhpParser/Node/Expr/UnaryMinus.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\UnaryPlus' => '/nikic-php-parser/PhpParser/Node/Expr/UnaryPlus.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\Variable' => '/nikic-php-parser/PhpParser/Node/Expr/Variable.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\YieldFrom' => '/nikic-php-parser/PhpParser/Node/Expr/YieldFrom.php', - 'PHPUnit\\PhpParser\\Node\\Expr\\Yield_' => '/nikic-php-parser/PhpParser/Node/Expr/Yield_.php', - 'PHPUnit\\PhpParser\\Node\\FunctionLike' => '/nikic-php-parser/PhpParser/Node/FunctionLike.php', - 'PHPUnit\\PhpParser\\Node\\Identifier' => '/nikic-php-parser/PhpParser/Node/Identifier.php', - 'PHPUnit\\PhpParser\\Node\\IntersectionType' => '/nikic-php-parser/PhpParser/Node/IntersectionType.php', - 'PHPUnit\\PhpParser\\Node\\MatchArm' => '/nikic-php-parser/PhpParser/Node/MatchArm.php', - 'PHPUnit\\PhpParser\\Node\\Name' => '/nikic-php-parser/PhpParser/Node/Name.php', - 'PHPUnit\\PhpParser\\Node\\Name\\FullyQualified' => '/nikic-php-parser/PhpParser/Node/Name/FullyQualified.php', - 'PHPUnit\\PhpParser\\Node\\Name\\Relative' => '/nikic-php-parser/PhpParser/Node/Name/Relative.php', - 'PHPUnit\\PhpParser\\Node\\NullableType' => '/nikic-php-parser/PhpParser/Node/NullableType.php', - 'PHPUnit\\PhpParser\\Node\\Param' => '/nikic-php-parser/PhpParser/Node/Param.php', - 'PHPUnit\\PhpParser\\Node\\Scalar' => '/nikic-php-parser/PhpParser/Node/Scalar.php', - 'PHPUnit\\PhpParser\\Node\\Scalar\\DNumber' => '/nikic-php-parser/PhpParser/Node/Scalar/DNumber.php', - 'PHPUnit\\PhpParser\\Node\\Scalar\\Encapsed' => '/nikic-php-parser/PhpParser/Node/Scalar/Encapsed.php', - 'PHPUnit\\PhpParser\\Node\\Scalar\\EncapsedStringPart' => '/nikic-php-parser/PhpParser/Node/Scalar/EncapsedStringPart.php', - 'PHPUnit\\PhpParser\\Node\\Scalar\\LNumber' => '/nikic-php-parser/PhpParser/Node/Scalar/LNumber.php', - 'PHPUnit\\PhpParser\\Node\\Scalar\\MagicConst' => '/nikic-php-parser/PhpParser/Node/Scalar/MagicConst.php', - 'PHPUnit\\PhpParser\\Node\\Scalar\\MagicConst\\Class_' => '/nikic-php-parser/PhpParser/Node/Scalar/MagicConst/Class_.php', - 'PHPUnit\\PhpParser\\Node\\Scalar\\MagicConst\\Dir' => '/nikic-php-parser/PhpParser/Node/Scalar/MagicConst/Dir.php', - 'PHPUnit\\PhpParser\\Node\\Scalar\\MagicConst\\File' => '/nikic-php-parser/PhpParser/Node/Scalar/MagicConst/File.php', - 'PHPUnit\\PhpParser\\Node\\Scalar\\MagicConst\\Function_' => '/nikic-php-parser/PhpParser/Node/Scalar/MagicConst/Function_.php', - 'PHPUnit\\PhpParser\\Node\\Scalar\\MagicConst\\Line' => '/nikic-php-parser/PhpParser/Node/Scalar/MagicConst/Line.php', - 'PHPUnit\\PhpParser\\Node\\Scalar\\MagicConst\\Method' => '/nikic-php-parser/PhpParser/Node/Scalar/MagicConst/Method.php', - 'PHPUnit\\PhpParser\\Node\\Scalar\\MagicConst\\Namespace_' => '/nikic-php-parser/PhpParser/Node/Scalar/MagicConst/Namespace_.php', - 'PHPUnit\\PhpParser\\Node\\Scalar\\MagicConst\\Trait_' => '/nikic-php-parser/PhpParser/Node/Scalar/MagicConst/Trait_.php', - 'PHPUnit\\PhpParser\\Node\\Scalar\\String_' => '/nikic-php-parser/PhpParser/Node/Scalar/String_.php', - 'PHPUnit\\PhpParser\\Node\\Stmt' => '/nikic-php-parser/PhpParser/Node/Stmt.php', - 'PHPUnit\\PhpParser\\Node\\Stmt\\Break_' => '/nikic-php-parser/PhpParser/Node/Stmt/Break_.php', - 'PHPUnit\\PhpParser\\Node\\Stmt\\Case_' => '/nikic-php-parser/PhpParser/Node/Stmt/Case_.php', - 'PHPUnit\\PhpParser\\Node\\Stmt\\Catch_' => '/nikic-php-parser/PhpParser/Node/Stmt/Catch_.php', - 'PHPUnit\\PhpParser\\Node\\Stmt\\ClassConst' => '/nikic-php-parser/PhpParser/Node/Stmt/ClassConst.php', - 'PHPUnit\\PhpParser\\Node\\Stmt\\ClassLike' => '/nikic-php-parser/PhpParser/Node/Stmt/ClassLike.php', - 'PHPUnit\\PhpParser\\Node\\Stmt\\ClassMethod' => '/nikic-php-parser/PhpParser/Node/Stmt/ClassMethod.php', - 'PHPUnit\\PhpParser\\Node\\Stmt\\Class_' => '/nikic-php-parser/PhpParser/Node/Stmt/Class_.php', - 'PHPUnit\\PhpParser\\Node\\Stmt\\Const_' => '/nikic-php-parser/PhpParser/Node/Stmt/Const_.php', - 'PHPUnit\\PhpParser\\Node\\Stmt\\Continue_' => '/nikic-php-parser/PhpParser/Node/Stmt/Continue_.php', - 'PHPUnit\\PhpParser\\Node\\Stmt\\DeclareDeclare' => '/nikic-php-parser/PhpParser/Node/Stmt/DeclareDeclare.php', - 'PHPUnit\\PhpParser\\Node\\Stmt\\Declare_' => '/nikic-php-parser/PhpParser/Node/Stmt/Declare_.php', - 'PHPUnit\\PhpParser\\Node\\Stmt\\Do_' => '/nikic-php-parser/PhpParser/Node/Stmt/Do_.php', - 'PHPUnit\\PhpParser\\Node\\Stmt\\Echo_' => '/nikic-php-parser/PhpParser/Node/Stmt/Echo_.php', - 'PHPUnit\\PhpParser\\Node\\Stmt\\ElseIf_' => '/nikic-php-parser/PhpParser/Node/Stmt/ElseIf_.php', - 'PHPUnit\\PhpParser\\Node\\Stmt\\Else_' => '/nikic-php-parser/PhpParser/Node/Stmt/Else_.php', - 'PHPUnit\\PhpParser\\Node\\Stmt\\EnumCase' => '/nikic-php-parser/PhpParser/Node/Stmt/EnumCase.php', - 'PHPUnit\\PhpParser\\Node\\Stmt\\Enum_' => '/nikic-php-parser/PhpParser/Node/Stmt/Enum_.php', - 'PHPUnit\\PhpParser\\Node\\Stmt\\Expression' => '/nikic-php-parser/PhpParser/Node/Stmt/Expression.php', - 'PHPUnit\\PhpParser\\Node\\Stmt\\Finally_' => '/nikic-php-parser/PhpParser/Node/Stmt/Finally_.php', - 'PHPUnit\\PhpParser\\Node\\Stmt\\For_' => '/nikic-php-parser/PhpParser/Node/Stmt/For_.php', - 'PHPUnit\\PhpParser\\Node\\Stmt\\Foreach_' => '/nikic-php-parser/PhpParser/Node/Stmt/Foreach_.php', - 'PHPUnit\\PhpParser\\Node\\Stmt\\Function_' => '/nikic-php-parser/PhpParser/Node/Stmt/Function_.php', - 'PHPUnit\\PhpParser\\Node\\Stmt\\Global_' => '/nikic-php-parser/PhpParser/Node/Stmt/Global_.php', - 'PHPUnit\\PhpParser\\Node\\Stmt\\Goto_' => '/nikic-php-parser/PhpParser/Node/Stmt/Goto_.php', - 'PHPUnit\\PhpParser\\Node\\Stmt\\GroupUse' => '/nikic-php-parser/PhpParser/Node/Stmt/GroupUse.php', - 'PHPUnit\\PhpParser\\Node\\Stmt\\HaltCompiler' => '/nikic-php-parser/PhpParser/Node/Stmt/HaltCompiler.php', - 'PHPUnit\\PhpParser\\Node\\Stmt\\If_' => '/nikic-php-parser/PhpParser/Node/Stmt/If_.php', - 'PHPUnit\\PhpParser\\Node\\Stmt\\InlineHTML' => '/nikic-php-parser/PhpParser/Node/Stmt/InlineHTML.php', - 'PHPUnit\\PhpParser\\Node\\Stmt\\Interface_' => '/nikic-php-parser/PhpParser/Node/Stmt/Interface_.php', - 'PHPUnit\\PhpParser\\Node\\Stmt\\Label' => '/nikic-php-parser/PhpParser/Node/Stmt/Label.php', - 'PHPUnit\\PhpParser\\Node\\Stmt\\Namespace_' => '/nikic-php-parser/PhpParser/Node/Stmt/Namespace_.php', - 'PHPUnit\\PhpParser\\Node\\Stmt\\Nop' => '/nikic-php-parser/PhpParser/Node/Stmt/Nop.php', - 'PHPUnit\\PhpParser\\Node\\Stmt\\Property' => '/nikic-php-parser/PhpParser/Node/Stmt/Property.php', - 'PHPUnit\\PhpParser\\Node\\Stmt\\PropertyProperty' => '/nikic-php-parser/PhpParser/Node/Stmt/PropertyProperty.php', - 'PHPUnit\\PhpParser\\Node\\Stmt\\Return_' => '/nikic-php-parser/PhpParser/Node/Stmt/Return_.php', - 'PHPUnit\\PhpParser\\Node\\Stmt\\StaticVar' => '/nikic-php-parser/PhpParser/Node/Stmt/StaticVar.php', - 'PHPUnit\\PhpParser\\Node\\Stmt\\Static_' => '/nikic-php-parser/PhpParser/Node/Stmt/Static_.php', - 'PHPUnit\\PhpParser\\Node\\Stmt\\Switch_' => '/nikic-php-parser/PhpParser/Node/Stmt/Switch_.php', - 'PHPUnit\\PhpParser\\Node\\Stmt\\Throw_' => '/nikic-php-parser/PhpParser/Node/Stmt/Throw_.php', - 'PHPUnit\\PhpParser\\Node\\Stmt\\TraitUse' => '/nikic-php-parser/PhpParser/Node/Stmt/TraitUse.php', - 'PHPUnit\\PhpParser\\Node\\Stmt\\TraitUseAdaptation' => '/nikic-php-parser/PhpParser/Node/Stmt/TraitUseAdaptation.php', - 'PHPUnit\\PhpParser\\Node\\Stmt\\TraitUseAdaptation\\Alias' => '/nikic-php-parser/PhpParser/Node/Stmt/TraitUseAdaptation/Alias.php', - 'PHPUnit\\PhpParser\\Node\\Stmt\\TraitUseAdaptation\\Precedence' => '/nikic-php-parser/PhpParser/Node/Stmt/TraitUseAdaptation/Precedence.php', - 'PHPUnit\\PhpParser\\Node\\Stmt\\Trait_' => '/nikic-php-parser/PhpParser/Node/Stmt/Trait_.php', - 'PHPUnit\\PhpParser\\Node\\Stmt\\TryCatch' => '/nikic-php-parser/PhpParser/Node/Stmt/TryCatch.php', - 'PHPUnit\\PhpParser\\Node\\Stmt\\Unset_' => '/nikic-php-parser/PhpParser/Node/Stmt/Unset_.php', - 'PHPUnit\\PhpParser\\Node\\Stmt\\UseUse' => '/nikic-php-parser/PhpParser/Node/Stmt/UseUse.php', - 'PHPUnit\\PhpParser\\Node\\Stmt\\Use_' => '/nikic-php-parser/PhpParser/Node/Stmt/Use_.php', - 'PHPUnit\\PhpParser\\Node\\Stmt\\While_' => '/nikic-php-parser/PhpParser/Node/Stmt/While_.php', - 'PHPUnit\\PhpParser\\Node\\UnionType' => '/nikic-php-parser/PhpParser/Node/UnionType.php', - 'PHPUnit\\PhpParser\\Node\\VarLikeIdentifier' => '/nikic-php-parser/PhpParser/Node/VarLikeIdentifier.php', - 'PHPUnit\\PhpParser\\Node\\VariadicPlaceholder' => '/nikic-php-parser/PhpParser/Node/VariadicPlaceholder.php', - 'PHPUnit\\PhpParser\\Parser' => '/nikic-php-parser/PhpParser/Parser.php', - 'PHPUnit\\PhpParser\\ParserAbstract' => '/nikic-php-parser/PhpParser/ParserAbstract.php', - 'PHPUnit\\PhpParser\\ParserFactory' => '/nikic-php-parser/PhpParser/ParserFactory.php', - 'PHPUnit\\PhpParser\\Parser\\Multiple' => '/nikic-php-parser/PhpParser/Parser/Multiple.php', - 'PHPUnit\\PhpParser\\Parser\\Php5' => '/nikic-php-parser/PhpParser/Parser/Php5.php', - 'PHPUnit\\PhpParser\\Parser\\Php7' => '/nikic-php-parser/PhpParser/Parser/Php7.php', - 'PHPUnit\\PhpParser\\Parser\\Tokens' => '/nikic-php-parser/PhpParser/Parser/Tokens.php', - 'PHPUnit\\PhpParser\\PrettyPrinterAbstract' => '/nikic-php-parser/PhpParser/PrettyPrinterAbstract.php', - 'PHPUnit\\PhpParser\\PrettyPrinter\\Standard' => '/nikic-php-parser/PhpParser/PrettyPrinter/Standard.php', 'PHPUnit\\Runner\\AfterIncompleteTestHook' => '/phpunit/Runner/Hook/AfterIncompleteTestHook.php', 'PHPUnit\\Runner\\AfterLastTestHook' => '/phpunit/Runner/Hook/AfterLastTestHook.php', 'PHPUnit\\Runner\\AfterRiskyTestHook' => '/phpunit/Runner/Hook/AfterRiskyTestHook.php', @@ -1755,206 +2258,6 @@ foreach (['PHPUnit\\DeepCopy\\DeepCopy' => '/myclabs-deep-copy/DeepCopy/DeepCopy 'PHPUnit\\Runner\\TestSuiteLoader' => '/phpunit/Runner/TestSuiteLoader.php', 'PHPUnit\\Runner\\TestSuiteSorter' => '/phpunit/Runner/TestSuiteSorter.php', 'PHPUnit\\Runner\\Version' => '/phpunit/Runner/Version.php', - 'PHPUnit\\SebastianBergmann\\CliParser\\AmbiguousOptionException' => '/sebastian-cli-parser/exceptions/AmbiguousOptionException.php', - 'PHPUnit\\SebastianBergmann\\CliParser\\Exception' => '/sebastian-cli-parser/exceptions/Exception.php', - 'PHPUnit\\SebastianBergmann\\CliParser\\OptionDoesNotAllowArgumentException' => '/sebastian-cli-parser/exceptions/OptionDoesNotAllowArgumentException.php', - 'PHPUnit\\SebastianBergmann\\CliParser\\Parser' => '/sebastian-cli-parser/Parser.php', - 'PHPUnit\\SebastianBergmann\\CliParser\\RequiredOptionArgumentMissingException' => '/sebastian-cli-parser/exceptions/RequiredOptionArgumentMissingException.php', - 'PHPUnit\\SebastianBergmann\\CliParser\\UnknownOptionException' => '/sebastian-cli-parser/exceptions/UnknownOptionException.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\BranchAndPathCoverageNotSupportedException' => '/php-code-coverage/Exception/BranchAndPathCoverageNotSupportedException.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\CodeCoverage' => '/php-code-coverage/CodeCoverage.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\DeadCodeDetectionNotSupportedException' => '/php-code-coverage/Exception/DeadCodeDetectionNotSupportedException.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Driver\\Driver' => '/php-code-coverage/Driver/Driver.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Driver\\PathExistsButIsNotDirectoryException' => '/php-code-coverage/Exception/PathExistsButIsNotDirectoryException.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Driver\\PcovDriver' => '/php-code-coverage/Driver/PcovDriver.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Driver\\PcovNotAvailableException' => '/php-code-coverage/Exception/PcovNotAvailableException.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Driver\\PhpdbgDriver' => '/php-code-coverage/Driver/PhpdbgDriver.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Driver\\PhpdbgNotAvailableException' => '/php-code-coverage/Exception/PhpdbgNotAvailableException.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Driver\\Selector' => '/php-code-coverage/Driver/Selector.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Driver\\WriteOperationFailedException' => '/php-code-coverage/Exception/WriteOperationFailedException.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Driver\\WrongXdebugVersionException' => '/php-code-coverage/Exception/WrongXdebugVersionException.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Driver\\Xdebug2Driver' => '/php-code-coverage/Driver/Xdebug2Driver.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Driver\\Xdebug2NotEnabledException' => '/php-code-coverage/Exception/Xdebug2NotEnabledException.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Driver\\Xdebug3Driver' => '/php-code-coverage/Driver/Xdebug3Driver.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Driver\\Xdebug3NotEnabledException' => '/php-code-coverage/Exception/Xdebug3NotEnabledException.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Driver\\XdebugNotAvailableException' => '/php-code-coverage/Exception/XdebugNotAvailableException.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Exception' => '/php-code-coverage/Exception/Exception.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Filter' => '/php-code-coverage/Filter.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\InvalidArgumentException' => '/php-code-coverage/Exception/InvalidArgumentException.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\NoCodeCoverageDriverAvailableException' => '/php-code-coverage/Exception/NoCodeCoverageDriverAvailableException.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\NoCodeCoverageDriverWithPathCoverageSupportAvailableException' => '/php-code-coverage/Exception/NoCodeCoverageDriverWithPathCoverageSupportAvailableException.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Node\\AbstractNode' => '/php-code-coverage/Node/AbstractNode.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Node\\Builder' => '/php-code-coverage/Node/Builder.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Node\\CrapIndex' => '/php-code-coverage/Node/CrapIndex.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Node\\Directory' => '/php-code-coverage/Node/Directory.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Node\\File' => '/php-code-coverage/Node/File.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Node\\Iterator' => '/php-code-coverage/Node/Iterator.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\ParserException' => '/php-code-coverage/Exception/ParserException.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\ProcessedCodeCoverageData' => '/php-code-coverage/ProcessedCodeCoverageData.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\RawCodeCoverageData' => '/php-code-coverage/RawCodeCoverageData.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\ReflectionException' => '/php-code-coverage/Exception/ReflectionException.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\ReportAlreadyFinalizedException' => '/php-code-coverage/Exception/ReportAlreadyFinalizedException.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Report\\Clover' => '/php-code-coverage/Report/Clover.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Report\\Cobertura' => '/php-code-coverage/Report/Cobertura.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Report\\Crap4j' => '/php-code-coverage/Report/Crap4j.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Report\\Html\\Dashboard' => '/php-code-coverage/Report/Html/Renderer/Dashboard.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Report\\Html\\Directory' => '/php-code-coverage/Report/Html/Renderer/Directory.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Report\\Html\\Facade' => '/php-code-coverage/Report/Html/Facade.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Report\\Html\\File' => '/php-code-coverage/Report/Html/Renderer/File.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Report\\Html\\Renderer' => '/php-code-coverage/Report/Html/Renderer.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Report\\PHP' => '/php-code-coverage/Report/PHP.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Report\\Text' => '/php-code-coverage/Report/Text.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Report\\Xml\\BuildInformation' => '/php-code-coverage/Report/Xml/BuildInformation.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Report\\Xml\\Coverage' => '/php-code-coverage/Report/Xml/Coverage.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Report\\Xml\\Directory' => '/php-code-coverage/Report/Xml/Directory.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Report\\Xml\\Facade' => '/php-code-coverage/Report/Xml/Facade.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Report\\Xml\\File' => '/php-code-coverage/Report/Xml/File.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Report\\Xml\\Method' => '/php-code-coverage/Report/Xml/Method.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Report\\Xml\\Node' => '/php-code-coverage/Report/Xml/Node.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Report\\Xml\\Project' => '/php-code-coverage/Report/Xml/Project.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Report\\Xml\\Report' => '/php-code-coverage/Report/Xml/Report.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Report\\Xml\\Source' => '/php-code-coverage/Report/Xml/Source.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Report\\Xml\\Tests' => '/php-code-coverage/Report/Xml/Tests.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Report\\Xml\\Totals' => '/php-code-coverage/Report/Xml/Totals.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Report\\Xml\\Unit' => '/php-code-coverage/Report/Xml/Unit.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\StaticAnalysisCacheNotConfiguredException' => '/php-code-coverage/Exception/StaticAnalysisCacheNotConfiguredException.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\StaticAnalysis\\CacheWarmer' => '/php-code-coverage/StaticAnalysis/CacheWarmer.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\StaticAnalysis\\CachingFileAnalyser' => '/php-code-coverage/StaticAnalysis/CachingFileAnalyser.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\StaticAnalysis\\CodeUnitFindingVisitor' => '/php-code-coverage/StaticAnalysis/CodeUnitFindingVisitor.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\StaticAnalysis\\ExecutableLinesFindingVisitor' => '/php-code-coverage/StaticAnalysis/ExecutableLinesFindingVisitor.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\StaticAnalysis\\FileAnalyser' => '/php-code-coverage/StaticAnalysis/FileAnalyser.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\StaticAnalysis\\IgnoredLinesFindingVisitor' => '/php-code-coverage/StaticAnalysis/IgnoredLinesFindingVisitor.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\StaticAnalysis\\ParsingFileAnalyser' => '/php-code-coverage/StaticAnalysis/ParsingFileAnalyser.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\TestIdMissingException' => '/php-code-coverage/Exception/TestIdMissingException.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\UnintentionallyCoveredCodeException' => '/php-code-coverage/Exception/UnintentionallyCoveredCodeException.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Util\\DirectoryCouldNotBeCreatedException' => '/php-code-coverage/Exception/DirectoryCouldNotBeCreatedException.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Util\\Filesystem' => '/php-code-coverage/Util/Filesystem.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Util\\Percentage' => '/php-code-coverage/Util/Percentage.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Version' => '/php-code-coverage/Version.php', - 'PHPUnit\\SebastianBergmann\\CodeCoverage\\XmlException' => '/php-code-coverage/Exception/XmlException.php', - 'PHPUnit\\SebastianBergmann\\CodeUnitReverseLookup\\Wizard' => '/sebastian-code-unit-reverse-lookup/Wizard.php', - 'PHPUnit\\SebastianBergmann\\CodeUnit\\ClassMethodUnit' => '/sebastian-code-unit/ClassMethodUnit.php', - 'PHPUnit\\SebastianBergmann\\CodeUnit\\ClassUnit' => '/sebastian-code-unit/ClassUnit.php', - 'PHPUnit\\SebastianBergmann\\CodeUnit\\CodeUnit' => '/sebastian-code-unit/CodeUnit.php', - 'PHPUnit\\SebastianBergmann\\CodeUnit\\CodeUnitCollection' => '/sebastian-code-unit/CodeUnitCollection.php', - 'PHPUnit\\SebastianBergmann\\CodeUnit\\CodeUnitCollectionIterator' => '/sebastian-code-unit/CodeUnitCollectionIterator.php', - 'PHPUnit\\SebastianBergmann\\CodeUnit\\Exception' => '/sebastian-code-unit/exceptions/Exception.php', - 'PHPUnit\\SebastianBergmann\\CodeUnit\\FunctionUnit' => '/sebastian-code-unit/FunctionUnit.php', - 'PHPUnit\\SebastianBergmann\\CodeUnit\\InterfaceMethodUnit' => '/sebastian-code-unit/InterfaceMethodUnit.php', - 'PHPUnit\\SebastianBergmann\\CodeUnit\\InterfaceUnit' => '/sebastian-code-unit/InterfaceUnit.php', - 'PHPUnit\\SebastianBergmann\\CodeUnit\\InvalidCodeUnitException' => '/sebastian-code-unit/exceptions/InvalidCodeUnitException.php', - 'PHPUnit\\SebastianBergmann\\CodeUnit\\Mapper' => '/sebastian-code-unit/Mapper.php', - 'PHPUnit\\SebastianBergmann\\CodeUnit\\NoTraitException' => '/sebastian-code-unit/exceptions/NoTraitException.php', - 'PHPUnit\\SebastianBergmann\\CodeUnit\\ReflectionException' => '/sebastian-code-unit/exceptions/ReflectionException.php', - 'PHPUnit\\SebastianBergmann\\CodeUnit\\TraitMethodUnit' => '/sebastian-code-unit/TraitMethodUnit.php', - 'PHPUnit\\SebastianBergmann\\CodeUnit\\TraitUnit' => '/sebastian-code-unit/TraitUnit.php', - 'PHPUnit\\SebastianBergmann\\Comparator\\ArrayComparator' => '/sebastian-comparator/ArrayComparator.php', - 'PHPUnit\\SebastianBergmann\\Comparator\\Comparator' => '/sebastian-comparator/Comparator.php', - 'PHPUnit\\SebastianBergmann\\Comparator\\ComparisonFailure' => '/sebastian-comparator/ComparisonFailure.php', - 'PHPUnit\\SebastianBergmann\\Comparator\\DOMNodeComparator' => '/sebastian-comparator/DOMNodeComparator.php', - 'PHPUnit\\SebastianBergmann\\Comparator\\DateTimeComparator' => '/sebastian-comparator/DateTimeComparator.php', - 'PHPUnit\\SebastianBergmann\\Comparator\\DoubleComparator' => '/sebastian-comparator/DoubleComparator.php', - 'PHPUnit\\SebastianBergmann\\Comparator\\Exception' => '/sebastian-comparator/exceptions/Exception.php', - 'PHPUnit\\SebastianBergmann\\Comparator\\ExceptionComparator' => '/sebastian-comparator/ExceptionComparator.php', - 'PHPUnit\\SebastianBergmann\\Comparator\\Factory' => '/sebastian-comparator/Factory.php', - 'PHPUnit\\SebastianBergmann\\Comparator\\MockObjectComparator' => '/sebastian-comparator/MockObjectComparator.php', - 'PHPUnit\\SebastianBergmann\\Comparator\\NumericComparator' => '/sebastian-comparator/NumericComparator.php', - 'PHPUnit\\SebastianBergmann\\Comparator\\ObjectComparator' => '/sebastian-comparator/ObjectComparator.php', - 'PHPUnit\\SebastianBergmann\\Comparator\\ResourceComparator' => '/sebastian-comparator/ResourceComparator.php', - 'PHPUnit\\SebastianBergmann\\Comparator\\RuntimeException' => '/sebastian-comparator/exceptions/RuntimeException.php', - 'PHPUnit\\SebastianBergmann\\Comparator\\ScalarComparator' => '/sebastian-comparator/ScalarComparator.php', - 'PHPUnit\\SebastianBergmann\\Comparator\\SplObjectStorageComparator' => '/sebastian-comparator/SplObjectStorageComparator.php', - 'PHPUnit\\SebastianBergmann\\Comparator\\TypeComparator' => '/sebastian-comparator/TypeComparator.php', - 'PHPUnit\\SebastianBergmann\\Complexity\\Calculator' => '/sebastian-complexity/Calculator.php', - 'PHPUnit\\SebastianBergmann\\Complexity\\Complexity' => '/sebastian-complexity/Complexity/Complexity.php', - 'PHPUnit\\SebastianBergmann\\Complexity\\ComplexityCalculatingVisitor' => '/sebastian-complexity/Visitor/ComplexityCalculatingVisitor.php', - 'PHPUnit\\SebastianBergmann\\Complexity\\ComplexityCollection' => '/sebastian-complexity/Complexity/ComplexityCollection.php', - 'PHPUnit\\SebastianBergmann\\Complexity\\ComplexityCollectionIterator' => '/sebastian-complexity/Complexity/ComplexityCollectionIterator.php', - 'PHPUnit\\SebastianBergmann\\Complexity\\CyclomaticComplexityCalculatingVisitor' => '/sebastian-complexity/Visitor/CyclomaticComplexityCalculatingVisitor.php', - 'PHPUnit\\SebastianBergmann\\Complexity\\Exception' => '/sebastian-complexity/Exception/Exception.php', - 'PHPUnit\\SebastianBergmann\\Complexity\\RuntimeException' => '/sebastian-complexity/Exception/RuntimeException.php', - 'PHPUnit\\SebastianBergmann\\Diff\\Chunk' => '/sebastian-diff/Chunk.php', - 'PHPUnit\\SebastianBergmann\\Diff\\ConfigurationException' => '/sebastian-diff/Exception/ConfigurationException.php', - 'PHPUnit\\SebastianBergmann\\Diff\\Diff' => '/sebastian-diff/Diff.php', - 'PHPUnit\\SebastianBergmann\\Diff\\Differ' => '/sebastian-diff/Differ.php', - 'PHPUnit\\SebastianBergmann\\Diff\\Exception' => '/sebastian-diff/Exception/Exception.php', - 'PHPUnit\\SebastianBergmann\\Diff\\InvalidArgumentException' => '/sebastian-diff/Exception/InvalidArgumentException.php', - 'PHPUnit\\SebastianBergmann\\Diff\\Line' => '/sebastian-diff/Line.php', - 'PHPUnit\\SebastianBergmann\\Diff\\LongestCommonSubsequenceCalculator' => '/sebastian-diff/LongestCommonSubsequenceCalculator.php', - 'PHPUnit\\SebastianBergmann\\Diff\\MemoryEfficientLongestCommonSubsequenceCalculator' => '/sebastian-diff/MemoryEfficientLongestCommonSubsequenceCalculator.php', - 'PHPUnit\\SebastianBergmann\\Diff\\Output\\AbstractChunkOutputBuilder' => '/sebastian-diff/Output/AbstractChunkOutputBuilder.php', - 'PHPUnit\\SebastianBergmann\\Diff\\Output\\DiffOnlyOutputBuilder' => '/sebastian-diff/Output/DiffOnlyOutputBuilder.php', - 'PHPUnit\\SebastianBergmann\\Diff\\Output\\DiffOutputBuilderInterface' => '/sebastian-diff/Output/DiffOutputBuilderInterface.php', - 'PHPUnit\\SebastianBergmann\\Diff\\Output\\StrictUnifiedDiffOutputBuilder' => '/sebastian-diff/Output/StrictUnifiedDiffOutputBuilder.php', - 'PHPUnit\\SebastianBergmann\\Diff\\Output\\UnifiedDiffOutputBuilder' => '/sebastian-diff/Output/UnifiedDiffOutputBuilder.php', - 'PHPUnit\\SebastianBergmann\\Diff\\Parser' => '/sebastian-diff/Parser.php', - 'PHPUnit\\SebastianBergmann\\Diff\\TimeEfficientLongestCommonSubsequenceCalculator' => '/sebastian-diff/TimeEfficientLongestCommonSubsequenceCalculator.php', - 'PHPUnit\\SebastianBergmann\\Environment\\Console' => '/sebastian-environment/Console.php', - 'PHPUnit\\SebastianBergmann\\Environment\\OperatingSystem' => '/sebastian-environment/OperatingSystem.php', - 'PHPUnit\\SebastianBergmann\\Environment\\Runtime' => '/sebastian-environment/Runtime.php', - 'PHPUnit\\SebastianBergmann\\Exporter\\Exporter' => '/sebastian-exporter/Exporter.php', - 'PHPUnit\\SebastianBergmann\\FileIterator\\Facade' => '/php-file-iterator/Facade.php', - 'PHPUnit\\SebastianBergmann\\FileIterator\\Factory' => '/php-file-iterator/Factory.php', - 'PHPUnit\\SebastianBergmann\\FileIterator\\Iterator' => '/php-file-iterator/Iterator.php', - 'PHPUnit\\SebastianBergmann\\GlobalState\\CodeExporter' => '/sebastian-global-state/CodeExporter.php', - 'PHPUnit\\SebastianBergmann\\GlobalState\\Exception' => '/sebastian-global-state/exceptions/Exception.php', - 'PHPUnit\\SebastianBergmann\\GlobalState\\ExcludeList' => '/sebastian-global-state/ExcludeList.php', - 'PHPUnit\\SebastianBergmann\\GlobalState\\Restorer' => '/sebastian-global-state/Restorer.php', - 'PHPUnit\\SebastianBergmann\\GlobalState\\RuntimeException' => '/sebastian-global-state/exceptions/RuntimeException.php', - 'PHPUnit\\SebastianBergmann\\GlobalState\\Snapshot' => '/sebastian-global-state/Snapshot.php', - 'PHPUnit\\SebastianBergmann\\Invoker\\Exception' => '/php-invoker/exceptions/Exception.php', - 'PHPUnit\\SebastianBergmann\\Invoker\\Invoker' => '/php-invoker/Invoker.php', - 'PHPUnit\\SebastianBergmann\\Invoker\\ProcessControlExtensionNotLoadedException' => '/php-invoker/exceptions/ProcessControlExtensionNotLoadedException.php', - 'PHPUnit\\SebastianBergmann\\Invoker\\TimeoutException' => '/php-invoker/exceptions/TimeoutException.php', - 'PHPUnit\\SebastianBergmann\\LinesOfCode\\Counter' => '/sebastian-lines-of-code/Counter.php', - 'PHPUnit\\SebastianBergmann\\LinesOfCode\\Exception' => '/sebastian-lines-of-code/Exception/Exception.php', - 'PHPUnit\\SebastianBergmann\\LinesOfCode\\IllogicalValuesException' => '/sebastian-lines-of-code/Exception/IllogicalValuesException.php', - 'PHPUnit\\SebastianBergmann\\LinesOfCode\\LineCountingVisitor' => '/sebastian-lines-of-code/LineCountingVisitor.php', - 'PHPUnit\\SebastianBergmann\\LinesOfCode\\LinesOfCode' => '/sebastian-lines-of-code/LinesOfCode.php', - 'PHPUnit\\SebastianBergmann\\LinesOfCode\\NegativeValueException' => '/sebastian-lines-of-code/Exception/NegativeValueException.php', - 'PHPUnit\\SebastianBergmann\\LinesOfCode\\RuntimeException' => '/sebastian-lines-of-code/Exception/RuntimeException.php', - 'PHPUnit\\SebastianBergmann\\ObjectEnumerator\\Enumerator' => '/sebastian-object-enumerator/Enumerator.php', - 'PHPUnit\\SebastianBergmann\\ObjectEnumerator\\Exception' => '/sebastian-object-enumerator/Exception.php', - 'PHPUnit\\SebastianBergmann\\ObjectEnumerator\\InvalidArgumentException' => '/sebastian-object-enumerator/InvalidArgumentException.php', - 'PHPUnit\\SebastianBergmann\\ObjectReflector\\Exception' => '/sebastian-object-reflector/Exception.php', - 'PHPUnit\\SebastianBergmann\\ObjectReflector\\InvalidArgumentException' => '/sebastian-object-reflector/InvalidArgumentException.php', - 'PHPUnit\\SebastianBergmann\\ObjectReflector\\ObjectReflector' => '/sebastian-object-reflector/ObjectReflector.php', - 'PHPUnit\\SebastianBergmann\\RecursionContext\\Context' => '/sebastian-recursion-context/Context.php', - 'PHPUnit\\SebastianBergmann\\RecursionContext\\Exception' => '/sebastian-recursion-context/Exception.php', - 'PHPUnit\\SebastianBergmann\\RecursionContext\\InvalidArgumentException' => '/sebastian-recursion-context/InvalidArgumentException.php', - 'PHPUnit\\SebastianBergmann\\ResourceOperations\\ResourceOperations' => '/sebastian-resource-operations/ResourceOperations.php', - 'PHPUnit\\SebastianBergmann\\Template\\Exception' => '/php-text-template/exceptions/Exception.php', - 'PHPUnit\\SebastianBergmann\\Template\\InvalidArgumentException' => '/php-text-template/exceptions/InvalidArgumentException.php', - 'PHPUnit\\SebastianBergmann\\Template\\RuntimeException' => '/php-text-template/exceptions/RuntimeException.php', - 'PHPUnit\\SebastianBergmann\\Template\\Template' => '/php-text-template/Template.php', - 'PHPUnit\\SebastianBergmann\\Timer\\Duration' => '/php-timer/Duration.php', - 'PHPUnit\\SebastianBergmann\\Timer\\Exception' => '/php-timer/exceptions/Exception.php', - 'PHPUnit\\SebastianBergmann\\Timer\\NoActiveTimerException' => '/php-timer/exceptions/NoActiveTimerException.php', - 'PHPUnit\\SebastianBergmann\\Timer\\ResourceUsageFormatter' => '/php-timer/ResourceUsageFormatter.php', - 'PHPUnit\\SebastianBergmann\\Timer\\TimeSinceStartOfRequestNotAvailableException' => '/php-timer/exceptions/TimeSinceStartOfRequestNotAvailableException.php', - 'PHPUnit\\SebastianBergmann\\Timer\\Timer' => '/php-timer/Timer.php', - 'PHPUnit\\SebastianBergmann\\Type\\CallableType' => '/sebastian-type/type/CallableType.php', - 'PHPUnit\\SebastianBergmann\\Type\\Exception' => '/sebastian-type/exception/Exception.php', - 'PHPUnit\\SebastianBergmann\\Type\\FalseType' => '/sebastian-type/type/FalseType.php', - 'PHPUnit\\SebastianBergmann\\Type\\GenericObjectType' => '/sebastian-type/type/GenericObjectType.php', - 'PHPUnit\\SebastianBergmann\\Type\\IntersectionType' => '/sebastian-type/type/IntersectionType.php', - 'PHPUnit\\SebastianBergmann\\Type\\IterableType' => '/sebastian-type/type/IterableType.php', - 'PHPUnit\\SebastianBergmann\\Type\\MixedType' => '/sebastian-type/type/MixedType.php', - 'PHPUnit\\SebastianBergmann\\Type\\NeverType' => '/sebastian-type/type/NeverType.php', - 'PHPUnit\\SebastianBergmann\\Type\\NullType' => '/sebastian-type/type/NullType.php', - 'PHPUnit\\SebastianBergmann\\Type\\ObjectType' => '/sebastian-type/type/ObjectType.php', - 'PHPUnit\\SebastianBergmann\\Type\\Parameter' => '/sebastian-type/Parameter.php', - 'PHPUnit\\SebastianBergmann\\Type\\ReflectionMapper' => '/sebastian-type/ReflectionMapper.php', - 'PHPUnit\\SebastianBergmann\\Type\\RuntimeException' => '/sebastian-type/exception/RuntimeException.php', - 'PHPUnit\\SebastianBergmann\\Type\\SimpleType' => '/sebastian-type/type/SimpleType.php', - 'PHPUnit\\SebastianBergmann\\Type\\StaticType' => '/sebastian-type/type/StaticType.php', - 'PHPUnit\\SebastianBergmann\\Type\\TrueType' => '/sebastian-type/type/TrueType.php', - 'PHPUnit\\SebastianBergmann\\Type\\Type' => '/sebastian-type/type/Type.php', - 'PHPUnit\\SebastianBergmann\\Type\\TypeName' => '/sebastian-type/TypeName.php', - 'PHPUnit\\SebastianBergmann\\Type\\UnionType' => '/sebastian-type/type/UnionType.php', - 'PHPUnit\\SebastianBergmann\\Type\\UnknownType' => '/sebastian-type/type/UnknownType.php', - 'PHPUnit\\SebastianBergmann\\Type\\VoidType' => '/sebastian-type/type/VoidType.php', - 'PHPUnit\\SebastianBergmann\\Version' => '/sebastian-version/Version.php', 'PHPUnit\\TextUI\\CliArguments\\Builder' => '/phpunit/TextUI/CliArguments/Builder.php', 'PHPUnit\\TextUI\\CliArguments\\Configuration' => '/phpunit/TextUI/CliArguments/Configuration.php', 'PHPUnit\\TextUI\\CliArguments\\Exception' => '/phpunit/TextUI/CliArguments/Exception.php', @@ -2049,14 +2352,6 @@ foreach (['PHPUnit\\DeepCopy\\DeepCopy' => '/myclabs-deep-copy/DeepCopy/DeepCopy 'PHPUnit\\TextUI\\XmlConfiguration\\Variable' => '/phpunit/TextUI/XmlConfiguration/PHP/Variable.php', 'PHPUnit\\TextUI\\XmlConfiguration\\VariableCollection' => '/phpunit/TextUI/XmlConfiguration/PHP/VariableCollection.php', 'PHPUnit\\TextUI\\XmlConfiguration\\VariableCollectionIterator' => '/phpunit/TextUI/XmlConfiguration/PHP/VariableCollectionIterator.php', - 'PHPUnit\\TheSeer\\Tokenizer\\Exception' => '/theseer-tokenizer/Exception.php', - 'PHPUnit\\TheSeer\\Tokenizer\\NamespaceUri' => '/theseer-tokenizer/NamespaceUri.php', - 'PHPUnit\\TheSeer\\Tokenizer\\NamespaceUriException' => '/theseer-tokenizer/NamespaceUriException.php', - 'PHPUnit\\TheSeer\\Tokenizer\\Token' => '/theseer-tokenizer/Token.php', - 'PHPUnit\\TheSeer\\Tokenizer\\TokenCollection' => '/theseer-tokenizer/TokenCollection.php', - 'PHPUnit\\TheSeer\\Tokenizer\\TokenCollectionException' => '/theseer-tokenizer/TokenCollectionException.php', - 'PHPUnit\\TheSeer\\Tokenizer\\Tokenizer' => '/theseer-tokenizer/Tokenizer.php', - 'PHPUnit\\TheSeer\\Tokenizer\\XMLSerializer' => '/theseer-tokenizer/XMLSerializer.php', 'PHPUnit\\Util\\Annotation\\DocBlock' => '/phpunit/Util/Annotation/DocBlock.php', 'PHPUnit\\Util\\Annotation\\Registry' => '/phpunit/Util/Annotation/Registry.php', 'PHPUnit\\Util\\Blacklist' => '/phpunit/Util/Blacklist.php', @@ -2103,105 +2398,6 @@ foreach (['PHPUnit\\DeepCopy\\DeepCopy' => '/myclabs-deep-copy/DeepCopy/DeepCopy 'PHPUnit\\Util\\Xml\\SuccessfulSchemaDetectionResult' => '/phpunit/Util/Xml/SuccessfulSchemaDetectionResult.php', 'PHPUnit\\Util\\Xml\\ValidationResult' => '/phpunit/Util/Xml/ValidationResult.php', 'PHPUnit\\Util\\Xml\\Validator' => '/phpunit/Util/Xml/Validator.php', - 'PHPUnit\\Webmozart\\Assert\\Assert' => '/webmozart-assert/Assert.php', - 'PHPUnit\\Webmozart\\Assert\\InvalidArgumentException' => '/webmozart-assert/InvalidArgumentException.php', - 'PHPUnit\\Webmozart\\Assert\\Mixin' => '/webmozart-assert/Mixin.php', - 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock' => '/phpdocumentor-reflection-docblock/DocBlock.php', - 'PHPUnit\\phpDocumentor\\Reflection\\DocBlockFactory' => '/phpdocumentor-reflection-docblock/DocBlockFactory.php', - 'PHPUnit\\phpDocumentor\\Reflection\\DocBlockFactoryInterface' => '/phpdocumentor-reflection-docblock/DocBlockFactoryInterface.php', - 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Description' => '/phpdocumentor-reflection-docblock/DocBlock/Description.php', - 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\DescriptionFactory' => '/phpdocumentor-reflection-docblock/DocBlock/DescriptionFactory.php', - 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\ExampleFinder' => '/phpdocumentor-reflection-docblock/DocBlock/ExampleFinder.php', - 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Serializer' => '/phpdocumentor-reflection-docblock/DocBlock/Serializer.php', - 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\StandardTagFactory' => '/phpdocumentor-reflection-docblock/DocBlock/StandardTagFactory.php', - 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Tag' => '/phpdocumentor-reflection-docblock/DocBlock/Tag.php', - 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\TagFactory' => '/phpdocumentor-reflection-docblock/DocBlock/TagFactory.php', - 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Author' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Author.php', - 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Tags\\BaseTag' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/BaseTag.php', - 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Covers' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Covers.php', - 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Deprecated' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Deprecated.php', - 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Example' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Example.php', - 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Factory\\StaticMethod' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Factory/StaticMethod.php', - 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Formatter' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Formatter.php', - 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Formatter\\AlignFormatter' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Formatter/AlignFormatter.php', - 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Formatter\\PassthroughFormatter' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Formatter/PassthroughFormatter.php', - 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Generic' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Generic.php', - 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Tags\\InvalidTag' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/InvalidTag.php', - 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Link' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Link.php', - 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Method' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Method.php', - 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Param' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Param.php', - 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Property' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Property.php', - 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Tags\\PropertyRead' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/PropertyRead.php', - 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Tags\\PropertyWrite' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/PropertyWrite.php', - 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Reference\\Fqsen' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Reference/Fqsen.php', - 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Reference\\Reference' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Reference/Reference.php', - 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Reference\\Url' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Reference/Url.php', - 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Return_' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Return_.php', - 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Tags\\See' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/See.php', - 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Since' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Since.php', - 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Source' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Source.php', - 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Tags\\TagWithType' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/TagWithType.php', - 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Throws' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Throws.php', - 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Uses' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Uses.php', - 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Var_' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Var_.php', - 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Version' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Version.php', - 'PHPUnit\\phpDocumentor\\Reflection\\Element' => '/phpdocumentor-reflection-common/Element.php', - 'PHPUnit\\phpDocumentor\\Reflection\\Exception\\PcreException' => '/phpdocumentor-reflection-docblock/Exception/PcreException.php', - 'PHPUnit\\phpDocumentor\\Reflection\\File' => '/phpdocumentor-reflection-common/File.php', - 'PHPUnit\\phpDocumentor\\Reflection\\Fqsen' => '/phpdocumentor-reflection-common/Fqsen.php', - 'PHPUnit\\phpDocumentor\\Reflection\\FqsenResolver' => '/phpdocumentor-type-resolver/FqsenResolver.php', - 'PHPUnit\\phpDocumentor\\Reflection\\Location' => '/phpdocumentor-reflection-common/Location.php', - 'PHPUnit\\phpDocumentor\\Reflection\\Project' => '/phpdocumentor-reflection-common/Project.php', - 'PHPUnit\\phpDocumentor\\Reflection\\ProjectFactory' => '/phpdocumentor-reflection-common/ProjectFactory.php', - 'PHPUnit\\phpDocumentor\\Reflection\\PseudoType' => '/phpdocumentor-type-resolver/PseudoType.php', - 'PHPUnit\\phpDocumentor\\Reflection\\PseudoTypes\\CallableString' => '/phpdocumentor-type-resolver/PseudoTypes/CallableString.php', - 'PHPUnit\\phpDocumentor\\Reflection\\PseudoTypes\\False_' => '/phpdocumentor-type-resolver/PseudoTypes/False_.php', - 'PHPUnit\\phpDocumentor\\Reflection\\PseudoTypes\\HtmlEscapedString' => '/phpdocumentor-type-resolver/PseudoTypes/HtmlEscapedString.php', - 'PHPUnit\\phpDocumentor\\Reflection\\PseudoTypes\\IntegerRange' => '/phpdocumentor-type-resolver/PseudoTypes/IntegerRange.php', - 'PHPUnit\\phpDocumentor\\Reflection\\PseudoTypes\\List_' => '/phpdocumentor-type-resolver/PseudoTypes/List_.php', - 'PHPUnit\\phpDocumentor\\Reflection\\PseudoTypes\\LiteralString' => '/phpdocumentor-type-resolver/PseudoTypes/LiteralString.php', - 'PHPUnit\\phpDocumentor\\Reflection\\PseudoTypes\\LowercaseString' => '/phpdocumentor-type-resolver/PseudoTypes/LowercaseString.php', - 'PHPUnit\\phpDocumentor\\Reflection\\PseudoTypes\\NegativeInteger' => '/phpdocumentor-type-resolver/PseudoTypes/NegativeInteger.php', - 'PHPUnit\\phpDocumentor\\Reflection\\PseudoTypes\\NonEmptyLowercaseString' => '/phpdocumentor-type-resolver/PseudoTypes/NonEmptyLowercaseString.php', - 'PHPUnit\\phpDocumentor\\Reflection\\PseudoTypes\\NonEmptyString' => '/phpdocumentor-type-resolver/PseudoTypes/NonEmptyString.php', - 'PHPUnit\\phpDocumentor\\Reflection\\PseudoTypes\\NumericString' => '/phpdocumentor-type-resolver/PseudoTypes/NumericString.php', - 'PHPUnit\\phpDocumentor\\Reflection\\PseudoTypes\\Numeric_' => '/phpdocumentor-type-resolver/PseudoTypes/Numeric_.php', - 'PHPUnit\\phpDocumentor\\Reflection\\PseudoTypes\\PositiveInteger' => '/phpdocumentor-type-resolver/PseudoTypes/PositiveInteger.php', - 'PHPUnit\\phpDocumentor\\Reflection\\PseudoTypes\\TraitString' => '/phpdocumentor-type-resolver/PseudoTypes/TraitString.php', - 'PHPUnit\\phpDocumentor\\Reflection\\PseudoTypes\\True_' => '/phpdocumentor-type-resolver/PseudoTypes/True_.php', - 'PHPUnit\\phpDocumentor\\Reflection\\Type' => '/phpdocumentor-type-resolver/Type.php', - 'PHPUnit\\phpDocumentor\\Reflection\\TypeResolver' => '/phpdocumentor-type-resolver/TypeResolver.php', - 'PHPUnit\\phpDocumentor\\Reflection\\Types\\AbstractList' => '/phpdocumentor-type-resolver/Types/AbstractList.php', - 'PHPUnit\\phpDocumentor\\Reflection\\Types\\AggregatedType' => '/phpdocumentor-type-resolver/Types/AggregatedType.php', - 'PHPUnit\\phpDocumentor\\Reflection\\Types\\ArrayKey' => '/phpdocumentor-type-resolver/Types/ArrayKey.php', - 'PHPUnit\\phpDocumentor\\Reflection\\Types\\Array_' => '/phpdocumentor-type-resolver/Types/Array_.php', - 'PHPUnit\\phpDocumentor\\Reflection\\Types\\Boolean' => '/phpdocumentor-type-resolver/Types/Boolean.php', - 'PHPUnit\\phpDocumentor\\Reflection\\Types\\Callable_' => '/phpdocumentor-type-resolver/Types/Callable_.php', - 'PHPUnit\\phpDocumentor\\Reflection\\Types\\ClassString' => '/phpdocumentor-type-resolver/Types/ClassString.php', - 'PHPUnit\\phpDocumentor\\Reflection\\Types\\Collection' => '/phpdocumentor-type-resolver/Types/Collection.php', - 'PHPUnit\\phpDocumentor\\Reflection\\Types\\Compound' => '/phpdocumentor-type-resolver/Types/Compound.php', - 'PHPUnit\\phpDocumentor\\Reflection\\Types\\Context' => '/phpdocumentor-type-resolver/Types/Context.php', - 'PHPUnit\\phpDocumentor\\Reflection\\Types\\ContextFactory' => '/phpdocumentor-type-resolver/Types/ContextFactory.php', - 'PHPUnit\\phpDocumentor\\Reflection\\Types\\Expression' => '/phpdocumentor-type-resolver/Types/Expression.php', - 'PHPUnit\\phpDocumentor\\Reflection\\Types\\Float_' => '/phpdocumentor-type-resolver/Types/Float_.php', - 'PHPUnit\\phpDocumentor\\Reflection\\Types\\Integer' => '/phpdocumentor-type-resolver/Types/Integer.php', - 'PHPUnit\\phpDocumentor\\Reflection\\Types\\InterfaceString' => '/phpdocumentor-type-resolver/Types/InterfaceString.php', - 'PHPUnit\\phpDocumentor\\Reflection\\Types\\Intersection' => '/phpdocumentor-type-resolver/Types/Intersection.php', - 'PHPUnit\\phpDocumentor\\Reflection\\Types\\Iterable_' => '/phpdocumentor-type-resolver/Types/Iterable_.php', - 'PHPUnit\\phpDocumentor\\Reflection\\Types\\Mixed_' => '/phpdocumentor-type-resolver/Types/Mixed_.php', - 'PHPUnit\\phpDocumentor\\Reflection\\Types\\Never_' => '/phpdocumentor-type-resolver/Types/Never_.php', - 'PHPUnit\\phpDocumentor\\Reflection\\Types\\Null_' => '/phpdocumentor-type-resolver/Types/Null_.php', - 'PHPUnit\\phpDocumentor\\Reflection\\Types\\Nullable' => '/phpdocumentor-type-resolver/Types/Nullable.php', - 'PHPUnit\\phpDocumentor\\Reflection\\Types\\Object_' => '/phpdocumentor-type-resolver/Types/Object_.php', - 'PHPUnit\\phpDocumentor\\Reflection\\Types\\Parent_' => '/phpdocumentor-type-resolver/Types/Parent_.php', - 'PHPUnit\\phpDocumentor\\Reflection\\Types\\Resource_' => '/phpdocumentor-type-resolver/Types/Resource_.php', - 'PHPUnit\\phpDocumentor\\Reflection\\Types\\Scalar' => '/phpdocumentor-type-resolver/Types/Scalar.php', - 'PHPUnit\\phpDocumentor\\Reflection\\Types\\Self_' => '/phpdocumentor-type-resolver/Types/Self_.php', - 'PHPUnit\\phpDocumentor\\Reflection\\Types\\Static_' => '/phpdocumentor-type-resolver/Types/Static_.php', - 'PHPUnit\\phpDocumentor\\Reflection\\Types\\String_' => '/phpdocumentor-type-resolver/Types/String_.php', - 'PHPUnit\\phpDocumentor\\Reflection\\Types\\This' => '/phpdocumentor-type-resolver/Types/This.php', - 'PHPUnit\\phpDocumentor\\Reflection\\Types\\Void_' => '/phpdocumentor-type-resolver/Types/Void_.php', - 'PHPUnit\\phpDocumentor\\Reflection\\Utils' => '/phpdocumentor-reflection-docblock/Utils.php', 'Prophecy\\Argument' => '/phpspec-prophecy/Prophecy/Argument.php', 'Prophecy\\Argument\\ArgumentsWildcard' => '/phpspec-prophecy/Prophecy/Argument/ArgumentsWildcard.php', 'Prophecy\\Argument\\Token\\AnyValueToken' => '/phpspec-prophecy/Prophecy/Argument/Token/AnyValueToken.php', @@ -2295,12 +2491,18 @@ foreach (['PHPUnit\\DeepCopy\\DeepCopy' => '/myclabs-deep-copy/DeepCopy/DeepCopy 'Prophecy\\Prophet' => '/phpspec-prophecy/Prophecy/Prophet.php', 'Prophecy\\Util\\ExportUtil' => '/phpspec-prophecy/Prophecy/Util/ExportUtil.php', 'Prophecy\\Util\\StringUtil' => '/phpspec-prophecy/Prophecy/Util/StringUtil.php'] as $file) { - require_once 'phar://phpunit-9.6.13.phar' . $file; + require_once 'phar://phpunit-9.6.19.phar' . $file; } require __PHPUNIT_PHAR_ROOT__ . '/phpunit/Framework/Assert/Functions.php'; if ($execute) { + if (isset($printComposerLock)) { + print file_get_contents(__PHPUNIT_PHAR_ROOT__ . '/composer.lock'); + + exit; + } + if (isset($printManifest)) { print file_get_contents(__PHPUNIT_PHAR_ROOT__ . '/manifest.txt'); @@ -2319,106 +2521,2466 @@ if ($execute) { } __HALT_COMPILER(); ?> -kphpunit-9.6.13.pharLdoctrine-instantiator/Doctrine/Instantiator/Exception/ExceptionInterface.php(4 ebRdoctrine-instantiator/Doctrine/Instantiator/Exception/InvalidArgumentException.php(4 eRdoctrine-instantiator/Doctrine/Instantiator/Exception/UnexpectedValueException.php:(4 e:_Y%[<doctrine-instantiator/Doctrine/Instantiator/Instantiator.php(4 e87Edoctrine-instantiator/Doctrine/Instantiator/InstantiatorInterface.php (4 e LȤdoctrine-instantiator/LICENSE$(4 e$ -͂ manifest.txt(4 eRoa'myclabs-deep-copy/DeepCopy/DeepCopy.php(4 eLä7myclabs-deep-copy/DeepCopy/Exception/CloneException.php(4 e {ˤ:myclabs-deep-copy/DeepCopy/Exception/PropertyException.php(4 e3Gz5myclabs-deep-copy/DeepCopy/Filter/ChainableFilter.php(4 eTE Gmyclabs-deep-copy/DeepCopy/Filter/Doctrine/DoctrineCollectionFilter.php -(4 e -DgLmyclabs-deep-copy/DeepCopy/Filter/Doctrine/DoctrineEmptyCollectionFilter.php(4 e)$Bmyclabs-deep-copy/DeepCopy/Filter/Doctrine/DoctrineProxyFilter.php(4 e),myclabs-deep-copy/DeepCopy/Filter/Filter.phpd(4 edM0myclabs-deep-copy/DeepCopy/Filter/KeepFilter.php(4 eYn3myclabs-deep-copy/DeepCopy/Filter/ReplaceFilter.php(4 e3myclabs-deep-copy/DeepCopy/Filter/SetNullFilter.php(4 e䊉Dmyclabs-deep-copy/DeepCopy/Matcher/Doctrine/DoctrineProxyMatcher.php(4 epr.myclabs-deep-copy/DeepCopy/Matcher/Matcher.php(4 e6myclabs-deep-copy/DeepCopy/Matcher/PropertyMatcher.php(4 e=Bv:myclabs-deep-copy/DeepCopy/Matcher/PropertyNameMatcher.php(4 eR:myclabs-deep-copy/DeepCopy/Matcher/PropertyTypeMatcher.php2(4 e2ZQͤ:myclabs-deep-copy/DeepCopy/Reflection/ReflectionHelper.php5(4 e5ىAmyclabs-deep-copy/DeepCopy/TypeFilter/Date/DateIntervalFilter.php(4 eƤ7myclabs-deep-copy/DeepCopy/TypeFilter/ReplaceFilter.php(4 ez;myclabs-deep-copy/DeepCopy/TypeFilter/ShallowCopyFilter.php(4 eؤ?myclabs-deep-copy/DeepCopy/TypeFilter/Spl/ArrayObjectFilter.php(4 e^Amyclabs-deep-copy/DeepCopy/TypeFilter/Spl/SplDoublyLinkedList.php(4 ev|Gmyclabs-deep-copy/DeepCopy/TypeFilter/Spl/SplDoublyLinkedListFilter.php(4 eT+4myclabs-deep-copy/DeepCopy/TypeFilter/TypeFilter.php(4 eVD6myclabs-deep-copy/DeepCopy/TypeMatcher/TypeMatcher.php(4 eQBŤ(myclabs-deep-copy/DeepCopy/deep_copy.php(4 erxmyclabs-deep-copy/LICENSE5(4 e5ʭ˄nikic-php-parser/LICENSE(4 e*&nikic-php-parser/PhpParser/Builder.php(4 e61nikic-php-parser/PhpParser/Builder/ClassConst.php(4 e-nikic-php-parser/PhpParser/Builder/Class_.php(4 ec32nikic-php-parser/PhpParser/Builder/Declaration.php(4 eE7/nikic-php-parser/PhpParser/Builder/EnumCase.php^(4 e^ueT,nikic-php-parser/PhpParser/Builder/Enum_.php (4 e #3nikic-php-parser/PhpParser/Builder/FunctionLike.php(4 eZqe0nikic-php-parser/PhpParser/Builder/Function_.phpF(4 eFux1nikic-php-parser/PhpParser/Builder/Interface_.php (4 e -nikic-php-parser/PhpParser/Builder/Method.php(4 e}1nikic-php-parser/PhpParser/Builder/Namespace_.php:(4 e:ˆp,nikic-php-parser/PhpParser/Builder/Param.php{(4 e{j4/nikic-php-parser/PhpParser/Builder/Property.php|(4 e|O /nikic-php-parser/PhpParser/Builder/TraitUse.phpW(4 eWL@9nikic-php-parser/PhpParser/Builder/TraitUseAdaptation.php(4 eUVx-nikic-php-parser/PhpParser/Builder/Trait_.php(4 ekj+nikic-php-parser/PhpParser/Builder/Use_.php(4 es-nikic-php-parser/PhpParser/BuilderFactory.php+(4 e+ $-nikic-php-parser/PhpParser/BuilderHelpers.php$(4 e$:@&nikic-php-parser/PhpParser/Comment.php(4 eA*nikic-php-parser/PhpParser/Comment/Doc.phpx(4 exp;nikic-php-parser/PhpParser/ConstExprEvaluationException.php_(4 e_I 1nikic-php-parser/PhpParser/ConstExprEvaluator.phpl%(4 el%evQ$nikic-php-parser/PhpParser/Error.php(4 eQZ+nikic-php-parser/PhpParser/ErrorHandler.php/(4 e/#\6nikic-php-parser/PhpParser/ErrorHandler/Collecting.php(4 e&Ȥ4nikic-php-parser/PhpParser/ErrorHandler/Throwing.php(4 eS}<0nikic-php-parser/PhpParser/Internal/DiffElem.php7(4 e7$.nikic-php-parser/PhpParser/Internal/Differ.php-(4 e-^Anikic-php-parser/PhpParser/Internal/PrintableNewAnonClassNode.php(4 e<3nikic-php-parser/PhpParser/Internal/TokenStream.php#(4 e#f*nikic-php-parser/PhpParser/JsonDecoder.php (4 e xg$nikic-php-parser/PhpParser/Lexer.phpyZ(4 eyZq⃤.nikic-php-parser/PhpParser/Lexer/Emulative.phpO#(4 eO#ܲݤDnikic-php-parser/PhpParser/Lexer/TokenEmulator/AttributeEmulator.php(4 erLnikic-php-parser/PhpParser/Lexer/TokenEmulator/CoaleseEqualTokenEmulator.php (4 e *§oDnikic-php-parser/PhpParser/Lexer/TokenEmulator/EnumTokenEmulator.php(4 eLFHnikic-php-parser/PhpParser/Lexer/TokenEmulator/ExplicitOctalEmulator.php(4 e*#Lnikic-php-parser/PhpParser/Lexer/TokenEmulator/FlexibleDocStringEmulator.phpn (4 en 1Bnikic-php-parser/PhpParser/Lexer/TokenEmulator/FnTokenEmulator.php(4 ejBnikic-php-parser/PhpParser/Lexer/TokenEmulator/KeywordEmulator.php(4 e`atEnikic-php-parser/PhpParser/Lexer/TokenEmulator/MatchTokenEmulator.php(4 ec/Hnikic-php-parser/PhpParser/Lexer/TokenEmulator/NullsafeTokenEmulator.php(4 e:&ERnikic-php-parser/PhpParser/Lexer/TokenEmulator/NumericLiteralSeparatorEmulator.phpV(4 eVPnikic-php-parser/PhpParser/Lexer/TokenEmulator/ReadonlyFunctionTokenEmulator.php(4 ee!ćHnikic-php-parser/PhpParser/Lexer/TokenEmulator/ReadonlyTokenEmulator.phpL(4 eL -`9JBnikic-php-parser/PhpParser/Lexer/TokenEmulator/ReverseEmulator.php(4 eI}@nikic-php-parser/PhpParser/Lexer/TokenEmulator/TokenEmulator.phpu(4 euD4h*nikic-php-parser/PhpParser/NameContext.php%(4 e%G-#nikic-php-parser/PhpParser/Node.php(4 eyݗ'nikic-php-parser/PhpParser/Node/Arg.php0(4 e0q H-nikic-php-parser/PhpParser/Node/Attribute.phpH(4 eHhqK2nikic-php-parser/PhpParser/Node/AttributeGroup.php(4 eB9/nikic-php-parser/PhpParser/Node/ComplexType.phpS(4 eS(*nikic-php-parser/PhpParser/Node/Const_.php(4 eZ(nikic-php-parser/PhpParser/Node/Expr.php(4 eh傤6nikic-php-parser/PhpParser/Node/Expr/ArrayDimFetch.phpM(4 eMIY2nikic-php-parser/PhpParser/Node/Expr/ArrayItem.phpx(4 ex| 2/nikic-php-parser/PhpParser/Node/Expr/Array_.php8(4 e8;p6nikic-php-parser/PhpParser/Node/Expr/ArrowFunction.php (4 e w3/nikic-php-parser/PhpParser/Node/Expr/Assign.php(4 e1nikic-php-parser/PhpParser/Node/Expr/AssignOp.php(4 e,<nikic-php-parser/PhpParser/Node/Expr/AssignOp/BitwiseAnd.php(4 eu;nikic-php-parser/PhpParser/Node/Expr/AssignOp/BitwiseOr.php(4 e;<nikic-php-parser/PhpParser/Node/Expr/AssignOp/BitwiseXor.php(4 elϚ:nikic-php-parser/PhpParser/Node/Expr/AssignOp/Coalesce.php(4 eq,8nikic-php-parser/PhpParser/Node/Expr/AssignOp/Concat.php(4 e5nikic-php-parser/PhpParser/Node/Expr/AssignOp/Div.php(4 eYP -7nikic-php-parser/PhpParser/Node/Expr/AssignOp/Minus.php(4 e隤5nikic-php-parser/PhpParser/Node/Expr/AssignOp/Mod.php(4 e]10Y5nikic-php-parser/PhpParser/Node/Expr/AssignOp/Mul.php(4 eπ/6nikic-php-parser/PhpParser/Node/Expr/AssignOp/Plus.php(4 e&|5nikic-php-parser/PhpParser/Node/Expr/AssignOp/Pow.php(4 eyV;nikic-php-parser/PhpParser/Node/Expr/AssignOp/ShiftLeft.php(4 e<nikic-php-parser/PhpParser/Node/Expr/AssignOp/ShiftRight.php(4 es*2nikic-php-parser/PhpParser/Node/Expr/AssignRef.phpH(4 eHE`ob1nikic-php-parser/PhpParser/Node/Expr/BinaryOp.phpo(4 eo Ѥ<nikic-php-parser/PhpParser/Node/Expr/BinaryOp/BitwiseAnd.phpP(4 eP6L6;nikic-php-parser/PhpParser/Node/Expr/BinaryOp/BitwiseOr.phpN(4 eN_|<nikic-php-parser/PhpParser/Node/Expr/BinaryOp/BitwiseXor.phpP(4 eP~Ƥ<nikic-php-parser/PhpParser/Node/Expr/BinaryOp/BooleanAnd.phpQ(4 eQ5v;nikic-php-parser/PhpParser/Node/Expr/BinaryOp/BooleanOr.phpO(4 eOeӸ:nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Coalesce.phpM(4 eMY 8nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Concat.phpH(4 eH @q5nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Div.phpB(4 eBi7nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Equal.phpG(4 eGݙʤ9nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Greater.phpJ(4 eJ4ͤ@nikic-php-parser/PhpParser/Node/Expr/BinaryOp/GreaterOrEqual.phpY(4 eY^ز;nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Identical.phpP(4 eP"<nikic-php-parser/PhpParser/Node/Expr/BinaryOp/LogicalAnd.phpR(4 eRi;nikic-php-parser/PhpParser/Node/Expr/BinaryOp/LogicalOr.phpO(4 eO@<nikic-php-parser/PhpParser/Node/Expr/BinaryOp/LogicalXor.phpR(4 eR4e7nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Minus.phpF(4 eF$Lˤ5nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Mod.phpB(4 eBʤ5nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Mul.phpB(4 eB|:nikic-php-parser/PhpParser/Node/Expr/BinaryOp/NotEqual.phpM(4 eM>nikic-php-parser/PhpParser/Node/Expr/BinaryOp/NotIdentical.phpV(4 eVh< -6nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Plus.phpD(4 eD' ,5nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Pow.phpC(4 eC;nikic-php-parser/PhpParser/Node/Expr/BinaryOp/ShiftLeft.phpO(4 eOQ#<nikic-php-parser/PhpParser/Node/Expr/BinaryOp/ShiftRight.phpQ(4 eQǤ9nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Smaller.phpJ(4 eJf@nikic-php-parser/PhpParser/Node/Expr/BinaryOp/SmallerOrEqual.phpY(4 eY⍤;nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Spaceship.phpP(4 ePHƉ.3nikic-php-parser/PhpParser/Node/Expr/BitwiseNot.php(4 e~'3nikic-php-parser/PhpParser/Node/Expr/BooleanNot.php(4 eDC1nikic-php-parser/PhpParser/Node/Expr/CallLike.php&(4 e&KS0-nikic-php-parser/PhpParser/Node/Expr/Cast.phpA(4 eA:Vs4nikic-php-parser/PhpParser/Node/Expr/Cast/Array_.php(4 eI|3nikic-php-parser/PhpParser/Node/Expr/Cast/Bool_.php(4 e V]S4nikic-php-parser/PhpParser/Node/Expr/Cast/Double.php(4 e>,2nikic-php-parser/PhpParser/Node/Expr/Cast/Int_.php(4 ec5nikic-php-parser/PhpParser/Node/Expr/Cast/Object_.php(4 e5nikic-php-parser/PhpParser/Node/Expr/Cast/String_.php(4 e4nikic-php-parser/PhpParser/Node/Expr/Cast/Unset_.php(4 e1Ӥ8nikic-php-parser/PhpParser/Node/Expr/ClassConstFetch.php(4 eE/nikic-php-parser/PhpParser/Node/Expr/Clone_.php(4 eW0nikic-php-parser/PhpParser/Node/Expr/Closure.php -(4 e -U;3nikic-php-parser/PhpParser/Node/Expr/ClosureUse.php(4 eh3nikic-php-parser/PhpParser/Node/Expr/ConstFetch.php(4 e޶%/nikic-php-parser/PhpParser/Node/Expr/Empty_.php(4 e'.nikic-php-parser/PhpParser/Node/Expr/Error.php(4 ea\6nikic-php-parser/PhpParser/Node/Expr/ErrorSuppress.php(4 eg.nikic-php-parser/PhpParser/Node/Expr/Eval_.php(4 e356.nikic-php-parser/PhpParser/Node/Expr/Exit_.php(4 e1nikic-php-parser/PhpParser/Node/Expr/FuncCall.php3(4 e3%A1nikic-php-parser/PhpParser/Node/Expr/Include_.php(4 ei4nikic-php-parser/PhpParser/Node/Expr/Instanceof_.phpa(4 ea< /nikic-php-parser/PhpParser/Node/Expr/Isset_.php(4 eI.nikic-php-parser/PhpParser/Node/Expr/List_.php(4 e/nikic-php-parser/PhpParser/Node/Expr/Match_.php(4 eW 3nikic-php-parser/PhpParser/Node/Expr/MethodCall.phpO(4 eODWX-nikic-php-parser/PhpParser/Node/Expr/New_.php(4 eiĤ;nikic-php-parser/PhpParser/Node/Expr/NullsafeMethodCall.phpf(4 efɤ>nikic-php-parser/PhpParser/Node/Expr/NullsafePropertyFetch.php(4 e /N0nikic-php-parser/PhpParser/Node/Expr/PostDec.php(4 ew:0nikic-php-parser/PhpParser/Node/Expr/PostInc.php(4 eᦦ!/nikic-php-parser/PhpParser/Node/Expr/PreDec.php(4 etg/nikic-php-parser/PhpParser/Node/Expr/PreInc.php(4 eYä/nikic-php-parser/PhpParser/Node/Expr/Print_.php(4 enX6nikic-php-parser/PhpParser/Node/Expr/PropertyFetch.php(4 eɾ2nikic-php-parser/PhpParser/Node/Expr/ShellExec.php(4 ehy3nikic-php-parser/PhpParser/Node/Expr/StaticCall.phpe(4 ee<nikic-php-parser/PhpParser/Node/Expr/StaticPropertyFetch.php&(4 e&ܐ0nikic-php-parser/PhpParser/Node/Expr/Ternary.php(4 eQͤ/nikic-php-parser/PhpParser/Node/Expr/Throw_.php(4 e ?3nikic-php-parser/PhpParser/Node/Expr/UnaryMinus.php(4 elA2nikic-php-parser/PhpParser/Node/Expr/UnaryPlus.php(4 ee̤1nikic-php-parser/PhpParser/Node/Expr/Variable.php(4 emJr2nikic-php-parser/PhpParser/Node/Expr/YieldFrom.php(4 ew8/nikic-php-parser/PhpParser/Node/Expr/Yield_.php\(4 e\ 0nikic-php-parser/PhpParser/Node/FunctionLike.php(4 e4ͤ.nikic-php-parser/PhpParser/Node/Identifier.php(4 eJa4nikic-php-parser/PhpParser/Node/IntersectionType.php(4 eo,nikic-php-parser/PhpParser/Node/MatchArm.php(4 e+m6(nikic-php-parser/PhpParser/Node/Name.php (4 e 7nikic-php-parser/PhpParser/Node/Name/FullyQualified.php(4 e 1nikic-php-parser/PhpParser/Node/Name/Relative.php(4 eǛEf0nikic-php-parser/PhpParser/Node/NullableType.php(4 e6C)nikic-php-parser/PhpParser/Node/Param.phpb(4 ebMߤ*nikic-php-parser/PhpParser/Node/Scalar.phpk(4 ek,ߤ2nikic-php-parser/PhpParser/Node/Scalar/DNumber.php(4 ex3H:3nikic-php-parser/PhpParser/Node/Scalar/Encapsed.php(4 eRU=nikic-php-parser/PhpParser/Node/Scalar/EncapsedStringPart.php(4 e%2nikic-php-parser/PhpParser/Node/Scalar/LNumber.php (4 e z5nikic-php-parser/PhpParser/Node/Scalar/MagicConst.phpc(4 ec,xG<nikic-php-parser/PhpParser/Node/Scalar/MagicConst/Class_.phpT(4 eT㨘X9nikic-php-parser/PhpParser/Node/Scalar/MagicConst/Dir.phpM(4 eMal:nikic-php-parser/PhpParser/Node/Scalar/MagicConst/File.phpP(4 eP#?nikic-php-parser/PhpParser/Node/Scalar/MagicConst/Function_.php](4 e]HnY:nikic-php-parser/PhpParser/Node/Scalar/MagicConst/Line.phpP(4 ePM4<nikic-php-parser/PhpParser/Node/Scalar/MagicConst/Method.phpV(4 eVΤ@nikic-php-parser/PhpParser/Node/Scalar/MagicConst/Namespace_.php`(4 e`><nikic-php-parser/PhpParser/Node/Scalar/MagicConst/Trait_.phpT(4 eTd2nikic-php-parser/PhpParser/Node/Scalar/String_.phpq(4 eqT$Q(nikic-php-parser/PhpParser/Node/Stmt.php(4 ev2//nikic-php-parser/PhpParser/Node/Stmt/Break_.php(4 e֤.nikic-php-parser/PhpParser/Node/Stmt/Case_.phpl(4 elu/nikic-php-parser/PhpParser/Node/Stmt/Catch_.php|(4 e|*V>3nikic-php-parser/PhpParser/Node/Stmt/ClassConst.php| (4 e| K:d2nikic-php-parser/PhpParser/Node/Stmt/ClassLike.php (4 e 04nikic-php-parser/PhpParser/Node/Stmt/ClassMethod.php(4 eX/nikic-php-parser/PhpParser/Node/Stmt/Class_.phpu(4 eu_ļ/nikic-php-parser/PhpParser/Node/Stmt/Const_.php(4 e2nikic-php-parser/PhpParser/Node/Stmt/Continue_.php(4 e7nikic-php-parser/PhpParser/Node/Stmt/DeclareDeclare.php(4 eƀ1nikic-php-parser/PhpParser/Node/Stmt/Declare_.php(4 e.. -,nikic-php-parser/PhpParser/Node/Stmt/Do_.phpB(4 eB -@.nikic-php-parser/PhpParser/Node/Stmt/Echo_.php(4 e͘Ƥ0nikic-php-parser/PhpParser/Node/Stmt/ElseIf_.phpI(4 eIEä.nikic-php-parser/PhpParser/Node/Stmt/Else_.php(4 e|ä1nikic-php-parser/PhpParser/Node/Stmt/EnumCase.php(4 ejD.nikic-php-parser/PhpParser/Node/Stmt/Enum_.php=(4 e=dA3nikic-php-parser/PhpParser/Node/Stmt/Expression.php(4 eRK1nikic-php-parser/PhpParser/Node/Stmt/Finally_.php(4 e1A-nikic-php-parser/PhpParser/Node/Stmt/For_.php>(4 e>NQ1nikic-php-parser/PhpParser/Node/Stmt/Foreach_.phpo(4 eo92nikic-php-parser/PhpParser/Node/Stmt/Function_.php, -(4 e, -nL0nikic-php-parser/PhpParser/Node/Stmt/Global_.php(4 e.nikic-php-parser/PhpParser/Node/Stmt/Goto_.php(4 eVyPn1nikic-php-parser/PhpParser/Node/Stmt/GroupUse.php -(4 e -ߎ0|5nikic-php-parser/PhpParser/Node/Stmt/HaltCompiler.php(4 e];,nikic-php-parser/PhpParser/Node/Stmt/If_.php:(4 e:u٤3nikic-php-parser/PhpParser/Node/Stmt/InlineHTML.php(4 e]3nikic-php-parser/PhpParser/Node/Stmt/Interface_.php(4 eL/Ǥ.nikic-php-parser/PhpParser/Node/Stmt/Label.php(4 eӤ3nikic-php-parser/PhpParser/Node/Stmt/Namespace_.php(4 e㹀,nikic-php-parser/PhpParser/Node/Stmt/Nop.php@(4 e@G1nikic-php-parser/PhpParser/Node/Stmt/Property.phpO -(4 eO -=9nikic-php-parser/PhpParser/Node/Stmt/PropertyProperty.php(4 e҉0nikic-php-parser/PhpParser/Node/Stmt/Return_.php(4 eͿ)e2nikic-php-parser/PhpParser/Node/Stmt/StaticVar.php(4 e0nikic-php-parser/PhpParser/Node/Stmt/Static_.php(4 e0nikic-php-parser/PhpParser/Node/Stmt/Switch_.php5(4 e5FFY/nikic-php-parser/PhpParser/Node/Stmt/Throw_.php(4 e1nikic-php-parser/PhpParser/Node/Stmt/TraitUse.php(4 eg,;nikic-php-parser/PhpParser/Node/Stmt/TraitUseAdaptation.php(4 ea8Anikic-php-parser/PhpParser/Node/Stmt/TraitUseAdaptation/Alias.phpA(4 eAdFnikic-php-parser/PhpParser/Node/Stmt/TraitUseAdaptation/Precedence.phpZ(4 eZP֤/nikic-php-parser/PhpParser/Node/Stmt/Trait_.php(4 e$v1nikic-php-parser/PhpParser/Node/Stmt/TryCatch.php$(4 e$W/nikic-php-parser/PhpParser/Node/Stmt/Unset_.php(4 e=oB/nikic-php-parser/PhpParser/Node/Stmt/UseUse.phpd(4 edb-nikic-php-parser/PhpParser/Node/Stmt/Use_.phpl(4 el9=|/nikic-php-parser/PhpParser/Node/Stmt/While_.phpE(4 eEա-nikic-php-parser/PhpParser/Node/UnionType.php(4 eԛ5nikic-php-parser/PhpParser/Node/VarLikeIdentifier.php(4 e&7nikic-php-parser/PhpParser/Node/VariadicPlaceholder.php(4 eP+nikic-php-parser/PhpParser/NodeAbstract.phpZ(4 eZ׻@)nikic-php-parser/PhpParser/NodeDumper.phpd(4 edY l)nikic-php-parser/PhpParser/NodeFinder.php (4 e ,nikic-php-parser/PhpParser/NodeTraverser.php]'(4 e]'TG:Ƥ5nikic-php-parser/PhpParser/NodeTraverserInterface.php|(4 e|Ś *nikic-php-parser/PhpParser/NodeVisitor.php(4 e39nikic-php-parser/PhpParser/NodeVisitor/CloningVisitor.php(4 e"WJ9nikic-php-parser/PhpParser/NodeVisitor/FindingVisitor.php(4 eB>nikic-php-parser/PhpParser/NodeVisitor/FirstFindingVisitor.php(4 em4Ť7nikic-php-parser/PhpParser/NodeVisitor/NameResolver.phpq&(4 eq&ǠG@nikic-php-parser/PhpParser/NodeVisitor/NodeConnectingVisitor.php(4 eu -äBnikic-php-parser/PhpParser/NodeVisitor/ParentConnectingVisitor.phpu(4 euME2nikic-php-parser/PhpParser/NodeVisitorAbstract.php(4 e%nikic-php-parser/PhpParser/Parser.php}(4 e}{.nikic-php-parser/PhpParser/Parser/Multiple.php(4 esF)7*nikic-php-parser/PhpParser/Parser/Php5.php+(4 e+1*nikic-php-parser/PhpParser/Parser/Php7.phpT(4 eT!V`,nikic-php-parser/PhpParser/Parser/Tokens.php&(4 e&<-nikic-php-parser/PhpParser/ParserAbstract.phpT(4 eT'[,nikic-php-parser/PhpParser/ParserFactory.php(4 e -~&5nikic-php-parser/PhpParser/PrettyPrinter/Standard.php+(4 e+4nikic-php-parser/PhpParser/PrettyPrinterAbstract.phpQ(4 eQ%jobject-enumerator/LICENSE(4 ey{object-reflector/LICENSE(4 e9vphar-io-manifest/LICENSE`(4 e`p+phar-io-manifest/ManifestDocumentMapper.php(4 e:#phar-io-manifest/ManifestLoader.php(4 e.-a'phar-io-manifest/ManifestSerializer.php(4 erp:phar-io-manifest/exceptions/ElementCollectionException.php(4 e I)phar-io-manifest/exceptions/Exception.php(4 e?phar-io-manifest/exceptions/InvalidApplicationNameException.php(4 e:@>5phar-io-manifest/exceptions/InvalidEmailException.php(4 e<3phar-io-manifest/exceptions/InvalidUrlException.php(4 e 9phar-io-manifest/exceptions/ManifestDocumentException.php(4 e!P4@phar-io-manifest/exceptions/ManifestDocumentLoadingException.phpH(4 eHǃ?phar-io-manifest/exceptions/ManifestDocumentMapperException.php(4 e:9z8phar-io-manifest/exceptions/ManifestElementException.php(4 eA47phar-io-manifest/exceptions/ManifestLoaderException.php(4 eD>'phar-io-manifest/values/Application.php(4 eI$ۤ+phar-io-manifest/values/ApplicationName.php;(4 e;D"phar-io-manifest/values/Author.php(4 eF,phar-io-manifest/values/AuthorCollection.php(4 eo4phar-io-manifest/values/AuthorCollectionIterator.php3(4 e3џ,phar-io-manifest/values/BundledComponent.php@(4 e@DP`6phar-io-manifest/values/BundledComponentCollection.php (4 e ¾W6>phar-io-manifest/values/BundledComponentCollectionIterator.php(4 eVh0phar-io-manifest/values/CopyrightInformation.phpP(4 eP ai!phar-io-manifest/values/Email.phpN(4 eNZ&%phar-io-manifest/values/Extension.php(4 eq}#phar-io-manifest/values/Library.php(4 eO#phar-io-manifest/values/License.php(4 e&!o$phar-io-manifest/values/Manifest.php -(4 e -=La3phar-io-manifest/values/PhpExtensionRequirement.php(4 e11phar-io-manifest/values/PhpVersionRequirement.php(4 em?'phar-io-manifest/values/Requirement.php(4 ed1phar-io-manifest/values/RequirementCollection.php(4 eP9phar-io-manifest/values/RequirementCollectionIterator.phpj(4 ejܭ: phar-io-manifest/values/Type.php(4 e=%phar-io-manifest/values/Url.php(4 e͚&phar-io-manifest/xml/AuthorElement.phpr(4 er<0phar-io-manifest/xml/AuthorElementCollection.php,(4 e,-'phar-io-manifest/xml/BundlesElement.phpS(4 eSWN>)phar-io-manifest/xml/ComponentElement.phpy(4 eyݤ3phar-io-manifest/xml/ComponentElementCollection.php5(4 e5(\(phar-io-manifest/xml/ContainsElement.phpn(4 enf)phar-io-manifest/xml/CopyrightElement.php(4 e7*phar-io-manifest/xml/ElementCollection.php(4 e@ #phar-io-manifest/xml/ExtElement.php (4 e y>-phar-io-manifest/xml/ExtElementCollection.php#(4 e#E)phar-io-manifest/xml/ExtensionElement.php}(4 e}0'phar-io-manifest/xml/LicenseElement.phpo(4 eo%:')phar-io-manifest/xml/ManifestDocument.php - (4 e - 4(phar-io-manifest/xml/ManifestElement.php4(4 e4#phar-io-manifest/xml/PhpElement.php(4 eB:5(phar-io-manifest/xml/RequiresElement.php$(4 e$>!phar-io-version/BuildMetaData.php(4 ephar-io-version/LICENSE&(4 e&Ҫ $phar-io-version/PreReleaseSuffix.php(4 e:phar-io-version/Version.php(4 eu#+phar-io-version/VersionConstraintParser.phpT (4 eT Ф*phar-io-version/VersionConstraintValue.phpH -(4 eH -F{~4!phar-io-version/VersionNumber.php(4 eO19phar-io-version/constraints/AbstractVersionConstraint.php(4 exB9phar-io-version/constraints/AndVersionConstraintGroup.php(4 eY4phar-io-version/constraints/AnyVersionConstraint.phpR(4 eR #6phar-io-version/constraints/ExactVersionConstraint.php(4 e!Ephar-io-version/constraints/GreaterThanOrEqualToVersionConstraint.php(4 eVU8phar-io-version/constraints/OrVersionConstraintGroup.php(4 eM%Fphar-io-version/constraints/SpecificMajorAndMinorVersionConstraint.php(4 eɍ>phar-io-version/constraints/SpecificMajorVersionConstraint.php(4 e`9q:1phar-io-version/constraints/VersionConstraint.php(4 eeDq(phar-io-version/exceptions/Exception.php(4 e$eb?phar-io-version/exceptions/InvalidPreReleaseSuffixException.php(4 eҵ6phar-io-version/exceptions/InvalidVersionException.php(4 e4/S7phar-io-version/exceptions/NoBuildMetaDataException.php(4 e]:phar-io-version/exceptions/NoPreReleaseSuffixException.php(4 eT4Dphar-io-version/exceptions/UnsupportedVersionConstraintException.php(4 e9"php-code-coverage/CodeCoverage.phpNE(4 eNE3M%#php-code-coverage/Driver/Driver.php(4 e3A'php-code-coverage/Driver/PcovDriver.phpJ(4 eJ)php-code-coverage/Driver/PhpdbgDriver.php^ -(4 e^ -_2G%php-code-coverage/Driver/Selector.php (4 e 6]*php-code-coverage/Driver/Xdebug2Driver.phpA (4 eA *php-code-coverage/Driver/Xdebug3Driver.php (4 e h*Jphp-code-coverage/Exception/BranchAndPathCoverageNotSupportedException.php(4 e77Fphp-code-coverage/Exception/DeadCodeDetectionNotSupportedException.php(4 eCphp-code-coverage/Exception/DirectoryCouldNotBeCreatedException.php(4 e)php-code-coverage/Exception/Exception.php}(4 e}z8php-code-coverage/Exception/InvalidArgumentException.php(4 eK.nFphp-code-coverage/Exception/NoCodeCoverageDriverAvailableException.php/(4 e/6R]php-code-coverage/Exception/NoCodeCoverageDriverWithPathCoverageSupportAvailableException.phpa(4 ea"A/php-code-coverage/Exception/ParserException.php(4 e,/Dphp-code-coverage/Exception/PathExistsButIsNotDirectoryException.php(4 e.29php-code-coverage/Exception/PcovNotAvailableException.phpa(4 eaj;php-code-coverage/Exception/PhpdbgNotAvailableException.php`(4 e`3php-code-coverage/Exception/ReflectionException.php(4 ek)?php-code-coverage/Exception/ReportAlreadyFinalizedException.php:(4 e:d%6Iphp-code-coverage/Exception/StaticAnalysisCacheNotConfiguredException.php(4 e}6php-code-coverage/Exception/TestIdMissingException.php(4 e -Cphp-code-coverage/Exception/UnintentionallyCoveredCodeException.php+(4 e+Q_ª=php-code-coverage/Exception/WriteOperationFailedException.php(4 e(e;php-code-coverage/Exception/WrongXdebugVersionException.php(4 e Ȥ:php-code-coverage/Exception/Xdebug2NotEnabledException.phpf(4 ef,':php-code-coverage/Exception/Xdebug3NotEnabledException.phpy(4 ey<>;php-code-coverage/Exception/XdebugNotAvailableException.phpe(4 eeNG,php-code-coverage/Exception/XmlException.php(4 eWܤphp-code-coverage/Filter.php (4 e 4php-code-coverage/LICENSE(4 e-~y֤'php-code-coverage/Node/AbstractNode.php:(4 e:%^"php-code-coverage/Node/Builder.php (4 e 2N$php-code-coverage/Node/CrapIndex.php(4 e# $php-code-coverage/Node/Directory.php -&(4 e -&}php-code-coverage/Node/File.phpK(4 eK{#php-code-coverage/Node/Iterator.php(4 eHk/php-code-coverage/ProcessedCodeCoverageData.php$(4 e$')php-code-coverage/RawCodeCoverageData.php!(4 e!#php-code-coverage/Report/Clover.phpW((4 eW(yD&php-code-coverage/Report/Cobertura.php1(4 e1 Z#php-code-coverage/Report/Crap4j.php<(4 e<r(php-code-coverage/Report/Html/Facade.php"(4 e";ڤ*php-code-coverage/Report/Html/Renderer.phpU!(4 eU!}4php-code-coverage/Report/Html/Renderer/Dashboard.phpC (4 eC L+4php-code-coverage/Report/Html/Renderer/Directory.php (4 e (/php-code-coverage/Report/Html/Renderer/File.php (4 e ZuBphp-code-coverage/Report/Html/Renderer/Template/branches.html.dist(4 eh2+Fphp-code-coverage/Report/Html/Renderer/Template/coverage_bar.html.dist'(4 e'O}Mphp-code-coverage/Report/Html/Renderer/Template/coverage_bar_branch.html.dist'(4 e'O}Ephp-code-coverage/Report/Html/Renderer/Template/css/bootstrap.min.cssy(4 eyĤ>php-code-coverage/Report/Html/Renderer/Template/css/custom.css(4 eAphp-code-coverage/Report/Html/Renderer/Template/css/nv.d3.min.cssX%(4 eX%0,@php-code-coverage/Report/Html/Renderer/Template/css/octicons.cssX(4 eX'#=php-code-coverage/Report/Html/Renderer/Template/css/style.css -(4 e -Cphp-code-coverage/Report/Html/Renderer/Template/dashboard.html.dist(4 eDJphp-code-coverage/Report/Html/Renderer/Template/dashboard_branch.html.dist(4 eDCphp-code-coverage/Report/Html/Renderer/Template/directory.html.dist(4 eՆJphp-code-coverage/Report/Html/Renderer/Template/directory_branch.html.dist(4 en2]Hphp-code-coverage/Report/Html/Renderer/Template/directory_item.html.distA(4 eAdsOphp-code-coverage/Report/Html/Renderer/Template/directory_item_branch.html.dist;(4 e;mۤ>php-code-coverage/Report/Html/Renderer/Template/file.html.distP (4 eP j*Ephp-code-coverage/Report/Html/Renderer/Template/file_branch.html.dist (4 e ㉞Cphp-code-coverage/Report/Html/Renderer/Template/file_item.html.distr(4 er/yJphp-code-coverage/Report/Html/Renderer/Template/file_item_branch.html.distl(4 el-Cphp-code-coverage/Report/Html/Renderer/Template/icons/file-code.svg0(4 e0QUUHphp-code-coverage/Report/Html/Renderer/Template/icons/file-directory.svg(4 eZCphp-code-coverage/Report/Html/Renderer/Template/js/bootstrap.min.jsc(4 ec"#<php-code-coverage/Report/Html/Renderer/Template/js/d3.min.jsP(4 ePhb:php-code-coverage/Report/Html/Renderer/Template/js/file.js(4 eb䆤@php-code-coverage/Report/Html/Renderer/Template/js/jquery.min.js@^(4 e@^ ?php-code-coverage/Report/Html/Renderer/Template/js/nv.d3.min.jsR(4 eRphp-code-coverage/Report/Html/Renderer/Template/line.html.dist(4 e{?php-code-coverage/Report/Html/Renderer/Template/lines.html.diste(4 eedf Ephp-code-coverage/Report/Html/Renderer/Template/method_item.html.dist(4 ejפLphp-code-coverage/Report/Html/Renderer/Template/method_item_branch.html.dist(4 eyĎk?php-code-coverage/Report/Html/Renderer/Template/paths.html.dist(4 e*'ݤ php-code-coverage/Report/PHP.php(4 e<[!php-code-coverage/Report/Text.php'(4 e' 6H1php-code-coverage/Report/Xml/BuildInformation.php (4 e T3e)php-code-coverage/Report/Xml/Coverage.php+(4 e+9E*php-code-coverage/Report/Xml/Directory.php(4 eAf'php-code-coverage/Report/Xml/Facade.php"(4 e"O}%php-code-coverage/Report/Xml/File.php+(4 e+g׃'php-code-coverage/Report/Xml/Method.phpW(4 eW ʤ%php-code-coverage/Report/Xml/Node.php3(4 e3(php-code-coverage/Report/Xml/Project.phpf(4 efPe'php-code-coverage/Report/Xml/Report.php (4 e HC'php-code-coverage/Report/Xml/Source.phpz(4 ez'1&php-code-coverage/Report/Xml/Tests.php(4 e'php-code-coverage/Report/Xml/Totals.php(4 e:6%php-code-coverage/Report/Xml/Unit.php(4 eY0php-code-coverage/StaticAnalysis/CacheWarmer.php`(4 e`_%פ8php-code-coverage/StaticAnalysis/CachingFileAnalyser.php(4 e2Q;php-code-coverage/StaticAnalysis/CodeUnitFindingVisitor.php,((4 e,(vMBphp-code-coverage/StaticAnalysis/ExecutableLinesFindingVisitor.phpm'(4 em'i1php-code-coverage/StaticAnalysis/FileAnalyser.php(4 eJ?php-code-coverage/StaticAnalysis/IgnoredLinesFindingVisitor.php (4 e 8php-code-coverage/StaticAnalysis/ParsingFileAnalyser.php(4 ed%php-code-coverage/Util/Filesystem.php(4 e%php-code-coverage/Util/Percentage.php(4 ephp-code-coverage/Version.php(4 eR郤php-file-iterator/Facade.php% -(4 e% -Τphp-file-iterator/Factory.php(4 eg ,php-file-iterator/Iterator.phpZ (4 eZ C܎php-file-iterator/LICENSE(4 eo:php-invoker/Invoker.php(4 e+L$php-invoker/exceptions/Exception.phpr(4 ervvduDphp-invoker/exceptions/ProcessControlExtensionNotLoadedException.php(4 e +php-invoker/exceptions/TimeoutException.php(4 e.php-text-template/LICENSE(4 euphp-text-template/Template.php( (4 e( *php-text-template/exceptions/Exception.phpy(4 eyn9php-text-template/exceptions/InvalidArgumentException.php(4 eaM1php-text-template/exceptions/RuntimeException.php(4 eYm'php-timer/Duration.php -(4 e -tXyphp-timer/LICENSE(4 ex$php-timer/ResourceUsageFormatter.php(4 ePھphp-timer/Timer.php(4 ecAɤ"php-timer/exceptions/Exception.phpn(4 eniuۤ/php-timer/exceptions/NoActiveTimerException.php(4 el٤Ephp-timer/exceptions/TimeSinceStartOfRequestNotAvailableException.php(4 e$b+phpdocumentor-reflection-common/Element.php (4 e %(phpdocumentor-reflection-common/File.php(4 eI))phpdocumentor-reflection-common/Fqsen.php(4 e?'phpdocumentor-reflection-common/LICENSE9(4 e9*2Ȑ,phpdocumentor-reflection-common/Location.php(4 e=(+phpdocumentor-reflection-common/Project.php(4 eJ2phpdocumentor-reflection-common/ProjectFactory.php_(4 e_j\".phpdocumentor-reflection-docblock/DocBlock.php(4 eHx>$:phpdocumentor-reflection-docblock/DocBlock/Description.php (4 e 54Aphpdocumentor-reflection-docblock/DocBlock/DescriptionFactory.php(4 ed=<phpdocumentor-reflection-docblock/DocBlock/ExampleFinder.php,(4 e,ׯf9phpdocumentor-reflection-docblock/DocBlock/Serializer.php (4 e ]Aphpdocumentor-reflection-docblock/DocBlock/StandardTagFactory.php0(4 e0<2phpdocumentor-reflection-docblock/DocBlock/Tag.php(4 e9phpdocumentor-reflection-docblock/DocBlock/TagFactory.php(4 eJMx:phpdocumentor-reflection-docblock/DocBlock/Tags/Author.php (4 e ;phpdocumentor-reflection-docblock/DocBlock/Tags/BaseTag.php(4 eZr:phpdocumentor-reflection-docblock/DocBlock/Tags/Covers.phpg -(4 eg -w8>phpdocumentor-reflection-docblock/DocBlock/Tags/Deprecated.php -(4 e -}CO;phpdocumentor-reflection-docblock/DocBlock/Tags/Example.php(4 ealN@Hphpdocumentor-reflection-docblock/DocBlock/Tags/Factory/StaticMethod.php(4 e.ͤ=phpdocumentor-reflection-docblock/DocBlock/Tags/Formatter.php(4 e}BܤLphpdocumentor-reflection-docblock/DocBlock/Tags/Formatter/AlignFormatter.phpq(4 eqRphpdocumentor-reflection-docblock/DocBlock/Tags/Formatter/PassthroughFormatter.php(4 eP~;phpdocumentor-reflection-docblock/DocBlock/Tags/Generic.phpx (4 ex Bn>phpdocumentor-reflection-docblock/DocBlock/Tags/InvalidTag.php,(4 e,Md88phpdocumentor-reflection-docblock/DocBlock/Tags/Link.php(4 eG:phpdocumentor-reflection-docblock/DocBlock/Tags/Method.php(4 eYKc9phpdocumentor-reflection-docblock/DocBlock/Tags/Param.php(4 eB<phpdocumentor-reflection-docblock/DocBlock/Tags/Property.php (4 e |yCϤ@phpdocumentor-reflection-docblock/DocBlock/Tags/PropertyRead.php (4 e #k:Aphpdocumentor-reflection-docblock/DocBlock/Tags/PropertyWrite.php (4 e v Cphpdocumentor-reflection-docblock/DocBlock/Tags/Reference/Fqsen.php,(4 e,%8Gphpdocumentor-reflection-docblock/DocBlock/Tags/Reference/Reference.php(4 e Aphpdocumentor-reflection-docblock/DocBlock/Tags/Reference/Url.php(4 ec[];phpdocumentor-reflection-docblock/DocBlock/Tags/Return_.php(4 e N7phpdocumentor-reflection-docblock/DocBlock/Tags/See.php (4 e :e9phpdocumentor-reflection-docblock/DocBlock/Tags/Since.phpW -(4 eW -1>:phpdocumentor-reflection-docblock/DocBlock/Tags/Source.php (4 e [K?phpdocumentor-reflection-docblock/DocBlock/Tags/TagWithType.php(4 e;u:phpdocumentor-reflection-docblock/DocBlock/Tags/Throws.php(4 e"G8phpdocumentor-reflection-docblock/DocBlock/Tags/Uses.php> -(4 e> - -8phpdocumentor-reflection-docblock/DocBlock/Tags/Var_.php (4 e u:ڤ;phpdocumentor-reflection-docblock/DocBlock/Tags/Version.php -(4 e -@S5phpdocumentor-reflection-docblock/DocBlockFactory.php$(4 e$br>phpdocumentor-reflection-docblock/DocBlockFactoryInterface.php(4 e)%ߤ=phpdocumentor-reflection-docblock/Exception/PcreException.php(4 e V)phpdocumentor-reflection-docblock/LICENSE8(4 e8ʤ+phpdocumentor-reflection-docblock/Utils.php (4 e -phpdocumentor-type-resolver/FqsenResolver.php(4 ej^#phpdocumentor-type-resolver/LICENSE8(4 e8ʤ*phpdocumentor-type-resolver/PseudoType.phpu(4 eu]\:phpdocumentor-type-resolver/PseudoTypes/CallableString.php`(4 e`Z2phpdocumentor-type-resolver/PseudoTypes/False_.php(4 eo䈤=phpdocumentor-type-resolver/PseudoTypes/HtmlEscapedString.phpg(4 egwe8phpdocumentor-type-resolver/PseudoTypes/IntegerRange.php%(4 e%R1phpdocumentor-type-resolver/PseudoTypes/List_.php(4 ewu9phpdocumentor-type-resolver/PseudoTypes/LiteralString.php^(4 e^=oNW;phpdocumentor-type-resolver/PseudoTypes/LowercaseString.phpb(4 eb7 ;phpdocumentor-type-resolver/PseudoTypes/NegativeInteger.php[(4 e[DEۤCphpdocumentor-type-resolver/PseudoTypes/NonEmptyLowercaseString.phpt(4 et):phpdocumentor-type-resolver/PseudoTypes/NonEmptyString.phpa(4 ea²,9phpdocumentor-type-resolver/PseudoTypes/NumericString.php^(4 e^8M4phpdocumentor-type-resolver/PseudoTypes/Numeric_.php(4 e=k;phpdocumentor-type-resolver/PseudoTypes/PositiveInteger.php[(4 e[H7phpdocumentor-type-resolver/PseudoTypes/TraitString.phpZ(4 eZgC1phpdocumentor-type-resolver/PseudoTypes/True_.php(4 el$phpdocumentor-type-resolver/Type.php(4 eb&,phpdocumentor-type-resolver/TypeResolver.php%U(4 e%UU2phpdocumentor-type-resolver/Types/AbstractList.phpt(4 ett4phpdocumentor-type-resolver/Types/AggregatedType.php -(4 e -Hɵ.phpdocumentor-type-resolver/Types/ArrayKey.php(4 ePĤ,phpdocumentor-type-resolver/Types/Array_.php(4 e4-phpdocumentor-type-resolver/Types/Boolean.phpn(4 enrĤ/phpdocumentor-type-resolver/Types/Callable_.php{(4 e{E1phpdocumentor-type-resolver/Types/ClassString.phpC(4 eCrvy0phpdocumentor-type-resolver/Types/Collection.php(4 e?.phpdocumentor-type-resolver/Types/Compound.php(4 e>7-phpdocumentor-type-resolver/Types/Context.php (4 e ]Z4phpdocumentor-type-resolver/Types/ContextFactory.php6(4 e6\0phpdocumentor-type-resolver/Types/Expression.php8(4 e8g,phpdocumentor-type-resolver/Types/Float_.phpm(4 em)J-phpdocumentor-type-resolver/Types/Integer.phpj(4 ejv5phpdocumentor-type-resolver/Types/InterfaceString.php(4 e2phpdocumentor-type-resolver/Types/Intersection.php(4 eUz$/phpdocumentor-type-resolver/Types/Iterable_.php?(4 e?Q8,phpdocumentor-type-resolver/Types/Mixed_.php(4 e3i,phpdocumentor-type-resolver/Types/Never_.php(4 ej+phpdocumentor-type-resolver/Types/Null_.phpx(4 exs.phpdocumentor-type-resolver/Types/Nullable.phpR(4 eRCp\-phpdocumentor-type-resolver/Types/Object_.php(4 ewEhN-phpdocumentor-type-resolver/Types/Parent_.php(4 eO!./phpdocumentor-type-resolver/Types/Resource_.php(4 eŞX,phpdocumentor-type-resolver/Types/Scalar.php(4 e+phpdocumentor-type-resolver/Types/Self_.php(4 eoȤ-phpdocumentor-type-resolver/Types/Static_.php(4 e8-phpdocumentor-type-resolver/Types/String_.phps(4 esH*phpdocumentor-type-resolver/Types/This.phpY(4 eY^?ֈ+phpdocumentor-type-resolver/Types/Void_.php(4 ekphpspec-prophecy/LICENSE}(4 e} ߦ&phpspec-prophecy/Prophecy/Argument.php](4 e]eQ8phpspec-prophecy/Prophecy/Argument/ArgumentsWildcard.php (4 e N<:phpspec-prophecy/Prophecy/Argument/Token/AnyValueToken.php(4 eIZ%%;phpspec-prophecy/Prophecy/Argument/Token/AnyValuesToken.php(4 e'`Bphpspec-prophecy/Prophecy/Argument/Token/ApproximateValueToken.phpm(4 em٬c<phpspec-prophecy/Prophecy/Argument/Token/ArrayCountToken.phpQ(4 eQ_穤<phpspec-prophecy/Prophecy/Argument/Token/ArrayEntryToken.php(4 eRAphpspec-prophecy/Prophecy/Argument/Token/ArrayEveryEntryToken.php(4 e#:phpspec-prophecy/Prophecy/Argument/Token/CallbackToken.php(4 ef"<phpspec-prophecy/Prophecy/Argument/Token/ExactValueToken.php (4 e :٤@phpspec-prophecy/Prophecy/Argument/Token/IdenticalValueToken.php(4 e49phpspec-prophecy/Prophecy/Argument/Token/InArrayToken.php(4 eͪ!<phpspec-prophecy/Prophecy/Argument/Token/LogicalAndToken.php(4 e<phpspec-prophecy/Prophecy/Argument/Token/LogicalNotToken.phpA(4 eA<phpspec-prophecy/Prophecy/Argument/Token/NotInArrayToken.php(4 e=phpspec-prophecy/Prophecy/Argument/Token/ObjectStateToken.php -(4 e -#"lV@phpspec-prophecy/Prophecy/Argument/Token/StringContainsToken.php-(4 e-3xD;phpspec-prophecy/Prophecy/Argument/Token/TokenInterface.php(4 eG66phpspec-prophecy/Prophecy/Argument/Token/TypeToken.php(4 e$'phpspec-prophecy/Prophecy/Call/Call.php(4 e΂-phpspec-prophecy/Prophecy/Call/CallCenter.php(4 e\%-j:phpspec-prophecy/Prophecy/Comparator/ClosureComparator.php(4 eO0phpspec-prophecy/Prophecy/Comparator/Factory.php(4 eU%8phpspec-prophecy/Prophecy/Comparator/FactoryProvider.php(4 ek;phpspec-prophecy/Prophecy/Comparator/ProphecyComparator.php (4 e 3phpspec-prophecy/Prophecy/Doubler/CachedDoubler.php(4 eUWDphpspec-prophecy/Prophecy/Doubler/ClassPatch/ClassPatchInterface.phph(4 ehq!ʤHphpspec-prophecy/Prophecy/Doubler/ClassPatch/DisableConstructorPatch.php(4 e,g=phpspec-prophecy/Prophecy/Doubler/ClassPatch/KeywordPatch.php'(4 e'?phpspec-prophecy/Prophecy/Doubler/ClassPatch/MagicCallPatch.php (4 e Q)7Ephpspec-prophecy/Prophecy/Doubler/ClassPatch/ProphecySubjectPatch.php$ (4 e$ bBۀPphpspec-prophecy/Prophecy/Doubler/ClassPatch/ReflectionClassNewInstancePatch.php(4 e -,Aphpspec-prophecy/Prophecy/Doubler/ClassPatch/SplFileInfoPatch.php@ (4 e@ YyG?phpspec-prophecy/Prophecy/Doubler/ClassPatch/ThrowablePatch.php" (4 e" aԤAphpspec-prophecy/Prophecy/Doubler/ClassPatch/TraversablePatch.php (4 e wp5phpspec-prophecy/Prophecy/Doubler/DoubleInterface.php(4 eBۤ-phpspec-prophecy/Prophecy/Doubler/Doubler.php(4 ePvlBphpspec-prophecy/Prophecy/Doubler/Generator/ClassCodeGenerator.php (4 e Wy<phpspec-prophecy/Prophecy/Doubler/Generator/ClassCreator.phpb(4 ebH1ؤ;phpspec-prophecy/Prophecy/Doubler/Generator/ClassMirror.php#(4 e#Aphpspec-prophecy/Prophecy/Doubler/Generator/Node/ArgumentNode.php(4 eZfEphpspec-prophecy/Prophecy/Doubler/Generator/Node/ArgumentTypeNode.php(4 eˤ>phpspec-prophecy/Prophecy/Doubler/Generator/Node/ClassNode.phpN(4 eN׉ ?phpspec-prophecy/Prophecy/Doubler/Generator/Node/MethodNode.php(4 eCphpspec-prophecy/Prophecy/Doubler/Generator/Node/ReturnTypeNode.php(4 eCٮEphpspec-prophecy/Prophecy/Doubler/Generator/Node/TypeNodeAbstract.php (4 e eCphpspec-prophecy/Prophecy/Doubler/Generator/ReflectionInterface.php(4 e YAphpspec-prophecy/Prophecy/Doubler/Generator/TypeHintReference.php"(4 e"&0phpspec-prophecy/Prophecy/Doubler/LazyDouble.php (4 e 3phpspec-prophecy/Prophecy/Doubler/NameGenerator.php(4 e,ôyDphpspec-prophecy/Prophecy/Exception/Call/UnexpectedCallException.php(4 eOEphpspec-prophecy/Prophecy/Exception/Doubler/ClassCreatorException.phpD(4 eDyXDphpspec-prophecy/Prophecy/Exception/Doubler/ClassMirrorException.phpd(4 edv48Fphpspec-prophecy/Prophecy/Exception/Doubler/ClassNotFoundException.php(4 e}:?phpspec-prophecy/Prophecy/Exception/Doubler/DoubleException.php(4 eV"^@phpspec-prophecy/Prophecy/Exception/Doubler/DoublerException.php(4 ehJphpspec-prophecy/Prophecy/Exception/Doubler/InterfaceNotFoundException.php!(4 e!R3Lphpspec-prophecy/Prophecy/Exception/Doubler/MethodNotExtendableException.php(4 e[Gphpspec-prophecy/Prophecy/Exception/Doubler/MethodNotFoundException.php(4 e#kJphpspec-prophecy/Prophecy/Exception/Doubler/ReturnByReferenceException.php(4 eL?1phpspec-prophecy/Prophecy/Exception/Exception.php(4 ex@phpspec-prophecy/Prophecy/Exception/InvalidArgumentException.php(4 e󱙤Ephpspec-prophecy/Prophecy/Exception/Prediction/AggregateException.php;(4 e;.Lphpspec-prophecy/Prophecy/Exception/Prediction/FailedPredictionException.phpg(4 eg3'}}Cphpspec-prophecy/Prophecy/Exception/Prediction/NoCallsException.php(4 eZFphpspec-prophecy/Prophecy/Exception/Prediction/PredictionException.php(4 eR2ͤPphpspec-prophecy/Prophecy/Exception/Prediction/UnexpectedCallsCountException.php (4 e ڶKphpspec-prophecy/Prophecy/Exception/Prediction/UnexpectedCallsException.php(4 eHphpspec-prophecy/Prophecy/Exception/Prophecy/MethodProphecyException.phpo(4 eoHphpspec-prophecy/Prophecy/Exception/Prophecy/ObjectProphecyException.php(4 eg6ڤBphpspec-prophecy/Prophecy/Exception/Prophecy/ProphecyException.php(4 eD7jIphpspec-prophecy/Prophecy/PhpDocumentor/ClassAndInterfaceTagRetriever.php\(4 e\,|Ϥ=phpspec-prophecy/Prophecy/PhpDocumentor/ClassTagRetriever.php(4 eGphpspec-prophecy/Prophecy/PhpDocumentor/MethodTagRetrieverInterface.php(4 eB7phpspec-prophecy/Prophecy/Prediction/CallPrediction.php"(4 e"Ҷڤ<phpspec-prophecy/Prophecy/Prediction/CallTimesPrediction.phpH (4 eH ,;phpspec-prophecy/Prophecy/Prediction/CallbackPrediction.php(4 e :phpspec-prophecy/Prophecy/Prediction/NoCallsPrediction.php>(4 e>y<phpspec-prophecy/Prophecy/Prediction/PredictionInterface.php(4 eD5phpspec-prophecy/Prophecy/Promise/CallbackPromise.php#(4 e#26phpspec-prophecy/Prophecy/Promise/PromiseInterface.phpa(4 eaĶ= -;phpspec-prophecy/Prophecy/Promise/ReturnArgumentPromise.phpE(4 eEj3phpspec-prophecy/Prophecy/Promise/ReturnPromise.phpu(4 euR2phpspec-prophecy/Prophecy/Promise/ThrowPromise.php -(4 e -cb5phpspec-prophecy/Prophecy/Prophecy/MethodProphecy.phpt<(4 et<n(5phpspec-prophecy/Prophecy/Prophecy/ObjectProphecy.php(4 eVK%8phpspec-prophecy/Prophecy/Prophecy/ProphecyInterface.phpq(4 eqhRw¤?phpspec-prophecy/Prophecy/Prophecy/ProphecySubjectInterface.php(4 eġAr/phpspec-prophecy/Prophecy/Prophecy/Revealer.php(4 e m8phpspec-prophecy/Prophecy/Prophecy/RevealerInterface.phpG(4 eGWnZ%phpspec-prophecy/Prophecy/Prophet.php(4 eNȤ-phpspec-prophecy/Prophecy/Util/ExportUtil.php(4 et-phpspec-prophecy/Prophecy/Util/StringUtil.php -(4 e -v|= phpunit.xsdRF(4 eRFAgphpunit/Exception.php(4 ea#phpunit/Framework/Assert.phpc(4 ectN&phpunit/Framework/Assert/Functions.php (4 e s0phpunit/Framework/Constraint/Boolean/IsFalse.php(4 e/phpunit/Framework/Constraint/Boolean/IsTrue.php(4 e})phpunit/Framework/Constraint/Callback.php?(4 e? -b2phpunit/Framework/Constraint/Cardinality/Count.phpj (4 ej xR@ؤ8phpunit/Framework/Constraint/Cardinality/GreaterThan.php(4 eh,d}4phpunit/Framework/Constraint/Cardinality/IsEmpty.php(4 ehf5phpunit/Framework/Constraint/Cardinality/LessThan.php(4 ea T5phpunit/Framework/Constraint/Cardinality/SameSize.php_(4 e_uŤ+phpunit/Framework/Constraint/Constraint.php"(4 e"*yK1phpunit/Framework/Constraint/Equality/IsEqual.php (4 e \hh?phpunit/Framework/Constraint/Equality/IsEqualCanonicalizing.php -(4 e -~=phpunit/Framework/Constraint/Equality/IsEqualIgnoringCase.php -(4 e -C\:phpunit/Framework/Constraint/Equality/IsEqualWithDelta.php -(4 e -(v4phpunit/Framework/Constraint/Exception/Exception.php(4 eRu{8phpunit/Framework/Constraint/Exception/ExceptionCode.php(4 eiأ;phpunit/Framework/Constraint/Exception/ExceptionMessage.php(4 ew;Lphpunit/Framework/Constraint/Exception/ExceptionMessageRegularExpression.php(4 eLj[i;phpunit/Framework/Constraint/Filesystem/DirectoryExists.phpj(4 eji+6phpunit/Framework/Constraint/Filesystem/FileExists.phpe(4 eeK6phpunit/Framework/Constraint/Filesystem/IsReadable.phpe(4 ee16phpunit/Framework/Constraint/Filesystem/IsWritable.phpe(4 ee+phpunit/Framework/Constraint/IsAnything.php(4 eE,phpunit/Framework/Constraint/IsIdentical.php (4 e &,phpunit/Framework/Constraint/JsonMatches.php% (4 e% 儃@phpunit/Framework/Constraint/JsonMatchesErrorMessageProvider.php5(4 e5mһ.phpunit/Framework/Constraint/Math/IsFinite.php(4 eZҗ0phpunit/Framework/Constraint/Math/IsInfinite.php(4 e'*~+phpunit/Framework/Constraint/Math/IsNan.php(4 e4g09phpunit/Framework/Constraint/Object/ClassHasAttribute.php(4 e*?phpunit/Framework/Constraint/Object/ClassHasStaticAttribute.php*(4 e*!%4phpunit/Framework/Constraint/Object/ObjectEquals.php -(4 e -0W:phpunit/Framework/Constraint/Object/ObjectHasAttribute.php(4 e$9phpunit/Framework/Constraint/Object/ObjectHasProperty.php(4 e|8phpunit/Framework/Constraint/Operator/BinaryOperator.phpG(4 eGS\4phpunit/Framework/Constraint/Operator/LogicalAnd.php(4 ebJ4phpunit/Framework/Constraint/Operator/LogicalNot.php (4 e 3phpunit/Framework/Constraint/Operator/LogicalOr.php(4 eZ4phpunit/Framework/Constraint/Operator/LogicalXor.php$(4 e$O2phpunit/Framework/Constraint/Operator/Operator.php&(4 e& Dܤ7phpunit/Framework/Constraint/Operator/UnaryOperator.php -(4 e - a.phpunit/Framework/Constraint/String/IsJson.php(4 e\@9phpunit/Framework/Constraint/String/RegularExpression.php(4 e+J6phpunit/Framework/Constraint/String/StringContains.php(4 eij"6phpunit/Framework/Constraint/String/StringEndsWith.php(4 e{Fphpunit/Framework/Constraint/String/StringMatchesFormatDescription.php -(4 e -J8phpunit/Framework/Constraint/String/StringStartsWith.php&(4 e&c>8phpunit/Framework/Constraint/Traversable/ArrayHasKey.php(4 e6@!@phpunit/Framework/Constraint/Traversable/TraversableContains.php(4 eEphpunit/Framework/Constraint/Traversable/TraversableContainsEqual.phpa(4 eawAIphpunit/Framework/Constraint/Traversable/TraversableContainsIdentical.php'(4 e'sӤDphpunit/Framework/Constraint/Traversable/TraversableContainsOnly.php (4 e RuФ2phpunit/Framework/Constraint/Type/IsInstanceOf.php:(4 e:@,phpunit/Framework/Constraint/Type/IsNull.php(4 e?),phpunit/Framework/Constraint/Type/IsType.php(4 eGȤ+phpunit/Framework/DataProviderTestSuite.php(4 e\8&phpunit/Framework/Error/Deprecated.phpz(4 ezV!phpunit/Framework/Error/Error.phpl(4 el]"phpunit/Framework/Error/Notice.phpv(4 evˤ#phpunit/Framework/Error/Warning.phpw(4 ewG#phpunit/Framework/ErrorTestCase.php(4 ecȶAphpunit/Framework/Exception/ActualValueIsNotAnObjectException.php(4 e`B4phpunit/Framework/Exception/AssertionFailedError.php(4 e5phpunit/Framework/Exception/CodeCoverageException.php(4 e[Sphpunit/Framework/Exception/ComparisonMethodDoesNotAcceptParameterTypeException.phpk(4 ek:phpunit/Framework/MockObject/Builder/InvocationStubber.php(4 e8phpunit/Framework/MockObject/Builder/MethodNameMatch.phpw(4 ewTy8phpunit/Framework/MockObject/Builder/ParametersMatch.php(4 eڃ-phpunit/Framework/MockObject/Builder/Stub.php(4 e(3phpunit/Framework/MockObject/ConfigurableMethod.php(4 eAphpunit/Framework/MockObject/Exception/BadMethodCallException.php(4 eΫXGphpunit/Framework/MockObject/Exception/CannotUseAddMethodsException.php5(4 e5{Hphpunit/Framework/MockObject/Exception/CannotUseOnlyMethodsException.phpE(4 eEFphpunit/Framework/MockObject/Exception/ClassAlreadyExistsException.php(4 e@phpunit/Framework/MockObject/Exception/ClassIsFinalException.php(4 e()Cphpunit/Framework/MockObject/Exception/ClassIsReadonlyException.php(4 eOuXYphpunit/Framework/MockObject/Exception/ConfigurableMethodsAlreadyInitializedException.php (4 e ɅWCphpunit/Framework/MockObject/Exception/DuplicateMethodException.php(4 ey4phpunit/Framework/MockObject/Exception/Exception.php(4 eB'Kphpunit/Framework/MockObject/Exception/IncompatibleReturnValueException.php(4 e3dfEphpunit/Framework/MockObject/Exception/InvalidMethodNameException.php(4 e ܤHphpunit/Framework/MockObject/Exception/MatchBuilderNotFoundException.php(4 eLphpunit/Framework/MockObject/Exception/MatcherAlreadyRegisteredException.php(4 ez'Lphpunit/Framework/MockObject/Exception/MethodCannotBeConfiguredException.php(4 e}QOphpunit/Framework/MockObject/Exception/MethodNameAlreadyConfiguredException.php(4 eӁƤKphpunit/Framework/MockObject/Exception/MethodNameNotConfiguredException.php~(4 e~x1)Uphpunit/Framework/MockObject/Exception/MethodParametersAlreadyConfiguredException.php(4 e rYphpunit/Framework/MockObject/Exception/OriginalConstructorInvocationRequiredException.php(4 eک>phpunit/Framework/MockObject/Exception/ReflectionException.php(4 e.ؔLphpunit/Framework/MockObject/Exception/ReturnValueNotConfiguredException.php6(4 e6?먙;phpunit/Framework/MockObject/Exception/RuntimeException.php(4 e_|Mphpunit/Framework/MockObject/Exception/SoapExtensionNotAvailableException.php(4 ez@phpunit/Framework/MockObject/Exception/UnknownClassException.php(4 e5uW@phpunit/Framework/MockObject/Exception/UnknownTraitException.php(4 eq¥?phpunit/Framework/MockObject/Exception/UnknownTypeException.php(4 e~*phpunit/Framework/MockObject/Generator.phpb(4 ebiEۤ6phpunit/Framework/MockObject/Generator/deprecation.tpl;(4 e;O5s7phpunit/Framework/MockObject/Generator/intersection.tplL(4 eL-X7phpunit/Framework/MockObject/Generator/mocked_class.tpl(4 ewZ8phpunit/Framework/MockObject/Generator/mocked_method.tplF(4 eFKFphpunit/Framework/MockObject/Generator/mocked_method_never_or_void.tpl(4 ep?phpunit/Framework/MockObject/Generator/mocked_static_method.tpl(4 e 4R9phpunit/Framework/MockObject/Generator/proxied_method.tpl}(4 e}@ėGphpunit/Framework/MockObject/Generator/proxied_method_never_or_void.tplv(4 evT6phpunit/Framework/MockObject/Generator/trait_class.tplQ(4 eQ<Ȥ5phpunit/Framework/MockObject/Generator/wsdl_class.tpl(4 e6phpunit/Framework/MockObject/Generator/wsdl_method.tpl<(4 e<i+phpunit/Framework/MockObject/Invocation.php(4 eid2phpunit/Framework/MockObject/InvocationHandler.php:(4 e:ˤ(phpunit/Framework/MockObject/Matcher.php(4 eD-5phpunit/Framework/MockObject/MethodNameConstraint.php -(4 e -A1|,phpunit/Framework/MockObject/MockBuilder.phpY+(4 eY+ϴ=*phpunit/Framework/MockObject/MockClass.php(4 e'C+phpunit/Framework/MockObject/MockMethod.phpz&(4 ez&p.phpunit/Framework/MockObject/MockMethodSet.php8(4 e8G\+phpunit/Framework/MockObject/MockObject.php(4 ebt*phpunit/Framework/MockObject/MockTrait.php(4 e&nä)phpunit/Framework/MockObject/MockType.php(4 eFFt5phpunit/Framework/MockObject/Rule/AnyInvokedCount.phpj(4 ej`Ť3phpunit/Framework/MockObject/Rule/AnyParameters.php(4 e~';phpunit/Framework/MockObject/Rule/ConsecutiveParameters.php~ (4 e~ 5phpunit/Framework/MockObject/Rule/InvocationOrder.php(4 eLDӤ4phpunit/Framework/MockObject/Rule/InvokedAtIndex.php,(4 e,kK9phpunit/Framework/MockObject/Rule/InvokedAtLeastCount.php(4 eB8phpunit/Framework/MockObject/Rule/InvokedAtLeastOnce.php-(4 e- (8phpunit/Framework/MockObject/Rule/InvokedAtMostCount.php(4 egY2phpunit/Framework/MockObject/Rule/InvokedCount.php (4 e ^ 0phpunit/Framework/MockObject/Rule/MethodName.php(4 e -WG0phpunit/Framework/MockObject/Rule/Parameters.phpQ(4 eQ`g|4phpunit/Framework/MockObject/Rule/ParametersRule.phpc(4 ec?(%phpunit/Framework/MockObject/Stub.php(4 eŎ6phpunit/Framework/MockObject/Stub/ConsecutiveCalls.php (4 e ./phpunit/Framework/MockObject/Stub/Exception.php((4 e(J4phpunit/Framework/MockObject/Stub/ReturnArgument.php(4 e?}64phpunit/Framework/MockObject/Stub/ReturnCallback.php(4 eD0Ӥ5phpunit/Framework/MockObject/Stub/ReturnReference.php (4 e f0phpunit/Framework/MockObject/Stub/ReturnSelf.php4(4 e4DD0phpunit/Framework/MockObject/Stub/ReturnStub.php(4 e4phpunit/Framework/MockObject/Stub/ReturnValueMap.php(4 eۤ*phpunit/Framework/MockObject/Stub/Stub.php3(4 e3>++phpunit/Framework/MockObject/Verifiable.php(4 e̐ s!phpunit/Framework/Reorderable.php(4 ez0$phpunit/Framework/SelfDescribing.php -(4 e -s!phpunit/Framework/SkippedTest.php(4 eS.%phpunit/Framework/SkippedTestCase.php(4 eQKhphpunit/Framework/Test.php~(4 e~wt!phpunit/Framework/TestBuilder.php"(4 e"14jphpunit/Framework/TestCase.php.(4 e.] !phpunit/Framework/TestFailure.php(4 e'q"phpunit/Framework/TestListener.phpr(4 erӪc^7phpunit/Framework/TestListenerDefaultImplementation.php'(4 e'! phpunit/Framework/TestResult.php -(4 e -rOphpunit/Framework/TestSuite.phpQd(4 eQd5ܛ'phpunit/Framework/TestSuiteIterator.php6(4 e6$ u%phpunit/Framework/WarningTestCase.php'(4 e'n@ !phpunit/Runner/BaseTestRunner.php (4 e C +)phpunit/Runner/DefaultTestResultCache.php!(4 e!/i^phpunit/Runner/Exception.php(4 ezZ-phpunit/Runner/Extension/ExtensionHandler.php (4 e Az'phpunit/Runner/Extension/PharLoader.php (4 e /4phpunit/Runner/Filter/ExcludeGroupFilterIterator.phps(4 es} -Z!phpunit/Runner/Filter/Factory.php(4 edcΤ-phpunit/Runner/Filter/GroupFilterIterator.php(4 e=;4phpunit/Runner/Filter/IncludeGroupFilterIterator.phpr(4 erP;AD,phpunit/Runner/Filter/NameFilterIterator.phpv (4 ev Z/phpunit/Runner/Hook/AfterIncompleteTestHook.php-(4 e-zԤ)phpunit/Runner/Hook/AfterLastTestHook.php(4 e0B֤*phpunit/Runner/Hook/AfterRiskyTestHook.php#(4 e#dm,phpunit/Runner/Hook/AfterSkippedTestHook.php'(4 e':/phpunit/Runner/Hook/AfterSuccessfulTestHook.php(4 e5w*phpunit/Runner/Hook/AfterTestErrorHook.php#(4 e#ݮ,phpunit/Runner/Hook/AfterTestFailureHook.php'(4 e'2F%phpunit/Runner/Hook/AfterTestHook.php(4 e;gA,phpunit/Runner/Hook/AfterTestWarningHook.php'(4 e'':+phpunit/Runner/Hook/BeforeFirstTestHook.php(4 ehWt&phpunit/Runner/Hook/BeforeTestHook.php(4 e"bphpunit/Runner/Hook/Hook.php(4 e. phpunit/Runner/Hook/TestHook.php(4 eZ_ -+phpunit/Runner/Hook/TestListenerAdapter.php(4 e\6E&phpunit/Runner/NullTestResultCache.php(4 eW<phpunit/Runner/PhptTestCase.phpRV(4 eRVԴO'phpunit/Runner/ResultCacheExtension.php<(4 e<6 _*phpunit/Runner/StandardTestSuiteLoader.php(4 e;i Ȥ"phpunit/Runner/TestResultCache.php(4 eK"phpunit/Runner/TestSuiteLoader.php(4 eޤ"phpunit/Runner/TestSuiteSorter.php,(4 e,kڤphpunit/Runner/Version.php(4 e'phpunit/TextUI/CliArguments/Builder.phpT(4 eTɣۤ-phpunit/TextUI/CliArguments/Configuration.php(4 eX)phpunit/TextUI/CliArguments/Exception.php(4 e%zE&phpunit/TextUI/CliArguments/Mapper.php+,(4 e+,'aphpunit/TextUI/Command.php`o(4 e`o;e"'phpunit/TextUI/DefaultResultPrinter.phpY7(4 eY7}G(J&phpunit/TextUI/Exception/Exception.php(4 eD{i0phpunit/TextUI/Exception/ReflectionException.php(4 e Y-phpunit/TextUI/Exception/RuntimeException.php(4 eF;phpunit/TextUI/Exception/TestDirectoryNotFoundException.php(4 e6phpunit/TextUI/Exception/TestFileNotFoundException.php(4 epCphpunit/TextUI/Help.php.(4 e.  phpunit/TextUI/ResultPrinter.phpp(4 epܤphpunit/TextUI/TestRunner.php(4 eG"phpunit/TextUI/TestSuiteMapper.php (4 e +;-=phpunit/TextUI/XmlConfiguration/CodeCoverage/CodeCoverage.php(4 erAphpunit/TextUI/XmlConfiguration/CodeCoverage/Filter/Directory.php(4 ec{Kphpunit/TextUI/XmlConfiguration/CodeCoverage/Filter/DirectoryCollection.php(4 eju}Sphpunit/TextUI/XmlConfiguration/CodeCoverage/Filter/DirectoryCollectionIterator.php(4 eJ=phpunit/TextUI/XmlConfiguration/CodeCoverage/FilterMapper.php(4 e}ݚ>phpunit/TextUI/XmlConfiguration/CodeCoverage/Report/Clover.php(4 e=CAphpunit/TextUI/XmlConfiguration/CodeCoverage/Report/Cobertura.php(4 ei>phpunit/TextUI/XmlConfiguration/CodeCoverage/Report/Crap4j.php(4 eG<phpunit/TextUI/XmlConfiguration/CodeCoverage/Report/Html.php(4 eE6;phpunit/TextUI/XmlConfiguration/CodeCoverage/Report/Php.php(4 epS<phpunit/TextUI/XmlConfiguration/CodeCoverage/Report/Text.php(4 eKkw;phpunit/TextUI/XmlConfiguration/CodeCoverage/Report/Xml.php(4 e?u1phpunit/TextUI/XmlConfiguration/Configuration.php5(4 e5˞-phpunit/TextUI/XmlConfiguration/Exception.php(4 eN5+8phpunit/TextUI/XmlConfiguration/Filesystem/Directory.php(4 e@Bphpunit/TextUI/XmlConfiguration/Filesystem/DirectoryCollection.php(4 e1EqJphpunit/TextUI/XmlConfiguration/Filesystem/DirectoryCollectionIterator.php(4 e&3phpunit/TextUI/XmlConfiguration/Filesystem/File.php(4 e.P =phpunit/TextUI/XmlConfiguration/Filesystem/FileCollection.php~(4 e~]rEphpunit/TextUI/XmlConfiguration/Filesystem/FileCollectionIterator.phpf(4 efĤ-phpunit/TextUI/XmlConfiguration/Generator.php(4 eF /phpunit/TextUI/XmlConfiguration/Group/Group.php(4 e9phpunit/TextUI/XmlConfiguration/Group/GroupCollection.php(4 eyAphpunit/TextUI/XmlConfiguration/Group/GroupCollectionIterator.phpq(4 eqY50phpunit/TextUI/XmlConfiguration/Group/Groups.php(4 e@I*phpunit/TextUI/XmlConfiguration/Loader.php(4 ecB1phpunit/TextUI/XmlConfiguration/Logging/Junit.php(4 eciG3phpunit/TextUI/XmlConfiguration/Logging/Logging.php (4 e ]٤4phpunit/TextUI/XmlConfiguration/Logging/TeamCity.php(4 e7Z鵤8phpunit/TextUI/XmlConfiguration/Logging/TestDox/Html.php(4 eV2ܤ8phpunit/TextUI/XmlConfiguration/Logging/TestDox/Text.php(4 eώ7phpunit/TextUI/XmlConfiguration/Logging/TestDox/Xml.php(4 et0phpunit/TextUI/XmlConfiguration/Logging/Text.php(4 eCn>phpunit/TextUI/XmlConfiguration/Migration/MigrationBuilder.php (4 e r:Gphpunit/TextUI/XmlConfiguration/Migration/MigrationBuilderException.php(4 eUWĝ@phpunit/TextUI/XmlConfiguration/Migration/MigrationException.php(4 e\ZHphpunit/TextUI/XmlConfiguration/Migration/Migrations/ConvertLogTypes.php(4 ehoeOphpunit/TextUI/XmlConfiguration/Migration/Migrations/CoverageCloverToReport.phpX(4 eXijOphpunit/TextUI/XmlConfiguration/Migration/Migrations/CoverageCrap4jToReport.php(4 e$i'Mphpunit/TextUI/XmlConfiguration/Migration/Migrations/CoverageHtmlToReport.php(4 eՄjLphpunit/TextUI/XmlConfiguration/Migration/Migrations/CoveragePhpToReport.phpF(4 eF^ӤMphpunit/TextUI/XmlConfiguration/Migration/Migrations/CoverageTextToReport.php(4 eV_Lphpunit/TextUI/XmlConfiguration/Migration/Migrations/CoverageXmlToReport.phpK(4 eK_ Qphpunit/TextUI/XmlConfiguration/Migration/Migrations/IntroduceCoverageElement.php(4 eUMphpunit/TextUI/XmlConfiguration/Migration/Migrations/LogToReportMigration.php(4 eUBphpunit/TextUI/XmlConfiguration/Migration/Migrations/Migration.php(4 e'dphpunit/TextUI/XmlConfiguration/Migration/Migrations/MoveAttributesFromFilterWhitelistToCoverage.php(4 eU%5Yphpunit/TextUI/XmlConfiguration/Migration/Migrations/MoveAttributesFromRootToCoverage.phpC(4 eCcFXphpunit/TextUI/XmlConfiguration/Migration/Migrations/MoveWhitelistExcludesToCoverage.php(4 eXphpunit/TextUI/XmlConfiguration/Migration/Migrations/MoveWhitelistIncludesToCoverage.php(4 etSphpunit/TextUI/XmlConfiguration/Migration/Migrations/RemoveCacheTokensAttribute.php(4 ewJphpunit/TextUI/XmlConfiguration/Migration/Migrations/RemoveEmptyFilter.php{(4 e{KGphpunit/TextUI/XmlConfiguration/Migration/Migrations/RemoveLogTypes.phpo(4 eo3Qphpunit/TextUI/XmlConfiguration/Migration/Migrations/UpdateSchemaLocationTo93.php(4 ebJ6phpunit/TextUI/XmlConfiguration/Migration/Migrator.php(4 eo$V0phpunit/TextUI/XmlConfiguration/PHP/Constant.php7(4 e7$Ҥ:phpunit/TextUI/XmlConfiguration/PHP/ConstantCollection.phpl(4 el%(Bphpunit/TextUI/XmlConfiguration/PHP/ConstantCollectionIterator.php(4 e}=Ƥ2phpunit/TextUI/XmlConfiguration/PHP/IniSetting.phpJ(4 eJOt<phpunit/TextUI/XmlConfiguration/PHP/IniSettingCollection.php(4 eޛ;Dphpunit/TextUI/XmlConfiguration/PHP/IniSettingCollectionIterator.php(4 e/mo+phpunit/TextUI/XmlConfiguration/PHP/Php.php(4 e6僤2phpunit/TextUI/XmlConfiguration/PHP/PhpHandler.phpw(4 ew` -0phpunit/TextUI/XmlConfiguration/PHP/Variable.php(4 eN:phpunit/TextUI/XmlConfiguration/PHP/VariableCollection.phpl(4 elsB@٤Bphpunit/TextUI/XmlConfiguration/PHP/VariableCollectionIterator.php(4 e!~gȤ5phpunit/TextUI/XmlConfiguration/PHPUnit/Extension.php(4 e}Q?phpunit/TextUI/XmlConfiguration/PHPUnit/ExtensionCollection.php(4 eo;RGphpunit/TextUI/XmlConfiguration/PHPUnit/ExtensionCollectionIterator.php(4 e|D?3phpunit/TextUI/XmlConfiguration/PHPUnit/PHPUnit.phplC(4 elCv;phpunit/TextUI/XmlConfiguration/TestSuite/TestDirectory.phpC(4 eC0Ephpunit/TextUI/XmlConfiguration/TestSuite/TestDirectoryCollection.php(4 eLCMphpunit/TextUI/XmlConfiguration/TestSuite/TestDirectoryCollectionIterator.php(4 en6phpunit/TextUI/XmlConfiguration/TestSuite/TestFile.php(4 e?y@phpunit/TextUI/XmlConfiguration/TestSuite/TestFileCollection.php(4 eXHphpunit/TextUI/XmlConfiguration/TestSuite/TestFileCollectionIterator.phpz(4 ezX17phpunit/TextUI/XmlConfiguration/TestSuite/TestSuite.php(4 e8wAphpunit/TextUI/XmlConfiguration/TestSuite/TestSuiteCollection.php(4 e/jIphpunit/TextUI/XmlConfiguration/TestSuite/TestSuiteCollectionIterator.php(4 e+6$phpunit/Util/Annotation/DocBlock.php@(4 e@n"$phpunit/Util/Annotation/Registry.phpN -(4 eN -?caphpunit/Util/Blacklist.php(4 esphpunit/Util/Cloner.php(4 e"Ɩܤphpunit/Util/Color.php(4 ej?phpunit/Util/ErrorHandler.php(4 e=phpunit/Util/Exception.php(4 e다phpunit/Util/ExcludeList.php(4 efphpunit/Util/FileLoader.php (4 e 'phpunit/Util/Filesystem.php(4 eܐphpunit/Util/Filter.php (4 e l* phpunit/Util/GlobalState.php(4 el(phpunit/Util/InvalidDataSetException.php(4 e1 phpunit/Util/Json.phpE (4 eE !phpunit/Util/Log/JUnit.phpb*(4 eb*}3phpunit/Util/Log/TeamCity.phpy&(4 ey&4g'phpunit/Util/PHP/AbstractPhpProcess.php'(4 e'-Eʤ&phpunit/Util/PHP/DefaultPhpProcess.phpz(4 ezCp*phpunit/Util/PHP/Template/PhptTestCase.tpl(4 e+phpunit/Util/PHP/Template/TestCaseClass.tpl (4 e #,phpunit/Util/PHP/Template/TestCaseMethod.tpl*(4 e* &phpunit/Util/PHP/WindowsPhpProcess.php(4 e)aBphpunit/Util/Printer.php (4 e shphpunit/Util/Reflection.php(4 eW챤"phpunit/Util/RegularExpression.php(4 e0uR)phpunit/Util/Test.php](4 e]1fä*phpunit/Util/TestDox/CliTestDoxPrinter.php(*(4 e(*@f*phpunit/Util/TestDox/HtmlResultPrinter.php (4 e 3~ʤ'phpunit/Util/TestDox/NamePrettifier.php/"(4 e/"p~J&phpunit/Util/TestDox/ResultPrinter.php"(4 e"1q$'phpunit/Util/TestDox/TestDoxPrinter.php)(4 e)K̤*phpunit/Util/TestDox/TextResultPrinter.php(4 eȹ!.)phpunit/Util/TestDox/XmlResultPrinter.php(4 eQ%phpunit/Util/TextTestListRenderer.php6(4 e6.phpunit/Util/Type.php(4 e|ä*phpunit/Util/VersionComparisonOperator.php(4 eb,phpunit/Util/XdebugFilterScriptGenerator.phpw(4 ewتphpunit/Util/Xml.php(4 e̤phpunit/Util/Xml/Exception.php(4 eӤ0phpunit/Util/Xml/FailedSchemaDetectionResult.php(4 e#Sphpunit/Util/Xml/Loader.php (4 e ,?*phpunit/Util/Xml/SchemaDetectionResult.php(4 e4χz#phpunit/Util/Xml/SchemaDetector.php-(4 e-!phpunit/Util/Xml/SchemaFinder.php(4 eS%phpunit/Util/Xml/SnapshotNodeList.phpH(4 eH ^d4phpunit/Util/Xml/SuccessfulSchemaDetectionResult.php'(4 e'g%phpunit/Util/Xml/ValidationResult.php(4 exv:phpunit/Util/Xml/Validator.php(4 eV$phpunit/Util/XmlTestListRenderer.php -(4 e -8sbom.xml -/(4 e -/OOschema/8.5.xsdB(4 eB贅schema/9.2.xsdB(4 eB|lsebastian-cli-parser/LICENSE(4 eusebastian-cli-parser/Parser.php(4 ekM<sebastian-cli-parser/exceptions/AmbiguousOptionException.phpF(4 eFm\-sebastian-cli-parser/exceptions/Exception.phpu(4 euӫGsebastian-cli-parser/exceptions/OptionDoesNotAllowArgumentException.php_(4 e_|13Jsebastian-cli-parser/exceptions/RequiredOptionArgumentMissingException.phph(4 ehC:sebastian-cli-parser/exceptions/UnknownOptionException.php?(4 e?vD*sebastian-code-unit-reverse-lookup/LICENSE(4 e3G (-sebastian-code-unit-reverse-lookup/Wizard.php (4 e }Z['sebastian-code-unit/ClassMethodUnit.php(4 e@[!sebastian-code-unit/ClassUnit.php(4 eF sebastian-code-unit/CodeUnit.php~%(4 e~%D){*sebastian-code-unit/CodeUnitCollection.php(4 eJ2sebastian-code-unit/CodeUnitCollectionIterator.php;(4 e;Lʤ$sebastian-code-unit/FunctionUnit.php(4 e`+sebastian-code-unit/InterfaceMethodUnit.php(4 eǦ%sebastian-code-unit/InterfaceUnit.php(4 ecsebastian-code-unit/LICENSE (4 e psebastian-code-unit/Mapper.php-(4 e-#'sebastian-code-unit/TraitMethodUnit.php(4 eqz!sebastian-code-unit/TraitUnit.php(4 eXA,sebastian-code-unit/exceptions/Exception.phps(4 estg;sebastian-code-unit/exceptions/InvalidCodeUnitException.php(4 e6-3sebastian-code-unit/exceptions/NoTraitException.php(4 eQ36sebastian-code-unit/exceptions/ReflectionException.php(4 e$(sebastian-comparator/ArrayComparator.phpu(4 euEmhf#sebastian-comparator/Comparator.php(4 et*sebastian-comparator/ComparisonFailure.php (4 e %*sebastian-comparator/DOMNodeComparator.php (4 e 1i+sebastian-comparator/DateTimeComparator.php (4 e KQ)sebastian-comparator/DoubleComparator.php(4 e:n,sebastian-comparator/ExceptionComparator.php(4 e1 sebastian-comparator/Factory.php(4 e?Nsebastian-comparator/LICENSE (4 e =(-sebastian-comparator/MockObjectComparator.php(4 eI*sebastian-comparator/NumericComparator.php3 (4 e3 i{l)sebastian-comparator/ObjectComparator.phpX (4 eX ׌+sebastian-comparator/ResourceComparator.php(4 eJ)sebastian-comparator/ScalarComparator.php/ (4 e/ dF3sebastian-comparator/SplObjectStorageComparator.php(4 e?/'sebastian-comparator/TypeComparator.php(4 ecX\-sebastian-comparator/exceptions/Exception.phpv(4 evEᵤ4sebastian-comparator/exceptions/RuntimeException.php(4 eV'#sebastian-complexity/Calculator.phpe (4 ee (6.sebastian-complexity/Complexity/Complexity.phpQ(4 eQl8sebastian-complexity/Complexity/ComplexityCollection.php(4 eil@sebastian-complexity/Complexity/ComplexityCollectionIterator.php,(4 e,e,sebastian-complexity/Exception/Exception.phpv(4 ev73sebastian-complexity/Exception/RuntimeException.php(4 eCdWsebastian-complexity/LICENSE(4 e=ݤ=sebastian-complexity/Visitor/ComplexityCalculatingVisitor.php (4 e OGsebastian-complexity/Visitor/CyclomaticComplexityCalculatingVisitor.php (4 e 7Ysebastian-diff/Chunk.php_(4 e_vsebastian-diff/Diff.phpj(4 ejbXAsebastian-diff/Differ.php $(4 e $wkz3sebastian-diff/Exception/ConfigurationException.php=(4 e=1/Ff&sebastian-diff/Exception/Exception.phpj(4 ej05sebastian-diff/Exception/InvalidArgumentException.php(4 eqsebastian-diff/LICENSE (4 e a1sebastian-diff/Line.phpL(4 eL -q5sebastian-diff/LongestCommonSubsequenceCalculator.php(4 e}e7zDsebastian-diff/MemoryEfficientLongestCommonSubsequenceCalculator.php{ (4 e{ d^4sebastian-diff/Output/AbstractChunkOutputBuilder.php(4 e\t/sebastian-diff/Output/DiffOnlyOutputBuilder.phpz(4 ezc4sebastian-diff/Output/DiffOutputBuilderInterface.php(4 eV8sebastian-diff/Output/StrictUnifiedDiffOutputBuilder.php((4 e(kv2sebastian-diff/Output/UnifiedDiffOutputBuilder.php>(4 e>'q)sebastian-diff/Parser.php (4 e X{Bsebastian-diff/TimeEfficientLongestCommonSubsequenceCalculator.php0 (4 e0 B !sebastian-environment/Console.php(4 eP1Ťsebastian-environment/LICENSE(4 eFy٤)sebastian-environment/OperatingSystem.php(4 ē!sebastian-environment/Runtime.php(4 e4sebastian-exporter/Exporter.phpx$(4 ex$sebastian-exporter/LICENSE(4 e 5٤'sebastian-global-state/CodeExporter.php (4 e &sebastian-global-state/ExcludeList.php -(4 e -R{sebastian-global-state/LICENSE(4 eJ#sebastian-global-state/Restorer.php(4 e9}b#sebastian-global-state/Snapshot.php*(4 e*X%/sebastian-global-state/exceptions/Exception.phpy(4 eyJ6sebastian-global-state/exceptions/RuntimeException.php(4 e;#sebastian-lines-of-code/Counter.php(4 eH5/sebastian-lines-of-code/Exception/Exception.phpz(4 ez aV>sebastian-lines-of-code/Exception/IllogicalValuesException.php(4 eG<sebastian-lines-of-code/Exception/NegativeValueException.php(4 e -ڤ6sebastian-lines-of-code/Exception/RuntimeException.php(4 eKsebastian-lines-of-code/LICENSE(4 ebS~/sebastian-lines-of-code/LineCountingVisitor.php(4 e~A'sebastian-lines-of-code/LinesOfCode.php (4 e fӤ*sebastian-object-enumerator/Enumerator.php(4 ex})sebastian-object-enumerator/Exception.php(4 e}Ȥ8sebastian-object-enumerator/InvalidArgumentException.php(4 eâ(sebastian-object-reflector/Exception.php(4 eЬۤ7sebastian-object-reflector/InvalidArgumentException.php(4 e -M.sebastian-object-reflector/ObjectReflector.php(4 e_'sebastian-recursion-context/Context.phpB(4 eBf> +*BX<doctrine-instantiator/Doctrine/Instantiator/Instantiator.phpfk9Edoctrine-instantiator/Doctrine/Instantiator/InstantiatorInterface.phpf1sXdoctrine-instantiator/LICENSE$f$ +͂ manifest.txt f jɤ'myclabs-deep-copy/DeepCopy/DeepCopy.phpf!]7myclabs-deep-copy/DeepCopy/Exception/CloneException.phpfJDȤ:myclabs-deep-copy/DeepCopy/Exception/PropertyException.phpfo#5myclabs-deep-copy/DeepCopy/Filter/ChainableFilter.phpf=(eGmyclabs-deep-copy/DeepCopy/Filter/Doctrine/DoctrineCollectionFilter.phpfcLmyclabs-deep-copy/DeepCopy/Filter/Doctrine/DoctrineEmptyCollectionFilter.phpftBmyclabs-deep-copy/DeepCopy/Filter/Doctrine/DoctrineProxyFilter.phpffQc_,myclabs-deep-copy/DeepCopy/Filter/Filter.phphfh߽0myclabs-deep-copy/DeepCopy/Filter/KeepFilter.phpf7#3myclabs-deep-copy/DeepCopy/Filter/ReplaceFilter.phpf/bM3myclabs-deep-copy/DeepCopy/Filter/SetNullFilter.phpfCkDmyclabs-deep-copy/DeepCopy/Matcher/Doctrine/DoctrineProxyMatcher.phpf 3.myclabs-deep-copy/DeepCopy/Matcher/Matcher.phpffä6myclabs-deep-copy/DeepCopy/Matcher/PropertyMatcher.phpfA^:myclabs-deep-copy/DeepCopy/Matcher/PropertyNameMatcher.phpfP:myclabs-deep-copy/DeepCopy/Matcher/PropertyTypeMatcher.php:f:A,A:myclabs-deep-copy/DeepCopy/Reflection/ReflectionHelper.php=f=߈:7Amyclabs-deep-copy/DeepCopy/TypeFilter/Date/DateIntervalFilter.phpf[7myclabs-deep-copy/DeepCopy/TypeFilter/ReplaceFilter.phpfWp;myclabs-deep-copy/DeepCopy/TypeFilter/ShallowCopyFilter.phpfF_e?myclabs-deep-copy/DeepCopy/TypeFilter/Spl/ArrayObjectFilter.phpfةAmyclabs-deep-copy/DeepCopy/TypeFilter/Spl/SplDoublyLinkedList.phpfK픤Gmyclabs-deep-copy/DeepCopy/TypeFilter/Spl/SplDoublyLinkedListFilter.php)f)q*4myclabs-deep-copy/DeepCopy/TypeFilter/TypeFilter.phpfԊ6myclabs-deep-copy/DeepCopy/TypeMatcher/TypeMatcher.phpf/(myclabs-deep-copy/DeepCopy/deep_copy.phpfmnmyclabs-deep-copy/LICENSE5f5ʭ˄nikic-php-parser/LICENSEf*&nikic-php-parser/PhpParser/Builder.phpf̝Ƥ1nikic-php-parser/PhpParser/Builder/ClassConst.phpfG E-nikic-php-parser/PhpParser/Builder/Class_.phpf82nikic-php-parser/PhpParser/Builder/Declaration.phpft5/nikic-php-parser/PhpParser/Builder/EnumCase.phpvfvE ,nikic-php-parser/PhpParser/Builder/Enum_.php f -,3nikic-php-parser/PhpParser/Builder/FunctionLike.phpfS0nikic-php-parser/PhpParser/Builder/Function_.phpZfZ0O1nikic-php-parser/PhpParser/Builder/Interface_.php f h"z-nikic-php-parser/PhpParser/Builder/Method.phpfP1nikic-php-parser/PhpParser/Builder/Namespace_.phpNfN,nikic-php-parser/PhpParser/Builder/Param.phpfqh/nikic-php-parser/PhpParser/Builder/Property.phpfr/nikic-php-parser/PhpParser/Builder/TraitUse.phpkfk?Z9nikic-php-parser/PhpParser/Builder/TraitUseAdaptation.phpf֤-nikic-php-parser/PhpParser/Builder/Trait_.phpfg+nikic-php-parser/PhpParser/Builder/Use_.phpfN-nikic-php-parser/PhpParser/BuilderFactory.php:+f:+~P7-nikic-php-parser/PhpParser/BuilderHelpers.php$f$0Ӥ&nikic-php-parser/PhpParser/Comment.phpfx:*nikic-php-parser/PhpParser/Comment/Doc.phpf袤;nikic-php-parser/PhpParser/ConstExprEvaluationException.phpcfc:ݻ1nikic-php-parser/PhpParser/ConstExprEvaluator.phpy%fy%2h$nikic-php-parser/PhpParser/Error.phpf`+nikic-php-parser/PhpParser/ErrorHandler.php3f3c6nikic-php-parser/PhpParser/ErrorHandler/Collecting.phpfj4nikic-php-parser/PhpParser/ErrorHandler/Throwing.phpfzp0nikic-php-parser/PhpParser/Internal/DiffElem.php;f;+'.nikic-php-parser/PhpParser/Internal/Differ.php1f1PAnikic-php-parser/PhpParser/Internal/PrintableNewAnonClassNode.phpfi3nikic-php-parser/PhpParser/Internal/TokenStream.php$f$Dw*nikic-php-parser/PhpParser/JsonDecoder.php$ f$ z$nikic-php-parser/PhpParser/Lexer.phpZfZ Τ.nikic-php-parser/PhpParser/Lexer/Emulative.php#f#KQ=Dnikic-php-parser/PhpParser/Lexer/TokenEmulator/AttributeEmulator.phpfmMLnikic-php-parser/PhpParser/Lexer/TokenEmulator/CoaleseEqualTokenEmulator.phpf:(Dnikic-php-parser/PhpParser/Lexer/TokenEmulator/EnumTokenEmulator.phpfRHnikic-php-parser/PhpParser/Lexer/TokenEmulator/ExplicitOctalEmulator.phpf7ǤLnikic-php-parser/PhpParser/Lexer/TokenEmulator/FlexibleDocStringEmulator.phpv fv a/Bnikic-php-parser/PhpParser/Lexer/TokenEmulator/FnTokenEmulator.phpf1@dBnikic-php-parser/PhpParser/Lexer/TokenEmulator/KeywordEmulator.phpfƤEnikic-php-parser/PhpParser/Lexer/TokenEmulator/MatchTokenEmulator.phpfFiHnikic-php-parser/PhpParser/Lexer/TokenEmulator/NullsafeTokenEmulator.phpfAdRnikic-php-parser/PhpParser/Lexer/TokenEmulator/NumericLiteralSeparatorEmulator.php^f^,Pnikic-php-parser/PhpParser/Lexer/TokenEmulator/ReadonlyFunctionTokenEmulator.phpfzbgHnikic-php-parser/PhpParser/Lexer/TokenEmulator/ReadonlyTokenEmulator.phpTfT_߇gBnikic-php-parser/PhpParser/Lexer/TokenEmulator/ReverseEmulator.phpf d#@nikic-php-parser/PhpParser/Lexer/TokenEmulator/TokenEmulator.phpyfy\"*nikic-php-parser/PhpParser/NameContext.php&f& E#nikic-php-parser/PhpParser/Node.phpf!b'nikic-php-parser/PhpParser/Node/Arg.php=f=?-nikic-php-parser/PhpParser/Node/Attribute.phpTfTΤ2nikic-php-parser/PhpParser/Node/AttributeGroup.phpfԡIM/nikic-php-parser/PhpParser/Node/ComplexType.php[f[0Us*nikic-php-parser/PhpParser/Node/Const_.phpfT}(nikic-php-parser/PhpParser/Node/Expr.phpf|)6nikic-php-parser/PhpParser/Node/Expr/ArrayDimFetch.phpVfV=|W2nikic-php-parser/PhpParser/Node/Expr/ArrayItem.phpf()/nikic-php-parser/PhpParser/Node/Expr/Array_.php@f@- 6nikic-php-parser/PhpParser/Node/Expr/ArrowFunction.php f =9/nikic-php-parser/PhpParser/Node/Expr/Assign.phpf&N1nikic-php-parser/PhpParser/Node/Expr/AssignOp.phpf ><nikic-php-parser/PhpParser/Node/Expr/AssignOp/BitwiseAnd.phpf`Q<;nikic-php-parser/PhpParser/Node/Expr/AssignOp/BitwiseOr.phpfO<nikic-php-parser/PhpParser/Node/Expr/AssignOp/BitwiseXor.phpfP3Ӥ:nikic-php-parser/PhpParser/Node/Expr/AssignOp/Coalesce.phpf  8nikic-php-parser/PhpParser/Node/Expr/AssignOp/Concat.phpf4cڤ5nikic-php-parser/PhpParser/Node/Expr/AssignOp/Div.phpfnikic-php-parser/PhpParser/Node/Expr/BinaryOp/NotIdentical.php^f^Rf86nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Plus.phpLfL ::5nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Pow.phpKfKMmи;nikic-php-parser/PhpParser/Node/Expr/BinaryOp/ShiftLeft.phpWfWei<nikic-php-parser/PhpParser/Node/Expr/BinaryOp/ShiftRight.phpYfY \639nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Smaller.phpRfRNYL@nikic-php-parser/PhpParser/Node/Expr/BinaryOp/SmallerOrEqual.phpafasa;nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Spaceship.phpXfX^ޤ3nikic-php-parser/PhpParser/Node/Expr/BitwiseNot.phpfTƼ3nikic-php-parser/PhpParser/Node/Expr/BooleanNot.phpfܐ@1nikic-php-parser/PhpParser/Node/Expr/CallLike.php6f6=p-nikic-php-parser/PhpParser/Node/Expr/Cast.phpIfI*4nikic-php-parser/PhpParser/Node/Expr/Cast/Array_.phpf7_ؤ3nikic-php-parser/PhpParser/Node/Expr/Cast/Bool_.phpfw4nikic-php-parser/PhpParser/Node/Expr/Cast/Double.phpf`9+2nikic-php-parser/PhpParser/Node/Expr/Cast/Int_.phpf%D5nikic-php-parser/PhpParser/Node/Expr/Cast/Object_.phpf/=5nikic-php-parser/PhpParser/Node/Expr/Cast/String_.phpf"k 4nikic-php-parser/PhpParser/Node/Expr/Cast/Unset_.phpfO58nikic-php-parser/PhpParser/Node/Expr/ClassConstFetch.phpf +/nikic-php-parser/PhpParser/Node/Expr/Clone_.phpf0nikic-php-parser/PhpParser/Node/Expr/Closure.php +f +d3nikic-php-parser/PhpParser/Node/Expr/ClosureUse.phpfJ3nikic-php-parser/PhpParser/Node/Expr/ConstFetch.phpf流./nikic-php-parser/PhpParser/Node/Expr/Empty_.phpfYФ.nikic-php-parser/PhpParser/Node/Expr/Error.php +f +|6nikic-php-parser/PhpParser/Node/Expr/ErrorSuppress.phpf}.nikic-php-parser/PhpParser/Node/Expr/Eval_.phpfmYg.nikic-php-parser/PhpParser/Node/Expr/Exit_.phpfS1nikic-php-parser/PhpParser/Node/Expr/FuncCall.php?f?V1nikic-php-parser/PhpParser/Node/Expr/Include_.phpfĚ:4nikic-php-parser/PhpParser/Node/Expr/Instanceof_.phpmfml/nikic-php-parser/PhpParser/Node/Expr/Isset_.phpfQ{.nikic-php-parser/PhpParser/Node/Expr/List_.phpf!B /nikic-php-parser/PhpParser/Node/Expr/Match_.phpf?N:3nikic-php-parser/PhpParser/Node/Expr/MethodCall.phpcfc[A-nikic-php-parser/PhpParser/Node/Expr/New_.phpfL;nikic-php-parser/PhpParser/Node/Expr/NullsafeMethodCall.phpzfzhu >nikic-php-parser/PhpParser/Node/Expr/NullsafePropertyFetch.phpf>0nikic-php-parser/PhpParser/Node/Expr/PostDec.phpfa0nikic-php-parser/PhpParser/Node/Expr/PostInc.phpfqz/nikic-php-parser/PhpParser/Node/Expr/PreDec.phpf6/nikic-php-parser/PhpParser/Node/Expr/PreInc.phpf5/nikic-php-parser/PhpParser/Node/Expr/Print_.phpf,6nikic-php-parser/PhpParser/Node/Expr/PropertyFetch.phpfˑ[2nikic-php-parser/PhpParser/Node/Expr/ShellExec.phpfe3nikic-php-parser/PhpParser/Node/Expr/StaticCall.php}f}6m`<nikic-php-parser/PhpParser/Node/Expr/StaticPropertyFetch.php6f6oSX0nikic-php-parser/PhpParser/Node/Expr/Ternary.phpfjˤ/nikic-php-parser/PhpParser/Node/Expr/Throw_.phpf+b?3nikic-php-parser/PhpParser/Node/Expr/UnaryMinus.phpfFz12nikic-php-parser/PhpParser/Node/Expr/UnaryPlus.phpfb1nikic-php-parser/PhpParser/Node/Expr/Variable.phpf2nikic-php-parser/PhpParser/Node/Expr/YieldFrom.phpfM/nikic-php-parser/PhpParser/Node/Expr/Yield_.phpfff#00nikic-php-parser/PhpParser/Node/FunctionLike.phpfkp.nikic-php-parser/PhpParser/Node/Identifier.phpf5k4nikic-php-parser/PhpParser/Node/IntersectionType.phpfwȤ,nikic-php-parser/PhpParser/Node/MatchArm.phpfo(nikic-php-parser/PhpParser/Node/Name.php)f)/7nikic-php-parser/PhpParser/Node/Name/FullyQualified.phpff1nikic-php-parser/PhpParser/Node/Name/Relative.phpfvmۤ0nikic-php-parser/PhpParser/Node/NullableType.phpfߡ)nikic-php-parser/PhpParser/Node/Param.phpkfkL*nikic-php-parser/PhpParser/Node/Scalar.phpofo=2nikic-php-parser/PhpParser/Node/Scalar/DNumber.phpfr1M3nikic-php-parser/PhpParser/Node/Scalar/Encapsed.phpf1G=nikic-php-parser/PhpParser/Node/Scalar/EncapsedStringPart.phpfBC2nikic-php-parser/PhpParser/Node/Scalar/LNumber.php f ;5nikic-php-parser/PhpParser/Node/Scalar/MagicConst.phpkfk +K<nikic-php-parser/PhpParser/Node/Scalar/MagicConst/Class_.php\f\E9nikic-php-parser/PhpParser/Node/Scalar/MagicConst/Dir.phpUfUIؤ:nikic-php-parser/PhpParser/Node/Scalar/MagicConst/File.phpXfX¤?nikic-php-parser/PhpParser/Node/Scalar/MagicConst/Function_.phpefe_ޤ:nikic-php-parser/PhpParser/Node/Scalar/MagicConst/Line.phpXfX~&<nikic-php-parser/PhpParser/Node/Scalar/MagicConst/Method.php^f^j@nikic-php-parser/PhpParser/Node/Scalar/MagicConst/Namespace_.phphfhRF<nikic-php-parser/PhpParser/Node/Scalar/MagicConst/Trait_.php\f\yA2nikic-php-parser/PhpParser/Node/Scalar/String_.php}f}է(nikic-php-parser/PhpParser/Node/Stmt.phpfy/nikic-php-parser/PhpParser/Node/Stmt/Break_.phpf1;.nikic-php-parser/PhpParser/Node/Stmt/Case_.phptft /nikic-php-parser/PhpParser/Node/Stmt/Catch_.phpf3nikic-php-parser/PhpParser/Node/Stmt/ClassConst.php f 9e2nikic-php-parser/PhpParser/Node/Stmt/ClassLike.php f :/4nikic-php-parser/PhpParser/Node/Stmt/ClassMethod.php f /nikic-php-parser/PhpParser/Node/Stmt/Class_.phpfw2/nikic-php-parser/PhpParser/Node/Stmt/Const_.phpf12nikic-php-parser/PhpParser/Node/Stmt/Continue_.phpfۖ(7nikic-php-parser/PhpParser/Node/Stmt/DeclareDeclare.phpfL1nikic-php-parser/PhpParser/Node/Stmt/Declare_.phpf涤,nikic-php-parser/PhpParser/Node/Stmt/Do_.phpJfJ/p.nikic-php-parser/PhpParser/Node/Stmt/Echo_.phpfu&0nikic-php-parser/PhpParser/Node/Stmt/ElseIf_.phpQfQJ.nikic-php-parser/PhpParser/Node/Stmt/Else_.phpf 1nikic-php-parser/PhpParser/Node/Stmt/EnumCase.phpf.nikic-php-parser/PhpParser/Node/Stmt/Enum_.phpEfE8;+3nikic-php-parser/PhpParser/Node/Stmt/Expression.phpf+91nikic-php-parser/PhpParser/Node/Stmt/Finally_.phpf<)r-nikic-php-parser/PhpParser/Node/Stmt/For_.phpFfFxΤ1nikic-php-parser/PhpParser/Node/Stmt/Foreach_.phpwfw`$ܤ2nikic-php-parser/PhpParser/Node/Stmt/Function_.php8 +f8 +T g0nikic-php-parser/PhpParser/Node/Stmt/Global_.phpf̤.nikic-php-parser/PhpParser/Node/Stmt/Goto_.phpf11nikic-php-parser/PhpParser/Node/Stmt/GroupUse.phpf1y.5nikic-php-parser/PhpParser/Node/Stmt/HaltCompiler.phpf^ ,nikic-php-parser/PhpParser/Node/Stmt/If_.phpBfB#Di3nikic-php-parser/PhpParser/Node/Stmt/InlineHTML.phpfi3nikic-php-parser/PhpParser/Node/Stmt/Interface_.phpfaY.nikic-php-parser/PhpParser/Node/Stmt/Label.phpf?>3nikic-php-parser/PhpParser/Node/Stmt/Namespace_.phpf V,nikic-php-parser/PhpParser/Node/Stmt/Nop.phpHfH11nikic-php-parser/PhpParser/Node/Stmt/Property.phpc +fc +9nikic-php-parser/PhpParser/Node/Stmt/PropertyProperty.phpf]0nikic-php-parser/PhpParser/Node/Stmt/Return_.phpfȤ2nikic-php-parser/PhpParser/Node/Stmt/StaticVar.phpfi0nikic-php-parser/PhpParser/Node/Stmt/Static_.phpfȤ0nikic-php-parser/PhpParser/Node/Stmt/Switch_.php=f=YM/nikic-php-parser/PhpParser/Node/Stmt/Throw_.phpf ˤ1nikic-php-parser/PhpParser/Node/Stmt/TraitUse.phpf>J;nikic-php-parser/PhpParser/Node/Stmt/TraitUseAdaptation.php"f")DAnikic-php-parser/PhpParser/Node/Stmt/TraitUseAdaptation/Alias.phpIfISƤFnikic-php-parser/PhpParser/Node/Stmt/TraitUseAdaptation/Precedence.phpbfb=@/nikic-php-parser/PhpParser/Node/Stmt/Trait_.php f vI1nikic-php-parser/PhpParser/Node/Stmt/TryCatch.php-f-/nikic-php-parser/PhpParser/Node/Stmt/Unset_.phpf9/nikic-php-parser/PhpParser/Node/Stmt/UseUse.phppfp=drt-nikic-php-parser/PhpParser/Node/Stmt/Use_.phptftX /nikic-php-parser/PhpParser/Node/Stmt/While_.phpMfM(o-nikic-php-parser/PhpParser/Node/UnionType.phpfvx5nikic-php-parser/PhpParser/Node/VarLikeIdentifier.phpfhd7nikic-php-parser/PhpParser/Node/VariadicPlaceholder.phpfSqC+nikic-php-parser/PhpParser/NodeAbstract.php^f^ B)nikic-php-parser/PhpParser/NodeDumper.php}f}5 )nikic-php-parser/PhpParser/NodeFinder.php f ۺ2-,nikic-php-parser/PhpParser/NodeTraverser.phpa'fa'7Z5nikic-php-parser/PhpParser/NodeTraverserInterface.phpf*nikic-php-parser/PhpParser/NodeVisitor.phpf.Jq9nikic-php-parser/PhpParser/NodeVisitor/CloningVisitor.phpf"ۤ9nikic-php-parser/PhpParser/NodeVisitor/FindingVisitor.phpfF#>nikic-php-parser/PhpParser/NodeVisitor/FirstFindingVisitor.php f bt 7nikic-php-parser/PhpParser/NodeVisitor/NameResolver.php'f' rä@nikic-php-parser/PhpParser/NodeVisitor/NodeConnectingVisitor.phpfut6Bnikic-php-parser/PhpParser/NodeVisitor/ParentConnectingVisitor.phpfv`2nikic-php-parser/PhpParser/NodeVisitorAbstract.phpf8%nikic-php-parser/PhpParser/Parser.phpfv+.nikic-php-parser/PhpParser/Parser/Multiple.phpfv*nikic-php-parser/PhpParser/Parser/Php5.php ,f ,^r*nikic-php-parser/PhpParser/Parser/Php7.php Uf Ui$v,nikic-php-parser/PhpParser/Parser/Tokens.php*f*ˣդ-nikic-php-parser/PhpParser/ParserAbstract.phpfM7Ӥ,nikic-php-parser/PhpParser/ParserFactory.php +f +*uA5nikic-php-parser/PhpParser/PrettyPrinter/Standard.phpWfW䏴4nikic-php-parser/PhpParser/PrettyPrinterAbstract.phpyfyME7object-enumerator/LICENSEfy{object-reflector/LICENSEf9vphar-io-manifest/LICENSE`f`p+phar-io-manifest/ManifestDocumentMapper.phpfn#phar-io-manifest/ManifestLoader.phpfTơ'phar-io-manifest/ManifestSerializer.php$f$^):phar-io-manifest/exceptions/ElementCollectionException.phpfIn)phar-io-manifest/exceptions/Exception.phpfֽН?phar-io-manifest/exceptions/InvalidApplicationNameException.php<f<W5phar-io-manifest/exceptions/InvalidEmailException.phpf)Ϫ}3phar-io-manifest/exceptions/InvalidUrlException.php f x᳤9phar-io-manifest/exceptions/ManifestDocumentException.phpf/"@phar-io-manifest/exceptions/ManifestDocumentLoadingException.phpf-?phar-io-manifest/exceptions/ManifestDocumentMapperException.phpfJR18phar-io-manifest/exceptions/ManifestElementException.phpf̏7phar-io-manifest/exceptions/ManifestLoaderException.phpfl +7phar-io-manifest/exceptions/NoEmailAddressException.phpf'phar-io-manifest/values/Application.php +f +n+phar-io-manifest/values/ApplicationName.phpfä"phar-io-manifest/values/Author.phpf ,phar-io-manifest/values/AuthorCollection.php1f1Q4phar-io-manifest/values/AuthorCollectionIterator.phpfw2S,phar-io-manifest/values/BundledComponent.phpfff6phar-io-manifest/values/BundledComponentCollection.phpfMv>phar-io-manifest/values/BundledComponentCollectionIterator.phpfC0phar-io-manifest/values/CopyrightInformation.phprfrOv!phar-io-manifest/values/Email.phpfe%phar-io-manifest/values/Extension.phpf#phar-io-manifest/values/Library.phpf\ic#phar-io-manifest/values/License.phpf -12$phar-io-manifest/values/Manifest.php0 +f0 +Q3phar-io-manifest/values/PhpExtensionRequirement.phpfk 1phar-io-manifest/values/PhpVersionRequirement.phpBfB3M'phar-io-manifest/values/Requirement.phpf U1phar-io-manifest/values/RequirementCollection.phpwfw<Ť9phar-io-manifest/values/RequirementCollectionIterator.phpfA phar-io-manifest/values/Type.phpffphar-io-manifest/values/Url.phpfz3&phar-io-manifest/xml/AuthorElement.phpfKe0phar-io-manifest/xml/AuthorElementCollection.phpNfNf̐'phar-io-manifest/xml/BundlesElement.phpufuy)phar-io-manifest/xml/ComponentElement.phpfDTt!3phar-io-manifest/xml/ComponentElementCollection.phpWfWDS8(phar-io-manifest/xml/ContainsElement.phpf"Ȥ)phar-io-manifest/xml/CopyrightElement.phpf:Qv*phar-io-manifest/xml/ElementCollection.phpfz%#phar-io-manifest/xml/ExtElement.php+f+sd-phar-io-manifest/xml/ExtElementCollection.phpEfE!)phar-io-manifest/xml/ExtensionElement.phpfb'phar-io-manifest/xml/LicenseElement.phpf̟)phar-io-manifest/xml/ManifestDocument.php f j(phar-io-manifest/xml/ManifestElement.phpfuʼq#phar-io-manifest/xml/PhpElement.php"f"E(phar-io-manifest/xml/RequiresElement.phpFfF~?!phar-io-version/BuildMetaData.phpfjTphar-io-version/LICENSE&f&Ҫ $phar-io-version/PreReleaseSuffix.phpf{,-phar-io-version/Version.phpf% Z+phar-io-version/VersionConstraintParser.phpX fX z'0*phar-io-version/VersionConstraintValue.phpL +fL +u9:!phar-io-version/VersionNumber.phpf3N9phar-io-version/constraints/AbstractVersionConstraint.phpfzB9phar-io-version/constraints/AndVersionConstraintGroup.phpf7 ۤ4phar-io-version/constraints/AnyVersionConstraint.phpVfV-86phar-io-version/constraints/ExactVersionConstraint.phpfEphar-io-version/constraints/GreaterThanOrEqualToVersionConstraint.phpfs+8phar-io-version/constraints/OrVersionConstraintGroup.phpfl,{Fphar-io-version/constraints/SpecificMajorAndMinorVersionConstraint.phpf{sMդ>phar-io-version/constraints/SpecificMajorVersionConstraint.php f ft~1phar-io-version/constraints/VersionConstraint.phpfw<(phar-io-version/exceptions/Exception.phpf<?phar-io-version/exceptions/InvalidPreReleaseSuffixException.phpf[6phar-io-version/exceptions/InvalidVersionException.phpfy7phar-io-version/exceptions/NoBuildMetaDataException.phpf+${:phar-io-version/exceptions/NoPreReleaseSuffixException.phpf"Dphar-io-version/exceptions/UnsupportedVersionConstraintException.phpf樤"php-code-coverage/CodeCoverage.phpnEfnEio'#php-code-coverage/Driver/Driver.phpf֣_7'php-code-coverage/Driver/PcovDriver.phpVfVQI)php-code-coverage/Driver/PhpdbgDriver.phpf +ff +K4%php-code-coverage/Driver/Selector.php f ])*php-code-coverage/Driver/Xdebug2Driver.phpM fM n*php-code-coverage/Driver/Xdebug3Driver.php f -Jphp-code-coverage/Exception/BranchAndPathCoverageNotSupportedException.phpfzFphp-code-coverage/Exception/DeadCodeDetectionNotSupportedException.phpfoICphp-code-coverage/Exception/DirectoryCouldNotBeCreatedException.phpf<6')php-code-coverage/Exception/Exception.phpf+Q8php-code-coverage/Exception/InvalidArgumentException.phpfl~ФFphp-code-coverage/Exception/NoCodeCoverageDriverAvailableException.php3f35oYC]php-code-coverage/Exception/NoCodeCoverageDriverWithPathCoverageSupportAvailableException.phpefeX3/php-code-coverage/Exception/ParserException.phpfpڤDphp-code-coverage/Exception/PathExistsButIsNotDirectoryException.phpfikd9php-code-coverage/Exception/PcovNotAvailableException.phpifiq;php-code-coverage/Exception/PhpdbgNotAvailableException.phphfh |=3php-code-coverage/Exception/ReflectionException.phpf`?php-code-coverage/Exception/ReportAlreadyFinalizedException.php>f>mU=Iphp-code-coverage/Exception/StaticAnalysisCacheNotConfiguredException.phpfp6php-code-coverage/Exception/TestIdMissingException.phpf3OCphp-code-coverage/Exception/UnintentionallyCoveredCodeException.php/f/s=php-code-coverage/Exception/WriteOperationFailedException.phpfu䶤;php-code-coverage/Exception/WrongXdebugVersionException.phpft!:php-code-coverage/Exception/Xdebug2NotEnabledException.phpnfn`h:php-code-coverage/Exception/Xdebug3NotEnabledException.phpfY+C;php-code-coverage/Exception/XdebugNotAvailableException.phpmfm{F,php-code-coverage/Exception/XmlException.phpf0)Rphp-code-coverage/Filter.php f _tphp-code-coverage/LICENSEf-~y֤'php-code-coverage/Node/AbstractNode.phpCfCvm"php-code-coverage/Node/Builder.phpfk!*Ф$php-code-coverage/Node/CrapIndex.phpfܤ$php-code-coverage/Node/Directory.php&f&fphp-code-coverage/Node/File.phpKfKFϕ#php-code-coverage/Node/Iterator.phpfL3>/php-code-coverage/ProcessedCodeCoverageData.php$f$n)php-code-coverage/RawCodeCoverageData.php%!f%!?ޤ#php-code-coverage/Report/Clover.phpk(fk(*Ð&php-code-coverage/Report/Cobertura.php1f1mI#php-code-coverage/Report/Crap4j.phpPfPK9T(php-code-coverage/Report/Html/Facade.php6f696N*php-code-coverage/Report/Html/Renderer.phpq!fq!|4php-code-coverage/Report/Html/Renderer/Dashboard.phpS fS >P94php-code-coverage/Report/Html/Renderer/Directory.php0f0(/php-code-coverage/Report/Html/Renderer/File.phpf+֤Bphp-code-coverage/Report/Html/Renderer/Template/branches.html.distfh2+Fphp-code-coverage/Report/Html/Renderer/Template/coverage_bar.html.dist'f'O}Mphp-code-coverage/Report/Html/Renderer/Template/coverage_bar_branch.html.dist'f'O}Ephp-code-coverage/Report/Html/Renderer/Template/css/bootstrap.min.cssyfyĤ>php-code-coverage/Report/Html/Renderer/Template/css/custom.cssfAphp-code-coverage/Report/Html/Renderer/Template/css/nv.d3.min.cssX%fX%0,@php-code-coverage/Report/Html/Renderer/Template/css/octicons.cssXfX'#=php-code-coverage/Report/Html/Renderer/Template/css/style.css +f +Cphp-code-coverage/Report/Html/Renderer/Template/dashboard.html.distfDJphp-code-coverage/Report/Html/Renderer/Template/dashboard_branch.html.distfDCphp-code-coverage/Report/Html/Renderer/Template/directory.html.distfՆJphp-code-coverage/Report/Html/Renderer/Template/directory_branch.html.distfn2]Hphp-code-coverage/Report/Html/Renderer/Template/directory_item.html.distAfAdsOphp-code-coverage/Report/Html/Renderer/Template/directory_item_branch.html.dist;f;mۤ>php-code-coverage/Report/Html/Renderer/Template/file.html.distP fP j*Ephp-code-coverage/Report/Html/Renderer/Template/file_branch.html.dist f ㉞Cphp-code-coverage/Report/Html/Renderer/Template/file_item.html.distrfr/yJphp-code-coverage/Report/Html/Renderer/Template/file_item_branch.html.distlfl-Cphp-code-coverage/Report/Html/Renderer/Template/icons/file-code.svg0f0QUUHphp-code-coverage/Report/Html/Renderer/Template/icons/file-directory.svgfZCphp-code-coverage/Report/Html/Renderer/Template/js/bootstrap.min.jscfc"#<php-code-coverage/Report/Html/Renderer/Template/js/d3.min.jsPfPhb:php-code-coverage/Report/Html/Renderer/Template/js/file.jsfb䆤@php-code-coverage/Report/Html/Renderer/Template/js/jquery.min.js@^f@^ ?php-code-coverage/Report/Html/Renderer/Template/js/nv.d3.min.jsRfRphp-code-coverage/Report/Html/Renderer/Template/line.html.distf{?php-code-coverage/Report/Html/Renderer/Template/lines.html.distefedf Ephp-code-coverage/Report/Html/Renderer/Template/method_item.html.distfjפLphp-code-coverage/Report/Html/Renderer/Template/method_item_branch.html.distfyĎk?php-code-coverage/Report/Html/Renderer/Template/paths.html.distf*'ݤ php-code-coverage/Report/PHP.php'f'~!php-code-coverage/Report/Text.php'f'a-41php-code-coverage/Report/Xml/BuildInformation.php f )php-code-coverage/Report/Xml/Coverage.php3f3R1F*php-code-coverage/Report/Xml/Directory.phpf Fn'php-code-coverage/Report/Xml/Facade.php4"f4"cb%php-code-coverage/Report/Xml/File.php/f/\'php-code-coverage/Report/Xml/Method.php[f[&J%php-code-coverage/Report/Xml/Node.php7f72s(php-code-coverage/Report/Xml/Project.phpjfj;Z0_'php-code-coverage/Report/Xml/Report.php f H^'php-code-coverage/Report/Xml/Source.phpf g&php-code-coverage/Report/Xml/Tests.phpfi)o'php-code-coverage/Report/Xml/Totals.phpf K%php-code-coverage/Report/Xml/Unit.phpf}=0php-code-coverage/StaticAnalysis/CacheWarmer.phphfh618php-code-coverage/StaticAnalysis/CachingFileAnalyser.phpfʙ;php-code-coverage/StaticAnalysis/CodeUnitFindingVisitor.phpt(ft(Y8Bphp-code-coverage/StaticAnalysis/ExecutableLinesFindingVisitor.php)f)<1php-code-coverage/StaticAnalysis/FileAnalyser.phpfNm?php-code-coverage/StaticAnalysis/IgnoredLinesFindingVisitor.phpA fA C8php-code-coverage/StaticAnalysis/ParsingFileAnalyser.phpf4,%php-code-coverage/Util/Filesystem.phpfv %php-code-coverage/Util/Percentage.phpfUTphp-code-coverage/Version.phpf +Ymphp-file-iterator/Facade.php) +f) +$!Jphp-file-iterator/Factory.phpf5php-file-iterator/Iterator.php^ f^ php-file-iterator/LICENSEfo:php-invoker/Invoker.php f Q$php-invoker/exceptions/Exception.phpvfv'P=Dphp-invoker/exceptions/ProcessControlExtensionNotLoadedException.phpfӤ+php-invoker/exceptions/TimeoutException.phpftJphp-text-template/LICENSEfuphp-text-template/Template.php, f, d*php-text-template/exceptions/Exception.php}f}`"9php-text-template/exceptions/InvalidArgumentException.phpf¤1php-text-template/exceptions/RuntimeException.phpf]Mpphp-timer/Duration.php +f +qݿphp-timer/LICENSEfx$php-timer/ResourceUsageFormatter.phpfqphp-timer/Timer.phpf"?Z"php-timer/exceptions/Exception.phprfr</php-timer/exceptions/NoActiveTimerException.phpf*Ephp-timer/exceptions/TimeSinceStartOfRequestNotAvailableException.phpf.+phpdocumentor-reflection-common/Element.php f (phpdocumentor-reflection-common/File.phpf +)phpdocumentor-reflection-common/Fqsen.phpf;U'phpdocumentor-reflection-common/LICENSE9f9*2Ȑ,phpdocumentor-reflection-common/Location.phpf$}+phpdocumentor-reflection-common/Project.phpfI 2phpdocumentor-reflection-common/ProjectFactory.phpcfciM.phpdocumentor-reflection-docblock/DocBlock.phpfu:phpdocumentor-reflection-docblock/DocBlock/Description.php f Aphpdocumentor-reflection-docblock/DocBlock/DescriptionFactory.phpf2+ =<phpdocumentor-reflection-docblock/DocBlock/ExampleFinder.php4f4`99phpdocumentor-reflection-docblock/DocBlock/Serializer.phpfMXAphpdocumentor-reflection-docblock/DocBlock/StandardTagFactory.phpH1fH1^|%2phpdocumentor-reflection-docblock/DocBlock/Tag.phpf:)9phpdocumentor-reflection-docblock/DocBlock/TagFactory.phpfRפ:phpdocumentor-reflection-docblock/DocBlock/Tags/Author.php f `n;phpdocumentor-reflection-docblock/DocBlock/Tags/BaseTag.phpf; A:phpdocumentor-reflection-docblock/DocBlock/Tags/Covers.php +f +*Fm=>phpdocumentor-reflection-docblock/DocBlock/Tags/Deprecated.php +f +$;phpdocumentor-reflection-docblock/DocBlock/Tags/Example.phpfBפHphpdocumentor-reflection-docblock/DocBlock/Tags/Factory/StaticMethod.php"f"V`i=phpdocumentor-reflection-docblock/DocBlock/Tags/Formatter.php&f&-Lphpdocumentor-reflection-docblock/DocBlock/Tags/Formatter/AlignFormatter.php}f}ZRphpdocumentor-reflection-docblock/DocBlock/Tags/Formatter/PassthroughFormatter.phpf0Z;phpdocumentor-reflection-docblock/DocBlock/Tags/Generic.php f  +>phpdocumentor-reflection-docblock/DocBlock/Tags/InvalidTag.php4f4&R8phpdocumentor-reflection-docblock/DocBlock/Tags/Link.phpflYp_:phpdocumentor-reflection-docblock/DocBlock/Tags/Method.phpf1 59phpdocumentor-reflection-docblock/DocBlock/Tags/Param.php f g/<phpdocumentor-reflection-docblock/DocBlock/Tags/Property.php f y@phpdocumentor-reflection-docblock/DocBlock/Tags/PropertyRead.php f cAphpdocumentor-reflection-docblock/DocBlock/Tags/PropertyWrite.php f ¨>Cphpdocumentor-reflection-docblock/DocBlock/Tags/Reference/Fqsen.php4f4\Gphpdocumentor-reflection-docblock/DocBlock/Tags/Reference/Reference.phpfFGAphpdocumentor-reflection-docblock/DocBlock/Tags/Reference/Url.phpfc8;phpdocumentor-reflection-docblock/DocBlock/Tags/Return_.php+f+:I7phpdocumentor-reflection-docblock/DocBlock/Tags/See.php4 f4 kg9phpdocumentor-reflection-docblock/DocBlock/Tags/Since.phpk +fk +诤:phpdocumentor-reflection-docblock/DocBlock/Tags/Source.php f Zpq?phpdocumentor-reflection-docblock/DocBlock/Tags/TagWithType.phpf4:phpdocumentor-reflection-docblock/DocBlock/Tags/Throws.php,f,oO8phpdocumentor-reflection-docblock/DocBlock/Tags/Uses.php^ +f^ +(Y8phpdocumentor-reflection-docblock/DocBlock/Tags/Var_.php f 셤;phpdocumentor-reflection-docblock/DocBlock/Tags/Version.php +f +&.25phpdocumentor-reflection-docblock/DocBlockFactory.php$f$j>phpdocumentor-reflection-docblock/DocBlockFactoryInterface.phpfX}=phpdocumentor-reflection-docblock/Exception/PcreException.phpf٤)phpdocumentor-reflection-docblock/LICENSE8f8ʤ+phpdocumentor-reflection-docblock/Utils.php f ޤ-phpdocumentor-type-resolver/FqsenResolver.php f C#phpdocumentor-type-resolver/LICENSE8f8ʤ*phpdocumentor-type-resolver/PseudoType.phpyfy!d6phpdocumentor-type-resolver/PseudoTypes/ArrayShape.phpfP7:phpdocumentor-type-resolver/PseudoTypes/ArrayShapeItem.phpfWw:phpdocumentor-type-resolver/PseudoTypes/CallableString.phppfp~ݤ;phpdocumentor-type-resolver/PseudoTypes/ConstExpression.phpf*2phpdocumentor-type-resolver/PseudoTypes/False_.phpf6phpdocumentor-type-resolver/PseudoTypes/FloatValue.phpfpn|=phpdocumentor-type-resolver/PseudoTypes/HtmlEscapedString.phpwfw$8phpdocumentor-type-resolver/PseudoTypes/IntegerRange.php5f5ˤ8phpdocumentor-type-resolver/PseudoTypes/IntegerValue.phpfQ#w1phpdocumentor-type-resolver/PseudoTypes/List_.phpfWnV9phpdocumentor-type-resolver/PseudoTypes/LiteralString.phpnfna;phpdocumentor-type-resolver/PseudoTypes/LowercaseString.phprfrˮn;phpdocumentor-type-resolver/PseudoTypes/NegativeInteger.phpkfkv<^8phpdocumentor-type-resolver/PseudoTypes/NonEmptyList.phpfNܤCphpdocumentor-type-resolver/PseudoTypes/NonEmptyLowercaseString.phpf[ :phpdocumentor-type-resolver/PseudoTypes/NonEmptyString.phpqfqd9phpdocumentor-type-resolver/PseudoTypes/NumericString.phpnfnj {4phpdocumentor-type-resolver/PseudoTypes/Numeric_.phpfIuԤ;phpdocumentor-type-resolver/PseudoTypes/PositiveInteger.phpkfk1ۤ7phpdocumentor-type-resolver/PseudoTypes/StringValue.phpf7phpdocumentor-type-resolver/PseudoTypes/TraitString.phpjfjAפ1phpdocumentor-type-resolver/PseudoTypes/True_.phpf $phpdocumentor-type-resolver/Type.phpf*ޤ,phpdocumentor-type-resolver/TypeResolver.phpUfUh֎2phpdocumentor-type-resolver/Types/AbstractList.php}f}y`4phpdocumentor-type-resolver/Types/AggregatedType.php +f +SqtW.phpdocumentor-type-resolver/Types/ArrayKey.phpf򔭤,phpdocumentor-type-resolver/Types/Array_.phpf=y-phpdocumentor-type-resolver/Types/Boolean.phpvfvȚD7phpdocumentor-type-resolver/Types/CallableParameter.phpf򐛤/phpdocumentor-type-resolver/Types/Callable_.phpf1phpdocumentor-type-resolver/Types/ClassString.phpSfSŤ0phpdocumentor-type-resolver/Types/Collection.phpfn%.phpdocumentor-type-resolver/Types/Compound.phpf݇9-phpdocumentor-type-resolver/Types/Context.php f _ʋ4phpdocumentor-type-resolver/Types/ContextFactory.php7f7֤0phpdocumentor-type-resolver/Types/Expression.php@f@M,phpdocumentor-type-resolver/Types/Float_.phpofo-phpdocumentor-type-resolver/Types/Integer.phprfry5phpdocumentor-type-resolver/Types/InterfaceString.phpf]`2phpdocumentor-type-resolver/Types/Intersection.phpf6פ/phpdocumentor-type-resolver/Types/Iterable_.phpCfCȤ,phpdocumentor-type-resolver/Types/Mixed_.phpfx/,phpdocumentor-type-resolver/Types/Never_.phpfsl+phpdocumentor-type-resolver/Types/Null_.phpfbr.phpdocumentor-type-resolver/Types/Nullable.phpZfZ#:?z-phpdocumentor-type-resolver/Types/Object_.phpfG-phpdocumentor-type-resolver/Types/Parent_.phpfD>/phpdocumentor-type-resolver/Types/Resource_.phpf,phpdocumentor-type-resolver/Types/Scalar.phpf#+phpdocumentor-type-resolver/Types/Self_.phpfS-phpdocumentor-type-resolver/Types/Static_.php f g20-phpdocumentor-type-resolver/Types/String_.php{f{uYQѤ*phpdocumentor-type-resolver/Types/This.phpafat':+phpdocumentor-type-resolver/Types/Void_.phpfKphpspec-prophecy/LICENSE}f} ߦ&phpspec-prophecy/Prophecy/Argument.php]f]eQ8phpspec-prophecy/Prophecy/Argument/ArgumentsWildcard.php f N<:phpspec-prophecy/Prophecy/Argument/Token/AnyValueToken.phpfIZ%%;phpspec-prophecy/Prophecy/Argument/Token/AnyValuesToken.phpf'`Bphpspec-prophecy/Prophecy/Argument/Token/ApproximateValueToken.phpmfm٬c<phpspec-prophecy/Prophecy/Argument/Token/ArrayCountToken.phpQfQ_穤<phpspec-prophecy/Prophecy/Argument/Token/ArrayEntryToken.phpfRAphpspec-prophecy/Prophecy/Argument/Token/ArrayEveryEntryToken.phpf#:phpspec-prophecy/Prophecy/Argument/Token/CallbackToken.phpff"<phpspec-prophecy/Prophecy/Argument/Token/ExactValueToken.php f W@phpspec-prophecy/Prophecy/Argument/Token/IdenticalValueToken.phpf49phpspec-prophecy/Prophecy/Argument/Token/InArrayToken.phpfͪ!<phpspec-prophecy/Prophecy/Argument/Token/LogicalAndToken.phpf<phpspec-prophecy/Prophecy/Argument/Token/LogicalNotToken.phpAfA<phpspec-prophecy/Prophecy/Argument/Token/NotInArrayToken.phpf=phpspec-prophecy/Prophecy/Argument/Token/ObjectStateToken.php +f +LN@phpspec-prophecy/Prophecy/Argument/Token/StringContainsToken.php-f-3xD;phpspec-prophecy/Prophecy/Argument/Token/TokenInterface.phpfG66phpspec-prophecy/Prophecy/Argument/Token/TypeToken.phpf$'phpspec-prophecy/Prophecy/Call/Call.phpf΂-phpspec-prophecy/Prophecy/Call/CallCenter.phpf\%-j:phpspec-prophecy/Prophecy/Comparator/ClosureComparator.phpfx0phpspec-prophecy/Prophecy/Comparator/Factory.phpfZ>8phpspec-prophecy/Prophecy/Comparator/FactoryProvider.phpfQ`;phpspec-prophecy/Prophecy/Comparator/ProphecyComparator.php+f+'ꨤ3phpspec-prophecy/Prophecy/Doubler/CachedDoubler.phpfyg5Dphpspec-prophecy/Prophecy/Doubler/ClassPatch/ClassPatchInterface.phphfhq!ʤHphpspec-prophecy/Prophecy/Doubler/ClassPatch/DisableConstructorPatch.phpf,g=phpspec-prophecy/Prophecy/Doubler/ClassPatch/KeywordPatch.php'f'?phpspec-prophecy/Prophecy/Doubler/ClassPatch/MagicCallPatch.php f Q)7Ephpspec-prophecy/Prophecy/Doubler/ClassPatch/ProphecySubjectPatch.php$ f$ bBۀPphpspec-prophecy/Prophecy/Doubler/ClassPatch/ReflectionClassNewInstancePatch.phpf +,Aphpspec-prophecy/Prophecy/Doubler/ClassPatch/SplFileInfoPatch.php@ f@ YyG?phpspec-prophecy/Prophecy/Doubler/ClassPatch/ThrowablePatch.php" f" aԤAphpspec-prophecy/Prophecy/Doubler/ClassPatch/TraversablePatch.php f wp5phpspec-prophecy/Prophecy/Doubler/DoubleInterface.phpfBۤ-phpspec-prophecy/Prophecy/Doubler/Doubler.phpf}t,Bphpspec-prophecy/Prophecy/Doubler/Generator/ClassCodeGenerator.php f Wy<phpspec-prophecy/Prophecy/Doubler/Generator/ClassCreator.phpbfbH1ؤ;phpspec-prophecy/Prophecy/Doubler/Generator/ClassMirror.php#f#Aphpspec-prophecy/Prophecy/Doubler/Generator/Node/ArgumentNode.phpfZfEphpspec-prophecy/Prophecy/Doubler/Generator/Node/ArgumentTypeNode.phpfˤ>phpspec-prophecy/Prophecy/Doubler/Generator/Node/ClassNode.php}f}KP?phpspec-prophecy/Prophecy/Doubler/Generator/Node/MethodNode.phpfCphpspec-prophecy/Prophecy/Doubler/Generator/Node/ReturnTypeNode.phpfCٮEphpspec-prophecy/Prophecy/Doubler/Generator/Node/TypeNodeAbstract.php f pACphpspec-prophecy/Prophecy/Doubler/Generator/ReflectionInterface.phpf YAphpspec-prophecy/Prophecy/Doubler/Generator/TypeHintReference.php"f"&0phpspec-prophecy/Prophecy/Doubler/LazyDouble.phpfV93phpspec-prophecy/Prophecy/Doubler/NameGenerator.phpf,ôyDphpspec-prophecy/Prophecy/Exception/Call/UnexpectedCallException.phpfOEphpspec-prophecy/Prophecy/Exception/Doubler/ClassCreatorException.phpDfDyXDphpspec-prophecy/Prophecy/Exception/Doubler/ClassMirrorException.phpdfdv48Fphpspec-prophecy/Prophecy/Exception/Doubler/ClassNotFoundException.phpf}:?phpspec-prophecy/Prophecy/Exception/Doubler/DoubleException.phpfV"^@phpspec-prophecy/Prophecy/Exception/Doubler/DoublerException.phpfhJphpspec-prophecy/Prophecy/Exception/Doubler/InterfaceNotFoundException.php!f!R3Lphpspec-prophecy/Prophecy/Exception/Doubler/MethodNotExtendableException.phpf[Gphpspec-prophecy/Prophecy/Exception/Doubler/MethodNotFoundException.phpf#kJphpspec-prophecy/Prophecy/Exception/Doubler/ReturnByReferenceException.phpfL?1phpspec-prophecy/Prophecy/Exception/Exception.phpfx@phpspec-prophecy/Prophecy/Exception/InvalidArgumentException.phpf󱙤Ephpspec-prophecy/Prophecy/Exception/Prediction/AggregateException.php;f;.Lphpspec-prophecy/Prophecy/Exception/Prediction/FailedPredictionException.phpgfg3'}}Cphpspec-prophecy/Prophecy/Exception/Prediction/NoCallsException.phpfZFphpspec-prophecy/Prophecy/Exception/Prediction/PredictionException.phpfR2ͤPphpspec-prophecy/Prophecy/Exception/Prediction/UnexpectedCallsCountException.php f ڶKphpspec-prophecy/Prophecy/Exception/Prediction/UnexpectedCallsException.phpfHphpspec-prophecy/Prophecy/Exception/Prophecy/MethodProphecyException.phpofoHphpspec-prophecy/Prophecy/Exception/Prophecy/ObjectProphecyException.phpfg6ڤBphpspec-prophecy/Prophecy/Exception/Prophecy/ProphecyException.phpfD7jIphpspec-prophecy/Prophecy/PhpDocumentor/ClassAndInterfaceTagRetriever.php`f`Oꖤ=phpspec-prophecy/Prophecy/PhpDocumentor/ClassTagRetriever.phpf:Gphpspec-prophecy/Prophecy/PhpDocumentor/MethodTagRetrieverInterface.phpfQ7phpspec-prophecy/Prophecy/Prediction/CallPrediction.php"f"Ҷڤ<phpspec-prophecy/Prophecy/Prediction/CallTimesPrediction.phpH fH ,;phpspec-prophecy/Prophecy/Prediction/CallbackPrediction.phpf :phpspec-prophecy/Prophecy/Prediction/NoCallsPrediction.php>f>y<phpspec-prophecy/Prophecy/Prediction/PredictionInterface.phpfD5phpspec-prophecy/Prophecy/Promise/CallbackPromise.php#f#26phpspec-prophecy/Prophecy/Promise/PromiseInterface.phpafaĶ= +;phpspec-prophecy/Prophecy/Promise/ReturnArgumentPromise.phpEfEj3phpspec-prophecy/Prophecy/Promise/ReturnPromise.phpufuR2phpspec-prophecy/Prophecy/Promise/ThrowPromise.php +f +^Ġ5phpspec-prophecy/Prophecy/Prophecy/MethodProphecy.php?f?prӤ5phpspec-prophecy/Prophecy/Prophecy/ObjectProphecy.phpfZRp8phpspec-prophecy/Prophecy/Prophecy/ProphecyInterface.phpqfqhRw¤?phpspec-prophecy/Prophecy/Prophecy/ProphecySubjectInterface.phpfġAr/phpspec-prophecy/Prophecy/Prophecy/Revealer.phpf m8phpspec-prophecy/Prophecy/Prophecy/RevealerInterface.phpGfGWnZ%phpspec-prophecy/Prophecy/Prophet.php<f<Vr-phpspec-prophecy/Prophecy/Util/ExportUtil.phpfTp7-phpspec-prophecy/Prophecy/Util/StringUtil.php +f +v|=1phpstan-phpdoc-parser/Ast/AbstractNodeVisitor.phpf5'phpstan-phpdoc-parser/Ast/Attribute.phpEfEhVX>phpstan-phpdoc-parser/Ast/ConstExpr/ConstExprArrayItemNode.phpf~n:phpstan-phpdoc-parser/Ast/ConstExpr/ConstExprArrayNode.php:f:}Ϥ:phpstan-phpdoc-parser/Ast/ConstExpr/ConstExprFalseNode.php1f1P:phpstan-phpdoc-parser/Ast/ConstExpr/ConstExprFloatNode.phpfR:<phpstan-phpdoc-parser/Ast/ConstExpr/ConstExprIntegerNode.phpfIl5phpstan-phpdoc-parser/Ast/ConstExpr/ConstExprNode.phpf79phpstan-phpdoc-parser/Ast/ConstExpr/ConstExprNullNode.php/f/Ko;phpstan-phpdoc-parser/Ast/ConstExpr/ConstExprStringNode.phpf/9phpstan-phpdoc-parser/Ast/ConstExpr/ConstExprTrueNode.php/f/jgͤ6phpstan-phpdoc-parser/Ast/ConstExpr/ConstFetchNode.phpfd= Cphpstan-phpdoc-parser/Ast/ConstExpr/DoctrineConstExprStringNode.phpifiwEphpstan-phpdoc-parser/Ast/ConstExpr/QuoteAwareConstExprStringNode.php f c_Ȥ"phpstan-phpdoc-parser/Ast/Node.phpf,phpstan-phpdoc-parser/Ast/NodeAttributes.phpf[/+phpstan-phpdoc-parser/Ast/NodeTraverser.php)f))phpstan-phpdoc-parser/Ast/NodeVisitor.php +f +vйQ8phpstan-phpdoc-parser/Ast/NodeVisitor/CloningVisitor.phpf"l=phpstan-phpdoc-parser/Ast/PhpDoc/AssertTagMethodValueNode.phpfg?phpstan-phpdoc-parser/Ast/PhpDoc/AssertTagPropertyValueNode.phpfb7phpstan-phpdoc-parser/Ast/PhpDoc/AssertTagValueNode.phpsfs9;phpstan-phpdoc-parser/Ast/PhpDoc/DeprecatedTagValueNode.phpfX숏@phpstan-phpdoc-parser/Ast/PhpDoc/Doctrine/DoctrineAnnotation.phpf@>phpstan-phpdoc-parser/Ast/PhpDoc/Doctrine/DoctrineArgument.phpfU!;phpstan-phpdoc-parser/Ast/PhpDoc/Doctrine/DoctrineArray.phpxfxH/?phpstan-phpdoc-parser/Ast/PhpDoc/Doctrine/DoctrineArrayItem.phpfݤBphpstan-phpdoc-parser/Ast/PhpDoc/Doctrine/DoctrineTagValueNode.phpfzY8phpstan-phpdoc-parser/Ast/PhpDoc/ExtendsTagValueNode.phpf98phpstan-phpdoc-parser/Ast/PhpDoc/GenericTagValueNode.phpf͡٤;phpstan-phpdoc-parser/Ast/PhpDoc/ImplementsTagValueNode.phpfY8phpstan-phpdoc-parser/Ast/PhpDoc/InvalidTagValueNode.phpfrr7phpstan-phpdoc-parser/Ast/PhpDoc/MethodTagValueNode.phpf2@phpstan-phpdoc-parser/Ast/PhpDoc/MethodTagValueParameterNode.phpofo,6phpstan-phpdoc-parser/Ast/PhpDoc/MixinTagValueNode.phpffZ Aphpstan-phpdoc-parser/Ast/PhpDoc/ParamClosureThisTagValueNode.php=f= +^Pphpstan-phpdoc-parser/Ast/PhpDoc/ParamImmediatelyInvokedCallableTagValueNode.phpf Jphpstan-phpdoc-parser/Ast/PhpDoc/ParamLaterInvokedCallableTagValueNode.phpf9phpstan-phpdoc-parser/Ast/PhpDoc/ParamOutTagValueNode.php5f58V6phpstan-phpdoc-parser/Ast/PhpDoc/ParamTagValueNode.phpf/4phpstan-phpdoc-parser/Ast/PhpDoc/PhpDocChildNode.phpfC/phpstan-phpdoc-parser/Ast/PhpDoc/PhpDocNode.php+f+P2phpstan-phpdoc-parser/Ast/PhpDoc/PhpDocTagNode.php +f ++7phpstan-phpdoc-parser/Ast/PhpDoc/PhpDocTagValueNode.phpf 3phpstan-phpdoc-parser/Ast/PhpDoc/PhpDocTextNode.phpf0l̤9phpstan-phpdoc-parser/Ast/PhpDoc/PropertyTagValueNode.php0f0Vm?phpstan-phpdoc-parser/Ast/PhpDoc/RequireExtendsTagValueNode.phpf1Bphpstan-phpdoc-parser/Ast/PhpDoc/RequireImplementsTagValueNode.phpf(7phpstan-phpdoc-parser/Ast/PhpDoc/ReturnTagValueNode.phpft8phpstan-phpdoc-parser/Ast/PhpDoc/SelfOutTagValueNode.phpfTr9phpstan-phpdoc-parser/Ast/PhpDoc/TemplateTagValueNode.phpfjR7phpstan-phpdoc-parser/Ast/PhpDoc/ThrowsTagValueNode.phpfLt@phpstan-phpdoc-parser/Ast/PhpDoc/TypeAliasImportTagValueNode.phpfՒn:phpstan-phpdoc-parser/Ast/PhpDoc/TypeAliasTagValueNode.phpzfzoY +>phpstan-phpdoc-parser/Ast/PhpDoc/TypelessParamTagValueNode.phpf5phpstan-phpdoc-parser/Ast/PhpDoc/UsesTagValueNode.phpfT>D4phpstan-phpdoc-parser/Ast/PhpDoc/VarTagValueNode.phpEfEK5phpstan-phpdoc-parser/Ast/Type/ArrayShapeItemNode.php|f|`;1phpstan-phpdoc-parser/Ast/Type/ArrayShapeNode.phpf7A,0phpstan-phpdoc-parser/Ast/Type/ArrayTypeNode.phpsfs%3phpstan-phpdoc-parser/Ast/Type/CallableTypeNode.phpf]<phpstan-phpdoc-parser/Ast/Type/CallableTypeParameterNode.phpfd Bphpstan-phpdoc-parser/Ast/Type/ConditionalTypeForParameterNode.phpfhפ6phpstan-phpdoc-parser/Ast/Type/ConditionalTypeNode.phpf&(i0phpstan-phpdoc-parser/Ast/Type/ConstTypeNode.phpf@wD<2phpstan-phpdoc-parser/Ast/Type/GenericTypeNode.php,f,kݤ5phpstan-phpdoc-parser/Ast/Type/IdentifierTypeNode.phpf֞hn7phpstan-phpdoc-parser/Ast/Type/IntersectionTypeNode.phpfx"r¤2phpstan-phpdoc-parser/Ast/Type/InvalidTypeNode.phpXfX3phpstan-phpdoc-parser/Ast/Type/NullableTypeNode.phpfe6phpstan-phpdoc-parser/Ast/Type/ObjectShapeItemNode.phpf·Ҥ2phpstan-phpdoc-parser/Ast/Type/ObjectShapeNode.phpFfF_7phpstan-phpdoc-parser/Ast/Type/OffsetAccessTypeNode.phpfY]/phpstan-phpdoc-parser/Ast/Type/ThisTypeNode.php!f! +b+phpstan-phpdoc-parser/Ast/Type/TypeNode.phpfǖ׹0phpstan-phpdoc-parser/Ast/Type/UnionTypeNode.phpfEphpstan-phpdoc-parser/LICENSE.f.-%phpstan-phpdoc-parser/Lexer/Lexer.phpfB]0phpstan-phpdoc-parser/Parser/ConstExprParser.phpx&fx&0phpstan-phpdoc-parser/Parser/ParserException.php f nd-phpstan-phpdoc-parser/Parser/PhpDocParser.php|f|CF0phpstan-phpdoc-parser/Parser/StringUnescaper.php f e%.phpstan-phpdoc-parser/Parser/TokenIterator.php#f#L + ++phpstan-phpdoc-parser/Parser/TypeParser.phpfD#*phpstan-phpdoc-parser/Printer/DiffElem.phpf=!s(phpstan-phpdoc-parser/Printer/Differ.phpgfgm:?)phpstan-phpdoc-parser/Printer/Printer.phpfu phpunit.xsdRFfRFAgphpunit/Exception.phpfa#phpunit/Framework/Assert.phpcfc/q&phpunit/Framework/Assert/Functions.php)f) 4]0phpunit/Framework/Constraint/Boolean/IsFalse.phpf/phpunit/Framework/Constraint/Boolean/IsTrue.phpf})phpunit/Framework/Constraint/Callback.php?f? +b2phpunit/Framework/Constraint/Cardinality/Count.phpj fj xR@ؤ8phpunit/Framework/Constraint/Cardinality/GreaterThan.php f 4phpunit/Framework/Constraint/Cardinality/IsEmpty.phpfhf5phpunit/Framework/Constraint/Cardinality/LessThan.phpf05phpunit/Framework/Constraint/Cardinality/SameSize.php_f_uŤ+phpunit/Framework/Constraint/Constraint.php"f"bǤ1phpunit/Framework/Constraint/Equality/IsEqual.php f [W?phpunit/Framework/Constraint/Equality/IsEqualCanonicalizing.php +f +'p=phpunit/Framework/Constraint/Equality/IsEqualIgnoringCase.php +f +_:phpunit/Framework/Constraint/Equality/IsEqualWithDelta.php? +f? +v;4phpunit/Framework/Constraint/Exception/Exception.phpfRu{8phpunit/Framework/Constraint/Exception/ExceptionCode.phpf ;phpunit/Framework/Constraint/Exception/ExceptionMessage.phpfw;Lphpunit/Framework/Constraint/Exception/ExceptionMessageRegularExpression.phpfLj[i;phpunit/Framework/Constraint/Filesystem/DirectoryExists.phpjfji+6phpunit/Framework/Constraint/Filesystem/FileExists.phpefeK6phpunit/Framework/Constraint/Filesystem/IsReadable.phpefe16phpunit/Framework/Constraint/Filesystem/IsWritable.phpefe+phpunit/Framework/Constraint/IsAnything.phpfE,phpunit/Framework/Constraint/IsIdentical.php f f,phpunit/Framework/Constraint/JsonMatches.phpa fa *}@phpunit/Framework/Constraint/JsonMatchesErrorMessageProvider.php5f5mһ.phpunit/Framework/Constraint/Math/IsFinite.phpfZҗ0phpunit/Framework/Constraint/Math/IsInfinite.phpf'*~+phpunit/Framework/Constraint/Math/IsNan.phpf4g09phpunit/Framework/Constraint/Object/ClassHasAttribute.phpf*?phpunit/Framework/Constraint/Object/ClassHasStaticAttribute.php*f*!%4phpunit/Framework/Constraint/Object/ObjectEquals.php +f +0W:phpunit/Framework/Constraint/Object/ObjectHasAttribute.phpf$9phpunit/Framework/Constraint/Object/ObjectHasProperty.phpf|8phpunit/Framework/Constraint/Operator/BinaryOperator.phpGfGS\4phpunit/Framework/Constraint/Operator/LogicalAnd.phpfbJ4phpunit/Framework/Constraint/Operator/LogicalNot.phpD fD e=3phpunit/Framework/Constraint/Operator/LogicalOr.phpfZ4phpunit/Framework/Constraint/Operator/LogicalXor.php$f$O2phpunit/Framework/Constraint/Operator/Operator.php&f& Dܤ7phpunit/Framework/Constraint/Operator/UnaryOperator.php3f3 m.phpunit/Framework/Constraint/String/IsJson.phpfj9phpunit/Framework/Constraint/String/RegularExpression.phpf+J6phpunit/Framework/Constraint/String/StringContains.phpfij"6phpunit/Framework/Constraint/String/StringEndsWith.phpf{Fphpunit/Framework/Constraint/String/StringMatchesFormatDescription.php +f + ՗8phpunit/Framework/Constraint/String/StringStartsWith.php&f&c>8phpunit/Framework/Constraint/Traversable/ArrayHasKey.phpfwe @phpunit/Framework/Constraint/Traversable/TraversableContains.php"f"TbKEphpunit/Framework/Constraint/Traversable/TraversableContainsEqual.phpafawAIphpunit/Framework/Constraint/Traversable/TraversableContainsIdentical.php'f'sӤDphpunit/Framework/Constraint/Traversable/TraversableContainsOnly.phpP fP /2phpunit/Framework/Constraint/Type/IsInstanceOf.phpcfc,phpunit/Framework/Constraint/Type/IsNull.phpf?),phpunit/Framework/Constraint/Type/IsType.phpfh+phpunit/Framework/DataProviderTestSuite.php;f;,;&phpunit/Framework/Error/Deprecated.phpzfzV!phpunit/Framework/Error/Error.phpmfmYg"phpunit/Framework/Error/Notice.phpvfvˤ#phpunit/Framework/Error/Warning.phpwfwG#phpunit/Framework/ErrorTestCase.phpfcȶAphpunit/Framework/Exception/ActualValueIsNotAnObjectException.phpf`B4phpunit/Framework/Exception/AssertionFailedError.phpf5phpunit/Framework/Exception/CodeCoverageException.phpf[Sphpunit/Framework/Exception/ComparisonMethodDoesNotAcceptParameterTypeException.phpkfkphpunit/Framework/MockObject/Exception/ReflectionException.phpf.ؔLphpunit/Framework/MockObject/Exception/ReturnValueNotConfiguredException.php6f6?먙;phpunit/Framework/MockObject/Exception/RuntimeException.phpf_|Mphpunit/Framework/MockObject/Exception/SoapExtensionNotAvailableException.phpfz@phpunit/Framework/MockObject/Exception/UnknownClassException.phpf5uW@phpunit/Framework/MockObject/Exception/UnknownTraitException.phpfq¥?phpunit/Framework/MockObject/Exception/UnknownTypeException.phpf~*phpunit/Framework/MockObject/Generator.php>f>l|56phpunit/Framework/MockObject/Generator/deprecation.tpl;f;O5s7phpunit/Framework/MockObject/Generator/intersection.tplLfL-X7phpunit/Framework/MockObject/Generator/mocked_class.tplfwZ8phpunit/Framework/MockObject/Generator/mocked_method.tplFfFKFphpunit/Framework/MockObject/Generator/mocked_method_never_or_void.tplfp?phpunit/Framework/MockObject/Generator/mocked_static_method.tplf 4R9phpunit/Framework/MockObject/Generator/proxied_method.tpl}f}@ėGphpunit/Framework/MockObject/Generator/proxied_method_never_or_void.tplvfvT6phpunit/Framework/MockObject/Generator/trait_class.tplQfQ<Ȥ5phpunit/Framework/MockObject/Generator/wsdl_class.tplf6phpunit/Framework/MockObject/Generator/wsdl_method.tpl<f<i+phpunit/Framework/MockObject/Invocation.phpfn2phpunit/Framework/MockObject/InvocationHandler.php:f:ˤ(phpunit/Framework/MockObject/Matcher.phpfƶ5phpunit/Framework/MockObject/MethodNameConstraint.php +f +A1|,phpunit/Framework/MockObject/MockBuilder.phpq+fq+JQ *phpunit/Framework/MockObject/MockClass.phpf'C+phpunit/Framework/MockObject/MockMethod.php&f&t.phpunit/Framework/MockObject/MockMethodSet.php8f8G\+phpunit/Framework/MockObject/MockObject.phpfbt*phpunit/Framework/MockObject/MockTrait.phpf&nä)phpunit/Framework/MockObject/MockType.phpfFFt5phpunit/Framework/MockObject/Rule/AnyInvokedCount.phpjfj`Ť3phpunit/Framework/MockObject/Rule/AnyParameters.phpf~';phpunit/Framework/MockObject/Rule/ConsecutiveParameters.phpZ fZ 035phpunit/Framework/MockObject/Rule/InvocationOrder.phpfLDӤ4phpunit/Framework/MockObject/Rule/InvokedAtIndex.php,f,kK9phpunit/Framework/MockObject/Rule/InvokedAtLeastCount.phpfB8phpunit/Framework/MockObject/Rule/InvokedAtLeastOnce.php-f- (8phpunit/Framework/MockObject/Rule/InvokedAtMostCount.phpfgY2phpunit/Framework/MockObject/Rule/InvokedCount.php f ^ 0phpunit/Framework/MockObject/Rule/MethodName.phpf[=0phpunit/Framework/MockObject/Rule/Parameters.phpVfVTu 4phpunit/Framework/MockObject/Rule/ParametersRule.phpcfc?(%phpunit/Framework/MockObject/Stub.phpfŎ6phpunit/Framework/MockObject/Stub/ConsecutiveCalls.phpfZ}hۤ/phpunit/Framework/MockObject/Stub/Exception.php,f,6¤4phpunit/Framework/MockObject/Stub/ReturnArgument.phpf?}64phpunit/Framework/MockObject/Stub/ReturnCallback.phpfD0Ӥ5phpunit/Framework/MockObject/Stub/ReturnReference.phpf +I0phpunit/Framework/MockObject/Stub/ReturnSelf.php4f4DD0phpunit/Framework/MockObject/Stub/ReturnStub.phpfA^4phpunit/Framework/MockObject/Stub/ReturnValueMap.phpfۤ*phpunit/Framework/MockObject/Stub/Stub.php3f3>++phpunit/Framework/MockObject/Verifiable.phpf̐ s!phpunit/Framework/Reorderable.phpfz0$phpunit/Framework/SelfDescribing.php +f +s!phpunit/Framework/SkippedTest.phpfS.%phpunit/Framework/SkippedTestCase.phpfj7phpunit/Framework/Test.phpf!phpunit/Framework/TestBuilder.php"f"14jphpunit/Framework/TestCase.php].f].`!phpunit/Framework/TestFailure.phpf'q"phpunit/Framework/TestListener.phprfrӪc^7phpunit/Framework/TestListenerDefaultImplementation.php'f'! phpunit/Framework/TestResult.php+f+5Ephpunit/Framework/TestSuite.phpsdfsdw%'phpunit/Framework/TestSuiteIterator.php6f6$ u%phpunit/Framework/WarningTestCase.php'f'n@ !phpunit/Runner/BaseTestRunner.php f +H)phpunit/Runner/DefaultTestResultCache.php!f!/i^phpunit/Runner/Exception.phpfzZ-phpunit/Runner/Extension/ExtensionHandler.php f Az'phpunit/Runner/Extension/PharLoader.php f =w4phpunit/Runner/Filter/ExcludeGroupFilterIterator.phpsfs} +Z!phpunit/Runner/Filter/Factory.phpfdcΤ-phpunit/Runner/Filter/GroupFilterIterator.phpf=;4phpunit/Runner/Filter/IncludeGroupFilterIterator.phprfrP;AD,phpunit/Runner/Filter/NameFilterIterator.php f T/phpunit/Runner/Hook/AfterIncompleteTestHook.php-f-zԤ)phpunit/Runner/Hook/AfterLastTestHook.phpf0B֤*phpunit/Runner/Hook/AfterRiskyTestHook.php#f#dm,phpunit/Runner/Hook/AfterSkippedTestHook.php'f':/phpunit/Runner/Hook/AfterSuccessfulTestHook.phpf5w*phpunit/Runner/Hook/AfterTestErrorHook.php#f#ݮ,phpunit/Runner/Hook/AfterTestFailureHook.php'f'2F%phpunit/Runner/Hook/AfterTestHook.phpf;gA,phpunit/Runner/Hook/AfterTestWarningHook.php'f'':+phpunit/Runner/Hook/BeforeFirstTestHook.phpfhWt&phpunit/Runner/Hook/BeforeTestHook.phpf"bphpunit/Runner/Hook/Hook.phpf. phpunit/Runner/Hook/TestHook.phpfZ_ ++phpunit/Runner/Hook/TestListenerAdapter.phpf\6E&phpunit/Runner/NullTestResultCache.phpfW<phpunit/Runner/PhptTestCase.phpVfVS 'phpunit/Runner/ResultCacheExtension.php<f<6 _*phpunit/Runner/StandardTestSuiteLoader.phpf;i Ȥ"phpunit/Runner/TestResultCache.phpfK"phpunit/Runner/TestSuiteLoader.phpfޤ"phpunit/Runner/TestSuiteSorter.php+f+~tphpunit/Runner/Version.phpf>'phpunit/TextUI/CliArguments/Builder.phpTfT3-phpunit/TextUI/CliArguments/Configuration.phpfX)phpunit/TextUI/CliArguments/Exception.phpf%zE&phpunit/TextUI/CliArguments/Mapper.php+,f+,'aphpunit/TextUI/Command.phprfr$QS'phpunit/TextUI/DefaultResultPrinter.phpe7fe78&phpunit/TextUI/Exception/Exception.phpfD{i0phpunit/TextUI/Exception/ReflectionException.phpf Y-phpunit/TextUI/Exception/RuntimeException.phpfF;phpunit/TextUI/Exception/TestDirectoryNotFoundException.phpf6phpunit/TextUI/Exception/TestFileNotFoundException.phpfpCphpunit/TextUI/Help.php1f1ؚ3Ť phpunit/TextUI/ResultPrinter.phppfpܤphpunit/TextUI/TestRunner.phpf]["phpunit/TextUI/TestSuiteMapper.php f I7iH=phpunit/TextUI/XmlConfiguration/CodeCoverage/CodeCoverage.phpfrAphpunit/TextUI/XmlConfiguration/CodeCoverage/Filter/Directory.phpfc{Kphpunit/TextUI/XmlConfiguration/CodeCoverage/Filter/DirectoryCollection.phpfju}Sphpunit/TextUI/XmlConfiguration/CodeCoverage/Filter/DirectoryCollectionIterator.phpfJ=phpunit/TextUI/XmlConfiguration/CodeCoverage/FilterMapper.phpfB>phpunit/TextUI/XmlConfiguration/CodeCoverage/Report/Clover.phpf=CAphpunit/TextUI/XmlConfiguration/CodeCoverage/Report/Cobertura.phpfi>phpunit/TextUI/XmlConfiguration/CodeCoverage/Report/Crap4j.phpfG<phpunit/TextUI/XmlConfiguration/CodeCoverage/Report/Html.phpfE6;phpunit/TextUI/XmlConfiguration/CodeCoverage/Report/Php.phpfpS<phpunit/TextUI/XmlConfiguration/CodeCoverage/Report/Text.phpfKkw;phpunit/TextUI/XmlConfiguration/CodeCoverage/Report/Xml.phpf?u1phpunit/TextUI/XmlConfiguration/Configuration.php5f5˞-phpunit/TextUI/XmlConfiguration/Exception.phpfN5+8phpunit/TextUI/XmlConfiguration/Filesystem/Directory.phpf@Bphpunit/TextUI/XmlConfiguration/Filesystem/DirectoryCollection.phpf1EqJphpunit/TextUI/XmlConfiguration/Filesystem/DirectoryCollectionIterator.phpf&3phpunit/TextUI/XmlConfiguration/Filesystem/File.phpf.P =phpunit/TextUI/XmlConfiguration/Filesystem/FileCollection.php~f~]rEphpunit/TextUI/XmlConfiguration/Filesystem/FileCollectionIterator.phpfffĤ-phpunit/TextUI/XmlConfiguration/Generator.phpfF /phpunit/TextUI/XmlConfiguration/Group/Group.phpf9phpunit/TextUI/XmlConfiguration/Group/GroupCollection.phpfyAphpunit/TextUI/XmlConfiguration/Group/GroupCollectionIterator.phpqfqY50phpunit/TextUI/XmlConfiguration/Group/Groups.phpf@I*phpunit/TextUI/XmlConfiguration/Loader.phpfE-1phpunit/TextUI/XmlConfiguration/Logging/Junit.phpfciG3phpunit/TextUI/XmlConfiguration/Logging/Logging.php f ]٤4phpunit/TextUI/XmlConfiguration/Logging/TeamCity.phpf7Z鵤8phpunit/TextUI/XmlConfiguration/Logging/TestDox/Html.phpfV2ܤ8phpunit/TextUI/XmlConfiguration/Logging/TestDox/Text.phpfώ7phpunit/TextUI/XmlConfiguration/Logging/TestDox/Xml.phpft0phpunit/TextUI/XmlConfiguration/Logging/Text.phpfCn>phpunit/TextUI/XmlConfiguration/Migration/MigrationBuilder.phpf"dGphpunit/TextUI/XmlConfiguration/Migration/MigrationBuilderException.phpfbs@phpunit/TextUI/XmlConfiguration/Migration/MigrationException.php +f +pHphpunit/TextUI/XmlConfiguration/Migration/Migrations/ConvertLogTypes.phpfhoeOphpunit/TextUI/XmlConfiguration/Migration/Migrations/CoverageCloverToReport.phpXfXijOphpunit/TextUI/XmlConfiguration/Migration/Migrations/CoverageCrap4jToReport.phpf$i'Mphpunit/TextUI/XmlConfiguration/Migration/Migrations/CoverageHtmlToReport.phpfՄjLphpunit/TextUI/XmlConfiguration/Migration/Migrations/CoveragePhpToReport.phpFfF^ӤMphpunit/TextUI/XmlConfiguration/Migration/Migrations/CoverageTextToReport.phpfV_Lphpunit/TextUI/XmlConfiguration/Migration/Migrations/CoverageXmlToReport.phpKfK_ Qphpunit/TextUI/XmlConfiguration/Migration/Migrations/IntroduceCoverageElement.phpfUMphpunit/TextUI/XmlConfiguration/Migration/Migrations/LogToReportMigration.phpfUBphpunit/TextUI/XmlConfiguration/Migration/Migrations/Migration.phpf'dphpunit/TextUI/XmlConfiguration/Migration/Migrations/MoveAttributesFromFilterWhitelistToCoverage.phpfU%5Yphpunit/TextUI/XmlConfiguration/Migration/Migrations/MoveAttributesFromRootToCoverage.phpCfCcFXphpunit/TextUI/XmlConfiguration/Migration/Migrations/MoveWhitelistExcludesToCoverage.phpfXphpunit/TextUI/XmlConfiguration/Migration/Migrations/MoveWhitelistIncludesToCoverage.phpftSphpunit/TextUI/XmlConfiguration/Migration/Migrations/RemoveCacheTokensAttribute.phpfwJphpunit/TextUI/XmlConfiguration/Migration/Migrations/RemoveEmptyFilter.php{f{KGphpunit/TextUI/XmlConfiguration/Migration/Migrations/RemoveLogTypes.phpofo3Qphpunit/TextUI/XmlConfiguration/Migration/Migrations/UpdateSchemaLocationTo93.phpfbJ6phpunit/TextUI/XmlConfiguration/Migration/Migrator.phpfo$V0phpunit/TextUI/XmlConfiguration/PHP/Constant.php7f7$Ҥ:phpunit/TextUI/XmlConfiguration/PHP/ConstantCollection.phplfl%(Bphpunit/TextUI/XmlConfiguration/PHP/ConstantCollectionIterator.phpf}=Ƥ2phpunit/TextUI/XmlConfiguration/PHP/IniSetting.phpJfJOt<phpunit/TextUI/XmlConfiguration/PHP/IniSettingCollection.phpfޛ;Dphpunit/TextUI/XmlConfiguration/PHP/IniSettingCollectionIterator.phpf/mo+phpunit/TextUI/XmlConfiguration/PHP/Php.phpf6僤2phpunit/TextUI/XmlConfiguration/PHP/PhpHandler.phpwfw` +0phpunit/TextUI/XmlConfiguration/PHP/Variable.phpfN:phpunit/TextUI/XmlConfiguration/PHP/VariableCollection.phplflsB@٤Bphpunit/TextUI/XmlConfiguration/PHP/VariableCollectionIterator.phpf!~gȤ5phpunit/TextUI/XmlConfiguration/PHPUnit/Extension.phpf}Q?phpunit/TextUI/XmlConfiguration/PHPUnit/ExtensionCollection.phpfo;RGphpunit/TextUI/XmlConfiguration/PHPUnit/ExtensionCollectionIterator.phpf|D?3phpunit/TextUI/XmlConfiguration/PHPUnit/PHPUnit.phplCflCv;phpunit/TextUI/XmlConfiguration/TestSuite/TestDirectory.phpCfC0Ephpunit/TextUI/XmlConfiguration/TestSuite/TestDirectoryCollection.phpfLCMphpunit/TextUI/XmlConfiguration/TestSuite/TestDirectoryCollectionIterator.phpfn6phpunit/TextUI/XmlConfiguration/TestSuite/TestFile.phpf?y@phpunit/TextUI/XmlConfiguration/TestSuite/TestFileCollection.phpfXHphpunit/TextUI/XmlConfiguration/TestSuite/TestFileCollectionIterator.phpzfzX17phpunit/TextUI/XmlConfiguration/TestSuite/TestSuite.phpf8wAphpunit/TextUI/XmlConfiguration/TestSuite/TestSuiteCollection.phpf/jIphpunit/TextUI/XmlConfiguration/TestSuite/TestSuiteCollectionIterator.phpf+6$phpunit/Util/Annotation/DocBlock.phpAfAp$phpunit/Util/Annotation/Registry.phpN +fN +?caphpunit/Util/Blacklist.phpfsphpunit/Util/Cloner.phpf"Ɩܤphpunit/Util/Color.phpfj?phpunit/Util/ErrorHandler.phpf=phpunit/Util/Exception.phpf다phpunit/Util/ExcludeList.phpfphpunit/Util/FileLoader.php f 'phpunit/Util/Filesystem.phpfܐphpunit/Util/Filter.php f l* phpunit/Util/GlobalState.phpfl(phpunit/Util/InvalidDataSetException.phpf,KOphpunit/Util/Json.php2 f2 Zˤphpunit/Util/Log/JUnit.phpb*fb*}3phpunit/Util/Log/TeamCity.php}&f}&t+'phpunit/Util/PHP/AbstractPhpProcess.php'f'P*&phpunit/Util/PHP/DefaultPhpProcess.phpzfzCp*phpunit/Util/PHP/Template/PhptTestCase.tplf׈j+phpunit/Util/PHP/Template/TestCaseClass.tpl f v,phpunit/Util/PHP/Template/TestCaseMethod.tpl2f2?&phpunit/Util/PHP/WindowsPhpProcess.phpfA}phpunit/Util/Printer.php f shphpunit/Util/Reflection.phpfW챤"phpunit/Util/RegularExpression.phpf0uR)phpunit/Util/Test.php^f^a*phpunit/Util/TestDox/CliTestDoxPrinter.php^*f^*W`*phpunit/Util/TestDox/HtmlResultPrinter.php f 3~ʤ'phpunit/Util/TestDox/NamePrettifier.php8"f8" +&phpunit/Util/TestDox/ResultPrinter.phpYfYXN 'phpunit/Util/TestDox/TestDoxPrinter.phpH)fH)9C*phpunit/Util/TestDox/TextResultPrinter.phpfȹ!.)phpunit/Util/TestDox/XmlResultPrinter.phpf/%phpunit/Util/TextTestListRenderer.php_f_rܯphpunit/Util/Type.phpf|ä*phpunit/Util/VersionComparisonOperator.phpfb,phpunit/Util/XdebugFilterScriptGenerator.phpwfwتphpunit/Util/Xml.phpf̤phpunit/Util/Xml/Exception.phpfӤ0phpunit/Util/Xml/FailedSchemaDetectionResult.phpf#Sphpunit/Util/Xml/Loader.php f ,?*phpunit/Util/Xml/SchemaDetectionResult.phpfV#phpunit/Util/Xml/SchemaDetector.phpwfwF^!phpunit/Util/Xml/SchemaFinder.phpf0WT%phpunit/Util/Xml/SnapshotNodeList.phpHfH ^d4phpunit/Util/Xml/SuccessfulSchemaDetectionResult.phpfffˤ%phpunit/Util/Xml/ValidationResult.phpfxv:phpunit/Util/Xml/Validator.phpfV$phpunit/Util/XmlTestListRenderer.php2 +f2 +qgפsbom.xml*2f*2UTschema/8.5.xsdBfB2A[schema/9.0.xsd4Bf4B7wschema/9.1.xsdBfBq'8schema/9.2.xsdBfBc-schema/9.3.xsdEfEqschema/9.4.xsd +Ff +FDOFIschema/9.5.xsdDFfDFs|sebastian-cli-parser/LICENSEfusebastian-cli-parser/Parser.phpf&<sebastian-cli-parser/exceptions/AmbiguousOptionException.phpJfJkK*-sebastian-cli-parser/exceptions/Exception.phpyfy>Gsebastian-cli-parser/exceptions/OptionDoesNotAllowArgumentException.phpcfcRjYJsebastian-cli-parser/exceptions/RequiredOptionArgumentMissingException.phplflzQ:sebastian-cli-parser/exceptions/UnknownOptionException.phpCfC*tP*sebastian-code-unit-reverse-lookup/LICENSEf3G (-sebastian-code-unit-reverse-lookup/Wizard.php f AJ'sebastian-code-unit/ClassMethodUnit.phpf!sebastian-code-unit/ClassUnit.php f ʰ sebastian-code-unit/CodeUnit.php%f%a*sebastian-code-unit/CodeUnitCollection.phpfKm2sebastian-code-unit/CodeUnitCollectionIterator.php?f?G9$sebastian-code-unit/FunctionUnit.phpf1+sebastian-code-unit/InterfaceMethodUnit.php#f#.R%sebastian-code-unit/InterfaceUnit.phpf6 ]ˤsebastian-code-unit/LICENSE f psebastian-code-unit/Mapper.php-f-'sebastian-code-unit/TraitMethodUnit.phpfG!sebastian-code-unit/TraitUnit.php f xV,sebastian-code-unit/exceptions/Exception.phpwfw5Ǥ;sebastian-code-unit/exceptions/InvalidCodeUnitException.phpfMvԊ3sebastian-code-unit/exceptions/NoTraitException.phpf]56sebastian-code-unit/exceptions/ReflectionException.phpfcQ(sebastian-comparator/ArrayComparator.phpyfy}פ#sebastian-comparator/Comparator.phpfo`*sebastian-comparator/ComparisonFailure.php f yP٤*sebastian-comparator/DOMNodeComparator.php$ f$ B3+sebastian-comparator/DateTimeComparator.php f -)sebastian-comparator/DoubleComparator.phpf&,sebastian-comparator/ExceptionComparator.phpf.L0 sebastian-comparator/Factory.phpf +sebastian-comparator/LICENSE f =(-sebastian-comparator/MockObjectComparator.phpf]*sebastian-comparator/NumericComparator.php7 f7 Af)sebastian-comparator/ObjectComparator.php\ f\ Fɫ+sebastian-comparator/ResourceComparator.php f WꞤ)sebastian-comparator/ScalarComparator.php3 f3 3sebastian-comparator/SplObjectStorageComparator.phpfF'sebastian-comparator/TypeComparator.phpf%Y\-sebastian-comparator/exceptions/Exception.phpzfzϤ4sebastian-comparator/exceptions/RuntimeException.phpf_#sebastian-complexity/Calculator.phpfjf٤.sebastian-complexity/Complexity/Complexity.phpUfU,#8sebastian-complexity/Complexity/ComplexityCollection.phpfU~@sebastian-complexity/Complexity/ComplexityCollectionIterator.php0f0&,sebastian-complexity/Exception/Exception.phpzfzȬˤ3sebastian-complexity/Exception/RuntimeException.phpfsebastian-complexity/LICENSEf=ݤ=sebastian-complexity/Visitor/ComplexityCalculatingVisitor.php f $8xȤGsebastian-complexity/Visitor/CyclomaticComplexityCalculatingVisitor.phpGfG)sebastian-diff/Chunk.phpcfc!rsebastian-diff/Diff.phpnfn_9sebastian-diff/Differ.php$f$3sebastian-diff/Exception/ConfigurationException.phpBfBw&sebastian-diff/Exception/Exception.phpnfn/\5sebastian-diff/Exception/InvalidArgumentException.phpf$ysebastian-diff/LICENSE f a1sebastian-diff/Line.phpPfPQF5sebastian-diff/LongestCommonSubsequenceCalculator.phpf`Dsebastian-diff/MemoryEfficientLongestCommonSubsequenceCalculator.php f N(R4sebastian-diff/Output/AbstractChunkOutputBuilder.phpfFǃ/sebastian-diff/Output/DiffOnlyOutputBuilder.phpf4sebastian-diff/Output/DiffOutputBuilderInterface.phpf=8sebastian-diff/Output/StrictUnifiedDiffOutputBuilder.php(f(O2sebastian-diff/Output/UnifiedDiffOutputBuilder.phpFfF+bsebastian-diff/Parser.php f wcBsebastian-diff/TimeEfficientLongestCommonSubsequenceCalculator.php4 f4 !sebastian-environment/Console.phpfcsebastian-environment/LICENSEfFy٤)sebastian-environment/OperatingSystem.phpfuv!sebastian-environment/Runtime.phpfŬsebastian-exporter/Exporter.php$f$csebastian-exporter/LICENSEf 5٤'sebastian-global-state/CodeExporter.php f  &sebastian-global-state/ExcludeList.php +f +Isebastian-global-state/LICENSEfJ#sebastian-global-state/Restorer.phpf+B7#sebastian-global-state/Snapshot.php*f*s{/sebastian-global-state/exceptions/Exception.php}f}ⴤ6sebastian-global-state/exceptions/RuntimeException.phpf##sebastian-lines-of-code/Counter.phpLfL9 /sebastian-lines-of-code/Exception/Exception.php~f~%>>sebastian-lines-of-code/Exception/IllogicalValuesException.phpfr<sebastian-lines-of-code/Exception/NegativeValueException.phpf&Ӥ6sebastian-lines-of-code/Exception/RuntimeException.phpf)Ϲsebastian-lines-of-code/LICENSEfbS~/sebastian-lines-of-code/LineCountingVisitor.phpfE'sebastian-lines-of-code/LinesOfCode.php f c*sebastian-object-enumerator/Enumerator.phpf9Ϥ)sebastian-object-enumerator/Exception.phpfNo8sebastian-object-enumerator/InvalidArgumentException.phpf(sebastian-object-reflector/Exception.phpf}dS7sebastian-object-reflector/InvalidArgumentException.phpf[a.sebastian-object-reflector/ObjectReflector.phpfV'sebastian-recursion-context/Context.phpFfF;wR_)sebastian-recursion-context/Exception.phpf8sebastian-recursion-context/InvalidArgumentException.phpf#sebastian-recursion-context/LICENSEfڤ%sebastian-resource-operations/LICENSEf]<4sebastian-resource-operations/ResourceOperations.php>f>,sebastian-type/LICENSE f &.sebastian-type/Parameter.phpfy#sebastian-type/ReflectionMapper.phptft=ysebastian-type/TypeName.php>f>&sebastian-type/exception/Exception.phpnfnH3-sebastian-type/exception/RuntimeException.phpf;s֤$sebastian-type/type/CallableType.php|f|KA'!sebastian-type/type/FalseType.phpfffFv)sebastian-type/type/GenericObjectType.php@f@wQ#(sebastian-type/type/IntersectionType.php +f +4$sebastian-type/type/IterableType.php"f"q!sebastian-type/type/MixedType.php+f+ v!sebastian-type/type/NeverType.phpfˤ sebastian-type/type/NullType.php&f&4"sebastian-type/type/ObjectType.phpafaeǤ"sebastian-type/type/SimpleType.phpf"sebastian-type/type/StaticType.phpf˶7 sebastian-type/type/TrueType.phpafaxsebastian-type/type/Type.phpfŵE!sebastian-type/type/UnionType.php( f( "Eä#sebastian-type/type/UnknownType.phpf4" sebastian-type/type/VoidType.phpfH sebastian-version/LICENSEfZsebastian-version/Version.phpftheseer-tokenizer/Exception.phprfrmtheseer-tokenizer/LICENSEfR ("theseer-tokenizer/NamespaceUri.phpLfLۊ9+theseer-tokenizer/NamespaceUriException.php}f}aՓtheseer-tokenizer/Token.phpfK%theseer-tokenizer/TokenCollection.phpf6.theseer-tokenizer/TokenCollectionException.phpf5ɤtheseer-tokenizer/Tokenizer.php f s'y#theseer-tokenizer/XMLSerializer.phpf#webmozart-assert/Assert.phpfc.`-webmozart-assert/InvalidArgumentException.phpfff̈́webmozart-assert/LICENSE<f<t}webmozart-assert/Mixin.php2f2a춤.phpstorm.meta.phpfO{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "5b99a9c8439c6b3977ee00d3697bebdb", + "packages": [ + { + "name": "doctrine/deprecations", + "version": "1.1.3", + "source": { + "type": "git", + "url": "https://github.com/doctrine/deprecations.git", + "reference": "dfbaa3c2d2e9a9df1118213f3b8b0c597bb99fab" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/deprecations/zipball/dfbaa3c2d2e9a9df1118213f3b8b0c597bb99fab", + "reference": "dfbaa3c2d2e9a9df1118213f3b8b0c597bb99fab", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "doctrine/coding-standard": "^9", + "phpstan/phpstan": "1.4.10 || 1.10.15", + "phpstan/phpstan-phpunit": "^1.0", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", + "psalm/plugin-phpunit": "0.18.4", + "psr/log": "^1 || ^2 || ^3", + "vimeo/psalm": "4.30.0 || 5.12.0" + }, + "suggest": { + "psr/log": "Allows logging deprecations via PSR-3 logger implementation" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Deprecations\\": "lib/Doctrine/Deprecations" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options to disable all deprecations or selectively for packages.", + "homepage": "https://www.doctrine-project.org/", + "support": { + "issues": "https://github.com/doctrine/deprecations/issues", + "source": "https://github.com/doctrine/deprecations/tree/1.1.3" + }, + "time": "2024-01-30T19:34:25+00:00" + }, + { + "name": "doctrine/instantiator", + "version": "1.5.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/instantiator.git", + "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/0a0fa9780f5d4e507415a065172d26a98d02047b", + "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "doctrine/coding-standard": "^9 || ^11", + "ext-pdo": "*", + "ext-phar": "*", + "phpbench/phpbench": "^0.16 || ^1", + "phpstan/phpstan": "^1.4", + "phpstan/phpstan-phpunit": "^1", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", + "vimeo/psalm": "^4.30 || ^5.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com", + "homepage": "https://ocramius.github.io/" + } + ], + "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", + "homepage": "https://www.doctrine-project.org/projects/instantiator.html", + "keywords": [ + "constructor", + "instantiate" + ], + "support": { + "issues": "https://github.com/doctrine/instantiator/issues", + "source": "https://github.com/doctrine/instantiator/tree/1.5.0" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finstantiator", + "type": "tidelift" + } + ], + "time": "2022-12-30T00:15:36+00:00" + }, + { + "name": "myclabs/deep-copy", + "version": "1.11.1", + "source": { + "type": "git", + "url": "https://github.com/myclabs/DeepCopy.git", + "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", + "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "conflict": { + "doctrine/collections": "<1.6.8", + "doctrine/common": "<2.13.3 || >=3,<3.2.2" + }, + "require-dev": { + "doctrine/collections": "^1.6.8", + "doctrine/common": "^2.13.3 || ^3.2.2", + "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" + }, + "type": "library", + "autoload": { + "files": [ + "src/DeepCopy/deep_copy.php" + ], + "psr-4": { + "DeepCopy\\": "src/DeepCopy/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Create deep copies (clones) of your objects", + "keywords": [ + "clone", + "copy", + "duplicate", + "object", + "object graph" + ], + "support": { + "issues": "https://github.com/myclabs/DeepCopy/issues", + "source": "https://github.com/myclabs/DeepCopy/tree/1.11.1" + }, + "funding": [ + { + "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", + "type": "tidelift" + } + ], + "time": "2023-03-08T13:26:56+00:00" + }, + { + "name": "nikic/php-parser", + "version": "v4.19.1", + "source": { + "type": "git", + "url": "https://github.com/nikic/PHP-Parser.git", + "reference": "4e1b88d21c69391150ace211e9eaf05810858d0b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/4e1b88d21c69391150ace211e9eaf05810858d0b", + "reference": "4e1b88d21c69391150ace211e9eaf05810858d0b", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": ">=7.1" + }, + "require-dev": { + "ircmaxell/php-yacc": "^0.0.7", + "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0" + }, + "bin": [ + "bin/php-parse" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.9-dev" + } + }, + "autoload": { + "psr-4": { + "PhpParser\\": "lib/PhpParser" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Nikita Popov" + } + ], + "description": "A PHP parser written in PHP", + "keywords": [ + "parser", + "php" + ], + "support": { + "issues": "https://github.com/nikic/PHP-Parser/issues", + "source": "https://github.com/nikic/PHP-Parser/tree/v4.19.1" + }, + "time": "2024-03-17T08:10:35+00:00" + }, + { + "name": "phar-io/manifest", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/phar-io/manifest.git", + "reference": "54750ef60c58e43759730615a392c31c80e23176" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/54750ef60c58e43759730615a392c31c80e23176", + "reference": "54750ef60c58e43759730615a392c31c80e23176", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "ext-phar": "*", + "ext-xmlwriter": "*", + "phar-io/version": "^3.0.1", + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", + "support": { + "issues": "https://github.com/phar-io/manifest/issues", + "source": "https://github.com/phar-io/manifest/tree/2.0.4" + }, + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2024-03-03T12:33:53+00:00" + }, + { + "name": "phar-io/version", + "version": "3.2.1", + "source": { + "type": "git", + "url": "https://github.com/phar-io/version.git", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Library for handling version information and constraints", + "support": { + "issues": "https://github.com/phar-io/version/issues", + "source": "https://github.com/phar-io/version/tree/3.2.1" + }, + "time": "2022-02-21T01:04:05+00:00" + }, + { + "name": "phpdocumentor/reflection-common", + "version": "2.2.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionCommon.git", + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-2.x": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jaap van Otterdijk", + "email": "opensource@ijaap.nl" + } + ], + "description": "Common reflection classes used by phpdocumentor to reflect the code structure", + "homepage": "http://www.phpdoc.org", + "keywords": [ + "FQSEN", + "phpDocumentor", + "phpdoc", + "reflection", + "static analysis" + ], + "support": { + "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues", + "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x" + }, + "time": "2020-06-27T09:03:43+00:00" + }, + { + "name": "phpdocumentor/reflection-docblock", + "version": "5.3.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", + "reference": "622548b623e81ca6d78b721c5e029f4ce664f170" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/622548b623e81ca6d78b721c5e029f4ce664f170", + "reference": "622548b623e81ca6d78b721c5e029f4ce664f170", + "shasum": "" + }, + "require": { + "ext-filter": "*", + "php": "^7.2 || ^8.0", + "phpdocumentor/reflection-common": "^2.2", + "phpdocumentor/type-resolver": "^1.3", + "webmozart/assert": "^1.9.1" + }, + "require-dev": { + "mockery/mockery": "~1.3.2", + "psalm/phar": "^4.8" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + }, + { + "name": "Jaap van Otterdijk", + "email": "account@ijaap.nl" + } + ], + "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", + "support": { + "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", + "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.3.0" + }, + "time": "2021-10-19T17:43:47+00:00" + }, + { + "name": "phpdocumentor/type-resolver", + "version": "1.8.2", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/TypeResolver.git", + "reference": "153ae662783729388a584b4361f2545e4d841e3c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/153ae662783729388a584b4361f2545e4d841e3c", + "reference": "153ae662783729388a584b4361f2545e4d841e3c", + "shasum": "" + }, + "require": { + "doctrine/deprecations": "^1.0", + "php": "^7.3 || ^8.0", + "phpdocumentor/reflection-common": "^2.0", + "phpstan/phpdoc-parser": "^1.13" + }, + "require-dev": { + "ext-tokenizer": "*", + "phpbench/phpbench": "^1.2", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan": "^1.8", + "phpstan/phpstan-phpunit": "^1.1", + "phpunit/phpunit": "^9.5", + "rector/rector": "^0.13.9", + "vimeo/psalm": "^4.25" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-1.x": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + } + ], + "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", + "support": { + "issues": "https://github.com/phpDocumentor/TypeResolver/issues", + "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.8.2" + }, + "time": "2024-02-23T11:10:43+00:00" + }, + { + "name": "phpspec/prophecy", + "version": "v1.19.0", + "source": { + "type": "git", + "url": "https://github.com/phpspec/prophecy.git", + "reference": "67a759e7d8746d501c41536ba40cd9c0a07d6a87" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/67a759e7d8746d501c41536ba40cd9c0a07d6a87", + "reference": "67a759e7d8746d501c41536ba40cd9c0a07d6a87", + "shasum": "" + }, + "require": { + "doctrine/instantiator": "^1.2 || ^2.0", + "php": "^7.2 || 8.0.* || 8.1.* || 8.2.* || 8.3.*", + "phpdocumentor/reflection-docblock": "^5.2", + "sebastian/comparator": "^3.0 || ^4.0 || ^5.0 || ^6.0", + "sebastian/recursion-context": "^3.0 || ^4.0 || ^5.0 || ^6.0" + }, + "require-dev": { + "phpspec/phpspec": "^6.0 || ^7.0", + "phpstan/phpstan": "^1.9", + "phpunit/phpunit": "^8.0 || ^9.0 || ^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Prophecy\\": "src/Prophecy" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Konstantin Kudryashov", + "email": "ever.zet@gmail.com", + "homepage": "http://everzet.com" + }, + { + "name": "Marcello Duarte", + "email": "marcello.duarte@gmail.com" + } + ], + "description": "Highly opinionated mocking framework for PHP 5.3+", + "homepage": "https://github.com/phpspec/prophecy", + "keywords": [ + "Double", + "Dummy", + "dev", + "fake", + "mock", + "spy", + "stub" + ], + "support": { + "issues": "https://github.com/phpspec/prophecy/issues", + "source": "https://github.com/phpspec/prophecy/tree/v1.19.0" + }, + "time": "2024-02-29T11:52:51+00:00" + }, + { + "name": "phpstan/phpdoc-parser", + "version": "1.28.0", + "source": { + "type": "git", + "url": "https://github.com/phpstan/phpdoc-parser.git", + "reference": "cd06d6b1a1b3c75b0b83f97577869fd85a3cd4fb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/cd06d6b1a1b3c75b0b83f97577869fd85a3cd4fb", + "reference": "cd06d6b1a1b3c75b0b83f97577869fd85a3cd4fb", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "doctrine/annotations": "^2.0", + "nikic/php-parser": "^4.15", + "php-parallel-lint/php-parallel-lint": "^1.2", + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^1.5", + "phpstan/phpstan-phpunit": "^1.1", + "phpstan/phpstan-strict-rules": "^1.0", + "phpunit/phpunit": "^9.5", + "symfony/process": "^5.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "PHPStan\\PhpDocParser\\": [ + "src/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHPDoc parser with support for nullable, intersection and generic types", + "support": { + "issues": "https://github.com/phpstan/phpdoc-parser/issues", + "source": "https://github.com/phpstan/phpdoc-parser/tree/1.28.0" + }, + "time": "2024-04-03T18:51:33+00:00" + }, + { + "name": "phpunit/php-code-coverage", + "version": "9.2.31", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "48c34b5d8d983006bd2adc2d0de92963b9155965" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/48c34b5d8d983006bd2adc2d0de92963b9155965", + "reference": "48c34b5d8d983006bd2adc2d0de92963b9155965", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "ext-xmlwriter": "*", + "nikic/php-parser": "^4.18 || ^5.0", + "php": ">=7.3", + "phpunit/php-file-iterator": "^3.0.3", + "phpunit/php-text-template": "^2.0.2", + "sebastian/code-unit-reverse-lookup": "^2.0.2", + "sebastian/complexity": "^2.0", + "sebastian/environment": "^5.1.2", + "sebastian/lines-of-code": "^1.0.3", + "sebastian/version": "^3.0.1", + "theseer/tokenizer": "^1.2.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-pcov": "PHP extension that provides line coverage", + "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "9.2-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", + "homepage": "https://github.com/sebastianbergmann/php-code-coverage", + "keywords": [ + "coverage", + "testing", + "xunit" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", + "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.31" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-02T06:37:42+00:00" + }, + { + "name": "phpunit/php-file-iterator", + "version": "3.0.6", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-file-iterator.git", + "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", + "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "FilterIterator implementation that filters files based on a list of suffixes.", + "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", + "keywords": [ + "filesystem", + "iterator" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.6" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2021-12-02T12:48:52+00:00" + }, + { + "name": "phpunit/php-invoker", + "version": "3.1.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-invoker.git", + "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/5a10147d0aaf65b58940a0b72f71c9ac0423cc67", + "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "ext-pcntl": "*", + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-pcntl": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Invoke callables with a timeout", + "homepage": "https://github.com/sebastianbergmann/php-invoker/", + "keywords": [ + "process" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-invoker/issues", + "source": "https://github.com/sebastianbergmann/php-invoker/tree/3.1.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T05:58:55+00:00" + }, + { + "name": "phpunit/php-text-template", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", + "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Simple template engine.", + "homepage": "https://github.com/sebastianbergmann/php-text-template/", + "keywords": [ + "template" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-text-template/issues", + "source": "https://github.com/sebastianbergmann/php-text-template/tree/2.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T05:33:50+00:00" + }, + { + "name": "phpunit/php-timer", + "version": "5.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", + "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Utility class for timing", + "homepage": "https://github.com/sebastianbergmann/php-timer/", + "keywords": [ + "timer" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-timer/issues", + "source": "https://github.com/sebastianbergmann/php-timer/tree/5.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:16:10+00:00" + }, + { + "name": "sebastian/cli-parser", + "version": "1.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/cli-parser.git", + "reference": "2b56bea83a09de3ac06bb18b92f068e60cc6f50b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/2b56bea83a09de3ac06bb18b92f068e60cc6f50b", + "reference": "2b56bea83a09de3ac06bb18b92f068e60cc6f50b", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for parsing CLI options", + "homepage": "https://github.com/sebastianbergmann/cli-parser", + "support": { + "issues": "https://github.com/sebastianbergmann/cli-parser/issues", + "source": "https://github.com/sebastianbergmann/cli-parser/tree/1.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-02T06:27:43+00:00" + }, + { + "name": "sebastian/code-unit", + "version": "1.0.8", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit.git", + "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/1fc9f64c0927627ef78ba436c9b17d967e68e120", + "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the PHP code units", + "homepage": "https://github.com/sebastianbergmann/code-unit", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit/issues", + "source": "https://github.com/sebastianbergmann/code-unit/tree/1.0.8" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:08:54+00:00" + }, + { + "name": "sebastian/code-unit-reverse-lookup", + "version": "2.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", + "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", + "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Looks up which function or method a line of code belongs to", + "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", + "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/2.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T05:30:19+00:00" + }, + { + "name": "sebastian/comparator", + "version": "4.0.8", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/comparator.git", + "reference": "fa0f136dd2334583309d32b62544682ee972b51a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/fa0f136dd2334583309d32b62544682ee972b51a", + "reference": "fa0f136dd2334583309d32b62544682ee972b51a", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "sebastian/diff": "^4.0", + "sebastian/exporter": "^4.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + } + ], + "description": "Provides the functionality to compare PHP values for equality", + "homepage": "https://github.com/sebastianbergmann/comparator", + "keywords": [ + "comparator", + "compare", + "equality" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/comparator/issues", + "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.8" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2022-09-14T12:41:17+00:00" + }, + { + "name": "sebastian/complexity", + "version": "2.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/complexity.git", + "reference": "25f207c40d62b8b7aa32f5ab026c53561964053a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/25f207c40d62b8b7aa32f5ab026c53561964053a", + "reference": "25f207c40d62b8b7aa32f5ab026c53561964053a", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^4.18 || ^5.0", + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for calculating the complexity of PHP code units", + "homepage": "https://github.com/sebastianbergmann/complexity", + "support": { + "issues": "https://github.com/sebastianbergmann/complexity/issues", + "source": "https://github.com/sebastianbergmann/complexity/tree/2.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-12-22T06:19:30+00:00" + }, + { + "name": "sebastian/diff", + "version": "4.0.6", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "ba01945089c3a293b01ba9badc29ad55b106b0bc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/ba01945089c3a293b01ba9badc29ad55b106b0bc", + "reference": "ba01945089c3a293b01ba9badc29ad55b106b0bc", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3", + "symfony/process": "^4.2 || ^5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" + } + ], + "description": "Diff implementation", + "homepage": "https://github.com/sebastianbergmann/diff", + "keywords": [ + "diff", + "udiff", + "unidiff", + "unified diff" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/diff/issues", + "source": "https://github.com/sebastianbergmann/diff/tree/4.0.6" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-02T06:30:58+00:00" + }, + { + "name": "sebastian/environment", + "version": "5.1.5", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/environment.git", + "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", + "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-posix": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides functionality to handle HHVM/PHP environments", + "homepage": "http://www.github.com/sebastianbergmann/environment", + "keywords": [ + "Xdebug", + "environment", + "hhvm" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/environment/issues", + "source": "https://github.com/sebastianbergmann/environment/tree/5.1.5" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:03:51+00:00" + }, + { + "name": "sebastian/exporter", + "version": "4.0.6", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/exporter.git", + "reference": "78c00df8f170e02473b682df15bfcdacc3d32d72" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/78c00df8f170e02473b682df15bfcdacc3d32d72", + "reference": "78c00df8f170e02473b682df15bfcdacc3d32d72", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "sebastian/recursion-context": "^4.0" + }, + "require-dev": { + "ext-mbstring": "*", + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Provides the functionality to export PHP variables for visualization", + "homepage": "https://www.github.com/sebastianbergmann/exporter", + "keywords": [ + "export", + "exporter" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/exporter/issues", + "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.6" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-02T06:33:00+00:00" + }, + { + "name": "sebastian/global-state", + "version": "5.0.7", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/global-state.git", + "reference": "bca7df1f32ee6fe93b4d4a9abbf69e13a4ada2c9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bca7df1f32ee6fe93b4d4a9abbf69e13a4ada2c9", + "reference": "bca7df1f32ee6fe93b4d4a9abbf69e13a4ada2c9", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "sebastian/object-reflector": "^2.0", + "sebastian/recursion-context": "^4.0" + }, + "require-dev": { + "ext-dom": "*", + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-uopz": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Snapshotting of global state", + "homepage": "http://www.github.com/sebastianbergmann/global-state", + "keywords": [ + "global state" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/global-state/issues", + "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.7" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-02T06:35:11+00:00" + }, + { + "name": "sebastian/lines-of-code", + "version": "1.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/lines-of-code.git", + "reference": "e1e4a170560925c26d424b6a03aed157e7dcc5c5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/e1e4a170560925c26d424b6a03aed157e7dcc5c5", + "reference": "e1e4a170560925c26d424b6a03aed157e7dcc5c5", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^4.18 || ^5.0", + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for counting the lines of code in PHP source code", + "homepage": "https://github.com/sebastianbergmann/lines-of-code", + "support": { + "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", + "source": "https://github.com/sebastianbergmann/lines-of-code/tree/1.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-12-22T06:20:34+00:00" + }, + { + "name": "sebastian/object-enumerator", + "version": "4.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-enumerator.git", + "reference": "5c9eeac41b290a3712d88851518825ad78f45c71" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/5c9eeac41b290a3712d88851518825ad78f45c71", + "reference": "5c9eeac41b290a3712d88851518825ad78f45c71", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "sebastian/object-reflector": "^2.0", + "sebastian/recursion-context": "^4.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Traverses array structures and object graphs to enumerate all referenced objects", + "homepage": "https://github.com/sebastianbergmann/object-enumerator/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", + "source": "https://github.com/sebastianbergmann/object-enumerator/tree/4.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:12:34+00:00" + }, + { + "name": "sebastian/object-reflector", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-reflector.git", + "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", + "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Allows reflection of object attributes, including inherited and non-public ones", + "homepage": "https://github.com/sebastianbergmann/object-reflector/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-reflector/issues", + "source": "https://github.com/sebastianbergmann/object-reflector/tree/2.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:14:26+00:00" + }, + { + "name": "sebastian/recursion-context", + "version": "4.0.5", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/recursion-context.git", + "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", + "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + } + ], + "description": "Provides functionality to recursively process PHP variables", + "homepage": "https://github.com/sebastianbergmann/recursion-context", + "support": { + "issues": "https://github.com/sebastianbergmann/recursion-context/issues", + "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.5" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:07:39+00:00" + }, + { + "name": "sebastian/resource-operations", + "version": "3.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/resource-operations.git", + "reference": "05d5692a7993ecccd56a03e40cd7e5b09b1d404e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/05d5692a7993ecccd56a03e40cd7e5b09b1d404e", + "reference": "05d5692a7993ecccd56a03e40cd7e5b09b1d404e", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides a list of PHP built-in functions that operate on resources", + "homepage": "https://www.github.com/sebastianbergmann/resource-operations", + "support": { + "source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-14T16:00:52+00:00" + }, + { + "name": "sebastian/type", + "version": "3.2.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/type.git", + "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", + "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.2-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the types of the PHP type system", + "homepage": "https://github.com/sebastianbergmann/type", + "support": { + "issues": "https://github.com/sebastianbergmann/type/issues", + "source": "https://github.com/sebastianbergmann/type/tree/3.2.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:13:03+00:00" + }, + { + "name": "sebastian/version", + "version": "3.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "c6c1022351a901512170118436c764e473f6de8c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c6c1022351a901512170118436c764e473f6de8c", + "reference": "c6c1022351a901512170118436c764e473f6de8c", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that helps with managing the version number of Git-hosted PHP projects", + "homepage": "https://github.com/sebastianbergmann/version", + "support": { + "issues": "https://github.com/sebastianbergmann/version/issues", + "source": "https://github.com/sebastianbergmann/version/tree/3.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T06:39:44+00:00" + }, + { + "name": "theseer/tokenizer", + "version": "1.2.3", + "source": { + "type": "git", + "url": "https://github.com/theseer/tokenizer.git", + "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", + "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": "^7.2 || ^8.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + } + ], + "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", + "support": { + "issues": "https://github.com/theseer/tokenizer/issues", + "source": "https://github.com/theseer/tokenizer/tree/1.2.3" + }, + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2024-03-03T12:36:25+00:00" + }, + { + "name": "webmozart/assert", + "version": "1.11.0", + "source": { + "type": "git", + "url": "https://github.com/webmozarts/assert.git", + "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/webmozarts/assert/zipball/11cb2199493b2f8a3b53e7f19068fc6aac760991", + "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "php": "^7.2 || ^8.0" + }, + "conflict": { + "phpstan/phpstan": "<0.12.20", + "vimeo/psalm": "<4.6.1 || 4.6.2" + }, + "require-dev": { + "phpunit/phpunit": "^8.5.13" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.10-dev" + } + }, + "autoload": { + "psr-4": { + "Webmozart\\Assert\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Assertions to validate method input/output with nice error messages.", + "keywords": [ + "assert", + "check", + "validate" + ], + "support": { + "issues": "https://github.com/webmozarts/assert/issues", + "source": "https://github.com/webmozarts/assert/tree/1.11.0" + }, + "time": "2022-06-03T18:03:27+00:00" + } + ], + "packages-dev": [], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": true, + "prefer-lowest": false, + "platform": { + "php": ">=7.3", + "ext-dom": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-xml": "*", + "ext-xmlwriter": "*" + }, + "platform-dev": [], + "platform-overrides": { + "php": "7.3.0" + }, + "plugin-api-version": "2.6.0" +} +|null */ + private static $type; + /** @var LoggerInterface|null */ + private static $logger; + /** @var array */ + private static $ignoredPackages = []; + /** @var array */ + private static $triggeredDeprecations = []; + /** @var array */ + private static $ignoredLinks = []; + /** @var bool */ + private static $deduplication = \true; + /** + * Trigger a deprecation for the given package and identfier. + * + * The link should point to a Github issue or Wiki entry detailing the + * deprecation. It is additionally used to de-duplicate the trigger of the + * same deprecation during a request. + * + * @param float|int|string $args + */ + public static function trigger(string $package, string $link, string $message, ...$args) : void + { + $type = self::$type ?? self::getTypeFromEnv(); + if ($type === self::TYPE_NONE) { + return; + } + if (isset(self::$ignoredLinks[$link])) { + return; + } + if (array_key_exists($link, self::$triggeredDeprecations)) { + self::$triggeredDeprecations[$link]++; + } else { + self::$triggeredDeprecations[$link] = 1; + } + if (self::$deduplication === \true && self::$triggeredDeprecations[$link] > 1) { + return; + } + if (isset(self::$ignoredPackages[$package])) { + return; + } + $backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2); + $message = sprintf($message, ...$args); + self::delegateTriggerToBackend($message, $backtrace, $link, $package); + } + /** + * Trigger a deprecation for the given package and identifier when called from outside. + * + * "Outside" means we assume that $package is currently installed as a + * dependency and the caller is not a file in that package. When $package + * is installed as a root package then deprecations triggered from the + * tests folder are also considered "outside". + * + * This deprecation method assumes that you are using Composer to install + * the dependency and are using the default /vendor/ folder and not a + * Composer plugin to change the install location. The assumption is also + * that $package is the exact composer packge name. + * + * Compared to {@link trigger()} this method causes some overhead when + * deprecation tracking is enabled even during deduplication, because it + * needs to call {@link debug_backtrace()} + * + * @param float|int|string $args + */ + public static function triggerIfCalledFromOutside(string $package, string $link, string $message, ...$args) : void + { + $type = self::$type ?? self::getTypeFromEnv(); + if ($type === self::TYPE_NONE) { + return; + } + $backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2); + // first check that the caller is not from a tests folder, in which case we always let deprecations pass + if (isset($backtrace[1]['file'], $backtrace[0]['file']) && strpos($backtrace[1]['file'], DIRECTORY_SEPARATOR . 'tests' . DIRECTORY_SEPARATOR) === \false) { + $path = DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR . str_replace('/', DIRECTORY_SEPARATOR, $package) . DIRECTORY_SEPARATOR; + if (strpos($backtrace[0]['file'], $path) === \false) { + return; + } + if (strpos($backtrace[1]['file'], $path) !== \false) { + return; + } + } + if (isset(self::$ignoredLinks[$link])) { + return; + } + if (array_key_exists($link, self::$triggeredDeprecations)) { + self::$triggeredDeprecations[$link]++; + } else { + self::$triggeredDeprecations[$link] = 1; + } + if (self::$deduplication === \true && self::$triggeredDeprecations[$link] > 1) { + return; + } + if (isset(self::$ignoredPackages[$package])) { + return; + } + $message = sprintf($message, ...$args); + self::delegateTriggerToBackend($message, $backtrace, $link, $package); + } + /** + * @param list $backtrace + */ + private static function delegateTriggerToBackend(string $message, array $backtrace, string $link, string $package) : void + { + $type = self::$type ?? self::getTypeFromEnv(); + if (($type & self::TYPE_PSR_LOGGER) > 0) { + $context = ['file' => $backtrace[0]['file'] ?? null, 'line' => $backtrace[0]['line'] ?? null, 'package' => $package, 'link' => $link]; + assert(self::$logger !== null); + self::$logger->notice($message, $context); + } + if (!(($type & self::TYPE_TRIGGER_ERROR) > 0)) { + return; + } + $message .= sprintf(' (%s:%d called by %s:%d, %s, package %s)', self::basename($backtrace[0]['file'] ?? 'native code'), $backtrace[0]['line'] ?? 0, self::basename($backtrace[1]['file'] ?? 'native code'), $backtrace[1]['line'] ?? 0, $link, $package); + @trigger_error($message, E_USER_DEPRECATED); + } + /** + * A non-local-aware version of PHPs basename function. + */ + private static function basename(string $filename) : string + { + $pos = strrpos($filename, DIRECTORY_SEPARATOR); + if ($pos === \false) { + return $filename; + } + return substr($filename, $pos + 1); + } + public static function enableTrackingDeprecations() : void + { + self::$type = self::$type ?? 0; + self::$type |= self::TYPE_TRACK_DEPRECATIONS; + } + public static function enableWithTriggerError() : void + { + self::$type = self::$type ?? 0; + self::$type |= self::TYPE_TRIGGER_ERROR; + } + public static function enableWithPsrLogger(LoggerInterface $logger) : void + { + self::$type = self::$type ?? 0; + self::$type |= self::TYPE_PSR_LOGGER; + self::$logger = $logger; + } + public static function withoutDeduplication() : void + { + self::$deduplication = \false; + } + public static function disable() : void + { + self::$type = self::TYPE_NONE; + self::$logger = null; + self::$deduplication = \true; + self::$ignoredLinks = []; + foreach (self::$triggeredDeprecations as $link => $count) { + self::$triggeredDeprecations[$link] = 0; + } + } + public static function ignorePackage(string $packageName) : void + { + self::$ignoredPackages[$packageName] = \true; + } + public static function ignoreDeprecations(string ...$links) : void + { + foreach ($links as $link) { + self::$ignoredLinks[$link] = \true; + } + } + public static function getUniqueTriggeredDeprecationsCount() : int + { + return array_reduce(self::$triggeredDeprecations, static function (int $carry, int $count) { + return $carry + $count; + }, 0); + } + /** + * Returns each triggered deprecation link identifier and the amount of occurrences. + * + * @return array + */ + public static function getTriggeredDeprecations() : array + { + return self::$triggeredDeprecations; + } + /** + * @return int-mask-of + */ + private static function getTypeFromEnv() : int + { + switch ($_SERVER['DOCTRINE_DEPRECATIONS'] ?? $_ENV['DOCTRINE_DEPRECATIONS'] ?? null) { + case 'trigger': + self::$type = self::TYPE_TRIGGER_ERROR; + break; + case 'track': + self::$type = self::TYPE_TRACK_DEPRECATIONS; + break; + default: + self::$type = self::TYPE_NONE; + break; + } + return self::$type; + } +} + */ + private $doctrineDeprecationsExpectations = []; + /** @var array */ + private $doctrineNoDeprecationsExpectations = []; + public function expectDeprecationWithIdentifier(string $identifier) : void + { + $this->doctrineDeprecationsExpectations[$identifier] = Deprecation::getTriggeredDeprecations()[$identifier] ?? 0; + } + public function expectNoDeprecationWithIdentifier(string $identifier) : void + { + $this->doctrineNoDeprecationsExpectations[$identifier] = Deprecation::getTriggeredDeprecations()[$identifier] ?? 0; + } + /** + * @before + */ + public function enableDeprecationTracking() : void + { + Deprecation::enableTrackingDeprecations(); + } + /** + * @after + */ + public function verifyDeprecationsAreTriggered() : void + { + foreach ($this->doctrineDeprecationsExpectations as $identifier => $expectation) { + $actualCount = Deprecation::getTriggeredDeprecations()[$identifier] ?? 0; + $this->assertTrue($actualCount > $expectation, sprintf("Expected deprecation with identifier '%s' was not triggered by code executed in test.", $identifier)); + } + foreach ($this->doctrineNoDeprecationsExpectations as $identifier => $expectation) { + $actualCount = Deprecation::getTriggeredDeprecations()[$identifier] ?? 0; + $this->assertTrue($actualCount === $expectation, sprintf("Expected deprecation with identifier '%s' was triggered by code executed in test, but expected not to.", $identifier)); + } + } +} +Copyright (c) 2020-2021 Doctrine Project + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +fallbackEvaluator = $fallbackEvaluator ?? function (Expr $expr) { throw new ConstExprEvaluationException("Expression of type {$expr->getType()} cannot be evaluated"); @@ -6291,7 +8855,7 @@ class ConstExprEvaluator attrGroups = $attrGroups; @@ -6779,7 +9343,7 @@ class PrintableNewAnonClassNode extends Expr emulators, function ($emulator) use($code) { return $emulator->isEmulationNeeded($code); @@ -7813,9 +10377,9 @@ class Emulative extends Lexer namespace = $namespace; $this->origAliases = $this->aliases = [Stmt\Use_::TYPE_NORMAL => [], Stmt\Use_::TYPE_FUNCTION => [], Stmt\Use_::TYPE_CONSTANT => []]; @@ -8632,7 +11196,7 @@ class NameContext attributes = $attributes; $this->name = $name; @@ -8811,10 +11375,10 @@ class Arg extends NodeAbstract attributes = $attributes; $this->var = $var; @@ -8966,9 +11530,9 @@ class ArrayDimFetch extends Expr attributes = $attributes; $this->key = $key; @@ -9007,9 +11571,9 @@ class ArrayItem extends Expr attributes = $attributes; $this->expr = $expr; @@ -10471,10 +13035,10 @@ class Exit_ extends Expr attributes = $attributes; $this->key = $key; @@ -11359,9 +13923,9 @@ class Yield_ extends Expr parts); $realOffset = $offset < 0 ? $offset + $numParts : $offset; @@ -11766,9 +14330,9 @@ class Name extends NodeAbstract attributes = $attributes; $this->type = \is_string($type) ? new Identifier($type) : $type; @@ -11953,7 +14517,7 @@ class Param extends NodeAbstract attributes = $attributes; $this->num = $num; @@ -12493,9 +15057,9 @@ class Break_ extends Node\Stmt attributes = $attributes; $this->types = $types; @@ -12566,9 +15130,9 @@ class Catch_ extends Node\Stmt attributes = $attributes; $this->num = $num; @@ -13081,9 +15645,9 @@ class Continue_ extends Node\Stmt attributes = $attributes; $this->declares = $declares; @@ -13149,9 +15713,9 @@ class Declare_ extends Node\Stmt name = \is_string($name) ? new Node\Identifier($name) : $name; @@ -13314,9 +15878,9 @@ class EnumCase extends Node\Stmt attributes = $attributes; $this->name = $name; @@ -13890,9 +16454,9 @@ class Namespace_ extends Node\Stmt attributes = $attributes; $this->name = \is_string($name) ? new Node\VarLikeIdentifier($name) : $name; @@ -14032,9 +16596,9 @@ class PropertyProperty extends Node\Stmt attributes = $attributes; $this->expr = $expr; @@ -14062,10 +16626,10 @@ class Return_ extends Node\Stmt attributes = $attributes; $this->var = $var; @@ -14097,9 +16661,9 @@ class StaticVar extends Node\Stmt attributes = $attributes; $this->stmts = $stmts; @@ -14381,9 +16945,9 @@ class TryCatch extends Node\Stmt code = $code; return $this->dumpRecursive($node); @@ -14976,10 +17540,10 @@ class NodeDumper nameContext = new NameContext($errorHandler ?? new ErrorHandler\Throwing()); $this->preserveOriginalNames = $options['preserveOriginalNames'] ?? \false; @@ -15620,6 +18184,9 @@ class NameResolver extends NodeVisitorAbstract } } else { if ($node instanceof Stmt\ClassConst) { + if (null !== $node->type) { + $node->type = $this->resolveType($node->type); + } $this->resolveAttrGroups($node); } else { if ($node instanceof Stmt\EnumCase) { @@ -15657,7 +18224,7 @@ class NameResolver extends NodeVisitorAbstract } return null; } - private function addAlias(Stmt\UseUse $use, int $type, Name $prefix = null) + private function addAlias(Stmt\UseUse $use, int $type, ?Name $prefix = null) { // Add prefix for group uses $name = $prefix ? Name::concat($prefix, $use->name) : $use->name; @@ -15745,10 +18312,10 @@ class NameResolver extends NodeVisitorAbstract parsers = $parsers; } - public function parse(string $code, ErrorHandler $errorHandler = null) + public function parse(string $code, ?ErrorHandler $errorHandler = null) { if (null === $errorHandler) { $errorHandler = new ErrorHandler\Throwing(); @@ -15928,21 +18495,21 @@ class Multiple implements Parser } semStack[$stackPos - (2 - 2)] !== null) { $this->semStack[$stackPos - (2 - 1)][] = $this->semStack[$stackPos - (2 - 2)]; $this->semValue = $this->semStack[$stackPos - (2 - 1)]; + } else { + $this->semValue = $this->semStack[$stackPos - (2 - 1)]; } }, 260 => function ($stackPos) { $this->semValue = array(); @@ -17246,21 +19815,21 @@ class Php5 extends \PHPUnit\PhpParser\ParserAbstract } semStack[$stackPos - (2 - 2)] !== null) { $this->semStack[$stackPos - (2 - 1)][] = $this->semStack[$stackPos - (2 - 2)]; $this->semValue = $this->semStack[$stackPos - (2 - 1)]; + } else { + $this->semValue = $this->semStack[$stackPos - (2 - 1)]; } }, 341 => function ($stackPos) { $this->semValue = array(); @@ -18685,7 +21256,7 @@ class Php7 extends \PHPUnit\PhpParser\ParserAbstract } errorHandler = $errorHandler ?: new ErrorHandler\Throwing(); $this->lexer->startLexing($code, $this->errorHandler); @@ -19696,8 +22267,10 @@ abstract class ParserAbstract implements Parser getLexerOptions())); + } + /** + * Create a parser targeting the host PHP version, that is the PHP version we're currently + * running on. This parser will not use any token emulation. + * + * All supported lexer attributes (comments, startLine, endLine, startTokenPos, endTokenPos, + * startFilePos, endFilePos) will be enabled. + */ + public function createForHostVersion() : Parser + { + return new Php7(new Lexer($this->getLexerOptions())); + } + private function getLexerOptions() : array + { + return ['usedAttributes' => ['comments', 'startLine', 'endLine', 'startTokenPos', 'endTokenPos', 'startFilePos', 'endFilePos']]; + } } , Sebastian Heuer , Sebastian Bergmann + * Copyright (c) Arne Blankerts , Sebastian Heuer , Sebastian Bergmann and contributors * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. + * */ -namespace PHPUnit\PharIo\Manifest; +namespace PHPUnitPHAR\PharIo\Manifest; -use PHPUnit\PharIo\Version\Exception as VersionException; -use PHPUnit\PharIo\Version\Version; -use PHPUnit\PharIo\Version\VersionConstraintParser; +use PHPUnitPHAR\PharIo\Version\Exception as VersionException; +use PHPUnitPHAR\PharIo\Version\Version; +use PHPUnitPHAR\PharIo\Version\VersionConstraintParser; +use Throwable; +use function sprintf; class ManifestDocumentMapper { public function map(ManifestDocument $document) : Manifest @@ -22078,9 +24681,7 @@ class ManifestDocumentMapper $requirements = $this->mapRequirements($document->getRequiresElement()); $bundledComponents = $this->mapBundledComponents($document); return new Manifest(new ApplicationName($contains->getName()), new Version($contains->getVersion()), $type, $copyright, $requirements, $bundledComponents); - } catch (VersionException $e) { - throw new ManifestDocumentMapperException($e->getMessage(), (int) $e->getCode(), $e); - } catch (Exception $e) { + } catch (Throwable $e) { throw new ManifestDocumentMapperException($e->getMessage(), (int) $e->getCode(), $e); } } @@ -22094,13 +24695,13 @@ class ManifestDocumentMapper case 'extension': return $this->mapExtension($contains->getExtensionElement()); } - throw new ManifestDocumentMapperException(\sprintf('Unsupported type %s', $contains->getType())); + throw new ManifestDocumentMapperException(sprintf('Unsupported type %s', $contains->getType())); } private function mapCopyright(CopyrightElement $copyright) : CopyrightInformation { $authors = new AuthorCollection(); foreach ($copyright->getAuthorElements() as $authorElement) { - $authors->add(new Author($authorElement->getName(), new Email($authorElement->getEmail()))); + $authors->add(new Author($authorElement->getName(), $authorElement->hasEMail() ? new Email($authorElement->getEmail()) : null)); } $licenseElement = $copyright->getLicenseElement(); $license = new License($licenseElement->getType(), new Url($licenseElement->getUrl())); @@ -22114,7 +24715,7 @@ class ManifestDocumentMapper try { $versionConstraint = $parser->parse($phpElement->getVersion()); } catch (VersionException $e) { - throw new ManifestDocumentMapperException(\sprintf('Unsupported version constraint - %s', $e->getMessage()), (int) $e->getCode(), $e); + throw new ManifestDocumentMapperException(sprintf('Unsupported version constraint - %s', $e->getMessage()), (int) $e->getCode(), $e); } $collection->add(new PhpVersionRequirement($versionConstraint)); if (!$phpElement->hasExtElements()) { @@ -22142,7 +24743,7 @@ class ManifestDocumentMapper $versionConstraint = (new VersionConstraintParser())->parse($extension->getCompatible()); return Type::extension(new ApplicationName($extension->getFor()), $versionConstraint); } catch (VersionException $e) { - throw new ManifestDocumentMapperException(\sprintf('Unsupported version constraint - %s', $e->getMessage()), (int) $e->getCode(), $e); + throw new ManifestDocumentMapperException(sprintf('Unsupported version constraint - %s', $e->getMessage()), (int) $e->getCode(), $e); } } } @@ -22152,13 +24753,15 @@ declare (strict_types=1); /* * This file is part of PharIo\Manifest. * - * (c) Arne Blankerts , Sebastian Heuer , Sebastian Bergmann + * Copyright (c) Arne Blankerts , Sebastian Heuer , Sebastian Bergmann and contributors * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. + * */ -namespace PHPUnit\PharIo\Manifest; +namespace PHPUnitPHAR\PharIo\Manifest; +use function sprintf; class ManifestLoader { public static function fromFile(string $filename) : Manifest @@ -22166,7 +24769,7 @@ class ManifestLoader try { return (new ManifestDocumentMapper())->map(ManifestDocument::fromFile($filename)); } catch (Exception $e) { - throw new ManifestLoaderException(\sprintf('Loading %s failed.', $filename), (int) $e->getCode(), $e); + throw new ManifestLoaderException(sprintf('Loading %s failed.', $filename), (int) $e->getCode(), $e); } } public static function fromPhar(string $filename) : Manifest @@ -22188,17 +24791,21 @@ declare (strict_types=1); /* * This file is part of PharIo\Manifest. * - * (c) Arne Blankerts , Sebastian Heuer , Sebastian Bergmann + * Copyright (c) Arne Blankerts , Sebastian Heuer , Sebastian Bergmann and contributors * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. + * */ -namespace PHPUnit\PharIo\Manifest; +namespace PHPUnitPHAR\PharIo\Manifest; -use PHPUnit\PharIo\Version\AnyVersionConstraint; -use PHPUnit\PharIo\Version\Version; -use PHPUnit\PharIo\Version\VersionConstraint; +use PHPUnitPHAR\PharIo\Version\AnyVersionConstraint; +use PHPUnitPHAR\PharIo\Version\Version; +use PHPUnitPHAR\PharIo\Version\VersionConstraint; use XMLWriter; +use function count; +use function file_put_contents; +use function str_repeat; /** @psalm-suppress MissingConstructor */ class ManifestSerializer { @@ -22206,7 +24813,7 @@ class ManifestSerializer private $xmlWriter; public function serializeToFile(Manifest $manifest, string $filename) : void { - \file_put_contents($filename, $this->serializeToString($manifest)); + file_put_contents($filename, $this->serializeToString($manifest)); } public function serializeToString(Manifest $manifest) : string { @@ -22222,7 +24829,7 @@ class ManifestSerializer $xmlWriter = new XMLWriter(); $xmlWriter->openMemory(); $xmlWriter->setIndent(\true); - $xmlWriter->setIndentString(\str_repeat(' ', 4)); + $xmlWriter->setIndentString(str_repeat(' ', 4)); $xmlWriter->startDocument('1.0', 'UTF-8'); $xmlWriter->startElement('phar'); $xmlWriter->writeAttribute('xmlns', 'https://phar.io/xml/manifest/1.0'); @@ -22298,7 +24905,7 @@ class ManifestSerializer } private function addBundles(BundledComponentCollection $bundledComponentCollection) : void { - if (\count($bundledComponentCollection) === 0) { + if (count($bundledComponentCollection) === 0) { return; } $this->xmlWriter->startElement('bundles'); @@ -22324,14 +24931,16 @@ declare (strict_types=1); /* * This file is part of PharIo\Manifest. * - * (c) Arne Blankerts , Sebastian Heuer , Sebastian Bergmann + * Copyright (c) Arne Blankerts , Sebastian Heuer , Sebastian Bergmann and contributors * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. + * */ -namespace PHPUnit\PharIo\Manifest; +namespace PHPUnitPHAR\PharIo\Manifest; -class ElementCollectionException extends \InvalidArgumentException implements Exception +use InvalidArgumentException; +class ElementCollectionException extends InvalidArgumentException implements Exception { } , Sebastian Heuer , Sebastian Bergmann + * Copyright (c) Arne Blankerts , Sebastian Heuer , Sebastian Bergmann and contributors * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. + * */ -namespace PHPUnit\PharIo\Manifest; +namespace PHPUnitPHAR\PharIo\Manifest; -interface Exception extends \Throwable +use Throwable; +interface Exception extends Throwable { } , Sebastian Heuer , Sebastian Bergmann + * Copyright (c) Arne Blankerts , Sebastian Heuer , Sebastian Bergmann and contributors * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. + * */ -namespace PHPUnit\PharIo\Manifest; +namespace PHPUnitPHAR\PharIo\Manifest; -class InvalidApplicationNameException extends \InvalidArgumentException implements Exception +use InvalidArgumentException; +class InvalidApplicationNameException extends InvalidArgumentException implements Exception { public const InvalidFormat = 2; } @@ -22373,14 +24986,16 @@ declare (strict_types=1); /* * This file is part of PharIo\Manifest. * - * (c) Arne Blankerts , Sebastian Heuer , Sebastian Bergmann + * Copyright (c) Arne Blankerts , Sebastian Heuer , Sebastian Bergmann and contributors * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. + * */ -namespace PHPUnit\PharIo\Manifest; +namespace PHPUnitPHAR\PharIo\Manifest; -class InvalidEmailException extends \InvalidArgumentException implements Exception +use InvalidArgumentException; +class InvalidEmailException extends InvalidArgumentException implements Exception { } , Sebastian Heuer , Sebastian Bergmann + * Copyright (c) Arne Blankerts , Sebastian Heuer , Sebastian Bergmann and contributors * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. + * */ -namespace PHPUnit\PharIo\Manifest; +namespace PHPUnitPHAR\PharIo\Manifest; -class InvalidUrlException extends \InvalidArgumentException implements Exception +use InvalidArgumentException; +class InvalidUrlException extends InvalidArgumentException implements Exception { } , Sebastian Heuer , Sebastian Bergmann and contributors + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ +namespace PHPUnitPHAR\PharIo\Manifest; -class ManifestDocumentException extends \RuntimeException implements Exception +use RuntimeException; +class ManifestDocumentException extends RuntimeException implements Exception { } , Sebastian Heuer , Sebastian Bergmann + * Copyright (c) Arne Blankerts , Sebastian Heuer , Sebastian Bergmann and contributors * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. + * */ -namespace PHPUnit\PharIo\Manifest; +namespace PHPUnitPHAR\PharIo\Manifest; use LibXMLError; +use function sprintf; class ManifestDocumentLoadingException extends \Exception implements Exception { /** @var LibXMLError[] */ @@ -22434,7 +25063,7 @@ class ManifestDocumentLoadingException extends \Exception implements Exception { $this->libxmlErrors = $libxmlErrors; $first = $this->libxmlErrors[0]; - parent::__construct(\sprintf('%s (Line: %d / Column: %d / File: %s)', $first->message, $first->line, $first->column, $first->file), $first->code); + parent::__construct(sprintf('%s (Line: %d / Column: %d / File: %s)', $first->message, $first->line, $first->column, $first->file), $first->code); } /** * @return LibXMLError[] @@ -22447,23 +25076,52 @@ class ManifestDocumentLoadingException extends \Exception implements Exception , Sebastian Heuer , Sebastian Bergmann and contributors + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ +namespace PHPUnitPHAR\PharIo\Manifest; -class ManifestDocumentMapperException extends \RuntimeException implements Exception +use RuntimeException; +class ManifestDocumentMapperException extends RuntimeException implements Exception { } , Sebastian Heuer , Sebastian Bergmann and contributors + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ +namespace PHPUnitPHAR\PharIo\Manifest; -class ManifestElementException extends \RuntimeException implements Exception +use RuntimeException; +class ManifestElementException extends RuntimeException implements Exception { } , Sebastian Heuer , Sebastian Bergmann and contributors + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ +namespace PHPUnitPHAR\PharIo\Manifest; class ManifestLoaderException extends \Exception implements Exception { @@ -22474,12 +25132,31 @@ declare (strict_types=1); /* * This file is part of PharIo\Manifest. * - * (c) Arne Blankerts , Sebastian Heuer , Sebastian Bergmann + * Copyright (c) Arne Blankerts , Sebastian Heuer , Sebastian Bergmann and contributors + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ +namespace PHPUnitPHAR\PharIo\Manifest; + +use InvalidArgumentException; +class NoEmailAddressException extends InvalidArgumentException implements Exception +{ +} +, Sebastian Heuer , Sebastian Bergmann and contributors * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. + * */ -namespace PHPUnit\PharIo\Manifest; +namespace PHPUnitPHAR\PharIo\Manifest; class Application extends Type { @@ -22494,13 +25171,16 @@ declare (strict_types=1); /* * This file is part of PharIo\Manifest. * - * (c) Arne Blankerts , Sebastian Heuer , Sebastian Bergmann + * Copyright (c) Arne Blankerts , Sebastian Heuer , Sebastian Bergmann and contributors * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. + * */ -namespace PHPUnit\PharIo\Manifest; +namespace PHPUnitPHAR\PharIo\Manifest; +use function preg_match; +use function sprintf; class ApplicationName { /** @var string */ @@ -22520,8 +25200,8 @@ class ApplicationName } private function ensureValidFormat(string $name) : void { - if (!\preg_match('#\\w/\\w#', $name)) { - throw new InvalidApplicationNameException(\sprintf('Format of name "%s" is not valid - expected: vendor/packagename', $name), InvalidApplicationNameException::InvalidFormat); + if (!preg_match('#\\w/\\w#', $name)) { + throw new InvalidApplicationNameException(sprintf('Format of name "%s" is not valid - expected: vendor/packagename', $name), InvalidApplicationNameException::InvalidFormat); } } } @@ -22531,34 +25211,49 @@ declare (strict_types=1); /* * This file is part of PharIo\Manifest. * - * (c) Arne Blankerts , Sebastian Heuer , Sebastian Bergmann + * Copyright (c) Arne Blankerts , Sebastian Heuer , Sebastian Bergmann and contributors * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. + * */ -namespace PHPUnit\PharIo\Manifest; +namespace PHPUnitPHAR\PharIo\Manifest; +use function sprintf; class Author { /** @var string */ private $name; - /** @var Email */ + /** @var null|Email */ private $email; - public function __construct(string $name, Email $email) + public function __construct(string $name, ?Email $email = null) { $this->name = $name; $this->email = $email; } public function asString() : string { - return \sprintf('%s <%s>', $this->name, $this->email->asString()); + if (!$this->hasEmail()) { + return $this->name; + } + return sprintf('%s <%s>', $this->name, $this->email->asString()); } public function getName() : string { return $this->name; } + /** + * @psalm-assert-if-true Email $this->email + */ + public function hasEmail() : bool + { + return $this->email !== null; + } public function getEmail() : Email { + if (!$this->hasEmail()) { + throw new NoEmailAddressException(); + } return $this->email; } } @@ -22568,14 +25263,19 @@ declare (strict_types=1); /* * This file is part of PharIo\Manifest. * - * (c) Arne Blankerts , Sebastian Heuer , Sebastian Bergmann + * Copyright (c) Arne Blankerts , Sebastian Heuer , Sebastian Bergmann and contributors * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. + * */ -namespace PHPUnit\PharIo\Manifest; +namespace PHPUnitPHAR\PharIo\Manifest; -class AuthorCollection implements \Countable, \IteratorAggregate +use Countable; +use IteratorAggregate; +use function count; +/** @template-implements IteratorAggregate */ +class AuthorCollection implements Countable, IteratorAggregate { /** @var Author[] */ private $authors = []; @@ -22592,7 +25292,7 @@ class AuthorCollection implements \Countable, \IteratorAggregate } public function count() : int { - return \count($this->authors); + return count($this->authors); } public function getIterator() : AuthorCollectionIterator { @@ -22605,14 +25305,18 @@ declare (strict_types=1); /* * This file is part of PharIo\Manifest. * - * (c) Arne Blankerts , Sebastian Heuer , Sebastian Bergmann + * Copyright (c) Arne Blankerts , Sebastian Heuer , Sebastian Bergmann and contributors * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. + * */ -namespace PHPUnit\PharIo\Manifest; +namespace PHPUnitPHAR\PharIo\Manifest; -class AuthorCollectionIterator implements \Iterator +use Iterator; +use function count; +/** @template-implements Iterator */ +class AuthorCollectionIterator implements Iterator { /** @var Author[] */ private $authors; @@ -22628,7 +25332,7 @@ class AuthorCollectionIterator implements \Iterator } public function valid() : bool { - return $this->position < \count($this->authors); + return $this->position < count($this->authors); } public function key() : int { @@ -22649,14 +25353,15 @@ declare (strict_types=1); /* * This file is part of PharIo\Manifest. * - * (c) Arne Blankerts , Sebastian Heuer , Sebastian Bergmann + * Copyright (c) Arne Blankerts , Sebastian Heuer , Sebastian Bergmann and contributors * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. + * */ -namespace PHPUnit\PharIo\Manifest; +namespace PHPUnitPHAR\PharIo\Manifest; -use PHPUnit\PharIo\Version\Version; +use PHPUnitPHAR\PharIo\Version\Version; class BundledComponent { /** @var string */ @@ -22683,14 +25388,19 @@ declare (strict_types=1); /* * This file is part of PharIo\Manifest. * - * (c) Arne Blankerts , Sebastian Heuer , Sebastian Bergmann + * Copyright (c) Arne Blankerts , Sebastian Heuer , Sebastian Bergmann and contributors * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. + * */ -namespace PHPUnit\PharIo\Manifest; +namespace PHPUnitPHAR\PharIo\Manifest; -class BundledComponentCollection implements \Countable, \IteratorAggregate +use Countable; +use IteratorAggregate; +use function count; +/** @template-implements IteratorAggregate */ +class BundledComponentCollection implements Countable, IteratorAggregate { /** @var BundledComponent[] */ private $bundledComponents = []; @@ -22707,7 +25417,7 @@ class BundledComponentCollection implements \Countable, \IteratorAggregate } public function count() : int { - return \count($this->bundledComponents); + return count($this->bundledComponents); } public function getIterator() : BundledComponentCollectionIterator { @@ -22720,14 +25430,18 @@ declare (strict_types=1); /* * This file is part of PharIo\Manifest. * - * (c) Arne Blankerts , Sebastian Heuer , Sebastian Bergmann + * Copyright (c) Arne Blankerts , Sebastian Heuer , Sebastian Bergmann and contributors * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. + * */ -namespace PHPUnit\PharIo\Manifest; +namespace PHPUnitPHAR\PharIo\Manifest; -class BundledComponentCollectionIterator implements \Iterator +use Iterator; +use function count; +/** @template-implements Iterator */ +class BundledComponentCollectionIterator implements Iterator { /** @var BundledComponent[] */ private $bundledComponents; @@ -22743,7 +25457,7 @@ class BundledComponentCollectionIterator implements \Iterator } public function valid() : bool { - return $this->position < \count($this->bundledComponents); + return $this->position < count($this->bundledComponents); } public function key() : int { @@ -22764,12 +25478,13 @@ declare (strict_types=1); /* * This file is part of PharIo\Manifest. * - * (c) Arne Blankerts , Sebastian Heuer , Sebastian Bergmann + * Copyright (c) Arne Blankerts , Sebastian Heuer , Sebastian Bergmann and contributors * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. + * */ -namespace PHPUnit\PharIo\Manifest; +namespace PHPUnitPHAR\PharIo\Manifest; class CopyrightInformation { @@ -22797,13 +25512,16 @@ declare (strict_types=1); /* * This file is part of PharIo\Manifest. * - * (c) Arne Blankerts , Sebastian Heuer , Sebastian Bergmann + * Copyright (c) Arne Blankerts , Sebastian Heuer , Sebastian Bergmann and contributors * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. + * */ -namespace PHPUnit\PharIo\Manifest; +namespace PHPUnitPHAR\PharIo\Manifest; +use const FILTER_VALIDATE_EMAIL; +use function filter_var; class Email { /** @var string */ @@ -22819,7 +25537,7 @@ class Email } private function ensureEmailIsValid(string $url) : void { - if (\filter_var($url, \FILTER_VALIDATE_EMAIL) === \false) { + if (filter_var($url, FILTER_VALIDATE_EMAIL) === \false) { throw new InvalidEmailException(); } } @@ -22830,15 +25548,16 @@ declare (strict_types=1); /* * This file is part of PharIo\Manifest. * - * (c) Arne Blankerts , Sebastian Heuer , Sebastian Bergmann + * Copyright (c) Arne Blankerts , Sebastian Heuer , Sebastian Bergmann and contributors * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. + * */ -namespace PHPUnit\PharIo\Manifest; +namespace PHPUnitPHAR\PharIo\Manifest; -use PHPUnit\PharIo\Version\Version; -use PHPUnit\PharIo\Version\VersionConstraint; +use PHPUnitPHAR\PharIo\Version\Version; +use PHPUnitPHAR\PharIo\Version\VersionConstraint; class Extension extends Type { /** @var ApplicationName */ @@ -22877,12 +25596,13 @@ declare (strict_types=1); /* * This file is part of PharIo\Manifest. * - * (c) Arne Blankerts , Sebastian Heuer , Sebastian Bergmann + * Copyright (c) Arne Blankerts , Sebastian Heuer , Sebastian Bergmann and contributors * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. + * */ -namespace PHPUnit\PharIo\Manifest; +namespace PHPUnitPHAR\PharIo\Manifest; class Library extends Type { @@ -22897,12 +25617,13 @@ declare (strict_types=1); /* * This file is part of PharIo\Manifest. * - * (c) Arne Blankerts , Sebastian Heuer , Sebastian Bergmann + * Copyright (c) Arne Blankerts , Sebastian Heuer , Sebastian Bergmann and contributors * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. + * */ -namespace PHPUnit\PharIo\Manifest; +namespace PHPUnitPHAR\PharIo\Manifest; class License { @@ -22930,14 +25651,15 @@ declare (strict_types=1); /* * This file is part of PharIo\Manifest. * - * (c) Arne Blankerts , Sebastian Heuer , Sebastian Bergmann + * Copyright (c) Arne Blankerts , Sebastian Heuer , Sebastian Bergmann and contributors * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. + * */ -namespace PHPUnit\PharIo\Manifest; +namespace PHPUnitPHAR\PharIo\Manifest; -use PHPUnit\PharIo\Version\Version; +use PHPUnitPHAR\PharIo\Version\Version; class Manifest { /** @var ApplicationName */ @@ -22997,7 +25719,7 @@ class Manifest { return $this->type->isExtension(); } - public function isExtensionFor(ApplicationName $application, Version $version = null) : bool + public function isExtensionFor(ApplicationName $application, ?Version $version = null) : bool { if (!$this->isExtension()) { return \false; @@ -23016,12 +25738,13 @@ declare (strict_types=1); /* * This file is part of PharIo\Manifest. * - * (c) Arne Blankerts , Sebastian Heuer , Sebastian Bergmann + * Copyright (c) Arne Blankerts , Sebastian Heuer , Sebastian Bergmann and contributors * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. + * */ -namespace PHPUnit\PharIo\Manifest; +namespace PHPUnitPHAR\PharIo\Manifest; class PhpExtensionRequirement implements Requirement { @@ -23042,14 +25765,15 @@ declare (strict_types=1); /* * This file is part of PharIo\Manifest. * - * (c) Arne Blankerts , Sebastian Heuer , Sebastian Bergmann + * Copyright (c) Arne Blankerts , Sebastian Heuer , Sebastian Bergmann and contributors * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. + * */ -namespace PHPUnit\PharIo\Manifest; +namespace PHPUnitPHAR\PharIo\Manifest; -use PHPUnit\PharIo\Version\VersionConstraint; +use PHPUnitPHAR\PharIo\Version\VersionConstraint; class PhpVersionRequirement implements Requirement { /** @var VersionConstraint */ @@ -23069,12 +25793,13 @@ declare (strict_types=1); /* * This file is part of PharIo\Manifest. * - * (c) Arne Blankerts , Sebastian Heuer , Sebastian Bergmann + * Copyright (c) Arne Blankerts , Sebastian Heuer , Sebastian Bergmann and contributors * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. + * */ -namespace PHPUnit\PharIo\Manifest; +namespace PHPUnitPHAR\PharIo\Manifest; interface Requirement { @@ -23085,14 +25810,19 @@ declare (strict_types=1); /* * This file is part of PharIo\Manifest. * - * (c) Arne Blankerts , Sebastian Heuer , Sebastian Bergmann + * Copyright (c) Arne Blankerts , Sebastian Heuer , Sebastian Bergmann and contributors * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. + * */ -namespace PHPUnit\PharIo\Manifest; +namespace PHPUnitPHAR\PharIo\Manifest; -class RequirementCollection implements \Countable, \IteratorAggregate +use Countable; +use IteratorAggregate; +use function count; +/** @template-implements IteratorAggregate */ +class RequirementCollection implements Countable, IteratorAggregate { /** @var Requirement[] */ private $requirements = []; @@ -23109,7 +25839,7 @@ class RequirementCollection implements \Countable, \IteratorAggregate } public function count() : int { - return \count($this->requirements); + return count($this->requirements); } public function getIterator() : RequirementCollectionIterator { @@ -23122,14 +25852,18 @@ declare (strict_types=1); /* * This file is part of PharIo\Manifest. * - * (c) Arne Blankerts , Sebastian Heuer , Sebastian Bergmann + * Copyright (c) Arne Blankerts , Sebastian Heuer , Sebastian Bergmann and contributors * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. + * */ -namespace PHPUnit\PharIo\Manifest; +namespace PHPUnitPHAR\PharIo\Manifest; -class RequirementCollectionIterator implements \Iterator +use Iterator; +use function count; +/** @template-implements Iterator */ +class RequirementCollectionIterator implements Iterator { /** @var Requirement[] */ private $requirements; @@ -23145,7 +25879,7 @@ class RequirementCollectionIterator implements \Iterator } public function valid() : bool { - return $this->position < \count($this->requirements); + return $this->position < count($this->requirements); } public function key() : int { @@ -23166,14 +25900,15 @@ declare (strict_types=1); /* * This file is part of PharIo\Manifest. * - * (c) Arne Blankerts , Sebastian Heuer , Sebastian Bergmann + * Copyright (c) Arne Blankerts , Sebastian Heuer , Sebastian Bergmann and contributors * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. + * */ -namespace PHPUnit\PharIo\Manifest; +namespace PHPUnitPHAR\PharIo\Manifest; -use PHPUnit\PharIo\Version\VersionConstraint; +use PHPUnitPHAR\PharIo\Version\VersionConstraint; abstract class Type { public static function application() : Application @@ -23210,13 +25945,16 @@ declare (strict_types=1); /* * This file is part of PharIo\Manifest. * - * (c) Arne Blankerts , Sebastian Heuer , Sebastian Bergmann + * Copyright (c) Arne Blankerts , Sebastian Heuer , Sebastian Bergmann and contributors * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. + * */ -namespace PHPUnit\PharIo\Manifest; +namespace PHPUnitPHAR\PharIo\Manifest; +use const FILTER_VALIDATE_URL; +use function filter_var; class Url { /** @var string */ @@ -23231,13 +25969,11 @@ class Url return $this->url; } /** - * @param string $url - * * @throws InvalidUrlException */ - private function ensureUrlIsValid($url) : void + private function ensureUrlIsValid(string $url) : void { - if (\filter_var($url, \FILTER_VALIDATE_URL) === \false) { + if (filter_var($url, FILTER_VALIDATE_URL) === \false) { throw new InvalidUrlException(); } } @@ -23248,12 +25984,13 @@ declare (strict_types=1); /* * This file is part of PharIo\Manifest. * - * (c) Arne Blankerts , Sebastian Heuer , Sebastian Bergmann + * Copyright (c) Arne Blankerts , Sebastian Heuer , Sebastian Bergmann and contributors * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. + * */ -namespace PHPUnit\PharIo\Manifest; +namespace PHPUnitPHAR\PharIo\Manifest; class AuthorElement extends ManifestElement { @@ -23265,6 +26002,10 @@ class AuthorElement extends ManifestElement { return $this->getAttributeValue('email'); } + public function hasEMail() : bool + { + return $this->hasAttribute('email'); + } } , Sebastian Heuer , Sebastian Bergmann + * Copyright (c) Arne Blankerts , Sebastian Heuer , Sebastian Bergmann and contributors * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. + * */ -namespace PHPUnit\PharIo\Manifest; +namespace PHPUnitPHAR\PharIo\Manifest; class AuthorElementCollection extends ElementCollection { @@ -23292,12 +26034,13 @@ declare (strict_types=1); /* * This file is part of PharIo\Manifest. * - * (c) Arne Blankerts , Sebastian Heuer , Sebastian Bergmann + * Copyright (c) Arne Blankerts , Sebastian Heuer , Sebastian Bergmann and contributors * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. + * */ -namespace PHPUnit\PharIo\Manifest; +namespace PHPUnitPHAR\PharIo\Manifest; class BundlesElement extends ManifestElement { @@ -23312,12 +26055,13 @@ declare (strict_types=1); /* * This file is part of PharIo\Manifest. * - * (c) Arne Blankerts , Sebastian Heuer , Sebastian Bergmann + * Copyright (c) Arne Blankerts , Sebastian Heuer , Sebastian Bergmann and contributors * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. + * */ -namespace PHPUnit\PharIo\Manifest; +namespace PHPUnitPHAR\PharIo\Manifest; class ComponentElement extends ManifestElement { @@ -23336,12 +26080,13 @@ declare (strict_types=1); /* * This file is part of PharIo\Manifest. * - * (c) Arne Blankerts , Sebastian Heuer , Sebastian Bergmann + * Copyright (c) Arne Blankerts , Sebastian Heuer , Sebastian Bergmann and contributors * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. + * */ -namespace PHPUnit\PharIo\Manifest; +namespace PHPUnitPHAR\PharIo\Manifest; class ComponentElementCollection extends ElementCollection { @@ -23356,12 +26101,13 @@ declare (strict_types=1); /* * This file is part of PharIo\Manifest. * - * (c) Arne Blankerts , Sebastian Heuer , Sebastian Bergmann + * Copyright (c) Arne Blankerts , Sebastian Heuer , Sebastian Bergmann and contributors * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. + * */ -namespace PHPUnit\PharIo\Manifest; +namespace PHPUnitPHAR\PharIo\Manifest; class ContainsElement extends ManifestElement { @@ -23388,12 +26134,13 @@ declare (strict_types=1); /* * This file is part of PharIo\Manifest. * - * (c) Arne Blankerts , Sebastian Heuer , Sebastian Bergmann + * Copyright (c) Arne Blankerts , Sebastian Heuer , Sebastian Bergmann and contributors * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. + * */ -namespace PHPUnit\PharIo\Manifest; +namespace PHPUnitPHAR\PharIo\Manifest; class CopyrightElement extends ManifestElement { @@ -23412,16 +26159,23 @@ declare (strict_types=1); /* * This file is part of PharIo\Manifest. * - * (c) Arne Blankerts , Sebastian Heuer , Sebastian Bergmann + * Copyright (c) Arne Blankerts , Sebastian Heuer , Sebastian Bergmann and contributors * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. + * */ -namespace PHPUnit\PharIo\Manifest; +namespace PHPUnitPHAR\PharIo\Manifest; use DOMElement; use DOMNodeList; -abstract class ElementCollection implements \Iterator +use Iterator; +use ReturnTypeWillChange; +use function count; +use function get_class; +use function sprintf; +/** @template-implements Iterator */ +abstract class ElementCollection implements Iterator { /** @var DOMElement[] */ private $nodes = []; @@ -23432,7 +26186,7 @@ abstract class ElementCollection implements \Iterator $this->position = 0; $this->importNodes($nodeList); } - #[\ReturnTypeWillChange] + #[ReturnTypeWillChange] public abstract function current(); public function next() : void { @@ -23444,7 +26198,7 @@ abstract class ElementCollection implements \Iterator } public function valid() : bool { - return $this->position < \count($this->nodes); + return $this->position < count($this->nodes); } public function rewind() : void { @@ -23458,7 +26212,7 @@ abstract class ElementCollection implements \Iterator { foreach ($nodeList as $node) { if (!$node instanceof DOMElement) { - throw new ElementCollectionException(\sprintf('\\DOMElement expected, got \\%s', \get_class($node))); + throw new ElementCollectionException(sprintf('\\DOMElement expected, got \\%s', get_class($node))); } $this->nodes[] = $node; } @@ -23470,12 +26224,13 @@ declare (strict_types=1); /* * This file is part of PharIo\Manifest. * - * (c) Arne Blankerts , Sebastian Heuer , Sebastian Bergmann + * Copyright (c) Arne Blankerts , Sebastian Heuer , Sebastian Bergmann and contributors * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. + * */ -namespace PHPUnit\PharIo\Manifest; +namespace PHPUnitPHAR\PharIo\Manifest; class ExtElement extends ManifestElement { @@ -23490,12 +26245,13 @@ declare (strict_types=1); /* * This file is part of PharIo\Manifest. * - * (c) Arne Blankerts , Sebastian Heuer , Sebastian Bergmann + * Copyright (c) Arne Blankerts , Sebastian Heuer , Sebastian Bergmann and contributors * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. + * */ -namespace PHPUnit\PharIo\Manifest; +namespace PHPUnitPHAR\PharIo\Manifest; class ExtElementCollection extends ElementCollection { @@ -23510,12 +26266,13 @@ declare (strict_types=1); /* * This file is part of PharIo\Manifest. * - * (c) Arne Blankerts , Sebastian Heuer , Sebastian Bergmann + * Copyright (c) Arne Blankerts , Sebastian Heuer , Sebastian Bergmann and contributors * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. + * */ -namespace PHPUnit\PharIo\Manifest; +namespace PHPUnitPHAR\PharIo\Manifest; class ExtensionElement extends ManifestElement { @@ -23534,12 +26291,13 @@ declare (strict_types=1); /* * This file is part of PharIo\Manifest. * - * (c) Arne Blankerts , Sebastian Heuer , Sebastian Bergmann + * Copyright (c) Arne Blankerts , Sebastian Heuer , Sebastian Bergmann and contributors * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. + * */ -namespace PHPUnit\PharIo\Manifest; +namespace PHPUnitPHAR\PharIo\Manifest; class LicenseElement extends ManifestElement { @@ -23558,15 +26316,24 @@ declare (strict_types=1); /* * This file is part of PharIo\Manifest. * - * (c) Arne Blankerts , Sebastian Heuer , Sebastian Bergmann + * Copyright (c) Arne Blankerts , Sebastian Heuer , Sebastian Bergmann and contributors * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. + * */ -namespace PHPUnit\PharIo\Manifest; +namespace PHPUnitPHAR\PharIo\Manifest; use DOMDocument; use DOMElement; +use Throwable; +use function count; +use function file_get_contents; +use function is_file; +use function libxml_clear_errors; +use function libxml_get_errors; +use function libxml_use_internal_errors; +use function sprintf; class ManifestDocument { public const XMLNS = 'https://phar.io/xml/manifest/1.0'; @@ -23574,20 +26341,24 @@ class ManifestDocument private $dom; public static function fromFile(string $filename) : ManifestDocument { - if (!\file_exists($filename)) { - throw new ManifestDocumentException(\sprintf('File "%s" not found', $filename)); + if (!is_file($filename)) { + throw new ManifestDocumentException(sprintf('File "%s" not found', $filename)); } - return self::fromString(\file_get_contents($filename)); + return self::fromString(file_get_contents($filename)); } public static function fromString(string $xmlString) : ManifestDocument { - $prev = \libxml_use_internal_errors(\true); - \libxml_clear_errors(); - $dom = new DOMDocument(); - $dom->loadXML($xmlString); - $errors = \libxml_get_errors(); - \libxml_use_internal_errors($prev); - if (\count($errors) !== 0) { + $prev = libxml_use_internal_errors(\true); + libxml_clear_errors(); + try { + $dom = new DOMDocument(); + $dom->loadXML($xmlString); + $errors = libxml_get_errors(); + libxml_use_internal_errors($prev); + } catch (Throwable $t) { + throw new ManifestDocumentException($t->getMessage(), 0, $t); + } + if (count($errors) !== 0) { throw new ManifestDocumentLoadingException($errors); } return new self($dom); @@ -23628,7 +26399,7 @@ class ManifestDocument { $element = $this->dom->getElementsByTagNameNS(self::XMLNS, $elementName)->item(0); if (!$element instanceof DOMElement) { - throw new ManifestDocumentException(\sprintf('Element %s missing', $elementName)); + throw new ManifestDocumentException(sprintf('Element %s missing', $elementName)); } return $element; } @@ -23639,15 +26410,17 @@ declare (strict_types=1); /* * This file is part of PharIo\Manifest. * - * (c) Arne Blankerts , Sebastian Heuer , Sebastian Bergmann + * Copyright (c) Arne Blankerts , Sebastian Heuer , Sebastian Bergmann and contributors * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. + * */ -namespace PHPUnit\PharIo\Manifest; +namespace PHPUnitPHAR\PharIo\Manifest; use DOMElement; use DOMNodeList; +use function sprintf; class ManifestElement { public const XMLNS = 'https://phar.io/xml/manifest/1.0'; @@ -23660,15 +26433,19 @@ class ManifestElement protected function getAttributeValue(string $name) : string { if (!$this->element->hasAttribute($name)) { - throw new ManifestElementException(\sprintf('Attribute %s not set on element %s', $name, $this->element->localName)); + throw new ManifestElementException(sprintf('Attribute %s not set on element %s', $name, $this->element->localName)); } return $this->element->getAttribute($name); } + protected function hasAttribute(string $name) : bool + { + return $this->element->hasAttribute($name); + } protected function getChildByName(string $elementName) : DOMElement { $element = $this->element->getElementsByTagNameNS(self::XMLNS, $elementName)->item(0); if (!$element instanceof DOMElement) { - throw new ManifestElementException(\sprintf('Element %s missing', $elementName)); + throw new ManifestElementException(sprintf('Element %s missing', $elementName)); } return $element; } @@ -23676,7 +26453,7 @@ class ManifestElement { $elementList = $this->element->getElementsByTagNameNS(self::XMLNS, $elementName); if ($elementList->length === 0) { - throw new ManifestElementException(\sprintf('Element(s) %s missing', $elementName)); + throw new ManifestElementException(sprintf('Element(s) %s missing', $elementName)); } return $elementList; } @@ -23691,12 +26468,13 @@ declare (strict_types=1); /* * This file is part of PharIo\Manifest. * - * (c) Arne Blankerts , Sebastian Heuer , Sebastian Bergmann + * Copyright (c) Arne Blankerts , Sebastian Heuer , Sebastian Bergmann and contributors * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. + * */ -namespace PHPUnit\PharIo\Manifest; +namespace PHPUnitPHAR\PharIo\Manifest; class PhpElement extends ManifestElement { @@ -23719,12 +26497,13 @@ declare (strict_types=1); /* * This file is part of PharIo\Manifest. * - * (c) Arne Blankerts , Sebastian Heuer , Sebastian Bergmann + * Copyright (c) Arne Blankerts , Sebastian Heuer , Sebastian Bergmann and contributors * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. + * */ -namespace PHPUnit\PharIo\Manifest; +namespace PHPUnitPHAR\PharIo\Manifest; class RequiresElement extends ManifestElement { @@ -23744,7 +26523,7 @@ declare (strict_types=1); * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\PharIo\Version; +namespace PHPUnitPHAR\PharIo\Version; class BuildMetaData { @@ -23795,7 +26574,7 @@ POSSIBILITY OF SUCH DAMAGE. throw statements + * as Stmt\Throw_ objects + */ if ($node instanceof Node\Stmt\Throw_) { $this->setLineBranch($node->expr->getEndLine(), $node->expr->getEndLine(), ++$this->nextBranch); return; } + /* + * nikic/php-parser ^5 represents throw statements + * as Stmt\Expression objects that contain an + * Expr\Throw_ object + */ + if ($node instanceof Node\Stmt\Expression && $node->expr instanceof Node\Expr\Throw_) { + $this->setLineBranch($node->expr->expr->getEndLine(), $node->expr->expr->getEndLine(), ++$this->nextBranch); + return; + } if ($node instanceof Node\Stmt\Enum_ || $node instanceof Node\Stmt\Function_ || $node instanceof Node\Stmt\Class_ || $node instanceof Node\Stmt\ClassMethod || $node instanceof Node\Expr\Closure || $node instanceof Node\Stmt\Trait_) { $isConcreteClassLike = $node instanceof Node\Stmt\Enum_ || $node instanceof Node\Stmt\Class_ || $node instanceof Node\Stmt\Trait_; if (null !== $node->stmts) { @@ -32687,7 +35479,7 @@ declare (strict_types=1); * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\CodeCoverage\StaticAnalysis; +namespace PHPUnitPHAR\SebastianBergmann\CodeCoverage\StaticAnalysis; /** * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage @@ -32715,20 +35507,20 @@ declare (strict_types=1); * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\CodeCoverage\StaticAnalysis; +namespace PHPUnitPHAR\SebastianBergmann\CodeCoverage\StaticAnalysis; use function array_merge; use function assert; use function range; use function strpos; -use PHPUnit\PhpParser\Node; -use PHPUnit\PhpParser\Node\Attribute; -use PHPUnit\PhpParser\Node\Stmt\Class_; -use PHPUnit\PhpParser\Node\Stmt\ClassMethod; -use PHPUnit\PhpParser\Node\Stmt\Function_; -use PHPUnit\PhpParser\Node\Stmt\Interface_; -use PHPUnit\PhpParser\Node\Stmt\Trait_; -use PHPUnit\PhpParser\NodeVisitorAbstract; +use PHPUnitPHAR\PhpParser\Node; +use PHPUnitPHAR\PhpParser\Node\Attribute; +use PHPUnitPHAR\PhpParser\Node\Stmt\Class_; +use PHPUnitPHAR\PhpParser\Node\Stmt\ClassMethod; +use PHPUnitPHAR\PhpParser\Node\Stmt\Function_; +use PHPUnitPHAR\PhpParser\Node\Stmt\Interface_; +use PHPUnitPHAR\PhpParser\Node\Stmt\Trait_; +use PHPUnitPHAR\PhpParser\NodeVisitorAbstract; /** * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage */ @@ -32805,7 +35597,7 @@ declare (strict_types=1); * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\CodeCoverage\StaticAnalysis; +namespace PHPUnitPHAR\SebastianBergmann\CodeCoverage\StaticAnalysis; use function array_merge; use function array_unique; @@ -32819,14 +35611,13 @@ use function sprintf; use function substr_count; use function token_get_all; use function trim; -use PHPUnit\PhpParser\Error; -use PHPUnit\PhpParser\Lexer; -use PHPUnit\PhpParser\NodeTraverser; -use PHPUnit\PhpParser\NodeVisitor\NameResolver; -use PHPUnit\PhpParser\NodeVisitor\ParentConnectingVisitor; -use PHPUnit\PhpParser\ParserFactory; -use PHPUnit\SebastianBergmann\CodeCoverage\ParserException; -use PHPUnit\SebastianBergmann\LinesOfCode\LineCountingVisitor; +use PHPUnitPHAR\PhpParser\Error; +use PHPUnitPHAR\PhpParser\NodeTraverser; +use PHPUnitPHAR\PhpParser\NodeVisitor\NameResolver; +use PHPUnitPHAR\PhpParser\NodeVisitor\ParentConnectingVisitor; +use PHPUnitPHAR\PhpParser\ParserFactory; +use PHPUnitPHAR\SebastianBergmann\CodeCoverage\ParserException; +use PHPUnitPHAR\SebastianBergmann\LinesOfCode\LineCountingVisitor; /** * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage */ @@ -32915,7 +35706,7 @@ final class ParsingFileAnalyser implements FileAnalyser if ($linesOfCode === 0 && !empty($source)) { $linesOfCode = 1; } - $parser = (new ParserFactory())->create(ParserFactory::PREFER_PHP7, new Lexer()); + $parser = (new ParserFactory())->createForHostVersion(); try { $nodes = $parser->parse($source); assert($nodes !== null); @@ -32987,7 +35778,7 @@ declare (strict_types=1); * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\CodeCoverage\Util; +namespace PHPUnitPHAR\SebastianBergmann\CodeCoverage\Util; use function is_dir; use function mkdir; @@ -33019,7 +35810,7 @@ declare (strict_types=1); * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\CodeCoverage\Util; +namespace PHPUnitPHAR\SebastianBergmann\CodeCoverage\Util; use function sprintf; /** @@ -33077,10 +35868,10 @@ declare (strict_types=1); * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\CodeCoverage; +namespace PHPUnitPHAR\SebastianBergmann\CodeCoverage; use function dirname; -use PHPUnit\SebastianBergmann\Version as VersionId; +use PHPUnitPHAR\SebastianBergmann\Version as VersionId; final class Version { /** @@ -33090,7 +35881,7 @@ final class Version public static function id() : string { if (self::$version === null) { - self::$version = (new VersionId('9.2.29', dirname(__DIR__)))->getVersion(); + self::$version = (new VersionId('9.2.31', dirname(__DIR__)))->getVersion(); } return self::$version; } @@ -33106,7 +35897,7 @@ declare (strict_types=1); * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\FileIterator; +namespace PHPUnitPHAR\SebastianBergmann\FileIterator; use const DIRECTORY_SEPARATOR; use function array_unique; @@ -33198,7 +35989,7 @@ declare (strict_types=1); * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\FileIterator; +namespace PHPUnitPHAR\SebastianBergmann\FileIterator; use const GLOB_ONLYDIR; use function array_filter; @@ -33271,7 +36062,7 @@ declare (strict_types=1); * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\FileIterator; +namespace PHPUnitPHAR\SebastianBergmann\FileIterator; use function array_filter; use function array_map; @@ -33400,7 +36191,7 @@ declare (strict_types=1); * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\Invoker; +namespace PHPUnitPHAR\SebastianBergmann\Invoker; use const SIGALRM; use function call_user_func_array; @@ -33452,7 +36243,7 @@ declare (strict_types=1); * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\Invoker; +namespace PHPUnitPHAR\SebastianBergmann\Invoker; use Throwable; interface Exception extends Throwable @@ -33469,7 +36260,7 @@ declare (strict_types=1); * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\Invoker; +namespace PHPUnitPHAR\SebastianBergmann\Invoker; use RuntimeException; final class ProcessControlExtensionNotLoadedException extends RuntimeException implements Exception @@ -33486,7 +36277,7 @@ declare (strict_types=1); * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\Invoker; +namespace PHPUnitPHAR\SebastianBergmann\Invoker; use RuntimeException; final class TimeoutException extends RuntimeException implements Exception @@ -33536,7 +36327,7 @@ declare (strict_types=1); * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\Template; +namespace PHPUnitPHAR\SebastianBergmann\Template; use function array_merge; use function file_exists; @@ -33622,7 +36413,7 @@ declare (strict_types=1); * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\Template; +namespace PHPUnitPHAR\SebastianBergmann\Template; use Throwable; interface Exception extends Throwable @@ -33639,7 +36430,7 @@ declare (strict_types=1); * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\Template; +namespace PHPUnitPHAR\SebastianBergmann\Template; final class InvalidArgumentException extends \InvalidArgumentException implements Exception { @@ -33655,7 +36446,7 @@ declare (strict_types=1); * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\Template; +namespace PHPUnitPHAR\SebastianBergmann\Template; use InvalidArgumentException; final class RuntimeException extends InvalidArgumentException implements Exception @@ -33672,7 +36463,7 @@ declare (strict_types=1); * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\Timer; +namespace PHPUnitPHAR\SebastianBergmann\Timer; use function floor; use function sprintf; @@ -33799,7 +36590,7 @@ declare (strict_types=1); * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\Timer; +namespace PHPUnitPHAR\SebastianBergmann\Timer; use function is_float; use function memory_get_peak_usage; @@ -33851,7 +36642,7 @@ declare (strict_types=1); * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\Timer; +namespace PHPUnitPHAR\SebastianBergmann\Timer; use function array_pop; use function hrtime; @@ -33887,7 +36678,7 @@ declare (strict_types=1); * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\Timer; +namespace PHPUnitPHAR\SebastianBergmann\Timer; use Throwable; interface Exception extends Throwable @@ -33904,7 +36695,7 @@ declare (strict_types=1); * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\Timer; +namespace PHPUnitPHAR\SebastianBergmann\Timer; use LogicException; final class NoActiveTimerException extends LogicException implements Exception @@ -33921,7 +36712,7 @@ declare (strict_types=1); * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\Timer; +namespace PHPUnitPHAR\SebastianBergmann\Timer; use RuntimeException; final class TimeSinceStartOfRequestNotAvailableException extends RuntimeException implements Exception @@ -33938,7 +36729,7 @@ declare (strict_types=1); * * @link http://phpdoc.org */ -namespace PHPUnit\phpDocumentor\Reflection; +namespace PHPUnitPHAR\phpDocumentor\Reflection; /** * Interface for Api Elements @@ -33965,7 +36756,7 @@ declare (strict_types=1); * * @link http://phpdoc.org */ -namespace PHPUnit\phpDocumentor\Reflection; +namespace PHPUnitPHAR\phpDocumentor\Reflection; /** * Interface for files processed by the ProjectFactory @@ -33996,7 +36787,7 @@ declare (strict_types=1); * * @link http://phpdoc.org */ -namespace PHPUnit\phpDocumentor\Reflection; +namespace PHPUnitPHAR\phpDocumentor\Reflection; use InvalidArgumentException; use function assert; @@ -34094,7 +36885,7 @@ declare (strict_types=1); * * @link http://phpdoc.org */ -namespace PHPUnit\phpDocumentor\Reflection; +namespace PHPUnitPHAR\phpDocumentor\Reflection; /** * The location where an element occurs within a file. @@ -34141,7 +36932,7 @@ declare (strict_types=1); * * @link http://phpdoc.org */ -namespace PHPUnit\phpDocumentor\Reflection; +namespace PHPUnitPHAR\phpDocumentor\Reflection; /** * Interface for project. Since the definition of a project can be different per factory this interface will be small. @@ -34164,7 +36955,7 @@ declare (strict_types=1); * * @link http://phpdoc.org */ -namespace PHPUnit\phpDocumentor\Reflection; +namespace PHPUnitPHAR\phpDocumentor\Reflection; /** * Interface for project factories. A project factory shall convert a set of files @@ -34190,11 +36981,11 @@ declare (strict_types=1); * * @link http://phpdoc.org */ -namespace PHPUnit\phpDocumentor\Reflection; +namespace PHPUnitPHAR\phpDocumentor\Reflection; -use PHPUnit\phpDocumentor\Reflection\DocBlock\Tag; -use PHPUnit\phpDocumentor\Reflection\DocBlock\Tags\TagWithType; -use PHPUnit\Webmozart\Assert\Assert; +use PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\Tag; +use PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\Tags\TagWithType; +use PHPUnitPHAR\Webmozart\Assert\Assert; final class DocBlock { /** @var string The opening line for this docblock. */ @@ -34379,10 +37170,10 @@ declare (strict_types=1); * * @link http://phpdoc.org */ -namespace PHPUnit\phpDocumentor\Reflection\DocBlock; +namespace PHPUnitPHAR\phpDocumentor\Reflection\DocBlock; -use PHPUnit\phpDocumentor\Reflection\DocBlock\Tags\Formatter; -use PHPUnit\phpDocumentor\Reflection\DocBlock\Tags\Formatter\PassthroughFormatter; +use PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\Tags\Formatter; +use PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\Tags\Formatter\PassthroughFormatter; use function vsprintf; /** * Object representing to description for a DocBlock. @@ -34482,10 +37273,10 @@ declare (strict_types=1); * * @link http://phpdoc.org */ -namespace PHPUnit\phpDocumentor\Reflection\DocBlock; +namespace PHPUnitPHAR\phpDocumentor\Reflection\DocBlock; -use PHPUnit\phpDocumentor\Reflection\Types\Context as TypeContext; -use PHPUnit\phpDocumentor\Reflection\Utils; +use PHPUnitPHAR\phpDocumentor\Reflection\Types\Context as TypeContext; +use PHPUnitPHAR\phpDocumentor\Reflection\Utils; use function count; use function implode; use function ltrim; @@ -34636,9 +37427,9 @@ declare (strict_types=1); * * @link http://phpdoc.org */ -namespace PHPUnit\phpDocumentor\Reflection\DocBlock; +namespace PHPUnitPHAR\phpDocumentor\Reflection\DocBlock; -use PHPUnit\phpDocumentor\Reflection\DocBlock\Tags\Example; +use PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\Tags\Example; use function array_slice; use function file; use function getcwd; @@ -34769,11 +37560,11 @@ declare (strict_types=1); * * @link http://phpdoc.org */ -namespace PHPUnit\phpDocumentor\Reflection\DocBlock; +namespace PHPUnitPHAR\phpDocumentor\Reflection\DocBlock; -use PHPUnit\phpDocumentor\Reflection\DocBlock; -use PHPUnit\phpDocumentor\Reflection\DocBlock\Tags\Formatter; -use PHPUnit\phpDocumentor\Reflection\DocBlock\Tags\Formatter\PassthroughFormatter; +use PHPUnitPHAR\phpDocumentor\Reflection\DocBlock; +use PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\Tags\Formatter; +use PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\Tags\Formatter\PassthroughFormatter; use function sprintf; use function str_repeat; use function str_replace; @@ -34878,34 +37669,34 @@ declare (strict_types=1); * * @link http://phpdoc.org */ -namespace PHPUnit\phpDocumentor\Reflection\DocBlock; +namespace PHPUnitPHAR\phpDocumentor\Reflection\DocBlock; use InvalidArgumentException; -use PHPUnit\phpDocumentor\Reflection\DocBlock\Tags\Author; -use PHPUnit\phpDocumentor\Reflection\DocBlock\Tags\Covers; -use PHPUnit\phpDocumentor\Reflection\DocBlock\Tags\Deprecated; -use PHPUnit\phpDocumentor\Reflection\DocBlock\Tags\Generic; -use PHPUnit\phpDocumentor\Reflection\DocBlock\Tags\InvalidTag; -use PHPUnit\phpDocumentor\Reflection\DocBlock\Tags\Link as LinkTag; -use PHPUnit\phpDocumentor\Reflection\DocBlock\Tags\Method; -use PHPUnit\phpDocumentor\Reflection\DocBlock\Tags\Param; -use PHPUnit\phpDocumentor\Reflection\DocBlock\Tags\Property; -use PHPUnit\phpDocumentor\Reflection\DocBlock\Tags\PropertyRead; -use PHPUnit\phpDocumentor\Reflection\DocBlock\Tags\PropertyWrite; -use PHPUnit\phpDocumentor\Reflection\DocBlock\Tags\Return_; -use PHPUnit\phpDocumentor\Reflection\DocBlock\Tags\See as SeeTag; -use PHPUnit\phpDocumentor\Reflection\DocBlock\Tags\Since; -use PHPUnit\phpDocumentor\Reflection\DocBlock\Tags\Source; -use PHPUnit\phpDocumentor\Reflection\DocBlock\Tags\Throws; -use PHPUnit\phpDocumentor\Reflection\DocBlock\Tags\Uses; -use PHPUnit\phpDocumentor\Reflection\DocBlock\Tags\Var_; -use PHPUnit\phpDocumentor\Reflection\DocBlock\Tags\Version; -use PHPUnit\phpDocumentor\Reflection\FqsenResolver; -use PHPUnit\phpDocumentor\Reflection\Types\Context as TypeContext; +use PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\Tags\Author; +use PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\Tags\Covers; +use PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\Tags\Deprecated; +use PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\Tags\Generic; +use PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\Tags\InvalidTag; +use PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\Tags\Link as LinkTag; +use PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\Tags\Method; +use PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\Tags\Param; +use PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\Tags\Property; +use PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\Tags\PropertyRead; +use PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\Tags\PropertyWrite; +use PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\Tags\Return_; +use PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\Tags\See as SeeTag; +use PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\Tags\Since; +use PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\Tags\Source; +use PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\Tags\Throws; +use PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\Tags\Uses; +use PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\Tags\Var_; +use PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\Tags\Version; +use PHPUnitPHAR\phpDocumentor\Reflection\FqsenResolver; +use PHPUnitPHAR\phpDocumentor\Reflection\Types\Context as TypeContext; use ReflectionMethod; use ReflectionNamedType; use ReflectionParameter; -use PHPUnit\Webmozart\Assert\Assert; +use PHPUnitPHAR\Webmozart\Assert\Assert; use function array_merge; use function array_slice; use function call_user_func_array; @@ -35172,9 +37963,9 @@ declare (strict_types=1); * * @link http://phpdoc.org */ -namespace PHPUnit\phpDocumentor\Reflection\DocBlock; +namespace PHPUnitPHAR\phpDocumentor\Reflection\DocBlock; -use PHPUnit\phpDocumentor\Reflection\DocBlock\Tags\Formatter; +use PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\Tags\Formatter; interface Tag { public function getName() : string; @@ -35197,10 +37988,10 @@ declare (strict_types=1); * * @link http://phpdoc.org */ -namespace PHPUnit\phpDocumentor\Reflection\DocBlock; +namespace PHPUnitPHAR\phpDocumentor\Reflection\DocBlock; use InvalidArgumentException; -use PHPUnit\phpDocumentor\Reflection\Types\Context as TypeContext; +use PHPUnitPHAR\phpDocumentor\Reflection\Types\Context as TypeContext; interface TagFactory { /** @@ -35275,7 +38066,7 @@ declare (strict_types=1); * * @link http://phpdoc.org */ -namespace PHPUnit\phpDocumentor\Reflection\DocBlock\Tags; +namespace PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\Tags; use InvalidArgumentException; use function filter_var; @@ -35360,10 +38151,10 @@ declare (strict_types=1); * * @link http://phpdoc.org */ -namespace PHPUnit\phpDocumentor\Reflection\DocBlock\Tags; +namespace PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\Tags; -use PHPUnit\phpDocumentor\Reflection\DocBlock; -use PHPUnit\phpDocumentor\Reflection\DocBlock\Description; +use PHPUnitPHAR\phpDocumentor\Reflection\DocBlock; +use PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\Description; /** * Parses a tag definition for a DocBlock. */ @@ -35405,15 +38196,15 @@ declare (strict_types=1); * * @link http://phpdoc.org */ -namespace PHPUnit\phpDocumentor\Reflection\DocBlock\Tags; +namespace PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\Tags; -use PHPUnit\phpDocumentor\Reflection\DocBlock\Description; -use PHPUnit\phpDocumentor\Reflection\DocBlock\DescriptionFactory; -use PHPUnit\phpDocumentor\Reflection\Fqsen; -use PHPUnit\phpDocumentor\Reflection\FqsenResolver; -use PHPUnit\phpDocumentor\Reflection\Types\Context as TypeContext; -use PHPUnit\phpDocumentor\Reflection\Utils; -use PHPUnit\Webmozart\Assert\Assert; +use PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\Description; +use PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\DescriptionFactory; +use PHPUnitPHAR\phpDocumentor\Reflection\Fqsen; +use PHPUnitPHAR\phpDocumentor\Reflection\FqsenResolver; +use PHPUnitPHAR\phpDocumentor\Reflection\Types\Context as TypeContext; +use PHPUnitPHAR\phpDocumentor\Reflection\Utils; +use PHPUnitPHAR\Webmozart\Assert\Assert; use function array_key_exists; use function explode; /** @@ -35483,12 +38274,12 @@ declare (strict_types=1); * * @link http://phpdoc.org */ -namespace PHPUnit\phpDocumentor\Reflection\DocBlock\Tags; +namespace PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\Tags; -use PHPUnit\phpDocumentor\Reflection\DocBlock\Description; -use PHPUnit\phpDocumentor\Reflection\DocBlock\DescriptionFactory; -use PHPUnit\phpDocumentor\Reflection\Types\Context as TypeContext; -use PHPUnit\Webmozart\Assert\Assert; +use PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\Description; +use PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\DescriptionFactory; +use PHPUnitPHAR\phpDocumentor\Reflection\Types\Context as TypeContext; +use PHPUnitPHAR\Webmozart\Assert\Assert; use function preg_match; /** * Reflection class for a {@}deprecated tag in a Docblock. @@ -35567,10 +38358,10 @@ declare (strict_types=1); * * @link http://phpdoc.org */ -namespace PHPUnit\phpDocumentor\Reflection\DocBlock\Tags; +namespace PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\Tags; -use PHPUnit\phpDocumentor\Reflection\DocBlock\Tag; -use PHPUnit\Webmozart\Assert\Assert; +use PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\Tag; +use PHPUnitPHAR\Webmozart\Assert\Assert; use function array_key_exists; use function preg_match; use function rawurlencode; @@ -35714,7 +38505,7 @@ declare (strict_types=1); * * @link http://phpdoc.org */ -namespace PHPUnit\phpDocumentor\Reflection\DocBlock\Tags\Factory; +namespace PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\Tags\Factory; /** * @deprecated This contract is totally covered by Tag contract. Every class using StaticMethod also use Tag @@ -35737,9 +38528,9 @@ declare (strict_types=1); * * @link http://phpdoc.org */ -namespace PHPUnit\phpDocumentor\Reflection\DocBlock\Tags; +namespace PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\Tags; -use PHPUnit\phpDocumentor\Reflection\DocBlock\Tag; +use PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\Tag; interface Formatter { /** @@ -35758,10 +38549,10 @@ declare (strict_types=1); * * @link http://phpdoc.org */ -namespace PHPUnit\phpDocumentor\Reflection\DocBlock\Tags\Formatter; +namespace PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\Tags\Formatter; -use PHPUnit\phpDocumentor\Reflection\DocBlock\Tag; -use PHPUnit\phpDocumentor\Reflection\DocBlock\Tags\Formatter; +use PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\Tag; +use PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\Tags\Formatter; use function max; use function str_repeat; use function strlen; @@ -35797,10 +38588,10 @@ declare (strict_types=1); * * @link http://phpdoc.org */ -namespace PHPUnit\phpDocumentor\Reflection\DocBlock\Tags\Formatter; +namespace PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\Tags\Formatter; -use PHPUnit\phpDocumentor\Reflection\DocBlock\Tag; -use PHPUnit\phpDocumentor\Reflection\DocBlock\Tags\Formatter; +use PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\Tag; +use PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\Tags\Formatter; use function trim; class PassthroughFormatter implements Formatter { @@ -35823,14 +38614,14 @@ declare (strict_types=1); * * @link http://phpdoc.org */ -namespace PHPUnit\phpDocumentor\Reflection\DocBlock\Tags; +namespace PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\Tags; use InvalidArgumentException; -use PHPUnit\phpDocumentor\Reflection\DocBlock\Description; -use PHPUnit\phpDocumentor\Reflection\DocBlock\DescriptionFactory; -use PHPUnit\phpDocumentor\Reflection\DocBlock\StandardTagFactory; -use PHPUnit\phpDocumentor\Reflection\Types\Context as TypeContext; -use PHPUnit\Webmozart\Assert\Assert; +use PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\Description; +use PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\DescriptionFactory; +use PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\StandardTagFactory; +use PHPUnitPHAR\phpDocumentor\Reflection\Types\Context as TypeContext; +use PHPUnitPHAR\Webmozart\Assert\Assert; use function preg_match; /** * Parses a tag definition for a DocBlock. @@ -35886,11 +38677,11 @@ final class Generic extends BaseTag implements Factory\StaticMethod items = $items; + } + /** + * @return ArrayShapeItem[] + */ + public function getItems() : array + { + return $this->items; + } + public function underlyingType() : Type + { + return new Array_(new Mixed_(), new ArrayKey()); + } + public function __toString() : string + { + return 'array{' . implode(', ', $this->items) . '}'; + } +} +key = $key; + $this->value = $value ?? new Mixed_(); + $this->optional = $optional; + } + public function getKey() : ?string + { + return $this->key; + } + public function getValue() : Type + { + return $this->value; + } + public function isOptional() : bool + { + return $this->optional; + } + public function __toString() : string + { + if ($this->key !== null) { + return sprintf('%s%s: %s', $this->key, $this->optional ? '?' : '', (string) $this->value); + } + return (string) $this->value; + } +} +owner = $owner; + $this->expression = $expression; + } + public function getOwner() : Type + { + return $this->owner; + } + public function getExpression() : string + { + return $this->expression; + } + public function underlyingType() : Type + { + return new Mixed_(); + } + public function __toString() : string + { + return sprintf('%s::%s', (string) $this->owner, $this->expression); + } +} +value = $value; + } + public function getValue() : float + { + return $this->value; + } + public function underlyingType() : Type + { + return new Float_(); + } + public function __toString() : string + { + return (string) $this->value; + } +} value = $value; + } + public function getValue() : int + { + return $this->value; + } + public function underlyingType() : Type + { + return new Integer(); + } + public function __toString() : string + { + return (string) $this->value; + } +} +valueType instanceof Mixed_) { + return 'non-empty-list'; + } + return 'non-empty-list<' . $this->valueType . '>'; + } +} +value = $value; + } + public function getValue() : string + { + return $this->value; + } + public function underlyingType() : Type + { + return new String_(); + } + public function __toString() : string + { + return sprintf('"%s"', $this->value); + } +} + List of recognized keywords and unto which Value Object they map * @psalm-var array> */ - private $keywords = ['string' => Types\String_::class, 'class-string' => Types\ClassString::class, 'interface-string' => Types\InterfaceString::class, 'html-escaped-string' => PseudoTypes\HtmlEscapedString::class, 'lowercase-string' => PseudoTypes\LowercaseString::class, 'non-empty-lowercase-string' => PseudoTypes\NonEmptyLowercaseString::class, 'non-empty-string' => PseudoTypes\NonEmptyString::class, 'numeric-string' => PseudoTypes\NumericString::class, 'numeric' => PseudoTypes\Numeric_::class, 'trait-string' => PseudoTypes\TraitString::class, 'int' => Types\Integer::class, 'integer' => Types\Integer::class, 'positive-int' => PseudoTypes\PositiveInteger::class, 'negative-int' => PseudoTypes\NegativeInteger::class, 'bool' => Types\Boolean::class, 'boolean' => Types\Boolean::class, 'real' => Types\Float_::class, 'float' => Types\Float_::class, 'double' => Types\Float_::class, 'object' => Types\Object_::class, 'mixed' => Types\Mixed_::class, 'array' => Types\Array_::class, 'array-key' => Types\ArrayKey::class, 'resource' => Types\Resource_::class, 'void' => Types\Void_::class, 'null' => Types\Null_::class, 'scalar' => Types\Scalar::class, 'callback' => Types\Callable_::class, 'callable' => Types\Callable_::class, 'callable-string' => PseudoTypes\CallableString::class, 'false' => PseudoTypes\False_::class, 'true' => PseudoTypes\True_::class, 'literal-string' => PseudoTypes\LiteralString::class, 'self' => Types\Self_::class, '$this' => Types\This::class, 'static' => Types\Static_::class, 'parent' => Types\Parent_::class, 'iterable' => Types\Iterable_::class, 'never' => Types\Never_::class, 'list' => PseudoTypes\List_::class]; + private $keywords = ['string' => String_::class, 'class-string' => ClassString::class, 'interface-string' => InterfaceString::class, 'html-escaped-string' => HtmlEscapedString::class, 'lowercase-string' => LowercaseString::class, 'non-empty-lowercase-string' => NonEmptyLowercaseString::class, 'non-empty-string' => NonEmptyString::class, 'numeric-string' => NumericString::class, 'numeric' => Numeric_::class, 'trait-string' => TraitString::class, 'int' => Integer::class, 'integer' => Integer::class, 'positive-int' => PositiveInteger::class, 'negative-int' => NegativeInteger::class, 'bool' => Boolean::class, 'boolean' => Boolean::class, 'real' => Float_::class, 'float' => Float_::class, 'double' => Float_::class, 'object' => Object_::class, 'mixed' => Mixed_::class, 'array' => Array_::class, 'array-key' => ArrayKey::class, 'resource' => Resource_::class, 'void' => Void_::class, 'null' => Null_::class, 'scalar' => Scalar::class, 'callback' => Callable_::class, 'callable' => Callable_::class, 'callable-string' => CallableString::class, 'false' => False_::class, 'true' => True_::class, 'literal-string' => LiteralString::class, 'self' => Self_::class, '$this' => This::class, 'static' => Static_::class, 'parent' => Parent_::class, 'iterable' => Iterable_::class, 'never' => Never_::class, 'list' => List_::class, 'non-empty-list' => NonEmptyList::class]; /** - * @var FqsenResolver * @psalm-readonly + * @var FqsenResolver */ private $fqsenResolver; + /** + * @psalm-readonly + * @var TypeParser + */ + private $typeParser; + /** + * @psalm-readonly + * @var Lexer + */ + private $lexer; /** * Initializes this TypeResolver with the means to create and resolve Fqsen objects. */ public function __construct(?FqsenResolver $fqsenResolver = null) { $this->fqsenResolver = $fqsenResolver ?: new FqsenResolver(); + $this->typeParser = new TypeParser(new ConstExprParser()); + $this->lexer = new Lexer(); } /** * Analyzes the given type and returns the FQCN variant. @@ -38595,9 +41748,9 @@ final class TypeResolver * This method only works as expected if the namespace and aliases are set; * no dynamic reflection is being performed here. * + * @uses Context::getNamespace() to determine with what to prefix the type name. * @uses Context::getNamespaceAliases() to check whether the first part of the relative type name should not be * replaced with another namespace. - * @uses Context::getNamespace() to determine with what to prefix the type name. * * @param string $type The relative or absolute type. */ @@ -38610,121 +41763,120 @@ final class TypeResolver if ($context === null) { $context = new Context(''); } - // split the type string into tokens `|`, `?`, `<`, `>`, `,`, `(`, `)`, `[]`, '<', '>' and type names - $tokens = preg_split('/(\\||\\?|<|>|&|, ?|\\(|\\)|\\[\\]+)/', $type, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE); - if ($tokens === \false) { - throw new InvalidArgumentException('Unable to split the type string "' . $type . '" into tokens'); + $tokens = $this->lexer->tokenize($type); + $tokenIterator = new TokenIterator($tokens); + $ast = $this->parse($tokenIterator); + $type = $this->createType($ast, $context); + return $this->tryParseRemainingCompoundTypes($tokenIterator, $context, $type); + } + public function createType(?TypeNode $type, Context $context) : Type + { + if ($type === null) { + return new Mixed_(); + } + switch (get_class($type)) { + case ArrayTypeNode::class: + return new Array_($this->createType($type->type, $context)); + case ArrayShapeNode::class: + return new ArrayShape(...array_map(function (ArrayShapeItemNode $item) use($context) : ArrayShapeItem { + return new ArrayShapeItem((string) $item->keyName, $this->createType($item->valueType, $context), $item->optional); + }, $type->items)); + case CallableTypeNode::class: + return $this->createFromCallable($type, $context); + case ConstTypeNode::class: + return $this->createFromConst($type, $context); + case GenericTypeNode::class: + return $this->createFromGeneric($type, $context); + case IdentifierTypeNode::class: + return $this->resolveSingleType($type->name, $context); + case IntersectionTypeNode::class: + return new Intersection(array_filter(array_map(function (TypeNode $nestedType) use($context) : Type { + $type = $this->createType($nestedType, $context); + if ($type instanceof AggregatedType) { + return new Expression($type); + } + return $type; + }, $type->types))); + case NullableTypeNode::class: + $nestedType = $this->createType($type->type, $context); + return new Nullable($nestedType); + case UnionTypeNode::class: + return new Compound(array_filter(array_map(function (TypeNode $nestedType) use($context) : Type { + $type = $this->createType($nestedType, $context); + if ($type instanceof AggregatedType) { + return new Expression($type); + } + return $type; + }, $type->types))); + case ThisTypeNode::class: + return new This(); + case ConditionalTypeNode::class: + case ConditionalTypeForParameterNode::class: + case OffsetAccessTypeNode::class: + default: + return new Mixed_(); } - /** @var ArrayIterator $tokenIterator */ - $tokenIterator = new ArrayIterator($tokens); - return $this->parseTypes($tokenIterator, $context, self::PARSER_IN_COMPOUND); } - /** - * Analyse each tokens and creates types - * - * @param ArrayIterator $tokens the iterator on tokens - * @param int $parserContext on of self::PARSER_* constants, indicating - * the context where we are in the parsing - */ - private function parseTypes(ArrayIterator $tokens, Context $context, int $parserContext) : Type + private function createFromGeneric(GenericTypeNode $type, Context $context) : Type { - $types = []; - $token = ''; - $compoundToken = '|'; - while ($tokens->valid()) { - $token = $tokens->current(); - if ($token === null) { - throw new RuntimeException('Unexpected nullable character'); - } - if ($token === '|' || $token === '&') { - if (count($types) === 0) { - throw new RuntimeException('A type is missing before a type separator'); - } - if (!in_array($parserContext, [self::PARSER_IN_COMPOUND, self::PARSER_IN_ARRAY_EXPRESSION, self::PARSER_IN_COLLECTION_EXPRESSION], \true)) { - throw new RuntimeException('Unexpected type separator'); - } - $compoundToken = $token; - $tokens->next(); - } elseif ($token === '?') { - if (!in_array($parserContext, [self::PARSER_IN_COMPOUND, self::PARSER_IN_ARRAY_EXPRESSION, self::PARSER_IN_COLLECTION_EXPRESSION], \true)) { - throw new RuntimeException('Unexpected nullable character'); - } - $tokens->next(); - $type = $this->parseTypes($tokens, $context, self::PARSER_IN_NULLABLE); - $types[] = new Nullable($type); - } elseif ($token === '(') { - $tokens->next(); - $type = $this->parseTypes($tokens, $context, self::PARSER_IN_ARRAY_EXPRESSION); - $token = $tokens->current(); - if ($token === null) { - // Someone did not properly close their array expression .. - break; - } - $tokens->next(); - $resolvedType = new Expression($type); - $types[] = $resolvedType; - } elseif ($parserContext === self::PARSER_IN_ARRAY_EXPRESSION && isset($token[0]) && $token[0] === ')') { - break; - } elseif ($token === '<') { - if (count($types) === 0) { - throw new RuntimeException('Unexpected collection operator "<", class name is missing'); - } - $classType = array_pop($types); - if ($classType !== null) { - if ((string) $classType === 'class-string') { - $types[] = $this->resolveClassString($tokens, $context); - } elseif ((string) $classType === 'int') { - $types[] = $this->resolveIntRange($tokens); - } elseif ((string) $classType === 'interface-string') { - $types[] = $this->resolveInterfaceString($tokens, $context); - } else { - $types[] = $this->resolveCollection($tokens, $classType, $context); - } + switch (strtolower($type->type->name)) { + case 'array': + return $this->createArray($type->genericTypes, $context); + case 'class-string': + $subType = $this->createType($type->genericTypes[0], $context); + if (!$subType instanceof Object_ || $subType->getFqsen() === null) { + throw new RuntimeException($subType . ' is not a class string'); } - $tokens->next(); - } elseif ($parserContext === self::PARSER_IN_COLLECTION_EXPRESSION && ($token === '>' || trim($token) === ',')) { - break; - } elseif ($token === self::OPERATOR_ARRAY) { - end($types); - $last = key($types); - if ($last === null) { - throw new InvalidArgumentException('Unexpected array operator'); + return new ClassString($subType->getFqsen()); + case 'interface-string': + $subType = $this->createType($type->genericTypes[0], $context); + if (!$subType instanceof Object_ || $subType->getFqsen() === null) { + throw new RuntimeException($subType . ' is not a class string'); } - $lastItem = $types[$last]; - if ($lastItem instanceof Expression) { - $lastItem = $lastItem->getValueType(); + return new InterfaceString($subType->getFqsen()); + case 'list': + return new List_($this->createType($type->genericTypes[0], $context)); + case 'non-empty-list': + return new NonEmptyList($this->createType($type->genericTypes[0], $context)); + case 'int': + if (isset($type->genericTypes[1]) === \false) { + throw new RuntimeException('int has not the correct format'); } - $types[$last] = new Array_($lastItem); - $tokens->next(); - } else { - $type = $this->resolveSingleType($token, $context); - $tokens->next(); - if ($parserContext === self::PARSER_IN_NULLABLE) { - return $type; + return new IntegerRange((string) $type->genericTypes[0], (string) $type->genericTypes[1]); + case 'iterable': + return new Iterable_(...array_reverse(array_map(function (TypeNode $genericType) use($context) : Type { + return $this->createType($genericType, $context); + }, $type->genericTypes))); + default: + $collectionType = $this->createType($type->type, $context); + if ($collectionType instanceof Object_ === \false) { + throw new RuntimeException(sprintf('%s is not a collection', (string) $collectionType)); } - $types[] = $type; - } - } - if ($token === '|' || $token === '&') { - throw new RuntimeException('A type is missing after a type separator'); - } - if (count($types) === 0) { - if ($parserContext === self::PARSER_IN_NULLABLE) { - throw new RuntimeException('A type is missing after a nullable character'); - } - if ($parserContext === self::PARSER_IN_ARRAY_EXPRESSION) { - throw new RuntimeException('A type is missing in an array expression'); - } - if ($parserContext === self::PARSER_IN_COLLECTION_EXPRESSION) { - throw new RuntimeException('A type is missing in a collection expression'); - } - } elseif (count($types) === 1) { - return current($types); + return new Collection($collectionType->getFqsen(), ...array_reverse(array_map(function (TypeNode $genericType) use($context) : Type { + return $this->createType($genericType, $context); + }, $type->genericTypes))); } - if ($compoundToken === '|') { - return new Compound(array_values($types)); + } + private function createFromCallable(CallableTypeNode $type, Context $context) : Callable_ + { + return new Callable_(array_map(function (CallableTypeParameterNode $param) use($context) : CallableParameter { + return new CallableParameter($this->createType($param->type, $context), $param->parameterName !== '' ? trim($param->parameterName, '$') : null, $param->isReference, $param->isVariadic, $param->isOptional); + }, $type->parameters), $this->createType($type->returnType, $context)); + } + private function createFromConst(ConstTypeNode $type, Context $context) : Type + { + switch (\true) { + case $type->constExpr instanceof ConstExprIntegerNode: + return new IntegerValue((int) $type->constExpr->value); + case $type->constExpr instanceof ConstExprFloatNode: + return new FloatValue((float) $type->constExpr->value); + case $type->constExpr instanceof ConstExprStringNode: + return new StringValue($type->constExpr->value); + case $type->constExpr instanceof ConstFetchNode: + return new ConstExpression($this->resolve($type->constExpr->className, $context), $type->constExpr->name); + default: + throw new RuntimeException(sprintf('Unsupported constant type %s', get_class($type))); } - return new Intersection(array_values($types)); } /** * resolve the given type into a type object @@ -38820,165 +41972,67 @@ final class TypeResolver { return new Object_($this->fqsenResolver->resolve($type, $context)); } - /** - * Resolves class string - * - * @param ArrayIterator $tokens - */ - private function resolveClassString(ArrayIterator $tokens, Context $context) : Type + /** @param TypeNode[] $typeNodes */ + private function createArray(array $typeNodes, Context $context) : Array_ { - $tokens->next(); - $classType = $this->parseTypes($tokens, $context, self::PARSER_IN_COLLECTION_EXPRESSION); - if (!$classType instanceof Object_ || $classType->getFqsen() === null) { - throw new RuntimeException($classType . ' is not a class string'); + $types = array_reverse(array_map(function (TypeNode $node) use($context) : Type { + return $this->createType($node, $context); + }, $typeNodes)); + if (isset($types[1]) === \false) { + return new Array_(...$types); + } + if ($this->validArrayKeyType($types[1]) || $types[1] instanceof ArrayKey) { + return new Array_(...$types); } - $token = $tokens->current(); - if ($token !== '>') { - if (empty($token)) { - throw new RuntimeException('class-string: ">" is missing'); + if ($types[1] instanceof Compound && $types[1]->getIterator()->count() === 2) { + if ($this->validArrayKeyType($types[1]->get(0)) && $this->validArrayKeyType($types[1]->get(1))) { + return new Array_(...$types); } - throw new RuntimeException('Unexpected character "' . $token . '", ">" is missing'); } - return new ClassString($classType->getFqsen()); + throw new RuntimeException('An array can have only integers or strings as keys'); } - /** - * Resolves integer ranges - * - * @param ArrayIterator $tokens - */ - private function resolveIntRange(ArrayIterator $tokens) : Type + private function validArrayKeyType(?Type $type) : bool { - $tokens->next(); - $token = ''; - $minValue = null; - $maxValue = null; - $commaFound = \false; - $tokenCounter = 0; - while ($tokens->valid()) { - $tokenCounter++; - $token = $tokens->current(); - if ($token === null) { - throw new RuntimeException('Unexpected nullable character'); - } - $token = trim($token); - if ($token === '>') { - break; - } - if ($token === ',') { - $commaFound = \true; - } - if ($commaFound === \false && $minValue === null) { - if (is_numeric($token) || $token === 'max' || $token === 'min') { - $minValue = $token; - } - } - if ($commaFound === \true && $maxValue === null) { - if (is_numeric($token) || $token === 'max' || $token === 'min') { - $maxValue = $token; - } - } - $tokens->next(); - } - if ($token !== '>') { - if (empty($token)) { - throw new RuntimeException('interface-string: ">" is missing'); - } - throw new RuntimeException('Unexpected character "' . $token . '", ">" is missing'); - } - if ($minValue === null || $maxValue === null || $tokenCounter > 4) { - throw new RuntimeException('int has not the correct format'); - } - return new IntegerRange($minValue, $maxValue); + return $type instanceof String_ || $type instanceof Integer; } - /** - * Resolves class string - * - * @param ArrayIterator $tokens - */ - private function resolveInterfaceString(ArrayIterator $tokens, Context $context) : Type + private function parse(TokenIterator $tokenIterator) : TypeNode { - $tokens->next(); - $classType = $this->parseTypes($tokens, $context, self::PARSER_IN_COLLECTION_EXPRESSION); - if (!$classType instanceof Object_ || $classType->getFqsen() === null) { - throw new RuntimeException($classType . ' is not a interface string'); - } - $token = $tokens->current(); - if ($token !== '>') { - if (empty($token)) { - throw new RuntimeException('interface-string: ">" is missing'); - } - throw new RuntimeException('Unexpected character "' . $token . '", ">" is missing'); + try { + $ast = $this->typeParser->parse($tokenIterator); + } catch (ParserException $e) { + throw new RuntimeException($e->getMessage(), 0, $e); } - return new InterfaceString($classType->getFqsen()); + return $ast; } /** - * Resolves the collection values and keys + * Will try to parse unsupported type notations by phpstan * - * @param ArrayIterator $tokens - * - * @return Array_|Iterable_|Collection + * The phpstan parser doesn't support the illegal nullable combinations like this library does. + * This method will warn the user about those notations but for bc purposes we will still have it here. */ - private function resolveCollection(ArrayIterator $tokens, Type $classType, Context $context) : Type + private function tryParseRemainingCompoundTypes(TokenIterator $tokenIterator, Context $context, Type $type) : Type { - $isArray = (string) $classType === 'array'; - $isIterable = (string) $classType === 'iterable'; - $isList = (string) $classType === 'list'; - // allow only "array", "iterable" or class name before "<" - if (!$isArray && !$isIterable && !$isList && (!$classType instanceof Object_ || $classType->getFqsen() === null)) { - throw new RuntimeException($classType . ' is not a collection'); + if ($tokenIterator->isCurrentTokenType(Lexer::TOKEN_UNION) || $tokenIterator->isCurrentTokenType(Lexer::TOKEN_INTERSECTION)) { + Deprecation::trigger('phpdocumentor/type-resolver', 'https://github.com/phpDocumentor/TypeResolver/issues/184', 'Legacy nullable type detected, please update your code as + you are using nullable types in a docblock. support will be removed in v2.0.0'); } - $tokens->next(); - $valueType = $this->parseTypes($tokens, $context, self::PARSER_IN_COLLECTION_EXPRESSION); - $keyType = null; - $token = $tokens->current(); - if ($token !== null && trim($token) === ',' && !$isList) { - // if we have a comma, then we just parsed the key type, not the value type - $keyType = $valueType; - if ($isArray) { - // check the key type for an "array" collection. We allow only - // strings or integers. - if (!$keyType instanceof ArrayKey && !$keyType instanceof String_ && !$keyType instanceof Integer && !$keyType instanceof Compound) { - throw new RuntimeException('An array can have only integers or strings as keys'); - } - if ($keyType instanceof Compound) { - foreach ($keyType->getIterator() as $item) { - if (!$item instanceof ArrayKey && !$item instanceof String_ && !$item instanceof Integer) { - throw new RuntimeException('An array can have only integers or strings as keys'); - } - } - } + $continue = \true; + while ($continue) { + $continue = \false; + while ($tokenIterator->tryConsumeTokenType(Lexer::TOKEN_UNION)) { + $ast = $this->parse($tokenIterator); + $type2 = $this->createType($ast, $context); + $type = new Compound([$type, $type2]); + $continue = \true; } - $tokens->next(); - // now let's parse the value type - $valueType = $this->parseTypes($tokens, $context, self::PARSER_IN_COLLECTION_EXPRESSION); - } - $token = $tokens->current(); - if ($token !== '>') { - if (empty($token)) { - throw new RuntimeException('Collection: ">" is missing'); + while ($tokenIterator->tryConsumeTokenType(Lexer::TOKEN_INTERSECTION)) { + $ast = $this->typeParser->parse($tokenIterator); + $type2 = $this->createType($ast, $context); + $type = new Intersection([$type, $type2]); + $continue = \true; } - throw new RuntimeException('Unexpected character "' . $token . '", ">" is missing'); - } - if ($isArray) { - return new Array_($valueType, $keyType); - } - if ($isIterable) { - return new Iterable_($valueType, $keyType); } - if ($isList) { - return new List_($valueType); - } - if ($classType instanceof Object_) { - return $this->makeCollectionFromObject($classType, $valueType, $keyType); - } - throw new RuntimeException('Invalid $classType provided'); - } - /** - * @psalm-pure - */ - private function makeCollectionFromObject(Object_ $object, Type $valueType, ?Type $keyType = null) : Collection - { - return new Collection($object->getFqsen(), $valueType, $keyType); + return $type; } } keyType ?? $this->defaultKeyType; } /** - * Returns the value for the keys of this array. + * Returns the type for the values of this array. */ public function getValueType() : Type { @@ -39062,11 +42116,11 @@ abstract class AbstractList implements Type * @link http://phpdoc.org */ declare (strict_types=1); -namespace PHPUnit\phpDocumentor\Reflection\Types; +namespace PHPUnitPHAR\phpDocumentor\Reflection\Types; use ArrayIterator; use IteratorAggregate; -use PHPUnit\phpDocumentor\Reflection\Type; +use PHPUnitPHAR\phpDocumentor\Reflection\Type; use function array_key_exists; use function implode; /** @@ -39146,7 +42200,7 @@ abstract class AggregatedType implements Type, IteratorAggregate */ private function add(Type $type) : void { - if ($type instanceof self) { + if ($type instanceof static) { foreach ($type->getIterator() as $subType) { $this->add($subType); } @@ -39170,10 +42224,10 @@ declare (strict_types=1); * * @link http://phpdoc.org */ -namespace PHPUnit\phpDocumentor\Reflection\Types; +namespace PHPUnitPHAR\phpDocumentor\Reflection\Types; -use PHPUnit\phpDocumentor\Reflection\PseudoType; -use PHPUnit\phpDocumentor\Reflection\Type; +use PHPUnitPHAR\phpDocumentor\Reflection\PseudoType; +use PHPUnitPHAR\phpDocumentor\Reflection\Type; /** * Value Object representing a array-key Type. * @@ -39207,7 +42261,7 @@ declare (strict_types=1); * * @link http://phpdoc.org */ -namespace PHPUnit\phpDocumentor\Reflection\Types; +namespace PHPUnitPHAR\phpDocumentor\Reflection\Types; /** * Represents an array type as described in the PSR-5, the PHPDoc Standard. @@ -39234,9 +42288,9 @@ declare (strict_types=1); * * @link http://phpdoc.org */ -namespace PHPUnit\phpDocumentor\Reflection\Types; +namespace PHPUnitPHAR\phpDocumentor\Reflection\Types; -use PHPUnit\phpDocumentor\Reflection\Type; +use PHPUnitPHAR\phpDocumentor\Reflection\Type; /** * Value Object representing a Boolean type. * @@ -39254,6 +42308,66 @@ class Boolean implements Type } type = $type; + $this->isReference = $isReference; + $this->isVariadic = $isVariadic; + $this->isOptional = $isOptional; + $this->name = $name; + } + public function getName() : ?string + { + return $this->name; + } + public function getType() : Type + { + return $this->type; + } + public function isReference() : bool + { + return $this->isReference; + } + public function isVariadic() : bool + { + return $this->isVariadic; + } + public function isOptional() : bool + { + return $this->isOptional; + } +} +parameters = $parameters; + $this->returnType = $returnType; + } + /** @return CallableParameter[] */ + public function getParameters() : array + { + return $this->parameters; + } + public function getReturnType() : ?Type + { + return $this->returnType; + } /** * Returns a rendered output of the Type as it would be used in a DocBlock. */ @@ -39292,11 +42427,11 @@ declare (strict_types=1); * * @link http://phpdoc.org */ -namespace PHPUnit\phpDocumentor\Reflection\Types; +namespace PHPUnitPHAR\phpDocumentor\Reflection\Types; -use PHPUnit\phpDocumentor\Reflection\Fqsen; -use PHPUnit\phpDocumentor\Reflection\PseudoType; -use PHPUnit\phpDocumentor\Reflection\Type; +use PHPUnitPHAR\phpDocumentor\Reflection\Fqsen; +use PHPUnitPHAR\phpDocumentor\Reflection\PseudoType; +use PHPUnitPHAR\phpDocumentor\Reflection\Type; /** * Value Object representing the type 'string'. * @@ -39346,10 +42481,10 @@ declare (strict_types=1); * * @link http://phpdoc.org */ -namespace PHPUnit\phpDocumentor\Reflection\Types; +namespace PHPUnitPHAR\phpDocumentor\Reflection\Types; -use PHPUnit\phpDocumentor\Reflection\Fqsen; -use PHPUnit\phpDocumentor\Reflection\Type; +use PHPUnitPHAR\phpDocumentor\Reflection\Fqsen; +use PHPUnitPHAR\phpDocumentor\Reflection\Type; /** * Represents a collection type as described in the PSR-5, the PHPDoc Standard. * @@ -39405,9 +42540,9 @@ declare (strict_types=1); * * @link http://phpdoc.org */ -namespace PHPUnit\phpDocumentor\Reflection\Types; +namespace PHPUnitPHAR\phpDocumentor\Reflection\Types; -use PHPUnit\phpDocumentor\Reflection\Type; +use PHPUnitPHAR\phpDocumentor\Reflection\Type; /** * Value Object representing a Compound Type. * @@ -39440,7 +42575,7 @@ declare (strict_types=1); * * @link http://phpdoc.org */ -namespace PHPUnit\phpDocumentor\Reflection\Types; +namespace PHPUnitPHAR\phpDocumentor\Reflection\Types; use function strlen; use function substr; @@ -39522,7 +42657,7 @@ declare (strict_types=1); * * @link http://phpdoc.org */ -namespace PHPUnit\phpDocumentor\Reflection\Types; +namespace PHPUnitPHAR\phpDocumentor\Reflection\Types; use ArrayIterator; use InvalidArgumentException; @@ -39554,12 +42689,13 @@ use const T_NAME_QUALIFIED; use const T_NAMESPACE; use const T_NS_SEPARATOR; use const T_STRING; +use const T_TRAIT; use const T_USE; if (!defined('T_NAME_QUALIFIED')) { - define('T_NAME_QUALIFIED', 'T_NAME_QUALIFIED'); + define('T_NAME_QUALIFIED', 10001); } if (!defined('T_NAME_FULLY_QUALIFIED')) { - define('T_NAME_FULLY_QUALIFIED', 'T_NAME_FULLY_QUALIFIED'); + define('T_NAME_FULLY_QUALIFIED', 10002); } /** * Convenience class to create a Context for DocBlocks when not using the Reflection Component of phpDocumentor. @@ -39665,6 +42801,7 @@ final class ContextFactory $currentNamespace = $this->parseNamespace($tokens); break; case T_CLASS: + case T_TRAIT: // Fast-forward the iterator through the class so that any // T_USE tokens found within are skipped - these are not // valid namespace use statements so should be ignored. @@ -39880,9 +43017,9 @@ declare (strict_types=1); * * @link http://phpdoc.org */ -namespace PHPUnit\phpDocumentor\Reflection\Types; +namespace PHPUnitPHAR\phpDocumentor\Reflection\Types; -use PHPUnit\phpDocumentor\Reflection\Type; +use PHPUnitPHAR\phpDocumentor\Reflection\Type; /** * Represents an expression type as described in the PSR-5, the PHPDoc Standard. * @@ -39925,15 +43062,15 @@ declare (strict_types=1); * * @link http://phpdoc.org */ -namespace PHPUnit\phpDocumentor\Reflection\Types; +namespace PHPUnitPHAR\phpDocumentor\Reflection\Types; -use PHPUnit\phpDocumentor\Reflection\Type; +use PHPUnitPHAR\phpDocumentor\Reflection\Type; /** * Value Object representing a Float. * * @psalm-immutable */ -final class Float_ implements Type +class Float_ implements Type { /** * Returns a rendered output of the Type as it would be used in a DocBlock. @@ -39954,9 +43091,9 @@ declare (strict_types=1); * * @link http://phpdoc.org */ -namespace PHPUnit\phpDocumentor\Reflection\Types; +namespace PHPUnitPHAR\phpDocumentor\Reflection\Types; -use PHPUnit\phpDocumentor\Reflection\Type; +use PHPUnitPHAR\phpDocumentor\Reflection\Type; /** * Value object representing Integer type * @@ -39983,10 +43120,10 @@ declare (strict_types=1); * * @link http://phpdoc.org */ -namespace PHPUnit\phpDocumentor\Reflection\Types; +namespace PHPUnitPHAR\phpDocumentor\Reflection\Types; -use PHPUnit\phpDocumentor\Reflection\Fqsen; -use PHPUnit\phpDocumentor\Reflection\Type; +use PHPUnitPHAR\phpDocumentor\Reflection\Fqsen; +use PHPUnitPHAR\phpDocumentor\Reflection\Type; /** * Value Object representing the type 'string'. * @@ -40032,9 +43169,9 @@ final class InterfaceString implements Type * @link http://phpdoc.org */ declare (strict_types=1); -namespace PHPUnit\phpDocumentor\Reflection\Types; +namespace PHPUnitPHAR\phpDocumentor\Reflection\Types; -use PHPUnit\phpDocumentor\Reflection\Type; +use PHPUnitPHAR\phpDocumentor\Reflection\Type; /** * Value Object representing a Compound Type. * @@ -40067,7 +43204,7 @@ declare (strict_types=1); * * @link http://phpdoc.org */ -namespace PHPUnit\phpDocumentor\Reflection\Types; +namespace PHPUnitPHAR\phpDocumentor\Reflection\Types; /** * Value Object representing iterable type @@ -40101,9 +43238,9 @@ declare (strict_types=1); * * @link http://phpdoc.org */ -namespace PHPUnit\phpDocumentor\Reflection\Types; +namespace PHPUnitPHAR\phpDocumentor\Reflection\Types; -use PHPUnit\phpDocumentor\Reflection\Type; +use PHPUnitPHAR\phpDocumentor\Reflection\Type; /** * Value Object representing an unknown, or mixed, type. * @@ -40130,9 +43267,9 @@ declare (strict_types=1); * * @link http://phpdoc.org */ -namespace PHPUnit\phpDocumentor\Reflection\Types; +namespace PHPUnitPHAR\phpDocumentor\Reflection\Types; -use PHPUnit\phpDocumentor\Reflection\Type; +use PHPUnitPHAR\phpDocumentor\Reflection\Type; /** * Value Object representing the return-type 'never'. * @@ -40162,9 +43299,9 @@ declare (strict_types=1); * * @link http://phpdoc.org */ -namespace PHPUnit\phpDocumentor\Reflection\Types; +namespace PHPUnitPHAR\phpDocumentor\Reflection\Types; -use PHPUnit\phpDocumentor\Reflection\Type; +use PHPUnitPHAR\phpDocumentor\Reflection\Type; /** * Value Object representing a null value or type. * @@ -40191,9 +43328,9 @@ declare (strict_types=1); * * @link http://phpdoc.org */ -namespace PHPUnit\phpDocumentor\Reflection\Types; +namespace PHPUnitPHAR\phpDocumentor\Reflection\Types; -use PHPUnit\phpDocumentor\Reflection\Type; +use PHPUnitPHAR\phpDocumentor\Reflection\Type; /** * Value Object representing a nullable type. The real type is wrapped. * @@ -40236,11 +43373,11 @@ declare (strict_types=1); * * @link http://phpdoc.org */ -namespace PHPUnit\phpDocumentor\Reflection\Types; +namespace PHPUnitPHAR\phpDocumentor\Reflection\Types; use InvalidArgumentException; -use PHPUnit\phpDocumentor\Reflection\Fqsen; -use PHPUnit\phpDocumentor\Reflection\Type; +use PHPUnitPHAR\phpDocumentor\Reflection\Fqsen; +use PHPUnitPHAR\phpDocumentor\Reflection\Type; use function strpos; /** * Value Object representing an object. @@ -40293,9 +43430,9 @@ declare (strict_types=1); * * @link http://phpdoc.org */ -namespace PHPUnit\phpDocumentor\Reflection\Types; +namespace PHPUnitPHAR\phpDocumentor\Reflection\Types; -use PHPUnit\phpDocumentor\Reflection\Type; +use PHPUnitPHAR\phpDocumentor\Reflection\Type; /** * Value Object representing the 'parent' type. * @@ -40324,9 +43461,9 @@ declare (strict_types=1); * * @link http://phpdoc.org */ -namespace PHPUnit\phpDocumentor\Reflection\Types; +namespace PHPUnitPHAR\phpDocumentor\Reflection\Types; -use PHPUnit\phpDocumentor\Reflection\Type; +use PHPUnitPHAR\phpDocumentor\Reflection\Type; /** * Value Object representing the 'resource' Type. * @@ -40353,9 +43490,9 @@ declare (strict_types=1); * * @link http://phpdoc.org */ -namespace PHPUnit\phpDocumentor\Reflection\Types; +namespace PHPUnitPHAR\phpDocumentor\Reflection\Types; -use PHPUnit\phpDocumentor\Reflection\Type; +use PHPUnitPHAR\phpDocumentor\Reflection\Type; /** * Value Object representing the 'scalar' pseudo-type, which is either a string, integer, float or boolean. * @@ -40382,9 +43519,9 @@ declare (strict_types=1); * * @link http://phpdoc.org */ -namespace PHPUnit\phpDocumentor\Reflection\Types; +namespace PHPUnitPHAR\phpDocumentor\Reflection\Types; -use PHPUnit\phpDocumentor\Reflection\Type; +use PHPUnitPHAR\phpDocumentor\Reflection\Type; /** * Value Object representing the 'self' type. * @@ -40413,9 +43550,9 @@ declare (strict_types=1); * * @link http://phpdoc.org */ -namespace PHPUnit\phpDocumentor\Reflection\Types; +namespace PHPUnitPHAR\phpDocumentor\Reflection\Types; -use PHPUnit\phpDocumentor\Reflection\Type; +use PHPUnitPHAR\phpDocumentor\Reflection\Type; /** * Value Object representing the 'static' type. * @@ -40449,9 +43586,9 @@ declare (strict_types=1); * * @link http://phpdoc.org */ -namespace PHPUnit\phpDocumentor\Reflection\Types; +namespace PHPUnitPHAR\phpDocumentor\Reflection\Types; -use PHPUnit\phpDocumentor\Reflection\Type; +use PHPUnitPHAR\phpDocumentor\Reflection\Type; /** * Value Object representing the type 'string'. * @@ -40478,9 +43615,9 @@ declare (strict_types=1); * * @link http://phpdoc.org */ -namespace PHPUnit\phpDocumentor\Reflection\Types; +namespace PHPUnitPHAR\phpDocumentor\Reflection\Types; -use PHPUnit\phpDocumentor\Reflection\Type; +use PHPUnitPHAR\phpDocumentor\Reflection\Type; /** * Value Object representing the '$this' pseudo-type. * @@ -40510,9 +43647,9 @@ declare (strict_types=1); * * @link http://phpdoc.org */ -namespace PHPUnit\phpDocumentor\Reflection\Types; +namespace PHPUnitPHAR\phpDocumentor\Reflection\Types; -use PHPUnit\phpDocumentor\Reflection\Type; +use PHPUnitPHAR\phpDocumentor\Reflection\Type; /** * Value Object representing the return-type 'void'. * @@ -41385,8 +44522,8 @@ class CallbackToken implements \Prophecy\Argument\Token\TokenInterface namespace Prophecy\Argument\Token; use Prophecy\Comparator\FactoryProvider; -use PHPUnit\SebastianBergmann\Comparator\ComparisonFailure; -use PHPUnit\SebastianBergmann\Comparator\Factory as ComparatorFactory; +use PHPUnitPHAR\SebastianBergmann\Comparator\ComparisonFailure; +use PHPUnitPHAR\SebastianBergmann\Comparator\Factory as ComparatorFactory; use Prophecy\Util\StringUtil; /** * Exact value token. @@ -41439,6 +44576,9 @@ class ExactValueToken implements \Prophecy\Argument\Token\TokenInterface if (\is_object($this->value) && !\method_exists($this->value, '__toString')) { return \false; } + if (\is_numeric($argument) xor \is_numeric($this->value)) { + return \strval($argument) == \strval($this->value) ? 10 : \false; + } } elseif (\is_numeric($argument) && \is_numeric($this->value)) { // noop } elseif (\gettype($argument) !== \gettype($this->value)) { @@ -41836,8 +44976,8 @@ class NotInArrayToken implements \Prophecy\Argument\Token\TokenInterface namespace Prophecy\Argument\Token; use Prophecy\Comparator\FactoryProvider; -use PHPUnit\SebastianBergmann\Comparator\ComparisonFailure; -use PHPUnit\SebastianBergmann\Comparator\Factory as ComparatorFactory; +use PHPUnitPHAR\SebastianBergmann\Comparator\ComparisonFailure; +use PHPUnitPHAR\SebastianBergmann\Comparator\Factory as ComparatorFactory; use Prophecy\Util\StringUtil; /** * Object state-checker token. @@ -42425,8 +45565,8 @@ class CallCenter */ namespace Prophecy\Comparator; -use PHPUnit\SebastianBergmann\Comparator\Comparator; -use PHPUnit\SebastianBergmann\Comparator\ComparisonFailure; +use PHPUnitPHAR\SebastianBergmann\Comparator\Comparator; +use PHPUnitPHAR\SebastianBergmann\Comparator\ComparisonFailure; /** * Closure comparator. * @@ -42434,20 +45574,35 @@ use PHPUnit\SebastianBergmann\Comparator\ComparisonFailure; */ final class ClosureComparator extends Comparator { + /** + * @param mixed $expected + * @param mixed $actual + */ public function accepts($expected, $actual) : bool { return \is_object($expected) && $expected instanceof \Closure && \is_object($actual) && $actual instanceof \Closure; } + /** + * @param mixed $expected + * @param mixed $actual + * @param float $delta + * @param bool $canonicalize + * @param bool $ignoreCase + */ public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = \false, $ignoreCase = \false) : void { if ($expected !== $actual) { + // Support for sebastian/comparator < 5 + if ((new \ReflectionMethod(ComparisonFailure::class, '__construct'))->getNumberOfParameters() >= 6) { + // @phpstan-ignore-next-line + throw new ComparisonFailure($expected, $actual, '', '', \false, 'all closures are different if not identical'); + } throw new ComparisonFailure( $expected, $actual, // we don't need a diff '', '', - \false, 'all closures are different if not identical' ); } @@ -42465,7 +45620,7 @@ final class ClosureComparator extends Comparator */ namespace Prophecy\Comparator; -use PHPUnit\SebastianBergmann\Comparator\Factory as BaseFactory; +use PHPUnitPHAR\SebastianBergmann\Comparator\Factory as BaseFactory; /** * Prophecy comparator factory. * @@ -42508,7 +45663,7 @@ final class Factory extends BaseFactory */ namespace Prophecy\Comparator; -use PHPUnit\SebastianBergmann\Comparator\Factory; +use PHPUnitPHAR\SebastianBergmann\Comparator\Factory; /** * Prophecy comparator factory. * @@ -42546,17 +45701,28 @@ final class FactoryProvider namespace Prophecy\Comparator; use Prophecy\Prophecy\ProphecyInterface; -use PHPUnit\SebastianBergmann\Comparator\ObjectComparator; +use PHPUnitPHAR\SebastianBergmann\Comparator\ObjectComparator; /** * @final */ class ProphecyComparator extends ObjectComparator { + /** + * @param mixed $expected + * @param mixed $actual + */ public function accepts($expected, $actual) : bool { return \is_object($expected) && \is_object($actual) && $actual instanceof ProphecyInterface; } /** + * @param mixed $expected + * @param mixed $actual + * @param float $delta + * @param bool $canonicalize + * @param bool $ignoreCase + * @param array $processed + * * @phpstan-param list $processed */ public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = \false, $ignoreCase = \false, array &$processed = array()) : void @@ -42587,7 +45753,7 @@ use ReflectionClass; class CachedDoubler extends \Prophecy\Doubler\Doubler { /** - * @var array + * @var array */ private static $classes = array(); protected function createDoubleClass(ReflectionClass $class = null, array $interfaces) @@ -42599,8 +45765,8 @@ class CachedDoubler extends \Prophecy\Doubler\Doubler return self::$classes[$classId] = parent::createDoubleClass($class, $interfaces); } /** - * @param ReflectionClass $class - * @param ReflectionClass[] $interfaces + * @param ReflectionClass|null $class + * @param ReflectionClass[] $interfaces * * @return string */ @@ -43345,7 +46511,7 @@ interface DoubleInterface */ namespace Prophecy\Doubler; -use PHPUnit\Doctrine\Instantiator\Instantiator; +use PHPUnitPHAR\Doctrine\Instantiator\Instantiator; use Prophecy\Doubler\ClassPatch\ClassPatchInterface; use Prophecy\Doubler\Generator\ClassMirror; use Prophecy\Doubler\Generator\ClassCreator; @@ -43402,11 +46568,13 @@ class Doubler /** * Creates double from specific class or/and list of interfaces. * - * @param ReflectionClass|null $class - * @param ReflectionClass[] $interfaces Array of ReflectionClass instances - * @param array|null $args Constructor arguments + * @template T of object + * + * @param ReflectionClass|null $class + * @param ReflectionClass[] $interfaces Array of ReflectionClass instances + * @param array|null $args Constructor arguments * - * @return DoubleInterface + * @return T&DoubleInterface * * @throws \Prophecy\Exception\InvalidArgumentException */ @@ -43433,10 +46601,12 @@ class Doubler /** * Creates double class and returns its FQN. * - * @param ReflectionClass|null $class - * @param ReflectionClass[] $interfaces + * @template T of object * - * @return string + * @param ReflectionClass|null $class + * @param ReflectionClass[] $interfaces + * + * @return class-string */ protected function createDoubleClass(ReflectionClass $class = null, array $interfaces) { @@ -43447,7 +46617,9 @@ class Doubler $patch->apply($node); } } + $node->addInterface(\Prophecy\Doubler\DoubleInterface::class); $this->creator->create($name, $node); + \assert(\class_exists($name, \false)); return $name; } } @@ -43998,11 +47170,11 @@ use Prophecy\Exception\InvalidArgumentException; class ClassNode { /** - * @var string + * @var class-string */ private $parentClass = 'stdClass'; /** - * @var list + * @var list */ private $interfaces = array(); /** @@ -44024,14 +47196,14 @@ class ClassNode */ private $methods = array(); /** - * @return string + * @return class-string */ public function getParentClass() { return $this->parentClass; } /** - * @param string $class + * @param class-string|null $class * * @return void */ @@ -44040,14 +47212,14 @@ class ClassNode $this->parentClass = $class ?: 'stdClass'; } /** - * @return list + * @return list */ public function getInterfaces() { return $this->interfaces; } /** - * @param string $interface + * @param class-string $interface * * @return void */ @@ -44059,7 +47231,7 @@ class ClassNode \array_unshift($this->interfaces, $interface); } /** - * @param string $interface + * @param class-string $interface * * @return bool */ @@ -44470,7 +47642,7 @@ abstract class TypeNodeAbstract } public function canUseNullShorthand() : bool { - return isset($this->types['null']) && \count($this->types) <= 2; + return isset($this->types['null']) && \count($this->types) === 2; } /** * @return list @@ -44641,13 +47813,15 @@ use ReflectionClass; * Lazy double. * Gives simple interface to describe double before creating it. * + * @template T of object + * * @author Konstantin Kudryashov */ class LazyDouble { private $doubler; /** - * @var ReflectionClass|null + * @var ReflectionClass|null */ private $class; /** @@ -44659,7 +47833,7 @@ class LazyDouble */ private $arguments = null; /** - * @var DoubleInterface|null + * @var (T&DoubleInterface)|null */ private $double; public function __construct(\Prophecy\Doubler\Doubler $doubler) @@ -44669,12 +47843,16 @@ class LazyDouble /** * Tells doubler to use specific class as parent one for double. * - * @param string|ReflectionClass $class + * @param class-string|ReflectionClass $class * * @return void * - * @throws \Prophecy\Exception\Doubler\ClassNotFoundException - * @throws \Prophecy\Exception\Doubler\DoubleException + * @template U of object + * @phpstan-param class-string|ReflectionClass $class + * @phpstan-this-out static + * + * @throws ClassNotFoundException + * @throws DoubleException */ public function setParentClass($class) { @@ -44687,17 +47865,22 @@ class LazyDouble } $class = new ReflectionClass($class); } + /** @var static $this */ $this->class = $class; } /** * Tells doubler to implement specific interface with double. * - * @param string|ReflectionClass $interface + * @param class-string|ReflectionClass $interface * * @return void * - * @throws \Prophecy\Exception\Doubler\InterfaceNotFoundException - * @throws \Prophecy\Exception\Doubler\DoubleException + * @template U of object + * @phpstan-param class-string|ReflectionClass $interface + * @phpstan-this-out static + * + * @throws InterfaceNotFoundException + * @throws DoubleException */ public function addInterface($interface) { @@ -44726,7 +47909,7 @@ class LazyDouble /** * Creates double instance or returns already created one. * - * @return DoubleInterface + * @return T&DoubleInterface */ public function getInstance() { @@ -45436,7 +48619,7 @@ interface ProphecyException extends Exception */ namespace Prophecy\PhpDocumentor; -use PHPUnit\phpDocumentor\Reflection\DocBlock\Tags\Method; +use PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\Tags\Method; /** * @author Théo FIDRY * @@ -45487,9 +48670,9 @@ final class ClassAndInterfaceTagRetriever implements \Prophecy\PhpDocumentor\Met */ namespace Prophecy\PhpDocumentor; -use PHPUnit\phpDocumentor\Reflection\DocBlock\Tags\Method; -use PHPUnit\phpDocumentor\Reflection\DocBlockFactory; -use PHPUnit\phpDocumentor\Reflection\Types\ContextFactory; +use PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\Tags\Method; +use PHPUnitPHAR\phpDocumentor\Reflection\DocBlockFactory; +use PHPUnitPHAR\phpDocumentor\Reflection\Types\ContextFactory; /** * @author Théo FIDRY * @@ -45532,7 +48715,7 @@ final class ClassTagRetriever implements \Prophecy\PhpDocumentor\MethodTagRetrie */ namespace Prophecy\PhpDocumentor; -use PHPUnit\phpDocumentor\Reflection\DocBlock\Tags\Method; +use PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\Tags\Method; /** * @author Théo FIDRY * @@ -45941,7 +49124,7 @@ class ReturnPromise implements \Prophecy\Promise\PromiseInterface */ namespace Prophecy\Promise; -use PHPUnit\Doctrine\Instantiator\Instantiator; +use PHPUnitPHAR\Doctrine\Instantiator\Instantiator; use Prophecy\Prophecy\ObjectProphecy; use Prophecy\Prophecy\MethodProphecy; use Prophecy\Exception\InvalidArgumentException; @@ -46121,7 +49304,7 @@ class MethodProphecy if ('void' === $defaultType) { $this->voidReturnType = \true; } - $this->will(function () use($defaultType) { + $this->will(function ($args, \Prophecy\Prophecy\ObjectProphecy $object, \Prophecy\Prophecy\MethodProphecy $method) use($defaultType) { switch ($defaultType) { case 'void': return; @@ -46135,6 +49318,12 @@ class MethodProphecy return \false; case 'array': return array(); + case 'true': + return \true; + case 'false': + return \false; + case 'null': + return null; case 'callable': case 'Closure': return function () { @@ -46144,7 +49333,13 @@ class MethodProphecy return (function () { yield; })(); + case 'object': + $prophet = new Prophet(); + return $prophet->prophesize()->reveal(); default: + if (!\class_exists($defaultType) && !\interface_exists($defaultType)) { + throw new MethodProphecyException(\sprintf('Cannot create a return value for the method as the type "%s" is not supported. Configure an explicit return value instead.', $defaultType), $method); + } $prophet = new Prophet(); return $prophet->prophesize($defaultType)->reveal(); } @@ -46197,14 +49392,16 @@ class MethodProphecy * * @see \Prophecy\Promise\ReturnPromise * + * @param mixed ...$return a list of return values + * * @return $this */ - public function willReturn() + public function willReturn(...$return) { if ($this->voidReturnType) { throw new MethodProphecyException("The method \"{$this->methodName}\" has a void return type, and so cannot return anything", $this); } - return $this->will(new Promise\ReturnPromise(\func_get_args())); + return $this->will(new Promise\ReturnPromise($return)); } /** * @param array $items @@ -46519,8 +49716,8 @@ class MethodProphecy namespace Prophecy\Prophecy; use Prophecy\Comparator\FactoryProvider; -use PHPUnit\SebastianBergmann\Comparator\ComparisonFailure; -use PHPUnit\SebastianBergmann\Comparator\Factory as ComparatorFactory; +use PHPUnitPHAR\SebastianBergmann\Comparator\ComparisonFailure; +use PHPUnitPHAR\SebastianBergmann\Comparator\Factory as ComparatorFactory; use Prophecy\Call\Call; use Prophecy\Doubler\LazyDouble; use Prophecy\Argument\ArgumentsWildcard; @@ -46537,6 +49734,9 @@ use Prophecy\Exception\Prediction\PredictionException; */ class ObjectProphecy implements \Prophecy\Prophecy\ProphecyInterface { + /** + * @var LazyDouble + */ private $lazyDouble; private $callCenter; private $revealer; @@ -46545,6 +49745,9 @@ class ObjectProphecy implements \Prophecy\Prophecy\ProphecyInterface * @var array> */ private $methodProphecies = array(); + /** + * @param LazyDouble $lazyDouble + */ public function __construct(LazyDouble $lazyDouble, CallCenter $callCenter = null, \Prophecy\Prophecy\RevealerInterface $revealer = null, ComparatorFactory $comparatorFactory = null) { $this->lazyDouble = $lazyDouble; @@ -46559,7 +49762,7 @@ class ObjectProphecy implements \Prophecy\Prophecy\ProphecyInterface * * @return $this * - * @template U + * @template U of object * @phpstan-param class-string $class * @phpstan-this-out static */ @@ -46575,7 +49778,7 @@ class ObjectProphecy implements \Prophecy\Prophecy\ProphecyInterface * * @return $this * - * @template U + * @template U of object * @phpstan-param class-string $interface * @phpstan-this-out static */ @@ -46898,6 +50101,7 @@ use Prophecy\Doubler\CachedDoubler; use Prophecy\Doubler\Doubler; use Prophecy\Doubler\LazyDouble; use Prophecy\Doubler\ClassPatch; +use Prophecy\Exception\Doubler\ClassNotFoundException; use Prophecy\Prophecy\ObjectProphecy; use Prophecy\Prophecy\RevealerInterface; use Prophecy\Prophecy\Revealer; @@ -46948,16 +50152,19 @@ class Prophet * * @template T of object * @phpstan-param class-string|null $classOrInterface - * @phpstan-return ObjectProphecy + * @phpstan-return ($classOrInterface is null ? ObjectProphecy : ObjectProphecy) */ public function prophesize($classOrInterface = null) { $this->prophecies[] = $prophecy = new ObjectProphecy(new LazyDouble($this->doubler), new CallCenter($this->util), $this->revealer); - if ($classOrInterface && \class_exists($classOrInterface)) { - return $prophecy->willExtend($classOrInterface); - } - if ($classOrInterface && \interface_exists($classOrInterface)) { - return $prophecy->willImplement($classOrInterface); + if ($classOrInterface) { + if (\class_exists($classOrInterface)) { + return $prophecy->willExtend($classOrInterface); + } + if (\interface_exists($classOrInterface)) { + return $prophecy->willImplement($classOrInterface); + } + throw new ClassNotFoundException(\sprintf('Cannot prophesize class %s, because it cannot be found.', $classOrInterface), $classOrInterface); } return $prophecy; } @@ -47006,7 +50213,7 @@ class Prophet namespace Prophecy\Util; use Prophecy\Prophecy\ProphecyInterface; -use PHPUnit\SebastianBergmann\RecursionContext\Context; +use PHPUnitPHAR\SebastianBergmann\RecursionContext\Context; /* * This file is part of the Prophecy. * (c) Konstantin Kudryashov @@ -47121,6 +50328,7 @@ class ExportUtil if (($key = $processed->contains($value)) !== \false) { return 'Array &' . $key; } + \assert(\is_array($value)); $array = $value; $key = $processed->add($value); $values = ''; @@ -47135,9 +50343,11 @@ class ExportUtil if (\is_object($value)) { $class = \get_class($value); if ($processed->contains($value)) { + \assert(\is_object($value)); return \sprintf('%s#%d Object', $class, \spl_object_id($value)); } $processed->add($value); + \assert(\is_object($value)); $values = ''; $array = self::toArray($value); if (\count($array) > 0) { @@ -47235,6165 +50445,8469 @@ class StringUtil }, $calls)); } } - - - - - This Schema file defines the rules by which the XML configuration file of PHPUnit 9.6 may be structured. - - - - - - Root Element - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - The main type specifying the document structure - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit; +namespace PHPUnitPHAR\PHPStan\PhpDocParser\Ast; -use Throwable; /** - * @internal This class is not covered by the backward compatibility promise for PHPUnit + * Inspired by https://github.com/nikic/PHP-Parser/tree/36a6dcd04e7b0285e8f0868f44bd4927802f7df1 + * + * Copyright (c) 2011, Nikita Popov + * All rights reserved. */ -interface Exception extends Throwable +abstract class AbstractNodeVisitor implements NodeVisitor { + public function beforeTraverse(array $nodes) : ?array + { + return null; + } + public function enterNode(Node $node) + { + return null; + } + public function leaveNode(Node $node) + { + return null; + } + public function afterTraverse(array $nodes) : ?array + { + return null; + } } - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework; +namespace PHPUnitPHAR\PHPStan\PhpDocParser\Ast; -use const DEBUG_BACKTRACE_IGNORE_ARGS; -use const PHP_EOL; -use function array_shift; -use function array_unshift; -use function assert; -use function class_exists; -use function count; -use function debug_backtrace; -use function explode; -use function file_get_contents; -use function func_get_args; -use function implode; -use function interface_exists; -use function is_array; -use function is_bool; -use function is_int; -use function is_iterable; -use function is_object; -use function is_string; -use function preg_match; -use function preg_split; +final class Attribute +{ + public const START_LINE = 'startLine'; + public const END_LINE = 'endLine'; + public const START_INDEX = 'startIndex'; + public const END_INDEX = 'endIndex'; + public const ORIGINAL_NODE = 'originalNode'; +} +key = $key; + $this->value = $value; } - /** - * Asserts that an array does not have a specified key. - * - * @param int|string $key - * @param array|ArrayAccess $array - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws Exception - * @throws ExpectationFailedException - */ - public static function assertArrayNotHasKey($key, $array, string $message = '') : void + public function __toString() : string { - if (!(is_int($key) || is_string($key))) { - throw \PHPUnit\Framework\InvalidArgumentException::create(1, 'integer or string'); - } - if (!(is_array($array) || $array instanceof ArrayAccess)) { - throw \PHPUnit\Framework\InvalidArgumentException::create(2, 'array or ArrayAccess'); + if ($this->key !== null) { + return sprintf('%s => %s', $this->key, $this->value); } - $constraint = new LogicalNot(new ArrayHasKey($key)); - static::assertThat($array, $constraint, $message); + return (string) $this->value; } +} +items = $items; } - public static function assertContainsEquals($needle, iterable $haystack, string $message = '') : void + public function __toString() : string { - $constraint = new TraversableContainsEqual($needle); - static::assertThat($haystack, $constraint, $message); + return '[' . implode(', ', $this->items) . ']'; } - /** - * Asserts that a haystack does not contain a needle. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws Exception - * @throws ExpectationFailedException - */ - public static function assertNotContains($needle, iterable $haystack, string $message = '') : void +} +value = $value; } - /** - * Asserts that a haystack contains only values of a given type. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - */ - public static function assertContainsOnly(string $type, iterable $haystack, ?bool $isNativeType = null, string $message = '') : void + public function __toString() : string { - if ($isNativeType === null) { - $isNativeType = Type::isType($type); - } - static::assertThat($haystack, new TraversableContainsOnly($type, $isNativeType), $message); + return $this->value; } - /** - * Asserts that a haystack contains only instances of a given class name. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - */ - public static function assertContainsOnlyInstancesOf(string $className, iterable $haystack, string $message = '') : void +} +value = $value; } - /** - * Asserts that a haystack does not contain only values of a given type. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - */ - public static function assertNotContainsOnly(string $type, iterable $haystack, ?bool $isNativeType = null, string $message = '') : void + public function __toString() : string { - if ($isNativeType === null) { - $isNativeType = Type::isType($type); - } - static::assertThat($haystack, new LogicalNot(new TraversableContainsOnly($type, $isNativeType)), $message); + return $this->value; } - /** - * Asserts the number of elements of an array, Countable or Traversable. - * - * @param Countable|iterable $haystack - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws Exception - * @throws ExpectationFailedException - */ - public static function assertCount(int $expectedCount, $haystack, string $message = '') : void +} +value = $value; + } + public function __toString() : string + { + return $this->value; + } +} +className = $className; + $this->name = $name; + } + public function __toString() : string + { + if ($this->className === '') { + return $this->name; } - static::assertThat($haystack, new Count($expectedCount), $message); + return "{$this->className}::{$this->name}"; + } +} +value = $value; + } + public function __toString() : string + { + return self::escape($this->value); + } + public static function unescape(string $value) : string + { + // from https://github.com/doctrine/annotations/blob/a9ec7af212302a75d1f92fa65d3abfbd16245a2a/lib/Doctrine/Common/Annotations/DocLexer.php#L103-L107 + return str_replace('""', '"', substr($value, 1, strlen($value) - 2)); + } + private static function escape(string $value) : string + { + // from https://github.com/phpstan/phpdoc-parser/issues/205#issuecomment-1662323656 + return sprintf('"%s"', str_replace('"', '""', $value)); } +} +quoteType = $quoteType; + } + public function __toString() : string + { + if ($this->quoteType === self::SINGLE_QUOTED) { + // from https://github.com/nikic/PHP-Parser/blob/0ffddce52d816f72d0efc4d9b02e276d3309ef01/lib/PhpParser/PrettyPrinter/Standard.php#L1007 + return sprintf("'%s'", addcslashes($this->value, '\'\\')); } - $constraint = new LogicalNot(new Count($expectedCount)); - static::assertThat($haystack, $constraint, $message); + // from https://github.com/nikic/PHP-Parser/blob/0ffddce52d816f72d0efc4d9b02e276d3309ef01/lib/PhpParser/PrettyPrinter/Standard.php#L1010-L1040 + return sprintf('"%s"', $this->escapeDoubleQuotedString()); } - /** - * Asserts that two variables are equal. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - */ - public static function assertEquals($expected, $actual, string $message = '') : void + private function escapeDoubleQuotedString() : string { - $constraint = new IsEqual($expected); - static::assertThat($actual, $constraint, $message); + $quote = '"'; + $escaped = addcslashes($this->value, "\n\r\t\f\v\$" . $quote . '\\'); + // Escape control characters and non-UTF-8 characters. + // Regex based on https://stackoverflow.com/a/11709412/385378. + $regex = '/( + [\\x00-\\x08\\x0E-\\x1F] # Control characters + | [\\xC0-\\xC1] # Invalid UTF-8 Bytes + | [\\xF5-\\xFF] # Invalid UTF-8 Bytes + | \\xE0(?=[\\x80-\\x9F]) # Overlong encoding of prior code point + | \\xF0(?=[\\x80-\\x8F]) # Overlong encoding of prior code point + | [\\xC2-\\xDF](?![\\x80-\\xBF]) # Invalid UTF-8 Sequence Start + | [\\xE0-\\xEF](?![\\x80-\\xBF]{2}) # Invalid UTF-8 Sequence Start + | [\\xF0-\\xF4](?![\\x80-\\xBF]{3}) # Invalid UTF-8 Sequence Start + | (?<=[\\x00-\\x7F\\xF5-\\xFF])[\\x80-\\xBF] # Invalid UTF-8 Sequence Middle + | (? */ + private $attributes = []; + /** + * @param mixed $value + */ + public function setAttribute(string $key, $value) : void { - $constraint = new IsEqualIgnoringCase($expected); - static::assertThat($actual, $constraint, $message); + $this->attributes[$key] = $value; + } + public function hasAttribute(string $key) : bool + { + return array_key_exists($key, $this->attributes); } /** - * Asserts that two variables are equal (with delta). - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException + * @return mixed */ - public static function assertEqualsWithDelta($expected, $actual, float $delta, string $message = '') : void + public function getAttribute(string $key) { - $constraint = new IsEqualWithDelta($expected, $delta); - static::assertThat($actual, $constraint, $message); + if ($this->hasAttribute($key)) { + return $this->attributes[$key]; + } + return null; } +} + Visitors */ + private $visitors = []; + /** @var bool Whether traversal should be stopped */ + private $stopTraversal; /** - * @throws ExpectationFailedException + * @param list $visitors */ - public static function assertObjectEquals(object $expected, object $actual, string $method = 'equals', string $message = '') : void + public function __construct(array $visitors) { - static::assertThat($actual, static::objectEquals($expected, $method), $message); + $this->visitors = $visitors; } /** - * Asserts that a variable is empty. + * Traverses an array of nodes using the registered visitors. * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException + * @param Node[] $nodes Array of nodes * - * @psalm-assert empty $actual + * @return Node[] Traversed array of nodes */ - public static function assertEmpty($actual, string $message = '') : void + public function traverse(array $nodes) : array { - if ($actual instanceof Generator) { - self::createWarning('Passing an argument of type Generator for the $actual parameter is deprecated. Support for this will be removed in PHPUnit 10.'); + $this->stopTraversal = \false; + foreach ($this->visitors as $visitor) { + $return = $visitor->beforeTraverse($nodes); + if ($return === null) { + continue; + } + $nodes = $return; } - static::assertThat($actual, static::isEmpty(), $message); + $nodes = $this->traverseArray($nodes); + foreach ($this->visitors as $visitor) { + $return = $visitor->afterTraverse($nodes); + if ($return === null) { + continue; + } + $nodes = $return; + } + return $nodes; } /** - * Asserts that a variable is not empty. + * Recursively traverse a node. * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException + * @param Node $node Node to traverse. * - * @psalm-assert !empty $actual + * @return Node Result of traversal (may be original node or new one) */ - public static function assertNotEmpty($actual, string $message = '') : void + private function traverseNode(Node $node) : Node { - if ($actual instanceof Generator) { - self::createWarning('Passing an argument of type Generator for the $actual parameter is deprecated. Support for this will be removed in PHPUnit 10.'); - } - static::assertThat($actual, static::logicalNot(static::isEmpty()), $message); - } - /** - * Asserts that a value is greater than another value. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - */ - public static function assertGreaterThan($expected, $actual, string $message = '') : void - { - static::assertThat($actual, static::greaterThan($expected), $message); + $subNodeNames = array_keys(get_object_vars($node)); + foreach ($subNodeNames as $name) { + $subNode =& $node->{$name}; + if (is_array($subNode)) { + $subNode = $this->traverseArray($subNode); + if ($this->stopTraversal) { + break; + } + } elseif ($subNode instanceof Node) { + $traverseChildren = \true; + $breakVisitorIndex = null; + foreach ($this->visitors as $visitorIndex => $visitor) { + $return = $visitor->enterNode($subNode); + if ($return === null) { + continue; + } + if ($return instanceof Node) { + $this->ensureReplacementReasonable($subNode, $return); + $subNode = $return; + } elseif ($return === self::DONT_TRAVERSE_CHILDREN) { + $traverseChildren = \false; + } elseif ($return === self::DONT_TRAVERSE_CURRENT_AND_CHILDREN) { + $traverseChildren = \false; + $breakVisitorIndex = $visitorIndex; + break; + } elseif ($return === self::STOP_TRAVERSAL) { + $this->stopTraversal = \true; + break 2; + } else { + throw new LogicException('enterNode() returned invalid value of type ' . gettype($return)); + } + } + if ($traverseChildren) { + $subNode = $this->traverseNode($subNode); + if ($this->stopTraversal) { + break; + } + } + foreach ($this->visitors as $visitorIndex => $visitor) { + $return = $visitor->leaveNode($subNode); + if ($return !== null) { + if ($return instanceof Node) { + $this->ensureReplacementReasonable($subNode, $return); + $subNode = $return; + } elseif ($return === self::STOP_TRAVERSAL) { + $this->stopTraversal = \true; + break 2; + } elseif (is_array($return)) { + throw new LogicException('leaveNode() may only return an array ' . 'if the parent structure is an array'); + } else { + throw new LogicException('leaveNode() returned invalid value of type ' . gettype($return)); + } + } + if ($breakVisitorIndex === $visitorIndex) { + break; + } + } + } + } + return $node; } /** - * Asserts that a value is greater than or equal to another value. + * Recursively traverse array (usually of nodes). * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException + * @param mixed[] $nodes Array to traverse + * + * @return mixed[] Result of traversal (may be original array or changed one) */ - public static function assertGreaterThanOrEqual($expected, $actual, string $message = '') : void + private function traverseArray(array $nodes) : array { - static::assertThat($actual, static::greaterThanOrEqual($expected), $message); + $doNodes = []; + foreach ($nodes as $i => &$node) { + if ($node instanceof Node) { + $traverseChildren = \true; + $breakVisitorIndex = null; + foreach ($this->visitors as $visitorIndex => $visitor) { + $return = $visitor->enterNode($node); + if ($return === null) { + continue; + } + if ($return instanceof Node) { + $this->ensureReplacementReasonable($node, $return); + $node = $return; + } elseif (is_array($return)) { + $doNodes[] = [$i, $return]; + continue 2; + } elseif ($return === self::REMOVE_NODE) { + $doNodes[] = [$i, []]; + continue 2; + } elseif ($return === self::DONT_TRAVERSE_CHILDREN) { + $traverseChildren = \false; + } elseif ($return === self::DONT_TRAVERSE_CURRENT_AND_CHILDREN) { + $traverseChildren = \false; + $breakVisitorIndex = $visitorIndex; + break; + } elseif ($return === self::STOP_TRAVERSAL) { + $this->stopTraversal = \true; + break 2; + } else { + throw new LogicException('enterNode() returned invalid value of type ' . gettype($return)); + } + } + if ($traverseChildren) { + $node = $this->traverseNode($node); + if ($this->stopTraversal) { + break; + } + } + foreach ($this->visitors as $visitorIndex => $visitor) { + $return = $visitor->leaveNode($node); + if ($return !== null) { + if ($return instanceof Node) { + $this->ensureReplacementReasonable($node, $return); + $node = $return; + } elseif (is_array($return)) { + $doNodes[] = [$i, $return]; + break; + } elseif ($return === self::REMOVE_NODE) { + $doNodes[] = [$i, []]; + break; + } elseif ($return === self::STOP_TRAVERSAL) { + $this->stopTraversal = \true; + break 2; + } else { + throw new LogicException('leaveNode() returned invalid value of type ' . gettype($return)); + } + } + if ($breakVisitorIndex === $visitorIndex) { + break; + } + } + } elseif (is_array($node)) { + throw new LogicException('Invalid node structure: Contains nested arrays'); + } + } + if (count($doNodes) > 0) { + while ([$i, $replace] = array_pop($doNodes)) { + array_splice($nodes, $i, 1, $replace); + } + } + return $nodes; } - /** - * Asserts that a value is smaller than another value. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - */ - public static function assertLessThan($expected, $actual, string $message = '') : void + private function ensureReplacementReasonable(Node $old, Node $new) : void { - static::assertThat($actual, static::lessThan($expected), $message); + if ($old instanceof TypeNode && !$new instanceof TypeNode) { + throw new LogicException(sprintf('Trying to replace TypeNode with %s', get_class($new))); + } + if ($old instanceof ConstExprNode && !$new instanceof ConstExprNode) { + throw new LogicException(sprintf('Trying to replace ConstExprNode with %s', get_class($new))); + } + if ($old instanceof PhpDocChildNode && !$new instanceof PhpDocChildNode) { + throw new LogicException(sprintf('Trying to replace PhpDocChildNode with %s', get_class($new))); + } + if ($old instanceof PhpDocTagValueNode && !$new instanceof PhpDocTagValueNode) { + throw new LogicException(sprintf('Trying to replace PhpDocTagValueNode with %s', get_class($new))); + } } +} + $node stays as-is + * * array (of Nodes) + * => The return value is merged into the parent array (at the position of the $node) + * * NodeTraverser::REMOVE_NODE + * => $node is removed from the parent array + * * NodeTraverser::DONT_TRAVERSE_CHILDREN + * => Children of $node are not traversed. $node stays as-is + * * NodeTraverser::DONT_TRAVERSE_CURRENT_AND_CHILDREN + * => Further visitors for the current node are skipped, and its children are not + * traversed. $node stays as-is. + * * NodeTraverser::STOP_TRAVERSAL + * => Traversal is aborted. $node stays as-is + * * otherwise + * => $node is set to the return value + * + * @param Node $node Node + * + * @return Node|Node[]|NodeTraverser::*|null Replacement node (or special return value) */ - public static function assertFileEquals(string $expected, string $actual, string $message = '') : void - { - static::assertFileExists($expected, $message); - static::assertFileExists($actual, $message); - $constraint = new IsEqual(file_get_contents($expected)); - static::assertThat(file_get_contents($actual), $constraint, $message); - } + public function enterNode(Node $node); /** - * Asserts that the contents of one file is equal to the contents of another - * file (canonicalizing). + * Called when leaving a node. * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException + * Return value semantics: + * * null + * => $node stays as-is + * * NodeTraverser::REMOVE_NODE + * => $node is removed from the parent array + * * NodeTraverser::STOP_TRAVERSAL + * => Traversal is aborted. $node stays as-is + * * array (of Nodes) + * => The return value is merged into the parent array (at the position of the $node) + * * otherwise + * => $node is set to the return value + * + * @param Node $node Node + * + * @return Node|Node[]|NodeTraverser::REMOVE_NODE|NodeTraverser::STOP_TRAVERSAL|null Replacement node (or special return value) */ - public static function assertFileEqualsCanonicalizing(string $expected, string $actual, string $message = '') : void - { - static::assertFileExists($expected, $message); - static::assertFileExists($actual, $message); - $constraint = new IsEqualCanonicalizing(file_get_contents($expected)); - static::assertThat(file_get_contents($actual), $constraint, $message); - } + public function leaveNode(Node $node); /** - * Asserts that the contents of one file is equal to the contents of another - * file (ignoring case). + * Called once after traversal. * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException + * Return value semantics: + * * null: $nodes stays as-is + * * otherwise: $nodes is set to the return value + * + * @param Node[] $nodes Array of nodes + * + * @return Node[]|null Array of nodes */ - public static function assertFileEqualsIgnoringCase(string $expected, string $actual, string $message = '') : void + public function afterTraverse(array $nodes) : ?array; +} +setAttribute(Attribute::ORIGINAL_NODE, $originalNode); + return $node; } - /** - * Asserts that the contents of one file is not equal to the contents of - * another file. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - */ - public static function assertFileNotEquals(string $expected, string $actual, string $message = '') : void +} +type = $type; + $this->parameter = $parameter; + $this->method = $method; + $this->isNegated = $isNegated; + $this->isEquality = $isEquality; + $this->description = $description; } - /** - * Asserts that the contents of one file is not equal to the contents of another - * file (canonicalizing). - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - */ - public static function assertFileNotEqualsCanonicalizing(string $expected, string $actual, string $message = '') : void + public function __toString() : string { - static::assertFileExists($expected, $message); - static::assertFileExists($actual, $message); - $constraint = new LogicalNot(new IsEqualCanonicalizing(file_get_contents($expected))); - static::assertThat(file_get_contents($actual), $constraint, $message); + $isNegated = $this->isNegated ? '!' : ''; + $isEquality = $this->isEquality ? '=' : ''; + return trim("{$isNegated}{$isEquality}{$this->type} {$this->parameter}->{$this->method}() {$this->description}"); } - /** - * Asserts that the contents of one file is not equal to the contents of another - * file (ignoring case). - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - */ - public static function assertFileNotEqualsIgnoringCase(string $expected, string $actual, string $message = '') : void +} +type = $type; + $this->parameter = $parameter; + $this->property = $property; + $this->isNegated = $isNegated; + $this->isEquality = $isEquality; + $this->description = $description; } - /** - * Asserts that the contents of a string is equal - * to the contents of a file. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - */ - public static function assertStringEqualsFile(string $expectedFile, string $actualString, string $message = '') : void + public function __toString() : string { - static::assertFileExists($expectedFile, $message); - $constraint = new IsEqual(file_get_contents($expectedFile)); - static::assertThat($actualString, $constraint, $message); + $isNegated = $this->isNegated ? '!' : ''; + $isEquality = $this->isEquality ? '=' : ''; + return trim("{$isNegated}{$isEquality}{$this->type} {$this->parameter}->{$this->property} {$this->description}"); } - /** - * Asserts that the contents of a string is equal - * to the contents of a file (canonicalizing). - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - */ - public static function assertStringEqualsFileCanonicalizing(string $expectedFile, string $actualString, string $message = '') : void +} +type = $type; + $this->parameter = $parameter; + $this->isNegated = $isNegated; + $this->isEquality = $isEquality; + $this->description = $description; } - /** - * Asserts that the contents of a string is equal - * to the contents of a file (ignoring case). - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - */ - public static function assertStringEqualsFileIgnoringCase(string $expectedFile, string $actualString, string $message = '') : void + public function __toString() : string { - static::assertFileExists($expectedFile, $message); - $constraint = new IsEqualIgnoringCase(file_get_contents($expectedFile)); - static::assertThat($actualString, $constraint, $message); + $isNegated = $this->isNegated ? '!' : ''; + $isEquality = $this->isEquality ? '=' : ''; + return trim("{$isNegated}{$isEquality}{$this->type} {$this->parameter} {$this->description}"); } - /** - * Asserts that the contents of a string is not equal - * to the contents of a file. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - */ - public static function assertStringNotEqualsFile(string $expectedFile, string $actualString, string $message = '') : void +} +description = $description; } - /** - * Asserts that the contents of a string is not equal - * to the contents of a file (canonicalizing). - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - */ - public static function assertStringNotEqualsFileCanonicalizing(string $expectedFile, string $actualString, string $message = '') : void + public function __toString() : string { - static::assertFileExists($expectedFile, $message); - $constraint = new LogicalNot(new IsEqualCanonicalizing(file_get_contents($expectedFile))); - static::assertThat($actualString, $constraint, $message); + return trim($this->description); } +} + */ + public $arguments; /** - * Asserts that the contents of a string is not equal - * to the contents of a file (ignoring case). - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException + * @param list $arguments */ - public static function assertStringNotEqualsFileIgnoringCase(string $expectedFile, string $actualString, string $message = '') : void + public function __construct(string $name, array $arguments) { - static::assertFileExists($expectedFile, $message); - $constraint = new LogicalNot(new IsEqualIgnoringCase(file_get_contents($expectedFile))); - static::assertThat($actualString, $constraint, $message); + $this->name = $name; + $this->arguments = $arguments; } - /** - * Asserts that a file/dir is readable. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - */ - public static function assertIsReadable(string $filename, string $message = '') : void + public function __toString() : string { - static::assertThat($filename, new IsReadable(), $message); + $arguments = implode(', ', $this->arguments); + return $this->name . '(' . $arguments . ')'; } +} +key = $key; + $this->value = $value; } - /** - * Asserts that a file/dir exists and is not readable. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - * - * @codeCoverageIgnore - * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4062 - */ - public static function assertNotIsReadable(string $filename, string $message = '') : void + public function __toString() : string { - self::createWarning('assertNotIsReadable() is deprecated and will be removed in PHPUnit 10. Refactor your code to use assertIsNotReadable() instead.'); - static::assertThat($filename, new LogicalNot(new IsReadable()), $message); + if ($this->key === null) { + return (string) $this->value; + } + return $this->key . '=' . $this->value; } +} + */ + public $items; /** - * Asserts that a file/dir exists and is writable. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException + * @param list $items */ - public static function assertIsWritable(string $filename, string $message = '') : void + public function __construct(array $items) { - static::assertThat($filename, new IsWritable(), $message); + $this->items = $items; } - /** - * Asserts that a file/dir exists and is not writable. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - */ - public static function assertIsNotWritable(string $filename, string $message = '') : void + public function __toString() : string { - static::assertThat($filename, new LogicalNot(new IsWritable()), $message); + $items = implode(', ', $this->items); + return '{' . $items . '}'; } +} +key = $key; + $this->value = $value; } - /** - * Asserts that a directory exists. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - */ - public static function assertDirectoryExists(string $directory, string $message = '') : void + public function __toString() : string { - static::assertThat($directory, new DirectoryExists(), $message); + if ($this->key === null) { + return (string) $this->value; + } + return $this->key . '=' . $this->value; } - /** - * Asserts that a directory does not exist. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - */ - public static function assertDirectoryDoesNotExist(string $directory, string $message = '') : void +} +annotation = $annotation; + $this->description = $description; } - /** - * Asserts that a directory does not exist. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - * - * @codeCoverageIgnore - * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4068 - */ - public static function assertDirectoryNotExists(string $directory, string $message = '') : void + public function __toString() : string { - self::createWarning('assertDirectoryNotExists() is deprecated and will be removed in PHPUnit 10. Refactor your code to use assertDirectoryDoesNotExist() instead.'); - static::assertThat($directory, new LogicalNot(new DirectoryExists()), $message); + return trim("{$this->annotation} {$this->description}"); } - /** - * Asserts that a directory exists and is readable. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - */ - public static function assertDirectoryIsReadable(string $directory, string $message = '') : void +} +type = $type; + $this->description = $description; + } + public function __toString() : string + { + return trim("{$this->type} {$this->description}"); + } +} +value = $value; + } + public function __toString() : string + { + return $this->value; + } +} +type = $type; + $this->description = $description; + } + public function __toString() : string + { + return trim("{$this->type} {$this->description}"); + } +} +value = $value; + $this->exceptionArgs = [$exception->getCurrentTokenValue(), $exception->getCurrentTokenType(), $exception->getCurrentOffset(), $exception->getExpectedTokenType(), $exception->getExpectedTokenValue(), $exception->getCurrentTokenLine()]; + } + public function __get(string $name) : ?ParserException + { + if ($name !== 'exception') { + trigger_error(sprintf('Undefined property: %s::$%s', self::class, $name), E_USER_WARNING); + return null; + } + return new ParserException(...$this->exceptionArgs); + } + public function __toString() : string + { + return $this->value; } +} +isStatic = $isStatic; + $this->returnType = $returnType; + $this->methodName = $methodName; + $this->parameters = $parameters; + $this->description = $description; + $this->templateTypes = $templateTypes; + } + public function __toString() : string + { + $static = $this->isStatic ? 'static ' : ''; + $returnType = $this->returnType !== null ? "{$this->returnType} " : ''; + $parameters = implode(', ', $this->parameters); + $description = $this->description !== '' ? " {$this->description}" : ''; + $templateTypes = count($this->templateTypes) > 0 ? '<' . implode(', ', $this->templateTypes) . '>' : ''; + return "{$static}{$returnType}{$this->methodName}{$templateTypes}({$parameters}){$description}"; + } +} +type = $type; + $this->isReference = $isReference; + $this->isVariadic = $isVariadic; + $this->parameterName = $parameterName; + $this->defaultValue = $defaultValue; + } + public function __toString() : string + { + $type = $this->type !== null ? "{$this->type} " : ''; + $isReference = $this->isReference ? '&' : ''; + $isVariadic = $this->isVariadic ? '...' : ''; + $default = $this->defaultValue !== null ? " = {$this->defaultValue}" : ''; + return "{$type}{$isReference}{$isVariadic}{$this->parameterName}{$default}"; + } +} +type = $type; + $this->description = $description; + } + public function __toString() : string + { + return trim("{$this->type} {$this->description}"); + } +} +type = $type; + $this->parameterName = $parameterName; + $this->description = $description; + } + public function __toString() : string + { + return trim("{$this->type} {$this->parameterName} {$this->description}"); + } +} +parameterName = $parameterName; + $this->description = $description; + } + public function __toString() : string + { + return trim("{$this->parameterName} {$this->description}"); + } +} +parameterName = $parameterName; + $this->description = $description; + } + public function __toString() : string + { + return trim("{$this->parameterName} {$this->description}"); + } +} +type = $type; + $this->parameterName = $parameterName; + $this->description = $description; + } + public function __toString() : string + { + return trim("{$this->type} {$this->parameterName} {$this->description}"); + } +} +type = $type; + $this->isReference = $isReference; + $this->isVariadic = $isVariadic; + $this->parameterName = $parameterName; + $this->description = $description; } + public function __toString() : string + { + $reference = $this->isReference ? '&' : ''; + $variadic = $this->isVariadic ? '...' : ''; + return trim("{$this->type} {$reference}{$variadic}{$this->parameterName} {$this->description}"); + } +} +children = $children; } /** - * Asserts that a directory exists and is writable. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException + * @return PhpDocTagNode[] */ - public static function assertDirectoryIsWritable(string $directory, string $message = '') : void + public function getTags() : array { - self::assertDirectoryExists($directory, $message); - self::assertIsWritable($directory, $message); + return array_filter($this->children, static function (PhpDocChildNode $child) : bool { + return $child instanceof PhpDocTagNode; + }); } /** - * Asserts that a directory exists and is not writable. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException + * @return PhpDocTagNode[] */ - public static function assertDirectoryIsNotWritable(string $directory, string $message = '') : void + public function getTagsByName(string $tagName) : array { - self::assertDirectoryExists($directory, $message); - self::assertIsNotWritable($directory, $message); + return array_filter($this->getTags(), static function (PhpDocTagNode $tag) use($tagName) : bool { + return $tag->name === $tagName; + }); } /** - * Asserts that a directory exists and is not writable. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - * - * @codeCoverageIgnore - * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4074 + * @return VarTagValueNode[] */ - public static function assertDirectoryNotIsWritable(string $directory, string $message = '') : void + public function getVarTagValues(string $tagName = '@var') : array { - self::createWarning('assertDirectoryNotIsWritable() is deprecated and will be removed in PHPUnit 10. Refactor your code to use assertDirectoryIsNotWritable() instead.'); - self::assertDirectoryExists($directory, $message); - self::assertIsNotWritable($directory, $message); + return array_filter(array_column($this->getTagsByName($tagName), 'value'), static function (PhpDocTagValueNode $value) : bool { + return $value instanceof VarTagValueNode; + }); } /** - * Asserts that a file exists. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException + * @return ParamTagValueNode[] */ - public static function assertFileExists(string $filename, string $message = '') : void + public function getParamTagValues(string $tagName = '@param') : array { - static::assertThat($filename, new FileExists(), $message); + return array_filter(array_column($this->getTagsByName($tagName), 'value'), static function (PhpDocTagValueNode $value) : bool { + return $value instanceof ParamTagValueNode; + }); } /** - * Asserts that a file does not exist. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException + * @return TypelessParamTagValueNode[] */ - public static function assertFileDoesNotExist(string $filename, string $message = '') : void + public function getTypelessParamTagValues(string $tagName = '@param') : array { - static::assertThat($filename, new LogicalNot(new FileExists()), $message); + return array_filter(array_column($this->getTagsByName($tagName), 'value'), static function (PhpDocTagValueNode $value) : bool { + return $value instanceof TypelessParamTagValueNode; + }); } /** - * Asserts that a file does not exist. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - * - * @codeCoverageIgnore - * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4077 + * @return ParamImmediatelyInvokedCallableTagValueNode[] */ - public static function assertFileNotExists(string $filename, string $message = '') : void + public function getParamImmediatelyInvokedCallableTagValues(string $tagName = '@param-immediately-invoked-callable') : array { - self::createWarning('assertFileNotExists() is deprecated and will be removed in PHPUnit 10. Refactor your code to use assertFileDoesNotExist() instead.'); - static::assertThat($filename, new LogicalNot(new FileExists()), $message); + return array_filter(array_column($this->getTagsByName($tagName), 'value'), static function (PhpDocTagValueNode $value) : bool { + return $value instanceof ParamImmediatelyInvokedCallableTagValueNode; + }); } /** - * Asserts that a file exists and is readable. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException + * @return ParamLaterInvokedCallableTagValueNode[] */ - public static function assertFileIsReadable(string $file, string $message = '') : void + public function getParamLaterInvokedCallableTagValues(string $tagName = '@param-later-invoked-callable') : array { - self::assertFileExists($file, $message); - self::assertIsReadable($file, $message); + return array_filter(array_column($this->getTagsByName($tagName), 'value'), static function (PhpDocTagValueNode $value) : bool { + return $value instanceof ParamLaterInvokedCallableTagValueNode; + }); } /** - * Asserts that a file exists and is not readable. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException + * @return ParamClosureThisTagValueNode[] */ - public static function assertFileIsNotReadable(string $file, string $message = '') : void + public function getParamClosureThisTagValues(string $tagName = '@param-closure-this') : array { - self::assertFileExists($file, $message); - self::assertIsNotReadable($file, $message); + return array_filter(array_column($this->getTagsByName($tagName), 'value'), static function (PhpDocTagValueNode $value) : bool { + return $value instanceof ParamClosureThisTagValueNode; + }); } /** - * Asserts that a file exists and is not readable. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - * - * @codeCoverageIgnore - * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4080 + * @return TemplateTagValueNode[] */ - public static function assertFileNotIsReadable(string $file, string $message = '') : void + public function getTemplateTagValues(string $tagName = '@template') : array { - self::createWarning('assertFileNotIsReadable() is deprecated and will be removed in PHPUnit 10. Refactor your code to use assertFileIsNotReadable() instead.'); - self::assertFileExists($file, $message); - self::assertIsNotReadable($file, $message); + return array_filter(array_column($this->getTagsByName($tagName), 'value'), static function (PhpDocTagValueNode $value) : bool { + return $value instanceof TemplateTagValueNode; + }); } /** - * Asserts that a file exists and is writable. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException + * @return ExtendsTagValueNode[] */ - public static function assertFileIsWritable(string $file, string $message = '') : void + public function getExtendsTagValues(string $tagName = '@extends') : array { - self::assertFileExists($file, $message); - self::assertIsWritable($file, $message); + return array_filter(array_column($this->getTagsByName($tagName), 'value'), static function (PhpDocTagValueNode $value) : bool { + return $value instanceof ExtendsTagValueNode; + }); } /** - * Asserts that a file exists and is not writable. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException + * @return ImplementsTagValueNode[] */ - public static function assertFileIsNotWritable(string $file, string $message = '') : void + public function getImplementsTagValues(string $tagName = '@implements') : array { - self::assertFileExists($file, $message); - self::assertIsNotWritable($file, $message); + return array_filter(array_column($this->getTagsByName($tagName), 'value'), static function (PhpDocTagValueNode $value) : bool { + return $value instanceof ImplementsTagValueNode; + }); } /** - * Asserts that a file exists and is not writable. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - * - * @codeCoverageIgnore - * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4083 + * @return UsesTagValueNode[] */ - public static function assertFileNotIsWritable(string $file, string $message = '') : void + public function getUsesTagValues(string $tagName = '@use') : array { - self::createWarning('assertFileNotIsWritable() is deprecated and will be removed in PHPUnit 10. Refactor your code to use assertFileIsNotWritable() instead.'); - self::assertFileExists($file, $message); - self::assertIsNotWritable($file, $message); + return array_filter(array_column($this->getTagsByName($tagName), 'value'), static function (PhpDocTagValueNode $value) : bool { + return $value instanceof UsesTagValueNode; + }); } /** - * Asserts that a condition is true. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - * - * @psalm-assert true $condition + * @return ReturnTagValueNode[] */ - public static function assertTrue($condition, string $message = '') : void + public function getReturnTagValues(string $tagName = '@return') : array { - static::assertThat($condition, static::isTrue(), $message); + return array_filter(array_column($this->getTagsByName($tagName), 'value'), static function (PhpDocTagValueNode $value) : bool { + return $value instanceof ReturnTagValueNode; + }); } /** - * Asserts that a condition is not true. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - * - * @psalm-assert !true $condition + * @return ThrowsTagValueNode[] */ - public static function assertNotTrue($condition, string $message = '') : void + public function getThrowsTagValues(string $tagName = '@throws') : array { - static::assertThat($condition, static::logicalNot(static::isTrue()), $message); + return array_filter(array_column($this->getTagsByName($tagName), 'value'), static function (PhpDocTagValueNode $value) : bool { + return $value instanceof ThrowsTagValueNode; + }); } /** - * Asserts that a condition is false. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - * - * @psalm-assert false $condition + * @return MixinTagValueNode[] */ - public static function assertFalse($condition, string $message = '') : void + public function getMixinTagValues(string $tagName = '@mixin') : array { - static::assertThat($condition, static::isFalse(), $message); + return array_filter(array_column($this->getTagsByName($tagName), 'value'), static function (PhpDocTagValueNode $value) : bool { + return $value instanceof MixinTagValueNode; + }); } /** - * Asserts that a condition is not false. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - * - * @psalm-assert !false $condition + * @return RequireExtendsTagValueNode[] */ - public static function assertNotFalse($condition, string $message = '') : void + public function getRequireExtendsTagValues(string $tagName = '@phpstan-require-extends') : array { - static::assertThat($condition, static::logicalNot(static::isFalse()), $message); + return array_filter(array_column($this->getTagsByName($tagName), 'value'), static function (PhpDocTagValueNode $value) : bool { + return $value instanceof RequireExtendsTagValueNode; + }); } /** - * Asserts that a variable is null. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - * - * @psalm-assert null $actual + * @return RequireImplementsTagValueNode[] */ - public static function assertNull($actual, string $message = '') : void + public function getRequireImplementsTagValues(string $tagName = '@phpstan-require-implements') : array { - static::assertThat($actual, static::isNull(), $message); + return array_filter(array_column($this->getTagsByName($tagName), 'value'), static function (PhpDocTagValueNode $value) : bool { + return $value instanceof RequireImplementsTagValueNode; + }); } /** - * Asserts that a variable is not null. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - * - * @psalm-assert !null $actual + * @return DeprecatedTagValueNode[] */ - public static function assertNotNull($actual, string $message = '') : void + public function getDeprecatedTagValues() : array { - static::assertThat($actual, static::logicalNot(static::isNull()), $message); + return array_filter(array_column($this->getTagsByName('@deprecated'), 'value'), static function (PhpDocTagValueNode $value) : bool { + return $value instanceof DeprecatedTagValueNode; + }); } /** - * Asserts that a variable is finite. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException + * @return PropertyTagValueNode[] */ - public static function assertFinite($actual, string $message = '') : void + public function getPropertyTagValues(string $tagName = '@property') : array { - static::assertThat($actual, static::isFinite(), $message); + return array_filter(array_column($this->getTagsByName($tagName), 'value'), static function (PhpDocTagValueNode $value) : bool { + return $value instanceof PropertyTagValueNode; + }); } /** - * Asserts that a variable is infinite. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException + * @return PropertyTagValueNode[] */ - public static function assertInfinite($actual, string $message = '') : void + public function getPropertyReadTagValues(string $tagName = '@property-read') : array { - static::assertThat($actual, static::isInfinite(), $message); + return array_filter(array_column($this->getTagsByName($tagName), 'value'), static function (PhpDocTagValueNode $value) : bool { + return $value instanceof PropertyTagValueNode; + }); } /** - * Asserts that a variable is nan. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException + * @return PropertyTagValueNode[] */ - public static function assertNan($actual, string $message = '') : void + public function getPropertyWriteTagValues(string $tagName = '@property-write') : array { - static::assertThat($actual, static::isNan(), $message); + return array_filter(array_column($this->getTagsByName($tagName), 'value'), static function (PhpDocTagValueNode $value) : bool { + return $value instanceof PropertyTagValueNode; + }); } /** - * Asserts that a class has a specified attribute. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws Exception - * @throws ExpectationFailedException - * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4601 + * @return MethodTagValueNode[] */ - public static function assertClassHasAttribute(string $attributeName, string $className, string $message = '') : void + public function getMethodTagValues(string $tagName = '@method') : array { - self::createWarning('assertClassHasAttribute() is deprecated and will be removed in PHPUnit 10.'); - if (!self::isValidClassAttributeName($attributeName)) { - throw \PHPUnit\Framework\InvalidArgumentException::create(1, 'valid attribute name'); - } - if (!class_exists($className)) { - throw \PHPUnit\Framework\InvalidArgumentException::create(2, 'class name'); - } - static::assertThat($className, new ClassHasAttribute($attributeName), $message); + return array_filter(array_column($this->getTagsByName($tagName), 'value'), static function (PhpDocTagValueNode $value) : bool { + return $value instanceof MethodTagValueNode; + }); } /** - * Asserts that a class does not have a specified attribute. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws Exception - * @throws ExpectationFailedException - * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4601 + * @return TypeAliasTagValueNode[] */ - public static function assertClassNotHasAttribute(string $attributeName, string $className, string $message = '') : void + public function getTypeAliasTagValues(string $tagName = '@phpstan-type') : array { - self::createWarning('assertClassNotHasAttribute() is deprecated and will be removed in PHPUnit 10.'); - if (!self::isValidClassAttributeName($attributeName)) { - throw \PHPUnit\Framework\InvalidArgumentException::create(1, 'valid attribute name'); - } - if (!class_exists($className)) { - throw \PHPUnit\Framework\InvalidArgumentException::create(2, 'class name'); - } - static::assertThat($className, new LogicalNot(new ClassHasAttribute($attributeName)), $message); + return array_filter(array_column($this->getTagsByName($tagName), 'value'), static function (PhpDocTagValueNode $value) : bool { + return $value instanceof TypeAliasTagValueNode; + }); } /** - * Asserts that a class has a specified static attribute. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws Exception - * @throws ExpectationFailedException - * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4601 + * @return TypeAliasImportTagValueNode[] */ - public static function assertClassHasStaticAttribute(string $attributeName, string $className, string $message = '') : void + public function getTypeAliasImportTagValues(string $tagName = '@phpstan-import-type') : array { - self::createWarning('assertClassHasStaticAttribute() is deprecated and will be removed in PHPUnit 10.'); - if (!self::isValidClassAttributeName($attributeName)) { - throw \PHPUnit\Framework\InvalidArgumentException::create(1, 'valid attribute name'); - } - if (!class_exists($className)) { - throw \PHPUnit\Framework\InvalidArgumentException::create(2, 'class name'); - } - static::assertThat($className, new ClassHasStaticAttribute($attributeName), $message); + return array_filter(array_column($this->getTagsByName($tagName), 'value'), static function (PhpDocTagValueNode $value) : bool { + return $value instanceof TypeAliasImportTagValueNode; + }); } /** - * Asserts that a class does not have a specified static attribute. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws Exception - * @throws ExpectationFailedException - * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4601 + * @return AssertTagValueNode[] */ - public static function assertClassNotHasStaticAttribute(string $attributeName, string $className, string $message = '') : void + public function getAssertTagValues(string $tagName = '@phpstan-assert') : array { - self::createWarning('assertClassNotHasStaticAttribute() is deprecated and will be removed in PHPUnit 10.'); - if (!self::isValidClassAttributeName($attributeName)) { - throw \PHPUnit\Framework\InvalidArgumentException::create(1, 'valid attribute name'); - } - if (!class_exists($className)) { - throw \PHPUnit\Framework\InvalidArgumentException::create(2, 'class name'); - } - static::assertThat($className, new LogicalNot(new ClassHasStaticAttribute($attributeName)), $message); + return array_filter(array_column($this->getTagsByName($tagName), 'value'), static function (PhpDocTagValueNode $value) : bool { + return $value instanceof AssertTagValueNode; + }); } /** - * Asserts that an object has a specified attribute. - * - * @param object $object - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws Exception - * @throws ExpectationFailedException - * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4601 + * @return AssertTagPropertyValueNode[] */ - public static function assertObjectHasAttribute(string $attributeName, $object, string $message = '') : void + public function getAssertPropertyTagValues(string $tagName = '@phpstan-assert') : array { - self::createWarning('assertObjectHasAttribute() is deprecated and will be removed in PHPUnit 10. Refactor your test to use assertObjectHasProperty() instead.'); - if (!self::isValidObjectAttributeName($attributeName)) { - throw \PHPUnit\Framework\InvalidArgumentException::create(1, 'valid attribute name'); - } - if (!is_object($object)) { - throw \PHPUnit\Framework\InvalidArgumentException::create(2, 'object'); - } - static::assertThat($object, new ObjectHasAttribute($attributeName), $message); + return array_filter(array_column($this->getTagsByName($tagName), 'value'), static function (PhpDocTagValueNode $value) : bool { + return $value instanceof AssertTagPropertyValueNode; + }); } /** - * Asserts that an object does not have a specified attribute. - * - * @param object $object - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws Exception - * @throws ExpectationFailedException - * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4601 + * @return AssertTagMethodValueNode[] */ - public static function assertObjectNotHasAttribute(string $attributeName, $object, string $message = '') : void + public function getAssertMethodTagValues(string $tagName = '@phpstan-assert') : array { - self::createWarning('assertObjectNotHasAttribute() is deprecated and will be removed in PHPUnit 10. Refactor your test to use assertObjectNotHasProperty() instead.'); - if (!self::isValidObjectAttributeName($attributeName)) { - throw \PHPUnit\Framework\InvalidArgumentException::create(1, 'valid attribute name'); - } - if (!is_object($object)) { - throw \PHPUnit\Framework\InvalidArgumentException::create(2, 'object'); - } - static::assertThat($object, new LogicalNot(new ObjectHasAttribute($attributeName)), $message); + return array_filter(array_column($this->getTagsByName($tagName), 'value'), static function (PhpDocTagValueNode $value) : bool { + return $value instanceof AssertTagMethodValueNode; + }); } /** - * Asserts that an object has a specified property. - * - * @throws ExpectationFailedException + * @return SelfOutTagValueNode[] */ - public static final function assertObjectHasProperty(string $propertyName, object $object, string $message = '') : void + public function getSelfOutTypeTagValues(string $tagName = '@phpstan-this-out') : array { - static::assertThat($object, new ObjectHasProperty($propertyName), $message); + return array_filter(array_column($this->getTagsByName($tagName), 'value'), static function (PhpDocTagValueNode $value) : bool { + return $value instanceof SelfOutTagValueNode; + }); } /** - * Asserts that an object does not have a specified property. - * - * @throws ExpectationFailedException + * @return ParamOutTagValueNode[] */ - public static final function assertObjectNotHasProperty(string $propertyName, object $object, string $message = '') : void + public function getParamOutTypeTagValues(string $tagName = '@param-out') : array { - static::assertThat($object, new LogicalNot(new ObjectHasProperty($propertyName)), $message); + return array_filter(array_column($this->getTagsByName($tagName), 'value'), static function (PhpDocTagValueNode $value) : bool { + return $value instanceof ParamOutTagValueNode; + }); } - /** - * Asserts that two variables have the same type and value. - * Used on objects, it asserts that two variables reference - * the same object. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - * - * @psalm-template ExpectedType - * - * @psalm-param ExpectedType $expected - * - * @psalm-assert =ExpectedType $actual - */ - public static function assertSame($expected, $actual, string $message = '') : void + public function __toString() : string { - static::assertThat($actual, new IsIdentical($expected), $message); + $children = array_map(static function (PhpDocChildNode $child) : string { + $s = (string) $child; + return $s === '' ? '' : ' ' . $s; + }, $this->children); + return "/**\n *" . implode("\n *", $children) . "\n */"; } - /** - * 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 \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - */ - public static function assertNotSame($expected, $actual, string $message = '') : void +} +name = $name; + $this->value = $value; } - /** - * Asserts that a variable is of a given type. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws Exception - * @throws ExpectationFailedException - * - * @psalm-template ExpectedType of object - * - * @psalm-param class-string $expected - * - * @psalm-assert =ExpectedType $actual - */ - public static function assertInstanceOf(string $expected, $actual, string $message = '') : void + public function __toString() : string { - if (!class_exists($expected) && !interface_exists($expected)) { - throw \PHPUnit\Framework\InvalidArgumentException::create(1, 'class or interface name'); + if ($this->value instanceof DoctrineTagValueNode) { + return (string) $this->value; } - static::assertThat($actual, new IsInstanceOf($expected), $message); + return trim("{$this->name} {$this->value}"); } - /** - * Asserts that a variable is not of a given type. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws Exception - * @throws ExpectationFailedException - * - * @psalm-template ExpectedType of object - * - * @psalm-param class-string $expected - * - * @psalm-assert !ExpectedType $actual - */ - public static function assertNotInstanceOf(string $expected, $actual, string $message = '') : void +} +text = $text; } - /** - * Asserts that a variable is of type array. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - * - * @psalm-assert array $actual - */ - public static function assertIsArray($actual, string $message = '') : void + public function __toString() : string { - static::assertThat($actual, new IsType(IsType::TYPE_ARRAY), $message); + return $this->text; } - /** - * Asserts that a variable is of type bool. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - * - * @psalm-assert bool $actual - */ - public static function assertIsBool($actual, string $message = '') : void +} +type = $type; + $this->propertyName = $propertyName; + $this->description = $description; } - /** - * Asserts that a variable is of type float. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - * - * @psalm-assert float $actual - */ - public static function assertIsFloat($actual, string $message = '') : void + public function __toString() : string { - static::assertThat($actual, new IsType(IsType::TYPE_FLOAT), $message); + return trim("{$this->type} {$this->propertyName} {$this->description}"); } - /** - * Asserts that a variable is of type int. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - * - * @psalm-assert int $actual - */ - public static function assertIsInt($actual, string $message = '') : void +} +type = $type; + $this->description = $description; } - /** - * Asserts that a variable is of type numeric. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - * - * @psalm-assert numeric $actual - */ - public static function assertIsNumeric($actual, string $message = '') : void + public function __toString() : string { - static::assertThat($actual, new IsType(IsType::TYPE_NUMERIC), $message); + return trim("{$this->type} {$this->description}"); } - /** - * Asserts that a variable is of type object. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - * - * @psalm-assert object $actual - */ - public static function assertIsObject($actual, string $message = '') : void +} +type = $type; + $this->description = $description; } - /** - * Asserts that a variable is of type resource. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - * - * @psalm-assert resource $actual - */ - public static function assertIsResource($actual, string $message = '') : void + public function __toString() : string { - static::assertThat($actual, new IsType(IsType::TYPE_RESOURCE), $message); + return trim("{$this->type} {$this->description}"); } - /** - * Asserts that a variable is of type resource and is closed. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - * - * @psalm-assert resource $actual - */ - public static function assertIsClosedResource($actual, string $message = '') : void +} +type = $type; + $this->description = $description; } - /** - * Asserts that a variable is of type string. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - * - * @psalm-assert string $actual - */ - public static function assertIsString($actual, string $message = '') : void + public function __toString() : string { - static::assertThat($actual, new IsType(IsType::TYPE_STRING), $message); + return trim("{$this->type} {$this->description}"); } - /** - * Asserts that a variable is of type scalar. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - * - * @psalm-assert scalar $actual - */ - public static function assertIsScalar($actual, string $message = '') : void +} +type = $type; + $this->description = $description; } - /** - * Asserts that a variable is of type callable. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - * - * @psalm-assert callable $actual - */ - public static function assertIsCallable($actual, string $message = '') : void + public function __toString() : string { - static::assertThat($actual, new IsType(IsType::TYPE_CALLABLE), $message); + return trim($this->type . ' ' . $this->description); } - /** - * Asserts that a variable is of type iterable. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - * - * @psalm-assert iterable $actual - */ - public static function assertIsIterable($actual, string $message = '') : void +} +name = $name; + $this->bound = $bound; + $this->default = $default; + $this->description = $description; } - /** - * Asserts that a variable is not of type array. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - * - * @psalm-assert !array $actual - */ - public static function assertIsNotArray($actual, string $message = '') : void + public function __toString() : string { - static::assertThat($actual, new LogicalNot(new IsType(IsType::TYPE_ARRAY)), $message); + $bound = $this->bound !== null ? " of {$this->bound}" : ''; + $default = $this->default !== null ? " = {$this->default}" : ''; + return trim("{$this->name}{$bound}{$default} {$this->description}"); } - /** - * Asserts that a variable is not of type bool. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - * - * @psalm-assert !bool $actual - */ - public static function assertIsNotBool($actual, string $message = '') : void +} +type = $type; + $this->description = $description; } - /** - * Asserts that a variable is not of type float. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - * - * @psalm-assert !float $actual - */ - public static function assertIsNotFloat($actual, string $message = '') : void + public function __toString() : string { - static::assertThat($actual, new LogicalNot(new IsType(IsType::TYPE_FLOAT)), $message); + return trim("{$this->type} {$this->description}"); } - /** - * Asserts that a variable is not of type int. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - * - * @psalm-assert !int $actual - */ - public static function assertIsNotInt($actual, string $message = '') : void +} +importedAlias = $importedAlias; + $this->importedFrom = $importedFrom; + $this->importedAs = $importedAs; } - /** - * Asserts that a variable is not of type numeric. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - * - * @psalm-assert !numeric $actual - */ - public static function assertIsNotNumeric($actual, string $message = '') : void + public function __toString() : string { - static::assertThat($actual, new LogicalNot(new IsType(IsType::TYPE_NUMERIC)), $message); + return trim("{$this->importedAlias} from {$this->importedFrom}" . ($this->importedAs !== null ? " as {$this->importedAs}" : '')); } - /** - * Asserts that a variable is not of type object. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - * - * @psalm-assert !object $actual - */ - public static function assertIsNotObject($actual, string $message = '') : void +} +alias = $alias; + $this->type = $type; } - /** - * Asserts that a variable is not of type resource. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - * - * @psalm-assert !resource $actual - */ - public static function assertIsNotResource($actual, string $message = '') : void + public function __toString() : string { - static::assertThat($actual, new LogicalNot(new IsType(IsType::TYPE_RESOURCE)), $message); + return trim("{$this->alias} {$this->type}"); } - /** - * Asserts that a variable is not of type resource. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - * - * @psalm-assert !resource $actual - */ - public static function assertIsNotClosedResource($actual, string $message = '') : void +} +isReference = $isReference; + $this->isVariadic = $isVariadic; + $this->parameterName = $parameterName; + $this->description = $description; } - /** - * Asserts that a variable is not of type string. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - * - * @psalm-assert !string $actual - */ - public static function assertIsNotString($actual, string $message = '') : void + public function __toString() : string { - static::assertThat($actual, new LogicalNot(new IsType(IsType::TYPE_STRING)), $message); + $reference = $this->isReference ? '&' : ''; + $variadic = $this->isVariadic ? '...' : ''; + return trim("{$reference}{$variadic}{$this->parameterName} {$this->description}"); } - /** - * Asserts that a variable is not of type scalar. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - * - * @psalm-assert !scalar $actual - */ - public static function assertIsNotScalar($actual, string $message = '') : void +} +type = $type; + $this->description = $description; } - /** - * Asserts that a variable is not of type callable. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - * - * @psalm-assert !callable $actual - */ - public static function assertIsNotCallable($actual, string $message = '') : void + public function __toString() : string { - static::assertThat($actual, new LogicalNot(new IsType(IsType::TYPE_CALLABLE)), $message); + return trim("{$this->type} {$this->description}"); } - /** - * Asserts that a variable is not of type iterable. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - * - * @psalm-assert !iterable $actual - */ - public static function assertIsNotIterable($actual, string $message = '') : void +} +type = $type; + $this->variableName = $variableName; + $this->description = $description; } - /** - * Asserts that a string matches a given regular expression. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - */ - public static function assertMatchesRegularExpression(string $pattern, string $string, string $message = '') : void + public function __toString() : string { - static::assertThat($string, new RegularExpression($pattern), $message); + return trim("{$this->type} " . trim("{$this->variableName} {$this->description}")); } +} +keyName = $keyName; + $this->optional = $optional; + $this->valueType = $valueType; } - /** - * Asserts that a string does not match a given regular expression. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - */ - public static function assertDoesNotMatchRegularExpression(string $pattern, string $string, string $message = '') : void + public function __toString() : string { - static::assertThat($string, new LogicalNot(new RegularExpression($pattern)), $message); + if ($this->keyName !== null) { + return sprintf('%s%s: %s', (string) $this->keyName, $this->optional ? '?' : '', (string) $this->valueType); + } + return (string) $this->valueType; } +} +items = $items; + $this->sealed = $sealed; + $this->kind = $kind; } - /** - * Assert that the size of two arrays (or `Countable` or `Traversable` objects) - * is the same. - * - * @param Countable|iterable $expected - * @param Countable|iterable $actual - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws Exception - * @throws ExpectationFailedException - */ - public static function assertSameSize($expected, $actual, string $message = '') : void + public function __toString() : string { - if ($expected instanceof Generator) { - self::createWarning('Passing an argument of type Generator for the $expected parameter is deprecated. Support for this will be removed in PHPUnit 10.'); - } - if ($actual instanceof Generator) { - self::createWarning('Passing an argument of type Generator for the $actual parameter is deprecated. Support for this will be removed in PHPUnit 10.'); - } - if (!$expected instanceof Countable && !is_iterable($expected)) { - throw \PHPUnit\Framework\InvalidArgumentException::create(1, 'countable or iterable'); + $items = $this->items; + if (!$this->sealed) { + $items[] = '...'; } - if (!$actual instanceof Countable && !is_iterable($actual)) { - throw \PHPUnit\Framework\InvalidArgumentException::create(2, 'countable or iterable'); + return $this->kind . '{' . implode(', ', $items) . '}'; + } +} +type = $type; + } + public function __toString() : string + { + if ($this->type instanceof CallableTypeNode || $this->type instanceof ConstTypeNode || $this->type instanceof NullableTypeNode) { + return '(' . $this->type . ')[]'; } - static::assertThat($actual, new SameSize($expected), $message); + return $this->type . '[]'; } +} +identifier = $identifier; + $this->parameters = $parameters; + $this->returnType = $returnType; + $this->templateTypes = $templateTypes; + } + public function __toString() : string + { + $returnType = $this->returnType; + if ($returnType instanceof self) { + $returnType = "({$returnType})"; } - static::assertThat($actual, new LogicalNot(new SameSize($expected)), $message); + $template = $this->templateTypes !== [] ? '<' . implode(', ', $this->templateTypes) . '>' : ''; + $parameters = implode(', ', $this->parameters); + return "{$this->identifier}{$template}({$parameters}): {$returnType}"; } - /** - * Asserts that a string matches a given format string. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - */ - public static function assertStringMatchesFormat(string $format, string $string, string $message = '') : void +} +type = $type; + $this->isReference = $isReference; + $this->isVariadic = $isVariadic; + $this->parameterName = $parameterName; + $this->isOptional = $isOptional; } - /** - * Asserts that a string does not match a given format string. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - */ - public static function assertStringNotMatchesFormat(string $format, string $string, string $message = '') : void + public function __toString() : string { - static::assertThat($string, new LogicalNot(new StringMatchesFormatDescription($format)), $message); + $type = "{$this->type} "; + $isReference = $this->isReference ? '&' : ''; + $isVariadic = $this->isVariadic ? '...' : ''; + $isOptional = $this->isOptional ? '=' : ''; + return trim("{$type}{$isReference}{$isVariadic}{$this->parameterName}") . $isOptional; } - /** - * Asserts that a string matches a given format file. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - */ - public static function assertStringMatchesFormatFile(string $formatFile, string $string, string $message = '') : void +} +parameterName = $parameterName; + $this->targetType = $targetType; + $this->if = $if; + $this->else = $else; + $this->negated = $negated; } - /** - * Asserts that a string does not match a given format string. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - */ - public static function assertStringNotMatchesFormatFile(string $formatFile, string $string, string $message = '') : void + public function __toString() : string { - static::assertFileExists($formatFile, $message); - static::assertThat($string, new LogicalNot(new StringMatchesFormatDescription(file_get_contents($formatFile))), $message); + return sprintf('(%s %s %s ? %s : %s)', $this->parameterName, $this->negated ? 'is not' : 'is', $this->targetType, $this->if, $this->else); } - /** - * Asserts that a string starts with a given prefix. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - */ - public static function assertStringStartsWith(string $prefix, string $string, string $message = '') : void +} +subjectType = $subjectType; + $this->targetType = $targetType; + $this->if = $if; + $this->else = $else; + $this->negated = $negated; } - /** - * Asserts that a string starts not with a given prefix. - * - * @param string $prefix - * @param string $string - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - */ - public static function assertStringStartsNotWith($prefix, $string, string $message = '') : void + public function __toString() : string { - static::assertThat($string, new LogicalNot(new StringStartsWith($prefix)), $message); + return sprintf('(%s %s %s ? %s : %s)', $this->subjectType, $this->negated ? 'is not' : 'is', $this->targetType, $this->if, $this->else); } - /** - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - */ - public static function assertStringContainsString(string $needle, string $haystack, string $message = '') : void +} +constExpr = $constExpr; } - /** - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - */ - public static function assertStringContainsStringIgnoringCase(string $needle, string $haystack, string $message = '') : void + public function __toString() : string { - $constraint = new StringContains($needle, \true); - static::assertThat($haystack, $constraint, $message); + return $this->constExpr->__toString(); } +} +type = $type; + $this->genericTypes = $genericTypes; + $this->variances = $variances; } - /** - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - */ - public static function assertStringNotContainsStringIgnoringCase(string $needle, string $haystack, string $message = '') : void + public function __toString() : string { - $constraint = new LogicalNot(new StringContains($needle, \true)); - static::assertThat($haystack, $constraint, $message); + $genericTypes = []; + foreach ($this->genericTypes as $index => $type) { + $variance = $this->variances[$index] ?? self::VARIANCE_INVARIANT; + if ($variance === self::VARIANCE_INVARIANT) { + $genericTypes[] = (string) $type; + } elseif ($variance === self::VARIANCE_BIVARIANT) { + $genericTypes[] = '*'; + } else { + $genericTypes[] = sprintf('%s %s', $variance, $type); + } + } + return $this->type . '<' . implode(', ', $genericTypes) . '>'; } - /** - * Asserts that a string ends with a given suffix. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - */ - public static function assertStringEndsWith(string $suffix, string $string, string $message = '') : void +} +name = $name; } - /** - * Asserts that a string ends not with a given suffix. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - */ - public static function assertStringEndsNotWith(string $suffix, string $string, string $message = '') : void + public function __toString() : string { - static::assertThat($string, new LogicalNot(new StringEndsWith($suffix)), $message); + return $this->name; } +} +loadFile($expectedFile); - $actual = (new XmlLoader())->loadFile($actualFile); - static::assertEquals($expected, $actual, $message); + $this->types = $types; } - /** - * Asserts that two XML files are not equal. - * - * @throws \PHPUnit\Util\Exception - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - */ - public static function assertXmlFileNotEqualsXmlFile(string $expectedFile, string $actualFile, string $message = '') : void + public function __toString() : string { - $expected = (new XmlLoader())->loadFile($expectedFile); - $actual = (new XmlLoader())->loadFile($actualFile); - static::assertNotEquals($expected, $actual, $message); + return '(' . implode(' & ', array_map(static function (TypeNode $type) : string { + if ($type instanceof NullableTypeNode) { + return '(' . $type . ')'; + } + return (string) $type; + }, $this->types)) . ')'; } - /** - * Asserts that two XML documents are equal. - * - * @param DOMDocument|string $actualXml - * - * @throws \PHPUnit\Util\Xml\Exception - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - */ - public static function assertXmlStringEqualsXmlFile(string $expectedFile, $actualXml, string $message = '') : void +} +load($actualXml); - } - $expected = (new XmlLoader())->loadFile($expectedFile); - static::assertEquals($expected, $actual, $message); + $this->exceptionArgs = [$exception->getCurrentTokenValue(), $exception->getCurrentTokenType(), $exception->getCurrentOffset(), $exception->getExpectedTokenType(), $exception->getExpectedTokenValue(), $exception->getCurrentTokenLine()]; } - /** - * Asserts that two XML documents are not equal. - * - * @param DOMDocument|string $actualXml - * - * @throws \PHPUnit\Util\Xml\Exception - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - */ - public static function assertXmlStringNotEqualsXmlFile(string $expectedFile, $actualXml, string $message = '') : void + public function getException() : ParserException { - if (!is_string($actualXml)) { - self::createWarning('Passing an argument of type DOMDocument for the $actualXml parameter is deprecated. Support for this will be removed in PHPUnit 10.'); - $actual = $actualXml; - } else { - $actual = (new XmlLoader())->load($actualXml); - } - $expected = (new XmlLoader())->loadFile($expectedFile); - static::assertNotEquals($expected, $actual, $message); + return new ParserException(...$this->exceptionArgs); } - /** - * Asserts that two XML documents are equal. - * - * @param DOMDocument|string $expectedXml - * @param DOMDocument|string $actualXml - * - * @throws \PHPUnit\Util\Xml\Exception - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - */ - public static function assertXmlStringEqualsXmlString($expectedXml, $actualXml, string $message = '') : void + public function __toString() : string { - if (!is_string($expectedXml)) { - self::createWarning('Passing an argument of type DOMDocument for the $expectedXml parameter is deprecated. Support for this will be removed in PHPUnit 10.'); - $expected = $expectedXml; - } else { - $expected = (new XmlLoader())->load($expectedXml); - } - if (!is_string($actualXml)) { - self::createWarning('Passing an argument of type DOMDocument for the $actualXml parameter is deprecated. Support for this will be removed in PHPUnit 10.'); - $actual = $actualXml; - } else { - $actual = (new XmlLoader())->load($actualXml); - } - static::assertEquals($expected, $actual, $message); + return '*Invalid type*'; } - /** - * Asserts that two XML documents are not equal. - * - * @param DOMDocument|string $expectedXml - * @param DOMDocument|string $actualXml - * - * @throws \PHPUnit\Util\Xml\Exception - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - */ - public static function assertXmlStringNotEqualsXmlString($expectedXml, $actualXml, string $message = '') : void +} +load($expectedXml); - } - if (!is_string($actualXml)) { - self::createWarning('Passing an argument of type DOMDocument for the $actualXml parameter is deprecated. Support for this will be removed in PHPUnit 10.'); - $actual = $actualXml; - } else { - $actual = (new XmlLoader())->load($actualXml); - } - static::assertNotEquals($expected, $actual, $message); + $this->type = $type; } - /** - * Asserts that a hierarchy of DOMElements matches. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws AssertionFailedError - * @throws ExpectationFailedException - * - * @codeCoverageIgnore - * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4091 - */ - public static function assertEqualXMLStructure(DOMElement $expectedElement, DOMElement $actualElement, bool $checkAttributes = \false, string $message = '') : void + public function __toString() : string { - self::createWarning('assertEqualXMLStructure() is deprecated and will be removed in PHPUnit 10.'); - $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); - } + return '?' . $this->type; } +} +evaluate($value, $message); + $this->keyName = $keyName; + $this->optional = $optional; + $this->valueType = $valueType; } - /** - * Asserts that a string is a valid JSON string. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - */ - public static function assertJson(string $actualJson, string $message = '') : void + public function __toString() : string { - static::assertThat($actualJson, static::isJson(), $message); + if ($this->keyName !== null) { + return sprintf('%s%s: %s', (string) $this->keyName, $this->optional ? '?' : '', (string) $this->valueType); + } + return (string) $this->valueType; } +} +items = $items; } - /** - * Asserts that two given JSON encoded objects or arrays are not equal. - * - * @param string $expectedJson - * @param string $actualJson - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - */ - public static function assertJsonStringNotEqualsJsonString($expectedJson, $actualJson, string $message = '') : void + public function __toString() : string { - static::assertJson($expectedJson, $message); - static::assertJson($actualJson, $message); - static::assertThat($actualJson, new LogicalNot(new JsonMatches($expectedJson)), $message); + $items = $this->items; + return 'object{' . implode(', ', $items) . '}'; } - /** - * Asserts that the generated JSON encoded object and the content of the given file are equal. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - */ - public static function assertJsonStringEqualsJsonFile(string $expectedFile, string $actualJson, string $message = '') : void +} +type = $type; + $this->offset = $offset; } - /** - * Asserts that the generated JSON encoded object and the content of the given file are not equal. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - */ - public static function assertJsonStringNotEqualsJsonFile(string $expectedFile, string $actualJson, string $message = '') : void + public function __toString() : string { - 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); + if ($this->type instanceof CallableTypeNode || $this->type instanceof ConstTypeNode || $this->type instanceof NullableTypeNode) { + return '(' . $this->type . ')[' . $this->offset . ']'; + } + return $this->type . '[' . $this->offset . ']'; + } +} +types = $types; + } + public function __toString() : string + { + return '(' . implode(' | ', array_map(static function (TypeNode $type) : string { + if ($type instanceof NullableTypeNode) { + return '(' . $type . ')'; + } + return (string) $type; + }, $this->types)) . ')'; + } +} +MIT License + +Copyright (c) 2016 Ondřej Mirtes + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. '\'&\'', self::TOKEN_UNION => '\'|\'', self::TOKEN_INTERSECTION => '\'&\'', self::TOKEN_NULLABLE => '\'?\'', self::TOKEN_NEGATED => '\'!\'', self::TOKEN_OPEN_PARENTHESES => '\'(\'', self::TOKEN_CLOSE_PARENTHESES => '\')\'', self::TOKEN_OPEN_ANGLE_BRACKET => '\'<\'', self::TOKEN_CLOSE_ANGLE_BRACKET => '\'>\'', self::TOKEN_OPEN_SQUARE_BRACKET => '\'[\'', self::TOKEN_CLOSE_SQUARE_BRACKET => '\']\'', self::TOKEN_OPEN_CURLY_BRACKET => '\'{\'', self::TOKEN_CLOSE_CURLY_BRACKET => '\'}\'', self::TOKEN_COMMA => '\',\'', self::TOKEN_COLON => '\':\'', self::TOKEN_VARIADIC => '\'...\'', self::TOKEN_DOUBLE_COLON => '\'::\'', self::TOKEN_DOUBLE_ARROW => '\'=>\'', self::TOKEN_ARROW => '\'->\'', self::TOKEN_EQUAL => '\'=\'', self::TOKEN_OPEN_PHPDOC => '\'/**\'', self::TOKEN_CLOSE_PHPDOC => '\'*/\'', self::TOKEN_PHPDOC_TAG => 'TOKEN_PHPDOC_TAG', self::TOKEN_DOCTRINE_TAG => 'TOKEN_DOCTRINE_TAG', self::TOKEN_PHPDOC_EOL => 'TOKEN_PHPDOC_EOL', self::TOKEN_FLOAT => 'TOKEN_FLOAT', self::TOKEN_INTEGER => 'TOKEN_INTEGER', self::TOKEN_SINGLE_QUOTED_STRING => 'TOKEN_SINGLE_QUOTED_STRING', self::TOKEN_DOUBLE_QUOTED_STRING => 'TOKEN_DOUBLE_QUOTED_STRING', self::TOKEN_DOCTRINE_ANNOTATION_STRING => 'TOKEN_DOCTRINE_ANNOTATION_STRING', self::TOKEN_IDENTIFIER => 'type', self::TOKEN_THIS_VARIABLE => '\'$this\'', self::TOKEN_VARIABLE => 'variable', self::TOKEN_HORIZONTAL_WS => 'TOKEN_HORIZONTAL_WS', self::TOKEN_OTHER => 'TOKEN_OTHER', self::TOKEN_END => 'TOKEN_END', self::TOKEN_WILDCARD => '*']; + public const VALUE_OFFSET = 0; + public const TYPE_OFFSET = 1; + public const LINE_OFFSET = 2; + /** @var bool */ + private $parseDoctrineAnnotations; + /** @var string|null */ + private $regexp; + public function __construct(bool $parseDoctrineAnnotations = \false) + { + $this->parseDoctrineAnnotations = $parseDoctrineAnnotations; } /** - * Asserts that two JSON files are not equal. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException + * @return list */ - public static function assertJsonFileNotEqualsJsonFile(string $expectedFile, string $actualFile, string $message = '') : void + public function tokenize(string $s) : array { - 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); + if ($this->regexp === null) { + $this->regexp = $this->generateRegexp(); + } + preg_match_all($this->regexp, $s, $matches, PREG_SET_ORDER); + $tokens = []; + $line = 1; + foreach ($matches as $match) { + $type = (int) $match['MARK']; + $tokens[] = [$match[0], $type, $line]; + if ($type !== self::TOKEN_PHPDOC_EOL) { + continue; + } + $line++; + } + $tokens[] = ['', self::TOKEN_END, $line]; + return $tokens; + } + private function generateRegexp() : string + { + $patterns = [ + self::TOKEN_HORIZONTAL_WS => '[\\x09\\x20]++', + self::TOKEN_IDENTIFIER => '(?:[\\\\]?+[a-z_\\x80-\\xFF][0-9a-z_\\x80-\\xFF-]*+)++', + self::TOKEN_THIS_VARIABLE => '\\$this(?![0-9a-z_\\x80-\\xFF])', + self::TOKEN_VARIABLE => '\\$[a-z_\\x80-\\xFF][0-9a-z_\\x80-\\xFF]*+', + // '&' followed by TOKEN_VARIADIC, TOKEN_VARIABLE, TOKEN_EQUAL, TOKEN_EQUAL or TOKEN_CLOSE_PARENTHESES + self::TOKEN_REFERENCE => '&(?=\\s*+(?:[.,=)]|(?:\\$(?!this(?![0-9a-z_\\x80-\\xFF])))))', + self::TOKEN_UNION => '\\|', + self::TOKEN_INTERSECTION => '&', + self::TOKEN_NULLABLE => '\\?', + self::TOKEN_NEGATED => '!', + self::TOKEN_OPEN_PARENTHESES => '\\(', + self::TOKEN_CLOSE_PARENTHESES => '\\)', + self::TOKEN_OPEN_ANGLE_BRACKET => '<', + self::TOKEN_CLOSE_ANGLE_BRACKET => '>', + self::TOKEN_OPEN_SQUARE_BRACKET => '\\[', + self::TOKEN_CLOSE_SQUARE_BRACKET => '\\]', + self::TOKEN_OPEN_CURLY_BRACKET => '\\{', + self::TOKEN_CLOSE_CURLY_BRACKET => '\\}', + self::TOKEN_COMMA => ',', + self::TOKEN_VARIADIC => '\\.\\.\\.', + self::TOKEN_DOUBLE_COLON => '::', + self::TOKEN_DOUBLE_ARROW => '=>', + self::TOKEN_ARROW => '->', + self::TOKEN_EQUAL => '=', + self::TOKEN_COLON => ':', + self::TOKEN_OPEN_PHPDOC => '/\\*\\*(?=\\s)\\x20?+', + self::TOKEN_CLOSE_PHPDOC => '\\*/', + self::TOKEN_PHPDOC_TAG => '@(?:[a-z][a-z0-9-\\\\]+:)?[a-z][a-z0-9-\\\\]*+', + self::TOKEN_PHPDOC_EOL => '\\r?+\\n[\\x09\\x20]*+(?:\\*(?!/)\\x20?+)?', + self::TOKEN_FLOAT => '[+\\-]?(?:(?:[0-9]++(_[0-9]++)*\\.[0-9]*+(_[0-9]++)*(?:e[+\\-]?[0-9]++(_[0-9]++)*)?)|(?:[0-9]*+(_[0-9]++)*\\.[0-9]++(_[0-9]++)*(?:e[+\\-]?[0-9]++(_[0-9]++)*)?)|(?:[0-9]++(_[0-9]++)*e[+\\-]?[0-9]++(_[0-9]++)*))', + self::TOKEN_INTEGER => '[+\\-]?(?:(?:0b[0-1]++(_[0-1]++)*)|(?:0o[0-7]++(_[0-7]++)*)|(?:0x[0-9a-f]++(_[0-9a-f]++)*)|(?:[0-9]++(_[0-9]++)*))', + self::TOKEN_SINGLE_QUOTED_STRING => '\'(?:\\\\[^\\r\\n]|[^\'\\r\\n\\\\])*+\'', + self::TOKEN_DOUBLE_QUOTED_STRING => '"(?:\\\\[^\\r\\n]|[^"\\r\\n\\\\])*+"', + self::TOKEN_WILDCARD => '\\*', + ]; + if ($this->parseDoctrineAnnotations) { + $patterns[self::TOKEN_DOCTRINE_TAG] = '@[a-z_\\\\][a-z0-9_\\:\\\\]*[a-z_][a-z0-9_]*'; + $patterns[self::TOKEN_DOCTRINE_ANNOTATION_STRING] = '"(?:""|[^"])*+"'; + } + // anything but TOKEN_CLOSE_PHPDOC or TOKEN_HORIZONTAL_WS or TOKEN_EOL + $patterns[self::TOKEN_OTHER] = '(?:(?!\\*/)[^\\s])++'; + foreach ($patterns as $type => &$pattern) { + $pattern = '(?:' . $pattern . ')(*MARK:' . $type . ')'; + } + return '~' . implode('|', $patterns) . '~Asi'; } +} +setConstraints($constraints); - return $constraint; + $this->unescapeStrings = $unescapeStrings; + $this->quoteAwareConstExprString = $quoteAwareConstExprString; + $this->useLinesAttributes = $usedAttributes['lines'] ?? \false; + $this->useIndexAttributes = $usedAttributes['indexes'] ?? \false; + $this->parseDoctrineStrings = \false; } - public static function logicalOr() : LogicalOr + /** + * @internal + */ + public function toDoctrine() : self { - $constraints = func_get_args(); - $constraint = new LogicalOr(); - $constraint->setConstraints($constraints); - return $constraint; + $self = new self($this->unescapeStrings, $this->quoteAwareConstExprString, ['lines' => $this->useLinesAttributes, 'indexes' => $this->useIndexAttributes]); + $self->parseDoctrineStrings = \true; + return $self; } - public static function logicalNot(Constraint $constraint) : LogicalNot + public function parse(TokenIterator $tokens, bool $trimStrings = \false) : Ast\ConstExpr\ConstExprNode { - return new LogicalNot($constraint); + $startLine = $tokens->currentTokenLine(); + $startIndex = $tokens->currentTokenIndex(); + if ($tokens->isCurrentTokenType(Lexer::TOKEN_FLOAT)) { + $value = $tokens->currentTokenValue(); + $tokens->next(); + return $this->enrichWithAttributes($tokens, new Ast\ConstExpr\ConstExprFloatNode(str_replace('_', '', $value)), $startLine, $startIndex); + } + if ($tokens->isCurrentTokenType(Lexer::TOKEN_INTEGER)) { + $value = $tokens->currentTokenValue(); + $tokens->next(); + return $this->enrichWithAttributes($tokens, new Ast\ConstExpr\ConstExprIntegerNode(str_replace('_', '', $value)), $startLine, $startIndex); + } + if ($this->parseDoctrineStrings && $tokens->isCurrentTokenType(Lexer::TOKEN_DOCTRINE_ANNOTATION_STRING)) { + $value = $tokens->currentTokenValue(); + $tokens->next(); + return $this->enrichWithAttributes($tokens, new Ast\ConstExpr\DoctrineConstExprStringNode(Ast\ConstExpr\DoctrineConstExprStringNode::unescape($value)), $startLine, $startIndex); + } + if ($tokens->isCurrentTokenType(Lexer::TOKEN_SINGLE_QUOTED_STRING, Lexer::TOKEN_DOUBLE_QUOTED_STRING)) { + if ($this->parseDoctrineStrings) { + if ($tokens->isCurrentTokenType(Lexer::TOKEN_SINGLE_QUOTED_STRING)) { + throw new ParserException($tokens->currentTokenValue(), $tokens->currentTokenType(), $tokens->currentTokenOffset(), Lexer::TOKEN_DOUBLE_QUOTED_STRING, null, $tokens->currentTokenLine()); + } + $value = $tokens->currentTokenValue(); + $tokens->next(); + return $this->enrichWithAttributes($tokens, $this->parseDoctrineString($value, $tokens), $startLine, $startIndex); + } + $value = $tokens->currentTokenValue(); + $type = $tokens->currentTokenType(); + if ($trimStrings) { + if ($this->unescapeStrings) { + $value = StringUnescaper::unescapeString($value); + } else { + $value = substr($value, 1, -1); + } + } + $tokens->next(); + if ($this->quoteAwareConstExprString) { + return $this->enrichWithAttributes($tokens, new Ast\ConstExpr\QuoteAwareConstExprStringNode($value, $type === Lexer::TOKEN_SINGLE_QUOTED_STRING ? Ast\ConstExpr\QuoteAwareConstExprStringNode::SINGLE_QUOTED : Ast\ConstExpr\QuoteAwareConstExprStringNode::DOUBLE_QUOTED), $startLine, $startIndex); + } + return $this->enrichWithAttributes($tokens, new Ast\ConstExpr\ConstExprStringNode($value), $startLine, $startIndex); + } elseif ($tokens->isCurrentTokenType(Lexer::TOKEN_IDENTIFIER)) { + $identifier = $tokens->currentTokenValue(); + $tokens->next(); + switch (strtolower($identifier)) { + case 'true': + return $this->enrichWithAttributes($tokens, new Ast\ConstExpr\ConstExprTrueNode(), $startLine, $startIndex); + case 'false': + return $this->enrichWithAttributes($tokens, new Ast\ConstExpr\ConstExprFalseNode(), $startLine, $startIndex); + case 'null': + return $this->enrichWithAttributes($tokens, new Ast\ConstExpr\ConstExprNullNode(), $startLine, $startIndex); + case 'array': + $tokens->consumeTokenType(Lexer::TOKEN_OPEN_PARENTHESES); + return $this->parseArray($tokens, Lexer::TOKEN_CLOSE_PARENTHESES, $startIndex); + } + if ($tokens->tryConsumeTokenType(Lexer::TOKEN_DOUBLE_COLON)) { + $classConstantName = ''; + $lastType = null; + while (\true) { + if ($lastType !== Lexer::TOKEN_IDENTIFIER && $tokens->currentTokenType() === Lexer::TOKEN_IDENTIFIER) { + $classConstantName .= $tokens->currentTokenValue(); + $tokens->consumeTokenType(Lexer::TOKEN_IDENTIFIER); + $lastType = Lexer::TOKEN_IDENTIFIER; + continue; + } + if ($lastType !== Lexer::TOKEN_WILDCARD && $tokens->tryConsumeTokenType(Lexer::TOKEN_WILDCARD)) { + $classConstantName .= '*'; + $lastType = Lexer::TOKEN_WILDCARD; + if ($tokens->getSkippedHorizontalWhiteSpaceIfAny() !== '') { + break; + } + continue; + } + if ($lastType === null) { + // trigger parse error if nothing valid was consumed + $tokens->consumeTokenType(Lexer::TOKEN_WILDCARD); + } + break; + } + return $this->enrichWithAttributes($tokens, new Ast\ConstExpr\ConstFetchNode($identifier, $classConstantName), $startLine, $startIndex); + } + return $this->enrichWithAttributes($tokens, new Ast\ConstExpr\ConstFetchNode('', $identifier), $startLine, $startIndex); + } elseif ($tokens->tryConsumeTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) { + return $this->parseArray($tokens, Lexer::TOKEN_CLOSE_SQUARE_BRACKET, $startIndex); + } + throw new ParserException($tokens->currentTokenValue(), $tokens->currentTokenType(), $tokens->currentTokenOffset(), Lexer::TOKEN_IDENTIFIER, null, $tokens->currentTokenLine()); } - public static function logicalXor() : LogicalXor + private function parseArray(TokenIterator $tokens, int $endToken, int $startIndex) : Ast\ConstExpr\ConstExprArrayNode { - $constraints = func_get_args(); - $constraint = new LogicalXor(); - $constraint->setConstraints($constraints); - return $constraint; + $items = []; + $startLine = $tokens->currentTokenLine(); + if (!$tokens->tryConsumeTokenType($endToken)) { + do { + $items[] = $this->parseArrayItem($tokens); + } while ($tokens->tryConsumeTokenType(Lexer::TOKEN_COMMA) && !$tokens->isCurrentTokenType($endToken)); + $tokens->consumeTokenType($endToken); + } + return $this->enrichWithAttributes($tokens, new Ast\ConstExpr\ConstExprArrayNode($items), $startLine, $startIndex); } - public static function anything() : IsAnything + /** + * This method is supposed to be called with TokenIterator after reading TOKEN_DOUBLE_QUOTED_STRING and shifting + * to the next token. + */ + public function parseDoctrineString(string $text, TokenIterator $tokens) : Ast\ConstExpr\DoctrineConstExprStringNode { - return new IsAnything(); + // Because of how Lexer works, a valid Doctrine string + // can consist of a sequence of TOKEN_DOUBLE_QUOTED_STRING and TOKEN_DOCTRINE_ANNOTATION_STRING + while ($tokens->isCurrentTokenType(Lexer::TOKEN_DOUBLE_QUOTED_STRING, Lexer::TOKEN_DOCTRINE_ANNOTATION_STRING)) { + $text .= $tokens->currentTokenValue(); + $tokens->next(); + } + return new Ast\ConstExpr\DoctrineConstExprStringNode(Ast\ConstExpr\DoctrineConstExprStringNode::unescape($text)); } - public static function isTrue() : IsTrue + private function parseArrayItem(TokenIterator $tokens) : Ast\ConstExpr\ConstExprArrayItemNode { - return new IsTrue(); + $startLine = $tokens->currentTokenLine(); + $startIndex = $tokens->currentTokenIndex(); + $expr = $this->parse($tokens); + if ($tokens->tryConsumeTokenType(Lexer::TOKEN_DOUBLE_ARROW)) { + $key = $expr; + $value = $this->parse($tokens); + } else { + $key = null; + $value = $expr; + } + return $this->enrichWithAttributes($tokens, new Ast\ConstExpr\ConstExprArrayItemNode($key, $value), $startLine, $startIndex); } /** - * @psalm-template CallbackInput of mixed - * - * @psalm-param callable(CallbackInput $callback): bool $callback - * - * @psalm-return Callback + * @template T of Ast\ConstExpr\ConstExprNode + * @param T $node + * @return T */ - public static function callback(callable $callback) : Callback + private function enrichWithAttributes(TokenIterator $tokens, Ast\ConstExpr\ConstExprNode $node, int $startLine, int $startIndex) : Ast\ConstExpr\ConstExprNode { - return new Callback($callback); + if ($this->useLinesAttributes) { + $node->setAttribute(Ast\Attribute::START_LINE, $startLine); + $node->setAttribute(Ast\Attribute::END_LINE, $tokens->currentTokenLine()); + } + if ($this->useIndexAttributes) { + $node->setAttribute(Ast\Attribute::START_INDEX, $startIndex); + $node->setAttribute(Ast\Attribute::END_INDEX, $tokens->endIndexOfLastRelevantToken()); + } + return $node; } - public static function isFalse() : IsFalse +} +currentTokenValue = $currentTokenValue; + $this->currentTokenType = $currentTokenType; + $this->currentOffset = $currentOffset; + $this->expectedTokenType = $expectedTokenType; + $this->expectedTokenValue = $expectedTokenValue; + $this->currentTokenLine = $currentTokenLine; + parent::__construct(sprintf('Unexpected token %s, expected %s%s at offset %d%s', $this->formatValue($currentTokenValue), Lexer::TOKEN_LABELS[$expectedTokenType], $expectedTokenValue !== null ? sprintf(' (%s)', $this->formatValue($expectedTokenValue)) : '', $currentOffset, $currentTokenLine === null ? '' : sprintf(' on line %d', $currentTokenLine))); } - public static function isJson() : IsJson + public function getCurrentTokenValue() : string { - return new IsJson(); + return $this->currentTokenValue; } - public static function isNull() : IsNull + public function getCurrentTokenType() : int { - return new IsNull(); + return $this->currentTokenType; } - public static function isFinite() : IsFinite + public function getCurrentOffset() : int { - return new IsFinite(); + return $this->currentOffset; } - public static function isInfinite() : IsInfinite + public function getExpectedTokenType() : int { - return new IsInfinite(); + return $this->expectedTokenType; } - public static function isNan() : IsNan + public function getExpectedTokenValue() : ?string { - return new IsNan(); + return $this->expectedTokenValue; } - public static function containsEqual($value) : TraversableContainsEqual + public function getCurrentTokenLine() : ?int { - return new TraversableContainsEqual($value); + return $this->currentTokenLine; } - public static function containsIdentical($value) : TraversableContainsIdentical + private function formatValue(string $value) : string { - return new TraversableContainsIdentical($value); + $json = json_encode($value, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_INVALID_UTF8_SUBSTITUTE); + assert($json !== \false); + return $json; } - public static function containsOnly(string $type) : TraversableContainsOnly - { - return new TraversableContainsOnly($type); +} +typeParser = $typeParser; + $this->constantExprParser = $constantExprParser; + $this->doctrineConstantExprParser = $constantExprParser->toDoctrine(); + $this->requireWhitespaceBeforeDescription = $requireWhitespaceBeforeDescription; + $this->preserveTypeAliasesWithInvalidTypes = $preserveTypeAliasesWithInvalidTypes; + $this->parseDoctrineAnnotations = $parseDoctrineAnnotations; + $this->useLinesAttributes = $usedAttributes['lines'] ?? \false; + $this->useIndexAttributes = $usedAttributes['indexes'] ?? \false; + $this->textBetweenTagsBelongsToDescription = $textBetweenTagsBelongsToDescription; + } + public function parse(TokenIterator $tokens) : Ast\PhpDoc\PhpDocNode + { + $tokens->consumeTokenType(Lexer::TOKEN_OPEN_PHPDOC); + $tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL); + $children = []; + if ($this->parseDoctrineAnnotations) { + if (!$tokens->isCurrentTokenType(Lexer::TOKEN_CLOSE_PHPDOC)) { + $lastChild = $this->parseChild($tokens); + $children[] = $lastChild; + while (!$tokens->isCurrentTokenType(Lexer::TOKEN_CLOSE_PHPDOC)) { + if ($lastChild instanceof Ast\PhpDoc\PhpDocTagNode && ($lastChild->value instanceof Doctrine\DoctrineTagValueNode || $lastChild->value instanceof Ast\PhpDoc\GenericTagValueNode)) { + $tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL); + if ($tokens->isCurrentTokenType(Lexer::TOKEN_CLOSE_PHPDOC)) { + break; + } + $lastChild = $this->parseChild($tokens); + $children[] = $lastChild; + continue; + } + if (!$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL)) { + break; + } + if ($tokens->isCurrentTokenType(Lexer::TOKEN_CLOSE_PHPDOC)) { + break; + } + $lastChild = $this->parseChild($tokens); + $children[] = $lastChild; + } + } + } else { + if (!$tokens->isCurrentTokenType(Lexer::TOKEN_CLOSE_PHPDOC)) { + $children[] = $this->parseChild($tokens); + while ($tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL) && !$tokens->isCurrentTokenType(Lexer::TOKEN_CLOSE_PHPDOC)) { + $children[] = $this->parseChild($tokens); + } + } + } + try { + $tokens->consumeTokenType(Lexer::TOKEN_CLOSE_PHPDOC); + } catch (ParserException $e) { + $name = ''; + $startLine = $tokens->currentTokenLine(); + $startIndex = $tokens->currentTokenIndex(); + if (count($children) > 0) { + $lastChild = $children[count($children) - 1]; + if ($lastChild instanceof Ast\PhpDoc\PhpDocTagNode) { + $name = $lastChild->name; + $startLine = $tokens->currentTokenLine(); + $startIndex = $tokens->currentTokenIndex(); + } + } + $tag = new Ast\PhpDoc\PhpDocTagNode($name, $this->enrichWithAttributes($tokens, new Ast\PhpDoc\InvalidTagValueNode($e->getMessage(), $e), $startLine, $startIndex)); + $tokens->forwardToTheEnd(); + return $this->enrichWithAttributes($tokens, new Ast\PhpDoc\PhpDocNode([$this->enrichWithAttributes($tokens, $tag, $startLine, $startIndex)]), 1, 0); + } + return $this->enrichWithAttributes($tokens, new Ast\PhpDoc\PhpDocNode(array_values($children)), 1, 0); } - public static function containsOnlyInstancesOf(string $className) : TraversableContainsOnly + /** @phpstan-impure */ + private function parseChild(TokenIterator $tokens) : Ast\PhpDoc\PhpDocChildNode { - return new TraversableContainsOnly($className, \false); + if ($tokens->isCurrentTokenType(Lexer::TOKEN_PHPDOC_TAG)) { + $startLine = $tokens->currentTokenLine(); + $startIndex = $tokens->currentTokenIndex(); + return $this->enrichWithAttributes($tokens, $this->parseTag($tokens), $startLine, $startIndex); + } + if ($tokens->isCurrentTokenType(Lexer::TOKEN_DOCTRINE_TAG)) { + $startLine = $tokens->currentTokenLine(); + $startIndex = $tokens->currentTokenIndex(); + $tag = $tokens->currentTokenValue(); + $tokens->next(); + $tagStartLine = $tokens->currentTokenLine(); + $tagStartIndex = $tokens->currentTokenIndex(); + return $this->enrichWithAttributes($tokens, new Ast\PhpDoc\PhpDocTagNode($tag, $this->enrichWithAttributes($tokens, $this->parseDoctrineTagValue($tokens, $tag), $tagStartLine, $tagStartIndex)), $startLine, $startIndex); + } + $startLine = $tokens->currentTokenLine(); + $startIndex = $tokens->currentTokenIndex(); + $text = $this->parseText($tokens); + return $this->enrichWithAttributes($tokens, $text, $startLine, $startIndex); } /** - * @param int|string $key + * @template T of Ast\Node + * @param T $tag + * @return T */ - public static function arrayHasKey($key) : ArrayHasKey - { - return new ArrayHasKey($key); - } - public static function equalTo($value) : IsEqual - { - return new IsEqual($value, 0.0, \false, \false); - } - public static function equalToCanonicalizing($value) : IsEqualCanonicalizing + private function enrichWithAttributes(TokenIterator $tokens, Ast\Node $tag, int $startLine, int $startIndex) : Ast\Node { - return new IsEqualCanonicalizing($value); + if ($this->useLinesAttributes) { + $tag->setAttribute(Ast\Attribute::START_LINE, $startLine); + $tag->setAttribute(Ast\Attribute::END_LINE, $tokens->currentTokenLine()); + } + if ($this->useIndexAttributes) { + $tag->setAttribute(Ast\Attribute::START_INDEX, $startIndex); + $tag->setAttribute(Ast\Attribute::END_INDEX, $tokens->endIndexOfLastRelevantToken()); + } + return $tag; } - public static function equalToIgnoringCase($value) : IsEqualIgnoringCase + private function parseText(TokenIterator $tokens) : Ast\PhpDoc\PhpDocTextNode { - return new IsEqualIgnoringCase($value); + $text = ''; + $endTokens = [Lexer::TOKEN_PHPDOC_EOL, Lexer::TOKEN_CLOSE_PHPDOC, Lexer::TOKEN_END]; + if ($this->textBetweenTagsBelongsToDescription) { + $endTokens = [Lexer::TOKEN_CLOSE_PHPDOC, Lexer::TOKEN_END]; + } + $savepoint = \false; + // if the next token is EOL, everything below is skipped and empty string is returned + while ($this->textBetweenTagsBelongsToDescription || !$tokens->isCurrentTokenType(Lexer::TOKEN_PHPDOC_EOL)) { + $tmpText = $tokens->getSkippedHorizontalWhiteSpaceIfAny() . $tokens->joinUntil(Lexer::TOKEN_PHPDOC_EOL, ...$endTokens); + $text .= $tmpText; + // stop if we're not at EOL - meaning it's the end of PHPDoc + if (!$tokens->isCurrentTokenType(Lexer::TOKEN_PHPDOC_EOL)) { + break; + } + if ($this->textBetweenTagsBelongsToDescription) { + if (!$savepoint) { + $tokens->pushSavePoint(); + $savepoint = \true; + } elseif ($tmpText !== '') { + $tokens->dropSavePoint(); + $tokens->pushSavePoint(); + } + } + $tokens->pushSavePoint(); + $tokens->next(); + // if we're at EOL, check what's next + // if next is a PHPDoc tag, EOL, or end of PHPDoc, stop + if ($tokens->isCurrentTokenType(Lexer::TOKEN_PHPDOC_TAG, Lexer::TOKEN_DOCTRINE_TAG, ...$endTokens)) { + $tokens->rollback(); + break; + } + // otherwise if the next is text, continue building the description string + $tokens->dropSavePoint(); + $text .= $tokens->getDetectedNewline() ?? "\n"; + } + if ($savepoint) { + $tokens->rollback(); + $text = rtrim($text, $tokens->getDetectedNewline() ?? "\n"); + } + return new Ast\PhpDoc\PhpDocTextNode(trim($text, " \t")); } - public static function equalToWithDelta($value, float $delta) : IsEqualWithDelta + private function parseOptionalDescriptionAfterDoctrineTag(TokenIterator $tokens) : string { - return new IsEqualWithDelta($value, $delta); + $text = ''; + $endTokens = [Lexer::TOKEN_PHPDOC_EOL, Lexer::TOKEN_CLOSE_PHPDOC, Lexer::TOKEN_END]; + if ($this->textBetweenTagsBelongsToDescription) { + $endTokens = [Lexer::TOKEN_CLOSE_PHPDOC, Lexer::TOKEN_END]; + } + $savepoint = \false; + // if the next token is EOL, everything below is skipped and empty string is returned + while ($this->textBetweenTagsBelongsToDescription || !$tokens->isCurrentTokenType(Lexer::TOKEN_PHPDOC_EOL)) { + $tmpText = $tokens->getSkippedHorizontalWhiteSpaceIfAny() . $tokens->joinUntil(Lexer::TOKEN_PHPDOC_TAG, Lexer::TOKEN_DOCTRINE_TAG, Lexer::TOKEN_PHPDOC_EOL, ...$endTokens); + $text .= $tmpText; + // stop if we're not at EOL - meaning it's the end of PHPDoc + if (!$tokens->isCurrentTokenType(Lexer::TOKEN_PHPDOC_EOL)) { + if (!$tokens->isPrecededByHorizontalWhitespace()) { + return trim($text . $this->parseText($tokens)->text, " \t"); + } + if ($tokens->isCurrentTokenType(Lexer::TOKEN_PHPDOC_TAG)) { + $tokens->pushSavePoint(); + $child = $this->parseChild($tokens); + if ($child instanceof Ast\PhpDoc\PhpDocTagNode) { + if ($child->value instanceof Ast\PhpDoc\GenericTagValueNode || $child->value instanceof Doctrine\DoctrineTagValueNode) { + $tokens->rollback(); + break; + } + if ($child->value instanceof Ast\PhpDoc\InvalidTagValueNode) { + $tokens->rollback(); + $tokens->pushSavePoint(); + $tokens->next(); + if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_PARENTHESES)) { + $tokens->rollback(); + break; + } + $tokens->rollback(); + return trim($text . $this->parseText($tokens)->text, " \t"); + } + } + $tokens->rollback(); + return trim($text . $this->parseText($tokens)->text, " \t"); + } + break; + } + if ($this->textBetweenTagsBelongsToDescription) { + if (!$savepoint) { + $tokens->pushSavePoint(); + $savepoint = \true; + } elseif ($tmpText !== '') { + $tokens->dropSavePoint(); + $tokens->pushSavePoint(); + } + } + $tokens->pushSavePoint(); + $tokens->next(); + // if we're at EOL, check what's next + // if next is a PHPDoc tag, EOL, or end of PHPDoc, stop + if ($tokens->isCurrentTokenType(Lexer::TOKEN_PHPDOC_TAG, Lexer::TOKEN_DOCTRINE_TAG, ...$endTokens)) { + $tokens->rollback(); + break; + } + // otherwise if the next is text, continue building the description string + $tokens->dropSavePoint(); + $text .= $tokens->getDetectedNewline() ?? "\n"; + } + if ($savepoint) { + $tokens->rollback(); + $text = rtrim($text, $tokens->getDetectedNewline() ?? "\n"); + } + return trim($text, " \t"); } - public static function isEmpty() : IsEmpty + public function parseTag(TokenIterator $tokens) : Ast\PhpDoc\PhpDocTagNode { - return new IsEmpty(); + $tag = $tokens->currentTokenValue(); + $tokens->next(); + $value = $this->parseTagValue($tokens, $tag); + return new Ast\PhpDoc\PhpDocTagNode($tag, $value); } - public static function isWritable() : IsWritable + public function parseTagValue(TokenIterator $tokens, string $tag) : Ast\PhpDoc\PhpDocTagValueNode { - return new IsWritable(); + $startLine = $tokens->currentTokenLine(); + $startIndex = $tokens->currentTokenIndex(); + try { + $tokens->pushSavePoint(); + switch ($tag) { + case '@param': + case '@phpstan-param': + case '@psalm-param': + case '@phan-param': + $tagValue = $this->parseParamTagValue($tokens); + break; + case '@param-immediately-invoked-callable': + case '@phpstan-param-immediately-invoked-callable': + $tagValue = $this->parseParamImmediatelyInvokedCallableTagValue($tokens); + break; + case '@param-later-invoked-callable': + case '@phpstan-param-later-invoked-callable': + $tagValue = $this->parseParamLaterInvokedCallableTagValue($tokens); + break; + case '@param-closure-this': + case '@phpstan-param-closure-this': + $tagValue = $this->parseParamClosureThisTagValue($tokens); + break; + case '@var': + case '@phpstan-var': + case '@psalm-var': + case '@phan-var': + $tagValue = $this->parseVarTagValue($tokens); + break; + case '@return': + case '@phpstan-return': + case '@psalm-return': + case '@phan-return': + case '@phan-real-return': + $tagValue = $this->parseReturnTagValue($tokens); + break; + case '@throws': + case '@phpstan-throws': + $tagValue = $this->parseThrowsTagValue($tokens); + break; + case '@mixin': + case '@phan-mixin': + $tagValue = $this->parseMixinTagValue($tokens); + break; + case '@psalm-require-extends': + case '@phpstan-require-extends': + $tagValue = $this->parseRequireExtendsTagValue($tokens); + break; + case '@psalm-require-implements': + case '@phpstan-require-implements': + $tagValue = $this->parseRequireImplementsTagValue($tokens); + break; + case '@deprecated': + $tagValue = $this->parseDeprecatedTagValue($tokens); + break; + case '@property': + case '@property-read': + case '@property-write': + case '@phpstan-property': + case '@phpstan-property-read': + case '@phpstan-property-write': + case '@psalm-property': + case '@psalm-property-read': + case '@psalm-property-write': + case '@phan-property': + case '@phan-property-read': + case '@phan-property-write': + $tagValue = $this->parsePropertyTagValue($tokens); + break; + case '@method': + case '@phpstan-method': + case '@psalm-method': + case '@phan-method': + $tagValue = $this->parseMethodTagValue($tokens); + break; + case '@template': + case '@phpstan-template': + case '@psalm-template': + case '@phan-template': + case '@template-covariant': + case '@phpstan-template-covariant': + case '@psalm-template-covariant': + case '@template-contravariant': + case '@phpstan-template-contravariant': + case '@psalm-template-contravariant': + $tagValue = $this->typeParser->parseTemplateTagValue($tokens, function ($tokens) { + return $this->parseOptionalDescription($tokens); + }); + break; + case '@extends': + case '@phpstan-extends': + case '@phan-extends': + case '@phan-inherits': + case '@template-extends': + $tagValue = $this->parseExtendsTagValue('@extends', $tokens); + break; + case '@implements': + case '@phpstan-implements': + case '@template-implements': + $tagValue = $this->parseExtendsTagValue('@implements', $tokens); + break; + case '@use': + case '@phpstan-use': + case '@template-use': + $tagValue = $this->parseExtendsTagValue('@use', $tokens); + break; + case '@phpstan-type': + case '@psalm-type': + case '@phan-type': + $tagValue = $this->parseTypeAliasTagValue($tokens); + break; + case '@phpstan-import-type': + case '@psalm-import-type': + $tagValue = $this->parseTypeAliasImportTagValue($tokens); + break; + case '@phpstan-assert': + case '@phpstan-assert-if-true': + case '@phpstan-assert-if-false': + case '@psalm-assert': + case '@psalm-assert-if-true': + case '@psalm-assert-if-false': + case '@phan-assert': + case '@phan-assert-if-true': + case '@phan-assert-if-false': + $tagValue = $this->parseAssertTagValue($tokens); + break; + case '@phpstan-this-out': + case '@phpstan-self-out': + case '@psalm-this-out': + case '@psalm-self-out': + $tagValue = $this->parseSelfOutTagValue($tokens); + break; + case '@param-out': + case '@phpstan-param-out': + case '@psalm-param-out': + $tagValue = $this->parseParamOutTagValue($tokens); + break; + default: + if ($this->parseDoctrineAnnotations) { + if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_PARENTHESES)) { + $tagValue = $this->parseDoctrineTagValue($tokens, $tag); + } else { + $tagValue = new Ast\PhpDoc\GenericTagValueNode($this->parseOptionalDescriptionAfterDoctrineTag($tokens)); + } + break; + } + $tagValue = new Ast\PhpDoc\GenericTagValueNode($this->parseOptionalDescription($tokens)); + break; + } + $tokens->dropSavePoint(); + } catch (ParserException $e) { + $tokens->rollback(); + $tagValue = new Ast\PhpDoc\InvalidTagValueNode($this->parseOptionalDescription($tokens), $e); + } + return $this->enrichWithAttributes($tokens, $tagValue, $startLine, $startIndex); } - public static function isReadable() : IsReadable + private function parseDoctrineTagValue(TokenIterator $tokens, string $tag) : Ast\PhpDoc\PhpDocTagValueNode { - return new IsReadable(); + $startLine = $tokens->currentTokenLine(); + $startIndex = $tokens->currentTokenIndex(); + return new Doctrine\DoctrineTagValueNode($this->enrichWithAttributes($tokens, new Doctrine\DoctrineAnnotation($tag, $this->parseDoctrineArguments($tokens, \false)), $startLine, $startIndex), $this->parseOptionalDescriptionAfterDoctrineTag($tokens)); } - public static function directoryExists() : DirectoryExists + /** + * @return list + */ + private function parseDoctrineArguments(TokenIterator $tokens, bool $deep) : array { - return new DirectoryExists(); + if (!$tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_PARENTHESES)) { + return []; + } + if (!$deep) { + $tokens->addEndOfLineToSkippedTokens(); + } + $arguments = []; + try { + $tokens->consumeTokenType(Lexer::TOKEN_OPEN_PARENTHESES); + do { + if ($tokens->isCurrentTokenType(Lexer::TOKEN_CLOSE_PARENTHESES)) { + break; + } + $arguments[] = $this->parseDoctrineArgument($tokens); + } while ($tokens->tryConsumeTokenType(Lexer::TOKEN_COMMA)); + } finally { + if (!$deep) { + $tokens->removeEndOfLineFromSkippedTokens(); + } + } + $tokens->consumeTokenType(Lexer::TOKEN_CLOSE_PARENTHESES); + return $arguments; } - public static function fileExists() : FileExists + private function parseDoctrineArgument(TokenIterator $tokens) : Doctrine\DoctrineArgument { - return new FileExists(); + if (!$tokens->isCurrentTokenType(Lexer::TOKEN_IDENTIFIER)) { + $startLine = $tokens->currentTokenLine(); + $startIndex = $tokens->currentTokenIndex(); + return $this->enrichWithAttributes($tokens, new Doctrine\DoctrineArgument(null, $this->parseDoctrineArgumentValue($tokens)), $startLine, $startIndex); + } + $startLine = $tokens->currentTokenLine(); + $startIndex = $tokens->currentTokenIndex(); + try { + $tokens->pushSavePoint(); + $currentValue = $tokens->currentTokenValue(); + $tokens->consumeTokenType(Lexer::TOKEN_IDENTIFIER); + $key = $this->enrichWithAttributes($tokens, new IdentifierTypeNode($currentValue), $startLine, $startIndex); + $tokens->consumeTokenType(Lexer::TOKEN_EQUAL); + $value = $this->parseDoctrineArgumentValue($tokens); + $tokens->dropSavePoint(); + return $this->enrichWithAttributes($tokens, new Doctrine\DoctrineArgument($key, $value), $startLine, $startIndex); + } catch (ParserException $e) { + $tokens->rollback(); + return $this->enrichWithAttributes($tokens, new Doctrine\DoctrineArgument(null, $this->parseDoctrineArgumentValue($tokens)), $startLine, $startIndex); + } } - public static function greaterThan($value) : GreaterThan + /** + * @return DoctrineValueType + */ + private function parseDoctrineArgumentValue(TokenIterator $tokens) { - return new GreaterThan($value); + $startLine = $tokens->currentTokenLine(); + $startIndex = $tokens->currentTokenIndex(); + if ($tokens->isCurrentTokenType(Lexer::TOKEN_PHPDOC_TAG, Lexer::TOKEN_DOCTRINE_TAG)) { + $name = $tokens->currentTokenValue(); + $tokens->next(); + return $this->enrichWithAttributes($tokens, new Doctrine\DoctrineAnnotation($name, $this->parseDoctrineArguments($tokens, \true)), $startLine, $startIndex); + } + if ($tokens->tryConsumeTokenType(Lexer::TOKEN_OPEN_CURLY_BRACKET)) { + $items = []; + do { + if ($tokens->isCurrentTokenType(Lexer::TOKEN_CLOSE_CURLY_BRACKET)) { + break; + } + $items[] = $this->parseDoctrineArrayItem($tokens); + } while ($tokens->tryConsumeTokenType(Lexer::TOKEN_COMMA)); + $tokens->consumeTokenType(Lexer::TOKEN_CLOSE_CURLY_BRACKET); + return $this->enrichWithAttributes($tokens, new Doctrine\DoctrineArray($items), $startLine, $startIndex); + } + $currentTokenValue = $tokens->currentTokenValue(); + $tokens->pushSavePoint(); + // because of ConstFetchNode + if ($tokens->tryConsumeTokenType(Lexer::TOKEN_IDENTIFIER)) { + $identifier = $this->enrichWithAttributes($tokens, new Ast\Type\IdentifierTypeNode($currentTokenValue), $startLine, $startIndex); + if (!$tokens->isCurrentTokenType(Lexer::TOKEN_DOUBLE_COLON)) { + $tokens->dropSavePoint(); + return $identifier; + } + $tokens->rollback(); + // because of ConstFetchNode + } else { + $tokens->dropSavePoint(); + // because of ConstFetchNode + } + $currentTokenValue = $tokens->currentTokenValue(); + $currentTokenType = $tokens->currentTokenType(); + $currentTokenOffset = $tokens->currentTokenOffset(); + $currentTokenLine = $tokens->currentTokenLine(); + try { + $constExpr = $this->doctrineConstantExprParser->parse($tokens, \true); + if ($constExpr instanceof Ast\ConstExpr\ConstExprArrayNode) { + throw new ParserException($currentTokenValue, $currentTokenType, $currentTokenOffset, Lexer::TOKEN_IDENTIFIER, null, $currentTokenLine); + } + return $constExpr; + } catch (LogicException $e) { + throw new ParserException($currentTokenValue, $currentTokenType, $currentTokenOffset, Lexer::TOKEN_IDENTIFIER, null, $currentTokenLine); + } } - public static function greaterThanOrEqual($value) : LogicalOr + private function parseDoctrineArrayItem(TokenIterator $tokens) : Doctrine\DoctrineArrayItem { - return static::logicalOr(new IsEqual($value), new GreaterThan($value)); + $startLine = $tokens->currentTokenLine(); + $startIndex = $tokens->currentTokenIndex(); + try { + $tokens->pushSavePoint(); + $key = $this->parseDoctrineArrayKey($tokens); + if (!$tokens->tryConsumeTokenType(Lexer::TOKEN_EQUAL)) { + if (!$tokens->tryConsumeTokenType(Lexer::TOKEN_COLON)) { + $tokens->consumeTokenType(Lexer::TOKEN_EQUAL); + // will throw exception + } + } + $value = $this->parseDoctrineArgumentValue($tokens); + $tokens->dropSavePoint(); + return $this->enrichWithAttributes($tokens, new Doctrine\DoctrineArrayItem($key, $value), $startLine, $startIndex); + } catch (ParserException $e) { + $tokens->rollback(); + return $this->enrichWithAttributes($tokens, new Doctrine\DoctrineArrayItem(null, $this->parseDoctrineArgumentValue($tokens)), $startLine, $startIndex); + } } /** - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4601 + * @return ConstExprIntegerNode|ConstExprStringNode|IdentifierTypeNode|ConstFetchNode */ - public static function classHasAttribute(string $attributeName) : ClassHasAttribute + private function parseDoctrineArrayKey(TokenIterator $tokens) { - self::createWarning('classHasAttribute() is deprecated and will be removed in PHPUnit 10.'); - return new ClassHasAttribute($attributeName); + $startLine = $tokens->currentTokenLine(); + $startIndex = $tokens->currentTokenIndex(); + if ($tokens->isCurrentTokenType(Lexer::TOKEN_INTEGER)) { + $key = new Ast\ConstExpr\ConstExprIntegerNode(str_replace('_', '', $tokens->currentTokenValue())); + $tokens->next(); + } elseif ($tokens->isCurrentTokenType(Lexer::TOKEN_DOCTRINE_ANNOTATION_STRING)) { + $key = new Ast\ConstExpr\DoctrineConstExprStringNode(Ast\ConstExpr\DoctrineConstExprStringNode::unescape($tokens->currentTokenValue())); + $tokens->next(); + } elseif ($tokens->isCurrentTokenType(Lexer::TOKEN_DOUBLE_QUOTED_STRING)) { + $value = $tokens->currentTokenValue(); + $tokens->next(); + $key = $this->doctrineConstantExprParser->parseDoctrineString($value, $tokens); + } else { + $currentTokenValue = $tokens->currentTokenValue(); + $tokens->pushSavePoint(); + // because of ConstFetchNode + if (!$tokens->tryConsumeTokenType(Lexer::TOKEN_IDENTIFIER)) { + $tokens->dropSavePoint(); + throw new ParserException($tokens->currentTokenValue(), $tokens->currentTokenType(), $tokens->currentTokenOffset(), Lexer::TOKEN_IDENTIFIER, null, $tokens->currentTokenLine()); + } + if (!$tokens->isCurrentTokenType(Lexer::TOKEN_DOUBLE_COLON)) { + $tokens->dropSavePoint(); + return $this->enrichWithAttributes($tokens, new IdentifierTypeNode($currentTokenValue), $startLine, $startIndex); + } + $tokens->rollback(); + $constExpr = $this->doctrineConstantExprParser->parse($tokens, \true); + if (!$constExpr instanceof Ast\ConstExpr\ConstFetchNode) { + throw new ParserException($tokens->currentTokenValue(), $tokens->currentTokenType(), $tokens->currentTokenOffset(), Lexer::TOKEN_IDENTIFIER, null, $tokens->currentTokenLine()); + } + return $constExpr; + } + return $this->enrichWithAttributes($tokens, $key, $startLine, $startIndex); } /** - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4601 + * @return Ast\PhpDoc\ParamTagValueNode|Ast\PhpDoc\TypelessParamTagValueNode */ - public static function classHasStaticAttribute(string $attributeName) : ClassHasStaticAttribute + private function parseParamTagValue(TokenIterator $tokens) : Ast\PhpDoc\PhpDocTagValueNode { - self::createWarning('classHasStaticAttribute() is deprecated and will be removed in PHPUnit 10.'); - return new ClassHasStaticAttribute($attributeName); + if ($tokens->isCurrentTokenType(Lexer::TOKEN_REFERENCE, Lexer::TOKEN_VARIADIC, Lexer::TOKEN_VARIABLE)) { + $type = null; + } else { + $type = $this->typeParser->parse($tokens); + } + $isReference = $tokens->tryConsumeTokenType(Lexer::TOKEN_REFERENCE); + $isVariadic = $tokens->tryConsumeTokenType(Lexer::TOKEN_VARIADIC); + $parameterName = $this->parseRequiredVariableName($tokens); + $description = $this->parseOptionalDescription($tokens); + if ($type !== null) { + return new Ast\PhpDoc\ParamTagValueNode($type, $isVariadic, $parameterName, $description, $isReference); + } + return new Ast\PhpDoc\TypelessParamTagValueNode($isVariadic, $parameterName, $description, $isReference); } - /** - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4601 - */ - public static function objectHasAttribute($attributeName) : ObjectHasAttribute + private function parseParamImmediatelyInvokedCallableTagValue(TokenIterator $tokens) : Ast\PhpDoc\ParamImmediatelyInvokedCallableTagValueNode { - self::createWarning('objectHasAttribute() is deprecated and will be removed in PHPUnit 10.'); - return new ObjectHasAttribute($attributeName); + $parameterName = $this->parseRequiredVariableName($tokens); + $description = $this->parseOptionalDescription($tokens); + return new Ast\PhpDoc\ParamImmediatelyInvokedCallableTagValueNode($parameterName, $description); } - public static function identicalTo($value) : IsIdentical + private function parseParamLaterInvokedCallableTagValue(TokenIterator $tokens) : Ast\PhpDoc\ParamLaterInvokedCallableTagValueNode { - return new IsIdentical($value); + $parameterName = $this->parseRequiredVariableName($tokens); + $description = $this->parseOptionalDescription($tokens); + return new Ast\PhpDoc\ParamLaterInvokedCallableTagValueNode($parameterName, $description); } - public static function isInstanceOf(string $className) : IsInstanceOf + private function parseParamClosureThisTagValue(TokenIterator $tokens) : Ast\PhpDoc\ParamClosureThisTagValueNode { - return new IsInstanceOf($className); + $type = $this->typeParser->parse($tokens); + $parameterName = $this->parseRequiredVariableName($tokens); + $description = $this->parseOptionalDescription($tokens); + return new Ast\PhpDoc\ParamClosureThisTagValueNode($type, $parameterName, $description); } - public static function isType(string $type) : IsType + private function parseVarTagValue(TokenIterator $tokens) : Ast\PhpDoc\VarTagValueNode { - return new IsType($type); + $type = $this->typeParser->parse($tokens); + $variableName = $this->parseOptionalVariableName($tokens); + $description = $this->parseOptionalDescription($tokens, $variableName === ''); + return new Ast\PhpDoc\VarTagValueNode($type, $variableName, $description); } - public static function lessThan($value) : LessThan + private function parseReturnTagValue(TokenIterator $tokens) : Ast\PhpDoc\ReturnTagValueNode { - return new LessThan($value); + $type = $this->typeParser->parse($tokens); + $description = $this->parseOptionalDescription($tokens, \true); + return new Ast\PhpDoc\ReturnTagValueNode($type, $description); } - public static function lessThanOrEqual($value) : LogicalOr + private function parseThrowsTagValue(TokenIterator $tokens) : Ast\PhpDoc\ThrowsTagValueNode { - return static::logicalOr(new IsEqual($value), new LessThan($value)); + $type = $this->typeParser->parse($tokens); + $description = $this->parseOptionalDescription($tokens, \true); + return new Ast\PhpDoc\ThrowsTagValueNode($type, $description); } - public static function matchesRegularExpression(string $pattern) : RegularExpression + private function parseMixinTagValue(TokenIterator $tokens) : Ast\PhpDoc\MixinTagValueNode { - return new RegularExpression($pattern); + $type = $this->typeParser->parse($tokens); + $description = $this->parseOptionalDescription($tokens, \true); + return new Ast\PhpDoc\MixinTagValueNode($type, $description); } - public static function matches(string $string) : StringMatchesFormatDescription + private function parseRequireExtendsTagValue(TokenIterator $tokens) : Ast\PhpDoc\RequireExtendsTagValueNode { - return new StringMatchesFormatDescription($string); + $type = $this->typeParser->parse($tokens); + $description = $this->parseOptionalDescription($tokens, \true); + return new Ast\PhpDoc\RequireExtendsTagValueNode($type, $description); } - public static function stringStartsWith($prefix) : StringStartsWith + private function parseRequireImplementsTagValue(TokenIterator $tokens) : Ast\PhpDoc\RequireImplementsTagValueNode { - return new StringStartsWith($prefix); + $type = $this->typeParser->parse($tokens); + $description = $this->parseOptionalDescription($tokens, \true); + return new Ast\PhpDoc\RequireImplementsTagValueNode($type, $description); } - public static function stringContains(string $string, bool $case = \true) : StringContains + private function parseDeprecatedTagValue(TokenIterator $tokens) : Ast\PhpDoc\DeprecatedTagValueNode { - return new StringContains($string, $case); + $description = $this->parseOptionalDescription($tokens); + return new Ast\PhpDoc\DeprecatedTagValueNode($description); } - public static function stringEndsWith(string $suffix) : StringEndsWith + private function parsePropertyTagValue(TokenIterator $tokens) : Ast\PhpDoc\PropertyTagValueNode { - return new StringEndsWith($suffix); + $type = $this->typeParser->parse($tokens); + $parameterName = $this->parseRequiredVariableName($tokens); + $description = $this->parseOptionalDescription($tokens); + return new Ast\PhpDoc\PropertyTagValueNode($type, $parameterName, $description); } - public static function countOf(int $count) : Count + private function parseMethodTagValue(TokenIterator $tokens) : Ast\PhpDoc\MethodTagValueNode { - return new Count($count); + $staticKeywordOrReturnTypeOrMethodName = $this->typeParser->parse($tokens); + if ($staticKeywordOrReturnTypeOrMethodName instanceof Ast\Type\IdentifierTypeNode && $staticKeywordOrReturnTypeOrMethodName->name === 'static') { + $isStatic = \true; + $returnTypeOrMethodName = $this->typeParser->parse($tokens); + } else { + $isStatic = \false; + $returnTypeOrMethodName = $staticKeywordOrReturnTypeOrMethodName; + } + if ($tokens->isCurrentTokenType(Lexer::TOKEN_IDENTIFIER)) { + $returnType = $returnTypeOrMethodName; + $methodName = $tokens->currentTokenValue(); + $tokens->next(); + } elseif ($returnTypeOrMethodName instanceof Ast\Type\IdentifierTypeNode) { + $returnType = $isStatic ? $staticKeywordOrReturnTypeOrMethodName : null; + $methodName = $returnTypeOrMethodName->name; + $isStatic = \false; + } else { + $tokens->consumeTokenType(Lexer::TOKEN_IDENTIFIER); + // will throw exception + exit; + } + $templateTypes = []; + if ($tokens->tryConsumeTokenType(Lexer::TOKEN_OPEN_ANGLE_BRACKET)) { + do { + $startLine = $tokens->currentTokenLine(); + $startIndex = $tokens->currentTokenIndex(); + $templateTypes[] = $this->enrichWithAttributes($tokens, $this->typeParser->parseTemplateTagValue($tokens), $startLine, $startIndex); + } while ($tokens->tryConsumeTokenType(Lexer::TOKEN_COMMA)); + $tokens->consumeTokenType(Lexer::TOKEN_CLOSE_ANGLE_BRACKET); + } + $parameters = []; + $tokens->consumeTokenType(Lexer::TOKEN_OPEN_PARENTHESES); + if (!$tokens->isCurrentTokenType(Lexer::TOKEN_CLOSE_PARENTHESES)) { + $parameters[] = $this->parseMethodTagValueParameter($tokens); + while ($tokens->tryConsumeTokenType(Lexer::TOKEN_COMMA)) { + $parameters[] = $this->parseMethodTagValueParameter($tokens); + } + } + $tokens->consumeTokenType(Lexer::TOKEN_CLOSE_PARENTHESES); + $description = $this->parseOptionalDescription($tokens); + return new Ast\PhpDoc\MethodTagValueNode($isStatic, $returnType, $methodName, $parameters, $description, $templateTypes); + } + private function parseMethodTagValueParameter(TokenIterator $tokens) : Ast\PhpDoc\MethodTagValueParameterNode + { + $startLine = $tokens->currentTokenLine(); + $startIndex = $tokens->currentTokenIndex(); + switch ($tokens->currentTokenType()) { + case Lexer::TOKEN_IDENTIFIER: + case Lexer::TOKEN_OPEN_PARENTHESES: + case Lexer::TOKEN_NULLABLE: + $parameterType = $this->typeParser->parse($tokens); + break; + default: + $parameterType = null; + } + $isReference = $tokens->tryConsumeTokenType(Lexer::TOKEN_REFERENCE); + $isVariadic = $tokens->tryConsumeTokenType(Lexer::TOKEN_VARIADIC); + $parameterName = $tokens->currentTokenValue(); + $tokens->consumeTokenType(Lexer::TOKEN_VARIABLE); + if ($tokens->tryConsumeTokenType(Lexer::TOKEN_EQUAL)) { + $defaultValue = $this->constantExprParser->parse($tokens); + } else { + $defaultValue = null; + } + return $this->enrichWithAttributes($tokens, new Ast\PhpDoc\MethodTagValueParameterNode($parameterType, $isReference, $isVariadic, $parameterName, $defaultValue), $startLine, $startIndex); + } + private function parseExtendsTagValue(string $tagName, TokenIterator $tokens) : Ast\PhpDoc\PhpDocTagValueNode + { + $startLine = $tokens->currentTokenLine(); + $startIndex = $tokens->currentTokenIndex(); + $baseType = new IdentifierTypeNode($tokens->currentTokenValue()); + $tokens->consumeTokenType(Lexer::TOKEN_IDENTIFIER); + $type = $this->typeParser->parseGeneric($tokens, $this->typeParser->enrichWithAttributes($tokens, $baseType, $startLine, $startIndex)); + $description = $this->parseOptionalDescription($tokens); + switch ($tagName) { + case '@extends': + return new Ast\PhpDoc\ExtendsTagValueNode($type, $description); + case '@implements': + return new Ast\PhpDoc\ImplementsTagValueNode($type, $description); + case '@use': + return new Ast\PhpDoc\UsesTagValueNode($type, $description); + } + throw new ShouldNotHappenException(); + } + private function parseTypeAliasTagValue(TokenIterator $tokens) : Ast\PhpDoc\TypeAliasTagValueNode + { + $alias = $tokens->currentTokenValue(); + $tokens->consumeTokenType(Lexer::TOKEN_IDENTIFIER); + // support phan-type/psalm-type syntax + $tokens->tryConsumeTokenType(Lexer::TOKEN_EQUAL); + if ($this->preserveTypeAliasesWithInvalidTypes) { + $startLine = $tokens->currentTokenLine(); + $startIndex = $tokens->currentTokenIndex(); + try { + $type = $this->typeParser->parse($tokens); + if (!$tokens->isCurrentTokenType(Lexer::TOKEN_CLOSE_PHPDOC)) { + if (!$tokens->isCurrentTokenType(Lexer::TOKEN_PHPDOC_EOL)) { + throw new ParserException($tokens->currentTokenValue(), $tokens->currentTokenType(), $tokens->currentTokenOffset(), Lexer::TOKEN_PHPDOC_EOL, null, $tokens->currentTokenLine()); + } + } + return new Ast\PhpDoc\TypeAliasTagValueNode($alias, $type); + } catch (ParserException $e) { + $this->parseOptionalDescription($tokens); + return new Ast\PhpDoc\TypeAliasTagValueNode($alias, $this->enrichWithAttributes($tokens, new Ast\Type\InvalidTypeNode($e), $startLine, $startIndex)); + } + } + $type = $this->typeParser->parse($tokens); + return new Ast\PhpDoc\TypeAliasTagValueNode($alias, $type); } - public static function objectEquals(object $object, string $method = 'equals') : ObjectEquals + private function parseTypeAliasImportTagValue(TokenIterator $tokens) : Ast\PhpDoc\TypeAliasImportTagValueNode { - return new ObjectEquals($object, $method); + $importedAlias = $tokens->currentTokenValue(); + $tokens->consumeTokenType(Lexer::TOKEN_IDENTIFIER); + $tokens->consumeTokenValue(Lexer::TOKEN_IDENTIFIER, 'from'); + $identifierStartLine = $tokens->currentTokenLine(); + $identifierStartIndex = $tokens->currentTokenIndex(); + $importedFrom = $tokens->currentTokenValue(); + $tokens->consumeTokenType(Lexer::TOKEN_IDENTIFIER); + $importedFromType = $this->enrichWithAttributes($tokens, new IdentifierTypeNode($importedFrom), $identifierStartLine, $identifierStartIndex); + $importedAs = null; + if ($tokens->tryConsumeTokenValue('as')) { + $importedAs = $tokens->currentTokenValue(); + $tokens->consumeTokenType(Lexer::TOKEN_IDENTIFIER); + } + return new Ast\PhpDoc\TypeAliasImportTagValueNode($importedAlias, $importedFromType, $importedAs); } /** - * Fails a test with the given message. - * - * @throws AssertionFailedError - * - * @psalm-return never-return + * @return Ast\PhpDoc\AssertTagValueNode|Ast\PhpDoc\AssertTagPropertyValueNode|Ast\PhpDoc\AssertTagMethodValueNode */ - public static function fail(string $message = '') : void + private function parseAssertTagValue(TokenIterator $tokens) : Ast\PhpDoc\PhpDocTagValueNode { - self::$count++; - throw new \PHPUnit\Framework\AssertionFailedError($message); + $isNegated = $tokens->tryConsumeTokenType(Lexer::TOKEN_NEGATED); + $isEquality = $tokens->tryConsumeTokenType(Lexer::TOKEN_EQUAL); + $type = $this->typeParser->parse($tokens); + $parameter = $this->parseAssertParameter($tokens); + $description = $this->parseOptionalDescription($tokens); + if (array_key_exists('method', $parameter)) { + return new Ast\PhpDoc\AssertTagMethodValueNode($type, $parameter['parameter'], $parameter['method'], $isNegated, $description, $isEquality); + } elseif (array_key_exists('property', $parameter)) { + return new Ast\PhpDoc\AssertTagPropertyValueNode($type, $parameter['parameter'], $parameter['property'], $isNegated, $description, $isEquality); + } + return new Ast\PhpDoc\AssertTagValueNode($type, $parameter['parameter'], $isNegated, $description, $isEquality); } /** - * Mark the test as incomplete. - * - * @throws IncompleteTestError - * - * @psalm-return never-return + * @return array{parameter: string}|array{parameter: string, property: string}|array{parameter: string, method: string} */ - public static function markTestIncomplete(string $message = '') : void + private function parseAssertParameter(TokenIterator $tokens) : array { - throw new \PHPUnit\Framework\IncompleteTestError($message); + if ($tokens->isCurrentTokenType(Lexer::TOKEN_THIS_VARIABLE)) { + $parameter = '$this'; + $tokens->next(); + } else { + $parameter = $tokens->currentTokenValue(); + $tokens->consumeTokenType(Lexer::TOKEN_VARIABLE); + } + if ($tokens->isCurrentTokenType(Lexer::TOKEN_ARROW)) { + $tokens->consumeTokenType(Lexer::TOKEN_ARROW); + $propertyOrMethod = $tokens->currentTokenValue(); + $tokens->consumeTokenType(Lexer::TOKEN_IDENTIFIER); + if ($tokens->tryConsumeTokenType(Lexer::TOKEN_OPEN_PARENTHESES)) { + $tokens->consumeTokenType(Lexer::TOKEN_CLOSE_PARENTHESES); + return ['parameter' => $parameter, 'method' => $propertyOrMethod]; + } + return ['parameter' => $parameter, 'property' => $propertyOrMethod]; + } + return ['parameter' => $parameter]; } - /** - * Mark the test as skipped. - * - * @throws SkippedTestError - * @throws SyntheticSkippedError - * - * @psalm-return never-return - */ - public static function markTestSkipped(string $message = '') : void + private function parseSelfOutTagValue(TokenIterator $tokens) : Ast\PhpDoc\SelfOutTagValueNode { - if ($hint = self::detectLocationHint($message)) { - $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); - array_unshift($trace, $hint); - throw new \PHPUnit\Framework\SyntheticSkippedError($hint['message'], 0, $hint['file'], (int) $hint['line'], $trace); - } - throw new \PHPUnit\Framework\SkippedTestError($message); + $type = $this->typeParser->parse($tokens); + $description = $this->parseOptionalDescription($tokens); + return new Ast\PhpDoc\SelfOutTagValueNode($type, $description); } - /** - * Return the current assertion count. - */ - public static function getCount() : int + private function parseParamOutTagValue(TokenIterator $tokens) : Ast\PhpDoc\ParamOutTagValueNode { - return self::$count; + $type = $this->typeParser->parse($tokens); + $parameterName = $this->parseRequiredVariableName($tokens); + $description = $this->parseOptionalDescription($tokens); + return new Ast\PhpDoc\ParamOutTagValueNode($type, $parameterName, $description); } - /** - * Reset the assertion counter. - */ - public static function resetCount() : void + private function parseOptionalVariableName(TokenIterator $tokens) : string { - self::$count = 0; + if ($tokens->isCurrentTokenType(Lexer::TOKEN_VARIABLE)) { + $parameterName = $tokens->currentTokenValue(); + $tokens->next(); + } elseif ($tokens->isCurrentTokenType(Lexer::TOKEN_THIS_VARIABLE)) { + $parameterName = '$this'; + $tokens->next(); + } else { + $parameterName = ''; + } + return $parameterName; } - private static function detectLocationHint(string $message) : ?array + private function parseRequiredVariableName(TokenIterator $tokens) : string { - $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]; + $parameterName = $tokens->currentTokenValue(); + $tokens->consumeTokenType(Lexer::TOKEN_VARIABLE); + return $parameterName; + } + private function parseOptionalDescription(TokenIterator $tokens, bool $limitStartToken = \false) : string + { + if ($limitStartToken) { + foreach (self::DISALLOWED_DESCRIPTION_START_TOKENS as $disallowedStartToken) { + if (!$tokens->isCurrentTokenType($disallowedStartToken)) { + continue; + } + $tokens->consumeTokenType(Lexer::TOKEN_OTHER); + // will throw exception } - if ($offset[0] === '__OFFSET_LINE') { - $hint['line'] = $offset[1]; + if ($this->requireWhitespaceBeforeDescription && !$tokens->isCurrentTokenType(Lexer::TOKEN_PHPDOC_EOL, Lexer::TOKEN_CLOSE_PHPDOC, Lexer::TOKEN_END) && !$tokens->isPrecededByHorizontalWhitespace()) { + $tokens->consumeTokenType(Lexer::TOKEN_HORIZONTAL_WS); + // will throw exception } } - if ($hint) { - $hint['message'] = implode(PHP_EOL, $lines); - } - return $hint; + return $this->parseText($tokens)->text; } - private static function isValidObjectAttributeName(string $attributeName) : bool +} + '\\', 'n' => "\n", 'r' => "\r", 't' => "\t", 'f' => "\f", 'v' => "\v", 'e' => "\x1b"]; + public static function unescapeString(string $string) : string { - return (bool) preg_match('/[^\\x00-\\x1f\\x7f-\\x9f]+/', $attributeName); + $quote = $string[0]; + if ($quote === '\'') { + return str_replace(['\\\\', '\\\''], ['\\', '\''], substr($string, 1, -1)); + } + return self::parseEscapeSequences(substr($string, 1, -1), '"'); } - private static function isValidClassAttributeName(string $attributeName) : bool + /** + * Implementation based on https://github.com/nikic/PHP-Parser/blob/b0edd4c41111042d43bb45c6c657b2e0db367d9e/lib/PhpParser/Node/Scalar/String_.php#L90-L130 + */ + private static function parseEscapeSequences(string $str, string $quote) : string { - return (bool) preg_match('/[a-zA-Z_\\x7f-\\xff][a-zA-Z0-9_\\x7f-\\xff]*/', $attributeName); + $str = str_replace('\\' . $quote, $quote, $str); + return preg_replace_callback('~\\\\([\\\\nrtfve]|[xX][0-9a-fA-F]{1,2}|[0-7]{1,3}|u\\{([0-9a-fA-F]+)\\})~', static function ($matches) { + $str = $matches[1]; + if (isset(self::REPLACEMENTS[$str])) { + return self::REPLACEMENTS[$str]; + } + if ($str[0] === 'x' || $str[0] === 'X') { + return chr((int) hexdec(substr($str, 1))); + } + if ($str[0] === 'u') { + return self::codePointToUtf8((int) hexdec($matches[2])); + } + return chr((int) octdec($str)); + }, $str); } /** - * @codeCoverageIgnore + * Implementation based on https://github.com/nikic/PHP-Parser/blob/b0edd4c41111042d43bb45c6c657b2e0db367d9e/lib/PhpParser/Node/Scalar/String_.php#L132-L154 */ - private static function createWarning(string $warning) : void + private static function codePointToUtf8(int $num) : string { - foreach (debug_backtrace() as $step) { - if (isset($step['object']) && $step['object'] instanceof \PHPUnit\Framework\TestCase) { - assert($step['object'] instanceof \PHPUnit\Framework\TestCase); - $step['object']->addWarning($warning); - break; - } + if ($num <= 0x7f) { + return chr($num); + } + if ($num <= 0x7ff) { + return chr(($num >> 6) + 0xc0) . chr(($num & 0x3f) + 0x80); + } + if ($num <= 0xffff) { + return chr(($num >> 12) + 0xe0) . chr(($num >> 6 & 0x3f) + 0x80) . chr(($num & 0x3f) + 0x80); + } + if ($num <= 0x1fffff) { + return chr(($num >> 18) + 0xf0) . chr(($num >> 12 & 0x3f) + 0x80) . chr(($num >> 6 & 0x3f) + 0x80) . chr(($num & 0x3f) + 0x80); } + // Invalid UTF-8 codepoint escape sequence: Codepoint too large + return "�"; } } - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework; +namespace PHPUnitPHAR\PHPStan\PhpDocParser\Parser; -use function func_get_args; -use function function_exists; -use ArrayAccess; -use Countable; -use DOMDocument; -use DOMElement; -use PHPUnit\Framework\Constraint\ArrayHasKey; -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\IsEqualCanonicalizing; -use PHPUnit\Framework\Constraint\IsEqualIgnoringCase; -use PHPUnit\Framework\Constraint\IsEqualWithDelta; -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\ObjectEquals; -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\TraversableContainsEqual; -use PHPUnit\Framework\Constraint\TraversableContainsIdentical; -use PHPUnit\Framework\Constraint\TraversableContainsOnly; -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; -use Throwable; -if (!function_exists('PHPUnit\\Framework\\assertArrayHasKey')) { +use LogicException; +use PHPUnitPHAR\PHPStan\PhpDocParser\Lexer\Lexer; +use function array_pop; +use function assert; +use function count; +use function in_array; +use function strlen; +use function substr; +class TokenIterator +{ + /** @var list */ + private $tokens; + /** @var int */ + private $index; + /** @var int[] */ + private $savePoints = []; + /** @var list */ + private $skippedTokenTypes = [Lexer::TOKEN_HORIZONTAL_WS]; + /** @var string|null */ + private $newline = null; /** - * Asserts that an array has a specified key. - * - * @param int|string $key - * @param array|ArrayAccess $array - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws Exception - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertArrayHasKey + * @param list $tokens */ - function assertArrayHasKey($key, $array, string $message = '') : void + public function __construct(array $tokens, int $index = 0) { - \PHPUnit\Framework\Assert::assertArrayHasKey(...func_get_args()); + $this->tokens = $tokens; + $this->index = $index; + $this->skipIrrelevantTokens(); } -} -if (!function_exists('PHPUnit\\Framework\\assertArrayNotHasKey')) { /** - * 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 - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertArrayNotHasKey + * @return list */ - function assertArrayNotHasKey($key, $array, string $message = '') : void + public function getTokens() : array { - \PHPUnit\Framework\Assert::assertArrayNotHasKey(...func_get_args()); + return $this->tokens; } -} -if (!function_exists('PHPUnit\\Framework\\assertContains')) { - /** - * Asserts that a haystack contains a needle. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws Exception - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertContains - */ - function assertContains($needle, iterable $haystack, string $message = '') : void + public function getContentBetween(int $startPos, int $endPos) : string { - \PHPUnit\Framework\Assert::assertContains(...func_get_args()); + if ($startPos < 0 || $endPos > count($this->tokens)) { + throw new LogicException(); + } + $content = ''; + for ($i = $startPos; $i < $endPos; $i++) { + $content .= $this->tokens[$i][Lexer::VALUE_OFFSET]; + } + return $content; } -} -if (!function_exists('PHPUnit\\Framework\\assertContainsEquals')) { - function assertContainsEquals($needle, iterable $haystack, string $message = '') : void + public function getTokenCount() : int { - \PHPUnit\Framework\Assert::assertContainsEquals(...func_get_args()); + return count($this->tokens); } -} -if (!function_exists('PHPUnit\\Framework\\assertNotContains')) { - /** - * Asserts that a haystack does not contain a needle. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws Exception - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertNotContains - */ - function assertNotContains($needle, iterable $haystack, string $message = '') : void + public function currentTokenValue() : string { - \PHPUnit\Framework\Assert::assertNotContains(...func_get_args()); + return $this->tokens[$this->index][Lexer::VALUE_OFFSET]; } -} -if (!function_exists('PHPUnit\\Framework\\assertNotContainsEquals')) { - function assertNotContainsEquals($needle, iterable $haystack, string $message = '') : void + public function currentTokenType() : int { - \PHPUnit\Framework\Assert::assertNotContainsEquals(...func_get_args()); + return $this->tokens[$this->index][Lexer::TYPE_OFFSET]; } -} -if (!function_exists('PHPUnit\\Framework\\assertContainsOnly')) { - /** - * Asserts that a haystack contains only values of a given type. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertContainsOnly - */ - function assertContainsOnly(string $type, iterable $haystack, ?bool $isNativeType = null, string $message = '') : void + public function currentTokenOffset() : int { - \PHPUnit\Framework\Assert::assertContainsOnly(...func_get_args()); + $offset = 0; + for ($i = 0; $i < $this->index; $i++) { + $offset += strlen($this->tokens[$i][Lexer::VALUE_OFFSET]); + } + return $offset; } -} -if (!function_exists('PHPUnit\\Framework\\assertContainsOnlyInstancesOf')) { - /** - * Asserts that a haystack contains only instances of a given class name. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertContainsOnlyInstancesOf - */ - function assertContainsOnlyInstancesOf(string $className, iterable $haystack, string $message = '') : void + public function currentTokenLine() : int { - \PHPUnit\Framework\Assert::assertContainsOnlyInstancesOf(...func_get_args()); + return $this->tokens[$this->index][Lexer::LINE_OFFSET]; } -} -if (!function_exists('PHPUnit\\Framework\\assertNotContainsOnly')) { - /** - * Asserts that a haystack does not contain only values of a given type. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertNotContainsOnly - */ - function assertNotContainsOnly(string $type, iterable $haystack, ?bool $isNativeType = null, string $message = '') : void + public function currentTokenIndex() : int { - \PHPUnit\Framework\Assert::assertNotContainsOnly(...func_get_args()); + return $this->index; } -} -if (!function_exists('PHPUnit\\Framework\\assertCount')) { - /** - * Asserts the number of elements of an array, Countable or Traversable. - * - * @param Countable|iterable $haystack - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws Exception - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertCount - */ - function assertCount(int $expectedCount, $haystack, string $message = '') : void + public function endIndexOfLastRelevantToken() : int { - \PHPUnit\Framework\Assert::assertCount(...func_get_args()); + $endIndex = $this->currentTokenIndex(); + $endIndex--; + while (in_array($this->tokens[$endIndex][Lexer::TYPE_OFFSET], $this->skippedTokenTypes, \true)) { + if (!isset($this->tokens[$endIndex - 1])) { + break; + } + $endIndex--; + } + return $endIndex; } -} -if (!function_exists('PHPUnit\\Framework\\assertNotCount')) { - /** - * Asserts the number of elements of an array, Countable or Traversable. - * - * @param Countable|iterable $haystack - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws Exception - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertNotCount - */ - function assertNotCount(int $expectedCount, $haystack, string $message = '') : void + public function isCurrentTokenValue(string $tokenValue) : bool { - \PHPUnit\Framework\Assert::assertNotCount(...func_get_args()); + return $this->tokens[$this->index][Lexer::VALUE_OFFSET] === $tokenValue; } -} -if (!function_exists('PHPUnit\\Framework\\assertEquals')) { - /** - * Asserts that two variables are equal. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertEquals - */ - function assertEquals($expected, $actual, string $message = '') : void + public function isCurrentTokenType(int ...$tokenType) : bool { - \PHPUnit\Framework\Assert::assertEquals(...func_get_args()); + return in_array($this->tokens[$this->index][Lexer::TYPE_OFFSET], $tokenType, \true); } -} -if (!function_exists('PHPUnit\\Framework\\assertEqualsCanonicalizing')) { - /** - * Asserts that two variables are equal (canonicalizing). - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertEqualsCanonicalizing - */ - function assertEqualsCanonicalizing($expected, $actual, string $message = '') : void + public function isPrecededByHorizontalWhitespace() : bool { - \PHPUnit\Framework\Assert::assertEqualsCanonicalizing(...func_get_args()); + return ($this->tokens[$this->index - 1][Lexer::TYPE_OFFSET] ?? -1) === Lexer::TOKEN_HORIZONTAL_WS; } -} -if (!function_exists('PHPUnit\\Framework\\assertEqualsIgnoringCase')) { /** - * Asserts that two variables are equal (ignoring case). - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertEqualsIgnoringCase + * @throws ParserException */ - function assertEqualsIgnoringCase($expected, $actual, string $message = '') : void + public function consumeTokenType(int $tokenType) : void { - \PHPUnit\Framework\Assert::assertEqualsIgnoringCase(...func_get_args()); + if ($this->tokens[$this->index][Lexer::TYPE_OFFSET] !== $tokenType) { + $this->throwError($tokenType); + } + if ($tokenType === Lexer::TOKEN_PHPDOC_EOL) { + if ($this->newline === null) { + $this->detectNewline(); + } + } + $this->index++; + $this->skipIrrelevantTokens(); } -} -if (!function_exists('PHPUnit\\Framework\\assertEqualsWithDelta')) { /** - * Asserts that two variables are equal (with delta). - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertEqualsWithDelta + * @throws ParserException */ - function assertEqualsWithDelta($expected, $actual, float $delta, string $message = '') : void + public function consumeTokenValue(int $tokenType, string $tokenValue) : void { - \PHPUnit\Framework\Assert::assertEqualsWithDelta(...func_get_args()); + if ($this->tokens[$this->index][Lexer::TYPE_OFFSET] !== $tokenType || $this->tokens[$this->index][Lexer::VALUE_OFFSET] !== $tokenValue) { + $this->throwError($tokenType, $tokenValue); + } + $this->index++; + $this->skipIrrelevantTokens(); } -} -if (!function_exists('PHPUnit\\Framework\\assertNotEquals')) { - /** - * Asserts that two variables are not equal. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertNotEquals - */ - function assertNotEquals($expected, $actual, string $message = '') : void + /** @phpstan-impure */ + public function tryConsumeTokenValue(string $tokenValue) : bool { - \PHPUnit\Framework\Assert::assertNotEquals(...func_get_args()); + if ($this->tokens[$this->index][Lexer::VALUE_OFFSET] !== $tokenValue) { + return \false; + } + $this->index++; + $this->skipIrrelevantTokens(); + return \true; + } + /** @phpstan-impure */ + public function tryConsumeTokenType(int $tokenType) : bool + { + if ($this->tokens[$this->index][Lexer::TYPE_OFFSET] !== $tokenType) { + return \false; + } + if ($tokenType === Lexer::TOKEN_PHPDOC_EOL) { + if ($this->newline === null) { + $this->detectNewline(); + } + } + $this->index++; + $this->skipIrrelevantTokens(); + return \true; + } + private function detectNewline() : void + { + $value = $this->currentTokenValue(); + if (substr($value, 0, 2) === "\r\n") { + $this->newline = "\r\n"; + } elseif (substr($value, 0, 1) === "\n") { + $this->newline = "\n"; + } + } + public function getSkippedHorizontalWhiteSpaceIfAny() : string + { + if ($this->index > 0 && $this->tokens[$this->index - 1][Lexer::TYPE_OFFSET] === Lexer::TOKEN_HORIZONTAL_WS) { + return $this->tokens[$this->index - 1][Lexer::VALUE_OFFSET]; + } + return ''; + } + /** @phpstan-impure */ + public function joinUntil(int ...$tokenType) : string + { + $s = ''; + while (!in_array($this->tokens[$this->index][Lexer::TYPE_OFFSET], $tokenType, \true)) { + $s .= $this->tokens[$this->index++][Lexer::VALUE_OFFSET]; + } + return $s; + } + public function next() : void + { + $this->index++; + $this->skipIrrelevantTokens(); + } + private function skipIrrelevantTokens() : void + { + if (!isset($this->tokens[$this->index])) { + return; + } + while (in_array($this->tokens[$this->index][Lexer::TYPE_OFFSET], $this->skippedTokenTypes, \true)) { + if (!isset($this->tokens[$this->index + 1])) { + break; + } + $this->index++; + } + } + public function addEndOfLineToSkippedTokens() : void + { + $this->skippedTokenTypes = [Lexer::TOKEN_HORIZONTAL_WS, Lexer::TOKEN_PHPDOC_EOL]; + } + public function removeEndOfLineFromSkippedTokens() : void + { + $this->skippedTokenTypes = [Lexer::TOKEN_HORIZONTAL_WS]; + } + /** @phpstan-impure */ + public function forwardToTheEnd() : void + { + $lastToken = count($this->tokens) - 1; + $this->index = $lastToken; + } + public function pushSavePoint() : void + { + $this->savePoints[] = $this->index; + } + public function dropSavePoint() : void + { + array_pop($this->savePoints); + } + public function rollback() : void + { + $index = array_pop($this->savePoints); + assert($index !== null); + $this->index = $index; } -} -if (!function_exists('PHPUnit\\Framework\\assertNotEqualsCanonicalizing')) { /** - * Asserts that two variables are not equal (canonicalizing). - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertNotEqualsCanonicalizing + * @throws ParserException */ - function assertNotEqualsCanonicalizing($expected, $actual, string $message = '') : void + private function throwError(int $expectedTokenType, ?string $expectedTokenValue = null) : void { - \PHPUnit\Framework\Assert::assertNotEqualsCanonicalizing(...func_get_args()); + throw new ParserException($this->currentTokenValue(), $this->currentTokenType(), $this->currentTokenOffset(), $expectedTokenType, $expectedTokenValue, $this->currentTokenLine()); } -} -if (!function_exists('PHPUnit\\Framework\\assertNotEqualsIgnoringCase')) { /** - * Asserts that two variables are not equal (ignoring case). - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * Check whether the position is directly preceded by a certain token type. * - * @see Assert::assertNotEqualsIgnoringCase + * During this check TOKEN_HORIZONTAL_WS and TOKEN_PHPDOC_EOL are skipped */ - function assertNotEqualsIgnoringCase($expected, $actual, string $message = '') : void + public function hasTokenImmediatelyBefore(int $pos, int $expectedTokenType) : bool { - \PHPUnit\Framework\Assert::assertNotEqualsIgnoringCase(...func_get_args()); + $tokens = $this->tokens; + $pos--; + for (; $pos >= 0; $pos--) { + $token = $tokens[$pos]; + $type = $token[Lexer::TYPE_OFFSET]; + if ($type === $expectedTokenType) { + return \true; + } + if (!in_array($type, [Lexer::TOKEN_HORIZONTAL_WS, Lexer::TOKEN_PHPDOC_EOL], \true)) { + break; + } + } + return \false; } -} -if (!function_exists('PHPUnit\\Framework\\assertNotEqualsWithDelta')) { /** - * Asserts that two variables are not equal (with delta). - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * Check whether the position is directly followed by a certain token type. * - * @see Assert::assertNotEqualsWithDelta + * During this check TOKEN_HORIZONTAL_WS and TOKEN_PHPDOC_EOL are skipped */ - function assertNotEqualsWithDelta($expected, $actual, float $delta, string $message = '') : void + public function hasTokenImmediatelyAfter(int $pos, int $expectedTokenType) : bool { - \PHPUnit\Framework\Assert::assertNotEqualsWithDelta(...func_get_args()); + $tokens = $this->tokens; + $pos++; + for ($c = count($tokens); $pos < $c; $pos++) { + $token = $tokens[$pos]; + $type = $token[Lexer::TYPE_OFFSET]; + if ($type === $expectedTokenType) { + return \true; + } + if (!in_array($type, [Lexer::TOKEN_HORIZONTAL_WS, Lexer::TOKEN_PHPDOC_EOL], \true)) { + break; + } + } + return \false; + } + public function getDetectedNewline() : ?string + { + return $this->newline; } -} -if (!function_exists('PHPUnit\\Framework\\assertObjectEquals')) { /** - * @throws ExpectationFailedException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertObjectEquals + * Whether the given position is immediately surrounded by parenthesis. */ - function assertObjectEquals(object $expected, object $actual, string $method = 'equals', string $message = '') : void + public function hasParentheses(int $startPos, int $endPos) : bool { - \PHPUnit\Framework\Assert::assertObjectEquals(...func_get_args()); + return $this->hasTokenImmediatelyBefore($startPos, Lexer::TOKEN_OPEN_PARENTHESES) && $this->hasTokenImmediatelyAfter($endPos, Lexer::TOKEN_CLOSE_PARENTHESES); } } -if (!function_exists('PHPUnit\\Framework\\assertEmpty')) { - /** - * Asserts that a variable is empty. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @psalm-assert empty $actual - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertEmpty +constExprParser = $constExprParser; + $this->quoteAwareConstExprString = $quoteAwareConstExprString; + $this->useLinesAttributes = $usedAttributes['lines'] ?? \false; + $this->useIndexAttributes = $usedAttributes['indexes'] ?? \false; } -} -if (!function_exists('PHPUnit\\Framework\\assertNotEmpty')) { - /** - * Asserts that a variable is not empty. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @psalm-assert !empty $actual - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertNotEmpty - */ - function assertNotEmpty($actual, string $message = '') : void + /** @phpstan-impure */ + public function parse(TokenIterator $tokens) : Ast\Type\TypeNode { - \PHPUnit\Framework\Assert::assertNotEmpty(...func_get_args()); + $startLine = $tokens->currentTokenLine(); + $startIndex = $tokens->currentTokenIndex(); + if ($tokens->isCurrentTokenType(Lexer::TOKEN_NULLABLE)) { + $type = $this->parseNullable($tokens); + } else { + $type = $this->parseAtomic($tokens); + if ($tokens->isCurrentTokenType(Lexer::TOKEN_UNION)) { + $type = $this->parseUnion($tokens, $type); + } elseif ($tokens->isCurrentTokenType(Lexer::TOKEN_INTERSECTION)) { + $type = $this->parseIntersection($tokens, $type); + } + } + return $this->enrichWithAttributes($tokens, $type, $startLine, $startIndex); } -} -if (!function_exists('PHPUnit\\Framework\\assertGreaterThan')) { /** - * Asserts that a value is greater than another value. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertGreaterThan + * @internal + * @template T of Ast\Node + * @param T $type + * @return T */ - function assertGreaterThan($expected, $actual, string $message = '') : void + public function enrichWithAttributes(TokenIterator $tokens, Ast\Node $type, int $startLine, int $startIndex) : Ast\Node { - \PHPUnit\Framework\Assert::assertGreaterThan(...func_get_args()); + if ($this->useLinesAttributes) { + $type->setAttribute(Ast\Attribute::START_LINE, $startLine); + $type->setAttribute(Ast\Attribute::END_LINE, $tokens->currentTokenLine()); + } + if ($this->useIndexAttributes) { + $type->setAttribute(Ast\Attribute::START_INDEX, $startIndex); + $type->setAttribute(Ast\Attribute::END_INDEX, $tokens->endIndexOfLastRelevantToken()); + } + return $type; } -} -if (!function_exists('PHPUnit\\Framework\\assertGreaterThanOrEqual')) { - /** - * Asserts that a value is greater than or equal to another value. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertGreaterThanOrEqual - */ - function assertGreaterThanOrEqual($expected, $actual, string $message = '') : void + /** @phpstan-impure */ + private function subParse(TokenIterator $tokens) : Ast\Type\TypeNode { - \PHPUnit\Framework\Assert::assertGreaterThanOrEqual(...func_get_args()); + $startLine = $tokens->currentTokenLine(); + $startIndex = $tokens->currentTokenIndex(); + if ($tokens->isCurrentTokenType(Lexer::TOKEN_NULLABLE)) { + $type = $this->parseNullable($tokens); + } elseif ($tokens->isCurrentTokenType(Lexer::TOKEN_VARIABLE)) { + $type = $this->parseConditionalForParameter($tokens, $tokens->currentTokenValue()); + } else { + $type = $this->parseAtomic($tokens); + if ($tokens->isCurrentTokenValue('is')) { + $type = $this->parseConditional($tokens, $type); + } else { + $tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL); + if ($tokens->isCurrentTokenType(Lexer::TOKEN_UNION)) { + $type = $this->subParseUnion($tokens, $type); + } elseif ($tokens->isCurrentTokenType(Lexer::TOKEN_INTERSECTION)) { + $type = $this->subParseIntersection($tokens, $type); + } + } + } + return $this->enrichWithAttributes($tokens, $type, $startLine, $startIndex); + } + /** @phpstan-impure */ + private function parseAtomic(TokenIterator $tokens) : Ast\Type\TypeNode + { + $startLine = $tokens->currentTokenLine(); + $startIndex = $tokens->currentTokenIndex(); + if ($tokens->tryConsumeTokenType(Lexer::TOKEN_OPEN_PARENTHESES)) { + $tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL); + $type = $this->subParse($tokens); + $tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL); + $tokens->consumeTokenType(Lexer::TOKEN_CLOSE_PARENTHESES); + if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) { + $type = $this->tryParseArrayOrOffsetAccess($tokens, $type); + } + return $this->enrichWithAttributes($tokens, $type, $startLine, $startIndex); + } + if ($tokens->tryConsumeTokenType(Lexer::TOKEN_THIS_VARIABLE)) { + $type = $this->enrichWithAttributes($tokens, new Ast\Type\ThisTypeNode(), $startLine, $startIndex); + if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) { + $type = $this->tryParseArrayOrOffsetAccess($tokens, $type); + } + return $this->enrichWithAttributes($tokens, $type, $startLine, $startIndex); + } + $currentTokenValue = $tokens->currentTokenValue(); + $tokens->pushSavePoint(); + // because of ConstFetchNode + if ($tokens->tryConsumeTokenType(Lexer::TOKEN_IDENTIFIER)) { + $type = $this->enrichWithAttributes($tokens, new Ast\Type\IdentifierTypeNode($currentTokenValue), $startLine, $startIndex); + if (!$tokens->isCurrentTokenType(Lexer::TOKEN_DOUBLE_COLON)) { + $tokens->dropSavePoint(); + // because of ConstFetchNode + if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_ANGLE_BRACKET)) { + $tokens->pushSavePoint(); + $isHtml = $this->isHtml($tokens); + $tokens->rollback(); + if ($isHtml) { + return $type; + } + $origType = $type; + $type = $this->tryParseCallable($tokens, $type, \true); + if ($type === $origType) { + $type = $this->parseGeneric($tokens, $type); + if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) { + $type = $this->tryParseArrayOrOffsetAccess($tokens, $type); + } + } + } elseif ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_PARENTHESES)) { + $type = $this->tryParseCallable($tokens, $type, \false); + } elseif ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) { + $type = $this->tryParseArrayOrOffsetAccess($tokens, $type); + } elseif (in_array($type->name, ['array', 'list', 'object'], \true) && $tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_CURLY_BRACKET) && !$tokens->isPrecededByHorizontalWhitespace()) { + if ($type->name === 'object') { + $type = $this->parseObjectShape($tokens); + } else { + $type = $this->parseArrayShape($tokens, $type, $type->name); + } + if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) { + $type = $this->tryParseArrayOrOffsetAccess($tokens, $this->enrichWithAttributes($tokens, $type, $startLine, $startIndex)); + } + } + return $this->enrichWithAttributes($tokens, $type, $startLine, $startIndex); + } else { + $tokens->rollback(); + // because of ConstFetchNode + } + } else { + $tokens->dropSavePoint(); + // because of ConstFetchNode + } + $currentTokenValue = $tokens->currentTokenValue(); + $currentTokenType = $tokens->currentTokenType(); + $currentTokenOffset = $tokens->currentTokenOffset(); + $currentTokenLine = $tokens->currentTokenLine(); + if ($this->constExprParser === null) { + throw new ParserException($currentTokenValue, $currentTokenType, $currentTokenOffset, Lexer::TOKEN_IDENTIFIER, null, $currentTokenLine); + } + try { + $constExpr = $this->constExprParser->parse($tokens, \true); + if ($constExpr instanceof Ast\ConstExpr\ConstExprArrayNode) { + throw new ParserException($currentTokenValue, $currentTokenType, $currentTokenOffset, Lexer::TOKEN_IDENTIFIER, null, $currentTokenLine); + } + return $this->enrichWithAttributes($tokens, new Ast\Type\ConstTypeNode($constExpr), $startLine, $startIndex); + } catch (LogicException $e) { + throw new ParserException($currentTokenValue, $currentTokenType, $currentTokenOffset, Lexer::TOKEN_IDENTIFIER, null, $currentTokenLine); + } + } + /** @phpstan-impure */ + private function parseUnion(TokenIterator $tokens, Ast\Type\TypeNode $type) : Ast\Type\TypeNode + { + $types = [$type]; + while ($tokens->tryConsumeTokenType(Lexer::TOKEN_UNION)) { + $types[] = $this->parseAtomic($tokens); + } + return new Ast\Type\UnionTypeNode($types); + } + /** @phpstan-impure */ + private function subParseUnion(TokenIterator $tokens, Ast\Type\TypeNode $type) : Ast\Type\TypeNode + { + $types = [$type]; + while ($tokens->tryConsumeTokenType(Lexer::TOKEN_UNION)) { + $tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL); + $types[] = $this->parseAtomic($tokens); + $tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL); + } + return new Ast\Type\UnionTypeNode($types); + } + /** @phpstan-impure */ + private function parseIntersection(TokenIterator $tokens, Ast\Type\TypeNode $type) : Ast\Type\TypeNode + { + $types = [$type]; + while ($tokens->tryConsumeTokenType(Lexer::TOKEN_INTERSECTION)) { + $types[] = $this->parseAtomic($tokens); + } + return new Ast\Type\IntersectionTypeNode($types); + } + /** @phpstan-impure */ + private function subParseIntersection(TokenIterator $tokens, Ast\Type\TypeNode $type) : Ast\Type\TypeNode + { + $types = [$type]; + while ($tokens->tryConsumeTokenType(Lexer::TOKEN_INTERSECTION)) { + $tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL); + $types[] = $this->parseAtomic($tokens); + $tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL); + } + return new Ast\Type\IntersectionTypeNode($types); + } + /** @phpstan-impure */ + private function parseConditional(TokenIterator $tokens, Ast\Type\TypeNode $subjectType) : Ast\Type\TypeNode + { + $tokens->consumeTokenType(Lexer::TOKEN_IDENTIFIER); + $negated = \false; + if ($tokens->isCurrentTokenValue('not')) { + $negated = \true; + $tokens->consumeTokenType(Lexer::TOKEN_IDENTIFIER); + } + $targetType = $this->parse($tokens); + $tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL); + $tokens->consumeTokenType(Lexer::TOKEN_NULLABLE); + $tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL); + $ifType = $this->parse($tokens); + $tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL); + $tokens->consumeTokenType(Lexer::TOKEN_COLON); + $tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL); + $elseType = $this->subParse($tokens); + return new Ast\Type\ConditionalTypeNode($subjectType, $targetType, $ifType, $elseType, $negated); + } + /** @phpstan-impure */ + private function parseConditionalForParameter(TokenIterator $tokens, string $parameterName) : Ast\Type\TypeNode + { + $tokens->consumeTokenType(Lexer::TOKEN_VARIABLE); + $tokens->consumeTokenValue(Lexer::TOKEN_IDENTIFIER, 'is'); + $negated = \false; + if ($tokens->isCurrentTokenValue('not')) { + $negated = \true; + $tokens->consumeTokenType(Lexer::TOKEN_IDENTIFIER); + } + $targetType = $this->parse($tokens); + $tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL); + $tokens->consumeTokenType(Lexer::TOKEN_NULLABLE); + $tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL); + $ifType = $this->parse($tokens); + $tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL); + $tokens->consumeTokenType(Lexer::TOKEN_COLON); + $tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL); + $elseType = $this->subParse($tokens); + return new Ast\Type\ConditionalTypeForParameterNode($parameterName, $targetType, $ifType, $elseType, $negated); + } + /** @phpstan-impure */ + private function parseNullable(TokenIterator $tokens) : Ast\Type\TypeNode + { + $tokens->consumeTokenType(Lexer::TOKEN_NULLABLE); + $type = $this->parseAtomic($tokens); + return new Ast\Type\NullableTypeNode($type); + } + /** @phpstan-impure */ + public function isHtml(TokenIterator $tokens) : bool + { + $tokens->consumeTokenType(Lexer::TOKEN_OPEN_ANGLE_BRACKET); + if (!$tokens->isCurrentTokenType(Lexer::TOKEN_IDENTIFIER)) { + return \false; + } + $htmlTagName = $tokens->currentTokenValue(); + $tokens->next(); + if (!$tokens->tryConsumeTokenType(Lexer::TOKEN_CLOSE_ANGLE_BRACKET)) { + return \false; + } + $endTag = ''; + $endTagSearchOffset = -strlen($endTag); + while (!$tokens->isCurrentTokenType(Lexer::TOKEN_END)) { + if ($tokens->tryConsumeTokenType(Lexer::TOKEN_OPEN_ANGLE_BRACKET) && strpos($tokens->currentTokenValue(), '/' . $htmlTagName . '>') !== \false || substr_compare($tokens->currentTokenValue(), $endTag, $endTagSearchOffset) === 0) { + return \true; + } + $tokens->next(); + } + return \false; + } + /** @phpstan-impure */ + public function parseGeneric(TokenIterator $tokens, Ast\Type\IdentifierTypeNode $baseType) : Ast\Type\GenericTypeNode + { + $tokens->consumeTokenType(Lexer::TOKEN_OPEN_ANGLE_BRACKET); + $startLine = $baseType->getAttribute(Ast\Attribute::START_LINE); + $startIndex = $baseType->getAttribute(Ast\Attribute::START_INDEX); + $genericTypes = []; + $variances = []; + $isFirst = \true; + while ($isFirst || $tokens->tryConsumeTokenType(Lexer::TOKEN_COMMA)) { + $tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL); + // trailing comma case + if (!$isFirst && $tokens->isCurrentTokenType(Lexer::TOKEN_CLOSE_ANGLE_BRACKET)) { + break; + } + $isFirst = \false; + [$genericTypes[], $variances[]] = $this->parseGenericTypeArgument($tokens); + $tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL); + } + $type = new Ast\Type\GenericTypeNode($baseType, $genericTypes, $variances); + if ($startLine !== null && $startIndex !== null) { + $type = $this->enrichWithAttributes($tokens, $type, $startLine, $startIndex); + } + $tokens->consumeTokenType(Lexer::TOKEN_CLOSE_ANGLE_BRACKET); + return $type; } -} -if (!function_exists('PHPUnit\\Framework\\assertLessThan')) { /** - * Asserts that a value is smaller than another value. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertLessThan + * @phpstan-impure + * @return array{Ast\Type\TypeNode, Ast\Type\GenericTypeNode::VARIANCE_*} */ - function assertLessThan($expected, $actual, string $message = '') : void + public function parseGenericTypeArgument(TokenIterator $tokens) : array { - \PHPUnit\Framework\Assert::assertLessThan(...func_get_args()); + $startLine = $tokens->currentTokenLine(); + $startIndex = $tokens->currentTokenIndex(); + if ($tokens->tryConsumeTokenType(Lexer::TOKEN_WILDCARD)) { + return [$this->enrichWithAttributes($tokens, new Ast\Type\IdentifierTypeNode('mixed'), $startLine, $startIndex), Ast\Type\GenericTypeNode::VARIANCE_BIVARIANT]; + } + if ($tokens->tryConsumeTokenValue('contravariant')) { + $variance = Ast\Type\GenericTypeNode::VARIANCE_CONTRAVARIANT; + } elseif ($tokens->tryConsumeTokenValue('covariant')) { + $variance = Ast\Type\GenericTypeNode::VARIANCE_COVARIANT; + } else { + $variance = Ast\Type\GenericTypeNode::VARIANCE_INVARIANT; + } + $type = $this->parse($tokens); + return [$type, $variance]; } -} -if (!function_exists('PHPUnit\\Framework\\assertLessThanOrEqual')) { /** - * Asserts that a value is smaller than or equal to another value. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertLessThanOrEqual + * @throws ParserException + * @param ?callable(TokenIterator): string $parseDescription */ - function assertLessThanOrEqual($expected, $actual, string $message = '') : void + public function parseTemplateTagValue(TokenIterator $tokens, ?callable $parseDescription = null) : TemplateTagValueNode { - \PHPUnit\Framework\Assert::assertLessThanOrEqual(...func_get_args()); + $name = $tokens->currentTokenValue(); + $tokens->consumeTokenType(Lexer::TOKEN_IDENTIFIER); + if ($tokens->tryConsumeTokenValue('of') || $tokens->tryConsumeTokenValue('as')) { + $bound = $this->parse($tokens); + } else { + $bound = null; + } + if ($tokens->tryConsumeTokenValue('=')) { + $default = $this->parse($tokens); + } else { + $default = null; + } + if ($parseDescription !== null) { + $description = $parseDescription($tokens); + } else { + $description = ''; + } + return new Ast\PhpDoc\TemplateTagValueNode($name, $bound, $description, $default); } -} -if (!function_exists('PHPUnit\\Framework\\assertFileEquals')) { - /** - * Asserts that the contents of one file is equal to the contents of another - * file. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertFileEquals - */ - function assertFileEquals(string $expected, string $actual, string $message = '') : void + /** @phpstan-impure */ + private function parseCallable(TokenIterator $tokens, Ast\Type\IdentifierTypeNode $identifier, bool $hasTemplate) : Ast\Type\TypeNode { - \PHPUnit\Framework\Assert::assertFileEquals(...func_get_args()); + $templates = $hasTemplate ? $this->parseCallableTemplates($tokens) : []; + $tokens->consumeTokenType(Lexer::TOKEN_OPEN_PARENTHESES); + $tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL); + $parameters = []; + if (!$tokens->isCurrentTokenType(Lexer::TOKEN_CLOSE_PARENTHESES)) { + $parameters[] = $this->parseCallableParameter($tokens); + $tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL); + while ($tokens->tryConsumeTokenType(Lexer::TOKEN_COMMA)) { + $tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL); + if ($tokens->isCurrentTokenType(Lexer::TOKEN_CLOSE_PARENTHESES)) { + break; + } + $parameters[] = $this->parseCallableParameter($tokens); + $tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL); + } + } + $tokens->consumeTokenType(Lexer::TOKEN_CLOSE_PARENTHESES); + $tokens->consumeTokenType(Lexer::TOKEN_COLON); + $startLine = $tokens->currentTokenLine(); + $startIndex = $tokens->currentTokenIndex(); + $returnType = $this->enrichWithAttributes($tokens, $this->parseCallableReturnType($tokens), $startLine, $startIndex); + return new Ast\Type\CallableTypeNode($identifier, $parameters, $returnType, $templates); } -} -if (!function_exists('PHPUnit\\Framework\\assertFileEqualsCanonicalizing')) { /** - * Asserts that the contents of one file is equal to the contents of another - * file (canonicalizing). + * @return Ast\PhpDoc\TemplateTagValueNode[] * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertFileEqualsCanonicalizing + * @phpstan-impure */ - function assertFileEqualsCanonicalizing(string $expected, string $actual, string $message = '') : void + private function parseCallableTemplates(TokenIterator $tokens) : array { - \PHPUnit\Framework\Assert::assertFileEqualsCanonicalizing(...func_get_args()); + $tokens->consumeTokenType(Lexer::TOKEN_OPEN_ANGLE_BRACKET); + $templates = []; + $isFirst = \true; + while ($isFirst || $tokens->tryConsumeTokenType(Lexer::TOKEN_COMMA)) { + $tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL); + // trailing comma case + if (!$isFirst && $tokens->isCurrentTokenType(Lexer::TOKEN_CLOSE_ANGLE_BRACKET)) { + break; + } + $isFirst = \false; + $templates[] = $this->parseCallableTemplateArgument($tokens); + $tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL); + } + $tokens->consumeTokenType(Lexer::TOKEN_CLOSE_ANGLE_BRACKET); + return $templates; } -} -if (!function_exists('PHPUnit\\Framework\\assertFileEqualsIgnoringCase')) { - /** - * Asserts that the contents of one file is equal to the contents of another - * file (ignoring case). - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertFileEqualsIgnoringCase - */ - function assertFileEqualsIgnoringCase(string $expected, string $actual, string $message = '') : void + private function parseCallableTemplateArgument(TokenIterator $tokens) : Ast\PhpDoc\TemplateTagValueNode { - \PHPUnit\Framework\Assert::assertFileEqualsIgnoringCase(...func_get_args()); + $startLine = $tokens->currentTokenLine(); + $startIndex = $tokens->currentTokenIndex(); + return $this->enrichWithAttributes($tokens, $this->parseTemplateTagValue($tokens), $startLine, $startIndex); } -} -if (!function_exists('PHPUnit\\Framework\\assertFileNotEquals')) { - /** - * Asserts that the contents of one file is not equal to the contents of - * another file. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertFileNotEquals - */ - function assertFileNotEquals(string $expected, string $actual, string $message = '') : void + /** @phpstan-impure */ + private function parseCallableParameter(TokenIterator $tokens) : Ast\Type\CallableTypeParameterNode { - \PHPUnit\Framework\Assert::assertFileNotEquals(...func_get_args()); + $startLine = $tokens->currentTokenLine(); + $startIndex = $tokens->currentTokenIndex(); + $type = $this->parse($tokens); + $isReference = $tokens->tryConsumeTokenType(Lexer::TOKEN_REFERENCE); + $isVariadic = $tokens->tryConsumeTokenType(Lexer::TOKEN_VARIADIC); + if ($tokens->isCurrentTokenType(Lexer::TOKEN_VARIABLE)) { + $parameterName = $tokens->currentTokenValue(); + $tokens->consumeTokenType(Lexer::TOKEN_VARIABLE); + } else { + $parameterName = ''; + } + $isOptional = $tokens->tryConsumeTokenType(Lexer::TOKEN_EQUAL); + return $this->enrichWithAttributes($tokens, new Ast\Type\CallableTypeParameterNode($type, $isReference, $isVariadic, $parameterName, $isOptional), $startLine, $startIndex); } -} -if (!function_exists('PHPUnit\\Framework\\assertFileNotEqualsCanonicalizing')) { - /** - * Asserts that the contents of one file is not equal to the contents of another - * file (canonicalizing). - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertFileNotEqualsCanonicalizing - */ - function assertFileNotEqualsCanonicalizing(string $expected, string $actual, string $message = '') : void + /** @phpstan-impure */ + private function parseCallableReturnType(TokenIterator $tokens) : Ast\Type\TypeNode { - \PHPUnit\Framework\Assert::assertFileNotEqualsCanonicalizing(...func_get_args()); + $startLine = $tokens->currentTokenLine(); + $startIndex = $tokens->currentTokenIndex(); + if ($tokens->isCurrentTokenType(Lexer::TOKEN_NULLABLE)) { + return $this->parseNullable($tokens); + } elseif ($tokens->tryConsumeTokenType(Lexer::TOKEN_OPEN_PARENTHESES)) { + $type = $this->subParse($tokens); + $tokens->consumeTokenType(Lexer::TOKEN_CLOSE_PARENTHESES); + if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) { + $type = $this->tryParseArrayOrOffsetAccess($tokens, $type); + } + return $type; + } elseif ($tokens->tryConsumeTokenType(Lexer::TOKEN_THIS_VARIABLE)) { + $type = new Ast\Type\ThisTypeNode(); + if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) { + $type = $this->tryParseArrayOrOffsetAccess($tokens, $this->enrichWithAttributes($tokens, $type, $startLine, $startIndex)); + } + return $type; + } else { + $currentTokenValue = $tokens->currentTokenValue(); + $tokens->pushSavePoint(); + // because of ConstFetchNode + if ($tokens->tryConsumeTokenType(Lexer::TOKEN_IDENTIFIER)) { + $type = new Ast\Type\IdentifierTypeNode($currentTokenValue); + if (!$tokens->isCurrentTokenType(Lexer::TOKEN_DOUBLE_COLON)) { + if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_ANGLE_BRACKET)) { + $type = $this->parseGeneric($tokens, $this->enrichWithAttributes($tokens, $type, $startLine, $startIndex)); + if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) { + $type = $this->tryParseArrayOrOffsetAccess($tokens, $this->enrichWithAttributes($tokens, $type, $startLine, $startIndex)); + } + } elseif ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) { + $type = $this->tryParseArrayOrOffsetAccess($tokens, $this->enrichWithAttributes($tokens, $type, $startLine, $startIndex)); + } elseif (in_array($type->name, ['array', 'list', 'object'], \true) && $tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_CURLY_BRACKET) && !$tokens->isPrecededByHorizontalWhitespace()) { + if ($type->name === 'object') { + $type = $this->parseObjectShape($tokens); + } else { + $type = $this->parseArrayShape($tokens, $this->enrichWithAttributes($tokens, $type, $startLine, $startIndex), $type->name); + } + if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) { + $type = $this->tryParseArrayOrOffsetAccess($tokens, $this->enrichWithAttributes($tokens, $type, $startLine, $startIndex)); + } + } + return $type; + } else { + $tokens->rollback(); + // because of ConstFetchNode + } + } else { + $tokens->dropSavePoint(); + // because of ConstFetchNode + } + } + $currentTokenValue = $tokens->currentTokenValue(); + $currentTokenType = $tokens->currentTokenType(); + $currentTokenOffset = $tokens->currentTokenOffset(); + $currentTokenLine = $tokens->currentTokenLine(); + if ($this->constExprParser === null) { + throw new ParserException($currentTokenValue, $currentTokenType, $currentTokenOffset, Lexer::TOKEN_IDENTIFIER, null, $currentTokenLine); + } + try { + $constExpr = $this->constExprParser->parse($tokens, \true); + if ($constExpr instanceof Ast\ConstExpr\ConstExprArrayNode) { + throw new ParserException($currentTokenValue, $currentTokenType, $currentTokenOffset, Lexer::TOKEN_IDENTIFIER, null, $currentTokenLine); + } + $type = new Ast\Type\ConstTypeNode($constExpr); + if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) { + $type = $this->tryParseArrayOrOffsetAccess($tokens, $this->enrichWithAttributes($tokens, $type, $startLine, $startIndex)); + } + return $type; + } catch (LogicException $e) { + throw new ParserException($currentTokenValue, $currentTokenType, $currentTokenOffset, Lexer::TOKEN_IDENTIFIER, null, $currentTokenLine); + } } -} -if (!function_exists('PHPUnit\\Framework\\assertFileNotEqualsIgnoringCase')) { - /** - * Asserts that the contents of one file is not equal to the contents of another - * file (ignoring case). - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertFileNotEqualsIgnoringCase - */ - function assertFileNotEqualsIgnoringCase(string $expected, string $actual, string $message = '') : void + /** @phpstan-impure */ + private function tryParseCallable(TokenIterator $tokens, Ast\Type\IdentifierTypeNode $identifier, bool $hasTemplate) : Ast\Type\TypeNode { - \PHPUnit\Framework\Assert::assertFileNotEqualsIgnoringCase(...func_get_args()); + try { + $tokens->pushSavePoint(); + $type = $this->parseCallable($tokens, $identifier, $hasTemplate); + $tokens->dropSavePoint(); + } catch (ParserException $e) { + $tokens->rollback(); + $type = $identifier; + } + return $type; } -} -if (!function_exists('PHPUnit\\Framework\\assertStringEqualsFile')) { - /** - * Asserts that the contents of a string is equal - * to the contents of a file. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertStringEqualsFile - */ - function assertStringEqualsFile(string $expectedFile, string $actualString, string $message = '') : void + /** @phpstan-impure */ + private function tryParseArrayOrOffsetAccess(TokenIterator $tokens, Ast\Type\TypeNode $type) : Ast\Type\TypeNode { - \PHPUnit\Framework\Assert::assertStringEqualsFile(...func_get_args()); + $startLine = $type->getAttribute(Ast\Attribute::START_LINE); + $startIndex = $type->getAttribute(Ast\Attribute::START_INDEX); + try { + while ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) { + $tokens->pushSavePoint(); + $canBeOffsetAccessType = !$tokens->isPrecededByHorizontalWhitespace(); + $tokens->consumeTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET); + if ($canBeOffsetAccessType && !$tokens->isCurrentTokenType(Lexer::TOKEN_CLOSE_SQUARE_BRACKET)) { + $offset = $this->parse($tokens); + $tokens->consumeTokenType(Lexer::TOKEN_CLOSE_SQUARE_BRACKET); + $tokens->dropSavePoint(); + $type = new Ast\Type\OffsetAccessTypeNode($type, $offset); + if ($startLine !== null && $startIndex !== null) { + $type = $this->enrichWithAttributes($tokens, $type, $startLine, $startIndex); + } + } else { + $tokens->consumeTokenType(Lexer::TOKEN_CLOSE_SQUARE_BRACKET); + $tokens->dropSavePoint(); + $type = new Ast\Type\ArrayTypeNode($type); + if ($startLine !== null && $startIndex !== null) { + $type = $this->enrichWithAttributes($tokens, $type, $startLine, $startIndex); + } + } + } + } catch (ParserException $e) { + $tokens->rollback(); + } + return $type; } -} -if (!function_exists('PHPUnit\\Framework\\assertStringEqualsFileCanonicalizing')) { /** - * Asserts that the contents of a string is equal - * to the contents of a file (canonicalizing). - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertStringEqualsFileCanonicalizing + * @phpstan-impure + * @param Ast\Type\ArrayShapeNode::KIND_* $kind */ - function assertStringEqualsFileCanonicalizing(string $expectedFile, string $actualString, string $message = '') : void + private function parseArrayShape(TokenIterator $tokens, Ast\Type\TypeNode $type, string $kind) : Ast\Type\ArrayShapeNode { - \PHPUnit\Framework\Assert::assertStringEqualsFileCanonicalizing(...func_get_args()); + $tokens->consumeTokenType(Lexer::TOKEN_OPEN_CURLY_BRACKET); + $items = []; + $sealed = \true; + do { + $tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL); + if ($tokens->tryConsumeTokenType(Lexer::TOKEN_CLOSE_CURLY_BRACKET)) { + return new Ast\Type\ArrayShapeNode($items, \true, $kind); + } + if ($tokens->tryConsumeTokenType(Lexer::TOKEN_VARIADIC)) { + $sealed = \false; + $tokens->tryConsumeTokenType(Lexer::TOKEN_COMMA); + break; + } + $items[] = $this->parseArrayShapeItem($tokens); + $tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL); + } while ($tokens->tryConsumeTokenType(Lexer::TOKEN_COMMA)); + $tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL); + $tokens->consumeTokenType(Lexer::TOKEN_CLOSE_CURLY_BRACKET); + return new Ast\Type\ArrayShapeNode($items, $sealed, $kind); } -} -if (!function_exists('PHPUnit\\Framework\\assertStringEqualsFileIgnoringCase')) { - /** - * Asserts that the contents of a string is equal - * to the contents of a file (ignoring case). - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertStringEqualsFileIgnoringCase - */ - function assertStringEqualsFileIgnoringCase(string $expectedFile, string $actualString, string $message = '') : void + /** @phpstan-impure */ + private function parseArrayShapeItem(TokenIterator $tokens) : Ast\Type\ArrayShapeItemNode { - \PHPUnit\Framework\Assert::assertStringEqualsFileIgnoringCase(...func_get_args()); + $startLine = $tokens->currentTokenLine(); + $startIndex = $tokens->currentTokenIndex(); + try { + $tokens->pushSavePoint(); + $key = $this->parseArrayShapeKey($tokens); + $optional = $tokens->tryConsumeTokenType(Lexer::TOKEN_NULLABLE); + $tokens->consumeTokenType(Lexer::TOKEN_COLON); + $value = $this->parse($tokens); + $tokens->dropSavePoint(); + return $this->enrichWithAttributes($tokens, new Ast\Type\ArrayShapeItemNode($key, $optional, $value), $startLine, $startIndex); + } catch (ParserException $e) { + $tokens->rollback(); + $value = $this->parse($tokens); + return $this->enrichWithAttributes($tokens, new Ast\Type\ArrayShapeItemNode(null, \false, $value), $startLine, $startIndex); + } } -} -if (!function_exists('PHPUnit\\Framework\\assertStringNotEqualsFile')) { /** - * Asserts that the contents of a string is not equal - * to the contents of a file. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertStringNotEqualsFile + * @phpstan-impure + * @return Ast\ConstExpr\ConstExprIntegerNode|Ast\ConstExpr\ConstExprStringNode|Ast\Type\IdentifierTypeNode */ - function assertStringNotEqualsFile(string $expectedFile, string $actualString, string $message = '') : void + private function parseArrayShapeKey(TokenIterator $tokens) { - \PHPUnit\Framework\Assert::assertStringNotEqualsFile(...func_get_args()); + $startIndex = $tokens->currentTokenIndex(); + $startLine = $tokens->currentTokenLine(); + if ($tokens->isCurrentTokenType(Lexer::TOKEN_INTEGER)) { + $key = new Ast\ConstExpr\ConstExprIntegerNode(str_replace('_', '', $tokens->currentTokenValue())); + $tokens->next(); + } elseif ($tokens->isCurrentTokenType(Lexer::TOKEN_SINGLE_QUOTED_STRING)) { + if ($this->quoteAwareConstExprString) { + $key = new Ast\ConstExpr\QuoteAwareConstExprStringNode(StringUnescaper::unescapeString($tokens->currentTokenValue()), Ast\ConstExpr\QuoteAwareConstExprStringNode::SINGLE_QUOTED); + } else { + $key = new Ast\ConstExpr\ConstExprStringNode(trim($tokens->currentTokenValue(), "'")); + } + $tokens->next(); + } elseif ($tokens->isCurrentTokenType(Lexer::TOKEN_DOUBLE_QUOTED_STRING)) { + if ($this->quoteAwareConstExprString) { + $key = new Ast\ConstExpr\QuoteAwareConstExprStringNode(StringUnescaper::unescapeString($tokens->currentTokenValue()), Ast\ConstExpr\QuoteAwareConstExprStringNode::DOUBLE_QUOTED); + } else { + $key = new Ast\ConstExpr\ConstExprStringNode(trim($tokens->currentTokenValue(), '"')); + } + $tokens->next(); + } else { + $key = new Ast\Type\IdentifierTypeNode($tokens->currentTokenValue()); + $tokens->consumeTokenType(Lexer::TOKEN_IDENTIFIER); + } + return $this->enrichWithAttributes($tokens, $key, $startLine, $startIndex); } -} -if (!function_exists('PHPUnit\\Framework\\assertStringNotEqualsFileCanonicalizing')) { /** - * Asserts that the contents of a string is not equal - * to the contents of a file (canonicalizing). - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertStringNotEqualsFileCanonicalizing + * @phpstan-impure */ - function assertStringNotEqualsFileCanonicalizing(string $expectedFile, string $actualString, string $message = '') : void + private function parseObjectShape(TokenIterator $tokens) : Ast\Type\ObjectShapeNode { - \PHPUnit\Framework\Assert::assertStringNotEqualsFileCanonicalizing(...func_get_args()); + $tokens->consumeTokenType(Lexer::TOKEN_OPEN_CURLY_BRACKET); + $items = []; + do { + $tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL); + if ($tokens->tryConsumeTokenType(Lexer::TOKEN_CLOSE_CURLY_BRACKET)) { + return new Ast\Type\ObjectShapeNode($items); + } + $items[] = $this->parseObjectShapeItem($tokens); + $tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL); + } while ($tokens->tryConsumeTokenType(Lexer::TOKEN_COMMA)); + $tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL); + $tokens->consumeTokenType(Lexer::TOKEN_CLOSE_CURLY_BRACKET); + return new Ast\Type\ObjectShapeNode($items); + } + /** @phpstan-impure */ + private function parseObjectShapeItem(TokenIterator $tokens) : Ast\Type\ObjectShapeItemNode + { + $startLine = $tokens->currentTokenLine(); + $startIndex = $tokens->currentTokenIndex(); + $key = $this->parseObjectShapeKey($tokens); + $optional = $tokens->tryConsumeTokenType(Lexer::TOKEN_NULLABLE); + $tokens->consumeTokenType(Lexer::TOKEN_COLON); + $value = $this->parse($tokens); + return $this->enrichWithAttributes($tokens, new Ast\Type\ObjectShapeItemNode($key, $optional, $value), $startLine, $startIndex); + } + /** + * @phpstan-impure + * @return Ast\ConstExpr\ConstExprStringNode|Ast\Type\IdentifierTypeNode + */ + private function parseObjectShapeKey(TokenIterator $tokens) + { + $startLine = $tokens->currentTokenLine(); + $startIndex = $tokens->currentTokenIndex(); + if ($tokens->isCurrentTokenType(Lexer::TOKEN_SINGLE_QUOTED_STRING)) { + if ($this->quoteAwareConstExprString) { + $key = new Ast\ConstExpr\QuoteAwareConstExprStringNode(StringUnescaper::unescapeString($tokens->currentTokenValue()), Ast\ConstExpr\QuoteAwareConstExprStringNode::SINGLE_QUOTED); + } else { + $key = new Ast\ConstExpr\ConstExprStringNode(trim($tokens->currentTokenValue(), "'")); + } + $tokens->next(); + } elseif ($tokens->isCurrentTokenType(Lexer::TOKEN_DOUBLE_QUOTED_STRING)) { + if ($this->quoteAwareConstExprString) { + $key = new Ast\ConstExpr\QuoteAwareConstExprStringNode(StringUnescaper::unescapeString($tokens->currentTokenValue()), Ast\ConstExpr\QuoteAwareConstExprStringNode::DOUBLE_QUOTED); + } else { + $key = new Ast\ConstExpr\ConstExprStringNode(trim($tokens->currentTokenValue(), '"')); + } + $tokens->next(); + } else { + $key = new Ast\Type\IdentifierTypeNode($tokens->currentTokenValue()); + $tokens->consumeTokenType(Lexer::TOKEN_IDENTIFIER); + } + return $this->enrichWithAttributes($tokens, $key, $startLine, $startIndex); } } -if (!function_exists('PHPUnit\\Framework\\assertStringNotEqualsFileIgnoringCase')) { +type = $type; + $this->old = $old; + $this->new = $new; } } -if (!function_exists('PHPUnit\\Framework\\assertIsReadable')) { +isEqual = $isEqual; } -} -if (!function_exists('PHPUnit\\Framework\\assertIsNotReadable')) { /** - * Asserts that a file/dir exists and is not readable. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * Calculate diff (edit script) from $old to $new. * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * @param T[] $old Original array + * @param T[] $new New array * - * @see Assert::assertIsNotReadable + * @return DiffElem[] Diff (edit script) */ - function assertIsNotReadable(string $filename, string $message = '') : void + public function diff(array $old, array $new) : array { - \PHPUnit\Framework\Assert::assertIsNotReadable(...func_get_args()); + [$trace, $x, $y] = $this->calculateTrace($old, $new); + return $this->extractDiff($trace, $x, $y, $old, $new); } -} -if (!function_exists('PHPUnit\\Framework\\assertNotIsReadable')) { /** - * Asserts that a file/dir exists and is not readable. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @codeCoverageIgnore + * Calculate diff, including "replace" operations. * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4062 + * If a sequence of remove operations is followed by the same number of add operations, these + * will be coalesced into replace operations. * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * @param T[] $old Original array + * @param T[] $new New array * - * @see Assert::assertNotIsReadable + * @return DiffElem[] Diff (edit script), including replace operations */ - function assertNotIsReadable(string $filename, string $message = '') : void + public function diffWithReplacements(array $old, array $new) : array { - \PHPUnit\Framework\Assert::assertNotIsReadable(...func_get_args()); + return $this->coalesceReplacements($this->diff($old, $new)); } -} -if (!function_exists('PHPUnit\\Framework\\assertIsWritable')) { /** - * Asserts that a file/dir exists and is writable. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertIsWritable + * @param T[] $old + * @param T[] $new + * @return array{array>, int, int} */ - function assertIsWritable(string $filename, string $message = '') : void + private function calculateTrace(array $old, array $new) : array { - \PHPUnit\Framework\Assert::assertIsWritable(...func_get_args()); + $n = count($old); + $m = count($new); + $max = $n + $m; + $v = [1 => 0]; + $trace = []; + for ($d = 0; $d <= $max; $d++) { + $trace[] = $v; + for ($k = -$d; $k <= $d; $k += 2) { + if ($k === -$d || $k !== $d && $v[$k - 1] < $v[$k + 1]) { + $x = $v[$k + 1]; + } else { + $x = $v[$k - 1] + 1; + } + $y = $x - $k; + while ($x < $n && $y < $m && ($this->isEqual)($old[$x], $new[$y])) { + $x++; + $y++; + } + $v[$k] = $x; + if ($x >= $n && $y >= $m) { + return [$trace, $x, $y]; + } + } + } + throw new Exception('Should not happen'); } -} -if (!function_exists('PHPUnit\\Framework\\assertIsNotWritable')) { /** - * Asserts that a file/dir exists and is not writable. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertIsNotWritable + * @param array> $trace + * @param T[] $old + * @param T[] $new + * @return DiffElem[] */ - function assertIsNotWritable(string $filename, string $message = '') : void + private function extractDiff(array $trace, int $x, int $y, array $old, array $new) : array { - \PHPUnit\Framework\Assert::assertIsNotWritable(...func_get_args()); - } -} -if (!function_exists('PHPUnit\\Framework\\assertNotIsWritable')) { - /** - * Asserts that a file/dir exists and is not writable. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @codeCoverageIgnore - * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4065 - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertNotIsWritable - */ - function assertNotIsWritable(string $filename, string $message = '') : void - { - \PHPUnit\Framework\Assert::assertNotIsWritable(...func_get_args()); - } -} -if (!function_exists('PHPUnit\\Framework\\assertDirectoryExists')) { - /** - * Asserts that a directory exists. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertDirectoryExists - */ - function assertDirectoryExists(string $directory, string $message = '') : void - { - \PHPUnit\Framework\Assert::assertDirectoryExists(...func_get_args()); - } -} -if (!function_exists('PHPUnit\\Framework\\assertDirectoryDoesNotExist')) { - /** - * Asserts that a directory does not exist. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertDirectoryDoesNotExist - */ - function assertDirectoryDoesNotExist(string $directory, string $message = '') : void - { - \PHPUnit\Framework\Assert::assertDirectoryDoesNotExist(...func_get_args()); - } -} -if (!function_exists('PHPUnit\\Framework\\assertDirectoryNotExists')) { - /** - * Asserts that a directory does not exist. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @codeCoverageIgnore - * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4068 - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertDirectoryNotExists - */ - function assertDirectoryNotExists(string $directory, string $message = '') : void - { - \PHPUnit\Framework\Assert::assertDirectoryNotExists(...func_get_args()); - } -} -if (!function_exists('PHPUnit\\Framework\\assertDirectoryIsReadable')) { - /** - * Asserts that a directory exists and is readable. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertDirectoryIsReadable - */ - function assertDirectoryIsReadable(string $directory, string $message = '') : void - { - \PHPUnit\Framework\Assert::assertDirectoryIsReadable(...func_get_args()); - } -} -if (!function_exists('PHPUnit\\Framework\\assertDirectoryIsNotReadable')) { - /** - * Asserts that a directory exists and is not readable. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertDirectoryIsNotReadable - */ - function assertDirectoryIsNotReadable(string $directory, string $message = '') : void - { - \PHPUnit\Framework\Assert::assertDirectoryIsNotReadable(...func_get_args()); - } -} -if (!function_exists('PHPUnit\\Framework\\assertDirectoryNotIsReadable')) { - /** - * Asserts that a directory exists and is not readable. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @codeCoverageIgnore - * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4071 - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertDirectoryNotIsReadable - */ - function assertDirectoryNotIsReadable(string $directory, string $message = '') : void - { - \PHPUnit\Framework\Assert::assertDirectoryNotIsReadable(...func_get_args()); - } -} -if (!function_exists('PHPUnit\\Framework\\assertDirectoryIsWritable')) { - /** - * Asserts that a directory exists and is writable. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertDirectoryIsWritable - */ - function assertDirectoryIsWritable(string $directory, string $message = '') : void - { - \PHPUnit\Framework\Assert::assertDirectoryIsWritable(...func_get_args()); + $result = []; + for ($d = count($trace) - 1; $d >= 0; $d--) { + $v = $trace[$d]; + $k = $x - $y; + if ($k === -$d || $k !== $d && $v[$k - 1] < $v[$k + 1]) { + $prevK = $k + 1; + } else { + $prevK = $k - 1; + } + $prevX = $v[$prevK]; + $prevY = $prevX - $prevK; + while ($x > $prevX && $y > $prevY) { + $result[] = new DiffElem(DiffElem::TYPE_KEEP, $old[$x - 1], $new[$y - 1]); + $x--; + $y--; + } + if ($d === 0) { + break; + } + while ($x > $prevX) { + $result[] = new DiffElem(DiffElem::TYPE_REMOVE, $old[$x - 1], null); + $x--; + } + while ($y > $prevY) { + $result[] = new DiffElem(DiffElem::TYPE_ADD, null, $new[$y - 1]); + $y--; + } + } + return array_reverse($result); } -} -if (!function_exists('PHPUnit\\Framework\\assertDirectoryIsNotWritable')) { /** - * Asserts that a directory exists and is not writable. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * Coalesce equal-length sequences of remove+add into a replace operation. * - * @see Assert::assertDirectoryIsNotWritable + * @param DiffElem[] $diff + * @return DiffElem[] */ - function assertDirectoryIsNotWritable(string $directory, string $message = '') : void + private function coalesceReplacements(array $diff) : array { - \PHPUnit\Framework\Assert::assertDirectoryIsNotWritable(...func_get_args()); + $newDiff = []; + $c = count($diff); + for ($i = 0; $i < $c; $i++) { + $diffType = $diff[$i]->type; + if ($diffType !== DiffElem::TYPE_REMOVE) { + $newDiff[] = $diff[$i]; + continue; + } + $j = $i; + while ($j < $c && $diff[$j]->type === DiffElem::TYPE_REMOVE) { + $j++; + } + $k = $j; + while ($k < $c && $diff[$k]->type === DiffElem::TYPE_ADD) { + $k++; + } + if ($j - $i === $k - $j) { + $len = $j - $i; + for ($n = 0; $n < $len; $n++) { + $newDiff[] = new DiffElem(DiffElem::TYPE_REPLACE, $diff[$i + $n]->old, $diff[$j + $n]->new); + } + } else { + for (; $i < $k; $i++) { + $newDiff[] = $diff[$i]; + } + } + $i = $k - 1; + } + return $newDiff; } } -if (!function_exists('PHPUnit\\Framework\\assertDirectoryNotIsWritable')) { + */ + private $differ; /** - * Asserts that a directory exists and is not writable. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * Map From "{$class}->{$subNode}" to string that should be inserted + * between elements of this list subnode * - * @codeCoverageIgnore - * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4074 - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertDirectoryNotIsWritable + * @var array */ - function assertDirectoryNotIsWritable(string $directory, string $message = '') : void - { - \PHPUnit\Framework\Assert::assertDirectoryNotIsWritable(...func_get_args()); - } -} -if (!function_exists('PHPUnit\\Framework\\assertFileExists')) { + private $listInsertionMap = [PhpDocNode::class . '->children' => "\n * ", UnionTypeNode::class . '->types' => '|', IntersectionTypeNode::class . '->types' => '&', ArrayShapeNode::class . '->items' => ', ', ObjectShapeNode::class . '->items' => ', ', CallableTypeNode::class . '->parameters' => ', ', CallableTypeNode::class . '->templateTypes' => ', ', GenericTypeNode::class . '->genericTypes' => ', ', ConstExprArrayNode::class . '->items' => ', ', MethodTagValueNode::class . '->parameters' => ', ', DoctrineArray::class . '->items' => ', ', DoctrineAnnotation::class . '->arguments' => ', ']; /** - * Asserts that a file exists. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * [$find, $extraLeft, $extraRight] * - * @see Assert::assertFileExists + * @var array */ - function assertFileExists(string $filename, string $message = '') : void + private $emptyListInsertionMap = [CallableTypeNode::class . '->parameters' => ['(', '', ''], ArrayShapeNode::class . '->items' => ['{', '', ''], ObjectShapeNode::class . '->items' => ['{', '', ''], DoctrineArray::class . '->items' => ['{', '', ''], DoctrineAnnotation::class . '->arguments' => ['(', '', '']]; + /** @var array>> */ + private $parenthesesMap = [CallableTypeNode::class . '->returnType' => [CallableTypeNode::class, UnionTypeNode::class, IntersectionTypeNode::class], ArrayTypeNode::class . '->type' => [CallableTypeNode::class, UnionTypeNode::class, IntersectionTypeNode::class, ConstTypeNode::class, NullableTypeNode::class], OffsetAccessTypeNode::class . '->type' => [CallableTypeNode::class, UnionTypeNode::class, IntersectionTypeNode::class, ConstTypeNode::class, NullableTypeNode::class]]; + /** @var array>> */ + private $parenthesesListMap = [IntersectionTypeNode::class . '->types' => [IntersectionTypeNode::class, UnionTypeNode::class, NullableTypeNode::class], UnionTypeNode::class . '->types' => [IntersectionTypeNode::class, UnionTypeNode::class, NullableTypeNode::class]]; + public function printFormatPreserving(PhpDocNode $node, PhpDocNode $originalNode, TokenIterator $originalTokens) : string { - \PHPUnit\Framework\Assert::assertFileExists(...func_get_args()); + $this->differ = new Differ(static function ($a, $b) { + if ($a instanceof Node && $b instanceof Node) { + return $a === $b->getAttribute(Attribute::ORIGINAL_NODE); + } + return \false; + }); + $tokenIndex = 0; + $result = $this->printArrayFormatPreserving($node->children, $originalNode->children, $originalTokens, $tokenIndex, PhpDocNode::class, 'children'); + if ($result !== null) { + return $result . $originalTokens->getContentBetween($tokenIndex, $originalTokens->getTokenCount()); + } + return $this->print($node); + } + public function print(Node $node) : string + { + if ($node instanceof PhpDocNode) { + return "/**\n *" . implode("\n *", array_map(function (PhpDocChildNode $child) : string { + $s = $this->print($child); + return $s === '' ? '' : ' ' . $s; + }, $node->children)) . "\n */"; + } + if ($node instanceof PhpDocTextNode) { + return $node->text; + } + if ($node instanceof PhpDocTagNode) { + if ($node->value instanceof DoctrineTagValueNode) { + return $this->print($node->value); + } + return trim(sprintf('%s %s', $node->name, $this->print($node->value))); + } + if ($node instanceof PhpDocTagValueNode) { + return $this->printTagValue($node); + } + if ($node instanceof TypeNode) { + return $this->printType($node); + } + if ($node instanceof ConstExprNode) { + return $this->printConstExpr($node); + } + if ($node instanceof MethodTagValueParameterNode) { + $type = $node->type !== null ? $this->print($node->type) . ' ' : ''; + $isReference = $node->isReference ? '&' : ''; + $isVariadic = $node->isVariadic ? '...' : ''; + $default = $node->defaultValue !== null ? ' = ' . $this->print($node->defaultValue) : ''; + return "{$type}{$isReference}{$isVariadic}{$node->parameterName}{$default}"; + } + if ($node instanceof CallableTypeParameterNode) { + $type = $this->print($node->type) . ' '; + $isReference = $node->isReference ? '&' : ''; + $isVariadic = $node->isVariadic ? '...' : ''; + $isOptional = $node->isOptional ? '=' : ''; + return trim("{$type}{$isReference}{$isVariadic}{$node->parameterName}") . $isOptional; + } + if ($node instanceof DoctrineAnnotation) { + return (string) $node; + } + if ($node instanceof DoctrineArgument) { + return (string) $node; + } + if ($node instanceof DoctrineArray) { + return (string) $node; + } + if ($node instanceof DoctrineArrayItem) { + return (string) $node; + } + throw new LogicException(sprintf('Unknown node type %s', get_class($node))); + } + private function printTagValue(PhpDocTagValueNode $node) : string + { + // only nodes that contain another node are handled here + // the rest falls back on (string) $node + if ($node instanceof AssertTagMethodValueNode) { + $isNegated = $node->isNegated ? '!' : ''; + $isEquality = $node->isEquality ? '=' : ''; + $type = $this->printType($node->type); + return trim("{$isNegated}{$isEquality}{$type} {$node->parameter}->{$node->method}() {$node->description}"); + } + if ($node instanceof AssertTagPropertyValueNode) { + $isNegated = $node->isNegated ? '!' : ''; + $isEquality = $node->isEquality ? '=' : ''; + $type = $this->printType($node->type); + return trim("{$isNegated}{$isEquality}{$type} {$node->parameter}->{$node->property} {$node->description}"); + } + if ($node instanceof AssertTagValueNode) { + $isNegated = $node->isNegated ? '!' : ''; + $isEquality = $node->isEquality ? '=' : ''; + $type = $this->printType($node->type); + return trim("{$isNegated}{$isEquality}{$type} {$node->parameter} {$node->description}"); + } + if ($node instanceof ExtendsTagValueNode || $node instanceof ImplementsTagValueNode) { + $type = $this->printType($node->type); + return trim("{$type} {$node->description}"); + } + if ($node instanceof MethodTagValueNode) { + $static = $node->isStatic ? 'static ' : ''; + $returnType = $node->returnType !== null ? $this->printType($node->returnType) . ' ' : ''; + $parameters = implode(', ', array_map(function (MethodTagValueParameterNode $parameter) : string { + return $this->print($parameter); + }, $node->parameters)); + $description = $node->description !== '' ? " {$node->description}" : ''; + $templateTypes = count($node->templateTypes) > 0 ? '<' . implode(', ', array_map(function (TemplateTagValueNode $templateTag) : string { + return $this->print($templateTag); + }, $node->templateTypes)) . '>' : ''; + return "{$static}{$returnType}{$node->methodName}{$templateTypes}({$parameters}){$description}"; + } + if ($node instanceof MixinTagValueNode) { + $type = $this->printType($node->type); + return trim("{$type} {$node->description}"); + } + if ($node instanceof RequireExtendsTagValueNode) { + $type = $this->printType($node->type); + return trim("{$type} {$node->description}"); + } + if ($node instanceof RequireImplementsTagValueNode) { + $type = $this->printType($node->type); + return trim("{$type} {$node->description}"); + } + if ($node instanceof ParamOutTagValueNode) { + $type = $this->printType($node->type); + return trim("{$type} {$node->parameterName} {$node->description}"); + } + if ($node instanceof ParamTagValueNode) { + $reference = $node->isReference ? '&' : ''; + $variadic = $node->isVariadic ? '...' : ''; + $type = $this->printType($node->type); + return trim("{$type} {$reference}{$variadic}{$node->parameterName} {$node->description}"); + } + if ($node instanceof ParamImmediatelyInvokedCallableTagValueNode) { + return trim("{$node->parameterName} {$node->description}"); + } + if ($node instanceof ParamLaterInvokedCallableTagValueNode) { + return trim("{$node->parameterName} {$node->description}"); + } + if ($node instanceof ParamClosureThisTagValueNode) { + return trim("{$node->type} {$node->parameterName} {$node->description}"); + } + if ($node instanceof PropertyTagValueNode) { + $type = $this->printType($node->type); + return trim("{$type} {$node->propertyName} {$node->description}"); + } + if ($node instanceof ReturnTagValueNode) { + $type = $this->printType($node->type); + return trim("{$type} {$node->description}"); + } + if ($node instanceof SelfOutTagValueNode) { + $type = $this->printType($node->type); + return trim($type . ' ' . $node->description); + } + if ($node instanceof TemplateTagValueNode) { + $bound = $node->bound !== null ? ' of ' . $this->printType($node->bound) : ''; + $default = $node->default !== null ? ' = ' . $this->printType($node->default) : ''; + return trim("{$node->name}{$bound}{$default} {$node->description}"); + } + if ($node instanceof ThrowsTagValueNode) { + $type = $this->printType($node->type); + return trim("{$type} {$node->description}"); + } + if ($node instanceof TypeAliasImportTagValueNode) { + return trim("{$node->importedAlias} from " . $this->printType($node->importedFrom) . ($node->importedAs !== null ? " as {$node->importedAs}" : '')); + } + if ($node instanceof TypeAliasTagValueNode) { + $type = $this->printType($node->type); + return trim("{$node->alias} {$type}"); + } + if ($node instanceof UsesTagValueNode) { + $type = $this->printType($node->type); + return trim("{$type} {$node->description}"); + } + if ($node instanceof VarTagValueNode) { + $type = $this->printType($node->type); + return trim("{$type} " . trim("{$node->variableName} {$node->description}")); + } + return (string) $node; + } + private function printType(TypeNode $node) : string + { + if ($node instanceof ArrayShapeNode) { + $items = array_map(function (ArrayShapeItemNode $item) : string { + return $this->printType($item); + }, $node->items); + if (!$node->sealed) { + $items[] = '...'; + } + return $node->kind . '{' . implode(', ', $items) . '}'; + } + if ($node instanceof ArrayShapeItemNode) { + if ($node->keyName !== null) { + return sprintf('%s%s: %s', $this->print($node->keyName), $node->optional ? '?' : '', $this->printType($node->valueType)); + } + return $this->printType($node->valueType); + } + if ($node instanceof ArrayTypeNode) { + return $this->printOffsetAccessType($node->type) . '[]'; + } + if ($node instanceof CallableTypeNode) { + if ($node->returnType instanceof CallableTypeNode || $node->returnType instanceof UnionTypeNode || $node->returnType instanceof IntersectionTypeNode) { + $returnType = $this->wrapInParentheses($node->returnType); + } else { + $returnType = $this->printType($node->returnType); + } + $template = $node->templateTypes !== [] ? '<' . implode(', ', array_map(function (TemplateTagValueNode $templateNode) : string { + return $this->print($templateNode); + }, $node->templateTypes)) . '>' : ''; + $parameters = implode(', ', array_map(function (CallableTypeParameterNode $parameterNode) : string { + return $this->print($parameterNode); + }, $node->parameters)); + return "{$node->identifier}{$template}({$parameters}): {$returnType}"; + } + if ($node instanceof ConditionalTypeForParameterNode) { + return sprintf('(%s %s %s ? %s : %s)', $node->parameterName, $node->negated ? 'is not' : 'is', $this->printType($node->targetType), $this->printType($node->if), $this->printType($node->else)); + } + if ($node instanceof ConditionalTypeNode) { + return sprintf('(%s %s %s ? %s : %s)', $this->printType($node->subjectType), $node->negated ? 'is not' : 'is', $this->printType($node->targetType), $this->printType($node->if), $this->printType($node->else)); + } + if ($node instanceof ConstTypeNode) { + return $this->printConstExpr($node->constExpr); + } + if ($node instanceof GenericTypeNode) { + $genericTypes = []; + foreach ($node->genericTypes as $index => $type) { + $variance = $node->variances[$index] ?? GenericTypeNode::VARIANCE_INVARIANT; + if ($variance === GenericTypeNode::VARIANCE_INVARIANT) { + $genericTypes[] = $this->printType($type); + } elseif ($variance === GenericTypeNode::VARIANCE_BIVARIANT) { + $genericTypes[] = '*'; + } else { + $genericTypes[] = sprintf('%s %s', $variance, $this->print($type)); + } + } + return $node->type . '<' . implode(', ', $genericTypes) . '>'; + } + if ($node instanceof IdentifierTypeNode) { + return $node->name; + } + if ($node instanceof IntersectionTypeNode || $node instanceof UnionTypeNode) { + $items = []; + foreach ($node->types as $type) { + if ($type instanceof IntersectionTypeNode || $type instanceof UnionTypeNode || $type instanceof NullableTypeNode) { + $items[] = $this->wrapInParentheses($type); + continue; + } + $items[] = $this->printType($type); + } + return implode($node instanceof IntersectionTypeNode ? '&' : '|', $items); + } + if ($node instanceof InvalidTypeNode) { + return (string) $node; + } + if ($node instanceof NullableTypeNode) { + if ($node->type instanceof IntersectionTypeNode || $node->type instanceof UnionTypeNode) { + return '?(' . $this->printType($node->type) . ')'; + } + return '?' . $this->printType($node->type); + } + if ($node instanceof ObjectShapeNode) { + $items = array_map(function (ObjectShapeItemNode $item) : string { + return $this->printType($item); + }, $node->items); + return 'object{' . implode(', ', $items) . '}'; + } + if ($node instanceof ObjectShapeItemNode) { + if ($node->keyName !== null) { + return sprintf('%s%s: %s', $this->print($node->keyName), $node->optional ? '?' : '', $this->printType($node->valueType)); + } + return $this->printType($node->valueType); + } + if ($node instanceof OffsetAccessTypeNode) { + return $this->printOffsetAccessType($node->type) . '[' . $this->printType($node->offset) . ']'; + } + if ($node instanceof ThisTypeNode) { + return (string) $node; + } + throw new LogicException(sprintf('Unknown node type %s', get_class($node))); } -} -if (!function_exists('PHPUnit\\Framework\\assertFileDoesNotExist')) { - /** - * Asserts that a file does not exist. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertFileDoesNotExist - */ - function assertFileDoesNotExist(string $filename, string $message = '') : void + private function wrapInParentheses(TypeNode $node) : string { - \PHPUnit\Framework\Assert::assertFileDoesNotExist(...func_get_args()); + return '(' . $this->printType($node) . ')'; } -} -if (!function_exists('PHPUnit\\Framework\\assertFileNotExists')) { - /** - * Asserts that a file does not exist. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @codeCoverageIgnore - * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4077 - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertFileNotExists - */ - function assertFileNotExists(string $filename, string $message = '') : void + private function printOffsetAccessType(TypeNode $type) : string { - \PHPUnit\Framework\Assert::assertFileNotExists(...func_get_args()); + if ($type instanceof CallableTypeNode || $type instanceof UnionTypeNode || $type instanceof IntersectionTypeNode || $type instanceof ConstTypeNode || $type instanceof NullableTypeNode) { + return $this->wrapInParentheses($type); + } + return $this->printType($type); } -} -if (!function_exists('PHPUnit\\Framework\\assertFileIsReadable')) { - /** - * Asserts that a file exists and is readable. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertFileIsReadable - */ - function assertFileIsReadable(string $file, string $message = '') : void + private function printConstExpr(ConstExprNode $node) : string { - \PHPUnit\Framework\Assert::assertFileIsReadable(...func_get_args()); + // this is fine - ConstExprNode classes do not contain nodes that need smart printer logic + return (string) $node; } -} -if (!function_exists('PHPUnit\\Framework\\assertFileIsNotReadable')) { /** - * Asserts that a file exists and is not readable. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertFileIsNotReadable + * @param Node[] $nodes + * @param Node[] $originalNodes */ - function assertFileIsNotReadable(string $file, string $message = '') : void + private function printArrayFormatPreserving(array $nodes, array $originalNodes, TokenIterator $originalTokens, int &$tokenIndex, string $parentNodeClass, string $subNodeName) : ?string { - \PHPUnit\Framework\Assert::assertFileIsNotReadable(...func_get_args()); + $diff = $this->differ->diffWithReplacements($originalNodes, $nodes); + $mapKey = $parentNodeClass . '->' . $subNodeName; + $insertStr = $this->listInsertionMap[$mapKey] ?? null; + $result = ''; + $beforeFirstKeepOrReplace = \true; + $delayedAdd = []; + $insertNewline = \false; + [$isMultiline, $beforeAsteriskIndent, $afterAsteriskIndent] = $this->isMultiline($tokenIndex, $originalNodes, $originalTokens); + if ($insertStr === "\n * ") { + $insertStr = sprintf('%s%s*%s', $originalTokens->getDetectedNewline() ?? "\n", $beforeAsteriskIndent, $afterAsteriskIndent); + } + foreach ($diff as $i => $diffElem) { + $diffType = $diffElem->type; + $newNode = $diffElem->new; + $originalNode = $diffElem->old; + if ($diffType === DiffElem::TYPE_KEEP || $diffType === DiffElem::TYPE_REPLACE) { + $beforeFirstKeepOrReplace = \false; + if (!$newNode instanceof Node || !$originalNode instanceof Node) { + return null; + } + $itemStartPos = $originalNode->getAttribute(Attribute::START_INDEX); + $itemEndPos = $originalNode->getAttribute(Attribute::END_INDEX); + if ($itemStartPos < 0 || $itemEndPos < 0 || $itemStartPos < $tokenIndex) { + throw new LogicException(); + } + $result .= $originalTokens->getContentBetween($tokenIndex, $itemStartPos); + if (count($delayedAdd) > 0) { + foreach ($delayedAdd as $delayedAddNode) { + $parenthesesNeeded = isset($this->parenthesesListMap[$mapKey]) && in_array(get_class($delayedAddNode), $this->parenthesesListMap[$mapKey], \true); + if ($parenthesesNeeded) { + $result .= '('; + } + $result .= $this->printNodeFormatPreserving($delayedAddNode, $originalTokens); + if ($parenthesesNeeded) { + $result .= ')'; + } + if ($insertNewline) { + $result .= $insertStr . sprintf('%s%s*%s', $originalTokens->getDetectedNewline() ?? "\n", $beforeAsteriskIndent, $afterAsteriskIndent); + } else { + $result .= $insertStr; + } + } + $delayedAdd = []; + } + $parenthesesNeeded = isset($this->parenthesesListMap[$mapKey]) && in_array(get_class($newNode), $this->parenthesesListMap[$mapKey], \true) && !in_array(get_class($originalNode), $this->parenthesesListMap[$mapKey], \true); + $addParentheses = $parenthesesNeeded && !$originalTokens->hasParentheses($itemStartPos, $itemEndPos); + if ($addParentheses) { + $result .= '('; + } + $result .= $this->printNodeFormatPreserving($newNode, $originalTokens); + if ($addParentheses) { + $result .= ')'; + } + $tokenIndex = $itemEndPos + 1; + } elseif ($diffType === DiffElem::TYPE_ADD) { + if ($insertStr === null) { + return null; + } + if (!$newNode instanceof Node) { + return null; + } + if ($insertStr === ', ' && $isMultiline) { + $insertStr = ','; + $insertNewline = \true; + } + if ($beforeFirstKeepOrReplace) { + // Will be inserted at the next "replace" or "keep" element + $delayedAdd[] = $newNode; + continue; + } + $itemEndPos = $tokenIndex - 1; + if ($insertNewline) { + $result .= $insertStr . sprintf('%s%s*%s', $originalTokens->getDetectedNewline() ?? "\n", $beforeAsteriskIndent, $afterAsteriskIndent); + } else { + $result .= $insertStr; + } + $parenthesesNeeded = isset($this->parenthesesListMap[$mapKey]) && in_array(get_class($newNode), $this->parenthesesListMap[$mapKey], \true); + if ($parenthesesNeeded) { + $result .= '('; + } + $result .= $this->printNodeFormatPreserving($newNode, $originalTokens); + if ($parenthesesNeeded) { + $result .= ')'; + } + $tokenIndex = $itemEndPos + 1; + } elseif ($diffType === DiffElem::TYPE_REMOVE) { + if (!$originalNode instanceof Node) { + return null; + } + $itemStartPos = $originalNode->getAttribute(Attribute::START_INDEX); + $itemEndPos = $originalNode->getAttribute(Attribute::END_INDEX); + if ($itemStartPos < 0 || $itemEndPos < 0) { + throw new LogicException(); + } + if ($i === 0) { + // If we're removing from the start, keep the tokens before the node and drop those after it, + // instead of the other way around. + $originalTokensArray = $originalTokens->getTokens(); + for ($j = $tokenIndex; $j < $itemStartPos; $j++) { + if ($originalTokensArray[$j][Lexer::TYPE_OFFSET] === Lexer::TOKEN_PHPDOC_EOL) { + break; + } + $result .= $originalTokensArray[$j][Lexer::VALUE_OFFSET]; + } + } + $tokenIndex = $itemEndPos + 1; + } + } + if (count($delayedAdd) > 0) { + if (!isset($this->emptyListInsertionMap[$mapKey])) { + return null; + } + [$findToken, $extraLeft, $extraRight] = $this->emptyListInsertionMap[$mapKey]; + if ($findToken !== null) { + $originalTokensArray = $originalTokens->getTokens(); + for (; $tokenIndex < count($originalTokensArray); $tokenIndex++) { + $result .= $originalTokensArray[$tokenIndex][Lexer::VALUE_OFFSET]; + if ($originalTokensArray[$tokenIndex][Lexer::VALUE_OFFSET] !== $findToken) { + continue; + } + $tokenIndex++; + break; + } + } + $first = \true; + $result .= $extraLeft; + foreach ($delayedAdd as $delayedAddNode) { + if (!$first) { + $result .= $insertStr; + if ($insertNewline) { + $result .= sprintf('%s%s*%s', $originalTokens->getDetectedNewline() ?? "\n", $beforeAsteriskIndent, $afterAsteriskIndent); + } + } + $result .= $this->printNodeFormatPreserving($delayedAddNode, $originalTokens); + $first = \false; + } + $result .= $extraRight; + } + return $result; } -} -if (!function_exists('PHPUnit\\Framework\\assertFileNotIsReadable')) { /** - * Asserts that a file exists and is not readable. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @codeCoverageIgnore - * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4080 - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertFileNotIsReadable + * @param Node[] $nodes + * @return array{bool, string, string} */ - function assertFileNotIsReadable(string $file, string $message = '') : void + private function isMultiline(int $initialIndex, array $nodes, TokenIterator $originalTokens) : array { - \PHPUnit\Framework\Assert::assertFileNotIsReadable(...func_get_args()); + $isMultiline = count($nodes) > 1; + $pos = $initialIndex; + $allText = ''; + /** @var Node|null $node */ + foreach ($nodes as $node) { + if (!$node instanceof Node) { + continue; + } + $endPos = $node->getAttribute(Attribute::END_INDEX) + 1; + $text = $originalTokens->getContentBetween($pos, $endPos); + $allText .= $text; + if (strpos($text, "\n") === \false) { + // We require that a newline is present between *every* item. If the formatting + // is inconsistent, with only some items having newlines, we don't consider it + // as multiline + $isMultiline = \false; + } + $pos = $endPos; + } + $c = preg_match_all('~\\n(?[\\x09\\x20]*)\\*(?\\x20*)~', $allText, $matches, PREG_SET_ORDER); + if ($c === 0) { + return [$isMultiline, '', '']; + } + $before = ''; + $after = ''; + foreach ($matches as $match) { + if (strlen($match['before']) > strlen($before)) { + $before = $match['before']; + } + if (strlen($match['after']) <= strlen($after)) { + continue; + } + $after = $match['after']; + } + return [$isMultiline, $before, $after]; } -} -if (!function_exists('PHPUnit\\Framework\\assertFileIsWritable')) { - /** - * Asserts that a file exists and is writable. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertFileIsWritable - */ - function assertFileIsWritable(string $file, string $message = '') : void + private function printNodeFormatPreserving(Node $node, TokenIterator $originalTokens) : string { - \PHPUnit\Framework\Assert::assertFileIsWritable(...func_get_args()); + /** @var Node|null $originalNode */ + $originalNode = $node->getAttribute(Attribute::ORIGINAL_NODE); + if ($originalNode === null) { + return $this->print($node); + } + $class = get_class($node); + if ($class !== get_class($originalNode)) { + throw new LogicException(); + } + $startPos = $originalNode->getAttribute(Attribute::START_INDEX); + $endPos = $originalNode->getAttribute(Attribute::END_INDEX); + if ($startPos < 0 || $endPos < 0) { + throw new LogicException(); + } + $result = ''; + $pos = $startPos; + $subNodeNames = array_keys(get_object_vars($node)); + foreach ($subNodeNames as $subNodeName) { + $subNode = $node->{$subNodeName}; + $origSubNode = $originalNode->{$subNodeName}; + if (!$subNode instanceof Node && $subNode !== null || !$origSubNode instanceof Node && $origSubNode !== null) { + if ($subNode === $origSubNode) { + // Unchanged, can reuse old code + continue; + } + if (is_array($subNode) && is_array($origSubNode)) { + // Array subnode changed, we might be able to reconstruct it + $listResult = $this->printArrayFormatPreserving($subNode, $origSubNode, $originalTokens, $pos, $class, $subNodeName); + if ($listResult === null) { + return $this->print($node); + } + $result .= $listResult; + continue; + } + return $this->print($node); + } + if ($origSubNode === null) { + if ($subNode === null) { + // Both null, nothing to do + continue; + } + return $this->print($node); + } + $subStartPos = $origSubNode->getAttribute(Attribute::START_INDEX); + $subEndPos = $origSubNode->getAttribute(Attribute::END_INDEX); + if ($subStartPos < 0 || $subEndPos < 0) { + throw new LogicException(); + } + if ($subEndPos < $subStartPos) { + return $this->print($node); + } + if ($subNode === null) { + return $this->print($node); + } + $result .= $originalTokens->getContentBetween($pos, $subStartPos); + $mapKey = get_class($node) . '->' . $subNodeName; + $parenthesesNeeded = isset($this->parenthesesMap[$mapKey]) && in_array(get_class($subNode), $this->parenthesesMap[$mapKey], \true); + if ($subNode->getAttribute(Attribute::ORIGINAL_NODE) !== null) { + $parenthesesNeeded = $parenthesesNeeded && !in_array(get_class($subNode->getAttribute(Attribute::ORIGINAL_NODE)), $this->parenthesesMap[$mapKey], \true); + } + $addParentheses = $parenthesesNeeded && !$originalTokens->hasParentheses($subStartPos, $subEndPos); + if ($addParentheses) { + $result .= '('; + } + $result .= $this->printNodeFormatPreserving($subNode, $originalTokens); + if ($addParentheses) { + $result .= ')'; + } + $pos = $subEndPos + 1; + } + return $result . $originalTokens->getContentBetween($pos, $endPos + 1); } } -if (!function_exists('PHPUnit\\Framework\\assertFileIsNotWritable')) { - /** - * Asserts that a file exists and is not writable. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertFileIsNotWritable - */ - function assertFileIsNotWritable(string $file, string $message = '') : void - { - \PHPUnit\Framework\Assert::assertFileIsNotWritable(...func_get_args()); - } + + + + + This Schema file defines the rules by which the XML configuration file of PHPUnit 9.6 may be structured. + + + + + + Root Element + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The main type specifying the document structure + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit; + +use Throwable; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +interface Exception extends Throwable +{ } -if (!function_exists('PHPUnit\\Framework\\assertFileNotIsWritable')) { + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use const DEBUG_BACKTRACE_IGNORE_ARGS; +use const PHP_EOL; +use function array_shift; +use function array_unshift; +use function assert; +use function class_exists; +use function count; +use function debug_backtrace; +use function explode; +use function file_get_contents; +use function func_get_args; +use function implode; +use function interface_exists; +use function is_array; +use function is_bool; +use function is_int; +use function is_iterable; +use function is_object; +use function is_string; +use function preg_match; +use function preg_split; +use function sprintf; +use function strpos; +use ArrayAccess; +use Countable; +use DOMAttr; +use DOMDocument; +use DOMElement; +use Generator; +use PHPUnit\Framework\Constraint\ArrayHasKey; +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\IsEqualCanonicalizing; +use PHPUnit\Framework\Constraint\IsEqualIgnoringCase; +use PHPUnit\Framework\Constraint\IsEqualWithDelta; +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\ObjectEquals; +use PHPUnit\Framework\Constraint\ObjectHasAttribute; +use PHPUnit\Framework\Constraint\ObjectHasProperty; +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\TraversableContainsEqual; +use PHPUnit\Framework\Constraint\TraversableContainsIdentical; +use PHPUnit\Framework\Constraint\TraversableContainsOnly; +use PHPUnit\Util\Type; +use PHPUnit\Util\Xml; +use PHPUnit\Util\Xml\Loader as XmlLoader; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +abstract class Assert +{ /** - * Asserts that a file exists and is not writable. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @codeCoverageIgnore - * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4083 + * @var int + */ + private static $count = 0; + /** + * Asserts that an array has a specified key. * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * @param int|string $key + * @param array|ArrayAccess $array * - * @see Assert::assertFileNotIsWritable + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws Exception + * @throws ExpectationFailedException */ - function assertFileNotIsWritable(string $file, string $message = '') : void + public static function assertArrayHasKey($key, $array, string $message = '') : void { - \PHPUnit\Framework\Assert::assertFileNotIsWritable(...func_get_args()); + if (!(is_int($key) || is_string($key))) { + throw \PHPUnit\Framework\InvalidArgumentException::create(1, 'integer or string'); + } + if (!(is_array($array) || $array instanceof ArrayAccess)) { + throw \PHPUnit\Framework\InvalidArgumentException::create(2, 'array or ArrayAccess'); + } + $constraint = new ArrayHasKey($key); + static::assertThat($array, $constraint, $message); } -} -if (!function_exists('PHPUnit\\Framework\\assertTrue')) { /** - * Asserts that a condition is true. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @psalm-assert true $condition + * Asserts that an array does not have a specified key. * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * @param int|string $key + * @param array|ArrayAccess $array * - * @see Assert::assertTrue + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws Exception + * @throws ExpectationFailedException */ - function assertTrue($condition, string $message = '') : void + public static function assertArrayNotHasKey($key, $array, string $message = '') : void { - \PHPUnit\Framework\Assert::assertTrue(...func_get_args()); + if (!(is_int($key) || is_string($key))) { + throw \PHPUnit\Framework\InvalidArgumentException::create(1, 'integer or string'); + } + if (!(is_array($array) || $array instanceof ArrayAccess)) { + throw \PHPUnit\Framework\InvalidArgumentException::create(2, 'array or ArrayAccess'); + } + $constraint = new LogicalNot(new ArrayHasKey($key)); + static::assertThat($array, $constraint, $message); } -} -if (!function_exists('PHPUnit\\Framework\\assertNotTrue')) { /** - * Asserts that a condition is not true. + * Asserts that a haystack contains a needle. * - * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @psalm-assert !true $condition - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertNotTrue + * @throws Exception + * @throws ExpectationFailedException */ - function assertNotTrue($condition, string $message = '') : void + public static function assertContains($needle, iterable $haystack, string $message = '') : void { - \PHPUnit\Framework\Assert::assertNotTrue(...func_get_args()); + $constraint = new TraversableContainsIdentical($needle); + static::assertThat($haystack, $constraint, $message); + } + public static function assertContainsEquals($needle, iterable $haystack, string $message = '') : void + { + $constraint = new TraversableContainsEqual($needle); + static::assertThat($haystack, $constraint, $message); } -} -if (!function_exists('PHPUnit\\Framework\\assertFalse')) { /** - * Asserts that a condition is false. + * Asserts that a haystack does not contain a needle. * - * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @psalm-assert false $condition - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertFalse + * @throws Exception + * @throws ExpectationFailedException */ - function assertFalse($condition, string $message = '') : void + public static function assertNotContains($needle, iterable $haystack, string $message = '') : void { - \PHPUnit\Framework\Assert::assertFalse(...func_get_args()); + $constraint = new LogicalNot(new TraversableContainsIdentical($needle)); + static::assertThat($haystack, $constraint, $message); + } + public static function assertNotContainsEquals($needle, iterable $haystack, string $message = '') : void + { + $constraint = new LogicalNot(new TraversableContainsEqual($needle)); + static::assertThat($haystack, $constraint, $message); } -} -if (!function_exists('PHPUnit\\Framework\\assertNotFalse')) { /** - * Asserts that a condition is not false. + * Asserts that a haystack contains only values of a given type. * - * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @psalm-assert !false $condition - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertNotFalse + * @throws ExpectationFailedException */ - function assertNotFalse($condition, string $message = '') : void + public static function assertContainsOnly(string $type, iterable $haystack, ?bool $isNativeType = null, string $message = '') : void { - \PHPUnit\Framework\Assert::assertNotFalse(...func_get_args()); + if ($isNativeType === null) { + $isNativeType = Type::isType($type); + } + static::assertThat($haystack, new TraversableContainsOnly($type, $isNativeType), $message); } -} -if (!function_exists('PHPUnit\\Framework\\assertNull')) { /** - * Asserts that a variable is null. + * Asserts that a haystack contains only instances of a given class name. * - * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @psalm-assert null $actual - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertNull + * @throws ExpectationFailedException */ - function assertNull($actual, string $message = '') : void + public static function assertContainsOnlyInstancesOf(string $className, iterable $haystack, string $message = '') : void { - \PHPUnit\Framework\Assert::assertNull(...func_get_args()); + static::assertThat($haystack, new TraversableContainsOnly($className, \false), $message); } -} -if (!function_exists('PHPUnit\\Framework\\assertNotNull')) { /** - * Asserts that a variable is not null. + * Asserts that a haystack does not contain only values of a given type. * - * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @psalm-assert !null $actual - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertNotNull + * @throws ExpectationFailedException */ - function assertNotNull($actual, string $message = '') : void + public static function assertNotContainsOnly(string $type, iterable $haystack, ?bool $isNativeType = null, string $message = '') : void { - \PHPUnit\Framework\Assert::assertNotNull(...func_get_args()); + if ($isNativeType === null) { + $isNativeType = Type::isType($type); + } + static::assertThat($haystack, new LogicalNot(new TraversableContainsOnly($type, $isNativeType)), $message); } -} -if (!function_exists('PHPUnit\\Framework\\assertFinite')) { /** - * Asserts that a variable is finite. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * Asserts the number of elements of an array, Countable or Traversable. * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * @param Countable|iterable $haystack * - * @see Assert::assertFinite + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws Exception + * @throws ExpectationFailedException */ - function assertFinite($actual, string $message = '') : void + public static function assertCount(int $expectedCount, $haystack, string $message = '') : void { - \PHPUnit\Framework\Assert::assertFinite(...func_get_args()); + if ($haystack instanceof Generator) { + self::createWarning('Passing an argument of type Generator for the $haystack parameter is deprecated. Support for this will be removed in PHPUnit 10.'); + } + if (!$haystack instanceof Countable && !is_iterable($haystack)) { + throw \PHPUnit\Framework\InvalidArgumentException::create(2, 'countable or iterable'); + } + static::assertThat($haystack, new Count($expectedCount), $message); } -} -if (!function_exists('PHPUnit\\Framework\\assertInfinite')) { /** - * Asserts that a variable is infinite. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * Asserts the number of elements of an array, Countable or Traversable. * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * @param Countable|iterable $haystack * - * @see Assert::assertInfinite + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws Exception + * @throws ExpectationFailedException */ - function assertInfinite($actual, string $message = '') : void + public static function assertNotCount(int $expectedCount, $haystack, string $message = '') : void { - \PHPUnit\Framework\Assert::assertInfinite(...func_get_args()); + if ($haystack instanceof Generator) { + self::createWarning('Passing an argument of type Generator for the $haystack parameter is deprecated. Support for this will be removed in PHPUnit 10.'); + } + if (!$haystack instanceof Countable && !is_iterable($haystack)) { + throw \PHPUnit\Framework\InvalidArgumentException::create(2, 'countable or iterable'); + } + $constraint = new LogicalNot(new Count($expectedCount)); + static::assertThat($haystack, $constraint, $message); } -} -if (!function_exists('PHPUnit\\Framework\\assertNan')) { /** - * Asserts that a variable is nan. + * Asserts that two variables are equal. * - * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertNan + * @throws ExpectationFailedException */ - function assertNan($actual, string $message = '') : void + public static function assertEquals($expected, $actual, string $message = '') : void { - \PHPUnit\Framework\Assert::assertNan(...func_get_args()); + $constraint = new IsEqual($expected); + static::assertThat($actual, $constraint, $message); } -} -if (!function_exists('PHPUnit\\Framework\\assertClassHasAttribute')) { /** - * Asserts that a class has a specified attribute. + * Asserts that two variables are equal (canonicalizing). * - * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws Exception - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertClassHasAttribute + * @throws ExpectationFailedException */ - function assertClassHasAttribute(string $attributeName, string $className, string $message = '') : void + public static function assertEqualsCanonicalizing($expected, $actual, string $message = '') : void { - \PHPUnit\Framework\Assert::assertClassHasAttribute(...func_get_args()); + $constraint = new IsEqualCanonicalizing($expected); + static::assertThat($actual, $constraint, $message); } -} -if (!function_exists('PHPUnit\\Framework\\assertClassNotHasAttribute')) { /** - * Asserts that a class does not have a specified attribute. + * Asserts that two variables are equal (ignoring case). * - * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws Exception - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertClassNotHasAttribute + * @throws ExpectationFailedException */ - function assertClassNotHasAttribute(string $attributeName, string $className, string $message = '') : void + public static function assertEqualsIgnoringCase($expected, $actual, string $message = '') : void { - \PHPUnit\Framework\Assert::assertClassNotHasAttribute(...func_get_args()); + $constraint = new IsEqualIgnoringCase($expected); + static::assertThat($actual, $constraint, $message); } -} -if (!function_exists('PHPUnit\\Framework\\assertClassHasStaticAttribute')) { /** - * Asserts that a class has a specified static attribute. + * Asserts that two variables are equal (with delta). * - * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws Exception - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertClassHasStaticAttribute + * @throws ExpectationFailedException */ - function assertClassHasStaticAttribute(string $attributeName, string $className, string $message = '') : void + public static function assertEqualsWithDelta($expected, $actual, float $delta, string $message = '') : void { - \PHPUnit\Framework\Assert::assertClassHasStaticAttribute(...func_get_args()); + $constraint = new IsEqualWithDelta($expected, $delta); + static::assertThat($actual, $constraint, $message); } -} -if (!function_exists('PHPUnit\\Framework\\assertClassNotHasStaticAttribute')) { /** - * Asserts that a class does not have a specified static attribute. + * Asserts that two variables are not equal. * - * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws Exception - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertClassNotHasStaticAttribute + * @throws ExpectationFailedException */ - function assertClassNotHasStaticAttribute(string $attributeName, string $className, string $message = '') : void + public static function assertNotEquals($expected, $actual, string $message = '') : void { - \PHPUnit\Framework\Assert::assertClassNotHasStaticAttribute(...func_get_args()); + $constraint = new LogicalNot(new IsEqual($expected)); + static::assertThat($actual, $constraint, $message); } -} -if (!function_exists('PHPUnit\\Framework\\assertObjectHasAttribute')) { /** - * Asserts that an object has a specified attribute. - * - * @param object $object + * Asserts that two variables are not equal (canonicalizing). * - * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws Exception - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertObjectHasAttribute + * @throws ExpectationFailedException */ - function assertObjectHasAttribute(string $attributeName, $object, string $message = '') : void + public static function assertNotEqualsCanonicalizing($expected, $actual, string $message = '') : void { - \PHPUnit\Framework\Assert::assertObjectHasAttribute(...func_get_args()); + $constraint = new LogicalNot(new IsEqualCanonicalizing($expected)); + static::assertThat($actual, $constraint, $message); } -} -if (!function_exists('PHPUnit\\Framework\\assertObjectNotHasAttribute')) { /** - * Asserts that an object does not have a specified attribute. - * - * @param object $object + * Asserts that two variables are not equal (ignoring case). * - * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws Exception - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertObjectNotHasAttribute + * @throws ExpectationFailedException */ - function assertObjectNotHasAttribute(string $attributeName, $object, string $message = '') : void + public static function assertNotEqualsIgnoringCase($expected, $actual, string $message = '') : void { - \PHPUnit\Framework\Assert::assertObjectNotHasAttribute(...func_get_args()); + $constraint = new LogicalNot(new IsEqualIgnoringCase($expected)); + static::assertThat($actual, $constraint, $message); } -} -if (!function_exists('PHPUnit\\Framework\\assertObjectHasProperty')) { /** - * Asserts that an object has a specified property. + * Asserts that two variables are not equal (with delta). * - * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws Exception - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertObjectHasProperty + * @throws ExpectationFailedException */ - function assertObjectHasProperty(string $attributeName, object $object, string $message = '') : void + public static function assertNotEqualsWithDelta($expected, $actual, float $delta, string $message = '') : void { - \PHPUnit\Framework\Assert::assertObjectHasProperty(...func_get_args()); + $constraint = new LogicalNot(new IsEqualWithDelta($expected, $delta)); + static::assertThat($actual, $constraint, $message); } -} -if (!function_exists('PHPUnit\\Framework\\assertObjectNotHasProperty')) { /** - * Asserts that an object does not have a specified property. - * * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws Exception - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertObjectNotHasProperty */ - function assertObjectNotHasProperty(string $attributeName, object $object, string $message = '') : void + public static function assertObjectEquals(object $expected, object $actual, string $method = 'equals', string $message = '') : void { - \PHPUnit\Framework\Assert::assertObjectNotHasProperty(...func_get_args()); + static::assertThat($actual, static::objectEquals($expected, $method), $message); } -} -if (!function_exists('PHPUnit\\Framework\\assertSame')) { /** - * Asserts that two variables have the same type and value. - * Used on objects, it asserts that two variables reference - * the same object. + * Asserts that a variable is empty. * - * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException * - * @psalm-template ExpectedType - * - * @psalm-param ExpectedType $expected - * - * @psalm-assert =ExpectedType $actual - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertSame + * @psalm-assert empty $actual */ - function assertSame($expected, $actual, string $message = '') : void + public static function assertEmpty($actual, string $message = '') : void { - \PHPUnit\Framework\Assert::assertSame(...func_get_args()); + if ($actual instanceof Generator) { + self::createWarning('Passing an argument of type Generator for the $actual parameter is deprecated. Support for this will be removed in PHPUnit 10.'); + } + static::assertThat($actual, static::isEmpty(), $message); } -} -if (!function_exists('PHPUnit\\Framework\\assertNotSame')) { /** - * 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. + * Asserts that a variable is not empty. * - * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertNotSame + * @psalm-assert !empty $actual */ - function assertNotSame($expected, $actual, string $message = '') : void + public static function assertNotEmpty($actual, string $message = '') : void { - \PHPUnit\Framework\Assert::assertNotSame(...func_get_args()); + if ($actual instanceof Generator) { + self::createWarning('Passing an argument of type Generator for the $actual parameter is deprecated. Support for this will be removed in PHPUnit 10.'); + } + static::assertThat($actual, static::logicalNot(static::isEmpty()), $message); } -} -if (!function_exists('PHPUnit\\Framework\\assertInstanceOf')) { /** - * Asserts that a variable is of a given type. + * Asserts that a value is greater than another value. * - * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws Exception - * - * @psalm-template ExpectedType of object - * - * @psalm-param class-string $expected - * - * @psalm-assert =ExpectedType $actual - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertInstanceOf + * @throws ExpectationFailedException */ - function assertInstanceOf(string $expected, $actual, string $message = '') : void + public static function assertGreaterThan($expected, $actual, string $message = '') : void { - \PHPUnit\Framework\Assert::assertInstanceOf(...func_get_args()); + static::assertThat($actual, static::greaterThan($expected), $message); } -} -if (!function_exists('PHPUnit\\Framework\\assertNotInstanceOf')) { /** - * Asserts that a variable is not of a given type. + * Asserts that a value is greater than or equal to another value. * - * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws Exception - * - * @psalm-template ExpectedType of object - * - * @psalm-param class-string $expected - * - * @psalm-assert !ExpectedType $actual - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertNotInstanceOf + * @throws ExpectationFailedException */ - function assertNotInstanceOf(string $expected, $actual, string $message = '') : void + public static function assertGreaterThanOrEqual($expected, $actual, string $message = '') : void { - \PHPUnit\Framework\Assert::assertNotInstanceOf(...func_get_args()); + static::assertThat($actual, static::greaterThanOrEqual($expected), $message); } -} -if (!function_exists('PHPUnit\\Framework\\assertIsArray')) { /** - * Asserts that a variable is of type array. + * Asserts that a value is smaller than another value. * - * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @psalm-assert array $actual - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertIsArray + * @throws ExpectationFailedException */ - function assertIsArray($actual, string $message = '') : void + public static function assertLessThan($expected, $actual, string $message = '') : void { - \PHPUnit\Framework\Assert::assertIsArray(...func_get_args()); + static::assertThat($actual, static::lessThan($expected), $message); } -} -if (!function_exists('PHPUnit\\Framework\\assertIsBool')) { /** - * Asserts that a variable is of type bool. + * Asserts that a value is smaller than or equal to another value. * - * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @psalm-assert bool $actual - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertIsBool + * @throws ExpectationFailedException */ - function assertIsBool($actual, string $message = '') : void + public static function assertLessThanOrEqual($expected, $actual, string $message = '') : void { - \PHPUnit\Framework\Assert::assertIsBool(...func_get_args()); + static::assertThat($actual, static::lessThanOrEqual($expected), $message); } -} -if (!function_exists('PHPUnit\\Framework\\assertIsFloat')) { /** - * Asserts that a variable is of type float. + * Asserts that the contents of one file is equal to the contents of another + * file. * - * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @psalm-assert float $actual - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertIsFloat + * @throws ExpectationFailedException */ - function assertIsFloat($actual, string $message = '') : void + public static function assertFileEquals(string $expected, string $actual, string $message = '') : void { - \PHPUnit\Framework\Assert::assertIsFloat(...func_get_args()); + static::assertFileExists($expected, $message); + static::assertFileExists($actual, $message); + $constraint = new IsEqual(file_get_contents($expected)); + static::assertThat(file_get_contents($actual), $constraint, $message); } -} -if (!function_exists('PHPUnit\\Framework\\assertIsInt')) { /** - * Asserts that a variable is of type int. + * Asserts that the contents of one file is equal to the contents of another + * file (canonicalizing). * - * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @psalm-assert int $actual - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertIsInt + * @throws ExpectationFailedException */ - function assertIsInt($actual, string $message = '') : void + public static function assertFileEqualsCanonicalizing(string $expected, string $actual, string $message = '') : void { - \PHPUnit\Framework\Assert::assertIsInt(...func_get_args()); + static::assertFileExists($expected, $message); + static::assertFileExists($actual, $message); + $constraint = new IsEqualCanonicalizing(file_get_contents($expected)); + static::assertThat(file_get_contents($actual), $constraint, $message); } -} -if (!function_exists('PHPUnit\\Framework\\assertIsNumeric')) { /** - * Asserts that a variable is of type numeric. + * Asserts that the contents of one file is equal to the contents of another + * file (ignoring case). * - * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @psalm-assert numeric $actual - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertIsNumeric + * @throws ExpectationFailedException */ - function assertIsNumeric($actual, string $message = '') : void + public static function assertFileEqualsIgnoringCase(string $expected, string $actual, string $message = '') : void { - \PHPUnit\Framework\Assert::assertIsNumeric(...func_get_args()); + static::assertFileExists($expected, $message); + static::assertFileExists($actual, $message); + $constraint = new IsEqualIgnoringCase(file_get_contents($expected)); + static::assertThat(file_get_contents($actual), $constraint, $message); } -} -if (!function_exists('PHPUnit\\Framework\\assertIsObject')) { /** - * Asserts that a variable is of type object. + * Asserts that the contents of one file is not equal to the contents of + * another file. * - * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @psalm-assert object $actual - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertIsObject + * @throws ExpectationFailedException */ - function assertIsObject($actual, string $message = '') : void + public static function assertFileNotEquals(string $expected, string $actual, string $message = '') : void { - \PHPUnit\Framework\Assert::assertIsObject(...func_get_args()); + static::assertFileExists($expected, $message); + static::assertFileExists($actual, $message); + $constraint = new LogicalNot(new IsEqual(file_get_contents($expected))); + static::assertThat(file_get_contents($actual), $constraint, $message); } -} -if (!function_exists('PHPUnit\\Framework\\assertIsResource')) { /** - * Asserts that a variable is of type resource. + * Asserts that the contents of one file is not equal to the contents of another + * file (canonicalizing). * - * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @psalm-assert resource $actual - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertIsResource + * @throws ExpectationFailedException */ - function assertIsResource($actual, string $message = '') : void + public static function assertFileNotEqualsCanonicalizing(string $expected, string $actual, string $message = '') : void { - \PHPUnit\Framework\Assert::assertIsResource(...func_get_args()); + static::assertFileExists($expected, $message); + static::assertFileExists($actual, $message); + $constraint = new LogicalNot(new IsEqualCanonicalizing(file_get_contents($expected))); + static::assertThat(file_get_contents($actual), $constraint, $message); } -} -if (!function_exists('PHPUnit\\Framework\\assertIsClosedResource')) { /** - * Asserts that a variable is of type resource and is closed. + * Asserts that the contents of one file is not equal to the contents of another + * file (ignoring case). * - * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @psalm-assert resource $actual - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertIsClosedResource + * @throws ExpectationFailedException */ - function assertIsClosedResource($actual, string $message = '') : void + public static function assertFileNotEqualsIgnoringCase(string $expected, string $actual, string $message = '') : void { - \PHPUnit\Framework\Assert::assertIsClosedResource(...func_get_args()); + static::assertFileExists($expected, $message); + static::assertFileExists($actual, $message); + $constraint = new LogicalNot(new IsEqualIgnoringCase(file_get_contents($expected))); + static::assertThat(file_get_contents($actual), $constraint, $message); } -} -if (!function_exists('PHPUnit\\Framework\\assertIsString')) { /** - * Asserts that a variable is of type string. + * Asserts that the contents of a string is equal + * to the contents of a file. * - * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @psalm-assert string $actual - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertIsString + * @throws ExpectationFailedException */ - function assertIsString($actual, string $message = '') : void + public static function assertStringEqualsFile(string $expectedFile, string $actualString, string $message = '') : void { - \PHPUnit\Framework\Assert::assertIsString(...func_get_args()); + static::assertFileExists($expectedFile, $message); + $constraint = new IsEqual(file_get_contents($expectedFile)); + static::assertThat($actualString, $constraint, $message); } -} -if (!function_exists('PHPUnit\\Framework\\assertIsScalar')) { /** - * Asserts that a variable is of type scalar. + * Asserts that the contents of a string is equal + * to the contents of a file (canonicalizing). * - * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @psalm-assert scalar $actual - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertIsScalar + * @throws ExpectationFailedException */ - function assertIsScalar($actual, string $message = '') : void + public static function assertStringEqualsFileCanonicalizing(string $expectedFile, string $actualString, string $message = '') : void { - \PHPUnit\Framework\Assert::assertIsScalar(...func_get_args()); + static::assertFileExists($expectedFile, $message); + $constraint = new IsEqualCanonicalizing(file_get_contents($expectedFile)); + static::assertThat($actualString, $constraint, $message); } -} -if (!function_exists('PHPUnit\\Framework\\assertIsCallable')) { /** - * Asserts that a variable is of type callable. + * Asserts that the contents of a string is equal + * to the contents of a file (ignoring case). * - * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @psalm-assert callable $actual - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertIsCallable + * @throws ExpectationFailedException */ - function assertIsCallable($actual, string $message = '') : void + public static function assertStringEqualsFileIgnoringCase(string $expectedFile, string $actualString, string $message = '') : void { - \PHPUnit\Framework\Assert::assertIsCallable(...func_get_args()); + static::assertFileExists($expectedFile, $message); + $constraint = new IsEqualIgnoringCase(file_get_contents($expectedFile)); + static::assertThat($actualString, $constraint, $message); } -} -if (!function_exists('PHPUnit\\Framework\\assertIsIterable')) { /** - * Asserts that a variable is of type iterable. + * Asserts that the contents of a string is not equal + * to the contents of a file. * - * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @psalm-assert iterable $actual - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertIsIterable + * @throws ExpectationFailedException */ - function assertIsIterable($actual, string $message = '') : void + public static function assertStringNotEqualsFile(string $expectedFile, string $actualString, string $message = '') : void { - \PHPUnit\Framework\Assert::assertIsIterable(...func_get_args()); + static::assertFileExists($expectedFile, $message); + $constraint = new LogicalNot(new IsEqual(file_get_contents($expectedFile))); + static::assertThat($actualString, $constraint, $message); } -} -if (!function_exists('PHPUnit\\Framework\\assertIsNotArray')) { /** - * Asserts that a variable is not of type array. + * Asserts that the contents of a string is not equal + * to the contents of a file (canonicalizing). * - * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @psalm-assert !array $actual - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertIsNotArray + * @throws ExpectationFailedException */ - function assertIsNotArray($actual, string $message = '') : void + public static function assertStringNotEqualsFileCanonicalizing(string $expectedFile, string $actualString, string $message = '') : void { - \PHPUnit\Framework\Assert::assertIsNotArray(...func_get_args()); + static::assertFileExists($expectedFile, $message); + $constraint = new LogicalNot(new IsEqualCanonicalizing(file_get_contents($expectedFile))); + static::assertThat($actualString, $constraint, $message); } -} -if (!function_exists('PHPUnit\\Framework\\assertIsNotBool')) { /** - * Asserts that a variable is not of type bool. + * Asserts that the contents of a string is not equal + * to the contents of a file (ignoring case). * - * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @psalm-assert !bool $actual - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertIsNotBool + * @throws ExpectationFailedException */ - function assertIsNotBool($actual, string $message = '') : void + public static function assertStringNotEqualsFileIgnoringCase(string $expectedFile, string $actualString, string $message = '') : void { - \PHPUnit\Framework\Assert::assertIsNotBool(...func_get_args()); + static::assertFileExists($expectedFile, $message); + $constraint = new LogicalNot(new IsEqualIgnoringCase(file_get_contents($expectedFile))); + static::assertThat($actualString, $constraint, $message); } -} -if (!function_exists('PHPUnit\\Framework\\assertIsNotFloat')) { /** - * Asserts that a variable is not of type float. + * Asserts that a file/dir is readable. * - * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @psalm-assert !float $actual - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertIsNotFloat + * @throws ExpectationFailedException */ - function assertIsNotFloat($actual, string $message = '') : void + public static function assertIsReadable(string $filename, string $message = '') : void { - \PHPUnit\Framework\Assert::assertIsNotFloat(...func_get_args()); + static::assertThat($filename, new IsReadable(), $message); } -} -if (!function_exists('PHPUnit\\Framework\\assertIsNotInt')) { /** - * Asserts that a variable is not of type int. + * Asserts that a file/dir exists and is not readable. * - * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @psalm-assert !int $actual - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertIsNotInt + * @throws ExpectationFailedException */ - function assertIsNotInt($actual, string $message = '') : void + public static function assertIsNotReadable(string $filename, string $message = '') : void { - \PHPUnit\Framework\Assert::assertIsNotInt(...func_get_args()); + static::assertThat($filename, new LogicalNot(new IsReadable()), $message); } -} -if (!function_exists('PHPUnit\\Framework\\assertIsNotNumeric')) { /** - * Asserts that a variable is not of type numeric. + * Asserts that a file/dir exists and is not readable. * - * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException * - * @psalm-assert !numeric $actual - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * @codeCoverageIgnore * - * @see Assert::assertIsNotNumeric + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4062 */ - function assertIsNotNumeric($actual, string $message = '') : void + public static function assertNotIsReadable(string $filename, string $message = '') : void { - \PHPUnit\Framework\Assert::assertIsNotNumeric(...func_get_args()); + self::createWarning('assertNotIsReadable() is deprecated and will be removed in PHPUnit 10. Refactor your code to use assertIsNotReadable() instead.'); + static::assertThat($filename, new LogicalNot(new IsReadable()), $message); } -} -if (!function_exists('PHPUnit\\Framework\\assertIsNotObject')) { /** - * Asserts that a variable is not of type object. + * Asserts that a file/dir exists and is writable. * - * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @psalm-assert !object $actual - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertIsNotObject + * @throws ExpectationFailedException */ - function assertIsNotObject($actual, string $message = '') : void + public static function assertIsWritable(string $filename, string $message = '') : void { - \PHPUnit\Framework\Assert::assertIsNotObject(...func_get_args()); + static::assertThat($filename, new IsWritable(), $message); } -} -if (!function_exists('PHPUnit\\Framework\\assertIsNotResource')) { /** - * Asserts that a variable is not of type resource. + * Asserts that a file/dir exists and is not writable. * - * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @psalm-assert !resource $actual - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertIsNotResource + * @throws ExpectationFailedException */ - function assertIsNotResource($actual, string $message = '') : void + public static function assertIsNotWritable(string $filename, string $message = '') : void { - \PHPUnit\Framework\Assert::assertIsNotResource(...func_get_args()); + static::assertThat($filename, new LogicalNot(new IsWritable()), $message); } -} -if (!function_exists('PHPUnit\\Framework\\assertIsNotClosedResource')) { /** - * Asserts that a variable is not of type resource. + * Asserts that a file/dir exists and is not writable. * - * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException * - * @psalm-assert !resource $actual - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * @codeCoverageIgnore * - * @see Assert::assertIsNotClosedResource + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4065 */ - function assertIsNotClosedResource($actual, string $message = '') : void + public static function assertNotIsWritable(string $filename, string $message = '') : void { - \PHPUnit\Framework\Assert::assertIsNotClosedResource(...func_get_args()); + self::createWarning('assertNotIsWritable() is deprecated and will be removed in PHPUnit 10. Refactor your code to use assertIsNotWritable() instead.'); + static::assertThat($filename, new LogicalNot(new IsWritable()), $message); } -} -if (!function_exists('PHPUnit\\Framework\\assertIsNotString')) { /** - * Asserts that a variable is not of type string. + * Asserts that a directory exists. * - * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @psalm-assert !string $actual - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertIsNotString + * @throws ExpectationFailedException */ - function assertIsNotString($actual, string $message = '') : void + public static function assertDirectoryExists(string $directory, string $message = '') : void { - \PHPUnit\Framework\Assert::assertIsNotString(...func_get_args()); + static::assertThat($directory, new DirectoryExists(), $message); } -} -if (!function_exists('PHPUnit\\Framework\\assertIsNotScalar')) { /** - * Asserts that a variable is not of type scalar. + * Asserts that a directory does not exist. * - * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @psalm-assert !scalar $actual - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertIsNotScalar + * @throws ExpectationFailedException */ - function assertIsNotScalar($actual, string $message = '') : void + public static function assertDirectoryDoesNotExist(string $directory, string $message = '') : void { - \PHPUnit\Framework\Assert::assertIsNotScalar(...func_get_args()); + static::assertThat($directory, new LogicalNot(new DirectoryExists()), $message); } -} -if (!function_exists('PHPUnit\\Framework\\assertIsNotCallable')) { /** - * Asserts that a variable is not of type callable. + * Asserts that a directory does not exist. * - * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException * - * @psalm-assert !callable $actual - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * @codeCoverageIgnore * - * @see Assert::assertIsNotCallable + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4068 */ - function assertIsNotCallable($actual, string $message = '') : void + public static function assertDirectoryNotExists(string $directory, string $message = '') : void { - \PHPUnit\Framework\Assert::assertIsNotCallable(...func_get_args()); + self::createWarning('assertDirectoryNotExists() is deprecated and will be removed in PHPUnit 10. Refactor your code to use assertDirectoryDoesNotExist() instead.'); + static::assertThat($directory, new LogicalNot(new DirectoryExists()), $message); } -} -if (!function_exists('PHPUnit\\Framework\\assertIsNotIterable')) { /** - * Asserts that a variable is not of type iterable. + * Asserts that a directory exists and is readable. * - * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @psalm-assert !iterable $actual - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertIsNotIterable + * @throws ExpectationFailedException */ - function assertIsNotIterable($actual, string $message = '') : void + public static function assertDirectoryIsReadable(string $directory, string $message = '') : void { - \PHPUnit\Framework\Assert::assertIsNotIterable(...func_get_args()); + self::assertDirectoryExists($directory, $message); + self::assertIsReadable($directory, $message); } -} -if (!function_exists('PHPUnit\\Framework\\assertMatchesRegularExpression')) { /** - * Asserts that a string matches a given regular expression. + * Asserts that a directory exists and is not readable. * - * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertMatchesRegularExpression + * @throws ExpectationFailedException */ - function assertMatchesRegularExpression(string $pattern, string $string, string $message = '') : void + public static function assertDirectoryIsNotReadable(string $directory, string $message = '') : void { - \PHPUnit\Framework\Assert::assertMatchesRegularExpression(...func_get_args()); + self::assertDirectoryExists($directory, $message); + self::assertIsNotReadable($directory, $message); } -} -if (!function_exists('PHPUnit\\Framework\\assertRegExp')) { /** - * Asserts that a string matches a given regular expression. + * Asserts that a directory exists and is not readable. * - * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException * * @codeCoverageIgnore * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4086 - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertRegExp + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4071 */ - function assertRegExp(string $pattern, string $string, string $message = '') : void + public static function assertDirectoryNotIsReadable(string $directory, string $message = '') : void { - \PHPUnit\Framework\Assert::assertRegExp(...func_get_args()); + self::createWarning('assertDirectoryNotIsReadable() is deprecated and will be removed in PHPUnit 10. Refactor your code to use assertDirectoryIsNotReadable() instead.'); + self::assertDirectoryExists($directory, $message); + self::assertIsNotReadable($directory, $message); } -} -if (!function_exists('PHPUnit\\Framework\\assertDoesNotMatchRegularExpression')) { /** - * Asserts that a string does not match a given regular expression. + * Asserts that a directory exists and is writable. * - * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertDoesNotMatchRegularExpression + * @throws ExpectationFailedException */ - function assertDoesNotMatchRegularExpression(string $pattern, string $string, string $message = '') : void + public static function assertDirectoryIsWritable(string $directory, string $message = '') : void { - \PHPUnit\Framework\Assert::assertDoesNotMatchRegularExpression(...func_get_args()); + self::assertDirectoryExists($directory, $message); + self::assertIsWritable($directory, $message); } -} -if (!function_exists('PHPUnit\\Framework\\assertNotRegExp')) { /** - * Asserts that a string does not match a given regular expression. + * Asserts that a directory exists and is not writable. * - * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @codeCoverageIgnore - * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4089 - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertNotRegExp + * @throws ExpectationFailedException */ - function assertNotRegExp(string $pattern, string $string, string $message = '') : void + public static function assertDirectoryIsNotWritable(string $directory, string $message = '') : void { - \PHPUnit\Framework\Assert::assertNotRegExp(...func_get_args()); + self::assertDirectoryExists($directory, $message); + self::assertIsNotWritable($directory, $message); } -} -if (!function_exists('PHPUnit\\Framework\\assertSameSize')) { /** - * Assert that the size of two arrays (or `Countable` or `Traversable` objects) - * is the same. - * - * @param Countable|iterable $expected - * @param Countable|iterable $actual + * Asserts that a directory exists and is not writable. * - * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws Exception + * @throws ExpectationFailedException * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * @codeCoverageIgnore * - * @see Assert::assertSameSize + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4074 */ - function assertSameSize($expected, $actual, string $message = '') : void + public static function assertDirectoryNotIsWritable(string $directory, string $message = '') : void { - \PHPUnit\Framework\Assert::assertSameSize(...func_get_args()); + self::createWarning('assertDirectoryNotIsWritable() is deprecated and will be removed in PHPUnit 10. Refactor your code to use assertDirectoryIsNotWritable() instead.'); + self::assertDirectoryExists($directory, $message); + self::assertIsNotWritable($directory, $message); } -} -if (!function_exists('PHPUnit\\Framework\\assertNotSameSize')) { /** - * Assert that the size of two arrays (or `Countable` or `Traversable` objects) - * is not the same. - * - * @param Countable|iterable $expected - * @param Countable|iterable $actual + * Asserts that a file exists. * - * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws Exception - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertNotSameSize + * @throws ExpectationFailedException */ - function assertNotSameSize($expected, $actual, string $message = '') : void + public static function assertFileExists(string $filename, string $message = '') : void { - \PHPUnit\Framework\Assert::assertNotSameSize(...func_get_args()); + static::assertThat($filename, new FileExists(), $message); } -} -if (!function_exists('PHPUnit\\Framework\\assertStringMatchesFormat')) { /** - * Asserts that a string matches a given format string. + * Asserts that a file does not exist. * - * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertStringMatchesFormat + * @throws ExpectationFailedException */ - function assertStringMatchesFormat(string $format, string $string, string $message = '') : void + public static function assertFileDoesNotExist(string $filename, string $message = '') : void { - \PHPUnit\Framework\Assert::assertStringMatchesFormat(...func_get_args()); + static::assertThat($filename, new LogicalNot(new FileExists()), $message); } -} -if (!function_exists('PHPUnit\\Framework\\assertStringNotMatchesFormat')) { /** - * Asserts that a string does not match a given format string. + * Asserts that a file does not exist. * - * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * @codeCoverageIgnore * - * @see Assert::assertStringNotMatchesFormat + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4077 */ - function assertStringNotMatchesFormat(string $format, string $string, string $message = '') : void + public static function assertFileNotExists(string $filename, string $message = '') : void { - \PHPUnit\Framework\Assert::assertStringNotMatchesFormat(...func_get_args()); + self::createWarning('assertFileNotExists() is deprecated and will be removed in PHPUnit 10. Refactor your code to use assertFileDoesNotExist() instead.'); + static::assertThat($filename, new LogicalNot(new FileExists()), $message); } -} -if (!function_exists('PHPUnit\\Framework\\assertStringMatchesFormatFile')) { /** - * Asserts that a string matches a given format file. + * Asserts that a file exists and is readable. * - * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertStringMatchesFormatFile + * @throws ExpectationFailedException */ - function assertStringMatchesFormatFile(string $formatFile, string $string, string $message = '') : void + public static function assertFileIsReadable(string $file, string $message = '') : void { - \PHPUnit\Framework\Assert::assertStringMatchesFormatFile(...func_get_args()); + self::assertFileExists($file, $message); + self::assertIsReadable($file, $message); } -} -if (!function_exists('PHPUnit\\Framework\\assertStringNotMatchesFormatFile')) { /** - * Asserts that a string does not match a given format string. + * Asserts that a file exists and is not readable. * - * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertStringNotMatchesFormatFile + * @throws ExpectationFailedException */ - function assertStringNotMatchesFormatFile(string $formatFile, string $string, string $message = '') : void + public static function assertFileIsNotReadable(string $file, string $message = '') : void { - \PHPUnit\Framework\Assert::assertStringNotMatchesFormatFile(...func_get_args()); + self::assertFileExists($file, $message); + self::assertIsNotReadable($file, $message); } -} -if (!function_exists('PHPUnit\\Framework\\assertStringStartsWith')) { /** - * Asserts that a string starts with a given prefix. + * Asserts that a file exists and is not readable. * - * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * @codeCoverageIgnore * - * @see Assert::assertStringStartsWith + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4080 */ - function assertStringStartsWith(string $prefix, string $string, string $message = '') : void + public static function assertFileNotIsReadable(string $file, string $message = '') : void { - \PHPUnit\Framework\Assert::assertStringStartsWith(...func_get_args()); + self::createWarning('assertFileNotIsReadable() is deprecated and will be removed in PHPUnit 10. Refactor your code to use assertFileIsNotReadable() instead.'); + self::assertFileExists($file, $message); + self::assertIsNotReadable($file, $message); } -} -if (!function_exists('PHPUnit\\Framework\\assertStringStartsNotWith')) { /** - * Asserts that a string starts not with a given prefix. - * - * @param string $prefix - * @param string $string + * Asserts that a file exists and is writable. * - * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertStringStartsNotWith + * @throws ExpectationFailedException */ - function assertStringStartsNotWith($prefix, $string, string $message = '') : void + public static function assertFileIsWritable(string $file, string $message = '') : void { - \PHPUnit\Framework\Assert::assertStringStartsNotWith(...func_get_args()); + self::assertFileExists($file, $message); + self::assertIsWritable($file, $message); } -} -if (!function_exists('PHPUnit\\Framework\\assertStringContainsString')) { /** - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * Asserts that a file exists and is not writable. * - * @see Assert::assertStringContainsString + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException */ - function assertStringContainsString(string $needle, string $haystack, string $message = '') : void + public static function assertFileIsNotWritable(string $file, string $message = '') : void { - \PHPUnit\Framework\Assert::assertStringContainsString(...func_get_args()); + self::assertFileExists($file, $message); + self::assertIsNotWritable($file, $message); } -} -if (!function_exists('PHPUnit\\Framework\\assertStringContainsStringIgnoringCase')) { /** - * @throws ExpectationFailedException + * Asserts that a file exists and is not writable. + * * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * @codeCoverageIgnore * - * @see Assert::assertStringContainsStringIgnoringCase + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4083 */ - function assertStringContainsStringIgnoringCase(string $needle, string $haystack, string $message = '') : void + public static function assertFileNotIsWritable(string $file, string $message = '') : void { - \PHPUnit\Framework\Assert::assertStringContainsStringIgnoringCase(...func_get_args()); + self::createWarning('assertFileNotIsWritable() is deprecated and will be removed in PHPUnit 10. Refactor your code to use assertFileIsNotWritable() instead.'); + self::assertFileExists($file, $message); + self::assertIsNotWritable($file, $message); } -} -if (!function_exists('PHPUnit\\Framework\\assertStringNotContainsString')) { /** - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * Asserts that a condition is true. * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException * - * @see Assert::assertStringNotContainsString + * @psalm-assert true $condition */ - function assertStringNotContainsString(string $needle, string $haystack, string $message = '') : void + public static function assertTrue($condition, string $message = '') : void { - \PHPUnit\Framework\Assert::assertStringNotContainsString(...func_get_args()); + static::assertThat($condition, static::isTrue(), $message); } -} -if (!function_exists('PHPUnit\\Framework\\assertStringNotContainsStringIgnoringCase')) { /** + * Asserts that a condition is not true. + * + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException * @throws ExpectationFailedException + * + * @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 \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * @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. * - * @see Assert::assertStringNotContainsStringIgnoringCase + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException + * + * @psalm-assert !false $condition */ - function assertStringNotContainsStringIgnoringCase(string $needle, string $haystack, string $message = '') : void + public static function assertNotFalse($condition, string $message = '') : void { - \PHPUnit\Framework\Assert::assertStringNotContainsStringIgnoringCase(...func_get_args()); + static::assertThat($condition, static::logicalNot(static::isFalse()), $message); } -} -if (!function_exists('PHPUnit\\Framework\\assertStringEndsWith')) { /** - * Asserts that a string ends with a given suffix. + * Asserts that a variable is null. * + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException * @throws ExpectationFailedException + * + * @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 \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * @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. * - * @see Assert::assertStringEndsWith + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException */ - function assertStringEndsWith(string $suffix, string $string, string $message = '') : void + public static function assertFinite($actual, string $message = '') : void { - \PHPUnit\Framework\Assert::assertStringEndsWith(...func_get_args()); + static::assertThat($actual, static::isFinite(), $message); } -} -if (!function_exists('PHPUnit\\Framework\\assertStringEndsNotWith')) { /** - * Asserts that a string ends not with a given suffix. + * Asserts that a variable is infinite. * + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException * @throws ExpectationFailedException + */ + public static function assertInfinite($actual, string $message = '') : void + { + static::assertThat($actual, static::isInfinite(), $message); + } + /** + * Asserts that a variable is nan. + * * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException + */ + public static function assertNan($actual, string $message = '') : void + { + static::assertThat($actual, static::isNan(), $message); + } + /** + * Asserts that a class has a specified attribute. * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws Exception + * @throws ExpectationFailedException * - * @see Assert::assertStringEndsNotWith + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4601 */ - function assertStringEndsNotWith(string $suffix, string $string, string $message = '') : void + public static function assertClassHasAttribute(string $attributeName, string $className, string $message = '') : void { - \PHPUnit\Framework\Assert::assertStringEndsNotWith(...func_get_args()); + self::createWarning('assertClassHasAttribute() is deprecated and will be removed in PHPUnit 10.'); + if (!self::isValidClassAttributeName($attributeName)) { + throw \PHPUnit\Framework\InvalidArgumentException::create(1, 'valid attribute name'); + } + if (!class_exists($className)) { + throw \PHPUnit\Framework\InvalidArgumentException::create(2, 'class name'); + } + static::assertThat($className, new ClassHasAttribute($attributeName), $message); } -} -if (!function_exists('PHPUnit\\Framework\\assertXmlFileEqualsXmlFile')) { /** - * Asserts that two XML files are equal. + * Asserts that a class does not have a specified attribute. * - * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException * @throws Exception + * @throws ExpectationFailedException * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertXmlFileEqualsXmlFile + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4601 */ - function assertXmlFileEqualsXmlFile(string $expectedFile, string $actualFile, string $message = '') : void + public static function assertClassNotHasAttribute(string $attributeName, string $className, string $message = '') : void { - \PHPUnit\Framework\Assert::assertXmlFileEqualsXmlFile(...func_get_args()); + self::createWarning('assertClassNotHasAttribute() is deprecated and will be removed in PHPUnit 10.'); + if (!self::isValidClassAttributeName($attributeName)) { + throw \PHPUnit\Framework\InvalidArgumentException::create(1, 'valid attribute name'); + } + if (!class_exists($className)) { + throw \PHPUnit\Framework\InvalidArgumentException::create(2, 'class name'); + } + static::assertThat($className, new LogicalNot(new ClassHasAttribute($attributeName)), $message); } -} -if (!function_exists('PHPUnit\\Framework\\assertXmlFileNotEqualsXmlFile')) { /** - * Asserts that two XML files are not equal. + * Asserts that a class has a specified static attribute. * - * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws \PHPUnit\Util\Exception - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * @throws Exception + * @throws ExpectationFailedException * - * @see Assert::assertXmlFileNotEqualsXmlFile + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4601 */ - function assertXmlFileNotEqualsXmlFile(string $expectedFile, string $actualFile, string $message = '') : void + public static function assertClassHasStaticAttribute(string $attributeName, string $className, string $message = '') : void { - \PHPUnit\Framework\Assert::assertXmlFileNotEqualsXmlFile(...func_get_args()); + self::createWarning('assertClassHasStaticAttribute() is deprecated and will be removed in PHPUnit 10.'); + if (!self::isValidClassAttributeName($attributeName)) { + throw \PHPUnit\Framework\InvalidArgumentException::create(1, 'valid attribute name'); + } + if (!class_exists($className)) { + throw \PHPUnit\Framework\InvalidArgumentException::create(2, 'class name'); + } + static::assertThat($className, new ClassHasStaticAttribute($attributeName), $message); } -} -if (!function_exists('PHPUnit\\Framework\\assertXmlStringEqualsXmlFile')) { /** - * Asserts that two XML documents are equal. - * - * @param DOMDocument|string $actualXml + * Asserts that a class does not have a specified static attribute. * - * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws \PHPUnit\Util\Xml\Exception - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * @throws Exception + * @throws ExpectationFailedException * - * @see Assert::assertXmlStringEqualsXmlFile + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4601 */ - function assertXmlStringEqualsXmlFile(string $expectedFile, $actualXml, string $message = '') : void + public static function assertClassNotHasStaticAttribute(string $attributeName, string $className, string $message = '') : void { - \PHPUnit\Framework\Assert::assertXmlStringEqualsXmlFile(...func_get_args()); + self::createWarning('assertClassNotHasStaticAttribute() is deprecated and will be removed in PHPUnit 10.'); + if (!self::isValidClassAttributeName($attributeName)) { + throw \PHPUnit\Framework\InvalidArgumentException::create(1, 'valid attribute name'); + } + if (!class_exists($className)) { + throw \PHPUnit\Framework\InvalidArgumentException::create(2, 'class name'); + } + static::assertThat($className, new LogicalNot(new ClassHasStaticAttribute($attributeName)), $message); } -} -if (!function_exists('PHPUnit\\Framework\\assertXmlStringNotEqualsXmlFile')) { /** - * Asserts that two XML documents are not equal. + * Asserts that an object has a specified attribute. * - * @param DOMDocument|string $actualXml + * @param object $object * - * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws \PHPUnit\Util\Xml\Exception - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * @throws Exception + * @throws ExpectationFailedException * - * @see Assert::assertXmlStringNotEqualsXmlFile + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4601 */ - function assertXmlStringNotEqualsXmlFile(string $expectedFile, $actualXml, string $message = '') : void + public static function assertObjectHasAttribute(string $attributeName, $object, string $message = '') : void { - \PHPUnit\Framework\Assert::assertXmlStringNotEqualsXmlFile(...func_get_args()); + self::createWarning('assertObjectHasAttribute() is deprecated and will be removed in PHPUnit 10. Refactor your test to use assertObjectHasProperty() instead.'); + if (!self::isValidObjectAttributeName($attributeName)) { + throw \PHPUnit\Framework\InvalidArgumentException::create(1, 'valid attribute name'); + } + if (!is_object($object)) { + throw \PHPUnit\Framework\InvalidArgumentException::create(2, 'object'); + } + static::assertThat($object, new ObjectHasAttribute($attributeName), $message); } -} -if (!function_exists('PHPUnit\\Framework\\assertXmlStringEqualsXmlString')) { /** - * Asserts that two XML documents are equal. + * Asserts that an object does not have a specified attribute. * - * @param DOMDocument|string $expectedXml - * @param DOMDocument|string $actualXml + * @param object $object * - * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws \PHPUnit\Util\Xml\Exception - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * @throws Exception + * @throws ExpectationFailedException * - * @see Assert::assertXmlStringEqualsXmlString + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4601 */ - function assertXmlStringEqualsXmlString($expectedXml, $actualXml, string $message = '') : void + public static function assertObjectNotHasAttribute(string $attributeName, $object, string $message = '') : void { - \PHPUnit\Framework\Assert::assertXmlStringEqualsXmlString(...func_get_args()); + self::createWarning('assertObjectNotHasAttribute() is deprecated and will be removed in PHPUnit 10. Refactor your test to use assertObjectNotHasProperty() instead.'); + if (!self::isValidObjectAttributeName($attributeName)) { + throw \PHPUnit\Framework\InvalidArgumentException::create(1, 'valid attribute name'); + } + if (!is_object($object)) { + throw \PHPUnit\Framework\InvalidArgumentException::create(2, 'object'); + } + static::assertThat($object, new LogicalNot(new ObjectHasAttribute($attributeName)), $message); } -} -if (!function_exists('PHPUnit\\Framework\\assertXmlStringNotEqualsXmlString')) { /** - * Asserts that two XML documents are not equal. + * Asserts that an object has a specified property. * - * @param DOMDocument|string $expectedXml - * @param DOMDocument|string $actualXml + * @throws ExpectationFailedException + */ + public static final function assertObjectHasProperty(string $propertyName, object $object, string $message = '') : void + { + static::assertThat($object, new ObjectHasProperty($propertyName), $message); + } + /** + * Asserts that an object does not have a specified property. * * @throws ExpectationFailedException + */ + public static final function assertObjectNotHasProperty(string $propertyName, object $object, string $message = '') : void + { + static::assertThat($object, new LogicalNot(new ObjectHasProperty($propertyName)), $message); + } + /** + * Asserts that two variables have the same type and value. + * Used on objects, it asserts that two variables reference + * the same object. + * * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws \PHPUnit\Util\Xml\Exception + * @throws ExpectationFailedException * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * @psalm-template ExpectedType * - * @see Assert::assertXmlStringNotEqualsXmlString + * @psalm-param ExpectedType $expected + * + * @psalm-assert =ExpectedType $actual */ - function assertXmlStringNotEqualsXmlString($expectedXml, $actualXml, string $message = '') : void + public static function assertSame($expected, $actual, string $message = '') : void { - \PHPUnit\Framework\Assert::assertXmlStringNotEqualsXmlString(...func_get_args()); + static::assertThat($actual, new IsIdentical($expected), $message); } -} -if (!function_exists('PHPUnit\\Framework\\assertEqualXMLStructure')) { /** - * Asserts that a hierarchy of DOMElements matches. + * 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 AssertionFailedError - * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException + */ + 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 is of a given type. * - * @codeCoverageIgnore + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws Exception + * @throws ExpectationFailedException * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4091 + * @psalm-template ExpectedType of object * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * @psalm-param class-string $expected * - * @see Assert::assertEqualXMLStructure + * @psalm-assert =ExpectedType $actual */ - function assertEqualXMLStructure(DOMElement $expectedElement, DOMElement $actualElement, bool $checkAttributes = \false, string $message = '') : void + public static function assertInstanceOf(string $expected, $actual, string $message = '') : void { - \PHPUnit\Framework\Assert::assertEqualXMLStructure(...func_get_args()); + if (!class_exists($expected) && !interface_exists($expected)) { + throw \PHPUnit\Framework\InvalidArgumentException::create(1, 'class or interface name'); + } + static::assertThat($actual, new IsInstanceOf($expected), $message); } -} -if (!function_exists('PHPUnit\\Framework\\assertThat')) { /** - * Evaluates a PHPUnit\Framework\Constraint matcher object. + * Asserts that a variable is not of a given type. * - * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws Exception + * @throws ExpectationFailedException * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * @psalm-template ExpectedType of object * - * @see Assert::assertThat + * @psalm-param class-string $expected + * + * @psalm-assert !ExpectedType $actual */ - function assertThat($value, Constraint $constraint, string $message = '') : void + public static function assertNotInstanceOf(string $expected, $actual, string $message = '') : void { - \PHPUnit\Framework\Assert::assertThat(...func_get_args()); + if (!class_exists($expected) && !interface_exists($expected)) { + throw \PHPUnit\Framework\InvalidArgumentException::create(1, 'class or interface name'); + } + static::assertThat($actual, new LogicalNot(new IsInstanceOf($expected)), $message); } -} -if (!function_exists('PHPUnit\\Framework\\assertJson')) { /** - * Asserts that a string is a valid JSON string. + * Asserts that a variable is of type array. * - * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertJson + * @psalm-assert array $actual */ - function assertJson(string $actualJson, string $message = '') : void + public static function assertIsArray($actual, string $message = '') : void { - \PHPUnit\Framework\Assert::assertJson(...func_get_args()); + static::assertThat($actual, new IsType(IsType::TYPE_ARRAY), $message); } -} -if (!function_exists('PHPUnit\\Framework\\assertJsonStringEqualsJsonString')) { /** - * Asserts that two given JSON encoded objects or arrays are equal. + * Asserts that a variable is of type bool. * - * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertJsonStringEqualsJsonString + * @psalm-assert bool $actual */ - function assertJsonStringEqualsJsonString(string $expectedJson, string $actualJson, string $message = '') : void + public static function assertIsBool($actual, string $message = '') : void { - \PHPUnit\Framework\Assert::assertJsonStringEqualsJsonString(...func_get_args()); + static::assertThat($actual, new IsType(IsType::TYPE_BOOL), $message); } -} -if (!function_exists('PHPUnit\\Framework\\assertJsonStringNotEqualsJsonString')) { /** - * Asserts that two given JSON encoded objects or arrays are not equal. - * - * @param string $expectedJson - * @param string $actualJson + * Asserts that a variable is of type float. * - * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertJsonStringNotEqualsJsonString + * @psalm-assert float $actual */ - function assertJsonStringNotEqualsJsonString($expectedJson, $actualJson, string $message = '') : void + public static function assertIsFloat($actual, string $message = '') : void { - \PHPUnit\Framework\Assert::assertJsonStringNotEqualsJsonString(...func_get_args()); + static::assertThat($actual, new IsType(IsType::TYPE_FLOAT), $message); } -} -if (!function_exists('PHPUnit\\Framework\\assertJsonStringEqualsJsonFile')) { /** - * Asserts that the generated JSON encoded object and the content of the given file are equal. + * Asserts that a variable is of type int. * - * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertJsonStringEqualsJsonFile + * @psalm-assert int $actual */ - function assertJsonStringEqualsJsonFile(string $expectedFile, string $actualJson, string $message = '') : void + public static function assertIsInt($actual, string $message = '') : void { - \PHPUnit\Framework\Assert::assertJsonStringEqualsJsonFile(...func_get_args()); + static::assertThat($actual, new IsType(IsType::TYPE_INT), $message); } -} -if (!function_exists('PHPUnit\\Framework\\assertJsonStringNotEqualsJsonFile')) { /** - * Asserts that the generated JSON encoded object and the content of the given file are not equal. + * Asserts that a variable is of type numeric. * - * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertJsonStringNotEqualsJsonFile + * @psalm-assert numeric $actual */ - function assertJsonStringNotEqualsJsonFile(string $expectedFile, string $actualJson, string $message = '') : void + public static function assertIsNumeric($actual, string $message = '') : void { - \PHPUnit\Framework\Assert::assertJsonStringNotEqualsJsonFile(...func_get_args()); + static::assertThat($actual, new IsType(IsType::TYPE_NUMERIC), $message); } -} -if (!function_exists('PHPUnit\\Framework\\assertJsonFileEqualsJsonFile')) { /** - * Asserts that two JSON files are equal. + * Asserts that a variable is of type object. * - * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertJsonFileEqualsJsonFile + * @psalm-assert object $actual */ - function assertJsonFileEqualsJsonFile(string $expectedFile, string $actualFile, string $message = '') : void + public static function assertIsObject($actual, string $message = '') : void { - \PHPUnit\Framework\Assert::assertJsonFileEqualsJsonFile(...func_get_args()); + static::assertThat($actual, new IsType(IsType::TYPE_OBJECT), $message); } -} -if (!function_exists('PHPUnit\\Framework\\assertJsonFileNotEqualsJsonFile')) { /** - * Asserts that two JSON files are not equal. + * Asserts that a variable is of type resource. * - * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertJsonFileNotEqualsJsonFile + * @psalm-assert resource $actual */ - function assertJsonFileNotEqualsJsonFile(string $expectedFile, string $actualFile, string $message = '') : void + public static function assertIsResource($actual, string $message = '') : void { - \PHPUnit\Framework\Assert::assertJsonFileNotEqualsJsonFile(...func_get_args()); + static::assertThat($actual, new IsType(IsType::TYPE_RESOURCE), $message); } -} -if (!function_exists('PHPUnit\\Framework\\logicalAnd')) { - function logicalAnd() : LogicalAnd + /** + * Asserts that a variable is of type resource and is closed. + * + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException + * + * @psalm-assert resource $actual + */ + public static function assertIsClosedResource($actual, string $message = '') : void { - return \PHPUnit\Framework\Assert::logicalAnd(...func_get_args()); + static::assertThat($actual, new IsType(IsType::TYPE_CLOSED_RESOURCE), $message); } -} -if (!function_exists('PHPUnit\\Framework\\logicalOr')) { - function logicalOr() : LogicalOr + /** + * Asserts that a variable is of type string. + * + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException + * + * @psalm-assert string $actual + */ + public static function assertIsString($actual, string $message = '') : void { - return \PHPUnit\Framework\Assert::logicalOr(...func_get_args()); + static::assertThat($actual, new IsType(IsType::TYPE_STRING), $message); } -} -if (!function_exists('PHPUnit\\Framework\\logicalNot')) { - function logicalNot(Constraint $constraint) : LogicalNot + /** + * Asserts that a variable is of type scalar. + * + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException + * + * @psalm-assert scalar $actual + */ + public static function assertIsScalar($actual, string $message = '') : void { - return \PHPUnit\Framework\Assert::logicalNot(...func_get_args()); + static::assertThat($actual, new IsType(IsType::TYPE_SCALAR), $message); } -} -if (!function_exists('PHPUnit\\Framework\\logicalXor')) { - function logicalXor() : LogicalXor + /** + * Asserts that a variable is of type callable. + * + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException + * + * @psalm-assert callable $actual + */ + public static function assertIsCallable($actual, string $message = '') : void { - return \PHPUnit\Framework\Assert::logicalXor(...func_get_args()); + static::assertThat($actual, new IsType(IsType::TYPE_CALLABLE), $message); } -} -if (!function_exists('PHPUnit\\Framework\\anything')) { - function anything() : IsAnything + /** + * Asserts that a variable is of type iterable. + * + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException + * + * @psalm-assert iterable $actual + */ + public static function assertIsIterable($actual, string $message = '') : void { - return \PHPUnit\Framework\Assert::anything(...func_get_args()); + static::assertThat($actual, new IsType(IsType::TYPE_ITERABLE), $message); } -} -if (!function_exists('PHPUnit\\Framework\\isTrue')) { - function isTrue() : IsTrue - { - return \PHPUnit\Framework\Assert::isTrue(...func_get_args()); - } -} -if (!function_exists('PHPUnit\\Framework\\callback')) { - function callback(callable $callback) : Callback - { - return \PHPUnit\Framework\Assert::callback(...func_get_args()); - } -} -if (!function_exists('PHPUnit\\Framework\\isFalse')) { - function isFalse() : IsFalse - { - return \PHPUnit\Framework\Assert::isFalse(...func_get_args()); - } -} -if (!function_exists('PHPUnit\\Framework\\isJson')) { - function isJson() : IsJson - { - return \PHPUnit\Framework\Assert::isJson(...func_get_args()); - } -} -if (!function_exists('PHPUnit\\Framework\\isNull')) { - function isNull() : IsNull - { - return \PHPUnit\Framework\Assert::isNull(...func_get_args()); - } -} -if (!function_exists('PHPUnit\\Framework\\isFinite')) { - function isFinite() : IsFinite - { - return \PHPUnit\Framework\Assert::isFinite(...func_get_args()); - } -} -if (!function_exists('PHPUnit\\Framework\\isInfinite')) { - function isInfinite() : IsInfinite - { - return \PHPUnit\Framework\Assert::isInfinite(...func_get_args()); - } -} -if (!function_exists('PHPUnit\\Framework\\isNan')) { - function isNan() : IsNan - { - return \PHPUnit\Framework\Assert::isNan(...func_get_args()); - } -} -if (!function_exists('PHPUnit\\Framework\\containsEqual')) { - function containsEqual($value) : TraversableContainsEqual - { - return \PHPUnit\Framework\Assert::containsEqual(...func_get_args()); - } -} -if (!function_exists('PHPUnit\\Framework\\containsIdentical')) { - function containsIdentical($value) : TraversableContainsIdentical - { - return \PHPUnit\Framework\Assert::containsIdentical(...func_get_args()); - } -} -if (!function_exists('PHPUnit\\Framework\\containsOnly')) { - function containsOnly(string $type) : TraversableContainsOnly - { - return \PHPUnit\Framework\Assert::containsOnly(...func_get_args()); - } -} -if (!function_exists('PHPUnit\\Framework\\containsOnlyInstancesOf')) { - function containsOnlyInstancesOf(string $className) : TraversableContainsOnly - { - return \PHPUnit\Framework\Assert::containsOnlyInstancesOf(...func_get_args()); - } -} -if (!function_exists('PHPUnit\\Framework\\arrayHasKey')) { - function arrayHasKey($key) : ArrayHasKey - { - return \PHPUnit\Framework\Assert::arrayHasKey(...func_get_args()); - } -} -if (!function_exists('PHPUnit\\Framework\\equalTo')) { - function equalTo($value) : IsEqual - { - return \PHPUnit\Framework\Assert::equalTo(...func_get_args()); - } -} -if (!function_exists('PHPUnit\\Framework\\equalToCanonicalizing')) { - function equalToCanonicalizing($value) : IsEqualCanonicalizing - { - return \PHPUnit\Framework\Assert::equalToCanonicalizing(...func_get_args()); - } -} -if (!function_exists('PHPUnit\\Framework\\equalToIgnoringCase')) { - function equalToIgnoringCase($value) : IsEqualIgnoringCase - { - return \PHPUnit\Framework\Assert::equalToIgnoringCase(...func_get_args()); - } -} -if (!function_exists('PHPUnit\\Framework\\equalToWithDelta')) { - function equalToWithDelta($value, float $delta) : IsEqualWithDelta - { - return \PHPUnit\Framework\Assert::equalToWithDelta(...func_get_args()); - } -} -if (!function_exists('PHPUnit\\Framework\\isEmpty')) { - function isEmpty() : IsEmpty - { - return \PHPUnit\Framework\Assert::isEmpty(...func_get_args()); - } -} -if (!function_exists('PHPUnit\\Framework\\isWritable')) { - function isWritable() : IsWritable - { - return \PHPUnit\Framework\Assert::isWritable(...func_get_args()); - } -} -if (!function_exists('PHPUnit\\Framework\\isReadable')) { - function isReadable() : IsReadable + /** + * Asserts that a variable is not of type array. + * + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException + * + * @psalm-assert !array $actual + */ + public static function assertIsNotArray($actual, string $message = '') : void { - return \PHPUnit\Framework\Assert::isReadable(...func_get_args()); + static::assertThat($actual, new LogicalNot(new IsType(IsType::TYPE_ARRAY)), $message); } -} -if (!function_exists('PHPUnit\\Framework\\directoryExists')) { - function directoryExists() : DirectoryExists + /** + * Asserts that a variable is not of type bool. + * + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException + * + * @psalm-assert !bool $actual + */ + public static function assertIsNotBool($actual, string $message = '') : void { - return \PHPUnit\Framework\Assert::directoryExists(...func_get_args()); + static::assertThat($actual, new LogicalNot(new IsType(IsType::TYPE_BOOL)), $message); } -} -if (!function_exists('PHPUnit\\Framework\\fileExists')) { - function fileExists() : FileExists + /** + * Asserts that a variable is not of type float. + * + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException + * + * @psalm-assert !float $actual + */ + public static function assertIsNotFloat($actual, string $message = '') : void { - return \PHPUnit\Framework\Assert::fileExists(...func_get_args()); + static::assertThat($actual, new LogicalNot(new IsType(IsType::TYPE_FLOAT)), $message); } -} -if (!function_exists('PHPUnit\\Framework\\greaterThan')) { - function greaterThan($value) : GreaterThan + /** + * Asserts that a variable is not of type int. + * + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException + * + * @psalm-assert !int $actual + */ + public static function assertIsNotInt($actual, string $message = '') : void { - return \PHPUnit\Framework\Assert::greaterThan(...func_get_args()); + static::assertThat($actual, new LogicalNot(new IsType(IsType::TYPE_INT)), $message); } -} -if (!function_exists('PHPUnit\\Framework\\greaterThanOrEqual')) { - function greaterThanOrEqual($value) : LogicalOr + /** + * Asserts that a variable is not of type numeric. + * + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException + * + * @psalm-assert !numeric $actual + */ + public static function assertIsNotNumeric($actual, string $message = '') : void { - return \PHPUnit\Framework\Assert::greaterThanOrEqual(...func_get_args()); + static::assertThat($actual, new LogicalNot(new IsType(IsType::TYPE_NUMERIC)), $message); } -} -if (!function_exists('PHPUnit\\Framework\\classHasAttribute')) { - function classHasAttribute(string $attributeName) : ClassHasAttribute + /** + * Asserts that a variable is not of type object. + * + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException + * + * @psalm-assert !object $actual + */ + public static function assertIsNotObject($actual, string $message = '') : void { - return \PHPUnit\Framework\Assert::classHasAttribute(...func_get_args()); + static::assertThat($actual, new LogicalNot(new IsType(IsType::TYPE_OBJECT)), $message); } -} -if (!function_exists('PHPUnit\\Framework\\classHasStaticAttribute')) { - function classHasStaticAttribute(string $attributeName) : ClassHasStaticAttribute + /** + * Asserts that a variable is not of type resource. + * + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException + * + * @psalm-assert !resource $actual + */ + public static function assertIsNotResource($actual, string $message = '') : void { - return \PHPUnit\Framework\Assert::classHasStaticAttribute(...func_get_args()); + static::assertThat($actual, new LogicalNot(new IsType(IsType::TYPE_RESOURCE)), $message); } -} -if (!function_exists('PHPUnit\\Framework\\objectHasAttribute')) { - function objectHasAttribute($attributeName) : ObjectHasAttribute + /** + * Asserts that a variable is not of type resource. + * + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException + * + * @psalm-assert !resource $actual + */ + public static function assertIsNotClosedResource($actual, string $message = '') : void { - return \PHPUnit\Framework\Assert::objectHasAttribute(...func_get_args()); + static::assertThat($actual, new LogicalNot(new IsType(IsType::TYPE_CLOSED_RESOURCE)), $message); } -} -if (!function_exists('PHPUnit\\Framework\\identicalTo')) { - function identicalTo($value) : IsIdentical + /** + * Asserts that a variable is not of type string. + * + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException + * + * @psalm-assert !string $actual + */ + public static function assertIsNotString($actual, string $message = '') : void { - return \PHPUnit\Framework\Assert::identicalTo(...func_get_args()); + static::assertThat($actual, new LogicalNot(new IsType(IsType::TYPE_STRING)), $message); } -} -if (!function_exists('PHPUnit\\Framework\\isInstanceOf')) { - function isInstanceOf(string $className) : IsInstanceOf + /** + * Asserts that a variable is not of type scalar. + * + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException + * + * @psalm-assert !scalar $actual + */ + public static function assertIsNotScalar($actual, string $message = '') : void { - return \PHPUnit\Framework\Assert::isInstanceOf(...func_get_args()); + static::assertThat($actual, new LogicalNot(new IsType(IsType::TYPE_SCALAR)), $message); } -} -if (!function_exists('PHPUnit\\Framework\\isType')) { - function isType(string $type) : IsType + /** + * Asserts that a variable is not of type callable. + * + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException + * + * @psalm-assert !callable $actual + */ + public static function assertIsNotCallable($actual, string $message = '') : void { - return \PHPUnit\Framework\Assert::isType(...func_get_args()); + static::assertThat($actual, new LogicalNot(new IsType(IsType::TYPE_CALLABLE)), $message); } -} -if (!function_exists('PHPUnit\\Framework\\lessThan')) { - function lessThan($value) : LessThan + /** + * Asserts that a variable is not of type iterable. + * + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException + * + * @psalm-assert !iterable $actual + */ + public static function assertIsNotIterable($actual, string $message = '') : void { - return \PHPUnit\Framework\Assert::lessThan(...func_get_args()); + static::assertThat($actual, new LogicalNot(new IsType(IsType::TYPE_ITERABLE)), $message); } -} -if (!function_exists('PHPUnit\\Framework\\lessThanOrEqual')) { - function lessThanOrEqual($value) : LogicalOr + /** + * Asserts that a string matches a given regular expression. + * + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException + */ + public static function assertMatchesRegularExpression(string $pattern, string $string, string $message = '') : void { - return \PHPUnit\Framework\Assert::lessThanOrEqual(...func_get_args()); + static::assertThat($string, new RegularExpression($pattern), $message); } -} -if (!function_exists('PHPUnit\\Framework\\matchesRegularExpression')) { - function matchesRegularExpression(string $pattern) : RegularExpression + /** + * Asserts that a string matches a given regular expression. + * + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException + * + * @codeCoverageIgnore + * + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4086 + */ + public static function assertRegExp(string $pattern, string $string, string $message = '') : void { - return \PHPUnit\Framework\Assert::matchesRegularExpression(...func_get_args()); + self::createWarning('assertRegExp() is deprecated and will be removed in PHPUnit 10. Refactor your code to use assertMatchesRegularExpression() instead.'); + static::assertThat($string, new RegularExpression($pattern), $message); } -} -if (!function_exists('PHPUnit\\Framework\\matches')) { - function matches(string $string) : StringMatchesFormatDescription + /** + * Asserts that a string does not match a given regular expression. + * + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException + */ + public static function assertDoesNotMatchRegularExpression(string $pattern, string $string, string $message = '') : void { - return \PHPUnit\Framework\Assert::matches(...func_get_args()); + static::assertThat($string, new LogicalNot(new RegularExpression($pattern)), $message); } -} -if (!function_exists('PHPUnit\\Framework\\stringStartsWith')) { - function stringStartsWith($prefix) : StringStartsWith + /** + * Asserts that a string does not match a given regular expression. + * + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException + * + * @codeCoverageIgnore + * + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4089 + */ + public static function assertNotRegExp(string $pattern, string $string, string $message = '') : void { - return \PHPUnit\Framework\Assert::stringStartsWith(...func_get_args()); + self::createWarning('assertNotRegExp() is deprecated and will be removed in PHPUnit 10. Refactor your code to use assertDoesNotMatchRegularExpression() instead.'); + static::assertThat($string, new LogicalNot(new RegularExpression($pattern)), $message); } -} -if (!function_exists('PHPUnit\\Framework\\stringContains')) { - function stringContains(string $string, bool $case = \true) : StringContains + /** + * Assert that the size of two arrays (or `Countable` or `Traversable` objects) + * is the same. + * + * @param Countable|iterable $expected + * @param Countable|iterable $actual + * + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws Exception + * @throws ExpectationFailedException + */ + public static function assertSameSize($expected, $actual, string $message = '') : void { - return \PHPUnit\Framework\Assert::stringContains(...func_get_args()); + if ($expected instanceof Generator) { + self::createWarning('Passing an argument of type Generator for the $expected parameter is deprecated. Support for this will be removed in PHPUnit 10.'); + } + if ($actual instanceof Generator) { + self::createWarning('Passing an argument of type Generator for the $actual parameter is deprecated. Support for this will be removed in PHPUnit 10.'); + } + if (!$expected instanceof Countable && !is_iterable($expected)) { + throw \PHPUnit\Framework\InvalidArgumentException::create(1, 'countable or iterable'); + } + if (!$actual instanceof Countable && !is_iterable($actual)) { + throw \PHPUnit\Framework\InvalidArgumentException::create(2, 'countable or iterable'); + } + static::assertThat($actual, new SameSize($expected), $message); } -} -if (!function_exists('PHPUnit\\Framework\\stringEndsWith')) { - function stringEndsWith(string $suffix) : StringEndsWith + /** + * 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 \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws Exception + * @throws ExpectationFailedException + */ + public static function assertNotSameSize($expected, $actual, string $message = '') : void { - return \PHPUnit\Framework\Assert::stringEndsWith(...func_get_args()); + if ($expected instanceof Generator) { + self::createWarning('Passing an argument of type Generator for the $expected parameter is deprecated. Support for this will be removed in PHPUnit 10.'); + } + if ($actual instanceof Generator) { + self::createWarning('Passing an argument of type Generator for the $actual parameter is deprecated. Support for this will be removed in PHPUnit 10.'); + } + if (!$expected instanceof Countable && !is_iterable($expected)) { + throw \PHPUnit\Framework\InvalidArgumentException::create(1, 'countable or iterable'); + } + if (!$actual instanceof Countable && !is_iterable($actual)) { + throw \PHPUnit\Framework\InvalidArgumentException::create(2, 'countable or iterable'); + } + static::assertThat($actual, new LogicalNot(new SameSize($expected)), $message); } -} -if (!function_exists('PHPUnit\\Framework\\countOf')) { - function countOf(int $count) : Count + /** + * Asserts that a string matches a given format string. + * + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException + */ + public static function assertStringMatchesFormat(string $format, string $string, string $message = '') : void { - return \PHPUnit\Framework\Assert::countOf(...func_get_args()); + static::assertThat($string, new StringMatchesFormatDescription($format), $message); } -} -if (!function_exists('PHPUnit\\Framework\\objectEquals')) { - function objectEquals(object $object, string $method = 'equals') : ObjectEquals + /** + * Asserts that a string does not match a given format string. + * + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException + */ + public static function assertStringNotMatchesFormat(string $format, string $string, string $message = '') : void { - return \PHPUnit\Framework\Assert::objectEquals(...func_get_args()); + static::assertThat($string, new LogicalNot(new StringMatchesFormatDescription($format)), $message); } -} -if (!function_exists('PHPUnit\\Framework\\any')) { /** - * Returns a matcher that matches when the method is executed - * zero or more times. + * Asserts that a string matches a given format file. + * + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException */ - function any() : AnyInvokedCountMatcher + public static function assertStringMatchesFormatFile(string $formatFile, string $string, string $message = '') : void { - return new AnyInvokedCountMatcher(); + static::assertFileExists($formatFile, $message); + static::assertThat($string, new StringMatchesFormatDescription(file_get_contents($formatFile)), $message); } -} -if (!function_exists('PHPUnit\\Framework\\never')) { /** - * Returns a matcher that matches when the method is never executed. + * Asserts that a string does not match a given format string. + * + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException */ - function never() : InvokedCountMatcher + public static function assertStringNotMatchesFormatFile(string $formatFile, string $string, string $message = '') : void { - return new InvokedCountMatcher(0); + static::assertFileExists($formatFile, $message); + static::assertThat($string, new LogicalNot(new StringMatchesFormatDescription(file_get_contents($formatFile))), $message); } -} -if (!function_exists('PHPUnit\\Framework\\atLeast')) { /** - * Returns a matcher that matches when the method is executed - * at least N times. + * Asserts that a string starts with a given prefix. + * + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException */ - function atLeast(int $requiredInvocations) : InvokedAtLeastCountMatcher + public static function assertStringStartsWith(string $prefix, string $string, string $message = '') : void { - return new InvokedAtLeastCountMatcher($requiredInvocations); + static::assertThat($string, new StringStartsWith($prefix), $message); } -} -if (!function_exists('PHPUnit\\Framework\\atLeastOnce')) { /** - * Returns a matcher that matches when the method is executed at least once. + * Asserts that a string starts not with a given prefix. + * + * @param string $prefix + * @param string $string + * + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException */ - function atLeastOnce() : InvokedAtLeastOnceMatcher + public static function assertStringStartsNotWith($prefix, $string, string $message = '') : void { - return new InvokedAtLeastOnceMatcher(); + static::assertThat($string, new LogicalNot(new StringStartsWith($prefix)), $message); } -} -if (!function_exists('PHPUnit\\Framework\\once')) { /** - * Returns a matcher that matches when the method is executed exactly once. + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException */ - function once() : InvokedCountMatcher + public static function assertStringContainsString(string $needle, string $haystack, string $message = '') : void { - return new InvokedCountMatcher(1); + $constraint = new StringContains($needle, \false); + static::assertThat($haystack, $constraint, $message); } -} -if (!function_exists('PHPUnit\\Framework\\exactly')) { /** - * Returns a matcher that matches when the method is executed - * exactly $count times. + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException */ - function exactly(int $count) : InvokedCountMatcher + public static function assertStringContainsStringIgnoringCase(string $needle, string $haystack, string $message = '') : void { - return new InvokedCountMatcher($count); + $constraint = new StringContains($needle, \true); + static::assertThat($haystack, $constraint, $message); } -} -if (!function_exists('PHPUnit\\Framework\\atMost')) { /** - * Returns a matcher that matches when the method is executed - * at most N times. + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException */ - function atMost(int $allowedInvocations) : InvokedAtMostCountMatcher + public static function assertStringNotContainsString(string $needle, string $haystack, string $message = '') : void { - return new InvokedAtMostCountMatcher($allowedInvocations); + $constraint = new LogicalNot(new StringContains($needle)); + static::assertThat($haystack, $constraint, $message); } -} -if (!function_exists('PHPUnit\\Framework\\at')) { /** - * Returns a matcher that matches when the method is executed - * at the given index. + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException */ - function at(int $index) : InvokedAtIndexMatcher + public static function assertStringNotContainsStringIgnoringCase(string $needle, string $haystack, string $message = '') : void { - return new InvokedAtIndexMatcher($index); - } -} -if (!function_exists('PHPUnit\\Framework\\returnValue')) { - function returnValue($value) : ReturnStub - { - return new ReturnStub($value); - } -} -if (!function_exists('PHPUnit\\Framework\\returnValueMap')) { - function returnValueMap(array $valueMap) : ReturnValueMapStub - { - return new ReturnValueMapStub($valueMap); - } -} -if (!function_exists('PHPUnit\\Framework\\returnArgument')) { - function returnArgument(int $argumentIndex) : ReturnArgumentStub - { - return new ReturnArgumentStub($argumentIndex); - } -} -if (!function_exists('PHPUnit\\Framework\\returnCallback')) { - function returnCallback($callback) : ReturnCallbackStub - { - return new ReturnCallbackStub($callback); + $constraint = new LogicalNot(new StringContains($needle, \true)); + static::assertThat($haystack, $constraint, $message); } -} -if (!function_exists('PHPUnit\\Framework\\returnSelf')) { /** - * Returns the current object. + * Asserts that a string ends with a given suffix. * - * This method is useful when mocking a fluent interface. - */ - function returnSelf() : ReturnSelfStub - { - return new ReturnSelfStub(); - } -} -if (!function_exists('PHPUnit\\Framework\\throwException')) { - function throwException(Throwable $exception) : ExceptionStub - { - return new ExceptionStub($exception); - } -} -if (!function_exists('PHPUnit\\Framework\\onConsecutiveCalls')) { - function onConsecutiveCalls() : ConsecutiveCallsStub - { - $args = func_get_args(); - return new ConsecutiveCallsStub($args); - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -final class IsFalse extends \PHPUnit\Framework\Constraint\Constraint -{ - /** - * Returns a string representation of the constraint. + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException */ - public function toString() : string + public static function assertStringEndsWith(string $suffix, string $string, string $message = '') : void { - return 'is false'; + static::assertThat($string, new StringEndsWith($suffix), $message); } /** - * Evaluates the constraint for parameter $other. Returns true if the - * constraint is met, false otherwise. + * Asserts that a string ends not with a given suffix. * - * @param mixed $other value or object to evaluate - */ - protected function matches($other) : bool - { - return $other === \false; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -final class IsTrue extends \PHPUnit\Framework\Constraint\Constraint -{ - /** - * Returns a string representation of the constraint. + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException */ - public function toString() : string + public static function assertStringEndsNotWith(string $suffix, string $string, string $message = '') : void { - return 'is true'; + static::assertThat($string, new LogicalNot(new StringEndsWith($suffix)), $message); } /** - * Evaluates the constraint for parameter $other. Returns true if the - * constraint is met, false otherwise. + * Asserts that two XML files are equal. * - * @param mixed $other value or object to evaluate + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws Exception + * @throws ExpectationFailedException */ - protected function matches($other) : bool + public static function assertXmlFileEqualsXmlFile(string $expectedFile, string $actualFile, string $message = '') : void { - return $other === \true; + $expected = (new XmlLoader())->loadFile($expectedFile); + $actual = (new XmlLoader())->loadFile($actualFile); + static::assertEquals($expected, $actual, $message); } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -/** - * @psalm-template CallbackInput of mixed - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -final class Callback extends \PHPUnit\Framework\Constraint\Constraint -{ /** - * @var callable + * Asserts that two XML files are not equal. * - * @psalm-var callable(CallbackInput $input): bool + * @throws \PHPUnit\Util\Exception + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException */ - private $callback; - /** @psalm-param callable(CallbackInput $input): bool $callback */ - public function __construct(callable $callback) + public static function assertXmlFileNotEqualsXmlFile(string $expectedFile, string $actualFile, string $message = '') : void { - $this->callback = $callback; + $expected = (new XmlLoader())->loadFile($expectedFile); + $actual = (new XmlLoader())->loadFile($actualFile); + static::assertNotEquals($expected, $actual, $message); } /** - * Returns a string representation of the constraint. + * Asserts that two XML documents are equal. + * + * @param DOMDocument|string $actualXml + * + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException + * @throws Xml\Exception */ - public function toString() : string + public static function assertXmlStringEqualsXmlFile(string $expectedFile, $actualXml, string $message = '') : void { - return 'is accepted by specified callback'; + if (!is_string($actualXml)) { + self::createWarning('Passing an argument of type DOMDocument for the $actualXml parameter is deprecated. Support for this will be removed in PHPUnit 10.'); + $actual = $actualXml; + } else { + $actual = (new XmlLoader())->load($actualXml); + } + $expected = (new XmlLoader())->loadFile($expectedFile); + static::assertEquals($expected, $actual, $message); } /** - * Evaluates the constraint for parameter $value. Returns true if the - * constraint is met, false otherwise. + * Asserts that two XML documents are not equal. * - * @param mixed $other value or object to evaluate + * @param DOMDocument|string $actualXml * - * @psalm-param CallbackInput $other + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException + * @throws Xml\Exception */ - protected function matches($other) : bool + public static function assertXmlStringNotEqualsXmlFile(string $expectedFile, $actualXml, string $message = '') : void { - return ($this->callback)($other); + if (!is_string($actualXml)) { + self::createWarning('Passing an argument of type DOMDocument for the $actualXml parameter is deprecated. Support for this will be removed in PHPUnit 10.'); + $actual = $actualXml; + } else { + $actual = (new XmlLoader())->load($actualXml); + } + $expected = (new XmlLoader())->loadFile($expectedFile); + static::assertNotEquals($expected, $actual, $message); } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -use function count; -use function is_array; -use function iterator_count; -use function sprintf; -use Countable; -use EmptyIterator; -use Generator; -use Iterator; -use IteratorAggregate; -use PHPUnit\Framework\Exception; -use Traversable; -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -class Count extends \PHPUnit\Framework\Constraint\Constraint -{ /** - * @var int + * Asserts that two XML documents are equal. + * + * @param DOMDocument|string $expectedXml + * @param DOMDocument|string $actualXml + * + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException + * @throws Xml\Exception */ - private $expectedCount; - public function __construct(int $expected) - { - $this->expectedCount = $expected; - } - public function toString() : string + public static function assertXmlStringEqualsXmlString($expectedXml, $actualXml, string $message = '') : void { - return sprintf('count matches %d', $this->expectedCount); + if (!is_string($expectedXml)) { + self::createWarning('Passing an argument of type DOMDocument for the $expectedXml parameter is deprecated. Support for this will be removed in PHPUnit 10.'); + $expected = $expectedXml; + } else { + $expected = (new XmlLoader())->load($expectedXml); + } + if (!is_string($actualXml)) { + self::createWarning('Passing an argument of type DOMDocument for the $actualXml parameter is deprecated. Support for this will be removed in PHPUnit 10.'); + $actual = $actualXml; + } else { + $actual = (new XmlLoader())->load($actualXml); + } + static::assertEquals($expected, $actual, $message); } /** - * Evaluates the constraint for parameter $other. Returns true if the - * constraint is met, false otherwise. + * Asserts that two XML documents are not equal. * - * @throws Exception + * @param DOMDocument|string $expectedXml + * @param DOMDocument|string $actualXml + * + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException + * @throws Xml\Exception */ - protected function matches($other) : bool + public static function assertXmlStringNotEqualsXmlString($expectedXml, $actualXml, string $message = '') : void { - return $this->expectedCount === $this->getCountOf($other); + if (!is_string($expectedXml)) { + self::createWarning('Passing an argument of type DOMDocument for the $expectedXml parameter is deprecated. Support for this will be removed in PHPUnit 10.'); + $expected = $expectedXml; + } else { + $expected = (new XmlLoader())->load($expectedXml); + } + if (!is_string($actualXml)) { + self::createWarning('Passing an argument of type DOMDocument for the $actualXml parameter is deprecated. Support for this will be removed in PHPUnit 10.'); + $actual = $actualXml; + } else { + $actual = (new XmlLoader())->load($actualXml); + } + static::assertNotEquals($expected, $actual, $message); } /** - * @throws Exception + * Asserts that a hierarchy of DOMElements matches. + * + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws AssertionFailedError + * @throws ExpectationFailedException + * + * @codeCoverageIgnore + * + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4091 */ - protected function getCountOf($other) : ?int + public static function assertEqualXMLStructure(DOMElement $expectedElement, DOMElement $actualElement, bool $checkAttributes = \false, string $message = '') : void { - if ($other instanceof Countable || is_array($other)) { - return count($other); - } - if ($other instanceof EmptyIterator) { - return 0; - } - if ($other instanceof Traversable) { - while ($other instanceof IteratorAggregate) { - try { - $other = $other->getIterator(); - } catch (\Exception $e) { - throw new Exception($e->getMessage(), $e->getCode(), $e); - } - } - $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(); + self::createWarning('assertEqualXMLStructure() is deprecated and will be removed in PHPUnit 10.'); + $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)); } } - return $count; } - return null; + 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); + } } /** - * Returns the total number of iterations from a generator. - * This will fully exhaust the generator. + * Evaluates a PHPUnit\Framework\Constraint matcher object. + * + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException */ - protected function getCountOfGenerator(Generator $generator) : int + public static function assertThat($value, Constraint $constraint, string $message = '') : void { - for ($count = 0; $generator->valid(); $generator->next()) { - $count++; - } - return $count; + self::$count += count($constraint); + $constraint->evaluate($value, $message); } /** - * 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. + * Asserts that a string is a valid JSON string. * - * @param mixed $other evaluated value or object + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException */ - protected function failureDescription($other) : string + public static function assertJson(string $actualJson, string $message = '') : void { - return sprintf('actual size %d matches expected size %d', (int) $this->getCountOf($other), $this->expectedCount); + static::assertThat($actualJson, static::isJson(), $message); } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -final class GreaterThan extends \PHPUnit\Framework\Constraint\Constraint -{ - /** - * @var float|int - */ - private $value; /** - * @param float|int $value + * Asserts that two given JSON encoded objects or arrays are equal. + * + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException */ - public function __construct($value) + public static function assertJsonStringEqualsJsonString(string $expectedJson, string $actualJson, string $message = '') : void { - $this->value = $value; + static::assertJson($expectedJson, $message); + static::assertJson($actualJson, $message); + static::assertThat($actualJson, new JsonMatches($expectedJson), $message); } /** - * Returns a string representation of the constraint. + * Asserts that two given JSON encoded objects or arrays are not equal. + * + * @param string $expectedJson + * @param string $actualJson * * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException */ - public function toString() : string + public static function assertJsonStringNotEqualsJsonString($expectedJson, $actualJson, string $message = '') : void { - return 'is greater than ' . $this->exporter()->export($this->value); + static::assertJson($expectedJson, $message); + static::assertJson($actualJson, $message); + static::assertThat($actualJson, new LogicalNot(new JsonMatches($expectedJson)), $message); } /** - * Evaluates the constraint for parameter $other. Returns true if the - * constraint is met, false otherwise. + * Asserts that the generated JSON encoded object and the content of the given file are equal. * - * @param mixed $other value or object to evaluate + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException */ - protected function matches($other) : bool + public static function assertJsonStringEqualsJsonFile(string $expectedFile, string $actualJson, string $message = '') : void { - return $this->value < $other; + static::assertFileExists($expectedFile, $message); + $expectedJson = file_get_contents($expectedFile); + static::assertJson($expectedJson, $message); + static::assertJson($actualJson, $message); + static::assertThat($actualJson, new JsonMatches($expectedJson), $message); } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -use function count; -use function gettype; -use function sprintf; -use function strpos; -use Countable; -use EmptyIterator; -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -final class IsEmpty extends \PHPUnit\Framework\Constraint\Constraint -{ /** - * Returns a string representation of the constraint. + * Asserts that the generated JSON encoded object and the content of the given file are not equal. + * + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException */ - public function toString() : string + public static function assertJsonStringNotEqualsJsonFile(string $expectedFile, string $actualJson, string $message = '') : void { - return 'is empty'; + 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); } /** - * Evaluates the constraint for parameter $other. Returns true if the - * constraint is met, false otherwise. + * Asserts that two JSON files are equal. * - * @param mixed $other value or object to evaluate + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException */ - protected function matches($other) : bool + public static function assertJsonFileEqualsJsonFile(string $expectedFile, string $actualFile, string $message = '') : void { - if ($other instanceof EmptyIterator) { - return \true; - } - if ($other instanceof Countable) { - return count($other) === 0; - } - return empty($other); + 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); } /** - * 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. + * Asserts that two JSON files are not equal. * - * @param mixed $other evaluated value or object + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException */ - protected function failureDescription($other) : string + public static function assertJsonFileNotEqualsJsonFile(string $expectedFile, string $actualFile, string $message = '') : void { - $type = gettype($other); - return sprintf('%s %s %s', strpos($type, 'a') === 0 || strpos($type, 'o') === 0 ? 'an' : 'a', $type, $this->toString()); + 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); } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -final class LessThan extends \PHPUnit\Framework\Constraint\Constraint -{ - /** - * @var float|int - */ - private $value; /** - * @param float|int $value + * @throws Exception */ - public function __construct($value) + public static function logicalAnd() : LogicalAnd { - $this->value = $value; + $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(); } /** - * Returns a string representation of the constraint. + * @psalm-template CallbackInput of mixed * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @psalm-param callable(CallbackInput $callback): bool $callback + * + * @psalm-return Callback */ - public function toString() : string + public static function callback(callable $callback) : Callback { - return 'is less than ' . $this->exporter()->export($this->value); + 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(); + } + public static function containsEqual($value) : TraversableContainsEqual + { + return new TraversableContainsEqual($value); + } + public static function containsIdentical($value) : TraversableContainsIdentical + { + return new TraversableContainsIdentical($value); + } + public static function containsOnly(string $type) : TraversableContainsOnly + { + return new TraversableContainsOnly($type); + } + public static function containsOnlyInstancesOf(string $className) : TraversableContainsOnly + { + return new TraversableContainsOnly($className, \false); } /** - * Evaluates the constraint for parameter $other. Returns true if the - * constraint is met, false otherwise. - * - * @param mixed $other value or object to evaluate + * @param int|string $key */ - protected function matches($other) : bool + public static function arrayHasKey($key) : ArrayHasKey { - return $this->value > $other; + return new ArrayHasKey($key); } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -final class SameSize extends \PHPUnit\Framework\Constraint\Count -{ - public function __construct(iterable $expected) + public static function equalTo($value) : IsEqual { - parent::__construct((int) $this->getCountOf($expected)); + return new IsEqual($value, 0.0, \false, \false); + } + public static function equalToCanonicalizing($value) : IsEqualCanonicalizing + { + return new IsEqualCanonicalizing($value); + } + public static function equalToIgnoringCase($value) : IsEqualIgnoringCase + { + return new IsEqualIgnoringCase($value); + } + public static function equalToWithDelta($value, float $delta) : IsEqualWithDelta + { + return new IsEqualWithDelta($value, $delta); + } + 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)); } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -use function sprintf; -use Countable; -use PHPUnit\Framework\ExpectationFailedException; -use PHPUnit\Framework\SelfDescribing; -use PHPUnit\SebastianBergmann\Comparator\ComparisonFailure; -use PHPUnit\SebastianBergmann\Exporter\Exporter; -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -abstract class Constraint implements Countable, SelfDescribing -{ /** - * @var ?Exporter + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4601 */ - private $exporter; + public static function classHasAttribute(string $attributeName) : ClassHasAttribute + { + self::createWarning('classHasAttribute() is deprecated and will be removed in PHPUnit 10.'); + return new ClassHasAttribute($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 \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4601 */ - public function evaluate($other, string $description = '', bool $returnResult = \false) : ?bool + public static function classHasStaticAttribute(string $attributeName) : ClassHasStaticAttribute { - $success = \false; - if ($this->matches($other)) { - $success = \true; - } - if ($returnResult) { - return $success; - } - if (!$success) { - $this->fail($other, $description); - } - return null; + self::createWarning('classHasStaticAttribute() is deprecated and will be removed in PHPUnit 10.'); + return new ClassHasStaticAttribute($attributeName); } /** - * Counts the number of constraint elements. + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4601 */ - public function count() : int + public static function objectHasAttribute($attributeName) : ObjectHasAttribute { - return 1; + self::createWarning('objectHasAttribute() is deprecated and will be removed in PHPUnit 10.'); + return new ObjectHasAttribute($attributeName); } - protected function exporter() : Exporter + public static function identicalTo($value) : IsIdentical { - if ($this->exporter === null) { - $this->exporter = new Exporter(); - } - return $this->exporter; + return new IsIdentical($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 - * - * @codeCoverageIgnore - */ - protected function matches($other) : bool + public static function isInstanceOf(string $className) : IsInstanceOf { - return \false; + 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); + } + public static function objectEquals(object $object, string $method = 'equals') : ObjectEquals + { + return new ObjectEquals($object, $method); } /** - * 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 + * Fails a test with the given message. * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException + * @throws AssertionFailedError * * @psalm-return never-return */ - protected function fail($other, $description, ComparisonFailure $comparisonFailure = null) : void + public static function fail(string $message = '') : 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); + self::$count++; + throw new \PHPUnit\Framework\AssertionFailedError($message); } /** - * Return additional failure description where needed. + * Mark the test as incomplete. * - * The function can be overridden to provide additional failure - * information like a diff + * @throws IncompleteTestError * - * @param mixed $other evaluated value or object + * @psalm-return never-return */ - protected function additionalFailureDescription($other) : string + public static function markTestIncomplete(string $message = '') : void { - return ''; + throw new \PHPUnit\Framework\IncompleteTestError($message); } /** - * 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. + * Mark the test as skipped. * - * @param mixed $other evaluated value or object + * @throws SkippedTestError + * @throws SyntheticSkippedError * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @psalm-return never-return */ - protected function failureDescription($other) : string + public static function markTestSkipped(string $message = '') : void { - return $this->exporter()->export($other) . ' ' . $this->toString(); + if ($hint = self::detectLocationHint($message)) { + $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); + array_unshift($trace, $hint); + throw new \PHPUnit\Framework\SyntheticSkippedError($hint['message'], 0, $hint['file'], (int) $hint['line'], $trace); + } + throw new \PHPUnit\Framework\SkippedTestError($message); } /** - * Returns a custom string representation of the constraint object when it - * appears in context of an $operator expression. - * - * The purpose of this method is to provide meaningful descriptive string - * in context of operators such as LogicalNot. Native PHPUnit constraints - * are supported out of the box by LogicalNot, but externally developed - * ones had no way to provide correct strings in this context. - * - * The method shall return empty string, when it does not handle - * customization by itself. - * - * @param Operator $operator the $operator of the expression - * @param mixed $role role of $this constraint in the $operator expression + * Return the current assertion count. */ - protected function toStringInContext(\PHPUnit\Framework\Constraint\Operator $operator, $role) : string + public static function getCount() : int { - return ''; + return self::$count; } /** - * Returns the description of the failure when this constraint appears in - * context of an $operator expression. - * - * The purpose of this method is to provide meaningful failure description - * in context of operators such as LogicalNot. Native PHPUnit constraints - * are supported out of the box by LogicalNot, but externally developed - * ones had no way to provide correct messages in this context. - * - * The method shall return empty string, when it does not handle - * customization by itself. - * - * @param Operator $operator the $operator of the expression - * @param mixed $role role of $this constraint in the $operator expression - * @param mixed $other evaluated value or object + * Reset the assertion counter. */ - protected function failureDescriptionInContext(\PHPUnit\Framework\Constraint\Operator $operator, $role, $other) : string + public static function resetCount() : void { - $string = $this->toStringInContext($operator, $role); - if ($string === '') { - return ''; + 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]; + } } - return $this->exporter()->export($other) . ' ' . $string; + 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); } /** - * Reduces the sub-expression starting at $this by skipping degenerate - * sub-expression and returns first descendant constraint that starts - * a non-reducible sub-expression. - * - * Returns $this for terminal constraints and for operators that start - * non-reducible sub-expression, or the nearest descendant of $this that - * starts a non-reducible sub-expression. - * - * A constraint expression may be modelled as a tree with non-terminal - * nodes (operators) and terminal nodes. For example: - * - * LogicalOr (operator, non-terminal) - * + LogicalAnd (operator, non-terminal) - * | + IsType('int') (terminal) - * | + GreaterThan(10) (terminal) - * + LogicalNot (operator, non-terminal) - * + IsType('array') (terminal) - * - * A degenerate sub-expression is a part of the tree, that effectively does - * not contribute to the evaluation of the expression it appears in. An example - * of degenerate sub-expression is a BinaryOperator constructed with single - * operand or nested BinaryOperators, each with single operand. An - * expression involving a degenerate sub-expression is equivalent to a - * reduced expression with the degenerate sub-expression removed, for example - * - * LogicalAnd (operator) - * + LogicalOr (degenerate operator) - * | + LogicalAnd (degenerate operator) - * | + IsType('int') (terminal) - * + GreaterThan(10) (terminal) - * - * is equivalent to - * - * LogicalAnd (operator) - * + IsType('int') (terminal) - * + GreaterThan(10) (terminal) - * - * because the subexpression - * - * + LogicalOr - * + LogicalAnd - * + - - * - * is degenerate. Calling reduce() on the LogicalOr object above, as well - * as on LogicalAnd, shall return the IsType('int') instance. - * - * Other specific reductions can be implemented, for example cascade of - * LogicalNot operators - * - * + LogicalNot - * + LogicalNot - * +LogicalNot - * + IsTrue - * - * can be reduced to - * - * LogicalNot - * + IsTrue + * @codeCoverageIgnore */ - protected function reduce() : self + private static function createWarning(string $warning) : void { - return $this; + foreach (debug_backtrace() as $step) { + if (isset($step['object']) && $step['object'] instanceof \PHPUnit\Framework\TestCase) { + assert($step['object'] instanceof \PHPUnit\Framework\TestCase); + $step['object']->addWarning($warning); + break; + } + } } } value = $value; - $this->delta = $delta; - $this->canonicalize = $canonicalize; - $this->ignoreCase = $ignoreCase; + \PHPUnit\Framework\Assert::assertArrayHasKey(...func_get_args()); } +} +if (!function_exists('PHPUnit\\Framework\\assertArrayNotHasKey')) { /** - * 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. + * Asserts that an array does not have a specified key. * - * If $returnResult is true, the result of the evaluation is returned as - * a boolean value instead: true in case of success, false in case of a - * failure. + * @param int|string $key + * @param array|ArrayAccess $array * * @throws ExpectationFailedException + * @throws InvalidArgumentException + * @throws Exception + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertArrayNotHasKey */ - public function evaluate($other, string $description = '', bool $returnResult = \false) : ?bool + function assertArrayNotHasKey($key, $array, string $message = '') : void { - // 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; + \PHPUnit\Framework\Assert::assertArrayNotHasKey(...func_get_args()); } +} +if (!function_exists('PHPUnit\\Framework\\assertContains')) { /** - * Returns a string representation of the constraint. + * Asserts that a haystack contains a needle. * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException + * @throws InvalidArgumentException + * @throws Exception + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertContains */ - public function toString() : string + function assertContains($needle, iterable $haystack, string $message = '') : void { - $delta = ''; - if (is_string($this->value)) { - if (strpos($this->value, "\n") !== \false) { - return 'is equal to '; - } - 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); + \PHPUnit\Framework\Assert::assertContains(...func_get_args()); } } - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -use function is_string; -use function sprintf; -use function strpos; -use function trim; -use PHPUnit\Framework\ExpectationFailedException; -use PHPUnit\SebastianBergmann\Comparator\ComparisonFailure; -use PHPUnit\SebastianBergmann\Comparator\Factory as ComparatorFactory; -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -final class IsEqualCanonicalizing extends \PHPUnit\Framework\Constraint\Constraint -{ - /** - * @var mixed - */ - private $value; - public function __construct($value) +if (!function_exists('PHPUnit\\Framework\\assertContainsEquals')) { + function assertContainsEquals($needle, iterable $haystack, string $message = '') : void { - $this->value = $value; + \PHPUnit\Framework\Assert::assertContainsEquals(...func_get_args()); } +} +if (!function_exists('PHPUnit\\Framework\\assertNotContains')) { /** - * Evaluates the constraint for parameter $other. + * Asserts that a haystack does not contain a needle. * - * If $returnResult is set to false (the default), an exception is thrown - * in case of a failure. null is returned otherwise. + * @throws ExpectationFailedException + * @throws InvalidArgumentException + * @throws Exception * - * 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. + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit * - * @throws ExpectationFailedException + * @see Assert::assertNotContains */ - public function evaluate($other, string $description = '', bool $returnResult = \false) : ?bool + function assertNotContains($needle, iterable $haystack, string $message = '') : void { - // 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, 0.0, \true, \false); - } catch (ComparisonFailure $f) { - if ($returnResult) { - return \false; - } - throw new ExpectationFailedException(trim($description . "\n" . $f->getMessage()), $f); - } - return \true; + \PHPUnit\Framework\Assert::assertNotContains(...func_get_args()); + } +} +if (!function_exists('PHPUnit\\Framework\\assertNotContainsEquals')) { + function assertNotContainsEquals($needle, iterable $haystack, string $message = '') : void + { + \PHPUnit\Framework\Assert::assertNotContainsEquals(...func_get_args()); } +} +if (!function_exists('PHPUnit\\Framework\\assertContainsOnly')) { /** - * Returns a string representation of the constraint. + * Asserts that a haystack contains only values of a given type. * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException + * @throws InvalidArgumentException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertContainsOnly */ - public function toString() : string + function assertContainsOnly(string $type, iterable $haystack, ?bool $isNativeType = null, string $message = '') : void { - if (is_string($this->value)) { - if (strpos($this->value, "\n") !== \false) { - return 'is equal to '; - } - return sprintf("is equal to '%s'", $this->value); - } - return sprintf('is equal to %s', $this->exporter()->export($this->value)); + \PHPUnit\Framework\Assert::assertContainsOnly(...func_get_args()); } } - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -use function is_string; -use function sprintf; -use function strpos; -use function trim; -use PHPUnit\Framework\ExpectationFailedException; -use PHPUnit\SebastianBergmann\Comparator\ComparisonFailure; -use PHPUnit\SebastianBergmann\Comparator\Factory as ComparatorFactory; -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -final class IsEqualIgnoringCase extends \PHPUnit\Framework\Constraint\Constraint -{ +if (!function_exists('PHPUnit\\Framework\\assertContainsOnlyInstancesOf')) { /** - * @var mixed + * Asserts that a haystack contains only instances of a given class name. + * + * @throws ExpectationFailedException + * @throws InvalidArgumentException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertContainsOnlyInstancesOf */ - private $value; - public function __construct($value) + function assertContainsOnlyInstancesOf(string $className, iterable $haystack, string $message = '') : void { - $this->value = $value; + \PHPUnit\Framework\Assert::assertContainsOnlyInstancesOf(...func_get_args()); } +} +if (!function_exists('PHPUnit\\Framework\\assertNotContainsOnly')) { /** - * Evaluates the constraint for parameter $other. + * Asserts that a haystack does not contain only values of a given type. * - * If $returnResult is set to false (the default), an exception is thrown - * in case of a failure. null is returned otherwise. + * @throws ExpectationFailedException + * @throws InvalidArgumentException * - * 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. + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit * - * @throws ExpectationFailedException + * @see Assert::assertNotContainsOnly */ - public function evaluate($other, string $description = '', bool $returnResult = \false) : ?bool + function assertNotContainsOnly(string $type, iterable $haystack, ?bool $isNativeType = null, string $message = '') : void { - // 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, 0.0, \false, \true); - } catch (ComparisonFailure $f) { - if ($returnResult) { - return \false; - } - throw new ExpectationFailedException(trim($description . "\n" . $f->getMessage()), $f); - } - return \true; + \PHPUnit\Framework\Assert::assertNotContainsOnly(...func_get_args()); } +} +if (!function_exists('PHPUnit\\Framework\\assertCount')) { /** - * Returns a string representation of the constraint. + * Asserts the number of elements of an array, Countable or Traversable. * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @param Countable|iterable $haystack + * + * @throws ExpectationFailedException + * @throws InvalidArgumentException + * @throws Exception + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertCount */ - public function toString() : string + function assertCount(int $expectedCount, $haystack, string $message = '') : void { - if (is_string($this->value)) { - if (strpos($this->value, "\n") !== \false) { - return 'is equal to '; - } - return sprintf("is equal to '%s'", $this->value); - } - return sprintf('is equal to %s', $this->exporter()->export($this->value)); + \PHPUnit\Framework\Assert::assertCount(...func_get_args()); } } - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -use function sprintf; -use function trim; -use PHPUnit\Framework\ExpectationFailedException; -use PHPUnit\SebastianBergmann\Comparator\ComparisonFailure; -use PHPUnit\SebastianBergmann\Comparator\Factory as ComparatorFactory; -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -final class IsEqualWithDelta extends \PHPUnit\Framework\Constraint\Constraint -{ - /** - * @var mixed - */ - private $value; +if (!function_exists('PHPUnit\\Framework\\assertNotCount')) { /** - * @var float + * Asserts the number of elements of an array, Countable or Traversable. + * + * @param Countable|iterable $haystack + * + * @throws ExpectationFailedException + * @throws InvalidArgumentException + * @throws Exception + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertNotCount */ - private $delta; - public function __construct($value, float $delta) + function assertNotCount(int $expectedCount, $haystack, string $message = '') : void { - $this->value = $value; - $this->delta = $delta; + \PHPUnit\Framework\Assert::assertNotCount(...func_get_args()); } +} +if (!function_exists('PHPUnit\\Framework\\assertEquals')) { /** - * Evaluates the constraint for parameter $other. + * Asserts that two variables are equal. * - * If $returnResult is set to false (the default), an exception is thrown - * in case of a failure. null is returned otherwise. + * @throws ExpectationFailedException + * @throws InvalidArgumentException * - * 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. + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit * - * @throws ExpectationFailedException + * @see Assert::assertEquals */ - public function evaluate($other, string $description = '', bool $returnResult = \false) : ?bool + function assertEquals($expected, $actual, string $message = '') : void { - // 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); - } catch (ComparisonFailure $f) { - if ($returnResult) { - return \false; - } - throw new ExpectationFailedException(trim($description . "\n" . $f->getMessage()), $f); - } - return \true; + \PHPUnit\Framework\Assert::assertEquals(...func_get_args()); } +} +if (!function_exists('PHPUnit\\Framework\\assertEqualsCanonicalizing')) { /** - * Returns a string representation of the constraint. + * Asserts that two variables are equal (canonicalizing). * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException + * @throws InvalidArgumentException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertEqualsCanonicalizing */ - public function toString() : string + function assertEqualsCanonicalizing($expected, $actual, string $message = '') : void { - return sprintf('is equal to %s with delta <%F>', $this->exporter()->export($this->value), $this->delta); + \PHPUnit\Framework\Assert::assertEqualsCanonicalizing(...func_get_args()); } } - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -use function get_class; -use function sprintf; -use PHPUnit\Util\Filter; -use Throwable; -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -final class Exception extends \PHPUnit\Framework\Constraint\Constraint -{ +if (!function_exists('PHPUnit\\Framework\\assertEqualsIgnoringCase')) { /** - * @var string + * Asserts that two variables are equal (ignoring case). + * + * @throws ExpectationFailedException + * @throws InvalidArgumentException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertEqualsIgnoringCase */ - private $className; - public function __construct(string $className) + function assertEqualsIgnoringCase($expected, $actual, string $message = '') : void { - $this->className = $className; + \PHPUnit\Framework\Assert::assertEqualsIgnoringCase(...func_get_args()); } +} +if (!function_exists('PHPUnit\\Framework\\assertEqualsWithDelta')) { /** - * Returns a string representation of the constraint. + * Asserts that two variables are equal (with delta). + * + * @throws ExpectationFailedException + * @throws InvalidArgumentException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertEqualsWithDelta */ - public function toString() : string + function assertEqualsWithDelta($expected, $actual, float $delta, string $message = '') : void { - return sprintf('exception of type "%s"', $this->className); + \PHPUnit\Framework\Assert::assertEqualsWithDelta(...func_get_args()); } +} +if (!function_exists('PHPUnit\\Framework\\assertNotEquals')) { /** - * Evaluates the constraint for parameter $other. Returns true if the - * constraint is met, false otherwise. + * Asserts that two variables are not equal. * - * @param mixed $other value or object to evaluate + * @throws ExpectationFailedException + * @throws InvalidArgumentException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertNotEquals */ - protected function matches($other) : bool + function assertNotEquals($expected, $actual, string $message = '') : void { - return $other instanceof $this->className; + \PHPUnit\Framework\Assert::assertNotEquals(...func_get_args()); } +} +if (!function_exists('PHPUnit\\Framework\\assertNotEqualsCanonicalizing')) { /** - * Returns the description of the failure. + * Asserts that two variables are not equal (canonicalizing). * - * The beginning of failure messages is "Failed asserting that" in most - * cases. This method should return the second part of that sentence. + * @throws ExpectationFailedException + * @throws InvalidArgumentException * - * @param mixed $other evaluated value or object + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertNotEqualsCanonicalizing */ - protected function failureDescription($other) : string + function assertNotEqualsCanonicalizing($expected, $actual, string $message = '') : void { - 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); + \PHPUnit\Framework\Assert::assertNotEqualsCanonicalizing(...func_get_args()); } } - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -use function sprintf; -use Throwable; -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -final class ExceptionCode extends \PHPUnit\Framework\Constraint\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 '; - } +if (!function_exists('PHPUnit\\Framework\\assertNotEqualsIgnoringCase')) { /** - * Evaluates the constraint for parameter $other. Returns true if the - * constraint is met, false otherwise. + * Asserts that two variables are not equal (ignoring case). * - * @param Throwable $other + * @throws ExpectationFailedException + * @throws InvalidArgumentException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertNotEqualsIgnoringCase */ - protected function matches($other) : bool + function assertNotEqualsIgnoringCase($expected, $actual, string $message = '') : void { - return (string) $other->getCode() === (string) $this->expectedCode; + \PHPUnit\Framework\Assert::assertNotEqualsIgnoringCase(...func_get_args()); } +} +if (!function_exists('PHPUnit\\Framework\\assertNotEqualsWithDelta')) { /** - * Returns the description of the failure. + * Asserts that two variables are not equal (with delta). * - * The beginning of failure messages is "Failed asserting that" in most - * cases. This method should return the second part of that sentence. + * @throws ExpectationFailedException + * @throws InvalidArgumentException * - * @param mixed $other evaluated value or object + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @see Assert::assertNotEqualsWithDelta */ - protected function failureDescription($other) : string + function assertNotEqualsWithDelta($expected, $actual, float $delta, string $message = '') : void { - return sprintf('%s is equal to expected exception code %s', $this->exporter()->export($other->getCode()), $this->exporter()->export($this->expectedCode)); + \PHPUnit\Framework\Assert::assertNotEqualsWithDelta(...func_get_args()); } } - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -use function sprintf; -use function strpos; -use Throwable; -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -final class ExceptionMessage extends \PHPUnit\Framework\Constraint\Constraint -{ +if (!function_exists('PHPUnit\\Framework\\assertObjectEquals')) { /** - * @var string + * @throws ExpectationFailedException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertObjectEquals */ - private $expectedMessage; - public function __construct(string $expected) - { - $this->expectedMessage = $expected; - } - public function toString() : string + function assertObjectEquals(object $expected, object $actual, string $method = 'equals', string $message = '') : void { - if ($this->expectedMessage === '') { - return 'exception message is empty'; - } - return 'exception message contains '; + \PHPUnit\Framework\Assert::assertObjectEquals(...func_get_args()); } +} +if (!function_exists('PHPUnit\\Framework\\assertEmpty')) { /** - * Evaluates the constraint for parameter $other. Returns true if the - * constraint is met, false otherwise. + * Asserts that a variable is empty. * - * @param Throwable $other + * @throws ExpectationFailedException + * @throws InvalidArgumentException + * + * @psalm-assert empty $actual + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertEmpty */ - protected function matches($other) : bool + function assertEmpty($actual, string $message = '') : void { - if ($this->expectedMessage === '') { - return $other->getMessage() === ''; - } - return strpos((string) $other->getMessage(), $this->expectedMessage) !== \false; + \PHPUnit\Framework\Assert::assertEmpty(...func_get_args()); } +} +if (!function_exists('PHPUnit\\Framework\\assertNotEmpty')) { /** - * Returns the description of the failure. + * Asserts that a variable is not empty. * - * The beginning of failure messages is "Failed asserting that" in most - * cases. This method should return the second part of that sentence. + * @throws ExpectationFailedException + * @throws InvalidArgumentException * - * @param mixed $other evaluated value or object + * @psalm-assert !empty $actual + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertNotEmpty */ - protected function failureDescription($other) : string + function assertNotEmpty($actual, string $message = '') : void { - 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); + \PHPUnit\Framework\Assert::assertNotEmpty(...func_get_args()); } } - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -use function sprintf; -use Exception; -use PHPUnit\Util\RegularExpression as RegularExpressionUtil; -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -final class ExceptionMessageRegularExpression extends \PHPUnit\Framework\Constraint\Constraint -{ +if (!function_exists('PHPUnit\\Framework\\assertGreaterThan')) { /** - * @var string + * Asserts that a value is greater than another value. + * + * @throws ExpectationFailedException + * @throws InvalidArgumentException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertGreaterThan */ - private $expectedMessageRegExp; - public function __construct(string $expected) - { - $this->expectedMessageRegExp = $expected; - } - public function toString() : string + function assertGreaterThan($expected, $actual, string $message = '') : void { - return 'exception message matches '; + \PHPUnit\Framework\Assert::assertGreaterThan(...func_get_args()); } +} +if (!function_exists('PHPUnit\\Framework\\assertGreaterThanOrEqual')) { /** - * Evaluates the constraint for parameter $other. Returns true if the - * constraint is met, false otherwise. + * Asserts that a value is greater than or equal to another value. * - * @param \PHPUnit\Framework\Exception $other + * @throws ExpectationFailedException + * @throws InvalidArgumentException * - * @throws \PHPUnit\Framework\Exception - * @throws Exception + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertGreaterThanOrEqual */ - protected function matches($other) : bool + function assertGreaterThanOrEqual($expected, $actual, string $message = '') : void { - $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; + \PHPUnit\Framework\Assert::assertGreaterThanOrEqual(...func_get_args()); } +} +if (!function_exists('PHPUnit\\Framework\\assertLessThan')) { /** - * Returns the description of the failure. + * Asserts that a value is smaller than another value. * - * The beginning of failure messages is "Failed asserting that" in most - * cases. This method should return the second part of that sentence. + * @throws ExpectationFailedException + * @throws InvalidArgumentException * - * @param mixed $other evaluated value or object + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertLessThan */ - protected function failureDescription($other) : string + function assertLessThan($expected, $actual, string $message = '') : void { - return sprintf("exception message '%s' matches '%s'", $other->getMessage(), $this->expectedMessageRegExp); + \PHPUnit\Framework\Assert::assertLessThan(...func_get_args()); } } - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -use function is_dir; -use function sprintf; -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -final class DirectoryExists extends \PHPUnit\Framework\Constraint\Constraint -{ +if (!function_exists('PHPUnit\\Framework\\assertLessThanOrEqual')) { /** - * Returns a string representation of the constraint. + * Asserts that a value is smaller than or equal to another value. + * + * @throws ExpectationFailedException + * @throws InvalidArgumentException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertLessThanOrEqual */ - public function toString() : string + function assertLessThanOrEqual($expected, $actual, string $message = '') : void { - return 'directory exists'; + \PHPUnit\Framework\Assert::assertLessThanOrEqual(...func_get_args()); } +} +if (!function_exists('PHPUnit\\Framework\\assertFileEquals')) { /** - * Evaluates the constraint for parameter $other. Returns true if the - * constraint is met, false otherwise. + * Asserts that the contents of one file is equal to the contents of another + * file. * - * @param mixed $other value or object to evaluate + * @throws ExpectationFailedException + * @throws InvalidArgumentException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertFileEquals */ - protected function matches($other) : bool + function assertFileEquals(string $expected, string $actual, string $message = '') : void { - return is_dir($other); + \PHPUnit\Framework\Assert::assertFileEquals(...func_get_args()); } +} +if (!function_exists('PHPUnit\\Framework\\assertFileEqualsCanonicalizing')) { /** - * Returns the description of the failure. + * Asserts that the contents of one file is equal to the contents of another + * file (canonicalizing). * - * The beginning of failure messages is "Failed asserting that" in most - * cases. This method should return the second part of that sentence. + * @throws ExpectationFailedException + * @throws InvalidArgumentException * - * @param mixed $other evaluated value or object + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertFileEqualsCanonicalizing */ - protected function failureDescription($other) : string + function assertFileEqualsCanonicalizing(string $expected, string $actual, string $message = '') : void { - return sprintf('directory "%s" exists', $other); + \PHPUnit\Framework\Assert::assertFileEqualsCanonicalizing(...func_get_args()); } } - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -use function file_exists; -use function sprintf; -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -final class FileExists extends \PHPUnit\Framework\Constraint\Constraint -{ +if (!function_exists('PHPUnit\\Framework\\assertFileEqualsIgnoringCase')) { /** - * Returns a string representation of the constraint. + * Asserts that the contents of one file is equal to the contents of another + * file (ignoring case). + * + * @throws ExpectationFailedException + * @throws InvalidArgumentException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertFileEqualsIgnoringCase */ - public function toString() : string + function assertFileEqualsIgnoringCase(string $expected, string $actual, string $message = '') : void { - return 'file exists'; + \PHPUnit\Framework\Assert::assertFileEqualsIgnoringCase(...func_get_args()); } +} +if (!function_exists('PHPUnit\\Framework\\assertFileNotEquals')) { /** - * Evaluates the constraint for parameter $other. Returns true if the - * constraint is met, false otherwise. + * Asserts that the contents of one file is not equal to the contents of + * another file. * - * @param mixed $other value or object to evaluate + * @throws ExpectationFailedException + * @throws InvalidArgumentException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertFileNotEquals */ - protected function matches($other) : bool + function assertFileNotEquals(string $expected, string $actual, string $message = '') : void { - return file_exists($other); + \PHPUnit\Framework\Assert::assertFileNotEquals(...func_get_args()); } +} +if (!function_exists('PHPUnit\\Framework\\assertFileNotEqualsCanonicalizing')) { /** - * Returns the description of the failure. + * Asserts that the contents of one file is not equal to the contents of another + * file (canonicalizing). * - * The beginning of failure messages is "Failed asserting that" in most - * cases. This method should return the second part of that sentence. + * @throws ExpectationFailedException + * @throws InvalidArgumentException * - * @param mixed $other evaluated value or object + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertFileNotEqualsCanonicalizing */ - protected function failureDescription($other) : string + function assertFileNotEqualsCanonicalizing(string $expected, string $actual, string $message = '') : void { - return sprintf('file "%s" exists', $other); + \PHPUnit\Framework\Assert::assertFileNotEqualsCanonicalizing(...func_get_args()); } } - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -use function is_readable; -use function sprintf; -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -final class IsReadable extends \PHPUnit\Framework\Constraint\Constraint -{ +if (!function_exists('PHPUnit\\Framework\\assertFileNotEqualsIgnoringCase')) { /** - * Returns a string representation of the constraint. + * Asserts that the contents of one file is not equal to the contents of another + * file (ignoring case). + * + * @throws ExpectationFailedException + * @throws InvalidArgumentException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertFileNotEqualsIgnoringCase */ - public function toString() : string + function assertFileNotEqualsIgnoringCase(string $expected, string $actual, string $message = '') : void { - return 'is readable'; + \PHPUnit\Framework\Assert::assertFileNotEqualsIgnoringCase(...func_get_args()); } +} +if (!function_exists('PHPUnit\\Framework\\assertStringEqualsFile')) { /** - * Evaluates the constraint for parameter $other. Returns true if the - * constraint is met, false otherwise. + * Asserts that the contents of a string is equal + * to the contents of a file. * - * @param mixed $other value or object to evaluate + * @throws ExpectationFailedException + * @throws InvalidArgumentException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertStringEqualsFile */ - protected function matches($other) : bool + function assertStringEqualsFile(string $expectedFile, string $actualString, string $message = '') : void { - return is_readable($other); + \PHPUnit\Framework\Assert::assertStringEqualsFile(...func_get_args()); } +} +if (!function_exists('PHPUnit\\Framework\\assertStringEqualsFileCanonicalizing')) { /** - * Returns the description of the failure. + * Asserts that the contents of a string is equal + * to the contents of a file (canonicalizing). * - * The beginning of failure messages is "Failed asserting that" in most - * cases. This method should return the second part of that sentence. + * @throws ExpectationFailedException + * @throws InvalidArgumentException * - * @param mixed $other evaluated value or object + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertStringEqualsFileCanonicalizing */ - protected function failureDescription($other) : string + function assertStringEqualsFileCanonicalizing(string $expectedFile, string $actualString, string $message = '') : void { - return sprintf('"%s" is readable', $other); + \PHPUnit\Framework\Assert::assertStringEqualsFileCanonicalizing(...func_get_args()); } } - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -use function is_writable; -use function sprintf; -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -final class IsWritable extends \PHPUnit\Framework\Constraint\Constraint -{ +if (!function_exists('PHPUnit\\Framework\\assertStringEqualsFileIgnoringCase')) { /** - * Returns a string representation of the constraint. + * Asserts that the contents of a string is equal + * to the contents of a file (ignoring case). + * + * @throws ExpectationFailedException + * @throws InvalidArgumentException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertStringEqualsFileIgnoringCase */ - public function toString() : string + function assertStringEqualsFileIgnoringCase(string $expectedFile, string $actualString, string $message = '') : void { - return 'is writable'; + \PHPUnit\Framework\Assert::assertStringEqualsFileIgnoringCase(...func_get_args()); } +} +if (!function_exists('PHPUnit\\Framework\\assertStringNotEqualsFile')) { /** - * Evaluates the constraint for parameter $other. Returns true if the - * constraint is met, false otherwise. + * Asserts that the contents of a string is not equal + * to the contents of a file. * - * @param mixed $other value or object to evaluate + * @throws ExpectationFailedException + * @throws InvalidArgumentException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertStringNotEqualsFile */ - protected function matches($other) : bool + function assertStringNotEqualsFile(string $expectedFile, string $actualString, string $message = '') : void { - return is_writable($other); + \PHPUnit\Framework\Assert::assertStringNotEqualsFile(...func_get_args()); } +} +if (!function_exists('PHPUnit\\Framework\\assertStringNotEqualsFileCanonicalizing')) { /** - * Returns the description of the failure. + * Asserts that the contents of a string is not equal + * to the contents of a file (canonicalizing). * - * The beginning of failure messages is "Failed asserting that" in most - * cases. This method should return the second part of that sentence. + * @throws ExpectationFailedException + * @throws InvalidArgumentException * - * @param mixed $other evaluated value or object + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertStringNotEqualsFileCanonicalizing */ - protected function failureDescription($other) : string + function assertStringNotEqualsFileCanonicalizing(string $expectedFile, string $actualString, string $message = '') : void { - return sprintf('"%s" is writable', $other); + \PHPUnit\Framework\Assert::assertStringNotEqualsFileCanonicalizing(...func_get_args()); } } - - * - * 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; -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -final class IsAnything extends \PHPUnit\Framework\Constraint\Constraint -{ +if (!function_exists('PHPUnit\\Framework\\assertStringNotEqualsFileIgnoringCase')) { /** - * Evaluates the constraint for parameter $other. + * Asserts that the contents of a string is not equal + * to the contents of a file (ignoring case). * - * If $returnResult is set to false (the default), an exception is thrown - * in case of a failure. null is returned otherwise. + * @throws ExpectationFailedException + * @throws InvalidArgumentException * - * 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. + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit * - * @throws ExpectationFailedException + * @see Assert::assertStringNotEqualsFileIgnoringCase */ - public function evaluate($other, string $description = '', bool $returnResult = \false) : ?bool + function assertStringNotEqualsFileIgnoringCase(string $expectedFile, string $actualString, string $message = '') : void { - return $returnResult ? \true : null; + \PHPUnit\Framework\Assert::assertStringNotEqualsFileIgnoringCase(...func_get_args()); } +} +if (!function_exists('PHPUnit\\Framework\\assertIsReadable')) { /** - * Returns a string representation of the constraint. + * Asserts that a file/dir is readable. + * + * @throws ExpectationFailedException + * @throws InvalidArgumentException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertIsReadable */ - public function toString() : string + function assertIsReadable(string $filename, string $message = '') : void { - return 'is anything'; + \PHPUnit\Framework\Assert::assertIsReadable(...func_get_args()); } +} +if (!function_exists('PHPUnit\\Framework\\assertIsNotReadable')) { /** - * Counts the number of constraint elements. + * Asserts that a file/dir exists and is not readable. + * + * @throws ExpectationFailedException + * @throws InvalidArgumentException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertIsNotReadable */ - public function count() : int + function assertIsNotReadable(string $filename, string $message = '') : void { - return 0; + \PHPUnit\Framework\Assert::assertIsNotReadable(...func_get_args()); } } - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -use function get_class; -use function is_array; -use function is_object; -use function is_string; -use function sprintf; -use PHPUnit\Framework\ExpectationFailedException; -use PHPUnit\SebastianBergmann\Comparator\ComparisonFailure; -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -final class IsIdentical extends \PHPUnit\Framework\Constraint\Constraint -{ +if (!function_exists('PHPUnit\\Framework\\assertNotIsReadable')) { /** - * @var mixed + * Asserts that a file/dir exists and is not readable. + * + * @throws ExpectationFailedException + * @throws InvalidArgumentException + * + * @codeCoverageIgnore + * + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4062 + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertNotIsReadable */ - private $value; - public function __construct($value) + function assertNotIsReadable(string $filename, string $message = '') : void { - $this->value = $value; + \PHPUnit\Framework\Assert::assertNotIsReadable(...func_get_args()); } +} +if (!function_exists('PHPUnit\\Framework\\assertIsWritable')) { /** - * Evaluates the constraint for parameter $other. + * Asserts that a file/dir exists and is writable. * - * If $returnResult is set to false (the default), an exception is thrown - * in case of a failure. null is returned otherwise. + * @throws ExpectationFailedException + * @throws InvalidArgumentException * - * 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. + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException + * @see Assert::assertIsWritable */ - public function evaluate($other, string $description = '', bool $returnResult = \false) : ?bool + function assertIsWritable(string $filename, string $message = '') : void { - $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); - } - return null; + \PHPUnit\Framework\Assert::assertIsWritable(...func_get_args()); } +} +if (!function_exists('PHPUnit\\Framework\\assertIsNotWritable')) { /** - * Returns a string representation of the constraint. + * Asserts that a file/dir exists and is not writable. * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException + * @throws InvalidArgumentException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertIsNotWritable */ - public function toString() : string + function assertIsNotWritable(string $filename, string $message = '') : void { - 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); + \PHPUnit\Framework\Assert::assertIsNotWritable(...func_get_args()); } +} +if (!function_exists('PHPUnit\\Framework\\assertNotIsWritable')) { /** - * Returns the description of the failure. + * Asserts that a file/dir exists and is not writable. * - * The beginning of failure messages is "Failed asserting that" in most - * cases. This method should return the second part of that sentence. + * @throws ExpectationFailedException + * @throws InvalidArgumentException * - * @param mixed $other evaluated value or object + * @codeCoverageIgnore * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4065 + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertNotIsWritable */ - protected function failureDescription($other) : string + function assertNotIsWritable(string $filename, string $message = '') : void { - 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); + \PHPUnit\Framework\Assert::assertNotIsWritable(...func_get_args()); } } - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -use function json_decode; -use function sprintf; -use PHPUnit\Framework\ExpectationFailedException; -use PHPUnit\Util\Json; -use PHPUnit\SebastianBergmann\Comparator\ComparisonFailure; -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -final class JsonMatches extends \PHPUnit\Framework\Constraint\Constraint -{ +if (!function_exists('PHPUnit\\Framework\\assertDirectoryExists')) { /** - * @var string + * Asserts that a directory exists. + * + * @throws ExpectationFailedException + * @throws InvalidArgumentException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertDirectoryExists */ - private $value; - public function __construct(string $value) + function assertDirectoryExists(string $directory, string $message = '') : void { - $this->value = $value; + \PHPUnit\Framework\Assert::assertDirectoryExists(...func_get_args()); } +} +if (!function_exists('PHPUnit\\Framework\\assertDirectoryDoesNotExist')) { /** - * Returns a string representation of the object. + * Asserts that a directory does not exist. + * + * @throws ExpectationFailedException + * @throws InvalidArgumentException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertDirectoryDoesNotExist */ - public function toString() : string + function assertDirectoryDoesNotExist(string $directory, string $message = '') : void { - return sprintf('matches JSON string "%s"', $this->value); + \PHPUnit\Framework\Assert::assertDirectoryDoesNotExist(...func_get_args()); } +} +if (!function_exists('PHPUnit\\Framework\\assertDirectoryNotExists')) { /** - * Evaluates the constraint for parameter $other. Returns true if the - * constraint is met, false otherwise. + * Asserts that a directory does not exist. * - * This method can be overridden to implement the evaluation algorithm. + * @throws ExpectationFailedException + * @throws InvalidArgumentException * - * @param mixed $other value or object to evaluate + * @codeCoverageIgnore + * + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4068 + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertDirectoryNotExists */ - protected function matches($other) : bool + function assertDirectoryNotExists(string $directory, string $message = '') : void { - [$error, $recodedOther] = Json::canonicalize($other); - if ($error) { - return \false; - } - [$error, $recodedValue] = Json::canonicalize($this->value); - if ($error) { - return \false; - } - return $recodedOther == $recodedValue; + \PHPUnit\Framework\Assert::assertDirectoryNotExists(...func_get_args()); } +} +if (!function_exists('PHPUnit\\Framework\\assertDirectoryIsReadable')) { /** - * 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 + * Asserts that a directory exists and is readable. * - * @throws \PHPUnit\Framework\Exception - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException * @throws ExpectationFailedException + * @throws InvalidArgumentException * - * @psalm-return never-return + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertDirectoryIsReadable */ - protected function fail($other, $description, ComparisonFailure $comparisonFailure = null) : void + function assertDirectoryIsReadable(string $directory, string $message = '') : void { - if ($comparisonFailure === null) { - [$error, $recodedOther] = Json::canonicalize($other); - if ($error) { - parent::fail($other, $description); - } - [$error, $recodedValue] = Json::canonicalize($this->value); - if ($error) { - parent::fail($other, $description); - } - $comparisonFailure = new ComparisonFailure(json_decode($this->value), json_decode($other), Json::prettify($recodedValue), Json::prettify($recodedOther), \false, 'Failed asserting that two json values are equal.'); - } - parent::fail($other, $description, $comparisonFailure); + \PHPUnit\Framework\Assert::assertDirectoryIsReadable(...func_get_args()); } } - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -use const JSON_ERROR_CTRL_CHAR; -use const JSON_ERROR_DEPTH; -use const JSON_ERROR_NONE; -use const JSON_ERROR_STATE_MISMATCH; -use const JSON_ERROR_SYNTAX; -use const JSON_ERROR_UTF8; -use function strtolower; -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -final class JsonMatchesErrorMessageProvider -{ +if (!function_exists('PHPUnit\\Framework\\assertDirectoryIsNotReadable')) { /** - * Translates JSON error to a human readable string. + * Asserts that a directory exists and is not readable. + * + * @throws ExpectationFailedException + * @throws InvalidArgumentException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertDirectoryIsNotReadable */ - public static function determineJsonError(string $error, string $prefix = '') : ?string + function assertDirectoryIsNotReadable(string $directory, string $message = '') : void { - 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'; - } + \PHPUnit\Framework\Assert::assertDirectoryIsNotReadable(...func_get_args()); } +} +if (!function_exists('PHPUnit\\Framework\\assertDirectoryNotIsReadable')) { /** - * Translates a given type to a human readable message prefix. + * Asserts that a directory exists and is not readable. + * + * @throws ExpectationFailedException + * @throws InvalidArgumentException + * + * @codeCoverageIgnore + * + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4071 + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertDirectoryNotIsReadable */ - public static function translateTypeToPrefix(string $type) : string + function assertDirectoryNotIsReadable(string $directory, string $message = '') : void { - 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; + \PHPUnit\Framework\Assert::assertDirectoryNotIsReadable(...func_get_args()); } } - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -use function is_finite; -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -final class IsFinite extends \PHPUnit\Framework\Constraint\Constraint -{ +if (!function_exists('PHPUnit\\Framework\\assertDirectoryIsWritable')) { /** - * Returns a string representation of the constraint. + * Asserts that a directory exists and is writable. + * + * @throws ExpectationFailedException + * @throws InvalidArgumentException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertDirectoryIsWritable */ - public function toString() : string + function assertDirectoryIsWritable(string $directory, string $message = '') : void { - return 'is finite'; + \PHPUnit\Framework\Assert::assertDirectoryIsWritable(...func_get_args()); } +} +if (!function_exists('PHPUnit\\Framework\\assertDirectoryIsNotWritable')) { /** - * Evaluates the constraint for parameter $other. Returns true if the - * constraint is met, false otherwise. + * Asserts that a directory exists and is not writable. * - * @param mixed $other value or object to evaluate + * @throws ExpectationFailedException + * @throws InvalidArgumentException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertDirectoryIsNotWritable */ - protected function matches($other) : bool + function assertDirectoryIsNotWritable(string $directory, string $message = '') : void { - return is_finite($other); + \PHPUnit\Framework\Assert::assertDirectoryIsNotWritable(...func_get_args()); } } - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -use function is_infinite; -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -final class IsInfinite extends \PHPUnit\Framework\Constraint\Constraint -{ +if (!function_exists('PHPUnit\\Framework\\assertDirectoryNotIsWritable')) { /** - * Returns a string representation of the constraint. + * Asserts that a directory exists and is not writable. + * + * @throws ExpectationFailedException + * @throws InvalidArgumentException + * + * @codeCoverageIgnore + * + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4074 + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertDirectoryNotIsWritable */ - public function toString() : string + function assertDirectoryNotIsWritable(string $directory, string $message = '') : void { - return 'is infinite'; + \PHPUnit\Framework\Assert::assertDirectoryNotIsWritable(...func_get_args()); } +} +if (!function_exists('PHPUnit\\Framework\\assertFileExists')) { /** - * Evaluates the constraint for parameter $other. Returns true if the - * constraint is met, false otherwise. + * Asserts that a file exists. * - * @param mixed $other value or object to evaluate + * @throws ExpectationFailedException + * @throws InvalidArgumentException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertFileExists */ - protected function matches($other) : bool + function assertFileExists(string $filename, string $message = '') : void { - return is_infinite($other); + \PHPUnit\Framework\Assert::assertFileExists(...func_get_args()); } } - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -use function is_nan; -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -final class IsNan extends \PHPUnit\Framework\Constraint\Constraint -{ +if (!function_exists('PHPUnit\\Framework\\assertFileDoesNotExist')) { /** - * Returns a string representation of the constraint. - */ - public function toString() : string + * Asserts that a file does not exist. + * + * @throws ExpectationFailedException + * @throws InvalidArgumentException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertFileDoesNotExist + */ + function assertFileDoesNotExist(string $filename, string $message = '') : void { - return 'is nan'; + \PHPUnit\Framework\Assert::assertFileDoesNotExist(...func_get_args()); } +} +if (!function_exists('PHPUnit\\Framework\\assertFileNotExists')) { /** - * Evaluates the constraint for parameter $other. Returns true if the - * constraint is met, false otherwise. + * Asserts that a file does not exist. * - * @param mixed $other value or object to evaluate + * @throws ExpectationFailedException + * @throws InvalidArgumentException + * + * @codeCoverageIgnore + * + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4077 + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertFileNotExists */ - protected function matches($other) : bool + function assertFileNotExists(string $filename, string $message = '') : void { - return is_nan($other); + \PHPUnit\Framework\Assert::assertFileNotExists(...func_get_args()); } } - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -use function get_class; -use function is_object; -use function sprintf; -use PHPUnit\Framework\Exception; -use ReflectionClass; -use ReflectionException; -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4601 - */ -class ClassHasAttribute extends \PHPUnit\Framework\Constraint\Constraint -{ +if (!function_exists('PHPUnit\\Framework\\assertFileIsReadable')) { /** - * @var string + * Asserts that a file exists and is readable. + * + * @throws ExpectationFailedException + * @throws InvalidArgumentException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertFileIsReadable */ - private $attributeName; - public function __construct(string $attributeName) + function assertFileIsReadable(string $file, string $message = '') : void { - $this->attributeName = $attributeName; + \PHPUnit\Framework\Assert::assertFileIsReadable(...func_get_args()); } +} +if (!function_exists('PHPUnit\\Framework\\assertFileIsNotReadable')) { /** - * Returns a string representation of the constraint. + * Asserts that a file exists and is not readable. + * + * @throws ExpectationFailedException + * @throws InvalidArgumentException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertFileIsNotReadable */ - public function toString() : string + function assertFileIsNotReadable(string $file, string $message = '') : void { - return sprintf('has attribute "%s"', $this->attributeName); + \PHPUnit\Framework\Assert::assertFileIsNotReadable(...func_get_args()); } +} +if (!function_exists('PHPUnit\\Framework\\assertFileNotIsReadable')) { /** - * Evaluates the constraint for parameter $other. Returns true if the - * constraint is met, false otherwise. + * Asserts that a file exists and is not readable. * - * @param mixed $other value or object to evaluate + * @throws ExpectationFailedException + * @throws InvalidArgumentException + * + * @codeCoverageIgnore + * + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4080 + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertFileNotIsReadable */ - protected function matches($other) : bool + function assertFileNotIsReadable(string $file, string $message = '') : void { - try { - return (new ReflectionClass($other))->hasProperty($this->attributeName); - // @codeCoverageIgnoreStart - } catch (ReflectionException $e) { - throw new Exception($e->getMessage(), $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd + \PHPUnit\Framework\Assert::assertFileNotIsReadable(...func_get_args()); } +} +if (!function_exists('PHPUnit\\Framework\\assertFileIsWritable')) { /** - * Returns the description of the failure. + * Asserts that a file exists and is writable. * - * The beginning of failure messages is "Failed asserting that" in most - * cases. This method should return the second part of that sentence. + * @throws ExpectationFailedException + * @throws InvalidArgumentException * - * @param mixed $other evaluated value or object + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertFileIsWritable */ - 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 + function assertFileIsWritable(string $file, string $message = '') : void { - return $this->attributeName; + \PHPUnit\Framework\Assert::assertFileIsWritable(...func_get_args()); } } - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -use function sprintf; -use PHPUnit\Framework\Exception; -use ReflectionClass; -use ReflectionException; -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4601 - */ -final class ClassHasStaticAttribute extends \PHPUnit\Framework\Constraint\ClassHasAttribute -{ +if (!function_exists('PHPUnit\\Framework\\assertFileIsNotWritable')) { /** - * Returns a string representation of the constraint. + * Asserts that a file exists and is not writable. + * + * @throws ExpectationFailedException + * @throws InvalidArgumentException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertFileIsNotWritable */ - public function toString() : string + function assertFileIsNotWritable(string $file, string $message = '') : void { - return sprintf('has static attribute "%s"', $this->attributeName()); + \PHPUnit\Framework\Assert::assertFileIsNotWritable(...func_get_args()); } +} +if (!function_exists('PHPUnit\\Framework\\assertFileNotIsWritable')) { /** - * Evaluates the constraint for parameter $other. Returns true if the - * constraint is met, false otherwise. + * Asserts that a file exists and is not writable. * - * @param mixed $other value or object to evaluate + * @throws ExpectationFailedException + * @throws InvalidArgumentException + * + * @codeCoverageIgnore + * + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4083 + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertFileNotIsWritable */ - protected function matches($other) : bool + function assertFileNotIsWritable(string $file, string $message = '') : void { - try { - $class = new ReflectionClass($other); - if ($class->hasProperty($this->attributeName())) { - return $class->getProperty($this->attributeName())->isStatic(); - } - // @codeCoverageIgnoreStart - } catch (ReflectionException $e) { - throw new Exception($e->getMessage(), $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd - return \false; + \PHPUnit\Framework\Assert::assertFileNotIsWritable(...func_get_args()); } } - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -use function get_class; -use function is_object; -use PHPUnit\Framework\ActualValueIsNotAnObjectException; -use PHPUnit\Framework\ComparisonMethodDoesNotAcceptParameterTypeException; -use PHPUnit\Framework\ComparisonMethodDoesNotDeclareBoolReturnTypeException; -use PHPUnit\Framework\ComparisonMethodDoesNotDeclareExactlyOneParameterException; -use PHPUnit\Framework\ComparisonMethodDoesNotDeclareParameterTypeException; -use PHPUnit\Framework\ComparisonMethodDoesNotExistException; -use ReflectionNamedType; -use ReflectionObject; -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -final class ObjectEquals extends \PHPUnit\Framework\Constraint\Constraint -{ - /** - * @var object - */ - private $expected; +if (!function_exists('PHPUnit\\Framework\\assertTrue')) { /** - * @var string + * Asserts that a condition is true. + * + * @throws ExpectationFailedException + * @throws InvalidArgumentException + * + * @psalm-assert true $condition + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertTrue */ - private $method; - public function __construct(object $object, string $method = 'equals') - { - $this->expected = $object; - $this->method = $method; - } - public function toString() : string + function assertTrue($condition, string $message = '') : void { - return 'two objects are equal'; + \PHPUnit\Framework\Assert::assertTrue(...func_get_args()); } +} +if (!function_exists('PHPUnit\\Framework\\assertNotTrue')) { /** - * @throws ActualValueIsNotAnObjectException - * @throws ComparisonMethodDoesNotAcceptParameterTypeException - * @throws ComparisonMethodDoesNotDeclareBoolReturnTypeException - * @throws ComparisonMethodDoesNotDeclareExactlyOneParameterException - * @throws ComparisonMethodDoesNotDeclareParameterTypeException - * @throws ComparisonMethodDoesNotExistException + * Asserts that a condition is not true. + * + * @throws ExpectationFailedException + * @throws InvalidArgumentException + * + * @psalm-assert !true $condition + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertNotTrue */ - protected function matches($other) : bool - { - if (!is_object($other)) { - throw new ActualValueIsNotAnObjectException(); - } - $object = new ReflectionObject($other); - if (!$object->hasMethod($this->method)) { - throw new ComparisonMethodDoesNotExistException(get_class($other), $this->method); - } - /** @noinspection PhpUnhandledExceptionInspection */ - $method = $object->getMethod($this->method); - if (!$method->hasReturnType()) { - throw new ComparisonMethodDoesNotDeclareBoolReturnTypeException(get_class($other), $this->method); - } - $returnType = $method->getReturnType(); - if (!$returnType instanceof ReflectionNamedType) { - throw new ComparisonMethodDoesNotDeclareBoolReturnTypeException(get_class($other), $this->method); - } - if ($returnType->allowsNull()) { - throw new ComparisonMethodDoesNotDeclareBoolReturnTypeException(get_class($other), $this->method); - } - if ($returnType->getName() !== 'bool') { - throw new ComparisonMethodDoesNotDeclareBoolReturnTypeException(get_class($other), $this->method); - } - if ($method->getNumberOfParameters() !== 1 || $method->getNumberOfRequiredParameters() !== 1) { - throw new ComparisonMethodDoesNotDeclareExactlyOneParameterException(get_class($other), $this->method); - } - $parameter = $method->getParameters()[0]; - if (!$parameter->hasType()) { - throw new ComparisonMethodDoesNotDeclareParameterTypeException(get_class($other), $this->method); - } - $type = $parameter->getType(); - if (!$type instanceof ReflectionNamedType) { - throw new ComparisonMethodDoesNotDeclareParameterTypeException(get_class($other), $this->method); - } - $typeName = $type->getName(); - if ($typeName === 'self') { - $typeName = get_class($other); - } - if (!$this->expected instanceof $typeName) { - throw new ComparisonMethodDoesNotAcceptParameterTypeException(get_class($other), $this->method, get_class($this->expected)); - } - return $other->{$this->method}($this->expected); - } - protected function failureDescription($other) : string + function assertNotTrue($condition, string $message = '') : void { - return $this->toString(); + \PHPUnit\Framework\Assert::assertNotTrue(...func_get_args()); } } - - * - * 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; -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4601 - */ -final class ObjectHasAttribute extends \PHPUnit\Framework\Constraint\ClassHasAttribute -{ +if (!function_exists('PHPUnit\\Framework\\assertFalse')) { /** - * Evaluates the constraint for parameter $other. Returns true if the - * constraint is met, false otherwise. + * Asserts that a condition is false. * - * @param mixed $other value or object to evaluate + * @throws ExpectationFailedException + * @throws InvalidArgumentException + * + * @psalm-assert false $condition + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertFalse */ - protected function matches($other) : bool + function assertFalse($condition, string $message = '') : void { - return (new ReflectionObject($other))->hasProperty($this->attributeName()); + \PHPUnit\Framework\Assert::assertFalse(...func_get_args()); } } - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -use function get_class; -use function gettype; -use function is_object; -use function sprintf; -use ReflectionObject; -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -final class ObjectHasProperty extends \PHPUnit\Framework\Constraint\Constraint -{ +if (!function_exists('PHPUnit\\Framework\\assertNotFalse')) { /** - * @var string + * Asserts that a condition is not false. + * + * @throws ExpectationFailedException + * @throws InvalidArgumentException + * + * @psalm-assert !false $condition + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertNotFalse */ - private $propertyName; - public function __construct(string $propertyName) + function assertNotFalse($condition, string $message = '') : void { - $this->propertyName = $propertyName; + \PHPUnit\Framework\Assert::assertNotFalse(...func_get_args()); } +} +if (!function_exists('PHPUnit\\Framework\\assertNull')) { /** - * Returns a string representation of the constraint. + * Asserts that a variable is null. + * + * @throws ExpectationFailedException + * @throws InvalidArgumentException + * + * @psalm-assert null $actual + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertNull */ - public function toString() : string + function assertNull($actual, string $message = '') : void { - return sprintf('has property "%s"', $this->propertyName); + \PHPUnit\Framework\Assert::assertNull(...func_get_args()); } +} +if (!function_exists('PHPUnit\\Framework\\assertNotNull')) { /** - * Evaluates the constraint for parameter $other. Returns true if the - * constraint is met, false otherwise. + * Asserts that a variable is not null. * - * @param mixed $other value or object to evaluate + * @throws ExpectationFailedException + * @throws InvalidArgumentException + * + * @psalm-assert !null $actual + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertNotNull */ - protected function matches($other) : bool + function assertNotNull($actual, string $message = '') : void { - if (!is_object($other)) { - return \false; - } - return (new ReflectionObject($other))->hasProperty($this->propertyName); + \PHPUnit\Framework\Assert::assertNotNull(...func_get_args()); } +} +if (!function_exists('PHPUnit\\Framework\\assertFinite')) { /** - * Returns the description of the failure. + * Asserts that a variable is finite. * - * The beginning of failure messages is "Failed asserting that" in most - * cases. This method should return the second part of that sentence. + * @throws ExpectationFailedException + * @throws InvalidArgumentException * - * @param mixed $other evaluated value or object + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertFinite */ - protected function failureDescription($other) : string + function assertFinite($actual, string $message = '') : void { - if (is_object($other)) { - return sprintf('object of class "%s" %s', get_class($other), $this->toString()); - } - return sprintf('"%s" (%s) %s', $other, gettype($other), $this->toString()); + \PHPUnit\Framework\Assert::assertFinite(...func_get_args()); } } - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -use function array_map; -use function array_values; -use function count; -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -abstract class BinaryOperator extends \PHPUnit\Framework\Constraint\Operator -{ +if (!function_exists('PHPUnit\\Framework\\assertInfinite')) { /** - * @var Constraint[] + * Asserts that a variable is infinite. + * + * @throws ExpectationFailedException + * @throws InvalidArgumentException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertInfinite */ - private $constraints = []; - public static function fromConstraints(\PHPUnit\Framework\Constraint\Constraint ...$constraints) : self + function assertInfinite($actual, string $message = '') : void { - $constraint = new static(); - $constraint->constraints = $constraints; - return $constraint; + \PHPUnit\Framework\Assert::assertInfinite(...func_get_args()); } +} +if (!function_exists('PHPUnit\\Framework\\assertNan')) { /** - * @param mixed[] $constraints + * Asserts that a variable is nan. + * + * @throws ExpectationFailedException + * @throws InvalidArgumentException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertNan */ - public function setConstraints(array $constraints) : void + function assertNan($actual, string $message = '') : void { - $this->constraints = array_map(function ($constraint) : \PHPUnit\Framework\Constraint\Constraint { - return $this->checkConstraint($constraint); - }, array_values($constraints)); + \PHPUnit\Framework\Assert::assertNan(...func_get_args()); } +} +if (!function_exists('PHPUnit\\Framework\\assertClassHasAttribute')) { /** - * Returns the number of operands (constraints). + * Asserts that a class has a specified attribute. + * + * @throws ExpectationFailedException + * @throws InvalidArgumentException + * @throws Exception + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertClassHasAttribute */ - public final function arity() : int + function assertClassHasAttribute(string $attributeName, string $className, string $message = '') : void { - return count($this->constraints); + \PHPUnit\Framework\Assert::assertClassHasAttribute(...func_get_args()); } +} +if (!function_exists('PHPUnit\\Framework\\assertClassNotHasAttribute')) { /** - * Returns a string representation of the constraint. + * Asserts that a class does not have a specified attribute. + * + * @throws ExpectationFailedException + * @throws InvalidArgumentException + * @throws Exception + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertClassNotHasAttribute */ - public function toString() : string + function assertClassNotHasAttribute(string $attributeName, string $className, string $message = '') : void { - $reduced = $this->reduce(); - if ($reduced !== $this) { - return $reduced->toString(); - } - $text = ''; - foreach ($this->constraints as $key => $constraint) { - $constraint = $constraint->reduce(); - $text .= $this->constraintToString($constraint, $key); - } - return $text; + \PHPUnit\Framework\Assert::assertClassNotHasAttribute(...func_get_args()); } +} +if (!function_exists('PHPUnit\\Framework\\assertClassHasStaticAttribute')) { /** - * Counts the number of constraint elements. + * Asserts that a class has a specified static attribute. + * + * @throws ExpectationFailedException + * @throws InvalidArgumentException + * @throws Exception + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertClassHasStaticAttribute */ - public function count() : int + function assertClassHasStaticAttribute(string $attributeName, string $className, string $message = '') : void { - $count = 0; - foreach ($this->constraints as $constraint) { - $count += count($constraint); - } - return $count; + \PHPUnit\Framework\Assert::assertClassHasStaticAttribute(...func_get_args()); } +} +if (!function_exists('PHPUnit\\Framework\\assertClassNotHasStaticAttribute')) { /** - * Returns the nested constraints. + * Asserts that a class does not have a specified static attribute. + * + * @throws ExpectationFailedException + * @throws InvalidArgumentException + * @throws Exception + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertClassNotHasStaticAttribute */ - protected final function constraints() : array + function assertClassNotHasStaticAttribute(string $attributeName, string $className, string $message = '') : void { - return $this->constraints; + \PHPUnit\Framework\Assert::assertClassNotHasStaticAttribute(...func_get_args()); } +} +if (!function_exists('PHPUnit\\Framework\\assertObjectHasAttribute')) { /** - * Returns true if the $constraint needs to be wrapped with braces. + * Asserts that an object has a specified attribute. + * + * @param object $object + * + * @throws ExpectationFailedException + * @throws InvalidArgumentException + * @throws Exception + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertObjectHasAttribute */ - protected final function constraintNeedsParentheses(\PHPUnit\Framework\Constraint\Constraint $constraint) : bool + function assertObjectHasAttribute(string $attributeName, $object, string $message = '') : void { - return $this->arity() > 1 && parent::constraintNeedsParentheses($constraint); + \PHPUnit\Framework\Assert::assertObjectHasAttribute(...func_get_args()); } +} +if (!function_exists('PHPUnit\\Framework\\assertObjectNotHasAttribute')) { /** - * Reduces the sub-expression starting at $this by skipping degenerate - * sub-expression and returns first descendant constraint that starts - * a non-reducible sub-expression. + * Asserts that an object does not have a specified attribute. * - * See Constraint::reduce() for more. + * @param object $object + * + * @throws ExpectationFailedException + * @throws InvalidArgumentException + * @throws Exception + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertObjectNotHasAttribute */ - protected function reduce() : \PHPUnit\Framework\Constraint\Constraint + function assertObjectNotHasAttribute(string $attributeName, $object, string $message = '') : void { - if ($this->arity() === 1 && $this->constraints[0] instanceof \PHPUnit\Framework\Constraint\Operator) { - return $this->constraints[0]->reduce(); - } - return parent::reduce(); + \PHPUnit\Framework\Assert::assertObjectNotHasAttribute(...func_get_args()); } +} +if (!function_exists('PHPUnit\\Framework\\assertObjectHasProperty')) { /** - * Returns string representation of given operand in context of this operator. + * Asserts that an object has a specified property. * - * @param Constraint $constraint operand constraint - * @param int $position position of $constraint in this expression + * @throws ExpectationFailedException + * @throws InvalidArgumentException + * @throws Exception + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertObjectHasProperty */ - private function constraintToString(\PHPUnit\Framework\Constraint\Constraint $constraint, int $position) : string + function assertObjectHasProperty(string $attributeName, object $object, string $message = '') : void { - $prefix = ''; - if ($position > 0) { - $prefix = ' ' . $this->operator() . ' '; - } - if ($this->constraintNeedsParentheses($constraint)) { - return $prefix . '( ' . $constraint->toString() . ' )'; - } - $string = $constraint->toStringInContext($this, $position); - if ($string === '') { - $string = $constraint->toString(); - } - return $prefix . $string; + \PHPUnit\Framework\Assert::assertObjectHasProperty(...func_get_args()); } } - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -final class LogicalAnd extends \PHPUnit\Framework\Constraint\BinaryOperator -{ +if (!function_exists('PHPUnit\\Framework\\assertObjectNotHasProperty')) { /** - * Returns the name of this operator. + * Asserts that an object does not have a specified property. + * + * @throws ExpectationFailedException + * @throws InvalidArgumentException + * @throws Exception + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertObjectNotHasProperty */ - public function operator() : string + function assertObjectNotHasProperty(string $attributeName, object $object, string $message = '') : void { - return 'and'; + \PHPUnit\Framework\Assert::assertObjectNotHasProperty(...func_get_args()); } +} +if (!function_exists('PHPUnit\\Framework\\assertSame')) { /** - * Returns this operator's precedence. + * Asserts that two variables have the same type and value. + * Used on objects, it asserts that two variables reference + * the same object. * - * @see https://www.php.net/manual/en/language.operators.precedence.php + * @throws ExpectationFailedException + * @throws InvalidArgumentException + * + * @psalm-template ExpectedType + * + * @psalm-param ExpectedType $expected + * + * @psalm-assert =ExpectedType $actual + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertSame */ - public function precedence() : int + function assertSame($expected, $actual, string $message = '') : void { - return 22; + \PHPUnit\Framework\Assert::assertSame(...func_get_args()); } +} +if (!function_exists('PHPUnit\\Framework\\assertNotSame')) { /** - * Evaluates the constraint for parameter $other. Returns true if the - * constraint is met, false otherwise. + * Asserts that two variables do not have the same type and value. + * Used on objects, it asserts that two variables do not reference + * the same object. * - * @param mixed $other value or object to evaluate + * @throws ExpectationFailedException + * @throws InvalidArgumentException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertNotSame */ - protected function matches($other) : bool + function assertNotSame($expected, $actual, string $message = '') : void { - foreach ($this->constraints() as $constraint) { - if (!$constraint->evaluate($other, '', \true)) { - return \false; - } - } - return [] !== $this->constraints(); + \PHPUnit\Framework\Assert::assertNotSame(...func_get_args()); } } - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -use function array_map; -use function count; -use function preg_match; -use function preg_quote; -use function preg_replace; -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -final class LogicalNot extends \PHPUnit\Framework\Constraint\UnaryOperator -{ - 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); - $positives = array_map(static function (string $s) { - return '/\\b' . preg_quote($s, '/') . '/'; - }, $positives); - if (count($matches) > 0) { - $nonInput = $matches[2]; - $negatedString = preg_replace('/' . preg_quote($nonInput, '/') . '/', preg_replace($positives, $negatives, $nonInput), $string); - } else { - $negatedString = preg_replace($positives, $negatives, $string); - } - return $negatedString; - } +if (!function_exists('PHPUnit\\Framework\\assertInstanceOf')) { /** - * Returns the name of this operator. + * Asserts that a variable is of a given type. + * + * @throws ExpectationFailedException + * @throws InvalidArgumentException + * @throws Exception + * + * @psalm-template ExpectedType of object + * + * @psalm-param class-string $expected + * + * @psalm-assert =ExpectedType $actual + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertInstanceOf */ - public function operator() : string + function assertInstanceOf(string $expected, $actual, string $message = '') : void { - return 'not'; + \PHPUnit\Framework\Assert::assertInstanceOf(...func_get_args()); } +} +if (!function_exists('PHPUnit\\Framework\\assertNotInstanceOf')) { /** - * Returns this operator's precedence. + * Asserts that a variable is not of a given type. * - * @see https://www.php.net/manual/en/language.operators.precedence.php + * @throws ExpectationFailedException + * @throws InvalidArgumentException + * @throws Exception + * + * @psalm-template ExpectedType of object + * + * @psalm-param class-string $expected + * + * @psalm-assert !ExpectedType $actual + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertNotInstanceOf */ - public function precedence() : int + function assertNotInstanceOf(string $expected, $actual, string $message = '') : void { - return 5; + \PHPUnit\Framework\Assert::assertNotInstanceOf(...func_get_args()); } +} +if (!function_exists('PHPUnit\\Framework\\assertIsArray')) { /** - * Evaluates the constraint for parameter $other. Returns true if the - * constraint is met, false otherwise. + * Asserts that a variable is of type array. * - * @param mixed $other value or object to evaluate + * @throws ExpectationFailedException + * @throws InvalidArgumentException + * + * @psalm-assert array $actual + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertIsArray */ - protected function matches($other) : bool + function assertIsArray($actual, string $message = '') : void { - return !$this->constraint()->evaluate($other, '', \true); + \PHPUnit\Framework\Assert::assertIsArray(...func_get_args()); } +} +if (!function_exists('PHPUnit\\Framework\\assertIsBool')) { /** - * Applies additional transformation to strings returned by toString() or - * failureDescription(). + * Asserts that a variable is of type bool. + * + * @throws ExpectationFailedException + * @throws InvalidArgumentException + * + * @psalm-assert bool $actual + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertIsBool */ - protected function transformString(string $string) : string + function assertIsBool($actual, string $message = '') : void { - return self::negate($string); + \PHPUnit\Framework\Assert::assertIsBool(...func_get_args()); } +} +if (!function_exists('PHPUnit\\Framework\\assertIsFloat')) { /** - * Reduces the sub-expression starting at $this by skipping degenerate - * sub-expression and returns first descendant constraint that starts - * a non-reducible sub-expression. + * Asserts that a variable is of type float. * - * See Constraint::reduce() for more. + * @throws ExpectationFailedException + * @throws InvalidArgumentException + * + * @psalm-assert float $actual + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertIsFloat */ - protected function reduce() : \PHPUnit\Framework\Constraint\Constraint + function assertIsFloat($actual, string $message = '') : void { - $constraint = $this->constraint(); - if ($constraint instanceof self) { - return $constraint->constraint()->reduce(); - } - return parent::reduce(); + \PHPUnit\Framework\Assert::assertIsFloat(...func_get_args()); } } - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -final class LogicalOr extends \PHPUnit\Framework\Constraint\BinaryOperator -{ +if (!function_exists('PHPUnit\\Framework\\assertIsInt')) { /** - * Returns the name of this operator. + * Asserts that a variable is of type int. + * + * @throws ExpectationFailedException + * @throws InvalidArgumentException + * + * @psalm-assert int $actual + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertIsInt */ - public function operator() : string + function assertIsInt($actual, string $message = '') : void { - return 'or'; + \PHPUnit\Framework\Assert::assertIsInt(...func_get_args()); } +} +if (!function_exists('PHPUnit\\Framework\\assertIsNumeric')) { /** - * Returns this operator's precedence. + * Asserts that a variable is of type numeric. * - * @see https://www.php.net/manual/en/language.operators.precedence.php + * @throws ExpectationFailedException + * @throws InvalidArgumentException + * + * @psalm-assert numeric $actual + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertIsNumeric */ - public function precedence() : int + function assertIsNumeric($actual, string $message = '') : void { - return 24; + \PHPUnit\Framework\Assert::assertIsNumeric(...func_get_args()); } +} +if (!function_exists('PHPUnit\\Framework\\assertIsObject')) { /** - * Evaluates the constraint for parameter $other. Returns true if the - * constraint is met, false otherwise. + * Asserts that a variable is of type object. * - * @param mixed $other value or object to evaluate + * @throws ExpectationFailedException + * @throws InvalidArgumentException + * + * @psalm-assert object $actual + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertIsObject */ - public function matches($other) : bool + function assertIsObject($actual, string $message = '') : void { - foreach ($this->constraints() as $constraint) { - if ($constraint->evaluate($other, '', \true)) { - return \true; - } - } - return \false; + \PHPUnit\Framework\Assert::assertIsObject(...func_get_args()); } } - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -use function array_reduce; -use function array_shift; -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -final class LogicalXor extends \PHPUnit\Framework\Constraint\BinaryOperator -{ +if (!function_exists('PHPUnit\\Framework\\assertIsResource')) { /** - * Returns the name of this operator. + * Asserts that a variable is of type resource. + * + * @throws ExpectationFailedException + * @throws InvalidArgumentException + * + * @psalm-assert resource $actual + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertIsResource */ - public function operator() : string + function assertIsResource($actual, string $message = '') : void { - return 'xor'; + \PHPUnit\Framework\Assert::assertIsResource(...func_get_args()); } +} +if (!function_exists('PHPUnit\\Framework\\assertIsClosedResource')) { /** - * Returns this operator's precedence. + * Asserts that a variable is of type resource and is closed. * - * @see https://www.php.net/manual/en/language.operators.precedence.php. + * @throws ExpectationFailedException + * @throws InvalidArgumentException + * + * @psalm-assert resource $actual + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertIsClosedResource */ - public function precedence() : int + function assertIsClosedResource($actual, string $message = '') : void { - return 23; + \PHPUnit\Framework\Assert::assertIsClosedResource(...func_get_args()); } +} +if (!function_exists('PHPUnit\\Framework\\assertIsString')) { /** - * Evaluates the constraint for parameter $other. Returns true if the - * constraint is met, false otherwise. + * Asserts that a variable is of type string. * - * @param mixed $other value or object to evaluate + * @throws ExpectationFailedException + * @throws InvalidArgumentException + * + * @psalm-assert string $actual + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertIsString */ - public function matches($other) : bool + function assertIsString($actual, string $message = '') : void { - $constraints = $this->constraints(); - $initial = array_shift($constraints); - if ($initial === null) { - return \false; - } - return array_reduce($constraints, static function (bool $matches, \PHPUnit\Framework\Constraint\Constraint $constraint) use($other) : bool { - return $matches xor $constraint->evaluate($other, '', \true); - }, $initial->evaluate($other, '', \true)); + \PHPUnit\Framework\Assert::assertIsString(...func_get_args()); } } - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -abstract class Operator extends \PHPUnit\Framework\Constraint\Constraint -{ - /** - * Returns the name of this operator. - */ - public abstract function operator() : string; +if (!function_exists('PHPUnit\\Framework\\assertIsScalar')) { /** - * Returns this operator's precedence. + * Asserts that a variable is of type scalar. * - * @see https://www.php.net/manual/en/language.operators.precedence.php - */ - public abstract function precedence() : int; - /** - * Returns the number of operands. - */ - public abstract function arity() : int; - /** - * Validates $constraint argument. + * @throws ExpectationFailedException + * @throws InvalidArgumentException + * + * @psalm-assert scalar $actual + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertIsScalar */ - protected function checkConstraint($constraint) : \PHPUnit\Framework\Constraint\Constraint + function assertIsScalar($actual, string $message = '') : void { - if (!$constraint instanceof \PHPUnit\Framework\Constraint\Constraint) { - return new \PHPUnit\Framework\Constraint\IsEqual($constraint); - } - return $constraint; + \PHPUnit\Framework\Assert::assertIsScalar(...func_get_args()); } +} +if (!function_exists('PHPUnit\\Framework\\assertIsCallable')) { /** - * Returns true if the $constraint needs to be wrapped with braces. + * Asserts that a variable is of type callable. + * + * @throws ExpectationFailedException + * @throws InvalidArgumentException + * + * @psalm-assert callable $actual + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertIsCallable */ - protected function constraintNeedsParentheses(\PHPUnit\Framework\Constraint\Constraint $constraint) : bool + function assertIsCallable($actual, string $message = '') : void { - return $constraint instanceof self && $constraint->arity() > 1 && $this->precedence() <= $constraint->precedence(); + \PHPUnit\Framework\Assert::assertIsCallable(...func_get_args()); } } - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -use function count; -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -abstract class UnaryOperator extends \PHPUnit\Framework\Constraint\Operator -{ - /** - * @var Constraint - */ - private $constraint; +if (!function_exists('PHPUnit\\Framework\\assertIsIterable')) { /** - * @param Constraint|mixed $constraint + * Asserts that a variable is of type iterable. + * + * @throws ExpectationFailedException + * @throws InvalidArgumentException + * + * @psalm-assert iterable $actual + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertIsIterable */ - public function __construct($constraint) + function assertIsIterable($actual, string $message = '') : void { - $this->constraint = $this->checkConstraint($constraint); + \PHPUnit\Framework\Assert::assertIsIterable(...func_get_args()); } +} +if (!function_exists('PHPUnit\\Framework\\assertIsNotArray')) { /** - * Returns the number of operands (constraints). + * Asserts that a variable is not of type array. + * + * @throws ExpectationFailedException + * @throws InvalidArgumentException + * + * @psalm-assert !array $actual + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertIsNotArray */ - public function arity() : int + function assertIsNotArray($actual, string $message = '') : void { - return 1; + \PHPUnit\Framework\Assert::assertIsNotArray(...func_get_args()); } +} +if (!function_exists('PHPUnit\\Framework\\assertIsNotBool')) { /** - * Returns a string representation of the constraint. + * Asserts that a variable is not of type bool. + * + * @throws ExpectationFailedException + * @throws InvalidArgumentException + * + * @psalm-assert !bool $actual + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertIsNotBool */ - public function toString() : string + function assertIsNotBool($actual, string $message = '') : void { - $reduced = $this->reduce(); - if ($reduced !== $this) { - return $reduced->toString(); - } - $constraint = $this->constraint->reduce(); - if ($this->constraintNeedsParentheses($constraint)) { - return $this->operator() . '( ' . $constraint->toString() . ' )'; - } - $string = $constraint->toStringInContext($this, 0); - if ($string === '') { - return $this->transformString($constraint->toString()); - } - return $string; + \PHPUnit\Framework\Assert::assertIsNotBool(...func_get_args()); } +} +if (!function_exists('PHPUnit\\Framework\\assertIsNotFloat')) { /** - * Counts the number of constraint elements. + * Asserts that a variable is not of type float. + * + * @throws ExpectationFailedException + * @throws InvalidArgumentException + * + * @psalm-assert !float $actual + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertIsNotFloat */ - public function count() : int + function assertIsNotFloat($actual, string $message = '') : void { - return count($this->constraint); + \PHPUnit\Framework\Assert::assertIsNotFloat(...func_get_args()); } +} +if (!function_exists('PHPUnit\\Framework\\assertIsNotInt')) { /** - * Returns the description of the failure. + * Asserts that a variable is not of type int. * - * The beginning of failure messages is "Failed asserting that" in most - * cases. This method should return the second part of that sentence. + * @throws ExpectationFailedException + * @throws InvalidArgumentException * - * @param mixed $other evaluated value or object + * @psalm-assert !int $actual * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertIsNotInt */ - protected function failureDescription($other) : string + function assertIsNotInt($actual, string $message = '') : void { - $reduced = $this->reduce(); - if ($reduced !== $this) { - return $reduced->failureDescription($other); - } - $constraint = $this->constraint->reduce(); - if ($this->constraintNeedsParentheses($constraint)) { - return $this->operator() . '( ' . $constraint->failureDescription($other) . ' )'; - } - $string = $constraint->failureDescriptionInContext($this, 0, $other); - if ($string === '') { - return $this->transformString($constraint->failureDescription($other)); - } - return $string; + \PHPUnit\Framework\Assert::assertIsNotInt(...func_get_args()); } +} +if (!function_exists('PHPUnit\\Framework\\assertIsNotNumeric')) { /** - * Transforms string returned by the memeber constraint's toString() or - * failureDescription() such that it reflects constraint's participation in - * this expression. + * Asserts that a variable is not of type numeric. * - * The method may be overwritten in a subclass to apply default - * transformation in case the operand constraint does not provide its own - * custom strings via toStringInContext() or failureDescriptionInContext(). + * @throws ExpectationFailedException + * @throws InvalidArgumentException * - * @param string $string the string to be transformed + * @psalm-assert !numeric $actual + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertIsNotNumeric */ - protected function transformString(string $string) : string + function assertIsNotNumeric($actual, string $message = '') : void { - return $string; + \PHPUnit\Framework\Assert::assertIsNotNumeric(...func_get_args()); } +} +if (!function_exists('PHPUnit\\Framework\\assertIsNotObject')) { /** - * Provides access to $this->constraint for subclasses. + * Asserts that a variable is not of type object. + * + * @throws ExpectationFailedException + * @throws InvalidArgumentException + * + * @psalm-assert !object $actual + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertIsNotObject */ - protected final function constraint() : \PHPUnit\Framework\Constraint\Constraint + function assertIsNotObject($actual, string $message = '') : void { - return $this->constraint; + \PHPUnit\Framework\Assert::assertIsNotObject(...func_get_args()); } +} +if (!function_exists('PHPUnit\\Framework\\assertIsNotResource')) { /** - * Returns true if the $constraint needs to be wrapped with parentheses. + * Asserts that a variable is not of type resource. + * + * @throws ExpectationFailedException + * @throws InvalidArgumentException + * + * @psalm-assert !resource $actual + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertIsNotResource */ - protected function constraintNeedsParentheses(\PHPUnit\Framework\Constraint\Constraint $constraint) : bool + function assertIsNotResource($actual, string $message = '') : void { - $constraint = $constraint->reduce(); - return $constraint instanceof self || parent::constraintNeedsParentheses($constraint); + \PHPUnit\Framework\Assert::assertIsNotResource(...func_get_args()); } } - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -use function json_decode; -use function json_last_error; -use function sprintf; -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -final class IsJson extends \PHPUnit\Framework\Constraint\Constraint -{ +if (!function_exists('PHPUnit\\Framework\\assertIsNotClosedResource')) { /** - * Returns a string representation of the constraint. + * Asserts that a variable is not of type resource. + * + * @throws ExpectationFailedException + * @throws InvalidArgumentException + * + * @psalm-assert !resource $actual + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertIsNotClosedResource */ - public function toString() : string + function assertIsNotClosedResource($actual, string $message = '') : void { - return 'is valid JSON'; + \PHPUnit\Framework\Assert::assertIsNotClosedResource(...func_get_args()); } +} +if (!function_exists('PHPUnit\\Framework\\assertIsNotString')) { /** - * Evaluates the constraint for parameter $other. Returns true if the - * constraint is met, false otherwise. + * Asserts that a variable is not of type string. * - * @param mixed $other value or object to evaluate + * @throws ExpectationFailedException + * @throws InvalidArgumentException + * + * @psalm-assert !string $actual + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertIsNotString */ - protected function matches($other) : bool + function assertIsNotString($actual, string $message = '') : void { - if ($other === '') { - return \false; - } - json_decode($other); - if (json_last_error()) { - return \false; - } - return \true; + \PHPUnit\Framework\Assert::assertIsNotString(...func_get_args()); } +} +if (!function_exists('PHPUnit\\Framework\\assertIsNotScalar')) { /** - * Returns the description of the failure. + * Asserts that a variable is not of type scalar. * - * The beginning of failure messages is "Failed asserting that" in most - * cases. This method should return the second part of that sentence. + * @throws ExpectationFailedException + * @throws InvalidArgumentException * - * @param mixed $other evaluated value or object + * @psalm-assert !scalar $actual * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertIsNotScalar */ - protected function failureDescription($other) : string + function assertIsNotScalar($actual, string $message = '') : void { - if ($other === '') { - return 'an empty string is valid JSON'; - } - json_decode($other); - $error = (string) \PHPUnit\Framework\Constraint\JsonMatchesErrorMessageProvider::determineJsonError((string) json_last_error()); - return sprintf('%s is valid JSON (%s)', $this->exporter()->shortenedExport($other), $error); + \PHPUnit\Framework\Assert::assertIsNotScalar(...func_get_args()); } } - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -use function preg_match; -use function sprintf; -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -class RegularExpression extends \PHPUnit\Framework\Constraint\Constraint -{ +if (!function_exists('PHPUnit\\Framework\\assertIsNotCallable')) { /** - * @var string + * Asserts that a variable is not of type callable. + * + * @throws ExpectationFailedException + * @throws InvalidArgumentException + * + * @psalm-assert !callable $actual + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertIsNotCallable */ - private $pattern; - public function __construct(string $pattern) + function assertIsNotCallable($actual, string $message = '') : void { - $this->pattern = $pattern; + \PHPUnit\Framework\Assert::assertIsNotCallable(...func_get_args()); } +} +if (!function_exists('PHPUnit\\Framework\\assertIsNotIterable')) { /** - * Returns a string representation of the constraint. + * Asserts that a variable is not of type iterable. + * + * @throws ExpectationFailedException + * @throws InvalidArgumentException + * + * @psalm-assert !iterable $actual + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertIsNotIterable */ - public function toString() : string + function assertIsNotIterable($actual, string $message = '') : void { - return sprintf('matches PCRE pattern "%s"', $this->pattern); + \PHPUnit\Framework\Assert::assertIsNotIterable(...func_get_args()); } +} +if (!function_exists('PHPUnit\\Framework\\assertMatchesRegularExpression')) { /** - * Evaluates the constraint for parameter $other. Returns true if the - * constraint is met, false otherwise. + * Asserts that a string matches a given regular expression. * - * @param mixed $other value or object to evaluate + * @throws ExpectationFailedException + * @throws InvalidArgumentException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertMatchesRegularExpression */ - protected function matches($other) : bool + function assertMatchesRegularExpression(string $pattern, string $string, string $message = '') : void { - return preg_match($this->pattern, $other) > 0; + \PHPUnit\Framework\Assert::assertMatchesRegularExpression(...func_get_args()); } } - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -use function mb_stripos; -use function mb_strtolower; -use function sprintf; -use function strpos; -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -final class StringContains extends \PHPUnit\Framework\Constraint\Constraint -{ - /** - * @var string - */ - private $string; +if (!function_exists('PHPUnit\\Framework\\assertRegExp')) { /** - * @var bool + * Asserts that a string matches a given regular expression. + * + * @throws ExpectationFailedException + * @throws InvalidArgumentException + * + * @codeCoverageIgnore + * + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4086 + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertRegExp */ - private $ignoreCase; - public function __construct(string $string, bool $ignoreCase = \false) + function assertRegExp(string $pattern, string $string, string $message = '') : void { - $this->string = $string; - $this->ignoreCase = $ignoreCase; + \PHPUnit\Framework\Assert::assertRegExp(...func_get_args()); } +} +if (!function_exists('PHPUnit\\Framework\\assertDoesNotMatchRegularExpression')) { /** - * Returns a string representation of the constraint. + * Asserts that a string does not match a given regular expression. + * + * @throws ExpectationFailedException + * @throws InvalidArgumentException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertDoesNotMatchRegularExpression */ - public function toString() : string + function assertDoesNotMatchRegularExpression(string $pattern, string $string, string $message = '') : void { - if ($this->ignoreCase) { - $string = mb_strtolower($this->string, 'UTF-8'); - } else { - $string = $this->string; - } - return sprintf('contains "%s"', $string); + \PHPUnit\Framework\Assert::assertDoesNotMatchRegularExpression(...func_get_args()); } +} +if (!function_exists('PHPUnit\\Framework\\assertNotRegExp')) { /** - * Evaluates the constraint for parameter $other. Returns true if the - * constraint is met, false otherwise. + * Asserts that a string does not match a given regular expression. * - * @param mixed $other value or object to evaluate + * @throws ExpectationFailedException + * @throws InvalidArgumentException + * + * @codeCoverageIgnore + * + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4089 + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertNotRegExp */ - protected function matches($other) : bool + function assertNotRegExp(string $pattern, string $string, string $message = '') : void { - if ('' === $this->string) { - return \true; - } - if ($this->ignoreCase) { - /* - * We must use the multi byte safe version so we can accurately compare non latin upper characters with - * their lowercase equivalents. - */ - return mb_stripos($other, $this->string, 0, 'UTF-8') !== \false; - } - /* - * Use the non multi byte safe functions to see if the string is contained in $other. - * - * This function is very fast and we don't care about the character position in the string. - * - * Additionally, we want this method to be binary safe so we can check if some binary data is in other binary - * data. - */ - return strpos($other, $this->string) !== \false; + \PHPUnit\Framework\Assert::assertNotRegExp(...func_get_args()); } } - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -use function strlen; -use function substr; -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -final class StringEndsWith extends \PHPUnit\Framework\Constraint\Constraint -{ +if (!function_exists('PHPUnit\\Framework\\assertSameSize')) { /** - * @var string + * 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 InvalidArgumentException + * @throws Exception + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertSameSize */ - private $suffix; - public function __construct(string $suffix) + function assertSameSize($expected, $actual, string $message = '') : void { - $this->suffix = $suffix; + \PHPUnit\Framework\Assert::assertSameSize(...func_get_args()); } +} +if (!function_exists('PHPUnit\\Framework\\assertNotSameSize')) { /** - * Returns a string representation of the constraint. + * 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 InvalidArgumentException + * @throws Exception + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertNotSameSize */ - public function toString() : string + function assertNotSameSize($expected, $actual, string $message = '') : void { - return 'ends with "' . $this->suffix . '"'; + \PHPUnit\Framework\Assert::assertNotSameSize(...func_get_args()); } +} +if (!function_exists('PHPUnit\\Framework\\assertStringMatchesFormat')) { /** - * Evaluates the constraint for parameter $other. Returns true if the - * constraint is met, false otherwise. + * Asserts that a string matches a given format string. * - * @param mixed $other value or object to evaluate + * @throws ExpectationFailedException + * @throws InvalidArgumentException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertStringMatchesFormat */ - protected function matches($other) : bool + function assertStringMatchesFormat(string $format, string $string, string $message = '') : void { - return substr($other, 0 - strlen($this->suffix)) === $this->suffix; + \PHPUnit\Framework\Assert::assertStringMatchesFormat(...func_get_args()); } } - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -use const DIRECTORY_SEPARATOR; -use function explode; -use function implode; -use function preg_match; -use function preg_quote; -use function preg_replace; -use function strtr; -use PHPUnit\SebastianBergmann\Diff\Differ; -use PHPUnit\SebastianBergmann\Diff\Output\UnifiedDiffOutputBuilder; -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -final class StringMatchesFormatDescription extends \PHPUnit\Framework\Constraint\RegularExpression -{ +if (!function_exists('PHPUnit\\Framework\\assertStringNotMatchesFormat')) { /** - * @var string + * Asserts that a string does not match a given format string. + * + * @throws ExpectationFailedException + * @throws InvalidArgumentException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertStringNotMatchesFormat */ - private $string; - public function __construct(string $string) + function assertStringNotMatchesFormat(string $format, string $string, string $message = '') : void { - parent::__construct($this->createPatternFromFormat($this->convertNewlines($string))); - $this->string = $string; + \PHPUnit\Framework\Assert::assertStringNotMatchesFormat(...func_get_args()); } +} +if (!function_exists('PHPUnit\\Framework\\assertStringMatchesFormatFile')) { /** - * Evaluates the constraint for parameter $other. Returns true if the - * constraint is met, false otherwise. + * Asserts that a string matches a given format file. * - * @param mixed $other value or object to evaluate + * @throws ExpectationFailedException + * @throws InvalidArgumentException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertStringMatchesFormatFile */ - 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(new UnifiedDiffOutputBuilder("--- 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(string $text) : string + function assertStringMatchesFormatFile(string $formatFile, string $string, string $message = '') : void { - return preg_replace('/\\r\\n/', "\n", $text); + \PHPUnit\Framework\Assert::assertStringMatchesFormatFile(...func_get_args()); } } - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -use function strpos; -use PHPUnit\Framework\InvalidArgumentException; -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -final class StringStartsWith extends \PHPUnit\Framework\Constraint\Constraint -{ +if (!function_exists('PHPUnit\\Framework\\assertStringNotMatchesFormatFile')) { /** - * @var string + * Asserts that a string does not match a given format string. + * + * @throws ExpectationFailedException + * @throws InvalidArgumentException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertStringNotMatchesFormatFile */ - private $prefix; - public function __construct(string $prefix) + function assertStringNotMatchesFormatFile(string $formatFile, string $string, string $message = '') : void { - if ($prefix === '') { - throw InvalidArgumentException::create(1, 'non-empty string'); - } - $this->prefix = $prefix; + \PHPUnit\Framework\Assert::assertStringNotMatchesFormatFile(...func_get_args()); } +} +if (!function_exists('PHPUnit\\Framework\\assertStringStartsWith')) { /** - * Returns a string representation of the constraint. + * Asserts that a string starts with a given prefix. + * + * @throws ExpectationFailedException + * @throws InvalidArgumentException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertStringStartsWith */ - public function toString() : string + function assertStringStartsWith(string $prefix, string $string, string $message = '') : void { - return 'starts with "' . $this->prefix . '"'; + \PHPUnit\Framework\Assert::assertStringStartsWith(...func_get_args()); } +} +if (!function_exists('PHPUnit\\Framework\\assertStringStartsNotWith')) { /** - * Evaluates the constraint for parameter $other. Returns true if the - * constraint is met, false otherwise. + * Asserts that a string starts not with a given prefix. * - * @param mixed $other value or object to evaluate + * @param string $prefix + * @param string $string + * + * @throws ExpectationFailedException + * @throws InvalidArgumentException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertStringStartsNotWith */ - protected function matches($other) : bool + function assertStringStartsNotWith($prefix, $string, string $message = '') : void { - return strpos((string) $other, $this->prefix) === 0; + \PHPUnit\Framework\Assert::assertStringStartsNotWith(...func_get_args()); } } - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -use function array_key_exists; -use function is_array; -use ArrayAccess; -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -final class ArrayHasKey extends \PHPUnit\Framework\Constraint\Constraint -{ +if (!function_exists('PHPUnit\\Framework\\assertStringContainsString')) { /** - * @var int|string + * @throws ExpectationFailedException + * @throws InvalidArgumentException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertStringContainsString */ - private $key; + function assertStringContainsString(string $needle, string $haystack, string $message = '') : void + { + \PHPUnit\Framework\Assert::assertStringContainsString(...func_get_args()); + } +} +if (!function_exists('PHPUnit\\Framework\\assertStringContainsStringIgnoringCase')) { /** - * @param int|string $key + * @throws ExpectationFailedException + * @throws InvalidArgumentException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertStringContainsStringIgnoringCase */ - public function __construct($key) + function assertStringContainsStringIgnoringCase(string $needle, string $haystack, string $message = '') : void { - $this->key = $key; + \PHPUnit\Framework\Assert::assertStringContainsStringIgnoringCase(...func_get_args()); } +} +if (!function_exists('PHPUnit\\Framework\\assertStringNotContainsString')) { /** - * Returns a string representation of the constraint. + * @throws ExpectationFailedException + * @throws InvalidArgumentException * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertStringNotContainsString */ - public function toString() : string + function assertStringNotContainsString(string $needle, string $haystack, string $message = '') : void { - return 'has the key ' . $this->exporter()->export($this->key); + \PHPUnit\Framework\Assert::assertStringNotContainsString(...func_get_args()); } +} +if (!function_exists('PHPUnit\\Framework\\assertStringNotContainsStringIgnoringCase')) { /** - * Evaluates the constraint for parameter $other. Returns true if the - * constraint is met, false otherwise. + * @throws ExpectationFailedException + * @throws InvalidArgumentException * - * @param mixed $other value or object to evaluate + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertStringNotContainsStringIgnoringCase */ - protected function matches($other) : bool + function assertStringNotContainsStringIgnoringCase(string $needle, string $haystack, string $message = '') : void { - if (is_array($other)) { - return array_key_exists($this->key, $other); - } - if ($other instanceof ArrayAccess) { - return $other->offsetExists($this->key); - } - return \false; + \PHPUnit\Framework\Assert::assertStringNotContainsStringIgnoringCase(...func_get_args()); } +} +if (!function_exists('PHPUnit\\Framework\\assertStringEndsWith')) { /** - * Returns the description of the failure. + * Asserts that a string ends with a given suffix. * - * The beginning of failure messages is "Failed asserting that" in most - * cases. This method should return the second part of that sentence. + * @throws ExpectationFailedException + * @throws InvalidArgumentException * - * @param mixed $other evaluated value or object + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @see Assert::assertStringEndsWith */ - protected function failureDescription($other) : string + function assertStringEndsWith(string $suffix, string $string, string $message = '') : void { - return 'an array ' . $this->toString(); + \PHPUnit\Framework\Assert::assertStringEndsWith(...func_get_args()); } } - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -use function is_array; -use function sprintf; -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -abstract class TraversableContains extends \PHPUnit\Framework\Constraint\Constraint -{ +if (!function_exists('PHPUnit\\Framework\\assertStringEndsNotWith')) { /** - * @var mixed + * Asserts that a string ends not with a given suffix. + * + * @throws ExpectationFailedException + * @throws InvalidArgumentException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertStringEndsNotWith */ - private $value; - public function __construct($value) + function assertStringEndsNotWith(string $suffix, string $string, string $message = '') : void { - $this->value = $value; + \PHPUnit\Framework\Assert::assertStringEndsNotWith(...func_get_args()); } +} +if (!function_exists('PHPUnit\\Framework\\assertXmlFileEqualsXmlFile')) { /** - * Returns a string representation of the constraint. + * Asserts that two XML files are equal. * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException + * @throws InvalidArgumentException + * @throws Exception + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertXmlFileEqualsXmlFile */ - public function toString() : string + function assertXmlFileEqualsXmlFile(string $expectedFile, string $actualFile, string $message = '') : void { - return 'contains ' . $this->exporter()->export($this->value); + \PHPUnit\Framework\Assert::assertXmlFileEqualsXmlFile(...func_get_args()); } +} +if (!function_exists('PHPUnit\\Framework\\assertXmlFileNotEqualsXmlFile')) { /** - * Returns the description of the failure. + * Asserts that two XML files are not equal. * - * The beginning of failure messages is "Failed asserting that" in most - * cases. This method should return the second part of that sentence. + * @throws ExpectationFailedException + * @throws InvalidArgumentException + * @throws \PHPUnit\Util\Exception * - * @param mixed $other evaluated value or object + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @see Assert::assertXmlFileNotEqualsXmlFile */ - protected function failureDescription($other) : string - { - return sprintf('%s %s', is_array($other) ? 'an array' : 'a traversable', $this->toString()); - } - protected function value() + function assertXmlFileNotEqualsXmlFile(string $expectedFile, string $actualFile, string $message = '') : void { - return $this->value; + \PHPUnit\Framework\Assert::assertXmlFileNotEqualsXmlFile(...func_get_args()); } } - - * - * 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; -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -final class TraversableContainsEqual extends \PHPUnit\Framework\Constraint\TraversableContains -{ +if (!function_exists('PHPUnit\\Framework\\assertXmlStringEqualsXmlFile')) { /** - * Evaluates the constraint for parameter $other. Returns true if the - * constraint is met, false otherwise. + * Asserts that two XML documents are equal. * - * @param mixed $other value or object to evaluate + * @param DOMDocument|string $actualXml + * + * @throws ExpectationFailedException + * @throws InvalidArgumentException + * @throws \PHPUnit\Util\Xml\Exception + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertXmlStringEqualsXmlFile */ - protected function matches($other) : bool + function assertXmlStringEqualsXmlFile(string $expectedFile, $actualXml, string $message = '') : void { - if ($other instanceof SplObjectStorage) { - return $other->contains($this->value()); - } - foreach ($other as $element) { - /* @noinspection TypeUnsafeComparisonInspection */ - if ($this->value() == $element) { - return \true; - } - } - return \false; + \PHPUnit\Framework\Assert::assertXmlStringEqualsXmlFile(...func_get_args()); } } - - * - * 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; -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -final class TraversableContainsIdentical extends \PHPUnit\Framework\Constraint\TraversableContains -{ +if (!function_exists('PHPUnit\\Framework\\assertXmlStringNotEqualsXmlFile')) { /** - * Evaluates the constraint for parameter $other. Returns true if the - * constraint is met, false otherwise. + * Asserts that two XML documents are not equal. * - * @param mixed $other value or object to evaluate + * @param DOMDocument|string $actualXml + * + * @throws ExpectationFailedException + * @throws InvalidArgumentException + * @throws \PHPUnit\Util\Xml\Exception + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertXmlStringNotEqualsXmlFile */ - protected function matches($other) : bool + function assertXmlStringNotEqualsXmlFile(string $expectedFile, $actualXml, string $message = '') : void { - if ($other instanceof SplObjectStorage) { - return $other->contains($this->value()); - } - foreach ($other as $element) { - if ($this->value() === $element) { - return \true; - } - } - return \false; + \PHPUnit\Framework\Assert::assertXmlStringNotEqualsXmlFile(...func_get_args()); } } - - * - * 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 Traversable; -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -final class TraversableContainsOnly extends \PHPUnit\Framework\Constraint\Constraint -{ - /** - * @var Constraint - */ - private $constraint; - /** - * @var string - */ - private $type; +if (!function_exists('PHPUnit\\Framework\\assertXmlStringEqualsXmlString')) { /** - * @throws \PHPUnit\Framework\Exception + * Asserts that two XML documents are equal. + * + * @param DOMDocument|string $expectedXml + * @param DOMDocument|string $actualXml + * + * @throws ExpectationFailedException + * @throws InvalidArgumentException + * @throws \PHPUnit\Util\Xml\Exception + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertXmlStringEqualsXmlString */ - public function __construct(string $type, bool $isNativeType = \true) + function assertXmlStringEqualsXmlString($expectedXml, $actualXml, string $message = '') : void { - if ($isNativeType) { - $this->constraint = new \PHPUnit\Framework\Constraint\IsType($type); - } else { - $this->constraint = new \PHPUnit\Framework\Constraint\IsInstanceOf($type); - } - $this->type = $type; + \PHPUnit\Framework\Assert::assertXmlStringEqualsXmlString(...func_get_args()); } +} +if (!function_exists('PHPUnit\\Framework\\assertXmlStringNotEqualsXmlString')) { /** - * Evaluates the constraint for parameter $other. + * Asserts that two XML documents are not equal. * - * If $returnResult is set to false (the default), an exception is thrown - * in case of a failure. null is returned otherwise. + * @param DOMDocument|string $expectedXml + * @param DOMDocument|string $actualXml * - * 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 InvalidArgumentException + * @throws \PHPUnit\Util\Xml\Exception * - * @param mixed|Traversable $other + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException + * @see Assert::assertXmlStringNotEqualsXmlString */ - public function evaluate($other, string $description = '', bool $returnResult = \false) : ?bool + function assertXmlStringNotEqualsXmlString($expectedXml, $actualXml, string $message = '') : void { - $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); - } - return null; + \PHPUnit\Framework\Assert::assertXmlStringNotEqualsXmlString(...func_get_args()); } +} +if (!function_exists('PHPUnit\\Framework\\assertEqualXMLStructure')) { /** - * Returns a string representation of the constraint. + * Asserts that a hierarchy of DOMElements matches. + * + * @throws AssertionFailedError + * @throws ExpectationFailedException + * @throws InvalidArgumentException + * + * @codeCoverageIgnore + * + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4091 + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertEqualXMLStructure */ - public function toString() : string + function assertEqualXMLStructure(DOMElement $expectedElement, DOMElement $actualElement, bool $checkAttributes = \false, string $message = '') : void { - return 'contains only values of type "' . $this->type . '"'; + \PHPUnit\Framework\Assert::assertEqualXMLStructure(...func_get_args()); } } - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -use function sprintf; -use ReflectionClass; -use ReflectionException; -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -final class IsInstanceOf extends \PHPUnit\Framework\Constraint\Constraint -{ +if (!function_exists('PHPUnit\\Framework\\assertThat')) { /** - * @var string + * Evaluates a PHPUnit\Framework\Constraint matcher object. + * + * @throws ExpectationFailedException + * @throws InvalidArgumentException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertThat */ - private $className; - public function __construct(string $className) + function assertThat($value, Constraint $constraint, string $message = '') : void { - $this->className = $className; + \PHPUnit\Framework\Assert::assertThat(...func_get_args()); } +} +if (!function_exists('PHPUnit\\Framework\\assertJson')) { /** - * Returns a string representation of the constraint. + * Asserts that a string is a valid JSON string. + * + * @throws ExpectationFailedException + * @throws InvalidArgumentException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertJson */ - public function toString() : string + function assertJson(string $actualJson, string $message = '') : void { - return sprintf('is instance of %s "%s"', $this->getType(), $this->className); + \PHPUnit\Framework\Assert::assertJson(...func_get_args()); } +} +if (!function_exists('PHPUnit\\Framework\\assertJsonStringEqualsJsonString')) { /** - * Evaluates the constraint for parameter $other. Returns true if the - * constraint is met, false otherwise. + * Asserts that two given JSON encoded objects or arrays are equal. * - * @param mixed $other value or object to evaluate + * @throws ExpectationFailedException + * @throws InvalidArgumentException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertJsonStringEqualsJsonString */ - protected function matches($other) : bool + function assertJsonStringEqualsJsonString(string $expectedJson, string $actualJson, string $message = '') : void { - return $other instanceof $this->className; + \PHPUnit\Framework\Assert::assertJsonStringEqualsJsonString(...func_get_args()); } +} +if (!function_exists('PHPUnit\\Framework\\assertJsonStringNotEqualsJsonString')) { /** - * Returns the description of the failure. + * Asserts that two given JSON encoded objects or arrays are not equal. * - * The beginning of failure messages is "Failed asserting that" in most - * cases. This method should return the second part of that sentence. + * @param string $expectedJson + * @param string $actualJson * - * @param mixed $other evaluated value or object + * @throws ExpectationFailedException + * @throws InvalidArgumentException * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertJsonStringNotEqualsJsonString */ - 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 + function assertJsonStringNotEqualsJsonString($expectedJson, $actualJson, string $message = '') : void { - try { - $reflection = new ReflectionClass($this->className); - if ($reflection->isInterface()) { - return 'interface'; - } - } catch (ReflectionException $e) { - } - return 'class'; + \PHPUnit\Framework\Assert::assertJsonStringNotEqualsJsonString(...func_get_args()); } } - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -final class IsNull extends \PHPUnit\Framework\Constraint\Constraint -{ +if (!function_exists('PHPUnit\\Framework\\assertJsonStringEqualsJsonFile')) { /** - * Returns a string representation of the constraint. + * Asserts that the generated JSON encoded object and the content of the given file are equal. + * + * @throws ExpectationFailedException + * @throws InvalidArgumentException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertJsonStringEqualsJsonFile */ - public function toString() : string + function assertJsonStringEqualsJsonFile(string $expectedFile, string $actualJson, string $message = '') : void { - return 'is null'; + \PHPUnit\Framework\Assert::assertJsonStringEqualsJsonFile(...func_get_args()); } +} +if (!function_exists('PHPUnit\\Framework\\assertJsonStringNotEqualsJsonFile')) { /** - * Evaluates the constraint for parameter $other. Returns true if the - * constraint is met, false otherwise. + * Asserts that the generated JSON encoded object and the content of the given file are not equal. * - * @param mixed $other value or object to evaluate + * @throws ExpectationFailedException + * @throws InvalidArgumentException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertJsonStringNotEqualsJsonFile */ - protected function matches($other) : bool + function assertJsonStringNotEqualsJsonFile(string $expectedFile, string $actualJson, string $message = '') : void { - return $other === null; + \PHPUnit\Framework\Assert::assertJsonStringNotEqualsJsonFile(...func_get_args()); } } - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -use function gettype; -use function is_array; -use function is_bool; -use function is_callable; -use function is_float; -use function is_int; -use function is_iterable; -use function is_numeric; -use function is_object; -use function is_scalar; -use function is_string; -use function sprintf; -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -final class IsType extends \PHPUnit\Framework\Constraint\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_CLOSED_RESOURCE = 'resource (closed)'; - /** - * @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 - */ - 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, 'resource (closed)' => \true, 'string' => \true, 'scalar' => \true, 'callable' => \true, 'iterable' => \true]; - /** - * @var string - */ - private $type; +if (!function_exists('PHPUnit\\Framework\\assertJsonFileEqualsJsonFile')) { /** - * @throws \PHPUnit\Framework\Exception + * Asserts that two JSON files are equal. + * + * @throws ExpectationFailedException + * @throws InvalidArgumentException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertJsonFileEqualsJsonFile */ - public function __construct(string $type) + function assertJsonFileEqualsJsonFile(string $expectedFile, string $actualFile, string $message = '') : void { - 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; + \PHPUnit\Framework\Assert::assertJsonFileEqualsJsonFile(...func_get_args()); } +} +if (!function_exists('PHPUnit\\Framework\\assertJsonFileNotEqualsJsonFile')) { /** - * Returns a string representation of the constraint. + * Asserts that two JSON files are not equal. + * + * @throws ExpectationFailedException + * @throws InvalidArgumentException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertJsonFileNotEqualsJsonFile */ - public function toString() : string + function assertJsonFileNotEqualsJsonFile(string $expectedFile, string $actualFile, string $message = '') : void { - return sprintf('is of type "%s"', $this->type); + \PHPUnit\Framework\Assert::assertJsonFileNotEqualsJsonFile(...func_get_args()); } - /** - * 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 (!function_exists('PHPUnit\\Framework\\logicalAnd')) { + function logicalAnd() : LogicalAnd { - 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': - $type = gettype($other); - return $type === 'resource' || $type === 'resource (closed)'; - case 'resource (closed)': - return gettype($other) === 'resource (closed)'; - case 'scalar': - return is_scalar($other); - case 'callable': - return is_callable($other); - case 'iterable': - return is_iterable($other); - default: - return \false; - } + return \PHPUnit\Framework\Assert::logicalAnd(...func_get_args()); } } - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework; - -use function explode; -use PHPUnit\Util\Test as TestUtil; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class DataProviderTestSuite extends \PHPUnit\Framework\TestSuite -{ - /** - * @var list - */ - private $dependencies = []; - /** - * @param list $dependencies - */ - public function setDependencies(array $dependencies) : void +if (!function_exists('PHPUnit\\Framework\\logicalOr')) { + function logicalOr() : LogicalOr { - $this->dependencies = $dependencies; - foreach ($this->tests as $test) { - if (!$test instanceof \PHPUnit\Framework\TestCase) { - // @codeCoverageIgnoreStart - continue; - // @codeCoverageIgnoreStart - } - $test->setDependencies($dependencies); - } + return \PHPUnit\Framework\Assert::logicalOr(...func_get_args()); } - /** - * @return list - */ - public function provides() : array +} +if (!function_exists('PHPUnit\\Framework\\logicalNot')) { + function logicalNot(Constraint $constraint) : LogicalNot { - if ($this->providedTests === null) { - $this->providedTests = [new \PHPUnit\Framework\ExecutionOrderDependency($this->getName())]; - } - return $this->providedTests; + return \PHPUnit\Framework\Assert::logicalNot(...func_get_args()); } - /** - * @return list - */ - public function requires() : array +} +if (!function_exists('PHPUnit\\Framework\\logicalXor')) { + function logicalXor() : LogicalXor { - // A DataProviderTestSuite does not have to traverse its child tests - // as these are inherited and cannot reference dataProvider rows directly - return $this->dependencies; + return \PHPUnit\Framework\Assert::logicalXor(...func_get_args()); } - /** - * Returns the size of the each test created using the data provider(s). - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public function getSize() : int +} +if (!function_exists('PHPUnit\\Framework\\anything')) { + function anything() : IsAnything { - [$className, $methodName] = explode('::', $this->getName()); - return TestUtil::getSize($className, $methodName); + return \PHPUnit\Framework\Assert::anything(...func_get_args()); } } - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Error; - -/** - * @internal - */ -final class Deprecated extends \PHPUnit\Framework\Error\Error -{ +if (!function_exists('PHPUnit\\Framework\\isTrue')) { + function isTrue() : IsTrue + { + return \PHPUnit\Framework\Assert::isTrue(...func_get_args()); + } } - - * - * 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; -/** - * @internal - */ -class Error extends Exception -{ - public function __construct(string $message, int $code, string $file, int $line, \Exception $previous = null) +if (!function_exists('PHPUnit\\Framework\\callback')) { + function callback(callable $callback) : Callback { - parent::__construct($message, $code, $previous); - $this->file = $file; - $this->line = $line; + return \PHPUnit\Framework\Assert::callback(...func_get_args()); } } - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Error; - -/** - * @internal - */ -final class Notice extends \PHPUnit\Framework\Error\Error -{ +if (!function_exists('PHPUnit\\Framework\\isFalse')) { + function isFalse() : IsFalse + { + return \PHPUnit\Framework\Assert::isFalse(...func_get_args()); + } } - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Error; - -/** - * @internal - */ -final class Warning extends \PHPUnit\Framework\Error\Error -{ +if (!function_exists('PHPUnit\\Framework\\isJson')) { + function isJson() : IsJson + { + return \PHPUnit\Framework\Assert::isJson(...func_get_args()); + } } - - * - * 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 ErrorTestCase extends \PHPUnit\Framework\TestCase -{ +if (!function_exists('PHPUnit\\Framework\\isNull')) { + function isNull() : IsNull + { + return \PHPUnit\Framework\Assert::isNull(...func_get_args()); + } +} +if (!function_exists('PHPUnit\\Framework\\isFinite')) { + function isFinite() : IsFinite + { + return \PHPUnit\Framework\Assert::isFinite(...func_get_args()); + } +} +if (!function_exists('PHPUnit\\Framework\\isInfinite')) { + function isInfinite() : IsInfinite + { + return \PHPUnit\Framework\Assert::isInfinite(...func_get_args()); + } +} +if (!function_exists('PHPUnit\\Framework\\isNan')) { + function isNan() : IsNan + { + return \PHPUnit\Framework\Assert::isNan(...func_get_args()); + } +} +if (!function_exists('PHPUnit\\Framework\\containsEqual')) { + function containsEqual($value) : TraversableContainsEqual + { + return \PHPUnit\Framework\Assert::containsEqual(...func_get_args()); + } +} +if (!function_exists('PHPUnit\\Framework\\containsIdentical')) { + function containsIdentical($value) : TraversableContainsIdentical + { + return \PHPUnit\Framework\Assert::containsIdentical(...func_get_args()); + } +} +if (!function_exists('PHPUnit\\Framework\\containsOnly')) { + function containsOnly(string $type) : TraversableContainsOnly + { + return \PHPUnit\Framework\Assert::containsOnly(...func_get_args()); + } +} +if (!function_exists('PHPUnit\\Framework\\containsOnlyInstancesOf')) { + function containsOnlyInstancesOf(string $className) : TraversableContainsOnly + { + return \PHPUnit\Framework\Assert::containsOnlyInstancesOf(...func_get_args()); + } +} +if (!function_exists('PHPUnit\\Framework\\arrayHasKey')) { + function arrayHasKey($key) : ArrayHasKey + { + return \PHPUnit\Framework\Assert::arrayHasKey(...func_get_args()); + } +} +if (!function_exists('PHPUnit\\Framework\\equalTo')) { + function equalTo($value) : IsEqual + { + return \PHPUnit\Framework\Assert::equalTo(...func_get_args()); + } +} +if (!function_exists('PHPUnit\\Framework\\equalToCanonicalizing')) { + function equalToCanonicalizing($value) : IsEqualCanonicalizing + { + return \PHPUnit\Framework\Assert::equalToCanonicalizing(...func_get_args()); + } +} +if (!function_exists('PHPUnit\\Framework\\equalToIgnoringCase')) { + function equalToIgnoringCase($value) : IsEqualIgnoringCase + { + return \PHPUnit\Framework\Assert::equalToIgnoringCase(...func_get_args()); + } +} +if (!function_exists('PHPUnit\\Framework\\equalToWithDelta')) { + function equalToWithDelta($value, float $delta) : IsEqualWithDelta + { + return \PHPUnit\Framework\Assert::equalToWithDelta(...func_get_args()); + } +} +if (!function_exists('PHPUnit\\Framework\\isEmpty')) { + function isEmpty() : IsEmpty + { + return \PHPUnit\Framework\Assert::isEmpty(...func_get_args()); + } +} +if (!function_exists('PHPUnit\\Framework\\isWritable')) { + function isWritable() : IsWritable + { + return \PHPUnit\Framework\Assert::isWritable(...func_get_args()); + } +} +if (!function_exists('PHPUnit\\Framework\\isReadable')) { + function isReadable() : IsReadable + { + return \PHPUnit\Framework\Assert::isReadable(...func_get_args()); + } +} +if (!function_exists('PHPUnit\\Framework\\directoryExists')) { + function directoryExists() : DirectoryExists + { + return \PHPUnit\Framework\Assert::directoryExists(...func_get_args()); + } +} +if (!function_exists('PHPUnit\\Framework\\fileExists')) { + function fileExists() : FileExists + { + return \PHPUnit\Framework\Assert::fileExists(...func_get_args()); + } +} +if (!function_exists('PHPUnit\\Framework\\greaterThan')) { + function greaterThan($value) : GreaterThan + { + return \PHPUnit\Framework\Assert::greaterThan(...func_get_args()); + } +} +if (!function_exists('PHPUnit\\Framework\\greaterThanOrEqual')) { + function greaterThanOrEqual($value) : LogicalOr + { + return \PHPUnit\Framework\Assert::greaterThanOrEqual(...func_get_args()); + } +} +if (!function_exists('PHPUnit\\Framework\\classHasAttribute')) { + function classHasAttribute(string $attributeName) : ClassHasAttribute + { + return \PHPUnit\Framework\Assert::classHasAttribute(...func_get_args()); + } +} +if (!function_exists('PHPUnit\\Framework\\classHasStaticAttribute')) { + function classHasStaticAttribute(string $attributeName) : ClassHasStaticAttribute + { + return \PHPUnit\Framework\Assert::classHasStaticAttribute(...func_get_args()); + } +} +if (!function_exists('PHPUnit\\Framework\\objectHasAttribute')) { + function objectHasAttribute($attributeName) : ObjectHasAttribute + { + return \PHPUnit\Framework\Assert::objectHasAttribute(...func_get_args()); + } +} +if (!function_exists('PHPUnit\\Framework\\identicalTo')) { + function identicalTo($value) : IsIdentical + { + return \PHPUnit\Framework\Assert::identicalTo(...func_get_args()); + } +} +if (!function_exists('PHPUnit\\Framework\\isInstanceOf')) { + function isInstanceOf(string $className) : IsInstanceOf + { + return \PHPUnit\Framework\Assert::isInstanceOf(...func_get_args()); + } +} +if (!function_exists('PHPUnit\\Framework\\isType')) { + function isType(string $type) : IsType + { + return \PHPUnit\Framework\Assert::isType(...func_get_args()); + } +} +if (!function_exists('PHPUnit\\Framework\\lessThan')) { + function lessThan($value) : LessThan + { + return \PHPUnit\Framework\Assert::lessThan(...func_get_args()); + } +} +if (!function_exists('PHPUnit\\Framework\\lessThanOrEqual')) { + function lessThanOrEqual($value) : LogicalOr + { + return \PHPUnit\Framework\Assert::lessThanOrEqual(...func_get_args()); + } +} +if (!function_exists('PHPUnit\\Framework\\matchesRegularExpression')) { + function matchesRegularExpression(string $pattern) : RegularExpression + { + return \PHPUnit\Framework\Assert::matchesRegularExpression(...func_get_args()); + } +} +if (!function_exists('PHPUnit\\Framework\\matches')) { + function matches(string $string) : StringMatchesFormatDescription + { + return \PHPUnit\Framework\Assert::matches(...func_get_args()); + } +} +if (!function_exists('PHPUnit\\Framework\\stringStartsWith')) { + function stringStartsWith($prefix) : StringStartsWith + { + return \PHPUnit\Framework\Assert::stringStartsWith(...func_get_args()); + } +} +if (!function_exists('PHPUnit\\Framework\\stringContains')) { + function stringContains(string $string, bool $case = \true) : StringContains + { + return \PHPUnit\Framework\Assert::stringContains(...func_get_args()); + } +} +if (!function_exists('PHPUnit\\Framework\\stringEndsWith')) { + function stringEndsWith(string $suffix) : StringEndsWith + { + return \PHPUnit\Framework\Assert::stringEndsWith(...func_get_args()); + } +} +if (!function_exists('PHPUnit\\Framework\\countOf')) { + function countOf(int $count) : Count + { + return \PHPUnit\Framework\Assert::countOf(...func_get_args()); + } +} +if (!function_exists('PHPUnit\\Framework\\objectEquals')) { + function objectEquals(object $object, string $method = 'equals') : ObjectEquals + { + return \PHPUnit\Framework\Assert::objectEquals(...func_get_args()); + } +} +if (!function_exists('PHPUnit\\Framework\\any')) { /** - * @var ?bool + * Returns a matcher that matches when the method is executed + * zero or more times. */ - protected $backupGlobals = \false; + function any() : AnyInvokedCountMatcher + { + return new AnyInvokedCountMatcher(); + } +} +if (!function_exists('PHPUnit\\Framework\\never')) { /** - * @var ?bool + * Returns a matcher that matches when the method is never executed. */ - protected $backupStaticAttributes = \false; + function never() : InvokedCountMatcher + { + return new InvokedCountMatcher(0); + } +} +if (!function_exists('PHPUnit\\Framework\\atLeast')) { /** - * @var ?bool + * Returns a matcher that matches when the method is executed + * at least N times. */ - protected $runTestInSeparateProcess = \false; + function atLeast(int $requiredInvocations) : InvokedAtLeastCountMatcher + { + return new InvokedAtLeastCountMatcher($requiredInvocations); + } +} +if (!function_exists('PHPUnit\\Framework\\atLeastOnce')) { /** - * @var string + * Returns a matcher that matches when the method is executed at least once. */ - private $message; - public function __construct(string $message = '') + function atLeastOnce() : InvokedAtLeastOnceMatcher { - $this->message = $message; - parent::__construct('Error'); + return new InvokedAtLeastOnceMatcher(); } - public function getMessage() : string +} +if (!function_exists('PHPUnit\\Framework\\once')) { + /** + * Returns a matcher that matches when the method is executed exactly once. + */ + function once() : InvokedCountMatcher { - return $this->message; + return new InvokedCountMatcher(1); } +} +if (!function_exists('PHPUnit\\Framework\\exactly')) { /** - * Returns a string representation of the test case. + * Returns a matcher that matches when the method is executed + * exactly $count times. */ - public function toString() : string + function exactly(int $count) : InvokedCountMatcher { - return 'Error'; + return new InvokedCountMatcher($count); } +} +if (!function_exists('PHPUnit\\Framework\\atMost')) { /** - * @throws Exception + * Returns a matcher that matches when the method is executed + * at most N times. + */ + function atMost(int $allowedInvocations) : InvokedAtMostCountMatcher + { + return new InvokedAtMostCountMatcher($allowedInvocations); + } +} +if (!function_exists('PHPUnit\\Framework\\at')) { + /** + * Returns a matcher that matches when the method is executed + * at the given index. + */ + function at(int $index) : InvokedAtIndexMatcher + { + return new InvokedAtIndexMatcher($index); + } +} +if (!function_exists('PHPUnit\\Framework\\returnValue')) { + function returnValue($value) : ReturnStub + { + return new ReturnStub($value); + } +} +if (!function_exists('PHPUnit\\Framework\\returnValueMap')) { + function returnValueMap(array $valueMap) : ReturnValueMapStub + { + return new ReturnValueMapStub($valueMap); + } +} +if (!function_exists('PHPUnit\\Framework\\returnArgument')) { + function returnArgument(int $argumentIndex) : ReturnArgumentStub + { + return new ReturnArgumentStub($argumentIndex); + } +} +if (!function_exists('PHPUnit\\Framework\\returnCallback')) { + function returnCallback($callback) : ReturnCallbackStub + { + return new ReturnCallbackStub($callback); + } +} +if (!function_exists('PHPUnit\\Framework\\returnSelf')) { + /** + * Returns the current object. * - * @psalm-return never-return + * This method is useful when mocking a fluent interface. */ - protected function runTest() : void + function returnSelf() : ReturnSelfStub { - throw new \PHPUnit\Framework\Error($this->message); + return new ReturnSelfStub(); + } +} +if (!function_exists('PHPUnit\\Framework\\throwException')) { + function throwException(Throwable $exception) : ExceptionStub + { + return new ExceptionStub($exception); + } +} +if (!function_exists('PHPUnit\\Framework\\onConsecutiveCalls')) { + function onConsecutiveCalls() : ConsecutiveCallsStub + { + $args = func_get_args(); + return new ConsecutiveCallsStub($args); } } getMessage() . PHP_EOL; + return $other === \false; } } getMessage(); + 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; } } 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 + * + * @psalm-param CallbackInput $other + */ + protected function matches($other) : bool + { + return ($this->callback)($other); + } } expectedCount = $expected; } - public function __toString() : string + public function toString() : string { - return $this->getMessage() . PHP_EOL; + return sprintf('count matches %d', $this->expectedCount); } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework; - -use const PHP_EOL; -use function sprintf; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class ComparisonMethodDoesNotDeclareBoolReturnTypeException extends \PHPUnit\Framework\Exception -{ - public function __construct(string $className, string $methodName) + /** + * Evaluates the constraint for parameter $other. Returns true if the + * constraint is met, false otherwise. + * + * @throws Exception + */ + protected function matches($other) : bool { - parent::__construct(sprintf('Comparison method %s::%s() does not declare bool return type.', $className, $methodName), 0, null); + return $this->expectedCount === $this->getCountOf($other); } - public function __toString() : string + /** + * @throws Exception + */ + protected function getCountOf($other) : ?int { - return $this->getMessage() . PHP_EOL; + if ($other instanceof Countable || is_array($other)) { + return count($other); + } + if ($other instanceof EmptyIterator) { + return 0; + } + if ($other instanceof Traversable) { + while ($other instanceof IteratorAggregate) { + try { + $other = $other->getIterator(); + } catch (\Exception $e) { + throw new Exception($e->getMessage(), $e->getCode(), $e); + } + } + $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; } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework; - -use const PHP_EOL; -use function sprintf; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class ComparisonMethodDoesNotDeclareExactlyOneParameterException extends \PHPUnit\Framework\Exception -{ - public function __construct(string $className, string $methodName) + /** + * Returns the total number of iterations from a generator. + * This will fully exhaust the generator. + */ + protected function getCountOfGenerator(Generator $generator) : int { - parent::__construct(sprintf('Comparison method %s::%s() does not declare exactly one parameter.', $className, $methodName), 0, null); + for ($count = 0; $generator->valid(); $generator->next()) { + $count++; + } + return $count; } - public function __toString() : string + /** + * Returns the description of the failure. + * + * The beginning of failure messages is "Failed asserting that" in most + * cases. This method should return the second part of that sentence. + * + * @param mixed $other evaluated value or object + */ + protected function failureDescription($other) : string { - return $this->getMessage() . PHP_EOL; + return sprintf('actual size %d matches expected size %d', (int) $this->getCountOf($other), $this->expectedCount); } } value = $value; } - public function __toString() : string + /** + * Returns a string representation of the constraint. + * + * @throws InvalidArgumentException + */ + public function toString() : string { - return $this->getMessage() . PHP_EOL; + 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; } } getMessage() . PHP_EOL; + 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()); } } value = $value; + } + /** + * Returns a string representation of the constraint. + * + * @throws 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; + } } getMessage(); + parent::__construct((int) $this->getCountOf($expected)); } } serializableTrace = $this->getTrace(); - foreach (array_keys($this->serializableTrace) as $key) { - unset($this->serializableTrace[$key]['args']); + $success = \false; + if ($this->matches($other)) { + $success = \true; + } + if ($returnResult) { + return $success; + } + if (!$success) { + $this->fail($other, $description); } + return null; } - public function __toString() : string + /** + * Counts the number of constraint elements. + */ + public function count() : int { - $string = \PHPUnit\Framework\TestFailure::exceptionToString($this); - if ($trace = Filter::getFilteredStacktrace($this)) { - $string .= "\n" . $trace; - } - return $string; + return 1; } - public function __sleep() : array + protected function exporter() : Exporter { - return array_keys(get_object_vars($this)); + if ($this->exporter === null) { + $this->exporter = new Exporter(); + } + return $this->exporter; } /** - * Returns the serializable trace (without 'args'). + * 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 */ - public function getSerializableTrace() : array + protected function matches($other) : bool { - return $this->serializableTrace; + return \false; } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework; - -use Exception; -use PHPUnit\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 \PHPUnit\Framework\AssertionFailedError -{ /** - * @var ComparisonFailure + * 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 + * + * @throws ExpectationFailedException + * @throws InvalidArgumentException + * + * @psalm-return never-return */ - protected $comparisonFailure; - public function __construct(string $message, ComparisonFailure $comparisonFailure = null, Exception $previous = null) + protected function fail($other, $description, ?ComparisonFailure $comparisonFailure = null) : void { - $this->comparisonFailure = $comparisonFailure; - parent::__construct($message, 0, $previous); + $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); } - public function getComparisonFailure() : ?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 $this->comparisonFailure; + return ''; } -} - - * - * 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 \PHPUnit\Framework\AssertionFailedError implements \PHPUnit\Framework\IncompleteTest -{ -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework; - -use function debug_backtrace; -use function in_array; -use function lcfirst; -use function sprintf; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class InvalidArgumentException extends \PHPUnit\Framework\Exception -{ - public static function create(int $argument, string $type) : self + /** + * 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 InvalidArgumentException + */ + protected function failureDescription($other) : string { - $stack = debug_backtrace(); - $function = $stack[1]['function']; - if (isset($stack[1]['class'])) { - $function = sprintf('%s::%s', $stack[1]['class'], $stack[1]['function']); - } - return new self(sprintf('Argument #%d of %s() must be %s %s', $argument, $function, in_array(lcfirst($type)[0], ['a', 'e', 'i', 'o', 'u'], \true) ? 'an' : 'a', $type)); + return $this->exporter()->export($other) . ' ' . $this->toString(); } - private function __construct(string $message = '', int $code = 0, \Exception $previous = null) + /** + * Returns a custom string representation of the constraint object when it + * appears in context of an $operator expression. + * + * The purpose of this method is to provide meaningful descriptive string + * in context of operators such as LogicalNot. Native PHPUnit constraints + * are supported out of the box by LogicalNot, but externally developed + * ones had no way to provide correct strings in this context. + * + * The method shall return empty string, when it does not handle + * customization by itself. + * + * @param Operator $operator the $operator of the expression + * @param mixed $role role of $this constraint in the $operator expression + */ + protected function toStringInContext(\PHPUnit\Framework\Constraint\Operator $operator, $role) : string { - parent::__construct($message, $code, $previous); + return ''; + } + /** + * Returns the description of the failure when this constraint appears in + * context of an $operator expression. + * + * The purpose of this method is to provide meaningful failure description + * in context of operators such as LogicalNot. Native PHPUnit constraints + * are supported out of the box by LogicalNot, but externally developed + * ones had no way to provide correct messages in this context. + * + * The method shall return empty string, when it does not handle + * customization by itself. + * + * @param Operator $operator the $operator of the expression + * @param mixed $role role of $this constraint in the $operator expression + * @param mixed $other evaluated value or object + */ + protected function failureDescriptionInContext(\PHPUnit\Framework\Constraint\Operator $operator, $role, $other) : string + { + $string = $this->toStringInContext($operator, $role); + if ($string === '') { + return ''; + } + return $this->exporter()->export($other) . ' ' . $string; + } + /** + * Reduces the sub-expression starting at $this by skipping degenerate + * sub-expression and returns first descendant constraint that starts + * a non-reducible sub-expression. + * + * Returns $this for terminal constraints and for operators that start + * non-reducible sub-expression, or the nearest descendant of $this that + * starts a non-reducible sub-expression. + * + * A constraint expression may be modelled as a tree with non-terminal + * nodes (operators) and terminal nodes. For example: + * + * LogicalOr (operator, non-terminal) + * + LogicalAnd (operator, non-terminal) + * | + IsType('int') (terminal) + * | + GreaterThan(10) (terminal) + * + LogicalNot (operator, non-terminal) + * + IsType('array') (terminal) + * + * A degenerate sub-expression is a part of the tree, that effectively does + * not contribute to the evaluation of the expression it appears in. An example + * of degenerate sub-expression is a BinaryOperator constructed with single + * operand or nested BinaryOperators, each with single operand. An + * expression involving a degenerate sub-expression is equivalent to a + * reduced expression with the degenerate sub-expression removed, for example + * + * LogicalAnd (operator) + * + LogicalOr (degenerate operator) + * | + LogicalAnd (degenerate operator) + * | + IsType('int') (terminal) + * + GreaterThan(10) (terminal) + * + * is equivalent to + * + * LogicalAnd (operator) + * + IsType('int') (terminal) + * + GreaterThan(10) (terminal) + * + * because the subexpression + * + * + LogicalOr + * + LogicalAnd + * + - + * + * is degenerate. Calling reduce() on the LogicalOr object above, as well + * as on LogicalAnd, shall return the IsType('int') instance. + * + * Other specific reductions can be implemented, for example cascade of + * LogicalNot operators + * + * + LogicalNot + * + LogicalNot + * +LogicalNot + * + IsTrue + * + * can be reduced to + * + * LogicalNot + * + IsTrue + */ + protected function reduce() : self + { + return $this; } } - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework; +namespace PHPUnit\Framework\Constraint; +use function is_string; +use function sprintf; +use function strpos; +use function trim; +use PHPUnit\Framework\ExpectationFailedException; +use PHPUnitPHAR\SebastianBergmann\Comparator\ComparisonFailure; +use PHPUnitPHAR\SebastianBergmann\Comparator\Factory as ComparatorFactory; +use PHPUnitPHAR\SebastianBergmann\RecursionContext\InvalidArgumentException; /** - * @internal This class is not covered by the backward compatibility promise for PHPUnit + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -final class InvalidDataProviderException extends \PHPUnit\Framework\Exception +final class IsEqual extends \PHPUnit\Framework\Constraint\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, 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) : ?bool + { + // 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 InvalidArgumentException + */ + public function toString() : string + { + $delta = ''; + if (is_string($this->value)) { + if (strpos($this->value, "\n") !== \false) { + return 'is equal to '; + } + 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); + } } 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 + */ + public function evaluate($other, string $description = '', bool $returnResult = \false) : ?bool + { + // 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, 0.0, \true, \false); + } 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 InvalidArgumentException + */ + public function toString() : string + { + if (is_string($this->value)) { + if (strpos($this->value, "\n") !== \false) { + return 'is equal to '; + } + return sprintf("is equal to '%s'", $this->value); + } + return sprintf('is equal to %s', $this->exporter()->export($this->value)); + } } 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 + */ + public function evaluate($other, string $description = '', bool $returnResult = \false) : ?bool + { + // 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, 0.0, \false, \true); + } 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 InvalidArgumentException + */ + public function toString() : string + { + if (is_string($this->value)) { + if (strpos($this->value, "\n") !== \false) { + return 'is equal to '; + } + return sprintf("is equal to '%s'", $this->value); + } + return sprintf('is equal to %s', $this->exporter()->export($this->value)); + } } value = $value; + $this->delta = $delta; + } + /** + * 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) : ?bool + { + // 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); + } 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 InvalidArgumentException + */ + public function toString() : string + { + return sprintf('is equal to %s with delta <%F>', $this->exporter()->export($this->value), $this->delta); + } } diff = $diff; + $this->className = $className; } - public function getDiff() : string + /** + * Returns a string representation of the constraint. + */ + public function toString() : string { - return $this->diff; + 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); } } - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework; +namespace PHPUnit\Framework\Constraint; +use function sprintf; +use PHPUnitPHAR\SebastianBergmann\RecursionContext\InvalidArgumentException; +use Throwable; /** - * @internal This class is not covered by the backward compatibility promise for PHPUnit + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -final class SkippedTestError extends \PHPUnit\Framework\AssertionFailedError implements \PHPUnit\Framework\SkippedTest +final class ExceptionCode extends \PHPUnit\Framework\Constraint\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 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)); + } } - * - * 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 \PHPUnit\Framework\AssertionFailedError +final class ExceptionMessage extends \PHPUnit\Framework\Constraint\Constraint { /** - * 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) + private $expectedMessage; + public function __construct(string $expected) { - parent::__construct($message, $code); - $this->syntheticFile = $file; - $this->syntheticLine = $line; - $this->syntheticTrace = $trace; + $this->expectedMessage = $expected; } - public function getSyntheticFile() : string + public function toString() : string { - return $this->syntheticFile; + if ($this->expectedMessage === '') { + return 'exception message is empty'; + } + return 'exception message contains '; } - public function getSyntheticLine() : int + /** + * Evaluates the constraint for parameter $other. Returns true if the + * constraint is met, false otherwise. + * + * @param Throwable $other + */ + protected function matches($other) : bool { - return $this->syntheticLine; + if ($this->expectedMessage === '') { + return $other->getMessage() === ''; + } + return strpos((string) $other->getMessage(), $this->expectedMessage) !== \false; } - public function getSyntheticTrace() : array + /** + * 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->syntheticTrace; + 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); } } 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 \PHPUnit\Framework\Exception + * @throws 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); + } } getMessage(); + 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); } } + * Returns a string representation of the constraint. */ - private $originalException; - 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 = \PHPUnit\Framework\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 + public function toString() : string { - $this->originalException($t); - $this->className = get_class($t); - $this->file = $t->getFile(); - $this->line = $t->getLine(); - $this->serializableTrace = $t->getTrace(); - foreach (array_keys($this->serializableTrace) as $key) { - unset($this->serializableTrace[$key]['args']); - } - if ($t->getPrevious()) { - $this->previous = new self($t->getPrevious()); - } + return 'is readable'; } - public function getOriginalException() : ?Throwable + /** + * 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->originalException(); + return is_readable($other); } /** - * 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. + * Returns the description of the failure. * - * Approach works both for var_dump() and var_export() and print_r(). + * 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 */ - private function originalException(Throwable $exceptionToStore = null) : ?Throwable + protected function failureDescription($other) : string { - // drop once PHP 7.3 support is removed - if (PHP_VERSION_ID < 70400) { - static $originalExceptions; - $instanceId = spl_object_hash($this); - if ($exceptionToStore) { - $originalExceptions[$instanceId] = $exceptionToStore; - } - return $originalExceptions[$instanceId] ?? null; - } - if ($exceptionToStore) { - $this->originalException = WeakReference::create($exceptionToStore); - } - return $this->originalException !== null ? $this->originalException->get() : null; + return sprintf('"%s" is readable', $other); } } $dependencies + * Evaluates the constraint for parameter $other. Returns true if the + * constraint is met, false otherwise. * - * @psalm-return list + * @param mixed $other value or object to evaluate */ - public static function filterInvalid(array $dependencies) : array + protected function matches($other) : bool { - return array_values(array_filter($dependencies, static function (self $d) { - return $d->isValid(); - })); + return is_writable($other); } /** - * @psalm-param list $existing - * @psalm-param list $additional + * Returns the description of the failure. * - * @psalm-return list - */ - public static function mergeUnique(array $existing, array $additional) : array - { - $existingTargets = array_map(static function ($dependency) { - return $dependency->getTarget(); - }, $existing); - foreach ($additional as $dependency) { - if (in_array($dependency->getTarget(), $existingTargets, \true)) { - continue; - } - $existingTargets[] = $dependency->getTarget(); - $existing[] = $dependency; - } - return $existing; - } - /** - * @psalm-param list $left - * @psalm-param list $right + * The beginning of failure messages is "Failed asserting that" in most + * cases. This method should return the second part of that sentence. * - * @psalm-return list + * @param mixed $other evaluated value or object */ - public static function diff(array $left, array $right) : array - { - if ($right === []) { - return $left; - } - if ($left === []) { - return []; - } - $diff = []; - $rightTargets = array_map(static function ($dependency) { - return $dependency->getTarget(); - }, $right); - foreach ($left as $dependency) { - if (in_array($dependency->getTarget(), $rightTargets, \true)) { - continue; - } - $diff[] = $dependency; - } - return $diff; - } - public function __construct(string $classOrCallableName, ?string $methodName = null, ?string $option = null) - { - if ($classOrCallableName === '') { - return; - } - if (strpos($classOrCallableName, '::') !== \false) { - [$this->className, $this->methodName] = explode('::', $classOrCallableName); - } else { - $this->className = $classOrCallableName; - $this->methodName = !empty($methodName) ? $methodName : 'class'; - } - if ($option === 'clone') { - $this->useDeepClone = \true; - } elseif ($option === 'shallowClone') { - $this->useShallowClone = \true; - } - } - public function __toString() : string - { - return $this->getTarget(); - } - public function isValid() : bool - { - // Invalid dependencies can be declared and are skipped by the runner - return $this->className !== '' && $this->methodName !== ''; - } - public function useShallowClone() : bool - { - return $this->useShallowClone; - } - public function useDeepClone() : bool - { - return $this->useDeepClone; - } - public function targetIsClass() : bool - { - return $this->methodName === 'class'; - } - public function getTarget() : string - { - return $this->isValid() ? $this->className . '::' . $this->methodName : ''; - } - public function getTargetClassName() : string + protected function failureDescription($other) : string { - return $this->className; + return sprintf('"%s" is writable', $other); } } value = $value; + } /** - * @var ?bool - */ - protected $backupStaticAttributes = \false; - /** - * @var ?bool - */ - protected $runTestInSeparateProcess = \false; - /** - * @var string + * Evaluates the constraint for parameter $other. + * + * If $returnResult is set to false (the default), an exception is thrown + * in case of a failure. null is returned otherwise. + * + * If $returnResult is true, the result of the evaluation is returned as + * a boolean value instead: true in case of success, false in case of a + * failure. + * + * @throws ExpectationFailedException + * @throws InvalidArgumentException */ - private $message; - public function __construct(string $className, string $methodName, string $message = '') - { - parent::__construct($className . '::' . $methodName); - $this->message = $message; - } - public function getMessage() : string + public function evaluate($other, string $description = '', bool $returnResult = \false) : ?bool { - return $this->message; + $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); + } + return null; } /** - * Returns a string representation of the test case. + * Returns a string representation of the constraint. * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws InvalidArgumentException */ public function toString() : string { - return $this->getName(); + 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); } /** - * @throws Exception + * 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 InvalidArgumentException */ - protected function runTest() : void + protected function failureDescription($other) : string { - $this->markTestIncomplete($this->message); + 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); } } 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 + * + * @throws Exception + * @throws ExpectationFailedException + * @throws InvalidArgumentException + * + * @psalm-return never-return + */ + protected function fail($other, $description, ?ComparisonFailure $comparisonFailure = null) : void + { + if ($comparisonFailure === null) { + [$error, $recodedOther] = Json::canonicalize($other); + if ($error) { + parent::fail($other, $description); + } + [$error, $recodedValue] = Json::canonicalize($this->value); + if ($error) { + parent::fail($other, $description); + } + $comparisonFailure = new ComparisonFailure(json_decode($this->value), json_decode($other), Json::prettify($recodedValue), Json::prettify($recodedOther), \false, 'Failed asserting that two json values are equal.'); + } + parent::fail($other, $description, $comparisonFailure); + } } __phpunit_originalObject = $originalObject; - } - /** @noinspection MagicMethodsValidityInspection */ - public function __phpunit_setReturnValueGeneration(bool $returnValueGeneration) : void - { - $this->__phpunit_returnValueGeneration = $returnValueGeneration; - } - /** @noinspection MagicMethodsValidityInspection */ - public function __phpunit_getInvocationHandler() : \PHPUnit\Framework\MockObject\InvocationHandler + public static function determineJsonError(string $error, string $prefix = '') : ?string { - if ($this->__phpunit_invocationMocker === null) { - $this->__phpunit_invocationMocker = new \PHPUnit\Framework\MockObject\InvocationHandler(static::$__phpunit_configurableMethods, $this->__phpunit_returnValueGeneration); + 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'; } - 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 + /** + * Translates a given type to a human readable message prefix. + */ + public static function translateTypeToPrefix(string $type) : string { - $this->__phpunit_getInvocationHandler()->verify(); - if ($unsetInvocationMocker) { - $this->__phpunit_invocationMocker = null; + switch (strtolower($type)) { + case 'expected': + $prefix = 'Expected value JSON decode error - '; + break; + case 'actual': + $prefix = 'Actual value JSON decode error - '; + break; + default: + $prefix = ''; + break; } - } - public function expects(InvocationOrder $matcher) : InvocationMockerBuilder - { - return $this->__phpunit_getInvocationHandler()->expects($matcher); + return $prefix; } } expects(new AnyInvokedCount()); - return call_user_func_array([$expects, 'method'], func_get_args()); + 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); } } invocationHandler = $handler; - $this->matcher = $matcher; - $this->configurableMethods = $configurableMethods; + return is_nan($other); } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Constraint; + +use function get_class; +use function is_object; +use function sprintf; +use PHPUnit\Framework\Exception; +use ReflectionClass; +use ReflectionException; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4601 + */ +class ClassHasAttribute extends \PHPUnit\Framework\Constraint\Constraint +{ /** - * @throws MatcherAlreadyRegisteredException - * - * @return $this + * @var string */ - public function id($id) : self + private $attributeName; + public function __construct(string $attributeName) { - $this->invocationHandler->registerMatcher($id, $this->matcher); - return $this; + $this->attributeName = $attributeName; } /** - * @return $this + * Returns a string representation of the constraint. */ - public function will(Stub $stub) : \PHPUnit\Framework\MockObject\Builder\Identity + public function toString() : string { - $this->matcher->setStub($stub); - return $this; + return sprintf('has attribute "%s"', $this->attributeName); } /** - * @param mixed $value - * @param mixed[] $nextValues + * Evaluates the constraint for parameter $other. Returns true if the + * constraint is met, false otherwise. * - * @throws IncompatibleReturnValueException + * @param mixed $other value or object to evaluate */ - public function willReturn($value, ...$nextValues) : self + protected function matches($other) : bool { - if (count($nextValues) === 0) { - $this->ensureTypeOfReturnValues([$value]); - $stub = $value instanceof Stub ? $value : new ReturnStub($value); - } else { - $values = array_merge([$value], $nextValues); - $this->ensureTypeOfReturnValues($values); - $stub = new ConsecutiveCalls($values); + try { + return (new ReflectionClass($other))->hasProperty($this->attributeName); + // @codeCoverageIgnoreStart + } catch (ReflectionException $e) { + throw new Exception($e->getMessage(), $e->getCode(), $e); } - return $this->will($stub); - } - 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); - } - public function willReturnCallback($callback) : self - { - $stub = new ReturnCallback($callback); - return $this->will($stub); - } - public function willReturnSelf() : self - { - $stub = new ReturnSelf(); - return $this->will($stub); + // @codeCoverageIgnoreEnd } - public function willReturnOnConsecutiveCalls(...$values) : self + /** + * 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 { - $stub = new ConsecutiveCalls($values); - return $this->will($stub); + return sprintf('%sclass "%s" %s', is_object($other) ? 'object of ' : '', is_object($other) ? get_class($other) : $other, $this->toString()); } - public function willThrowException(Throwable $exception) : self + protected function attributeName() : string { - $stub = new Exception($exception); - return $this->will($stub); + return $this->attributeName; } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Constraint; + +use function sprintf; +use PHPUnit\Framework\Exception; +use ReflectionClass; +use ReflectionException; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4601 + */ +final class ClassHasStaticAttribute extends \PHPUnit\Framework\Constraint\ClassHasAttribute +{ /** - * @return $this + * Returns a string representation of the constraint. */ - public function after($id) : self + public function toString() : string { - $this->matcher->setAfterMatchBuilderId($id); - return $this; + return sprintf('has static attribute "%s"', $this->attributeName()); } /** - * @param mixed[] $arguments - * - * @throws \PHPUnit\Framework\Exception - * @throws MethodNameNotConfiguredException - * @throws MethodParametersAlreadyConfiguredException + * Evaluates the constraint for parameter $other. Returns true if the + * constraint is met, false otherwise. * - * @return $this + * @param mixed $other value or object to evaluate */ - public function with(...$arguments) : self + protected function matches($other) : bool { - $this->ensureParametersCanBeConfigured(); - $this->matcher->setParametersRule(new Rule\Parameters($arguments)); - return $this; - } - /** - * @param array ...$arguments - * - * @throws \PHPUnit\Framework\Exception - * @throws MethodNameNotConfiguredException - * @throws MethodParametersAlreadyConfiguredException - * - * @return $this - * - * @deprecated - */ - public function withConsecutive(...$arguments) : self - { - $this->ensureParametersCanBeConfigured(); - $this->matcher->setParametersRule(new Rule\ConsecutiveParameters($arguments)); - return $this; - } - /** - * @throws MethodNameNotConfiguredException - * @throws MethodParametersAlreadyConfiguredException - * - * @return $this - */ - public function withAnyParameters() : self - { - $this->ensureParametersCanBeConfigured(); - $this->matcher->setParametersRule(new Rule\AnyParameters()); - return $this; - } - /** - * @param Constraint|string $constraint - * - * @throws \PHPUnit\Framework\InvalidArgumentException - * @throws MethodCannotBeConfiguredException - * @throws MethodNameAlreadyConfiguredException - * - * @return $this - */ - public function method($constraint) : self - { - if ($this->matcher->hasMethodNameRule()) { - throw new MethodNameAlreadyConfiguredException(); - } - $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 MethodCannotBeConfiguredException($constraint); - } - $this->matcher->setMethodNameRule(new Rule\MethodName($constraint)); - return $this; - } - /** - * @throws MethodNameNotConfiguredException - * @throws MethodParametersAlreadyConfiguredException - */ - private function ensureParametersCanBeConfigured() : void - { - if (!$this->matcher->hasMethodNameRule()) { - throw new MethodNameNotConfiguredException(); - } - if ($this->matcher->hasParametersRule()) { - throw new MethodParametersAlreadyConfiguredException(); - } - } - 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; - } - /** - * @throws IncompatibleReturnValueException - */ - private function ensureTypeOfReturnValues(array $values) : void - { - $configuredMethod = $this->getConfiguredMethod(); - if ($configuredMethod === null) { - return; - } - foreach ($values as $value) { - if (!$configuredMethod->mayReturn($value)) { - throw new IncompatibleReturnValueException($configuredMethod, $value); + try { + $class = new ReflectionClass($other); + if ($class->hasProperty($this->attributeName())) { + return $class->getProperty($this->attributeName())->isStatic(); } + // @codeCoverageIgnoreStart + } catch (ReflectionException $e) { + throw new Exception($e->getMessage(), $e->getCode(), $e); } + // @codeCoverageIgnoreEnd + return \false; } } > $valueMap - * - * @return self - */ - public function willReturnMap(array $valueMap); /** - * @param int $argumentIndex - * - * @return self + * @var object */ - public function willReturnArgument($argumentIndex); + private $expected; /** - * @param callable $callback - * - * @return self + * @var string */ - public function willReturnCallback($callback); - /** @return self */ - public function willReturnSelf(); + private $method; + public function __construct(object $object, string $method = 'equals') + { + $this->expected = $object; + $this->method = $method; + } + public function toString() : string + { + return 'two objects are equal'; + } /** - * @param mixed $values - * - * @return self + * @throws ActualValueIsNotAnObjectException + * @throws ComparisonMethodDoesNotAcceptParameterTypeException + * @throws ComparisonMethodDoesNotDeclareBoolReturnTypeException + * @throws ComparisonMethodDoesNotDeclareExactlyOneParameterException + * @throws ComparisonMethodDoesNotDeclareParameterTypeException + * @throws ComparisonMethodDoesNotExistException */ - public function willReturnOnConsecutiveCalls(...$values); - /** @return self */ - public function willThrowException(Throwable $exception); + protected function matches($other) : bool + { + if (!is_object($other)) { + throw new ActualValueIsNotAnObjectException(); + } + $object = new ReflectionObject($other); + if (!$object->hasMethod($this->method)) { + throw new ComparisonMethodDoesNotExistException(get_class($other), $this->method); + } + /** @noinspection PhpUnhandledExceptionInspection */ + $method = $object->getMethod($this->method); + if (!$method->hasReturnType()) { + throw new ComparisonMethodDoesNotDeclareBoolReturnTypeException(get_class($other), $this->method); + } + $returnType = $method->getReturnType(); + if (!$returnType instanceof ReflectionNamedType) { + throw new ComparisonMethodDoesNotDeclareBoolReturnTypeException(get_class($other), $this->method); + } + if ($returnType->allowsNull()) { + throw new ComparisonMethodDoesNotDeclareBoolReturnTypeException(get_class($other), $this->method); + } + if ($returnType->getName() !== 'bool') { + throw new ComparisonMethodDoesNotDeclareBoolReturnTypeException(get_class($other), $this->method); + } + if ($method->getNumberOfParameters() !== 1 || $method->getNumberOfRequiredParameters() !== 1) { + throw new ComparisonMethodDoesNotDeclareExactlyOneParameterException(get_class($other), $this->method); + } + $parameter = $method->getParameters()[0]; + if (!$parameter->hasType()) { + throw new ComparisonMethodDoesNotDeclareParameterTypeException(get_class($other), $this->method); + } + $type = $parameter->getType(); + if (!$type instanceof ReflectionNamedType) { + throw new ComparisonMethodDoesNotDeclareParameterTypeException(get_class($other), $this->method); + } + $typeName = $type->getName(); + if ($typeName === 'self') { + $typeName = get_class($other); + } + if (!$this->expected instanceof $typeName) { + throw new ComparisonMethodDoesNotAcceptParameterTypeException(get_class($other), $this->method, get_class($this->expected)); + } + return $other->{$this->method}($this->expected); + } + protected function failureDescription($other) : string + { + return $this->toString(); + } } hasProperty($this->attributeName()); + } } propertyName = $propertyName; + } /** - * 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: - * - * // 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)); - * + * Returns a string representation of the constraint. + */ + public function toString() : string + { + return sprintf('has property "%s"', $this->propertyName); + } + /** + * Evaluates the constraint for parameter $other. Returns true if the + * constraint is met, false otherwise. * - * @return ParametersMatch + * @param mixed $other value or object to evaluate */ - public function with(...$arguments); + protected function matches($other) : bool + { + if (!is_object($other)) { + return \false; + } + return (new ReflectionObject($other))->hasProperty($this->propertyName); + } /** - * Sets a rule which allows any kind of parameters. + * Returns the description of the failure. * - * Some examples: - * - * // match any number of parameters - * $b->withAnyParameters(); - * + * The beginning of failure messages is "Failed asserting that" in most + * cases. This method should return the second part of that sentence. * - * @return ParametersMatch + * @param mixed $other evaluated value or object */ - public function withAnyParameters(); + protected function failureDescription($other) : string + { + if (is_object($other)) { + return sprintf('object of class "%s" %s', get_class($other), $this->toString()); + } + return sprintf('"%s" (%s) %s', $other, gettype($other), $this->toString()); + } } - * - * 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\SebastianBergmann\Type\Type; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class ConfigurableMethod -{ + private $constraints = []; + public static function fromConstraints(\PHPUnit\Framework\Constraint\Constraint ...$constraints) : self + { + $constraint = new static(); + $constraint->constraints = $constraints; + return $constraint; + } /** - * @var string + * @param mixed[] $constraints */ - private $name; + public function setConstraints(array $constraints) : void + { + $this->constraints = array_map(function ($constraint) : \PHPUnit\Framework\Constraint\Constraint { + return $this->checkConstraint($constraint); + }, array_values($constraints)); + } /** - * @var Type + * Returns the number of operands (constraints). */ - private $returnType; - public function __construct(string $name, Type $returnType) + public final function arity() : int { - $this->name = $name; - $this->returnType = $returnType; + return count($this->constraints); } - public function getName() : string + /** + * Returns a string representation of the constraint. + */ + public function toString() : string { - return $this->name; + $reduced = $this->reduce(); + if ($reduced !== $this) { + return $reduced->toString(); + } + $text = ''; + foreach ($this->constraints as $key => $constraint) { + $constraint = $constraint->reduce(); + $text .= $this->constraintToString($constraint, $key); + } + return $text; } - public function mayReturn($value) : bool + /** + * Counts the number of constraint elements. + */ + public function count() : int { - if ($value === null && $this->returnType->allowsNull()) { - return \true; + $count = 0; + foreach ($this->constraints as $constraint) { + $count += count($constraint); } - return $this->returnType->isAssignable(Type::fromValue($value, \false)); + return $count; } - public function getReturnTypeDeclaration() : string + /** + * Returns the nested constraints. + */ + protected final function constraints() : array { - return $this->returnType->asString(); + return $this->constraints; + } + /** + * Returns true if the $constraint needs to be wrapped with braces. + */ + protected final function constraintNeedsParentheses(\PHPUnit\Framework\Constraint\Constraint $constraint) : bool + { + return $this->arity() > 1 && parent::constraintNeedsParentheses($constraint); + } + /** + * Reduces the sub-expression starting at $this by skipping degenerate + * sub-expression and returns first descendant constraint that starts + * a non-reducible sub-expression. + * + * See Constraint::reduce() for more. + */ + protected function reduce() : \PHPUnit\Framework\Constraint\Constraint + { + if ($this->arity() === 1 && $this->constraints[0] instanceof \PHPUnit\Framework\Constraint\Operator) { + return $this->constraints[0]->reduce(); + } + return parent::reduce(); + } + /** + * Returns string representation of given operand in context of this operator. + * + * @param Constraint $constraint operand constraint + * @param int $position position of $constraint in this expression + */ + private function constraintToString(\PHPUnit\Framework\Constraint\Constraint $constraint, int $position) : string + { + $prefix = ''; + if ($position > 0) { + $prefix = ' ' . $this->operator() . ' '; + } + if ($this->constraintNeedsParentheses($constraint)) { + return $prefix . '( ' . $constraint->toString() . ' )'; + } + $string = $constraint->toStringInContext($this, $position); + if ($string === '') { + $string = $constraint->toString(); + } + return $prefix . $string; } } - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject; +namespace PHPUnit\Framework\Constraint; -use function sprintf; /** - * @internal This class is not covered by the backward compatibility promise for PHPUnit + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -final class CannotUseAddMethodsException extends \PHPUnit\Framework\Exception implements \PHPUnit\Framework\MockObject\Exception +final class LogicalAnd extends \PHPUnit\Framework\Constraint\BinaryOperator { - public function __construct(string $type, string $methodName) + /** + * Returns the name of this operator. + */ + public function operator() : string { - parent::__construct(sprintf('Trying to configure method "%s" with addMethods(), but it exists in class "%s". Use onlyMethods() for methods that exist in the class', $methodName, $type)); + return 'and'; + } + /** + * Returns this operator's precedence. + * + * @see https://www.php.net/manual/en/language.operators.precedence.php + */ + public function precedence() : int + { + return 22; + } + /** + * 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 + { + foreach ($this->constraints() as $constraint) { + if (!$constraint->evaluate($other, '', \true)) { + return \false; + } + } + return [] !== $this->constraints(); } } 0) { + $nonInput = $matches[2]; + $negatedString = preg_replace('/' . preg_quote($nonInput, '/') . '/', preg_replace($positives, $negatives, $nonInput), $string); + } else { + $negatedString = preg_replace($positives, $negatives, $string); + } + return $negatedString; + } + /** + * Returns the name of this operator. + */ + public function operator() : string + { + return 'not'; + } + /** + * Returns this operator's precedence. + * + * @see https://www.php.net/manual/en/language.operators.precedence.php + */ + public function precedence() : int + { + return 5; + } + /** + * 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->constraint()->evaluate($other, '', \true); + } + /** + * Applies additional transformation to strings returned by toString() or + * failureDescription(). + */ + protected function transformString(string $string) : string + { + return self::negate($string); + } + /** + * Reduces the sub-expression starting at $this by skipping degenerate + * sub-expression and returns first descendant constraint that starts + * a non-reducible sub-expression. + * + * See Constraint::reduce() for more. + */ + protected function reduce() : \PHPUnit\Framework\Constraint\Constraint + { + $constraint = $this->constraint(); + if ($constraint instanceof self) { + return $constraint->constraint()->reduce(); + } + return parent::reduce(); } } constraints() as $constraint) { + if ($constraint->evaluate($other, '', \true)) { + return \true; + } + } + return \false; } } constraints(); + $initial = array_shift($constraints); + if ($initial === null) { + return \false; + } + return array_reduce($constraints, static function (bool $matches, \PHPUnit\Framework\Constraint\Constraint $constraint) use($other) : bool { + return $matches xor $constraint->evaluate($other, '', \true); + }, $initial->evaluate($other, '', \true)); } } arity() > 1 && $this->precedence() <= $constraint->precedence(); } } constraint = $this->checkConstraint($constraint); + } + /** + * Returns the number of operands (constraints). + */ + public function arity() : int + { + return 1; + } + /** + * Returns a string representation of the constraint. + */ + public function toString() : string + { + $reduced = $this->reduce(); + if ($reduced !== $this) { + return $reduced->toString(); + } + $constraint = $this->constraint->reduce(); + if ($this->constraintNeedsParentheses($constraint)) { + return $this->operator() . '( ' . $constraint->toString() . ' )'; + } + $string = $constraint->toStringInContext($this, 0); + if ($string === '') { + return $this->transformString($constraint->toString()); + } + return $string; + } + /** + * 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 InvalidArgumentException + */ + protected function failureDescription($other) : string + { + $reduced = $this->reduce(); + if ($reduced !== $this) { + return $reduced->failureDescription($other); + } + $constraint = $this->constraint->reduce(); + if ($this->constraintNeedsParentheses($constraint)) { + return $this->operator() . '( ' . $constraint->failureDescription($other) . ' )'; + } + $string = $constraint->failureDescriptionInContext($this, 0, $other); + if ($string === '') { + return $this->transformString($constraint->failureDescription($other)); + } + return $string; + } + /** + * Transforms string returned by the memeber constraint's toString() or + * failureDescription() such that it reflects constraint's participation in + * this expression. + * + * The method may be overwritten in a subclass to apply default + * transformation in case the operand constraint does not provide its own + * custom strings via toStringInContext() or failureDescriptionInContext(). + * + * @param string $string the string to be transformed + */ + protected function transformString(string $string) : string + { + return $string; + } + /** + * Provides access to $this->constraint for subclasses. + */ + protected final function constraint() : \PHPUnit\Framework\Constraint\Constraint + { + return $this->constraint; + } + /** + * Returns true if the $constraint needs to be wrapped with parentheses. + */ + protected function constraintNeedsParentheses(\PHPUnit\Framework\Constraint\Constraint $constraint) : bool + { + $constraint = $constraint->reduce(); + return $constraint instanceof self || parent::constraintNeedsParentheses($constraint); + } } $methods + * Returns a string representation of the constraint. */ - public function __construct(array $methods) + public function toString() : string { - parent::__construct(sprintf('Cannot double using a method list that contains duplicates: "%s" (duplicate: "%s")', implode(', ', $methods), implode(', ', array_unique(array_diff_assoc($methods, array_unique($methods)))))); + 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 InvalidArgumentException + */ + protected function failureDescription($other) : string + { + if ($other === '') { + return 'an empty string is valid JSON'; + } + json_decode($other); + $error = (string) \PHPUnit\Framework\Constraint\JsonMatchesErrorMessageProvider::determineJsonError((string) json_last_error()); + return sprintf('%s is valid JSON (%s)', $this->exporter()->shortenedExport($other), $error); } } - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject; +namespace PHPUnit\Framework\Constraint; -use function get_class; -use function gettype; -use function is_object; +use function preg_match; use function sprintf; /** - * @internal This class is not covered by the backward compatibility promise for PHPUnit + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -final class IncompatibleReturnValueException extends \PHPUnit\Framework\Exception implements \PHPUnit\Framework\MockObject\Exception +class RegularExpression extends \PHPUnit\Framework\Constraint\Constraint { /** - * @param mixed $value + * @var string */ - public function __construct(\PHPUnit\Framework\MockObject\ConfigurableMethod $method, $value) + private $pattern; + public function __construct(string $pattern) { - parent::__construct(sprintf('Method %s may not return value of type %s, its declared return type is "%s"', $method->getName(), is_object($value) ? get_class($value) : gettype($value), $method->getReturnTypeDeclaration())); + $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; } } string = $string; + $this->ignoreCase = $ignoreCase; + } + /** + * Returns a string representation of the constraint. + */ + public function toString() : string + { + if ($this->ignoreCase) { + $string = mb_strtolower($this->string, 'UTF-8'); + } 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) { + /* + * We must use the multi byte safe version so we can accurately compare non latin upper characters with + * their lowercase equivalents. + */ + return mb_stripos($other, $this->string, 0, 'UTF-8') !== \false; + } + /* + * Use the non multi byte safe functions to see if the string is contained in $other. + * + * This function is very fast and we don't care about the character position in the string. + * + * Additionally, we want this method to be binary safe so we can check if some binary data is in other binary + * data. + */ + return strpos($other, $this->string) !== \false; } } ', $id)); + $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; } } is already registered', $id)); - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject; - -use function sprintf; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class MethodCannotBeConfiguredException extends \PHPUnit\Framework\Exception implements \PHPUnit\Framework\MockObject\Exception -{ - public function __construct(string $method) + /** + * @var string + */ + private $string; + public function __construct(string $string) { - parent::__construct(sprintf('Trying to configure method "%s" which cannot be configured because it does not exist, has not been specified, is final, or is static', $method)); + parent::__construct($this->createPatternFromFormat($this->convertNewlines($string))); + $this->string = $string; } -} - - * - * 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 MethodNameAlreadyConfiguredException extends \PHPUnit\Framework\Exception implements \PHPUnit\Framework\MockObject\Exception -{ - public function __construct() + /** + * 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 { - parent::__construct('Method name is already configured'); + return parent::matches($this->convertNewlines($other)); } -} - - * - * 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 MethodNameNotConfiguredException extends \PHPUnit\Framework\Exception implements \PHPUnit\Framework\MockObject\Exception -{ - public function __construct() + protected function failureDescription($other) : string { - parent::__construct('Method name is not configured'); + return 'string matches format description'; } -} - - * - * 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 MethodParametersAlreadyConfiguredException extends \PHPUnit\Framework\Exception implements \PHPUnit\Framework\MockObject\Exception -{ - public function __construct() + protected function additionalFailureDescription($other) : string { - parent::__construct('Method parameters already configured'); + $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(new UnifiedDiffOutputBuilder("--- 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(string $text) : string + { + return preg_replace('/\\r\\n/', "\n", $text); } } 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; } } key = $key; + } + /** + * Returns a string representation of the constraint. + * + * @throws 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 InvalidArgumentException + */ + protected function failureDescription($other) : string + { + return 'an array ' . $this->toString(); + } } getClassName(), $invocation->getMethodName())); + $this->value = $value; + } + /** + * Returns a string representation of the constraint. + * + * @throws InvalidArgumentException + */ + public function toString() : string + { + return 'contains ' . $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 InvalidArgumentException + */ + protected function failureDescription($other) : string + { + return sprintf('%s %s', is_array($other) ? 'an array' : 'a traversable', $this->toString()); + } + protected function value() + { + return $this->value; } } contains($this->value()); + } + foreach ($other as $element) { + /* @noinspection TypeUnsafeComparisonInspection */ + if ($this->value() == $element) { + return \true; + } + } + return \false; + } } contains($this->value()); + } + foreach ($other as $element) { + if ($this->value() === $element) { + return \true; + } + } + return \false; } } constraint = new \PHPUnit\Framework\Constraint\IsType($type); + } else { + $this->constraint = new \PHPUnit\Framework\Constraint\IsInstanceOf($type); + } + $this->type = $type; + } + /** + * Evaluates the constraint for parameter $other. + * + * If $returnResult is set to false (the default), an exception is thrown + * in case of a failure. null is returned otherwise. + * + * If $returnResult is true, the result of the evaluation is returned as + * a boolean value instead: true in case of success, false in case of a + * failure. + * + * @param mixed|Traversable $other + * + * @throws ExpectationFailedException + * @throws InvalidArgumentException + */ + public function evaluate($other, string $description = '', bool $returnResult = \false) : ?bool + { + $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); + } + return null; + } + /** + * Returns a string representation of the constraint. + */ + public function toString() : string + { + return 'contains only values of type "' . $this->type . '"'; } } 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 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'; } } __phpunit_invocationMocker = clone $this->__phpunit_getInvocationHandler(); - } -} -EOT; - private const MOCKED_CLONE_METHOD_WITHOUT_RETURN_TYPE_TRAIT = <<<'EOT' -namespace PHPUnit\Framework\MockObject; - -trait MockedCloneMethodWithoutReturnType -{ - public function __clone() - { - $this->__phpunit_invocationMocker = clone $this->__phpunit_getInvocationHandler(); - } -} -EOT; - private const UNMOCKED_CLONE_METHOD_WITH_VOID_RETURN_TYPE_TRAIT = <<<'EOT' -namespace PHPUnit\Framework\MockObject; - -trait UnmockedCloneMethodWithVoidReturnType -{ - public function __clone(): void - { - $this->__phpunit_invocationMocker = clone $this->__phpunit_getInvocationHandler(); - - parent::__clone(); - } -} -EOT; - private const UNMOCKED_CLONE_METHOD_WITHOUT_RETURN_TYPE_TRAIT = <<<'EOT' -namespace PHPUnit\Framework\MockObject; - -trait UnmockedCloneMethodWithoutReturnType +final class IsType extends \PHPUnit\Framework\Constraint\Constraint { - public function __clone() - { - $this->__phpunit_invocationMocker = clone $this->__phpunit_getInvocationHandler(); - - parent::__clone(); - } -} -EOT; /** - * @var array + * @var string */ - private const EXCLUDED_METHOD_NAMES = ['__CLASS__' => \true, '__DIR__' => \true, '__FILE__' => \true, '__FUNCTION__' => \true, '__LINE__' => \true, '__METHOD__' => \true, '__NAMESPACE__' => \true, '__TRAIT__' => \true, '__clone' => \true, '__halt_compiler' => \true]; + public const TYPE_ARRAY = 'array'; /** - * @var array + * @var string */ - private static $cache = []; + public const TYPE_BOOL = 'bool'; /** - * @var Template[] + * @var string */ - private static $templates = []; + public const TYPE_FLOAT = 'float'; /** - * Returns a mock object for the specified class. - * - * @param null|array $methods - * - * @throws \PHPUnit\Framework\InvalidArgumentException - * @throws ClassAlreadyExistsException - * @throws ClassIsFinalException - * @throws ClassIsReadonlyException - * @throws DuplicateMethodException - * @throws InvalidMethodNameException - * @throws OriginalConstructorInvocationRequiredException - * @throws ReflectionException - * @throws RuntimeException - * @throws UnknownTypeException + * @var string */ - public function getMock(string $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) : \PHPUnit\Framework\MockObject\MockObject - { - if (!is_array($methods) && null !== $methods) { - throw InvalidArgumentException::create(2, 'array'); - } - if ($type === 'Traversable' || $type === '\\Traversable') { - $type = 'Iterator'; - } - if (!$allowMockingUnknownTypes && !class_exists($type, $callAutoload) && !interface_exists($type, $callAutoload)) { - throw new \PHPUnit\Framework\MockObject\UnknownTypeException($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 \PHPUnit\Framework\MockObject\InvalidMethodNameException((string) $method); - } - } - if ($methods !== array_unique($methods)) { - throw new \PHPUnit\Framework\MockObject\DuplicateMethodException($methods); - } - } - if ($mockClassName !== '' && class_exists($mockClassName, \false)) { - try { - $reflector = new ReflectionClass($mockClassName); - // @codeCoverageIgnoreStart - } catch (\ReflectionException $e) { - throw new \PHPUnit\Framework\MockObject\ReflectionException($e->getMessage(), $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd - if (!$reflector->implementsInterface(\PHPUnit\Framework\MockObject\MockObject::class)) { - throw new \PHPUnit\Framework\MockObject\ClassAlreadyExistsException($mockClassName); - } - } - if (!$callOriginalConstructor && $callOriginalMethods) { - throw new \PHPUnit\Framework\MockObject\OriginalConstructorInvocationRequiredException(); - } - $mock = $this->generate($type, $methods, $mockClassName, $callOriginalClone, $callAutoload, $cloneArguments, $callOriginalMethods); - return $this->getObject($mock, $type, $callOriginalConstructor, $callAutoload, $arguments, $callOriginalMethods, $proxyTarget, $returnValueGeneration); - } + public const TYPE_INT = 'int'; /** - * @psalm-param list $interfaces - * - * @throws RuntimeException - * @throws UnknownTypeException + * @var string */ - public function getMockForInterfaces(array $interfaces, bool $callAutoload = \true) : \PHPUnit\Framework\MockObject\MockObject - { - if (count($interfaces) < 2) { - throw new \PHPUnit\Framework\MockObject\RuntimeException('At least two interfaces must be specified'); - } - foreach ($interfaces as $interface) { - if (!interface_exists($interface, $callAutoload)) { - throw new \PHPUnit\Framework\MockObject\UnknownTypeException($interface); - } - } - sort($interfaces); - $methods = []; - foreach ($interfaces as $interface) { - $methods = array_merge($methods, $this->getClassMethods($interface)); - } - if (count(array_unique($methods)) < count($methods)) { - throw new \PHPUnit\Framework\MockObject\RuntimeException('Interfaces must not declare the same method'); - } - $unqualifiedNames = []; - foreach ($interfaces as $interface) { - $parts = explode('\\', $interface); - $unqualifiedNames[] = array_pop($parts); - } - sort($unqualifiedNames); - do { - $intersectionName = sprintf('Intersection_%s_%s', implode('_', $unqualifiedNames), substr(md5((string) mt_rand()), 0, 8)); - } while (interface_exists($intersectionName, \false)); - $template = $this->getTemplate('intersection.tpl'); - $template->setVar(['intersection' => $intersectionName, 'interfaces' => implode(', ', $interfaces)]); - eval($template->render()); - return $this->getMock($intersectionName); - } + public const TYPE_NULL = 'null'; /** - * 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. - * - * @psalm-template RealInstanceType of object - * - * @psalm-param class-string $originalClassName - * - * @psalm-return MockObject&RealInstanceType - * - * @throws \PHPUnit\Framework\InvalidArgumentException - * @throws ClassAlreadyExistsException - * @throws ClassIsFinalException - * @throws ClassIsReadonlyException - * @throws DuplicateMethodException - * @throws InvalidMethodNameException - * @throws OriginalConstructorInvocationRequiredException - * @throws ReflectionException - * @throws RuntimeException - * @throws UnknownClassException - * @throws UnknownTypeException + * @var string */ - public function getMockForAbstractClass(string $originalClassName, array $arguments = [], string $mockClassName = '', bool $callOriginalConstructor = \true, bool $callOriginalClone = \true, bool $callAutoload = \true, array $mockedMethods = null, bool $cloneArguments = \true) : \PHPUnit\Framework\MockObject\MockObject - { - if (class_exists($originalClassName, $callAutoload) || interface_exists($originalClassName, $callAutoload)) { - try { - $reflector = new ReflectionClass($originalClassName); - // @codeCoverageIgnoreStart - } catch (\ReflectionException $e) { - throw new \PHPUnit\Framework\MockObject\ReflectionException($e->getMessage(), $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd - $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 \PHPUnit\Framework\MockObject\UnknownClassException($originalClassName); - } + public const TYPE_NUMERIC = 'numeric'; /** - * 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. - * - * @psalm-param trait-string $traitName - * - * @throws \PHPUnit\Framework\InvalidArgumentException - * @throws ClassAlreadyExistsException - * @throws ClassIsFinalException - * @throws ClassIsReadonlyException - * @throws DuplicateMethodException - * @throws InvalidMethodNameException - * @throws OriginalConstructorInvocationRequiredException - * @throws ReflectionException - * @throws RuntimeException - * @throws UnknownClassException - * @throws UnknownTraitException - * @throws UnknownTypeException + * @var string */ - public function getMockForTrait(string $traitName, array $arguments = [], string $mockClassName = '', bool $callOriginalConstructor = \true, bool $callOriginalClone = \true, bool $callAutoload = \true, array $mockedMethods = null, bool $cloneArguments = \true) : \PHPUnit\Framework\MockObject\MockObject - { - if (!trait_exists($traitName, $callAutoload)) { - throw new \PHPUnit\Framework\MockObject\UnknownTraitException($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 \PHPUnit\Framework\MockObject\MockTrait($classTemplate->render(), $className['className']); - $mockTrait->generate(); - return $this->getMockForAbstractClass($className['className'], $arguments, $mockClassName, $callOriginalConstructor, $callOriginalClone, $callAutoload, $mockedMethods, $cloneArguments); - } + public const TYPE_OBJECT = 'object'; /** - * Returns an object for the specified trait. - * - * @psalm-param trait-string $traitName - * - * @throws ReflectionException - * @throws RuntimeException - * @throws UnknownTraitException + * @var string */ - public function getObjectForTrait(string $traitName, string $traitClassName = '', bool $callAutoload = \true, bool $callOriginalConstructor = \false, array $arguments = []) : object - { - if (!trait_exists($traitName, $callAutoload)) { - throw new \PHPUnit\Framework\MockObject\UnknownTraitException($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 \PHPUnit\Framework\MockObject\MockTrait($classTemplate->render(), $className['className']), '', $callOriginalConstructor, $callAutoload, $arguments); - } + public const TYPE_RESOURCE = 'resource'; /** - * @throws ClassIsFinalException - * @throws ClassIsReadonlyException - * @throws ReflectionException - * @throws RuntimeException + * @var string + */ + public const TYPE_CLOSED_RESOURCE = 'resource (closed)'; + /** + * @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 + */ + 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, 'resource (closed)' => \true, 'string' => \true, 'scalar' => \true, 'callable' => \true, 'iterable' => \true]; + /** + * @var string + */ + private $type; + /** + * @throws Exception */ - public function generate(string $type, array $methods = null, string $mockClassName = '', bool $callOriginalClone = \true, bool $callAutoload = \true, bool $cloneArguments = \true, bool $callOriginalMethods = \false) : \PHPUnit\Framework\MockObject\MockClass + public function __construct(string $type) { - if ($mockClassName !== '') { - return $this->generateMock($type, $methods, $mockClassName, $callOriginalClone, $callAutoload, $cloneArguments, $callOriginalMethods); - } - $key = md5($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); + if (!isset(self::KNOWN_TYPES[$type])) { + throw new Exception(sprintf('Type specified for PHPUnit\\Framework\\Constraint\\IsType <%s> ' . 'is not a valid type.', $type)); } - return self::$cache[$key]; + $this->type = $type; } /** - * @throws RuntimeException - * @throws SoapExtensionNotAvailableException + * Returns a string representation of the constraint. */ - public function generateClassFromWsdl(string $wsdlFile, string $className, array $methods = [], array $options = []) : string + public function toString() : string { - if (!extension_loaded('soap')) { - throw new \PHPUnit\Framework\MockObject\SoapExtensionNotAvailableException(); - } - $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 \PHPUnit\Framework\MockObject\RuntimeException($e->getMessage(), $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) { - $parameterStart = strpos($args[$i], '$'); - if (!$parameterStart) { - continue; - } - $args[$i] = substr($args[$i], $parameterStart); - } - $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(); + return sprintf('is of type "%s"', $this->type); } /** - * @throws ReflectionException + * Evaluates the constraint for parameter $other. Returns true if the + * constraint is met, false otherwise. * - * @return string[] + * @param mixed $other value or object to evaluate */ - public function getClassMethods(string $className) : array + protected function matches($other) : bool { - try { - $class = new ReflectionClass($className); - // @codeCoverageIgnoreStart - } catch (\ReflectionException $e) { - throw new \PHPUnit\Framework\MockObject\ReflectionException($e->getMessage(), $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd - $methods = []; - foreach ($class->getMethods() as $method) { - if ($method->isPublic() || $method->isAbstract()) { - $methods[] = $method->getName(); - } + 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': + $type = gettype($other); + return $type === 'resource' || $type === 'resource (closed)'; + case 'resource (closed)': + return gettype($other) === 'resource (closed)'; + case 'scalar': + return is_scalar($other); + case 'callable': + return is_callable($other); + case 'iterable': + return is_iterable($other); + default: + return \false; } - return $methods; } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use function explode; +use PHPUnit\Util\Test as TestUtil; +use PHPUnitPHAR\SebastianBergmann\RecursionContext\InvalidArgumentException; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class DataProviderTestSuite extends \PHPUnit\Framework\TestSuite +{ /** - * @throws ReflectionException - * - * @return MockMethod[] + * @var list */ - public function mockClassMethods(string $className, bool $callOriginalMethods, bool $cloneArguments) : array + private $dependencies = []; + /** + * @param list $dependencies + */ + public function setDependencies(array $dependencies) : void { - try { - $class = new ReflectionClass($className); - // @codeCoverageIgnoreStart - } catch (\ReflectionException $e) { - throw new \PHPUnit\Framework\MockObject\ReflectionException($e->getMessage(), $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd - $methods = []; - foreach ($class->getMethods() as $method) { - if (($method->isPublic() || $method->isAbstract()) && $this->canMockMethod($method)) { - $methods[] = \PHPUnit\Framework\MockObject\MockMethod::fromReflection($method, $callOriginalMethods, $cloneArguments); + $this->dependencies = $dependencies; + foreach ($this->tests as $test) { + if (!$test instanceof \PHPUnit\Framework\TestCase) { + // @codeCoverageIgnoreStart + continue; + // @codeCoverageIgnoreStart } + $test->setDependencies($dependencies); } - return $methods; } /** - * @throws ReflectionException - * - * @return MockMethod[] + * @return list */ - public function mockInterfaceMethods(string $interfaceName, bool $cloneArguments) : array + public function provides() : array { - try { - $class = new ReflectionClass($interfaceName); - // @codeCoverageIgnoreStart - } catch (\ReflectionException $e) { - throw new \PHPUnit\Framework\MockObject\ReflectionException($e->getMessage(), $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd - $methods = []; - foreach ($class->getMethods() as $method) { - $methods[] = \PHPUnit\Framework\MockObject\MockMethod::fromReflection($method, \false, $cloneArguments); + if ($this->providedTests === null) { + $this->providedTests = [new \PHPUnit\Framework\ExecutionOrderDependency($this->getName())]; } - return $methods; + return $this->providedTests; } /** - * @psalm-param class-string $interfaceName - * - * @throws ReflectionException - * - * @return ReflectionMethod[] + * @return list */ - private function userDefinedInterfaceMethods(string $interfaceName) : array + public function requires() : array { - try { - // @codeCoverageIgnoreStart - $interface = new ReflectionClass($interfaceName); - } catch (\ReflectionException $e) { - throw new \PHPUnit\Framework\MockObject\ReflectionException($e->getMessage(), $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd - $methods = []; - foreach ($interface->getMethods() as $method) { - if (!$method->isUserDefined()) { - continue; - } - $methods[] = $method; - } - return $methods; + // A DataProviderTestSuite does not have to traverse its child tests + // as these are inherited and cannot reference dataProvider rows directly + return $this->dependencies; } /** - * @throws ReflectionException - * @throws RuntimeException + * Returns the size of the each test created using the data provider(s). + * + * @throws InvalidArgumentException */ - private function getObject(\PHPUnit\Framework\MockObject\MockType $mockClass, $type = '', bool $callOriginalConstructor = \false, bool $callAutoload = \false, array $arguments = [], bool $callOriginalMethods = \false, object $proxyTarget = null, bool $returnValueGeneration = \true) + public function getSize() : int { - $className = $mockClass->generate(); - if ($callOriginalConstructor) { - if (count($arguments) === 0) { - $object = new $className(); - } else { - try { - $class = new ReflectionClass($className); - // @codeCoverageIgnoreStart - } catch (\ReflectionException $e) { - throw new \PHPUnit\Framework\MockObject\ReflectionException($e->getMessage(), $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd - $object = $class->newInstanceArgs($arguments); - } - } else { - try { - $object = (new Instantiator())->instantiate($className); - } catch (InstantiatorException $e) { - throw new \PHPUnit\Framework\MockObject\RuntimeException($e->getMessage()); - } - } - if ($callOriginalMethods) { - if (!is_object($proxyTarget)) { - if (count($arguments) === 0) { - $proxyTarget = new $type(); - } else { - try { - $class = new ReflectionClass($type); - // @codeCoverageIgnoreStart - } catch (\ReflectionException $e) { - throw new \PHPUnit\Framework\MockObject\ReflectionException($e->getMessage(), $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd - $proxyTarget = $class->newInstanceArgs($arguments); - } - } - $object->__phpunit_setOriginalObject($proxyTarget); - } - if ($object instanceof \PHPUnit\Framework\MockObject\MockObject) { - $object->__phpunit_setReturnValueGeneration($returnValueGeneration); - } - return $object; - } - /** - * @throws ClassIsFinalException - * @throws ClassIsReadonlyException - * @throws ReflectionException - * @throws RuntimeException - */ - private function generateMock(string $type, ?array $explicitMethods, string $mockClassName, bool $callOriginalClone, bool $callAutoload, bool $cloneArguments, bool $callOriginalMethods) : \PHPUnit\Framework\MockObject\MockClass - { - $classTemplate = $this->getTemplate('mocked_class.tpl'); - $additionalInterfaces = []; - $mockedCloneMethod = \false; - $unmockedCloneMethod = \false; - $isClass = \false; - $isInterface = \false; - $class = null; - $mockMethods = new \PHPUnit\Framework\MockObject\MockMethodSet(); - $_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']); - // @codeCoverageIgnoreStart - } catch (\ReflectionException $e) { - throw new \PHPUnit\Framework\MockObject\ReflectionException($e->getMessage(), $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd - if ($class->isFinal()) { - throw new \PHPUnit\Framework\MockObject\ClassIsFinalException($_mockClassName['fullClassName']); - } - if (method_exists($class, 'isReadOnly') && $class->isReadOnly()) { - throw new \PHPUnit\Framework\MockObject\ClassIsReadonlyException($_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); - // @codeCoverageIgnoreStart - } catch (\ReflectionException $e) { - throw new \PHPUnit\Framework\MockObject\ReflectionException($e->getMessage(), $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd - foreach ($this->userDefinedInterfaceMethods($_mockClassName['fullClassName']) as $method) { - $methodName = $method->getName(); - if ($class->hasMethod($methodName)) { - try { - $classMethod = $class->getMethod($methodName); - // @codeCoverageIgnoreStart - } catch (\ReflectionException $e) { - throw new \PHPUnit\Framework\MockObject\ReflectionException($e->getMessage(), $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd - if (!$this->canMockMethod($classMethod)) { - continue; - } - } - $mockMethods->addMethods(\PHPUnit\Framework\MockObject\MockMethod::fromReflection($method, $callOriginalMethods, $cloneArguments)); - } - $_mockClassName = $this->generateClassName($actualClassName, $_mockClassName['className'], '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'); - // @codeCoverageIgnoreStart - } catch (\ReflectionException $e) { - throw new \PHPUnit\Framework\MockObject\ReflectionException($e->getMessage(), $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd - if (!$cloneMethod->isFinal()) { - if ($callOriginalClone && !$isInterface) { - $unmockedCloneMethod = \true; - } else { - $mockedCloneMethod = \true; - } - } - } else { - $mockedCloneMethod = \true; - } - } - if ($isClass && $explicitMethods === []) { - $mockMethods->addMethods(...$this->mockClassMethods($_mockClassName['fullClassName'], $callOriginalMethods, $cloneArguments)); - } - if ($isInterface && ($explicitMethods === [] || $explicitMethods === null)) { - $mockMethods->addMethods(...$this->mockInterfaceMethods($_mockClassName['fullClassName'], $cloneArguments)); - } - if (is_array($explicitMethods)) { - foreach ($explicitMethods as $methodName) { - if ($class !== null && $class->hasMethod($methodName)) { - try { - $method = $class->getMethod($methodName); - // @codeCoverageIgnoreStart - } catch (\ReflectionException $e) { - throw new \PHPUnit\Framework\MockObject\ReflectionException($e->getMessage(), $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd - if ($this->canMockMethod($method)) { - $mockMethods->addMethods(\PHPUnit\Framework\MockObject\MockMethod::fromReflection($method, $callOriginalMethods, $cloneArguments)); - } - } else { - $mockMethods->addMethods(\PHPUnit\Framework\MockObject\MockMethod::fromName($_mockClassName['fullClassName'], $methodName, $cloneArguments)); - } - } - } - $mockedMethods = ''; - $configurable = []; - foreach ($mockMethods->asArray() as $mockMethod) { - $mockedMethods .= $mockMethod->generateCode(); - $configurable[] = new \PHPUnit\Framework\MockObject\ConfigurableMethod($mockMethod->getName(), $mockMethod->getReturnType()); - } - $method = ''; - if (!$mockMethods->hasMethod('method') && (!isset($class) || !$class->hasMethod('method'))) { - $method = PHP_EOL . ' use \\PHPUnit\\Framework\\MockObject\\Method;'; - } - $cloneTrait = ''; - if ($mockedCloneMethod) { - $cloneTrait = $this->mockedCloneMethod(); - } - if ($unmockedCloneMethod) { - $cloneTrait = $this->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' => $method]); - return new \PHPUnit\Framework\MockObject\MockClass($classTemplate->render(), $_mockClassName['className'], $configurable); - } - private function generateClassName(string $type, string $className, string $prefix) : array - { - 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[] = \PHPUnit\Framework\MockObject\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 !($this->isConstructor($method) || $method->isFinal() || $method->isPrivate() || $this->isMethodNameExcluded($method->getName())); - } - private function isMethodNameExcluded(string $name) : bool - { - return isset(self::EXCLUDED_METHOD_NAMES[$name]); - } - /** - * @throws RuntimeException - */ - private function getTemplate(string $template) : Template - { - $filename = __DIR__ . DIRECTORY_SEPARATOR . 'Generator' . DIRECTORY_SEPARATOR . $template; - if (!isset(self::$templates[$filename])) { - try { - self::$templates[$filename] = new Template($filename); - } catch (TemplateException $e) { - throw new \PHPUnit\Framework\MockObject\RuntimeException($e->getMessage(), $e->getCode(), $e); - } - } - return self::$templates[$filename]; - } - /** - * @see https://github.com/sebastianbergmann/phpunit/issues/4139#issuecomment-605409765 - */ - private function isConstructor(ReflectionMethod $method) : bool - { - $methodName = strtolower($method->getName()); - if ($methodName === '__construct') { - return \true; - } - if (PHP_MAJOR_VERSION >= 8) { - return \false; - } - $className = strtolower($method->getDeclaringClass()->getName()); - return $methodName === $className; - } - private function mockedCloneMethod() : string - { - if (PHP_MAJOR_VERSION >= 8) { - if (!trait_exists('\\PHPUnit\\Framework\\MockObject\\MockedCloneMethodWithVoidReturnType')) { - eval(self::MOCKED_CLONE_METHOD_WITH_VOID_RETURN_TYPE_TRAIT); - } - return PHP_EOL . ' use \\PHPUnit\\Framework\\MockObject\\MockedCloneMethodWithVoidReturnType;'; - } - if (!trait_exists('\\PHPUnit\\Framework\\MockObject\\MockedCloneMethodWithoutReturnType')) { - eval(self::MOCKED_CLONE_METHOD_WITHOUT_RETURN_TYPE_TRAIT); - } - return PHP_EOL . ' use \\PHPUnit\\Framework\\MockObject\\MockedCloneMethodWithoutReturnType;'; - } - private function unmockedCloneMethod() : string - { - if (PHP_MAJOR_VERSION >= 8) { - if (!trait_exists('\\PHPUnit\\Framework\\MockObject\\UnmockedCloneMethodWithVoidReturnType')) { - eval(self::UNMOCKED_CLONE_METHOD_WITH_VOID_RETURN_TYPE_TRAIT); - } - return PHP_EOL . ' use \\PHPUnit\\Framework\\MockObject\\UnmockedCloneMethodWithVoidReturnType;'; - } - if (!trait_exists('\\PHPUnit\\Framework\\MockObject\\UnmockedCloneMethodWithoutReturnType')) { - eval(self::UNMOCKED_CLONE_METHOD_WITHOUT_RETURN_TYPE_TRAIT); - } - return PHP_EOL . ' use \\PHPUnit\\Framework\\MockObject\\UnmockedCloneMethodWithoutReturnType;'; + [$className, $methodName] = explode('::', $this->getName()); + return TestUtil::getSize($className, $methodName); } } + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Error; -interface {intersection} extends {interfaces} +/** + * @internal + */ +final class Deprecated extends \PHPUnit\Framework\Error\Error { } -declare(strict_types=1); - -{prologue}{class_declaration} -{ - use \PHPUnit\Framework\MockObject\Api;{method}{clone} -{mocked_methods}}{epilogue} - - {modifier} function {reference}{method_name}({arguments_decl}){return_declaration} - {{deprecation} - $__phpunit_arguments = [{arguments_call}]; - $__phpunit_count = func_num_args(); - - if ($__phpunit_count > {arguments_count}) { - $__phpunit_arguments_tmp = func_get_args(); - - for ($__phpunit_i = {arguments_count}; $__phpunit_i < $__phpunit_count; $__phpunit_i++) { - $__phpunit_arguments[] = $__phpunit_arguments_tmp[$__phpunit_i]; - } - } - - $__phpunit_result = $this->__phpunit_getInvocationHandler()->invoke( - new \PHPUnit\Framework\MockObject\Invocation( - '{class_name}', '{method_name}', $__phpunit_arguments, '{return_type}', $this, {clone_arguments} - ) - ); - - return $__phpunit_result; - } - - {modifier} function {reference}{method_name}({arguments_decl}){return_declaration} - {{deprecation} - $__phpunit_arguments = [{arguments_call}]; - $__phpunit_count = func_num_args(); - - if ($__phpunit_count > {arguments_count}) { - $__phpunit_arguments_tmp = func_get_args(); - - for ($__phpunit_i = {arguments_count}; $__phpunit_i < $__phpunit_count; $__phpunit_i++) { - $__phpunit_arguments[] = $__phpunit_arguments_tmp[$__phpunit_i]; - } - } - - $this->__phpunit_getInvocationHandler()->invoke( - new \PHPUnit\Framework\MockObject\Invocation( - '{class_name}', '{method_name}', $__phpunit_arguments, '{return_type}', $this, {clone_arguments} - ) - ); - } + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Error; - {modifier} function {reference}{method_name}({arguments_decl}){return_declaration} +use PHPUnit\Framework\Exception; +/** + * @internal + */ +class Error extends Exception +{ + public function __construct(string $message, int $code, string $file, int $line, ?\Exception $previous = null) { - $__phpunit_arguments = [{arguments_call}]; - $__phpunit_count = func_num_args(); - - if ($__phpunit_count > {arguments_count}) { - $__phpunit_arguments_tmp = func_get_args(); - - for ($__phpunit_i = {arguments_count}; $__phpunit_i < $__phpunit_count; $__phpunit_i++) { - $__phpunit_arguments[] = $__phpunit_arguments_tmp[$__phpunit_i]; - } - } - - $this->__phpunit_getInvocationHandler()->invoke( - new \PHPUnit\Framework\MockObject\Invocation( - '{class_name}', '{method_name}', $__phpunit_arguments, '{return_type}', $this, {clone_arguments}, true - ) - ); - - return call_user_func_array(array($this->__phpunit_originalObject, "{method_name}"), $__phpunit_arguments); + parent::__construct($message, $code, $previous); + $this->file = $file; + $this->line = $line; } +} + {arguments_count}) { - $__phpunit_arguments_tmp = func_get_args(); - - for ($__phpunit_i = {arguments_count}; $__phpunit_i < $__phpunit_count; $__phpunit_i++) { - $__phpunit_arguments[] = $__phpunit_arguments_tmp[$__phpunit_i]; - } - } - - $this->__phpunit_getInvocationHandler()->invoke( - new \PHPUnit\Framework\MockObject\Invocation( - '{class_name}', '{method_name}', $__phpunit_arguments, '{return_type}', $this, {clone_arguments}, true - ) - ); - - call_user_func_array(array($this->__phpunit_originalObject, "{method_name}"), $__phpunit_arguments); - } -declare(strict_types=1); +declare (strict_types=1); +/* + * This file is part of PHPUnit. + * + * (c) Sebastian Bergmann + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Error; -{prologue}class {class_name} +/** + * @internal + */ +final class Notice extends \PHPUnit\Framework\Error\Error { - use {trait_name}; } -declare(strict_types=1); + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Error; - public function {method_name}({arguments}) - { - } +/** + * @internal + */ +final class Warning extends \PHPUnit\Framework\Error\Error +{ +} message = $message; + parent::__construct('Error'); + } + public function getMessage() : string + { + return $this->message; + } /** - * @var bool + * Returns a string representation of the test case. */ - private $isReturnTypeNullable = \false; + public function toString() : string + { + return 'Error'; + } /** - * @var bool + * @throws Exception + * + * @psalm-return never-return */ - 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; - 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] = Cloner::clone($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 null; - } - $intersection = \false; - $union = \false; - $unionContainsIntersections = \false; - if (strpos($this->returnType, '|') !== \false) { - $types = explode('|', $this->returnType); - $union = \true; - if (strpos($this->returnType, '(') !== \false) { - $unionContainsIntersections = \true; - } - } elseif (strpos($this->returnType, '&') !== \false) { - $types = explode('&', $this->returnType); - $intersection = \true; - } else { - $types = [$this->returnType]; - } - $types = array_map('strtolower', $types); - if (!$intersection && !$unionContainsIntersections) { - if (in_array('', $types, \true) || in_array('null', $types, \true) || in_array('mixed', $types, \true) || in_array('void', $types, \true)) { - return null; - } - if (in_array('true', $types, \true)) { - return \true; - } - if (in_array('false', $types, \true) || in_array('bool', $types, \true)) { - return \false; - } - if (in_array('float', $types, \true)) { - return 0.0; - } - if (in_array('int', $types, \true)) { - return 0; - } - if (in_array('string', $types, \true)) { - return ''; - } - if (in_array('array', $types, \true)) { - return []; - } - if (in_array('static', $types, \true)) { - try { - return (new Instantiator())->instantiate(get_class($this->object)); - } catch (Throwable $t) { - throw new \PHPUnit\Framework\MockObject\RuntimeException($t->getMessage(), (int) $t->getCode(), $t); - } - } - if (in_array('object', $types, \true)) { - return new stdClass(); - } - if (in_array('callable', $types, \true) || in_array('closure', $types, \true)) { - return static function () : void { - }; - } - if (in_array('traversable', $types, \true) || in_array('generator', $types, \true) || in_array('iterable', $types, \true)) { - $generator = static function () : \Generator { - yield from []; - }; - return $generator(); - } - if (!$union) { - try { - return (new \PHPUnit\Framework\MockObject\Generator())->getMock($this->returnType, [], [], '', \false); - } catch (Throwable $t) { - if ($t instanceof \PHPUnit\Framework\MockObject\Exception) { - throw $t; - } - throw new \PHPUnit\Framework\MockObject\RuntimeException($t->getMessage(), (int) $t->getCode(), $t); - } - } - } - if ($intersection && $this->onlyInterfaces($types)) { - try { - return (new \PHPUnit\Framework\MockObject\Generator())->getMockForInterfaces($types); - } catch (Throwable $t) { - throw new \PHPUnit\Framework\MockObject\RuntimeException(sprintf('Return value for %s::%s() cannot be generated: %s', $this->className, $this->methodName, $t->getMessage()), (int) $t->getCode()); - } - } - $reason = ''; - if ($union) { - $reason = ' because the declared return type is a union'; - } elseif ($intersection) { - $reason = ' because the declared return type is an intersection'; - } - throw new \PHPUnit\Framework\MockObject\RuntimeException(sprintf('Return value for %s::%s() cannot be generated%s, please configure a return value for this method', $this->className, $this->methodName, $reason)); - } - 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; - } - /** - * @psalm-param non-empty-list $types - */ - private function onlyInterfaces(array $types) : bool + protected function runTest() : void { - foreach ($types as $type) { - if (!interface_exists($type)) { - return \false; - } - } - return \true; + throw new \PHPUnit\Framework\Error($this->message); } } 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) : ?\PHPUnit\Framework\MockObject\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 MatcherAlreadyRegisteredException - */ - public function registerMatcher(string $id, \PHPUnit\Framework\MockObject\Matcher $matcher) : void - { - if (isset($this->matcherMap[$id])) { - throw new \PHPUnit\Framework\MockObject\MatcherAlreadyRegisteredException($id); - } - $this->matcherMap[$id] = $matcher; - } - public function expects(InvocationOrder $rule) : InvocationMocker - { - $matcher = new \PHPUnit\Framework\MockObject\Matcher($rule); - $this->addMatcher($matcher); - return new InvocationMocker($this, $matcher, ...$this->configurableMethods); - } - /** - * @throws Exception - * @throws RuntimeException - */ - public function invoke(\PHPUnit\Framework\MockObject\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 \PHPUnit\Framework\MockObject\ReturnValueNotConfiguredException($invocation); - if (strtolower($invocation->getMethodName()) === '__tostring') { - $this->deferredError = $exception; - return ''; - } - throw $exception; - } - return $invocation->generateReturnValue(); - } - public function matches(\PHPUnit\Framework\MockObject\Invocation $invocation) : bool - { - foreach ($this->matchers as $matcher) { - if (!$matcher->matches($invocation)) { - return \false; - } - } - return \true; - } - /** - * @throws Throwable - */ - public function verify() : void + public function __construct() { - foreach ($this->matchers as $matcher) { - $matcher->verify(); - } - if ($this->deferredError) { - throw $this->deferredError; - } + parent::__construct('Actual value is not an object', 0, null); } - private function addMatcher(\PHPUnit\Framework\MockObject\Matcher $matcher) : void + public function __toString() : string { - $this->matchers[] = $matcher; + return $this->getMessage() . PHP_EOL; } } 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 ExpectationFailedException - * @throws MatchBuilderNotFoundException - * @throws MethodNameNotConfiguredException - * @throws RuntimeException - */ - public function invoked(\PHPUnit\Framework\MockObject\Invocation $invocation) - { - if ($this->methodNameRule === null) { - throw new \PHPUnit\Framework\MockObject\MethodNameNotConfiguredException(); - } - if ($this->afterMatchBuilderId !== null) { - $matcher = $invocation->getObject()->__phpunit_getInvocationHandler()->lookupMatcher($this->afterMatchBuilderId); - if (!$matcher) { - throw new \PHPUnit\Framework\MockObject\MatchBuilderNotFoundException($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 \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - * @throws MatchBuilderNotFoundException - * @throws MethodNameNotConfiguredException - * @throws RuntimeException - */ - public function matches(\PHPUnit\Framework\MockObject\Invocation $invocation) : bool - { - if ($this->afterMatchBuilderId !== null) { - $matcher = $invocation->getObject()->__phpunit_getInvocationHandler()->lookupMatcher($this->afterMatchBuilderId); - if (!$matcher) { - throw new \PHPUnit\Framework\MockObject\MatchBuilderNotFoundException($this->afterMatchBuilderId); - } - assert($matcher instanceof self); - if (!$matcher->invocationRule->hasBeenInvoked()) { - return \false; - } - } - if ($this->methodNameRule === null) { - throw new \PHPUnit\Framework\MockObject\MethodNameNotConfiguredException(); - } - 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 \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - * @throws MethodNameNotConfiguredException + * Wrapper for getMessage() which is declared as final. */ - public function verify() : void - { - if ($this->methodNameRule === null) { - throw new \PHPUnit\Framework\MockObject\MethodNameNotConfiguredException(); - } - 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(); - $invocationIsAtMost = $this->invocationRule instanceof InvokedAtMostCount; - if (!$invocationIsAny && !$invocationIsNever && !$invocationIsAtMost) { - $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); + return $this->getMessage(); } } 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); - } } |string|string[] $type - */ - public function __construct(TestCase $testCase, $type) - { - $this->testCase = $testCase; - $this->type = $type; - $this->generator = new \PHPUnit\Framework\MockObject\Generator(); - } - /** - * Creates a mock object using a fluent interface. - * - * @throws \PHPUnit\Framework\InvalidArgumentException - * @throws ClassAlreadyExistsException - * @throws ClassIsFinalException - * @throws ClassIsReadonlyException - * @throws DuplicateMethodException - * @throws InvalidMethodNameException - * @throws OriginalConstructorInvocationRequiredException - * @throws ReflectionException - * @throws RuntimeException - * @throws UnknownTypeException - * - * @psalm-return MockObject&MockedType - */ - public function getMock() : \PHPUnit\Framework\MockObject\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. - * - * @psalm-return MockObject&MockedType - * - * @throws \PHPUnit\Framework\Exception - * @throws ReflectionException - * @throws RuntimeException - */ - public function getMockForAbstractClass() : \PHPUnit\Framework\MockObject\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. - * - * @psalm-return MockObject&MockedType - * - * @throws \PHPUnit\Framework\Exception - * @throws ReflectionException - * @throws RuntimeException - */ - public function getMockForTrait() : \PHPUnit\Framework\MockObject\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 - * - * @return $this - */ - public function setMethods(?array $methods = null) : self - { - if ($methods === null) { - $this->methods = $methods; - } else { - $this->methods = array_merge($this->methods ?? [], $methods); - } - return $this; - } - /** - * Specifies the subset of methods to mock, requiring each to exist in the class. - * - * @param string[] $methods - * - * @throws CannotUseOnlyMethodsException - * @throws ReflectionException - * - * @return $this - */ - public function onlyMethods(array $methods) : self - { - if (empty($methods)) { - $this->emptyMethodsArray = \true; - return $this; - } - try { - $reflector = new ReflectionClass($this->type); - // @codeCoverageIgnoreStart - } catch (\ReflectionException $e) { - throw new \PHPUnit\Framework\MockObject\ReflectionException($e->getMessage(), $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd - foreach ($methods as $method) { - if (!$reflector->hasMethod($method)) { - throw new \PHPUnit\Framework\MockObject\CannotUseOnlyMethodsException($this->type, $method); - } - } - $this->methods = array_merge($this->methods ?? [], $methods); - return $this; - } - /** - * Specifies methods that don't exist in the class which you want to mock. - * - * @param string[] $methods - * - * @throws CannotUseAddMethodsException - * @throws ReflectionException - * @throws RuntimeException - * - * @return $this - */ - public function addMethods(array $methods) : self - { - if (empty($methods)) { - $this->emptyMethodsArray = \true; - return $this; - } - try { - $reflector = new ReflectionClass($this->type); - // @codeCoverageIgnoreStart - } catch (\ReflectionException $e) { - throw new \PHPUnit\Framework\MockObject\ReflectionException($e->getMessage(), $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd - foreach ($methods as $method) { - if ($reflector->hasMethod($method)) { - throw new \PHPUnit\Framework\MockObject\CannotUseAddMethodsException($this->type, $method); - } - } - $this->methods = array_merge($this->methods ?? [], $methods); - return $this; - } - /** - * Specifies the subset of methods to not mock. Default is to mock all of them. - * - * @deprecated https://github.com/sebastianbergmann/phpunit/pull/3687 - * - * @throws ReflectionException - */ - public function setMethodsExcept(array $methods = []) : self - { - return $this->setMethods(array_diff($this->generator->getClassMethods($this->type), $methods)); - } - /** - * Specifies the arguments for the constructor. - * - * @return $this - */ - public function setConstructorArgs(array $args) : self - { - $this->constructorArgs = $args; - return $this; - } - /** - * Specifies the name for the mock class. - * - * @return $this - */ - public function setMockClassName(string $name) : self - { - $this->mockClassName = $name; - return $this; - } - /** - * Disables the invocation of the original constructor. - * - * @return $this - */ - public function disableOriginalConstructor() : self - { - $this->originalConstructor = \false; - return $this; - } - /** - * Enables the invocation of the original constructor. - * - * @return $this - */ - public function enableOriginalConstructor() : self - { - $this->originalConstructor = \true; - return $this; - } - /** - * Disables the invocation of the original clone constructor. - * - * @return $this - */ - public function disableOriginalClone() : self - { - $this->originalClone = \false; - return $this; - } - /** - * Enables the invocation of the original clone constructor. - * - * @return $this - */ - public function enableOriginalClone() : self - { - $this->originalClone = \true; - return $this; - } - /** - * Disables the use of class autoloading while creating the mock object. - * - * @return $this - */ - public function disableAutoload() : self - { - $this->autoload = \false; - return $this; - } - /** - * Enables the use of class autoloading while creating the mock object. - * - * @return $this - */ - public function enableAutoload() : self - { - $this->autoload = \true; - return $this; - } - /** - * Disables the cloning of arguments passed to mocked methods. - * - * @return $this - */ - public function disableArgumentCloning() : self - { - $this->cloneArguments = \false; - return $this; - } - /** - * Enables the cloning of arguments passed to mocked methods. - * - * @return $this - */ - public function enableArgumentCloning() : self + public function __construct(string $className, string $methodName, string $type) { - $this->cloneArguments = \true; - return $this; + parent::__construct(sprintf('%s is not an accepted argument type for comparison method %s::%s().', $type, $className, $methodName), 0, null); } - /** - * Enables the invocation of the original methods. - * - * @return $this - */ - public function enableProxyingToOriginalMethods() : self + public function __toString() : string { - $this->callOriginalMethods = \true; - return $this; + return $this->getMessage() . PHP_EOL; } - /** - * Disables the invocation of the original methods. - * - * @return $this - */ - public function disableProxyingToOriginalMethods() : self +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use const PHP_EOL; +use function sprintf; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class ComparisonMethodDoesNotDeclareBoolReturnTypeException extends \PHPUnit\Framework\Exception +{ + public function __construct(string $className, string $methodName) { - $this->callOriginalMethods = \false; - $this->proxyTarget = null; - return $this; + parent::__construct(sprintf('Comparison method %s::%s() does not declare bool return type.', $className, $methodName), 0, null); } - /** - * Sets the proxy target. - * - * @return $this - */ - public function setProxyTarget(object $object) : self + public function __toString() : string { - $this->proxyTarget = $object; - return $this; + return $this->getMessage() . PHP_EOL; } - /** - * @return $this - */ - public function allowMockingUnknownTypes() : self +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use const PHP_EOL; +use function sprintf; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class ComparisonMethodDoesNotDeclareExactlyOneParameterException extends \PHPUnit\Framework\Exception +{ + public function __construct(string $className, string $methodName) { - $this->allowMockingUnknownTypes = \true; - return $this; + parent::__construct(sprintf('Comparison method %s::%s() does not declare exactly one parameter.', $className, $methodName), 0, null); } - /** - * @return $this - */ - public function disallowMockingUnknownTypes() : self + public function __toString() : string { - $this->allowMockingUnknownTypes = \false; - return $this; + return $this->getMessage() . PHP_EOL; } - /** - * @return $this - */ - public function enableAutoReturnValueGeneration() : self +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use const PHP_EOL; +use function sprintf; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class ComparisonMethodDoesNotDeclareParameterTypeException extends \PHPUnit\Framework\Exception +{ + public function __construct(string $className, string $methodName) { - $this->returnValueGeneration = \true; - return $this; + parent::__construct(sprintf('Parameter of comparison method %s::%s() does not have a declared type.', $className, $methodName), 0, null); } - /** - * @return $this - */ - public function disableAutoReturnValueGeneration() : self + public function __toString() : string { - $this->returnValueGeneration = \false; - return $this; + return $this->getMessage() . PHP_EOL; } } classCode = $classCode; - $this->mockName = $mockName; - $this->configurableMethods = $configurableMethods; - } - /** - * @psalm-return class-string - */ - public function generate() : string + public function __construct(string $className, string $methodName) { - if (!class_exists($this->mockName, \false)) { - eval($this->classCode); - call_user_func([$this->mockName, '__phpunit_initConfigurableMethods'], ...$this->configurableMethods); - } - return $this->mockName; + parent::__construct(sprintf('Comparison method %s::%s() does not exist.', $className, $methodName), 0, null); } - public function getClassCode() : string + public function __toString() : string { - return $this->classCode; + return $this->getMessage() . PHP_EOL; } } + * + * 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 Error extends \PHPUnit\Framework\Exception implements \PHPUnit\Framework\SelfDescribing { /** - * @var 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; - /** - * @throws ReflectionException - * @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::getMethodParametersForDeclaration($method), self::getMethodParametersForCall($method), (new ReflectionMapper())->fromReturnType($method), $reference, $callOriginalMethod, $method->isStatic(), $deprecation); - } - public static function fromName(string $fullClassName, string $methodName, bool $cloneArguments) : self - { - return new self($fullClassName, $methodName, $cloneArguments, 'public', '', '', new UnknownType(), '', \false, \false, null); - } - 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) - { - $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; - } - public function getName() : string - { - return $this->methodName; - } - /** - * @throws RuntimeException + * Wrapper for getMessage() which is declared as final. */ - public function generateCode() : string - { - if ($this->static) { - $templateFile = 'mocked_static_method.tpl'; - } elseif ($this->returnType->isNever() || $this->returnType->isVoid()) { - $templateFile = sprintf('%s_method_never_or_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' => !empty($this->returnType->asString()) ? ': ' . $this->returnType->asString() : '', 'return_type' => $this->returnType->asString(), '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 + public function toString() : string { - return $this->returnType; + return $this->getMessage(); } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use function array_keys; +use function get_object_vars; +use PHPUnit\Util\Filter; +use RuntimeException; +use Throwable; +/** + * 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 +{ /** - * @throws RuntimeException + * @var array */ - private function getTemplate(string $template) : Template + protected $serializableTrace; + public function __construct($message = '', $code = 0, ?Throwable $previous = null) { - $filename = __DIR__ . DIRECTORY_SEPARATOR . 'Generator' . DIRECTORY_SEPARATOR . $template; - if (!isset(self::$templates[$filename])) { - try { - self::$templates[$filename] = new Template($filename); - } catch (TemplateException $e) { - throw new \PHPUnit\Framework\MockObject\RuntimeException($e->getMessage(), $e->getCode(), $e); - } + parent::__construct($message, $code, $previous); + $this->serializableTrace = $this->getTrace(); + foreach (array_keys($this->serializableTrace) as $key) { + unset($this->serializableTrace[$key]['args']); } - return self::$templates[$filename]; } - /** - * Returns the parameters of a function or method. - * - * @throws RuntimeException - */ - private static function getMethodParametersForDeclaration(ReflectionMethod $method) : string + public function __toString() : string { - $parameters = []; - $types = (new ReflectionMapper())->fromParameterTypes($method); - foreach ($method->getParameters() as $i => $parameter) { - $name = '$' . $parameter->getName(); - /* Note: PHP extensions may use empty names for reference arguments - * or "..." for methods taking a variable number of arguments. - */ - if ($name === '$' || $name === '$...') { - $name = '$arg' . $i; - } - $default = ''; - $reference = ''; - $typeDeclaration = ''; - if (!$types[$i]->type()->isUnknown()) { - $typeDeclaration = $types[$i]->type()->asString() . ' '; - } - if ($parameter->isPassedByReference()) { - $reference = '&'; - } - if ($parameter->isVariadic()) { - $name = '...' . $name; - } elseif ($parameter->isDefaultValueAvailable()) { - $default = ' = ' . self::exportDefaultValue($parameter); - } elseif ($parameter->isOptional()) { - $default = ' = null'; - } - $parameters[] = $typeDeclaration . $reference . $name . $default; + $string = \PHPUnit\Framework\TestFailure::exceptionToString($this); + if ($trace = Filter::getFilteredStacktrace($this)) { + $string .= "\n" . $trace; } - return implode(', ', $parameters); + return $string; } - /** - * Returns the parameters of a function or method. - * - * @throws ReflectionException - */ - private static function getMethodParametersForCall(ReflectionMethod $method) : string + public function __sleep() : array { - $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()) { - continue; - } - if ($parameter->isPassedByReference()) { - $parameters[] = '&' . $name; - } else { - $parameters[] = $name; - } - } - return implode(', ', $parameters); + return array_keys(get_object_vars($this)); } /** - * @throws ReflectionException + * Returns the serializable trace (without 'args'). */ - private static function exportDefaultValue(ReflectionParameter $parameter) : string + public function getSerializableTrace() : array { - try { - $defaultValue = $parameter->getDefaultValue(); - if (!is_object($defaultValue)) { - return (string) var_export($defaultValue, \true); - } - $parameterAsString = $parameter->__toString(); - return (string) explode(' = ', substr(substr($parameterAsString, strpos($parameterAsString, ' ') + strlen(' ')), 0, -2))[1]; - // @codeCoverageIgnoreStart - } catch (\ReflectionException $e) { - throw new \PHPUnit\Framework\MockObject\ReflectionException($e->getMessage(), $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd + return $this->serializableTrace; } } methods[strtolower($method->getName())] = $method; - } - } - /** - * @return MockMethod[] + * @var ComparisonFailure */ - public function asArray() : array + protected $comparisonFailure; + public function __construct(string $message, ?ComparisonFailure $comparisonFailure = null, ?Exception $previous = null) { - return array_values($this->methods); + $this->comparisonFailure = $comparisonFailure; + parent::__construct($message, 0, $previous); } - public function hasMethod(string $methodName) : bool + public function getComparisonFailure() : ?ComparisonFailure { - return array_key_exists(strtolower($methodName), $this->methods); + return $this->comparisonFailure; } } classCode = $classCode; - $this->mockName = $mockName; - } - /** - * @psalm-return class-string - */ - public function generate() : string + public static function create(int $argument, string $type) : self { - if (!class_exists($this->mockName, \false)) { - eval($this->classCode); + $stack = debug_backtrace(); + $function = $stack[1]['function']; + if (isset($stack[1]['class'])) { + $function = sprintf('%s::%s', $stack[1]['class'], $stack[1]['function']); } - return $this->mockName; + return new self(sprintf('Argument #%d of %s() must be %s %s', $argument, $function, in_array(lcfirst($type)[0], ['a', 'e', 'i', 'o', 'u'], \true) ? 'an' : 'a', $type)); } - public function getClassCode() : string + private function __construct(string $message = '', int $code = 0, ?\Exception $previous = null) { - return $this->classCode; + parent::__construct($message, $code, $previous); } } $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 \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - */ - 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 \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - */ - private function verifyInvocation(BaseInvocation $invocation, $callIndex) : void - { - if (!isset($this->parameterGroups[$callIndex])) { - // no parameter assertion for this call index - return; - } - $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())); - } - } } invocations); - } - public function hasBeenInvoked() : bool - { - return count($this->invocations) > 0; - } - public final function invoked(BaseInvocation $invocation) - { - $this->invocations[] = $invocation; - return $this->invokedDo($invocation); - } - public abstract function matches(BaseInvocation $invocation) : bool; - protected abstract function invokedDo(BaseInvocation $invocation); } 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 + * @var string */ - public function verify() : void + private $diff; + public function __construct(string $message, int $code, string $file, int $line, array $trace, string $diff) { - if ($this->currentIndex < $this->sequenceIndex) { - throw new ExpectationFailedException(sprintf('The expected invocation at index %s was never reached.', $this->sequenceIndex)); - } + parent::__construct($message, $code, $file, $line, $trace); + $this->diff = $diff; } - protected function invokedDo(BaseInvocation $invocation) : void + public function getDiff() : string { + return $this->diff; } } 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 - { - } } 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 - { - } } 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 - { - } } expectedCount = $expectedCount; - } - public function isNever() : bool - { - return $this->expectedCount === 0; - } - public function toString() : string + protected $syntheticTrace = []; + public function __construct(string $message, int $code, string $file, int $line, array $trace) { - return 'invoked ' . $this->expectedCount . ' time(s)'; + parent::__construct($message, $code); + $this->syntheticFile = $file; + $this->syntheticLine = $line; + $this->syntheticTrace = $trace; } - public function matches(BaseInvocation $invocation) : bool + public function getSyntheticFile() : string { - return \true; + return $this->syntheticFile; } - /** - * 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 + public function getSyntheticLine() : int { - $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)); - } + return $this->syntheticLine; } - /** - * @throws ExpectationFailedException - */ - protected function invokedDo(BaseInvocation $invocation) : void + public function getSyntheticTrace() : array { - $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); - } + return $this->syntheticTrace; } } 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()); - } - /** - * @throws \PHPUnit\Framework\ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public function matchesName(string $methodName) : bool - { - return (bool) $this->constraint->evaluate($methodName, '', \true); - } } 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 \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - */ - public function verify() : void - { - $this->doVerify(); - } - /** - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - */ - 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; - } } getMessage(); + } } + * Unlike PHPUnit\Framework\Exception, the complete stack of previous Exceptions + * is processed. * - * 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 function array_shift; -use function sprintf; -use PHPUnit\Framework\MockObject\Invocation; -use PHPUnit\SebastianBergmann\Exporter\Exporter; -/** * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class ConsecutiveCalls implements \PHPUnit\Framework\MockObject\Stub\Stub +final class ExceptionWrapper extends \PHPUnit\Framework\Exception { /** - * @var array + * @var string */ - private $stack; + protected $className; /** - * @var mixed + * @var null|ExceptionWrapper */ - private $value; - public function __construct(array $stack) + protected $previous; + /** + * @var null|WeakReference + */ + private $originalException; + public function __construct(Throwable $t) { - $this->stack = $stack; + // 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 invoke(Invocation $invocation) + public function __toString() : string { - $this->value = array_shift($this->stack); - if ($this->value instanceof \PHPUnit\Framework\MockObject\Stub\Stub) { - $this->value = $this->value->invoke($invocation); + $string = \PHPUnit\Framework\TestFailure::exceptionToString($this); + if ($trace = Filter::getFilteredStacktrace($this)) { + $string .= "\n" . $trace; } - return $this->value; + if ($this->previous) { + $string .= "\nCaused by\n" . $this->previous; + } + return $string; } - public function toString() : string + public function getClassName() : string { - $exporter = new Exporter(); - return sprintf('return user-specified value %s', $exporter->export($this->value)); + 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 (array_keys($this->serializableTrace) as $key) { + unset($this->serializableTrace[$key]['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 + { + // drop once PHP 7.3 support is removed + if (PHP_VERSION_ID < 70400) { + static $originalExceptions; + $instanceId = spl_object_hash($this); + if ($exceptionToStore) { + $originalExceptions[$instanceId] = $exceptionToStore; + } + return $originalExceptions[$instanceId] ?? null; + } + if ($exceptionToStore) { + $this->originalException = WeakReference::create($exceptionToStore); + } + return $this->originalException !== null ? $this->originalException->get() : null; + } +} exception = $exception; + // Split clone option and target + $parts = explode(' ', trim($annotation), 2); + if (count($parts) === 1) { + $cloneOption = ''; + $target = $parts[0]; + } else { + $cloneOption = $parts[0]; + $target = $parts[1]; + } + // Prefix provided class for targets assumed to be in scope + if ($target !== '' && strpos($target, '::') === \false) { + $target = $className . '::' . $target; + } + return new self($target, null, $cloneOption); } /** - * @throws Throwable + * @psalm-param list $dependencies + * + * @psalm-return list */ - public function invoke(Invocation $invocation) : void + public static function filterInvalid(array $dependencies) : array { - throw $this->exception; + return array_values(array_filter($dependencies, static function (self $d) { + return $d->isValid(); + })); } - public function toString() : string + /** + * @psalm-param list $existing + * @psalm-param list $additional + * + * @psalm-return list + */ + public static function mergeUnique(array $existing, array $additional) : array { - $exporter = new Exporter(); - return sprintf('raise user-specified exception %s', $exporter->export($this->exception)); + $existingTargets = array_map(static function ($dependency) { + return $dependency->getTarget(); + }, $existing); + foreach ($additional as $dependency) { + if (in_array($dependency->getTarget(), $existingTargets, \true)) { + continue; + } + $existingTargets[] = $dependency->getTarget(); + $existing[] = $dependency; + } + return $existing; } -} - - * - * 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 function sprintf; -use PHPUnit\Framework\MockObject\Invocation; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class ReturnArgument implements \PHPUnit\Framework\MockObject\Stub\Stub -{ /** - * @var int + * @psalm-param list $left + * @psalm-param list $right + * + * @psalm-return list */ - private $argumentIndex; - public function __construct($argumentIndex) + public static function diff(array $left, array $right) : array { - $this->argumentIndex = $argumentIndex; + if ($right === []) { + return $left; + } + if ($left === []) { + return []; + } + $diff = []; + $rightTargets = array_map(static function ($dependency) { + return $dependency->getTarget(); + }, $right); + foreach ($left as $dependency) { + if (in_array($dependency->getTarget(), $rightTargets, \true)) { + continue; + } + $diff[] = $dependency; + } + return $diff; } - public function invoke(Invocation $invocation) + public function __construct(string $classOrCallableName, ?string $methodName = null, ?string $option = null) { - if (isset($invocation->getParameters()[$this->argumentIndex])) { - return $invocation->getParameters()[$this->argumentIndex]; + if ($classOrCallableName === '') { + return; + } + if (strpos($classOrCallableName, '::') !== \false) { + [$this->className, $this->methodName] = explode('::', $classOrCallableName); + } else { + $this->className = $classOrCallableName; + $this->methodName = !empty($methodName) ? $methodName : 'class'; + } + if ($option === 'clone') { + $this->useDeepClone = \true; + } elseif ($option === 'shallowClone') { + $this->useShallowClone = \true; } } - public function toString() : string + public function __toString() : string { - return sprintf('return argument #%d', $this->argumentIndex); + return $this->getTarget(); + } + public function isValid() : bool + { + // Invalid dependencies can be declared and are skipped by the runner + return $this->className !== '' && $this->methodName !== ''; + } + public function useShallowClone() : bool + { + return $this->useShallowClone; + } + public function useDeepClone() : bool + { + return $this->useDeepClone; + } + public function targetIsClass() : bool + { + return $this->methodName === 'class'; + } + public function getTarget() : string + { + return $this->isValid() ? $this->className . '::' . $this->methodName : ''; + } + public function getTargetClassName() : string + { + return $this->className; } } 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'; - } } reference =& $reference; + parent::__construct($className . '::' . $methodName); + $this->message = $message; } - public function invoke(Invocation $invocation) + public function getMessage() : string { - return $this->reference; + return $this->message; } + /** + * Returns a string representation of the test case. + * + * @throws InvalidArgumentException + */ public function toString() : string { - $exporter = new Exporter(); - return sprintf('return user-specified reference %s', $exporter->export($this->reference)); + return $this->getName(); + } + /** + * @throws Exception + */ + protected function runTest() : void + { + $this->markTestIncomplete($this->message); } } getObject(); - } - public function toString() : string - { - return 'return the current object'; - } } value = $value; + if (isset(static::$__phpunit_configurableMethods)) { + throw new \PHPUnit\Framework\MockObject\ConfigurableMethodsAlreadyInitializedException('Configurable methods is already initialized and can not be reinitialized'); + } + static::$__phpunit_configurableMethods = $configurableMethods; } - public function invoke(Invocation $invocation) + /** @noinspection MagicMethodsValidityInspection */ + public function __phpunit_setOriginalObject($originalObject) : void { - return $this->value; + $this->__phpunit_originalObject = $originalObject; } - public function toString() : string + /** @noinspection MagicMethodsValidityInspection */ + public function __phpunit_setReturnValueGeneration(bool $returnValueGeneration) : void { - $exporter = new Exporter(); - return sprintf('return user-specified value %s', $exporter->export($this->value)); + $this->__phpunit_returnValueGeneration = $returnValueGeneration; } -} - - * - * 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 function array_pop; -use function count; -use function is_array; -use PHPUnit\Framework\MockObject\Invocation; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class ReturnValueMap implements \PHPUnit\Framework\MockObject\Stub\Stub -{ - /** - * @var array - */ - private $valueMap; - public function __construct(array $valueMap) + /** @noinspection MagicMethodsValidityInspection */ + public function __phpunit_getInvocationHandler() : \PHPUnit\Framework\MockObject\InvocationHandler { - $this->valueMap = $valueMap; + if ($this->__phpunit_invocationMocker === null) { + $this->__phpunit_invocationMocker = new \PHPUnit\Framework\MockObject\InvocationHandler(static::$__phpunit_configurableMethods, $this->__phpunit_returnValueGeneration); + } + return $this->__phpunit_invocationMocker; } - public function invoke(Invocation $invocation) + /** @noinspection MagicMethodsValidityInspection */ + public function __phpunit_hasMatchers() : bool { - $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; - } + 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 toString() : string + public function expects(InvocationOrder $matcher) : InvocationMockerBuilder { - return 'return value from a map'; + return $this->__phpunit_getInvocationHandler()->expects($matcher); } } expects(new AnyInvokedCount()); + return call_user_func_array([$expects, 'method'], func_get_args()); + } } + * @var InvocationHandler */ - public function provides() : array; + private $invocationHandler; /** - * @return list + * @var Matcher */ - public function requires() : array; -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. + private $matcher; + /** + * @var ConfigurableMethod[] + */ + private $configurableMethods; + public function __construct(InvocationHandler $handler, Matcher $matcher, ConfigurableMethod ...$configurableMethods) + { + $this->invocationHandler = $handler; + $this->matcher = $matcher; + $this->configurableMethods = $configurableMethods; + } + /** + * @throws MatcherAlreadyRegisteredException + * + * @return $this + */ + public function id($id) : self + { + $this->invocationHandler->registerMatcher($id, $this->matcher); + return $this; + } + /** + * @return $this + */ + public function will(Stub $stub) : \PHPUnit\Framework\MockObject\Builder\Identity + { + $this->matcher->setStub($stub); + return $this; + } + /** + * @param mixed $value + * @param mixed[] $nextValues + * + * @throws IncompatibleReturnValueException + */ + public function willReturn($value, ...$nextValues) : self + { + if (count($nextValues) === 0) { + $this->ensureTypeOfReturnValues([$value]); + $stub = $value instanceof Stub ? $value : new ReturnStub($value); + } else { + $values = array_merge([$value], $nextValues); + $this->ensureTypeOfReturnValues($values); + $stub = new ConsecutiveCalls($values); + } + return $this->will($stub); + } + 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); + } + 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); + } + /** + * @return $this + */ + public function after($id) : self + { + $this->matcher->setAfterMatchBuilderId($id); + return $this; + } + /** + * @param mixed[] $arguments + * + * @throws \PHPUnit\Framework\Exception + * @throws MethodNameNotConfiguredException + * @throws MethodParametersAlreadyConfiguredException + * + * @return $this + */ + public function with(...$arguments) : self + { + $this->ensureParametersCanBeConfigured(); + $this->matcher->setParametersRule(new Rule\Parameters($arguments)); + return $this; + } + /** + * @param array ...$arguments + * + * @throws \PHPUnit\Framework\Exception + * @throws MethodNameNotConfiguredException + * @throws MethodParametersAlreadyConfiguredException + * + * @return $this + * + * @deprecated + */ + public function withConsecutive(...$arguments) : self + { + $this->ensureParametersCanBeConfigured(); + $this->matcher->setParametersRule(new Rule\ConsecutiveParameters($arguments)); + return $this; + } + /** + * @throws MethodNameNotConfiguredException + * @throws MethodParametersAlreadyConfiguredException + * + * @return $this + */ + public function withAnyParameters() : self + { + $this->ensureParametersCanBeConfigured(); + $this->matcher->setParametersRule(new Rule\AnyParameters()); + return $this; + } + /** + * @param Constraint|string $constraint + * + * @throws InvalidArgumentException + * @throws MethodCannotBeConfiguredException + * @throws MethodNameAlreadyConfiguredException + * + * @return $this + */ + public function method($constraint) : self + { + if ($this->matcher->hasMethodNameRule()) { + throw new MethodNameAlreadyConfiguredException(); + } + $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 MethodCannotBeConfiguredException($constraint); + } + $this->matcher->setMethodNameRule(new Rule\MethodName($constraint)); + return $this; + } + /** + * @throws MethodNameNotConfiguredException + * @throws MethodParametersAlreadyConfiguredException + */ + private function ensureParametersCanBeConfigured() : void + { + if (!$this->matcher->hasMethodNameRule()) { + throw new MethodNameNotConfiguredException(); + } + if ($this->matcher->hasParametersRule()) { + throw new MethodParametersAlreadyConfiguredException(); + } + } + 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; + } + /** + * @throws IncompatibleReturnValueException + */ + private function ensureTypeOfReturnValues(array $values) : void + { + $configuredMethod = $this->getConfiguredMethod(); + if ($configuredMethod === null) { + return; + } + foreach ($values as $value) { + if (!$configuredMethod->mayReturn($value)) { + throw new IncompatibleReturnValueException($configuredMethod, $value); + } + } + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. */ -namespace PHPUnit\Framework; +namespace PHPUnit\Framework\MockObject\Builder; +use PHPUnit\Framework\MockObject\Stub\Stub; +use Throwable; /** - * @internal This class is not covered by the backward compatibility promise for PHPUnit + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -interface SelfDescribing +interface InvocationStubber { + public function will(Stub $stub) : \PHPUnit\Framework\MockObject\Builder\Identity; + /** @return self */ + public function willReturn($value, ...$nextValues); /** - * Returns a string representation of the object. + * @param mixed $reference + * + * @return self */ - public function toString() : string; + public function willReturnReference(&$reference); + /** + * @param array> $valueMap + * + * @return self + */ + public function willReturnMap(array $valueMap); + /** + * @param int $argumentIndex + * + * @return self + */ + public function willReturnArgument($argumentIndex); + /** + * @param callable $callback + * + * @return self + */ + public function willReturnCallback($callback); + /** @return self */ + public function willReturnSelf(); + /** + * @param mixed $values + * + * @return self + */ + public function willReturnOnConsecutiveCalls(...$values); + /** @return self */ + public function willThrowException(Throwable $exception); } message = $message; - } - public function getMessage() : string - { - return $this->message; - } + public function after($id); /** - * Returns a string representation of the test case. + * 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. * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * Some examples: + * + * // match first parameter with value 2 + * $b->with(2); + * // match first parameter with value 'smock' and second identical to 42 + * $b->with('smock', new PHPUnit\Framework\Constraint\IsEqual(42)); + * + * + * @return ParametersMatch */ - public function toString() : string - { - return $this->getName(); - } + public function with(...$arguments); /** - * @throws Exception + * Sets a rule which allows any kind of parameters. + * + * Some examples: + * + * // match any number of parameters + * $b->withAnyParameters(); + * + * + * @return ParametersMatch */ - protected function runTest() : void - { - $this->markTestSkipped($this->message); - } + public function withAnyParameters(); } getName(); - if (!$theClass->isInstantiable()) { - return new \PHPUnit\Framework\ErrorTestCase(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 \PHPUnit\Framework\Exception('No valid test provided.'); - } - $parameters = $constructor->getParameters(); - // TestCase() or TestCase($name) - if (count($parameters) < 2) { - $test = $this->buildTestWithoutData($className); - } else { - try { - $data = TestUtil::getProvidedData($className, $methodName); - } catch (\PHPUnit\Framework\IncompleteTestError $e) { - $message = sprintf("Test for %s::%s marked incomplete by data provider\n%s", $className, $methodName, $this->throwableToString($e)); - $data = new \PHPUnit\Framework\IncompleteTestCase($className, $methodName, $message); - } catch (\PHPUnit\Framework\SkippedTestError $e) { - $message = sprintf("Test for %s::%s skipped by data provider\n%s", $className, $methodName, $this->throwableToString($e)); - $data = new \PHPUnit\Framework\SkippedTestCase($className, $methodName, $message); - } catch (Throwable $t) { - $message = sprintf("The data provider specified for %s::%s is invalid.\n%s", $className, $methodName, $this->throwableToString($t)); - $data = new \PHPUnit\Framework\ErrorTestCase($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 \PHPUnit\Framework\TestCase) { - $test->setName($methodName); - $this->configureTestCase($test, $runTestInSeparateProcess, $preserveGlobalState, $runClassInSeparateProcess, $backupSettings); - } - return $test; + $this->name = $name; + $this->returnType = $returnType; } - /** @psalm-param class-string $className */ - private function buildTestWithoutData(string $className) + public function getName() : string { - return new $className(); + return $this->name; } - /** @psalm-param class-string $className */ - private function buildDataProviderTestSuite(string $methodName, string $className, $data, bool $runTestInSeparateProcess, ?bool $preserveGlobalState, bool $runClassInSeparateProcess, array $backupSettings) : \PHPUnit\Framework\DataProviderTestSuite + public function mayReturn($value) : bool { - $dataProviderTestSuite = new \PHPUnit\Framework\DataProviderTestSuite($className . '::' . $methodName); - $groups = TestUtil::getGroups($className, $methodName); - if ($data instanceof \PHPUnit\Framework\ErrorTestCase || $data instanceof \PHPUnit\Framework\SkippedTestCase || $data instanceof \PHPUnit\Framework\IncompleteTestCase) { - $dataProviderTestSuite->addTest($data, $groups); - } else { - foreach ($data as $_dataName => $_data) { - $_test = new $className($methodName, $_data, $_dataName); - assert($_test instanceof \PHPUnit\Framework\TestCase); - $this->configureTestCase($_test, $runTestInSeparateProcess, $preserveGlobalState, $runClassInSeparateProcess, $backupSettings); - $dataProviderTestSuite->addTest($_test, $groups); - } + if ($value === null && $this->returnType->allowsNull()) { + return \true; } - return $dataProviderTestSuite; + return $this->returnType->isAssignable(Type::fromValue($value, \false)); } - private function configureTestCase(\PHPUnit\Framework\TestCase $test, bool $runTestInSeparateProcess, ?bool $preserveGlobalState, bool $runClassInSeparateProcess, array $backupSettings) : void + public function getReturnTypeDeclaration() : string { - 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']); - } + return $this->returnType->asString(); } - private function throwableToString(Throwable $t) : string +} + + * + * 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 \PHPUnit\Framework\MockObject\Exception +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject; + +use function sprintf; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class CannotUseAddMethodsException extends \PHPUnit\Framework\Exception implements \PHPUnit\Framework\MockObject\Exception +{ + public function __construct(string $type, string $methodName) { - $message = $t->getMessage(); - if (empty(trim($message))) { - $message = ''; - } - if ($t instanceof InvalidDataSetException) { - return sprintf("%s\n%s", $message, Filter::getFilteredStacktrace($t)); - } - return sprintf("%s: %s\n%s", get_class($t), $message, Filter::getFilteredStacktrace($t)); + parent::__construct(sprintf('Trying to configure method "%s" with addMethods(), but it exists in class "%s". Use onlyMethods() for methods that exist in the class', $methodName, $type)); } } > - */ - protected $backupStaticAttributesExcludeList = []; - /** - * @var array> - * - * @deprecated Use $backupStaticAttributesExcludeList instead - */ - protected $backupStaticAttributesBlacklist = []; - /** - * @var ?bool - */ - protected $runTestInSeparateProcess; - /** - * @var bool - */ - protected $preserveGlobalState = \true; - /** - * @var list - */ - protected $providedTests = []; - /** - * @var ?bool - */ - private $runClassInSeparateProcess; - /** - * @var bool - */ - private $inIsolation = \false; - /** - * @var array - */ - private $data; - /** - * @var int|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 list - */ - private $dependencies = []; - /** - * @var array - */ - private $dependencyInput = []; - /** - * @var array - */ - 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 + public function __construct(string $type, string $methodName) { - return new AnyInvokedCountMatcher(); + parent::__construct(sprintf('Trying to configure method "%s" with onlyMethods(), but it does not exist in class "%s". Use addMethods() for methods that do not exist in the class', $methodName, $type)); } - /** - * Returns a matcher that matches when the method is never executed. - */ - public static function never() : InvokedCountMatcher +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject; + +use function sprintf; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class ClassAlreadyExistsException extends \PHPUnit\Framework\Exception implements \PHPUnit\Framework\MockObject\Exception +{ + public function __construct(string $className) { - return new InvokedCountMatcher(0); + parent::__construct(sprintf('Class "%s" already exists', $className)); } - /** - * Returns a matcher that matches when the method is executed - * at least N times. - */ - public static function atLeast(int $requiredInvocations) : InvokedAtLeastCountMatcher +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject; + +use function sprintf; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class ClassIsFinalException extends \PHPUnit\Framework\Exception implements \PHPUnit\Framework\MockObject\Exception +{ + public function __construct(string $className) { - return new InvokedAtLeastCountMatcher($requiredInvocations); + parent::__construct(sprintf('Class "%s" is declared "final" and cannot be doubled', $className)); } - /** - * Returns a matcher that matches when the method is executed at least once. - */ - public static function atLeastOnce() : InvokedAtLeastOnceMatcher +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject; + +use function sprintf; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class ClassIsReadonlyException extends \PHPUnit\Framework\Exception implements \PHPUnit\Framework\MockObject\Exception +{ + public function __construct(string $className) { - return new InvokedAtLeastOnceMatcher(); + parent::__construct(sprintf('Class "%s" is declared "readonly" and cannot be doubled', $className)); } +} + + * + * 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 \PHPUnit\Framework\MockObject\Exception +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject; + +use function array_diff_assoc; +use function array_unique; +use function implode; +use function sprintf; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class DuplicateMethodException extends \PHPUnit\Framework\Exception implements \PHPUnit\Framework\MockObject\Exception +{ /** - * Returns a matcher that matches when the method is executed exactly once. + * @psalm-param list $methods */ - public static function once() : InvokedCountMatcher + public function __construct(array $methods) { - return new InvokedCountMatcher(1); + parent::__construct(sprintf('Cannot double using a method list that contains duplicates: "%s" (duplicate: "%s")', implode(', ', $methods), implode(', ', array_unique(array_diff_assoc($methods, array_unique($methods)))))); } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject; + +use Throwable; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +interface Exception extends Throwable +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject; + +use function get_class; +use function gettype; +use function is_object; +use function sprintf; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class IncompatibleReturnValueException extends \PHPUnit\Framework\Exception implements \PHPUnit\Framework\MockObject\Exception +{ /** - * Returns a matcher that matches when the method is executed - * exactly $count times. + * @param mixed $value */ - public static function exactly(int $count) : InvokedCountMatcher + public function __construct(\PHPUnit\Framework\MockObject\ConfigurableMethod $method, $value) { - return new InvokedCountMatcher($count); + parent::__construct(sprintf('Method %s may not return value of type %s, its declared return type is "%s"', $method->getName(), is_object($value) ? get_class($value) : gettype($value), $method->getReturnTypeDeclaration())); } - /** - * Returns a matcher that matches when the method is executed - * at most N times. - */ - public static function atMost(int $allowedInvocations) : InvokedAtMostCountMatcher +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject; + +use function sprintf; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class InvalidMethodNameException extends \PHPUnit\Framework\Exception implements \PHPUnit\Framework\MockObject\Exception +{ + public function __construct(string $method) { - return new InvokedAtMostCountMatcher($allowedInvocations); + parent::__construct(sprintf('Cannot double method with invalid name "%s"', $method)); } - /** - * Returns a matcher that matches when the method is executed - * at the given index. - * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4297 - * - * @codeCoverageIgnore - */ - public static function at(int $index) : InvokedAtIndexMatcher - { - $stack = debug_backtrace(); - while (!empty($stack)) { - $frame = array_pop($stack); - if (isset($frame['object']) && $frame['object'] instanceof self) { - $frame['object']->addWarning('The at() matcher has been deprecated. It will be removed in PHPUnit 10. Please refactor your test to not rely on the order in which methods are invoked.'); - break; - } - } - 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 +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject; + +use function sprintf; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class MatchBuilderNotFoundException extends \PHPUnit\Framework\Exception implements \PHPUnit\Framework\MockObject\Exception +{ + public function __construct(string $id) { - return new ReturnCallbackStub($callback); + parent::__construct(sprintf('No builder found for match builder identification <%s>', $id)); } - /** - * Returns the current object. - * - * This method is useful when mocking a fluent interface. - */ - public static function returnSelf() : ReturnSelfStub +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject; + +use function sprintf; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class MatcherAlreadyRegisteredException extends \PHPUnit\Framework\Exception implements \PHPUnit\Framework\MockObject\Exception +{ + public function __construct(string $id) { - return new ReturnSelfStub(); + parent::__construct(sprintf('Matcher with id <%s> is already registered', $id)); } - public static function throwException(Throwable $exception) : ExceptionStub +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject; + +use function sprintf; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class MethodCannotBeConfiguredException extends \PHPUnit\Framework\Exception implements \PHPUnit\Framework\MockObject\Exception +{ + public function __construct(string $method) { - return new ExceptionStub($exception); + parent::__construct(sprintf('Trying to configure method "%s" which cannot be configured because it does not exist, has not been specified, is final, or is static', $method)); } - public static function onConsecutiveCalls(...$args) : ConsecutiveCallsStub +} + + * + * 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 MethodNameAlreadyConfiguredException extends \PHPUnit\Framework\Exception implements \PHPUnit\Framework\MockObject\Exception +{ + public function __construct() { - return new ConsecutiveCallsStub($args); + parent::__construct('Method name is already configured'); } - /** - * @param int|string $dataName - * - * @internal This method is not covered by the backward compatibility promise for PHPUnit - */ - public function __construct(?string $name = null, array $data = [], $dataName = '') +} + + * + * 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 MethodNameNotConfiguredException extends \PHPUnit\Framework\Exception implements \PHPUnit\Framework\MockObject\Exception +{ + public function __construct() { - if ($name !== null) { - $this->setName($name); - } - $this->data = $data; - $this->dataName = $dataName; + parent::__construct('Method name is not configured'); } - /** - * This method is called before the first test of this test class is run. - */ - public static function setUpBeforeClass() : void +} + + * + * 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 MethodParametersAlreadyConfiguredException extends \PHPUnit\Framework\Exception implements \PHPUnit\Framework\MockObject\Exception +{ + public function __construct() { + parent::__construct('Method parameters already configured'); } - /** - * This method is called after the last test of this test class is run. - */ - public static function tearDownAfterClass() : void +} + + * + * 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 OriginalConstructorInvocationRequiredException extends \PHPUnit\Framework\Exception implements \PHPUnit\Framework\MockObject\Exception +{ + public function __construct() { + parent::__construct('Proxying to original methods requires invoking the original constructor'); } - /** - * This method is called before each test. - */ - protected function setUp() : void +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject; + +use RuntimeException; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class ReflectionException extends RuntimeException implements \PHPUnit\Framework\MockObject\Exception +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject; + +use function sprintf; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class ReturnValueNotConfiguredException extends \PHPUnit\Framework\Exception implements \PHPUnit\Framework\MockObject\Exception +{ + public function __construct(\PHPUnit\Framework\MockObject\Invocation $invocation) { + parent::__construct(sprintf('Return value inference disabled and no expectation set up for %s::%s()', $invocation->getClassName(), $invocation->getMethodName())); } - /** - * Performs assertions shared by all tests of a test case. - * - * This method is called between setUp() and test. - */ - protected function assertPreConditions() : void +} + + * + * 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 \PHPUnit\Framework\MockObject\Exception +{ +} + + * + * 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 SoapExtensionNotAvailableException extends \PHPUnit\Framework\Exception implements \PHPUnit\Framework\MockObject\Exception +{ + public function __construct() { + parent::__construct('The SOAP extension is required to generate a test double from WSDL'); } - /** - * Performs assertions shared by all tests of a test case. - * - * This method is called between test and tearDown(). - */ - protected function assertPostConditions() : void +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject; + +use function sprintf; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class UnknownClassException extends \PHPUnit\Framework\Exception implements \PHPUnit\Framework\MockObject\Exception +{ + public function __construct(string $className) { + parent::__construct(sprintf('Class "%s" does not exist', $className)); } - /** - * This method is called after each test. - */ - protected function tearDown() : void +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject; + +use function sprintf; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class UnknownTraitException extends \PHPUnit\Framework\Exception implements \PHPUnit\Framework\MockObject\Exception +{ + public function __construct(string $traitName) { + parent::__construct(sprintf('Trait "%s" does not exist', $traitName)); } - /** - * Returns a string representation of the test case. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws Exception - */ - public function toString() : string +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject; + +use function sprintf; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class UnknownTypeException extends \PHPUnit\Framework\Exception implements \PHPUnit\Framework\MockObject\Exception +{ + public function __construct(string $type) { - try { - $class = new ReflectionClass($this); - // @codeCoverageIgnoreStart - } catch (ReflectionException $e) { - throw new \PHPUnit\Framework\Exception($e->getMessage(), $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd - $buffer = sprintf('%s::%s', $class->name, $this->getName(\false)); - return $buffer . $this->getDataSetAsString(); + parent::__construct(sprintf('Class or interface "%s" does not exist', $type)); } - public function count() : int +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject; + +use const DIRECTORY_SEPARATOR; +use const PHP_EOL; +use const PHP_MAJOR_VERSION; +use const PREG_OFFSET_CAPTURE; +use const WSDL_CACHE_NONE; +use function array_merge; +use function array_pop; +use function array_unique; +use function class_exists; +use function count; +use function explode; +use function extension_loaded; +use function implode; +use function in_array; +use function interface_exists; +use function is_array; +use function is_object; +use function md5; +use function method_exists; +use function mt_rand; +use function preg_match; +use function preg_match_all; +use function range; +use function serialize; +use function sort; +use function sprintf; +use function str_replace; +use function strlen; +use function strpos; +use function strtolower; +use function substr; +use function trait_exists; +use PHPUnitPHAR\Doctrine\Instantiator\Exception\ExceptionInterface as InstantiatorException; +use PHPUnitPHAR\Doctrine\Instantiator\Instantiator; +use Exception; +use Iterator; +use IteratorAggregate; +use PHPUnit\Framework\InvalidArgumentException; +use ReflectionClass; +use ReflectionMethod; +use PHPUnitPHAR\SebastianBergmann\Template\Exception as TemplateException; +use PHPUnitPHAR\SebastianBergmann\Template\Template; +use SoapClient; +use SoapFault; +use Throwable; +use Traversable; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class Generator +{ + private const MOCKED_CLONE_METHOD_WITH_VOID_RETURN_TYPE_TRAIT = <<<'EOT' +namespace PHPUnit\Framework\MockObject; + +trait MockedCloneMethodWithVoidReturnType +{ + public function __clone(): void { - return 1; + $this->__phpunit_invocationMocker = clone $this->__phpunit_getInvocationHandler(); } - public function getActualOutputForAssertion() : string +} +EOT; + private const MOCKED_CLONE_METHOD_WITHOUT_RETURN_TYPE_TRAIT = <<<'EOT' +namespace PHPUnit\Framework\MockObject; + +trait MockedCloneMethodWithoutReturnType +{ + public function __clone() { - $this->outputRetrievedForAssertion = \true; - return $this->getActualOutput(); + $this->__phpunit_invocationMocker = clone $this->__phpunit_getInvocationHandler(); } - public function expectOutputRegex(string $expectedRegex) : void +} +EOT; + private const UNMOCKED_CLONE_METHOD_WITH_VOID_RETURN_TYPE_TRAIT = <<<'EOT' +namespace PHPUnit\Framework\MockObject; + +trait UnmockedCloneMethodWithVoidReturnType +{ + public function __clone(): void { - $this->outputExpectedRegex = $expectedRegex; + $this->__phpunit_invocationMocker = clone $this->__phpunit_getInvocationHandler(); + + parent::__clone(); } - public function expectOutputString(string $expectedString) : void +} +EOT; + private const UNMOCKED_CLONE_METHOD_WITHOUT_RETURN_TYPE_TRAIT = <<<'EOT' +namespace PHPUnit\Framework\MockObject; + +trait UnmockedCloneMethodWithoutReturnType +{ + public function __clone() { - $this->outputExpectedString = $expectedString; + $this->__phpunit_invocationMocker = clone $this->__phpunit_getInvocationHandler(); + + parent::__clone(); } +} +EOT; /** - * @psalm-param class-string<\Throwable> $exception + * @var array */ - public function expectException(string $exception) : void - { - // @codeCoverageIgnoreStart - switch ($exception) { - case Deprecated::class: - $this->addWarning('Expecting E_DEPRECATED and E_USER_DEPRECATED is deprecated and will no longer be possible in PHPUnit 10.'); - break; - case Error::class: - $this->addWarning('Expecting E_ERROR and E_USER_ERROR is deprecated and will no longer be possible in PHPUnit 10.'); - break; - case Notice::class: - $this->addWarning('Expecting E_STRICT, E_NOTICE, and E_USER_NOTICE is deprecated and will no longer be possible in PHPUnit 10.'); - break; - case WarningError::class: - $this->addWarning('Expecting E_WARNING and E_USER_WARNING is deprecated and will no longer be possible in PHPUnit 10.'); - break; - } - // @codeCoverageIgnoreEnd - $this->expectedException = $exception; - } + private const EXCLUDED_METHOD_NAMES = ['__CLASS__' => \true, '__DIR__' => \true, '__FILE__' => \true, '__FUNCTION__' => \true, '__LINE__' => \true, '__METHOD__' => \true, '__NAMESPACE__' => \true, '__TRAIT__' => \true, '__clone' => \true, '__halt_compiler' => \true]; /** - * @param int|string $code + * @var array */ - 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; - } + private static $cache = []; /** - * 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. + * @var Template[] */ - 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; - } + private static $templates = []; /** - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/5062 + * Returns a mock object for the specified class. + * + * @param null|array $methods + * + * @throws ClassAlreadyExistsException + * @throws ClassIsFinalException + * @throws ClassIsReadonlyException + * @throws DuplicateMethodException + * @throws InvalidArgumentException + * @throws InvalidMethodNameException + * @throws OriginalConstructorInvocationRequiredException + * @throws ReflectionException + * @throws RuntimeException + * @throws UnknownTypeException */ - public function expectDeprecation() : void + public function getMock(string $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) : \PHPUnit\Framework\MockObject\MockObject { - $this->addWarning('Expecting E_DEPRECATED and E_USER_DEPRECATED is deprecated and will no longer be possible in PHPUnit 10.'); - $this->expectedException = Deprecated::class; + if (!is_array($methods) && null !== $methods) { + throw InvalidArgumentException::create(2, 'array'); + } + if ($type === 'Traversable' || $type === '\\Traversable') { + $type = 'Iterator'; + } + if (!$allowMockingUnknownTypes && !class_exists($type, $callAutoload) && !interface_exists($type, $callAutoload)) { + throw new \PHPUnit\Framework\MockObject\UnknownTypeException($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 \PHPUnit\Framework\MockObject\InvalidMethodNameException((string) $method); + } + } + if ($methods !== array_unique($methods)) { + throw new \PHPUnit\Framework\MockObject\DuplicateMethodException($methods); + } + } + if ($mockClassName !== '' && class_exists($mockClassName, \false)) { + try { + $reflector = new ReflectionClass($mockClassName); + // @codeCoverageIgnoreStart + } catch (\ReflectionException $e) { + throw new \PHPUnit\Framework\MockObject\ReflectionException($e->getMessage(), $e->getCode(), $e); + } + // @codeCoverageIgnoreEnd + if (!$reflector->implementsInterface(\PHPUnit\Framework\MockObject\MockObject::class)) { + throw new \PHPUnit\Framework\MockObject\ClassAlreadyExistsException($mockClassName); + } + } + if (!$callOriginalConstructor && $callOriginalMethods) { + throw new \PHPUnit\Framework\MockObject\OriginalConstructorInvocationRequiredException(); + } + $mock = $this->generate($type, $methods, $mockClassName, $callOriginalClone, $callAutoload, $cloneArguments, $callOriginalMethods); + return $this->getObject($mock, $type, $callOriginalConstructor, $callAutoload, $arguments, $callOriginalMethods, $proxyTarget, $returnValueGeneration); } /** - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/5062 + * @psalm-param list $interfaces + * + * @throws RuntimeException + * @throws UnknownTypeException */ - public function expectDeprecationMessage(string $message) : void + public function getMockForInterfaces(array $interfaces, bool $callAutoload = \true) : \PHPUnit\Framework\MockObject\MockObject { - $this->addWarning('Expecting E_DEPRECATED and E_USER_DEPRECATED is deprecated and will no longer be possible in PHPUnit 10.'); - $this->expectExceptionMessage($message); + if (count($interfaces) < 2) { + throw new \PHPUnit\Framework\MockObject\RuntimeException('At least two interfaces must be specified'); + } + foreach ($interfaces as $interface) { + if (!interface_exists($interface, $callAutoload)) { + throw new \PHPUnit\Framework\MockObject\UnknownTypeException($interface); + } + } + sort($interfaces); + $methods = []; + foreach ($interfaces as $interface) { + $methods = array_merge($methods, $this->getClassMethods($interface)); + } + if (count(array_unique($methods)) < count($methods)) { + throw new \PHPUnit\Framework\MockObject\RuntimeException('Interfaces must not declare the same method'); + } + $unqualifiedNames = []; + foreach ($interfaces as $interface) { + $parts = explode('\\', $interface); + $unqualifiedNames[] = array_pop($parts); + } + sort($unqualifiedNames); + do { + $intersectionName = sprintf('Intersection_%s_%s', implode('_', $unqualifiedNames), substr(md5((string) mt_rand()), 0, 8)); + } while (interface_exists($intersectionName, \false)); + $template = $this->getTemplate('intersection.tpl'); + $template->setVar(['intersection' => $intersectionName, 'interfaces' => implode(', ', $interfaces)]); + eval($template->render()); + return $this->getMock($intersectionName); } /** - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/5062 + * 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. + * + * @psalm-template RealInstanceType of object + * + * @psalm-param class-string $originalClassName + * + * @psalm-return MockObject&RealInstanceType + * + * @throws ClassAlreadyExistsException + * @throws ClassIsFinalException + * @throws ClassIsReadonlyException + * @throws DuplicateMethodException + * @throws InvalidArgumentException + * @throws InvalidMethodNameException + * @throws OriginalConstructorInvocationRequiredException + * @throws ReflectionException + * @throws RuntimeException + * @throws UnknownClassException + * @throws UnknownTypeException */ - public function expectDeprecationMessageMatches(string $regularExpression) : void + public function getMockForAbstractClass(string $originalClassName, array $arguments = [], string $mockClassName = '', bool $callOriginalConstructor = \true, bool $callOriginalClone = \true, bool $callAutoload = \true, ?array $mockedMethods = null, bool $cloneArguments = \true) : \PHPUnit\Framework\MockObject\MockObject { - $this->addWarning('Expecting E_DEPRECATED and E_USER_DEPRECATED is deprecated and will no longer be possible in PHPUnit 10.'); - $this->expectExceptionMessageMatches($regularExpression); + if (class_exists($originalClassName, $callAutoload) || interface_exists($originalClassName, $callAutoload)) { + try { + $reflector = new ReflectionClass($originalClassName); + // @codeCoverageIgnoreStart + } catch (\ReflectionException $e) { + throw new \PHPUnit\Framework\MockObject\ReflectionException($e->getMessage(), $e->getCode(), $e); + } + // @codeCoverageIgnoreEnd + $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 \PHPUnit\Framework\MockObject\UnknownClassException($originalClassName); } /** - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/5062 + * 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. + * + * @psalm-param trait-string $traitName + * + * @throws ClassAlreadyExistsException + * @throws ClassIsFinalException + * @throws ClassIsReadonlyException + * @throws DuplicateMethodException + * @throws InvalidArgumentException + * @throws InvalidMethodNameException + * @throws OriginalConstructorInvocationRequiredException + * @throws ReflectionException + * @throws RuntimeException + * @throws UnknownClassException + * @throws UnknownTraitException + * @throws UnknownTypeException */ - public function expectNotice() : void + public function getMockForTrait(string $traitName, array $arguments = [], string $mockClassName = '', bool $callOriginalConstructor = \true, bool $callOriginalClone = \true, bool $callAutoload = \true, ?array $mockedMethods = null, bool $cloneArguments = \true) : \PHPUnit\Framework\MockObject\MockObject { - $this->addWarning('Expecting E_STRICT, E_NOTICE, and E_USER_NOTICE is deprecated and will no longer be possible in PHPUnit 10.'); - $this->expectedException = Notice::class; + if (!trait_exists($traitName, $callAutoload)) { + throw new \PHPUnit\Framework\MockObject\UnknownTraitException($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 \PHPUnit\Framework\MockObject\MockTrait($classTemplate->render(), $className['className']); + $mockTrait->generate(); + return $this->getMockForAbstractClass($className['className'], $arguments, $mockClassName, $callOriginalConstructor, $callOriginalClone, $callAutoload, $mockedMethods, $cloneArguments); } /** - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/5062 + * Returns an object for the specified trait. + * + * @psalm-param trait-string $traitName + * + * @throws ReflectionException + * @throws RuntimeException + * @throws UnknownTraitException */ - public function expectNoticeMessage(string $message) : void + public function getObjectForTrait(string $traitName, string $traitClassName = '', bool $callAutoload = \true, bool $callOriginalConstructor = \false, array $arguments = []) : object { - $this->addWarning('Expecting E_STRICT, E_NOTICE, and E_USER_NOTICE is deprecated and will no longer be possible in PHPUnit 10.'); - $this->expectExceptionMessage($message); + if (!trait_exists($traitName, $callAutoload)) { + throw new \PHPUnit\Framework\MockObject\UnknownTraitException($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 \PHPUnit\Framework\MockObject\MockTrait($classTemplate->render(), $className['className']), '', $callOriginalConstructor, $callAutoload, $arguments); } /** - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/5062 + * @throws ClassIsFinalException + * @throws ClassIsReadonlyException + * @throws ReflectionException + * @throws RuntimeException */ - public function expectNoticeMessageMatches(string $regularExpression) : void + public function generate(string $type, ?array $methods = null, string $mockClassName = '', bool $callOriginalClone = \true, bool $callAutoload = \true, bool $cloneArguments = \true, bool $callOriginalMethods = \false) : \PHPUnit\Framework\MockObject\MockClass { - $this->addWarning('Expecting E_STRICT, E_NOTICE, and E_USER_NOTICE is deprecated and will no longer be possible in PHPUnit 10.'); - $this->expectExceptionMessageMatches($regularExpression); + if ($mockClassName !== '') { + return $this->generateMock($type, $methods, $mockClassName, $callOriginalClone, $callAutoload, $cloneArguments, $callOriginalMethods); + } + $key = md5($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]; } /** - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/5062 + * @throws RuntimeException + * @throws SoapExtensionNotAvailableException */ - public function expectWarning() : void + public function generateClassFromWsdl(string $wsdlFile, string $className, array $methods = [], array $options = []) : string { - $this->addWarning('Expecting E_WARNING and E_USER_WARNING is deprecated and will no longer be possible in PHPUnit 10.'); - $this->expectedException = WarningError::class; + if (!extension_loaded('soap')) { + throw new \PHPUnit\Framework\MockObject\SoapExtensionNotAvailableException(); + } + $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 \PHPUnit\Framework\MockObject\RuntimeException($e->getMessage(), $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) { + $parameterStart = strpos($args[$i], '$'); + if (!$parameterStart) { + continue; + } + $args[$i] = substr($args[$i], $parameterStart); + } + $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(); } /** - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/5062 + * @throws ReflectionException + * + * @return string[] */ - public function expectWarningMessage(string $message) : void + public function getClassMethods(string $className) : array { - $this->addWarning('Expecting E_WARNING and E_USER_WARNING is deprecated and will no longer be possible in PHPUnit 10.'); - $this->expectExceptionMessage($message); + try { + $class = new ReflectionClass($className); + // @codeCoverageIgnoreStart + } catch (\ReflectionException $e) { + throw new \PHPUnit\Framework\MockObject\ReflectionException($e->getMessage(), $e->getCode(), $e); + } + // @codeCoverageIgnoreEnd + $methods = []; + foreach ($class->getMethods() as $method) { + if ($method->isPublic() || $method->isAbstract()) { + $methods[] = $method->getName(); + } + } + return $methods; } /** - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/5062 + * @throws ReflectionException + * + * @return MockMethod[] */ - public function expectWarningMessageMatches(string $regularExpression) : void + public function mockClassMethods(string $className, bool $callOriginalMethods, bool $cloneArguments) : array { - $this->addWarning('Expecting E_WARNING and E_USER_WARNING is deprecated and will no longer be possible in PHPUnit 10.'); - $this->expectExceptionMessageMatches($regularExpression); + try { + $class = new ReflectionClass($className); + // @codeCoverageIgnoreStart + } catch (\ReflectionException $e) { + throw new \PHPUnit\Framework\MockObject\ReflectionException($e->getMessage(), $e->getCode(), $e); + } + // @codeCoverageIgnoreEnd + $methods = []; + foreach ($class->getMethods() as $method) { + if (($method->isPublic() || $method->isAbstract()) && $this->canMockMethod($method)) { + $methods[] = \PHPUnit\Framework\MockObject\MockMethod::fromReflection($method, $callOriginalMethods, $cloneArguments); + } + } + return $methods; } /** - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/5062 + * @throws ReflectionException + * + * @return MockMethod[] */ - public function expectError() : void + public function mockInterfaceMethods(string $interfaceName, bool $cloneArguments) : array { - $this->addWarning('Expecting E_ERROR and E_USER_ERROR is deprecated and will no longer be possible in PHPUnit 10.'); - $this->expectedException = Error::class; + try { + $class = new ReflectionClass($interfaceName); + // @codeCoverageIgnoreStart + } catch (\ReflectionException $e) { + throw new \PHPUnit\Framework\MockObject\ReflectionException($e->getMessage(), $e->getCode(), $e); + } + // @codeCoverageIgnoreEnd + $methods = []; + foreach ($class->getMethods() as $method) { + $methods[] = \PHPUnit\Framework\MockObject\MockMethod::fromReflection($method, \false, $cloneArguments); + } + return $methods; } /** - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/5062 + * @psalm-param class-string $interfaceName + * + * @throws ReflectionException + * + * @return ReflectionMethod[] */ - public function expectErrorMessage(string $message) : void + private function userDefinedInterfaceMethods(string $interfaceName) : array { - $this->addWarning('Expecting E_ERROR and E_USER_ERROR is deprecated and will no longer be possible in PHPUnit 10.'); - $this->expectExceptionMessage($message); + try { + // @codeCoverageIgnoreStart + $interface = new ReflectionClass($interfaceName); + } catch (\ReflectionException $e) { + throw new \PHPUnit\Framework\MockObject\ReflectionException($e->getMessage(), $e->getCode(), $e); + } + // @codeCoverageIgnoreEnd + $methods = []; + foreach ($interface->getMethods() as $method) { + if (!$method->isUserDefined()) { + continue; + } + $methods[] = $method; + } + return $methods; } /** - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/5062 + * @throws ReflectionException + * @throws RuntimeException */ - public function expectErrorMessageMatches(string $regularExpression) : void - { - $this->addWarning('Expecting E_ERROR and E_USER_ERROR is deprecated and will no longer be possible in PHPUnit 10.'); - $this->expectExceptionMessageMatches($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 + private function getObject(\PHPUnit\Framework\MockObject\MockType $mockClass, $type = '', bool $callOriginalConstructor = \false, bool $callAutoload = \false, array $arguments = [], bool $callOriginalMethods = \false, ?object $proxyTarget = null, bool $returnValueGeneration = \true) { - $status = $this->getStatus(); - return $status === BaseTestRunner::STATUS_FAILURE || $status === BaseTestRunner::STATUS_ERROR; + $className = $mockClass->generate(); + if ($callOriginalConstructor) { + if (count($arguments) === 0) { + $object = new $className(); + } else { + try { + $class = new ReflectionClass($className); + // @codeCoverageIgnoreStart + } catch (\ReflectionException $e) { + throw new \PHPUnit\Framework\MockObject\ReflectionException($e->getMessage(), $e->getCode(), $e); + } + // @codeCoverageIgnoreEnd + $object = $class->newInstanceArgs($arguments); + } + } else { + try { + $object = (new Instantiator())->instantiate($className); + } catch (InstantiatorException $e) { + throw new \PHPUnit\Framework\MockObject\RuntimeException($e->getMessage()); + } + } + if ($callOriginalMethods) { + if (!is_object($proxyTarget)) { + if (count($arguments) === 0) { + $proxyTarget = new $type(); + } else { + try { + $class = new ReflectionClass($type); + // @codeCoverageIgnoreStart + } catch (\ReflectionException $e) { + throw new \PHPUnit\Framework\MockObject\ReflectionException($e->getMessage(), $e->getCode(), $e); + } + // @codeCoverageIgnoreEnd + $proxyTarget = $class->newInstanceArgs($arguments); + } + } + $object->__phpunit_setOriginalObject($proxyTarget); + } + if ($object instanceof \PHPUnit\Framework\MockObject\MockObject) { + $object->__phpunit_setReturnValueGeneration($returnValueGeneration); + } + return $object; } /** - * 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 \SebastianBergmann\CodeCoverage\InvalidArgumentException - * @throws \SebastianBergmann\CodeCoverage\UnintentionallyCoveredCodeException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws CodeCoverageException - * @throws UtilException + * @throws ClassIsFinalException + * @throws ClassIsReadonlyException + * @throws ReflectionException + * @throws RuntimeException */ - public function run(\PHPUnit\Framework\TestResult $result = null) : \PHPUnit\Framework\TestResult + private function generateMock(string $type, ?array $explicitMethods, string $mockClassName, bool $callOriginalClone, bool $callAutoload, bool $cloneArguments, bool $callOriginalMethods) : \PHPUnit\Framework\MockObject\MockClass { - if ($result === null) { - $result = $this->createResult(); - } - if (!$this instanceof \PHPUnit\Framework\ErrorTestCase && !$this instanceof \PHPUnit\Framework\WarningTestCase) { - $this->setTestResultObject($result); - } - if (!$this instanceof \PHPUnit\Framework\ErrorTestCase && !$this instanceof \PHPUnit\Framework\WarningTestCase && !$this instanceof \PHPUnit\Framework\SkippedTestCase && !$this->handleDependencies()) { - return $result; + $classTemplate = $this->getTemplate('mocked_class.tpl'); + $additionalInterfaces = []; + $mockedCloneMethod = \false; + $unmockedCloneMethod = \false; + $isClass = \false; + $isInterface = \false; + $class = null; + $mockMethods = new \PHPUnit\Framework\MockObject\MockMethodSet(); + $_mockClassName = $this->generateClassName($type, $mockClassName, 'Mock_'); + if (class_exists($_mockClassName['fullClassName'], $callAutoload)) { + $isClass = \true; + } elseif (interface_exists($_mockClassName['fullClassName'], $callAutoload)) { + $isInterface = \true; } - if ($this->runInSeparateProcess()) { - $runEntireClass = $this->runClassInSeparateProcess && !$this->runTestInSeparateProcess; + 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($this); + $class = new ReflectionClass($_mockClassName['fullClassName']); // @codeCoverageIgnoreStart - } catch (ReflectionException $e) { - throw new \PHPUnit\Framework\Exception($e->getMessage(), $e->getCode(), $e); + } catch (\ReflectionException $e) { + throw new \PHPUnit\Framework\MockObject\ReflectionException($e->getMessage(), $e->getCode(), $e); } // @codeCoverageIgnoreEnd - if ($runEntireClass) { - $template = new Template(__DIR__ . '/../Util/PHP/Template/TestCaseClass.tpl'); - } else { - $template = new Template(__DIR__ . '/../Util/PHP/Template/TestCaseMethod.tpl'); + if ($class->isFinal()) { + throw new \PHPUnit\Framework\MockObject\ClassIsFinalException($_mockClassName['fullClassName']); } - 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 = ''; + if (method_exists($class, 'isReadOnly') && $class->isReadOnly()) { + throw new \PHPUnit\Framework\MockObject\ClassIsReadonlyException($_mockClassName['fullClassName']); } - $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 = '\'\''; + // @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); + // @codeCoverageIgnoreStart + } catch (\ReflectionException $e) { + throw new \PHPUnit\Framework\MockObject\ReflectionException($e->getMessage(), $e->getCode(), $e); + } + // @codeCoverageIgnoreEnd + foreach ($this->userDefinedInterfaceMethods($_mockClassName['fullClassName']) as $method) { + $methodName = $method->getName(); + if ($class->hasMethod($methodName)) { + try { + $classMethod = $class->getMethod($methodName); + // @codeCoverageIgnoreStart + } catch (\ReflectionException $e) { + throw new \PHPUnit\Framework\MockObject\ReflectionException($e->getMessage(), $e->getCode(), $e); + } + // @codeCoverageIgnoreEnd + if (!$this->canMockMethod($classMethod)) { + continue; + } + } + $mockMethods->addMethods(\PHPUnit\Framework\MockObject\MockMethod::fromReflection($method, $callOriginalMethods, $cloneArguments)); + } + $_mockClassName = $this->generateClassName($actualClassName, $_mockClassName['className'], 'Mock_'); } - if (defined('__PHPUNIT_PHAR__')) { - $phar = var_export(__PHPUNIT_PHAR__, \true); - } else { - $phar = '\'\''; + // @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)); } - $codeCoverage = $result->getCodeCoverage(); - $codeCoverageFilter = null; - $cachesStaticAnalysis = 'false'; - $codeCoverageCacheDirectory = null; - $driverMethod = 'forLineCoverage'; - if ($codeCoverage) { - $codeCoverageFilter = $codeCoverage->filter(); - if ($codeCoverage->collectsBranchAndPathCoverage()) { - $driverMethod = 'forLineAndPathCoverage'; + if ($class->hasMethod('__clone')) { + try { + $cloneMethod = $class->getMethod('__clone'); + // @codeCoverageIgnoreStart + } catch (\ReflectionException $e) { + throw new \PHPUnit\Framework\MockObject\ReflectionException($e->getMessage(), $e->getCode(), $e); } - if ($codeCoverage->cachesStaticAnalysis()) { - $cachesStaticAnalysis = 'true'; - $codeCoverageCacheDirectory = $codeCoverage->cacheDirectory(); + // @codeCoverageIgnoreEnd + if (!$cloneMethod->isFinal()) { + if ($callOriginalClone && !$isInterface) { + $unmockedCloneMethod = \true; + } else { + $mockedCloneMethod = \true; + } } + } else { + $mockedCloneMethod = \true; } - $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); - $codeCoverageCacheDirectory = var_export(serialize($codeCoverageCacheDirectory), \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 . ".'"; - $codeCoverageCacheDirectory = "'." . $codeCoverageCacheDirectory . ".'"; - $configurationFilePath = $GLOBALS['__PHPUNIT_CONFIGURATION_FILE'] ?? ''; - $processResultFile = tempnam(sys_get_temp_dir(), 'phpunit_'); - $var = ['composerAutoload' => $composerAutoload, 'phar' => $phar, 'filename' => $class->getFileName(), 'className' => $class->getName(), 'collectCodeCoverageInformation' => $coverage, 'cachesStaticAnalysis' => $cachesStaticAnalysis, 'codeCoverageCacheDirectory' => $codeCoverageCacheDirectory, 'driverMethod' => $driverMethod, '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), 'processResultFile' => $processResultFile]; - if (!$runEntireClass) { - $var['methodName'] = $this->name; + } + if ($isClass && $explicitMethods === []) { + $mockMethods->addMethods(...$this->mockClassMethods($_mockClassName['fullClassName'], $callOriginalMethods, $cloneArguments)); + } + if ($isInterface && ($explicitMethods === [] || $explicitMethods === null)) { + $mockMethods->addMethods(...$this->mockInterfaceMethods($_mockClassName['fullClassName'], $cloneArguments)); + } + if (is_array($explicitMethods)) { + foreach ($explicitMethods as $methodName) { + if ($class !== null && $class->hasMethod($methodName)) { + try { + $method = $class->getMethod($methodName); + // @codeCoverageIgnoreStart + } catch (\ReflectionException $e) { + throw new \PHPUnit\Framework\MockObject\ReflectionException($e->getMessage(), $e->getCode(), $e); + } + // @codeCoverageIgnoreEnd + if ($this->canMockMethod($method)) { + $mockMethods->addMethods(\PHPUnit\Framework\MockObject\MockMethod::fromReflection($method, $callOriginalMethods, $cloneArguments)); + } + } else { + $mockMethods->addMethods(\PHPUnit\Framework\MockObject\MockMethod::fromName($_mockClassName['fullClassName'], $methodName, $cloneArguments)); + } } - $template->setVar($var); - $php = AbstractPhpProcess::factory(); - $php->runTestJob($template->render(), $this, $result, $processResultFile); - } else { - $result->run($this); } - $this->result = null; - return $result; + $mockedMethods = ''; + $configurable = []; + foreach ($mockMethods->asArray() as $mockMethod) { + $mockedMethods .= $mockMethod->generateCode(); + $configurable[] = new \PHPUnit\Framework\MockObject\ConfigurableMethod($mockMethod->getName(), $mockMethod->getReturnType()); + } + $method = ''; + if (!$mockMethods->hasMethod('method') && (!isset($class) || !$class->hasMethod('method'))) { + $method = PHP_EOL . ' use \\PHPUnit\\Framework\\MockObject\\Method;'; + } + $cloneTrait = ''; + if ($mockedCloneMethod) { + $cloneTrait = $this->mockedCloneMethod(); + } + if ($unmockedCloneMethod) { + $cloneTrait = $this->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' => $method]); + return new \PHPUnit\Framework\MockObject\MockClass($classTemplate->render(), $_mockClassName['className'], $configurable); } - /** - * Returns a builder object to create mock objects using a fluent interface. - * - * @psalm-template RealInstanceType of object - * - * @psalm-param class-string $className - * - * @psalm-return MockBuilder - */ - public function getMockBuilder(string $className) : MockBuilder + private function generateClassName(string $type, string $className, string $prefix) : array { - $this->recordDoubledType($className); - return new MockBuilder($this, $className); + 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]; } - public function registerComparator(Comparator $comparator) : void + private function generateMockClassDeclaration(array $mockClassName, bool $isInterface, array $additionalInterfaces = []) : string { - ComparatorFactory::getInstance()->register($comparator); - $this->customComparators[] = $comparator; + $buffer = 'class '; + $additionalInterfaces[] = \PHPUnit\Framework\MockObject\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; } - /** - * @return string[] - * - * @internal This method is not covered by the backward compatibility promise for PHPUnit - */ - public function doubledTypes() : array + private function canMockMethod(ReflectionMethod $method) : bool { - return array_unique($this->doubledTypes); + return !($this->isConstructor($method) || $method->isFinal() || $method->isPrivate() || $this->isMethodNameExcluded($method->getName())); } - /** - * @internal This method is not covered by the backward compatibility promise for PHPUnit - */ - public function getGroups() : array + private function isMethodNameExcluded(string $name) : bool { - return $this->groups; + return isset(self::EXCLUDED_METHOD_NAMES[$name]); } /** - * @internal This method is not covered by the backward compatibility promise for PHPUnit + * @throws RuntimeException */ - public function setGroups(array $groups) : void + private function getTemplate(string $template) : Template { - $this->groups = $groups; + $filename = __DIR__ . DIRECTORY_SEPARATOR . 'Generator' . DIRECTORY_SEPARATOR . $template; + if (!isset(self::$templates[$filename])) { + try { + self::$templates[$filename] = new Template($filename); + } catch (TemplateException $e) { + throw new \PHPUnit\Framework\MockObject\RuntimeException($e->getMessage(), $e->getCode(), $e); + } + } + return self::$templates[$filename]; } /** - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @internal This method is not covered by the backward compatibility promise for PHPUnit + * @see https://github.com/sebastianbergmann/phpunit/issues/4139#issuecomment-605409765 */ - public function getName(bool $withDataSet = \true) : string + private function isConstructor(ReflectionMethod $method) : bool { - if ($withDataSet) { - return $this->name . $this->getDataSetAsString(\false); + $methodName = strtolower($method->getName()); + if ($methodName === '__construct') { + return \true; } - return $this->name; + if (PHP_MAJOR_VERSION >= 8) { + return \false; + } + $className = strtolower($method->getDeclaringClass()->getName()); + return $methodName === $className; } - /** - * 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 + private function mockedCloneMethod() : string { - return TestUtil::getSize(static::class, $this->getName(\false)); + if (PHP_MAJOR_VERSION >= 8) { + if (!trait_exists('\\PHPUnit\\Framework\\MockObject\\MockedCloneMethodWithVoidReturnType')) { + eval(self::MOCKED_CLONE_METHOD_WITH_VOID_RETURN_TYPE_TRAIT); + } + return PHP_EOL . ' use \\PHPUnit\\Framework\\MockObject\\MockedCloneMethodWithVoidReturnType;'; + } + if (!trait_exists('\\PHPUnit\\Framework\\MockObject\\MockedCloneMethodWithoutReturnType')) { + eval(self::MOCKED_CLONE_METHOD_WITHOUT_RETURN_TYPE_TRAIT); + } + return PHP_EOL . ' use \\PHPUnit\\Framework\\MockObject\\MockedCloneMethodWithoutReturnType;'; } - /** - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @internal This method is not covered by the backward compatibility promise for PHPUnit - */ - public function hasSize() : bool + private function unmockedCloneMethod() : string { - return $this->getSize() !== TestUtil::UNKNOWN; + if (PHP_MAJOR_VERSION >= 8) { + if (!trait_exists('\\PHPUnit\\Framework\\MockObject\\UnmockedCloneMethodWithVoidReturnType')) { + eval(self::UNMOCKED_CLONE_METHOD_WITH_VOID_RETURN_TYPE_TRAIT); + } + return PHP_EOL . ' use \\PHPUnit\\Framework\\MockObject\\UnmockedCloneMethodWithVoidReturnType;'; + } + if (!trait_exists('\\PHPUnit\\Framework\\MockObject\\UnmockedCloneMethodWithoutReturnType')) { + eval(self::UNMOCKED_CLONE_METHOD_WITHOUT_RETURN_TYPE_TRAIT); + } + return PHP_EOL . ' use \\PHPUnit\\Framework\\MockObject\\UnmockedCloneMethodWithoutReturnType;'; } - /** - * @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; +} + + @trigger_error({deprecation}, E_USER_DEPRECATED); +declare(strict_types=1); + +interface {intersection} extends {interfaces} +{ +} +declare(strict_types=1); + +{prologue}{class_declaration} +{ + use \PHPUnit\Framework\MockObject\Api;{method}{clone} +{mocked_methods}}{epilogue} + + {modifier} function {reference}{method_name}({arguments_decl}){return_declaration} + {{deprecation} + $__phpunit_arguments = [{arguments_call}]; + $__phpunit_count = func_num_args(); + + if ($__phpunit_count > {arguments_count}) { + $__phpunit_arguments_tmp = func_get_args(); + + for ($__phpunit_i = {arguments_count}; $__phpunit_i < $__phpunit_count; $__phpunit_i++) { + $__phpunit_arguments[] = $__phpunit_arguments_tmp[$__phpunit_i]; + } + } + + $__phpunit_result = $this->__phpunit_getInvocationHandler()->invoke( + new \PHPUnit\Framework\MockObject\Invocation( + '{class_name}', '{method_name}', $__phpunit_arguments, '{return_type}', $this, {clone_arguments} + ) + ); + + return $__phpunit_result; } - /** - * @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; + + {modifier} function {reference}{method_name}({arguments_decl}){return_declaration} + {{deprecation} + $__phpunit_arguments = [{arguments_call}]; + $__phpunit_count = func_num_args(); + + if ($__phpunit_count > {arguments_count}) { + $__phpunit_arguments_tmp = func_get_args(); + + for ($__phpunit_i = {arguments_count}; $__phpunit_i < $__phpunit_count; $__phpunit_i++) { + $__phpunit_arguments[] = $__phpunit_arguments_tmp[$__phpunit_i]; + } + } + + $this->__phpunit_getInvocationHandler()->invoke( + new \PHPUnit\Framework\MockObject\Invocation( + '{class_name}', '{method_name}', $__phpunit_arguments, '{return_type}', $this, {clone_arguments} + ) + ); } - /** - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @internal This method is not covered by the backward compatibility promise for PHPUnit - */ - public function isLarge() : bool + + {modifier} function {reference}{method_name}({arguments_decl}){return_declaration} { - return $this->getSize() === TestUtil::LARGE; + throw new \PHPUnit\Framework\MockObject\BadMethodCallException('Static method "{method_name}" cannot be invoked on mock object'); } - /** - * @internal This method is not covered by the backward compatibility promise for PHPUnit - */ - public function getActualOutput() : string + + {modifier} function {reference}{method_name}({arguments_decl}){return_declaration} { - if (!$this->outputBufferingActive) { - return $this->output; + $__phpunit_arguments = [{arguments_call}]; + $__phpunit_count = func_num_args(); + + if ($__phpunit_count > {arguments_count}) { + $__phpunit_arguments_tmp = func_get_args(); + + for ($__phpunit_i = {arguments_count}; $__phpunit_i < $__phpunit_count; $__phpunit_i++) { + $__phpunit_arguments[] = $__phpunit_arguments_tmp[$__phpunit_i]; + } } - return (string) ob_get_contents(); + + $this->__phpunit_getInvocationHandler()->invoke( + new \PHPUnit\Framework\MockObject\Invocation( + '{class_name}', '{method_name}', $__phpunit_arguments, '{return_type}', $this, {clone_arguments}, true + ) + ); + + return call_user_func_array(array($this->__phpunit_originalObject, "{method_name}"), $__phpunit_arguments); } - /** - * @internal This method is not covered by the backward compatibility promise for PHPUnit - */ - public function hasOutput() : bool + + {modifier} function {reference}{method_name}({arguments_decl}){return_declaration} { - if ($this->output === '') { - return \false; - } - if ($this->hasExpectationOnOutput()) { - return \false; + $__phpunit_arguments = [{arguments_call}]; + $__phpunit_count = func_num_args(); + + if ($__phpunit_count > {arguments_count}) { + $__phpunit_arguments_tmp = func_get_args(); + + for ($__phpunit_i = {arguments_count}; $__phpunit_i < $__phpunit_count; $__phpunit_i++) { + $__phpunit_arguments[] = $__phpunit_arguments_tmp[$__phpunit_i]; + } } - return \true; + + $this->__phpunit_getInvocationHandler()->invoke( + new \PHPUnit\Framework\MockObject\Invocation( + '{class_name}', '{method_name}', $__phpunit_arguments, '{return_type}', $this, {clone_arguments}, true + ) + ); + + call_user_func_array(array($this->__phpunit_originalObject, "{method_name}"), $__phpunit_arguments); } - /** - * @internal This method is not covered by the backward compatibility promise for PHPUnit - */ - public function doesNotPerformAssertions() : bool +declare(strict_types=1); + +{prologue}class {class_name} +{ + use {trait_name}; +} +declare(strict_types=1); + +{namespace}class {class_name} extends \SoapClient +{ + public function __construct($wsdl, array $options) { - return $this->doesNotPerformAssertions; + parent::__construct('{wsdl}', $options); } - /** - * @internal This method is not covered by the backward compatibility promise for PHPUnit - */ - public function hasExpectationOnOutput() : bool +{methods}} + + public function {method_name}({arguments}) { - return is_string($this->outputExpectedString) || is_string($this->outputExpectedRegex) || $this->outputRetrievedForAssertion; } + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject; + +use function array_map; +use function explode; +use function get_class; +use function implode; +use function in_array; +use function interface_exists; +use function is_object; +use function sprintf; +use function strpos; +use function strtolower; +use function substr; +use PHPUnitPHAR\Doctrine\Instantiator\Instantiator; +use PHPUnit\Framework\SelfDescribing; +use PHPUnit\Util\Cloner; +use PHPUnitPHAR\SebastianBergmann\Exporter\Exporter; +use stdClass; +use Throwable; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class Invocation implements SelfDescribing +{ /** - * @internal This method is not covered by the backward compatibility promise for PHPUnit + * @var string */ - public function getExpectedException() : ?string - { - return $this->expectedException; - } + private $className; /** - * @return null|int|string - * - * @internal This method is not covered by the backward compatibility promise for PHPUnit + * @var string */ - public function getExpectedExceptionCode() - { - return $this->expectedExceptionCode; - } + private $methodName; /** - * @internal This method is not covered by the backward compatibility promise for PHPUnit + * @var array */ - public function getExpectedExceptionMessage() : ?string - { - return $this->expectedExceptionMessage; - } + private $parameters; /** - * @internal This method is not covered by the backward compatibility promise for PHPUnit + * @var string */ - public function getExpectedExceptionMessageRegExp() : ?string - { - return $this->expectedExceptionMessageRegExp; - } + private $returnType; /** - * @internal This method is not covered by the backward compatibility promise for PHPUnit + * @var bool */ - public function setRegisterMockObjectsFromTestArgumentsRecursively(bool $flag) : void + 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->registerMockObjectsFromTestArgumentsRecursively = $flag; + $this->className = $className; + $this->methodName = $methodName; + $this->parameters = $parameters; + $this->object = $object; + $this->proxiedCall = $proxiedCall; + 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] = Cloner::clone($value); + } + } + } + public function getClassName() : string + { + return $this->className; + } + public function getMethodName() : string + { + return $this->methodName; + } + public function getParameters() : array + { + return $this->parameters; } /** - * @throws Throwable + * @throws RuntimeException * - * @internal This method is not covered by the backward compatibility promise for PHPUnit + * @return mixed Mocked return value */ - public function runBare() : void + public function generateReturnValue() { - $this->numAssertions = 0; - $this->snapshotGlobalState(); - $this->startOutputBuffering(); - clearstatcache(); - $currentWorkingDirectory = getcwd(); - $hookMethods = TestUtil::getHookMethods(static::class); - $hasMetRequirements = \false; - try { - $this->checkRequirements(); - $hasMetRequirements = \true; - if ($this->inIsolation) { - foreach ($hookMethods['beforeClass'] as $method) { - $this->{$method}(); - } + if ($this->isReturnTypeNullable || $this->proxiedCall) { + return null; + } + $intersection = \false; + $union = \false; + $unionContainsIntersections = \false; + if (strpos($this->returnType, '|') !== \false) { + $types = explode('|', $this->returnType); + $union = \true; + if (strpos($this->returnType, '(') !== \false) { + $unionContainsIntersections = \true; } - $this->setDoesNotPerformAssertionsFromAnnotation(); - foreach ($hookMethods['before'] as $method) { - $this->{$method}(); + } elseif (strpos($this->returnType, '&') !== \false) { + $types = explode('&', $this->returnType); + $intersection = \true; + } else { + $types = [$this->returnType]; + } + $types = array_map('strtolower', $types); + if (!$intersection && !$unionContainsIntersections) { + if (in_array('', $types, \true) || in_array('null', $types, \true) || in_array('mixed', $types, \true) || in_array('void', $types, \true)) { + return null; } - foreach ($hookMethods['preCondition'] as $method) { - $this->{$method}(); + if (in_array('true', $types, \true)) { + return \true; } - $this->testResult = $this->runTest(); - $this->verifyMockObjects(); - foreach ($hookMethods['postCondition'] as $method) { - $this->{$method}(); + if (in_array('false', $types, \true) || in_array('bool', $types, \true)) { + return \false; } - if (!empty($this->warnings)) { - throw new \PHPUnit\Framework\Warning(implode("\n", array_unique($this->warnings))); + if (in_array('float', $types, \true)) { + return 0.0; } - $this->status = BaseTestRunner::STATUS_PASSED; - } catch (\PHPUnit\Framework\IncompleteTest $e) { - $this->status = BaseTestRunner::STATUS_INCOMPLETE; - $this->statusMessage = $e->getMessage(); - } catch (\PHPUnit\Framework\SkippedTest $e) { - $this->status = BaseTestRunner::STATUS_SKIPPED; - $this->statusMessage = $e->getMessage(); - } catch (\PHPUnit\Framework\Warning $e) { - $this->status = BaseTestRunner::STATUS_WARNING; - $this->statusMessage = $e->getMessage(); - } catch (\PHPUnit\Framework\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 (in_array('int', $types, \true)) { + return 0; + } + if (in_array('string', $types, \true)) { + return ''; + } + if (in_array('array', $types, \true)) { + return []; + } + if (in_array('static', $types, \true)) { + try { + return (new Instantiator())->instantiate(get_class($this->object)); + } catch (Throwable $t) { + throw new \PHPUnit\Framework\MockObject\RuntimeException($t->getMessage(), (int) $t->getCode(), $t); } - if ($this->inIsolation) { - foreach ($hookMethods['afterClass'] as $method) { - $this->{$method}(); + } + if (in_array('object', $types, \true)) { + return new stdClass(); + } + if (in_array('callable', $types, \true) || in_array('closure', $types, \true)) { + return static function () : void { + }; + } + if (in_array('traversable', $types, \true) || in_array('generator', $types, \true) || in_array('iterable', $types, \true)) { + $generator = static function () : \Generator { + yield from []; + }; + return $generator(); + } + if (!$union) { + try { + return (new \PHPUnit\Framework\MockObject\Generator())->getMock($this->returnType, [], [], '', \false); + } catch (Throwable $t) { + if ($t instanceof \PHPUnit\Framework\MockObject\Exception) { + throw $t; } + throw new \PHPUnit\Framework\MockObject\RuntimeException($t->getMessage(), (int) $t->getCode(), $t); } } - } catch (Throwable $_e) { - $e = $e ?? $_e; - } - try { - $this->stopOutputBuffering(); - } catch (\PHPUnit\Framework\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)) { + if ($intersection && $this->onlyInterfaces($types)) { try { - if ($this->outputExpectedRegex !== null) { - $this->assertMatchesRegularExpression($this->outputExpectedRegex, $this->output); - } elseif ($this->outputExpectedString !== null) { - $this->assertEquals($this->outputExpectedString, $this->output); - } - } catch (Throwable $_e) { - $e = $_e; + return (new \PHPUnit\Framework\MockObject\Generator())->getMockForInterfaces($types); + } catch (Throwable $t) { + throw new \PHPUnit\Framework\MockObject\RuntimeException(sprintf('Return value for %s::%s() cannot be generated: %s', $this->className, $this->methodName, $t->getMessage()), (int) $t->getCode()); } } - // Workaround for missing "finally". - if (isset($e)) { - if ($e instanceof PredictionException) { - $e = new \PHPUnit\Framework\AssertionFailedError($e->getMessage()); - } - $this->onNotSuccessfulTest($e); + $reason = ''; + if ($union) { + $reason = ' because the declared return type is a union'; + } elseif ($intersection) { + $reason = ' because the declared return type is an intersection'; } + throw new \PHPUnit\Framework\MockObject\RuntimeException(sprintf('Return value for %s::%s() cannot be generated%s, please configure a return value for this method', $this->className, $this->methodName, $reason)); + } + 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; } /** - * @internal This method is not covered by the backward compatibility promise for PHPUnit + * @psalm-param non-empty-list $types */ - public function setName(string $name) : void + private function onlyInterfaces(array $types) : bool { - $this->name = $name; - if (is_callable($this->sortId(), \true)) { - $this->providedTests = [new \PHPUnit\Framework\ExecutionOrderDependency($this->sortId())]; + foreach ($types as $type) { + if (!interface_exists($type)) { + return \false; + } } + return \true; } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject; + +use function strtolower; +use Exception; +use PHPUnit\Framework\MockObject\Builder\InvocationMocker; +use PHPUnit\Framework\MockObject\Rule\InvocationOrder; +use Throwable; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class InvocationHandler +{ /** - * @param list $dependencies - * - * @internal This method is not covered by the backward compatibility promise for PHPUnit + * @var Matcher[] */ - public function setDependencies(array $dependencies) : void - { - $this->dependencies = $dependencies; - } + private $matchers = []; /** - * @internal This method is not covered by the backward compatibility promise for PHPUnit + * @var Matcher[] */ - public function setDependencyInput(array $dependencyInput) : void - { - $this->dependencyInput = $dependencyInput; - } + private $matcherMap = []; /** - * @internal This method is not covered by the backward compatibility promise for PHPUnit + * @var ConfigurableMethod[] */ - public function setBeStrictAboutChangesToGlobalState(?bool $beStrictAboutChangesToGlobalState) : void - { - $this->beStrictAboutChangesToGlobalState = $beStrictAboutChangesToGlobalState; - } + private $configurableMethods; /** - * @internal This method is not covered by the backward compatibility promise for PHPUnit + * @var bool */ - public function setBackupGlobals(?bool $backupGlobals) : void - { - if ($this->backupGlobals === null && $backupGlobals !== null) { - $this->backupGlobals = $backupGlobals; - } - } + private $returnValueGeneration; /** - * @internal This method is not covered by the backward compatibility promise for PHPUnit + * @var Throwable */ - public function setBackupStaticAttributes(?bool $backupStaticAttributes) : void + private $deferredError; + public function __construct(array $configurableMethods, bool $returnValueGeneration) { - if ($this->backupStaticAttributes === null && $backupStaticAttributes !== null) { - $this->backupStaticAttributes = $backupStaticAttributes; + $this->configurableMethods = $configurableMethods; + $this->returnValueGeneration = $returnValueGeneration; + } + public function hasMatchers() : bool + { + foreach ($this->matchers as $matcher) { + if ($matcher->hasMatchers()) { + return \true; + } } + return \false; } /** - * @internal This method is not covered by the backward compatibility promise for PHPUnit + * Looks up the match builder with identification $id and returns it. + * + * @param string $id The identification of the match builder */ - public function setRunTestInSeparateProcess(bool $runTestInSeparateProcess) : void + public function lookupMatcher(string $id) : ?\PHPUnit\Framework\MockObject\Matcher { - if ($this->runTestInSeparateProcess === null) { - $this->runTestInSeparateProcess = $runTestInSeparateProcess; + if (isset($this->matcherMap[$id])) { + return $this->matcherMap[$id]; } + return null; } /** - * @internal This method is not covered by the backward compatibility promise for PHPUnit + * 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 MatcherAlreadyRegisteredException */ - public function setRunClassInSeparateProcess(bool $runClassInSeparateProcess) : void + public function registerMatcher(string $id, \PHPUnit\Framework\MockObject\Matcher $matcher) : void { - if ($this->runClassInSeparateProcess === null) { - $this->runClassInSeparateProcess = $runClassInSeparateProcess; + if (isset($this->matcherMap[$id])) { + throw new \PHPUnit\Framework\MockObject\MatcherAlreadyRegisteredException($id); } + $this->matcherMap[$id] = $matcher; } - /** - * @internal This method is not covered by the backward compatibility promise for PHPUnit - */ - public function setPreserveGlobalState(bool $preserveGlobalState) : void + public function expects(InvocationOrder $rule) : InvocationMocker { - $this->preserveGlobalState = $preserveGlobalState; + $matcher = new \PHPUnit\Framework\MockObject\Matcher($rule); + $this->addMatcher($matcher); + return new InvocationMocker($this, $matcher, ...$this->configurableMethods); } /** - * @internal This method is not covered by the backward compatibility promise for PHPUnit + * @throws Exception + * @throws RuntimeException */ - public function setInIsolation(bool $inIsolation) : void + public function invoke(\PHPUnit\Framework\MockObject\Invocation $invocation) { - $this->inIsolation = $inIsolation; + $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 \PHPUnit\Framework\MockObject\ReturnValueNotConfiguredException($invocation); + if (strtolower($invocation->getMethodName()) === '__tostring') { + $this->deferredError = $exception; + return ''; + } + throw $exception; + } + return $invocation->generateReturnValue(); } - /** - * @internal This method is not covered by the backward compatibility promise for PHPUnit - */ - public function isInIsolation() : bool + public function matches(\PHPUnit\Framework\MockObject\Invocation $invocation) : bool { - return $this->inIsolation; + foreach ($this->matchers as $matcher) { + if (!$matcher->matches($invocation)) { + return \false; + } + } + return \true; } /** - * @internal This method is not covered by the backward compatibility promise for PHPUnit + * @throws Throwable */ - public function getResult() + public function verify() : void { - return $this->testResult; + foreach ($this->matchers as $matcher) { + $matcher->verify(); + } + if ($this->deferredError) { + throw $this->deferredError; + } } - /** - * @internal This method is not covered by the backward compatibility promise for PHPUnit - */ - public function setResult($result) : void + private function addMatcher(\PHPUnit\Framework\MockObject\Matcher $matcher) : void { - $this->testResult = $result; + $this->matchers[] = $matcher; } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject; + +use function assert; +use function implode; +use function sprintf; +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\InvokedAtMostCount; +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; +use PHPUnitPHAR\SebastianBergmann\RecursionContext\InvalidArgumentException; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class Matcher +{ /** - * @internal This method is not covered by the backward compatibility promise for PHPUnit + * @var InvocationOrder */ - public function setOutputCallback(callable $callback) : void - { - $this->outputCallback = $callback; - } + private $invocationRule; /** - * @internal This method is not covered by the backward compatibility promise for PHPUnit + * @var mixed */ - public function getTestResultObject() : ?\PHPUnit\Framework\TestResult - { - return $this->result; - } + private $afterMatchBuilderId; /** - * @internal This method is not covered by the backward compatibility promise for PHPUnit + * @var bool */ - public function setTestResultObject(\PHPUnit\Framework\TestResult $result) : void - { - $this->result = $result; - } + private $afterMatchBuilderIsInvoked = \false; /** - * @internal This method is not covered by the backward compatibility promise for PHPUnit + * @var MethodName */ - public function registerMockObject(MockObject $mockObject) : void - { - $this->mockObjects[] = $mockObject; - } + private $methodNameRule; /** - * @internal This method is not covered by the backward compatibility promise for PHPUnit + * @var ParametersRule */ - public function addToAssertionCount(int $count) : void - { - $this->numAssertions += $count; - } + private $parametersRule; /** - * Returns the number of assertions performed by this test. - * - * @internal This method is not covered by the backward compatibility promise for PHPUnit + * @var Stub */ - public function getNumAssertions() : int + private $stub; + public function __construct(InvocationOrder $rule) { - return $this->numAssertions; + $this->invocationRule = $rule; } - /** - * @internal This method is not covered by the backward compatibility promise for PHPUnit - */ - public function usesDataProvider() : bool + public function hasMatchers() : bool { - return !empty($this->data); + return !$this->invocationRule instanceof AnyInvokedCount; } - /** - * @return int|string - * - * @internal This method is not covered by the backward compatibility promise for PHPUnit - */ - public function dataName() + public function hasMethodNameRule() : bool { - return $this->dataName; + return $this->methodNameRule !== null; } - /** - * @internal This method is not covered by the backward compatibility promise for PHPUnit - */ - public function getDataSetAsString(bool $includeData = \true) : string + public function getMethodNameRule() : MethodName { - $buffer = ''; - if (!empty($this->data)) { - if (is_int($this->dataName)) { - $buffer .= sprintf(' with data set #%d', $this->dataName); - } else { - $buffer .= sprintf(' with data set "%s"', $this->dataName); - } - if ($includeData) { - $exporter = new Exporter(); - $buffer .= sprintf(' (%s)', $exporter->shortenedRecursiveExport($this->data)); - } - } - return $buffer; + return $this->methodNameRule; } - /** - * Gets the data set of a TestCase. - * - * @internal This method is not covered by the backward compatibility promise for PHPUnit - */ - public function getProvidedData() : array + public function setMethodNameRule(MethodName $rule) : void { - return $this->data; + $this->methodNameRule = $rule; } - /** - * @internal This method is not covered by the backward compatibility promise for PHPUnit - */ - public function addWarning(string $warning) : void + public function hasParametersRule() : bool { - $this->warnings[] = $warning; + return $this->parametersRule !== null; } - public function sortId() : string + public function setParametersRule(ParametersRule $rule) : void { - $id = $this->name; - if (strpos($id, '::') === \false) { - $id = static::class . '::' . $id; - } - if ($this->usesDataProvider()) { - $id .= $this->getDataSetAsString(\false); - } - return $id; + $this->parametersRule = $rule; } - /** - * Returns the normalized test name as class::method. - * - * @return list - */ - public function provides() : array + public function setStub(Stub $stub) : void { - return $this->providedTests; + $this->stub = $stub; } - /** - * Returns a list of normalized dependency names, class::method. - * - * This list can differ from the raw dependencies as the resolver has - * no need for the [!][shallow]clone prefix that is filtered out - * during normalization. - * - * @return list - */ - public function requires() : array + public function setAfterMatchBuilderId(string $id) : void { - return $this->dependencies; + $this->afterMatchBuilderId = $id; } /** - * Override to run the test and assert its state. - * - * @throws \SebastianBergmann\ObjectEnumerator\InvalidArgumentException - * @throws AssertionFailedError - * @throws Exception * @throws ExpectationFailedException - * @throws Throwable + * @throws MatchBuilderNotFoundException + * @throws MethodNameNotConfiguredException + * @throws RuntimeException */ - protected function runTest() + public function invoked(\PHPUnit\Framework\MockObject\Invocation $invocation) { - if (trim($this->name) === '') { - throw new \PHPUnit\Framework\Exception('PHPUnit\\Framework\\TestCase::$name must be a non-blank string.'); + if ($this->methodNameRule === null) { + throw new \PHPUnit\Framework\MockObject\MethodNameNotConfiguredException(); } - $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) { - if ($this->expectedException === Error::class) { - $this->assertThat($exception, LogicalOr::fromConstraints(new ExceptionConstraint(Error::class), new ExceptionConstraint(\Error::class))); - } else { - $this->assertThat($exception, new ExceptionConstraint($this->expectedException)); - } - } - if ($this->expectedExceptionMessage !== null) { - $this->assertThat($exception, new ExceptionMessage($this->expectedExceptionMessage)); + if ($this->afterMatchBuilderId !== null) { + $matcher = $invocation->getObject()->__phpunit_getInvocationHandler()->lookupMatcher($this->afterMatchBuilderId); + if (!$matcher) { + throw new \PHPUnit\Framework\MockObject\MatchBuilderNotFoundException($this->afterMatchBuilderId); } - if ($this->expectedExceptionMessageRegExp !== null) { - $this->assertThat($exception, new ExceptionMessageRegularExpression($this->expectedExceptionMessageRegExp)); + assert($matcher instanceof self); + if ($matcher->invocationRule->hasBeenInvoked()) { + $this->afterMatchBuilderIsInvoked = \true; } - if ($this->expectedExceptionCode !== null) { - $this->assertThat($exception, new ExceptionCode($this->expectedExceptionCode)); + } + $this->invocationRule->invoked($invocation); + try { + if ($this->parametersRule !== null) { + $this->parametersRule->apply($invocation); } - return; + } 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->expectedException !== null) { - $this->assertThat(null, new ExceptionConstraint($this->expectedException)); - } elseif ($this->expectedExceptionMessage !== null) { - $this->numAssertions++; - throw new \PHPUnit\Framework\AssertionFailedError(sprintf('Failed asserting that exception with message "%s" is thrown', $this->expectedExceptionMessage)); - } elseif ($this->expectedExceptionMessageRegExp !== null) { - $this->numAssertions++; - throw new \PHPUnit\Framework\AssertionFailedError(sprintf('Failed asserting that exception with message matching "%s" is thrown', $this->expectedExceptionMessageRegExp)); - } elseif ($this->expectedExceptionCode !== null) { - $this->numAssertions++; - throw new \PHPUnit\Framework\AssertionFailedError(sprintf('Failed asserting that exception with code "%s" is thrown', $this->expectedExceptionCode)); + if ($this->stub) { + return $this->stub->invoke($invocation); } - return $testResult; + return $invocation->generateReturnValue(); } /** - * 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 + * @throws ExpectationFailedException + * @throws InvalidArgumentException + * @throws MatchBuilderNotFoundException + * @throws MethodNameNotConfiguredException + * @throws RuntimeException */ - protected function iniSet(string $varName, string $newValue) : void + public function matches(\PHPUnit\Framework\MockObject\Invocation $invocation) : bool { - $currentValue = ini_set($varName, $newValue); - if ($currentValue !== \false) { - $this->iniSettings[$varName] = $currentValue; - } else { - throw new \PHPUnit\Framework\Exception(sprintf('INI setting "%s" could not be set to "%s".', $varName, $newValue)); + if ($this->afterMatchBuilderId !== null) { + $matcher = $invocation->getObject()->__phpunit_getInvocationHandler()->lookupMatcher($this->afterMatchBuilderId); + if (!$matcher) { + throw new \PHPUnit\Framework\MockObject\MatchBuilderNotFoundException($this->afterMatchBuilderId); + } + assert($matcher instanceof self); + if (!$matcher->invocationRule->hasBeenInvoked()) { + return \false; + } + } + if ($this->methodNameRule === null) { + throw new \PHPUnit\Framework\MockObject\MethodNameNotConfiguredException(); + } + 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; } /** - * 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 + * @throws ExpectationFailedException + * @throws InvalidArgumentException + * @throws MethodNameNotConfiguredException */ - protected function setLocale(...$args) : void + public function verify() : void { - if (count($args) < 2) { - throw new \PHPUnit\Framework\Exception(); + if ($this->methodNameRule === null) { + throw new \PHPUnit\Framework\MockObject\MethodNameNotConfiguredException(); } - [$category, $locale] = $args; - if (!in_array($category, self::LOCALE_CATEGORIES, \true)) { - throw new \PHPUnit\Framework\Exception(); + 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(); + $invocationIsAtMost = $this->invocationRule instanceof InvokedAtMostCount; + if (!$invocationIsAny && !$invocationIsNever && !$invocationIsAtMost) { + $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))); } - if (!is_array($locale) && !is_string($locale)) { - throw new \PHPUnit\Framework\Exception(); + } + public function toString() : string + { + $list = []; + if ($this->invocationRule !== null) { + $list[] = $this->invocationRule->toString(); } - $this->locale[$category] = setlocale($category, 0); - $result = setlocale(...$args); - if ($result === \false) { - throw new \PHPUnit\Framework\Exception('The locale functionality is not implemented on your platform, ' . 'the specified locale does not exist or the category name is ' . 'invalid.'); + 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); } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject; + +use function is_string; +use function sprintf; +use function strtolower; +use PHPUnit\Framework\Constraint\Constraint; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class MethodNameConstraint extends Constraint +{ /** - * Makes configurable stub for the specified class. + * @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); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject; + +use function array_diff; +use function array_merge; +use PHPUnit\Framework\Exception; +use PHPUnit\Framework\InvalidArgumentException; +use PHPUnit\Framework\TestCase; +use ReflectionClass; +/** + * @psalm-template MockedType + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class MockBuilder +{ + /** + * @var TestCase + */ + private $testCase; + /** + * @var string + */ + private $type; + /** + * @var null|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; + /** + * @param string|string[] $type * - * @psalm-template RealInstanceType of object + * @psalm-param class-string|string|string[] $type + */ + public function __construct(TestCase $testCase, $type) + { + $this->testCase = $testCase; + $this->type = $type; + $this->generator = new \PHPUnit\Framework\MockObject\Generator(); + } + /** + * Creates a mock object using a fluent interface. * - * @psalm-param class-string $originalClassName + * @throws ClassAlreadyExistsException + * @throws ClassIsFinalException + * @throws ClassIsReadonlyException + * @throws DuplicateMethodException + * @throws InvalidArgumentException + * @throws InvalidMethodNameException + * @throws OriginalConstructorInvocationRequiredException + * @throws ReflectionException + * @throws RuntimeException + * @throws UnknownTypeException * - * @psalm-return Stub&RealInstanceType + * @psalm-return MockObject&MockedType */ - protected function createStub(string $originalClassName) : Stub + public function getMock() : \PHPUnit\Framework\MockObject\MockObject { - return $this->createMockObject($originalClassName); + $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; } /** - * Returns a mock object for the specified class. - * - * @psalm-template RealInstanceType of object + * Creates a mock object for an abstract class using a fluent interface. * - * @psalm-param class-string $originalClassName + * @psalm-return MockObject&MockedType * - * @psalm-return MockObject&RealInstanceType + * @throws Exception + * @throws ReflectionException + * @throws RuntimeException */ - protected function createMock(string $originalClassName) : MockObject + public function getMockForAbstractClass() : \PHPUnit\Framework\MockObject\MockObject { - return $this->createMockObject($originalClassName); + $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; } /** - * Returns a configured mock object for the specified class. + * Creates a mock object for a trait using a fluent interface. * - * @psalm-template RealInstanceType of object + * @psalm-return MockObject&MockedType * - * @psalm-param class-string $originalClassName + * @throws Exception + * @throws ReflectionException + * @throws RuntimeException + */ + public function getMockForTrait() : \PHPUnit\Framework\MockObject\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. * - * @psalm-return MockObject&RealInstanceType + * @deprecated https://github.com/sebastianbergmann/phpunit/pull/3687 + * + * @return $this */ - protected function createConfiguredMock(string $originalClassName, array $configuration) : MockObject + public function setMethods(?array $methods = null) : self { - $o = $this->createMockObject($originalClassName); - foreach ($configuration as $method => $return) { - $o->method($method)->willReturn($return); + if ($methods === null) { + $this->methods = $methods; + } else { + $this->methods = array_merge($this->methods ?? [], $methods); } - return $o; + return $this; } /** - * Returns a partial mock object for the specified class. + * Specifies the subset of methods to mock, requiring each to exist in the class. * * @param string[] $methods * - * @psalm-template RealInstanceType of object - * - * @psalm-param class-string $originalClassName + * @throws CannotUseOnlyMethodsException + * @throws ReflectionException * - * @psalm-return MockObject&RealInstanceType + * @return $this */ - protected function createPartialMock(string $originalClassName, array $methods) : MockObject + public function onlyMethods(array $methods) : self { + if (empty($methods)) { + $this->emptyMethodsArray = \true; + return $this; + } try { - $reflector = new ReflectionClass($originalClassName); + $reflector = new ReflectionClass($this->type); // @codeCoverageIgnoreStart - } catch (ReflectionException $e) { - throw new \PHPUnit\Framework\Exception($e->getMessage(), $e->getCode(), $e); + } catch (\ReflectionException $e) { + throw new \PHPUnit\Framework\MockObject\ReflectionException($e->getMessage(), $e->getCode(), $e); } // @codeCoverageIgnoreEnd - $mockedMethodsThatDontExist = array_filter($methods, static function (string $method) use($reflector) { - return !$reflector->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), $originalClassName)); + foreach ($methods as $method) { + if (!$reflector->hasMethod($method)) { + throw new \PHPUnit\Framework\MockObject\CannotUseOnlyMethodsException($this->type, $method); + } } - return $this->getMockBuilder($originalClassName)->disableOriginalConstructor()->disableOriginalClone()->disableArgumentCloning()->disallowMockingUnknownTypes()->setMethods(empty($methods) ? null : $methods)->getMock(); + $this->methods = array_merge($this->methods ?? [], $methods); + return $this; } /** - * Returns a test proxy for the specified class. + * Specifies methods that don't exist in the class which you want to mock. * - * @psalm-template RealInstanceType of object + * @param string[] $methods * - * @psalm-param class-string $originalClassName + * @throws CannotUseAddMethodsException + * @throws ReflectionException + * @throws RuntimeException * - * @psalm-return MockObject&RealInstanceType + * @return $this */ - protected function createTestProxy(string $originalClassName, array $constructorArguments = []) : MockObject + public function addMethods(array $methods) : self { - return $this->getMockBuilder($originalClassName)->setConstructorArgs($constructorArguments)->enableProxyingToOriginalMethods()->getMock(); + if (empty($methods)) { + $this->emptyMethodsArray = \true; + return $this; + } + try { + $reflector = new ReflectionClass($this->type); + // @codeCoverageIgnoreStart + } catch (\ReflectionException $e) { + throw new \PHPUnit\Framework\MockObject\ReflectionException($e->getMessage(), $e->getCode(), $e); + } + // @codeCoverageIgnoreEnd + foreach ($methods as $method) { + if ($reflector->hasMethod($method)) { + throw new \PHPUnit\Framework\MockObject\CannotUseAddMethodsException($this->type, $method); + } + } + $this->methods = array_merge($this->methods ?? [], $methods); + return $this; } /** - * Mocks the specified class and returns the name of the mocked class. - * - * @param null|array $methods $methods - * - * @psalm-template RealInstanceType of object - * - * @psalm-param class-string|string $originalClassName + * Specifies the subset of methods to not mock. Default is to mock all of them. * - * @psalm-return class-string + * @deprecated https://github.com/sebastianbergmann/phpunit/pull/3687 * - * @deprecated + * @throws ReflectionException */ - protected function getMockClass(string $originalClassName, $methods = [], array $arguments = [], string $mockClassName = '', bool $callOriginalConstructor = \false, bool $callOriginalClone = \true, bool $callAutoload = \true, bool $cloneArguments = \false) : string + public function setMethodsExcept(array $methods = []) : self { - $this->addWarning('PHPUnit\\Framework\\TestCase::getMockClass() is deprecated and will be removed in PHPUnit 10.'); - $this->recordDoubledType($originalClassName); - $mock = $this->getMockObjectGenerator()->getMock($originalClassName, $methods, $arguments, $mockClassName, $callOriginalConstructor, $callOriginalClone, $callAutoload, $cloneArguments); - return get_class($mock); + return $this->setMethods(array_diff($this->generator->getClassMethods($this->type), $methods)); } /** - * 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). - * - * @psalm-template RealInstanceType of object - * - * @psalm-param class-string $originalClassName + * Specifies the arguments for the constructor. * - * @psalm-return MockObject&RealInstanceType + * @return $this */ - protected function getMockForAbstractClass(string $originalClassName, array $arguments = [], string $mockClassName = '', bool $callOriginalConstructor = \true, bool $callOriginalClone = \true, bool $callAutoload = \true, array $mockedMethods = [], bool $cloneArguments = \false) : MockObject + public function setConstructorArgs(array $args) : self { - $this->recordDoubledType($originalClassName); - $mockObject = $this->getMockObjectGenerator()->getMockForAbstractClass($originalClassName, $arguments, $mockClassName, $callOriginalConstructor, $callOriginalClone, $callAutoload, $mockedMethods, $cloneArguments); - $this->registerMockObject($mockObject); - return $mockObject; + $this->constructorArgs = $args; + return $this; } /** - * Returns a mock object based on the given WSDL file. - * - * @psalm-template RealInstanceType of object - * - * @psalm-param class-string|string $originalClassName + * Specifies the name for the mock class. * - * @psalm-return MockObject&RealInstanceType + * @return $this */ - protected function getMockFromWsdl(string $wsdlFile, string $originalClassName = '', string $mockClassName = '', array $methods = [], bool $callOriginalConstructor = \true, array $options = []) : MockObject + public function setMockClassName(string $name) : self { - $this->recordDoubledType(SoapClient::class); - if ($originalClassName === '') { - $fileName = pathinfo(basename(parse_url($wsdlFile, PHP_URL_PATH)), PATHINFO_FILENAME); - $originalClassName = preg_replace('/\\W/', '', $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; + $this->mockClassName = $name; + return $this; } /** - * 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. + * Disables the invocation of the original constructor. * - * @psalm-param trait-string $traitName + * @return $this */ - protected function getMockForTrait(string $traitName, array $arguments = [], string $mockClassName = '', bool $callOriginalConstructor = \true, bool $callOriginalClone = \true, bool $callAutoload = \true, array $mockedMethods = [], bool $cloneArguments = \false) : MockObject + public function disableOriginalConstructor() : self { - $this->recordDoubledType($traitName); - $mockObject = $this->getMockObjectGenerator()->getMockForTrait($traitName, $arguments, $mockClassName, $callOriginalConstructor, $callOriginalClone, $callAutoload, $mockedMethods, $cloneArguments); - $this->registerMockObject($mockObject); - return $mockObject; + $this->originalConstructor = \false; + return $this; } /** - * Returns an object for the specified trait. + * Enables the invocation of the original constructor. * - * @psalm-param trait-string $traitName + * @return $this */ - protected function getObjectForTrait(string $traitName, array $arguments = [], string $traitClassName = '', bool $callOriginalConstructor = \true, bool $callOriginalClone = \true, bool $callAutoload = \true) : object + public function enableOriginalConstructor() : self { - $this->recordDoubledType($traitName); - return $this->getMockObjectGenerator()->getObjectForTrait($traitName, $traitClassName, $callAutoload, $callOriginalConstructor, $arguments); + $this->originalConstructor = \true; + return $this; } /** - * @throws \Prophecy\Exception\Doubler\ClassNotFoundException - * @throws \Prophecy\Exception\Doubler\DoubleException - * @throws \Prophecy\Exception\Doubler\InterfaceNotFoundException - * - * @psalm-param class-string|null $classOrInterface + * Disables the invocation of the original clone constructor. * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4141 + * @return $this */ - protected function prophesize(?string $classOrInterface = null) : ObjectProphecy + public function disableOriginalClone() : self { - if (!class_exists(Prophet::class)) { - throw new \PHPUnit\Framework\Exception('This test uses TestCase::prophesize(), but phpspec/prophecy is not installed. Please run "composer require --dev phpspec/prophecy".'); - } - $this->addWarning('PHPUnit\\Framework\\TestCase::prophesize() is deprecated and will be removed in PHPUnit 10. Please use the trait provided by phpspec/prophecy-phpunit.'); - if (is_string($classOrInterface)) { - $this->recordDoubledType($classOrInterface); - } - return $this->getProphet()->prophesize($classOrInterface); + $this->originalClone = \false; + return $this; } /** - * Creates a default TestResult object. + * Enables the invocation of the original clone constructor. * - * @internal This method is not covered by the backward compatibility promise for PHPUnit + * @return $this */ - protected function createResult() : \PHPUnit\Framework\TestResult + public function enableOriginalClone() : self { - return new \PHPUnit\Framework\TestResult(); + $this->originalClone = \true; + return $this; } /** - * This method is called when a test method did not execute successfully. + * Disables the use of class autoloading while creating the mock object. * - * @throws Throwable - */ - protected function onNotSuccessfulTest(Throwable $t) : void - { - throw $t; - } - protected function recordDoubledType(string $originalClassName) : void - { - $this->doubledTypes[] = $originalClassName; - } - /** - * @throws Throwable + * @return $this */ - private function verifyMockObjects() : void + public function disableAutoload() : self { - 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) { - /* @var MethodProphecy $methodProphecy */ - $this->numAssertions += count($methodProphecy->getCheckedPredictions()); - } - } - } - } - } + $this->autoload = \false; + return $this; } /** - * @throws SkippedTestError - * @throws SyntheticSkippedError - * @throws Warning + * Enables the use of class autoloading while creating the mock object. + * + * @return $this */ - private function checkRequirements() : void - { - if (!$this->name || !method_exists($this, $this->name)) { - return; - } - $missingRequirements = TestUtil::getMissingRequirements(static::class, $this->name); - if (!empty($missingRequirements)) { - $this->markTestSkipped(implode(PHP_EOL, $missingRequirements)); - } - } - private function handleDependencies() : bool - { - if ([] === $this->dependencies || $this->inIsolation) { - return \true; - } - $passed = $this->result->passed(); - $passedKeys = array_keys($passed); - $numKeys = count($passedKeys); - for ($i = 0; $i < $numKeys; $i++) { - $pos = strpos($passedKeys[$i], ' with data set'); - if ($pos !== \false) { - $passedKeys[$i] = substr($passedKeys[$i], 0, $pos); - } - } - $passedKeys = array_flip(array_unique($passedKeys)); - foreach ($this->dependencies as $dependency) { - if (!$dependency->isValid()) { - $this->markSkippedForNotSpecifyingDependency(); - return \false; - } - if ($dependency->targetIsClass()) { - $dependencyClassName = $dependency->getTargetClassName(); - if (array_search($dependencyClassName, $this->result->passedClasses(), \true) === \false) { - $this->markSkippedForMissingDependency($dependency); - return \false; - } - continue; - } - $dependencyTarget = $dependency->getTarget(); - if (!isset($passedKeys[$dependencyTarget])) { - if (!$this->isCallableTestMethod($dependencyTarget)) { - $this->markWarningForUncallableDependency($dependency); - } else { - $this->markSkippedForMissingDependency($dependency); - } - return \false; - } - if (isset($passed[$dependencyTarget])) { - if ($passed[$dependencyTarget]['size'] != \PHPUnit\Util\Test::UNKNOWN && $this->getSize() != \PHPUnit\Util\Test::UNKNOWN && $passed[$dependencyTarget]['size'] > $this->getSize()) { - $this->result->addError($this, new \PHPUnit\Framework\SkippedTestError('This test depends on a test that is larger than itself.'), 0); - return \false; - } - if ($dependency->useDeepClone()) { - $deepCopy = new DeepCopy(); - $deepCopy->skipUncloneable(\false); - $this->dependencyInput[$dependencyTarget] = $deepCopy->copy($passed[$dependencyTarget]['result']); - } elseif ($dependency->useShallowClone()) { - $this->dependencyInput[$dependencyTarget] = clone $passed[$dependencyTarget]['result']; - } else { - $this->dependencyInput[$dependencyTarget] = $passed[$dependencyTarget]['result']; - } - } else { - $this->dependencyInput[$dependencyTarget] = null; - } - } - return \true; - } - private function markSkippedForNotSpecifyingDependency() : void - { - $this->status = BaseTestRunner::STATUS_SKIPPED; - $this->result->startTest($this); - $this->result->addError($this, new \PHPUnit\Framework\SkippedTestError('This method has an invalid @depends annotation.'), 0); - $this->result->endTest($this, 0); - } - private function markSkippedForMissingDependency(\PHPUnit\Framework\ExecutionOrderDependency $dependency) : void - { - $this->status = BaseTestRunner::STATUS_SKIPPED; - $this->result->startTest($this); - $this->result->addError($this, new \PHPUnit\Framework\SkippedTestError(sprintf('This test depends on "%s" to pass.', $dependency->getTarget())), 0); - $this->result->endTest($this, 0); - } - private function markWarningForUncallableDependency(\PHPUnit\Framework\ExecutionOrderDependency $dependency) : void + public function enableAutoload() : self { - $this->status = BaseTestRunner::STATUS_WARNING; - $this->result->startTest($this); - $this->result->addWarning($this, new \PHPUnit\Framework\Warning(sprintf('This test depends on "%s" which does not exist.', $dependency->getTarget())), 0); - $this->result->endTest($this, 0); + $this->autoload = \true; + return $this; } /** - * Get the mock object generator, creating it if it doesn't exist. + * Disables the cloning of arguments passed to mocked methods. + * + * @return $this */ - private function getMockObjectGenerator() : MockGenerator - { - if ($this->mockObjectGenerator === null) { - $this->mockObjectGenerator = new MockGenerator(); - } - return $this->mockObjectGenerator; - } - private function startOutputBuffering() : void + public function disableArgumentCloning() : self { - ob_start(); - $this->outputBufferingActive = \true; - $this->outputBufferingLevel = ob_get_level(); + $this->cloneArguments = \false; + return $this; } /** - * @throws RiskyTestError + * Enables the cloning of arguments passed to mocked methods. + * + * @return $this */ - private function stopOutputBuffering() : void - { - if (ob_get_level() !== $this->outputBufferingLevel) { - while (ob_get_level() >= $this->outputBufferingLevel) { - ob_end_clean(); - } - throw new \PHPUnit\Framework\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 + public function enableArgumentCloning() : self { - if ($this->runTestInSeparateProcess || $this->inIsolation || !$this->backupGlobals && !$this->backupStaticAttributes) { - return; - } - $this->snapshot = $this->createGlobalStateSnapshot($this->backupGlobals === \true); + $this->cloneArguments = \true; + return $this; } /** - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws RiskyTestError + * Enables the invocation of the original methods. + * + * @return $this */ - private function restoreGlobalState() : void - { - if (!$this->snapshot instanceof Snapshot) { - return; - } - if ($this->beStrictAboutChangesToGlobalState) { - try { - $this->compareGlobalStateSnapshots($this->snapshot, $this->createGlobalStateSnapshot($this->backupGlobals === \true)); - } catch (\PHPUnit\Framework\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 + public function enableProxyingToOriginalMethods() : self { - $excludeList = new ExcludeList(); - foreach ($this->backupGlobalsExcludeList as $globalVariable) { - $excludeList->addGlobalVariable($globalVariable); - } - if (!empty($this->backupGlobalsBlacklist)) { - $this->addWarning('PHPUnit\\Framework\\TestCase::$backupGlobalsBlacklist is deprecated and will be removed in PHPUnit 10. Please use PHPUnit\\Framework\\TestCase::$backupGlobalsExcludeList instead.'); - foreach ($this->backupGlobalsBlacklist as $globalVariable) { - $excludeList->addGlobalVariable($globalVariable); - } - } - if (!defined('PHPUNIT_TESTSUITE')) { - $excludeList->addClassNamePrefix('PHPUnit'); - $excludeList->addClassNamePrefix('PHPUnit\\SebastianBergmann\\CodeCoverage'); - $excludeList->addClassNamePrefix('PHPUnit\\SebastianBergmann\\FileIterator'); - $excludeList->addClassNamePrefix('PHPUnit\\SebastianBergmann\\Invoker'); - $excludeList->addClassNamePrefix('PHPUnit\\SebastianBergmann\\Template'); - $excludeList->addClassNamePrefix('PHPUnit\\SebastianBergmann\\Timer'); - $excludeList->addClassNamePrefix('PHPUnit\\Doctrine\\Instantiator'); - $excludeList->addClassNamePrefix('Prophecy'); - $excludeList->addStaticAttribute(ComparatorFactory::class, 'instance'); - foreach ($this->backupStaticAttributesExcludeList as $class => $attributes) { - foreach ($attributes as $attribute) { - $excludeList->addStaticAttribute($class, $attribute); - } - } - if (!empty($this->backupStaticAttributesBlacklist)) { - $this->addWarning('PHPUnit\\Framework\\TestCase::$backupStaticAttributesBlacklist is deprecated and will be removed in PHPUnit 10. Please use PHPUnit\\Framework\\TestCase::$backupStaticAttributesExcludeList instead.'); - foreach ($this->backupStaticAttributesBlacklist as $class => $attributes) { - foreach ($attributes as $attribute) { - $excludeList->addStaticAttribute($class, $attribute); - } - } - } - } - return new Snapshot($excludeList, $backupGlobals, (bool) $this->backupStaticAttributes, \false, \false, \false, \false, \false, \false, \false); + $this->callOriginalMethods = \true; + return $this; } /** - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws RiskyTestError + * Disables the invocation of the original methods. + * + * @return $this */ - private function compareGlobalStateSnapshots(Snapshot $before, Snapshot $after) : void + public function disableProxyingToOriginalMethods() : self { - $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"); - } + $this->callOriginalMethods = \false; + $this->proxyTarget = null; + return $this; } /** - * @throws RiskyTestError + * Sets the proxy target. + * + * @return $this */ - 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 \PHPUnit\Framework\RiskyTestError($diff); - } - } - private function getProphet() : Prophet + public function setProxyTarget(object $object) : self { - if ($this->prophet === null) { - $this->prophet = new Prophet(); - } - return $this->prophet; + $this->proxyTarget = $object; + return $this; } /** - * @throws \SebastianBergmann\ObjectEnumerator\InvalidArgumentException + * @return $this */ - private function shouldInvocationMockerBeReset(MockObject $mock) : bool + public function allowMockingUnknownTypes() : self { - $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); + $this->allowMockingUnknownTypes = \true; + return $this; } /** - * @throws \SebastianBergmann\ObjectEnumerator\InvalidArgumentException - * @throws \SebastianBergmann\ObjectReflector\InvalidArgumentException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @return $this */ - 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) { - $testArgument = Cloner::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 = TestUtil::parseTestMethodAnnotations(static::class, $this->name); - 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 + public function disallowMockingUnknownTypes() : self { - foreach ($this->locale as $category => $locale) { - setlocale($category, $locale); - } - $this->locale = []; + $this->allowMockingUnknownTypes = \false; + return $this; } /** - * @throws Exception + * @return $this */ - 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 \PHPUnit\Framework\Exception) { - $result = \false; - } - if (is_string($this->expectedException)) { - try { - $reflector = new ReflectionClass($this->expectedException); - // @codeCoverageIgnoreStart - } catch (ReflectionException $e) { - throw new \PHPUnit\Framework\Exception($e->getMessage(), $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd - if ($this->expectedException === 'PHPUnit\\Framework\\Exception' || $this->expectedException === '\\PHPUnit\\Framework\\Exception' || $reflector->isSubclassOf(\PHPUnit\Framework\Exception::class)) { - $result = \true; - } - } - return $result; - } - private function runInSeparateProcess() : bool - { - return ($this->runTestInSeparateProcess || $this->runClassInSeparateProcess) && !$this->inIsolation && !$this instanceof PhptTestCase; - } - private function isCallableTestMethod(string $dependency) : bool + public function enableAutoReturnValueGeneration() : self { - [$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); + $this->returnValueGeneration = \true; + return $this; } /** - * @psalm-template RealInstanceType of object - * - * @psalm-param class-string $originalClassName - * - * @psalm-return MockObject&RealInstanceType + * @return $this */ - private function createMockObject(string $originalClassName) : MockObject + public function disableAutoReturnValueGeneration() : self { - return $this->getMockBuilder($originalClassName)->disableOriginalConstructor()->disableOriginalClone()->disableArgumentCloning()->disallowMockingUnknownTypes()->getMock(); + $this->returnValueGeneration = \false; + return $this; } } classCode = $classCode; + $this->mockName = $mockName; + $this->configurableMethods = $configurableMethods; + } + /** + * @psalm-return class-string + */ + 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; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject; + +use const DIRECTORY_SEPARATOR; +use function explode; +use function implode; +use function is_object; +use function is_string; +use function preg_match; +use function preg_replace; +use function sprintf; +use function strlen; +use function strpos; +use function substr; +use function substr_count; +use function trim; +use function var_export; +use ReflectionMethod; +use ReflectionParameter; +use PHPUnitPHAR\SebastianBergmann\Template\Exception as TemplateException; +use PHPUnitPHAR\SebastianBergmann\Template\Template; +use PHPUnitPHAR\SebastianBergmann\Type\ReflectionMapper; +use PHPUnitPHAR\SebastianBergmann\Type\Type; +use PHPUnitPHAR\SebastianBergmann\Type\UnknownType; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class MockMethod +{ + /** + * @var Template[] + */ + private static $templates = []; /** * @var string */ - private $testName; + private $className; /** - * Returns a description for an exception. + * @var string */ - public static function exceptionToString(Throwable $e) : 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; + /** + * @throws ReflectionException + * @throws RuntimeException + */ + public static function fromReflection(ReflectionMethod $method, bool $callOriginalMethod, bool $cloneArguments) : self { - if ($e instanceof \PHPUnit\Framework\SelfDescribing) { - $buffer = $e->toString(); - if ($e instanceof \PHPUnit\Framework\ExpectationFailedException && $e->getComparisonFailure()) { - $buffer .= $e->getComparisonFailure()->getDiff(); - } - if ($e instanceof \PHPUnit\Framework\PHPTAssertionFailedError) { - $buffer .= $e->getDiff(); - } - if (!empty($buffer)) { - $buffer = trim($buffer) . "\n"; - } - return $buffer; + if ($method->isPrivate()) { + $modifier = 'private'; + } elseif ($method->isProtected()) { + $modifier = 'protected'; + } else { + $modifier = 'public'; } - if ($e instanceof Error) { - return $e->getMessage() . "\n"; + if ($method->isStatic()) { + $modifier .= ' static'; } - if ($e instanceof \PHPUnit\Framework\ExceptionWrapper) { - return $e->getClassName() . ': ' . $e->getMessage() . "\n"; + if ($method->returnsReference()) { + $reference = '&'; + } else { + $reference = ''; } - return get_class($e) . ': ' . $e->getMessage() . "\n"; + $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::getMethodParametersForDeclaration($method), self::getMethodParametersForCall($method), (new ReflectionMapper())->fromReturnType($method), $reference, $callOriginalMethod, $method->isStatic(), $deprecation); + } + public static function fromName(string $fullClassName, string $methodName, bool $cloneArguments) : self + { + return new self($fullClassName, $methodName, $cloneArguments, 'public', '', '', new UnknownType(), '', \false, \false, null); + } + 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) + { + $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; + } + public function getName() : string + { + return $this->methodName; } /** - * Constructs a TestFailure with the given test and exception. + * @throws RuntimeException */ - public function __construct(\PHPUnit\Framework\Test $failedTest, Throwable $t) + public function generateCode() : string { - if ($failedTest instanceof \PHPUnit\Framework\SelfDescribing) { - $this->testName = $failedTest->toString(); + if ($this->static) { + $templateFile = 'mocked_static_method.tpl'; + } elseif ($this->returnType->isNever() || $this->returnType->isVoid()) { + $templateFile = sprintf('%s_method_never_or_void.tpl', $this->callOriginalMethod ? 'proxied' : 'mocked'); } else { - $this->testName = get_class($failedTest); + $templateFile = sprintf('%s_method.tpl', $this->callOriginalMethod ? 'proxied' : 'mocked'); } - if (!$failedTest instanceof \PHPUnit\Framework\TestCase || !$failedTest->isInIsolation()) { - $this->failedTest = $failedTest; + $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(); } - $this->thrownException = $t; + $template = $this->getTemplate($templateFile); + $template->setVar(['arguments_decl' => $this->argumentsForDeclaration, 'arguments_call' => $this->argumentsForCall, 'return_declaration' => !empty($this->returnType->asString()) ? ': ' . $this->returnType->asString() : '', 'return_type' => $this->returnType->asString(), '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(); } - /** - * Returns a short description of the failure. - */ - public function toString() : string + public function getReturnType() : Type { - return sprintf('%s: %s', $this->testName, $this->thrownException->getMessage()); + return $this->returnType; } /** - * Returns a description for the thrown exception. + * @throws RuntimeException */ - public function getExceptionAsString() : string + private function getTemplate(string $template) : Template { - return self::exceptionToString($this->thrownException); + $filename = __DIR__ . DIRECTORY_SEPARATOR . 'Generator' . DIRECTORY_SEPARATOR . $template; + if (!isset(self::$templates[$filename])) { + try { + self::$templates[$filename] = new Template($filename); + } catch (TemplateException $e) { + throw new \PHPUnit\Framework\MockObject\RuntimeException($e->getMessage(), $e->getCode(), $e); + } + } + return self::$templates[$filename]; } /** - * Returns the name of the failing test (including data set, if any). + * Returns the parameters of a function or method. + * + * @throws RuntimeException */ - public function getTestName() : string + private static function getMethodParametersForDeclaration(ReflectionMethod $method) : string { - return $this->testName; + $parameters = []; + $types = (new ReflectionMapper())->fromParameterTypes($method); + foreach ($method->getParameters() as $i => $parameter) { + $name = '$' . $parameter->getName(); + /* Note: PHP extensions may use empty names for reference arguments + * or "..." for methods taking a variable number of arguments. + */ + if ($name === '$' || $name === '$...') { + $name = '$arg' . $i; + } + $default = ''; + $reference = ''; + $typeDeclaration = ''; + if (!$types[$i]->type()->isUnknown()) { + $typeDeclaration = $types[$i]->type()->asString() . ' '; + } + if ($parameter->isPassedByReference()) { + $reference = '&'; + } + if ($parameter->isVariadic()) { + $name = '...' . $name; + } elseif ($parameter->isDefaultValueAvailable()) { + $default = ' = ' . self::exportDefaultValue($parameter); + } elseif ($parameter->isOptional()) { + $default = ' = null'; + } + $parameters[] = $typeDeclaration . $reference . $name . $default; + } + return implode(', ', $parameters); } /** - * Returns the failing test. - * - * Note: The test object is not set when the test is executed in process - * isolation. + * Returns the parameters of a function or method. * - * @see Exception + * @throws ReflectionException */ - public function failedTest() : ?\PHPUnit\Framework\Test + private static function getMethodParametersForCall(ReflectionMethod $method) : string { - return $this->failedTest; + $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()) { + continue; + } + if ($parameter->isPassedByReference()) { + $parameters[] = '&' . $name; + } else { + $parameters[] = $name; + } + } + return implode(', ', $parameters); } /** - * Gets the thrown exception. + * @throws ReflectionException */ - public function thrownException() : Throwable + private static function exportDefaultValue(ReflectionParameter $parameter) : string { - return $this->thrownException; + try { + $defaultValue = $parameter->getDefaultValue(); + if (!is_object($defaultValue)) { + return (string) var_export($defaultValue, \true); + } + $parameterAsString = $parameter->__toString(); + return (string) explode(' = ', substr(substr($parameterAsString, strpos($parameterAsString, ' ') + strlen(' ')), 0, -2))[1]; + // @codeCoverageIgnoreStart + } catch (\ReflectionException $e) { + throw new \PHPUnit\Framework\MockObject\ReflectionException($e->getMessage(), $e->getCode(), $e); + } + // @codeCoverageIgnoreEnd } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject; + +use function array_key_exists; +use function array_values; +use function strtolower; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class MockMethodSet +{ /** - * Returns the exception's message. + * @var MockMethod[] */ - public function exceptionMessage() : string + private $methods = []; + public function addMethods(\PHPUnit\Framework\MockObject\MockMethod ...$methods) : void { - return $this->thrownException()->getMessage(); + foreach ($methods as $method) { + $this->methods[strtolower($method->getName())] = $method; + } } /** - * Returns true if the thrown exception - * is of type AssertionFailedError. + * @return MockMethod[] */ - public function isFailure() : bool + public function asArray() : array { - return $this->thrownException() instanceof \PHPUnit\Framework\AssertionFailedError; + return array_values($this->methods); + } + public function hasMethod(string $methodName) : bool + { + return array_key_exists(strtolower($methodName), $this->methods); } } classCode = $classCode; + $this->mockName = $mockName; } - public function addWarning(\PHPUnit\Framework\Test $test, \PHPUnit\Framework\Warning $e, float $time) : void + /** + * @psalm-return class-string + */ + public function generate() : string { + if (!class_exists($this->mockName, \false)) { + eval($this->classCode); + } + return $this->mockName; } - public function addFailure(\PHPUnit\Framework\Test $test, \PHPUnit\Framework\AssertionFailedError $e, float $time) : void + public function getClassCode() : string { + return $this->classCode; } - public function addIncompleteTest(\PHPUnit\Framework\Test $test, Throwable $t, float $time) : void +} + + * + * 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 +{ + /** + * @psalm-return class-string + */ + public function generate() : string; +} + + * + * 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 \PHPUnit\Framework\MockObject\Rule\InvocationOrder +{ + public function toString() : string { + return 'invoked zero or more times'; } - public function addRiskyTest(\PHPUnit\Framework\Test $test, Throwable $t, float $time) : void + public function verify() : void { } - public function addSkippedTest(\PHPUnit\Framework\Test $test, Throwable $t, float $time) : void + public function matches(BaseInvocation $invocation) : bool { + return \true; } - public function startTestSuite(\PHPUnit\Framework\TestSuite $suite) : void + protected function invokedDo(BaseInvocation $invocation) : void { } - public function endTestSuite(\PHPUnit\Framework\TestSuite $suite) : void +} + + * + * 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 \PHPUnit\Framework\MockObject\Rule\ParametersRule +{ + public function toString() : string { + return 'with any parameters'; } - public function startTest(\PHPUnit\Framework\Test $test) : void + public function apply(BaseInvocation $invocation) : void { } - public function endTest(\PHPUnit\Framework\Test $test, float $time) : void + public function verify() : void { } } @@ -64913,984 +70418,898 @@ declare (strict_types=1); * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\Framework; +namespace PHPUnit\Framework\MockObject\Rule; -use const PHP_EOL; -use function class_exists; use function count; -use function extension_loaded; -use function function_exists; -use function get_class; +use function gettype; +use function is_iterable; use function sprintf; -use function xdebug_get_monitored_functions; -use function xdebug_is_debugger_active; -use function xdebug_start_function_monitor; -use function xdebug_stop_function_monitor; -use AssertionError; -use Countable; -use Error; -use PHPUnit\Util\ErrorHandler; -use PHPUnit\Util\ExcludeList; -use PHPUnit\Util\Printer; -use PHPUnit\Util\Test as TestUtil; -use ReflectionClass; -use ReflectionException; -use PHPUnit\SebastianBergmann\CodeCoverage\CodeCoverage; -use PHPUnit\SebastianBergmann\CodeCoverage\Exception as OriginalCodeCoverageException; -use PHPUnit\SebastianBergmann\CodeCoverage\UnintentionallyCoveredCodeException; -use PHPUnit\SebastianBergmann\Invoker\Invoker; -use PHPUnit\SebastianBergmann\Invoker\TimeoutException; -use PHPUnit\SebastianBergmann\ResourceOperations\ResourceOperations; -use PHPUnit\SebastianBergmann\Timer\Timer; -use Throwable; +use PHPUnit\Framework\Constraint\Constraint; +use PHPUnit\Framework\Constraint\IsEqual; +use PHPUnit\Framework\Exception; +use PHPUnit\Framework\ExpectationFailedException; +use PHPUnit\Framework\InvalidParameterGroupException; +use PHPUnit\Framework\MockObject\Invocation as BaseInvocation; +use PHPUnitPHAR\SebastianBergmann\RecursionContext\InvalidArgumentException; /** * @internal This class is not covered by the backward compatibility promise for PHPUnit + * + * @deprecated */ -final class TestResult implements Countable +final class ConsecutiveParameters implements \PHPUnit\Framework\MockObject\Rule\ParametersRule { /** * @var array */ - private $passed = []; - /** - * @var array - */ - private $passedTestClasses = []; - /** - * @var bool - */ - private $currentTestSuiteFailed = \false; - /** - * @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; - /** - * Code Coverage information. - * - * @var CodeCoverage - */ - private $codeCoverage; - /** - * @var bool - */ - private $convertDeprecationsToExceptions = \false; - /** - * @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 bool - */ - private $forceCoversAnnotation = \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; + private $parameterGroups = []; /** - * @deprecated Use the `TestHook` interfaces instead - * - * @codeCoverageIgnore - * - * Registers a TestListener. + * @var array */ - public function addListener(\PHPUnit\Framework\TestListener $listener) : void - { - $this->listeners[] = $listener; - } + private $invocations = []; /** - * @deprecated Use the `TestHook` interfaces instead - * - * @codeCoverageIgnore - * - * Unregisters a TestListener. + * @throws Exception */ - public function removeListener(\PHPUnit\Framework\TestListener $listener) : void + public function __construct(array $parameterGroups) { - foreach ($this->listeners as $key => $_listener) { - if ($listener === $_listener) { - unset($this->listeners[$key]); + 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; } } } - /** - * @deprecated Use the `TestHook` interfaces instead - * - * @codeCoverageIgnore - * - * Flushes all flushable TestListeners. - */ - public function flushListeners() : void + public function toString() : string { - foreach ($this->listeners as $listener) { - if ($listener instanceof Printer) { - $listener->flush(); - } - } + return 'with consecutive parameters'; } /** - * Adds an error to the list of errors. + * @throws ExpectationFailedException + * @throws InvalidArgumentException */ - public function addError(\PHPUnit\Framework\Test $test, Throwable $t, float $time) : void + public function apply(BaseInvocation $invocation) : void { - if ($t instanceof \PHPUnit\Framework\RiskyTestError) { - $this->recordRisky($test, $t); - $notifyMethod = 'addRiskyTest'; - if ($test instanceof \PHPUnit\Framework\TestCase) { - $test->markAsRisky(); - } - if ($this->stopOnRisky || $this->stopOnDefect) { - $this->stop(); - } - } elseif ($t instanceof \PHPUnit\Framework\IncompleteTest) { - $this->recordNotImplemented($test, $t); - $notifyMethod = 'addIncompleteTest'; - if ($this->stopOnIncomplete) { - $this->stop(); - } - } elseif ($t instanceof \PHPUnit\Framework\SkippedTest) { - $this->recordSkipped($test, $t); - $notifyMethod = 'addSkippedTest'; - if ($this->stopOnSkipped) { - $this->stop(); - } - } else { - $this->recordError($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 \PHPUnit\Framework\ExceptionWrapper($t); - } - foreach ($this->listeners as $listener) { - $listener->{$notifyMethod}($test, $t, $time); - } - $this->lastTestFailed = \true; - $this->time += $time; + $this->invocations[] = $invocation; + $callIndex = count($this->invocations) - 1; + $this->verifyInvocation($invocation, $callIndex); } /** - * Adds a warning to the list of warnings. - * The passed in exception caused the warning. + * @throws ExpectationFailedException + * @throws InvalidArgumentException */ - public function addWarning(\PHPUnit\Framework\Test $test, \PHPUnit\Framework\Warning $e, float $time) : void + public function verify() : void { - if ($this->stopOnWarning || $this->stopOnDefect) { - $this->stop(); - } - $this->recordWarning($test, $e); - foreach ($this->listeners as $listener) { - $listener->addWarning($test, $e, $time); + foreach ($this->invocations as $callIndex => $invocation) { + $this->verifyInvocation($invocation, $callIndex); } - $this->time += $time; } /** - * Adds a failure to the list of failures. - * The passed in exception caused the failure. + * Verify a single invocation. + * + * @param int $callIndex + * + * @throws ExpectationFailedException + * @throws InvalidArgumentException */ - public function addFailure(\PHPUnit\Framework\Test $test, \PHPUnit\Framework\AssertionFailedError $e, float $time) : void + private function verifyInvocation(BaseInvocation $invocation, $callIndex) : void { - if ($e instanceof \PHPUnit\Framework\RiskyTestError || $e instanceof \PHPUnit\Framework\OutputError) { - $this->recordRisky($test, $e); - $notifyMethod = 'addRiskyTest'; - if ($test instanceof \PHPUnit\Framework\TestCase) { - $test->markAsRisky(); - } - if ($this->stopOnRisky || $this->stopOnDefect) { - $this->stop(); - } - } elseif ($e instanceof \PHPUnit\Framework\IncompleteTest) { - $this->recordNotImplemented($test, $e); - $notifyMethod = 'addIncompleteTest'; - if ($this->stopOnIncomplete) { - $this->stop(); - } - } elseif ($e instanceof \PHPUnit\Framework\SkippedTest) { - $this->recordSkipped($test, $e); - $notifyMethod = 'addSkippedTest'; - if ($this->stopOnSkipped) { - $this->stop(); - } - } else { - $this->failures[] = new \PHPUnit\Framework\TestFailure($test, $e); - $notifyMethod = 'addFailure'; - if ($this->stopOnFailure || $this->stopOnDefect) { - $this->stop(); - } + if (!isset($this->parameterGroups[$callIndex])) { + // no parameter assertion for this call index + return; } - foreach ($this->listeners as $listener) { - $listener->{$notifyMethod}($test, $e, $time); + $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())); } - $this->lastTestFailed = \true; - $this->time += $time; } +} + + * + * 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 function count; +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 +{ /** - * Informs the result that a test suite will be started. + * @var BaseInvocation[] */ - public function startTestSuite(\PHPUnit\Framework\TestSuite $suite) : void + private $invocations = []; + public function getInvocationCount() : int { - $this->currentTestSuiteFailed = \false; - foreach ($this->listeners as $listener) { - $listener->startTestSuite($suite); - } + return count($this->invocations); } - /** - * Informs the result that a test suite was completed. - */ - public function endTestSuite(\PHPUnit\Framework\TestSuite $suite) : void + public function hasBeenInvoked() : bool { - if (!$this->currentTestSuiteFailed) { - $this->passedTestClasses[] = $suite->getName(); - } - foreach ($this->listeners as $listener) { - $listener->endTestSuite($suite); - } + return count($this->invocations) > 0; } - /** - * Informs the result that a test will be started. - */ - public function startTest(\PHPUnit\Framework\Test $test) : void + public final function invoked(BaseInvocation $invocation) { - $this->lastTestFailed = \false; - $this->runTests += count($test); - foreach ($this->listeners as $listener) { - $listener->startTest($test); - } + $this->invocations[] = $invocation; + return $this->invokedDo($invocation); } + public abstract function matches(BaseInvocation $invocation) : bool; + protected abstract function invokedDo(BaseInvocation $invocation); +} + + * + * 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 function sprintf; +use PHPUnit\Framework\ExpectationFailedException; +use PHPUnit\Framework\MockObject\Invocation as BaseInvocation; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + * + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4297 + * + * @codeCoverageIgnore + */ +final class InvokedAtIndex extends \PHPUnit\Framework\MockObject\Rule\InvocationOrder +{ /** - * Informs the result that a test was completed. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @var int */ - public function endTest(\PHPUnit\Framework\Test $test, float $time) : void - { - foreach ($this->listeners as $listener) { - $listener->endTest($test, $time); - } - if (!$this->lastTestFailed && $test instanceof \PHPUnit\Framework\TestCase) { - $class = get_class($test); - $key = $class . '::' . $test->getName(); - $this->passed[$key] = ['result' => $test->getResult(), 'size' => TestUtil::getSize($class, $test->getName(\false))]; - $this->time += $time; - } - if ($this->lastTestFailed && $test instanceof \PHPUnit\Framework\TestCase) { - $this->currentTestSuiteFailed = \true; - } - } + private $sequenceIndex; /** - * Returns true if no risky test occurred. + * @var int */ - public function allHarmless() : bool - { - return $this->riskyCount() === 0; - } + private $currentIndex = -1; /** - * Gets the number of risky tests. + * @param int $sequenceIndex */ - public function riskyCount() : int + public function __construct($sequenceIndex) { - return count($this->risky); + $this->sequenceIndex = $sequenceIndex; } - /** - * Returns true if no incomplete test occurred. - */ - public function allCompletelyImplemented() : bool + public function toString() : string { - return $this->notImplementedCount() === 0; + return 'invoked at sequence index ' . $this->sequenceIndex; } - /** - * Gets the number of incomplete tests. - */ - public function notImplementedCount() : int + public function matches(BaseInvocation $invocation) : bool { - return count($this->notImplemented); + $this->currentIndex++; + return $this->currentIndex == $this->sequenceIndex; } /** - * Returns an array of TestFailure objects for the risky tests. + * Verifies that the current expectation is valid. If everything is OK the + * code should just return, if not it must throw an exception. * - * @return TestFailure[] + * @throws ExpectationFailedException */ - public function risky() : array + public function verify() : void { - return $this->risky; + if ($this->currentIndex < $this->sequenceIndex) { + throw new ExpectationFailedException(sprintf('The expected invocation at index %s was never reached.', $this->sequenceIndex)); + } } - /** - * Returns an array of TestFailure objects for the incomplete tests. - * - * @return TestFailure[] - */ - public function notImplemented() : array + protected function invokedDo(BaseInvocation $invocation) : void { - return $this->notImplemented; } +} + + * + * 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 \PHPUnit\Framework\MockObject\Rule\InvocationOrder +{ /** - * Returns true if no test has been skipped. + * @var int */ - public function noneSkipped() : bool - { - return $this->skippedCount() === 0; - } + private $requiredInvocations; /** - * Gets the number of skipped tests. + * @param int $requiredInvocations */ - public function skippedCount() : int + public function __construct($requiredInvocations) { - return count($this->skipped); + $this->requiredInvocations = $requiredInvocations; + } + public function toString() : string + { + return 'invoked at least ' . $this->requiredInvocations . ' times'; } /** - * Returns an array of TestFailure objects for the skipped tests. + * Verifies that the current expectation is valid. If everything is OK the + * code should just return, if not it must throw an exception. * - * @return TestFailure[] + * @throws ExpectationFailedException */ - public function skipped() : array + public function verify() : void { - return $this->skipped; + $count = $this->getInvocationCount(); + if ($count < $this->requiredInvocations) { + throw new ExpectationFailedException('Expected invocation at least ' . $this->requiredInvocations . ' times but it occurred ' . $count . ' time(s).'); + } } - /** - * Gets the number of detected errors. - */ - public function errorCount() : int + public function matches(BaseInvocation $invocation) : bool { - return count($this->errors); + return \true; } - /** - * Returns an array of TestFailure objects for the errors. - * - * @return TestFailure[] - */ - public function errors() : array + protected function invokedDo(BaseInvocation $invocation) : void { - return $this->errors; } - /** - * Gets the number of detected failures. - */ - public function failureCount() : int +} + + * + * 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 \PHPUnit\Framework\MockObject\Rule\InvocationOrder +{ + public function toString() : string { - return count($this->failures); + return 'invoked at least once'; } /** - * Returns an array of TestFailure objects for the failures. + * Verifies that the current expectation is valid. If everything is OK the + * code should just return, if not it must throw an exception. * - * @return TestFailure[] + * @throws ExpectationFailedException */ - public function failures() : array + public function verify() : void { - return $this->failures; + $count = $this->getInvocationCount(); + if ($count < 1) { + throw new ExpectationFailedException('Expected invocation at least once but it never occurred.'); + } } - /** - * Gets the number of detected warnings. - */ - public function warningCount() : int + public function matches(BaseInvocation $invocation) : bool { - return count($this->warnings); + return \true; } - /** - * Returns an array of TestFailure objects for the warnings. - * - * @return TestFailure[] - */ - public function warnings() : array + protected function invokedDo(BaseInvocation $invocation) : void { - return $this->warnings; } +} + + * + * 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 \PHPUnit\Framework\MockObject\Rule\InvocationOrder +{ /** - * Returns the names of the tests that have passed. + * @var int */ - public function passed() : array - { - return $this->passed; - } + private $allowedInvocations; /** - * Returns the names of the TestSuites that have passed. - * - * This enables @depends-annotations for TestClassName::class + * @param int $allowedInvocations */ - public function passedClasses() : array + public function __construct($allowedInvocations) { - return $this->passedTestClasses; + $this->allowedInvocations = $allowedInvocations; } - /** - * Returns whether code coverage information should be collected. - */ - public function getCollectCodeCoverageInformation() : bool + public function toString() : string { - return $this->codeCoverage !== null; + return 'invoked at most ' . $this->allowedInvocations . ' times'; } /** - * Runs a TestCase. + * Verifies that the current expectation is valid. If everything is OK the + * code should just return, if not it must throw an exception. * - * @throws \SebastianBergmann\CodeCoverage\InvalidArgumentException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws CodeCoverageException - * @throws UnintentionallyCoveredCodeException + * @throws ExpectationFailedException */ - public function run(\PHPUnit\Framework\Test $test) : void + public function verify() : void { - \PHPUnit\Framework\Assert::resetCount(); - $size = TestUtil::UNKNOWN; - if ($test instanceof \PHPUnit\Framework\TestCase) { - $test->setRegisterMockObjectsFromTestArgumentsRecursively($this->registerMockObjectsFromTestArgumentsRecursively); - $isAnyCoverageRequired = TestUtil::requiresCodeCoverageDataCollection($test); - $size = $test->getSize(); - } - $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 \PHPUnit\Framework\ErrorTestCase && !$test instanceof \PHPUnit\Framework\WarningTestCase && $isAnyCoverageRequired; - if ($collectCodeCoverage) { - $this->codeCoverage->start($test); - } - $monitorFunctions = $this->beStrictAboutResourceUsageDuringSmallTests && !$test instanceof \PHPUnit\Framework\ErrorTestCase && !$test instanceof \PHPUnit\Framework\WarningTestCase && $size === TestUtil::SMALL && function_exists('xdebug_start_function_monitor'); - if ($monitorFunctions) { - /* @noinspection ForgottenDebugOutputInspection */ - xdebug_start_function_monitor(ResourceOperations::getFunctions()); - } - $timer = new Timer(); - $timer->start(); - try { - $invoker = new Invoker(); - if (!$test instanceof \PHPUnit\Framework\ErrorTestCase && !$test instanceof \PHPUnit\Framework\WarningTestCase && $this->shouldTimeLimitBeEnforced($size) && $invoker->canInvokeWithTimeout()) { - switch ($size) { - case TestUtil::SMALL: - $_timeout = $this->timeoutForSmallTests; - break; - case TestUtil::MEDIUM: - $_timeout = $this->timeoutForMediumTests; - break; - case TestUtil::LARGE: - $_timeout = $this->timeoutForLargeTests; - break; - default: - $_timeout = $this->defaultTimeLimit; - } - $invoker->invoke([$test, 'runBare'], [], $_timeout); - } else { - $test->runBare(); - } - } catch (TimeoutException $e) { - $this->addFailure($test, new \PHPUnit\Framework\RiskyTestError($e->getMessage()), $_timeout); - $risky = \true; - } catch (\PHPUnit\Framework\AssertionFailedError $e) { - $failure = \true; - if ($e instanceof \PHPUnit\Framework\RiskyTestError) { - $risky = \true; - } elseif ($e instanceof \PHPUnit\Framework\IncompleteTestError) { - $incomplete = \true; - } elseif ($e instanceof \PHPUnit\Framework\SkippedTestError) { - $skipped = \true; - } - } catch (AssertionError $e) { - $test->addToAssertionCount(1); - $failure = \true; - $frame = $e->getTrace()[0]; - $e = new \PHPUnit\Framework\AssertionFailedError(sprintf('%s in %s:%s', $e->getMessage(), $frame['file'] ?? $e->getFile(), $frame['line'] ?? $e->getLine()), 0, $e); - } catch (\PHPUnit\Framework\Warning $e) { - $warning = \true; - } catch (\PHPUnit\Framework\Exception $e) { - $error = \true; - } catch (Throwable $e) { - $e = new \PHPUnit\Framework\ExceptionWrapper($e); - $error = \true; - } - $time = $timer->stop()->asSeconds(); - $test->addToAssertionCount(\PHPUnit\Framework\Assert::getCount()); - if ($monitorFunctions) { - $excludeList = new ExcludeList(); - /** @noinspection ForgottenDebugOutputInspection */ - $functions = xdebug_get_monitored_functions(); - /* @noinspection ForgottenDebugOutputInspection */ - xdebug_stop_function_monitor(); - foreach ($functions as $function) { - if (!$excludeList->isExcluded($function['filename'])) { - $this->addFailure($test, new \PHPUnit\Framework\RiskyTestError(sprintf('%s() used in %s:%s', $function['function'], $function['filename'], $function['lineno'])), $time); - } - } - } - if ($this->beStrictAboutTestsThatDoNotTestAnything && !$test->doesNotPerformAssertions() && $test->getNumAssertions() === 0) { - $risky = \true; - } - if ($this->forceCoversAnnotation && !$error && !$failure && !$warning && !$incomplete && !$skipped && !$risky) { - $annotations = TestUtil::parseTestMethodAnnotations(get_class($test), $test->getName(\false)); - if (!isset($annotations['class']['covers']) && !isset($annotations['method']['covers']) && !isset($annotations['class']['coversNothing']) && !isset($annotations['method']['coversNothing'])) { - $this->addFailure($test, new \PHPUnit\Framework\MissingCoversAnnotationException('This test does not have a @covers annotation but is expected to have one'), $time); - $risky = \true; - } - } - if ($collectCodeCoverage) { - $append = !$risky && !$incomplete && !$skipped; - $linesToBeCovered = []; - $linesToBeUsed = []; - if ($append && $test instanceof \PHPUnit\Framework\TestCase) { - try { - $linesToBeCovered = TestUtil::getLinesToBeCovered(get_class($test), $test->getName(\false)); - $linesToBeUsed = TestUtil::getLinesToBeUsed(get_class($test), $test->getName(\false)); - } catch (\PHPUnit\Framework\InvalidCoversTargetException $cce) { - $this->addWarning($test, new \PHPUnit\Framework\Warning($cce->getMessage()), $time); - } - } - try { - $this->codeCoverage->stop($append, $linesToBeCovered, $linesToBeUsed); - } catch (UnintentionallyCoveredCodeException $cce) { - $unintentionallyCoveredCodeError = new \PHPUnit\Framework\UnintentionallyCoveredCodeError('This test executed code that is not listed as code to be covered or used:' . PHP_EOL . $cce->getMessage()); - } 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 (isset($unintentionallyCoveredCodeError)) { - $this->addFailure($test, $unintentionallyCoveredCodeError, $time); - } elseif ($this->beStrictAboutTestsThatDoNotTestAnything && !$test->doesNotPerformAssertions() && $test->getNumAssertions() === 0) { - try { - $reflected = new ReflectionClass($test); - // @codeCoverageIgnoreStart - } catch (ReflectionException $e) { - throw new \PHPUnit\Framework\Exception($e->getMessage(), $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd - $name = $test->getName(\false); - if ($name && $reflected->hasMethod($name)) { - try { - $reflected = $reflected->getMethod($name); - // @codeCoverageIgnoreStart - } catch (ReflectionException $e) { - throw new \PHPUnit\Framework\Exception($e->getMessage(), $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd - } - $this->addFailure($test, new \PHPUnit\Framework\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 \PHPUnit\Framework\RiskyTestError(sprintf('This test is annotated with "@doesNotPerformAssertions" but performed %d assertions', $test->getNumAssertions())), $time); - } elseif ($this->beStrictAboutOutputDuringTests && $test->hasOutput()) { - $this->addFailure($test, new \PHPUnit\Framework\OutputError(sprintf('This test printed output: %s', $test->getActualOutput())), $time); - } elseif ($this->beStrictAboutTodoAnnotatedTests && $test instanceof \PHPUnit\Framework\TestCase) { - $annotations = TestUtil::parseTestMethodAnnotations(get_class($test), $test->getName(\false)); - if (isset($annotations['method']['todo'])) { - $this->addFailure($test, new \PHPUnit\Framework\RiskyTestError('Test method is annotated with @todo'), $time); - } + $count = $this->getInvocationCount(); + if ($count > $this->allowedInvocations) { + throw new ExpectationFailedException('Expected invocation at most ' . $this->allowedInvocations . ' times but it occurred ' . $count . ' time(s).'); } - $this->endTest($test, $time); } - /** - * Gets the number of run tests. - */ - public function count() : int + public function matches(BaseInvocation $invocation) : bool { - return $this->runTests; + return \true; } - /** - * Checks whether the test run should stop. - */ - public function shouldStop() : bool + protected function invokedDo(BaseInvocation $invocation) : void { - return $this->stop; } +} + + * + * 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 function sprintf; +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 \PHPUnit\Framework\MockObject\Rule\InvocationOrder +{ /** - * Marks that the test run should stop. + * @var int */ - public function stop() : void - { - $this->stop = \true; - } + private $expectedCount; /** - * Returns the code coverage object. + * @param int $expectedCount */ - public function getCodeCoverage() : ?CodeCoverage + public function __construct($expectedCount) { - return $this->codeCoverage; + $this->expectedCount = $expectedCount; } - /** - * Sets the code coverage object. - */ - public function setCodeCoverage(CodeCoverage $codeCoverage) : void + public function isNever() : bool { - $this->codeCoverage = $codeCoverage; + return $this->expectedCount === 0; } - /** - * Enables or disables the deprecation-to-exception conversion. - */ - public function convertDeprecationsToExceptions(bool $flag) : void + public function toString() : string { - $this->convertDeprecationsToExceptions = $flag; + return 'invoked ' . $this->expectedCount . ' time(s)'; } - /** - * Returns the deprecation-to-exception conversion setting. - */ - public function getConvertDeprecationsToExceptions() : bool + public function matches(BaseInvocation $invocation) : bool { - return $this->convertDeprecationsToExceptions; + return \true; } /** - * Enables or disables the error-to-exception conversion. + * 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 convertErrorsToExceptions(bool $flag) : void + public function verify() : void { - $this->convertErrorsToExceptions = $flag; + $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)); + } } /** - * Returns the error-to-exception conversion setting. + * @throws ExpectationFailedException */ - public function getConvertErrorsToExceptions() : bool + protected function invokedDo(BaseInvocation $invocation) : void { - return $this->convertErrorsToExceptions; + $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); + } } +} + + * + * 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 function is_string; +use PHPUnit\Framework\Constraint\Constraint; +use PHPUnit\Framework\ExpectationFailedException; +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 +{ /** - * Enables or disables the notice-to-exception conversion. + * @var Constraint */ - public function convertNoticesToExceptions(bool $flag) : void - { - $this->convertNoticesToExceptions = $flag; - } + private $constraint; /** - * Returns the notice-to-exception conversion setting. + * @param Constraint|string $constraint + * + * @throws InvalidArgumentException */ - public function getConvertNoticesToExceptions() : bool + public function __construct($constraint) { - return $this->convertNoticesToExceptions; + if (is_string($constraint)) { + $constraint = new MethodNameConstraint($constraint); + } + if (!$constraint instanceof Constraint) { + throw InvalidArgumentException::create(1, 'PHPUnit\\Framework\\Constraint\\Constraint object or string'); + } + $this->constraint = $constraint; } - /** - * Enables or disables the warning-to-exception conversion. - */ - public function convertWarningsToExceptions(bool $flag) : void + public function toString() : string { - $this->convertWarningsToExceptions = $flag; + return 'method name ' . $this->constraint->toString(); } /** - * Returns the warning-to-exception conversion setting. + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException */ - public function getConvertWarningsToExceptions() : bool + public function matches(BaseInvocation $invocation) : bool { - return $this->convertWarningsToExceptions; + return $this->matchesName($invocation->getMethodName()); } /** - * Enables or disables the stopping when an error occurs. + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException */ - public function stopOnError(bool $flag) : void + public function matchesName(string $methodName) : bool { - $this->stopOnError = $flag; + return (bool) $this->constraint->evaluate($methodName, '', \true); } +} + + * + * 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 function count; +use function get_class; +use function sprintf; +use Exception; +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; +use PHPUnitPHAR\SebastianBergmann\RecursionContext\InvalidArgumentException; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class Parameters implements \PHPUnit\Framework\MockObject\Rule\ParametersRule +{ /** - * Enables or disables the stopping when a failure occurs. + * @var Constraint[] */ - public function stopOnFailure(bool $flag) : void - { - $this->stopOnFailure = $flag; - } + private $parameters = []; /** - * Enables or disables the stopping when a warning occurs. + * @var BaseInvocation */ - 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; - } - public function forceCoversAnnotation() : void + private $invocation; + /** + * @var bool|ExpectationFailedException + */ + private $parameterVerificationResult; + /** + * @throws \PHPUnit\Framework\Exception + */ + public function __construct(array $parameters) { - $this->forceCoversAnnotation = \true; + foreach ($parameters as $parameter) { + if (!$parameter instanceof Constraint) { + $parameter = new IsEqual($parameter); + } + $this->parameters[] = $parameter; + } } - public function forcesCoversAnnotation() : bool + public function toString() : string { - return $this->forceCoversAnnotation; + $text = 'with parameter'; + foreach ($this->parameters as $index => $parameter) { + if ($index > 0) { + $text .= ' and'; + } + $text .= ' ' . $index . ' ' . $parameter->toString(); + } + return $text; } /** - * Enables or disables the stopping for risky tests. + * @throws Exception */ - public function stopOnRisky(bool $flag) : void + public function apply(BaseInvocation $invocation) : void { - $this->stopOnRisky = $flag; + $this->invocation = $invocation; + $this->parameterVerificationResult = null; + try { + $this->parameterVerificationResult = $this->doVerify(); + } catch (ExpectationFailedException $e) { + $this->parameterVerificationResult = $e; + throw $this->parameterVerificationResult; + } } /** - * Enables or disables the stopping for incomplete tests. + * 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 InvalidArgumentException */ - public function stopOnIncomplete(bool $flag) : void + public function verify() : void { - $this->stopOnIncomplete = $flag; + $this->doVerify(); } /** - * Enables or disables the stopping for skipped tests. + * @throws ExpectationFailedException + * @throws InvalidArgumentException */ - public function stopOnSkipped(bool $flag) : void + private function doVerify() : bool { - $this->stopOnSkipped = $flag; + 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; } /** - * Enables or disables the stopping for defects: error, failure, warning. + * @throws ExpectationFailedException */ - public function stopOnDefect(bool $flag) : void + private function guardAgainstDuplicateEvaluationOfParameterConstraints() : bool { - $this->stopOnDefect = $flag; + if ($this->parameterVerificationResult instanceof ExpectationFailedException) { + throw $this->parameterVerificationResult; + } + return (bool) $this->parameterVerificationResult; } +} + + * + * 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; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +interface ParametersRule extends SelfDescribing, Verifiable +{ /** - * Returns the time spent running the tests. + * @throws ExpectationFailedException if the invocation violates the rule */ - public function time() : float - { - return $this->time; - } + public function apply(BaseInvocation $invocation) : void; + public function verify() : void; +} + + * + * 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) + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +interface Stub +{ + public function __phpunit_getInvocationHandler() : \PHPUnit\Framework\MockObject\InvocationHandler; + public function __phpunit_hasMatchers() : bool; + public function __phpunit_setReturnValueGeneration(bool $returnValueGeneration) : void; +} + + * + * 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 function array_shift; +use function sprintf; +use PHPUnit\Framework\MockObject\Invocation; +use PHPUnitPHAR\SebastianBergmann\Exporter\Exporter; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class ConsecutiveCalls implements \PHPUnit\Framework\MockObject\Stub\Stub +{ /** - * Returns whether the entire test was successful or not. + * @var array */ - public function wasSuccessful() : bool + private $stack; + /** + * @var mixed + */ + private $value; + public function __construct(array $stack) { - return $this->wasSuccessfulIgnoringWarnings() && empty($this->warnings); + $this->stack = $stack; } - public function wasSuccessfulIgnoringWarnings() : bool + public function invoke(Invocation $invocation) { - return empty($this->errors) && empty($this->failures); + $this->value = array_shift($this->stack); + if ($this->value instanceof \PHPUnit\Framework\MockObject\Stub\Stub) { + $this->value = $this->value->invoke($invocation); + } + return $this->value; } - public function wasSuccessfulAndNoTestIsRiskyOrSkippedOrIncomplete() : bool + public function toString() : string { - return $this->wasSuccessful() && $this->allHarmless() && $this->allCompletelyImplemented() && $this->noneSkipped(); + $exporter = new Exporter(); + return sprintf('return user-specified value %s', $exporter->export($this->value)); } - /** - * Sets the default timeout for tests. - */ - public function setDefaultTimeLimit(int $timeout) : void +} + + * + * 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 function sprintf; +use PHPUnit\Framework\MockObject\Invocation; +use PHPUnitPHAR\SebastianBergmann\Exporter\Exporter; +use Throwable; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class Exception implements \PHPUnit\Framework\MockObject\Stub\Stub +{ + private $exception; + public function __construct(Throwable $exception) { - $this->defaultTimeLimit = $timeout; + $this->exception = $exception; } /** - * Sets the timeout for small tests. + * @throws Throwable */ - public function setTimeoutForSmallTests(int $timeout) : void + public function invoke(Invocation $invocation) : void { - $this->timeoutForSmallTests = $timeout; + throw $this->exception; } - /** - * Sets the timeout for medium tests. - */ - public function setTimeoutForMediumTests(int $timeout) : void + public function toString() : string { - $this->timeoutForMediumTests = $timeout; + $exporter = new Exporter(); + return sprintf('raise user-specified exception %s', $exporter->export($this->exception)); } +} + + * + * 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 function sprintf; +use PHPUnit\Framework\MockObject\Invocation; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class ReturnArgument implements \PHPUnit\Framework\MockObject\Stub\Stub +{ /** - * Sets the timeout for large tests. + * @var int */ - public function setTimeoutForLargeTests(int $timeout) : void + private $argumentIndex; + public function __construct($argumentIndex) { - $this->timeoutForLargeTests = $timeout; + $this->argumentIndex = $argumentIndex; } - /** - * Returns the set timeout for large tests. - */ - public function getTimeoutForLargeTests() : int + public function invoke(Invocation $invocation) { - return $this->timeoutForLargeTests; + if (isset($invocation->getParameters()[$this->argumentIndex])) { + return $invocation->getParameters()[$this->argumentIndex]; + } } - public function setRegisterMockObjectsFromTestArgumentsRecursively(bool $flag) : void + public function toString() : string { - $this->registerMockObjectsFromTestArgumentsRecursively = $flag; + return sprintf('return argument #%d', $this->argumentIndex); } - private function recordError(\PHPUnit\Framework\Test $test, Throwable $t) : void +} + + * + * 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 function call_user_func_array; +use function get_class; +use function is_array; +use function is_object; +use function sprintf; +use PHPUnit\Framework\MockObject\Invocation; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class ReturnCallback implements \PHPUnit\Framework\MockObject\Stub\Stub +{ + private $callback; + public function __construct($callback) { - $this->errors[] = new \PHPUnit\Framework\TestFailure($test, $t); + $this->callback = $callback; } - private function recordNotImplemented(\PHPUnit\Framework\Test $test, Throwable $t) : void + public function invoke(Invocation $invocation) { - $this->notImplemented[] = new \PHPUnit\Framework\TestFailure($test, $t); + return call_user_func_array($this->callback, $invocation->getParameters()); } - private function recordRisky(\PHPUnit\Framework\Test $test, Throwable $t) : void + public function toString() : string { - $this->risky[] = new \PHPUnit\Framework\TestFailure($test, $t); + 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'; } - private function recordSkipped(\PHPUnit\Framework\Test $test, Throwable $t) : void +} + + * + * 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 function sprintf; +use PHPUnit\Framework\MockObject\Invocation; +use PHPUnitPHAR\SebastianBergmann\Exporter\Exporter; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class ReturnReference implements \PHPUnit\Framework\MockObject\Stub\Stub +{ + /** + * @var mixed + */ + private $reference; + public function __construct(&$reference) { - $this->skipped[] = new \PHPUnit\Framework\TestFailure($test, $t); + $this->reference =& $reference; } - private function recordWarning(\PHPUnit\Framework\Test $test, Throwable $t) : void + public function invoke(Invocation $invocation) { - $this->warnings[] = new \PHPUnit\Framework\TestFailure($test, $t); + return $this->reference; } - private function shouldTimeLimitBeEnforced(int $size) : bool + public function toString() : string { - if (!$this->enforceTimeLimit) { - return \false; - } - if (!($this->defaultTimeLimit || $size !== TestUtil::UNKNOWN)) { - return \false; - } - if (!extension_loaded('pcntl')) { - return \false; - } - if (!class_exists(Invoker::class)) { - return \false; - } - if (extension_loaded('xdebug') && xdebug_is_debugger_active()) { - return \false; - } - return \true; + $exporter = new Exporter(); + return sprintf('return user-specified reference %s', $exporter->export($this->reference)); } } - * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -class TestSuite implements IteratorAggregate, \PHPUnit\Framework\Reorderable, \PHPUnit\Framework\SelfDescribing, \PHPUnit\Framework\Test +final class ReturnSelf implements \PHPUnit\Framework\MockObject\Stub\Stub { /** - * 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 + * @throws RuntimeException */ - protected $name = ''; + public function invoke(Invocation $invocation) + { + return $invocation->getObject(); + } + public function toString() : string + { + return 'return the current object'; + } +} + + * + * 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 function sprintf; +use PHPUnit\Framework\MockObject\Invocation; +use PHPUnitPHAR\SebastianBergmann\Exporter\Exporter; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class ReturnStub implements \PHPUnit\Framework\MockObject\Stub\Stub +{ /** - * The test groups of the test suite. - * - * @psalm-var array> + * @var mixed */ - protected $groups = []; - /** - * The tests in the test suite. - * - * @var Test[] + 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)); + } +} + + * + * 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 function array_pop; +use function count; +use function is_array; +use PHPUnit\Framework\MockObject\Invocation; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class ReturnValueMap implements \PHPUnit\Framework\MockObject\Stub\Stub +{ + /** + * @var array */ - protected $tests = []; + 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'; + } +} + + * + * 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 +{ /** - * The number of tests in the test suite. + * Fakes the processing of the invocation $invocation by returning a + * specific value. * - * @var int + * @param Invocation $invocation The invocation which was mocked and matched by the current method and argument matchers */ - protected $numTests = -1; + public function invoke(Invocation $invocation); +} + + * + * 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 +{ /** - * @var bool + * 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 */ - protected $testCase = \false; + public function verify() : void; +} + + * + * 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 Reorderable +{ + public function sortId() : string; /** - * @var string[] + * @return list */ - protected $foundClasses = []; + public function provides() : array; /** - * @var null|list + * @return list */ - protected $providedTests; + public function requires() : array; +} + + * + * 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 +{ /** - * @var null|list + * Returns a string representation of the object. */ - protected $requiredTests; + public function toString() : string; +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use Throwable; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +interface SkippedTest extends Throwable +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use PHPUnitPHAR\SebastianBergmann\RecursionContext\InvalidArgumentException; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class SkippedTestCase extends \PHPUnit\Framework\TestCase +{ /** - * @var bool + * @var ?bool */ - private $beStrictAboutChangesToGlobalState; + protected $backupGlobals = \false; /** - * @var Factory + * @var ?bool */ - private $iteratorFilter; + protected $backupStaticAttributes = \false; /** - * @var int + * @var ?bool */ - private $declaredClassesPointer; + protected $runTestInSeparateProcess = \false; /** - * @psalm-var array + * @var string */ - private $warnings = []; + 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; + } /** - * 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 + * Returns a string representation of the test case. * - * @throws Exception + * @throws InvalidArgumentException */ - public function __construct($theClass = '', string $name = '') + public function toString() : string { - if (!is_string($theClass) && !$theClass instanceof ReflectionClass) { - throw \PHPUnit\Framework\InvalidArgumentException::create(1, 'ReflectionClass object or string'); - } - $this->declaredClassesPointer = count(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 \PHPUnit\Framework\Exception($e->getMessage(), $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd - } else { - $this->setName($theClass); - return; - } - } - if (!$theClass->isSubclassOf(\PHPUnit\Framework\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 \PHPUnit\Framework\WarningTestCase(sprintf('Class "%s" has no public constructor.', $theClass->getName()))); - return; - } - foreach ((new Reflection())->publicMethodsInTestClass($theClass) as $method) { - if (!TestUtil::isTestMethod($method)) { - continue; - } - $this->addTestMethod($theClass, $method); - } - if (empty($this->tests)) { - $this->addTest(new \PHPUnit\Framework\WarningTestCase(sprintf('No tests found in class "%s".', $theClass->getName()))); - } - $this->testCase = \true; + return $this->getName(); } /** - * Returns a string representation of the test suite. + * @throws Exception */ - public function toString() : string + protected function runTest() : void { - return $this->getName(); + $this->markTestSkipped($this->message); } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use Countable; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +interface Test extends Countable +{ /** - * Adds a test to the suite. - * - * @param array $groups + * Runs a test and collects its result in a TestResult instance. */ - public function addTest(\PHPUnit\Framework\Test $test, $groups = []) : void + public function run(?\PHPUnit\Framework\TestResult $result = null) : \PHPUnit\Framework\TestResult; +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use function assert; +use function count; +use function get_class; +use function sprintf; +use function trim; +use PHPUnit\Util\Filter; +use PHPUnit\Util\InvalidDataSetException; +use PHPUnit\Util\Test as TestUtil; +use ReflectionClass; +use Throwable; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class TestBuilder +{ + public function build(ReflectionClass $theClass, string $methodName) : \PHPUnit\Framework\Test { - try { - $class = new ReflectionClass($test); - // @codeCoverageIgnoreStart - } catch (ReflectionException $e) { - throw new \PHPUnit\Framework\Exception($e->getMessage(), $e->getCode(), $e); + $className = $theClass->getName(); + if (!$theClass->isInstantiable()) { + return new \PHPUnit\Framework\ErrorTestCase(sprintf('Cannot instantiate class "%s".', $className)); } - // @codeCoverageIgnoreEnd - if (!$class->isAbstract()) { - $this->tests[] = $test; - $this->clearCaches(); - if ($test instanceof self && empty($groups)) { - $groups = $test->getGroups(); - } - if ($this->containsOnlyVirtualGroups($groups)) { - $groups[] = 'default'; - } - foreach ($groups as $group) { - if (!isset($this->groups[$group])) { - $this->groups[$group] = [$test]; - } else { - $this->groups[$group][] = $test; - } + $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 \PHPUnit\Framework\Exception('No valid test provided.'); + } + $parameters = $constructor->getParameters(); + // TestCase() or TestCase($name) + if (count($parameters) < 2) { + $test = $this->buildTestWithoutData($className); + } else { + try { + $data = TestUtil::getProvidedData($className, $methodName); + } catch (\PHPUnit\Framework\IncompleteTestError $e) { + $message = sprintf("Test for %s::%s marked incomplete by data provider\n%s", $className, $methodName, $this->throwableToString($e)); + $data = new \PHPUnit\Framework\IncompleteTestCase($className, $methodName, $message); + } catch (\PHPUnit\Framework\SkippedTestError $e) { + $message = sprintf("Test for %s::%s skipped by data provider\n%s", $className, $methodName, $this->throwableToString($e)); + $data = new \PHPUnit\Framework\SkippedTestCase($className, $methodName, $message); + } catch (Throwable $t) { + $message = sprintf("The data provider specified for %s::%s is invalid.\n%s", $className, $methodName, $this->throwableToString($t)); + $data = new \PHPUnit\Framework\ErrorTestCase($message); } - if ($test instanceof \PHPUnit\Framework\TestCase) { - $test->setGroups($groups); + // 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 \PHPUnit\Framework\TestCase) { + $test->setName($methodName); + $this->configureTestCase($test, $runTestInSeparateProcess, $preserveGlobalState, $runClassInSeparateProcess, $backupSettings); + } + return $test; } - /** - * Adds the tests from the given class to the suite. - * - * @psalm-param object|class-string $testClass - * - * @throws Exception - */ - public function addTestSuite($testClass) : void + /** @psalm-param class-string $className */ + private function buildTestWithoutData(string $className) { - if (!(is_object($testClass) || is_string($testClass) && class_exists($testClass))) { - throw \PHPUnit\Framework\InvalidArgumentException::create(1, 'class name or object'); - } - if (!is_object($testClass)) { - try { - $testClass = new ReflectionClass($testClass); - // @codeCoverageIgnoreStart - } catch (ReflectionException $e) { - throw new \PHPUnit\Framework\Exception($e->getMessage(), $e->getCode(), $e); + return new $className(); + } + /** @psalm-param class-string $className */ + private function buildDataProviderTestSuite(string $methodName, string $className, $data, bool $runTestInSeparateProcess, ?bool $preserveGlobalState, bool $runClassInSeparateProcess, array $backupSettings) : \PHPUnit\Framework\DataProviderTestSuite + { + $dataProviderTestSuite = new \PHPUnit\Framework\DataProviderTestSuite($className . '::' . $methodName); + $groups = TestUtil::getGroups($className, $methodName); + if ($data instanceof \PHPUnit\Framework\ErrorTestCase || $data instanceof \PHPUnit\Framework\SkippedTestCase || $data instanceof \PHPUnit\Framework\IncompleteTestCase) { + $dataProviderTestSuite->addTest($data, $groups); + } else { + foreach ($data as $_dataName => $_data) { + $_test = new $className($methodName, $_data, $_dataName); + assert($_test instanceof \PHPUnit\Framework\TestCase); + $this->configureTestCase($_test, $runTestInSeparateProcess, $preserveGlobalState, $runClassInSeparateProcess, $backupSettings); + $dataProviderTestSuite->addTest($_test, $groups); } - // @codeCoverageIgnoreEnd } - 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); - // @codeCoverageIgnoreStart - } catch (ReflectionException $e) { - throw new \PHPUnit\Framework\Exception($e->getMessage(), $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd - if ($method->isStatic()) { - $this->addTest($method->invoke(null, $testClass->getName())); - $suiteMethod = \true; - } + return $dataProviderTestSuite; + } + private function configureTestCase(\PHPUnit\Framework\TestCase $test, bool $runTestInSeparateProcess, ?bool $preserveGlobalState, bool $runClassInSeparateProcess, array $backupSettings) : void + { + if ($runTestInSeparateProcess) { + $test->setRunTestInSeparateProcess(\true); + if ($preserveGlobalState !== null) { + $test->setPreserveGlobalState($preserveGlobalState); } - if (!$suiteMethod && !$testClass->isAbstract() && $testClass->isSubclassOf(\PHPUnit\Framework\TestCase::class)) { - $this->addTest(new self($testClass)); + } + if ($runClassInSeparateProcess) { + $test->setRunClassInSeparateProcess(\true); + if ($preserveGlobalState !== null) { + $test->setPreserveGlobalState($preserveGlobalState); } - } else { - throw new \PHPUnit\Framework\Exception(); + } + if ($backupSettings['backupGlobals'] !== null) { + $test->setBackupGlobals($backupSettings['backupGlobals']); + } + if ($backupSettings['backupStaticAttributes'] !== null) { + $test->setBackupStaticAttributes($backupSettings['backupStaticAttributes']); } } - public function addWarning(string $warning) : void + private function throwableToString(Throwable $t) : string { - $this->warnings[] = $warning; + $message = $t->getMessage(); + if (empty(trim($message))) { + $message = ''; + } + if ($t instanceof InvalidDataSetException) { + return sprintf("%s\n%s", $message, Filter::getFilteredStacktrace($t)); + } + return sprintf("%s: %s\n%s", get_class($t), $message, Filter::getFilteredStacktrace($t)); } - /** - * Wraps both addTest() and addTestSuite - * as well as the separate import statements for the user's convenience. - * - * If the named file cannot be read or there are no new tests that can be - * added, a PHPUnit\Framework\WarningTestCase will be created instead, - * leaving the current test run untouched. - * - * @throws Exception +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use const LC_ALL; +use const LC_COLLATE; +use const LC_CTYPE; +use const LC_MONETARY; +use const LC_NUMERIC; +use const LC_TIME; +use const PATHINFO_FILENAME; +use const PHP_EOL; +use const PHP_URL_PATH; +use function array_filter; +use function array_flip; +use function array_keys; +use function array_merge; +use function array_pop; +use function array_search; +use function array_unique; +use function array_values; +use function basename; +use function call_user_func; +use function chdir; +use function class_exists; +use function clearstatcache; +use function count; +use function debug_backtrace; +use function defined; +use function explode; +use function get_class; +use function get_include_path; +use function getcwd; +use function implode; +use function in_array; +use function ini_set; +use function is_array; +use function is_callable; +use function is_int; +use function is_object; +use function is_string; +use function libxml_clear_errors; +use function method_exists; +use function ob_end_clean; +use function ob_get_contents; +use function ob_get_level; +use function ob_start; +use function parse_url; +use function pathinfo; +use function preg_replace; +use function serialize; +use function setlocale; +use function sprintf; +use function strpos; +use function substr; +use function sys_get_temp_dir; +use function tempnam; +use function trim; +use function var_export; +use PHPUnitPHAR\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\Constraint\LogicalOr; +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\Cloner; +use PHPUnit\Util\Exception as UtilException; +use PHPUnit\Util\GlobalState; +use PHPUnit\Util\PHP\AbstractPhpProcess; +use PHPUnit\Util\Test as TestUtil; +use Prophecy\Exception\Doubler\ClassNotFoundException; +use Prophecy\Exception\Doubler\DoubleException; +use Prophecy\Exception\Doubler\InterfaceNotFoundException; +use Prophecy\Exception\Prediction\PredictionException; +use Prophecy\Prophecy\MethodProphecy; +use Prophecy\Prophecy\ObjectProphecy; +use Prophecy\Prophet; +use ReflectionClass; +use ReflectionException; +use PHPUnitPHAR\SebastianBergmann\CodeCoverage\UnintentionallyCoveredCodeException; +use PHPUnitPHAR\SebastianBergmann\Comparator\Comparator; +use PHPUnitPHAR\SebastianBergmann\Comparator\Factory as ComparatorFactory; +use PHPUnitPHAR\SebastianBergmann\Diff\Differ; +use PHPUnitPHAR\SebastianBergmann\Exporter\Exporter; +use PHPUnitPHAR\SebastianBergmann\GlobalState\ExcludeList; +use PHPUnitPHAR\SebastianBergmann\GlobalState\Restorer; +use PHPUnitPHAR\SebastianBergmann\GlobalState\Snapshot; +use PHPUnitPHAR\SebastianBergmann\ObjectEnumerator\Enumerator; +use PHPUnitPHAR\SebastianBergmann\RecursionContext\InvalidArgumentException; +use PHPUnitPHAR\SebastianBergmann\Template\Template; +use SoapClient; +use Throwable; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +abstract class TestCase extends \PHPUnit\Framework\Assert implements \PHPUnit\Framework\Reorderable, \PHPUnit\Framework\SelfDescribing, \PHPUnit\Framework\Test +{ + private const LOCALE_CATEGORIES = [LC_ALL, LC_COLLATE, LC_CTYPE, LC_MONETARY, LC_NUMERIC, LC_TIME]; + /** + * @var ?bool */ - public function addTestFile(string $filename) : void - { - if (is_file($filename) && substr($filename, -5) === '.phpt') { - $this->addTest(new PhptTestCase($filename)); - $this->declaredClassesPointer = count(get_declared_classes()); - return; - } - $numTests = count($this->tests); - // 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_slice(get_declared_classes(), $this->declaredClassesPointer); - // 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->declaredClassesPointer = count(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); - // @codeCoverageIgnoreStart - } catch (ReflectionException $e) { - throw new \PHPUnit\Framework\Exception($e->getMessage(), $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd - if ($class->getFileName() == $filename) { - $newClasses = [$className]; - unset($this->foundClasses[$i]); - break; - } - } - } - foreach ($newClasses as $className) { - try { - $class = new ReflectionClass($className); - // @codeCoverageIgnoreStart - } catch (ReflectionException $e) { - throw new \PHPUnit\Framework\Exception($e->getMessage(), $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd - if (dirname($class->getFileName()) === __DIR__) { - continue; - } - if ($class->isAbstract() && $class->isSubclassOf(\PHPUnit\Framework\TestCase::class)) { - $this->addWarning(sprintf('Abstract test case classes with "Test" suffix are deprecated (%s)', $class->getName())); - } - if (!$class->isAbstract()) { - if ($class->hasMethod(BaseTestRunner::SUITE_METHODNAME)) { - try { - $method = $class->getMethod(BaseTestRunner::SUITE_METHODNAME); - // @codeCoverageIgnoreStart - } catch (ReflectionException $e) { - throw new \PHPUnit\Framework\Exception($e->getMessage(), $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd - if ($method->isStatic()) { - $this->addTest($method->invoke(null, $className)); - } - } elseif ($class->implementsInterface(\PHPUnit\Framework\Test::class)) { - // Do we have modern namespacing ('Foo\Bar\WhizBangTest') or old-school namespacing ('Foo_Bar_WhizBangTest')? - $isPsr0 = !$class->inNamespace() && strpos($class->getName(), '_') !== \false; - $expectedClassName = $isPsr0 ? $className : $shortName; - if (($pos = strpos($expectedClassName, '.')) !== \false) { - $expectedClassName = substr($expectedClassName, 0, $pos); - } - if ($class->getShortName() !== $expectedClassName) { - $this->addWarning(sprintf("Test case class not matching filename is deprecated\n in %s\n Class name was '%s', expected '%s'", $filename, $class->getShortName(), $expectedClassName)); - } - $this->addTestSuite($class); - } - } - } - if (count($this->tests) > ++$numTests) { - $this->addWarning(sprintf("Multiple test case classes per file is deprecated\n in %s", $filename)); - } - $this->numTests = -1; - } + protected $backupGlobals; /** - * Wrapper for addTestFile() that adds multiple test files. - * - * @throws Exception + * @var string[] */ - public function addTestFiles(iterable $fileNames) : void - { - foreach ($fileNames as $filename) { - $this->addTestFile((string) $filename); - } - } + protected $backupGlobalsExcludeList = []; /** - * Counts the number of test cases that will be run by this test. + * @var string[] * - * @todo refactor usage of numTests in DefaultResultPrinter + * @deprecated Use $backupGlobalsExcludeList instead */ - public function count() : int - { - $this->numTests = 0; - foreach ($this as $test) { - $this->numTests += count($test); - } - return $this->numTests; - } + protected $backupGlobalsBlacklist = []; /** - * Returns the name of the suite. + * @var ?bool */ - public function getName() : string - { - return $this->name; - } + protected $backupStaticAttributes; /** - * Returns the test groups of the suite. + * @var array> + */ + protected $backupStaticAttributesExcludeList = []; + /** + * @var array> * - * @psalm-return list + * @deprecated Use $backupStaticAttributesExcludeList instead */ - public function getGroups() : array - { - return array_map(static function ($key) : string { - return (string) $key; - }, array_keys($this->groups)); - } - public function getGroupDetails() : array - { - return $this->groups; - } + protected $backupStaticAttributesBlacklist = []; /** - * Set tests groups of the test case. + * @var ?bool */ - public function setGroupDetails(array $groups) : void - { - $this->groups = $groups; - } + protected $runTestInSeparateProcess; /** - * Runs the tests and collects their result in a TestResult. - * - * @throws \PHPUnit\Framework\CodeCoverageException - * @throws \SebastianBergmann\CodeCoverage\InvalidArgumentException - * @throws \SebastianBergmann\CodeCoverage\UnintentionallyCoveredCodeException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws Warning + * @var bool */ - public function run(\PHPUnit\Framework\TestResult $result = null) : \PHPUnit\Framework\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); - $test = null; - if ($this->testCase && class_exists($this->name, \false)) { - try { - foreach ($hookMethods['beforeClass'] as $beforeClassMethod) { - if (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 (\PHPUnit\Framework\SkippedTestError|\PHPUnit\Framework\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) { - $errorAdded = \false; - foreach ($this->tests() as $test) { - if ($result->shouldStop()) { - break; - } - $result->startTest($test); - if (!$errorAdded) { - $result->addError($test, $t, 0); - $errorAdded = \true; - } else { - $result->addFailure($test, new \PHPUnit\Framework\SkippedTestError('Test skipped because of an error in hook method'), 0); - } - $result->endTest($test, 0); - } - $result->endTestSuite($this); - return $result; - } - } - foreach ($this as $test) { - if ($result->shouldStop()) { - break; - } - if ($test instanceof \PHPUnit\Framework\TestCase || $test instanceof self) { - $test->setBeStrictAboutChangesToGlobalState($this->beStrictAboutChangesToGlobalState); - $test->setBackupGlobals($this->backupGlobals); - $test->setBackupStaticAttributes($this->backupStaticAttributes); - $test->setRunTestInSeparateProcess($this->runTestInSeparateProcess); - } - $test->run($result); - } - if ($this->testCase && class_exists($this->name, \false)) { - foreach ($hookMethods['afterClass'] as $afterClassMethod) { - if (method_exists($this->name, $afterClassMethod)) { - try { - call_user_func([$this->name, $afterClassMethod]); - } catch (Throwable $t) { - $message = "Exception in {$this->name}::{$afterClassMethod}" . PHP_EOL . $t->getMessage(); - $error = new \PHPUnit\Framework\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; - } + protected $preserveGlobalState = \true; /** - * Returns the tests as an enumeration. - * - * @return Test[] + * @var list */ - public function tests() : array - { - return $this->tests; - } + protected $providedTests = []; /** - * Set tests of the test suite. - * - * @param Test[] $tests + * @var ?bool */ - public function setTests(array $tests) : void - { - $this->tests = $tests; - } + private $runClassInSeparateProcess; /** - * Mark the test suite as skipped. - * - * @param string $message - * - * @throws SkippedTestSuiteError - * - * @psalm-return never-return + * @var bool */ - public function markTestSuiteSkipped($message = '') : void - { - throw new \PHPUnit\Framework\SkippedTestSuiteError($message); - } + private $inIsolation = \false; /** - * @param bool $beStrictAboutChangesToGlobalState + * @var array */ - public function setBeStrictAboutChangesToGlobalState($beStrictAboutChangesToGlobalState) : void - { - if (null === $this->beStrictAboutChangesToGlobalState && is_bool($beStrictAboutChangesToGlobalState)) { - $this->beStrictAboutChangesToGlobalState = $beStrictAboutChangesToGlobalState; - } - } + private $data; /** - * @param bool $backupGlobals + * @var int|string */ - public function setBackupGlobals($backupGlobals) : void - { - if (null === $this->backupGlobals && is_bool($backupGlobals)) { - $this->backupGlobals = $backupGlobals; - } - } + private $dataName; /** - * @param bool $backupStaticAttributes + * @var null|string */ - public function setBackupStaticAttributes($backupStaticAttributes) : void - { - if (null === $this->backupStaticAttributes && is_bool($backupStaticAttributes)) { - $this->backupStaticAttributes = $backupStaticAttributes; - } - } + private $expectedException; /** - * Returns an iterator for this test suite. + * @var null|string */ - public function getIterator() : Iterator - { - $iterator = new \PHPUnit\Framework\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); - } - } - } + private $expectedExceptionMessage; /** - * @psalm-return array + * @var null|string */ - public function warnings() : array - { - return array_unique($this->warnings); - } + private $expectedExceptionMessageRegExp; /** - * @return list + * @var null|int|string */ - public function provides() : array - { - if ($this->providedTests === null) { - $this->providedTests = []; - if (is_callable($this->sortId(), \true)) { - $this->providedTests[] = new \PHPUnit\Framework\ExecutionOrderDependency($this->sortId()); - } - foreach ($this->tests as $test) { - if (!$test instanceof \PHPUnit\Framework\Reorderable) { - // @codeCoverageIgnoreStart - continue; - // @codeCoverageIgnoreEnd - } - $this->providedTests = \PHPUnit\Framework\ExecutionOrderDependency::mergeUnique($this->providedTests, $test->provides()); - } - } - return $this->providedTests; - } + private $expectedExceptionCode; /** - * @return list + * @var string */ - public function requires() : array - { - if ($this->requiredTests === null) { - $this->requiredTests = []; - foreach ($this->tests as $test) { - if (!$test instanceof \PHPUnit\Framework\Reorderable) { - // @codeCoverageIgnoreStart - continue; - // @codeCoverageIgnoreEnd - } - $this->requiredTests = \PHPUnit\Framework\ExecutionOrderDependency::mergeUnique(\PHPUnit\Framework\ExecutionOrderDependency::filterInvalid($this->requiredTests), $test->requires()); - } - $this->requiredTests = \PHPUnit\Framework\ExecutionOrderDependency::diff($this->requiredTests, $this->provides()); - } - return $this->requiredTests; - } - public function sortId() : string - { - return $this->getName() . '::class'; - } + private $name = ''; /** - * Creates a default TestResult object. + * @var list */ - protected function createResult() : \PHPUnit\Framework\TestResult - { - return new \PHPUnit\Framework\TestResult(); - } + private $dependencies = []; /** - * @throws Exception + * @var array */ - protected function addTestMethod(ReflectionClass $class, ReflectionMethod $method) : void - { - $methodName = $method->getName(); - $test = (new \PHPUnit\Framework\TestBuilder())->build($class, $methodName); - if ($test instanceof \PHPUnit\Framework\TestCase || $test instanceof \PHPUnit\Framework\DataProviderTestSuite) { - $test->setDependencies(TestUtil::getDependencies($class->getName(), $methodName)); - } - $this->addTest($test, TestUtil::getGroups($class->getName(), $methodName)); - } - private function clearCaches() : void - { - $this->numTests = -1; - $this->providedTests = null; - $this->requiredTests = null; - } - private function containsOnlyVirtualGroups(array $groups) : bool - { - foreach ($groups as $group) { - if (strpos($group, '__phpunit_') !== 0) { - return \false; - } - } - return \true; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework; - -use function assert; -use function count; -use RecursiveIterator; -/** - * @template-implements RecursiveIterator - * - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class TestSuiteIterator implements RecursiveIterator -{ + private $dependencyInput = []; /** - * @var int + * @var array */ - private $position = 0; + private $iniSettings = []; /** - * @var Test[] + * @var array */ - private $tests; - public function __construct(\PHPUnit\Framework\TestSuite $testSuite) - { - $this->tests = $testSuite->tests(); - } - public function rewind() : void - { - $this->position = 0; - } - public function valid() : bool - { - return $this->position < count($this->tests); - } - public function key() : int - { - return $this->position; - } - public function current() : \PHPUnit\Framework\Test - { - return $this->tests[$this->position]; - } - public function next() : void - { - $this->position++; - } + private $locale = []; /** - * @throws NoChildTestSuiteException + * @var MockObject[] */ - public function getChildren() : self - { - if (!$this->hasChildren()) { - throw new \PHPUnit\Framework\NoChildTestSuiteException('The current item is not a TestSuite instance and therefore does not have any children.'); - } - $current = $this->current(); - assert($current instanceof \PHPUnit\Framework\TestSuite); - return new self($current); - } - public function hasChildren() : bool - { - return $this->valid() && $this->current() instanceof \PHPUnit\Framework\TestSuite; - } -} - - * - * 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 \PHPUnit\Framework\TestCase -{ + private $mockObjects = []; /** - * @var ?bool + * @var MockGenerator */ - protected $backupGlobals = \false; + private $mockObjectGenerator; /** - * @var ?bool + * @var int */ - protected $backupStaticAttributes = \false; + private $status = BaseTestRunner::STATUS_UNKNOWN; /** - * @var ?bool + * @var string */ - protected $runTestInSeparateProcess = \false; + private $statusMessage = ''; + /** + * @var int + */ + private $numAssertions = 0; + /** + * @var TestResult + */ + private $result; + /** + * @var mixed + */ + private $testResult; /** * @var string */ - private $message; - public function __construct(string $message = '') - { - $this->message = $message; - parent::__construct('Warning'); - } - public function getMessage() : string - { - return $this->message; - } + private $output = ''; /** - * Returns a string representation of the test case. + * @var ?string */ - public function toString() : string - { - return 'Warning'; - } + private $outputExpectedRegex; /** - * @throws Exception - * - * @psalm-return never-return + * @var ?string */ - protected function runTest() : void - { - throw new \PHPUnit\Framework\Warning($this->message); - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Runner; - -use function is_dir; -use function is_file; -use function substr; -use PHPUnit\Framework\Exception; -use PHPUnit\Framework\TestSuite; -use ReflectionClass; -use ReflectionException; -use PHPUnit\SebastianBergmann\FileIterator\Facade as FileIteratorFacade; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -abstract class BaseTestRunner -{ + private $outputExpectedString; /** - * @var int + * @var mixed */ - public const STATUS_UNKNOWN = -1; + private $outputCallback = \false; /** - * @var int + * @var bool */ - public const STATUS_PASSED = 0; + private $outputBufferingActive = \false; /** * @var int */ - public const STATUS_SKIPPED = 1; + private $outputBufferingLevel; /** - * @var int + * @var bool */ - public const STATUS_INCOMPLETE = 2; + private $outputRetrievedForAssertion = \false; /** - * @var int + * @var ?Snapshot */ - public const STATUS_FAILURE = 3; + private $snapshot; /** - * @var int + * @var Prophet */ - public const STATUS_ERROR = 4; + private $prophet; /** - * @var int + * @var bool */ - public const STATUS_RISKY = 5; + private $beStrictAboutChangesToGlobalState = \false; /** - * @var int + * @var bool */ - public const STATUS_WARNING = 6; + private $registerMockObjectsFromTestArgumentsRecursively = \false; /** - * @var string + * @var string[] */ - public const SUITE_METHODNAME = 'suite'; + private $warnings = []; /** - * Returns the loader to be used. + * @var string[] */ - public function getLoader() : \PHPUnit\Runner\TestSuiteLoader + 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 \PHPUnit\Runner\StandardTestSuiteLoader(); + return new AnyInvokedCountMatcher(); } /** - * 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 + * Returns a matcher that matches when the method is never executed. */ - public function getTest(string $suiteClassFile, $suffixes = '') : ?TestSuite + public static function never() : InvokedCountMatcher { - if (is_dir($suiteClassFile)) { - /** @var string[] $files */ - $files = (new FileIteratorFacade())->getFilesAsArray($suiteClassFile, $suffixes); - $suite = new TestSuite($suiteClassFile); - $suite->addTestFiles($files); - return $suite; - } - if (is_file($suiteClassFile) && substr($suiteClassFile, -5, 5) === '.phpt') { - $suite = new TestSuite(); - $suite->addTestFile($suiteClassFile); - return $suite; - } - try { - $testClass = $this->loadSuiteClass($suiteClassFile); - } catch (\PHPUnit\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) { - $test = new TestSuite($testClass); - } - $this->clearStatus(); - return $test; + return new InvokedCountMatcher(0); } /** - * Returns the loaded ReflectionClass for a suite name. + * Returns a matcher that matches when the method is executed + * at least N times. */ - protected function loadSuiteClass(string $suiteClassFile) : ReflectionClass + public static function atLeast(int $requiredInvocations) : InvokedAtLeastCountMatcher { - return $this->getLoader()->load($suiteClassFile); + return new InvokedAtLeastCountMatcher($requiredInvocations); } /** - * Clears the status message. + * Returns a matcher that matches when the method is executed at least once. */ - protected function clearStatus() : void + public static function atLeastOnce() : InvokedAtLeastOnceMatcher { + return new InvokedAtLeastOnceMatcher(); } /** - * Override to define how to handle a failed loading of - * a test suite. + * Returns a matcher that matches when the method is executed exactly once. */ - protected abstract function runFailed(string $message) : void; -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Runner; - -use const DIRECTORY_SEPARATOR; -use const LOCK_EX; -use function assert; -use function dirname; -use function file_get_contents; -use function file_put_contents; -use function in_array; -use function is_array; -use function is_dir; -use function is_file; -use function json_decode; -use function json_encode; -use function sprintf; -use PHPUnit\Util\Filesystem; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class DefaultTestResultCache implements \PHPUnit\Runner\TestResultCache -{ + public static function once() : InvokedCountMatcher + { + return new InvokedCountMatcher(1); + } /** - * @var int + * Returns a matcher that matches when the method is executed + * exactly $count times. */ - private const VERSION = 1; + public static function exactly(int $count) : InvokedCountMatcher + { + return new InvokedCountMatcher($count); + } /** - * @psalm-var list + * Returns a matcher that matches when the method is executed + * at most N times. */ - private const ALLOWED_TEST_STATUSES = [\PHPUnit\Runner\BaseTestRunner::STATUS_SKIPPED, \PHPUnit\Runner\BaseTestRunner::STATUS_INCOMPLETE, \PHPUnit\Runner\BaseTestRunner::STATUS_FAILURE, \PHPUnit\Runner\BaseTestRunner::STATUS_ERROR, \PHPUnit\Runner\BaseTestRunner::STATUS_RISKY, \PHPUnit\Runner\BaseTestRunner::STATUS_WARNING]; + public static function atMost(int $allowedInvocations) : InvokedAtMostCountMatcher + { + return new InvokedAtMostCountMatcher($allowedInvocations); + } /** - * @var string + * Returns a matcher that matches when the method is executed + * at the given index. + * + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4297 + * + * @codeCoverageIgnore */ - private const DEFAULT_RESULT_CACHE_FILENAME = '.phpunit.result.cache'; + public static function at(int $index) : InvokedAtIndexMatcher + { + $stack = debug_backtrace(); + while (!empty($stack)) { + $frame = array_pop($stack); + if (isset($frame['object']) && $frame['object'] instanceof self) { + $frame['object']->addWarning('The at() matcher has been deprecated. It will be removed in PHPUnit 10. Please refactor your test to not rely on the order in which methods are invoked.'); + break; + } + } + 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); + } /** - * @var string + * Returns the current object. + * + * This method is useful when mocking a fluent interface. */ - private $cacheFilename; + 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); + } /** - * @psalm-var array + * @param int|string $dataName + * + * @internal This method is not covered by the backward compatibility promise for PHPUnit */ - private $defects = []; + public function __construct(?string $name = null, array $data = [], $dataName = '') + { + if ($name !== null) { + $this->setName($name); + } + $this->data = $data; + $this->dataName = $dataName; + } /** - * @psalm-var array + * This method is called before the first test of this test class is run. */ - private $times = []; - public function __construct(?string $filepath = null) + public static function setUpBeforeClass() : void { - if ($filepath !== null && is_dir($filepath)) { - $filepath .= DIRECTORY_SEPARATOR . self::DEFAULT_RESULT_CACHE_FILENAME; - } - $this->cacheFilename = $filepath ?? $_ENV['PHPUNIT_RESULT_CACHE'] ?? self::DEFAULT_RESULT_CACHE_FILENAME; } - public function setState(string $testName, int $state) : void + /** + * This method is called after the last test of this test class is run. + */ + public static function tearDownAfterClass() : void { - if (!in_array($state, self::ALLOWED_TEST_STATUSES, \true)) { - return; - } - $this->defects[$testName] = $state; } - public function getState(string $testName) : int + /** + * This method is called before each test. + */ + protected function setUp() : void { - return $this->defects[$testName] ?? \PHPUnit\Runner\BaseTestRunner::STATUS_UNKNOWN; } - public function setTime(string $testName, float $time) : void + /** + * Performs assertions shared by all tests of a test case. + * + * This method is called between setUp() and test. + */ + protected function assertPreConditions() : void { - $this->times[$testName] = $time; } - public function getTime(string $testName) : float + /** + * Performs assertions shared by all tests of a test case. + * + * This method is called between test and tearDown(). + */ + protected function assertPostConditions() : void { - return $this->times[$testName] ?? 0.0; } - public function load() : void + /** + * This method is called after each test. + */ + protected function tearDown() : void { - if (!is_file($this->cacheFilename)) { - return; - } - $data = json_decode(file_get_contents($this->cacheFilename), \true); - if ($data === null) { - return; - } - if (!isset($data['version'])) { - return; - } - if ($data['version'] !== self::VERSION) { - return; - } - assert(isset($data['defects']) && is_array($data['defects'])); - assert(isset($data['times']) && is_array($data['times'])); - $this->defects = $data['defects']; - $this->times = $data['times']; } /** + * Returns a string representation of the test case. + * * @throws Exception + * @throws InvalidArgumentException */ - public function persist() : void + public function toString() : string { - if (!Filesystem::createDirectory(dirname($this->cacheFilename))) { - throw new \PHPUnit\Runner\Exception(sprintf('Cannot create directory "%s" for result cache file', $this->cacheFilename)); + try { + $class = new ReflectionClass($this); + // @codeCoverageIgnoreStart + } catch (ReflectionException $e) { + throw new \PHPUnit\Framework\Exception($e->getMessage(), $e->getCode(), $e); } - file_put_contents($this->cacheFilename, json_encode(['version' => self::VERSION, 'defects' => $this->defects, 'times' => $this->times]), LOCK_EX); + // @codeCoverageIgnoreEnd + $buffer = sprintf('%s::%s', $class->name, $this->getName(\false)); + return $buffer . $this->getDataSetAsString(); } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Runner; - -use RuntimeException; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class Exception extends RuntimeException implements \PHPUnit\Exception -{ -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Runner\Extension; - -use function class_exists; -use function sprintf; -use PHPUnit\Framework\TestListener; -use PHPUnit\Runner\Exception; -use PHPUnit\Runner\Hook; -use PHPUnit\TextUI\TestRunner; -use PHPUnit\TextUI\XmlConfiguration\Extension; -use ReflectionClass; -use ReflectionException; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class ExtensionHandler -{ - /** - * @throws Exception - */ - public function registerExtension(Extension $extensionConfiguration, TestRunner $runner) : void + public function count() : int { - $extension = $this->createInstance($extensionConfiguration); - if (!$extension instanceof Hook) { - throw new Exception(sprintf('Class "%s" does not implement a PHPUnit\\Runner\\Hook interface', $extensionConfiguration->className())); - } - $runner->addExtension($extension); + return 1; } - /** - * @throws Exception - * - * @deprecated - */ - public function createTestListenerInstance(Extension $listenerConfiguration) : TestListener + public function getActualOutputForAssertion() : string { - $listener = $this->createInstance($listenerConfiguration); - if (!$listener instanceof TestListener) { - throw new Exception(sprintf('Class "%s" does not implement the PHPUnit\\Framework\\TestListener interface', $listenerConfiguration->className())); - } - return $listener; + $this->outputRetrievedForAssertion = \true; + return $this->getActualOutput(); + } + public function expectOutputRegex(string $expectedRegex) : void + { + $this->outputExpectedRegex = $expectedRegex; + } + public function expectOutputString(string $expectedString) : void + { + $this->outputExpectedString = $expectedString; } /** - * @throws Exception + * @psalm-param class-string<\Throwable> $exception */ - private function createInstance(Extension $extensionConfiguration) : object + public function expectException(string $exception) : void { - $this->ensureClassExists($extensionConfiguration); - try { - $reflector = new ReflectionClass($extensionConfiguration->className()); - } catch (ReflectionException $e) { - throw new Exception($e->getMessage(), $e->getCode(), $e); - } - if (!$extensionConfiguration->hasArguments()) { - return $reflector->newInstance(); + // @codeCoverageIgnoreStart + switch ($exception) { + case Deprecated::class: + $this->addWarning('Expecting E_DEPRECATED and E_USER_DEPRECATED is deprecated and will no longer be possible in PHPUnit 10.'); + break; + case Error::class: + $this->addWarning('Expecting E_ERROR and E_USER_ERROR is deprecated and will no longer be possible in PHPUnit 10.'); + break; + case Notice::class: + $this->addWarning('Expecting E_STRICT, E_NOTICE, and E_USER_NOTICE is deprecated and will no longer be possible in PHPUnit 10.'); + break; + case WarningError::class: + $this->addWarning('Expecting E_WARNING and E_USER_WARNING is deprecated and will no longer be possible in PHPUnit 10.'); + break; } - return $reflector->newInstanceArgs($extensionConfiguration->arguments()); + // @codeCoverageIgnoreEnd + $this->expectedException = $exception; } /** - * @throws Exception + * @param int|string $code */ - private function ensureClassExists(Extension $extensionConfiguration) : void + public function expectExceptionCode($code) : void { - if (class_exists($extensionConfiguration->className(), \false)) { - return; - } - if ($extensionConfiguration->hasSourceFile()) { - /** - * @noinspection PhpIncludeInspection - * - * @psalm-suppress UnresolvableInclude - */ - require_once $extensionConfiguration->sourceFile(); - } - if (!class_exists($extensionConfiguration->className())) { - throw new Exception(sprintf('Class "%s" does not exist', $extensionConfiguration->className())); - } + $this->expectedExceptionCode = $code; + } + public function expectExceptionMessage(string $message) : void + { + $this->expectedExceptionMessage = $message; + } + public function expectExceptionMessageMatches(string $regularExpression) : void + { + $this->expectedExceptionMessageRegExp = $regularExpression; } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Runner\Extension; - -use function count; -use function explode; -use function implode; -use function is_file; -use function strpos; -use PHPUnit\PharIo\Manifest\ApplicationName; -use PHPUnit\PharIo\Manifest\Exception as ManifestException; -use PHPUnit\PharIo\Manifest\ManifestLoader; -use PHPUnit\PharIo\Version\Version as PharIoVersion; -use PHPUnit\Runner\Version; -use PHPUnit\SebastianBergmann\FileIterator\Facade as FileIteratorFacade; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class PharLoader -{ /** - * @psalm-return array{loadedExtensions: list, notLoadedExtensions: list} + * 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 loadPharExtensionsInDirectory(string $directory) : array + public function expectExceptionObject(\Exception $exception) : void { - $loadedExtensions = []; - $notLoadedExtensions = []; - foreach ((new FileIteratorFacade())->getFilesAsArray($directory, '.phar') as $file) { - if (!is_file('phar://' . $file . '/manifest.xml')) { - $notLoadedExtensions[] = $file . ' is not an extension for PHPUnit'; - continue; - } - try { - $applicationName = new ApplicationName('phpunit/phpunit'); - $version = new PharIoVersion($this->phpunitVersion()); - $manifest = ManifestLoader::fromFile('phar://' . $file . '/manifest.xml'); - if (!$manifest->isExtensionFor($applicationName)) { - $notLoadedExtensions[] = $file . ' is not an extension for PHPUnit'; - continue; - } - if (!$manifest->isExtensionFor($applicationName, $version)) { - $notLoadedExtensions[] = $file . ' is not compatible with this version of PHPUnit'; - continue; - } - } catch (ManifestException $e) { - $notLoadedExtensions[] = $file . ': ' . $e->getMessage(); - continue; - } - /** - * @noinspection PhpIncludeInspection - * - * @psalm-suppress UnresolvableInclude - */ - require $file; - $loadedExtensions[] = $manifest->getName()->asString() . ' ' . $manifest->getVersion()->getVersionString(); - } - return ['loadedExtensions' => $loadedExtensions, 'notLoadedExtensions' => $notLoadedExtensions]; + $this->expectException(get_class($exception)); + $this->expectExceptionMessage($exception->getMessage()); + $this->expectExceptionCode($exception->getCode()); } - private function phpunitVersion() : string + public function expectNotToPerformAssertions() : void { - $version = Version::id(); - if (strpos($version, '-') === \false) { - return $version; - } - $parts = explode('.', explode('-', $version)[0]); - if (count($parts) === 2) { - $parts[] = 0; - } - return implode('.', $parts); + $this->doesNotPerformAssertions = \true; } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Runner\Filter; - -use function in_array; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class ExcludeGroupFilterIterator extends \PHPUnit\Runner\Filter\GroupFilterIterator -{ - protected function doAccept(string $hash) : bool + /** + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/5062 + */ + public function expectDeprecation() : void { - return !in_array($hash, $this->groupTests, \true); + $this->addWarning('Expecting E_DEPRECATED and E_USER_DEPRECATED is deprecated and will no longer be possible in PHPUnit 10.'); + $this->expectedException = Deprecated::class; } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Runner\Filter; - -use function assert; -use function sprintf; -use FilterIterator; -use Iterator; -use PHPUnit\Framework\TestSuite; -use PHPUnit\Runner\Exception; -use RecursiveFilterIterator; -use ReflectionClass; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class Factory -{ /** - * @psalm-var array + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/5062 */ - private $filters = []; + public function expectDeprecationMessage(string $message) : void + { + $this->addWarning('Expecting E_DEPRECATED and E_USER_DEPRECATED is deprecated and will no longer be possible in PHPUnit 10.'); + $this->expectExceptionMessage($message); + } /** - * @param array|string $args - * - * @throws Exception + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/5062 */ - public function addFilter(ReflectionClass $filter, $args) : void + public function expectDeprecationMessageMatches(string $regularExpression) : void { - if (!$filter->isSubclassOf(RecursiveFilterIterator::class)) { - throw new Exception(sprintf('Class "%s" does not extend RecursiveFilterIterator', $filter->name)); - } - $this->filters[] = [$filter, $args]; + $this->addWarning('Expecting E_DEPRECATED and E_USER_DEPRECATED is deprecated and will no longer be possible in PHPUnit 10.'); + $this->expectExceptionMessageMatches($regularExpression); } - public function factory(Iterator $iterator, TestSuite $suite) : FilterIterator + /** + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/5062 + */ + public function expectNotice() : void { - foreach ($this->filters as $filter) { - [$class, $args] = $filter; - $iterator = $class->newInstance($iterator, $args, $suite); - } - assert($iterator instanceof FilterIterator); - return $iterator; + $this->addWarning('Expecting E_STRICT, E_NOTICE, and E_USER_NOTICE is deprecated and will no longer be possible in PHPUnit 10.'); + $this->expectedException = Notice::class; } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Runner\Filter; - -use function array_map; -use function array_merge; -use function in_array; -use function spl_object_hash; -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[] + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/5062 */ - protected $groupTests = []; - public function __construct(RecursiveIterator $iterator, array $groups, TestSuite $suite) + public function expectNoticeMessage(string $message) : void { - 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); - } - } + $this->addWarning('Expecting E_STRICT, E_NOTICE, and E_USER_NOTICE is deprecated and will no longer be possible in PHPUnit 10.'); + $this->expectExceptionMessage($message); } - public function accept() : bool + /** + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/5062 + */ + public function expectNoticeMessageMatches(string $regularExpression) : void { - $test = $this->getInnerIterator()->current(); - if ($test instanceof TestSuite) { - return \true; - } - return $this->doAccept(spl_object_hash($test)); + $this->addWarning('Expecting E_STRICT, E_NOTICE, and E_USER_NOTICE is deprecated and will no longer be possible in PHPUnit 10.'); + $this->expectExceptionMessageMatches($regularExpression); } - protected abstract function doAccept(string $hash); -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Runner\Filter; - -use function in_array; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class IncludeGroupFilterIterator extends \PHPUnit\Runner\Filter\GroupFilterIterator -{ - protected function doAccept(string $hash) : bool + /** + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/5062 + */ + public function expectWarning() : void { - return in_array($hash, $this->groupTests, \true); + $this->addWarning('Expecting E_WARNING and E_USER_WARNING is deprecated and will no longer be possible in PHPUnit 10.'); + $this->expectedException = WarningError::class; } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Runner\Filter; - -use function end; -use function implode; -use function preg_match; -use function sprintf; -use function str_replace; -use Exception; -use PHPUnit\Framework\ErrorTestCase; -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 + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/5062 */ - private $filter; + public function expectWarningMessage(string $message) : void + { + $this->addWarning('Expecting E_WARNING and E_USER_WARNING is deprecated and will no longer be possible in PHPUnit 10.'); + $this->expectExceptionMessage($message); + } /** - * @var int + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/5062 */ - private $filterMin; + public function expectWarningMessageMatches(string $regularExpression) : void + { + $this->addWarning('Expecting E_WARNING and E_USER_WARNING is deprecated and will no longer be possible in PHPUnit 10.'); + $this->expectExceptionMessageMatches($regularExpression); + } /** - * @var int + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/5062 */ - private $filterMax; + public function expectError() : void + { + $this->addWarning('Expecting E_ERROR and E_USER_ERROR is deprecated and will no longer be possible in PHPUnit 10.'); + $this->expectedException = Error::class; + } /** - * @throws Exception + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/5062 */ - public function __construct(RecursiveIterator $iterator, string $filter) + public function expectErrorMessage(string $message) : void { - parent::__construct($iterator); - $this->setFilter($filter); + $this->addWarning('Expecting E_ERROR and E_USER_ERROR is deprecated and will no longer be possible in PHPUnit 10.'); + $this->expectExceptionMessage($message); } /** - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/5062 */ - public function accept() : bool + public function expectErrorMessageMatches(string $regularExpression) : void { - $test = $this->getInnerIterator()->current(); - if ($test instanceof TestSuite) { - return \true; - } - $tmp = \PHPUnit\Util\Test::describe($test); - if ($test instanceof ErrorTestCase || $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; + $this->addWarning('Expecting E_ERROR and E_USER_ERROR is deprecated and will no longer be possible in PHPUnit 10.'); + $this->expectExceptionMessageMatches($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; } /** - * @throws Exception + * 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 \SebastianBergmann\CodeCoverage\InvalidArgumentException + * @throws CodeCoverageException + * @throws InvalidArgumentException + * @throws UnintentionallyCoveredCodeException + * @throws UtilException */ - private function setFilter(string $filter) : void + public function run(?\PHPUnit\Framework\TestResult $result = null) : \PHPUnit\Framework\TestResult { - 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 = (int) $matches[2]; - $this->filterMax = (int) $matches[3]; - } else { - $filter = sprintf('%s.*with data set #%s$', $matches[1], $matches[2]); - } - } 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)); + if ($result === null) { + $result = $this->createResult(); } - $this->filter = $filter; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Runner; - -/** - * This interface, as well as the associated mechanism for extending PHPUnit, - * will be removed in PHPUnit 10. There is no alternative available in this - * version of PHPUnit. - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see https://github.com/sebastianbergmann/phpunit/issues/4676 - */ -interface AfterIncompleteTestHook extends \PHPUnit\Runner\TestHook -{ - public function executeAfterIncompleteTest(string $test, string $message, float $time) : void; -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Runner; - -/** - * This interface, as well as the associated mechanism for extending PHPUnit, - * will be removed in PHPUnit 10. There is no alternative available in this - * version of PHPUnit. - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see https://github.com/sebastianbergmann/phpunit/issues/4676 - */ -interface AfterLastTestHook extends \PHPUnit\Runner\Hook -{ - public function executeAfterLastTest() : void; -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Runner; - -/** - * This interface, as well as the associated mechanism for extending PHPUnit, - * will be removed in PHPUnit 10. There is no alternative available in this - * version of PHPUnit. - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see https://github.com/sebastianbergmann/phpunit/issues/4676 - */ -interface AfterRiskyTestHook extends \PHPUnit\Runner\TestHook -{ - public function executeAfterRiskyTest(string $test, string $message, float $time) : void; -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Runner; - -/** - * This interface, as well as the associated mechanism for extending PHPUnit, - * will be removed in PHPUnit 10. There is no alternative available in this - * version of PHPUnit. - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see https://github.com/sebastianbergmann/phpunit/issues/4676 - */ -interface AfterSkippedTestHook extends \PHPUnit\Runner\TestHook -{ - public function executeAfterSkippedTest(string $test, string $message, float $time) : void; -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Runner; - -/** - * This interface, as well as the associated mechanism for extending PHPUnit, - * will be removed in PHPUnit 10. There is no alternative available in this - * version of PHPUnit. - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see https://github.com/sebastianbergmann/phpunit/issues/4676 - */ -interface AfterSuccessfulTestHook extends \PHPUnit\Runner\TestHook -{ - public function executeAfterSuccessfulTest(string $test, float $time) : void; -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Runner; - -/** - * This interface, as well as the associated mechanism for extending PHPUnit, - * will be removed in PHPUnit 10. There is no alternative available in this - * version of PHPUnit. - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see https://github.com/sebastianbergmann/phpunit/issues/4676 - */ -interface AfterTestErrorHook extends \PHPUnit\Runner\TestHook -{ - public function executeAfterTestError(string $test, string $message, float $time) : void; -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Runner; - -/** - * This interface, as well as the associated mechanism for extending PHPUnit, - * will be removed in PHPUnit 10. There is no alternative available in this - * version of PHPUnit. - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see https://github.com/sebastianbergmann/phpunit/issues/4676 - */ -interface AfterTestFailureHook extends \PHPUnit\Runner\TestHook -{ - public function executeAfterTestFailure(string $test, string $message, float $time) : void; -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Runner; - -/** - * This interface, as well as the associated mechanism for extending PHPUnit, - * will be removed in PHPUnit 10. There is no alternative available in this - * version of PHPUnit. - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see https://github.com/sebastianbergmann/phpunit/issues/4676 - */ -interface AfterTestHook extends \PHPUnit\Runner\TestHook -{ + if (!$this instanceof \PHPUnit\Framework\ErrorTestCase && !$this instanceof \PHPUnit\Framework\WarningTestCase) { + $this->setTestResultObject($result); + } + if (!$this instanceof \PHPUnit\Framework\ErrorTestCase && !$this instanceof \PHPUnit\Framework\WarningTestCase && !$this instanceof \PHPUnit\Framework\SkippedTestCase && !$this->handleDependencies()) { + return $result; + } + if ($this->runInSeparateProcess()) { + $runEntireClass = $this->runClassInSeparateProcess && !$this->runTestInSeparateProcess; + try { + $class = new ReflectionClass($this); + // @codeCoverageIgnoreStart + } catch (ReflectionException $e) { + throw new \PHPUnit\Framework\Exception($e->getMessage(), $e->getCode(), $e); + } + // @codeCoverageIgnoreEnd + if ($runEntireClass) { + $template = new Template(__DIR__ . '/../Util/PHP/Template/TestCaseClass.tpl'); + } else { + $template = new 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 = '\'\''; + } + $codeCoverage = $result->getCodeCoverage(); + $codeCoverageFilter = null; + $cachesStaticAnalysis = 'false'; + $codeCoverageCacheDirectory = null; + $driverMethod = 'forLineCoverage'; + if ($codeCoverage) { + $codeCoverageFilter = $codeCoverage->filter(); + if ($codeCoverage->collectsBranchAndPathCoverage()) { + $driverMethod = 'forLineAndPathCoverage'; + } + if ($codeCoverage->cachesStaticAnalysis()) { + $cachesStaticAnalysis = 'true'; + $codeCoverageCacheDirectory = $codeCoverage->cacheDirectory(); + } + } + $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); + $codeCoverageCacheDirectory = var_export(serialize($codeCoverageCacheDirectory), \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 . ".'"; + $codeCoverageCacheDirectory = "'." . $codeCoverageCacheDirectory . ".'"; + $configurationFilePath = $GLOBALS['__PHPUNIT_CONFIGURATION_FILE'] ?? ''; + $processResultFile = tempnam(sys_get_temp_dir(), 'phpunit_'); + $var = ['composerAutoload' => $composerAutoload, 'phar' => $phar, 'filename' => $class->getFileName(), 'className' => $class->getName(), 'collectCodeCoverageInformation' => $coverage, 'cachesStaticAnalysis' => $cachesStaticAnalysis, 'codeCoverageCacheDirectory' => $codeCoverageCacheDirectory, 'driverMethod' => $driverMethod, '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), 'processResultFile' => $processResultFile]; + if (!$runEntireClass) { + $var['methodName'] = $this->name; + } + $template->setVar($var); + $php = AbstractPhpProcess::factory(); + $php->runTestJob($template->render(), $this, $result, $processResultFile); + } else { + $result->run($this); + } + $this->result = null; + return $result; + } /** - * This hook will fire after any test, regardless of the result. + * Returns a builder object to create mock objects using a fluent interface. * - * For more fine grained control, have a look at the other hooks - * that extend PHPUnit\Runner\Hook. + * @psalm-template RealInstanceType of object + * + * @psalm-param class-string $className + * + * @psalm-return MockBuilder */ - public function executeAfterTest(string $test, float $time) : void; -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Runner; - -/** - * This interface, as well as the associated mechanism for extending PHPUnit, - * will be removed in PHPUnit 10. There is no alternative available in this - * version of PHPUnit. - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see https://github.com/sebastianbergmann/phpunit/issues/4676 - */ -interface AfterTestWarningHook extends \PHPUnit\Runner\TestHook -{ - public function executeAfterTestWarning(string $test, string $message, float $time) : void; -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Runner; - -/** - * This interface, as well as the associated mechanism for extending PHPUnit, - * will be removed in PHPUnit 10. There is no alternative available in this - * version of PHPUnit. - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see https://github.com/sebastianbergmann/phpunit/issues/4676 - */ -interface BeforeFirstTestHook extends \PHPUnit\Runner\Hook -{ - public function executeBeforeFirstTest() : void; -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Runner; - -/** - * This interface, as well as the associated mechanism for extending PHPUnit, - * will be removed in PHPUnit 10. There is no alternative available in this - * version of PHPUnit. - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see https://github.com/sebastianbergmann/phpunit/issues/4676 - */ -interface BeforeTestHook extends \PHPUnit\Runner\TestHook -{ - public function executeBeforeTest(string $test) : void; -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Runner; - -/** - * This interface, as well as the associated mechanism for extending PHPUnit, - * will be removed in PHPUnit 10. There is no alternative available in this - * version of PHPUnit. - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see https://github.com/sebastianbergmann/phpunit/issues/4676 - */ -interface Hook -{ -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Runner; - -/** - * This interface, as well as the associated mechanism for extending PHPUnit, - * will be removed in PHPUnit 10. There is no alternative available in this - * version of PHPUnit. - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see https://github.com/sebastianbergmann/phpunit/issues/4676 - */ -interface TestHook extends \PHPUnit\Runner\Hook -{ -} - - * - * 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; -use Throwable; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class TestListenerAdapter implements TestListener -{ + public function getMockBuilder(string $className) : MockBuilder + { + $this->recordDoubledType($className); + return new MockBuilder($this, $className); + } + public function registerComparator(Comparator $comparator) : void + { + ComparatorFactory::getInstance()->register($comparator); + $this->customComparators[] = $comparator; + } /** - * @var TestHook[] + * @return string[] + * + * @internal This method is not covered by the backward compatibility promise for PHPUnit */ - private $hooks = []; + public function doubledTypes() : array + { + return array_unique($this->doubledTypes); + } /** - * @var bool + * @internal This method is not covered by the backward compatibility promise for PHPUnit */ - private $lastTestWasNotSuccessful; - public function add(\PHPUnit\Runner\TestHook $hook) : void + public function getGroups() : array { - $this->hooks[] = $hook; + return $this->groups; } - public function startTest(Test $test) : void + /** + * @internal This method is not covered by the backward compatibility promise for PHPUnit + */ + public function setGroups(array $groups) : void { - foreach ($this->hooks as $hook) { - if ($hook instanceof \PHPUnit\Runner\BeforeTestHook) { - $hook->executeBeforeTest(TestUtil::describeAsString($test)); - } - } - $this->lastTestWasNotSuccessful = \false; + $this->groups = $groups; } - public function addError(Test $test, Throwable $t, float $time) : void + /** + * @throws InvalidArgumentException + * + * @internal This method is not covered by the backward compatibility promise for PHPUnit + */ + public function getName(bool $withDataSet = \true) : string { - foreach ($this->hooks as $hook) { - if ($hook instanceof \PHPUnit\Runner\AfterTestErrorHook) { - $hook->executeAfterTestError(TestUtil::describeAsString($test), $t->getMessage(), $time); - } + if ($withDataSet) { + return $this->name . $this->getDataSetAsString(\false); } - $this->lastTestWasNotSuccessful = \true; + return $this->name; } - public function addWarning(Test $test, Warning $e, float $time) : void + /** + * Returns the size of the test. + * + * @throws InvalidArgumentException + * + * @internal This method is not covered by the backward compatibility promise for PHPUnit + */ + public function getSize() : int { - foreach ($this->hooks as $hook) { - if ($hook instanceof \PHPUnit\Runner\AfterTestWarningHook) { - $hook->executeAfterTestWarning(TestUtil::describeAsString($test), $e->getMessage(), $time); - } - } - $this->lastTestWasNotSuccessful = \true; + return TestUtil::getSize(static::class, $this->getName(\false)); } - public function addFailure(Test $test, AssertionFailedError $e, float $time) : void + /** + * @throws InvalidArgumentException + * + * @internal This method is not covered by the backward compatibility promise for PHPUnit + */ + public function hasSize() : bool { - foreach ($this->hooks as $hook) { - if ($hook instanceof \PHPUnit\Runner\AfterTestFailureHook) { - $hook->executeAfterTestFailure(TestUtil::describeAsString($test), $e->getMessage(), $time); - } - } - $this->lastTestWasNotSuccessful = \true; + return $this->getSize() !== TestUtil::UNKNOWN; } - public function addIncompleteTest(Test $test, Throwable $t, float $time) : void + /** + * @throws InvalidArgumentException + * + * @internal This method is not covered by the backward compatibility promise for PHPUnit + */ + public function isSmall() : bool { - foreach ($this->hooks as $hook) { - if ($hook instanceof \PHPUnit\Runner\AfterIncompleteTestHook) { - $hook->executeAfterIncompleteTest(TestUtil::describeAsString($test), $t->getMessage(), $time); - } - } - $this->lastTestWasNotSuccessful = \true; + return $this->getSize() === TestUtil::SMALL; } - public function addRiskyTest(Test $test, Throwable $t, float $time) : void + /** + * @throws InvalidArgumentException + * + * @internal This method is not covered by the backward compatibility promise for PHPUnit + */ + public function isMedium() : bool { - foreach ($this->hooks as $hook) { - if ($hook instanceof \PHPUnit\Runner\AfterRiskyTestHook) { - $hook->executeAfterRiskyTest(TestUtil::describeAsString($test), $t->getMessage(), $time); - } - } - $this->lastTestWasNotSuccessful = \true; + return $this->getSize() === TestUtil::MEDIUM; } - public function addSkippedTest(Test $test, Throwable $t, float $time) : void + /** + * @throws InvalidArgumentException + * + * @internal This method is not covered by the backward compatibility promise for PHPUnit + */ + public function isLarge() : bool { - foreach ($this->hooks as $hook) { - if ($hook instanceof \PHPUnit\Runner\AfterSkippedTestHook) { - $hook->executeAfterSkippedTest(TestUtil::describeAsString($test), $t->getMessage(), $time); - } - } - $this->lastTestWasNotSuccessful = \true; + return $this->getSize() === TestUtil::LARGE; } - public function endTest(Test $test, float $time) : void + /** + * @internal This method is not covered by the backward compatibility promise for PHPUnit + */ + public function getActualOutput() : string { - if (!$this->lastTestWasNotSuccessful) { - foreach ($this->hooks as $hook) { - if ($hook instanceof \PHPUnit\Runner\AfterSuccessfulTestHook) { - $hook->executeAfterSuccessfulTest(TestUtil::describeAsString($test), $time); - } - } - } - foreach ($this->hooks as $hook) { - if ($hook instanceof \PHPUnit\Runner\AfterTestHook) { - $hook->executeAfterTest(TestUtil::describeAsString($test), $time); - } + if (!$this->outputBufferingActive) { + return $this->output; } + return (string) ob_get_contents(); } - public function startTestSuite(TestSuite $suite) : void - { - } - public function endTestSuite(TestSuite $suite) : void - { - } -} - - * - * 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 \PHPUnit\Runner\TestResultCache -{ - public function setState(string $testName, int $state) : void - { - } - public function getState(string $testName) : int - { - return \PHPUnit\Runner\BaseTestRunner::STATUS_UNKNOWN; - } - public function setTime(string $testName, float $time) : void - { - } - public function getTime(string $testName) : float + /** + * @internal This method is not covered by the backward compatibility promise for PHPUnit + */ + public function hasOutput() : bool { - return 0; + if ($this->output === '') { + return \false; + } + if ($this->hasExpectationOnOutput()) { + return \false; + } + return \true; } - public function load() : void + /** + * @internal This method is not covered by the backward compatibility promise for PHPUnit + */ + public function doesNotPerformAssertions() : bool { + return $this->doesNotPerformAssertions; } - public function persist() : void + /** + * @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; } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Runner; - -use const DEBUG_BACKTRACE_IGNORE_ARGS; -use const DIRECTORY_SEPARATOR; -use function array_merge; -use function basename; -use function debug_backtrace; -use function defined; -use function dirname; -use function explode; -use function extension_loaded; -use function file; -use function file_get_contents; -use function file_put_contents; -use function is_array; -use function is_file; -use function is_readable; -use function is_string; -use function ltrim; -use function phpversion; -use function preg_match; -use function preg_replace; -use function preg_split; -use function realpath; -use function rtrim; -use function sprintf; -use function str_replace; -use function strncasecmp; -use function strpos; -use function substr; -use function trim; -use function unlink; -use function unserialize; -use function var_export; -use function version_compare; -use PHPUnit\Framework\Assert; -use PHPUnit\Framework\AssertionFailedError; -use PHPUnit\Framework\ExecutionOrderDependency; -use PHPUnit\Framework\ExpectationFailedException; -use PHPUnit\Framework\IncompleteTestError; -use PHPUnit\Framework\PHPTAssertionFailedError; -use PHPUnit\Framework\Reorderable; -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 PHPUnit\SebastianBergmann\CodeCoverage\RawCodeCoverageData; -use PHPUnit\SebastianBergmann\Template\Template; -use PHPUnit\SebastianBergmann\Timer\Timer; -use Throwable; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class PhptTestCase implements Reorderable, SelfDescribing, Test -{ /** - * @var string + * @internal This method is not covered by the backward compatibility promise for PHPUnit */ - private $filename; + public function getExpectedException() : ?string + { + return $this->expectedException; + } /** - * @var AbstractPhpProcess + * @return null|int|string + * + * @internal This method is not covered by the backward compatibility promise for PHPUnit */ - private $phpUtil; + public function getExpectedExceptionCode() + { + return $this->expectedExceptionCode; + } /** - * @var string + * @internal This method is not covered by the backward compatibility promise for PHPUnit */ - private $output = ''; + public function getExpectedExceptionMessage() : ?string + { + return $this->expectedExceptionMessage; + } /** - * Constructs a test case with the given filename. - * - * @throws Exception + * @internal This method is not covered by the backward compatibility promise for PHPUnit */ - public function __construct(string $filename, AbstractPhpProcess $phpUtil = null) + public function getExpectedExceptionMessageRegExp() : ?string { - if (!is_file($filename)) { - throw new \PHPUnit\Runner\Exception(sprintf('File "%s" does not exist.', $filename)); - } - $this->filename = $filename; - $this->phpUtil = $phpUtil ?: AbstractPhpProcess::factory(); + return $this->expectedExceptionMessageRegExp; } /** - * Counts the number of test cases executed by run(TestResult result). + * @internal This method is not covered by the backward compatibility promise for PHPUnit */ - public function count() : int + public function setRegisterMockObjectsFromTestArgumentsRecursively(bool $flag) : void { - return 1; + $this->registerMockObjectsFromTestArgumentsRecursively = $flag; } /** - * Runs a test and collects its result in a TestResult instance. + * @throws Throwable * - * @throws \SebastianBergmann\CodeCoverage\InvalidArgumentException - * @throws \SebastianBergmann\CodeCoverage\UnintentionallyCoveredCodeException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws Exception + * @internal This method is not covered by the backward compatibility promise for PHPUnit */ - public function run(TestResult $result = null) : TestResult + public function runBare() : void { - if ($result === null) { - $result = new TestResult(); - } + $this->numAssertions = 0; + $this->snapshotGlobalState(); + $this->startOutputBuffering(); + clearstatcache(); + $currentWorkingDirectory = getcwd(); + $hookMethods = TestUtil::getHookMethods(static::class); + $hasMetRequirements = \false; try { - $sections = $this->parse(); - } catch (\PHPUnit\Runner\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($this->settings($result->getCollectCodeCoverageInformation())); - $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()); + $this->checkRequirements(); + $hasMetRequirements = \true; + if ($this->inIsolation) { + foreach ($hookMethods['beforeClass'] as $method) { + $this->{$method}(); + } + } + $this->setDoesNotPerformAssertionsFromAnnotation(); + foreach ($hookMethods['before'] as $method) { + $this->{$method}(); + } + foreach ($hookMethods['preCondition'] as $method) { + $this->{$method}(); + } + $this->testResult = $this->runTest(); + $this->verifyMockObjects(); + foreach ($hookMethods['postCondition'] as $method) { + $this->{$method}(); + } + if (!empty($this->warnings)) { + throw new \PHPUnit\Framework\Warning(implode("\n", array_unique($this->warnings))); + } + $this->status = BaseTestRunner::STATUS_PASSED; + } catch (\PHPUnit\Framework\IncompleteTest $e) { + $this->status = BaseTestRunner::STATUS_INCOMPLETE; + $this->statusMessage = $e->getMessage(); + } catch (\PHPUnit\Framework\SkippedTest $e) { + $this->status = BaseTestRunner::STATUS_SKIPPED; + $this->statusMessage = $e->getMessage(); + } catch (\PHPUnit\Framework\Warning $e) { + $this->status = BaseTestRunner::STATUS_WARNING; + $this->statusMessage = $e->getMessage(); + } catch (\PHPUnit\Framework\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(); } - $skip = $this->runSkip($sections, $result, $settings); - if ($skip) { - return $result; + $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; } - if (isset($sections['XFAIL'])) { - $xfail = trim($sections['XFAIL']); + try { + $this->stopOutputBuffering(); + } catch (\PHPUnit\Framework\RiskyTestError $_e) { + $e = $e ?? $_e; } - if (isset($sections['STDIN'])) { - $this->phpUtil->setStdin($sections['STDIN']); + if (isset($_e)) { + $this->status = BaseTestRunner::STATUS_ERROR; + $this->statusMessage = $_e->getMessage(); } - if (isset($sections['ARGS'])) { - $this->phpUtil->setArgs($sections['ARGS']); + clearstatcache(); + if ($currentWorkingDirectory !== getcwd()) { + chdir($currentWorkingDirectory); } - if ($result->getCollectCodeCoverageInformation()) { - $codeCoverageCacheDirectory = null; - $pathCoverage = \false; - $codeCoverage = $result->getCodeCoverage(); - if ($codeCoverage) { - if ($codeCoverage->cachesStaticAnalysis()) { - $codeCoverageCacheDirectory = $codeCoverage->cacheDirectory(); + $this->restoreGlobalState(); + $this->unregisterCustomComparators(); + $this->cleanupIniSettings(); + $this->cleanupLocaleSettings(); + libxml_clear_errors(); + // Perform assertion on output. + if (!isset($e)) { + try { + if ($this->outputExpectedRegex !== null) { + $this->assertMatchesRegularExpression($this->outputExpectedRegex, $this->output); + } elseif ($this->outputExpectedString !== null) { + $this->assertEquals($this->outputExpectedString, $this->output); } - $pathCoverage = $codeCoverage->collectsBranchAndPathCoverage(); + } catch (Throwable $_e) { + $e = $_e; } - $this->renderForCoverage($code, $pathCoverage, $codeCoverageCacheDirectory); - } - $timer = new Timer(); - $timer->start(); - $jobResult = $this->phpUtil->runJob($code, $this->stringifyIni($settings)); - $time = $timer->stop()->asSeconds(); - $this->output = $jobResult['stdout'] ?? ''; - if (isset($codeCoverage) && ($coverage = $this->cleanupForCoverage())) { - $codeCoverage->append($coverage, $this, \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 : ''); + // Workaround for missing "finally". + if (isset($e)) { + if ($e instanceof PredictionException) { + $e = new \PHPUnit\Framework\AssertionFailedError($e->getMessage()); } - $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->onNotSuccessfulTest($e); } - $this->runClean($sections, $result->getCollectCodeCoverageInformation()); - $result->endTest($this, $time); - return $result; } /** - * Returns the name of the test case. + * @internal This method is not covered by the backward compatibility promise for PHPUnit */ - public function getName() : string + public function setName(string $name) : void { - return $this->toString(); + $this->name = $name; + if (is_callable($this->sortId(), \true)) { + $this->providedTests = [new \PHPUnit\Framework\ExecutionOrderDependency($this->sortId())]; + } } /** - * Returns a string representation of the test case. + * @param list $dependencies + * + * @internal This method is not covered by the backward compatibility promise for PHPUnit */ - public function toString() : string + public function setDependencies(array $dependencies) : void { - return $this->filename; + $this->dependencies = $dependencies; } - public function usesDataProvider() : bool + /** + * @internal This method is not covered by the backward compatibility promise for PHPUnit + */ + public function setDependencyInput(array $dependencyInput) : void { - return \false; + $this->dependencyInput = $dependencyInput; } - public function getNumAssertions() : int + /** + * @internal This method is not covered by the backward compatibility promise for PHPUnit + */ + public function setBeStrictAboutChangesToGlobalState(?bool $beStrictAboutChangesToGlobalState) : void { - return 1; + $this->beStrictAboutChangesToGlobalState = $beStrictAboutChangesToGlobalState; } - public function getActualOutput() : string + /** + * @internal This method is not covered by the backward compatibility promise for PHPUnit + */ + public function setBackupGlobals(?bool $backupGlobals) : void { - return $this->output; + if ($this->backupGlobals === null && $backupGlobals !== null) { + $this->backupGlobals = $backupGlobals; + } } - public function hasOutput() : bool + /** + * @internal This method is not covered by the backward compatibility promise for PHPUnit + */ + public function setBackupStaticAttributes(?bool $backupStaticAttributes) : void { - return !empty($this->output); + if ($this->backupStaticAttributes === null && $backupStaticAttributes !== null) { + $this->backupStaticAttributes = $backupStaticAttributes; + } } - public function sortId() : string + /** + * @internal This method is not covered by the backward compatibility promise for PHPUnit + */ + public function setRunTestInSeparateProcess(bool $runTestInSeparateProcess) : void { - return $this->filename; + if ($this->runTestInSeparateProcess === null) { + $this->runTestInSeparateProcess = $runTestInSeparateProcess; + } } /** - * @return list + * @internal This method is not covered by the backward compatibility promise for PHPUnit */ - public function provides() : array + public function setRunClassInSeparateProcess(bool $runClassInSeparateProcess) : void { - return []; + if ($this->runClassInSeparateProcess === null) { + $this->runClassInSeparateProcess = $runClassInSeparateProcess; + } } /** - * @return list + * @internal This method is not covered by the backward compatibility promise for PHPUnit */ - public function requires() : array + public function setPreserveGlobalState(bool $preserveGlobalState) : void { - return []; + $this->preserveGlobalState = $preserveGlobalState; } /** - * Parse --INI-- section key value pairs and return as array. - * - * @param array|string $content + * @internal This method is not covered by the backward compatibility promise for PHPUnit */ - private function parseIniSection($content, array $ini = []) : array + public function setInIsolation(bool $inIsolation) : void { - 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; + $this->inIsolation = $inIsolation; } - private function parseEnvSection(string $content) : array + /** + * @internal This method is not covered by the backward compatibility promise for PHPUnit + */ + public function isInIsolation() : bool { - $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; + return $this->inIsolation; } /** - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws Exception - * @throws ExpectationFailedException + * @internal This method is not covered by the backward compatibility promise for PHPUnit */ - private function assertPhptExpectation(array $sections, string $output) : void + public function getResult() { - $assertions = ['EXPECT' => 'assertEquals', 'EXPECTF' => 'assertStringMatchesFormat', 'EXPECTREGEX' => 'assertMatchesRegularExpression']; - $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 === '') { - throw new \PHPUnit\Runner\Exception('No PHPT expectation found'); - } - Assert::$sectionAssertion($expected, $actual); - return; - } - } - throw new \PHPUnit\Runner\Exception('No PHPT assertion found'); + return $this->testResult; } /** - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @internal This method is not covered by the backward compatibility promise for PHPUnit */ - private function runSkip(array &$sections, TestResult $result, array $settings) : bool + public function setResult($result) : void { - 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; + $this->testResult = $result; } - private function runClean(array &$sections, bool $collectCoverage) : void + /** + * @internal This method is not covered by the backward compatibility promise for PHPUnit + */ + public function setOutputCallback(callable $callback) : void { - $this->phpUtil->setStdin(''); - $this->phpUtil->setArgs(''); - if (isset($sections['CLEAN'])) { - $cleanCode = $this->render($sections['CLEAN']); - $this->phpUtil->runJob($cleanCode, $this->settings($collectCoverage)); - } + $this->outputCallback = $callback; } /** - * @throws Exception + * @internal This method is not covered by the backward compatibility promise for PHPUnit */ - private function parse() : array + public function getTestResultObject() : ?\PHPUnit\Framework\TestResult { - $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 \PHPUnit\Runner\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 \PHPUnit\Runner\Exception('Invalid PHPT file'); - } - foreach ($unsupportedSections as $section) { - if (isset($sections[$section])) { - throw new \PHPUnit\Runner\Exception("PHPUnit does not support PHPT {$section} sections"); - } - } - return $sections; + return $this->result; } /** - * @throws Exception + * @internal This method is not covered by the backward compatibility promise for PHPUnit */ - private function parseExternal(array &$sections) : void + public function setTestResultObject(\PHPUnit\Framework\TestResult $result) : 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 \PHPUnit\Runner\Exception(sprintf('Could not load --%s-- %s for PHPT file', $section . '_EXTERNAL', $testDirectory . $externalFilename)); - } - $sections[$section] = file_get_contents($testDirectory . $externalFilename); - } - } + $this->result = $result; } - private function validate(array &$sections) : bool + /** + * @internal This method is not covered by the backward compatibility promise for PHPUnit + */ + public function registerMockObject(MockObject $mockObject) : void { - $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; + $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); + } + /** + * @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); } - if (!isset($sections[$section])) { - return \false; + if ($includeData) { + $exporter = new Exporter(); + $buffer .= sprintf(' (%s)', $exporter->shortenedRecursiveExport($this->data)); } } - return \true; + return $buffer; } - private function render(string $code) : string + /** + * 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 str_replace(['__DIR__', '__FILE__'], ["'" . dirname($this->filename) . "'", "'" . $this->filename . "'"], $code); + return $this->data; } - private function getCoverageFiles() : array + /** + * @internal This method is not covered by the backward compatibility promise for PHPUnit + */ + public function addWarning(string $warning) : void { - $baseDir = dirname(realpath($this->filename)) . DIRECTORY_SEPARATOR; - $basename = basename($this->filename, 'phpt'); - return ['coverage' => $baseDir . $basename . 'coverage', 'job' => $baseDir . $basename . 'php']; + $this->warnings[] = $warning; } - private function renderForCoverage(string &$job, bool $pathCoverage, ?string $codeCoverageCacheDirectory) : void + public function sortId() : string { - $files = $this->getCoverageFiles(); - $template = new Template(__DIR__ . '/../Util/PHP/Template/PhptTestCase.tpl'); - $composerAutoload = '\'\''; - if (defined('PHPUNIT_COMPOSER_INSTALL')) { - $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"; + $id = $this->name; + if (strpos($id, '::') === \false) { + $id = static::class . '::' . $id; } - if ($codeCoverageCacheDirectory === null) { - $codeCoverageCacheDirectory = 'null'; - } else { - $codeCoverageCacheDirectory = "'" . $codeCoverageCacheDirectory . "'"; + if ($this->usesDataProvider()) { + $id .= $this->getDataSetAsString(\false); } - $template->setVar(['composerAutoload' => $composerAutoload, 'phar' => $phar, 'globals' => $globals, 'job' => $files['job'], 'coverageFile' => $files['coverage'], 'driverMethod' => $pathCoverage ? 'forLineAndPathCoverage' : 'forLineCoverage', 'codeCoverageCacheDirectory' => $codeCoverageCacheDirectory]); - file_put_contents($files['job'], $job); - $job = $template->render(); + return $id; } - private function cleanupForCoverage() : RawCodeCoverageData + /** + * Returns the normalized test name as class::method. + * + * @return list + */ + public function provides() : array { - $coverage = RawCodeCoverageData::fromXdebugWithoutPathCoverage([]); - $files = $this->getCoverageFiles(); - if (is_file($files['coverage'])) { - $buffer = @file_get_contents($files['coverage']); - if ($buffer !== \false) { - $coverage = @unserialize($buffer); - if ($coverage === \false) { - $coverage = RawCodeCoverageData::fromXdebugWithoutPathCoverage([]); - } - } - } - foreach ($files as $file) { - @unlink($file); - } - return $coverage; + return $this->providedTests; } - private function stringifyIni(array $ini) : array + /** + * Returns a list of normalized dependency names, class::method. + * + * This list can differ from the raw dependencies as the resolver has + * no need for the [!][shallow]clone prefix that is filtered out + * during normalization. + * + * @return list + */ + public function requires() : array { - $settings = []; - foreach ($ini as $key => $value) { - if (is_array($value)) { - foreach ($value as $val) { - $settings[] = $key . '=' . $val; - } - continue; - } - $settings[] = $key . '=' . $value; - } - return $settings; + return $this->dependencies; } - private function getLocationHintFromDiff(string $message, array $sections) : array + /** + * Override to run the test and assert its state. + * + * @throws \SebastianBergmann\ObjectEnumerator\InvalidArgumentException + * @throws AssertionFailedError + * @throws Exception + * @throws ExpectationFailedException + * @throws Throwable + */ + protected function runTest() { - $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 (trim($this->name) === '') { + throw new \PHPUnit\Framework\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 ($block === 'diff') { - if (strpos($line, '+') === 0) { - $needle = $this->getCleanDiffLine($previousLine); - break; - } - if (strpos($line, '-') === 0) { - $needle = $this->getCleanDiffLine($line); - break; + if ($this->expectedException !== null) { + if ($this->expectedException === Error::class) { + $this->assertThat($exception, LogicalOr::fromConstraints(new ExceptionConstraint(Error::class), new ExceptionConstraint(\Error::class))); + } else { + $this->assertThat($exception, new ExceptionConstraint($this->expectedException)); } } - if (!empty($line)) { - $previousLine = $line; + 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; } - return $this->getLocationHint($needle, $sections); + if ($this->expectedException !== null) { + $this->assertThat(null, new ExceptionConstraint($this->expectedException)); + } elseif ($this->expectedExceptionMessage !== null) { + $this->numAssertions++; + throw new \PHPUnit\Framework\AssertionFailedError(sprintf('Failed asserting that exception with message "%s" is thrown', $this->expectedExceptionMessage)); + } elseif ($this->expectedExceptionMessageRegExp !== null) { + $this->numAssertions++; + throw new \PHPUnit\Framework\AssertionFailedError(sprintf('Failed asserting that exception with message matching "%s" is thrown', $this->expectedExceptionMessageRegExp)); + } elseif ($this->expectedExceptionCode !== null) { + $this->numAssertions++; + throw new \PHPUnit\Framework\AssertionFailedError(sprintf('Failed asserting that exception with code "%s" is thrown', $this->expectedExceptionCode)); + } + return $testResult; } - private function getCleanDiffLine(string $line) : string + /** + * 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 { - if (preg_match('/^[\\-+]([\'\\"]?)(.*)\\1$/', $line, $matches)) { - $line = $matches[2]; + $currentValue = ini_set($varName, $newValue); + if ($currentValue !== \false) { + $this->iniSettings[$varName] = $currentValue; + } else { + throw new \PHPUnit\Framework\Exception(sprintf('INI setting "%s" could not be set to "%s".', $varName, $newValue)); } - return $line; } - private function getLocationHint(string $needle, array $sections, ?string $sectionName = null) : array + /** + * 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 { - $needle = trim($needle); - if (empty($needle)) { - return [['file' => realpath($this->filename), 'line' => 1]]; + if (count($args) < 2) { + throw new \PHPUnit\Framework\Exception(); } - if ($sectionName) { - $search = [$sectionName]; - } else { - $search = [ - // 'FILE', - 'EXPECT', - 'EXPECTF', - 'EXPECTREGEX', - ]; + [$category, $locale] = $args; + if (!in_array($category, self::LOCALE_CATEGORIES, \true)) { + throw new \PHPUnit\Framework\Exception(); } - $sectionOffset = null; - 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 (!is_array($locale) && !is_string($locale)) { + throw new \PHPUnit\Framework\Exception(); } - if ($sectionName) { - // String not found in specified section, show user the start of the named section - return [['file' => realpath($this->filename), 'line' => $sectionOffset]]; + $this->locale[$category] = setlocale($category, 0); + $result = setlocale(...$args); + if ($result === \false) { + throw new \PHPUnit\Framework\Exception('The locale functionality is not implemented on your platform, ' . 'the specified locale does not exist or the category name is ' . 'invalid.'); } - // No section specified, show user start of code - return [['file' => realpath($this->filename), 'line' => 1]]; } /** - * @psalm-return list + * Makes configurable stub for the specified class. + * + * @psalm-template RealInstanceType of object + * + * @psalm-param class-string $originalClassName + * + * @psalm-return Stub&RealInstanceType */ - private function settings(bool $collectCoverage) : array + protected function createStub(string $originalClassName) : Stub { - $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', 'open_basedir=', 'output_buffering=Off', 'output_handler=', 'report_memleaks=0', 'report_zend_debug=0']; - if (extension_loaded('pcov')) { - if ($collectCoverage) { - $settings[] = 'pcov.enabled=1'; - } else { - $settings[] = 'pcov.enabled=0'; - } - } - if (extension_loaded('xdebug')) { - if (version_compare(phpversion('xdebug'), '3', '>=')) { - if ($collectCoverage) { - $settings[] = 'xdebug.mode=coverage'; - } else { - $settings[] = 'xdebug.mode=off'; - } - } else { - $settings[] = 'xdebug.default_enable=0'; - if ($collectCoverage) { - $settings[] = 'xdebug.coverage_enable=1'; - } - } - } - return $settings; + return $this->createMockObject($originalClassName); } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Runner; - -use function preg_match; -use function round; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class ResultCacheExtension implements \PHPUnit\Runner\AfterIncompleteTestHook, \PHPUnit\Runner\AfterLastTestHook, \PHPUnit\Runner\AfterRiskyTestHook, \PHPUnit\Runner\AfterSkippedTestHook, \PHPUnit\Runner\AfterSuccessfulTestHook, \PHPUnit\Runner\AfterTestErrorHook, \PHPUnit\Runner\AfterTestFailureHook, \PHPUnit\Runner\AfterTestWarningHook -{ /** - * @var TestResultCache + * Returns a mock object for the specified class. + * + * @psalm-template RealInstanceType of object + * + * @psalm-param class-string $originalClassName + * + * @psalm-return MockObject&RealInstanceType */ - private $cache; - public function __construct(\PHPUnit\Runner\TestResultCache $cache) + protected function createMock(string $originalClassName) : MockObject { - $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, \PHPUnit\Runner\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, \PHPUnit\Runner\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, \PHPUnit\Runner\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, \PHPUnit\Runner\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, \PHPUnit\Runner\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, \PHPUnit\Runner\BaseTestRunner::STATUS_WARNING); - } - public function executeAfterLastTest() : void - { - $this->flush(); + return $this->createMockObject($originalClassName); } /** - * @param string $test A long description format of the current test + * Returns a configured mock object for the specified class. * - * @return string The test name without TestSuiteClassName:: and @dataprovider details + * @psalm-template RealInstanceType of object + * + * @psalm-param class-string $originalClassName + * + * @psalm-return MockObject&RealInstanceType */ - private function getTestName(string $test) : string + protected function createConfiguredMock(string $originalClassName, array $configuration) : MockObject { - $matches = []; - if (preg_match('/^(?\\S+::\\S+)(?:(? with data set (?:#\\d+|"[^"]+"))\\s\\()?/', $test, $matches)) { - $test = $matches['name'] . ($matches['dataname'] ?? ''); + $o = $this->createMockObject($originalClassName); + foreach ($configuration as $method => $return) { + $o->method($method)->willReturn($return); } - return $test; + return $o; } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Runner; - -use function array_diff; -use function array_values; -use function basename; -use function class_exists; -use function get_declared_classes; -use function sprintf; -use function stripos; -use function strlen; -use function substr; -use PHPUnit\Framework\TestCase; -use PHPUnit\Util\FileLoader; -use ReflectionClass; -use ReflectionException; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - * - * @deprecated see https://github.com/sebastianbergmann/phpunit/issues/4039 - */ -final class StandardTestSuiteLoader implements \PHPUnit\Runner\TestSuiteLoader -{ /** - * @throws Exception + * Returns a partial mock object for the specified class. + * + * @param string[] $methods + * + * @psalm-template RealInstanceType of object + * + * @psalm-param class-string $originalClassName + * + * @psalm-return MockObject&RealInstanceType */ - public function load(string $suiteClassFile) : ReflectionClass + protected function createPartialMock(string $originalClassName, array $methods) : MockObject { - $suiteClassName = basename($suiteClassFile, '.php'); - $loadedClasses = get_declared_classes(); - if (!class_exists($suiteClassName, \false)) { - /* @noinspection UnusedFunctionResultInspection */ - FileLoader::checkAndLoad($suiteClassFile); - $loadedClasses = array_values(array_diff(get_declared_classes(), $loadedClasses)); - if (empty($loadedClasses)) { - throw new \PHPUnit\Runner\Exception(sprintf('Class %s could not be found in %s', $suiteClassName, $suiteClassFile)); - } - } - if (!class_exists($suiteClassName, \false)) { - $offset = 0 - strlen($suiteClassName); - foreach ($loadedClasses as $loadedClass) { - // @see https://github.com/sebastianbergmann/phpunit/issues/5020 - if (stripos(substr($loadedClass, $offset - 1), '\\' . $suiteClassName) === 0 || stripos(substr($loadedClass, $offset - 1), '_' . $suiteClassName) === 0) { - $suiteClassName = $loadedClass; - break; - } - } - } - if (!class_exists($suiteClassName, \false)) { - throw new \PHPUnit\Runner\Exception(sprintf('Class %s could not be found in %s', $suiteClassName, $suiteClassFile)); - } try { - $class = new ReflectionClass($suiteClassName); + $reflector = new ReflectionClass($originalClassName); // @codeCoverageIgnoreStart } catch (ReflectionException $e) { - throw new \PHPUnit\Runner\Exception($e->getMessage(), $e->getCode(), $e); + throw new \PHPUnit\Framework\Exception($e->getMessage(), $e->getCode(), $e); } // @codeCoverageIgnoreEnd - if ($class->isSubclassOf(TestCase::class)) { - if ($class->isAbstract()) { - throw new \PHPUnit\Runner\Exception(sprintf('Class %s declared in %s is abstract', $suiteClassName, $suiteClassFile)); - } - return $class; - } - if ($class->hasMethod('suite')) { - try { - $method = $class->getMethod('suite'); - // @codeCoverageIgnoreStart - } catch (ReflectionException $e) { - throw new \PHPUnit\Runner\Exception(sprintf('Method %s::suite() declared in %s is abstract', $suiteClassName, $suiteClassFile)); - } - if (!$method->isPublic()) { - throw new \PHPUnit\Runner\Exception(sprintf('Method %s::suite() declared in %s is not public', $suiteClassName, $suiteClassFile)); - } - if (!$method->isStatic()) { - throw new \PHPUnit\Runner\Exception(sprintf('Method %s::suite() declared in %s is not static', $suiteClassName, $suiteClassFile)); - } + $mockedMethodsThatDontExist = array_filter($methods, static function (string $method) use($reflector) { + return !$reflector->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), $originalClassName)); } - return $class; - } - public function reload(ReflectionClass $aClass) : ReflectionClass - { - return $aClass; + return $this->getMockBuilder($originalClassName)->disableOriginalConstructor()->disableOriginalClone()->disableArgumentCloning()->disallowMockingUnknownTypes()->setMethods(empty($methods) ? null : $methods)->getMock(); } -} - - * - * 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; -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Runner; - -use ReflectionClass; -/** - * @deprecated see https://github.com/sebastianbergmann/phpunit/issues/4039 - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -interface TestSuiteLoader -{ - public function load(string $suiteClassFile) : ReflectionClass; - public function reload(ReflectionClass $aClass) : ReflectionClass; -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Runner; - -use function array_diff; -use function array_merge; -use function array_reverse; -use function array_splice; -use function count; -use function in_array; -use function max; -use function shuffle; -use function usort; -use PHPUnit\Framework\DataProviderTestSuite; -use PHPUnit\Framework\Reorderable; -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'. + * Returns a test proxy for the specified class. * - * @var int + * @psalm-template RealInstanceType of object + * + * @psalm-param class-string $originalClassName + * + * @psalm-return MockObject&RealInstanceType */ - public const ORDER_SIZE = 5; + protected function createTestProxy(string $originalClassName, array $constructorArguments = []) : MockObject + { + return $this->getMockBuilder($originalClassName)->setConstructorArgs($constructorArguments)->enableProxyingToOriginalMethods()->getMock(); + } /** - * List of sorting weights for all test result codes. A higher number gives higher priority. + * Mocks the specified class and returns the name of the mocked class. + * + * @param null|array $methods $methods + * + * @psalm-template RealInstanceType of object + * + * @psalm-param class-string|string $originalClassName + * + * @psalm-return class-string + * + * @deprecated */ - private const DEFECT_SORT_WEIGHT = [\PHPUnit\Runner\BaseTestRunner::STATUS_ERROR => 6, \PHPUnit\Runner\BaseTestRunner::STATUS_FAILURE => 5, \PHPUnit\Runner\BaseTestRunner::STATUS_WARNING => 4, \PHPUnit\Runner\BaseTestRunner::STATUS_INCOMPLETE => 3, \PHPUnit\Runner\BaseTestRunner::STATUS_RISKY => 2, \PHPUnit\Runner\BaseTestRunner::STATUS_SKIPPED => 1, \PHPUnit\Runner\BaseTestRunner::STATUS_UNKNOWN => 0]; - private const SIZE_SORT_WEIGHT = [TestUtil::SMALL => 1, TestUtil::MEDIUM => 2, TestUtil::LARGE => 3, TestUtil::UNKNOWN => 4]; + protected function getMockClass(string $originalClassName, $methods = [], array $arguments = [], string $mockClassName = '', bool $callOriginalConstructor = \false, bool $callOriginalClone = \true, bool $callAutoload = \true, bool $cloneArguments = \false) : string + { + $this->addWarning('PHPUnit\\Framework\\TestCase::getMockClass() is deprecated and will be removed in PHPUnit 10.'); + $this->recordDoubledType($originalClassName); + $mock = $this->getMockObjectGenerator()->getMock($originalClassName, $methods, $arguments, $mockClassName, $callOriginalConstructor, $callOriginalClone, $callAutoload, $cloneArguments); + return get_class($mock); + } /** - * @var array Associative array of (string => DEFECT_SORT_WEIGHT) elements + * 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). + * + * @psalm-template RealInstanceType of object + * + * @psalm-param class-string $originalClassName + * + * @psalm-return MockObject&RealInstanceType */ - private $defectSortOrder = []; + protected function getMockForAbstractClass(string $originalClassName, array $arguments = [], string $mockClassName = '', bool $callOriginalConstructor = \true, bool $callOriginalClone = \true, bool $callAutoload = \true, array $mockedMethods = [], bool $cloneArguments = \false) : MockObject + { + $this->recordDoubledType($originalClassName); + $mockObject = $this->getMockObjectGenerator()->getMockForAbstractClass($originalClassName, $arguments, $mockClassName, $callOriginalConstructor, $callOriginalClone, $callAutoload, $mockedMethods, $cloneArguments); + $this->registerMockObject($mockObject); + return $mockObject; + } /** - * @var TestResultCache + * Returns a mock object based on the given WSDL file. + * + * @psalm-template RealInstanceType of object + * + * @psalm-param class-string|string $originalClassName + * + * @psalm-return MockObject&RealInstanceType */ - private $cache; + protected function getMockFromWsdl(string $wsdlFile, string $originalClassName = '', string $mockClassName = '', array $methods = [], bool $callOriginalConstructor = \true, array $options = []) : MockObject + { + $this->recordDoubledType(SoapClient::class); + if ($originalClassName === '') { + $fileName = pathinfo(basename(parse_url($wsdlFile, PHP_URL_PATH)), PATHINFO_FILENAME); + $originalClassName = preg_replace('/\\W/', '', $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; + } /** - * @var array A list of normalized names of tests before reordering + * 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. + * + * @psalm-param trait-string $traitName */ - private $originalExecutionOrder = []; + protected function getMockForTrait(string $traitName, array $arguments = [], string $mockClassName = '', bool $callOriginalConstructor = \true, bool $callOriginalClone = \true, bool $callAutoload = \true, array $mockedMethods = [], bool $cloneArguments = \false) : MockObject + { + $this->recordDoubledType($traitName); + $mockObject = $this->getMockObjectGenerator()->getMockForTrait($traitName, $arguments, $mockClassName, $callOriginalConstructor, $callOriginalClone, $callAutoload, $mockedMethods, $cloneArguments); + $this->registerMockObject($mockObject); + return $mockObject; + } /** - * @var array A list of normalized names of tests affected by reordering + * Returns an object for the specified trait. + * + * @psalm-param trait-string $traitName */ - private $executionOrder = []; - public function __construct(?\PHPUnit\Runner\TestResultCache $cache = null) + protected function getObjectForTrait(string $traitName, array $arguments = [], string $traitClassName = '', bool $callOriginalConstructor = \true, bool $callOriginalClone = \true, bool $callAutoload = \true) : object { - $this->cache = $cache ?? new \PHPUnit\Runner\NullTestResultCache(); + $this->recordDoubledType($traitName); + return $this->getMockObjectGenerator()->getObjectForTrait($traitName, $traitClassName, $callAutoload, $callOriginalConstructor, $arguments); } /** - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws Exception + * @throws ClassNotFoundException + * @throws DoubleException + * @throws InterfaceNotFoundException + * + * @psalm-param class-string|null $classOrInterface + * + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4141 */ - public function reorderTestsInSuite(Test $suite, int $order, bool $resolveDependencies, int $orderDefects, bool $isRootTestSuite = \true) : void + protected function prophesize(?string $classOrInterface = null) : ObjectProphecy { - $allowedOrders = [self::ORDER_DEFAULT, self::ORDER_REVERSED, self::ORDER_RANDOMIZED, self::ORDER_DURATION, self::ORDER_SIZE]; - if (!in_array($order, $allowedOrders, \true)) { - throw new \PHPUnit\Runner\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 \PHPUnit\Runner\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 (!class_exists(Prophet::class)) { + throw new \PHPUnit\Framework\Exception('This test uses TestCase::prophesize(), but phpspec/prophecy is not installed. Please run "composer require --dev phpspec/prophecy".'); } - if ($isRootTestSuite) { - $this->executionOrder = $this->calculateTestExecutionOrder($suite); + $this->addWarning('PHPUnit\\Framework\\TestCase::prophesize() is deprecated and will be removed in PHPUnit 10. Please use the trait provided by phpspec/prophecy-phpunit.'); + if (is_string($classOrInterface)) { + $this->recordDoubledType($classOrInterface); } + return $this->getProphet()->prophesize($classOrInterface); } - public function getOriginalExecutionOrder() : array + /** + * Creates a default TestResult object. + * + * @internal This method is not covered by the backward compatibility promise for PHPUnit + */ + protected function createResult() : \PHPUnit\Framework\TestResult { - return $this->originalExecutionOrder; + return new \PHPUnit\Framework\TestResult(); } - public function getExecutionOrder() : array + /** + * This method is called when a test method did not execute successfully. + * + * @throws Throwable + */ + protected function onNotSuccessfulTest(Throwable $t) : void { - return $this->executionOrder; + throw $t; } - private function sort(TestSuite $suite, int $order, bool $resolveDependencies, int $orderDefects) : void + protected function recordDoubledType(string $originalClassName) : 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) { - /** @var TestCase[] $tests */ - $tests = $suite->tests(); - $suite->setTests($this->resolveDependencies($tests)); - } + $this->doubledTypes[] = $originalClassName; } /** - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws Throwable */ - private function addSuiteToDefectSortOrder(TestSuite $suite) : void + private function verifyMockObjects() : void { - $max = 0; - foreach ($suite->tests() as $test) { - if (!$test instanceof Reorderable) { - continue; + foreach ($this->mockObjects as $mockObject) { + if ($mockObject->__phpunit_hasMatchers()) { + $this->numAssertions++; } - if (!isset($this->defectSortOrder[$test->sortId()])) { - $this->defectSortOrder[$test->sortId()] = self::DEFECT_SORT_WEIGHT[$this->cache->getState($test->sortId())]; - $max = max($max, $this->defectSortOrder[$test->sortId()]); + $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) { + /* @var MethodProphecy $methodProphecy */ + $this->numAssertions += count($methodProphecy->getCheckedPredictions()); + } + } + } } } - $this->defectSortOrder[$suite->sortId()] = $max; - } - 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 + /** + * @throws SkippedTestError + * @throws SyntheticSkippedError + * @throws Warning + */ + private function checkRequirements() : void { - usort( - $tests, - /** - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - function ($left, $right) { - return $this->cmpDefectPriorityAndTime($left, $right); - } - ); - return $tests; + if (!$this->name || !method_exists($this, $this->name)) { + return; + } + $missingRequirements = TestUtil::getMissingRequirements(static::class, $this->name); + if (!empty($missingRequirements)) { + $this->markTestSkipped(implode(PHP_EOL, $missingRequirements)); + } } - private function sortByDuration(array $tests) : array + private function handleDependencies() : bool { - usort( - $tests, - /** - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - function ($left, $right) { - return $this->cmpDuration($left, $right); + if ([] === $this->dependencies || $this->inIsolation) { + return \true; + } + $passed = $this->result->passed(); + $passedKeys = array_keys($passed); + $numKeys = count($passedKeys); + for ($i = 0; $i < $numKeys; $i++) { + $pos = strpos($passedKeys[$i], ' with data set'); + if ($pos !== \false) { + $passedKeys[$i] = substr($passedKeys[$i], 0, $pos); } - ); - return $tests; + } + $passedKeys = array_flip(array_unique($passedKeys)); + foreach ($this->dependencies as $dependency) { + if (!$dependency->isValid()) { + $this->markSkippedForNotSpecifyingDependency(); + return \false; + } + if ($dependency->targetIsClass()) { + $dependencyClassName = $dependency->getTargetClassName(); + if (array_search($dependencyClassName, $this->result->passedClasses(), \true) === \false) { + $this->markSkippedForMissingDependency($dependency); + return \false; + } + continue; + } + $dependencyTarget = $dependency->getTarget(); + if (!isset($passedKeys[$dependencyTarget])) { + if (!$this->isCallableTestMethod($dependencyTarget)) { + $this->markWarningForUncallableDependency($dependency); + } else { + $this->markSkippedForMissingDependency($dependency); + } + return \false; + } + if (isset($passed[$dependencyTarget])) { + if ($passed[$dependencyTarget]['size'] != TestUtil::UNKNOWN && $this->getSize() != TestUtil::UNKNOWN && $passed[$dependencyTarget]['size'] > $this->getSize()) { + $this->result->addError($this, new \PHPUnit\Framework\SkippedTestError('This test depends on a test that is larger than itself.'), 0); + return \false; + } + if ($dependency->useDeepClone()) { + $deepCopy = new DeepCopy(); + $deepCopy->skipUncloneable(\false); + $this->dependencyInput[$dependencyTarget] = $deepCopy->copy($passed[$dependencyTarget]['result']); + } elseif ($dependency->useShallowClone()) { + $this->dependencyInput[$dependencyTarget] = clone $passed[$dependencyTarget]['result']; + } else { + $this->dependencyInput[$dependencyTarget] = $passed[$dependencyTarget]['result']; + } + } else { + $this->dependencyInput[$dependencyTarget] = null; + } + } + return \true; } - private function sortBySize(array $tests) : array + private function markSkippedForNotSpecifyingDependency() : void { - usort( - $tests, - /** - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - function ($left, $right) { - return $this->cmpSize($left, $right); + $this->status = BaseTestRunner::STATUS_SKIPPED; + $this->result->startTest($this); + $this->result->addError($this, new \PHPUnit\Framework\SkippedTestError('This method has an invalid @depends annotation.'), 0); + $this->result->endTest($this, 0); + } + private function markSkippedForMissingDependency(\PHPUnit\Framework\ExecutionOrderDependency $dependency) : void + { + $this->status = BaseTestRunner::STATUS_SKIPPED; + $this->result->startTest($this); + $this->result->addError($this, new \PHPUnit\Framework\SkippedTestError(sprintf('This test depends on "%s" to pass.', $dependency->getTarget())), 0); + $this->result->endTest($this, 0); + } + private function markWarningForUncallableDependency(\PHPUnit\Framework\ExecutionOrderDependency $dependency) : void + { + $this->status = BaseTestRunner::STATUS_WARNING; + $this->result->startTest($this); + $this->result->addWarning($this, new \PHPUnit\Framework\Warning(sprintf('This test depends on "%s" which does not exist.', $dependency->getTarget())), 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(); } - ); - return $tests; + throw new \PHPUnit\Framework\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); } /** - * 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 + * @throws InvalidArgumentException + * @throws RiskyTestError */ - private function cmpDefectPriorityAndTime(Test $a, Test $b) : int + private function restoreGlobalState() : void { - if (!($a instanceof Reorderable && $b instanceof Reorderable)) { - return 0; + if (!$this->snapshot instanceof Snapshot) { + return; } - $priorityA = $this->defectSortOrder[$a->sortId()] ?? 0; - $priorityB = $this->defectSortOrder[$b->sortId()] ?? 0; - if ($priorityB <=> $priorityA) { - // Sort defect weight descending - return $priorityB <=> $priorityA; + if ($this->beStrictAboutChangesToGlobalState) { + try { + $this->compareGlobalStateSnapshots($this->snapshot, $this->createGlobalStateSnapshot($this->backupGlobals === \true)); + } catch (\PHPUnit\Framework\RiskyTestError $rte) { + // Intentionally left empty + } } - if ($priorityA || $priorityB) { - return $this->cmpDuration($a, $b); + $restorer = new Restorer(); + if ($this->backupGlobals) { + $restorer->restoreGlobalVariables($this->snapshot); } - // do not change execution order - return 0; + if ($this->backupStaticAttributes) { + $restorer->restoreStaticAttributes($this->snapshot); + } + $this->snapshot = null; + if (isset($rte)) { + throw $rte; + } + } + private function createGlobalStateSnapshot(bool $backupGlobals) : Snapshot + { + $excludeList = new ExcludeList(); + foreach ($this->backupGlobalsExcludeList as $globalVariable) { + $excludeList->addGlobalVariable($globalVariable); + } + if (!empty($this->backupGlobalsBlacklist)) { + $this->addWarning('PHPUnit\\Framework\\TestCase::$backupGlobalsBlacklist is deprecated and will be removed in PHPUnit 10. Please use PHPUnit\\Framework\\TestCase::$backupGlobalsExcludeList instead.'); + foreach ($this->backupGlobalsBlacklist as $globalVariable) { + $excludeList->addGlobalVariable($globalVariable); + } + } + if (!defined('PHPUNIT_TESTSUITE')) { + $excludeList->addClassNamePrefix('PHPUnit'); + $excludeList->addClassNamePrefix('PHPUnitPHAR\\SebastianBergmann\\CodeCoverage'); + $excludeList->addClassNamePrefix('PHPUnitPHAR\\SebastianBergmann\\FileIterator'); + $excludeList->addClassNamePrefix('PHPUnitPHAR\\SebastianBergmann\\Invoker'); + $excludeList->addClassNamePrefix('PHPUnitPHAR\\SebastianBergmann\\Template'); + $excludeList->addClassNamePrefix('PHPUnitPHAR\\SebastianBergmann\\Timer'); + $excludeList->addClassNamePrefix('PHPUnitPHAR\\Doctrine\\Instantiator'); + $excludeList->addClassNamePrefix('Prophecy'); + $excludeList->addStaticAttribute(ComparatorFactory::class, 'instance'); + foreach ($this->backupStaticAttributesExcludeList as $class => $attributes) { + foreach ($attributes as $attribute) { + $excludeList->addStaticAttribute($class, $attribute); + } + } + if (!empty($this->backupStaticAttributesBlacklist)) { + $this->addWarning('PHPUnit\\Framework\\TestCase::$backupStaticAttributesBlacklist is deprecated and will be removed in PHPUnit 10. Please use PHPUnit\\Framework\\TestCase::$backupStaticAttributesExcludeList instead.'); + foreach ($this->backupStaticAttributesBlacklist as $class => $attributes) { + foreach ($attributes as $attribute) { + $excludeList->addStaticAttribute($class, $attribute); + } + } + } + } + return new Snapshot($excludeList, $backupGlobals, (bool) $this->backupStaticAttributes, \false, \false, \false, \false, \false, \false, \false); } /** - * Compares test duration for sorting tests by duration ascending. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws InvalidArgumentException + * @throws RiskyTestError */ - private function cmpDuration(Test $a, Test $b) : int + private function compareGlobalStateSnapshots(Snapshot $before, Snapshot $after) : void { - if (!($a instanceof Reorderable && $b instanceof Reorderable)) { - return 0; + $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"); } - return $this->cache->getTime($a->sortId()) <=> $this->cache->getTime($b->sortId()); } /** - * Compares test size for sorting tests small->medium->large->unknown. + * @throws RiskyTestError */ - private function cmpSize(Test $a, Test $b) : int + private function compareGlobalStateSnapshotPart(array $before, array $after, string $header) : void { - $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]; + if ($before != $after) { + $differ = new Differ($header); + $exporter = new Exporter(); + $diff = $differ->diff($exporter->export($before), $exporter->export($after)); + throw new \PHPUnit\Framework\RiskyTestError($diff); + } + } + private function getProphet() : Prophet + { + if ($this->prophet === null) { + $this->prophet = new Prophet(); + } + return $this->prophet; } /** - * 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 $tests - * - * @return array + * @throws \SebastianBergmann\ObjectEnumerator\InvalidArgumentException */ - private function resolveDependencies(array $tests) : array + private function shouldInvocationMockerBeReset(MockObject $mock) : bool { - $newTestOrder = []; - $i = 0; - $provided = []; - do { - if ([] === array_diff($tests[$i]->requires(), $provided)) { - $provided = array_merge($provided, $tests[$i]->provides()); - $newTestOrder = array_merge($newTestOrder, array_splice($tests, $i, 1)); - $i = 0; - } else { - $i++; + $enumerator = new Enumerator(); + foreach ($enumerator->enumerate($this->dependencyInput) as $object) { + if ($mock === $object) { + return \false; } - } while (!empty($tests) && $i < count($tests)); - return array_merge($newTestOrder, $tests); + } + if (!is_array($this->testResult) && !is_object($this->testResult)) { + return \true; + } + return !in_array($mock, $enumerator->enumerate($this->testResult), \true); } /** - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws \SebastianBergmann\ObjectEnumerator\InvalidArgumentException + * @throws \SebastianBergmann\ObjectReflector\InvalidArgumentException + * @throws InvalidArgumentException */ - private function calculateTestExecutionOrder(Test $suite) : array + private function registerMockObjectsFromTestArguments(array $testArguments, array &$visited = []) : void { - $tests = []; - if ($suite instanceof TestSuite) { - foreach ($suite->tests() as $test) { - if (!$test instanceof TestSuite && $test instanceof Reorderable) { - $tests[] = $test->sortId(); - } else { - $tests = array_merge($tests, $this->calculateTestExecutionOrder($test)); + 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) { + $testArgument = Cloner::clone($testArgument); + $this->registerMockObject($testArgument); + } elseif (is_array($testArgument) && !in_array($testArgument, $visited, \true)) { + $visited[] = $testArgument; + $this->registerMockObjectsFromTestArguments($testArgument, $visited); } } } - return $tests; + } + private function setDoesNotPerformAssertionsFromAnnotation() : void + { + $annotations = TestUtil::parseTestMethodAnnotations(static::class, $this->name); + 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 \PHPUnit\Framework\Exception) { + $result = \false; + } + if (is_string($this->expectedException)) { + try { + $reflector = new ReflectionClass($this->expectedException); + // @codeCoverageIgnoreStart + } catch (ReflectionException $e) { + throw new \PHPUnit\Framework\Exception($e->getMessage(), $e->getCode(), $e); + } + // @codeCoverageIgnoreEnd + if ($this->expectedException === 'PHPUnit\\Framework\\Exception' || $this->expectedException === '\\PHPUnit\\Framework\\Exception' || $reflector->isSubclassOf(\PHPUnit\Framework\Exception::class)) { + $result = \true; + } + } + return $result; + } + private function runInSeparateProcess() : bool + { + return ($this->runTestInSeparateProcess || $this->runClassInSeparateProcess) && !$this->inIsolation && !$this instanceof PhptTestCase; + } + 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); + } + /** + * @psalm-template RealInstanceType of object + * + * @psalm-param class-string $originalClassName + * + * @psalm-return MockObject&RealInstanceType + */ + private function createMockObject(string $originalClassName) : MockObject + { + return $this->getMockBuilder($originalClassName)->disableOriginalConstructor()->disableOriginalClone()->disableArgumentCloning()->disallowMockingUnknownTypes()->getMock(); } } toString(); + if ($e instanceof \PHPUnit\Framework\ExpectationFailedException && $e->getComparisonFailure()) { + $buffer .= $e->getComparisonFailure()->getDiff(); + } + if ($e instanceof \PHPUnit\Framework\PHPTAssertionFailedError) { + $buffer .= $e->getDiff(); + } + if (!empty($buffer)) { + $buffer = trim($buffer) . "\n"; + } + return $buffer; } - if (self::$version === '') { - self::$version = (new VersionId('9.6.13', dirname(__DIR__, 2)))->getVersion(); + if ($e instanceof Error) { + return $e->getMessage() . "\n"; } - return self::$version; + if ($e instanceof \PHPUnit\Framework\ExceptionWrapper) { + return $e->getClassName() . ': ' . $e->getMessage() . "\n"; + } + return get_class($e) . ': ' . $e->getMessage() . "\n"; } - public static function series() : string + /** + * Constructs a TestFailure with the given test and exception. + */ + public function __construct(\PHPUnit\Framework\Test $failedTest, Throwable $t) { - if (strpos(self::id(), '-')) { - $version = explode('-', self::id())[0]; + if ($failedTest instanceof \PHPUnit\Framework\SelfDescribing) { + $this->testName = $failedTest->toString(); } else { - $version = self::id(); + $this->testName = get_class($failedTest); } - return implode('.', array_slice(explode('.', $version), 0, 2)); + if (!$failedTest instanceof \PHPUnit\Framework\TestCase || !$failedTest->isInIsolation()) { + $this->failedTest = $failedTest; + } + $this->thrownException = $t; } - public static function getVersionString() : string + /** + * Returns a short description of the failure. + */ + public function toString() : string { - return 'PHPUnit ' . self::id() . ' by Sebastian Bergmann and contributors.'; + return sprintf('%s: %s', $this->testName, $this->thrownException->getMessage()); } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TextUI\CliArguments; - -use function array_map; -use function array_merge; -use function class_exists; -use function explode; -use function is_numeric; -use function str_replace; -use PHPUnit\Runner\TestSuiteSorter; -use PHPUnit\TextUI\DefaultResultPrinter; -use PHPUnit\TextUI\XmlConfiguration\Extension; -use PHPUnit\Util\Log\TeamCity; -use PHPUnit\Util\TestDox\CliTestDoxPrinter; -use PHPUnit\SebastianBergmann\CliParser\Exception as CliParserException; -use PHPUnit\SebastianBergmann\CliParser\Parser as CliParser; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class Builder -{ - private const LONG_OPTIONS = ['atleast-version=', 'prepend=', 'bootstrap=', 'cache-result', 'do-not-cache-result', 'cache-result-file=', 'check-version', 'colors==', 'columns=', 'configuration=', 'coverage-cache=', 'warm-coverage-cache', 'coverage-filter=', 'coverage-clover=', 'coverage-cobertura=', 'coverage-crap4j=', 'coverage-html=', 'coverage-php=', 'coverage-text==', 'coverage-xml=', 'path-coverage', 'debug', 'disallow-test-output', 'disallow-resource-usage', 'disallow-todo-tests', 'default-time-limit=', 'enforce-time-limit', 'exclude-group=', 'extensions=', 'filter=', 'generate-configuration', 'globals-backup', 'group=', 'covers=', 'uses=', 'help', 'resolve-dependencies', 'ignore-dependencies', 'include-path=', 'list-groups', 'list-suites', 'list-tests', 'list-tests-xml=', 'loader=', 'log-junit=', 'log-teamcity=', 'migrate-configuration', 'no-configuration', 'no-coverage', 'no-logging', 'no-interaction', 'no-extensions', 'order-by=', 'printer=', 'process-isolation', 'repeat=', 'dont-report-useless-tests', 'random-order', 'random-order-seed=', 'reverse-order', 'reverse-list', 'static-backup', 'stderr', 'stop-on-defect', 'stop-on-error', 'stop-on-failure', 'stop-on-warning', 'stop-on-incomplete', 'stop-on-risky', 'stop-on-skipped', 'fail-on-empty-test-suite', 'fail-on-incomplete', 'fail-on-risky', 'fail-on-skipped', 'fail-on-warning', 'strict-coverage', 'disable-coverage-ignore', 'strict-global-state', 'teamcity', 'testdox', 'testdox-group=', 'testdox-exclude-group=', 'testdox-html=', 'testdox-text=', 'testdox-xml=', 'test-suffix=', 'testsuite=', 'verbose', 'version', 'whitelist=', 'dump-xdebug-filter=']; - private const SHORT_OPTIONS = 'd:c:hv'; - public function fromParameters(array $parameters, array $additionalLongOptions) : \PHPUnit\TextUI\CliArguments\Configuration + /** + * Returns a description for the thrown exception. + */ + public function getExceptionAsString() : string { - try { - $options = (new CliParser())->parse($parameters, self::SHORT_OPTIONS, array_merge(self::LONG_OPTIONS, $additionalLongOptions)); - } catch (CliParserException $e) { - throw new \PHPUnit\TextUI\CliArguments\Exception($e->getMessage(), $e->getCode(), $e); - } - $argument = null; - $atLeastVersion = null; - $backupGlobals = null; - $backupStaticAttributes = null; - $beStrictAboutChangesToGlobalState = null; - $beStrictAboutResourceUsageDuringSmallTests = null; - $bootstrap = null; - $cacheResult = null; - $cacheResultFile = null; - $checkVersion = null; - $colors = null; - $columns = null; - $configuration = null; - $coverageCacheDirectory = null; - $warmCoverageCache = null; - $coverageFilter = null; - $coverageClover = null; - $coverageCobertura = null; - $coverageCrap4J = null; - $coverageHtml = null; - $coveragePhp = null; - $coverageText = null; - $coverageTextShowUncoveredFiles = null; - $coverageTextShowOnlySummary = null; - $coverageXml = null; - $pathCoverage = null; - $debug = null; - $defaultTimeLimit = null; - $disableCodeCoverageIgnore = null; - $disallowTestOutput = null; - $disallowTodoAnnotatedTests = null; - $enforceTimeLimit = null; - $excludeGroups = null; - $executionOrder = null; - $executionOrderDefects = null; - $extensions = []; - $unavailableExtensions = []; - $failOnEmptyTestSuite = null; - $failOnIncomplete = null; - $failOnRisky = null; - $failOnSkipped = null; - $failOnWarning = null; - $filter = null; - $generateConfiguration = null; - $migrateConfiguration = null; - $groups = null; - $testsCovering = null; - $testsUsing = null; - $help = null; - $includePath = null; - $iniSettings = []; - $junitLogfile = null; - $listGroups = null; - $listSuites = null; - $listTests = null; - $listTestsXml = null; - $loader = null; - $noCoverage = null; - $noExtensions = null; - $noInteraction = null; - $noLogging = null; - $printer = null; - $processIsolation = null; - $randomOrderSeed = null; - $repeat = null; - $reportUselessTests = null; - $resolveDependencies = null; - $reverseList = null; - $stderr = null; - $strictCoverage = null; - $stopOnDefect = null; - $stopOnError = null; - $stopOnFailure = null; - $stopOnIncomplete = null; - $stopOnRisky = null; - $stopOnSkipped = null; - $stopOnWarning = null; - $teamcityLogfile = null; - $testdoxExcludeGroups = null; - $testdoxGroups = null; - $testdoxHtmlFile = null; - $testdoxTextFile = null; - $testdoxXmlFile = null; - $testSuffixes = null; - $testSuite = null; - $unrecognizedOptions = []; - $unrecognizedOrderBy = null; - $useDefaultConfiguration = null; - $verbose = null; - $version = null; - $xdebugFilterFile = null; - if (isset($options[1][0])) { - $argument = $options[1][0]; - } - foreach ($options[0] as $option) { - switch ($option[0]) { - case '--colors': - $colors = $option[1] ?: DefaultResultPrinter::COLOR_AUTO; - break; - case '--bootstrap': - $bootstrap = $option[1]; - break; - case '--cache-result': - $cacheResult = \true; - break; - case '--do-not-cache-result': - $cacheResult = \false; - break; - case '--cache-result-file': - $cacheResultFile = $option[1]; - break; - case '--columns': - if (is_numeric($option[1])) { - $columns = (int) $option[1]; - } elseif ($option[1] === 'max') { - $columns = 'max'; - } - break; - case 'c': - case '--configuration': - $configuration = $option[1]; - break; - case '--coverage-cache': - $coverageCacheDirectory = $option[1]; - break; - case '--warm-coverage-cache': - $warmCoverageCache = \true; - break; - case '--coverage-clover': - $coverageClover = $option[1]; - break; - case '--coverage-cobertura': - $coverageCobertura = $option[1]; - break; - case '--coverage-crap4j': - $coverageCrap4J = $option[1]; - break; - case '--coverage-html': - $coverageHtml = $option[1]; - break; - case '--coverage-php': - $coveragePhp = $option[1]; - break; - case '--coverage-text': - if ($option[1] === null) { - $option[1] = 'php://stdout'; - } - $coverageText = $option[1]; - $coverageTextShowUncoveredFiles = \false; - $coverageTextShowOnlySummary = \false; - break; - case '--coverage-xml': - $coverageXml = $option[1]; - break; - case '--path-coverage': - $pathCoverage = \true; - break; - case 'd': - $tmp = explode('=', $option[1]); - if (isset($tmp[0])) { - if (isset($tmp[1])) { - $iniSettings[$tmp[0]] = $tmp[1]; - } else { - $iniSettings[$tmp[0]] = '1'; - } - } - break; - case '--debug': - $debug = \true; - break; - case 'h': - case '--help': - $help = \true; - break; - case '--filter': - $filter = $option[1]; - break; - case '--testsuite': - $testSuite = $option[1]; - break; - case '--generate-configuration': - $generateConfiguration = \true; - break; - case '--migrate-configuration': - $migrateConfiguration = \true; - break; - case '--group': - $groups = explode(',', $option[1]); - break; - case '--exclude-group': - $excludeGroups = explode(',', $option[1]); - break; - case '--covers': - $testsCovering = array_map('strtolower', explode(',', $option[1])); - break; - case '--uses': - $testsUsing = array_map('strtolower', explode(',', $option[1])); - break; - case '--test-suffix': - $testSuffixes = explode(',', $option[1]); - break; - case '--include-path': - $includePath = $option[1]; - break; - case '--list-groups': - $listGroups = \true; - break; - case '--list-suites': - $listSuites = \true; - break; - case '--list-tests': - $listTests = \true; - break; - case '--list-tests-xml': - $listTestsXml = $option[1]; - break; - case '--printer': - $printer = $option[1]; - break; - case '--loader': - $loader = $option[1]; - break; - case '--log-junit': - $junitLogfile = $option[1]; - break; - case '--log-teamcity': - $teamcityLogfile = $option[1]; - break; - case '--order-by': - foreach (explode(',', $option[1]) as $order) { - switch ($order) { - case 'default': - $executionOrder = TestSuiteSorter::ORDER_DEFAULT; - $executionOrderDefects = TestSuiteSorter::ORDER_DEFAULT; - $resolveDependencies = \true; - break; - case 'defects': - $executionOrderDefects = TestSuiteSorter::ORDER_DEFECTS_FIRST; - break; - case 'depends': - $resolveDependencies = \true; - break; - case 'duration': - $executionOrder = TestSuiteSorter::ORDER_DURATION; - break; - case 'no-depends': - $resolveDependencies = \false; - break; - case 'random': - $executionOrder = TestSuiteSorter::ORDER_RANDOMIZED; - break; - case 'reverse': - $executionOrder = TestSuiteSorter::ORDER_REVERSED; - break; - case 'size': - $executionOrder = TestSuiteSorter::ORDER_SIZE; - break; - default: - $unrecognizedOrderBy = $order; - } - } - break; - case '--process-isolation': - $processIsolation = \true; - break; - case '--repeat': - $repeat = (int) $option[1]; - break; - case '--stderr': - $stderr = \true; - break; - case '--stop-on-defect': - $stopOnDefect = \true; - break; - case '--stop-on-error': - $stopOnError = \true; - break; - case '--stop-on-failure': - $stopOnFailure = \true; - break; - case '--stop-on-warning': - $stopOnWarning = \true; - break; - case '--stop-on-incomplete': - $stopOnIncomplete = \true; - break; - case '--stop-on-risky': - $stopOnRisky = \true; - break; - case '--stop-on-skipped': - $stopOnSkipped = \true; - break; - case '--fail-on-empty-test-suite': - $failOnEmptyTestSuite = \true; - break; - case '--fail-on-incomplete': - $failOnIncomplete = \true; - break; - case '--fail-on-risky': - $failOnRisky = \true; - break; - case '--fail-on-skipped': - $failOnSkipped = \true; - break; - case '--fail-on-warning': - $failOnWarning = \true; - break; - case '--teamcity': - $printer = TeamCity::class; - break; - case '--testdox': - $printer = CliTestDoxPrinter::class; - break; - case '--testdox-group': - $testdoxGroups = explode(',', $option[1]); - break; - case '--testdox-exclude-group': - $testdoxExcludeGroups = explode(',', $option[1]); - break; - case '--testdox-html': - $testdoxHtmlFile = $option[1]; - break; - case '--testdox-text': - $testdoxTextFile = $option[1]; - break; - case '--testdox-xml': - $testdoxXmlFile = $option[1]; - break; - case '--no-configuration': - $useDefaultConfiguration = \false; - break; - case '--extensions': - foreach (explode(',', $option[1]) as $extensionClass) { - if (!class_exists($extensionClass)) { - $unavailableExtensions[] = $extensionClass; - continue; - } - $extensions[] = new Extension($extensionClass, '', []); - } - break; - case '--no-extensions': - $noExtensions = \true; - break; - case '--no-coverage': - $noCoverage = \true; - break; - case '--no-logging': - $noLogging = \true; - break; - case '--no-interaction': - $noInteraction = \true; - break; - case '--globals-backup': - $backupGlobals = \true; - break; - case '--static-backup': - $backupStaticAttributes = \true; - break; - case 'v': - case '--verbose': - $verbose = \true; - break; - case '--atleast-version': - $atLeastVersion = $option[1]; - break; - case '--version': - $version = \true; - break; - case '--dont-report-useless-tests': - $reportUselessTests = \false; - break; - case '--strict-coverage': - $strictCoverage = \true; - break; - case '--disable-coverage-ignore': - $disableCodeCoverageIgnore = \true; - break; - case '--strict-global-state': - $beStrictAboutChangesToGlobalState = \true; - break; - case '--disallow-test-output': - $disallowTestOutput = \true; - break; - case '--disallow-resource-usage': - $beStrictAboutResourceUsageDuringSmallTests = \true; - break; - case '--default-time-limit': - $defaultTimeLimit = (int) $option[1]; - break; - case '--enforce-time-limit': - $enforceTimeLimit = \true; - break; - case '--disallow-todo-tests': - $disallowTodoAnnotatedTests = \true; - break; - case '--reverse-list': - $reverseList = \true; - break; - case '--check-version': - $checkVersion = \true; - break; - case '--coverage-filter': - case '--whitelist': - if ($coverageFilter === null) { - $coverageFilter = []; - } - $coverageFilter[] = $option[1]; - break; - case '--random-order': - $executionOrder = TestSuiteSorter::ORDER_RANDOMIZED; - break; - case '--random-order-seed': - $randomOrderSeed = (int) $option[1]; - break; - case '--resolve-dependencies': - $resolveDependencies = \true; - break; - case '--ignore-dependencies': - $resolveDependencies = \false; - break; - case '--reverse-order': - $executionOrder = TestSuiteSorter::ORDER_REVERSED; - break; - case '--dump-xdebug-filter': - $xdebugFilterFile = $option[1]; - break; - default: - $unrecognizedOptions[str_replace('--', '', $option[0])] = $option[1]; - } - } - if (empty($extensions)) { - $extensions = null; - } - if (empty($unavailableExtensions)) { - $unavailableExtensions = null; - } - if (empty($iniSettings)) { - $iniSettings = null; - } - if (empty($coverageFilter)) { - $coverageFilter = null; - } - return new \PHPUnit\TextUI\CliArguments\Configuration($argument, $atLeastVersion, $backupGlobals, $backupStaticAttributes, $beStrictAboutChangesToGlobalState, $beStrictAboutResourceUsageDuringSmallTests, $bootstrap, $cacheResult, $cacheResultFile, $checkVersion, $colors, $columns, $configuration, $coverageClover, $coverageCobertura, $coverageCrap4J, $coverageHtml, $coveragePhp, $coverageText, $coverageTextShowUncoveredFiles, $coverageTextShowOnlySummary, $coverageXml, $pathCoverage, $coverageCacheDirectory, $warmCoverageCache, $debug, $defaultTimeLimit, $disableCodeCoverageIgnore, $disallowTestOutput, $disallowTodoAnnotatedTests, $enforceTimeLimit, $excludeGroups, $executionOrder, $executionOrderDefects, $extensions, $unavailableExtensions, $failOnEmptyTestSuite, $failOnIncomplete, $failOnRisky, $failOnSkipped, $failOnWarning, $filter, $generateConfiguration, $migrateConfiguration, $groups, $testsCovering, $testsUsing, $help, $includePath, $iniSettings, $junitLogfile, $listGroups, $listSuites, $listTests, $listTestsXml, $loader, $noCoverage, $noExtensions, $noInteraction, $noLogging, $printer, $processIsolation, $randomOrderSeed, $repeat, $reportUselessTests, $resolveDependencies, $reverseList, $stderr, $strictCoverage, $stopOnDefect, $stopOnError, $stopOnFailure, $stopOnIncomplete, $stopOnRisky, $stopOnSkipped, $stopOnWarning, $teamcityLogfile, $testdoxExcludeGroups, $testdoxGroups, $testdoxHtmlFile, $testdoxTextFile, $testdoxXmlFile, $testSuffixes, $testSuite, $unrecognizedOptions, $unrecognizedOrderBy, $useDefaultConfiguration, $verbose, $version, $coverageFilter, $xdebugFilterFile); + 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() : ?\PHPUnit\Framework\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 \PHPUnit\Framework\AssertionFailedError; } } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use Throwable; +/** + * @deprecated The `TestListener` interface is deprecated + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +trait TestListenerDefaultImplementation +{ + public function addError(\PHPUnit\Framework\Test $test, Throwable $t, float $time) : void + { + } + public function addWarning(\PHPUnit\Framework\Test $test, \PHPUnit\Framework\Warning $e, float $time) : void + { + } + public function addFailure(\PHPUnit\Framework\Test $test, \PHPUnit\Framework\AssertionFailedError $e, float $time) : void + { + } + public function addIncompleteTest(\PHPUnit\Framework\Test $test, Throwable $t, float $time) : void + { + } + public function addRiskyTest(\PHPUnit\Framework\Test $test, Throwable $t, float $time) : void + { + } + public function addSkippedTest(\PHPUnit\Framework\Test $test, Throwable $t, float $time) : void + { + } + public function startTestSuite(\PHPUnit\Framework\TestSuite $suite) : void + { + } + public function endTestSuite(\PHPUnit\Framework\TestSuite $suite) : void + { + } + public function startTest(\PHPUnit\Framework\Test $test) : void + { + } + public function endTest(\PHPUnit\Framework\Test $test, float $time) : void + { + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use const PHP_EOL; +use function class_exists; +use function count; +use function extension_loaded; +use function function_exists; +use function get_class; +use function sprintf; +use function xdebug_get_monitored_functions; +use function xdebug_is_debugger_active; +use function xdebug_start_function_monitor; +use function xdebug_stop_function_monitor; +use AssertionError; +use Countable; +use Error; +use PHPUnit\Util\ErrorHandler; +use PHPUnit\Util\ExcludeList; +use PHPUnit\Util\Printer; +use PHPUnit\Util\Test as TestUtil; +use ReflectionClass; +use ReflectionException; +use PHPUnitPHAR\SebastianBergmann\CodeCoverage\CodeCoverage; +use PHPUnitPHAR\SebastianBergmann\CodeCoverage\Exception as OriginalCodeCoverageException; +use PHPUnitPHAR\SebastianBergmann\CodeCoverage\UnintentionallyCoveredCodeException; +use PHPUnitPHAR\SebastianBergmann\Invoker\Invoker; +use PHPUnitPHAR\SebastianBergmann\Invoker\TimeoutException; +use PHPUnitPHAR\SebastianBergmann\RecursionContext\InvalidArgumentException; +use PHPUnitPHAR\SebastianBergmann\ResourceOperations\ResourceOperations; +use PHPUnitPHAR\SebastianBergmann\Timer\Timer; +use Throwable; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class TestResult implements Countable { /** - * @var ?string - */ - private $argument; - /** - * @var ?string - */ - private $atLeastVersion; - /** - * @var ?bool - */ - private $backupGlobals; - /** - * @var ?bool + * @var array */ - private $backupStaticAttributes; + private $passed = []; /** - * @var ?bool + * @var array */ - private $beStrictAboutChangesToGlobalState; + private $passedTestClasses = []; /** - * @var ?bool + * @var bool */ - private $beStrictAboutResourceUsageDuringSmallTests; + private $currentTestSuiteFailed = \false; /** - * @var ?string + * @var TestFailure[] */ - private $bootstrap; + private $errors = []; /** - * @var ?bool + * @var TestFailure[] */ - private $cacheResult; + private $failures = []; /** - * @var ?string + * @var TestFailure[] */ - private $cacheResultFile; + private $warnings = []; /** - * @var ?bool + * @var TestFailure[] */ - private $checkVersion; + private $notImplemented = []; /** - * @var ?string + * @var TestFailure[] */ - private $colors; + private $risky = []; /** - * @var null|int|string + * @var TestFailure[] */ - private $columns; + private $skipped = []; /** - * @var ?string + * @deprecated Use the `TestHook` interfaces instead + * + * @var TestListener[] */ - private $configuration; + private $listeners = []; /** - * @var null|string[] + * @var int */ - private $coverageFilter; + private $runTests = 0; /** - * @var ?string + * @var float */ - private $coverageClover; + private $time = 0; /** - * @var ?string + * Code Coverage information. + * + * @var CodeCoverage */ - private $coverageCobertura; + private $codeCoverage; /** - * @var ?string + * @var bool */ - private $coverageCrap4J; + private $convertDeprecationsToExceptions = \false; /** - * @var ?string + * @var bool */ - private $coverageHtml; + private $convertErrorsToExceptions = \true; /** - * @var ?string + * @var bool */ - private $coveragePhp; + private $convertNoticesToExceptions = \true; /** - * @var ?string + * @var bool */ - private $coverageText; + private $convertWarningsToExceptions = \true; /** - * @var ?bool + * @var bool */ - private $coverageTextShowUncoveredFiles; + private $stop = \false; /** - * @var ?bool + * @var bool */ - private $coverageTextShowOnlySummary; + private $stopOnError = \false; /** - * @var ?string + * @var bool */ - private $coverageXml; + private $stopOnFailure = \false; /** - * @var ?bool + * @var bool */ - private $pathCoverage; + private $stopOnWarning = \false; /** - * @var ?string + * @var bool */ - private $coverageCacheDirectory; + private $beStrictAboutTestsThatDoNotTestAnything = \true; /** - * @var ?bool + * @var bool */ - private $warmCoverageCache; + private $beStrictAboutOutputDuringTests = \false; /** - * @var ?bool + * @var bool */ - private $debug; + private $beStrictAboutTodoAnnotatedTests = \false; /** - * @var ?int + * @var bool */ - private $defaultTimeLimit; + private $beStrictAboutResourceUsageDuringSmallTests = \false; /** - * @var ?bool + * @var bool */ - private $disableCodeCoverageIgnore; + private $enforceTimeLimit = \false; /** - * @var ?bool + * @var bool */ - private $disallowTestOutput; + private $forceCoversAnnotation = \false; /** - * @var ?bool + * @var int */ - private $disallowTodoAnnotatedTests; + private $timeoutForSmallTests = 1; /** - * @var ?bool + * @var int */ - private $enforceTimeLimit; + private $timeoutForMediumTests = 10; /** - * @var null|string[] + * @var int */ - private $excludeGroups; + private $timeoutForLargeTests = 60; /** - * @var ?int + * @var bool */ - private $executionOrder; + private $stopOnRisky = \false; /** - * @var ?int + * @var bool */ - private $executionOrderDefects; + private $stopOnIncomplete = \false; /** - * @var null|Extension[] + * @var bool */ - private $extensions; + private $stopOnSkipped = \false; /** - * @var null|string[] + * @var bool */ - private $unavailableExtensions; + private $lastTestFailed = \false; /** - * @var ?bool + * @var int */ - private $failOnEmptyTestSuite; + private $defaultTimeLimit = 0; /** - * @var ?bool + * @var bool */ - private $failOnIncomplete; + private $stopOnDefect = \false; /** - * @var ?bool + * @var bool */ - private $failOnRisky; + private $registerMockObjectsFromTestArgumentsRecursively = \false; /** - * @var ?bool + * @deprecated Use the `TestHook` interfaces instead + * + * @codeCoverageIgnore + * + * Registers a TestListener. */ - private $failOnSkipped; + public function addListener(\PHPUnit\Framework\TestListener $listener) : void + { + $this->listeners[] = $listener; + } /** - * @var ?bool + * @deprecated Use the `TestHook` interfaces instead + * + * @codeCoverageIgnore + * + * Unregisters a TestListener. */ - private $failOnWarning; + public function removeListener(\PHPUnit\Framework\TestListener $listener) : void + { + foreach ($this->listeners as $key => $_listener) { + if ($listener === $_listener) { + unset($this->listeners[$key]); + } + } + } /** - * @var ?string + * @deprecated Use the `TestHook` interfaces instead + * + * @codeCoverageIgnore + * + * Flushes all flushable TestListeners. */ - private $filter; + public function flushListeners() : void + { + foreach ($this->listeners as $listener) { + if ($listener instanceof Printer) { + $listener->flush(); + } + } + } /** - * @var ?bool + * Adds an error to the list of errors. */ - private $generateConfiguration; + public function addError(\PHPUnit\Framework\Test $test, Throwable $t, float $time) : void + { + if ($t instanceof \PHPUnit\Framework\RiskyTestError) { + $this->recordRisky($test, $t); + $notifyMethod = 'addRiskyTest'; + if ($test instanceof \PHPUnit\Framework\TestCase) { + $test->markAsRisky(); + } + if ($this->stopOnRisky || $this->stopOnDefect) { + $this->stop(); + } + } elseif ($t instanceof \PHPUnit\Framework\IncompleteTest) { + $this->recordNotImplemented($test, $t); + $notifyMethod = 'addIncompleteTest'; + if ($this->stopOnIncomplete) { + $this->stop(); + } + } elseif ($t instanceof \PHPUnit\Framework\SkippedTest) { + $this->recordSkipped($test, $t); + $notifyMethod = 'addSkippedTest'; + if ($this->stopOnSkipped) { + $this->stop(); + } + } else { + $this->recordError($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 \PHPUnit\Framework\ExceptionWrapper($t); + } + foreach ($this->listeners as $listener) { + $listener->{$notifyMethod}($test, $t, $time); + } + $this->lastTestFailed = \true; + $this->time += $time; + } /** - * @var ?bool + * Adds a warning to the list of warnings. + * The passed in exception caused the warning. */ - private $migrateConfiguration; + public function addWarning(\PHPUnit\Framework\Test $test, \PHPUnit\Framework\Warning $e, float $time) : void + { + if ($this->stopOnWarning || $this->stopOnDefect) { + $this->stop(); + } + $this->recordWarning($test, $e); + foreach ($this->listeners as $listener) { + $listener->addWarning($test, $e, $time); + } + $this->time += $time; + } /** - * @var null|string[] + * Adds a failure to the list of failures. + * The passed in exception caused the failure. */ - private $groups; + public function addFailure(\PHPUnit\Framework\Test $test, \PHPUnit\Framework\AssertionFailedError $e, float $time) : void + { + if ($e instanceof \PHPUnit\Framework\RiskyTestError || $e instanceof \PHPUnit\Framework\OutputError) { + $this->recordRisky($test, $e); + $notifyMethod = 'addRiskyTest'; + if ($test instanceof \PHPUnit\Framework\TestCase) { + $test->markAsRisky(); + } + if ($this->stopOnRisky || $this->stopOnDefect) { + $this->stop(); + } + } elseif ($e instanceof \PHPUnit\Framework\IncompleteTest) { + $this->recordNotImplemented($test, $e); + $notifyMethod = 'addIncompleteTest'; + if ($this->stopOnIncomplete) { + $this->stop(); + } + } elseif ($e instanceof \PHPUnit\Framework\SkippedTest) { + $this->recordSkipped($test, $e); + $notifyMethod = 'addSkippedTest'; + if ($this->stopOnSkipped) { + $this->stop(); + } + } else { + $this->failures[] = new \PHPUnit\Framework\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; + } /** - * @var null|string[] + * Informs the result that a test suite will be started. */ - private $testsCovering; + public function startTestSuite(\PHPUnit\Framework\TestSuite $suite) : void + { + $this->currentTestSuiteFailed = \false; + foreach ($this->listeners as $listener) { + $listener->startTestSuite($suite); + } + } /** - * @var null|string[] + * Informs the result that a test suite was completed. */ - private $testsUsing; + public function endTestSuite(\PHPUnit\Framework\TestSuite $suite) : void + { + if (!$this->currentTestSuiteFailed) { + $this->passedTestClasses[] = $suite->getName(); + } + foreach ($this->listeners as $listener) { + $listener->endTestSuite($suite); + } + } /** - * @var ?bool + * Informs the result that a test will be started. */ - private $help; + public function startTest(\PHPUnit\Framework\Test $test) : void + { + $this->lastTestFailed = \false; + $this->runTests += count($test); + foreach ($this->listeners as $listener) { + $listener->startTest($test); + } + } /** - * @var ?string + * Informs the result that a test was completed. + * + * @throws InvalidArgumentException */ - private $includePath; + public function endTest(\PHPUnit\Framework\Test $test, float $time) : void + { + foreach ($this->listeners as $listener) { + $listener->endTest($test, $time); + } + if (!$this->lastTestFailed && $test instanceof \PHPUnit\Framework\TestCase) { + $class = get_class($test); + $key = $class . '::' . $test->getName(); + $this->passed[$key] = ['result' => $test->getResult(), 'size' => TestUtil::getSize($class, $test->getName(\false))]; + $this->time += $time; + } + if ($this->lastTestFailed && $test instanceof \PHPUnit\Framework\TestCase) { + $this->currentTestSuiteFailed = \true; + } + } /** - * @var null|string[] + * Returns true if no risky test occurred. */ - private $iniSettings; + public function allHarmless() : bool + { + return $this->riskyCount() === 0; + } /** - * @var ?string + * Gets the number of risky tests. */ - private $junitLogfile; + public function riskyCount() : int + { + return count($this->risky); + } /** - * @var ?bool + * Returns true if no incomplete test occurred. */ - private $listGroups; + public function allCompletelyImplemented() : bool + { + return $this->notImplementedCount() === 0; + } /** - * @var ?bool + * Gets the number of incomplete tests. */ - private $listSuites; + public function notImplementedCount() : int + { + return count($this->notImplemented); + } /** - * @var ?bool + * Returns an array of TestFailure objects for the risky tests. + * + * @return TestFailure[] */ - private $listTests; + public function risky() : array + { + return $this->risky; + } /** - * @var ?string + * Returns an array of TestFailure objects for the incomplete tests. + * + * @return TestFailure[] */ - private $listTestsXml; + public function notImplemented() : array + { + return $this->notImplemented; + } /** - * @var ?string + * Returns true if no test has been skipped. */ - private $loader; + public function noneSkipped() : bool + { + return $this->skippedCount() === 0; + } /** - * @var ?bool + * Gets the number of skipped tests. */ - private $noCoverage; + public function skippedCount() : int + { + return count($this->skipped); + } /** - * @var ?bool + * Returns an array of TestFailure objects for the skipped tests. + * + * @return TestFailure[] */ - private $noExtensions; - /** - * @var ?bool + public function skipped() : array + { + return $this->skipped; + } + /** + * Gets the number of detected errors. */ - private $noInteraction; + public function errorCount() : int + { + return count($this->errors); + } /** - * @var ?bool + * Returns an array of TestFailure objects for the errors. + * + * @return TestFailure[] */ - private $noLogging; + public function errors() : array + { + return $this->errors; + } /** - * @var ?string + * Gets the number of detected failures. */ - private $printer; + public function failureCount() : int + { + return count($this->failures); + } /** - * @var ?bool + * Returns an array of TestFailure objects for the failures. + * + * @return TestFailure[] */ - private $processIsolation; + public function failures() : array + { + return $this->failures; + } /** - * @var ?int + * Gets the number of detected warnings. */ - private $randomOrderSeed; + public function warningCount() : int + { + return count($this->warnings); + } /** - * @var ?int + * Returns an array of TestFailure objects for the warnings. + * + * @return TestFailure[] */ - private $repeat; + public function warnings() : array + { + return $this->warnings; + } /** - * @var ?bool + * Returns the names of the tests that have passed. */ - private $reportUselessTests; + public function passed() : array + { + return $this->passed; + } /** - * @var ?bool + * Returns the names of the TestSuites that have passed. + * + * This enables @depends-annotations for TestClassName::class */ - private $resolveDependencies; + public function passedClasses() : array + { + return $this->passedTestClasses; + } /** - * @var ?bool + * Returns whether code coverage information should be collected. */ - private $reverseList; + public function getCollectCodeCoverageInformation() : bool + { + return $this->codeCoverage !== null; + } /** - * @var ?bool + * Runs a TestCase. + * + * @throws \SebastianBergmann\CodeCoverage\InvalidArgumentException + * @throws CodeCoverageException + * @throws InvalidArgumentException + * @throws UnintentionallyCoveredCodeException */ - private $stderr; + public function run(\PHPUnit\Framework\Test $test) : void + { + \PHPUnit\Framework\Assert::resetCount(); + $size = TestUtil::UNKNOWN; + if ($test instanceof \PHPUnit\Framework\TestCase) { + $test->setRegisterMockObjectsFromTestArgumentsRecursively($this->registerMockObjectsFromTestArgumentsRecursively); + $isAnyCoverageRequired = TestUtil::requiresCodeCoverageDataCollection($test); + $size = $test->getSize(); + } + $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 \PHPUnit\Framework\ErrorTestCase && !$test instanceof \PHPUnit\Framework\WarningTestCase && $isAnyCoverageRequired; + if ($collectCodeCoverage) { + $this->codeCoverage->start($test); + } + $monitorFunctions = $this->beStrictAboutResourceUsageDuringSmallTests && !$test instanceof \PHPUnit\Framework\ErrorTestCase && !$test instanceof \PHPUnit\Framework\WarningTestCase && $size === TestUtil::SMALL && function_exists('xdebug_start_function_monitor'); + if ($monitorFunctions) { + /* @noinspection ForgottenDebugOutputInspection */ + xdebug_start_function_monitor(ResourceOperations::getFunctions()); + } + $timer = new Timer(); + $timer->start(); + try { + $invoker = new Invoker(); + if (!$test instanceof \PHPUnit\Framework\ErrorTestCase && !$test instanceof \PHPUnit\Framework\WarningTestCase && $this->shouldTimeLimitBeEnforced($size) && $invoker->canInvokeWithTimeout()) { + switch ($size) { + case TestUtil::SMALL: + $_timeout = $this->timeoutForSmallTests; + break; + case TestUtil::MEDIUM: + $_timeout = $this->timeoutForMediumTests; + break; + case TestUtil::LARGE: + $_timeout = $this->timeoutForLargeTests; + break; + default: + $_timeout = $this->defaultTimeLimit; + } + $invoker->invoke([$test, 'runBare'], [], $_timeout); + } else { + $test->runBare(); + } + } catch (TimeoutException $e) { + $this->addFailure($test, new \PHPUnit\Framework\RiskyTestError($e->getMessage()), $_timeout); + $risky = \true; + } catch (\PHPUnit\Framework\AssertionFailedError $e) { + $failure = \true; + if ($e instanceof \PHPUnit\Framework\RiskyTestError) { + $risky = \true; + } elseif ($e instanceof \PHPUnit\Framework\IncompleteTestError) { + $incomplete = \true; + } elseif ($e instanceof \PHPUnit\Framework\SkippedTestError) { + $skipped = \true; + } + } catch (AssertionError $e) { + $test->addToAssertionCount(1); + $failure = \true; + $frame = $e->getTrace()[0]; + $e = new \PHPUnit\Framework\AssertionFailedError(sprintf('%s in %s:%s', $e->getMessage(), $frame['file'] ?? $e->getFile(), $frame['line'] ?? $e->getLine()), 0, $e); + } catch (\PHPUnit\Framework\Warning $e) { + $warning = \true; + } catch (\PHPUnit\Framework\Exception $e) { + $error = \true; + } catch (Throwable $e) { + $e = new \PHPUnit\Framework\ExceptionWrapper($e); + $error = \true; + } + $time = $timer->stop()->asSeconds(); + $test->addToAssertionCount(\PHPUnit\Framework\Assert::getCount()); + if ($monitorFunctions) { + $excludeList = new ExcludeList(); + /** @noinspection ForgottenDebugOutputInspection */ + $functions = xdebug_get_monitored_functions(); + /* @noinspection ForgottenDebugOutputInspection */ + xdebug_stop_function_monitor(); + foreach ($functions as $function) { + if (!$excludeList->isExcluded($function['filename'])) { + $this->addFailure($test, new \PHPUnit\Framework\RiskyTestError(sprintf('%s() used in %s:%s', $function['function'], $function['filename'], $function['lineno'])), $time); + } + } + } + if ($this->beStrictAboutTestsThatDoNotTestAnything && !$test->doesNotPerformAssertions() && $test->getNumAssertions() === 0) { + $risky = \true; + } + if ($this->forceCoversAnnotation && !$error && !$failure && !$warning && !$incomplete && !$skipped && !$risky) { + $annotations = TestUtil::parseTestMethodAnnotations(get_class($test), $test->getName(\false)); + if (!isset($annotations['class']['covers']) && !isset($annotations['method']['covers']) && !isset($annotations['class']['coversNothing']) && !isset($annotations['method']['coversNothing'])) { + $this->addFailure($test, new \PHPUnit\Framework\MissingCoversAnnotationException('This test does not have a @covers annotation but is expected to have one'), $time); + $risky = \true; + } + } + if ($collectCodeCoverage) { + $append = !$risky && !$incomplete && !$skipped; + $linesToBeCovered = []; + $linesToBeUsed = []; + if ($append && $test instanceof \PHPUnit\Framework\TestCase) { + try { + $linesToBeCovered = TestUtil::getLinesToBeCovered(get_class($test), $test->getName(\false)); + $linesToBeUsed = TestUtil::getLinesToBeUsed(get_class($test), $test->getName(\false)); + } catch (\PHPUnit\Framework\InvalidCoversTargetException $cce) { + $this->addWarning($test, new \PHPUnit\Framework\Warning($cce->getMessage()), $time); + } + } + try { + $this->codeCoverage->stop($append, $linesToBeCovered, $linesToBeUsed); + } catch (UnintentionallyCoveredCodeException $cce) { + $unintentionallyCoveredCodeError = new \PHPUnit\Framework\UnintentionallyCoveredCodeError('This test executed code that is not listed as code to be covered or used:' . PHP_EOL . $cce->getMessage()); + } 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 (isset($unintentionallyCoveredCodeError)) { + $this->addFailure($test, $unintentionallyCoveredCodeError, $time); + } elseif ($this->beStrictAboutTestsThatDoNotTestAnything && !$test->doesNotPerformAssertions() && $test->getNumAssertions() === 0) { + try { + $reflected = new ReflectionClass($test); + // @codeCoverageIgnoreStart + } catch (ReflectionException $e) { + throw new \PHPUnit\Framework\Exception($e->getMessage(), $e->getCode(), $e); + } + // @codeCoverageIgnoreEnd + $name = $test->getName(\false); + if ($name && $reflected->hasMethod($name)) { + try { + $reflected = $reflected->getMethod($name); + // @codeCoverageIgnoreStart + } catch (ReflectionException $e) { + throw new \PHPUnit\Framework\Exception($e->getMessage(), $e->getCode(), $e); + } + // @codeCoverageIgnoreEnd + } + $this->addFailure($test, new \PHPUnit\Framework\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 \PHPUnit\Framework\RiskyTestError(sprintf('This test is annotated with "@doesNotPerformAssertions" but performed %d assertions', $test->getNumAssertions())), $time); + } elseif ($this->beStrictAboutOutputDuringTests && $test->hasOutput()) { + $this->addFailure($test, new \PHPUnit\Framework\OutputError(sprintf('This test printed output: %s', $test->getActualOutput())), $time); + } elseif ($this->beStrictAboutTodoAnnotatedTests && $test instanceof \PHPUnit\Framework\TestCase) { + $annotations = TestUtil::parseTestMethodAnnotations(get_class($test), $test->getName(\false)); + if (isset($annotations['method']['todo'])) { + $this->addFailure($test, new \PHPUnit\Framework\RiskyTestError('Test method is annotated with @todo'), $time); + } + } + $this->endTest($test, $time); + } /** - * @var ?bool + * Gets the number of run tests. */ - private $strictCoverage; + public function count() : int + { + return $this->runTests; + } /** - * @var ?bool + * Checks whether the test run should stop. */ - private $stopOnDefect; + public function shouldStop() : bool + { + return $this->stop; + } /** - * @var ?bool + * Marks that the test run should stop. */ - private $stopOnError; + public function stop() : void + { + $this->stop = \true; + } /** - * @var ?bool + * Returns the code coverage object. */ - private $stopOnFailure; + public function getCodeCoverage() : ?CodeCoverage + { + return $this->codeCoverage; + } /** - * @var ?bool + * Sets the code coverage object. */ - private $stopOnIncomplete; + public function setCodeCoverage(CodeCoverage $codeCoverage) : void + { + $this->codeCoverage = $codeCoverage; + } /** - * @var ?bool + * Enables or disables the deprecation-to-exception conversion. */ - private $stopOnRisky; + public function convertDeprecationsToExceptions(bool $flag) : void + { + $this->convertDeprecationsToExceptions = $flag; + } /** - * @var ?bool + * Returns the deprecation-to-exception conversion setting. */ - private $stopOnSkipped; + public function getConvertDeprecationsToExceptions() : bool + { + return $this->convertDeprecationsToExceptions; + } /** - * @var ?bool + * Enables or disables the error-to-exception conversion. */ - private $stopOnWarning; + public function convertErrorsToExceptions(bool $flag) : void + { + $this->convertErrorsToExceptions = $flag; + } /** - * @var ?string + * Returns the error-to-exception conversion setting. */ - private $teamcityLogfile; + public function getConvertErrorsToExceptions() : bool + { + return $this->convertErrorsToExceptions; + } /** - * @var null|string[] + * Enables or disables the notice-to-exception conversion. */ - private $testdoxExcludeGroups; + public function convertNoticesToExceptions(bool $flag) : void + { + $this->convertNoticesToExceptions = $flag; + } /** - * @var null|string[] + * Returns the notice-to-exception conversion setting. */ - private $testdoxGroups; + public function getConvertNoticesToExceptions() : bool + { + return $this->convertNoticesToExceptions; + } /** - * @var ?string + * Enables or disables the warning-to-exception conversion. */ - private $testdoxHtmlFile; + public function convertWarningsToExceptions(bool $flag) : void + { + $this->convertWarningsToExceptions = $flag; + } /** - * @var ?string + * Returns the warning-to-exception conversion setting. */ - private $testdoxTextFile; + public function getConvertWarningsToExceptions() : bool + { + return $this->convertWarningsToExceptions; + } /** - * @var ?string + * Enables or disables the stopping when an error occurs. */ - private $testdoxXmlFile; + public function stopOnError(bool $flag) : void + { + $this->stopOnError = $flag; + } /** - * @var null|string[] + * Enables or disables the stopping when a failure occurs. */ - private $testSuffixes; + public function stopOnFailure(bool $flag) : void + { + $this->stopOnFailure = $flag; + } /** - * @var ?string + * Enables or disables the stopping when a warning occurs. */ - private $testSuite; + 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; + } + public function forceCoversAnnotation() : void + { + $this->forceCoversAnnotation = \true; + } + public function forcesCoversAnnotation() : bool + { + return $this->forceCoversAnnotation; + } /** - * @var string[] + * Enables or disables the stopping for risky tests. */ - private $unrecognizedOptions; + public function stopOnRisky(bool $flag) : void + { + $this->stopOnRisky = $flag; + } /** - * @var ?string + * Enables or disables the stopping for incomplete tests. */ - private $unrecognizedOrderBy; + public function stopOnIncomplete(bool $flag) : void + { + $this->stopOnIncomplete = $flag; + } /** - * @var ?bool + * Enables or disables the stopping for skipped tests. */ - private $useDefaultConfiguration; + public function stopOnSkipped(bool $flag) : void + { + $this->stopOnSkipped = $flag; + } /** - * @var ?bool + * Enables or disables the stopping for defects: error, failure, warning. */ - private $verbose; + public function stopOnDefect(bool $flag) : void + { + $this->stopOnDefect = $flag; + } /** - * @var ?bool + * Returns the time spent running the tests. */ - private $version; + public function time() : float + { + return $this->time; + } /** - * @var ?string + * Returns whether the entire test was successful or not. */ - private $xdebugFilterFile; + public function wasSuccessful() : bool + { + return $this->wasSuccessfulIgnoringWarnings() && empty($this->warnings); + } + public function wasSuccessfulIgnoringWarnings() : bool + { + return empty($this->errors) && empty($this->failures); + } + public function wasSuccessfulAndNoTestIsRiskyOrSkippedOrIncomplete() : bool + { + return $this->wasSuccessful() && $this->allHarmless() && $this->allCompletelyImplemented() && $this->noneSkipped(); + } /** - * @param null|int|string $columns + * Sets the default timeout for tests. */ - public function __construct(?string $argument, ?string $atLeastVersion, ?bool $backupGlobals, ?bool $backupStaticAttributes, ?bool $beStrictAboutChangesToGlobalState, ?bool $beStrictAboutResourceUsageDuringSmallTests, ?string $bootstrap, ?bool $cacheResult, ?string $cacheResultFile, ?bool $checkVersion, ?string $colors, $columns, ?string $configuration, ?string $coverageClover, ?string $coverageCobertura, ?string $coverageCrap4J, ?string $coverageHtml, ?string $coveragePhp, ?string $coverageText, ?bool $coverageTextShowUncoveredFiles, ?bool $coverageTextShowOnlySummary, ?string $coverageXml, ?bool $pathCoverage, ?string $coverageCacheDirectory, ?bool $warmCoverageCache, ?bool $debug, ?int $defaultTimeLimit, ?bool $disableCodeCoverageIgnore, ?bool $disallowTestOutput, ?bool $disallowTodoAnnotatedTests, ?bool $enforceTimeLimit, ?array $excludeGroups, ?int $executionOrder, ?int $executionOrderDefects, ?array $extensions, ?array $unavailableExtensions, ?bool $failOnEmptyTestSuite, ?bool $failOnIncomplete, ?bool $failOnRisky, ?bool $failOnSkipped, ?bool $failOnWarning, ?string $filter, ?bool $generateConfiguration, ?bool $migrateConfiguration, ?array $groups, ?array $testsCovering, ?array $testsUsing, ?bool $help, ?string $includePath, ?array $iniSettings, ?string $junitLogfile, ?bool $listGroups, ?bool $listSuites, ?bool $listTests, ?string $listTestsXml, ?string $loader, ?bool $noCoverage, ?bool $noExtensions, ?bool $noInteraction, ?bool $noLogging, ?string $printer, ?bool $processIsolation, ?int $randomOrderSeed, ?int $repeat, ?bool $reportUselessTests, ?bool $resolveDependencies, ?bool $reverseList, ?bool $stderr, ?bool $strictCoverage, ?bool $stopOnDefect, ?bool $stopOnError, ?bool $stopOnFailure, ?bool $stopOnIncomplete, ?bool $stopOnRisky, ?bool $stopOnSkipped, ?bool $stopOnWarning, ?string $teamcityLogfile, ?array $testdoxExcludeGroups, ?array $testdoxGroups, ?string $testdoxHtmlFile, ?string $testdoxTextFile, ?string $testdoxXmlFile, ?array $testSuffixes, ?string $testSuite, array $unrecognizedOptions, ?string $unrecognizedOrderBy, ?bool $useDefaultConfiguration, ?bool $verbose, ?bool $version, ?array $coverageFilter, ?string $xdebugFilterFile) + public function setDefaultTimeLimit(int $timeout) : void { - $this->argument = $argument; - $this->atLeastVersion = $atLeastVersion; - $this->backupGlobals = $backupGlobals; - $this->backupStaticAttributes = $backupStaticAttributes; - $this->beStrictAboutChangesToGlobalState = $beStrictAboutChangesToGlobalState; - $this->beStrictAboutResourceUsageDuringSmallTests = $beStrictAboutResourceUsageDuringSmallTests; - $this->bootstrap = $bootstrap; - $this->cacheResult = $cacheResult; - $this->cacheResultFile = $cacheResultFile; - $this->checkVersion = $checkVersion; - $this->colors = $colors; - $this->columns = $columns; - $this->configuration = $configuration; - $this->coverageFilter = $coverageFilter; - $this->coverageClover = $coverageClover; - $this->coverageCobertura = $coverageCobertura; - $this->coverageCrap4J = $coverageCrap4J; - $this->coverageHtml = $coverageHtml; - $this->coveragePhp = $coveragePhp; - $this->coverageText = $coverageText; - $this->coverageTextShowUncoveredFiles = $coverageTextShowUncoveredFiles; - $this->coverageTextShowOnlySummary = $coverageTextShowOnlySummary; - $this->coverageXml = $coverageXml; - $this->pathCoverage = $pathCoverage; - $this->coverageCacheDirectory = $coverageCacheDirectory; - $this->warmCoverageCache = $warmCoverageCache; - $this->debug = $debug; - $this->defaultTimeLimit = $defaultTimeLimit; - $this->disableCodeCoverageIgnore = $disableCodeCoverageIgnore; - $this->disallowTestOutput = $disallowTestOutput; - $this->disallowTodoAnnotatedTests = $disallowTodoAnnotatedTests; - $this->enforceTimeLimit = $enforceTimeLimit; - $this->excludeGroups = $excludeGroups; - $this->executionOrder = $executionOrder; - $this->executionOrderDefects = $executionOrderDefects; - $this->extensions = $extensions; - $this->unavailableExtensions = $unavailableExtensions; - $this->failOnEmptyTestSuite = $failOnEmptyTestSuite; - $this->failOnIncomplete = $failOnIncomplete; - $this->failOnRisky = $failOnRisky; - $this->failOnSkipped = $failOnSkipped; - $this->failOnWarning = $failOnWarning; - $this->filter = $filter; - $this->generateConfiguration = $generateConfiguration; - $this->migrateConfiguration = $migrateConfiguration; - $this->groups = $groups; - $this->testsCovering = $testsCovering; - $this->testsUsing = $testsUsing; - $this->help = $help; - $this->includePath = $includePath; - $this->iniSettings = $iniSettings; - $this->junitLogfile = $junitLogfile; - $this->listGroups = $listGroups; - $this->listSuites = $listSuites; - $this->listTests = $listTests; - $this->listTestsXml = $listTestsXml; - $this->loader = $loader; - $this->noCoverage = $noCoverage; - $this->noExtensions = $noExtensions; - $this->noInteraction = $noInteraction; - $this->noLogging = $noLogging; - $this->printer = $printer; - $this->processIsolation = $processIsolation; - $this->randomOrderSeed = $randomOrderSeed; - $this->repeat = $repeat; - $this->reportUselessTests = $reportUselessTests; - $this->resolveDependencies = $resolveDependencies; - $this->reverseList = $reverseList; - $this->stderr = $stderr; - $this->strictCoverage = $strictCoverage; - $this->stopOnDefect = $stopOnDefect; - $this->stopOnError = $stopOnError; - $this->stopOnFailure = $stopOnFailure; - $this->stopOnIncomplete = $stopOnIncomplete; - $this->stopOnRisky = $stopOnRisky; - $this->stopOnSkipped = $stopOnSkipped; - $this->stopOnWarning = $stopOnWarning; - $this->teamcityLogfile = $teamcityLogfile; - $this->testdoxExcludeGroups = $testdoxExcludeGroups; - $this->testdoxGroups = $testdoxGroups; - $this->testdoxHtmlFile = $testdoxHtmlFile; - $this->testdoxTextFile = $testdoxTextFile; - $this->testdoxXmlFile = $testdoxXmlFile; - $this->testSuffixes = $testSuffixes; - $this->testSuite = $testSuite; - $this->unrecognizedOptions = $unrecognizedOptions; - $this->unrecognizedOrderBy = $unrecognizedOrderBy; - $this->useDefaultConfiguration = $useDefaultConfiguration; - $this->verbose = $verbose; - $this->version = $version; - $this->xdebugFilterFile = $xdebugFilterFile; - } - public function hasArgument() : bool - { - return $this->argument !== null; + $this->defaultTimeLimit = $timeout; } /** - * @throws Exception + * Sets the timeout for small tests. */ - public function argument() : string - { - if ($this->argument === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->argument; - } - public function hasAtLeastVersion() : bool + public function setTimeoutForSmallTests(int $timeout) : void { - return $this->atLeastVersion !== null; + $this->timeoutForSmallTests = $timeout; } /** - * @throws Exception + * Sets the timeout for medium tests. */ - public function atLeastVersion() : string - { - if ($this->atLeastVersion === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->atLeastVersion; - } - public function hasBackupGlobals() : bool + public function setTimeoutForMediumTests(int $timeout) : void { - return $this->backupGlobals !== null; + $this->timeoutForMediumTests = $timeout; } /** - * @throws Exception + * Sets the timeout for large tests. */ - public function backupGlobals() : bool - { - if ($this->backupGlobals === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->backupGlobals; - } - public function hasBackupStaticAttributes() : bool + public function setTimeoutForLargeTests(int $timeout) : void { - return $this->backupStaticAttributes !== null; + $this->timeoutForLargeTests = $timeout; } /** - * @throws Exception + * Returns the set timeout for large tests. */ - public function backupStaticAttributes() : bool + public function getTimeoutForLargeTests() : int { - if ($this->backupStaticAttributes === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->backupStaticAttributes; + return $this->timeoutForLargeTests; } - public function hasBeStrictAboutChangesToGlobalState() : bool + public function setRegisterMockObjectsFromTestArgumentsRecursively(bool $flag) : void { - return $this->beStrictAboutChangesToGlobalState !== null; + $this->registerMockObjectsFromTestArgumentsRecursively = $flag; } - /** - * @throws Exception - */ - public function beStrictAboutChangesToGlobalState() : bool + private function recordError(\PHPUnit\Framework\Test $test, Throwable $t) : void { - if ($this->beStrictAboutChangesToGlobalState === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->beStrictAboutChangesToGlobalState; + $this->errors[] = new \PHPUnit\Framework\TestFailure($test, $t); } - public function hasBeStrictAboutResourceUsageDuringSmallTests() : bool + private function recordNotImplemented(\PHPUnit\Framework\Test $test, Throwable $t) : void { - return $this->beStrictAboutResourceUsageDuringSmallTests !== null; + $this->notImplemented[] = new \PHPUnit\Framework\TestFailure($test, $t); } - /** - * @throws Exception - */ - public function beStrictAboutResourceUsageDuringSmallTests() : bool + private function recordRisky(\PHPUnit\Framework\Test $test, Throwable $t) : void { - if ($this->beStrictAboutResourceUsageDuringSmallTests === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->beStrictAboutResourceUsageDuringSmallTests; + $this->risky[] = new \PHPUnit\Framework\TestFailure($test, $t); } - public function hasBootstrap() : bool + private function recordSkipped(\PHPUnit\Framework\Test $test, Throwable $t) : void { - return $this->bootstrap !== null; + $this->skipped[] = new \PHPUnit\Framework\TestFailure($test, $t); } - /** - * @throws Exception - */ - public function bootstrap() : string + private function recordWarning(\PHPUnit\Framework\Test $test, Throwable $t) : void { - if ($this->bootstrap === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->bootstrap; + $this->warnings[] = new \PHPUnit\Framework\TestFailure($test, $t); } - public function hasCacheResult() : bool + private function shouldTimeLimitBeEnforced(int $size) : bool { - return $this->cacheResult !== null; + if (!$this->enforceTimeLimit) { + return \false; + } + if (!($this->defaultTimeLimit || $size !== TestUtil::UNKNOWN)) { + return \false; + } + if (!extension_loaded('pcntl')) { + return \false; + } + if (!class_exists(Invoker::class)) { + return \false; + } + if (extension_loaded('xdebug') && xdebug_is_debugger_active()) { + return \false; + } + return \true; } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use const PHP_EOL; +use function array_keys; +use function array_map; +use function array_merge; +use function array_slice; +use function array_unique; +use function basename; +use function call_user_func; +use function class_exists; +use function count; +use function dirname; +use function get_declared_classes; +use function implode; +use function is_bool; +use function is_callable; +use function is_file; +use function is_object; +use function is_string; +use function method_exists; +use function preg_match; +use function preg_quote; +use function sprintf; +use function strpos; +use function substr; +use Iterator; +use IteratorAggregate; +use PHPUnit\Runner\BaseTestRunner; +use PHPUnit\Runner\Filter\Factory; +use PHPUnit\Runner\PhptTestCase; +use PHPUnit\Util\FileLoader; +use PHPUnit\Util\Reflection; +use PHPUnit\Util\Test as TestUtil; +use ReflectionClass; +use ReflectionException; +use ReflectionMethod; +use PHPUnitPHAR\SebastianBergmann\CodeCoverage\UnintentionallyCoveredCodeException; +use Throwable; +/** + * @template-implements IteratorAggregate + * + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +class TestSuite implements IteratorAggregate, \PHPUnit\Framework\Reorderable, \PHPUnit\Framework\SelfDescribing, \PHPUnit\Framework\Test +{ /** - * @throws Exception + * Enable or disable the backup and restoration of the $GLOBALS array. + * + * @var bool */ - public function cacheResult() : bool - { - if ($this->cacheResult === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->cacheResult; - } - public function hasCacheResultFile() : bool - { - return $this->cacheResultFile !== null; - } + protected $backupGlobals; /** - * @throws Exception + * Enable or disable the backup and restoration of static attributes. + * + * @var bool */ - public function cacheResultFile() : string - { - if ($this->cacheResultFile === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->cacheResultFile; - } - public function hasCheckVersion() : bool - { - return $this->checkVersion !== null; - } + protected $backupStaticAttributes; /** - * @throws Exception + * @var bool */ - public function checkVersion() : bool - { - if ($this->checkVersion === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->checkVersion; - } - public function hasColors() : bool - { - return $this->colors !== null; - } + protected $runTestInSeparateProcess = \false; /** - * @throws Exception + * The name of the test suite. + * + * @var string */ - public function colors() : string - { - if ($this->colors === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->colors; - } - public function hasColumns() : bool - { - return $this->columns !== null; - } + protected $name = ''; /** - * @throws Exception + * The test groups of the test suite. + * + * @psalm-var array> */ - public function columns() - { - if ($this->columns === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->columns; - } - public function hasConfiguration() : bool - { - return $this->configuration !== null; - } + protected $groups = []; /** - * @throws Exception + * The tests in the test suite. + * + * @var Test[] */ - public function configuration() : string - { - if ($this->configuration === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->configuration; - } - public function hasCoverageFilter() : bool - { - return $this->coverageFilter !== null; - } + protected $tests = []; /** - * @throws Exception + * The number of tests in the test suite. + * + * @var int */ - public function coverageFilter() : array - { - if ($this->coverageFilter === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->coverageFilter; - } - public function hasCoverageClover() : bool - { - return $this->coverageClover !== null; - } + protected $numTests = -1; /** - * @throws Exception + * @var bool */ - public function coverageClover() : string - { - if ($this->coverageClover === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->coverageClover; - } - public function hasCoverageCobertura() : bool - { - return $this->coverageCobertura !== null; - } + protected $testCase = \false; /** - * @throws Exception + * @var string[] */ - public function coverageCobertura() : string - { - if ($this->coverageCobertura === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->coverageCobertura; - } - public function hasCoverageCrap4J() : bool - { - return $this->coverageCrap4J !== null; - } + protected $foundClasses = []; /** - * @throws Exception + * @var null|list */ - public function coverageCrap4J() : string - { - if ($this->coverageCrap4J === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->coverageCrap4J; - } - public function hasCoverageHtml() : bool - { - return $this->coverageHtml !== null; - } + protected $providedTests; /** - * @throws Exception + * @var null|list */ - public function coverageHtml() : string - { - if ($this->coverageHtml === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->coverageHtml; - } - public function hasCoveragePhp() : bool - { - return $this->coveragePhp !== null; - } + protected $requiredTests; /** - * @throws Exception + * @var bool */ - public function coveragePhp() : string - { - if ($this->coveragePhp === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->coveragePhp; - } - public function hasCoverageText() : bool - { - return $this->coverageText !== null; - } + private $beStrictAboutChangesToGlobalState; /** - * @throws Exception + * @var Factory */ - public function coverageText() : string - { - if ($this->coverageText === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->coverageText; - } - public function hasCoverageTextShowUncoveredFiles() : bool - { - return $this->coverageTextShowUncoveredFiles !== null; - } + private $iteratorFilter; /** - * @throws Exception + * @var int */ - public function coverageTextShowUncoveredFiles() : bool - { - if ($this->coverageTextShowUncoveredFiles === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->coverageTextShowUncoveredFiles; - } - public function hasCoverageTextShowOnlySummary() : bool - { - return $this->coverageTextShowOnlySummary !== null; - } + private $declaredClassesPointer; /** - * @throws Exception + * @psalm-var array */ - public function coverageTextShowOnlySummary() : bool - { - if ($this->coverageTextShowOnlySummary === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->coverageTextShowOnlySummary; - } - public function hasCoverageXml() : bool - { - return $this->coverageXml !== null; - } + private $warnings = []; /** + * 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 coverageXml() : string + public function __construct($theClass = '', string $name = '') { - if ($this->coverageXml === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); + if (!is_string($theClass) && !$theClass instanceof ReflectionClass) { + throw \PHPUnit\Framework\InvalidArgumentException::create(1, 'ReflectionClass object or string'); } - return $this->coverageXml; + $this->declaredClassesPointer = count(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 \PHPUnit\Framework\Exception($e->getMessage(), $e->getCode(), $e); + } + // @codeCoverageIgnoreEnd + } else { + $this->setName($theClass); + return; + } + } + if (!$theClass->isSubclassOf(\PHPUnit\Framework\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 \PHPUnit\Framework\WarningTestCase(sprintf('Class "%s" has no public constructor.', $theClass->getName()))); + return; + } + foreach ((new Reflection())->publicMethodsInTestClass($theClass) as $method) { + if (!TestUtil::isTestMethod($method)) { + continue; + } + $this->addTestMethod($theClass, $method); + } + if (empty($this->tests)) { + $this->addTest(new \PHPUnit\Framework\WarningTestCase(sprintf('No tests found in class "%s".', $theClass->getName()))); + } + $this->testCase = \true; } - public function hasPathCoverage() : bool + /** + * Returns a string representation of the test suite. + */ + public function toString() : string { - return $this->pathCoverage !== null; + return $this->getName(); } /** - * @throws Exception + * Adds a test to the suite. + * + * @param array $groups */ - public function pathCoverage() : bool + public function addTest(\PHPUnit\Framework\Test $test, $groups = []) : void { - if ($this->pathCoverage === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); + try { + $class = new ReflectionClass($test); + // @codeCoverageIgnoreStart + } catch (ReflectionException $e) { + throw new \PHPUnit\Framework\Exception($e->getMessage(), $e->getCode(), $e); + } + // @codeCoverageIgnoreEnd + if (!$class->isAbstract()) { + $this->tests[] = $test; + $this->clearCaches(); + if ($test instanceof self && empty($groups)) { + $groups = $test->getGroups(); + } + if ($this->containsOnlyVirtualGroups($groups)) { + $groups[] = 'default'; + } + foreach ($groups as $group) { + if (!isset($this->groups[$group])) { + $this->groups[$group] = [$test]; + } else { + $this->groups[$group][] = $test; + } + } + if ($test instanceof \PHPUnit\Framework\TestCase) { + $test->setGroups($groups); + } } - return $this->pathCoverage; - } - public function hasCoverageCacheDirectory() : bool - { - return $this->coverageCacheDirectory !== null; } /** + * Adds the tests from the given class to the suite. + * + * @psalm-param object|class-string $testClass + * * @throws Exception */ - public function coverageCacheDirectory() : string + public function addTestSuite($testClass) : void { - if ($this->coverageCacheDirectory === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); + if (!(is_object($testClass) || is_string($testClass) && class_exists($testClass))) { + throw \PHPUnit\Framework\InvalidArgumentException::create(1, 'class name or object'); + } + if (!is_object($testClass)) { + try { + $testClass = new ReflectionClass($testClass); + // @codeCoverageIgnoreStart + } catch (ReflectionException $e) { + throw new \PHPUnit\Framework\Exception($e->getMessage(), $e->getCode(), $e); + } + // @codeCoverageIgnoreEnd + } + 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); + // @codeCoverageIgnoreStart + } catch (ReflectionException $e) { + throw new \PHPUnit\Framework\Exception($e->getMessage(), $e->getCode(), $e); + } + // @codeCoverageIgnoreEnd + if ($method->isStatic()) { + $this->addTest($method->invoke(null, $testClass->getName())); + $suiteMethod = \true; + } + } + if (!$suiteMethod && !$testClass->isAbstract() && $testClass->isSubclassOf(\PHPUnit\Framework\TestCase::class)) { + $this->addTest(new self($testClass)); + } + } else { + throw new \PHPUnit\Framework\Exception(); } - return $this->coverageCacheDirectory; } - public function hasWarmCoverageCache() : bool + public function addWarning(string $warning) : void { - return $this->warmCoverageCache !== null; + $this->warnings[] = $warning; } /** + * Wraps both addTest() and addTestSuite + * as well as the separate import statements for the user's convenience. + * + * If the named file cannot be read or there are no new tests that can be + * added, a PHPUnit\Framework\WarningTestCase will be created instead, + * leaving the current test run untouched. + * * @throws Exception */ - public function warmCoverageCache() : bool + public function addTestFile(string $filename) : void { - if ($this->warmCoverageCache === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); + if (is_file($filename) && substr($filename, -5) === '.phpt') { + $this->addTest(new PhptTestCase($filename)); + $this->declaredClassesPointer = count(get_declared_classes()); + return; } - return $this->warmCoverageCache; - } - public function hasDebug() : bool - { - return $this->debug !== null; + $numTests = count($this->tests); + // 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_slice(get_declared_classes(), $this->declaredClassesPointer); + // 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->declaredClassesPointer = count(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); + // @codeCoverageIgnoreStart + } catch (ReflectionException $e) { + throw new \PHPUnit\Framework\Exception($e->getMessage(), $e->getCode(), $e); + } + // @codeCoverageIgnoreEnd + if ($class->getFileName() == $filename) { + $newClasses = [$className]; + unset($this->foundClasses[$i]); + break; + } + } + } + foreach ($newClasses as $className) { + try { + $class = new ReflectionClass($className); + // @codeCoverageIgnoreStart + } catch (ReflectionException $e) { + throw new \PHPUnit\Framework\Exception($e->getMessage(), $e->getCode(), $e); + } + // @codeCoverageIgnoreEnd + if (dirname($class->getFileName()) === __DIR__) { + continue; + } + if ($class->isAbstract() && $class->isSubclassOf(\PHPUnit\Framework\TestCase::class)) { + $this->addWarning(sprintf('Abstract test case classes with "Test" suffix are deprecated (%s)', $class->getName())); + } + if (!$class->isAbstract()) { + if ($class->hasMethod(BaseTestRunner::SUITE_METHODNAME)) { + try { + $method = $class->getMethod(BaseTestRunner::SUITE_METHODNAME); + // @codeCoverageIgnoreStart + } catch (ReflectionException $e) { + throw new \PHPUnit\Framework\Exception($e->getMessage(), $e->getCode(), $e); + } + // @codeCoverageIgnoreEnd + if ($method->isStatic()) { + $this->addTest($method->invoke(null, $className)); + } + } elseif ($class->implementsInterface(\PHPUnit\Framework\Test::class)) { + // Do we have modern namespacing ('Foo\Bar\WhizBangTest') or old-school namespacing ('Foo_Bar_WhizBangTest')? + $isPsr0 = !$class->inNamespace() && strpos($class->getName(), '_') !== \false; + $expectedClassName = $isPsr0 ? $className : $shortName; + if (($pos = strpos($expectedClassName, '.')) !== \false) { + $expectedClassName = substr($expectedClassName, 0, $pos); + } + if ($class->getShortName() !== $expectedClassName) { + $this->addWarning(sprintf("Test case class not matching filename is deprecated\n in %s\n Class name was '%s', expected '%s'", $filename, $class->getShortName(), $expectedClassName)); + } + $this->addTestSuite($class); + } + } + } + if (count($this->tests) > ++$numTests) { + $this->addWarning(sprintf("Multiple test case classes per file is deprecated\n in %s", $filename)); + } + $this->numTests = -1; } /** + * Wrapper for addTestFile() that adds multiple test files. + * * @throws Exception */ - public function debug() : bool + public function addTestFiles(iterable $fileNames) : void { - if ($this->debug === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); + foreach ($fileNames as $filename) { + $this->addTestFile((string) $filename); } - return $this->debug; - } - public function hasDefaultTimeLimit() : bool - { - return $this->defaultTimeLimit !== null; } /** - * @throws Exception + * Counts the number of test cases that will be run by this test. + * + * @todo refactor usage of numTests in DefaultResultPrinter */ - public function defaultTimeLimit() : int + public function count() : int { - if ($this->defaultTimeLimit === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); + $this->numTests = 0; + foreach ($this as $test) { + $this->numTests += count($test); } - return $this->defaultTimeLimit; + return $this->numTests; } - public function hasDisableCodeCoverageIgnore() : bool + /** + * Returns the name of the suite. + */ + public function getName() : string { - return $this->disableCodeCoverageIgnore !== null; + return $this->name; } /** - * @throws Exception + * Returns the test groups of the suite. + * + * @psalm-return list */ - public function disableCodeCoverageIgnore() : bool + public function getGroups() : array { - if ($this->disableCodeCoverageIgnore === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->disableCodeCoverageIgnore; + return array_map(static function ($key) : string { + return (string) $key; + }, array_keys($this->groups)); } - public function hasDisallowTestOutput() : bool + public function getGroupDetails() : array { - return $this->disallowTestOutput !== null; + return $this->groups; } /** - * @throws Exception + * Set tests groups of the test case. */ - public function disallowTestOutput() : bool - { - if ($this->disallowTestOutput === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->disallowTestOutput; - } - public function hasDisallowTodoAnnotatedTests() : bool + public function setGroupDetails(array $groups) : void { - return $this->disallowTodoAnnotatedTests !== null; + $this->groups = $groups; } /** - * @throws Exception + * Runs the tests and collects their result in a TestResult. + * + * @throws \SebastianBergmann\CodeCoverage\InvalidArgumentException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws CodeCoverageException + * @throws UnintentionallyCoveredCodeException + * @throws Warning */ - public function disallowTodoAnnotatedTests() : bool + public function run(?\PHPUnit\Framework\TestResult $result = null) : \PHPUnit\Framework\TestResult { - if ($this->disallowTodoAnnotatedTests === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); + if ($result === null) { + $result = $this->createResult(); } - return $this->disallowTodoAnnotatedTests; + if (count($this) === 0) { + return $result; + } + /** @psalm-var class-string $className */ + $className = $this->name; + $hookMethods = TestUtil::getHookMethods($className); + $result->startTestSuite($this); + $test = null; + if ($this->testCase && class_exists($this->name, \false)) { + try { + foreach ($hookMethods['beforeClass'] as $beforeClassMethod) { + if (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 (\PHPUnit\Framework\SkippedTestError|\PHPUnit\Framework\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) { + $errorAdded = \false; + foreach ($this->tests() as $test) { + if ($result->shouldStop()) { + break; + } + $result->startTest($test); + if (!$errorAdded) { + $result->addError($test, $t, 0); + $errorAdded = \true; + } else { + $result->addFailure($test, new \PHPUnit\Framework\SkippedTestError('Test skipped because of an error in hook method'), 0); + } + $result->endTest($test, 0); + } + $result->endTestSuite($this); + return $result; + } + } + foreach ($this as $test) { + if ($result->shouldStop()) { + break; + } + if ($test instanceof \PHPUnit\Framework\TestCase || $test instanceof self) { + $test->setBeStrictAboutChangesToGlobalState($this->beStrictAboutChangesToGlobalState); + $test->setBackupGlobals($this->backupGlobals); + $test->setBackupStaticAttributes($this->backupStaticAttributes); + $test->setRunTestInSeparateProcess($this->runTestInSeparateProcess); + } + $test->run($result); + } + if ($this->testCase && class_exists($this->name, \false)) { + foreach ($hookMethods['afterClass'] as $afterClassMethod) { + if (method_exists($this->name, $afterClassMethod)) { + try { + call_user_func([$this->name, $afterClassMethod]); + } catch (Throwable $t) { + $message = "Exception in {$this->name}::{$afterClassMethod}" . PHP_EOL . $t->getMessage(); + $error = new \PHPUnit\Framework\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 hasEnforceTimeLimit() : bool + public function setRunTestInSeparateProcess(bool $runTestInSeparateProcess) : void { - return $this->enforceTimeLimit !== null; + $this->runTestInSeparateProcess = $runTestInSeparateProcess; } - /** - * @throws Exception - */ - public function enforceTimeLimit() : bool + public function setName(string $name) : void { - if ($this->enforceTimeLimit === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->enforceTimeLimit; + $this->name = $name; } - public function hasExcludeGroups() : bool + /** + * Returns the tests as an enumeration. + * + * @return Test[] + */ + public function tests() : array { - return $this->excludeGroups !== null; + return $this->tests; } /** - * @throws Exception + * Set tests of the test suite. + * + * @param Test[] $tests */ - public function excludeGroups() : array + public function setTests(array $tests) : void { - if ($this->excludeGroups === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->excludeGroups; + $this->tests = $tests; } - public function hasExecutionOrder() : bool + /** + * Mark the test suite as skipped. + * + * @param string $message + * + * @throws SkippedTestSuiteError + * + * @psalm-return never-return + */ + public function markTestSuiteSkipped($message = '') : void { - return $this->executionOrder !== null; + throw new \PHPUnit\Framework\SkippedTestSuiteError($message); } /** - * @throws Exception + * @param bool $beStrictAboutChangesToGlobalState */ - public function executionOrder() : int + public function setBeStrictAboutChangesToGlobalState($beStrictAboutChangesToGlobalState) : void { - if ($this->executionOrder === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); + if (null === $this->beStrictAboutChangesToGlobalState && is_bool($beStrictAboutChangesToGlobalState)) { + $this->beStrictAboutChangesToGlobalState = $beStrictAboutChangesToGlobalState; } - return $this->executionOrder; - } - public function hasExecutionOrderDefects() : bool - { - return $this->executionOrderDefects !== null; } /** - * @throws Exception + * @param bool $backupGlobals */ - public function executionOrderDefects() : int + public function setBackupGlobals($backupGlobals) : void { - if ($this->executionOrderDefects === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); + if (null === $this->backupGlobals && is_bool($backupGlobals)) { + $this->backupGlobals = $backupGlobals; } - return $this->executionOrderDefects; - } - public function hasFailOnEmptyTestSuite() : bool - { - return $this->failOnEmptyTestSuite !== null; } /** - * @throws Exception + * @param bool $backupStaticAttributes */ - public function failOnEmptyTestSuite() : bool + public function setBackupStaticAttributes($backupStaticAttributes) : void { - if ($this->failOnEmptyTestSuite === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); + if (null === $this->backupStaticAttributes && is_bool($backupStaticAttributes)) { + $this->backupStaticAttributes = $backupStaticAttributes; } - return $this->failOnEmptyTestSuite; - } - public function hasFailOnIncomplete() : bool - { - return $this->failOnIncomplete !== null; } /** - * @throws Exception + * Returns an iterator for this test suite. */ - public function failOnIncomplete() : bool + public function getIterator() : Iterator { - if ($this->failOnIncomplete === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); + $iterator = new \PHPUnit\Framework\TestSuiteIterator($this); + if ($this->iteratorFilter !== null) { + $iterator = $this->iteratorFilter->factory($iterator, $this); } - return $this->failOnIncomplete; + return $iterator; } - public function hasFailOnRisky() : bool + public function injectFilter(Factory $filter) : void { - return $this->failOnRisky !== null; + $this->iteratorFilter = $filter; + foreach ($this as $test) { + if ($test instanceof self) { + $test->injectFilter($filter); + } + } } /** - * @throws Exception + * @psalm-return array */ - public function failOnRisky() : bool + public function warnings() : array { - if ($this->failOnRisky === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->failOnRisky; + return array_unique($this->warnings); } - public function hasFailOnSkipped() : bool + /** + * @return list + */ + public function provides() : array { - return $this->failOnSkipped !== null; + if ($this->providedTests === null) { + $this->providedTests = []; + if (is_callable($this->sortId(), \true)) { + $this->providedTests[] = new \PHPUnit\Framework\ExecutionOrderDependency($this->sortId()); + } + foreach ($this->tests as $test) { + if (!$test instanceof \PHPUnit\Framework\Reorderable) { + // @codeCoverageIgnoreStart + continue; + // @codeCoverageIgnoreEnd + } + $this->providedTests = \PHPUnit\Framework\ExecutionOrderDependency::mergeUnique($this->providedTests, $test->provides()); + } + } + return $this->providedTests; } /** - * @throws Exception + * @return list */ - public function failOnSkipped() : bool + public function requires() : array { - if ($this->failOnSkipped === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); + if ($this->requiredTests === null) { + $this->requiredTests = []; + foreach ($this->tests as $test) { + if (!$test instanceof \PHPUnit\Framework\Reorderable) { + // @codeCoverageIgnoreStart + continue; + // @codeCoverageIgnoreEnd + } + $this->requiredTests = \PHPUnit\Framework\ExecutionOrderDependency::mergeUnique(\PHPUnit\Framework\ExecutionOrderDependency::filterInvalid($this->requiredTests), $test->requires()); + } + $this->requiredTests = \PHPUnit\Framework\ExecutionOrderDependency::diff($this->requiredTests, $this->provides()); } - return $this->failOnSkipped; + return $this->requiredTests; } - public function hasFailOnWarning() : bool + public function sortId() : string { - return $this->failOnWarning !== null; + return $this->getName() . '::class'; } /** - * @throws Exception + * Creates a default TestResult object. */ - public function failOnWarning() : bool - { - if ($this->failOnWarning === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->failOnWarning; - } - public function hasFilter() : bool + protected function createResult() : \PHPUnit\Framework\TestResult { - return $this->filter !== null; + return new \PHPUnit\Framework\TestResult(); } /** * @throws Exception */ - public function filter() : string + protected function addTestMethod(ReflectionClass $class, ReflectionMethod $method) : void { - if ($this->filter === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); + $methodName = $method->getName(); + $test = (new \PHPUnit\Framework\TestBuilder())->build($class, $methodName); + if ($test instanceof \PHPUnit\Framework\TestCase || $test instanceof \PHPUnit\Framework\DataProviderTestSuite) { + $test->setDependencies(TestUtil::getDependencies($class->getName(), $methodName)); } - return $this->filter; + $this->addTest($test, TestUtil::getGroups($class->getName(), $methodName)); } - public function hasGenerateConfiguration() : bool + private function clearCaches() : void { - return $this->generateConfiguration !== null; + $this->numTests = -1; + $this->providedTests = null; + $this->requiredTests = null; } - /** - * @throws Exception - */ - public function generateConfiguration() : bool + private function containsOnlyVirtualGroups(array $groups) : bool { - if ($this->generateConfiguration === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); + foreach ($groups as $group) { + if (strpos($group, '__phpunit_') !== 0) { + return \false; + } } - return $this->generateConfiguration; - } - public function hasMigrateConfiguration() : bool - { - return $this->migrateConfiguration !== null; + return \true; } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use function assert; +use function count; +use RecursiveIterator; +/** + * @template-implements RecursiveIterator + * + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class TestSuiteIterator implements RecursiveIterator +{ /** - * @throws Exception + * @var int */ - public function migrateConfiguration() : bool - { - if ($this->migrateConfiguration === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->migrateConfiguration; - } - public function hasGroups() : bool - { - return $this->groups !== null; - } + private $position = 0; /** - * @throws Exception + * @var Test[] */ - public function groups() : array + private $tests; + public function __construct(\PHPUnit\Framework\TestSuite $testSuite) { - if ($this->groups === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->groups; + $this->tests = $testSuite->tests(); } - public function hasTestsCovering() : bool + public function rewind() : void { - return $this->testsCovering !== null; + $this->position = 0; } - /** - * @throws Exception - */ - public function testsCovering() : array + public function valid() : bool { - if ($this->testsCovering === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->testsCovering; + return $this->position < count($this->tests); } - public function hasTestsUsing() : bool + public function key() : int { - return $this->testsUsing !== null; + return $this->position; } - /** - * @throws Exception - */ - public function testsUsing() : array + public function current() : \PHPUnit\Framework\Test { - if ($this->testsUsing === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->testsUsing; + return $this->tests[$this->position]; } - public function hasHelp() : bool + public function next() : void { - return $this->help !== null; + $this->position++; } /** - * @throws Exception + * @throws NoChildTestSuiteException */ - public function help() : bool + public function getChildren() : self { - if ($this->help === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); + if (!$this->hasChildren()) { + throw new \PHPUnit\Framework\NoChildTestSuiteException('The current item is not a TestSuite instance and therefore does not have any children.'); } - return $this->help; + $current = $this->current(); + assert($current instanceof \PHPUnit\Framework\TestSuite); + return new self($current); } - public function hasIncludePath() : bool + public function hasChildren() : bool { - return $this->includePath !== null; + return $this->valid() && $this->current() instanceof \PHPUnit\Framework\TestSuite; } +} + + * + * 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 \PHPUnit\Framework\TestCase +{ /** - * @throws Exception + * @var ?bool */ - public function includePath() : string - { - if ($this->includePath === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->includePath; - } - public function hasIniSettings() : bool - { - return $this->iniSettings !== null; - } + protected $backupGlobals = \false; /** - * @throws Exception + * @var ?bool */ - public function iniSettings() : array - { - if ($this->iniSettings === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->iniSettings; - } - public function hasJunitLogfile() : bool - { - return $this->junitLogfile !== null; - } + protected $backupStaticAttributes = \false; /** - * @throws Exception + * @var ?bool */ - public function junitLogfile() : string - { - if ($this->junitLogfile === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->junitLogfile; - } - public function hasListGroups() : bool - { - return $this->listGroups !== null; - } + protected $runTestInSeparateProcess = \false; /** - * @throws Exception + * @var string */ - public function listGroups() : bool + private $message; + public function __construct(string $message = '') { - if ($this->listGroups === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->listGroups; + $this->message = $message; + parent::__construct('Warning'); } - public function hasListSuites() : bool + public function getMessage() : string { - return $this->listSuites !== null; + return $this->message; } /** - * @throws Exception + * Returns a string representation of the test case. */ - public function listSuites() : bool - { - if ($this->listSuites === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->listSuites; - } - public function hasListTests() : bool + public function toString() : string { - return $this->listTests !== null; + return 'Warning'; } /** * @throws Exception + * + * @psalm-return never-return */ - public function listTests() : bool - { - if ($this->listTests === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->listTests; - } - public function hasListTestsXml() : bool + protected function runTest() : void { - return $this->listTestsXml !== null; + throw new \PHPUnit\Framework\Warning($this->message); } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Runner; + +use function is_dir; +use function is_file; +use function substr; +use PHPUnit\Framework\Exception; +use PHPUnit\Framework\TestSuite; +use ReflectionClass; +use ReflectionException; +use PHPUnitPHAR\SebastianBergmann\FileIterator\Facade as FileIteratorFacade; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +abstract class BaseTestRunner +{ /** - * @throws Exception + * @var int */ - public function listTestsXml() : string - { - if ($this->listTestsXml === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->listTestsXml; - } - public function hasLoader() : bool - { - return $this->loader !== null; - } + public const STATUS_UNKNOWN = -1; /** - * @throws Exception + * @var int */ - public function loader() : string - { - if ($this->loader === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->loader; - } - public function hasNoCoverage() : bool - { - return $this->noCoverage !== null; - } + public const STATUS_PASSED = 0; /** - * @throws Exception + * @var int */ - public function noCoverage() : bool - { - if ($this->noCoverage === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->noCoverage; - } - public function hasNoExtensions() : bool - { - return $this->noExtensions !== null; - } + public const STATUS_SKIPPED = 1; /** - * @throws Exception + * @var int */ - public function noExtensions() : bool - { - if ($this->noExtensions === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->noExtensions; - } - public function hasExtensions() : bool - { - return $this->extensions !== null; - } + public const STATUS_INCOMPLETE = 2; /** - * @throws Exception + * @var int */ - public function extensions() : array - { - if ($this->extensions === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->extensions; - } - public function hasUnavailableExtensions() : bool - { - return $this->unavailableExtensions !== null; - } + public const STATUS_FAILURE = 3; /** - * @throws Exception + * @var int */ - public function unavailableExtensions() : array - { - if ($this->unavailableExtensions === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->unavailableExtensions; - } - public function hasNoInteraction() : bool - { - return $this->noInteraction !== null; - } + public const STATUS_ERROR = 4; /** - * @throws Exception + * @var int */ - public function noInteraction() : bool - { - if ($this->noInteraction === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->noInteraction; - } - public function hasNoLogging() : bool - { - return $this->noLogging !== null; - } + public const STATUS_RISKY = 5; /** - * @throws Exception + * @var int */ - public function noLogging() : bool - { - if ($this->noLogging === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->noLogging; - } - public function hasPrinter() : bool - { - return $this->printer !== null; - } + public const STATUS_WARNING = 6; /** - * @throws Exception + * @var string */ - public function printer() : string - { - if ($this->printer === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->printer; - } - public function hasProcessIsolation() : bool - { - return $this->processIsolation !== null; - } + public const SUITE_METHODNAME = 'suite'; /** - * @throws Exception + * Returns the loader to be used. */ - public function processIsolation() : bool - { - if ($this->processIsolation === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->processIsolation; - } - public function hasRandomOrderSeed() : bool + public function getLoader() : \PHPUnit\Runner\TestSuiteLoader { - return $this->randomOrderSeed !== null; + return new \PHPUnit\Runner\StandardTestSuiteLoader(); } /** + * Returns the Test corresponding to the given suite. + * This is a template method, subclasses override + * the runFailed() and clearStatus() methods. + * + * @param string|string[] $suffixes + * * @throws Exception */ - public function randomOrderSeed() : int + public function getTest(string $suiteClassFile, $suffixes = '') : ?TestSuite { - if ($this->randomOrderSeed === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); + if (is_dir($suiteClassFile)) { + /** @var string[] $files */ + $files = (new FileIteratorFacade())->getFilesAsArray($suiteClassFile, $suffixes); + $suite = new TestSuite($suiteClassFile); + $suite->addTestFiles($files); + return $suite; } - return $this->randomOrderSeed; - } - public function hasRepeat() : bool - { - return $this->repeat !== null; - } - /** - * @throws Exception - */ - public function repeat() : int - { - if ($this->repeat === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); + if (is_file($suiteClassFile) && substr($suiteClassFile, -5, 5) === '.phpt') { + $suite = new TestSuite(); + $suite->addTestFile($suiteClassFile); + return $suite; } - return $this->repeat; - } - public function hasReportUselessTests() : bool - { - return $this->reportUselessTests !== null; - } - /** - * @throws Exception - */ - public function reportUselessTests() : bool - { - if ($this->reportUselessTests === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); + try { + $testClass = $this->loadSuiteClass($suiteClassFile); + } catch (\PHPUnit\Exception $e) { + $this->runFailed($e->getMessage()); + return null; } - return $this->reportUselessTests; - } - public function hasResolveDependencies() : bool - { - return $this->resolveDependencies !== null; - } - /** - * @throws Exception - */ - public function resolveDependencies() : bool - { - if ($this->resolveDependencies === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); + 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) { + $test = new TestSuite($testClass); } - return $this->resolveDependencies; - } - public function hasReverseList() : bool - { - return $this->reverseList !== null; + $this->clearStatus(); + return $test; } /** - * @throws Exception + * Returns the loaded ReflectionClass for a suite name. */ - public function reverseList() : bool - { - if ($this->reverseList === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->reverseList; - } - public function hasStderr() : bool + protected function loadSuiteClass(string $suiteClassFile) : ReflectionClass { - return $this->stderr !== null; + return $this->getLoader()->load($suiteClassFile); } /** - * @throws Exception + * Clears the status message. */ - public function stderr() : bool - { - if ($this->stderr === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->stderr; - } - public function hasStrictCoverage() : bool + protected function clearStatus() : void { - return $this->strictCoverage !== null; } /** - * @throws Exception + * Override to define how to handle a failed loading of + * a test suite. */ - public function strictCoverage() : bool - { - if ($this->strictCoverage === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->strictCoverage; - } - public function hasStopOnDefect() : bool - { - return $this->stopOnDefect !== null; - } - /** - * @throws Exception - */ - public function stopOnDefect() : bool - { - if ($this->stopOnDefect === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->stopOnDefect; - } - public function hasStopOnError() : bool - { - return $this->stopOnError !== null; - } + protected abstract function runFailed(string $message) : void; +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Runner; + +use const DIRECTORY_SEPARATOR; +use const LOCK_EX; +use function assert; +use function dirname; +use function file_get_contents; +use function file_put_contents; +use function in_array; +use function is_array; +use function is_dir; +use function is_file; +use function json_decode; +use function json_encode; +use function sprintf; +use PHPUnit\Util\Filesystem; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class DefaultTestResultCache implements \PHPUnit\Runner\TestResultCache +{ /** - * @throws Exception + * @var int */ - public function stopOnError() : bool - { - if ($this->stopOnError === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->stopOnError; - } - public function hasStopOnFailure() : bool - { - return $this->stopOnFailure !== null; - } + private const VERSION = 1; /** - * @throws Exception + * @psalm-var list */ - public function stopOnFailure() : bool - { - if ($this->stopOnFailure === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->stopOnFailure; - } - public function hasStopOnIncomplete() : bool - { - return $this->stopOnIncomplete !== null; - } + private const ALLOWED_TEST_STATUSES = [\PHPUnit\Runner\BaseTestRunner::STATUS_SKIPPED, \PHPUnit\Runner\BaseTestRunner::STATUS_INCOMPLETE, \PHPUnit\Runner\BaseTestRunner::STATUS_FAILURE, \PHPUnit\Runner\BaseTestRunner::STATUS_ERROR, \PHPUnit\Runner\BaseTestRunner::STATUS_RISKY, \PHPUnit\Runner\BaseTestRunner::STATUS_WARNING]; /** - * @throws Exception + * @var string */ - public function stopOnIncomplete() : bool - { - if ($this->stopOnIncomplete === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->stopOnIncomplete; - } - public function hasStopOnRisky() : bool - { - return $this->stopOnRisky !== null; - } + private const DEFAULT_RESULT_CACHE_FILENAME = '.phpunit.result.cache'; /** - * @throws Exception + * @var string */ - public function stopOnRisky() : bool - { - if ($this->stopOnRisky === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->stopOnRisky; - } - public function hasStopOnSkipped() : bool - { - return $this->stopOnSkipped !== null; - } + private $cacheFilename; /** - * @throws Exception + * @psalm-var array */ - public function stopOnSkipped() : bool - { - if ($this->stopOnSkipped === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->stopOnSkipped; - } - public function hasStopOnWarning() : bool - { - return $this->stopOnWarning !== null; - } + private $defects = []; /** - * @throws Exception + * @psalm-var array */ - public function stopOnWarning() : bool + private $times = []; + public function __construct(?string $filepath = null) { - if ($this->stopOnWarning === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); + if ($filepath !== null && is_dir($filepath)) { + $filepath .= DIRECTORY_SEPARATOR . self::DEFAULT_RESULT_CACHE_FILENAME; } - return $this->stopOnWarning; - } - public function hasTeamcityLogfile() : bool - { - return $this->teamcityLogfile !== null; + $this->cacheFilename = $filepath ?? $_ENV['PHPUNIT_RESULT_CACHE'] ?? self::DEFAULT_RESULT_CACHE_FILENAME; } - /** - * @throws Exception - */ - public function teamcityLogfile() : string + public function setState(string $testName, int $state) : void { - if ($this->teamcityLogfile === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); + if (!in_array($state, self::ALLOWED_TEST_STATUSES, \true)) { + return; } - return $this->teamcityLogfile; + $this->defects[$testName] = $state; } - public function hasTestdoxExcludeGroups() : bool + public function getState(string $testName) : int { - return $this->testdoxExcludeGroups !== null; + return $this->defects[$testName] ?? \PHPUnit\Runner\BaseTestRunner::STATUS_UNKNOWN; } - /** - * @throws Exception - */ - public function testdoxExcludeGroups() : array + public function setTime(string $testName, float $time) : void { - if ($this->testdoxExcludeGroups === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->testdoxExcludeGroups; + $this->times[$testName] = $time; } - public function hasTestdoxGroups() : bool + public function getTime(string $testName) : float { - return $this->testdoxGroups !== null; + return $this->times[$testName] ?? 0.0; } - /** - * @throws Exception - */ - public function testdoxGroups() : array + public function load() : void { - if ($this->testdoxGroups === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); + if (!is_file($this->cacheFilename)) { + return; } - return $this->testdoxGroups; - } - public function hasTestdoxHtmlFile() : bool - { - return $this->testdoxHtmlFile !== null; - } - /** - * @throws Exception - */ - public function testdoxHtmlFile() : string - { - if ($this->testdoxHtmlFile === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); + $data = json_decode(file_get_contents($this->cacheFilename), \true); + if ($data === null) { + return; } - return $this->testdoxHtmlFile; - } - public function hasTestdoxTextFile() : bool - { - return $this->testdoxTextFile !== null; - } - /** - * @throws Exception - */ - public function testdoxTextFile() : string - { - if ($this->testdoxTextFile === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); + if (!isset($data['version'])) { + return; } - return $this->testdoxTextFile; - } - public function hasTestdoxXmlFile() : bool - { - return $this->testdoxXmlFile !== null; - } - /** - * @throws Exception - */ - public function testdoxXmlFile() : string - { - if ($this->testdoxXmlFile === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); + if ($data['version'] !== self::VERSION) { + return; } - return $this->testdoxXmlFile; - } - public function hasTestSuffixes() : bool - { - return $this->testSuffixes !== null; + assert(isset($data['defects']) && is_array($data['defects'])); + assert(isset($data['times']) && is_array($data['times'])); + $this->defects = $data['defects']; + $this->times = $data['times']; } /** * @throws Exception */ - public function testSuffixes() : array + public function persist() : void { - if ($this->testSuffixes === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); + if (!Filesystem::createDirectory(dirname($this->cacheFilename))) { + throw new \PHPUnit\Runner\Exception(sprintf('Cannot create directory "%s" for result cache file', $this->cacheFilename)); } - return $this->testSuffixes; - } - public function hasTestSuite() : bool - { - return $this->testSuite !== null; + file_put_contents($this->cacheFilename, json_encode(['version' => self::VERSION, 'defects' => $this->defects, 'times' => $this->times]), LOCK_EX); } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Runner; + +use RuntimeException; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class Exception extends RuntimeException implements \PHPUnit\Exception +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Runner\Extension; + +use function class_exists; +use function sprintf; +use PHPUnit\Framework\TestListener; +use PHPUnit\Runner\Exception; +use PHPUnit\Runner\Hook; +use PHPUnit\TextUI\TestRunner; +use PHPUnit\TextUI\XmlConfiguration\Extension; +use ReflectionClass; +use ReflectionException; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class ExtensionHandler +{ /** * @throws Exception */ - public function testSuite() : string + public function registerExtension(Extension $extensionConfiguration, TestRunner $runner) : void { - if ($this->testSuite === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); + $extension = $this->createInstance($extensionConfiguration); + if (!$extension instanceof Hook) { + throw new Exception(sprintf('Class "%s" does not implement a PHPUnit\\Runner\\Hook interface', $extensionConfiguration->className())); } - return $this->testSuite; - } - public function unrecognizedOptions() : array - { - return $this->unrecognizedOptions; - } - public function hasUnrecognizedOrderBy() : bool - { - return $this->unrecognizedOrderBy !== null; + $runner->addExtension($extension); } /** * @throws Exception + * + * @deprecated */ - public function unrecognizedOrderBy() : string + public function createTestListenerInstance(Extension $listenerConfiguration) : TestListener { - if ($this->unrecognizedOrderBy === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); + $listener = $this->createInstance($listenerConfiguration); + if (!$listener instanceof TestListener) { + throw new Exception(sprintf('Class "%s" does not implement the PHPUnit\\Framework\\TestListener interface', $listenerConfiguration->className())); } - return $this->unrecognizedOrderBy; - } - public function hasUseDefaultConfiguration() : bool - { - return $this->useDefaultConfiguration !== null; + return $listener; } /** * @throws Exception */ - public function useDefaultConfiguration() : bool + private function createInstance(Extension $extensionConfiguration) : object { - if ($this->useDefaultConfiguration === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); + $this->ensureClassExists($extensionConfiguration); + try { + $reflector = new ReflectionClass($extensionConfiguration->className()); + } catch (ReflectionException $e) { + throw new Exception($e->getMessage(), $e->getCode(), $e); } - return $this->useDefaultConfiguration; - } - public function hasVerbose() : bool - { - return $this->verbose !== null; + if (!$extensionConfiguration->hasArguments()) { + return $reflector->newInstance(); + } + return $reflector->newInstanceArgs($extensionConfiguration->arguments()); } /** * @throws Exception */ - public function verbose() : bool + private function ensureClassExists(Extension $extensionConfiguration) : void { - if ($this->verbose === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); + if (class_exists($extensionConfiguration->className(), \false)) { + return; + } + if ($extensionConfiguration->hasSourceFile()) { + /** + * @noinspection PhpIncludeInspection + * + * @psalm-suppress UnresolvableInclude + */ + require_once $extensionConfiguration->sourceFile(); + } + if (!class_exists($extensionConfiguration->className())) { + throw new Exception(sprintf('Class "%s" does not exist', $extensionConfiguration->className())); } - return $this->verbose; - } - public function hasVersion() : bool - { - return $this->version !== null; } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Runner\Extension; + +use function count; +use function explode; +use function implode; +use function is_file; +use function strpos; +use PHPUnitPHAR\PharIo\Manifest\ApplicationName; +use PHPUnitPHAR\PharIo\Manifest\Exception as ManifestException; +use PHPUnitPHAR\PharIo\Manifest\ManifestLoader; +use PHPUnitPHAR\PharIo\Version\Version as PharIoVersion; +use PHPUnit\Runner\Version; +use PHPUnitPHAR\SebastianBergmann\FileIterator\Facade as FileIteratorFacade; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class PharLoader +{ /** - * @throws Exception + * @psalm-return array{loadedExtensions: list, notLoadedExtensions: list} */ - public function version() : bool + public function loadPharExtensionsInDirectory(string $directory) : array { - if ($this->version === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); + $loadedExtensions = []; + $notLoadedExtensions = []; + foreach ((new FileIteratorFacade())->getFilesAsArray($directory, '.phar') as $file) { + if (!is_file('phar://' . $file . '/manifest.xml')) { + $notLoadedExtensions[] = $file . ' is not an extension for PHPUnit'; + continue; + } + try { + $applicationName = new ApplicationName('phpunit/phpunit'); + $version = new PharIoVersion($this->phpunitVersion()); + $manifest = ManifestLoader::fromFile('phar://' . $file . '/manifest.xml'); + if (!$manifest->isExtensionFor($applicationName)) { + $notLoadedExtensions[] = $file . ' is not an extension for PHPUnit'; + continue; + } + if (!$manifest->isExtensionFor($applicationName, $version)) { + $notLoadedExtensions[] = $file . ' is not compatible with this version of PHPUnit'; + continue; + } + } catch (ManifestException $e) { + $notLoadedExtensions[] = $file . ': ' . $e->getMessage(); + continue; + } + /** + * @noinspection PhpIncludeInspection + * + * @psalm-suppress UnresolvableInclude + */ + require $file; + $loadedExtensions[] = $manifest->getName()->asString() . ' ' . $manifest->getVersion()->getVersionString(); } - return $this->version; - } - public function hasXdebugFilterFile() : bool - { - return $this->xdebugFilterFile !== null; + return ['loadedExtensions' => $loadedExtensions, 'notLoadedExtensions' => $notLoadedExtensions]; } - /** - * @throws Exception - */ - public function xdebugFilterFile() : string + private function phpunitVersion() : string { - if ($this->xdebugFilterFile === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); + $version = Version::id(); + if (strpos($version, '-') === \false) { + return $version; } - return $this->xdebugFilterFile; + $parts = explode('.', explode('-', $version)[0]); + if (count($parts) === 2) { + $parts[] = 0; + } + return implode('.', $parts); } } groupTests, \true); + } } + */ + private $filters = []; + /** + * @param array|string $args + * * @throws Exception */ - public function mapToLegacyArray(\PHPUnit\TextUI\CliArguments\Configuration $arguments) : array + public function addFilter(ReflectionClass $filter, $args) : void { - $result = ['extensions' => [], 'listGroups' => \false, 'listSuites' => \false, 'listTests' => \false, 'listTestsXml' => \false, 'loader' => null, 'useDefaultConfiguration' => \true, 'loadedExtensions' => [], 'unavailableExtensions' => [], 'notLoadedExtensions' => []]; - if ($arguments->hasColors()) { - $result['colors'] = $arguments->colors(); - } - if ($arguments->hasBootstrap()) { - $result['bootstrap'] = $arguments->bootstrap(); - } - if ($arguments->hasCacheResult()) { - $result['cacheResult'] = $arguments->cacheResult(); - } - if ($arguments->hasCacheResultFile()) { - $result['cacheResultFile'] = $arguments->cacheResultFile(); - } - if ($arguments->hasColumns()) { - $result['columns'] = $arguments->columns(); - } - if ($arguments->hasConfiguration()) { - $result['configuration'] = $arguments->configuration(); - } - if ($arguments->hasCoverageCacheDirectory()) { - $result['coverageCacheDirectory'] = $arguments->coverageCacheDirectory(); - } - if ($arguments->hasWarmCoverageCache()) { - $result['warmCoverageCache'] = $arguments->warmCoverageCache(); - } - if ($arguments->hasCoverageClover()) { - $result['coverageClover'] = $arguments->coverageClover(); - } - if ($arguments->hasCoverageCobertura()) { - $result['coverageCobertura'] = $arguments->coverageCobertura(); + if (!$filter->isSubclassOf(RecursiveFilterIterator::class)) { + throw new Exception(sprintf('Class "%s" does not extend RecursiveFilterIterator', $filter->name)); } - if ($arguments->hasCoverageCrap4J()) { - $result['coverageCrap4J'] = $arguments->coverageCrap4J(); + $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); } - if ($arguments->hasCoverageHtml()) { - $result['coverageHtml'] = $arguments->coverageHtml(); - } - if ($arguments->hasCoveragePhp()) { - $result['coveragePHP'] = $arguments->coveragePhp(); - } - if ($arguments->hasCoverageText()) { - $result['coverageText'] = $arguments->coverageText(); - } - if ($arguments->hasCoverageTextShowUncoveredFiles()) { - $result['coverageTextShowUncoveredFiles'] = $arguments->hasCoverageTextShowUncoveredFiles(); - } - if ($arguments->hasCoverageTextShowOnlySummary()) { - $result['coverageTextShowOnlySummary'] = $arguments->coverageTextShowOnlySummary(); - } - if ($arguments->hasCoverageXml()) { - $result['coverageXml'] = $arguments->coverageXml(); - } - if ($arguments->hasPathCoverage()) { - $result['pathCoverage'] = $arguments->pathCoverage(); - } - if ($arguments->hasDebug()) { - $result['debug'] = $arguments->debug(); - } - if ($arguments->hasHelp()) { - $result['help'] = $arguments->help(); - } - if ($arguments->hasFilter()) { - $result['filter'] = $arguments->filter(); - } - if ($arguments->hasTestSuite()) { - $result['testsuite'] = $arguments->testSuite(); - } - if ($arguments->hasGroups()) { - $result['groups'] = $arguments->groups(); - } - if ($arguments->hasExcludeGroups()) { - $result['excludeGroups'] = $arguments->excludeGroups(); - } - if ($arguments->hasTestsCovering()) { - $result['testsCovering'] = $arguments->testsCovering(); - } - if ($arguments->hasTestsUsing()) { - $result['testsUsing'] = $arguments->testsUsing(); - } - if ($arguments->hasTestSuffixes()) { - $result['testSuffixes'] = $arguments->testSuffixes(); - } - if ($arguments->hasIncludePath()) { - $result['includePath'] = $arguments->includePath(); - } - if ($arguments->hasListGroups()) { - $result['listGroups'] = $arguments->listGroups(); - } - if ($arguments->hasListSuites()) { - $result['listSuites'] = $arguments->listSuites(); - } - if ($arguments->hasListTests()) { - $result['listTests'] = $arguments->listTests(); - } - if ($arguments->hasListTestsXml()) { - $result['listTestsXml'] = $arguments->listTestsXml(); - } - if ($arguments->hasPrinter()) { - $result['printer'] = $arguments->printer(); - } - if ($arguments->hasLoader()) { - $result['loader'] = $arguments->loader(); - } - if ($arguments->hasJunitLogfile()) { - $result['junitLogfile'] = $arguments->junitLogfile(); - } - if ($arguments->hasTeamcityLogfile()) { - $result['teamcityLogfile'] = $arguments->teamcityLogfile(); - } - if ($arguments->hasExecutionOrder()) { - $result['executionOrder'] = $arguments->executionOrder(); - } - if ($arguments->hasExecutionOrderDefects()) { - $result['executionOrderDefects'] = $arguments->executionOrderDefects(); - } - if ($arguments->hasExtensions()) { - $result['extensions'] = $arguments->extensions(); - } - if ($arguments->hasUnavailableExtensions()) { - $result['unavailableExtensions'] = $arguments->unavailableExtensions(); - } - if ($arguments->hasResolveDependencies()) { - $result['resolveDependencies'] = $arguments->resolveDependencies(); - } - if ($arguments->hasProcessIsolation()) { - $result['processIsolation'] = $arguments->processIsolation(); - } - if ($arguments->hasRepeat()) { - $result['repeat'] = $arguments->repeat(); - } - if ($arguments->hasStderr()) { - $result['stderr'] = $arguments->stderr(); - } - if ($arguments->hasStopOnDefect()) { - $result['stopOnDefect'] = $arguments->stopOnDefect(); - } - if ($arguments->hasStopOnError()) { - $result['stopOnError'] = $arguments->stopOnError(); - } - if ($arguments->hasStopOnFailure()) { - $result['stopOnFailure'] = $arguments->stopOnFailure(); - } - if ($arguments->hasStopOnWarning()) { - $result['stopOnWarning'] = $arguments->stopOnWarning(); - } - if ($arguments->hasStopOnIncomplete()) { - $result['stopOnIncomplete'] = $arguments->stopOnIncomplete(); - } - if ($arguments->hasStopOnRisky()) { - $result['stopOnRisky'] = $arguments->stopOnRisky(); - } - if ($arguments->hasStopOnSkipped()) { - $result['stopOnSkipped'] = $arguments->stopOnSkipped(); - } - if ($arguments->hasFailOnEmptyTestSuite()) { - $result['failOnEmptyTestSuite'] = $arguments->failOnEmptyTestSuite(); - } - if ($arguments->hasFailOnIncomplete()) { - $result['failOnIncomplete'] = $arguments->failOnIncomplete(); - } - if ($arguments->hasFailOnRisky()) { - $result['failOnRisky'] = $arguments->failOnRisky(); - } - if ($arguments->hasFailOnSkipped()) { - $result['failOnSkipped'] = $arguments->failOnSkipped(); - } - if ($arguments->hasFailOnWarning()) { - $result['failOnWarning'] = $arguments->failOnWarning(); - } - if ($arguments->hasTestdoxGroups()) { - $result['testdoxGroups'] = $arguments->testdoxGroups(); - } - if ($arguments->hasTestdoxExcludeGroups()) { - $result['testdoxExcludeGroups'] = $arguments->testdoxExcludeGroups(); - } - if ($arguments->hasTestdoxHtmlFile()) { - $result['testdoxHTMLFile'] = $arguments->testdoxHtmlFile(); - } - if ($arguments->hasTestdoxTextFile()) { - $result['testdoxTextFile'] = $arguments->testdoxTextFile(); - } - if ($arguments->hasTestdoxXmlFile()) { - $result['testdoxXMLFile'] = $arguments->testdoxXmlFile(); - } - if ($arguments->hasUseDefaultConfiguration()) { - $result['useDefaultConfiguration'] = $arguments->useDefaultConfiguration(); - } - if ($arguments->hasNoExtensions()) { - $result['noExtensions'] = $arguments->noExtensions(); - } - if ($arguments->hasNoCoverage()) { - $result['noCoverage'] = $arguments->noCoverage(); - } - if ($arguments->hasNoLogging()) { - $result['noLogging'] = $arguments->noLogging(); - } - if ($arguments->hasNoInteraction()) { - $result['noInteraction'] = $arguments->noInteraction(); - } - if ($arguments->hasBackupGlobals()) { - $result['backupGlobals'] = $arguments->backupGlobals(); - } - if ($arguments->hasBackupStaticAttributes()) { - $result['backupStaticAttributes'] = $arguments->backupStaticAttributes(); - } - if ($arguments->hasVerbose()) { - $result['verbose'] = $arguments->verbose(); - } - if ($arguments->hasReportUselessTests()) { - $result['reportUselessTests'] = $arguments->reportUselessTests(); - } - if ($arguments->hasStrictCoverage()) { - $result['strictCoverage'] = $arguments->strictCoverage(); - } - if ($arguments->hasDisableCodeCoverageIgnore()) { - $result['disableCodeCoverageIgnore'] = $arguments->disableCodeCoverageIgnore(); - } - if ($arguments->hasBeStrictAboutChangesToGlobalState()) { - $result['beStrictAboutChangesToGlobalState'] = $arguments->beStrictAboutChangesToGlobalState(); - } - if ($arguments->hasDisallowTestOutput()) { - $result['disallowTestOutput'] = $arguments->disallowTestOutput(); - } - if ($arguments->hasBeStrictAboutResourceUsageDuringSmallTests()) { - $result['beStrictAboutResourceUsageDuringSmallTests'] = $arguments->beStrictAboutResourceUsageDuringSmallTests(); - } - if ($arguments->hasDefaultTimeLimit()) { - $result['defaultTimeLimit'] = $arguments->defaultTimeLimit(); - } - if ($arguments->hasEnforceTimeLimit()) { - $result['enforceTimeLimit'] = $arguments->enforceTimeLimit(); - } - if ($arguments->hasDisallowTodoAnnotatedTests()) { - $result['disallowTodoAnnotatedTests'] = $arguments->disallowTodoAnnotatedTests(); - } - if ($arguments->hasReverseList()) { - $result['reverseList'] = $arguments->reverseList(); - } - if ($arguments->hasCoverageFilter()) { - $result['coverageFilter'] = $arguments->coverageFilter(); - } - if ($arguments->hasRandomOrderSeed()) { - $result['randomOrderSeed'] = $arguments->randomOrderSeed(); + assert($iterator instanceof FilterIterator); + return $iterator; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Runner\Filter; + +use function array_map; +use function array_merge; +use function in_array; +use function spl_object_hash; +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); + } } - if ($arguments->hasXdebugFilterFile()) { - $result['xdebugFilterFile'] = $arguments->xdebugFilterFile(); + } + public function accept() : bool + { + $test = $this->getInnerIterator()->current(); + if ($test instanceof TestSuite) { + return \true; } - return $result; + return $this->doAccept(spl_object_hash($test)); } + protected abstract function doAccept(string $hash); } groupTests, \true); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Runner\Filter; + +use function end; +use function implode; +use function preg_match; use function sprintf; -use function stream_resolve_include_path; -use function strpos; -use function trim; -use function version_compare; +use function str_replace; +use Exception; +use PHPUnit\Framework\ErrorTestCase; use PHPUnit\Framework\TestSuite; -use PHPUnit\Runner\Extension\PharLoader; -use PHPUnit\Runner\StandardTestSuiteLoader; -use PHPUnit\Runner\TestSuiteLoader; -use PHPUnit\Runner\Version; -use PHPUnit\TextUI\CliArguments\Builder; -use PHPUnit\TextUI\CliArguments\Configuration; -use PHPUnit\TextUI\CliArguments\Exception as ArgumentsException; -use PHPUnit\TextUI\CliArguments\Mapper; -use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\FilterMapper; -use PHPUnit\TextUI\XmlConfiguration\Generator; -use PHPUnit\TextUI\XmlConfiguration\Loader; -use PHPUnit\TextUI\XmlConfiguration\Migrator; -use PHPUnit\TextUI\XmlConfiguration\PhpHandler; -use PHPUnit\Util\FileLoader; -use PHPUnit\Util\Filesystem; -use PHPUnit\Util\Printer; -use PHPUnit\Util\TextTestListRenderer; -use PHPUnit\Util\Xml\SchemaDetector; -use PHPUnit\Util\XmlTestListRenderer; -use ReflectionClass; -use PHPUnit\SebastianBergmann\CodeCoverage\Filter; -use PHPUnit\SebastianBergmann\CodeCoverage\StaticAnalysis\CacheWarmer; -use PHPUnit\SebastianBergmann\Timer\Timer; -use Throwable; +use PHPUnit\Framework\WarningTestCase; +use PHPUnit\Util\RegularExpression; +use PHPUnit\Util\Test; +use RecursiveFilterIterator; +use RecursiveIterator; +use PHPUnitPHAR\SebastianBergmann\RecursionContext\InvalidArgumentException; /** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -class Command +final class NameFilterIterator extends RecursiveFilterIterator { /** - * @var array - */ - protected $arguments = []; - /** - * @var array + * @var string */ - protected $longOptions = []; + private $filter; /** - * @var bool + * @var int */ - private $versionStringPrinted = \false; + private $filterMin; /** - * @psalm-var list + * @var int */ - private $warnings = []; + private $filterMax; /** * @throws Exception */ - public static function main(bool $exit = \true) : int + public function __construct(RecursiveIterator $iterator, string $filter) { - try { - return (new static())->run($_SERVER['argv'], $exit); - } catch (Throwable $t) { - throw new \PHPUnit\TextUI\RuntimeException($t->getMessage(), (int) $t->getCode(), $t); - } + parent::__construct($iterator); + $this->setFilter($filter); } /** - * @throws Exception + * @throws InvalidArgumentException */ - public function run(array $argv, bool $exit = \true) : int + public function accept() : bool { - $this->handleArguments($argv); - $runner = $this->createRunner(); - if ($this->arguments['test'] instanceof TestSuite) { - $suite = $this->arguments['test']; - } else { - $suite = $runner->getTest($this->arguments['test'], $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->run($suite, $this->arguments, $this->warnings, $exit); - } catch (Throwable $t) { - print $t->getMessage() . PHP_EOL; + $test = $this->getInnerIterator()->current(); + if ($test instanceof TestSuite) { + return \true; } - $return = \PHPUnit\TextUI\TestRunner::FAILURE_EXIT; - if (isset($result) && $result->wasSuccessful()) { - $return = \PHPUnit\TextUI\TestRunner::SUCCESS_EXIT; - } elseif (!isset($result) || $result->errorCount() > 0) { - $return = \PHPUnit\TextUI\TestRunner::EXCEPTION_EXIT; + $tmp = Test::describe($test); + if ($test instanceof ErrorTestCase || $test instanceof WarningTestCase) { + $name = $test->getMessage(); + } elseif ($tmp[0] !== '') { + $name = implode('::', $tmp); + } else { + $name = $tmp[1]; } - if ($exit) { - exit($return); + $accepted = @preg_match($this->filter, $name, $matches); + if ($accepted && isset($this->filterMax)) { + $set = end($matches); + $accepted = $set >= $this->filterMin && $set <= $this->filterMax; } - return $return; - } - /** - * Create a TestRunner, override in subclasses. - */ - protected function createRunner() : \PHPUnit\TextUI\TestRunner - { - return new \PHPUnit\TextUI\TestRunner($this->arguments['loader']); + return (bool) $accepted; } /** - * Handles the command-line arguments. - * - * A child class of PHPUnit\TextUI\Command can hook into the argument - * parsing by adding the switch(es) to the $longOptions array and point to a - * callback method that handles the switch(es) in the child class like this - * - * - * longOptions['my-switch'] = 'myHandler'; - * // my-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); - * } - * - * } - * - * * @throws Exception */ - protected function handleArguments(array $argv) : void + private function setFilter(string $filter) : void { - try { - $arguments = (new Builder())->fromParameters($argv, array_keys($this->longOptions)); - } catch (ArgumentsException $e) { - $this->exitWithErrorMessage($e->getMessage()); - } - assert(isset($arguments) && $arguments instanceof Configuration); - if ($arguments->hasGenerateConfiguration() && $arguments->generateConfiguration()) { - $this->generateConfiguration(); - } - if ($arguments->hasAtLeastVersion()) { - if (version_compare(Version::id(), $arguments->atLeastVersion(), '>=')) { - exit(\PHPUnit\TextUI\TestRunner::SUCCESS_EXIT); + 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 = (int) $matches[2]; + $this->filterMax = (int) $matches[3]; + } else { + $filter = sprintf('%s.*with data set #%s$', $matches[1], $matches[2]); + } + } elseif (preg_match('/^(.*?)@(.+)$/', $filter, $matches)) { + $filter = sprintf('%s.*with data set "%s"$', $matches[1], $matches[2]); } - exit(\PHPUnit\TextUI\TestRunner::FAILURE_EXIT); - } - if ($arguments->hasVersion() && $arguments->version()) { - $this->printVersionString(); - exit(\PHPUnit\TextUI\TestRunner::SUCCESS_EXIT); - } - if ($arguments->hasCheckVersion() && $arguments->checkVersion()) { - $this->handleVersionCheck(); - } - if ($arguments->hasHelp()) { - $this->showHelp(); - exit(\PHPUnit\TextUI\TestRunner::SUCCESS_EXIT); - } - if ($arguments->hasUnrecognizedOrderBy()) { - $this->exitWithErrorMessage(sprintf('unrecognized --order-by option: %s', $arguments->unrecognizedOrderBy())); - } - if ($arguments->hasIniSettings()) { - foreach ($arguments->iniSettings() as $name => $value) { - ini_set($name, $value); - } - } - if ($arguments->hasIncludePath()) { - ini_set('include_path', $arguments->includePath() . PATH_SEPARATOR . ini_get('include_path')); - } - $this->arguments = (new Mapper())->mapToLegacyArray($arguments); - $this->handleCustomOptions($arguments->unrecognizedOptions()); - $this->handleCustomTestSuite(); - if (!isset($this->arguments['testSuffixes'])) { - $this->arguments['testSuffixes'] = ['Test.php', '.phpt']; - } - if (!isset($this->arguments['test']) && $arguments->hasArgument()) { - $this->arguments['test'] = realpath($arguments->argument()); - if ($this->arguments['test'] === \false) { - $this->exitWithErrorMessage(sprintf('Cannot open file "%s".', $arguments->argument())); - } - } - if ($this->arguments['loader'] !== null) { - $this->arguments['loader'] = $this->handleLoader($this->arguments['loader']); - } - if (isset($this->arguments['configuration'])) { - if (is_dir($this->arguments['configuration'])) { - $candidate = $this->configurationFileInDirectory($this->arguments['configuration']); - if ($candidate !== null) { - $this->arguments['configuration'] = $candidate; - } - } - } elseif ($this->arguments['useDefaultConfiguration']) { - $candidate = $this->configurationFileInDirectory(getcwd()); - if ($candidate !== null) { - $this->arguments['configuration'] = $candidate; - } - } - if ($arguments->hasMigrateConfiguration() && $arguments->migrateConfiguration()) { - if (!isset($this->arguments['configuration'])) { - print 'No configuration file found to migrate.' . PHP_EOL; - exit(\PHPUnit\TextUI\TestRunner::EXCEPTION_EXIT); - } - $this->migrateConfiguration(realpath($this->arguments['configuration'])); - } - if (isset($this->arguments['configuration'])) { - try { - $this->arguments['configurationObject'] = (new Loader())->load($this->arguments['configuration']); - } catch (Throwable $e) { - print $e->getMessage() . PHP_EOL; - exit(\PHPUnit\TextUI\TestRunner::FAILURE_EXIT); - } - $phpunitConfiguration = $this->arguments['configurationObject']->phpunit(); - (new PhpHandler())->handle($this->arguments['configurationObject']->php()); - if (isset($this->arguments['bootstrap'])) { - $this->handleBootstrap($this->arguments['bootstrap']); - } elseif ($phpunitConfiguration->hasBootstrap()) { - $this->handleBootstrap($phpunitConfiguration->bootstrap()); - } - if (!isset($this->arguments['stderr'])) { - $this->arguments['stderr'] = $phpunitConfiguration->stderr(); - } - if (!isset($this->arguments['noExtensions']) && $phpunitConfiguration->hasExtensionsDirectory() && extension_loaded('phar')) { - $result = (new PharLoader())->loadPharExtensionsInDirectory($phpunitConfiguration->extensionsDirectory()); - $this->arguments['loadedExtensions'] = $result['loadedExtensions']; - $this->arguments['notLoadedExtensions'] = $result['notLoadedExtensions']; - unset($result); - } - if (!isset($this->arguments['columns'])) { - $this->arguments['columns'] = $phpunitConfiguration->columns(); - } - if (!isset($this->arguments['printer']) && $phpunitConfiguration->hasPrinterClass()) { - $file = $phpunitConfiguration->hasPrinterFile() ? $phpunitConfiguration->printerFile() : ''; - $this->arguments['printer'] = $this->handlePrinter($phpunitConfiguration->printerClass(), $file); - } - if ($phpunitConfiguration->hasTestSuiteLoaderClass()) { - $file = $phpunitConfiguration->hasTestSuiteLoaderFile() ? $phpunitConfiguration->testSuiteLoaderFile() : ''; - $this->arguments['loader'] = $this->handleLoader($phpunitConfiguration->testSuiteLoaderClass(), $file); - } - if (!isset($this->arguments['testsuite']) && $phpunitConfiguration->hasDefaultTestSuite()) { - $this->arguments['testsuite'] = $phpunitConfiguration->defaultTestSuite(); - } - if (!isset($this->arguments['test'])) { - try { - $this->arguments['test'] = (new \PHPUnit\TextUI\TestSuiteMapper())->map($this->arguments['configurationObject']->testSuite(), $this->arguments['testsuite'] ?? ''); - } catch (\PHPUnit\TextUI\Exception $e) { - $this->printVersionString(); - print $e->getMessage() . PHP_EOL; - exit(\PHPUnit\TextUI\TestRunner::EXCEPTION_EXIT); - } - } - } 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['configurationObject'], $this->arguments['warmCoverageCache'])) { - $this->handleWarmCoverageCache($this->arguments['configurationObject']); - } - if (!isset($this->arguments['test'])) { - $this->showHelp(); - exit(\PHPUnit\TextUI\TestRunner::EXCEPTION_EXIT); - } - } - /** - * Handles the loading of the PHPUnit\Runner\TestSuiteLoader implementation. - * - * @deprecated see https://github.com/sebastianbergmann/phpunit/issues/4039 - */ - protected function handleLoader(string $loaderClass, string $loaderFile = '') : ?TestSuiteLoader - { - $this->warnings[] = 'Using a custom test suite loader is deprecated'; - if (!class_exists($loaderClass, \false)) { - if ($loaderFile == '') { - $loaderFile = Filesystem::classNameToFilename($loaderClass); - } - $loaderFile = stream_resolve_include_path($loaderFile); - if ($loaderFile) { - /** - * @noinspection PhpIncludeInspection - * - * @psalm-suppress UnresolvableInclude - */ - require $loaderFile; - } - } - if (class_exists($loaderClass, \false)) { - try { - $class = new ReflectionClass($loaderClass); - // @codeCoverageIgnoreStart - } catch (\ReflectionException $e) { - throw new \PHPUnit\TextUI\ReflectionException($e->getMessage(), $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd - 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) { - /** - * @noinspection PhpIncludeInspection - * - * @psalm-suppress UnresolvableInclude - */ - 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); - // @codeCoverageIgnoreStart - } catch (\ReflectionException $e) { - throw new \PHPUnit\TextUI\ReflectionException($e->getMessage(), $e->getCode(), $e); - // @codeCoverageIgnoreEnd - } - if (!$class->implementsInterface(\PHPUnit\TextUI\ResultPrinter::class)) { - $this->exitWithErrorMessage(sprintf('Could not use "%s" as printer: class does not implement %s', $printerClass, \PHPUnit\TextUI\ResultPrinter::class)); - } - if (!$class->isInstantiable()) { - $this->exitWithErrorMessage(sprintf('Could not use "%s" as printer: class cannot be instantiated', $printerClass)); - } - if ($class->isSubclassOf(\PHPUnit\TextUI\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 (Throwable $t) { - if ($t instanceof \PHPUnit\Exception) { - $this->exitWithErrorMessage($t->getMessage()); - } - $message = sprintf('Error in bootstrap script: %s:%s%s%s%s', get_class($t), PHP_EOL, $t->getMessage(), PHP_EOL, $t->getTraceAsString()); - while ($t = $t->getPrevious()) { - $message .= sprintf('%s%sPrevious error: %s:%s%s%s%s', PHP_EOL, PHP_EOL, get_class($t), PHP_EOL, $t->getMessage(), PHP_EOL, $t->getTraceAsString()); - } - $this->exitWithErrorMessage($message); - } - } - 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(\PHPUnit\TextUI\TestRunner::SUCCESS_EXIT); - } - /** - * Show the help message. - */ - protected function showHelp() : void - { - $this->printVersionString(); - (new \PHPUnit\TextUI\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(\PHPUnit\TextUI\TestRunner::FAILURE_EXIT); - } - private function handleListGroups(TestSuite $suite, bool $exit) : int - { - $this->printVersionString(); - $this->warnAboutConflictingOptions('listGroups', ['filter', 'groups', 'excludeGroups', 'testsuite']); - print 'Available test group(s):' . PHP_EOL; - $groups = $suite->getGroups(); - sort($groups); - foreach ($groups as $group) { - if (strpos($group, '__phpunit_') === 0) { - continue; - } - printf(' - %s' . PHP_EOL, $group); - } - if ($exit) { - exit(\PHPUnit\TextUI\TestRunner::SUCCESS_EXIT); - } - return \PHPUnit\TextUI\TestRunner::SUCCESS_EXIT; - } - /** - * @throws \PHPUnit\Framework\Exception - * @throws \PHPUnit\TextUI\XmlConfiguration\Exception - */ - private function handleListSuites(bool $exit) : int - { - $this->printVersionString(); - $this->warnAboutConflictingOptions('listSuites', ['filter', 'groups', 'excludeGroups', 'testsuite']); - print 'Available test suite(s):' . PHP_EOL; - foreach ($this->arguments['configurationObject']->testSuite() as $testSuite) { - printf(' - %s' . PHP_EOL, $testSuite->name()); - } - if ($exit) { - exit(\PHPUnit\TextUI\TestRunner::SUCCESS_EXIT); - } - return \PHPUnit\TextUI\TestRunner::SUCCESS_EXIT; - } - /** - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - private function handleListTests(TestSuite $suite, bool $exit) : int - { - $this->printVersionString(); - $this->warnAboutConflictingOptions('listTests', ['filter', 'groups', 'excludeGroups']); - $renderer = new TextTestListRenderer(); - print $renderer->render($suite); - if ($exit) { - exit(\PHPUnit\TextUI\TestRunner::SUCCESS_EXIT); - } - return \PHPUnit\TextUI\TestRunner::SUCCESS_EXIT; - } - /** - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - private function handleListTestsXml(TestSuite $suite, string $target, bool $exit) : int - { - $this->printVersionString(); - $this->warnAboutConflictingOptions('listTestsXml', ['filter', 'groups', 'excludeGroups']); - $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(\PHPUnit\TextUI\TestRunner::SUCCESS_EXIT); - } - return \PHPUnit\TextUI\TestRunner::SUCCESS_EXIT; - } - private function generateConfiguration() : void - { - $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)); - print 'Cache directory (relative to path shown above; default: .phpunit.cache): '; - $cacheDirectory = trim(fgets(STDIN)); - if ($bootstrapScript === '') { - $bootstrapScript = 'vendor/autoload.php'; - } - if ($testsDirectory === '') { - $testsDirectory = 'tests'; - } - if ($src === '') { - $src = 'src'; - } - if ($cacheDirectory === '') { - $cacheDirectory = '.phpunit.cache'; - } - $generator = new Generator(); - file_put_contents('phpunit.xml', $generator->generateDefaultConfiguration(Version::series(), $bootstrapScript, $testsDirectory, $src, $cacheDirectory)); - print PHP_EOL . 'Generated phpunit.xml in ' . getcwd() . '.' . PHP_EOL; - print 'Make sure to exclude the ' . $cacheDirectory . ' directory from version control.' . PHP_EOL; - exit(\PHPUnit\TextUI\TestRunner::SUCCESS_EXIT); - } - private function migrateConfiguration(string $filename) : void - { - $this->printVersionString(); - if (!(new SchemaDetector())->detect($filename)->detected()) { - print $filename . ' does not need to be migrated.' . PHP_EOL; - exit(\PHPUnit\TextUI\TestRunner::EXCEPTION_EXIT); - } - copy($filename, $filename . '.bak'); - print 'Created backup: ' . $filename . '.bak' . PHP_EOL; - try { - file_put_contents($filename, (new Migrator())->migrate($filename)); - print 'Migrated configuration: ' . $filename . PHP_EOL; - } catch (Throwable $t) { - print 'Migration failed: ' . $t->getMessage() . PHP_EOL; - exit(\PHPUnit\TextUI\TestRunner::EXCEPTION_EXIT); - } - exit(\PHPUnit\TextUI\TestRunner::SUCCESS_EXIT); - } - private function handleCustomOptions(array $unrecognizedOptions) : void - { - foreach ($unrecognizedOptions as $name => $value) { - if (isset($this->longOptions[$name])) { - $handler = $this->longOptions[$name]; - } - $name .= '='; - if (isset($this->longOptions[$name])) { - $handler = $this->longOptions[$name]; - } - if (isset($handler) && is_callable([$this, $handler])) { - $this->{$handler}($value); - unset($handler); - } - } - } - private function handleWarmCoverageCache(\PHPUnit\TextUI\XmlConfiguration\Configuration $configuration) : void - { - $this->printVersionString(); - if (isset($this->arguments['coverageCacheDirectory'])) { - $cacheDirectory = $this->arguments['coverageCacheDirectory']; - } elseif ($configuration->codeCoverage()->hasCacheDirectory()) { - $cacheDirectory = $configuration->codeCoverage()->cacheDirectory()->path(); - } else { - print 'Cache for static analysis has not been configured' . PHP_EOL; - exit(\PHPUnit\TextUI\TestRunner::EXCEPTION_EXIT); - } - $filter = new Filter(); - if ($configuration->codeCoverage()->hasNonEmptyListOfFilesToBeIncludedInCodeCoverageReport()) { - (new FilterMapper())->map($filter, $configuration->codeCoverage()); - } elseif (isset($this->arguments['coverageFilter'])) { - if (!is_array($this->arguments['coverageFilter'])) { - $coverageFilterDirectories = [$this->arguments['coverageFilter']]; - } else { - $coverageFilterDirectories = $this->arguments['coverageFilter']; - } - foreach ($coverageFilterDirectories as $coverageFilterDirectory) { - $filter->includeDirectory($coverageFilterDirectory); - } - } else { - print 'Filter for code coverage has not been configured' . PHP_EOL; - exit(\PHPUnit\TextUI\TestRunner::EXCEPTION_EXIT); - } - $timer = new Timer(); - $timer->start(); - print 'Warming cache for static analysis ... '; - (new CacheWarmer())->warmCache($cacheDirectory, !$configuration->codeCoverage()->disableCodeCoverageIgnore(), $configuration->codeCoverage()->ignoreDeprecatedCodeUnits(), $filter); - print 'done [' . $timer->stop()->asString() . ']' . PHP_EOL; - exit(\PHPUnit\TextUI\TestRunner::SUCCESS_EXIT); - } - private function configurationFileInDirectory(string $directory) : ?string - { - $candidates = [$directory . '/phpunit.xml', $directory . '/phpunit.xml.dist']; - foreach ($candidates as $candidate) { - if (is_file($candidate)) { - return realpath($candidate); - } - } - return null; - } - /** - * @psalm-param "listGroups"|"listSuites"|"listTests"|"listTestsXml"|"filter"|"groups"|"excludeGroups"|"testsuite" $key - * @psalm-param list<"listGroups"|"listSuites"|"listTests"|"listTestsXml"|"filter"|"groups"|"excludeGroups"|"testsuite"> $keys - */ - private function warnAboutConflictingOptions(string $key, array $keys) : void - { - $warningPrinted = \false; - foreach ($keys as $_key) { - if (!empty($this->arguments[$_key])) { - printf('The %s and %s options cannot be combined, %s is ignored' . PHP_EOL, $this->mapKeyToOptionForWarning($_key), $this->mapKeyToOptionForWarning($key), $this->mapKeyToOptionForWarning($_key)); - $warningPrinted = \true; - } - } - if ($warningPrinted) { - print PHP_EOL; - } - } - /** - * @psalm-param "listGroups"|"listSuites"|"listTests"|"listTestsXml"|"filter"|"groups"|"excludeGroups"|"testsuite" $key - */ - private function mapKeyToOptionForWarning(string $key) : string - { - switch ($key) { - case 'listGroups': - return '--list-groups'; - case 'listSuites': - return '--list-suites'; - case 'listTests': - return '--list-tests'; - case 'listTestsXml': - return '--list-tests-xml'; - case 'filter': - return '--filter'; - case 'groups': - return '--group'; - case 'excludeGroups': - return '--exclude-group'; - case 'testsuite': - return '--testsuite'; + // Escape delimiters in regular expression. Do NOT use preg_quote, + // to keep magic characters. + $filter = sprintf('/%s/i', str_replace('/', '\\/', $filter)); } + $this->filter = $filter; } } 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; - } - $this->timer = new Timer(); - $this->timer->start(); - } - public function printResult(TestResult $result) : void - { - $this->printHeader($result); - $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" . trim((string) $e) . "\n"); - } - } - 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'); - } - protected function printHeader(TestResult $result) : void - { - if (count($result) > 0) { - $this->write(PHP_EOL . PHP_EOL . (new ResourceUsageFormatter())->resourceUsage($this->timer->stop()) . PHP_EOL . PHP_EOL); - } - } - protected function printFooter(TestResult $result) : void - { - if (count($result) === 0) { - $this->writeWithColor('fg-black, bg-yellow', 'No tests executed!'); - return; - } - if ($result->wasSuccessfulAndNoTestIsRiskyOrSkippedOrIncomplete()) { - $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')); - return; - } - $color = 'fg-black, bg-yellow'; - if ($result->wasSuccessful()) { - 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; - } - } + public function executeAfterIncompleteTest(string $test, string $message, float $time) : void; } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Runner; + +/** + * This interface, as well as the associated mechanism for extending PHPUnit, + * will be removed in PHPUnit 10. There is no alternative available in this + * version of PHPUnit. + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see https://github.com/sebastianbergmann/phpunit/issues/4676 + */ +interface AfterTestHook extends \PHPUnit\Runner\TestHook { - private const LEFT_MARGIN = ' '; - private const HELP_TEXT = ['Usage' => [['text' => 'phpunit [options] UnitTest.php'], ['text' => 'phpunit [options] ']], 'Code Coverage Options' => [['arg' => '--coverage-clover ', 'desc' => 'Generate code coverage report in Clover XML format'], ['arg' => '--coverage-cobertura ', 'desc' => 'Generate code coverage report in Cobertura XML format'], ['arg' => '--coverage-crap4j ', 'desc' => 'Generate code coverage report in Crap4J XML format'], ['arg' => '--coverage-html ', 'desc' => 'Generate code coverage report in HTML format'], ['arg' => '--coverage-php ', 'desc' => 'Export PHP_CodeCoverage object to file'], ['arg' => '--coverage-text=', 'desc' => 'Generate code coverage report in text format [default: standard output]'], ['arg' => '--coverage-xml ', 'desc' => 'Generate code coverage report in PHPUnit XML format'], ['arg' => '--coverage-cache ', 'desc' => 'Cache static analysis results'], ['arg' => '--warm-coverage-cache', 'desc' => 'Warm static analysis cache'], ['arg' => '--coverage-filter ', 'desc' => 'Include in code coverage analysis'], ['arg' => '--path-coverage', 'desc' => 'Perform path coverage analysis'], ['arg' => '--disable-coverage-ignore', 'desc' => 'Disable annotations for ignoring code coverage'], ['arg' => '--no-coverage', 'desc' => 'Ignore code coverage configuration']], 'Logging Options' => [['arg' => '--log-junit ', 'desc' => 'Log test execution in JUnit XML format to file'], ['arg' => '--log-teamcity ', 'desc' => 'Log test execution in TeamCity format to file'], ['arg' => '--testdox-html ', 'desc' => 'Write agile documentation in HTML format to file'], ['arg' => '--testdox-text ', 'desc' => 'Write agile documentation in Text format to file'], ['arg' => '--testdox-xml ', 'desc' => 'Write agile documentation in XML format to file'], ['arg' => '--reverse-list', 'desc' => 'Print defects in reverse order'], ['arg' => '--no-logging', 'desc' => 'Ignore logging configuration']], 'Test Selection Options' => [['arg' => '--list-suites', 'desc' => 'List available test suites'], ['arg' => '--testsuite ', 'desc' => 'Filter which testsuite to run'], ['arg' => '--list-groups', 'desc' => 'List available test groups'], ['arg' => '--group ', 'desc' => 'Only runs tests from the specified group(s)'], ['arg' => '--exclude-group ', 'desc' => 'Exclude tests from the specified group(s)'], ['arg' => '--covers ', 'desc' => 'Only runs tests annotated with "@covers "'], ['arg' => '--uses ', 'desc' => 'Only runs tests annotated with "@uses "'], ['arg' => '--list-tests', 'desc' => 'List available tests'], ['arg' => '--list-tests-xml ', 'desc' => 'List available tests in XML format'], ['arg' => '--filter ', 'desc' => 'Filter which tests to run'], ['arg' => '--test-suffix ', '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 ', '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 ', 'desc' => 'Use colors in output ("never", "auto" or "always")'], ['arg' => '--columns ', '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-incomplete', 'desc' => 'Treat incomplete tests as failures'], ['arg' => '--fail-on-risky', 'desc' => 'Treat risky tests as failures'], ['arg' => '--fail-on-skipped', 'desc' => 'Treat skipped tests as failures'], ['arg' => '--fail-on-warning', 'desc' => 'Treat tests with warnings as failures'], ['arg' => '-v|--verbose', 'desc' => 'Output more verbose information'], ['arg' => '--debug', 'desc' => 'Display debugging information'], ['spacer' => ''], ['arg' => '--repeat ', '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 ', 'desc' => 'TestListener implementation to use'], ['spacer' => ''], ['arg' => '--order-by ', 'desc' => 'Run tests in order: default|defects|duration|no-depends|random|reverse|size'], ['arg' => '--random-order-seed ', 'desc' => 'Use a specific random seed 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 ', 'desc' => 'A PHP script that is included as early as possible'], ['arg' => '--bootstrap ', 'desc' => 'A PHP script that is included before the tests run'], ['arg' => '-c|--configuration ', 'desc' => 'Read configuration from XML file'], ['arg' => '--no-configuration', 'desc' => 'Ignore default configuration file (phpunit.xml)'], ['arg' => '--extensions ', 'desc' => 'A comma separated list of PHPUnit extensions to load'], ['arg' => '--no-extensions', 'desc' => 'Do not load PHPUnit extensions'], ['arg' => '--include-path ', 'desc' => 'Prepend PHP\'s include_path with given path(s)'], ['arg' => '-d ', 'desc' => 'Sets a php.ini value'], ['arg' => '--cache-result-file ', 'desc' => 'Specify result cache path and filename'], ['arg' => '--generate-configuration', 'desc' => 'Generate configuration file with suggested settings'], ['arg' => '--migrate-configuration', 'desc' => 'Migrate configuration file to current format']], 'Miscellaneous Options' => [['arg' => '-h|--help', 'desc' => 'Prints this usage information'], ['arg' => '--version', 'desc' => 'Prints the version and exits'], ['arg' => '--atleast-version ', '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 + * 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. */ - private $hasColor = \false; - public function __construct(?int $width = null, ?bool $withColor = null) + public function executeAfterTest(string $test, float $time) : void; +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Runner; + +/** + * This interface, as well as the associated mechanism for extending PHPUnit, + * will be removed in PHPUnit 10. There is no alternative available in this + * version of PHPUnit. + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see https://github.com/sebastianbergmann/phpunit/issues/4676 + */ +interface AfterTestWarningHook extends \PHPUnit\Runner\TestHook +{ + public function executeAfterTestWarning(string $test, string $message, float $time) : void; +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Runner; + +/** + * This interface, as well as the associated mechanism for extending PHPUnit, + * will be removed in PHPUnit 10. There is no alternative available in this + * version of PHPUnit. + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see https://github.com/sebastianbergmann/phpunit/issues/4676 + */ +interface BeforeFirstTestHook extends \PHPUnit\Runner\Hook +{ + public function executeBeforeFirstTest() : void; +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Runner; + +/** + * This interface, as well as the associated mechanism for extending PHPUnit, + * will be removed in PHPUnit 10. There is no alternative available in this + * version of PHPUnit. + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see https://github.com/sebastianbergmann/phpunit/issues/4676 + */ +interface BeforeTestHook extends \PHPUnit\Runner\TestHook +{ + public function executeBeforeTest(string $test) : void; +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Runner; + +/** + * This interface, as well as the associated mechanism for extending PHPUnit, + * will be removed in PHPUnit 10. There is no alternative available in this + * version of PHPUnit. + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see https://github.com/sebastianbergmann/phpunit/issues/4676 + */ +interface Hook +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Runner; + +/** + * This interface, as well as the associated mechanism for extending PHPUnit, + * will be removed in PHPUnit 10. There is no alternative available in this + * version of PHPUnit. + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see https://github.com/sebastianbergmann/phpunit/issues/4676 + */ +interface TestHook extends \PHPUnit\Runner\Hook +{ +} + + * + * 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; +use Throwable; +/** + * @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(\PHPUnit\Runner\TestHook $hook) : void { - if ($width === null) { - $width = (new Console())->getNumberOfColumns(); + $this->hooks[] = $hook; + } + public function startTest(Test $test) : void + { + foreach ($this->hooks as $hook) { + if ($hook instanceof \PHPUnit\Runner\BeforeTestHook) { + $hook->executeBeforeTest(TestUtil::describeAsString($test)); + } } - if ($withColor === null) { - $this->hasColor = (new Console())->hasColorSupport(); - } else { - $this->hasColor = $withColor; + $this->lastTestWasNotSuccessful = \false; + } + public function addError(Test $test, Throwable $t, float $time) : void + { + foreach ($this->hooks as $hook) { + if ($hook instanceof \PHPUnit\Runner\AfterTestErrorHook) { + $hook->executeAfterTestError(TestUtil::describeAsString($test), $t->getMessage(), $time); + } } - foreach (self::HELP_TEXT as $options) { - foreach ($options as $option) { - if (isset($option['arg'])) { - $this->maxArgLength = max($this->maxArgLength, isset($option['arg']) ? strlen($option['arg']) : 0); - } + $this->lastTestWasNotSuccessful = \true; + } + public function addWarning(Test $test, Warning $e, float $time) : void + { + foreach ($this->hooks as $hook) { + if ($hook instanceof \PHPUnit\Runner\AfterTestWarningHook) { + $hook->executeAfterTestWarning(TestUtil::describeAsString($test), $e->getMessage(), $time); } } - $this->maxDescLength = $width - $this->maxArgLength - 4; + $this->lastTestWasNotSuccessful = \true; } - /** - * Write the help file to the CLI, adapting width and colors to the console. - */ - public function writeToConsole() : void + public function addFailure(Test $test, AssertionFailedError $e, float $time) : void { - if ($this->hasColor) { - $this->writeWithColor(); - } else { - $this->writePlaintext(); + foreach ($this->hooks as $hook) { + if ($hook instanceof \PHPUnit\Runner\AfterTestFailureHook) { + $hook->executeAfterTestFailure(TestUtil::describeAsString($test), $e->getMessage(), $time); + } } + $this->lastTestWasNotSuccessful = \true; } - private function writePlaintext() : void + public function addIncompleteTest(Test $test, Throwable $t, float $time) : void { - foreach (self::HELP_TEXT as $section => $options) { - print "{$section}:" . PHP_EOL; - if ($section !== 'Usage') { - print PHP_EOL; + foreach ($this->hooks as $hook) { + if ($hook instanceof \PHPUnit\Runner\AfterIncompleteTestHook) { + $hook->executeAfterIncompleteTest(TestUtil::describeAsString($test), $t->getMessage(), $time); } - 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; - } + } + $this->lastTestWasNotSuccessful = \true; + } + public function addRiskyTest(Test $test, Throwable $t, float $time) : void + { + foreach ($this->hooks as $hook) { + if ($hook instanceof \PHPUnit\Runner\AfterRiskyTestHook) { + $hook->executeAfterRiskyTest(TestUtil::describeAsString($test), $t->getMessage(), $time); } - print PHP_EOL; } + $this->lastTestWasNotSuccessful = \true; } - private function writeWithColor() : void + public function addSkippedTest(Test $test, Throwable $t, float $time) : 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; - } + foreach ($this->hooks as $hook) { + if ($hook instanceof \PHPUnit\Runner\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 \PHPUnit\Runner\AfterSuccessfulTestHook) { + $hook->executeAfterSuccessfulTest(TestUtil::describeAsString($test), $time); } } - print PHP_EOL; } + foreach ($this->hooks as $hook) { + if ($hook instanceof \PHPUnit\Runner\AfterTestHook) { + $hook->executeAfterTest(TestUtil::describeAsString($test), $time); + } + } + } + public function startTestSuite(TestSuite $suite) : void + { + } + public function endTestSuite(TestSuite $suite) : void + { } } filename = $filename; + $this->phpUtil = $phpUtil ?: AbstractPhpProcess::factory(); + } /** - * @var Timer + * Counts the number of test cases executed by run(TestResult result). */ - private $timer; - public function __construct(TestSuiteLoader $loader = null, CodeCoverageFilter $filter = null) + public function count() : int { - if ($filter === null) { - $filter = new CodeCoverageFilter(); - } - $this->codeCoverageFilter = $filter; - $this->loader = $loader; - $this->timer = new Timer(); + return 1; } /** - * @throws \PHPUnit\Runner\Exception - * @throws \PHPUnit\TextUI\XmlConfiguration\Exception + * Runs a test and collects its result in a TestResult instance. + * + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException * @throws Exception + * @throws InvalidArgumentException + * @throws UnintentionallyCoveredCodeException */ - public function run(TestSuite $suite, array $arguments = [], array $warnings = [], bool $exit = \true) : TestResult + public function run(?TestResult $result = null) : TestResult { - if (isset($arguments['configuration'])) { - $GLOBALS['__PHPUNIT_CONFIGURATION_FILE'] = $arguments['configuration']; + if ($result === null) { + $result = new TestResult(); } - $this->handleConfiguration($arguments); - $warnings = array_merge($warnings, $arguments['warnings']); - if (is_int($arguments['columns']) && $arguments['columns'] < 16) { - $arguments['columns'] = 16; - $tooFewColumnsRequested = \true; + try { + $sections = $this->parse(); + } catch (\PHPUnit\Runner\Exception $e) { + $result->startTest($this); + $result->addFailure($this, new SkippedTestError($e->getMessage()), 0); + $result->endTest($this, 0); + return $result; } - if (isset($arguments['bootstrap'])) { - $GLOBALS['__PHPUNIT_BOOTSTRAP'] = $arguments['bootstrap']; + $code = $this->render($sections['FILE']); + $xfail = \false; + $settings = $this->parseIniSection($this->settings($result->getCollectCodeCoverageInformation())); + $result->startTest($this); + if (isset($sections['INI'])) { + $settings = $this->parseIniSection($sections['INI'], $settings); } - if ($arguments['backupGlobals'] === \true) { - $suite->setBackupGlobals(\true); + if (isset($sections['ENV'])) { + $env = $this->parseEnvSection($sections['ENV']); + $this->phpUtil->setEnv($env); } - if ($arguments['backupStaticAttributes'] === \true) { - $suite->setBackupStaticAttributes(\true); + $this->phpUtil->setUseStderrRedirection(\true); + if ($result->enforcesTimeLimit()) { + $this->phpUtil->setTimeout($result->getTimeoutForLargeTests()); } - if ($arguments['beStrictAboutChangesToGlobalState'] === \true) { - $suite->setBeStrictAboutChangesToGlobalState(\true); + $skip = $this->runSkip($sections, $result, $settings); + if ($skip) { + return $result; } - if ($arguments['executionOrder'] === TestSuiteSorter::ORDER_RANDOMIZED) { - mt_srand($arguments['randomOrderSeed']); + if (isset($sections['XFAIL'])) { + $xfail = trim($sections['XFAIL']); } - if ($arguments['cacheResult']) { - if (!isset($arguments['cacheResultFile'])) { - if (isset($arguments['configurationObject'])) { - assert($arguments['configurationObject'] instanceof Configuration); - $cacheLocation = $arguments['configurationObject']->filename(); - } 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 (isset($sections['STDIN'])) { + $this->phpUtil->setStdin($sections['STDIN']); } - 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 (isset($sections['ARGS'])) { + $this->phpUtil->setArgs($sections['ARGS']); } - if (is_int($arguments['repeat']) && $arguments['repeat'] > 0) { - $_suite = new TestSuite(); - /* @noinspection PhpUnusedLocalVariableInspection */ - foreach (range(1, $arguments['repeat']) as $step) { - $_suite->addTest($suite); + if ($result->getCollectCodeCoverageInformation()) { + $codeCoverageCacheDirectory = null; + $pathCoverage = \false; + $codeCoverage = $result->getCodeCoverage(); + if ($codeCoverage) { + if ($codeCoverage->cachesStaticAnalysis()) { + $codeCoverageCacheDirectory = $codeCoverage->cacheDirectory(); + } + $pathCoverage = $codeCoverage->collectsBranchAndPathCoverage(); } - $suite = $_suite; - unset($_suite); + $this->renderForCoverage($code, $pathCoverage, $codeCoverageCacheDirectory); } - $result = $this->createTestResult(); - $listener = new TestListenerAdapter(); - $listenerNeeded = \false; - foreach ($this->extensions as $extension) { - if ($extension instanceof TestHook) { - $listener->add($extension); - $listenerNeeded = \true; + $timer = new Timer(); + $timer->start(); + $jobResult = $this->phpUtil->runJob($code, $this->stringifyIni($settings)); + $time = $timer->stop()->asSeconds(); + $this->output = $jobResult['stdout'] ?? ''; + if (isset($codeCoverage) && ($coverage = $this->cleanupForCoverage())) { + $codeCoverage->append($coverage, $this, \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 ($listenerNeeded) { - $result->addListener($listener); + if ($xfail !== \false && $result->allCompletelyImplemented()) { + $result->addFailure($this, new IncompleteTestError('XFAIL section but test passes'), $time); } - unset($listener, $listenerNeeded); - if ($arguments['convertDeprecationsToExceptions']) { - $result->convertDeprecationsToExceptions(\true); + $this->runClean($sections, $result->getCollectCodeCoverageInformation()); + $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); + } + public function sortId() : string + { + return $this->filename; + } + /** + * @return list + */ + public function provides() : array + { + return []; + } + /** + * @return list + */ + public function requires() : array + { + return []; + } + /** + * Parse --INI-- section key value pairs and return as array. + * + * @param array|string $content + */ + private function parseIniSection($content, array $ini = []) : array + { + if (is_string($content)) { + $content = explode("\n", trim($content)); } - if (!$arguments['convertErrorsToExceptions']) { - $result->convertErrorsToExceptions(\false); + 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; } - if (!$arguments['convertNoticesToExceptions']) { - $result->convertNoticesToExceptions(\false); + 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]; + } } - if (!$arguments['convertWarningsToExceptions']) { - $result->convertWarningsToExceptions(\false); + return $env; + } + /** + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws Exception + * @throws ExpectationFailedException + */ + private function assertPhptExpectation(array $sections, string $output) : void + { + $assertions = ['EXPECT' => 'assertEquals', 'EXPECTF' => 'assertStringMatchesFormat', 'EXPECTREGEX' => 'assertMatchesRegularExpression']; + $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 === '') { + throw new \PHPUnit\Runner\Exception('No PHPT expectation found'); + } + Assert::$sectionAssertion($expected, $actual); + return; + } } - if ($arguments['stopOnError']) { - $result->stopOnError(\true); + throw new \PHPUnit\Runner\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; } - if ($arguments['stopOnFailure']) { - $result->stopOnFailure(\true); + $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; } - if ($arguments['stopOnWarning']) { - $result->stopOnWarning(\true); + return \false; + } + private function runClean(array &$sections, bool $collectCoverage) : void + { + $this->phpUtil->setStdin(''); + $this->phpUtil->setArgs(''); + if (isset($sections['CLEAN'])) { + $cleanCode = $this->render($sections['CLEAN']); + $this->phpUtil->runJob($cleanCode, $this->settings($collectCoverage)); } - if ($arguments['stopOnIncomplete']) { - $result->stopOnIncomplete(\true); + } + /** + * @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 \PHPUnit\Runner\Exception('Invalid PHPT file: empty section header'); + } + $sections[$section] .= $line; } - if ($arguments['stopOnRisky']) { - $result->stopOnRisky(\true); + if (isset($sections['FILEEOF'])) { + $sections['FILE'] = rtrim($sections['FILEEOF'], "\r\n"); + unset($sections['FILEEOF']); } - if ($arguments['stopOnSkipped']) { - $result->stopOnSkipped(\true); + $this->parseExternal($sections); + if (!$this->validate($sections)) { + throw new \PHPUnit\Runner\Exception('Invalid PHPT file'); } - if ($arguments['stopOnDefect']) { - $result->stopOnDefect(\true); + foreach ($unsupportedSections as $section) { + if (isset($sections[$section])) { + throw new \PHPUnit\Runner\Exception("PHPUnit does not support PHPT {$section} sections"); + } } - if ($arguments['registerMockObjectsFromTestArgumentsRecursively']) { - $result->setRegisterMockObjectsFromTestArgumentsRecursively(\true); + 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 \PHPUnit\Runner\Exception(sprintf('Could not load --%s-- %s for PHPT file', $section . '_EXTERNAL', $testDirectory . $externalFilename)); + } + $sections[$section] = file_get_contents($testDirectory . $externalFilename); + } } - if ($this->printer === null) { - if (isset($arguments['printer'])) { - if ($arguments['printer'] instanceof \PHPUnit\TextUI\ResultPrinter) { - $this->printer = $arguments['printer']; - } elseif (is_string($arguments['printer']) && class_exists($arguments['printer'], \false)) { - try { - $reflector = new ReflectionClass($arguments['printer']); - if ($reflector->implementsInterface(\PHPUnit\TextUI\ResultPrinter::class)) { - $this->printer = $this->createPrinter($arguments['printer'], $arguments); - } - // @codeCoverageIgnoreStart - } catch (ReflectionException $e) { - throw new Exception($e->getMessage(), $e->getCode(), $e); + } + 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; } - // @codeCoverageIgnoreEnd } - } else { - $this->printer = $this->createPrinter(\PHPUnit\TextUI\DefaultResultPrinter::class, $arguments); + if (!$foundSection) { + return \false; + } + continue; + } + if (!isset($sections[$section])) { + return \false; } } - if (isset($originalExecutionOrder) && $this->printer instanceof CliTestDoxPrinter) { - assert($this->printer instanceof CliTestDoxPrinter); - $this->printer->setOriginalExecutionOrder($originalExecutionOrder); - $this->printer->setShowProgressAnimation(!$arguments['noInteraction']); + 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, bool $pathCoverage, ?string $codeCoverageCacheDirectory) : void + { + $files = $this->getCoverageFiles(); + $template = new Template(__DIR__ . '/../Util/PHP/Template/PhptTestCase.tpl'); + $composerAutoload = '\'\''; + if (defined('PHPUNIT_COMPOSER_INSTALL')) { + $composerAutoload = var_export(PHPUNIT_COMPOSER_INSTALL, \true); } - $this->write(Version::getVersionString() . "\n"); - foreach ($arguments['listeners'] as $listener) { - $result->addListener($listener); + $phar = '\'\''; + if (defined('__PHPUNIT_PHAR__')) { + $phar = var_export(__PHPUNIT_PHAR__, \true); } - $result->addListener($this->printer); - $coverageFilterFromConfigurationFile = \false; - $coverageFilterFromOption = \false; - $codeCoverageReports = 0; - if (isset($arguments['testdoxHTMLFile'])) { - $result->addListener(new HtmlResultPrinter($arguments['testdoxHTMLFile'], $arguments['testdoxGroups'], $arguments['testdoxExcludeGroups'])); + $globals = ''; + if (!empty($GLOBALS['__PHPUNIT_BOOTSTRAP'])) { + $globals = '$GLOBALS[\'__PHPUNIT_BOOTSTRAP\'] = ' . var_export($GLOBALS['__PHPUNIT_BOOTSTRAP'], \true) . ";\n"; } - if (isset($arguments['testdoxTextFile'])) { - $result->addListener(new TextResultPrinter($arguments['testdoxTextFile'], $arguments['testdoxGroups'], $arguments['testdoxExcludeGroups'])); + if ($codeCoverageCacheDirectory === null) { + $codeCoverageCacheDirectory = 'null'; + } else { + $codeCoverageCacheDirectory = "'" . $codeCoverageCacheDirectory . "'"; } - if (isset($arguments['testdoxXMLFile'])) { - $result->addListener(new XmlResultPrinter($arguments['testdoxXMLFile'])); + $template->setVar(['composerAutoload' => $composerAutoload, 'phar' => $phar, 'globals' => $globals, 'job' => $files['job'], 'coverageFile' => $files['coverage'], 'driverMethod' => $pathCoverage ? 'forLineAndPathCoverage' : 'forLineCoverage', 'codeCoverageCacheDirectory' => $codeCoverageCacheDirectory]); + file_put_contents($files['job'], $job); + $job = $template->render(); + } + private function cleanupForCoverage() : RawCodeCoverageData + { + $coverage = RawCodeCoverageData::fromXdebugWithoutPathCoverage([]); + $files = $this->getCoverageFiles(); + if (is_file($files['coverage'])) { + $buffer = @file_get_contents($files['coverage']); + if ($buffer !== \false) { + $coverage = @unserialize($buffer); + if ($coverage === \false) { + $coverage = RawCodeCoverageData::fromXdebugWithoutPathCoverage([]); + } + } } - if (isset($arguments['teamcityLogfile'])) { - $result->addListener(new TeamCity($arguments['teamcityLogfile'])); + foreach ($files as $file) { + @unlink($file); } - if (isset($arguments['junitLogfile'])) { - $result->addListener(new JUnit($arguments['junitLogfile'], $arguments['reportUselessTests'])); + 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; } - if (isset($arguments['coverageClover'])) { - $codeCoverageReports++; + 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; + } } - if (isset($arguments['coverageCobertura'])) { - $codeCoverageReports++; + return $this->getLocationHint($needle, $sections); + } + private function getCleanDiffLine(string $line) : string + { + if (preg_match('/^[\\-+]([\'\\"]?)(.*)\\1$/', $line, $matches)) { + $line = $matches[2]; } - if (isset($arguments['coverageCrap4J'])) { - $codeCoverageReports++; + 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 (isset($arguments['coverageHtml'])) { - $codeCoverageReports++; + if ($sectionName) { + $search = [$sectionName]; + } else { + $search = [ + // 'FILE', + 'EXPECT', + 'EXPECTF', + 'EXPECTREGEX', + ]; } - if (isset($arguments['coveragePHP'])) { - $codeCoverageReports++; + $sectionOffset = null; + 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 (isset($arguments['coverageText'])) { - $codeCoverageReports++; + if ($sectionName) { + // String not found in specified section, show user the start of the named section + return [['file' => realpath($this->filename), 'line' => $sectionOffset]]; } - if (isset($arguments['coverageXml'])) { - $codeCoverageReports++; + // No section specified, show user start of code + return [['file' => realpath($this->filename), 'line' => 1]]; + } + /** + * @psalm-return list + */ + private function settings(bool $collectCoverage) : array + { + $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', 'open_basedir=', 'output_buffering=Off', 'output_handler=', 'report_memleaks=0', 'report_zend_debug=0']; + if (extension_loaded('pcov')) { + if ($collectCoverage) { + $settings[] = 'pcov.enabled=1'; + } else { + $settings[] = 'pcov.enabled=0'; + } } - if ($codeCoverageReports > 0 || isset($arguments['xdebugFilterFile'])) { - if (isset($arguments['coverageFilter'])) { - if (!is_array($arguments['coverageFilter'])) { - $coverageFilterDirectories = [$arguments['coverageFilter']]; + if (extension_loaded('xdebug')) { + if (version_compare(phpversion('xdebug'), '3', '>=')) { + if ($collectCoverage) { + $settings[] = 'xdebug.mode=coverage'; } else { - $coverageFilterDirectories = $arguments['coverageFilter']; - } - foreach ($coverageFilterDirectories as $coverageFilterDirectory) { - $this->codeCoverageFilter->includeDirectory($coverageFilterDirectory); + $settings[] = 'xdebug.mode=off'; } - $coverageFilterFromOption = \true; - } - if (isset($arguments['configurationObject'])) { - assert($arguments['configurationObject'] instanceof Configuration); - $codeCoverageConfiguration = $arguments['configurationObject']->codeCoverage(); - if ($codeCoverageConfiguration->hasNonEmptyListOfFilesToBeIncludedInCodeCoverageReport()) { - $coverageFilterFromConfigurationFile = \true; - (new FilterMapper())->map($this->codeCoverageFilter, $codeCoverageConfiguration); - } - } - } - if ($codeCoverageReports > 0) { - try { - if (isset($codeCoverageConfiguration) && ($codeCoverageConfiguration->pathCoverage() || isset($arguments['pathCoverage']) && $arguments['pathCoverage'] === \true)) { - $codeCoverageDriver = (new Selector())->forLineAndPathCoverage($this->codeCoverageFilter); - } else { - $codeCoverageDriver = (new Selector())->forLineCoverage($this->codeCoverageFilter); - } - $codeCoverage = new CodeCoverage($codeCoverageDriver, $this->codeCoverageFilter); - if (isset($codeCoverageConfiguration) && $codeCoverageConfiguration->hasCacheDirectory()) { - $codeCoverage->cacheStaticAnalysis($codeCoverageConfiguration->cacheDirectory()->path()); - } - if (isset($arguments['coverageCacheDirectory'])) { - $codeCoverage->cacheStaticAnalysis($arguments['coverageCacheDirectory']); - } - $codeCoverage->excludeSubclassesOfThisClassFromUnintentionallyCoveredCodeCheck(Comparator::class); - if ($arguments['strictCoverage']) { - $codeCoverage->enableCheckForUnintentionallyCoveredCode(); - } - if (isset($arguments['ignoreDeprecatedCodeUnitsFromCodeCoverage'])) { - if ($arguments['ignoreDeprecatedCodeUnitsFromCodeCoverage']) { - $codeCoverage->ignoreDeprecatedCode(); - } else { - $codeCoverage->doNotIgnoreDeprecatedCode(); - } - } - if (isset($arguments['disableCodeCoverageIgnore'])) { - if ($arguments['disableCodeCoverageIgnore']) { - $codeCoverage->disableAnnotationsForIgnoringCode(); - } else { - $codeCoverage->enableAnnotationsForIgnoringCode(); - } - } - if (isset($arguments['configurationObject'])) { - $codeCoverageConfiguration = $arguments['configurationObject']->codeCoverage(); - if ($codeCoverageConfiguration->hasNonEmptyListOfFilesToBeIncludedInCodeCoverageReport()) { - if ($codeCoverageConfiguration->includeUncoveredFiles()) { - $codeCoverage->includeUncoveredFiles(); - } else { - $codeCoverage->excludeUncoveredFiles(); - } - if ($codeCoverageConfiguration->processUncoveredFiles()) { - $codeCoverage->processUncoveredFiles(); - } else { - $codeCoverage->doNotProcessUncoveredFiles(); - } - } - } - if ($this->codeCoverageFilter->isEmpty()) { - if (!$coverageFilterFromConfigurationFile && !$coverageFilterFromOption) { - $warnings[] = 'No filter is configured, code coverage will not be processed'; - } else { - $warnings[] = 'Incorrect filter configuration, code coverage will not be processed'; - } - unset($codeCoverage); - } - } catch (CodeCoverageException $e) { - $warnings[] = $e->getMessage(); - } - } - if ($arguments['verbose']) { - if (PHP_SAPI === 'phpdbg') { - $this->writeMessage('Runtime', 'PHPDBG ' . PHP_VERSION); } else { - $runtime = 'PHP ' . PHP_VERSION; - if (isset($codeCoverageDriver)) { - $runtime .= ' with ' . $codeCoverageDriver->nameAndVersion(); - } - $this->writeMessage('Runtime', $runtime); - } - if (isset($arguments['configurationObject'])) { - assert($arguments['configurationObject'] instanceof Configuration); - $this->writeMessage('Configuration', $arguments['configurationObject']->filename()); - } - 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)) { - $warnings[] = 'Less than 16 columns requested, number of columns set to 16'; - } - if ((new Runtime())->discardsComments()) { - $warnings[] = 'opcache.save_comments=0 set; annotations will not work'; - } - if (isset($arguments['conflictBetweenPrinterClassAndTestdox'])) { - $warnings[] = 'Directives printerClass and testdox are mutually exclusive'; - } - $warnings = array_merge($warnings, $suite->warnings()); - sort($warnings); - foreach ($warnings as $warning) { - $this->writeMessage('Warning', $warning); - } - if (isset($arguments['configurationObject'])) { - assert($arguments['configurationObject'] instanceof Configuration); - if ($arguments['configurationObject']->hasValidationErrors()) { - if ((new SchemaDetector())->detect($arguments['configurationObject']->filename())->detected()) { - $this->writeMessage('Warning', 'Your XML configuration validates against a deprecated schema.'); - $this->writeMessage('Suggestion', 'Migrate your XML configuration using "--migrate-configuration"!'); - } else { - $this->write("\n Warning - The configuration file did not pass validation!\n The following problems have been detected:\n"); - $this->write($arguments['configurationObject']->validationErrors()); - $this->write("\n Test results may not be as expected.\n\n"); - } - } - } - if (isset($arguments['xdebugFilterFile'], $codeCoverageConfiguration)) { - $this->write(PHP_EOL . 'Please note that --dump-xdebug-filter and --prepend are deprecated and will be removed in PHPUnit 10.' . PHP_EOL); - $script = (new XdebugFilterScriptGenerator())->generate($codeCoverageConfiguration); - 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 . PHP_EOL, $arguments['xdebugFilterFile'])); - exit(self::SUCCESS_EXIT); - } - $this->write("\n"); - if (isset($codeCoverage)) { - $result->setCodeCoverage($codeCoverage); - } - $result->beStrictAboutTestsThatDoNotTestAnything($arguments['reportUselessTests']); - $result->beStrictAboutOutputDuringTests($arguments['disallowTestOutput']); - $result->beStrictAboutTodoAnnotatedTests($arguments['disallowTodoAnnotatedTests']); - $result->beStrictAboutResourceUsageDuringSmallTests($arguments['beStrictAboutResourceUsageDuringSmallTests']); - if ($arguments['enforceTimeLimit'] === \true && !(new Invoker())->canInvokeWithTimeout()) { - $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 (isset($arguments['forceCoversAnnotation']) && $arguments['forceCoversAnnotation'] === \true) { - $result->forceCoversAnnotation(); - } - $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(); - $this->printer->printResult($result); - if (isset($codeCoverage)) { - 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['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['coverageCobertura'])) { - $this->codeCoverageGenerationStart('Cobertura XML'); - try { - $writer = new CoberturaReport(); - $writer->process($codeCoverage, $arguments['coverageCobertura']); - $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 PHPUnit %s', Version::id())); - $writer->process($codeCoverage, $arguments['coverageHtml']); - $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'] !== \PHPUnit\TextUI\DefaultResultPrinter::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 (isset($arguments['failOnEmptyTestSuite']) && $arguments['failOnEmptyTestSuite'] === \true && count($result) === 0) { - exit(self::FAILURE_EXIT); - } - if ($result->wasSuccessfulIgnoringWarnings()) { - if ($arguments['failOnRisky'] && !$result->allHarmless()) { - exit(self::FAILURE_EXIT); - } - if ($arguments['failOnWarning'] && $result->warningCount() > 0) { - exit(self::FAILURE_EXIT); - } - if ($arguments['failOnIncomplete'] && $result->notImplementedCount() > 0) { - exit(self::FAILURE_EXIT); - } - if ($arguments['failOnSkipped'] && $result->skippedCount() > 0) { - exit(self::FAILURE_EXIT); + $settings[] = 'xdebug.default_enable=0'; + if ($collectCoverage) { + $settings[] = 'xdebug.coverage_enable=1'; } - exit(self::SUCCESS_EXIT); - } - if ($result->errorCount() > 0) { - exit(self::EXCEPTION_EXIT); - } - if ($result->failureCount() > 0) { - exit(self::FAILURE_EXIT); } } - return $result; + return $settings; } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Runner; + +use function preg_match; +use function round; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class ResultCacheExtension implements \PHPUnit\Runner\AfterIncompleteTestHook, \PHPUnit\Runner\AfterLastTestHook, \PHPUnit\Runner\AfterRiskyTestHook, \PHPUnit\Runner\AfterSkippedTestHook, \PHPUnit\Runner\AfterSuccessfulTestHook, \PHPUnit\Runner\AfterTestErrorHook, \PHPUnit\Runner\AfterTestFailureHook, \PHPUnit\Runner\AfterTestWarningHook +{ /** - * Returns the loader to be used. + * @var TestResultCache */ - public function getLoader() : TestSuiteLoader + private $cache; + public function __construct(\PHPUnit\Runner\TestResultCache $cache) { - if ($this->loader === null) { - $this->loader = new StandardTestSuiteLoader(); - } - return $this->loader; + $this->cache = $cache; } - public function addExtension(Hook $extension) : void + public function flush() : void { - $this->extensions[] = $extension; + $this->cache->persist(); } - /** - * Override to define how to handle a failed loading of - * a test suite. - */ - protected function runFailed(string $message) : void + public function executeAfterSuccessfulTest(string $test, float $time) : void { - $this->write($message . PHP_EOL); - exit(self::FAILURE_EXIT); + $testName = $this->getTestName($test); + $this->cache->setTime($testName, round($time, 3)); } - private function createTestResult() : TestResult + public function executeAfterIncompleteTest(string $test, string $message, float $time) : void { - return new TestResult(); + $testName = $this->getTestName($test); + $this->cache->setTime($testName, round($time, 3)); + $this->cache->setState($testName, \PHPUnit\Runner\BaseTestRunner::STATUS_INCOMPLETE); } - private function write(string $buffer) : void + public function executeAfterRiskyTest(string $test, string $message, float $time) : void { - if (PHP_SAPI !== 'cli' && PHP_SAPI !== 'phpdbg') { - $buffer = htmlspecialchars($buffer); - } - if ($this->printer !== null) { - $this->printer->write($buffer); - } else { - print $buffer; + $testName = $this->getTestName($test); + $this->cache->setTime($testName, round($time, 3)); + $this->cache->setState($testName, \PHPUnit\Runner\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, \PHPUnit\Runner\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, \PHPUnit\Runner\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, \PHPUnit\Runner\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, \PHPUnit\Runner\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('/^(?\\S+::\\S+)(?:(? with data set (?:#\\d+|"[^"]+"))\\s\\()?/', $test, $matches)) { + $test = $matches['name'] . ($matches['dataname'] ?? ''); } + return $test; } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Runner; + +use function array_diff; +use function array_values; +use function basename; +use function class_exists; +use function get_declared_classes; +use function sprintf; +use function stripos; +use function strlen; +use function substr; +use PHPUnit\Framework\TestCase; +use PHPUnit\Util\FileLoader; +use ReflectionClass; +use ReflectionException; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + * + * @deprecated see https://github.com/sebastianbergmann/phpunit/issues/4039 + */ +final class StandardTestSuiteLoader implements \PHPUnit\Runner\TestSuiteLoader +{ /** - * @throws \PHPUnit\TextUI\XmlConfiguration\Exception * @throws Exception */ - private function handleConfiguration(array &$arguments) : void + public function load(string $suiteClassFile) : ReflectionClass { - if (!isset($arguments['configurationObject']) && isset($arguments['configuration'])) { - $arguments['configurationObject'] = (new Loader())->load($arguments['configuration']); - } - if (!isset($arguments['warnings'])) { - $arguments['warnings'] = []; + $suiteClassName = basename($suiteClassFile, '.php'); + $loadedClasses = get_declared_classes(); + if (!class_exists($suiteClassName, \false)) { + /* @noinspection UnusedFunctionResultInspection */ + FileLoader::checkAndLoad($suiteClassFile); + $loadedClasses = array_values(array_diff(get_declared_classes(), $loadedClasses)); + if (empty($loadedClasses)) { + throw new \PHPUnit\Runner\Exception(sprintf('Class %s could not be found in %s', $suiteClassName, $suiteClassFile)); + } } - $arguments['debug'] = $arguments['debug'] ?? \false; - $arguments['filter'] = $arguments['filter'] ?? \false; - $arguments['listeners'] = $arguments['listeners'] ?? []; - if (isset($arguments['configurationObject'])) { - (new PhpHandler())->handle($arguments['configurationObject']->php()); - $codeCoverageConfiguration = $arguments['configurationObject']->codeCoverage(); - if (!isset($arguments['noCoverage'])) { - if (!isset($arguments['coverageClover']) && $codeCoverageConfiguration->hasClover()) { - $arguments['coverageClover'] = $codeCoverageConfiguration->clover()->target()->path(); - } - if (!isset($arguments['coverageCobertura']) && $codeCoverageConfiguration->hasCobertura()) { - $arguments['coverageCobertura'] = $codeCoverageConfiguration->cobertura()->target()->path(); - } - if (!isset($arguments['coverageCrap4J']) && $codeCoverageConfiguration->hasCrap4j()) { - $arguments['coverageCrap4J'] = $codeCoverageConfiguration->crap4j()->target()->path(); - if (!isset($arguments['crap4jThreshold'])) { - $arguments['crap4jThreshold'] = $codeCoverageConfiguration->crap4j()->threshold(); - } - } - if (!isset($arguments['coverageHtml']) && $codeCoverageConfiguration->hasHtml()) { - $arguments['coverageHtml'] = $codeCoverageConfiguration->html()->target()->path(); - if (!isset($arguments['reportLowUpperBound'])) { - $arguments['reportLowUpperBound'] = $codeCoverageConfiguration->html()->lowUpperBound(); - } - if (!isset($arguments['reportHighLowerBound'])) { - $arguments['reportHighLowerBound'] = $codeCoverageConfiguration->html()->highLowerBound(); - } - } - if (!isset($arguments['coveragePHP']) && $codeCoverageConfiguration->hasPhp()) { - $arguments['coveragePHP'] = $codeCoverageConfiguration->php()->target()->path(); - } - if (!isset($arguments['coverageText']) && $codeCoverageConfiguration->hasText()) { - $arguments['coverageText'] = $codeCoverageConfiguration->text()->target()->path(); - $arguments['coverageTextShowUncoveredFiles'] = $codeCoverageConfiguration->text()->showUncoveredFiles(); - $arguments['coverageTextShowOnlySummary'] = $codeCoverageConfiguration->text()->showOnlySummary(); - } - if (!isset($arguments['coverageXml']) && $codeCoverageConfiguration->hasXml()) { - $arguments['coverageXml'] = $codeCoverageConfiguration->xml()->target()->path(); + if (!class_exists($suiteClassName, \false)) { + $offset = 0 - strlen($suiteClassName); + foreach ($loadedClasses as $loadedClass) { + // @see https://github.com/sebastianbergmann/phpunit/issues/5020 + if (stripos(substr($loadedClass, $offset - 1), '\\' . $suiteClassName) === 0 || stripos(substr($loadedClass, $offset - 1), '_' . $suiteClassName) === 0) { + $suiteClassName = $loadedClass; + break; } } - $phpunitConfiguration = $arguments['configurationObject']->phpunit(); - $arguments['backupGlobals'] = $arguments['backupGlobals'] ?? $phpunitConfiguration->backupGlobals(); - $arguments['backupStaticAttributes'] = $arguments['backupStaticAttributes'] ?? $phpunitConfiguration->backupStaticAttributes(); - $arguments['beStrictAboutChangesToGlobalState'] = $arguments['beStrictAboutChangesToGlobalState'] ?? $phpunitConfiguration->beStrictAboutChangesToGlobalState(); - $arguments['cacheResult'] = $arguments['cacheResult'] ?? $phpunitConfiguration->cacheResult(); - $arguments['colors'] = $arguments['colors'] ?? $phpunitConfiguration->colors(); - $arguments['convertDeprecationsToExceptions'] = $arguments['convertDeprecationsToExceptions'] ?? $phpunitConfiguration->convertDeprecationsToExceptions(); - $arguments['convertErrorsToExceptions'] = $arguments['convertErrorsToExceptions'] ?? $phpunitConfiguration->convertErrorsToExceptions(); - $arguments['convertNoticesToExceptions'] = $arguments['convertNoticesToExceptions'] ?? $phpunitConfiguration->convertNoticesToExceptions(); - $arguments['convertWarningsToExceptions'] = $arguments['convertWarningsToExceptions'] ?? $phpunitConfiguration->convertWarningsToExceptions(); - $arguments['processIsolation'] = $arguments['processIsolation'] ?? $phpunitConfiguration->processIsolation(); - $arguments['stopOnDefect'] = $arguments['stopOnDefect'] ?? $phpunitConfiguration->stopOnDefect(); - $arguments['stopOnError'] = $arguments['stopOnError'] ?? $phpunitConfiguration->stopOnError(); - $arguments['stopOnFailure'] = $arguments['stopOnFailure'] ?? $phpunitConfiguration->stopOnFailure(); - $arguments['stopOnWarning'] = $arguments['stopOnWarning'] ?? $phpunitConfiguration->stopOnWarning(); - $arguments['stopOnIncomplete'] = $arguments['stopOnIncomplete'] ?? $phpunitConfiguration->stopOnIncomplete(); - $arguments['stopOnRisky'] = $arguments['stopOnRisky'] ?? $phpunitConfiguration->stopOnRisky(); - $arguments['stopOnSkipped'] = $arguments['stopOnSkipped'] ?? $phpunitConfiguration->stopOnSkipped(); - $arguments['failOnEmptyTestSuite'] = $arguments['failOnEmptyTestSuite'] ?? $phpunitConfiguration->failOnEmptyTestSuite(); - $arguments['failOnIncomplete'] = $arguments['failOnIncomplete'] ?? $phpunitConfiguration->failOnIncomplete(); - $arguments['failOnRisky'] = $arguments['failOnRisky'] ?? $phpunitConfiguration->failOnRisky(); - $arguments['failOnSkipped'] = $arguments['failOnSkipped'] ?? $phpunitConfiguration->failOnSkipped(); - $arguments['failOnWarning'] = $arguments['failOnWarning'] ?? $phpunitConfiguration->failOnWarning(); - $arguments['enforceTimeLimit'] = $arguments['enforceTimeLimit'] ?? $phpunitConfiguration->enforceTimeLimit(); - $arguments['defaultTimeLimit'] = $arguments['defaultTimeLimit'] ?? $phpunitConfiguration->defaultTimeLimit(); - $arguments['timeoutForSmallTests'] = $arguments['timeoutForSmallTests'] ?? $phpunitConfiguration->timeoutForSmallTests(); - $arguments['timeoutForMediumTests'] = $arguments['timeoutForMediumTests'] ?? $phpunitConfiguration->timeoutForMediumTests(); - $arguments['timeoutForLargeTests'] = $arguments['timeoutForLargeTests'] ?? $phpunitConfiguration->timeoutForLargeTests(); - $arguments['reportUselessTests'] = $arguments['reportUselessTests'] ?? $phpunitConfiguration->beStrictAboutTestsThatDoNotTestAnything(); - $arguments['strictCoverage'] = $arguments['strictCoverage'] ?? $phpunitConfiguration->beStrictAboutCoversAnnotation(); - $arguments['ignoreDeprecatedCodeUnitsFromCodeCoverage'] = $arguments['ignoreDeprecatedCodeUnitsFromCodeCoverage'] ?? $codeCoverageConfiguration->ignoreDeprecatedCodeUnits(); - $arguments['disallowTestOutput'] = $arguments['disallowTestOutput'] ?? $phpunitConfiguration->beStrictAboutOutputDuringTests(); - $arguments['disallowTodoAnnotatedTests'] = $arguments['disallowTodoAnnotatedTests'] ?? $phpunitConfiguration->beStrictAboutTodoAnnotatedTests(); - $arguments['beStrictAboutResourceUsageDuringSmallTests'] = $arguments['beStrictAboutResourceUsageDuringSmallTests'] ?? $phpunitConfiguration->beStrictAboutResourceUsageDuringSmallTests(); - $arguments['verbose'] = $arguments['verbose'] ?? $phpunitConfiguration->verbose(); - $arguments['reverseDefectList'] = $arguments['reverseDefectList'] ?? $phpunitConfiguration->reverseDefectList(); - $arguments['forceCoversAnnotation'] = $arguments['forceCoversAnnotation'] ?? $phpunitConfiguration->forceCoversAnnotation(); - $arguments['disableCodeCoverageIgnore'] = $arguments['disableCodeCoverageIgnore'] ?? $codeCoverageConfiguration->disableCodeCoverageIgnore(); - $arguments['registerMockObjectsFromTestArgumentsRecursively'] = $arguments['registerMockObjectsFromTestArgumentsRecursively'] ?? $phpunitConfiguration->registerMockObjectsFromTestArgumentsRecursively(); - $arguments['noInteraction'] = $arguments['noInteraction'] ?? $phpunitConfiguration->noInteraction(); - $arguments['executionOrder'] = $arguments['executionOrder'] ?? $phpunitConfiguration->executionOrder(); - $arguments['resolveDependencies'] = $arguments['resolveDependencies'] ?? $phpunitConfiguration->resolveDependencies(); - if (!isset($arguments['bootstrap']) && $phpunitConfiguration->hasBootstrap()) { - $arguments['bootstrap'] = $phpunitConfiguration->bootstrap(); - } - if (!isset($arguments['cacheResultFile']) && $phpunitConfiguration->hasCacheResultFile()) { - $arguments['cacheResultFile'] = $phpunitConfiguration->cacheResultFile(); - } - if (!isset($arguments['executionOrderDefects'])) { - $arguments['executionOrderDefects'] = $phpunitConfiguration->defectsFirst() ? TestSuiteSorter::ORDER_DEFECTS_FIRST : TestSuiteSorter::ORDER_DEFAULT; - } - if ($phpunitConfiguration->conflictBetweenPrinterClassAndTestdox()) { - $arguments['conflictBetweenPrinterClassAndTestdox'] = \true; - } - $groupCliArgs = []; - if (!empty($arguments['groups'])) { - $groupCliArgs = $arguments['groups']; - } - $groupConfiguration = $arguments['configurationObject']->groups(); - if (!isset($arguments['groups']) && $groupConfiguration->hasInclude()) { - $arguments['groups'] = $groupConfiguration->include()->asArrayOfStrings(); - } - if (!isset($arguments['excludeGroups']) && $groupConfiguration->hasExclude()) { - $arguments['excludeGroups'] = array_diff($groupConfiguration->exclude()->asArrayOfStrings(), $groupCliArgs); - } - if (!isset($this->arguments['noExtensions'])) { - $extensionHandler = new ExtensionHandler(); - foreach ($arguments['configurationObject']->extensions() as $extension) { - $extensionHandler->registerExtension($extension, $this); - } - foreach ($arguments['configurationObject']->listeners() as $listener) { - $arguments['listeners'][] = $extensionHandler->createTestListenerInstance($listener); - } - unset($extensionHandler); - } - foreach ($arguments['unavailableExtensions'] as $extension) { - $arguments['warnings'][] = sprintf('Extension "%s" is not available', $extension); - } - $loggingConfiguration = $arguments['configurationObject']->logging(); - if (!isset($arguments['noLogging'])) { - if ($loggingConfiguration->hasText()) { - $arguments['listeners'][] = new \PHPUnit\TextUI\DefaultResultPrinter($loggingConfiguration->text()->target()->path(), \true); - } - if (!isset($arguments['teamcityLogfile']) && $loggingConfiguration->hasTeamCity()) { - $arguments['teamcityLogfile'] = $loggingConfiguration->teamCity()->target()->path(); - } - if (!isset($arguments['junitLogfile']) && $loggingConfiguration->hasJunit()) { - $arguments['junitLogfile'] = $loggingConfiguration->junit()->target()->path(); - } - if (!isset($arguments['testdoxHTMLFile']) && $loggingConfiguration->hasTestDoxHtml()) { - $arguments['testdoxHTMLFile'] = $loggingConfiguration->testDoxHtml()->target()->path(); - } - if (!isset($arguments['testdoxTextFile']) && $loggingConfiguration->hasTestDoxText()) { - $arguments['testdoxTextFile'] = $loggingConfiguration->testDoxText()->target()->path(); - } - if (!isset($arguments['testdoxXMLFile']) && $loggingConfiguration->hasTestDoxXml()) { - $arguments['testdoxXMLFile'] = $loggingConfiguration->testDoxXml()->target()->path(); - } - } - $testdoxGroupConfiguration = $arguments['configurationObject']->testdoxGroups(); - if (!isset($arguments['testdoxGroups']) && $testdoxGroupConfiguration->hasInclude()) { - $arguments['testdoxGroups'] = $testdoxGroupConfiguration->include()->asArrayOfStrings(); - } - if (!isset($arguments['testdoxExcludeGroups']) && $testdoxGroupConfiguration->hasExclude()) { - $arguments['testdoxExcludeGroups'] = $testdoxGroupConfiguration->exclude()->asArrayOfStrings(); - } - } - $extensionHandler = new ExtensionHandler(); - foreach ($arguments['extensions'] as $extension) { - $extensionHandler->registerExtension($extension, $this); - } - unset($extensionHandler); - $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['colors'] = $arguments['colors'] ?? \PHPUnit\TextUI\DefaultResultPrinter::COLOR_DEFAULT; - $arguments['columns'] = $arguments['columns'] ?? 80; - $arguments['convertDeprecationsToExceptions'] = $arguments['convertDeprecationsToExceptions'] ?? \false; - $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['failOnIncomplete'] = $arguments['failOnIncomplete'] ?? \false; - $arguments['failOnRisky'] = $arguments['failOnRisky'] ?? \false; - $arguments['failOnSkipped'] = $arguments['failOnSkipped'] ?? \false; - $arguments['failOnWarning'] = $arguments['failOnWarning'] ?? \false; - $arguments['groups'] = $arguments['groups'] ?? []; - $arguments['noInteraction'] = $arguments['noInteraction'] ?? \false; - $arguments['processIsolation'] = $arguments['processIsolation'] ?? \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; - if ($arguments['reportLowUpperBound'] > $arguments['reportHighLowerBound']) { - $arguments['reportLowUpperBound'] = 50; - $arguments['reportHighLowerBound'] = 90; - } - } - private function processSuiteFilters(TestSuite $suite, array $arguments) : void - { - if (!$arguments['filter'] && empty($arguments['groups']) && empty($arguments['excludeGroups']) && empty($arguments['testsCovering']) && empty($arguments['testsUsing'])) { - 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 (!empty($arguments['testsCovering'])) { - $filterFactory->addFilter(new ReflectionClass(IncludeGroupFilterIterator::class), array_map(static function (string $name) : string { - return '__phpunit_covers_' . $name; - }, $arguments['testsCovering'])); + if (!class_exists($suiteClassName, \false)) { + throw new \PHPUnit\Runner\Exception(sprintf('Class %s could not be found in %s', $suiteClassName, $suiteClassFile)); } - if (!empty($arguments['testsUsing'])) { - $filterFactory->addFilter(new ReflectionClass(IncludeGroupFilterIterator::class), array_map(static function (string $name) : string { - return '__phpunit_uses_' . $name; - }, $arguments['testsUsing'])); + try { + $class = new ReflectionClass($suiteClassName); + // @codeCoverageIgnoreStart + } catch (ReflectionException $e) { + throw new \PHPUnit\Runner\Exception($e->getMessage(), $e->getCode(), $e); } - if ($arguments['filter']) { - $filterFactory->addFilter(new ReflectionClass(NameFilterIterator::class), $arguments['filter']); + // @codeCoverageIgnoreEnd + if ($class->isSubclassOf(TestCase::class)) { + if ($class->isAbstract()) { + throw new \PHPUnit\Runner\Exception(sprintf('Class %s declared in %s is abstract', $suiteClassName, $suiteClassFile)); + } + return $class; } - $suite->injectFilter($filterFactory); - } - private function writeMessage(string $type, string $message) : void - { - if (!$this->messagePrinted) { - $this->write("\n"); + if ($class->hasMethod('suite')) { + try { + $method = $class->getMethod('suite'); + // @codeCoverageIgnoreStart + } catch (ReflectionException $e) { + throw new \PHPUnit\Runner\Exception(sprintf('Method %s::suite() declared in %s is abstract', $suiteClassName, $suiteClassFile)); + } + if (!$method->isPublic()) { + throw new \PHPUnit\Runner\Exception(sprintf('Method %s::suite() declared in %s is not public', $suiteClassName, $suiteClassFile)); + } + if (!$method->isStatic()) { + throw new \PHPUnit\Runner\Exception(sprintf('Method %s::suite() declared in %s is not static', $suiteClassName, $suiteClassFile)); + } } - $this->write(sprintf("%-15s%s\n", $type . ':', $message)); - $this->messagePrinted = \true; - } - private function createPrinter(string $class, array $arguments) : \PHPUnit\TextUI\ResultPrinter - { - $object = new $class(isset($arguments['stderr']) && $arguments['stderr'] === \true ? 'php://stderr' : null, $arguments['verbose'], $arguments['colors'], $arguments['debug'], $arguments['columns'], $arguments['reverseList']); - assert($object instanceof \PHPUnit\TextUI\ResultPrinter); - return $object; - } - private function codeCoverageGenerationStart(string $format) : void - { - $this->write(sprintf("\nGenerating code coverage report in %s format ... ", $format)); - $this->timer->start(); - } - private function codeCoverageGenerationSucceeded() : void - { - $this->write(sprintf("done [%s]\n", $this->timer->stop()->asString())); + return $class; } - private function codeCoverageGenerationFailed(\Exception $e) : void + public function reload(ReflectionClass $aClass) : ReflectionClass { - $this->write(sprintf("failed [%s]\n%s\n", $this->timer->stop()->asString(), $e->getMessage())); + return $aClass; } } name(), $filterAsArray, \true)) { - continue; - } - $testSuite = new TestSuiteObject($testSuiteConfiguration->name()); - $testSuiteEmpty = \true; - $exclude = []; - foreach ($testSuiteConfiguration->exclude()->asArray() as $file) { - $exclude[] = $file->path(); - } - foreach ($testSuiteConfiguration->directories() as $directory) { - if (!version_compare(PHP_VERSION, $directory->phpVersion(), $directory->phpVersionOperator()->asString())) { - continue; - } - $files = (new Facade())->getFilesAsArray($directory->path(), $directory->suffix(), $directory->prefix(), $exclude); - if (!empty($files)) { - $testSuite->addTestFiles($files); - $testSuiteEmpty = \false; - } elseif (strpos($directory->path(), '*') === \false && !is_dir($directory->path())) { - throw new \PHPUnit\TextUI\TestDirectoryNotFoundException($directory->path()); - } - } - foreach ($testSuiteConfiguration->files() as $file) { - if (!is_file($file->path())) { - throw new \PHPUnit\TextUI\TestFileNotFoundException($file->path()); - } - if (!version_compare(PHP_VERSION, $file->phpVersion(), $file->phpVersionOperator()->asString())) { - continue; - } - $testSuite->addTestFile($file->path()); - $testSuiteEmpty = \false; - } - if (!$testSuiteEmpty) { - $result->addTest($testSuite); - } - } - return $result; - } catch (FrameworkException $e) { - throw new \PHPUnit\TextUI\RuntimeException($e->getMessage(), $e->getCode(), $e); - } - } + 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; } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Runner; +use function array_diff; +use function array_merge; +use function array_reverse; +use function array_splice; use function count; -use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Filter\DirectoryCollection; -use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report\Clover; -use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report\Cobertura; -use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report\Crap4j; -use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report\Html; -use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report\Php; -use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report\Text; -use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report\Xml; -use PHPUnit\TextUI\XmlConfiguration\Directory; -use PHPUnit\TextUI\XmlConfiguration\Exception; -use PHPUnit\TextUI\XmlConfiguration\FileCollection; +use function in_array; +use function max; +use function shuffle; +use function usort; +use PHPUnit\Framework\DataProviderTestSuite; +use PHPUnit\Framework\Reorderable; +use PHPUnit\Framework\Test; +use PHPUnit\Framework\TestCase; +use PHPUnit\Framework\TestSuite; +use PHPUnit\Util\Test as TestUtil; +use PHPUnitPHAR\SebastianBergmann\RecursionContext\InvalidArgumentException; /** * @internal This class is not covered by the backward compatibility promise for PHPUnit - * - * @psalm-immutable */ -final class CodeCoverage +final class TestSuiteSorter { /** - * @var ?Directory - */ - private $cacheDirectory; - /** - * @var DirectoryCollection - */ - private $directories; - /** - * @var FileCollection - */ - private $files; - /** - * @var DirectoryCollection - */ - private $excludeDirectories; - /** - * @var FileCollection - */ - private $excludeFiles; - /** - * @var bool - */ - private $pathCoverage; - /** - * @var bool - */ - private $includeUncoveredFiles; - /** - * @var bool + * @var int */ - private $processUncoveredFiles; + public const ORDER_DEFAULT = 0; /** - * @var bool + * @var int */ - private $ignoreDeprecatedCodeUnits; + public const ORDER_RANDOMIZED = 1; /** - * @var bool + * @var int */ - private $disableCodeCoverageIgnore; + public const ORDER_REVERSED = 2; /** - * @var ?Clover + * @var int */ - private $clover; + public const ORDER_DEFECTS_FIRST = 3; /** - * @var ?Cobertura + * @var int */ - private $cobertura; + public const ORDER_DURATION = 4; /** - * @var ?Crap4j + * Order tests by @size annotation 'small', 'medium', 'large'. + * + * @var int */ - private $crap4j; + public const ORDER_SIZE = 5; /** - * @var ?Html + * List of sorting weights for all test result codes. A higher number gives higher priority. */ - private $html; + private const DEFECT_SORT_WEIGHT = [\PHPUnit\Runner\BaseTestRunner::STATUS_ERROR => 6, \PHPUnit\Runner\BaseTestRunner::STATUS_FAILURE => 5, \PHPUnit\Runner\BaseTestRunner::STATUS_WARNING => 4, \PHPUnit\Runner\BaseTestRunner::STATUS_INCOMPLETE => 3, \PHPUnit\Runner\BaseTestRunner::STATUS_RISKY => 2, \PHPUnit\Runner\BaseTestRunner::STATUS_SKIPPED => 1, \PHPUnit\Runner\BaseTestRunner::STATUS_UNKNOWN => 0]; + private const SIZE_SORT_WEIGHT = [TestUtil::SMALL => 1, TestUtil::MEDIUM => 2, TestUtil::LARGE => 3, TestUtil::UNKNOWN => 4]; /** - * @var ?Php + * @var array Associative array of (string => DEFECT_SORT_WEIGHT) elements */ - private $php; + private $defectSortOrder = []; /** - * @var ?Text + * @var TestResultCache */ - private $text; + private $cache; /** - * @var ?Xml + * @var array A list of normalized names of tests before reordering */ - private $xml; - public function __construct(?Directory $cacheDirectory, DirectoryCollection $directories, FileCollection $files, DirectoryCollection $excludeDirectories, FileCollection $excludeFiles, bool $pathCoverage, bool $includeUncoveredFiles, bool $processUncoveredFiles, bool $ignoreDeprecatedCodeUnits, bool $disableCodeCoverageIgnore, ?Clover $clover, ?Cobertura $cobertura, ?Crap4j $crap4j, ?Html $html, ?Php $php, ?Text $text, ?Xml $xml) - { - $this->cacheDirectory = $cacheDirectory; - $this->directories = $directories; - $this->files = $files; - $this->excludeDirectories = $excludeDirectories; - $this->excludeFiles = $excludeFiles; - $this->pathCoverage = $pathCoverage; - $this->includeUncoveredFiles = $includeUncoveredFiles; - $this->processUncoveredFiles = $processUncoveredFiles; - $this->ignoreDeprecatedCodeUnits = $ignoreDeprecatedCodeUnits; - $this->disableCodeCoverageIgnore = $disableCodeCoverageIgnore; - $this->clover = $clover; - $this->cobertura = $cobertura; - $this->crap4j = $crap4j; - $this->html = $html; - $this->php = $php; - $this->text = $text; - $this->xml = $xml; - } + private $originalExecutionOrder = []; /** - * @psalm-assert-if-true !null $this->cacheDirectory + * @var array A list of normalized names of tests affected by reordering */ - public function hasCacheDirectory() : bool + private $executionOrder = []; + public function __construct(?\PHPUnit\Runner\TestResultCache $cache = null) { - return $this->cacheDirectory !== null; + $this->cache = $cache ?? new \PHPUnit\Runner\NullTestResultCache(); } /** * @throws Exception + * @throws InvalidArgumentException */ - public function cacheDirectory() : Directory + public function reorderTestsInSuite(Test $suite, int $order, bool $resolveDependencies, int $orderDefects, bool $isRootTestSuite = \true) : void { - if (!$this->hasCacheDirectory()) { - throw new Exception('No cache directory has been configured'); + $allowedOrders = [self::ORDER_DEFAULT, self::ORDER_REVERSED, self::ORDER_RANDOMIZED, self::ORDER_DURATION, self::ORDER_SIZE]; + if (!in_array($order, $allowedOrders, \true)) { + throw new \PHPUnit\Runner\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 \PHPUnit\Runner\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); } - return $this->cacheDirectory; - } - public function hasNonEmptyListOfFilesToBeIncludedInCodeCoverageReport() : bool - { - return count($this->directories) > 0 || count($this->files) > 0; } - public function directories() : DirectoryCollection + public function getOriginalExecutionOrder() : array { - return $this->directories; + return $this->originalExecutionOrder; } - public function files() : FileCollection + public function getExecutionOrder() : array { - return $this->files; + return $this->executionOrder; } - public function excludeDirectories() : DirectoryCollection + private function sort(TestSuite $suite, int $order, bool $resolveDependencies, int $orderDefects) : void { - return $this->excludeDirectories; + 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) { + /** @var TestCase[] $tests */ + $tests = $suite->tests(); + $suite->setTests($this->resolveDependencies($tests)); + } } - public function excludeFiles() : FileCollection + /** + * @throws InvalidArgumentException + */ + private function addSuiteToDefectSortOrder(TestSuite $suite) : void { - return $this->excludeFiles; + $max = 0; + foreach ($suite->tests() as $test) { + if (!$test instanceof Reorderable) { + continue; + } + if (!isset($this->defectSortOrder[$test->sortId()])) { + $this->defectSortOrder[$test->sortId()] = self::DEFECT_SORT_WEIGHT[$this->cache->getState($test->sortId())]; + $max = max($max, $this->defectSortOrder[$test->sortId()]); + } + } + $this->defectSortOrder[$suite->sortId()] = $max; } - public function pathCoverage() : bool + private function reverse(array $tests) : array { - return $this->pathCoverage; + return array_reverse($tests); } - public function includeUncoveredFiles() : bool + private function randomize(array $tests) : array { - return $this->includeUncoveredFiles; + shuffle($tests); + return $tests; } - public function ignoreDeprecatedCodeUnits() : bool + private function sortDefectsFirst(array $tests) : array { - return $this->ignoreDeprecatedCodeUnits; - } - public function disableCodeCoverageIgnore() : bool - { - return $this->disableCodeCoverageIgnore; - } - public function processUncoveredFiles() : bool - { - return $this->processUncoveredFiles; - } - /** - * @psalm-assert-if-true !null $this->clover - */ - public function hasClover() : bool - { - return $this->clover !== null; + usort( + $tests, + /** + * @throws InvalidArgumentException + */ + function ($left, $right) { + return $this->cmpDefectPriorityAndTime($left, $right); + } + ); + return $tests; } - /** - * @throws Exception - */ - public function clover() : Clover + private function sortByDuration(array $tests) : array { - if (!$this->hasClover()) { - throw new Exception('Code Coverage report "Clover XML" has not been configured'); - } - return $this->clover; + usort( + $tests, + /** + * @throws InvalidArgumentException + */ + function ($left, $right) { + return $this->cmpDuration($left, $right); + } + ); + return $tests; } - /** - * @psalm-assert-if-true !null $this->cobertura - */ - public function hasCobertura() : bool + private function sortBySize(array $tests) : array { - return $this->cobertura !== null; + usort( + $tests, + /** + * @throws InvalidArgumentException + */ + function ($left, $right) { + return $this->cmpSize($left, $right); + } + ); + return $tests; } /** - * @throws Exception + * 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 InvalidArgumentException */ - public function cobertura() : Cobertura + private function cmpDefectPriorityAndTime(Test $a, Test $b) : int { - if (!$this->hasCobertura()) { - throw new Exception('Code Coverage report "Cobertura XML" has not been configured'); + if (!($a instanceof Reorderable && $b instanceof Reorderable)) { + return 0; } - return $this->cobertura; - } - /** - * @psalm-assert-if-true !null $this->crap4j - */ - public function hasCrap4j() : bool - { - return $this->crap4j !== null; - } - /** - * @throws Exception - */ - public function crap4j() : Crap4j - { - if (!$this->hasCrap4j()) { - throw new Exception('Code Coverage report "Crap4J" has not been configured'); + $priorityA = $this->defectSortOrder[$a->sortId()] ?? 0; + $priorityB = $this->defectSortOrder[$b->sortId()] ?? 0; + if ($priorityB <=> $priorityA) { + // Sort defect weight descending + return $priorityB <=> $priorityA; } - return $this->crap4j; - } - /** - * @psalm-assert-if-true !null $this->html - */ - public function hasHtml() : bool - { - return $this->html !== null; - } - /** - * @throws Exception - */ - public function html() : Html - { - if (!$this->hasHtml()) { - throw new Exception('Code Coverage report "HTML" has not been configured'); + if ($priorityA || $priorityB) { + return $this->cmpDuration($a, $b); } - return $this->html; - } - /** - * @psalm-assert-if-true !null $this->php - */ - public function hasPhp() : bool - { - return $this->php !== null; + // do not change execution order + return 0; } /** - * @throws Exception + * Compares test duration for sorting tests by duration ascending. + * + * @throws InvalidArgumentException */ - public function php() : Php + private function cmpDuration(Test $a, Test $b) : int { - if (!$this->hasPhp()) { - throw new Exception('Code Coverage report "PHP" has not been configured'); + if (!($a instanceof Reorderable && $b instanceof Reorderable)) { + return 0; } - return $this->php; - } - /** - * @psalm-assert-if-true !null $this->text - */ - public function hasText() : bool - { - return $this->text !== null; + return $this->cache->getTime($a->sortId()) <=> $this->cache->getTime($b->sortId()); } /** - * @throws Exception + * Compares test size for sorting tests small->medium->large->unknown. */ - public function text() : Text + private function cmpSize(Test $a, Test $b) : int { - if (!$this->hasText()) { - throw new Exception('Code Coverage report "Text" has not been configured'); - } - return $this->text; + $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]; } /** - * @psalm-assert-if-true !null $this->xml + * 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 $tests + * + * @return array */ - public function hasXml() : bool + private function resolveDependencies(array $tests) : array { - return $this->xml !== null; + $newTestOrder = []; + $i = 0; + $provided = []; + do { + if ([] === array_diff($tests[$i]->requires(), $provided)) { + $provided = array_merge($provided, $tests[$i]->provides()); + $newTestOrder = array_merge($newTestOrder, array_splice($tests, $i, 1)); + $i = 0; + } else { + $i++; + } + } while (!empty($tests) && $i < count($tests)); + return array_merge($newTestOrder, $tests); } /** - * @throws Exception + * @throws InvalidArgumentException */ - public function xml() : Xml + private function calculateTestExecutionOrder(Test $suite) : array { - if (!$this->hasXml()) { - throw new Exception('Code Coverage report "XML" has not been configured'); + $tests = []; + if ($suite instanceof TestSuite) { + foreach ($suite->tests() as $test) { + if (!$test instanceof TestSuite && $test instanceof Reorderable) { + $tests[] = $test->sortId(); + } else { + $tests = array_merge($tests, $this->calculateTestExecutionOrder($test)); + } + } } - return $this->xml; + return $tests; } } path = $path; - $this->prefix = $prefix; - $this->suffix = $suffix; - $this->group = $group; - } - public function path() : string - { - return $this->path; - } - public function prefix() : string - { - return $this->prefix; - } - public function suffix() : string - { - return $this->suffix; - } - public function group() : string - { - return $this->group; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Filter; - -use function count; -use Countable; -use IteratorAggregate; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - * - * @psalm-immutable - * - * @template-implements IteratorAggregate - */ -final class DirectoryCollection implements Countable, IteratorAggregate -{ - /** - * @var Directory[] - */ - private $directories; - /** - * @param Directory[] $directories - */ - public static function fromArray(array $directories) : self - { - return new self(...$directories); - } - private function __construct(\PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Filter\Directory ...$directories) - { - $this->directories = $directories; - } - /** - * @return Directory[] - */ - public function asArray() : array - { - return $this->directories; - } - public function count() : int - { - return count($this->directories); - } - public function getIterator() : \PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Filter\DirectoryCollectionIterator - { - return new \PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Filter\DirectoryCollectionIterator($this); - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Filter; - -use function count; -use function iterator_count; -use Countable; -use Iterator; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - * - * @template-implements Iterator - */ -final class DirectoryCollectionIterator implements Countable, Iterator -{ - /** - * @var Directory[] - */ - private $directories; + private static $version = ''; /** - * @var int + * Returns the current version of PHPUnit. + * + * @psalm-return non-empty-string */ - private $position; - public function __construct(\PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Filter\DirectoryCollection $directories) - { - $this->directories = $directories->asArray(); - } - public function count() : int - { - return iterator_count($this); - } - public function rewind() : void - { - $this->position = 0; - } - public function valid() : bool - { - return $this->position < count($this->directories); - } - public function key() : int - { - return $this->position; - } - public function current() : \PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Filter\Directory - { - return $this->directories[$this->position]; - } - public function next() : void - { - $this->position++; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TextUI\XmlConfiguration\CodeCoverage; - -use PHPUnit\SebastianBergmann\CodeCoverage\Filter; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class FilterMapper -{ - public function map(Filter $filter, \PHPUnit\TextUI\XmlConfiguration\CodeCoverage\CodeCoverage $configuration) : void + public static function id() : string { - foreach ($configuration->directories() as $directory) { - $filter->includeDirectory($directory->path(), $directory->suffix(), $directory->prefix()); - } - foreach ($configuration->files() as $file) { - $filter->includeFile($file->path()); - } - foreach ($configuration->excludeDirectories() as $directory) { - $filter->excludeDirectory($directory->path(), $directory->suffix(), $directory->prefix()); + if (self::$pharVersion !== '') { + return self::$pharVersion; } - foreach ($configuration->excludeFiles() as $file) { - $filter->excludeFile($file->path()); + if (self::$version === '') { + self::$version = (new VersionId('9.6.19', dirname(__DIR__, 2)))->getVersion(); + assert(!empty(self::$version)); } + return self::$version; } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report; - -use PHPUnit\TextUI\XmlConfiguration\File; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - * - * @psalm-immutable - */ -final class Clover -{ /** - * @var File + * @psalm-return non-empty-string */ - private $target; - public function __construct(File $target) - { - $this->target = $target; - } - public function target() : File + public static function series() : string { - return $this->target; + if (strpos(self::id(), '-')) { + $version = explode('-', self::id())[0]; + } else { + $version = self::id(); + } + return implode('.', array_slice(explode('.', $version), 0, 2)); } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report; - -use PHPUnit\TextUI\XmlConfiguration\File; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - * - * @psalm-immutable - */ -final class Cobertura -{ /** - * @var File + * @psalm-return non-empty-string */ - private $target; - public function __construct(File $target) - { - $this->target = $target; - } - public function target() : File + public static function getVersionString() : string { - return $this->target; + return 'PHPUnit ' . self::id() . ' by Sebastian Bergmann and contributors.'; } } target = $target; - $this->threshold = $threshold; - } - public function target() : File - { - return $this->target; - } - public function threshold() : int + private const LONG_OPTIONS = ['atleast-version=', 'prepend=', 'bootstrap=', 'cache-result', 'do-not-cache-result', 'cache-result-file=', 'check-version', 'colors==', 'columns=', 'configuration=', 'coverage-cache=', 'warm-coverage-cache', 'coverage-filter=', 'coverage-clover=', 'coverage-cobertura=', 'coverage-crap4j=', 'coverage-html=', 'coverage-php=', 'coverage-text==', 'coverage-xml=', 'path-coverage', 'debug', 'disallow-test-output', 'disallow-resource-usage', 'disallow-todo-tests', 'default-time-limit=', 'enforce-time-limit', 'exclude-group=', 'extensions=', 'filter=', 'generate-configuration', 'globals-backup', 'group=', 'covers=', 'uses=', 'help', 'resolve-dependencies', 'ignore-dependencies', 'include-path=', 'list-groups', 'list-suites', 'list-tests', 'list-tests-xml=', 'loader=', 'log-junit=', 'log-teamcity=', 'migrate-configuration', 'no-configuration', 'no-coverage', 'no-logging', 'no-interaction', 'no-extensions', 'order-by=', 'printer=', 'process-isolation', 'repeat=', 'dont-report-useless-tests', 'random-order', 'random-order-seed=', 'reverse-order', 'reverse-list', 'static-backup', 'stderr', 'stop-on-defect', 'stop-on-error', 'stop-on-failure', 'stop-on-warning', 'stop-on-incomplete', 'stop-on-risky', 'stop-on-skipped', 'fail-on-empty-test-suite', 'fail-on-incomplete', 'fail-on-risky', 'fail-on-skipped', 'fail-on-warning', 'strict-coverage', 'disable-coverage-ignore', 'strict-global-state', 'teamcity', 'testdox', 'testdox-group=', 'testdox-exclude-group=', 'testdox-html=', 'testdox-text=', 'testdox-xml=', 'test-suffix=', 'testsuite=', 'verbose', 'version', 'whitelist=', 'dump-xdebug-filter=']; + private const SHORT_OPTIONS = 'd:c:hv'; + public function fromParameters(array $parameters, array $additionalLongOptions) : \PHPUnit\TextUI\CliArguments\Configuration { - return $this->threshold; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report; - -use PHPUnit\TextUI\XmlConfiguration\Directory; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - * - * @psalm-immutable - */ -final class Html -{ - /** - * @var Directory - */ - private $target; - /** - * @var int - */ - private $lowUpperBound; - /** - * @var int - */ - private $highLowerBound; - public function __construct(Directory $target, int $lowUpperBound, int $highLowerBound) - { - $this->target = $target; - $this->lowUpperBound = $lowUpperBound; - $this->highLowerBound = $highLowerBound; - } - public function target() : Directory - { - return $this->target; - } - public function lowUpperBound() : int - { - return $this->lowUpperBound; - } - public function highLowerBound() : int - { - return $this->highLowerBound; + try { + $options = (new CliParser())->parse($parameters, self::SHORT_OPTIONS, array_merge(self::LONG_OPTIONS, $additionalLongOptions)); + } catch (CliParserException $e) { + throw new \PHPUnit\TextUI\CliArguments\Exception($e->getMessage(), $e->getCode(), $e); + } + $argument = null; + $atLeastVersion = null; + $backupGlobals = null; + $backupStaticAttributes = null; + $beStrictAboutChangesToGlobalState = null; + $beStrictAboutResourceUsageDuringSmallTests = null; + $bootstrap = null; + $cacheResult = null; + $cacheResultFile = null; + $checkVersion = null; + $colors = null; + $columns = null; + $configuration = null; + $coverageCacheDirectory = null; + $warmCoverageCache = null; + $coverageFilter = null; + $coverageClover = null; + $coverageCobertura = null; + $coverageCrap4J = null; + $coverageHtml = null; + $coveragePhp = null; + $coverageText = null; + $coverageTextShowUncoveredFiles = null; + $coverageTextShowOnlySummary = null; + $coverageXml = null; + $pathCoverage = null; + $debug = null; + $defaultTimeLimit = null; + $disableCodeCoverageIgnore = null; + $disallowTestOutput = null; + $disallowTodoAnnotatedTests = null; + $enforceTimeLimit = null; + $excludeGroups = null; + $executionOrder = null; + $executionOrderDefects = null; + $extensions = []; + $unavailableExtensions = []; + $failOnEmptyTestSuite = null; + $failOnIncomplete = null; + $failOnRisky = null; + $failOnSkipped = null; + $failOnWarning = null; + $filter = null; + $generateConfiguration = null; + $migrateConfiguration = null; + $groups = null; + $testsCovering = null; + $testsUsing = null; + $help = null; + $includePath = null; + $iniSettings = []; + $junitLogfile = null; + $listGroups = null; + $listSuites = null; + $listTests = null; + $listTestsXml = null; + $loader = null; + $noCoverage = null; + $noExtensions = null; + $noInteraction = null; + $noLogging = null; + $printer = null; + $processIsolation = null; + $randomOrderSeed = null; + $repeat = null; + $reportUselessTests = null; + $resolveDependencies = null; + $reverseList = null; + $stderr = null; + $strictCoverage = null; + $stopOnDefect = null; + $stopOnError = null; + $stopOnFailure = null; + $stopOnIncomplete = null; + $stopOnRisky = null; + $stopOnSkipped = null; + $stopOnWarning = null; + $teamcityLogfile = null; + $testdoxExcludeGroups = null; + $testdoxGroups = null; + $testdoxHtmlFile = null; + $testdoxTextFile = null; + $testdoxXmlFile = null; + $testSuffixes = null; + $testSuite = null; + $unrecognizedOptions = []; + $unrecognizedOrderBy = null; + $useDefaultConfiguration = null; + $verbose = null; + $version = null; + $xdebugFilterFile = null; + if (isset($options[1][0])) { + $argument = $options[1][0]; + } + foreach ($options[0] as $option) { + switch ($option[0]) { + case '--colors': + $colors = $option[1] ?: DefaultResultPrinter::COLOR_AUTO; + break; + case '--bootstrap': + $bootstrap = $option[1]; + break; + case '--cache-result': + $cacheResult = \true; + break; + case '--do-not-cache-result': + $cacheResult = \false; + break; + case '--cache-result-file': + $cacheResultFile = $option[1]; + break; + case '--columns': + if (is_numeric($option[1])) { + $columns = (int) $option[1]; + } elseif ($option[1] === 'max') { + $columns = 'max'; + } + break; + case 'c': + case '--configuration': + $configuration = $option[1]; + break; + case '--coverage-cache': + $coverageCacheDirectory = $option[1]; + break; + case '--warm-coverage-cache': + $warmCoverageCache = \true; + break; + case '--coverage-clover': + $coverageClover = $option[1]; + break; + case '--coverage-cobertura': + $coverageCobertura = $option[1]; + break; + case '--coverage-crap4j': + $coverageCrap4J = $option[1]; + break; + case '--coverage-html': + $coverageHtml = $option[1]; + break; + case '--coverage-php': + $coveragePhp = $option[1]; + break; + case '--coverage-text': + if ($option[1] === null) { + $option[1] = 'php://stdout'; + } + $coverageText = $option[1]; + $coverageTextShowUncoveredFiles = \false; + $coverageTextShowOnlySummary = \false; + break; + case '--coverage-xml': + $coverageXml = $option[1]; + break; + case '--path-coverage': + $pathCoverage = \true; + break; + case 'd': + $tmp = explode('=', $option[1]); + if (isset($tmp[0])) { + if (isset($tmp[1])) { + $iniSettings[$tmp[0]] = $tmp[1]; + } else { + $iniSettings[$tmp[0]] = '1'; + } + } + break; + case '--debug': + $debug = \true; + break; + case 'h': + case '--help': + $help = \true; + break; + case '--filter': + $filter = $option[1]; + break; + case '--testsuite': + $testSuite = $option[1]; + break; + case '--generate-configuration': + $generateConfiguration = \true; + break; + case '--migrate-configuration': + $migrateConfiguration = \true; + break; + case '--group': + $groups = explode(',', $option[1]); + break; + case '--exclude-group': + $excludeGroups = explode(',', $option[1]); + break; + case '--covers': + $testsCovering = array_map('strtolower', explode(',', $option[1])); + break; + case '--uses': + $testsUsing = array_map('strtolower', explode(',', $option[1])); + break; + case '--test-suffix': + $testSuffixes = explode(',', $option[1]); + break; + case '--include-path': + $includePath = $option[1]; + break; + case '--list-groups': + $listGroups = \true; + break; + case '--list-suites': + $listSuites = \true; + break; + case '--list-tests': + $listTests = \true; + break; + case '--list-tests-xml': + $listTestsXml = $option[1]; + break; + case '--printer': + $printer = $option[1]; + break; + case '--loader': + $loader = $option[1]; + break; + case '--log-junit': + $junitLogfile = $option[1]; + break; + case '--log-teamcity': + $teamcityLogfile = $option[1]; + break; + case '--order-by': + foreach (explode(',', $option[1]) as $order) { + switch ($order) { + case 'default': + $executionOrder = TestSuiteSorter::ORDER_DEFAULT; + $executionOrderDefects = TestSuiteSorter::ORDER_DEFAULT; + $resolveDependencies = \true; + break; + case 'defects': + $executionOrderDefects = TestSuiteSorter::ORDER_DEFECTS_FIRST; + break; + case 'depends': + $resolveDependencies = \true; + break; + case 'duration': + $executionOrder = TestSuiteSorter::ORDER_DURATION; + break; + case 'no-depends': + $resolveDependencies = \false; + break; + case 'random': + $executionOrder = TestSuiteSorter::ORDER_RANDOMIZED; + break; + case 'reverse': + $executionOrder = TestSuiteSorter::ORDER_REVERSED; + break; + case 'size': + $executionOrder = TestSuiteSorter::ORDER_SIZE; + break; + default: + $unrecognizedOrderBy = $order; + } + } + break; + case '--process-isolation': + $processIsolation = \true; + break; + case '--repeat': + $repeat = (int) $option[1]; + break; + case '--stderr': + $stderr = \true; + break; + case '--stop-on-defect': + $stopOnDefect = \true; + break; + case '--stop-on-error': + $stopOnError = \true; + break; + case '--stop-on-failure': + $stopOnFailure = \true; + break; + case '--stop-on-warning': + $stopOnWarning = \true; + break; + case '--stop-on-incomplete': + $stopOnIncomplete = \true; + break; + case '--stop-on-risky': + $stopOnRisky = \true; + break; + case '--stop-on-skipped': + $stopOnSkipped = \true; + break; + case '--fail-on-empty-test-suite': + $failOnEmptyTestSuite = \true; + break; + case '--fail-on-incomplete': + $failOnIncomplete = \true; + break; + case '--fail-on-risky': + $failOnRisky = \true; + break; + case '--fail-on-skipped': + $failOnSkipped = \true; + break; + case '--fail-on-warning': + $failOnWarning = \true; + break; + case '--teamcity': + $printer = TeamCity::class; + break; + case '--testdox': + $printer = CliTestDoxPrinter::class; + break; + case '--testdox-group': + $testdoxGroups = explode(',', $option[1]); + break; + case '--testdox-exclude-group': + $testdoxExcludeGroups = explode(',', $option[1]); + break; + case '--testdox-html': + $testdoxHtmlFile = $option[1]; + break; + case '--testdox-text': + $testdoxTextFile = $option[1]; + break; + case '--testdox-xml': + $testdoxXmlFile = $option[1]; + break; + case '--no-configuration': + $useDefaultConfiguration = \false; + break; + case '--extensions': + foreach (explode(',', $option[1]) as $extensionClass) { + if (!class_exists($extensionClass)) { + $unavailableExtensions[] = $extensionClass; + continue; + } + $extensions[] = new Extension($extensionClass, '', []); + } + break; + case '--no-extensions': + $noExtensions = \true; + break; + case '--no-coverage': + $noCoverage = \true; + break; + case '--no-logging': + $noLogging = \true; + break; + case '--no-interaction': + $noInteraction = \true; + break; + case '--globals-backup': + $backupGlobals = \true; + break; + case '--static-backup': + $backupStaticAttributes = \true; + break; + case 'v': + case '--verbose': + $verbose = \true; + break; + case '--atleast-version': + $atLeastVersion = $option[1]; + break; + case '--version': + $version = \true; + break; + case '--dont-report-useless-tests': + $reportUselessTests = \false; + break; + case '--strict-coverage': + $strictCoverage = \true; + break; + case '--disable-coverage-ignore': + $disableCodeCoverageIgnore = \true; + break; + case '--strict-global-state': + $beStrictAboutChangesToGlobalState = \true; + break; + case '--disallow-test-output': + $disallowTestOutput = \true; + break; + case '--disallow-resource-usage': + $beStrictAboutResourceUsageDuringSmallTests = \true; + break; + case '--default-time-limit': + $defaultTimeLimit = (int) $option[1]; + break; + case '--enforce-time-limit': + $enforceTimeLimit = \true; + break; + case '--disallow-todo-tests': + $disallowTodoAnnotatedTests = \true; + break; + case '--reverse-list': + $reverseList = \true; + break; + case '--check-version': + $checkVersion = \true; + break; + case '--coverage-filter': + case '--whitelist': + if ($coverageFilter === null) { + $coverageFilter = []; + } + $coverageFilter[] = $option[1]; + break; + case '--random-order': + $executionOrder = TestSuiteSorter::ORDER_RANDOMIZED; + break; + case '--random-order-seed': + $randomOrderSeed = (int) $option[1]; + break; + case '--resolve-dependencies': + $resolveDependencies = \true; + break; + case '--ignore-dependencies': + $resolveDependencies = \false; + break; + case '--reverse-order': + $executionOrder = TestSuiteSorter::ORDER_REVERSED; + break; + case '--dump-xdebug-filter': + $xdebugFilterFile = $option[1]; + break; + default: + $unrecognizedOptions[str_replace('--', '', $option[0])] = $option[1]; + } + } + if (empty($extensions)) { + $extensions = null; + } + if (empty($unavailableExtensions)) { + $unavailableExtensions = null; + } + if (empty($iniSettings)) { + $iniSettings = null; + } + if (empty($coverageFilter)) { + $coverageFilter = null; + } + return new \PHPUnit\TextUI\CliArguments\Configuration($argument, $atLeastVersion, $backupGlobals, $backupStaticAttributes, $beStrictAboutChangesToGlobalState, $beStrictAboutResourceUsageDuringSmallTests, $bootstrap, $cacheResult, $cacheResultFile, $checkVersion, $colors, $columns, $configuration, $coverageClover, $coverageCobertura, $coverageCrap4J, $coverageHtml, $coveragePhp, $coverageText, $coverageTextShowUncoveredFiles, $coverageTextShowOnlySummary, $coverageXml, $pathCoverage, $coverageCacheDirectory, $warmCoverageCache, $debug, $defaultTimeLimit, $disableCodeCoverageIgnore, $disallowTestOutput, $disallowTodoAnnotatedTests, $enforceTimeLimit, $excludeGroups, $executionOrder, $executionOrderDefects, $extensions, $unavailableExtensions, $failOnEmptyTestSuite, $failOnIncomplete, $failOnRisky, $failOnSkipped, $failOnWarning, $filter, $generateConfiguration, $migrateConfiguration, $groups, $testsCovering, $testsUsing, $help, $includePath, $iniSettings, $junitLogfile, $listGroups, $listSuites, $listTests, $listTestsXml, $loader, $noCoverage, $noExtensions, $noInteraction, $noLogging, $printer, $processIsolation, $randomOrderSeed, $repeat, $reportUselessTests, $resolveDependencies, $reverseList, $stderr, $strictCoverage, $stopOnDefect, $stopOnError, $stopOnFailure, $stopOnIncomplete, $stopOnRisky, $stopOnSkipped, $stopOnWarning, $teamcityLogfile, $testdoxExcludeGroups, $testdoxGroups, $testdoxHtmlFile, $testdoxTextFile, $testdoxXmlFile, $testSuffixes, $testSuite, $unrecognizedOptions, $unrecognizedOrderBy, $useDefaultConfiguration, $verbose, $version, $coverageFilter, $xdebugFilterFile); } } target = $target; - } - public function target() : File - { - return $this->target; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report; - -use PHPUnit\TextUI\XmlConfiguration\File; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - * - * @psalm-immutable - */ -final class Text -{ + private $argument; /** - * @var File + * @var ?string */ - private $target; + private $atLeastVersion; /** - * @var bool + * @var ?bool */ - private $showUncoveredFiles; + private $backupGlobals; /** - * @var bool + * @var ?bool */ - private $showOnlySummary; - public function __construct(File $target, bool $showUncoveredFiles, bool $showOnlySummary) - { - $this->target = $target; - $this->showUncoveredFiles = $showUncoveredFiles; - $this->showOnlySummary = $showOnlySummary; - } - public function target() : File - { - return $this->target; - } - public function showUncoveredFiles() : bool - { - return $this->showUncoveredFiles; - } - public function showOnlySummary() : bool - { - return $this->showOnlySummary; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report; - -use PHPUnit\TextUI\XmlConfiguration\Directory; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - * - * @psalm-immutable - */ -final class Xml -{ + private $backupStaticAttributes; /** - * @var Directory + * @var ?bool */ - private $target; - public function __construct(Directory $target) - { - $this->target = $target; - } - public function target() : Directory - { - return $this->target; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TextUI\XmlConfiguration; - -use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\CodeCoverage; -use PHPUnit\TextUI\XmlConfiguration\Logging\Logging; -use PHPUnit\Util\Xml\ValidationResult; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - * - * @psalm-immutable - */ -final class Configuration -{ + private $beStrictAboutChangesToGlobalState; /** - * @var string + * @var ?bool */ - private $filename; + private $beStrictAboutResourceUsageDuringSmallTests; /** - * @var ValidationResult + * @var ?string */ - private $validationResult; + private $bootstrap; /** - * @var ExtensionCollection + * @var ?bool */ - private $extensions; + private $cacheResult; /** - * @var CodeCoverage + * @var ?string */ - private $codeCoverage; + private $cacheResultFile; /** - * @var Groups + * @var ?bool */ - private $groups; + private $checkVersion; /** - * @var Groups + * @var ?string */ - private $testdoxGroups; + private $colors; /** - * @var ExtensionCollection + * @var null|int|string */ - private $listeners; + private $columns; /** - * @var Logging + * @var ?string */ - private $logging; + private $configuration; /** - * @var Php + * @var null|string[] */ - private $php; + private $coverageFilter; /** - * @var PHPUnit + * @var ?string */ - private $phpunit; + private $coverageClover; /** - * @var TestSuiteCollection + * @var ?string */ - private $testSuite; - public function __construct(string $filename, ValidationResult $validationResult, \PHPUnit\TextUI\XmlConfiguration\ExtensionCollection $extensions, CodeCoverage $codeCoverage, \PHPUnit\TextUI\XmlConfiguration\Groups $groups, \PHPUnit\TextUI\XmlConfiguration\Groups $testdoxGroups, \PHPUnit\TextUI\XmlConfiguration\ExtensionCollection $listeners, Logging $logging, \PHPUnit\TextUI\XmlConfiguration\Php $php, \PHPUnit\TextUI\XmlConfiguration\PHPUnit $phpunit, \PHPUnit\TextUI\XmlConfiguration\TestSuiteCollection $testSuite) - { - $this->filename = $filename; - $this->validationResult = $validationResult; - $this->extensions = $extensions; - $this->codeCoverage = $codeCoverage; - $this->groups = $groups; - $this->testdoxGroups = $testdoxGroups; - $this->listeners = $listeners; - $this->logging = $logging; - $this->php = $php; - $this->phpunit = $phpunit; - $this->testSuite = $testSuite; - } - public function filename() : string - { - return $this->filename; - } - public function hasValidationErrors() : bool - { - return $this->validationResult->hasValidationErrors(); - } - public function validationErrors() : string - { - return $this->validationResult->asString(); - } - public function extensions() : \PHPUnit\TextUI\XmlConfiguration\ExtensionCollection - { - return $this->extensions; - } - public function codeCoverage() : CodeCoverage - { - return $this->codeCoverage; - } - public function groups() : \PHPUnit\TextUI\XmlConfiguration\Groups - { - return $this->groups; - } - public function testdoxGroups() : \PHPUnit\TextUI\XmlConfiguration\Groups - { - return $this->testdoxGroups; - } - public function listeners() : \PHPUnit\TextUI\XmlConfiguration\ExtensionCollection - { - return $this->listeners; - } - public function logging() : Logging - { - return $this->logging; - } - public function php() : \PHPUnit\TextUI\XmlConfiguration\Php - { - return $this->php; - } - public function phpunit() : \PHPUnit\TextUI\XmlConfiguration\PHPUnit - { - return $this->phpunit; - } - public function testSuite() : \PHPUnit\TextUI\XmlConfiguration\TestSuiteCollection - { - return $this->testSuite; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TextUI\XmlConfiguration; - -use RuntimeException; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class Exception extends RuntimeException implements \PHPUnit\Exception -{ -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TextUI\XmlConfiguration; - -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - * - * @psalm-immutable - */ -final class Directory -{ + private $coverageCobertura; /** - * @var string + * @var ?string */ - private $path; - public function __construct(string $path) - { - $this->path = $path; - } - public function path() : string - { - return $this->path; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TextUI\XmlConfiguration; - -use function count; -use Countable; -use IteratorAggregate; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - * - * @psalm-immutable - * - * @template-implements IteratorAggregate - */ -final class DirectoryCollection implements Countable, IteratorAggregate -{ + private $coverageCrap4J; /** - * @var Directory[] + * @var ?string */ - private $directories; + private $coverageHtml; /** - * @param Directory[] $directories + * @var ?string */ - public static function fromArray(array $directories) : self - { - return new self(...$directories); - } - private function __construct(\PHPUnit\TextUI\XmlConfiguration\Directory ...$directories) - { - $this->directories = $directories; - } + private $coveragePhp; /** - * @return Directory[] + * @var ?string */ - public function asArray() : array - { - return $this->directories; - } - public function count() : int - { - return count($this->directories); - } - public function getIterator() : \PHPUnit\TextUI\XmlConfiguration\DirectoryCollectionIterator - { - return new \PHPUnit\TextUI\XmlConfiguration\DirectoryCollectionIterator($this); - } - public function isEmpty() : bool - { - return $this->count() === 0; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TextUI\XmlConfiguration; - -use function count; -use function iterator_count; -use Countable; -use Iterator; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - * - * @template-implements Iterator - */ -final class DirectoryCollectionIterator implements Countable, Iterator -{ + private $coverageText; /** - * @var Directory[] + * @var ?bool */ - private $directories; + private $coverageTextShowUncoveredFiles; /** - * @var int + * @var ?bool */ - private $position; - public function __construct(\PHPUnit\TextUI\XmlConfiguration\DirectoryCollection $directories) - { - $this->directories = $directories->asArray(); - } - public function count() : int - { - return iterator_count($this); - } - public function rewind() : void - { - $this->position = 0; - } - public function valid() : bool - { - return $this->position < count($this->directories); - } - public function key() : int - { - return $this->position; - } - public function current() : \PHPUnit\TextUI\XmlConfiguration\Directory - { - return $this->directories[$this->position]; - } - public function next() : void - { - $this->position++; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TextUI\XmlConfiguration; - -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - * - * @psalm-immutable - */ -final class File -{ + private $coverageTextShowOnlySummary; /** - * @var string + * @var ?string */ - private $path; - public function __construct(string $path) - { - $this->path = $path; - } - public function path() : string - { - return $this->path; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TextUI\XmlConfiguration; - -use function count; -use Countable; -use IteratorAggregate; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - * - * @psalm-immutable - * - * @template-implements IteratorAggregate - */ -final class FileCollection implements Countable, IteratorAggregate -{ + private $coverageXml; /** - * @var File[] + * @var ?bool */ - private $files; + private $pathCoverage; /** - * @param File[] $files + * @var ?string */ - public static function fromArray(array $files) : self - { - return new self(...$files); - } - private function __construct(\PHPUnit\TextUI\XmlConfiguration\File ...$files) - { - $this->files = $files; - } + private $coverageCacheDirectory; /** - * @return File[] + * @var ?bool */ - public function asArray() : array - { - return $this->files; - } - public function count() : int - { - return count($this->files); - } - public function getIterator() : \PHPUnit\TextUI\XmlConfiguration\FileCollectionIterator - { - return new \PHPUnit\TextUI\XmlConfiguration\FileCollectionIterator($this); - } - public function isEmpty() : bool - { - return $this->count() === 0; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TextUI\XmlConfiguration; - -use function count; -use function iterator_count; -use Countable; -use Iterator; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - * - * @template-implements Iterator - */ -final class FileCollectionIterator implements Countable, Iterator -{ + private $warmCoverageCache; /** - * @var File[] + * @var ?bool */ - private $files; + private $debug; /** - * @var int + * @var ?int */ - private $position; - public function __construct(\PHPUnit\TextUI\XmlConfiguration\FileCollection $files) - { - $this->files = $files->asArray(); - } - public function count() : int + private $defaultTimeLimit; + /** + * @var ?bool + */ + private $disableCodeCoverageIgnore; + /** + * @var ?bool + */ + private $disallowTestOutput; + /** + * @var ?bool + */ + private $disallowTodoAnnotatedTests; + /** + * @var ?bool + */ + private $enforceTimeLimit; + /** + * @var null|string[] + */ + private $excludeGroups; + /** + * @var ?int + */ + private $executionOrder; + /** + * @var ?int + */ + private $executionOrderDefects; + /** + * @var null|Extension[] + */ + private $extensions; + /** + * @var null|string[] + */ + private $unavailableExtensions; + /** + * @var ?bool + */ + private $failOnEmptyTestSuite; + /** + * @var ?bool + */ + private $failOnIncomplete; + /** + * @var ?bool + */ + private $failOnRisky; + /** + * @var ?bool + */ + private $failOnSkipped; + /** + * @var ?bool + */ + private $failOnWarning; + /** + * @var ?string + */ + private $filter; + /** + * @var ?bool + */ + private $generateConfiguration; + /** + * @var ?bool + */ + private $migrateConfiguration; + /** + * @var null|string[] + */ + private $groups; + /** + * @var null|string[] + */ + private $testsCovering; + /** + * @var null|string[] + */ + private $testsUsing; + /** + * @var ?bool + */ + private $help; + /** + * @var ?string + */ + private $includePath; + /** + * @var null|string[] + */ + private $iniSettings; + /** + * @var ?string + */ + private $junitLogfile; + /** + * @var ?bool + */ + private $listGroups; + /** + * @var ?bool + */ + private $listSuites; + /** + * @var ?bool + */ + private $listTests; + /** + * @var ?string + */ + private $listTestsXml; + /** + * @var ?string + */ + private $loader; + /** + * @var ?bool + */ + private $noCoverage; + /** + * @var ?bool + */ + private $noExtensions; + /** + * @var ?bool + */ + private $noInteraction; + /** + * @var ?bool + */ + private $noLogging; + /** + * @var ?string + */ + private $printer; + /** + * @var ?bool + */ + private $processIsolation; + /** + * @var ?int + */ + private $randomOrderSeed; + /** + * @var ?int + */ + private $repeat; + /** + * @var ?bool + */ + private $reportUselessTests; + /** + * @var ?bool + */ + private $resolveDependencies; + /** + * @var ?bool + */ + private $reverseList; + /** + * @var ?bool + */ + private $stderr; + /** + * @var ?bool + */ + private $strictCoverage; + /** + * @var ?bool + */ + private $stopOnDefect; + /** + * @var ?bool + */ + private $stopOnError; + /** + * @var ?bool + */ + private $stopOnFailure; + /** + * @var ?bool + */ + private $stopOnIncomplete; + /** + * @var ?bool + */ + private $stopOnRisky; + /** + * @var ?bool + */ + private $stopOnSkipped; + /** + * @var ?bool + */ + private $stopOnWarning; + /** + * @var ?string + */ + private $teamcityLogfile; + /** + * @var null|string[] + */ + private $testdoxExcludeGroups; + /** + * @var null|string[] + */ + private $testdoxGroups; + /** + * @var ?string + */ + private $testdoxHtmlFile; + /** + * @var ?string + */ + private $testdoxTextFile; + /** + * @var ?string + */ + private $testdoxXmlFile; + /** + * @var null|string[] + */ + private $testSuffixes; + /** + * @var ?string + */ + private $testSuite; + /** + * @var string[] + */ + private $unrecognizedOptions; + /** + * @var ?string + */ + private $unrecognizedOrderBy; + /** + * @var ?bool + */ + private $useDefaultConfiguration; + /** + * @var ?bool + */ + private $verbose; + /** + * @var ?bool + */ + private $version; + /** + * @var ?string + */ + private $xdebugFilterFile; + /** + * @param null|int|string $columns + */ + public function __construct(?string $argument, ?string $atLeastVersion, ?bool $backupGlobals, ?bool $backupStaticAttributes, ?bool $beStrictAboutChangesToGlobalState, ?bool $beStrictAboutResourceUsageDuringSmallTests, ?string $bootstrap, ?bool $cacheResult, ?string $cacheResultFile, ?bool $checkVersion, ?string $colors, $columns, ?string $configuration, ?string $coverageClover, ?string $coverageCobertura, ?string $coverageCrap4J, ?string $coverageHtml, ?string $coveragePhp, ?string $coverageText, ?bool $coverageTextShowUncoveredFiles, ?bool $coverageTextShowOnlySummary, ?string $coverageXml, ?bool $pathCoverage, ?string $coverageCacheDirectory, ?bool $warmCoverageCache, ?bool $debug, ?int $defaultTimeLimit, ?bool $disableCodeCoverageIgnore, ?bool $disallowTestOutput, ?bool $disallowTodoAnnotatedTests, ?bool $enforceTimeLimit, ?array $excludeGroups, ?int $executionOrder, ?int $executionOrderDefects, ?array $extensions, ?array $unavailableExtensions, ?bool $failOnEmptyTestSuite, ?bool $failOnIncomplete, ?bool $failOnRisky, ?bool $failOnSkipped, ?bool $failOnWarning, ?string $filter, ?bool $generateConfiguration, ?bool $migrateConfiguration, ?array $groups, ?array $testsCovering, ?array $testsUsing, ?bool $help, ?string $includePath, ?array $iniSettings, ?string $junitLogfile, ?bool $listGroups, ?bool $listSuites, ?bool $listTests, ?string $listTestsXml, ?string $loader, ?bool $noCoverage, ?bool $noExtensions, ?bool $noInteraction, ?bool $noLogging, ?string $printer, ?bool $processIsolation, ?int $randomOrderSeed, ?int $repeat, ?bool $reportUselessTests, ?bool $resolveDependencies, ?bool $reverseList, ?bool $stderr, ?bool $strictCoverage, ?bool $stopOnDefect, ?bool $stopOnError, ?bool $stopOnFailure, ?bool $stopOnIncomplete, ?bool $stopOnRisky, ?bool $stopOnSkipped, ?bool $stopOnWarning, ?string $teamcityLogfile, ?array $testdoxExcludeGroups, ?array $testdoxGroups, ?string $testdoxHtmlFile, ?string $testdoxTextFile, ?string $testdoxXmlFile, ?array $testSuffixes, ?string $testSuite, array $unrecognizedOptions, ?string $unrecognizedOrderBy, ?bool $useDefaultConfiguration, ?bool $verbose, ?bool $version, ?array $coverageFilter, ?string $xdebugFilterFile) { - return iterator_count($this); + $this->argument = $argument; + $this->atLeastVersion = $atLeastVersion; + $this->backupGlobals = $backupGlobals; + $this->backupStaticAttributes = $backupStaticAttributes; + $this->beStrictAboutChangesToGlobalState = $beStrictAboutChangesToGlobalState; + $this->beStrictAboutResourceUsageDuringSmallTests = $beStrictAboutResourceUsageDuringSmallTests; + $this->bootstrap = $bootstrap; + $this->cacheResult = $cacheResult; + $this->cacheResultFile = $cacheResultFile; + $this->checkVersion = $checkVersion; + $this->colors = $colors; + $this->columns = $columns; + $this->configuration = $configuration; + $this->coverageFilter = $coverageFilter; + $this->coverageClover = $coverageClover; + $this->coverageCobertura = $coverageCobertura; + $this->coverageCrap4J = $coverageCrap4J; + $this->coverageHtml = $coverageHtml; + $this->coveragePhp = $coveragePhp; + $this->coverageText = $coverageText; + $this->coverageTextShowUncoveredFiles = $coverageTextShowUncoveredFiles; + $this->coverageTextShowOnlySummary = $coverageTextShowOnlySummary; + $this->coverageXml = $coverageXml; + $this->pathCoverage = $pathCoverage; + $this->coverageCacheDirectory = $coverageCacheDirectory; + $this->warmCoverageCache = $warmCoverageCache; + $this->debug = $debug; + $this->defaultTimeLimit = $defaultTimeLimit; + $this->disableCodeCoverageIgnore = $disableCodeCoverageIgnore; + $this->disallowTestOutput = $disallowTestOutput; + $this->disallowTodoAnnotatedTests = $disallowTodoAnnotatedTests; + $this->enforceTimeLimit = $enforceTimeLimit; + $this->excludeGroups = $excludeGroups; + $this->executionOrder = $executionOrder; + $this->executionOrderDefects = $executionOrderDefects; + $this->extensions = $extensions; + $this->unavailableExtensions = $unavailableExtensions; + $this->failOnEmptyTestSuite = $failOnEmptyTestSuite; + $this->failOnIncomplete = $failOnIncomplete; + $this->failOnRisky = $failOnRisky; + $this->failOnSkipped = $failOnSkipped; + $this->failOnWarning = $failOnWarning; + $this->filter = $filter; + $this->generateConfiguration = $generateConfiguration; + $this->migrateConfiguration = $migrateConfiguration; + $this->groups = $groups; + $this->testsCovering = $testsCovering; + $this->testsUsing = $testsUsing; + $this->help = $help; + $this->includePath = $includePath; + $this->iniSettings = $iniSettings; + $this->junitLogfile = $junitLogfile; + $this->listGroups = $listGroups; + $this->listSuites = $listSuites; + $this->listTests = $listTests; + $this->listTestsXml = $listTestsXml; + $this->loader = $loader; + $this->noCoverage = $noCoverage; + $this->noExtensions = $noExtensions; + $this->noInteraction = $noInteraction; + $this->noLogging = $noLogging; + $this->printer = $printer; + $this->processIsolation = $processIsolation; + $this->randomOrderSeed = $randomOrderSeed; + $this->repeat = $repeat; + $this->reportUselessTests = $reportUselessTests; + $this->resolveDependencies = $resolveDependencies; + $this->reverseList = $reverseList; + $this->stderr = $stderr; + $this->strictCoverage = $strictCoverage; + $this->stopOnDefect = $stopOnDefect; + $this->stopOnError = $stopOnError; + $this->stopOnFailure = $stopOnFailure; + $this->stopOnIncomplete = $stopOnIncomplete; + $this->stopOnRisky = $stopOnRisky; + $this->stopOnSkipped = $stopOnSkipped; + $this->stopOnWarning = $stopOnWarning; + $this->teamcityLogfile = $teamcityLogfile; + $this->testdoxExcludeGroups = $testdoxExcludeGroups; + $this->testdoxGroups = $testdoxGroups; + $this->testdoxHtmlFile = $testdoxHtmlFile; + $this->testdoxTextFile = $testdoxTextFile; + $this->testdoxXmlFile = $testdoxXmlFile; + $this->testSuffixes = $testSuffixes; + $this->testSuite = $testSuite; + $this->unrecognizedOptions = $unrecognizedOptions; + $this->unrecognizedOrderBy = $unrecognizedOrderBy; + $this->useDefaultConfiguration = $useDefaultConfiguration; + $this->verbose = $verbose; + $this->version = $version; + $this->xdebugFilterFile = $xdebugFilterFile; } - public function rewind() : void + public function hasArgument() : bool { - $this->position = 0; + return $this->argument !== null; } - public function valid() : bool + /** + * @throws Exception + */ + public function argument() : string { - return $this->position < count($this->files); + if ($this->argument === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->argument; } - public function key() : int + public function hasAtLeastVersion() : bool { - return $this->position; + return $this->atLeastVersion !== null; } - public function current() : \PHPUnit\TextUI\XmlConfiguration\File + /** + * @throws Exception + */ + public function atLeastVersion() : string { - return $this->files[$this->position]; + if ($this->atLeastVersion === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->atLeastVersion; } - public function next() : void + public function hasBackupGlobals() : bool { - $this->position++; + return $this->backupGlobals !== null; } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TextUI\XmlConfiguration; - -use function str_replace; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class Generator -{ /** - * @var string + * @throws Exception */ - private const TEMPLATE = <<<'EOT' - - - - - {tests_directory} - - - - - - {src_directory} - - - - -EOT; - public function generateDefaultConfiguration(string $phpunitVersion, string $bootstrapScript, string $testsDirectory, string $srcDirectory, string $cacheDirectory) : string + public function backupGlobals() : bool { - return str_replace(['{phpunit_version}', '{bootstrap_script}', '{tests_directory}', '{src_directory}', '{cache_directory}'], [$phpunitVersion, $bootstrapScript, $testsDirectory, $srcDirectory, $cacheDirectory], self::TEMPLATE); + if ($this->backupGlobals === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->backupGlobals; + } + public function hasBackupStaticAttributes() : bool + { + return $this->backupStaticAttributes !== null; } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TextUI\XmlConfiguration; - -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - * - * @psalm-immutable - */ -final class Group -{ /** - * @var string + * @throws Exception */ - private $name; - public function __construct(string $name) + public function backupStaticAttributes() : bool { - $this->name = $name; + if ($this->backupStaticAttributes === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->backupStaticAttributes; } - public function name() : string + public function hasBeStrictAboutChangesToGlobalState() : bool { - return $this->name; + return $this->beStrictAboutChangesToGlobalState !== null; } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TextUI\XmlConfiguration; - -use IteratorAggregate; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - * - * @psalm-immutable - * - * @template-implements IteratorAggregate - */ -final class GroupCollection implements IteratorAggregate -{ /** - * @var Group[] + * @throws Exception */ - private $groups; + public function beStrictAboutChangesToGlobalState() : bool + { + if ($this->beStrictAboutChangesToGlobalState === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->beStrictAboutChangesToGlobalState; + } + public function hasBeStrictAboutResourceUsageDuringSmallTests() : bool + { + return $this->beStrictAboutResourceUsageDuringSmallTests !== null; + } /** - * @param Group[] $groups + * @throws Exception */ - public static function fromArray(array $groups) : self + public function beStrictAboutResourceUsageDuringSmallTests() : bool { - return new self(...$groups); + if ($this->beStrictAboutResourceUsageDuringSmallTests === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->beStrictAboutResourceUsageDuringSmallTests; } - private function __construct(\PHPUnit\TextUI\XmlConfiguration\Group ...$groups) + public function hasBootstrap() : bool { - $this->groups = $groups; + return $this->bootstrap !== null; } /** - * @return Group[] + * @throws Exception */ - public function asArray() : array + public function bootstrap() : string { - return $this->groups; + if ($this->bootstrap === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->bootstrap; + } + public function hasCacheResult() : bool + { + return $this->cacheResult !== null; } /** - * @return string[] + * @throws Exception */ - public function asArrayOfStrings() : array + public function cacheResult() : bool { - $result = []; - foreach ($this->groups as $group) { - $result[] = $group->name(); + if ($this->cacheResult === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); } - return $result; + return $this->cacheResult; } - public function isEmpty() : bool + public function hasCacheResultFile() : bool { - return empty($this->groups); + return $this->cacheResultFile !== null; } - public function getIterator() : \PHPUnit\TextUI\XmlConfiguration\GroupCollectionIterator + /** + * @throws Exception + */ + public function cacheResultFile() : string { - return new \PHPUnit\TextUI\XmlConfiguration\GroupCollectionIterator($this); + if ($this->cacheResultFile === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->cacheResultFile; + } + public function hasCheckVersion() : bool + { + return $this->checkVersion !== null; } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TextUI\XmlConfiguration; - -use function count; -use function iterator_count; -use Countable; -use Iterator; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - * - * @template-implements Iterator - */ -final class GroupCollectionIterator implements Countable, Iterator -{ /** - * @var Group[] + * @throws Exception */ - private $groups; + public function checkVersion() : bool + { + if ($this->checkVersion === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->checkVersion; + } + public function hasColors() : bool + { + return $this->colors !== null; + } /** - * @var int + * @throws Exception */ - private $position; - public function __construct(\PHPUnit\TextUI\XmlConfiguration\GroupCollection $groups) + public function colors() : string { - $this->groups = $groups->asArray(); + if ($this->colors === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->colors; } - public function count() : int + public function hasColumns() : bool { - return iterator_count($this); + return $this->columns !== null; } - public function rewind() : void + /** + * @throws Exception + */ + public function columns() { - $this->position = 0; + if ($this->columns === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->columns; } - public function valid() : bool + public function hasConfiguration() : bool { - return $this->position < count($this->groups); + return $this->configuration !== null; } - public function key() : int + /** + * @throws Exception + */ + public function configuration() : string { - return $this->position; + if ($this->configuration === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->configuration; } - public function current() : \PHPUnit\TextUI\XmlConfiguration\Group + public function hasCoverageFilter() : bool { - return $this->groups[$this->position]; + return $this->coverageFilter !== null; } - public function next() : void + /** + * @throws Exception + */ + public function coverageFilter() : array { - $this->position++; + if ($this->coverageFilter === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->coverageFilter; + } + public function hasCoverageClover() : bool + { + return $this->coverageClover !== null; } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TextUI\XmlConfiguration; - -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - * - * @psalm-immutable - */ -final class Groups -{ /** - * @var GroupCollection + * @throws Exception */ - private $include; + public function coverageClover() : string + { + if ($this->coverageClover === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->coverageClover; + } + public function hasCoverageCobertura() : bool + { + return $this->coverageCobertura !== null; + } /** - * @var GroupCollection + * @throws Exception */ - private $exclude; - public function __construct(\PHPUnit\TextUI\XmlConfiguration\GroupCollection $include, \PHPUnit\TextUI\XmlConfiguration\GroupCollection $exclude) + public function coverageCobertura() : string { - $this->include = $include; - $this->exclude = $exclude; + if ($this->coverageCobertura === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->coverageCobertura; } - public function hasInclude() : bool + public function hasCoverageCrap4J() : bool { - return !$this->include->isEmpty(); + return $this->coverageCrap4J !== null; } - public function include() : \PHPUnit\TextUI\XmlConfiguration\GroupCollection + /** + * @throws Exception + */ + public function coverageCrap4J() : string { - return $this->include; + if ($this->coverageCrap4J === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->coverageCrap4J; } - public function hasExclude() : bool + public function hasCoverageHtml() : bool { - return !$this->exclude->isEmpty(); + return $this->coverageHtml !== null; } - public function exclude() : \PHPUnit\TextUI\XmlConfiguration\GroupCollection + /** + * @throws Exception + */ + public function coverageHtml() : string { - return $this->exclude; + if ($this->coverageHtml === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->coverageHtml; + } + public function hasCoveragePhp() : bool + { + return $this->coveragePhp !== null; } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TextUI\XmlConfiguration; - -use const DIRECTORY_SEPARATOR; -use const PHP_VERSION; -use function assert; -use function defined; -use function dirname; -use function explode; -use function is_file; -use function is_numeric; -use function preg_match; -use function stream_resolve_include_path; -use function strlen; -use function strpos; -use function strtolower; -use function substr; -use function trim; -use DOMDocument; -use DOMElement; -use DOMNodeList; -use DOMXPath; -use PHPUnit\Runner\TestSuiteSorter; -use PHPUnit\Runner\Version; -use PHPUnit\TextUI\DefaultResultPrinter; -use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\CodeCoverage; -use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Filter\Directory as FilterDirectory; -use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Filter\DirectoryCollection as FilterDirectoryCollection; -use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report\Clover; -use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report\Cobertura; -use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report\Crap4j; -use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report\Html as CodeCoverageHtml; -use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report\Php as CodeCoveragePhp; -use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report\Text as CodeCoverageText; -use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report\Xml as CodeCoverageXml; -use PHPUnit\TextUI\XmlConfiguration\Logging\Junit; -use PHPUnit\TextUI\XmlConfiguration\Logging\Logging; -use PHPUnit\TextUI\XmlConfiguration\Logging\TeamCity; -use PHPUnit\TextUI\XmlConfiguration\Logging\TestDox\Html as TestDoxHtml; -use PHPUnit\TextUI\XmlConfiguration\Logging\TestDox\Text as TestDoxText; -use PHPUnit\TextUI\XmlConfiguration\Logging\TestDox\Xml as TestDoxXml; -use PHPUnit\TextUI\XmlConfiguration\Logging\Text; -use PHPUnit\TextUI\XmlConfiguration\TestSuite as TestSuiteConfiguration; -use PHPUnit\Util\TestDox\CliTestDoxPrinter; -use PHPUnit\Util\VersionComparisonOperator; -use PHPUnit\Util\Xml; -use PHPUnit\Util\Xml\Exception as XmlException; -use PHPUnit\Util\Xml\Loader as XmlLoader; -use PHPUnit\Util\Xml\SchemaFinder; -use PHPUnit\Util\Xml\Validator; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class Loader -{ /** * @throws Exception */ - public function load(string $filename) : \PHPUnit\TextUI\XmlConfiguration\Configuration + public function coveragePhp() : string { - try { - $document = (new XmlLoader())->loadFile($filename, \false, \true, \true); - } catch (XmlException $e) { - throw new \PHPUnit\TextUI\XmlConfiguration\Exception($e->getMessage(), $e->getCode(), $e); - } - $xpath = new DOMXPath($document); - try { - $xsdFilename = (new SchemaFinder())->find(Version::series()); - } catch (XmlException $e) { - throw new \PHPUnit\TextUI\XmlConfiguration\Exception($e->getMessage(), $e->getCode(), $e); + if ($this->coveragePhp === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); } - return new \PHPUnit\TextUI\XmlConfiguration\Configuration($filename, (new Validator())->validate($document, $xsdFilename), $this->extensions($filename, $xpath), $this->codeCoverage($filename, $xpath, $document), $this->groups($xpath), $this->testdoxGroups($xpath), $this->listeners($filename, $xpath), $this->logging($filename, $xpath), $this->php($filename, $xpath), $this->phpunit($filename, $document), $this->testSuite($filename, $xpath)); + return $this->coveragePhp; } - public function logging(string $filename, DOMXPath $xpath) : Logging + public function hasCoverageText() : bool { - if ($xpath->query('logging/log')->length !== 0) { - return $this->legacyLogging($filename, $xpath); - } - $junit = null; - $element = $this->element($xpath, 'logging/junit'); - if ($element) { - $junit = new Junit(new \PHPUnit\TextUI\XmlConfiguration\File($this->toAbsolutePath($filename, (string) $this->getStringAttribute($element, 'outputFile')))); - } - $text = null; - $element = $this->element($xpath, 'logging/text'); - if ($element) { - $text = new Text(new \PHPUnit\TextUI\XmlConfiguration\File($this->toAbsolutePath($filename, (string) $this->getStringAttribute($element, 'outputFile')))); - } - $teamCity = null; - $element = $this->element($xpath, 'logging/teamcity'); - if ($element) { - $teamCity = new TeamCity(new \PHPUnit\TextUI\XmlConfiguration\File($this->toAbsolutePath($filename, (string) $this->getStringAttribute($element, 'outputFile')))); - } - $testDoxHtml = null; - $element = $this->element($xpath, 'logging/testdoxHtml'); - if ($element) { - $testDoxHtml = new TestDoxHtml(new \PHPUnit\TextUI\XmlConfiguration\File($this->toAbsolutePath($filename, (string) $this->getStringAttribute($element, 'outputFile')))); - } - $testDoxText = null; - $element = $this->element($xpath, 'logging/testdoxText'); - if ($element) { - $testDoxText = new TestDoxText(new \PHPUnit\TextUI\XmlConfiguration\File($this->toAbsolutePath($filename, (string) $this->getStringAttribute($element, 'outputFile')))); - } - $testDoxXml = null; - $element = $this->element($xpath, 'logging/testdoxXml'); - if ($element) { - $testDoxXml = new TestDoxXml(new \PHPUnit\TextUI\XmlConfiguration\File($this->toAbsolutePath($filename, (string) $this->getStringAttribute($element, 'outputFile')))); - } - return new Logging($junit, $text, $teamCity, $testDoxHtml, $testDoxText, $testDoxXml); + return $this->coverageText !== null; } - public function legacyLogging(string $filename, DOMXPath $xpath) : Logging + /** + * @throws Exception + */ + public function coverageText() : string { - $junit = null; - $teamCity = null; - $testDoxHtml = null; - $testDoxText = null; - $testDoxXml = null; - $text = null; - foreach ($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($filename, $target); - switch ($type) { - case 'plain': - $text = new Text(new \PHPUnit\TextUI\XmlConfiguration\File($target)); - break; - case 'junit': - $junit = new Junit(new \PHPUnit\TextUI\XmlConfiguration\File($target)); - break; - case 'teamcity': - $teamCity = new TeamCity(new \PHPUnit\TextUI\XmlConfiguration\File($target)); - break; - case 'testdox-html': - $testDoxHtml = new TestDoxHtml(new \PHPUnit\TextUI\XmlConfiguration\File($target)); - break; - case 'testdox-text': - $testDoxText = new TestDoxText(new \PHPUnit\TextUI\XmlConfiguration\File($target)); - break; - case 'testdox-xml': - $testDoxXml = new TestDoxXml(new \PHPUnit\TextUI\XmlConfiguration\File($target)); - break; - } + if ($this->coverageText === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); } - return new Logging($junit, $text, $teamCity, $testDoxHtml, $testDoxText, $testDoxXml); + return $this->coverageText; } - private function extensions(string $filename, DOMXPath $xpath) : \PHPUnit\TextUI\XmlConfiguration\ExtensionCollection + public function hasCoverageTextShowUncoveredFiles() : bool { - $extensions = []; - foreach ($xpath->query('extensions/extension') as $extension) { - assert($extension instanceof DOMElement); - $extensions[] = $this->getElementConfigurationParameters($filename, $extension); - } - return \PHPUnit\TextUI\XmlConfiguration\ExtensionCollection::fromArray($extensions); + return $this->coverageTextShowUncoveredFiles !== null; } - private function getElementConfigurationParameters(string $filename, DOMElement $element) : \PHPUnit\TextUI\XmlConfiguration\Extension + /** + * @throws Exception + */ + public function coverageTextShowUncoveredFiles() : bool { - /** @psalm-var class-string $class */ - $class = (string) $element->getAttribute('class'); - $file = ''; - $arguments = $this->getConfigurationArguments($filename, $element->childNodes); - if ($element->getAttribute('file')) { - $file = $this->toAbsolutePath($filename, (string) $element->getAttribute('file'), \true); + if ($this->coverageTextShowUncoveredFiles === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); } - return new \PHPUnit\TextUI\XmlConfiguration\Extension($class, $file, $arguments); + return $this->coverageTextShowUncoveredFiles; } - private function toAbsolutePath(string $filename, string $path, bool $useIncludePath = \false) : string + public function hasCoverageTextShowOnlySummary() : bool { - $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($filename) . DIRECTORY_SEPARATOR . $path; - if ($useIncludePath && !is_file($file)) { - $includePathFile = stream_resolve_include_path($path); - if ($includePathFile) { - $file = $includePathFile; - } - } - return $file; + return $this->coverageTextShowOnlySummary !== null; } - private function getConfigurationArguments(string $filename, DOMNodeList $nodes) : array + /** + * @throws Exception + */ + public function coverageTextShowOnlySummary() : bool { - $arguments = []; - if ($nodes->length === 0) { - return $arguments; + if ($this->coverageTextShowOnlySummary === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); } - 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($filename, (string) $argument->textContent); - } else { - $arguments[] = Xml::xmlToVariable($argument); - } - } + return $this->coverageTextShowOnlySummary; + } + public function hasCoverageXml() : bool + { + return $this->coverageXml !== null; + } + /** + * @throws Exception + */ + public function coverageXml() : string + { + if ($this->coverageXml === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); } - return $arguments; + return $this->coverageXml; } - private function codeCoverage(string $filename, DOMXPath $xpath, DOMDocument $document) : CodeCoverage + public function hasPathCoverage() : bool { - if ($xpath->query('filter/whitelist')->length !== 0) { - return $this->legacyCodeCoverage($filename, $xpath, $document); + return $this->pathCoverage !== null; + } + /** + * @throws Exception + */ + public function pathCoverage() : bool + { + if ($this->pathCoverage === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); } - $cacheDirectory = null; - $pathCoverage = \false; - $includeUncoveredFiles = \true; - $processUncoveredFiles = \false; - $ignoreDeprecatedCodeUnits = \false; - $disableCodeCoverageIgnore = \false; - $element = $this->element($xpath, 'coverage'); - if ($element) { - $cacheDirectory = $this->getStringAttribute($element, 'cacheDirectory'); - if ($cacheDirectory !== null) { - $cacheDirectory = new \PHPUnit\TextUI\XmlConfiguration\Directory($this->toAbsolutePath($filename, $cacheDirectory)); - } - $pathCoverage = $this->getBooleanAttribute($element, 'pathCoverage', \false); - $includeUncoveredFiles = $this->getBooleanAttribute($element, 'includeUncoveredFiles', \true); - $processUncoveredFiles = $this->getBooleanAttribute($element, 'processUncoveredFiles', \false); - $ignoreDeprecatedCodeUnits = $this->getBooleanAttribute($element, 'ignoreDeprecatedCodeUnits', \false); - $disableCodeCoverageIgnore = $this->getBooleanAttribute($element, 'disableCodeCoverageIgnore', \false); - } - $clover = null; - $element = $this->element($xpath, 'coverage/report/clover'); - if ($element) { - $clover = new Clover(new \PHPUnit\TextUI\XmlConfiguration\File($this->toAbsolutePath($filename, (string) $this->getStringAttribute($element, 'outputFile')))); - } - $cobertura = null; - $element = $this->element($xpath, 'coverage/report/cobertura'); - if ($element) { - $cobertura = new Cobertura(new \PHPUnit\TextUI\XmlConfiguration\File($this->toAbsolutePath($filename, (string) $this->getStringAttribute($element, 'outputFile')))); - } - $crap4j = null; - $element = $this->element($xpath, 'coverage/report/crap4j'); - if ($element) { - $crap4j = new Crap4j(new \PHPUnit\TextUI\XmlConfiguration\File($this->toAbsolutePath($filename, (string) $this->getStringAttribute($element, 'outputFile'))), $this->getIntegerAttribute($element, 'threshold', 30)); - } - $html = null; - $element = $this->element($xpath, 'coverage/report/html'); - if ($element) { - $html = new CodeCoverageHtml(new \PHPUnit\TextUI\XmlConfiguration\Directory($this->toAbsolutePath($filename, (string) $this->getStringAttribute($element, 'outputDirectory'))), $this->getIntegerAttribute($element, 'lowUpperBound', 50), $this->getIntegerAttribute($element, 'highLowerBound', 90)); - } - $php = null; - $element = $this->element($xpath, 'coverage/report/php'); - if ($element) { - $php = new CodeCoveragePhp(new \PHPUnit\TextUI\XmlConfiguration\File($this->toAbsolutePath($filename, (string) $this->getStringAttribute($element, 'outputFile')))); - } - $text = null; - $element = $this->element($xpath, 'coverage/report/text'); - if ($element) { - $text = new CodeCoverageText(new \PHPUnit\TextUI\XmlConfiguration\File($this->toAbsolutePath($filename, (string) $this->getStringAttribute($element, 'outputFile'))), $this->getBooleanAttribute($element, 'showUncoveredFiles', \false), $this->getBooleanAttribute($element, 'showOnlySummary', \false)); - } - $xml = null; - $element = $this->element($xpath, 'coverage/report/xml'); - if ($element) { - $xml = new CodeCoverageXml(new \PHPUnit\TextUI\XmlConfiguration\Directory($this->toAbsolutePath($filename, (string) $this->getStringAttribute($element, 'outputDirectory')))); - } - return new CodeCoverage($cacheDirectory, $this->readFilterDirectories($filename, $xpath, 'coverage/include/directory'), $this->readFilterFiles($filename, $xpath, 'coverage/include/file'), $this->readFilterDirectories($filename, $xpath, 'coverage/exclude/directory'), $this->readFilterFiles($filename, $xpath, 'coverage/exclude/file'), $pathCoverage, $includeUncoveredFiles, $processUncoveredFiles, $ignoreDeprecatedCodeUnits, $disableCodeCoverageIgnore, $clover, $cobertura, $crap4j, $html, $php, $text, $xml); + return $this->pathCoverage; + } + public function hasCoverageCacheDirectory() : bool + { + return $this->coverageCacheDirectory !== null; } /** - * @deprecated + * @throws Exception */ - private function legacyCodeCoverage(string $filename, DOMXPath $xpath, DOMDocument $document) : CodeCoverage + public function coverageCacheDirectory() : string { - $ignoreDeprecatedCodeUnits = $this->getBooleanAttribute($document->documentElement, 'ignoreDeprecatedCodeUnitsFromCodeCoverage', \false); - $disableCodeCoverageIgnore = $this->getBooleanAttribute($document->documentElement, 'disableCodeCoverageIgnore', \false); - $includeUncoveredFiles = \true; - $processUncoveredFiles = \false; - $element = $this->element($xpath, 'filter/whitelist'); - if ($element) { - if ($element->hasAttribute('addUncoveredFilesFromWhitelist')) { - $includeUncoveredFiles = (bool) $this->getBoolean((string) $element->getAttribute('addUncoveredFilesFromWhitelist'), \true); - } - if ($element->hasAttribute('processUncoveredFilesFromWhitelist')) { - $processUncoveredFiles = (bool) $this->getBoolean((string) $element->getAttribute('processUncoveredFilesFromWhitelist'), \false); - } - } - $clover = null; - $cobertura = null; - $crap4j = null; - $html = null; - $php = null; - $text = null; - $xml = null; - foreach ($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($filename, $target); - switch ($type) { - case 'coverage-clover': - $clover = new Clover(new \PHPUnit\TextUI\XmlConfiguration\File($target)); - break; - case 'coverage-cobertura': - $cobertura = new Cobertura(new \PHPUnit\TextUI\XmlConfiguration\File($target)); - break; - case 'coverage-crap4j': - $crap4j = new Crap4j(new \PHPUnit\TextUI\XmlConfiguration\File($target), $this->getIntegerAttribute($log, 'threshold', 30)); - break; - case 'coverage-html': - $html = new CodeCoverageHtml(new \PHPUnit\TextUI\XmlConfiguration\Directory($target), $this->getIntegerAttribute($log, 'lowUpperBound', 50), $this->getIntegerAttribute($log, 'highLowerBound', 90)); - break; - case 'coverage-php': - $php = new CodeCoveragePhp(new \PHPUnit\TextUI\XmlConfiguration\File($target)); - break; - case 'coverage-text': - $text = new CodeCoverageText(new \PHPUnit\TextUI\XmlConfiguration\File($target), $this->getBooleanAttribute($log, 'showUncoveredFiles', \false), $this->getBooleanAttribute($log, 'showOnlySummary', \false)); - break; - case 'coverage-xml': - $xml = new CodeCoverageXml(new \PHPUnit\TextUI\XmlConfiguration\Directory($target)); - break; - } + if ($this->coverageCacheDirectory === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); } - return new CodeCoverage(null, $this->readFilterDirectories($filename, $xpath, 'filter/whitelist/directory'), $this->readFilterFiles($filename, $xpath, 'filter/whitelist/file'), $this->readFilterDirectories($filename, $xpath, 'filter/whitelist/exclude/directory'), $this->readFilterFiles($filename, $xpath, 'filter/whitelist/exclude/file'), \false, $includeUncoveredFiles, $processUncoveredFiles, $ignoreDeprecatedCodeUnits, $disableCodeCoverageIgnore, $clover, $cobertura, $crap4j, $html, $php, $text, $xml); + return $this->coverageCacheDirectory; + } + public function hasWarmCoverageCache() : bool + { + return $this->warmCoverageCache !== null; } /** - * 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\TextUI\XmlConfigurationTest::testPHPConfigurationIsReadCorrectly - * - * @param bool|string $default - * - * @return bool|string + * @throws Exception */ - private function getBoolean(string $value, $default) + public function warmCoverageCache() : bool { - if (strtolower($value) === 'false') { - return \false; - } - if (strtolower($value) === 'true') { - return \true; + if ($this->warmCoverageCache === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); } - return $default; + return $this->warmCoverageCache; } - private function readFilterDirectories(string $filename, DOMXPath $xpath, string $query) : FilterDirectoryCollection + public function hasDebug() : bool { - $directories = []; - foreach ($xpath->query($query) as $directoryNode) { - assert($directoryNode instanceof DOMElement); - $directoryPath = (string) $directoryNode->textContent; - if (!$directoryPath) { - continue; - } - $directories[] = new FilterDirectory($this->toAbsolutePath($filename, $directoryPath), $directoryNode->hasAttribute('prefix') ? (string) $directoryNode->getAttribute('prefix') : '', $directoryNode->hasAttribute('suffix') ? (string) $directoryNode->getAttribute('suffix') : '.php', $directoryNode->hasAttribute('group') ? (string) $directoryNode->getAttribute('group') : 'DEFAULT'); - } - return FilterDirectoryCollection::fromArray($directories); + return $this->debug !== null; } - private function readFilterFiles(string $filename, DOMXPath $xpath, string $query) : \PHPUnit\TextUI\XmlConfiguration\FileCollection + /** + * @throws Exception + */ + public function debug() : bool { - $files = []; - foreach ($xpath->query($query) as $file) { - $filePath = (string) $file->textContent; - if ($filePath) { - $files[] = new \PHPUnit\TextUI\XmlConfiguration\File($this->toAbsolutePath($filename, $filePath)); - } + if ($this->debug === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); } - return \PHPUnit\TextUI\XmlConfiguration\FileCollection::fromArray($files); + return $this->debug; } - private function groups(DOMXPath $xpath) : \PHPUnit\TextUI\XmlConfiguration\Groups + public function hasDefaultTimeLimit() : bool { - return $this->parseGroupConfiguration($xpath, 'groups'); + return $this->defaultTimeLimit !== null; } - private function testdoxGroups(DOMXPath $xpath) : \PHPUnit\TextUI\XmlConfiguration\Groups + /** + * @throws Exception + */ + public function defaultTimeLimit() : int { - return $this->parseGroupConfiguration($xpath, 'testdoxGroups'); + if ($this->defaultTimeLimit === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->defaultTimeLimit; } - private function parseGroupConfiguration(DOMXPath $xpath, string $root) : \PHPUnit\TextUI\XmlConfiguration\Groups + public function hasDisableCodeCoverageIgnore() : bool { - $include = []; - $exclude = []; - foreach ($xpath->query($root . '/include/group') as $group) { - $include[] = new \PHPUnit\TextUI\XmlConfiguration\Group((string) $group->textContent); - } - foreach ($xpath->query($root . '/exclude/group') as $group) { - $exclude[] = new \PHPUnit\TextUI\XmlConfiguration\Group((string) $group->textContent); - } - return new \PHPUnit\TextUI\XmlConfiguration\Groups(\PHPUnit\TextUI\XmlConfiguration\GroupCollection::fromArray($include), \PHPUnit\TextUI\XmlConfiguration\GroupCollection::fromArray($exclude)); + return $this->disableCodeCoverageIgnore !== null; } - private function listeners(string $filename, DOMXPath $xpath) : \PHPUnit\TextUI\XmlConfiguration\ExtensionCollection + /** + * @throws Exception + */ + public function disableCodeCoverageIgnore() : bool { - $listeners = []; - foreach ($xpath->query('listeners/listener') as $listener) { - assert($listener instanceof DOMElement); - $listeners[] = $this->getElementConfigurationParameters($filename, $listener); + if ($this->disableCodeCoverageIgnore === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); } - return \PHPUnit\TextUI\XmlConfiguration\ExtensionCollection::fromArray($listeners); + return $this->disableCodeCoverageIgnore; } - private function getBooleanAttribute(DOMElement $element, string $attribute, bool $default) : bool + public function hasDisallowTestOutput() : bool { - if (!$element->hasAttribute($attribute)) { - return $default; - } - return (bool) $this->getBoolean((string) $element->getAttribute($attribute), \false); + return $this->disallowTestOutput !== null; } - private function getIntegerAttribute(DOMElement $element, string $attribute, int $default) : int + /** + * @throws Exception + */ + public function disallowTestOutput() : bool { - if (!$element->hasAttribute($attribute)) { - return $default; + if ($this->disallowTestOutput === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); } - return $this->getInteger((string) $element->getAttribute($attribute), $default); + return $this->disallowTestOutput; } - private function getStringAttribute(DOMElement $element, string $attribute) : ?string + public function hasDisallowTodoAnnotatedTests() : bool { - if (!$element->hasAttribute($attribute)) { - return null; - } - return (string) $element->getAttribute($attribute); + return $this->disallowTodoAnnotatedTests !== null; } - private function getInteger(string $value, int $default) : int + /** + * @throws Exception + */ + public function disallowTodoAnnotatedTests() : bool { - if (is_numeric($value)) { - return (int) $value; + if ($this->disallowTodoAnnotatedTests === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); } - return $default; + return $this->disallowTodoAnnotatedTests; } - private function php(string $filename, DOMXPath $xpath) : \PHPUnit\TextUI\XmlConfiguration\Php + public function hasEnforceTimeLimit() : bool { - $includePaths = []; - foreach ($xpath->query('php/includePath') as $includePath) { - $path = (string) $includePath->textContent; - if ($path) { - $includePaths[] = new \PHPUnit\TextUI\XmlConfiguration\Directory($this->toAbsolutePath($filename, $path)); - } - } - $iniSettings = []; - foreach ($xpath->query('php/ini') as $ini) { - assert($ini instanceof DOMElement); - $iniSettings[] = new \PHPUnit\TextUI\XmlConfiguration\IniSetting((string) $ini->getAttribute('name'), (string) $ini->getAttribute('value')); - } - $constants = []; - foreach ($xpath->query('php/const') as $const) { - assert($const instanceof DOMElement); - $value = (string) $const->getAttribute('value'); - $constants[] = new \PHPUnit\TextUI\XmlConfiguration\Constant((string) $const->getAttribute('name'), $this->getBoolean($value, $value)); - } - $variables = ['var' => [], 'env' => [], 'post' => [], 'get' => [], 'cookie' => [], 'server' => [], 'files' => [], 'request' => []]; - foreach (['var', 'env', 'post', 'get', 'cookie', 'server', 'files', 'request'] as $array) { - foreach ($xpath->query('php/' . $array) as $var) { - assert($var instanceof DOMElement); - $name = (string) $var->getAttribute('name'); - $value = (string) $var->getAttribute('value'); - $force = \false; - $verbatim = \false; - if ($var->hasAttribute('force')) { - $force = (bool) $this->getBoolean($var->getAttribute('force'), \false); - } - if ($var->hasAttribute('verbatim')) { - $verbatim = $this->getBoolean($var->getAttribute('verbatim'), \false); - } - if (!$verbatim) { - $value = $this->getBoolean($value, $value); - } - $variables[$array][] = new \PHPUnit\TextUI\XmlConfiguration\Variable($name, $value, $force); - } - } - return new \PHPUnit\TextUI\XmlConfiguration\Php(\PHPUnit\TextUI\XmlConfiguration\DirectoryCollection::fromArray($includePaths), \PHPUnit\TextUI\XmlConfiguration\IniSettingCollection::fromArray($iniSettings), \PHPUnit\TextUI\XmlConfiguration\ConstantCollection::fromArray($constants), \PHPUnit\TextUI\XmlConfiguration\VariableCollection::fromArray($variables['var']), \PHPUnit\TextUI\XmlConfiguration\VariableCollection::fromArray($variables['env']), \PHPUnit\TextUI\XmlConfiguration\VariableCollection::fromArray($variables['post']), \PHPUnit\TextUI\XmlConfiguration\VariableCollection::fromArray($variables['get']), \PHPUnit\TextUI\XmlConfiguration\VariableCollection::fromArray($variables['cookie']), \PHPUnit\TextUI\XmlConfiguration\VariableCollection::fromArray($variables['server']), \PHPUnit\TextUI\XmlConfiguration\VariableCollection::fromArray($variables['files']), \PHPUnit\TextUI\XmlConfiguration\VariableCollection::fromArray($variables['request'])); + return $this->enforceTimeLimit !== null; } - private function phpunit(string $filename, DOMDocument $document) : \PHPUnit\TextUI\XmlConfiguration\PHPUnit + /** + * @throws Exception + */ + public function enforceTimeLimit() : bool { - $executionOrder = TestSuiteSorter::ORDER_DEFAULT; - $defectsFirst = \false; - $resolveDependencies = $this->getBooleanAttribute($document->documentElement, 'resolveDependencies', \true); - if ($document->documentElement->hasAttribute('executionOrder')) { - foreach (explode(',', $document->documentElement->getAttribute('executionOrder')) as $order) { - switch ($order) { - case 'default': - $executionOrder = TestSuiteSorter::ORDER_DEFAULT; - $defectsFirst = \false; - $resolveDependencies = \true; - break; - case 'depends': - $resolveDependencies = \true; - break; - case 'no-depends': - $resolveDependencies = \false; - break; - case 'defects': - $defectsFirst = \true; - break; - case 'duration': - $executionOrder = TestSuiteSorter::ORDER_DURATION; - break; - case 'random': - $executionOrder = TestSuiteSorter::ORDER_RANDOMIZED; - break; - case 'reverse': - $executionOrder = TestSuiteSorter::ORDER_REVERSED; - break; - case 'size': - $executionOrder = TestSuiteSorter::ORDER_SIZE; - break; - } - } - } - $printerClass = $this->getStringAttribute($document->documentElement, 'printerClass'); - $testdox = $this->getBooleanAttribute($document->documentElement, 'testdox', \false); - $conflictBetweenPrinterClassAndTestdox = \false; - if ($testdox) { - if ($printerClass !== null) { - $conflictBetweenPrinterClassAndTestdox = \true; - } - $printerClass = CliTestDoxPrinter::class; - } - $cacheResultFile = $this->getStringAttribute($document->documentElement, 'cacheResultFile'); - if ($cacheResultFile !== null) { - $cacheResultFile = $this->toAbsolutePath($filename, $cacheResultFile); - } - $bootstrap = $this->getStringAttribute($document->documentElement, 'bootstrap'); - if ($bootstrap !== null) { - $bootstrap = $this->toAbsolutePath($filename, $bootstrap); - } - $extensionsDirectory = $this->getStringAttribute($document->documentElement, 'extensionsDirectory'); - if ($extensionsDirectory !== null) { - $extensionsDirectory = $this->toAbsolutePath($filename, $extensionsDirectory); - } - $testSuiteLoaderFile = $this->getStringAttribute($document->documentElement, 'testSuiteLoaderFile'); - if ($testSuiteLoaderFile !== null) { - $testSuiteLoaderFile = $this->toAbsolutePath($filename, $testSuiteLoaderFile); - } - $printerFile = $this->getStringAttribute($document->documentElement, 'printerFile'); - if ($printerFile !== null) { - $printerFile = $this->toAbsolutePath($filename, $printerFile); + if ($this->enforceTimeLimit === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); } - return new \PHPUnit\TextUI\XmlConfiguration\PHPUnit($this->getBooleanAttribute($document->documentElement, 'cacheResult', \true), $cacheResultFile, $this->getColumns($document), $this->getColors($document), $this->getBooleanAttribute($document->documentElement, 'stderr', \false), $this->getBooleanAttribute($document->documentElement, 'noInteraction', \false), $this->getBooleanAttribute($document->documentElement, 'verbose', \false), $this->getBooleanAttribute($document->documentElement, 'reverseDefectList', \false), $this->getBooleanAttribute($document->documentElement, 'convertDeprecationsToExceptions', \false), $this->getBooleanAttribute($document->documentElement, 'convertErrorsToExceptions', \true), $this->getBooleanAttribute($document->documentElement, 'convertNoticesToExceptions', \true), $this->getBooleanAttribute($document->documentElement, 'convertWarningsToExceptions', \true), $this->getBooleanAttribute($document->documentElement, 'forceCoversAnnotation', \false), $bootstrap, $this->getBooleanAttribute($document->documentElement, 'processIsolation', \false), $this->getBooleanAttribute($document->documentElement, 'failOnEmptyTestSuite', \false), $this->getBooleanAttribute($document->documentElement, 'failOnIncomplete', \false), $this->getBooleanAttribute($document->documentElement, 'failOnRisky', \false), $this->getBooleanAttribute($document->documentElement, 'failOnSkipped', \false), $this->getBooleanAttribute($document->documentElement, 'failOnWarning', \false), $this->getBooleanAttribute($document->documentElement, 'stopOnDefect', \false), $this->getBooleanAttribute($document->documentElement, 'stopOnError', \false), $this->getBooleanAttribute($document->documentElement, 'stopOnFailure', \false), $this->getBooleanAttribute($document->documentElement, 'stopOnWarning', \false), $this->getBooleanAttribute($document->documentElement, 'stopOnIncomplete', \false), $this->getBooleanAttribute($document->documentElement, 'stopOnRisky', \false), $this->getBooleanAttribute($document->documentElement, 'stopOnSkipped', \false), $extensionsDirectory, $this->getStringAttribute($document->documentElement, 'testSuiteLoaderClass'), $testSuiteLoaderFile, $printerClass, $printerFile, $this->getBooleanAttribute($document->documentElement, 'beStrictAboutChangesToGlobalState', \false), $this->getBooleanAttribute($document->documentElement, 'beStrictAboutOutputDuringTests', \false), $this->getBooleanAttribute($document->documentElement, 'beStrictAboutResourceUsageDuringSmallTests', \false), $this->getBooleanAttribute($document->documentElement, 'beStrictAboutTestsThatDoNotTestAnything', \true), $this->getBooleanAttribute($document->documentElement, 'beStrictAboutTodoAnnotatedTests', \false), $this->getBooleanAttribute($document->documentElement, 'beStrictAboutCoversAnnotation', \false), $this->getBooleanAttribute($document->documentElement, 'enforceTimeLimit', \false), $this->getIntegerAttribute($document->documentElement, 'defaultTimeLimit', 1), $this->getIntegerAttribute($document->documentElement, 'timeoutForSmallTests', 1), $this->getIntegerAttribute($document->documentElement, 'timeoutForMediumTests', 10), $this->getIntegerAttribute($document->documentElement, 'timeoutForLargeTests', 60), $this->getStringAttribute($document->documentElement, 'defaultTestSuite'), $executionOrder, $resolveDependencies, $defectsFirst, $this->getBooleanAttribute($document->documentElement, 'backupGlobals', \false), $this->getBooleanAttribute($document->documentElement, 'backupStaticAttributes', \false), $this->getBooleanAttribute($document->documentElement, 'registerMockObjectsFromTestArgumentsRecursively', \false), $conflictBetweenPrinterClassAndTestdox); + return $this->enforceTimeLimit; } - private function getColors(DOMDocument $document) : string + public function hasExcludeGroups() : bool { - $colors = DefaultResultPrinter::COLOR_DEFAULT; - if ($document->documentElement->hasAttribute('colors')) { - /* only allow boolean for compatibility with previous versions - 'always' only allowed from command line */ - if ($this->getBoolean($document->documentElement->getAttribute('colors'), \false)) { - $colors = DefaultResultPrinter::COLOR_AUTO; - } else { - $colors = DefaultResultPrinter::COLOR_NEVER; - } - } - return $colors; + return $this->excludeGroups !== null; } /** - * @return int|string + * @throws Exception */ - private function getColumns(DOMDocument $document) + public function excludeGroups() : array { - $columns = 80; - if ($document->documentElement->hasAttribute('columns')) { - $columns = (string) $document->documentElement->getAttribute('columns'); - if ($columns !== 'max') { - $columns = $this->getInteger($columns, 80); - } + if ($this->excludeGroups === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); } - return $columns; + return $this->excludeGroups; } - private function testSuite(string $filename, DOMXPath $xpath) : \PHPUnit\TextUI\XmlConfiguration\TestSuiteCollection + public function hasExecutionOrder() : bool { - $testSuites = []; - foreach ($this->getTestSuiteElements($xpath) as $element) { - $exclude = []; - foreach ($element->getElementsByTagName('exclude') as $excludeNode) { - $excludeFile = (string) $excludeNode->textContent; - if ($excludeFile) { - $exclude[] = new \PHPUnit\TextUI\XmlConfiguration\File($this->toAbsolutePath($filename, $excludeFile)); - } - } - $directories = []; - foreach ($element->getElementsByTagName('directory') as $directoryNode) { - assert($directoryNode instanceof DOMElement); - $directory = (string) $directoryNode->textContent; - if (empty($directory)) { - continue; - } - $prefix = ''; - if ($directoryNode->hasAttribute('prefix')) { - $prefix = (string) $directoryNode->getAttribute('prefix'); - } - $suffix = 'Test.php'; - if ($directoryNode->hasAttribute('suffix')) { - $suffix = (string) $directoryNode->getAttribute('suffix'); - } - $phpVersion = PHP_VERSION; - if ($directoryNode->hasAttribute('phpVersion')) { - $phpVersion = (string) $directoryNode->getAttribute('phpVersion'); - } - $phpVersionOperator = new VersionComparisonOperator('>='); - if ($directoryNode->hasAttribute('phpVersionOperator')) { - $phpVersionOperator = new VersionComparisonOperator((string) $directoryNode->getAttribute('phpVersionOperator')); - } - $directories[] = new \PHPUnit\TextUI\XmlConfiguration\TestDirectory($this->toAbsolutePath($filename, $directory), $prefix, $suffix, $phpVersion, $phpVersionOperator); - } - $files = []; - foreach ($element->getElementsByTagName('file') as $fileNode) { - assert($fileNode instanceof DOMElement); - $file = (string) $fileNode->textContent; - if (empty($file)) { - continue; - } - $phpVersion = PHP_VERSION; - if ($fileNode->hasAttribute('phpVersion')) { - $phpVersion = (string) $fileNode->getAttribute('phpVersion'); - } - $phpVersionOperator = new VersionComparisonOperator('>='); - if ($fileNode->hasAttribute('phpVersionOperator')) { - $phpVersionOperator = new VersionComparisonOperator((string) $fileNode->getAttribute('phpVersionOperator')); - } - $files[] = new \PHPUnit\TextUI\XmlConfiguration\TestFile($this->toAbsolutePath($filename, $file), $phpVersion, $phpVersionOperator); - } - $testSuites[] = new TestSuiteConfiguration((string) $element->getAttribute('name'), \PHPUnit\TextUI\XmlConfiguration\TestDirectoryCollection::fromArray($directories), \PHPUnit\TextUI\XmlConfiguration\TestFileCollection::fromArray($files), \PHPUnit\TextUI\XmlConfiguration\FileCollection::fromArray($exclude)); - } - return \PHPUnit\TextUI\XmlConfiguration\TestSuiteCollection::fromArray($testSuites); + return $this->executionOrder !== null; } /** - * @return DOMElement[] + * @throws Exception */ - private function getTestSuiteElements(DOMXPath $xpath) : array + public function executionOrder() : int { - /** @var DOMElement[] $elements */ - $elements = []; - $testSuiteNodes = $xpath->query('testsuites/testsuite'); - if ($testSuiteNodes->length === 0) { - $testSuiteNodes = $xpath->query('testsuite'); - } - if ($testSuiteNodes->length === 1) { - $element = $testSuiteNodes->item(0); - assert($element instanceof DOMElement); - $elements[] = $element; - } else { - foreach ($testSuiteNodes as $testSuiteNode) { - assert($testSuiteNode instanceof DOMElement); - $elements[] = $testSuiteNode; - } + if ($this->executionOrder === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); } - return $elements; + return $this->executionOrder; } - private function element(DOMXPath $xpath, string $element) : ?DOMElement + public function hasExecutionOrderDefects() : bool { - $nodes = $xpath->query($element); - if ($nodes->length === 1) { - $node = $nodes->item(0); - assert($node instanceof DOMElement); - return $node; - } - return null; + return $this->executionOrderDefects !== null; } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TextUI\XmlConfiguration\Logging; - -use PHPUnit\TextUI\XmlConfiguration\File; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - * - * @psalm-immutable - */ -final class Junit -{ /** - * @var File + * @throws Exception */ - private $target; - public function __construct(File $target) + public function executionOrderDefects() : int { - $this->target = $target; + if ($this->executionOrderDefects === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->executionOrderDefects; } - public function target() : File + public function hasFailOnEmptyTestSuite() : bool { - return $this->target; + return $this->failOnEmptyTestSuite !== null; } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TextUI\XmlConfiguration\Logging; - -use PHPUnit\TextUI\XmlConfiguration\Exception; -use PHPUnit\TextUI\XmlConfiguration\Logging\TestDox\Html as TestDoxHtml; -use PHPUnit\TextUI\XmlConfiguration\Logging\TestDox\Text as TestDoxText; -use PHPUnit\TextUI\XmlConfiguration\Logging\TestDox\Xml as TestDoxXml; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - * - * @psalm-immutable - */ -final class Logging -{ /** - * @var ?Junit + * @throws Exception */ - private $junit; + public function failOnEmptyTestSuite() : bool + { + if ($this->failOnEmptyTestSuite === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->failOnEmptyTestSuite; + } + public function hasFailOnIncomplete() : bool + { + return $this->failOnIncomplete !== null; + } /** - * @var ?Text + * @throws Exception */ - private $text; + public function failOnIncomplete() : bool + { + if ($this->failOnIncomplete === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->failOnIncomplete; + } + public function hasFailOnRisky() : bool + { + return $this->failOnRisky !== null; + } /** - * @var ?TeamCity + * @throws Exception */ - private $teamCity; + public function failOnRisky() : bool + { + if ($this->failOnRisky === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->failOnRisky; + } + public function hasFailOnSkipped() : bool + { + return $this->failOnSkipped !== null; + } /** - * @var ?TestDoxHtml + * @throws Exception */ - private $testDoxHtml; + public function failOnSkipped() : bool + { + if ($this->failOnSkipped === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->failOnSkipped; + } + public function hasFailOnWarning() : bool + { + return $this->failOnWarning !== null; + } /** - * @var ?TestDoxText + * @throws Exception */ - private $testDoxText; + public function failOnWarning() : bool + { + if ($this->failOnWarning === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->failOnWarning; + } + public function hasFilter() : bool + { + return $this->filter !== null; + } /** - * @var ?TestDoxXml + * @throws Exception */ - private $testDoxXml; - public function __construct(?\PHPUnit\TextUI\XmlConfiguration\Logging\Junit $junit, ?\PHPUnit\TextUI\XmlConfiguration\Logging\Text $text, ?\PHPUnit\TextUI\XmlConfiguration\Logging\TeamCity $teamCity, ?TestDoxHtml $testDoxHtml, ?TestDoxText $testDoxText, ?TestDoxXml $testDoxXml) + public function filter() : string { - $this->junit = $junit; - $this->text = $text; - $this->teamCity = $teamCity; - $this->testDoxHtml = $testDoxHtml; - $this->testDoxText = $testDoxText; - $this->testDoxXml = $testDoxXml; + if ($this->filter === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->filter; } - public function hasJunit() : bool + public function hasGenerateConfiguration() : bool { - return $this->junit !== null; + return $this->generateConfiguration !== null; } - public function junit() : \PHPUnit\TextUI\XmlConfiguration\Logging\Junit + /** + * @throws Exception + */ + public function generateConfiguration() : bool { - if ($this->junit === null) { - throw new Exception('Logger "JUnit XML" is not configured'); + if ($this->generateConfiguration === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); } - return $this->junit; + return $this->generateConfiguration; } - public function hasText() : bool + public function hasMigrateConfiguration() : bool { - return $this->text !== null; + return $this->migrateConfiguration !== null; } - public function text() : \PHPUnit\TextUI\XmlConfiguration\Logging\Text + /** + * @throws Exception + */ + public function migrateConfiguration() : bool { - if ($this->text === null) { - throw new Exception('Logger "Text" is not configured'); + if ($this->migrateConfiguration === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); } - return $this->text; + return $this->migrateConfiguration; } - public function hasTeamCity() : bool + public function hasGroups() : bool { - return $this->teamCity !== null; + return $this->groups !== null; } - public function teamCity() : \PHPUnit\TextUI\XmlConfiguration\Logging\TeamCity + /** + * @throws Exception + */ + public function groups() : array { - if ($this->teamCity === null) { - throw new Exception('Logger "Team City" is not configured'); + if ($this->groups === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); } - return $this->teamCity; + return $this->groups; } - public function hasTestDoxHtml() : bool + public function hasTestsCovering() : bool { - return $this->testDoxHtml !== null; + return $this->testsCovering !== null; } - public function testDoxHtml() : TestDoxHtml + /** + * @throws Exception + */ + public function testsCovering() : array { - if ($this->testDoxHtml === null) { - throw new Exception('Logger "TestDox HTML" is not configured'); + if ($this->testsCovering === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); } - return $this->testDoxHtml; + return $this->testsCovering; } - public function hasTestDoxText() : bool + public function hasTestsUsing() : bool { - return $this->testDoxText !== null; + return $this->testsUsing !== null; } - public function testDoxText() : TestDoxText + /** + * @throws Exception + */ + public function testsUsing() : array { - if ($this->testDoxText === null) { - throw new Exception('Logger "TestDox Text" is not configured'); + if ($this->testsUsing === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); } - return $this->testDoxText; + return $this->testsUsing; } - public function hasTestDoxXml() : bool + public function hasHelp() : bool { - return $this->testDoxXml !== null; + return $this->help !== null; } - public function testDoxXml() : TestDoxXml + /** + * @throws Exception + */ + public function help() : bool { - if ($this->testDoxXml === null) { - throw new Exception('Logger "TestDox XML" is not configured'); + if ($this->help === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); } - return $this->testDoxXml; + return $this->help; + } + public function hasIncludePath() : bool + { + return $this->includePath !== null; } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TextUI\XmlConfiguration\Logging; - -use PHPUnit\TextUI\XmlConfiguration\File; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - * - * @psalm-immutable - */ -final class TeamCity -{ /** - * @var File + * @throws Exception */ - private $target; - public function __construct(File $target) + public function includePath() : string { - $this->target = $target; + if ($this->includePath === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->includePath; } - public function target() : File + public function hasIniSettings() : bool { - return $this->target; + return $this->iniSettings !== null; } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TextUI\XmlConfiguration\Logging\TestDox; - -use PHPUnit\TextUI\XmlConfiguration\File; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - * - * @psalm-immutable - */ -final class Html -{ /** - * @var File + * @throws Exception */ - private $target; - public function __construct(File $target) + public function iniSettings() : array { - $this->target = $target; + if ($this->iniSettings === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->iniSettings; } - public function target() : File + public function hasJunitLogfile() : bool { - return $this->target; + return $this->junitLogfile !== null; } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TextUI\XmlConfiguration\Logging\TestDox; - -use PHPUnit\TextUI\XmlConfiguration\File; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - * - * @psalm-immutable - */ -final class Text -{ /** - * @var File + * @throws Exception */ - private $target; - public function __construct(File $target) + public function junitLogfile() : string { - $this->target = $target; + if ($this->junitLogfile === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->junitLogfile; } - public function target() : File + public function hasListGroups() : bool { - return $this->target; + return $this->listGroups !== null; } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TextUI\XmlConfiguration\Logging\TestDox; - -use PHPUnit\TextUI\XmlConfiguration\File; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - * - * @psalm-immutable - */ -final class Xml -{ /** - * @var File + * @throws Exception */ - private $target; - public function __construct(File $target) + public function listGroups() : bool { - $this->target = $target; + if ($this->listGroups === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->listGroups; } - public function target() : File + public function hasListSuites() : bool { - return $this->target; + return $this->listSuites !== null; } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TextUI\XmlConfiguration\Logging; - -use PHPUnit\TextUI\XmlConfiguration\File; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - * - * @psalm-immutable - */ -final class Text -{ /** - * @var File + * @throws Exception */ - private $target; - public function __construct(File $target) + public function listSuites() : bool { - $this->target = $target; + if ($this->listSuites === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->listSuites; } - public function target() : File + public function hasListTests() : bool { - return $this->target; + return $this->listTests !== null; } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TextUI\XmlConfiguration; - -use function array_key_exists; -use function sprintf; -use function version_compare; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class MigrationBuilder -{ - private const AVAILABLE_MIGRATIONS = ['8.5' => [\PHPUnit\TextUI\XmlConfiguration\RemoveLogTypes::class], '9.2' => [\PHPUnit\TextUI\XmlConfiguration\RemoveCacheTokensAttribute::class, \PHPUnit\TextUI\XmlConfiguration\IntroduceCoverageElement::class, \PHPUnit\TextUI\XmlConfiguration\MoveAttributesFromRootToCoverage::class, \PHPUnit\TextUI\XmlConfiguration\MoveAttributesFromFilterWhitelistToCoverage::class, \PHPUnit\TextUI\XmlConfiguration\MoveWhitelistIncludesToCoverage::class, \PHPUnit\TextUI\XmlConfiguration\MoveWhitelistExcludesToCoverage::class, \PHPUnit\TextUI\XmlConfiguration\RemoveEmptyFilter::class, \PHPUnit\TextUI\XmlConfiguration\CoverageCloverToReport::class, \PHPUnit\TextUI\XmlConfiguration\CoverageCrap4jToReport::class, \PHPUnit\TextUI\XmlConfiguration\CoverageHtmlToReport::class, \PHPUnit\TextUI\XmlConfiguration\CoveragePhpToReport::class, \PHPUnit\TextUI\XmlConfiguration\CoverageTextToReport::class, \PHPUnit\TextUI\XmlConfiguration\CoverageXmlToReport::class, \PHPUnit\TextUI\XmlConfiguration\ConvertLogTypes::class, \PHPUnit\TextUI\XmlConfiguration\UpdateSchemaLocationTo93::class]]; /** - * @throws MigrationBuilderException + * @throws Exception */ - public function build(string $fromVersion) : array + public function listTests() : bool { - if (!array_key_exists($fromVersion, self::AVAILABLE_MIGRATIONS)) { - throw new \PHPUnit\TextUI\XmlConfiguration\MigrationBuilderException(sprintf('Migration from schema version %s is not supported', $fromVersion)); - } - $stack = []; - foreach (self::AVAILABLE_MIGRATIONS as $version => $migrations) { - if (version_compare($version, $fromVersion, '<')) { - continue; - } - foreach ($migrations as $migration) { - $stack[] = new $migration(); - } + if ($this->listTests === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); } - return $stack; + return $this->listTests; } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TextUI\XmlConfiguration; - -use RuntimeException; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class MigrationBuilderException extends RuntimeException implements \PHPUnit\Exception -{ -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TextUI\XmlConfiguration; - -use RuntimeException; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class MigrationException extends RuntimeException implements \PHPUnit\Exception -{ -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TextUI\XmlConfiguration; - -use DOMDocument; -use DOMElement; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class ConvertLogTypes implements \PHPUnit\TextUI\XmlConfiguration\Migration -{ - public function migrate(DOMDocument $document) : void + public function hasListTestsXml() : bool { - $logging = $document->getElementsByTagName('logging')->item(0); - if (!$logging instanceof DOMElement) { - return; - } - $types = ['junit' => 'junit', 'teamcity' => 'teamcity', 'testdox-html' => 'testdoxHtml', 'testdox-text' => 'testdoxText', 'testdox-xml' => 'testdoxXml', 'plain' => 'text']; - $logNodes = []; - foreach ($logging->getElementsByTagName('log') as $logNode) { - if (!isset($types[$logNode->getAttribute('type')])) { - continue; - } - $logNodes[] = $logNode; - } - foreach ($logNodes as $oldNode) { - $newLogNode = $document->createElement($types[$oldNode->getAttribute('type')]); - $newLogNode->setAttribute('outputFile', $oldNode->getAttribute('target')); - $logging->replaceChild($newLogNode, $oldNode); + return $this->listTestsXml !== null; + } + /** + * @throws Exception + */ + public function listTestsXml() : string + { + if ($this->listTestsXml === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); } + return $this->listTestsXml; } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TextUI\XmlConfiguration; - -use DOMElement; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class CoverageCloverToReport extends \PHPUnit\TextUI\XmlConfiguration\LogToReportMigration -{ - protected function forType() : string + public function hasLoader() : bool { - return 'coverage-clover'; + return $this->loader !== null; } - protected function toReportFormat(DOMElement $logNode) : DOMElement + /** + * @throws Exception + */ + public function loader() : string { - $clover = $logNode->ownerDocument->createElement('clover'); - $clover->setAttribute('outputFile', $logNode->getAttribute('target')); - return $clover; + if ($this->loader === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->loader; } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TextUI\XmlConfiguration; - -use DOMElement; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class CoverageCrap4jToReport extends \PHPUnit\TextUI\XmlConfiguration\LogToReportMigration -{ - protected function forType() : string + public function hasNoCoverage() : bool { - return 'coverage-crap4j'; + return $this->noCoverage !== null; } - protected function toReportFormat(DOMElement $logNode) : DOMElement + /** + * @throws Exception + */ + public function noCoverage() : bool { - $crap4j = $logNode->ownerDocument->createElement('crap4j'); - $crap4j->setAttribute('outputFile', $logNode->getAttribute('target')); - $this->migrateAttributes($logNode, $crap4j, ['threshold']); - return $crap4j; + if ($this->noCoverage === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->noCoverage; } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TextUI\XmlConfiguration; - -use DOMElement; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class CoverageHtmlToReport extends \PHPUnit\TextUI\XmlConfiguration\LogToReportMigration -{ - protected function forType() : string + public function hasNoExtensions() : bool { - return 'coverage-html'; + return $this->noExtensions !== null; } - protected function toReportFormat(DOMElement $logNode) : DOMElement + /** + * @throws Exception + */ + public function noExtensions() : bool { - $html = $logNode->ownerDocument->createElement('html'); - $html->setAttribute('outputDirectory', $logNode->getAttribute('target')); - $this->migrateAttributes($logNode, $html, ['lowUpperBound', 'highLowerBound']); - return $html; + if ($this->noExtensions === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->noExtensions; } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TextUI\XmlConfiguration; - -use DOMElement; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class CoveragePhpToReport extends \PHPUnit\TextUI\XmlConfiguration\LogToReportMigration -{ - protected function forType() : string + public function hasExtensions() : bool { - return 'coverage-php'; + return $this->extensions !== null; } - protected function toReportFormat(DOMElement $logNode) : DOMElement + /** + * @throws Exception + */ + public function extensions() : array { - $php = $logNode->ownerDocument->createElement('php'); - $php->setAttribute('outputFile', $logNode->getAttribute('target')); - return $php; + if ($this->extensions === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->extensions; } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TextUI\XmlConfiguration; - -use DOMElement; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class CoverageTextToReport extends \PHPUnit\TextUI\XmlConfiguration\LogToReportMigration -{ - protected function forType() : string + public function hasUnavailableExtensions() : bool { - return 'coverage-text'; + return $this->unavailableExtensions !== null; } - protected function toReportFormat(DOMElement $logNode) : DOMElement + /** + * @throws Exception + */ + public function unavailableExtensions() : array { - $text = $logNode->ownerDocument->createElement('text'); - $text->setAttribute('outputFile', $logNode->getAttribute('target')); - $this->migrateAttributes($logNode, $text, ['showUncoveredFiles', 'showOnlySummary']); - return $text; + if ($this->unavailableExtensions === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->unavailableExtensions; } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TextUI\XmlConfiguration; - -use DOMElement; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class CoverageXmlToReport extends \PHPUnit\TextUI\XmlConfiguration\LogToReportMigration -{ - protected function forType() : string + public function hasNoInteraction() : bool { - return 'coverage-xml'; + return $this->noInteraction !== null; } - protected function toReportFormat(DOMElement $logNode) : DOMElement + /** + * @throws Exception + */ + public function noInteraction() : bool { - $xml = $logNode->ownerDocument->createElement('xml'); - $xml->setAttribute('outputDirectory', $logNode->getAttribute('target')); - return $xml; + if ($this->noInteraction === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->noInteraction; } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TextUI\XmlConfiguration; - -use DOMDocument; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class IntroduceCoverageElement implements \PHPUnit\TextUI\XmlConfiguration\Migration -{ - public function migrate(DOMDocument $document) : void + public function hasNoLogging() : bool { - $coverage = $document->createElement('coverage'); - $document->documentElement->insertBefore($coverage, $document->documentElement->firstChild); + return $this->noLogging !== null; } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TextUI\XmlConfiguration; - -use function sprintf; -use DOMDocument; -use DOMElement; -use DOMXPath; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -abstract class LogToReportMigration implements \PHPUnit\TextUI\XmlConfiguration\Migration -{ /** - * @throws MigrationException + * @throws Exception */ - public function migrate(DOMDocument $document) : void + public function noLogging() : bool { - $coverage = $document->getElementsByTagName('coverage')->item(0); - if (!$coverage instanceof DOMElement) { - throw new \PHPUnit\TextUI\XmlConfiguration\MigrationException('Unexpected state - No coverage element'); - } - $logNode = $this->findLogNode($document); - if ($logNode === null) { - return; - } - $reportChild = $this->toReportFormat($logNode); - $report = $coverage->getElementsByTagName('report')->item(0); - if ($report === null) { - $report = $coverage->appendChild($document->createElement('report')); + if ($this->noLogging === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); } - $report->appendChild($reportChild); - $logNode->parentNode->removeChild($logNode); + return $this->noLogging; } - protected function migrateAttributes(DOMElement $src, DOMElement $dest, array $attributes) : void + public function hasPrinter() : bool { - foreach ($attributes as $attr) { - if (!$src->hasAttribute($attr)) { - continue; - } - $dest->setAttribute($attr, $src->getAttribute($attr)); - $src->removeAttribute($attr); - } + return $this->printer !== null; } - protected abstract function forType() : string; - protected abstract function toReportFormat(DOMElement $logNode) : DOMElement; - private function findLogNode(DOMDocument $document) : ?DOMElement + /** + * @throws Exception + */ + public function printer() : string { - $logNode = (new DOMXPath($document))->query(sprintf('//logging/log[@type="%s"]', $this->forType()))->item(0); - if (!$logNode instanceof DOMElement) { - return null; + if ($this->printer === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); } - return $logNode; + return $this->printer; + } + public function hasProcessIsolation() : bool + { + return $this->processIsolation !== null; } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TextUI\XmlConfiguration; - -use DOMDocument; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -interface Migration -{ - public function migrate(DOMDocument $document) : void; -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TextUI\XmlConfiguration; - -use DOMDocument; -use DOMElement; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class MoveAttributesFromFilterWhitelistToCoverage implements \PHPUnit\TextUI\XmlConfiguration\Migration -{ /** - * @throws MigrationException + * @throws Exception */ - public function migrate(DOMDocument $document) : void + public function processIsolation() : bool { - $whitelist = $document->getElementsByTagName('whitelist')->item(0); - if (!$whitelist) { - return; + if ($this->processIsolation === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); } - $coverage = $document->getElementsByTagName('coverage')->item(0); - if (!$coverage instanceof DOMElement) { - throw new \PHPUnit\TextUI\XmlConfiguration\MigrationException('Unexpected state - No coverage element'); + return $this->processIsolation; + } + public function hasRandomOrderSeed() : bool + { + return $this->randomOrderSeed !== null; + } + /** + * @throws Exception + */ + public function randomOrderSeed() : int + { + if ($this->randomOrderSeed === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); } - $map = ['addUncoveredFilesFromWhitelist' => 'includeUncoveredFiles', 'processUncoveredFilesFromWhitelist' => 'processUncoveredFiles']; - foreach ($map as $old => $new) { - if (!$whitelist->hasAttribute($old)) { - continue; - } - $coverage->setAttribute($new, $whitelist->getAttribute($old)); - $whitelist->removeAttribute($old); + return $this->randomOrderSeed; + } + public function hasRepeat() : bool + { + return $this->repeat !== null; + } + /** + * @throws Exception + */ + public function repeat() : int + { + if ($this->repeat === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); } + return $this->repeat; + } + public function hasReportUselessTests() : bool + { + return $this->reportUselessTests !== null; } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TextUI\XmlConfiguration; - -use DOMDocument; -use DOMElement; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class MoveAttributesFromRootToCoverage implements \PHPUnit\TextUI\XmlConfiguration\Migration -{ /** - * @throws MigrationException + * @throws Exception */ - public function migrate(DOMDocument $document) : void + public function reportUselessTests() : bool { - $map = ['disableCodeCoverageIgnore' => 'disableCodeCoverageIgnore', 'ignoreDeprecatedCodeUnitsFromCodeCoverage' => 'ignoreDeprecatedCodeUnits']; - $root = $document->documentElement; - $coverage = $document->getElementsByTagName('coverage')->item(0); - if (!$coverage instanceof DOMElement) { - throw new \PHPUnit\TextUI\XmlConfiguration\MigrationException('Unexpected state - No coverage element'); + if ($this->reportUselessTests === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); } - foreach ($map as $old => $new) { - if (!$root->hasAttribute($old)) { - continue; - } - $coverage->setAttribute($new, $root->getAttribute($old)); - $root->removeAttribute($old); + return $this->reportUselessTests; + } + public function hasResolveDependencies() : bool + { + return $this->resolveDependencies !== null; + } + /** + * @throws Exception + */ + public function resolveDependencies() : bool + { + if ($this->resolveDependencies === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); } + return $this->resolveDependencies; + } + public function hasReverseList() : bool + { + return $this->reverseList !== null; } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TextUI\XmlConfiguration; - -use function assert; -use function in_array; -use DOMDocument; -use DOMElement; -use PHPUnit\Util\Xml\SnapshotNodeList; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class MoveWhitelistExcludesToCoverage implements \PHPUnit\TextUI\XmlConfiguration\Migration -{ /** - * @throws MigrationException + * @throws Exception */ - public function migrate(DOMDocument $document) : void + public function reverseList() : bool { - $whitelist = $document->getElementsByTagName('whitelist')->item(0); - if ($whitelist === null) { - return; + if ($this->reverseList === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); } - $excludeNodes = SnapshotNodeList::fromNodeList($whitelist->getElementsByTagName('exclude')); - if ($excludeNodes->count() === 0) { - return; - } - $coverage = $document->getElementsByTagName('coverage')->item(0); - if (!$coverage instanceof DOMElement) { - throw new \PHPUnit\TextUI\XmlConfiguration\MigrationException('Unexpected state - No coverage element'); - } - $targetExclude = $coverage->getElementsByTagName('exclude')->item(0); - if ($targetExclude === null) { - $targetExclude = $coverage->appendChild($document->createElement('exclude')); - } - foreach ($excludeNodes as $excludeNode) { - assert($excludeNode instanceof DOMElement); - foreach (SnapshotNodeList::fromNodeList($excludeNode->childNodes) as $child) { - if (!$child instanceof DOMElement || !in_array($child->nodeName, ['directory', 'file'], \true)) { - continue; - } - $targetExclude->appendChild($child); - } - if ($excludeNode->getElementsByTagName('*')->count() !== 0) { - throw new \PHPUnit\TextUI\XmlConfiguration\MigrationException('Dangling child elements in exclude found.'); - } - $whitelist->removeChild($excludeNode); + return $this->reverseList; + } + public function hasStderr() : bool + { + return $this->stderr !== null; + } + /** + * @throws Exception + */ + public function stderr() : bool + { + if ($this->stderr === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); } + return $this->stderr; + } + public function hasStrictCoverage() : bool + { + return $this->strictCoverage !== null; } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TextUI\XmlConfiguration; - -use DOMDocument; -use DOMElement; -use PHPUnit\Util\Xml\SnapshotNodeList; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class MoveWhitelistIncludesToCoverage implements \PHPUnit\TextUI\XmlConfiguration\Migration -{ /** - * @throws MigrationException + * @throws Exception */ - public function migrate(DOMDocument $document) : void + public function strictCoverage() : bool { - $whitelist = $document->getElementsByTagName('whitelist')->item(0); - if ($whitelist === null) { - return; - } - $coverage = $document->getElementsByTagName('coverage')->item(0); - if (!$coverage instanceof DOMElement) { - throw new \PHPUnit\TextUI\XmlConfiguration\MigrationException('Unexpected state - No coverage element'); - } - $include = $document->createElement('include'); - $coverage->appendChild($include); - foreach (SnapshotNodeList::fromNodeList($whitelist->childNodes) as $child) { - if (!$child instanceof DOMElement) { - continue; - } - if (!($child->nodeName === 'directory' || $child->nodeName === 'file')) { - continue; - } - $include->appendChild($child); + if ($this->strictCoverage === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); } + return $this->strictCoverage; } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TextUI\XmlConfiguration; - -use DOMDocument; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class RemoveCacheTokensAttribute implements \PHPUnit\TextUI\XmlConfiguration\Migration -{ - public function migrate(DOMDocument $document) : void + public function hasStopOnDefect() : bool { - $root = $document->documentElement; - if ($root->hasAttribute('cacheTokens')) { - $root->removeAttribute('cacheTokens'); - } + return $this->stopOnDefect !== null; } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TextUI\XmlConfiguration; - -use function sprintf; -use DOMDocument; -use DOMElement; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class RemoveEmptyFilter implements \PHPUnit\TextUI\XmlConfiguration\Migration -{ /** - * @throws MigrationException + * @throws Exception */ - public function migrate(DOMDocument $document) : void + public function stopOnDefect() : bool { - $whitelist = $document->getElementsByTagName('whitelist')->item(0); - if ($whitelist instanceof DOMElement) { - $this->ensureEmpty($whitelist); - $whitelist->parentNode->removeChild($whitelist); - } - $filter = $document->getElementsByTagName('filter')->item(0); - if ($filter instanceof DOMElement) { - $this->ensureEmpty($filter); - $filter->parentNode->removeChild($filter); + if ($this->stopOnDefect === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); } + return $this->stopOnDefect; + } + public function hasStopOnError() : bool + { + return $this->stopOnError !== null; } /** - * @throws MigrationException + * @throws Exception */ - private function ensureEmpty(DOMElement $element) : void + public function stopOnError() : bool { - if ($element->attributes->length > 0) { - throw new \PHPUnit\TextUI\XmlConfiguration\MigrationException(sprintf('%s element has unexpected attributes', $element->nodeName)); - } - if ($element->getElementsByTagName('*')->length > 0) { - throw new \PHPUnit\TextUI\XmlConfiguration\MigrationException(sprintf('%s element has unexpected children', $element->nodeName)); + if ($this->stopOnError === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); } + return $this->stopOnError; } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TextUI\XmlConfiguration; - -use function assert; -use DOMDocument; -use DOMElement; -use PHPUnit\Util\Xml\SnapshotNodeList; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class RemoveLogTypes implements \PHPUnit\TextUI\XmlConfiguration\Migration -{ - public function migrate(DOMDocument $document) : void + public function hasStopOnFailure() : bool { - $logging = $document->getElementsByTagName('logging')->item(0); - if (!$logging instanceof DOMElement) { - return; - } - foreach (SnapshotNodeList::fromNodeList($logging->getElementsByTagName('log')) as $logNode) { - assert($logNode instanceof DOMElement); - switch ($logNode->getAttribute('type')) { - case 'json': - case 'tap': - $logging->removeChild($logNode); - } + return $this->stopOnFailure !== null; + } + /** + * @throws Exception + */ + public function stopOnFailure() : bool + { + if ($this->stopOnFailure === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); } + return $this->stopOnFailure; } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TextUI\XmlConfiguration; - -use DOMDocument; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class UpdateSchemaLocationTo93 implements \PHPUnit\TextUI\XmlConfiguration\Migration -{ - public function migrate(DOMDocument $document) : void + public function hasStopOnIncomplete() : bool { - $document->documentElement->setAttributeNS('http://www.w3.org/2001/XMLSchema-instance', 'xsi:noNamespaceSchemaLocation', 'https://schema.phpunit.de/9.3/phpunit.xsd'); + return $this->stopOnIncomplete !== null; } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TextUI\XmlConfiguration; - -use function sprintf; -use PHPUnit\Util\Xml\Exception as XmlException; -use PHPUnit\Util\Xml\Loader as XmlLoader; -use PHPUnit\Util\Xml\SchemaDetector; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class Migrator -{ /** * @throws Exception - * @throws MigrationBuilderException - * @throws MigrationException - * @throws XmlException */ - public function migrate(string $filename) : string + public function stopOnIncomplete() : bool { - $origin = (new SchemaDetector())->detect($filename); - if (!$origin->detected()) { - throw new \PHPUnit\TextUI\XmlConfiguration\Exception(sprintf('"%s" is not a valid PHPUnit XML configuration file that can be migrated', $filename)); + if ($this->stopOnIncomplete === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); } - $configurationDocument = (new XmlLoader())->loadFile($filename, \false, \true, \true); - foreach ((new \PHPUnit\TextUI\XmlConfiguration\MigrationBuilder())->build($origin->version()) as $migration) { - $migration->migrate($configurationDocument); + return $this->stopOnIncomplete; + } + public function hasStopOnRisky() : bool + { + return $this->stopOnRisky !== null; + } + /** + * @throws Exception + */ + public function stopOnRisky() : bool + { + if ($this->stopOnRisky === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); } - $configurationDocument->formatOutput = \true; - $configurationDocument->preserveWhiteSpace = \false; - return $configurationDocument->saveXML(); + return $this->stopOnRisky; + } + public function hasStopOnSkipped() : bool + { + return $this->stopOnSkipped !== null; } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TextUI\XmlConfiguration; - -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - * - * @psalm-immutable - */ -final class Constant -{ /** - * @var string + * @throws Exception */ - private $name; + public function stopOnSkipped() : bool + { + if ($this->stopOnSkipped === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->stopOnSkipped; + } + public function hasStopOnWarning() : bool + { + return $this->stopOnWarning !== null; + } /** - * @var mixed + * @throws Exception */ - private $value; - public function __construct(string $name, $value) + public function stopOnWarning() : bool { - $this->name = $name; - $this->value = $value; + if ($this->stopOnWarning === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->stopOnWarning; } - public function name() : string + public function hasTeamcityLogfile() : bool { - return $this->name; + return $this->teamcityLogfile !== null; } - public function value() + /** + * @throws Exception + */ + public function teamcityLogfile() : string { - return $this->value; + if ($this->teamcityLogfile === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->teamcityLogfile; + } + public function hasTestdoxExcludeGroups() : bool + { + return $this->testdoxExcludeGroups !== null; } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TextUI\XmlConfiguration; - -use function count; -use Countable; -use IteratorAggregate; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - * - * @psalm-immutable - * - * @template-implements IteratorAggregate - */ -final class ConstantCollection implements Countable, IteratorAggregate -{ /** - * @var Constant[] + * @throws Exception */ - private $constants; + public function testdoxExcludeGroups() : array + { + if ($this->testdoxExcludeGroups === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->testdoxExcludeGroups; + } + public function hasTestdoxGroups() : bool + { + return $this->testdoxGroups !== null; + } /** - * @param Constant[] $constants + * @throws Exception */ - public static function fromArray(array $constants) : self + public function testdoxGroups() : array { - return new self(...$constants); + if ($this->testdoxGroups === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->testdoxGroups; } - private function __construct(\PHPUnit\TextUI\XmlConfiguration\Constant ...$constants) + public function hasTestdoxHtmlFile() : bool { - $this->constants = $constants; + return $this->testdoxHtmlFile !== null; } /** - * @return Constant[] + * @throws Exception */ - public function asArray() : array + public function testdoxHtmlFile() : string { - return $this->constants; + if ($this->testdoxHtmlFile === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->testdoxHtmlFile; } - public function count() : int + public function hasTestdoxTextFile() : bool { - return count($this->constants); + return $this->testdoxTextFile !== null; } - public function getIterator() : \PHPUnit\TextUI\XmlConfiguration\ConstantCollectionIterator + /** + * @throws Exception + */ + public function testdoxTextFile() : string { - return new \PHPUnit\TextUI\XmlConfiguration\ConstantCollectionIterator($this); + if ($this->testdoxTextFile === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->testdoxTextFile; + } + public function hasTestdoxXmlFile() : bool + { + return $this->testdoxXmlFile !== null; } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TextUI\XmlConfiguration; - -use function count; -use function iterator_count; -use Countable; -use Iterator; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - * - * @template-implements Iterator - */ -final class ConstantCollectionIterator implements Countable, Iterator -{ /** - * @var Constant[] + * @throws Exception */ - private $constants; + public function testdoxXmlFile() : string + { + if ($this->testdoxXmlFile === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->testdoxXmlFile; + } + public function hasTestSuffixes() : bool + { + return $this->testSuffixes !== null; + } /** - * @var int + * @throws Exception */ - private $position; - public function __construct(\PHPUnit\TextUI\XmlConfiguration\ConstantCollection $constants) + public function testSuffixes() : array { - $this->constants = $constants->asArray(); + if ($this->testSuffixes === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->testSuffixes; } - public function count() : int + public function hasTestSuite() : bool { - return iterator_count($this); + return $this->testSuite !== null; } - public function rewind() : void + /** + * @throws Exception + */ + public function testSuite() : string { - $this->position = 0; + if ($this->testSuite === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->testSuite; } - public function valid() : bool + public function unrecognizedOptions() : array { - return $this->position < count($this->constants); + return $this->unrecognizedOptions; } - public function key() : int + public function hasUnrecognizedOrderBy() : bool { - return $this->position; + return $this->unrecognizedOrderBy !== null; } - public function current() : \PHPUnit\TextUI\XmlConfiguration\Constant + /** + * @throws Exception + */ + public function unrecognizedOrderBy() : string { - return $this->constants[$this->position]; + if ($this->unrecognizedOrderBy === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->unrecognizedOrderBy; } - public function next() : void + public function hasUseDefaultConfiguration() : bool { - $this->position++; + return $this->useDefaultConfiguration !== null; } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TextUI\XmlConfiguration; - -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - * - * @psalm-immutable - */ -final class IniSetting -{ /** - * @var string + * @throws Exception */ - private $name; + public function useDefaultConfiguration() : bool + { + if ($this->useDefaultConfiguration === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->useDefaultConfiguration; + } + public function hasVerbose() : bool + { + return $this->verbose !== null; + } /** - * @var string + * @throws Exception */ - private $value; - public function __construct(string $name, string $value) + public function verbose() : bool { - $this->name = $name; - $this->value = $value; + if ($this->verbose === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->verbose; } - public function name() : string + public function hasVersion() : bool { - return $this->name; + return $this->version !== null; } - public function value() : string + /** + * @throws Exception + */ + public function version() : bool { - return $this->value; + if ($this->version === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->version; + } + public function hasXdebugFilterFile() : bool + { + return $this->xdebugFilterFile !== null; + } + /** + * @throws Exception + */ + public function xdebugFilterFile() : string + { + if ($this->xdebugFilterFile === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->xdebugFilterFile; } } */ -final class IniSettingCollection implements Countable, IteratorAggregate +final class Exception extends RuntimeException implements \PHPUnit\Exception { - /** - * @var IniSetting[] - */ - private $iniSettings; - /** - * @param IniSetting[] $iniSettings - */ - public static function fromArray(array $iniSettings) : self - { - return new self(...$iniSettings); - } - private function __construct(\PHPUnit\TextUI\XmlConfiguration\IniSetting ...$iniSettings) - { - $this->iniSettings = $iniSettings; - } - /** - * @return IniSetting[] - */ - public function asArray() : array - { - return $this->iniSettings; - } - public function count() : int - { - return count($this->iniSettings); - } - public function getIterator() : \PHPUnit\TextUI\XmlConfiguration\IniSettingCollectionIterator - { - return new \PHPUnit\TextUI\XmlConfiguration\IniSettingCollectionIterator($this); - } } */ -final class IniSettingCollectionIterator implements Countable, Iterator +final class Mapper { /** - * @var IniSetting[] - */ - private $iniSettings; - /** - * @var int + * @throws Exception */ - private $position; - public function __construct(\PHPUnit\TextUI\XmlConfiguration\IniSettingCollection $iniSettings) - { - $this->iniSettings = $iniSettings->asArray(); - } - public function count() : int - { - return iterator_count($this); - } - public function rewind() : void - { - $this->position = 0; - } - public function valid() : bool - { - return $this->position < count($this->iniSettings); - } - public function key() : int - { - return $this->position; - } - public function current() : \PHPUnit\TextUI\XmlConfiguration\IniSetting - { - return $this->iniSettings[$this->position]; - } - public function next() : void + public function mapToLegacyArray(\PHPUnit\TextUI\CliArguments\Configuration $arguments) : array { - $this->position++; + $result = ['extensions' => [], 'listGroups' => \false, 'listSuites' => \false, 'listTests' => \false, 'listTestsXml' => \false, 'loader' => null, 'useDefaultConfiguration' => \true, 'loadedExtensions' => [], 'unavailableExtensions' => [], 'notLoadedExtensions' => []]; + if ($arguments->hasColors()) { + $result['colors'] = $arguments->colors(); + } + if ($arguments->hasBootstrap()) { + $result['bootstrap'] = $arguments->bootstrap(); + } + if ($arguments->hasCacheResult()) { + $result['cacheResult'] = $arguments->cacheResult(); + } + if ($arguments->hasCacheResultFile()) { + $result['cacheResultFile'] = $arguments->cacheResultFile(); + } + if ($arguments->hasColumns()) { + $result['columns'] = $arguments->columns(); + } + if ($arguments->hasConfiguration()) { + $result['configuration'] = $arguments->configuration(); + } + if ($arguments->hasCoverageCacheDirectory()) { + $result['coverageCacheDirectory'] = $arguments->coverageCacheDirectory(); + } + if ($arguments->hasWarmCoverageCache()) { + $result['warmCoverageCache'] = $arguments->warmCoverageCache(); + } + if ($arguments->hasCoverageClover()) { + $result['coverageClover'] = $arguments->coverageClover(); + } + if ($arguments->hasCoverageCobertura()) { + $result['coverageCobertura'] = $arguments->coverageCobertura(); + } + if ($arguments->hasCoverageCrap4J()) { + $result['coverageCrap4J'] = $arguments->coverageCrap4J(); + } + if ($arguments->hasCoverageHtml()) { + $result['coverageHtml'] = $arguments->coverageHtml(); + } + if ($arguments->hasCoveragePhp()) { + $result['coveragePHP'] = $arguments->coveragePhp(); + } + if ($arguments->hasCoverageText()) { + $result['coverageText'] = $arguments->coverageText(); + } + if ($arguments->hasCoverageTextShowUncoveredFiles()) { + $result['coverageTextShowUncoveredFiles'] = $arguments->hasCoverageTextShowUncoveredFiles(); + } + if ($arguments->hasCoverageTextShowOnlySummary()) { + $result['coverageTextShowOnlySummary'] = $arguments->coverageTextShowOnlySummary(); + } + if ($arguments->hasCoverageXml()) { + $result['coverageXml'] = $arguments->coverageXml(); + } + if ($arguments->hasPathCoverage()) { + $result['pathCoverage'] = $arguments->pathCoverage(); + } + if ($arguments->hasDebug()) { + $result['debug'] = $arguments->debug(); + } + if ($arguments->hasHelp()) { + $result['help'] = $arguments->help(); + } + if ($arguments->hasFilter()) { + $result['filter'] = $arguments->filter(); + } + if ($arguments->hasTestSuite()) { + $result['testsuite'] = $arguments->testSuite(); + } + if ($arguments->hasGroups()) { + $result['groups'] = $arguments->groups(); + } + if ($arguments->hasExcludeGroups()) { + $result['excludeGroups'] = $arguments->excludeGroups(); + } + if ($arguments->hasTestsCovering()) { + $result['testsCovering'] = $arguments->testsCovering(); + } + if ($arguments->hasTestsUsing()) { + $result['testsUsing'] = $arguments->testsUsing(); + } + if ($arguments->hasTestSuffixes()) { + $result['testSuffixes'] = $arguments->testSuffixes(); + } + if ($arguments->hasIncludePath()) { + $result['includePath'] = $arguments->includePath(); + } + if ($arguments->hasListGroups()) { + $result['listGroups'] = $arguments->listGroups(); + } + if ($arguments->hasListSuites()) { + $result['listSuites'] = $arguments->listSuites(); + } + if ($arguments->hasListTests()) { + $result['listTests'] = $arguments->listTests(); + } + if ($arguments->hasListTestsXml()) { + $result['listTestsXml'] = $arguments->listTestsXml(); + } + if ($arguments->hasPrinter()) { + $result['printer'] = $arguments->printer(); + } + if ($arguments->hasLoader()) { + $result['loader'] = $arguments->loader(); + } + if ($arguments->hasJunitLogfile()) { + $result['junitLogfile'] = $arguments->junitLogfile(); + } + if ($arguments->hasTeamcityLogfile()) { + $result['teamcityLogfile'] = $arguments->teamcityLogfile(); + } + if ($arguments->hasExecutionOrder()) { + $result['executionOrder'] = $arguments->executionOrder(); + } + if ($arguments->hasExecutionOrderDefects()) { + $result['executionOrderDefects'] = $arguments->executionOrderDefects(); + } + if ($arguments->hasExtensions()) { + $result['extensions'] = $arguments->extensions(); + } + if ($arguments->hasUnavailableExtensions()) { + $result['unavailableExtensions'] = $arguments->unavailableExtensions(); + } + if ($arguments->hasResolveDependencies()) { + $result['resolveDependencies'] = $arguments->resolveDependencies(); + } + if ($arguments->hasProcessIsolation()) { + $result['processIsolation'] = $arguments->processIsolation(); + } + if ($arguments->hasRepeat()) { + $result['repeat'] = $arguments->repeat(); + } + if ($arguments->hasStderr()) { + $result['stderr'] = $arguments->stderr(); + } + if ($arguments->hasStopOnDefect()) { + $result['stopOnDefect'] = $arguments->stopOnDefect(); + } + if ($arguments->hasStopOnError()) { + $result['stopOnError'] = $arguments->stopOnError(); + } + if ($arguments->hasStopOnFailure()) { + $result['stopOnFailure'] = $arguments->stopOnFailure(); + } + if ($arguments->hasStopOnWarning()) { + $result['stopOnWarning'] = $arguments->stopOnWarning(); + } + if ($arguments->hasStopOnIncomplete()) { + $result['stopOnIncomplete'] = $arguments->stopOnIncomplete(); + } + if ($arguments->hasStopOnRisky()) { + $result['stopOnRisky'] = $arguments->stopOnRisky(); + } + if ($arguments->hasStopOnSkipped()) { + $result['stopOnSkipped'] = $arguments->stopOnSkipped(); + } + if ($arguments->hasFailOnEmptyTestSuite()) { + $result['failOnEmptyTestSuite'] = $arguments->failOnEmptyTestSuite(); + } + if ($arguments->hasFailOnIncomplete()) { + $result['failOnIncomplete'] = $arguments->failOnIncomplete(); + } + if ($arguments->hasFailOnRisky()) { + $result['failOnRisky'] = $arguments->failOnRisky(); + } + if ($arguments->hasFailOnSkipped()) { + $result['failOnSkipped'] = $arguments->failOnSkipped(); + } + if ($arguments->hasFailOnWarning()) { + $result['failOnWarning'] = $arguments->failOnWarning(); + } + if ($arguments->hasTestdoxGroups()) { + $result['testdoxGroups'] = $arguments->testdoxGroups(); + } + if ($arguments->hasTestdoxExcludeGroups()) { + $result['testdoxExcludeGroups'] = $arguments->testdoxExcludeGroups(); + } + if ($arguments->hasTestdoxHtmlFile()) { + $result['testdoxHTMLFile'] = $arguments->testdoxHtmlFile(); + } + if ($arguments->hasTestdoxTextFile()) { + $result['testdoxTextFile'] = $arguments->testdoxTextFile(); + } + if ($arguments->hasTestdoxXmlFile()) { + $result['testdoxXMLFile'] = $arguments->testdoxXmlFile(); + } + if ($arguments->hasUseDefaultConfiguration()) { + $result['useDefaultConfiguration'] = $arguments->useDefaultConfiguration(); + } + if ($arguments->hasNoExtensions()) { + $result['noExtensions'] = $arguments->noExtensions(); + } + if ($arguments->hasNoCoverage()) { + $result['noCoverage'] = $arguments->noCoverage(); + } + if ($arguments->hasNoLogging()) { + $result['noLogging'] = $arguments->noLogging(); + } + if ($arguments->hasNoInteraction()) { + $result['noInteraction'] = $arguments->noInteraction(); + } + if ($arguments->hasBackupGlobals()) { + $result['backupGlobals'] = $arguments->backupGlobals(); + } + if ($arguments->hasBackupStaticAttributes()) { + $result['backupStaticAttributes'] = $arguments->backupStaticAttributes(); + } + if ($arguments->hasVerbose()) { + $result['verbose'] = $arguments->verbose(); + } + if ($arguments->hasReportUselessTests()) { + $result['reportUselessTests'] = $arguments->reportUselessTests(); + } + if ($arguments->hasStrictCoverage()) { + $result['strictCoverage'] = $arguments->strictCoverage(); + } + if ($arguments->hasDisableCodeCoverageIgnore()) { + $result['disableCodeCoverageIgnore'] = $arguments->disableCodeCoverageIgnore(); + } + if ($arguments->hasBeStrictAboutChangesToGlobalState()) { + $result['beStrictAboutChangesToGlobalState'] = $arguments->beStrictAboutChangesToGlobalState(); + } + if ($arguments->hasDisallowTestOutput()) { + $result['disallowTestOutput'] = $arguments->disallowTestOutput(); + } + if ($arguments->hasBeStrictAboutResourceUsageDuringSmallTests()) { + $result['beStrictAboutResourceUsageDuringSmallTests'] = $arguments->beStrictAboutResourceUsageDuringSmallTests(); + } + if ($arguments->hasDefaultTimeLimit()) { + $result['defaultTimeLimit'] = $arguments->defaultTimeLimit(); + } + if ($arguments->hasEnforceTimeLimit()) { + $result['enforceTimeLimit'] = $arguments->enforceTimeLimit(); + } + if ($arguments->hasDisallowTodoAnnotatedTests()) { + $result['disallowTodoAnnotatedTests'] = $arguments->disallowTodoAnnotatedTests(); + } + if ($arguments->hasReverseList()) { + $result['reverseList'] = $arguments->reverseList(); + } + if ($arguments->hasCoverageFilter()) { + $result['coverageFilter'] = $arguments->coverageFilter(); + } + if ($arguments->hasRandomOrderSeed()) { + $result['randomOrderSeed'] = $arguments->randomOrderSeed(); + } + if ($arguments->hasXdebugFilterFile()) { + $result['xdebugFilterFile'] = $arguments->xdebugFilterFile(); + } + return $result; } } */ - private $envVariables; + protected $arguments = []; /** - * @var VariableCollection + * @var array */ - private $postVariables; + protected $longOptions = []; /** - * @var VariableCollection + * @var bool */ - private $getVariables; + private $versionStringPrinted = \false; /** - * @var VariableCollection + * @psalm-var list */ - private $cookieVariables; + private $warnings = []; /** - * @var VariableCollection + * @throws Exception */ - private $serverVariables; + public static function main(bool $exit = \true) : int + { + try { + return (new static())->run($_SERVER['argv'], $exit); + } catch (Throwable $t) { + throw new \PHPUnit\TextUI\RuntimeException($t->getMessage(), (int) $t->getCode(), $t); + } + } /** - * @var VariableCollection + * @throws Exception */ - private $filesVariables; - /** - * @var VariableCollection - */ - private $requestVariables; - public function __construct(\PHPUnit\TextUI\XmlConfiguration\DirectoryCollection $includePaths, \PHPUnit\TextUI\XmlConfiguration\IniSettingCollection $iniSettings, \PHPUnit\TextUI\XmlConfiguration\ConstantCollection $constants, \PHPUnit\TextUI\XmlConfiguration\VariableCollection $globalVariables, \PHPUnit\TextUI\XmlConfiguration\VariableCollection $envVariables, \PHPUnit\TextUI\XmlConfiguration\VariableCollection $postVariables, \PHPUnit\TextUI\XmlConfiguration\VariableCollection $getVariables, \PHPUnit\TextUI\XmlConfiguration\VariableCollection $cookieVariables, \PHPUnit\TextUI\XmlConfiguration\VariableCollection $serverVariables, \PHPUnit\TextUI\XmlConfiguration\VariableCollection $filesVariables, \PHPUnit\TextUI\XmlConfiguration\VariableCollection $requestVariables) - { - $this->includePaths = $includePaths; - $this->iniSettings = $iniSettings; - $this->constants = $constants; - $this->globalVariables = $globalVariables; - $this->envVariables = $envVariables; - $this->postVariables = $postVariables; - $this->getVariables = $getVariables; - $this->cookieVariables = $cookieVariables; - $this->serverVariables = $serverVariables; - $this->filesVariables = $filesVariables; - $this->requestVariables = $requestVariables; - } - public function includePaths() : \PHPUnit\TextUI\XmlConfiguration\DirectoryCollection - { - return $this->includePaths; - } - public function iniSettings() : \PHPUnit\TextUI\XmlConfiguration\IniSettingCollection + public function run(array $argv, bool $exit = \true) : int { - return $this->iniSettings; + $this->handleArguments($argv); + $runner = $this->createRunner(); + if ($this->arguments['test'] instanceof TestSuite) { + $suite = $this->arguments['test']; + } else { + $suite = $runner->getTest($this->arguments['test'], $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->run($suite, $this->arguments, $this->warnings, $exit); + } catch (Throwable $t) { + print $t->getMessage() . PHP_EOL; + } + $return = \PHPUnit\TextUI\TestRunner::FAILURE_EXIT; + if (isset($result) && $result->wasSuccessful()) { + $return = \PHPUnit\TextUI\TestRunner::SUCCESS_EXIT; + } elseif (!isset($result) || $result->errorCount() > 0) { + $return = \PHPUnit\TextUI\TestRunner::EXCEPTION_EXIT; + } + if ($exit) { + exit($return); + } + return $return; } - public function constants() : \PHPUnit\TextUI\XmlConfiguration\ConstantCollection + /** + * Create a TestRunner, override in subclasses. + */ + protected function createRunner() : \PHPUnit\TextUI\TestRunner { - return $this->constants; + return new \PHPUnit\TextUI\TestRunner($this->arguments['loader']); } - public function globalVariables() : \PHPUnit\TextUI\XmlConfiguration\VariableCollection + /** + * Handles the command-line arguments. + * + * A child class of PHPUnit\TextUI\Command can hook into the argument + * parsing by adding the switch(es) to the $longOptions array and point to a + * callback method that handles the switch(es) in the child class like this + * + * + * longOptions['my-switch'] = 'myHandler'; + * // my-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); + * } + * + * } + * + * + * @throws Exception + */ + protected function handleArguments(array $argv) : void { - return $this->globalVariables; + try { + $arguments = (new Builder())->fromParameters($argv, array_keys($this->longOptions)); + } catch (ArgumentsException $e) { + $this->exitWithErrorMessage($e->getMessage()); + } + assert(isset($arguments) && $arguments instanceof Configuration); + if ($arguments->hasGenerateConfiguration() && $arguments->generateConfiguration()) { + $this->generateConfiguration(); + } + if ($arguments->hasAtLeastVersion()) { + if (version_compare(Version::id(), $arguments->atLeastVersion(), '>=')) { + exit(\PHPUnit\TextUI\TestRunner::SUCCESS_EXIT); + } + exit(\PHPUnit\TextUI\TestRunner::FAILURE_EXIT); + } + if ($arguments->hasVersion() && $arguments->version()) { + $this->printVersionString(); + exit(\PHPUnit\TextUI\TestRunner::SUCCESS_EXIT); + } + if ($arguments->hasCheckVersion() && $arguments->checkVersion()) { + $this->handleVersionCheck(); + } + if ($arguments->hasHelp()) { + $this->showHelp(); + exit(\PHPUnit\TextUI\TestRunner::SUCCESS_EXIT); + } + if ($arguments->hasUnrecognizedOrderBy()) { + $this->exitWithErrorMessage(sprintf('unrecognized --order-by option: %s', $arguments->unrecognizedOrderBy())); + } + if ($arguments->hasIniSettings()) { + foreach ($arguments->iniSettings() as $name => $value) { + ini_set($name, $value); + } + } + if ($arguments->hasIncludePath()) { + ini_set('include_path', $arguments->includePath() . PATH_SEPARATOR . ini_get('include_path')); + } + $this->arguments = (new Mapper())->mapToLegacyArray($arguments); + $this->handleCustomOptions($arguments->unrecognizedOptions()); + $this->handleCustomTestSuite(); + if (!isset($this->arguments['testSuffixes'])) { + $this->arguments['testSuffixes'] = ['Test.php', '.phpt']; + } + if (!isset($this->arguments['test']) && $arguments->hasArgument()) { + $this->arguments['test'] = realpath($arguments->argument()); + if ($this->arguments['test'] === \false) { + $this->exitWithErrorMessage(sprintf('Cannot open file "%s".', $arguments->argument())); + } + } + if ($this->arguments['loader'] !== null) { + $this->arguments['loader'] = $this->handleLoader($this->arguments['loader']); + } + if (isset($this->arguments['configuration'])) { + if (is_dir($this->arguments['configuration'])) { + $candidate = $this->configurationFileInDirectory($this->arguments['configuration']); + if ($candidate !== null) { + $this->arguments['configuration'] = $candidate; + } + } + } elseif ($this->arguments['useDefaultConfiguration']) { + $candidate = $this->configurationFileInDirectory(getcwd()); + if ($candidate !== null) { + $this->arguments['configuration'] = $candidate; + } + } + if ($arguments->hasMigrateConfiguration() && $arguments->migrateConfiguration()) { + if (!isset($this->arguments['configuration'])) { + print 'No configuration file found to migrate.' . PHP_EOL; + exit(\PHPUnit\TextUI\TestRunner::EXCEPTION_EXIT); + } + $this->migrateConfiguration(realpath($this->arguments['configuration'])); + } + if (isset($this->arguments['configuration'])) { + try { + $this->arguments['configurationObject'] = (new Loader())->load($this->arguments['configuration']); + } catch (Throwable $e) { + print $e->getMessage() . PHP_EOL; + exit(\PHPUnit\TextUI\TestRunner::FAILURE_EXIT); + } + $phpunitConfiguration = $this->arguments['configurationObject']->phpunit(); + (new PhpHandler())->handle($this->arguments['configurationObject']->php()); + if (isset($this->arguments['bootstrap'])) { + $this->handleBootstrap($this->arguments['bootstrap']); + } elseif ($phpunitConfiguration->hasBootstrap()) { + $this->handleBootstrap($phpunitConfiguration->bootstrap()); + } + if (!isset($this->arguments['stderr'])) { + $this->arguments['stderr'] = $phpunitConfiguration->stderr(); + } + if (!isset($this->arguments['noExtensions']) && $phpunitConfiguration->hasExtensionsDirectory() && extension_loaded('phar')) { + $result = (new PharLoader())->loadPharExtensionsInDirectory($phpunitConfiguration->extensionsDirectory()); + $this->arguments['loadedExtensions'] = $result['loadedExtensions']; + $this->arguments['notLoadedExtensions'] = $result['notLoadedExtensions']; + unset($result); + } + if (!isset($this->arguments['columns'])) { + $this->arguments['columns'] = $phpunitConfiguration->columns(); + } + if (!isset($this->arguments['printer']) && $phpunitConfiguration->hasPrinterClass()) { + $file = $phpunitConfiguration->hasPrinterFile() ? $phpunitConfiguration->printerFile() : ''; + $this->arguments['printer'] = $this->handlePrinter($phpunitConfiguration->printerClass(), $file); + } + if ($phpunitConfiguration->hasTestSuiteLoaderClass()) { + $file = $phpunitConfiguration->hasTestSuiteLoaderFile() ? $phpunitConfiguration->testSuiteLoaderFile() : ''; + $this->arguments['loader'] = $this->handleLoader($phpunitConfiguration->testSuiteLoaderClass(), $file); + } + if (!isset($this->arguments['testsuite']) && $phpunitConfiguration->hasDefaultTestSuite()) { + $this->arguments['testsuite'] = $phpunitConfiguration->defaultTestSuite(); + } + if (!isset($this->arguments['test'])) { + try { + $this->arguments['test'] = (new \PHPUnit\TextUI\TestSuiteMapper())->map($this->arguments['configurationObject']->testSuite(), $this->arguments['testsuite'] ?? ''); + } catch (\PHPUnit\TextUI\Exception $e) { + $this->printVersionString(); + print $e->getMessage() . PHP_EOL; + exit(\PHPUnit\TextUI\TestRunner::EXCEPTION_EXIT); + } + } + } 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['configurationObject'], $this->arguments['warmCoverageCache'])) { + $this->handleWarmCoverageCache($this->arguments['configurationObject']); + } + if (!isset($this->arguments['test'])) { + $this->showHelp(); + exit(\PHPUnit\TextUI\TestRunner::EXCEPTION_EXIT); + } } - public function envVariables() : \PHPUnit\TextUI\XmlConfiguration\VariableCollection + /** + * Handles the loading of the PHPUnit\Runner\TestSuiteLoader implementation. + * + * @deprecated see https://github.com/sebastianbergmann/phpunit/issues/4039 + */ + protected function handleLoader(string $loaderClass, string $loaderFile = '') : ?TestSuiteLoader { - return $this->envVariables; + $this->warnings[] = 'Using a custom test suite loader is deprecated'; + if (!class_exists($loaderClass, \false)) { + if ($loaderFile == '') { + $loaderFile = Filesystem::classNameToFilename($loaderClass); + } + $loaderFile = stream_resolve_include_path($loaderFile); + if ($loaderFile) { + /** + * @noinspection PhpIncludeInspection + * + * @psalm-suppress UnresolvableInclude + */ + require $loaderFile; + } + } + if (class_exists($loaderClass, \false)) { + try { + $class = new ReflectionClass($loaderClass); + // @codeCoverageIgnoreStart + } catch (\ReflectionException $e) { + throw new \PHPUnit\TextUI\ReflectionException($e->getMessage(), $e->getCode(), $e); + } + // @codeCoverageIgnoreEnd + 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; } - public function postVariables() : \PHPUnit\TextUI\XmlConfiguration\VariableCollection + /** + * Handles the loading of the PHPUnit\Util\Printer implementation. + * + * @return null|Printer|string + */ + protected function handlePrinter(string $printerClass, string $printerFile = '') { - return $this->postVariables; + if (!class_exists($printerClass, \false)) { + if ($printerFile === '') { + $printerFile = Filesystem::classNameToFilename($printerClass); + } + $printerFile = stream_resolve_include_path($printerFile); + if ($printerFile) { + /** + * @noinspection PhpIncludeInspection + * + * @psalm-suppress UnresolvableInclude + */ + 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); + // @codeCoverageIgnoreStart + } catch (\ReflectionException $e) { + throw new \PHPUnit\TextUI\ReflectionException($e->getMessage(), $e->getCode(), $e); + // @codeCoverageIgnoreEnd + } + if (!$class->implementsInterface(\PHPUnit\TextUI\ResultPrinter::class)) { + $this->exitWithErrorMessage(sprintf('Could not use "%s" as printer: class does not implement %s', $printerClass, \PHPUnit\TextUI\ResultPrinter::class)); + } + if (!$class->isInstantiable()) { + $this->exitWithErrorMessage(sprintf('Could not use "%s" as printer: class cannot be instantiated', $printerClass)); + } + if ($class->isSubclassOf(\PHPUnit\TextUI\ResultPrinter::class)) { + return $printerClass; + } + $outputStream = isset($this->arguments['stderr']) ? 'php://stderr' : null; + return $class->newInstance($outputStream); } - public function getVariables() : \PHPUnit\TextUI\XmlConfiguration\VariableCollection + /** + * Loads a bootstrap file. + */ + protected function handleBootstrap(string $filename) : void { - return $this->getVariables; + try { + FileLoader::checkAndLoad($filename); + } catch (Throwable $t) { + if ($t instanceof \PHPUnit\Exception) { + $this->exitWithErrorMessage($t->getMessage()); + } + $message = sprintf('Error in bootstrap script: %s:%s%s%s%s', get_class($t), PHP_EOL, $t->getMessage(), PHP_EOL, $t->getTraceAsString()); + while ($t = $t->getPrevious()) { + $message .= sprintf('%s%sPrevious error: %s:%s%s%s%s', PHP_EOL, PHP_EOL, get_class($t), PHP_EOL, $t->getMessage(), PHP_EOL, $t->getTraceAsString()); + } + $this->exitWithErrorMessage($message); + } } - public function cookieVariables() : \PHPUnit\TextUI\XmlConfiguration\VariableCollection + protected function handleVersionCheck() : void { - return $this->cookieVariables; + $this->printVersionString(); + $latestVersion = file_get_contents('https://phar.phpunit.de/latest-version-of/phpunit'); + $latestCompatibleVersion = file_get_contents('https://phar.phpunit.de/latest-version-of/phpunit-' . explode('.', Version::series())[0]); + $notLatest = version_compare($latestVersion, Version::id(), '>'); + $notLatestCompatible = version_compare($latestCompatibleVersion, Version::id(), '>'); + if ($notLatest || $notLatestCompatible) { + print 'You are not using the latest version of PHPUnit.' . PHP_EOL; + } else { + print 'You are using the latest version of PHPUnit.' . PHP_EOL; + } + if ($notLatestCompatible) { + printf('The latest version compatible with PHPUnit %s is PHPUnit %s.' . PHP_EOL, Version::id(), $latestCompatibleVersion); + } + if ($notLatest) { + printf('The latest version is PHPUnit %s.' . PHP_EOL, $latestVersion); + } + exit(\PHPUnit\TextUI\TestRunner::SUCCESS_EXIT); } - public function serverVariables() : \PHPUnit\TextUI\XmlConfiguration\VariableCollection + /** + * Show the help message. + */ + protected function showHelp() : void { - return $this->serverVariables; + $this->printVersionString(); + (new \PHPUnit\TextUI\Help())->writeToConsole(); } - public function filesVariables() : \PHPUnit\TextUI\XmlConfiguration\VariableCollection + /** + * Custom callback for test suite discovery. + */ + protected function handleCustomTestSuite() : void { - return $this->filesVariables; } - public function requestVariables() : \PHPUnit\TextUI\XmlConfiguration\VariableCollection + private function printVersionString() : void { - return $this->requestVariables; + if ($this->versionStringPrinted) { + return; + } + print Version::getVersionString() . PHP_EOL . PHP_EOL; + $this->versionStringPrinted = \true; } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TextUI\XmlConfiguration; - -use const PATH_SEPARATOR; -use function constant; -use function define; -use function defined; -use function getenv; -use function implode; -use function ini_get; -use function ini_set; -use function putenv; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class PhpHandler -{ - public function handle(\PHPUnit\TextUI\XmlConfiguration\Php $configuration) : void + private function exitWithErrorMessage(string $message) : void { - $this->handleIncludePaths($configuration->includePaths()); - $this->handleIniSettings($configuration->iniSettings()); - $this->handleConstants($configuration->constants()); - $this->handleGlobalVariables($configuration->globalVariables()); - $this->handleServerVariables($configuration->serverVariables()); - $this->handleEnvVariables($configuration->envVariables()); - $this->handleVariables('_POST', $configuration->postVariables()); - $this->handleVariables('_GET', $configuration->getVariables()); - $this->handleVariables('_COOKIE', $configuration->cookieVariables()); - $this->handleVariables('_FILES', $configuration->filesVariables()); - $this->handleVariables('_REQUEST', $configuration->requestVariables()); + $this->printVersionString(); + print $message . PHP_EOL; + exit(\PHPUnit\TextUI\TestRunner::FAILURE_EXIT); } - private function handleIncludePaths(\PHPUnit\TextUI\XmlConfiguration\DirectoryCollection $includePaths) : void + private function handleListGroups(TestSuite $suite, bool $exit) : int { - if (!$includePaths->isEmpty()) { - $includePathsAsStrings = []; - foreach ($includePaths as $includePath) { - $includePathsAsStrings[] = $includePath->path(); + $this->printVersionString(); + $this->warnAboutConflictingOptions('listGroups', ['filter', 'groups', 'excludeGroups', 'testsuite']); + print 'Available test group(s):' . PHP_EOL; + $groups = $suite->getGroups(); + sort($groups); + foreach ($groups as $group) { + if (strpos($group, '__phpunit_') === 0) { + continue; } - ini_set('include_path', implode(PATH_SEPARATOR, $includePathsAsStrings) . PATH_SEPARATOR . ini_get('include_path')); + printf(' - %s' . PHP_EOL, $group); } - } - private function handleIniSettings(\PHPUnit\TextUI\XmlConfiguration\IniSettingCollection $iniSettings) : void - { - foreach ($iniSettings as $iniSetting) { - $value = $iniSetting->value(); - if (defined($value)) { - $value = (string) constant($value); - } - ini_set($iniSetting->name(), $value); + if ($exit) { + exit(\PHPUnit\TextUI\TestRunner::SUCCESS_EXIT); } + return \PHPUnit\TextUI\TestRunner::SUCCESS_EXIT; } - private function handleConstants(\PHPUnit\TextUI\XmlConfiguration\ConstantCollection $constants) : void + /** + * @throws \PHPUnit\Framework\Exception + * @throws XmlConfiguration\Exception + */ + private function handleListSuites(bool $exit) : int { - foreach ($constants as $constant) { - if (!defined($constant->name())) { - define($constant->name(), $constant->value()); - } + $this->printVersionString(); + $this->warnAboutConflictingOptions('listSuites', ['filter', 'groups', 'excludeGroups', 'testsuite']); + print 'Available test suite(s):' . PHP_EOL; + foreach ($this->arguments['configurationObject']->testSuite() as $testSuite) { + printf(' - %s' . PHP_EOL, $testSuite->name()); } - } - private function handleGlobalVariables(\PHPUnit\TextUI\XmlConfiguration\VariableCollection $variables) : void - { - foreach ($variables as $variable) { - $GLOBALS[$variable->name()] = $variable->value(); + if ($exit) { + exit(\PHPUnit\TextUI\TestRunner::SUCCESS_EXIT); } + return \PHPUnit\TextUI\TestRunner::SUCCESS_EXIT; } - private function handleServerVariables(\PHPUnit\TextUI\XmlConfiguration\VariableCollection $variables) : void + /** + * @throws InvalidArgumentException + */ + private function handleListTests(TestSuite $suite, bool $exit) : int { - foreach ($variables as $variable) { - $_SERVER[$variable->name()] = $variable->value(); + $this->printVersionString(); + $this->warnAboutConflictingOptions('listTests', ['filter', 'groups', 'excludeGroups']); + $renderer = new TextTestListRenderer(); + print $renderer->render($suite); + if ($exit) { + exit(\PHPUnit\TextUI\TestRunner::SUCCESS_EXIT); } + return \PHPUnit\TextUI\TestRunner::SUCCESS_EXIT; } - private function handleVariables(string $target, \PHPUnit\TextUI\XmlConfiguration\VariableCollection $variables) : void + /** + * @throws InvalidArgumentException + */ + private function handleListTestsXml(TestSuite $suite, string $target, bool $exit) : int { - foreach ($variables as $variable) { - $GLOBALS[$target][$variable->name()] = $variable->value(); + $this->printVersionString(); + $this->warnAboutConflictingOptions('listTestsXml', ['filter', 'groups', 'excludeGroups']); + $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(\PHPUnit\TextUI\TestRunner::SUCCESS_EXIT); } + return \PHPUnit\TextUI\TestRunner::SUCCESS_EXIT; } - private function handleEnvVariables(\PHPUnit\TextUI\XmlConfiguration\VariableCollection $variables) : void + private function generateConfiguration() : void { - foreach ($variables as $variable) { - $name = $variable->name(); - $value = $variable->value(); - $force = $variable->force(); - if ($force || getenv($name) === \false) { - putenv("{$name}={$value}"); - } - $value = getenv($name); - if ($force || !isset($_ENV[$name])) { - $_ENV[$name] = $value; - } + $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)); + print 'Cache directory (relative to path shown above; default: .phpunit.cache): '; + $cacheDirectory = trim(fgets(STDIN)); + if ($bootstrapScript === '') { + $bootstrapScript = 'vendor/autoload.php'; } + if ($testsDirectory === '') { + $testsDirectory = 'tests'; + } + if ($src === '') { + $src = 'src'; + } + if ($cacheDirectory === '') { + $cacheDirectory = '.phpunit.cache'; + } + $generator = new Generator(); + file_put_contents('phpunit.xml', $generator->generateDefaultConfiguration(Version::series(), $bootstrapScript, $testsDirectory, $src, $cacheDirectory)); + print PHP_EOL . 'Generated phpunit.xml in ' . getcwd() . '.' . PHP_EOL; + print 'Make sure to exclude the ' . $cacheDirectory . ' directory from version control.' . PHP_EOL; + exit(\PHPUnit\TextUI\TestRunner::SUCCESS_EXIT); } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TextUI\XmlConfiguration; - -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - * - * @psalm-immutable - */ -final class Variable -{ - /** - * @var string - */ - private $name; - /** - * @var mixed - */ - private $value; - /** - * @var bool - */ - private $force; - public function __construct(string $name, $value, bool $force) + private function migrateConfiguration(string $filename) : void { - $this->name = $name; - $this->value = $value; - $this->force = $force; + $this->printVersionString(); + $result = (new SchemaDetector())->detect($filename); + if (!$result->detected()) { + print $filename . ' does not validate against any known schema.' . PHP_EOL; + exit(\PHPUnit\TextUI\TestRunner::EXCEPTION_EXIT); + } + /** @psalm-suppress MissingThrowsDocblock */ + if ($result->version() === Version::series()) { + print $filename . ' does not need to be migrated.' . PHP_EOL; + exit(\PHPUnit\TextUI\TestRunner::EXCEPTION_EXIT); + } + copy($filename, $filename . '.bak'); + print 'Created backup: ' . $filename . '.bak' . PHP_EOL; + try { + file_put_contents($filename, (new Migrator())->migrate($filename)); + print 'Migrated configuration: ' . $filename . PHP_EOL; + } catch (Throwable $t) { + print 'Migration failed: ' . $t->getMessage() . PHP_EOL; + exit(\PHPUnit\TextUI\TestRunner::EXCEPTION_EXIT); + } + exit(\PHPUnit\TextUI\TestRunner::SUCCESS_EXIT); } - public function name() : string + private function handleCustomOptions(array $unrecognizedOptions) : void { - return $this->name; + foreach ($unrecognizedOptions as $name => $value) { + if (isset($this->longOptions[$name])) { + $handler = $this->longOptions[$name]; + } + $name .= '='; + if (isset($this->longOptions[$name])) { + $handler = $this->longOptions[$name]; + } + if (isset($handler) && is_callable([$this, $handler])) { + $this->{$handler}($value); + unset($handler); + } + } } - public function value() + private function handleWarmCoverageCache(\PHPUnit\TextUI\XmlConfiguration\Configuration $configuration) : void { - return $this->value; + $this->printVersionString(); + if (isset($this->arguments['coverageCacheDirectory'])) { + $cacheDirectory = $this->arguments['coverageCacheDirectory']; + } elseif ($configuration->codeCoverage()->hasCacheDirectory()) { + $cacheDirectory = $configuration->codeCoverage()->cacheDirectory()->path(); + } else { + print 'Cache for static analysis has not been configured' . PHP_EOL; + exit(\PHPUnit\TextUI\TestRunner::EXCEPTION_EXIT); + } + $filter = new Filter(); + if ($configuration->codeCoverage()->hasNonEmptyListOfFilesToBeIncludedInCodeCoverageReport()) { + (new FilterMapper())->map($filter, $configuration->codeCoverage()); + } elseif (isset($this->arguments['coverageFilter'])) { + if (!is_array($this->arguments['coverageFilter'])) { + $coverageFilterDirectories = [$this->arguments['coverageFilter']]; + } else { + $coverageFilterDirectories = $this->arguments['coverageFilter']; + } + foreach ($coverageFilterDirectories as $coverageFilterDirectory) { + $filter->includeDirectory($coverageFilterDirectory); + } + } else { + print 'Filter for code coverage has not been configured' . PHP_EOL; + exit(\PHPUnit\TextUI\TestRunner::EXCEPTION_EXIT); + } + $timer = new Timer(); + $timer->start(); + print 'Warming cache for static analysis ... '; + (new CacheWarmer())->warmCache($cacheDirectory, !$configuration->codeCoverage()->disableCodeCoverageIgnore(), $configuration->codeCoverage()->ignoreDeprecatedCodeUnits(), $filter); + print 'done [' . $timer->stop()->asString() . ']' . PHP_EOL; + exit(\PHPUnit\TextUI\TestRunner::SUCCESS_EXIT); } - public function force() : bool + private function configurationFileInDirectory(string $directory) : ?string { - return $this->force; + $candidates = [$directory . '/phpunit.xml', $directory . '/phpunit.xml.dist']; + foreach ($candidates as $candidate) { + if (is_file($candidate)) { + return realpath($candidate); + } + } + return null; } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TextUI\XmlConfiguration; - -use function count; -use Countable; -use IteratorAggregate; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - * - * @psalm-immutable - * - * @template-implements IteratorAggregate - */ -final class VariableCollection implements Countable, IteratorAggregate -{ - /** - * @var Variable[] - */ - private $variables; /** - * @param Variable[] $variables + * @psalm-param "listGroups"|"listSuites"|"listTests"|"listTestsXml"|"filter"|"groups"|"excludeGroups"|"testsuite" $key + * @psalm-param list<"listGroups"|"listSuites"|"listTests"|"listTestsXml"|"filter"|"groups"|"excludeGroups"|"testsuite"> $keys */ - public static function fromArray(array $variables) : self - { - return new self(...$variables); - } - private function __construct(\PHPUnit\TextUI\XmlConfiguration\Variable ...$variables) + private function warnAboutConflictingOptions(string $key, array $keys) : void { - $this->variables = $variables; + $warningPrinted = \false; + foreach ($keys as $_key) { + if (!empty($this->arguments[$_key])) { + printf('The %s and %s options cannot be combined, %s is ignored' . PHP_EOL, $this->mapKeyToOptionForWarning($_key), $this->mapKeyToOptionForWarning($key), $this->mapKeyToOptionForWarning($_key)); + $warningPrinted = \true; + } + } + if ($warningPrinted) { + print PHP_EOL; + } } /** - * @return Variable[] + * @psalm-param "listGroups"|"listSuites"|"listTests"|"listTestsXml"|"filter"|"groups"|"excludeGroups"|"testsuite" $key */ - public function asArray() : array - { - return $this->variables; - } - public function count() : int - { - return count($this->variables); - } - public function getIterator() : \PHPUnit\TextUI\XmlConfiguration\VariableCollectionIterator + private function mapKeyToOptionForWarning(string $key) : string { - return new \PHPUnit\TextUI\XmlConfiguration\VariableCollectionIterator($this); + switch ($key) { + case 'listGroups': + return '--list-groups'; + case 'listSuites': + return '--list-suites'; + case 'listTests': + return '--list-tests'; + case 'listTestsXml': + return '--list-tests-xml'; + case 'filter': + return '--filter'; + case 'groups': + return '--group'; + case 'excludeGroups': + return '--exclude-group'; + case 'testsuite': + return '--testsuite'; + } } } */ -final class VariableCollectionIterator implements Countable, Iterator +class DefaultResultPrinter extends Printer implements \PHPUnit\TextUI\ResultPrinter { + 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 Variable[] + * @var int */ - private $variables; + protected $column = 0; /** * @var int */ - private $position; - public function __construct(\PHPUnit\TextUI\XmlConfiguration\VariableCollection $variables) - { - $this->variables = $variables->asArray(); - } - public function count() : int - { - return iterator_count($this); - } - public function rewind() : void - { - $this->position = 0; - } - public function valid() : bool - { - return $this->position < count($this->variables); - } - public function key() : int - { - return $this->position; - } - public function current() : \PHPUnit\TextUI\XmlConfiguration\Variable - { - return $this->variables[$this->position]; - } - public function next() : void - { - $this->position++; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TextUI\XmlConfiguration; - -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - * - * @psalm-immutable - */ -final class Extension -{ + protected $maxColumn; /** - * @var string - * - * @psalm-var class-string + * @var bool */ - private $className; + protected $lastTestFailed = \false; /** - * @var string + * @var int */ - private $sourceFile; + protected $numAssertions = 0; /** - * @var array + * @var int */ - private $arguments; + protected $numTests = -1; /** - * @psalm-param class-string $className + * @var int */ - public function __construct(string $className, string $sourceFile, array $arguments) - { - $this->className = $className; - $this->sourceFile = $sourceFile; - $this->arguments = $arguments; - } + protected $numTestsRun = 0; /** - * @psalm-return class-string + * @var int */ - public function className() : string - { - return $this->className; - } - public function hasSourceFile() : bool + 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; + /** + * @var Timer + */ + private $timer; + /** + * 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) { - return $this->sourceFile !== ''; + 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; + } + $this->timer = new Timer(); + $this->timer->start(); } - public function sourceFile() : string + public function printResult(TestResult $result) : void { - return $this->sourceFile; + $this->printHeader($result); + $this->printErrors($result); + $this->printWarnings($result); + $this->printFailures($result); + $this->printRisky($result); + if ($this->verbose) { + $this->printIncompletes($result); + $this->printSkipped($result); + } + $this->printFooter($result); } - public function hasArguments() : bool + /** + * An error occurred. + */ + public function addError(Test $test, Throwable $t, float $time) : void { - return !empty($this->arguments); + $this->writeProgressWithColor('fg-red, bold', 'E'); + $this->lastTestFailed = \true; } - public function arguments() : array + /** + * A failure occurred. + */ + public function addFailure(Test $test, AssertionFailedError $e, float $time) : void { - return $this->arguments; + $this->writeProgressWithColor('bg-red, fg-white', 'F'); + $this->lastTestFailed = \true; } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TextUI\XmlConfiguration; - -use IteratorAggregate; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - * - * @psalm-immutable - * - * @template-implements IteratorAggregate - */ -final class ExtensionCollection implements IteratorAggregate -{ /** - * @var Extension[] + * A warning occurred. */ - private $extensions; + public function addWarning(Test $test, Warning $e, float $time) : void + { + $this->writeProgressWithColor('fg-yellow, bold', 'W'); + $this->lastTestFailed = \true; + } /** - * @param Extension[] $extensions + * Incomplete test. */ - public static function fromArray(array $extensions) : self + public function addIncompleteTest(Test $test, Throwable $t, float $time) : void { - return new self(...$extensions); + $this->writeProgressWithColor('fg-yellow, bold', 'I'); + $this->lastTestFailed = \true; } - private function __construct(\PHPUnit\TextUI\XmlConfiguration\Extension ...$extensions) + /** + * Risky test. + */ + public function addRiskyTest(Test $test, Throwable $t, float $time) : void { - $this->extensions = $extensions; + $this->writeProgressWithColor('fg-yellow, bold', 'R'); + $this->lastTestFailed = \true; } /** - * @return Extension[] + * Skipped test. */ - public function asArray() : array + public function addSkippedTest(Test $test, Throwable $t, float $time) : void { - return $this->extensions; + $this->writeProgressWithColor('fg-cyan, bold', 'S'); + $this->lastTestFailed = \true; } - public function getIterator() : \PHPUnit\TextUI\XmlConfiguration\ExtensionCollectionIterator + /** + * A testsuite started. + */ + public function startTestSuite(TestSuite $suite) : void { - return new \PHPUnit\TextUI\XmlConfiguration\ExtensionCollectionIterator($this); + 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" . trim((string) $e) . "\n"); + } + } + 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'); + } + protected function printHeader(TestResult $result) : void + { + if (count($result) > 0) { + $this->write(PHP_EOL . PHP_EOL . (new ResourceUsageFormatter())->resourceUsage($this->timer->stop()) . PHP_EOL . PHP_EOL); + } + } + protected function printFooter(TestResult $result) : void + { + if (count($result) === 0) { + $this->writeWithColor('fg-black, bg-yellow', 'No tests executed!'); + return; + } + if ($result->wasSuccessfulAndNoTestIsRiskyOrSkippedOrIncomplete()) { + $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')); + return; + } + $color = 'fg-black, bg-yellow'; + if ($result->wasSuccessful()) { + 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; + } } } + * (c) Sebastian Bergmann + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. */ -final class ExtensionCollectionIterator implements Countable, Iterator +namespace PHPUnit\TextUI; + +use RuntimeException; +/** + * @internal This interface is not covered by the backward compatibility promise for PHPUnit + */ +final class ReflectionException extends RuntimeException implements \PHPUnit\TextUI\Exception { - /** - * @var Extension[] - */ - private $extensions; - /** - * @var int - */ - private $position; - public function __construct(\PHPUnit\TextUI\XmlConfiguration\ExtensionCollection $extensions) - { - $this->extensions = $extensions->asArray(); - } - public function count() : int - { - return iterator_count($this); - } - public function rewind() : void - { - $this->position = 0; - } - public function valid() : bool - { - return $this->position < count($this->extensions); - } - public function key() : int - { - return $this->position; - } - public function current() : \PHPUnit\TextUI\XmlConfiguration\Extension +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI; + +/** + * @internal This interface is not covered by the backward compatibility promise for PHPUnit + */ +final class RuntimeException extends \RuntimeException implements \PHPUnit\TextUI\Exception +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI; + +use function sprintf; +use RuntimeException; +/** + * @internal This interface is not covered by the backward compatibility promise for PHPUnit + */ +final class TestDirectoryNotFoundException extends RuntimeException implements \PHPUnit\TextUI\Exception +{ + public function __construct(string $path) { - return $this->extensions[$this->position]; + parent::__construct(sprintf('Test directory "%s" not found', $path)); } - public function next() : void +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI; + +use function sprintf; +use RuntimeException; +/** + * @internal This interface is not covered by the backward compatibility promise for PHPUnit + */ +final class TestFileNotFoundException extends RuntimeException implements \PHPUnit\TextUI\Exception +{ + public function __construct(string $path) { - $this->position++; + parent::__construct(sprintf('Test file "%s" not found', $path)); } } getNumberOfColumns(); + } + if ($withColor === null) { + $this->hasColor = (new Console())->hasColorSupport(); + } else { + $this->hasColor = $withColor; + } + foreach ($this->elements() as $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; + } /** - * @var bool + * Write the help file to the CLI, adapting width and colors to the console. */ - private $stopOnDefect; + public function writeToConsole() : void + { + if ($this->hasColor) { + $this->writeWithColor(); + } else { + $this->writePlaintext(); + } + } + private function writePlaintext() : void + { + foreach ($this->elements() 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 ($this->elements() 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; + } + } /** - * @var bool + * @psalm-return array> */ - private $stopOnError; - /** - * @var bool - */ - private $stopOnFailure; - /** - * @var bool - */ - private $stopOnWarning; - /** - * @var bool - */ - private $stopOnIncomplete; - /** - * @var bool - */ - private $stopOnRisky; - /** - * @var bool - */ - private $stopOnSkipped; - /** - * @var ?string - */ - private $extensionsDirectory; - /** - * @var ?string - * - * @deprecated see https://github.com/sebastianbergmann/phpunit/issues/4039 - */ - private $testSuiteLoaderClass; - /** - * @var ?string - * - * @deprecated see https://github.com/sebastianbergmann/phpunit/issues/4039 - */ - private $testSuiteLoaderFile; - /** - * @var ?string - */ - private $printerClass; - /** - * @var ?string - */ - private $printerFile; - /** - * @var bool - */ - private $beStrictAboutChangesToGlobalState; - /** - * @var bool - */ - private $beStrictAboutOutputDuringTests; - /** - * @var bool - */ - private $beStrictAboutResourceUsageDuringSmallTests; - /** - * @var bool - */ - private $beStrictAboutTestsThatDoNotTestAnything; - /** - * @var bool - */ - private $beStrictAboutTodoAnnotatedTests; - /** - * @var bool - */ - private $beStrictAboutCoversAnnotation; - /** - * @var bool - */ - private $enforceTimeLimit; - /** - * @var int - */ - private $defaultTimeLimit; - /** - * @var int - */ - private $timeoutForSmallTests; - /** - * @var int - */ - private $timeoutForMediumTests; - /** - * @var int - */ - private $timeoutForLargeTests; - /** - * @var ?string - */ - private $defaultTestSuite; - /** - * @var int - */ - private $executionOrder; - /** - * @var bool - */ - private $resolveDependencies; - /** - * @var bool - */ - private $defectsFirst; + private function elements() : array + { + $elements = ['Usage' => [['text' => 'phpunit [options] UnitTest.php'], ['text' => 'phpunit [options] ']], 'Code Coverage Options' => [['arg' => '--coverage-clover ', 'desc' => 'Generate code coverage report in Clover XML format'], ['arg' => '--coverage-cobertura ', 'desc' => 'Generate code coverage report in Cobertura XML format'], ['arg' => '--coverage-crap4j ', 'desc' => 'Generate code coverage report in Crap4J XML format'], ['arg' => '--coverage-html ', 'desc' => 'Generate code coverage report in HTML format'], ['arg' => '--coverage-php ', 'desc' => 'Export PHP_CodeCoverage object to file'], ['arg' => '--coverage-text=', 'desc' => 'Generate code coverage report in text format [default: standard output]'], ['arg' => '--coverage-xml ', 'desc' => 'Generate code coverage report in PHPUnit XML format'], ['arg' => '--coverage-cache ', 'desc' => 'Cache static analysis results'], ['arg' => '--warm-coverage-cache', 'desc' => 'Warm static analysis cache'], ['arg' => '--coverage-filter ', 'desc' => 'Include in code coverage analysis'], ['arg' => '--path-coverage', 'desc' => 'Perform path coverage analysis'], ['arg' => '--disable-coverage-ignore', 'desc' => 'Disable annotations for ignoring code coverage'], ['arg' => '--no-coverage', 'desc' => 'Ignore code coverage configuration']], 'Logging Options' => [['arg' => '--log-junit ', 'desc' => 'Log test execution in JUnit XML format to file'], ['arg' => '--log-teamcity ', 'desc' => 'Log test execution in TeamCity format to file'], ['arg' => '--testdox-html ', 'desc' => 'Write agile documentation in HTML format to file'], ['arg' => '--testdox-text ', 'desc' => 'Write agile documentation in Text format to file'], ['arg' => '--testdox-xml ', 'desc' => 'Write agile documentation in XML format to file'], ['arg' => '--reverse-list', 'desc' => 'Print defects in reverse order'], ['arg' => '--no-logging', 'desc' => 'Ignore logging configuration']], 'Test Selection Options' => [['arg' => '--list-suites', 'desc' => 'List available test suites'], ['arg' => '--testsuite ', 'desc' => 'Filter which testsuite to run'], ['arg' => '--list-groups', 'desc' => 'List available test groups'], ['arg' => '--group ', 'desc' => 'Only runs tests from the specified group(s)'], ['arg' => '--exclude-group ', 'desc' => 'Exclude tests from the specified group(s)'], ['arg' => '--covers ', 'desc' => 'Only runs tests annotated with "@covers "'], ['arg' => '--uses ', 'desc' => 'Only runs tests annotated with "@uses "'], ['arg' => '--list-tests', 'desc' => 'List available tests'], ['arg' => '--list-tests-xml ', 'desc' => 'List available tests in XML format'], ['arg' => '--filter ', 'desc' => 'Filter which tests to run'], ['arg' => '--test-suffix ', '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 ', '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 ', 'desc' => 'Use colors in output ("never", "auto" or "always")'], ['arg' => '--columns ', '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-incomplete', 'desc' => 'Treat incomplete tests as failures'], ['arg' => '--fail-on-risky', 'desc' => 'Treat risky tests as failures'], ['arg' => '--fail-on-skipped', 'desc' => 'Treat skipped tests as failures'], ['arg' => '--fail-on-warning', 'desc' => 'Treat tests with warnings as failures'], ['arg' => '-v|--verbose', 'desc' => 'Output more verbose information'], ['arg' => '--debug', 'desc' => 'Display debugging information'], ['spacer' => ''], ['arg' => '--repeat ', '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 ', 'desc' => 'TestListener implementation to use'], ['spacer' => ''], ['arg' => '--order-by ', 'desc' => 'Run tests in order: default|defects|duration|no-depends|random|reverse|size'], ['arg' => '--random-order-seed ', 'desc' => 'Use a specific random seed 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 ', 'desc' => 'A PHP script that is included as early as possible'], ['arg' => '--bootstrap ', 'desc' => 'A PHP script that is included before the tests run'], ['arg' => '-c|--configuration ', 'desc' => 'Read configuration from XML file'], ['arg' => '--no-configuration', 'desc' => 'Ignore default configuration file (phpunit.xml)'], ['arg' => '--extensions ', 'desc' => 'A comma separated list of PHPUnit extensions to load'], ['arg' => '--no-extensions', 'desc' => 'Do not load PHPUnit extensions'], ['arg' => '--include-path ', 'desc' => 'Prepend PHP\'s include_path with given path(s)'], ['arg' => '-d ', 'desc' => 'Sets a php.ini value'], ['arg' => '--cache-result-file ', 'desc' => 'Specify result cache path and filename'], ['arg' => '--generate-configuration', 'desc' => 'Generate configuration file with suggested settings'], ['arg' => '--migrate-configuration', 'desc' => 'Migrate configuration file to current format']]]; + if (defined('__PHPUNIT_PHAR__')) { + $elements['PHAR Options'] = [['arg' => '--manifest', 'desc' => 'Print Software Bill of Materials (SBOM) in plain-text format'], ['arg' => '--sbom', 'desc' => 'Print Software Bill of Materials (SBOM) in CycloneDX XML format'], ['arg' => '--composer-lock', 'desc' => 'Print composer.lock file used to build the PHAR']]; + } + $elements['Miscellaneous Options'] = [['arg' => '-h|--help', 'desc' => 'Prints this usage information'], ['arg' => '--version', 'desc' => 'Prints the version and exits'], ['arg' => '--atleast-version ', 'desc' => 'Checks that version is greater than min and exits'], ['arg' => '--check-version', 'desc' => 'Checks whether PHPUnit is the latest version and exits']]; + return $elements; + } +} + + * + * 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\TestListener; +use PHPUnit\Framework\TestResult; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +interface ResultPrinter extends TestListener +{ + public function printResult(TestResult $result) : void; + public function write(string $buffer) : void; +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI; + +use const PHP_EOL; +use const PHP_SAPI; +use const PHP_VERSION; +use function array_diff; +use function array_map; +use function array_merge; +use function assert; +use function class_exists; +use function count; +use function dirname; +use function file_put_contents; +use function htmlspecialchars; +use function is_array; +use function is_int; +use function is_string; +use function mt_srand; +use function range; +use function realpath; +use function sort; +use function sprintf; +use function time; +use PHPUnit\Framework\Exception; +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\Extension\ExtensionHandler; +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\TextUI\XmlConfiguration\CodeCoverage\FilterMapper; +use PHPUnit\TextUI\XmlConfiguration\Configuration; +use PHPUnit\TextUI\XmlConfiguration\Loader; +use PHPUnit\TextUI\XmlConfiguration\PhpHandler; +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 PHPUnit\Util\Xml\SchemaDetector; +use ReflectionClass; +use ReflectionException; +use PHPUnitPHAR\SebastianBergmann\CodeCoverage\CodeCoverage; +use PHPUnitPHAR\SebastianBergmann\CodeCoverage\Driver\Selector; +use PHPUnitPHAR\SebastianBergmann\CodeCoverage\Exception as CodeCoverageException; +use PHPUnitPHAR\SebastianBergmann\CodeCoverage\Filter as CodeCoverageFilter; +use PHPUnitPHAR\SebastianBergmann\CodeCoverage\Report\Clover as CloverReport; +use PHPUnitPHAR\SebastianBergmann\CodeCoverage\Report\Cobertura as CoberturaReport; +use PHPUnitPHAR\SebastianBergmann\CodeCoverage\Report\Crap4j as Crap4jReport; +use PHPUnitPHAR\SebastianBergmann\CodeCoverage\Report\Html\Facade as HtmlReport; +use PHPUnitPHAR\SebastianBergmann\CodeCoverage\Report\PHP as PhpReport; +use PHPUnitPHAR\SebastianBergmann\CodeCoverage\Report\Text as TextReport; +use PHPUnitPHAR\SebastianBergmann\CodeCoverage\Report\Xml\Facade as XmlReport; +use PHPUnitPHAR\SebastianBergmann\Comparator\Comparator; +use PHPUnitPHAR\SebastianBergmann\Environment\Runtime; +use PHPUnitPHAR\SebastianBergmann\Invoker\Invoker; +use PHPUnitPHAR\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 + * @var CodeCoverageFilter */ - private $backupGlobals; + private $codeCoverageFilter; /** - * @var bool + * @var TestSuiteLoader */ - private $backupStaticAttributes; + private $loader; /** - * @var bool + * @var ResultPrinter */ - private $registerMockObjectsFromTestArgumentsRecursively; + private $printer; /** * @var bool */ - private $conflictBetweenPrinterClassAndTestdox; - public function __construct(bool $cacheResult, ?string $cacheResultFile, $columns, string $colors, bool $stderr, bool $noInteraction, bool $verbose, bool $reverseDefectList, bool $convertDeprecationsToExceptions, bool $convertErrorsToExceptions, bool $convertNoticesToExceptions, bool $convertWarningsToExceptions, bool $forceCoversAnnotation, ?string $bootstrap, bool $processIsolation, bool $failOnEmptyTestSuite, bool $failOnIncomplete, bool $failOnRisky, bool $failOnSkipped, bool $failOnWarning, bool $stopOnDefect, bool $stopOnError, bool $stopOnFailure, bool $stopOnWarning, bool $stopOnIncomplete, bool $stopOnRisky, bool $stopOnSkipped, ?string $extensionsDirectory, ?string $testSuiteLoaderClass, ?string $testSuiteLoaderFile, ?string $printerClass, ?string $printerFile, bool $beStrictAboutChangesToGlobalState, bool $beStrictAboutOutputDuringTests, bool $beStrictAboutResourceUsageDuringSmallTests, bool $beStrictAboutTestsThatDoNotTestAnything, bool $beStrictAboutTodoAnnotatedTests, bool $beStrictAboutCoversAnnotation, bool $enforceTimeLimit, int $defaultTimeLimit, int $timeoutForSmallTests, int $timeoutForMediumTests, int $timeoutForLargeTests, ?string $defaultTestSuite, int $executionOrder, bool $resolveDependencies, bool $defectsFirst, bool $backupGlobals, bool $backupStaticAttributes, bool $registerMockObjectsFromTestArgumentsRecursively, bool $conflictBetweenPrinterClassAndTestdox) - { - $this->cacheResult = $cacheResult; - $this->cacheResultFile = $cacheResultFile; - $this->columns = $columns; - $this->colors = $colors; - $this->stderr = $stderr; - $this->noInteraction = $noInteraction; - $this->verbose = $verbose; - $this->reverseDefectList = $reverseDefectList; - $this->convertDeprecationsToExceptions = $convertDeprecationsToExceptions; - $this->convertErrorsToExceptions = $convertErrorsToExceptions; - $this->convertNoticesToExceptions = $convertNoticesToExceptions; - $this->convertWarningsToExceptions = $convertWarningsToExceptions; - $this->forceCoversAnnotation = $forceCoversAnnotation; - $this->bootstrap = $bootstrap; - $this->processIsolation = $processIsolation; - $this->failOnEmptyTestSuite = $failOnEmptyTestSuite; - $this->failOnIncomplete = $failOnIncomplete; - $this->failOnRisky = $failOnRisky; - $this->failOnSkipped = $failOnSkipped; - $this->failOnWarning = $failOnWarning; - $this->stopOnDefect = $stopOnDefect; - $this->stopOnError = $stopOnError; - $this->stopOnFailure = $stopOnFailure; - $this->stopOnWarning = $stopOnWarning; - $this->stopOnIncomplete = $stopOnIncomplete; - $this->stopOnRisky = $stopOnRisky; - $this->stopOnSkipped = $stopOnSkipped; - $this->extensionsDirectory = $extensionsDirectory; - $this->testSuiteLoaderClass = $testSuiteLoaderClass; - $this->testSuiteLoaderFile = $testSuiteLoaderFile; - $this->printerClass = $printerClass; - $this->printerFile = $printerFile; - $this->beStrictAboutChangesToGlobalState = $beStrictAboutChangesToGlobalState; - $this->beStrictAboutOutputDuringTests = $beStrictAboutOutputDuringTests; - $this->beStrictAboutResourceUsageDuringSmallTests = $beStrictAboutResourceUsageDuringSmallTests; - $this->beStrictAboutTestsThatDoNotTestAnything = $beStrictAboutTestsThatDoNotTestAnything; - $this->beStrictAboutTodoAnnotatedTests = $beStrictAboutTodoAnnotatedTests; - $this->beStrictAboutCoversAnnotation = $beStrictAboutCoversAnnotation; - $this->enforceTimeLimit = $enforceTimeLimit; - $this->defaultTimeLimit = $defaultTimeLimit; - $this->timeoutForSmallTests = $timeoutForSmallTests; - $this->timeoutForMediumTests = $timeoutForMediumTests; - $this->timeoutForLargeTests = $timeoutForLargeTests; - $this->defaultTestSuite = $defaultTestSuite; - $this->executionOrder = $executionOrder; - $this->resolveDependencies = $resolveDependencies; - $this->defectsFirst = $defectsFirst; - $this->backupGlobals = $backupGlobals; - $this->backupStaticAttributes = $backupStaticAttributes; - $this->registerMockObjectsFromTestArgumentsRecursively = $registerMockObjectsFromTestArgumentsRecursively; - $this->conflictBetweenPrinterClassAndTestdox = $conflictBetweenPrinterClassAndTestdox; - } - public function cacheResult() : bool - { - return $this->cacheResult; - } + private $messagePrinted = \false; /** - * @psalm-assert-if-true !null $this->cacheResultFile + * @var Hook[] */ - public function hasCacheResultFile() : bool - { - return $this->cacheResultFile !== null; - } + private $extensions = []; /** - * @throws Exception + * @var Timer */ - public function cacheResultFile() : string + private $timer; + public function __construct(?TestSuiteLoader $loader = null, ?CodeCoverageFilter $filter = null) { - if (!$this->hasCacheResultFile()) { - throw new \PHPUnit\TextUI\XmlConfiguration\Exception('Cache result file is not configured'); + if ($filter === null) { + $filter = new CodeCoverageFilter(); } - return (string) $this->cacheResultFile; - } - public function columns() - { - return $this->columns; - } - public function colors() : string - { - return $this->colors; - } - public function stderr() : bool - { - return $this->stderr; - } - public function noInteraction() : bool - { - return $this->noInteraction; - } - public function verbose() : bool - { - return $this->verbose; - } - public function reverseDefectList() : bool - { - return $this->reverseDefectList; - } - public function convertDeprecationsToExceptions() : bool - { - return $this->convertDeprecationsToExceptions; - } - public function convertErrorsToExceptions() : bool - { - return $this->convertErrorsToExceptions; - } - public function convertNoticesToExceptions() : bool - { - return $this->convertNoticesToExceptions; - } - public function convertWarningsToExceptions() : bool - { - return $this->convertWarningsToExceptions; - } - public function forceCoversAnnotation() : bool - { - return $this->forceCoversAnnotation; - } - /** - * @psalm-assert-if-true !null $this->bootstrap - */ - public function hasBootstrap() : bool - { - return $this->bootstrap !== null; + $this->codeCoverageFilter = $filter; + $this->loader = $loader; + $this->timer = new Timer(); } /** + * @throws \PHPUnit\Runner\Exception * @throws Exception + * @throws XmlConfiguration\Exception */ - public function bootstrap() : string + public function run(TestSuite $suite, array $arguments = [], array $warnings = [], bool $exit = \true) : TestResult { - if (!$this->hasBootstrap()) { - throw new \PHPUnit\TextUI\XmlConfiguration\Exception('Bootstrap script is not configured'); + if (isset($arguments['configuration'])) { + $GLOBALS['__PHPUNIT_CONFIGURATION_FILE'] = $arguments['configuration']; } - return (string) $this->bootstrap; - } - public function processIsolation() : bool - { - return $this->processIsolation; - } - public function failOnEmptyTestSuite() : bool - { - return $this->failOnEmptyTestSuite; - } - public function failOnIncomplete() : bool - { - return $this->failOnIncomplete; - } - public function failOnRisky() : bool - { - return $this->failOnRisky; - } - public function failOnSkipped() : bool - { - return $this->failOnSkipped; - } - public function failOnWarning() : bool - { - return $this->failOnWarning; - } - public function stopOnDefect() : bool - { - return $this->stopOnDefect; - } - public function stopOnError() : bool - { - return $this->stopOnError; - } - public function stopOnFailure() : bool - { - return $this->stopOnFailure; - } - public function stopOnWarning() : bool - { - return $this->stopOnWarning; - } - public function stopOnIncomplete() : bool - { - return $this->stopOnIncomplete; - } - public function stopOnRisky() : bool - { - return $this->stopOnRisky; - } - public function stopOnSkipped() : bool - { - return $this->stopOnSkipped; - } - /** - * @psalm-assert-if-true !null $this->extensionsDirectory - */ - public function hasExtensionsDirectory() : bool - { - return $this->extensionsDirectory !== null; - } - /** - * @throws Exception - */ - public function extensionsDirectory() : string - { - if (!$this->hasExtensionsDirectory()) { - throw new \PHPUnit\TextUI\XmlConfiguration\Exception('Extensions directory is not configured'); + $this->handleConfiguration($arguments); + $warnings = array_merge($warnings, $arguments['warnings']); + if (is_int($arguments['columns']) && $arguments['columns'] < 16) { + $arguments['columns'] = 16; + $tooFewColumnsRequested = \true; } - return (string) $this->extensionsDirectory; - } - /** - * @psalm-assert-if-true !null $this->testSuiteLoaderClass - * - * @deprecated see https://github.com/sebastianbergmann/phpunit/issues/4039 - */ - public function hasTestSuiteLoaderClass() : bool - { - return $this->testSuiteLoaderClass !== null; - } - /** - * @throws Exception - * - * @deprecated see https://github.com/sebastianbergmann/phpunit/issues/4039 - */ - public function testSuiteLoaderClass() : string - { - if (!$this->hasTestSuiteLoaderClass()) { - throw new \PHPUnit\TextUI\XmlConfiguration\Exception('TestSuiteLoader class is not configured'); + if (isset($arguments['bootstrap'])) { + $GLOBALS['__PHPUNIT_BOOTSTRAP'] = $arguments['bootstrap']; } - return (string) $this->testSuiteLoaderClass; - } - /** - * @psalm-assert-if-true !null $this->testSuiteLoaderFile - * - * @deprecated see https://github.com/sebastianbergmann/phpunit/issues/4039 - */ - public function hasTestSuiteLoaderFile() : bool - { - return $this->testSuiteLoaderFile !== null; - } - /** - * @throws Exception - * - * @deprecated see https://github.com/sebastianbergmann/phpunit/issues/4039 - */ - public function testSuiteLoaderFile() : string - { - if (!$this->hasTestSuiteLoaderFile()) { - throw new \PHPUnit\TextUI\XmlConfiguration\Exception('TestSuiteLoader sourcecode file is not configured'); + if ($arguments['backupGlobals'] === \true) { + $suite->setBackupGlobals(\true); } - return (string) $this->testSuiteLoaderFile; - } - /** - * @psalm-assert-if-true !null $this->printerClass - */ - public function hasPrinterClass() : bool - { - return $this->printerClass !== null; - } - /** - * @throws Exception - */ - public function printerClass() : string - { - if (!$this->hasPrinterClass()) { - throw new \PHPUnit\TextUI\XmlConfiguration\Exception('ResultPrinter class is not configured'); + if ($arguments['backupStaticAttributes'] === \true) { + $suite->setBackupStaticAttributes(\true); } - return (string) $this->printerClass; - } - /** - * @psalm-assert-if-true !null $this->printerFile - */ - public function hasPrinterFile() : bool - { - return $this->printerFile !== null; - } - /** - * @throws Exception - */ - public function printerFile() : string - { - if (!$this->hasPrinterFile()) { - throw new \PHPUnit\TextUI\XmlConfiguration\Exception('ResultPrinter sourcecode file is not configured'); + if ($arguments['beStrictAboutChangesToGlobalState'] === \true) { + $suite->setBeStrictAboutChangesToGlobalState(\true); } - return (string) $this->printerFile; - } - public function beStrictAboutChangesToGlobalState() : bool - { - return $this->beStrictAboutChangesToGlobalState; - } - public function beStrictAboutOutputDuringTests() : bool - { - return $this->beStrictAboutOutputDuringTests; - } - public function beStrictAboutResourceUsageDuringSmallTests() : bool - { - return $this->beStrictAboutResourceUsageDuringSmallTests; - } - public function beStrictAboutTestsThatDoNotTestAnything() : bool - { - return $this->beStrictAboutTestsThatDoNotTestAnything; - } - public function beStrictAboutTodoAnnotatedTests() : bool - { - return $this->beStrictAboutTodoAnnotatedTests; - } - public function beStrictAboutCoversAnnotation() : bool - { - return $this->beStrictAboutCoversAnnotation; - } - public function enforceTimeLimit() : bool - { - return $this->enforceTimeLimit; + if ($arguments['executionOrder'] === TestSuiteSorter::ORDER_RANDOMIZED) { + mt_srand($arguments['randomOrderSeed']); + } + if ($arguments['cacheResult']) { + if (!isset($arguments['cacheResultFile'])) { + if (isset($arguments['configurationObject'])) { + assert($arguments['configurationObject'] instanceof Configuration); + $cacheLocation = $arguments['configurationObject']->filename(); + } 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(\true); + } + 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 \PHPUnit\TextUI\ResultPrinter) { + $this->printer = $arguments['printer']; + } elseif (is_string($arguments['printer']) && class_exists($arguments['printer'], \false)) { + try { + $reflector = new ReflectionClass($arguments['printer']); + if ($reflector->implementsInterface(\PHPUnit\TextUI\ResultPrinter::class)) { + $this->printer = $this->createPrinter($arguments['printer'], $arguments); + } + // @codeCoverageIgnoreStart + } catch (ReflectionException $e) { + throw new Exception($e->getMessage(), $e->getCode(), $e); + } + // @codeCoverageIgnoreEnd + } + } else { + $this->printer = $this->createPrinter(\PHPUnit\TextUI\DefaultResultPrinter::class, $arguments); + } + } + if (isset($originalExecutionOrder) && $this->printer instanceof CliTestDoxPrinter) { + assert($this->printer instanceof CliTestDoxPrinter); + $this->printer->setOriginalExecutionOrder($originalExecutionOrder); + $this->printer->setShowProgressAnimation(!$arguments['noInteraction']); + } + $this->write(Version::getVersionString() . "\n"); + foreach ($arguments['listeners'] as $listener) { + $result->addListener($listener); + } + $result->addListener($this->printer); + $coverageFilterFromConfigurationFile = \false; + $coverageFilterFromOption = \false; + $codeCoverageReports = 0; + 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['coverageCobertura'])) { + $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 ($codeCoverageReports > 0 || isset($arguments['xdebugFilterFile'])) { + if (isset($arguments['coverageFilter'])) { + if (!is_array($arguments['coverageFilter'])) { + $coverageFilterDirectories = [$arguments['coverageFilter']]; + } else { + $coverageFilterDirectories = $arguments['coverageFilter']; + } + foreach ($coverageFilterDirectories as $coverageFilterDirectory) { + $this->codeCoverageFilter->includeDirectory($coverageFilterDirectory); + } + $coverageFilterFromOption = \true; + } + if (isset($arguments['configurationObject'])) { + assert($arguments['configurationObject'] instanceof Configuration); + $codeCoverageConfiguration = $arguments['configurationObject']->codeCoverage(); + if ($codeCoverageConfiguration->hasNonEmptyListOfFilesToBeIncludedInCodeCoverageReport()) { + $coverageFilterFromConfigurationFile = \true; + (new FilterMapper())->map($this->codeCoverageFilter, $codeCoverageConfiguration); + } + } + } + if ($codeCoverageReports > 0) { + try { + if (isset($codeCoverageConfiguration) && ($codeCoverageConfiguration->pathCoverage() || isset($arguments['pathCoverage']) && $arguments['pathCoverage'] === \true)) { + $codeCoverageDriver = (new Selector())->forLineAndPathCoverage($this->codeCoverageFilter); + } else { + $codeCoverageDriver = (new Selector())->forLineCoverage($this->codeCoverageFilter); + } + $codeCoverage = new CodeCoverage($codeCoverageDriver, $this->codeCoverageFilter); + if (isset($codeCoverageConfiguration) && $codeCoverageConfiguration->hasCacheDirectory()) { + $codeCoverage->cacheStaticAnalysis($codeCoverageConfiguration->cacheDirectory()->path()); + } + if (isset($arguments['coverageCacheDirectory'])) { + $codeCoverage->cacheStaticAnalysis($arguments['coverageCacheDirectory']); + } + $codeCoverage->excludeSubclassesOfThisClassFromUnintentionallyCoveredCodeCheck(Comparator::class); + if ($arguments['strictCoverage']) { + $codeCoverage->enableCheckForUnintentionallyCoveredCode(); + } + if (isset($arguments['ignoreDeprecatedCodeUnitsFromCodeCoverage'])) { + if ($arguments['ignoreDeprecatedCodeUnitsFromCodeCoverage']) { + $codeCoverage->ignoreDeprecatedCode(); + } else { + $codeCoverage->doNotIgnoreDeprecatedCode(); + } + } + if (isset($arguments['disableCodeCoverageIgnore'])) { + if ($arguments['disableCodeCoverageIgnore']) { + $codeCoverage->disableAnnotationsForIgnoringCode(); + } else { + $codeCoverage->enableAnnotationsForIgnoringCode(); + } + } + if (isset($arguments['configurationObject'])) { + $codeCoverageConfiguration = $arguments['configurationObject']->codeCoverage(); + if ($codeCoverageConfiguration->hasNonEmptyListOfFilesToBeIncludedInCodeCoverageReport()) { + if ($codeCoverageConfiguration->includeUncoveredFiles()) { + $codeCoverage->includeUncoveredFiles(); + } else { + $codeCoverage->excludeUncoveredFiles(); + } + if ($codeCoverageConfiguration->processUncoveredFiles()) { + $codeCoverage->processUncoveredFiles(); + } else { + $codeCoverage->doNotProcessUncoveredFiles(); + } + } + } + if ($this->codeCoverageFilter->isEmpty()) { + if (!$coverageFilterFromConfigurationFile && !$coverageFilterFromOption) { + $warnings[] = 'No filter is configured, code coverage will not be processed'; + } else { + $warnings[] = 'Incorrect filter configuration, code coverage will not be processed'; + } + unset($codeCoverage); + } + } catch (CodeCoverageException $e) { + $warnings[] = $e->getMessage(); + } + } + if ($arguments['verbose']) { + if (PHP_SAPI === 'phpdbg') { + $this->writeMessage('Runtime', 'PHPDBG ' . PHP_VERSION); + } else { + $runtime = 'PHP ' . PHP_VERSION; + if (isset($codeCoverageDriver)) { + $runtime .= ' with ' . $codeCoverageDriver->nameAndVersion(); + } + $this->writeMessage('Runtime', $runtime); + } + if (isset($arguments['configurationObject'])) { + assert($arguments['configurationObject'] instanceof Configuration); + $this->writeMessage('Configuration', $arguments['configurationObject']->filename()); + } + 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)) { + $warnings[] = 'Less than 16 columns requested, number of columns set to 16'; + } + if ((new Runtime())->discardsComments()) { + $warnings[] = 'opcache.save_comments=0 set; annotations will not work'; + } + if (isset($arguments['conflictBetweenPrinterClassAndTestdox'])) { + $warnings[] = 'Directives printerClass and testdox are mutually exclusive'; + } + $warnings = array_merge($warnings, $suite->warnings()); + sort($warnings); + foreach ($warnings as $warning) { + $this->writeMessage('Warning', $warning); + } + if (isset($arguments['configurationObject'])) { + assert($arguments['configurationObject'] instanceof Configuration); + if ($arguments['configurationObject']->hasValidationErrors()) { + if ((new SchemaDetector())->detect($arguments['configurationObject']->filename())->detected()) { + $this->writeMessage('Warning', 'Your XML configuration validates against a deprecated schema.'); + $this->writeMessage('Suggestion', 'Migrate your XML configuration using "--migrate-configuration"!'); + } else { + $this->write("\n Warning - The configuration file did not pass validation!\n The following problems have been detected:\n"); + $this->write($arguments['configurationObject']->validationErrors()); + $this->write("\n Test results may not be as expected.\n\n"); + } + } + } + if (isset($arguments['xdebugFilterFile'], $codeCoverageConfiguration)) { + $this->write(PHP_EOL . 'Please note that --dump-xdebug-filter and --prepend are deprecated and will be removed in PHPUnit 10.' . PHP_EOL); + $script = (new XdebugFilterScriptGenerator())->generate($codeCoverageConfiguration); + 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 . PHP_EOL, $arguments['xdebugFilterFile'])); + exit(self::SUCCESS_EXIT); + } + $this->write("\n"); + if (isset($codeCoverage)) { + $result->setCodeCoverage($codeCoverage); + } + $result->beStrictAboutTestsThatDoNotTestAnything($arguments['reportUselessTests']); + $result->beStrictAboutOutputDuringTests($arguments['disallowTestOutput']); + $result->beStrictAboutTodoAnnotatedTests($arguments['disallowTodoAnnotatedTests']); + $result->beStrictAboutResourceUsageDuringSmallTests($arguments['beStrictAboutResourceUsageDuringSmallTests']); + if ($arguments['enforceTimeLimit'] === \true && !(new Invoker())->canInvokeWithTimeout()) { + $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 (isset($arguments['forceCoversAnnotation']) && $arguments['forceCoversAnnotation'] === \true) { + $result->forceCoversAnnotation(); + } + $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(); + $this->printer->printResult($result); + if (isset($codeCoverage)) { + 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['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['coverageCobertura'])) { + $this->codeCoverageGenerationStart('Cobertura XML'); + try { + $writer = new CoberturaReport(); + $writer->process($codeCoverage, $arguments['coverageCobertura']); + $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 PHPUnit %s', Version::id())); + $writer->process($codeCoverage, $arguments['coverageHtml']); + $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'] !== \PHPUnit\TextUI\DefaultResultPrinter::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 (isset($arguments['failOnEmptyTestSuite']) && $arguments['failOnEmptyTestSuite'] === \true && count($result) === 0) { + exit(self::FAILURE_EXIT); + } + if ($result->wasSuccessfulIgnoringWarnings()) { + if ($arguments['failOnRisky'] && !$result->allHarmless()) { + exit(self::FAILURE_EXIT); + } + if ($arguments['failOnWarning'] && $result->warningCount() > 0) { + exit(self::FAILURE_EXIT); + } + if ($arguments['failOnIncomplete'] && $result->notImplementedCount() > 0) { + exit(self::FAILURE_EXIT); + } + if ($arguments['failOnSkipped'] && $result->skippedCount() > 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 defaultTimeLimit() : int + /** + * Returns the loader to be used. + */ + public function getLoader() : TestSuiteLoader { - return $this->defaultTimeLimit; + if ($this->loader === null) { + $this->loader = new StandardTestSuiteLoader(); + } + return $this->loader; } - public function timeoutForSmallTests() : int + public function addExtension(Hook $extension) : void { - return $this->timeoutForSmallTests; + $this->extensions[] = $extension; } - public function timeoutForMediumTests() : int + /** + * Override to define how to handle a failed loading of + * a test suite. + */ + protected function runFailed(string $message) : void { - return $this->timeoutForMediumTests; + $this->write($message . PHP_EOL); + exit(self::FAILURE_EXIT); } - public function timeoutForLargeTests() : int + private function createTestResult() : TestResult { - return $this->timeoutForLargeTests; + return new TestResult(); } - /** - * @psalm-assert-if-true !null $this->defaultTestSuite - */ - public function hasDefaultTestSuite() : bool + private function write(string $buffer) : void { - return $this->defaultTestSuite !== null; + if (PHP_SAPI !== 'cli' && PHP_SAPI !== 'phpdbg') { + $buffer = htmlspecialchars($buffer); + } + if ($this->printer !== null) { + $this->printer->write($buffer); + } else { + print $buffer; + } } /** * @throws Exception + * @throws XmlConfiguration\Exception */ - public function defaultTestSuite() : string + private function handleConfiguration(array &$arguments) : void { - if (!$this->hasDefaultTestSuite()) { - throw new \PHPUnit\TextUI\XmlConfiguration\Exception('Default test suite is not configured'); - } - return (string) $this->defaultTestSuite; - } - public function executionOrder() : int - { - return $this->executionOrder; + if (!isset($arguments['configurationObject']) && isset($arguments['configuration'])) { + $arguments['configurationObject'] = (new Loader())->load($arguments['configuration']); + } + if (!isset($arguments['warnings'])) { + $arguments['warnings'] = []; + } + $arguments['debug'] = $arguments['debug'] ?? \false; + $arguments['filter'] = $arguments['filter'] ?? \false; + $arguments['listeners'] = $arguments['listeners'] ?? []; + if (isset($arguments['configurationObject'])) { + (new PhpHandler())->handle($arguments['configurationObject']->php()); + $codeCoverageConfiguration = $arguments['configurationObject']->codeCoverage(); + if (!isset($arguments['noCoverage'])) { + if (!isset($arguments['coverageClover']) && $codeCoverageConfiguration->hasClover()) { + $arguments['coverageClover'] = $codeCoverageConfiguration->clover()->target()->path(); + } + if (!isset($arguments['coverageCobertura']) && $codeCoverageConfiguration->hasCobertura()) { + $arguments['coverageCobertura'] = $codeCoverageConfiguration->cobertura()->target()->path(); + } + if (!isset($arguments['coverageCrap4J']) && $codeCoverageConfiguration->hasCrap4j()) { + $arguments['coverageCrap4J'] = $codeCoverageConfiguration->crap4j()->target()->path(); + if (!isset($arguments['crap4jThreshold'])) { + $arguments['crap4jThreshold'] = $codeCoverageConfiguration->crap4j()->threshold(); + } + } + if (!isset($arguments['coverageHtml']) && $codeCoverageConfiguration->hasHtml()) { + $arguments['coverageHtml'] = $codeCoverageConfiguration->html()->target()->path(); + if (!isset($arguments['reportLowUpperBound'])) { + $arguments['reportLowUpperBound'] = $codeCoverageConfiguration->html()->lowUpperBound(); + } + if (!isset($arguments['reportHighLowerBound'])) { + $arguments['reportHighLowerBound'] = $codeCoverageConfiguration->html()->highLowerBound(); + } + } + if (!isset($arguments['coveragePHP']) && $codeCoverageConfiguration->hasPhp()) { + $arguments['coveragePHP'] = $codeCoverageConfiguration->php()->target()->path(); + } + if (!isset($arguments['coverageText']) && $codeCoverageConfiguration->hasText()) { + $arguments['coverageText'] = $codeCoverageConfiguration->text()->target()->path(); + $arguments['coverageTextShowUncoveredFiles'] = $codeCoverageConfiguration->text()->showUncoveredFiles(); + $arguments['coverageTextShowOnlySummary'] = $codeCoverageConfiguration->text()->showOnlySummary(); + } + if (!isset($arguments['coverageXml']) && $codeCoverageConfiguration->hasXml()) { + $arguments['coverageXml'] = $codeCoverageConfiguration->xml()->target()->path(); + } + } + $phpunitConfiguration = $arguments['configurationObject']->phpunit(); + $arguments['backupGlobals'] = $arguments['backupGlobals'] ?? $phpunitConfiguration->backupGlobals(); + $arguments['backupStaticAttributes'] = $arguments['backupStaticAttributes'] ?? $phpunitConfiguration->backupStaticAttributes(); + $arguments['beStrictAboutChangesToGlobalState'] = $arguments['beStrictAboutChangesToGlobalState'] ?? $phpunitConfiguration->beStrictAboutChangesToGlobalState(); + $arguments['cacheResult'] = $arguments['cacheResult'] ?? $phpunitConfiguration->cacheResult(); + $arguments['colors'] = $arguments['colors'] ?? $phpunitConfiguration->colors(); + $arguments['convertDeprecationsToExceptions'] = $arguments['convertDeprecationsToExceptions'] ?? $phpunitConfiguration->convertDeprecationsToExceptions(); + $arguments['convertErrorsToExceptions'] = $arguments['convertErrorsToExceptions'] ?? $phpunitConfiguration->convertErrorsToExceptions(); + $arguments['convertNoticesToExceptions'] = $arguments['convertNoticesToExceptions'] ?? $phpunitConfiguration->convertNoticesToExceptions(); + $arguments['convertWarningsToExceptions'] = $arguments['convertWarningsToExceptions'] ?? $phpunitConfiguration->convertWarningsToExceptions(); + $arguments['processIsolation'] = $arguments['processIsolation'] ?? $phpunitConfiguration->processIsolation(); + $arguments['stopOnDefect'] = $arguments['stopOnDefect'] ?? $phpunitConfiguration->stopOnDefect(); + $arguments['stopOnError'] = $arguments['stopOnError'] ?? $phpunitConfiguration->stopOnError(); + $arguments['stopOnFailure'] = $arguments['stopOnFailure'] ?? $phpunitConfiguration->stopOnFailure(); + $arguments['stopOnWarning'] = $arguments['stopOnWarning'] ?? $phpunitConfiguration->stopOnWarning(); + $arguments['stopOnIncomplete'] = $arguments['stopOnIncomplete'] ?? $phpunitConfiguration->stopOnIncomplete(); + $arguments['stopOnRisky'] = $arguments['stopOnRisky'] ?? $phpunitConfiguration->stopOnRisky(); + $arguments['stopOnSkipped'] = $arguments['stopOnSkipped'] ?? $phpunitConfiguration->stopOnSkipped(); + $arguments['failOnEmptyTestSuite'] = $arguments['failOnEmptyTestSuite'] ?? $phpunitConfiguration->failOnEmptyTestSuite(); + $arguments['failOnIncomplete'] = $arguments['failOnIncomplete'] ?? $phpunitConfiguration->failOnIncomplete(); + $arguments['failOnRisky'] = $arguments['failOnRisky'] ?? $phpunitConfiguration->failOnRisky(); + $arguments['failOnSkipped'] = $arguments['failOnSkipped'] ?? $phpunitConfiguration->failOnSkipped(); + $arguments['failOnWarning'] = $arguments['failOnWarning'] ?? $phpunitConfiguration->failOnWarning(); + $arguments['enforceTimeLimit'] = $arguments['enforceTimeLimit'] ?? $phpunitConfiguration->enforceTimeLimit(); + $arguments['defaultTimeLimit'] = $arguments['defaultTimeLimit'] ?? $phpunitConfiguration->defaultTimeLimit(); + $arguments['timeoutForSmallTests'] = $arguments['timeoutForSmallTests'] ?? $phpunitConfiguration->timeoutForSmallTests(); + $arguments['timeoutForMediumTests'] = $arguments['timeoutForMediumTests'] ?? $phpunitConfiguration->timeoutForMediumTests(); + $arguments['timeoutForLargeTests'] = $arguments['timeoutForLargeTests'] ?? $phpunitConfiguration->timeoutForLargeTests(); + $arguments['reportUselessTests'] = $arguments['reportUselessTests'] ?? $phpunitConfiguration->beStrictAboutTestsThatDoNotTestAnything(); + $arguments['strictCoverage'] = $arguments['strictCoverage'] ?? $phpunitConfiguration->beStrictAboutCoversAnnotation(); + $arguments['ignoreDeprecatedCodeUnitsFromCodeCoverage'] = $arguments['ignoreDeprecatedCodeUnitsFromCodeCoverage'] ?? $codeCoverageConfiguration->ignoreDeprecatedCodeUnits(); + $arguments['disallowTestOutput'] = $arguments['disallowTestOutput'] ?? $phpunitConfiguration->beStrictAboutOutputDuringTests(); + $arguments['disallowTodoAnnotatedTests'] = $arguments['disallowTodoAnnotatedTests'] ?? $phpunitConfiguration->beStrictAboutTodoAnnotatedTests(); + $arguments['beStrictAboutResourceUsageDuringSmallTests'] = $arguments['beStrictAboutResourceUsageDuringSmallTests'] ?? $phpunitConfiguration->beStrictAboutResourceUsageDuringSmallTests(); + $arguments['verbose'] = $arguments['verbose'] ?? $phpunitConfiguration->verbose(); + $arguments['reverseDefectList'] = $arguments['reverseDefectList'] ?? $phpunitConfiguration->reverseDefectList(); + $arguments['forceCoversAnnotation'] = $arguments['forceCoversAnnotation'] ?? $phpunitConfiguration->forceCoversAnnotation(); + $arguments['disableCodeCoverageIgnore'] = $arguments['disableCodeCoverageIgnore'] ?? $codeCoverageConfiguration->disableCodeCoverageIgnore(); + $arguments['registerMockObjectsFromTestArgumentsRecursively'] = $arguments['registerMockObjectsFromTestArgumentsRecursively'] ?? $phpunitConfiguration->registerMockObjectsFromTestArgumentsRecursively(); + $arguments['noInteraction'] = $arguments['noInteraction'] ?? $phpunitConfiguration->noInteraction(); + $arguments['executionOrder'] = $arguments['executionOrder'] ?? $phpunitConfiguration->executionOrder(); + $arguments['resolveDependencies'] = $arguments['resolveDependencies'] ?? $phpunitConfiguration->resolveDependencies(); + if (!isset($arguments['bootstrap']) && $phpunitConfiguration->hasBootstrap()) { + $arguments['bootstrap'] = $phpunitConfiguration->bootstrap(); + } + if (!isset($arguments['cacheResultFile']) && $phpunitConfiguration->hasCacheResultFile()) { + $arguments['cacheResultFile'] = $phpunitConfiguration->cacheResultFile(); + } + if (!isset($arguments['executionOrderDefects'])) { + $arguments['executionOrderDefects'] = $phpunitConfiguration->defectsFirst() ? TestSuiteSorter::ORDER_DEFECTS_FIRST : TestSuiteSorter::ORDER_DEFAULT; + } + if ($phpunitConfiguration->conflictBetweenPrinterClassAndTestdox()) { + $arguments['conflictBetweenPrinterClassAndTestdox'] = \true; + } + $groupCliArgs = []; + if (!empty($arguments['groups'])) { + $groupCliArgs = $arguments['groups']; + } + $groupConfiguration = $arguments['configurationObject']->groups(); + if (!isset($arguments['groups']) && $groupConfiguration->hasInclude()) { + $arguments['groups'] = $groupConfiguration->include()->asArrayOfStrings(); + } + if (!isset($arguments['excludeGroups']) && $groupConfiguration->hasExclude()) { + $arguments['excludeGroups'] = array_diff($groupConfiguration->exclude()->asArrayOfStrings(), $groupCliArgs); + } + if (!isset($arguments['noExtensions'])) { + $extensionHandler = new ExtensionHandler(); + foreach ($arguments['configurationObject']->extensions() as $extension) { + $extensionHandler->registerExtension($extension, $this); + } + foreach ($arguments['configurationObject']->listeners() as $listener) { + $arguments['listeners'][] = $extensionHandler->createTestListenerInstance($listener); + } + unset($extensionHandler); + } + foreach ($arguments['unavailableExtensions'] as $extension) { + $arguments['warnings'][] = sprintf('Extension "%s" is not available', $extension); + } + $loggingConfiguration = $arguments['configurationObject']->logging(); + if (!isset($arguments['noLogging'])) { + if ($loggingConfiguration->hasText()) { + $arguments['listeners'][] = new \PHPUnit\TextUI\DefaultResultPrinter($loggingConfiguration->text()->target()->path(), \true); + } + if (!isset($arguments['teamcityLogfile']) && $loggingConfiguration->hasTeamCity()) { + $arguments['teamcityLogfile'] = $loggingConfiguration->teamCity()->target()->path(); + } + if (!isset($arguments['junitLogfile']) && $loggingConfiguration->hasJunit()) { + $arguments['junitLogfile'] = $loggingConfiguration->junit()->target()->path(); + } + if (!isset($arguments['testdoxHTMLFile']) && $loggingConfiguration->hasTestDoxHtml()) { + $arguments['testdoxHTMLFile'] = $loggingConfiguration->testDoxHtml()->target()->path(); + } + if (!isset($arguments['testdoxTextFile']) && $loggingConfiguration->hasTestDoxText()) { + $arguments['testdoxTextFile'] = $loggingConfiguration->testDoxText()->target()->path(); + } + if (!isset($arguments['testdoxXMLFile']) && $loggingConfiguration->hasTestDoxXml()) { + $arguments['testdoxXMLFile'] = $loggingConfiguration->testDoxXml()->target()->path(); + } + } + $testdoxGroupConfiguration = $arguments['configurationObject']->testdoxGroups(); + if (!isset($arguments['testdoxGroups']) && $testdoxGroupConfiguration->hasInclude()) { + $arguments['testdoxGroups'] = $testdoxGroupConfiguration->include()->asArrayOfStrings(); + } + if (!isset($arguments['testdoxExcludeGroups']) && $testdoxGroupConfiguration->hasExclude()) { + $arguments['testdoxExcludeGroups'] = $testdoxGroupConfiguration->exclude()->asArrayOfStrings(); + } + } + $extensionHandler = new ExtensionHandler(); + foreach ($arguments['extensions'] as $extension) { + $extensionHandler->registerExtension($extension, $this); + } + unset($extensionHandler); + $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['colors'] = $arguments['colors'] ?? \PHPUnit\TextUI\DefaultResultPrinter::COLOR_DEFAULT; + $arguments['columns'] = $arguments['columns'] ?? 80; + $arguments['convertDeprecationsToExceptions'] = $arguments['convertDeprecationsToExceptions'] ?? \false; + $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['failOnIncomplete'] = $arguments['failOnIncomplete'] ?? \false; + $arguments['failOnRisky'] = $arguments['failOnRisky'] ?? \false; + $arguments['failOnSkipped'] = $arguments['failOnSkipped'] ?? \false; + $arguments['failOnWarning'] = $arguments['failOnWarning'] ?? \false; + $arguments['groups'] = $arguments['groups'] ?? []; + $arguments['noInteraction'] = $arguments['noInteraction'] ?? \false; + $arguments['processIsolation'] = $arguments['processIsolation'] ?? \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; + if ($arguments['reportLowUpperBound'] > $arguments['reportHighLowerBound']) { + $arguments['reportLowUpperBound'] = 50; + $arguments['reportHighLowerBound'] = 90; + } } - public function resolveDependencies() : bool + private function processSuiteFilters(TestSuite $suite, array $arguments) : void { - return $this->resolveDependencies; + if (!$arguments['filter'] && empty($arguments['groups']) && empty($arguments['excludeGroups']) && empty($arguments['testsCovering']) && empty($arguments['testsUsing'])) { + 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 (!empty($arguments['testsCovering'])) { + $filterFactory->addFilter(new ReflectionClass(IncludeGroupFilterIterator::class), array_map(static function (string $name) : string { + return '__phpunit_covers_' . $name; + }, $arguments['testsCovering'])); + } + if (!empty($arguments['testsUsing'])) { + $filterFactory->addFilter(new ReflectionClass(IncludeGroupFilterIterator::class), array_map(static function (string $name) : string { + return '__phpunit_uses_' . $name; + }, $arguments['testsUsing'])); + } + if ($arguments['filter']) { + $filterFactory->addFilter(new ReflectionClass(NameFilterIterator::class), $arguments['filter']); + } + $suite->injectFilter($filterFactory); } - public function defectsFirst() : bool + private function writeMessage(string $type, string $message) : void { - return $this->defectsFirst; + if (!$this->messagePrinted) { + $this->write("\n"); + } + $this->write(sprintf("%-15s%s\n", $type . ':', $message)); + $this->messagePrinted = \true; } - public function backupGlobals() : bool + private function createPrinter(string $class, array $arguments) : \PHPUnit\TextUI\ResultPrinter { - return $this->backupGlobals; + $object = new $class(isset($arguments['stderr']) && $arguments['stderr'] === \true ? 'php://stderr' : null, $arguments['verbose'], $arguments['colors'], $arguments['debug'], $arguments['columns'], $arguments['reverseList']); + assert($object instanceof \PHPUnit\TextUI\ResultPrinter); + return $object; } - public function backupStaticAttributes() : bool + private function codeCoverageGenerationStart(string $format) : void { - return $this->backupStaticAttributes; + $this->write(sprintf("\nGenerating code coverage report in %s format ... ", $format)); + $this->timer->start(); } - public function registerMockObjectsFromTestArgumentsRecursively() : bool + private function codeCoverageGenerationSucceeded() : void { - return $this->registerMockObjectsFromTestArgumentsRecursively; + $this->write(sprintf("done [%s]\n", $this->timer->stop()->asString())); } - public function conflictBetweenPrinterClassAndTestdox() : bool + private function codeCoverageGenerationFailed(\Exception $e) : void { - return $this->conflictBetweenPrinterClassAndTestdox; + $this->write(sprintf("failed [%s]\n%s\n", $this->timer->stop()->asString(), $e->getMessage())); } } path = $path; - $this->prefix = $prefix; - $this->suffix = $suffix; - $this->phpVersion = $phpVersion; - $this->phpVersionOperator = $phpVersionOperator; - } - public function path() : string - { - return $this->path; - } - public function prefix() : string - { - return $this->prefix; - } - public function suffix() : string - { - return $this->suffix; - } - public function phpVersion() : string - { - return $this->phpVersion; - } - public function phpVersionOperator() : VersionComparisonOperator + public function map(TestSuiteCollection $configuration, string $filter) : TestSuiteObject { - return $this->phpVersionOperator; + try { + $filterAsArray = $filter ? explode(',', $filter) : []; + $result = new TestSuiteObject(); + foreach ($configuration as $testSuiteConfiguration) { + if (!empty($filterAsArray) && !in_array($testSuiteConfiguration->name(), $filterAsArray, \true)) { + continue; + } + $testSuite = new TestSuiteObject($testSuiteConfiguration->name()); + $testSuiteEmpty = \true; + $exclude = []; + foreach ($testSuiteConfiguration->exclude()->asArray() as $file) { + $exclude[] = $file->path(); + } + foreach ($testSuiteConfiguration->directories() as $directory) { + if (!version_compare(PHP_VERSION, $directory->phpVersion(), $directory->phpVersionOperator()->asString())) { + continue; + } + $files = (new Facade())->getFilesAsArray($directory->path(), $directory->suffix(), $directory->prefix(), $exclude); + if (!empty($files)) { + $testSuite->addTestFiles($files); + $testSuiteEmpty = \false; + } elseif (strpos($directory->path(), '*') === \false && !is_dir($directory->path())) { + throw new \PHPUnit\TextUI\TestDirectoryNotFoundException($directory->path()); + } + } + foreach ($testSuiteConfiguration->files() as $file) { + if (!is_file($file->path())) { + throw new \PHPUnit\TextUI\TestFileNotFoundException($file->path()); + } + if (!version_compare(PHP_VERSION, $file->phpVersion(), $file->phpVersionOperator()->asString())) { + continue; + } + $testSuite->addTestFile($file->path()); + $testSuiteEmpty = \false; + } + if (!$testSuiteEmpty) { + $result->addTest($testSuite); + } + } + return $result; + } catch (FrameworkException $e) { + throw new \PHPUnit\TextUI\RuntimeException($e->getMessage(), $e->getCode(), $e); + } } } */ -final class TestDirectoryCollection implements Countable, IteratorAggregate +final class CodeCoverage { /** - * @var TestDirectory[] + * @var ?Directory + */ + private $cacheDirectory; + /** + * @var DirectoryCollection */ private $directories; /** - * @param TestDirectory[] $directories + * @var FileCollection */ - public static function fromArray(array $directories) : self + private $files; + /** + * @var DirectoryCollection + */ + private $excludeDirectories; + /** + * @var FileCollection + */ + private $excludeFiles; + /** + * @var bool + */ + private $pathCoverage; + /** + * @var bool + */ + private $includeUncoveredFiles; + /** + * @var bool + */ + private $processUncoveredFiles; + /** + * @var bool + */ + private $ignoreDeprecatedCodeUnits; + /** + * @var bool + */ + private $disableCodeCoverageIgnore; + /** + * @var ?Clover + */ + private $clover; + /** + * @var ?Cobertura + */ + private $cobertura; + /** + * @var ?Crap4j + */ + private $crap4j; + /** + * @var ?Html + */ + private $html; + /** + * @var ?Php + */ + private $php; + /** + * @var ?Text + */ + private $text; + /** + * @var ?Xml + */ + private $xml; + public function __construct(?Directory $cacheDirectory, DirectoryCollection $directories, FileCollection $files, DirectoryCollection $excludeDirectories, FileCollection $excludeFiles, bool $pathCoverage, bool $includeUncoveredFiles, bool $processUncoveredFiles, bool $ignoreDeprecatedCodeUnits, bool $disableCodeCoverageIgnore, ?Clover $clover, ?Cobertura $cobertura, ?Crap4j $crap4j, ?Html $html, ?Php $php, ?Text $text, ?Xml $xml) { - return new self(...$directories); + $this->cacheDirectory = $cacheDirectory; + $this->directories = $directories; + $this->files = $files; + $this->excludeDirectories = $excludeDirectories; + $this->excludeFiles = $excludeFiles; + $this->pathCoverage = $pathCoverage; + $this->includeUncoveredFiles = $includeUncoveredFiles; + $this->processUncoveredFiles = $processUncoveredFiles; + $this->ignoreDeprecatedCodeUnits = $ignoreDeprecatedCodeUnits; + $this->disableCodeCoverageIgnore = $disableCodeCoverageIgnore; + $this->clover = $clover; + $this->cobertura = $cobertura; + $this->crap4j = $crap4j; + $this->html = $html; + $this->php = $php; + $this->text = $text; + $this->xml = $xml; } - private function __construct(\PHPUnit\TextUI\XmlConfiguration\TestDirectory ...$directories) + /** + * @psalm-assert-if-true !null $this->cacheDirectory + */ + public function hasCacheDirectory() : bool { - $this->directories = $directories; + return $this->cacheDirectory !== null; } /** - * @return TestDirectory[] + * @throws Exception */ - public function asArray() : array + public function cacheDirectory() : Directory { - return $this->directories; + if (!$this->hasCacheDirectory()) { + throw new Exception('No cache directory has been configured'); + } + return $this->cacheDirectory; } - public function count() : int + public function hasNonEmptyListOfFilesToBeIncludedInCodeCoverageReport() : bool { - return count($this->directories); + return count($this->directories) > 0 || count($this->files) > 0; } - public function getIterator() : \PHPUnit\TextUI\XmlConfiguration\TestDirectoryCollectionIterator + public function directories() : DirectoryCollection { - return new \PHPUnit\TextUI\XmlConfiguration\TestDirectoryCollectionIterator($this); + return $this->directories; } - public function isEmpty() : bool + public function files() : FileCollection { - return $this->count() === 0; + return $this->files; + } + public function excludeDirectories() : DirectoryCollection + { + return $this->excludeDirectories; + } + public function excludeFiles() : FileCollection + { + return $this->excludeFiles; + } + public function pathCoverage() : bool + { + return $this->pathCoverage; + } + public function includeUncoveredFiles() : bool + { + return $this->includeUncoveredFiles; + } + public function ignoreDeprecatedCodeUnits() : bool + { + return $this->ignoreDeprecatedCodeUnits; + } + public function disableCodeCoverageIgnore() : bool + { + return $this->disableCodeCoverageIgnore; + } + public function processUncoveredFiles() : bool + { + return $this->processUncoveredFiles; } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TextUI\XmlConfiguration; - -use function count; -use function iterator_count; -use Countable; -use Iterator; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - * - * @template-implements Iterator - */ -final class TestDirectoryCollectionIterator implements Countable, Iterator -{ /** - * @var TestDirectory[] + * @psalm-assert-if-true !null $this->clover */ - private $directories; + public function hasClover() : bool + { + return $this->clover !== null; + } /** - * @var int + * @throws Exception */ - private $position; - public function __construct(\PHPUnit\TextUI\XmlConfiguration\TestDirectoryCollection $directories) + public function clover() : Clover { - $this->directories = $directories->asArray(); + if (!$this->hasClover()) { + throw new Exception('Code Coverage report "Clover XML" has not been configured'); + } + return $this->clover; } - public function count() : int + /** + * @psalm-assert-if-true !null $this->cobertura + */ + public function hasCobertura() : bool { - return iterator_count($this); + return $this->cobertura !== null; } - public function rewind() : void + /** + * @throws Exception + */ + public function cobertura() : Cobertura { - $this->position = 0; + if (!$this->hasCobertura()) { + throw new Exception('Code Coverage report "Cobertura XML" has not been configured'); + } + return $this->cobertura; } - public function valid() : bool + /** + * @psalm-assert-if-true !null $this->crap4j + */ + public function hasCrap4j() : bool { - return $this->position < count($this->directories); + return $this->crap4j !== null; } - public function key() : int + /** + * @throws Exception + */ + public function crap4j() : Crap4j { - return $this->position; + if (!$this->hasCrap4j()) { + throw new Exception('Code Coverage report "Crap4J" has not been configured'); + } + return $this->crap4j; } - public function current() : \PHPUnit\TextUI\XmlConfiguration\TestDirectory + /** + * @psalm-assert-if-true !null $this->html + */ + public function hasHtml() : bool { - return $this->directories[$this->position]; + return $this->html !== null; } - public function next() : void + /** + * @throws Exception + */ + public function html() : Html { - $this->position++; + if (!$this->hasHtml()) { + throw new Exception('Code Coverage report "HTML" has not been configured'); + } + return $this->html; + } + /** + * @psalm-assert-if-true !null $this->php + */ + public function hasPhp() : bool + { + return $this->php !== null; + } + /** + * @throws Exception + */ + public function php() : Php + { + if (!$this->hasPhp()) { + throw new Exception('Code Coverage report "PHP" has not been configured'); + } + return $this->php; + } + /** + * @psalm-assert-if-true !null $this->text + */ + public function hasText() : bool + { + return $this->text !== null; + } + /** + * @throws Exception + */ + public function text() : Text + { + if (!$this->hasText()) { + throw new Exception('Code Coverage report "Text" has not been configured'); + } + return $this->text; + } + /** + * @psalm-assert-if-true !null $this->xml + */ + public function hasXml() : bool + { + return $this->xml !== null; + } + /** + * @throws Exception + */ + public function xml() : Xml + { + if (!$this->hasXml()) { + throw new Exception('Code Coverage report "XML" has not been configured'); + } + return $this->xml; } } path = $path; - $this->phpVersion = $phpVersion; - $this->phpVersionOperator = $phpVersionOperator; + $this->prefix = $prefix; + $this->suffix = $suffix; + $this->group = $group; } public function path() : string { return $this->path; } - public function phpVersion() : string + public function prefix() : string { - return $this->phpVersion; + return $this->prefix; } - public function phpVersionOperator() : VersionComparisonOperator + public function suffix() : string { - return $this->phpVersionOperator; + return $this->suffix; + } + public function group() : string + { + return $this->group; } } + * @template-implements IteratorAggregate */ -final class TestFileCollection implements Countable, IteratorAggregate +final class DirectoryCollection implements Countable, IteratorAggregate { /** - * @var TestFile[] + * @var Directory[] */ - private $files; + private $directories; /** - * @param TestFile[] $files + * @param Directory[] $directories */ - public static function fromArray(array $files) : self + public static function fromArray(array $directories) : self { - return new self(...$files); + return new self(...$directories); } - private function __construct(\PHPUnit\TextUI\XmlConfiguration\TestFile ...$files) + private function __construct(\PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Filter\Directory ...$directories) { - $this->files = $files; + $this->directories = $directories; } /** - * @return TestFile[] + * @return Directory[] */ public function asArray() : array { - return $this->files; + return $this->directories; } public function count() : int { - return count($this->files); - } - public function getIterator() : \PHPUnit\TextUI\XmlConfiguration\TestFileCollectionIterator - { - return new \PHPUnit\TextUI\XmlConfiguration\TestFileCollectionIterator($this); + return count($this->directories); } - public function isEmpty() : bool + public function getIterator() : \PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Filter\DirectoryCollectionIterator { - return $this->count() === 0; + return new \PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Filter\DirectoryCollectionIterator($this); } } + * @template-implements Iterator */ -final class TestFileCollectionIterator implements Countable, Iterator +final class DirectoryCollectionIterator implements Countable, Iterator { /** - * @var TestFile[] + * @var Directory[] */ - private $files; + private $directories; /** * @var int */ private $position; - public function __construct(\PHPUnit\TextUI\XmlConfiguration\TestFileCollection $files) + public function __construct(\PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Filter\DirectoryCollection $directories) { - $this->files = $files->asArray(); + $this->directories = $directories->asArray(); } public function count() : int { @@ -79127,15 +83574,15 @@ final class TestFileCollectionIterator implements Countable, Iterator } public function valid() : bool { - return $this->position < count($this->files); + return $this->position < count($this->directories); } public function key() : int { return $this->position; } - public function current() : \PHPUnit\TextUI\XmlConfiguration\TestFile + public function current() : \PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Filter\Directory { - return $this->files[$this->position]; + return $this->directories[$this->position]; } public function next() : void { @@ -79153,53 +83600,62 @@ declare (strict_types=1); * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\TextUI\XmlConfiguration; +namespace PHPUnit\TextUI\XmlConfiguration\CodeCoverage; + +use PHPUnitPHAR\SebastianBergmann\CodeCoverage\Filter; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class FilterMapper +{ + public function map(Filter $filter, \PHPUnit\TextUI\XmlConfiguration\CodeCoverage\CodeCoverage $configuration) : void + { + foreach ($configuration->directories() as $directory) { + $filter->includeDirectory($directory->path(), $directory->suffix(), $directory->prefix()); + } + foreach ($configuration->files() as $file) { + $filter->includeFile($file->path()); + } + foreach ($configuration->excludeDirectories() as $directory) { + $filter->excludeDirectory($directory->path(), $directory->suffix(), $directory->prefix()); + } + foreach ($configuration->excludeFiles() as $file) { + $filter->excludeFile($file->path()); + } + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report; +use PHPUnit\TextUI\XmlConfiguration\File; /** * @internal This class is not covered by the backward compatibility promise for PHPUnit * * @psalm-immutable */ -final class TestSuite +final class Clover { /** - * @var string - */ - private $name; - /** - * @var TestDirectoryCollection - */ - private $directories; - /** - * @var TestFileCollection - */ - private $files; - /** - * @var FileCollection + * @var File */ - private $exclude; - public function __construct(string $name, \PHPUnit\TextUI\XmlConfiguration\TestDirectoryCollection $directories, \PHPUnit\TextUI\XmlConfiguration\TestFileCollection $files, \PHPUnit\TextUI\XmlConfiguration\FileCollection $exclude) - { - $this->name = $name; - $this->directories = $directories; - $this->files = $files; - $this->exclude = $exclude; - } - public function name() : string - { - return $this->name; - } - public function directories() : \PHPUnit\TextUI\XmlConfiguration\TestDirectoryCollection - { - return $this->directories; - } - public function files() : \PHPUnit\TextUI\XmlConfiguration\TestFileCollection + private $target; + public function __construct(File $target) { - return $this->files; + $this->target = $target; } - public function exclude() : \PHPUnit\TextUI\XmlConfiguration\FileCollection + public function target() : File { - return $this->exclude; + return $this->target; } } */ -final class TestSuiteCollection implements Countable, IteratorAggregate +final class Cobertura { /** - * @var TestSuite[] - */ - private $testSuites; - /** - * @param TestSuite[] $testSuites + * @var File */ - public static function fromArray(array $testSuites) : self + private $target; + public function __construct(File $target) { - return new self(...$testSuites); + $this->target = $target; } - private function __construct(\PHPUnit\TextUI\XmlConfiguration\TestSuite ...$testSuites) + public function target() : File { - $this->testSuites = $testSuites; + return $this->target; } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report; + +use PHPUnit\TextUI\XmlConfiguration\File; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + * + * @psalm-immutable + */ +final class Crap4j +{ /** - * @return TestSuite[] + * @var File */ - public function asArray() : array - { - return $this->testSuites; - } - public function count() : int + private $target; + /** + * @var int + */ + private $threshold; + public function __construct(File $target, int $threshold) { - return count($this->testSuites); + $this->target = $target; + $this->threshold = $threshold; } - public function getIterator() : \PHPUnit\TextUI\XmlConfiguration\TestSuiteCollectionIterator + public function target() : File { - return new \PHPUnit\TextUI\XmlConfiguration\TestSuiteCollectionIterator($this); + return $this->target; } - public function isEmpty() : bool + public function threshold() : int { - return $this->count() === 0; + return $this->threshold; } } + * @psalm-immutable */ -final class TestSuiteCollectionIterator implements Countable, Iterator +final class Html { /** - * @var TestSuite[] + * @var Directory */ - private $testSuites; + private $target; /** * @var int */ - private $position; - public function __construct(\PHPUnit\TextUI\XmlConfiguration\TestSuiteCollection $testSuites) - { - $this->testSuites = $testSuites->asArray(); - } - public function count() : int - { - return iterator_count($this); - } - public function rewind() : void - { - $this->position = 0; - } - public function valid() : bool + private $lowUpperBound; + /** + * @var int + */ + private $highLowerBound; + public function __construct(Directory $target, int $lowUpperBound, int $highLowerBound) { - return $this->position < count($this->testSuites); + $this->target = $target; + $this->lowUpperBound = $lowUpperBound; + $this->highLowerBound = $highLowerBound; } - public function key() : int + public function target() : Directory { - return $this->position; + return $this->target; } - public function current() : \PHPUnit\TextUI\XmlConfiguration\TestSuite + public function lowUpperBound() : int { - return $this->testSuites[$this->position]; + return $this->lowUpperBound; } - public function next() : void + public function highLowerBound() : int { - $this->position++; + return $this->highLowerBound; } } PHP(?:Unit)?)\\s+(?P[<>=!]{0,2})\\s*(?P[\\d\\.-]+(dev|(RC|alpha|beta)[\\d\\.])?)[ \\t]*\\r?$/m'; - private const REGEX_REQUIRES_VERSION_CONSTRAINT = '/@requires\\s+(?PPHP(?:Unit)?)\\s+(?P[\\d\\t \\-.|~^]+)[ \\t]*\\r?$/m'; - private const REGEX_REQUIRES_OS = '/@requires\\s+(?POS(?:FAMILY)?)\\s+(?P.+?)[ \\t]*\\r?$/m'; - private const REGEX_REQUIRES_SETTING = '/@requires\\s+(?Psetting)\\s+(?P([^ ]+?))\\s*(?P[\\w\\.-]+[\\w\\.]?)?[ \\t]*\\r?$/m'; - private const REGEX_REQUIRES = '/@requires\\s+(?Pfunction|extension)\\s+(?P([^\\s<>=!]+))\\s*(?P[<>=!]{0,2})\\s*(?P[\\d\\.-]+[\\d\\.]?)?[ \\t]*\\r?$/m'; - private const REGEX_TEST_WITH = '/@testWith\\s+/'; - /** @var string */ - private $docComment; - /** @var bool */ - private $isMethod; - /** @var array> pre-parsed annotations indexed by name and occurrence index */ - private $symbolAnnotations; - /** - * @var null|array - * - * @psalm-var null|(array{ - * __OFFSET: array&array{__FILE: string}, - * setting?: array, - * extension_versions?: array - * }&array< - * string, - * string|array{version: string, operator: string}|array{constraint: string}|array - * >) - */ - 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> $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&array{__FILE: string}, - * setting?: array, - * extension_versions?: array - * }&array< - * string, - * string|array{version: string, operator: string}|array{constraint: string}|array - * > - * - * @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)]; - // Trim docblock markers, split it into lines and rewind offset to start of docblock - $lines = preg_replace(['#^/\\*{2}#', '#\\*/$#'], '', 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 (\PHPUnit\PharIo\Version\Exception $e) { - 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])); - } - /** - * 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 InvalidDataSetException(sprintf('Data set %s is invalid.', is_int($key) ? '#' . $key : '"' . $key . '"')); - } - } - return $data; - } - /** - * @psalm-return array - */ - 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[A-Za-z_-]+)(?:[ \\t]+(?P.*?))?[ \\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); - } - public function isToBeExecutedAsPreCondition() : bool - { - return 1 === preg_match('/@preCondition\\b/', $this->docComment); - } - public function isToBeExecutedAsPostCondition() : bool - { - return 1 === preg_match('/@postCondition\\b/', $this->docComment); - } - 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); - // @codeCoverageIgnoreStart - } catch (ReflectionException $e) { - throw new Exception($e->getMessage(), $e->getCode(), $e); - // @codeCoverageIgnoreEnd - } - 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> */ - private static function parseDocBlock(string $docBlock) : array + private $target; + public function __construct(File $target) { - // 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[A-Za-z_-]+)(?:[ \\t]+(?P.*?))?[ \\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; + $this->target = $target; } - /** @param ReflectionClass|ReflectionFunctionAbstract $reflector */ - private static function extractAnnotationsFromReflector(Reflector $reflector) : array + public function target() : File { - $annotations = []; - if ($reflector instanceof ReflectionClass) { - $annotations = array_merge($annotations, ...array_map(static function (ReflectionClass $trait) : array { - return self::parseDocBlock((string) $trait->getDocComment()); - }, array_values($reflector->getTraits()))); - } - return array_merge($annotations, self::parseDocBlock((string) $reflector->getDocComment())); + return $this->target; } } indexed by class name */ - private $classDocBlocks = []; - /** @var array> 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 + * @var File */ - public function forClassName(string $class) : \PHPUnit\Util\Annotation\DocBlock - { - if (array_key_exists($class, $this->classDocBlocks)) { - return $this->classDocBlocks[$class]; - } - try { - $reflection = new ReflectionClass($class); - // @codeCoverageIgnoreStart - } catch (ReflectionException $e) { - throw new Exception($e->getMessage(), $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd - return $this->classDocBlocks[$class] = \PHPUnit\Util\Annotation\DocBlock::ofClass($reflection); - } + private $target; /** - * @throws Exception - * - * @psalm-param class-string $classInHierarchy + * @var bool */ - public function forMethod(string $classInHierarchy, string $method) : \PHPUnit\Util\Annotation\DocBlock + private $showUncoveredFiles; + /** + * @var bool + */ + private $showOnlySummary; + public function __construct(File $target, bool $showUncoveredFiles, bool $showOnlySummary) { - if (isset($this->methodDocBlocks[$classInHierarchy][$method])) { - return $this->methodDocBlocks[$classInHierarchy][$method]; - } - try { - $reflection = new ReflectionMethod($classInHierarchy, $method); - // @codeCoverageIgnoreStart - } catch (ReflectionException $e) { - throw new Exception($e->getMessage(), $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd - return $this->methodDocBlocks[$classInHierarchy][$method] = \PHPUnit\Util\Annotation\DocBlock::ofMethod($reflection, $classInHierarchy); + $this->target = $target; + $this->showUncoveredFiles = $showUncoveredFiles; + $this->showOnlySummary = $showOnlySummary; } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Util; - -/** - * @deprecated Use ExcludeList instead - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -final class Blacklist -{ - public static function addDirectory(string $directory) : void + public function target() : File { - \PHPUnit\Util\ExcludeList::addDirectory($directory); + return $this->target; } - /** - * @throws Exception - * - * @return string[] - */ - public function getBlacklistedDirectories() : array + public function showUncoveredFiles() : bool { - return (new \PHPUnit\Util\ExcludeList())->getExcludedDirectories(); + return $this->showUncoveredFiles; } - /** - * @throws Exception - */ - public function isBlacklisted(string $file) : bool + public function showOnlySummary() : bool { - return (new \PHPUnit\Util\ExcludeList())->isExcluded($file); + return $this->showOnlySummary; } } target = $target; + } + public function target() : Directory + { + return $this->target; } } + * @var string */ - private const WHITESPACE_MAP = [' ' => '·', "\t" => '⇥']; + private $filename; /** - * @var array + * @var ValidationResult */ - private const WHITESPACE_EOL_MAP = [' ' => '·', "\t" => '⇥', "\n" => '↵', "\r" => '⟵']; + private $validationResult; /** - * @var array + * @var ExtensionCollection */ - 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 "\x1b[2m{$buffer}\x1b[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 = ["/\x1b\\[22m\x1b\\[2m/" => '', "/\x1b\\[([^m]*)m\x1b\\[([1-9][0-9;]*)m/" => "\x1b[\$1;\$2m", "/(\x1b\\[[^m]*m)+(\x1b\\[0m)/" => '$2']; - return preg_replace(array_keys($patterns), array_values($patterns), $buffer); - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Util; - -use const E_DEPRECATED; -use const E_NOTICE; -use const E_STRICT; -use const E_USER_DEPRECATED; -use const E_USER_NOTICE; -use const E_USER_WARNING; -use const E_WARNING; -use function error_reporting; -use function restore_error_handler; -use function set_error_handler; -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 -{ + private $extensions; /** - * @var bool + * @var CodeCoverage */ - private $convertDeprecationsToExceptions; + private $codeCoverage; /** - * @var bool + * @var Groups */ - private $convertErrorsToExceptions; + private $groups; /** - * @var bool + * @var Groups */ - private $convertNoticesToExceptions; + private $testdoxGroups; /** - * @var bool + * @var ExtensionCollection */ - private $convertWarningsToExceptions; + private $listeners; /** - * @var bool + * @var Logging */ - private $registered = \false; - public static function invokeIgnoringWarnings(callable $callable) + private $logging; + /** + * @var Php + */ + private $php; + /** + * @var PHPUnit + */ + private $phpunit; + /** + * @var TestSuiteCollection + */ + private $testSuite; + public function __construct(string $filename, ValidationResult $validationResult, \PHPUnit\TextUI\XmlConfiguration\ExtensionCollection $extensions, CodeCoverage $codeCoverage, \PHPUnit\TextUI\XmlConfiguration\Groups $groups, \PHPUnit\TextUI\XmlConfiguration\Groups $testdoxGroups, \PHPUnit\TextUI\XmlConfiguration\ExtensionCollection $listeners, Logging $logging, \PHPUnit\TextUI\XmlConfiguration\Php $php, \PHPUnit\TextUI\XmlConfiguration\PHPUnit $phpunit, \PHPUnit\TextUI\XmlConfiguration\TestSuiteCollection $testSuite) { - set_error_handler(static function ($errorNumber, $errorString) { - if ($errorNumber === E_WARNING) { - return; - } - return \false; - }); - $result = $callable(); - restore_error_handler(); - return $result; + $this->filename = $filename; + $this->validationResult = $validationResult; + $this->extensions = $extensions; + $this->codeCoverage = $codeCoverage; + $this->groups = $groups; + $this->testdoxGroups = $testdoxGroups; + $this->listeners = $listeners; + $this->logging = $logging; + $this->php = $php; + $this->phpunit = $phpunit; + $this->testSuite = $testSuite; } - public function __construct(bool $convertDeprecationsToExceptions, bool $convertErrorsToExceptions, bool $convertNoticesToExceptions, bool $convertWarningsToExceptions) + public function filename() : string { - $this->convertDeprecationsToExceptions = $convertDeprecationsToExceptions; - $this->convertErrorsToExceptions = $convertErrorsToExceptions; - $this->convertNoticesToExceptions = $convertNoticesToExceptions; - $this->convertWarningsToExceptions = $convertWarningsToExceptions; + return $this->filename; } - public function __invoke(int $errorNumber, string $errorString, string $errorFile, int $errorLine) : bool + public function hasValidationErrors() : 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); - } + return $this->validationResult->hasValidationErrors(); } - public function register() : void + public function validationErrors() : string { - if ($this->registered) { - return; - } - $oldErrorHandler = set_error_handler($this); - if ($oldErrorHandler !== null) { - restore_error_handler(); - return; - } - $this->registered = \true; + return $this->validationResult->asString(); } - public function unregister() : void + public function extensions() : \PHPUnit\TextUI\XmlConfiguration\ExtensionCollection { - if (!$this->registered) { - return; - } - restore_error_handler(); + return $this->extensions; + } + public function codeCoverage() : CodeCoverage + { + return $this->codeCoverage; + } + public function groups() : \PHPUnit\TextUI\XmlConfiguration\Groups + { + return $this->groups; + } + public function testdoxGroups() : \PHPUnit\TextUI\XmlConfiguration\Groups + { + return $this->testdoxGroups; + } + public function listeners() : \PHPUnit\TextUI\XmlConfiguration\ExtensionCollection + { + return $this->listeners; + } + public function logging() : Logging + { + return $this->logging; + } + public function php() : \PHPUnit\TextUI\XmlConfiguration\Php + { + return $this->php; + } + public function phpunit() : \PHPUnit\TextUI\XmlConfiguration\PHPUnit + { + return $this->phpunit; + } + public function testSuite() : \PHPUnit\TextUI\XmlConfiguration\TestSuiteCollection + { + return $this->testSuite; } } - */ - private const EXCLUDED_CLASS_NAMES = [ - // composer - ClassLoader::class => 1, - // doctrine/instantiator - Instantiator::class => 1, - // myclabs/deepcopy - DeepCopy::class => 1, - // nikic/php-parser - Parser::class => 1, - // phar-io/manifest - Manifest::class => 1, - // phar-io/version - PharIoVersion::class => 1, - // phpdocumentor/type-resolver - \PHPUnit\Util\Type::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 - Template::class => 1, - // phpunit/php-timer - Timer::class => 1, - // sebastian/cli-parser - CliParser::class => 1, - // sebastian/code-unit - CodeUnit::class => 1, - // sebastian/code-unit-reverse-lookup - Wizard::class => 1, - // sebastian/comparator - Comparator::class => 1, - // sebastian/complexity - Calculator::class => 1, - // sebastian/diff - Diff::class => 1, - // sebastian/environment - Runtime::class => 1, - // sebastian/exporter - Exporter::class => 1, - // sebastian/global-state - Snapshot::class => 1, - // sebastian/lines-of-code - Counter::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, - ]; - /** - * @var string[] - */ - private static $directories = []; - /** - * @var bool - */ - private static $initialized = \false; - public static function addDirectory(string $directory) : void - { - if (!is_dir($directory)) { - throw new \PHPUnit\Util\Exception(sprintf('"%s" is not a directory', $directory)); - } - self::$directories[] = realpath($directory); - } - /** - * @throws Exception - * - * @return string[] - */ - public function getExcludedDirectories() : array - { - $this->initialize(); - return self::$directories; - } - /** - * @throws Exception + * @var string */ - public function isExcluded(string $file) : bool + private $path; + public function __construct(string $path) { - if (defined('PHPUNIT_TESTSUITE')) { - return \false; - } - $this->initialize(); - foreach (self::$directories as $directory) { - if (strpos($file, $directory) === 0) { - return \true; - } - } - return \false; + $this->path = $path; } - /** - * @throws Exception - */ - private function initialize() : void + public function path() : string { - if (self::$initialized) { - return; - } - foreach (self::EXCLUDED_CLASS_NAMES as $className => $parent) { - if (!class_exists($className)) { - continue; - } - $directory = (new ReflectionClass($className))->getFileName(); - 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'; - } - self::$initialized = \true; + return $this->path; } } */ -final class FileLoader +final class DirectoryCollection implements Countable, IteratorAggregate { /** - * 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 + * @var Directory[] */ - public static function checkAndLoad(string $filename) : string - { - $includePathFilename = stream_resolve_include_path($filename); - $localFile = __DIR__ . DIRECTORY_SEPARATOR . $filename; - if (!$includePathFilename || $includePathFilename === $localFile || !self::isReadable($includePathFilename)) { - throw new \PHPUnit\Util\Exception(sprintf('Cannot open file "%s".' . "\n", $filename)); - } - self::load($includePathFilename); - return $includePathFilename; - } + private $directories; /** - * Loads a PHP sourcefile. + * @param Directory[] $directories */ - public static function load(string $filename) : void + public static function fromArray(array $directories) : self { - $oldVariableNames = array_keys(get_defined_vars()); - /** - * @noinspection PhpIncludeInspection - * - * @psalm-suppress UnresolvableInclude - */ - include_once $filename; - $newVariables = get_defined_vars(); - foreach (array_diff(array_keys($newVariables), $oldVariableNames) as $variableName) { - if ($variableName !== 'oldVariableNames') { - $GLOBALS[$variableName] = $newVariables[$variableName]; - } - } + return new self(...$directories); + } + private function __construct(\PHPUnit\TextUI\XmlConfiguration\Directory ...$directories) + { + $this->directories = $directories; } /** - * @see https://github.com/sebastianbergmann/phpunit/pull/2751 + * @return Directory[] */ - private static function isReadable(string $filename) : bool + public function asArray() : array { - return @fopen($filename, 'r') !== \false; + return $this->directories; + } + public function count() : int + { + return count($this->directories); + } + public function getIterator() : \PHPUnit\TextUI\XmlConfiguration\DirectoryCollectionIterator + { + return new \PHPUnit\TextUI\XmlConfiguration\DirectoryCollectionIterator($this); + } + public function isEmpty() : bool + { + return $this->count() === 0; } } */ -final class Filesystem +final class DirectoryCollectionIterator implements Countable, Iterator { /** - * Maps class names to source file names. - * - * - PEAR CS: Foo_Bar_Baz -> Foo/Bar/Baz.php - * - Namespace: Foo\Bar\Baz -> Foo/Bar/Baz.php + * @var Directory[] */ - public static function classNameToFilename(string $className) : string + private $directories; + /** + * @var int + */ + private $position; + public function __construct(\PHPUnit\TextUI\XmlConfiguration\DirectoryCollection $directories) { - return str_replace(['_', '\\'], DIRECTORY_SEPARATOR, $className) . '.php'; + $this->directories = $directories->asArray(); } - public static function createDirectory(string $directory) : bool + public function count() : int { - return !(!is_dir($directory) && !@mkdir($directory, 0777, \true) && !is_dir($directory)); + return iterator_count($this); + } + public function rewind() : void + { + $this->position = 0; + } + public function valid() : bool + { + return $this->position < count($this->directories); + } + public function key() : int + { + return $this->position; + } + public function current() : \PHPUnit\TextUI\XmlConfiguration\Directory + { + return $this->directories[$this->position]; + } + public function next() : void + { + $this->position++; } } 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]); - } - $prefix = defined('__PHPUNIT_PHAR_ROOT__') ? __PHPUNIT_PHAR_ROOT__ : \false; - $excludeList = new \PHPUnit\Util\ExcludeList(); - foreach ($eTrace as $frame) { - if (self::shouldPrintFrame($frame, $prefix, $excludeList)) { - $filteredStacktrace .= sprintf("%s:%s\n", $frame['file'], $frame['line'] ?? '?'); - } - } - return $filteredStacktrace; - } - private static function shouldPrintFrame(array $frame, $prefix, \PHPUnit\Util\ExcludeList $excludeList) : bool - { - if (!isset($frame['file'])) { - return \false; - } - $file = $frame['file']; - $fileIsNotPrefixed = $prefix === \false || strpos($file, $prefix) !== 0; - // @see https://github.com/sebastianbergmann/phpunit/issues/4033 - if (isset($GLOBALS['_SERVER']['SCRIPT_NAME'])) { - $script = realpath($GLOBALS['_SERVER']['SCRIPT_NAME']); - } else { - $script = ''; - } - return is_file($file) && self::fileIsExcluded($file, $excludeList) && $fileIsNotPrefixed && $file !== $script; - } - private static function fileIsExcluded(string $file, \PHPUnit\Util\ExcludeList $excludeList) : bool + private $path; + public function __construct(string $path) { - return (empty($GLOBALS['__PHPUNIT_ISOLATION_EXCLUDE_LIST']) || !in_array($file, $GLOBALS['__PHPUNIT_ISOLATION_EXCLUDE_LIST'], \true)) && !$excludeList->isExcluded($file); + $this->path = $path; } - private static function frameExists(array $trace, string $file, int $line) : bool + public function path() : string { - foreach ($trace as $frame) { - if (isset($frame['file'], $frame['line']) && $frame['file'] === $file && $frame['line'] === $line) { - return \true; - } - } - return \false; + return $this->path; } } */ -final class GlobalState +final class FileCollection implements Countable, IteratorAggregate { /** - * @var string[] + * @var File[] */ - private const SUPER_GLOBAL_ARRAYS = ['_ENV', '_POST', '_GET', '_COOKIE', '_SERVER', '_FILES', '_REQUEST']; + private $files; /** - * @psalm-var array> + * @param File[] $files */ - private const DEPRECATED_INI_SETTINGS = ['7.3' => ['iconv.input_encoding' => \true, 'iconv.output_encoding' => \true, 'iconv.internal_encoding' => \true, 'mbstring.func_overload' => \true, 'mbstring.http_input' => \true, 'mbstring.http_output' => \true, 'mbstring.internal_encoding' => \true, 'string.strip_tags' => \true], '7.4' => ['iconv.input_encoding' => \true, 'iconv.output_encoding' => \true, 'iconv.internal_encoding' => \true, 'mbstring.func_overload' => \true, 'mbstring.http_input' => \true, 'mbstring.http_output' => \true, 'mbstring.internal_encoding' => \true, 'pdo_odbc.db2_instance_name' => \true, 'string.strip_tags' => \true], '8.0' => ['iconv.input_encoding' => \true, 'iconv.output_encoding' => \true, 'iconv.internal_encoding' => \true, 'mbstring.http_input' => \true, 'mbstring.http_output' => \true, 'mbstring.internal_encoding' => \true], '8.1' => ['auto_detect_line_endings' => \true, 'filter.default' => \true, 'iconv.input_encoding' => \true, 'iconv.output_encoding' => \true, 'iconv.internal_encoding' => \true, 'mbstring.http_input' => \true, 'mbstring.http_output' => \true, 'mbstring.internal_encoding' => \true, 'oci8.old_oci_close_semantics' => \true], '8.2' => ['auto_detect_line_endings' => \true, 'filter.default' => \true, 'iconv.input_encoding' => \true, 'iconv.output_encoding' => \true, 'iconv.internal_encoding' => \true, 'mbstring.http_input' => \true, 'mbstring.http_output' => \true, 'mbstring.internal_encoding' => \true, 'oci8.old_oci_close_semantics' => \true], '8.3' => ['auto_detect_line_endings' => \true, 'filter.default' => \true, 'iconv.input_encoding' => \true, 'iconv.output_encoding' => \true, 'iconv.internal_encoding' => \true, 'mbstring.http_input' => \true, 'mbstring.http_output' => \true, 'mbstring.internal_encoding' => \true, 'oci8.old_oci_close_semantics' => \true]]; + public static function fromArray(array $files) : self + { + return new self(...$files); + } + private function __construct(\PHPUnit\TextUI\XmlConfiguration\File ...$files) + { + $this->files = $files; + } /** - * @throws Exception + * @return File[] */ - public static function getIncludedFilesAsString() : string + public function asArray() : array { - return self::processIncludedFilesAsString(get_included_files()); + return $this->files; } + public function count() : int + { + return count($this->files); + } + public function getIterator() : \PHPUnit\TextUI\XmlConfiguration\FileCollectionIterator + { + return new \PHPUnit\TextUI\XmlConfiguration\FileCollectionIterator($this); + } + public function isEmpty() : bool + { + return $this->count() === 0; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration; + +use function count; +use function iterator_count; +use Countable; +use Iterator; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + * + * @template-implements Iterator + */ +final class FileCollectionIterator implements Countable, Iterator +{ /** - * @param string[] $files - * - * @throws Exception + * @var File[] */ - public static function processIncludedFilesAsString(array $files) : string + private $files; + /** + * @var int + */ + private $position; + public function __construct(\PHPUnit\TextUI\XmlConfiguration\FileCollection $files) { - $excludeList = new \PHPUnit\Util\ExcludeList(); - $prefix = \false; - $result = ''; - if (defined('__PHPUNIT_PHAR__')) { - $prefix = 'phar://' . __PHPUNIT_PHAR__ . '/'; - } - // Do not process bootstrap script - array_shift($files); - // If bootstrap script was a Composer bin proxy, skip the second entry as well - if (substr(strtr($files[0], '\\', '/'), -24) === '/phpunit/phpunit/phpunit') { - array_shift($files); - } - foreach (array_reverse($files) as $file) { - if (!empty($GLOBALS['__PHPUNIT_ISOLATION_EXCLUDE_LIST']) && in_array($file, $GLOBALS['__PHPUNIT_ISOLATION_EXCLUDE_LIST'], \true)) { - 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 (!$excludeList->isExcluded($file) && is_file($file)) { - $result = 'require_once \'' . $file . "';\n" . $result; - } - } - return $result; + $this->files = $files->asArray(); } - public static function getIniSettingsAsString() : string + public function count() : int { - $result = ''; - foreach (ini_get_all(null, \false) as $key => $value) { - if (self::isIniSettingDeprecated($key)) { - continue; - } - $result .= sprintf('@ini_set(%s, %s);' . "\n", self::exportVariable($key), self::exportVariable((string) $value)); - } - return $result; + return iterator_count($this); } - public static function getConstantsAsString() : string + public function rewind() : void { - $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; + $this->position = 0; } - public static function getGlobalsAsString() : string + public function valid() : bool { - $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])); - } - } - } - $excludeList = self::SUPER_GLOBAL_ARRAYS; - $excludeList[] = 'GLOBALS'; - foreach (array_keys($GLOBALS) as $key) { - if (!$GLOBALS[$key] instanceof Closure && !in_array($key, $excludeList, \true)) { - $result .= sprintf('$GLOBALS[\'%s\'] = %s;' . "\n", $key, self::exportVariable($GLOBALS[$key])); - } - } - return $result; + return $this->position < count($this->files); } - private static function exportVariable($variable) : string + public function key() : int { - if (is_scalar($variable) || $variable === null || is_array($variable) && self::arrayOnlyContainsScalars($variable)) { - return var_export($variable, \true); - } - return 'unserialize(' . var_export(serialize($variable), \true) . ')'; + return $this->position; } - private static function arrayOnlyContainsScalars(array $array) : bool + public function current() : \PHPUnit\TextUI\XmlConfiguration\File { - $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; + return $this->files[$this->position]; } - private static function isIniSettingDeprecated(string $iniSetting) : bool + public function next() : void { - return isset(self::DEPRECATED_INI_SETTINGS[PHP_MAJOR_VERSION . '.' . PHP_MINOR_VERSION][$iniSetting]); + $this->position++; } } + + + + {tests_directory} + + + + + + {src_directory} + + + + +EOT; + public function generateDefaultConfiguration(string $phpunitVersion, string $bootstrapScript, string $testsDirectory, string $srcDirectory, string $cacheDirectory) : string + { + return str_replace(['{phpunit_version}', '{bootstrap_script}', '{tests_directory}', '{src_directory}', '{cache_directory}'], [$phpunitVersion, $bootstrapScript, $testsDirectory, $srcDirectory, $cacheDirectory], self::TEMPLATE); + } } name = $name; } - /** - * 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 + public function name() : string { - 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); - } + return $this->name; } } */ -final class JUnit extends Printer implements TestListener +final class GroupCollection implements IteratorAggregate { /** - * @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 + * @var Group[] */ - private $currentTestCase; + private $groups; /** - * @param null|mixed $out + * @param Group[] $groups */ - public function __construct($out = null, bool $reportRiskyTests = \false) + public static function fromArray(array $groups) : self { - $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; + return new self(...$groups); } - /** - * Flush buffer and close output. - */ - public function flush() : void + private function __construct(\PHPUnit\TextUI\XmlConfiguration\Group ...$groups) { - $this->write($this->getXML()); - parent::flush(); + $this->groups = $groups; } /** - * An error occurred. + * @return Group[] */ - public function addError(Test $test, Throwable $t, float $time) : void + public function asArray() : array { - $this->doAddFault($test, $t, 'error'); - $this->testSuiteErrors[$this->testSuiteLevel]++; + return $this->groups; } /** - * A warning occurred. + * @return string[] */ - public function addWarning(Test $test, Warning $e, float $time) : void + public function asArrayOfStrings() : array { - $this->doAddFault($test, $e, 'warning'); - $this->testSuiteWarnings[$this->testSuiteLevel]++; + $result = []; + foreach ($this->groups as $group) { + $result[] = $group->name(); + } + return $result; } - /** - * A failure occurred. - */ - public function addFailure(Test $test, AssertionFailedError $e, float $time) : void - { - $this->doAddFault($test, $e, '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) { - return; - } - $this->doAddFault($test, $t, '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); - // @codeCoverageIgnoreStart - } catch (ReflectionException $e) { - throw new Exception($e->getMessage(), $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd - $methodName = $test->getName(!$usesDataprovider); - if ($class->hasMethod($methodName)) { - try { - $method = $class->getMethod($methodName); - // @codeCoverageIgnoreStart - } catch (ReflectionException $e) { - throw new Exception($e->getMessage(), $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd - $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, string $type) : void + public function isEmpty() : bool { - if ($this->currentTestCase === null) { - return; - } - if ($test instanceof SelfDescribing) { - $buffer = $test->toString() . "\n"; - } else { - $buffer = ''; - } - $buffer .= trim(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); + return empty($this->groups); } - private function doAddSkipped() : void + public function getIterator() : \PHPUnit\TextUI\XmlConfiguration\GroupCollectionIterator { - if ($this->currentTestCase === null) { - return; - } - $skipped = $this->document->createElement('skipped'); - $this->currentTestCase->appendChild($skipped); - $this->testSuiteSkipped[$this->testSuiteLevel]++; + return new \PHPUnit\TextUI\XmlConfiguration\GroupCollectionIterator($this); } } */ -final class TeamCity extends DefaultResultPrinter +final class GroupCollectionIterator implements Countable, Iterator { /** - * @var bool - */ - private $isSummaryTestCountPrinted = \false; - /** - * @var string - */ - private $startedTestName; - /** - * @var false|int - */ - private $flowId; - public function printResult(TestResult $result) : void - { - $this->printHeader($result); - $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. + * @var Group[] */ - public function addWarning(Test $test, Warning $e, float $time) : void - { - $this->write(self::getMessage($e) . \PHP_EOL); - } + private $groups; /** - * A failure occurred. + * @var int */ - public function addFailure(Test $test, AssertionFailedError $e, float $time) : void + private $position; + public function __construct(\PHPUnit\TextUI\XmlConfiguration\GroupCollection $groups) { - $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); + $this->groups = $groups->asArray(); } - /** - * Incomplete test. - */ - public function addIncompleteTest(Test $test, Throwable $t, float $time) : void + public function count() : int { - $this->printIgnoredTest($test->getName(), $t, $time); + return iterator_count($this); } - /** - * Risky test. - */ - public function addRiskyTest(Test $test, Throwable $t, float $time) : void + public function rewind() : void { - $this->addError($test, $t, $time); + $this->position = 0; } - /** - * Skipped test. - */ - public function addSkippedTest(Test $test, Throwable $t, float $time) : void + public function valid() : bool { - $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); - } + return $this->position < count($this->groups); } - public function printIgnoredTest(string $testName, Throwable $t, float $time) : void + public function key() : int { - $this->printEvent('testIgnored', ['name' => $testName, 'message' => self::getMessage($t), 'details' => self::getDetails($t), 'duration' => self::toMilliseconds($time)]); + return $this->position; } - /** - * A testsuite started. - */ - public function startTestSuite(TestSuite $suite) : void + public function current() : \PHPUnit\TextUI\XmlConfiguration\Group { - 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); + return $this->groups[$this->position]; } - /** - * A testsuite ended. - */ - public function endTestSuite(TestSuite $suite) : void + public function next() : 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); + $this->position++; } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration; + +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + * + * @psalm-immutable + */ +final class Groups +{ /** - * A test started. + * @var GroupCollection */ - 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); - } + private $include; /** - * A test ended. + * @var GroupCollection */ - 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 + private $exclude; + public function __construct(\PHPUnit\TextUI\XmlConfiguration\GroupCollection $include, \PHPUnit\TextUI\XmlConfiguration\GroupCollection $exclude) { - $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); + $this->include = $include; + $this->exclude = $exclude; } - private static function getPrimitiveValueAsString($value) : ?string + public function hasInclude() : bool { - if ($value === null) { - return 'null'; - } - if (is_bool($value)) { - return $value ? 'true' : 'false'; - } - if (is_scalar($value)) { - return print_r($value, \true); - } - return null; + return !$this->include->isEmpty(); } - private static function escapeValue(string $text) : string + public function include() : \PHPUnit\TextUI\XmlConfiguration\GroupCollection { - return str_replace(['|', "'", "\n", "\r", ']', '['], ['||', "|'", '|n', '|r', '|]', '|['], $text); + return $this->include; } - /** - * @param string $className - */ - private static function getFileName($className) : string + public function hasExclude() : bool { - try { - return (new ReflectionClass($className))->getFileName(); - // @codeCoverageIgnoreStart - } catch (ReflectionException $e) { - throw new Exception($e->getMessage(), $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd + return !$this->exclude->isEmpty(); } - /** - * @param float $time microseconds - */ - private static function toMilliseconds(float $time) : int + public function exclude() : \PHPUnit\TextUI\XmlConfiguration\GroupCollection { - return (int) round($time * 1000); + return $this->exclude; } } - */ - protected $env = []; - /** - * @var int + * @throws Exception */ - protected $timeout = 0; - public static function factory() : self + public function load(string $filename) : \PHPUnit\TextUI\XmlConfiguration\Configuration { - if (DIRECTORY_SEPARATOR === '\\') { - return new \PHPUnit\Util\PHP\WindowsPhpProcess(); + try { + $document = (new XmlLoader())->loadFile($filename, \false, \true, \true); + } catch (XmlException $e) { + throw new \PHPUnit\TextUI\XmlConfiguration\Exception($e->getMessage(), $e->getCode(), $e); } - return new \PHPUnit\Util\PHP\DefaultPhpProcess(); + $xpath = new DOMXPath($document); + try { + $xsdFilename = (new SchemaFinder())->find(Version::series()); + } catch (XmlException $e) { + throw new \PHPUnit\TextUI\XmlConfiguration\Exception($e->getMessage(), $e->getCode(), $e); + } + return new \PHPUnit\TextUI\XmlConfiguration\Configuration($filename, (new Validator())->validate($document, $xsdFilename), $this->extensions($filename, $xpath), $this->codeCoverage($filename, $xpath, $document), $this->groups($xpath), $this->testdoxGroups($xpath), $this->listeners($filename, $xpath), $this->logging($filename, $xpath), $this->php($filename, $xpath), $this->phpunit($filename, $document), $this->testSuite($filename, $xpath)); } - public function __construct() + public function logging(string $filename, DOMXPath $xpath) : Logging { - $this->runtime = new Runtime(); + if ($xpath->query('logging/log')->length !== 0) { + return $this->legacyLogging($filename, $xpath); + } + $junit = null; + $element = $this->element($xpath, 'logging/junit'); + if ($element) { + $junit = new Junit(new \PHPUnit\TextUI\XmlConfiguration\File($this->toAbsolutePath($filename, (string) $this->getStringAttribute($element, 'outputFile')))); + } + $text = null; + $element = $this->element($xpath, 'logging/text'); + if ($element) { + $text = new Text(new \PHPUnit\TextUI\XmlConfiguration\File($this->toAbsolutePath($filename, (string) $this->getStringAttribute($element, 'outputFile')))); + } + $teamCity = null; + $element = $this->element($xpath, 'logging/teamcity'); + if ($element) { + $teamCity = new TeamCity(new \PHPUnit\TextUI\XmlConfiguration\File($this->toAbsolutePath($filename, (string) $this->getStringAttribute($element, 'outputFile')))); + } + $testDoxHtml = null; + $element = $this->element($xpath, 'logging/testdoxHtml'); + if ($element) { + $testDoxHtml = new TestDoxHtml(new \PHPUnit\TextUI\XmlConfiguration\File($this->toAbsolutePath($filename, (string) $this->getStringAttribute($element, 'outputFile')))); + } + $testDoxText = null; + $element = $this->element($xpath, 'logging/testdoxText'); + if ($element) { + $testDoxText = new TestDoxText(new \PHPUnit\TextUI\XmlConfiguration\File($this->toAbsolutePath($filename, (string) $this->getStringAttribute($element, 'outputFile')))); + } + $testDoxXml = null; + $element = $this->element($xpath, 'logging/testdoxXml'); + if ($element) { + $testDoxXml = new TestDoxXml(new \PHPUnit\TextUI\XmlConfiguration\File($this->toAbsolutePath($filename, (string) $this->getStringAttribute($element, 'outputFile')))); + } + return new Logging($junit, $text, $teamCity, $testDoxHtml, $testDoxText, $testDoxXml); } - /** - * Defines if should use STDERR redirection or not. - * - * Then $stderrRedirection is TRUE, STDERR is redirected to STDOUT. - */ - public function setUseStderrRedirection(bool $stderrRedirection) : void + public function legacyLogging(string $filename, DOMXPath $xpath) : Logging { - $this->stderrRedirection = $stderrRedirection; + $junit = null; + $teamCity = null; + $testDoxHtml = null; + $testDoxText = null; + $testDoxXml = null; + $text = null; + foreach ($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($filename, $target); + switch ($type) { + case 'plain': + $text = new Text(new \PHPUnit\TextUI\XmlConfiguration\File($target)); + break; + case 'junit': + $junit = new Junit(new \PHPUnit\TextUI\XmlConfiguration\File($target)); + break; + case 'teamcity': + $teamCity = new TeamCity(new \PHPUnit\TextUI\XmlConfiguration\File($target)); + break; + case 'testdox-html': + $testDoxHtml = new TestDoxHtml(new \PHPUnit\TextUI\XmlConfiguration\File($target)); + break; + case 'testdox-text': + $testDoxText = new TestDoxText(new \PHPUnit\TextUI\XmlConfiguration\File($target)); + break; + case 'testdox-xml': + $testDoxXml = new TestDoxXml(new \PHPUnit\TextUI\XmlConfiguration\File($target)); + break; + } + } + return new Logging($junit, $text, $teamCity, $testDoxHtml, $testDoxText, $testDoxXml); } - /** - * Returns TRUE if uses STDERR redirection or FALSE if not. - */ - public function useStderrRedirection() : bool + private function extensions(string $filename, DOMXPath $xpath) : \PHPUnit\TextUI\XmlConfiguration\ExtensionCollection { - return $this->stderrRedirection; + $extensions = []; + foreach ($xpath->query('extensions/extension') as $extension) { + assert($extension instanceof DOMElement); + $extensions[] = $this->getElementConfigurationParameters($filename, $extension); + } + return \PHPUnit\TextUI\XmlConfiguration\ExtensionCollection::fromArray($extensions); } - /** - * Sets the input string to be sent via STDIN. - */ - public function setStdin(string $stdin) : void + private function getElementConfigurationParameters(string $filename, DOMElement $element) : \PHPUnit\TextUI\XmlConfiguration\Extension { - $this->stdin = $stdin; + /** @psalm-var class-string $class */ + $class = (string) $element->getAttribute('class'); + $file = ''; + $arguments = $this->getConfigurationArguments($filename, $element->childNodes); + if ($element->getAttribute('file')) { + $file = $this->toAbsolutePath($filename, (string) $element->getAttribute('file'), \true); + } + return new \PHPUnit\TextUI\XmlConfiguration\Extension($class, $file, $arguments); } - /** - * Returns the input string to be sent via STDIN. - */ - public function getStdin() : string + private function toAbsolutePath(string $filename, string $path, bool $useIncludePath = \false) : string { - return $this->stdin; + $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($filename) . DIRECTORY_SEPARATOR . $path; + if ($useIncludePath && !is_file($file)) { + $includePathFile = stream_resolve_include_path($path); + if ($includePathFile) { + $file = $includePathFile; + } + } + return $file; } - /** - * Sets the string of arguments to pass to the php job. - */ - public function setArgs(string $args) : void + private function getConfigurationArguments(string $filename, DOMNodeList $nodes) : array { - $this->args = $args; + $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($filename, (string) $argument->textContent); + } else { + $arguments[] = Xml::xmlToVariable($argument); + } + } + } + return $arguments; + } + private function codeCoverage(string $filename, DOMXPath $xpath, DOMDocument $document) : CodeCoverage + { + if ($xpath->query('filter/whitelist')->length !== 0) { + return $this->legacyCodeCoverage($filename, $xpath, $document); + } + $cacheDirectory = null; + $pathCoverage = \false; + $includeUncoveredFiles = \true; + $processUncoveredFiles = \false; + $ignoreDeprecatedCodeUnits = \false; + $disableCodeCoverageIgnore = \false; + $element = $this->element($xpath, 'coverage'); + if ($element) { + $cacheDirectory = $this->getStringAttribute($element, 'cacheDirectory'); + if ($cacheDirectory !== null) { + $cacheDirectory = new \PHPUnit\TextUI\XmlConfiguration\Directory($this->toAbsolutePath($filename, $cacheDirectory)); + } + $pathCoverage = $this->getBooleanAttribute($element, 'pathCoverage', \false); + $includeUncoveredFiles = $this->getBooleanAttribute($element, 'includeUncoveredFiles', \true); + $processUncoveredFiles = $this->getBooleanAttribute($element, 'processUncoveredFiles', \false); + $ignoreDeprecatedCodeUnits = $this->getBooleanAttribute($element, 'ignoreDeprecatedCodeUnits', \false); + $disableCodeCoverageIgnore = $this->getBooleanAttribute($element, 'disableCodeCoverageIgnore', \false); + } + $clover = null; + $element = $this->element($xpath, 'coverage/report/clover'); + if ($element) { + $clover = new Clover(new \PHPUnit\TextUI\XmlConfiguration\File($this->toAbsolutePath($filename, (string) $this->getStringAttribute($element, 'outputFile')))); + } + $cobertura = null; + $element = $this->element($xpath, 'coverage/report/cobertura'); + if ($element) { + $cobertura = new Cobertura(new \PHPUnit\TextUI\XmlConfiguration\File($this->toAbsolutePath($filename, (string) $this->getStringAttribute($element, 'outputFile')))); + } + $crap4j = null; + $element = $this->element($xpath, 'coverage/report/crap4j'); + if ($element) { + $crap4j = new Crap4j(new \PHPUnit\TextUI\XmlConfiguration\File($this->toAbsolutePath($filename, (string) $this->getStringAttribute($element, 'outputFile'))), $this->getIntegerAttribute($element, 'threshold', 30)); + } + $html = null; + $element = $this->element($xpath, 'coverage/report/html'); + if ($element) { + $html = new CodeCoverageHtml(new \PHPUnit\TextUI\XmlConfiguration\Directory($this->toAbsolutePath($filename, (string) $this->getStringAttribute($element, 'outputDirectory'))), $this->getIntegerAttribute($element, 'lowUpperBound', 50), $this->getIntegerAttribute($element, 'highLowerBound', 90)); + } + $php = null; + $element = $this->element($xpath, 'coverage/report/php'); + if ($element) { + $php = new CodeCoveragePhp(new \PHPUnit\TextUI\XmlConfiguration\File($this->toAbsolutePath($filename, (string) $this->getStringAttribute($element, 'outputFile')))); + } + $text = null; + $element = $this->element($xpath, 'coverage/report/text'); + if ($element) { + $text = new CodeCoverageText(new \PHPUnit\TextUI\XmlConfiguration\File($this->toAbsolutePath($filename, (string) $this->getStringAttribute($element, 'outputFile'))), $this->getBooleanAttribute($element, 'showUncoveredFiles', \false), $this->getBooleanAttribute($element, 'showOnlySummary', \false)); + } + $xml = null; + $element = $this->element($xpath, 'coverage/report/xml'); + if ($element) { + $xml = new CodeCoverageXml(new \PHPUnit\TextUI\XmlConfiguration\Directory($this->toAbsolutePath($filename, (string) $this->getStringAttribute($element, 'outputDirectory')))); + } + return new CodeCoverage($cacheDirectory, $this->readFilterDirectories($filename, $xpath, 'coverage/include/directory'), $this->readFilterFiles($filename, $xpath, 'coverage/include/file'), $this->readFilterDirectories($filename, $xpath, 'coverage/exclude/directory'), $this->readFilterFiles($filename, $xpath, 'coverage/exclude/file'), $pathCoverage, $includeUncoveredFiles, $processUncoveredFiles, $ignoreDeprecatedCodeUnits, $disableCodeCoverageIgnore, $clover, $cobertura, $crap4j, $html, $php, $text, $xml); } /** - * Returns the string of arguments to pass to the php job. + * @deprecated */ - public function getArgs() : string + private function legacyCodeCoverage(string $filename, DOMXPath $xpath, DOMDocument $document) : CodeCoverage { - return $this->args; + $ignoreDeprecatedCodeUnits = $this->getBooleanAttribute($document->documentElement, 'ignoreDeprecatedCodeUnitsFromCodeCoverage', \false); + $disableCodeCoverageIgnore = $this->getBooleanAttribute($document->documentElement, 'disableCodeCoverageIgnore', \false); + $includeUncoveredFiles = \true; + $processUncoveredFiles = \false; + $element = $this->element($xpath, 'filter/whitelist'); + if ($element) { + if ($element->hasAttribute('addUncoveredFilesFromWhitelist')) { + $includeUncoveredFiles = (bool) $this->getBoolean((string) $element->getAttribute('addUncoveredFilesFromWhitelist'), \true); + } + if ($element->hasAttribute('processUncoveredFilesFromWhitelist')) { + $processUncoveredFiles = (bool) $this->getBoolean((string) $element->getAttribute('processUncoveredFilesFromWhitelist'), \false); + } + } + $clover = null; + $cobertura = null; + $crap4j = null; + $html = null; + $php = null; + $text = null; + $xml = null; + foreach ($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($filename, $target); + switch ($type) { + case 'coverage-clover': + $clover = new Clover(new \PHPUnit\TextUI\XmlConfiguration\File($target)); + break; + case 'coverage-cobertura': + $cobertura = new Cobertura(new \PHPUnit\TextUI\XmlConfiguration\File($target)); + break; + case 'coverage-crap4j': + $crap4j = new Crap4j(new \PHPUnit\TextUI\XmlConfiguration\File($target), $this->getIntegerAttribute($log, 'threshold', 30)); + break; + case 'coverage-html': + $html = new CodeCoverageHtml(new \PHPUnit\TextUI\XmlConfiguration\Directory($target), $this->getIntegerAttribute($log, 'lowUpperBound', 50), $this->getIntegerAttribute($log, 'highLowerBound', 90)); + break; + case 'coverage-php': + $php = new CodeCoveragePhp(new \PHPUnit\TextUI\XmlConfiguration\File($target)); + break; + case 'coverage-text': + $text = new CodeCoverageText(new \PHPUnit\TextUI\XmlConfiguration\File($target), $this->getBooleanAttribute($log, 'showUncoveredFiles', \false), $this->getBooleanAttribute($log, 'showOnlySummary', \false)); + break; + case 'coverage-xml': + $xml = new CodeCoverageXml(new \PHPUnit\TextUI\XmlConfiguration\Directory($target)); + break; + } + } + return new CodeCoverage(null, $this->readFilterDirectories($filename, $xpath, 'filter/whitelist/directory'), $this->readFilterFiles($filename, $xpath, 'filter/whitelist/file'), $this->readFilterDirectories($filename, $xpath, 'filter/whitelist/exclude/directory'), $this->readFilterFiles($filename, $xpath, 'filter/whitelist/exclude/file'), \false, $includeUncoveredFiles, $processUncoveredFiles, $ignoreDeprecatedCodeUnits, $disableCodeCoverageIgnore, $clover, $cobertura, $crap4j, $html, $php, $text, $xml); } /** - * Sets the array of environment variables to start the child process with. + * If $value is 'false' or 'true', this returns the value that $value represents. + * Otherwise, returns $default, which may be a string in rare cases. * - * @param array $env + * @see \PHPUnit\TextUI\XmlConfigurationTest::testPHPConfigurationIsReadCorrectly + * + * @param bool|string $default + * + * @return bool|string */ - public function setEnv(array $env) : void + private function getBoolean(string $value, $default) { - $this->env = $env; + if (strtolower($value) === 'false') { + return \false; + } + if (strtolower($value) === 'true') { + return \true; + } + return $default; } - /** - * Returns the array of environment variables to start the child process with. - */ - public function getEnv() : array + private function readFilterDirectories(string $filename, DOMXPath $xpath, string $query) : FilterDirectoryCollection { - return $this->env; + $directories = []; + foreach ($xpath->query($query) as $directoryNode) { + assert($directoryNode instanceof DOMElement); + $directoryPath = (string) $directoryNode->textContent; + if (!$directoryPath) { + continue; + } + $directories[] = new FilterDirectory($this->toAbsolutePath($filename, $directoryPath), $directoryNode->hasAttribute('prefix') ? (string) $directoryNode->getAttribute('prefix') : '', $directoryNode->hasAttribute('suffix') ? (string) $directoryNode->getAttribute('suffix') : '.php', $directoryNode->hasAttribute('group') ? (string) $directoryNode->getAttribute('group') : 'DEFAULT'); + } + return FilterDirectoryCollection::fromArray($directories); } - /** - * Sets the amount of seconds to wait before timing out. - */ - public function setTimeout(int $timeout) : void + private function readFilterFiles(string $filename, DOMXPath $xpath, string $query) : \PHPUnit\TextUI\XmlConfiguration\FileCollection { - $this->timeout = $timeout; + $files = []; + foreach ($xpath->query($query) as $file) { + assert($file instanceof DOMNode); + $filePath = (string) $file->textContent; + if ($filePath) { + $files[] = new \PHPUnit\TextUI\XmlConfiguration\File($this->toAbsolutePath($filename, $filePath)); + } + } + return \PHPUnit\TextUI\XmlConfiguration\FileCollection::fromArray($files); } - /** - * Returns the amount of seconds to wait before timing out. - */ - public function getTimeout() : int + private function groups(DOMXPath $xpath) : \PHPUnit\TextUI\XmlConfiguration\Groups { - return $this->timeout; + return $this->parseGroupConfiguration($xpath, 'groups'); } - /** - * Runs a single test in a separate PHP process. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public function runTestJob(string $job, Test $test, TestResult $result, string $processResultFile) : void + private function testdoxGroups(DOMXPath $xpath) : \PHPUnit\TextUI\XmlConfiguration\Groups { - $result->startTest($test); - $processResult = ''; - $_result = $this->runJob($job); - if (file_exists($processResultFile)) { - $processResult = file_get_contents($processResultFile); - @unlink($processResultFile); - } - $this->processChildResult($test, $result, $processResult, $_result['stderr']); + return $this->parseGroupConfiguration($xpath, 'testdoxGroups'); } - /** - * Returns the command based into the configurations. - */ - public function getCommand(array $settings, string $file = null) : string + private function parseGroupConfiguration(DOMXPath $xpath, string $root) : \PHPUnit\TextUI\XmlConfiguration\Groups { - $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')))); + $include = []; + $exclude = []; + foreach ($xpath->query($root . '/include/group') as $group) { + assert($group instanceof DOMNode); + $include[] = new \PHPUnit\TextUI\XmlConfiguration\Group((string) $group->textContent); } - $command .= $this->settingsToParameters($settings); - if (PHP_SAPI === 'phpdbg') { - $command .= ' -qrr'; - if (!$file) { - $command .= 's='; - } + foreach ($xpath->query($root . '/exclude/group') as $group) { + assert($group instanceof DOMNode); + $exclude[] = new \PHPUnit\TextUI\XmlConfiguration\Group((string) $group->textContent); } - if ($file) { - $command .= ' ' . escapeshellarg($file); + return new \PHPUnit\TextUI\XmlConfiguration\Groups(\PHPUnit\TextUI\XmlConfiguration\GroupCollection::fromArray($include), \PHPUnit\TextUI\XmlConfiguration\GroupCollection::fromArray($exclude)); + } + private function listeners(string $filename, DOMXPath $xpath) : \PHPUnit\TextUI\XmlConfiguration\ExtensionCollection + { + $listeners = []; + foreach ($xpath->query('listeners/listener') as $listener) { + assert($listener instanceof DOMElement); + $listeners[] = $this->getElementConfigurationParameters($filename, $listener); } - if ($this->args) { - if (!$file) { - $command .= ' --'; - } - $command .= ' ' . $this->args; + return \PHPUnit\TextUI\XmlConfiguration\ExtensionCollection::fromArray($listeners); + } + private function getBooleanAttribute(DOMElement $element, string $attribute, bool $default) : bool + { + if (!$element->hasAttribute($attribute)) { + return $default; } - if ($this->stderrRedirection) { - $command .= ' 2>&1'; + return (bool) $this->getBoolean((string) $element->getAttribute($attribute), \false); + } + private function getIntegerAttribute(DOMElement $element, string $attribute, int $default) : int + { + if (!$element->hasAttribute($attribute)) { + return $default; } - return $command; + return $this->getInteger((string) $element->getAttribute($attribute), $default); } - /** - * Runs a single job (PHP code) using a separate PHP process. - */ - public abstract function runJob(string $job, array $settings = []) : array; - protected function settingsToParameters(array $settings) : string + private function getStringAttribute(DOMElement $element, string $attribute) : ?string { - $buffer = ''; - foreach ($settings as $setting) { - $buffer .= ' -d ' . escapeshellarg($setting); + if (!$element->hasAttribute($attribute)) { + return null; } - return $buffer; + return (string) $element->getAttribute($attribute); } - /** - * Processes the TestResult object from an isolated process. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - private function processChildResult(Test $test, TestResult $result, string $stdout, string $stderr) : void + private function getInteger(string $value, int $default) : int { - $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); + if (is_numeric($value)) { + return (int) $value; + } + return $default; + } + private function php(string $filename, DOMXPath $xpath) : \PHPUnit\TextUI\XmlConfiguration\Php + { + $includePaths = []; + foreach ($xpath->query('php/includePath') as $includePath) { + assert($includePath instanceof DOMNode); + $path = (string) $includePath->textContent; + if ($path) { + $includePaths[] = new \PHPUnit\TextUI\XmlConfiguration\Directory($this->toAbsolutePath($filename, $path)); + } + } + $iniSettings = []; + foreach ($xpath->query('php/ini') as $ini) { + assert($ini instanceof DOMElement); + $iniSettings[] = new \PHPUnit\TextUI\XmlConfiguration\IniSetting((string) $ini->getAttribute('name'), (string) $ini->getAttribute('value')); + } + $constants = []; + foreach ($xpath->query('php/const') as $const) { + assert($const instanceof DOMElement); + $value = (string) $const->getAttribute('value'); + $constants[] = new \PHPUnit\TextUI\XmlConfiguration\Constant((string) $const->getAttribute('name'), $this->getBoolean($value, $value)); + } + $variables = ['var' => [], 'env' => [], 'post' => [], 'get' => [], 'cookie' => [], 'server' => [], 'files' => [], 'request' => []]; + foreach (['var', 'env', 'post', 'get', 'cookie', 'server', 'files', 'request'] as $array) { + foreach ($xpath->query('php/' . $array) as $var) { + assert($var instanceof DOMElement); + $name = (string) $var->getAttribute('name'); + $value = (string) $var->getAttribute('value'); + $force = \false; + $verbatim = \false; + if ($var->hasAttribute('force')) { + $force = (bool) $this->getBoolean($var->getAttribute('force'), \false); } - ); - try { - if (strpos($stdout, "#!/usr/bin/env php\n") === 0) { - $stdout = substr($stdout, 19); + if ($var->hasAttribute('verbatim')) { + $verbatim = $this->getBoolean($var->getAttribute('verbatim'), \false); } - $childResult = unserialize(str_replace("#!/usr/bin/env php\n", '', $stdout)); - restore_error_handler(); - if ($childResult === \false) { - $result->addFailure($test, new AssertionFailedError('Test was run in child process and ended unexpectedly'), $time); + if (!$verbatim) { + $value = $this->getBoolean($value, $value); } - } catch (ErrorException $e) { - restore_error_handler(); - $childResult = \false; - $result->addError($test, new Exception(trim($stdout), 0, $e), $time); + $variables[$array][] = new \PHPUnit\TextUI\XmlConfiguration\Variable($name, $value, $force); } - if ($childResult !== \false) { - if (!empty($childResult['output'])) { - $output = $childResult['output']; + } + return new \PHPUnit\TextUI\XmlConfiguration\Php(\PHPUnit\TextUI\XmlConfiguration\DirectoryCollection::fromArray($includePaths), \PHPUnit\TextUI\XmlConfiguration\IniSettingCollection::fromArray($iniSettings), \PHPUnit\TextUI\XmlConfiguration\ConstantCollection::fromArray($constants), \PHPUnit\TextUI\XmlConfiguration\VariableCollection::fromArray($variables['var']), \PHPUnit\TextUI\XmlConfiguration\VariableCollection::fromArray($variables['env']), \PHPUnit\TextUI\XmlConfiguration\VariableCollection::fromArray($variables['post']), \PHPUnit\TextUI\XmlConfiguration\VariableCollection::fromArray($variables['get']), \PHPUnit\TextUI\XmlConfiguration\VariableCollection::fromArray($variables['cookie']), \PHPUnit\TextUI\XmlConfiguration\VariableCollection::fromArray($variables['server']), \PHPUnit\TextUI\XmlConfiguration\VariableCollection::fromArray($variables['files']), \PHPUnit\TextUI\XmlConfiguration\VariableCollection::fromArray($variables['request'])); + } + private function phpunit(string $filename, DOMDocument $document) : \PHPUnit\TextUI\XmlConfiguration\PHPUnit + { + $executionOrder = TestSuiteSorter::ORDER_DEFAULT; + $defectsFirst = \false; + $resolveDependencies = $this->getBooleanAttribute($document->documentElement, 'resolveDependencies', \true); + if ($document->documentElement->hasAttribute('executionOrder')) { + foreach (explode(',', $document->documentElement->getAttribute('executionOrder')) as $order) { + switch ($order) { + case 'default': + $executionOrder = TestSuiteSorter::ORDER_DEFAULT; + $defectsFirst = \false; + $resolveDependencies = \true; + break; + case 'depends': + $resolveDependencies = \true; + break; + case 'no-depends': + $resolveDependencies = \false; + break; + case 'defects': + $defectsFirst = \true; + break; + case 'duration': + $executionOrder = TestSuiteSorter::ORDER_DURATION; + break; + case 'random': + $executionOrder = TestSuiteSorter::ORDER_RANDOMIZED; + break; + case 'reverse': + $executionOrder = TestSuiteSorter::ORDER_REVERSED; + break; + case 'size': + $executionOrder = TestSuiteSorter::ORDER_SIZE; + break; } - /* @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()); + } + } + $printerClass = $this->getStringAttribute($document->documentElement, 'printerClass'); + $testdox = $this->getBooleanAttribute($document->documentElement, 'testdox', \false); + $conflictBetweenPrinterClassAndTestdox = \false; + if ($testdox) { + if ($printerClass !== null) { + $conflictBetweenPrinterClassAndTestdox = \true; + } + $printerClass = CliTestDoxPrinter::class; + } + $cacheResultFile = $this->getStringAttribute($document->documentElement, 'cacheResultFile'); + if ($cacheResultFile !== null) { + $cacheResultFile = $this->toAbsolutePath($filename, $cacheResultFile); + } + $bootstrap = $this->getStringAttribute($document->documentElement, 'bootstrap'); + if ($bootstrap !== null) { + $bootstrap = $this->toAbsolutePath($filename, $bootstrap); + } + $extensionsDirectory = $this->getStringAttribute($document->documentElement, 'extensionsDirectory'); + if ($extensionsDirectory !== null) { + $extensionsDirectory = $this->toAbsolutePath($filename, $extensionsDirectory); + } + $testSuiteLoaderFile = $this->getStringAttribute($document->documentElement, 'testSuiteLoaderFile'); + if ($testSuiteLoaderFile !== null) { + $testSuiteLoaderFile = $this->toAbsolutePath($filename, $testSuiteLoaderFile); + } + $printerFile = $this->getStringAttribute($document->documentElement, 'printerFile'); + if ($printerFile !== null) { + $printerFile = $this->toAbsolutePath($filename, $printerFile); + } + return new \PHPUnit\TextUI\XmlConfiguration\PHPUnit($this->getBooleanAttribute($document->documentElement, 'cacheResult', \true), $cacheResultFile, $this->getColumns($document), $this->getColors($document), $this->getBooleanAttribute($document->documentElement, 'stderr', \false), $this->getBooleanAttribute($document->documentElement, 'noInteraction', \false), $this->getBooleanAttribute($document->documentElement, 'verbose', \false), $this->getBooleanAttribute($document->documentElement, 'reverseDefectList', \false), $this->getBooleanAttribute($document->documentElement, 'convertDeprecationsToExceptions', \false), $this->getBooleanAttribute($document->documentElement, 'convertErrorsToExceptions', \true), $this->getBooleanAttribute($document->documentElement, 'convertNoticesToExceptions', \true), $this->getBooleanAttribute($document->documentElement, 'convertWarningsToExceptions', \true), $this->getBooleanAttribute($document->documentElement, 'forceCoversAnnotation', \false), $bootstrap, $this->getBooleanAttribute($document->documentElement, 'processIsolation', \false), $this->getBooleanAttribute($document->documentElement, 'failOnEmptyTestSuite', \false), $this->getBooleanAttribute($document->documentElement, 'failOnIncomplete', \false), $this->getBooleanAttribute($document->documentElement, 'failOnRisky', \false), $this->getBooleanAttribute($document->documentElement, 'failOnSkipped', \false), $this->getBooleanAttribute($document->documentElement, 'failOnWarning', \false), $this->getBooleanAttribute($document->documentElement, 'stopOnDefect', \false), $this->getBooleanAttribute($document->documentElement, 'stopOnError', \false), $this->getBooleanAttribute($document->documentElement, 'stopOnFailure', \false), $this->getBooleanAttribute($document->documentElement, 'stopOnWarning', \false), $this->getBooleanAttribute($document->documentElement, 'stopOnIncomplete', \false), $this->getBooleanAttribute($document->documentElement, 'stopOnRisky', \false), $this->getBooleanAttribute($document->documentElement, 'stopOnSkipped', \false), $extensionsDirectory, $this->getStringAttribute($document->documentElement, 'testSuiteLoaderClass'), $testSuiteLoaderFile, $printerClass, $printerFile, $this->getBooleanAttribute($document->documentElement, 'beStrictAboutChangesToGlobalState', \false), $this->getBooleanAttribute($document->documentElement, 'beStrictAboutOutputDuringTests', \false), $this->getBooleanAttribute($document->documentElement, 'beStrictAboutResourceUsageDuringSmallTests', \false), $this->getBooleanAttribute($document->documentElement, 'beStrictAboutTestsThatDoNotTestAnything', \true), $this->getBooleanAttribute($document->documentElement, 'beStrictAboutTodoAnnotatedTests', \false), $this->getBooleanAttribute($document->documentElement, 'beStrictAboutCoversAnnotation', \false), $this->getBooleanAttribute($document->documentElement, 'enforceTimeLimit', \false), $this->getIntegerAttribute($document->documentElement, 'defaultTimeLimit', 1), $this->getIntegerAttribute($document->documentElement, 'timeoutForSmallTests', 1), $this->getIntegerAttribute($document->documentElement, 'timeoutForMediumTests', 10), $this->getIntegerAttribute($document->documentElement, 'timeoutForLargeTests', 60), $this->getStringAttribute($document->documentElement, 'defaultTestSuite'), $executionOrder, $resolveDependencies, $defectsFirst, $this->getBooleanAttribute($document->documentElement, 'backupGlobals', \false), $this->getBooleanAttribute($document->documentElement, 'backupStaticAttributes', \false), $this->getBooleanAttribute($document->documentElement, 'registerMockObjectsFromTestArgumentsRecursively', \false), $conflictBetweenPrinterClassAndTestdox); + } + private function getColors(DOMDocument $document) : string + { + $colors = DefaultResultPrinter::COLOR_DEFAULT; + if ($document->documentElement->hasAttribute('colors')) { + /* only allow boolean for compatibility with previous versions + 'always' only allowed from command line */ + if ($this->getBoolean($document->documentElement->getAttribute('colors'), \false)) { + $colors = DefaultResultPrinter::COLOR_AUTO; + } else { + $colors = DefaultResultPrinter::COLOR_NEVER; + } + } + return $colors; + } + /** + * @return int|string + */ + private function getColumns(DOMDocument $document) + { + $columns = 80; + if ($document->documentElement->hasAttribute('columns')) { + $columns = (string) $document->documentElement->getAttribute('columns'); + if ($columns !== 'max') { + $columns = $this->getInteger($columns, 80); + } + } + return $columns; + } + private function testSuite(string $filename, DOMXPath $xpath) : \PHPUnit\TextUI\XmlConfiguration\TestSuiteCollection + { + $testSuites = []; + foreach ($this->getTestSuiteElements($xpath) as $element) { + $exclude = []; + foreach ($element->getElementsByTagName('exclude') as $excludeNode) { + $excludeFile = (string) $excludeNode->textContent; + if ($excludeFile) { + $exclude[] = new \PHPUnit\TextUI\XmlConfiguration\File($this->toAbsolutePath($filename, $excludeFile)); } - $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); + } + $directories = []; + foreach ($element->getElementsByTagName('directory') as $directoryNode) { + assert($directoryNode instanceof DOMElement); + $directory = (string) $directoryNode->textContent; + if (empty($directory)) { + continue; + } + $prefix = ''; + if ($directoryNode->hasAttribute('prefix')) { + $prefix = (string) $directoryNode->getAttribute('prefix'); + } + $suffix = 'Test.php'; + if ($directoryNode->hasAttribute('suffix')) { + $suffix = (string) $directoryNode->getAttribute('suffix'); } + $phpVersion = PHP_VERSION; + if ($directoryNode->hasAttribute('phpVersion')) { + $phpVersion = (string) $directoryNode->getAttribute('phpVersion'); + } + $phpVersionOperator = new VersionComparisonOperator('>='); + if ($directoryNode->hasAttribute('phpVersionOperator')) { + $phpVersionOperator = new VersionComparisonOperator((string) $directoryNode->getAttribute('phpVersionOperator')); + } + $directories[] = new \PHPUnit\TextUI\XmlConfiguration\TestDirectory($this->toAbsolutePath($filename, $directory), $prefix, $suffix, $phpVersion, $phpVersionOperator); } + $files = []; + foreach ($element->getElementsByTagName('file') as $fileNode) { + assert($fileNode instanceof DOMElement); + $file = (string) $fileNode->textContent; + if (empty($file)) { + continue; + } + $phpVersion = PHP_VERSION; + if ($fileNode->hasAttribute('phpVersion')) { + $phpVersion = (string) $fileNode->getAttribute('phpVersion'); + } + $phpVersionOperator = new VersionComparisonOperator('>='); + if ($fileNode->hasAttribute('phpVersionOperator')) { + $phpVersionOperator = new VersionComparisonOperator((string) $fileNode->getAttribute('phpVersionOperator')); + } + $files[] = new \PHPUnit\TextUI\XmlConfiguration\TestFile($this->toAbsolutePath($filename, $file), $phpVersion, $phpVersionOperator); + } + $testSuites[] = new TestSuiteConfiguration((string) $element->getAttribute('name'), \PHPUnit\TextUI\XmlConfiguration\TestDirectoryCollection::fromArray($directories), \PHPUnit\TextUI\XmlConfiguration\TestFileCollection::fromArray($files), \PHPUnit\TextUI\XmlConfiguration\FileCollection::fromArray($exclude)); } - $result->endTest($test, $time); - if (!empty($output)) { - print $output; - } + return \PHPUnit\TextUI\XmlConfiguration\TestSuiteCollection::fromArray($testSuites); } /** - * Gets the thrown exception from a PHPUnit\Framework\TestFailure. - * - * @see https://github.com/sebastianbergmann/phpunit/issues/74 + * @return DOMElement[] */ - private function getException(TestFailure $error) : Exception + private function getTestSuiteElements(DOMXPath $xpath) : array { - $exception = $error->thrownException(); - if ($exception instanceof __PHP_Incomplete_Class) { - $exceptionArray = []; - foreach ((array) $exception as $key => $value) { - $key = substr($key, strrpos($key, "\x00") + 1); - $exceptionArray[$key] = $value; + /** @var DOMElement[] $elements */ + $elements = []; + $testSuiteNodes = $xpath->query('testsuites/testsuite'); + if ($testSuiteNodes->length === 0) { + $testSuiteNodes = $xpath->query('testsuite'); + } + if ($testSuiteNodes->length === 1) { + $element = $testSuiteNodes->item(0); + assert($element instanceof DOMElement); + $elements[] = $element; + } else { + foreach ($testSuiteNodes as $testSuiteNode) { + assert($testSuiteNode instanceof DOMElement); + $elements[] = $testSuiteNode; } - $exception = new SyntheticError(sprintf('%s: %s', $exceptionArray['_PHP_Incomplete_Class_Name'], $exceptionArray['message']), $exceptionArray['code'], $exceptionArray['file'], $exceptionArray['line'], $exceptionArray['trace']); } - return $exception; + return $elements; + } + private function element(DOMXPath $xpath, string $element) : ?DOMElement + { + $nodes = $xpath->query($element); + if ($nodes->length === 1) { + $node = $nodes->item(0); + assert($node instanceof DOMElement); + return $node; + } + return null; } } 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); + $this->target = $target; } - /** - * Returns an array of file handles to be used in place of pipes. - */ - protected function getHandles() : array + public function target() : File { - return []; + return $this->target; } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration\Logging; + +use PHPUnit\TextUI\XmlConfiguration\Exception; +use PHPUnit\TextUI\XmlConfiguration\Logging\TestDox\Html as TestDoxHtml; +use PHPUnit\TextUI\XmlConfiguration\Logging\TestDox\Text as TestDoxText; +use PHPUnit\TextUI\XmlConfiguration\Logging\TestDox\Xml as TestDoxXml; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + * + * @psalm-immutable + */ +final class Logging +{ /** - * Handles creating the child process and returning the STDOUT and STDERR. - * - * @throws Exception + * @var ?Junit */ - protected function runProcess(string $job, array $settings) : array + private $junit; + /** + * @var ?Text + */ + private $text; + /** + * @var ?TeamCity + */ + private $teamCity; + /** + * @var ?TestDoxHtml + */ + private $testDoxHtml; + /** + * @var ?TestDoxText + */ + private $testDoxText; + /** + * @var ?TestDoxXml + */ + private $testDoxXml; + public function __construct(?\PHPUnit\TextUI\XmlConfiguration\Logging\Junit $junit, ?\PHPUnit\TextUI\XmlConfiguration\Logging\Text $text, ?\PHPUnit\TextUI\XmlConfiguration\Logging\TeamCity $teamCity, ?TestDoxHtml $testDoxHtml, ?TestDoxText $testDoxText, ?TestDoxXml $testDoxXml) { - $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 === '' || $line === \false) { - 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]); + $this->junit = $junit; + $this->text = $text; + $this->teamCity = $teamCity; + $this->testDoxHtml = $testDoxHtml; + $this->testDoxText = $testDoxText; + $this->testDoxXml = $testDoxXml; + } + public function hasJunit() : bool + { + return $this->junit !== null; + } + public function junit() : \PHPUnit\TextUI\XmlConfiguration\Logging\Junit + { + if ($this->junit === null) { + throw new Exception('Logger "JUnit XML" is not configured'); } - if (isset($handles[2])) { - rewind($handles[2]); - $stderr = stream_get_contents($handles[2]); - fclose($handles[2]); + return $this->junit; + } + public function hasText() : bool + { + return $this->text !== null; + } + public function text() : \PHPUnit\TextUI\XmlConfiguration\Logging\Text + { + if ($this->text === null) { + throw new Exception('Logger "Text" is not configured'); } - proc_close($process); - $this->cleanup(); - return ['stdout' => $stdout, 'stderr' => $stderr]; + return $this->text; } - /** - * @param resource $pipe - */ - protected function process($pipe, string $job) : void + public function hasTeamCity() : bool { - fwrite($pipe, $job); + return $this->teamCity !== null; } - protected function cleanup() : void + public function teamCity() : \PHPUnit\TextUI\XmlConfiguration\Logging\TeamCity { - if ($this->tempFile) { - unlink($this->tempFile); + if ($this->teamCity === null) { + throw new Exception('Logger "Team City" is not configured'); } + return $this->teamCity; } - protected function useTemporaryFile() : bool + public function hasTestDoxHtml() : bool { - return \false; + return $this->testDoxHtml !== null; } -} -{driverMethod}($filter), - $filter - ); - - if ({codeCoverageCacheDirectory}) { - $coverage->cacheStaticAnalysis({codeCoverageCacheDirectory}); - } - - $coverage->start(__FILE__); -} - -register_shutdown_function( - function() use ($coverage) { - $output = null; - - if ($coverage) { - $output = $coverage->stop(); + public function testDoxHtml() : TestDoxHtml + { + if ($this->testDoxHtml === null) { + throw new Exception('Logger "TestDox HTML" is not configured'); } - - file_put_contents('{coverageFile}', serialize($output)); + return $this->testDoxHtml; } -); - -ob_end_clean(); - -require '{job}'; -testDoxText !== null; } - - $result = new PHPUnit\Framework\TestResult; - - if ({collectCodeCoverageInformation}) { - $filter = unserialize('{codeCoverageFilter}'); - - $codeCoverage = new CodeCoverage( - (new Selector)->{driverMethod}($filter), - $filter - ); - - if ({cachesStaticAnalysis}) { - $codeCoverage->cacheStaticAnalysis(unserialize('{codeCoverageCacheDirectory}')); + public function testDoxText() : TestDoxText + { + if ($this->testDoxText === null) { + throw new Exception('Logger "TestDox Text" is not configured'); } - - $result->setCodeCoverage($codeCoverage); + return $this->testDoxText; } - - $result->beStrictAboutTestsThatDoNotTestAnything({isStrictAboutTestsThatDoNotTestAnything}); - $result->beStrictAboutOutputDuringTests({isStrictAboutOutputDuringTests}); - $result->enforceTimeLimit({enforcesTimeLimit}); - $result->beStrictAboutTodoAnnotatedTests({isStrictAboutTodoAnnotatedTests}); - $result->beStrictAboutResourceUsageDuringSmallTests({isStrictAboutResourceUsageDuringSmallTests}); - - $test = new {className}('{name}', unserialize('{data}'), '{dataName}'); - $test->setDependencyInput(unserialize('{dependencyInput}')); - $test->setInIsolation(true); - - ob_end_clean(); - $test->run($result); - $output = ''; - if (!$test->hasExpectationOnOutput()) { - $output = $test->getActualOutput(); + public function hasTestDoxXml() : bool + { + return $this->testDoxXml !== null; } - - ini_set('xdebug.scream', '0'); - - @rewind(STDOUT); /* @ as not every STDOUT target stream is rewindable */ - if ($stdout = @stream_get_contents(STDOUT)) { - $output = $stdout . $output; - $streamMetaData = stream_get_meta_data(STDOUT); - if (!empty($streamMetaData['stream_type']) && 'STDIO' === $streamMetaData['stream_type']) { - @ftruncate(STDOUT, 0); - @rewind(STDOUT); + public function testDoxXml() : TestDoxXml + { + if ($this->testDoxXml === null) { + throw new Exception('Logger "TestDox XML" is not configured'); } + return $this->testDoxXml; } - - file_put_contents( - '{processResultFile}', - serialize( - [ - 'testResult' => $test->getResult(), - 'numAssertions' => $test->getNumAssertions(), - 'result' => $result, - 'output' => $output - ] - ) - ); -} - -$configurationFilePath = '{configurationFilePath}'; - -if ('' !== $configurationFilePath) { - $configuration = (new Loader)->load($configurationFilePath); - - (new PhpHandler)->handle($configuration->php()); - - unset($configuration); -} - -function __phpunit_error_handler($errno, $errstr, $errfile, $errline) -{ - return true; -} - -set_error_handler('__phpunit_error_handler'); - -{constants} -{included_files} -{globals} - -restore_error_handler(); - -if (isset($GLOBALS['__PHPUNIT_BOOTSTRAP'])) { - require_once $GLOBALS['__PHPUNIT_BOOTSTRAP']; - unset($GLOBALS['__PHPUNIT_BOOTSTRAP']); } - -__phpunit_run_isolated_test(); + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration\Logging; -function __phpunit_run_isolated_test() +use PHPUnit\TextUI\XmlConfiguration\File; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + * + * @psalm-immutable + */ +final class TeamCity { - if (!class_exists('{className}')) { - require_once '{filename}'; - } - - $result = new PHPUnit\Framework\TestResult; - - if ({collectCodeCoverageInformation}) { - $filter = unserialize('{codeCoverageFilter}'); - - $codeCoverage = new CodeCoverage( - (new Selector)->{driverMethod}($filter), - $filter - ); - - if ({cachesStaticAnalysis}) { - $codeCoverage->cacheStaticAnalysis(unserialize('{codeCoverageCacheDirectory}')); - } - - $result->setCodeCoverage($codeCoverage); - } - - $result->beStrictAboutTestsThatDoNotTestAnything({isStrictAboutTestsThatDoNotTestAnything}); - $result->beStrictAboutOutputDuringTests({isStrictAboutOutputDuringTests}); - $result->enforceTimeLimit({enforcesTimeLimit}); - $result->beStrictAboutTodoAnnotatedTests({isStrictAboutTodoAnnotatedTests}); - $result->beStrictAboutResourceUsageDuringSmallTests({isStrictAboutResourceUsageDuringSmallTests}); - - $test = new {className}('{methodName}', unserialize('{data}'), '{dataName}'); - \assert($test instanceof TestCase); - - $test->setDependencyInput(unserialize('{dependencyInput}')); - $test->setInIsolation(true); - - ob_end_clean(); - $test->run($result); - $output = ''; - if (!$test->hasExpectationOnOutput()) { - $output = $test->getActualOutput(); + /** + * @var File + */ + private $target; + public function __construct(File $target) + { + $this->target = $target; } - - ini_set('xdebug.scream', '0'); - - @rewind(STDOUT); /* @ as not every STDOUT target stream is rewindable */ - if ($stdout = @stream_get_contents(STDOUT)) { - $output = $stdout . $output; - $streamMetaData = stream_get_meta_data(STDOUT); - if (!empty($streamMetaData['stream_type']) && 'STDIO' === $streamMetaData['stream_type']) { - @ftruncate(STDOUT, 0); - @rewind(STDOUT); - } + public function target() : File + { + return $this->target; } - - file_put_contents( - '{processResultFile}', - serialize( - [ - 'testResult' => $test->getResult(), - 'numAssertions' => $test->getNumAssertions(), - 'result' => $result, - 'output' => $output - ] - ) - ); -} - -$configurationFilePath = '{configurationFilePath}'; - -if ('' !== $configurationFilePath) { - $configuration = (new Loader)->load($configurationFilePath); - - (new PhpHandler)->handle($configuration->php()); - - unset($configuration); -} - -function __phpunit_error_handler($errno, $errstr, $errfile, $errline) -{ - return true; -} - -set_error_handler('__phpunit_error_handler'); - -{constants} -{included_files} -{globals} - -restore_error_handler(); - -if (isset($GLOBALS['__PHPUNIT_BOOTSTRAP'])) { - require_once $GLOBALS['__PHPUNIT_BOOTSTRAP']; - unset($GLOBALS['__PHPUNIT_BOOTSTRAP']); } - -__phpunit_run_isolated_test(); $stdout_handle]; + $this->target = $target; } - protected function useTemporaryFile() : bool + public function target() : File { - return \true; + return $this->target; } } stream = $out; - return; - } - if (!is_string($out)) { - return; - } - if (strpos($out, 'socket://') === 0) { - $tmp = explode(':', str_replace('socket://', '', $out)); - if (count($tmp) !== 2) { - throw new \PHPUnit\Util\Exception(sprintf('"%s" does not match "socket://hostname:port" format', $out)); - } - $this->stream = fsockopen($tmp[0], (int) $tmp[1]); - return; - } - if (strpos($out, 'php://') === \false && !\PHPUnit\Util\Filesystem::createDirectory(dirname($out))) { - throw new \PHPUnit\Util\Exception(sprintf('Directory "%s" was not created', dirname($out))); - } - $this->stream = fopen($out, 'wb'); - $this->isPhpStream = strncmp($out, 'php://', 6) !== 0; - } - public function write(string $buffer) : void + private $target; + public function __construct(File $target) { - if ($this->stream) { - assert(is_resource($this->stream)); - fwrite($this->stream, $buffer); - } else { - if (PHP_SAPI !== 'cli' && PHP_SAPI !== 'phpdbg') { - $buffer = htmlspecialchars($buffer, ENT_COMPAT | ENT_SUBSTITUTE); - } - print $buffer; - } + $this->target = $target; } - public function flush() : void + public function target() : File { - if ($this->stream && $this->isPhpStream) { - assert(is_resource($this->stream)); - fclose($this->stream); - } + return $this->target; } } - */ - public function publicMethodsInTestClass(ReflectionClass $class) : array - { - return $this->filterMethods($class, ReflectionMethod::IS_PUBLIC); - } - /** - * @psalm-return list + * @var File */ - public function methodsInTestClass(ReflectionClass $class) : array + private $target; + public function __construct(File $target) { - return $this->filterMethods($class, null); + $this->target = $target; } - /** - * @psalm-return list - */ - private function filterMethods(ReflectionClass $class, ?int $filter) : array + public function target() : File { - $methods = []; - // PHP <7.3.5 throw error when null is passed - // to ReflectionClass::getMethods() when strict_types is enabled. - $classMethods = $filter === null ? $class->getMethods() : $class->getMethods($filter); - foreach ($classMethods as $method) { - if ($method->getDeclaringClass()->getName() === TestCase::class) { - continue; - } - if ($method->getDeclaringClass()->getName() === Assert::class) { - continue; - } - $methods[] = $method; - } - return $methods; + return $this->target; } } target = $target; + } + public function target() : File + { + return $this->target; } } [\PHPUnit\TextUI\XmlConfiguration\RemoveLogTypes::class], '9.2' => [\PHPUnit\TextUI\XmlConfiguration\RemoveCacheTokensAttribute::class, \PHPUnit\TextUI\XmlConfiguration\IntroduceCoverageElement::class, \PHPUnit\TextUI\XmlConfiguration\MoveAttributesFromRootToCoverage::class, \PHPUnit\TextUI\XmlConfiguration\MoveAttributesFromFilterWhitelistToCoverage::class, \PHPUnit\TextUI\XmlConfiguration\MoveWhitelistIncludesToCoverage::class, \PHPUnit\TextUI\XmlConfiguration\MoveWhitelistExcludesToCoverage::class, \PHPUnit\TextUI\XmlConfiguration\RemoveEmptyFilter::class, \PHPUnit\TextUI\XmlConfiguration\CoverageCloverToReport::class, \PHPUnit\TextUI\XmlConfiguration\CoverageCrap4jToReport::class, \PHPUnit\TextUI\XmlConfiguration\CoverageHtmlToReport::class, \PHPUnit\TextUI\XmlConfiguration\CoveragePhpToReport::class, \PHPUnit\TextUI\XmlConfiguration\CoverageTextToReport::class, \PHPUnit\TextUI\XmlConfiguration\CoverageXmlToReport::class, \PHPUnit\TextUI\XmlConfiguration\ConvertLogTypes::class, \PHPUnit\TextUI\XmlConfiguration\UpdateSchemaLocationTo93::class]]; /** - * @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 + * @throws MigrationBuilderException */ - public static function describe(\PHPUnit\Framework\Test $test) : array + public function build(string $fromVersion) : array { - if ($test instanceof TestCase) { - return [get_class($test), $test->getName()]; - } - if ($test instanceof SelfDescribing) { - return ['', $test->toString()]; + $stack = []; + foreach (self::AVAILABLE_MIGRATIONS as $version => $migrations) { + if (version_compare($version, $fromVersion, '<')) { + continue; + } + foreach ($migrations as $migration) { + $stack[] = new $migration(); + } } - return ['', get_class($test)]; + return $stack; } - public static function describeAsString(\PHPUnit\Framework\Test $test) : string +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration; + +use PHPUnit\Exception; +use RuntimeException; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class MigrationBuilderException extends RuntimeException implements Exception +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration; + +use PHPUnit\Exception; +use RuntimeException; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class MigrationException extends RuntimeException implements Exception +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration; + +use DOMDocument; +use DOMElement; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class ConvertLogTypes implements \PHPUnit\TextUI\XmlConfiguration\Migration +{ + public function migrate(DOMDocument $document) : void { - if ($test instanceof SelfDescribing) { - return $test->toString(); + $logging = $document->getElementsByTagName('logging')->item(0); + if (!$logging instanceof DOMElement) { + return; + } + $types = ['junit' => 'junit', 'teamcity' => 'teamcity', 'testdox-html' => 'testdoxHtml', 'testdox-text' => 'testdoxText', 'testdox-xml' => 'testdoxXml', 'plain' => 'text']; + $logNodes = []; + foreach ($logging->getElementsByTagName('log') as $logNode) { + if (!isset($types[$logNode->getAttribute('type')])) { + continue; + } + $logNodes[] = $logNode; + } + foreach ($logNodes as $oldNode) { + $newLogNode = $document->createElement($types[$oldNode->getAttribute('type')]); + $newLogNode->setAttribute('outputFile', $oldNode->getAttribute('target')); + $logging->replaceChild($newLogNode, $oldNode); } - return get_class($test); } - /** - * @throws CodeCoverageException - * - * @return array|bool - * - * @psalm-param class-string $className - */ - public static function getLinesToBeCovered(string $className, string $methodName) +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration; + +use DOMElement; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class CoverageCloverToReport extends \PHPUnit\TextUI\XmlConfiguration\LogToReportMigration +{ + protected function forType() : string { - $annotations = self::parseTestMethodAnnotations($className, $methodName); - if (!self::shouldCoversAnnotationBeUsed($annotations)) { - return \false; - } - return self::getLinesToBeCoveredOrUsed($className, $methodName, 'covers'); + return 'coverage-clover'; } - /** - * 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 + protected function toReportFormat(DOMElement $logNode) : DOMElement { - return self::getLinesToBeCoveredOrUsed($className, $methodName, 'uses'); + $clover = $logNode->ownerDocument->createElement('clover'); + $clover->setAttribute('outputFile', $logNode->getAttribute('target')); + return $clover; } - public static function requiresCodeCoverageDataCollection(TestCase $test) : bool +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration; + +use DOMElement; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class CoverageCrap4jToReport extends \PHPUnit\TextUI\XmlConfiguration\LogToReportMigration +{ + protected function forType() : string { - $annotations = self::parseTestMethodAnnotations(get_class($test), $test->getName(\false)); - // 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'])) { - // @see https://github.com/sebastianbergmann/phpunit/issues/4947#issuecomment-1084480950 - // 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'])) { - // @see https://github.com/sebastianbergmann/phpunit/issues/4947#issuecomment-1084480950 - // return false; - } - // If there is no @coversNothing annotation then - // code coverage data may be collected - return \true; + return 'coverage-crap4j'; } - /** - * @throws Exception - * - * @psalm-param class-string $className - */ - public static function getRequirements(string $className, string $methodName) : array + protected function toReportFormat(DOMElement $logNode) : DOMElement { - return self::mergeArraysRecursively(Registry::getInstance()->forClassName($className)->requirements(), Registry::getInstance()->forMethod($className, $methodName)->requirements()); + $crap4j = $logNode->ownerDocument->createElement('crap4j'); + $crap4j->setAttribute('outputFile', $logNode->getAttribute('target')); + $this->migrateAttributes($logNode, $crap4j, ['threshold']); + return $crap4j; } - /** - * 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 +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration; + +use DOMElement; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class CoverageHtmlToReport extends \PHPUnit\TextUI\XmlConfiguration\LogToReportMigration +{ + protected function forType() : string { - $required = self::getRequirements($className, $methodName); - $missing = []; - $hint = null; - if (!empty($required['PHP'])) { - $operator = new \PHPUnit\Util\VersionComparisonOperator(empty($required['PHP']['operator']) ? '>=' : $required['PHP']['operator']); - if (!version_compare(PHP_VERSION, $required['PHP']['version'], $operator->asString())) { - $missing[] = sprintf('PHP %s %s is required.', $operator->asString(), $required['PHP']['version']); - $hint = 'PHP'; - } - } elseif (!empty($required['PHP_constraint'])) { - $version = new \PHPUnit\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 = 'PHP_constraint'; - } - } - if (!empty($required['PHPUnit'])) { - $phpunitVersion = Version::id(); - $operator = new \PHPUnit\Util\VersionComparisonOperator(empty($required['PHPUnit']['operator']) ? '>=' : $required['PHPUnit']['operator']); - if (!version_compare($phpunitVersion, $required['PHPUnit']['version'], $operator->asString())) { - $missing[] = sprintf('PHPUnit %s %s is required.', $operator->asString(), $required['PHPUnit']['version']); - $hint = $hint ?? 'PHPUnit'; - } - } elseif (!empty($required['PHPUnit_constraint'])) { - $phpunitVersion = new \PHPUnit\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 = new \PHPUnit\Util\VersionComparisonOperator(empty($req['operator']) ? '>=' : $req['operator']); - if ($actualVersion === \false || !version_compare($actualVersion, $req['version'], $operator->asString())) { - $missing[] = sprintf('Extension %s %s %s is required.', $extension, $operator->asString(), $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; + return 'coverage-html'; } - /** - * Returns the provided data for a method. - * - * @throws Exception - * - * @psalm-param class-string $className - */ - public static function getProvidedData(string $className, string $methodName) : ?array + protected function toReportFormat(DOMElement $logNode) : DOMElement { - return Registry::getInstance()->forMethod($className, $methodName)->getProvidedData(); + $html = $logNode->ownerDocument->createElement('html'); + $html->setAttribute('outputDirectory', $logNode->getAttribute('target')); + $this->migrateAttributes($logNode, $html, ['lowUpperBound', 'highLowerBound']); + return $html; } - /** - * @psalm-param class-string $className - */ - public static function parseTestMethodAnnotations(string $className, ?string $methodName = null) : array +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration; + +use DOMElement; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class CoveragePhpToReport extends \PHPUnit\TextUI\XmlConfiguration\LogToReportMigration +{ + protected function forType() : string { - $registry = Registry::getInstance(); - if ($methodName !== null) { - try { - return ['method' => $registry->forMethod($className, $methodName)->symbolAnnotations(), 'class' => $registry->forClassName($className)->symbolAnnotations()]; - } catch (\PHPUnit\Util\Exception $methodNotFound) { - // ignored - } - } - return ['method' => null, 'class' => $registry->forClassName($className)->symbolAnnotations()]; + return 'coverage-php'; } - /** - * @psalm-param class-string $className - */ - public static function getInlineAnnotations(string $className, string $methodName) : array + protected function toReportFormat(DOMElement $logNode) : DOMElement { - return Registry::getInstance()->forMethod($className, $methodName)->getInlineAnnotations(); + $php = $logNode->ownerDocument->createElement('php'); + $php->setAttribute('outputFile', $logNode->getAttribute('target')); + return $php; } - /** @psalm-param class-string $className */ - public static function getBackupSettings(string $className, string $methodName) : array +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration; + +use DOMElement; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class CoverageTextToReport extends \PHPUnit\TextUI\XmlConfiguration\LogToReportMigration +{ + protected function forType() : string { - return ['backupGlobals' => self::getBooleanAnnotationSetting($className, $methodName, 'backupGlobals'), 'backupStaticAttributes' => self::getBooleanAnnotationSetting($className, $methodName, 'backupStaticAttributes')]; + return 'coverage-text'; } - /** - * @psalm-param class-string $className - * - * @return ExecutionOrderDependency[] - */ - public static function getDependencies(string $className, string $methodName) : array + protected function toReportFormat(DOMElement $logNode) : DOMElement { - $annotations = self::parseTestMethodAnnotations($className, $methodName); - $dependsAnnotations = $annotations['class']['depends'] ?? []; - if (isset($annotations['method']['depends'])) { - $dependsAnnotations = array_merge($dependsAnnotations, $annotations['method']['depends']); - } - // Normalize dependency name to className::methodName - $dependencies = []; - foreach ($dependsAnnotations as $value) { - $dependencies[] = ExecutionOrderDependency::createFromDependsAnnotation($className, $value); - } - return array_unique($dependencies); + $text = $logNode->ownerDocument->createElement('text'); + $text->setAttribute('outputFile', $logNode->getAttribute('target')); + $this->migrateAttributes($logNode, $text, ['showUncoveredFiles', 'showOnlySummary']); + return $text; } - /** @psalm-param class-string $className */ - public static function getGroups(string $className, ?string $methodName = '') : array +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration; + +use DOMElement; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class CoverageXmlToReport extends \PHPUnit\TextUI\XmlConfiguration\LogToReportMigration +{ + protected function forType() : string { - $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; - } - } - } - foreach (['method', 'class'] as $element) { - if (isset($annotations[$element]['covers'])) { - foreach ($annotations[$element]['covers'] as $coversTarget) { - $groups[] = ['__phpunit_covers_' . self::canonicalizeName($coversTarget)]; - } - } - if (isset($annotations[$element]['uses'])) { - foreach ($annotations[$element]['uses'] as $usesTarget) { - $groups[] = ['__phpunit_uses_' . self::canonicalizeName($usesTarget)]; - } - } - } - 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; + return 'coverage-xml'; } - /** @psalm-param class-string $className */ - public static function getProcessIsolationSettings(string $className, string $methodName) : bool + protected function toReportFormat(DOMElement $logNode) : DOMElement { - $annotations = self::parseTestMethodAnnotations($className, $methodName); - return isset($annotations['class']['runTestsInSeparateProcesses']) || isset($annotations['method']['runInSeparateProcess']); + $xml = $logNode->ownerDocument->createElement('xml'); + $xml->setAttribute('outputDirectory', $logNode->getAttribute('target')); + return $xml; } - /** @psalm-param class-string $className */ - public static function getClassProcessIsolationSettings(string $className, string $methodName) : bool +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration; + +use DOMDocument; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class IntroduceCoverageElement implements \PHPUnit\TextUI\XmlConfiguration\Migration +{ + public function migrate(DOMDocument $document) : void { - $annotations = self::parseTestMethodAnnotations($className, $methodName); - return isset($annotations['class']['runClassInSeparateProcess']); + $coverage = $document->createElement('coverage'); + $document->documentElement->insertBefore($coverage, $document->documentElement->firstChild); } - /** @psalm-param class-string $className */ - public static function getPreserveGlobalStateSettings(string $className, string $methodName) : ?bool +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration; + +use function sprintf; +use DOMDocument; +use DOMElement; +use DOMXPath; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +abstract class LogToReportMigration implements \PHPUnit\TextUI\XmlConfiguration\Migration +{ + /** + * @throws MigrationException + */ + public function migrate(DOMDocument $document) : void { - return self::getBooleanAnnotationSetting($className, $methodName, 'preserveGlobalState'); + $coverage = $document->getElementsByTagName('coverage')->item(0); + if (!$coverage instanceof DOMElement) { + throw new \PHPUnit\TextUI\XmlConfiguration\MigrationException('Unexpected state - No coverage element'); + } + $logNode = $this->findLogNode($document); + if ($logNode === null) { + return; + } + $reportChild = $this->toReportFormat($logNode); + $report = $coverage->getElementsByTagName('report')->item(0); + if ($report === null) { + $report = $coverage->appendChild($document->createElement('report')); + } + $report->appendChild($reportChild); + $logNode->parentNode->removeChild($logNode); } - /** @psalm-param class-string $className */ - public static function getHookMethods(string $className) : array + protected function migrateAttributes(DOMElement $src, DOMElement $dest, array $attributes) : void { - if (!class_exists($className, \false)) { - return self::emptyHookMethodsArray(); - } - if (!isset(self::$hookMethods[$className])) { - self::$hookMethods[$className] = self::emptyHookMethodsArray(); - try { - foreach ((new \PHPUnit\Util\Reflection())->methodsInTestClass(new ReflectionClass($className)) as $method) { - $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->isToBeExecutedAsPreCondition()) { - array_unshift(self::$hookMethods[$className]['preCondition'], $method->getName()); - } - if ($docBlock->isToBeExecutedAsPostCondition()) { - self::$hookMethods[$className]['postCondition'][] = $method->getName(); - } - if ($docBlock->isToBeExecutedAfterTest()) { - self::$hookMethods[$className]['after'][] = $method->getName(); - } - } - } catch (ReflectionException $e) { + foreach ($attributes as $attr) { + if (!$src->hasAttribute($attr)) { + continue; } + $dest->setAttribute($attr, $src->getAttribute($attr)); + $src->removeAttribute($attr); } - return self::$hookMethods[$className]; } - public static function isTestMethod(ReflectionMethod $method) : bool + protected abstract function forType() : string; + protected abstract function toReportFormat(DOMElement $logNode) : DOMElement; + private function findLogNode(DOMDocument $document) : ?DOMElement { - if (!$method->isPublic()) { - return \false; - } - if (strpos($method->getName(), 'test') === 0) { - return \true; + $logNode = (new DOMXPath($document))->query(sprintf('//logging/log[@type="%s"]', $this->forType()))->item(0); + if (!$logNode instanceof DOMElement) { + return null; } - return array_key_exists('test', Registry::getInstance()->forMethod($method->getDeclaringClass()->getName(), $method->getName())->symbolAnnotations()); + return $logNode; } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration; + +use DOMDocument; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +interface Migration +{ + public function migrate(DOMDocument $document) : void; +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration; + +use DOMDocument; +use DOMElement; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class MoveAttributesFromFilterWhitelistToCoverage implements \PHPUnit\TextUI\XmlConfiguration\Migration +{ /** - * @throws CodeCoverageException - * - * @psalm-param class-string $className + * @throws MigrationException */ - private static function getLinesToBeCoveredOrUsed(string $className, string $methodName, string $mode) : array + public function migrate(DOMDocument $document) : void { - $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]; + $whitelist = $document->getElementsByTagName('whitelist')->item(0); + if (!$whitelist) { + return; } - $list = $annotations['class'][$mode] ?? []; - if (isset($annotations['method'][$mode])) { - $list = array_merge($list, $annotations['method'][$mode]); + $coverage = $document->getElementsByTagName('coverage')->item(0); + if (!$coverage instanceof DOMElement) { + throw new \PHPUnit\TextUI\XmlConfiguration\MigrationException('Unexpected state - No coverage element'); } - $codeUnits = CodeUnitCollection::fromArray([]); - $mapper = new Mapper(); - 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)); - } - try { - $codeUnits = $codeUnits->mergeWith($mapper->stringToCodeUnits($element)); - } catch (InvalidCodeUnitException $e) { - throw new InvalidCoversTargetException(sprintf('"@%s %s" is invalid', $mode, $element), $e->getCode(), $e); + $map = ['addUncoveredFilesFromWhitelist' => 'includeUncoveredFiles', 'processUncoveredFilesFromWhitelist' => 'processUncoveredFiles']; + foreach ($map as $old => $new) { + if (!$whitelist->hasAttribute($old)) { + continue; } + $coverage->setAttribute($new, $whitelist->getAttribute($old)); + $whitelist->removeAttribute($old); } - return $mapper->codeUnitsToSourceLines($codeUnits); - } - private static function emptyHookMethodsArray() : array - { - return ['beforeClass' => ['setUpBeforeClass'], 'before' => ['setUp'], 'preCondition' => ['assertPreConditions'], 'postCondition' => ['assertPostConditions'], 'after' => ['tearDown'], 'afterClass' => ['tearDownAfterClass']]; } - /** @psalm-param class-string $className */ - private static function getBooleanAnnotationSetting(string $className, ?string $methodName, string $settingName) : ?bool +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration; + +use DOMDocument; +use DOMElement; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class MoveAttributesFromRootToCoverage implements \PHPUnit\TextUI\XmlConfiguration\Migration +{ + /** + * @throws MigrationException + */ + public function migrate(DOMDocument $document) : void { - $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; - } + $map = ['disableCodeCoverageIgnore' => 'disableCodeCoverageIgnore', 'ignoreDeprecatedCodeUnitsFromCodeCoverage' => 'ignoreDeprecatedCodeUnits']; + $root = $document->documentElement; + $coverage = $document->getElementsByTagName('coverage')->item(0); + if (!$coverage instanceof DOMElement) { + throw new \PHPUnit\TextUI\XmlConfiguration\MigrationException('Unexpected state - No coverage element'); } - if (isset($annotations['class'][$settingName])) { - if ($annotations['class'][$settingName][0] === 'enabled') { - return \true; - } - if ($annotations['class'][$settingName][0] === 'disabled') { - return \false; + foreach ($map as $old => $new) { + if (!$root->hasAttribute($old)) { + continue; } + $coverage->setAttribute($new, $root->getAttribute($old)); + $root->removeAttribute($old); } - return null; } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration; + +use function assert; +use function in_array; +use DOMDocument; +use DOMElement; +use PHPUnit\Util\Xml\SnapshotNodeList; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class MoveWhitelistExcludesToCoverage implements \PHPUnit\TextUI\XmlConfiguration\Migration +{ /** - * Trims any extensions from version string that follows after - * the .[.] format. + * @throws MigrationException */ - private static function sanitizeVersionNumber(string $version) - { - return preg_replace('/^(\\d+\\.\\d+(?:.\\d+)?).*$/', '$1', $version); - } - private static function shouldCoversAnnotationBeUsed(array $annotations) : bool + public function migrate(DOMDocument $document) : void { - if (isset($annotations['method']['coversNothing'])) { - return \false; + $whitelist = $document->getElementsByTagName('whitelist')->item(0); + if ($whitelist === null) { + return; } - if (isset($annotations['method']['covers'])) { - return \true; + $excludeNodes = SnapshotNodeList::fromNodeList($whitelist->getElementsByTagName('exclude')); + if ($excludeNodes->count() === 0) { + return; } - if (isset($annotations['class']['coversNothing'])) { - return \false; + $coverage = $document->getElementsByTagName('coverage')->item(0); + if (!$coverage instanceof DOMElement) { + throw new \PHPUnit\TextUI\XmlConfiguration\MigrationException('Unexpected state - No coverage element'); + } + $targetExclude = $coverage->getElementsByTagName('exclude')->item(0); + if ($targetExclude === null) { + $targetExclude = $coverage->appendChild($document->createElement('exclude')); + } + foreach ($excludeNodes as $excludeNode) { + assert($excludeNode instanceof DOMElement); + foreach (SnapshotNodeList::fromNodeList($excludeNode->childNodes) as $child) { + if (!$child instanceof DOMElement || !in_array($child->nodeName, ['directory', 'file'], \true)) { + continue; + } + $targetExclude->appendChild($child); + } + if ($excludeNode->getElementsByTagName('*')->count() !== 0) { + throw new \PHPUnit\TextUI\XmlConfiguration\MigrationException('Dangling child elements in exclude found.'); + } + $whitelist->removeChild($excludeNode); } - return \true; } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration; + +use DOMDocument; +use DOMElement; +use PHPUnit\Util\Xml\SnapshotNodeList; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class MoveWhitelistIncludesToCoverage implements \PHPUnit\TextUI\XmlConfiguration\Migration +{ /** - * 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/) - * - * @see 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 + * @throws MigrationException */ - private static function mergeArraysRecursively(array $a, array $b) : array + public function migrate(DOMDocument $document) : void { - 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; + $whitelist = $document->getElementsByTagName('whitelist')->item(0); + if ($whitelist === null) { + return; + } + $coverage = $document->getElementsByTagName('coverage')->item(0); + if (!$coverage instanceof DOMElement) { + throw new \PHPUnit\TextUI\XmlConfiguration\MigrationException('Unexpected state - No coverage element'); + } + $include = $document->createElement('include'); + $coverage->appendChild($include); + foreach (SnapshotNodeList::fromNodeList($whitelist->childNodes) as $child) { + if (!$child instanceof DOMElement) { + continue; + } + if (!($child->nodeName === 'directory' || $child->nodeName === 'file')) { + continue; } + $include->appendChild($child); } - return $a; } - private static function canonicalizeName(string $name) : string +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration; + +use DOMDocument; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class RemoveCacheTokensAttribute implements \PHPUnit\TextUI\XmlConfiguration\Migration +{ + public function migrate(DOMDocument $document) : void { - return strtolower(trim($name, '\\')); + $root = $document->documentElement; + if ($root->hasAttribute('cacheTokens')) { + $root->removeAttribute('cacheTokens'); + } } } '│', '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 = [" \x1b[36m◐\x1b[0m running tests", " \x1b[36m◓\x1b[0m running tests", " \x1b[36m◑\x1b[0m running tests", " \x1b[36m◒\x1b[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 = []; - /** - * @var Timer - */ - private $timer; - /** - * @param null|resource|string $out - * @param int|string $numberOfColumns - * - * @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->timer = new Timer(); - $this->timer->start(); - } - public function printResult(TestResult $result) : void - { - $this->printHeader($result); - $this->printNonSuccessfulTestsSummary($result->count()); - $this->printFooter($result); - } - protected function printHeader(TestResult $result) : void - { - $this->write("\n" . (new ResourceUsageFormatter())->resourceUsage($this->timer->stop()) . "\n\n"); - } - protected function formatClassName(Test $test) : string + public function migrate(DOMDocument $document) : void { - if ($test instanceof TestCase) { - return $this->prettifier->prettifyTestClass(get_class($test)); + $whitelist = $document->getElementsByTagName('whitelist')->item(0); + if ($whitelist instanceof DOMElement) { + $this->ensureEmpty($whitelist); + $whitelist->parentNode->removeChild($whitelist); } - 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; + $filter = $document->getElementsByTagName('filter')->item(0); + if ($filter instanceof DOMElement) { + $this->ensureEmpty($filter); + $filter->parentNode->removeChild($filter); } - parent::registerTestResult($test, $t, $status, $time, $verbose); } /** - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws MigrationException */ - 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 + private function ensureEmpty(DOMElement $element) : void { - $trace = \PHPUnit\Util\Filter::getFilteredStacktrace($t); - if (!$this->colors) { - return $trace; + if ($element->attributes->length > 0) { + throw new \PHPUnit\TextUI\XmlConfiguration\MigrationException(sprintf('%s element has unexpected attributes', $element->nodeName)); } - $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 = ''; - } + if ($element->getElementsByTagName('*')->length > 0) { + throw new \PHPUnit\TextUI\XmlConfiguration\MigrationException(sprintf('%s element has unexpected children', $element->nodeName)); } - return implode('', $lines); } - protected function formatTestResultMessage(Throwable $t, array $result, ?string $prefix = null) : string +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration; + +use function assert; +use DOMDocument; +use DOMElement; +use PHPUnit\Util\Xml\SnapshotNodeList; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class RemoveLogTypes implements \PHPUnit\TextUI\XmlConfiguration\Migration +{ + public function migrate(DOMDocument $document) : void { - $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; + $logging = $document->getElementsByTagName('logging')->item(0); + if (!$logging instanceof DOMElement) { + return; } - if ($trace) { - if ($message || $diff) { - $out .= $this->prefixLines($prefix['default'], PHP_EOL) . PHP_EOL; + foreach (SnapshotNodeList::fromNodeList($logging->getElementsByTagName('log')) as $logNode) { + assert($logNode instanceof DOMElement); + switch ($logNode->getAttribute('type')) { + case 'json': + case 'tap': + $logging->removeChild($logNode); } - $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("\x1b[1K\x1b[" . strlen(self::SPINNER_ICONS[$id]) . 'D'); } } - private function formatRuntime(float $time, string $color = '') : string +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration; + +use DOMDocument; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class UpdateSchemaLocationTo93 implements \PHPUnit\TextUI\XmlConfiguration\Migration +{ + public function migrate(DOMDocument $document) : void { - 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')); + $document->documentElement->setAttributeNS('http://www.w3.org/2001/XMLSchema-instance', 'xsi:noNamespaceSchemaLocation', 'https://schema.phpunit.de/9.3/phpunit.xsd'); } - private function printNonSuccessfulTestsSummary(int $numberOfExecutedTests) : void +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration; + +use function sprintf; +use PHPUnit\Util\Xml\Exception as XmlException; +use PHPUnit\Util\Xml\Loader as XmlLoader; +use PHPUnit\Util\Xml\SchemaDetector; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class Migrator +{ + /** + * @throws Exception + * @throws MigrationBuilderException + * @throws MigrationException + * @throws XmlException + */ + public function migrate(string $filename) : string { - if (empty($this->nonSuccessfulTestResults)) { - return; - } - if (count($this->nonSuccessfulTestResults) / $numberOfExecutedTests >= 0.7) { - return; + $origin = (new SchemaDetector())->detect($filename); + if (!$origin->detected()) { + throw new \PHPUnit\TextUI\XmlConfiguration\Exception(sprintf('"%s" is not a valid PHPUnit XML configuration file that can be migrated', $filename)); } - $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; + $configurationDocument = (new XmlLoader())->loadFile($filename, \false, \true, \true); + foreach ((new \PHPUnit\TextUI\XmlConfiguration\MigrationBuilder())->build($origin->version()) as $migration) { + $migration->migrate($configurationDocument); } + $configurationDocument->formatOutput = \true; + $configurationDocument->preserveWhiteSpace = \false; + return $configurationDocument->saveXML(); } } - - - - Test Documentation - - - -EOT; + private $name; /** - * @var string + * @var mixed */ - private const CLASS_HEADER = <<<'EOT' + private $value; + public function __construct(string $name, $value) + { + $this->name = $name; + $this->value = $value; + } + public function name() : string + { + return $this->name; + } + public function value() + { + return $this->value; + } +} +%s -
    +declare (strict_types=1); +/* + * This file is part of PHPUnit. + * + * (c) Sebastian Bergmann + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration; -EOT; - /** - * @var string - */ - private const CLASS_FOOTER = <<<'EOT' -
-EOT; +use function count; +use Countable; +use IteratorAggregate; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + * + * @psalm-immutable + * + * @template-implements IteratorAggregate + */ +final class ConstantCollection implements Countable, IteratorAggregate +{ /** - * @var string + * @var Constant[] */ - private const PAGE_FOOTER = <<<'EOT' - - - -EOT; - public function printResult(TestResult $result) : void - { - } + private $constants; /** - * Handler for 'start run' event. + * @param Constant[] $constants */ - protected function startRun() : void + public static function fromArray(array $constants) : self { - $this->write(self::PAGE_HEADER); + return new self(...$constants); } - /** - * Handler for 'start class' event. - */ - protected function startClass(string $name) : void + private function __construct(\PHPUnit\TextUI\XmlConfiguration\Constant ...$constants) { - $this->write(sprintf(self::CLASS_HEADER, $this->currentTestClassPrettified)); + $this->constants = $constants; } /** - * Handler for 'on test' event. + * @return Constant[] */ - protected function onTest(string $name, bool $success = \true) : void + public function asArray() : array { - $this->write(sprintf("
  • %s
  • \n", $success ? 'success' : 'defect', $name)); + return $this->constants; } - /** - * Handler for 'end class' event. - */ - protected function endClass(string $name) : void + public function count() : int { - $this->write(self::CLASS_FOOTER); + return count($this->constants); } - /** - * Handler for 'end run' event. - */ - protected function endRun() : void + public function getIterator() : \PHPUnit\TextUI\XmlConfiguration\ConstantCollectionIterator { - $this->write(self::PAGE_FOOTER); + return new \PHPUnit\TextUI\XmlConfiguration\ConstantCollectionIterator($this); } } */ -final class NamePrettifier +final class ConstantCollectionIterator implements Countable, Iterator { /** - * @var string[] + * @var Constant[] */ - private $strings = []; + private $constants; /** - * @var bool + * @var int */ - private $useColor; - public function __construct(bool $useColor = \false) + private $position; + public function __construct(\PHPUnit\TextUI\XmlConfiguration\ConstantCollection $constants) { - $this->useColor = $useColor; + $this->constants = $constants->asArray(); } - /** - * Prettifies the name of a test class. - * - * @psalm-param class-string $className - */ - public function prettifyTestClass(string $className) : string + public function count() : int { - try { - $annotations = Test::parseTestMethodAnnotations($className); - if (isset($annotations['class']['testdox'][0])) { - return $annotations['class']['testdox'][0]; - } - } catch (UtilException $e) { - // ignore, determine className by parsing the provided name - } - $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($className)) { - $className = 'UnnamedTests'; - } - if (!empty($parts)) { - $parts[] = $className; - $fullyQualifiedName = implode('\\', $parts); - } else { - $fullyQualifiedName = $className; - } - $result = preg_replace('/(?<=[[:lower:]])(?=[[:upper:]])/u', ' ', $className); - if ($fullyQualifiedName !== $className) { - return $result . ' (' . $fullyQualifiedName . ')'; - } - return $result; + return iterator_count($this); } - /** - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public function prettifyTestCase(TestCase $test) : string + public function rewind() : void { - $annotations = Test::parseTestMethodAnnotations(get_class($test), $test->getName(\false)); - $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; + $this->position = 0; } - public function prettifyDataSet(TestCase $test) : string + public function valid() : bool { - 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((string) $test->dataName())); - } - return $data; + return $this->position < count($this->constants); } - /** - * Prettifies the name of a test method. - */ - public function prettifyTestMethod(string $name) : string + public function key() : int { - $buffer = ''; - if ($name === '') { - return $buffer; - } - $string = (string) preg_replace('#\\d+$#', '', $name, -1, $count); - if (in_array($string, $this->strings, \true)) { - $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; + return $this->position; } - /** - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - private function mapTestMethodParameterNamesToProvidedDataValues(TestCase $test) : array + public function current() : \PHPUnit\TextUI\XmlConfiguration\Constant { - try { - $reflector = new ReflectionMethod(get_class($test), $test->getName(\false)); - // @codeCoverageIgnoreStart - } catch (ReflectionException $e) { - throw new UtilException($e->getMessage(), $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd - $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(); - // @codeCoverageIgnoreStart - } catch (ReflectionException $e) { - throw new UtilException($e->getMessage(), $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd - } - $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; + return $this->constants[$this->position]; + } + public function next() : void + { + $this->position++; } } groups = $groups; - $this->excludeGroups = $excludeGroups; - $this->prettifier = new \PHPUnit\Util\TestDox\NamePrettifier(); - $this->startRun(); - } + private $name; /** - * Flush buffer and close output. + * @var string */ - public function flush() : void + private $value; + public function __construct(string $name, string $value) { - $this->doEndClass(); - $this->endRun(); - parent::flush(); + $this->name = $name; + $this->value = $value; } - /** - * An error occurred. - */ - public function addError(Test $test, Throwable $t, float $time) : void + public function name() : string { - if (!$this->isOfInterest($test)) { - return; - } - $this->testStatus = BaseTestRunner::STATUS_ERROR; - $this->failed++; + return $this->name; } - /** - * A warning occurred. - */ - public function addWarning(Test $test, Warning $e, float $time) : void + public function value() : string { - if (!$this->isOfInterest($test)) { - return; - } - $this->testStatus = BaseTestRunner::STATUS_WARNING; - $this->warned++; + return $this->value; } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration; + +use function count; +use Countable; +use IteratorAggregate; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + * + * @psalm-immutable + * + * @template-implements IteratorAggregate + */ +final class IniSettingCollection implements Countable, IteratorAggregate +{ /** - * A failure occurred. + * @var IniSetting[] */ - public function addFailure(Test $test, AssertionFailedError $e, float $time) : void - { - if (!$this->isOfInterest($test)) { - return; - } - $this->testStatus = BaseTestRunner::STATUS_FAILURE; - $this->failed++; - } + private $iniSettings; /** - * Incomplete test. + * @param IniSetting[] $iniSettings */ - public function addIncompleteTest(Test $test, Throwable $t, float $time) : void + public static function fromArray(array $iniSettings) : self { - if (!$this->isOfInterest($test)) { - return; - } - $this->testStatus = BaseTestRunner::STATUS_INCOMPLETE; - $this->incomplete++; + return new self(...$iniSettings); } - /** - * Risky test. - */ - public function addRiskyTest(Test $test, Throwable $t, float $time) : void + private function __construct(\PHPUnit\TextUI\XmlConfiguration\IniSetting ...$iniSettings) { - if (!$this->isOfInterest($test)) { - return; - } - $this->testStatus = BaseTestRunner::STATUS_RISKY; - $this->risky++; + $this->iniSettings = $iniSettings; } /** - * Skipped test. + * @return IniSetting[] */ - public function addSkippedTest(Test $test, Throwable $t, float $time) : void + public function asArray() : array { - if (!$this->isOfInterest($test)) { - return; - } - $this->testStatus = BaseTestRunner::STATUS_SKIPPED; - $this->skipped++; + return $this->iniSettings; } - /** - * A testsuite started. - */ - public function startTestSuite(TestSuite $suite) : void + public function count() : int { + return count($this->iniSettings); } - /** - * A testsuite ended. - */ - public function endTestSuite(TestSuite $suite) : void + public function getIterator() : \PHPUnit\TextUI\XmlConfiguration\IniSettingCollectionIterator { + return new \PHPUnit\TextUI\XmlConfiguration\IniSettingCollectionIterator($this); } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration; + +use function count; +use function iterator_count; +use Countable; +use Iterator; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + * + * @template-implements Iterator + */ +final class IniSettingCollectionIterator implements Countable, Iterator +{ /** - * A test started. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @var IniSetting[] */ - 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; - } + private $iniSettings; /** - * A test ended. + * @var int */ - 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 + private $position; + public function __construct(\PHPUnit\TextUI\XmlConfiguration\IniSettingCollection $iniSettings) { - foreach ($this->tests as $test) { - $this->onTest($test[0], $test[1] === BaseTestRunner::STATUS_PASSED); - } - $this->endClass($this->testClass); + $this->iniSettings = $iniSettings->asArray(); } - /** - * Handler for 'start run' event. - */ - protected function startRun() : void + public function count() : int { + return iterator_count($this); } - /** - * Handler for 'start class' event. - */ - protected function startClass(string $name) : void + public function rewind() : void { + $this->position = 0; } - /** - * Handler for 'on test' event. - */ - protected function onTest(string $name, bool $success = \true) : void + public function valid() : bool { + return $this->position < count($this->iniSettings); } - /** - * Handler for 'end class' event. - */ - protected function endClass(string $name) : void + public function key() : int { + return $this->position; } - /** - * Handler for 'end run' event. - */ - protected function endRun() : void + public function current() : \PHPUnit\TextUI\XmlConfiguration\IniSetting { + return $this->iniSettings[$this->position]; } - private function isOfInterest(Test $test) : bool + public function next() : void { - if (!$test instanceof TestCase) { - return \false; - } - if ($test instanceof ErrorTestCase || $test instanceof WarningTestCase) { - return \false; - } - if (!empty($this->groups)) { - foreach ($test->getGroups() as $group) { - if (in_array($group, $this->groups, \true)) { - return \true; - } - } - return \false; - } - if (!empty($this->excludeGroups)) { - foreach ($test->getGroups() as $group) { - if (in_array($group, $this->excludeGroups, \true)) { - return \false; - } - } - return \true; - } - return \true; + $this->position++; } } Buffer for test results + * @var VariableCollection */ - protected $testResults = []; + private $globalVariables; /** - * @var array Lookup table for testname to testResults[index] + * @var VariableCollection */ - protected $testNameResultIndex = []; + private $envVariables; /** - * @var bool + * @var VariableCollection */ - protected $enableOutputBuffer = \false; + private $postVariables; /** - * @var array array + * @var VariableCollection */ - protected $originalExecutionOrder = []; + private $getVariables; /** - * @var int + * @var VariableCollection */ - protected $spinState = 0; + private $cookieVariables; /** - * @var bool + * @var VariableCollection */ - protected $showProgress = \true; + private $serverVariables; /** - * @param null|resource|string $out - * @param int|string $numberOfColumns - * - * @throws \PHPUnit\Framework\Exception + * @var VariableCollection */ - 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 \PHPUnit\Util\TestDox\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 - { - } + private $filesVariables; /** - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @var VariableCollection */ - public function endTest(Test $test, float $time) : void + private $requestVariables; + public function __construct(\PHPUnit\TextUI\XmlConfiguration\DirectoryCollection $includePaths, \PHPUnit\TextUI\XmlConfiguration\IniSettingCollection $iniSettings, \PHPUnit\TextUI\XmlConfiguration\ConstantCollection $constants, \PHPUnit\TextUI\XmlConfiguration\VariableCollection $globalVariables, \PHPUnit\TextUI\XmlConfiguration\VariableCollection $envVariables, \PHPUnit\TextUI\XmlConfiguration\VariableCollection $postVariables, \PHPUnit\TextUI\XmlConfiguration\VariableCollection $getVariables, \PHPUnit\TextUI\XmlConfiguration\VariableCollection $cookieVariables, \PHPUnit\TextUI\XmlConfiguration\VariableCollection $serverVariables, \PHPUnit\TextUI\XmlConfiguration\VariableCollection $filesVariables, \PHPUnit\TextUI\XmlConfiguration\VariableCollection $requestVariables) { - 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); + $this->includePaths = $includePaths; + $this->iniSettings = $iniSettings; + $this->constants = $constants; + $this->globalVariables = $globalVariables; + $this->envVariables = $envVariables; + $this->postVariables = $postVariables; + $this->getVariables = $getVariables; + $this->cookieVariables = $cookieVariables; + $this->serverVariables = $serverVariables; + $this->filesVariables = $filesVariables; + $this->requestVariables = $requestVariables; } - /** - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public function addError(Test $test, Throwable $t, float $time) : void + public function includePaths() : \PHPUnit\TextUI\XmlConfiguration\DirectoryCollection { - $this->registerTestResult($test, $t, BaseTestRunner::STATUS_ERROR, $time, \true); + return $this->includePaths; } - /** - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public function addWarning(Test $test, Warning $e, float $time) : void + public function iniSettings() : \PHPUnit\TextUI\XmlConfiguration\IniSettingCollection { - $this->registerTestResult($test, $e, BaseTestRunner::STATUS_WARNING, $time, \true); + return $this->iniSettings; } - /** - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public function addFailure(Test $test, AssertionFailedError $e, float $time) : void + public function constants() : \PHPUnit\TextUI\XmlConfiguration\ConstantCollection { - $this->registerTestResult($test, $e, BaseTestRunner::STATUS_FAILURE, $time, \true); + return $this->constants; } - /** - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public function addIncompleteTest(Test $test, Throwable $t, float $time) : void + public function globalVariables() : \PHPUnit\TextUI\XmlConfiguration\VariableCollection { - $this->registerTestResult($test, $t, BaseTestRunner::STATUS_INCOMPLETE, $time, \false); + return $this->globalVariables; } - /** - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public function addRiskyTest(Test $test, Throwable $t, float $time) : void + public function envVariables() : \PHPUnit\TextUI\XmlConfiguration\VariableCollection { - $this->registerTestResult($test, $t, BaseTestRunner::STATUS_RISKY, $time, \false); + return $this->envVariables; } - /** - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public function addSkippedTest(Test $test, Throwable $t, float $time) : void + public function postVariables() : \PHPUnit\TextUI\XmlConfiguration\VariableCollection { - $this->registerTestResult($test, $t, BaseTestRunner::STATUS_SKIPPED, $time, \false); + return $this->postVariables; } - public function writeProgress(string $progress) : void + public function getVariables() : \PHPUnit\TextUI\XmlConfiguration\VariableCollection { - $this->flushOutputBuffer(); + return $this->getVariables; } - public function flush() : void + public function cookieVariables() : \PHPUnit\TextUI\XmlConfiguration\VariableCollection { - $this->flushOutputBuffer(\true); + return $this->cookieVariables; } - /** - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - protected function registerTestResult(Test $test, ?Throwable $t, int $status, float $time, bool $verbose) : void + public function serverVariables() : \PHPUnit\TextUI\XmlConfiguration\VariableCollection { - $testName = $test instanceof Reorderable ? $test->sortId() : $test->getName(); - $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; + return $this->serverVariables; } - protected function formatTestName(Test $test) : string + public function filesVariables() : \PHPUnit\TextUI\XmlConfiguration\VariableCollection { - return method_exists($test, 'getName') ? $test->getName() : ''; + return $this->filesVariables; } - protected function formatClassName(Test $test) : string + public function requestVariables() : \PHPUnit\TextUI\XmlConfiguration\VariableCollection { - return get_class($test); + return $this->requestVariables; } - protected function testHasPassed() : bool +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration; + +use const PATH_SEPARATOR; +use function constant; +use function define; +use function defined; +use function getenv; +use function implode; +use function ini_get; +use function ini_set; +use function putenv; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class PhpHandler +{ + public function handle(\PHPUnit\TextUI\XmlConfiguration\Php $configuration) : void { - if (!isset($this->testResults[$this->testIndex]['status'])) { - return \true; - } - if ($this->testResults[$this->testIndex]['status'] === BaseTestRunner::STATUS_PASSED) { - return \true; - } - return \false; + $this->handleIncludePaths($configuration->includePaths()); + $this->handleIniSettings($configuration->iniSettings()); + $this->handleConstants($configuration->constants()); + $this->handleGlobalVariables($configuration->globalVariables()); + $this->handleServerVariables($configuration->serverVariables()); + $this->handleEnvVariables($configuration->envVariables()); + $this->handleVariables('_POST', $configuration->postVariables()); + $this->handleVariables('_GET', $configuration->getVariables()); + $this->handleVariables('_COOKIE', $configuration->cookieVariables()); + $this->handleVariables('_FILES', $configuration->filesVariables()); + $this->handleVariables('_REQUEST', $configuration->requestVariables()); } - protected function flushOutputBuffer(bool $forceFlush = \false) : void + private function handleIncludePaths(\PHPUnit\TextUI\XmlConfiguration\DirectoryCollection $includePaths) : void { - if ($this->testFlushIndex === $this->testIndex) { - return; - } - if ($this->testFlushIndex > 0) { - if ($this->enableOutputBuffer && isset($this->originalExecutionOrder[$this->testFlushIndex - 1])) { - $prevResult = $this->getTestResultByName($this->originalExecutionOrder[$this->testFlushIndex - 1]); - } else { - $prevResult = $this->testResults[$this->testFlushIndex - 1]; + if (!$includePaths->isEmpty()) { + $includePathsAsStrings = []; + foreach ($includePaths as $includePath) { + $includePathsAsStrings[] = $includePath->path(); } - } 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); + ini_set('include_path', implode(PATH_SEPARATOR, $includePathsAsStrings) . PATH_SEPARATOR . ini_get('include_path')); } } - protected function showSpinner() : void + private function handleIniSettings(\PHPUnit\TextUI\XmlConfiguration\IniSettingCollection $iniSettings) : void { - if (!$this->showProgress) { - return; - } - if ($this->spinState) { - $this->undrawSpinner(); + foreach ($iniSettings as $iniSetting) { + $value = $iniSetting->value(); + if (defined($value)) { + $value = (string) constant($value); + } + ini_set($iniSetting->name(), $value); } - $this->spinState++; - $this->drawSpinner(); } - protected function hideSpinner() : void + private function handleConstants(\PHPUnit\TextUI\XmlConfiguration\ConstantCollection $constants) : void { - if (!$this->showProgress) { - return; - } - if ($this->spinState) { - $this->undrawSpinner(); + foreach ($constants as $constant) { + if (!defined($constant->name())) { + define($constant->name(), $constant->value()); + } } - $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 + private function handleGlobalVariables(\PHPUnit\TextUI\XmlConfiguration\VariableCollection $variables) : void { - if (isset($this->testNameResultIndex[$testName])) { - return $this->testResults[$this->testNameResultIndex[$testName]]; + foreach ($variables as $variable) { + $GLOBALS[$variable->name()] = $variable->value(); } - return []; } - protected function formatThrowable(Throwable $t, ?int $status = null) : string + private function handleServerVariables(\PHPUnit\TextUI\XmlConfiguration\VariableCollection $variables) : void { - $message = trim(\PHPUnit\Framework\TestFailure::exceptionToString($t)); - if ($message) { - $message .= PHP_EOL . PHP_EOL . $this->formatStacktrace($t); - } else { - $message = $this->formatStacktrace($t); + foreach ($variables as $variable) { + $_SERVER[$variable->name()] = $variable->value(); } - return $message; - } - protected function formatStacktrace(Throwable $t) : string - { - return \PHPUnit\Util\Filter::getFilteredStacktrace($t); } - protected function formatTestResultMessage(Throwable $t, array $result, string $prefix = '│') : string + private function handleVariables(string $target, \PHPUnit\TextUI\XmlConfiguration\VariableCollection $variables) : void { - $message = $this->formatThrowable($t, $result['status']); - if ($message === '') { - return ''; - } - if (!($this->verbose || $result['verbose'])) { - return ''; + foreach ($variables as $variable) { + $GLOBALS[$target][$variable->name()] = $variable->value(); } - return $this->prefixLines($prefix, $message); } - protected function prefixLines(string $prefix, string $message) : string + private function handleEnvVariables(\PHPUnit\TextUI\XmlConfiguration\VariableCollection $variables) : void { - $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))); + foreach ($variables as $variable) { + $name = $variable->name(); + $value = $variable->value(); + $force = $variable->force(); + if ($force || getenv($name) === \false) { + putenv("{$name}={$value}"); + } + $value = getenv($name); + if ($force || !isset($_ENV[$name])) { + $_ENV[$name] = $value; + } + } } } write($this->currentTestClassPrettified . "\n"); - } + private $name; /** - * Handler for 'on test' event. + * @var mixed */ - protected function onTest(string $name, bool $success = \true) : void - { - if ($success) { - $this->write(' [x] '); - } else { - $this->write(' [ ] '); - } - $this->write($name . "\n"); - } + private $value; /** - * Handler for 'end class' event. + * @var bool */ - protected function endClass(string $name) : void + private $force; + public function __construct(string $name, $value, bool $force) { - $this->write("\n"); + $this->name = $name; + $this->value = $value; + $this->force = $force; + } + public function name() : string + { + return $this->name; + } + public function value() + { + return $this->value; + } + public function force() : bool + { + return $this->force; } } */ -final class XmlResultPrinter extends Printer implements TestListener +final class VariableCollection implements Countable, IteratorAggregate { /** - * @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 \PHPUnit\Util\TestDox\NamePrettifier(); - parent::__construct($out); - } - /** - * Flush buffer and close output. - */ - public function flush() : void - { - $this->write($this->document->saveXML()); - parent::flush(); - } - /** - * An error occurred. + * @var Variable[] */ - public function addError(Test $test, Throwable $t, float $time) : void - { - $this->exception = $t; - } + private $variables; /** - * A warning occurred. + * @param Variable[] $variables */ - public function addWarning(Test $test, Warning $e, float $time) : void + public static function fromArray(array $variables) : self { + return new self(...$variables); } - /** - * A failure occurred. - */ - public function addFailure(Test $test, AssertionFailedError $e, float $time) : void + private function __construct(\PHPUnit\TextUI\XmlConfiguration\Variable ...$variables) { - $this->exception = $e; + $this->variables = $variables; } /** - * Incomplete test. + * @return Variable[] */ - public function addIncompleteTest(Test $test, Throwable $t, float $time) : void + public function asArray() : array { + return $this->variables; } - /** - * Risky test. - */ - public function addRiskyTest(Test $test, Throwable $t, float $time) : void + public function count() : int { + return count($this->variables); } - /** - * Skipped test. - */ - public function addSkippedTest(Test $test, Throwable $t, float $time) : void + public function getIterator() : \PHPUnit\TextUI\XmlConfiguration\VariableCollectionIterator { - } - /** - * 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 || $test instanceof WarningTestCase) { - return; - } - $groups = array_filter($test->getGroups(), static function ($group) { - return !($group === 'small' || $group === 'medium' || $group === 'large' || strpos($group, '__phpunit_') === 0); - }); - $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 = TestUtil::parseTestMethodAnnotations(get_class($test), $test->getName(\false)); - 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(); - // @codeCoverageIgnoreStart - } catch (ReflectionException $e) { - throw new Exception($e->getMessage(), $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd - 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); + return new \PHPUnit\TextUI\XmlConfiguration\VariableCollectionIterator($this); } } */ -final class TextTestListRenderer +final class VariableCollectionIterator implements Countable, Iterator { /** - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @var Variable[] */ - public function render(TestSuite $suite) : string + private $variables; + /** + * @var int + */ + private $position; + public function __construct(\PHPUnit\TextUI\XmlConfiguration\VariableCollection $variables) { - $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; + $this->variables = $variables->asArray(); } -} - - * - * 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 + public function count() : int { - 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; - } + return iterator_count($this); + } + public function rewind() : void + { + $this->position = 0; + } + public function valid() : bool + { + return $this->position < count($this->variables); + } + public function key() : int + { + return $this->position; + } + public function current() : \PHPUnit\TextUI\XmlConfiguration\Variable + { + return $this->variables[$this->position]; + } + public function next() : void + { + $this->position++; } } '|'gt'|'>='|'ge'|'=='|'='|'eq'|'!='|'<>'|'ne' + * @var string + * + * @psalm-var class-string */ - private $operator; - public function __construct(string $operator) - { - $this->ensureOperatorIsValid($operator); - $this->operator = $operator; - } + private $className; /** - * @return '!='|'<'|'<='|'<>'|'='|'=='|'>'|'>='|'eq'|'ge'|'gt'|'le'|'lt'|'ne' + * @var string */ - public function asString() : string + private $sourceFile; + /** + * @var array + */ + private $arguments; + /** + * @psalm-param class-string $className + */ + public function __construct(string $className, string $sourceFile, array $arguments) { - return $this->operator; + $this->className = $className; + $this->sourceFile = $sourceFile; + $this->arguments = $arguments; } /** - * @throws Exception - * - * @psalm-assert '<'|'lt'|'<='|'le'|'>'|'gt'|'>='|'ge'|'=='|'='|'eq'|'!='|'<>'|'ne' $operator + * @psalm-return class-string */ - private function ensureOperatorIsValid(string $operator) : void + public function className() : string { - if (!in_array($operator, ['<', 'lt', '<=', 'le', '>', 'gt', '>=', 'ge', '==', '=', 'eq', '!=', '<>', 'ne'], \true)) { - throw new \PHPUnit\Util\Exception(sprintf('"%s" is not a valid version_compare() operator', $operator)); - } + return $this->className; + } + public function hasSourceFile() : bool + { + return $this->sourceFile !== ''; + } + public function sourceFile() : string + { + return $this->sourceFile; + } + public function hasArguments() : bool + { + return !empty($this->arguments); + } + public function arguments() : array + { + return $this->arguments; } } */ -final class XdebugFilterScriptGenerator +final class ExtensionCollection implements IteratorAggregate { - public function generate(FilterConfiguration $filter) : string + /** + * @var Extension[] + */ + private $extensions; + /** + * @param Extension[] $extensions + */ + public static function fromArray(array $extensions) : self { - $files = array_map(static function ($item) { - return sprintf(" '%s'", $item); - }, $this->getItems($filter)); - $files = implode(",\n", $files); - return <<directories() as $directory) { - $path = realpath($directory->path()); - if (is_string($path)) { - $files[] = sprintf(addslashes('%s' . DIRECTORY_SEPARATOR), $path); - } - } - foreach ($filter->files() as $file) { - $files[] = $file->path(); - } - return $files; + $this->extensions = $extensions; + } + /** + * @return Extension[] + */ + public function asArray() : array + { + return $this->extensions; + } + public function getIterator() : \PHPUnit\TextUI\XmlConfiguration\ExtensionCollectionIterator + { + return new \PHPUnit\TextUI\XmlConfiguration\ExtensionCollectionIterator($this); } } */ -final class Xml +final class ExtensionCollectionIterator implements Countable, Iterator { /** - * @deprecated Only used by assertEqualXMLStructure() + * @var Extension[] */ - public static function import(DOMElement $element) : DOMElement - { - return (new DOMDocument())->importNode($element, \true); - } + private $extensions; /** - * @deprecated Only used by assertEqualXMLStructure() + * @var int */ - public static function removeCharacterDataNodes(DOMNode $node) : void + private $position; + public function __construct(\PHPUnit\TextUI\XmlConfiguration\ExtensionCollection $extensions) { - if ($node->hasChildNodes()) { - for ($i = $node->childNodes->length - 1; $i >= 0; $i--) { - if (($child = $node->childNodes->item($i)) instanceof DOMCharacterData) { - $node->removeChild($child); - } - } - } + $this->extensions = $extensions->asArray(); } - /** - * 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 + public function count() : int { - return preg_replace('/[\\x00-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]/', '', htmlspecialchars(self::convertToUtf8($string), ENT_QUOTES)); + return iterator_count($this); } - /** - * "Convert" a DOMElement object into a PHP variable. - */ - public static function xmlToVariable(DOMElement $element) + public function rewind() : void { - $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 { - assert(class_exists($className)); - $variable = (new ReflectionClass($className))->newInstanceArgs($constructorArgs); - // @codeCoverageIgnoreStart - } catch (ReflectionException $e) { - throw new \PHPUnit\Util\Exception($e->getMessage(), $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd - } 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; + $this->position = 0; } - private static function convertToUtf8(string $string) : string + public function valid() : bool { - if (!self::isUtf8($string)) { - $string = mb_convert_encoding($string, 'UTF-8'); - } - return $string; + return $this->position < count($this->extensions); } - private static function isUtf8(string $string) : bool + public function key() : int { - $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; + return $this->position; + } + public function current() : \PHPUnit\TextUI\XmlConfiguration\Extension + { + return $this->extensions[$this->position]; + } + public function next() : void + { + $this->position++; } } - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Util\Xml; +namespace PHPUnit\TextUI\XmlConfiguration; /** * @internal This class is not covered by the backward compatibility promise for PHPUnit * * @psalm-immutable */ -final class FailedSchemaDetectionResult extends \PHPUnit\Util\Xml\SchemaDetectionResult -{ -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Util\Xml; - -use function chdir; -use function dirname; -use function error_reporting; -use function file_get_contents; -use function getcwd; -use function libxml_get_errors; -use function libxml_use_internal_errors; -use function sprintf; -use DOMDocument; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class Loader +final class PHPUnit { /** - * @throws Exception + * @var bool */ - public 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 \PHPUnit\Util\Xml\Exception(sprintf('Could not read "%s".', $filename)); - } - return $this->load($contents, $isHtml, $filename, $xinclude, $strict); - } + private $cacheResult; /** - * @throws Exception + * @var ?string */ - public function load(string $actual, bool $isHtml = \false, string $filename = '', bool $xinclude = \false, bool $strict = \false) : DOMDocument + private $cacheResultFile; + /** + * @var int|string + */ + private $columns; + /** + * @var string + */ + private $colors; + /** + * @var bool + */ + private $stderr; + /** + * @var bool + */ + private $noInteraction; + /** + * @var bool + */ + private $verbose; + /** + * @var bool + */ + private $reverseDefectList; + /** + * @var bool + */ + private $convertDeprecationsToExceptions; + /** + * @var bool + */ + private $convertErrorsToExceptions; + /** + * @var bool + */ + private $convertNoticesToExceptions; + /** + * @var bool + */ + private $convertWarningsToExceptions; + /** + * @var bool + */ + private $forceCoversAnnotation; + /** + * @var ?string + */ + private $bootstrap; + /** + * @var bool + */ + private $processIsolation; + /** + * @var bool + */ + private $failOnEmptyTestSuite; + /** + * @var bool + */ + private $failOnIncomplete; + /** + * @var bool + */ + private $failOnRisky; + /** + * @var bool + */ + private $failOnSkipped; + /** + * @var bool + */ + private $failOnWarning; + /** + * @var bool + */ + private $stopOnDefect; + /** + * @var bool + */ + private $stopOnError; + /** + * @var bool + */ + private $stopOnFailure; + /** + * @var bool + */ + private $stopOnWarning; + /** + * @var bool + */ + private $stopOnIncomplete; + /** + * @var bool + */ + private $stopOnRisky; + /** + * @var bool + */ + private $stopOnSkipped; + /** + * @var ?string + */ + private $extensionsDirectory; + /** + * @var ?string + * + * @deprecated see https://github.com/sebastianbergmann/phpunit/issues/4039 + */ + private $testSuiteLoaderClass; + /** + * @var ?string + * + * @deprecated see https://github.com/sebastianbergmann/phpunit/issues/4039 + */ + private $testSuiteLoaderFile; + /** + * @var ?string + */ + private $printerClass; + /** + * @var ?string + */ + private $printerFile; + /** + * @var bool + */ + private $beStrictAboutChangesToGlobalState; + /** + * @var bool + */ + private $beStrictAboutOutputDuringTests; + /** + * @var bool + */ + private $beStrictAboutResourceUsageDuringSmallTests; + /** + * @var bool + */ + private $beStrictAboutTestsThatDoNotTestAnything; + /** + * @var bool + */ + private $beStrictAboutTodoAnnotatedTests; + /** + * @var bool + */ + private $beStrictAboutCoversAnnotation; + /** + * @var bool + */ + private $enforceTimeLimit; + /** + * @var int + */ + private $defaultTimeLimit; + /** + * @var int + */ + private $timeoutForSmallTests; + /** + * @var int + */ + private $timeoutForMediumTests; + /** + * @var int + */ + private $timeoutForLargeTests; + /** + * @var ?string + */ + private $defaultTestSuite; + /** + * @var int + */ + private $executionOrder; + /** + * @var bool + */ + private $resolveDependencies; + /** + * @var bool + */ + private $defectsFirst; + /** + * @var bool + */ + private $backupGlobals; + /** + * @var bool + */ + private $backupStaticAttributes; + /** + * @var bool + */ + private $registerMockObjectsFromTestArgumentsRecursively; + /** + * @var bool + */ + private $conflictBetweenPrinterClassAndTestdox; + public function __construct(bool $cacheResult, ?string $cacheResultFile, $columns, string $colors, bool $stderr, bool $noInteraction, bool $verbose, bool $reverseDefectList, bool $convertDeprecationsToExceptions, bool $convertErrorsToExceptions, bool $convertNoticesToExceptions, bool $convertWarningsToExceptions, bool $forceCoversAnnotation, ?string $bootstrap, bool $processIsolation, bool $failOnEmptyTestSuite, bool $failOnIncomplete, bool $failOnRisky, bool $failOnSkipped, bool $failOnWarning, bool $stopOnDefect, bool $stopOnError, bool $stopOnFailure, bool $stopOnWarning, bool $stopOnIncomplete, bool $stopOnRisky, bool $stopOnSkipped, ?string $extensionsDirectory, ?string $testSuiteLoaderClass, ?string $testSuiteLoaderFile, ?string $printerClass, ?string $printerFile, bool $beStrictAboutChangesToGlobalState, bool $beStrictAboutOutputDuringTests, bool $beStrictAboutResourceUsageDuringSmallTests, bool $beStrictAboutTestsThatDoNotTestAnything, bool $beStrictAboutTodoAnnotatedTests, bool $beStrictAboutCoversAnnotation, bool $enforceTimeLimit, int $defaultTimeLimit, int $timeoutForSmallTests, int $timeoutForMediumTests, int $timeoutForLargeTests, ?string $defaultTestSuite, int $executionOrder, bool $resolveDependencies, bool $defectsFirst, bool $backupGlobals, bool $backupStaticAttributes, bool $registerMockObjectsFromTestArgumentsRecursively, bool $conflictBetweenPrinterClassAndTestdox) { - if ($actual === '') { - throw new \PHPUnit\Util\Xml\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 \PHPUnit\Util\Xml\Exception(sprintf('Could not load "%s".%s', $filename, $message !== '' ? "\n" . $message : '')); - } - if ($message === '') { - $message = 'Could not load XML for unknown reason'; - } - throw new \PHPUnit\Util\Xml\Exception($message); - } - return $document; + $this->cacheResult = $cacheResult; + $this->cacheResultFile = $cacheResultFile; + $this->columns = $columns; + $this->colors = $colors; + $this->stderr = $stderr; + $this->noInteraction = $noInteraction; + $this->verbose = $verbose; + $this->reverseDefectList = $reverseDefectList; + $this->convertDeprecationsToExceptions = $convertDeprecationsToExceptions; + $this->convertErrorsToExceptions = $convertErrorsToExceptions; + $this->convertNoticesToExceptions = $convertNoticesToExceptions; + $this->convertWarningsToExceptions = $convertWarningsToExceptions; + $this->forceCoversAnnotation = $forceCoversAnnotation; + $this->bootstrap = $bootstrap; + $this->processIsolation = $processIsolation; + $this->failOnEmptyTestSuite = $failOnEmptyTestSuite; + $this->failOnIncomplete = $failOnIncomplete; + $this->failOnRisky = $failOnRisky; + $this->failOnSkipped = $failOnSkipped; + $this->failOnWarning = $failOnWarning; + $this->stopOnDefect = $stopOnDefect; + $this->stopOnError = $stopOnError; + $this->stopOnFailure = $stopOnFailure; + $this->stopOnWarning = $stopOnWarning; + $this->stopOnIncomplete = $stopOnIncomplete; + $this->stopOnRisky = $stopOnRisky; + $this->stopOnSkipped = $stopOnSkipped; + $this->extensionsDirectory = $extensionsDirectory; + $this->testSuiteLoaderClass = $testSuiteLoaderClass; + $this->testSuiteLoaderFile = $testSuiteLoaderFile; + $this->printerClass = $printerClass; + $this->printerFile = $printerFile; + $this->beStrictAboutChangesToGlobalState = $beStrictAboutChangesToGlobalState; + $this->beStrictAboutOutputDuringTests = $beStrictAboutOutputDuringTests; + $this->beStrictAboutResourceUsageDuringSmallTests = $beStrictAboutResourceUsageDuringSmallTests; + $this->beStrictAboutTestsThatDoNotTestAnything = $beStrictAboutTestsThatDoNotTestAnything; + $this->beStrictAboutTodoAnnotatedTests = $beStrictAboutTodoAnnotatedTests; + $this->beStrictAboutCoversAnnotation = $beStrictAboutCoversAnnotation; + $this->enforceTimeLimit = $enforceTimeLimit; + $this->defaultTimeLimit = $defaultTimeLimit; + $this->timeoutForSmallTests = $timeoutForSmallTests; + $this->timeoutForMediumTests = $timeoutForMediumTests; + $this->timeoutForLargeTests = $timeoutForLargeTests; + $this->defaultTestSuite = $defaultTestSuite; + $this->executionOrder = $executionOrder; + $this->resolveDependencies = $resolveDependencies; + $this->defectsFirst = $defectsFirst; + $this->backupGlobals = $backupGlobals; + $this->backupStaticAttributes = $backupStaticAttributes; + $this->registerMockObjectsFromTestArgumentsRecursively = $registerMockObjectsFromTestArgumentsRecursively; + $this->conflictBetweenPrinterClassAndTestdox = $conflictBetweenPrinterClassAndTestdox; } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Util\Xml; - -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - * - * @psalm-immutable - */ -abstract class SchemaDetectionResult -{ - public function detected() : bool + public function cacheResult() : bool { - return \false; + return $this->cacheResult; } /** - * @throws Exception + * @psalm-assert-if-true !null $this->cacheResultFile */ - public function version() : string + public function hasCacheResultFile() : bool { - throw new \PHPUnit\Util\Xml\Exception('No supported schema was detected'); + return $this->cacheResultFile !== null; } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Util\Xml; - -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class SchemaDetector -{ /** * @throws Exception */ - public function detect(string $filename) : \PHPUnit\Util\Xml\SchemaDetectionResult + public function cacheResultFile() : string { - $document = (new \PHPUnit\Util\Xml\Loader())->loadFile($filename, \false, \true, \true); - foreach (['9.2', '8.5'] as $candidate) { - $schema = (new \PHPUnit\Util\Xml\SchemaFinder())->find($candidate); - if (!(new \PHPUnit\Util\Xml\Validator())->validate($document, $schema)->hasValidationErrors()) { - return new \PHPUnit\Util\Xml\SuccessfulSchemaDetectionResult($candidate); - } + if (!$this->hasCacheResultFile()) { + throw new \PHPUnit\TextUI\XmlConfiguration\Exception('Cache result file is not configured'); } - return new \PHPUnit\Util\Xml\FailedSchemaDetectionResult(); + return (string) $this->cacheResultFile; } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Util\Xml; - -use function defined; -use function is_file; -use function sprintf; -use PHPUnit\Runner\Version; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class SchemaFinder -{ - /** - * @throws Exception - */ - public function find(string $version) : string + public function columns() { - if ($version === Version::series()) { - $filename = $this->path() . 'phpunit.xsd'; - } else { - $filename = $this->path() . 'schema/' . $version . '.xsd'; - } - if (!is_file($filename)) { - throw new \PHPUnit\Util\Xml\Exception(sprintf('Schema for PHPUnit %s is not available', $version)); - } - return $filename; + return $this->columns; } - private function path() : string + public function colors() : string { - if (defined('__PHPUNIT_PHAR_ROOT__')) { - return __PHPUNIT_PHAR_ROOT__ . '/'; - } - return __DIR__ . '/../../../'; + return $this->colors; } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Util\Xml; - -use function count; -use ArrayIterator; -use Countable; -use DOMNode; -use DOMNodeList; -use IteratorAggregate; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - * - * @template-implements IteratorAggregate - */ -final class SnapshotNodeList implements Countable, IteratorAggregate -{ - /** - * @var DOMNode[] - */ - private $nodes = []; - public static function fromNodeList(DOMNodeList $list) : self + public function stderr() : bool { - $snapshot = new self(); - foreach ($list as $node) { - $snapshot->nodes[] = $node; - } - return $snapshot; + return $this->stderr; } - public function count() : int + public function noInteraction() : bool { - return count($this->nodes); + return $this->noInteraction; } - public function getIterator() : ArrayIterator + public function verbose() : bool { - return new ArrayIterator($this->nodes); + return $this->verbose; } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Util\Xml; - -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - * - * @psalm-immutable - */ -final class SuccessfulSchemaDetectionResult extends \PHPUnit\Util\Xml\SchemaDetectionResult -{ - /** - * @var string - */ - private $version; - public function __construct(string $version) + public function reverseDefectList() : bool { - $this->version = $version; + return $this->reverseDefectList; } - public function detected() : bool + public function convertDeprecationsToExceptions() : bool { - return \true; + return $this->convertDeprecationsToExceptions; } - public function version() : string + public function convertErrorsToExceptions() : bool { - return $this->version; + return $this->convertErrorsToExceptions; + } + public function convertNoticesToExceptions() : bool + { + return $this->convertNoticesToExceptions; + } + public function convertWarningsToExceptions() : bool + { + return $this->convertWarningsToExceptions; + } + public function forceCoversAnnotation() : bool + { + return $this->forceCoversAnnotation; + } + /** + * @psalm-assert-if-true !null $this->bootstrap + */ + public function hasBootstrap() : bool + { + return $this->bootstrap !== null; + } + /** + * @throws Exception + */ + public function bootstrap() : string + { + if (!$this->hasBootstrap()) { + throw new \PHPUnit\TextUI\XmlConfiguration\Exception('Bootstrap script is not configured'); + } + return (string) $this->bootstrap; + } + public function processIsolation() : bool + { + return $this->processIsolation; + } + public function failOnEmptyTestSuite() : bool + { + return $this->failOnEmptyTestSuite; + } + public function failOnIncomplete() : bool + { + return $this->failOnIncomplete; + } + public function failOnRisky() : bool + { + return $this->failOnRisky; + } + public function failOnSkipped() : bool + { + return $this->failOnSkipped; + } + public function failOnWarning() : bool + { + return $this->failOnWarning; + } + public function stopOnDefect() : bool + { + return $this->stopOnDefect; + } + public function stopOnError() : bool + { + return $this->stopOnError; + } + public function stopOnFailure() : bool + { + return $this->stopOnFailure; + } + public function stopOnWarning() : bool + { + return $this->stopOnWarning; + } + public function stopOnIncomplete() : bool + { + return $this->stopOnIncomplete; + } + public function stopOnRisky() : bool + { + return $this->stopOnRisky; + } + public function stopOnSkipped() : bool + { + return $this->stopOnSkipped; + } + /** + * @psalm-assert-if-true !null $this->extensionsDirectory + */ + public function hasExtensionsDirectory() : bool + { + return $this->extensionsDirectory !== null; + } + /** + * @throws Exception + */ + public function extensionsDirectory() : string + { + if (!$this->hasExtensionsDirectory()) { + throw new \PHPUnit\TextUI\XmlConfiguration\Exception('Extensions directory is not configured'); + } + return (string) $this->extensionsDirectory; + } + /** + * @psalm-assert-if-true !null $this->testSuiteLoaderClass + * + * @deprecated see https://github.com/sebastianbergmann/phpunit/issues/4039 + */ + public function hasTestSuiteLoaderClass() : bool + { + return $this->testSuiteLoaderClass !== null; + } + /** + * @throws Exception + * + * @deprecated see https://github.com/sebastianbergmann/phpunit/issues/4039 + */ + public function testSuiteLoaderClass() : string + { + if (!$this->hasTestSuiteLoaderClass()) { + throw new \PHPUnit\TextUI\XmlConfiguration\Exception('TestSuiteLoader class is not configured'); + } + return (string) $this->testSuiteLoaderClass; + } + /** + * @psalm-assert-if-true !null $this->testSuiteLoaderFile + * + * @deprecated see https://github.com/sebastianbergmann/phpunit/issues/4039 + */ + public function hasTestSuiteLoaderFile() : bool + { + return $this->testSuiteLoaderFile !== null; + } + /** + * @throws Exception + * + * @deprecated see https://github.com/sebastianbergmann/phpunit/issues/4039 + */ + public function testSuiteLoaderFile() : string + { + if (!$this->hasTestSuiteLoaderFile()) { + throw new \PHPUnit\TextUI\XmlConfiguration\Exception('TestSuiteLoader sourcecode file is not configured'); + } + return (string) $this->testSuiteLoaderFile; + } + /** + * @psalm-assert-if-true !null $this->printerClass + */ + public function hasPrinterClass() : bool + { + return $this->printerClass !== null; + } + /** + * @throws Exception + */ + public function printerClass() : string + { + if (!$this->hasPrinterClass()) { + throw new \PHPUnit\TextUI\XmlConfiguration\Exception('ResultPrinter class is not configured'); + } + return (string) $this->printerClass; + } + /** + * @psalm-assert-if-true !null $this->printerFile + */ + public function hasPrinterFile() : bool + { + return $this->printerFile !== null; + } + /** + * @throws Exception + */ + public function printerFile() : string + { + if (!$this->hasPrinterFile()) { + throw new \PHPUnit\TextUI\XmlConfiguration\Exception('ResultPrinter sourcecode file is not configured'); + } + return (string) $this->printerFile; + } + public function beStrictAboutChangesToGlobalState() : bool + { + return $this->beStrictAboutChangesToGlobalState; + } + public function beStrictAboutOutputDuringTests() : bool + { + return $this->beStrictAboutOutputDuringTests; + } + public function beStrictAboutResourceUsageDuringSmallTests() : bool + { + return $this->beStrictAboutResourceUsageDuringSmallTests; + } + public function beStrictAboutTestsThatDoNotTestAnything() : bool + { + return $this->beStrictAboutTestsThatDoNotTestAnything; + } + public function beStrictAboutTodoAnnotatedTests() : bool + { + return $this->beStrictAboutTodoAnnotatedTests; + } + public function beStrictAboutCoversAnnotation() : bool + { + return $this->beStrictAboutCoversAnnotation; + } + public function enforceTimeLimit() : bool + { + return $this->enforceTimeLimit; + } + public function defaultTimeLimit() : int + { + return $this->defaultTimeLimit; + } + public function timeoutForSmallTests() : int + { + return $this->timeoutForSmallTests; + } + public function timeoutForMediumTests() : int + { + return $this->timeoutForMediumTests; + } + public function timeoutForLargeTests() : int + { + return $this->timeoutForLargeTests; + } + /** + * @psalm-assert-if-true !null $this->defaultTestSuite + */ + public function hasDefaultTestSuite() : bool + { + return $this->defaultTestSuite !== null; + } + /** + * @throws Exception + */ + public function defaultTestSuite() : string + { + if (!$this->hasDefaultTestSuite()) { + throw new \PHPUnit\TextUI\XmlConfiguration\Exception('Default test suite is not configured'); + } + return (string) $this->defaultTestSuite; + } + public function executionOrder() : int + { + return $this->executionOrder; + } + public function resolveDependencies() : bool + { + return $this->resolveDependencies; + } + public function defectsFirst() : bool + { + return $this->defectsFirst; + } + public function backupGlobals() : bool + { + return $this->backupGlobals; + } + public function backupStaticAttributes() : bool + { + return $this->backupStaticAttributes; + } + public function registerMockObjectsFromTestArgumentsRecursively() : bool + { + return $this->registerMockObjectsFromTestArgumentsRecursively; + } + public function conflictBetweenPrinterClassAndTestdox() : bool + { + return $this->conflictBetweenPrinterClassAndTestdox; } } > + * @var string */ - private $validationErrors = []; + private $path; /** - * @psalm-param array $errors + * @var string */ - public static function fromArray(array $errors) : self + private $prefix; + /** + * @var string + */ + private $suffix; + /** + * @var string + */ + private $phpVersion; + /** + * @var VersionComparisonOperator + */ + private $phpVersionOperator; + public function __construct(string $path, string $prefix, string $suffix, string $phpVersion, VersionComparisonOperator $phpVersionOperator) { - $validationErrors = []; - foreach ($errors as $error) { - if (!isset($validationErrors[$error->line])) { - $validationErrors[$error->line] = []; - } - $validationErrors[$error->line][] = trim($error->message); - } - return new self($validationErrors); + $this->path = $path; + $this->prefix = $prefix; + $this->suffix = $suffix; + $this->phpVersion = $phpVersion; + $this->phpVersionOperator = $phpVersionOperator; } - private function __construct(array $validationErrors) + public function path() : string { - $this->validationErrors = $validationErrors; + return $this->path; } - public function hasValidationErrors() : bool + public function prefix() : string { - return !empty($this->validationErrors); + return $this->prefix; } - public function asString() : string + public function suffix() : string { - $buffer = ''; - foreach ($this->validationErrors as $line => $validationErrorsOnLine) { - $buffer .= sprintf(\PHP_EOL . ' Line %d:' . \PHP_EOL, $line); - foreach ($validationErrorsOnLine as $validationError) { - $buffer .= sprintf(' - %s' . \PHP_EOL, $validationError); - } - } - return $buffer; + return $this->suffix; + } + public function phpVersion() : string + { + return $this->phpVersion; + } + public function phpVersionOperator() : VersionComparisonOperator + { + return $this->phpVersionOperator; } } */ -final class Validator +final class TestDirectoryCollection implements Countable, IteratorAggregate { - public function validate(DOMDocument $document, string $xsdFilename) : \PHPUnit\Util\Xml\ValidationResult + /** + * @var TestDirectory[] + */ + private $directories; + /** + * @param TestDirectory[] $directories + */ + public static function fromArray(array $directories) : self { - $originalErrorHandling = libxml_use_internal_errors(\true); - $document->schemaValidateSource(file_get_contents($xsdFilename)); - $errors = libxml_get_errors(); - libxml_clear_errors(); - libxml_use_internal_errors($originalErrorHandling); - return \PHPUnit\Util\Xml\ValidationResult::fromArray($errors); + return new self(...$directories); + } + private function __construct(\PHPUnit\TextUI\XmlConfiguration\TestDirectory ...$directories) + { + $this->directories = $directories; + } + /** + * @return TestDirectory[] + */ + public function asArray() : array + { + return $this->directories; + } + public function count() : int + { + return count($this->directories); + } + public function getIterator() : \PHPUnit\TextUI\XmlConfiguration\TestDirectoryCollectionIterator + { + return new \PHPUnit\TextUI\XmlConfiguration\TestDirectoryCollectionIterator($this); + } + public function isEmpty() : bool + { + return $this->count() === 0; } } */ -final class XmlTestListRenderer +final class TestDirectoryCollectionIterator implements Countable, Iterator { /** - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @var TestDirectory[] */ - public function render(TestSuite $suite) : string + private $directories; + /** + * @var int + */ + private $position; + public function __construct(\PHPUnit\TextUI\XmlConfiguration\TestDirectoryCollection $directories) { - $writer = new XMLWriter(); - $writer->openMemory(); - $writer->setIndent(\true); - $writer->startDocument('1.0', 'UTF-8'); - $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(); - $writer->endDocument(); - return $writer->outputMemory(); + $this->directories = $directories->asArray(); + } + public function count() : int + { + return iterator_count($this); + } + public function rewind() : void + { + $this->position = 0; + } + public function valid() : bool + { + return $this->position < count($this->directories); + } + public function key() : int + { + return $this->position; + } + public function current() : \PHPUnit\TextUI\XmlConfiguration\TestDirectory + { + return $this->directories[$this->position]; + } + public function next() : void + { + $this->position++; } } - - - - - phpunit - phpunit - 9.6.13 - The PHP Unit Testing framework. - - - BSD-3-Clause - - - pkg:composer/phpunit/phpunit@9.6.13 - - - doctrine - instantiator - 1.5.0 - A small, lightweight utility to instantiate objects in PHP without invoking their constructors - - - MIT - - - pkg:composer/doctrine/instantiator@1.5.0 - + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration; + +use PHPUnit\Util\VersionComparisonOperator; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + * + * @psalm-immutable + */ +final class TestFile +{ + /** + * @var string + */ + private $path; + /** + * @var string + */ + private $phpVersion; + /** + * @var VersionComparisonOperator + */ + private $phpVersionOperator; + public function __construct(string $path, string $phpVersion, VersionComparisonOperator $phpVersionOperator) + { + $this->path = $path; + $this->phpVersion = $phpVersion; + $this->phpVersionOperator = $phpVersionOperator; + } + public function path() : string + { + return $this->path; + } + public function phpVersion() : string + { + return $this->phpVersion; + } + public function phpVersionOperator() : VersionComparisonOperator + { + return $this->phpVersionOperator; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration; + +use function count; +use Countable; +use IteratorAggregate; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + * + * @psalm-immutable + * + * @template-implements IteratorAggregate + */ +final class TestFileCollection implements Countable, IteratorAggregate +{ + /** + * @var TestFile[] + */ + private $files; + /** + * @param TestFile[] $files + */ + public static function fromArray(array $files) : self + { + return new self(...$files); + } + private function __construct(\PHPUnit\TextUI\XmlConfiguration\TestFile ...$files) + { + $this->files = $files; + } + /** + * @return TestFile[] + */ + public function asArray() : array + { + return $this->files; + } + public function count() : int + { + return count($this->files); + } + public function getIterator() : \PHPUnit\TextUI\XmlConfiguration\TestFileCollectionIterator + { + return new \PHPUnit\TextUI\XmlConfiguration\TestFileCollectionIterator($this); + } + public function isEmpty() : bool + { + return $this->count() === 0; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration; + +use function count; +use function iterator_count; +use Countable; +use Iterator; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + * + * @template-implements Iterator + */ +final class TestFileCollectionIterator implements Countable, Iterator +{ + /** + * @var TestFile[] + */ + private $files; + /** + * @var int + */ + private $position; + public function __construct(\PHPUnit\TextUI\XmlConfiguration\TestFileCollection $files) + { + $this->files = $files->asArray(); + } + public function count() : int + { + return iterator_count($this); + } + public function rewind() : void + { + $this->position = 0; + } + public function valid() : bool + { + return $this->position < count($this->files); + } + public function key() : int + { + return $this->position; + } + public function current() : \PHPUnit\TextUI\XmlConfiguration\TestFile + { + return $this->files[$this->position]; + } + public function next() : void + { + $this->position++; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration; + +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + * + * @psalm-immutable + */ +final class TestSuite +{ + /** + * @var string + */ + private $name; + /** + * @var TestDirectoryCollection + */ + private $directories; + /** + * @var TestFileCollection + */ + private $files; + /** + * @var FileCollection + */ + private $exclude; + public function __construct(string $name, \PHPUnit\TextUI\XmlConfiguration\TestDirectoryCollection $directories, \PHPUnit\TextUI\XmlConfiguration\TestFileCollection $files, \PHPUnit\TextUI\XmlConfiguration\FileCollection $exclude) + { + $this->name = $name; + $this->directories = $directories; + $this->files = $files; + $this->exclude = $exclude; + } + public function name() : string + { + return $this->name; + } + public function directories() : \PHPUnit\TextUI\XmlConfiguration\TestDirectoryCollection + { + return $this->directories; + } + public function files() : \PHPUnit\TextUI\XmlConfiguration\TestFileCollection + { + return $this->files; + } + public function exclude() : \PHPUnit\TextUI\XmlConfiguration\FileCollection + { + return $this->exclude; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration; + +use function count; +use Countable; +use IteratorAggregate; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + * + * @psalm-immutable + * + * @template-implements IteratorAggregate + */ +final class TestSuiteCollection implements Countable, IteratorAggregate +{ + /** + * @var TestSuite[] + */ + private $testSuites; + /** + * @param TestSuite[] $testSuites + */ + public static function fromArray(array $testSuites) : self + { + return new self(...$testSuites); + } + private function __construct(\PHPUnit\TextUI\XmlConfiguration\TestSuite ...$testSuites) + { + $this->testSuites = $testSuites; + } + /** + * @return TestSuite[] + */ + public function asArray() : array + { + return $this->testSuites; + } + public function count() : int + { + return count($this->testSuites); + } + public function getIterator() : \PHPUnit\TextUI\XmlConfiguration\TestSuiteCollectionIterator + { + return new \PHPUnit\TextUI\XmlConfiguration\TestSuiteCollectionIterator($this); + } + public function isEmpty() : bool + { + return $this->count() === 0; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration; + +use function count; +use function iterator_count; +use Countable; +use Iterator; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + * + * @template-implements Iterator + */ +final class TestSuiteCollectionIterator implements Countable, Iterator +{ + /** + * @var TestSuite[] + */ + private $testSuites; + /** + * @var int + */ + private $position; + public function __construct(\PHPUnit\TextUI\XmlConfiguration\TestSuiteCollection $testSuites) + { + $this->testSuites = $testSuites->asArray(); + } + public function count() : int + { + return iterator_count($this); + } + public function rewind() : void + { + $this->position = 0; + } + public function valid() : bool + { + return $this->position < count($this->testSuites); + } + public function key() : int + { + return $this->position; + } + public function current() : \PHPUnit\TextUI\XmlConfiguration\TestSuite + { + return $this->testSuites[$this->position]; + } + public function next() : void + { + $this->position++; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Util\Annotation; + +use const JSON_ERROR_NONE; +use const PREG_OFFSET_CAPTURE; +use function array_filter; +use function array_key_exists; +use function array_map; +use function array_merge; +use function array_pop; +use function array_slice; +use function array_values; +use function count; +use function explode; +use function file; +use function implode; +use function is_array; +use function is_int; +use function json_decode; +use function json_last_error; +use function json_last_error_msg; +use function preg_match; +use function preg_match_all; +use function preg_replace; +use function preg_split; +use function realpath; +use function rtrim; +use function sprintf; +use function str_replace; +use function strlen; +use function strpos; +use function strtolower; +use function substr; +use function trim; +use PHPUnitPHAR\PharIo\Version\VersionConstraintParser; +use PHPUnit\Framework\InvalidDataProviderException; +use PHPUnit\Framework\SkippedTestError; +use PHPUnit\Framework\Warning; +use PHPUnit\Util\Exception; +use PHPUnit\Util\InvalidDataSetException; +use ReflectionClass; +use ReflectionException; +use ReflectionFunctionAbstract; +use ReflectionMethod; +use Reflector; +use Traversable; +/** + * 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+(?PPHP(?:Unit)?)\\s+(?P[<>=!]{0,2})\\s*(?P[\\d\\.-]+(dev|(RC|alpha|beta)[\\d\\.])?)[ \\t]*\\r?$/m'; + private const REGEX_REQUIRES_VERSION_CONSTRAINT = '/@requires\\s+(?PPHP(?:Unit)?)\\s+(?P[\\d\\t \\-.|~^]+)[ \\t]*\\r?$/m'; + private const REGEX_REQUIRES_OS = '/@requires\\s+(?POS(?:FAMILY)?)\\s+(?P.+?)[ \\t]*\\r?$/m'; + private const REGEX_REQUIRES_SETTING = '/@requires\\s+(?Psetting)\\s+(?P([^ ]+?))\\s*(?P[\\w\\.-]+[\\w\\.]?)?[ \\t]*\\r?$/m'; + private const REGEX_REQUIRES = '/@requires\\s+(?Pfunction|extension)\\s+(?P([^\\s<>=!]+))\\s*(?P[<>=!]{0,2})\\s*(?P[\\d\\.-]+[\\d\\.]?)?[ \\t]*\\r?$/m'; + private const REGEX_TEST_WITH = '/@testWith\\s+/'; + /** @var string */ + private $docComment; + /** @var bool */ + private $isMethod; + /** @var array> pre-parsed annotations indexed by name and occurrence index */ + private $symbolAnnotations; + /** + * @var null|array + * + * @psalm-var null|(array{ + * __OFFSET: array&array{__FILE: string}, + * setting?: array, + * extension_versions?: array + * }&array< + * string, + * string|array{version: string, operator: string}|array{constraint: string}|array + * >) + */ + 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> $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&array{__FILE: string}, + * setting?: array, + * extension_versions?: array + * }&array< + * string, + * string|array{version: string, operator: string}|array{constraint: string}|array + * > + * + * @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)]; + // Trim docblock markers, split it into lines and rewind offset to start of docblock + $lines = preg_replace(['#^/\\*{2}#', '#\\*/$#'], '', 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 (\PHPUnitPHAR\PharIo\Version\Exception $e) { + 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])); + } + /** + * 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 InvalidDataSetException(sprintf('Data set %s is invalid.', is_int($key) ? '#' . $key : '"' . $key . '"')); + } + } + return $data; + } + /** + * @psalm-return array + */ + 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[A-Za-z_-]+)(?:[ \\t]+(?P.*?))?[ \\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); + } + public function isToBeExecutedAsPreCondition() : bool + { + return 1 === preg_match('/@preCondition\\b/', $this->docComment); + } + public function isToBeExecutedAsPostCondition() : bool + { + return 1 === preg_match('/@postCondition\\b/', $this->docComment); + } + 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); + // @codeCoverageIgnoreStart + } catch (ReflectionException $e) { + throw new Exception($e->getMessage(), $e->getCode(), $e); + // @codeCoverageIgnoreEnd + } + 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> */ + 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[A-Za-z_-]+)(?:[ \\t]+(?P.*?))?[ \\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(static function (ReflectionClass $trait) : array { + return self::parseDocBlock((string) $trait->getDocComment()); + }, array_values($reflector->getTraits()))); + } + return array_merge($annotations, self::parseDocBlock((string) $reflector->getDocComment())); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Util\Annotation; + +use function array_key_exists; +use PHPUnit\Util\Exception; +use ReflectionClass; +use ReflectionException; +use ReflectionMethod; +/** + * 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 indexed by class name */ + private $classDocBlocks = []; + /** @var array> 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) : \PHPUnit\Util\Annotation\DocBlock + { + if (array_key_exists($class, $this->classDocBlocks)) { + return $this->classDocBlocks[$class]; + } + try { + $reflection = new ReflectionClass($class); + // @codeCoverageIgnoreStart + } catch (ReflectionException $e) { + throw new Exception($e->getMessage(), $e->getCode(), $e); + } + // @codeCoverageIgnoreEnd + return $this->classDocBlocks[$class] = \PHPUnit\Util\Annotation\DocBlock::ofClass($reflection); + } + /** + * @throws Exception + * + * @psalm-param class-string $classInHierarchy + */ + public function forMethod(string $classInHierarchy, string $method) : \PHPUnit\Util\Annotation\DocBlock + { + if (isset($this->methodDocBlocks[$classInHierarchy][$method])) { + return $this->methodDocBlocks[$classInHierarchy][$method]; + } + try { + $reflection = new ReflectionMethod($classInHierarchy, $method); + // @codeCoverageIgnoreStart + } catch (ReflectionException $e) { + throw new Exception($e->getMessage(), $e->getCode(), $e); + } + // @codeCoverageIgnoreEnd + return $this->methodDocBlocks[$classInHierarchy][$method] = \PHPUnit\Util\Annotation\DocBlock::ofMethod($reflection, $classInHierarchy); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Util; + +/** + * @deprecated Use ExcludeList instead + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class Blacklist +{ + public static function addDirectory(string $directory) : void + { + \PHPUnit\Util\ExcludeList::addDirectory($directory); + } + /** + * @throws Exception + * + * @return string[] + */ + public function getBlacklistedDirectories() : array + { + return (new \PHPUnit\Util\ExcludeList())->getExcludedDirectories(); + } + /** + * @throws Exception + */ + public function isBlacklisted(string $file) : bool + { + return (new \PHPUnit\Util\ExcludeList())->isExcluded($file); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Util; + +use Throwable; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class Cloner +{ + /** + * @psalm-template OriginalType + * + * @psalm-param OriginalType $original + * + * @psalm-return OriginalType + */ + public static function clone(object $original) : object + { + try { + return clone $original; + } catch (Throwable $t) { + return $original; + } + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Util; + +use const DIRECTORY_SEPARATOR; +use function array_keys; +use function array_map; +use function array_values; +use function count; +use function explode; +use function implode; +use function min; +use function preg_replace; +use function preg_replace_callback; +use function sprintf; +use function strtr; +use function trim; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class Color +{ + /** + * @var array + */ + private const WHITESPACE_MAP = [' ' => '·', "\t" => '⇥']; + /** + * @var array + */ + private const WHITESPACE_EOL_MAP = [' ' => '·', "\t" => '⇥', "\n" => '↵', "\r" => '⟵']; + /** + * @var array + */ + 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 "\x1b[2m{$buffer}\x1b[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 = ["/\x1b\\[22m\x1b\\[2m/" => '', "/\x1b\\[([^m]*)m\x1b\\[([1-9][0-9;]*)m/" => "\x1b[\$1;\$2m", "/(\x1b\\[[^m]*m)+(\x1b\\[0m)/" => '$2']; + return preg_replace(array_keys($patterns), array_values($patterns), $buffer); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Util; + +use const E_DEPRECATED; +use const E_NOTICE; +use const E_STRICT; +use const E_USER_DEPRECATED; +use const E_USER_NOTICE; +use const E_USER_WARNING; +use const E_WARNING; +use function error_reporting; +use function restore_error_handler; +use function set_error_handler; +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(); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Util; + +use RuntimeException; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class Exception extends RuntimeException implements \PHPUnit\Exception +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Util; + +use const DIRECTORY_SEPARATOR; +use function class_exists; +use function defined; +use function dirname; +use function is_dir; +use function realpath; +use function sprintf; +use function strpos; +use function sys_get_temp_dir; +use PHPUnitPHAR\Composer\Autoload\ClassLoader; +use PHPUnitPHAR\DeepCopy\DeepCopy; +use PHPUnitPHAR\Doctrine\Instantiator\Instantiator; +use PHPUnitPHAR\PharIo\Manifest\Manifest; +use PHPUnitPHAR\PharIo\Version\Version as PharIoVersion; +use PHPUnitPHAR\PhpParser\Parser; +use PHPUnit\Framework\TestCase; +use ReflectionClass; +use PHPUnitPHAR\SebastianBergmann\CliParser\Parser as CliParser; +use PHPUnitPHAR\SebastianBergmann\CodeCoverage\CodeCoverage; +use PHPUnitPHAR\SebastianBergmann\CodeUnit\CodeUnit; +use PHPUnitPHAR\SebastianBergmann\CodeUnitReverseLookup\Wizard; +use PHPUnitPHAR\SebastianBergmann\Comparator\Comparator; +use PHPUnitPHAR\SebastianBergmann\Complexity\Calculator; +use PHPUnitPHAR\SebastianBergmann\Diff\Diff; +use PHPUnitPHAR\SebastianBergmann\Environment\Runtime; +use PHPUnitPHAR\SebastianBergmann\Exporter\Exporter; +use PHPUnitPHAR\SebastianBergmann\FileIterator\Facade as FileIteratorFacade; +use PHPUnitPHAR\SebastianBergmann\GlobalState\Snapshot; +use PHPUnitPHAR\SebastianBergmann\Invoker\Invoker; +use PHPUnitPHAR\SebastianBergmann\LinesOfCode\Counter; +use PHPUnitPHAR\SebastianBergmann\ObjectEnumerator\Enumerator; +use PHPUnitPHAR\SebastianBergmann\ObjectReflector\ObjectReflector; +use PHPUnitPHAR\SebastianBergmann\RecursionContext\Context; +use PHPUnitPHAR\SebastianBergmann\ResourceOperations\ResourceOperations; +use PHPUnitPHAR\SebastianBergmann\Template\Template; +use PHPUnitPHAR\SebastianBergmann\Timer\Timer; +use PHPUnitPHAR\SebastianBergmann\Type\TypeName; +use PHPUnitPHAR\SebastianBergmann\Version; +use PHPUnitPHAR\TheSeer\Tokenizer\Tokenizer; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class ExcludeList +{ + /** + * @var array + */ + private const EXCLUDED_CLASS_NAMES = [ + // composer + ClassLoader::class => 1, + // doctrine/instantiator + Instantiator::class => 1, + // myclabs/deepcopy + DeepCopy::class => 1, + // nikic/php-parser + Parser::class => 1, + // phar-io/manifest + Manifest::class => 1, + // phar-io/version + PharIoVersion::class => 1, + // phpdocumentor/type-resolver + \PHPUnit\Util\Type::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 + Template::class => 1, + // phpunit/php-timer + Timer::class => 1, + // sebastian/cli-parser + CliParser::class => 1, + // sebastian/code-unit + CodeUnit::class => 1, + // sebastian/code-unit-reverse-lookup + Wizard::class => 1, + // sebastian/comparator + Comparator::class => 1, + // sebastian/complexity + Calculator::class => 1, + // sebastian/diff + Diff::class => 1, + // sebastian/environment + Runtime::class => 1, + // sebastian/exporter + Exporter::class => 1, + // sebastian/global-state + Snapshot::class => 1, + // sebastian/lines-of-code + Counter::class => 1, + // sebastian/object-enumerator + Enumerator::class => 1, + // sebastian/object-reflector + ObjectReflector::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, + ]; + /** + * @var string[] + */ + private static $directories = []; + /** + * @var bool + */ + private static $initialized = \false; + public static function addDirectory(string $directory) : void + { + if (!is_dir($directory)) { + throw new \PHPUnit\Util\Exception(sprintf('"%s" is not a directory', $directory)); + } + self::$directories[] = realpath($directory); + } + /** + * @throws Exception + * + * @return string[] + */ + public function getExcludedDirectories() : array + { + $this->initialize(); + return self::$directories; + } + /** + * @throws Exception + */ + public function isExcluded(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::$initialized) { + return; + } + foreach (self::EXCLUDED_CLASS_NAMES as $className => $parent) { + if (!class_exists($className)) { + continue; + } + $directory = (new ReflectionClass($className))->getFileName(); + 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'; + } + self::$initialized = \true; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Util; + +use const DIRECTORY_SEPARATOR; +use function array_diff; +use function array_keys; +use function fopen; +use function get_defined_vars; +use function sprintf; +use function stream_resolve_include_path; +/** + * @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); + $localFile = __DIR__ . DIRECTORY_SEPARATOR . $filename; + if (!$includePathFilename || $includePathFilename === $localFile || !self::isReadable($includePathFilename)) { + throw new \PHPUnit\Util\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()); + /** + * @noinspection PhpIncludeInspection + * + * @psalm-suppress UnresolvableInclude + */ + 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; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Util; + +use const DIRECTORY_SEPARATOR; +use function is_dir; +use function mkdir; +use function str_replace; +/** + * @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)); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Util; + +use function array_unshift; +use function defined; +use function in_array; +use function is_file; +use function realpath; +use function sprintf; +use function strpos; +use PHPUnit\Framework\Exception; +use PHPUnit\Framework\SyntheticError; +use Throwable; +/** + * @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 + { + $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]); + } + $prefix = defined('__PHPUNIT_PHAR_ROOT__') ? __PHPUNIT_PHAR_ROOT__ : \false; + $excludeList = new \PHPUnit\Util\ExcludeList(); + foreach ($eTrace as $frame) { + if (self::shouldPrintFrame($frame, $prefix, $excludeList)) { + $filteredStacktrace .= sprintf("%s:%s\n", $frame['file'], $frame['line'] ?? '?'); + } + } + return $filteredStacktrace; + } + private static function shouldPrintFrame(array $frame, $prefix, \PHPUnit\Util\ExcludeList $excludeList) : bool + { + if (!isset($frame['file'])) { + return \false; + } + $file = $frame['file']; + $fileIsNotPrefixed = $prefix === \false || strpos($file, $prefix) !== 0; + // @see https://github.com/sebastianbergmann/phpunit/issues/4033 + if (isset($GLOBALS['_SERVER']['SCRIPT_NAME'])) { + $script = realpath($GLOBALS['_SERVER']['SCRIPT_NAME']); + } else { + $script = ''; + } + return is_file($file) && self::fileIsExcluded($file, $excludeList) && $fileIsNotPrefixed && $file !== $script; + } + private static function fileIsExcluded(string $file, \PHPUnit\Util\ExcludeList $excludeList) : bool + { + return (empty($GLOBALS['__PHPUNIT_ISOLATION_EXCLUDE_LIST']) || !in_array($file, $GLOBALS['__PHPUNIT_ISOLATION_EXCLUDE_LIST'], \true)) && !$excludeList->isExcluded($file); + } + private static function frameExists(array $trace, string $file, int $line) : bool + { + foreach ($trace as $frame) { + if (isset($frame['file'], $frame['line']) && $frame['file'] === $file && $frame['line'] === $line) { + return \true; + } + } + return \false; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Util; + +use const PHP_MAJOR_VERSION; +use const PHP_MINOR_VERSION; +use function array_keys; +use function array_reverse; +use function array_shift; +use function defined; +use function get_defined_constants; +use function get_included_files; +use function in_array; +use function ini_get_all; +use function is_array; +use function is_file; +use function is_scalar; +use function preg_match; +use function serialize; +use function sprintf; +use function strpos; +use function strtr; +use function substr; +use function var_export; +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']; + /** + * @psalm-var array> + */ + private const DEPRECATED_INI_SETTINGS = ['7.3' => ['iconv.input_encoding' => \true, 'iconv.output_encoding' => \true, 'iconv.internal_encoding' => \true, 'mbstring.func_overload' => \true, 'mbstring.http_input' => \true, 'mbstring.http_output' => \true, 'mbstring.internal_encoding' => \true, 'string.strip_tags' => \true], '7.4' => ['iconv.input_encoding' => \true, 'iconv.output_encoding' => \true, 'iconv.internal_encoding' => \true, 'mbstring.func_overload' => \true, 'mbstring.http_input' => \true, 'mbstring.http_output' => \true, 'mbstring.internal_encoding' => \true, 'pdo_odbc.db2_instance_name' => \true, 'string.strip_tags' => \true], '8.0' => ['iconv.input_encoding' => \true, 'iconv.output_encoding' => \true, 'iconv.internal_encoding' => \true, 'mbstring.http_input' => \true, 'mbstring.http_output' => \true, 'mbstring.internal_encoding' => \true], '8.1' => ['auto_detect_line_endings' => \true, 'filter.default' => \true, 'iconv.input_encoding' => \true, 'iconv.output_encoding' => \true, 'iconv.internal_encoding' => \true, 'mbstring.http_input' => \true, 'mbstring.http_output' => \true, 'mbstring.internal_encoding' => \true, 'oci8.old_oci_close_semantics' => \true], '8.2' => ['auto_detect_line_endings' => \true, 'filter.default' => \true, 'iconv.input_encoding' => \true, 'iconv.output_encoding' => \true, 'iconv.internal_encoding' => \true, 'mbstring.http_input' => \true, 'mbstring.http_output' => \true, 'mbstring.internal_encoding' => \true, 'oci8.old_oci_close_semantics' => \true], '8.3' => ['auto_detect_line_endings' => \true, 'filter.default' => \true, 'iconv.input_encoding' => \true, 'iconv.output_encoding' => \true, 'iconv.internal_encoding' => \true, 'mbstring.http_input' => \true, 'mbstring.http_output' => \true, 'mbstring.internal_encoding' => \true, 'oci8.old_oci_close_semantics' => \true]]; + /** + * @throws Exception + */ + public static function getIncludedFilesAsString() : string + { + return self::processIncludedFilesAsString(get_included_files()); + } + /** + * @param string[] $files + * + * @throws Exception + */ + public static function processIncludedFilesAsString(array $files) : string + { + $excludeList = new \PHPUnit\Util\ExcludeList(); + $prefix = \false; + $result = ''; + if (defined('__PHPUNIT_PHAR__')) { + $prefix = 'phar://' . __PHPUNIT_PHAR__ . '/'; + } + // Do not process bootstrap script + array_shift($files); + // If bootstrap script was a Composer bin proxy, skip the second entry as well + if (substr(strtr($files[0], '\\', '/'), -24) === '/phpunit/phpunit/phpunit') { + array_shift($files); + } + foreach (array_reverse($files) as $file) { + if (!empty($GLOBALS['__PHPUNIT_ISOLATION_EXCLUDE_LIST']) && in_array($file, $GLOBALS['__PHPUNIT_ISOLATION_EXCLUDE_LIST'], \true)) { + 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 (!$excludeList->isExcluded($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) { + if (self::isIniSettingDeprecated($key)) { + continue; + } + $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])); + } + } + } + $excludeList = self::SUPER_GLOBAL_ARRAYS; + $excludeList[] = 'GLOBALS'; + foreach (array_keys($GLOBALS) as $key) { + if (!$GLOBALS[$key] instanceof Closure && !in_array($key, $excludeList, \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; + } + private static function isIniSettingDeprecated(string $iniSetting) : bool + { + return isset(self::DEPRECATED_INI_SETTINGS[PHP_MAJOR_VERSION . '.' . PHP_MINOR_VERSION][$iniSetting]); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Util; + +use PHPUnit\Exception; +use RuntimeException; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class InvalidDataSetException extends RuntimeException implements Exception +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Util; + +use const JSON_PRETTY_PRINT; +use const JSON_UNESCAPED_SLASHES; +use const JSON_UNESCAPED_UNICODE; +use function count; +use function is_array; +use function is_object; +use function json_decode; +use function json_encode; +use function json_last_error; +use function ksort; +use PHPUnit\Framework\Exception; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class Json +{ + /** + * Prettify json string. + * + * @throws Exception + */ + public static function prettify(string $json) : string + { + $decodedJson = json_decode($json, \false); + if (json_last_error()) { + throw new Exception('Cannot prettify invalid json'); + } + return json_encode($decodedJson, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); + } + /** + * 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); + } + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Util\Log; + +use function class_exists; +use function get_class; +use function method_exists; +use function sprintf; +use function str_replace; +use function trim; +use DOMDocument; +use DOMElement; +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; +use ReflectionClass; +use ReflectionException; +use Throwable; +/** + * @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, 'error'); + $this->testSuiteErrors[$this->testSuiteLevel]++; + } + /** + * A warning occurred. + */ + public function addWarning(Test $test, Warning $e, float $time) : void + { + $this->doAddFault($test, $e, 'warning'); + $this->testSuiteWarnings[$this->testSuiteLevel]++; + } + /** + * A failure occurred. + */ + public function addFailure(Test $test, AssertionFailedError $e, float $time) : void + { + $this->doAddFault($test, $e, '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) { + return; + } + $this->doAddFault($test, $t, '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); + // @codeCoverageIgnoreStart + } catch (ReflectionException $e) { + throw new Exception($e->getMessage(), $e->getCode(), $e); + } + // @codeCoverageIgnoreEnd + $methodName = $test->getName(!$usesDataprovider); + if ($class->hasMethod($methodName)) { + try { + $method = $class->getMethod($methodName); + // @codeCoverageIgnoreStart + } catch (ReflectionException $e) { + throw new Exception($e->getMessage(), $e->getCode(), $e); + } + // @codeCoverageIgnoreEnd + $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, string $type) : void + { + if ($this->currentTestCase === null) { + return; + } + if ($test instanceof SelfDescribing) { + $buffer = $test->toString() . "\n"; + } else { + $buffer = ''; + } + $buffer .= trim(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]++; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Util\Log; + +use function class_exists; +use function count; +use function explode; +use function get_class; +use function getmypid; +use function ini_get; +use function is_bool; +use function is_scalar; +use function method_exists; +use function print_r; +use function round; +use function str_replace; +use function stripos; +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\DefaultResultPrinter; +use PHPUnit\Util\Exception; +use PHPUnit\Util\Filter; +use ReflectionClass; +use ReflectionException; +use PHPUnitPHAR\SebastianBergmann\Comparator\ComparisonFailure; +use Throwable; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class TeamCity extends DefaultResultPrinter +{ + /** + * @var bool + */ + private $isSummaryTestCountPrinted = \false; + /** + * @var string + */ + private $startedTestName; + /** + * @var false|int + */ + private $flowId; + public function printResult(TestResult $result) : void + { + $this->printHeader($result); + $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->write(self::getMessage($e) . \PHP_EOL); + } + /** + * 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(string $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(); + // @codeCoverageIgnoreStart + } catch (ReflectionException $e) { + throw new Exception($e->getMessage(), $e->getCode(), $e); + } + // @codeCoverageIgnoreEnd + } + /** + * @param float $time microseconds + */ + private static function toMilliseconds(float $time) : int + { + return (int) round($time * 1000); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Util\PHP; + +use const DIRECTORY_SEPARATOR; +use const PHP_SAPI; +use function array_keys; +use function array_merge; +use function assert; +use function escapeshellarg; +use function file_exists; +use function file_get_contents; +use function ini_get_all; +use function restore_error_handler; +use function set_error_handler; +use function sprintf; +use function str_replace; +use function strpos; +use function strrpos; +use function substr; +use function trim; +use function unlink; +use function unserialize; +use __PHP_Incomplete_Class; +use ErrorException; +use PHPUnit\Framework\AssertionFailedError; +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 PHPUnitPHAR\SebastianBergmann\Environment\Runtime; +use PHPUnitPHAR\SebastianBergmann\RecursionContext\InvalidArgumentException; +/** + * @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 + */ + protected $env = []; + /** + * @var int + */ + protected $timeout = 0; + public static function factory() : self + { + if (DIRECTORY_SEPARATOR === '\\') { + return new \PHPUnit\Util\PHP\WindowsPhpProcess(); + } + return new \PHPUnit\Util\PHP\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 $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 InvalidArgumentException + */ + public function runTestJob(string $job, Test $test, TestResult $result, string $processResultFile) : void + { + $result->startTest($test); + $processResult = ''; + $_result = $this->runJob($job); + if (file_exists($processResultFile)) { + $processResult = file_get_contents($processResultFile); + @unlink($processResultFile); + } + $this->processChildResult($test, $result, $processResult, $_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. + */ + public abstract 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 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(); + if ($childResult === \false) { + $result->addFailure($test, new AssertionFailedError('Test was run in child process and ended unexpectedly'), $time); + } + } 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, "\x00") + 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; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Util\PHP; + +use function array_merge; +use function fclose; +use function file_put_contents; +use function fread; +use function fwrite; +use function is_array; +use function is_resource; +use function proc_close; +use function proc_open; +use function proc_terminate; +use function rewind; +use function sprintf; +use function stream_get_contents; +use function stream_select; +use function sys_get_temp_dir; +use function tempnam; +use function unlink; +use PHPUnit\Framework\Exception; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +class DefaultPhpProcess extends \PHPUnit\Util\PHP\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 === '' || $line === \false) { + 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]; + } + /** + * @param resource $pipe + */ + 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; + } +} +{driverMethod}($filter), + $filter + ); + + if ({codeCoverageCacheDirectory}) { + $coverage->cacheStaticAnalysis({codeCoverageCacheDirectory}); + } + + $coverage->start(__FILE__); +} + +register_shutdown_function( + function() use ($coverage) { + $output = null; + + if ($coverage) { + $output = $coverage->stop(); + } + + file_put_contents('{coverageFile}', serialize($output)); + } +); + +ob_end_clean(); + +require '{job}'; +{driverMethod}($filter), + $filter + ); + + if ({cachesStaticAnalysis}) { + $codeCoverage->cacheStaticAnalysis(unserialize('{codeCoverageCacheDirectory}')); + } + + $result->setCodeCoverage($codeCoverage); + } + + $result->beStrictAboutTestsThatDoNotTestAnything({isStrictAboutTestsThatDoNotTestAnything}); + $result->beStrictAboutOutputDuringTests({isStrictAboutOutputDuringTests}); + $result->enforceTimeLimit({enforcesTimeLimit}); + $result->beStrictAboutTodoAnnotatedTests({isStrictAboutTodoAnnotatedTests}); + $result->beStrictAboutResourceUsageDuringSmallTests({isStrictAboutResourceUsageDuringSmallTests}); + + $test = new {className}('{name}', unserialize('{data}'), '{dataName}'); + $test->setDependencyInput(unserialize('{dependencyInput}')); + $test->setInIsolation(true); + + ob_end_clean(); + $test->run($result); + $output = ''; + if (!$test->hasExpectationOnOutput()) { + $output = $test->getActualOutput(); + } + + ini_set('xdebug.scream', '0'); + + @rewind(STDOUT); /* @ as not every STDOUT target stream is rewindable */ + if ($stdout = @stream_get_contents(STDOUT)) { + $output = $stdout . $output; + $streamMetaData = stream_get_meta_data(STDOUT); + if (!empty($streamMetaData['stream_type']) && 'STDIO' === $streamMetaData['stream_type']) { + @ftruncate(STDOUT, 0); + @rewind(STDOUT); + } + } + + file_put_contents( + '{processResultFile}', + serialize( + [ + 'testResult' => $test->getResult(), + 'numAssertions' => $test->getNumAssertions(), + 'result' => $result, + 'output' => $output + ] + ) + ); +} + +$configurationFilePath = '{configurationFilePath}'; + +if ('' !== $configurationFilePath) { + $configuration = (new Loader)->load($configurationFilePath); + + (new PhpHandler)->handle($configuration->php()); + + unset($configuration); +} + +function __phpunit_error_handler($errno, $errstr, $errfile, $errline) +{ + return true; +} + +set_error_handler('__phpunit_error_handler'); + +{constants} +{included_files} +{globals} + +restore_error_handler(); + +if (isset($GLOBALS['__PHPUNIT_BOOTSTRAP'])) { + require_once $GLOBALS['__PHPUNIT_BOOTSTRAP']; + unset($GLOBALS['__PHPUNIT_BOOTSTRAP']); +} + +__phpunit_run_isolated_test(); +{driverMethod}($filter), + $filter + ); + + if ({cachesStaticAnalysis}) { + $codeCoverage->cacheStaticAnalysis(unserialize('{codeCoverageCacheDirectory}')); + } + + $result->setCodeCoverage($codeCoverage); + } + + $result->beStrictAboutTestsThatDoNotTestAnything({isStrictAboutTestsThatDoNotTestAnything}); + $result->beStrictAboutOutputDuringTests({isStrictAboutOutputDuringTests}); + $result->enforceTimeLimit({enforcesTimeLimit}); + $result->beStrictAboutTodoAnnotatedTests({isStrictAboutTodoAnnotatedTests}); + $result->beStrictAboutResourceUsageDuringSmallTests({isStrictAboutResourceUsageDuringSmallTests}); + + $test = new {className}('{methodName}', unserialize('{data}'), '{dataName}'); + \assert($test instanceof TestCase); + + $test->setDependencyInput(unserialize('{dependencyInput}')); + $test->setInIsolation(true); + + ob_end_clean(); + $test->run($result); + $output = ''; + if (!$test->hasExpectationOnOutput()) { + $output = $test->getActualOutput(); + } + + ini_set('xdebug.scream', '0'); + + @rewind(STDOUT); /* @ as not every STDOUT target stream is rewindable */ + if ($stdout = @stream_get_contents(STDOUT)) { + $output = $stdout . $output; + $streamMetaData = stream_get_meta_data(STDOUT); + if (!empty($streamMetaData['stream_type']) && 'STDIO' === $streamMetaData['stream_type']) { + @ftruncate(STDOUT, 0); + @rewind(STDOUT); + } + } + + file_put_contents( + '{processResultFile}', + serialize( + [ + 'testResult' => $test->getResult(), + 'numAssertions' => $test->getNumAssertions(), + 'result' => $result, + 'output' => $output + ] + ) + ); +} + +$configurationFilePath = '{configurationFilePath}'; + +if ('' !== $configurationFilePath) { + $configuration = (new Loader)->load($configurationFilePath); + + (new PhpHandler)->handle($configuration->php()); + + unset($configuration); +} + +function __phpunit_error_handler($errno, $errstr, $errfile, $errline) +{ + return true; +} + +set_error_handler('__phpunit_error_handler'); + +{constants} +{included_files} +{globals} + +restore_error_handler(); + +if (isset($GLOBALS['__PHPUNIT_BOOTSTRAP'])) { + require_once $GLOBALS['__PHPUNIT_BOOTSTRAP']; + unset($GLOBALS['__PHPUNIT_BOOTSTRAP']); +} + +__phpunit_run_isolated_test(); + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Util\PHP; + +use const PHP_MAJOR_VERSION; +use function tmpfile; +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 \PHPUnit\Util\PHP\DefaultPhpProcess +{ + public function getCommand(array $settings, ?string $file = null) : string + { + if (PHP_MAJOR_VERSION < 8) { + return '"' . parent::getCommand($settings, $file) . '"'; + } + 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; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Util; + +use const ENT_COMPAT; +use const ENT_SUBSTITUTE; +use const PHP_SAPI; +use function assert; +use function count; +use function dirname; +use function explode; +use function fclose; +use function fopen; +use function fsockopen; +use function fwrite; +use function htmlspecialchars; +use function is_resource; +use function is_string; +use function sprintf; +use function str_replace; +use function strncmp; +use function strpos; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +class Printer +{ + /** + * @psalm-var closed-resource|resource + */ + private $stream; + /** + * @var bool + */ + private $isPhpStream; + /** + * @param null|resource|string $out + * + * @throws Exception + */ + public function __construct($out = null) + { + if (is_resource($out)) { + $this->stream = $out; + return; + } + if (!is_string($out)) { + return; + } + if (strpos($out, 'socket://') === 0) { + $tmp = explode(':', str_replace('socket://', '', $out)); + if (count($tmp) !== 2) { + throw new \PHPUnit\Util\Exception(sprintf('"%s" does not match "socket://hostname:port" format', $out)); + } + $this->stream = fsockopen($tmp[0], (int) $tmp[1]); + return; + } + if (strpos($out, 'php://') === \false && !\PHPUnit\Util\Filesystem::createDirectory(dirname($out))) { + throw new \PHPUnit\Util\Exception(sprintf('Directory "%s" was not created', dirname($out))); + } + $this->stream = fopen($out, 'wb'); + $this->isPhpStream = strncmp($out, 'php://', 6) !== 0; + } + public function write(string $buffer) : void + { + if ($this->stream) { + assert(is_resource($this->stream)); + fwrite($this->stream, $buffer); + } else { + if (PHP_SAPI !== 'cli' && PHP_SAPI !== 'phpdbg') { + $buffer = htmlspecialchars($buffer, ENT_COMPAT | ENT_SUBSTITUTE); + } + print $buffer; + } + } + public function flush() : void + { + if ($this->stream && $this->isPhpStream) { + assert(is_resource($this->stream)); + fclose($this->stream); + } + } +} + + * + * 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\TestCase; +use ReflectionClass; +use ReflectionMethod; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class Reflection +{ + /** + * @psalm-return list + */ + public function publicMethodsInTestClass(ReflectionClass $class) : array + { + return $this->filterMethods($class, ReflectionMethod::IS_PUBLIC); + } + /** + * @psalm-return list + */ + public function methodsInTestClass(ReflectionClass $class) : array + { + return $this->filterMethods($class, null); + } + /** + * @psalm-return list + */ + private function filterMethods(ReflectionClass $class, ?int $filter) : array + { + $methods = []; + // PHP <7.3.5 throw error when null is passed + // to ReflectionClass::getMethods() when strict_types is enabled. + $classMethods = $filter === null ? $class->getMethods() : $class->getMethods($filter); + foreach ($classMethods as $method) { + if ($method->getDeclaringClass()->getName() === TestCase::class) { + continue; + } + if ($method->getDeclaringClass()->getName() === Assert::class) { + continue; + } + $methods[] = $method; + } + return $methods; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Util; + +use function preg_match; +/** + * @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) + { + return \PHPUnit\Util\ErrorHandler::invokeIgnoringWarnings(static function () use($pattern, $subject) { + return preg_match($pattern, $subject); + }); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Util; + +use const PHP_OS; +use const PHP_VERSION; +use function addcslashes; +use function array_flip; +use function array_key_exists; +use function array_merge; +use function array_unique; +use function array_unshift; +use function class_exists; +use function count; +use function explode; +use function extension_loaded; +use function function_exists; +use function get_class; +use function ini_get; +use function interface_exists; +use function is_array; +use function is_int; +use function method_exists; +use function phpversion; +use function preg_match; +use function preg_replace; +use function sprintf; +use function strncmp; +use function strpos; +use function strtolower; +use function trim; +use function version_compare; +use PHPUnit\Framework\CodeCoverageException; +use PHPUnit\Framework\ExecutionOrderDependency; +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 ReflectionClass; +use ReflectionException; +use ReflectionMethod; +use PHPUnitPHAR\SebastianBergmann\CodeUnit\CodeUnitCollection; +use PHPUnitPHAR\SebastianBergmann\CodeUnit\InvalidCodeUnitException; +use PHPUnitPHAR\SebastianBergmann\CodeUnit\Mapper; +use PHPUnitPHAR\SebastianBergmann\Environment\OperatingSystem; +use PHPUnitPHAR\SebastianBergmann\RecursionContext\InvalidArgumentException; +/** + * @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 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 = self::parseTestMethodAnnotations(get_class($test), $test->getName(\false)); + // 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'])) { + // @see https://github.com/sebastianbergmann/phpunit/issues/4947#issuecomment-1084480950 + // 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'])) { + // @see https://github.com/sebastianbergmann/phpunit/issues/4947#issuecomment-1084480950 + // 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 = self::getRequirements($className, $methodName); + $missing = []; + $hint = null; + if (!empty($required['PHP'])) { + $operator = new \PHPUnit\Util\VersionComparisonOperator(empty($required['PHP']['operator']) ? '>=' : $required['PHP']['operator']); + if (!version_compare(PHP_VERSION, $required['PHP']['version'], $operator->asString())) { + $missing[] = sprintf('PHP %s %s is required.', $operator->asString(), $required['PHP']['version']); + $hint = 'PHP'; + } + } elseif (!empty($required['PHP_constraint'])) { + $version = new \PHPUnitPHAR\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 = 'PHP_constraint'; + } + } + if (!empty($required['PHPUnit'])) { + $phpunitVersion = Version::id(); + $operator = new \PHPUnit\Util\VersionComparisonOperator(empty($required['PHPUnit']['operator']) ? '>=' : $required['PHPUnit']['operator']); + if (!version_compare($phpunitVersion, $required['PHPUnit']['version'], $operator->asString())) { + $missing[] = sprintf('PHPUnit %s %s is required.', $operator->asString(), $required['PHPUnit']['version']); + $hint = $hint ?? 'PHPUnit'; + } + } elseif (!empty($required['PHPUnit_constraint'])) { + $phpunitVersion = new \PHPUnitPHAR\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 = new \PHPUnit\Util\VersionComparisonOperator(empty($req['operator']) ? '>=' : $req['operator']); + if ($actualVersion === \false || !version_compare($actualVersion, $req['version'], $operator->asString())) { + $missing[] = sprintf('Extension %s %s %s is required.', $extension, $operator->asString(), $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 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 = null) : array + { + $registry = Registry::getInstance(); + if ($methodName !== null) { + try { + return ['method' => $registry->forMethod($className, $methodName)->symbolAnnotations(), 'class' => $registry->forClassName($className)->symbolAnnotations()]; + } catch (\PHPUnit\Util\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 + * + * @return ExecutionOrderDependency[] + */ + public static function getDependencies(string $className, string $methodName) : array + { + $annotations = self::parseTestMethodAnnotations($className, $methodName); + $dependsAnnotations = $annotations['class']['depends'] ?? []; + if (isset($annotations['method']['depends'])) { + $dependsAnnotations = array_merge($dependsAnnotations, $annotations['method']['depends']); + } + // Normalize dependency name to className::methodName + $dependencies = []; + foreach ($dependsAnnotations as $value) { + $dependencies[] = ExecutionOrderDependency::createFromDependsAnnotation($className, $value); + } + 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; + } + } + } + foreach (['method', 'class'] as $element) { + if (isset($annotations[$element]['covers'])) { + foreach ($annotations[$element]['covers'] as $coversTarget) { + $groups[] = ['__phpunit_covers_' . self::canonicalizeName($coversTarget)]; + } + } + if (isset($annotations[$element]['uses'])) { + foreach ($annotations[$element]['uses'] as $usesTarget) { + $groups[] = ['__phpunit_uses_' . self::canonicalizeName($usesTarget)]; + } + } + } + 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 \PHPUnit\Util\Reflection())->methodsInTestClass(new ReflectionClass($className)) as $method) { + $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->isToBeExecutedAsPreCondition()) { + array_unshift(self::$hookMethods[$className]['preCondition'], $method->getName()); + } + if ($docBlock->isToBeExecutedAsPostCondition()) { + self::$hookMethods[$className]['postCondition'][] = $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 (!$method->isPublic()) { + return \false; + } + 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]); + } + $codeUnits = CodeUnitCollection::fromArray([]); + $mapper = new Mapper(); + 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)); + } + try { + $codeUnits = $codeUnits->mergeWith($mapper->stringToCodeUnits($element)); + } catch (InvalidCodeUnitException $e) { + throw new InvalidCoversTargetException(sprintf('"@%s %s" is invalid', $mode, $element), $e->getCode(), $e); + } + } + return $mapper->codeUnitsToSourceLines($codeUnits); + } + private static function emptyHookMethodsArray() : array + { + return ['beforeClass' => ['setUpBeforeClass'], 'before' => ['setUp'], 'preCondition' => ['assertPreConditions'], 'postCondition' => ['assertPostConditions'], '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; + } + /** + * Trims any extensions from version string that follows after + * the .[.] 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/) + * + * @see 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; + } + private static function canonicalizeName(string $name) : string + { + return strtolower(trim($name, '\\')); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Util\TestDox; + +use const PHP_EOL; +use function array_map; +use function ceil; +use function count; +use function explode; +use function get_class; +use function implode; +use function preg_match; +use function sprintf; +use function strlen; +use function strpos; +use function trim; +use PHPUnit\Framework\Exception; +use PHPUnit\Framework\Test; +use PHPUnit\Framework\TestCase; +use PHPUnit\Framework\TestFailure; +use PHPUnit\Framework\TestResult; +use PHPUnit\Runner\BaseTestRunner; +use PHPUnit\Runner\PhptTestCase; +use PHPUnit\Util\Color; +use PHPUnit\Util\Filter; +use PHPUnitPHAR\SebastianBergmann\RecursionContext\InvalidArgumentException; +use PHPUnitPHAR\SebastianBergmann\Timer\ResourceUsageFormatter; +use PHPUnitPHAR\SebastianBergmann\Timer\Timer; +use Throwable; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +class CliTestDoxPrinter extends \PHPUnit\Util\TestDox\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 = [" \x1b[36m◐\x1b[0m running tests", " \x1b[36m◓\x1b[0m running tests", " \x1b[36m◑\x1b[0m running tests", " \x1b[36m◒\x1b[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 = []; + /** + * @var Timer + */ + private $timer; + /** + * @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, $verbose, $colors, $debug, $numberOfColumns, $reverse); + $this->timer = new Timer(); + $this->timer->start(); + } + public function printResult(TestResult $result) : void + { + $this->printHeader($result); + $this->printNonSuccessfulTestsSummary($result->count()); + $this->printFooter($result); + } + protected function printHeader(TestResult $result) : void + { + $this->write("\n" . (new ResourceUsageFormatter())->resourceUsage($this->timer->stop()) . "\n\n"); + } + protected function formatClassName(Test $test) : string + { + if ($test instanceof TestCase) { + return $this->prettifier->prettifyTestClass(get_class($test)); + } + return get_class($test); + } + /** + * @throws 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 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(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 = 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("\x1b[1K\x1b[" . 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; + } + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Util\TestDox; + +use function sprintf; +use PHPUnit\Framework\TestResult; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class HtmlResultPrinter extends \PHPUnit\Util\TestDox\ResultPrinter +{ + /** + * @var string + */ + private const PAGE_HEADER = <<<'EOT' + + + + + Test Documentation + + + +EOT; + /** + * @var string + */ + private const CLASS_HEADER = <<<'EOT' + +

    %s

    +
      + +EOT; + /** + * @var string + */ + private const CLASS_FOOTER = <<<'EOT' +
    +EOT; + /** + * @var string + */ + private const PAGE_FOOTER = <<<'EOT' + + + +EOT; + public function printResult(TestResult $result) : void + { + } + /** + * 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, $this->currentTestClassPrettified)); + } + /** + * Handler for 'on test' event. + */ + protected function onTest(string $name, bool $success = \true) : void + { + $this->write(sprintf("
  • %s
  • \n", $success ? 'success' : 'defect', $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); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Util\TestDox; + +use function array_key_exists; +use function array_keys; +use function array_map; +use function array_pop; +use function array_values; +use function explode; +use function get_class; +use function gettype; +use function implode; +use function in_array; +use function is_bool; +use function is_float; +use function is_int; +use function is_numeric; +use function is_object; +use function is_scalar; +use function is_string; +use function ord; +use function preg_quote; +use function preg_replace; +use function range; +use function sprintf; +use function str_replace; +use function strlen; +use function strpos; +use function strtolower; +use function strtoupper; +use function substr; +use function trim; +use PHPUnit\Framework\TestCase; +use PHPUnit\Util\Color; +use PHPUnit\Util\Exception as UtilException; +use PHPUnit\Util\Test; +use ReflectionException; +use ReflectionMethod; +use ReflectionObject; +use PHPUnitPHAR\SebastianBergmann\Exporter\Exporter; +use PHPUnitPHAR\SebastianBergmann\RecursionContext\InvalidArgumentException; +/** + * @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(bool $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) { + // ignore, determine className by parsing the provided name + } + $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($className)) { + $className = 'UnnamedTests'; + } + if (!empty($parts)) { + $parts[] = $className; + $fullyQualifiedName = implode('\\', $parts); + } else { + $fullyQualifiedName = $className; + } + $result = preg_replace('/(?<=[[:lower:]])(?=[[:upper:]])/u', ' ', $className); + if ($fullyQualifiedName !== $className) { + return $result . ' (' . $fullyQualifiedName . ')'; + } + return $result; + } + /** + * @throws InvalidArgumentException + */ + public function prettifyTestCase(TestCase $test) : string + { + $annotations = Test::parseTestMethodAnnotations(get_class($test), $test->getName(\false)); + $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((string) $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, \true)) { + $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 InvalidArgumentException + */ + private function mapTestMethodParameterNamesToProvidedDataValues(TestCase $test) : array + { + try { + $reflector = new ReflectionMethod(get_class($test), $test->getName(\false)); + // @codeCoverageIgnoreStart + } catch (ReflectionException $e) { + throw new UtilException($e->getMessage(), $e->getCode(), $e); + } + // @codeCoverageIgnoreEnd + $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(); + // @codeCoverageIgnoreStart + } catch (ReflectionException $e) { + throw new UtilException($e->getMessage(), $e->getCode(), $e); + } + // @codeCoverageIgnoreEnd + } + $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; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Util\TestDox; + +use function get_class; +use function in_array; +use PHPUnit\Framework\AssertionFailedError; +use PHPUnit\Framework\ErrorTestCase; +use PHPUnit\Framework\Exception; +use PHPUnit\Framework\Test; +use PHPUnit\Framework\TestCase; +use PHPUnit\Framework\TestSuite; +use PHPUnit\Framework\Warning; +use PHPUnit\Framework\WarningTestCase; +use PHPUnit\Runner\BaseTestRunner; +use PHPUnit\TextUI\ResultPrinter as ResultPrinterInterface; +use PHPUnit\Util\Printer; +use PHPUnitPHAR\SebastianBergmann\RecursionContext\InvalidArgumentException; +use Throwable; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +abstract class ResultPrinter extends Printer implements ResultPrinterInterface +{ + /** + * @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 Exception + */ + public function __construct($out = null, array $groups = [], array $excludeGroups = []) + { + parent::__construct($out); + $this->groups = $groups; + $this->excludeGroups = $excludeGroups; + $this->prettifier = new \PHPUnit\Util\TestDox\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 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(string $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 ErrorTestCase || $test instanceof WarningTestCase) { + return \false; + } + if (!empty($this->groups)) { + foreach ($test->getGroups() as $group) { + if (in_array($group, $this->groups, \true)) { + return \true; + } + } + return \false; + } + if (!empty($this->excludeGroups)) { + foreach ($test->getGroups() as $group) { + if (in_array($group, $this->excludeGroups, \true)) { + return \false; + } + } + return \true; + } + return \true; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Util\TestDox; + +use const PHP_EOL; +use function array_map; +use function get_class; +use function implode; +use function method_exists; +use function preg_split; +use function trim; +use PHPUnit\Framework\AssertionFailedError; +use PHPUnit\Framework\Exception; +use PHPUnit\Framework\Reorderable; +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\Runner\BaseTestRunner; +use PHPUnit\Runner\PhptTestCase; +use PHPUnit\TextUI\DefaultResultPrinter; +use PHPUnit\Util\Filter; +use PHPUnitPHAR\SebastianBergmann\RecursionContext\InvalidArgumentException; +use Throwable; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +class TestDoxPrinter extends DefaultResultPrinter +{ + /** + * @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 Buffer for test results + */ + protected $testResults = []; + /** + * @var array Lookup table for testname to testResults[index] + */ + protected $testNameResultIndex = []; + /** + * @var bool + */ + protected $enableOutputBuffer = \false; + /** + * @var array array + */ + protected $originalExecutionOrder = []; + /** + * @var int + */ + protected $spinState = 0; + /** + * @var bool + */ + protected $showProgress = \true; + /** + * @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, $verbose, $colors, $debug, $numberOfColumns, $reverse); + $this->prettifier = new \PHPUnit\Util\TestDox\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 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 InvalidArgumentException + */ + public function addError(Test $test, Throwable $t, float $time) : void + { + $this->registerTestResult($test, $t, BaseTestRunner::STATUS_ERROR, $time, \true); + } + /** + * @throws InvalidArgumentException + */ + public function addWarning(Test $test, Warning $e, float $time) : void + { + $this->registerTestResult($test, $e, BaseTestRunner::STATUS_WARNING, $time, \true); + } + /** + * @throws InvalidArgumentException + */ + public function addFailure(Test $test, AssertionFailedError $e, float $time) : void + { + $this->registerTestResult($test, $e, BaseTestRunner::STATUS_FAILURE, $time, \true); + } + /** + * @throws InvalidArgumentException + */ + public function addIncompleteTest(Test $test, Throwable $t, float $time) : void + { + $this->registerTestResult($test, $t, BaseTestRunner::STATUS_INCOMPLETE, $time, \false); + } + /** + * @throws InvalidArgumentException + */ + public function addRiskyTest(Test $test, Throwable $t, float $time) : void + { + $this->registerTestResult($test, $t, BaseTestRunner::STATUS_RISKY, $time, \false); + } + /** + * @throws 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 InvalidArgumentException + */ + protected function registerTestResult(Test $test, ?Throwable $t, int $status, float $time, bool $verbose) : void + { + $testName = $test instanceof Reorderable ? $test->sortId() : $test->getName(); + $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 method_exists($test, 'getName') ? $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 && isset($this->originalExecutionOrder[$this->testFlushIndex - 1])) { + $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(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 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))); + } +} + + * + * 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\TestResult; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class TextResultPrinter extends \PHPUnit\Util\TestDox\ResultPrinter +{ + public function printResult(TestResult $result) : void + { + } + /** + * Handler for 'start class' event. + */ + protected function startClass(string $name) : void + { + $this->write($this->currentTestClassPrettified . "\n"); + } + /** + * Handler for 'on test' event. + */ + protected function onTest(string $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"); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Util\TestDox; + +use function array_filter; +use function get_class; +use function implode; +use function strpos; +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\Framework\WarningTestCase; +use PHPUnit\Util\Printer; +use PHPUnit\Util\Test as TestUtil; +use ReflectionClass; +use ReflectionException; +use PHPUnitPHAR\SebastianBergmann\RecursionContext\InvalidArgumentException; +use Throwable; +/** + * @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 \PHPUnit\Util\TestDox\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 InvalidArgumentException + */ + public function endTest(Test $test, float $time) : void + { + if (!$test instanceof TestCase || $test instanceof WarningTestCase) { + return; + } + $groups = array_filter($test->getGroups(), static function ($group) { + return !($group === 'small' || $group === 'medium' || $group === 'large' || strpos($group, '__phpunit_') === 0); + }); + $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 = TestUtil::parseTestMethodAnnotations(get_class($test), $test->getName(\false)); + 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 = TestUtil::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(); + // @codeCoverageIgnoreStart + } catch (ReflectionException $e) { + throw new Exception($e->getMessage(), $e->getCode(), $e); + } + // @codeCoverageIgnoreEnd + 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); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Util; + +use const PHP_EOL; +use function get_class; +use function sprintf; +use function str_replace; +use PHPUnit\Framework\TestCase; +use PHPUnit\Framework\TestSuite; +use PHPUnit\Runner\PhptTestCase; +use RecursiveIteratorIterator; +use PHPUnitPHAR\SebastianBergmann\RecursionContext\InvalidArgumentException; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class TextTestListRenderer +{ + /** + * @throws 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; + } +} + + * + * 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; + } + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Util; + +use function in_array; +use function sprintf; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + * + * @psalm-immutable + */ +final class VersionComparisonOperator +{ + /** + * @psalm-var '<'|'lt'|'<='|'le'|'>'|'gt'|'>='|'ge'|'=='|'='|'eq'|'!='|'<>'|'ne' + */ + private $operator; + public function __construct(string $operator) + { + $this->ensureOperatorIsValid($operator); + $this->operator = $operator; + } + /** + * @return '!='|'<'|'<='|'<>'|'='|'=='|'>'|'>='|'eq'|'ge'|'gt'|'le'|'lt'|'ne' + */ + public function asString() : string + { + return $this->operator; + } + /** + * @throws Exception + * + * @psalm-assert '<'|'lt'|'<='|'le'|'>'|'gt'|'>='|'ge'|'=='|'='|'eq'|'!='|'<>'|'ne' $operator + */ + private function ensureOperatorIsValid(string $operator) : void + { + if (!in_array($operator, ['<', 'lt', '<=', 'le', '>', 'gt', '>=', 'ge', '==', '=', 'eq', '!=', '<>', 'ne'], \true)) { + throw new \PHPUnit\Util\Exception(sprintf('"%s" is not a valid version_compare() operator', $operator)); + } + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Util; + +use const DIRECTORY_SEPARATOR; +use function addslashes; +use function array_map; +use function implode; +use function is_string; +use function realpath; +use function sprintf; +use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\CodeCoverage as FilterConfiguration; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + * + * @deprecated + */ +final class XdebugFilterScriptGenerator +{ + public function generate(FilterConfiguration $filter) : string + { + $files = array_map(static function ($item) { + return sprintf(" '%s'", $item); + }, $this->getItems($filter)); + $files = implode(",\n", $files); + return <<directories() as $directory) { + $path = realpath($directory->path()); + if (is_string($path)) { + $files[] = sprintf(addslashes('%s' . DIRECTORY_SEPARATOR), $path); + } + } + foreach ($filter->files() as $file) { + $files[] = $file->path(); + } + return $files; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Util; + +use const ENT_QUOTES; +use function assert; +use function class_exists; +use function htmlspecialchars; +use function mb_convert_encoding; +use function ord; +use function preg_replace; +use function settype; +use function strlen; +use DOMCharacterData; +use DOMDocument; +use DOMElement; +use DOMNode; +use DOMText; +use ReflectionClass; +use ReflectionException; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class Xml +{ + /** + * @deprecated Only used by assertEqualXMLStructure() + */ + public static function import(DOMElement $element) : DOMElement + { + return (new DOMDocument())->importNode($element, \true); + } + /** + * @deprecated Only used by assertEqualXMLStructure() + */ + 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 { + assert(class_exists($className)); + $variable = (new ReflectionClass($className))->newInstanceArgs($constructorArgs); + // @codeCoverageIgnoreStart + } catch (ReflectionException $e) { + throw new \PHPUnit\Util\Exception($e->getMessage(), $e->getCode(), $e); + } + // @codeCoverageIgnoreEnd + } 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; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Util\Xml; + +use RuntimeException; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class Exception extends RuntimeException implements \PHPUnit\Exception +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Util\Xml; + +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + * + * @psalm-immutable + */ +final class FailedSchemaDetectionResult extends \PHPUnit\Util\Xml\SchemaDetectionResult +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Util\Xml; + +use function chdir; +use function dirname; +use function error_reporting; +use function file_get_contents; +use function getcwd; +use function libxml_get_errors; +use function libxml_use_internal_errors; +use function sprintf; +use DOMDocument; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class Loader +{ + /** + * @throws Exception + */ + public 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 \PHPUnit\Util\Xml\Exception(sprintf('Could not read "%s".', $filename)); + } + return $this->load($contents, $isHtml, $filename, $xinclude, $strict); + } + /** + * @throws Exception + */ + public function load(string $actual, bool $isHtml = \false, string $filename = '', bool $xinclude = \false, bool $strict = \false) : DOMDocument + { + if ($actual === '') { + throw new \PHPUnit\Util\Xml\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 \PHPUnit\Util\Xml\Exception(sprintf('Could not load "%s".%s', $filename, $message !== '' ? "\n" . $message : '')); + } + if ($message === '') { + $message = 'Could not load XML for unknown reason'; + } + throw new \PHPUnit\Util\Xml\Exception($message); + } + return $document; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Util\Xml; + +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + * + * @psalm-immutable + */ +abstract class SchemaDetectionResult +{ + /** + * @psalm-assert-if-true SuccessfulSchemaDetectionResult $this + */ + public function detected() : bool + { + return \false; + } + /** + * @throws Exception + */ + public function version() : string + { + throw new \PHPUnit\Util\Xml\Exception('No supported schema was detected'); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Util\Xml; + +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class SchemaDetector +{ + /** + * @throws Exception + */ + public function detect(string $filename) : \PHPUnit\Util\Xml\SchemaDetectionResult + { + $document = (new \PHPUnit\Util\Xml\Loader())->loadFile($filename, \false, \true, \true); + $schemaFinder = new \PHPUnit\Util\Xml\SchemaFinder(); + foreach ($schemaFinder->available() as $candidate) { + $schema = (new \PHPUnit\Util\Xml\SchemaFinder())->find($candidate); + if (!(new \PHPUnit\Util\Xml\Validator())->validate($document, $schema)->hasValidationErrors()) { + return new \PHPUnit\Util\Xml\SuccessfulSchemaDetectionResult($candidate); + } + } + return new \PHPUnit\Util\Xml\FailedSchemaDetectionResult(); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Util\Xml; + +use function assert; +use function defined; +use function is_file; +use function rsort; +use function sprintf; +use DirectoryIterator; +use PHPUnit\Runner\Version; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class SchemaFinder +{ + /** + * @psalm-return non-empty-list + */ + public function available() : array + { + $result = [Version::series()]; + foreach (new DirectoryIterator($this->path() . 'schema') as $file) { + if ($file->isDot()) { + continue; + } + $version = $file->getBasename('.xsd'); + assert(!empty($version)); + $result[] = $version; + } + rsort($result); + return $result; + } + /** + * @throws Exception + */ + public function find(string $version) : string + { + if ($version === Version::series()) { + $filename = $this->path() . 'phpunit.xsd'; + } else { + $filename = $this->path() . 'schema/' . $version . '.xsd'; + } + if (!is_file($filename)) { + throw new \PHPUnit\Util\Xml\Exception(sprintf('Schema for PHPUnit %s is not available', $version)); + } + return $filename; + } + private function path() : string + { + if (defined('__PHPUNIT_PHAR_ROOT__')) { + return __PHPUNIT_PHAR_ROOT__ . '/'; + } + return __DIR__ . '/../../../'; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Util\Xml; + +use function count; +use ArrayIterator; +use Countable; +use DOMNode; +use DOMNodeList; +use IteratorAggregate; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + * + * @template-implements IteratorAggregate + */ +final class SnapshotNodeList implements Countable, IteratorAggregate +{ + /** + * @var DOMNode[] + */ + private $nodes = []; + public static function fromNodeList(DOMNodeList $list) : self + { + $snapshot = new self(); + foreach ($list as $node) { + $snapshot->nodes[] = $node; + } + return $snapshot; + } + public function count() : int + { + return count($this->nodes); + } + public function getIterator() : ArrayIterator + { + return new ArrayIterator($this->nodes); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Util\Xml; + +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + * + * @psalm-immutable + */ +final class SuccessfulSchemaDetectionResult extends \PHPUnit\Util\Xml\SchemaDetectionResult +{ + /** + * @psalm-var non-empty-string + */ + private $version; + /** + * @psalm-param non-empty-string $version + */ + public function __construct(string $version) + { + $this->version = $version; + } + /** + * @psalm-assert-if-true SuccessfulSchemaDetectionResult $this + */ + public function detected() : bool + { + return \true; + } + /** + * @psalm-return non-empty-string + */ + public function version() : string + { + return $this->version; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Util\Xml; + +use function sprintf; +use function trim; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + * + * @psalm-immutable + */ +final class ValidationResult +{ + /** + * @psalm-var array> + */ + private $validationErrors = []; + /** + * @psalm-param array $errors + */ + public static function fromArray(array $errors) : self + { + $validationErrors = []; + foreach ($errors as $error) { + if (!isset($validationErrors[$error->line])) { + $validationErrors[$error->line] = []; + } + $validationErrors[$error->line][] = trim($error->message); + } + return new self($validationErrors); + } + private function __construct(array $validationErrors) + { + $this->validationErrors = $validationErrors; + } + public function hasValidationErrors() : bool + { + return !empty($this->validationErrors); + } + public function asString() : string + { + $buffer = ''; + foreach ($this->validationErrors as $line => $validationErrorsOnLine) { + $buffer .= sprintf(\PHP_EOL . ' Line %d:' . \PHP_EOL, $line); + foreach ($validationErrorsOnLine as $validationError) { + $buffer .= sprintf(' - %s' . \PHP_EOL, $validationError); + } + } + return $buffer; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Util\Xml; + +use function file_get_contents; +use function libxml_clear_errors; +use function libxml_get_errors; +use function libxml_use_internal_errors; +use DOMDocument; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class Validator +{ + public function validate(DOMDocument $document, string $xsdFilename) : \PHPUnit\Util\Xml\ValidationResult + { + $originalErrorHandling = libxml_use_internal_errors(\true); + $document->schemaValidateSource(file_get_contents($xsdFilename)); + $errors = libxml_get_errors(); + libxml_clear_errors(); + libxml_use_internal_errors($originalErrorHandling); + return \PHPUnit\Util\Xml\ValidationResult::fromArray($errors); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Util; + +use function get_class; +use function implode; +use function str_replace; +use PHPUnit\Framework\TestCase; +use PHPUnit\Framework\TestSuite; +use PHPUnit\Runner\PhptTestCase; +use RecursiveIteratorIterator; +use PHPUnitPHAR\SebastianBergmann\RecursionContext\InvalidArgumentException; +use XMLWriter; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class XmlTestListRenderer +{ + /** + * @throws InvalidArgumentException + */ + public function render(TestSuite $suite) : string + { + $writer = new XMLWriter(); + $writer->openMemory(); + $writer->setIndent(\true); + $writer->startDocument('1.0', 'UTF-8'); + $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(); + $writer->endDocument(); + return $writer->outputMemory(); + } +} + + + + + phpunit + phpunit + 9.6.19 + The PHP Unit Testing framework. + + + BSD-3-Clause + + + pkg:composer/phpunit/phpunit@9.6.19 + + + doctrine + deprecations + 1.1.3 + A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options to disable all deprecations or selectively for packages. + + + MIT + + + pkg:composer/doctrine/deprecations@1.1.3 + + + doctrine + instantiator + 1.5.0 + A small, lightweight utility to instantiate objects in PHP without invoking their constructors + + + MIT + + + pkg:composer/doctrine/instantiator@1.5.0 + myclabs deep-copy @@ -85333,26 +94540,26 @@ final class XmlTestListRenderer nikic php-parser - v4.17.1 + v4.19.1 A PHP parser written in PHP BSD-3-Clause - pkg:composer/nikic/php-parser@v4.17.1 + pkg:composer/nikic/php-parser@v4.19.1 phar-io manifest - 2.0.3 + 2.0.4 Component for reading phar.io manifest information from a PHP Archive (PHAR) BSD-3-Clause - pkg:composer/phar-io/manifest@2.0.3 + pkg:composer/phar-io/manifest@2.0.4 phar-io @@ -85393,38 +94600,50 @@ final class XmlTestListRenderer phpdocumentor type-resolver - 1.6.1 + 1.8.2 A PSR-5 based resolver of Class names, Types and Structural Element Names MIT - pkg:composer/phpdocumentor/type-resolver@1.6.1 + pkg:composer/phpdocumentor/type-resolver@1.8.2 phpspec prophecy - v1.17.0 + v1.19.0 Highly opinionated mocking framework for PHP 5.3+ MIT - pkg:composer/phpspec/prophecy@v1.17.0 + pkg:composer/phpspec/prophecy@v1.19.0 + + + phpstan + phpdoc-parser + 1.28.0 + PHPDoc parser with support for nullable, intersection and generic types + + + MIT + + + pkg:composer/phpstan/phpdoc-parser@1.28.0 phpunit php-code-coverage - 9.2.29 + 9.2.31 Library that provides collection, processing, and rendering functionality for PHP code coverage information. BSD-3-Clause - pkg:composer/phpunit/php-code-coverage@9.2.29 + pkg:composer/phpunit/php-code-coverage@9.2.31 phpunit @@ -85477,14 +94696,14 @@ final class XmlTestListRenderer sebastian cli-parser - 1.0.1 + 1.0.2 Library for parsing CLI options BSD-3-Clause - pkg:composer/sebastian/cli-parser@1.0.1 + pkg:composer/sebastian/cli-parser@1.0.2 sebastian @@ -85525,26 +94744,26 @@ final class XmlTestListRenderer sebastian complexity - 2.0.2 + 2.0.3 Library for calculating the complexity of PHP code units BSD-3-Clause - pkg:composer/sebastian/complexity@2.0.2 + pkg:composer/sebastian/complexity@2.0.3 sebastian diff - 4.0.5 + 4.0.6 Diff implementation BSD-3-Clause - pkg:composer/sebastian/diff@4.0.5 + pkg:composer/sebastian/diff@4.0.6 sebastian @@ -85561,38 +94780,38 @@ final class XmlTestListRenderer sebastian exporter - 4.0.5 + 4.0.6 Provides the functionality to export PHP variables for visualization BSD-3-Clause - pkg:composer/sebastian/exporter@4.0.5 + pkg:composer/sebastian/exporter@4.0.6 sebastian global-state - 5.0.6 + 5.0.7 Snapshotting of global state BSD-3-Clause - pkg:composer/sebastian/global-state@5.0.6 + pkg:composer/sebastian/global-state@5.0.7 sebastian lines-of-code - 1.0.3 + 1.0.4 Library for counting the lines of code in PHP source code BSD-3-Clause - pkg:composer/sebastian/lines-of-code@1.0.3 + pkg:composer/sebastian/lines-of-code@1.0.4 sebastian @@ -85633,14 +94852,14 @@ final class XmlTestListRenderer sebastian resource-operations - 3.0.3 + 3.0.4 Provides a list of PHP built-in functions that operate on resources BSD-3-Clause - pkg:composer/sebastian/resource-operations@3.0.3 + pkg:composer/sebastian/resource-operations@3.0.4 sebastian @@ -85669,14 +94888,14 @@ final class XmlTestListRenderer theseer tokenizer - 1.2.1 + 1.2.3 A small library for converting tokenized PHP source code into XML and potentially other formats BSD-3-Clause - pkg:composer/theseer/tokenizer@1.2.1 + pkg:composer/theseer/tokenizer@1.2.3 webmozart @@ -85696,7 +94915,1602 @@ final class XmlTestListRenderer - This Schema file defines the rules by which the XML configuration file of PHPUnit 8.5 may be structured. + This Schema file defines the rules by which the XML configuration file of PHPUnit 8.5 may be structured. + + + + + + Root Element + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The main type specifying the document structure + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This Schema file defines the rules by which the XML configuration file of PHPUnit 9.0 may be structured. + + + + + + Root Element + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The main type specifying the document structure + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This Schema file defines the rules by which the XML configuration file of PHPUnit 9.0 may be structured. + + + + + + Root Element + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The main type specifying the document structure + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This Schema file defines the rules by which the XML configuration file of PHPUnit 9.2 may be structured. + + + + + + Root Element + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The main type specifying the document structure + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This Schema file defines the rules by which the XML configuration file of PHPUnit 9.3 may be structured. + + + + + + Root Element + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The main type specifying the document structure + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This Schema file defines the rules by which the XML configuration file of PHPUnit 9.4 may be structured. @@ -85705,30 +96519,33 @@ final class XmlTestListRenderer Root Element - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - + + @@ -85816,39 +96633,334 @@ final class XmlTestListRenderer - + - + + + + + + + + + + + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The main type specifying the document structure + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This Schema file defines the rules by which the XML configuration file of PHPUnit 9.5 may be structured. + + + + + + Root Element + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - + - - - - - - - - + + @@ -85935,16 +97047,14 @@ final class XmlTestListRenderer - - + - - + @@ -85954,7 +97064,10 @@ final class XmlTestListRenderer + + + @@ -85964,7 +97077,6 @@ final class XmlTestListRenderer - @@ -85986,8 +97098,8 @@ final class XmlTestListRenderer - - + + @@ -86003,327 +97115,53 @@ final class XmlTestListRenderer - - - - - - - - - - - This Schema file defines the rules by which the XML configuration file of PHPUnit 9.2 may be structured. - - - - - - Root Element - - - - - - - - - - + - - - - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - - - - + + - - - The main type specifying the document structure - - - + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + - - - - - - + + + + sebastian/cli-parser @@ -86370,7 +97208,7 @@ declare (strict_types=1); * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\CliParser; +namespace PHPUnitPHAR\SebastianBergmann\CliParser; use function array_map; use function array_merge; @@ -86403,7 +97241,7 @@ final class Parser * @throws OptionDoesNotAllowArgumentException * @throws UnknownOptionException */ - public function parse(array $argv, string $shortOptions, array $longOptions = null) : array + public function parse(array $argv, string $shortOptions, ?array $longOptions = null) : array { if (empty($argv)) { return [[], []]; @@ -86527,7 +97365,7 @@ declare (strict_types=1); * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\CliParser; +namespace PHPUnitPHAR\SebastianBergmann\CliParser; use function sprintf; use RuntimeException; @@ -86549,7 +97387,7 @@ declare (strict_types=1); * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\CliParser; +namespace PHPUnitPHAR\SebastianBergmann\CliParser; use Throwable; interface Exception extends Throwable @@ -86566,7 +97404,7 @@ declare (strict_types=1); * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\CliParser; +namespace PHPUnitPHAR\SebastianBergmann\CliParser; use function sprintf; use RuntimeException; @@ -86588,7 +97426,7 @@ declare (strict_types=1); * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\CliParser; +namespace PHPUnitPHAR\SebastianBergmann\CliParser; use function sprintf; use RuntimeException; @@ -86610,7 +97448,7 @@ declare (strict_types=1); * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\CliParser; +namespace PHPUnitPHAR\SebastianBergmann\CliParser; use function sprintf; use RuntimeException; @@ -86665,7 +97503,7 @@ declare (strict_types=1); * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\CodeUnitReverseLookup; +namespace PHPUnitPHAR\SebastianBergmann\CodeUnitReverseLookup; use function array_merge; use function assert; @@ -86771,7 +97609,7 @@ declare (strict_types=1); * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\CodeUnit; +namespace PHPUnitPHAR\SebastianBergmann\CodeUnit; /** * @psalm-immutable @@ -86797,7 +97635,7 @@ declare (strict_types=1); * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\CodeUnit; +namespace PHPUnitPHAR\SebastianBergmann\CodeUnit; /** * @psalm-immutable @@ -86823,7 +97661,7 @@ declare (strict_types=1); * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\CodeUnit; +namespace PHPUnitPHAR\SebastianBergmann\CodeUnit; use function range; use function sprintf; @@ -87109,7 +97947,7 @@ declare (strict_types=1); * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\CodeUnit; +namespace PHPUnitPHAR\SebastianBergmann\CodeUnit; use function array_merge; use function count; @@ -87178,7 +98016,7 @@ declare (strict_types=1); * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\CodeUnit; +namespace PHPUnitPHAR\SebastianBergmann\CodeUnit; use Iterator; final class CodeUnitCollectionIterator implements Iterator @@ -87227,7 +98065,7 @@ declare (strict_types=1); * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\CodeUnit; +namespace PHPUnitPHAR\SebastianBergmann\CodeUnit; /** * @psalm-immutable @@ -87253,7 +98091,7 @@ declare (strict_types=1); * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\CodeUnit; +namespace PHPUnitPHAR\SebastianBergmann\CodeUnit; /** * @psalm-immutable @@ -87279,7 +98117,7 @@ declare (strict_types=1); * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\CodeUnit; +namespace PHPUnitPHAR\SebastianBergmann\CodeUnit; /** * @psalm-immutable @@ -87338,7 +98176,7 @@ declare (strict_types=1); * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\CodeUnit; +namespace PHPUnitPHAR\SebastianBergmann\CodeUnit; use function array_keys; use function array_merge; @@ -87666,7 +98504,7 @@ declare (strict_types=1); * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\CodeUnit; +namespace PHPUnitPHAR\SebastianBergmann\CodeUnit; /** * @psalm-immutable @@ -87692,7 +98530,7 @@ declare (strict_types=1); * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\CodeUnit; +namespace PHPUnitPHAR\SebastianBergmann\CodeUnit; /** * @psalm-immutable @@ -87718,7 +98556,7 @@ declare (strict_types=1); * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\CodeUnit; +namespace PHPUnitPHAR\SebastianBergmann\CodeUnit; use Throwable; interface Exception extends Throwable @@ -87735,7 +98573,7 @@ declare (strict_types=1); * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\CodeUnit; +namespace PHPUnitPHAR\SebastianBergmann\CodeUnit; use RuntimeException; final class InvalidCodeUnitException extends RuntimeException implements Exception @@ -87752,7 +98590,7 @@ declare (strict_types=1); * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\CodeUnit; +namespace PHPUnitPHAR\SebastianBergmann\CodeUnit; use RuntimeException; final class NoTraitException extends RuntimeException implements Exception @@ -87769,7 +98607,7 @@ declare (strict_types=1); * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\CodeUnit; +namespace PHPUnitPHAR\SebastianBergmann\CodeUnit; use RuntimeException; final class ReflectionException extends RuntimeException implements Exception @@ -87786,7 +98624,7 @@ declare (strict_types=1); * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\Comparator; +namespace PHPUnitPHAR\SebastianBergmann\Comparator; use function array_key_exists; use function is_array; @@ -87881,9 +98719,9 @@ declare (strict_types=1); * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\Comparator; +namespace PHPUnitPHAR\SebastianBergmann\Comparator; -use PHPUnit\SebastianBergmann\Exporter\Exporter; +use PHPUnitPHAR\SebastianBergmann\Exporter\Exporter; /** * Abstract base class for comparators which compare values for equality. */ @@ -87938,11 +98776,11 @@ declare (strict_types=1); * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\Comparator; +namespace PHPUnitPHAR\SebastianBergmann\Comparator; use RuntimeException; -use PHPUnit\SebastianBergmann\Diff\Differ; -use PHPUnit\SebastianBergmann\Diff\Output\UnifiedDiffOutputBuilder; +use PHPUnitPHAR\SebastianBergmann\Diff\Differ; +use PHPUnitPHAR\SebastianBergmann\Diff\Output\UnifiedDiffOutputBuilder; /** * Thrown when an assertion for string equality failed. */ @@ -88054,7 +98892,7 @@ declare (strict_types=1); * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\Comparator; +namespace PHPUnitPHAR\SebastianBergmann\Comparator; use function sprintf; use function strtolower; @@ -88131,7 +98969,7 @@ declare (strict_types=1); * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\Comparator; +namespace PHPUnitPHAR\SebastianBergmann\Comparator; use function abs; use function floor; @@ -88207,7 +99045,7 @@ declare (strict_types=1); * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\Comparator; +namespace PHPUnitPHAR\SebastianBergmann\Comparator; use function is_float; use function is_numeric; @@ -88266,7 +99104,7 @@ declare (strict_types=1); * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\Comparator; +namespace PHPUnitPHAR\SebastianBergmann\Comparator; use Exception; /** @@ -88312,7 +99150,7 @@ declare (strict_types=1); * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\Comparator; +namespace PHPUnitPHAR\SebastianBergmann\Comparator; use function array_unshift; /** @@ -88473,7 +99311,7 @@ declare (strict_types=1); * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\Comparator; +namespace PHPUnitPHAR\SebastianBergmann\Comparator; use PHPUnit\Framework\MockObject\MockObject; /** @@ -88519,7 +99357,7 @@ declare (strict_types=1); * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\Comparator; +namespace PHPUnitPHAR\SebastianBergmann\Comparator; use function abs; use function is_float; @@ -88586,7 +99424,7 @@ declare (strict_types=1); * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\Comparator; +namespace PHPUnitPHAR\SebastianBergmann\Comparator; use function get_class; use function in_array; @@ -88675,7 +99513,7 @@ declare (strict_types=1); * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\Comparator; +namespace PHPUnitPHAR\SebastianBergmann\Comparator; use function is_resource; /** @@ -88724,7 +99562,7 @@ declare (strict_types=1); * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\Comparator; +namespace PHPUnitPHAR\SebastianBergmann\Comparator; use function is_bool; use function is_object; @@ -88804,7 +99642,7 @@ declare (strict_types=1); * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\Comparator; +namespace PHPUnitPHAR\SebastianBergmann\Comparator; use SplObjectStorage; /** @@ -88860,7 +99698,7 @@ declare (strict_types=1); * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\Comparator; +namespace PHPUnitPHAR\SebastianBergmann\Comparator; use function gettype; use function sprintf; @@ -88918,7 +99756,7 @@ declare (strict_types=1); * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\Comparator; +namespace PHPUnitPHAR\SebastianBergmann\Comparator; use Throwable; interface Exception extends Throwable @@ -88935,7 +99773,7 @@ declare (strict_types=1); * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\Comparator; +namespace PHPUnitPHAR\SebastianBergmann\Comparator; final class RuntimeException extends \RuntimeException implements Exception { @@ -88951,16 +99789,14 @@ declare (strict_types=1); * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\Complexity; +namespace PHPUnitPHAR\SebastianBergmann\Complexity; -use PHPUnit\PhpParser\Error; -use PHPUnit\PhpParser\Lexer; -use PHPUnit\PhpParser\Node; -use PHPUnit\PhpParser\NodeTraverser; -use PHPUnit\PhpParser\NodeVisitor\NameResolver; -use PHPUnit\PhpParser\NodeVisitor\ParentConnectingVisitor; -use PHPUnit\PhpParser\Parser; -use PHPUnit\PhpParser\ParserFactory; +use PHPUnitPHAR\PhpParser\Error; +use PHPUnitPHAR\PhpParser\Node; +use PHPUnitPHAR\PhpParser\NodeTraverser; +use PHPUnitPHAR\PhpParser\NodeVisitor\NameResolver; +use PHPUnitPHAR\PhpParser\NodeVisitor\ParentConnectingVisitor; +use PHPUnitPHAR\PhpParser\ParserFactory; final class Calculator { /** @@ -88976,7 +99812,7 @@ final class Calculator public function calculateForSourceString(string $source) : ComplexityCollection { try { - $nodes = $this->parser()->parse($source); + $nodes = (new ParserFactory())->createForHostVersion()->parse($source); \assert($nodes !== null); return $this->calculateForAbstractSyntaxTree($nodes); // @codeCoverageIgnoreStart @@ -89007,10 +99843,6 @@ final class Calculator // @codeCoverageIgnoreEnd return $complexityCalculatingVisitor->result(); } - private function parser() : Parser - { - return (new ParserFactory())->create(ParserFactory::PREFER_PHP7, new Lexer()); - } } diffToArray($this->normalizeDiffInput($from), $this->normalizeDiffInput($to), $lcs); return $this->outputBuilder->getDiff($diff); @@ -89607,7 +100439,7 @@ final class Differ * @param array|string $to * @param LongestCommonSubsequenceCalculator $lcs */ - public function diffToArray($from, $to, LongestCommonSubsequenceCalculator $lcs = null) : array + public function diffToArray($from, $to, ?LongestCommonSubsequenceCalculator $lcs = null) : array { if (is_string($from)) { $from = $this->splitStringByLines($from); @@ -89789,7 +100621,7 @@ declare (strict_types=1); * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\Diff; +namespace PHPUnitPHAR\SebastianBergmann\Diff; use function get_class; use function gettype; @@ -89798,7 +100630,7 @@ use function sprintf; use Exception; final class ConfigurationException extends InvalidArgumentException { - public function __construct(string $option, string $expected, $value, int $code = 0, Exception $previous = null) + public function __construct(string $option, string $expected, $value, int $code = 0, ?Exception $previous = null) { parent::__construct(sprintf('Option "%s" must be %s, got "%s".', $option, $expected, is_object($value) ? get_class($value) : (null === $value ? '' : gettype($value) . '#' . $value)), $code, $previous); } @@ -89814,7 +100646,7 @@ declare (strict_types=1); * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\Diff; +namespace PHPUnitPHAR\SebastianBergmann\Diff; use Throwable; interface Exception extends Throwable @@ -89831,7 +100663,7 @@ declare (strict_types=1); * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\Diff; +namespace PHPUnitPHAR\SebastianBergmann\Diff; class InvalidArgumentException extends \InvalidArgumentException implements Exception { @@ -89880,7 +100712,7 @@ declare (strict_types=1); * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\Diff; +namespace PHPUnitPHAR\SebastianBergmann\Diff; final class Line { @@ -89920,7 +100752,7 @@ declare (strict_types=1); * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\Diff; +namespace PHPUnitPHAR\SebastianBergmann\Diff; interface LongestCommonSubsequenceCalculator { @@ -89940,7 +100772,7 @@ declare (strict_types=1); * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\Diff; +namespace PHPUnitPHAR\SebastianBergmann\Diff; use function array_fill; use function array_merge; @@ -90019,7 +100851,7 @@ declare (strict_types=1); * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\Diff\Output; +namespace PHPUnitPHAR\SebastianBergmann\Diff\Output; use function count; abstract class AbstractChunkOutputBuilder implements DiffOutputBuilderInterface @@ -90068,14 +100900,14 @@ declare (strict_types=1); * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\Diff\Output; +namespace PHPUnitPHAR\SebastianBergmann\Diff\Output; use function fclose; use function fopen; use function fwrite; use function stream_get_contents; use function substr; -use PHPUnit\SebastianBergmann\Diff\Differ; +use PHPUnitPHAR\SebastianBergmann\Diff\Differ; /** * Builds a diff string representation in a loose unified diff format * listing only changes lines. Does not include line numbers. @@ -90135,7 +100967,7 @@ declare (strict_types=1); * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\Diff\Output; +namespace PHPUnitPHAR\SebastianBergmann\Diff\Output; /** * Defines how an output builder should take a generated @@ -90156,7 +100988,7 @@ declare (strict_types=1); * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\Diff\Output; +namespace PHPUnitPHAR\SebastianBergmann\Diff\Output; use function array_merge; use function array_splice; @@ -90172,8 +101004,8 @@ use function min; use function sprintf; use function stream_get_contents; use function substr; -use PHPUnit\SebastianBergmann\Diff\ConfigurationException; -use PHPUnit\SebastianBergmann\Diff\Differ; +use PHPUnitPHAR\SebastianBergmann\Diff\ConfigurationException; +use PHPUnitPHAR\SebastianBergmann\Diff\Differ; /** * Strict Unified diff output builder. * @@ -90403,7 +101235,7 @@ declare (strict_types=1); * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\Diff\Output; +namespace PHPUnitPHAR\SebastianBergmann\Diff\Output; use function array_splice; use function count; @@ -90415,7 +101247,7 @@ use function min; use function stream_get_contents; use function strlen; use function substr; -use PHPUnit\SebastianBergmann\Diff\Differ; +use PHPUnitPHAR\SebastianBergmann\Diff\Differ; /** * Builds a diff string representation in unified diff format in chunks. */ @@ -90599,7 +101431,7 @@ declare (strict_types=1); * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\Diff; +namespace PHPUnitPHAR\SebastianBergmann\Diff; use function array_pop; use function count; @@ -90685,7 +101517,7 @@ declare (strict_types=1); * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\Diff; +namespace PHPUnitPHAR\SebastianBergmann\Diff; use function array_reverse; use function count; @@ -90759,7 +101591,7 @@ declare (strict_types=1); * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\Environment; +namespace PHPUnitPHAR\SebastianBergmann\Environment; use const DIRECTORY_SEPARATOR; use const STDIN; @@ -90943,7 +101775,7 @@ declare (strict_types=1); * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\Environment; +namespace PHPUnitPHAR\SebastianBergmann\Environment; use const DIRECTORY_SEPARATOR; use const PHP_OS; @@ -90991,7 +101823,7 @@ declare (strict_types=1); * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\Environment; +namespace PHPUnitPHAR\SebastianBergmann\Environment; use const PHP_BINARY; use const PHP_BINDIR; @@ -91249,7 +102081,7 @@ declare (strict_types=1); * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\Exporter; +namespace PHPUnitPHAR\SebastianBergmann\Exporter; use function bin2hex; use function count; @@ -91275,7 +102107,7 @@ use function str_replace; use function strlen; use function substr; use function var_export; -use PHPUnit\SebastianBergmann\RecursionContext\Context; +use PHPUnitPHAR\SebastianBergmann\RecursionContext\Context; use SplObjectStorage; /** * A nifty utility for visualizing PHP variables. @@ -91317,7 +102149,7 @@ class Exporter * * @return string */ - public function shortenedRecursiveExport(&$data, Context $context = null) + public function shortenedRecursiveExport(&$data, ?Context $context = null) { $result = []; $exporter = new self(); @@ -91547,7 +102379,7 @@ declare (strict_types=1); * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\GlobalState; +namespace PHPUnitPHAR\SebastianBergmann\GlobalState; use const PHP_EOL; use function is_array; @@ -91629,7 +102461,7 @@ declare (strict_types=1); * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\GlobalState; +namespace PHPUnitPHAR\SebastianBergmann\GlobalState; use function in_array; use function strpos; @@ -91762,7 +102594,7 @@ declare (strict_types=1); * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\GlobalState; +namespace PHPUnitPHAR\SebastianBergmann\GlobalState; use function array_diff; use function array_key_exists; @@ -91788,7 +102620,7 @@ class Restorer */ public function restoreFunctions(Snapshot $snapshot) : void { - if (!function_exists('PHPUnit\\uopz_delete')) { + if (!function_exists('PHPUnitPHAR\\uopz_delete')) { throw new RuntimeException('The uopz_delete() function is required for this operation'); } $functions = get_defined_functions(); @@ -91879,7 +102711,7 @@ declare (strict_types=1); * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\GlobalState; +namespace PHPUnitPHAR\SebastianBergmann\GlobalState; use const PHP_VERSION_ID; use function array_keys; @@ -91901,8 +102733,8 @@ use function is_scalar; use function serialize; use function unserialize; use ReflectionClass; -use PHPUnit\SebastianBergmann\ObjectReflector\ObjectReflector; -use PHPUnit\SebastianBergmann\RecursionContext\Context; +use PHPUnitPHAR\SebastianBergmann\ObjectReflector\ObjectReflector; +use PHPUnitPHAR\SebastianBergmann\RecursionContext\Context; use Throwable; /** * A snapshot of global state. @@ -91960,7 +102792,7 @@ class Snapshot /** * Creates a snapshot of the current global state. */ - public function __construct(ExcludeList $excludeList = 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) + public function __construct(?ExcludeList $excludeList = 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) { $this->excludeList = $excludeList ?: new ExcludeList(); if ($includeConstants) { @@ -92231,7 +103063,7 @@ declare (strict_types=1); * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\GlobalState; +namespace PHPUnitPHAR\SebastianBergmann\GlobalState; use Throwable; interface Exception extends Throwable @@ -92248,7 +103080,7 @@ declare (strict_types=1); * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\GlobalState; +namespace PHPUnitPHAR\SebastianBergmann\GlobalState; final class RuntimeException extends \RuntimeException implements Exception { @@ -92264,15 +103096,13 @@ declare (strict_types=1); * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\LinesOfCode; +namespace PHPUnitPHAR\SebastianBergmann\LinesOfCode; use function substr_count; -use PHPUnit\PhpParser\Error; -use PHPUnit\PhpParser\Lexer; -use PHPUnit\PhpParser\Node; -use PHPUnit\PhpParser\NodeTraverser; -use PHPUnit\PhpParser\Parser; -use PHPUnit\PhpParser\ParserFactory; +use PHPUnitPHAR\PhpParser\Error; +use PHPUnitPHAR\PhpParser\Node; +use PHPUnitPHAR\PhpParser\NodeTraverser; +use PHPUnitPHAR\PhpParser\ParserFactory; final class Counter { /** @@ -92292,7 +103122,7 @@ final class Counter $linesOfCode = 1; } try { - $nodes = $this->parser()->parse($source); + $nodes = (new ParserFactory())->createForHostVersion()->parse($source); \assert($nodes !== null); return $this->countInAbstractSyntaxTree($linesOfCode, $nodes); // @codeCoverageIgnoreStart @@ -92321,10 +103151,6 @@ final class Counter // @codeCoverageIgnoreEnd return $visitor->result(); } - private function parser() : Parser - { - return (new ParserFactory())->create(ParserFactory::PREFER_PHP7, new Lexer()); - } } addToken(new Token($line, \token_name($tok[0]), '{binary data}')); + continue; + } foreach ($values as $v) { $token = new Token($line, \token_name($tok[0]), $v); $lastToken = $token; @@ -94861,11 +105691,6 @@ class Tokenizer $prev = new Token(0, 'Placeholder', ''); $final = new TokenCollection(); foreach ($tokens as $token) { - if ($prev === null) { - $final->addToken($token); - $prev = $token; - continue; - } $gap = $token->getLine() - $prev->getLine(); while ($gap > 1) { $linebreak = new Token($prev->getLine() + 1, 'T_WHITESPACE', ''); @@ -94889,7 +105714,7 @@ class Tokenizer "$0"]) ); } -RJ _q@jSY i܄ӟ6|4l=ﶉ)z0GBMB \ No newline at end of file +䅢=ƿcL]yџ4 Zl8+AMM]ʆ'2GBMB \ No newline at end of file From 18ab1a9cdc8c8df273e053bfc3ead282a9ab4544 Mon Sep 17 00:00:00 2001 From: Toon Verwerft Date: Tue, 9 Jul 2024 14:25:41 +0200 Subject: [PATCH 10/10] Bump to phunit 10 --- .phive/phars.xml | 2 +- phpunit.xml | 39 +- src/Xml/Reader/Configurator/xsd_schema.php | 2 +- tests/Xml/Dom/Assert/AssertCDataTest.php | 2 +- tests/Xml/Dom/Assert/AssertDocumentTest.php | 2 +- tests/Xml/Dom/Assert/AssertElementTest.php | 2 +- .../Xml/Dom/Configurator/CanonicalizeTest.php | 2 +- tests/Xml/Dom/Configurator/ComparableTest.php | 2 +- .../Configurator/OptimizeNamespacesTest.php | 2 +- .../Locator/Attribute/AttributesListTest.php | 2 +- .../Attribute/XmlnsAttributesListTest.php | 2 +- .../Document/OptimizeNamespacesTest.php | 2 +- tests/Xml/Dom/Manipulator/Node/RenameTest.php | 4 - .../Xml/Dom/Manipulator/Xmlns/RenameTest.php | 2 +- tests/Xml/Dom/Predicate/IsAttributeTest.php | 2 +- tests/Xml/Dom/Predicate/IsCDataTest.php | 2 +- .../Predicate/IsDefaultXmlnsAttributeTest.php | 2 +- .../Dom/Predicate/IsDocumentElementTest.php | 2 +- tests/Xml/Dom/Predicate/IsDocumentTest.php | 2 +- tests/Xml/Dom/Predicate/IsElementTest.php | 2 +- .../Xml/Dom/Predicate/IsNonEmptyTextTest.php | 2 +- tests/Xml/Dom/Predicate/IsTextTest.php | 2 +- tests/Xml/Dom/Predicate/IsWhitespaceTest.php | 2 +- .../Dom/Predicate/IsXmlnsAttributeTest.php | 2 +- .../Validator/InternalXsdValidatorTest.php | 2 +- .../Xml/Dom/Validator/ValidatorChainTest.php | 10 +- tests/Xml/Dom/Validator/XsdValidatorTest.php | 2 +- tests/Xml/Encoding/EncodingTest.php | 26 +- tests/Xml/Encoding/TypedTest.php | 2 +- .../AssertStrictPrefixedNameTest.php | 4 +- .../Xml/ErrorHandling/Issue/UseIssueTrait.php | 2 +- .../IssueLevelFromXmlErrorTest.php | 2 +- .../Xml/Reader/Configurator/XsdSchemaTest.php | 2 +- ...cherTest.php => AbstractMatcherTester.php} | 2 +- tests/Xml/Reader/Matcher/AllTest.php | 2 +- tests/Xml/Reader/Matcher/AnyTest.php | 2 +- .../Reader/Matcher/AttributeLocalNameTest.php | 2 +- .../Matcher/AttributeLocalValueTest.php | 2 +- .../Xml/Reader/Matcher/AttributeNameTest.php | 2 +- .../Xml/Reader/Matcher/AttributeValueTest.php | 2 +- .../Reader/Matcher/DocumentElementTest.php | 2 +- .../Reader/Matcher/ElementLocalNameTest.php | 2 +- tests/Xml/Reader/Matcher/ElementNameTest.php | 2 +- .../Reader/Matcher/ElementPositionTest.php | 2 +- .../Matcher/NamespacedAttributeTest.php | 2 +- .../Matcher/NamespacedAttributeValueTest.php | 2 +- .../Matcher/NamespacedElementNameTest.php | 2 +- tests/Xml/Reader/Matcher/NestedTest.php | 2 +- tests/Xml/Reader/Matcher/NotTest.php | 2 +- tests/Xml/Reader/Matcher/SequenceTest.php | 2 +- tests/Xml/Reader/ReaderTest.php | 2 +- .../Writer/Builder/NamespaceAttributeTest.php | 8 +- .../Writer/Builder/NamespacedElementTest.php | 8 +- .../Writer/Builder/PrefixedAttributeTest.php | 13 +- .../Writer/Builder/PrefixedAttributesTest.php | 17 +- tests/Xml/Xmlns/XmlnsTest.php | 2 +- tools/phpunit.phar | 134581 +++++++-------- 57 files changed, 64732 insertions(+), 70072 deletions(-) rename tests/Xml/Reader/Matcher/{AbstractMatcherTest.php => AbstractMatcherTester.php} (95%) diff --git a/.phive/phars.xml b/.phive/phars.xml index 3c109e8..3107fd6 100644 --- a/.phive/phars.xml +++ b/.phive/phars.xml @@ -1,6 +1,6 @@ - + diff --git a/phpunit.xml b/phpunit.xml index 1dbb0c7..eecef62 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -1,26 +1,19 @@ - - - src - - - src/bootstrap.php - + - - + + @@ -28,4 +21,12 @@ tests/Xml + + + src + + + src/bootstrap.php + + diff --git a/src/Xml/Reader/Configurator/xsd_schema.php b/src/Xml/Reader/Configurator/xsd_schema.php index 1107cf0..383fa69 100644 --- a/src/Xml/Reader/Configurator/xsd_schema.php +++ b/src/Xml/Reader/Configurator/xsd_schema.php @@ -20,7 +20,7 @@ function xsd_schema(string $schemaFile): Closure Assert::fileExists($schemaFile); disallow_libxml_false_returns( - $reader->setSchema($schemaFile), + @$reader->setSchema($schemaFile), 'Unable to apply XSD schema to the XML Reader.' ); diff --git a/tests/Xml/Dom/Assert/AssertCDataTest.php b/tests/Xml/Dom/Assert/AssertCDataTest.php index a0739e5..2ff980a 100644 --- a/tests/Xml/Dom/Assert/AssertCDataTest.php +++ b/tests/Xml/Dom/Assert/AssertCDataTest.php @@ -26,7 +26,7 @@ public function test_it_knows_cdata(?\DOM\Node $node, bool $expected): void static::assertSame($node, $actual); } - public function provideTestCases() + public static function provideTestCases() { $doc = Document::fromXmlString( << [ '', diff --git a/tests/Xml/Dom/Configurator/ComparableTest.php b/tests/Xml/Dom/Configurator/ComparableTest.php index 0765626..7cdd830 100644 --- a/tests/Xml/Dom/Configurator/ComparableTest.php +++ b/tests/Xml/Dom/Configurator/ComparableTest.php @@ -23,7 +23,7 @@ public function test_it_can_canonicalize(string $input, string $expected): void static::assertSame($expected, $actual); } - public function provideXmls() + public static function provideXmls() { yield 'no-action' => [ '', diff --git a/tests/Xml/Dom/Configurator/OptimizeNamespacesTest.php b/tests/Xml/Dom/Configurator/OptimizeNamespacesTest.php index 8f2224e..158117c 100644 --- a/tests/Xml/Dom/Configurator/OptimizeNamespacesTest.php +++ b/tests/Xml/Dom/Configurator/OptimizeNamespacesTest.php @@ -23,7 +23,7 @@ public function test_it_can_optimize_namespaces(string $input, string $expected) static::assertSame($expected, $actual); } - public function provideXmls() + public static function provideXmls() { yield 'no-action' => [ '', diff --git a/tests/Xml/Dom/Locator/Attribute/AttributesListTest.php b/tests/Xml/Dom/Locator/Attribute/AttributesListTest.php index 7e6d3b4..be1b2e2 100644 --- a/tests/Xml/Dom/Locator/Attribute/AttributesListTest.php +++ b/tests/Xml/Dom/Locator/Attribute/AttributesListTest.php @@ -21,7 +21,7 @@ public function test_it_can_fetch_xmlns_attribute_list_from_node(\DOM\Node $node static::assertEquals($expected, [...$actual]); } - public function provideTestCases() + public static function provideTestCases() { $doc = Document::fromXmlString( << [ '', diff --git a/tests/Xml/Dom/Manipulator/Node/RenameTest.php b/tests/Xml/Dom/Manipulator/Node/RenameTest.php index 7c912f7..bf3d017 100644 --- a/tests/Xml/Dom/Manipulator/Node/RenameTest.php +++ b/tests/Xml/Dom/Manipulator/Node/RenameTest.php @@ -160,12 +160,8 @@ public function test_it_can_rename_namespaced_attributes(): void static::assertSame($root->getAttributeNode('a:you'), $result); } - /** - * https://github.com/php/php-src/blob/1c8bb6d6818c10a691d6836b336bcee003478b44/ext/dom/element.c#L663 - */ public function test_it_can_not_rename_namespaced_attribute_prefix_when_the_xmlns_is_still_available(): void { - $this->markAsRisky('Broken DOM functionality'); $doc = Document::fromXmlString(''); $root = $doc->map(document_element()); $node = $root->getAttributeNode('a:who'); diff --git a/tests/Xml/Dom/Manipulator/Xmlns/RenameTest.php b/tests/Xml/Dom/Manipulator/Xmlns/RenameTest.php index c94c0f0..07b3562 100644 --- a/tests/Xml/Dom/Manipulator/Xmlns/RenameTest.php +++ b/tests/Xml/Dom/Manipulator/Xmlns/RenameTest.php @@ -24,7 +24,7 @@ public function test_it_can_rename_namespaces(string $input, string $expected): static::assertSame($expected, $actual); } - public function provideXmls() + public static function provideXmls() { yield 'simple' => [ '', diff --git a/tests/Xml/Dom/Predicate/IsAttributeTest.php b/tests/Xml/Dom/Predicate/IsAttributeTest.php index a1689be..476646d 100644 --- a/tests/Xml/Dom/Predicate/IsAttributeTest.php +++ b/tests/Xml/Dom/Predicate/IsAttributeTest.php @@ -20,7 +20,7 @@ public function test_it_knows_attributes(\DOM\Node $node, bool $expected): void static::assertSame($expected, is_attribute($node)); } - public function provideTestCases() + public static function provideTestCases() { $doc = Document::fromXmlString( << [ 'xml' => 'xml-valid.xml', diff --git a/tests/Xml/Dom/Validator/ValidatorChainTest.php b/tests/Xml/Dom/Validator/ValidatorChainTest.php index 5ca3831..aa0994f 100644 --- a/tests/Xml/Dom/Validator/ValidatorChainTest.php +++ b/tests/Xml/Dom/Validator/ValidatorChainTest.php @@ -29,7 +29,7 @@ public function test_it_can_validate_multiple_validators(callable $validator, in static::assertCount($errors, $issues); } - public function provideErrorCases() + public static function provideErrorCases() { yield 'empty' => [ 'validator' => validator_chain(), @@ -47,7 +47,7 @@ public function provideErrorCases() 'validator' => validator_chain( static fn (DOMDocument $document) => new IssueCollection(), fn (DOMDocument $document) => new IssueCollection( - $this->createIssue(Level::fatal()) + self::createIssue(Level::fatal()) ), static fn (DOMDocument $document) => new IssueCollection(), static fn (DOMDocument $document) => new IssueCollection() @@ -57,11 +57,11 @@ public function provideErrorCases() yield 'allFails' => [ 'validator' => validator_chain( fn (DOMDocument $document) => new IssueCollection( - $this->createIssue(Level::fatal()) + self::createIssue(Level::fatal()) ), fn (DOMDocument $document) => new IssueCollection( - $this->createIssue(Level::fatal()), - $this->createIssue(Level::fatal()) + self::createIssue(Level::fatal()), + self::createIssue(Level::fatal()) ), ), 'errors' => 3, diff --git a/tests/Xml/Dom/Validator/XsdValidatorTest.php b/tests/Xml/Dom/Validator/XsdValidatorTest.php index 4a2d9bd..54842a5 100644 --- a/tests/Xml/Dom/Validator/XsdValidatorTest.php +++ b/tests/Xml/Dom/Validator/XsdValidatorTest.php @@ -32,7 +32,7 @@ public function test_it_can_validate_xsds(string $xml, string $xsd, int $errors) /** * @return array */ - public function provideSchemeValidation() + public static function provideSchemeValidation() { yield 'valid' => [ 'xml' => 'xml-valid.xml', diff --git a/tests/Xml/Encoding/EncodingTest.php b/tests/Xml/Encoding/EncodingTest.php index ff373ad..d694732 100644 --- a/tests/Xml/Encoding/EncodingTest.php +++ b/tests/Xml/Encoding/EncodingTest.php @@ -103,13 +103,13 @@ public function test_it_errors_while_encoding_invalid_xml_element(string $xml, a /** * @dataProvider provideInvalidXml */ - public function test_it_errors_while_decoding_invalid_xml(string $xml) + public function test_it_errors_while_decoding_invalid_xml(string $xml, array $data) { $this->expectException(EncodingException::class); xml_decode($xml); } - public function provideBidirectionalCases() + public static function provideBidirectionalCases() { yield 'empty' => [ 'xml' => '', @@ -239,19 +239,13 @@ public function provideBidirectionalCases() ] ] ]; - yield 'cdata' => [ - 'xml' => 'world]]>', - 'data' => ['hello' => [ - '@cdata' => 'world' - ]] - ]; yield 'mixed cdata' => [ 'xml' => 'hello world]]>', 'data' => ['hello' => 'hello world'] ]; } - public function provideRiskyBidirectionalCases() + public static function provideRiskyBidirectionalCases() { yield 'namespaced' => [ 'xml' => << [ 'xml' => << [ + 'xml' => 'world]]>', + 'data' => ['hello' => [ + '@cdata' => 'world' + ]] + ]; } - public function provideDecodingOnly() + public static function provideDecodingOnly() { yield 'cdata' => [ 'xml' => '', @@ -345,7 +345,7 @@ public function provideDecodingOnly() ]; } - public function provideRiskyDecodingOnly() + public static function provideRiskyDecodingOnly() { yield 'falsy namespaced' => [ 'xml' => << [ 'xml' => << [ 'xml' => <<matches($actual)); } - public function provideErrors() + public static function provideErrors() { yield 'error' => [ LIBXML_ERR_ERROR, diff --git a/tests/Xml/Reader/Configurator/XsdSchemaTest.php b/tests/Xml/Reader/Configurator/XsdSchemaTest.php index 98bd323..73c9990 100644 --- a/tests/Xml/Reader/Configurator/XsdSchemaTest.php +++ b/tests/Xml/Reader/Configurator/XsdSchemaTest.php @@ -105,7 +105,7 @@ public function test_it_can_not_set_a_schema_if_the_schema_is_invalid(): void $iterator = $reader->provide(element_name('user')); $this->expectException(RuntimeException::class); - $this->expectExceptionMessage('Schema contains errors'); + $this->expectExceptionMessage('Unable to apply XSD schema to the XML Reader.'); [...$iterator]; fclose($xsdHandle); diff --git a/tests/Xml/Reader/Matcher/AbstractMatcherTest.php b/tests/Xml/Reader/Matcher/AbstractMatcherTester.php similarity index 95% rename from tests/Xml/Reader/Matcher/AbstractMatcherTest.php rename to tests/Xml/Reader/Matcher/AbstractMatcherTester.php index cb490d2..fb33337 100644 --- a/tests/Xml/Reader/Matcher/AbstractMatcherTest.php +++ b/tests/Xml/Reader/Matcher/AbstractMatcherTester.php @@ -11,7 +11,7 @@ use VeeWee\Xml\Reader\Reader; use function Psl\Vec\map; -abstract class AbstractMatcherTest extends TestCase +abstract class AbstractMatcherTester extends TestCase { abstract public static function provideRealXmlCases(): Generator; abstract public static function provideMatcherCases(): Generator; diff --git a/tests/Xml/Reader/Matcher/AllTest.php b/tests/Xml/Reader/Matcher/AllTest.php index bdf824a..3d1505b 100644 --- a/tests/Xml/Reader/Matcher/AllTest.php +++ b/tests/Xml/Reader/Matcher/AllTest.php @@ -9,7 +9,7 @@ use function VeeWee\Xml\Reader\Matcher\all; use function VeeWee\Xml\Reader\Matcher\element_name; -final class AllTest extends AbstractMatcherTest +final class AllTest extends AbstractMatcherTester { public static function provideRealXmlCases(): Generator { diff --git a/tests/Xml/Reader/Matcher/AnyTest.php b/tests/Xml/Reader/Matcher/AnyTest.php index 3fb9aff..3806241 100644 --- a/tests/Xml/Reader/Matcher/AnyTest.php +++ b/tests/Xml/Reader/Matcher/AnyTest.php @@ -9,7 +9,7 @@ use function VeeWee\Xml\Reader\Matcher\any; use function VeeWee\Xml\Reader\Matcher\element_name; -final class AnyTest extends AbstractMatcherTest +final class AnyTest extends AbstractMatcherTester { public static function provideRealXmlCases(): Generator { diff --git a/tests/Xml/Reader/Matcher/AttributeLocalNameTest.php b/tests/Xml/Reader/Matcher/AttributeLocalNameTest.php index 6c0e635..1bd639b 100644 --- a/tests/Xml/Reader/Matcher/AttributeLocalNameTest.php +++ b/tests/Xml/Reader/Matcher/AttributeLocalNameTest.php @@ -10,7 +10,7 @@ use VeeWee\Xml\Reader\Node\NodeSequence; use function VeeWee\Xml\Reader\Matcher\attribute_local_name; -final class AttributeLocalNameTest extends AbstractMatcherTest +final class AttributeLocalNameTest extends AbstractMatcherTester { public static function provideRealXmlCases(): Generator { diff --git a/tests/Xml/Reader/Matcher/AttributeLocalValueTest.php b/tests/Xml/Reader/Matcher/AttributeLocalValueTest.php index a3d9edc..fe77446 100644 --- a/tests/Xml/Reader/Matcher/AttributeLocalValueTest.php +++ b/tests/Xml/Reader/Matcher/AttributeLocalValueTest.php @@ -10,7 +10,7 @@ use VeeWee\Xml\Reader\Node\NodeSequence; use function VeeWee\Xml\Reader\Matcher\attribute_local_value; -final class AttributeLocalValueTest extends AbstractMatcherTest +final class AttributeLocalValueTest extends AbstractMatcherTester { public static function provideRealXmlCases(): Generator { diff --git a/tests/Xml/Reader/Matcher/AttributeNameTest.php b/tests/Xml/Reader/Matcher/AttributeNameTest.php index 6753c8f..f50e98a 100644 --- a/tests/Xml/Reader/Matcher/AttributeNameTest.php +++ b/tests/Xml/Reader/Matcher/AttributeNameTest.php @@ -10,7 +10,7 @@ use VeeWee\Xml\Reader\Node\NodeSequence; use function VeeWee\Xml\Reader\Matcher\attribute_name; -final class AttributeNameTest extends AbstractMatcherTest +final class AttributeNameTest extends AbstractMatcherTester { public static function provideRealXmlCases(): Generator { diff --git a/tests/Xml/Reader/Matcher/AttributeValueTest.php b/tests/Xml/Reader/Matcher/AttributeValueTest.php index faf0523..e0fe29e 100644 --- a/tests/Xml/Reader/Matcher/AttributeValueTest.php +++ b/tests/Xml/Reader/Matcher/AttributeValueTest.php @@ -10,7 +10,7 @@ use VeeWee\Xml\Reader\Node\NodeSequence; use function VeeWee\Xml\Reader\Matcher\attribute_value; -final class AttributeValueTest extends AbstractMatcherTest +final class AttributeValueTest extends AbstractMatcherTester { public static function provideRealXmlCases(): Generator { diff --git a/tests/Xml/Reader/Matcher/DocumentElementTest.php b/tests/Xml/Reader/Matcher/DocumentElementTest.php index 02d16b0..59dd361 100644 --- a/tests/Xml/Reader/Matcher/DocumentElementTest.php +++ b/tests/Xml/Reader/Matcher/DocumentElementTest.php @@ -9,7 +9,7 @@ use VeeWee\Xml\Reader\Node\NodeSequence; use function VeeWee\Xml\Reader\Matcher\document_element; -final class DocumentElementTest extends AbstractMatcherTest +final class DocumentElementTest extends AbstractMatcherTester { public static function provideRealXmlCases(): Generator { diff --git a/tests/Xml/Reader/Matcher/ElementLocalNameTest.php b/tests/Xml/Reader/Matcher/ElementLocalNameTest.php index 53c0d76..7d99769 100644 --- a/tests/Xml/Reader/Matcher/ElementLocalNameTest.php +++ b/tests/Xml/Reader/Matcher/ElementLocalNameTest.php @@ -9,7 +9,7 @@ use VeeWee\Xml\Reader\Node\NodeSequence; use function VeeWee\Xml\Reader\Matcher\element_local_name; -final class ElementLocalNameTest extends AbstractMatcherTest +final class ElementLocalNameTest extends AbstractMatcherTester { public static function provideRealXmlCases(): Generator { diff --git a/tests/Xml/Reader/Matcher/ElementNameTest.php b/tests/Xml/Reader/Matcher/ElementNameTest.php index a806a16..643594a 100644 --- a/tests/Xml/Reader/Matcher/ElementNameTest.php +++ b/tests/Xml/Reader/Matcher/ElementNameTest.php @@ -9,7 +9,7 @@ use VeeWee\Xml\Reader\Node\NodeSequence; use function VeeWee\Xml\Reader\Matcher\element_name; -final class ElementNameTest extends AbstractMatcherTest +final class ElementNameTest extends AbstractMatcherTester { public static function provideRealXmlCases(): Generator { diff --git a/tests/Xml/Reader/Matcher/ElementPositionTest.php b/tests/Xml/Reader/Matcher/ElementPositionTest.php index 3d298f4..5bd6c84 100644 --- a/tests/Xml/Reader/Matcher/ElementPositionTest.php +++ b/tests/Xml/Reader/Matcher/ElementPositionTest.php @@ -9,7 +9,7 @@ use VeeWee\Xml\Reader\Node\NodeSequence; use function VeeWee\Xml\Reader\Matcher\element_position; -final class ElementPositionTest extends AbstractMatcherTest +final class ElementPositionTest extends AbstractMatcherTester { public static function provideRealXmlCases(): Generator { diff --git a/tests/Xml/Reader/Matcher/NamespacedAttributeTest.php b/tests/Xml/Reader/Matcher/NamespacedAttributeTest.php index 5c3218e..8776c36 100644 --- a/tests/Xml/Reader/Matcher/NamespacedAttributeTest.php +++ b/tests/Xml/Reader/Matcher/NamespacedAttributeTest.php @@ -10,7 +10,7 @@ use VeeWee\Xml\Reader\Node\NodeSequence; use function VeeWee\Xml\Reader\Matcher\namespaced_attribute; -final class NamespacedAttributeTest extends AbstractMatcherTest +final class NamespacedAttributeTest extends AbstractMatcherTester { public static function provideRealXmlCases(): Generator { diff --git a/tests/Xml/Reader/Matcher/NamespacedAttributeValueTest.php b/tests/Xml/Reader/Matcher/NamespacedAttributeValueTest.php index acf5aa8..1a20a66 100644 --- a/tests/Xml/Reader/Matcher/NamespacedAttributeValueTest.php +++ b/tests/Xml/Reader/Matcher/NamespacedAttributeValueTest.php @@ -10,7 +10,7 @@ use VeeWee\Xml\Reader\Node\NodeSequence; use function VeeWee\Xml\Reader\Matcher\namespaced_attribute_value; -final class NamespacedAttributeValueTest extends AbstractMatcherTest +final class NamespacedAttributeValueTest extends AbstractMatcherTester { public static function provideRealXmlCases(): Generator { diff --git a/tests/Xml/Reader/Matcher/NamespacedElementNameTest.php b/tests/Xml/Reader/Matcher/NamespacedElementNameTest.php index eff410b..75b7ec0 100644 --- a/tests/Xml/Reader/Matcher/NamespacedElementNameTest.php +++ b/tests/Xml/Reader/Matcher/NamespacedElementNameTest.php @@ -9,7 +9,7 @@ use VeeWee\Xml\Reader\Node\NodeSequence; use function VeeWee\Xml\Reader\Matcher\namespaced_element; -final class NamespacedElementNameTest extends AbstractMatcherTest +final class NamespacedElementNameTest extends AbstractMatcherTester { public static function provideRealXmlCases(): Generator { diff --git a/tests/Xml/Reader/Matcher/NestedTest.php b/tests/Xml/Reader/Matcher/NestedTest.php index 5f08800..e4f62b9 100644 --- a/tests/Xml/Reader/Matcher/NestedTest.php +++ b/tests/Xml/Reader/Matcher/NestedTest.php @@ -14,7 +14,7 @@ use function VeeWee\Xml\Reader\Matcher\nested; use function VeeWee\Xml\Reader\Matcher\sequence; -final class NestedTest extends AbstractMatcherTest +final class NestedTest extends AbstractMatcherTester { public static function provideRealXmlCases(): Generator { diff --git a/tests/Xml/Reader/Matcher/NotTest.php b/tests/Xml/Reader/Matcher/NotTest.php index f7edd21..597e237 100644 --- a/tests/Xml/Reader/Matcher/NotTest.php +++ b/tests/Xml/Reader/Matcher/NotTest.php @@ -9,7 +9,7 @@ use function VeeWee\Xml\Reader\Matcher\element_name; use function VeeWee\Xml\Reader\Matcher\not; -final class NotTest extends AbstractMatcherTest +final class NotTest extends AbstractMatcherTester { public static function provideRealXmlCases(): Generator { diff --git a/tests/Xml/Reader/Matcher/SequenceTest.php b/tests/Xml/Reader/Matcher/SequenceTest.php index b12a00a..5a0ac22 100644 --- a/tests/Xml/Reader/Matcher/SequenceTest.php +++ b/tests/Xml/Reader/Matcher/SequenceTest.php @@ -13,7 +13,7 @@ use function VeeWee\Xml\Reader\Matcher\element_name; use function VeeWee\Xml\Reader\Matcher\sequence; -final class SequenceTest extends AbstractMatcherTest +final class SequenceTest extends AbstractMatcherTester { public static function provideRealXmlCases(): Generator { diff --git a/tests/Xml/Reader/ReaderTest.php b/tests/Xml/Reader/ReaderTest.php index baf6e5e..ea4b7f0 100644 --- a/tests/Xml/Reader/ReaderTest.php +++ b/tests/Xml/Reader/ReaderTest.php @@ -90,7 +90,7 @@ public function test_it_can_send_stop_signal(): void static::assertSame(['Jos'], $actual); } - public function provideXmlExpectations() + public static function provideXmlExpectations() { yield 'simple' => [ <<<'EOXML' diff --git a/tests/Xml/Writer/Builder/NamespaceAttributeTest.php b/tests/Xml/Writer/Builder/NamespaceAttributeTest.php index 67bcce9..0490cf7 100644 --- a/tests/Xml/Writer/Builder/NamespaceAttributeTest.php +++ b/tests/Xml/Writer/Builder/NamespaceAttributeTest.php @@ -15,7 +15,7 @@ final class NamespaceAttributeTest extends TestCase { use UseInMemoryWriterTrait; - + public function test_it_can_create_namespace_attribute_without_prefix(): void { $result = $this->runInMemory(static function (XMLWriter $xmlWriter): void { @@ -28,16 +28,16 @@ public function test_it_can_create_namespace_attribute_without_prefix(): void static::assertXmlStringEqualsXmlString('', $result); } - + public function test_it_can_create_namespace_attribute_with_prefix(): void { $result = $this->runInMemory(static function (XMLWriter $xmlWriter): void { $writer = Writer::fromUnsafeWriter($xmlWriter); $writer->write( - element('root', namespace_attribute('https://awesome.xom', 'xml')) + element('root', namespace_attribute('https://awesome.xom', 'awesome')) ); }); - static::assertXmlStringEqualsXmlString('', $result); + static::assertXmlStringEqualsXmlString('', $result); } } diff --git a/tests/Xml/Writer/Builder/NamespacedElementTest.php b/tests/Xml/Writer/Builder/NamespacedElementTest.php index db18569..caf205f 100644 --- a/tests/Xml/Writer/Builder/NamespacedElementTest.php +++ b/tests/Xml/Writer/Builder/NamespacedElementTest.php @@ -21,11 +21,11 @@ public function test_it_can_create_self_closing_element(): void $result = $this->runInMemory(static function (XMLWriter $xmlWriter): void { $writer = Writer::fromUnsafeWriter($xmlWriter); $writer->write( - namespaced_element('http://ns', 'xml', 'root') + namespaced_element('http://ns', 'ns', 'root') ); }); - static::assertXmlStringEqualsXmlString('', $result); + static::assertXmlStringEqualsXmlString('', $result); } @@ -34,11 +34,11 @@ public function test_it_can_create_element_with_value(): void $result = $this->runInMemory(static function (XMLWriter $xmlWriter): void { $writer = Writer::fromUnsafeWriter($xmlWriter); $writer->write( - namespaced_element('http://ns', 'xml', 'hello', value('world')) + namespaced_element('http://ns', 'ns', 'hello', value('world')) ); }); - static::assertXmlStringEqualsXmlString('world', $result); + static::assertXmlStringEqualsXmlString('world', $result); } public function test_it_can_create_element_without_prefix(): void diff --git a/tests/Xml/Writer/Builder/PrefixedAttributeTest.php b/tests/Xml/Writer/Builder/PrefixedAttributeTest.php index 00807ed..7957b3b 100644 --- a/tests/Xml/Writer/Builder/PrefixedAttributeTest.php +++ b/tests/Xml/Writer/Builder/PrefixedAttributeTest.php @@ -10,29 +10,34 @@ use VeeWee\Xml\Writer\Writer; use XMLWriter; use function VeeWee\Xml\Writer\Builder\element; +use function VeeWee\Xml\Writer\Builder\children; +use function VeeWee\Xml\Writer\Builder\namespace_attribute; use function VeeWee\Xml\Writer\Builder\prefixed_attribute; final class PrefixedAttributeTest extends TestCase { use UseInMemoryWriterTrait; - + public function test_it_can_add_atribute_to_element(): void { $result = $this->runInMemory(static function (XMLWriter $xmlWriter): void { $writer = Writer::fromUnsafeWriter($xmlWriter); $writer->write( - element('hello', prefixed_attribute('pfx', 'value', 'world')) + element('hello', children([ + namespace_attribute('http://pfx', 'pfx'), + prefixed_attribute('pfx', 'value', 'world'), + ])) ); }); static::assertXmlStringEqualsXmlString( - '', + '', $result ); } - + public function test_it_can_not_write_attribute_to_invalid_context(): void { $this->expectException(RuntimeException::class); diff --git a/tests/Xml/Writer/Builder/PrefixedAttributesTest.php b/tests/Xml/Writer/Builder/PrefixedAttributesTest.php index bd7bb9c..4173e04 100644 --- a/tests/Xml/Writer/Builder/PrefixedAttributesTest.php +++ b/tests/Xml/Writer/Builder/PrefixedAttributesTest.php @@ -9,33 +9,38 @@ use VeeWee\Xml\Exception\RuntimeException; use VeeWee\Xml\Writer\Writer; use XMLWriter; +use function VeeWee\Xml\Writer\Builder\children; use function VeeWee\Xml\Writer\Builder\element; +use function VeeWee\Xml\Writer\Builder\namespace_attribute; use function VeeWee\Xml\Writer\Builder\prefixed_attributes; final class PrefixedAttributesTest extends TestCase { use UseInMemoryWriterTrait; - + public function test_it_can_add_atributes_to_element(): void { $result = $this->runInMemory(static function (XMLWriter $xmlWriter): void { $writer = Writer::fromUnsafeWriter($xmlWriter); $writer->write( - element('hello', prefixed_attributes([ - 'pfx:default' => 'world', - 'pfx:value' => 'Jos', + element('hello', children([ + namespace_attribute('http://pfx', 'pfx'), + prefixed_attributes([ + 'pfx:default' => 'world', + 'pfx:value' => 'Jos', + ]) ])) ); }); static::assertXmlStringEqualsXmlString( - '', + '', $result ); } - + public function test_it_throws_exception_on_non_prefixed_attribute(): void { $this->expectException(RuntimeException::class); diff --git a/tests/Xml/Xmlns/XmlnsTest.php b/tests/Xml/Xmlns/XmlnsTest.php index 2639853..6008592 100644 --- a/tests/Xml/Xmlns/XmlnsTest.php +++ b/tests/Xml/Xmlns/XmlnsTest.php @@ -21,7 +21,7 @@ public function test_it_knows_some_xmlnses(callable $factory, string $uri): void static::assertTrue($xmlns->matches(Xmlns::load($uri))); } - public function provideKnownXmlnses() + public static function provideKnownXmlnses() { yield 'xml' => [ static fn () => Xmlns::xml(), diff --git a/tools/phpunit.phar b/tools/phpunit.phar index c517982..77f1d5d 100755 --- a/tools/phpunit.phar +++ b/tools/phpunit.phar @@ -15,12 +15,12 @@ if (!version_compare(PHP_VERSION, PHP_VERSION, '=')) { die(1); } -if (version_compare('7.3.0', PHP_VERSION, '>')) { +if (version_compare('8.1.0', PHP_VERSION, '>')) { fwrite( STDERR, sprintf( - 'PHPUnit 9.6.19 by Sebastian Bergmann and contributors.' . PHP_EOL . PHP_EOL . - 'This version of PHPUnit requires PHP >= 7.3.' . PHP_EOL . + 'PHPUnit 10.5.26 by Sebastian Bergmann and contributors.' . PHP_EOL . PHP_EOL . + 'This version of PHPUnit requires PHP >= 8.1.' . PHP_EOL . 'You are using PHP %s (%s).' . PHP_EOL, PHP_VERSION, PHP_BINARY @@ -30,7 +30,7 @@ if (version_compare('7.3.0', PHP_VERSION, '>')) { die(1); } -$requiredExtensions = ['dom', 'json', 'libxml', 'mbstring', 'tokenizer', 'xml', 'xmlwriter']; +$requiredExtensions = ['ctype', 'dom', 'json', 'libxml', 'mbstring', 'tokenizer', 'xml', 'xmlwriter']; $unavailableExtensions = array_filter( $requiredExtensions, @@ -61,11 +61,7 @@ if (__FILE__ === realpath($_SERVER['SCRIPT_NAME'])) { $execute = false; } -$options = getopt('', array('prepend:', 'composer-lock', 'manifest', 'sbom')); - -if (isset($options['prepend'])) { - require $options['prepend']; -} +$options = getopt('', array('composer-lock', 'manifest', 'sbom')); if (isset($options['composer-lock'])) { $printComposerLock = true; @@ -78,17 +74,16 @@ if (isset($options['composer-lock'])) { unset($options); define('__PHPUNIT_PHAR__', str_replace(DIRECTORY_SEPARATOR, '/', __FILE__)); -define('__PHPUNIT_PHAR_ROOT__', 'phar://phpunit-9.6.19.phar'); +define('__PHPUNIT_PHAR_ROOT__', 'phar://phpunit-10.5.26.phar'); -Phar::mapPhar('phpunit-9.6.19.phar'); +Phar::mapPhar('phpunit-10.5.26.phar'); spl_autoload_register( function ($class) { static $classes = null; if ($classes === null) { - $classes = ['Doctrine\\Deprecations\\PHPUnit\\VerifyDeprecations' => '/doctrine-deprecations/Doctrine/Deprecations/PHPUnit/VerifyDeprecations.php', - 'PHPUnitPHAR\\DeepCopy\\DeepCopy' => '/myclabs-deep-copy/DeepCopy/DeepCopy.php', + $classes = ['PHPUnitPHAR\\DeepCopy\\DeepCopy' => '/myclabs-deep-copy/DeepCopy/DeepCopy.php', 'PHPUnitPHAR\\DeepCopy\\Exception\\CloneException' => '/myclabs-deep-copy/DeepCopy/Exception/CloneException.php', 'PHPUnitPHAR\\DeepCopy\\Exception\\PropertyException' => '/myclabs-deep-copy/DeepCopy/Exception/PropertyException.php', 'PHPUnitPHAR\\DeepCopy\\Filter\\ChainableFilter' => '/myclabs-deep-copy/DeepCopy/Filter/ChainableFilter.php', @@ -113,98 +108,6 @@ spl_autoload_register( 'PHPUnitPHAR\\DeepCopy\\TypeFilter\\Spl\\SplDoublyLinkedListFilter' => '/myclabs-deep-copy/DeepCopy/TypeFilter/Spl/SplDoublyLinkedListFilter.php', 'PHPUnitPHAR\\DeepCopy\\TypeFilter\\TypeFilter' => '/myclabs-deep-copy/DeepCopy/TypeFilter/TypeFilter.php', 'PHPUnitPHAR\\DeepCopy\\TypeMatcher\\TypeMatcher' => '/myclabs-deep-copy/DeepCopy/TypeMatcher/TypeMatcher.php', - 'PHPUnitPHAR\\Doctrine\\Deprecations\\Deprecation' => '/doctrine-deprecations/Doctrine/Deprecations/Deprecation.php', - 'PHPUnitPHAR\\Doctrine\\Instantiator\\Exception\\ExceptionInterface' => '/doctrine-instantiator/Doctrine/Instantiator/Exception/ExceptionInterface.php', - 'PHPUnitPHAR\\Doctrine\\Instantiator\\Exception\\InvalidArgumentException' => '/doctrine-instantiator/Doctrine/Instantiator/Exception/InvalidArgumentException.php', - 'PHPUnitPHAR\\Doctrine\\Instantiator\\Exception\\UnexpectedValueException' => '/doctrine-instantiator/Doctrine/Instantiator/Exception/UnexpectedValueException.php', - 'PHPUnitPHAR\\Doctrine\\Instantiator\\Instantiator' => '/doctrine-instantiator/Doctrine/Instantiator/Instantiator.php', - 'PHPUnitPHAR\\Doctrine\\Instantiator\\InstantiatorInterface' => '/doctrine-instantiator/Doctrine/Instantiator/InstantiatorInterface.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\AbstractNodeVisitor' => '/phpstan-phpdoc-parser/Ast/AbstractNodeVisitor.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\Attribute' => '/phpstan-phpdoc-parser/Ast/Attribute.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\ConstExpr\\ConstExprArrayItemNode' => '/phpstan-phpdoc-parser/Ast/ConstExpr/ConstExprArrayItemNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\ConstExpr\\ConstExprArrayNode' => '/phpstan-phpdoc-parser/Ast/ConstExpr/ConstExprArrayNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\ConstExpr\\ConstExprFalseNode' => '/phpstan-phpdoc-parser/Ast/ConstExpr/ConstExprFalseNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\ConstExpr\\ConstExprFloatNode' => '/phpstan-phpdoc-parser/Ast/ConstExpr/ConstExprFloatNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\ConstExpr\\ConstExprIntegerNode' => '/phpstan-phpdoc-parser/Ast/ConstExpr/ConstExprIntegerNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\ConstExpr\\ConstExprNode' => '/phpstan-phpdoc-parser/Ast/ConstExpr/ConstExprNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\ConstExpr\\ConstExprNullNode' => '/phpstan-phpdoc-parser/Ast/ConstExpr/ConstExprNullNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\ConstExpr\\ConstExprStringNode' => '/phpstan-phpdoc-parser/Ast/ConstExpr/ConstExprStringNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\ConstExpr\\ConstExprTrueNode' => '/phpstan-phpdoc-parser/Ast/ConstExpr/ConstExprTrueNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\ConstExpr\\ConstFetchNode' => '/phpstan-phpdoc-parser/Ast/ConstExpr/ConstFetchNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\ConstExpr\\DoctrineConstExprStringNode' => '/phpstan-phpdoc-parser/Ast/ConstExpr/DoctrineConstExprStringNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\ConstExpr\\QuoteAwareConstExprStringNode' => '/phpstan-phpdoc-parser/Ast/ConstExpr/QuoteAwareConstExprStringNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\Node' => '/phpstan-phpdoc-parser/Ast/Node.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\NodeAttributes' => '/phpstan-phpdoc-parser/Ast/NodeAttributes.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\NodeTraverser' => '/phpstan-phpdoc-parser/Ast/NodeTraverser.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\NodeVisitor' => '/phpstan-phpdoc-parser/Ast/NodeVisitor.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\NodeVisitor\\CloningVisitor' => '/phpstan-phpdoc-parser/Ast/NodeVisitor/CloningVisitor.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\AssertTagMethodValueNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/AssertTagMethodValueNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\AssertTagPropertyValueNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/AssertTagPropertyValueNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\AssertTagValueNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/AssertTagValueNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\DeprecatedTagValueNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/DeprecatedTagValueNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\Doctrine\\DoctrineAnnotation' => '/phpstan-phpdoc-parser/Ast/PhpDoc/Doctrine/DoctrineAnnotation.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\Doctrine\\DoctrineArgument' => '/phpstan-phpdoc-parser/Ast/PhpDoc/Doctrine/DoctrineArgument.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\Doctrine\\DoctrineArray' => '/phpstan-phpdoc-parser/Ast/PhpDoc/Doctrine/DoctrineArray.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\Doctrine\\DoctrineArrayItem' => '/phpstan-phpdoc-parser/Ast/PhpDoc/Doctrine/DoctrineArrayItem.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\Doctrine\\DoctrineTagValueNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/Doctrine/DoctrineTagValueNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\ExtendsTagValueNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/ExtendsTagValueNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\GenericTagValueNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/GenericTagValueNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\ImplementsTagValueNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/ImplementsTagValueNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\InvalidTagValueNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/InvalidTagValueNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\MethodTagValueNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/MethodTagValueNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\MethodTagValueParameterNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/MethodTagValueParameterNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\MixinTagValueNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/MixinTagValueNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\ParamClosureThisTagValueNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/ParamClosureThisTagValueNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\ParamImmediatelyInvokedCallableTagValueNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/ParamImmediatelyInvokedCallableTagValueNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\ParamLaterInvokedCallableTagValueNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/ParamLaterInvokedCallableTagValueNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\ParamOutTagValueNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/ParamOutTagValueNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\ParamTagValueNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/ParamTagValueNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\PhpDocChildNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/PhpDocChildNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\PhpDocNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/PhpDocNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\PhpDocTagNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/PhpDocTagNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\PhpDocTagValueNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/PhpDocTagValueNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\PhpDocTextNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/PhpDocTextNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\PropertyTagValueNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/PropertyTagValueNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\RequireExtendsTagValueNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/RequireExtendsTagValueNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\RequireImplementsTagValueNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/RequireImplementsTagValueNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\ReturnTagValueNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/ReturnTagValueNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\SelfOutTagValueNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/SelfOutTagValueNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\TemplateTagValueNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/TemplateTagValueNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\ThrowsTagValueNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/ThrowsTagValueNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\TypeAliasImportTagValueNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/TypeAliasImportTagValueNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\TypeAliasTagValueNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/TypeAliasTagValueNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\TypelessParamTagValueNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/TypelessParamTagValueNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\UsesTagValueNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/UsesTagValueNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\VarTagValueNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/VarTagValueNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\Type\\ArrayShapeItemNode' => '/phpstan-phpdoc-parser/Ast/Type/ArrayShapeItemNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\Type\\ArrayShapeNode' => '/phpstan-phpdoc-parser/Ast/Type/ArrayShapeNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\Type\\ArrayTypeNode' => '/phpstan-phpdoc-parser/Ast/Type/ArrayTypeNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\Type\\CallableTypeNode' => '/phpstan-phpdoc-parser/Ast/Type/CallableTypeNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\Type\\CallableTypeParameterNode' => '/phpstan-phpdoc-parser/Ast/Type/CallableTypeParameterNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\Type\\ConditionalTypeForParameterNode' => '/phpstan-phpdoc-parser/Ast/Type/ConditionalTypeForParameterNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\Type\\ConditionalTypeNode' => '/phpstan-phpdoc-parser/Ast/Type/ConditionalTypeNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\Type\\ConstTypeNode' => '/phpstan-phpdoc-parser/Ast/Type/ConstTypeNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\Type\\GenericTypeNode' => '/phpstan-phpdoc-parser/Ast/Type/GenericTypeNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\Type\\IdentifierTypeNode' => '/phpstan-phpdoc-parser/Ast/Type/IdentifierTypeNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\Type\\IntersectionTypeNode' => '/phpstan-phpdoc-parser/Ast/Type/IntersectionTypeNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\Type\\InvalidTypeNode' => '/phpstan-phpdoc-parser/Ast/Type/InvalidTypeNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\Type\\NullableTypeNode' => '/phpstan-phpdoc-parser/Ast/Type/NullableTypeNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\Type\\ObjectShapeItemNode' => '/phpstan-phpdoc-parser/Ast/Type/ObjectShapeItemNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\Type\\ObjectShapeNode' => '/phpstan-phpdoc-parser/Ast/Type/ObjectShapeNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\Type\\OffsetAccessTypeNode' => '/phpstan-phpdoc-parser/Ast/Type/OffsetAccessTypeNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\Type\\ThisTypeNode' => '/phpstan-phpdoc-parser/Ast/Type/ThisTypeNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\Type\\TypeNode' => '/phpstan-phpdoc-parser/Ast/Type/TypeNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\Type\\UnionTypeNode' => '/phpstan-phpdoc-parser/Ast/Type/UnionTypeNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Lexer\\Lexer' => '/phpstan-phpdoc-parser/Lexer/Lexer.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Parser\\ConstExprParser' => '/phpstan-phpdoc-parser/Parser/ConstExprParser.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Parser\\ParserException' => '/phpstan-phpdoc-parser/Parser/ParserException.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Parser\\PhpDocParser' => '/phpstan-phpdoc-parser/Parser/PhpDocParser.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Parser\\StringUnescaper' => '/phpstan-phpdoc-parser/Parser/StringUnescaper.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Parser\\TokenIterator' => '/phpstan-phpdoc-parser/Parser/TokenIterator.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Parser\\TypeParser' => '/phpstan-phpdoc-parser/Parser/TypeParser.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Printer\\DiffElem' => '/phpstan-phpdoc-parser/Printer/DiffElem.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Printer\\Differ' => '/phpstan-phpdoc-parser/Printer/Differ.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Printer\\Printer' => '/phpstan-phpdoc-parser/Printer/Printer.php', 'PHPUnitPHAR\\PharIo\\Manifest\\Application' => '/phar-io-manifest/values/Application.php', 'PHPUnitPHAR\\PharIo\\Manifest\\ApplicationName' => '/phar-io-manifest/values/ApplicationName.php', 'PHPUnitPHAR\\PharIo\\Manifest\\Author' => '/phar-io-manifest/values/Author.php', @@ -307,24 +210,22 @@ spl_autoload_register( 'PHPUnitPHAR\\PhpParser\\Internal\\DiffElem' => '/nikic-php-parser/PhpParser/Internal/DiffElem.php', 'PHPUnitPHAR\\PhpParser\\Internal\\Differ' => '/nikic-php-parser/PhpParser/Internal/Differ.php', 'PHPUnitPHAR\\PhpParser\\Internal\\PrintableNewAnonClassNode' => '/nikic-php-parser/PhpParser/Internal/PrintableNewAnonClassNode.php', + 'PHPUnitPHAR\\PhpParser\\Internal\\TokenPolyfill' => '/nikic-php-parser/PhpParser/Internal/TokenPolyfill.php', 'PHPUnitPHAR\\PhpParser\\Internal\\TokenStream' => '/nikic-php-parser/PhpParser/Internal/TokenStream.php', 'PHPUnitPHAR\\PhpParser\\JsonDecoder' => '/nikic-php-parser/PhpParser/JsonDecoder.php', 'PHPUnitPHAR\\PhpParser\\Lexer' => '/nikic-php-parser/PhpParser/Lexer.php', 'PHPUnitPHAR\\PhpParser\\Lexer\\Emulative' => '/nikic-php-parser/PhpParser/Lexer/Emulative.php', 'PHPUnitPHAR\\PhpParser\\Lexer\\TokenEmulator\\AttributeEmulator' => '/nikic-php-parser/PhpParser/Lexer/TokenEmulator/AttributeEmulator.php', - 'PHPUnitPHAR\\PhpParser\\Lexer\\TokenEmulator\\CoaleseEqualTokenEmulator' => '/nikic-php-parser/PhpParser/Lexer/TokenEmulator/CoaleseEqualTokenEmulator.php', 'PHPUnitPHAR\\PhpParser\\Lexer\\TokenEmulator\\EnumTokenEmulator' => '/nikic-php-parser/PhpParser/Lexer/TokenEmulator/EnumTokenEmulator.php', 'PHPUnitPHAR\\PhpParser\\Lexer\\TokenEmulator\\ExplicitOctalEmulator' => '/nikic-php-parser/PhpParser/Lexer/TokenEmulator/ExplicitOctalEmulator.php', - 'PHPUnitPHAR\\PhpParser\\Lexer\\TokenEmulator\\FlexibleDocStringEmulator' => '/nikic-php-parser/PhpParser/Lexer/TokenEmulator/FlexibleDocStringEmulator.php', - 'PHPUnitPHAR\\PhpParser\\Lexer\\TokenEmulator\\FnTokenEmulator' => '/nikic-php-parser/PhpParser/Lexer/TokenEmulator/FnTokenEmulator.php', 'PHPUnitPHAR\\PhpParser\\Lexer\\TokenEmulator\\KeywordEmulator' => '/nikic-php-parser/PhpParser/Lexer/TokenEmulator/KeywordEmulator.php', 'PHPUnitPHAR\\PhpParser\\Lexer\\TokenEmulator\\MatchTokenEmulator' => '/nikic-php-parser/PhpParser/Lexer/TokenEmulator/MatchTokenEmulator.php', 'PHPUnitPHAR\\PhpParser\\Lexer\\TokenEmulator\\NullsafeTokenEmulator' => '/nikic-php-parser/PhpParser/Lexer/TokenEmulator/NullsafeTokenEmulator.php', - 'PHPUnitPHAR\\PhpParser\\Lexer\\TokenEmulator\\NumericLiteralSeparatorEmulator' => '/nikic-php-parser/PhpParser/Lexer/TokenEmulator/NumericLiteralSeparatorEmulator.php', 'PHPUnitPHAR\\PhpParser\\Lexer\\TokenEmulator\\ReadonlyFunctionTokenEmulator' => '/nikic-php-parser/PhpParser/Lexer/TokenEmulator/ReadonlyFunctionTokenEmulator.php', 'PHPUnitPHAR\\PhpParser\\Lexer\\TokenEmulator\\ReadonlyTokenEmulator' => '/nikic-php-parser/PhpParser/Lexer/TokenEmulator/ReadonlyTokenEmulator.php', 'PHPUnitPHAR\\PhpParser\\Lexer\\TokenEmulator\\ReverseEmulator' => '/nikic-php-parser/PhpParser/Lexer/TokenEmulator/ReverseEmulator.php', 'PHPUnitPHAR\\PhpParser\\Lexer\\TokenEmulator\\TokenEmulator' => '/nikic-php-parser/PhpParser/Lexer/TokenEmulator/TokenEmulator.php', + 'PHPUnitPHAR\\PhpParser\\Modifiers' => '/nikic-php-parser/PhpParser/Modifiers.php', 'PHPUnitPHAR\\PhpParser\\NameContext' => '/nikic-php-parser/PhpParser/NameContext.php', 'PHPUnitPHAR\\PhpParser\\Node' => '/nikic-php-parser/PhpParser/Node.php', 'PHPUnitPHAR\\PhpParser\\NodeAbstract' => '/nikic-php-parser/PhpParser/NodeAbstract.php', @@ -335,19 +236,22 @@ spl_autoload_register( 'PHPUnitPHAR\\PhpParser\\NodeVisitor' => '/nikic-php-parser/PhpParser/NodeVisitor.php', 'PHPUnitPHAR\\PhpParser\\NodeVisitorAbstract' => '/nikic-php-parser/PhpParser/NodeVisitorAbstract.php', 'PHPUnitPHAR\\PhpParser\\NodeVisitor\\CloningVisitor' => '/nikic-php-parser/PhpParser/NodeVisitor/CloningVisitor.php', + 'PHPUnitPHAR\\PhpParser\\NodeVisitor\\CommentAnnotatingVisitor' => '/nikic-php-parser/PhpParser/NodeVisitor/CommentAnnotatingVisitor.php', 'PHPUnitPHAR\\PhpParser\\NodeVisitor\\FindingVisitor' => '/nikic-php-parser/PhpParser/NodeVisitor/FindingVisitor.php', 'PHPUnitPHAR\\PhpParser\\NodeVisitor\\FirstFindingVisitor' => '/nikic-php-parser/PhpParser/NodeVisitor/FirstFindingVisitor.php', 'PHPUnitPHAR\\PhpParser\\NodeVisitor\\NameResolver' => '/nikic-php-parser/PhpParser/NodeVisitor/NameResolver.php', 'PHPUnitPHAR\\PhpParser\\NodeVisitor\\NodeConnectingVisitor' => '/nikic-php-parser/PhpParser/NodeVisitor/NodeConnectingVisitor.php', 'PHPUnitPHAR\\PhpParser\\NodeVisitor\\ParentConnectingVisitor' => '/nikic-php-parser/PhpParser/NodeVisitor/ParentConnectingVisitor.php', 'PHPUnitPHAR\\PhpParser\\Node\\Arg' => '/nikic-php-parser/PhpParser/Node/Arg.php', + 'PHPUnitPHAR\\PhpParser\\Node\\ArrayItem' => '/nikic-php-parser/PhpParser/Node/ArrayItem.php', 'PHPUnitPHAR\\PhpParser\\Node\\Attribute' => '/nikic-php-parser/PhpParser/Node/Attribute.php', 'PHPUnitPHAR\\PhpParser\\Node\\AttributeGroup' => '/nikic-php-parser/PhpParser/Node/AttributeGroup.php', + 'PHPUnitPHAR\\PhpParser\\Node\\ClosureUse' => '/nikic-php-parser/PhpParser/Node/ClosureUse.php', 'PHPUnitPHAR\\PhpParser\\Node\\ComplexType' => '/nikic-php-parser/PhpParser/Node/ComplexType.php', 'PHPUnitPHAR\\PhpParser\\Node\\Const_' => '/nikic-php-parser/PhpParser/Node/Const_.php', + 'PHPUnitPHAR\\PhpParser\\Node\\DeclareItem' => '/nikic-php-parser/PhpParser/Node/DeclareItem.php', 'PHPUnitPHAR\\PhpParser\\Node\\Expr' => '/nikic-php-parser/PhpParser/Node/Expr.php', 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\ArrayDimFetch' => '/nikic-php-parser/PhpParser/Node/Expr/ArrayDimFetch.php', - 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\ArrayItem' => '/nikic-php-parser/PhpParser/Node/Expr/ArrayItem.php', 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\Array_' => '/nikic-php-parser/PhpParser/Node/Expr/Array_.php', 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\ArrowFunction' => '/nikic-php-parser/PhpParser/Node/Expr/ArrowFunction.php', 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\Assign' => '/nikic-php-parser/PhpParser/Node/Expr/Assign.php', @@ -408,7 +312,6 @@ spl_autoload_register( 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\ClassConstFetch' => '/nikic-php-parser/PhpParser/Node/Expr/ClassConstFetch.php', 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\Clone_' => '/nikic-php-parser/PhpParser/Node/Expr/Clone_.php', 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\Closure' => '/nikic-php-parser/PhpParser/Node/Expr/Closure.php', - 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\ClosureUse' => '/nikic-php-parser/PhpParser/Node/Expr/ClosureUse.php', 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\ConstFetch' => '/nikic-php-parser/PhpParser/Node/Expr/ConstFetch.php', 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\Empty_' => '/nikic-php-parser/PhpParser/Node/Expr/Empty_.php', 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\Error' => '/nikic-php-parser/PhpParser/Node/Expr/Error.php', @@ -443,6 +346,7 @@ spl_autoload_register( 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\Yield_' => '/nikic-php-parser/PhpParser/Node/Expr/Yield_.php', 'PHPUnitPHAR\\PhpParser\\Node\\FunctionLike' => '/nikic-php-parser/PhpParser/Node/FunctionLike.php', 'PHPUnitPHAR\\PhpParser\\Node\\Identifier' => '/nikic-php-parser/PhpParser/Node/Identifier.php', + 'PHPUnitPHAR\\PhpParser\\Node\\InterpolatedStringPart' => '/nikic-php-parser/PhpParser/Node/InterpolatedStringPart.php', 'PHPUnitPHAR\\PhpParser\\Node\\IntersectionType' => '/nikic-php-parser/PhpParser/Node/IntersectionType.php', 'PHPUnitPHAR\\PhpParser\\Node\\MatchArm' => '/nikic-php-parser/PhpParser/Node/MatchArm.php', 'PHPUnitPHAR\\PhpParser\\Node\\Name' => '/nikic-php-parser/PhpParser/Node/Name.php', @@ -450,11 +354,11 @@ spl_autoload_register( 'PHPUnitPHAR\\PhpParser\\Node\\Name\\Relative' => '/nikic-php-parser/PhpParser/Node/Name/Relative.php', 'PHPUnitPHAR\\PhpParser\\Node\\NullableType' => '/nikic-php-parser/PhpParser/Node/NullableType.php', 'PHPUnitPHAR\\PhpParser\\Node\\Param' => '/nikic-php-parser/PhpParser/Node/Param.php', + 'PHPUnitPHAR\\PhpParser\\Node\\PropertyItem' => '/nikic-php-parser/PhpParser/Node/PropertyItem.php', 'PHPUnitPHAR\\PhpParser\\Node\\Scalar' => '/nikic-php-parser/PhpParser/Node/Scalar.php', - 'PHPUnitPHAR\\PhpParser\\Node\\Scalar\\DNumber' => '/nikic-php-parser/PhpParser/Node/Scalar/DNumber.php', - 'PHPUnitPHAR\\PhpParser\\Node\\Scalar\\Encapsed' => '/nikic-php-parser/PhpParser/Node/Scalar/Encapsed.php', - 'PHPUnitPHAR\\PhpParser\\Node\\Scalar\\EncapsedStringPart' => '/nikic-php-parser/PhpParser/Node/Scalar/EncapsedStringPart.php', - 'PHPUnitPHAR\\PhpParser\\Node\\Scalar\\LNumber' => '/nikic-php-parser/PhpParser/Node/Scalar/LNumber.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Scalar\\Float_' => '/nikic-php-parser/PhpParser/Node/Scalar/Float_.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Scalar\\Int_' => '/nikic-php-parser/PhpParser/Node/Scalar/Int_.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Scalar\\InterpolatedString' => '/nikic-php-parser/PhpParser/Node/Scalar/InterpolatedString.php', 'PHPUnitPHAR\\PhpParser\\Node\\Scalar\\MagicConst' => '/nikic-php-parser/PhpParser/Node/Scalar/MagicConst.php', 'PHPUnitPHAR\\PhpParser\\Node\\Scalar\\MagicConst\\Class_' => '/nikic-php-parser/PhpParser/Node/Scalar/MagicConst/Class_.php', 'PHPUnitPHAR\\PhpParser\\Node\\Scalar\\MagicConst\\Dir' => '/nikic-php-parser/PhpParser/Node/Scalar/MagicConst/Dir.php', @@ -465,7 +369,9 @@ spl_autoload_register( 'PHPUnitPHAR\\PhpParser\\Node\\Scalar\\MagicConst\\Namespace_' => '/nikic-php-parser/PhpParser/Node/Scalar/MagicConst/Namespace_.php', 'PHPUnitPHAR\\PhpParser\\Node\\Scalar\\MagicConst\\Trait_' => '/nikic-php-parser/PhpParser/Node/Scalar/MagicConst/Trait_.php', 'PHPUnitPHAR\\PhpParser\\Node\\Scalar\\String_' => '/nikic-php-parser/PhpParser/Node/Scalar/String_.php', + 'PHPUnitPHAR\\PhpParser\\Node\\StaticVar' => '/nikic-php-parser/PhpParser/Node/StaticVar.php', 'PHPUnitPHAR\\PhpParser\\Node\\Stmt' => '/nikic-php-parser/PhpParser/Node/Stmt.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\Block' => '/nikic-php-parser/PhpParser/Node/Stmt/Block.php', 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\Break_' => '/nikic-php-parser/PhpParser/Node/Stmt/Break_.php', 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\Case_' => '/nikic-php-parser/PhpParser/Node/Stmt/Case_.php', 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\Catch_' => '/nikic-php-parser/PhpParser/Node/Stmt/Catch_.php', @@ -475,7 +381,6 @@ spl_autoload_register( 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\Class_' => '/nikic-php-parser/PhpParser/Node/Stmt/Class_.php', 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\Const_' => '/nikic-php-parser/PhpParser/Node/Stmt/Const_.php', 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\Continue_' => '/nikic-php-parser/PhpParser/Node/Stmt/Continue_.php', - 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\DeclareDeclare' => '/nikic-php-parser/PhpParser/Node/Stmt/DeclareDeclare.php', 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\Declare_' => '/nikic-php-parser/PhpParser/Node/Stmt/Declare_.php', 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\Do_' => '/nikic-php-parser/PhpParser/Node/Stmt/Do_.php', 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\Echo_' => '/nikic-php-parser/PhpParser/Node/Stmt/Echo_.php', @@ -499,12 +404,9 @@ spl_autoload_register( 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\Namespace_' => '/nikic-php-parser/PhpParser/Node/Stmt/Namespace_.php', 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\Nop' => '/nikic-php-parser/PhpParser/Node/Stmt/Nop.php', 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\Property' => '/nikic-php-parser/PhpParser/Node/Stmt/Property.php', - 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\PropertyProperty' => '/nikic-php-parser/PhpParser/Node/Stmt/PropertyProperty.php', 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\Return_' => '/nikic-php-parser/PhpParser/Node/Stmt/Return_.php', - 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\StaticVar' => '/nikic-php-parser/PhpParser/Node/Stmt/StaticVar.php', 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\Static_' => '/nikic-php-parser/PhpParser/Node/Stmt/Static_.php', 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\Switch_' => '/nikic-php-parser/PhpParser/Node/Stmt/Switch_.php', - 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\Throw_' => '/nikic-php-parser/PhpParser/Node/Stmt/Throw_.php', 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\TraitUse' => '/nikic-php-parser/PhpParser/Node/Stmt/TraitUse.php', 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\TraitUseAdaptation' => '/nikic-php-parser/PhpParser/Node/Stmt/TraitUseAdaptation.php', 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\TraitUseAdaptation\\Alias' => '/nikic-php-parser/PhpParser/Node/Stmt/TraitUseAdaptation/Alias.php', @@ -512,21 +414,22 @@ spl_autoload_register( 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\Trait_' => '/nikic-php-parser/PhpParser/Node/Stmt/Trait_.php', 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\TryCatch' => '/nikic-php-parser/PhpParser/Node/Stmt/TryCatch.php', 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\Unset_' => '/nikic-php-parser/PhpParser/Node/Stmt/Unset_.php', - 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\UseUse' => '/nikic-php-parser/PhpParser/Node/Stmt/UseUse.php', 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\Use_' => '/nikic-php-parser/PhpParser/Node/Stmt/Use_.php', 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\While_' => '/nikic-php-parser/PhpParser/Node/Stmt/While_.php', 'PHPUnitPHAR\\PhpParser\\Node\\UnionType' => '/nikic-php-parser/PhpParser/Node/UnionType.php', + 'PHPUnitPHAR\\PhpParser\\Node\\UseItem' => '/nikic-php-parser/PhpParser/Node/UseItem.php', 'PHPUnitPHAR\\PhpParser\\Node\\VarLikeIdentifier' => '/nikic-php-parser/PhpParser/Node/VarLikeIdentifier.php', 'PHPUnitPHAR\\PhpParser\\Node\\VariadicPlaceholder' => '/nikic-php-parser/PhpParser/Node/VariadicPlaceholder.php', 'PHPUnitPHAR\\PhpParser\\Parser' => '/nikic-php-parser/PhpParser/Parser.php', 'PHPUnitPHAR\\PhpParser\\ParserAbstract' => '/nikic-php-parser/PhpParser/ParserAbstract.php', 'PHPUnitPHAR\\PhpParser\\ParserFactory' => '/nikic-php-parser/PhpParser/ParserFactory.php', - 'PHPUnitPHAR\\PhpParser\\Parser\\Multiple' => '/nikic-php-parser/PhpParser/Parser/Multiple.php', - 'PHPUnitPHAR\\PhpParser\\Parser\\Php5' => '/nikic-php-parser/PhpParser/Parser/Php5.php', 'PHPUnitPHAR\\PhpParser\\Parser\\Php7' => '/nikic-php-parser/PhpParser/Parser/Php7.php', - 'PHPUnitPHAR\\PhpParser\\Parser\\Tokens' => '/nikic-php-parser/PhpParser/Parser/Tokens.php', + 'PHPUnitPHAR\\PhpParser\\Parser\\Php8' => '/nikic-php-parser/PhpParser/Parser/Php8.php', + 'PHPUnitPHAR\\PhpParser\\PhpVersion' => '/nikic-php-parser/PhpParser/PhpVersion.php', + 'PHPUnitPHAR\\PhpParser\\PrettyPrinter' => '/nikic-php-parser/PhpParser/PrettyPrinter.php', 'PHPUnitPHAR\\PhpParser\\PrettyPrinterAbstract' => '/nikic-php-parser/PhpParser/PrettyPrinterAbstract.php', 'PHPUnitPHAR\\PhpParser\\PrettyPrinter\\Standard' => '/nikic-php-parser/PhpParser/PrettyPrinter/Standard.php', + 'PHPUnitPHAR\\PhpParser\\Token' => '/nikic-php-parser/PhpParser/Token.php', 'PHPUnitPHAR\\SebastianBergmann\\CliParser\\AmbiguousOptionException' => '/sebastian-cli-parser/exceptions/AmbiguousOptionException.php', 'PHPUnitPHAR\\SebastianBergmann\\CliParser\\Exception' => '/sebastian-cli-parser/exceptions/Exception.php', 'PHPUnitPHAR\\SebastianBergmann\\CliParser\\OptionDoesNotAllowArgumentException' => '/sebastian-cli-parser/exceptions/OptionDoesNotAllowArgumentException.php', @@ -535,22 +438,20 @@ spl_autoload_register( 'PHPUnitPHAR\\SebastianBergmann\\CliParser\\UnknownOptionException' => '/sebastian-cli-parser/exceptions/UnknownOptionException.php', 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\BranchAndPathCoverageNotSupportedException' => '/php-code-coverage/Exception/BranchAndPathCoverageNotSupportedException.php', 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\CodeCoverage' => '/php-code-coverage/CodeCoverage.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Data\\ProcessedCodeCoverageData' => '/php-code-coverage/Data/ProcessedCodeCoverageData.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Data\\RawCodeCoverageData' => '/php-code-coverage/Data/RawCodeCoverageData.php', 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\DeadCodeDetectionNotSupportedException' => '/php-code-coverage/Exception/DeadCodeDetectionNotSupportedException.php', 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Driver\\Driver' => '/php-code-coverage/Driver/Driver.php', 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Driver\\PathExistsButIsNotDirectoryException' => '/php-code-coverage/Exception/PathExistsButIsNotDirectoryException.php', 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Driver\\PcovDriver' => '/php-code-coverage/Driver/PcovDriver.php', 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Driver\\PcovNotAvailableException' => '/php-code-coverage/Exception/PcovNotAvailableException.php', - 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Driver\\PhpdbgDriver' => '/php-code-coverage/Driver/PhpdbgDriver.php', - 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Driver\\PhpdbgNotAvailableException' => '/php-code-coverage/Exception/PhpdbgNotAvailableException.php', 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Driver\\Selector' => '/php-code-coverage/Driver/Selector.php', 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Driver\\WriteOperationFailedException' => '/php-code-coverage/Exception/WriteOperationFailedException.php', - 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Driver\\WrongXdebugVersionException' => '/php-code-coverage/Exception/WrongXdebugVersionException.php', - 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Driver\\Xdebug2Driver' => '/php-code-coverage/Driver/Xdebug2Driver.php', - 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Driver\\Xdebug2NotEnabledException' => '/php-code-coverage/Exception/Xdebug2NotEnabledException.php', - 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Driver\\Xdebug3Driver' => '/php-code-coverage/Driver/Xdebug3Driver.php', - 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Driver\\Xdebug3NotEnabledException' => '/php-code-coverage/Exception/Xdebug3NotEnabledException.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Driver\\XdebugDriver' => '/php-code-coverage/Driver/XdebugDriver.php', 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Driver\\XdebugNotAvailableException' => '/php-code-coverage/Exception/XdebugNotAvailableException.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Driver\\XdebugNotEnabledException' => '/php-code-coverage/Exception/XdebugNotEnabledException.php', 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Exception' => '/php-code-coverage/Exception/Exception.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\FileCouldNotBeWrittenException' => '/php-code-coverage/Exception/FileCouldNotBeWrittenException.php', 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Filter' => '/php-code-coverage/Filter.php', 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\InvalidArgumentException' => '/php-code-coverage/Exception/InvalidArgumentException.php', 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\NoCodeCoverageDriverAvailableException' => '/php-code-coverage/Exception/NoCodeCoverageDriverAvailableException.php', @@ -562,13 +463,13 @@ spl_autoload_register( 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Node\\File' => '/php-code-coverage/Node/File.php', 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Node\\Iterator' => '/php-code-coverage/Node/Iterator.php', 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\ParserException' => '/php-code-coverage/Exception/ParserException.php', - 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\ProcessedCodeCoverageData' => '/php-code-coverage/ProcessedCodeCoverageData.php', - 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\RawCodeCoverageData' => '/php-code-coverage/RawCodeCoverageData.php', 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\ReflectionException' => '/php-code-coverage/Exception/ReflectionException.php', 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\ReportAlreadyFinalizedException' => '/php-code-coverage/Exception/ReportAlreadyFinalizedException.php', 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Report\\Clover' => '/php-code-coverage/Report/Clover.php', 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Report\\Cobertura' => '/php-code-coverage/Report/Cobertura.php', 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Report\\Crap4j' => '/php-code-coverage/Report/Crap4j.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Report\\Html\\Colors' => '/php-code-coverage/Report/Html/Colors.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Report\\Html\\CustomCssFile' => '/php-code-coverage/Report/Html/CustomCssFile.php', 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Report\\Html\\Dashboard' => '/php-code-coverage/Report/Html/Renderer/Dashboard.php', 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Report\\Html\\Directory' => '/php-code-coverage/Report/Html/Renderer/Directory.php', 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Report\\Html\\Facade' => '/php-code-coverage/Report/Html/Facade.php', @@ -576,6 +477,7 @@ spl_autoload_register( 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Report\\Html\\Renderer' => '/php-code-coverage/Report/Html/Renderer.php', 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Report\\PHP' => '/php-code-coverage/Report/PHP.php', 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Report\\Text' => '/php-code-coverage/Report/Text.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Report\\Thresholds' => '/php-code-coverage/Report/Thresholds.php', 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Report\\Xml\\BuildInformation' => '/php-code-coverage/Report/Xml/BuildInformation.php', 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Report\\Xml\\Coverage' => '/php-code-coverage/Report/Xml/Coverage.php', 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Report\\Xml\\Directory' => '/php-code-coverage/Report/Xml/Directory.php', @@ -598,6 +500,17 @@ spl_autoload_register( 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\StaticAnalysis\\IgnoredLinesFindingVisitor' => '/php-code-coverage/StaticAnalysis/IgnoredLinesFindingVisitor.php', 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\StaticAnalysis\\ParsingFileAnalyser' => '/php-code-coverage/StaticAnalysis/ParsingFileAnalyser.php', 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\TestIdMissingException' => '/php-code-coverage/Exception/TestIdMissingException.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Test\\TestSize\\Known' => '/php-code-coverage/TestSize/Known.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Test\\TestSize\\Large' => '/php-code-coverage/TestSize/Large.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Test\\TestSize\\Medium' => '/php-code-coverage/TestSize/Medium.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Test\\TestSize\\Small' => '/php-code-coverage/TestSize/Small.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Test\\TestSize\\TestSize' => '/php-code-coverage/TestSize/TestSize.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Test\\TestSize\\Unknown' => '/php-code-coverage/TestSize/Unknown.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Test\\TestStatus\\Failure' => '/php-code-coverage/TestStatus/Failure.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Test\\TestStatus\\Known' => '/php-code-coverage/TestStatus/Known.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Test\\TestStatus\\Success' => '/php-code-coverage/TestStatus/Success.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Test\\TestStatus\\TestStatus' => '/php-code-coverage/TestStatus/TestStatus.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Test\\TestStatus\\Unknown' => '/php-code-coverage/TestStatus/Unknown.php', 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\UnintentionallyCoveredCodeException' => '/php-code-coverage/Exception/UnintentionallyCoveredCodeException.php', 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Util\\DirectoryCouldNotBeCreatedException' => '/php-code-coverage/Exception/DirectoryCouldNotBeCreatedException.php', 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Util\\Filesystem' => '/php-code-coverage/Util/Filesystem.php', @@ -611,6 +524,7 @@ spl_autoload_register( 'PHPUnitPHAR\\SebastianBergmann\\CodeUnit\\CodeUnitCollection' => '/sebastian-code-unit/CodeUnitCollection.php', 'PHPUnitPHAR\\SebastianBergmann\\CodeUnit\\CodeUnitCollectionIterator' => '/sebastian-code-unit/CodeUnitCollectionIterator.php', 'PHPUnitPHAR\\SebastianBergmann\\CodeUnit\\Exception' => '/sebastian-code-unit/exceptions/Exception.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeUnit\\FileUnit' => '/sebastian-code-unit/FileUnit.php', 'PHPUnitPHAR\\SebastianBergmann\\CodeUnit\\FunctionUnit' => '/sebastian-code-unit/FunctionUnit.php', 'PHPUnitPHAR\\SebastianBergmann\\CodeUnit\\InterfaceMethodUnit' => '/sebastian-code-unit/InterfaceMethodUnit.php', 'PHPUnitPHAR\\SebastianBergmann\\CodeUnit\\InterfaceUnit' => '/sebastian-code-unit/InterfaceUnit.php', @@ -625,7 +539,6 @@ spl_autoload_register( 'PHPUnitPHAR\\SebastianBergmann\\Comparator\\ComparisonFailure' => '/sebastian-comparator/ComparisonFailure.php', 'PHPUnitPHAR\\SebastianBergmann\\Comparator\\DOMNodeComparator' => '/sebastian-comparator/DOMNodeComparator.php', 'PHPUnitPHAR\\SebastianBergmann\\Comparator\\DateTimeComparator' => '/sebastian-comparator/DateTimeComparator.php', - 'PHPUnitPHAR\\SebastianBergmann\\Comparator\\DoubleComparator' => '/sebastian-comparator/DoubleComparator.php', 'PHPUnitPHAR\\SebastianBergmann\\Comparator\\Exception' => '/sebastian-comparator/exceptions/Exception.php', 'PHPUnitPHAR\\SebastianBergmann\\Comparator\\ExceptionComparator' => '/sebastian-comparator/ExceptionComparator.php', 'PHPUnitPHAR\\SebastianBergmann\\Comparator\\Factory' => '/sebastian-comparator/Factory.php', @@ -662,9 +575,9 @@ spl_autoload_register( 'PHPUnitPHAR\\SebastianBergmann\\Diff\\Parser' => '/sebastian-diff/Parser.php', 'PHPUnitPHAR\\SebastianBergmann\\Diff\\TimeEfficientLongestCommonSubsequenceCalculator' => '/sebastian-diff/TimeEfficientLongestCommonSubsequenceCalculator.php', 'PHPUnitPHAR\\SebastianBergmann\\Environment\\Console' => '/sebastian-environment/Console.php', - 'PHPUnitPHAR\\SebastianBergmann\\Environment\\OperatingSystem' => '/sebastian-environment/OperatingSystem.php', 'PHPUnitPHAR\\SebastianBergmann\\Environment\\Runtime' => '/sebastian-environment/Runtime.php', 'PHPUnitPHAR\\SebastianBergmann\\Exporter\\Exporter' => '/sebastian-exporter/Exporter.php', + 'PHPUnitPHAR\\SebastianBergmann\\FileIterator\\ExcludeIterator' => '/php-file-iterator/ExcludeIterator.php', 'PHPUnitPHAR\\SebastianBergmann\\FileIterator\\Facade' => '/php-file-iterator/Facade.php', 'PHPUnitPHAR\\SebastianBergmann\\FileIterator\\Factory' => '/php-file-iterator/Factory.php', 'PHPUnitPHAR\\SebastianBergmann\\FileIterator\\Iterator' => '/php-file-iterator/Iterator.php', @@ -686,15 +599,8 @@ spl_autoload_register( 'PHPUnitPHAR\\SebastianBergmann\\LinesOfCode\\NegativeValueException' => '/sebastian-lines-of-code/Exception/NegativeValueException.php', 'PHPUnitPHAR\\SebastianBergmann\\LinesOfCode\\RuntimeException' => '/sebastian-lines-of-code/Exception/RuntimeException.php', 'PHPUnitPHAR\\SebastianBergmann\\ObjectEnumerator\\Enumerator' => '/sebastian-object-enumerator/Enumerator.php', - 'PHPUnitPHAR\\SebastianBergmann\\ObjectEnumerator\\Exception' => '/sebastian-object-enumerator/Exception.php', - 'PHPUnitPHAR\\SebastianBergmann\\ObjectEnumerator\\InvalidArgumentException' => '/sebastian-object-enumerator/InvalidArgumentException.php', - 'PHPUnitPHAR\\SebastianBergmann\\ObjectReflector\\Exception' => '/sebastian-object-reflector/Exception.php', - 'PHPUnitPHAR\\SebastianBergmann\\ObjectReflector\\InvalidArgumentException' => '/sebastian-object-reflector/InvalidArgumentException.php', 'PHPUnitPHAR\\SebastianBergmann\\ObjectReflector\\ObjectReflector' => '/sebastian-object-reflector/ObjectReflector.php', 'PHPUnitPHAR\\SebastianBergmann\\RecursionContext\\Context' => '/sebastian-recursion-context/Context.php', - 'PHPUnitPHAR\\SebastianBergmann\\RecursionContext\\Exception' => '/sebastian-recursion-context/Exception.php', - 'PHPUnitPHAR\\SebastianBergmann\\RecursionContext\\InvalidArgumentException' => '/sebastian-recursion-context/InvalidArgumentException.php', - 'PHPUnitPHAR\\SebastianBergmann\\ResourceOperations\\ResourceOperations' => '/sebastian-resource-operations/ResourceOperations.php', 'PHPUnitPHAR\\SebastianBergmann\\Template\\Exception' => '/php-text-template/exceptions/Exception.php', 'PHPUnitPHAR\\SebastianBergmann\\Template\\InvalidArgumentException' => '/php-text-template/exceptions/InvalidArgumentException.php', 'PHPUnitPHAR\\SebastianBergmann\\Template\\RuntimeException' => '/php-text-template/exceptions/RuntimeException.php', @@ -735,135 +641,296 @@ spl_autoload_register( 'PHPUnitPHAR\\TheSeer\\Tokenizer\\TokenCollectionException' => '/theseer-tokenizer/TokenCollectionException.php', 'PHPUnitPHAR\\TheSeer\\Tokenizer\\Tokenizer' => '/theseer-tokenizer/Tokenizer.php', 'PHPUnitPHAR\\TheSeer\\Tokenizer\\XMLSerializer' => '/theseer-tokenizer/XMLSerializer.php', - 'PHPUnitPHAR\\Webmozart\\Assert\\Assert' => '/webmozart-assert/Assert.php', - 'PHPUnitPHAR\\Webmozart\\Assert\\InvalidArgumentException' => '/webmozart-assert/InvalidArgumentException.php', - 'PHPUnitPHAR\\Webmozart\\Assert\\Mixin' => '/webmozart-assert/Mixin.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock' => '/phpdocumentor-reflection-docblock/DocBlock.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlockFactory' => '/phpdocumentor-reflection-docblock/DocBlockFactory.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlockFactoryInterface' => '/phpdocumentor-reflection-docblock/DocBlockFactoryInterface.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Description' => '/phpdocumentor-reflection-docblock/DocBlock/Description.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\DescriptionFactory' => '/phpdocumentor-reflection-docblock/DocBlock/DescriptionFactory.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\ExampleFinder' => '/phpdocumentor-reflection-docblock/DocBlock/ExampleFinder.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Serializer' => '/phpdocumentor-reflection-docblock/DocBlock/Serializer.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\StandardTagFactory' => '/phpdocumentor-reflection-docblock/DocBlock/StandardTagFactory.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Tag' => '/phpdocumentor-reflection-docblock/DocBlock/Tag.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\TagFactory' => '/phpdocumentor-reflection-docblock/DocBlock/TagFactory.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Author' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Author.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Tags\\BaseTag' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/BaseTag.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Covers' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Covers.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Deprecated' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Deprecated.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Example' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Example.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Factory\\StaticMethod' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Factory/StaticMethod.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Formatter' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Formatter.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Formatter\\AlignFormatter' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Formatter/AlignFormatter.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Formatter\\PassthroughFormatter' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Formatter/PassthroughFormatter.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Generic' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Generic.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Tags\\InvalidTag' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/InvalidTag.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Link' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Link.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Method' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Method.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Param' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Param.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Property' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Property.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Tags\\PropertyRead' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/PropertyRead.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Tags\\PropertyWrite' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/PropertyWrite.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Reference\\Fqsen' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Reference/Fqsen.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Reference\\Reference' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Reference/Reference.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Reference\\Url' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Reference/Url.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Return_' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Return_.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Tags\\See' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/See.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Since' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Since.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Source' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Source.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Tags\\TagWithType' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/TagWithType.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Throws' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Throws.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Uses' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Uses.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Var_' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Var_.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Version' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Version.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Element' => '/phpdocumentor-reflection-common/Element.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Exception\\PcreException' => '/phpdocumentor-reflection-docblock/Exception/PcreException.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\File' => '/phpdocumentor-reflection-common/File.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Fqsen' => '/phpdocumentor-reflection-common/Fqsen.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\FqsenResolver' => '/phpdocumentor-type-resolver/FqsenResolver.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Location' => '/phpdocumentor-reflection-common/Location.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Project' => '/phpdocumentor-reflection-common/Project.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\ProjectFactory' => '/phpdocumentor-reflection-common/ProjectFactory.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\PseudoType' => '/phpdocumentor-type-resolver/PseudoType.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\PseudoTypes\\ArrayShape' => '/phpdocumentor-type-resolver/PseudoTypes/ArrayShape.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\PseudoTypes\\ArrayShapeItem' => '/phpdocumentor-type-resolver/PseudoTypes/ArrayShapeItem.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\PseudoTypes\\CallableString' => '/phpdocumentor-type-resolver/PseudoTypes/CallableString.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\PseudoTypes\\ConstExpression' => '/phpdocumentor-type-resolver/PseudoTypes/ConstExpression.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\PseudoTypes\\False_' => '/phpdocumentor-type-resolver/PseudoTypes/False_.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\PseudoTypes\\FloatValue' => '/phpdocumentor-type-resolver/PseudoTypes/FloatValue.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\PseudoTypes\\HtmlEscapedString' => '/phpdocumentor-type-resolver/PseudoTypes/HtmlEscapedString.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\PseudoTypes\\IntegerRange' => '/phpdocumentor-type-resolver/PseudoTypes/IntegerRange.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\PseudoTypes\\IntegerValue' => '/phpdocumentor-type-resolver/PseudoTypes/IntegerValue.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\PseudoTypes\\List_' => '/phpdocumentor-type-resolver/PseudoTypes/List_.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\PseudoTypes\\LiteralString' => '/phpdocumentor-type-resolver/PseudoTypes/LiteralString.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\PseudoTypes\\LowercaseString' => '/phpdocumentor-type-resolver/PseudoTypes/LowercaseString.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\PseudoTypes\\NegativeInteger' => '/phpdocumentor-type-resolver/PseudoTypes/NegativeInteger.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\PseudoTypes\\NonEmptyList' => '/phpdocumentor-type-resolver/PseudoTypes/NonEmptyList.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\PseudoTypes\\NonEmptyLowercaseString' => '/phpdocumentor-type-resolver/PseudoTypes/NonEmptyLowercaseString.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\PseudoTypes\\NonEmptyString' => '/phpdocumentor-type-resolver/PseudoTypes/NonEmptyString.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\PseudoTypes\\NumericString' => '/phpdocumentor-type-resolver/PseudoTypes/NumericString.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\PseudoTypes\\Numeric_' => '/phpdocumentor-type-resolver/PseudoTypes/Numeric_.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\PseudoTypes\\PositiveInteger' => '/phpdocumentor-type-resolver/PseudoTypes/PositiveInteger.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\PseudoTypes\\StringValue' => '/phpdocumentor-type-resolver/PseudoTypes/StringValue.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\PseudoTypes\\TraitString' => '/phpdocumentor-type-resolver/PseudoTypes/TraitString.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\PseudoTypes\\True_' => '/phpdocumentor-type-resolver/PseudoTypes/True_.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Type' => '/phpdocumentor-type-resolver/Type.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\TypeResolver' => '/phpdocumentor-type-resolver/TypeResolver.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\AbstractList' => '/phpdocumentor-type-resolver/Types/AbstractList.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\AggregatedType' => '/phpdocumentor-type-resolver/Types/AggregatedType.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\ArrayKey' => '/phpdocumentor-type-resolver/Types/ArrayKey.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\Array_' => '/phpdocumentor-type-resolver/Types/Array_.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\Boolean' => '/phpdocumentor-type-resolver/Types/Boolean.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\CallableParameter' => '/phpdocumentor-type-resolver/Types/CallableParameter.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\Callable_' => '/phpdocumentor-type-resolver/Types/Callable_.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\ClassString' => '/phpdocumentor-type-resolver/Types/ClassString.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\Collection' => '/phpdocumentor-type-resolver/Types/Collection.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\Compound' => '/phpdocumentor-type-resolver/Types/Compound.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\Context' => '/phpdocumentor-type-resolver/Types/Context.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\ContextFactory' => '/phpdocumentor-type-resolver/Types/ContextFactory.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\Expression' => '/phpdocumentor-type-resolver/Types/Expression.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\Float_' => '/phpdocumentor-type-resolver/Types/Float_.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\Integer' => '/phpdocumentor-type-resolver/Types/Integer.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\InterfaceString' => '/phpdocumentor-type-resolver/Types/InterfaceString.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\Intersection' => '/phpdocumentor-type-resolver/Types/Intersection.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\Iterable_' => '/phpdocumentor-type-resolver/Types/Iterable_.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\Mixed_' => '/phpdocumentor-type-resolver/Types/Mixed_.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\Never_' => '/phpdocumentor-type-resolver/Types/Never_.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\Null_' => '/phpdocumentor-type-resolver/Types/Null_.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\Nullable' => '/phpdocumentor-type-resolver/Types/Nullable.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\Object_' => '/phpdocumentor-type-resolver/Types/Object_.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\Parent_' => '/phpdocumentor-type-resolver/Types/Parent_.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\Resource_' => '/phpdocumentor-type-resolver/Types/Resource_.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\Scalar' => '/phpdocumentor-type-resolver/Types/Scalar.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\Self_' => '/phpdocumentor-type-resolver/Types/Self_.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\Static_' => '/phpdocumentor-type-resolver/Types/Static_.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\String_' => '/phpdocumentor-type-resolver/Types/String_.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\This' => '/phpdocumentor-type-resolver/Types/This.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\Void_' => '/phpdocumentor-type-resolver/Types/Void_.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Utils' => '/phpdocumentor-reflection-docblock/Utils.php', + 'PHPUnit\\Event\\Application\\Finished' => '/phpunit/Event/Events/Application/Finished.php', + 'PHPUnit\\Event\\Application\\FinishedSubscriber' => '/phpunit/Event/Events/Application/FinishedSubscriber.php', + 'PHPUnit\\Event\\Application\\Started' => '/phpunit/Event/Events/Application/Started.php', + 'PHPUnit\\Event\\Application\\StartedSubscriber' => '/phpunit/Event/Events/Application/StartedSubscriber.php', + 'PHPUnit\\Event\\Code\\ClassMethod' => '/phpunit/Event/Value/ClassMethod.php', + 'PHPUnit\\Event\\Code\\ComparisonFailure' => '/phpunit/Event/Value/ComparisonFailure.php', + 'PHPUnit\\Event\\Code\\ComparisonFailureBuilder' => '/phpunit/Event/Value/ComparisonFailureBuilder.php', + 'PHPUnit\\Event\\Code\\NoTestCaseObjectOnCallStackException' => '/phpunit/Event/Exception/NoTestCaseObjectOnCallStackException.php', + 'PHPUnit\\Event\\Code\\Phpt' => '/phpunit/Event/Value/Test/Phpt.php', + 'PHPUnit\\Event\\Code\\Test' => '/phpunit/Event/Value/Test/Test.php', + 'PHPUnit\\Event\\Code\\TestCollection' => '/phpunit/Event/Value/Test/TestCollection.php', + 'PHPUnit\\Event\\Code\\TestCollectionIterator' => '/phpunit/Event/Value/Test/TestCollectionIterator.php', + 'PHPUnit\\Event\\Code\\TestDox' => '/phpunit/Event/Value/Test/TestDox.php', + 'PHPUnit\\Event\\Code\\TestDoxBuilder' => '/phpunit/Event/Value/Test/TestDoxBuilder.php', + 'PHPUnit\\Event\\Code\\TestMethod' => '/phpunit/Event/Value/Test/TestMethod.php', + 'PHPUnit\\Event\\Code\\TestMethodBuilder' => '/phpunit/Event/Value/Test/TestMethodBuilder.php', + 'PHPUnit\\Event\\Code\\Throwable' => '/phpunit/Event/Value/Throwable.php', + 'PHPUnit\\Event\\Code\\ThrowableBuilder' => '/phpunit/Event/Value/ThrowableBuilder.php', + 'PHPUnit\\Event\\CollectingDispatcher' => '/phpunit/Event/Dispatcher/CollectingDispatcher.php', + 'PHPUnit\\Event\\DeferringDispatcher' => '/phpunit/Event/Dispatcher/DeferringDispatcher.php', + 'PHPUnit\\Event\\DirectDispatcher' => '/phpunit/Event/Dispatcher/DirectDispatcher.php', + 'PHPUnit\\Event\\Dispatcher' => '/phpunit/Event/Dispatcher/Dispatcher.php', + 'PHPUnit\\Event\\DispatchingEmitter' => '/phpunit/Event/Emitter/DispatchingEmitter.php', + 'PHPUnit\\Event\\Emitter' => '/phpunit/Event/Emitter/Emitter.php', + 'PHPUnit\\Event\\Event' => '/phpunit/Event/Events/Event.php', + 'PHPUnit\\Event\\EventAlreadyAssignedException' => '/phpunit/Event/Exception/EventAlreadyAssignedException.php', + 'PHPUnit\\Event\\EventCollection' => '/phpunit/Event/Events/EventCollection.php', + 'PHPUnit\\Event\\EventCollectionIterator' => '/phpunit/Event/Events/EventCollectionIterator.php', + 'PHPUnit\\Event\\EventFacadeIsSealedException' => '/phpunit/Event/Exception/EventFacadeIsSealedException.php', + 'PHPUnit\\Event\\Exception' => '/phpunit/Event/Exception/Exception.php', + 'PHPUnit\\Event\\Facade' => '/phpunit/Event/Facade.php', + 'PHPUnit\\Event\\InvalidArgumentException' => '/phpunit/Event/Exception/InvalidArgumentException.php', + 'PHPUnit\\Event\\InvalidEventException' => '/phpunit/Event/Exception/InvalidEventException.php', + 'PHPUnit\\Event\\InvalidSubscriberException' => '/phpunit/Event/Exception/InvalidSubscriberException.php', + 'PHPUnit\\Event\\MapError' => '/phpunit/Event/Exception/MapError.php', + 'PHPUnit\\Event\\NoPreviousThrowableException' => '/phpunit/Event/Exception/NoPreviousThrowableException.php', + 'PHPUnit\\Event\\RuntimeException' => '/phpunit/Event/Exception/RuntimeException.php', + 'PHPUnit\\Event\\Runtime\\OperatingSystem' => '/phpunit/Event/Value/Runtime/OperatingSystem.php', + 'PHPUnit\\Event\\Runtime\\PHP' => '/phpunit/Event/Value/Runtime/PHP.php', + 'PHPUnit\\Event\\Runtime\\PHPUnit' => '/phpunit/Event/Value/Runtime/PHPUnit.php', + 'PHPUnit\\Event\\Runtime\\Runtime' => '/phpunit/Event/Value/Runtime/Runtime.php', + 'PHPUnit\\Event\\SubscribableDispatcher' => '/phpunit/Event/Dispatcher/SubscribableDispatcher.php', + 'PHPUnit\\Event\\Subscriber' => '/phpunit/Event/Subscriber.php', + 'PHPUnit\\Event\\SubscriberTypeAlreadyRegisteredException' => '/phpunit/Event/Exception/SubscriberTypeAlreadyRegisteredException.php', + 'PHPUnit\\Event\\Telemetry\\Duration' => '/phpunit/Event/Value/Telemetry/Duration.php', + 'PHPUnit\\Event\\Telemetry\\GarbageCollectorStatus' => '/phpunit/Event/Value/Telemetry/GarbageCollectorStatus.php', + 'PHPUnit\\Event\\Telemetry\\GarbageCollectorStatusProvider' => '/phpunit/Event/Value/Telemetry/GarbageCollectorStatusProvider.php', + 'PHPUnit\\Event\\Telemetry\\HRTime' => '/phpunit/Event/Value/Telemetry/HRTime.php', + 'PHPUnit\\Event\\Telemetry\\Info' => '/phpunit/Event/Value/Telemetry/Info.php', + 'PHPUnit\\Event\\Telemetry\\MemoryMeter' => '/phpunit/Event/Value/Telemetry/MemoryMeter.php', + 'PHPUnit\\Event\\Telemetry\\MemoryUsage' => '/phpunit/Event/Value/Telemetry/MemoryUsage.php', + 'PHPUnit\\Event\\Telemetry\\Php81GarbageCollectorStatusProvider' => '/phpunit/Event/Value/Telemetry/Php81GarbageCollectorStatusProvider.php', + 'PHPUnit\\Event\\Telemetry\\Php83GarbageCollectorStatusProvider' => '/phpunit/Event/Value/Telemetry/Php83GarbageCollectorStatusProvider.php', + 'PHPUnit\\Event\\Telemetry\\Snapshot' => '/phpunit/Event/Value/Telemetry/Snapshot.php', + 'PHPUnit\\Event\\Telemetry\\StopWatch' => '/phpunit/Event/Value/Telemetry/StopWatch.php', + 'PHPUnit\\Event\\Telemetry\\System' => '/phpunit/Event/Value/Telemetry/System.php', + 'PHPUnit\\Event\\Telemetry\\SystemMemoryMeter' => '/phpunit/Event/Value/Telemetry/SystemMemoryMeter.php', + 'PHPUnit\\Event\\Telemetry\\SystemStopWatch' => '/phpunit/Event/Value/Telemetry/SystemStopWatch.php', + 'PHPUnit\\Event\\Telemetry\\SystemStopWatchWithOffset' => '/phpunit/Event/Value/Telemetry/SystemStopWatchWithOffset.php', + 'PHPUnit\\Event\\TestData\\DataFromDataProvider' => '/phpunit/Event/Value/Test/TestData/DataFromDataProvider.php', + 'PHPUnit\\Event\\TestData\\DataFromTestDependency' => '/phpunit/Event/Value/Test/TestData/DataFromTestDependency.php', + 'PHPUnit\\Event\\TestData\\MoreThanOneDataSetFromDataProviderException' => '/phpunit/Event/Exception/MoreThanOneDataSetFromDataProviderException.php', + 'PHPUnit\\Event\\TestData\\NoDataSetFromDataProviderException' => '/phpunit/Event/Exception/NoDataSetFromDataProviderException.php', + 'PHPUnit\\Event\\TestData\\TestData' => '/phpunit/Event/Value/Test/TestData/TestData.php', + 'PHPUnit\\Event\\TestData\\TestDataCollection' => '/phpunit/Event/Value/Test/TestData/TestDataCollection.php', + 'PHPUnit\\Event\\TestData\\TestDataCollectionIterator' => '/phpunit/Event/Value/Test/TestData/TestDataCollectionIterator.php', + 'PHPUnit\\Event\\TestRunner\\BootstrapFinished' => '/phpunit/Event/Events/TestRunner/BootstrapFinished.php', + 'PHPUnit\\Event\\TestRunner\\BootstrapFinishedSubscriber' => '/phpunit/Event/Events/TestRunner/BootstrapFinishedSubscriber.php', + 'PHPUnit\\Event\\TestRunner\\Configured' => '/phpunit/Event/Events/TestRunner/Configured.php', + 'PHPUnit\\Event\\TestRunner\\ConfiguredSubscriber' => '/phpunit/Event/Events/TestRunner/ConfiguredSubscriber.php', + 'PHPUnit\\Event\\TestRunner\\DeprecationTriggered' => '/phpunit/Event/Events/TestRunner/DeprecationTriggered.php', + 'PHPUnit\\Event\\TestRunner\\DeprecationTriggeredSubscriber' => '/phpunit/Event/Events/TestRunner/DeprecationTriggeredSubscriber.php', + 'PHPUnit\\Event\\TestRunner\\EventFacadeSealed' => '/phpunit/Event/Events/TestRunner/EventFacadeSealed.php', + 'PHPUnit\\Event\\TestRunner\\EventFacadeSealedSubscriber' => '/phpunit/Event/Events/TestRunner/EventFacadeSealedSubscriber.php', + 'PHPUnit\\Event\\TestRunner\\ExecutionAborted' => '/phpunit/Event/Events/TestRunner/ExecutionAborted.php', + 'PHPUnit\\Event\\TestRunner\\ExecutionAbortedSubscriber' => '/phpunit/Event/Events/TestRunner/ExecutionAbortedSubscriber.php', + 'PHPUnit\\Event\\TestRunner\\ExecutionFinished' => '/phpunit/Event/Events/TestRunner/ExecutionFinished.php', + 'PHPUnit\\Event\\TestRunner\\ExecutionFinishedSubscriber' => '/phpunit/Event/Events/TestRunner/ExecutionFinishedSubscriber.php', + 'PHPUnit\\Event\\TestRunner\\ExecutionStarted' => '/phpunit/Event/Events/TestRunner/ExecutionStarted.php', + 'PHPUnit\\Event\\TestRunner\\ExecutionStartedSubscriber' => '/phpunit/Event/Events/TestRunner/ExecutionStartedSubscriber.php', + 'PHPUnit\\Event\\TestRunner\\ExtensionBootstrapped' => '/phpunit/Event/Events/TestRunner/ExtensionBootstrapped.php', + 'PHPUnit\\Event\\TestRunner\\ExtensionBootstrappedSubscriber' => '/phpunit/Event/Events/TestRunner/ExtensionBootstrappedSubscriber.php', + 'PHPUnit\\Event\\TestRunner\\ExtensionLoadedFromPhar' => '/phpunit/Event/Events/TestRunner/ExtensionLoadedFromPhar.php', + 'PHPUnit\\Event\\TestRunner\\ExtensionLoadedFromPharSubscriber' => '/phpunit/Event/Events/TestRunner/ExtensionLoadedFromPharSubscriber.php', + 'PHPUnit\\Event\\TestRunner\\Finished' => '/phpunit/Event/Events/TestRunner/Finished.php', + 'PHPUnit\\Event\\TestRunner\\FinishedSubscriber' => '/phpunit/Event/Events/TestRunner/FinishedSubscriber.php', + 'PHPUnit\\Event\\TestRunner\\GarbageCollectionDisabled' => '/phpunit/Event/Events/TestRunner/GarbageCollectionDisabled.php', + 'PHPUnit\\Event\\TestRunner\\GarbageCollectionDisabledSubscriber' => '/phpunit/Event/Events/TestRunner/GarbageCollectionDisabledSubscriber.php', + 'PHPUnit\\Event\\TestRunner\\GarbageCollectionEnabled' => '/phpunit/Event/Events/TestRunner/GarbageCollectionEnabled.php', + 'PHPUnit\\Event\\TestRunner\\GarbageCollectionEnabledSubscriber' => '/phpunit/Event/Events/TestRunner/GarbageCollectionEnabledSubscriber.php', + 'PHPUnit\\Event\\TestRunner\\GarbageCollectionTriggered' => '/phpunit/Event/Events/TestRunner/GarbageCollectionTriggered.php', + 'PHPUnit\\Event\\TestRunner\\GarbageCollectionTriggeredSubscriber' => '/phpunit/Event/Events/TestRunner/GarbageCollectionTriggeredSubscriber.php', + 'PHPUnit\\Event\\TestRunner\\Started' => '/phpunit/Event/Events/TestRunner/Started.php', + 'PHPUnit\\Event\\TestRunner\\StartedSubscriber' => '/phpunit/Event/Events/TestRunner/StartedSubscriber.php', + 'PHPUnit\\Event\\TestRunner\\WarningTriggered' => '/phpunit/Event/Events/TestRunner/WarningTriggered.php', + 'PHPUnit\\Event\\TestRunner\\WarningTriggeredSubscriber' => '/phpunit/Event/Events/TestRunner/WarningTriggeredSubscriber.php', + 'PHPUnit\\Event\\TestSuite\\Filtered' => '/phpunit/Event/Events/TestSuite/Filtered.php', + 'PHPUnit\\Event\\TestSuite\\FilteredSubscriber' => '/phpunit/Event/Events/TestSuite/FilteredSubscriber.php', + 'PHPUnit\\Event\\TestSuite\\Finished' => '/phpunit/Event/Events/TestSuite/Finished.php', + 'PHPUnit\\Event\\TestSuite\\FinishedSubscriber' => '/phpunit/Event/Events/TestSuite/FinishedSubscriber.php', + 'PHPUnit\\Event\\TestSuite\\Loaded' => '/phpunit/Event/Events/TestSuite/Loaded.php', + 'PHPUnit\\Event\\TestSuite\\LoadedSubscriber' => '/phpunit/Event/Events/TestSuite/LoadedSubscriber.php', + 'PHPUnit\\Event\\TestSuite\\Skipped' => '/phpunit/Event/Events/TestSuite/Skipped.php', + 'PHPUnit\\Event\\TestSuite\\SkippedSubscriber' => '/phpunit/Event/Events/TestSuite/SkippedSubscriber.php', + 'PHPUnit\\Event\\TestSuite\\Sorted' => '/phpunit/Event/Events/TestSuite/Sorted.php', + 'PHPUnit\\Event\\TestSuite\\SortedSubscriber' => '/phpunit/Event/Events/TestSuite/SortedSubscriber.php', + 'PHPUnit\\Event\\TestSuite\\Started' => '/phpunit/Event/Events/TestSuite/Started.php', + 'PHPUnit\\Event\\TestSuite\\StartedSubscriber' => '/phpunit/Event/Events/TestSuite/StartedSubscriber.php', + 'PHPUnit\\Event\\TestSuite\\TestSuite' => '/phpunit/Event/Value/TestSuite/TestSuite.php', + 'PHPUnit\\Event\\TestSuite\\TestSuiteBuilder' => '/phpunit/Event/Value/TestSuite/TestSuiteBuilder.php', + 'PHPUnit\\Event\\TestSuite\\TestSuiteForTestClass' => '/phpunit/Event/Value/TestSuite/TestSuiteForTestClass.php', + 'PHPUnit\\Event\\TestSuite\\TestSuiteForTestMethodWithDataProvider' => '/phpunit/Event/Value/TestSuite/TestSuiteForTestMethodWithDataProvider.php', + 'PHPUnit\\Event\\TestSuite\\TestSuiteWithName' => '/phpunit/Event/Value/TestSuite/TestSuiteWithName.php', + 'PHPUnit\\Event\\Test\\AfterLastTestMethodCalled' => '/phpunit/Event/Events/Test/HookMethod/AfterLastTestMethodCalled.php', + 'PHPUnit\\Event\\Test\\AfterLastTestMethodCalledSubscriber' => '/phpunit/Event/Events/Test/HookMethod/AfterLastTestMethodCalledSubscriber.php', + 'PHPUnit\\Event\\Test\\AfterLastTestMethodFinished' => '/phpunit/Event/Events/Test/HookMethod/AfterLastTestMethodFinished.php', + 'PHPUnit\\Event\\Test\\AfterLastTestMethodFinishedSubscriber' => '/phpunit/Event/Events/Test/HookMethod/AfterLastTestMethodFinishedSubscriber.php', + 'PHPUnit\\Event\\Test\\AfterTestMethodCalled' => '/phpunit/Event/Events/Test/HookMethod/AfterTestMethodCalled.php', + 'PHPUnit\\Event\\Test\\AfterTestMethodCalledSubscriber' => '/phpunit/Event/Events/Test/HookMethod/AfterTestMethodCalledSubscriber.php', + 'PHPUnit\\Event\\Test\\AfterTestMethodFinished' => '/phpunit/Event/Events/Test/HookMethod/AfterTestMethodFinished.php', + 'PHPUnit\\Event\\Test\\AfterTestMethodFinishedSubscriber' => '/phpunit/Event/Events/Test/HookMethod/AfterTestMethodFinishedSubscriber.php', + 'PHPUnit\\Event\\Test\\AssertionFailed' => '/phpunit/Event/Events/Test/Assertion/AssertionFailed.php', + 'PHPUnit\\Event\\Test\\AssertionFailedSubscriber' => '/phpunit/Event/Events/Test/Assertion/AssertionFailedSubscriber.php', + 'PHPUnit\\Event\\Test\\AssertionSucceeded' => '/phpunit/Event/Events/Test/Assertion/AssertionSucceeded.php', + 'PHPUnit\\Event\\Test\\AssertionSucceededSubscriber' => '/phpunit/Event/Events/Test/Assertion/AssertionSucceededSubscriber.php', + 'PHPUnit\\Event\\Test\\BeforeFirstTestMethodCalled' => '/phpunit/Event/Events/Test/HookMethod/BeforeFirstTestMethodCalled.php', + 'PHPUnit\\Event\\Test\\BeforeFirstTestMethodCalledSubscriber' => '/phpunit/Event/Events/Test/HookMethod/BeforeFirstTestMethodCalledSubscriber.php', + 'PHPUnit\\Event\\Test\\BeforeFirstTestMethodErrored' => '/phpunit/Event/Events/Test/HookMethod/BeforeFirstTestMethodErrored.php', + 'PHPUnit\\Event\\Test\\BeforeFirstTestMethodErroredSubscriber' => '/phpunit/Event/Events/Test/HookMethod/BeforeFirstTestMethodErroredSubscriber.php', + 'PHPUnit\\Event\\Test\\BeforeFirstTestMethodFinished' => '/phpunit/Event/Events/Test/HookMethod/BeforeFirstTestMethodFinished.php', + 'PHPUnit\\Event\\Test\\BeforeFirstTestMethodFinishedSubscriber' => '/phpunit/Event/Events/Test/HookMethod/BeforeFirstTestMethodFinishedSubscriber.php', + 'PHPUnit\\Event\\Test\\BeforeTestMethodCalled' => '/phpunit/Event/Events/Test/HookMethod/BeforeTestMethodCalled.php', + 'PHPUnit\\Event\\Test\\BeforeTestMethodCalledSubscriber' => '/phpunit/Event/Events/Test/HookMethod/BeforeTestMethodCalledSubscriber.php', + 'PHPUnit\\Event\\Test\\BeforeTestMethodFinished' => '/phpunit/Event/Events/Test/HookMethod/BeforeTestMethodFinished.php', + 'PHPUnit\\Event\\Test\\BeforeTestMethodFinishedSubscriber' => '/phpunit/Event/Events/Test/HookMethod/BeforeTestMethodFinishedSubscriber.php', + 'PHPUnit\\Event\\Test\\ComparatorRegistered' => '/phpunit/Event/Events/Test/ComparatorRegistered.php', + 'PHPUnit\\Event\\Test\\ComparatorRegisteredSubscriber' => '/phpunit/Event/Events/Test/ComparatorRegisteredSubscriber.php', + 'PHPUnit\\Event\\Test\\ConsideredRisky' => '/phpunit/Event/Events/Test/Issue/ConsideredRisky.php', + 'PHPUnit\\Event\\Test\\ConsideredRiskySubscriber' => '/phpunit/Event/Events/Test/Issue/ConsideredRiskySubscriber.php', + 'PHPUnit\\Event\\Test\\DataProviderMethodCalled' => '/phpunit/Event/Events/Test/Lifecycle/DataProviderMethodCalled.php', + 'PHPUnit\\Event\\Test\\DataProviderMethodCalledSubscriber' => '/phpunit/Event/Events/Test/Lifecycle/DataProviderMethodCalledSubscriber.php', + 'PHPUnit\\Event\\Test\\DataProviderMethodFinished' => '/phpunit/Event/Events/Test/Lifecycle/DataProviderMethodFinished.php', + 'PHPUnit\\Event\\Test\\DataProviderMethodFinishedSubscriber' => '/phpunit/Event/Events/Test/Lifecycle/DataProviderMethodFinishedSubscriber.php', + 'PHPUnit\\Event\\Test\\DeprecationTriggered' => '/phpunit/Event/Events/Test/Issue/DeprecationTriggered.php', + 'PHPUnit\\Event\\Test\\DeprecationTriggeredSubscriber' => '/phpunit/Event/Events/Test/Issue/DeprecationTriggeredSubscriber.php', + 'PHPUnit\\Event\\Test\\ErrorTriggered' => '/phpunit/Event/Events/Test/Issue/ErrorTriggered.php', + 'PHPUnit\\Event\\Test\\ErrorTriggeredSubscriber' => '/phpunit/Event/Events/Test/Issue/ErrorTriggeredSubscriber.php', + 'PHPUnit\\Event\\Test\\Errored' => '/phpunit/Event/Events/Test/Outcome/Errored.php', + 'PHPUnit\\Event\\Test\\ErroredSubscriber' => '/phpunit/Event/Events/Test/Outcome/ErroredSubscriber.php', + 'PHPUnit\\Event\\Test\\Failed' => '/phpunit/Event/Events/Test/Outcome/Failed.php', + 'PHPUnit\\Event\\Test\\FailedSubscriber' => '/phpunit/Event/Events/Test/Outcome/FailedSubscriber.php', + 'PHPUnit\\Event\\Test\\Finished' => '/phpunit/Event/Events/Test/Lifecycle/Finished.php', + 'PHPUnit\\Event\\Test\\FinishedSubscriber' => '/phpunit/Event/Events/Test/Lifecycle/FinishedSubscriber.php', + 'PHPUnit\\Event\\Test\\MarkedIncomplete' => '/phpunit/Event/Events/Test/Outcome/MarkedIncomplete.php', + 'PHPUnit\\Event\\Test\\MarkedIncompleteSubscriber' => '/phpunit/Event/Events/Test/Outcome/MarkedIncompleteSubscriber.php', + 'PHPUnit\\Event\\Test\\MockObjectCreated' => '/phpunit/Event/Events/Test/TestDouble/MockObjectCreated.php', + 'PHPUnit\\Event\\Test\\MockObjectCreatedSubscriber' => '/phpunit/Event/Events/Test/TestDouble/MockObjectCreatedSubscriber.php', + 'PHPUnit\\Event\\Test\\MockObjectForAbstractClassCreated' => '/phpunit/Event/Events/Test/TestDouble/MockObjectForAbstractClassCreated.php', + 'PHPUnit\\Event\\Test\\MockObjectForAbstractClassCreatedSubscriber' => '/phpunit/Event/Events/Test/TestDouble/MockObjectForAbstractClassCreatedSubscriber.php', + 'PHPUnit\\Event\\Test\\MockObjectForIntersectionOfInterfacesCreated' => '/phpunit/Event/Events/Test/TestDouble/MockObjectForIntersectionOfInterfacesCreated.php', + 'PHPUnit\\Event\\Test\\MockObjectForIntersectionOfInterfacesCreatedSubscriber' => '/phpunit/Event/Events/Test/TestDouble/MockObjectForIntersectionOfInterfacesCreatedSubscriber.php', + 'PHPUnit\\Event\\Test\\MockObjectForTraitCreated' => '/phpunit/Event/Events/Test/TestDouble/MockObjectForTraitCreated.php', + 'PHPUnit\\Event\\Test\\MockObjectForTraitCreatedSubscriber' => '/phpunit/Event/Events/Test/TestDouble/MockObjectForTraitCreatedSubscriber.php', + 'PHPUnit\\Event\\Test\\MockObjectFromWsdlCreated' => '/phpunit/Event/Events/Test/TestDouble/MockObjectFromWsdlCreated.php', + 'PHPUnit\\Event\\Test\\MockObjectFromWsdlCreatedSubscriber' => '/phpunit/Event/Events/Test/TestDouble/MockObjectFromWsdlCreatedSubscriber.php', + 'PHPUnit\\Event\\Test\\NoComparisonFailureException' => '/phpunit/Event/Exception/NoComparisonFailureException.php', + 'PHPUnit\\Event\\Test\\NoticeTriggered' => '/phpunit/Event/Events/Test/Issue/NoticeTriggered.php', + 'PHPUnit\\Event\\Test\\NoticeTriggeredSubscriber' => '/phpunit/Event/Events/Test/Issue/NoticeTriggeredSubscriber.php', + 'PHPUnit\\Event\\Test\\PartialMockObjectCreated' => '/phpunit/Event/Events/Test/TestDouble/PartialMockObjectCreated.php', + 'PHPUnit\\Event\\Test\\PartialMockObjectCreatedSubscriber' => '/phpunit/Event/Events/Test/TestDouble/PartialMockObjectCreatedSubscriber.php', + 'PHPUnit\\Event\\Test\\Passed' => '/phpunit/Event/Events/Test/Outcome/Passed.php', + 'PHPUnit\\Event\\Test\\PassedSubscriber' => '/phpunit/Event/Events/Test/Outcome/PassedSubscriber.php', + 'PHPUnit\\Event\\Test\\PhpDeprecationTriggered' => '/phpunit/Event/Events/Test/Issue/PhpDeprecationTriggered.php', + 'PHPUnit\\Event\\Test\\PhpDeprecationTriggeredSubscriber' => '/phpunit/Event/Events/Test/Issue/PhpDeprecationTriggeredSubscriber.php', + 'PHPUnit\\Event\\Test\\PhpNoticeTriggered' => '/phpunit/Event/Events/Test/Issue/PhpNoticeTriggered.php', + 'PHPUnit\\Event\\Test\\PhpNoticeTriggeredSubscriber' => '/phpunit/Event/Events/Test/Issue/PhpNoticeTriggeredSubscriber.php', + 'PHPUnit\\Event\\Test\\PhpWarningTriggered' => '/phpunit/Event/Events/Test/Issue/PhpWarningTriggered.php', + 'PHPUnit\\Event\\Test\\PhpWarningTriggeredSubscriber' => '/phpunit/Event/Events/Test/Issue/PhpWarningTriggeredSubscriber.php', + 'PHPUnit\\Event\\Test\\PhpunitDeprecationTriggered' => '/phpunit/Event/Events/Test/Issue/PhpunitDeprecationTriggered.php', + 'PHPUnit\\Event\\Test\\PhpunitDeprecationTriggeredSubscriber' => '/phpunit/Event/Events/Test/Issue/PhpunitDeprecationTriggeredSubscriber.php', + 'PHPUnit\\Event\\Test\\PhpunitErrorTriggered' => '/phpunit/Event/Events/Test/Issue/PhpunitErrorTriggered.php', + 'PHPUnit\\Event\\Test\\PhpunitErrorTriggeredSubscriber' => '/phpunit/Event/Events/Test/Issue/PhpunitErrorTriggeredSubscriber.php', + 'PHPUnit\\Event\\Test\\PhpunitWarningTriggered' => '/phpunit/Event/Events/Test/Issue/PhpunitWarningTriggered.php', + 'PHPUnit\\Event\\Test\\PhpunitWarningTriggeredSubscriber' => '/phpunit/Event/Events/Test/Issue/PhpunitWarningTriggeredSubscriber.php', + 'PHPUnit\\Event\\Test\\PostConditionCalled' => '/phpunit/Event/Events/Test/HookMethod/PostConditionCalled.php', + 'PHPUnit\\Event\\Test\\PostConditionCalledSubscriber' => '/phpunit/Event/Events/Test/HookMethod/PostConditionCalledSubscriber.php', + 'PHPUnit\\Event\\Test\\PostConditionFinished' => '/phpunit/Event/Events/Test/HookMethod/PostConditionFinished.php', + 'PHPUnit\\Event\\Test\\PostConditionFinishedSubscriber' => '/phpunit/Event/Events/Test/HookMethod/PostConditionFinishedSubscriber.php', + 'PHPUnit\\Event\\Test\\PreConditionCalled' => '/phpunit/Event/Events/Test/HookMethod/PreConditionCalled.php', + 'PHPUnit\\Event\\Test\\PreConditionCalledSubscriber' => '/phpunit/Event/Events/Test/HookMethod/PreConditionCalledSubscriber.php', + 'PHPUnit\\Event\\Test\\PreConditionFinished' => '/phpunit/Event/Events/Test/HookMethod/PreConditionFinished.php', + 'PHPUnit\\Event\\Test\\PreConditionFinishedSubscriber' => '/phpunit/Event/Events/Test/HookMethod/PreConditionFinishedSubscriber.php', + 'PHPUnit\\Event\\Test\\PreparationFailed' => '/phpunit/Event/Events/Test/Lifecycle/PreparationFailed.php', + 'PHPUnit\\Event\\Test\\PreparationFailedSubscriber' => '/phpunit/Event/Events/Test/Lifecycle/PreparationFailedSubscriber.php', + 'PHPUnit\\Event\\Test\\PreparationStarted' => '/phpunit/Event/Events/Test/Lifecycle/PreparationStarted.php', + 'PHPUnit\\Event\\Test\\PreparationStartedSubscriber' => '/phpunit/Event/Events/Test/Lifecycle/PreparationStartedSubscriber.php', + 'PHPUnit\\Event\\Test\\Prepared' => '/phpunit/Event/Events/Test/Lifecycle/Prepared.php', + 'PHPUnit\\Event\\Test\\PreparedSubscriber' => '/phpunit/Event/Events/Test/Lifecycle/PreparedSubscriber.php', + 'PHPUnit\\Event\\Test\\PrintedUnexpectedOutput' => '/phpunit/Event/Events/Test/PrintedUnexpectedOutput.php', + 'PHPUnit\\Event\\Test\\PrintedUnexpectedOutputSubscriber' => '/phpunit/Event/Events/Test/PrintedUnexpectedOutputSubscriber.php', + 'PHPUnit\\Event\\Test\\Skipped' => '/phpunit/Event/Events/Test/Outcome/Skipped.php', + 'PHPUnit\\Event\\Test\\SkippedSubscriber' => '/phpunit/Event/Events/Test/Outcome/SkippedSubscriber.php', + 'PHPUnit\\Event\\Test\\TestProxyCreated' => '/phpunit/Event/Events/Test/TestDouble/TestProxyCreated.php', + 'PHPUnit\\Event\\Test\\TestProxyCreatedSubscriber' => '/phpunit/Event/Events/Test/TestDouble/TestProxyCreatedSubscriber.php', + 'PHPUnit\\Event\\Test\\TestStubCreated' => '/phpunit/Event/Events/Test/TestDouble/TestStubCreated.php', + 'PHPUnit\\Event\\Test\\TestStubCreatedSubscriber' => '/phpunit/Event/Events/Test/TestDouble/TestStubCreatedSubscriber.php', + 'PHPUnit\\Event\\Test\\TestStubForIntersectionOfInterfacesCreated' => '/phpunit/Event/Events/Test/TestDouble/TestStubForIntersectionOfInterfacesCreated.php', + 'PHPUnit\\Event\\Test\\TestStubForIntersectionOfInterfacesCreatedSubscriber' => '/phpunit/Event/Events/Test/TestDouble/TestStubForIntersectionOfInterfacesCreatedSubscriber.php', + 'PHPUnit\\Event\\Test\\WarningTriggered' => '/phpunit/Event/Events/Test/Issue/WarningTriggered.php', + 'PHPUnit\\Event\\Test\\WarningTriggeredSubscriber' => '/phpunit/Event/Events/Test/Issue/WarningTriggeredSubscriber.php', + 'PHPUnit\\Event\\Tracer\\Tracer' => '/phpunit/Event/Tracer.php', + 'PHPUnit\\Event\\TypeMap' => '/phpunit/Event/TypeMap.php', + 'PHPUnit\\Event\\UnknownEventException' => '/phpunit/Event/Exception/UnknownEventException.php', + 'PHPUnit\\Event\\UnknownEventTypeException' => '/phpunit/Event/Exception/UnknownEventTypeException.php', + 'PHPUnit\\Event\\UnknownSubscriberException' => '/phpunit/Event/Exception/UnknownSubscriberException.php', + 'PHPUnit\\Event\\UnknownSubscriberTypeException' => '/phpunit/Event/Exception/UnknownSubscriberTypeException.php', 'PHPUnit\\Exception' => '/phpunit/Exception.php', - 'PHPUnit\\Framework\\ActualValueIsNotAnObjectException' => '/phpunit/Framework/Exception/ActualValueIsNotAnObjectException.php', + 'PHPUnit\\Framework\\ActualValueIsNotAnObjectException' => '/phpunit/Framework/Exception/ObjectEquals/ActualValueIsNotAnObjectException.php', 'PHPUnit\\Framework\\Assert' => '/phpunit/Framework/Assert.php', 'PHPUnit\\Framework\\AssertionFailedError' => '/phpunit/Framework/Exception/AssertionFailedError.php', + 'PHPUnit\\Framework\\Attributes\\After' => '/phpunit/Framework/Attributes/After.php', + 'PHPUnit\\Framework\\Attributes\\AfterClass' => '/phpunit/Framework/Attributes/AfterClass.php', + 'PHPUnit\\Framework\\Attributes\\BackupGlobals' => '/phpunit/Framework/Attributes/BackupGlobals.php', + 'PHPUnit\\Framework\\Attributes\\BackupStaticProperties' => '/phpunit/Framework/Attributes/BackupStaticProperties.php', + 'PHPUnit\\Framework\\Attributes\\Before' => '/phpunit/Framework/Attributes/Before.php', + 'PHPUnit\\Framework\\Attributes\\BeforeClass' => '/phpunit/Framework/Attributes/BeforeClass.php', + 'PHPUnit\\Framework\\Attributes\\CodeCoverageIgnore' => '/phpunit/Framework/Attributes/CodeCoverageIgnore.php', + 'PHPUnit\\Framework\\Attributes\\CoversClass' => '/phpunit/Framework/Attributes/CoversClass.php', + 'PHPUnit\\Framework\\Attributes\\CoversFunction' => '/phpunit/Framework/Attributes/CoversFunction.php', + 'PHPUnit\\Framework\\Attributes\\CoversNothing' => '/phpunit/Framework/Attributes/CoversNothing.php', + 'PHPUnit\\Framework\\Attributes\\DataProvider' => '/phpunit/Framework/Attributes/DataProvider.php', + 'PHPUnit\\Framework\\Attributes\\DataProviderExternal' => '/phpunit/Framework/Attributes/DataProviderExternal.php', + 'PHPUnit\\Framework\\Attributes\\Depends' => '/phpunit/Framework/Attributes/Depends.php', + 'PHPUnit\\Framework\\Attributes\\DependsExternal' => '/phpunit/Framework/Attributes/DependsExternal.php', + 'PHPUnit\\Framework\\Attributes\\DependsExternalUsingDeepClone' => '/phpunit/Framework/Attributes/DependsExternalUsingDeepClone.php', + 'PHPUnit\\Framework\\Attributes\\DependsExternalUsingShallowClone' => '/phpunit/Framework/Attributes/DependsExternalUsingShallowClone.php', + 'PHPUnit\\Framework\\Attributes\\DependsOnClass' => '/phpunit/Framework/Attributes/DependsOnClass.php', + 'PHPUnit\\Framework\\Attributes\\DependsOnClassUsingDeepClone' => '/phpunit/Framework/Attributes/DependsOnClassUsingDeepClone.php', + 'PHPUnit\\Framework\\Attributes\\DependsOnClassUsingShallowClone' => '/phpunit/Framework/Attributes/DependsOnClassUsingShallowClone.php', + 'PHPUnit\\Framework\\Attributes\\DependsUsingDeepClone' => '/phpunit/Framework/Attributes/DependsUsingDeepClone.php', + 'PHPUnit\\Framework\\Attributes\\DependsUsingShallowClone' => '/phpunit/Framework/Attributes/DependsUsingShallowClone.php', + 'PHPUnit\\Framework\\Attributes\\DoesNotPerformAssertions' => '/phpunit/Framework/Attributes/DoesNotPerformAssertions.php', + 'PHPUnit\\Framework\\Attributes\\ExcludeGlobalVariableFromBackup' => '/phpunit/Framework/Attributes/ExcludeGlobalVariableFromBackup.php', + 'PHPUnit\\Framework\\Attributes\\ExcludeStaticPropertyFromBackup' => '/phpunit/Framework/Attributes/ExcludeStaticPropertyFromBackup.php', + 'PHPUnit\\Framework\\Attributes\\Group' => '/phpunit/Framework/Attributes/Group.php', + 'PHPUnit\\Framework\\Attributes\\IgnoreClassForCodeCoverage' => '/phpunit/Framework/Attributes/IgnoreClassForCodeCoverage.php', + 'PHPUnit\\Framework\\Attributes\\IgnoreDeprecations' => '/phpunit/Framework/Attributes/IgnoreDeprecations.php', + 'PHPUnit\\Framework\\Attributes\\IgnoreFunctionForCodeCoverage' => '/phpunit/Framework/Attributes/IgnoreFunctionForCodeCoverage.php', + 'PHPUnit\\Framework\\Attributes\\IgnoreMethodForCodeCoverage' => '/phpunit/Framework/Attributes/IgnoreMethodForCodeCoverage.php', + 'PHPUnit\\Framework\\Attributes\\Large' => '/phpunit/Framework/Attributes/Large.php', + 'PHPUnit\\Framework\\Attributes\\Medium' => '/phpunit/Framework/Attributes/Medium.php', + 'PHPUnit\\Framework\\Attributes\\PostCondition' => '/phpunit/Framework/Attributes/PostCondition.php', + 'PHPUnit\\Framework\\Attributes\\PreCondition' => '/phpunit/Framework/Attributes/PreCondition.php', + 'PHPUnit\\Framework\\Attributes\\PreserveGlobalState' => '/phpunit/Framework/Attributes/PreserveGlobalState.php', + 'PHPUnit\\Framework\\Attributes\\RequiresFunction' => '/phpunit/Framework/Attributes/RequiresFunction.php', + 'PHPUnit\\Framework\\Attributes\\RequiresMethod' => '/phpunit/Framework/Attributes/RequiresMethod.php', + 'PHPUnit\\Framework\\Attributes\\RequiresOperatingSystem' => '/phpunit/Framework/Attributes/RequiresOperatingSystem.php', + 'PHPUnit\\Framework\\Attributes\\RequiresOperatingSystemFamily' => '/phpunit/Framework/Attributes/RequiresOperatingSystemFamily.php', + 'PHPUnit\\Framework\\Attributes\\RequiresPhp' => '/phpunit/Framework/Attributes/RequiresPhp.php', + 'PHPUnit\\Framework\\Attributes\\RequiresPhpExtension' => '/phpunit/Framework/Attributes/RequiresPhpExtension.php', + 'PHPUnit\\Framework\\Attributes\\RequiresPhpunit' => '/phpunit/Framework/Attributes/RequiresPhpunit.php', + 'PHPUnit\\Framework\\Attributes\\RequiresSetting' => '/phpunit/Framework/Attributes/RequiresSetting.php', + 'PHPUnit\\Framework\\Attributes\\RunClassInSeparateProcess' => '/phpunit/Framework/Attributes/RunClassInSeparateProcess.php', + 'PHPUnit\\Framework\\Attributes\\RunInSeparateProcess' => '/phpunit/Framework/Attributes/RunInSeparateProcess.php', + 'PHPUnit\\Framework\\Attributes\\RunTestsInSeparateProcesses' => '/phpunit/Framework/Attributes/RunTestsInSeparateProcesses.php', + 'PHPUnit\\Framework\\Attributes\\Small' => '/phpunit/Framework/Attributes/Small.php', + 'PHPUnit\\Framework\\Attributes\\Test' => '/phpunit/Framework/Attributes/Test.php', + 'PHPUnit\\Framework\\Attributes\\TestDox' => '/phpunit/Framework/Attributes/TestDox.php', + 'PHPUnit\\Framework\\Attributes\\TestWith' => '/phpunit/Framework/Attributes/TestWith.php', + 'PHPUnit\\Framework\\Attributes\\TestWithJson' => '/phpunit/Framework/Attributes/TestWithJson.php', + 'PHPUnit\\Framework\\Attributes\\Ticket' => '/phpunit/Framework/Attributes/Ticket.php', + 'PHPUnit\\Framework\\Attributes\\UsesClass' => '/phpunit/Framework/Attributes/UsesClass.php', + 'PHPUnit\\Framework\\Attributes\\UsesFunction' => '/phpunit/Framework/Attributes/UsesFunction.php', + 'PHPUnit\\Framework\\Attributes\\WithoutErrorHandler' => '/phpunit/Framework/Attributes/WithoutErrorHandler.php', 'PHPUnit\\Framework\\CodeCoverageException' => '/phpunit/Framework/Exception/CodeCoverageException.php', - 'PHPUnit\\Framework\\ComparisonMethodDoesNotAcceptParameterTypeException' => '/phpunit/Framework/Exception/ComparisonMethodDoesNotAcceptParameterTypeException.php', - 'PHPUnit\\Framework\\ComparisonMethodDoesNotDeclareBoolReturnTypeException' => '/phpunit/Framework/Exception/ComparisonMethodDoesNotDeclareBoolReturnTypeException.php', - 'PHPUnit\\Framework\\ComparisonMethodDoesNotDeclareExactlyOneParameterException' => '/phpunit/Framework/Exception/ComparisonMethodDoesNotDeclareExactlyOneParameterException.php', - 'PHPUnit\\Framework\\ComparisonMethodDoesNotDeclareParameterTypeException' => '/phpunit/Framework/Exception/ComparisonMethodDoesNotDeclareParameterTypeException.php', - 'PHPUnit\\Framework\\ComparisonMethodDoesNotExistException' => '/phpunit/Framework/Exception/ComparisonMethodDoesNotExistException.php', + 'PHPUnit\\Framework\\ComparisonMethodDoesNotAcceptParameterTypeException' => '/phpunit/Framework/Exception/ObjectEquals/ComparisonMethodDoesNotAcceptParameterTypeException.php', + 'PHPUnit\\Framework\\ComparisonMethodDoesNotDeclareBoolReturnTypeException' => '/phpunit/Framework/Exception/ObjectEquals/ComparisonMethodDoesNotDeclareBoolReturnTypeException.php', + 'PHPUnit\\Framework\\ComparisonMethodDoesNotDeclareExactlyOneParameterException' => '/phpunit/Framework/Exception/ObjectEquals/ComparisonMethodDoesNotDeclareExactlyOneParameterException.php', + 'PHPUnit\\Framework\\ComparisonMethodDoesNotDeclareParameterTypeException' => '/phpunit/Framework/Exception/ObjectEquals/ComparisonMethodDoesNotDeclareParameterTypeException.php', + 'PHPUnit\\Framework\\ComparisonMethodDoesNotExistException' => '/phpunit/Framework/Exception/ObjectEquals/ComparisonMethodDoesNotExistException.php', 'PHPUnit\\Framework\\Constraint\\ArrayHasKey' => '/phpunit/Framework/Constraint/Traversable/ArrayHasKey.php', 'PHPUnit\\Framework\\Constraint\\BinaryOperator' => '/phpunit/Framework/Constraint/Operator/BinaryOperator.php', 'PHPUnit\\Framework\\Constraint\\Callback' => '/phpunit/Framework/Constraint/Callback.php', - 'PHPUnit\\Framework\\Constraint\\ClassHasAttribute' => '/phpunit/Framework/Constraint/Object/ClassHasAttribute.php', - 'PHPUnit\\Framework\\Constraint\\ClassHasStaticAttribute' => '/phpunit/Framework/Constraint/Object/ClassHasStaticAttribute.php', 'PHPUnit\\Framework\\Constraint\\Constraint' => '/phpunit/Framework/Constraint/Constraint.php', 'PHPUnit\\Framework\\Constraint\\Count' => '/phpunit/Framework/Constraint/Cardinality/Count.php', 'PHPUnit\\Framework\\Constraint\\DirectoryExists' => '/phpunit/Framework/Constraint/Filesystem/DirectoryExists.php', 'PHPUnit\\Framework\\Constraint\\Exception' => '/phpunit/Framework/Constraint/Exception/Exception.php', 'PHPUnit\\Framework\\Constraint\\ExceptionCode' => '/phpunit/Framework/Constraint/Exception/ExceptionCode.php', - 'PHPUnit\\Framework\\Constraint\\ExceptionMessage' => '/phpunit/Framework/Constraint/Exception/ExceptionMessage.php', - 'PHPUnit\\Framework\\Constraint\\ExceptionMessageRegularExpression' => '/phpunit/Framework/Constraint/Exception/ExceptionMessageRegularExpression.php', + 'PHPUnit\\Framework\\Constraint\\ExceptionMessageIsOrContains' => '/phpunit/Framework/Constraint/Exception/ExceptionMessageIsOrContains.php', + 'PHPUnit\\Framework\\Constraint\\ExceptionMessageMatchesRegularExpression' => '/phpunit/Framework/Constraint/Exception/ExceptionMessageMatchesRegularExpression.php', 'PHPUnit\\Framework\\Constraint\\FileExists' => '/phpunit/Framework/Constraint/Filesystem/FileExists.php', 'PHPUnit\\Framework\\Constraint\\GreaterThan' => '/phpunit/Framework/Constraint/Cardinality/GreaterThan.php', 'PHPUnit\\Framework\\Constraint\\IsAnything' => '/phpunit/Framework/Constraint/IsAnything.php', @@ -878,6 +945,7 @@ spl_autoload_register( 'PHPUnit\\Framework\\Constraint\\IsInfinite' => '/phpunit/Framework/Constraint/Math/IsInfinite.php', 'PHPUnit\\Framework\\Constraint\\IsInstanceOf' => '/phpunit/Framework/Constraint/Type/IsInstanceOf.php', 'PHPUnit\\Framework\\Constraint\\IsJson' => '/phpunit/Framework/Constraint/String/IsJson.php', + 'PHPUnit\\Framework\\Constraint\\IsList' => '/phpunit/Framework/Constraint/Traversable/IsList.php', 'PHPUnit\\Framework\\Constraint\\IsNan' => '/phpunit/Framework/Constraint/Math/IsNan.php', 'PHPUnit\\Framework\\Constraint\\IsNull' => '/phpunit/Framework/Constraint/Type/IsNull.php', 'PHPUnit\\Framework\\Constraint\\IsReadable' => '/phpunit/Framework/Constraint/Filesystem/IsReadable.php', @@ -885,20 +953,19 @@ spl_autoload_register( 'PHPUnit\\Framework\\Constraint\\IsType' => '/phpunit/Framework/Constraint/Type/IsType.php', 'PHPUnit\\Framework\\Constraint\\IsWritable' => '/phpunit/Framework/Constraint/Filesystem/IsWritable.php', 'PHPUnit\\Framework\\Constraint\\JsonMatches' => '/phpunit/Framework/Constraint/JsonMatches.php', - 'PHPUnit\\Framework\\Constraint\\JsonMatchesErrorMessageProvider' => '/phpunit/Framework/Constraint/JsonMatchesErrorMessageProvider.php', 'PHPUnit\\Framework\\Constraint\\LessThan' => '/phpunit/Framework/Constraint/Cardinality/LessThan.php', 'PHPUnit\\Framework\\Constraint\\LogicalAnd' => '/phpunit/Framework/Constraint/Operator/LogicalAnd.php', 'PHPUnit\\Framework\\Constraint\\LogicalNot' => '/phpunit/Framework/Constraint/Operator/LogicalNot.php', 'PHPUnit\\Framework\\Constraint\\LogicalOr' => '/phpunit/Framework/Constraint/Operator/LogicalOr.php', 'PHPUnit\\Framework\\Constraint\\LogicalXor' => '/phpunit/Framework/Constraint/Operator/LogicalXor.php', 'PHPUnit\\Framework\\Constraint\\ObjectEquals' => '/phpunit/Framework/Constraint/Object/ObjectEquals.php', - 'PHPUnit\\Framework\\Constraint\\ObjectHasAttribute' => '/phpunit/Framework/Constraint/Object/ObjectHasAttribute.php', 'PHPUnit\\Framework\\Constraint\\ObjectHasProperty' => '/phpunit/Framework/Constraint/Object/ObjectHasProperty.php', 'PHPUnit\\Framework\\Constraint\\Operator' => '/phpunit/Framework/Constraint/Operator/Operator.php', 'PHPUnit\\Framework\\Constraint\\RegularExpression' => '/phpunit/Framework/Constraint/String/RegularExpression.php', 'PHPUnit\\Framework\\Constraint\\SameSize' => '/phpunit/Framework/Constraint/Cardinality/SameSize.php', 'PHPUnit\\Framework\\Constraint\\StringContains' => '/phpunit/Framework/Constraint/String/StringContains.php', 'PHPUnit\\Framework\\Constraint\\StringEndsWith' => '/phpunit/Framework/Constraint/String/StringEndsWith.php', + 'PHPUnit\\Framework\\Constraint\\StringEqualsStringIgnoringLineEndings' => '/phpunit/Framework/Constraint/String/StringEqualsStringIgnoringLineEndings.php', 'PHPUnit\\Framework\\Constraint\\StringMatchesFormatDescription' => '/phpunit/Framework/Constraint/String/StringMatchesFormatDescription.php', 'PHPUnit\\Framework\\Constraint\\StringStartsWith' => '/phpunit/Framework/Constraint/String/StringStartsWith.php', 'PHPUnit\\Framework\\Constraint\\TraversableContains' => '/phpunit/Framework/Constraint/Traversable/TraversableContains.php', @@ -906,396 +973,566 @@ spl_autoload_register( 'PHPUnit\\Framework\\Constraint\\TraversableContainsIdentical' => '/phpunit/Framework/Constraint/Traversable/TraversableContainsIdentical.php', 'PHPUnit\\Framework\\Constraint\\TraversableContainsOnly' => '/phpunit/Framework/Constraint/Traversable/TraversableContainsOnly.php', 'PHPUnit\\Framework\\Constraint\\UnaryOperator' => '/phpunit/Framework/Constraint/Operator/UnaryOperator.php', - 'PHPUnit\\Framework\\CoveredCodeNotExecutedException' => '/phpunit/Framework/Exception/CoveredCodeNotExecutedException.php', 'PHPUnit\\Framework\\DataProviderTestSuite' => '/phpunit/Framework/DataProviderTestSuite.php', - 'PHPUnit\\Framework\\Error' => '/phpunit/Framework/Exception/Error.php', - 'PHPUnit\\Framework\\ErrorTestCase' => '/phpunit/Framework/ErrorTestCase.php', - 'PHPUnit\\Framework\\Error\\Deprecated' => '/phpunit/Framework/Error/Deprecated.php', - 'PHPUnit\\Framework\\Error\\Error' => '/phpunit/Framework/Error/Error.php', - 'PHPUnit\\Framework\\Error\\Notice' => '/phpunit/Framework/Error/Notice.php', - 'PHPUnit\\Framework\\Error\\Warning' => '/phpunit/Framework/Error/Warning.php', + 'PHPUnit\\Framework\\EmptyStringException' => '/phpunit/Framework/Exception/EmptyStringException.php', 'PHPUnit\\Framework\\Exception' => '/phpunit/Framework/Exception/Exception.php', - 'PHPUnit\\Framework\\ExceptionWrapper' => '/phpunit/Framework/ExceptionWrapper.php', 'PHPUnit\\Framework\\ExecutionOrderDependency' => '/phpunit/Framework/ExecutionOrderDependency.php', 'PHPUnit\\Framework\\ExpectationFailedException' => '/phpunit/Framework/Exception/ExpectationFailedException.php', - 'PHPUnit\\Framework\\IncompleteTest' => '/phpunit/Framework/IncompleteTest.php', - 'PHPUnit\\Framework\\IncompleteTestCase' => '/phpunit/Framework/IncompleteTestCase.php', - 'PHPUnit\\Framework\\IncompleteTestError' => '/phpunit/Framework/Exception/IncompleteTestError.php', + 'PHPUnit\\Framework\\GeneratorNotSupportedException' => '/phpunit/Framework/Exception/GeneratorNotSupportedException.php', + 'PHPUnit\\Framework\\IncompleteTest' => '/phpunit/Framework/Exception/Incomplete/IncompleteTest.php', + 'PHPUnit\\Framework\\IncompleteTestError' => '/phpunit/Framework/Exception/Incomplete/IncompleteTestError.php', 'PHPUnit\\Framework\\InvalidArgumentException' => '/phpunit/Framework/Exception/InvalidArgumentException.php', 'PHPUnit\\Framework\\InvalidCoversTargetException' => '/phpunit/Framework/Exception/InvalidCoversTargetException.php', 'PHPUnit\\Framework\\InvalidDataProviderException' => '/phpunit/Framework/Exception/InvalidDataProviderException.php', - 'PHPUnit\\Framework\\InvalidParameterGroupException' => '/phpunit/Framework/InvalidParameterGroupException.php', - 'PHPUnit\\Framework\\MissingCoversAnnotationException' => '/phpunit/Framework/Exception/MissingCoversAnnotationException.php', - 'PHPUnit\\Framework\\MockObject\\Api' => '/phpunit/Framework/MockObject/Api/Api.php', + 'PHPUnit\\Framework\\InvalidDependencyException' => '/phpunit/Framework/Exception/InvalidDependencyException.php', 'PHPUnit\\Framework\\MockObject\\BadMethodCallException' => '/phpunit/Framework/MockObject/Exception/BadMethodCallException.php', - 'PHPUnit\\Framework\\MockObject\\Builder\\Identity' => '/phpunit/Framework/MockObject/Builder/Identity.php', - 'PHPUnit\\Framework\\MockObject\\Builder\\InvocationMocker' => '/phpunit/Framework/MockObject/Builder/InvocationMocker.php', - 'PHPUnit\\Framework\\MockObject\\Builder\\InvocationStubber' => '/phpunit/Framework/MockObject/Builder/InvocationStubber.php', - 'PHPUnit\\Framework\\MockObject\\Builder\\MethodNameMatch' => '/phpunit/Framework/MockObject/Builder/MethodNameMatch.php', - 'PHPUnit\\Framework\\MockObject\\Builder\\ParametersMatch' => '/phpunit/Framework/MockObject/Builder/ParametersMatch.php', - 'PHPUnit\\Framework\\MockObject\\Builder\\Stub' => '/phpunit/Framework/MockObject/Builder/Stub.php', - 'PHPUnit\\Framework\\MockObject\\CannotUseAddMethodsException' => '/phpunit/Framework/MockObject/Exception/CannotUseAddMethodsException.php', + 'PHPUnit\\Framework\\MockObject\\Builder\\Identity' => '/phpunit/Framework/MockObject/Runtime/Builder/Identity.php', + 'PHPUnit\\Framework\\MockObject\\Builder\\InvocationMocker' => '/phpunit/Framework/MockObject/Runtime/Builder/InvocationMocker.php', + 'PHPUnit\\Framework\\MockObject\\Builder\\InvocationStubber' => '/phpunit/Framework/MockObject/Runtime/Builder/InvocationStubber.php', + 'PHPUnit\\Framework\\MockObject\\Builder\\MethodNameMatch' => '/phpunit/Framework/MockObject/Runtime/Builder/MethodNameMatch.php', + 'PHPUnit\\Framework\\MockObject\\Builder\\ParametersMatch' => '/phpunit/Framework/MockObject/Runtime/Builder/ParametersMatch.php', + 'PHPUnit\\Framework\\MockObject\\Builder\\Stub' => '/phpunit/Framework/MockObject/Runtime/Builder/Stub.php', 'PHPUnit\\Framework\\MockObject\\CannotUseOnlyMethodsException' => '/phpunit/Framework/MockObject/Exception/CannotUseOnlyMethodsException.php', - 'PHPUnit\\Framework\\MockObject\\ClassAlreadyExistsException' => '/phpunit/Framework/MockObject/Exception/ClassAlreadyExistsException.php', - 'PHPUnit\\Framework\\MockObject\\ClassIsFinalException' => '/phpunit/Framework/MockObject/Exception/ClassIsFinalException.php', - 'PHPUnit\\Framework\\MockObject\\ClassIsReadonlyException' => '/phpunit/Framework/MockObject/Exception/ClassIsReadonlyException.php', 'PHPUnit\\Framework\\MockObject\\ConfigurableMethod' => '/phpunit/Framework/MockObject/ConfigurableMethod.php', - 'PHPUnit\\Framework\\MockObject\\ConfigurableMethodsAlreadyInitializedException' => '/phpunit/Framework/MockObject/Exception/ConfigurableMethodsAlreadyInitializedException.php', - 'PHPUnit\\Framework\\MockObject\\DuplicateMethodException' => '/phpunit/Framework/MockObject/Exception/DuplicateMethodException.php', + 'PHPUnit\\Framework\\MockObject\\DoubledCloneMethod' => '/phpunit/Framework/MockObject/Runtime/Api/DoubledCloneMethod.php', 'PHPUnit\\Framework\\MockObject\\Exception' => '/phpunit/Framework/MockObject/Exception/Exception.php', - 'PHPUnit\\Framework\\MockObject\\Generator' => '/phpunit/Framework/MockObject/Generator.php', + 'PHPUnit\\Framework\\MockObject\\Generator\\CannotUseAddMethodsException' => '/phpunit/Framework/MockObject/Generator/Exception/CannotUseAddMethodsException.php', + 'PHPUnit\\Framework\\MockObject\\Generator\\ClassIsEnumerationException' => '/phpunit/Framework/MockObject/Generator/Exception/ClassIsEnumerationException.php', + 'PHPUnit\\Framework\\MockObject\\Generator\\ClassIsFinalException' => '/phpunit/Framework/MockObject/Generator/Exception/ClassIsFinalException.php', + 'PHPUnit\\Framework\\MockObject\\Generator\\ClassIsReadonlyException' => '/phpunit/Framework/MockObject/Generator/Exception/ClassIsReadonlyException.php', + 'PHPUnit\\Framework\\MockObject\\Generator\\DuplicateMethodException' => '/phpunit/Framework/MockObject/Generator/Exception/DuplicateMethodException.php', + 'PHPUnit\\Framework\\MockObject\\Generator\\Exception' => '/phpunit/Framework/MockObject/Generator/Exception/Exception.php', + 'PHPUnit\\Framework\\MockObject\\Generator\\Generator' => '/phpunit/Framework/MockObject/Generator/Generator.php', + 'PHPUnit\\Framework\\MockObject\\Generator\\InvalidMethodNameException' => '/phpunit/Framework/MockObject/Generator/Exception/InvalidMethodNameException.php', + 'PHPUnit\\Framework\\MockObject\\Generator\\MockClass' => '/phpunit/Framework/MockObject/Generator/MockClass.php', + 'PHPUnit\\Framework\\MockObject\\Generator\\MockMethod' => '/phpunit/Framework/MockObject/Generator/MockMethod.php', + 'PHPUnit\\Framework\\MockObject\\Generator\\MockMethodSet' => '/phpunit/Framework/MockObject/Generator/MockMethodSet.php', + 'PHPUnit\\Framework\\MockObject\\Generator\\MockTrait' => '/phpunit/Framework/MockObject/Generator/MockTrait.php', + 'PHPUnit\\Framework\\MockObject\\Generator\\MockType' => '/phpunit/Framework/MockObject/Generator/MockType.php', + 'PHPUnit\\Framework\\MockObject\\Generator\\NameAlreadyInUseException' => '/phpunit/Framework/MockObject/Generator/Exception/NameAlreadyInUseException.php', + 'PHPUnit\\Framework\\MockObject\\Generator\\OriginalConstructorInvocationRequiredException' => '/phpunit/Framework/MockObject/Generator/Exception/OriginalConstructorInvocationRequiredException.php', + 'PHPUnit\\Framework\\MockObject\\Generator\\ReflectionException' => '/phpunit/Framework/MockObject/Generator/Exception/ReflectionException.php', + 'PHPUnit\\Framework\\MockObject\\Generator\\RuntimeException' => '/phpunit/Framework/MockObject/Generator/Exception/RuntimeException.php', + 'PHPUnit\\Framework\\MockObject\\Generator\\SoapExtensionNotAvailableException' => '/phpunit/Framework/MockObject/Generator/Exception/SoapExtensionNotAvailableException.php', + 'PHPUnit\\Framework\\MockObject\\Generator\\TemplateLoader' => '/phpunit/Framework/MockObject/Generator/TemplateLoader.php', + 'PHPUnit\\Framework\\MockObject\\Generator\\UnknownClassException' => '/phpunit/Framework/MockObject/Generator/Exception/UnknownClassException.php', + 'PHPUnit\\Framework\\MockObject\\Generator\\UnknownTraitException' => '/phpunit/Framework/MockObject/Generator/Exception/UnknownTraitException.php', + 'PHPUnit\\Framework\\MockObject\\Generator\\UnknownTypeException' => '/phpunit/Framework/MockObject/Generator/Exception/UnknownTypeException.php', 'PHPUnit\\Framework\\MockObject\\IncompatibleReturnValueException' => '/phpunit/Framework/MockObject/Exception/IncompatibleReturnValueException.php', - 'PHPUnit\\Framework\\MockObject\\InvalidMethodNameException' => '/phpunit/Framework/MockObject/Exception/InvalidMethodNameException.php', - 'PHPUnit\\Framework\\MockObject\\Invocation' => '/phpunit/Framework/MockObject/Invocation.php', - 'PHPUnit\\Framework\\MockObject\\InvocationHandler' => '/phpunit/Framework/MockObject/InvocationHandler.php', + 'PHPUnit\\Framework\\MockObject\\Invocation' => '/phpunit/Framework/MockObject/Runtime/Invocation.php', + 'PHPUnit\\Framework\\MockObject\\InvocationHandler' => '/phpunit/Framework/MockObject/Runtime/InvocationHandler.php', 'PHPUnit\\Framework\\MockObject\\MatchBuilderNotFoundException' => '/phpunit/Framework/MockObject/Exception/MatchBuilderNotFoundException.php', - 'PHPUnit\\Framework\\MockObject\\Matcher' => '/phpunit/Framework/MockObject/Matcher.php', + 'PHPUnit\\Framework\\MockObject\\Matcher' => '/phpunit/Framework/MockObject/Runtime/Matcher.php', 'PHPUnit\\Framework\\MockObject\\MatcherAlreadyRegisteredException' => '/phpunit/Framework/MockObject/Exception/MatcherAlreadyRegisteredException.php', - 'PHPUnit\\Framework\\MockObject\\Method' => '/phpunit/Framework/MockObject/Api/Method.php', + 'PHPUnit\\Framework\\MockObject\\Method' => '/phpunit/Framework/MockObject/Runtime/Api/Method.php', 'PHPUnit\\Framework\\MockObject\\MethodCannotBeConfiguredException' => '/phpunit/Framework/MockObject/Exception/MethodCannotBeConfiguredException.php', 'PHPUnit\\Framework\\MockObject\\MethodNameAlreadyConfiguredException' => '/phpunit/Framework/MockObject/Exception/MethodNameAlreadyConfiguredException.php', - 'PHPUnit\\Framework\\MockObject\\MethodNameConstraint' => '/phpunit/Framework/MockObject/MethodNameConstraint.php', + 'PHPUnit\\Framework\\MockObject\\MethodNameConstraint' => '/phpunit/Framework/MockObject/Runtime/MethodNameConstraint.php', 'PHPUnit\\Framework\\MockObject\\MethodNameNotConfiguredException' => '/phpunit/Framework/MockObject/Exception/MethodNameNotConfiguredException.php', 'PHPUnit\\Framework\\MockObject\\MethodParametersAlreadyConfiguredException' => '/phpunit/Framework/MockObject/Exception/MethodParametersAlreadyConfiguredException.php', 'PHPUnit\\Framework\\MockObject\\MockBuilder' => '/phpunit/Framework/MockObject/MockBuilder.php', - 'PHPUnit\\Framework\\MockObject\\MockClass' => '/phpunit/Framework/MockObject/MockClass.php', - 'PHPUnit\\Framework\\MockObject\\MockMethod' => '/phpunit/Framework/MockObject/MockMethod.php', - 'PHPUnit\\Framework\\MockObject\\MockMethodSet' => '/phpunit/Framework/MockObject/MockMethodSet.php', - 'PHPUnit\\Framework\\MockObject\\MockObject' => '/phpunit/Framework/MockObject/MockObject.php', - 'PHPUnit\\Framework\\MockObject\\MockTrait' => '/phpunit/Framework/MockObject/MockTrait.php', - 'PHPUnit\\Framework\\MockObject\\MockType' => '/phpunit/Framework/MockObject/MockType.php', - 'PHPUnit\\Framework\\MockObject\\OriginalConstructorInvocationRequiredException' => '/phpunit/Framework/MockObject/Exception/OriginalConstructorInvocationRequiredException.php', + 'PHPUnit\\Framework\\MockObject\\MockObject' => '/phpunit/Framework/MockObject/Runtime/Interface/MockObject.php', + 'PHPUnit\\Framework\\MockObject\\MockObjectApi' => '/phpunit/Framework/MockObject/Runtime/Api/MockObjectApi.php', + 'PHPUnit\\Framework\\MockObject\\MockObjectInternal' => '/phpunit/Framework/MockObject/Runtime/Interface/MockObjectInternal.php', + 'PHPUnit\\Framework\\MockObject\\NeverReturningMethodException' => '/phpunit/Framework/MockObject/Exception/NeverReturningMethodException.php', + 'PHPUnit\\Framework\\MockObject\\ProxiedCloneMethod' => '/phpunit/Framework/MockObject/Runtime/Api/ProxiedCloneMethod.php', 'PHPUnit\\Framework\\MockObject\\ReflectionException' => '/phpunit/Framework/MockObject/Exception/ReflectionException.php', + 'PHPUnit\\Framework\\MockObject\\ReturnValueGenerator' => '/phpunit/Framework/MockObject/Runtime/ReturnValueGenerator.php', 'PHPUnit\\Framework\\MockObject\\ReturnValueNotConfiguredException' => '/phpunit/Framework/MockObject/Exception/ReturnValueNotConfiguredException.php', - 'PHPUnit\\Framework\\MockObject\\Rule\\AnyInvokedCount' => '/phpunit/Framework/MockObject/Rule/AnyInvokedCount.php', - 'PHPUnit\\Framework\\MockObject\\Rule\\AnyParameters' => '/phpunit/Framework/MockObject/Rule/AnyParameters.php', - 'PHPUnit\\Framework\\MockObject\\Rule\\ConsecutiveParameters' => '/phpunit/Framework/MockObject/Rule/ConsecutiveParameters.php', - 'PHPUnit\\Framework\\MockObject\\Rule\\InvocationOrder' => '/phpunit/Framework/MockObject/Rule/InvocationOrder.php', - 'PHPUnit\\Framework\\MockObject\\Rule\\InvokedAtIndex' => '/phpunit/Framework/MockObject/Rule/InvokedAtIndex.php', - 'PHPUnit\\Framework\\MockObject\\Rule\\InvokedAtLeastCount' => '/phpunit/Framework/MockObject/Rule/InvokedAtLeastCount.php', - 'PHPUnit\\Framework\\MockObject\\Rule\\InvokedAtLeastOnce' => '/phpunit/Framework/MockObject/Rule/InvokedAtLeastOnce.php', - 'PHPUnit\\Framework\\MockObject\\Rule\\InvokedAtMostCount' => '/phpunit/Framework/MockObject/Rule/InvokedAtMostCount.php', - 'PHPUnit\\Framework\\MockObject\\Rule\\InvokedCount' => '/phpunit/Framework/MockObject/Rule/InvokedCount.php', - 'PHPUnit\\Framework\\MockObject\\Rule\\MethodName' => '/phpunit/Framework/MockObject/Rule/MethodName.php', - 'PHPUnit\\Framework\\MockObject\\Rule\\Parameters' => '/phpunit/Framework/MockObject/Rule/Parameters.php', - 'PHPUnit\\Framework\\MockObject\\Rule\\ParametersRule' => '/phpunit/Framework/MockObject/Rule/ParametersRule.php', + 'PHPUnit\\Framework\\MockObject\\Rule\\AnyInvokedCount' => '/phpunit/Framework/MockObject/Runtime/Rule/AnyInvokedCount.php', + 'PHPUnit\\Framework\\MockObject\\Rule\\AnyParameters' => '/phpunit/Framework/MockObject/Runtime/Rule/AnyParameters.php', + 'PHPUnit\\Framework\\MockObject\\Rule\\InvocationOrder' => '/phpunit/Framework/MockObject/Runtime/Rule/InvocationOrder.php', + 'PHPUnit\\Framework\\MockObject\\Rule\\InvokedAtLeastCount' => '/phpunit/Framework/MockObject/Runtime/Rule/InvokedAtLeastCount.php', + 'PHPUnit\\Framework\\MockObject\\Rule\\InvokedAtLeastOnce' => '/phpunit/Framework/MockObject/Runtime/Rule/InvokedAtLeastOnce.php', + 'PHPUnit\\Framework\\MockObject\\Rule\\InvokedAtMostCount' => '/phpunit/Framework/MockObject/Runtime/Rule/InvokedAtMostCount.php', + 'PHPUnit\\Framework\\MockObject\\Rule\\InvokedCount' => '/phpunit/Framework/MockObject/Runtime/Rule/InvokedCount.php', + 'PHPUnit\\Framework\\MockObject\\Rule\\MethodName' => '/phpunit/Framework/MockObject/Runtime/Rule/MethodName.php', + 'PHPUnit\\Framework\\MockObject\\Rule\\Parameters' => '/phpunit/Framework/MockObject/Runtime/Rule/Parameters.php', + 'PHPUnit\\Framework\\MockObject\\Rule\\ParametersRule' => '/phpunit/Framework/MockObject/Runtime/Rule/ParametersRule.php', 'PHPUnit\\Framework\\MockObject\\RuntimeException' => '/phpunit/Framework/MockObject/Exception/RuntimeException.php', - 'PHPUnit\\Framework\\MockObject\\SoapExtensionNotAvailableException' => '/phpunit/Framework/MockObject/Exception/SoapExtensionNotAvailableException.php', - 'PHPUnit\\Framework\\MockObject\\Stub' => '/phpunit/Framework/MockObject/Stub.php', - 'PHPUnit\\Framework\\MockObject\\Stub\\ConsecutiveCalls' => '/phpunit/Framework/MockObject/Stub/ConsecutiveCalls.php', - 'PHPUnit\\Framework\\MockObject\\Stub\\Exception' => '/phpunit/Framework/MockObject/Stub/Exception.php', - 'PHPUnit\\Framework\\MockObject\\Stub\\ReturnArgument' => '/phpunit/Framework/MockObject/Stub/ReturnArgument.php', - 'PHPUnit\\Framework\\MockObject\\Stub\\ReturnCallback' => '/phpunit/Framework/MockObject/Stub/ReturnCallback.php', - 'PHPUnit\\Framework\\MockObject\\Stub\\ReturnReference' => '/phpunit/Framework/MockObject/Stub/ReturnReference.php', - 'PHPUnit\\Framework\\MockObject\\Stub\\ReturnSelf' => '/phpunit/Framework/MockObject/Stub/ReturnSelf.php', - 'PHPUnit\\Framework\\MockObject\\Stub\\ReturnStub' => '/phpunit/Framework/MockObject/Stub/ReturnStub.php', - 'PHPUnit\\Framework\\MockObject\\Stub\\ReturnValueMap' => '/phpunit/Framework/MockObject/Stub/ReturnValueMap.php', - 'PHPUnit\\Framework\\MockObject\\Stub\\Stub' => '/phpunit/Framework/MockObject/Stub/Stub.php', - 'PHPUnit\\Framework\\MockObject\\UnknownClassException' => '/phpunit/Framework/MockObject/Exception/UnknownClassException.php', - 'PHPUnit\\Framework\\MockObject\\UnknownTraitException' => '/phpunit/Framework/MockObject/Exception/UnknownTraitException.php', - 'PHPUnit\\Framework\\MockObject\\UnknownTypeException' => '/phpunit/Framework/MockObject/Exception/UnknownTypeException.php', - 'PHPUnit\\Framework\\MockObject\\Verifiable' => '/phpunit/Framework/MockObject/Verifiable.php', + 'PHPUnit\\Framework\\MockObject\\Stub' => '/phpunit/Framework/MockObject/Runtime/Interface/Stub.php', + 'PHPUnit\\Framework\\MockObject\\StubApi' => '/phpunit/Framework/MockObject/Runtime/Api/StubApi.php', + 'PHPUnit\\Framework\\MockObject\\StubInternal' => '/phpunit/Framework/MockObject/Runtime/Interface/StubInternal.php', + 'PHPUnit\\Framework\\MockObject\\Stub\\ConsecutiveCalls' => '/phpunit/Framework/MockObject/Runtime/Stub/ConsecutiveCalls.php', + 'PHPUnit\\Framework\\MockObject\\Stub\\Exception' => '/phpunit/Framework/MockObject/Runtime/Stub/Exception.php', + 'PHPUnit\\Framework\\MockObject\\Stub\\ReturnArgument' => '/phpunit/Framework/MockObject/Runtime/Stub/ReturnArgument.php', + 'PHPUnit\\Framework\\MockObject\\Stub\\ReturnCallback' => '/phpunit/Framework/MockObject/Runtime/Stub/ReturnCallback.php', + 'PHPUnit\\Framework\\MockObject\\Stub\\ReturnReference' => '/phpunit/Framework/MockObject/Runtime/Stub/ReturnReference.php', + 'PHPUnit\\Framework\\MockObject\\Stub\\ReturnSelf' => '/phpunit/Framework/MockObject/Runtime/Stub/ReturnSelf.php', + 'PHPUnit\\Framework\\MockObject\\Stub\\ReturnStub' => '/phpunit/Framework/MockObject/Runtime/Stub/ReturnStub.php', + 'PHPUnit\\Framework\\MockObject\\Stub\\ReturnValueMap' => '/phpunit/Framework/MockObject/Runtime/Stub/ReturnValueMap.php', + 'PHPUnit\\Framework\\MockObject\\Stub\\Stub' => '/phpunit/Framework/MockObject/Runtime/Stub/Stub.php', 'PHPUnit\\Framework\\NoChildTestSuiteException' => '/phpunit/Framework/Exception/NoChildTestSuiteException.php', - 'PHPUnit\\Framework\\OutputError' => '/phpunit/Framework/Exception/OutputError.php', - 'PHPUnit\\Framework\\PHPTAssertionFailedError' => '/phpunit/Framework/Exception/PHPTAssertionFailedError.php', + 'PHPUnit\\Framework\\PhptAssertionFailedError' => '/phpunit/Framework/Exception/PhptAssertionFailedError.php', + 'PHPUnit\\Framework\\ProcessIsolationException' => '/phpunit/Framework/Exception/ProcessIsolationException.php', 'PHPUnit\\Framework\\Reorderable' => '/phpunit/Framework/Reorderable.php', - 'PHPUnit\\Framework\\RiskyTestError' => '/phpunit/Framework/Exception/RiskyTestError.php', 'PHPUnit\\Framework\\SelfDescribing' => '/phpunit/Framework/SelfDescribing.php', - 'PHPUnit\\Framework\\SkippedTest' => '/phpunit/Framework/SkippedTest.php', - 'PHPUnit\\Framework\\SkippedTestCase' => '/phpunit/Framework/SkippedTestCase.php', - 'PHPUnit\\Framework\\SkippedTestError' => '/phpunit/Framework/Exception/SkippedTestError.php', - 'PHPUnit\\Framework\\SkippedTestSuiteError' => '/phpunit/Framework/Exception/SkippedTestSuiteError.php', - 'PHPUnit\\Framework\\SyntheticError' => '/phpunit/Framework/Exception/SyntheticError.php', - 'PHPUnit\\Framework\\SyntheticSkippedError' => '/phpunit/Framework/Exception/SyntheticSkippedError.php', + 'PHPUnit\\Framework\\SkippedTest' => '/phpunit/Framework/Exception/Skipped/SkippedTest.php', + 'PHPUnit\\Framework\\SkippedTestSuiteError' => '/phpunit/Framework/Exception/Skipped/SkippedTestSuiteError.php', + 'PHPUnit\\Framework\\SkippedWithMessageException' => '/phpunit/Framework/Exception/Skipped/SkippedWithMessageException.php', 'PHPUnit\\Framework\\Test' => '/phpunit/Framework/Test.php', 'PHPUnit\\Framework\\TestBuilder' => '/phpunit/Framework/TestBuilder.php', 'PHPUnit\\Framework\\TestCase' => '/phpunit/Framework/TestCase.php', - 'PHPUnit\\Framework\\TestFailure' => '/phpunit/Framework/TestFailure.php', - 'PHPUnit\\Framework\\TestListener' => '/phpunit/Framework/TestListener.php', - 'PHPUnit\\Framework\\TestListenerDefaultImplementation' => '/phpunit/Framework/TestListenerDefaultImplementation.php', - 'PHPUnit\\Framework\\TestResult' => '/phpunit/Framework/TestResult.php', + 'PHPUnit\\Framework\\TestRunner' => '/phpunit/Framework/TestRunner.php', + 'PHPUnit\\Framework\\TestSize\\Known' => '/phpunit/Framework/TestSize/Known.php', + 'PHPUnit\\Framework\\TestSize\\Large' => '/phpunit/Framework/TestSize/Large.php', + 'PHPUnit\\Framework\\TestSize\\Medium' => '/phpunit/Framework/TestSize/Medium.php', + 'PHPUnit\\Framework\\TestSize\\Small' => '/phpunit/Framework/TestSize/Small.php', + 'PHPUnit\\Framework\\TestSize\\TestSize' => '/phpunit/Framework/TestSize/TestSize.php', + 'PHPUnit\\Framework\\TestSize\\Unknown' => '/phpunit/Framework/TestSize/Unknown.php', + 'PHPUnit\\Framework\\TestStatus\\Deprecation' => '/phpunit/Framework/TestStatus/Deprecation.php', + 'PHPUnit\\Framework\\TestStatus\\Error' => '/phpunit/Framework/TestStatus/Error.php', + 'PHPUnit\\Framework\\TestStatus\\Failure' => '/phpunit/Framework/TestStatus/Failure.php', + 'PHPUnit\\Framework\\TestStatus\\Incomplete' => '/phpunit/Framework/TestStatus/Incomplete.php', + 'PHPUnit\\Framework\\TestStatus\\Known' => '/phpunit/Framework/TestStatus/Known.php', + 'PHPUnit\\Framework\\TestStatus\\Notice' => '/phpunit/Framework/TestStatus/Notice.php', + 'PHPUnit\\Framework\\TestStatus\\Risky' => '/phpunit/Framework/TestStatus/Risky.php', + 'PHPUnit\\Framework\\TestStatus\\Skipped' => '/phpunit/Framework/TestStatus/Skipped.php', + 'PHPUnit\\Framework\\TestStatus\\Success' => '/phpunit/Framework/TestStatus/Success.php', + 'PHPUnit\\Framework\\TestStatus\\TestStatus' => '/phpunit/Framework/TestStatus/TestStatus.php', + 'PHPUnit\\Framework\\TestStatus\\Unknown' => '/phpunit/Framework/TestStatus/Unknown.php', + 'PHPUnit\\Framework\\TestStatus\\Warning' => '/phpunit/Framework/TestStatus/Warning.php', 'PHPUnit\\Framework\\TestSuite' => '/phpunit/Framework/TestSuite.php', 'PHPUnit\\Framework\\TestSuiteIterator' => '/phpunit/Framework/TestSuiteIterator.php', - 'PHPUnit\\Framework\\UnintentionallyCoveredCodeError' => '/phpunit/Framework/Exception/UnintentionallyCoveredCodeError.php', - 'PHPUnit\\Framework\\Warning' => '/phpunit/Framework/Exception/Warning.php', - 'PHPUnit\\Framework\\WarningTestCase' => '/phpunit/Framework/WarningTestCase.php', - 'PHPUnit\\Runner\\AfterIncompleteTestHook' => '/phpunit/Runner/Hook/AfterIncompleteTestHook.php', - 'PHPUnit\\Runner\\AfterLastTestHook' => '/phpunit/Runner/Hook/AfterLastTestHook.php', - 'PHPUnit\\Runner\\AfterRiskyTestHook' => '/phpunit/Runner/Hook/AfterRiskyTestHook.php', - 'PHPUnit\\Runner\\AfterSkippedTestHook' => '/phpunit/Runner/Hook/AfterSkippedTestHook.php', - 'PHPUnit\\Runner\\AfterSuccessfulTestHook' => '/phpunit/Runner/Hook/AfterSuccessfulTestHook.php', - 'PHPUnit\\Runner\\AfterTestErrorHook' => '/phpunit/Runner/Hook/AfterTestErrorHook.php', - 'PHPUnit\\Runner\\AfterTestFailureHook' => '/phpunit/Runner/Hook/AfterTestFailureHook.php', - 'PHPUnit\\Runner\\AfterTestHook' => '/phpunit/Runner/Hook/AfterTestHook.php', - 'PHPUnit\\Runner\\AfterTestWarningHook' => '/phpunit/Runner/Hook/AfterTestWarningHook.php', - 'PHPUnit\\Runner\\BaseTestRunner' => '/phpunit/Runner/BaseTestRunner.php', - 'PHPUnit\\Runner\\BeforeFirstTestHook' => '/phpunit/Runner/Hook/BeforeFirstTestHook.php', - 'PHPUnit\\Runner\\BeforeTestHook' => '/phpunit/Runner/Hook/BeforeTestHook.php', - 'PHPUnit\\Runner\\DefaultTestResultCache' => '/phpunit/Runner/DefaultTestResultCache.php', - 'PHPUnit\\Runner\\Exception' => '/phpunit/Runner/Exception.php', - 'PHPUnit\\Runner\\Extension\\ExtensionHandler' => '/phpunit/Runner/Extension/ExtensionHandler.php', + 'PHPUnit\\Framework\\UnknownClassOrInterfaceException' => '/phpunit/Framework/Exception/UnknownClassOrInterfaceException.php', + 'PHPUnit\\Framework\\UnknownTypeException' => '/phpunit/Framework/Exception/UnknownTypeException.php', + 'PHPUnit\\Logging\\EventLogger' => '/phpunit/Logging/EventLogger.php', + 'PHPUnit\\Logging\\Exception' => '/phpunit/Logging/Exception.php', + 'PHPUnit\\Logging\\JUnit\\JunitXmlLogger' => '/phpunit/Logging/JUnit/JunitXmlLogger.php', + 'PHPUnit\\Logging\\JUnit\\Subscriber' => '/phpunit/Logging/JUnit/Subscriber/Subscriber.php', + 'PHPUnit\\Logging\\JUnit\\TestErroredSubscriber' => '/phpunit/Logging/JUnit/Subscriber/TestErroredSubscriber.php', + 'PHPUnit\\Logging\\JUnit\\TestFailedSubscriber' => '/phpunit/Logging/JUnit/Subscriber/TestFailedSubscriber.php', + 'PHPUnit\\Logging\\JUnit\\TestFinishedSubscriber' => '/phpunit/Logging/JUnit/Subscriber/TestFinishedSubscriber.php', + 'PHPUnit\\Logging\\JUnit\\TestMarkedIncompleteSubscriber' => '/phpunit/Logging/JUnit/Subscriber/TestMarkedIncompleteSubscriber.php', + 'PHPUnit\\Logging\\JUnit\\TestPreparationFailedSubscriber' => '/phpunit/Logging/JUnit/Subscriber/TestPreparationFailedSubscriber.php', + 'PHPUnit\\Logging\\JUnit\\TestPreparationStartedSubscriber' => '/phpunit/Logging/JUnit/Subscriber/TestPreparationStartedSubscriber.php', + 'PHPUnit\\Logging\\JUnit\\TestPreparedSubscriber' => '/phpunit/Logging/JUnit/Subscriber/TestPreparedSubscriber.php', + 'PHPUnit\\Logging\\JUnit\\TestRunnerExecutionFinishedSubscriber' => '/phpunit/Logging/JUnit/Subscriber/TestRunnerExecutionFinishedSubscriber.php', + 'PHPUnit\\Logging\\JUnit\\TestSkippedSubscriber' => '/phpunit/Logging/JUnit/Subscriber/TestSkippedSubscriber.php', + 'PHPUnit\\Logging\\JUnit\\TestSuiteFinishedSubscriber' => '/phpunit/Logging/JUnit/Subscriber/TestSuiteFinishedSubscriber.php', + 'PHPUnit\\Logging\\JUnit\\TestSuiteStartedSubscriber' => '/phpunit/Logging/JUnit/Subscriber/TestSuiteStartedSubscriber.php', + 'PHPUnit\\Logging\\TeamCity\\Subscriber' => '/phpunit/Logging/TeamCity/Subscriber/Subscriber.php', + 'PHPUnit\\Logging\\TeamCity\\TeamCityLogger' => '/phpunit/Logging/TeamCity/TeamCityLogger.php', + 'PHPUnit\\Logging\\TeamCity\\TestConsideredRiskySubscriber' => '/phpunit/Logging/TeamCity/Subscriber/TestConsideredRiskySubscriber.php', + 'PHPUnit\\Logging\\TeamCity\\TestErroredSubscriber' => '/phpunit/Logging/TeamCity/Subscriber/TestErroredSubscriber.php', + 'PHPUnit\\Logging\\TeamCity\\TestFailedSubscriber' => '/phpunit/Logging/TeamCity/Subscriber/TestFailedSubscriber.php', + 'PHPUnit\\Logging\\TeamCity\\TestFinishedSubscriber' => '/phpunit/Logging/TeamCity/Subscriber/TestFinishedSubscriber.php', + 'PHPUnit\\Logging\\TeamCity\\TestMarkedIncompleteSubscriber' => '/phpunit/Logging/TeamCity/Subscriber/TestMarkedIncompleteSubscriber.php', + 'PHPUnit\\Logging\\TeamCity\\TestPreparedSubscriber' => '/phpunit/Logging/TeamCity/Subscriber/TestPreparedSubscriber.php', + 'PHPUnit\\Logging\\TeamCity\\TestRunnerExecutionFinishedSubscriber' => '/phpunit/Logging/TeamCity/Subscriber/TestRunnerExecutionFinishedSubscriber.php', + 'PHPUnit\\Logging\\TeamCity\\TestSkippedSubscriber' => '/phpunit/Logging/TeamCity/Subscriber/TestSkippedSubscriber.php', + 'PHPUnit\\Logging\\TeamCity\\TestSuiteFinishedSubscriber' => '/phpunit/Logging/TeamCity/Subscriber/TestSuiteFinishedSubscriber.php', + 'PHPUnit\\Logging\\TeamCity\\TestSuiteStartedSubscriber' => '/phpunit/Logging/TeamCity/Subscriber/TestSuiteStartedSubscriber.php', + 'PHPUnit\\Logging\\TestDox\\HtmlRenderer' => '/phpunit/Logging/TestDox/HtmlRenderer.php', + 'PHPUnit\\Logging\\TestDox\\NamePrettifier' => '/phpunit/Logging/TestDox/NamePrettifier.php', + 'PHPUnit\\Logging\\TestDox\\PlainTextRenderer' => '/phpunit/Logging/TestDox/PlainTextRenderer.php', + 'PHPUnit\\Logging\\TestDox\\Subscriber' => '/phpunit/Logging/TestDox/TestResult/Subscriber/Subscriber.php', + 'PHPUnit\\Logging\\TestDox\\TestConsideredRiskySubscriber' => '/phpunit/Logging/TestDox/TestResult/Subscriber/TestConsideredRiskySubscriber.php', + 'PHPUnit\\Logging\\TestDox\\TestErroredSubscriber' => '/phpunit/Logging/TestDox/TestResult/Subscriber/TestErroredSubscriber.php', + 'PHPUnit\\Logging\\TestDox\\TestFailedSubscriber' => '/phpunit/Logging/TestDox/TestResult/Subscriber/TestFailedSubscriber.php', + 'PHPUnit\\Logging\\TestDox\\TestFinishedSubscriber' => '/phpunit/Logging/TestDox/TestResult/Subscriber/TestFinishedSubscriber.php', + 'PHPUnit\\Logging\\TestDox\\TestMarkedIncompleteSubscriber' => '/phpunit/Logging/TestDox/TestResult/Subscriber/TestMarkedIncompleteSubscriber.php', + 'PHPUnit\\Logging\\TestDox\\TestPassedSubscriber' => '/phpunit/Logging/TestDox/TestResult/Subscriber/TestPassedSubscriber.php', + 'PHPUnit\\Logging\\TestDox\\TestPreparedSubscriber' => '/phpunit/Logging/TestDox/TestResult/Subscriber/TestPreparedSubscriber.php', + 'PHPUnit\\Logging\\TestDox\\TestResult' => '/phpunit/Logging/TestDox/TestResult/TestResult.php', + 'PHPUnit\\Logging\\TestDox\\TestResultCollection' => '/phpunit/Logging/TestDox/TestResult/TestResultCollection.php', + 'PHPUnit\\Logging\\TestDox\\TestResultCollectionIterator' => '/phpunit/Logging/TestDox/TestResult/TestResultCollectionIterator.php', + 'PHPUnit\\Logging\\TestDox\\TestResultCollector' => '/phpunit/Logging/TestDox/TestResult/TestResultCollector.php', + 'PHPUnit\\Logging\\TestDox\\TestSkippedSubscriber' => '/phpunit/Logging/TestDox/TestResult/Subscriber/TestSkippedSubscriber.php', + 'PHPUnit\\Logging\\TestDox\\TestTriggeredDeprecationSubscriber' => '/phpunit/Logging/TestDox/TestResult/Subscriber/TestTriggeredDeprecationSubscriber.php', + 'PHPUnit\\Logging\\TestDox\\TestTriggeredNoticeSubscriber' => '/phpunit/Logging/TestDox/TestResult/Subscriber/TestTriggeredNoticeSubscriber.php', + 'PHPUnit\\Logging\\TestDox\\TestTriggeredPhpDeprecationSubscriber' => '/phpunit/Logging/TestDox/TestResult/Subscriber/TestTriggeredPhpDeprecationSubscriber.php', + 'PHPUnit\\Logging\\TestDox\\TestTriggeredPhpNoticeSubscriber' => '/phpunit/Logging/TestDox/TestResult/Subscriber/TestTriggeredPhpNoticeSubscriber.php', + 'PHPUnit\\Logging\\TestDox\\TestTriggeredPhpWarningSubscriber' => '/phpunit/Logging/TestDox/TestResult/Subscriber/TestTriggeredPhpWarningSubscriber.php', + 'PHPUnit\\Logging\\TestDox\\TestTriggeredPhpunitDeprecationSubscriber' => '/phpunit/Logging/TestDox/TestResult/Subscriber/TestTriggeredPhpunitDeprecationSubscriber.php', + 'PHPUnit\\Logging\\TestDox\\TestTriggeredPhpunitErrorSubscriber' => '/phpunit/Logging/TestDox/TestResult/Subscriber/TestTriggeredPhpunitErrorSubscriber.php', + 'PHPUnit\\Logging\\TestDox\\TestTriggeredPhpunitWarningSubscriber' => '/phpunit/Logging/TestDox/TestResult/Subscriber/TestTriggeredPhpunitWarningSubscriber.php', + 'PHPUnit\\Logging\\TestDox\\TestTriggeredWarningSubscriber' => '/phpunit/Logging/TestDox/TestResult/Subscriber/TestTriggeredWarningSubscriber.php', + 'PHPUnit\\Metadata\\After' => '/phpunit/Metadata/After.php', + 'PHPUnit\\Metadata\\AfterClass' => '/phpunit/Metadata/AfterClass.php', + 'PHPUnit\\Metadata\\Annotation\\Parser\\DocBlock' => '/phpunit/Metadata/Parser/Annotation/DocBlock.php', + 'PHPUnit\\Metadata\\Annotation\\Parser\\Registry' => '/phpunit/Metadata/Parser/Annotation/Registry.php', + 'PHPUnit\\Metadata\\AnnotationsAreNotSupportedForInternalClassesException' => '/phpunit/Metadata/Exception/AnnotationsAreNotSupportedForInternalClassesException.php', + 'PHPUnit\\Metadata\\Api\\CodeCoverage' => '/phpunit/Metadata/Api/CodeCoverage.php', + 'PHPUnit\\Metadata\\Api\\DataProvider' => '/phpunit/Metadata/Api/DataProvider.php', + 'PHPUnit\\Metadata\\Api\\Dependencies' => '/phpunit/Metadata/Api/Dependencies.php', + 'PHPUnit\\Metadata\\Api\\Groups' => '/phpunit/Metadata/Api/Groups.php', + 'PHPUnit\\Metadata\\Api\\HookMethods' => '/phpunit/Metadata/Api/HookMethods.php', + 'PHPUnit\\Metadata\\Api\\Requirements' => '/phpunit/Metadata/Api/Requirements.php', + 'PHPUnit\\Metadata\\BackupGlobals' => '/phpunit/Metadata/BackupGlobals.php', + 'PHPUnit\\Metadata\\BackupStaticProperties' => '/phpunit/Metadata/BackupStaticProperties.php', + 'PHPUnit\\Metadata\\Before' => '/phpunit/Metadata/Before.php', + 'PHPUnit\\Metadata\\BeforeClass' => '/phpunit/Metadata/BeforeClass.php', + 'PHPUnit\\Metadata\\Covers' => '/phpunit/Metadata/Covers.php', + 'PHPUnit\\Metadata\\CoversClass' => '/phpunit/Metadata/CoversClass.php', + 'PHPUnit\\Metadata\\CoversDefaultClass' => '/phpunit/Metadata/CoversDefaultClass.php', + 'PHPUnit\\Metadata\\CoversFunction' => '/phpunit/Metadata/CoversFunction.php', + 'PHPUnit\\Metadata\\CoversNothing' => '/phpunit/Metadata/CoversNothing.php', + 'PHPUnit\\Metadata\\DataProvider' => '/phpunit/Metadata/DataProvider.php', + 'PHPUnit\\Metadata\\DependsOnClass' => '/phpunit/Metadata/DependsOnClass.php', + 'PHPUnit\\Metadata\\DependsOnMethod' => '/phpunit/Metadata/DependsOnMethod.php', + 'PHPUnit\\Metadata\\DoesNotPerformAssertions' => '/phpunit/Metadata/DoesNotPerformAssertions.php', + 'PHPUnit\\Metadata\\Exception' => '/phpunit/Metadata/Exception/Exception.php', + 'PHPUnit\\Metadata\\ExcludeGlobalVariableFromBackup' => '/phpunit/Metadata/ExcludeGlobalVariableFromBackup.php', + 'PHPUnit\\Metadata\\ExcludeStaticPropertyFromBackup' => '/phpunit/Metadata/ExcludeStaticPropertyFromBackup.php', + 'PHPUnit\\Metadata\\Group' => '/phpunit/Metadata/Group.php', + 'PHPUnit\\Metadata\\IgnoreClassForCodeCoverage' => '/phpunit/Metadata/IgnoreClassForCodeCoverage.php', + 'PHPUnit\\Metadata\\IgnoreDeprecations' => '/phpunit/Metadata/IgnoreDeprecations.php', + 'PHPUnit\\Metadata\\IgnoreFunctionForCodeCoverage' => '/phpunit/Metadata/IgnoreFunctionForCodeCoverage.php', + 'PHPUnit\\Metadata\\IgnoreMethodForCodeCoverage' => '/phpunit/Metadata/IgnoreMethodForCodeCoverage.php', + 'PHPUnit\\Metadata\\InvalidVersionRequirementException' => '/phpunit/Metadata/Exception/InvalidVersionRequirementException.php', + 'PHPUnit\\Metadata\\Metadata' => '/phpunit/Metadata/Metadata.php', + 'PHPUnit\\Metadata\\MetadataCollection' => '/phpunit/Metadata/MetadataCollection.php', + 'PHPUnit\\Metadata\\MetadataCollectionIterator' => '/phpunit/Metadata/MetadataCollectionIterator.php', + 'PHPUnit\\Metadata\\NoVersionRequirementException' => '/phpunit/Metadata/Exception/NoVersionRequirementException.php', + 'PHPUnit\\Metadata\\Parser\\AnnotationParser' => '/phpunit/Metadata/Parser/AnnotationParser.php', + 'PHPUnit\\Metadata\\Parser\\AttributeParser' => '/phpunit/Metadata/Parser/AttributeParser.php', + 'PHPUnit\\Metadata\\Parser\\CachingParser' => '/phpunit/Metadata/Parser/CachingParser.php', + 'PHPUnit\\Metadata\\Parser\\Parser' => '/phpunit/Metadata/Parser/Parser.php', + 'PHPUnit\\Metadata\\Parser\\ParserChain' => '/phpunit/Metadata/Parser/ParserChain.php', + 'PHPUnit\\Metadata\\Parser\\Registry' => '/phpunit/Metadata/Parser/Registry.php', + 'PHPUnit\\Metadata\\PostCondition' => '/phpunit/Metadata/PostCondition.php', + 'PHPUnit\\Metadata\\PreCondition' => '/phpunit/Metadata/PreCondition.php', + 'PHPUnit\\Metadata\\PreserveGlobalState' => '/phpunit/Metadata/PreserveGlobalState.php', + 'PHPUnit\\Metadata\\ReflectionException' => '/phpunit/Metadata/Exception/ReflectionException.php', + 'PHPUnit\\Metadata\\RequiresFunction' => '/phpunit/Metadata/RequiresFunction.php', + 'PHPUnit\\Metadata\\RequiresMethod' => '/phpunit/Metadata/RequiresMethod.php', + 'PHPUnit\\Metadata\\RequiresOperatingSystem' => '/phpunit/Metadata/RequiresOperatingSystem.php', + 'PHPUnit\\Metadata\\RequiresOperatingSystemFamily' => '/phpunit/Metadata/RequiresOperatingSystemFamily.php', + 'PHPUnit\\Metadata\\RequiresPhp' => '/phpunit/Metadata/RequiresPhp.php', + 'PHPUnit\\Metadata\\RequiresPhpExtension' => '/phpunit/Metadata/RequiresPhpExtension.php', + 'PHPUnit\\Metadata\\RequiresPhpunit' => '/phpunit/Metadata/RequiresPhpunit.php', + 'PHPUnit\\Metadata\\RequiresSetting' => '/phpunit/Metadata/RequiresSetting.php', + 'PHPUnit\\Metadata\\RunClassInSeparateProcess' => '/phpunit/Metadata/RunClassInSeparateProcess.php', + 'PHPUnit\\Metadata\\RunInSeparateProcess' => '/phpunit/Metadata/RunInSeparateProcess.php', + 'PHPUnit\\Metadata\\RunTestsInSeparateProcesses' => '/phpunit/Metadata/RunTestsInSeparateProcesses.php', + 'PHPUnit\\Metadata\\Test' => '/phpunit/Metadata/Test.php', + 'PHPUnit\\Metadata\\TestDox' => '/phpunit/Metadata/TestDox.php', + 'PHPUnit\\Metadata\\TestWith' => '/phpunit/Metadata/TestWith.php', + 'PHPUnit\\Metadata\\Uses' => '/phpunit/Metadata/Uses.php', + 'PHPUnit\\Metadata\\UsesClass' => '/phpunit/Metadata/UsesClass.php', + 'PHPUnit\\Metadata\\UsesDefaultClass' => '/phpunit/Metadata/UsesDefaultClass.php', + 'PHPUnit\\Metadata\\UsesFunction' => '/phpunit/Metadata/UsesFunction.php', + 'PHPUnit\\Metadata\\Version\\ComparisonRequirement' => '/phpunit/Metadata/Version/ComparisonRequirement.php', + 'PHPUnit\\Metadata\\Version\\ConstraintRequirement' => '/phpunit/Metadata/Version/ConstraintRequirement.php', + 'PHPUnit\\Metadata\\Version\\Requirement' => '/phpunit/Metadata/Version/Requirement.php', + 'PHPUnit\\Metadata\\WithoutErrorHandler' => '/phpunit/Metadata/WithoutErrorHandler.php', + 'PHPUnit\\Runner\\Baseline\\Baseline' => '/phpunit/Runner/Baseline/Baseline.php', + 'PHPUnit\\Runner\\Baseline\\CannotLoadBaselineException' => '/phpunit/Runner/Baseline/Exception/CannotLoadBaselineException.php', + 'PHPUnit\\Runner\\Baseline\\FileDoesNotHaveLineException' => '/phpunit/Runner/Baseline/Exception/FileDoesNotHaveLineException.php', + 'PHPUnit\\Runner\\Baseline\\Generator' => '/phpunit/Runner/Baseline/Generator.php', + 'PHPUnit\\Runner\\Baseline\\Issue' => '/phpunit/Runner/Baseline/Issue.php', + 'PHPUnit\\Runner\\Baseline\\Reader' => '/phpunit/Runner/Baseline/Reader.php', + 'PHPUnit\\Runner\\Baseline\\RelativePathCalculator' => '/phpunit/Runner/Baseline/RelativePathCalculator.php', + 'PHPUnit\\Runner\\Baseline\\Subscriber' => '/phpunit/Runner/Baseline/Subscriber/Subscriber.php', + 'PHPUnit\\Runner\\Baseline\\TestTriggeredDeprecationSubscriber' => '/phpunit/Runner/Baseline/Subscriber/TestTriggeredDeprecationSubscriber.php', + 'PHPUnit\\Runner\\Baseline\\TestTriggeredNoticeSubscriber' => '/phpunit/Runner/Baseline/Subscriber/TestTriggeredNoticeSubscriber.php', + 'PHPUnit\\Runner\\Baseline\\TestTriggeredPhpDeprecationSubscriber' => '/phpunit/Runner/Baseline/Subscriber/TestTriggeredPhpDeprecationSubscriber.php', + 'PHPUnit\\Runner\\Baseline\\TestTriggeredPhpNoticeSubscriber' => '/phpunit/Runner/Baseline/Subscriber/TestTriggeredPhpNoticeSubscriber.php', + 'PHPUnit\\Runner\\Baseline\\TestTriggeredPhpWarningSubscriber' => '/phpunit/Runner/Baseline/Subscriber/TestTriggeredPhpWarningSubscriber.php', + 'PHPUnit\\Runner\\Baseline\\TestTriggeredWarningSubscriber' => '/phpunit/Runner/Baseline/Subscriber/TestTriggeredWarningSubscriber.php', + 'PHPUnit\\Runner\\Baseline\\Writer' => '/phpunit/Runner/Baseline/Writer.php', + 'PHPUnit\\Runner\\ClassCannotBeFoundException' => '/phpunit/Runner/Exception/ClassCannotBeFoundException.php', + 'PHPUnit\\Runner\\ClassDoesNotExtendTestCaseException' => '/phpunit/Runner/Exception/ClassDoesNotExtendTestCaseException.php', + 'PHPUnit\\Runner\\ClassIsAbstractException' => '/phpunit/Runner/Exception/ClassIsAbstractException.php', + 'PHPUnit\\Runner\\CodeCoverage' => '/phpunit/Runner/CodeCoverage.php', + 'PHPUnit\\Runner\\DirectoryDoesNotExistException' => '/phpunit/Runner/Exception/DirectoryDoesNotExistException.php', + 'PHPUnit\\Runner\\ErrorException' => '/phpunit/Runner/Exception/ErrorException.php', + 'PHPUnit\\Runner\\ErrorHandler' => '/phpunit/Runner/ErrorHandler.php', + 'PHPUnit\\Runner\\Exception' => '/phpunit/Runner/Exception/Exception.php', + 'PHPUnit\\Runner\\Extension\\Extension' => '/phpunit/Runner/Extension/Extension.php', + 'PHPUnit\\Runner\\Extension\\ExtensionBootstrapper' => '/phpunit/Runner/Extension/ExtensionBootstrapper.php', + 'PHPUnit\\Runner\\Extension\\Facade' => '/phpunit/Runner/Extension/Facade.php', + 'PHPUnit\\Runner\\Extension\\ParameterCollection' => '/phpunit/Runner/Extension/ParameterCollection.php', 'PHPUnit\\Runner\\Extension\\PharLoader' => '/phpunit/Runner/Extension/PharLoader.php', + 'PHPUnit\\Runner\\FileDoesNotExistException' => '/phpunit/Runner/Exception/FileDoesNotExistException.php', 'PHPUnit\\Runner\\Filter\\ExcludeGroupFilterIterator' => '/phpunit/Runner/Filter/ExcludeGroupFilterIterator.php', 'PHPUnit\\Runner\\Filter\\Factory' => '/phpunit/Runner/Filter/Factory.php', 'PHPUnit\\Runner\\Filter\\GroupFilterIterator' => '/phpunit/Runner/Filter/GroupFilterIterator.php', 'PHPUnit\\Runner\\Filter\\IncludeGroupFilterIterator' => '/phpunit/Runner/Filter/IncludeGroupFilterIterator.php', 'PHPUnit\\Runner\\Filter\\NameFilterIterator' => '/phpunit/Runner/Filter/NameFilterIterator.php', - 'PHPUnit\\Runner\\Hook' => '/phpunit/Runner/Hook/Hook.php', - 'PHPUnit\\Runner\\NullTestResultCache' => '/phpunit/Runner/NullTestResultCache.php', + 'PHPUnit\\Runner\\Filter\\TestIdFilterIterator' => '/phpunit/Runner/Filter/TestIdFilterIterator.php', + 'PHPUnit\\Runner\\GarbageCollection\\ExecutionFinishedSubscriber' => '/phpunit/Runner/GarbageCollection/Subscriber/ExecutionFinishedSubscriber.php', + 'PHPUnit\\Runner\\GarbageCollection\\ExecutionStartedSubscriber' => '/phpunit/Runner/GarbageCollection/Subscriber/ExecutionStartedSubscriber.php', + 'PHPUnit\\Runner\\GarbageCollection\\GarbageCollectionHandler' => '/phpunit/Runner/GarbageCollection/GarbageCollectionHandler.php', + 'PHPUnit\\Runner\\GarbageCollection\\Subscriber' => '/phpunit/Runner/GarbageCollection/Subscriber/Subscriber.php', + 'PHPUnit\\Runner\\GarbageCollection\\TestFinishedSubscriber' => '/phpunit/Runner/GarbageCollection/Subscriber/TestFinishedSubscriber.php', + 'PHPUnit\\Runner\\InvalidOrderException' => '/phpunit/Runner/Exception/InvalidOrderException.php', + 'PHPUnit\\Runner\\InvalidPhptFileException' => '/phpunit/Runner/Exception/InvalidPhptFileException.php', + 'PHPUnit\\Runner\\NoIgnoredEventException' => '/phpunit/Runner/Exception/NoIgnoredEventException.php', + 'PHPUnit\\Runner\\ParameterDoesNotExistException' => '/phpunit/Runner/Exception/ParameterDoesNotExistException.php', + 'PHPUnit\\Runner\\PhptExternalFileCannotBeLoadedException' => '/phpunit/Runner/Exception/PhptExternalFileCannotBeLoadedException.php', 'PHPUnit\\Runner\\PhptTestCase' => '/phpunit/Runner/PhptTestCase.php', - 'PHPUnit\\Runner\\ResultCacheExtension' => '/phpunit/Runner/ResultCacheExtension.php', - 'PHPUnit\\Runner\\StandardTestSuiteLoader' => '/phpunit/Runner/StandardTestSuiteLoader.php', - 'PHPUnit\\Runner\\TestHook' => '/phpunit/Runner/Hook/TestHook.php', - 'PHPUnit\\Runner\\TestListenerAdapter' => '/phpunit/Runner/Hook/TestListenerAdapter.php', - 'PHPUnit\\Runner\\TestResultCache' => '/phpunit/Runner/TestResultCache.php', + 'PHPUnit\\Runner\\ReflectionException' => '/phpunit/Runner/Exception/ReflectionException.php', + 'PHPUnit\\Runner\\ResultCache\\DefaultResultCache' => '/phpunit/Runner/ResultCache/DefaultResultCache.php', + 'PHPUnit\\Runner\\ResultCache\\NullResultCache' => '/phpunit/Runner/ResultCache/NullResultCache.php', + 'PHPUnit\\Runner\\ResultCache\\ResultCache' => '/phpunit/Runner/ResultCache/ResultCache.php', + 'PHPUnit\\Runner\\ResultCache\\ResultCacheHandler' => '/phpunit/Runner/ResultCache/ResultCacheHandler.php', + 'PHPUnit\\Runner\\ResultCache\\Subscriber' => '/phpunit/Runner/ResultCache/Subscriber/Subscriber.php', + 'PHPUnit\\Runner\\ResultCache\\TestConsideredRiskySubscriber' => '/phpunit/Runner/ResultCache/Subscriber/TestConsideredRiskySubscriber.php', + 'PHPUnit\\Runner\\ResultCache\\TestErroredSubscriber' => '/phpunit/Runner/ResultCache/Subscriber/TestErroredSubscriber.php', + 'PHPUnit\\Runner\\ResultCache\\TestFailedSubscriber' => '/phpunit/Runner/ResultCache/Subscriber/TestFailedSubscriber.php', + 'PHPUnit\\Runner\\ResultCache\\TestFinishedSubscriber' => '/phpunit/Runner/ResultCache/Subscriber/TestFinishedSubscriber.php', + 'PHPUnit\\Runner\\ResultCache\\TestMarkedIncompleteSubscriber' => '/phpunit/Runner/ResultCache/Subscriber/TestMarkedIncompleteSubscriber.php', + 'PHPUnit\\Runner\\ResultCache\\TestPreparedSubscriber' => '/phpunit/Runner/ResultCache/Subscriber/TestPreparedSubscriber.php', + 'PHPUnit\\Runner\\ResultCache\\TestSkippedSubscriber' => '/phpunit/Runner/ResultCache/Subscriber/TestSkippedSubscriber.php', + 'PHPUnit\\Runner\\ResultCache\\TestSuiteFinishedSubscriber' => '/phpunit/Runner/ResultCache/Subscriber/TestSuiteFinishedSubscriber.php', + 'PHPUnit\\Runner\\ResultCache\\TestSuiteStartedSubscriber' => '/phpunit/Runner/ResultCache/Subscriber/TestSuiteStartedSubscriber.php', 'PHPUnit\\Runner\\TestSuiteLoader' => '/phpunit/Runner/TestSuiteLoader.php', 'PHPUnit\\Runner\\TestSuiteSorter' => '/phpunit/Runner/TestSuiteSorter.php', + 'PHPUnit\\Runner\\UnsupportedPhptSectionException' => '/phpunit/Runner/Exception/UnsupportedPhptSectionException.php', 'PHPUnit\\Runner\\Version' => '/phpunit/Runner/Version.php', - 'PHPUnit\\TextUI\\CliArguments\\Builder' => '/phpunit/TextUI/CliArguments/Builder.php', - 'PHPUnit\\TextUI\\CliArguments\\Configuration' => '/phpunit/TextUI/CliArguments/Configuration.php', - 'PHPUnit\\TextUI\\CliArguments\\Exception' => '/phpunit/TextUI/CliArguments/Exception.php', - 'PHPUnit\\TextUI\\CliArguments\\Mapper' => '/phpunit/TextUI/CliArguments/Mapper.php', - 'PHPUnit\\TextUI\\Command' => '/phpunit/TextUI/Command.php', - 'PHPUnit\\TextUI\\DefaultResultPrinter' => '/phpunit/TextUI/DefaultResultPrinter.php', + 'PHPUnit\\TestRunner\\TestResult\\BeforeTestClassMethodErroredSubscriber' => '/phpunit/Runner/TestResult/Subscriber/BeforeTestClassMethodErroredSubscriber.php', + 'PHPUnit\\TestRunner\\TestResult\\Collector' => '/phpunit/Runner/TestResult/Collector.php', + 'PHPUnit\\TestRunner\\TestResult\\ExecutionStartedSubscriber' => '/phpunit/Runner/TestResult/Subscriber/ExecutionStartedSubscriber.php', + 'PHPUnit\\TestRunner\\TestResult\\Facade' => '/phpunit/Runner/TestResult/Facade.php', + 'PHPUnit\\TestRunner\\TestResult\\Issues\\Issue' => '/phpunit/Runner/TestResult/Issue.php', + 'PHPUnit\\TestRunner\\TestResult\\PassedTests' => '/phpunit/Runner/TestResult/PassedTests.php', + 'PHPUnit\\TestRunner\\TestResult\\Subscriber' => '/phpunit/Runner/TestResult/Subscriber/Subscriber.php', + 'PHPUnit\\TestRunner\\TestResult\\TestConsideredRiskySubscriber' => '/phpunit/Runner/TestResult/Subscriber/TestConsideredRiskySubscriber.php', + 'PHPUnit\\TestRunner\\TestResult\\TestErroredSubscriber' => '/phpunit/Runner/TestResult/Subscriber/TestErroredSubscriber.php', + 'PHPUnit\\TestRunner\\TestResult\\TestFailedSubscriber' => '/phpunit/Runner/TestResult/Subscriber/TestFailedSubscriber.php', + 'PHPUnit\\TestRunner\\TestResult\\TestFinishedSubscriber' => '/phpunit/Runner/TestResult/Subscriber/TestFinishedSubscriber.php', + 'PHPUnit\\TestRunner\\TestResult\\TestMarkedIncompleteSubscriber' => '/phpunit/Runner/TestResult/Subscriber/TestMarkedIncompleteSubscriber.php', + 'PHPUnit\\TestRunner\\TestResult\\TestPreparedSubscriber' => '/phpunit/Runner/TestResult/Subscriber/TestPreparedSubscriber.php', + 'PHPUnit\\TestRunner\\TestResult\\TestResult' => '/phpunit/Runner/TestResult/TestResult.php', + 'PHPUnit\\TestRunner\\TestResult\\TestRunnerTriggeredDeprecationSubscriber' => '/phpunit/Runner/TestResult/Subscriber/TestRunnerTriggeredDeprecationSubscriber.php', + 'PHPUnit\\TestRunner\\TestResult\\TestRunnerTriggeredWarningSubscriber' => '/phpunit/Runner/TestResult/Subscriber/TestRunnerTriggeredWarningSubscriber.php', + 'PHPUnit\\TestRunner\\TestResult\\TestSkippedSubscriber' => '/phpunit/Runner/TestResult/Subscriber/TestSkippedSubscriber.php', + 'PHPUnit\\TestRunner\\TestResult\\TestSuiteFinishedSubscriber' => '/phpunit/Runner/TestResult/Subscriber/TestSuiteFinishedSubscriber.php', + 'PHPUnit\\TestRunner\\TestResult\\TestSuiteSkippedSubscriber' => '/phpunit/Runner/TestResult/Subscriber/TestSuiteSkippedSubscriber.php', + 'PHPUnit\\TestRunner\\TestResult\\TestSuiteStartedSubscriber' => '/phpunit/Runner/TestResult/Subscriber/TestSuiteStartedSubscriber.php', + 'PHPUnit\\TestRunner\\TestResult\\TestTriggeredDeprecationSubscriber' => '/phpunit/Runner/TestResult/Subscriber/TestTriggeredDeprecationSubscriber.php', + 'PHPUnit\\TestRunner\\TestResult\\TestTriggeredErrorSubscriber' => '/phpunit/Runner/TestResult/Subscriber/TestTriggeredErrorSubscriber.php', + 'PHPUnit\\TestRunner\\TestResult\\TestTriggeredNoticeSubscriber' => '/phpunit/Runner/TestResult/Subscriber/TestTriggeredNoticeSubscriber.php', + 'PHPUnit\\TestRunner\\TestResult\\TestTriggeredPhpDeprecationSubscriber' => '/phpunit/Runner/TestResult/Subscriber/TestTriggeredPhpDeprecationSubscriber.php', + 'PHPUnit\\TestRunner\\TestResult\\TestTriggeredPhpNoticeSubscriber' => '/phpunit/Runner/TestResult/Subscriber/TestTriggeredPhpNoticeSubscriber.php', + 'PHPUnit\\TestRunner\\TestResult\\TestTriggeredPhpWarningSubscriber' => '/phpunit/Runner/TestResult/Subscriber/TestTriggeredPhpWarningSubscriber.php', + 'PHPUnit\\TestRunner\\TestResult\\TestTriggeredPhpunitDeprecationSubscriber' => '/phpunit/Runner/TestResult/Subscriber/TestTriggeredPhpunitDeprecationSubscriber.php', + 'PHPUnit\\TestRunner\\TestResult\\TestTriggeredPhpunitErrorSubscriber' => '/phpunit/Runner/TestResult/Subscriber/TestTriggeredPhpunitErrorSubscriber.php', + 'PHPUnit\\TestRunner\\TestResult\\TestTriggeredPhpunitWarningSubscriber' => '/phpunit/Runner/TestResult/Subscriber/TestTriggeredPhpunitWarningSubscriber.php', + 'PHPUnit\\TestRunner\\TestResult\\TestTriggeredWarningSubscriber' => '/phpunit/Runner/TestResult/Subscriber/TestTriggeredWarningSubscriber.php', + 'PHPUnit\\TextUI\\Application' => '/phpunit/TextUI/Application.php', + 'PHPUnit\\TextUI\\CannotOpenSocketException' => '/phpunit/TextUI/Exception/CannotOpenSocketException.php', + 'PHPUnit\\TextUI\\CliArguments\\Builder' => '/phpunit/TextUI/Configuration/Cli/Builder.php', + 'PHPUnit\\TextUI\\CliArguments\\Configuration' => '/phpunit/TextUI/Configuration/Cli/Configuration.php', + 'PHPUnit\\TextUI\\CliArguments\\Exception' => '/phpunit/TextUI/Configuration/Cli/Exception.php', + 'PHPUnit\\TextUI\\CliArguments\\XmlConfigurationFileFinder' => '/phpunit/TextUI/Configuration/Cli/XmlConfigurationFileFinder.php', + 'PHPUnit\\TextUI\\Command\\AtLeastVersionCommand' => '/phpunit/TextUI/Command/Commands/AtLeastVersionCommand.php', + 'PHPUnit\\TextUI\\Command\\Command' => '/phpunit/TextUI/Command/Command.php', + 'PHPUnit\\TextUI\\Command\\GenerateConfigurationCommand' => '/phpunit/TextUI/Command/Commands/GenerateConfigurationCommand.php', + 'PHPUnit\\TextUI\\Command\\ListGroupsCommand' => '/phpunit/TextUI/Command/Commands/ListGroupsCommand.php', + 'PHPUnit\\TextUI\\Command\\ListTestSuitesCommand' => '/phpunit/TextUI/Command/Commands/ListTestSuitesCommand.php', + 'PHPUnit\\TextUI\\Command\\ListTestsAsTextCommand' => '/phpunit/TextUI/Command/Commands/ListTestsAsTextCommand.php', + 'PHPUnit\\TextUI\\Command\\ListTestsAsXmlCommand' => '/phpunit/TextUI/Command/Commands/ListTestsAsXmlCommand.php', + 'PHPUnit\\TextUI\\Command\\MigrateConfigurationCommand' => '/phpunit/TextUI/Command/Commands/MigrateConfigurationCommand.php', + 'PHPUnit\\TextUI\\Command\\Result' => '/phpunit/TextUI/Command/Result.php', + 'PHPUnit\\TextUI\\Command\\ShowHelpCommand' => '/phpunit/TextUI/Command/Commands/ShowHelpCommand.php', + 'PHPUnit\\TextUI\\Command\\ShowVersionCommand' => '/phpunit/TextUI/Command/Commands/ShowVersionCommand.php', + 'PHPUnit\\TextUI\\Command\\VersionCheckCommand' => '/phpunit/TextUI/Command/Commands/VersionCheckCommand.php', + 'PHPUnit\\TextUI\\Command\\WarmCodeCoverageCacheCommand' => '/phpunit/TextUI/Command/Commands/WarmCodeCoverageCacheCommand.php', + 'PHPUnit\\TextUI\\Configuration\\Builder' => '/phpunit/TextUI/Configuration/Builder.php', + 'PHPUnit\\TextUI\\Configuration\\CodeCoverageFilterRegistry' => '/phpunit/TextUI/Configuration/CodeCoverageFilterRegistry.php', + 'PHPUnit\\TextUI\\Configuration\\CodeCoverageReportNotConfiguredException' => '/phpunit/TextUI/Configuration/Exception/CodeCoverageReportNotConfiguredException.php', + 'PHPUnit\\TextUI\\Configuration\\Configuration' => '/phpunit/TextUI/Configuration/Configuration.php', + 'PHPUnit\\TextUI\\Configuration\\ConfigurationCannotBeBuiltException' => '/phpunit/TextUI/Configuration/Exception/ConfigurationCannotBeBuiltException.php', + 'PHPUnit\\TextUI\\Configuration\\Constant' => '/phpunit/TextUI/Configuration/Value/Constant.php', + 'PHPUnit\\TextUI\\Configuration\\ConstantCollection' => '/phpunit/TextUI/Configuration/Value/ConstantCollection.php', + 'PHPUnit\\TextUI\\Configuration\\ConstantCollectionIterator' => '/phpunit/TextUI/Configuration/Value/ConstantCollectionIterator.php', + 'PHPUnit\\TextUI\\Configuration\\Directory' => '/phpunit/TextUI/Configuration/Value/Directory.php', + 'PHPUnit\\TextUI\\Configuration\\DirectoryCollection' => '/phpunit/TextUI/Configuration/Value/DirectoryCollection.php', + 'PHPUnit\\TextUI\\Configuration\\DirectoryCollectionIterator' => '/phpunit/TextUI/Configuration/Value/DirectoryCollectionIterator.php', + 'PHPUnit\\TextUI\\Configuration\\Exception' => '/phpunit/TextUI/Configuration/Exception/Exception.php', + 'PHPUnit\\TextUI\\Configuration\\ExtensionBootstrap' => '/phpunit/TextUI/Configuration/Value/ExtensionBootstrap.php', + 'PHPUnit\\TextUI\\Configuration\\ExtensionBootstrapCollection' => '/phpunit/TextUI/Configuration/Value/ExtensionBootstrapCollection.php', + 'PHPUnit\\TextUI\\Configuration\\ExtensionBootstrapCollectionIterator' => '/phpunit/TextUI/Configuration/Value/ExtensionBootstrapCollectionIterator.php', + 'PHPUnit\\TextUI\\Configuration\\File' => '/phpunit/TextUI/Configuration/Value/File.php', + 'PHPUnit\\TextUI\\Configuration\\FileCollection' => '/phpunit/TextUI/Configuration/Value/FileCollection.php', + 'PHPUnit\\TextUI\\Configuration\\FileCollectionIterator' => '/phpunit/TextUI/Configuration/Value/FileCollectionIterator.php', + 'PHPUnit\\TextUI\\Configuration\\FilterDirectory' => '/phpunit/TextUI/Configuration/Value/FilterDirectory.php', + 'PHPUnit\\TextUI\\Configuration\\FilterDirectoryCollection' => '/phpunit/TextUI/Configuration/Value/FilterDirectoryCollection.php', + 'PHPUnit\\TextUI\\Configuration\\FilterDirectoryCollectionIterator' => '/phpunit/TextUI/Configuration/Value/FilterDirectoryCollectionIterator.php', + 'PHPUnit\\TextUI\\Configuration\\FilterNotConfiguredException' => '/phpunit/TextUI/Configuration/Exception/FilterNotConfiguredException.php', + 'PHPUnit\\TextUI\\Configuration\\Group' => '/phpunit/TextUI/Configuration/Value/Group.php', + 'PHPUnit\\TextUI\\Configuration\\GroupCollection' => '/phpunit/TextUI/Configuration/Value/GroupCollection.php', + 'PHPUnit\\TextUI\\Configuration\\GroupCollectionIterator' => '/phpunit/TextUI/Configuration/Value/GroupCollectionIterator.php', + 'PHPUnit\\TextUI\\Configuration\\IncludePathNotConfiguredException' => '/phpunit/TextUI/Configuration/Exception/IncludePathNotConfiguredException.php', + 'PHPUnit\\TextUI\\Configuration\\IniSetting' => '/phpunit/TextUI/Configuration/Value/IniSetting.php', + 'PHPUnit\\TextUI\\Configuration\\IniSettingCollection' => '/phpunit/TextUI/Configuration/Value/IniSettingCollection.php', + 'PHPUnit\\TextUI\\Configuration\\IniSettingCollectionIterator' => '/phpunit/TextUI/Configuration/Value/IniSettingCollectionIterator.php', + 'PHPUnit\\TextUI\\Configuration\\LoggingNotConfiguredException' => '/phpunit/TextUI/Configuration/Exception/LoggingNotConfiguredException.php', + 'PHPUnit\\TextUI\\Configuration\\Merger' => '/phpunit/TextUI/Configuration/Merger.php', + 'PHPUnit\\TextUI\\Configuration\\NoBaselineException' => '/phpunit/TextUI/Configuration/Exception/NoBaselineException.php', + 'PHPUnit\\TextUI\\Configuration\\NoBootstrapException' => '/phpunit/TextUI/Configuration/Exception/NoBootstrapException.php', + 'PHPUnit\\TextUI\\Configuration\\NoCacheDirectoryException' => '/phpunit/TextUI/Configuration/Exception/NoCacheDirectoryException.php', + 'PHPUnit\\TextUI\\Configuration\\NoCliArgumentException' => '/phpunit/TextUI/Configuration/Exception/NoCliArgumentException.php', + 'PHPUnit\\TextUI\\Configuration\\NoConfigurationFileException' => '/phpunit/TextUI/Configuration/Exception/NoConfigurationFileException.php', + 'PHPUnit\\TextUI\\Configuration\\NoCoverageCacheDirectoryException' => '/phpunit/TextUI/Configuration/Exception/NoCoverageCacheDirectoryException.php', + 'PHPUnit\\TextUI\\Configuration\\NoCustomCssFileException' => '/phpunit/TextUI/Configuration/Exception/NoCustomCssFileException.php', + 'PHPUnit\\TextUI\\Configuration\\NoDefaultTestSuiteException' => '/phpunit/TextUI/Configuration/Exception/NoDefaultTestSuiteException.php', + 'PHPUnit\\TextUI\\Configuration\\NoPharExtensionDirectoryException' => '/phpunit/TextUI/Configuration/Exception/NoPharExtensionDirectoryException.php', + 'PHPUnit\\TextUI\\Configuration\\Php' => '/phpunit/TextUI/Configuration/Value/Php.php', + 'PHPUnit\\TextUI\\Configuration\\PhpHandler' => '/phpunit/TextUI/Configuration/PhpHandler.php', + 'PHPUnit\\TextUI\\Configuration\\Registry' => '/phpunit/TextUI/Configuration/Registry.php', + 'PHPUnit\\TextUI\\Configuration\\Source' => '/phpunit/TextUI/Configuration/Value/Source.php', + 'PHPUnit\\TextUI\\Configuration\\SourceFilter' => '/phpunit/TextUI/Configuration/SourceFilter.php', + 'PHPUnit\\TextUI\\Configuration\\SourceMapper' => '/phpunit/TextUI/Configuration/SourceMapper.php', + 'PHPUnit\\TextUI\\Configuration\\TestDirectory' => '/phpunit/TextUI/Configuration/Value/TestDirectory.php', + 'PHPUnit\\TextUI\\Configuration\\TestDirectoryCollection' => '/phpunit/TextUI/Configuration/Value/TestDirectoryCollection.php', + 'PHPUnit\\TextUI\\Configuration\\TestDirectoryCollectionIterator' => '/phpunit/TextUI/Configuration/Value/TestDirectoryCollectionIterator.php', + 'PHPUnit\\TextUI\\Configuration\\TestFile' => '/phpunit/TextUI/Configuration/Value/TestFile.php', + 'PHPUnit\\TextUI\\Configuration\\TestFileCollection' => '/phpunit/TextUI/Configuration/Value/TestFileCollection.php', + 'PHPUnit\\TextUI\\Configuration\\TestFileCollectionIterator' => '/phpunit/TextUI/Configuration/Value/TestFileCollectionIterator.php', + 'PHPUnit\\TextUI\\Configuration\\TestSuite' => '/phpunit/TextUI/Configuration/Value/TestSuite.php', + 'PHPUnit\\TextUI\\Configuration\\TestSuiteBuilder' => '/phpunit/TextUI/Configuration/TestSuiteBuilder.php', + 'PHPUnit\\TextUI\\Configuration\\TestSuiteCollection' => '/phpunit/TextUI/Configuration/Value/TestSuiteCollection.php', + 'PHPUnit\\TextUI\\Configuration\\TestSuiteCollectionIterator' => '/phpunit/TextUI/Configuration/Value/TestSuiteCollectionIterator.php', + 'PHPUnit\\TextUI\\Configuration\\Variable' => '/phpunit/TextUI/Configuration/Value/Variable.php', + 'PHPUnit\\TextUI\\Configuration\\VariableCollection' => '/phpunit/TextUI/Configuration/Value/VariableCollection.php', + 'PHPUnit\\TextUI\\Configuration\\VariableCollectionIterator' => '/phpunit/TextUI/Configuration/Value/VariableCollectionIterator.php', 'PHPUnit\\TextUI\\Exception' => '/phpunit/TextUI/Exception/Exception.php', + 'PHPUnit\\TextUI\\ExtensionsNotConfiguredException' => '/phpunit/TextUI/Exception/ExtensionsNotConfiguredException.php', 'PHPUnit\\TextUI\\Help' => '/phpunit/TextUI/Help.php', + 'PHPUnit\\TextUI\\InvalidSocketException' => '/phpunit/TextUI/Exception/InvalidSocketException.php', + 'PHPUnit\\TextUI\\Output\\DefaultPrinter' => '/phpunit/TextUI/Output/Printer/DefaultPrinter.php', + 'PHPUnit\\TextUI\\Output\\Default\\ProgressPrinter\\BeforeTestClassMethodErroredSubscriber' => '/phpunit/TextUI/Output/Default/ProgressPrinter/Subscriber/BeforeTestClassMethodErroredSubscriber.php', + 'PHPUnit\\TextUI\\Output\\Default\\ProgressPrinter\\ProgressPrinter' => '/phpunit/TextUI/Output/Default/ProgressPrinter/ProgressPrinter.php', + 'PHPUnit\\TextUI\\Output\\Default\\ProgressPrinter\\Subscriber' => '/phpunit/TextUI/Output/Default/ProgressPrinter/Subscriber/Subscriber.php', + 'PHPUnit\\TextUI\\Output\\Default\\ProgressPrinter\\TestConsideredRiskySubscriber' => '/phpunit/TextUI/Output/Default/ProgressPrinter/Subscriber/TestConsideredRiskySubscriber.php', + 'PHPUnit\\TextUI\\Output\\Default\\ProgressPrinter\\TestErroredSubscriber' => '/phpunit/TextUI/Output/Default/ProgressPrinter/Subscriber/TestErroredSubscriber.php', + 'PHPUnit\\TextUI\\Output\\Default\\ProgressPrinter\\TestFailedSubscriber' => '/phpunit/TextUI/Output/Default/ProgressPrinter/Subscriber/TestFailedSubscriber.php', + 'PHPUnit\\TextUI\\Output\\Default\\ProgressPrinter\\TestFinishedSubscriber' => '/phpunit/TextUI/Output/Default/ProgressPrinter/Subscriber/TestFinishedSubscriber.php', + 'PHPUnit\\TextUI\\Output\\Default\\ProgressPrinter\\TestMarkedIncompleteSubscriber' => '/phpunit/TextUI/Output/Default/ProgressPrinter/Subscriber/TestMarkedIncompleteSubscriber.php', + 'PHPUnit\\TextUI\\Output\\Default\\ProgressPrinter\\TestPreparedSubscriber' => '/phpunit/TextUI/Output/Default/ProgressPrinter/Subscriber/TestPreparedSubscriber.php', + 'PHPUnit\\TextUI\\Output\\Default\\ProgressPrinter\\TestRunnerExecutionStartedSubscriber' => '/phpunit/TextUI/Output/Default/ProgressPrinter/Subscriber/TestRunnerExecutionStartedSubscriber.php', + 'PHPUnit\\TextUI\\Output\\Default\\ProgressPrinter\\TestSkippedSubscriber' => '/phpunit/TextUI/Output/Default/ProgressPrinter/Subscriber/TestSkippedSubscriber.php', + 'PHPUnit\\TextUI\\Output\\Default\\ProgressPrinter\\TestTriggeredDeprecationSubscriber' => '/phpunit/TextUI/Output/Default/ProgressPrinter/Subscriber/TestTriggeredDeprecationSubscriber.php', + 'PHPUnit\\TextUI\\Output\\Default\\ProgressPrinter\\TestTriggeredErrorSubscriber' => '/phpunit/TextUI/Output/Default/ProgressPrinter/Subscriber/TestTriggeredErrorSubscriber.php', + 'PHPUnit\\TextUI\\Output\\Default\\ProgressPrinter\\TestTriggeredNoticeSubscriber' => '/phpunit/TextUI/Output/Default/ProgressPrinter/Subscriber/TestTriggeredNoticeSubscriber.php', + 'PHPUnit\\TextUI\\Output\\Default\\ProgressPrinter\\TestTriggeredPhpDeprecationSubscriber' => '/phpunit/TextUI/Output/Default/ProgressPrinter/Subscriber/TestTriggeredPhpDeprecationSubscriber.php', + 'PHPUnit\\TextUI\\Output\\Default\\ProgressPrinter\\TestTriggeredPhpNoticeSubscriber' => '/phpunit/TextUI/Output/Default/ProgressPrinter/Subscriber/TestTriggeredPhpNoticeSubscriber.php', + 'PHPUnit\\TextUI\\Output\\Default\\ProgressPrinter\\TestTriggeredPhpWarningSubscriber' => '/phpunit/TextUI/Output/Default/ProgressPrinter/Subscriber/TestTriggeredPhpWarningSubscriber.php', + 'PHPUnit\\TextUI\\Output\\Default\\ProgressPrinter\\TestTriggeredPhpunitDeprecationSubscriber' => '/phpunit/TextUI/Output/Default/ProgressPrinter/Subscriber/TestTriggeredPhpunitDeprecationSubscriber.php', + 'PHPUnit\\TextUI\\Output\\Default\\ProgressPrinter\\TestTriggeredPhpunitWarningSubscriber' => '/phpunit/TextUI/Output/Default/ProgressPrinter/Subscriber/TestTriggeredPhpunitWarningSubscriber.php', + 'PHPUnit\\TextUI\\Output\\Default\\ProgressPrinter\\TestTriggeredWarningSubscriber' => '/phpunit/TextUI/Output/Default/ProgressPrinter/Subscriber/TestTriggeredWarningSubscriber.php', + 'PHPUnit\\TextUI\\Output\\Default\\ResultPrinter' => '/phpunit/TextUI/Output/Default/ResultPrinter.php', + 'PHPUnit\\TextUI\\Output\\Default\\UnexpectedOutputPrinter' => '/phpunit/TextUI/Output/Default/UnexpectedOutputPrinter.php', + 'PHPUnit\\TextUI\\Output\\Facade' => '/phpunit/TextUI/Output/Facade.php', + 'PHPUnit\\TextUI\\Output\\NullPrinter' => '/phpunit/TextUI/Output/Printer/NullPrinter.php', + 'PHPUnit\\TextUI\\Output\\Printer' => '/phpunit/TextUI/Output/Printer/Printer.php', + 'PHPUnit\\TextUI\\Output\\SummaryPrinter' => '/phpunit/TextUI/Output/SummaryPrinter.php', + 'PHPUnit\\TextUI\\Output\\TestDox\\ResultPrinter' => '/phpunit/TextUI/Output/TestDox/ResultPrinter.php', 'PHPUnit\\TextUI\\ReflectionException' => '/phpunit/TextUI/Exception/ReflectionException.php', - 'PHPUnit\\TextUI\\ResultPrinter' => '/phpunit/TextUI/ResultPrinter.php', 'PHPUnit\\TextUI\\RuntimeException' => '/phpunit/TextUI/Exception/RuntimeException.php', + 'PHPUnit\\TextUI\\ShellExitCodeCalculator' => '/phpunit/TextUI/ShellExitCodeCalculator.php', 'PHPUnit\\TextUI\\TestDirectoryNotFoundException' => '/phpunit/TextUI/Exception/TestDirectoryNotFoundException.php', 'PHPUnit\\TextUI\\TestFileNotFoundException' => '/phpunit/TextUI/Exception/TestFileNotFoundException.php', 'PHPUnit\\TextUI\\TestRunner' => '/phpunit/TextUI/TestRunner.php', - 'PHPUnit\\TextUI\\TestSuiteMapper' => '/phpunit/TextUI/TestSuiteMapper.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\CodeCoverage\\CodeCoverage' => '/phpunit/TextUI/XmlConfiguration/CodeCoverage/CodeCoverage.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\CodeCoverage\\FilterMapper' => '/phpunit/TextUI/XmlConfiguration/CodeCoverage/FilterMapper.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\CodeCoverage\\Filter\\Directory' => '/phpunit/TextUI/XmlConfiguration/CodeCoverage/Filter/Directory.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\CodeCoverage\\Filter\\DirectoryCollection' => '/phpunit/TextUI/XmlConfiguration/CodeCoverage/Filter/DirectoryCollection.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\CodeCoverage\\Filter\\DirectoryCollectionIterator' => '/phpunit/TextUI/XmlConfiguration/CodeCoverage/Filter/DirectoryCollectionIterator.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\CodeCoverage\\Report\\Clover' => '/phpunit/TextUI/XmlConfiguration/CodeCoverage/Report/Clover.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\CodeCoverage\\Report\\Cobertura' => '/phpunit/TextUI/XmlConfiguration/CodeCoverage/Report/Cobertura.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\CodeCoverage\\Report\\Crap4j' => '/phpunit/TextUI/XmlConfiguration/CodeCoverage/Report/Crap4j.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\CodeCoverage\\Report\\Html' => '/phpunit/TextUI/XmlConfiguration/CodeCoverage/Report/Html.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\CodeCoverage\\Report\\Php' => '/phpunit/TextUI/XmlConfiguration/CodeCoverage/Report/Php.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\CodeCoverage\\Report\\Text' => '/phpunit/TextUI/XmlConfiguration/CodeCoverage/Report/Text.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\CodeCoverage\\Report\\Xml' => '/phpunit/TextUI/XmlConfiguration/CodeCoverage/Report/Xml.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\Configuration' => '/phpunit/TextUI/XmlConfiguration/Configuration.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\Constant' => '/phpunit/TextUI/XmlConfiguration/PHP/Constant.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\ConstantCollection' => '/phpunit/TextUI/XmlConfiguration/PHP/ConstantCollection.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\ConstantCollectionIterator' => '/phpunit/TextUI/XmlConfiguration/PHP/ConstantCollectionIterator.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\ConvertLogTypes' => '/phpunit/TextUI/XmlConfiguration/Migration/Migrations/ConvertLogTypes.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\CoverageCloverToReport' => '/phpunit/TextUI/XmlConfiguration/Migration/Migrations/CoverageCloverToReport.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\CoverageCrap4jToReport' => '/phpunit/TextUI/XmlConfiguration/Migration/Migrations/CoverageCrap4jToReport.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\CoverageHtmlToReport' => '/phpunit/TextUI/XmlConfiguration/Migration/Migrations/CoverageHtmlToReport.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\CoveragePhpToReport' => '/phpunit/TextUI/XmlConfiguration/Migration/Migrations/CoveragePhpToReport.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\CoverageTextToReport' => '/phpunit/TextUI/XmlConfiguration/Migration/Migrations/CoverageTextToReport.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\CoverageXmlToReport' => '/phpunit/TextUI/XmlConfiguration/Migration/Migrations/CoverageXmlToReport.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\Directory' => '/phpunit/TextUI/XmlConfiguration/Filesystem/Directory.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\DirectoryCollection' => '/phpunit/TextUI/XmlConfiguration/Filesystem/DirectoryCollection.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\DirectoryCollectionIterator' => '/phpunit/TextUI/XmlConfiguration/Filesystem/DirectoryCollectionIterator.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\Exception' => '/phpunit/TextUI/XmlConfiguration/Exception.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\Extension' => '/phpunit/TextUI/XmlConfiguration/PHPUnit/Extension.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\ExtensionCollection' => '/phpunit/TextUI/XmlConfiguration/PHPUnit/ExtensionCollection.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\ExtensionCollectionIterator' => '/phpunit/TextUI/XmlConfiguration/PHPUnit/ExtensionCollectionIterator.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\File' => '/phpunit/TextUI/XmlConfiguration/Filesystem/File.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\FileCollection' => '/phpunit/TextUI/XmlConfiguration/Filesystem/FileCollection.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\FileCollectionIterator' => '/phpunit/TextUI/XmlConfiguration/Filesystem/FileCollectionIterator.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\Generator' => '/phpunit/TextUI/XmlConfiguration/Generator.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\Group' => '/phpunit/TextUI/XmlConfiguration/Group/Group.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\GroupCollection' => '/phpunit/TextUI/XmlConfiguration/Group/GroupCollection.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\GroupCollectionIterator' => '/phpunit/TextUI/XmlConfiguration/Group/GroupCollectionIterator.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\Groups' => '/phpunit/TextUI/XmlConfiguration/Group/Groups.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\IniSetting' => '/phpunit/TextUI/XmlConfiguration/PHP/IniSetting.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\IniSettingCollection' => '/phpunit/TextUI/XmlConfiguration/PHP/IniSettingCollection.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\IniSettingCollectionIterator' => '/phpunit/TextUI/XmlConfiguration/PHP/IniSettingCollectionIterator.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\IntroduceCoverageElement' => '/phpunit/TextUI/XmlConfiguration/Migration/Migrations/IntroduceCoverageElement.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\Loader' => '/phpunit/TextUI/XmlConfiguration/Loader.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\LogToReportMigration' => '/phpunit/TextUI/XmlConfiguration/Migration/Migrations/LogToReportMigration.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\Logging\\Junit' => '/phpunit/TextUI/XmlConfiguration/Logging/Junit.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\Logging\\Logging' => '/phpunit/TextUI/XmlConfiguration/Logging/Logging.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\Logging\\TeamCity' => '/phpunit/TextUI/XmlConfiguration/Logging/TeamCity.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\Logging\\TestDox\\Html' => '/phpunit/TextUI/XmlConfiguration/Logging/TestDox/Html.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\Logging\\TestDox\\Text' => '/phpunit/TextUI/XmlConfiguration/Logging/TestDox/Text.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\Logging\\TestDox\\Xml' => '/phpunit/TextUI/XmlConfiguration/Logging/TestDox/Xml.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\Logging\\Text' => '/phpunit/TextUI/XmlConfiguration/Logging/Text.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\Migration' => '/phpunit/TextUI/XmlConfiguration/Migration/Migrations/Migration.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\MigrationBuilder' => '/phpunit/TextUI/XmlConfiguration/Migration/MigrationBuilder.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\MigrationBuilderException' => '/phpunit/TextUI/XmlConfiguration/Migration/MigrationBuilderException.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\MigrationException' => '/phpunit/TextUI/XmlConfiguration/Migration/MigrationException.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\Migrator' => '/phpunit/TextUI/XmlConfiguration/Migration/Migrator.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\MoveAttributesFromFilterWhitelistToCoverage' => '/phpunit/TextUI/XmlConfiguration/Migration/Migrations/MoveAttributesFromFilterWhitelistToCoverage.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\MoveAttributesFromRootToCoverage' => '/phpunit/TextUI/XmlConfiguration/Migration/Migrations/MoveAttributesFromRootToCoverage.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\MoveWhitelistExcludesToCoverage' => '/phpunit/TextUI/XmlConfiguration/Migration/Migrations/MoveWhitelistExcludesToCoverage.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\MoveWhitelistIncludesToCoverage' => '/phpunit/TextUI/XmlConfiguration/Migration/Migrations/MoveWhitelistIncludesToCoverage.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\PHPUnit' => '/phpunit/TextUI/XmlConfiguration/PHPUnit/PHPUnit.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\Php' => '/phpunit/TextUI/XmlConfiguration/PHP/Php.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\PhpHandler' => '/phpunit/TextUI/XmlConfiguration/PHP/PhpHandler.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\RemoveCacheTokensAttribute' => '/phpunit/TextUI/XmlConfiguration/Migration/Migrations/RemoveCacheTokensAttribute.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\RemoveEmptyFilter' => '/phpunit/TextUI/XmlConfiguration/Migration/Migrations/RemoveEmptyFilter.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\RemoveLogTypes' => '/phpunit/TextUI/XmlConfiguration/Migration/Migrations/RemoveLogTypes.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\TestDirectory' => '/phpunit/TextUI/XmlConfiguration/TestSuite/TestDirectory.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\TestDirectoryCollection' => '/phpunit/TextUI/XmlConfiguration/TestSuite/TestDirectoryCollection.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\TestDirectoryCollectionIterator' => '/phpunit/TextUI/XmlConfiguration/TestSuite/TestDirectoryCollectionIterator.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\TestFile' => '/phpunit/TextUI/XmlConfiguration/TestSuite/TestFile.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\TestFileCollection' => '/phpunit/TextUI/XmlConfiguration/TestSuite/TestFileCollection.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\TestFileCollectionIterator' => '/phpunit/TextUI/XmlConfiguration/TestSuite/TestFileCollectionIterator.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\TestSuite' => '/phpunit/TextUI/XmlConfiguration/TestSuite/TestSuite.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\TestSuiteCollection' => '/phpunit/TextUI/XmlConfiguration/TestSuite/TestSuiteCollection.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\TestSuiteCollectionIterator' => '/phpunit/TextUI/XmlConfiguration/TestSuite/TestSuiteCollectionIterator.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\UpdateSchemaLocationTo93' => '/phpunit/TextUI/XmlConfiguration/Migration/Migrations/UpdateSchemaLocationTo93.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\Variable' => '/phpunit/TextUI/XmlConfiguration/PHP/Variable.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\VariableCollection' => '/phpunit/TextUI/XmlConfiguration/PHP/VariableCollection.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\VariableCollectionIterator' => '/phpunit/TextUI/XmlConfiguration/PHP/VariableCollectionIterator.php', - 'PHPUnit\\Util\\Annotation\\DocBlock' => '/phpunit/Util/Annotation/DocBlock.php', - 'PHPUnit\\Util\\Annotation\\Registry' => '/phpunit/Util/Annotation/Registry.php', - 'PHPUnit\\Util\\Blacklist' => '/phpunit/Util/Blacklist.php', + 'PHPUnit\\TextUI\\TestSuiteFilterProcessor' => '/phpunit/TextUI/TestSuiteFilterProcessor.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\CannotFindSchemaException' => '/phpunit/TextUI/Configuration/Exception/CannotFindSchemaException.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\CodeCoverage\\CodeCoverage' => '/phpunit/TextUI/Configuration/Xml/CodeCoverage/CodeCoverage.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\CodeCoverage\\Report\\Clover' => '/phpunit/TextUI/Configuration/Xml/CodeCoverage/Report/Clover.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\CodeCoverage\\Report\\Cobertura' => '/phpunit/TextUI/Configuration/Xml/CodeCoverage/Report/Cobertura.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\CodeCoverage\\Report\\Crap4j' => '/phpunit/TextUI/Configuration/Xml/CodeCoverage/Report/Crap4j.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\CodeCoverage\\Report\\Html' => '/phpunit/TextUI/Configuration/Xml/CodeCoverage/Report/Html.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\CodeCoverage\\Report\\Php' => '/phpunit/TextUI/Configuration/Xml/CodeCoverage/Report/Php.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\CodeCoverage\\Report\\Text' => '/phpunit/TextUI/Configuration/Xml/CodeCoverage/Report/Text.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\CodeCoverage\\Report\\Xml' => '/phpunit/TextUI/Configuration/Xml/CodeCoverage/Report/Xml.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\Configuration' => '/phpunit/TextUI/Configuration/Xml/Configuration.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\ConvertLogTypes' => '/phpunit/TextUI/Configuration/Xml/Migration/Migrations/ConvertLogTypes.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\CoverageCloverToReport' => '/phpunit/TextUI/Configuration/Xml/Migration/Migrations/CoverageCloverToReport.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\CoverageCrap4jToReport' => '/phpunit/TextUI/Configuration/Xml/Migration/Migrations/CoverageCrap4jToReport.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\CoverageHtmlToReport' => '/phpunit/TextUI/Configuration/Xml/Migration/Migrations/CoverageHtmlToReport.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\CoveragePhpToReport' => '/phpunit/TextUI/Configuration/Xml/Migration/Migrations/CoveragePhpToReport.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\CoverageTextToReport' => '/phpunit/TextUI/Configuration/Xml/Migration/Migrations/CoverageTextToReport.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\CoverageXmlToReport' => '/phpunit/TextUI/Configuration/Xml/Migration/Migrations/CoverageXmlToReport.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\DefaultConfiguration' => '/phpunit/TextUI/Configuration/Xml/DefaultConfiguration.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\Exception' => '/phpunit/TextUI/Configuration/Xml/Exception.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\FailedSchemaDetectionResult' => '/phpunit/TextUI/Configuration/Xml/SchemaDetector/FailedSchemaDetectionResult.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\Generator' => '/phpunit/TextUI/Configuration/Xml/Generator.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\Groups' => '/phpunit/TextUI/Configuration/Xml/Groups.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\IntroduceCacheDirectoryAttribute' => '/phpunit/TextUI/Configuration/Xml/Migration/Migrations/IntroduceCacheDirectoryAttribute.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\IntroduceCoverageElement' => '/phpunit/TextUI/Configuration/Xml/Migration/Migrations/IntroduceCoverageElement.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\LoadedFromFileConfiguration' => '/phpunit/TextUI/Configuration/Xml/LoadedFromFileConfiguration.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\Loader' => '/phpunit/TextUI/Configuration/Xml/Loader.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\LogToReportMigration' => '/phpunit/TextUI/Configuration/Xml/Migration/Migrations/LogToReportMigration.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\Logging\\Junit' => '/phpunit/TextUI/Configuration/Xml/Logging/Junit.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\Logging\\Logging' => '/phpunit/TextUI/Configuration/Xml/Logging/Logging.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\Logging\\TeamCity' => '/phpunit/TextUI/Configuration/Xml/Logging/TeamCity.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\Logging\\TestDox\\Html' => '/phpunit/TextUI/Configuration/Xml/Logging/TestDox/Html.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\Logging\\TestDox\\Text' => '/phpunit/TextUI/Configuration/Xml/Logging/TestDox/Text.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\Migration' => '/phpunit/TextUI/Configuration/Xml/Migration/Migrations/Migration.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\MigrationBuilder' => '/phpunit/TextUI/Configuration/Xml/Migration/MigrationBuilder.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\MigrationBuilderException' => '/phpunit/TextUI/Configuration/Xml/Migration/MigrationBuilderException.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\MigrationException' => '/phpunit/TextUI/Configuration/Xml/Migration/MigrationException.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\Migrator' => '/phpunit/TextUI/Configuration/Xml/Migration/Migrator.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\MoveAttributesFromFilterWhitelistToCoverage' => '/phpunit/TextUI/Configuration/Xml/Migration/Migrations/MoveAttributesFromFilterWhitelistToCoverage.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\MoveAttributesFromRootToCoverage' => '/phpunit/TextUI/Configuration/Xml/Migration/Migrations/MoveAttributesFromRootToCoverage.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\MoveCoverageDirectoriesToSource' => '/phpunit/TextUI/Configuration/Xml/Migration/Migrations/MoveCoverageDirectoriesToSource.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\MoveWhitelistExcludesToCoverage' => '/phpunit/TextUI/Configuration/Xml/Migration/Migrations/MoveWhitelistExcludesToCoverage.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\MoveWhitelistIncludesToCoverage' => '/phpunit/TextUI/Configuration/Xml/Migration/Migrations/MoveWhitelistIncludesToCoverage.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\PHPUnit' => '/phpunit/TextUI/Configuration/Xml/PHPUnit.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\RemoveBeStrictAboutResourceUsageDuringSmallTestsAttribute' => '/phpunit/TextUI/Configuration/Xml/Migration/Migrations/RemoveBeStrictAboutResourceUsageDuringSmallTestsAttribute.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\RemoveBeStrictAboutTodoAnnotatedTestsAttribute' => '/phpunit/TextUI/Configuration/Xml/Migration/Migrations/RemoveBeStrictAboutTodoAnnotatedTestsAttribute.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\RemoveCacheResultFileAttribute' => '/phpunit/TextUI/Configuration/Xml/Migration/Migrations/RemoveCacheResultFileAttribute.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\RemoveCacheTokensAttribute' => '/phpunit/TextUI/Configuration/Xml/Migration/Migrations/RemoveCacheTokensAttribute.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\RemoveConversionToExceptionsAttributes' => '/phpunit/TextUI/Configuration/Xml/Migration/Migrations/RemoveConversionToExceptionsAttributes.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\RemoveCoverageElementCacheDirectoryAttribute' => '/phpunit/TextUI/Configuration/Xml/Migration/Migrations/RemoveCoverageElementCacheDirectoryAttribute.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\RemoveCoverageElementProcessUncoveredFilesAttribute' => '/phpunit/TextUI/Configuration/Xml/Migration/Migrations/RemoveCoverageElementProcessUncoveredFilesAttribute.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\RemoveEmptyFilter' => '/phpunit/TextUI/Configuration/Xml/Migration/Migrations/RemoveEmptyFilter.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\RemoveListeners' => '/phpunit/TextUI/Configuration/Xml/Migration/Migrations/RemoveListeners.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\RemoveLogTypes' => '/phpunit/TextUI/Configuration/Xml/Migration/Migrations/RemoveLogTypes.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\RemoveLoggingElements' => '/phpunit/TextUI/Configuration/Xml/Migration/Migrations/RemoveLoggingElements.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\RemoveNoInteractionAttribute' => '/phpunit/TextUI/Configuration/Xml/Migration/Migrations/RemoveNoInteractionAttribute.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\RemovePrinterAttributes' => '/phpunit/TextUI/Configuration/Xml/Migration/Migrations/RemovePrinterAttributes.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\RemoveTestDoxGroupsElement' => '/phpunit/TextUI/Configuration/Xml/Migration/Migrations/RemoveTestDoxGroupsElement.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\RemoveTestSuiteLoaderAttributes' => '/phpunit/TextUI/Configuration/Xml/Migration/Migrations/RemoveTestSuiteLoaderAttributes.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\RemoveVerboseAttribute' => '/phpunit/TextUI/Configuration/Xml/Migration/Migrations/RemoveVerboseAttribute.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\RenameBackupStaticAttributesAttribute' => '/phpunit/TextUI/Configuration/Xml/Migration/Migrations/RenameBackupStaticAttributesAttribute.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\RenameBeStrictAboutCoversAnnotationAttribute' => '/phpunit/TextUI/Configuration/Xml/Migration/Migrations/RenameBeStrictAboutCoversAnnotationAttribute.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\RenameForceCoversAnnotationAttribute' => '/phpunit/TextUI/Configuration/Xml/Migration/Migrations/RenameForceCoversAnnotationAttribute.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\SchemaDetectionResult' => '/phpunit/TextUI/Configuration/Xml/SchemaDetector/SchemaDetectionResult.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\SchemaDetector' => '/phpunit/TextUI/Configuration/Xml/SchemaDetector/SchemaDetector.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\SchemaFinder' => '/phpunit/TextUI/Configuration/Xml/SchemaFinder.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\SnapshotNodeList' => '/phpunit/TextUI/Configuration/Xml/Migration/SnapshotNodeList.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\SuccessfulSchemaDetectionResult' => '/phpunit/TextUI/Configuration/Xml/SchemaDetector/SuccessfulSchemaDetectionResult.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\TestSuiteMapper' => '/phpunit/TextUI/Configuration/Xml/TestSuiteMapper.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\UpdateSchemaLocation' => '/phpunit/TextUI/Configuration/Xml/Migration/Migrations/UpdateSchemaLocation.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\ValidationResult' => '/phpunit/TextUI/Configuration/Xml/Validator/ValidationResult.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\Validator' => '/phpunit/TextUI/Configuration/Xml/Validator/Validator.php', 'PHPUnit\\Util\\Cloner' => '/phpunit/Util/Cloner.php', 'PHPUnit\\Util\\Color' => '/phpunit/Util/Color.php', - 'PHPUnit\\Util\\ErrorHandler' => '/phpunit/Util/ErrorHandler.php', - 'PHPUnit\\Util\\Exception' => '/phpunit/Util/Exception.php', + 'PHPUnit\\Util\\Exception' => '/phpunit/Util/Exception/Exception.php', 'PHPUnit\\Util\\ExcludeList' => '/phpunit/Util/ExcludeList.php', - 'PHPUnit\\Util\\FileLoader' => '/phpunit/Util/FileLoader.php', + 'PHPUnit\\Util\\Exporter' => '/phpunit/Util/Exporter.php', 'PHPUnit\\Util\\Filesystem' => '/phpunit/Util/Filesystem.php', 'PHPUnit\\Util\\Filter' => '/phpunit/Util/Filter.php', 'PHPUnit\\Util\\GlobalState' => '/phpunit/Util/GlobalState.php', - 'PHPUnit\\Util\\InvalidDataSetException' => '/phpunit/Util/InvalidDataSetException.php', + 'PHPUnit\\Util\\InvalidDirectoryException' => '/phpunit/Util/Exception/InvalidDirectoryException.php', + 'PHPUnit\\Util\\InvalidJsonException' => '/phpunit/Util/Exception/InvalidJsonException.php', + 'PHPUnit\\Util\\InvalidVersionOperatorException' => '/phpunit/Util/Exception/InvalidVersionOperatorException.php', 'PHPUnit\\Util\\Json' => '/phpunit/Util/Json.php', - 'PHPUnit\\Util\\Log\\JUnit' => '/phpunit/Util/Log/JUnit.php', - 'PHPUnit\\Util\\Log\\TeamCity' => '/phpunit/Util/Log/TeamCity.php', 'PHPUnit\\Util\\PHP\\AbstractPhpProcess' => '/phpunit/Util/PHP/AbstractPhpProcess.php', 'PHPUnit\\Util\\PHP\\DefaultPhpProcess' => '/phpunit/Util/PHP/DefaultPhpProcess.php', - 'PHPUnit\\Util\\PHP\\WindowsPhpProcess' => '/phpunit/Util/PHP/WindowsPhpProcess.php', - 'PHPUnit\\Util\\Printer' => '/phpunit/Util/Printer.php', + 'PHPUnit\\Util\\PHP\\PhpProcessException' => '/phpunit/Util/Exception/PhpProcessException.php', 'PHPUnit\\Util\\Reflection' => '/phpunit/Util/Reflection.php', - 'PHPUnit\\Util\\RegularExpression' => '/phpunit/Util/RegularExpression.php', 'PHPUnit\\Util\\Test' => '/phpunit/Util/Test.php', - 'PHPUnit\\Util\\TestDox\\CliTestDoxPrinter' => '/phpunit/Util/TestDox/CliTestDoxPrinter.php', - 'PHPUnit\\Util\\TestDox\\HtmlResultPrinter' => '/phpunit/Util/TestDox/HtmlResultPrinter.php', - 'PHPUnit\\Util\\TestDox\\NamePrettifier' => '/phpunit/Util/TestDox/NamePrettifier.php', - 'PHPUnit\\Util\\TestDox\\ResultPrinter' => '/phpunit/Util/TestDox/ResultPrinter.php', - 'PHPUnit\\Util\\TestDox\\TestDoxPrinter' => '/phpunit/Util/TestDox/TestDoxPrinter.php', - 'PHPUnit\\Util\\TestDox\\TextResultPrinter' => '/phpunit/Util/TestDox/TextResultPrinter.php', - 'PHPUnit\\Util\\TestDox\\XmlResultPrinter' => '/phpunit/Util/TestDox/XmlResultPrinter.php', - 'PHPUnit\\Util\\TextTestListRenderer' => '/phpunit/Util/TextTestListRenderer.php', - 'PHPUnit\\Util\\Type' => '/phpunit/Util/Type.php', + 'PHPUnit\\Util\\ThrowableToStringMapper' => '/phpunit/Util/ThrowableToStringMapper.php', 'PHPUnit\\Util\\VersionComparisonOperator' => '/phpunit/Util/VersionComparisonOperator.php', - 'PHPUnit\\Util\\XdebugFilterScriptGenerator' => '/phpunit/Util/XdebugFilterScriptGenerator.php', - 'PHPUnit\\Util\\Xml' => '/phpunit/Util/Xml.php', - 'PHPUnit\\Util\\XmlTestListRenderer' => '/phpunit/Util/XmlTestListRenderer.php', - 'PHPUnit\\Util\\Xml\\Exception' => '/phpunit/Util/Xml/Exception.php', - 'PHPUnit\\Util\\Xml\\FailedSchemaDetectionResult' => '/phpunit/Util/Xml/FailedSchemaDetectionResult.php', + 'PHPUnit\\Util\\Xml' => '/phpunit/Util/Xml/Xml.php', 'PHPUnit\\Util\\Xml\\Loader' => '/phpunit/Util/Xml/Loader.php', - 'PHPUnit\\Util\\Xml\\SchemaDetectionResult' => '/phpunit/Util/Xml/SchemaDetectionResult.php', - 'PHPUnit\\Util\\Xml\\SchemaDetector' => '/phpunit/Util/Xml/SchemaDetector.php', - 'PHPUnit\\Util\\Xml\\SchemaFinder' => '/phpunit/Util/Xml/SchemaFinder.php', - 'PHPUnit\\Util\\Xml\\SnapshotNodeList' => '/phpunit/Util/Xml/SnapshotNodeList.php', - 'PHPUnit\\Util\\Xml\\SuccessfulSchemaDetectionResult' => '/phpunit/Util/Xml/SuccessfulSchemaDetectionResult.php', - 'PHPUnit\\Util\\Xml\\ValidationResult' => '/phpunit/Util/Xml/ValidationResult.php', - 'PHPUnit\\Util\\Xml\\Validator' => '/phpunit/Util/Xml/Validator.php', - 'Prophecy\\Argument' => '/phpspec-prophecy/Prophecy/Argument.php', - 'Prophecy\\Argument\\ArgumentsWildcard' => '/phpspec-prophecy/Prophecy/Argument/ArgumentsWildcard.php', - 'Prophecy\\Argument\\Token\\AnyValueToken' => '/phpspec-prophecy/Prophecy/Argument/Token/AnyValueToken.php', - 'Prophecy\\Argument\\Token\\AnyValuesToken' => '/phpspec-prophecy/Prophecy/Argument/Token/AnyValuesToken.php', - 'Prophecy\\Argument\\Token\\ApproximateValueToken' => '/phpspec-prophecy/Prophecy/Argument/Token/ApproximateValueToken.php', - 'Prophecy\\Argument\\Token\\ArrayCountToken' => '/phpspec-prophecy/Prophecy/Argument/Token/ArrayCountToken.php', - 'Prophecy\\Argument\\Token\\ArrayEntryToken' => '/phpspec-prophecy/Prophecy/Argument/Token/ArrayEntryToken.php', - 'Prophecy\\Argument\\Token\\ArrayEveryEntryToken' => '/phpspec-prophecy/Prophecy/Argument/Token/ArrayEveryEntryToken.php', - 'Prophecy\\Argument\\Token\\CallbackToken' => '/phpspec-prophecy/Prophecy/Argument/Token/CallbackToken.php', - 'Prophecy\\Argument\\Token\\ExactValueToken' => '/phpspec-prophecy/Prophecy/Argument/Token/ExactValueToken.php', - 'Prophecy\\Argument\\Token\\IdenticalValueToken' => '/phpspec-prophecy/Prophecy/Argument/Token/IdenticalValueToken.php', - 'Prophecy\\Argument\\Token\\InArrayToken' => '/phpspec-prophecy/Prophecy/Argument/Token/InArrayToken.php', - 'Prophecy\\Argument\\Token\\LogicalAndToken' => '/phpspec-prophecy/Prophecy/Argument/Token/LogicalAndToken.php', - 'Prophecy\\Argument\\Token\\LogicalNotToken' => '/phpspec-prophecy/Prophecy/Argument/Token/LogicalNotToken.php', - 'Prophecy\\Argument\\Token\\NotInArrayToken' => '/phpspec-prophecy/Prophecy/Argument/Token/NotInArrayToken.php', - 'Prophecy\\Argument\\Token\\ObjectStateToken' => '/phpspec-prophecy/Prophecy/Argument/Token/ObjectStateToken.php', - 'Prophecy\\Argument\\Token\\StringContainsToken' => '/phpspec-prophecy/Prophecy/Argument/Token/StringContainsToken.php', - 'Prophecy\\Argument\\Token\\TokenInterface' => '/phpspec-prophecy/Prophecy/Argument/Token/TokenInterface.php', - 'Prophecy\\Argument\\Token\\TypeToken' => '/phpspec-prophecy/Prophecy/Argument/Token/TypeToken.php', - 'Prophecy\\Call\\Call' => '/phpspec-prophecy/Prophecy/Call/Call.php', - 'Prophecy\\Call\\CallCenter' => '/phpspec-prophecy/Prophecy/Call/CallCenter.php', - 'Prophecy\\Comparator\\ClosureComparator' => '/phpspec-prophecy/Prophecy/Comparator/ClosureComparator.php', - 'Prophecy\\Comparator\\Factory' => '/phpspec-prophecy/Prophecy/Comparator/Factory.php', - 'Prophecy\\Comparator\\FactoryProvider' => '/phpspec-prophecy/Prophecy/Comparator/FactoryProvider.php', - 'Prophecy\\Comparator\\ProphecyComparator' => '/phpspec-prophecy/Prophecy/Comparator/ProphecyComparator.php', - 'Prophecy\\Doubler\\CachedDoubler' => '/phpspec-prophecy/Prophecy/Doubler/CachedDoubler.php', - 'Prophecy\\Doubler\\ClassPatch\\ClassPatchInterface' => '/phpspec-prophecy/Prophecy/Doubler/ClassPatch/ClassPatchInterface.php', - 'Prophecy\\Doubler\\ClassPatch\\DisableConstructorPatch' => '/phpspec-prophecy/Prophecy/Doubler/ClassPatch/DisableConstructorPatch.php', - 'Prophecy\\Doubler\\ClassPatch\\KeywordPatch' => '/phpspec-prophecy/Prophecy/Doubler/ClassPatch/KeywordPatch.php', - 'Prophecy\\Doubler\\ClassPatch\\MagicCallPatch' => '/phpspec-prophecy/Prophecy/Doubler/ClassPatch/MagicCallPatch.php', - 'Prophecy\\Doubler\\ClassPatch\\ProphecySubjectPatch' => '/phpspec-prophecy/Prophecy/Doubler/ClassPatch/ProphecySubjectPatch.php', - 'Prophecy\\Doubler\\ClassPatch\\ReflectionClassNewInstancePatch' => '/phpspec-prophecy/Prophecy/Doubler/ClassPatch/ReflectionClassNewInstancePatch.php', - 'Prophecy\\Doubler\\ClassPatch\\SplFileInfoPatch' => '/phpspec-prophecy/Prophecy/Doubler/ClassPatch/SplFileInfoPatch.php', - 'Prophecy\\Doubler\\ClassPatch\\ThrowablePatch' => '/phpspec-prophecy/Prophecy/Doubler/ClassPatch/ThrowablePatch.php', - 'Prophecy\\Doubler\\ClassPatch\\TraversablePatch' => '/phpspec-prophecy/Prophecy/Doubler/ClassPatch/TraversablePatch.php', - 'Prophecy\\Doubler\\DoubleInterface' => '/phpspec-prophecy/Prophecy/Doubler/DoubleInterface.php', - 'Prophecy\\Doubler\\Doubler' => '/phpspec-prophecy/Prophecy/Doubler/Doubler.php', - 'Prophecy\\Doubler\\Generator\\ClassCodeGenerator' => '/phpspec-prophecy/Prophecy/Doubler/Generator/ClassCodeGenerator.php', - 'Prophecy\\Doubler\\Generator\\ClassCreator' => '/phpspec-prophecy/Prophecy/Doubler/Generator/ClassCreator.php', - 'Prophecy\\Doubler\\Generator\\ClassMirror' => '/phpspec-prophecy/Prophecy/Doubler/Generator/ClassMirror.php', - 'Prophecy\\Doubler\\Generator\\Node\\ArgumentNode' => '/phpspec-prophecy/Prophecy/Doubler/Generator/Node/ArgumentNode.php', - 'Prophecy\\Doubler\\Generator\\Node\\ArgumentTypeNode' => '/phpspec-prophecy/Prophecy/Doubler/Generator/Node/ArgumentTypeNode.php', - 'Prophecy\\Doubler\\Generator\\Node\\ClassNode' => '/phpspec-prophecy/Prophecy/Doubler/Generator/Node/ClassNode.php', - 'Prophecy\\Doubler\\Generator\\Node\\MethodNode' => '/phpspec-prophecy/Prophecy/Doubler/Generator/Node/MethodNode.php', - 'Prophecy\\Doubler\\Generator\\Node\\ReturnTypeNode' => '/phpspec-prophecy/Prophecy/Doubler/Generator/Node/ReturnTypeNode.php', - 'Prophecy\\Doubler\\Generator\\Node\\TypeNodeAbstract' => '/phpspec-prophecy/Prophecy/Doubler/Generator/Node/TypeNodeAbstract.php', - 'Prophecy\\Doubler\\Generator\\ReflectionInterface' => '/phpspec-prophecy/Prophecy/Doubler/Generator/ReflectionInterface.php', - 'Prophecy\\Doubler\\Generator\\TypeHintReference' => '/phpspec-prophecy/Prophecy/Doubler/Generator/TypeHintReference.php', - 'Prophecy\\Doubler\\LazyDouble' => '/phpspec-prophecy/Prophecy/Doubler/LazyDouble.php', - 'Prophecy\\Doubler\\NameGenerator' => '/phpspec-prophecy/Prophecy/Doubler/NameGenerator.php', - 'Prophecy\\Exception\\Call\\UnexpectedCallException' => '/phpspec-prophecy/Prophecy/Exception/Call/UnexpectedCallException.php', - 'Prophecy\\Exception\\Doubler\\ClassCreatorException' => '/phpspec-prophecy/Prophecy/Exception/Doubler/ClassCreatorException.php', - 'Prophecy\\Exception\\Doubler\\ClassMirrorException' => '/phpspec-prophecy/Prophecy/Exception/Doubler/ClassMirrorException.php', - 'Prophecy\\Exception\\Doubler\\ClassNotFoundException' => '/phpspec-prophecy/Prophecy/Exception/Doubler/ClassNotFoundException.php', - 'Prophecy\\Exception\\Doubler\\DoubleException' => '/phpspec-prophecy/Prophecy/Exception/Doubler/DoubleException.php', - 'Prophecy\\Exception\\Doubler\\DoublerException' => '/phpspec-prophecy/Prophecy/Exception/Doubler/DoublerException.php', - 'Prophecy\\Exception\\Doubler\\InterfaceNotFoundException' => '/phpspec-prophecy/Prophecy/Exception/Doubler/InterfaceNotFoundException.php', - 'Prophecy\\Exception\\Doubler\\MethodNotExtendableException' => '/phpspec-prophecy/Prophecy/Exception/Doubler/MethodNotExtendableException.php', - 'Prophecy\\Exception\\Doubler\\MethodNotFoundException' => '/phpspec-prophecy/Prophecy/Exception/Doubler/MethodNotFoundException.php', - 'Prophecy\\Exception\\Doubler\\ReturnByReferenceException' => '/phpspec-prophecy/Prophecy/Exception/Doubler/ReturnByReferenceException.php', - 'Prophecy\\Exception\\Exception' => '/phpspec-prophecy/Prophecy/Exception/Exception.php', - 'Prophecy\\Exception\\InvalidArgumentException' => '/phpspec-prophecy/Prophecy/Exception/InvalidArgumentException.php', - 'Prophecy\\Exception\\Prediction\\AggregateException' => '/phpspec-prophecy/Prophecy/Exception/Prediction/AggregateException.php', - 'Prophecy\\Exception\\Prediction\\FailedPredictionException' => '/phpspec-prophecy/Prophecy/Exception/Prediction/FailedPredictionException.php', - 'Prophecy\\Exception\\Prediction\\NoCallsException' => '/phpspec-prophecy/Prophecy/Exception/Prediction/NoCallsException.php', - 'Prophecy\\Exception\\Prediction\\PredictionException' => '/phpspec-prophecy/Prophecy/Exception/Prediction/PredictionException.php', - 'Prophecy\\Exception\\Prediction\\UnexpectedCallsCountException' => '/phpspec-prophecy/Prophecy/Exception/Prediction/UnexpectedCallsCountException.php', - 'Prophecy\\Exception\\Prediction\\UnexpectedCallsException' => '/phpspec-prophecy/Prophecy/Exception/Prediction/UnexpectedCallsException.php', - 'Prophecy\\Exception\\Prophecy\\MethodProphecyException' => '/phpspec-prophecy/Prophecy/Exception/Prophecy/MethodProphecyException.php', - 'Prophecy\\Exception\\Prophecy\\ObjectProphecyException' => '/phpspec-prophecy/Prophecy/Exception/Prophecy/ObjectProphecyException.php', - 'Prophecy\\Exception\\Prophecy\\ProphecyException' => '/phpspec-prophecy/Prophecy/Exception/Prophecy/ProphecyException.php', - 'Prophecy\\PhpDocumentor\\ClassAndInterfaceTagRetriever' => '/phpspec-prophecy/Prophecy/PhpDocumentor/ClassAndInterfaceTagRetriever.php', - 'Prophecy\\PhpDocumentor\\ClassTagRetriever' => '/phpspec-prophecy/Prophecy/PhpDocumentor/ClassTagRetriever.php', - 'Prophecy\\PhpDocumentor\\MethodTagRetrieverInterface' => '/phpspec-prophecy/Prophecy/PhpDocumentor/MethodTagRetrieverInterface.php', - 'Prophecy\\Prediction\\CallPrediction' => '/phpspec-prophecy/Prophecy/Prediction/CallPrediction.php', - 'Prophecy\\Prediction\\CallTimesPrediction' => '/phpspec-prophecy/Prophecy/Prediction/CallTimesPrediction.php', - 'Prophecy\\Prediction\\CallbackPrediction' => '/phpspec-prophecy/Prophecy/Prediction/CallbackPrediction.php', - 'Prophecy\\Prediction\\NoCallsPrediction' => '/phpspec-prophecy/Prophecy/Prediction/NoCallsPrediction.php', - 'Prophecy\\Prediction\\PredictionInterface' => '/phpspec-prophecy/Prophecy/Prediction/PredictionInterface.php', - 'Prophecy\\Promise\\CallbackPromise' => '/phpspec-prophecy/Prophecy/Promise/CallbackPromise.php', - 'Prophecy\\Promise\\PromiseInterface' => '/phpspec-prophecy/Prophecy/Promise/PromiseInterface.php', - 'Prophecy\\Promise\\ReturnArgumentPromise' => '/phpspec-prophecy/Prophecy/Promise/ReturnArgumentPromise.php', - 'Prophecy\\Promise\\ReturnPromise' => '/phpspec-prophecy/Prophecy/Promise/ReturnPromise.php', - 'Prophecy\\Promise\\ThrowPromise' => '/phpspec-prophecy/Prophecy/Promise/ThrowPromise.php', - 'Prophecy\\Prophecy\\MethodProphecy' => '/phpspec-prophecy/Prophecy/Prophecy/MethodProphecy.php', - 'Prophecy\\Prophecy\\ObjectProphecy' => '/phpspec-prophecy/Prophecy/Prophecy/ObjectProphecy.php', - 'Prophecy\\Prophecy\\ProphecyInterface' => '/phpspec-prophecy/Prophecy/Prophecy/ProphecyInterface.php', - 'Prophecy\\Prophecy\\ProphecySubjectInterface' => '/phpspec-prophecy/Prophecy/Prophecy/ProphecySubjectInterface.php', - 'Prophecy\\Prophecy\\Revealer' => '/phpspec-prophecy/Prophecy/Prophecy/Revealer.php', - 'Prophecy\\Prophecy\\RevealerInterface' => '/phpspec-prophecy/Prophecy/Prophecy/RevealerInterface.php', - 'Prophecy\\Prophet' => '/phpspec-prophecy/Prophecy/Prophet.php', - 'Prophecy\\Util\\ExportUtil' => '/phpspec-prophecy/Prophecy/Util/ExportUtil.php', - 'Prophecy\\Util\\StringUtil' => '/phpspec-prophecy/Prophecy/Util/StringUtil.php']; + 'PHPUnit\\Util\\Xml\\XmlException' => '/phpunit/Util/Exception/XmlException.php']; } if (isset($classes[$class])) { - require_once 'phar://phpunit-9.6.19.phar' . $classes[$class]; + require_once 'phar://phpunit-10.5.26.phar' . $classes[$class]; } }, true, false ); -foreach (['Doctrine\\Deprecations\\PHPUnit\\VerifyDeprecations' => '/doctrine-deprecations/Doctrine/Deprecations/PHPUnit/VerifyDeprecations.php', - 'PHPUnitPHAR\\DeepCopy\\DeepCopy' => '/myclabs-deep-copy/DeepCopy/DeepCopy.php', +foreach (['PHPUnitPHAR\\DeepCopy\\DeepCopy' => '/myclabs-deep-copy/DeepCopy/DeepCopy.php', 'PHPUnitPHAR\\DeepCopy\\Exception\\CloneException' => '/myclabs-deep-copy/DeepCopy/Exception/CloneException.php', 'PHPUnitPHAR\\DeepCopy\\Exception\\PropertyException' => '/myclabs-deep-copy/DeepCopy/Exception/PropertyException.php', 'PHPUnitPHAR\\DeepCopy\\Filter\\ChainableFilter' => '/myclabs-deep-copy/DeepCopy/Filter/ChainableFilter.php', @@ -1320,98 +1557,6 @@ foreach (['Doctrine\\Deprecations\\PHPUnit\\VerifyDeprecations' => '/doctrine-de 'PHPUnitPHAR\\DeepCopy\\TypeFilter\\Spl\\SplDoublyLinkedListFilter' => '/myclabs-deep-copy/DeepCopy/TypeFilter/Spl/SplDoublyLinkedListFilter.php', 'PHPUnitPHAR\\DeepCopy\\TypeFilter\\TypeFilter' => '/myclabs-deep-copy/DeepCopy/TypeFilter/TypeFilter.php', 'PHPUnitPHAR\\DeepCopy\\TypeMatcher\\TypeMatcher' => '/myclabs-deep-copy/DeepCopy/TypeMatcher/TypeMatcher.php', - 'PHPUnitPHAR\\Doctrine\\Deprecations\\Deprecation' => '/doctrine-deprecations/Doctrine/Deprecations/Deprecation.php', - 'PHPUnitPHAR\\Doctrine\\Instantiator\\Exception\\ExceptionInterface' => '/doctrine-instantiator/Doctrine/Instantiator/Exception/ExceptionInterface.php', - 'PHPUnitPHAR\\Doctrine\\Instantiator\\Exception\\InvalidArgumentException' => '/doctrine-instantiator/Doctrine/Instantiator/Exception/InvalidArgumentException.php', - 'PHPUnitPHAR\\Doctrine\\Instantiator\\Exception\\UnexpectedValueException' => '/doctrine-instantiator/Doctrine/Instantiator/Exception/UnexpectedValueException.php', - 'PHPUnitPHAR\\Doctrine\\Instantiator\\Instantiator' => '/doctrine-instantiator/Doctrine/Instantiator/Instantiator.php', - 'PHPUnitPHAR\\Doctrine\\Instantiator\\InstantiatorInterface' => '/doctrine-instantiator/Doctrine/Instantiator/InstantiatorInterface.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\AbstractNodeVisitor' => '/phpstan-phpdoc-parser/Ast/AbstractNodeVisitor.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\Attribute' => '/phpstan-phpdoc-parser/Ast/Attribute.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\ConstExpr\\ConstExprArrayItemNode' => '/phpstan-phpdoc-parser/Ast/ConstExpr/ConstExprArrayItemNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\ConstExpr\\ConstExprArrayNode' => '/phpstan-phpdoc-parser/Ast/ConstExpr/ConstExprArrayNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\ConstExpr\\ConstExprFalseNode' => '/phpstan-phpdoc-parser/Ast/ConstExpr/ConstExprFalseNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\ConstExpr\\ConstExprFloatNode' => '/phpstan-phpdoc-parser/Ast/ConstExpr/ConstExprFloatNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\ConstExpr\\ConstExprIntegerNode' => '/phpstan-phpdoc-parser/Ast/ConstExpr/ConstExprIntegerNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\ConstExpr\\ConstExprNode' => '/phpstan-phpdoc-parser/Ast/ConstExpr/ConstExprNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\ConstExpr\\ConstExprNullNode' => '/phpstan-phpdoc-parser/Ast/ConstExpr/ConstExprNullNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\ConstExpr\\ConstExprStringNode' => '/phpstan-phpdoc-parser/Ast/ConstExpr/ConstExprStringNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\ConstExpr\\ConstExprTrueNode' => '/phpstan-phpdoc-parser/Ast/ConstExpr/ConstExprTrueNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\ConstExpr\\ConstFetchNode' => '/phpstan-phpdoc-parser/Ast/ConstExpr/ConstFetchNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\ConstExpr\\DoctrineConstExprStringNode' => '/phpstan-phpdoc-parser/Ast/ConstExpr/DoctrineConstExprStringNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\ConstExpr\\QuoteAwareConstExprStringNode' => '/phpstan-phpdoc-parser/Ast/ConstExpr/QuoteAwareConstExprStringNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\Node' => '/phpstan-phpdoc-parser/Ast/Node.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\NodeAttributes' => '/phpstan-phpdoc-parser/Ast/NodeAttributes.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\NodeTraverser' => '/phpstan-phpdoc-parser/Ast/NodeTraverser.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\NodeVisitor' => '/phpstan-phpdoc-parser/Ast/NodeVisitor.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\NodeVisitor\\CloningVisitor' => '/phpstan-phpdoc-parser/Ast/NodeVisitor/CloningVisitor.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\AssertTagMethodValueNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/AssertTagMethodValueNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\AssertTagPropertyValueNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/AssertTagPropertyValueNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\AssertTagValueNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/AssertTagValueNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\DeprecatedTagValueNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/DeprecatedTagValueNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\Doctrine\\DoctrineAnnotation' => '/phpstan-phpdoc-parser/Ast/PhpDoc/Doctrine/DoctrineAnnotation.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\Doctrine\\DoctrineArgument' => '/phpstan-phpdoc-parser/Ast/PhpDoc/Doctrine/DoctrineArgument.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\Doctrine\\DoctrineArray' => '/phpstan-phpdoc-parser/Ast/PhpDoc/Doctrine/DoctrineArray.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\Doctrine\\DoctrineArrayItem' => '/phpstan-phpdoc-parser/Ast/PhpDoc/Doctrine/DoctrineArrayItem.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\Doctrine\\DoctrineTagValueNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/Doctrine/DoctrineTagValueNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\ExtendsTagValueNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/ExtendsTagValueNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\GenericTagValueNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/GenericTagValueNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\ImplementsTagValueNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/ImplementsTagValueNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\InvalidTagValueNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/InvalidTagValueNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\MethodTagValueNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/MethodTagValueNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\MethodTagValueParameterNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/MethodTagValueParameterNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\MixinTagValueNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/MixinTagValueNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\ParamClosureThisTagValueNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/ParamClosureThisTagValueNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\ParamImmediatelyInvokedCallableTagValueNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/ParamImmediatelyInvokedCallableTagValueNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\ParamLaterInvokedCallableTagValueNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/ParamLaterInvokedCallableTagValueNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\ParamOutTagValueNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/ParamOutTagValueNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\ParamTagValueNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/ParamTagValueNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\PhpDocChildNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/PhpDocChildNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\PhpDocNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/PhpDocNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\PhpDocTagNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/PhpDocTagNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\PhpDocTagValueNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/PhpDocTagValueNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\PhpDocTextNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/PhpDocTextNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\PropertyTagValueNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/PropertyTagValueNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\RequireExtendsTagValueNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/RequireExtendsTagValueNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\RequireImplementsTagValueNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/RequireImplementsTagValueNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\ReturnTagValueNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/ReturnTagValueNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\SelfOutTagValueNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/SelfOutTagValueNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\TemplateTagValueNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/TemplateTagValueNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\ThrowsTagValueNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/ThrowsTagValueNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\TypeAliasImportTagValueNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/TypeAliasImportTagValueNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\TypeAliasTagValueNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/TypeAliasTagValueNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\TypelessParamTagValueNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/TypelessParamTagValueNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\UsesTagValueNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/UsesTagValueNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\PhpDoc\\VarTagValueNode' => '/phpstan-phpdoc-parser/Ast/PhpDoc/VarTagValueNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\Type\\ArrayShapeItemNode' => '/phpstan-phpdoc-parser/Ast/Type/ArrayShapeItemNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\Type\\ArrayShapeNode' => '/phpstan-phpdoc-parser/Ast/Type/ArrayShapeNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\Type\\ArrayTypeNode' => '/phpstan-phpdoc-parser/Ast/Type/ArrayTypeNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\Type\\CallableTypeNode' => '/phpstan-phpdoc-parser/Ast/Type/CallableTypeNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\Type\\CallableTypeParameterNode' => '/phpstan-phpdoc-parser/Ast/Type/CallableTypeParameterNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\Type\\ConditionalTypeForParameterNode' => '/phpstan-phpdoc-parser/Ast/Type/ConditionalTypeForParameterNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\Type\\ConditionalTypeNode' => '/phpstan-phpdoc-parser/Ast/Type/ConditionalTypeNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\Type\\ConstTypeNode' => '/phpstan-phpdoc-parser/Ast/Type/ConstTypeNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\Type\\GenericTypeNode' => '/phpstan-phpdoc-parser/Ast/Type/GenericTypeNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\Type\\IdentifierTypeNode' => '/phpstan-phpdoc-parser/Ast/Type/IdentifierTypeNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\Type\\IntersectionTypeNode' => '/phpstan-phpdoc-parser/Ast/Type/IntersectionTypeNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\Type\\InvalidTypeNode' => '/phpstan-phpdoc-parser/Ast/Type/InvalidTypeNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\Type\\NullableTypeNode' => '/phpstan-phpdoc-parser/Ast/Type/NullableTypeNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\Type\\ObjectShapeItemNode' => '/phpstan-phpdoc-parser/Ast/Type/ObjectShapeItemNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\Type\\ObjectShapeNode' => '/phpstan-phpdoc-parser/Ast/Type/ObjectShapeNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\Type\\OffsetAccessTypeNode' => '/phpstan-phpdoc-parser/Ast/Type/OffsetAccessTypeNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\Type\\ThisTypeNode' => '/phpstan-phpdoc-parser/Ast/Type/ThisTypeNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\Type\\TypeNode' => '/phpstan-phpdoc-parser/Ast/Type/TypeNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Ast\\Type\\UnionTypeNode' => '/phpstan-phpdoc-parser/Ast/Type/UnionTypeNode.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Lexer\\Lexer' => '/phpstan-phpdoc-parser/Lexer/Lexer.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Parser\\ConstExprParser' => '/phpstan-phpdoc-parser/Parser/ConstExprParser.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Parser\\ParserException' => '/phpstan-phpdoc-parser/Parser/ParserException.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Parser\\PhpDocParser' => '/phpstan-phpdoc-parser/Parser/PhpDocParser.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Parser\\StringUnescaper' => '/phpstan-phpdoc-parser/Parser/StringUnescaper.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Parser\\TokenIterator' => '/phpstan-phpdoc-parser/Parser/TokenIterator.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Parser\\TypeParser' => '/phpstan-phpdoc-parser/Parser/TypeParser.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Printer\\DiffElem' => '/phpstan-phpdoc-parser/Printer/DiffElem.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Printer\\Differ' => '/phpstan-phpdoc-parser/Printer/Differ.php', - 'PHPUnitPHAR\\PHPStan\\PhpDocParser\\Printer\\Printer' => '/phpstan-phpdoc-parser/Printer/Printer.php', 'PHPUnitPHAR\\PharIo\\Manifest\\Application' => '/phar-io-manifest/values/Application.php', 'PHPUnitPHAR\\PharIo\\Manifest\\ApplicationName' => '/phar-io-manifest/values/ApplicationName.php', 'PHPUnitPHAR\\PharIo\\Manifest\\Author' => '/phar-io-manifest/values/Author.php', @@ -1514,24 +1659,22 @@ foreach (['Doctrine\\Deprecations\\PHPUnit\\VerifyDeprecations' => '/doctrine-de 'PHPUnitPHAR\\PhpParser\\Internal\\DiffElem' => '/nikic-php-parser/PhpParser/Internal/DiffElem.php', 'PHPUnitPHAR\\PhpParser\\Internal\\Differ' => '/nikic-php-parser/PhpParser/Internal/Differ.php', 'PHPUnitPHAR\\PhpParser\\Internal\\PrintableNewAnonClassNode' => '/nikic-php-parser/PhpParser/Internal/PrintableNewAnonClassNode.php', + 'PHPUnitPHAR\\PhpParser\\Internal\\TokenPolyfill' => '/nikic-php-parser/PhpParser/Internal/TokenPolyfill.php', 'PHPUnitPHAR\\PhpParser\\Internal\\TokenStream' => '/nikic-php-parser/PhpParser/Internal/TokenStream.php', 'PHPUnitPHAR\\PhpParser\\JsonDecoder' => '/nikic-php-parser/PhpParser/JsonDecoder.php', 'PHPUnitPHAR\\PhpParser\\Lexer' => '/nikic-php-parser/PhpParser/Lexer.php', 'PHPUnitPHAR\\PhpParser\\Lexer\\Emulative' => '/nikic-php-parser/PhpParser/Lexer/Emulative.php', 'PHPUnitPHAR\\PhpParser\\Lexer\\TokenEmulator\\AttributeEmulator' => '/nikic-php-parser/PhpParser/Lexer/TokenEmulator/AttributeEmulator.php', - 'PHPUnitPHAR\\PhpParser\\Lexer\\TokenEmulator\\CoaleseEqualTokenEmulator' => '/nikic-php-parser/PhpParser/Lexer/TokenEmulator/CoaleseEqualTokenEmulator.php', 'PHPUnitPHAR\\PhpParser\\Lexer\\TokenEmulator\\EnumTokenEmulator' => '/nikic-php-parser/PhpParser/Lexer/TokenEmulator/EnumTokenEmulator.php', 'PHPUnitPHAR\\PhpParser\\Lexer\\TokenEmulator\\ExplicitOctalEmulator' => '/nikic-php-parser/PhpParser/Lexer/TokenEmulator/ExplicitOctalEmulator.php', - 'PHPUnitPHAR\\PhpParser\\Lexer\\TokenEmulator\\FlexibleDocStringEmulator' => '/nikic-php-parser/PhpParser/Lexer/TokenEmulator/FlexibleDocStringEmulator.php', - 'PHPUnitPHAR\\PhpParser\\Lexer\\TokenEmulator\\FnTokenEmulator' => '/nikic-php-parser/PhpParser/Lexer/TokenEmulator/FnTokenEmulator.php', 'PHPUnitPHAR\\PhpParser\\Lexer\\TokenEmulator\\KeywordEmulator' => '/nikic-php-parser/PhpParser/Lexer/TokenEmulator/KeywordEmulator.php', 'PHPUnitPHAR\\PhpParser\\Lexer\\TokenEmulator\\MatchTokenEmulator' => '/nikic-php-parser/PhpParser/Lexer/TokenEmulator/MatchTokenEmulator.php', 'PHPUnitPHAR\\PhpParser\\Lexer\\TokenEmulator\\NullsafeTokenEmulator' => '/nikic-php-parser/PhpParser/Lexer/TokenEmulator/NullsafeTokenEmulator.php', - 'PHPUnitPHAR\\PhpParser\\Lexer\\TokenEmulator\\NumericLiteralSeparatorEmulator' => '/nikic-php-parser/PhpParser/Lexer/TokenEmulator/NumericLiteralSeparatorEmulator.php', 'PHPUnitPHAR\\PhpParser\\Lexer\\TokenEmulator\\ReadonlyFunctionTokenEmulator' => '/nikic-php-parser/PhpParser/Lexer/TokenEmulator/ReadonlyFunctionTokenEmulator.php', 'PHPUnitPHAR\\PhpParser\\Lexer\\TokenEmulator\\ReadonlyTokenEmulator' => '/nikic-php-parser/PhpParser/Lexer/TokenEmulator/ReadonlyTokenEmulator.php', 'PHPUnitPHAR\\PhpParser\\Lexer\\TokenEmulator\\ReverseEmulator' => '/nikic-php-parser/PhpParser/Lexer/TokenEmulator/ReverseEmulator.php', 'PHPUnitPHAR\\PhpParser\\Lexer\\TokenEmulator\\TokenEmulator' => '/nikic-php-parser/PhpParser/Lexer/TokenEmulator/TokenEmulator.php', + 'PHPUnitPHAR\\PhpParser\\Modifiers' => '/nikic-php-parser/PhpParser/Modifiers.php', 'PHPUnitPHAR\\PhpParser\\NameContext' => '/nikic-php-parser/PhpParser/NameContext.php', 'PHPUnitPHAR\\PhpParser\\Node' => '/nikic-php-parser/PhpParser/Node.php', 'PHPUnitPHAR\\PhpParser\\NodeAbstract' => '/nikic-php-parser/PhpParser/NodeAbstract.php', @@ -1542,19 +1685,22 @@ foreach (['Doctrine\\Deprecations\\PHPUnit\\VerifyDeprecations' => '/doctrine-de 'PHPUnitPHAR\\PhpParser\\NodeVisitor' => '/nikic-php-parser/PhpParser/NodeVisitor.php', 'PHPUnitPHAR\\PhpParser\\NodeVisitorAbstract' => '/nikic-php-parser/PhpParser/NodeVisitorAbstract.php', 'PHPUnitPHAR\\PhpParser\\NodeVisitor\\CloningVisitor' => '/nikic-php-parser/PhpParser/NodeVisitor/CloningVisitor.php', + 'PHPUnitPHAR\\PhpParser\\NodeVisitor\\CommentAnnotatingVisitor' => '/nikic-php-parser/PhpParser/NodeVisitor/CommentAnnotatingVisitor.php', 'PHPUnitPHAR\\PhpParser\\NodeVisitor\\FindingVisitor' => '/nikic-php-parser/PhpParser/NodeVisitor/FindingVisitor.php', 'PHPUnitPHAR\\PhpParser\\NodeVisitor\\FirstFindingVisitor' => '/nikic-php-parser/PhpParser/NodeVisitor/FirstFindingVisitor.php', 'PHPUnitPHAR\\PhpParser\\NodeVisitor\\NameResolver' => '/nikic-php-parser/PhpParser/NodeVisitor/NameResolver.php', 'PHPUnitPHAR\\PhpParser\\NodeVisitor\\NodeConnectingVisitor' => '/nikic-php-parser/PhpParser/NodeVisitor/NodeConnectingVisitor.php', 'PHPUnitPHAR\\PhpParser\\NodeVisitor\\ParentConnectingVisitor' => '/nikic-php-parser/PhpParser/NodeVisitor/ParentConnectingVisitor.php', 'PHPUnitPHAR\\PhpParser\\Node\\Arg' => '/nikic-php-parser/PhpParser/Node/Arg.php', + 'PHPUnitPHAR\\PhpParser\\Node\\ArrayItem' => '/nikic-php-parser/PhpParser/Node/ArrayItem.php', 'PHPUnitPHAR\\PhpParser\\Node\\Attribute' => '/nikic-php-parser/PhpParser/Node/Attribute.php', 'PHPUnitPHAR\\PhpParser\\Node\\AttributeGroup' => '/nikic-php-parser/PhpParser/Node/AttributeGroup.php', + 'PHPUnitPHAR\\PhpParser\\Node\\ClosureUse' => '/nikic-php-parser/PhpParser/Node/ClosureUse.php', 'PHPUnitPHAR\\PhpParser\\Node\\ComplexType' => '/nikic-php-parser/PhpParser/Node/ComplexType.php', 'PHPUnitPHAR\\PhpParser\\Node\\Const_' => '/nikic-php-parser/PhpParser/Node/Const_.php', + 'PHPUnitPHAR\\PhpParser\\Node\\DeclareItem' => '/nikic-php-parser/PhpParser/Node/DeclareItem.php', 'PHPUnitPHAR\\PhpParser\\Node\\Expr' => '/nikic-php-parser/PhpParser/Node/Expr.php', 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\ArrayDimFetch' => '/nikic-php-parser/PhpParser/Node/Expr/ArrayDimFetch.php', - 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\ArrayItem' => '/nikic-php-parser/PhpParser/Node/Expr/ArrayItem.php', 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\Array_' => '/nikic-php-parser/PhpParser/Node/Expr/Array_.php', 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\ArrowFunction' => '/nikic-php-parser/PhpParser/Node/Expr/ArrowFunction.php', 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\Assign' => '/nikic-php-parser/PhpParser/Node/Expr/Assign.php', @@ -1615,7 +1761,6 @@ foreach (['Doctrine\\Deprecations\\PHPUnit\\VerifyDeprecations' => '/doctrine-de 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\ClassConstFetch' => '/nikic-php-parser/PhpParser/Node/Expr/ClassConstFetch.php', 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\Clone_' => '/nikic-php-parser/PhpParser/Node/Expr/Clone_.php', 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\Closure' => '/nikic-php-parser/PhpParser/Node/Expr/Closure.php', - 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\ClosureUse' => '/nikic-php-parser/PhpParser/Node/Expr/ClosureUse.php', 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\ConstFetch' => '/nikic-php-parser/PhpParser/Node/Expr/ConstFetch.php', 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\Empty_' => '/nikic-php-parser/PhpParser/Node/Expr/Empty_.php', 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\Error' => '/nikic-php-parser/PhpParser/Node/Expr/Error.php', @@ -1650,6 +1795,7 @@ foreach (['Doctrine\\Deprecations\\PHPUnit\\VerifyDeprecations' => '/doctrine-de 'PHPUnitPHAR\\PhpParser\\Node\\Expr\\Yield_' => '/nikic-php-parser/PhpParser/Node/Expr/Yield_.php', 'PHPUnitPHAR\\PhpParser\\Node\\FunctionLike' => '/nikic-php-parser/PhpParser/Node/FunctionLike.php', 'PHPUnitPHAR\\PhpParser\\Node\\Identifier' => '/nikic-php-parser/PhpParser/Node/Identifier.php', + 'PHPUnitPHAR\\PhpParser\\Node\\InterpolatedStringPart' => '/nikic-php-parser/PhpParser/Node/InterpolatedStringPart.php', 'PHPUnitPHAR\\PhpParser\\Node\\IntersectionType' => '/nikic-php-parser/PhpParser/Node/IntersectionType.php', 'PHPUnitPHAR\\PhpParser\\Node\\MatchArm' => '/nikic-php-parser/PhpParser/Node/MatchArm.php', 'PHPUnitPHAR\\PhpParser\\Node\\Name' => '/nikic-php-parser/PhpParser/Node/Name.php', @@ -1657,11 +1803,11 @@ foreach (['Doctrine\\Deprecations\\PHPUnit\\VerifyDeprecations' => '/doctrine-de 'PHPUnitPHAR\\PhpParser\\Node\\Name\\Relative' => '/nikic-php-parser/PhpParser/Node/Name/Relative.php', 'PHPUnitPHAR\\PhpParser\\Node\\NullableType' => '/nikic-php-parser/PhpParser/Node/NullableType.php', 'PHPUnitPHAR\\PhpParser\\Node\\Param' => '/nikic-php-parser/PhpParser/Node/Param.php', + 'PHPUnitPHAR\\PhpParser\\Node\\PropertyItem' => '/nikic-php-parser/PhpParser/Node/PropertyItem.php', 'PHPUnitPHAR\\PhpParser\\Node\\Scalar' => '/nikic-php-parser/PhpParser/Node/Scalar.php', - 'PHPUnitPHAR\\PhpParser\\Node\\Scalar\\DNumber' => '/nikic-php-parser/PhpParser/Node/Scalar/DNumber.php', - 'PHPUnitPHAR\\PhpParser\\Node\\Scalar\\Encapsed' => '/nikic-php-parser/PhpParser/Node/Scalar/Encapsed.php', - 'PHPUnitPHAR\\PhpParser\\Node\\Scalar\\EncapsedStringPart' => '/nikic-php-parser/PhpParser/Node/Scalar/EncapsedStringPart.php', - 'PHPUnitPHAR\\PhpParser\\Node\\Scalar\\LNumber' => '/nikic-php-parser/PhpParser/Node/Scalar/LNumber.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Scalar\\Float_' => '/nikic-php-parser/PhpParser/Node/Scalar/Float_.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Scalar\\Int_' => '/nikic-php-parser/PhpParser/Node/Scalar/Int_.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Scalar\\InterpolatedString' => '/nikic-php-parser/PhpParser/Node/Scalar/InterpolatedString.php', 'PHPUnitPHAR\\PhpParser\\Node\\Scalar\\MagicConst' => '/nikic-php-parser/PhpParser/Node/Scalar/MagicConst.php', 'PHPUnitPHAR\\PhpParser\\Node\\Scalar\\MagicConst\\Class_' => '/nikic-php-parser/PhpParser/Node/Scalar/MagicConst/Class_.php', 'PHPUnitPHAR\\PhpParser\\Node\\Scalar\\MagicConst\\Dir' => '/nikic-php-parser/PhpParser/Node/Scalar/MagicConst/Dir.php', @@ -1672,7 +1818,9 @@ foreach (['Doctrine\\Deprecations\\PHPUnit\\VerifyDeprecations' => '/doctrine-de 'PHPUnitPHAR\\PhpParser\\Node\\Scalar\\MagicConst\\Namespace_' => '/nikic-php-parser/PhpParser/Node/Scalar/MagicConst/Namespace_.php', 'PHPUnitPHAR\\PhpParser\\Node\\Scalar\\MagicConst\\Trait_' => '/nikic-php-parser/PhpParser/Node/Scalar/MagicConst/Trait_.php', 'PHPUnitPHAR\\PhpParser\\Node\\Scalar\\String_' => '/nikic-php-parser/PhpParser/Node/Scalar/String_.php', + 'PHPUnitPHAR\\PhpParser\\Node\\StaticVar' => '/nikic-php-parser/PhpParser/Node/StaticVar.php', 'PHPUnitPHAR\\PhpParser\\Node\\Stmt' => '/nikic-php-parser/PhpParser/Node/Stmt.php', + 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\Block' => '/nikic-php-parser/PhpParser/Node/Stmt/Block.php', 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\Break_' => '/nikic-php-parser/PhpParser/Node/Stmt/Break_.php', 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\Case_' => '/nikic-php-parser/PhpParser/Node/Stmt/Case_.php', 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\Catch_' => '/nikic-php-parser/PhpParser/Node/Stmt/Catch_.php', @@ -1682,7 +1830,6 @@ foreach (['Doctrine\\Deprecations\\PHPUnit\\VerifyDeprecations' => '/doctrine-de 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\Class_' => '/nikic-php-parser/PhpParser/Node/Stmt/Class_.php', 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\Const_' => '/nikic-php-parser/PhpParser/Node/Stmt/Const_.php', 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\Continue_' => '/nikic-php-parser/PhpParser/Node/Stmt/Continue_.php', - 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\DeclareDeclare' => '/nikic-php-parser/PhpParser/Node/Stmt/DeclareDeclare.php', 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\Declare_' => '/nikic-php-parser/PhpParser/Node/Stmt/Declare_.php', 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\Do_' => '/nikic-php-parser/PhpParser/Node/Stmt/Do_.php', 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\Echo_' => '/nikic-php-parser/PhpParser/Node/Stmt/Echo_.php', @@ -1706,12 +1853,9 @@ foreach (['Doctrine\\Deprecations\\PHPUnit\\VerifyDeprecations' => '/doctrine-de 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\Namespace_' => '/nikic-php-parser/PhpParser/Node/Stmt/Namespace_.php', 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\Nop' => '/nikic-php-parser/PhpParser/Node/Stmt/Nop.php', 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\Property' => '/nikic-php-parser/PhpParser/Node/Stmt/Property.php', - 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\PropertyProperty' => '/nikic-php-parser/PhpParser/Node/Stmt/PropertyProperty.php', 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\Return_' => '/nikic-php-parser/PhpParser/Node/Stmt/Return_.php', - 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\StaticVar' => '/nikic-php-parser/PhpParser/Node/Stmt/StaticVar.php', 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\Static_' => '/nikic-php-parser/PhpParser/Node/Stmt/Static_.php', 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\Switch_' => '/nikic-php-parser/PhpParser/Node/Stmt/Switch_.php', - 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\Throw_' => '/nikic-php-parser/PhpParser/Node/Stmt/Throw_.php', 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\TraitUse' => '/nikic-php-parser/PhpParser/Node/Stmt/TraitUse.php', 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\TraitUseAdaptation' => '/nikic-php-parser/PhpParser/Node/Stmt/TraitUseAdaptation.php', 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\TraitUseAdaptation\\Alias' => '/nikic-php-parser/PhpParser/Node/Stmt/TraitUseAdaptation/Alias.php', @@ -1719,21 +1863,22 @@ foreach (['Doctrine\\Deprecations\\PHPUnit\\VerifyDeprecations' => '/doctrine-de 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\Trait_' => '/nikic-php-parser/PhpParser/Node/Stmt/Trait_.php', 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\TryCatch' => '/nikic-php-parser/PhpParser/Node/Stmt/TryCatch.php', 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\Unset_' => '/nikic-php-parser/PhpParser/Node/Stmt/Unset_.php', - 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\UseUse' => '/nikic-php-parser/PhpParser/Node/Stmt/UseUse.php', 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\Use_' => '/nikic-php-parser/PhpParser/Node/Stmt/Use_.php', 'PHPUnitPHAR\\PhpParser\\Node\\Stmt\\While_' => '/nikic-php-parser/PhpParser/Node/Stmt/While_.php', 'PHPUnitPHAR\\PhpParser\\Node\\UnionType' => '/nikic-php-parser/PhpParser/Node/UnionType.php', + 'PHPUnitPHAR\\PhpParser\\Node\\UseItem' => '/nikic-php-parser/PhpParser/Node/UseItem.php', 'PHPUnitPHAR\\PhpParser\\Node\\VarLikeIdentifier' => '/nikic-php-parser/PhpParser/Node/VarLikeIdentifier.php', 'PHPUnitPHAR\\PhpParser\\Node\\VariadicPlaceholder' => '/nikic-php-parser/PhpParser/Node/VariadicPlaceholder.php', 'PHPUnitPHAR\\PhpParser\\Parser' => '/nikic-php-parser/PhpParser/Parser.php', 'PHPUnitPHAR\\PhpParser\\ParserAbstract' => '/nikic-php-parser/PhpParser/ParserAbstract.php', 'PHPUnitPHAR\\PhpParser\\ParserFactory' => '/nikic-php-parser/PhpParser/ParserFactory.php', - 'PHPUnitPHAR\\PhpParser\\Parser\\Multiple' => '/nikic-php-parser/PhpParser/Parser/Multiple.php', - 'PHPUnitPHAR\\PhpParser\\Parser\\Php5' => '/nikic-php-parser/PhpParser/Parser/Php5.php', 'PHPUnitPHAR\\PhpParser\\Parser\\Php7' => '/nikic-php-parser/PhpParser/Parser/Php7.php', - 'PHPUnitPHAR\\PhpParser\\Parser\\Tokens' => '/nikic-php-parser/PhpParser/Parser/Tokens.php', + 'PHPUnitPHAR\\PhpParser\\Parser\\Php8' => '/nikic-php-parser/PhpParser/Parser/Php8.php', + 'PHPUnitPHAR\\PhpParser\\PhpVersion' => '/nikic-php-parser/PhpParser/PhpVersion.php', + 'PHPUnitPHAR\\PhpParser\\PrettyPrinter' => '/nikic-php-parser/PhpParser/PrettyPrinter.php', 'PHPUnitPHAR\\PhpParser\\PrettyPrinterAbstract' => '/nikic-php-parser/PhpParser/PrettyPrinterAbstract.php', 'PHPUnitPHAR\\PhpParser\\PrettyPrinter\\Standard' => '/nikic-php-parser/PhpParser/PrettyPrinter/Standard.php', + 'PHPUnitPHAR\\PhpParser\\Token' => '/nikic-php-parser/PhpParser/Token.php', 'PHPUnitPHAR\\SebastianBergmann\\CliParser\\AmbiguousOptionException' => '/sebastian-cli-parser/exceptions/AmbiguousOptionException.php', 'PHPUnitPHAR\\SebastianBergmann\\CliParser\\Exception' => '/sebastian-cli-parser/exceptions/Exception.php', 'PHPUnitPHAR\\SebastianBergmann\\CliParser\\OptionDoesNotAllowArgumentException' => '/sebastian-cli-parser/exceptions/OptionDoesNotAllowArgumentException.php', @@ -1742,22 +1887,20 @@ foreach (['Doctrine\\Deprecations\\PHPUnit\\VerifyDeprecations' => '/doctrine-de 'PHPUnitPHAR\\SebastianBergmann\\CliParser\\UnknownOptionException' => '/sebastian-cli-parser/exceptions/UnknownOptionException.php', 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\BranchAndPathCoverageNotSupportedException' => '/php-code-coverage/Exception/BranchAndPathCoverageNotSupportedException.php', 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\CodeCoverage' => '/php-code-coverage/CodeCoverage.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Data\\ProcessedCodeCoverageData' => '/php-code-coverage/Data/ProcessedCodeCoverageData.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Data\\RawCodeCoverageData' => '/php-code-coverage/Data/RawCodeCoverageData.php', 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\DeadCodeDetectionNotSupportedException' => '/php-code-coverage/Exception/DeadCodeDetectionNotSupportedException.php', 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Driver\\Driver' => '/php-code-coverage/Driver/Driver.php', 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Driver\\PathExistsButIsNotDirectoryException' => '/php-code-coverage/Exception/PathExistsButIsNotDirectoryException.php', 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Driver\\PcovDriver' => '/php-code-coverage/Driver/PcovDriver.php', 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Driver\\PcovNotAvailableException' => '/php-code-coverage/Exception/PcovNotAvailableException.php', - 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Driver\\PhpdbgDriver' => '/php-code-coverage/Driver/PhpdbgDriver.php', - 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Driver\\PhpdbgNotAvailableException' => '/php-code-coverage/Exception/PhpdbgNotAvailableException.php', 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Driver\\Selector' => '/php-code-coverage/Driver/Selector.php', 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Driver\\WriteOperationFailedException' => '/php-code-coverage/Exception/WriteOperationFailedException.php', - 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Driver\\WrongXdebugVersionException' => '/php-code-coverage/Exception/WrongXdebugVersionException.php', - 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Driver\\Xdebug2Driver' => '/php-code-coverage/Driver/Xdebug2Driver.php', - 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Driver\\Xdebug2NotEnabledException' => '/php-code-coverage/Exception/Xdebug2NotEnabledException.php', - 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Driver\\Xdebug3Driver' => '/php-code-coverage/Driver/Xdebug3Driver.php', - 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Driver\\Xdebug3NotEnabledException' => '/php-code-coverage/Exception/Xdebug3NotEnabledException.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Driver\\XdebugDriver' => '/php-code-coverage/Driver/XdebugDriver.php', 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Driver\\XdebugNotAvailableException' => '/php-code-coverage/Exception/XdebugNotAvailableException.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Driver\\XdebugNotEnabledException' => '/php-code-coverage/Exception/XdebugNotEnabledException.php', 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Exception' => '/php-code-coverage/Exception/Exception.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\FileCouldNotBeWrittenException' => '/php-code-coverage/Exception/FileCouldNotBeWrittenException.php', 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Filter' => '/php-code-coverage/Filter.php', 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\InvalidArgumentException' => '/php-code-coverage/Exception/InvalidArgumentException.php', 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\NoCodeCoverageDriverAvailableException' => '/php-code-coverage/Exception/NoCodeCoverageDriverAvailableException.php', @@ -1769,13 +1912,13 @@ foreach (['Doctrine\\Deprecations\\PHPUnit\\VerifyDeprecations' => '/doctrine-de 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Node\\File' => '/php-code-coverage/Node/File.php', 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Node\\Iterator' => '/php-code-coverage/Node/Iterator.php', 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\ParserException' => '/php-code-coverage/Exception/ParserException.php', - 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\ProcessedCodeCoverageData' => '/php-code-coverage/ProcessedCodeCoverageData.php', - 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\RawCodeCoverageData' => '/php-code-coverage/RawCodeCoverageData.php', 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\ReflectionException' => '/php-code-coverage/Exception/ReflectionException.php', 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\ReportAlreadyFinalizedException' => '/php-code-coverage/Exception/ReportAlreadyFinalizedException.php', 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Report\\Clover' => '/php-code-coverage/Report/Clover.php', 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Report\\Cobertura' => '/php-code-coverage/Report/Cobertura.php', 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Report\\Crap4j' => '/php-code-coverage/Report/Crap4j.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Report\\Html\\Colors' => '/php-code-coverage/Report/Html/Colors.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Report\\Html\\CustomCssFile' => '/php-code-coverage/Report/Html/CustomCssFile.php', 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Report\\Html\\Dashboard' => '/php-code-coverage/Report/Html/Renderer/Dashboard.php', 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Report\\Html\\Directory' => '/php-code-coverage/Report/Html/Renderer/Directory.php', 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Report\\Html\\Facade' => '/php-code-coverage/Report/Html/Facade.php', @@ -1783,6 +1926,7 @@ foreach (['Doctrine\\Deprecations\\PHPUnit\\VerifyDeprecations' => '/doctrine-de 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Report\\Html\\Renderer' => '/php-code-coverage/Report/Html/Renderer.php', 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Report\\PHP' => '/php-code-coverage/Report/PHP.php', 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Report\\Text' => '/php-code-coverage/Report/Text.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Report\\Thresholds' => '/php-code-coverage/Report/Thresholds.php', 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Report\\Xml\\BuildInformation' => '/php-code-coverage/Report/Xml/BuildInformation.php', 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Report\\Xml\\Coverage' => '/php-code-coverage/Report/Xml/Coverage.php', 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Report\\Xml\\Directory' => '/php-code-coverage/Report/Xml/Directory.php', @@ -1805,6 +1949,17 @@ foreach (['Doctrine\\Deprecations\\PHPUnit\\VerifyDeprecations' => '/doctrine-de 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\StaticAnalysis\\IgnoredLinesFindingVisitor' => '/php-code-coverage/StaticAnalysis/IgnoredLinesFindingVisitor.php', 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\StaticAnalysis\\ParsingFileAnalyser' => '/php-code-coverage/StaticAnalysis/ParsingFileAnalyser.php', 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\TestIdMissingException' => '/php-code-coverage/Exception/TestIdMissingException.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Test\\TestSize\\Known' => '/php-code-coverage/TestSize/Known.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Test\\TestSize\\Large' => '/php-code-coverage/TestSize/Large.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Test\\TestSize\\Medium' => '/php-code-coverage/TestSize/Medium.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Test\\TestSize\\Small' => '/php-code-coverage/TestSize/Small.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Test\\TestSize\\TestSize' => '/php-code-coverage/TestSize/TestSize.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Test\\TestSize\\Unknown' => '/php-code-coverage/TestSize/Unknown.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Test\\TestStatus\\Failure' => '/php-code-coverage/TestStatus/Failure.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Test\\TestStatus\\Known' => '/php-code-coverage/TestStatus/Known.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Test\\TestStatus\\Success' => '/php-code-coverage/TestStatus/Success.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Test\\TestStatus\\TestStatus' => '/php-code-coverage/TestStatus/TestStatus.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Test\\TestStatus\\Unknown' => '/php-code-coverage/TestStatus/Unknown.php', 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\UnintentionallyCoveredCodeException' => '/php-code-coverage/Exception/UnintentionallyCoveredCodeException.php', 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Util\\DirectoryCouldNotBeCreatedException' => '/php-code-coverage/Exception/DirectoryCouldNotBeCreatedException.php', 'PHPUnitPHAR\\SebastianBergmann\\CodeCoverage\\Util\\Filesystem' => '/php-code-coverage/Util/Filesystem.php', @@ -1818,6 +1973,7 @@ foreach (['Doctrine\\Deprecations\\PHPUnit\\VerifyDeprecations' => '/doctrine-de 'PHPUnitPHAR\\SebastianBergmann\\CodeUnit\\CodeUnitCollection' => '/sebastian-code-unit/CodeUnitCollection.php', 'PHPUnitPHAR\\SebastianBergmann\\CodeUnit\\CodeUnitCollectionIterator' => '/sebastian-code-unit/CodeUnitCollectionIterator.php', 'PHPUnitPHAR\\SebastianBergmann\\CodeUnit\\Exception' => '/sebastian-code-unit/exceptions/Exception.php', + 'PHPUnitPHAR\\SebastianBergmann\\CodeUnit\\FileUnit' => '/sebastian-code-unit/FileUnit.php', 'PHPUnitPHAR\\SebastianBergmann\\CodeUnit\\FunctionUnit' => '/sebastian-code-unit/FunctionUnit.php', 'PHPUnitPHAR\\SebastianBergmann\\CodeUnit\\InterfaceMethodUnit' => '/sebastian-code-unit/InterfaceMethodUnit.php', 'PHPUnitPHAR\\SebastianBergmann\\CodeUnit\\InterfaceUnit' => '/sebastian-code-unit/InterfaceUnit.php', @@ -1832,7 +1988,6 @@ foreach (['Doctrine\\Deprecations\\PHPUnit\\VerifyDeprecations' => '/doctrine-de 'PHPUnitPHAR\\SebastianBergmann\\Comparator\\ComparisonFailure' => '/sebastian-comparator/ComparisonFailure.php', 'PHPUnitPHAR\\SebastianBergmann\\Comparator\\DOMNodeComparator' => '/sebastian-comparator/DOMNodeComparator.php', 'PHPUnitPHAR\\SebastianBergmann\\Comparator\\DateTimeComparator' => '/sebastian-comparator/DateTimeComparator.php', - 'PHPUnitPHAR\\SebastianBergmann\\Comparator\\DoubleComparator' => '/sebastian-comparator/DoubleComparator.php', 'PHPUnitPHAR\\SebastianBergmann\\Comparator\\Exception' => '/sebastian-comparator/exceptions/Exception.php', 'PHPUnitPHAR\\SebastianBergmann\\Comparator\\ExceptionComparator' => '/sebastian-comparator/ExceptionComparator.php', 'PHPUnitPHAR\\SebastianBergmann\\Comparator\\Factory' => '/sebastian-comparator/Factory.php', @@ -1869,9 +2024,9 @@ foreach (['Doctrine\\Deprecations\\PHPUnit\\VerifyDeprecations' => '/doctrine-de 'PHPUnitPHAR\\SebastianBergmann\\Diff\\Parser' => '/sebastian-diff/Parser.php', 'PHPUnitPHAR\\SebastianBergmann\\Diff\\TimeEfficientLongestCommonSubsequenceCalculator' => '/sebastian-diff/TimeEfficientLongestCommonSubsequenceCalculator.php', 'PHPUnitPHAR\\SebastianBergmann\\Environment\\Console' => '/sebastian-environment/Console.php', - 'PHPUnitPHAR\\SebastianBergmann\\Environment\\OperatingSystem' => '/sebastian-environment/OperatingSystem.php', 'PHPUnitPHAR\\SebastianBergmann\\Environment\\Runtime' => '/sebastian-environment/Runtime.php', 'PHPUnitPHAR\\SebastianBergmann\\Exporter\\Exporter' => '/sebastian-exporter/Exporter.php', + 'PHPUnitPHAR\\SebastianBergmann\\FileIterator\\ExcludeIterator' => '/php-file-iterator/ExcludeIterator.php', 'PHPUnitPHAR\\SebastianBergmann\\FileIterator\\Facade' => '/php-file-iterator/Facade.php', 'PHPUnitPHAR\\SebastianBergmann\\FileIterator\\Factory' => '/php-file-iterator/Factory.php', 'PHPUnitPHAR\\SebastianBergmann\\FileIterator\\Iterator' => '/php-file-iterator/Iterator.php', @@ -1893,15 +2048,8 @@ foreach (['Doctrine\\Deprecations\\PHPUnit\\VerifyDeprecations' => '/doctrine-de 'PHPUnitPHAR\\SebastianBergmann\\LinesOfCode\\NegativeValueException' => '/sebastian-lines-of-code/Exception/NegativeValueException.php', 'PHPUnitPHAR\\SebastianBergmann\\LinesOfCode\\RuntimeException' => '/sebastian-lines-of-code/Exception/RuntimeException.php', 'PHPUnitPHAR\\SebastianBergmann\\ObjectEnumerator\\Enumerator' => '/sebastian-object-enumerator/Enumerator.php', - 'PHPUnitPHAR\\SebastianBergmann\\ObjectEnumerator\\Exception' => '/sebastian-object-enumerator/Exception.php', - 'PHPUnitPHAR\\SebastianBergmann\\ObjectEnumerator\\InvalidArgumentException' => '/sebastian-object-enumerator/InvalidArgumentException.php', - 'PHPUnitPHAR\\SebastianBergmann\\ObjectReflector\\Exception' => '/sebastian-object-reflector/Exception.php', - 'PHPUnitPHAR\\SebastianBergmann\\ObjectReflector\\InvalidArgumentException' => '/sebastian-object-reflector/InvalidArgumentException.php', 'PHPUnitPHAR\\SebastianBergmann\\ObjectReflector\\ObjectReflector' => '/sebastian-object-reflector/ObjectReflector.php', 'PHPUnitPHAR\\SebastianBergmann\\RecursionContext\\Context' => '/sebastian-recursion-context/Context.php', - 'PHPUnitPHAR\\SebastianBergmann\\RecursionContext\\Exception' => '/sebastian-recursion-context/Exception.php', - 'PHPUnitPHAR\\SebastianBergmann\\RecursionContext\\InvalidArgumentException' => '/sebastian-recursion-context/InvalidArgumentException.php', - 'PHPUnitPHAR\\SebastianBergmann\\ResourceOperations\\ResourceOperations' => '/sebastian-resource-operations/ResourceOperations.php', 'PHPUnitPHAR\\SebastianBergmann\\Template\\Exception' => '/php-text-template/exceptions/Exception.php', 'PHPUnitPHAR\\SebastianBergmann\\Template\\InvalidArgumentException' => '/php-text-template/exceptions/InvalidArgumentException.php', 'PHPUnitPHAR\\SebastianBergmann\\Template\\RuntimeException' => '/php-text-template/exceptions/RuntimeException.php', @@ -1942,135 +2090,296 @@ foreach (['Doctrine\\Deprecations\\PHPUnit\\VerifyDeprecations' => '/doctrine-de 'PHPUnitPHAR\\TheSeer\\Tokenizer\\TokenCollectionException' => '/theseer-tokenizer/TokenCollectionException.php', 'PHPUnitPHAR\\TheSeer\\Tokenizer\\Tokenizer' => '/theseer-tokenizer/Tokenizer.php', 'PHPUnitPHAR\\TheSeer\\Tokenizer\\XMLSerializer' => '/theseer-tokenizer/XMLSerializer.php', - 'PHPUnitPHAR\\Webmozart\\Assert\\Assert' => '/webmozart-assert/Assert.php', - 'PHPUnitPHAR\\Webmozart\\Assert\\InvalidArgumentException' => '/webmozart-assert/InvalidArgumentException.php', - 'PHPUnitPHAR\\Webmozart\\Assert\\Mixin' => '/webmozart-assert/Mixin.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock' => '/phpdocumentor-reflection-docblock/DocBlock.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlockFactory' => '/phpdocumentor-reflection-docblock/DocBlockFactory.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlockFactoryInterface' => '/phpdocumentor-reflection-docblock/DocBlockFactoryInterface.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Description' => '/phpdocumentor-reflection-docblock/DocBlock/Description.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\DescriptionFactory' => '/phpdocumentor-reflection-docblock/DocBlock/DescriptionFactory.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\ExampleFinder' => '/phpdocumentor-reflection-docblock/DocBlock/ExampleFinder.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Serializer' => '/phpdocumentor-reflection-docblock/DocBlock/Serializer.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\StandardTagFactory' => '/phpdocumentor-reflection-docblock/DocBlock/StandardTagFactory.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Tag' => '/phpdocumentor-reflection-docblock/DocBlock/Tag.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\TagFactory' => '/phpdocumentor-reflection-docblock/DocBlock/TagFactory.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Author' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Author.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Tags\\BaseTag' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/BaseTag.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Covers' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Covers.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Deprecated' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Deprecated.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Example' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Example.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Factory\\StaticMethod' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Factory/StaticMethod.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Formatter' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Formatter.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Formatter\\AlignFormatter' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Formatter/AlignFormatter.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Formatter\\PassthroughFormatter' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Formatter/PassthroughFormatter.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Generic' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Generic.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Tags\\InvalidTag' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/InvalidTag.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Link' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Link.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Method' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Method.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Param' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Param.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Property' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Property.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Tags\\PropertyRead' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/PropertyRead.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Tags\\PropertyWrite' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/PropertyWrite.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Reference\\Fqsen' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Reference/Fqsen.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Reference\\Reference' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Reference/Reference.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Reference\\Url' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Reference/Url.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Return_' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Return_.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Tags\\See' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/See.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Since' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Since.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Source' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Source.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Tags\\TagWithType' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/TagWithType.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Throws' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Throws.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Uses' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Uses.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Var_' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Var_.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Version' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Version.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Element' => '/phpdocumentor-reflection-common/Element.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Exception\\PcreException' => '/phpdocumentor-reflection-docblock/Exception/PcreException.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\File' => '/phpdocumentor-reflection-common/File.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Fqsen' => '/phpdocumentor-reflection-common/Fqsen.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\FqsenResolver' => '/phpdocumentor-type-resolver/FqsenResolver.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Location' => '/phpdocumentor-reflection-common/Location.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Project' => '/phpdocumentor-reflection-common/Project.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\ProjectFactory' => '/phpdocumentor-reflection-common/ProjectFactory.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\PseudoType' => '/phpdocumentor-type-resolver/PseudoType.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\PseudoTypes\\ArrayShape' => '/phpdocumentor-type-resolver/PseudoTypes/ArrayShape.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\PseudoTypes\\ArrayShapeItem' => '/phpdocumentor-type-resolver/PseudoTypes/ArrayShapeItem.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\PseudoTypes\\CallableString' => '/phpdocumentor-type-resolver/PseudoTypes/CallableString.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\PseudoTypes\\ConstExpression' => '/phpdocumentor-type-resolver/PseudoTypes/ConstExpression.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\PseudoTypes\\False_' => '/phpdocumentor-type-resolver/PseudoTypes/False_.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\PseudoTypes\\FloatValue' => '/phpdocumentor-type-resolver/PseudoTypes/FloatValue.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\PseudoTypes\\HtmlEscapedString' => '/phpdocumentor-type-resolver/PseudoTypes/HtmlEscapedString.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\PseudoTypes\\IntegerRange' => '/phpdocumentor-type-resolver/PseudoTypes/IntegerRange.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\PseudoTypes\\IntegerValue' => '/phpdocumentor-type-resolver/PseudoTypes/IntegerValue.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\PseudoTypes\\List_' => '/phpdocumentor-type-resolver/PseudoTypes/List_.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\PseudoTypes\\LiteralString' => '/phpdocumentor-type-resolver/PseudoTypes/LiteralString.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\PseudoTypes\\LowercaseString' => '/phpdocumentor-type-resolver/PseudoTypes/LowercaseString.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\PseudoTypes\\NegativeInteger' => '/phpdocumentor-type-resolver/PseudoTypes/NegativeInteger.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\PseudoTypes\\NonEmptyList' => '/phpdocumentor-type-resolver/PseudoTypes/NonEmptyList.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\PseudoTypes\\NonEmptyLowercaseString' => '/phpdocumentor-type-resolver/PseudoTypes/NonEmptyLowercaseString.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\PseudoTypes\\NonEmptyString' => '/phpdocumentor-type-resolver/PseudoTypes/NonEmptyString.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\PseudoTypes\\NumericString' => '/phpdocumentor-type-resolver/PseudoTypes/NumericString.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\PseudoTypes\\Numeric_' => '/phpdocumentor-type-resolver/PseudoTypes/Numeric_.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\PseudoTypes\\PositiveInteger' => '/phpdocumentor-type-resolver/PseudoTypes/PositiveInteger.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\PseudoTypes\\StringValue' => '/phpdocumentor-type-resolver/PseudoTypes/StringValue.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\PseudoTypes\\TraitString' => '/phpdocumentor-type-resolver/PseudoTypes/TraitString.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\PseudoTypes\\True_' => '/phpdocumentor-type-resolver/PseudoTypes/True_.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Type' => '/phpdocumentor-type-resolver/Type.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\TypeResolver' => '/phpdocumentor-type-resolver/TypeResolver.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\AbstractList' => '/phpdocumentor-type-resolver/Types/AbstractList.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\AggregatedType' => '/phpdocumentor-type-resolver/Types/AggregatedType.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\ArrayKey' => '/phpdocumentor-type-resolver/Types/ArrayKey.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\Array_' => '/phpdocumentor-type-resolver/Types/Array_.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\Boolean' => '/phpdocumentor-type-resolver/Types/Boolean.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\CallableParameter' => '/phpdocumentor-type-resolver/Types/CallableParameter.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\Callable_' => '/phpdocumentor-type-resolver/Types/Callable_.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\ClassString' => '/phpdocumentor-type-resolver/Types/ClassString.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\Collection' => '/phpdocumentor-type-resolver/Types/Collection.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\Compound' => '/phpdocumentor-type-resolver/Types/Compound.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\Context' => '/phpdocumentor-type-resolver/Types/Context.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\ContextFactory' => '/phpdocumentor-type-resolver/Types/ContextFactory.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\Expression' => '/phpdocumentor-type-resolver/Types/Expression.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\Float_' => '/phpdocumentor-type-resolver/Types/Float_.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\Integer' => '/phpdocumentor-type-resolver/Types/Integer.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\InterfaceString' => '/phpdocumentor-type-resolver/Types/InterfaceString.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\Intersection' => '/phpdocumentor-type-resolver/Types/Intersection.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\Iterable_' => '/phpdocumentor-type-resolver/Types/Iterable_.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\Mixed_' => '/phpdocumentor-type-resolver/Types/Mixed_.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\Never_' => '/phpdocumentor-type-resolver/Types/Never_.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\Null_' => '/phpdocumentor-type-resolver/Types/Null_.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\Nullable' => '/phpdocumentor-type-resolver/Types/Nullable.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\Object_' => '/phpdocumentor-type-resolver/Types/Object_.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\Parent_' => '/phpdocumentor-type-resolver/Types/Parent_.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\Resource_' => '/phpdocumentor-type-resolver/Types/Resource_.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\Scalar' => '/phpdocumentor-type-resolver/Types/Scalar.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\Self_' => '/phpdocumentor-type-resolver/Types/Self_.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\Static_' => '/phpdocumentor-type-resolver/Types/Static_.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\String_' => '/phpdocumentor-type-resolver/Types/String_.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\This' => '/phpdocumentor-type-resolver/Types/This.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\Void_' => '/phpdocumentor-type-resolver/Types/Void_.php', - 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Utils' => '/phpdocumentor-reflection-docblock/Utils.php', + 'PHPUnit\\Event\\Application\\Finished' => '/phpunit/Event/Events/Application/Finished.php', + 'PHPUnit\\Event\\Application\\FinishedSubscriber' => '/phpunit/Event/Events/Application/FinishedSubscriber.php', + 'PHPUnit\\Event\\Application\\Started' => '/phpunit/Event/Events/Application/Started.php', + 'PHPUnit\\Event\\Application\\StartedSubscriber' => '/phpunit/Event/Events/Application/StartedSubscriber.php', + 'PHPUnit\\Event\\Code\\ClassMethod' => '/phpunit/Event/Value/ClassMethod.php', + 'PHPUnit\\Event\\Code\\ComparisonFailure' => '/phpunit/Event/Value/ComparisonFailure.php', + 'PHPUnit\\Event\\Code\\ComparisonFailureBuilder' => '/phpunit/Event/Value/ComparisonFailureBuilder.php', + 'PHPUnit\\Event\\Code\\NoTestCaseObjectOnCallStackException' => '/phpunit/Event/Exception/NoTestCaseObjectOnCallStackException.php', + 'PHPUnit\\Event\\Code\\Phpt' => '/phpunit/Event/Value/Test/Phpt.php', + 'PHPUnit\\Event\\Code\\Test' => '/phpunit/Event/Value/Test/Test.php', + 'PHPUnit\\Event\\Code\\TestCollection' => '/phpunit/Event/Value/Test/TestCollection.php', + 'PHPUnit\\Event\\Code\\TestCollectionIterator' => '/phpunit/Event/Value/Test/TestCollectionIterator.php', + 'PHPUnit\\Event\\Code\\TestDox' => '/phpunit/Event/Value/Test/TestDox.php', + 'PHPUnit\\Event\\Code\\TestDoxBuilder' => '/phpunit/Event/Value/Test/TestDoxBuilder.php', + 'PHPUnit\\Event\\Code\\TestMethod' => '/phpunit/Event/Value/Test/TestMethod.php', + 'PHPUnit\\Event\\Code\\TestMethodBuilder' => '/phpunit/Event/Value/Test/TestMethodBuilder.php', + 'PHPUnit\\Event\\Code\\Throwable' => '/phpunit/Event/Value/Throwable.php', + 'PHPUnit\\Event\\Code\\ThrowableBuilder' => '/phpunit/Event/Value/ThrowableBuilder.php', + 'PHPUnit\\Event\\CollectingDispatcher' => '/phpunit/Event/Dispatcher/CollectingDispatcher.php', + 'PHPUnit\\Event\\DeferringDispatcher' => '/phpunit/Event/Dispatcher/DeferringDispatcher.php', + 'PHPUnit\\Event\\DirectDispatcher' => '/phpunit/Event/Dispatcher/DirectDispatcher.php', + 'PHPUnit\\Event\\Dispatcher' => '/phpunit/Event/Dispatcher/Dispatcher.php', + 'PHPUnit\\Event\\DispatchingEmitter' => '/phpunit/Event/Emitter/DispatchingEmitter.php', + 'PHPUnit\\Event\\Emitter' => '/phpunit/Event/Emitter/Emitter.php', + 'PHPUnit\\Event\\Event' => '/phpunit/Event/Events/Event.php', + 'PHPUnit\\Event\\EventAlreadyAssignedException' => '/phpunit/Event/Exception/EventAlreadyAssignedException.php', + 'PHPUnit\\Event\\EventCollection' => '/phpunit/Event/Events/EventCollection.php', + 'PHPUnit\\Event\\EventCollectionIterator' => '/phpunit/Event/Events/EventCollectionIterator.php', + 'PHPUnit\\Event\\EventFacadeIsSealedException' => '/phpunit/Event/Exception/EventFacadeIsSealedException.php', + 'PHPUnit\\Event\\Exception' => '/phpunit/Event/Exception/Exception.php', + 'PHPUnit\\Event\\Facade' => '/phpunit/Event/Facade.php', + 'PHPUnit\\Event\\InvalidArgumentException' => '/phpunit/Event/Exception/InvalidArgumentException.php', + 'PHPUnit\\Event\\InvalidEventException' => '/phpunit/Event/Exception/InvalidEventException.php', + 'PHPUnit\\Event\\InvalidSubscriberException' => '/phpunit/Event/Exception/InvalidSubscriberException.php', + 'PHPUnit\\Event\\MapError' => '/phpunit/Event/Exception/MapError.php', + 'PHPUnit\\Event\\NoPreviousThrowableException' => '/phpunit/Event/Exception/NoPreviousThrowableException.php', + 'PHPUnit\\Event\\RuntimeException' => '/phpunit/Event/Exception/RuntimeException.php', + 'PHPUnit\\Event\\Runtime\\OperatingSystem' => '/phpunit/Event/Value/Runtime/OperatingSystem.php', + 'PHPUnit\\Event\\Runtime\\PHP' => '/phpunit/Event/Value/Runtime/PHP.php', + 'PHPUnit\\Event\\Runtime\\PHPUnit' => '/phpunit/Event/Value/Runtime/PHPUnit.php', + 'PHPUnit\\Event\\Runtime\\Runtime' => '/phpunit/Event/Value/Runtime/Runtime.php', + 'PHPUnit\\Event\\SubscribableDispatcher' => '/phpunit/Event/Dispatcher/SubscribableDispatcher.php', + 'PHPUnit\\Event\\Subscriber' => '/phpunit/Event/Subscriber.php', + 'PHPUnit\\Event\\SubscriberTypeAlreadyRegisteredException' => '/phpunit/Event/Exception/SubscriberTypeAlreadyRegisteredException.php', + 'PHPUnit\\Event\\Telemetry\\Duration' => '/phpunit/Event/Value/Telemetry/Duration.php', + 'PHPUnit\\Event\\Telemetry\\GarbageCollectorStatus' => '/phpunit/Event/Value/Telemetry/GarbageCollectorStatus.php', + 'PHPUnit\\Event\\Telemetry\\GarbageCollectorStatusProvider' => '/phpunit/Event/Value/Telemetry/GarbageCollectorStatusProvider.php', + 'PHPUnit\\Event\\Telemetry\\HRTime' => '/phpunit/Event/Value/Telemetry/HRTime.php', + 'PHPUnit\\Event\\Telemetry\\Info' => '/phpunit/Event/Value/Telemetry/Info.php', + 'PHPUnit\\Event\\Telemetry\\MemoryMeter' => '/phpunit/Event/Value/Telemetry/MemoryMeter.php', + 'PHPUnit\\Event\\Telemetry\\MemoryUsage' => '/phpunit/Event/Value/Telemetry/MemoryUsage.php', + 'PHPUnit\\Event\\Telemetry\\Php81GarbageCollectorStatusProvider' => '/phpunit/Event/Value/Telemetry/Php81GarbageCollectorStatusProvider.php', + 'PHPUnit\\Event\\Telemetry\\Php83GarbageCollectorStatusProvider' => '/phpunit/Event/Value/Telemetry/Php83GarbageCollectorStatusProvider.php', + 'PHPUnit\\Event\\Telemetry\\Snapshot' => '/phpunit/Event/Value/Telemetry/Snapshot.php', + 'PHPUnit\\Event\\Telemetry\\StopWatch' => '/phpunit/Event/Value/Telemetry/StopWatch.php', + 'PHPUnit\\Event\\Telemetry\\System' => '/phpunit/Event/Value/Telemetry/System.php', + 'PHPUnit\\Event\\Telemetry\\SystemMemoryMeter' => '/phpunit/Event/Value/Telemetry/SystemMemoryMeter.php', + 'PHPUnit\\Event\\Telemetry\\SystemStopWatch' => '/phpunit/Event/Value/Telemetry/SystemStopWatch.php', + 'PHPUnit\\Event\\Telemetry\\SystemStopWatchWithOffset' => '/phpunit/Event/Value/Telemetry/SystemStopWatchWithOffset.php', + 'PHPUnit\\Event\\TestData\\DataFromDataProvider' => '/phpunit/Event/Value/Test/TestData/DataFromDataProvider.php', + 'PHPUnit\\Event\\TestData\\DataFromTestDependency' => '/phpunit/Event/Value/Test/TestData/DataFromTestDependency.php', + 'PHPUnit\\Event\\TestData\\MoreThanOneDataSetFromDataProviderException' => '/phpunit/Event/Exception/MoreThanOneDataSetFromDataProviderException.php', + 'PHPUnit\\Event\\TestData\\NoDataSetFromDataProviderException' => '/phpunit/Event/Exception/NoDataSetFromDataProviderException.php', + 'PHPUnit\\Event\\TestData\\TestData' => '/phpunit/Event/Value/Test/TestData/TestData.php', + 'PHPUnit\\Event\\TestData\\TestDataCollection' => '/phpunit/Event/Value/Test/TestData/TestDataCollection.php', + 'PHPUnit\\Event\\TestData\\TestDataCollectionIterator' => '/phpunit/Event/Value/Test/TestData/TestDataCollectionIterator.php', + 'PHPUnit\\Event\\TestRunner\\BootstrapFinished' => '/phpunit/Event/Events/TestRunner/BootstrapFinished.php', + 'PHPUnit\\Event\\TestRunner\\BootstrapFinishedSubscriber' => '/phpunit/Event/Events/TestRunner/BootstrapFinishedSubscriber.php', + 'PHPUnit\\Event\\TestRunner\\Configured' => '/phpunit/Event/Events/TestRunner/Configured.php', + 'PHPUnit\\Event\\TestRunner\\ConfiguredSubscriber' => '/phpunit/Event/Events/TestRunner/ConfiguredSubscriber.php', + 'PHPUnit\\Event\\TestRunner\\DeprecationTriggered' => '/phpunit/Event/Events/TestRunner/DeprecationTriggered.php', + 'PHPUnit\\Event\\TestRunner\\DeprecationTriggeredSubscriber' => '/phpunit/Event/Events/TestRunner/DeprecationTriggeredSubscriber.php', + 'PHPUnit\\Event\\TestRunner\\EventFacadeSealed' => '/phpunit/Event/Events/TestRunner/EventFacadeSealed.php', + 'PHPUnit\\Event\\TestRunner\\EventFacadeSealedSubscriber' => '/phpunit/Event/Events/TestRunner/EventFacadeSealedSubscriber.php', + 'PHPUnit\\Event\\TestRunner\\ExecutionAborted' => '/phpunit/Event/Events/TestRunner/ExecutionAborted.php', + 'PHPUnit\\Event\\TestRunner\\ExecutionAbortedSubscriber' => '/phpunit/Event/Events/TestRunner/ExecutionAbortedSubscriber.php', + 'PHPUnit\\Event\\TestRunner\\ExecutionFinished' => '/phpunit/Event/Events/TestRunner/ExecutionFinished.php', + 'PHPUnit\\Event\\TestRunner\\ExecutionFinishedSubscriber' => '/phpunit/Event/Events/TestRunner/ExecutionFinishedSubscriber.php', + 'PHPUnit\\Event\\TestRunner\\ExecutionStarted' => '/phpunit/Event/Events/TestRunner/ExecutionStarted.php', + 'PHPUnit\\Event\\TestRunner\\ExecutionStartedSubscriber' => '/phpunit/Event/Events/TestRunner/ExecutionStartedSubscriber.php', + 'PHPUnit\\Event\\TestRunner\\ExtensionBootstrapped' => '/phpunit/Event/Events/TestRunner/ExtensionBootstrapped.php', + 'PHPUnit\\Event\\TestRunner\\ExtensionBootstrappedSubscriber' => '/phpunit/Event/Events/TestRunner/ExtensionBootstrappedSubscriber.php', + 'PHPUnit\\Event\\TestRunner\\ExtensionLoadedFromPhar' => '/phpunit/Event/Events/TestRunner/ExtensionLoadedFromPhar.php', + 'PHPUnit\\Event\\TestRunner\\ExtensionLoadedFromPharSubscriber' => '/phpunit/Event/Events/TestRunner/ExtensionLoadedFromPharSubscriber.php', + 'PHPUnit\\Event\\TestRunner\\Finished' => '/phpunit/Event/Events/TestRunner/Finished.php', + 'PHPUnit\\Event\\TestRunner\\FinishedSubscriber' => '/phpunit/Event/Events/TestRunner/FinishedSubscriber.php', + 'PHPUnit\\Event\\TestRunner\\GarbageCollectionDisabled' => '/phpunit/Event/Events/TestRunner/GarbageCollectionDisabled.php', + 'PHPUnit\\Event\\TestRunner\\GarbageCollectionDisabledSubscriber' => '/phpunit/Event/Events/TestRunner/GarbageCollectionDisabledSubscriber.php', + 'PHPUnit\\Event\\TestRunner\\GarbageCollectionEnabled' => '/phpunit/Event/Events/TestRunner/GarbageCollectionEnabled.php', + 'PHPUnit\\Event\\TestRunner\\GarbageCollectionEnabledSubscriber' => '/phpunit/Event/Events/TestRunner/GarbageCollectionEnabledSubscriber.php', + 'PHPUnit\\Event\\TestRunner\\GarbageCollectionTriggered' => '/phpunit/Event/Events/TestRunner/GarbageCollectionTriggered.php', + 'PHPUnit\\Event\\TestRunner\\GarbageCollectionTriggeredSubscriber' => '/phpunit/Event/Events/TestRunner/GarbageCollectionTriggeredSubscriber.php', + 'PHPUnit\\Event\\TestRunner\\Started' => '/phpunit/Event/Events/TestRunner/Started.php', + 'PHPUnit\\Event\\TestRunner\\StartedSubscriber' => '/phpunit/Event/Events/TestRunner/StartedSubscriber.php', + 'PHPUnit\\Event\\TestRunner\\WarningTriggered' => '/phpunit/Event/Events/TestRunner/WarningTriggered.php', + 'PHPUnit\\Event\\TestRunner\\WarningTriggeredSubscriber' => '/phpunit/Event/Events/TestRunner/WarningTriggeredSubscriber.php', + 'PHPUnit\\Event\\TestSuite\\Filtered' => '/phpunit/Event/Events/TestSuite/Filtered.php', + 'PHPUnit\\Event\\TestSuite\\FilteredSubscriber' => '/phpunit/Event/Events/TestSuite/FilteredSubscriber.php', + 'PHPUnit\\Event\\TestSuite\\Finished' => '/phpunit/Event/Events/TestSuite/Finished.php', + 'PHPUnit\\Event\\TestSuite\\FinishedSubscriber' => '/phpunit/Event/Events/TestSuite/FinishedSubscriber.php', + 'PHPUnit\\Event\\TestSuite\\Loaded' => '/phpunit/Event/Events/TestSuite/Loaded.php', + 'PHPUnit\\Event\\TestSuite\\LoadedSubscriber' => '/phpunit/Event/Events/TestSuite/LoadedSubscriber.php', + 'PHPUnit\\Event\\TestSuite\\Skipped' => '/phpunit/Event/Events/TestSuite/Skipped.php', + 'PHPUnit\\Event\\TestSuite\\SkippedSubscriber' => '/phpunit/Event/Events/TestSuite/SkippedSubscriber.php', + 'PHPUnit\\Event\\TestSuite\\Sorted' => '/phpunit/Event/Events/TestSuite/Sorted.php', + 'PHPUnit\\Event\\TestSuite\\SortedSubscriber' => '/phpunit/Event/Events/TestSuite/SortedSubscriber.php', + 'PHPUnit\\Event\\TestSuite\\Started' => '/phpunit/Event/Events/TestSuite/Started.php', + 'PHPUnit\\Event\\TestSuite\\StartedSubscriber' => '/phpunit/Event/Events/TestSuite/StartedSubscriber.php', + 'PHPUnit\\Event\\TestSuite\\TestSuite' => '/phpunit/Event/Value/TestSuite/TestSuite.php', + 'PHPUnit\\Event\\TestSuite\\TestSuiteBuilder' => '/phpunit/Event/Value/TestSuite/TestSuiteBuilder.php', + 'PHPUnit\\Event\\TestSuite\\TestSuiteForTestClass' => '/phpunit/Event/Value/TestSuite/TestSuiteForTestClass.php', + 'PHPUnit\\Event\\TestSuite\\TestSuiteForTestMethodWithDataProvider' => '/phpunit/Event/Value/TestSuite/TestSuiteForTestMethodWithDataProvider.php', + 'PHPUnit\\Event\\TestSuite\\TestSuiteWithName' => '/phpunit/Event/Value/TestSuite/TestSuiteWithName.php', + 'PHPUnit\\Event\\Test\\AfterLastTestMethodCalled' => '/phpunit/Event/Events/Test/HookMethod/AfterLastTestMethodCalled.php', + 'PHPUnit\\Event\\Test\\AfterLastTestMethodCalledSubscriber' => '/phpunit/Event/Events/Test/HookMethod/AfterLastTestMethodCalledSubscriber.php', + 'PHPUnit\\Event\\Test\\AfterLastTestMethodFinished' => '/phpunit/Event/Events/Test/HookMethod/AfterLastTestMethodFinished.php', + 'PHPUnit\\Event\\Test\\AfterLastTestMethodFinishedSubscriber' => '/phpunit/Event/Events/Test/HookMethod/AfterLastTestMethodFinishedSubscriber.php', + 'PHPUnit\\Event\\Test\\AfterTestMethodCalled' => '/phpunit/Event/Events/Test/HookMethod/AfterTestMethodCalled.php', + 'PHPUnit\\Event\\Test\\AfterTestMethodCalledSubscriber' => '/phpunit/Event/Events/Test/HookMethod/AfterTestMethodCalledSubscriber.php', + 'PHPUnit\\Event\\Test\\AfterTestMethodFinished' => '/phpunit/Event/Events/Test/HookMethod/AfterTestMethodFinished.php', + 'PHPUnit\\Event\\Test\\AfterTestMethodFinishedSubscriber' => '/phpunit/Event/Events/Test/HookMethod/AfterTestMethodFinishedSubscriber.php', + 'PHPUnit\\Event\\Test\\AssertionFailed' => '/phpunit/Event/Events/Test/Assertion/AssertionFailed.php', + 'PHPUnit\\Event\\Test\\AssertionFailedSubscriber' => '/phpunit/Event/Events/Test/Assertion/AssertionFailedSubscriber.php', + 'PHPUnit\\Event\\Test\\AssertionSucceeded' => '/phpunit/Event/Events/Test/Assertion/AssertionSucceeded.php', + 'PHPUnit\\Event\\Test\\AssertionSucceededSubscriber' => '/phpunit/Event/Events/Test/Assertion/AssertionSucceededSubscriber.php', + 'PHPUnit\\Event\\Test\\BeforeFirstTestMethodCalled' => '/phpunit/Event/Events/Test/HookMethod/BeforeFirstTestMethodCalled.php', + 'PHPUnit\\Event\\Test\\BeforeFirstTestMethodCalledSubscriber' => '/phpunit/Event/Events/Test/HookMethod/BeforeFirstTestMethodCalledSubscriber.php', + 'PHPUnit\\Event\\Test\\BeforeFirstTestMethodErrored' => '/phpunit/Event/Events/Test/HookMethod/BeforeFirstTestMethodErrored.php', + 'PHPUnit\\Event\\Test\\BeforeFirstTestMethodErroredSubscriber' => '/phpunit/Event/Events/Test/HookMethod/BeforeFirstTestMethodErroredSubscriber.php', + 'PHPUnit\\Event\\Test\\BeforeFirstTestMethodFinished' => '/phpunit/Event/Events/Test/HookMethod/BeforeFirstTestMethodFinished.php', + 'PHPUnit\\Event\\Test\\BeforeFirstTestMethodFinishedSubscriber' => '/phpunit/Event/Events/Test/HookMethod/BeforeFirstTestMethodFinishedSubscriber.php', + 'PHPUnit\\Event\\Test\\BeforeTestMethodCalled' => '/phpunit/Event/Events/Test/HookMethod/BeforeTestMethodCalled.php', + 'PHPUnit\\Event\\Test\\BeforeTestMethodCalledSubscriber' => '/phpunit/Event/Events/Test/HookMethod/BeforeTestMethodCalledSubscriber.php', + 'PHPUnit\\Event\\Test\\BeforeTestMethodFinished' => '/phpunit/Event/Events/Test/HookMethod/BeforeTestMethodFinished.php', + 'PHPUnit\\Event\\Test\\BeforeTestMethodFinishedSubscriber' => '/phpunit/Event/Events/Test/HookMethod/BeforeTestMethodFinishedSubscriber.php', + 'PHPUnit\\Event\\Test\\ComparatorRegistered' => '/phpunit/Event/Events/Test/ComparatorRegistered.php', + 'PHPUnit\\Event\\Test\\ComparatorRegisteredSubscriber' => '/phpunit/Event/Events/Test/ComparatorRegisteredSubscriber.php', + 'PHPUnit\\Event\\Test\\ConsideredRisky' => '/phpunit/Event/Events/Test/Issue/ConsideredRisky.php', + 'PHPUnit\\Event\\Test\\ConsideredRiskySubscriber' => '/phpunit/Event/Events/Test/Issue/ConsideredRiskySubscriber.php', + 'PHPUnit\\Event\\Test\\DataProviderMethodCalled' => '/phpunit/Event/Events/Test/Lifecycle/DataProviderMethodCalled.php', + 'PHPUnit\\Event\\Test\\DataProviderMethodCalledSubscriber' => '/phpunit/Event/Events/Test/Lifecycle/DataProviderMethodCalledSubscriber.php', + 'PHPUnit\\Event\\Test\\DataProviderMethodFinished' => '/phpunit/Event/Events/Test/Lifecycle/DataProviderMethodFinished.php', + 'PHPUnit\\Event\\Test\\DataProviderMethodFinishedSubscriber' => '/phpunit/Event/Events/Test/Lifecycle/DataProviderMethodFinishedSubscriber.php', + 'PHPUnit\\Event\\Test\\DeprecationTriggered' => '/phpunit/Event/Events/Test/Issue/DeprecationTriggered.php', + 'PHPUnit\\Event\\Test\\DeprecationTriggeredSubscriber' => '/phpunit/Event/Events/Test/Issue/DeprecationTriggeredSubscriber.php', + 'PHPUnit\\Event\\Test\\ErrorTriggered' => '/phpunit/Event/Events/Test/Issue/ErrorTriggered.php', + 'PHPUnit\\Event\\Test\\ErrorTriggeredSubscriber' => '/phpunit/Event/Events/Test/Issue/ErrorTriggeredSubscriber.php', + 'PHPUnit\\Event\\Test\\Errored' => '/phpunit/Event/Events/Test/Outcome/Errored.php', + 'PHPUnit\\Event\\Test\\ErroredSubscriber' => '/phpunit/Event/Events/Test/Outcome/ErroredSubscriber.php', + 'PHPUnit\\Event\\Test\\Failed' => '/phpunit/Event/Events/Test/Outcome/Failed.php', + 'PHPUnit\\Event\\Test\\FailedSubscriber' => '/phpunit/Event/Events/Test/Outcome/FailedSubscriber.php', + 'PHPUnit\\Event\\Test\\Finished' => '/phpunit/Event/Events/Test/Lifecycle/Finished.php', + 'PHPUnit\\Event\\Test\\FinishedSubscriber' => '/phpunit/Event/Events/Test/Lifecycle/FinishedSubscriber.php', + 'PHPUnit\\Event\\Test\\MarkedIncomplete' => '/phpunit/Event/Events/Test/Outcome/MarkedIncomplete.php', + 'PHPUnit\\Event\\Test\\MarkedIncompleteSubscriber' => '/phpunit/Event/Events/Test/Outcome/MarkedIncompleteSubscriber.php', + 'PHPUnit\\Event\\Test\\MockObjectCreated' => '/phpunit/Event/Events/Test/TestDouble/MockObjectCreated.php', + 'PHPUnit\\Event\\Test\\MockObjectCreatedSubscriber' => '/phpunit/Event/Events/Test/TestDouble/MockObjectCreatedSubscriber.php', + 'PHPUnit\\Event\\Test\\MockObjectForAbstractClassCreated' => '/phpunit/Event/Events/Test/TestDouble/MockObjectForAbstractClassCreated.php', + 'PHPUnit\\Event\\Test\\MockObjectForAbstractClassCreatedSubscriber' => '/phpunit/Event/Events/Test/TestDouble/MockObjectForAbstractClassCreatedSubscriber.php', + 'PHPUnit\\Event\\Test\\MockObjectForIntersectionOfInterfacesCreated' => '/phpunit/Event/Events/Test/TestDouble/MockObjectForIntersectionOfInterfacesCreated.php', + 'PHPUnit\\Event\\Test\\MockObjectForIntersectionOfInterfacesCreatedSubscriber' => '/phpunit/Event/Events/Test/TestDouble/MockObjectForIntersectionOfInterfacesCreatedSubscriber.php', + 'PHPUnit\\Event\\Test\\MockObjectForTraitCreated' => '/phpunit/Event/Events/Test/TestDouble/MockObjectForTraitCreated.php', + 'PHPUnit\\Event\\Test\\MockObjectForTraitCreatedSubscriber' => '/phpunit/Event/Events/Test/TestDouble/MockObjectForTraitCreatedSubscriber.php', + 'PHPUnit\\Event\\Test\\MockObjectFromWsdlCreated' => '/phpunit/Event/Events/Test/TestDouble/MockObjectFromWsdlCreated.php', + 'PHPUnit\\Event\\Test\\MockObjectFromWsdlCreatedSubscriber' => '/phpunit/Event/Events/Test/TestDouble/MockObjectFromWsdlCreatedSubscriber.php', + 'PHPUnit\\Event\\Test\\NoComparisonFailureException' => '/phpunit/Event/Exception/NoComparisonFailureException.php', + 'PHPUnit\\Event\\Test\\NoticeTriggered' => '/phpunit/Event/Events/Test/Issue/NoticeTriggered.php', + 'PHPUnit\\Event\\Test\\NoticeTriggeredSubscriber' => '/phpunit/Event/Events/Test/Issue/NoticeTriggeredSubscriber.php', + 'PHPUnit\\Event\\Test\\PartialMockObjectCreated' => '/phpunit/Event/Events/Test/TestDouble/PartialMockObjectCreated.php', + 'PHPUnit\\Event\\Test\\PartialMockObjectCreatedSubscriber' => '/phpunit/Event/Events/Test/TestDouble/PartialMockObjectCreatedSubscriber.php', + 'PHPUnit\\Event\\Test\\Passed' => '/phpunit/Event/Events/Test/Outcome/Passed.php', + 'PHPUnit\\Event\\Test\\PassedSubscriber' => '/phpunit/Event/Events/Test/Outcome/PassedSubscriber.php', + 'PHPUnit\\Event\\Test\\PhpDeprecationTriggered' => '/phpunit/Event/Events/Test/Issue/PhpDeprecationTriggered.php', + 'PHPUnit\\Event\\Test\\PhpDeprecationTriggeredSubscriber' => '/phpunit/Event/Events/Test/Issue/PhpDeprecationTriggeredSubscriber.php', + 'PHPUnit\\Event\\Test\\PhpNoticeTriggered' => '/phpunit/Event/Events/Test/Issue/PhpNoticeTriggered.php', + 'PHPUnit\\Event\\Test\\PhpNoticeTriggeredSubscriber' => '/phpunit/Event/Events/Test/Issue/PhpNoticeTriggeredSubscriber.php', + 'PHPUnit\\Event\\Test\\PhpWarningTriggered' => '/phpunit/Event/Events/Test/Issue/PhpWarningTriggered.php', + 'PHPUnit\\Event\\Test\\PhpWarningTriggeredSubscriber' => '/phpunit/Event/Events/Test/Issue/PhpWarningTriggeredSubscriber.php', + 'PHPUnit\\Event\\Test\\PhpunitDeprecationTriggered' => '/phpunit/Event/Events/Test/Issue/PhpunitDeprecationTriggered.php', + 'PHPUnit\\Event\\Test\\PhpunitDeprecationTriggeredSubscriber' => '/phpunit/Event/Events/Test/Issue/PhpunitDeprecationTriggeredSubscriber.php', + 'PHPUnit\\Event\\Test\\PhpunitErrorTriggered' => '/phpunit/Event/Events/Test/Issue/PhpunitErrorTriggered.php', + 'PHPUnit\\Event\\Test\\PhpunitErrorTriggeredSubscriber' => '/phpunit/Event/Events/Test/Issue/PhpunitErrorTriggeredSubscriber.php', + 'PHPUnit\\Event\\Test\\PhpunitWarningTriggered' => '/phpunit/Event/Events/Test/Issue/PhpunitWarningTriggered.php', + 'PHPUnit\\Event\\Test\\PhpunitWarningTriggeredSubscriber' => '/phpunit/Event/Events/Test/Issue/PhpunitWarningTriggeredSubscriber.php', + 'PHPUnit\\Event\\Test\\PostConditionCalled' => '/phpunit/Event/Events/Test/HookMethod/PostConditionCalled.php', + 'PHPUnit\\Event\\Test\\PostConditionCalledSubscriber' => '/phpunit/Event/Events/Test/HookMethod/PostConditionCalledSubscriber.php', + 'PHPUnit\\Event\\Test\\PostConditionFinished' => '/phpunit/Event/Events/Test/HookMethod/PostConditionFinished.php', + 'PHPUnit\\Event\\Test\\PostConditionFinishedSubscriber' => '/phpunit/Event/Events/Test/HookMethod/PostConditionFinishedSubscriber.php', + 'PHPUnit\\Event\\Test\\PreConditionCalled' => '/phpunit/Event/Events/Test/HookMethod/PreConditionCalled.php', + 'PHPUnit\\Event\\Test\\PreConditionCalledSubscriber' => '/phpunit/Event/Events/Test/HookMethod/PreConditionCalledSubscriber.php', + 'PHPUnit\\Event\\Test\\PreConditionFinished' => '/phpunit/Event/Events/Test/HookMethod/PreConditionFinished.php', + 'PHPUnit\\Event\\Test\\PreConditionFinishedSubscriber' => '/phpunit/Event/Events/Test/HookMethod/PreConditionFinishedSubscriber.php', + 'PHPUnit\\Event\\Test\\PreparationFailed' => '/phpunit/Event/Events/Test/Lifecycle/PreparationFailed.php', + 'PHPUnit\\Event\\Test\\PreparationFailedSubscriber' => '/phpunit/Event/Events/Test/Lifecycle/PreparationFailedSubscriber.php', + 'PHPUnit\\Event\\Test\\PreparationStarted' => '/phpunit/Event/Events/Test/Lifecycle/PreparationStarted.php', + 'PHPUnit\\Event\\Test\\PreparationStartedSubscriber' => '/phpunit/Event/Events/Test/Lifecycle/PreparationStartedSubscriber.php', + 'PHPUnit\\Event\\Test\\Prepared' => '/phpunit/Event/Events/Test/Lifecycle/Prepared.php', + 'PHPUnit\\Event\\Test\\PreparedSubscriber' => '/phpunit/Event/Events/Test/Lifecycle/PreparedSubscriber.php', + 'PHPUnit\\Event\\Test\\PrintedUnexpectedOutput' => '/phpunit/Event/Events/Test/PrintedUnexpectedOutput.php', + 'PHPUnit\\Event\\Test\\PrintedUnexpectedOutputSubscriber' => '/phpunit/Event/Events/Test/PrintedUnexpectedOutputSubscriber.php', + 'PHPUnit\\Event\\Test\\Skipped' => '/phpunit/Event/Events/Test/Outcome/Skipped.php', + 'PHPUnit\\Event\\Test\\SkippedSubscriber' => '/phpunit/Event/Events/Test/Outcome/SkippedSubscriber.php', + 'PHPUnit\\Event\\Test\\TestProxyCreated' => '/phpunit/Event/Events/Test/TestDouble/TestProxyCreated.php', + 'PHPUnit\\Event\\Test\\TestProxyCreatedSubscriber' => '/phpunit/Event/Events/Test/TestDouble/TestProxyCreatedSubscriber.php', + 'PHPUnit\\Event\\Test\\TestStubCreated' => '/phpunit/Event/Events/Test/TestDouble/TestStubCreated.php', + 'PHPUnit\\Event\\Test\\TestStubCreatedSubscriber' => '/phpunit/Event/Events/Test/TestDouble/TestStubCreatedSubscriber.php', + 'PHPUnit\\Event\\Test\\TestStubForIntersectionOfInterfacesCreated' => '/phpunit/Event/Events/Test/TestDouble/TestStubForIntersectionOfInterfacesCreated.php', + 'PHPUnit\\Event\\Test\\TestStubForIntersectionOfInterfacesCreatedSubscriber' => '/phpunit/Event/Events/Test/TestDouble/TestStubForIntersectionOfInterfacesCreatedSubscriber.php', + 'PHPUnit\\Event\\Test\\WarningTriggered' => '/phpunit/Event/Events/Test/Issue/WarningTriggered.php', + 'PHPUnit\\Event\\Test\\WarningTriggeredSubscriber' => '/phpunit/Event/Events/Test/Issue/WarningTriggeredSubscriber.php', + 'PHPUnit\\Event\\Tracer\\Tracer' => '/phpunit/Event/Tracer.php', + 'PHPUnit\\Event\\TypeMap' => '/phpunit/Event/TypeMap.php', + 'PHPUnit\\Event\\UnknownEventException' => '/phpunit/Event/Exception/UnknownEventException.php', + 'PHPUnit\\Event\\UnknownEventTypeException' => '/phpunit/Event/Exception/UnknownEventTypeException.php', + 'PHPUnit\\Event\\UnknownSubscriberException' => '/phpunit/Event/Exception/UnknownSubscriberException.php', + 'PHPUnit\\Event\\UnknownSubscriberTypeException' => '/phpunit/Event/Exception/UnknownSubscriberTypeException.php', 'PHPUnit\\Exception' => '/phpunit/Exception.php', - 'PHPUnit\\Framework\\ActualValueIsNotAnObjectException' => '/phpunit/Framework/Exception/ActualValueIsNotAnObjectException.php', + 'PHPUnit\\Framework\\ActualValueIsNotAnObjectException' => '/phpunit/Framework/Exception/ObjectEquals/ActualValueIsNotAnObjectException.php', 'PHPUnit\\Framework\\Assert' => '/phpunit/Framework/Assert.php', 'PHPUnit\\Framework\\AssertionFailedError' => '/phpunit/Framework/Exception/AssertionFailedError.php', + 'PHPUnit\\Framework\\Attributes\\After' => '/phpunit/Framework/Attributes/After.php', + 'PHPUnit\\Framework\\Attributes\\AfterClass' => '/phpunit/Framework/Attributes/AfterClass.php', + 'PHPUnit\\Framework\\Attributes\\BackupGlobals' => '/phpunit/Framework/Attributes/BackupGlobals.php', + 'PHPUnit\\Framework\\Attributes\\BackupStaticProperties' => '/phpunit/Framework/Attributes/BackupStaticProperties.php', + 'PHPUnit\\Framework\\Attributes\\Before' => '/phpunit/Framework/Attributes/Before.php', + 'PHPUnit\\Framework\\Attributes\\BeforeClass' => '/phpunit/Framework/Attributes/BeforeClass.php', + 'PHPUnit\\Framework\\Attributes\\CodeCoverageIgnore' => '/phpunit/Framework/Attributes/CodeCoverageIgnore.php', + 'PHPUnit\\Framework\\Attributes\\CoversClass' => '/phpunit/Framework/Attributes/CoversClass.php', + 'PHPUnit\\Framework\\Attributes\\CoversFunction' => '/phpunit/Framework/Attributes/CoversFunction.php', + 'PHPUnit\\Framework\\Attributes\\CoversNothing' => '/phpunit/Framework/Attributes/CoversNothing.php', + 'PHPUnit\\Framework\\Attributes\\DataProvider' => '/phpunit/Framework/Attributes/DataProvider.php', + 'PHPUnit\\Framework\\Attributes\\DataProviderExternal' => '/phpunit/Framework/Attributes/DataProviderExternal.php', + 'PHPUnit\\Framework\\Attributes\\Depends' => '/phpunit/Framework/Attributes/Depends.php', + 'PHPUnit\\Framework\\Attributes\\DependsExternal' => '/phpunit/Framework/Attributes/DependsExternal.php', + 'PHPUnit\\Framework\\Attributes\\DependsExternalUsingDeepClone' => '/phpunit/Framework/Attributes/DependsExternalUsingDeepClone.php', + 'PHPUnit\\Framework\\Attributes\\DependsExternalUsingShallowClone' => '/phpunit/Framework/Attributes/DependsExternalUsingShallowClone.php', + 'PHPUnit\\Framework\\Attributes\\DependsOnClass' => '/phpunit/Framework/Attributes/DependsOnClass.php', + 'PHPUnit\\Framework\\Attributes\\DependsOnClassUsingDeepClone' => '/phpunit/Framework/Attributes/DependsOnClassUsingDeepClone.php', + 'PHPUnit\\Framework\\Attributes\\DependsOnClassUsingShallowClone' => '/phpunit/Framework/Attributes/DependsOnClassUsingShallowClone.php', + 'PHPUnit\\Framework\\Attributes\\DependsUsingDeepClone' => '/phpunit/Framework/Attributes/DependsUsingDeepClone.php', + 'PHPUnit\\Framework\\Attributes\\DependsUsingShallowClone' => '/phpunit/Framework/Attributes/DependsUsingShallowClone.php', + 'PHPUnit\\Framework\\Attributes\\DoesNotPerformAssertions' => '/phpunit/Framework/Attributes/DoesNotPerformAssertions.php', + 'PHPUnit\\Framework\\Attributes\\ExcludeGlobalVariableFromBackup' => '/phpunit/Framework/Attributes/ExcludeGlobalVariableFromBackup.php', + 'PHPUnit\\Framework\\Attributes\\ExcludeStaticPropertyFromBackup' => '/phpunit/Framework/Attributes/ExcludeStaticPropertyFromBackup.php', + 'PHPUnit\\Framework\\Attributes\\Group' => '/phpunit/Framework/Attributes/Group.php', + 'PHPUnit\\Framework\\Attributes\\IgnoreClassForCodeCoverage' => '/phpunit/Framework/Attributes/IgnoreClassForCodeCoverage.php', + 'PHPUnit\\Framework\\Attributes\\IgnoreDeprecations' => '/phpunit/Framework/Attributes/IgnoreDeprecations.php', + 'PHPUnit\\Framework\\Attributes\\IgnoreFunctionForCodeCoverage' => '/phpunit/Framework/Attributes/IgnoreFunctionForCodeCoverage.php', + 'PHPUnit\\Framework\\Attributes\\IgnoreMethodForCodeCoverage' => '/phpunit/Framework/Attributes/IgnoreMethodForCodeCoverage.php', + 'PHPUnit\\Framework\\Attributes\\Large' => '/phpunit/Framework/Attributes/Large.php', + 'PHPUnit\\Framework\\Attributes\\Medium' => '/phpunit/Framework/Attributes/Medium.php', + 'PHPUnit\\Framework\\Attributes\\PostCondition' => '/phpunit/Framework/Attributes/PostCondition.php', + 'PHPUnit\\Framework\\Attributes\\PreCondition' => '/phpunit/Framework/Attributes/PreCondition.php', + 'PHPUnit\\Framework\\Attributes\\PreserveGlobalState' => '/phpunit/Framework/Attributes/PreserveGlobalState.php', + 'PHPUnit\\Framework\\Attributes\\RequiresFunction' => '/phpunit/Framework/Attributes/RequiresFunction.php', + 'PHPUnit\\Framework\\Attributes\\RequiresMethod' => '/phpunit/Framework/Attributes/RequiresMethod.php', + 'PHPUnit\\Framework\\Attributes\\RequiresOperatingSystem' => '/phpunit/Framework/Attributes/RequiresOperatingSystem.php', + 'PHPUnit\\Framework\\Attributes\\RequiresOperatingSystemFamily' => '/phpunit/Framework/Attributes/RequiresOperatingSystemFamily.php', + 'PHPUnit\\Framework\\Attributes\\RequiresPhp' => '/phpunit/Framework/Attributes/RequiresPhp.php', + 'PHPUnit\\Framework\\Attributes\\RequiresPhpExtension' => '/phpunit/Framework/Attributes/RequiresPhpExtension.php', + 'PHPUnit\\Framework\\Attributes\\RequiresPhpunit' => '/phpunit/Framework/Attributes/RequiresPhpunit.php', + 'PHPUnit\\Framework\\Attributes\\RequiresSetting' => '/phpunit/Framework/Attributes/RequiresSetting.php', + 'PHPUnit\\Framework\\Attributes\\RunClassInSeparateProcess' => '/phpunit/Framework/Attributes/RunClassInSeparateProcess.php', + 'PHPUnit\\Framework\\Attributes\\RunInSeparateProcess' => '/phpunit/Framework/Attributes/RunInSeparateProcess.php', + 'PHPUnit\\Framework\\Attributes\\RunTestsInSeparateProcesses' => '/phpunit/Framework/Attributes/RunTestsInSeparateProcesses.php', + 'PHPUnit\\Framework\\Attributes\\Small' => '/phpunit/Framework/Attributes/Small.php', + 'PHPUnit\\Framework\\Attributes\\Test' => '/phpunit/Framework/Attributes/Test.php', + 'PHPUnit\\Framework\\Attributes\\TestDox' => '/phpunit/Framework/Attributes/TestDox.php', + 'PHPUnit\\Framework\\Attributes\\TestWith' => '/phpunit/Framework/Attributes/TestWith.php', + 'PHPUnit\\Framework\\Attributes\\TestWithJson' => '/phpunit/Framework/Attributes/TestWithJson.php', + 'PHPUnit\\Framework\\Attributes\\Ticket' => '/phpunit/Framework/Attributes/Ticket.php', + 'PHPUnit\\Framework\\Attributes\\UsesClass' => '/phpunit/Framework/Attributes/UsesClass.php', + 'PHPUnit\\Framework\\Attributes\\UsesFunction' => '/phpunit/Framework/Attributes/UsesFunction.php', + 'PHPUnit\\Framework\\Attributes\\WithoutErrorHandler' => '/phpunit/Framework/Attributes/WithoutErrorHandler.php', 'PHPUnit\\Framework\\CodeCoverageException' => '/phpunit/Framework/Exception/CodeCoverageException.php', - 'PHPUnit\\Framework\\ComparisonMethodDoesNotAcceptParameterTypeException' => '/phpunit/Framework/Exception/ComparisonMethodDoesNotAcceptParameterTypeException.php', - 'PHPUnit\\Framework\\ComparisonMethodDoesNotDeclareBoolReturnTypeException' => '/phpunit/Framework/Exception/ComparisonMethodDoesNotDeclareBoolReturnTypeException.php', - 'PHPUnit\\Framework\\ComparisonMethodDoesNotDeclareExactlyOneParameterException' => '/phpunit/Framework/Exception/ComparisonMethodDoesNotDeclareExactlyOneParameterException.php', - 'PHPUnit\\Framework\\ComparisonMethodDoesNotDeclareParameterTypeException' => '/phpunit/Framework/Exception/ComparisonMethodDoesNotDeclareParameterTypeException.php', - 'PHPUnit\\Framework\\ComparisonMethodDoesNotExistException' => '/phpunit/Framework/Exception/ComparisonMethodDoesNotExistException.php', + 'PHPUnit\\Framework\\ComparisonMethodDoesNotAcceptParameterTypeException' => '/phpunit/Framework/Exception/ObjectEquals/ComparisonMethodDoesNotAcceptParameterTypeException.php', + 'PHPUnit\\Framework\\ComparisonMethodDoesNotDeclareBoolReturnTypeException' => '/phpunit/Framework/Exception/ObjectEquals/ComparisonMethodDoesNotDeclareBoolReturnTypeException.php', + 'PHPUnit\\Framework\\ComparisonMethodDoesNotDeclareExactlyOneParameterException' => '/phpunit/Framework/Exception/ObjectEquals/ComparisonMethodDoesNotDeclareExactlyOneParameterException.php', + 'PHPUnit\\Framework\\ComparisonMethodDoesNotDeclareParameterTypeException' => '/phpunit/Framework/Exception/ObjectEquals/ComparisonMethodDoesNotDeclareParameterTypeException.php', + 'PHPUnit\\Framework\\ComparisonMethodDoesNotExistException' => '/phpunit/Framework/Exception/ObjectEquals/ComparisonMethodDoesNotExistException.php', 'PHPUnit\\Framework\\Constraint\\ArrayHasKey' => '/phpunit/Framework/Constraint/Traversable/ArrayHasKey.php', 'PHPUnit\\Framework\\Constraint\\BinaryOperator' => '/phpunit/Framework/Constraint/Operator/BinaryOperator.php', 'PHPUnit\\Framework\\Constraint\\Callback' => '/phpunit/Framework/Constraint/Callback.php', - 'PHPUnit\\Framework\\Constraint\\ClassHasAttribute' => '/phpunit/Framework/Constraint/Object/ClassHasAttribute.php', - 'PHPUnit\\Framework\\Constraint\\ClassHasStaticAttribute' => '/phpunit/Framework/Constraint/Object/ClassHasStaticAttribute.php', 'PHPUnit\\Framework\\Constraint\\Constraint' => '/phpunit/Framework/Constraint/Constraint.php', 'PHPUnit\\Framework\\Constraint\\Count' => '/phpunit/Framework/Constraint/Cardinality/Count.php', 'PHPUnit\\Framework\\Constraint\\DirectoryExists' => '/phpunit/Framework/Constraint/Filesystem/DirectoryExists.php', 'PHPUnit\\Framework\\Constraint\\Exception' => '/phpunit/Framework/Constraint/Exception/Exception.php', 'PHPUnit\\Framework\\Constraint\\ExceptionCode' => '/phpunit/Framework/Constraint/Exception/ExceptionCode.php', - 'PHPUnit\\Framework\\Constraint\\ExceptionMessage' => '/phpunit/Framework/Constraint/Exception/ExceptionMessage.php', - 'PHPUnit\\Framework\\Constraint\\ExceptionMessageRegularExpression' => '/phpunit/Framework/Constraint/Exception/ExceptionMessageRegularExpression.php', + 'PHPUnit\\Framework\\Constraint\\ExceptionMessageIsOrContains' => '/phpunit/Framework/Constraint/Exception/ExceptionMessageIsOrContains.php', + 'PHPUnit\\Framework\\Constraint\\ExceptionMessageMatchesRegularExpression' => '/phpunit/Framework/Constraint/Exception/ExceptionMessageMatchesRegularExpression.php', 'PHPUnit\\Framework\\Constraint\\FileExists' => '/phpunit/Framework/Constraint/Filesystem/FileExists.php', 'PHPUnit\\Framework\\Constraint\\GreaterThan' => '/phpunit/Framework/Constraint/Cardinality/GreaterThan.php', 'PHPUnit\\Framework\\Constraint\\IsAnything' => '/phpunit/Framework/Constraint/IsAnything.php', @@ -2085,6 +2394,7 @@ foreach (['Doctrine\\Deprecations\\PHPUnit\\VerifyDeprecations' => '/doctrine-de 'PHPUnit\\Framework\\Constraint\\IsInfinite' => '/phpunit/Framework/Constraint/Math/IsInfinite.php', 'PHPUnit\\Framework\\Constraint\\IsInstanceOf' => '/phpunit/Framework/Constraint/Type/IsInstanceOf.php', 'PHPUnit\\Framework\\Constraint\\IsJson' => '/phpunit/Framework/Constraint/String/IsJson.php', + 'PHPUnit\\Framework\\Constraint\\IsList' => '/phpunit/Framework/Constraint/Traversable/IsList.php', 'PHPUnit\\Framework\\Constraint\\IsNan' => '/phpunit/Framework/Constraint/Math/IsNan.php', 'PHPUnit\\Framework\\Constraint\\IsNull' => '/phpunit/Framework/Constraint/Type/IsNull.php', 'PHPUnit\\Framework\\Constraint\\IsReadable' => '/phpunit/Framework/Constraint/Filesystem/IsReadable.php', @@ -2092,20 +2402,19 @@ foreach (['Doctrine\\Deprecations\\PHPUnit\\VerifyDeprecations' => '/doctrine-de 'PHPUnit\\Framework\\Constraint\\IsType' => '/phpunit/Framework/Constraint/Type/IsType.php', 'PHPUnit\\Framework\\Constraint\\IsWritable' => '/phpunit/Framework/Constraint/Filesystem/IsWritable.php', 'PHPUnit\\Framework\\Constraint\\JsonMatches' => '/phpunit/Framework/Constraint/JsonMatches.php', - 'PHPUnit\\Framework\\Constraint\\JsonMatchesErrorMessageProvider' => '/phpunit/Framework/Constraint/JsonMatchesErrorMessageProvider.php', 'PHPUnit\\Framework\\Constraint\\LessThan' => '/phpunit/Framework/Constraint/Cardinality/LessThan.php', 'PHPUnit\\Framework\\Constraint\\LogicalAnd' => '/phpunit/Framework/Constraint/Operator/LogicalAnd.php', 'PHPUnit\\Framework\\Constraint\\LogicalNot' => '/phpunit/Framework/Constraint/Operator/LogicalNot.php', 'PHPUnit\\Framework\\Constraint\\LogicalOr' => '/phpunit/Framework/Constraint/Operator/LogicalOr.php', 'PHPUnit\\Framework\\Constraint\\LogicalXor' => '/phpunit/Framework/Constraint/Operator/LogicalXor.php', 'PHPUnit\\Framework\\Constraint\\ObjectEquals' => '/phpunit/Framework/Constraint/Object/ObjectEquals.php', - 'PHPUnit\\Framework\\Constraint\\ObjectHasAttribute' => '/phpunit/Framework/Constraint/Object/ObjectHasAttribute.php', 'PHPUnit\\Framework\\Constraint\\ObjectHasProperty' => '/phpunit/Framework/Constraint/Object/ObjectHasProperty.php', 'PHPUnit\\Framework\\Constraint\\Operator' => '/phpunit/Framework/Constraint/Operator/Operator.php', 'PHPUnit\\Framework\\Constraint\\RegularExpression' => '/phpunit/Framework/Constraint/String/RegularExpression.php', 'PHPUnit\\Framework\\Constraint\\SameSize' => '/phpunit/Framework/Constraint/Cardinality/SameSize.php', 'PHPUnit\\Framework\\Constraint\\StringContains' => '/phpunit/Framework/Constraint/String/StringContains.php', 'PHPUnit\\Framework\\Constraint\\StringEndsWith' => '/phpunit/Framework/Constraint/String/StringEndsWith.php', + 'PHPUnit\\Framework\\Constraint\\StringEqualsStringIgnoringLineEndings' => '/phpunit/Framework/Constraint/String/StringEqualsStringIgnoringLineEndings.php', 'PHPUnit\\Framework\\Constraint\\StringMatchesFormatDescription' => '/phpunit/Framework/Constraint/String/StringMatchesFormatDescription.php', 'PHPUnit\\Framework\\Constraint\\StringStartsWith' => '/phpunit/Framework/Constraint/String/StringStartsWith.php', 'PHPUnit\\Framework\\Constraint\\TraversableContains' => '/phpunit/Framework/Constraint/Traversable/TraversableContains.php', @@ -2113,385 +2422,556 @@ foreach (['Doctrine\\Deprecations\\PHPUnit\\VerifyDeprecations' => '/doctrine-de 'PHPUnit\\Framework\\Constraint\\TraversableContainsIdentical' => '/phpunit/Framework/Constraint/Traversable/TraversableContainsIdentical.php', 'PHPUnit\\Framework\\Constraint\\TraversableContainsOnly' => '/phpunit/Framework/Constraint/Traversable/TraversableContainsOnly.php', 'PHPUnit\\Framework\\Constraint\\UnaryOperator' => '/phpunit/Framework/Constraint/Operator/UnaryOperator.php', - 'PHPUnit\\Framework\\CoveredCodeNotExecutedException' => '/phpunit/Framework/Exception/CoveredCodeNotExecutedException.php', 'PHPUnit\\Framework\\DataProviderTestSuite' => '/phpunit/Framework/DataProviderTestSuite.php', - 'PHPUnit\\Framework\\Error' => '/phpunit/Framework/Exception/Error.php', - 'PHPUnit\\Framework\\ErrorTestCase' => '/phpunit/Framework/ErrorTestCase.php', - 'PHPUnit\\Framework\\Error\\Deprecated' => '/phpunit/Framework/Error/Deprecated.php', - 'PHPUnit\\Framework\\Error\\Error' => '/phpunit/Framework/Error/Error.php', - 'PHPUnit\\Framework\\Error\\Notice' => '/phpunit/Framework/Error/Notice.php', - 'PHPUnit\\Framework\\Error\\Warning' => '/phpunit/Framework/Error/Warning.php', + 'PHPUnit\\Framework\\EmptyStringException' => '/phpunit/Framework/Exception/EmptyStringException.php', 'PHPUnit\\Framework\\Exception' => '/phpunit/Framework/Exception/Exception.php', - 'PHPUnit\\Framework\\ExceptionWrapper' => '/phpunit/Framework/ExceptionWrapper.php', 'PHPUnit\\Framework\\ExecutionOrderDependency' => '/phpunit/Framework/ExecutionOrderDependency.php', 'PHPUnit\\Framework\\ExpectationFailedException' => '/phpunit/Framework/Exception/ExpectationFailedException.php', - 'PHPUnit\\Framework\\IncompleteTest' => '/phpunit/Framework/IncompleteTest.php', - 'PHPUnit\\Framework\\IncompleteTestCase' => '/phpunit/Framework/IncompleteTestCase.php', - 'PHPUnit\\Framework\\IncompleteTestError' => '/phpunit/Framework/Exception/IncompleteTestError.php', + 'PHPUnit\\Framework\\GeneratorNotSupportedException' => '/phpunit/Framework/Exception/GeneratorNotSupportedException.php', + 'PHPUnit\\Framework\\IncompleteTest' => '/phpunit/Framework/Exception/Incomplete/IncompleteTest.php', + 'PHPUnit\\Framework\\IncompleteTestError' => '/phpunit/Framework/Exception/Incomplete/IncompleteTestError.php', 'PHPUnit\\Framework\\InvalidArgumentException' => '/phpunit/Framework/Exception/InvalidArgumentException.php', 'PHPUnit\\Framework\\InvalidCoversTargetException' => '/phpunit/Framework/Exception/InvalidCoversTargetException.php', 'PHPUnit\\Framework\\InvalidDataProviderException' => '/phpunit/Framework/Exception/InvalidDataProviderException.php', - 'PHPUnit\\Framework\\InvalidParameterGroupException' => '/phpunit/Framework/InvalidParameterGroupException.php', - 'PHPUnit\\Framework\\MissingCoversAnnotationException' => '/phpunit/Framework/Exception/MissingCoversAnnotationException.php', - 'PHPUnit\\Framework\\MockObject\\Api' => '/phpunit/Framework/MockObject/Api/Api.php', + 'PHPUnit\\Framework\\InvalidDependencyException' => '/phpunit/Framework/Exception/InvalidDependencyException.php', 'PHPUnit\\Framework\\MockObject\\BadMethodCallException' => '/phpunit/Framework/MockObject/Exception/BadMethodCallException.php', - 'PHPUnit\\Framework\\MockObject\\Builder\\Identity' => '/phpunit/Framework/MockObject/Builder/Identity.php', - 'PHPUnit\\Framework\\MockObject\\Builder\\InvocationMocker' => '/phpunit/Framework/MockObject/Builder/InvocationMocker.php', - 'PHPUnit\\Framework\\MockObject\\Builder\\InvocationStubber' => '/phpunit/Framework/MockObject/Builder/InvocationStubber.php', - 'PHPUnit\\Framework\\MockObject\\Builder\\MethodNameMatch' => '/phpunit/Framework/MockObject/Builder/MethodNameMatch.php', - 'PHPUnit\\Framework\\MockObject\\Builder\\ParametersMatch' => '/phpunit/Framework/MockObject/Builder/ParametersMatch.php', - 'PHPUnit\\Framework\\MockObject\\Builder\\Stub' => '/phpunit/Framework/MockObject/Builder/Stub.php', - 'PHPUnit\\Framework\\MockObject\\CannotUseAddMethodsException' => '/phpunit/Framework/MockObject/Exception/CannotUseAddMethodsException.php', + 'PHPUnit\\Framework\\MockObject\\Builder\\Identity' => '/phpunit/Framework/MockObject/Runtime/Builder/Identity.php', + 'PHPUnit\\Framework\\MockObject\\Builder\\InvocationMocker' => '/phpunit/Framework/MockObject/Runtime/Builder/InvocationMocker.php', + 'PHPUnit\\Framework\\MockObject\\Builder\\InvocationStubber' => '/phpunit/Framework/MockObject/Runtime/Builder/InvocationStubber.php', + 'PHPUnit\\Framework\\MockObject\\Builder\\MethodNameMatch' => '/phpunit/Framework/MockObject/Runtime/Builder/MethodNameMatch.php', + 'PHPUnit\\Framework\\MockObject\\Builder\\ParametersMatch' => '/phpunit/Framework/MockObject/Runtime/Builder/ParametersMatch.php', + 'PHPUnit\\Framework\\MockObject\\Builder\\Stub' => '/phpunit/Framework/MockObject/Runtime/Builder/Stub.php', 'PHPUnit\\Framework\\MockObject\\CannotUseOnlyMethodsException' => '/phpunit/Framework/MockObject/Exception/CannotUseOnlyMethodsException.php', - 'PHPUnit\\Framework\\MockObject\\ClassAlreadyExistsException' => '/phpunit/Framework/MockObject/Exception/ClassAlreadyExistsException.php', - 'PHPUnit\\Framework\\MockObject\\ClassIsFinalException' => '/phpunit/Framework/MockObject/Exception/ClassIsFinalException.php', - 'PHPUnit\\Framework\\MockObject\\ClassIsReadonlyException' => '/phpunit/Framework/MockObject/Exception/ClassIsReadonlyException.php', 'PHPUnit\\Framework\\MockObject\\ConfigurableMethod' => '/phpunit/Framework/MockObject/ConfigurableMethod.php', - 'PHPUnit\\Framework\\MockObject\\ConfigurableMethodsAlreadyInitializedException' => '/phpunit/Framework/MockObject/Exception/ConfigurableMethodsAlreadyInitializedException.php', - 'PHPUnit\\Framework\\MockObject\\DuplicateMethodException' => '/phpunit/Framework/MockObject/Exception/DuplicateMethodException.php', + 'PHPUnit\\Framework\\MockObject\\DoubledCloneMethod' => '/phpunit/Framework/MockObject/Runtime/Api/DoubledCloneMethod.php', 'PHPUnit\\Framework\\MockObject\\Exception' => '/phpunit/Framework/MockObject/Exception/Exception.php', - 'PHPUnit\\Framework\\MockObject\\Generator' => '/phpunit/Framework/MockObject/Generator.php', + 'PHPUnit\\Framework\\MockObject\\Generator\\CannotUseAddMethodsException' => '/phpunit/Framework/MockObject/Generator/Exception/CannotUseAddMethodsException.php', + 'PHPUnit\\Framework\\MockObject\\Generator\\ClassIsEnumerationException' => '/phpunit/Framework/MockObject/Generator/Exception/ClassIsEnumerationException.php', + 'PHPUnit\\Framework\\MockObject\\Generator\\ClassIsFinalException' => '/phpunit/Framework/MockObject/Generator/Exception/ClassIsFinalException.php', + 'PHPUnit\\Framework\\MockObject\\Generator\\ClassIsReadonlyException' => '/phpunit/Framework/MockObject/Generator/Exception/ClassIsReadonlyException.php', + 'PHPUnit\\Framework\\MockObject\\Generator\\DuplicateMethodException' => '/phpunit/Framework/MockObject/Generator/Exception/DuplicateMethodException.php', + 'PHPUnit\\Framework\\MockObject\\Generator\\Exception' => '/phpunit/Framework/MockObject/Generator/Exception/Exception.php', + 'PHPUnit\\Framework\\MockObject\\Generator\\Generator' => '/phpunit/Framework/MockObject/Generator/Generator.php', + 'PHPUnit\\Framework\\MockObject\\Generator\\InvalidMethodNameException' => '/phpunit/Framework/MockObject/Generator/Exception/InvalidMethodNameException.php', + 'PHPUnit\\Framework\\MockObject\\Generator\\MockClass' => '/phpunit/Framework/MockObject/Generator/MockClass.php', + 'PHPUnit\\Framework\\MockObject\\Generator\\MockMethod' => '/phpunit/Framework/MockObject/Generator/MockMethod.php', + 'PHPUnit\\Framework\\MockObject\\Generator\\MockMethodSet' => '/phpunit/Framework/MockObject/Generator/MockMethodSet.php', + 'PHPUnit\\Framework\\MockObject\\Generator\\MockTrait' => '/phpunit/Framework/MockObject/Generator/MockTrait.php', + 'PHPUnit\\Framework\\MockObject\\Generator\\MockType' => '/phpunit/Framework/MockObject/Generator/MockType.php', + 'PHPUnit\\Framework\\MockObject\\Generator\\NameAlreadyInUseException' => '/phpunit/Framework/MockObject/Generator/Exception/NameAlreadyInUseException.php', + 'PHPUnit\\Framework\\MockObject\\Generator\\OriginalConstructorInvocationRequiredException' => '/phpunit/Framework/MockObject/Generator/Exception/OriginalConstructorInvocationRequiredException.php', + 'PHPUnit\\Framework\\MockObject\\Generator\\ReflectionException' => '/phpunit/Framework/MockObject/Generator/Exception/ReflectionException.php', + 'PHPUnit\\Framework\\MockObject\\Generator\\RuntimeException' => '/phpunit/Framework/MockObject/Generator/Exception/RuntimeException.php', + 'PHPUnit\\Framework\\MockObject\\Generator\\SoapExtensionNotAvailableException' => '/phpunit/Framework/MockObject/Generator/Exception/SoapExtensionNotAvailableException.php', + 'PHPUnit\\Framework\\MockObject\\Generator\\TemplateLoader' => '/phpunit/Framework/MockObject/Generator/TemplateLoader.php', + 'PHPUnit\\Framework\\MockObject\\Generator\\UnknownClassException' => '/phpunit/Framework/MockObject/Generator/Exception/UnknownClassException.php', + 'PHPUnit\\Framework\\MockObject\\Generator\\UnknownTraitException' => '/phpunit/Framework/MockObject/Generator/Exception/UnknownTraitException.php', + 'PHPUnit\\Framework\\MockObject\\Generator\\UnknownTypeException' => '/phpunit/Framework/MockObject/Generator/Exception/UnknownTypeException.php', 'PHPUnit\\Framework\\MockObject\\IncompatibleReturnValueException' => '/phpunit/Framework/MockObject/Exception/IncompatibleReturnValueException.php', - 'PHPUnit\\Framework\\MockObject\\InvalidMethodNameException' => '/phpunit/Framework/MockObject/Exception/InvalidMethodNameException.php', - 'PHPUnit\\Framework\\MockObject\\Invocation' => '/phpunit/Framework/MockObject/Invocation.php', - 'PHPUnit\\Framework\\MockObject\\InvocationHandler' => '/phpunit/Framework/MockObject/InvocationHandler.php', + 'PHPUnit\\Framework\\MockObject\\Invocation' => '/phpunit/Framework/MockObject/Runtime/Invocation.php', + 'PHPUnit\\Framework\\MockObject\\InvocationHandler' => '/phpunit/Framework/MockObject/Runtime/InvocationHandler.php', 'PHPUnit\\Framework\\MockObject\\MatchBuilderNotFoundException' => '/phpunit/Framework/MockObject/Exception/MatchBuilderNotFoundException.php', - 'PHPUnit\\Framework\\MockObject\\Matcher' => '/phpunit/Framework/MockObject/Matcher.php', + 'PHPUnit\\Framework\\MockObject\\Matcher' => '/phpunit/Framework/MockObject/Runtime/Matcher.php', 'PHPUnit\\Framework\\MockObject\\MatcherAlreadyRegisteredException' => '/phpunit/Framework/MockObject/Exception/MatcherAlreadyRegisteredException.php', - 'PHPUnit\\Framework\\MockObject\\Method' => '/phpunit/Framework/MockObject/Api/Method.php', + 'PHPUnit\\Framework\\MockObject\\Method' => '/phpunit/Framework/MockObject/Runtime/Api/Method.php', 'PHPUnit\\Framework\\MockObject\\MethodCannotBeConfiguredException' => '/phpunit/Framework/MockObject/Exception/MethodCannotBeConfiguredException.php', 'PHPUnit\\Framework\\MockObject\\MethodNameAlreadyConfiguredException' => '/phpunit/Framework/MockObject/Exception/MethodNameAlreadyConfiguredException.php', - 'PHPUnit\\Framework\\MockObject\\MethodNameConstraint' => '/phpunit/Framework/MockObject/MethodNameConstraint.php', + 'PHPUnit\\Framework\\MockObject\\MethodNameConstraint' => '/phpunit/Framework/MockObject/Runtime/MethodNameConstraint.php', 'PHPUnit\\Framework\\MockObject\\MethodNameNotConfiguredException' => '/phpunit/Framework/MockObject/Exception/MethodNameNotConfiguredException.php', 'PHPUnit\\Framework\\MockObject\\MethodParametersAlreadyConfiguredException' => '/phpunit/Framework/MockObject/Exception/MethodParametersAlreadyConfiguredException.php', 'PHPUnit\\Framework\\MockObject\\MockBuilder' => '/phpunit/Framework/MockObject/MockBuilder.php', - 'PHPUnit\\Framework\\MockObject\\MockClass' => '/phpunit/Framework/MockObject/MockClass.php', - 'PHPUnit\\Framework\\MockObject\\MockMethod' => '/phpunit/Framework/MockObject/MockMethod.php', - 'PHPUnit\\Framework\\MockObject\\MockMethodSet' => '/phpunit/Framework/MockObject/MockMethodSet.php', - 'PHPUnit\\Framework\\MockObject\\MockObject' => '/phpunit/Framework/MockObject/MockObject.php', - 'PHPUnit\\Framework\\MockObject\\MockTrait' => '/phpunit/Framework/MockObject/MockTrait.php', - 'PHPUnit\\Framework\\MockObject\\MockType' => '/phpunit/Framework/MockObject/MockType.php', - 'PHPUnit\\Framework\\MockObject\\OriginalConstructorInvocationRequiredException' => '/phpunit/Framework/MockObject/Exception/OriginalConstructorInvocationRequiredException.php', + 'PHPUnit\\Framework\\MockObject\\MockObject' => '/phpunit/Framework/MockObject/Runtime/Interface/MockObject.php', + 'PHPUnit\\Framework\\MockObject\\MockObjectApi' => '/phpunit/Framework/MockObject/Runtime/Api/MockObjectApi.php', + 'PHPUnit\\Framework\\MockObject\\MockObjectInternal' => '/phpunit/Framework/MockObject/Runtime/Interface/MockObjectInternal.php', + 'PHPUnit\\Framework\\MockObject\\NeverReturningMethodException' => '/phpunit/Framework/MockObject/Exception/NeverReturningMethodException.php', + 'PHPUnit\\Framework\\MockObject\\ProxiedCloneMethod' => '/phpunit/Framework/MockObject/Runtime/Api/ProxiedCloneMethod.php', 'PHPUnit\\Framework\\MockObject\\ReflectionException' => '/phpunit/Framework/MockObject/Exception/ReflectionException.php', + 'PHPUnit\\Framework\\MockObject\\ReturnValueGenerator' => '/phpunit/Framework/MockObject/Runtime/ReturnValueGenerator.php', 'PHPUnit\\Framework\\MockObject\\ReturnValueNotConfiguredException' => '/phpunit/Framework/MockObject/Exception/ReturnValueNotConfiguredException.php', - 'PHPUnit\\Framework\\MockObject\\Rule\\AnyInvokedCount' => '/phpunit/Framework/MockObject/Rule/AnyInvokedCount.php', - 'PHPUnit\\Framework\\MockObject\\Rule\\AnyParameters' => '/phpunit/Framework/MockObject/Rule/AnyParameters.php', - 'PHPUnit\\Framework\\MockObject\\Rule\\ConsecutiveParameters' => '/phpunit/Framework/MockObject/Rule/ConsecutiveParameters.php', - 'PHPUnit\\Framework\\MockObject\\Rule\\InvocationOrder' => '/phpunit/Framework/MockObject/Rule/InvocationOrder.php', - 'PHPUnit\\Framework\\MockObject\\Rule\\InvokedAtIndex' => '/phpunit/Framework/MockObject/Rule/InvokedAtIndex.php', - 'PHPUnit\\Framework\\MockObject\\Rule\\InvokedAtLeastCount' => '/phpunit/Framework/MockObject/Rule/InvokedAtLeastCount.php', - 'PHPUnit\\Framework\\MockObject\\Rule\\InvokedAtLeastOnce' => '/phpunit/Framework/MockObject/Rule/InvokedAtLeastOnce.php', - 'PHPUnit\\Framework\\MockObject\\Rule\\InvokedAtMostCount' => '/phpunit/Framework/MockObject/Rule/InvokedAtMostCount.php', - 'PHPUnit\\Framework\\MockObject\\Rule\\InvokedCount' => '/phpunit/Framework/MockObject/Rule/InvokedCount.php', - 'PHPUnit\\Framework\\MockObject\\Rule\\MethodName' => '/phpunit/Framework/MockObject/Rule/MethodName.php', - 'PHPUnit\\Framework\\MockObject\\Rule\\Parameters' => '/phpunit/Framework/MockObject/Rule/Parameters.php', - 'PHPUnit\\Framework\\MockObject\\Rule\\ParametersRule' => '/phpunit/Framework/MockObject/Rule/ParametersRule.php', + 'PHPUnit\\Framework\\MockObject\\Rule\\AnyInvokedCount' => '/phpunit/Framework/MockObject/Runtime/Rule/AnyInvokedCount.php', + 'PHPUnit\\Framework\\MockObject\\Rule\\AnyParameters' => '/phpunit/Framework/MockObject/Runtime/Rule/AnyParameters.php', + 'PHPUnit\\Framework\\MockObject\\Rule\\InvocationOrder' => '/phpunit/Framework/MockObject/Runtime/Rule/InvocationOrder.php', + 'PHPUnit\\Framework\\MockObject\\Rule\\InvokedAtLeastCount' => '/phpunit/Framework/MockObject/Runtime/Rule/InvokedAtLeastCount.php', + 'PHPUnit\\Framework\\MockObject\\Rule\\InvokedAtLeastOnce' => '/phpunit/Framework/MockObject/Runtime/Rule/InvokedAtLeastOnce.php', + 'PHPUnit\\Framework\\MockObject\\Rule\\InvokedAtMostCount' => '/phpunit/Framework/MockObject/Runtime/Rule/InvokedAtMostCount.php', + 'PHPUnit\\Framework\\MockObject\\Rule\\InvokedCount' => '/phpunit/Framework/MockObject/Runtime/Rule/InvokedCount.php', + 'PHPUnit\\Framework\\MockObject\\Rule\\MethodName' => '/phpunit/Framework/MockObject/Runtime/Rule/MethodName.php', + 'PHPUnit\\Framework\\MockObject\\Rule\\Parameters' => '/phpunit/Framework/MockObject/Runtime/Rule/Parameters.php', + 'PHPUnit\\Framework\\MockObject\\Rule\\ParametersRule' => '/phpunit/Framework/MockObject/Runtime/Rule/ParametersRule.php', 'PHPUnit\\Framework\\MockObject\\RuntimeException' => '/phpunit/Framework/MockObject/Exception/RuntimeException.php', - 'PHPUnit\\Framework\\MockObject\\SoapExtensionNotAvailableException' => '/phpunit/Framework/MockObject/Exception/SoapExtensionNotAvailableException.php', - 'PHPUnit\\Framework\\MockObject\\Stub' => '/phpunit/Framework/MockObject/Stub.php', - 'PHPUnit\\Framework\\MockObject\\Stub\\ConsecutiveCalls' => '/phpunit/Framework/MockObject/Stub/ConsecutiveCalls.php', - 'PHPUnit\\Framework\\MockObject\\Stub\\Exception' => '/phpunit/Framework/MockObject/Stub/Exception.php', - 'PHPUnit\\Framework\\MockObject\\Stub\\ReturnArgument' => '/phpunit/Framework/MockObject/Stub/ReturnArgument.php', - 'PHPUnit\\Framework\\MockObject\\Stub\\ReturnCallback' => '/phpunit/Framework/MockObject/Stub/ReturnCallback.php', - 'PHPUnit\\Framework\\MockObject\\Stub\\ReturnReference' => '/phpunit/Framework/MockObject/Stub/ReturnReference.php', - 'PHPUnit\\Framework\\MockObject\\Stub\\ReturnSelf' => '/phpunit/Framework/MockObject/Stub/ReturnSelf.php', - 'PHPUnit\\Framework\\MockObject\\Stub\\ReturnStub' => '/phpunit/Framework/MockObject/Stub/ReturnStub.php', - 'PHPUnit\\Framework\\MockObject\\Stub\\ReturnValueMap' => '/phpunit/Framework/MockObject/Stub/ReturnValueMap.php', - 'PHPUnit\\Framework\\MockObject\\Stub\\Stub' => '/phpunit/Framework/MockObject/Stub/Stub.php', - 'PHPUnit\\Framework\\MockObject\\UnknownClassException' => '/phpunit/Framework/MockObject/Exception/UnknownClassException.php', - 'PHPUnit\\Framework\\MockObject\\UnknownTraitException' => '/phpunit/Framework/MockObject/Exception/UnknownTraitException.php', - 'PHPUnit\\Framework\\MockObject\\UnknownTypeException' => '/phpunit/Framework/MockObject/Exception/UnknownTypeException.php', - 'PHPUnit\\Framework\\MockObject\\Verifiable' => '/phpunit/Framework/MockObject/Verifiable.php', + 'PHPUnit\\Framework\\MockObject\\Stub' => '/phpunit/Framework/MockObject/Runtime/Interface/Stub.php', + 'PHPUnit\\Framework\\MockObject\\StubApi' => '/phpunit/Framework/MockObject/Runtime/Api/StubApi.php', + 'PHPUnit\\Framework\\MockObject\\StubInternal' => '/phpunit/Framework/MockObject/Runtime/Interface/StubInternal.php', + 'PHPUnit\\Framework\\MockObject\\Stub\\ConsecutiveCalls' => '/phpunit/Framework/MockObject/Runtime/Stub/ConsecutiveCalls.php', + 'PHPUnit\\Framework\\MockObject\\Stub\\Exception' => '/phpunit/Framework/MockObject/Runtime/Stub/Exception.php', + 'PHPUnit\\Framework\\MockObject\\Stub\\ReturnArgument' => '/phpunit/Framework/MockObject/Runtime/Stub/ReturnArgument.php', + 'PHPUnit\\Framework\\MockObject\\Stub\\ReturnCallback' => '/phpunit/Framework/MockObject/Runtime/Stub/ReturnCallback.php', + 'PHPUnit\\Framework\\MockObject\\Stub\\ReturnReference' => '/phpunit/Framework/MockObject/Runtime/Stub/ReturnReference.php', + 'PHPUnit\\Framework\\MockObject\\Stub\\ReturnSelf' => '/phpunit/Framework/MockObject/Runtime/Stub/ReturnSelf.php', + 'PHPUnit\\Framework\\MockObject\\Stub\\ReturnStub' => '/phpunit/Framework/MockObject/Runtime/Stub/ReturnStub.php', + 'PHPUnit\\Framework\\MockObject\\Stub\\ReturnValueMap' => '/phpunit/Framework/MockObject/Runtime/Stub/ReturnValueMap.php', + 'PHPUnit\\Framework\\MockObject\\Stub\\Stub' => '/phpunit/Framework/MockObject/Runtime/Stub/Stub.php', 'PHPUnit\\Framework\\NoChildTestSuiteException' => '/phpunit/Framework/Exception/NoChildTestSuiteException.php', - 'PHPUnit\\Framework\\OutputError' => '/phpunit/Framework/Exception/OutputError.php', - 'PHPUnit\\Framework\\PHPTAssertionFailedError' => '/phpunit/Framework/Exception/PHPTAssertionFailedError.php', + 'PHPUnit\\Framework\\PhptAssertionFailedError' => '/phpunit/Framework/Exception/PhptAssertionFailedError.php', + 'PHPUnit\\Framework\\ProcessIsolationException' => '/phpunit/Framework/Exception/ProcessIsolationException.php', 'PHPUnit\\Framework\\Reorderable' => '/phpunit/Framework/Reorderable.php', - 'PHPUnit\\Framework\\RiskyTestError' => '/phpunit/Framework/Exception/RiskyTestError.php', 'PHPUnit\\Framework\\SelfDescribing' => '/phpunit/Framework/SelfDescribing.php', - 'PHPUnit\\Framework\\SkippedTest' => '/phpunit/Framework/SkippedTest.php', - 'PHPUnit\\Framework\\SkippedTestCase' => '/phpunit/Framework/SkippedTestCase.php', - 'PHPUnit\\Framework\\SkippedTestError' => '/phpunit/Framework/Exception/SkippedTestError.php', - 'PHPUnit\\Framework\\SkippedTestSuiteError' => '/phpunit/Framework/Exception/SkippedTestSuiteError.php', - 'PHPUnit\\Framework\\SyntheticError' => '/phpunit/Framework/Exception/SyntheticError.php', - 'PHPUnit\\Framework\\SyntheticSkippedError' => '/phpunit/Framework/Exception/SyntheticSkippedError.php', + 'PHPUnit\\Framework\\SkippedTest' => '/phpunit/Framework/Exception/Skipped/SkippedTest.php', + 'PHPUnit\\Framework\\SkippedTestSuiteError' => '/phpunit/Framework/Exception/Skipped/SkippedTestSuiteError.php', + 'PHPUnit\\Framework\\SkippedWithMessageException' => '/phpunit/Framework/Exception/Skipped/SkippedWithMessageException.php', 'PHPUnit\\Framework\\Test' => '/phpunit/Framework/Test.php', 'PHPUnit\\Framework\\TestBuilder' => '/phpunit/Framework/TestBuilder.php', 'PHPUnit\\Framework\\TestCase' => '/phpunit/Framework/TestCase.php', - 'PHPUnit\\Framework\\TestFailure' => '/phpunit/Framework/TestFailure.php', - 'PHPUnit\\Framework\\TestListener' => '/phpunit/Framework/TestListener.php', - 'PHPUnit\\Framework\\TestListenerDefaultImplementation' => '/phpunit/Framework/TestListenerDefaultImplementation.php', - 'PHPUnit\\Framework\\TestResult' => '/phpunit/Framework/TestResult.php', + 'PHPUnit\\Framework\\TestRunner' => '/phpunit/Framework/TestRunner.php', + 'PHPUnit\\Framework\\TestSize\\Known' => '/phpunit/Framework/TestSize/Known.php', + 'PHPUnit\\Framework\\TestSize\\Large' => '/phpunit/Framework/TestSize/Large.php', + 'PHPUnit\\Framework\\TestSize\\Medium' => '/phpunit/Framework/TestSize/Medium.php', + 'PHPUnit\\Framework\\TestSize\\Small' => '/phpunit/Framework/TestSize/Small.php', + 'PHPUnit\\Framework\\TestSize\\TestSize' => '/phpunit/Framework/TestSize/TestSize.php', + 'PHPUnit\\Framework\\TestSize\\Unknown' => '/phpunit/Framework/TestSize/Unknown.php', + 'PHPUnit\\Framework\\TestStatus\\Deprecation' => '/phpunit/Framework/TestStatus/Deprecation.php', + 'PHPUnit\\Framework\\TestStatus\\Error' => '/phpunit/Framework/TestStatus/Error.php', + 'PHPUnit\\Framework\\TestStatus\\Failure' => '/phpunit/Framework/TestStatus/Failure.php', + 'PHPUnit\\Framework\\TestStatus\\Incomplete' => '/phpunit/Framework/TestStatus/Incomplete.php', + 'PHPUnit\\Framework\\TestStatus\\Known' => '/phpunit/Framework/TestStatus/Known.php', + 'PHPUnit\\Framework\\TestStatus\\Notice' => '/phpunit/Framework/TestStatus/Notice.php', + 'PHPUnit\\Framework\\TestStatus\\Risky' => '/phpunit/Framework/TestStatus/Risky.php', + 'PHPUnit\\Framework\\TestStatus\\Skipped' => '/phpunit/Framework/TestStatus/Skipped.php', + 'PHPUnit\\Framework\\TestStatus\\Success' => '/phpunit/Framework/TestStatus/Success.php', + 'PHPUnit\\Framework\\TestStatus\\TestStatus' => '/phpunit/Framework/TestStatus/TestStatus.php', + 'PHPUnit\\Framework\\TestStatus\\Unknown' => '/phpunit/Framework/TestStatus/Unknown.php', + 'PHPUnit\\Framework\\TestStatus\\Warning' => '/phpunit/Framework/TestStatus/Warning.php', 'PHPUnit\\Framework\\TestSuite' => '/phpunit/Framework/TestSuite.php', 'PHPUnit\\Framework\\TestSuiteIterator' => '/phpunit/Framework/TestSuiteIterator.php', - 'PHPUnit\\Framework\\UnintentionallyCoveredCodeError' => '/phpunit/Framework/Exception/UnintentionallyCoveredCodeError.php', - 'PHPUnit\\Framework\\Warning' => '/phpunit/Framework/Exception/Warning.php', - 'PHPUnit\\Framework\\WarningTestCase' => '/phpunit/Framework/WarningTestCase.php', - 'PHPUnit\\Runner\\AfterIncompleteTestHook' => '/phpunit/Runner/Hook/AfterIncompleteTestHook.php', - 'PHPUnit\\Runner\\AfterLastTestHook' => '/phpunit/Runner/Hook/AfterLastTestHook.php', - 'PHPUnit\\Runner\\AfterRiskyTestHook' => '/phpunit/Runner/Hook/AfterRiskyTestHook.php', - 'PHPUnit\\Runner\\AfterSkippedTestHook' => '/phpunit/Runner/Hook/AfterSkippedTestHook.php', - 'PHPUnit\\Runner\\AfterSuccessfulTestHook' => '/phpunit/Runner/Hook/AfterSuccessfulTestHook.php', - 'PHPUnit\\Runner\\AfterTestErrorHook' => '/phpunit/Runner/Hook/AfterTestErrorHook.php', - 'PHPUnit\\Runner\\AfterTestFailureHook' => '/phpunit/Runner/Hook/AfterTestFailureHook.php', - 'PHPUnit\\Runner\\AfterTestHook' => '/phpunit/Runner/Hook/AfterTestHook.php', - 'PHPUnit\\Runner\\AfterTestWarningHook' => '/phpunit/Runner/Hook/AfterTestWarningHook.php', - 'PHPUnit\\Runner\\BaseTestRunner' => '/phpunit/Runner/BaseTestRunner.php', - 'PHPUnit\\Runner\\BeforeFirstTestHook' => '/phpunit/Runner/Hook/BeforeFirstTestHook.php', - 'PHPUnit\\Runner\\BeforeTestHook' => '/phpunit/Runner/Hook/BeforeTestHook.php', - 'PHPUnit\\Runner\\DefaultTestResultCache' => '/phpunit/Runner/DefaultTestResultCache.php', - 'PHPUnit\\Runner\\Exception' => '/phpunit/Runner/Exception.php', - 'PHPUnit\\Runner\\Extension\\ExtensionHandler' => '/phpunit/Runner/Extension/ExtensionHandler.php', + 'PHPUnit\\Framework\\UnknownClassOrInterfaceException' => '/phpunit/Framework/Exception/UnknownClassOrInterfaceException.php', + 'PHPUnit\\Framework\\UnknownTypeException' => '/phpunit/Framework/Exception/UnknownTypeException.php', + 'PHPUnit\\Logging\\EventLogger' => '/phpunit/Logging/EventLogger.php', + 'PHPUnit\\Logging\\Exception' => '/phpunit/Logging/Exception.php', + 'PHPUnit\\Logging\\JUnit\\JunitXmlLogger' => '/phpunit/Logging/JUnit/JunitXmlLogger.php', + 'PHPUnit\\Logging\\JUnit\\Subscriber' => '/phpunit/Logging/JUnit/Subscriber/Subscriber.php', + 'PHPUnit\\Logging\\JUnit\\TestErroredSubscriber' => '/phpunit/Logging/JUnit/Subscriber/TestErroredSubscriber.php', + 'PHPUnit\\Logging\\JUnit\\TestFailedSubscriber' => '/phpunit/Logging/JUnit/Subscriber/TestFailedSubscriber.php', + 'PHPUnit\\Logging\\JUnit\\TestFinishedSubscriber' => '/phpunit/Logging/JUnit/Subscriber/TestFinishedSubscriber.php', + 'PHPUnit\\Logging\\JUnit\\TestMarkedIncompleteSubscriber' => '/phpunit/Logging/JUnit/Subscriber/TestMarkedIncompleteSubscriber.php', + 'PHPUnit\\Logging\\JUnit\\TestPreparationFailedSubscriber' => '/phpunit/Logging/JUnit/Subscriber/TestPreparationFailedSubscriber.php', + 'PHPUnit\\Logging\\JUnit\\TestPreparationStartedSubscriber' => '/phpunit/Logging/JUnit/Subscriber/TestPreparationStartedSubscriber.php', + 'PHPUnit\\Logging\\JUnit\\TestPreparedSubscriber' => '/phpunit/Logging/JUnit/Subscriber/TestPreparedSubscriber.php', + 'PHPUnit\\Logging\\JUnit\\TestRunnerExecutionFinishedSubscriber' => '/phpunit/Logging/JUnit/Subscriber/TestRunnerExecutionFinishedSubscriber.php', + 'PHPUnit\\Logging\\JUnit\\TestSkippedSubscriber' => '/phpunit/Logging/JUnit/Subscriber/TestSkippedSubscriber.php', + 'PHPUnit\\Logging\\JUnit\\TestSuiteFinishedSubscriber' => '/phpunit/Logging/JUnit/Subscriber/TestSuiteFinishedSubscriber.php', + 'PHPUnit\\Logging\\JUnit\\TestSuiteStartedSubscriber' => '/phpunit/Logging/JUnit/Subscriber/TestSuiteStartedSubscriber.php', + 'PHPUnit\\Logging\\TeamCity\\Subscriber' => '/phpunit/Logging/TeamCity/Subscriber/Subscriber.php', + 'PHPUnit\\Logging\\TeamCity\\TeamCityLogger' => '/phpunit/Logging/TeamCity/TeamCityLogger.php', + 'PHPUnit\\Logging\\TeamCity\\TestConsideredRiskySubscriber' => '/phpunit/Logging/TeamCity/Subscriber/TestConsideredRiskySubscriber.php', + 'PHPUnit\\Logging\\TeamCity\\TestErroredSubscriber' => '/phpunit/Logging/TeamCity/Subscriber/TestErroredSubscriber.php', + 'PHPUnit\\Logging\\TeamCity\\TestFailedSubscriber' => '/phpunit/Logging/TeamCity/Subscriber/TestFailedSubscriber.php', + 'PHPUnit\\Logging\\TeamCity\\TestFinishedSubscriber' => '/phpunit/Logging/TeamCity/Subscriber/TestFinishedSubscriber.php', + 'PHPUnit\\Logging\\TeamCity\\TestMarkedIncompleteSubscriber' => '/phpunit/Logging/TeamCity/Subscriber/TestMarkedIncompleteSubscriber.php', + 'PHPUnit\\Logging\\TeamCity\\TestPreparedSubscriber' => '/phpunit/Logging/TeamCity/Subscriber/TestPreparedSubscriber.php', + 'PHPUnit\\Logging\\TeamCity\\TestRunnerExecutionFinishedSubscriber' => '/phpunit/Logging/TeamCity/Subscriber/TestRunnerExecutionFinishedSubscriber.php', + 'PHPUnit\\Logging\\TeamCity\\TestSkippedSubscriber' => '/phpunit/Logging/TeamCity/Subscriber/TestSkippedSubscriber.php', + 'PHPUnit\\Logging\\TeamCity\\TestSuiteFinishedSubscriber' => '/phpunit/Logging/TeamCity/Subscriber/TestSuiteFinishedSubscriber.php', + 'PHPUnit\\Logging\\TeamCity\\TestSuiteStartedSubscriber' => '/phpunit/Logging/TeamCity/Subscriber/TestSuiteStartedSubscriber.php', + 'PHPUnit\\Logging\\TestDox\\HtmlRenderer' => '/phpunit/Logging/TestDox/HtmlRenderer.php', + 'PHPUnit\\Logging\\TestDox\\NamePrettifier' => '/phpunit/Logging/TestDox/NamePrettifier.php', + 'PHPUnit\\Logging\\TestDox\\PlainTextRenderer' => '/phpunit/Logging/TestDox/PlainTextRenderer.php', + 'PHPUnit\\Logging\\TestDox\\Subscriber' => '/phpunit/Logging/TestDox/TestResult/Subscriber/Subscriber.php', + 'PHPUnit\\Logging\\TestDox\\TestConsideredRiskySubscriber' => '/phpunit/Logging/TestDox/TestResult/Subscriber/TestConsideredRiskySubscriber.php', + 'PHPUnit\\Logging\\TestDox\\TestErroredSubscriber' => '/phpunit/Logging/TestDox/TestResult/Subscriber/TestErroredSubscriber.php', + 'PHPUnit\\Logging\\TestDox\\TestFailedSubscriber' => '/phpunit/Logging/TestDox/TestResult/Subscriber/TestFailedSubscriber.php', + 'PHPUnit\\Logging\\TestDox\\TestFinishedSubscriber' => '/phpunit/Logging/TestDox/TestResult/Subscriber/TestFinishedSubscriber.php', + 'PHPUnit\\Logging\\TestDox\\TestMarkedIncompleteSubscriber' => '/phpunit/Logging/TestDox/TestResult/Subscriber/TestMarkedIncompleteSubscriber.php', + 'PHPUnit\\Logging\\TestDox\\TestPassedSubscriber' => '/phpunit/Logging/TestDox/TestResult/Subscriber/TestPassedSubscriber.php', + 'PHPUnit\\Logging\\TestDox\\TestPreparedSubscriber' => '/phpunit/Logging/TestDox/TestResult/Subscriber/TestPreparedSubscriber.php', + 'PHPUnit\\Logging\\TestDox\\TestResult' => '/phpunit/Logging/TestDox/TestResult/TestResult.php', + 'PHPUnit\\Logging\\TestDox\\TestResultCollection' => '/phpunit/Logging/TestDox/TestResult/TestResultCollection.php', + 'PHPUnit\\Logging\\TestDox\\TestResultCollectionIterator' => '/phpunit/Logging/TestDox/TestResult/TestResultCollectionIterator.php', + 'PHPUnit\\Logging\\TestDox\\TestResultCollector' => '/phpunit/Logging/TestDox/TestResult/TestResultCollector.php', + 'PHPUnit\\Logging\\TestDox\\TestSkippedSubscriber' => '/phpunit/Logging/TestDox/TestResult/Subscriber/TestSkippedSubscriber.php', + 'PHPUnit\\Logging\\TestDox\\TestTriggeredDeprecationSubscriber' => '/phpunit/Logging/TestDox/TestResult/Subscriber/TestTriggeredDeprecationSubscriber.php', + 'PHPUnit\\Logging\\TestDox\\TestTriggeredNoticeSubscriber' => '/phpunit/Logging/TestDox/TestResult/Subscriber/TestTriggeredNoticeSubscriber.php', + 'PHPUnit\\Logging\\TestDox\\TestTriggeredPhpDeprecationSubscriber' => '/phpunit/Logging/TestDox/TestResult/Subscriber/TestTriggeredPhpDeprecationSubscriber.php', + 'PHPUnit\\Logging\\TestDox\\TestTriggeredPhpNoticeSubscriber' => '/phpunit/Logging/TestDox/TestResult/Subscriber/TestTriggeredPhpNoticeSubscriber.php', + 'PHPUnit\\Logging\\TestDox\\TestTriggeredPhpWarningSubscriber' => '/phpunit/Logging/TestDox/TestResult/Subscriber/TestTriggeredPhpWarningSubscriber.php', + 'PHPUnit\\Logging\\TestDox\\TestTriggeredPhpunitDeprecationSubscriber' => '/phpunit/Logging/TestDox/TestResult/Subscriber/TestTriggeredPhpunitDeprecationSubscriber.php', + 'PHPUnit\\Logging\\TestDox\\TestTriggeredPhpunitErrorSubscriber' => '/phpunit/Logging/TestDox/TestResult/Subscriber/TestTriggeredPhpunitErrorSubscriber.php', + 'PHPUnit\\Logging\\TestDox\\TestTriggeredPhpunitWarningSubscriber' => '/phpunit/Logging/TestDox/TestResult/Subscriber/TestTriggeredPhpunitWarningSubscriber.php', + 'PHPUnit\\Logging\\TestDox\\TestTriggeredWarningSubscriber' => '/phpunit/Logging/TestDox/TestResult/Subscriber/TestTriggeredWarningSubscriber.php', + 'PHPUnit\\Metadata\\After' => '/phpunit/Metadata/After.php', + 'PHPUnit\\Metadata\\AfterClass' => '/phpunit/Metadata/AfterClass.php', + 'PHPUnit\\Metadata\\Annotation\\Parser\\DocBlock' => '/phpunit/Metadata/Parser/Annotation/DocBlock.php', + 'PHPUnit\\Metadata\\Annotation\\Parser\\Registry' => '/phpunit/Metadata/Parser/Annotation/Registry.php', + 'PHPUnit\\Metadata\\AnnotationsAreNotSupportedForInternalClassesException' => '/phpunit/Metadata/Exception/AnnotationsAreNotSupportedForInternalClassesException.php', + 'PHPUnit\\Metadata\\Api\\CodeCoverage' => '/phpunit/Metadata/Api/CodeCoverage.php', + 'PHPUnit\\Metadata\\Api\\DataProvider' => '/phpunit/Metadata/Api/DataProvider.php', + 'PHPUnit\\Metadata\\Api\\Dependencies' => '/phpunit/Metadata/Api/Dependencies.php', + 'PHPUnit\\Metadata\\Api\\Groups' => '/phpunit/Metadata/Api/Groups.php', + 'PHPUnit\\Metadata\\Api\\HookMethods' => '/phpunit/Metadata/Api/HookMethods.php', + 'PHPUnit\\Metadata\\Api\\Requirements' => '/phpunit/Metadata/Api/Requirements.php', + 'PHPUnit\\Metadata\\BackupGlobals' => '/phpunit/Metadata/BackupGlobals.php', + 'PHPUnit\\Metadata\\BackupStaticProperties' => '/phpunit/Metadata/BackupStaticProperties.php', + 'PHPUnit\\Metadata\\Before' => '/phpunit/Metadata/Before.php', + 'PHPUnit\\Metadata\\BeforeClass' => '/phpunit/Metadata/BeforeClass.php', + 'PHPUnit\\Metadata\\Covers' => '/phpunit/Metadata/Covers.php', + 'PHPUnit\\Metadata\\CoversClass' => '/phpunit/Metadata/CoversClass.php', + 'PHPUnit\\Metadata\\CoversDefaultClass' => '/phpunit/Metadata/CoversDefaultClass.php', + 'PHPUnit\\Metadata\\CoversFunction' => '/phpunit/Metadata/CoversFunction.php', + 'PHPUnit\\Metadata\\CoversNothing' => '/phpunit/Metadata/CoversNothing.php', + 'PHPUnit\\Metadata\\DataProvider' => '/phpunit/Metadata/DataProvider.php', + 'PHPUnit\\Metadata\\DependsOnClass' => '/phpunit/Metadata/DependsOnClass.php', + 'PHPUnit\\Metadata\\DependsOnMethod' => '/phpunit/Metadata/DependsOnMethod.php', + 'PHPUnit\\Metadata\\DoesNotPerformAssertions' => '/phpunit/Metadata/DoesNotPerformAssertions.php', + 'PHPUnit\\Metadata\\Exception' => '/phpunit/Metadata/Exception/Exception.php', + 'PHPUnit\\Metadata\\ExcludeGlobalVariableFromBackup' => '/phpunit/Metadata/ExcludeGlobalVariableFromBackup.php', + 'PHPUnit\\Metadata\\ExcludeStaticPropertyFromBackup' => '/phpunit/Metadata/ExcludeStaticPropertyFromBackup.php', + 'PHPUnit\\Metadata\\Group' => '/phpunit/Metadata/Group.php', + 'PHPUnit\\Metadata\\IgnoreClassForCodeCoverage' => '/phpunit/Metadata/IgnoreClassForCodeCoverage.php', + 'PHPUnit\\Metadata\\IgnoreDeprecations' => '/phpunit/Metadata/IgnoreDeprecations.php', + 'PHPUnit\\Metadata\\IgnoreFunctionForCodeCoverage' => '/phpunit/Metadata/IgnoreFunctionForCodeCoverage.php', + 'PHPUnit\\Metadata\\IgnoreMethodForCodeCoverage' => '/phpunit/Metadata/IgnoreMethodForCodeCoverage.php', + 'PHPUnit\\Metadata\\InvalidVersionRequirementException' => '/phpunit/Metadata/Exception/InvalidVersionRequirementException.php', + 'PHPUnit\\Metadata\\Metadata' => '/phpunit/Metadata/Metadata.php', + 'PHPUnit\\Metadata\\MetadataCollection' => '/phpunit/Metadata/MetadataCollection.php', + 'PHPUnit\\Metadata\\MetadataCollectionIterator' => '/phpunit/Metadata/MetadataCollectionIterator.php', + 'PHPUnit\\Metadata\\NoVersionRequirementException' => '/phpunit/Metadata/Exception/NoVersionRequirementException.php', + 'PHPUnit\\Metadata\\Parser\\AnnotationParser' => '/phpunit/Metadata/Parser/AnnotationParser.php', + 'PHPUnit\\Metadata\\Parser\\AttributeParser' => '/phpunit/Metadata/Parser/AttributeParser.php', + 'PHPUnit\\Metadata\\Parser\\CachingParser' => '/phpunit/Metadata/Parser/CachingParser.php', + 'PHPUnit\\Metadata\\Parser\\Parser' => '/phpunit/Metadata/Parser/Parser.php', + 'PHPUnit\\Metadata\\Parser\\ParserChain' => '/phpunit/Metadata/Parser/ParserChain.php', + 'PHPUnit\\Metadata\\Parser\\Registry' => '/phpunit/Metadata/Parser/Registry.php', + 'PHPUnit\\Metadata\\PostCondition' => '/phpunit/Metadata/PostCondition.php', + 'PHPUnit\\Metadata\\PreCondition' => '/phpunit/Metadata/PreCondition.php', + 'PHPUnit\\Metadata\\PreserveGlobalState' => '/phpunit/Metadata/PreserveGlobalState.php', + 'PHPUnit\\Metadata\\ReflectionException' => '/phpunit/Metadata/Exception/ReflectionException.php', + 'PHPUnit\\Metadata\\RequiresFunction' => '/phpunit/Metadata/RequiresFunction.php', + 'PHPUnit\\Metadata\\RequiresMethod' => '/phpunit/Metadata/RequiresMethod.php', + 'PHPUnit\\Metadata\\RequiresOperatingSystem' => '/phpunit/Metadata/RequiresOperatingSystem.php', + 'PHPUnit\\Metadata\\RequiresOperatingSystemFamily' => '/phpunit/Metadata/RequiresOperatingSystemFamily.php', + 'PHPUnit\\Metadata\\RequiresPhp' => '/phpunit/Metadata/RequiresPhp.php', + 'PHPUnit\\Metadata\\RequiresPhpExtension' => '/phpunit/Metadata/RequiresPhpExtension.php', + 'PHPUnit\\Metadata\\RequiresPhpunit' => '/phpunit/Metadata/RequiresPhpunit.php', + 'PHPUnit\\Metadata\\RequiresSetting' => '/phpunit/Metadata/RequiresSetting.php', + 'PHPUnit\\Metadata\\RunClassInSeparateProcess' => '/phpunit/Metadata/RunClassInSeparateProcess.php', + 'PHPUnit\\Metadata\\RunInSeparateProcess' => '/phpunit/Metadata/RunInSeparateProcess.php', + 'PHPUnit\\Metadata\\RunTestsInSeparateProcesses' => '/phpunit/Metadata/RunTestsInSeparateProcesses.php', + 'PHPUnit\\Metadata\\Test' => '/phpunit/Metadata/Test.php', + 'PHPUnit\\Metadata\\TestDox' => '/phpunit/Metadata/TestDox.php', + 'PHPUnit\\Metadata\\TestWith' => '/phpunit/Metadata/TestWith.php', + 'PHPUnit\\Metadata\\Uses' => '/phpunit/Metadata/Uses.php', + 'PHPUnit\\Metadata\\UsesClass' => '/phpunit/Metadata/UsesClass.php', + 'PHPUnit\\Metadata\\UsesDefaultClass' => '/phpunit/Metadata/UsesDefaultClass.php', + 'PHPUnit\\Metadata\\UsesFunction' => '/phpunit/Metadata/UsesFunction.php', + 'PHPUnit\\Metadata\\Version\\ComparisonRequirement' => '/phpunit/Metadata/Version/ComparisonRequirement.php', + 'PHPUnit\\Metadata\\Version\\ConstraintRequirement' => '/phpunit/Metadata/Version/ConstraintRequirement.php', + 'PHPUnit\\Metadata\\Version\\Requirement' => '/phpunit/Metadata/Version/Requirement.php', + 'PHPUnit\\Metadata\\WithoutErrorHandler' => '/phpunit/Metadata/WithoutErrorHandler.php', + 'PHPUnit\\Runner\\Baseline\\Baseline' => '/phpunit/Runner/Baseline/Baseline.php', + 'PHPUnit\\Runner\\Baseline\\CannotLoadBaselineException' => '/phpunit/Runner/Baseline/Exception/CannotLoadBaselineException.php', + 'PHPUnit\\Runner\\Baseline\\FileDoesNotHaveLineException' => '/phpunit/Runner/Baseline/Exception/FileDoesNotHaveLineException.php', + 'PHPUnit\\Runner\\Baseline\\Generator' => '/phpunit/Runner/Baseline/Generator.php', + 'PHPUnit\\Runner\\Baseline\\Issue' => '/phpunit/Runner/Baseline/Issue.php', + 'PHPUnit\\Runner\\Baseline\\Reader' => '/phpunit/Runner/Baseline/Reader.php', + 'PHPUnit\\Runner\\Baseline\\RelativePathCalculator' => '/phpunit/Runner/Baseline/RelativePathCalculator.php', + 'PHPUnit\\Runner\\Baseline\\Subscriber' => '/phpunit/Runner/Baseline/Subscriber/Subscriber.php', + 'PHPUnit\\Runner\\Baseline\\TestTriggeredDeprecationSubscriber' => '/phpunit/Runner/Baseline/Subscriber/TestTriggeredDeprecationSubscriber.php', + 'PHPUnit\\Runner\\Baseline\\TestTriggeredNoticeSubscriber' => '/phpunit/Runner/Baseline/Subscriber/TestTriggeredNoticeSubscriber.php', + 'PHPUnit\\Runner\\Baseline\\TestTriggeredPhpDeprecationSubscriber' => '/phpunit/Runner/Baseline/Subscriber/TestTriggeredPhpDeprecationSubscriber.php', + 'PHPUnit\\Runner\\Baseline\\TestTriggeredPhpNoticeSubscriber' => '/phpunit/Runner/Baseline/Subscriber/TestTriggeredPhpNoticeSubscriber.php', + 'PHPUnit\\Runner\\Baseline\\TestTriggeredPhpWarningSubscriber' => '/phpunit/Runner/Baseline/Subscriber/TestTriggeredPhpWarningSubscriber.php', + 'PHPUnit\\Runner\\Baseline\\TestTriggeredWarningSubscriber' => '/phpunit/Runner/Baseline/Subscriber/TestTriggeredWarningSubscriber.php', + 'PHPUnit\\Runner\\Baseline\\Writer' => '/phpunit/Runner/Baseline/Writer.php', + 'PHPUnit\\Runner\\ClassCannotBeFoundException' => '/phpunit/Runner/Exception/ClassCannotBeFoundException.php', + 'PHPUnit\\Runner\\ClassDoesNotExtendTestCaseException' => '/phpunit/Runner/Exception/ClassDoesNotExtendTestCaseException.php', + 'PHPUnit\\Runner\\ClassIsAbstractException' => '/phpunit/Runner/Exception/ClassIsAbstractException.php', + 'PHPUnit\\Runner\\CodeCoverage' => '/phpunit/Runner/CodeCoverage.php', + 'PHPUnit\\Runner\\DirectoryDoesNotExistException' => '/phpunit/Runner/Exception/DirectoryDoesNotExistException.php', + 'PHPUnit\\Runner\\ErrorException' => '/phpunit/Runner/Exception/ErrorException.php', + 'PHPUnit\\Runner\\ErrorHandler' => '/phpunit/Runner/ErrorHandler.php', + 'PHPUnit\\Runner\\Exception' => '/phpunit/Runner/Exception/Exception.php', + 'PHPUnit\\Runner\\Extension\\Extension' => '/phpunit/Runner/Extension/Extension.php', + 'PHPUnit\\Runner\\Extension\\ExtensionBootstrapper' => '/phpunit/Runner/Extension/ExtensionBootstrapper.php', + 'PHPUnit\\Runner\\Extension\\Facade' => '/phpunit/Runner/Extension/Facade.php', + 'PHPUnit\\Runner\\Extension\\ParameterCollection' => '/phpunit/Runner/Extension/ParameterCollection.php', 'PHPUnit\\Runner\\Extension\\PharLoader' => '/phpunit/Runner/Extension/PharLoader.php', + 'PHPUnit\\Runner\\FileDoesNotExistException' => '/phpunit/Runner/Exception/FileDoesNotExistException.php', 'PHPUnit\\Runner\\Filter\\ExcludeGroupFilterIterator' => '/phpunit/Runner/Filter/ExcludeGroupFilterIterator.php', 'PHPUnit\\Runner\\Filter\\Factory' => '/phpunit/Runner/Filter/Factory.php', 'PHPUnit\\Runner\\Filter\\GroupFilterIterator' => '/phpunit/Runner/Filter/GroupFilterIterator.php', 'PHPUnit\\Runner\\Filter\\IncludeGroupFilterIterator' => '/phpunit/Runner/Filter/IncludeGroupFilterIterator.php', 'PHPUnit\\Runner\\Filter\\NameFilterIterator' => '/phpunit/Runner/Filter/NameFilterIterator.php', - 'PHPUnit\\Runner\\Hook' => '/phpunit/Runner/Hook/Hook.php', - 'PHPUnit\\Runner\\NullTestResultCache' => '/phpunit/Runner/NullTestResultCache.php', + 'PHPUnit\\Runner\\Filter\\TestIdFilterIterator' => '/phpunit/Runner/Filter/TestIdFilterIterator.php', + 'PHPUnit\\Runner\\GarbageCollection\\ExecutionFinishedSubscriber' => '/phpunit/Runner/GarbageCollection/Subscriber/ExecutionFinishedSubscriber.php', + 'PHPUnit\\Runner\\GarbageCollection\\ExecutionStartedSubscriber' => '/phpunit/Runner/GarbageCollection/Subscriber/ExecutionStartedSubscriber.php', + 'PHPUnit\\Runner\\GarbageCollection\\GarbageCollectionHandler' => '/phpunit/Runner/GarbageCollection/GarbageCollectionHandler.php', + 'PHPUnit\\Runner\\GarbageCollection\\Subscriber' => '/phpunit/Runner/GarbageCollection/Subscriber/Subscriber.php', + 'PHPUnit\\Runner\\GarbageCollection\\TestFinishedSubscriber' => '/phpunit/Runner/GarbageCollection/Subscriber/TestFinishedSubscriber.php', + 'PHPUnit\\Runner\\InvalidOrderException' => '/phpunit/Runner/Exception/InvalidOrderException.php', + 'PHPUnit\\Runner\\InvalidPhptFileException' => '/phpunit/Runner/Exception/InvalidPhptFileException.php', + 'PHPUnit\\Runner\\NoIgnoredEventException' => '/phpunit/Runner/Exception/NoIgnoredEventException.php', + 'PHPUnit\\Runner\\ParameterDoesNotExistException' => '/phpunit/Runner/Exception/ParameterDoesNotExistException.php', + 'PHPUnit\\Runner\\PhptExternalFileCannotBeLoadedException' => '/phpunit/Runner/Exception/PhptExternalFileCannotBeLoadedException.php', 'PHPUnit\\Runner\\PhptTestCase' => '/phpunit/Runner/PhptTestCase.php', - 'PHPUnit\\Runner\\ResultCacheExtension' => '/phpunit/Runner/ResultCacheExtension.php', - 'PHPUnit\\Runner\\StandardTestSuiteLoader' => '/phpunit/Runner/StandardTestSuiteLoader.php', - 'PHPUnit\\Runner\\TestHook' => '/phpunit/Runner/Hook/TestHook.php', - 'PHPUnit\\Runner\\TestListenerAdapter' => '/phpunit/Runner/Hook/TestListenerAdapter.php', - 'PHPUnit\\Runner\\TestResultCache' => '/phpunit/Runner/TestResultCache.php', + 'PHPUnit\\Runner\\ReflectionException' => '/phpunit/Runner/Exception/ReflectionException.php', + 'PHPUnit\\Runner\\ResultCache\\DefaultResultCache' => '/phpunit/Runner/ResultCache/DefaultResultCache.php', + 'PHPUnit\\Runner\\ResultCache\\NullResultCache' => '/phpunit/Runner/ResultCache/NullResultCache.php', + 'PHPUnit\\Runner\\ResultCache\\ResultCache' => '/phpunit/Runner/ResultCache/ResultCache.php', + 'PHPUnit\\Runner\\ResultCache\\ResultCacheHandler' => '/phpunit/Runner/ResultCache/ResultCacheHandler.php', + 'PHPUnit\\Runner\\ResultCache\\Subscriber' => '/phpunit/Runner/ResultCache/Subscriber/Subscriber.php', + 'PHPUnit\\Runner\\ResultCache\\TestConsideredRiskySubscriber' => '/phpunit/Runner/ResultCache/Subscriber/TestConsideredRiskySubscriber.php', + 'PHPUnit\\Runner\\ResultCache\\TestErroredSubscriber' => '/phpunit/Runner/ResultCache/Subscriber/TestErroredSubscriber.php', + 'PHPUnit\\Runner\\ResultCache\\TestFailedSubscriber' => '/phpunit/Runner/ResultCache/Subscriber/TestFailedSubscriber.php', + 'PHPUnit\\Runner\\ResultCache\\TestFinishedSubscriber' => '/phpunit/Runner/ResultCache/Subscriber/TestFinishedSubscriber.php', + 'PHPUnit\\Runner\\ResultCache\\TestMarkedIncompleteSubscriber' => '/phpunit/Runner/ResultCache/Subscriber/TestMarkedIncompleteSubscriber.php', + 'PHPUnit\\Runner\\ResultCache\\TestPreparedSubscriber' => '/phpunit/Runner/ResultCache/Subscriber/TestPreparedSubscriber.php', + 'PHPUnit\\Runner\\ResultCache\\TestSkippedSubscriber' => '/phpunit/Runner/ResultCache/Subscriber/TestSkippedSubscriber.php', + 'PHPUnit\\Runner\\ResultCache\\TestSuiteFinishedSubscriber' => '/phpunit/Runner/ResultCache/Subscriber/TestSuiteFinishedSubscriber.php', + 'PHPUnit\\Runner\\ResultCache\\TestSuiteStartedSubscriber' => '/phpunit/Runner/ResultCache/Subscriber/TestSuiteStartedSubscriber.php', 'PHPUnit\\Runner\\TestSuiteLoader' => '/phpunit/Runner/TestSuiteLoader.php', 'PHPUnit\\Runner\\TestSuiteSorter' => '/phpunit/Runner/TestSuiteSorter.php', + 'PHPUnit\\Runner\\UnsupportedPhptSectionException' => '/phpunit/Runner/Exception/UnsupportedPhptSectionException.php', 'PHPUnit\\Runner\\Version' => '/phpunit/Runner/Version.php', - 'PHPUnit\\TextUI\\CliArguments\\Builder' => '/phpunit/TextUI/CliArguments/Builder.php', - 'PHPUnit\\TextUI\\CliArguments\\Configuration' => '/phpunit/TextUI/CliArguments/Configuration.php', - 'PHPUnit\\TextUI\\CliArguments\\Exception' => '/phpunit/TextUI/CliArguments/Exception.php', - 'PHPUnit\\TextUI\\CliArguments\\Mapper' => '/phpunit/TextUI/CliArguments/Mapper.php', - 'PHPUnit\\TextUI\\Command' => '/phpunit/TextUI/Command.php', - 'PHPUnit\\TextUI\\DefaultResultPrinter' => '/phpunit/TextUI/DefaultResultPrinter.php', + 'PHPUnit\\TestRunner\\TestResult\\BeforeTestClassMethodErroredSubscriber' => '/phpunit/Runner/TestResult/Subscriber/BeforeTestClassMethodErroredSubscriber.php', + 'PHPUnit\\TestRunner\\TestResult\\Collector' => '/phpunit/Runner/TestResult/Collector.php', + 'PHPUnit\\TestRunner\\TestResult\\ExecutionStartedSubscriber' => '/phpunit/Runner/TestResult/Subscriber/ExecutionStartedSubscriber.php', + 'PHPUnit\\TestRunner\\TestResult\\Facade' => '/phpunit/Runner/TestResult/Facade.php', + 'PHPUnit\\TestRunner\\TestResult\\Issues\\Issue' => '/phpunit/Runner/TestResult/Issue.php', + 'PHPUnit\\TestRunner\\TestResult\\PassedTests' => '/phpunit/Runner/TestResult/PassedTests.php', + 'PHPUnit\\TestRunner\\TestResult\\Subscriber' => '/phpunit/Runner/TestResult/Subscriber/Subscriber.php', + 'PHPUnit\\TestRunner\\TestResult\\TestConsideredRiskySubscriber' => '/phpunit/Runner/TestResult/Subscriber/TestConsideredRiskySubscriber.php', + 'PHPUnit\\TestRunner\\TestResult\\TestErroredSubscriber' => '/phpunit/Runner/TestResult/Subscriber/TestErroredSubscriber.php', + 'PHPUnit\\TestRunner\\TestResult\\TestFailedSubscriber' => '/phpunit/Runner/TestResult/Subscriber/TestFailedSubscriber.php', + 'PHPUnit\\TestRunner\\TestResult\\TestFinishedSubscriber' => '/phpunit/Runner/TestResult/Subscriber/TestFinishedSubscriber.php', + 'PHPUnit\\TestRunner\\TestResult\\TestMarkedIncompleteSubscriber' => '/phpunit/Runner/TestResult/Subscriber/TestMarkedIncompleteSubscriber.php', + 'PHPUnit\\TestRunner\\TestResult\\TestPreparedSubscriber' => '/phpunit/Runner/TestResult/Subscriber/TestPreparedSubscriber.php', + 'PHPUnit\\TestRunner\\TestResult\\TestResult' => '/phpunit/Runner/TestResult/TestResult.php', + 'PHPUnit\\TestRunner\\TestResult\\TestRunnerTriggeredDeprecationSubscriber' => '/phpunit/Runner/TestResult/Subscriber/TestRunnerTriggeredDeprecationSubscriber.php', + 'PHPUnit\\TestRunner\\TestResult\\TestRunnerTriggeredWarningSubscriber' => '/phpunit/Runner/TestResult/Subscriber/TestRunnerTriggeredWarningSubscriber.php', + 'PHPUnit\\TestRunner\\TestResult\\TestSkippedSubscriber' => '/phpunit/Runner/TestResult/Subscriber/TestSkippedSubscriber.php', + 'PHPUnit\\TestRunner\\TestResult\\TestSuiteFinishedSubscriber' => '/phpunit/Runner/TestResult/Subscriber/TestSuiteFinishedSubscriber.php', + 'PHPUnit\\TestRunner\\TestResult\\TestSuiteSkippedSubscriber' => '/phpunit/Runner/TestResult/Subscriber/TestSuiteSkippedSubscriber.php', + 'PHPUnit\\TestRunner\\TestResult\\TestSuiteStartedSubscriber' => '/phpunit/Runner/TestResult/Subscriber/TestSuiteStartedSubscriber.php', + 'PHPUnit\\TestRunner\\TestResult\\TestTriggeredDeprecationSubscriber' => '/phpunit/Runner/TestResult/Subscriber/TestTriggeredDeprecationSubscriber.php', + 'PHPUnit\\TestRunner\\TestResult\\TestTriggeredErrorSubscriber' => '/phpunit/Runner/TestResult/Subscriber/TestTriggeredErrorSubscriber.php', + 'PHPUnit\\TestRunner\\TestResult\\TestTriggeredNoticeSubscriber' => '/phpunit/Runner/TestResult/Subscriber/TestTriggeredNoticeSubscriber.php', + 'PHPUnit\\TestRunner\\TestResult\\TestTriggeredPhpDeprecationSubscriber' => '/phpunit/Runner/TestResult/Subscriber/TestTriggeredPhpDeprecationSubscriber.php', + 'PHPUnit\\TestRunner\\TestResult\\TestTriggeredPhpNoticeSubscriber' => '/phpunit/Runner/TestResult/Subscriber/TestTriggeredPhpNoticeSubscriber.php', + 'PHPUnit\\TestRunner\\TestResult\\TestTriggeredPhpWarningSubscriber' => '/phpunit/Runner/TestResult/Subscriber/TestTriggeredPhpWarningSubscriber.php', + 'PHPUnit\\TestRunner\\TestResult\\TestTriggeredPhpunitDeprecationSubscriber' => '/phpunit/Runner/TestResult/Subscriber/TestTriggeredPhpunitDeprecationSubscriber.php', + 'PHPUnit\\TestRunner\\TestResult\\TestTriggeredPhpunitErrorSubscriber' => '/phpunit/Runner/TestResult/Subscriber/TestTriggeredPhpunitErrorSubscriber.php', + 'PHPUnit\\TestRunner\\TestResult\\TestTriggeredPhpunitWarningSubscriber' => '/phpunit/Runner/TestResult/Subscriber/TestTriggeredPhpunitWarningSubscriber.php', + 'PHPUnit\\TestRunner\\TestResult\\TestTriggeredWarningSubscriber' => '/phpunit/Runner/TestResult/Subscriber/TestTriggeredWarningSubscriber.php', + 'PHPUnit\\TextUI\\Application' => '/phpunit/TextUI/Application.php', + 'PHPUnit\\TextUI\\CannotOpenSocketException' => '/phpunit/TextUI/Exception/CannotOpenSocketException.php', + 'PHPUnit\\TextUI\\CliArguments\\Builder' => '/phpunit/TextUI/Configuration/Cli/Builder.php', + 'PHPUnit\\TextUI\\CliArguments\\Configuration' => '/phpunit/TextUI/Configuration/Cli/Configuration.php', + 'PHPUnit\\TextUI\\CliArguments\\Exception' => '/phpunit/TextUI/Configuration/Cli/Exception.php', + 'PHPUnit\\TextUI\\CliArguments\\XmlConfigurationFileFinder' => '/phpunit/TextUI/Configuration/Cli/XmlConfigurationFileFinder.php', + 'PHPUnit\\TextUI\\Command\\AtLeastVersionCommand' => '/phpunit/TextUI/Command/Commands/AtLeastVersionCommand.php', + 'PHPUnit\\TextUI\\Command\\Command' => '/phpunit/TextUI/Command/Command.php', + 'PHPUnit\\TextUI\\Command\\GenerateConfigurationCommand' => '/phpunit/TextUI/Command/Commands/GenerateConfigurationCommand.php', + 'PHPUnit\\TextUI\\Command\\ListGroupsCommand' => '/phpunit/TextUI/Command/Commands/ListGroupsCommand.php', + 'PHPUnit\\TextUI\\Command\\ListTestSuitesCommand' => '/phpunit/TextUI/Command/Commands/ListTestSuitesCommand.php', + 'PHPUnit\\TextUI\\Command\\ListTestsAsTextCommand' => '/phpunit/TextUI/Command/Commands/ListTestsAsTextCommand.php', + 'PHPUnit\\TextUI\\Command\\ListTestsAsXmlCommand' => '/phpunit/TextUI/Command/Commands/ListTestsAsXmlCommand.php', + 'PHPUnit\\TextUI\\Command\\MigrateConfigurationCommand' => '/phpunit/TextUI/Command/Commands/MigrateConfigurationCommand.php', + 'PHPUnit\\TextUI\\Command\\Result' => '/phpunit/TextUI/Command/Result.php', + 'PHPUnit\\TextUI\\Command\\ShowHelpCommand' => '/phpunit/TextUI/Command/Commands/ShowHelpCommand.php', + 'PHPUnit\\TextUI\\Command\\ShowVersionCommand' => '/phpunit/TextUI/Command/Commands/ShowVersionCommand.php', + 'PHPUnit\\TextUI\\Command\\VersionCheckCommand' => '/phpunit/TextUI/Command/Commands/VersionCheckCommand.php', + 'PHPUnit\\TextUI\\Command\\WarmCodeCoverageCacheCommand' => '/phpunit/TextUI/Command/Commands/WarmCodeCoverageCacheCommand.php', + 'PHPUnit\\TextUI\\Configuration\\Builder' => '/phpunit/TextUI/Configuration/Builder.php', + 'PHPUnit\\TextUI\\Configuration\\CodeCoverageFilterRegistry' => '/phpunit/TextUI/Configuration/CodeCoverageFilterRegistry.php', + 'PHPUnit\\TextUI\\Configuration\\CodeCoverageReportNotConfiguredException' => '/phpunit/TextUI/Configuration/Exception/CodeCoverageReportNotConfiguredException.php', + 'PHPUnit\\TextUI\\Configuration\\Configuration' => '/phpunit/TextUI/Configuration/Configuration.php', + 'PHPUnit\\TextUI\\Configuration\\ConfigurationCannotBeBuiltException' => '/phpunit/TextUI/Configuration/Exception/ConfigurationCannotBeBuiltException.php', + 'PHPUnit\\TextUI\\Configuration\\Constant' => '/phpunit/TextUI/Configuration/Value/Constant.php', + 'PHPUnit\\TextUI\\Configuration\\ConstantCollection' => '/phpunit/TextUI/Configuration/Value/ConstantCollection.php', + 'PHPUnit\\TextUI\\Configuration\\ConstantCollectionIterator' => '/phpunit/TextUI/Configuration/Value/ConstantCollectionIterator.php', + 'PHPUnit\\TextUI\\Configuration\\Directory' => '/phpunit/TextUI/Configuration/Value/Directory.php', + 'PHPUnit\\TextUI\\Configuration\\DirectoryCollection' => '/phpunit/TextUI/Configuration/Value/DirectoryCollection.php', + 'PHPUnit\\TextUI\\Configuration\\DirectoryCollectionIterator' => '/phpunit/TextUI/Configuration/Value/DirectoryCollectionIterator.php', + 'PHPUnit\\TextUI\\Configuration\\Exception' => '/phpunit/TextUI/Configuration/Exception/Exception.php', + 'PHPUnit\\TextUI\\Configuration\\ExtensionBootstrap' => '/phpunit/TextUI/Configuration/Value/ExtensionBootstrap.php', + 'PHPUnit\\TextUI\\Configuration\\ExtensionBootstrapCollection' => '/phpunit/TextUI/Configuration/Value/ExtensionBootstrapCollection.php', + 'PHPUnit\\TextUI\\Configuration\\ExtensionBootstrapCollectionIterator' => '/phpunit/TextUI/Configuration/Value/ExtensionBootstrapCollectionIterator.php', + 'PHPUnit\\TextUI\\Configuration\\File' => '/phpunit/TextUI/Configuration/Value/File.php', + 'PHPUnit\\TextUI\\Configuration\\FileCollection' => '/phpunit/TextUI/Configuration/Value/FileCollection.php', + 'PHPUnit\\TextUI\\Configuration\\FileCollectionIterator' => '/phpunit/TextUI/Configuration/Value/FileCollectionIterator.php', + 'PHPUnit\\TextUI\\Configuration\\FilterDirectory' => '/phpunit/TextUI/Configuration/Value/FilterDirectory.php', + 'PHPUnit\\TextUI\\Configuration\\FilterDirectoryCollection' => '/phpunit/TextUI/Configuration/Value/FilterDirectoryCollection.php', + 'PHPUnit\\TextUI\\Configuration\\FilterDirectoryCollectionIterator' => '/phpunit/TextUI/Configuration/Value/FilterDirectoryCollectionIterator.php', + 'PHPUnit\\TextUI\\Configuration\\FilterNotConfiguredException' => '/phpunit/TextUI/Configuration/Exception/FilterNotConfiguredException.php', + 'PHPUnit\\TextUI\\Configuration\\Group' => '/phpunit/TextUI/Configuration/Value/Group.php', + 'PHPUnit\\TextUI\\Configuration\\GroupCollection' => '/phpunit/TextUI/Configuration/Value/GroupCollection.php', + 'PHPUnit\\TextUI\\Configuration\\GroupCollectionIterator' => '/phpunit/TextUI/Configuration/Value/GroupCollectionIterator.php', + 'PHPUnit\\TextUI\\Configuration\\IncludePathNotConfiguredException' => '/phpunit/TextUI/Configuration/Exception/IncludePathNotConfiguredException.php', + 'PHPUnit\\TextUI\\Configuration\\IniSetting' => '/phpunit/TextUI/Configuration/Value/IniSetting.php', + 'PHPUnit\\TextUI\\Configuration\\IniSettingCollection' => '/phpunit/TextUI/Configuration/Value/IniSettingCollection.php', + 'PHPUnit\\TextUI\\Configuration\\IniSettingCollectionIterator' => '/phpunit/TextUI/Configuration/Value/IniSettingCollectionIterator.php', + 'PHPUnit\\TextUI\\Configuration\\LoggingNotConfiguredException' => '/phpunit/TextUI/Configuration/Exception/LoggingNotConfiguredException.php', + 'PHPUnit\\TextUI\\Configuration\\Merger' => '/phpunit/TextUI/Configuration/Merger.php', + 'PHPUnit\\TextUI\\Configuration\\NoBaselineException' => '/phpunit/TextUI/Configuration/Exception/NoBaselineException.php', + 'PHPUnit\\TextUI\\Configuration\\NoBootstrapException' => '/phpunit/TextUI/Configuration/Exception/NoBootstrapException.php', + 'PHPUnit\\TextUI\\Configuration\\NoCacheDirectoryException' => '/phpunit/TextUI/Configuration/Exception/NoCacheDirectoryException.php', + 'PHPUnit\\TextUI\\Configuration\\NoCliArgumentException' => '/phpunit/TextUI/Configuration/Exception/NoCliArgumentException.php', + 'PHPUnit\\TextUI\\Configuration\\NoConfigurationFileException' => '/phpunit/TextUI/Configuration/Exception/NoConfigurationFileException.php', + 'PHPUnit\\TextUI\\Configuration\\NoCoverageCacheDirectoryException' => '/phpunit/TextUI/Configuration/Exception/NoCoverageCacheDirectoryException.php', + 'PHPUnit\\TextUI\\Configuration\\NoCustomCssFileException' => '/phpunit/TextUI/Configuration/Exception/NoCustomCssFileException.php', + 'PHPUnit\\TextUI\\Configuration\\NoDefaultTestSuiteException' => '/phpunit/TextUI/Configuration/Exception/NoDefaultTestSuiteException.php', + 'PHPUnit\\TextUI\\Configuration\\NoPharExtensionDirectoryException' => '/phpunit/TextUI/Configuration/Exception/NoPharExtensionDirectoryException.php', + 'PHPUnit\\TextUI\\Configuration\\Php' => '/phpunit/TextUI/Configuration/Value/Php.php', + 'PHPUnit\\TextUI\\Configuration\\PhpHandler' => '/phpunit/TextUI/Configuration/PhpHandler.php', + 'PHPUnit\\TextUI\\Configuration\\Registry' => '/phpunit/TextUI/Configuration/Registry.php', + 'PHPUnit\\TextUI\\Configuration\\Source' => '/phpunit/TextUI/Configuration/Value/Source.php', + 'PHPUnit\\TextUI\\Configuration\\SourceFilter' => '/phpunit/TextUI/Configuration/SourceFilter.php', + 'PHPUnit\\TextUI\\Configuration\\SourceMapper' => '/phpunit/TextUI/Configuration/SourceMapper.php', + 'PHPUnit\\TextUI\\Configuration\\TestDirectory' => '/phpunit/TextUI/Configuration/Value/TestDirectory.php', + 'PHPUnit\\TextUI\\Configuration\\TestDirectoryCollection' => '/phpunit/TextUI/Configuration/Value/TestDirectoryCollection.php', + 'PHPUnit\\TextUI\\Configuration\\TestDirectoryCollectionIterator' => '/phpunit/TextUI/Configuration/Value/TestDirectoryCollectionIterator.php', + 'PHPUnit\\TextUI\\Configuration\\TestFile' => '/phpunit/TextUI/Configuration/Value/TestFile.php', + 'PHPUnit\\TextUI\\Configuration\\TestFileCollection' => '/phpunit/TextUI/Configuration/Value/TestFileCollection.php', + 'PHPUnit\\TextUI\\Configuration\\TestFileCollectionIterator' => '/phpunit/TextUI/Configuration/Value/TestFileCollectionIterator.php', + 'PHPUnit\\TextUI\\Configuration\\TestSuite' => '/phpunit/TextUI/Configuration/Value/TestSuite.php', + 'PHPUnit\\TextUI\\Configuration\\TestSuiteBuilder' => '/phpunit/TextUI/Configuration/TestSuiteBuilder.php', + 'PHPUnit\\TextUI\\Configuration\\TestSuiteCollection' => '/phpunit/TextUI/Configuration/Value/TestSuiteCollection.php', + 'PHPUnit\\TextUI\\Configuration\\TestSuiteCollectionIterator' => '/phpunit/TextUI/Configuration/Value/TestSuiteCollectionIterator.php', + 'PHPUnit\\TextUI\\Configuration\\Variable' => '/phpunit/TextUI/Configuration/Value/Variable.php', + 'PHPUnit\\TextUI\\Configuration\\VariableCollection' => '/phpunit/TextUI/Configuration/Value/VariableCollection.php', + 'PHPUnit\\TextUI\\Configuration\\VariableCollectionIterator' => '/phpunit/TextUI/Configuration/Value/VariableCollectionIterator.php', 'PHPUnit\\TextUI\\Exception' => '/phpunit/TextUI/Exception/Exception.php', + 'PHPUnit\\TextUI\\ExtensionsNotConfiguredException' => '/phpunit/TextUI/Exception/ExtensionsNotConfiguredException.php', 'PHPUnit\\TextUI\\Help' => '/phpunit/TextUI/Help.php', + 'PHPUnit\\TextUI\\InvalidSocketException' => '/phpunit/TextUI/Exception/InvalidSocketException.php', + 'PHPUnit\\TextUI\\Output\\DefaultPrinter' => '/phpunit/TextUI/Output/Printer/DefaultPrinter.php', + 'PHPUnit\\TextUI\\Output\\Default\\ProgressPrinter\\BeforeTestClassMethodErroredSubscriber' => '/phpunit/TextUI/Output/Default/ProgressPrinter/Subscriber/BeforeTestClassMethodErroredSubscriber.php', + 'PHPUnit\\TextUI\\Output\\Default\\ProgressPrinter\\ProgressPrinter' => '/phpunit/TextUI/Output/Default/ProgressPrinter/ProgressPrinter.php', + 'PHPUnit\\TextUI\\Output\\Default\\ProgressPrinter\\Subscriber' => '/phpunit/TextUI/Output/Default/ProgressPrinter/Subscriber/Subscriber.php', + 'PHPUnit\\TextUI\\Output\\Default\\ProgressPrinter\\TestConsideredRiskySubscriber' => '/phpunit/TextUI/Output/Default/ProgressPrinter/Subscriber/TestConsideredRiskySubscriber.php', + 'PHPUnit\\TextUI\\Output\\Default\\ProgressPrinter\\TestErroredSubscriber' => '/phpunit/TextUI/Output/Default/ProgressPrinter/Subscriber/TestErroredSubscriber.php', + 'PHPUnit\\TextUI\\Output\\Default\\ProgressPrinter\\TestFailedSubscriber' => '/phpunit/TextUI/Output/Default/ProgressPrinter/Subscriber/TestFailedSubscriber.php', + 'PHPUnit\\TextUI\\Output\\Default\\ProgressPrinter\\TestFinishedSubscriber' => '/phpunit/TextUI/Output/Default/ProgressPrinter/Subscriber/TestFinishedSubscriber.php', + 'PHPUnit\\TextUI\\Output\\Default\\ProgressPrinter\\TestMarkedIncompleteSubscriber' => '/phpunit/TextUI/Output/Default/ProgressPrinter/Subscriber/TestMarkedIncompleteSubscriber.php', + 'PHPUnit\\TextUI\\Output\\Default\\ProgressPrinter\\TestPreparedSubscriber' => '/phpunit/TextUI/Output/Default/ProgressPrinter/Subscriber/TestPreparedSubscriber.php', + 'PHPUnit\\TextUI\\Output\\Default\\ProgressPrinter\\TestRunnerExecutionStartedSubscriber' => '/phpunit/TextUI/Output/Default/ProgressPrinter/Subscriber/TestRunnerExecutionStartedSubscriber.php', + 'PHPUnit\\TextUI\\Output\\Default\\ProgressPrinter\\TestSkippedSubscriber' => '/phpunit/TextUI/Output/Default/ProgressPrinter/Subscriber/TestSkippedSubscriber.php', + 'PHPUnit\\TextUI\\Output\\Default\\ProgressPrinter\\TestTriggeredDeprecationSubscriber' => '/phpunit/TextUI/Output/Default/ProgressPrinter/Subscriber/TestTriggeredDeprecationSubscriber.php', + 'PHPUnit\\TextUI\\Output\\Default\\ProgressPrinter\\TestTriggeredErrorSubscriber' => '/phpunit/TextUI/Output/Default/ProgressPrinter/Subscriber/TestTriggeredErrorSubscriber.php', + 'PHPUnit\\TextUI\\Output\\Default\\ProgressPrinter\\TestTriggeredNoticeSubscriber' => '/phpunit/TextUI/Output/Default/ProgressPrinter/Subscriber/TestTriggeredNoticeSubscriber.php', + 'PHPUnit\\TextUI\\Output\\Default\\ProgressPrinter\\TestTriggeredPhpDeprecationSubscriber' => '/phpunit/TextUI/Output/Default/ProgressPrinter/Subscriber/TestTriggeredPhpDeprecationSubscriber.php', + 'PHPUnit\\TextUI\\Output\\Default\\ProgressPrinter\\TestTriggeredPhpNoticeSubscriber' => '/phpunit/TextUI/Output/Default/ProgressPrinter/Subscriber/TestTriggeredPhpNoticeSubscriber.php', + 'PHPUnit\\TextUI\\Output\\Default\\ProgressPrinter\\TestTriggeredPhpWarningSubscriber' => '/phpunit/TextUI/Output/Default/ProgressPrinter/Subscriber/TestTriggeredPhpWarningSubscriber.php', + 'PHPUnit\\TextUI\\Output\\Default\\ProgressPrinter\\TestTriggeredPhpunitDeprecationSubscriber' => '/phpunit/TextUI/Output/Default/ProgressPrinter/Subscriber/TestTriggeredPhpunitDeprecationSubscriber.php', + 'PHPUnit\\TextUI\\Output\\Default\\ProgressPrinter\\TestTriggeredPhpunitWarningSubscriber' => '/phpunit/TextUI/Output/Default/ProgressPrinter/Subscriber/TestTriggeredPhpunitWarningSubscriber.php', + 'PHPUnit\\TextUI\\Output\\Default\\ProgressPrinter\\TestTriggeredWarningSubscriber' => '/phpunit/TextUI/Output/Default/ProgressPrinter/Subscriber/TestTriggeredWarningSubscriber.php', + 'PHPUnit\\TextUI\\Output\\Default\\ResultPrinter' => '/phpunit/TextUI/Output/Default/ResultPrinter.php', + 'PHPUnit\\TextUI\\Output\\Default\\UnexpectedOutputPrinter' => '/phpunit/TextUI/Output/Default/UnexpectedOutputPrinter.php', + 'PHPUnit\\TextUI\\Output\\Facade' => '/phpunit/TextUI/Output/Facade.php', + 'PHPUnit\\TextUI\\Output\\NullPrinter' => '/phpunit/TextUI/Output/Printer/NullPrinter.php', + 'PHPUnit\\TextUI\\Output\\Printer' => '/phpunit/TextUI/Output/Printer/Printer.php', + 'PHPUnit\\TextUI\\Output\\SummaryPrinter' => '/phpunit/TextUI/Output/SummaryPrinter.php', + 'PHPUnit\\TextUI\\Output\\TestDox\\ResultPrinter' => '/phpunit/TextUI/Output/TestDox/ResultPrinter.php', 'PHPUnit\\TextUI\\ReflectionException' => '/phpunit/TextUI/Exception/ReflectionException.php', - 'PHPUnit\\TextUI\\ResultPrinter' => '/phpunit/TextUI/ResultPrinter.php', 'PHPUnit\\TextUI\\RuntimeException' => '/phpunit/TextUI/Exception/RuntimeException.php', + 'PHPUnit\\TextUI\\ShellExitCodeCalculator' => '/phpunit/TextUI/ShellExitCodeCalculator.php', 'PHPUnit\\TextUI\\TestDirectoryNotFoundException' => '/phpunit/TextUI/Exception/TestDirectoryNotFoundException.php', 'PHPUnit\\TextUI\\TestFileNotFoundException' => '/phpunit/TextUI/Exception/TestFileNotFoundException.php', 'PHPUnit\\TextUI\\TestRunner' => '/phpunit/TextUI/TestRunner.php', - 'PHPUnit\\TextUI\\TestSuiteMapper' => '/phpunit/TextUI/TestSuiteMapper.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\CodeCoverage\\CodeCoverage' => '/phpunit/TextUI/XmlConfiguration/CodeCoverage/CodeCoverage.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\CodeCoverage\\FilterMapper' => '/phpunit/TextUI/XmlConfiguration/CodeCoverage/FilterMapper.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\CodeCoverage\\Filter\\Directory' => '/phpunit/TextUI/XmlConfiguration/CodeCoverage/Filter/Directory.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\CodeCoverage\\Filter\\DirectoryCollection' => '/phpunit/TextUI/XmlConfiguration/CodeCoverage/Filter/DirectoryCollection.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\CodeCoverage\\Filter\\DirectoryCollectionIterator' => '/phpunit/TextUI/XmlConfiguration/CodeCoverage/Filter/DirectoryCollectionIterator.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\CodeCoverage\\Report\\Clover' => '/phpunit/TextUI/XmlConfiguration/CodeCoverage/Report/Clover.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\CodeCoverage\\Report\\Cobertura' => '/phpunit/TextUI/XmlConfiguration/CodeCoverage/Report/Cobertura.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\CodeCoverage\\Report\\Crap4j' => '/phpunit/TextUI/XmlConfiguration/CodeCoverage/Report/Crap4j.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\CodeCoverage\\Report\\Html' => '/phpunit/TextUI/XmlConfiguration/CodeCoverage/Report/Html.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\CodeCoverage\\Report\\Php' => '/phpunit/TextUI/XmlConfiguration/CodeCoverage/Report/Php.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\CodeCoverage\\Report\\Text' => '/phpunit/TextUI/XmlConfiguration/CodeCoverage/Report/Text.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\CodeCoverage\\Report\\Xml' => '/phpunit/TextUI/XmlConfiguration/CodeCoverage/Report/Xml.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\Configuration' => '/phpunit/TextUI/XmlConfiguration/Configuration.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\Constant' => '/phpunit/TextUI/XmlConfiguration/PHP/Constant.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\ConstantCollection' => '/phpunit/TextUI/XmlConfiguration/PHP/ConstantCollection.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\ConstantCollectionIterator' => '/phpunit/TextUI/XmlConfiguration/PHP/ConstantCollectionIterator.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\ConvertLogTypes' => '/phpunit/TextUI/XmlConfiguration/Migration/Migrations/ConvertLogTypes.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\CoverageCloverToReport' => '/phpunit/TextUI/XmlConfiguration/Migration/Migrations/CoverageCloverToReport.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\CoverageCrap4jToReport' => '/phpunit/TextUI/XmlConfiguration/Migration/Migrations/CoverageCrap4jToReport.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\CoverageHtmlToReport' => '/phpunit/TextUI/XmlConfiguration/Migration/Migrations/CoverageHtmlToReport.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\CoveragePhpToReport' => '/phpunit/TextUI/XmlConfiguration/Migration/Migrations/CoveragePhpToReport.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\CoverageTextToReport' => '/phpunit/TextUI/XmlConfiguration/Migration/Migrations/CoverageTextToReport.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\CoverageXmlToReport' => '/phpunit/TextUI/XmlConfiguration/Migration/Migrations/CoverageXmlToReport.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\Directory' => '/phpunit/TextUI/XmlConfiguration/Filesystem/Directory.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\DirectoryCollection' => '/phpunit/TextUI/XmlConfiguration/Filesystem/DirectoryCollection.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\DirectoryCollectionIterator' => '/phpunit/TextUI/XmlConfiguration/Filesystem/DirectoryCollectionIterator.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\Exception' => '/phpunit/TextUI/XmlConfiguration/Exception.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\Extension' => '/phpunit/TextUI/XmlConfiguration/PHPUnit/Extension.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\ExtensionCollection' => '/phpunit/TextUI/XmlConfiguration/PHPUnit/ExtensionCollection.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\ExtensionCollectionIterator' => '/phpunit/TextUI/XmlConfiguration/PHPUnit/ExtensionCollectionIterator.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\File' => '/phpunit/TextUI/XmlConfiguration/Filesystem/File.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\FileCollection' => '/phpunit/TextUI/XmlConfiguration/Filesystem/FileCollection.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\FileCollectionIterator' => '/phpunit/TextUI/XmlConfiguration/Filesystem/FileCollectionIterator.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\Generator' => '/phpunit/TextUI/XmlConfiguration/Generator.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\Group' => '/phpunit/TextUI/XmlConfiguration/Group/Group.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\GroupCollection' => '/phpunit/TextUI/XmlConfiguration/Group/GroupCollection.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\GroupCollectionIterator' => '/phpunit/TextUI/XmlConfiguration/Group/GroupCollectionIterator.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\Groups' => '/phpunit/TextUI/XmlConfiguration/Group/Groups.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\IniSetting' => '/phpunit/TextUI/XmlConfiguration/PHP/IniSetting.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\IniSettingCollection' => '/phpunit/TextUI/XmlConfiguration/PHP/IniSettingCollection.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\IniSettingCollectionIterator' => '/phpunit/TextUI/XmlConfiguration/PHP/IniSettingCollectionIterator.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\IntroduceCoverageElement' => '/phpunit/TextUI/XmlConfiguration/Migration/Migrations/IntroduceCoverageElement.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\Loader' => '/phpunit/TextUI/XmlConfiguration/Loader.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\LogToReportMigration' => '/phpunit/TextUI/XmlConfiguration/Migration/Migrations/LogToReportMigration.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\Logging\\Junit' => '/phpunit/TextUI/XmlConfiguration/Logging/Junit.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\Logging\\Logging' => '/phpunit/TextUI/XmlConfiguration/Logging/Logging.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\Logging\\TeamCity' => '/phpunit/TextUI/XmlConfiguration/Logging/TeamCity.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\Logging\\TestDox\\Html' => '/phpunit/TextUI/XmlConfiguration/Logging/TestDox/Html.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\Logging\\TestDox\\Text' => '/phpunit/TextUI/XmlConfiguration/Logging/TestDox/Text.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\Logging\\TestDox\\Xml' => '/phpunit/TextUI/XmlConfiguration/Logging/TestDox/Xml.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\Logging\\Text' => '/phpunit/TextUI/XmlConfiguration/Logging/Text.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\Migration' => '/phpunit/TextUI/XmlConfiguration/Migration/Migrations/Migration.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\MigrationBuilder' => '/phpunit/TextUI/XmlConfiguration/Migration/MigrationBuilder.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\MigrationBuilderException' => '/phpunit/TextUI/XmlConfiguration/Migration/MigrationBuilderException.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\MigrationException' => '/phpunit/TextUI/XmlConfiguration/Migration/MigrationException.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\Migrator' => '/phpunit/TextUI/XmlConfiguration/Migration/Migrator.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\MoveAttributesFromFilterWhitelistToCoverage' => '/phpunit/TextUI/XmlConfiguration/Migration/Migrations/MoveAttributesFromFilterWhitelistToCoverage.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\MoveAttributesFromRootToCoverage' => '/phpunit/TextUI/XmlConfiguration/Migration/Migrations/MoveAttributesFromRootToCoverage.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\MoveWhitelistExcludesToCoverage' => '/phpunit/TextUI/XmlConfiguration/Migration/Migrations/MoveWhitelistExcludesToCoverage.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\MoveWhitelistIncludesToCoverage' => '/phpunit/TextUI/XmlConfiguration/Migration/Migrations/MoveWhitelistIncludesToCoverage.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\PHPUnit' => '/phpunit/TextUI/XmlConfiguration/PHPUnit/PHPUnit.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\Php' => '/phpunit/TextUI/XmlConfiguration/PHP/Php.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\PhpHandler' => '/phpunit/TextUI/XmlConfiguration/PHP/PhpHandler.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\RemoveCacheTokensAttribute' => '/phpunit/TextUI/XmlConfiguration/Migration/Migrations/RemoveCacheTokensAttribute.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\RemoveEmptyFilter' => '/phpunit/TextUI/XmlConfiguration/Migration/Migrations/RemoveEmptyFilter.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\RemoveLogTypes' => '/phpunit/TextUI/XmlConfiguration/Migration/Migrations/RemoveLogTypes.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\TestDirectory' => '/phpunit/TextUI/XmlConfiguration/TestSuite/TestDirectory.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\TestDirectoryCollection' => '/phpunit/TextUI/XmlConfiguration/TestSuite/TestDirectoryCollection.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\TestDirectoryCollectionIterator' => '/phpunit/TextUI/XmlConfiguration/TestSuite/TestDirectoryCollectionIterator.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\TestFile' => '/phpunit/TextUI/XmlConfiguration/TestSuite/TestFile.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\TestFileCollection' => '/phpunit/TextUI/XmlConfiguration/TestSuite/TestFileCollection.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\TestFileCollectionIterator' => '/phpunit/TextUI/XmlConfiguration/TestSuite/TestFileCollectionIterator.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\TestSuite' => '/phpunit/TextUI/XmlConfiguration/TestSuite/TestSuite.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\TestSuiteCollection' => '/phpunit/TextUI/XmlConfiguration/TestSuite/TestSuiteCollection.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\TestSuiteCollectionIterator' => '/phpunit/TextUI/XmlConfiguration/TestSuite/TestSuiteCollectionIterator.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\UpdateSchemaLocationTo93' => '/phpunit/TextUI/XmlConfiguration/Migration/Migrations/UpdateSchemaLocationTo93.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\Variable' => '/phpunit/TextUI/XmlConfiguration/PHP/Variable.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\VariableCollection' => '/phpunit/TextUI/XmlConfiguration/PHP/VariableCollection.php', - 'PHPUnit\\TextUI\\XmlConfiguration\\VariableCollectionIterator' => '/phpunit/TextUI/XmlConfiguration/PHP/VariableCollectionIterator.php', - 'PHPUnit\\Util\\Annotation\\DocBlock' => '/phpunit/Util/Annotation/DocBlock.php', - 'PHPUnit\\Util\\Annotation\\Registry' => '/phpunit/Util/Annotation/Registry.php', - 'PHPUnit\\Util\\Blacklist' => '/phpunit/Util/Blacklist.php', + 'PHPUnit\\TextUI\\TestSuiteFilterProcessor' => '/phpunit/TextUI/TestSuiteFilterProcessor.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\CannotFindSchemaException' => '/phpunit/TextUI/Configuration/Exception/CannotFindSchemaException.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\CodeCoverage\\CodeCoverage' => '/phpunit/TextUI/Configuration/Xml/CodeCoverage/CodeCoverage.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\CodeCoverage\\Report\\Clover' => '/phpunit/TextUI/Configuration/Xml/CodeCoverage/Report/Clover.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\CodeCoverage\\Report\\Cobertura' => '/phpunit/TextUI/Configuration/Xml/CodeCoverage/Report/Cobertura.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\CodeCoverage\\Report\\Crap4j' => '/phpunit/TextUI/Configuration/Xml/CodeCoverage/Report/Crap4j.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\CodeCoverage\\Report\\Html' => '/phpunit/TextUI/Configuration/Xml/CodeCoverage/Report/Html.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\CodeCoverage\\Report\\Php' => '/phpunit/TextUI/Configuration/Xml/CodeCoverage/Report/Php.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\CodeCoverage\\Report\\Text' => '/phpunit/TextUI/Configuration/Xml/CodeCoverage/Report/Text.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\CodeCoverage\\Report\\Xml' => '/phpunit/TextUI/Configuration/Xml/CodeCoverage/Report/Xml.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\Configuration' => '/phpunit/TextUI/Configuration/Xml/Configuration.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\ConvertLogTypes' => '/phpunit/TextUI/Configuration/Xml/Migration/Migrations/ConvertLogTypes.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\CoverageCloverToReport' => '/phpunit/TextUI/Configuration/Xml/Migration/Migrations/CoverageCloverToReport.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\CoverageCrap4jToReport' => '/phpunit/TextUI/Configuration/Xml/Migration/Migrations/CoverageCrap4jToReport.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\CoverageHtmlToReport' => '/phpunit/TextUI/Configuration/Xml/Migration/Migrations/CoverageHtmlToReport.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\CoveragePhpToReport' => '/phpunit/TextUI/Configuration/Xml/Migration/Migrations/CoveragePhpToReport.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\CoverageTextToReport' => '/phpunit/TextUI/Configuration/Xml/Migration/Migrations/CoverageTextToReport.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\CoverageXmlToReport' => '/phpunit/TextUI/Configuration/Xml/Migration/Migrations/CoverageXmlToReport.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\DefaultConfiguration' => '/phpunit/TextUI/Configuration/Xml/DefaultConfiguration.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\Exception' => '/phpunit/TextUI/Configuration/Xml/Exception.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\FailedSchemaDetectionResult' => '/phpunit/TextUI/Configuration/Xml/SchemaDetector/FailedSchemaDetectionResult.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\Generator' => '/phpunit/TextUI/Configuration/Xml/Generator.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\Groups' => '/phpunit/TextUI/Configuration/Xml/Groups.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\IntroduceCacheDirectoryAttribute' => '/phpunit/TextUI/Configuration/Xml/Migration/Migrations/IntroduceCacheDirectoryAttribute.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\IntroduceCoverageElement' => '/phpunit/TextUI/Configuration/Xml/Migration/Migrations/IntroduceCoverageElement.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\LoadedFromFileConfiguration' => '/phpunit/TextUI/Configuration/Xml/LoadedFromFileConfiguration.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\Loader' => '/phpunit/TextUI/Configuration/Xml/Loader.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\LogToReportMigration' => '/phpunit/TextUI/Configuration/Xml/Migration/Migrations/LogToReportMigration.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\Logging\\Junit' => '/phpunit/TextUI/Configuration/Xml/Logging/Junit.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\Logging\\Logging' => '/phpunit/TextUI/Configuration/Xml/Logging/Logging.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\Logging\\TeamCity' => '/phpunit/TextUI/Configuration/Xml/Logging/TeamCity.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\Logging\\TestDox\\Html' => '/phpunit/TextUI/Configuration/Xml/Logging/TestDox/Html.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\Logging\\TestDox\\Text' => '/phpunit/TextUI/Configuration/Xml/Logging/TestDox/Text.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\Migration' => '/phpunit/TextUI/Configuration/Xml/Migration/Migrations/Migration.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\MigrationBuilder' => '/phpunit/TextUI/Configuration/Xml/Migration/MigrationBuilder.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\MigrationBuilderException' => '/phpunit/TextUI/Configuration/Xml/Migration/MigrationBuilderException.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\MigrationException' => '/phpunit/TextUI/Configuration/Xml/Migration/MigrationException.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\Migrator' => '/phpunit/TextUI/Configuration/Xml/Migration/Migrator.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\MoveAttributesFromFilterWhitelistToCoverage' => '/phpunit/TextUI/Configuration/Xml/Migration/Migrations/MoveAttributesFromFilterWhitelistToCoverage.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\MoveAttributesFromRootToCoverage' => '/phpunit/TextUI/Configuration/Xml/Migration/Migrations/MoveAttributesFromRootToCoverage.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\MoveCoverageDirectoriesToSource' => '/phpunit/TextUI/Configuration/Xml/Migration/Migrations/MoveCoverageDirectoriesToSource.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\MoveWhitelistExcludesToCoverage' => '/phpunit/TextUI/Configuration/Xml/Migration/Migrations/MoveWhitelistExcludesToCoverage.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\MoveWhitelistIncludesToCoverage' => '/phpunit/TextUI/Configuration/Xml/Migration/Migrations/MoveWhitelistIncludesToCoverage.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\PHPUnit' => '/phpunit/TextUI/Configuration/Xml/PHPUnit.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\RemoveBeStrictAboutResourceUsageDuringSmallTestsAttribute' => '/phpunit/TextUI/Configuration/Xml/Migration/Migrations/RemoveBeStrictAboutResourceUsageDuringSmallTestsAttribute.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\RemoveBeStrictAboutTodoAnnotatedTestsAttribute' => '/phpunit/TextUI/Configuration/Xml/Migration/Migrations/RemoveBeStrictAboutTodoAnnotatedTestsAttribute.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\RemoveCacheResultFileAttribute' => '/phpunit/TextUI/Configuration/Xml/Migration/Migrations/RemoveCacheResultFileAttribute.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\RemoveCacheTokensAttribute' => '/phpunit/TextUI/Configuration/Xml/Migration/Migrations/RemoveCacheTokensAttribute.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\RemoveConversionToExceptionsAttributes' => '/phpunit/TextUI/Configuration/Xml/Migration/Migrations/RemoveConversionToExceptionsAttributes.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\RemoveCoverageElementCacheDirectoryAttribute' => '/phpunit/TextUI/Configuration/Xml/Migration/Migrations/RemoveCoverageElementCacheDirectoryAttribute.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\RemoveCoverageElementProcessUncoveredFilesAttribute' => '/phpunit/TextUI/Configuration/Xml/Migration/Migrations/RemoveCoverageElementProcessUncoveredFilesAttribute.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\RemoveEmptyFilter' => '/phpunit/TextUI/Configuration/Xml/Migration/Migrations/RemoveEmptyFilter.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\RemoveListeners' => '/phpunit/TextUI/Configuration/Xml/Migration/Migrations/RemoveListeners.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\RemoveLogTypes' => '/phpunit/TextUI/Configuration/Xml/Migration/Migrations/RemoveLogTypes.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\RemoveLoggingElements' => '/phpunit/TextUI/Configuration/Xml/Migration/Migrations/RemoveLoggingElements.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\RemoveNoInteractionAttribute' => '/phpunit/TextUI/Configuration/Xml/Migration/Migrations/RemoveNoInteractionAttribute.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\RemovePrinterAttributes' => '/phpunit/TextUI/Configuration/Xml/Migration/Migrations/RemovePrinterAttributes.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\RemoveTestDoxGroupsElement' => '/phpunit/TextUI/Configuration/Xml/Migration/Migrations/RemoveTestDoxGroupsElement.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\RemoveTestSuiteLoaderAttributes' => '/phpunit/TextUI/Configuration/Xml/Migration/Migrations/RemoveTestSuiteLoaderAttributes.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\RemoveVerboseAttribute' => '/phpunit/TextUI/Configuration/Xml/Migration/Migrations/RemoveVerboseAttribute.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\RenameBackupStaticAttributesAttribute' => '/phpunit/TextUI/Configuration/Xml/Migration/Migrations/RenameBackupStaticAttributesAttribute.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\RenameBeStrictAboutCoversAnnotationAttribute' => '/phpunit/TextUI/Configuration/Xml/Migration/Migrations/RenameBeStrictAboutCoversAnnotationAttribute.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\RenameForceCoversAnnotationAttribute' => '/phpunit/TextUI/Configuration/Xml/Migration/Migrations/RenameForceCoversAnnotationAttribute.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\SchemaDetectionResult' => '/phpunit/TextUI/Configuration/Xml/SchemaDetector/SchemaDetectionResult.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\SchemaDetector' => '/phpunit/TextUI/Configuration/Xml/SchemaDetector/SchemaDetector.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\SchemaFinder' => '/phpunit/TextUI/Configuration/Xml/SchemaFinder.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\SnapshotNodeList' => '/phpunit/TextUI/Configuration/Xml/Migration/SnapshotNodeList.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\SuccessfulSchemaDetectionResult' => '/phpunit/TextUI/Configuration/Xml/SchemaDetector/SuccessfulSchemaDetectionResult.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\TestSuiteMapper' => '/phpunit/TextUI/Configuration/Xml/TestSuiteMapper.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\UpdateSchemaLocation' => '/phpunit/TextUI/Configuration/Xml/Migration/Migrations/UpdateSchemaLocation.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\ValidationResult' => '/phpunit/TextUI/Configuration/Xml/Validator/ValidationResult.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\Validator' => '/phpunit/TextUI/Configuration/Xml/Validator/Validator.php', 'PHPUnit\\Util\\Cloner' => '/phpunit/Util/Cloner.php', 'PHPUnit\\Util\\Color' => '/phpunit/Util/Color.php', - 'PHPUnit\\Util\\ErrorHandler' => '/phpunit/Util/ErrorHandler.php', - 'PHPUnit\\Util\\Exception' => '/phpunit/Util/Exception.php', + 'PHPUnit\\Util\\Exception' => '/phpunit/Util/Exception/Exception.php', 'PHPUnit\\Util\\ExcludeList' => '/phpunit/Util/ExcludeList.php', - 'PHPUnit\\Util\\FileLoader' => '/phpunit/Util/FileLoader.php', + 'PHPUnit\\Util\\Exporter' => '/phpunit/Util/Exporter.php', 'PHPUnit\\Util\\Filesystem' => '/phpunit/Util/Filesystem.php', 'PHPUnit\\Util\\Filter' => '/phpunit/Util/Filter.php', 'PHPUnit\\Util\\GlobalState' => '/phpunit/Util/GlobalState.php', - 'PHPUnit\\Util\\InvalidDataSetException' => '/phpunit/Util/InvalidDataSetException.php', + 'PHPUnit\\Util\\InvalidDirectoryException' => '/phpunit/Util/Exception/InvalidDirectoryException.php', + 'PHPUnit\\Util\\InvalidJsonException' => '/phpunit/Util/Exception/InvalidJsonException.php', + 'PHPUnit\\Util\\InvalidVersionOperatorException' => '/phpunit/Util/Exception/InvalidVersionOperatorException.php', 'PHPUnit\\Util\\Json' => '/phpunit/Util/Json.php', - 'PHPUnit\\Util\\Log\\JUnit' => '/phpunit/Util/Log/JUnit.php', - 'PHPUnit\\Util\\Log\\TeamCity' => '/phpunit/Util/Log/TeamCity.php', 'PHPUnit\\Util\\PHP\\AbstractPhpProcess' => '/phpunit/Util/PHP/AbstractPhpProcess.php', 'PHPUnit\\Util\\PHP\\DefaultPhpProcess' => '/phpunit/Util/PHP/DefaultPhpProcess.php', - 'PHPUnit\\Util\\PHP\\WindowsPhpProcess' => '/phpunit/Util/PHP/WindowsPhpProcess.php', - 'PHPUnit\\Util\\Printer' => '/phpunit/Util/Printer.php', + 'PHPUnit\\Util\\PHP\\PhpProcessException' => '/phpunit/Util/Exception/PhpProcessException.php', 'PHPUnit\\Util\\Reflection' => '/phpunit/Util/Reflection.php', - 'PHPUnit\\Util\\RegularExpression' => '/phpunit/Util/RegularExpression.php', 'PHPUnit\\Util\\Test' => '/phpunit/Util/Test.php', - 'PHPUnit\\Util\\TestDox\\CliTestDoxPrinter' => '/phpunit/Util/TestDox/CliTestDoxPrinter.php', - 'PHPUnit\\Util\\TestDox\\HtmlResultPrinter' => '/phpunit/Util/TestDox/HtmlResultPrinter.php', - 'PHPUnit\\Util\\TestDox\\NamePrettifier' => '/phpunit/Util/TestDox/NamePrettifier.php', - 'PHPUnit\\Util\\TestDox\\ResultPrinter' => '/phpunit/Util/TestDox/ResultPrinter.php', - 'PHPUnit\\Util\\TestDox\\TestDoxPrinter' => '/phpunit/Util/TestDox/TestDoxPrinter.php', - 'PHPUnit\\Util\\TestDox\\TextResultPrinter' => '/phpunit/Util/TestDox/TextResultPrinter.php', - 'PHPUnit\\Util\\TestDox\\XmlResultPrinter' => '/phpunit/Util/TestDox/XmlResultPrinter.php', - 'PHPUnit\\Util\\TextTestListRenderer' => '/phpunit/Util/TextTestListRenderer.php', - 'PHPUnit\\Util\\Type' => '/phpunit/Util/Type.php', + 'PHPUnit\\Util\\ThrowableToStringMapper' => '/phpunit/Util/ThrowableToStringMapper.php', 'PHPUnit\\Util\\VersionComparisonOperator' => '/phpunit/Util/VersionComparisonOperator.php', - 'PHPUnit\\Util\\XdebugFilterScriptGenerator' => '/phpunit/Util/XdebugFilterScriptGenerator.php', - 'PHPUnit\\Util\\Xml' => '/phpunit/Util/Xml.php', - 'PHPUnit\\Util\\XmlTestListRenderer' => '/phpunit/Util/XmlTestListRenderer.php', - 'PHPUnit\\Util\\Xml\\Exception' => '/phpunit/Util/Xml/Exception.php', - 'PHPUnit\\Util\\Xml\\FailedSchemaDetectionResult' => '/phpunit/Util/Xml/FailedSchemaDetectionResult.php', + 'PHPUnit\\Util\\Xml' => '/phpunit/Util/Xml/Xml.php', 'PHPUnit\\Util\\Xml\\Loader' => '/phpunit/Util/Xml/Loader.php', - 'PHPUnit\\Util\\Xml\\SchemaDetectionResult' => '/phpunit/Util/Xml/SchemaDetectionResult.php', - 'PHPUnit\\Util\\Xml\\SchemaDetector' => '/phpunit/Util/Xml/SchemaDetector.php', - 'PHPUnit\\Util\\Xml\\SchemaFinder' => '/phpunit/Util/Xml/SchemaFinder.php', - 'PHPUnit\\Util\\Xml\\SnapshotNodeList' => '/phpunit/Util/Xml/SnapshotNodeList.php', - 'PHPUnit\\Util\\Xml\\SuccessfulSchemaDetectionResult' => '/phpunit/Util/Xml/SuccessfulSchemaDetectionResult.php', - 'PHPUnit\\Util\\Xml\\ValidationResult' => '/phpunit/Util/Xml/ValidationResult.php', - 'PHPUnit\\Util\\Xml\\Validator' => '/phpunit/Util/Xml/Validator.php', - 'Prophecy\\Argument' => '/phpspec-prophecy/Prophecy/Argument.php', - 'Prophecy\\Argument\\ArgumentsWildcard' => '/phpspec-prophecy/Prophecy/Argument/ArgumentsWildcard.php', - 'Prophecy\\Argument\\Token\\AnyValueToken' => '/phpspec-prophecy/Prophecy/Argument/Token/AnyValueToken.php', - 'Prophecy\\Argument\\Token\\AnyValuesToken' => '/phpspec-prophecy/Prophecy/Argument/Token/AnyValuesToken.php', - 'Prophecy\\Argument\\Token\\ApproximateValueToken' => '/phpspec-prophecy/Prophecy/Argument/Token/ApproximateValueToken.php', - 'Prophecy\\Argument\\Token\\ArrayCountToken' => '/phpspec-prophecy/Prophecy/Argument/Token/ArrayCountToken.php', - 'Prophecy\\Argument\\Token\\ArrayEntryToken' => '/phpspec-prophecy/Prophecy/Argument/Token/ArrayEntryToken.php', - 'Prophecy\\Argument\\Token\\ArrayEveryEntryToken' => '/phpspec-prophecy/Prophecy/Argument/Token/ArrayEveryEntryToken.php', - 'Prophecy\\Argument\\Token\\CallbackToken' => '/phpspec-prophecy/Prophecy/Argument/Token/CallbackToken.php', - 'Prophecy\\Argument\\Token\\ExactValueToken' => '/phpspec-prophecy/Prophecy/Argument/Token/ExactValueToken.php', - 'Prophecy\\Argument\\Token\\IdenticalValueToken' => '/phpspec-prophecy/Prophecy/Argument/Token/IdenticalValueToken.php', - 'Prophecy\\Argument\\Token\\InArrayToken' => '/phpspec-prophecy/Prophecy/Argument/Token/InArrayToken.php', - 'Prophecy\\Argument\\Token\\LogicalAndToken' => '/phpspec-prophecy/Prophecy/Argument/Token/LogicalAndToken.php', - 'Prophecy\\Argument\\Token\\LogicalNotToken' => '/phpspec-prophecy/Prophecy/Argument/Token/LogicalNotToken.php', - 'Prophecy\\Argument\\Token\\NotInArrayToken' => '/phpspec-prophecy/Prophecy/Argument/Token/NotInArrayToken.php', - 'Prophecy\\Argument\\Token\\ObjectStateToken' => '/phpspec-prophecy/Prophecy/Argument/Token/ObjectStateToken.php', - 'Prophecy\\Argument\\Token\\StringContainsToken' => '/phpspec-prophecy/Prophecy/Argument/Token/StringContainsToken.php', - 'Prophecy\\Argument\\Token\\TokenInterface' => '/phpspec-prophecy/Prophecy/Argument/Token/TokenInterface.php', - 'Prophecy\\Argument\\Token\\TypeToken' => '/phpspec-prophecy/Prophecy/Argument/Token/TypeToken.php', - 'Prophecy\\Call\\Call' => '/phpspec-prophecy/Prophecy/Call/Call.php', - 'Prophecy\\Call\\CallCenter' => '/phpspec-prophecy/Prophecy/Call/CallCenter.php', - 'Prophecy\\Comparator\\ClosureComparator' => '/phpspec-prophecy/Prophecy/Comparator/ClosureComparator.php', - 'Prophecy\\Comparator\\Factory' => '/phpspec-prophecy/Prophecy/Comparator/Factory.php', - 'Prophecy\\Comparator\\FactoryProvider' => '/phpspec-prophecy/Prophecy/Comparator/FactoryProvider.php', - 'Prophecy\\Comparator\\ProphecyComparator' => '/phpspec-prophecy/Prophecy/Comparator/ProphecyComparator.php', - 'Prophecy\\Doubler\\CachedDoubler' => '/phpspec-prophecy/Prophecy/Doubler/CachedDoubler.php', - 'Prophecy\\Doubler\\ClassPatch\\ClassPatchInterface' => '/phpspec-prophecy/Prophecy/Doubler/ClassPatch/ClassPatchInterface.php', - 'Prophecy\\Doubler\\ClassPatch\\DisableConstructorPatch' => '/phpspec-prophecy/Prophecy/Doubler/ClassPatch/DisableConstructorPatch.php', - 'Prophecy\\Doubler\\ClassPatch\\KeywordPatch' => '/phpspec-prophecy/Prophecy/Doubler/ClassPatch/KeywordPatch.php', - 'Prophecy\\Doubler\\ClassPatch\\MagicCallPatch' => '/phpspec-prophecy/Prophecy/Doubler/ClassPatch/MagicCallPatch.php', - 'Prophecy\\Doubler\\ClassPatch\\ProphecySubjectPatch' => '/phpspec-prophecy/Prophecy/Doubler/ClassPatch/ProphecySubjectPatch.php', - 'Prophecy\\Doubler\\ClassPatch\\ReflectionClassNewInstancePatch' => '/phpspec-prophecy/Prophecy/Doubler/ClassPatch/ReflectionClassNewInstancePatch.php', - 'Prophecy\\Doubler\\ClassPatch\\SplFileInfoPatch' => '/phpspec-prophecy/Prophecy/Doubler/ClassPatch/SplFileInfoPatch.php', - 'Prophecy\\Doubler\\ClassPatch\\ThrowablePatch' => '/phpspec-prophecy/Prophecy/Doubler/ClassPatch/ThrowablePatch.php', - 'Prophecy\\Doubler\\ClassPatch\\TraversablePatch' => '/phpspec-prophecy/Prophecy/Doubler/ClassPatch/TraversablePatch.php', - 'Prophecy\\Doubler\\DoubleInterface' => '/phpspec-prophecy/Prophecy/Doubler/DoubleInterface.php', - 'Prophecy\\Doubler\\Doubler' => '/phpspec-prophecy/Prophecy/Doubler/Doubler.php', - 'Prophecy\\Doubler\\Generator\\ClassCodeGenerator' => '/phpspec-prophecy/Prophecy/Doubler/Generator/ClassCodeGenerator.php', - 'Prophecy\\Doubler\\Generator\\ClassCreator' => '/phpspec-prophecy/Prophecy/Doubler/Generator/ClassCreator.php', - 'Prophecy\\Doubler\\Generator\\ClassMirror' => '/phpspec-prophecy/Prophecy/Doubler/Generator/ClassMirror.php', - 'Prophecy\\Doubler\\Generator\\Node\\ArgumentNode' => '/phpspec-prophecy/Prophecy/Doubler/Generator/Node/ArgumentNode.php', - 'Prophecy\\Doubler\\Generator\\Node\\ArgumentTypeNode' => '/phpspec-prophecy/Prophecy/Doubler/Generator/Node/ArgumentTypeNode.php', - 'Prophecy\\Doubler\\Generator\\Node\\ClassNode' => '/phpspec-prophecy/Prophecy/Doubler/Generator/Node/ClassNode.php', - 'Prophecy\\Doubler\\Generator\\Node\\MethodNode' => '/phpspec-prophecy/Prophecy/Doubler/Generator/Node/MethodNode.php', - 'Prophecy\\Doubler\\Generator\\Node\\ReturnTypeNode' => '/phpspec-prophecy/Prophecy/Doubler/Generator/Node/ReturnTypeNode.php', - 'Prophecy\\Doubler\\Generator\\Node\\TypeNodeAbstract' => '/phpspec-prophecy/Prophecy/Doubler/Generator/Node/TypeNodeAbstract.php', - 'Prophecy\\Doubler\\Generator\\ReflectionInterface' => '/phpspec-prophecy/Prophecy/Doubler/Generator/ReflectionInterface.php', - 'Prophecy\\Doubler\\Generator\\TypeHintReference' => '/phpspec-prophecy/Prophecy/Doubler/Generator/TypeHintReference.php', - 'Prophecy\\Doubler\\LazyDouble' => '/phpspec-prophecy/Prophecy/Doubler/LazyDouble.php', - 'Prophecy\\Doubler\\NameGenerator' => '/phpspec-prophecy/Prophecy/Doubler/NameGenerator.php', - 'Prophecy\\Exception\\Call\\UnexpectedCallException' => '/phpspec-prophecy/Prophecy/Exception/Call/UnexpectedCallException.php', - 'Prophecy\\Exception\\Doubler\\ClassCreatorException' => '/phpspec-prophecy/Prophecy/Exception/Doubler/ClassCreatorException.php', - 'Prophecy\\Exception\\Doubler\\ClassMirrorException' => '/phpspec-prophecy/Prophecy/Exception/Doubler/ClassMirrorException.php', - 'Prophecy\\Exception\\Doubler\\ClassNotFoundException' => '/phpspec-prophecy/Prophecy/Exception/Doubler/ClassNotFoundException.php', - 'Prophecy\\Exception\\Doubler\\DoubleException' => '/phpspec-prophecy/Prophecy/Exception/Doubler/DoubleException.php', - 'Prophecy\\Exception\\Doubler\\DoublerException' => '/phpspec-prophecy/Prophecy/Exception/Doubler/DoublerException.php', - 'Prophecy\\Exception\\Doubler\\InterfaceNotFoundException' => '/phpspec-prophecy/Prophecy/Exception/Doubler/InterfaceNotFoundException.php', - 'Prophecy\\Exception\\Doubler\\MethodNotExtendableException' => '/phpspec-prophecy/Prophecy/Exception/Doubler/MethodNotExtendableException.php', - 'Prophecy\\Exception\\Doubler\\MethodNotFoundException' => '/phpspec-prophecy/Prophecy/Exception/Doubler/MethodNotFoundException.php', - 'Prophecy\\Exception\\Doubler\\ReturnByReferenceException' => '/phpspec-prophecy/Prophecy/Exception/Doubler/ReturnByReferenceException.php', - 'Prophecy\\Exception\\Exception' => '/phpspec-prophecy/Prophecy/Exception/Exception.php', - 'Prophecy\\Exception\\InvalidArgumentException' => '/phpspec-prophecy/Prophecy/Exception/InvalidArgumentException.php', - 'Prophecy\\Exception\\Prediction\\AggregateException' => '/phpspec-prophecy/Prophecy/Exception/Prediction/AggregateException.php', - 'Prophecy\\Exception\\Prediction\\FailedPredictionException' => '/phpspec-prophecy/Prophecy/Exception/Prediction/FailedPredictionException.php', - 'Prophecy\\Exception\\Prediction\\NoCallsException' => '/phpspec-prophecy/Prophecy/Exception/Prediction/NoCallsException.php', - 'Prophecy\\Exception\\Prediction\\PredictionException' => '/phpspec-prophecy/Prophecy/Exception/Prediction/PredictionException.php', - 'Prophecy\\Exception\\Prediction\\UnexpectedCallsCountException' => '/phpspec-prophecy/Prophecy/Exception/Prediction/UnexpectedCallsCountException.php', - 'Prophecy\\Exception\\Prediction\\UnexpectedCallsException' => '/phpspec-prophecy/Prophecy/Exception/Prediction/UnexpectedCallsException.php', - 'Prophecy\\Exception\\Prophecy\\MethodProphecyException' => '/phpspec-prophecy/Prophecy/Exception/Prophecy/MethodProphecyException.php', - 'Prophecy\\Exception\\Prophecy\\ObjectProphecyException' => '/phpspec-prophecy/Prophecy/Exception/Prophecy/ObjectProphecyException.php', - 'Prophecy\\Exception\\Prophecy\\ProphecyException' => '/phpspec-prophecy/Prophecy/Exception/Prophecy/ProphecyException.php', - 'Prophecy\\PhpDocumentor\\ClassAndInterfaceTagRetriever' => '/phpspec-prophecy/Prophecy/PhpDocumentor/ClassAndInterfaceTagRetriever.php', - 'Prophecy\\PhpDocumentor\\ClassTagRetriever' => '/phpspec-prophecy/Prophecy/PhpDocumentor/ClassTagRetriever.php', - 'Prophecy\\PhpDocumentor\\MethodTagRetrieverInterface' => '/phpspec-prophecy/Prophecy/PhpDocumentor/MethodTagRetrieverInterface.php', - 'Prophecy\\Prediction\\CallPrediction' => '/phpspec-prophecy/Prophecy/Prediction/CallPrediction.php', - 'Prophecy\\Prediction\\CallTimesPrediction' => '/phpspec-prophecy/Prophecy/Prediction/CallTimesPrediction.php', - 'Prophecy\\Prediction\\CallbackPrediction' => '/phpspec-prophecy/Prophecy/Prediction/CallbackPrediction.php', - 'Prophecy\\Prediction\\NoCallsPrediction' => '/phpspec-prophecy/Prophecy/Prediction/NoCallsPrediction.php', - 'Prophecy\\Prediction\\PredictionInterface' => '/phpspec-prophecy/Prophecy/Prediction/PredictionInterface.php', - 'Prophecy\\Promise\\CallbackPromise' => '/phpspec-prophecy/Prophecy/Promise/CallbackPromise.php', - 'Prophecy\\Promise\\PromiseInterface' => '/phpspec-prophecy/Prophecy/Promise/PromiseInterface.php', - 'Prophecy\\Promise\\ReturnArgumentPromise' => '/phpspec-prophecy/Prophecy/Promise/ReturnArgumentPromise.php', - 'Prophecy\\Promise\\ReturnPromise' => '/phpspec-prophecy/Prophecy/Promise/ReturnPromise.php', - 'Prophecy\\Promise\\ThrowPromise' => '/phpspec-prophecy/Prophecy/Promise/ThrowPromise.php', - 'Prophecy\\Prophecy\\MethodProphecy' => '/phpspec-prophecy/Prophecy/Prophecy/MethodProphecy.php', - 'Prophecy\\Prophecy\\ObjectProphecy' => '/phpspec-prophecy/Prophecy/Prophecy/ObjectProphecy.php', - 'Prophecy\\Prophecy\\ProphecyInterface' => '/phpspec-prophecy/Prophecy/Prophecy/ProphecyInterface.php', - 'Prophecy\\Prophecy\\ProphecySubjectInterface' => '/phpspec-prophecy/Prophecy/Prophecy/ProphecySubjectInterface.php', - 'Prophecy\\Prophecy\\Revealer' => '/phpspec-prophecy/Prophecy/Prophecy/Revealer.php', - 'Prophecy\\Prophecy\\RevealerInterface' => '/phpspec-prophecy/Prophecy/Prophecy/RevealerInterface.php', - 'Prophecy\\Prophet' => '/phpspec-prophecy/Prophecy/Prophet.php', - 'Prophecy\\Util\\ExportUtil' => '/phpspec-prophecy/Prophecy/Util/ExportUtil.php', - 'Prophecy\\Util\\StringUtil' => '/phpspec-prophecy/Prophecy/Util/StringUtil.php'] as $file) { - require_once 'phar://phpunit-9.6.19.phar' . $file; + 'PHPUnit\\Util\\Xml\\XmlException' => '/phpunit/Util/Exception/XmlException.php'] as $file) { + require_once 'phar://phpunit-10.5.26.phar' . $file; } require __PHPUNIT_PHAR_ROOT__ . '/phpunit/Framework/Assert/Functions.php'; @@ -2517,243 +2997,133 @@ if ($execute) { unset($execute); - PHPUnit\TextUI\Command::main(); + exit((new PHPUnit\TextUI\Application)->run($_SERVER['argv'])); } __HALT_COMPILER(); ?> -܋ phpunit-9.6.19.phar composer.lock#f#ĚՉ;doctrine-deprecations/Doctrine/Deprecations/Deprecation.phpV$fV$AJdoctrine-deprecations/Doctrine/Deprecations/PHPUnit/VerifyDeprecations.phpf/doctrine-deprecations/LICENSE)f)"0Ldoctrine-instantiator/Doctrine/Instantiator/Exception/ExceptionInterface.phpfW|\Rdoctrine-instantiator/Doctrine/Instantiator/Exception/InvalidArgumentException.phpf]weRdoctrine-instantiator/Doctrine/Instantiator/Exception/UnexpectedValueException.php>f> -*BX<doctrine-instantiator/Doctrine/Instantiator/Instantiator.phpfk9Edoctrine-instantiator/Doctrine/Instantiator/InstantiatorInterface.phpf1sXdoctrine-instantiator/LICENSE$f$ -͂ manifest.txt f jɤ'myclabs-deep-copy/DeepCopy/DeepCopy.phpf!]7myclabs-deep-copy/DeepCopy/Exception/CloneException.phpfJDȤ:myclabs-deep-copy/DeepCopy/Exception/PropertyException.phpfo#5myclabs-deep-copy/DeepCopy/Filter/ChainableFilter.phpf=(eGmyclabs-deep-copy/DeepCopy/Filter/Doctrine/DoctrineCollectionFilter.phpfcLmyclabs-deep-copy/DeepCopy/Filter/Doctrine/DoctrineEmptyCollectionFilter.phpftBmyclabs-deep-copy/DeepCopy/Filter/Doctrine/DoctrineProxyFilter.phpffQc_,myclabs-deep-copy/DeepCopy/Filter/Filter.phphfh߽0myclabs-deep-copy/DeepCopy/Filter/KeepFilter.phpf7#3myclabs-deep-copy/DeepCopy/Filter/ReplaceFilter.phpf/bM3myclabs-deep-copy/DeepCopy/Filter/SetNullFilter.phpfCkDmyclabs-deep-copy/DeepCopy/Matcher/Doctrine/DoctrineProxyMatcher.phpf 3.myclabs-deep-copy/DeepCopy/Matcher/Matcher.phpffä6myclabs-deep-copy/DeepCopy/Matcher/PropertyMatcher.phpfA^:myclabs-deep-copy/DeepCopy/Matcher/PropertyNameMatcher.phpfP:myclabs-deep-copy/DeepCopy/Matcher/PropertyTypeMatcher.php:f:A,A:myclabs-deep-copy/DeepCopy/Reflection/ReflectionHelper.php=f=߈:7Amyclabs-deep-copy/DeepCopy/TypeFilter/Date/DateIntervalFilter.phpf[7myclabs-deep-copy/DeepCopy/TypeFilter/ReplaceFilter.phpfWp;myclabs-deep-copy/DeepCopy/TypeFilter/ShallowCopyFilter.phpfF_e?myclabs-deep-copy/DeepCopy/TypeFilter/Spl/ArrayObjectFilter.phpfةAmyclabs-deep-copy/DeepCopy/TypeFilter/Spl/SplDoublyLinkedList.phpfK픤Gmyclabs-deep-copy/DeepCopy/TypeFilter/Spl/SplDoublyLinkedListFilter.php)f)q*4myclabs-deep-copy/DeepCopy/TypeFilter/TypeFilter.phpfԊ6myclabs-deep-copy/DeepCopy/TypeMatcher/TypeMatcher.phpf/(myclabs-deep-copy/DeepCopy/deep_copy.phpfmnmyclabs-deep-copy/LICENSE5f5ʭ˄nikic-php-parser/LICENSEf*&nikic-php-parser/PhpParser/Builder.phpf̝Ƥ1nikic-php-parser/PhpParser/Builder/ClassConst.phpfG E-nikic-php-parser/PhpParser/Builder/Class_.phpf82nikic-php-parser/PhpParser/Builder/Declaration.phpft5/nikic-php-parser/PhpParser/Builder/EnumCase.phpvfvE ,nikic-php-parser/PhpParser/Builder/Enum_.php f -,3nikic-php-parser/PhpParser/Builder/FunctionLike.phpfS0nikic-php-parser/PhpParser/Builder/Function_.phpZfZ0O1nikic-php-parser/PhpParser/Builder/Interface_.php f h"z-nikic-php-parser/PhpParser/Builder/Method.phpfP1nikic-php-parser/PhpParser/Builder/Namespace_.phpNfN,nikic-php-parser/PhpParser/Builder/Param.phpfqh/nikic-php-parser/PhpParser/Builder/Property.phpfr/nikic-php-parser/PhpParser/Builder/TraitUse.phpkfk?Z9nikic-php-parser/PhpParser/Builder/TraitUseAdaptation.phpf֤-nikic-php-parser/PhpParser/Builder/Trait_.phpfg+nikic-php-parser/PhpParser/Builder/Use_.phpfN-nikic-php-parser/PhpParser/BuilderFactory.php:+f:+~P7-nikic-php-parser/PhpParser/BuilderHelpers.php$f$0Ӥ&nikic-php-parser/PhpParser/Comment.phpfx:*nikic-php-parser/PhpParser/Comment/Doc.phpf袤;nikic-php-parser/PhpParser/ConstExprEvaluationException.phpcfc:ݻ1nikic-php-parser/PhpParser/ConstExprEvaluator.phpy%fy%2h$nikic-php-parser/PhpParser/Error.phpf`+nikic-php-parser/PhpParser/ErrorHandler.php3f3c6nikic-php-parser/PhpParser/ErrorHandler/Collecting.phpfj4nikic-php-parser/PhpParser/ErrorHandler/Throwing.phpfzp0nikic-php-parser/PhpParser/Internal/DiffElem.php;f;+'.nikic-php-parser/PhpParser/Internal/Differ.php1f1PAnikic-php-parser/PhpParser/Internal/PrintableNewAnonClassNode.phpfi3nikic-php-parser/PhpParser/Internal/TokenStream.php$f$Dw*nikic-php-parser/PhpParser/JsonDecoder.php$ f$ z$nikic-php-parser/PhpParser/Lexer.phpZfZ Τ.nikic-php-parser/PhpParser/Lexer/Emulative.php#f#KQ=Dnikic-php-parser/PhpParser/Lexer/TokenEmulator/AttributeEmulator.phpfmMLnikic-php-parser/PhpParser/Lexer/TokenEmulator/CoaleseEqualTokenEmulator.phpf:(Dnikic-php-parser/PhpParser/Lexer/TokenEmulator/EnumTokenEmulator.phpfRHnikic-php-parser/PhpParser/Lexer/TokenEmulator/ExplicitOctalEmulator.phpf7ǤLnikic-php-parser/PhpParser/Lexer/TokenEmulator/FlexibleDocStringEmulator.phpv fv a/Bnikic-php-parser/PhpParser/Lexer/TokenEmulator/FnTokenEmulator.phpf1@dBnikic-php-parser/PhpParser/Lexer/TokenEmulator/KeywordEmulator.phpfƤEnikic-php-parser/PhpParser/Lexer/TokenEmulator/MatchTokenEmulator.phpfFiHnikic-php-parser/PhpParser/Lexer/TokenEmulator/NullsafeTokenEmulator.phpfAdRnikic-php-parser/PhpParser/Lexer/TokenEmulator/NumericLiteralSeparatorEmulator.php^f^,Pnikic-php-parser/PhpParser/Lexer/TokenEmulator/ReadonlyFunctionTokenEmulator.phpfzbgHnikic-php-parser/PhpParser/Lexer/TokenEmulator/ReadonlyTokenEmulator.phpTfT_߇gBnikic-php-parser/PhpParser/Lexer/TokenEmulator/ReverseEmulator.phpf d#@nikic-php-parser/PhpParser/Lexer/TokenEmulator/TokenEmulator.phpyfy\"*nikic-php-parser/PhpParser/NameContext.php&f& E#nikic-php-parser/PhpParser/Node.phpf!b'nikic-php-parser/PhpParser/Node/Arg.php=f=?-nikic-php-parser/PhpParser/Node/Attribute.phpTfTΤ2nikic-php-parser/PhpParser/Node/AttributeGroup.phpfԡIM/nikic-php-parser/PhpParser/Node/ComplexType.php[f[0Us*nikic-php-parser/PhpParser/Node/Const_.phpfT}(nikic-php-parser/PhpParser/Node/Expr.phpf|)6nikic-php-parser/PhpParser/Node/Expr/ArrayDimFetch.phpVfV=|W2nikic-php-parser/PhpParser/Node/Expr/ArrayItem.phpf()/nikic-php-parser/PhpParser/Node/Expr/Array_.php@f@- 6nikic-php-parser/PhpParser/Node/Expr/ArrowFunction.php f =9/nikic-php-parser/PhpParser/Node/Expr/Assign.phpf&N1nikic-php-parser/PhpParser/Node/Expr/AssignOp.phpf ><nikic-php-parser/PhpParser/Node/Expr/AssignOp/BitwiseAnd.phpf`Q<;nikic-php-parser/PhpParser/Node/Expr/AssignOp/BitwiseOr.phpfO<nikic-php-parser/PhpParser/Node/Expr/AssignOp/BitwiseXor.phpfP3Ӥ:nikic-php-parser/PhpParser/Node/Expr/AssignOp/Coalesce.phpf  8nikic-php-parser/PhpParser/Node/Expr/AssignOp/Concat.phpf4cڤ5nikic-php-parser/PhpParser/Node/Expr/AssignOp/Div.phpfnikic-php-parser/PhpParser/Node/Expr/BinaryOp/NotIdentical.php^f^Rf86nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Plus.phpLfL ::5nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Pow.phpKfKMmи;nikic-php-parser/PhpParser/Node/Expr/BinaryOp/ShiftLeft.phpWfWei<nikic-php-parser/PhpParser/Node/Expr/BinaryOp/ShiftRight.phpYfY \639nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Smaller.phpRfRNYL@nikic-php-parser/PhpParser/Node/Expr/BinaryOp/SmallerOrEqual.phpafasa;nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Spaceship.phpXfX^ޤ3nikic-php-parser/PhpParser/Node/Expr/BitwiseNot.phpfTƼ3nikic-php-parser/PhpParser/Node/Expr/BooleanNot.phpfܐ@1nikic-php-parser/PhpParser/Node/Expr/CallLike.php6f6=p-nikic-php-parser/PhpParser/Node/Expr/Cast.phpIfI*4nikic-php-parser/PhpParser/Node/Expr/Cast/Array_.phpf7_ؤ3nikic-php-parser/PhpParser/Node/Expr/Cast/Bool_.phpfw4nikic-php-parser/PhpParser/Node/Expr/Cast/Double.phpf`9+2nikic-php-parser/PhpParser/Node/Expr/Cast/Int_.phpf%D5nikic-php-parser/PhpParser/Node/Expr/Cast/Object_.phpf/=5nikic-php-parser/PhpParser/Node/Expr/Cast/String_.phpf"k 4nikic-php-parser/PhpParser/Node/Expr/Cast/Unset_.phpfO58nikic-php-parser/PhpParser/Node/Expr/ClassConstFetch.phpf -/nikic-php-parser/PhpParser/Node/Expr/Clone_.phpf0nikic-php-parser/PhpParser/Node/Expr/Closure.php -f -d3nikic-php-parser/PhpParser/Node/Expr/ClosureUse.phpfJ3nikic-php-parser/PhpParser/Node/Expr/ConstFetch.phpf流./nikic-php-parser/PhpParser/Node/Expr/Empty_.phpfYФ.nikic-php-parser/PhpParser/Node/Expr/Error.php -f -|6nikic-php-parser/PhpParser/Node/Expr/ErrorSuppress.phpf}.nikic-php-parser/PhpParser/Node/Expr/Eval_.phpfmYg.nikic-php-parser/PhpParser/Node/Expr/Exit_.phpfS1nikic-php-parser/PhpParser/Node/Expr/FuncCall.php?f?V1nikic-php-parser/PhpParser/Node/Expr/Include_.phpfĚ:4nikic-php-parser/PhpParser/Node/Expr/Instanceof_.phpmfml/nikic-php-parser/PhpParser/Node/Expr/Isset_.phpfQ{.nikic-php-parser/PhpParser/Node/Expr/List_.phpf!B /nikic-php-parser/PhpParser/Node/Expr/Match_.phpf?N:3nikic-php-parser/PhpParser/Node/Expr/MethodCall.phpcfc[A-nikic-php-parser/PhpParser/Node/Expr/New_.phpfL;nikic-php-parser/PhpParser/Node/Expr/NullsafeMethodCall.phpzfzhu >nikic-php-parser/PhpParser/Node/Expr/NullsafePropertyFetch.phpf>0nikic-php-parser/PhpParser/Node/Expr/PostDec.phpfa0nikic-php-parser/PhpParser/Node/Expr/PostInc.phpfqz/nikic-php-parser/PhpParser/Node/Expr/PreDec.phpf6/nikic-php-parser/PhpParser/Node/Expr/PreInc.phpf5/nikic-php-parser/PhpParser/Node/Expr/Print_.phpf,6nikic-php-parser/PhpParser/Node/Expr/PropertyFetch.phpfˑ[2nikic-php-parser/PhpParser/Node/Expr/ShellExec.phpfe3nikic-php-parser/PhpParser/Node/Expr/StaticCall.php}f}6m`<nikic-php-parser/PhpParser/Node/Expr/StaticPropertyFetch.php6f6oSX0nikic-php-parser/PhpParser/Node/Expr/Ternary.phpfjˤ/nikic-php-parser/PhpParser/Node/Expr/Throw_.phpf+b?3nikic-php-parser/PhpParser/Node/Expr/UnaryMinus.phpfFz12nikic-php-parser/PhpParser/Node/Expr/UnaryPlus.phpfb1nikic-php-parser/PhpParser/Node/Expr/Variable.phpf2nikic-php-parser/PhpParser/Node/Expr/YieldFrom.phpfM/nikic-php-parser/PhpParser/Node/Expr/Yield_.phpfff#00nikic-php-parser/PhpParser/Node/FunctionLike.phpfkp.nikic-php-parser/PhpParser/Node/Identifier.phpf5k4nikic-php-parser/PhpParser/Node/IntersectionType.phpfwȤ,nikic-php-parser/PhpParser/Node/MatchArm.phpfo(nikic-php-parser/PhpParser/Node/Name.php)f)/7nikic-php-parser/PhpParser/Node/Name/FullyQualified.phpff1nikic-php-parser/PhpParser/Node/Name/Relative.phpfvmۤ0nikic-php-parser/PhpParser/Node/NullableType.phpfߡ)nikic-php-parser/PhpParser/Node/Param.phpkfkL*nikic-php-parser/PhpParser/Node/Scalar.phpofo=2nikic-php-parser/PhpParser/Node/Scalar/DNumber.phpfr1M3nikic-php-parser/PhpParser/Node/Scalar/Encapsed.phpf1G=nikic-php-parser/PhpParser/Node/Scalar/EncapsedStringPart.phpfBC2nikic-php-parser/PhpParser/Node/Scalar/LNumber.php f ;5nikic-php-parser/PhpParser/Node/Scalar/MagicConst.phpkfk -K<nikic-php-parser/PhpParser/Node/Scalar/MagicConst/Class_.php\f\E9nikic-php-parser/PhpParser/Node/Scalar/MagicConst/Dir.phpUfUIؤ:nikic-php-parser/PhpParser/Node/Scalar/MagicConst/File.phpXfX¤?nikic-php-parser/PhpParser/Node/Scalar/MagicConst/Function_.phpefe_ޤ:nikic-php-parser/PhpParser/Node/Scalar/MagicConst/Line.phpXfX~&<nikic-php-parser/PhpParser/Node/Scalar/MagicConst/Method.php^f^j@nikic-php-parser/PhpParser/Node/Scalar/MagicConst/Namespace_.phphfhRF<nikic-php-parser/PhpParser/Node/Scalar/MagicConst/Trait_.php\f\yA2nikic-php-parser/PhpParser/Node/Scalar/String_.php}f}է(nikic-php-parser/PhpParser/Node/Stmt.phpfy/nikic-php-parser/PhpParser/Node/Stmt/Break_.phpf1;.nikic-php-parser/PhpParser/Node/Stmt/Case_.phptft /nikic-php-parser/PhpParser/Node/Stmt/Catch_.phpf3nikic-php-parser/PhpParser/Node/Stmt/ClassConst.php f 9e2nikic-php-parser/PhpParser/Node/Stmt/ClassLike.php f :/4nikic-php-parser/PhpParser/Node/Stmt/ClassMethod.php f /nikic-php-parser/PhpParser/Node/Stmt/Class_.phpfw2/nikic-php-parser/PhpParser/Node/Stmt/Const_.phpf12nikic-php-parser/PhpParser/Node/Stmt/Continue_.phpfۖ(7nikic-php-parser/PhpParser/Node/Stmt/DeclareDeclare.phpfL1nikic-php-parser/PhpParser/Node/Stmt/Declare_.phpf涤,nikic-php-parser/PhpParser/Node/Stmt/Do_.phpJfJ/p.nikic-php-parser/PhpParser/Node/Stmt/Echo_.phpfu&0nikic-php-parser/PhpParser/Node/Stmt/ElseIf_.phpQfQJ.nikic-php-parser/PhpParser/Node/Stmt/Else_.phpf 1nikic-php-parser/PhpParser/Node/Stmt/EnumCase.phpf.nikic-php-parser/PhpParser/Node/Stmt/Enum_.phpEfE8;+3nikic-php-parser/PhpParser/Node/Stmt/Expression.phpf+91nikic-php-parser/PhpParser/Node/Stmt/Finally_.phpf<)r-nikic-php-parser/PhpParser/Node/Stmt/For_.phpFfFxΤ1nikic-php-parser/PhpParser/Node/Stmt/Foreach_.phpwfw`$ܤ2nikic-php-parser/PhpParser/Node/Stmt/Function_.php8 -f8 -T g0nikic-php-parser/PhpParser/Node/Stmt/Global_.phpf̤.nikic-php-parser/PhpParser/Node/Stmt/Goto_.phpf11nikic-php-parser/PhpParser/Node/Stmt/GroupUse.phpf1y.5nikic-php-parser/PhpParser/Node/Stmt/HaltCompiler.phpf^ ,nikic-php-parser/PhpParser/Node/Stmt/If_.phpBfB#Di3nikic-php-parser/PhpParser/Node/Stmt/InlineHTML.phpfi3nikic-php-parser/PhpParser/Node/Stmt/Interface_.phpfaY.nikic-php-parser/PhpParser/Node/Stmt/Label.phpf?>3nikic-php-parser/PhpParser/Node/Stmt/Namespace_.phpf V,nikic-php-parser/PhpParser/Node/Stmt/Nop.phpHfH11nikic-php-parser/PhpParser/Node/Stmt/Property.phpc -fc -9nikic-php-parser/PhpParser/Node/Stmt/PropertyProperty.phpf]0nikic-php-parser/PhpParser/Node/Stmt/Return_.phpfȤ2nikic-php-parser/PhpParser/Node/Stmt/StaticVar.phpfi0nikic-php-parser/PhpParser/Node/Stmt/Static_.phpfȤ0nikic-php-parser/PhpParser/Node/Stmt/Switch_.php=f=YM/nikic-php-parser/PhpParser/Node/Stmt/Throw_.phpf ˤ1nikic-php-parser/PhpParser/Node/Stmt/TraitUse.phpf>J;nikic-php-parser/PhpParser/Node/Stmt/TraitUseAdaptation.php"f")DAnikic-php-parser/PhpParser/Node/Stmt/TraitUseAdaptation/Alias.phpIfISƤFnikic-php-parser/PhpParser/Node/Stmt/TraitUseAdaptation/Precedence.phpbfb=@/nikic-php-parser/PhpParser/Node/Stmt/Trait_.php f vI1nikic-php-parser/PhpParser/Node/Stmt/TryCatch.php-f-/nikic-php-parser/PhpParser/Node/Stmt/Unset_.phpf9/nikic-php-parser/PhpParser/Node/Stmt/UseUse.phppfp=drt-nikic-php-parser/PhpParser/Node/Stmt/Use_.phptftX /nikic-php-parser/PhpParser/Node/Stmt/While_.phpMfM(o-nikic-php-parser/PhpParser/Node/UnionType.phpfvx5nikic-php-parser/PhpParser/Node/VarLikeIdentifier.phpfhd7nikic-php-parser/PhpParser/Node/VariadicPlaceholder.phpfSqC+nikic-php-parser/PhpParser/NodeAbstract.php^f^ B)nikic-php-parser/PhpParser/NodeDumper.php}f}5 )nikic-php-parser/PhpParser/NodeFinder.php f ۺ2-,nikic-php-parser/PhpParser/NodeTraverser.phpa'fa'7Z5nikic-php-parser/PhpParser/NodeTraverserInterface.phpf*nikic-php-parser/PhpParser/NodeVisitor.phpf.Jq9nikic-php-parser/PhpParser/NodeVisitor/CloningVisitor.phpf"ۤ9nikic-php-parser/PhpParser/NodeVisitor/FindingVisitor.phpfF#>nikic-php-parser/PhpParser/NodeVisitor/FirstFindingVisitor.php f bt 7nikic-php-parser/PhpParser/NodeVisitor/NameResolver.php'f' rä@nikic-php-parser/PhpParser/NodeVisitor/NodeConnectingVisitor.phpfut6Bnikic-php-parser/PhpParser/NodeVisitor/ParentConnectingVisitor.phpfv`2nikic-php-parser/PhpParser/NodeVisitorAbstract.phpf8%nikic-php-parser/PhpParser/Parser.phpfv+.nikic-php-parser/PhpParser/Parser/Multiple.phpfv*nikic-php-parser/PhpParser/Parser/Php5.php ,f ,^r*nikic-php-parser/PhpParser/Parser/Php7.php Uf Ui$v,nikic-php-parser/PhpParser/Parser/Tokens.php*f*ˣդ-nikic-php-parser/PhpParser/ParserAbstract.phpfM7Ӥ,nikic-php-parser/PhpParser/ParserFactory.php -f -*uA5nikic-php-parser/PhpParser/PrettyPrinter/Standard.phpWfW䏴4nikic-php-parser/PhpParser/PrettyPrinterAbstract.phpyfyME7object-enumerator/LICENSEfy{object-reflector/LICENSEf9vphar-io-manifest/LICENSE`f`p+phar-io-manifest/ManifestDocumentMapper.phpfn#phar-io-manifest/ManifestLoader.phpfTơ'phar-io-manifest/ManifestSerializer.php$f$^):phar-io-manifest/exceptions/ElementCollectionException.phpfIn)phar-io-manifest/exceptions/Exception.phpfֽН?phar-io-manifest/exceptions/InvalidApplicationNameException.php<f<W5phar-io-manifest/exceptions/InvalidEmailException.phpf)Ϫ}3phar-io-manifest/exceptions/InvalidUrlException.php f x᳤9phar-io-manifest/exceptions/ManifestDocumentException.phpf/"@phar-io-manifest/exceptions/ManifestDocumentLoadingException.phpf-?phar-io-manifest/exceptions/ManifestDocumentMapperException.phpfJR18phar-io-manifest/exceptions/ManifestElementException.phpf̏7phar-io-manifest/exceptions/ManifestLoaderException.phpfl -7phar-io-manifest/exceptions/NoEmailAddressException.phpf'phar-io-manifest/values/Application.php -f -n+phar-io-manifest/values/ApplicationName.phpfä"phar-io-manifest/values/Author.phpf ,phar-io-manifest/values/AuthorCollection.php1f1Q4phar-io-manifest/values/AuthorCollectionIterator.phpfw2S,phar-io-manifest/values/BundledComponent.phpfff6phar-io-manifest/values/BundledComponentCollection.phpfMv>phar-io-manifest/values/BundledComponentCollectionIterator.phpfC0phar-io-manifest/values/CopyrightInformation.phprfrOv!phar-io-manifest/values/Email.phpfe%phar-io-manifest/values/Extension.phpf#phar-io-manifest/values/Library.phpf\ic#phar-io-manifest/values/License.phpf -12$phar-io-manifest/values/Manifest.php0 -f0 -Q3phar-io-manifest/values/PhpExtensionRequirement.phpfk 1phar-io-manifest/values/PhpVersionRequirement.phpBfB3M'phar-io-manifest/values/Requirement.phpf U1phar-io-manifest/values/RequirementCollection.phpwfw<Ť9phar-io-manifest/values/RequirementCollectionIterator.phpfA phar-io-manifest/values/Type.phpffphar-io-manifest/values/Url.phpfz3&phar-io-manifest/xml/AuthorElement.phpfKe0phar-io-manifest/xml/AuthorElementCollection.phpNfNf̐'phar-io-manifest/xml/BundlesElement.phpufuy)phar-io-manifest/xml/ComponentElement.phpfDTt!3phar-io-manifest/xml/ComponentElementCollection.phpWfWDS8(phar-io-manifest/xml/ContainsElement.phpf"Ȥ)phar-io-manifest/xml/CopyrightElement.phpf:Qv*phar-io-manifest/xml/ElementCollection.phpfz%#phar-io-manifest/xml/ExtElement.php+f+sd-phar-io-manifest/xml/ExtElementCollection.phpEfE!)phar-io-manifest/xml/ExtensionElement.phpfb'phar-io-manifest/xml/LicenseElement.phpf̟)phar-io-manifest/xml/ManifestDocument.php f j(phar-io-manifest/xml/ManifestElement.phpfuʼq#phar-io-manifest/xml/PhpElement.php"f"E(phar-io-manifest/xml/RequiresElement.phpFfF~?!phar-io-version/BuildMetaData.phpfjTphar-io-version/LICENSE&f&Ҫ $phar-io-version/PreReleaseSuffix.phpf{,-phar-io-version/Version.phpf% Z+phar-io-version/VersionConstraintParser.phpX fX z'0*phar-io-version/VersionConstraintValue.phpL -fL -u9:!phar-io-version/VersionNumber.phpf3N9phar-io-version/constraints/AbstractVersionConstraint.phpfzB9phar-io-version/constraints/AndVersionConstraintGroup.phpf7 ۤ4phar-io-version/constraints/AnyVersionConstraint.phpVfV-86phar-io-version/constraints/ExactVersionConstraint.phpfEphar-io-version/constraints/GreaterThanOrEqualToVersionConstraint.phpfs+8phar-io-version/constraints/OrVersionConstraintGroup.phpfl,{Fphar-io-version/constraints/SpecificMajorAndMinorVersionConstraint.phpf{sMդ>phar-io-version/constraints/SpecificMajorVersionConstraint.php f ft~1phar-io-version/constraints/VersionConstraint.phpfw<(phar-io-version/exceptions/Exception.phpf<?phar-io-version/exceptions/InvalidPreReleaseSuffixException.phpf[6phar-io-version/exceptions/InvalidVersionException.phpfy7phar-io-version/exceptions/NoBuildMetaDataException.phpf+${:phar-io-version/exceptions/NoPreReleaseSuffixException.phpf"Dphar-io-version/exceptions/UnsupportedVersionConstraintException.phpf樤"php-code-coverage/CodeCoverage.phpnEfnEio'#php-code-coverage/Driver/Driver.phpf֣_7'php-code-coverage/Driver/PcovDriver.phpVfVQI)php-code-coverage/Driver/PhpdbgDriver.phpf -ff -K4%php-code-coverage/Driver/Selector.php f ])*php-code-coverage/Driver/Xdebug2Driver.phpM fM n*php-code-coverage/Driver/Xdebug3Driver.php f -Jphp-code-coverage/Exception/BranchAndPathCoverageNotSupportedException.phpfzFphp-code-coverage/Exception/DeadCodeDetectionNotSupportedException.phpfoICphp-code-coverage/Exception/DirectoryCouldNotBeCreatedException.phpf<6')php-code-coverage/Exception/Exception.phpf+Q8php-code-coverage/Exception/InvalidArgumentException.phpfl~ФFphp-code-coverage/Exception/NoCodeCoverageDriverAvailableException.php3f35oYC]php-code-coverage/Exception/NoCodeCoverageDriverWithPathCoverageSupportAvailableException.phpefeX3/php-code-coverage/Exception/ParserException.phpfpڤDphp-code-coverage/Exception/PathExistsButIsNotDirectoryException.phpfikd9php-code-coverage/Exception/PcovNotAvailableException.phpifiq;php-code-coverage/Exception/PhpdbgNotAvailableException.phphfh |=3php-code-coverage/Exception/ReflectionException.phpf`?php-code-coverage/Exception/ReportAlreadyFinalizedException.php>f>mU=Iphp-code-coverage/Exception/StaticAnalysisCacheNotConfiguredException.phpfp6php-code-coverage/Exception/TestIdMissingException.phpf3OCphp-code-coverage/Exception/UnintentionallyCoveredCodeException.php/f/s=php-code-coverage/Exception/WriteOperationFailedException.phpfu䶤;php-code-coverage/Exception/WrongXdebugVersionException.phpft!:php-code-coverage/Exception/Xdebug2NotEnabledException.phpnfn`h:php-code-coverage/Exception/Xdebug3NotEnabledException.phpfY+C;php-code-coverage/Exception/XdebugNotAvailableException.phpmfm{F,php-code-coverage/Exception/XmlException.phpf0)Rphp-code-coverage/Filter.php f _tphp-code-coverage/LICENSEf-~y֤'php-code-coverage/Node/AbstractNode.phpCfCvm"php-code-coverage/Node/Builder.phpfk!*Ф$php-code-coverage/Node/CrapIndex.phpfܤ$php-code-coverage/Node/Directory.php&f&fphp-code-coverage/Node/File.phpKfKFϕ#php-code-coverage/Node/Iterator.phpfL3>/php-code-coverage/ProcessedCodeCoverageData.php$f$n)php-code-coverage/RawCodeCoverageData.php%!f%!?ޤ#php-code-coverage/Report/Clover.phpk(fk(*Ð&php-code-coverage/Report/Cobertura.php1f1mI#php-code-coverage/Report/Crap4j.phpPfPK9T(php-code-coverage/Report/Html/Facade.php6f696N*php-code-coverage/Report/Html/Renderer.phpq!fq!|4php-code-coverage/Report/Html/Renderer/Dashboard.phpS fS >P94php-code-coverage/Report/Html/Renderer/Directory.php0f0(/php-code-coverage/Report/Html/Renderer/File.phpf+֤Bphp-code-coverage/Report/Html/Renderer/Template/branches.html.distfh2+Fphp-code-coverage/Report/Html/Renderer/Template/coverage_bar.html.dist'f'O}Mphp-code-coverage/Report/Html/Renderer/Template/coverage_bar_branch.html.dist'f'O}Ephp-code-coverage/Report/Html/Renderer/Template/css/bootstrap.min.cssyfyĤ>php-code-coverage/Report/Html/Renderer/Template/css/custom.cssfAphp-code-coverage/Report/Html/Renderer/Template/css/nv.d3.min.cssX%fX%0,@php-code-coverage/Report/Html/Renderer/Template/css/octicons.cssXfX'#=php-code-coverage/Report/Html/Renderer/Template/css/style.css -f -Cphp-code-coverage/Report/Html/Renderer/Template/dashboard.html.distfDJphp-code-coverage/Report/Html/Renderer/Template/dashboard_branch.html.distfDCphp-code-coverage/Report/Html/Renderer/Template/directory.html.distfՆJphp-code-coverage/Report/Html/Renderer/Template/directory_branch.html.distfn2]Hphp-code-coverage/Report/Html/Renderer/Template/directory_item.html.distAfAdsOphp-code-coverage/Report/Html/Renderer/Template/directory_item_branch.html.dist;f;mۤ>php-code-coverage/Report/Html/Renderer/Template/file.html.distP fP j*Ephp-code-coverage/Report/Html/Renderer/Template/file_branch.html.dist f ㉞Cphp-code-coverage/Report/Html/Renderer/Template/file_item.html.distrfr/yJphp-code-coverage/Report/Html/Renderer/Template/file_item_branch.html.distlfl-Cphp-code-coverage/Report/Html/Renderer/Template/icons/file-code.svg0f0QUUHphp-code-coverage/Report/Html/Renderer/Template/icons/file-directory.svgfZCphp-code-coverage/Report/Html/Renderer/Template/js/bootstrap.min.jscfc"#<php-code-coverage/Report/Html/Renderer/Template/js/d3.min.jsPfPhb:php-code-coverage/Report/Html/Renderer/Template/js/file.jsfb䆤@php-code-coverage/Report/Html/Renderer/Template/js/jquery.min.js@^f@^ ?php-code-coverage/Report/Html/Renderer/Template/js/nv.d3.min.jsRfRphp-code-coverage/Report/Html/Renderer/Template/line.html.distf{?php-code-coverage/Report/Html/Renderer/Template/lines.html.distefedf Ephp-code-coverage/Report/Html/Renderer/Template/method_item.html.distfjפLphp-code-coverage/Report/Html/Renderer/Template/method_item_branch.html.distfyĎk?php-code-coverage/Report/Html/Renderer/Template/paths.html.distf*'ݤ php-code-coverage/Report/PHP.php'f'~!php-code-coverage/Report/Text.php'f'a-41php-code-coverage/Report/Xml/BuildInformation.php f )php-code-coverage/Report/Xml/Coverage.php3f3R1F*php-code-coverage/Report/Xml/Directory.phpf Fn'php-code-coverage/Report/Xml/Facade.php4"f4"cb%php-code-coverage/Report/Xml/File.php/f/\'php-code-coverage/Report/Xml/Method.php[f[&J%php-code-coverage/Report/Xml/Node.php7f72s(php-code-coverage/Report/Xml/Project.phpjfj;Z0_'php-code-coverage/Report/Xml/Report.php f H^'php-code-coverage/Report/Xml/Source.phpf g&php-code-coverage/Report/Xml/Tests.phpfi)o'php-code-coverage/Report/Xml/Totals.phpf K%php-code-coverage/Report/Xml/Unit.phpf}=0php-code-coverage/StaticAnalysis/CacheWarmer.phphfh618php-code-coverage/StaticAnalysis/CachingFileAnalyser.phpfʙ;php-code-coverage/StaticAnalysis/CodeUnitFindingVisitor.phpt(ft(Y8Bphp-code-coverage/StaticAnalysis/ExecutableLinesFindingVisitor.php)f)<1php-code-coverage/StaticAnalysis/FileAnalyser.phpfNm?php-code-coverage/StaticAnalysis/IgnoredLinesFindingVisitor.phpA fA C8php-code-coverage/StaticAnalysis/ParsingFileAnalyser.phpf4,%php-code-coverage/Util/Filesystem.phpfv %php-code-coverage/Util/Percentage.phpfUTphp-code-coverage/Version.phpf -Ymphp-file-iterator/Facade.php) -f) -$!Jphp-file-iterator/Factory.phpf5php-file-iterator/Iterator.php^ f^ php-file-iterator/LICENSEfo:php-invoker/Invoker.php f Q$php-invoker/exceptions/Exception.phpvfv'P=Dphp-invoker/exceptions/ProcessControlExtensionNotLoadedException.phpfӤ+php-invoker/exceptions/TimeoutException.phpftJphp-text-template/LICENSEfuphp-text-template/Template.php, f, d*php-text-template/exceptions/Exception.php}f}`"9php-text-template/exceptions/InvalidArgumentException.phpf¤1php-text-template/exceptions/RuntimeException.phpf]Mpphp-timer/Duration.php -f -qݿphp-timer/LICENSEfx$php-timer/ResourceUsageFormatter.phpfqphp-timer/Timer.phpf"?Z"php-timer/exceptions/Exception.phprfr</php-timer/exceptions/NoActiveTimerException.phpf*Ephp-timer/exceptions/TimeSinceStartOfRequestNotAvailableException.phpf.+phpdocumentor-reflection-common/Element.php f (phpdocumentor-reflection-common/File.phpf -)phpdocumentor-reflection-common/Fqsen.phpf;U'phpdocumentor-reflection-common/LICENSE9f9*2Ȑ,phpdocumentor-reflection-common/Location.phpf$}+phpdocumentor-reflection-common/Project.phpfI 2phpdocumentor-reflection-common/ProjectFactory.phpcfciM.phpdocumentor-reflection-docblock/DocBlock.phpfu:phpdocumentor-reflection-docblock/DocBlock/Description.php f Aphpdocumentor-reflection-docblock/DocBlock/DescriptionFactory.phpf2+ =<phpdocumentor-reflection-docblock/DocBlock/ExampleFinder.php4f4`99phpdocumentor-reflection-docblock/DocBlock/Serializer.phpfMXAphpdocumentor-reflection-docblock/DocBlock/StandardTagFactory.phpH1fH1^|%2phpdocumentor-reflection-docblock/DocBlock/Tag.phpf:)9phpdocumentor-reflection-docblock/DocBlock/TagFactory.phpfRפ:phpdocumentor-reflection-docblock/DocBlock/Tags/Author.php f `n;phpdocumentor-reflection-docblock/DocBlock/Tags/BaseTag.phpf; A:phpdocumentor-reflection-docblock/DocBlock/Tags/Covers.php -f -*Fm=>phpdocumentor-reflection-docblock/DocBlock/Tags/Deprecated.php -f -$;phpdocumentor-reflection-docblock/DocBlock/Tags/Example.phpfBפHphpdocumentor-reflection-docblock/DocBlock/Tags/Factory/StaticMethod.php"f"V`i=phpdocumentor-reflection-docblock/DocBlock/Tags/Formatter.php&f&-Lphpdocumentor-reflection-docblock/DocBlock/Tags/Formatter/AlignFormatter.php}f}ZRphpdocumentor-reflection-docblock/DocBlock/Tags/Formatter/PassthroughFormatter.phpf0Z;phpdocumentor-reflection-docblock/DocBlock/Tags/Generic.php f  ->phpdocumentor-reflection-docblock/DocBlock/Tags/InvalidTag.php4f4&R8phpdocumentor-reflection-docblock/DocBlock/Tags/Link.phpflYp_:phpdocumentor-reflection-docblock/DocBlock/Tags/Method.phpf1 59phpdocumentor-reflection-docblock/DocBlock/Tags/Param.php f g/<phpdocumentor-reflection-docblock/DocBlock/Tags/Property.php f y@phpdocumentor-reflection-docblock/DocBlock/Tags/PropertyRead.php f cAphpdocumentor-reflection-docblock/DocBlock/Tags/PropertyWrite.php f ¨>Cphpdocumentor-reflection-docblock/DocBlock/Tags/Reference/Fqsen.php4f4\Gphpdocumentor-reflection-docblock/DocBlock/Tags/Reference/Reference.phpfFGAphpdocumentor-reflection-docblock/DocBlock/Tags/Reference/Url.phpfc8;phpdocumentor-reflection-docblock/DocBlock/Tags/Return_.php+f+:I7phpdocumentor-reflection-docblock/DocBlock/Tags/See.php4 f4 kg9phpdocumentor-reflection-docblock/DocBlock/Tags/Since.phpk -fk -诤:phpdocumentor-reflection-docblock/DocBlock/Tags/Source.php f Zpq?phpdocumentor-reflection-docblock/DocBlock/Tags/TagWithType.phpf4:phpdocumentor-reflection-docblock/DocBlock/Tags/Throws.php,f,oO8phpdocumentor-reflection-docblock/DocBlock/Tags/Uses.php^ -f^ -(Y8phpdocumentor-reflection-docblock/DocBlock/Tags/Var_.php f 셤;phpdocumentor-reflection-docblock/DocBlock/Tags/Version.php -f -&.25phpdocumentor-reflection-docblock/DocBlockFactory.php$f$j>phpdocumentor-reflection-docblock/DocBlockFactoryInterface.phpfX}=phpdocumentor-reflection-docblock/Exception/PcreException.phpf٤)phpdocumentor-reflection-docblock/LICENSE8f8ʤ+phpdocumentor-reflection-docblock/Utils.php f ޤ-phpdocumentor-type-resolver/FqsenResolver.php f C#phpdocumentor-type-resolver/LICENSE8f8ʤ*phpdocumentor-type-resolver/PseudoType.phpyfy!d6phpdocumentor-type-resolver/PseudoTypes/ArrayShape.phpfP7:phpdocumentor-type-resolver/PseudoTypes/ArrayShapeItem.phpfWw:phpdocumentor-type-resolver/PseudoTypes/CallableString.phppfp~ݤ;phpdocumentor-type-resolver/PseudoTypes/ConstExpression.phpf*2phpdocumentor-type-resolver/PseudoTypes/False_.phpf6phpdocumentor-type-resolver/PseudoTypes/FloatValue.phpfpn|=phpdocumentor-type-resolver/PseudoTypes/HtmlEscapedString.phpwfw$8phpdocumentor-type-resolver/PseudoTypes/IntegerRange.php5f5ˤ8phpdocumentor-type-resolver/PseudoTypes/IntegerValue.phpfQ#w1phpdocumentor-type-resolver/PseudoTypes/List_.phpfWnV9phpdocumentor-type-resolver/PseudoTypes/LiteralString.phpnfna;phpdocumentor-type-resolver/PseudoTypes/LowercaseString.phprfrˮn;phpdocumentor-type-resolver/PseudoTypes/NegativeInteger.phpkfkv<^8phpdocumentor-type-resolver/PseudoTypes/NonEmptyList.phpfNܤCphpdocumentor-type-resolver/PseudoTypes/NonEmptyLowercaseString.phpf[ :phpdocumentor-type-resolver/PseudoTypes/NonEmptyString.phpqfqd9phpdocumentor-type-resolver/PseudoTypes/NumericString.phpnfnj {4phpdocumentor-type-resolver/PseudoTypes/Numeric_.phpfIuԤ;phpdocumentor-type-resolver/PseudoTypes/PositiveInteger.phpkfk1ۤ7phpdocumentor-type-resolver/PseudoTypes/StringValue.phpf7phpdocumentor-type-resolver/PseudoTypes/TraitString.phpjfjAפ1phpdocumentor-type-resolver/PseudoTypes/True_.phpf $phpdocumentor-type-resolver/Type.phpf*ޤ,phpdocumentor-type-resolver/TypeResolver.phpUfUh֎2phpdocumentor-type-resolver/Types/AbstractList.php}f}y`4phpdocumentor-type-resolver/Types/AggregatedType.php -f -SqtW.phpdocumentor-type-resolver/Types/ArrayKey.phpf򔭤,phpdocumentor-type-resolver/Types/Array_.phpf=y-phpdocumentor-type-resolver/Types/Boolean.phpvfvȚD7phpdocumentor-type-resolver/Types/CallableParameter.phpf򐛤/phpdocumentor-type-resolver/Types/Callable_.phpf1phpdocumentor-type-resolver/Types/ClassString.phpSfSŤ0phpdocumentor-type-resolver/Types/Collection.phpfn%.phpdocumentor-type-resolver/Types/Compound.phpf݇9-phpdocumentor-type-resolver/Types/Context.php f _ʋ4phpdocumentor-type-resolver/Types/ContextFactory.php7f7֤0phpdocumentor-type-resolver/Types/Expression.php@f@M,phpdocumentor-type-resolver/Types/Float_.phpofo-phpdocumentor-type-resolver/Types/Integer.phprfry5phpdocumentor-type-resolver/Types/InterfaceString.phpf]`2phpdocumentor-type-resolver/Types/Intersection.phpf6פ/phpdocumentor-type-resolver/Types/Iterable_.phpCfCȤ,phpdocumentor-type-resolver/Types/Mixed_.phpfx/,phpdocumentor-type-resolver/Types/Never_.phpfsl+phpdocumentor-type-resolver/Types/Null_.phpfbr.phpdocumentor-type-resolver/Types/Nullable.phpZfZ#:?z-phpdocumentor-type-resolver/Types/Object_.phpfG-phpdocumentor-type-resolver/Types/Parent_.phpfD>/phpdocumentor-type-resolver/Types/Resource_.phpf,phpdocumentor-type-resolver/Types/Scalar.phpf#+phpdocumentor-type-resolver/Types/Self_.phpfS-phpdocumentor-type-resolver/Types/Static_.php f g20-phpdocumentor-type-resolver/Types/String_.php{f{uYQѤ*phpdocumentor-type-resolver/Types/This.phpafat':+phpdocumentor-type-resolver/Types/Void_.phpfKphpspec-prophecy/LICENSE}f} ߦ&phpspec-prophecy/Prophecy/Argument.php]f]eQ8phpspec-prophecy/Prophecy/Argument/ArgumentsWildcard.php f N<:phpspec-prophecy/Prophecy/Argument/Token/AnyValueToken.phpfIZ%%;phpspec-prophecy/Prophecy/Argument/Token/AnyValuesToken.phpf'`Bphpspec-prophecy/Prophecy/Argument/Token/ApproximateValueToken.phpmfm٬c<phpspec-prophecy/Prophecy/Argument/Token/ArrayCountToken.phpQfQ_穤<phpspec-prophecy/Prophecy/Argument/Token/ArrayEntryToken.phpfRAphpspec-prophecy/Prophecy/Argument/Token/ArrayEveryEntryToken.phpf#:phpspec-prophecy/Prophecy/Argument/Token/CallbackToken.phpff"<phpspec-prophecy/Prophecy/Argument/Token/ExactValueToken.php f W@phpspec-prophecy/Prophecy/Argument/Token/IdenticalValueToken.phpf49phpspec-prophecy/Prophecy/Argument/Token/InArrayToken.phpfͪ!<phpspec-prophecy/Prophecy/Argument/Token/LogicalAndToken.phpf<phpspec-prophecy/Prophecy/Argument/Token/LogicalNotToken.phpAfA<phpspec-prophecy/Prophecy/Argument/Token/NotInArrayToken.phpf=phpspec-prophecy/Prophecy/Argument/Token/ObjectStateToken.php -f -LN@phpspec-prophecy/Prophecy/Argument/Token/StringContainsToken.php-f-3xD;phpspec-prophecy/Prophecy/Argument/Token/TokenInterface.phpfG66phpspec-prophecy/Prophecy/Argument/Token/TypeToken.phpf$'phpspec-prophecy/Prophecy/Call/Call.phpf΂-phpspec-prophecy/Prophecy/Call/CallCenter.phpf\%-j:phpspec-prophecy/Prophecy/Comparator/ClosureComparator.phpfx0phpspec-prophecy/Prophecy/Comparator/Factory.phpfZ>8phpspec-prophecy/Prophecy/Comparator/FactoryProvider.phpfQ`;phpspec-prophecy/Prophecy/Comparator/ProphecyComparator.php+f+'ꨤ3phpspec-prophecy/Prophecy/Doubler/CachedDoubler.phpfyg5Dphpspec-prophecy/Prophecy/Doubler/ClassPatch/ClassPatchInterface.phphfhq!ʤHphpspec-prophecy/Prophecy/Doubler/ClassPatch/DisableConstructorPatch.phpf,g=phpspec-prophecy/Prophecy/Doubler/ClassPatch/KeywordPatch.php'f'?phpspec-prophecy/Prophecy/Doubler/ClassPatch/MagicCallPatch.php f Q)7Ephpspec-prophecy/Prophecy/Doubler/ClassPatch/ProphecySubjectPatch.php$ f$ bBۀPphpspec-prophecy/Prophecy/Doubler/ClassPatch/ReflectionClassNewInstancePatch.phpf -,Aphpspec-prophecy/Prophecy/Doubler/ClassPatch/SplFileInfoPatch.php@ f@ YyG?phpspec-prophecy/Prophecy/Doubler/ClassPatch/ThrowablePatch.php" f" aԤAphpspec-prophecy/Prophecy/Doubler/ClassPatch/TraversablePatch.php f wp5phpspec-prophecy/Prophecy/Doubler/DoubleInterface.phpfBۤ-phpspec-prophecy/Prophecy/Doubler/Doubler.phpf}t,Bphpspec-prophecy/Prophecy/Doubler/Generator/ClassCodeGenerator.php f Wy<phpspec-prophecy/Prophecy/Doubler/Generator/ClassCreator.phpbfbH1ؤ;phpspec-prophecy/Prophecy/Doubler/Generator/ClassMirror.php#f#Aphpspec-prophecy/Prophecy/Doubler/Generator/Node/ArgumentNode.phpfZfEphpspec-prophecy/Prophecy/Doubler/Generator/Node/ArgumentTypeNode.phpfˤ>phpspec-prophecy/Prophecy/Doubler/Generator/Node/ClassNode.php}f}KP?phpspec-prophecy/Prophecy/Doubler/Generator/Node/MethodNode.phpfCphpspec-prophecy/Prophecy/Doubler/Generator/Node/ReturnTypeNode.phpfCٮEphpspec-prophecy/Prophecy/Doubler/Generator/Node/TypeNodeAbstract.php f pACphpspec-prophecy/Prophecy/Doubler/Generator/ReflectionInterface.phpf YAphpspec-prophecy/Prophecy/Doubler/Generator/TypeHintReference.php"f"&0phpspec-prophecy/Prophecy/Doubler/LazyDouble.phpfV93phpspec-prophecy/Prophecy/Doubler/NameGenerator.phpf,ôyDphpspec-prophecy/Prophecy/Exception/Call/UnexpectedCallException.phpfOEphpspec-prophecy/Prophecy/Exception/Doubler/ClassCreatorException.phpDfDyXDphpspec-prophecy/Prophecy/Exception/Doubler/ClassMirrorException.phpdfdv48Fphpspec-prophecy/Prophecy/Exception/Doubler/ClassNotFoundException.phpf}:?phpspec-prophecy/Prophecy/Exception/Doubler/DoubleException.phpfV"^@phpspec-prophecy/Prophecy/Exception/Doubler/DoublerException.phpfhJphpspec-prophecy/Prophecy/Exception/Doubler/InterfaceNotFoundException.php!f!R3Lphpspec-prophecy/Prophecy/Exception/Doubler/MethodNotExtendableException.phpf[Gphpspec-prophecy/Prophecy/Exception/Doubler/MethodNotFoundException.phpf#kJphpspec-prophecy/Prophecy/Exception/Doubler/ReturnByReferenceException.phpfL?1phpspec-prophecy/Prophecy/Exception/Exception.phpfx@phpspec-prophecy/Prophecy/Exception/InvalidArgumentException.phpf󱙤Ephpspec-prophecy/Prophecy/Exception/Prediction/AggregateException.php;f;.Lphpspec-prophecy/Prophecy/Exception/Prediction/FailedPredictionException.phpgfg3'}}Cphpspec-prophecy/Prophecy/Exception/Prediction/NoCallsException.phpfZFphpspec-prophecy/Prophecy/Exception/Prediction/PredictionException.phpfR2ͤPphpspec-prophecy/Prophecy/Exception/Prediction/UnexpectedCallsCountException.php f ڶKphpspec-prophecy/Prophecy/Exception/Prediction/UnexpectedCallsException.phpfHphpspec-prophecy/Prophecy/Exception/Prophecy/MethodProphecyException.phpofoHphpspec-prophecy/Prophecy/Exception/Prophecy/ObjectProphecyException.phpfg6ڤBphpspec-prophecy/Prophecy/Exception/Prophecy/ProphecyException.phpfD7jIphpspec-prophecy/Prophecy/PhpDocumentor/ClassAndInterfaceTagRetriever.php`f`Oꖤ=phpspec-prophecy/Prophecy/PhpDocumentor/ClassTagRetriever.phpf:Gphpspec-prophecy/Prophecy/PhpDocumentor/MethodTagRetrieverInterface.phpfQ7phpspec-prophecy/Prophecy/Prediction/CallPrediction.php"f"Ҷڤ<phpspec-prophecy/Prophecy/Prediction/CallTimesPrediction.phpH fH ,;phpspec-prophecy/Prophecy/Prediction/CallbackPrediction.phpf :phpspec-prophecy/Prophecy/Prediction/NoCallsPrediction.php>f>y<phpspec-prophecy/Prophecy/Prediction/PredictionInterface.phpfD5phpspec-prophecy/Prophecy/Promise/CallbackPromise.php#f#26phpspec-prophecy/Prophecy/Promise/PromiseInterface.phpafaĶ= -;phpspec-prophecy/Prophecy/Promise/ReturnArgumentPromise.phpEfEj3phpspec-prophecy/Prophecy/Promise/ReturnPromise.phpufuR2phpspec-prophecy/Prophecy/Promise/ThrowPromise.php -f -^Ġ5phpspec-prophecy/Prophecy/Prophecy/MethodProphecy.php?f?prӤ5phpspec-prophecy/Prophecy/Prophecy/ObjectProphecy.phpfZRp8phpspec-prophecy/Prophecy/Prophecy/ProphecyInterface.phpqfqhRw¤?phpspec-prophecy/Prophecy/Prophecy/ProphecySubjectInterface.phpfġAr/phpspec-prophecy/Prophecy/Prophecy/Revealer.phpf m8phpspec-prophecy/Prophecy/Prophecy/RevealerInterface.phpGfGWnZ%phpspec-prophecy/Prophecy/Prophet.php<f<Vr-phpspec-prophecy/Prophecy/Util/ExportUtil.phpfTp7-phpspec-prophecy/Prophecy/Util/StringUtil.php -f -v|=1phpstan-phpdoc-parser/Ast/AbstractNodeVisitor.phpf5'phpstan-phpdoc-parser/Ast/Attribute.phpEfEhVX>phpstan-phpdoc-parser/Ast/ConstExpr/ConstExprArrayItemNode.phpf~n:phpstan-phpdoc-parser/Ast/ConstExpr/ConstExprArrayNode.php:f:}Ϥ:phpstan-phpdoc-parser/Ast/ConstExpr/ConstExprFalseNode.php1f1P:phpstan-phpdoc-parser/Ast/ConstExpr/ConstExprFloatNode.phpfR:<phpstan-phpdoc-parser/Ast/ConstExpr/ConstExprIntegerNode.phpfIl5phpstan-phpdoc-parser/Ast/ConstExpr/ConstExprNode.phpf79phpstan-phpdoc-parser/Ast/ConstExpr/ConstExprNullNode.php/f/Ko;phpstan-phpdoc-parser/Ast/ConstExpr/ConstExprStringNode.phpf/9phpstan-phpdoc-parser/Ast/ConstExpr/ConstExprTrueNode.php/f/jgͤ6phpstan-phpdoc-parser/Ast/ConstExpr/ConstFetchNode.phpfd= Cphpstan-phpdoc-parser/Ast/ConstExpr/DoctrineConstExprStringNode.phpifiwEphpstan-phpdoc-parser/Ast/ConstExpr/QuoteAwareConstExprStringNode.php f c_Ȥ"phpstan-phpdoc-parser/Ast/Node.phpf,phpstan-phpdoc-parser/Ast/NodeAttributes.phpf[/+phpstan-phpdoc-parser/Ast/NodeTraverser.php)f))phpstan-phpdoc-parser/Ast/NodeVisitor.php -f -vйQ8phpstan-phpdoc-parser/Ast/NodeVisitor/CloningVisitor.phpf"l=phpstan-phpdoc-parser/Ast/PhpDoc/AssertTagMethodValueNode.phpfg?phpstan-phpdoc-parser/Ast/PhpDoc/AssertTagPropertyValueNode.phpfb7phpstan-phpdoc-parser/Ast/PhpDoc/AssertTagValueNode.phpsfs9;phpstan-phpdoc-parser/Ast/PhpDoc/DeprecatedTagValueNode.phpfX숏@phpstan-phpdoc-parser/Ast/PhpDoc/Doctrine/DoctrineAnnotation.phpf@>phpstan-phpdoc-parser/Ast/PhpDoc/Doctrine/DoctrineArgument.phpfU!;phpstan-phpdoc-parser/Ast/PhpDoc/Doctrine/DoctrineArray.phpxfxH/?phpstan-phpdoc-parser/Ast/PhpDoc/Doctrine/DoctrineArrayItem.phpfݤBphpstan-phpdoc-parser/Ast/PhpDoc/Doctrine/DoctrineTagValueNode.phpfzY8phpstan-phpdoc-parser/Ast/PhpDoc/ExtendsTagValueNode.phpf98phpstan-phpdoc-parser/Ast/PhpDoc/GenericTagValueNode.phpf͡٤;phpstan-phpdoc-parser/Ast/PhpDoc/ImplementsTagValueNode.phpfY8phpstan-phpdoc-parser/Ast/PhpDoc/InvalidTagValueNode.phpfrr7phpstan-phpdoc-parser/Ast/PhpDoc/MethodTagValueNode.phpf2@phpstan-phpdoc-parser/Ast/PhpDoc/MethodTagValueParameterNode.phpofo,6phpstan-phpdoc-parser/Ast/PhpDoc/MixinTagValueNode.phpffZ Aphpstan-phpdoc-parser/Ast/PhpDoc/ParamClosureThisTagValueNode.php=f= -^Pphpstan-phpdoc-parser/Ast/PhpDoc/ParamImmediatelyInvokedCallableTagValueNode.phpf Jphpstan-phpdoc-parser/Ast/PhpDoc/ParamLaterInvokedCallableTagValueNode.phpf9phpstan-phpdoc-parser/Ast/PhpDoc/ParamOutTagValueNode.php5f58V6phpstan-phpdoc-parser/Ast/PhpDoc/ParamTagValueNode.phpf/4phpstan-phpdoc-parser/Ast/PhpDoc/PhpDocChildNode.phpfC/phpstan-phpdoc-parser/Ast/PhpDoc/PhpDocNode.php+f+P2phpstan-phpdoc-parser/Ast/PhpDoc/PhpDocTagNode.php -f -+7phpstan-phpdoc-parser/Ast/PhpDoc/PhpDocTagValueNode.phpf 3phpstan-phpdoc-parser/Ast/PhpDoc/PhpDocTextNode.phpf0l̤9phpstan-phpdoc-parser/Ast/PhpDoc/PropertyTagValueNode.php0f0Vm?phpstan-phpdoc-parser/Ast/PhpDoc/RequireExtendsTagValueNode.phpf1Bphpstan-phpdoc-parser/Ast/PhpDoc/RequireImplementsTagValueNode.phpf(7phpstan-phpdoc-parser/Ast/PhpDoc/ReturnTagValueNode.phpft8phpstan-phpdoc-parser/Ast/PhpDoc/SelfOutTagValueNode.phpfTr9phpstan-phpdoc-parser/Ast/PhpDoc/TemplateTagValueNode.phpfjR7phpstan-phpdoc-parser/Ast/PhpDoc/ThrowsTagValueNode.phpfLt@phpstan-phpdoc-parser/Ast/PhpDoc/TypeAliasImportTagValueNode.phpfՒn:phpstan-phpdoc-parser/Ast/PhpDoc/TypeAliasTagValueNode.phpzfzoY ->phpstan-phpdoc-parser/Ast/PhpDoc/TypelessParamTagValueNode.phpf5phpstan-phpdoc-parser/Ast/PhpDoc/UsesTagValueNode.phpfT>D4phpstan-phpdoc-parser/Ast/PhpDoc/VarTagValueNode.phpEfEK5phpstan-phpdoc-parser/Ast/Type/ArrayShapeItemNode.php|f|`;1phpstan-phpdoc-parser/Ast/Type/ArrayShapeNode.phpf7A,0phpstan-phpdoc-parser/Ast/Type/ArrayTypeNode.phpsfs%3phpstan-phpdoc-parser/Ast/Type/CallableTypeNode.phpf]<phpstan-phpdoc-parser/Ast/Type/CallableTypeParameterNode.phpfd Bphpstan-phpdoc-parser/Ast/Type/ConditionalTypeForParameterNode.phpfhפ6phpstan-phpdoc-parser/Ast/Type/ConditionalTypeNode.phpf&(i0phpstan-phpdoc-parser/Ast/Type/ConstTypeNode.phpf@wD<2phpstan-phpdoc-parser/Ast/Type/GenericTypeNode.php,f,kݤ5phpstan-phpdoc-parser/Ast/Type/IdentifierTypeNode.phpf֞hn7phpstan-phpdoc-parser/Ast/Type/IntersectionTypeNode.phpfx"r¤2phpstan-phpdoc-parser/Ast/Type/InvalidTypeNode.phpXfX3phpstan-phpdoc-parser/Ast/Type/NullableTypeNode.phpfe6phpstan-phpdoc-parser/Ast/Type/ObjectShapeItemNode.phpf·Ҥ2phpstan-phpdoc-parser/Ast/Type/ObjectShapeNode.phpFfF_7phpstan-phpdoc-parser/Ast/Type/OffsetAccessTypeNode.phpfY]/phpstan-phpdoc-parser/Ast/Type/ThisTypeNode.php!f! -b+phpstan-phpdoc-parser/Ast/Type/TypeNode.phpfǖ׹0phpstan-phpdoc-parser/Ast/Type/UnionTypeNode.phpfEphpstan-phpdoc-parser/LICENSE.f.-%phpstan-phpdoc-parser/Lexer/Lexer.phpfB]0phpstan-phpdoc-parser/Parser/ConstExprParser.phpx&fx&0phpstan-phpdoc-parser/Parser/ParserException.php f nd-phpstan-phpdoc-parser/Parser/PhpDocParser.php|f|CF0phpstan-phpdoc-parser/Parser/StringUnescaper.php f e%.phpstan-phpdoc-parser/Parser/TokenIterator.php#f#L - -+phpstan-phpdoc-parser/Parser/TypeParser.phpfD#*phpstan-phpdoc-parser/Printer/DiffElem.phpf=!s(phpstan-phpdoc-parser/Printer/Differ.phpgfgm:?)phpstan-phpdoc-parser/Printer/Printer.phpfu phpunit.xsdRFfRFAgphpunit/Exception.phpfa#phpunit/Framework/Assert.phpcfc/q&phpunit/Framework/Assert/Functions.php)f) 4]0phpunit/Framework/Constraint/Boolean/IsFalse.phpf/phpunit/Framework/Constraint/Boolean/IsTrue.phpf})phpunit/Framework/Constraint/Callback.php?f? -b2phpunit/Framework/Constraint/Cardinality/Count.phpj fj xR@ؤ8phpunit/Framework/Constraint/Cardinality/GreaterThan.php f 4phpunit/Framework/Constraint/Cardinality/IsEmpty.phpfhf5phpunit/Framework/Constraint/Cardinality/LessThan.phpf05phpunit/Framework/Constraint/Cardinality/SameSize.php_f_uŤ+phpunit/Framework/Constraint/Constraint.php"f"bǤ1phpunit/Framework/Constraint/Equality/IsEqual.php f [W?phpunit/Framework/Constraint/Equality/IsEqualCanonicalizing.php -f -'p=phpunit/Framework/Constraint/Equality/IsEqualIgnoringCase.php -f -_:phpunit/Framework/Constraint/Equality/IsEqualWithDelta.php? -f? -v;4phpunit/Framework/Constraint/Exception/Exception.phpfRu{8phpunit/Framework/Constraint/Exception/ExceptionCode.phpf ;phpunit/Framework/Constraint/Exception/ExceptionMessage.phpfw;Lphpunit/Framework/Constraint/Exception/ExceptionMessageRegularExpression.phpfLj[i;phpunit/Framework/Constraint/Filesystem/DirectoryExists.phpjfji+6phpunit/Framework/Constraint/Filesystem/FileExists.phpefeK6phpunit/Framework/Constraint/Filesystem/IsReadable.phpefe16phpunit/Framework/Constraint/Filesystem/IsWritable.phpefe+phpunit/Framework/Constraint/IsAnything.phpfE,phpunit/Framework/Constraint/IsIdentical.php f f,phpunit/Framework/Constraint/JsonMatches.phpa fa *}@phpunit/Framework/Constraint/JsonMatchesErrorMessageProvider.php5f5mһ.phpunit/Framework/Constraint/Math/IsFinite.phpfZҗ0phpunit/Framework/Constraint/Math/IsInfinite.phpf'*~+phpunit/Framework/Constraint/Math/IsNan.phpf4g09phpunit/Framework/Constraint/Object/ClassHasAttribute.phpf*?phpunit/Framework/Constraint/Object/ClassHasStaticAttribute.php*f*!%4phpunit/Framework/Constraint/Object/ObjectEquals.php -f -0W:phpunit/Framework/Constraint/Object/ObjectHasAttribute.phpf$9phpunit/Framework/Constraint/Object/ObjectHasProperty.phpf|8phpunit/Framework/Constraint/Operator/BinaryOperator.phpGfGS\4phpunit/Framework/Constraint/Operator/LogicalAnd.phpfbJ4phpunit/Framework/Constraint/Operator/LogicalNot.phpD fD e=3phpunit/Framework/Constraint/Operator/LogicalOr.phpfZ4phpunit/Framework/Constraint/Operator/LogicalXor.php$f$O2phpunit/Framework/Constraint/Operator/Operator.php&f& Dܤ7phpunit/Framework/Constraint/Operator/UnaryOperator.php3f3 m.phpunit/Framework/Constraint/String/IsJson.phpfj9phpunit/Framework/Constraint/String/RegularExpression.phpf+J6phpunit/Framework/Constraint/String/StringContains.phpfij"6phpunit/Framework/Constraint/String/StringEndsWith.phpf{Fphpunit/Framework/Constraint/String/StringMatchesFormatDescription.php -f - ՗8phpunit/Framework/Constraint/String/StringStartsWith.php&f&c>8phpunit/Framework/Constraint/Traversable/ArrayHasKey.phpfwe @phpunit/Framework/Constraint/Traversable/TraversableContains.php"f"TbKEphpunit/Framework/Constraint/Traversable/TraversableContainsEqual.phpafawAIphpunit/Framework/Constraint/Traversable/TraversableContainsIdentical.php'f'sӤDphpunit/Framework/Constraint/Traversable/TraversableContainsOnly.phpP fP /2phpunit/Framework/Constraint/Type/IsInstanceOf.phpcfc,phpunit/Framework/Constraint/Type/IsNull.phpf?),phpunit/Framework/Constraint/Type/IsType.phpfh+phpunit/Framework/DataProviderTestSuite.php;f;,;&phpunit/Framework/Error/Deprecated.phpzfzV!phpunit/Framework/Error/Error.phpmfmYg"phpunit/Framework/Error/Notice.phpvfvˤ#phpunit/Framework/Error/Warning.phpwfwG#phpunit/Framework/ErrorTestCase.phpfcȶAphpunit/Framework/Exception/ActualValueIsNotAnObjectException.phpf`B4phpunit/Framework/Exception/AssertionFailedError.phpf5phpunit/Framework/Exception/CodeCoverageException.phpf[Sphpunit/Framework/Exception/ComparisonMethodDoesNotAcceptParameterTypeException.phpkfkphpunit/Framework/MockObject/Exception/ReflectionException.phpf.ؔLphpunit/Framework/MockObject/Exception/ReturnValueNotConfiguredException.php6f6?먙;phpunit/Framework/MockObject/Exception/RuntimeException.phpf_|Mphpunit/Framework/MockObject/Exception/SoapExtensionNotAvailableException.phpfz@phpunit/Framework/MockObject/Exception/UnknownClassException.phpf5uW@phpunit/Framework/MockObject/Exception/UnknownTraitException.phpfq¥?phpunit/Framework/MockObject/Exception/UnknownTypeException.phpf~*phpunit/Framework/MockObject/Generator.php>f>l|56phpunit/Framework/MockObject/Generator/deprecation.tpl;f;O5s7phpunit/Framework/MockObject/Generator/intersection.tplLfL-X7phpunit/Framework/MockObject/Generator/mocked_class.tplfwZ8phpunit/Framework/MockObject/Generator/mocked_method.tplFfFKFphpunit/Framework/MockObject/Generator/mocked_method_never_or_void.tplfp?phpunit/Framework/MockObject/Generator/mocked_static_method.tplf 4R9phpunit/Framework/MockObject/Generator/proxied_method.tpl}f}@ėGphpunit/Framework/MockObject/Generator/proxied_method_never_or_void.tplvfvT6phpunit/Framework/MockObject/Generator/trait_class.tplQfQ<Ȥ5phpunit/Framework/MockObject/Generator/wsdl_class.tplf6phpunit/Framework/MockObject/Generator/wsdl_method.tpl<f<i+phpunit/Framework/MockObject/Invocation.phpfn2phpunit/Framework/MockObject/InvocationHandler.php:f:ˤ(phpunit/Framework/MockObject/Matcher.phpfƶ5phpunit/Framework/MockObject/MethodNameConstraint.php -f -A1|,phpunit/Framework/MockObject/MockBuilder.phpq+fq+JQ *phpunit/Framework/MockObject/MockClass.phpf'C+phpunit/Framework/MockObject/MockMethod.php&f&t.phpunit/Framework/MockObject/MockMethodSet.php8f8G\+phpunit/Framework/MockObject/MockObject.phpfbt*phpunit/Framework/MockObject/MockTrait.phpf&nä)phpunit/Framework/MockObject/MockType.phpfFFt5phpunit/Framework/MockObject/Rule/AnyInvokedCount.phpjfj`Ť3phpunit/Framework/MockObject/Rule/AnyParameters.phpf~';phpunit/Framework/MockObject/Rule/ConsecutiveParameters.phpZ fZ 035phpunit/Framework/MockObject/Rule/InvocationOrder.phpfLDӤ4phpunit/Framework/MockObject/Rule/InvokedAtIndex.php,f,kK9phpunit/Framework/MockObject/Rule/InvokedAtLeastCount.phpfB8phpunit/Framework/MockObject/Rule/InvokedAtLeastOnce.php-f- (8phpunit/Framework/MockObject/Rule/InvokedAtMostCount.phpfgY2phpunit/Framework/MockObject/Rule/InvokedCount.php f ^ 0phpunit/Framework/MockObject/Rule/MethodName.phpf[=0phpunit/Framework/MockObject/Rule/Parameters.phpVfVTu 4phpunit/Framework/MockObject/Rule/ParametersRule.phpcfc?(%phpunit/Framework/MockObject/Stub.phpfŎ6phpunit/Framework/MockObject/Stub/ConsecutiveCalls.phpfZ}hۤ/phpunit/Framework/MockObject/Stub/Exception.php,f,6¤4phpunit/Framework/MockObject/Stub/ReturnArgument.phpf?}64phpunit/Framework/MockObject/Stub/ReturnCallback.phpfD0Ӥ5phpunit/Framework/MockObject/Stub/ReturnReference.phpf -I0phpunit/Framework/MockObject/Stub/ReturnSelf.php4f4DD0phpunit/Framework/MockObject/Stub/ReturnStub.phpfA^4phpunit/Framework/MockObject/Stub/ReturnValueMap.phpfۤ*phpunit/Framework/MockObject/Stub/Stub.php3f3>++phpunit/Framework/MockObject/Verifiable.phpf̐ s!phpunit/Framework/Reorderable.phpfz0$phpunit/Framework/SelfDescribing.php -f -s!phpunit/Framework/SkippedTest.phpfS.%phpunit/Framework/SkippedTestCase.phpfj7phpunit/Framework/Test.phpf!phpunit/Framework/TestBuilder.php"f"14jphpunit/Framework/TestCase.php].f].`!phpunit/Framework/TestFailure.phpf'q"phpunit/Framework/TestListener.phprfrӪc^7phpunit/Framework/TestListenerDefaultImplementation.php'f'! phpunit/Framework/TestResult.php+f+5Ephpunit/Framework/TestSuite.phpsdfsdw%'phpunit/Framework/TestSuiteIterator.php6f6$ u%phpunit/Framework/WarningTestCase.php'f'n@ !phpunit/Runner/BaseTestRunner.php f -H)phpunit/Runner/DefaultTestResultCache.php!f!/i^phpunit/Runner/Exception.phpfzZ-phpunit/Runner/Extension/ExtensionHandler.php f Az'phpunit/Runner/Extension/PharLoader.php f =w4phpunit/Runner/Filter/ExcludeGroupFilterIterator.phpsfs} -Z!phpunit/Runner/Filter/Factory.phpfdcΤ-phpunit/Runner/Filter/GroupFilterIterator.phpf=;4phpunit/Runner/Filter/IncludeGroupFilterIterator.phprfrP;AD,phpunit/Runner/Filter/NameFilterIterator.php f T/phpunit/Runner/Hook/AfterIncompleteTestHook.php-f-zԤ)phpunit/Runner/Hook/AfterLastTestHook.phpf0B֤*phpunit/Runner/Hook/AfterRiskyTestHook.php#f#dm,phpunit/Runner/Hook/AfterSkippedTestHook.php'f':/phpunit/Runner/Hook/AfterSuccessfulTestHook.phpf5w*phpunit/Runner/Hook/AfterTestErrorHook.php#f#ݮ,phpunit/Runner/Hook/AfterTestFailureHook.php'f'2F%phpunit/Runner/Hook/AfterTestHook.phpf;gA,phpunit/Runner/Hook/AfterTestWarningHook.php'f'':+phpunit/Runner/Hook/BeforeFirstTestHook.phpfhWt&phpunit/Runner/Hook/BeforeTestHook.phpf"bphpunit/Runner/Hook/Hook.phpf. phpunit/Runner/Hook/TestHook.phpfZ_ -+phpunit/Runner/Hook/TestListenerAdapter.phpf\6E&phpunit/Runner/NullTestResultCache.phpfW<phpunit/Runner/PhptTestCase.phpVfVS 'phpunit/Runner/ResultCacheExtension.php<f<6 _*phpunit/Runner/StandardTestSuiteLoader.phpf;i Ȥ"phpunit/Runner/TestResultCache.phpfK"phpunit/Runner/TestSuiteLoader.phpfޤ"phpunit/Runner/TestSuiteSorter.php+f+~tphpunit/Runner/Version.phpf>'phpunit/TextUI/CliArguments/Builder.phpTfT3-phpunit/TextUI/CliArguments/Configuration.phpfX)phpunit/TextUI/CliArguments/Exception.phpf%zE&phpunit/TextUI/CliArguments/Mapper.php+,f+,'aphpunit/TextUI/Command.phprfr$QS'phpunit/TextUI/DefaultResultPrinter.phpe7fe78&phpunit/TextUI/Exception/Exception.phpfD{i0phpunit/TextUI/Exception/ReflectionException.phpf Y-phpunit/TextUI/Exception/RuntimeException.phpfF;phpunit/TextUI/Exception/TestDirectoryNotFoundException.phpf6phpunit/TextUI/Exception/TestFileNotFoundException.phpfpCphpunit/TextUI/Help.php1f1ؚ3Ť phpunit/TextUI/ResultPrinter.phppfpܤphpunit/TextUI/TestRunner.phpf]["phpunit/TextUI/TestSuiteMapper.php f I7iH=phpunit/TextUI/XmlConfiguration/CodeCoverage/CodeCoverage.phpfrAphpunit/TextUI/XmlConfiguration/CodeCoverage/Filter/Directory.phpfc{Kphpunit/TextUI/XmlConfiguration/CodeCoverage/Filter/DirectoryCollection.phpfju}Sphpunit/TextUI/XmlConfiguration/CodeCoverage/Filter/DirectoryCollectionIterator.phpfJ=phpunit/TextUI/XmlConfiguration/CodeCoverage/FilterMapper.phpfB>phpunit/TextUI/XmlConfiguration/CodeCoverage/Report/Clover.phpf=CAphpunit/TextUI/XmlConfiguration/CodeCoverage/Report/Cobertura.phpfi>phpunit/TextUI/XmlConfiguration/CodeCoverage/Report/Crap4j.phpfG<phpunit/TextUI/XmlConfiguration/CodeCoverage/Report/Html.phpfE6;phpunit/TextUI/XmlConfiguration/CodeCoverage/Report/Php.phpfpS<phpunit/TextUI/XmlConfiguration/CodeCoverage/Report/Text.phpfKkw;phpunit/TextUI/XmlConfiguration/CodeCoverage/Report/Xml.phpf?u1phpunit/TextUI/XmlConfiguration/Configuration.php5f5˞-phpunit/TextUI/XmlConfiguration/Exception.phpfN5+8phpunit/TextUI/XmlConfiguration/Filesystem/Directory.phpf@Bphpunit/TextUI/XmlConfiguration/Filesystem/DirectoryCollection.phpf1EqJphpunit/TextUI/XmlConfiguration/Filesystem/DirectoryCollectionIterator.phpf&3phpunit/TextUI/XmlConfiguration/Filesystem/File.phpf.P =phpunit/TextUI/XmlConfiguration/Filesystem/FileCollection.php~f~]rEphpunit/TextUI/XmlConfiguration/Filesystem/FileCollectionIterator.phpfffĤ-phpunit/TextUI/XmlConfiguration/Generator.phpfF /phpunit/TextUI/XmlConfiguration/Group/Group.phpf9phpunit/TextUI/XmlConfiguration/Group/GroupCollection.phpfyAphpunit/TextUI/XmlConfiguration/Group/GroupCollectionIterator.phpqfqY50phpunit/TextUI/XmlConfiguration/Group/Groups.phpf@I*phpunit/TextUI/XmlConfiguration/Loader.phpfE-1phpunit/TextUI/XmlConfiguration/Logging/Junit.phpfciG3phpunit/TextUI/XmlConfiguration/Logging/Logging.php f ]٤4phpunit/TextUI/XmlConfiguration/Logging/TeamCity.phpf7Z鵤8phpunit/TextUI/XmlConfiguration/Logging/TestDox/Html.phpfV2ܤ8phpunit/TextUI/XmlConfiguration/Logging/TestDox/Text.phpfώ7phpunit/TextUI/XmlConfiguration/Logging/TestDox/Xml.phpft0phpunit/TextUI/XmlConfiguration/Logging/Text.phpfCn>phpunit/TextUI/XmlConfiguration/Migration/MigrationBuilder.phpf"dGphpunit/TextUI/XmlConfiguration/Migration/MigrationBuilderException.phpfbs@phpunit/TextUI/XmlConfiguration/Migration/MigrationException.php -f -pHphpunit/TextUI/XmlConfiguration/Migration/Migrations/ConvertLogTypes.phpfhoeOphpunit/TextUI/XmlConfiguration/Migration/Migrations/CoverageCloverToReport.phpXfXijOphpunit/TextUI/XmlConfiguration/Migration/Migrations/CoverageCrap4jToReport.phpf$i'Mphpunit/TextUI/XmlConfiguration/Migration/Migrations/CoverageHtmlToReport.phpfՄjLphpunit/TextUI/XmlConfiguration/Migration/Migrations/CoveragePhpToReport.phpFfF^ӤMphpunit/TextUI/XmlConfiguration/Migration/Migrations/CoverageTextToReport.phpfV_Lphpunit/TextUI/XmlConfiguration/Migration/Migrations/CoverageXmlToReport.phpKfK_ Qphpunit/TextUI/XmlConfiguration/Migration/Migrations/IntroduceCoverageElement.phpfUMphpunit/TextUI/XmlConfiguration/Migration/Migrations/LogToReportMigration.phpfUBphpunit/TextUI/XmlConfiguration/Migration/Migrations/Migration.phpf'dphpunit/TextUI/XmlConfiguration/Migration/Migrations/MoveAttributesFromFilterWhitelistToCoverage.phpfU%5Yphpunit/TextUI/XmlConfiguration/Migration/Migrations/MoveAttributesFromRootToCoverage.phpCfCcFXphpunit/TextUI/XmlConfiguration/Migration/Migrations/MoveWhitelistExcludesToCoverage.phpfXphpunit/TextUI/XmlConfiguration/Migration/Migrations/MoveWhitelistIncludesToCoverage.phpftSphpunit/TextUI/XmlConfiguration/Migration/Migrations/RemoveCacheTokensAttribute.phpfwJphpunit/TextUI/XmlConfiguration/Migration/Migrations/RemoveEmptyFilter.php{f{KGphpunit/TextUI/XmlConfiguration/Migration/Migrations/RemoveLogTypes.phpofo3Qphpunit/TextUI/XmlConfiguration/Migration/Migrations/UpdateSchemaLocationTo93.phpfbJ6phpunit/TextUI/XmlConfiguration/Migration/Migrator.phpfo$V0phpunit/TextUI/XmlConfiguration/PHP/Constant.php7f7$Ҥ:phpunit/TextUI/XmlConfiguration/PHP/ConstantCollection.phplfl%(Bphpunit/TextUI/XmlConfiguration/PHP/ConstantCollectionIterator.phpf}=Ƥ2phpunit/TextUI/XmlConfiguration/PHP/IniSetting.phpJfJOt<phpunit/TextUI/XmlConfiguration/PHP/IniSettingCollection.phpfޛ;Dphpunit/TextUI/XmlConfiguration/PHP/IniSettingCollectionIterator.phpf/mo+phpunit/TextUI/XmlConfiguration/PHP/Php.phpf6僤2phpunit/TextUI/XmlConfiguration/PHP/PhpHandler.phpwfw` -0phpunit/TextUI/XmlConfiguration/PHP/Variable.phpfN:phpunit/TextUI/XmlConfiguration/PHP/VariableCollection.phplflsB@٤Bphpunit/TextUI/XmlConfiguration/PHP/VariableCollectionIterator.phpf!~gȤ5phpunit/TextUI/XmlConfiguration/PHPUnit/Extension.phpf}Q?phpunit/TextUI/XmlConfiguration/PHPUnit/ExtensionCollection.phpfo;RGphpunit/TextUI/XmlConfiguration/PHPUnit/ExtensionCollectionIterator.phpf|D?3phpunit/TextUI/XmlConfiguration/PHPUnit/PHPUnit.phplCflCv;phpunit/TextUI/XmlConfiguration/TestSuite/TestDirectory.phpCfC0Ephpunit/TextUI/XmlConfiguration/TestSuite/TestDirectoryCollection.phpfLCMphpunit/TextUI/XmlConfiguration/TestSuite/TestDirectoryCollectionIterator.phpfn6phpunit/TextUI/XmlConfiguration/TestSuite/TestFile.phpf?y@phpunit/TextUI/XmlConfiguration/TestSuite/TestFileCollection.phpfXHphpunit/TextUI/XmlConfiguration/TestSuite/TestFileCollectionIterator.phpzfzX17phpunit/TextUI/XmlConfiguration/TestSuite/TestSuite.phpf8wAphpunit/TextUI/XmlConfiguration/TestSuite/TestSuiteCollection.phpf/jIphpunit/TextUI/XmlConfiguration/TestSuite/TestSuiteCollectionIterator.phpf+6$phpunit/Util/Annotation/DocBlock.phpAfAp$phpunit/Util/Annotation/Registry.phpN -fN -?caphpunit/Util/Blacklist.phpfsphpunit/Util/Cloner.phpf"Ɩܤphpunit/Util/Color.phpfj?phpunit/Util/ErrorHandler.phpf=phpunit/Util/Exception.phpf다phpunit/Util/ExcludeList.phpfphpunit/Util/FileLoader.php f 'phpunit/Util/Filesystem.phpfܐphpunit/Util/Filter.php f l* phpunit/Util/GlobalState.phpfl(phpunit/Util/InvalidDataSetException.phpf,KOphpunit/Util/Json.php2 f2 Zˤphpunit/Util/Log/JUnit.phpb*fb*}3phpunit/Util/Log/TeamCity.php}&f}&t+'phpunit/Util/PHP/AbstractPhpProcess.php'f'P*&phpunit/Util/PHP/DefaultPhpProcess.phpzfzCp*phpunit/Util/PHP/Template/PhptTestCase.tplf׈j+phpunit/Util/PHP/Template/TestCaseClass.tpl f v,phpunit/Util/PHP/Template/TestCaseMethod.tpl2f2?&phpunit/Util/PHP/WindowsPhpProcess.phpfA}phpunit/Util/Printer.php f shphpunit/Util/Reflection.phpfW챤"phpunit/Util/RegularExpression.phpf0uR)phpunit/Util/Test.php^f^a*phpunit/Util/TestDox/CliTestDoxPrinter.php^*f^*W`*phpunit/Util/TestDox/HtmlResultPrinter.php f 3~ʤ'phpunit/Util/TestDox/NamePrettifier.php8"f8" -&phpunit/Util/TestDox/ResultPrinter.phpYfYXN 'phpunit/Util/TestDox/TestDoxPrinter.phpH)fH)9C*phpunit/Util/TestDox/TextResultPrinter.phpfȹ!.)phpunit/Util/TestDox/XmlResultPrinter.phpf/%phpunit/Util/TextTestListRenderer.php_f_rܯphpunit/Util/Type.phpf|ä*phpunit/Util/VersionComparisonOperator.phpfb,phpunit/Util/XdebugFilterScriptGenerator.phpwfwتphpunit/Util/Xml.phpf̤phpunit/Util/Xml/Exception.phpfӤ0phpunit/Util/Xml/FailedSchemaDetectionResult.phpf#Sphpunit/Util/Xml/Loader.php f ,?*phpunit/Util/Xml/SchemaDetectionResult.phpfV#phpunit/Util/Xml/SchemaDetector.phpwfwF^!phpunit/Util/Xml/SchemaFinder.phpf0WT%phpunit/Util/Xml/SnapshotNodeList.phpHfH ^d4phpunit/Util/Xml/SuccessfulSchemaDetectionResult.phpfffˤ%phpunit/Util/Xml/ValidationResult.phpfxv:phpunit/Util/Xml/Validator.phpfV$phpunit/Util/XmlTestListRenderer.php2 -f2 -qgפsbom.xml*2f*2UTschema/8.5.xsdBfB2A[schema/9.0.xsd4Bf4B7wschema/9.1.xsdBfBq'8schema/9.2.xsdBfBc-schema/9.3.xsdEfEqschema/9.4.xsd -Ff -FDOFIschema/9.5.xsdDFfDFs|sebastian-cli-parser/LICENSEfusebastian-cli-parser/Parser.phpf&<sebastian-cli-parser/exceptions/AmbiguousOptionException.phpJfJkK*-sebastian-cli-parser/exceptions/Exception.phpyfy>Gsebastian-cli-parser/exceptions/OptionDoesNotAllowArgumentException.phpcfcRjYJsebastian-cli-parser/exceptions/RequiredOptionArgumentMissingException.phplflzQ:sebastian-cli-parser/exceptions/UnknownOptionException.phpCfC*tP*sebastian-code-unit-reverse-lookup/LICENSEf3G (-sebastian-code-unit-reverse-lookup/Wizard.php f AJ'sebastian-code-unit/ClassMethodUnit.phpf!sebastian-code-unit/ClassUnit.php f ʰ sebastian-code-unit/CodeUnit.php%f%a*sebastian-code-unit/CodeUnitCollection.phpfKm2sebastian-code-unit/CodeUnitCollectionIterator.php?f?G9$sebastian-code-unit/FunctionUnit.phpf1+sebastian-code-unit/InterfaceMethodUnit.php#f#.R%sebastian-code-unit/InterfaceUnit.phpf6 ]ˤsebastian-code-unit/LICENSE f psebastian-code-unit/Mapper.php-f-'sebastian-code-unit/TraitMethodUnit.phpfG!sebastian-code-unit/TraitUnit.php f xV,sebastian-code-unit/exceptions/Exception.phpwfw5Ǥ;sebastian-code-unit/exceptions/InvalidCodeUnitException.phpfMvԊ3sebastian-code-unit/exceptions/NoTraitException.phpf]56sebastian-code-unit/exceptions/ReflectionException.phpfcQ(sebastian-comparator/ArrayComparator.phpyfy}פ#sebastian-comparator/Comparator.phpfo`*sebastian-comparator/ComparisonFailure.php f yP٤*sebastian-comparator/DOMNodeComparator.php$ f$ B3+sebastian-comparator/DateTimeComparator.php f -)sebastian-comparator/DoubleComparator.phpf&,sebastian-comparator/ExceptionComparator.phpf.L0 sebastian-comparator/Factory.phpf -sebastian-comparator/LICENSE f =(-sebastian-comparator/MockObjectComparator.phpf]*sebastian-comparator/NumericComparator.php7 f7 Af)sebastian-comparator/ObjectComparator.php\ f\ Fɫ+sebastian-comparator/ResourceComparator.php f WꞤ)sebastian-comparator/ScalarComparator.php3 f3 3sebastian-comparator/SplObjectStorageComparator.phpfF'sebastian-comparator/TypeComparator.phpf%Y\-sebastian-comparator/exceptions/Exception.phpzfzϤ4sebastian-comparator/exceptions/RuntimeException.phpf_#sebastian-complexity/Calculator.phpfjf٤.sebastian-complexity/Complexity/Complexity.phpUfU,#8sebastian-complexity/Complexity/ComplexityCollection.phpfU~@sebastian-complexity/Complexity/ComplexityCollectionIterator.php0f0&,sebastian-complexity/Exception/Exception.phpzfzȬˤ3sebastian-complexity/Exception/RuntimeException.phpfsebastian-complexity/LICENSEf=ݤ=sebastian-complexity/Visitor/ComplexityCalculatingVisitor.php f $8xȤGsebastian-complexity/Visitor/CyclomaticComplexityCalculatingVisitor.phpGfG)sebastian-diff/Chunk.phpcfc!rsebastian-diff/Diff.phpnfn_9sebastian-diff/Differ.php$f$3sebastian-diff/Exception/ConfigurationException.phpBfBw&sebastian-diff/Exception/Exception.phpnfn/\5sebastian-diff/Exception/InvalidArgumentException.phpf$ysebastian-diff/LICENSE f a1sebastian-diff/Line.phpPfPQF5sebastian-diff/LongestCommonSubsequenceCalculator.phpf`Dsebastian-diff/MemoryEfficientLongestCommonSubsequenceCalculator.php f N(R4sebastian-diff/Output/AbstractChunkOutputBuilder.phpfFǃ/sebastian-diff/Output/DiffOnlyOutputBuilder.phpf4sebastian-diff/Output/DiffOutputBuilderInterface.phpf=8sebastian-diff/Output/StrictUnifiedDiffOutputBuilder.php(f(O2sebastian-diff/Output/UnifiedDiffOutputBuilder.phpFfF+bsebastian-diff/Parser.php f wcBsebastian-diff/TimeEfficientLongestCommonSubsequenceCalculator.php4 f4 !sebastian-environment/Console.phpfcsebastian-environment/LICENSEfFy٤)sebastian-environment/OperatingSystem.phpfuv!sebastian-environment/Runtime.phpfŬsebastian-exporter/Exporter.php$f$csebastian-exporter/LICENSEf 5٤'sebastian-global-state/CodeExporter.php f  &sebastian-global-state/ExcludeList.php -f -Isebastian-global-state/LICENSEfJ#sebastian-global-state/Restorer.phpf+B7#sebastian-global-state/Snapshot.php*f*s{/sebastian-global-state/exceptions/Exception.php}f}ⴤ6sebastian-global-state/exceptions/RuntimeException.phpf##sebastian-lines-of-code/Counter.phpLfL9 /sebastian-lines-of-code/Exception/Exception.php~f~%>>sebastian-lines-of-code/Exception/IllogicalValuesException.phpfr<sebastian-lines-of-code/Exception/NegativeValueException.phpf&Ӥ6sebastian-lines-of-code/Exception/RuntimeException.phpf)Ϲsebastian-lines-of-code/LICENSEfbS~/sebastian-lines-of-code/LineCountingVisitor.phpfE'sebastian-lines-of-code/LinesOfCode.php f c*sebastian-object-enumerator/Enumerator.phpf9Ϥ)sebastian-object-enumerator/Exception.phpfNo8sebastian-object-enumerator/InvalidArgumentException.phpf(sebastian-object-reflector/Exception.phpf}dS7sebastian-object-reflector/InvalidArgumentException.phpf[a.sebastian-object-reflector/ObjectReflector.phpfV'sebastian-recursion-context/Context.phpFfF;wR_)sebastian-recursion-context/Exception.phpf8sebastian-recursion-context/InvalidArgumentException.phpf#sebastian-recursion-context/LICENSEfڤ%sebastian-resource-operations/LICENSEf]<4sebastian-resource-operations/ResourceOperations.php>f>,sebastian-type/LICENSE f &.sebastian-type/Parameter.phpfy#sebastian-type/ReflectionMapper.phptft=ysebastian-type/TypeName.php>f>&sebastian-type/exception/Exception.phpnfnH3-sebastian-type/exception/RuntimeException.phpf;s֤$sebastian-type/type/CallableType.php|f|KA'!sebastian-type/type/FalseType.phpfffFv)sebastian-type/type/GenericObjectType.php@f@wQ#(sebastian-type/type/IntersectionType.php -f -4$sebastian-type/type/IterableType.php"f"q!sebastian-type/type/MixedType.php+f+ v!sebastian-type/type/NeverType.phpfˤ sebastian-type/type/NullType.php&f&4"sebastian-type/type/ObjectType.phpafaeǤ"sebastian-type/type/SimpleType.phpf"sebastian-type/type/StaticType.phpf˶7 sebastian-type/type/TrueType.phpafaxsebastian-type/type/Type.phpfŵE!sebastian-type/type/UnionType.php( f( "Eä#sebastian-type/type/UnknownType.phpf4" sebastian-type/type/VoidType.phpfH sebastian-version/LICENSEfZsebastian-version/Version.phpftheseer-tokenizer/Exception.phprfrmtheseer-tokenizer/LICENSEfR ("theseer-tokenizer/NamespaceUri.phpLfLۊ9+theseer-tokenizer/NamespaceUriException.php}f}aՓtheseer-tokenizer/Token.phpfK%theseer-tokenizer/TokenCollection.phpf6.theseer-tokenizer/TokenCollectionException.phpf5ɤtheseer-tokenizer/Tokenizer.php f s'y#theseer-tokenizer/XMLSerializer.phpf#webmozart-assert/Assert.phpfc.`-webmozart-assert/InvalidArgumentException.phpfff̈́webmozart-assert/LICENSE<f<t}webmozart-assert/Mixin.php2f2a춤.phpstorm.meta.phpfO{ +)phpunit-10.5.26.phar composer.lockyfL٤ manifest.txtyf?פ'myclabs-deep-copy/DeepCopy/DeepCopy.phpyfݤ7myclabs-deep-copy/DeepCopy/Exception/CloneException.phpyfJDȤ:myclabs-deep-copy/DeepCopy/Exception/PropertyException.phpyfo#5myclabs-deep-copy/DeepCopy/Filter/ChainableFilter.phpyf=(eGmyclabs-deep-copy/DeepCopy/Filter/Doctrine/DoctrineCollectionFilter.phpyfJZELmyclabs-deep-copy/DeepCopy/Filter/Doctrine/DoctrineEmptyCollectionFilter.phpyftBmyclabs-deep-copy/DeepCopy/Filter/Doctrine/DoctrineProxyFilter.phpyffQc_,myclabs-deep-copy/DeepCopy/Filter/Filter.phphyfh߽0myclabs-deep-copy/DeepCopy/Filter/KeepFilter.phpyf7#3myclabs-deep-copy/DeepCopy/Filter/ReplaceFilter.phpyfT:3myclabs-deep-copy/DeepCopy/Filter/SetNullFilter.phpyfCkDmyclabs-deep-copy/DeepCopy/Matcher/Doctrine/DoctrineProxyMatcher.phpyf 3.myclabs-deep-copy/DeepCopy/Matcher/Matcher.phpyffä6myclabs-deep-copy/DeepCopy/Matcher/PropertyMatcher.phpyfA^:myclabs-deep-copy/DeepCopy/Matcher/PropertyNameMatcher.phpyfP:myclabs-deep-copy/DeepCopy/Matcher/PropertyTypeMatcher.php9yf9Ge잤:myclabs-deep-copy/DeepCopy/Reflection/ReflectionHelper.php9yf91Amyclabs-deep-copy/DeepCopy/TypeFilter/Date/DateIntervalFilter.phpyf[7myclabs-deep-copy/DeepCopy/TypeFilter/ReplaceFilter.phpyf8;;myclabs-deep-copy/DeepCopy/TypeFilter/ShallowCopyFilter.phpyfF_e?myclabs-deep-copy/DeepCopy/TypeFilter/Spl/ArrayObjectFilter.phpyfةAmyclabs-deep-copy/DeepCopy/TypeFilter/Spl/SplDoublyLinkedList.phpyfK픤Gmyclabs-deep-copy/DeepCopy/TypeFilter/Spl/SplDoublyLinkedListFilter.php*yf*L- 4myclabs-deep-copy/DeepCopy/TypeFilter/TypeFilter.phpyfԊ6myclabs-deep-copy/DeepCopy/TypeMatcher/TypeMatcher.phpyf +n(myclabs-deep-copy/DeepCopy/deep_copy.phpyfWȕmyclabs-deep-copy/LICENSE5yf5ʭ˄nikic-php-parser/LICENSEyf*&nikic-php-parser/PhpParser/Builder.phpyf[ṳ1nikic-php-parser/PhpParser/Builder/ClassConst.php'yf'X-nikic-php-parser/PhpParser/Builder/Class_.phpkyfkӐQ2nikic-php-parser/PhpParser/Builder/Declaration.phpyf`X:/nikic-php-parser/PhpParser/Builder/EnumCase.phpyf$,nikic-php-parser/PhpParser/Builder/Enum_.php yf Uf3nikic-php-parser/PhpParser/Builder/FunctionLike.php9yf9B0nikic-php-parser/PhpParser/Builder/Function_.phpyf33A1nikic-php-parser/PhpParser/Builder/Interface_.phph +yfh +|-nikic-php-parser/PhpParser/Builder/Method.phpyfڷt1nikic-php-parser/PhpParser/Builder/Namespace_.phpwyfwˢ,nikic-php-parser/PhpParser/Builder/Param.php>yf>$B/nikic-php-parser/PhpParser/Builder/Property.phpyf"k/nikic-php-parser/PhpParser/Builder/TraitUse.phpyf!rѤ9nikic-php-parser/PhpParser/Builder/TraitUseAdaptation.phpyf0-nikic-php-parser/PhpParser/Builder/Trait_.php4 yf4 ѬZ+nikic-php-parser/PhpParser/Builder/Use_.php,yf,K,g-nikic-php-parser/PhpParser/BuilderFactory.php(yf(Yl-nikic-php-parser/PhpParser/BuilderHelpers.php$yf$MA&nikic-php-parser/PhpParser/Comment.phpyfդ*nikic-php-parser/PhpParser/Comment/Doc.phpyf袤;nikic-php-parser/PhpParser/ConstExprEvaluationException.php}yf}O1nikic-php-parser/PhpParser/ConstExprEvaluator.php&yf&$nikic-php-parser/PhpParser/Error.php]yf]:+nikic-php-parser/PhpParser/ErrorHandler.php9yf9yo6nikic-php-parser/PhpParser/ErrorHandler/Collecting.phpyfHr(4nikic-php-parser/PhpParser/ErrorHandler/Throwing.phpyfr0nikic-php-parser/PhpParser/Internal/DiffElem.php +yf +_P.nikic-php-parser/PhpParser/Internal/Differ.phpyfd;v,Anikic-php-parser/PhpParser/Internal/PrintableNewAnonClassNode.phpe +yfe +Go5nikic-php-parser/PhpParser/Internal/TokenPolyfill.php$yf$^6"3nikic-php-parser/PhpParser/Internal/TokenStream.phpP"yfP"kΤ*nikic-php-parser/PhpParser/JsonDecoder.php yf afZ$nikic-php-parser/PhpParser/Lexer.php(yf(&.nikic-php-parser/PhpParser/Lexer/Emulative.php yf OEDnikic-php-parser/PhpParser/Lexer/TokenEmulator/AttributeEmulator.phpyf g$Dnikic-php-parser/PhpParser/Lexer/TokenEmulator/EnumTokenEmulator.phpyfHnikic-php-parser/PhpParser/Lexer/TokenEmulator/ExplicitOctalEmulator.phpyfHUjBnikic-php-parser/PhpParser/Lexer/TokenEmulator/KeywordEmulator.phpyf)ÍEnikic-php-parser/PhpParser/Lexer/TokenEmulator/MatchTokenEmulator.phpyf9GHnikic-php-parser/PhpParser/Lexer/TokenEmulator/NullsafeTokenEmulator.php-yf-[SPnikic-php-parser/PhpParser/Lexer/TokenEmulator/ReadonlyFunctionTokenEmulator.phpyfPΤHnikic-php-parser/PhpParser/Lexer/TokenEmulator/ReadonlyTokenEmulator.phpdyfd/CBnikic-php-parser/PhpParser/Lexer/TokenEmulator/ReverseEmulator.phpyf٤@nikic-php-parser/PhpParser/Lexer/TokenEmulator/TokenEmulator.phpWyfW3(nikic-php-parser/PhpParser/Modifiers.phpyf9m&Τ*nikic-php-parser/PhpParser/NameContext.php&yf&Ҙ#nikic-php-parser/PhpParser/Node.phpyfhm'nikic-php-parser/PhpParser/Node/Arg.php yf #E-nikic-php-parser/PhpParser/Node/ArrayItem.phpyfݤ-nikic-php-parser/PhpParser/Node/Attribute.php`yf`v¤2nikic-php-parser/PhpParser/Node/AttributeGroup.phpyfGiʤ.nikic-php-parser/PhpParser/Node/ClosureUse.phpyf\jt/nikic-php-parser/PhpParser/Node/ComplexType.php[yf[0Us*nikic-php-parser/PhpParser/Node/Const_.phpyfT=y/nikic-php-parser/PhpParser/Node/DeclareItem.php yf o#(nikic-php-parser/PhpParser/Node/Expr.phpyf|)6nikic-php-parser/PhpParser/Node/Expr/ArrayDimFetch.phpWyfWS!2nikic-php-parser/PhpParser/Node/Expr/ArrayItem.php`yf`t<&]/nikic-php-parser/PhpParser/Node/Expr/Array_.phpryfr§sG6nikic-php-parser/PhpParser/Node/Expr/ArrowFunction.php> +yf> +M/nikic-php-parser/PhpParser/Node/Expr/Assign.php'yf'0) +1nikic-php-parser/PhpParser/Node/Expr/AssignOp.phpyfb/<nikic-php-parser/PhpParser/Node/Expr/AssignOp/BitwiseAnd.phpyf?Q;nikic-php-parser/PhpParser/Node/Expr/AssignOp/BitwiseOr.phpyf)<nikic-php-parser/PhpParser/Node/Expr/AssignOp/BitwiseXor.phpyf&T:nikic-php-parser/PhpParser/Node/Expr/AssignOp/Coalesce.phpyf98nikic-php-parser/PhpParser/Node/Expr/AssignOp/Concat.phpyfG35nikic-php-parser/PhpParser/Node/Expr/AssignOp/Div.phpyf/7nikic-php-parser/PhpParser/Node/Expr/AssignOp/Minus.phpyfc5nikic-php-parser/PhpParser/Node/Expr/AssignOp/Mod.phpyfj5nikic-php-parser/PhpParser/Node/Expr/AssignOp/Mul.phpyfY:;6nikic-php-parser/PhpParser/Node/Expr/AssignOp/Plus.phpyfK]5nikic-php-parser/PhpParser/Node/Expr/AssignOp/Pow.phpyfߊA;nikic-php-parser/PhpParser/Node/Expr/AssignOp/ShiftLeft.phpyf(?<nikic-php-parser/PhpParser/Node/Expr/AssignOp/ShiftRight.phpyf2nikic-php-parser/PhpParser/Node/Expr/AssignRef.phpXyfX[1nikic-php-parser/PhpParser/Node/Expr/BinaryOp.phpdyfd`<nikic-php-parser/PhpParser/Node/Expr/BinaryOp/BitwiseAnd.phpVyfVNVD;nikic-php-parser/PhpParser/Node/Expr/BinaryOp/BitwiseOr.phpTyfT<nikic-php-parser/PhpParser/Node/Expr/BinaryOp/BitwiseXor.phpVyfV3$<nikic-php-parser/PhpParser/Node/Expr/BinaryOp/BooleanAnd.phpWyfW;nikic-php-parser/PhpParser/Node/Expr/BinaryOp/BooleanOr.phpUyfUG:nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Coalesce.phpSyfS/8nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Concat.phpNyfN5nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Div.phpHyfHA+7nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Equal.phpMyfM$39nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Greater.phpPyfPX@nikic-php-parser/PhpParser/Node/Expr/BinaryOp/GreaterOrEqual.php_yf_ ;nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Identical.phpVyfV<nikic-php-parser/PhpParser/Node/Expr/BinaryOp/LogicalAnd.phpXyfXF=;nikic-php-parser/PhpParser/Node/Expr/BinaryOp/LogicalOr.phpUyfU-3<nikic-php-parser/PhpParser/Node/Expr/BinaryOp/LogicalXor.phpXyfX7nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Minus.phpLyfL"75nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Mod.phpHyfH + +5nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Mul.phpHyfH t:nikic-php-parser/PhpParser/Node/Expr/BinaryOp/NotEqual.phpSyfSͤ>nikic-php-parser/PhpParser/Node/Expr/BinaryOp/NotIdentical.php\yf\c_6nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Plus.phpJyfJcm¤5nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Pow.phpIyfI,;nikic-php-parser/PhpParser/Node/Expr/BinaryOp/ShiftLeft.phpUyfUCXe<nikic-php-parser/PhpParser/Node/Expr/BinaryOp/ShiftRight.phpWyfW;9nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Smaller.phpPyfPT@nikic-php-parser/PhpParser/Node/Expr/BinaryOp/SmallerOrEqual.php_yf_J;nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Spaceship.phpVyfVEx3nikic-php-parser/PhpParser/Node/Expr/BitwiseNot.phpyf}lMˤ3nikic-php-parser/PhpParser/Node/Expr/BooleanNot.phpyfs7ޤ1nikic-php-parser/PhpParser/Node/Expr/CallLike.phpyf#-nikic-php-parser/PhpParser/Node/Expr/Cast.phpUyfU4nikic-php-parser/PhpParser/Node/Expr/Cast/Array_.phpyfȯ3nikic-php-parser/PhpParser/Node/Expr/Cast/Bool_.phpyf>4nikic-php-parser/PhpParser/Node/Expr/Cast/Double.phpyfS(ۤ2nikic-php-parser/PhpParser/Node/Expr/Cast/Int_.phpyf,͜5nikic-php-parser/PhpParser/Node/Expr/Cast/Object_.phpyf\5nikic-php-parser/PhpParser/Node/Expr/Cast/String_.phpyf*u4nikic-php-parser/PhpParser/Node/Expr/Cast/Unset_.phpyfɔԤ8nikic-php-parser/PhpParser/Node/Expr/ClassConstFetch.phpyf2/nikic-php-parser/PhpParser/Node/Expr/Clone_.phpyf0nikic-php-parser/PhpParser/Node/Expr/Closure.phpj yfj I3nikic-php-parser/PhpParser/Node/Expr/ClosureUse.phpayfa3nikic-php-parser/PhpParser/Node/Expr/ConstFetch.phpyfs>6/nikic-php-parser/PhpParser/Node/Expr/Empty_.phpyfݤ.nikic-php-parser/PhpParser/Node/Expr/Error.phpyf$L6nikic-php-parser/PhpParser/Node/Expr/ErrorSuppress.phpyf &.nikic-php-parser/PhpParser/Node/Expr/Eval_.phpyfA[.nikic-php-parser/PhpParser/Node/Expr/Exit_.phpyfg1nikic-php-parser/PhpParser/Node/Expr/FuncCall.phpyf@"]1nikic-php-parser/PhpParser/Node/Expr/Include_.phpyf 4nikic-php-parser/PhpParser/Node/Expr/Instanceof_.phpyf.B/nikic-php-parser/PhpParser/Node/Expr/Isset_.phpyf>:.nikic-php-parser/PhpParser/Node/Expr/List_.phpyf2)/nikic-php-parser/PhpParser/Node/Expr/Match_.php;yf;h3nikic-php-parser/PhpParser/Node/Expr/MethodCall.phpQyfQOD-nikic-php-parser/PhpParser/Node/Expr/New_.phpyf<~h;nikic-php-parser/PhpParser/Node/Expr/NullsafeMethodCall.phphyfhk[S>nikic-php-parser/PhpParser/Node/Expr/NullsafePropertyFetch.phpyfk*0nikic-php-parser/PhpParser/Node/Expr/PostDec.phpyfPh@0nikic-php-parser/PhpParser/Node/Expr/PostInc.phpyfd/nikic-php-parser/PhpParser/Node/Expr/PreDec.phpyfmB'/nikic-php-parser/PhpParser/Node/Expr/PreInc.phpyfԷ$x/nikic-php-parser/PhpParser/Node/Expr/Print_.phpyfU6nikic-php-parser/PhpParser/Node/Expr/PropertyFetch.phpyf:%g2nikic-php-parser/PhpParser/Node/Expr/ShellExec.phpHyfHed3nikic-php-parser/PhpParser/Node/Expr/StaticCall.php\yf\P<nikic-php-parser/PhpParser/Node/Expr/StaticPropertyFetch.php;yf;. 0nikic-php-parser/PhpParser/Node/Expr/Ternary.phpyf:X(/nikic-php-parser/PhpParser/Node/Expr/Throw_.phpyf#63nikic-php-parser/PhpParser/Node/Expr/UnaryMinus.phpyf*C2nikic-php-parser/PhpParser/Node/Expr/UnaryPlus.phpyfF!Đ1nikic-php-parser/PhpParser/Node/Expr/Variable.phpyfEk2nikic-php-parser/PhpParser/Node/Expr/YieldFrom.phpyfB /nikic-php-parser/PhpParser/Node/Expr/Yield_.phpoyfoƴ0nikic-php-parser/PhpParser/Node/FunctionLike.phpyfj.nikic-php-parser/PhpParser/Node/Identifier.phpAyfA5:nikic-php-parser/PhpParser/Node/InterpolatedStringPart.phpryfrkGn4nikic-php-parser/PhpParser/Node/IntersectionType.phpyfu,nikic-php-parser/PhpParser/Node/MatchArm.phpyf'b?(nikic-php-parser/PhpParser/Node/Name.php!yf!Јp7nikic-php-parser/PhpParser/Node/Name/FullyQualified.phpyf21nikic-php-parser/PhpParser/Node/Name/Relative.phpyf8V0nikic-php-parser/PhpParser/Node/NullableType.phpyf)nikic-php-parser/PhpParser/Node/Param.phpM +yfM +,%x0nikic-php-parser/PhpParser/Node/PropertyItem.php\yf\`*nikic-php-parser/PhpParser/Node/Scalar.phpoyfo=2nikic-php-parser/PhpParser/Node/Scalar/DNumber.phpZyfZW3nikic-php-parser/PhpParser/Node/Scalar/Encapsed.phpfyff=nikic-php-parser/PhpParser/Node/Scalar/EncapsedStringPart.phpmyfm8I懤1nikic-php-parser/PhpParser/Node/Scalar/Float_.phpWyfWjƤ/nikic-php-parser/PhpParser/Node/Scalar/Int_.php yf =nikic-php-parser/PhpParser/Node/Scalar/InterpolatedString.phpyf? Z2nikic-php-parser/PhpParser/Node/Scalar/LNumber.phpXyfX5nikic-php-parser/PhpParser/Node/Scalar/MagicConst.phpxyfxϮ<nikic-php-parser/PhpParser/Node/Scalar/MagicConst/Class_.phpZyfZ59nikic-php-parser/PhpParser/Node/Scalar/MagicConst/Dir.phpSyfSrfɤ:nikic-php-parser/PhpParser/Node/Scalar/MagicConst/File.phpVyfV6Q?nikic-php-parser/PhpParser/Node/Scalar/MagicConst/Function_.phpcyfc5:nikic-php-parser/PhpParser/Node/Scalar/MagicConst/Line.phpVyfVDE<nikic-php-parser/PhpParser/Node/Scalar/MagicConst/Method.php\yf\2 N@nikic-php-parser/PhpParser/Node/Scalar/MagicConst/Namespace_.phpfyffq<nikic-php-parser/PhpParser/Node/Scalar/MagicConst/Trait_.phpZyfZݤ2nikic-php-parser/PhpParser/Node/Scalar/String_.phpyfzQ-nikic-php-parser/PhpParser/Node/StaticVar.phpyfT>(nikic-php-parser/PhpParser/Node/Stmt.phpyfy.nikic-php-parser/PhpParser/Node/Stmt/Block.phpyfj/nikic-php-parser/PhpParser/Node/Stmt/Break_.phpyfmj.nikic-php-parser/PhpParser/Node/Stmt/Case_.phpyfݸO//nikic-php-parser/PhpParser/Node/Stmt/Catch_.phpyyfy{^}3nikic-php-parser/PhpParser/Node/Stmt/ClassConst.phpSyfS/꾬2nikic-php-parser/PhpParser/Node/Stmt/ClassLike.php yf 3q4nikic-php-parser/PhpParser/Node/Stmt/ClassMethod.phpyf #c/nikic-php-parser/PhpParser/Node/Stmt/Class_.php yf 1?/nikic-php-parser/PhpParser/Node/Stmt/Const_.phpyf>92nikic-php-parser/PhpParser/Node/Stmt/Continue_.phpyf򥀤7nikic-php-parser/PhpParser/Node/Stmt/DeclareDeclare.phpbyfb[Ѥ1nikic-php-parser/PhpParser/Node/Stmt/Declare_.phpyf5`,nikic-php-parser/PhpParser/Node/Stmt/Do_.phpTyfT%0.nikic-php-parser/PhpParser/Node/Stmt/Echo_.phpyf`)0nikic-php-parser/PhpParser/Node/Stmt/ElseIf_.php[yf[bEѤ.nikic-php-parser/PhpParser/Node/Stmt/Else_.phpyf&1nikic-php-parser/PhpParser/Node/Stmt/EnumCase.phpyfw4m.nikic-php-parser/PhpParser/Node/Stmt/Enum_.phpJyfJ?3nikic-php-parser/PhpParser/Node/Stmt/Expression.phpyf 1nikic-php-parser/PhpParser/Node/Stmt/Finally_.phpyf8-nikic-php-parser/PhpParser/Node/Stmt/For_.phpyf+1nikic-php-parser/PhpParser/Node/Stmt/Foreach_.phpyf0פ2nikic-php-parser/PhpParser/Node/Stmt/Function_.php +yf +f*키0nikic-php-parser/PhpParser/Node/Stmt/Global_.phpyfPo6.nikic-php-parser/PhpParser/Node/Stmt/Goto_.php"yf"X 1nikic-php-parser/PhpParser/Node/Stmt/GroupUse.php^yf^7Ѥ5nikic-php-parser/PhpParser/Node/Stmt/HaltCompiler.php!yf!y,nikic-php-parser/PhpParser/Node/Stmt/If_.phpyf4o3nikic-php-parser/PhpParser/Node/Stmt/InlineHTML.phpyfoȇ3nikic-php-parser/PhpParser/Node/Stmt/Interface_.phpNyfNyƤ.nikic-php-parser/PhpParser/Node/Stmt/Label.phpyfJ3nikic-php-parser/PhpParser/Node/Stmt/Namespace_.phpyfSvEj,nikic-php-parser/PhpParser/Node/Stmt/Nop.phpFyfF$6ؤ1nikic-php-parser/PhpParser/Node/Stmt/Property.phpv yfv (q9nikic-php-parser/PhpParser/Node/Stmt/PropertyProperty.phpcyfcݦ0nikic-php-parser/PhpParser/Node/Stmt/Return_.phpyf|M2nikic-php-parser/PhpParser/Node/Stmt/StaticVar.php`yf`dTv0nikic-php-parser/PhpParser/Node/Stmt/Static_.phpyfٜޤ0nikic-php-parser/PhpParser/Node/Stmt/Switch_.phpIyfIi1nikic-php-parser/PhpParser/Node/Stmt/TraitUse.phpyf?\;nikic-php-parser/PhpParser/Node/Stmt/TraitUseAdaptation.php=yf={:Anikic-php-parser/PhpParser/Node/Stmt/TraitUseAdaptation/Alias.php1yf1b ֤Fnikic-php-parser/PhpParser/Node/Stmt/TraitUseAdaptation/Precedence.php>yf> /nikic-php-parser/PhpParser/Node/Stmt/Trait_.phpQyfQEQ1nikic-php-parser/PhpParser/Node/Stmt/TryCatch.php6yf62./nikic-php-parser/PhpParser/Node/Stmt/Unset_.phpyf:Vɤ/nikic-php-parser/PhpParser/Node/Stmt/UseUse.php^yf^ˋQ-nikic-php-parser/PhpParser/Node/Stmt/Use_.phpyf |/nikic-php-parser/PhpParser/Node/Stmt/While_.phpWyfWQ/פ-nikic-php-parser/PhpParser/Node/UnionType.phpyfH+nikic-php-parser/PhpParser/Node/UseItem.phpyf73U5nikic-php-parser/PhpParser/Node/VarLikeIdentifier.phpyfy.7nikic-php-parser/PhpParser/Node/VariadicPlaceholder.phpyfmW+nikic-php-parser/PhpParser/NodeAbstract.php7yf7i)nikic-php-parser/PhpParser/NodeDumper.php&yf&,))nikic-php-parser/PhpParser/NodeFinder.phpW +yfW +^,nikic-php-parser/PhpParser/NodeTraverser.php'yf''G[5nikic-php-parser/PhpParser/NodeTraverserInterface.phpayfa*nikic-php-parser/PhpParser/NodeVisitor.phpXyfXp}9nikic-php-parser/PhpParser/NodeVisitor/CloningVisitor.phpyf"ۤCnikic-php-parser/PhpParser/NodeVisitor/CommentAnnotatingVisitor.php +yf +^9nikic-php-parser/PhpParser/NodeVisitor/FindingVisitor.phpyfQۤ>nikic-php-parser/PhpParser/NodeVisitor/FirstFindingVisitor.phpyf~Ĭ*7nikic-php-parser/PhpParser/NodeVisitor/NameResolver.php&yf&@nikic-php-parser/PhpParser/NodeVisitor/NodeConnectingVisitor.phpyfg-oBnikic-php-parser/PhpParser/NodeVisitor/ParentConnectingVisitor.phpyfA2nikic-php-parser/PhpParser/NodeVisitorAbstract.phpyf~%nikic-php-parser/PhpParser/Parser.php yf 0@*nikic-php-parser/PhpParser/Parser/Php7.phpnhyfnh_ +c*nikic-php-parser/PhpParser/Parser/Php8.phpxgyfxgT -nikic-php-parser/PhpParser/ParserAbstract.phpyfl,nikic-php-parser/PhpParser/ParserFactory.phpyf(c)nikic-php-parser/PhpParser/PhpVersion.phpyfNy,nikic-php-parser/PhpParser/PrettyPrinter.phpyf>5nikic-php-parser/PhpParser/PrettyPrinter/Standard.phpuyfuDŽ 4nikic-php-parser/PhpParser/PrettyPrinterAbstract.phpyfUW$nikic-php-parser/PhpParser/Token.phpyf ̤3nikic-php-parser/PhpParser/compatibility_tokens.php^yf^Nüobject-enumerator/LICENSEyffobject-reflector/LICENSEyfR6phar-io-manifest/LICENSE`yf`p+phar-io-manifest/ManifestDocumentMapper.phpyf^D#phar-io-manifest/ManifestLoader.phpyfXj'phar-io-manifest/ManifestSerializer.phpyf?j':phar-io-manifest/exceptions/ElementCollectionException.phpyfIn)phar-io-manifest/exceptions/Exception.phpyfֽН?phar-io-manifest/exceptions/InvalidApplicationNameException.php<yf<W5phar-io-manifest/exceptions/InvalidEmailException.phpyf)Ϫ}3phar-io-manifest/exceptions/InvalidUrlException.php yf x᳤9phar-io-manifest/exceptions/ManifestDocumentException.phpyf/"@phar-io-manifest/exceptions/ManifestDocumentLoadingException.php~yf~H}?phar-io-manifest/exceptions/ManifestDocumentMapperException.phpyfJR18phar-io-manifest/exceptions/ManifestElementException.phpyf̏7phar-io-manifest/exceptions/ManifestLoaderException.phpyfl +7phar-io-manifest/exceptions/NoEmailAddressException.phpyf'phar-io-manifest/values/Application.php yf ;k+phar-io-manifest/values/ApplicationName.phpyf"phar-io-manifest/values/Author.phpyfx,phar-io-manifest/values/AuthorCollection.php-yf-4phar-io-manifest/values/AuthorCollectionIterator.phpyfЪe,phar-io-manifest/values/BundledComponent.phpdyfd76phar-io-manifest/values/BundledComponentCollection.phpyfߤ>phar-io-manifest/values/BundledComponentCollectionIterator.phpyf _0phar-io-manifest/values/CopyrightInformation.phppyfpP!phar-io-manifest/values/Email.phpyfS%phar-io-manifest/values/Extension.phpyfF {#phar-io-manifest/values/Library.phpyfv#phar-io-manifest/values/License.phpyf4$phar-io-manifest/values/Manifest.php& +yf& +3phar-io-manifest/values/PhpExtensionRequirement.phpyfPη1phar-io-manifest/values/PhpVersionRequirement.phpAyfAi'phar-io-manifest/values/Requirement.phpyf U1phar-io-manifest/values/RequirementCollection.phpsyfs6M9phar-io-manifest/values/RequirementCollectionIterator.phpyfU phar-io-manifest/values/Type.phpyfܲ3phar-io-manifest/values/Url.phpyfO&phar-io-manifest/xml/AuthorElement.phpyfڤ0phar-io-manifest/xml/AuthorElementCollection.phpMyfMj'phar-io-manifest/xml/BundlesElement.phptyft]Y)phar-io-manifest/xml/ComponentElement.phpyfna3phar-io-manifest/xml/ComponentElementCollection.phpVyfV?(phar-io-manifest/xml/ContainsElement.phpyfl8)phar-io-manifest/xml/CopyrightElement.phpyfhDp*phar-io-manifest/xml/ElementCollection.phpyf<^ޤ#phar-io-manifest/xml/ExtElement.php*yf*^פ-phar-io-manifest/xml/ExtElementCollection.phpDyfDβS)phar-io-manifest/xml/ExtensionElement.phpyf J'phar-io-manifest/xml/LicenseElement.phpyfv/!)phar-io-manifest/xml/ManifestDocument.php yf i_(phar-io-manifest/xml/ManifestElement.phpyf#=#phar-io-manifest/xml/PhpElement.phpyfY(phar-io-manifest/xml/RequiresElement.phpEyfEdwʤ!phar-io-version/BuildMetaData.phpyf3A(*phar-io-version/LICENSE&yf&Ҫ $phar-io-version/PreReleaseSuffix.phpyf8^phar-io-version/Version.phpyf+phar-io-version/VersionConstraintParser.phpN yfN n%ˤ*phar-io-version/VersionConstraintValue.phpE +yfE +qDF!phar-io-version/VersionNumber.phpyfKp_9phar-io-version/constraints/AbstractVersionConstraint.phpyf42o9phar-io-version/constraints/AndVersionConstraintGroup.phpyfkO4phar-io-version/constraints/AnyVersionConstraint.phpTyfTv6phar-io-version/constraints/ExactVersionConstraint.phpyfgqEphar-io-version/constraints/GreaterThanOrEqualToVersionConstraint.phpyf_8phar-io-version/constraints/OrVersionConstraintGroup.phpyf6Fphar-io-version/constraints/SpecificMajorAndMinorVersionConstraint.phpyfB,>phar-io-version/constraints/SpecificMajorVersionConstraint.php yf 1phar-io-version/constraints/VersionConstraint.phpyfd(phar-io-version/exceptions/Exception.phpyf<?phar-io-version/exceptions/InvalidPreReleaseSuffixException.phpyf[6phar-io-version/exceptions/InvalidVersionException.phpyfy7phar-io-version/exceptions/NoBuildMetaDataException.phpyf+${:phar-io-version/exceptions/NoPreReleaseSuffixException.phpyf"Dphar-io-version/exceptions/UnsupportedVersionConstraintException.phpyf樤"php-code-coverage/CodeCoverage.php%Ayf%AY94php-code-coverage/Data/ProcessedCodeCoverageData.phpZ'yfZ'.Jj.php-code-coverage/Data/RawCodeCoverageData.php#yf#I#php-code-coverage/Driver/Driver.php yf Y֤'php-code-coverage/Driver/PcovDriver.phpyf= %php-code-coverage/Driver/Selector.phpCyfC;I)php-code-coverage/Driver/XdebugDriver.phpyfdJphp-code-coverage/Exception/BranchAndPathCoverageNotSupportedException.phpyfzFphp-code-coverage/Exception/DeadCodeDetectionNotSupportedException.phpyfoICphp-code-coverage/Exception/DirectoryCouldNotBeCreatedException.phpyf<6')php-code-coverage/Exception/Exception.phpyf+Q>php-code-coverage/Exception/FileCouldNotBeWrittenException.phpyf8php-code-coverage/Exception/InvalidArgumentException.phpyfl~ФFphp-code-coverage/Exception/NoCodeCoverageDriverAvailableException.php3yf35oYC]php-code-coverage/Exception/NoCodeCoverageDriverWithPathCoverageSupportAvailableException.phpeyfeX3/php-code-coverage/Exception/ParserException.phpyfpڤDphp-code-coverage/Exception/PathExistsButIsNotDirectoryException.phpyfikd9php-code-coverage/Exception/PcovNotAvailableException.phpiyfiq3php-code-coverage/Exception/ReflectionException.phpyf`?php-code-coverage/Exception/ReportAlreadyFinalizedException.php>yf>mU=Iphp-code-coverage/Exception/StaticAnalysisCacheNotConfiguredException.phpyfp6php-code-coverage/Exception/TestIdMissingException.phpyf3OCphp-code-coverage/Exception/UnintentionallyCoveredCodeException.phpyf{:=php-code-coverage/Exception/WriteOperationFailedException.phpyfu䶤;php-code-coverage/Exception/XdebugNotAvailableException.phpmyfm{F9php-code-coverage/Exception/XdebugNotEnabledException.phpyfJ]0,php-code-coverage/Exception/XmlException.phpyf0)Rphp-code-coverage/Filter.phpb yfb 7bphp-code-coverage/LICENSEyf>R'php-code-coverage/Node/AbstractNode.phpyf1*3"php-code-coverage/Node/Builder.php[yf[\:$php-code-coverage/Node/CrapIndex.phpyf$php-code-coverage/Node/Directory.php1$yf1$uaphp-code-coverage/Node/File.phpDWyfDWv #php-code-coverage/Node/Iterator.phpuyfuB0#php-code-coverage/Report/Clover.phpJ(yfJ(php-code-coverage/Report/Html/Renderer/Template/css/custom.cssyfAphp-code-coverage/Report/Html/Renderer/Template/css/nv.d3.min.cssX%yfX%0,@php-code-coverage/Report/Html/Renderer/Template/css/octicons.cssXyfX'#=php-code-coverage/Report/Html/Renderer/Template/css/style.cssH +yfH +BѺCphp-code-coverage/Report/Html/Renderer/Template/dashboard.html.distyfDJphp-code-coverage/Report/Html/Renderer/Template/dashboard_branch.html.distyfDCphp-code-coverage/Report/Html/Renderer/Template/directory.html.distyfՆJphp-code-coverage/Report/Html/Renderer/Template/directory_branch.html.distyfn2]Hphp-code-coverage/Report/Html/Renderer/Template/directory_item.html.distAyfAdsOphp-code-coverage/Report/Html/Renderer/Template/directory_item_branch.html.dist;yf;mۤ>php-code-coverage/Report/Html/Renderer/Template/file.html.distP yfP j*Ephp-code-coverage/Report/Html/Renderer/Template/file_branch.html.dist yf ㉞Cphp-code-coverage/Report/Html/Renderer/Template/file_item.html.distryfr/yJphp-code-coverage/Report/Html/Renderer/Template/file_item_branch.html.distlyfl-Cphp-code-coverage/Report/Html/Renderer/Template/icons/file-code.svg0yf0QUUHphp-code-coverage/Report/Html/Renderer/Template/icons/file-directory.svgyfZCphp-code-coverage/Report/Html/Renderer/Template/js/bootstrap.min.jscyfc"#<php-code-coverage/Report/Html/Renderer/Template/js/d3.min.jsPyfPhb:php-code-coverage/Report/Html/Renderer/Template/js/file.jsyfb䆤@php-code-coverage/Report/Html/Renderer/Template/js/jquery.min.js@^yf@^ ?php-code-coverage/Report/Html/Renderer/Template/js/nv.d3.min.jsRyfRphp-code-coverage/Report/Html/Renderer/Template/line.html.distyf{?php-code-coverage/Report/Html/Renderer/Template/lines.html.disteyfedf Ephp-code-coverage/Report/Html/Renderer/Template/method_item.html.distyfjפLphp-code-coverage/Report/Html/Renderer/Template/method_item_branch.html.distyfyĎk?php-code-coverage/Report/Html/Renderer/Template/paths.html.distyf*'ݤ php-code-coverage/Report/PHP.php'yf';!php-code-coverage/Report/Text.php&yf&+Ҥ'php-code-coverage/Report/Thresholds.phpWyfW&a1php-code-coverage/Report/Xml/BuildInformation.phpyfX5)php-code-coverage/Report/Xml/Coverage.phpyf5 ,*php-code-coverage/Report/Xml/Directory.phpyf Fn'php-code-coverage/Report/Xml/Facade.php!yf!X%php-code-coverage/Report/Xml/File.phpyfc'php-code-coverage/Report/Xml/Method.phpCyfCGV%php-code-coverage/Report/Xml/Node.phpyf f!>(php-code-coverage/Report/Xml/Project.phpdyfd'php-code-coverage/Report/Xml/Report.php yf d'php-code-coverage/Report/Xml/Source.phpyf;&php-code-coverage/Report/Xml/Tests.phpyfzH)פ'php-code-coverage/Report/Xml/Totals.phpyf'%php-code-coverage/Report/Xml/Unit.phpyf` p0php-code-coverage/StaticAnalysis/CacheWarmer.phpgyfg][78php-code-coverage/StaticAnalysis/CachingFileAnalyser.phpyf`D{;php-code-coverage/StaticAnalysis/CodeUnitFindingVisitor.php%yf%$Bphp-code-coverage/StaticAnalysis/ExecutableLinesFindingVisitor.phpe-yfe- B1php-code-coverage/StaticAnalysis/FileAnalyser.phpyf?4?php-code-coverage/StaticAnalysis/IgnoredLinesFindingVisitor.php9yf9I 68php-code-coverage/StaticAnalysis/ParsingFileAnalyser.phpyf$php-code-coverage/TestSize/Known.phpZyfZG$php-code-coverage/TestSize/Large.phpyfmr%php-code-coverage/TestSize/Medium.phpyf8$php-code-coverage/TestSize/Small.phpyf/H'php-code-coverage/TestSize/TestSize.phpyf87Ť&php-code-coverage/TestSize/Unknown.phpkyfk?!ޤ(php-code-coverage/TestStatus/Failure.phpjyfjZ&php-code-coverage/TestStatus/Known.phpyf>(php-code-coverage/TestStatus/Success.phpjyfjՑ++php-code-coverage/TestStatus/TestStatus.phpyfNxQ(php-code-coverage/TestStatus/Unknown.phpoyfog%php-code-coverage/Util/Filesystem.phpyf(У|%php-code-coverage/Util/Percentage.php^yf^&Bphp-code-coverage/Version.phpyf%php-file-iterator/ExcludeIterator.php.yf.php-file-iterator/Facade.phpyf'hphp-file-iterator/Factory.php +yf +uwphp-file-iterator/Iterator.php yf $"vphp-file-iterator/LICENSEyf-~y֤php-invoker/Invoker.phpyflx5$php-invoker/exceptions/Exception.phpvyfv'P=Dphp-invoker/exceptions/ProcessControlExtensionNotLoadedException.phpyfӤ+php-invoker/exceptions/TimeoutException.phpyftJphp-text-template/LICENSEyf-~y֤php-text-template/Template.phpV yfV %X*php-text-template/exceptions/Exception.php}yf}`"9php-text-template/exceptions/InvalidArgumentException.phpyf¤1php-text-template/exceptions/RuntimeException.phpyf]Mpphp-timer/Duration.php yf ߤphp-timer/LICENSEyf$php-timer/ResourceUsageFormatter.phpyf:$php-timer/Timer.phpyf"php-timer/exceptions/Exception.phpryfr</php-timer/exceptions/NoActiveTimerException.phpyf*Ephp-timer/exceptions/TimeSinceStartOfRequestNotAvailableException.phpyf. phpunit.xsdGFyfGF1&`1phpunit/Event/Dispatcher/CollectingDispatcher.phpyf=200phpunit/Event/Dispatcher/DeferringDispatcher.php6yf66 -phpunit/Event/Dispatcher/DirectDispatcher.phpT yfT ʉ'phpunit/Event/Dispatcher/Dispatcher.phpyfWӤ3phpunit/Event/Dispatcher/SubscribableDispatcher.phpyfE,phpunit/Event/Emitter/DispatchingEmitter.phpOryfOrz!phpunit/Event/Emitter/Emitter.php *yf *.е-phpunit/Event/Events/Application/Finished.phpyf +7phpunit/Event/Events/Application/FinishedSubscriber.php6yf6),phpunit/Event/Events/Application/Started.phpyfi6phpunit/Event/Events/Application/StartedSubscriber.php4yf4J'phpunit/Event/Events/Event.php +yf +췠(phpunit/Event/Events/EventCollection.phpIyfI +Ȥ0phpunit/Event/Events/EventCollectionIterator.phpyfx7phpunit/Event/Events/Test/Assertion/AssertionFailed.phpyf5Aphpunit/Event/Events/Test/Assertion/AssertionFailedSubscriber.phpHyfHV<:phpunit/Event/Events/Test/Assertion/AssertionSucceeded.phpyf̓ФDphpunit/Event/Events/Test/Assertion/AssertionSucceededSubscriber.phpNyfNc2phpunit/Event/Events/Test/ComparatorRegistered.php +yf +Za<phpunit/Event/Events/Test/ComparatorRegisteredSubscriber.php@yf@ڭBphpunit/Event/Events/Test/HookMethod/AfterLastTestMethodCalled.phpiyfidLphpunit/Event/Events/Test/HookMethod/AfterLastTestMethodCalledSubscriber.phpJyfJDphpunit/Event/Events/Test/HookMethod/AfterLastTestMethodFinished.php`yf`@Nphpunit/Event/Events/Test/HookMethod/AfterLastTestMethodFinishedSubscriber.phpNyfNwK>phpunit/Event/Events/Test/HookMethod/AfterTestMethodCalled.php`yf`7Hphpunit/Event/Events/Test/HookMethod/AfterTestMethodCalledSubscriber.phpByfBR@phpunit/Event/Events/Test/HookMethod/AfterTestMethodFinished.phpWyfWSJphpunit/Event/Events/Test/HookMethod/AfterTestMethodFinishedSubscriber.phpFyfF^_Dphpunit/Event/Events/Test/HookMethod/BeforeFirstTestMethodCalled.phpmyfm\Nphpunit/Event/Events/Test/HookMethod/BeforeFirstTestMethodCalledSubscriber.phpNyfNeuEphpunit/Event/Events/Test/HookMethod/BeforeFirstTestMethodErrored.phpyf+[Ophpunit/Event/Events/Test/HookMethod/BeforeFirstTestMethodErroredSubscriber.phpPyfP񨥲Fphpunit/Event/Events/Test/HookMethod/BeforeFirstTestMethodFinished.phpdyfdX'ŤPphpunit/Event/Events/Test/HookMethod/BeforeFirstTestMethodFinishedSubscriber.phpRyfR\?phpunit/Event/Events/Test/HookMethod/BeforeTestMethodCalled.phpbyfb~Iphpunit/Event/Events/Test/HookMethod/BeforeTestMethodCalledSubscriber.phpDyfDƾAphpunit/Event/Events/Test/HookMethod/BeforeTestMethodFinished.phpYyfY%'Kphpunit/Event/Events/Test/HookMethod/BeforeTestMethodFinishedSubscriber.phpHyfHxS<phpunit/Event/Events/Test/HookMethod/PostConditionCalled.phpbyfb<Fphpunit/Event/Events/Test/HookMethod/PostConditionCalledSubscriber.php>yf>b*>phpunit/Event/Events/Test/HookMethod/PostConditionFinished.phpYyfYw:Hphpunit/Event/Events/Test/HookMethod/PostConditionFinishedSubscriber.phpByfBWXF;phpunit/Event/Events/Test/HookMethod/PreConditionCalled.php`yf` FEphpunit/Event/Events/Test/HookMethod/PreConditionCalledSubscriber.php<yf<u8O&=phpunit/Event/Events/Test/HookMethod/PreConditionFinished.phpWyfW)/IyGphpunit/Event/Events/Test/HookMethod/PreConditionFinishedSubscriber.php@yf@X3phpunit/Event/Events/Test/Issue/ConsideredRisky.phpyf"=phpunit/Event/Events/Test/Issue/ConsideredRiskySubscriber.php6yf6S%LT8phpunit/Event/Events/Test/Issue/DeprecationTriggered.php yf 9Bphpunit/Event/Events/Test/Issue/DeprecationTriggeredSubscriber.php@yf@2phpunit/Event/Events/Test/Issue/ErrorTriggered.phpw yfw <phpunit/Event/Events/Test/Issue/ErrorTriggeredSubscriber.php4yf4R53phpunit/Event/Events/Test/Issue/NoticeTriggered.php +yf +=phpunit/Event/Events/Test/Issue/NoticeTriggeredSubscriber.php6yf6'T$;phpunit/Event/Events/Test/Issue/PhpDeprecationTriggered.php yf *Ephpunit/Event/Events/Test/Issue/PhpDeprecationTriggeredSubscriber.phpFyfFs6phpunit/Event/Events/Test/Issue/PhpNoticeTriggered.php +yf +n`@phpunit/Event/Events/Test/Issue/PhpNoticeTriggeredSubscriber.php<yf<Ms7phpunit/Event/Events/Test/Issue/PhpWarningTriggered.php +yf +dDAphpunit/Event/Events/Test/Issue/PhpWarningTriggeredSubscriber.php>yf> +d?phpunit/Event/Events/Test/Issue/PhpunitDeprecationTriggered.phpdyfdnIphpunit/Event/Events/Test/Issue/PhpunitDeprecationTriggeredSubscriber.phpNyfNlp9phpunit/Event/Events/Test/Issue/PhpunitErrorTriggered.phpqyfq'0cCphpunit/Event/Events/Test/Issue/PhpunitErrorTriggeredSubscriber.phpByfB;;phpunit/Event/Events/Test/Issue/PhpunitWarningTriggered.php\yf\g.Ephpunit/Event/Events/Test/Issue/PhpunitWarningTriggeredSubscriber.phpFyfF4phpunit/Event/Events/Test/Issue/WarningTriggered.php +yf +,>phpunit/Event/Events/Test/Issue/WarningTriggeredSubscriber.php8yf8Z{@phpunit/Event/Events/Test/Lifecycle/DataProviderMethodCalled.php.yf.bj}ZJphpunit/Event/Events/Test/Lifecycle/DataProviderMethodCalledSubscriber.phpHyfH~-rBphpunit/Event/Events/Test/Lifecycle/DataProviderMethodFinished.php(yf(rLphpunit/Event/Events/Test/Lifecycle/DataProviderMethodFinishedSubscriber.phpLyfL ?0phpunit/Event/Events/Test/Lifecycle/Finished.phpwyfwu=6y:phpunit/Event/Events/Test/Lifecycle/FinishedSubscriber.php(yf(.͵P9phpunit/Event/Events/Test/Lifecycle/PreparationFailed.phppyfp3&-Cphpunit/Event/Events/Test/Lifecycle/PreparationFailedSubscriber.php:yf:":phpunit/Event/Events/Test/Lifecycle/PreparationStarted.phpryfr_Dphpunit/Event/Events/Test/Lifecycle/PreparationStartedSubscriber.php<yf<&Uep0phpunit/Event/Events/Test/Lifecycle/Prepared.php]yf]q}:phpunit/Event/Events/Test/Lifecycle/PreparedSubscriber.php(yf(ħK-phpunit/Event/Events/Test/Outcome/Errored.phpyf@ޤ7phpunit/Event/Events/Test/Outcome/ErroredSubscriber.php&yf&d3,phpunit/Event/Events/Test/Outcome/Failed.phpyfEfɤ6phpunit/Event/Events/Test/Outcome/FailedSubscriber.php$yf$\*6phpunit/Event/Events/Test/Outcome/MarkedIncomplete.php yf GEϤ@phpunit/Event/Events/Test/Outcome/MarkedIncompleteSubscriber.php8yf8 +6,phpunit/Event/Events/Test/Outcome/Passed.phpYyfY^6phpunit/Event/Events/Test/Outcome/PassedSubscriber.php$yf$:y-phpunit/Event/Events/Test/Outcome/Skipped.phpyfW7phpunit/Event/Events/Test/Outcome/SkippedSubscriber.php&yf&{dz5phpunit/Event/Events/Test/PrintedUnexpectedOutput.phpyf7?phpunit/Event/Events/Test/PrintedUnexpectedOutputSubscriber.phpFyfF4:phpunit/Event/Events/Test/TestDouble/MockObjectCreated.phpyf*~Dphpunit/Event/Events/Test/TestDouble/MockObjectCreatedSubscriber.php:yf:E'Jphpunit/Event/Events/Test/TestDouble/MockObjectForAbstractClassCreated.phpyf/bLTphpunit/Event/Events/Test/TestDouble/MockObjectForAbstractClassCreatedSubscriber.phpZyfZ*Uphpunit/Event/Events/Test/TestDouble/MockObjectForIntersectionOfInterfacesCreated.phpUyfUd_phpunit/Event/Events/Test/TestDouble/MockObjectForIntersectionOfInterfacesCreatedSubscriber.phppyfp-Bphpunit/Event/Events/Test/TestDouble/MockObjectForTraitCreated.php yf [Lphpunit/Event/Events/Test/TestDouble/MockObjectForTraitCreatedSubscriber.phpJyfJ~H Bphpunit/Event/Events/Test/TestDouble/MockObjectFromWsdlCreated.php +yf +PLphpunit/Event/Events/Test/TestDouble/MockObjectFromWsdlCreatedSubscriber.phpJyfJͤAphpunit/Event/Events/Test/TestDouble/PartialMockObjectCreated.php9yf9RgäKphpunit/Event/Events/Test/TestDouble/PartialMockObjectCreatedSubscriber.phpHyfH9phpunit/Event/Events/Test/TestDouble/TestProxyCreated.phpyfϤCphpunit/Event/Events/Test/TestDouble/TestProxyCreatedSubscriber.php8yf848phpunit/Event/Events/Test/TestDouble/TestStubCreated.phpyf 0Bphpunit/Event/Events/Test/TestDouble/TestStubCreatedSubscriber.php6yf6 Sphpunit/Event/Events/Test/TestDouble/TestStubForIntersectionOfInterfacesCreated.phpQyfQ#]phpunit/Event/Events/Test/TestDouble/TestStubForIntersectionOfInterfacesCreatedSubscriber.phplyflR5phpunit/Event/Events/TestRunner/BootstrapFinished.phpfyffJ?phpunit/Event/Events/TestRunner/BootstrapFinishedSubscriber.phpFyfF2=.phpunit/Event/Events/TestRunner/Configured.phpzyfzrw88phpunit/Event/Events/TestRunner/ConfiguredSubscriber.php8yf8LϤ8phpunit/Event/Events/TestRunner/DeprecationTriggered.phpqyfq o~Bphpunit/Event/Events/TestRunner/DeprecationTriggeredSubscriber.phpLyfL`25phpunit/Event/Events/TestRunner/EventFacadeSealed.phpyfjZ?phpunit/Event/Events/TestRunner/EventFacadeSealedSubscriber.phpFyfF$v^4phpunit/Event/Events/TestRunner/ExecutionAborted.phpyfp>phpunit/Event/Events/TestRunner/ExecutionAbortedSubscriber.phpDyfDsl|5phpunit/Event/Events/TestRunner/ExecutionFinished.phpyfJlM?phpunit/Event/Events/TestRunner/ExecutionFinishedSubscriber.phpFyfFӾ4phpunit/Event/Events/TestRunner/ExecutionStarted.phpyf>phpunit/Event/Events/TestRunner/ExecutionStartedSubscriber.phpDyfD.9phpunit/Event/Events/TestRunner/ExtensionBootstrapped.phpuyfu@*Cphpunit/Event/Events/TestRunner/ExtensionBootstrappedSubscriber.phpNyfNå;phpunit/Event/Events/TestRunner/ExtensionLoadedFromPhar.phpyf&OEphpunit/Event/Events/TestRunner/ExtensionLoadedFromPharSubscriber.phpRyfRAƾ,phpunit/Event/Events/TestRunner/Finished.phpyyfy6phpunit/Event/Events/TestRunner/FinishedSubscriber.php4yf4[Ix=phpunit/Event/Events/TestRunner/GarbageCollectionDisabled.phpyfwGphpunit/Event/Events/TestRunner/GarbageCollectionDisabledSubscriber.phpVyfVy W<phpunit/Event/Events/TestRunner/GarbageCollectionEnabled.phpyfa'֤Fphpunit/Event/Events/TestRunner/GarbageCollectionEnabledSubscriber.phpTyfTy +>phpunit/Event/Events/TestRunner/GarbageCollectionTriggered.phpyfjqHphpunit/Event/Events/TestRunner/GarbageCollectionTriggeredSubscriber.phpXyfXJz+phpunit/Event/Events/TestRunner/Started.phpwyfw0t5phpunit/Event/Events/TestRunner/StartedSubscriber.php2yf2}Rp4phpunit/Event/Events/TestRunner/WarningTriggered.phpiyfib>phpunit/Event/Events/TestRunner/WarningTriggeredSubscriber.phpDyfDʉ+phpunit/Event/Events/TestSuite/Filtered.phpyf5jJ5phpunit/Event/Events/TestSuite/FilteredSubscriber.php2yf2?ˤ+phpunit/Event/Events/TestSuite/Finished.phpyfeM5phpunit/Event/Events/TestSuite/FinishedSubscriber.php2yf2JV8)phpunit/Event/Events/TestSuite/Loaded.phpyf W3phpunit/Event/Events/TestSuite/LoadedSubscriber.php.yf.)*phpunit/Event/Events/TestSuite/Skipped.php}yf}[}4phpunit/Event/Events/TestSuite/SkippedSubscriber.php0yf0)phpunit/Event/Events/TestSuite/Sorted.php"yf":^3phpunit/Event/Events/TestSuite/SortedSubscriber.php.yf.IN"*phpunit/Event/Events/TestSuite/Started.phpyf*04phpunit/Event/Events/TestSuite/StartedSubscriber.php0yf0.9phpunit/Event/Exception/EventAlreadyAssignedException.php yf 0ɤ8phpunit/Event/Exception/EventFacadeIsSealedException.php +yf +J ؤ%phpunit/Event/Exception/Exception.phpLyfLgx4phpunit/Event/Exception/InvalidArgumentException.phpyf䀤1phpunit/Event/Exception/InvalidEventException.phpyfE>6phpunit/Event/Exception/InvalidSubscriberException.phpyfSg$phpunit/Event/Exception/MapError.phpyfRGphpunit/Event/Exception/MoreThanOneDataSetFromDataProviderException.php0yf0R=8phpunit/Event/Exception/NoComparisonFailureException.phpyf{k>phpunit/Event/Exception/NoDataSetFromDataProviderException.php'yf'@~8phpunit/Event/Exception/NoPreviousThrowableException.php +yf +~@phpunit/Event/Exception/NoTestCaseObjectOnCallStackException.phpyf,phpunit/Event/Exception/RuntimeException.phpyfLDphpunit/Event/Exception/SubscriberTypeAlreadyRegisteredException.phpyfįK1phpunit/Event/Exception/UnknownEventException.phpyf}5phpunit/Event/Exception/UnknownEventTypeException.phpyf/<6phpunit/Event/Exception/UnknownSubscriberException.phpyf ˤ:phpunit/Event/Exception/UnknownSubscriberTypeException.php yf &'*phpunit/Event/Facade.php!yf!nMphpunit/Event/Subscriber.phpyfdlkphpunit/Event/Tracer.phpyfmphpunit/Event/TypeMap.php+yf+ o#phpunit/Event/Value/ClassMethod.phpyf@դ)phpunit/Event/Value/ComparisonFailure.phpyf0phpunit/Event/Value/ComparisonFailureBuilder.phpyfht1/phpunit/Event/Value/Runtime/OperatingSystem.phpyfSᛤ#phpunit/Event/Value/Runtime/PHP.php yf @h'phpunit/Event/Value/Runtime/PHPUnit.phpmyfm'phpunit/Event/Value/Runtime/Runtime.phpyf5l*phpunit/Event/Value/Telemetry/Duration.php yf 8N8phpunit/Event/Value/Telemetry/GarbageCollectorStatus.phpJyfJ}@phpunit/Event/Value/Telemetry/GarbageCollectorStatusProvider.phpyfg(phpunit/Event/Value/Telemetry/HRTime.php yf l!&phpunit/Event/Value/Telemetry/Info.php +yf +LWh-phpunit/Event/Value/Telemetry/MemoryMeter.php8yf8lE-phpunit/Event/Value/Telemetry/MemoryUsage.phpdyfdEphpunit/Event/Value/Telemetry/Php81GarbageCollectorStatusProvider.phpiyfiBRXEphpunit/Event/Value/Telemetry/Php83GarbageCollectorStatusProvider.phpyfBU*phpunit/Event/Value/Telemetry/Snapshot.phpyf^r+phpunit/Event/Value/Telemetry/StopWatch.phpyfq(phpunit/Event/Value/Telemetry/System.php;yf;}Ŗ3phpunit/Event/Value/Telemetry/SystemMemoryMeter.php|yf|n1phpunit/Event/Value/Telemetry/SystemStopWatch.phpyfw;s;phpunit/Event/Value/Telemetry/SystemStopWatchWithOffset.phpQyfQ7!phpunit/Event/Value/Test/Phpt.php]yf]2詤!phpunit/Event/Value/Test/Test.phpyfΥ8+phpunit/Event/Value/Test/TestCollection.php-yf-3phpunit/Event/Value/Test/TestCollectionIterator.phpyfqˤ:phpunit/Event/Value/Test/TestData/DataFromDataProvider.phpyf}<phpunit/Event/Value/Test/TestData/DataFromTestDependency.phpyfUﻤ.phpunit/Event/Value/Test/TestData/TestData.phpyf##8phpunit/Event/Value/Test/TestData/TestDataCollection.php +yf +oAѤ@phpunit/Event/Value/Test/TestData/TestDataCollectionIterator.phpyf-K$phpunit/Event/Value/Test/TestDox.php +yf +%+phpunit/Event/Value/Test/TestDoxBuilder.phpyf*R'phpunit/Event/Value/Test/TestMethod.php +yf +.~.phpunit/Event/Value/Test/TestMethodBuilder.php yf +phpunit/Event/Value/TestSuite/TestSuite.phpyfG2phpunit/Event/Value/TestSuite/TestSuiteBuilder.phpv yfv 7phpunit/Event/Value/TestSuite/TestSuiteForTestClass.phpyfh~Hphpunit/Event/Value/TestSuite/TestSuiteForTestMethodWithDataProvider.phpzyfzqSUy3phpunit/Event/Value/TestSuite/TestSuiteWithName.phpyfcmy!phpunit/Event/Value/Throwable.phpX yfX _S[y(phpunit/Event/Value/ThrowableBuilder.php yf وphpunit/Exception.phpLyfLߊphpunit/Framework/Assert.phpyf;&phpunit/Framework/Assert/Functions.phpZyfZ*j-&phpunit/Framework/Attributes/After.phpyf~jL¤+phpunit/Framework/Attributes/AfterClass.phpyf׎.phpunit/Framework/Attributes/BackupGlobals.phpyf<ߤ7phpunit/Framework/Attributes/BackupStaticProperties.phpyf'phpunit/Framework/Attributes/Before.phpyf;,phpunit/Framework/Attributes/BeforeClass.phpyf\3phpunit/Framework/Attributes/CodeCoverageIgnore.phpnyfnpw,phpunit/Framework/Attributes/CoversClass.phpyf-ʤ/phpunit/Framework/Attributes/CoversFunction.phpyfu +.phpunit/Framework/Attributes/CoversNothing.phpyf&ޏ-phpunit/Framework/Attributes/DataProvider.phpyfs\C5phpunit/Framework/Attributes/DataProviderExternal.phpyfi (phpunit/Framework/Attributes/Depends.phpyf4ZR0phpunit/Framework/Attributes/DependsExternal.phpyfԭ|>phpunit/Framework/Attributes/DependsExternalUsingDeepClone.phpyfz⪐Aphpunit/Framework/Attributes/DependsExternalUsingShallowClone.php yf K b /phpunit/Framework/Attributes/DependsOnClass.phpyf%*B=phpunit/Framework/Attributes/DependsOnClassUsingDeepClone.phpyfE2@phpunit/Framework/Attributes/DependsOnClassUsingShallowClone.phpyf#Y6phpunit/Framework/Attributes/DependsUsingDeepClone.phpyf$Bv9phpunit/Framework/Attributes/DependsUsingShallowClone.phpyf},Fm9phpunit/Framework/Attributes/DoesNotPerformAssertions.php)yf)J.~@phpunit/Framework/Attributes/ExcludeGlobalVariableFromBackup.phpyf@7@phpunit/Framework/Attributes/ExcludeStaticPropertyFromBackup.php0yf0Hlp&phpunit/Framework/Attributes/Group.phpyfQߤ;phpunit/Framework/Attributes/IgnoreClassForCodeCoverage.phpyf:3phpunit/Framework/Attributes/IgnoreDeprecations.php#yf#]e>phpunit/Framework/Attributes/IgnoreFunctionForCodeCoverage.phpyf1<phpunit/Framework/Attributes/IgnoreMethodForCodeCoverage.phpNyfN&phpunit/Framework/Attributes/Large.phpyf"mn'phpunit/Framework/Attributes/Medium.phpyf'2.phpunit/Framework/Attributes/PostCondition.phpyfK-phpunit/Framework/Attributes/PreCondition.phpyfؕ@4phpunit/Framework/Attributes/PreserveGlobalState.phpyf)1phpunit/Framework/Attributes/RequiresFunction.phpyf42\/phpunit/Framework/Attributes/RequiresMethod.phpyf~fi8phpunit/Framework/Attributes/RequiresOperatingSystem.phpyfAa>phpunit/Framework/Attributes/RequiresOperatingSystemFamily.phpyfJv5,phpunit/Framework/Attributes/RequiresPhp.phpyf9ge5phpunit/Framework/Attributes/RequiresPhpExtension.phptyft:hK0phpunit/Framework/Attributes/RequiresPhpunit.phpyf"0phpunit/Framework/Attributes/RequiresSetting.phpyf:phpunit/Framework/Attributes/RunClassInSeparateProcess.phpyf:5phpunit/Framework/Attributes/RunInSeparateProcess.php yf <phpunit/Framework/Attributes/RunTestsInSeparateProcesses.phpyfĪ&phpunit/Framework/Attributes/Small.phpyfw~%phpunit/Framework/Attributes/Test.phpyf#(phpunit/Framework/Attributes/TestDox.phpyf2)phpunit/Framework/Attributes/TestWith.phpyfD6-phpunit/Framework/Attributes/TestWithJson.phpyf'phpunit/Framework/Attributes/Ticket.phpyf͙*phpunit/Framework/Attributes/UsesClass.phpyf 5-phpunit/Framework/Attributes/UsesFunction.phpyf4phpunit/Framework/Attributes/WithoutErrorHandler.php +yf +0phpunit/Framework/Constraint/Boolean/IsFalse.php`yf`, /phpunit/Framework/Constraint/Boolean/IsTrue.php]yf]?6ͤ)phpunit/Framework/Constraint/Callback.phpyfj2phpunit/Framework/Constraint/Cardinality/Count.php yf F8phpunit/Framework/Constraint/Cardinality/GreaterThan.phpTyfTFdBI4phpunit/Framework/Constraint/Cardinality/IsEmpty.phpcyfcHQX5phpunit/Framework/Constraint/Cardinality/LessThan.phpNyfNC5phpunit/Framework/Constraint/Cardinality/SameSize.phpyfBs+phpunit/Framework/Constraint/Constraint.php yf E1phpunit/Framework/Constraint/Equality/IsEqual.phpe yfe ?phpunit/Framework/Constraint/Equality/IsEqualCanonicalizing.php +yf +M+:}=phpunit/Framework/Constraint/Equality/IsEqualIgnoringCase.php +yf +ړ:phpunit/Framework/Constraint/Equality/IsEqualWithDelta.php yf ]D4phpunit/Framework/Constraint/Exception/Exception.phpsyfs*8phpunit/Framework/Constraint/Exception/ExceptionCode.phpyfV"/Gphpunit/Framework/Constraint/Exception/ExceptionMessageIsOrContains.phpyf%cSphpunit/Framework/Constraint/Exception/ExceptionMessageMatchesRegularExpression.phpKyfKj];phpunit/Framework/Constraint/Filesystem/DirectoryExists.phpyf-p6phpunit/Framework/Constraint/Filesystem/FileExists.phpyf+6phpunit/Framework/Constraint/Filesystem/IsReadable.phpyfA6phpunit/Framework/Constraint/Filesystem/IsWritable.phpyfB+phpunit/Framework/Constraint/IsAnything.phpyf\ s,phpunit/Framework/Constraint/IsIdentical.php yf ;jK,phpunit/Framework/Constraint/JsonMatches.php +yf +*Q.phpunit/Framework/Constraint/Math/IsFinite.phpzyfzԊ0phpunit/Framework/Constraint/Math/IsInfinite.phpyfC+phpunit/Framework/Constraint/Math/IsNan.phpnyfnx4phpunit/Framework/Constraint/Object/ObjectEquals.phpqyfqj9phpunit/Framework/Constraint/Object/ObjectHasProperty.phpyf"Ǥ8phpunit/Framework/Constraint/Operator/BinaryOperator.phpV yfV M +4phpunit/Framework/Constraint/Operator/LogicalAnd.phpYyfY4phpunit/Framework/Constraint/Operator/LogicalNot.php8 yf8 /3phpunit/Framework/Constraint/Operator/LogicalOr.php=yf="iN4phpunit/Framework/Constraint/Operator/LogicalXor.phpyfޤ2phpunit/Framework/Constraint/Operator/Operator.php'yf'J%7phpunit/Framework/Constraint/Operator/UnaryOperator.phpyf.phpunit/Framework/Constraint/String/IsJson.php0 yf0 t9phpunit/Framework/Constraint/String/RegularExpression.php^yf^ˬ>6phpunit/Framework/Constraint/String/StringContains.phpZyfZ9kR6phpunit/Framework/Constraint/String/StringEndsWith.phpyf@%-Mphpunit/Framework/Constraint/String/StringEqualsStringIgnoringLineEndings.php7yf7[JFphpunit/Framework/Constraint/String/StringMatchesFormatDescription.php yf h8phpunit/Framework/Constraint/String/StringStartsWith.phpyf.8phpunit/Framework/Constraint/Traversable/ArrayHasKey.phpyfphpunit/Framework/Exception/GeneratorNotSupportedException.phpyfh9phpunit/Framework/Exception/Incomplete/IncompleteTest.phpyf,+>phpunit/Framework/Exception/Incomplete/IncompleteTestError.phpyfםܤ8phpunit/Framework/Exception/InvalidArgumentException.phpyf:(<phpunit/Framework/Exception/InvalidCoversTargetException.phpyfo苤<phpunit/Framework/Exception/InvalidDataProviderException.phpyf.ڜɤ:phpunit/Framework/Exception/InvalidDependencyException.phpyf}9phpunit/Framework/Exception/NoChildTestSuiteException.phpyfP$Nphpunit/Framework/Exception/ObjectEquals/ActualValueIsNotAnObjectException.phpAyfA<ؤ`phpunit/Framework/Exception/ObjectEquals/ComparisonMethodDoesNotAcceptParameterTypeException.phpyf@bphpunit/Framework/Exception/ObjectEquals/ComparisonMethodDoesNotDeclareBoolReturnTypeException.phpyfPigphpunit/Framework/Exception/ObjectEquals/ComparisonMethodDoesNotDeclareExactlyOneParameterException.phpyfFaphpunit/Framework/Exception/ObjectEquals/ComparisonMethodDoesNotDeclareParameterTypeException.phpyfENRphpunit/Framework/Exception/ObjectEquals/ComparisonMethodDoesNotExistException.phpyfui8phpunit/Framework/Exception/PhptAssertionFailedError.php.yf..4|9phpunit/Framework/Exception/ProcessIsolationException.phpyfH:3phpunit/Framework/Exception/Skipped/SkippedTest.phpyfS.=phpunit/Framework/Exception/Skipped/SkippedTestSuiteError.phpyfxCphpunit/Framework/Exception/Skipped/SkippedWithMessageException.phpyfz$@phpunit/Framework/Exception/UnknownClassOrInterfaceException.phpyfӤ4phpunit/Framework/Exception/UnknownTypeException.phpnyfn(j.phpunit/Framework/ExecutionOrderDependency.phpyf 83phpunit/Framework/MockObject/ConfigurableMethod.phpyf-Aphpunit/Framework/MockObject/Exception/BadMethodCallException.phpyfΫXHphpunit/Framework/MockObject/Exception/CannotUseOnlyMethodsException.phpyf/(4phpunit/Framework/MockObject/Exception/Exception.phpyfB'Kphpunit/Framework/MockObject/Exception/IncompatibleReturnValueException.phpyyfy|/Hphpunit/Framework/MockObject/Exception/MatchBuilderNotFoundException.phpyfLphpunit/Framework/MockObject/Exception/MatcherAlreadyRegisteredException.phpyfz'Lphpunit/Framework/MockObject/Exception/MethodCannotBeConfiguredException.phpyf}QOphpunit/Framework/MockObject/Exception/MethodNameAlreadyConfiguredException.phpyfӁƤKphpunit/Framework/MockObject/Exception/MethodNameNotConfiguredException.php~yf~x1)Uphpunit/Framework/MockObject/Exception/MethodParametersAlreadyConfiguredException.phpyf rHphpunit/Framework/MockObject/Exception/NeverReturningMethodException.phpmyfm+">phpunit/Framework/MockObject/Exception/ReflectionException.phpyf.ؔLphpunit/Framework/MockObject/Exception/ReturnValueNotConfiguredException.php<yf<Qo;phpunit/Framework/MockObject/Exception/RuntimeException.phpyf_|Qphpunit/Framework/MockObject/Generator/Exception/CannotUseAddMethodsException.phpIyfI!;Pphpunit/Framework/MockObject/Generator/Exception/ClassIsEnumerationException.phpyfYJphpunit/Framework/MockObject/Generator/Exception/ClassIsFinalException.phpyfQMphpunit/Framework/MockObject/Generator/Exception/ClassIsReadonlyException.phpyfYMphpunit/Framework/MockObject/Generator/Exception/DuplicateMethodException.phpyf.٤>phpunit/Framework/MockObject/Generator/Exception/Exception.phpyfOphpunit/Framework/MockObject/Generator/Exception/InvalidMethodNameException.phpyflDNphpunit/Framework/MockObject/Generator/Exception/NameAlreadyInUseException.phpyf'Tcphpunit/Framework/MockObject/Generator/Exception/OriginalConstructorInvocationRequiredException.phpyfDKƤHphpunit/Framework/MockObject/Generator/Exception/ReflectionException.phpyfq3Ephpunit/Framework/MockObject/Generator/Exception/RuntimeException.phpyf}ʻWphpunit/Framework/MockObject/Generator/Exception/SoapExtensionNotAvailableException.phpyfn5Jphpunit/Framework/MockObject/Generator/Exception/UnknownClassException.phpyfZMJphpunit/Framework/MockObject/Generator/Exception/UnknownTraitException.php +yf +9ѤIphpunit/Framework/MockObject/Generator/Exception/UnknownTypeException.phpyf#J4phpunit/Framework/MockObject/Generator/Generator.php~yf~JG4phpunit/Framework/MockObject/Generator/MockClass.phpbyfbܤ5phpunit/Framework/MockObject/Generator/MockMethod.phpw)yfw)8phpunit/Framework/MockObject/Generator/MockMethodSet.phpnyfn4phpunit/Framework/MockObject/Generator/MockTrait.phpyf]t3phpunit/Framework/MockObject/Generator/MockType.phpyfgN9phpunit/Framework/MockObject/Generator/TemplateLoader.phpyf+@phpunit/Framework/MockObject/Generator/templates/deprecation.tpl;yf;O5sCphpunit/Framework/MockObject/Generator/templates/doubled_method.tpl8yf8)դJphpunit/Framework/MockObject/Generator/templates/doubled_static_method.tplyf 4RAphpunit/Framework/MockObject/Generator/templates/intersection.tplLyfL-XCphpunit/Framework/MockObject/Generator/templates/proxied_method.tplyf Fphpunit/Framework/MockObject/Generator/templates/test_double_class.tplfyff' @phpunit/Framework/MockObject/Generator/templates/trait_class.tplQyfQ<Ȥ?phpunit/Framework/MockObject/Generator/templates/wsdl_class.tplyf@phpunit/Framework/MockObject/Generator/templates/wsdl_method.tpl<yf<i,phpunit/Framework/MockObject/MockBuilder.php~/yf~/&?phpunit/Framework/MockObject/Runtime/Api/DoubledCloneMethod.php1yf1 w3phpunit/Framework/MockObject/Runtime/Api/Method.php yf =Yvr:phpunit/Framework/MockObject/Runtime/Api/MockObjectApi.phpwyfwl?phpunit/Framework/MockObject/Runtime/Api/ProxiedCloneMethod.phpLyfL+4phpunit/Framework/MockObject/Runtime/Api/StubApi.php1yf1Wb19phpunit/Framework/MockObject/Runtime/Builder/Identity.phpZyfZdAphpunit/Framework/MockObject/Runtime/Builder/InvocationMocker.php#yf#Ԧ#Bphpunit/Framework/MockObject/Runtime/Builder/InvocationStubber.phpyf(I@phpunit/Framework/MockObject/Runtime/Builder/MethodNameMatch.phpyfw@phpunit/Framework/MockObject/Runtime/Builder/ParametersMatch.phpKyfKue5phpunit/Framework/MockObject/Runtime/Builder/Stub.phpyfȉ=phpunit/Framework/MockObject/Runtime/Interface/MockObject.phpyfmOEphpunit/Framework/MockObject/Runtime/Interface/MockObjectInternal.phpyfR7phpunit/Framework/MockObject/Runtime/Interface/Stub.phpyfliH?phpunit/Framework/MockObject/Runtime/Interface/StubInternal.php`yf`$ 3phpunit/Framework/MockObject/Runtime/Invocation.phpyf#:phpunit/Framework/MockObject/Runtime/InvocationHandler.phpyf|?̤0phpunit/Framework/MockObject/Runtime/Matcher.phpyf=phpunit/Framework/MockObject/Runtime/MethodNameConstraint.phpyfY=phpunit/Framework/MockObject/Runtime/ReturnValueGenerator.phpyf)/դ=phpunit/Framework/MockObject/Runtime/Rule/AnyInvokedCount.phpyfB;phpunit/Framework/MockObject/Runtime/Rule/AnyParameters.phpyfiu=phpunit/Framework/MockObject/Runtime/Rule/InvocationOrder.phpyf>Aphpunit/Framework/MockObject/Runtime/Rule/InvokedAtLeastCount.phpyf:d@phpunit/Framework/MockObject/Runtime/Rule/InvokedAtLeastOnce.phpyf{m@phpunit/Framework/MockObject/Runtime/Rule/InvokedAtMostCount.phpyfDhҤ:phpunit/Framework/MockObject/Runtime/Rule/InvokedCount.php[ yf[ Rr8phpunit/Framework/MockObject/Runtime/Rule/MethodName.phpyf8phpunit/Framework/MockObject/Runtime/Rule/Parameters.php5yf5}D<phpunit/Framework/MockObject/Runtime/Rule/ParametersRule.phpyfvY>phpunit/Framework/MockObject/Runtime/Stub/ConsecutiveCalls.phpyfbm7phpunit/Framework/MockObject/Runtime/Stub/Exception.php:yf:U +<phpunit/Framework/MockObject/Runtime/Stub/ReturnArgument.php3yf3u<phpunit/Framework/MockObject/Runtime/Stub/ReturnCallback.phphyfh&<=phpunit/Framework/MockObject/Runtime/Stub/ReturnReference.phpyf8phpunit/Framework/MockObject/Runtime/Stub/ReturnSelf.phpyf8phpunit/Framework/MockObject/Runtime/Stub/ReturnStub.phpyfR<phpunit/Framework/MockObject/Runtime/Stub/ReturnValueMap.phpyf:S2phpunit/Framework/MockObject/Runtime/Stub/Stub.phpyyfyD!phpunit/Framework/Reorderable.phpyfZܤ$phpunit/Framework/SelfDescribing.php yf ]H4phpunit/Framework/Test.phpyf9!phpunit/Framework/TestBuilder.php}%yf}%Lphpunit/Framework/TestCase.php yf T phpunit/Framework/TestRunner.php8yf8d*$phpunit/Framework/TestSize/Known.phpyf?&$phpunit/Framework/TestSize/Large.php,yf,C@<%phpunit/Framework/TestSize/Medium.php/yf/&$phpunit/Framework/TestSize/Small.php yf uׁ'phpunit/Framework/TestSize/TestSize.php+yf+N]&phpunit/Framework/TestSize/Unknown.phpyf=,phpunit/Framework/TestStatus/Deprecation.phpyfTl&phpunit/Framework/TestStatus/Error.phpyfeɤ(phpunit/Framework/TestStatus/Failure.phpyfu+phpunit/Framework/TestStatus/Incomplete.phpyf^&phpunit/Framework/TestStatus/Known.phpiyfiH'phpunit/Framework/TestStatus/Notice.phpyf#(I&phpunit/Framework/TestStatus/Risky.phpyf ߤ(phpunit/Framework/TestStatus/Skipped.phpyfcx(phpunit/Framework/TestStatus/Success.phpyfRL!^+phpunit/Framework/TestStatus/TestStatus.phpDyfDͮ։(phpunit/Framework/TestStatus/Unknown.phpyfQ(phpunit/Framework/TestStatus/Warning.phpyf6Ҥphpunit/Framework/TestSuite.phpGyfG&̤'phpunit/Framework/TestSuiteIterator.php,yf, 4}jphpunit/Logging/EventLogger.phpyfGphpunit/Logging/Exception.phpyf%٤(phpunit/Logging/JUnit/JunitXmlLogger.phpF/yfF/ks/phpunit/Logging/JUnit/Subscriber/Subscriber.phpyf":phpunit/Logging/JUnit/Subscriber/TestErroredSubscriber.phpyfU9phpunit/Logging/JUnit/Subscriber/TestFailedSubscriber.phpyfɧF;phpunit/Logging/JUnit/Subscriber/TestFinishedSubscriber.phpyfCphpunit/Logging/JUnit/Subscriber/TestMarkedIncompleteSubscriber.php>yf>)ÎDphpunit/Logging/JUnit/Subscriber/TestPreparationFailedSubscriber.php>yf>T kEphpunit/Logging/JUnit/Subscriber/TestPreparationStartedSubscriber.phpJyfJÒE;phpunit/Logging/JUnit/Subscriber/TestPreparedSubscriber.phpyfT Jphpunit/Logging/JUnit/Subscriber/TestRunnerExecutionFinishedSubscriber.phpyf$ R:phpunit/Logging/JUnit/Subscriber/TestSkippedSubscriber.phpyfwx@phpunit/Logging/JUnit/Subscriber/TestSuiteFinishedSubscriber.phpyfA$?phpunit/Logging/JUnit/Subscriber/TestSuiteStartedSubscriber.phpyfb'2phpunit/Logging/TeamCity/Subscriber/Subscriber.phpyfnEphpunit/Logging/TeamCity/Subscriber/TestConsideredRiskySubscriber.php>yf>fJ=phpunit/Logging/TeamCity/Subscriber/TestErroredSubscriber.phpyfʤ<phpunit/Logging/TeamCity/Subscriber/TestFailedSubscriber.phpyfoฤ>phpunit/Logging/TeamCity/Subscriber/TestFinishedSubscriber.phpyf kFphpunit/Logging/TeamCity/Subscriber/TestMarkedIncompleteSubscriber.phpDyfD'HE>phpunit/Logging/TeamCity/Subscriber/TestPreparedSubscriber.phpyfѮ5oMphpunit/Logging/TeamCity/Subscriber/TestRunnerExecutionFinishedSubscriber.phpyfc/i=phpunit/Logging/TeamCity/Subscriber/TestSkippedSubscriber.phpyfCphpunit/Logging/TeamCity/Subscriber/TestSuiteFinishedSubscriber.phpyfUBphpunit/Logging/TeamCity/Subscriber/TestSuiteStartedSubscriber.phpyf +phpunit/Logging/TeamCity/TeamCityLogger.php$yf$(phpunit/Logging/TestDox/HtmlRenderer.php yf B*phpunit/Logging/TestDox/NamePrettifier.phpA!yfA!>]-phpunit/Logging/TestDox/PlainTextRenderer.phpyf۞1<phpunit/Logging/TestDox/TestResult/Subscriber/Subscriber.phpyf)Ophpunit/Logging/TestDox/TestResult/Subscriber/TestConsideredRiskySubscriber.phpyfl>Gphpunit/Logging/TestDox/TestResult/Subscriber/TestErroredSubscriber.phpyfԅFphpunit/Logging/TestDox/TestResult/Subscriber/TestFailedSubscriber.phpyf;ȿHphpunit/Logging/TestDox/TestResult/Subscriber/TestFinishedSubscriber.phpyf~Pphpunit/Logging/TestDox/TestResult/Subscriber/TestMarkedIncompleteSubscriber.phpyfmܤFphpunit/Logging/TestDox/TestResult/Subscriber/TestPassedSubscriber.phpyfl@Hphpunit/Logging/TestDox/TestResult/Subscriber/TestPreparedSubscriber.phpyf,#פGphpunit/Logging/TestDox/TestResult/Subscriber/TestSkippedSubscriber.phpyf^Tphpunit/Logging/TestDox/TestResult/Subscriber/TestTriggeredDeprecationSubscriber.phpyfKjkOphpunit/Logging/TestDox/TestResult/Subscriber/TestTriggeredNoticeSubscriber.phpyf Wphpunit/Logging/TestDox/TestResult/Subscriber/TestTriggeredPhpDeprecationSubscriber.php yf VRphpunit/Logging/TestDox/TestResult/Subscriber/TestTriggeredPhpNoticeSubscriber.phpyf蟸Sphpunit/Logging/TestDox/TestResult/Subscriber/TestTriggeredPhpWarningSubscriber.phpyfS[phpunit/Logging/TestDox/TestResult/Subscriber/TestTriggeredPhpunitDeprecationSubscriber.php#yf#RUphpunit/Logging/TestDox/TestResult/Subscriber/TestTriggeredPhpunitErrorSubscriber.phpyf?ۤWphpunit/Logging/TestDox/TestResult/Subscriber/TestTriggeredPhpunitWarningSubscriber.php yf ѥPphpunit/Logging/TestDox/TestResult/Subscriber/TestTriggeredWarningSubscriber.phpyfwz1phpunit/Logging/TestDox/TestResult/TestResult.phpyf+ͤ;phpunit/Logging/TestDox/TestResult/TestResultCollection.phpyfm"uCphpunit/Logging/TestDox/TestResult/TestResultCollectionIterator.phpyfV:phpunit/Logging/TestDox/TestResult/TestResultCollector.php'yf'àbphpunit/Metadata/After.php\yf\ #phpunit/Metadata/AfterClass.phpkyfkb٤%phpunit/Metadata/Api/CodeCoverage.php%yf%b%phpunit/Metadata/Api/DataProvider.php1 yf1 ݥ%phpunit/Metadata/Api/Dependencies.phpyfGphpunit/Metadata/Api/Groups.phpyfu$phpunit/Metadata/Api/HookMethods.phps yfs 4%phpunit/Metadata/Api/Requirements.phpyf"phpunit/Metadata/BackupGlobals.phpyf-5/+phpunit/Metadata/BackupStaticProperties.phpyf`/rphpunit/Metadata/Before.php_yf_dݤ phpunit/Metadata/BeforeClass.phpnyfn)Mphpunit/Metadata/Covers.php)yf)l phpunit/Metadata/CoversClass.php=yf=߂'phpunit/Metadata/CoversDefaultClass.phpVyfV0#phpunit/Metadata/CoversFunction.phpHyfH`&"phpunit/Metadata/CoversNothing.phptyftΎǤ!phpunit/Metadata/DataProvider.phpyf#phpunit/Metadata/DependsOnClass.phpyf%$phpunit/Metadata/DependsOnMethod.phpyf--phpunit/Metadata/DoesNotPerformAssertions.phpyf!Tphpunit/Metadata/Exception/AnnotationsAreNotSupportedForInternalClassesException.phpHyfH6 (phpunit/Metadata/Exception/Exception.phpOyfOwAphpunit/Metadata/Exception/InvalidVersionRequirementException.phpyfPR<phpunit/Metadata/Exception/NoVersionRequirementException.phpyf?+睤2phpunit/Metadata/Exception/ReflectionException.phpyf@ͤ4phpunit/Metadata/ExcludeGlobalVariableFromBackup.phpyf4phpunit/Metadata/ExcludeStaticPropertyFromBackup.phpyf0^phpunit/Metadata/Group.php;yf;A\/phpunit/Metadata/IgnoreClassForCodeCoverage.phpyf/Zg'phpunit/Metadata/IgnoreDeprecations.phpyf?iW2phpunit/Metadata/IgnoreFunctionForCodeCoverage.phpyf*0phpunit/Metadata/IgnoreMethodForCodeCoverage.phpyfQ??phpunit/Metadata/Metadata.php[yf[=)P'phpunit/Metadata/MetadataCollection.php.yf.e/phpunit/Metadata/MetadataCollectionIterator.phpyf1/phpunit/Metadata/Parser/Annotation/DocBlock.phpZ"yfZ"TҤ/phpunit/Metadata/Parser/Annotation/Registry.php~ yf~ 7\,phpunit/Metadata/Parser/AnnotationParser.phpFyfF-+phpunit/Metadata/Parser/AttributeParser.phpWyfWM!Ƥ)phpunit/Metadata/Parser/CachingParser.phpyf81|"phpunit/Metadata/Parser/Parser.phpyfe 'phpunit/Metadata/Parser/ParserChain.phpyf7$phpunit/Metadata/Parser/Registry.phpyf )d"phpunit/Metadata/PostCondition.phptyftվ.!phpunit/Metadata/PreCondition.phpqyfqS(phpunit/Metadata/PreserveGlobalState.phpyf*od%phpunit/Metadata/RequiresFunction.phpqyfq#phpunit/Metadata/RequiresMethod.phpyf9,phpunit/Metadata/RequiresOperatingSystem.phpyfcM)2phpunit/Metadata/RequiresOperatingSystemFamily.phpyfIM-~ phpunit/Metadata/RequiresPhp.php#yf#c)phpunit/Metadata/RequiresPhpExtension.phpyfq$phpunit/Metadata/RequiresPhpunit.php/yf/9D$phpunit/Metadata/RequiresSetting.phpyfK.phpunit/Metadata/RunClassInSeparateProcess.phpyfmE)phpunit/Metadata/RunInSeparateProcess.phpyf3U0phpunit/Metadata/RunTestsInSeparateProcesses.phpyfnaphpunit/Metadata/Test.phpYyfYz7凤phpunit/Metadata/TestDox.phpyf3phpunit/Metadata/TestWith.phpyfWKphpunit/Metadata/Uses.php#yf#Nܤphpunit/Metadata/UsesClass.php7yf7Ư%phpunit/Metadata/UsesDefaultClass.phpPyfPIC!phpunit/Metadata/UsesFunction.php?yf?`&2phpunit/Metadata/Version/ComparisonRequirement.phpfyffΉ2phpunit/Metadata/Version/ConstraintRequirement.phpyftҤ(phpunit/Metadata/Version/Requirement.phpyf[ä(phpunit/Metadata/WithoutErrorHandler.phpyfF=f$phpunit/Runner/Baseline/Baseline.phpyfAAphpunit/Runner/Baseline/Exception/CannotLoadBaselineException.phpyf\8Bphpunit/Runner/Baseline/Exception/FileDoesNotHaveLineException.phpyfM%phpunit/Runner/Baseline/Generator.php yf tՙ!phpunit/Runner/Baseline/Issue.php yf .cu"phpunit/Runner/Baseline/Reader.phpB yfB bǂ2phpunit/Runner/Baseline/RelativePathCalculator.php +yf +&1phpunit/Runner/Baseline/Subscriber/Subscriber.phpyfL%Iphpunit/Runner/Baseline/Subscriber/TestTriggeredDeprecationSubscriber.phpyf_vdDphpunit/Runner/Baseline/Subscriber/TestTriggeredNoticeSubscriber.phpmyfmW@ߤLphpunit/Runner/Baseline/Subscriber/TestTriggeredPhpDeprecationSubscriber.phpyfGphpunit/Runner/Baseline/Subscriber/TestTriggeredPhpNoticeSubscriber.php|yf|=d*Hphpunit/Runner/Baseline/Subscriber/TestTriggeredPhpWarningSubscriber.phpyffyEphpunit/Runner/Baseline/Subscriber/TestTriggeredWarningSubscriber.phpryfr/."phpunit/Runner/Baseline/Writer.phpyf%Sphpunit/Runner/CodeCoverage.php2yf2gNphpunit/Runner/ErrorHandler.phpyfIMu8phpunit/Runner/Exception/ClassCannotBeFoundException.phpyfc@phpunit/Runner/Exception/ClassDoesNotExtendTestCaseException.phpyf5phpunit/Runner/Exception/ClassIsAbstractException.phpyfp7o;phpunit/Runner/Exception/DirectoryDoesNotExistException.phpyf)4`+phpunit/Runner/Exception/ErrorException.phpyfe"&phpunit/Runner/Exception/Exception.phpyfnHA6phpunit/Runner/Exception/FileDoesNotExistException.phpyfg K2phpunit/Runner/Exception/InvalidOrderException.phpyf8Kڤ5phpunit/Runner/Exception/InvalidPhptFileException.phpyfzA>4phpunit/Runner/Exception/NoIgnoredEventException.phpyf3Uǚ;phpunit/Runner/Exception/ParameterDoesNotExistException.phpyf]wQDphpunit/Runner/Exception/PhptExternalFileCannotBeLoadedException.phpyfj0phpunit/Runner/Exception/ReflectionException.phpyf^ss<phpunit/Runner/Exception/UnsupportedPhptSectionException.phpyf &phpunit/Runner/Extension/Extension.phpyfsƲ2phpunit/Runner/Extension/ExtensionBootstrapper.phpZ yfZ Q?f/#phpunit/Runner/Extension/Facade.php +yf +~hؤ0phpunit/Runner/Extension/ParameterCollection.phpyfP|'phpunit/Runner/Extension/PharLoader.phpyf7<4phpunit/Runner/Filter/ExcludeGroupFilterIterator.phpkyfknA!phpunit/Runner/Filter/Factory.phpyfZ@-phpunit/Runner/Filter/GroupFilterIterator.php7yf7JE4phpunit/Runner/Filter/IncludeGroupFilterIterator.phpjyfjR_ ,phpunit/Runner/Filter/NameFilterIterator.php yf )6.phpunit/Runner/Filter/TestIdFilterIterator.phpyf6=phpunit/Runner/GarbageCollection/GarbageCollectionHandler.phpyf<0Kphpunit/Runner/GarbageCollection/Subscriber/ExecutionFinishedSubscriber.phpyf)Jphpunit/Runner/GarbageCollection/Subscriber/ExecutionStartedSubscriber.phpyf賓:phpunit/Runner/GarbageCollection/Subscriber/Subscriber.php!yf!@Fphpunit/Runner/GarbageCollection/Subscriber/TestFinishedSubscriber.phpZyfZKkߤphpunit/Runner/PhptTestCase.phpUyfUIg1phpunit/Runner/ResultCache/DefaultResultCache.php yf .phpunit/Runner/ResultCache/NullResultCache.phpyfs*phpunit/Runner/ResultCache/ResultCache.phpyfDM1phpunit/Runner/ResultCache/ResultCacheHandler.phpyffp4phpunit/Runner/ResultCache/Subscriber/Subscriber.phpyfUGphpunit/Runner/ResultCache/Subscriber/TestConsideredRiskySubscriber.phpyf#?phpunit/Runner/ResultCache/Subscriber/TestErroredSubscriber.phpyf>phpunit/Runner/ResultCache/Subscriber/TestFailedSubscriber.phpyfY@phpunit/Runner/ResultCache/Subscriber/TestFinishedSubscriber.phpTyfTr!*Hphpunit/Runner/ResultCache/Subscriber/TestMarkedIncompleteSubscriber.phpyfg6@phpunit/Runner/ResultCache/Subscriber/TestPreparedSubscriber.phpyf*\5?phpunit/Runner/ResultCache/Subscriber/TestSkippedSubscriber.phpNyfNa 1Ephpunit/Runner/ResultCache/Subscriber/TestSuiteFinishedSubscriber.phpyfk٤Dphpunit/Runner/ResultCache/Subscriber/TestSuiteStartedSubscriber.phpyfQ'phpunit/Runner/TestResult/Collector.phpHyfH@E$phpunit/Runner/TestResult/Facade.php yf oP#phpunit/Runner/TestResult/Issue.php yf hZD)phpunit/Runner/TestResult/PassedTests.php yf ҵ-cOphpunit/Runner/TestResult/Subscriber/BeforeTestClassMethodErroredSubscriber.php-yf- зCphpunit/Runner/TestResult/Subscriber/ExecutionStartedSubscriber.php#yf#f<3phpunit/Runner/TestResult/Subscriber/Subscriber.phpyfwFphpunit/Runner/TestResult/Subscriber/TestConsideredRiskySubscriber.phpyfZh>phpunit/Runner/TestResult/Subscriber/TestErroredSubscriber.phpyfAT=phpunit/Runner/TestResult/Subscriber/TestFailedSubscriber.phpyfؤ?phpunit/Runner/TestResult/Subscriber/TestFinishedSubscriber.phpyf~Gphpunit/Runner/TestResult/Subscriber/TestMarkedIncompleteSubscriber.phpyf?phpunit/Runner/TestResult/Subscriber/TestPreparedSubscriber.phpyfƓQphpunit/Runner/TestResult/Subscriber/TestRunnerTriggeredDeprecationSubscriber.phpyfn|" Mphpunit/Runner/TestResult/Subscriber/TestRunnerTriggeredWarningSubscriber.phpyfJä>phpunit/Runner/TestResult/Subscriber/TestSkippedSubscriber.phpyfDphpunit/Runner/TestResult/Subscriber/TestSuiteFinishedSubscriber.phpyf9_!Cphpunit/Runner/TestResult/Subscriber/TestSuiteSkippedSubscriber.phpyfd>ؤCphpunit/Runner/TestResult/Subscriber/TestSuiteStartedSubscriber.phpyfGKphpunit/Runner/TestResult/Subscriber/TestTriggeredDeprecationSubscriber.phpyfFEphpunit/Runner/TestResult/Subscriber/TestTriggeredErrorSubscriber.phpyfS.|Fphpunit/Runner/TestResult/Subscriber/TestTriggeredNoticeSubscriber.phpyfھNphpunit/Runner/TestResult/Subscriber/TestTriggeredPhpDeprecationSubscriber.phpyfzIphpunit/Runner/TestResult/Subscriber/TestTriggeredPhpNoticeSubscriber.phpyfmJphpunit/Runner/TestResult/Subscriber/TestTriggeredPhpWarningSubscriber.phpyfְRphpunit/Runner/TestResult/Subscriber/TestTriggeredPhpunitDeprecationSubscriber.php/yf/Lphpunit/Runner/TestResult/Subscriber/TestTriggeredPhpunitErrorSubscriber.php yf MzNphpunit/Runner/TestResult/Subscriber/TestTriggeredPhpunitWarningSubscriber.phpyf$"dGphpunit/Runner/TestResult/Subscriber/TestTriggeredWarningSubscriber.phpyf-j(phpunit/Runner/TestResult/TestResult.php<yf<Ym"phpunit/Runner/TestSuiteLoader.php$yf$j"phpunit/Runner/TestSuiteSorter.phpG%yfG%NȤphpunit/Runner/Version.phpTyfTϴphpunit/TextUI/Application.phpMUyfMU. ("phpunit/TextUI/Command/Command.phpyfG9phpunit/TextUI/Command/Commands/AtLeastVersionCommand.phpyfjd@phpunit/TextUI/Command/Commands/GenerateConfigurationCommand.phpyfuQ5phpunit/TextUI/Command/Commands/ListGroupsCommand.phpyfk9phpunit/TextUI/Command/Commands/ListTestSuitesCommand.phpyf:phpunit/TextUI/Command/Commands/ListTestsAsTextCommand.phpyfdʼ9phpunit/TextUI/Command/Commands/ListTestsAsXmlCommand.phpyfB?phpunit/TextUI/Command/Commands/MigrateConfigurationCommand.phpyf +6U3phpunit/TextUI/Command/Commands/ShowHelpCommand.php.yf.}36phpunit/TextUI/Command/Commands/ShowVersionCommand.phpRyfRv7phpunit/TextUI/Command/Commands/VersionCheckCommand.phpyfLW@phpunit/TextUI/Command/Commands/WarmCodeCoverageCacheCommand.php yf _,!phpunit/TextUI/Command/Result.php`yf`u`t(phpunit/TextUI/Configuration/Builder.phpyf^f,phpunit/TextUI/Configuration/Cli/Builder.php^yf^"2phpunit/TextUI/Configuration/Cli/Configuration.phpyf䷤.phpunit/TextUI/Configuration/Cli/Exception.phpyf%zE?phpunit/TextUI/Configuration/Cli/XmlConfigurationFileFinder.phpyf.-;phpunit/TextUI/Configuration/CodeCoverageFilterRegistry.phpRyfRs.phpunit/TextUI/Configuration/Configuration.phpyfV>Dphpunit/TextUI/Configuration/Exception/CannotFindSchemaException.php&yf&ؔ}Sphpunit/TextUI/Configuration/Exception/CodeCoverageReportNotConfiguredException.php$yf$Nphpunit/TextUI/Configuration/Exception/ConfigurationCannotBeBuiltException.phpyfZ$&4phpunit/TextUI/Configuration/Exception/Exception.phpyfGphpunit/TextUI/Configuration/Exception/FilterNotConfiguredException.phpyfgGLphpunit/TextUI/Configuration/Exception/IncludePathNotConfiguredException.phpyfxHphpunit/TextUI/Configuration/Exception/LoggingNotConfiguredException.phpyfY%>phpunit/TextUI/Configuration/Exception/NoBaselineException.phpyfP!:?phpunit/TextUI/Configuration/Exception/NoBootstrapException.phpyf`lƤDphpunit/TextUI/Configuration/Exception/NoCacheDirectoryException.phpyfCAphpunit/TextUI/Configuration/Exception/NoCliArgumentException.phpyf|J?RGphpunit/TextUI/Configuration/Exception/NoConfigurationFileException.phpyfMLphpunit/TextUI/Configuration/Exception/NoCoverageCacheDirectoryException.phpyf;Cphpunit/TextUI/Configuration/Exception/NoCustomCssFileException.phpyfʑ0Fphpunit/TextUI/Configuration/Exception/NoDefaultTestSuiteException.phpyf Lphpunit/TextUI/Configuration/Exception/NoPharExtensionDirectoryException.phpyf)R'phpunit/TextUI/Configuration/Merger.php{yf{<+phpunit/TextUI/Configuration/PhpHandler.phpTyfTԿb)phpunit/TextUI/Configuration/Registry.php yf |(+-phpunit/TextUI/Configuration/SourceFilter.phpyf&Wn&-phpunit/TextUI/Configuration/SourceMapper.php yf L1phpunit/TextUI/Configuration/TestSuiteBuilder.phpyf"p5h/phpunit/TextUI/Configuration/Value/Constant.php<yf<S:9phpunit/TextUI/Configuration/Value/ConstantCollection.phpyf?T.Aphpunit/TextUI/Configuration/Value/ConstantCollectionIterator.phpyfJ,)0phpunit/TextUI/Configuration/Value/Directory.phpyfx:phpunit/TextUI/Configuration/Value/DirectoryCollection.phpyf +Bphpunit/TextUI/Configuration/Value/DirectoryCollectionIterator.phpyfc9phpunit/TextUI/Configuration/Value/ExtensionBootstrap.phpyfPCphpunit/TextUI/Configuration/Value/ExtensionBootstrapCollection.phpyf!@TKphpunit/TextUI/Configuration/Value/ExtensionBootstrapCollectionIterator.phpyf+phpunit/TextUI/Configuration/Value/File.php.yf.5phpunit/TextUI/Configuration/Value/FileCollection.phpyf);=phpunit/TextUI/Configuration/Value/FileCollectionIterator.phphyfh^D6phpunit/TextUI/Configuration/Value/FilterDirectory.phpyf=K@phpunit/TextUI/Configuration/Value/FilterDirectoryCollection.php?yf?'mHphpunit/TextUI/Configuration/Value/FilterDirectoryCollectionIterator.phpyf4N,phpunit/TextUI/Configuration/Value/Group.phpyf$6phpunit/TextUI/Configuration/Value/GroupCollection.php3yf3K,>phpunit/TextUI/Configuration/Value/GroupCollectionIterator.phpsyfs;1phpunit/TextUI/Configuration/Value/IniSetting.php/yf/;phpunit/TextUI/Configuration/Value/IniSettingCollection.phpyfICCphpunit/TextUI/Configuration/Value/IniSettingCollectionIterator.phpyf2-*phpunit/TextUI/Configuration/Value/Php.phpNyfN}=-phpunit/TextUI/Configuration/Value/Source.phpvyfv|c4phpunit/TextUI/Configuration/Value/TestDirectory.phpyfL>phpunit/TextUI/Configuration/Value/TestDirectoryCollection.php(yf(mJFphpunit/TextUI/Configuration/Value/TestDirectoryCollectionIterator.phpyf8Q/phpunit/TextUI/Configuration/Value/TestFile.phpyf9phpunit/TextUI/Configuration/Value/TestFileCollection.phpyfdAphpunit/TextUI/Configuration/Value/TestFileCollectionIterator.php|yf| N0phpunit/TextUI/Configuration/Value/TestSuite.phpyfܤ:phpunit/TextUI/Configuration/Value/TestSuiteCollection.phpyfawBphpunit/TextUI/Configuration/Value/TestSuiteCollectionIterator.phpyf@>¤/phpunit/TextUI/Configuration/Value/Variable.phpyf,¤9phpunit/TextUI/Configuration/Value/VariableCollection.phpyfTr0Aphpunit/TextUI/Configuration/Value/VariableCollectionIterator.phpyfl븤>phpunit/TextUI/Configuration/Xml/CodeCoverage/CodeCoverage.phpzyfz?phpunit/TextUI/Configuration/Xml/CodeCoverage/Report/Clover.phpyf<<ߤBphpunit/TextUI/Configuration/Xml/CodeCoverage/Report/Cobertura.phpyf@—d?phpunit/TextUI/Configuration/Xml/CodeCoverage/Report/Crap4j.phpnyfnu@=phpunit/TextUI/Configuration/Xml/CodeCoverage/Report/Html.php +yf +Ho<phpunit/TextUI/Configuration/Xml/CodeCoverage/Report/Php.phpyfs=phpunit/TextUI/Configuration/Xml/CodeCoverage/Report/Text.phpzyfz<phpunit/TextUI/Configuration/Xml/CodeCoverage/Report/Xml.phpyf22phpunit/TextUI/Configuration/Xml/Configuration.php yf 6q9phpunit/TextUI/Configuration/Xml/DefaultConfiguration.php yf $+.phpunit/TextUI/Configuration/Xml/Exception.phpyfN5+.phpunit/TextUI/Configuration/Xml/Generator.phpyfoxH+phpunit/TextUI/Configuration/Xml/Groups.php`yf`@phpunit/TextUI/Configuration/Xml/LoadedFromFileConfiguration.phpyf(դ+phpunit/TextUI/Configuration/Xml/Loader.phphyfh~2phpunit/TextUI/Configuration/Xml/Logging/Junit.phpyf94phpunit/TextUI/Configuration/Xml/Logging/Logging.php +yf +U5phpunit/TextUI/Configuration/Xml/Logging/TeamCity.phpyf9phpunit/TextUI/Configuration/Xml/Logging/TestDox/Html.phpyf/Z9phpunit/TextUI/Configuration/Xml/Logging/TestDox/Text.phpyfY8O?phpunit/TextUI/Configuration/Xml/Migration/MigrationBuilder.php_ yf_ C_vHphpunit/TextUI/Configuration/Xml/Migration/MigrationBuilderException.phpyfbsAphpunit/TextUI/Configuration/Xml/Migration/MigrationException.php +yf +pIphpunit/TextUI/Configuration/Xml/Migration/Migrations/ConvertLogTypes.phpyfEނ$Pphpunit/TextUI/Configuration/Xml/Migration/Migrations/CoverageCloverToReport.phpVyfV$s¤Pphpunit/TextUI/Configuration/Xml/Migration/Migrations/CoverageCrap4jToReport.phpyfIONphpunit/TextUI/Configuration/Xml/Migration/Migrations/CoverageHtmlToReport.phpyfDMphpunit/TextUI/Configuration/Xml/Migration/Migrations/CoveragePhpToReport.phpDyfDRYNphpunit/TextUI/Configuration/Xml/Migration/Migrations/CoverageTextToReport.phpyfmi0_Mphpunit/TextUI/Configuration/Xml/Migration/Migrations/CoverageXmlToReport.phpIyfIKZphpunit/TextUI/Configuration/Xml/Migration/Migrations/IntroduceCacheDirectoryAttribute.php[yf[Rphpunit/TextUI/Configuration/Xml/Migration/Migrations/IntroduceCoverageElement.phpyf{Nphpunit/TextUI/Configuration/Xml/Migration/Migrations/LogToReportMigration.phpyf|Cphpunit/TextUI/Configuration/Xml/Migration/Migrations/Migration.phpyfwgephpunit/TextUI/Configuration/Xml/Migration/Migrations/MoveAttributesFromFilterWhitelistToCoverage.phpyfĉZphpunit/TextUI/Configuration/Xml/Migration/Migrations/MoveAttributesFromRootToCoverage.phpyf Yphpunit/TextUI/Configuration/Xml/Migration/Migrations/MoveCoverageDirectoriesToSource.phpVyfV˻?(Yphpunit/TextUI/Configuration/Xml/Migration/Migrations/MoveWhitelistExcludesToCoverage.phpyf9pYphpunit/TextUI/Configuration/Xml/Migration/Migrations/MoveWhitelistIncludesToCoverage.phpyf?ˤsphpunit/TextUI/Configuration/Xml/Migration/Migrations/RemoveBeStrictAboutResourceUsageDuringSmallTestsAttribute.phpyf>hphpunit/TextUI/Configuration/Xml/Migration/Migrations/RemoveBeStrictAboutTodoAnnotatedTestsAttribute.phplyfl=ܤXphpunit/TextUI/Configuration/Xml/Migration/Migrations/RemoveCacheResultFileAttribute.php<yf< Tphpunit/TextUI/Configuration/Xml/Migration/Migrations/RemoveCacheTokensAttribute.php0yf0|^X`phpunit/TextUI/Configuration/Xml/Migration/Migrations/RemoveConversionToExceptionsAttributes.php yf ʤfphpunit/TextUI/Configuration/Xml/Migration/Migrations/RemoveCoverageElementCacheDirectoryAttribute.phpyfsؤmphpunit/TextUI/Configuration/Xml/Migration/Migrations/RemoveCoverageElementProcessUncoveredFilesAttribute.phpyfKFäKphpunit/TextUI/Configuration/Xml/Migration/Migrations/RemoveEmptyFilter.phpyyfyJcIphpunit/TextUI/Configuration/Xml/Migration/Migrations/RemoveListeners.php&yf& 6bHphpunit/TextUI/Configuration/Xml/Migration/Migrations/RemoveLogTypes.phphyfhOphpunit/TextUI/Configuration/Xml/Migration/Migrations/RemoveLoggingElements.php&yf&Vphpunit/TextUI/Configuration/Xml/Migration/Migrations/RemoveNoInteractionAttribute.php6yf6:rQphpunit/TextUI/Configuration/Xml/Migration/Migrations/RemovePrinterAttributes.phpyfшTphpunit/TextUI/Configuration/Xml/Migration/Migrations/RemoveTestDoxGroupsElement.php5yf5OL=yYphpunit/TextUI/Configuration/Xml/Migration/Migrations/RemoveTestSuiteLoaderAttributes.phpyfPphpunit/TextUI/Configuration/Xml/Migration/Migrations/RemoveVerboseAttribute.php$yf$ۼ'_phpunit/TextUI/Configuration/Xml/Migration/Migrations/RenameBackupStaticAttributesAttribute.php#yf#zfphpunit/TextUI/Configuration/Xml/Migration/Migrations/RenameBeStrictAboutCoversAnnotationAttribute.phpMyfM<^phpunit/TextUI/Configuration/Xml/Migration/Migrations/RenameForceCoversAnnotationAttribute.php!yf!⇤Nphpunit/TextUI/Configuration/Xml/Migration/Migrations/UpdateSchemaLocation.phpyfeԤ7phpunit/TextUI/Configuration/Xml/Migration/Migrator.php9yf9BXG?phpunit/TextUI/Configuration/Xml/Migration/SnapshotNodeList.phpdyfdtmդ,phpunit/TextUI/Configuration/Xml/PHPUnit.phpx;yfx;Ophpunit/TextUI/Configuration/Xml/SchemaDetector/FailedSchemaDetectionResult.phpyf,F6¤Iphpunit/TextUI/Configuration/Xml/SchemaDetector/SchemaDetectionResult.php,yf,iBphpunit/TextUI/Configuration/Xml/SchemaDetector/SchemaDetector.phpyfsNSphpunit/TextUI/Configuration/Xml/SchemaDetector/SuccessfulSchemaDetectionResult.php*yf*71phpunit/TextUI/Configuration/Xml/SchemaFinder.php yf GW,4phpunit/TextUI/Configuration/Xml/TestSuiteMapper.phpyfPy?phpunit/TextUI/Configuration/Xml/Validator/ValidationResult.phpyfn8phpunit/TextUI/Configuration/Xml/Validator/Validator.php0yf0]6phpunit/TextUI/Exception/CannotOpenSocketException.phpyf:ɧ&phpunit/TextUI/Exception/Exception.phpyfD{i=phpunit/TextUI/Exception/ExtensionsNotConfiguredException.phpyf3f.3phpunit/TextUI/Exception/InvalidSocketException.phpyf0phpunit/TextUI/Exception/ReflectionException.phpyfJuv-phpunit/TextUI/Exception/RuntimeException.phpyf ;phpunit/TextUI/Exception/TestDirectoryNotFoundException.phpyfnL6phpunit/TextUI/Exception/TestFileNotFoundException.phpyf?lphpunit/TextUI/Help.phpb6yfb6hTAphpunit/TextUI/Output/Default/ProgressPrinter/ProgressPrinter.phpY0yfY0Scphpunit/TextUI/Output/Default/ProgressPrinter/Subscriber/BeforeTestClassMethodErroredSubscriber.phpEyfE?}Gphpunit/TextUI/Output/Default/ProgressPrinter/Subscriber/Subscriber.php:yf:7dZphpunit/TextUI/Output/Default/ProgressPrinter/Subscriber/TestConsideredRiskySubscriber.phpyfy,=Rphpunit/TextUI/Output/Default/ProgressPrinter/Subscriber/TestErroredSubscriber.phpyf])GQphpunit/TextUI/Output/Default/ProgressPrinter/Subscriber/TestFailedSubscriber.phpyflQSphpunit/TextUI/Output/Default/ProgressPrinter/Subscriber/TestFinishedSubscriber.phpyf׮[phpunit/TextUI/Output/Default/ProgressPrinter/Subscriber/TestMarkedIncompleteSubscriber.phpyf`'Sphpunit/TextUI/Output/Default/ProgressPrinter/Subscriber/TestPreparedSubscriber.phpyfUAaphpunit/TextUI/Output/Default/ProgressPrinter/Subscriber/TestRunnerExecutionStartedSubscriber.php#yf#sERphpunit/TextUI/Output/Default/ProgressPrinter/Subscriber/TestSkippedSubscriber.phpyfk_phpunit/TextUI/Output/Default/ProgressPrinter/Subscriber/TestTriggeredDeprecationSubscriber.php#yf#[Yphpunit/TextUI/Output/Default/ProgressPrinter/Subscriber/TestTriggeredErrorSubscriber.phpyfZphpunit/TextUI/Output/Default/ProgressPrinter/Subscriber/TestTriggeredNoticeSubscriber.phpyf9G}bphpunit/TextUI/Output/Default/ProgressPrinter/Subscriber/TestTriggeredPhpDeprecationSubscriber.php5yf5K]phpunit/TextUI/Output/Default/ProgressPrinter/Subscriber/TestTriggeredPhpNoticeSubscriber.phpyf6^phpunit/TextUI/Output/Default/ProgressPrinter/Subscriber/TestTriggeredPhpWarningSubscriber.phpyfofphpunit/TextUI/Output/Default/ProgressPrinter/Subscriber/TestTriggeredPhpunitDeprecationSubscriber.phpGyfG['Ӥbphpunit/TextUI/Output/Default/ProgressPrinter/Subscriber/TestTriggeredPhpunitWarningSubscriber.php/yf/[phpunit/TextUI/Output/Default/ProgressPrinter/Subscriber/TestTriggeredWarningSubscriber.php yf V/phpunit/TextUI/Output/Default/ResultPrinter.phpIyfIz}9phpunit/TextUI/Output/Default/UnexpectedOutputPrinter.php3yf3Og phpunit/TextUI/Output/Facade.php yf ʤ0phpunit/TextUI/Output/Printer/DefaultPrinter.php yf Y-phpunit/TextUI/Output/Printer/NullPrinter.php2yf2O)phpunit/TextUI/Output/Printer/Printer.phpyfJO(phpunit/TextUI/Output/SummaryPrinter.php$yf$fz/phpunit/TextUI/Output/TestDox/ResultPrinter.php#yf#,.*phpunit/TextUI/ShellExitCodeCalculator.phpyfє:phpunit/TextUI/TestRunner.phpkyfk(+phpunit/TextUI/TestSuiteFilterProcessor.phpyfȤphpunit/Util/Cloner.phpyfa˧phpunit/Util/Color.php&yf&$phpunit/Util/Exception/Exception.phpyf94phpunit/Util/Exception/InvalidDirectoryException.phpyfN/phpunit/Util/Exception/InvalidJsonException.phpyf)s:phpunit/Util/Exception/InvalidVersionOperatorException.phpyfw.phpunit/Util/Exception/PhpProcessException.phpyfXt-'phpunit/Util/Exception/XmlException.phpyf;phpunit/Util/ExcludeList.phpyf$phpunit/Util/Exporter.phpyfDphpunit/Util/Filesystem.phpyfANphpunit/Util/Filter.php yf rphpunit/Util/GlobalState.php5yf5Jphpunit/Util/Json.php +yf +`e`'phpunit/Util/PHP/AbstractPhpProcess.phpMyfM!ឤ&phpunit/Util/PHP/DefaultPhpProcess.php% yf% p+phpunit/Util/PHP/Template/TestCaseClass.tpl yf zU,phpunit/Util/PHP/Template/TestCaseMethod.tpl yf L+*phpunit/Util/PHP/Template/PhptTestCase.tplyf>uphpunit/Util/Reflection.php +yf +aKtphpunit/Util/Test.phpyfw (phpunit/Util/ThrowableToStringMapper.php7yf7\*phpunit/Util/VersionComparisonOperator.php1yf1 ̚phpunit/Util/Xml/Loader.phpX yfX ὶsphpunit/Util/Xml/Xml.phpyfsbom.xml$yf$8lschema/10.0.xsd=yf=|Hschema/10.1.xsdByfBܭrschema/10.2.xsdQEyfQEnschema/10.3.xsdFyfF3&schema/10.4.xsdGFyfGF?schema/8.5.xsdByfB2A[schema/9.0.xsd4Byf4B7wschema/9.1.xsdByfBq'8schema/9.2.xsdByfBc-schema/9.3.xsdEyfEqschema/9.4.xsd +Fyf +FDOFIschema/9.5.xsdDFyfDFs|sebastian-cli-parser/LICENSEyfksebastian-cli-parser/Parser.phpyfB%<sebastian-cli-parser/exceptions/AmbiguousOptionException.phpJyfJkK*-sebastian-cli-parser/exceptions/Exception.phpyyfy>Gsebastian-cli-parser/exceptions/OptionDoesNotAllowArgumentException.phpcyfcRjYJsebastian-cli-parser/exceptions/RequiredOptionArgumentMissingException.phplyflzQ:sebastian-cli-parser/exceptions/UnknownOptionException.phpCyfC*tP*sebastian-code-unit-reverse-lookup/LICENSEyff-sebastian-code-unit-reverse-lookup/Wizard.php yf C'sebastian-code-unit/ClassMethodUnit.phpyfv!sebastian-code-unit/ClassUnit.phpyfJk sebastian-code-unit/CodeUnit.php(yf(Hפ*sebastian-code-unit/CodeUnitCollection.phpyfj2sebastian-code-unit/CodeUnitCollectionIterator.php\yf\Ѥ sebastian-code-unit/FileUnit.phpyfow]$sebastian-code-unit/FunctionUnit.phpyf􋕤+sebastian-code-unit/InterfaceMethodUnit.php"yf"_!_%sebastian-code-unit/InterfaceUnit.phpyfš%sebastian-code-unit/LICENSEyfP@٤sebastian-code-unit/Mapper.phpyf"h8'sebastian-code-unit/TraitMethodUnit.phpyf%E:!sebastian-code-unit/TraitUnit.phpyfߕ,sebastian-code-unit/exceptions/Exception.phpwyfw5Ǥ;sebastian-code-unit/exceptions/InvalidCodeUnitException.phpyfMvԊ3sebastian-code-unit/exceptions/NoTraitException.phpyf]56sebastian-code-unit/exceptions/ReflectionException.phpyfcQ(sebastian-comparator/ArrayComparator.php yf -_o#sebastian-comparator/Comparator.phpRyfR?*sebastian-comparator/ComparisonFailure.php yf ***sebastian-comparator/DOMNodeComparator.phpyfc!h+sebastian-comparator/DateTimeComparator.phpYyfY6\,sebastian-comparator/ExceptionComparator.phpyfK\  sebastian-comparator/Factory.phpz yfz |=|sebastian-comparator/LICENSEyfT-sebastian-comparator/MockObjectComparator.php@yf@ASF *sebastian-comparator/NumericComparator.phpyf̌)sebastian-comparator/ObjectComparator.php +yf +2 }+sebastian-comparator/ResourceComparator.phpOyfOdn)sebastian-comparator/ScalarComparator.php yf c3sebastian-comparator/SplObjectStorageComparator.php yf bf'sebastian-comparator/TypeComparator.phpyfU +h-sebastian-comparator/exceptions/Exception.phpzyfzϤ4sebastian-comparator/exceptions/RuntimeException.phpyf_#sebastian-complexity/Calculator.phpyfߎ*.sebastian-complexity/Complexity/Complexity.php2yf2j;8sebastian-complexity/Complexity/ComplexityCollection.php + +yf + +u@sebastian-complexity/Complexity/ComplexityCollectionIterator.phpyfy,sebastian-complexity/Exception/Exception.phpzyfzȬˤ3sebastian-complexity/Exception/RuntimeException.phpyfsebastian-complexity/LICENSEyfP@٤=sebastian-complexity/Visitor/ComplexityCalculatingVisitor.phpyf9q2Gsebastian-complexity/Visitor/CyclomaticComplexityCalculatingVisitor.php8yf8ɮsebastian-diff/Chunk.php yf  sebastian-diff/Diff.phpSyfS:sebastian-diff/Differ.phpyf(3sebastian-diff/Exception/ConfigurationException.php*yf*^&sebastian-diff/Exception/Exception.phpnyfn/\5sebastian-diff/Exception/InvalidArgumentException.phpyf$ysebastian-diff/LICENSEyfܡ7sebastian-diff/Line.phpyfFJ5sebastian-diff/LongestCommonSubsequenceCalculator.phpyfep6Dsebastian-diff/MemoryEfficientLongestCommonSubsequenceCalculator.phpyfeE4sebastian-diff/Output/AbstractChunkOutputBuilder.phpyf?&/sebastian-diff/Output/DiffOnlyOutputBuilder.phpyf|4sebastian-diff/Output/DiffOutputBuilderInterface.phpyfpm8sebastian-diff/Output/StrictUnifiedDiffOutputBuilder.phpm(yfm(Tn2sebastian-diff/Output/UnifiedDiffOutputBuilder.phpyf-{Usebastian-diff/Parser.php yf &pѤBsebastian-diff/TimeEfficientLongestCommonSubsequenceCalculator.phpyfX.!sebastian-environment/Console.phpyf*E2sebastian-environment/LICENSEyf'|!sebastian-environment/Runtime.phpyfRsebastian-exporter/Exporter.php"yf"Jsebastian-exporter/LICENSEyfܡ7'sebastian-global-state/CodeExporter.phpg yfg V&sebastian-global-state/ExcludeList.php +yf + sebastian-global-state/LICENSEyfik#sebastian-global-state/Restorer.php3 yf3 #sebastian-global-state/Snapshot.php&yf&N/sebastian-global-state/exceptions/Exception.php}yf}ⴤ6sebastian-global-state/exceptions/RuntimeException.phpyf##sebastian-lines-of-code/Counter.phpyf Z~/sebastian-lines-of-code/Exception/Exception.php~yf~%>>sebastian-lines-of-code/Exception/IllogicalValuesException.phpyfr<sebastian-lines-of-code/Exception/NegativeValueException.phpyf&Ӥ6sebastian-lines-of-code/Exception/RuntimeException.phpyf)Ϲsebastian-lines-of-code/LICENSEyfP@٤/sebastian-lines-of-code/LineCountingVisitor.php yf S'sebastian-lines-of-code/LinesOfCode.php yf *sebastian-object-enumerator/Enumerator.phpyf=.sebastian-object-reflector/ObjectReflector.phpyf9O'sebastian-recursion-context/Context.phpyfSͤ#sebastian-recursion-context/LICENSEyfTsebastian-type/LICENSEyf sebastian-type/Parameter.phpyfE#sebastian-type/ReflectionMapper.phpyfl ܤsebastian-type/TypeName.phpyfٕ&sebastian-type/exception/Exception.phpnyfnH3-sebastian-type/exception/RuntimeException.phpyf;s֤$sebastian-type/type/CallableType.phpyfM{)!sebastian-type/type/FalseType.phpbyfbnޅ^)sebastian-type/type/GenericObjectType.php yf IG(sebastian-type/type/IntersectionType.php +yf +sؤ$sebastian-type/type/IterableType.phpyf~!sebastian-type/type/MixedType.php&yf&Ԥ!sebastian-type/type/NeverType.phpyfS sebastian-type/type/NullType.php!yf!n6"sebastian-type/type/ObjectType.php$yf$a`"sebastian-type/type/SimpleType.php*yf*'hH"sebastian-type/type/StaticType.phpyfs92 sebastian-type/type/TrueType.php]yf]zsebastian-type/type/Type.phpyf!sebastian-type/type/UnionType.php% yf% E#sebastian-type/type/UnknownType.phpyfH sebastian-type/type/VoidType.phpyf[sebastian-version/LICENSEyfVosebastian-version/Version.phpyfDtheseer-tokenizer/Exception.phpryfrmtheseer-tokenizer/LICENSEyfR ("theseer-tokenizer/NamespaceUri.phpJyfJW]+theseer-tokenizer/NamespaceUriException.php}yf}aՓtheseer-tokenizer/Token.phpyfK`¤%theseer-tokenizer/TokenCollection.phpyf rt.theseer-tokenizer/TokenCollectionException.phpyf5ɤtheseer-tokenizer/Tokenizer.php yf !N#theseer-tokenizer/XMLSerializer.phpyfu(դ.phpstorm.meta.phpyfɒs{ "_readme": [ "This file locks the dependencies of your project to a known state", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "5b99a9c8439c6b3977ee00d3697bebdb", + "content-hash": "e06728e5442edec84af96f94a889b4a7", "packages": [ - { - "name": "doctrine/deprecations", - "version": "1.1.3", - "source": { - "type": "git", - "url": "https://github.com/doctrine/deprecations.git", - "reference": "dfbaa3c2d2e9a9df1118213f3b8b0c597bb99fab" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/deprecations/zipball/dfbaa3c2d2e9a9df1118213f3b8b0c597bb99fab", - "reference": "dfbaa3c2d2e9a9df1118213f3b8b0c597bb99fab", - "shasum": "" - }, - "require": { - "php": "^7.1 || ^8.0" - }, - "require-dev": { - "doctrine/coding-standard": "^9", - "phpstan/phpstan": "1.4.10 || 1.10.15", - "phpstan/phpstan-phpunit": "^1.0", - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", - "psalm/plugin-phpunit": "0.18.4", - "psr/log": "^1 || ^2 || ^3", - "vimeo/psalm": "4.30.0 || 5.12.0" - }, - "suggest": { - "psr/log": "Allows logging deprecations via PSR-3 logger implementation" - }, - "type": "library", - "autoload": { - "psr-4": { - "Doctrine\\Deprecations\\": "lib/Doctrine/Deprecations" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options to disable all deprecations or selectively for packages.", - "homepage": "https://www.doctrine-project.org/", - "support": { - "issues": "https://github.com/doctrine/deprecations/issues", - "source": "https://github.com/doctrine/deprecations/tree/1.1.3" - }, - "time": "2024-01-30T19:34:25+00:00" - }, - { - "name": "doctrine/instantiator", - "version": "1.5.0", - "source": { - "type": "git", - "url": "https://github.com/doctrine/instantiator.git", - "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/0a0fa9780f5d4e507415a065172d26a98d02047b", - "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b", - "shasum": "" - }, - "require": { - "php": "^7.1 || ^8.0" - }, - "require-dev": { - "doctrine/coding-standard": "^9 || ^11", - "ext-pdo": "*", - "ext-phar": "*", - "phpbench/phpbench": "^0.16 || ^1", - "phpstan/phpstan": "^1.4", - "phpstan/phpstan-phpunit": "^1", - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", - "vimeo/psalm": "^4.30 || ^5.4" - }, - "type": "library", - "autoload": { - "psr-4": { - "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Marco Pivetta", - "email": "ocramius@gmail.com", - "homepage": "https://ocramius.github.io/" - } - ], - "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", - "homepage": "https://www.doctrine-project.org/projects/instantiator.html", - "keywords": [ - "constructor", - "instantiate" - ], - "support": { - "issues": "https://github.com/doctrine/instantiator/issues", - "source": "https://github.com/doctrine/instantiator/tree/1.5.0" - }, - "funding": [ - { - "url": "https://www.doctrine-project.org/sponsorship.html", - "type": "custom" - }, - { - "url": "https://www.patreon.com/phpdoctrine", - "type": "patreon" - }, - { - "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finstantiator", - "type": "tidelift" - } - ], - "time": "2022-12-30T00:15:36+00:00" - }, { "name": "myclabs/deep-copy", - "version": "1.11.1", + "version": "1.12.0", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c" + "reference": "3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", - "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c", + "reference": "3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c", "shasum": "" }, "require": { @@ -2761,11 +3131,12 @@ FDOFI }, "conflict": { "doctrine/collections": "<1.6.8", - "doctrine/common": "<2.13.3 || >=3,<3.2.2" + "doctrine/common": "<2.13.3 || >=3 <3.2.2" }, "require-dev": { "doctrine/collections": "^1.6.8", "doctrine/common": "^2.13.3 || ^3.2.2", + "phpspec/prophecy": "^1.10", "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" }, "type": "library", @@ -2791,7 +3162,7 @@ FDOFI ], "support": { "issues": "https://github.com/myclabs/DeepCopy/issues", - "source": "https://github.com/myclabs/DeepCopy/tree/1.11.1" + "source": "https://github.com/myclabs/DeepCopy/tree/1.12.0" }, "funding": [ { @@ -2799,29 +3170,31 @@ FDOFI "type": "tidelift" } ], - "time": "2023-03-08T13:26:56+00:00" + "time": "2024-06-12T14:39:25+00:00" }, { "name": "nikic/php-parser", - "version": "v4.19.1", + "version": "v5.1.0", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "4e1b88d21c69391150ace211e9eaf05810858d0b" + "reference": "683130c2ff8c2739f4822ff7ac5c873ec529abd1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/4e1b88d21c69391150ace211e9eaf05810858d0b", - "reference": "4e1b88d21c69391150ace211e9eaf05810858d0b", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/683130c2ff8c2739f4822ff7ac5c873ec529abd1", + "reference": "683130c2ff8c2739f4822ff7ac5c873ec529abd1", "shasum": "" }, "require": { + "ext-ctype": "*", + "ext-json": "*", "ext-tokenizer": "*", - "php": ">=7.1" + "php": ">=7.4" }, "require-dev": { "ircmaxell/php-yacc": "^0.0.7", - "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0" + "phpunit/phpunit": "^9.0" }, "bin": [ "bin/php-parse" @@ -2829,7 +3202,7 @@ FDOFI "type": "library", "extra": { "branch-alias": { - "dev-master": "4.9-dev" + "dev-master": "5.0-dev" } }, "autoload": { @@ -2853,9 +3226,9 @@ FDOFI ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v4.19.1" + "source": "https://github.com/nikic/PHP-Parser/tree/v5.1.0" }, - "time": "2024-03-17T08:10:35+00:00" + "time": "2024-07-01T20:03:41+00:00" }, { "name": "phar-io/manifest", @@ -2976,329 +3349,291 @@ FDOFI "time": "2022-02-21T01:04:05+00:00" }, { - "name": "phpdocumentor/reflection-common", - "version": "2.2.0", + "name": "phpunit/php-code-coverage", + "version": "10.1.15", "source": { "type": "git", - "url": "https://github.com/phpDocumentor/ReflectionCommon.git", - "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b" + "url": "https://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "5da8b1728acd1e6ffdf2ff32ffbdfd04307f26ae" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b", - "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/5da8b1728acd1e6ffdf2ff32ffbdfd04307f26ae", + "reference": "5da8b1728acd1e6ffdf2ff32ffbdfd04307f26ae", "shasum": "" }, "require": { - "php": "^7.2 || ^8.0" + "ext-dom": "*", + "ext-libxml": "*", + "ext-xmlwriter": "*", + "nikic/php-parser": "^4.18 || ^5.0", + "php": ">=8.1", + "phpunit/php-file-iterator": "^4.0", + "phpunit/php-text-template": "^3.0", + "sebastian/code-unit-reverse-lookup": "^3.0", + "sebastian/complexity": "^3.0", + "sebastian/environment": "^6.0", + "sebastian/lines-of-code": "^2.0", + "sebastian/version": "^4.0", + "theseer/tokenizer": "^1.2.0" + }, + "require-dev": { + "phpunit/phpunit": "^10.1" + }, + "suggest": { + "ext-pcov": "PHP extension that provides line coverage", + "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" }, "type": "library", "extra": { "branch-alias": { - "dev-2.x": "2.x-dev" + "dev-main": "10.1-dev" } }, "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": "src/" - } + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Jaap van Otterdijk", - "email": "opensource@ijaap.nl" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" } ], - "description": "Common reflection classes used by phpdocumentor to reflect the code structure", - "homepage": "http://www.phpdoc.org", + "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", + "homepage": "https://github.com/sebastianbergmann/php-code-coverage", "keywords": [ - "FQSEN", - "phpDocumentor", - "phpdoc", - "reflection", - "static analysis" + "coverage", + "testing", + "xunit" ], "support": { - "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues", - "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x" + "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", + "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.15" }, - "time": "2020-06-27T09:03:43+00:00" + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-06-29T08:25:15+00:00" }, { - "name": "phpdocumentor/reflection-docblock", - "version": "5.3.0", + "name": "phpunit/php-file-iterator", + "version": "4.1.0", "source": { "type": "git", - "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "622548b623e81ca6d78b721c5e029f4ce664f170" + "url": "https://github.com/sebastianbergmann/php-file-iterator.git", + "reference": "a95037b6d9e608ba092da1b23931e537cadc3c3c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/622548b623e81ca6d78b721c5e029f4ce664f170", - "reference": "622548b623e81ca6d78b721c5e029f4ce664f170", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/a95037b6d9e608ba092da1b23931e537cadc3c3c", + "reference": "a95037b6d9e608ba092da1b23931e537cadc3c3c", "shasum": "" }, "require": { - "ext-filter": "*", - "php": "^7.2 || ^8.0", - "phpdocumentor/reflection-common": "^2.2", - "phpdocumentor/type-resolver": "^1.3", - "webmozart/assert": "^1.9.1" + "php": ">=8.1" }, "require-dev": { - "mockery/mockery": "~1.3.2", - "psalm/phar": "^4.8" + "phpunit/phpunit": "^10.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "5.x-dev" + "dev-main": "4.0-dev" } }, "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": "src" - } + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Mike van Riel", - "email": "me@mikevanriel.com" - }, - { - "name": "Jaap van Otterdijk", - "email": "account@ijaap.nl" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" } ], - "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", + "description": "FilterIterator implementation that filters files based on a list of suffixes.", + "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", + "keywords": [ + "filesystem", + "iterator" + ], "support": { - "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", - "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.3.0" + "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", + "security": "https://github.com/sebastianbergmann/php-file-iterator/security/policy", + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/4.1.0" }, - "time": "2021-10-19T17:43:47+00:00" + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-08-31T06:24:48+00:00" }, { - "name": "phpdocumentor/type-resolver", - "version": "1.8.2", + "name": "phpunit/php-invoker", + "version": "4.0.0", "source": { "type": "git", - "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "153ae662783729388a584b4361f2545e4d841e3c" + "url": "https://github.com/sebastianbergmann/php-invoker.git", + "reference": "f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/153ae662783729388a584b4361f2545e4d841e3c", - "reference": "153ae662783729388a584b4361f2545e4d841e3c", + "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7", + "reference": "f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7", "shasum": "" }, "require": { - "doctrine/deprecations": "^1.0", - "php": "^7.3 || ^8.0", - "phpdocumentor/reflection-common": "^2.0", - "phpstan/phpdoc-parser": "^1.13" + "php": ">=8.1" }, "require-dev": { - "ext-tokenizer": "*", - "phpbench/phpbench": "^1.2", - "phpstan/extension-installer": "^1.1", - "phpstan/phpstan": "^1.8", - "phpstan/phpstan-phpunit": "^1.1", - "phpunit/phpunit": "^9.5", - "rector/rector": "^0.13.9", - "vimeo/psalm": "^4.25" + "ext-pcntl": "*", + "phpunit/phpunit": "^10.0" + }, + "suggest": { + "ext-pcntl": "*" }, "type": "library", "extra": { "branch-alias": { - "dev-1.x": "1.x-dev" + "dev-main": "4.0-dev" } }, "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": "src" - } + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Mike van Riel", - "email": "me@mikevanriel.com" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" } ], - "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", + "description": "Invoke callables with a timeout", + "homepage": "https://github.com/sebastianbergmann/php-invoker/", + "keywords": [ + "process" + ], "support": { - "issues": "https://github.com/phpDocumentor/TypeResolver/issues", - "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.8.2" + "issues": "https://github.com/sebastianbergmann/php-invoker/issues", + "source": "https://github.com/sebastianbergmann/php-invoker/tree/4.0.0" }, - "time": "2024-02-23T11:10:43+00:00" + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:56:09+00:00" }, { - "name": "phpspec/prophecy", - "version": "v1.19.0", + "name": "phpunit/php-text-template", + "version": "3.0.1", "source": { "type": "git", - "url": "https://github.com/phpspec/prophecy.git", - "reference": "67a759e7d8746d501c41536ba40cd9c0a07d6a87" + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "0c7b06ff49e3d5072f057eb1fa59258bf287a748" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/67a759e7d8746d501c41536ba40cd9c0a07d6a87", - "reference": "67a759e7d8746d501c41536ba40cd9c0a07d6a87", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/0c7b06ff49e3d5072f057eb1fa59258bf287a748", + "reference": "0c7b06ff49e3d5072f057eb1fa59258bf287a748", "shasum": "" }, "require": { - "doctrine/instantiator": "^1.2 || ^2.0", - "php": "^7.2 || 8.0.* || 8.1.* || 8.2.* || 8.3.*", - "phpdocumentor/reflection-docblock": "^5.2", - "sebastian/comparator": "^3.0 || ^4.0 || ^5.0 || ^6.0", - "sebastian/recursion-context": "^3.0 || ^4.0 || ^5.0 || ^6.0" + "php": ">=8.1" }, "require-dev": { - "phpspec/phpspec": "^6.0 || ^7.0", - "phpstan/phpstan": "^1.9", - "phpunit/phpunit": "^8.0 || ^9.0 || ^10.0" + "phpunit/phpunit": "^10.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.x-dev" + "dev-main": "3.0-dev" } }, "autoload": { - "psr-4": { - "Prophecy\\": "src/Prophecy" - } + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Konstantin Kudryashov", - "email": "ever.zet@gmail.com", - "homepage": "http://everzet.com" - }, - { - "name": "Marcello Duarte", - "email": "marcello.duarte@gmail.com" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" } ], - "description": "Highly opinionated mocking framework for PHP 5.3+", - "homepage": "https://github.com/phpspec/prophecy", + "description": "Simple template engine.", + "homepage": "https://github.com/sebastianbergmann/php-text-template/", "keywords": [ - "Double", - "Dummy", - "dev", - "fake", - "mock", - "spy", - "stub" + "template" ], "support": { - "issues": "https://github.com/phpspec/prophecy/issues", - "source": "https://github.com/phpspec/prophecy/tree/v1.19.0" - }, - "time": "2024-02-29T11:52:51+00:00" - }, - { - "name": "phpstan/phpdoc-parser", - "version": "1.28.0", - "source": { - "type": "git", - "url": "https://github.com/phpstan/phpdoc-parser.git", - "reference": "cd06d6b1a1b3c75b0b83f97577869fd85a3cd4fb" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/cd06d6b1a1b3c75b0b83f97577869fd85a3cd4fb", - "reference": "cd06d6b1a1b3c75b0b83f97577869fd85a3cd4fb", - "shasum": "" - }, - "require": { - "php": "^7.2 || ^8.0" - }, - "require-dev": { - "doctrine/annotations": "^2.0", - "nikic/php-parser": "^4.15", - "php-parallel-lint/php-parallel-lint": "^1.2", - "phpstan/extension-installer": "^1.0", - "phpstan/phpstan": "^1.5", - "phpstan/phpstan-phpunit": "^1.1", - "phpstan/phpstan-strict-rules": "^1.0", - "phpunit/phpunit": "^9.5", - "symfony/process": "^5.2" + "issues": "https://github.com/sebastianbergmann/php-text-template/issues", + "security": "https://github.com/sebastianbergmann/php-text-template/security/policy", + "source": "https://github.com/sebastianbergmann/php-text-template/tree/3.0.1" }, - "type": "library", - "autoload": { - "psr-4": { - "PHPStan\\PhpDocParser\\": [ - "src/" - ] + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" ], - "description": "PHPDoc parser with support for nullable, intersection and generic types", - "support": { - "issues": "https://github.com/phpstan/phpdoc-parser/issues", - "source": "https://github.com/phpstan/phpdoc-parser/tree/1.28.0" - }, - "time": "2024-04-03T18:51:33+00:00" + "time": "2023-08-31T14:07:24+00:00" }, { - "name": "phpunit/php-code-coverage", - "version": "9.2.31", + "name": "phpunit/php-timer", + "version": "6.0.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "48c34b5d8d983006bd2adc2d0de92963b9155965" + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "e2a2d67966e740530f4a3343fe2e030ffdc1161d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/48c34b5d8d983006bd2adc2d0de92963b9155965", - "reference": "48c34b5d8d983006bd2adc2d0de92963b9155965", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/e2a2d67966e740530f4a3343fe2e030ffdc1161d", + "reference": "e2a2d67966e740530f4a3343fe2e030ffdc1161d", "shasum": "" }, "require": { - "ext-dom": "*", - "ext-libxml": "*", - "ext-xmlwriter": "*", - "nikic/php-parser": "^4.18 || ^5.0", - "php": ">=7.3", - "phpunit/php-file-iterator": "^3.0.3", - "phpunit/php-text-template": "^2.0.2", - "sebastian/code-unit-reverse-lookup": "^2.0.2", - "sebastian/complexity": "^2.0", - "sebastian/environment": "^5.1.2", - "sebastian/lines-of-code": "^1.0.3", - "sebastian/version": "^3.0.1", - "theseer/tokenizer": "^1.2.0" + "php": ">=8.1" }, "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "suggest": { - "ext-pcov": "PHP extension that provides line coverage", - "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" + "phpunit/phpunit": "^10.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "9.2-dev" + "dev-main": "6.0-dev" } }, "autoload": { @@ -3317,17 +3652,14 @@ FDOFI "role": "lead" } ], - "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", - "homepage": "https://github.com/sebastianbergmann/php-code-coverage", + "description": "Utility class for timing", + "homepage": "https://github.com/sebastianbergmann/php-timer/", "keywords": [ - "coverage", - "testing", - "xunit" + "timer" ], "support": { - "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", - "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.31" + "issues": "https://github.com/sebastianbergmann/php-timer/issues", + "source": "https://github.com/sebastianbergmann/php-timer/tree/6.0.0" }, "funding": [ { @@ -3335,32 +3667,32 @@ FDOFI "type": "github" } ], - "time": "2024-03-02T06:37:42+00:00" + "time": "2023-02-03T06:57:52+00:00" }, { - "name": "phpunit/php-file-iterator", - "version": "3.0.6", + "name": "sebastian/cli-parser", + "version": "2.0.1", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf" + "url": "https://github.com/sebastianbergmann/cli-parser.git", + "reference": "c34583b87e7b7a8055bf6c450c2c77ce32a24084" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", - "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/c34583b87e7b7a8055bf6c450c2c77ce32a24084", + "reference": "c34583b87e7b7a8055bf6c450c2c77ce32a24084", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.1" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^10.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0-dev" + "dev-main": "2.0-dev" } }, "autoload": { @@ -3379,15 +3711,12 @@ FDOFI "role": "lead" } ], - "description": "FilterIterator implementation that filters files based on a list of suffixes.", - "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", - "keywords": [ - "filesystem", - "iterator" - ], + "description": "Library for parsing CLI options", + "homepage": "https://github.com/sebastianbergmann/cli-parser", "support": { - "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", - "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.6" + "issues": "https://github.com/sebastianbergmann/cli-parser/issues", + "security": "https://github.com/sebastianbergmann/cli-parser/security/policy", + "source": "https://github.com/sebastianbergmann/cli-parser/tree/2.0.1" }, "funding": [ { @@ -3395,36 +3724,32 @@ FDOFI "type": "github" } ], - "time": "2021-12-02T12:48:52+00:00" + "time": "2024-03-02T07:12:49+00:00" }, { - "name": "phpunit/php-invoker", - "version": "3.1.1", + "name": "sebastian/code-unit", + "version": "2.0.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/php-invoker.git", - "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67" + "url": "https://github.com/sebastianbergmann/code-unit.git", + "reference": "a81fee9eef0b7a76af11d121767abc44c104e503" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/5a10147d0aaf65b58940a0b72f71c9ac0423cc67", - "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/a81fee9eef0b7a76af11d121767abc44c104e503", + "reference": "a81fee9eef0b7a76af11d121767abc44c104e503", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.1" }, "require-dev": { - "ext-pcntl": "*", - "phpunit/phpunit": "^9.3" - }, - "suggest": { - "ext-pcntl": "*" + "phpunit/phpunit": "^10.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.1-dev" + "dev-main": "2.0-dev" } }, "autoload": { @@ -3443,14 +3768,11 @@ FDOFI "role": "lead" } ], - "description": "Invoke callables with a timeout", - "homepage": "https://github.com/sebastianbergmann/php-invoker/", - "keywords": [ - "process" - ], + "description": "Collection of value objects that represent the PHP code units", + "homepage": "https://github.com/sebastianbergmann/code-unit", "support": { - "issues": "https://github.com/sebastianbergmann/php-invoker/issues", - "source": "https://github.com/sebastianbergmann/php-invoker/tree/3.1.1" + "issues": "https://github.com/sebastianbergmann/code-unit/issues", + "source": "https://github.com/sebastianbergmann/code-unit/tree/2.0.0" }, "funding": [ { @@ -3458,32 +3780,32 @@ FDOFI "type": "github" } ], - "time": "2020-09-28T05:58:55+00:00" + "time": "2023-02-03T06:58:43+00:00" }, { - "name": "phpunit/php-text-template", - "version": "2.0.4", + "name": "sebastian/code-unit-reverse-lookup", + "version": "3.0.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/php-text-template.git", - "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28" + "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", + "reference": "5e3a687f7d8ae33fb362c5c0743794bbb2420a1d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", - "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/5e3a687f7d8ae33fb362c5c0743794bbb2420a1d", + "reference": "5e3a687f7d8ae33fb362c5c0743794bbb2420a1d", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.1" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^10.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0-dev" + "dev-main": "3.0-dev" } }, "autoload": { @@ -3498,18 +3820,14 @@ FDOFI "authors": [ { "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" + "email": "sebastian@phpunit.de" } ], - "description": "Simple template engine.", - "homepage": "https://github.com/sebastianbergmann/php-text-template/", - "keywords": [ - "template" - ], + "description": "Looks up which function or method a line of code belongs to", + "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", "support": { - "issues": "https://github.com/sebastianbergmann/php-text-template/issues", - "source": "https://github.com/sebastianbergmann/php-text-template/tree/2.0.4" + "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", + "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/3.0.0" }, "funding": [ { @@ -3517,32 +3835,36 @@ FDOFI "type": "github" } ], - "time": "2020-10-26T05:33:50+00:00" + "time": "2023-02-03T06:59:15+00:00" }, { - "name": "phpunit/php-timer", - "version": "5.0.3", + "name": "sebastian/comparator", + "version": "5.0.1", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2" + "url": "https://github.com/sebastianbergmann/comparator.git", + "reference": "2db5010a484d53ebf536087a70b4a5423c102372" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", - "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/2db5010a484d53ebf536087a70b4a5423c102372", + "reference": "2db5010a484d53ebf536087a70b4a5423c102372", "shasum": "" }, "require": { - "php": ">=7.3" + "ext-dom": "*", + "ext-mbstring": "*", + "php": ">=8.1", + "sebastian/diff": "^5.0", + "sebastian/exporter": "^5.0" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^10.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "5.0-dev" + "dev-main": "5.0-dev" } }, "autoload": { @@ -3557,18 +3879,32 @@ FDOFI "authors": [ { "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" } ], - "description": "Utility class for timing", - "homepage": "https://github.com/sebastianbergmann/php-timer/", + "description": "Provides the functionality to compare PHP values for equality", + "homepage": "https://github.com/sebastianbergmann/comparator", "keywords": [ - "timer" + "comparator", + "compare", + "equality" ], "support": { - "issues": "https://github.com/sebastianbergmann/php-timer/issues", - "source": "https://github.com/sebastianbergmann/php-timer/tree/5.0.3" + "issues": "https://github.com/sebastianbergmann/comparator/issues", + "security": "https://github.com/sebastianbergmann/comparator/security/policy", + "source": "https://github.com/sebastianbergmann/comparator/tree/5.0.1" }, "funding": [ { @@ -3576,32 +3912,33 @@ FDOFI "type": "github" } ], - "time": "2020-10-26T13:16:10+00:00" + "time": "2023-08-14T13:18:12+00:00" }, { - "name": "sebastian/cli-parser", - "version": "1.0.2", + "name": "sebastian/complexity", + "version": "3.2.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/cli-parser.git", - "reference": "2b56bea83a09de3ac06bb18b92f068e60cc6f50b" + "url": "https://github.com/sebastianbergmann/complexity.git", + "reference": "68ff824baeae169ec9f2137158ee529584553799" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/2b56bea83a09de3ac06bb18b92f068e60cc6f50b", - "reference": "2b56bea83a09de3ac06bb18b92f068e60cc6f50b", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/68ff824baeae169ec9f2137158ee529584553799", + "reference": "68ff824baeae169ec9f2137158ee529584553799", "shasum": "" }, "require": { - "php": ">=7.3" + "nikic/php-parser": "^4.18 || ^5.0", + "php": ">=8.1" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^10.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0-dev" + "dev-main": "3.2-dev" } }, "autoload": { @@ -3620,11 +3957,12 @@ FDOFI "role": "lead" } ], - "description": "Library for parsing CLI options", - "homepage": "https://github.com/sebastianbergmann/cli-parser", + "description": "Library for calculating the complexity of PHP code units", + "homepage": "https://github.com/sebastianbergmann/complexity", "support": { - "issues": "https://github.com/sebastianbergmann/cli-parser/issues", - "source": "https://github.com/sebastianbergmann/cli-parser/tree/1.0.2" + "issues": "https://github.com/sebastianbergmann/complexity/issues", + "security": "https://github.com/sebastianbergmann/complexity/security/policy", + "source": "https://github.com/sebastianbergmann/complexity/tree/3.2.0" }, "funding": [ { @@ -3632,32 +3970,33 @@ FDOFI "type": "github" } ], - "time": "2024-03-02T06:27:43+00:00" + "time": "2023-12-21T08:37:17+00:00" }, { - "name": "sebastian/code-unit", - "version": "1.0.8", + "name": "sebastian/diff", + "version": "5.1.1", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/code-unit.git", - "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120" + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "c41e007b4b62af48218231d6c2275e4c9b975b2e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/1fc9f64c0927627ef78ba436c9b17d967e68e120", - "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/c41e007b4b62af48218231d6c2275e4c9b975b2e", + "reference": "c41e007b4b62af48218231d6c2275e4c9b975b2e", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.1" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^10.0", + "symfony/process": "^6.4" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0-dev" + "dev-main": "5.1-dev" } }, "autoload": { @@ -3672,15 +4011,25 @@ FDOFI "authors": [ { "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" + "email": "sebastian@phpunit.de" + }, + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" } ], - "description": "Collection of value objects that represent the PHP code units", - "homepage": "https://github.com/sebastianbergmann/code-unit", + "description": "Diff implementation", + "homepage": "https://github.com/sebastianbergmann/diff", + "keywords": [ + "diff", + "udiff", + "unidiff", + "unified diff" + ], "support": { - "issues": "https://github.com/sebastianbergmann/code-unit/issues", - "source": "https://github.com/sebastianbergmann/code-unit/tree/1.0.8" + "issues": "https://github.com/sebastianbergmann/diff/issues", + "security": "https://github.com/sebastianbergmann/diff/security/policy", + "source": "https://github.com/sebastianbergmann/diff/tree/5.1.1" }, "funding": [ { @@ -3688,32 +4037,35 @@ FDOFI "type": "github" } ], - "time": "2020-10-26T13:08:54+00:00" + "time": "2024-03-02T07:15:17+00:00" }, { - "name": "sebastian/code-unit-reverse-lookup", - "version": "2.0.3", + "name": "sebastian/environment", + "version": "6.1.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", - "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5" + "url": "https://github.com/sebastianbergmann/environment.git", + "reference": "8074dbcd93529b357029f5cc5058fd3e43666984" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", - "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/8074dbcd93529b357029f5cc5058fd3e43666984", + "reference": "8074dbcd93529b357029f5cc5058fd3e43666984", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.1" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^10.0" + }, + "suggest": { + "ext-posix": "*" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0-dev" + "dev-main": "6.1-dev" } }, "autoload": { @@ -3731,11 +4083,17 @@ FDOFI "email": "sebastian@phpunit.de" } ], - "description": "Looks up which function or method a line of code belongs to", - "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", + "description": "Provides functionality to handle HHVM/PHP environments", + "homepage": "https://github.com/sebastianbergmann/environment", + "keywords": [ + "Xdebug", + "environment", + "hhvm" + ], "support": { - "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", - "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/2.0.3" + "issues": "https://github.com/sebastianbergmann/environment/issues", + "security": "https://github.com/sebastianbergmann/environment/security/policy", + "source": "https://github.com/sebastianbergmann/environment/tree/6.1.0" }, "funding": [ { @@ -3743,34 +4101,34 @@ FDOFI "type": "github" } ], - "time": "2020-09-28T05:30:19+00:00" + "time": "2024-03-23T08:47:14+00:00" }, { - "name": "sebastian/comparator", - "version": "4.0.8", + "name": "sebastian/exporter", + "version": "5.1.2", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "fa0f136dd2334583309d32b62544682ee972b51a" + "url": "https://github.com/sebastianbergmann/exporter.git", + "reference": "955288482d97c19a372d3f31006ab3f37da47adf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/fa0f136dd2334583309d32b62544682ee972b51a", - "reference": "fa0f136dd2334583309d32b62544682ee972b51a", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/955288482d97c19a372d3f31006ab3f37da47adf", + "reference": "955288482d97c19a372d3f31006ab3f37da47adf", "shasum": "" }, "require": { - "php": ">=7.3", - "sebastian/diff": "^4.0", - "sebastian/exporter": "^4.0" + "ext-mbstring": "*", + "php": ">=8.1", + "sebastian/recursion-context": "^5.0" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^10.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.0-dev" + "dev-main": "5.1-dev" } }, "autoload": { @@ -3795,21 +4153,25 @@ FDOFI "name": "Volker Dusch", "email": "github@wallbash.com" }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + }, { "name": "Bernhard Schussek", - "email": "bschussek@2bepublished.at" + "email": "bschussek@gmail.com" } ], - "description": "Provides the functionality to compare PHP values for equality", - "homepage": "https://github.com/sebastianbergmann/comparator", + "description": "Provides the functionality to export PHP variables for visualization", + "homepage": "https://www.github.com/sebastianbergmann/exporter", "keywords": [ - "comparator", - "compare", - "equality" + "export", + "exporter" ], "support": { - "issues": "https://github.com/sebastianbergmann/comparator/issues", - "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.8" + "issues": "https://github.com/sebastianbergmann/exporter/issues", + "security": "https://github.com/sebastianbergmann/exporter/security/policy", + "source": "https://github.com/sebastianbergmann/exporter/tree/5.1.2" }, "funding": [ { @@ -3817,33 +4179,95 @@ FDOFI "type": "github" } ], - "time": "2022-09-14T12:41:17+00:00" + "time": "2024-03-02T07:17:12+00:00" }, { - "name": "sebastian/complexity", - "version": "2.0.3", + "name": "sebastian/global-state", + "version": "6.0.2", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/complexity.git", - "reference": "25f207c40d62b8b7aa32f5ab026c53561964053a" + "url": "https://github.com/sebastianbergmann/global-state.git", + "reference": "987bafff24ecc4c9ac418cab1145b96dd6e9cbd9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/987bafff24ecc4c9ac418cab1145b96dd6e9cbd9", + "reference": "987bafff24ecc4c9ac418cab1145b96dd6e9cbd9", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "sebastian/object-reflector": "^3.0", + "sebastian/recursion-context": "^5.0" + }, + "require-dev": { + "ext-dom": "*", + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Snapshotting of global state", + "homepage": "https://www.github.com/sebastianbergmann/global-state", + "keywords": [ + "global state" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/global-state/issues", + "security": "https://github.com/sebastianbergmann/global-state/security/policy", + "source": "https://github.com/sebastianbergmann/global-state/tree/6.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-02T07:19:19+00:00" + }, + { + "name": "sebastian/lines-of-code", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/lines-of-code.git", + "reference": "856e7f6a75a84e339195d48c556f23be2ebf75d0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/25f207c40d62b8b7aa32f5ab026c53561964053a", - "reference": "25f207c40d62b8b7aa32f5ab026c53561964053a", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/856e7f6a75a84e339195d48c556f23be2ebf75d0", + "reference": "856e7f6a75a84e339195d48c556f23be2ebf75d0", "shasum": "" }, "require": { "nikic/php-parser": "^4.18 || ^5.0", - "php": ">=7.3" + "php": ">=8.1" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^10.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0-dev" + "dev-main": "2.0-dev" } }, "autoload": { @@ -3862,11 +4286,12 @@ FDOFI "role": "lead" } ], - "description": "Library for calculating the complexity of PHP code units", - "homepage": "https://github.com/sebastianbergmann/complexity", + "description": "Library for counting the lines of code in PHP source code", + "homepage": "https://github.com/sebastianbergmann/lines-of-code", "support": { - "issues": "https://github.com/sebastianbergmann/complexity/issues", - "source": "https://github.com/sebastianbergmann/complexity/tree/2.0.3" + "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", + "security": "https://github.com/sebastianbergmann/lines-of-code/security/policy", + "source": "https://github.com/sebastianbergmann/lines-of-code/tree/2.0.2" }, "funding": [ { @@ -3874,33 +4299,34 @@ FDOFI "type": "github" } ], - "time": "2023-12-22T06:19:30+00:00" + "time": "2023-12-21T08:38:20+00:00" }, { - "name": "sebastian/diff", - "version": "4.0.6", + "name": "sebastian/object-enumerator", + "version": "5.0.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "ba01945089c3a293b01ba9badc29ad55b106b0bc" + "url": "https://github.com/sebastianbergmann/object-enumerator.git", + "reference": "202d0e344a580d7f7d04b3fafce6933e59dae906" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/ba01945089c3a293b01ba9badc29ad55b106b0bc", - "reference": "ba01945089c3a293b01ba9badc29ad55b106b0bc", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/202d0e344a580d7f7d04b3fafce6933e59dae906", + "reference": "202d0e344a580d7f7d04b3fafce6933e59dae906", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.1", + "sebastian/object-reflector": "^3.0", + "sebastian/recursion-context": "^5.0" }, "require-dev": { - "phpunit/phpunit": "^9.3", - "symfony/process": "^4.2 || ^5" + "phpunit/phpunit": "^10.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.0-dev" + "dev-main": "5.0-dev" } }, "autoload": { @@ -3916,23 +4342,13 @@ FDOFI { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de" - }, - { - "name": "Kore Nordmann", - "email": "mail@kore-nordmann.de" } ], - "description": "Diff implementation", - "homepage": "https://github.com/sebastianbergmann/diff", - "keywords": [ - "diff", - "udiff", - "unidiff", - "unified diff" - ], + "description": "Traverses array structures and object graphs to enumerate all referenced objects", + "homepage": "https://github.com/sebastianbergmann/object-enumerator/", "support": { - "issues": "https://github.com/sebastianbergmann/diff/issues", - "source": "https://github.com/sebastianbergmann/diff/tree/4.0.6" + "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", + "source": "https://github.com/sebastianbergmann/object-enumerator/tree/5.0.0" }, "funding": [ { @@ -3940,35 +4356,32 @@ FDOFI "type": "github" } ], - "time": "2024-03-02T06:30:58+00:00" + "time": "2023-02-03T07:08:32+00:00" }, { - "name": "sebastian/environment", - "version": "5.1.5", + "name": "sebastian/object-reflector", + "version": "3.0.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed" + "url": "https://github.com/sebastianbergmann/object-reflector.git", + "reference": "24ed13d98130f0e7122df55d06c5c4942a577957" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", - "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/24ed13d98130f0e7122df55d06c5c4942a577957", + "reference": "24ed13d98130f0e7122df55d06c5c4942a577957", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.1" }, "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "suggest": { - "ext-posix": "*" + "phpunit/phpunit": "^10.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "5.1-dev" + "dev-main": "3.0-dev" } }, "autoload": { @@ -3986,16 +4399,11 @@ FDOFI "email": "sebastian@phpunit.de" } ], - "description": "Provides functionality to handle HHVM/PHP environments", - "homepage": "http://www.github.com/sebastianbergmann/environment", - "keywords": [ - "Xdebug", - "environment", - "hhvm" - ], + "description": "Allows reflection of object attributes, including inherited and non-public ones", + "homepage": "https://github.com/sebastianbergmann/object-reflector/", "support": { - "issues": "https://github.com/sebastianbergmann/environment/issues", - "source": "https://github.com/sebastianbergmann/environment/tree/5.1.5" + "issues": "https://github.com/sebastianbergmann/object-reflector/issues", + "source": "https://github.com/sebastianbergmann/object-reflector/tree/3.0.0" }, "funding": [ { @@ -4003,34 +4411,32 @@ FDOFI "type": "github" } ], - "time": "2023-02-03T06:03:51+00:00" + "time": "2023-02-03T07:06:18+00:00" }, { - "name": "sebastian/exporter", - "version": "4.0.6", + "name": "sebastian/recursion-context", + "version": "5.0.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "78c00df8f170e02473b682df15bfcdacc3d32d72" + "url": "https://github.com/sebastianbergmann/recursion-context.git", + "reference": "05909fb5bc7df4c52992396d0116aed689f93712" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/78c00df8f170e02473b682df15bfcdacc3d32d72", - "reference": "78c00df8f170e02473b682df15bfcdacc3d32d72", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/05909fb5bc7df4c52992396d0116aed689f93712", + "reference": "05909fb5bc7df4c52992396d0116aed689f93712", "shasum": "" }, "require": { - "php": ">=7.3", - "sebastian/recursion-context": "^4.0" + "php": ">=8.1" }, "require-dev": { - "ext-mbstring": "*", - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^10.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.0-dev" + "dev-main": "5.0-dev" } }, "autoload": { @@ -4051,28 +4457,16 @@ FDOFI "name": "Jeff Welch", "email": "whatthejeff@gmail.com" }, - { - "name": "Volker Dusch", - "email": "github@wallbash.com" - }, { "name": "Adam Harvey", "email": "aharvey@php.net" - }, - { - "name": "Bernhard Schussek", - "email": "bschussek@gmail.com" } ], - "description": "Provides the functionality to export PHP variables for visualization", - "homepage": "https://www.github.com/sebastianbergmann/exporter", - "keywords": [ - "export", - "exporter" - ], + "description": "Provides functionality to recursively process PHP variables", + "homepage": "https://github.com/sebastianbergmann/recursion-context", "support": { - "issues": "https://github.com/sebastianbergmann/exporter/issues", - "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.6" + "issues": "https://github.com/sebastianbergmann/recursion-context/issues", + "source": "https://github.com/sebastianbergmann/recursion-context/tree/5.0.0" }, "funding": [ { @@ -4080,38 +4474,32 @@ FDOFI "type": "github" } ], - "time": "2024-03-02T06:33:00+00:00" + "time": "2023-02-03T07:05:40+00:00" }, { - "name": "sebastian/global-state", - "version": "5.0.7", + "name": "sebastian/type", + "version": "4.0.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "bca7df1f32ee6fe93b4d4a9abbf69e13a4ada2c9" + "url": "https://github.com/sebastianbergmann/type.git", + "reference": "462699a16464c3944eefc02ebdd77882bd3925bf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bca7df1f32ee6fe93b4d4a9abbf69e13a4ada2c9", - "reference": "bca7df1f32ee6fe93b4d4a9abbf69e13a4ada2c9", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/462699a16464c3944eefc02ebdd77882bd3925bf", + "reference": "462699a16464c3944eefc02ebdd77882bd3925bf", "shasum": "" }, "require": { - "php": ">=7.3", - "sebastian/object-reflector": "^2.0", - "sebastian/recursion-context": "^4.0" + "php": ">=8.1" }, "require-dev": { - "ext-dom": "*", - "phpunit/phpunit": "^9.3" - }, - "suggest": { - "ext-uopz": "*" + "phpunit/phpunit": "^10.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "5.0-dev" + "dev-main": "4.0-dev" } }, "autoload": { @@ -4126,17 +4514,15 @@ FDOFI "authors": [ { "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" + "email": "sebastian@phpunit.de", + "role": "lead" } ], - "description": "Snapshotting of global state", - "homepage": "http://www.github.com/sebastianbergmann/global-state", - "keywords": [ - "global state" - ], + "description": "Collection of value objects that represent the types of the PHP type system", + "homepage": "https://github.com/sebastianbergmann/type", "support": { - "issues": "https://github.com/sebastianbergmann/global-state/issues", - "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.7" + "issues": "https://github.com/sebastianbergmann/type/issues", + "source": "https://github.com/sebastianbergmann/type/tree/4.0.0" }, "funding": [ { @@ -4144,371 +4530,29 @@ FDOFI "type": "github" } ], - "time": "2024-03-02T06:35:11+00:00" + "time": "2023-02-03T07:10:45+00:00" }, { - "name": "sebastian/lines-of-code", - "version": "1.0.4", + "name": "sebastian/version", + "version": "4.0.1", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/lines-of-code.git", - "reference": "e1e4a170560925c26d424b6a03aed157e7dcc5c5" + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "c51fa83a5d8f43f1402e3f32a005e6262244ef17" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/e1e4a170560925c26d424b6a03aed157e7dcc5c5", - "reference": "e1e4a170560925c26d424b6a03aed157e7dcc5c5", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c51fa83a5d8f43f1402e3f32a005e6262244ef17", + "reference": "c51fa83a5d8f43f1402e3f32a005e6262244ef17", "shasum": "" }, "require": { - "nikic/php-parser": "^4.18 || ^5.0", - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" + "php": ">=8.1" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Library for counting the lines of code in PHP source code", - "homepage": "https://github.com/sebastianbergmann/lines-of-code", - "support": { - "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", - "source": "https://github.com/sebastianbergmann/lines-of-code/tree/1.0.4" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-12-22T06:20:34+00:00" - }, - { - "name": "sebastian/object-enumerator", - "version": "4.0.4", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/object-enumerator.git", - "reference": "5c9eeac41b290a3712d88851518825ad78f45c71" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/5c9eeac41b290a3712d88851518825ad78f45c71", - "reference": "5c9eeac41b290a3712d88851518825ad78f45c71", - "shasum": "" - }, - "require": { - "php": ">=7.3", - "sebastian/object-reflector": "^2.0", - "sebastian/recursion-context": "^4.0" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Traverses array structures and object graphs to enumerate all referenced objects", - "homepage": "https://github.com/sebastianbergmann/object-enumerator/", - "support": { - "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", - "source": "https://github.com/sebastianbergmann/object-enumerator/tree/4.0.4" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-10-26T13:12:34+00:00" - }, - { - "name": "sebastian/object-reflector", - "version": "2.0.4", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/object-reflector.git", - "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", - "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Allows reflection of object attributes, including inherited and non-public ones", - "homepage": "https://github.com/sebastianbergmann/object-reflector/", - "support": { - "issues": "https://github.com/sebastianbergmann/object-reflector/issues", - "source": "https://github.com/sebastianbergmann/object-reflector/tree/2.0.4" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-10-26T13:14:26+00:00" - }, - { - "name": "sebastian/recursion-context", - "version": "4.0.5", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", - "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Adam Harvey", - "email": "aharvey@php.net" - } - ], - "description": "Provides functionality to recursively process PHP variables", - "homepage": "https://github.com/sebastianbergmann/recursion-context", - "support": { - "issues": "https://github.com/sebastianbergmann/recursion-context/issues", - "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.5" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-02-03T06:07:39+00:00" - }, - { - "name": "sebastian/resource-operations", - "version": "3.0.4", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/resource-operations.git", - "reference": "05d5692a7993ecccd56a03e40cd7e5b09b1d404e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/05d5692a7993ecccd56a03e40cd7e5b09b1d404e", - "reference": "05d5692a7993ecccd56a03e40cd7e5b09b1d404e", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "3.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Provides a list of PHP built-in functions that operate on resources", - "homepage": "https://www.github.com/sebastianbergmann/resource-operations", - "support": { - "source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.4" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2024-03-14T16:00:52+00:00" - }, - { - "name": "sebastian/type", - "version": "3.2.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/type.git", - "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", - "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.5" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.2-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Collection of value objects that represent the types of the PHP type system", - "homepage": "https://github.com/sebastianbergmann/type", - "support": { - "issues": "https://github.com/sebastianbergmann/type/issues", - "source": "https://github.com/sebastianbergmann/type/tree/3.2.1" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-02-03T06:13:03+00:00" - }, - { - "name": "sebastian/version", - "version": "3.0.2", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/version.git", - "reference": "c6c1022351a901512170118436c764e473f6de8c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c6c1022351a901512170118436c764e473f6de8c", - "reference": "c6c1022351a901512170118436c764e473f6de8c", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.0-dev" + "dev-main": "4.0-dev" } }, "autoload": { @@ -4531,7 +4575,7 @@ FDOFI "homepage": "https://github.com/sebastianbergmann/version", "support": { "issues": "https://github.com/sebastianbergmann/version/issues", - "source": "https://github.com/sebastianbergmann/version/tree/3.0.2" + "source": "https://github.com/sebastianbergmann/version/tree/4.0.1" }, "funding": [ { @@ -4539,7 +4583,7 @@ FDOFI "type": "github" } ], - "time": "2020-09-28T06:39:44+00:00" + "time": "2023-02-07T11:34:05+00:00" }, { "name": "theseer/tokenizer", @@ -4590,64 +4634,6 @@ FDOFI } ], "time": "2024-03-03T12:36:25+00:00" - }, - { - "name": "webmozart/assert", - "version": "1.11.0", - "source": { - "type": "git", - "url": "https://github.com/webmozarts/assert.git", - "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/webmozarts/assert/zipball/11cb2199493b2f8a3b53e7f19068fc6aac760991", - "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991", - "shasum": "" - }, - "require": { - "ext-ctype": "*", - "php": "^7.2 || ^8.0" - }, - "conflict": { - "phpstan/phpstan": "<0.12.20", - "vimeo/psalm": "<4.6.1 || 4.6.2" - }, - "require-dev": { - "phpunit/phpunit": "^8.5.13" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.10-dev" - } - }, - "autoload": { - "psr-4": { - "Webmozart\\Assert\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Bernhard Schussek", - "email": "bschussek@gmail.com" - } - ], - "description": "Assertions to validate method input/output with nice error messages.", - "keywords": [ - "assert", - "check", - "validate" - ], - "support": { - "issues": "https://github.com/webmozarts/assert/issues", - "source": "https://github.com/webmozarts/assert/tree/1.11.0" - }, - "time": "2022-06-03T18:03:27+00:00" } ], "packages-dev": [], @@ -4657,7 +4643,7 @@ FDOFI "prefer-stable": true, "prefer-lowest": false, "platform": { - "php": ">=7.3", + "php": ">=8.1", "ext-dom": "*", "ext-json": "*", "ext-libxml": "*", @@ -4667,836 +4653,182 @@ FDOFI }, "platform-dev": [], "platform-overrides": { - "php": "7.3.0" + "php": "8.1.0" }, "plugin-api-version": "2.6.0" } +phpunit/phpunit: 10.5.26 +myclabs/deep-copy: 1.12.0 +nikic/php-parser: v5.1.0 +phar-io/manifest: 2.0.4 +phar-io/version: 3.2.1 +phpunit/php-code-coverage: 10.1.15 +phpunit/php-file-iterator: 4.1.0 +phpunit/php-invoker: 4.0.0 +phpunit/php-text-template: 3.0.1 +phpunit/php-timer: 6.0.0 +sebastian/cli-parser: 2.0.1 +sebastian/code-unit: 2.0.0 +sebastian/code-unit-reverse-lookup: 3.0.0 +sebastian/comparator: 5.0.1 +sebastian/complexity: 3.2.0 +sebastian/diff: 5.1.1 +sebastian/environment: 6.1.0 +sebastian/exporter: 5.1.2 +sebastian/global-state: 6.0.2 +sebastian/lines-of-code: 2.0.2 +sebastian/object-enumerator: 5.0.0 +sebastian/object-reflector: 3.0.0 +sebastian/recursion-context: 5.0.0 +sebastian/type: 4.0.0 +sebastian/version: 4.0.1 +theseer/tokenizer: 1.2.3 |null */ - private static $type; - /** @var LoggerInterface|null */ - private static $logger; - /** @var array */ - private static $ignoredPackages = []; - /** @var array */ - private static $triggeredDeprecations = []; - /** @var array */ - private static $ignoredLinks = []; - /** @var bool */ - private static $deduplication = \true; /** - * Trigger a deprecation for the given package and identfier. - * - * The link should point to a Github issue or Wiki entry detailing the - * deprecation. It is additionally used to de-duplicate the trigger of the - * same deprecation during a request. - * - * @param float|int|string $args + * @var object[] List of objects copied. */ - public static function trigger(string $package, string $link, string $message, ...$args) : void - { - $type = self::$type ?? self::getTypeFromEnv(); - if ($type === self::TYPE_NONE) { - return; - } - if (isset(self::$ignoredLinks[$link])) { - return; - } - if (array_key_exists($link, self::$triggeredDeprecations)) { - self::$triggeredDeprecations[$link]++; - } else { - self::$triggeredDeprecations[$link] = 1; - } - if (self::$deduplication === \true && self::$triggeredDeprecations[$link] > 1) { - return; - } - if (isset(self::$ignoredPackages[$package])) { - return; - } - $backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2); - $message = sprintf($message, ...$args); - self::delegateTriggerToBackend($message, $backtrace, $link, $package); - } + private $hashMap = []; /** - * Trigger a deprecation for the given package and identifier when called from outside. - * - * "Outside" means we assume that $package is currently installed as a - * dependency and the caller is not a file in that package. When $package - * is installed as a root package then deprecations triggered from the - * tests folder are also considered "outside". - * - * This deprecation method assumes that you are using Composer to install - * the dependency and are using the default /vendor/ folder and not a - * Composer plugin to change the install location. The assumption is also - * that $package is the exact composer packge name. + * Filters to apply. * - * Compared to {@link trigger()} this method causes some overhead when - * deprecation tracking is enabled even during deduplication, because it - * needs to call {@link debug_backtrace()} + * @var array Array of ['filter' => Filter, 'matcher' => Matcher] pairs. + */ + private $filters = []; + /** + * Type Filters to apply. * - * @param float|int|string $args + * @var array Array of ['filter' => Filter, 'matcher' => Matcher] pairs. */ - public static function triggerIfCalledFromOutside(string $package, string $link, string $message, ...$args) : void - { - $type = self::$type ?? self::getTypeFromEnv(); - if ($type === self::TYPE_NONE) { - return; - } - $backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2); - // first check that the caller is not from a tests folder, in which case we always let deprecations pass - if (isset($backtrace[1]['file'], $backtrace[0]['file']) && strpos($backtrace[1]['file'], DIRECTORY_SEPARATOR . 'tests' . DIRECTORY_SEPARATOR) === \false) { - $path = DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR . str_replace('/', DIRECTORY_SEPARATOR, $package) . DIRECTORY_SEPARATOR; - if (strpos($backtrace[0]['file'], $path) === \false) { - return; - } - if (strpos($backtrace[1]['file'], $path) !== \false) { - return; - } - } - if (isset(self::$ignoredLinks[$link])) { - return; - } - if (array_key_exists($link, self::$triggeredDeprecations)) { - self::$triggeredDeprecations[$link]++; - } else { - self::$triggeredDeprecations[$link] = 1; - } - if (self::$deduplication === \true && self::$triggeredDeprecations[$link] > 1) { - return; - } - if (isset(self::$ignoredPackages[$package])) { - return; - } - $message = sprintf($message, ...$args); - self::delegateTriggerToBackend($message, $backtrace, $link, $package); - } + private $typeFilters = []; + /** + * @var bool + */ + private $skipUncloneable = \false; + /** + * @var bool + */ + private $useCloneMethod; /** - * @param list $backtrace + * @param bool $useCloneMethod If set to true, when an object implements the __clone() function, it will be used + * instead of the regular deep cloning. */ - private static function delegateTriggerToBackend(string $message, array $backtrace, string $link, string $package) : void + public function __construct($useCloneMethod = \false) { - $type = self::$type ?? self::getTypeFromEnv(); - if (($type & self::TYPE_PSR_LOGGER) > 0) { - $context = ['file' => $backtrace[0]['file'] ?? null, 'line' => $backtrace[0]['line'] ?? null, 'package' => $package, 'link' => $link]; - assert(self::$logger !== null); - self::$logger->notice($message, $context); - } - if (!(($type & self::TYPE_TRIGGER_ERROR) > 0)) { - return; - } - $message .= sprintf(' (%s:%d called by %s:%d, %s, package %s)', self::basename($backtrace[0]['file'] ?? 'native code'), $backtrace[0]['line'] ?? 0, self::basename($backtrace[1]['file'] ?? 'native code'), $backtrace[1]['line'] ?? 0, $link, $package); - @trigger_error($message, E_USER_DEPRECATED); + $this->useCloneMethod = $useCloneMethod; + $this->addTypeFilter(new ArrayObjectFilter($this), new TypeMatcher(ArrayObject::class)); + $this->addTypeFilter(new DateIntervalFilter(), new TypeMatcher(DateInterval::class)); + $this->addTypeFilter(new SplDoublyLinkedListFilter($this), new TypeMatcher(SplDoublyLinkedList::class)); } /** - * A non-local-aware version of PHPs basename function. + * If enabled, will not throw an exception when coming across an uncloneable property. + * + * @param $skipUncloneable + * + * @return $this */ - private static function basename(string $filename) : string + public function skipUncloneable($skipUncloneable = \true) { - $pos = strrpos($filename, DIRECTORY_SEPARATOR); - if ($pos === \false) { - return $filename; - } - return substr($filename, $pos + 1); + $this->skipUncloneable = $skipUncloneable; + return $this; } - public static function enableTrackingDeprecations() : void + /** + * Deep copies the given object. + * + * @param mixed $object + * + * @return mixed + */ + public function copy($object) { - self::$type = self::$type ?? 0; - self::$type |= self::TYPE_TRACK_DEPRECATIONS; + $this->hashMap = []; + return $this->recursiveCopy($object); } - public static function enableWithTriggerError() : void + public function addFilter(Filter $filter, Matcher $matcher) { - self::$type = self::$type ?? 0; - self::$type |= self::TYPE_TRIGGER_ERROR; + $this->filters[] = ['matcher' => $matcher, 'filter' => $filter]; } - public static function enableWithPsrLogger(LoggerInterface $logger) : void + public function prependFilter(Filter $filter, Matcher $matcher) { - self::$type = self::$type ?? 0; - self::$type |= self::TYPE_PSR_LOGGER; - self::$logger = $logger; + array_unshift($this->filters, ['matcher' => $matcher, 'filter' => $filter]); } - public static function withoutDeduplication() : void + public function addTypeFilter(TypeFilter $filter, TypeMatcher $matcher) { - self::$deduplication = \false; + $this->typeFilters[] = ['matcher' => $matcher, 'filter' => $filter]; } - public static function disable() : void + private function recursiveCopy($var) { - self::$type = self::TYPE_NONE; - self::$logger = null; - self::$deduplication = \true; - self::$ignoredLinks = []; - foreach (self::$triggeredDeprecations as $link => $count) { - self::$triggeredDeprecations[$link] = 0; + // Matches Type Filter + if ($filter = $this->getFirstMatchedTypeFilter($this->typeFilters, $var)) { + return $filter->apply($var); } - } - public static function ignorePackage(string $packageName) : void - { - self::$ignoredPackages[$packageName] = \true; - } - public static function ignoreDeprecations(string ...$links) : void - { - foreach ($links as $link) { - self::$ignoredLinks[$link] = \true; + // Resource + if (is_resource($var)) { + return $var; } - } - public static function getUniqueTriggeredDeprecationsCount() : int - { - return array_reduce(self::$triggeredDeprecations, static function (int $carry, int $count) { - return $carry + $count; - }, 0); - } - /** - * Returns each triggered deprecation link identifier and the amount of occurrences. - * - * @return array - */ - public static function getTriggeredDeprecations() : array - { - return self::$triggeredDeprecations; - } - /** - * @return int-mask-of - */ - private static function getTypeFromEnv() : int - { - switch ($_SERVER['DOCTRINE_DEPRECATIONS'] ?? $_ENV['DOCTRINE_DEPRECATIONS'] ?? null) { - case 'trigger': - self::$type = self::TYPE_TRIGGER_ERROR; - break; - case 'track': - self::$type = self::TYPE_TRACK_DEPRECATIONS; - break; - default: - self::$type = self::TYPE_NONE; - break; + // Array + if (is_array($var)) { + return $this->copyArray($var); } - return self::$type; - } -} - */ - private $doctrineDeprecationsExpectations = []; - /** @var array */ - private $doctrineNoDeprecationsExpectations = []; - public function expectDeprecationWithIdentifier(string $identifier) : void - { - $this->doctrineDeprecationsExpectations[$identifier] = Deprecation::getTriggeredDeprecations()[$identifier] ?? 0; - } - public function expectNoDeprecationWithIdentifier(string $identifier) : void - { - $this->doctrineNoDeprecationsExpectations[$identifier] = Deprecation::getTriggeredDeprecations()[$identifier] ?? 0; + // Scalar + if (!is_object($var)) { + return $var; + } + // Enum + if (\PHP_VERSION_ID >= 80100 && enum_exists(get_class($var))) { + return $var; + } + // Object + return $this->copyObject($var); } /** - * @before + * Copy an array + * @param array $array + * @return array */ - public function enableDeprecationTracking() : void + private function copyArray(array $array) { - Deprecation::enableTrackingDeprecations(); + foreach ($array as $key => $value) { + $array[$key] = $this->recursiveCopy($value); + } + return $array; } /** - * @after + * Copies an object. + * + * @param object $object + * + * @throws CloneException + * + * @return object */ - public function verifyDeprecationsAreTriggered() : void + private function copyObject($object) { - foreach ($this->doctrineDeprecationsExpectations as $identifier => $expectation) { - $actualCount = Deprecation::getTriggeredDeprecations()[$identifier] ?? 0; - $this->assertTrue($actualCount > $expectation, sprintf("Expected deprecation with identifier '%s' was not triggered by code executed in test.", $identifier)); - } - foreach ($this->doctrineNoDeprecationsExpectations as $identifier => $expectation) { - $actualCount = Deprecation::getTriggeredDeprecations()[$identifier] ?? 0; - $this->assertTrue($actualCount === $expectation, sprintf("Expected deprecation with identifier '%s' was triggered by code executed in test, but expected not to.", $identifier)); - } - } -} -Copyright (c) 2020-2021 Doctrine Project - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -of the Software, and to permit persons to whom the Software is furnished to do -so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - $reflectionClass - * - * @template T of object - */ - public static function fromAbstractClass(ReflectionClass $reflectionClass) : self - { - return new self(sprintf('The provided class "%s" is abstract, and cannot be instantiated', $reflectionClass->getName())); - } - public static function fromEnum(string $className) : self - { - return new self(sprintf('The provided class "%s" is an enum, and cannot be instantiated', $className)); - } -} - $reflectionClass - * - * @template T of object - */ - public static function fromSerializationTriggeredException(ReflectionClass $reflectionClass, Exception $exception) : self - { - return new self(sprintf('An exception was raised while trying to instantiate an instance of "%s" via un-serialization', $reflectionClass->getName()), 0, $exception); - } - /** - * @phpstan-param ReflectionClass $reflectionClass - * - * @template T of object - */ - public static function fromUncleanUnSerialization(ReflectionClass $reflectionClass, string $errorString, int $errorCode, string $errorFile, int $errorLine) : self - { - return new self(sprintf('Could not produce an instance of "%s" via un-serialization, since an error was triggered ' . 'in file "%s" at line "%d"', $reflectionClass->getName(), $errorFile, $errorLine), 0, new Exception($errorString, $errorCode)); - } -} - $className - * - * @return object - * @phpstan-return T - * - * @throws ExceptionInterface - * - * @template T of object - */ - public function instantiate($className) - { - if (isset(self::$cachedCloneables[$className])) { - /** @phpstan-var T */ - $cachedCloneable = self::$cachedCloneables[$className]; - return clone $cachedCloneable; - } - if (isset(self::$cachedInstantiators[$className])) { - $factory = self::$cachedInstantiators[$className]; - return $factory(); - } - return $this->buildAndCacheFromFactory($className); - } - /** - * Builds the requested object and caches it in static properties for performance - * - * @phpstan-param class-string $className - * - * @return object - * @phpstan-return T - * - * @template T of object - */ - private function buildAndCacheFromFactory(string $className) - { - $factory = self::$cachedInstantiators[$className] = $this->buildFactory($className); - $instance = $factory(); - if ($this->isSafeToClone(new ReflectionClass($instance))) { - self::$cachedCloneables[$className] = clone $instance; - } - return $instance; - } - /** - * Builds a callable capable of instantiating the given $className without - * invoking its constructor. - * - * @phpstan-param class-string $className - * - * @phpstan-return callable(): T - * - * @throws InvalidArgumentException - * @throws UnexpectedValueException - * @throws ReflectionException - * - * @template T of object - */ - private function buildFactory(string $className) : callable - { - $reflectionClass = $this->getReflectionClass($className); - if ($this->isInstantiableViaReflection($reflectionClass)) { - return [$reflectionClass, 'newInstanceWithoutConstructor']; - } - $serializedString = sprintf('%s:%d:"%s":0:{}', is_subclass_of($className, Serializable::class) ? self::SERIALIZATION_FORMAT_USE_UNSERIALIZER : self::SERIALIZATION_FORMAT_AVOID_UNSERIALIZER, strlen($className), $className); - $this->checkIfUnSerializationIsSupported($reflectionClass, $serializedString); - return static function () use($serializedString) { - return unserialize($serializedString); - }; - } - /** - * @phpstan-param class-string $className - * - * @phpstan-return ReflectionClass - * - * @throws InvalidArgumentException - * @throws ReflectionException - * - * @template T of object - */ - private function getReflectionClass(string $className) : ReflectionClass - { - if (!class_exists($className)) { - throw InvalidArgumentException::fromNonExistingClass($className); - } - if (PHP_VERSION_ID >= 80100 && enum_exists($className, \false)) { - throw InvalidArgumentException::fromEnum($className); - } - $reflection = new ReflectionClass($className); - if ($reflection->isAbstract()) { - throw InvalidArgumentException::fromAbstractClass($reflection); - } - return $reflection; - } - /** - * @phpstan-param ReflectionClass $reflectionClass - * - * @throws UnexpectedValueException - * - * @template T of object - */ - private function checkIfUnSerializationIsSupported(ReflectionClass $reflectionClass, string $serializedString) : void - { - set_error_handler(static function (int $code, string $message, string $file, int $line) use($reflectionClass, &$error) : bool { - $error = UnexpectedValueException::fromUncleanUnSerialization($reflectionClass, $message, $code, $file, $line); - return \true; - }); - try { - $this->attemptInstantiationViaUnSerialization($reflectionClass, $serializedString); - } finally { - restore_error_handler(); - } - if ($error) { - throw $error; - } - } - /** - * @phpstan-param ReflectionClass $reflectionClass - * - * @throws UnexpectedValueException - * - * @template T of object - */ - private function attemptInstantiationViaUnSerialization(ReflectionClass $reflectionClass, string $serializedString) : void - { - try { - unserialize($serializedString); - } catch (Exception $exception) { - throw UnexpectedValueException::fromSerializationTriggeredException($reflectionClass, $exception); - } - } - /** - * @phpstan-param ReflectionClass $reflectionClass - * - * @template T of object - */ - private function isInstantiableViaReflection(ReflectionClass $reflectionClass) : bool - { - return !($this->hasInternalAncestors($reflectionClass) && $reflectionClass->isFinal()); - } - /** - * Verifies whether the given class is to be considered internal - * - * @phpstan-param ReflectionClass $reflectionClass - * - * @template T of object - */ - private function hasInternalAncestors(ReflectionClass $reflectionClass) : bool - { - do { - if ($reflectionClass->isInternal()) { - return \true; - } - $reflectionClass = $reflectionClass->getParentClass(); - } while ($reflectionClass); - return \false; - } - /** - * Checks if a class is cloneable - * - * Classes implementing `__clone` cannot be safely cloned, as that may cause side-effects. - * - * @phpstan-param ReflectionClass $reflectionClass - * - * @template T of object - */ - private function isSafeToClone(ReflectionClass $reflectionClass) : bool - { - return $reflectionClass->isCloneable() && !$reflectionClass->hasMethod('__clone') && !$reflectionClass->isSubclassOf(ArrayIterator::class); - } -} - $className - * - * @return object - * @phpstan-return T - * - * @throws ExceptionInterface - * - * @template T of object - */ - public function instantiate($className); -} -Copyright (c) 2014 Doctrine Project - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -of the Software, and to permit persons to whom the Software is furnished to do -so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -phpunit/phpunit: 9.6.19 -doctrine/deprecations: 1.1.3 -doctrine/instantiator: 1.5.0 -myclabs/deep-copy: 1.11.1 -nikic/php-parser: v4.19.1 -phar-io/manifest: 2.0.4 -phar-io/version: 3.2.1 -phpdocumentor/reflection-common: 2.2.0 -phpdocumentor/reflection-docblock: 5.3.0 -phpdocumentor/type-resolver: 1.8.2 -phpspec/prophecy: v1.19.0 -phpstan/phpdoc-parser: 1.28.0 -phpunit/php-code-coverage: 9.2.31 -phpunit/php-file-iterator: 3.0.6 -phpunit/php-invoker: 3.1.1 -phpunit/php-text-template: 2.0.4 -phpunit/php-timer: 5.0.3 -sebastian/cli-parser: 1.0.2 -sebastian/code-unit: 1.0.8 -sebastian/code-unit-reverse-lookup: 2.0.3 -sebastian/comparator: 4.0.8 -sebastian/complexity: 2.0.3 -sebastian/diff: 4.0.6 -sebastian/environment: 5.1.5 -sebastian/exporter: 4.0.6 -sebastian/global-state: 5.0.7 -sebastian/lines-of-code: 1.0.4 -sebastian/object-enumerator: 4.0.4 -sebastian/object-reflector: 2.0.4 -sebastian/recursion-context: 4.0.5 -sebastian/resource-operations: 3.0.4 -sebastian/type: 3.2.1 -sebastian/version: 3.0.2 -theseer/tokenizer: 1.2.3 -webmozart/assert: 1.11.0 - Filter, 'matcher' => Matcher] pairs. - */ - private $filters = []; - /** - * Type Filters to apply. - * - * @var array Array of ['filter' => Filter, 'matcher' => Matcher] pairs. - */ - private $typeFilters = []; - /** - * @var bool - */ - private $skipUncloneable = \false; - /** - * @var bool - */ - private $useCloneMethod; - /** - * @param bool $useCloneMethod If set to true, when an object implements the __clone() function, it will be used - * instead of the regular deep cloning. - */ - public function __construct($useCloneMethod = \false) - { - $this->useCloneMethod = $useCloneMethod; - $this->addTypeFilter(new ArrayObjectFilter($this), new TypeMatcher(ArrayObject::class)); - $this->addTypeFilter(new DateIntervalFilter(), new TypeMatcher(DateInterval::class)); - $this->addTypeFilter(new SplDoublyLinkedListFilter($this), new TypeMatcher(SplDoublyLinkedList::class)); - } - /** - * If enabled, will not throw an exception when coming across an uncloneable property. - * - * @param $skipUncloneable - * - * @return $this - */ - public function skipUncloneable($skipUncloneable = \true) - { - $this->skipUncloneable = $skipUncloneable; - return $this; - } - /** - * Deep copies the given object. - * - * @param mixed $object - * - * @return mixed - */ - public function copy($object) - { - $this->hashMap = []; - return $this->recursiveCopy($object); - } - public function addFilter(Filter $filter, Matcher $matcher) - { - $this->filters[] = ['matcher' => $matcher, 'filter' => $filter]; - } - public function prependFilter(Filter $filter, Matcher $matcher) - { - \array_unshift($this->filters, ['matcher' => $matcher, 'filter' => $filter]); - } - public function addTypeFilter(TypeFilter $filter, TypeMatcher $matcher) - { - $this->typeFilters[] = ['matcher' => $matcher, 'filter' => $filter]; - } - private function recursiveCopy($var) - { - // Matches Type Filter - if ($filter = $this->getFirstMatchedTypeFilter($this->typeFilters, $var)) { - return $filter->apply($var); - } - // Resource - if (\is_resource($var)) { - return $var; - } - // Array - if (\is_array($var)) { - return $this->copyArray($var); - } - // Scalar - if (!\is_object($var)) { - return $var; - } - // Enum - if (\PHP_VERSION_ID >= 80100 && \enum_exists(\get_class($var))) { - return $var; - } - // Object - return $this->copyObject($var); - } - /** - * Copy an array - * @param array $array - * @return array - */ - private function copyArray(array $array) - { - foreach ($array as $key => $value) { - $array[$key] = $this->recursiveCopy($value); - } - return $array; - } - /** - * Copies an object. - * - * @param object $object - * - * @throws CloneException - * - * @return object - */ - private function copyObject($object) - { - $objectHash = \spl_object_hash($object); + $objectHash = spl_object_hash($object); if (isset($this->hashMap[$objectHash])) { return $this->hashMap[$objectHash]; } @@ -5507,7 +4839,7 @@ class DeepCopy $this->hashMap[$objectHash] = $object; return $object; } - throw new CloneException(\sprintf('The class "%s" is not cloneable.', $reflectedObject->getName())); + throw new CloneException(sprintf('The class "%s" is not cloneable.', $reflectedObject->getName())); } $newObject = clone $object; $this->hashMap[$objectHash] = $newObject; @@ -5528,6 +4860,10 @@ class DeepCopy if ($property->isStatic()) { return; } + // Ignore readonly properties + if (method_exists($property, 'isReadOnly') && $property->isReadOnly()) { + return; + } // Apply the filters foreach ($this->filters as $item) { /** @var Matcher $matcher */ @@ -5547,7 +4883,7 @@ class DeepCopy } $property->setAccessible(\true); // Ignore uninitialized properties (for PHP >7.4) - if (\method_exists($property, 'isInitialized') && !$property->isInitialized($object)) { + if (method_exists($property, 'isInitialized') && !$property->isInitialized($object)) { return; } $propertyValue = $property->getValue($object); @@ -5565,7 +4901,7 @@ class DeepCopy */ private function getFirstMatchedTypeFilter(array $filterRecords, $var) { - $matched = $this->first($filterRecords, function (array $record) use($var) { + $matched = $this->first($filterRecords, function (array $record) use ($var) { /* @var TypeMatcher $matcher */ $matcher = $record['matcher']; return $matcher->matches($var); @@ -5584,7 +4920,7 @@ class DeepCopy private function first(array $elements, callable $predicate) { foreach ($elements as $element) { - if (\call_user_func($predicate, $element)) { + if (call_user_func($predicate, $element)) { return $element; } } @@ -5650,7 +4986,7 @@ class DoctrineCollectionFilter implements Filter $reflectionProperty = ReflectionHelper::getProperty($object, $property); $reflectionProperty->setAccessible(\true); $oldCollection = $reflectionProperty->getValue($object); - $newCollection = $oldCollection->map(function ($item) use($objectCopier) { + $newCollection = $oldCollection->map(function ($item) use ($objectCopier) { return $objectCopier($item); }); $reflectionProperty->setValue($object, $newCollection); @@ -5767,7 +5103,7 @@ class ReplaceFilter implements Filter { $reflectionProperty = ReflectionHelper::getProperty($object, $property); $reflectionProperty->setAccessible(\true); - $value = \call_user_func($this->callback, $reflectionProperty->getValue($object)); + $value = call_user_func($this->callback, $reflectionProperty->getValue($object)); $reflectionProperty->setValue($object, $value); } } @@ -5933,7 +5269,7 @@ class PropertyTypeMatcher implements Matcher } $reflectionProperty->setAccessible(\true); // Uninitialized properties (for PHP >7.4) - if (\method_exists($reflectionProperty, 'isInitialized') && !$reflectionProperty->isInitialized($object)) { + if (method_exists($reflectionProperty, 'isInitialized') && !$reflectionProperty->isInitialized($object)) { // null instanceof $this->propertyType return \false; } @@ -5993,14 +5329,14 @@ class ReflectionHelper */ public static function getProperty($object, $name) { - $reflection = \is_object($object) ? new ReflectionObject($object) : new ReflectionClass($object); + $reflection = is_object($object) ? new ReflectionObject($object) : new ReflectionClass($object); if ($reflection->hasProperty($name)) { return $reflection->getProperty($name); } if ($parentClass = $reflection->getParentClass()) { return self::getProperty($parentClass->getName(), $name); } - throw new PropertyException(\sprintf('The class "%s" doesn\'t have a property with the given name: "%s".', \is_object($object) ? \get_class($object) : $object, $name)); + throw new PropertyException(sprintf('The class "%s" doesn\'t have a property with the given name: "%s".', is_object($object) ? get_class($object) : $object, $name)); } } callback, $element); + return call_user_func($this->callback, $element); } } copier; - $copy = function (SplDoublyLinkedList $list) use($copier) { + $copy = function (SplDoublyLinkedList $list) use ($copier) { // Replace each element in the list with a deep copy of itself for ($i = 1; $i <= $list->count(); $i++) { $copy = $copier->recursiveCopy($list->shift()); @@ -6197,7 +5533,7 @@ class TypeMatcher */ public function matches($element) { - return \is_object($element) ? \is_a($element, $this->type) : \gettype($element) === $this->type; + return is_object($element) ? is_a($element, $this->type) : (gettype($element) === $this->type); } } */ + protected array $attributes = []; + /** @var list */ + protected array $constants = []; + /** @var list */ + protected array $attributeGroups = []; + /** @var Identifier|Node\Name|Node\ComplexType|null */ + protected ?Node $type = null; /** * Creates a class constant builder * - * @param string|Identifier $name Name + * @param string|Identifier $name Name * @param Node\Expr|bool|null|int|float|string|array $value Value */ public function __construct($name, $value) @@ -6315,7 +5654,7 @@ class ClassConst implements PhpParser\Builder /** * Add another constant to const group * - * @param string|Identifier $name Name + * @param string|Identifier $name Name * @param Node\Expr|bool|null|int|float|string|array $value Value * * @return $this The builder instance (for fluid interface) @@ -6332,7 +5671,7 @@ class ClassConst implements PhpParser\Builder */ public function makePublic() { - $this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_PUBLIC); + $this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::PUBLIC); return $this; } /** @@ -6342,7 +5681,7 @@ class ClassConst implements PhpParser\Builder */ public function makeProtected() { - $this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_PROTECTED); + $this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::PROTECTED); return $this; } /** @@ -6352,7 +5691,7 @@ class ClassConst implements PhpParser\Builder */ public function makePrivate() { - $this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_PRIVATE); + $this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::PRIVATE); return $this; } /** @@ -6362,7 +5701,7 @@ class ClassConst implements PhpParser\Builder */ public function makeFinal() { - $this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_FINAL); + $this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::FINAL); return $this; } /** @@ -6406,7 +5745,7 @@ class ClassConst implements PhpParser\Builder * * @return Stmt\ClassConst The built constant node */ - public function getNode() : PhpParser\Node + public function getNode(): PhpParser\Node { return new Stmt\ClassConst($this->constants, $this->flags, $this->attributes, $this->attributeGroups, $this->type); } @@ -6418,21 +5757,27 @@ namespace PHPUnitPHAR\PhpParser\Builder; use PHPUnitPHAR\PhpParser; use PHPUnitPHAR\PhpParser\BuilderHelpers; +use PHPUnitPHAR\PhpParser\Modifiers; use PHPUnitPHAR\PhpParser\Node; use PHPUnitPHAR\PhpParser\Node\Name; use PHPUnitPHAR\PhpParser\Node\Stmt; class Class_ extends Declaration { - protected $name; - protected $extends = null; - protected $implements = []; - protected $flags = 0; - protected $uses = []; - protected $constants = []; - protected $properties = []; - protected $methods = []; - /** @var Node\AttributeGroup[] */ - protected $attributeGroups = []; + protected string $name; + protected ?Name $extends = null; + /** @var list */ + protected array $implements = []; + protected int $flags = 0; + /** @var list */ + protected array $uses = []; + /** @var list */ + protected array $constants = []; + /** @var list */ + protected array $properties = []; + /** @var list */ + protected array $methods = []; + /** @var list */ + protected array $attributeGroups = []; /** * Creates a class builder. * @@ -6475,7 +5820,7 @@ class Class_ extends Declaration */ public function makeAbstract() { - $this->flags = BuilderHelpers::addClassModifier($this->flags, Stmt\Class_::MODIFIER_ABSTRACT); + $this->flags = BuilderHelpers::addClassModifier($this->flags, Modifiers::ABSTRACT); return $this; } /** @@ -6485,12 +5830,17 @@ class Class_ extends Declaration */ public function makeFinal() { - $this->flags = BuilderHelpers::addClassModifier($this->flags, Stmt\Class_::MODIFIER_FINAL); + $this->flags = BuilderHelpers::addClassModifier($this->flags, Modifiers::FINAL); return $this; } + /** + * Makes the class readonly. + * + * @return $this The builder instance (for fluid interface) + */ public function makeReadonly() { - $this->flags = BuilderHelpers::addClassModifier($this->flags, Stmt\Class_::MODIFIER_READONLY); + $this->flags = BuilderHelpers::addClassModifier($this->flags, Modifiers::READONLY); return $this; } /** @@ -6503,12 +5853,17 @@ class Class_ extends Declaration public function addStmt($stmt) { $stmt = BuilderHelpers::normalizeNode($stmt); - $targets = [Stmt\TraitUse::class => &$this->uses, Stmt\ClassConst::class => &$this->constants, Stmt\Property::class => &$this->properties, Stmt\ClassMethod::class => &$this->methods]; - $class = \get_class($stmt); - if (!isset($targets[$class])) { - throw new \LogicException(\sprintf('Unexpected node of type "%s"', $stmt->getType())); + if ($stmt instanceof Stmt\Property) { + $this->properties[] = $stmt; + } elseif ($stmt instanceof Stmt\ClassMethod) { + $this->methods[] = $stmt; + } elseif ($stmt instanceof Stmt\TraitUse) { + $this->uses[] = $stmt; + } elseif ($stmt instanceof Stmt\ClassConst) { + $this->constants[] = $stmt; + } else { + throw new \LogicException(sprintf('Unexpected node of type "%s"', $stmt->getType())); } - $targets[$class][] = $stmt; return $this; } /** @@ -6528,9 +5883,9 @@ class Class_ extends Declaration * * @return Stmt\Class_ The built class node */ - public function getNode() : PhpParser\Node + public function getNode(): PhpParser\Node { - return new Stmt\Class_($this->name, ['flags' => $this->flags, 'extends' => $this->extends, 'implements' => $this->implements, 'stmts' => \array_merge($this->uses, $this->constants, $this->properties, $this->methods), 'attrGroups' => $this->attributeGroups], $this->attributes); + return new Stmt\Class_($this->name, ['flags' => $this->flags, 'extends' => $this->extends, 'implements' => $this->implements, 'stmts' => array_merge($this->uses, $this->constants, $this->properties, $this->methods), 'attrGroups' => $this->attributeGroups], $this->attributes); } } */ + protected array $attributes = []; + /** + * Adds a statement. + * + * @param PhpParser\Node\Stmt|PhpParser\Builder $stmt The statement to add + * + * @return $this The builder instance (for fluid interface) + */ + abstract public function addStmt($stmt); /** * Adds multiple statements. * - * @param array $stmts The statements to add + * @param (PhpParser\Node\Stmt|PhpParser\Builder)[] $stmts The statements to add * * @return $this The builder instance (for fluid interface) */ @@ -6583,15 +5946,18 @@ use PHPUnitPHAR\PhpParser\Node\Identifier; use PHPUnitPHAR\PhpParser\Node\Stmt; class EnumCase implements PhpParser\Builder { + /** @var Identifier|string */ protected $name; - protected $value = null; - protected $attributes = []; - /** @var Node\AttributeGroup[] */ - protected $attributeGroups = []; + /** @var ?Node\Expr */ + protected ?Node\Expr $value = null; + /** @var array */ + protected array $attributes = []; + /** @var list */ + protected array $attributeGroups = []; /** * Creates an enum case builder. * - * @param string|Identifier $name Name + * @param string|Identifier $name Name */ public function __construct($name) { @@ -6638,7 +6004,7 @@ class EnumCase implements PhpParser\Builder * * @return Stmt\EnumCase The built constant node */ - public function getNode() : PhpParser\Node + public function getNode(): PhpParser\Node { return new Stmt\EnumCase($this->name, $this->value, $this->attributeGroups, $this->attributes); } @@ -6656,15 +6022,20 @@ use PHPUnitPHAR\PhpParser\Node\Name; use PHPUnitPHAR\PhpParser\Node\Stmt; class Enum_ extends Declaration { - protected $name; - protected $scalarType = null; - protected $implements = []; - protected $uses = []; - protected $enumCases = []; - protected $constants = []; - protected $methods = []; - /** @var Node\AttributeGroup[] */ - protected $attributeGroups = []; + protected string $name; + protected ?Identifier $scalarType = null; + /** @var list */ + protected array $implements = []; + /** @var list */ + protected array $uses = []; + /** @var list */ + protected array $enumCases = []; + /** @var list */ + protected array $constants = []; + /** @var list */ + protected array $methods = []; + /** @var list */ + protected array $attributeGroups = []; /** * Creates an enum builder. * @@ -6677,7 +6048,7 @@ class Enum_ extends Declaration /** * Sets the scalar type. * - * @param string|Identifier $type + * @param string|Identifier $scalarType * * @return $this */ @@ -6710,12 +6081,17 @@ class Enum_ extends Declaration public function addStmt($stmt) { $stmt = BuilderHelpers::normalizeNode($stmt); - $targets = [Stmt\TraitUse::class => &$this->uses, Stmt\EnumCase::class => &$this->enumCases, Stmt\ClassConst::class => &$this->constants, Stmt\ClassMethod::class => &$this->methods]; - $class = \get_class($stmt); - if (!isset($targets[$class])) { - throw new \LogicException(\sprintf('Unexpected node of type "%s"', $stmt->getType())); + if ($stmt instanceof Stmt\EnumCase) { + $this->enumCases[] = $stmt; + } elseif ($stmt instanceof Stmt\ClassMethod) { + $this->methods[] = $stmt; + } elseif ($stmt instanceof Stmt\TraitUse) { + $this->uses[] = $stmt; + } elseif ($stmt instanceof Stmt\ClassConst) { + $this->constants[] = $stmt; + } else { + throw new \LogicException(sprintf('Unexpected node of type "%s"', $stmt->getType())); } - $targets[$class][] = $stmt; return $this; } /** @@ -6735,9 +6111,9 @@ class Enum_ extends Declaration * * @return Stmt\Enum_ The built enum node */ - public function getNode() : PhpParser\Node + public function getNode(): PhpParser\Node { - return new Stmt\Enum_($this->name, ['scalarType' => $this->scalarType, 'implements' => $this->implements, 'stmts' => \array_merge($this->uses, $this->enumCases, $this->constants, $this->methods), 'attrGroups' => $this->attributeGroups], $this->attributes); + return new Stmt\Enum_($this->name, ['scalarType' => $this->scalarType, 'implements' => $this->implements, 'stmts' => array_merge($this->uses, $this->enumCases, $this->constants, $this->methods), 'attrGroups' => $this->attributeGroups], $this->attributes); } } getType())); + throw new \LogicException(sprintf('Expected parameter node, got "%s"', $param->getType())); } $this->params[] = $param; return $this; @@ -6782,7 +6159,7 @@ abstract class FunctionLike extends Declaration /** * Adds multiple parameters. * - * @param array $params The parameters to add + * @param (Node\Param|Param)[] $params The parameters to add * * @return $this The builder instance (for fluid interface) */ @@ -6817,10 +6194,11 @@ use PHPUnitPHAR\PhpParser\Node; use PHPUnitPHAR\PhpParser\Node\Stmt; class Function_ extends FunctionLike { - protected $name; - protected $stmts = []; - /** @var Node\AttributeGroup[] */ - protected $attributeGroups = []; + protected string $name; + /** @var list */ + protected array $stmts = []; + /** @var list */ + protected array $attributeGroups = []; /** * Creates a function builder. * @@ -6859,7 +6237,7 @@ class Function_ extends FunctionLike * * @return Stmt\Function_ The built function node */ - public function getNode() : Node + public function getNode(): Node { return new Stmt\Function_($this->name, ['byRef' => $this->returnByRef, 'params' => $this->params, 'returnType' => $this->returnType, 'stmts' => $this->stmts, 'attrGroups' => $this->attributeGroups], $this->attributes); } @@ -6876,12 +6254,15 @@ use PHPUnitPHAR\PhpParser\Node\Name; use PHPUnitPHAR\PhpParser\Node\Stmt; class Interface_ extends Declaration { - protected $name; - protected $extends = []; - protected $constants = []; - protected $methods = []; - /** @var Node\AttributeGroup[] */ - protected $attributeGroups = []; + protected string $name; + /** @var list */ + protected array $extends = []; + /** @var list */ + protected array $constants = []; + /** @var list */ + protected array $methods = []; + /** @var list */ + protected array $attributeGroups = []; /** * Creates an interface builder. * @@ -6922,7 +6303,7 @@ class Interface_ extends Declaration $stmt->stmts = null; $this->methods[] = $stmt; } else { - throw new \LogicException(\sprintf('Unexpected node of type "%s"', $stmt->getType())); + throw new \LogicException(sprintf('Unexpected node of type "%s"', $stmt->getType())); } return $this; } @@ -6943,9 +6324,9 @@ class Interface_ extends Declaration * * @return Stmt\Interface_ The built interface node */ - public function getNode() : PhpParser\Node + public function getNode(): PhpParser\Node { - return new Stmt\Interface_($this->name, ['extends' => $this->extends, 'stmts' => \array_merge($this->constants, $this->methods), 'attrGroups' => $this->attributeGroups], $this->attributes); + return new Stmt\Interface_($this->name, ['extends' => $this->extends, 'stmts' => array_merge($this->constants, $this->methods), 'attrGroups' => $this->attributeGroups], $this->attributes); } } |null */ + protected ?array $stmts = []; + /** @var list */ + protected array $attributeGroups = []; /** * Creates a method builder. * @@ -6981,7 +6363,7 @@ class Method extends FunctionLike */ public function makePublic() { - $this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_PUBLIC); + $this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::PUBLIC); return $this; } /** @@ -6991,7 +6373,7 @@ class Method extends FunctionLike */ public function makeProtected() { - $this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_PROTECTED); + $this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::PROTECTED); return $this; } /** @@ -7001,7 +6383,7 @@ class Method extends FunctionLike */ public function makePrivate() { - $this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_PRIVATE); + $this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::PRIVATE); return $this; } /** @@ -7011,7 +6393,7 @@ class Method extends FunctionLike */ public function makeStatic() { - $this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_STATIC); + $this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::STATIC); return $this; } /** @@ -7024,7 +6406,7 @@ class Method extends FunctionLike if (!empty($this->stmts)) { throw new \LogicException('Cannot make method with statements abstract'); } - $this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_ABSTRACT); + $this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::ABSTRACT); $this->stmts = null; // abstract methods don't have statements return $this; @@ -7036,7 +6418,7 @@ class Method extends FunctionLike */ public function makeFinal() { - $this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_FINAL); + $this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::FINAL); return $this; } /** @@ -7071,7 +6453,7 @@ class Method extends FunctionLike * * @return Stmt\ClassMethod The built method node */ - public function getNode() : Node + public function getNode(): Node { return new Stmt\ClassMethod($this->name, ['flags' => $this->flags, 'byRef' => $this->returnByRef, 'params' => $this->params, 'returnType' => $this->returnType, 'stmts' => $this->stmts, 'attrGroups' => $this->attributeGroups], $this->attributes); } @@ -7087,8 +6469,9 @@ use PHPUnitPHAR\PhpParser\Node; use PHPUnitPHAR\PhpParser\Node\Stmt; class Namespace_ extends Declaration { - private $name; - private $stmts = []; + private ?Node\Name $name; + /** @var Stmt[] */ + private array $stmts = []; /** * Creates a namespace builder. * @@ -7096,7 +6479,7 @@ class Namespace_ extends Declaration */ public function __construct($name) { - $this->name = null !== $name ? BuilderHelpers::normalizeName($name) : null; + $this->name = (null !== $name) ? BuilderHelpers::normalizeName($name) : null; } /** * Adds a statement. @@ -7115,7 +6498,7 @@ class Namespace_ extends Declaration * * @return Stmt\Namespace_ The built node */ - public function getNode() : Node + public function getNode(): Node { return new Stmt\Namespace_($this->name, $this->stmts, $this->attributes); } @@ -7127,18 +6510,19 @@ namespace PHPUnitPHAR\PhpParser\Builder; use PHPUnitPHAR\PhpParser; use PHPUnitPHAR\PhpParser\BuilderHelpers; +use PHPUnitPHAR\PhpParser\Modifiers; use PHPUnitPHAR\PhpParser\Node; class Param implements PhpParser\Builder { - protected $name; - protected $default = null; - /** @var Node\Identifier|Node\Name|Node\NullableType|null */ - protected $type = null; - protected $byRef = \false; - protected $variadic = \false; - protected $flags = 0; - /** @var Node\AttributeGroup[] */ - protected $attributeGroups = []; + protected string $name; + protected ?Node\Expr $default = null; + /** @var Node\Identifier|Node\Name|Node\ComplexType|null */ + protected ?Node $type = null; + protected bool $byRef = \false; + protected int $flags = 0; + protected bool $variadic = \false; + /** @var list */ + protected array $attributeGroups = []; /** * Creates a parameter builder. * @@ -7175,19 +6559,6 @@ class Param implements PhpParser\Builder } return $this; } - /** - * Sets type for the parameter. - * - * @param string|Node\Name|Node\Identifier|Node\ComplexType $type Parameter type - * - * @return $this The builder instance (for fluid interface) - * - * @deprecated Use setType() instead - */ - public function setTypeHint($type) - { - return $this->setType($type); - } /** * Make the parameter accept the value by reference. * @@ -7215,7 +6586,7 @@ class Param implements PhpParser\Builder */ public function makePublic() { - $this->flags = BuilderHelpers::addModifier($this->flags, Node\Stmt\Class_::MODIFIER_PUBLIC); + $this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::PUBLIC); return $this; } /** @@ -7225,7 +6596,7 @@ class Param implements PhpParser\Builder */ public function makeProtected() { - $this->flags = BuilderHelpers::addModifier($this->flags, Node\Stmt\Class_::MODIFIER_PROTECTED); + $this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::PROTECTED); return $this; } /** @@ -7235,7 +6606,7 @@ class Param implements PhpParser\Builder */ public function makePrivate() { - $this->flags = BuilderHelpers::addModifier($this->flags, Node\Stmt\Class_::MODIFIER_PRIVATE); + $this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::PRIVATE); return $this; } /** @@ -7245,7 +6616,7 @@ class Param implements PhpParser\Builder */ public function makeReadonly() { - $this->flags = BuilderHelpers::addModifier($this->flags, Node\Stmt\Class_::MODIFIER_READONLY); + $this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::READONLY); return $this; } /** @@ -7265,7 +6636,7 @@ class Param implements PhpParser\Builder * * @return Node\Param The built parameter node */ - public function getNode() : Node + public function getNode(): Node { return new Node\Param(new Node\Expr\Variable($this->name), $this->default, $this->type, $this->byRef, $this->variadic, [], $this->flags, $this->attributeGroups); } @@ -7277,6 +6648,7 @@ namespace PHPUnitPHAR\PhpParser\Builder; use PHPUnitPHAR\PhpParser; use PHPUnitPHAR\PhpParser\BuilderHelpers; +use PHPUnitPHAR\PhpParser\Modifiers; use PHPUnitPHAR\PhpParser\Node; use PHPUnitPHAR\PhpParser\Node\Identifier; use PHPUnitPHAR\PhpParser\Node\Name; @@ -7284,14 +6656,15 @@ use PHPUnitPHAR\PhpParser\Node\Stmt; use PHPUnitPHAR\PhpParser\Node\ComplexType; class Property implements PhpParser\Builder { - protected $name; - protected $flags = 0; - protected $default = null; - protected $attributes = []; - /** @var null|Identifier|Name|NullableType */ - protected $type; - /** @var Node\AttributeGroup[] */ - protected $attributeGroups = []; + protected string $name; + protected int $flags = 0; + protected ?Node\Expr $default = null; + /** @var array */ + protected array $attributes = []; + /** @var null|Identifier|Name|ComplexType */ + protected ?Node $type = null; + /** @var list */ + protected array $attributeGroups = []; /** * Creates a property builder. * @@ -7308,7 +6681,7 @@ class Property implements PhpParser\Builder */ public function makePublic() { - $this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_PUBLIC); + $this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::PUBLIC); return $this; } /** @@ -7318,7 +6691,7 @@ class Property implements PhpParser\Builder */ public function makeProtected() { - $this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_PROTECTED); + $this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::PROTECTED); return $this; } /** @@ -7328,7 +6701,7 @@ class Property implements PhpParser\Builder */ public function makePrivate() { - $this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_PRIVATE); + $this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::PRIVATE); return $this; } /** @@ -7338,7 +6711,7 @@ class Property implements PhpParser\Builder */ public function makeStatic() { - $this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_STATIC); + $this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::STATIC); return $this; } /** @@ -7348,7 +6721,7 @@ class Property implements PhpParser\Builder */ public function makeReadonly() { - $this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_READONLY); + $this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::READONLY); return $this; } /** @@ -7404,9 +6777,9 @@ class Property implements PhpParser\Builder * * @return Stmt\Property The built property node */ - public function getNode() : PhpParser\Node + public function getNode(): PhpParser\Node { - return new Stmt\Property($this->flags !== 0 ? $this->flags : Stmt\Class_::MODIFIER_PUBLIC, [new Stmt\PropertyProperty($this->name, $this->default)], $this->attributes, $this->type, $this->attributeGroups); + return new Stmt\Property(($this->flags !== 0) ? $this->flags : Modifiers::PUBLIC, [new Node\PropertyItem($this->name, $this->default)], $this->attributes, $this->type, $this->attributeGroups); } } traits, $this->adaptations); } @@ -7478,36 +6853,37 @@ namespace PHPUnitPHAR\PhpParser\Builder; use PHPUnitPHAR\PhpParser\Builder; use PHPUnitPHAR\PhpParser\BuilderHelpers; +use PHPUnitPHAR\PhpParser\Modifiers; use PHPUnitPHAR\PhpParser\Node; use PHPUnitPHAR\PhpParser\Node\Stmt; class TraitUseAdaptation implements Builder { - const TYPE_UNDEFINED = 0; - const TYPE_ALIAS = 1; - const TYPE_PRECEDENCE = 2; - /** @var int Type of building adaptation */ - protected $type; - protected $trait; - protected $method; - protected $modifier = null; - protected $alias = null; - protected $insteadof = []; + private const TYPE_UNDEFINED = 0; + private const TYPE_ALIAS = 1; + private const TYPE_PRECEDENCE = 2; + protected int $type; + protected ?Node\Name $trait; + protected Node\Identifier $method; + protected ?int $modifier = null; + protected ?Node\Identifier $alias = null; + /** @var Node\Name[] */ + protected array $insteadof = []; /** * Creates a trait use adaptation builder. * - * @param Node\Name|string|null $trait Name of adaptated trait - * @param Node\Identifier|string $method Name of adaptated method + * @param Node\Name|string|null $trait Name of adapted trait + * @param Node\Identifier|string $method Name of adapted method */ public function __construct($trait, $method) { $this->type = self::TYPE_UNDEFINED; - $this->trait = \is_null($trait) ? null : BuilderHelpers::normalizeName($trait); + $this->trait = is_null($trait) ? null : BuilderHelpers::normalizeName($trait); $this->method = BuilderHelpers::normalizeIdentifier($method); } /** * Sets alias of method. * - * @param Node\Identifier|string $alias Alias for adaptated method + * @param Node\Identifier|string $alias Alias for adapted method * * @return $this The builder instance (for fluid interface) */ @@ -7519,37 +6895,37 @@ class TraitUseAdaptation implements Builder if ($this->type !== self::TYPE_ALIAS) { throw new \LogicException('Cannot set alias for not alias adaptation buider'); } - $this->alias = $alias; + $this->alias = BuilderHelpers::normalizeIdentifier($alias); return $this; } /** - * Sets adaptated method public. + * Sets adapted method public. * * @return $this The builder instance (for fluid interface) */ public function makePublic() { - $this->setModifier(Stmt\Class_::MODIFIER_PUBLIC); + $this->setModifier(Modifiers::PUBLIC); return $this; } /** - * Sets adaptated method protected. + * Sets adapted method protected. * * @return $this The builder instance (for fluid interface) */ public function makeProtected() { - $this->setModifier(Stmt\Class_::MODIFIER_PROTECTED); + $this->setModifier(Modifiers::PROTECTED); return $this; } /** - * Sets adaptated method private. + * Sets adapted method private. * * @return $this The builder instance (for fluid interface) */ public function makePrivate() { - $this->setModifier(Stmt\Class_::MODIFIER_PRIVATE); + $this->setModifier(Modifiers::PRIVATE); return $this; } /** @@ -7562,7 +6938,7 @@ class TraitUseAdaptation implements Builder public function insteadof(...$traits) { if ($this->type === self::TYPE_UNDEFINED) { - if (\is_null($this->trait)) { + if (is_null($this->trait)) { throw new \LogicException('Precedence adaptation must have trait'); } $this->type = self::TYPE_PRECEDENCE; @@ -7575,7 +6951,7 @@ class TraitUseAdaptation implements Builder } return $this; } - protected function setModifier(int $modifier) + protected function setModifier(int $modifier): void { if ($this->type === self::TYPE_UNDEFINED) { $this->type = self::TYPE_ALIAS; @@ -7583,7 +6959,7 @@ class TraitUseAdaptation implements Builder if ($this->type !== self::TYPE_ALIAS) { throw new \LogicException('Cannot set access modifier for not alias adaptation buider'); } - if (\is_null($this->modifier)) { + if (is_null($this->modifier)) { $this->modifier = $modifier; } else { throw new \LogicException('Multiple access type modifiers are not allowed'); @@ -7594,7 +6970,7 @@ class TraitUseAdaptation implements Builder * * @return Node The built node */ - public function getNode() : Node + public function getNode(): Node { switch ($this->type) { case self::TYPE_ALIAS: @@ -7617,12 +6993,17 @@ use PHPUnitPHAR\PhpParser\Node; use PHPUnitPHAR\PhpParser\Node\Stmt; class Trait_ extends Declaration { - protected $name; - protected $uses = []; - protected $properties = []; - protected $methods = []; - /** @var Node\AttributeGroup[] */ - protected $attributeGroups = []; + protected string $name; + /** @var list */ + protected array $uses = []; + /** @var list */ + protected array $constants = []; + /** @var list */ + protected array $properties = []; + /** @var list */ + protected array $methods = []; + /** @var list */ + protected array $attributeGroups = []; /** * Creates an interface builder. * @@ -7648,8 +7029,10 @@ class Trait_ extends Declaration $this->methods[] = $stmt; } elseif ($stmt instanceof Stmt\TraitUse) { $this->uses[] = $stmt; + } elseif ($stmt instanceof Stmt\ClassConst) { + $this->constants[] = $stmt; } else { - throw new \LogicException(\sprintf('Unexpected node of type "%s"', $stmt->getType())); + throw new \LogicException(sprintf('Unexpected node of type "%s"', $stmt->getType())); } return $this; } @@ -7670,9 +7053,9 @@ class Trait_ extends Declaration * * @return Stmt\Trait_ The built interface node */ - public function getNode() : PhpParser\Node + public function getNode(): PhpParser\Node { - return new Stmt\Trait_($this->name, ['stmts' => \array_merge($this->uses, $this->properties, $this->methods), 'attrGroups' => $this->attributeGroups], $this->attributes); + return new Stmt\Trait_($this->name, ['stmts' => array_merge($this->uses, $this->constants, $this->properties, $this->methods), 'attrGroups' => $this->attributeGroups], $this->attributes); } } name, $this->alias)], $this->type); + return new Stmt\Use_([new Node\UseItem($this->name, $this->alias)], $this->type); } } args($args)); } @@ -7755,7 +7137,7 @@ class BuilderFactory * * @return Builder\Namespace_ The created namespace builder */ - public function namespace($name) : Builder\Namespace_ + public function namespace($name): Builder\Namespace_ { return new Builder\Namespace_($name); } @@ -7766,7 +7148,7 @@ class BuilderFactory * * @return Builder\Class_ The created class builder */ - public function class(string $name) : Builder\Class_ + public function class(string $name): Builder\Class_ { return new Builder\Class_($name); } @@ -7777,7 +7159,7 @@ class BuilderFactory * * @return Builder\Interface_ The created interface builder */ - public function interface(string $name) : Builder\Interface_ + public function interface(string $name): Builder\Interface_ { return new Builder\Interface_($name); } @@ -7788,7 +7170,7 @@ class BuilderFactory * * @return Builder\Trait_ The created trait builder */ - public function trait(string $name) : Builder\Trait_ + public function trait(string $name): Builder\Trait_ { return new Builder\Trait_($name); } @@ -7799,7 +7181,7 @@ class BuilderFactory * * @return Builder\Enum_ The created enum builder */ - public function enum(string $name) : Builder\Enum_ + public function enum(string $name): Builder\Enum_ { return new Builder\Enum_($name); } @@ -7808,21 +7190,21 @@ class BuilderFactory * * @param Node\Name|string ...$traits Trait names * - * @return Builder\TraitUse The create trait use builder + * @return Builder\TraitUse The created trait use builder */ - public function useTrait(...$traits) : Builder\TraitUse + public function useTrait(...$traits): Builder\TraitUse { return new Builder\TraitUse(...$traits); } /** * Creates a trait use adaptation builder. * - * @param Node\Name|string|null $trait Trait name + * @param Node\Name|string|null $trait Trait name * @param Node\Identifier|string $method Method name * - * @return Builder\TraitUseAdaptation The create trait use adaptation builder + * @return Builder\TraitUseAdaptation The created trait use adaptation builder */ - public function traitUseAdaptation($trait, $method = null) : Builder\TraitUseAdaptation + public function traitUseAdaptation($trait, $method = null): Builder\TraitUseAdaptation { if ($method === null) { $method = $trait; @@ -7837,7 +7219,7 @@ class BuilderFactory * * @return Builder\Method The created method builder */ - public function method(string $name) : Builder\Method + public function method(string $name): Builder\Method { return new Builder\Method($name); } @@ -7848,7 +7230,7 @@ class BuilderFactory * * @return Builder\Param The created parameter builder */ - public function param(string $name) : Builder\Param + public function param(string $name): Builder\Param { return new Builder\Param($name); } @@ -7859,7 +7241,7 @@ class BuilderFactory * * @return Builder\Property The created property builder */ - public function property(string $name) : Builder\Property + public function property(string $name): Builder\Property { return new Builder\Property($name); } @@ -7870,7 +7252,7 @@ class BuilderFactory * * @return Builder\Function_ The created function builder */ - public function function(string $name) : Builder\Function_ + public function function(string $name): Builder\Function_ { return new Builder\Function_($name); } @@ -7881,7 +7263,7 @@ class BuilderFactory * * @return Builder\Use_ The created use builder */ - public function use($name) : Builder\Use_ + public function use($name): Builder\Use_ { return new Builder\Use_($name, Use_::TYPE_NORMAL); } @@ -7892,7 +7274,7 @@ class BuilderFactory * * @return Builder\Use_ The created use function builder */ - public function useFunction($name) : Builder\Use_ + public function useFunction($name): Builder\Use_ { return new Builder\Use_($name, Use_::TYPE_FUNCTION); } @@ -7903,30 +7285,30 @@ class BuilderFactory * * @return Builder\Use_ The created use const builder */ - public function useConst($name) : Builder\Use_ + public function useConst($name): Builder\Use_ { return new Builder\Use_($name, Use_::TYPE_CONSTANT); } /** * Creates a class constant builder. * - * @param string|Identifier $name Name + * @param string|Identifier $name Name * @param Node\Expr|bool|null|int|float|string|array $value Value * * @return Builder\ClassConst The created use const builder */ - public function classConst($name, $value) : Builder\ClassConst + public function classConst($name, $value): Builder\ClassConst { return new Builder\ClassConst($name, $value); } /** * Creates an enum case builder. * - * @param string|Identifier $name Name + * @param string|Identifier $name Name * * @return Builder\EnumCase The created use const builder */ - public function enumCase($name) : Builder\EnumCase + public function enumCase($name): Builder\EnumCase { return new Builder\EnumCase($name); } @@ -7934,10 +7316,8 @@ class BuilderFactory * Creates node a for a literal value. * * @param Expr|bool|null|int|float|string|array $value $value - * - * @return Expr */ - public function val($value) : Expr + public function val($value): Expr { return BuilderHelpers::normalizeValue($value); } @@ -7945,10 +7325,8 @@ class BuilderFactory * Creates variable node. * * @param string|Expr $name Name - * - * @return Expr\Variable */ - public function var($name) : Expr\Variable + public function var($name): Expr\Variable { if (!\is_string($name) && !$name instanceof Expr) { throw new \LogicException('Variable name must be string or Expr'); @@ -7962,9 +7340,9 @@ class BuilderFactory * * @param array $args List of arguments to normalize * - * @return Arg[] + * @return list */ - public function args(array $args) : array + public function args(array $args): array { $normalizedArgs = []; foreach ($args as $key => $arg) { @@ -7982,37 +7360,31 @@ class BuilderFactory * Creates a function call node. * * @param string|Name|Expr $name Function name - * @param array $args Function arguments - * - * @return Expr\FuncCall + * @param array $args Function arguments */ - public function funcCall($name, array $args = []) : Expr\FuncCall + public function funcCall($name, array $args = []): Expr\FuncCall { return new Expr\FuncCall(BuilderHelpers::normalizeNameOrExpr($name), $this->args($args)); } /** * Creates a method call node. * - * @param Expr $var Variable the method is called on + * @param Expr $var Variable the method is called on * @param string|Identifier|Expr $name Method name - * @param array $args Method arguments - * - * @return Expr\MethodCall + * @param array $args Method arguments */ - public function methodCall(Expr $var, $name, array $args = []) : Expr\MethodCall + public function methodCall(Expr $var, $name, array $args = []): Expr\MethodCall { return new Expr\MethodCall($var, BuilderHelpers::normalizeIdentifierOrExpr($name), $this->args($args)); } /** * Creates a static method call node. * - * @param string|Name|Expr $class Class name - * @param string|Identifier|Expr $name Method name - * @param array $args Method arguments - * - * @return Expr\StaticCall + * @param string|Name|Expr $class Class name + * @param string|Identifier|Expr $name Method name + * @param array $args Method arguments */ - public function staticCall($class, $name, array $args = []) : Expr\StaticCall + public function staticCall($class, $name, array $args = []): Expr\StaticCall { return new Expr\StaticCall(BuilderHelpers::normalizeNameOrExpr($class), BuilderHelpers::normalizeIdentifierOrExpr($name), $this->args($args)); } @@ -8020,11 +7392,9 @@ class BuilderFactory * Creates an object creation node. * * @param string|Name|Expr $class Class name - * @param array $args Constructor arguments - * - * @return Expr\New_ + * @param array $args Constructor arguments */ - public function new($class, array $args = []) : Expr\New_ + public function new($class, array $args = []): Expr\New_ { return new Expr\New_(BuilderHelpers::normalizeNameOrExpr($class), $this->args($args)); } @@ -8032,22 +7402,18 @@ class BuilderFactory * Creates a constant fetch node. * * @param string|Name $name Constant name - * - * @return Expr\ConstFetch */ - public function constFetch($name) : Expr\ConstFetch + public function constFetch($name): Expr\ConstFetch { return new Expr\ConstFetch(BuilderHelpers::normalizeName($name)); } /** * Creates a property fetch node. * - * @param Expr $var Variable holding object + * @param Expr $var Variable holding object * @param string|Identifier|Expr $name Property name - * - * @return Expr\PropertyFetch */ - public function propertyFetch(Expr $var, $name) : Expr\PropertyFetch + public function propertyFetch(Expr $var, $name): Expr\PropertyFetch { return new Expr\PropertyFetch($var, BuilderHelpers::normalizeIdentifierOrExpr($name)); } @@ -8055,11 +7421,9 @@ class BuilderFactory * Creates a class constant fetch node. * * @param string|Name|Expr $class Class name - * @param string|Identifier|Expr $name Constant name - * - * @return Expr\ClassConstFetch + * @param string|Identifier|Expr $name Constant name */ - public function classConstFetch($class, $name) : Expr\ClassConstFetch + public function classConstFetch($class, $name): Expr\ClassConstFetch { return new Expr\ClassConstFetch(BuilderHelpers::normalizeNameOrExpr($class), BuilderHelpers::normalizeIdentifierOrExpr($name)); } @@ -8067,12 +7431,10 @@ class BuilderFactory * Creates nested Concat nodes from a list of expressions. * * @param Expr|string ...$exprs Expressions or literal strings - * - * @return Concat */ - public function concat(...$exprs) : Concat + public function concat(...$exprs): Concat { - $numExprs = \count($exprs); + $numExprs = count($exprs); if ($numExprs < 2) { throw new \LogicException('Expected at least two expressions'); } @@ -8084,9 +7446,8 @@ class BuilderFactory } /** * @param string|Expr $expr - * @return Expr */ - private function normalizeStringExpr($expr) : Expr + private function normalizeStringExpr($expr): Expr { if ($expr instanceof Expr) { return $expr; @@ -8123,7 +7484,7 @@ final class BuilderHelpers * * @return Node The normalized node */ - public static function normalizeNode($node) : Node + public static function normalizeNode($node): Node { if ($node instanceof Builder) { return $node->getNode(); @@ -8142,7 +7503,7 @@ final class BuilderHelpers * * @return Stmt The normalized statement node */ - public static function normalizeStmt($node) : Stmt + public static function normalizeStmt($node): Stmt { $node = self::normalizeNode($node); if ($node instanceof Stmt) { @@ -8160,7 +7521,7 @@ final class BuilderHelpers * * @return Identifier The normalized identifier */ - public static function normalizeIdentifier($name) : Identifier + public static function normalizeIdentifier($name): Identifier { if ($name instanceof Identifier) { return $name; @@ -8168,7 +7529,7 @@ final class BuilderHelpers if (\is_string($name)) { return new Identifier($name); } - throw new \LogicException('Expected string or instance of Node\\Identifier'); + throw new \LogicException('Expected string or instance of Node\Identifier'); } /** * Normalizes strings to Identifier, also allowing expressions. @@ -8185,7 +7546,7 @@ final class BuilderHelpers if (\is_string($name)) { return new Identifier($name); } - throw new \LogicException('Expected string or instance of Node\\Identifier or Node\\Expr'); + throw new \LogicException('Expected string or instance of Node\Identifier or Node\Expr'); } /** * Normalizes a name: Converts string names to Name nodes. @@ -8194,24 +7555,24 @@ final class BuilderHelpers * * @return Name The normalized name */ - public static function normalizeName($name) : Name + public static function normalizeName($name): Name { if ($name instanceof Name) { return $name; } - if (\is_string($name)) { + if (is_string($name)) { if (!$name) { throw new \LogicException('Name cannot be empty'); } if ($name[0] === '\\') { - return new Name\FullyQualified(\substr($name, 1)); + return new Name\FullyQualified(substr($name, 1)); } - if (0 === \strpos($name, 'namespace\\')) { - return new Name\Relative(\substr($name, \strlen('namespace\\'))); + if (0 === strpos($name, 'namespace\\')) { + return new Name\Relative(substr($name, strlen('namespace\\'))); } return new Name($name); } - throw new \LogicException('Name must be a string or an instance of Node\\Name'); + throw new \LogicException('Name must be a string or an instance of Node\Name'); } /** * Normalizes a name: Converts string names to Name nodes, while also allowing expressions. @@ -8225,8 +7586,8 @@ final class BuilderHelpers if ($name instanceof Expr) { return $name; } - if (!\is_string($name) && !$name instanceof Name) { - throw new \LogicException('Name must be a string or an instance of Node\\Name or Node\\Expr'); + if (!is_string($name) && !$name instanceof Name) { + throw new \LogicException('Name must be a string or an instance of Node\Name or Node\Expr'); } return self::normalizeName($name); } @@ -8242,27 +7603,27 @@ final class BuilderHelpers */ public static function normalizeType($type) { - if (!\is_string($type)) { + if (!is_string($type)) { if (!$type instanceof Name && !$type instanceof Identifier && !$type instanceof ComplexType) { throw new \LogicException('Type must be a string, or an instance of Name, Identifier or ComplexType'); } return $type; } $nullable = \false; - if (\strlen($type) > 0 && $type[0] === '?') { + if (strlen($type) > 0 && $type[0] === '?') { $nullable = \true; - $type = \substr($type, 1); + $type = substr($type, 1); } $builtinTypes = ['array', 'callable', 'bool', 'int', 'float', 'string', 'iterable', 'void', 'object', 'null', 'false', 'mixed', 'never', 'true']; - $lowerType = \strtolower($type); - if (\in_array($lowerType, $builtinTypes)) { + $lowerType = strtolower($type); + if (in_array($lowerType, $builtinTypes)) { $type = new Identifier($lowerType); } else { $type = self::normalizeName($type); } $notNullableTypes = ['void', 'mixed', 'never']; - if ($nullable && \in_array((string) $type, $notNullableTypes)) { - throw new \LogicException(\sprintf('%s type cannot be nullable', $type)); + if ($nullable && in_array((string) $type, $notNullableTypes)) { + throw new \LogicException(sprintf('%s type cannot be nullable', $type)); } return $nullable ? new NullableType($type) : $type; } @@ -8274,36 +7635,36 @@ final class BuilderHelpers * * @return Expr The normalized value */ - public static function normalizeValue($value) : Expr + public static function normalizeValue($value): Expr { if ($value instanceof Node\Expr) { return $value; } - if (\is_null($value)) { + if (is_null($value)) { return new Expr\ConstFetch(new Name('null')); } - if (\is_bool($value)) { + if (is_bool($value)) { return new Expr\ConstFetch(new Name($value ? 'true' : 'false')); } - if (\is_int($value)) { - return new Scalar\LNumber($value); + if (is_int($value)) { + return new Scalar\Int_($value); } - if (\is_float($value)) { - return new Scalar\DNumber($value); + if (is_float($value)) { + return new Scalar\Float_($value); } - if (\is_string($value)) { + if (is_string($value)) { return new Scalar\String_($value); } - if (\is_array($value)) { + if (is_array($value)) { $items = []; $lastKey = -1; foreach ($value as $itemKey => $itemValue) { // for consecutive, numeric keys don't generate keys if (null !== $lastKey && ++$lastKey === $itemKey) { - $items[] = new Expr\ArrayItem(self::normalizeValue($itemValue)); + $items[] = new Node\ArrayItem(self::normalizeValue($itemValue)); } else { $lastKey = null; - $items[] = new Expr\ArrayItem(self::normalizeValue($itemValue), self::normalizeValue($itemKey)); + $items[] = new Node\ArrayItem(self::normalizeValue($itemValue), self::normalizeValue($itemKey)); } } return new Expr\Array_($items); @@ -8317,15 +7678,15 @@ final class BuilderHelpers * * @return Comment\Doc The normalized doc comment */ - public static function normalizeDocComment($docComment) : Comment\Doc + public static function normalizeDocComment($docComment): Comment\Doc { if ($docComment instanceof Comment\Doc) { return $docComment; } - if (\is_string($docComment)) { + if (is_string($docComment)) { return new Comment\Doc($docComment); } - throw new \LogicException('Doc comment must be a string or an instance of PhpParser\\Comment\\Doc'); + throw new \LogicException('Doc comment must be a string or an instance of PhpParser\Comment\Doc'); } /** * Normalizes a attribute: Converts attribute to the Attribute Group if needed. @@ -8334,13 +7695,13 @@ final class BuilderHelpers * * @return Node\AttributeGroup The Attribute Group */ - public static function normalizeAttribute($attribute) : Node\AttributeGroup + public static function normalizeAttribute($attribute): Node\AttributeGroup { if ($attribute instanceof Node\AttributeGroup) { return $attribute; } if (!$attribute instanceof Node\Attribute) { - throw new \LogicException('Attribute must be an instance of PhpParser\\Node\\Attribute or PhpParser\\Node\\AttributeGroup'); + throw new \LogicException('Attribute must be an instance of PhpParser\Node\Attribute or PhpParser\Node\AttributeGroup'); } return new Node\AttributeGroup([$attribute]); } @@ -8348,22 +7709,22 @@ final class BuilderHelpers * Adds a modifier and returns new modifier bitmask. * * @param int $modifiers Existing modifiers - * @param int $modifier Modifier to set + * @param int $modifier Modifier to set * * @return int New modifiers */ - public static function addModifier(int $modifiers, int $modifier) : int + public static function addModifier(int $modifiers, int $modifier): int { - Stmt\Class_::verifyModifier($modifiers, $modifier); + Modifiers::verifyModifier($modifiers, $modifier); return $modifiers | $modifier; } /** * Adds a modifier and returns new modifier bitmask. * @return int New modifiers */ - public static function addClassModifier(int $existingModifiers, int $modifierToSet) : int + public static function addClassModifier(int $existingModifiers, int $modifierToSet): int { - Stmt\Class_::verifyClassModifier($existingModifiers, $modifierToSet); + Modifiers::verifyClassModifier($existingModifiers, $modifierToSet); return $existingModifiers | $modifierToSet; } } @@ -8374,20 +7735,20 @@ namespace PHPUnitPHAR\PhpParser; class Comment implements \JsonSerializable { - protected $text; - protected $startLine; - protected $startFilePos; - protected $startTokenPos; - protected $endLine; - protected $endFilePos; - protected $endTokenPos; + protected string $text; + protected int $startLine; + protected int $startFilePos; + protected int $startTokenPos; + protected int $endLine; + protected int $endFilePos; + protected int $endTokenPos; /** * Constructs a comment node. * - * @param string $text Comment text (including comment delimiters like /*) - * @param int $startLine Line number the comment started on - * @param int $startFilePos File offset the comment started on - * @param int $startTokenPos Token offset the comment started on + * @param string $text Comment text (including comment delimiters like /*) + * @param int $startLine Line number the comment started on + * @param int $startFilePos File offset the comment started on + * @param int $startTokenPos Token offset the comment started on */ public function __construct(string $text, int $startLine = -1, int $startFilePos = -1, int $startTokenPos = -1, int $endLine = -1, int $endFilePos = -1, int $endTokenPos = -1) { @@ -8404,7 +7765,7 @@ class Comment implements \JsonSerializable * * @return string The comment text (including comment delimiters like /*) */ - public function getText() : string + public function getText(): string { return $this->text; } @@ -8412,8 +7773,9 @@ class Comment implements \JsonSerializable * Gets the line number the comment started on. * * @return int Line number (or -1 if not available) + * @phpstan-return -1|positive-int */ - public function getStartLine() : int + public function getStartLine(): int { return $this->startLine; } @@ -8422,7 +7784,7 @@ class Comment implements \JsonSerializable * * @return int File offset (or -1 if not available) */ - public function getStartFilePos() : int + public function getStartFilePos(): int { return $this->startFilePos; } @@ -8431,7 +7793,7 @@ class Comment implements \JsonSerializable * * @return int Token offset (or -1 if not available) */ - public function getStartTokenPos() : int + public function getStartTokenPos(): int { return $this->startTokenPos; } @@ -8439,8 +7801,9 @@ class Comment implements \JsonSerializable * Gets the line number the comment ends on. * * @return int Line number (or -1 if not available) + * @phpstan-return -1|positive-int */ - public function getEndLine() : int + public function getEndLine(): int { return $this->endLine; } @@ -8449,7 +7812,7 @@ class Comment implements \JsonSerializable * * @return int File offset (or -1 if not available) */ - public function getEndFilePos() : int + public function getEndFilePos(): int { return $this->endFilePos; } @@ -8458,49 +7821,16 @@ class Comment implements \JsonSerializable * * @return int Token offset (or -1 if not available) */ - public function getEndTokenPos() : int + public function getEndTokenPos(): int { return $this->endTokenPos; } - /** - * Gets the line number the comment started on. - * - * @deprecated Use getStartLine() instead - * - * @return int Line number - */ - public function getLine() : int - { - return $this->startLine; - } - /** - * Gets the file offset the comment started on. - * - * @deprecated Use getStartFilePos() instead - * - * @return int File offset - */ - public function getFilePos() : int - { - return $this->startFilePos; - } - /** - * Gets the token offset the comment started on. - * - * @deprecated Use getStartTokenPos() instead - * - * @return int Token offset - */ - public function getTokenPos() : int - { - return $this->startTokenPos; - } /** * Gets the comment text. * * @return string The comment text (including comment delimiters like /*) */ - public function __toString() : string + public function __toString(): string { return $this->text; } @@ -8509,19 +7839,20 @@ class Comment implements \JsonSerializable * * "Reformatted" here means that we try to clean up the whitespace at the * starts of the lines. This is necessary because we receive the comments - * without trailing whitespace on the first line, but with trailing whitespace + * without leading whitespace on the first line, but with leading whitespace * on all subsequent lines. * - * @return mixed|string + * Additionally, this normalizes CRLF newlines to LF newlines. */ - public function getReformattedText() + public function getReformattedText(): string { - $text = \trim($this->text); - $newlinePos = \strpos($text, "\n"); + $text = str_replace("\r\n", "\n", $this->text); + $newlinePos = strpos($text, "\n"); if (\false === $newlinePos) { // Single line comments don't need further processing return $text; - } elseif (\preg_match('((*BSR_ANYCRLF)(*ANYCRLF)^.*(?:\\R\\s+\\*.*)+$)', $text)) { + } + if (preg_match('(^.*(?:\n\s+\*.*)+$)', $text)) { // Multi line comment of the type // // /* @@ -8530,8 +7861,9 @@ class Comment implements \JsonSerializable // */ // // is handled by replacing the whitespace sequences before the * by a single space - return \preg_replace('(^\\s+\\*)m', ' *', $this->text); - } elseif (\preg_match('(^/\\*\\*?\\s*[\\r\\n])', $text) && \preg_match('(\\n(\\s*)\\*/$)', $text, $matches)) { + return preg_replace('(^\s+\*)m', ' *', $text); + } + if (preg_match('(^/\*\*?\s*\n)', $text) && preg_match('(\n(\s*)\*/$)', $text, $matches)) { // Multi line comment of the type // // /* @@ -8542,8 +7874,9 @@ class Comment implements \JsonSerializable // is handled by removing the whitespace sequence on the line before the closing // */ on all lines. So if the last line is " */", then " " is removed at the // start of all lines. - return \preg_replace('(^' . \preg_quote($matches[1]) . ')m', '', $text); - } elseif (\preg_match('(^/\\*\\*?\\s*(?!\\s))', $text, $matches)) { + return preg_replace('(^' . preg_quote($matches[1]) . ')m', '', $text); + } + if (preg_match('(^/\*\*?\s*(?!\s))', $text, $matches)) { // Multi line comment of the type // // /* Some text. @@ -8553,9 +7886,9 @@ class Comment implements \JsonSerializable // // is handled by removing the difference between the shortest whitespace prefix on all // lines and the length of the "/* " opening sequence. - $prefixLen = $this->getShortestWhitespacePrefixLen(\substr($text, $newlinePos + 1)); - $removeLen = $prefixLen - \strlen($matches[0]); - return \preg_replace('(^\\s{' . $removeLen . '})m', '', $text); + $prefixLen = $this->getShortestWhitespacePrefixLen(substr($text, $newlinePos + 1)); + $removeLen = $prefixLen - strlen($matches[0]); + return preg_replace('(^\s{' . $removeLen . '})m', '', $text); } // No idea how to format this comment, so simply return as is return $text; @@ -8568,13 +7901,13 @@ class Comment implements \JsonSerializable * @param string $str String to check * @return int Length in characters. Tabs count as single characters. */ - private function getShortestWhitespacePrefixLen(string $str) : int + private function getShortestWhitespacePrefixLen(string $str): int { - $lines = \explode("\n", $str); - $shortestPrefixLen = \INF; + $lines = explode("\n", $str); + $shortestPrefixLen = \PHP_INT_MAX; foreach ($lines as $line) { - \preg_match('(^\\s*)', $line, $matches); - $prefixLen = \strlen($matches[0]); + preg_match('(^\s*)', $line, $matches); + $prefixLen = strlen($matches[0]); if ($prefixLen < $shortestPrefixLen) { $shortestPrefixLen = $prefixLen; } @@ -8582,13 +7915,12 @@ class Comment implements \JsonSerializable return $shortestPrefixLen; } /** - * @return array - * @psalm-return array{nodeType:string, text:mixed, line:mixed, filePos:mixed} + * @return array{nodeType:string, text:mixed, line:mixed, filePos:mixed} */ - public function jsonSerialize() : array + public function jsonSerialize(): array { // Technically not a node, but we make it look like one anyway - $type = $this instanceof Comment\Doc ? 'Comment_Doc' : 'Comment'; + $type = ($this instanceof Comment\Doc) ? 'Comment_Doc' : 'Comment'; return [ 'nodeType' => $type, 'text' => $this->text, @@ -8612,6 +7944,7 @@ class Doc extends \PHPUnitPHAR\PhpParser\Comment } evaluate($expr); } + /** @return mixed */ private function evaluate(Expr $expr) { - if ($expr instanceof Scalar\LNumber || $expr instanceof Scalar\DNumber || $expr instanceof Scalar\String_) { + if ($expr instanceof Scalar\Int_ || $expr instanceof Scalar\Float_ || $expr instanceof Scalar\String_) { return $expr->value; } if ($expr instanceof Expr\Array_) { @@ -8749,7 +8085,7 @@ class ConstExprEvaluator } return ($this->fallbackEvaluator)($expr); } - private function evaluateArray(Expr\Array_ $expr) + private function evaluateArray(Expr\Array_ $expr): array { $array = []; foreach ($expr->items as $item) { @@ -8763,6 +8099,7 @@ class ConstExprEvaluator } return $array; } + /** @return mixed */ private function evaluateTernary(Expr\Ternary $expr) { if (null === $expr->if) { @@ -8770,6 +8107,7 @@ class ConstExprEvaluator } return $this->evaluate($expr->cond) ? $this->evaluate($expr->if) : $this->evaluate($expr->else); } + /** @return mixed */ private function evaluateBinaryOp(Expr\BinaryOp $expr) { if ($expr instanceof Expr\BinaryOp\Coalesce && $expr->left instanceof Expr\ArrayDimFetch) { @@ -8838,6 +8176,7 @@ class ConstExprEvaluator } throw new \Exception('Should not happen'); } + /** @return mixed */ private function evaluateConstFetch(Expr\ConstFetch $expr) { $name = $expr->name->toLowerString(); @@ -8859,23 +8198,19 @@ namespace PHPUnitPHAR\PhpParser; class Error extends \RuntimeException { - protected $rawMessage; - protected $attributes; + protected string $rawMessage; + /** @var array */ + protected array $attributes; /** * Creates an Exception signifying a parse error. * - * @param string $message Error message - * @param array|int $attributes Attributes of node/token where error occurred - * (or start line of error -- deprecated) + * @param string $message Error message + * @param array $attributes Attributes of node/token where error occurred */ - public function __construct(string $message, $attributes = []) + public function __construct(string $message, array $attributes = []) { $this->rawMessage = $message; - if (\is_array($attributes)) { - $this->attributes = $attributes; - } else { - $this->attributes = ['startLine' => $attributes]; - } + $this->attributes = $attributes; $this->updateMessage(); } /** @@ -8883,7 +8218,7 @@ class Error extends \RuntimeException * * @return string Error message */ - public function getRawMessage() : string + public function getRawMessage(): string { return $this->rawMessage; } @@ -8891,8 +8226,9 @@ class Error extends \RuntimeException * Gets the line the error starts in. * * @return int Error start line + * @phpstan-return -1|positive-int */ - public function getStartLine() : int + public function getStartLine(): int { return $this->attributes['startLine'] ?? -1; } @@ -8900,26 +8236,27 @@ class Error extends \RuntimeException * Gets the line the error ends in. * * @return int Error end line + * @phpstan-return -1|positive-int */ - public function getEndLine() : int + public function getEndLine(): int { return $this->attributes['endLine'] ?? -1; } /** * Gets the attributes of the node/token the error occurred at. * - * @return array + * @return array */ - public function getAttributes() : array + public function getAttributes(): array { return $this->attributes; } /** * Sets the attributes of the node/token the error occurred at. * - * @param array $attributes + * @param array $attributes */ - public function setAttributes(array $attributes) + public function setAttributes(array $attributes): void { $this->attributes = $attributes; $this->updateMessage(); @@ -8929,7 +8266,7 @@ class Error extends \RuntimeException * * @param string $message Error message */ - public function setRawMessage(string $message) + public function setRawMessage(string $message): void { $this->rawMessage = $message; $this->updateMessage(); @@ -8939,7 +8276,7 @@ class Error extends \RuntimeException * * @param int $line Error start line */ - public function setStartLine(int $line) + public function setStartLine(int $line): void { $this->attributes['startLine'] = $line; $this->updateMessage(); @@ -8948,10 +8285,8 @@ class Error extends \RuntimeException * Returns whether the error has start and end column information. * * For column information enable the startFilePos and endFilePos in the lexer options. - * - * @return bool */ - public function hasColumnInfo() : bool + public function hasColumnInfo(): bool { return isset($this->attributes['startFilePos'], $this->attributes['endFilePos']); } @@ -8959,9 +8294,8 @@ class Error extends \RuntimeException * Gets the start column (1-based) into the line where the error started. * * @param string $code Source code of the file - * @return int */ - public function getStartColumn(string $code) : int + public function getStartColumn(string $code): int { if (!$this->hasColumnInfo()) { throw new \RuntimeException('Error does not have column information'); @@ -8972,9 +8306,8 @@ class Error extends \RuntimeException * Gets the end column (1-based) into the line where the error ended. * * @param string $code Source code of the file - * @return int */ - public function getEndColumn(string $code) : int + public function getEndColumn(string $code): int { if (!$this->hasColumnInfo()) { throw new \RuntimeException('Error does not have column information'); @@ -8988,24 +8321,24 @@ class Error extends \RuntimeException * * @return string Formatted message */ - public function getMessageWithColumnInfo(string $code) : string + public function getMessageWithColumnInfo(string $code): string { - return \sprintf('%s from %d:%d to %d:%d', $this->getRawMessage(), $this->getStartLine(), $this->getStartColumn($code), $this->getEndLine(), $this->getEndColumn($code)); + return sprintf('%s from %d:%d to %d:%d', $this->getRawMessage(), $this->getStartLine(), $this->getStartColumn($code), $this->getEndLine(), $this->getEndColumn($code)); } /** * Converts a file offset into a column. * * @param string $code Source code that $pos indexes into - * @param int $pos 0-based position in $code + * @param int $pos 0-based position in $code * * @return int 1-based column (relative to start of line) */ - private function toColumn(string $code, int $pos) : int + private function toColumn(string $code, int $pos): int { - if ($pos > \strlen($code)) { + if ($pos > strlen($code)) { throw new \RuntimeException('Invalid position information'); } - $lineStartPos = \strrpos($code, "\n", $pos - \strlen($code)); + $lineStartPos = strrpos($code, "\n", $pos - strlen($code)); if (\false === $lineStartPos) { $lineStartPos = -1; } @@ -9014,7 +8347,7 @@ class Error extends \RuntimeException /** * Updates the exception message after a change to rawMessage or rawLine. */ - protected function updateMessage() + protected function updateMessage(): void { $this->message = $this->rawMessage; if (-1 === $this->getStartLine()) { @@ -9036,7 +8369,7 @@ interface ErrorHandler * * @param Error $error The error that needs to be handled */ - public function handleError(Error $error); + public function handleError(Error $error): void; } errors[] = $error; } @@ -9063,23 +8396,21 @@ class Collecting implements ErrorHandler * * @return Error[] */ - public function getErrors() : array + public function getErrors(): array { return $this->errors; } /** * Check whether there are any errors. - * - * @return bool */ - public function hasErrors() : bool + public function hasErrors(): bool { return !empty($this->errors); } /** * Reset/clear collected errors. */ - public function clearErrors() + public function clearErrors(): void { $this->errors = []; } @@ -9098,7 +8429,7 @@ use PHPUnitPHAR\PhpParser\ErrorHandler; */ class Throwing implements ErrorHandler { - public function handleError(Error $error) + public function handleError(Error $error): void { throw $error; } @@ -9113,16 +8444,21 @@ namespace PHPUnitPHAR\PhpParser\Internal; */ class DiffElem { - const TYPE_KEEP = 0; - const TYPE_REMOVE = 1; - const TYPE_ADD = 2; - const TYPE_REPLACE = 3; + public const TYPE_KEEP = 0; + public const TYPE_REMOVE = 1; + public const TYPE_ADD = 2; + public const TYPE_REPLACE = 3; /** @var int One of the TYPE_* constants */ - public $type; + public int $type; /** @var mixed Is null for add operations */ public $old; /** @var mixed Is null for remove operations */ public $new; + /** + * @param int $type One of the TYPE_* constants + * @param mixed $old Is null for add operations + * @param mixed $new Is null for remove operations + */ public function __construct(int $type, $old, $new) { $this->type = $type; @@ -9141,15 +8477,17 @@ namespace PHPUnitPHAR\PhpParser\Internal; * Myers, Eugene W. "An O (ND) difference algorithm and its variations." * Algorithmica 1.1 (1986): 251-266. * + * @template T * @internal */ class Differ { + /** @var callable(T, T): bool */ private $isEqual; /** * Create differ over the given equality relation. * - * @param callable $isEqual Equality relation with signature function($a, $b) : bool + * @param callable(T, T): bool $isEqual Equality relation */ public function __construct(callable $isEqual) { @@ -9158,13 +8496,15 @@ class Differ /** * Calculate diff (edit script) from $old to $new. * - * @param array $old Original array - * @param array $new New array + * @param T[] $old Original array + * @param T[] $new New array * * @return DiffElem[] Diff (edit script) */ - public function diff(array $old, array $new) + public function diff(array $old, array $new): array { + $old = \array_values($old); + $new = \array_values($new); list($trace, $x, $y) = $this->calculateTrace($old, $new); return $this->extractDiff($trace, $x, $y, $old, $new); } @@ -9174,19 +8514,24 @@ class Differ * If a sequence of remove operations is followed by the same number of add operations, these * will be coalesced into replace operations. * - * @param array $old Original array - * @param array $new New array + * @param T[] $old Original array + * @param T[] $new New array * * @return DiffElem[] Diff (edit script), including replace operations */ - public function diffWithReplacements(array $old, array $new) + public function diffWithReplacements(array $old, array $new): array { return $this->coalesceReplacements($this->diff($old, $new)); } - private function calculateTrace(array $a, array $b) + /** + * @param T[] $old + * @param T[] $new + * @return array{array>, int, int} + */ + private function calculateTrace(array $old, array $new): array { - $n = \count($a); - $m = \count($b); + $n = \count($old); + $m = \count($new); $max = $n + $m; $v = [1 => 0]; $trace = []; @@ -9199,7 +8544,7 @@ class Differ $x = $v[$k - 1] + 1; } $y = $x - $k; - while ($x < $n && $y < $m && ($this->isEqual)($a[$x], $b[$y])) { + while ($x < $n && $y < $m && ($this->isEqual)($old[$x], $new[$y])) { $x++; $y++; } @@ -9211,7 +8556,13 @@ class Differ } throw new \Exception('Should not happen'); } - private function extractDiff(array $trace, int $x, int $y, array $a, array $b) + /** + * @param array> $trace + * @param T[] $old + * @param T[] $new + * @return DiffElem[] + */ + private function extractDiff(array $trace, int $x, int $y, array $old, array $new): array { $result = []; for ($d = \count($trace) - 1; $d >= 0; $d--) { @@ -9225,7 +8576,7 @@ class Differ $prevX = $v[$prevK]; $prevY = $prevX - $prevK; while ($x > $prevX && $y > $prevY) { - $result[] = new DiffElem(DiffElem::TYPE_KEEP, $a[$x - 1], $b[$y - 1]); + $result[] = new DiffElem(DiffElem::TYPE_KEEP, $old[$x - 1], $new[$y - 1]); $x--; $y--; } @@ -9233,15 +8584,15 @@ class Differ break; } while ($x > $prevX) { - $result[] = new DiffElem(DiffElem::TYPE_REMOVE, $a[$x - 1], null); + $result[] = new DiffElem(DiffElem::TYPE_REMOVE, $old[$x - 1], null); $x--; } while ($y > $prevY) { - $result[] = new DiffElem(DiffElem::TYPE_ADD, null, $b[$y - 1]); + $result[] = new DiffElem(DiffElem::TYPE_ADD, null, $new[$y - 1]); $y--; } } - return \array_reverse($result); + return array_reverse($result); } /** * Coalesce equal-length sequences of remove+add into a replace operation. @@ -9249,7 +8600,7 @@ class Differ * @param DiffElem[] $diff * @return DiffElem[] */ - private function coalesceReplacements(array $diff) + private function coalesceReplacements(array $diff): array { $newDiff = []; $c = \count($diff); @@ -9302,17 +8653,25 @@ use PHPUnitPHAR\PhpParser\Node\Expr; class PrintableNewAnonClassNode extends Expr { /** @var Node\AttributeGroup[] PHP attribute groups */ - public $attrGroups; + public array $attrGroups; /** @var int Modifiers */ - public $flags; - /** @var Node\Arg[] Arguments */ - public $args; + public int $flags; + /** @var (Node\Arg|Node\VariadicPlaceholder)[] Arguments */ + public array $args; /** @var null|Node\Name Name of extended class */ - public $extends; + public ?Node\Name $extends; /** @var Node\Name[] Names of implemented interfaces */ - public $implements; + public array $implements; /** @var Node\Stmt[] Statements */ - public $stmts; + public array $stmts; + /** + * @param Node\AttributeGroup[] $attrGroups PHP attribute groups + * @param (Node\Arg|Node\VariadicPlaceholder)[] $args Arguments + * @param Node\Name|null $extends Name of extended class + * @param Node\Name[] $implements Names of implemented interfaces + * @param Node\Stmt[] $stmts Statements + * @param array $attributes Attributes + */ public function __construct(array $attrGroups, int $flags, array $args, ?Node\Name $extends, array $implements, array $stmts, array $attributes) { parent::__construct($attributes); @@ -9323,19 +8682,19 @@ class PrintableNewAnonClassNode extends Expr $this->implements = $implements; $this->stmts = $stmts; } - public static function fromNewNode(Expr\New_ $newNode) + public static function fromNewNode(Expr\New_ $newNode): self { $class = $newNode->class; - \assert($class instanceof Node\Stmt\Class_); + assert($class instanceof Node\Stmt\Class_); // We don't assert that $class->name is null here, to allow consumers to assign unique names // to anonymous classes for their own purposes. We simplify ignore the name here. return new self($class->attrGroups, $class->flags, $newNode->args, $class->extends, $class->implements, $class->stmts, $newNode->getAttributes()); } - public function getType() : string + public function getType(): string { return 'Expr_PrintableNewAnonClass'; } - public function getSubNodeNames() : array + public function getSubNodeNames(): array { return ['attrGroups', 'flags', 'args', 'extends', 'implements', 'stmts']; } @@ -9345,6 +8704,215 @@ class PrintableNewAnonClassNode extends Expr declare (strict_types=1); namespace PHPUnitPHAR\PhpParser\Internal; +if (\PHP_VERSION_ID >= 80000) { + class TokenPolyfill extends \PhpToken + { + } + return; +} +/** + * This is a polyfill for the PhpToken class introduced in PHP 8.0. We do not actually polyfill + * PhpToken, because composer might end up picking a different polyfill implementation, which does + * not meet our requirements. + * + * @internal + */ +class TokenPolyfill +{ + /** @var int The ID of the token. Either a T_* constant of a character code < 256. */ + public int $id; + /** @var string The textual content of the token. */ + public string $text; + /** @var int The 1-based starting line of the token (or -1 if unknown). */ + public int $line; + /** @var int The 0-based starting position of the token (or -1 if unknown). */ + public int $pos; + /** @var array Tokens ignored by the PHP parser. */ + private const IGNORABLE_TOKENS = [\T_WHITESPACE => \true, \T_COMMENT => \true, \T_DOC_COMMENT => \true, \T_OPEN_TAG => \true]; + /** @var array Tokens that may be part of a T_NAME_* identifier. */ + private static array $identifierTokens; + /** + * Create a Token with the given ID and text, as well optional line and position information. + */ + final public function __construct(int $id, string $text, int $line = -1, int $pos = -1) + { + $this->id = $id; + $this->text = $text; + $this->line = $line; + $this->pos = $pos; + } + /** + * Get the name of the token. For single-char tokens this will be the token character. + * Otherwise it will be a T_* style name, or null if the token ID is unknown. + */ + public function getTokenName(): ?string + { + if ($this->id < 256) { + return \chr($this->id); + } + $name = token_name($this->id); + return ($name === 'UNKNOWN') ? null : $name; + } + /** + * Check whether the token is of the given kind. The kind may be either an integer that matches + * the token ID, a string that matches the token text, or an array of integers/strings. In the + * latter case, the function returns true if any of the kinds in the array match. + * + * @param int|string|(int|string)[] $kind + */ + public function is($kind): bool + { + if (\is_int($kind)) { + return $this->id === $kind; + } + if (\is_string($kind)) { + return $this->text === $kind; + } + if (\is_array($kind)) { + foreach ($kind as $entry) { + if (\is_int($entry)) { + if ($this->id === $entry) { + return \true; + } + } elseif (\is_string($entry)) { + if ($this->text === $entry) { + return \true; + } + } else { + throw new \TypeError('Argument #1 ($kind) must only have elements of type string|int, ' . gettype($entry) . ' given'); + } + } + return \false; + } + throw new \TypeError('Argument #1 ($kind) must be of type string|int|array, ' . gettype($kind) . ' given'); + } + /** + * Check whether this token would be ignored by the PHP parser. Returns true for T_WHITESPACE, + * T_COMMENT, T_DOC_COMMENT and T_OPEN_TAG, and false for everything else. + */ + public function isIgnorable(): bool + { + return isset(self::IGNORABLE_TOKENS[$this->id]); + } + /** + * Return the textual content of the token. + */ + public function __toString(): string + { + return $this->text; + } + /** + * Tokenize the given source code and return an array of tokens. + * + * This performs certain canonicalizations to match the PHP 8.0 token format: + * * Bad characters are represented using T_BAD_CHARACTER rather than omitted. + * * T_COMMENT does not include trailing newlines, instead the newline is part of a following + * T_WHITESPACE token. + * * Namespaced names are represented using T_NAME_* tokens. + * + * @return static[] + */ + public static function tokenize(string $code, int $flags = 0): array + { + self::init(); + $tokens = []; + $line = 1; + $pos = 0; + $origTokens = \token_get_all($code, $flags); + $numTokens = \count($origTokens); + for ($i = 0; $i < $numTokens; $i++) { + $token = $origTokens[$i]; + if (\is_string($token)) { + if (\strlen($token) === 2) { + // b" and B" are tokenized as single-char tokens, even though they aren't. + $tokens[] = new static(\ord('"'), $token, $line, $pos); + $pos += 2; + } else { + $tokens[] = new static(\ord($token), $token, $line, $pos); + $pos++; + } + } else { + $id = $token[0]; + $text = $token[1]; + // Emulate PHP 8.0 comment format, which does not include trailing whitespace anymore. + if ($id === \T_COMMENT && \substr($text, 0, 2) !== '/*' && \preg_match('/(\r\n|\n|\r)$/D', $text, $matches)) { + $trailingNewline = $matches[0]; + $text = \substr($text, 0, -\strlen($trailingNewline)); + $tokens[] = new static($id, $text, $line, $pos); + $pos += \strlen($text); + if ($i + 1 < $numTokens && $origTokens[$i + 1][0] === \T_WHITESPACE) { + // Move trailing newline into following T_WHITESPACE token, if it already exists. + $origTokens[$i + 1][1] = $trailingNewline . $origTokens[$i + 1][1]; + $origTokens[$i + 1][2]--; + } else { + // Otherwise, we need to create a new T_WHITESPACE token. + $tokens[] = new static(\T_WHITESPACE, $trailingNewline, $line, $pos); + $line++; + $pos += \strlen($trailingNewline); + } + continue; + } + // Emulate PHP 8.0 T_NAME_* tokens, by combining sequences of T_NS_SEPARATOR and + // T_STRING into a single token. + if ($id === \T_NS_SEPARATOR || isset(self::$identifierTokens[$id])) { + $newText = $text; + $lastWasSeparator = $id === \T_NS_SEPARATOR; + for ($j = $i + 1; $j < $numTokens; $j++) { + if ($lastWasSeparator) { + if (!isset(self::$identifierTokens[$origTokens[$j][0]])) { + break; + } + $lastWasSeparator = \false; + } else { + if ($origTokens[$j][0] !== \T_NS_SEPARATOR) { + break; + } + $lastWasSeparator = \true; + } + $newText .= $origTokens[$j][1]; + } + if ($lastWasSeparator) { + // Trailing separator is not part of the name. + $j--; + $newText = \substr($newText, 0, -1); + } + if ($j > $i + 1) { + if ($id === \T_NS_SEPARATOR) { + $id = \T_NAME_FULLY_QUALIFIED; + } elseif ($id === \T_NAMESPACE) { + $id = \T_NAME_RELATIVE; + } else { + $id = \T_NAME_QUALIFIED; + } + $tokens[] = new static($id, $newText, $line, $pos); + $pos += \strlen($newText); + $i = $j - 1; + continue; + } + } + $tokens[] = new static($id, $text, $line, $pos); + $line += \substr_count($text, "\n"); + $pos += \strlen($text); + } + } + return $tokens; + } + /** Initialize private static state needed by tokenize(). */ + private static function init(): void + { + if (isset(self::$identifierTokens)) { + return; + } + // Based on semi_reserved production. + self::$identifierTokens = \array_fill_keys([\T_STRING, \T_STATIC, \T_ABSTRACT, \T_FINAL, \T_PRIVATE, \T_PROTECTED, \T_PUBLIC, \T_READONLY, \T_INCLUDE, \T_INCLUDE_ONCE, \T_EVAL, \T_REQUIRE, \T_REQUIRE_ONCE, \T_LOGICAL_OR, \T_LOGICAL_XOR, \T_LOGICAL_AND, \T_INSTANCEOF, \T_NEW, \T_CLONE, \T_EXIT, \T_IF, \T_ELSEIF, \T_ELSE, \T_ENDIF, \T_ECHO, \T_DO, \T_WHILE, \T_ENDWHILE, \T_FOR, \T_ENDFOR, \T_FOREACH, \T_ENDFOREACH, \T_DECLARE, \T_ENDDECLARE, \T_AS, \T_TRY, \T_CATCH, \T_FINALLY, \T_THROW, \T_USE, \T_INSTEADOF, \T_GLOBAL, \T_VAR, \T_UNSET, \T_ISSET, \T_EMPTY, \T_CONTINUE, \T_GOTO, \T_FUNCTION, \T_CONST, \T_RETURN, \T_PRINT, \T_YIELD, \T_LIST, \T_SWITCH, \T_ENDSWITCH, \T_CASE, \T_DEFAULT, \T_BREAK, \T_ARRAY, \T_CALLABLE, \T_EXTENDS, \T_IMPLEMENTS, \T_NAMESPACE, \T_TRAIT, \T_INTERFACE, \T_CLASS, \T_CLASS_C, \T_TRAIT_C, \T_FUNC_C, \T_METHOD_C, \T_LINE, \T_FILE, \T_DIR, \T_NS_C, \T_HALT_COMPILER, \T_FN, \T_MATCH], \true); + } +} +haveTokenImmediatelyBefore($startPos, '(') && $this->haveTokenImmediatelyAfter($endPos, ')'); } @@ -9382,11 +8948,9 @@ class TokenStream * Whether the given position is immediately surrounded by braces. * * @param int $startPos Start position - * @param int $endPos End position - * - * @return bool + * @param int $endPos End position */ - public function haveBraces(int $startPos, int $endPos) : bool + public function haveBraces(int $startPos, int $endPos): bool { return ($this->haveTokenImmediatelyBefore($startPos, '{') || $this->haveTokenImmediatelyBefore($startPos, \T_CURLY_OPEN)) && $this->haveTokenImmediatelyAfter($endPos, '}'); } @@ -9395,21 +8959,21 @@ class TokenStream * * During this check whitespace and comments are skipped. * - * @param int $pos Position before which the token should occur + * @param int $pos Position before which the token should occur * @param int|string $expectedTokenType Token to check for * * @return bool Whether the expected token was found */ - public function haveTokenImmediatelyBefore(int $pos, $expectedTokenType) : bool + public function haveTokenImmediatelyBefore(int $pos, $expectedTokenType): bool { $tokens = $this->tokens; $pos--; for (; $pos >= 0; $pos--) { - $tokenType = $tokens[$pos][0]; - if ($tokenType === $expectedTokenType) { + $token = $tokens[$pos]; + if ($token->is($expectedTokenType)) { return \true; } - if ($tokenType !== \T_WHITESPACE && $tokenType !== \T_COMMENT && $tokenType !== \T_DOC_COMMENT) { + if (!$token->isIgnorable()) { break; } } @@ -9420,48 +8984,50 @@ class TokenStream * * During this check whitespace and comments are skipped. * - * @param int $pos Position after which the token should occur + * @param int $pos Position after which the token should occur * @param int|string $expectedTokenType Token to check for * * @return bool Whether the expected token was found */ - public function haveTokenImmediatelyAfter(int $pos, $expectedTokenType) : bool + public function haveTokenImmediatelyAfter(int $pos, $expectedTokenType): bool { $tokens = $this->tokens; $pos++; - for (; $pos < \count($tokens); $pos++) { - $tokenType = $tokens[$pos][0]; - if ($tokenType === $expectedTokenType) { + for ($c = \count($tokens); $pos < $c; $pos++) { + $token = $tokens[$pos]; + if ($token->is($expectedTokenType)) { return \true; } - if ($tokenType !== \T_WHITESPACE && $tokenType !== \T_COMMENT && $tokenType !== \T_DOC_COMMENT) { + if (!$token->isIgnorable()) { break; } } return \false; } - public function skipLeft(int $pos, $skipTokenType) + /** @param int|string|(int|string)[] $skipTokenType */ + public function skipLeft(int $pos, $skipTokenType): int { $tokens = $this->tokens; $pos = $this->skipLeftWhitespace($pos); if ($skipTokenType === \T_WHITESPACE) { return $pos; } - if ($tokens[$pos][0] !== $skipTokenType) { + if (!$tokens[$pos]->is($skipTokenType)) { // Shouldn't happen. The skip token MUST be there throw new \Exception('Encountered unexpected token'); } $pos--; return $this->skipLeftWhitespace($pos); } - public function skipRight(int $pos, $skipTokenType) + /** @param int|string|(int|string)[] $skipTokenType */ + public function skipRight(int $pos, $skipTokenType): int { $tokens = $this->tokens; $pos = $this->skipRightWhitespace($pos); if ($skipTokenType === \T_WHITESPACE) { return $pos; } - if ($tokens[$pos][0] !== $skipTokenType) { + if (!$tokens[$pos]->is($skipTokenType)) { // Shouldn't happen. The skip token MUST be there throw new \Exception('Encountered unexpected token'); } @@ -9474,12 +9040,11 @@ class TokenStream * @param int $pos Token position * @return int Non-whitespace token position */ - public function skipLeftWhitespace(int $pos) + public function skipLeftWhitespace(int $pos): int { $tokens = $this->tokens; for (; $pos >= 0; $pos--) { - $type = $tokens[$pos][0]; - if ($type !== \T_WHITESPACE && $type !== \T_COMMENT && $type !== \T_DOC_COMMENT) { + if (!$tokens[$pos]->isIgnorable()) { break; } } @@ -9491,23 +9056,22 @@ class TokenStream * @param int $pos Token position * @return int Non-whitespace token position */ - public function skipRightWhitespace(int $pos) + public function skipRightWhitespace(int $pos): int { $tokens = $this->tokens; for ($count = \count($tokens); $pos < $count; $pos++) { - $type = $tokens[$pos][0]; - if ($type !== \T_WHITESPACE && $type !== \T_COMMENT && $type !== \T_DOC_COMMENT) { + if (!$tokens[$pos]->isIgnorable()) { break; } } return $pos; } - public function findRight(int $pos, $findTokenType) + /** @param int|string|(int|string)[] $findTokenType */ + public function findRight(int $pos, $findTokenType): int { $tokens = $this->tokens; for ($count = \count($tokens); $pos < $count; $pos++) { - $type = $tokens[$pos][0]; - if ($type === $findTokenType) { + if ($tokens[$pos]->is($findTokenType)) { return $pos; } } @@ -9521,21 +9085,17 @@ class TokenStream * @param int|string $tokenType Token type to look for * @return bool Whether the token occurs in the given range */ - public function haveTokenInRange(int $startPos, int $endPos, $tokenType) + public function haveTokenInRange(int $startPos, int $endPos, $tokenType): bool { $tokens = $this->tokens; for ($pos = $startPos; $pos < $endPos; $pos++) { - if ($tokens[$pos][0] === $tokenType) { + if ($tokens[$pos]->is($tokenType)) { return \true; } } return \false; } - public function haveBracesInRange(int $startPos, int $endPos) - { - return $this->haveTokenInRange($startPos, $endPos, '{') || $this->haveTokenInRange($startPos, $endPos, \T_CURLY_OPEN) || $this->haveTokenInRange($startPos, $endPos, '}'); - } - public function haveTagInRange(int $startPos, int $endPos) : bool + public function haveTagInRange(int $startPos, int $endPos): bool { return $this->haveTokenInRange($startPos, $endPos, \T_OPEN_TAG) || $this->haveTokenInRange($startPos, $endPos, \T_CLOSE_TAG); } @@ -9546,42 +9106,35 @@ class TokenStream * * @return int Indentation depth (in spaces) */ - public function getIndentationBefore(int $pos) : int + public function getIndentationBefore(int $pos): int { return $this->indentMap[$pos]; } /** * Get the code corresponding to a token offset range, optionally adjusted for indentation. * - * @param int $from Token start position (inclusive) - * @param int $to Token end position (exclusive) + * @param int $from Token start position (inclusive) + * @param int $to Token end position (exclusive) * @param int $indent By how much the code should be indented (can be negative as well) * * @return string Code corresponding to token range, adjusted for indentation */ - public function getTokenCode(int $from, int $to, int $indent) : string + public function getTokenCode(int $from, int $to, int $indent): string { $tokens = $this->tokens; $result = ''; for ($pos = $from; $pos < $to; $pos++) { $token = $tokens[$pos]; - if (\is_array($token)) { - $type = $token[0]; - $content = $token[1]; - if ($type === \T_CONSTANT_ENCAPSED_STRING || $type === \T_ENCAPSED_AND_WHITESPACE) { - $result .= $content; - } else { - // TODO Handle non-space indentation - if ($indent < 0) { - $result .= \str_replace("\n" . \str_repeat(" ", -$indent), "\n", $content); - } elseif ($indent > 0) { - $result .= \str_replace("\n", "\n" . \str_repeat(" ", $indent), $content); - } else { - $result .= $content; - } - } + $id = $token->id; + $text = $token->text; + if ($id === \T_CONSTANT_ENCAPSED_STRING || $id === \T_ENCAPSED_AND_WHITESPACE) { + $result .= $text; + } else if ($indent < 0) { + $result .= str_replace("\n" . str_repeat(" ", -$indent), "\n", $text); + } elseif ($indent > 0) { + $result .= str_replace("\n", "\n" . str_repeat(" ", $indent), $text); } else { - $result .= $token; + $result .= $text; } } return $result; @@ -9591,17 +9144,20 @@ class TokenStream * * @return int[] Token position to indentation map */ - private function calcIndentMap() + private function calcIndentMap(): array { $indentMap = []; $indent = 0; - foreach ($this->tokens as $token) { + foreach ($this->tokens as $i => $token) { $indentMap[] = $indent; - if ($token[0] === \T_WHITESPACE) { - $content = $token[1]; + if ($token->id === \T_WHITESPACE) { + $content = $token->text; $newlinePos = \strrpos($content, "\n"); if (\false !== $newlinePos) { $indent = \strlen($content) - $newlinePos - 1; + } elseif ($i === 1 && $this->tokens[0]->id === \T_OPEN_TAG && $this->tokens[0]->text[\strlen($this->tokens[0]->text) - 1] === "\n") { + // Special case: Newline at the end of opening tag followed by whitespace. + $indent = \strlen($content); } } } @@ -9617,16 +9173,21 @@ namespace PHPUnitPHAR\PhpParser; class JsonDecoder { - /** @var \ReflectionClass[] Node type to reflection class map */ - private $reflectionClassCache; + /** @var \ReflectionClass[] Node type to reflection class map */ + private array $reflectionClassCache; + /** @return mixed */ public function decode(string $json) { - $value = \json_decode($json, \true); - if (\json_last_error()) { - throw new \RuntimeException('JSON decoding error: ' . \json_last_error_msg()); + $value = json_decode($json, \true); + if (json_last_error()) { + throw new \RuntimeException('JSON decoding error: ' . json_last_error_msg()); } return $this->decodeRecursive($value); } + /** + * @param mixed $value + * @return mixed + */ private function decodeRecursive($value) { if (\is_array($value)) { @@ -9640,7 +9201,7 @@ class JsonDecoder } return $value; } - private function decodeArray(array $array) : array + private function decodeArray(array $array): array { $decodedArray = []; foreach ($array as $key => $value) { @@ -9648,14 +9209,13 @@ class JsonDecoder } return $decodedArray; } - private function decodeNode(array $value) : Node + private function decodeNode(array $value): Node { $nodeType = $value['nodeType']; if (!\is_string($nodeType)) { throw new \RuntimeException('Node type must be a string'); } $reflectionClass = $this->reflectionClassFromNodeType($nodeType); - /** @var Node $node */ $node = $reflectionClass->newInstanceWithoutConstructor(); if (isset($value['attributes'])) { if (!\is_array($value['attributes'])) { @@ -9671,15 +9231,16 @@ class JsonDecoder } return $node; } - private function decodeComment(array $value) : Comment + private function decodeComment(array $value): Comment { - $className = $value['nodeType'] === 'Comment' ? Comment::class : Comment\Doc::class; + $className = ($value['nodeType'] === 'Comment') ? Comment::class : Comment\Doc::class; if (!isset($value['text'])) { throw new \RuntimeException('Comment must have text'); } return new $className($value['text'], $value['line'] ?? -1, $value['filePos'] ?? -1, $value['tokenPos'] ?? -1, $value['endLine'] ?? -1, $value['endFilePos'] ?? -1, $value['endTokenPos'] ?? -1); } - private function reflectionClassFromNodeType(string $nodeType) : \ReflectionClass + /** @return \ReflectionClass */ + private function reflectionClassFromNodeType(string $nodeType): \ReflectionClass { if (!isset($this->reflectionClassCache[$nodeType])) { $className = $this->classNameFromNodeType($nodeType); @@ -9687,14 +9248,15 @@ class JsonDecoder } return $this->reflectionClassCache[$nodeType]; } - private function classNameFromNodeType(string $nodeType) : string + /** @return class-string */ + private function classNameFromNodeType(string $nodeType): string { - $className = 'PhpParser\\Node\\' . \strtr($nodeType, '_', '\\'); - if (\class_exists($className)) { + $className = 'PhpParser\Node\\' . strtr($nodeType, '_', '\\'); + if (class_exists($className)) { return $className; } $className .= '_'; - if (\class_exists($className)) { + if (class_exists($className)) { return $className; } throw new \RuntimeException("Unknown node type \"{$nodeType}\""); @@ -9705,464 +9267,90 @@ class JsonDecoder declare (strict_types=1); namespace PHPUnitPHAR\PhpParser; -use PHPUnitPHAR\PhpParser\Parser\Tokens; +require __DIR__ . '/compatibility_tokens.php'; class Lexer { - protected $code; - protected $tokens; - protected $pos; - protected $line; - protected $filePos; - protected $prevCloseTagHasNewline; - protected $tokenMap; - protected $dropTokens; - protected $identifierTokens; - private $attributeStartLineUsed; - private $attributeEndLineUsed; - private $attributeStartTokenPosUsed; - private $attributeEndTokenPosUsed; - private $attributeStartFilePosUsed; - private $attributeEndFilePosUsed; - private $attributeCommentsUsed; - /** - * Creates a Lexer. - * - * @param array $options Options array. Currently only the 'usedAttributes' option is supported, - * which is an array of attributes to add to the AST nodes. Possible - * attributes are: 'comments', 'startLine', 'endLine', 'startTokenPos', - * 'endTokenPos', 'startFilePos', 'endFilePos'. The option defaults to the - * first three. For more info see getNextToken() docs. - */ - public function __construct(array $options = []) - { - // Create Map from internal tokens to PhpParser tokens. - $this->defineCompatibilityTokens(); - $this->tokenMap = $this->createTokenMap(); - $this->identifierTokens = $this->createIdentifierTokenMap(); - // map of tokens to drop while lexing (the map is only used for isset lookup, - // that's why the value is simply set to 1; the value is never actually used.) - $this->dropTokens = \array_fill_keys([\T_WHITESPACE, \T_OPEN_TAG, \T_COMMENT, \T_DOC_COMMENT, \T_BAD_CHARACTER], 1); - $defaultAttributes = ['comments', 'startLine', 'endLine']; - $usedAttributes = \array_fill_keys($options['usedAttributes'] ?? $defaultAttributes, \true); - // Create individual boolean properties to make these checks faster. - $this->attributeStartLineUsed = isset($usedAttributes['startLine']); - $this->attributeEndLineUsed = isset($usedAttributes['endLine']); - $this->attributeStartTokenPosUsed = isset($usedAttributes['startTokenPos']); - $this->attributeEndTokenPosUsed = isset($usedAttributes['endTokenPos']); - $this->attributeStartFilePosUsed = isset($usedAttributes['startFilePos']); - $this->attributeEndFilePosUsed = isset($usedAttributes['endFilePos']); - $this->attributeCommentsUsed = isset($usedAttributes['comments']); - } - /** - * Initializes the lexer for lexing the provided source code. - * - * This function does not throw if lexing errors occur. Instead, errors may be retrieved using - * the getErrors() method. - * - * @param string $code The source code to lex + /** + * Tokenize the provided source code. + * + * The token array is in the same format as provided by the PhpToken::tokenize() method in + * PHP 8.0. The tokens are instances of PhpParser\Token, to abstract over a polyfill + * implementation in earlier PHP version. + * + * The token array is terminated by a sentinel token with token ID 0. + * The token array does not discard any tokens (i.e. whitespace and comments are included). + * The token position attributes are against this token array. + * + * @param string $code The source code to tokenize. * @param ErrorHandler|null $errorHandler Error handler to use for lexing errors. Defaults to - * ErrorHandler\Throwing + * ErrorHandler\Throwing. + * @return Token[] Tokens */ - public function startLexing(string $code, ?ErrorHandler $errorHandler = null) + public function tokenize(string $code, ?ErrorHandler $errorHandler = null): array { if (null === $errorHandler) { $errorHandler = new ErrorHandler\Throwing(); } - $this->code = $code; - // keep the code around for __halt_compiler() handling - $this->pos = -1; - $this->line = 1; - $this->filePos = 0; - // If inline HTML occurs without preceding code, treat it as if it had a leading newline. - // This ensures proper composability, because having a newline is the "safe" assumption. - $this->prevCloseTagHasNewline = \true; - $scream = \ini_set('xdebug.scream', '0'); - $this->tokens = @\token_get_all($code); - $this->postprocessTokens($errorHandler); + $scream = ini_set('xdebug.scream', '0'); + $tokens = @Token::tokenize($code); + $this->postprocessTokens($tokens, $errorHandler); if (\false !== $scream) { - \ini_set('xdebug.scream', $scream); - } - } - private function handleInvalidCharacterRange($start, $end, $line, ErrorHandler $errorHandler) - { - $tokens = []; - for ($i = $start; $i < $end; $i++) { - $chr = $this->code[$i]; - if ($chr === "\x00") { - // PHP cuts error message after null byte, so need special case - $errorMsg = 'Unexpected null byte'; - } else { - $errorMsg = \sprintf('Unexpected character "%s" (ASCII %d)', $chr, \ord($chr)); - } - $tokens[] = [\T_BAD_CHARACTER, $chr, $line]; - $errorHandler->handleError(new Error($errorMsg, ['startLine' => $line, 'endLine' => $line, 'startFilePos' => $i, 'endFilePos' => $i])); + ini_set('xdebug.scream', $scream); } return $tokens; } - /** - * Check whether comment token is unterminated. - * - * @return bool - */ - private function isUnterminatedComment($token) : bool - { - return ($token[0] === \T_COMMENT || $token[0] === \T_DOC_COMMENT) && \substr($token[1], 0, 2) === '/*' && \substr($token[1], -2) !== '*/'; - } - protected function postprocessTokens(ErrorHandler $errorHandler) - { - // PHP's error handling for token_get_all() is rather bad, so if we want detailed - // error information we need to compute it ourselves. Invalid character errors are - // detected by finding "gaps" in the token array. Unterminated comments are detected - // by checking if a trailing comment has a "*/" at the end. - // - // Additionally, we perform a number of canonicalizations here: - // * Use the PHP 8.0 comment format, which does not include trailing whitespace anymore. - // * Use PHP 8.0 T_NAME_* tokens. - // * Use PHP 8.1 T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG and - // T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG tokens used to disambiguate intersection types. - $filePos = 0; - $line = 1; - $numTokens = \count($this->tokens); - for ($i = 0; $i < $numTokens; $i++) { - $token = $this->tokens[$i]; - // Since PHP 7.4 invalid characters are represented by a T_BAD_CHARACTER token. - // In this case we only need to emit an error. - if ($token[0] === \T_BAD_CHARACTER) { - $this->handleInvalidCharacterRange($filePos, $filePos + 1, $line, $errorHandler); - } - if ($token[0] === \T_COMMENT && \substr($token[1], 0, 2) !== '/*' && \preg_match('/(\\r\\n|\\n|\\r)$/D', $token[1], $matches)) { - $trailingNewline = $matches[0]; - $token[1] = \substr($token[1], 0, -\strlen($trailingNewline)); - $this->tokens[$i] = $token; - if (isset($this->tokens[$i + 1]) && $this->tokens[$i + 1][0] === \T_WHITESPACE) { - // Move trailing newline into following T_WHITESPACE token, if it already exists. - $this->tokens[$i + 1][1] = $trailingNewline . $this->tokens[$i + 1][1]; - $this->tokens[$i + 1][2]--; - } else { - // Otherwise, we need to create a new T_WHITESPACE token. - \array_splice($this->tokens, $i + 1, 0, [[\T_WHITESPACE, $trailingNewline, $line]]); - $numTokens++; - } - } - // Emulate PHP 8 T_NAME_* tokens, by combining sequences of T_NS_SEPARATOR and T_STRING - // into a single token. - if (\is_array($token) && ($token[0] === \T_NS_SEPARATOR || isset($this->identifierTokens[$token[0]]))) { - $lastWasSeparator = $token[0] === \T_NS_SEPARATOR; - $text = $token[1]; - for ($j = $i + 1; isset($this->tokens[$j]); $j++) { - if ($lastWasSeparator) { - if (!isset($this->identifierTokens[$this->tokens[$j][0]])) { - break; - } - $lastWasSeparator = \false; - } else { - if ($this->tokens[$j][0] !== \T_NS_SEPARATOR) { - break; - } - $lastWasSeparator = \true; - } - $text .= $this->tokens[$j][1]; - } - if ($lastWasSeparator) { - // Trailing separator is not part of the name. - $j--; - $text = \substr($text, 0, -1); - } - if ($j > $i + 1) { - if ($token[0] === \T_NS_SEPARATOR) { - $type = \T_NAME_FULLY_QUALIFIED; - } else { - if ($token[0] === \T_NAMESPACE) { - $type = \T_NAME_RELATIVE; - } else { - $type = \T_NAME_QUALIFIED; - } - } - $token = [$type, $text, $line]; - \array_splice($this->tokens, $i, $j - $i, [$token]); - $numTokens -= $j - $i - 1; - } - } - if ($token === '&') { - $next = $i + 1; - while (isset($this->tokens[$next]) && $this->tokens[$next][0] === \T_WHITESPACE) { - $next++; - } - $followedByVarOrVarArg = isset($this->tokens[$next]) && ($this->tokens[$next][0] === \T_VARIABLE || $this->tokens[$next][0] === \T_ELLIPSIS); - $this->tokens[$i] = $token = [$followedByVarOrVarArg ? \T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG : \T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG, '&', $line]; - } - $tokenValue = \is_string($token) ? $token : $token[1]; - $tokenLen = \strlen($tokenValue); - if (\substr($this->code, $filePos, $tokenLen) !== $tokenValue) { - // Something is missing, must be an invalid character - $nextFilePos = \strpos($this->code, $tokenValue, $filePos); - $badCharTokens = $this->handleInvalidCharacterRange($filePos, $nextFilePos, $line, $errorHandler); - $filePos = (int) $nextFilePos; - \array_splice($this->tokens, $i, 0, $badCharTokens); - $numTokens += \count($badCharTokens); - $i += \count($badCharTokens); - } - $filePos += $tokenLen; - $line += \substr_count($tokenValue, "\n"); - } - if ($filePos !== \strlen($this->code)) { - if (\substr($this->code, $filePos, 2) === '/*') { - // Unlike PHP, HHVM will drop unterminated comments entirely - $comment = \substr($this->code, $filePos); - $errorHandler->handleError(new Error('Unterminated comment', ['startLine' => $line, 'endLine' => $line + \substr_count($comment, "\n"), 'startFilePos' => $filePos, 'endFilePos' => $filePos + \strlen($comment)])); - // Emulate the PHP behavior - $isDocComment = isset($comment[3]) && $comment[3] === '*'; - $this->tokens[] = [$isDocComment ? \T_DOC_COMMENT : \T_COMMENT, $comment, $line]; - } else { - // Invalid characters at the end of the input - $badCharTokens = $this->handleInvalidCharacterRange($filePos, \strlen($this->code), $line, $errorHandler); - $this->tokens = \array_merge($this->tokens, $badCharTokens); - } - return; - } - if (\count($this->tokens) > 0) { - // Check for unterminated comment - $lastToken = $this->tokens[\count($this->tokens) - 1]; - if ($this->isUnterminatedComment($lastToken)) { - $errorHandler->handleError(new Error('Unterminated comment', ['startLine' => $line - \substr_count($lastToken[1], "\n"), 'endLine' => $line, 'startFilePos' => $filePos - \strlen($lastToken[1]), 'endFilePos' => $filePos])); - } - } - } - /** - * Fetches the next token. - * - * The available attributes are determined by the 'usedAttributes' option, which can - * be specified in the constructor. The following attributes are supported: - * - * * 'comments' => Array of PhpParser\Comment or PhpParser\Comment\Doc instances, - * representing all comments that occurred between the previous - * non-discarded token and the current one. - * * 'startLine' => Line in which the node starts. - * * 'endLine' => Line in which the node ends. - * * 'startTokenPos' => Offset into the token array of the first token in the node. - * * 'endTokenPos' => Offset into the token array of the last token in the node. - * * 'startFilePos' => Offset into the code string of the first character that is part of the node. - * * 'endFilePos' => Offset into the code string of the last character that is part of the node. - * - * @param mixed $value Variable to store token content in - * @param mixed $startAttributes Variable to store start attributes in - * @param mixed $endAttributes Variable to store end attributes in - * - * @return int Token id - */ - public function getNextToken(&$value = null, &$startAttributes = null, &$endAttributes = null) : int + private function handleInvalidCharacter(Token $token, ErrorHandler $errorHandler): void { - $startAttributes = []; - $endAttributes = []; - while (1) { - if (isset($this->tokens[++$this->pos])) { - $token = $this->tokens[$this->pos]; - } else { - // EOF token with ID 0 - $token = "\x00"; - } - if ($this->attributeStartLineUsed) { - $startAttributes['startLine'] = $this->line; - } - if ($this->attributeStartTokenPosUsed) { - $startAttributes['startTokenPos'] = $this->pos; - } - if ($this->attributeStartFilePosUsed) { - $startAttributes['startFilePos'] = $this->filePos; - } - if (\is_string($token)) { - $value = $token; - if (isset($token[1])) { - // bug in token_get_all - $this->filePos += 2; - $id = \ord('"'); - } else { - $this->filePos += 1; - $id = \ord($token); - } - } elseif (!isset($this->dropTokens[$token[0]])) { - $value = $token[1]; - $id = $this->tokenMap[$token[0]]; - if (\T_CLOSE_TAG === $token[0]) { - $this->prevCloseTagHasNewline = \false !== \strpos($token[1], "\n") || \false !== \strpos($token[1], "\r"); - } elseif (\T_INLINE_HTML === $token[0]) { - $startAttributes['hasLeadingNewline'] = $this->prevCloseTagHasNewline; - } - $this->line += \substr_count($value, "\n"); - $this->filePos += \strlen($value); - } else { - $origLine = $this->line; - $origFilePos = $this->filePos; - $this->line += \substr_count($token[1], "\n"); - $this->filePos += \strlen($token[1]); - if (\T_COMMENT === $token[0] || \T_DOC_COMMENT === $token[0]) { - if ($this->attributeCommentsUsed) { - $comment = \T_DOC_COMMENT === $token[0] ? new Comment\Doc($token[1], $origLine, $origFilePos, $this->pos, $this->line, $this->filePos - 1, $this->pos) : new Comment($token[1], $origLine, $origFilePos, $this->pos, $this->line, $this->filePos - 1, $this->pos); - $startAttributes['comments'][] = $comment; - } - } - continue; - } - if ($this->attributeEndLineUsed) { - $endAttributes['endLine'] = $this->line; - } - if ($this->attributeEndTokenPosUsed) { - $endAttributes['endTokenPos'] = $this->pos; - } - if ($this->attributeEndFilePosUsed) { - $endAttributes['endFilePos'] = $this->filePos - 1; - } - return $id; + $chr = $token->text; + if ($chr === "\x00") { + // PHP cuts error message after null byte, so need special case + $errorMsg = 'Unexpected null byte'; + } else { + $errorMsg = sprintf('Unexpected character "%s" (ASCII %d)', $chr, ord($chr)); } - throw new \RuntimeException('Reached end of lexer loop'); + $errorHandler->handleError(new Error($errorMsg, ['startLine' => $token->line, 'endLine' => $token->line, 'startFilePos' => $token->pos, 'endFilePos' => $token->pos])); } - /** - * Returns the token array for current code. - * - * The token array is in the same format as provided by the - * token_get_all() function and does not discard tokens (i.e. - * whitespace and comments are included). The token position - * attributes are against this token array. - * - * @return array Array of tokens in token_get_all() format - */ - public function getTokens() : array + private function isUnterminatedComment(Token $token): bool { - return $this->tokens; + return $token->is([\T_COMMENT, \T_DOC_COMMENT]) && substr($token->text, 0, 2) === '/*' && substr($token->text, -2) !== '*/'; } /** - * Handles __halt_compiler() by returning the text after it. - * - * @return string Remaining text + * @param list $tokens */ - public function handleHaltCompiler() : string + protected function postprocessTokens(array &$tokens, ErrorHandler $errorHandler): void { - // text after T_HALT_COMPILER, still including (); - $textAfter = \substr($this->code, $this->filePos); - // ensure that it is followed by (); - // this simplifies the situation, by not allowing any comments - // in between of the tokens. - if (!\preg_match('~^\\s*\\(\\s*\\)\\s*(?:;|\\?>\\r?\\n?)~', $textAfter, $matches)) { - throw new Error('__HALT_COMPILER must be followed by "();"'); - } - // prevent the lexer from returning any further tokens - $this->pos = \count($this->tokens); - // return with (); removed - return \substr($textAfter, \strlen($matches[0])); - } - private function defineCompatibilityTokens() - { - static $compatTokensDefined = \false; - if ($compatTokensDefined) { + // This function reports errors (bad characters and unterminated comments) in the token + // array, and performs certain canonicalizations: + // * Use PHP 8.1 T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG and + // T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG tokens used to disambiguate intersection types. + // * Add a sentinel token with ID 0. + $numTokens = \count($tokens); + if ($numTokens === 0) { + // Empty input edge case: Just add the sentinel token. + $tokens[] = new Token(0, "\x00", 1, 0); return; } - $compatTokens = [ - // PHP 7.4 - 'T_BAD_CHARACTER', - 'T_FN', - 'T_COALESCE_EQUAL', - // PHP 8.0 - 'T_NAME_QUALIFIED', - 'T_NAME_FULLY_QUALIFIED', - 'T_NAME_RELATIVE', - 'T_MATCH', - 'T_NULLSAFE_OBJECT_OPERATOR', - 'T_ATTRIBUTE', - // PHP 8.1 - 'T_ENUM', - 'T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG', - 'T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG', - 'T_READONLY', - ]; - // PHP-Parser might be used together with another library that also emulates some or all - // of these tokens. Perform a sanity-check that all already defined tokens have been - // assigned a unique ID. - $usedTokenIds = []; - foreach ($compatTokens as $token) { - if (\defined($token)) { - $tokenId = \constant($token); - $clashingToken = $usedTokenIds[$tokenId] ?? null; - if ($clashingToken !== null) { - throw new \Error(\sprintf('Token %s has same ID as token %s, ' . 'you may be using a library with broken token emulation', $token, $clashingToken)); - } - $usedTokenIds[$tokenId] = $token; - } - } - // Now define any tokens that have not yet been emulated. Try to assign IDs from -1 - // downwards, but skip any IDs that may already be in use. - $newTokenId = -1; - foreach ($compatTokens as $token) { - if (!\defined($token)) { - while (isset($usedTokenIds[$newTokenId])) { - $newTokenId--; - } - \define($token, $newTokenId); - $newTokenId--; + for ($i = 0; $i < $numTokens; $i++) { + $token = $tokens[$i]; + if ($token->id === \T_BAD_CHARACTER) { + $this->handleInvalidCharacter($token, $errorHandler); } - } - $compatTokensDefined = \true; - } - /** - * Creates the token map. - * - * The token map maps the PHP internal token identifiers - * to the identifiers used by the Parser. Additionally it - * maps T_OPEN_TAG_WITH_ECHO to T_ECHO and T_CLOSE_TAG to ';'. - * - * @return array The token map - */ - protected function createTokenMap() : array - { - $tokenMap = []; - // 256 is the minimum possible token number, as everything below - // it is an ASCII value - for ($i = 256; $i < 1000; ++$i) { - if (\T_DOUBLE_COLON === $i) { - // T_DOUBLE_COLON is equivalent to T_PAAMAYIM_NEKUDOTAYIM - $tokenMap[$i] = Tokens::T_PAAMAYIM_NEKUDOTAYIM; - } elseif (\T_OPEN_TAG_WITH_ECHO === $i) { - // T_OPEN_TAG_WITH_ECHO with dropped T_OPEN_TAG results in T_ECHO - $tokenMap[$i] = Tokens::T_ECHO; - } elseif (\T_CLOSE_TAG === $i) { - // T_CLOSE_TAG is equivalent to ';' - $tokenMap[$i] = \ord(';'); - } elseif ('UNKNOWN' !== ($name = \token_name($i))) { - if ('T_HASHBANG' === $name) { - // HHVM uses a special token for #! hashbang lines - $tokenMap[$i] = Tokens::T_INLINE_HTML; - } elseif (\defined($name = Tokens::class . '::' . $name)) { - // Other tokens can be mapped directly - $tokenMap[$i] = \constant($name); + if ($token->id === \ord('&')) { + $next = $i + 1; + while (isset($tokens[$next]) && $tokens[$next]->id === \T_WHITESPACE) { + $next++; } + $followedByVarOrVarArg = isset($tokens[$next]) && $tokens[$next]->is([\T_VARIABLE, \T_ELLIPSIS]); + $token->id = $followedByVarOrVarArg ? \T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG : \T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG; } } - // HHVM uses a special token for numbers that overflow to double - if (\defined('T_ONUMBER')) { - $tokenMap[\T_ONUMBER] = Tokens::T_DNUMBER; + // Check for unterminated comment + $lastToken = $tokens[$numTokens - 1]; + if ($this->isUnterminatedComment($lastToken)) { + $errorHandler->handleError(new Error('Unterminated comment', ['startLine' => $lastToken->line, 'endLine' => $lastToken->getEndLine(), 'startFilePos' => $lastToken->pos, 'endFilePos' => $lastToken->getEndPos()])); } - // HHVM also has a separate token for the __COMPILER_HALT_OFFSET__ constant - if (\defined('T_COMPILER_HALT_OFFSET')) { - $tokenMap[\T_COMPILER_HALT_OFFSET] = Tokens::T_STRING; - } - // Assign tokens for which we define compatibility constants, as token_name() does not know them. - $tokenMap[\T_FN] = Tokens::T_FN; - $tokenMap[\T_COALESCE_EQUAL] = Tokens::T_COALESCE_EQUAL; - $tokenMap[\T_NAME_QUALIFIED] = Tokens::T_NAME_QUALIFIED; - $tokenMap[\T_NAME_FULLY_QUALIFIED] = Tokens::T_NAME_FULLY_QUALIFIED; - $tokenMap[\T_NAME_RELATIVE] = Tokens::T_NAME_RELATIVE; - $tokenMap[\T_MATCH] = Tokens::T_MATCH; - $tokenMap[\T_NULLSAFE_OBJECT_OPERATOR] = Tokens::T_NULLSAFE_OBJECT_OPERATOR; - $tokenMap[\T_ATTRIBUTE] = Tokens::T_ATTRIBUTE; - $tokenMap[\T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG] = Tokens::T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG; - $tokenMap[\T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG] = Tokens::T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG; - $tokenMap[\T_ENUM] = Tokens::T_ENUM; - $tokenMap[\T_READONLY] = Tokens::T_READONLY; - return $tokenMap; - } - private function createIdentifierTokenMap() : array - { - // Based on semi_reserved production. - return \array_fill_keys([\T_STRING, \T_STATIC, \T_ABSTRACT, \T_FINAL, \T_PRIVATE, \T_PROTECTED, \T_PUBLIC, \T_READONLY, \T_INCLUDE, \T_INCLUDE_ONCE, \T_EVAL, \T_REQUIRE, \T_REQUIRE_ONCE, \T_LOGICAL_OR, \T_LOGICAL_XOR, \T_LOGICAL_AND, \T_INSTANCEOF, \T_NEW, \T_CLONE, \T_EXIT, \T_IF, \T_ELSEIF, \T_ELSE, \T_ENDIF, \T_ECHO, \T_DO, \T_WHILE, \T_ENDWHILE, \T_FOR, \T_ENDFOR, \T_FOREACH, \T_ENDFOREACH, \T_DECLARE, \T_ENDDECLARE, \T_AS, \T_TRY, \T_CATCH, \T_FINALLY, \T_THROW, \T_USE, \T_INSTEADOF, \T_GLOBAL, \T_VAR, \T_UNSET, \T_ISSET, \T_EMPTY, \T_CONTINUE, \T_GOTO, \T_FUNCTION, \T_CONST, \T_RETURN, \T_PRINT, \T_YIELD, \T_LIST, \T_SWITCH, \T_ENDSWITCH, \T_CASE, \T_DEFAULT, \T_BREAK, \T_ARRAY, \T_CALLABLE, \T_EXTENDS, \T_IMPLEMENTS, \T_NAMESPACE, \T_TRAIT, \T_INTERFACE, \T_CLASS, \T_CLASS_C, \T_TRAIT_C, \T_FUNC_C, \T_METHOD_C, \T_LINE, \T_FILE, \T_DIR, \T_NS_C, \T_HALT_COMPILER, \T_FN, \T_MATCH], \true); + // Add sentinel token. + $tokens[] = new Token(0, "\x00", $lastToken->getEndLine(), $lastToken->getEndPos()); } } */ + private array $emulators = []; + private PhpVersion $targetPhpVersion; + private PhpVersion $hostPhpVersion; /** - * @param mixed[] $options Lexer options. In addition to the usual options, - * accepts a 'phpVersion' string that specifies the - * version to emulate. Defaults to newest supported. + * @param PhpVersion|null $phpVersion PHP version to emulate. Defaults to newest supported. */ - public function __construct(array $options = []) + public function __construct(?PhpVersion $phpVersion = null) { - $this->targetPhpVersion = $options['phpVersion'] ?? Emulative::PHP_8_2; - unset($options['phpVersion']); - parent::__construct($options); - $emulators = [new FlexibleDocStringEmulator(), new FnTokenEmulator(), new MatchTokenEmulator(), new CoaleseEqualTokenEmulator(), new NumericLiteralSeparatorEmulator(), new NullsafeTokenEmulator(), new AttributeEmulator(), new EnumTokenEmulator(), new ReadonlyTokenEmulator(), new ExplicitOctalEmulator(), new ReadonlyFunctionTokenEmulator()]; + $this->targetPhpVersion = $phpVersion ?? PhpVersion::getNewestSupported(); + $this->hostPhpVersion = PhpVersion::getHostVersion(); + $emulators = [new MatchTokenEmulator(), new NullsafeTokenEmulator(), new AttributeEmulator(), new EnumTokenEmulator(), new ReadonlyTokenEmulator(), new ExplicitOctalEmulator(), new ReadonlyFunctionTokenEmulator()]; // Collect emulators that are relevant for the PHP version we're running // and the PHP version we're targeting for emulation. foreach ($emulators as $emulator) { $emulatorPhpVersion = $emulator->getPhpVersion(); if ($this->isForwardEmulationNeeded($emulatorPhpVersion)) { $this->emulators[] = $emulator; - } else { - if ($this->isReverseEmulationNeeded($emulatorPhpVersion)) { - $this->emulators[] = new ReverseEmulator($emulator); - } + } elseif ($this->isReverseEmulationNeeded($emulatorPhpVersion)) { + $this->emulators[] = new ReverseEmulator($emulator); } } } - public function startLexing(string $code, ?ErrorHandler $errorHandler = null) + public function tokenize(string $code, ?ErrorHandler $errorHandler = null): array { - $emulators = \array_filter($this->emulators, function ($emulator) use($code) { + $emulators = array_filter($this->emulators, function ($emulator) use ($code) { return $emulator->isEmulationNeeded($code); }); if (empty($emulators)) { // Nothing to emulate, yay - parent::startLexing($code, $errorHandler); - return; + return parent::tokenize($code, $errorHandler); + } + if ($errorHandler === null) { + $errorHandler = new ErrorHandler\Throwing(); } $this->patches = []; foreach ($emulators as $emulator) { $code = $emulator->preprocessCode($code, $this->patches); } $collector = new ErrorHandler\Collecting(); - parent::startLexing($code, $collector); + $tokens = parent::tokenize($code, $collector); $this->sortPatches(); - $this->fixupTokens(); + $tokens = $this->fixupTokens($tokens); $errors = $collector->getErrors(); if (!empty($errors)) { $this->fixupErrors($errors); @@ -10249,102 +9431,92 @@ class Emulative extends Lexer } } foreach ($emulators as $emulator) { - $this->tokens = $emulator->emulate($code, $this->tokens); + $tokens = $emulator->emulate($code, $tokens); } + return $tokens; } - private function isForwardEmulationNeeded(string $emulatorPhpVersion) : bool + private function isForwardEmulationNeeded(PhpVersion $emulatorPhpVersion): bool { - return \version_compare(\PHP_VERSION, $emulatorPhpVersion, '<') && \version_compare($this->targetPhpVersion, $emulatorPhpVersion, '>='); + return $this->hostPhpVersion->older($emulatorPhpVersion) && $this->targetPhpVersion->newerOrEqual($emulatorPhpVersion); } - private function isReverseEmulationNeeded(string $emulatorPhpVersion) : bool + private function isReverseEmulationNeeded(PhpVersion $emulatorPhpVersion): bool { - return \version_compare(\PHP_VERSION, $emulatorPhpVersion, '>=') && \version_compare($this->targetPhpVersion, $emulatorPhpVersion, '<'); + return $this->hostPhpVersion->newerOrEqual($emulatorPhpVersion) && $this->targetPhpVersion->older($emulatorPhpVersion); } - private function sortPatches() + private function sortPatches(): void { // Patches may be contributed by different emulators. // Make sure they are sorted by increasing patch position. - \usort($this->patches, function ($p1, $p2) { + usort($this->patches, function ($p1, $p2) { return $p1[0] <=> $p2[0]; }); } - private function fixupTokens() + /** + * @param list $tokens + * @return list + */ + private function fixupTokens(array $tokens): array { if (\count($this->patches) === 0) { - return; + return $tokens; } // Load first patch $patchIdx = 0; list($patchPos, $patchType, $patchText) = $this->patches[$patchIdx]; // We use a manual loop over the tokens, because we modify the array on the fly - $pos = 0; - for ($i = 0, $c = \count($this->tokens); $i < $c; $i++) { - $token = $this->tokens[$i]; - if (\is_string($token)) { - if ($patchPos === $pos) { - // Only support replacement for string tokens. - \assert($patchType === 'replace'); - $this->tokens[$i] = $patchText; - // Fetch the next patch - $patchIdx++; - if ($patchIdx >= \count($this->patches)) { - // No more patches, we're done - return; - } - list($patchPos, $patchType, $patchText) = $this->patches[$patchIdx]; - } - $pos += \strlen($token); - continue; - } - $len = \strlen($token[1]); - $posDelta = 0; + $posDelta = 0; + $lineDelta = 0; + for ($i = 0, $c = \count($tokens); $i < $c; $i++) { + $token = $tokens[$i]; + $pos = $token->pos; + $token->pos += $posDelta; + $token->line += $lineDelta; + $localPosDelta = 0; + $len = \strlen($token->text); while ($patchPos >= $pos && $patchPos < $pos + $len) { $patchTextLen = \strlen($patchText); if ($patchType === 'remove') { if ($patchPos === $pos && $patchTextLen === $len) { // Remove token entirely - \array_splice($this->tokens, $i, 1, []); + array_splice($tokens, $i, 1, []); $i--; $c--; } else { // Remove from token string - $this->tokens[$i][1] = \substr_replace($token[1], '', $patchPos - $pos + $posDelta, $patchTextLen); - $posDelta -= $patchTextLen; + $token->text = substr_replace($token->text, '', $patchPos - $pos + $localPosDelta, $patchTextLen); + $localPosDelta -= $patchTextLen; } + $lineDelta -= \substr_count($patchText, "\n"); } elseif ($patchType === 'add') { // Insert into the token string - $this->tokens[$i][1] = \substr_replace($token[1], $patchText, $patchPos - $pos + $posDelta, 0); - $posDelta += $patchTextLen; + $token->text = substr_replace($token->text, $patchText, $patchPos - $pos + $localPosDelta, 0); + $localPosDelta += $patchTextLen; + $lineDelta += \substr_count($patchText, "\n"); + } elseif ($patchType === 'replace') { + // Replace inside the token string + $token->text = substr_replace($token->text, $patchText, $patchPos - $pos + $localPosDelta, $patchTextLen); } else { - if ($patchType === 'replace') { - // Replace inside the token string - $this->tokens[$i][1] = \substr_replace($token[1], $patchText, $patchPos - $pos + $posDelta, $patchTextLen); - } else { - \assert(\false); - } + assert(\false); } // Fetch the next patch $patchIdx++; if ($patchIdx >= \count($this->patches)) { - // No more patches, we're done - return; + // No more patches. However, we still need to adjust position. + $patchPos = \PHP_INT_MAX; + break; } list($patchPos, $patchType, $patchText) = $this->patches[$patchIdx]; - // Multiple patches may apply to the same token. Reload the current one to check - // If the new patch applies - $token = $this->tokens[$i]; } - $pos += $len; + $posDelta += $localPosDelta; } - // A patch did not apply - \assert(\false); + return $tokens; } /** * Fixup line and position information in errors. * * @param Error[] $errors */ - private function fixupErrors(array $errors) + private function fixupErrors(array $errors): void { foreach ($errors as $error) { $attrs = $error->getAttributes(); @@ -10357,13 +9529,11 @@ class Emulative extends Lexer break; } if ($patchType === 'add') { - $posDelta += \strlen($patchText); - $lineDelta += \substr_count($patchText, "\n"); - } else { - if ($patchType === 'remove') { - $posDelta -= \strlen($patchText); - $lineDelta -= \substr_count($patchText, "\n"); - } + $posDelta += strlen($patchText); + $lineDelta += substr_count($patchText, "\n"); + } elseif ($patchType === 'remove') { + $posDelta -= strlen($patchText); + $lineDelta -= substr_count($patchText, "\n"); } } $attrs['startFilePos'] += $posDelta; @@ -10379,43 +9549,41 @@ class Emulative extends Lexer declare (strict_types=1); namespace PHPUnitPHAR\PhpParser\Lexer\TokenEmulator; -use PHPUnitPHAR\PhpParser\Lexer\Emulative; +use PHPUnitPHAR\PhpParser\PhpVersion; +use PHPUnitPHAR\PhpParser\Token; final class AttributeEmulator extends TokenEmulator { - public function getPhpVersion() : string + public function getPhpVersion(): PhpVersion { - return Emulative::PHP_8_0; + return PhpVersion::fromComponents(8, 0); } - public function isEmulationNeeded(string $code) : bool + public function isEmulationNeeded(string $code): bool { - return \strpos($code, '#[') !== \false; + return strpos($code, '#[') !== \false; } - public function emulate(string $code, array $tokens) : array + public function emulate(string $code, array $tokens): array { // We need to manually iterate and manage a count because we'll change // the tokens array on the way. - $line = 1; - for ($i = 0, $c = \count($tokens); $i < $c; ++$i) { - if ($tokens[$i] === '#' && isset($tokens[$i + 1]) && $tokens[$i + 1] === '[') { - \array_splice($tokens, $i, 2, [[\T_ATTRIBUTE, '#[', $line]]); + for ($i = 0, $c = count($tokens); $i < $c; ++$i) { + $token = $tokens[$i]; + if ($token->text === '#' && isset($tokens[$i + 1]) && $tokens[$i + 1]->text === '[') { + array_splice($tokens, $i, 2, [new Token(\T_ATTRIBUTE, '#[', $token->line, $token->pos)]); $c--; continue; } - if (\is_array($tokens[$i])) { - $line += \substr_count($tokens[$i][1], "\n"); - } } return $tokens; } - public function reverseEmulate(string $code, array $tokens) : array + public function reverseEmulate(string $code, array $tokens): array { // TODO return $tokens; } - public function preprocessCode(string $code, array &$patches) : string + public function preprocessCode(string $code, array &$patches): string { $pos = 0; - while (\false !== ($pos = \strpos($code, '#[', $pos))) { + while (\false !== $pos = strpos($code, '#[', $pos)) { // Replace #[ with %[ $code[$pos] = '%'; $patches[] = [$pos, 'replace', '#']; @@ -10429,65 +9597,24 @@ final class AttributeEmulator extends TokenEmulator declare (strict_types=1); namespace PHPUnitPHAR\PhpParser\Lexer\TokenEmulator; -use PHPUnitPHAR\PhpParser\Lexer\Emulative; -final class CoaleseEqualTokenEmulator extends TokenEmulator -{ - public function getPhpVersion() : string - { - return Emulative::PHP_7_4; - } - public function isEmulationNeeded(string $code) : bool - { - return \strpos($code, '??=') !== \false; - } - public function emulate(string $code, array $tokens) : array - { - // We need to manually iterate and manage a count because we'll change - // the tokens array on the way - $line = 1; - for ($i = 0, $c = \count($tokens); $i < $c; ++$i) { - if (isset($tokens[$i + 1])) { - if ($tokens[$i][0] === \T_COALESCE && $tokens[$i + 1] === '=') { - \array_splice($tokens, $i, 2, [[\T_COALESCE_EQUAL, '??=', $line]]); - $c--; - continue; - } - } - if (\is_array($tokens[$i])) { - $line += \substr_count($tokens[$i][1], "\n"); - } - } - return $tokens; - } - public function reverseEmulate(string $code, array $tokens) : array - { - // ??= was not valid code previously, don't bother. - return $tokens; - } -} -id === \T_WHITESPACE && $tokens[$pos + 2]->id === \T_STRING; } } resolveIntegerOrFloatToken($tokens[$i + 1][1]); - \array_splice($tokens, $i, 2, [[$tokenKind, '0' . $tokens[$i + 1][1], $tokens[$i][2]]]); + for ($i = 0, $c = count($tokens); $i < $c; ++$i) { + $token = $tokens[$i]; + if ($token->id == \T_LNUMBER && $token->text === '0' && isset($tokens[$i + 1]) && $tokens[$i + 1]->id == \T_STRING && preg_match('/[oO][0-7]+(?:_[0-7]+)*/', $tokens[$i + 1]->text)) { + $tokenKind = $this->resolveIntegerOrFloatToken($tokens[$i + 1]->text); + array_splice($tokens, $i, 2, [new Token($tokenKind, '0' . $tokens[$i + 1]->text, $token->line, $token->pos)]); $c--; } } return $tokens; } - private function resolveIntegerOrFloatToken(string $str) : int + private function resolveIntegerOrFloatToken(string $str): int { - $str = \substr($str, 1); - $str = \str_replace('_', '', $str); - $num = \octdec($str); - return \is_float($num) ? \T_DNUMBER : \T_LNUMBER; + $str = substr($str, 1); + $str = str_replace('_', '', $str); + $num = octdec($str); + return is_float($num) ? \T_DNUMBER : \T_LNUMBER; } - public function reverseEmulate(string $code, array $tokens) : array + public function reverseEmulate(string $code, array $tokens): array { // Explicit octals were not legal code previously, don't bother. return $tokens; @@ -10535,136 +9664,48 @@ class ExplicitOctalEmulator extends TokenEmulator declare (strict_types=1); namespace PHPUnitPHAR\PhpParser\Lexer\TokenEmulator; -use PHPUnitPHAR\PhpParser\Lexer\Emulative; -final class FlexibleDocStringEmulator extends TokenEmulator -{ - const FLEXIBLE_DOC_STRING_REGEX = <<<'REGEX' -/<<<[ \t]*(['"]?)([a-zA-Z_\x80-\xff][a-zA-Z0-9_\x80-\xff]*)\1\r?\n -(?:.*\r?\n)*? -(?\h*)\2(?![a-zA-Z0-9_\x80-\xff])(?(?:;?[\r\n])?)/x -REGEX; - public function getPhpVersion() : string - { - return Emulative::PHP_7_3; - } - public function isEmulationNeeded(string $code) : bool - { - return \strpos($code, '<<<') !== \false; - } - public function emulate(string $code, array $tokens) : array - { - // Handled by preprocessing + fixup. - return $tokens; - } - public function reverseEmulate(string $code, array $tokens) : array - { - // Not supported. - return $tokens; - } - public function preprocessCode(string $code, array &$patches) : string - { - if (!\preg_match_all(self::FLEXIBLE_DOC_STRING_REGEX, $code, $matches, \PREG_SET_ORDER | \PREG_OFFSET_CAPTURE)) { - // No heredoc/nowdoc found - return $code; - } - // Keep track of how much we need to adjust string offsets due to the modifications we - // already made - $posDelta = 0; - foreach ($matches as $match) { - $indentation = $match['indentation'][0]; - $indentationStart = $match['indentation'][1]; - $separator = $match['separator'][0]; - $separatorStart = $match['separator'][1]; - if ($indentation === '' && $separator !== '') { - // Ordinary heredoc/nowdoc - continue; - } - if ($indentation !== '') { - // Remove indentation - $indentationLen = \strlen($indentation); - $code = \substr_replace($code, '', $indentationStart + $posDelta, $indentationLen); - $patches[] = [$indentationStart + $posDelta, 'add', $indentation]; - $posDelta -= $indentationLen; - } - if ($separator === '') { - // Insert newline as separator - $code = \substr_replace($code, "\n", $separatorStart + $posDelta, 0); - $patches[] = [$separatorStart + $posDelta, 'remove', "\n"]; - $posDelta += 1; - } - } - return $code; - } -} -getKeywordString()) !== \false; + return strpos(strtolower($code), $this->getKeywordString()) !== \false; } - protected function isKeywordContext(array $tokens, int $pos) : bool + /** @param Token[] $tokens */ + protected function isKeywordContext(array $tokens, int $pos): bool { $previousNonSpaceToken = $this->getPreviousNonSpaceToken($tokens, $pos); - return $previousNonSpaceToken === null || $previousNonSpaceToken[0] !== \T_OBJECT_OPERATOR; + return $previousNonSpaceToken === null || $previousNonSpaceToken->id !== \T_OBJECT_OPERATOR; } - public function emulate(string $code, array $tokens) : array + public function emulate(string $code, array $tokens): array { $keywordString = $this->getKeywordString(); foreach ($tokens as $i => $token) { - if ($token[0] === \T_STRING && \strtolower($token[1]) === $keywordString && $this->isKeywordContext($tokens, $i)) { - $tokens[$i][0] = $this->getKeywordToken(); + if ($token->id === \T_STRING && strtolower($token->text) === $keywordString && $this->isKeywordContext($tokens, $i)) { + $token->id = $this->getKeywordToken(); } } return $tokens; } - /** - * @param mixed[] $tokens - * @return array|string|null - */ - private function getPreviousNonSpaceToken(array $tokens, int $start) + /** @param Token[] $tokens */ + private function getPreviousNonSpaceToken(array $tokens, int $start): ?Token { for ($i = $start - 1; $i >= 0; --$i) { - if ($tokens[$i][0] === \T_WHITESPACE) { + if ($tokens[$i]->id === \T_WHITESPACE) { continue; } return $tokens[$i]; } return null; } - public function reverseEmulate(string $code, array $tokens) : array + public function reverseEmulate(string $code, array $tokens): array { $keywordToken = $this->getKeywordToken(); - foreach ($tokens as $i => $token) { - if ($token[0] === $keywordToken) { - $tokens[$i][0] = \T_STRING; + foreach ($tokens as $token) { + if ($token->id === $keywordToken) { + $token->id = \T_STRING; } } return $tokens; @@ -10675,18 +9716,18 @@ abstract class KeywordEmulator extends TokenEmulator declare (strict_types=1); namespace PHPUnitPHAR\PhpParser\Lexer\TokenEmulator; -use PHPUnitPHAR\PhpParser\Lexer\Emulative; +use PHPUnitPHAR\PhpParser\PhpVersion; final class MatchTokenEmulator extends KeywordEmulator { - public function getPhpVersion() : string + public function getPhpVersion(): PhpVersion { - return Emulative::PHP_8_0; + return PhpVersion::fromComponents(8, 0); } - public function getKeywordString() : string + public function getKeywordString(): string { return 'match'; } - public function getKeywordToken() : int + public function getKeywordToken(): int { return \T_MATCH; } @@ -10696,45 +9737,44 @@ final class MatchTokenEmulator extends KeywordEmulator declare (strict_types=1); namespace PHPUnitPHAR\PhpParser\Lexer\TokenEmulator; -use PHPUnitPHAR\PhpParser\Lexer\Emulative; +use PHPUnitPHAR\PhpParser\PhpVersion; +use PHPUnitPHAR\PhpParser\Token; final class NullsafeTokenEmulator extends TokenEmulator { - public function getPhpVersion() : string + public function getPhpVersion(): PhpVersion { - return Emulative::PHP_8_0; + return PhpVersion::fromComponents(8, 0); } - public function isEmulationNeeded(string $code) : bool + public function isEmulationNeeded(string $code): bool { - return \strpos($code, '?->') !== \false; + return strpos($code, '?->') !== \false; } - public function emulate(string $code, array $tokens) : array + public function emulate(string $code, array $tokens): array { // We need to manually iterate and manage a count because we'll change // the tokens array on the way - $line = 1; - for ($i = 0, $c = \count($tokens); $i < $c; ++$i) { - if ($tokens[$i] === '?' && isset($tokens[$i + 1]) && $tokens[$i + 1][0] === \T_OBJECT_OPERATOR) { - \array_splice($tokens, $i, 2, [[\T_NULLSAFE_OBJECT_OPERATOR, '?->', $line]]); + for ($i = 0, $c = count($tokens); $i < $c; ++$i) { + $token = $tokens[$i]; + if ($token->text === '?' && isset($tokens[$i + 1]) && $tokens[$i + 1]->id === \T_OBJECT_OPERATOR) { + array_splice($tokens, $i, 2, [new Token(\T_NULLSAFE_OBJECT_OPERATOR, '?->', $token->line, $token->pos)]); $c--; continue; } // Handle ?-> inside encapsed string. - if ($tokens[$i][0] === \T_ENCAPSED_AND_WHITESPACE && isset($tokens[$i - 1]) && $tokens[$i - 1][0] === \T_VARIABLE && \preg_match('/^\\?->([a-zA-Z_\\x80-\\xff][a-zA-Z0-9_\\x80-\\xff]*)/', $tokens[$i][1], $matches)) { - $replacement = [[\T_NULLSAFE_OBJECT_OPERATOR, '?->', $line], [\T_STRING, $matches[1], $line]]; - if (\strlen($matches[0]) !== \strlen($tokens[$i][1])) { - $replacement[] = [\T_ENCAPSED_AND_WHITESPACE, \substr($tokens[$i][1], \strlen($matches[0])), $line]; + if ($token->id === \T_ENCAPSED_AND_WHITESPACE && isset($tokens[$i - 1]) && $tokens[$i - 1]->id === \T_VARIABLE && preg_match('/^\?->([a-zA-Z_\x80-\xff][a-zA-Z0-9_\x80-\xff]*)/', $token->text, $matches)) { + $replacement = [new Token(\T_NULLSAFE_OBJECT_OPERATOR, '?->', $token->line, $token->pos), new Token(\T_STRING, $matches[1], $token->line, $token->pos + 3)]; + $matchLen = \strlen($matches[0]); + if ($matchLen !== \strlen($token->text)) { + $replacement[] = new Token(\T_ENCAPSED_AND_WHITESPACE, \substr($token->text, $matchLen), $token->line, $token->pos + $matchLen); } - \array_splice($tokens, $i, 1, $replacement); + array_splice($tokens, $i, 1, $replacement); $c += \count($replacement) - 1; continue; } - if (\is_array($tokens[$i])) { - $line += \substr_count($tokens[$i][1], "\n"); - } } return $tokens; } - public function reverseEmulate(string $code, array $tokens) : array + public function reverseEmulate(string $code, array $tokens): array { // ?-> was not valid code previously, don't bother. return $tokens; @@ -10745,95 +9785,7 @@ final class NullsafeTokenEmulator extends TokenEmulator declare (strict_types=1); namespace PHPUnitPHAR\PhpParser\Lexer\TokenEmulator; -use PHPUnitPHAR\PhpParser\Lexer\Emulative; -final class NumericLiteralSeparatorEmulator extends TokenEmulator -{ - const BIN = '(?:0b[01]+(?:_[01]+)*)'; - const HEX = '(?:0x[0-9a-f]+(?:_[0-9a-f]+)*)'; - const DEC = '(?:[0-9]+(?:_[0-9]+)*)'; - const SIMPLE_FLOAT = '(?:' . self::DEC . '\\.' . self::DEC . '?|\\.' . self::DEC . ')'; - const EXP = '(?:e[+-]?' . self::DEC . ')'; - const FLOAT = '(?:' . self::SIMPLE_FLOAT . self::EXP . '?|' . self::DEC . self::EXP . ')'; - const NUMBER = '~' . self::FLOAT . '|' . self::BIN . '|' . self::HEX . '|' . self::DEC . '~iA'; - public function getPhpVersion() : string - { - return Emulative::PHP_7_4; - } - public function isEmulationNeeded(string $code) : bool - { - return \preg_match('~[0-9]_[0-9]~', $code) || \preg_match('~0x[0-9a-f]+_[0-9a-f]~i', $code); - } - public function emulate(string $code, array $tokens) : array - { - // We need to manually iterate and manage a count because we'll change - // the tokens array on the way - $codeOffset = 0; - for ($i = 0, $c = \count($tokens); $i < $c; ++$i) { - $token = $tokens[$i]; - $tokenLen = \strlen(\is_array($token) ? $token[1] : $token); - if ($token[0] !== \T_LNUMBER && $token[0] !== \T_DNUMBER) { - $codeOffset += $tokenLen; - continue; - } - $res = \preg_match(self::NUMBER, $code, $matches, 0, $codeOffset); - \assert($res, "No number at number token position"); - $match = $matches[0]; - $matchLen = \strlen($match); - if ($matchLen === $tokenLen) { - // Original token already holds the full number. - $codeOffset += $tokenLen; - continue; - } - $tokenKind = $this->resolveIntegerOrFloatToken($match); - $newTokens = [[$tokenKind, $match, $token[2]]]; - $numTokens = 1; - $len = $tokenLen; - while ($matchLen > $len) { - $nextToken = $tokens[$i + $numTokens]; - $nextTokenText = \is_array($nextToken) ? $nextToken[1] : $nextToken; - $nextTokenLen = \strlen($nextTokenText); - $numTokens++; - if ($matchLen < $len + $nextTokenLen) { - // Split trailing characters into a partial token. - \assert(\is_array($nextToken), "Partial token should be an array token"); - $partialText = \substr($nextTokenText, $matchLen - $len); - $newTokens[] = [$nextToken[0], $partialText, $nextToken[2]]; - break; - } - $len += $nextTokenLen; - } - \array_splice($tokens, $i, $numTokens, $newTokens); - $c -= $numTokens - \count($newTokens); - $codeOffset += $matchLen; - } - return $tokens; - } - private function resolveIntegerOrFloatToken(string $str) : int - { - $str = \str_replace('_', '', $str); - if (\stripos($str, '0b') === 0) { - $num = \bindec($str); - } elseif (\stripos($str, '0x') === 0) { - $num = \hexdec($str); - } elseif (\stripos($str, '0') === 0 && \ctype_digit($str)) { - $num = \octdec($str); - } else { - $num = +$str; - } - return \is_float($num) ? \T_DNUMBER : \T_LNUMBER; - } - public function reverseEmulate(string $code, array $tokens) : array - { - // Numeric separators were not legal code previously, don't bother. - return $tokens; - } -} -text === '(' || $tokens[$pos + 1]->id === \T_WHITESPACE && isset($tokens[$pos + 2]) && $tokens[$pos + 2]->text === '(')); } } emulator = $emulator; } - public function getPhpVersion() : string + public function getPhpVersion(): PhpVersion { return $this->emulator->getPhpVersion(); } - public function isEmulationNeeded(string $code) : bool + public function isEmulationNeeded(string $code): bool { return $this->emulator->isEmulationNeeded($code); } - public function emulate(string $code, array $tokens) : array + public function emulate(string $code, array $tokens): array { return $this->emulator->reverseEmulate($code, $tokens); } - public function reverseEmulate(string $code, array $tokens) : array + public function reverseEmulate(string $code, array $tokens): array { return $this->emulator->emulate($code, $tokens); } - public function preprocessCode(string $code, array &$patches) : string + public function preprocessCode(string $code, array &$patches): string { return $code; } @@ -10932,20 +9885,25 @@ final class ReverseEmulator extends TokenEmulator declare (strict_types=1); namespace PHPUnitPHAR\PhpParser\Lexer\TokenEmulator; +use PHPUnitPHAR\PhpParser\PhpVersion; +use PHPUnitPHAR\PhpParser\Token; /** @internal */ abstract class TokenEmulator { - public abstract function getPhpVersion() : string; - public abstract function isEmulationNeeded(string $code) : bool; + abstract public function getPhpVersion(): PhpVersion; + abstract public function isEmulationNeeded(string $code): bool; /** - * @return array Modified Tokens + * @param Token[] $tokens Original tokens + * @return Token[] Modified Tokens */ - public abstract function emulate(string $code, array $tokens) : array; + abstract public function emulate(string $code, array $tokens): array; /** - * @return array Modified Tokens + * @param Token[] $tokens Original tokens + * @return Token[] Modified Tokens */ - public abstract function reverseEmulate(string $code, array $tokens) : array; - public function preprocessCode(string $code, array &$patches) : string + abstract public function reverseEmulate(string $code, array $tokens): array; + /** @param array{int, string, string}[] $patches */ + public function preprocessCode(string $code, array &$patches): string { return $code; } @@ -10955,19 +9913,81 @@ abstract class TokenEmulator declare (strict_types=1); namespace PHPUnitPHAR\PhpParser; +/** + * Modifiers used (as a bit mask) by various flags subnodes, for example on classes, functions, + * properties and constants. + */ +final class Modifiers +{ + public const PUBLIC = 1; + public const PROTECTED = 2; + public const PRIVATE = 4; + public const STATIC = 8; + public const ABSTRACT = 16; + public const FINAL = 32; + public const READONLY = 64; + public const VISIBILITY_MASK = 1 | 2 | 4; + /** + * @internal + */ + public static function verifyClassModifier(int $a, int $b): void + { + if ($a & Modifiers::ABSTRACT && $b & Modifiers::ABSTRACT) { + throw new Error('Multiple abstract modifiers are not allowed'); + } + if ($a & Modifiers::FINAL && $b & Modifiers::FINAL) { + throw new Error('Multiple final modifiers are not allowed'); + } + if ($a & Modifiers::READONLY && $b & Modifiers::READONLY) { + throw new Error('Multiple readonly modifiers are not allowed'); + } + if ($a & 48 && $b & 48) { + throw new Error('Cannot use the final modifier on an abstract class'); + } + } + /** + * @internal + */ + public static function verifyModifier(int $a, int $b): void + { + if ($a & Modifiers::VISIBILITY_MASK && $b & Modifiers::VISIBILITY_MASK) { + throw new Error('Multiple access type modifiers are not allowed'); + } + if ($a & Modifiers::ABSTRACT && $b & Modifiers::ABSTRACT) { + throw new Error('Multiple abstract modifiers are not allowed'); + } + if ($a & Modifiers::STATIC && $b & Modifiers::STATIC) { + throw new Error('Multiple static modifiers are not allowed'); + } + if ($a & Modifiers::FINAL && $b & Modifiers::FINAL) { + throw new Error('Multiple final modifiers are not allowed'); + } + if ($a & Modifiers::READONLY && $b & Modifiers::READONLY) { + throw new Error('Multiple readonly modifiers are not allowed'); + } + if ($a & 48 && $b & 48) { + throw new Error('Cannot use the final modifier on an abstract class member'); + } + } +} + [aliasName => originalName]] */ - protected $aliases = []; + protected array $aliases = []; /** @var Name[][] Same as $aliases but preserving original case */ - protected $origAliases = []; + protected array $origAliases = []; /** @var ErrorHandler Error handler */ - protected $errorHandler; + protected ErrorHandler $errorHandler; /** * Create a name context. * @@ -10984,7 +10004,7 @@ class NameContext * * @param Name|null $namespace Null is the global namespace */ - public function startNamespace(?Name $namespace = null) + public function startNamespace(?Name $namespace = null): void { $this->namespace = $namespace; $this->origAliases = $this->aliases = [Stmt\Use_::TYPE_NORMAL => [], Stmt\Use_::TYPE_FUNCTION => [], Stmt\Use_::TYPE_CONSTANT => []]; @@ -10992,22 +10012,22 @@ class NameContext /** * Add an alias / import. * - * @param Name $name Original name - * @param string $aliasName Aliased name - * @param int $type One of Stmt\Use_::TYPE_* - * @param array $errorAttrs Attributes to use to report an error + * @param Name $name Original name + * @param string $aliasName Aliased name + * @param Stmt\Use_::TYPE_* $type One of Stmt\Use_::TYPE_* + * @param array $errorAttrs Attributes to use to report an error */ - public function addAlias(Name $name, string $aliasName, int $type, array $errorAttrs = []) + public function addAlias(Name $name, string $aliasName, int $type, array $errorAttrs = []): void { // Constant names are case sensitive, everything else case insensitive if ($type === Stmt\Use_::TYPE_CONSTANT) { $aliasLookupName = $aliasName; } else { - $aliasLookupName = \strtolower($aliasName); + $aliasLookupName = strtolower($aliasName); } if (isset($this->aliases[$type][$aliasLookupName])) { $typeStringMap = [Stmt\Use_::TYPE_NORMAL => '', Stmt\Use_::TYPE_FUNCTION => 'function ', Stmt\Use_::TYPE_CONSTANT => 'const ']; - $this->errorHandler->handleError(new Error(\sprintf('Cannot use %s%s as %s because the name is already in use', $typeStringMap[$type], $name, $aliasName), $errorAttrs)); + $this->errorHandler->handleError(new Error(sprintf('Cannot use %s%s as %s because the name is already in use', $typeStringMap[$type], $name, $aliasName), $errorAttrs)); return; } $this->aliases[$type][$aliasLookupName] = $name; @@ -11018,7 +10038,7 @@ class NameContext * * @return null|Name Namespace (or null if global namespace) */ - public function getNamespace() + public function getNamespace(): ?Name { return $this->namespace; } @@ -11026,16 +10046,16 @@ class NameContext * Get resolved name. * * @param Name $name Name to resolve - * @param int $type One of Stmt\Use_::TYPE_{FUNCTION|CONSTANT} + * @param Stmt\Use_::TYPE_* $type One of Stmt\Use_::TYPE_{FUNCTION|CONSTANT} * * @return null|Name Resolved name, or null if static resolution is not possible */ - public function getResolvedName(Name $name, int $type) + public function getResolvedName(Name $name, int $type): ?Name { // don't resolve special class names if ($type === Stmt\Use_::TYPE_NORMAL && $name->isSpecialClassName()) { if (!$name->isUnqualified()) { - $this->errorHandler->handleError(new Error(\sprintf("'\\%s' is an invalid class name", $name->toString()), $name->getAttributes())); + $this->errorHandler->handleError(new Error(sprintf("'\\%s' is an invalid class name", $name->toString()), $name->getAttributes())); } return $name; } @@ -11044,7 +10064,7 @@ class NameContext return $name; } // Try to resolve aliases - if (null !== ($resolvedName = $this->resolveAlias($name, $type))) { + if (null !== $resolvedName = $this->resolveAlias($name, $type)) { return $resolvedName; } if ($type !== Stmt\Use_::TYPE_NORMAL && $name->isUnqualified()) { @@ -11065,7 +10085,7 @@ class NameContext * * @return Name Resolved name */ - public function getResolvedClassName(Name $name) : Name + public function getResolvedClassName(Name $name): Name { return $this->getResolvedName($name, Stmt\Use_::TYPE_NORMAL); } @@ -11073,13 +10093,13 @@ class NameContext * Get possible ways of writing a fully qualified name (e.g., by making use of aliases). * * @param string $name Fully-qualified name (without leading namespace separator) - * @param int $type One of Stmt\Use_::TYPE_* + * @param Stmt\Use_::TYPE_* $type One of Stmt\Use_::TYPE_* * * @return Name[] Possible representations of the name */ - public function getPossibleNames(string $name, int $type) : array + public function getPossibleNames(string $name, int $type): array { - $lcName = \strtolower($name); + $lcName = strtolower($name); if ($type === Stmt\Use_::TYPE_NORMAL) { // self, parent and static must always be unqualified if ($lcName === "self" || $lcName === "parent" || $lcName === "static") { @@ -11088,7 +10108,7 @@ class NameContext } // Collect possible ways to write this name, starting with the fully-qualified name $possibleNames = [new FullyQualified($name)]; - if (null !== ($nsRelativeName = $this->getNamespaceRelativeName($name, $lcName, $type))) { + if (null !== $nsRelativeName = $this->getNamespaceRelativeName($name, $lcName, $type)) { // Make sure there is no alias that makes the normally namespace-relative name // into something else if (null === $this->resolveAlias($nsRelativeName, $type)) { @@ -11098,8 +10118,8 @@ class NameContext // Check for relevant namespace use statements foreach ($this->origAliases[Stmt\Use_::TYPE_NORMAL] as $alias => $orig) { $lcOrig = $orig->toLowerString(); - if (0 === \strpos($lcName, $lcOrig . '\\')) { - $possibleNames[] = new Name($alias . \substr($name, \strlen($lcOrig))); + if (0 === strpos($lcName, $lcOrig . '\\')) { + $possibleNames[] = new Name($alias . substr($name, strlen($lcOrig))); } } // Check for relevant type-specific use statements @@ -11110,11 +10130,8 @@ class NameContext if ($normalizedOrig === $this->normalizeConstName($name)) { $possibleNames[] = new Name($alias); } - } else { - // Everything else is case-insensitive - if ($orig->toLowerString() === $lcName) { - $possibleNames[] = new Name($alias); - } + } else if ($orig->toLowerString() === $lcName) { + $possibleNames[] = new Name($alias); } } return $possibleNames; @@ -11123,18 +10140,18 @@ class NameContext * Get shortest representation of this fully-qualified name. * * @param string $name Fully-qualified name (without leading namespace separator) - * @param int $type One of Stmt\Use_::TYPE_* + * @param Stmt\Use_::TYPE_* $type One of Stmt\Use_::TYPE_* * * @return Name Shortest representation */ - public function getShortName(string $name, int $type) : Name + public function getShortName(string $name, int $type): Name { $possibleNames = $this->getPossibleNames($name, $type); // Find shortest name $shortestName = null; $shortestLength = \INF; foreach ($possibleNames as $possibleName) { - $length = \strlen($possibleName->toCodeString()); + $length = strlen($possibleName->toCodeString()); if ($length < $shortestLength) { $shortestName = $possibleName; $shortestLength = $length; @@ -11142,19 +10159,19 @@ class NameContext } return $shortestName; } - private function resolveAlias(Name $name, $type) + private function resolveAlias(Name $name, int $type): ?FullyQualified { $firstPart = $name->getFirst(); if ($name->isQualified()) { // resolve aliases for qualified names, always against class alias table - $checkName = \strtolower($firstPart); + $checkName = strtolower($firstPart); if (isset($this->aliases[Stmt\Use_::TYPE_NORMAL][$checkName])) { $alias = $this->aliases[Stmt\Use_::TYPE_NORMAL][$checkName]; return FullyQualified::concat($alias, $name->slice(1), $name->getAttributes()); } } elseif ($name->isUnqualified()) { // constant aliases are case-sensitive, function aliases case-insensitive - $checkName = $type === Stmt\Use_::TYPE_CONSTANT ? $firstPart : \strtolower($firstPart); + $checkName = ($type === Stmt\Use_::TYPE_CONSTANT) ? $firstPart : strtolower($firstPart); if (isset($this->aliases[$type][$checkName])) { // resolve unqualified aliases return new FullyQualified($this->aliases[$type][$checkName], $name->getAttributes()); @@ -11163,7 +10180,7 @@ class NameContext // No applicable aliases return null; } - private function getNamespaceRelativeName(string $name, string $lcName, int $type) + private function getNamespaceRelativeName(string $name, string $lcName, int $type): ?Name { if (null === $this->namespace) { return new Name($name); @@ -11175,22 +10192,22 @@ class NameContext return new Name($name); } } - $namespacePrefix = \strtolower($this->namespace . '\\'); - if (0 === \strpos($lcName, $namespacePrefix)) { - return new Name(\substr($name, \strlen($namespacePrefix))); + $namespacePrefix = strtolower($this->namespace . '\\'); + if (0 === strpos($lcName, $namespacePrefix)) { + return new Name(substr($name, strlen($namespacePrefix))); } return null; } - private function normalizeConstName(string $name) + private function normalizeConstName(string $name): string { - $nsSep = \strrpos($name, '\\'); + $nsSep = strrpos($name, '\\'); if (\false === $nsSep) { return $name; } // Constants have case-insensitive namespace and case-sensitive short-name - $ns = \substr($name, 0, $nsSep); - $shortName = \substr($name, $nsSep + 1); - return \strtolower($ns) . '\\' . $shortName; + $ns = substr($name, 0, $nsSep); + $shortName = substr($name, $nsSep + 1); + return strtolower($ns) . '\\' . $shortName; } } */ - public function getAttributes() : array; + public function getAttributes(): array; /** * Replaces all the attributes of this node. * - * @param array $attributes + * @param array $attributes */ - public function setAttributes(array $attributes); + public function setAttributes(array $attributes): void; } $attributes Additional attributes * @param Identifier|null $name Parameter name (for named parameters) */ public function __construct(Expr $value, bool $byRef = \false, bool $unpack = \false, array $attributes = [], ?Identifier $name = null) @@ -11363,11 +10379,11 @@ class Arg extends NodeAbstract $this->byRef = $byRef; $this->unpack = $unpack; } - public function getSubNodeNames() : array + public function getSubNodeNames(): array { return ['name', 'value', 'byRef', 'unpack']; } - public function getType() : string + public function getType(): string { return 'Arg'; } @@ -11377,18 +10393,61 @@ class Arg extends NodeAbstract declare (strict_types=1); namespace PHPUnitPHAR\PhpParser\Node; +use PHPUnitPHAR\PhpParser\NodeAbstract; +class ArrayItem extends NodeAbstract +{ + /** @var null|Expr Key */ + public ?Expr $key; + /** @var Expr Value */ + public Expr $value; + /** @var bool Whether to assign by reference */ + public bool $byRef; + /** @var bool Whether to unpack the argument */ + public bool $unpack; + /** + * Constructs an array item node. + * + * @param Expr $value Value + * @param null|Expr $key Key + * @param bool $byRef Whether to assign by reference + * @param array $attributes Additional attributes + */ + public function __construct(Expr $value, ?Expr $key = null, bool $byRef = \false, array $attributes = [], bool $unpack = \false) + { + $this->attributes = $attributes; + $this->key = $key; + $this->value = $value; + $this->byRef = $byRef; + $this->unpack = $unpack; + } + public function getSubNodeNames(): array + { + return ['key', 'value', 'byRef', 'unpack']; + } + public function getType(): string + { + return 'ArrayItem'; + } +} +// @deprecated compatibility alias +class_alias(ArrayItem::class, Expr\ArrayItem::class); + Attribute arguments */ + public array $args; /** - * @param Node\Name $name Attribute name - * @param Arg[] $args Attribute arguments - * @param array $attributes Additional node attributes + * @param Node\Name $name Attribute name + * @param list $args Attribute arguments + * @param array $attributes Additional node attributes */ public function __construct(Name $name, array $args = [], array $attributes = []) { @@ -11396,11 +10455,11 @@ class Attribute extends NodeAbstract $this->name = $name; $this->args = $args; } - public function getSubNodeNames() : array + public function getSubNodeNames(): array { return ['name', 'args']; } - public function getType() : string + public function getType(): string { return 'Attribute'; } @@ -11410,26 +10469,25 @@ class Attribute extends NodeAbstract declare (strict_types=1); namespace PHPUnitPHAR\PhpParser\Node; -use PHPUnitPHAR\PhpParser\Node; use PHPUnitPHAR\PhpParser\NodeAbstract; class AttributeGroup extends NodeAbstract { /** @var Attribute[] Attributes */ - public $attrs; + public array $attrs; /** * @param Attribute[] $attrs PHP attributes - * @param array $attributes Additional node attributes + * @param array $attributes Additional node attributes */ public function __construct(array $attrs, array $attributes = []) { $this->attributes = $attributes; $this->attrs = $attrs; } - public function getSubNodeNames() : array + public function getSubNodeNames(): array { return ['attrs']; } - public function getType() : string + public function getType(): string { return 'AttributeGroup'; } @@ -11439,6 +10497,42 @@ class AttributeGroup extends NodeAbstract declare (strict_types=1); namespace PHPUnitPHAR\PhpParser\Node; +use PHPUnitPHAR\PhpParser\NodeAbstract; +class ClosureUse extends NodeAbstract +{ + /** @var Expr\Variable Variable to use */ + public Expr\Variable $var; + /** @var bool Whether to use by reference */ + public bool $byRef; + /** + * Constructs a closure use node. + * + * @param Expr\Variable $var Variable to use + * @param bool $byRef Whether to use by reference + * @param array $attributes Additional attributes + */ + public function __construct(Expr\Variable $var, bool $byRef = \false, array $attributes = []) + { + $this->attributes = $attributes; + $this->var = $var; + $this->byRef = $byRef; + } + public function getSubNodeNames(): array + { + return ['var', 'byRef']; + } + public function getType(): string + { + return 'ClosureUse'; + } +} +// @deprecated compatibility alias +class_alias(ClosureUse::class, Expr\ClosureUse::class); + $attributes Additional attributes */ public function __construct($name, Expr $value, array $attributes = []) { @@ -11475,11 +10569,11 @@ class Const_ extends NodeAbstract $this->name = \is_string($name) ? new Identifier($name) : $name; $this->value = $value; } - public function getSubNodeNames() : array + public function getSubNodeNames(): array { return ['name', 'value']; } - public function getType() : string + public function getType(): string { return 'Const'; } @@ -11489,6 +10583,43 @@ class Const_ extends NodeAbstract declare (strict_types=1); namespace PHPUnitPHAR\PhpParser\Node; +use PHPUnitPHAR\PhpParser\Node; +use PHPUnitPHAR\PhpParser\NodeAbstract; +class DeclareItem extends NodeAbstract +{ + /** @var Node\Identifier Key */ + public Identifier $key; + /** @var Node\Expr Value */ + public Expr $value; + /** + * Constructs a declare key=>value pair node. + * + * @param string|Node\Identifier $key Key + * @param Node\Expr $value Value + * @param array $attributes Additional attributes + */ + public function __construct($key, Node\Expr $value, array $attributes = []) + { + $this->attributes = $attributes; + $this->key = \is_string($key) ? new Node\Identifier($key) : $key; + $this->value = $value; + } + public function getSubNodeNames(): array + { + return ['key', 'value']; + } + public function getType(): string + { + return 'DeclareItem'; + } +} +// @deprecated compatibility alias +class_alias(DeclareItem::class, Stmt\DeclareDeclare::class); + $attributes Additional attributes */ public function __construct(Expr $var, ?Expr $dim = null, array $attributes = []) { @@ -11518,11 +10649,11 @@ class ArrayDimFetch extends Expr $this->var = $var; $this->dim = $dim; } - public function getSubNodeNames() : array + public function getSubNodeNames(): array { return ['var', 'dim']; } - public function getType() : string + public function getType(): string { return 'Expr_ArrayDimFetch'; } @@ -11530,75 +10661,41 @@ class ArrayDimFetch extends Expr attributes = $attributes; - $this->key = $key; - $this->value = $value; - $this->byRef = $byRef; - $this->unpack = $unpack; - } - public function getSubNodeNames() : array - { - return ['key', 'value', 'byRef', 'unpack']; - } - public function getType() : string - { - return 'Expr_ArrayItem'; - } -} +require __DIR__ . '/../ArrayItem.php'; $attributes Additional attributes */ public function __construct(array $items = [], array $attributes = []) { $this->attributes = $attributes; $this->items = $items; } - public function getSubNodeNames() : array + public function getSubNodeNames(): array { return ['items']; } - public function getType() : string + public function getType(): string { return 'Expr_Array'; } @@ -11613,48 +10710,54 @@ use PHPUnitPHAR\PhpParser\Node\Expr; use PHPUnitPHAR\PhpParser\Node\FunctionLike; class ArrowFunction extends Expr implements FunctionLike { - /** @var bool */ - public $static; - /** @var bool */ - public $byRef; + /** @var bool Whether the closure is static */ + public bool $static; + /** @var bool Whether to return by reference */ + public bool $byRef; /** @var Node\Param[] */ - public $params = []; + public array $params = []; /** @var null|Node\Identifier|Node\Name|Node\ComplexType */ - public $returnType; - /** @var Expr */ - public $expr; + public ?Node $returnType; + /** @var Expr Expression body */ + public Expr $expr; /** @var Node\AttributeGroup[] */ - public $attrGroups; - /** - * @param array $subNodes Array of the following optional subnodes: - * 'static' => false : Whether the closure is static - * 'byRef' => false : Whether to return by reference - * 'params' => array() : Parameters - * 'returnType' => null : Return type - * 'expr' => Expr : Expression body - * 'attrGroups' => array() : PHP attribute groups - * @param array $attributes Additional attributes - */ - public function __construct(array $subNodes = [], array $attributes = []) + public array $attrGroups; + /** + * @param array{ + * expr: Expr, + * static?: bool, + * byRef?: bool, + * params?: Node\Param[], + * returnType?: null|Node\Identifier|Node\Name|Node\ComplexType, + * attrGroups?: Node\AttributeGroup[] + * } $subNodes Array of the following subnodes: + * 'expr' : Expression body + * 'static' => false : Whether the closure is static + * 'byRef' => false : Whether to return by reference + * 'params' => array() : Parameters + * 'returnType' => null : Return type + * 'attrGroups' => array() : PHP attribute groups + * @param array $attributes Additional attributes + */ + public function __construct(array $subNodes, array $attributes = []) { $this->attributes = $attributes; $this->static = $subNodes['static'] ?? \false; $this->byRef = $subNodes['byRef'] ?? \false; $this->params = $subNodes['params'] ?? []; - $returnType = $subNodes['returnType'] ?? null; - $this->returnType = \is_string($returnType) ? new Node\Identifier($returnType) : $returnType; + $this->returnType = $subNodes['returnType'] ?? null; $this->expr = $subNodes['expr']; $this->attrGroups = $subNodes['attrGroups'] ?? []; } - public function getSubNodeNames() : array + public function getSubNodeNames(): array { return ['attrGroups', 'static', 'byRef', 'params', 'returnType', 'expr']; } - public function returnsByRef() : bool + public function returnsByRef(): bool { return $this->byRef; } - public function getParams() : array + public function getParams(): array { return $this->params; } @@ -11662,18 +10765,18 @@ class ArrowFunction extends Expr implements FunctionLike { return $this->returnType; } - public function getAttrGroups() : array + public function getAttrGroups(): array { return $this->attrGroups; } /** * @return Node\Stmt\Return_[] */ - public function getStmts() : array + public function getStmts(): array { return [new Node\Stmt\Return_($this->expr)]; } - public function getType() : string + public function getType(): string { return 'Expr_ArrowFunction'; } @@ -11687,15 +10790,15 @@ use PHPUnitPHAR\PhpParser\Node\Expr; class Assign extends Expr { /** @var Expr Variable */ - public $var; + public Expr $var; /** @var Expr Expression */ - public $expr; + public Expr $expr; /** * Constructs an assignment node. * - * @param Expr $var Variable - * @param Expr $expr Expression - * @param array $attributes Additional attributes + * @param Expr $var Variable + * @param Expr $expr Expression + * @param array $attributes Additional attributes */ public function __construct(Expr $var, Expr $expr, array $attributes = []) { @@ -11703,11 +10806,11 @@ class Assign extends Expr $this->var = $var; $this->expr = $expr; } - public function getSubNodeNames() : array + public function getSubNodeNames(): array { return ['var', 'expr']; } - public function getType() : string + public function getType(): string { return 'Expr_Assign'; } @@ -11721,15 +10824,15 @@ use PHPUnitPHAR\PhpParser\Node\Expr; abstract class AssignOp extends Expr { /** @var Expr Variable */ - public $var; + public Expr $var; /** @var Expr Expression */ - public $expr; + public Expr $expr; /** * Constructs a compound assignment operation node. * - * @param Expr $var Variable - * @param Expr $expr Expression - * @param array $attributes Additional attributes + * @param Expr $var Variable + * @param Expr $expr Expression + * @param array $attributes Additional attributes */ public function __construct(Expr $var, Expr $expr, array $attributes = []) { @@ -11737,7 +10840,7 @@ abstract class AssignOp extends Expr $this->var = $var; $this->expr = $expr; } - public function getSubNodeNames() : array + public function getSubNodeNames(): array { return ['var', 'expr']; } @@ -11750,7 +10853,7 @@ namespace PHPUnitPHAR\PhpParser\Node\Expr\AssignOp; use PHPUnitPHAR\PhpParser\Node\Expr\AssignOp; class BitwiseAnd extends AssignOp { - public function getType() : string + public function getType(): string { return 'Expr_AssignOp_BitwiseAnd'; } @@ -11763,7 +10866,7 @@ namespace PHPUnitPHAR\PhpParser\Node\Expr\AssignOp; use PHPUnitPHAR\PhpParser\Node\Expr\AssignOp; class BitwiseOr extends AssignOp { - public function getType() : string + public function getType(): string { return 'Expr_AssignOp_BitwiseOr'; } @@ -11776,7 +10879,7 @@ namespace PHPUnitPHAR\PhpParser\Node\Expr\AssignOp; use PHPUnitPHAR\PhpParser\Node\Expr\AssignOp; class BitwiseXor extends AssignOp { - public function getType() : string + public function getType(): string { return 'Expr_AssignOp_BitwiseXor'; } @@ -11789,7 +10892,7 @@ namespace PHPUnitPHAR\PhpParser\Node\Expr\AssignOp; use PHPUnitPHAR\PhpParser\Node\Expr\AssignOp; class Coalesce extends AssignOp { - public function getType() : string + public function getType(): string { return 'Expr_AssignOp_Coalesce'; } @@ -11802,7 +10905,7 @@ namespace PHPUnitPHAR\PhpParser\Node\Expr\AssignOp; use PHPUnitPHAR\PhpParser\Node\Expr\AssignOp; class Concat extends AssignOp { - public function getType() : string + public function getType(): string { return 'Expr_AssignOp_Concat'; } @@ -11815,7 +10918,7 @@ namespace PHPUnitPHAR\PhpParser\Node\Expr\AssignOp; use PHPUnitPHAR\PhpParser\Node\Expr\AssignOp; class Div extends AssignOp { - public function getType() : string + public function getType(): string { return 'Expr_AssignOp_Div'; } @@ -11828,7 +10931,7 @@ namespace PHPUnitPHAR\PhpParser\Node\Expr\AssignOp; use PHPUnitPHAR\PhpParser\Node\Expr\AssignOp; class Minus extends AssignOp { - public function getType() : string + public function getType(): string { return 'Expr_AssignOp_Minus'; } @@ -11841,7 +10944,7 @@ namespace PHPUnitPHAR\PhpParser\Node\Expr\AssignOp; use PHPUnitPHAR\PhpParser\Node\Expr\AssignOp; class Mod extends AssignOp { - public function getType() : string + public function getType(): string { return 'Expr_AssignOp_Mod'; } @@ -11854,7 +10957,7 @@ namespace PHPUnitPHAR\PhpParser\Node\Expr\AssignOp; use PHPUnitPHAR\PhpParser\Node\Expr\AssignOp; class Mul extends AssignOp { - public function getType() : string + public function getType(): string { return 'Expr_AssignOp_Mul'; } @@ -11867,7 +10970,7 @@ namespace PHPUnitPHAR\PhpParser\Node\Expr\AssignOp; use PHPUnitPHAR\PhpParser\Node\Expr\AssignOp; class Plus extends AssignOp { - public function getType() : string + public function getType(): string { return 'Expr_AssignOp_Plus'; } @@ -11880,7 +10983,7 @@ namespace PHPUnitPHAR\PhpParser\Node\Expr\AssignOp; use PHPUnitPHAR\PhpParser\Node\Expr\AssignOp; class Pow extends AssignOp { - public function getType() : string + public function getType(): string { return 'Expr_AssignOp_Pow'; } @@ -11893,7 +10996,7 @@ namespace PHPUnitPHAR\PhpParser\Node\Expr\AssignOp; use PHPUnitPHAR\PhpParser\Node\Expr\AssignOp; class ShiftLeft extends AssignOp { - public function getType() : string + public function getType(): string { return 'Expr_AssignOp_ShiftLeft'; } @@ -11906,7 +11009,7 @@ namespace PHPUnitPHAR\PhpParser\Node\Expr\AssignOp; use PHPUnitPHAR\PhpParser\Node\Expr\AssignOp; class ShiftRight extends AssignOp { - public function getType() : string + public function getType(): string { return 'Expr_AssignOp_ShiftRight'; } @@ -11920,15 +11023,15 @@ use PHPUnitPHAR\PhpParser\Node\Expr; class AssignRef extends Expr { /** @var Expr Variable reference is assigned to */ - public $var; + public Expr $var; /** @var Expr Variable which is referenced */ - public $expr; + public Expr $expr; /** * Constructs an assignment node. * - * @param Expr $var Variable - * @param Expr $expr Expression - * @param array $attributes Additional attributes + * @param Expr $var Variable + * @param Expr $expr Expression + * @param array $attributes Additional attributes */ public function __construct(Expr $var, Expr $expr, array $attributes = []) { @@ -11936,11 +11039,11 @@ class AssignRef extends Expr $this->var = $var; $this->expr = $expr; } - public function getSubNodeNames() : array + public function getSubNodeNames(): array { return ['var', 'expr']; } - public function getType() : string + public function getType(): string { return 'Expr_AssignRef'; } @@ -11954,15 +11057,15 @@ use PHPUnitPHAR\PhpParser\Node\Expr; abstract class BinaryOp extends Expr { /** @var Expr The left hand side expression */ - public $left; + public Expr $left; /** @var Expr The right hand side expression */ - public $right; + public Expr $right; /** * Constructs a binary operator node. * - * @param Expr $left The left hand side expression - * @param Expr $right The right hand side expression - * @param array $attributes Additional attributes + * @param Expr $left The left hand side expression + * @param Expr $right The right hand side expression + * @param array $attributes Additional attributes */ public function __construct(Expr $left, Expr $right, array $attributes = []) { @@ -11970,7 +11073,7 @@ abstract class BinaryOp extends Expr $this->left = $left; $this->right = $right; } - public function getSubNodeNames() : array + public function getSubNodeNames(): array { return ['left', 'right']; } @@ -11979,10 +11082,8 @@ abstract class BinaryOp extends Expr * * In the case there are multiple possible sigils for an operator, this method does not * necessarily return the one used in the parsed code. - * - * @return string */ - public abstract function getOperatorSigil() : string; + abstract public function getOperatorSigil(): string; } '; } - public function getType() : string + public function getType(): string { return 'Expr_BinaryOp_Greater'; } @@ -12162,11 +11263,11 @@ namespace PHPUnitPHAR\PhpParser\Node\Expr\BinaryOp; use PHPUnitPHAR\PhpParser\Node\Expr\BinaryOp; class GreaterOrEqual extends BinaryOp { - public function getOperatorSigil() : string + public function getOperatorSigil(): string { return '>='; } - public function getType() : string + public function getType(): string { return 'Expr_BinaryOp_GreaterOrEqual'; } @@ -12179,11 +11280,11 @@ namespace PHPUnitPHAR\PhpParser\Node\Expr\BinaryOp; use PHPUnitPHAR\PhpParser\Node\Expr\BinaryOp; class Identical extends BinaryOp { - public function getOperatorSigil() : string + public function getOperatorSigil(): string { return '==='; } - public function getType() : string + public function getType(): string { return 'Expr_BinaryOp_Identical'; } @@ -12196,11 +11297,11 @@ namespace PHPUnitPHAR\PhpParser\Node\Expr\BinaryOp; use PHPUnitPHAR\PhpParser\Node\Expr\BinaryOp; class LogicalAnd extends BinaryOp { - public function getOperatorSigil() : string + public function getOperatorSigil(): string { return 'and'; } - public function getType() : string + public function getType(): string { return 'Expr_BinaryOp_LogicalAnd'; } @@ -12213,11 +11314,11 @@ namespace PHPUnitPHAR\PhpParser\Node\Expr\BinaryOp; use PHPUnitPHAR\PhpParser\Node\Expr\BinaryOp; class LogicalOr extends BinaryOp { - public function getOperatorSigil() : string + public function getOperatorSigil(): string { return 'or'; } - public function getType() : string + public function getType(): string { return 'Expr_BinaryOp_LogicalOr'; } @@ -12230,11 +11331,11 @@ namespace PHPUnitPHAR\PhpParser\Node\Expr\BinaryOp; use PHPUnitPHAR\PhpParser\Node\Expr\BinaryOp; class LogicalXor extends BinaryOp { - public function getOperatorSigil() : string + public function getOperatorSigil(): string { return 'xor'; } - public function getType() : string + public function getType(): string { return 'Expr_BinaryOp_LogicalXor'; } @@ -12247,11 +11348,11 @@ namespace PHPUnitPHAR\PhpParser\Node\Expr\BinaryOp; use PHPUnitPHAR\PhpParser\Node\Expr\BinaryOp; class Minus extends BinaryOp { - public function getOperatorSigil() : string + public function getOperatorSigil(): string { return '-'; } - public function getType() : string + public function getType(): string { return 'Expr_BinaryOp_Minus'; } @@ -12264,11 +11365,11 @@ namespace PHPUnitPHAR\PhpParser\Node\Expr\BinaryOp; use PHPUnitPHAR\PhpParser\Node\Expr\BinaryOp; class Mod extends BinaryOp { - public function getOperatorSigil() : string + public function getOperatorSigil(): string { return '%'; } - public function getType() : string + public function getType(): string { return 'Expr_BinaryOp_Mod'; } @@ -12281,11 +11382,11 @@ namespace PHPUnitPHAR\PhpParser\Node\Expr\BinaryOp; use PHPUnitPHAR\PhpParser\Node\Expr\BinaryOp; class Mul extends BinaryOp { - public function getOperatorSigil() : string + public function getOperatorSigil(): string { return '*'; } - public function getType() : string + public function getType(): string { return 'Expr_BinaryOp_Mul'; } @@ -12298,11 +11399,11 @@ namespace PHPUnitPHAR\PhpParser\Node\Expr\BinaryOp; use PHPUnitPHAR\PhpParser\Node\Expr\BinaryOp; class NotEqual extends BinaryOp { - public function getOperatorSigil() : string + public function getOperatorSigil(): string { return '!='; } - public function getType() : string + public function getType(): string { return 'Expr_BinaryOp_NotEqual'; } @@ -12315,11 +11416,11 @@ namespace PHPUnitPHAR\PhpParser\Node\Expr\BinaryOp; use PHPUnitPHAR\PhpParser\Node\Expr\BinaryOp; class NotIdentical extends BinaryOp { - public function getOperatorSigil() : string + public function getOperatorSigil(): string { return '!=='; } - public function getType() : string + public function getType(): string { return 'Expr_BinaryOp_NotIdentical'; } @@ -12332,11 +11433,11 @@ namespace PHPUnitPHAR\PhpParser\Node\Expr\BinaryOp; use PHPUnitPHAR\PhpParser\Node\Expr\BinaryOp; class Plus extends BinaryOp { - public function getOperatorSigil() : string + public function getOperatorSigil(): string { return '+'; } - public function getType() : string + public function getType(): string { return 'Expr_BinaryOp_Plus'; } @@ -12349,11 +11450,11 @@ namespace PHPUnitPHAR\PhpParser\Node\Expr\BinaryOp; use PHPUnitPHAR\PhpParser\Node\Expr\BinaryOp; class Pow extends BinaryOp { - public function getOperatorSigil() : string + public function getOperatorSigil(): string { return '**'; } - public function getType() : string + public function getType(): string { return 'Expr_BinaryOp_Pow'; } @@ -12366,11 +11467,11 @@ namespace PHPUnitPHAR\PhpParser\Node\Expr\BinaryOp; use PHPUnitPHAR\PhpParser\Node\Expr\BinaryOp; class ShiftLeft extends BinaryOp { - public function getOperatorSigil() : string + public function getOperatorSigil(): string { return '<<'; } - public function getType() : string + public function getType(): string { return 'Expr_BinaryOp_ShiftLeft'; } @@ -12383,11 +11484,11 @@ namespace PHPUnitPHAR\PhpParser\Node\Expr\BinaryOp; use PHPUnitPHAR\PhpParser\Node\Expr\BinaryOp; class ShiftRight extends BinaryOp { - public function getOperatorSigil() : string + public function getOperatorSigil(): string { return '>>'; } - public function getType() : string + public function getType(): string { return 'Expr_BinaryOp_ShiftRight'; } @@ -12400,11 +11501,11 @@ namespace PHPUnitPHAR\PhpParser\Node\Expr\BinaryOp; use PHPUnitPHAR\PhpParser\Node\Expr\BinaryOp; class Smaller extends BinaryOp { - public function getOperatorSigil() : string + public function getOperatorSigil(): string { return '<'; } - public function getType() : string + public function getType(): string { return 'Expr_BinaryOp_Smaller'; } @@ -12417,11 +11518,11 @@ namespace PHPUnitPHAR\PhpParser\Node\Expr\BinaryOp; use PHPUnitPHAR\PhpParser\Node\Expr\BinaryOp; class SmallerOrEqual extends BinaryOp { - public function getOperatorSigil() : string + public function getOperatorSigil(): string { return '<='; } - public function getType() : string + public function getType(): string { return 'Expr_BinaryOp_SmallerOrEqual'; } @@ -12434,11 +11535,11 @@ namespace PHPUnitPHAR\PhpParser\Node\Expr\BinaryOp; use PHPUnitPHAR\PhpParser\Node\Expr\BinaryOp; class Spaceship extends BinaryOp { - public function getOperatorSigil() : string + public function getOperatorSigil(): string { return '<=>'; } - public function getType() : string + public function getType(): string { return 'Expr_BinaryOp_Spaceship'; } @@ -12452,23 +11553,23 @@ use PHPUnitPHAR\PhpParser\Node\Expr; class BitwiseNot extends Expr { /** @var Expr Expression */ - public $expr; + public Expr $expr; /** * Constructs a bitwise not node. * - * @param Expr $expr Expression - * @param array $attributes Additional attributes + * @param Expr $expr Expression + * @param array $attributes Additional attributes */ public function __construct(Expr $expr, array $attributes = []) { $this->attributes = $attributes; $this->expr = $expr; } - public function getSubNodeNames() : array + public function getSubNodeNames(): array { return ['expr']; } - public function getType() : string + public function getType(): string { return 'Expr_BitwiseNot'; } @@ -12482,23 +11583,23 @@ use PHPUnitPHAR\PhpParser\Node\Expr; class BooleanNot extends Expr { /** @var Expr Expression */ - public $expr; + public Expr $expr; /** * Constructs a boolean not node. * - * @param Expr $expr Expression - * @param array $attributes Additional attributes + * @param Expr $expr Expression + * @param array $attributes Additional attributes */ public function __construct(Expr $expr, array $attributes = []) { $this->attributes = $attributes; $this->expr = $expr; } - public function getSubNodeNames() : array + public function getSubNodeNames(): array { return ['expr']; } - public function getType() : string + public function getType(): string { return 'Expr_BooleanNot'; } @@ -12519,27 +11620,23 @@ abstract class CallLike extends Expr * * @return array */ - public abstract function getRawArgs() : array; + abstract public function getRawArgs(): array; /** * Returns whether this call expression is actually a first class callable. */ - public function isFirstClassCallable() : bool + public function isFirstClassCallable(): bool { - foreach ($this->getRawArgs() as $arg) { - if ($arg instanceof VariadicPlaceholder) { - return \true; - } - } - return \false; + $rawArgs = $this->getRawArgs(); + return count($rawArgs) === 1 && current($rawArgs) instanceof VariadicPlaceholder; } /** * Assert that this is not a first-class callable and return only ordinary Args. * * @return Arg[] */ - public function getArgs() : array + public function getArgs(): array { - \assert(!$this->isFirstClassCallable()); + assert(!$this->isFirstClassCallable()); return $this->getRawArgs(); } } @@ -12552,19 +11649,19 @@ use PHPUnitPHAR\PhpParser\Node\Expr; abstract class Cast extends Expr { /** @var Expr Expression */ - public $expr; + public Expr $expr; /** * Constructs a cast node. * - * @param Expr $expr Expression - * @param array $attributes Additional attributes + * @param Expr $expr Expression + * @param array $attributes Additional attributes */ public function __construct(Expr $expr, array $attributes = []) { $this->attributes = $attributes; $this->expr = $expr; } - public function getSubNodeNames() : array + public function getSubNodeNames(): array { return ['expr']; } @@ -12577,7 +11674,7 @@ namespace PHPUnitPHAR\PhpParser\Node\Expr\Cast; use PHPUnitPHAR\PhpParser\Node\Expr\Cast; class Array_ extends Cast { - public function getType() : string + public function getType(): string { return 'Expr_Cast_Array'; } @@ -12590,7 +11687,7 @@ namespace PHPUnitPHAR\PhpParser\Node\Expr\Cast; use PHPUnitPHAR\PhpParser\Node\Expr\Cast; class Bool_ extends Cast { - public function getType() : string + public function getType(): string { return 'Expr_Cast_Bool'; } @@ -12604,13 +11701,13 @@ use PHPUnitPHAR\PhpParser\Node\Expr\Cast; class Double extends Cast { // For use in "kind" attribute - const KIND_DOUBLE = 1; + public const KIND_DOUBLE = 1; // "double" syntax - const KIND_FLOAT = 2; + public const KIND_FLOAT = 2; // "float" syntax - const KIND_REAL = 3; + public const KIND_REAL = 3; // "real" syntax - public function getType() : string + public function getType(): string { return 'Expr_Cast_Double'; } @@ -12623,7 +11720,7 @@ namespace PHPUnitPHAR\PhpParser\Node\Expr\Cast; use PHPUnitPHAR\PhpParser\Node\Expr\Cast; class Int_ extends Cast { - public function getType() : string + public function getType(): string { return 'Expr_Cast_Int'; } @@ -12636,7 +11733,7 @@ namespace PHPUnitPHAR\PhpParser\Node\Expr\Cast; use PHPUnitPHAR\PhpParser\Node\Expr\Cast; class Object_ extends Cast { - public function getType() : string + public function getType(): string { return 'Expr_Cast_Object'; } @@ -12649,7 +11746,7 @@ namespace PHPUnitPHAR\PhpParser\Node\Expr\Cast; use PHPUnitPHAR\PhpParser\Node\Expr\Cast; class String_ extends Cast { - public function getType() : string + public function getType(): string { return 'Expr_Cast_String'; } @@ -12662,7 +11759,7 @@ namespace PHPUnitPHAR\PhpParser\Node\Expr\Cast; use PHPUnitPHAR\PhpParser\Node\Expr\Cast; class Unset_ extends Cast { - public function getType() : string + public function getType(): string { return 'Expr_Cast_Unset'; } @@ -12672,33 +11769,34 @@ class Unset_ extends Cast declare (strict_types=1); namespace PHPUnitPHAR\PhpParser\Node\Expr; +use PHPUnitPHAR\PhpParser\Node; use PHPUnitPHAR\PhpParser\Node\Expr; use PHPUnitPHAR\PhpParser\Node\Identifier; use PHPUnitPHAR\PhpParser\Node\Name; class ClassConstFetch extends Expr { /** @var Name|Expr Class name */ - public $class; + public Node $class; /** @var Identifier|Expr|Error Constant name */ - public $name; + public Node $name; /** * Constructs a class const fetch node. * - * @param Name|Expr $class Class name - * @param string|Identifier|Expr|Error $name Constant name - * @param array $attributes Additional attributes + * @param Name|Expr $class Class name + * @param string|Identifier|Expr|Error $name Constant name + * @param array $attributes Additional attributes */ - public function __construct($class, $name, array $attributes = []) + public function __construct(Node $class, $name, array $attributes = []) { $this->attributes = $attributes; $this->class = $class; $this->name = \is_string($name) ? new Identifier($name) : $name; } - public function getSubNodeNames() : array + public function getSubNodeNames(): array { return ['class', 'name']; } - public function getType() : string + public function getType(): string { return 'Expr_ClassConstFetch'; } @@ -12712,23 +11810,23 @@ use PHPUnitPHAR\PhpParser\Node\Expr; class Clone_ extends Expr { /** @var Expr Expression */ - public $expr; + public Expr $expr; /** * Constructs a clone node. * - * @param Expr $expr Expression - * @param array $attributes Additional attributes + * @param Expr $expr Expression + * @param array $attributes Additional attributes */ public function __construct(Expr $expr, array $attributes = []) { $this->attributes = $attributes; $this->expr = $expr; } - public function getSubNodeNames() : array + public function getSubNodeNames(): array { return ['expr']; } - public function getType() : string + public function getType(): string { return 'Expr_Clone'; } @@ -12739,36 +11837,45 @@ declare (strict_types=1); namespace PHPUnitPHAR\PhpParser\Node\Expr; use PHPUnitPHAR\PhpParser\Node; +use PHPUnitPHAR\PhpParser\Node\ClosureUse; use PHPUnitPHAR\PhpParser\Node\Expr; use PHPUnitPHAR\PhpParser\Node\FunctionLike; class Closure extends Expr implements FunctionLike { /** @var bool Whether the closure is static */ - public $static; + public bool $static; /** @var bool Whether to return by reference */ - public $byRef; + public bool $byRef; /** @var Node\Param[] Parameters */ - public $params; + public array $params; /** @var ClosureUse[] use()s */ - public $uses; + public array $uses; /** @var null|Node\Identifier|Node\Name|Node\ComplexType Return type */ - public $returnType; + public ?Node $returnType; /** @var Node\Stmt[] Statements */ - public $stmts; + public array $stmts; /** @var Node\AttributeGroup[] PHP attribute groups */ - public $attrGroups; + public array $attrGroups; /** * Constructs a lambda function node. * - * @param array $subNodes Array of the following optional subnodes: - * 'static' => false : Whether the closure is static - * 'byRef' => false : Whether to return by reference - * 'params' => array(): Parameters - * 'uses' => array(): use()s - * 'returnType' => null : Return type - * 'stmts' => array(): Statements - * 'attrGroups' => array(): PHP attributes groups - * @param array $attributes Additional attributes + * @param array{ + * static?: bool, + * byRef?: bool, + * params?: Node\Param[], + * uses?: ClosureUse[], + * returnType?: null|Node\Identifier|Node\Name|Node\ComplexType, + * stmts?: Node\Stmt[], + * attrGroups?: Node\AttributeGroup[], + * } $subNodes Array of the following optional subnodes: + * 'static' => false : Whether the closure is static + * 'byRef' => false : Whether to return by reference + * 'params' => array(): Parameters + * 'uses' => array(): use()s + * 'returnType' => null : Return type + * 'stmts' => array(): Statements + * 'attrGroups' => array(): PHP attributes groups + * @param array $attributes Additional attributes */ public function __construct(array $subNodes = [], array $attributes = []) { @@ -12777,20 +11884,19 @@ class Closure extends Expr implements FunctionLike $this->byRef = $subNodes['byRef'] ?? \false; $this->params = $subNodes['params'] ?? []; $this->uses = $subNodes['uses'] ?? []; - $returnType = $subNodes['returnType'] ?? null; - $this->returnType = \is_string($returnType) ? new Node\Identifier($returnType) : $returnType; + $this->returnType = $subNodes['returnType'] ?? null; $this->stmts = $subNodes['stmts'] ?? []; $this->attrGroups = $subNodes['attrGroups'] ?? []; } - public function getSubNodeNames() : array + public function getSubNodeNames(): array { return ['attrGroups', 'static', 'byRef', 'params', 'uses', 'returnType', 'stmts']; } - public function returnsByRef() : bool + public function returnsByRef(): bool { return $this->byRef; } - public function getParams() : array + public function getParams(): array { return $this->params; } @@ -12799,15 +11905,15 @@ class Closure extends Expr implements FunctionLike return $this->returnType; } /** @return Node\Stmt[] */ - public function getStmts() : array + public function getStmts(): array { return $this->stmts; } - public function getAttrGroups() : array + public function getAttrGroups(): array { return $this->attrGroups; } - public function getType() : string + public function getType(): string { return 'Expr_Closure'; } @@ -12815,37 +11921,9 @@ class Closure extends Expr implements FunctionLike attributes = $attributes; - $this->var = $var; - $this->byRef = $byRef; - } - public function getSubNodeNames() : array - { - return ['var', 'byRef']; - } - public function getType() : string - { - return 'Expr_ClosureUse'; - } -} +require __DIR__ . '/../ClosureUse.php'; $attributes Additional attributes */ public function __construct(Name $name, array $attributes = []) { $this->attributes = $attributes; $this->name = $name; } - public function getSubNodeNames() : array + public function getSubNodeNames(): array { return ['name']; } - public function getType() : string + public function getType(): string { return 'Expr_ConstFetch'; } @@ -12886,23 +11964,23 @@ use PHPUnitPHAR\PhpParser\Node\Expr; class Empty_ extends Expr { /** @var Expr Expression */ - public $expr; + public Expr $expr; /** * Constructs an empty() node. * - * @param Expr $expr Expression - * @param array $attributes Additional attributes + * @param Expr $expr Expression + * @param array $attributes Additional attributes */ public function __construct(Expr $expr, array $attributes = []) { $this->attributes = $attributes; $this->expr = $expr; } - public function getSubNodeNames() : array + public function getSubNodeNames(): array { return ['expr']; } - public function getType() : string + public function getType(): string { return 'Expr_Empty'; } @@ -12924,17 +12002,17 @@ class Error extends Expr /** * Constructs an error node. * - * @param array $attributes Additional attributes + * @param array $attributes Additional attributes */ public function __construct(array $attributes = []) { $this->attributes = $attributes; } - public function getSubNodeNames() : array + public function getSubNodeNames(): array { return []; } - public function getType() : string + public function getType(): string { return 'Expr_Error'; } @@ -12948,23 +12026,23 @@ use PHPUnitPHAR\PhpParser\Node\Expr; class ErrorSuppress extends Expr { /** @var Expr Expression */ - public $expr; + public Expr $expr; /** * Constructs an error suppress node. * - * @param Expr $expr Expression - * @param array $attributes Additional attributes + * @param Expr $expr Expression + * @param array $attributes Additional attributes */ public function __construct(Expr $expr, array $attributes = []) { $this->attributes = $attributes; $this->expr = $expr; } - public function getSubNodeNames() : array + public function getSubNodeNames(): array { return ['expr']; } - public function getType() : string + public function getType(): string { return 'Expr_ErrorSuppress'; } @@ -12978,23 +12056,23 @@ use PHPUnitPHAR\PhpParser\Node\Expr; class Eval_ extends Expr { /** @var Expr Expression */ - public $expr; + public Expr $expr; /** * Constructs an eval() node. * - * @param Expr $expr Expression - * @param array $attributes Additional attributes + * @param Expr $expr Expression + * @param array $attributes Additional attributes */ public function __construct(Expr $expr, array $attributes = []) { $this->attributes = $attributes; $this->expr = $expr; } - public function getSubNodeNames() : array + public function getSubNodeNames(): array { return ['expr']; } - public function getType() : string + public function getType(): string { return 'Expr_Eval'; } @@ -13008,26 +12086,26 @@ use PHPUnitPHAR\PhpParser\Node\Expr; class Exit_ extends Expr { /* For use in "kind" attribute */ - const KIND_EXIT = 1; - const KIND_DIE = 2; + public const KIND_EXIT = 1; + public const KIND_DIE = 2; /** @var null|Expr Expression */ - public $expr; + public ?Expr $expr; /** * Constructs an exit() node. * - * @param null|Expr $expr Expression - * @param array $attributes Additional attributes + * @param null|Expr $expr Expression + * @param array $attributes Additional attributes */ public function __construct(?Expr $expr = null, array $attributes = []) { $this->attributes = $attributes; $this->expr = $expr; } - public function getSubNodeNames() : array + public function getSubNodeNames(): array { return ['expr']; } - public function getType() : string + public function getType(): string { return 'Expr_Exit'; } @@ -13042,31 +12120,31 @@ use PHPUnitPHAR\PhpParser\Node\Expr; class FuncCall extends CallLike { /** @var Node\Name|Expr Function name */ - public $name; + public Node $name; /** @var array Arguments */ - public $args; + public array $args; /** * Constructs a function call node. * - * @param Node\Name|Expr $name Function name - * @param array $args Arguments - * @param array $attributes Additional attributes + * @param Node\Name|Expr $name Function name + * @param array $args Arguments + * @param array $attributes Additional attributes */ - public function __construct($name, array $args = [], array $attributes = []) + public function __construct(Node $name, array $args = [], array $attributes = []) { $this->attributes = $attributes; $this->name = $name; $this->args = $args; } - public function getSubNodeNames() : array + public function getSubNodeNames(): array { return ['name', 'args']; } - public function getType() : string + public function getType(): string { return 'Expr_FuncCall'; } - public function getRawArgs() : array + public function getRawArgs(): array { return $this->args; } @@ -13079,20 +12157,20 @@ namespace PHPUnitPHAR\PhpParser\Node\Expr; use PHPUnitPHAR\PhpParser\Node\Expr; class Include_ extends Expr { - const TYPE_INCLUDE = 1; - const TYPE_INCLUDE_ONCE = 2; - const TYPE_REQUIRE = 3; - const TYPE_REQUIRE_ONCE = 4; + public const TYPE_INCLUDE = 1; + public const TYPE_INCLUDE_ONCE = 2; + public const TYPE_REQUIRE = 3; + public const TYPE_REQUIRE_ONCE = 4; /** @var Expr Expression */ - public $expr; + public Expr $expr; /** @var int Type of include */ - public $type; + public int $type; /** * Constructs an include node. * - * @param Expr $expr Expression - * @param int $type Type of include - * @param array $attributes Additional attributes + * @param Expr $expr Expression + * @param int $type Type of include + * @param array $attributes Additional attributes */ public function __construct(Expr $expr, int $type, array $attributes = []) { @@ -13100,11 +12178,11 @@ class Include_ extends Expr $this->expr = $expr; $this->type = $type; } - public function getSubNodeNames() : array + public function getSubNodeNames(): array { return ['expr', 'type']; } - public function getType() : string + public function getType(): string { return 'Expr_Include'; } @@ -13114,32 +12192,33 @@ class Include_ extends Expr declare (strict_types=1); namespace PHPUnitPHAR\PhpParser\Node\Expr; +use PHPUnitPHAR\PhpParser\Node; use PHPUnitPHAR\PhpParser\Node\Expr; use PHPUnitPHAR\PhpParser\Node\Name; class Instanceof_ extends Expr { /** @var Expr Expression */ - public $expr; + public Expr $expr; /** @var Name|Expr Class name */ - public $class; + public Node $class; /** * Constructs an instanceof check node. * - * @param Expr $expr Expression - * @param Name|Expr $class Class name - * @param array $attributes Additional attributes + * @param Expr $expr Expression + * @param Name|Expr $class Class name + * @param array $attributes Additional attributes */ - public function __construct(Expr $expr, $class, array $attributes = []) + public function __construct(Expr $expr, Node $class, array $attributes = []) { $this->attributes = $attributes; $this->expr = $expr; $this->class = $class; } - public function getSubNodeNames() : array + public function getSubNodeNames(): array { return ['expr', 'class']; } - public function getType() : string + public function getType(): string { return 'Expr_Instanceof'; } @@ -13153,23 +12232,23 @@ use PHPUnitPHAR\PhpParser\Node\Expr; class Isset_ extends Expr { /** @var Expr[] Variables */ - public $vars; + public array $vars; /** * Constructs an array node. * - * @param Expr[] $vars Variables - * @param array $attributes Additional attributes + * @param Expr[] $vars Variables + * @param array $attributes Additional attributes */ public function __construct(array $vars, array $attributes = []) { $this->attributes = $attributes; $this->vars = $vars; } - public function getSubNodeNames() : array + public function getSubNodeNames(): array { return ['vars']; } - public function getType() : string + public function getType(): string { return 'Expr_Isset'; } @@ -13179,27 +12258,33 @@ class Isset_ extends Expr declare (strict_types=1); namespace PHPUnitPHAR\PhpParser\Node\Expr; +use PHPUnitPHAR\PhpParser\Node\ArrayItem; use PHPUnitPHAR\PhpParser\Node\Expr; class List_ extends Expr { + // For use in "kind" attribute + public const KIND_LIST = 1; + // list() syntax + public const KIND_ARRAY = 2; + // [] syntax /** @var (ArrayItem|null)[] List of items to assign to */ - public $items; + public array $items; /** * Constructs a list() destructuring node. * - * @param (ArrayItem|null)[] $items List of items to assign to - * @param array $attributes Additional attributes + * @param (ArrayItem|null)[] $items List of items to assign to + * @param array $attributes Additional attributes */ public function __construct(array $items, array $attributes = []) { $this->attributes = $attributes; $this->items = $items; } - public function getSubNodeNames() : array + public function getSubNodeNames(): array { return ['items']; } - public function getType() : string + public function getType(): string { return 'Expr_List'; } @@ -13213,12 +12298,14 @@ use PHPUnitPHAR\PhpParser\Node; use PHPUnitPHAR\PhpParser\Node\MatchArm; class Match_ extends Node\Expr { - /** @var Node\Expr */ - public $cond; + /** @var Node\Expr Condition */ + public Node\Expr $cond; /** @var MatchArm[] */ - public $arms; + public array $arms; /** + * @param Node\Expr $cond Condition * @param MatchArm[] $arms + * @param array $attributes Additional attributes */ public function __construct(Node\Expr $cond, array $arms = [], array $attributes = []) { @@ -13226,11 +12313,11 @@ class Match_ extends Node\Expr $this->cond = $cond; $this->arms = $arms; } - public function getSubNodeNames() : array + public function getSubNodeNames(): array { return ['cond', 'arms']; } - public function getType() : string + public function getType(): string { return 'Expr_Match'; } @@ -13240,6 +12327,7 @@ class Match_ extends Node\Expr declare (strict_types=1); namespace PHPUnitPHAR\PhpParser\Node\Expr; +use PHPUnitPHAR\PhpParser\Node; use PHPUnitPHAR\PhpParser\Node\Arg; use PHPUnitPHAR\PhpParser\Node\Expr; use PHPUnitPHAR\PhpParser\Node\Identifier; @@ -13247,18 +12335,18 @@ use PHPUnitPHAR\PhpParser\Node\VariadicPlaceholder; class MethodCall extends CallLike { /** @var Expr Variable holding object */ - public $var; + public Expr $var; /** @var Identifier|Expr Method name */ - public $name; + public Node $name; /** @var array Arguments */ - public $args; + public array $args; /** * Constructs a function call node. * - * @param Expr $var Variable holding object - * @param string|Identifier|Expr $name Method name - * @param array $args Arguments - * @param array $attributes Additional attributes + * @param Expr $var Variable holding object + * @param string|Identifier|Expr $name Method name + * @param array $args Arguments + * @param array $attributes Additional attributes */ public function __construct(Expr $var, $name, array $args = [], array $attributes = []) { @@ -13267,15 +12355,15 @@ class MethodCall extends CallLike $this->name = \is_string($name) ? new Identifier($name) : $name; $this->args = $args; } - public function getSubNodeNames() : array + public function getSubNodeNames(): array { return ['var', 'name', 'args']; } - public function getType() : string + public function getType(): string { return 'Expr_MethodCall'; } - public function getRawArgs() : array + public function getRawArgs(): array { return $this->args; } @@ -13292,31 +12380,31 @@ use PHPUnitPHAR\PhpParser\Node\VariadicPlaceholder; class New_ extends CallLike { /** @var Node\Name|Expr|Node\Stmt\Class_ Class name */ - public $class; + public Node $class; /** @var array Arguments */ - public $args; + public array $args; /** * Constructs a function call node. * - * @param Node\Name|Expr|Node\Stmt\Class_ $class Class name (or class node for anonymous classes) - * @param array $args Arguments - * @param array $attributes Additional attributes + * @param Node\Name|Expr|Node\Stmt\Class_ $class Class name (or class node for anonymous classes) + * @param array $args Arguments + * @param array $attributes Additional attributes */ - public function __construct($class, array $args = [], array $attributes = []) + public function __construct(Node $class, array $args = [], array $attributes = []) { $this->attributes = $attributes; $this->class = $class; $this->args = $args; } - public function getSubNodeNames() : array + public function getSubNodeNames(): array { return ['class', 'args']; } - public function getType() : string + public function getType(): string { return 'Expr_New'; } - public function getRawArgs() : array + public function getRawArgs(): array { return $this->args; } @@ -13326,6 +12414,7 @@ class New_ extends CallLike declare (strict_types=1); namespace PHPUnitPHAR\PhpParser\Node\Expr; +use PHPUnitPHAR\PhpParser\Node; use PHPUnitPHAR\PhpParser\Node\Arg; use PHPUnitPHAR\PhpParser\Node\Expr; use PHPUnitPHAR\PhpParser\Node\Identifier; @@ -13333,18 +12422,18 @@ use PHPUnitPHAR\PhpParser\Node\VariadicPlaceholder; class NullsafeMethodCall extends CallLike { /** @var Expr Variable holding object */ - public $var; + public Expr $var; /** @var Identifier|Expr Method name */ - public $name; + public Node $name; /** @var array Arguments */ - public $args; + public array $args; /** * Constructs a nullsafe method call node. * - * @param Expr $var Variable holding object - * @param string|Identifier|Expr $name Method name - * @param array $args Arguments - * @param array $attributes Additional attributes + * @param Expr $var Variable holding object + * @param string|Identifier|Expr $name Method name + * @param array $args Arguments + * @param array $attributes Additional attributes */ public function __construct(Expr $var, $name, array $args = [], array $attributes = []) { @@ -13353,15 +12442,15 @@ class NullsafeMethodCall extends CallLike $this->name = \is_string($name) ? new Identifier($name) : $name; $this->args = $args; } - public function getSubNodeNames() : array + public function getSubNodeNames(): array { return ['var', 'name', 'args']; } - public function getType() : string + public function getType(): string { return 'Expr_NullsafeMethodCall'; } - public function getRawArgs() : array + public function getRawArgs(): array { return $this->args; } @@ -13371,20 +12460,21 @@ class NullsafeMethodCall extends CallLike declare (strict_types=1); namespace PHPUnitPHAR\PhpParser\Node\Expr; +use PHPUnitPHAR\PhpParser\Node; use PHPUnitPHAR\PhpParser\Node\Expr; use PHPUnitPHAR\PhpParser\Node\Identifier; class NullsafePropertyFetch extends Expr { /** @var Expr Variable holding object */ - public $var; + public Expr $var; /** @var Identifier|Expr Property name */ - public $name; + public Node $name; /** * Constructs a nullsafe property fetch node. * - * @param Expr $var Variable holding object - * @param string|Identifier|Expr $name Property name - * @param array $attributes Additional attributes + * @param Expr $var Variable holding object + * @param string|Identifier|Expr $name Property name + * @param array $attributes Additional attributes */ public function __construct(Expr $var, $name, array $attributes = []) { @@ -13392,11 +12482,11 @@ class NullsafePropertyFetch extends Expr $this->var = $var; $this->name = \is_string($name) ? new Identifier($name) : $name; } - public function getSubNodeNames() : array + public function getSubNodeNames(): array { return ['var', 'name']; } - public function getType() : string + public function getType(): string { return 'Expr_NullsafePropertyFetch'; } @@ -13410,23 +12500,23 @@ use PHPUnitPHAR\PhpParser\Node\Expr; class PostDec extends Expr { /** @var Expr Variable */ - public $var; + public Expr $var; /** * Constructs a post decrement node. * - * @param Expr $var Variable - * @param array $attributes Additional attributes + * @param Expr $var Variable + * @param array $attributes Additional attributes */ public function __construct(Expr $var, array $attributes = []) { $this->attributes = $attributes; $this->var = $var; } - public function getSubNodeNames() : array + public function getSubNodeNames(): array { return ['var']; } - public function getType() : string + public function getType(): string { return 'Expr_PostDec'; } @@ -13440,23 +12530,23 @@ use PHPUnitPHAR\PhpParser\Node\Expr; class PostInc extends Expr { /** @var Expr Variable */ - public $var; + public Expr $var; /** * Constructs a post increment node. * - * @param Expr $var Variable - * @param array $attributes Additional attributes + * @param Expr $var Variable + * @param array $attributes Additional attributes */ public function __construct(Expr $var, array $attributes = []) { $this->attributes = $attributes; $this->var = $var; } - public function getSubNodeNames() : array + public function getSubNodeNames(): array { return ['var']; } - public function getType() : string + public function getType(): string { return 'Expr_PostInc'; } @@ -13470,23 +12560,23 @@ use PHPUnitPHAR\PhpParser\Node\Expr; class PreDec extends Expr { /** @var Expr Variable */ - public $var; + public Expr $var; /** * Constructs a pre decrement node. * - * @param Expr $var Variable - * @param array $attributes Additional attributes + * @param Expr $var Variable + * @param array $attributes Additional attributes */ public function __construct(Expr $var, array $attributes = []) { $this->attributes = $attributes; $this->var = $var; } - public function getSubNodeNames() : array + public function getSubNodeNames(): array { return ['var']; } - public function getType() : string + public function getType(): string { return 'Expr_PreDec'; } @@ -13500,23 +12590,23 @@ use PHPUnitPHAR\PhpParser\Node\Expr; class PreInc extends Expr { /** @var Expr Variable */ - public $var; + public Expr $var; /** * Constructs a pre increment node. * - * @param Expr $var Variable - * @param array $attributes Additional attributes + * @param Expr $var Variable + * @param array $attributes Additional attributes */ public function __construct(Expr $var, array $attributes = []) { $this->attributes = $attributes; $this->var = $var; } - public function getSubNodeNames() : array + public function getSubNodeNames(): array { return ['var']; } - public function getType() : string + public function getType(): string { return 'Expr_PreInc'; } @@ -13530,23 +12620,23 @@ use PHPUnitPHAR\PhpParser\Node\Expr; class Print_ extends Expr { /** @var Expr Expression */ - public $expr; + public Expr $expr; /** * Constructs an print() node. * - * @param Expr $expr Expression - * @param array $attributes Additional attributes + * @param Expr $expr Expression + * @param array $attributes Additional attributes */ public function __construct(Expr $expr, array $attributes = []) { $this->attributes = $attributes; $this->expr = $expr; } - public function getSubNodeNames() : array + public function getSubNodeNames(): array { return ['expr']; } - public function getType() : string + public function getType(): string { return 'Expr_Print'; } @@ -13556,20 +12646,21 @@ class Print_ extends Expr declare (strict_types=1); namespace PHPUnitPHAR\PhpParser\Node\Expr; +use PHPUnitPHAR\PhpParser\Node; use PHPUnitPHAR\PhpParser\Node\Expr; use PHPUnitPHAR\PhpParser\Node\Identifier; class PropertyFetch extends Expr { /** @var Expr Variable holding object */ - public $var; + public Expr $var; /** @var Identifier|Expr Property name */ - public $name; + public Node $name; /** * Constructs a function call node. * - * @param Expr $var Variable holding object - * @param string|Identifier|Expr $name Property name - * @param array $attributes Additional attributes + * @param Expr $var Variable holding object + * @param string|Identifier|Expr $name Property name + * @param array $attributes Additional attributes */ public function __construct(Expr $var, $name, array $attributes = []) { @@ -13577,11 +12668,11 @@ class PropertyFetch extends Expr $this->var = $var; $this->name = \is_string($name) ? new Identifier($name) : $name; } - public function getSubNodeNames() : array + public function getSubNodeNames(): array { return ['var', 'name']; } - public function getType() : string + public function getType(): string { return 'Expr_PropertyFetch'; } @@ -13592,26 +12683,27 @@ declare (strict_types=1); namespace PHPUnitPHAR\PhpParser\Node\Expr; use PHPUnitPHAR\PhpParser\Node\Expr; +use PHPUnitPHAR\PhpParser\Node\InterpolatedStringPart; class ShellExec extends Expr { - /** @var array Encapsed string array */ - public $parts; + /** @var (Expr|InterpolatedStringPart)[] Interpolated string array */ + public array $parts; /** * Constructs a shell exec (backtick) node. * - * @param array $parts Encapsed string array - * @param array $attributes Additional attributes + * @param (Expr|InterpolatedStringPart)[] $parts Interpolated string array + * @param array $attributes Additional attributes */ public function __construct(array $parts, array $attributes = []) { $this->attributes = $attributes; $this->parts = $parts; } - public function getSubNodeNames() : array + public function getSubNodeNames(): array { return ['parts']; } - public function getType() : string + public function getType(): string { return 'Expr_ShellExec'; } @@ -13629,35 +12721,35 @@ use PHPUnitPHAR\PhpParser\Node\VariadicPlaceholder; class StaticCall extends CallLike { /** @var Node\Name|Expr Class name */ - public $class; + public Node $class; /** @var Identifier|Expr Method name */ - public $name; + public Node $name; /** @var array Arguments */ - public $args; + public array $args; /** * Constructs a static method call node. * - * @param Node\Name|Expr $class Class name - * @param string|Identifier|Expr $name Method name - * @param array $args Arguments - * @param array $attributes Additional attributes + * @param Node\Name|Expr $class Class name + * @param string|Identifier|Expr $name Method name + * @param array $args Arguments + * @param array $attributes Additional attributes */ - public function __construct($class, $name, array $args = [], array $attributes = []) + public function __construct(Node $class, $name, array $args = [], array $attributes = []) { $this->attributes = $attributes; $this->class = $class; $this->name = \is_string($name) ? new Identifier($name) : $name; $this->args = $args; } - public function getSubNodeNames() : array + public function getSubNodeNames(): array { return ['class', 'name', 'args']; } - public function getType() : string + public function getType(): string { return 'Expr_StaticCall'; } - public function getRawArgs() : array + public function getRawArgs(): array { return $this->args; } @@ -13667,33 +12759,34 @@ class StaticCall extends CallLike declare (strict_types=1); namespace PHPUnitPHAR\PhpParser\Node\Expr; +use PHPUnitPHAR\PhpParser\Node; use PHPUnitPHAR\PhpParser\Node\Expr; use PHPUnitPHAR\PhpParser\Node\Name; use PHPUnitPHAR\PhpParser\Node\VarLikeIdentifier; class StaticPropertyFetch extends Expr { /** @var Name|Expr Class name */ - public $class; + public Node $class; /** @var VarLikeIdentifier|Expr Property name */ - public $name; + public Node $name; /** * Constructs a static property fetch node. * - * @param Name|Expr $class Class name - * @param string|VarLikeIdentifier|Expr $name Property name - * @param array $attributes Additional attributes + * @param Name|Expr $class Class name + * @param string|VarLikeIdentifier|Expr $name Property name + * @param array $attributes Additional attributes */ - public function __construct($class, $name, array $attributes = []) + public function __construct(Node $class, $name, array $attributes = []) { $this->attributes = $attributes; $this->class = $class; $this->name = \is_string($name) ? new VarLikeIdentifier($name) : $name; } - public function getSubNodeNames() : array + public function getSubNodeNames(): array { return ['class', 'name']; } - public function getType() : string + public function getType(): string { return 'Expr_StaticPropertyFetch'; } @@ -13707,31 +12800,31 @@ use PHPUnitPHAR\PhpParser\Node\Expr; class Ternary extends Expr { /** @var Expr Condition */ - public $cond; + public Expr $cond; /** @var null|Expr Expression for true */ - public $if; + public ?Expr $if; /** @var Expr Expression for false */ - public $else; + public Expr $else; /** * Constructs a ternary operator node. * - * @param Expr $cond Condition - * @param null|Expr $if Expression for true - * @param Expr $else Expression for false - * @param array $attributes Additional attributes + * @param Expr $cond Condition + * @param null|Expr $if Expression for true + * @param Expr $else Expression for false + * @param array $attributes Additional attributes */ - public function __construct(Expr $cond, $if, Expr $else, array $attributes = []) + public function __construct(Expr $cond, ?Expr $if, Expr $else, array $attributes = []) { $this->attributes = $attributes; $this->cond = $cond; $this->if = $if; $this->else = $else; } - public function getSubNodeNames() : array + public function getSubNodeNames(): array { return ['cond', 'if', 'else']; } - public function getType() : string + public function getType(): string { return 'Expr_Ternary'; } @@ -13745,23 +12838,23 @@ use PHPUnitPHAR\PhpParser\Node; class Throw_ extends Node\Expr { /** @var Node\Expr Expression */ - public $expr; + public Node\Expr $expr; /** * Constructs a throw expression node. * - * @param Node\Expr $expr Expression - * @param array $attributes Additional attributes + * @param Node\Expr $expr Expression + * @param array $attributes Additional attributes */ public function __construct(Node\Expr $expr, array $attributes = []) { $this->attributes = $attributes; $this->expr = $expr; } - public function getSubNodeNames() : array + public function getSubNodeNames(): array { return ['expr']; } - public function getType() : string + public function getType(): string { return 'Expr_Throw'; } @@ -13775,23 +12868,23 @@ use PHPUnitPHAR\PhpParser\Node\Expr; class UnaryMinus extends Expr { /** @var Expr Expression */ - public $expr; + public Expr $expr; /** * Constructs a unary minus node. * - * @param Expr $expr Expression - * @param array $attributes Additional attributes + * @param Expr $expr Expression + * @param array $attributes Additional attributes */ public function __construct(Expr $expr, array $attributes = []) { $this->attributes = $attributes; $this->expr = $expr; } - public function getSubNodeNames() : array + public function getSubNodeNames(): array { return ['expr']; } - public function getType() : string + public function getType(): string { return 'Expr_UnaryMinus'; } @@ -13805,23 +12898,23 @@ use PHPUnitPHAR\PhpParser\Node\Expr; class UnaryPlus extends Expr { /** @var Expr Expression */ - public $expr; + public Expr $expr; /** * Constructs a unary plus node. * - * @param Expr $expr Expression - * @param array $attributes Additional attributes + * @param Expr $expr Expression + * @param array $attributes Additional attributes */ public function __construct(Expr $expr, array $attributes = []) { $this->attributes = $attributes; $this->expr = $expr; } - public function getSubNodeNames() : array + public function getSubNodeNames(): array { return ['expr']; } - public function getType() : string + public function getType(): string { return 'Expr_UnaryPlus'; } @@ -13839,19 +12932,19 @@ class Variable extends Expr /** * Constructs a variable node. * - * @param string|Expr $name Name - * @param array $attributes Additional attributes + * @param string|Expr $name Name + * @param array $attributes Additional attributes */ public function __construct($name, array $attributes = []) { $this->attributes = $attributes; $this->name = $name; } - public function getSubNodeNames() : array + public function getSubNodeNames(): array { return ['name']; } - public function getType() : string + public function getType(): string { return 'Expr_Variable'; } @@ -13865,23 +12958,23 @@ use PHPUnitPHAR\PhpParser\Node\Expr; class YieldFrom extends Expr { /** @var Expr Expression to yield from */ - public $expr; + public Expr $expr; /** * Constructs an "yield from" node. * - * @param Expr $expr Expression - * @param array $attributes Additional attributes + * @param Expr $expr Expression + * @param array $attributes Additional attributes */ public function __construct(Expr $expr, array $attributes = []) { $this->attributes = $attributes; $this->expr = $expr; } - public function getSubNodeNames() : array + public function getSubNodeNames(): array { return ['expr']; } - public function getType() : string + public function getType(): string { return 'Expr_YieldFrom'; } @@ -13895,15 +12988,15 @@ use PHPUnitPHAR\PhpParser\Node\Expr; class Yield_ extends Expr { /** @var null|Expr Key expression */ - public $key; + public ?Expr $key; /** @var null|Expr Value expression */ - public $value; + public ?Expr $value; /** * Constructs a yield expression node. * - * @param null|Expr $value Value expression - * @param null|Expr $key Key expression - * @param array $attributes Additional attributes + * @param null|Expr $value Value expression + * @param null|Expr $key Key expression + * @param array $attributes Additional attributes */ public function __construct(?Expr $value = null, ?Expr $key = null, array $attributes = []) { @@ -13911,11 +13004,11 @@ class Yield_ extends Expr $this->key = $key; $this->value = $value; } - public function getSubNodeNames() : array + public function getSubNodeNames(): array { return ['key', 'value']; } - public function getType() : string + public function getType(): string { return 'Expr_Yield'; } @@ -13930,16 +13023,14 @@ interface FunctionLike extends Node { /** * Whether to return by reference - * - * @return bool */ - public function returnsByRef() : bool; + public function returnsByRef(): bool; /** * List of parameters * * @return Param[] */ - public function getParams() : array; + public function getParams(): array; /** * Get the declared return type or null * @@ -13951,13 +13042,13 @@ interface FunctionLike extends Node * * @return Stmt[]|null */ - public function getStmts(); + public function getStmts(): ?array; /** * Get PHP attribute groups. * * @return AttributeGroup[] */ - public function getAttrGroups() : array; + public function getAttrGroups(): array; } \true, 'parent' => \true, 'static' => \true]; + /** + * @psalm-var non-empty-string + * @var string Identifier as string + */ + public string $name; + /** @var array */ + private static array $specialClassNames = ['self' => \true, 'parent' => \true, 'static' => \true]; /** * Constructs an identifier node. * - * @param string $name Identifier as string - * @param array $attributes Additional attributes + * @param string $name Identifier as string + * @param array $attributes Additional attributes */ public function __construct(string $name, array $attributes = []) { + if ($name === '') { + throw new \InvalidArgumentException('Identifier name cannot be empty'); + } $this->attributes = $attributes; $this->name = $name; } - public function getSubNodeNames() : array + public function getSubNodeNames(): array { return ['name']; } /** * Get identifier as string. * + * @psalm-return non-empty-string * @return string Identifier as string. */ - public function toString() : string + public function toString(): string { return $this->name; } /** * Get lowercased identifier as string. * + * @psalm-return non-empty-string * @return string Lowercased identifier as string */ - public function toLowerString() : string + public function toLowerString(): string { - return \strtolower($this->name); + return strtolower($this->name); } /** * Checks whether the identifier is a special class name (self, parent or static). * * @return bool Whether identifier is a special class name */ - public function isSpecialClassName() : bool + public function isSpecialClassName(): bool { - return isset(self::$specialClassNames[\strtolower($this->name)]); + return isset(self::$specialClassNames[strtolower($this->name)]); } /** * Get identifier as string. * + * @psalm-return non-empty-string * @return string Identifier as string */ - public function __toString() : string + public function __toString(): string { return $this->name; } - public function getType() : string + public function getType(): string { return 'Identifier'; } @@ -14035,26 +13136,57 @@ declare (strict_types=1); namespace PHPUnitPHAR\PhpParser\Node; use PHPUnitPHAR\PhpParser\NodeAbstract; +class InterpolatedStringPart extends NodeAbstract +{ + /** @var string String value */ + public string $value; + /** + * Constructs a node representing a string part of an interpolated string. + * + * @param string $value String value + * @param array $attributes Additional attributes + */ + public function __construct(string $value, array $attributes = []) + { + $this->attributes = $attributes; + $this->value = $value; + } + public function getSubNodeNames(): array + { + return ['value']; + } + public function getType(): string + { + return 'InterpolatedStringPart'; + } +} +// @deprecated compatibility alias +class_alias(InterpolatedStringPart::class, Scalar\EncapsedStringPart::class); + $attributes Additional attributes */ public function __construct(array $types, array $attributes = []) { $this->attributes = $attributes; $this->types = $types; } - public function getSubNodeNames() : array + public function getSubNodeNames(): array { return ['types']; } - public function getType() : string + public function getType(): string { return 'IntersectionType'; } @@ -14068,24 +13200,24 @@ use PHPUnitPHAR\PhpParser\Node; use PHPUnitPHAR\PhpParser\NodeAbstract; class MatchArm extends NodeAbstract { - /** @var null|Node\Expr[] */ - public $conds; + /** @var null|list */ + public ?array $conds; /** @var Node\Expr */ - public $body; + public Expr $body; /** - * @param null|Node\Expr[] $conds + * @param null|list $conds */ - public function __construct($conds, Node\Expr $body, array $attributes = []) + public function __construct(?array $conds, Node\Expr $body, array $attributes = []) { $this->conds = $conds; $this->body = $body; $this->attributes = $attributes; } - public function getSubNodeNames() : array + public function getSubNodeNames(): array { return ['conds', 'body']; } - public function getType() : string + public function getType(): string { return 'MatchArm'; } @@ -14099,77 +13231,85 @@ use PHPUnitPHAR\PhpParser\NodeAbstract; class Name extends NodeAbstract { /** - * @var string[] Parts of the name - * @deprecated Use getParts() instead + * @psalm-var non-empty-string + * @var string Name as string */ - public $parts; - private static $specialClassNames = ['self' => \true, 'parent' => \true, 'static' => \true]; + public string $name; + /** @var array */ + private static array $specialClassNames = ['self' => \true, 'parent' => \true, 'static' => \true]; /** * Constructs a name node. * - * @param string|string[]|self $name Name as string, part array or Name instance (copy ctor) - * @param array $attributes Additional attributes + * @param string|string[]|self $name Name as string, part array or Name instance (copy ctor) + * @param array $attributes Additional attributes */ - public function __construct($name, array $attributes = []) + final public function __construct($name, array $attributes = []) { $this->attributes = $attributes; - $this->parts = self::prepareName($name); + $this->name = self::prepareName($name); } - public function getSubNodeNames() : array + public function getSubNodeNames(): array { - return ['parts']; + return ['name']; } /** * Get parts of name (split by the namespace separator). * + * @psalm-return non-empty-list * @return string[] Parts of name */ - public function getParts() : array + public function getParts(): array { - return $this->parts; + return \explode('\\', $this->name); } /** * Gets the first part of the name, i.e. everything before the first namespace separator. * * @return string First part of the name */ - public function getFirst() : string + public function getFirst(): string { - return $this->parts[0]; + if (\false !== $pos = \strpos($this->name, '\\')) { + return \substr($this->name, 0, $pos); + } + return $this->name; } /** * Gets the last part of the name, i.e. everything after the last namespace separator. * * @return string Last part of the name */ - public function getLast() : string + public function getLast(): string { - return $this->parts[\count($this->parts) - 1]; + if (\false !== $pos = \strrpos($this->name, '\\')) { + return \substr($this->name, $pos + 1); + } + return $this->name; } /** * Checks whether the name is unqualified. (E.g. Name) * * @return bool Whether the name is unqualified */ - public function isUnqualified() : bool + public function isUnqualified(): bool { - return 1 === \count($this->parts); + return \false === \strpos($this->name, '\\'); } /** * Checks whether the name is qualified. (E.g. Name\Name) * * @return bool Whether the name is qualified */ - public function isQualified() : bool + public function isQualified(): bool { - return 1 < \count($this->parts); + return \false !== \strpos($this->name, '\\'); } /** * Checks whether the name is fully qualified. (E.g. \Name) * * @return bool Whether the name is fully qualified */ - public function isFullyQualified() : bool + public function isFullyQualified(): bool { return \false; } @@ -14178,7 +13318,7 @@ class Name extends NodeAbstract * * @return bool Whether the name is relative */ - public function isRelative() : bool + public function isRelative(): bool { return \false; } @@ -14186,19 +13326,21 @@ class Name extends NodeAbstract * Returns a string representation of the name itself, without taking the name type into * account (e.g., not including a leading backslash for fully qualified names). * + * @psalm-return non-empty-string * @return string String representation */ - public function toString() : string + public function toString(): string { - return \implode('\\', $this->parts); + return $this->name; } /** * Returns a string representation of the name as it would occur in code (e.g., including * leading backslash for fully qualified names. * + * @psalm-return non-empty-string * @return string String representation */ - public function toCodeString() : string + public function toCodeString(): string { return $this->toString(); } @@ -14206,30 +13348,32 @@ class Name extends NodeAbstract * Returns lowercased string representation of the name, without taking the name type into * account (e.g., no leading backslash for fully qualified names). * + * @psalm-return non-empty-string * @return string Lowercased string representation */ - public function toLowerString() : string + public function toLowerString(): string { - return \strtolower(\implode('\\', $this->parts)); + return strtolower($this->name); } /** * Checks whether the identifier is a special class name (self, parent or static). * * @return bool Whether identifier is a special class name */ - public function isSpecialClassName() : bool + public function isSpecialClassName(): bool { - return \count($this->parts) === 1 && isset(self::$specialClassNames[\strtolower($this->parts[0])]); + return isset(self::$specialClassNames[strtolower($this->name)]); } /** * Returns a string representation of the name by imploding the namespace parts with the * namespace separator. * + * @psalm-return non-empty-string * @return string String representation */ - public function __toString() : string + public function __toString(): string { - return \implode('\\', $this->parts); + return $this->name; } /** * Gets a slice of a name (similar to array_slice). @@ -14242,31 +13386,39 @@ class Name extends NodeAbstract * * Offset and length have the same meaning as in array_slice(). * - * @param int $offset Offset to start the slice at (may be negative) + * @param int $offset Offset to start the slice at (may be negative) * @param int|null $length Length of the slice (may be negative) * * @return static|null Sliced name */ public function slice(int $offset, ?int $length = null) { - $numParts = \count($this->parts); - $realOffset = $offset < 0 ? $offset + $numParts : $offset; + if ($offset === 1 && $length === null) { + // Short-circuit the common case. + if (\false !== $pos = \strpos($this->name, '\\')) { + return new static(\substr($this->name, $pos + 1)); + } + return null; + } + $parts = \explode('\\', $this->name); + $numParts = \count($parts); + $realOffset = ($offset < 0) ? $offset + $numParts : $offset; if ($realOffset < 0 || $realOffset > $numParts) { - throw new \OutOfBoundsException(\sprintf('Offset %d is out of bounds', $offset)); + throw new \OutOfBoundsException(sprintf('Offset %d is out of bounds', $offset)); } if (null === $length) { $realLength = $numParts - $realOffset; } else { - $realLength = $length < 0 ? $length + $numParts - $realOffset : $length; + $realLength = ($length < 0) ? $length + $numParts - $realOffset : $length; if ($realLength < 0 || $realLength > $numParts - $realOffset) { - throw new \OutOfBoundsException(\sprintf('Length %d is out of bounds', $length)); + throw new \OutOfBoundsException(sprintf('Length %d is out of bounds', $length)); } } if ($realLength === 0) { // Empty slice is represented as null return null; } - return new static(\array_slice($this->parts, $realOffset, $realLength), $this->attributes); + return new static(array_slice($parts, $realOffset, $realLength), $this->attributes); } /** * Concatenate two names, yielding a new Name instance. @@ -14279,9 +13431,9 @@ class Name extends NodeAbstract * Name::concat($namespace, $shortName) * where $namespace is a Name node or null will work as expected. * - * @param string|string[]|self|null $name1 The first name - * @param string|string[]|self|null $name2 The second name - * @param array $attributes Attributes to assign to concatenated name + * @param string|string[]|self|null $name1 The first name + * @param string|string[]|self|null $name2 The second name + * @param array $attributes Attributes to assign to concatenated name * * @return static|null Concatenated name */ @@ -14289,40 +13441,45 @@ class Name extends NodeAbstract { if (null === $name1 && null === $name2) { return null; - } elseif (null === $name1) { - return new static(self::prepareName($name2), $attributes); - } elseif (null === $name2) { - return new static(self::prepareName($name1), $attributes); + } + if (null === $name1) { + return new static($name2, $attributes); + } + if (null === $name2) { + return new static($name1, $attributes); } else { - return new static(\array_merge(self::prepareName($name1), self::prepareName($name2)), $attributes); + return new static(self::prepareName($name1) . '\\' . self::prepareName($name2), $attributes); } } /** * Prepares a (string, array or Name node) name for use in name changing methods by converting - * it to an array. + * it to a string. * * @param string|string[]|self $name Name to prepare * - * @return string[] Prepared name + * @psalm-return non-empty-string + * @return string Prepared name */ - private static function prepareName($name) : array + private static function prepareName($name): string { if (\is_string($name)) { if ('' === $name) { throw new \InvalidArgumentException('Name cannot be empty'); } - return \explode('\\', $name); - } elseif (\is_array($name)) { + return $name; + } + if (\is_array($name)) { if (empty($name)) { throw new \InvalidArgumentException('Name cannot be empty'); } - return $name; - } elseif ($name instanceof self) { - return $name->parts; + return implode('\\', $name); + } + if ($name instanceof self) { + return $name->name; } throw new \InvalidArgumentException('Expected string, array of parts or Name instance'); } - public function getType() : string + public function getType(): string { return 'Name'; } @@ -14339,7 +13496,7 @@ class FullyQualified extends \PHPUnitPHAR\PhpParser\Node\Name * * @return bool Whether the name is unqualified */ - public function isUnqualified() : bool + public function isUnqualified(): bool { return \false; } @@ -14348,7 +13505,7 @@ class FullyQualified extends \PHPUnitPHAR\PhpParser\Node\Name * * @return bool Whether the name is qualified */ - public function isQualified() : bool + public function isQualified(): bool { return \false; } @@ -14357,7 +13514,7 @@ class FullyQualified extends \PHPUnitPHAR\PhpParser\Node\Name * * @return bool Whether the name is fully qualified */ - public function isFullyQualified() : bool + public function isFullyQualified(): bool { return \true; } @@ -14366,15 +13523,15 @@ class FullyQualified extends \PHPUnitPHAR\PhpParser\Node\Name * * @return bool Whether the name is relative */ - public function isRelative() : bool + public function isRelative(): bool { return \false; } - public function toCodeString() : string + public function toCodeString(): string { return '\\' . $this->toString(); } - public function getType() : string + public function getType(): string { return 'Name_FullyQualified'; } @@ -14391,7 +13548,7 @@ class Relative extends \PHPUnitPHAR\PhpParser\Node\Name * * @return bool Whether the name is unqualified */ - public function isUnqualified() : bool + public function isUnqualified(): bool { return \false; } @@ -14400,7 +13557,7 @@ class Relative extends \PHPUnitPHAR\PhpParser\Node\Name * * @return bool Whether the name is qualified */ - public function isQualified() : bool + public function isQualified(): bool { return \false; } @@ -14409,7 +13566,7 @@ class Relative extends \PHPUnitPHAR\PhpParser\Node\Name * * @return bool Whether the name is fully qualified */ - public function isFullyQualified() : bool + public function isFullyQualified(): bool { return \false; } @@ -14418,15 +13575,15 @@ class Relative extends \PHPUnitPHAR\PhpParser\Node\Name * * @return bool Whether the name is relative */ - public function isRelative() : bool + public function isRelative(): bool { return \true; } - public function toCodeString() : string + public function toCodeString(): string { return 'namespace\\' . $this->toString(); } - public function getType() : string + public function getType(): string { return 'Name_Relative'; } @@ -14436,26 +13593,27 @@ class Relative extends \PHPUnitPHAR\PhpParser\Node\Name declare (strict_types=1); namespace PHPUnitPHAR\PhpParser\Node; +use PHPUnitPHAR\PhpParser\Node; class NullableType extends ComplexType { /** @var Identifier|Name Type */ - public $type; + public Node $type; /** * Constructs a nullable type (wrapping another type). * - * @param string|Identifier|Name $type Type - * @param array $attributes Additional attributes + * @param Identifier|Name $type Type + * @param array $attributes Additional attributes */ - public function __construct($type, array $attributes = []) + public function __construct(Node $type, array $attributes = []) { $this->attributes = $attributes; - $this->type = \is_string($type) ? new Identifier($type) : $type; + $this->type = $type; } - public function getSubNodeNames() : array + public function getSubNodeNames(): array { return ['type']; } - public function getType() : string + public function getType(): string { return 'NullableType'; } @@ -14465,39 +13623,41 @@ class NullableType extends ComplexType declare (strict_types=1); namespace PHPUnitPHAR\PhpParser\Node; +use PHPUnitPHAR\PhpParser\Modifiers; +use PHPUnitPHAR\PhpParser\Node; use PHPUnitPHAR\PhpParser\NodeAbstract; class Param extends NodeAbstract { /** @var null|Identifier|Name|ComplexType Type declaration */ - public $type; + public ?Node $type; /** @var bool Whether parameter is passed by reference */ - public $byRef; + public bool $byRef; /** @var bool Whether this is a variadic argument */ - public $variadic; + public bool $variadic; /** @var Expr\Variable|Expr\Error Parameter variable */ - public $var; + public Expr $var; /** @var null|Expr Default value */ - public $default; - /** @var int */ - public $flags; + public ?Expr $default; + /** @var int Optional visibility flags */ + public int $flags; /** @var AttributeGroup[] PHP attribute groups */ - public $attrGroups; + public array $attrGroups; /** * Constructs a parameter node. * - * @param Expr\Variable|Expr\Error $var Parameter variable - * @param null|Expr $default Default value - * @param null|string|Identifier|Name|ComplexType $type Type declaration - * @param bool $byRef Whether is passed by reference - * @param bool $variadic Whether this is a variadic argument - * @param array $attributes Additional attributes - * @param int $flags Optional visibility flags - * @param AttributeGroup[] $attrGroups PHP attribute groups + * @param Expr\Variable|Expr\Error $var Parameter variable + * @param null|Expr $default Default value + * @param null|Identifier|Name|ComplexType $type Type declaration + * @param bool $byRef Whether is passed by reference + * @param bool $variadic Whether this is a variadic argument + * @param array $attributes Additional attributes + * @param int $flags Optional visibility flags + * @param list $attrGroups PHP attribute groups */ - public function __construct($var, ?Expr $default = null, $type = null, bool $byRef = \false, bool $variadic = \false, array $attributes = [], int $flags = 0, array $attrGroups = []) + public function __construct(Expr $var, ?Expr $default = null, ?Node $type = null, bool $byRef = \false, bool $variadic = \false, array $attributes = [], int $flags = 0, array $attrGroups = []) { $this->attributes = $attributes; - $this->type = \is_string($type) ? new Identifier($type) : $type; + $this->type = $type; $this->byRef = $byRef; $this->variadic = $variadic; $this->var = $var; @@ -14505,15 +13665,75 @@ class Param extends NodeAbstract $this->flags = $flags; $this->attrGroups = $attrGroups; } - public function getSubNodeNames() : array + public function getSubNodeNames(): array { return ['attrGroups', 'flags', 'type', 'byRef', 'variadic', 'var', 'default']; } - public function getType() : string + public function getType(): string { return 'Param'; } + /** + * Whether this parameter uses constructor property promotion. + */ + public function isPromoted(): bool + { + return $this->flags !== 0; + } + public function isPublic(): bool + { + return (bool) ($this->flags & Modifiers::PUBLIC); + } + public function isProtected(): bool + { + return (bool) ($this->flags & Modifiers::PROTECTED); + } + public function isPrivate(): bool + { + return (bool) ($this->flags & Modifiers::PRIVATE); + } + public function isReadonly(): bool + { + return (bool) ($this->flags & Modifiers::READONLY); + } +} + $attributes Additional attributes + */ + public function __construct($name, ?Node\Expr $default = null, array $attributes = []) + { + $this->attributes = $attributes; + $this->name = \is_string($name) ? new Node\VarLikeIdentifier($name) : $name; + $this->default = $default; + } + public function getSubNodeNames(): array + { + return ['name', 'default']; + } + public function getType(): string + { + return 'PropertyItem'; + } } +// @deprecated compatibility alias +class_alias(PropertyItem::class, Stmt\PropertyProperty::class); $attributes Additional attributes */ public function __construct(float $value, array $attributes = []) { $this->attributes = $attributes; $this->value = $value; } - public function getSubNodeNames() : array + public function getSubNodeNames(): array { return ['value']; } /** * @param mixed[] $attributes */ - public static function fromString(string $str, array $attributes = []) : DNumber + public static function fromString(string $str, array $attributes = []): Float_ { $attributes['rawValue'] = $str; $float = self::parse($str); - return new DNumber($float, $attributes); + return new Float_($float, $attributes); } /** * @internal @@ -14565,95 +13803,36 @@ class DNumber extends Scalar * * @return float The parsed number */ - public static function parse(string $str) : float + public static function parse(string $str): float { - $str = \str_replace('_', '', $str); + $str = str_replace('_', '', $str); // Check whether this is one of the special integer notations. if ('0' === $str[0]) { // hex if ('x' === $str[1] || 'X' === $str[1]) { - return \hexdec($str); + return hexdec($str); } // bin if ('b' === $str[1] || 'B' === $str[1]) { - return \bindec($str); + return bindec($str); } // oct, but only if the string does not contain any of '.eE'. - if (\false === \strpbrk($str, '.eE')) { + if (\false === strpbrk($str, '.eE')) { // substr($str, 0, strcspn($str, '89')) cuts the string at the first invalid digit // (8 or 9) so that only the digits before that are used. - return \octdec(\substr($str, 0, \strcspn($str, '89'))); + return octdec(substr($str, 0, strcspn($str, '89'))); } } // dec return (float) $str; } - public function getType() : string + public function getType(): string { - return 'Scalar_DNumber'; - } -} -attributes = $attributes; - $this->parts = $parts; - } - public function getSubNodeNames() : array - { - return ['parts']; - } - public function getType() : string - { - return 'Scalar_Encapsed'; - } -} -attributes = $attributes; - $this->value = $value; - } - public function getSubNodeNames() : array - { - return ['value']; - } - public function getType() : string - { - return 'Scalar_EncapsedStringPart'; + return 'Scalar_Float'; } } +// @deprecated compatibility alias +class_alias(Float_::class, DNumber::class); $attributes Additional attributes */ public function __construct(int $value, array $attributes = []) { $this->attributes = $attributes; $this->value = $value; } - public function getSubNodeNames() : array + public function getSubNodeNames(): array { return ['value']; } /** - * Constructs an LNumber node from a string number literal. + * Constructs an Int node from a string number literal. * - * @param string $str String number literal (decimal, octal, hex or binary) - * @param array $attributes Additional attributes - * @param bool $allowInvalidOctal Whether to allow invalid octal numbers (PHP 5) + * @param string $str String number literal (decimal, octal, hex or binary) + * @param array $attributes Additional attributes + * @param bool $allowInvalidOctal Whether to allow invalid octal numbers (PHP 5) * - * @return LNumber The constructed LNumber, including kind attribute + * @return Int_ The constructed LNumber, including kind attribute */ - public static function fromString(string $str, array $attributes = [], bool $allowInvalidOctal = \false) : LNumber + public static function fromString(string $str, array $attributes = [], bool $allowInvalidOctal = \false): Int_ { $attributes['rawValue'] = $str; - $str = \str_replace('_', '', $str); + $str = str_replace('_', '', $str); if ('0' !== $str[0] || '0' === $str) { - $attributes['kind'] = LNumber::KIND_DEC; - return new LNumber((int) $str, $attributes); + $attributes['kind'] = Int_::KIND_DEC; + return new Int_((int) $str, $attributes); } if ('x' === $str[1] || 'X' === $str[1]) { - $attributes['kind'] = LNumber::KIND_HEX; - return new LNumber(\hexdec($str), $attributes); + $attributes['kind'] = Int_::KIND_HEX; + return new Int_(hexdec($str), $attributes); } if ('b' === $str[1] || 'B' === $str[1]) { - $attributes['kind'] = LNumber::KIND_BIN; - return new LNumber(\bindec($str), $attributes); + $attributes['kind'] = Int_::KIND_BIN; + return new Int_(bindec($str), $attributes); } - if (!$allowInvalidOctal && \strpbrk($str, '89')) { + if (!$allowInvalidOctal && strpbrk($str, '89')) { throw new Error('Invalid numeric literal', $attributes); } // Strip optional explicit octal prefix. if ('o' === $str[1] || 'O' === $str[1]) { - $str = \substr($str, 2); + $str = substr($str, 2); } // use intval instead of octdec to get proper cutting behavior with malformed numbers - $attributes['kind'] = LNumber::KIND_OCT; - return new LNumber(\intval($str, 8), $attributes); + $attributes['kind'] = Int_::KIND_OCT; + return new Int_(intval($str, 8), $attributes); + } + public function getType(): string + { + return 'Scalar_Int'; + } +} +// @deprecated compatibility alias +class_alias(Int_::class, LNumber::class); + $attributes Additional attributes + */ + public function __construct(array $parts, array $attributes = []) + { + $this->attributes = $attributes; + $this->parts = $parts; + } + public function getSubNodeNames(): array + { + return ['parts']; } - public function getType() : string + public function getType(): string { - return 'Scalar_LNumber'; + return 'Scalar_InterpolatedString'; } } +// @deprecated compatibility alias +class_alias(InterpolatedString::class, Encapsed::class); + $attributes Additional attributes */ public function __construct(array $attributes = []) { $this->attributes = $attributes; } - public function getSubNodeNames() : array + public function getSubNodeNames(): array { return []; } @@ -14752,7 +13973,7 @@ abstract class MagicConst extends Scalar * * @return string Name of magic constant */ - public abstract function getName() : string; + abstract public function getName(): string; } '\\', '$' => '$', 'n' => "\n", 'r' => "\r", 't' => "\t", 'f' => "\f", 'v' => "\v", 'e' => "\x1b"]; + public string $value; + /** @var array Escaped character to its decoded value */ + protected static array $replacements = ['\\' => '\\', '$' => '$', 'n' => "\n", 'r' => "\r", 't' => "\t", 'f' => "\f", 'v' => "\v", 'e' => "\x1b"]; /** * Constructs a string scalar node. * - * @param string $value Value of the string - * @param array $attributes Additional attributes + * @param string $value Value of the string + * @param array $attributes Additional attributes */ public function __construct(string $value, array $attributes = []) { $this->attributes = $attributes; $this->value = $value; } - public function getSubNodeNames() : array + public function getSubNodeNames(): array { return ['value']; } /** + * @param array $attributes * @param bool $parseUnicodeEscape Whether to parse PHP 7 \u escapes */ - public static function fromString(string $str, array $attributes = [], bool $parseUnicodeEscape = \true) : self + public static function fromString(string $str, array $attributes = [], bool $parseUnicodeEscape = \true): self { - $attributes['kind'] = $str[0] === "'" || $str[1] === "'" && ($str[0] === 'b' || $str[0] === 'B') ? Scalar\String_::KIND_SINGLE_QUOTED : Scalar\String_::KIND_DOUBLE_QUOTED; + $attributes['kind'] = ($str[0] === "'" || $str[1] === "'" && ($str[0] === 'b' || $str[0] === 'B')) ? Scalar\String_::KIND_SINGLE_QUOTED : Scalar\String_::KIND_DOUBLE_QUOTED; $attributes['rawValue'] = $str; $string = self::parse($str, $parseUnicodeEscape); return new self($string, $attributes); @@ -14942,16 +14165,16 @@ class String_ extends Scalar * * @return string The parsed string */ - public static function parse(string $str, bool $parseUnicodeEscape = \true) : string + public static function parse(string $str, bool $parseUnicodeEscape = \true): string { $bLength = 0; if ('b' === $str[0] || 'B' === $str[0]) { $bLength = 1; } if ('\'' === $str[$bLength]) { - return \str_replace(['\\\\', '\\\''], ['\\', '\''], \substr($str, $bLength + 1, -1)); + return str_replace(['\\\\', '\\\''], ['\\', '\''], substr($str, $bLength + 1, -1)); } else { - return self::parseEscapeSequences(\substr($str, $bLength + 1, -1), '"', $parseUnicodeEscape); + return self::parseEscapeSequences(substr($str, $bLength + 1, -1), '"', $parseUnicodeEscape); } } /** @@ -14959,31 +14182,35 @@ class String_ extends Scalar * * Parses escape sequences in strings (all string types apart from single quoted). * - * @param string $str String without quotes + * @param string $str String without quotes * @param null|string $quote Quote type * @param bool $parseUnicodeEscape Whether to parse PHP 7 \u escapes * * @return string String with escape sequences parsed */ - public static function parseEscapeSequences(string $str, $quote, bool $parseUnicodeEscape = \true) : string + public static function parseEscapeSequences(string $str, ?string $quote, bool $parseUnicodeEscape = \true): string { if (null !== $quote) { - $str = \str_replace('\\' . $quote, $quote, $str); + $str = str_replace('\\' . $quote, $quote, $str); } $extra = ''; if ($parseUnicodeEscape) { - $extra = '|u\\{([0-9a-fA-F]+)\\}'; + $extra = '|u\{([0-9a-fA-F]+)\}'; } - return \preg_replace_callback('~\\\\([\\\\$nrtfve]|[xX][0-9a-fA-F]{1,2}|[0-7]{1,3}' . $extra . ')~', function ($matches) { + return preg_replace_callback('~\\\\([\\\\$nrtfve]|[xX][0-9a-fA-F]{1,2}|[0-7]{1,3}' . $extra . ')~', function ($matches) { $str = $matches[1]; if (isset(self::$replacements[$str])) { return self::$replacements[$str]; - } elseif ('x' === $str[0] || 'X' === $str[0]) { - return \chr(\hexdec(\substr($str, 1))); - } elseif ('u' === $str[0]) { - return self::codePointToUtf8(\hexdec($matches[2])); + } + if ('x' === $str[0] || 'X' === $str[0]) { + return chr(hexdec(substr($str, 1))); + } + if ('u' === $str[0]) { + $dec = hexdec($matches[2]); + // If it overflowed to float, treat as INT_MAX, it will throw an error anyway. + return self::codePointToUtf8(\is_int($dec) ? $dec : \PHP_INT_MAX); } else { - return \chr(\octdec($str)); + return chr(octdec($str)); } }, $str); } @@ -14994,23 +14221,23 @@ class String_ extends Scalar * * @return string UTF-8 representation of code point */ - private static function codePointToUtf8(int $num) : string + private static function codePointToUtf8(int $num): string { if ($num <= 0x7f) { - return \chr($num); + return chr($num); } if ($num <= 0x7ff) { - return \chr(($num >> 6) + 0xc0) . \chr(($num & 0x3f) + 0x80); + return chr(($num >> 6) + 0xc0) . chr(($num & 0x3f) + 0x80); } if ($num <= 0xffff) { - return \chr(($num >> 12) + 0xe0) . \chr(($num >> 6 & 0x3f) + 0x80) . \chr(($num & 0x3f) + 0x80); + return chr(($num >> 12) + 0xe0) . chr(($num >> 6 & 0x3f) + 0x80) . chr(($num & 0x3f) + 0x80); } if ($num <= 0x1fffff) { - return \chr(($num >> 18) + 0xf0) . \chr(($num >> 12 & 0x3f) + 0x80) . \chr(($num >> 6 & 0x3f) + 0x80) . \chr(($num & 0x3f) + 0x80); + return chr(($num >> 18) + 0xf0) . chr(($num >> 12 & 0x3f) + 0x80) . chr(($num >> 6 & 0x3f) + 0x80) . chr(($num & 0x3f) + 0x80); } throw new Error('Invalid UTF-8 codepoint escape sequence: Codepoint too large'); } - public function getType() : string + public function getType(): string { return 'Scalar_String'; } @@ -15020,6 +14247,43 @@ class String_ extends Scalar declare (strict_types=1); namespace PHPUnitPHAR\PhpParser\Node; +use PHPUnitPHAR\PhpParser\Node; +use PHPUnitPHAR\PhpParser\NodeAbstract; +class StaticVar extends NodeAbstract +{ + /** @var Expr\Variable Variable */ + public Expr\Variable $var; + /** @var null|Node\Expr Default value */ + public ?Expr $default; + /** + * Constructs a static variable node. + * + * @param Expr\Variable $var Name + * @param null|Node\Expr $default Default value + * @param array $attributes Additional attributes + */ + public function __construct(Expr\Variable $var, ?Node\Expr $default = null, array $attributes = []) + { + $this->attributes = $attributes; + $this->var = $var; + $this->default = $default; + } + public function getSubNodeNames(): array + { + return ['var', 'default']; + } + public function getType(): string + { + return 'StaticVar'; + } +} +// @deprecated compatibility alias +class_alias(StaticVar::class, Stmt\StaticVar::class); + $attributes Additional attributes + */ + public function __construct(array $stmts, array $attributes = []) + { + $this->attributes = $attributes; + $this->stmts = $stmts; + } + public function getType(): string + { + return 'Stmt_Block'; + } + public function getSubNodeNames(): array + { + return ['stmts']; + } +} + $attributes Additional attributes */ public function __construct(?Node\Expr $num = null, array $attributes = []) { $this->attributes = $attributes; $this->num = $num; } - public function getSubNodeNames() : array + public function getSubNodeNames(): array { return ['num']; } - public function getType() : string + public function getType(): string { return 'Stmt_Break'; } @@ -15063,27 +14357,27 @@ use PHPUnitPHAR\PhpParser\Node; class Case_ extends Node\Stmt { /** @var null|Node\Expr Condition (null for default) */ - public $cond; + public ?Node\Expr $cond; /** @var Node\Stmt[] Statements */ - public $stmts; + public array $stmts; /** * Constructs a case node. * - * @param null|Node\Expr $cond Condition (null for default) - * @param Node\Stmt[] $stmts Statements - * @param array $attributes Additional attributes + * @param null|Node\Expr $cond Condition (null for default) + * @param Node\Stmt[] $stmts Statements + * @param array $attributes Additional attributes */ - public function __construct($cond, array $stmts = [], array $attributes = []) + public function __construct(?Node\Expr $cond, array $stmts = [], array $attributes = []) { $this->attributes = $attributes; $this->cond = $cond; $this->stmts = $stmts; } - public function getSubNodeNames() : array + public function getSubNodeNames(): array { return ['cond', 'stmts']; } - public function getType() : string + public function getType(): string { return 'Stmt_Case'; } @@ -15098,18 +14392,18 @@ use PHPUnitPHAR\PhpParser\Node\Expr; class Catch_ extends Node\Stmt { /** @var Node\Name[] Types of exceptions to catch */ - public $types; + public array $types; /** @var Expr\Variable|null Variable for exception */ - public $var; + public ?Expr\Variable $var; /** @var Node\Stmt[] Statements */ - public $stmts; + public array $stmts; /** * Constructs a catch node. * - * @param Node\Name[] $types Types of exceptions to catch - * @param Expr\Variable|null $var Variable for exception - * @param Node\Stmt[] $stmts Statements - * @param array $attributes Additional attributes + * @param Node\Name[] $types Types of exceptions to catch + * @param Expr\Variable|null $var Variable for exception + * @param Node\Stmt[] $stmts Statements + * @param array $attributes Additional attributes */ public function __construct(array $types, ?Expr\Variable $var = null, array $stmts = [], array $attributes = []) { @@ -15118,11 +14412,11 @@ class Catch_ extends Node\Stmt $this->var = $var; $this->stmts = $stmts; } - public function getSubNodeNames() : array + public function getSubNodeNames(): array { return ['types', 'var', 'stmts']; } - public function getType() : string + public function getType(): string { return 'Stmt_Catch'; } @@ -15132,75 +14426,68 @@ class Catch_ extends Node\Stmt declare (strict_types=1); namespace PHPUnitPHAR\PhpParser\Node\Stmt; +use PHPUnitPHAR\PhpParser\Modifiers; use PHPUnitPHAR\PhpParser\Node; class ClassConst extends Node\Stmt { /** @var int Modifiers */ - public $flags; + public int $flags; /** @var Node\Const_[] Constant declarations */ - public $consts; + public array $consts; /** @var Node\AttributeGroup[] PHP attribute groups */ - public $attrGroups; + public array $attrGroups; /** @var Node\Identifier|Node\Name|Node\ComplexType|null Type declaration */ - public $type; + public ?Node $type; /** * Constructs a class const list node. * - * @param Node\Const_[] $consts Constant declarations - * @param int $flags Modifiers - * @param array $attributes Additional attributes - * @param Node\AttributeGroup[] $attrGroups PHP attribute groups - * @param null|string|Node\Identifier|Node\Name|Node\ComplexType $type Type declaration + * @param Node\Const_[] $consts Constant declarations + * @param int $flags Modifiers + * @param array $attributes Additional attributes + * @param list $attrGroups PHP attribute groups + * @param null|Node\Identifier|Node\Name|Node\ComplexType $type Type declaration */ - public function __construct(array $consts, int $flags = 0, array $attributes = [], array $attrGroups = [], $type = null) + public function __construct(array $consts, int $flags = 0, array $attributes = [], array $attrGroups = [], ?Node $type = null) { $this->attributes = $attributes; $this->flags = $flags; $this->consts = $consts; $this->attrGroups = $attrGroups; - $this->type = \is_string($type) ? new Node\Identifier($type) : $type; + $this->type = $type; } - public function getSubNodeNames() : array + public function getSubNodeNames(): array { return ['attrGroups', 'flags', 'type', 'consts']; } /** * Whether constant is explicitly or implicitly public. - * - * @return bool */ - public function isPublic() : bool + public function isPublic(): bool { - return ($this->flags & Class_::MODIFIER_PUBLIC) !== 0 || ($this->flags & Class_::VISIBILITY_MODIFIER_MASK) === 0; + return ($this->flags & Modifiers::PUBLIC) !== 0 || ($this->flags & Modifiers::VISIBILITY_MASK) === 0; } /** * Whether constant is protected. - * - * @return bool */ - public function isProtected() : bool + public function isProtected(): bool { - return (bool) ($this->flags & Class_::MODIFIER_PROTECTED); + return (bool) ($this->flags & Modifiers::PROTECTED); } /** * Whether constant is private. - * - * @return bool */ - public function isPrivate() : bool + public function isPrivate(): bool { - return (bool) ($this->flags & Class_::MODIFIER_PRIVATE); + return (bool) ($this->flags & Modifiers::PRIVATE); } /** * Whether constant is final. - * - * @return bool */ - public function isFinal() : bool + public function isFinal(): bool { - return (bool) ($this->flags & Class_::MODIFIER_FINAL); + return (bool) ($this->flags & Modifiers::FINAL); } - public function getType() : string + public function getType(): string { return 'Stmt_ClassConst'; } @@ -15211,20 +14498,21 @@ declare (strict_types=1); namespace PHPUnitPHAR\PhpParser\Node\Stmt; use PHPUnitPHAR\PhpParser\Node; +use PHPUnitPHAR\PhpParser\Node\PropertyItem; abstract class ClassLike extends Node\Stmt { /** @var Node\Identifier|null Name */ - public $name; + public ?Node\Identifier $name; /** @var Node\Stmt[] Statements */ - public $stmts; + public array $stmts; /** @var Node\AttributeGroup[] PHP attribute groups */ - public $attrGroups; + public array $attrGroups; /** @var Node\Name|null Namespaced name (if using NameResolver) */ - public $namespacedName; + public ?Node\Name $namespacedName; /** * @return TraitUse[] */ - public function getTraitUses() : array + public function getTraitUses(): array { $traitUses = []; foreach ($this->stmts as $stmt) { @@ -15237,7 +14525,7 @@ abstract class ClassLike extends Node\Stmt /** * @return ClassConst[] */ - public function getConstants() : array + public function getConstants(): array { $constants = []; foreach ($this->stmts as $stmt) { @@ -15250,7 +14538,7 @@ abstract class ClassLike extends Node\Stmt /** * @return Property[] */ - public function getProperties() : array + public function getProperties(): array { $properties = []; foreach ($this->stmts as $stmt) { @@ -15267,12 +14555,12 @@ abstract class ClassLike extends Node\Stmt * * @return Property|null Property node or null if the property does not exist */ - public function getProperty(string $name) + public function getProperty(string $name): ?Property { foreach ($this->stmts as $stmt) { if ($stmt instanceof Property) { foreach ($stmt->props as $prop) { - if ($prop instanceof PropertyProperty && $name === $prop->name->toString()) { + if ($prop instanceof PropertyItem && $name === $prop->name->toString()) { return $stmt; } } @@ -15285,7 +14573,7 @@ abstract class ClassLike extends Node\Stmt * * @return ClassMethod[] */ - public function getMethods() : array + public function getMethods(): array { $methods = []; foreach ($this->stmts as $stmt) { @@ -15302,9 +14590,9 @@ abstract class ClassLike extends Node\Stmt * * @return ClassMethod|null Method node or null if the method does not exist */ - public function getMethod(string $name) + public function getMethod(string $name): ?ClassMethod { - $lowerName = \strtolower($name); + $lowerName = strtolower($name); foreach ($this->stmts as $stmt) { if ($stmt instanceof ClassMethod && $lowerName === $stmt->name->toLowerString()) { return $stmt; @@ -15318,37 +14606,46 @@ abstract class ClassLike extends Node\Stmt declare (strict_types=1); namespace PHPUnitPHAR\PhpParser\Node\Stmt; +use PHPUnitPHAR\PhpParser\Modifiers; use PHPUnitPHAR\PhpParser\Node; use PHPUnitPHAR\PhpParser\Node\FunctionLike; class ClassMethod extends Node\Stmt implements FunctionLike { /** @var int Flags */ - public $flags; + public int $flags; /** @var bool Whether to return by reference */ - public $byRef; + public bool $byRef; /** @var Node\Identifier Name */ - public $name; + public Node\Identifier $name; /** @var Node\Param[] Parameters */ - public $params; + public array $params; /** @var null|Node\Identifier|Node\Name|Node\ComplexType Return type */ - public $returnType; + public ?Node $returnType; /** @var Node\Stmt[]|null Statements */ - public $stmts; + public ?array $stmts; /** @var Node\AttributeGroup[] PHP attribute groups */ - public $attrGroups; - private static $magicNames = ['__construct' => \true, '__destruct' => \true, '__call' => \true, '__callstatic' => \true, '__get' => \true, '__set' => \true, '__isset' => \true, '__unset' => \true, '__sleep' => \true, '__wakeup' => \true, '__tostring' => \true, '__set_state' => \true, '__clone' => \true, '__invoke' => \true, '__debuginfo' => \true, '__serialize' => \true, '__unserialize' => \true]; + public array $attrGroups; + /** @var array */ + private static array $magicNames = ['__construct' => \true, '__destruct' => \true, '__call' => \true, '__callstatic' => \true, '__get' => \true, '__set' => \true, '__isset' => \true, '__unset' => \true, '__sleep' => \true, '__wakeup' => \true, '__tostring' => \true, '__set_state' => \true, '__clone' => \true, '__invoke' => \true, '__debuginfo' => \true, '__serialize' => \true, '__unserialize' => \true]; /** * Constructs a class method node. * * @param string|Node\Identifier $name Name - * @param array $subNodes Array of the following optional subnodes: - * 'flags => MODIFIER_PUBLIC: Flags - * 'byRef' => false : Whether to return by reference - * 'params' => array() : Parameters - * 'returnType' => null : Return type - * 'stmts' => array() : Statements - * 'attrGroups' => array() : PHP attribute groups - * @param array $attributes Additional attributes + * @param array{ + * flags?: int, + * byRef?: bool, + * params?: Node\Param[], + * returnType?: null|Node\Identifier|Node\Name|Node\ComplexType, + * stmts?: Node\Stmt[]|null, + * attrGroups?: Node\AttributeGroup[], + * } $subNodes Array of the following optional subnodes: + * 'flags => 0 : Flags + * 'byRef' => false : Whether to return by reference + * 'params' => array() : Parameters + * 'returnType' => null : Return type + * 'stmts' => array() : Statements + * 'attrGroups' => array() : PHP attribute groups + * @param array $attributes Additional attributes */ public function __construct($name, array $subNodes = [], array $attributes = []) { @@ -15357,20 +14654,19 @@ class ClassMethod extends Node\Stmt implements FunctionLike $this->byRef = $subNodes['byRef'] ?? \false; $this->name = \is_string($name) ? new Node\Identifier($name) : $name; $this->params = $subNodes['params'] ?? []; - $returnType = $subNodes['returnType'] ?? null; - $this->returnType = \is_string($returnType) ? new Node\Identifier($returnType) : $returnType; - $this->stmts = \array_key_exists('stmts', $subNodes) ? $subNodes['stmts'] : []; + $this->returnType = $subNodes['returnType'] ?? null; + $this->stmts = array_key_exists('stmts', $subNodes) ? $subNodes['stmts'] : []; $this->attrGroups = $subNodes['attrGroups'] ?? []; } - public function getSubNodeNames() : array + public function getSubNodeNames(): array { return ['attrGroups', 'flags', 'byRef', 'name', 'params', 'returnType', 'stmts']; } - public function returnsByRef() : bool + public function returnsByRef(): bool { return $this->byRef; } - public function getParams() : array + public function getParams(): array { return $this->params; } @@ -15378,78 +14674,64 @@ class ClassMethod extends Node\Stmt implements FunctionLike { return $this->returnType; } - public function getStmts() + public function getStmts(): ?array { return $this->stmts; } - public function getAttrGroups() : array + public function getAttrGroups(): array { return $this->attrGroups; } /** * Whether the method is explicitly or implicitly public. - * - * @return bool */ - public function isPublic() : bool + public function isPublic(): bool { - return ($this->flags & Class_::MODIFIER_PUBLIC) !== 0 || ($this->flags & Class_::VISIBILITY_MODIFIER_MASK) === 0; + return ($this->flags & Modifiers::PUBLIC) !== 0 || ($this->flags & Modifiers::VISIBILITY_MASK) === 0; } /** * Whether the method is protected. - * - * @return bool */ - public function isProtected() : bool + public function isProtected(): bool { - return (bool) ($this->flags & Class_::MODIFIER_PROTECTED); + return (bool) ($this->flags & Modifiers::PROTECTED); } /** * Whether the method is private. - * - * @return bool */ - public function isPrivate() : bool + public function isPrivate(): bool { - return (bool) ($this->flags & Class_::MODIFIER_PRIVATE); + return (bool) ($this->flags & Modifiers::PRIVATE); } /** * Whether the method is abstract. - * - * @return bool */ - public function isAbstract() : bool + public function isAbstract(): bool { - return (bool) ($this->flags & Class_::MODIFIER_ABSTRACT); + return (bool) ($this->flags & Modifiers::ABSTRACT); } /** * Whether the method is final. - * - * @return bool */ - public function isFinal() : bool + public function isFinal(): bool { - return (bool) ($this->flags & Class_::MODIFIER_FINAL); + return (bool) ($this->flags & Modifiers::FINAL); } /** * Whether the method is static. - * - * @return bool */ - public function isStatic() : bool + public function isStatic(): bool { - return (bool) ($this->flags & Class_::MODIFIER_STATIC); + return (bool) ($this->flags & Modifiers::STATIC); } /** * Whether the method is magic. - * - * @return bool */ - public function isMagic() : bool + public function isMagic(): bool { return isset(self::$magicNames[$this->name->toLowerString()]); } - public function getType() : string + public function getType(): string { return 'Stmt_ClassMethod'; } @@ -15459,36 +14741,50 @@ class ClassMethod extends Node\Stmt implements FunctionLike declare (strict_types=1); namespace PHPUnitPHAR\PhpParser\Node\Stmt; -use PHPUnitPHAR\PhpParser\Error; +use PHPUnitPHAR\PhpParser\Modifiers; use PHPUnitPHAR\PhpParser\Node; class Class_ extends ClassLike { - const MODIFIER_PUBLIC = 1; - const MODIFIER_PROTECTED = 2; - const MODIFIER_PRIVATE = 4; - const MODIFIER_STATIC = 8; - const MODIFIER_ABSTRACT = 16; - const MODIFIER_FINAL = 32; - const MODIFIER_READONLY = 64; - const VISIBILITY_MODIFIER_MASK = 7; + /** @deprecated Use Modifiers::PUBLIC instead */ + public const MODIFIER_PUBLIC = 1; + /** @deprecated Use Modifiers::PROTECTED instead */ + public const MODIFIER_PROTECTED = 2; + /** @deprecated Use Modifiers::PRIVATE instead */ + public const MODIFIER_PRIVATE = 4; + /** @deprecated Use Modifiers::STATIC instead */ + public const MODIFIER_STATIC = 8; + /** @deprecated Use Modifiers::ABSTRACT instead */ + public const MODIFIER_ABSTRACT = 16; + /** @deprecated Use Modifiers::FINAL instead */ + public const MODIFIER_FINAL = 32; + /** @deprecated Use Modifiers::READONLY instead */ + public const MODIFIER_READONLY = 64; + /** @deprecated Use Modifiers::VISIBILITY_MASK instead */ + public const VISIBILITY_MODIFIER_MASK = 7; // 1 | 2 | 4 - /** @var int Type */ - public $flags; + /** @var int Modifiers */ + public int $flags; /** @var null|Node\Name Name of extended class */ - public $extends; + public ?Node\Name $extends; /** @var Node\Name[] Names of implemented interfaces */ - public $implements; + public array $implements; /** * Constructs a class node. * * @param string|Node\Identifier|null $name Name - * @param array $subNodes Array of the following optional subnodes: - * 'flags' => 0 : Flags - * 'extends' => null : Name of extended class - * 'implements' => array(): Names of implemented interfaces - * 'stmts' => array(): Statements - * 'attrGroups' => array(): PHP attribute groups - * @param array $attributes Additional attributes + * @param array{ + * flags?: int, + * extends?: Node\Name|null, + * implements?: Node\Name[], + * stmts?: Node\Stmt[], + * attrGroups?: Node\AttributeGroup[], + * } $subNodes Array of the following optional subnodes: + * 'flags' => 0 : Flags + * 'extends' => null : Name of extended class + * 'implements' => array(): Names of implemented interfaces + * 'stmts' => array(): Statements + * 'attrGroups' => array(): PHP attribute groups + * @param array $attributes Additional attributes */ public function __construct($name, array $subNodes = [], array $attributes = []) { @@ -15500,84 +14796,36 @@ class Class_ extends ClassLike $this->stmts = $subNodes['stmts'] ?? []; $this->attrGroups = $subNodes['attrGroups'] ?? []; } - public function getSubNodeNames() : array + public function getSubNodeNames(): array { return ['attrGroups', 'flags', 'name', 'extends', 'implements', 'stmts']; } /** * Whether the class is explicitly abstract. - * - * @return bool */ - public function isAbstract() : bool + public function isAbstract(): bool { - return (bool) ($this->flags & self::MODIFIER_ABSTRACT); + return (bool) ($this->flags & Modifiers::ABSTRACT); } /** * Whether the class is final. - * - * @return bool */ - public function isFinal() : bool + public function isFinal(): bool { - return (bool) ($this->flags & self::MODIFIER_FINAL); + return (bool) ($this->flags & Modifiers::FINAL); } - public function isReadonly() : bool + public function isReadonly(): bool { - return (bool) ($this->flags & self::MODIFIER_READONLY); + return (bool) ($this->flags & Modifiers::READONLY); } /** * Whether the class is anonymous. - * - * @return bool */ - public function isAnonymous() : bool + public function isAnonymous(): bool { return null === $this->name; } - /** - * @internal - */ - public static function verifyClassModifier($a, $b) - { - if ($a & self::MODIFIER_ABSTRACT && $b & self::MODIFIER_ABSTRACT) { - throw new Error('Multiple abstract modifiers are not allowed'); - } - if ($a & self::MODIFIER_FINAL && $b & self::MODIFIER_FINAL) { - throw new Error('Multiple final modifiers are not allowed'); - } - if ($a & self::MODIFIER_READONLY && $b & self::MODIFIER_READONLY) { - throw new Error('Multiple readonly modifiers are not allowed'); - } - if ($a & 48 && $b & 48) { - throw new Error('Cannot use the final modifier on an abstract class'); - } - } - /** - * @internal - */ - public static function verifyModifier($a, $b) - { - if ($a & self::VISIBILITY_MODIFIER_MASK && $b & self::VISIBILITY_MODIFIER_MASK) { - throw new Error('Multiple access type modifiers are not allowed'); - } - if ($a & self::MODIFIER_ABSTRACT && $b & self::MODIFIER_ABSTRACT) { - throw new Error('Multiple abstract modifiers are not allowed'); - } - if ($a & self::MODIFIER_STATIC && $b & self::MODIFIER_STATIC) { - throw new Error('Multiple static modifiers are not allowed'); - } - if ($a & self::MODIFIER_FINAL && $b & self::MODIFIER_FINAL) { - throw new Error('Multiple final modifiers are not allowed'); - } - if ($a & self::MODIFIER_READONLY && $b & self::MODIFIER_READONLY) { - throw new Error('Multiple readonly modifiers are not allowed'); - } - if ($a & 48 && $b & 48) { - throw new Error('Cannot use the final modifier on an abstract class member'); - } - } - public function getType() : string + public function getType(): string { return 'Stmt_Class'; } @@ -15591,23 +14839,23 @@ use PHPUnitPHAR\PhpParser\Node; class Const_ extends Node\Stmt { /** @var Node\Const_[] Constant declarations */ - public $consts; + public array $consts; /** * Constructs a const list node. * - * @param Node\Const_[] $consts Constant declarations - * @param array $attributes Additional attributes + * @param Node\Const_[] $consts Constant declarations + * @param array $attributes Additional attributes */ public function __construct(array $consts, array $attributes = []) { $this->attributes = $attributes; $this->consts = $consts; } - public function getSubNodeNames() : array + public function getSubNodeNames(): array { return ['consts']; } - public function getType() : string + public function getType(): string { return 'Stmt_Const'; } @@ -15621,23 +14869,23 @@ use PHPUnitPHAR\PhpParser\Node; class Continue_ extends Node\Stmt { /** @var null|Node\Expr Number of loops to continue */ - public $num; + public ?Node\Expr $num; /** * Constructs a continue node. * - * @param null|Node\Expr $num Number of loops to continue - * @param array $attributes Additional attributes + * @param null|Node\Expr $num Number of loops to continue + * @param array $attributes Additional attributes */ public function __construct(?Node\Expr $num = null, array $attributes = []) { $this->attributes = $attributes; $this->num = $num; } - public function getSubNodeNames() : array + public function getSubNodeNames(): array { return ['num']; } - public function getType() : string + public function getType(): string { return 'Stmt_Continue'; } @@ -15645,55 +14893,28 @@ class Continue_ extends Node\Stmt value pair node. - * - * @param string|Node\Identifier $key Key - * @param Node\Expr $value Value - * @param array $attributes Additional attributes - */ - public function __construct($key, Node\Expr $value, array $attributes = []) - { - $this->attributes = $attributes; - $this->key = \is_string($key) ? new Node\Identifier($key) : $key; - $this->value = $value; - } - public function getSubNodeNames() : array - { - return ['key', 'value']; - } - public function getType() : string - { - return 'Stmt_DeclareDeclare'; - } -} +require __DIR__ . '/../DeclareItem.php'; $attributes Additional attributes */ public function __construct(array $declares, ?array $stmts = null, array $attributes = []) { @@ -15701,11 +14922,11 @@ class Declare_ extends Node\Stmt $this->declares = $declares; $this->stmts = $stmts; } - public function getSubNodeNames() : array + public function getSubNodeNames(): array { return ['declares', 'stmts']; } - public function getType() : string + public function getType(): string { return 'Stmt_Declare'; } @@ -15719,15 +14940,15 @@ use PHPUnitPHAR\PhpParser\Node; class Do_ extends Node\Stmt { /** @var Node\Stmt[] Statements */ - public $stmts; + public array $stmts; /** @var Node\Expr Condition */ - public $cond; + public Node\Expr $cond; /** * Constructs a do while node. * - * @param Node\Expr $cond Condition - * @param Node\Stmt[] $stmts Statements - * @param array $attributes Additional attributes + * @param Node\Expr $cond Condition + * @param Node\Stmt[] $stmts Statements + * @param array $attributes Additional attributes */ public function __construct(Node\Expr $cond, array $stmts = [], array $attributes = []) { @@ -15735,11 +14956,11 @@ class Do_ extends Node\Stmt $this->cond = $cond; $this->stmts = $stmts; } - public function getSubNodeNames() : array + public function getSubNodeNames(): array { return ['stmts', 'cond']; } - public function getType() : string + public function getType(): string { return 'Stmt_Do'; } @@ -15753,23 +14974,23 @@ use PHPUnitPHAR\PhpParser\Node; class Echo_ extends Node\Stmt { /** @var Node\Expr[] Expressions */ - public $exprs; + public array $exprs; /** * Constructs an echo node. * - * @param Node\Expr[] $exprs Expressions - * @param array $attributes Additional attributes + * @param Node\Expr[] $exprs Expressions + * @param array $attributes Additional attributes */ public function __construct(array $exprs, array $attributes = []) { $this->attributes = $attributes; $this->exprs = $exprs; } - public function getSubNodeNames() : array + public function getSubNodeNames(): array { return ['exprs']; } - public function getType() : string + public function getType(): string { return 'Stmt_Echo'; } @@ -15783,15 +15004,15 @@ use PHPUnitPHAR\PhpParser\Node; class ElseIf_ extends Node\Stmt { /** @var Node\Expr Condition */ - public $cond; + public Node\Expr $cond; /** @var Node\Stmt[] Statements */ - public $stmts; + public array $stmts; /** * Constructs an elseif node. * - * @param Node\Expr $cond Condition - * @param Node\Stmt[] $stmts Statements - * @param array $attributes Additional attributes + * @param Node\Expr $cond Condition + * @param Node\Stmt[] $stmts Statements + * @param array $attributes Additional attributes */ public function __construct(Node\Expr $cond, array $stmts = [], array $attributes = []) { @@ -15799,11 +15020,11 @@ class ElseIf_ extends Node\Stmt $this->cond = $cond; $this->stmts = $stmts; } - public function getSubNodeNames() : array + public function getSubNodeNames(): array { return ['cond', 'stmts']; } - public function getType() : string + public function getType(): string { return 'Stmt_ElseIf'; } @@ -15817,23 +15038,23 @@ use PHPUnitPHAR\PhpParser\Node; class Else_ extends Node\Stmt { /** @var Node\Stmt[] Statements */ - public $stmts; + public array $stmts; /** * Constructs an else node. * - * @param Node\Stmt[] $stmts Statements - * @param array $attributes Additional attributes + * @param Node\Stmt[] $stmts Statements + * @param array $attributes Additional attributes */ public function __construct(array $stmts = [], array $attributes = []) { $this->attributes = $attributes; $this->stmts = $stmts; } - public function getSubNodeNames() : array + public function getSubNodeNames(): array { return ['stmts']; } - public function getType() : string + public function getType(): string { return 'Stmt_Else'; } @@ -15848,16 +15069,16 @@ use PHPUnitPHAR\PhpParser\Node\AttributeGroup; class EnumCase extends Node\Stmt { /** @var Node\Identifier Enum case name */ - public $name; + public Node\Identifier $name; /** @var Node\Expr|null Enum case expression */ - public $expr; + public ?Node\Expr $expr; /** @var Node\AttributeGroup[] PHP attribute groups */ - public $attrGroups; + public array $attrGroups; /** - * @param string|Node\Identifier $name Enum case name - * @param Node\Expr|null $expr Enum case expression - * @param AttributeGroup[] $attrGroups PHP attribute groups - * @param array $attributes Additional attributes + * @param string|Node\Identifier $name Enum case name + * @param Node\Expr|null $expr Enum case expression + * @param list $attrGroups PHP attribute groups + * @param array $attributes Additional attributes */ public function __construct($name, ?Node\Expr $expr = null, array $attrGroups = [], array $attributes = []) { @@ -15866,11 +15087,11 @@ class EnumCase extends Node\Stmt $this->expr = $expr; $this->attrGroups = $attrGroups; } - public function getSubNodeNames() : array + public function getSubNodeNames(): array { return ['attrGroups', 'name', 'expr']; } - public function getType() : string + public function getType(): string { return 'Stmt_EnumCase'; } @@ -15884,17 +15105,22 @@ use PHPUnitPHAR\PhpParser\Node; class Enum_ extends ClassLike { /** @var null|Node\Identifier Scalar Type */ - public $scalarType; + public ?Node $scalarType; /** @var Node\Name[] Names of implemented interfaces */ - public $implements; + public array $implements; /** - * @param string|Node\Identifier|null $name Name - * @param array $subNodes Array of the following optional subnodes: - * 'scalarType' => null : Scalar type - * 'implements' => array() : Names of implemented interfaces - * 'stmts' => array() : Statements - * 'attrGroups' => array() : PHP attribute groups - * @param array $attributes Additional attributes + * @param string|Node\Identifier|null $name Name + * @param array{ + * scalarType?: Node\Identifier|null, + * implements?: Node\Name[], + * stmts?: Node\Stmt[], + * attrGroups?: Node\AttributeGroup[], + * } $subNodes Array of the following optional subnodes: + * 'scalarType' => null : Scalar type + * 'implements' => array() : Names of implemented interfaces + * 'stmts' => array() : Statements + * 'attrGroups' => array() : PHP attribute groups + * @param array $attributes Additional attributes */ public function __construct($name, array $subNodes = [], array $attributes = []) { @@ -15905,11 +15131,11 @@ class Enum_ extends ClassLike $this->attrGroups = $subNodes['attrGroups'] ?? []; parent::__construct($attributes); } - public function getSubNodeNames() : array + public function getSubNodeNames(): array { return ['attrGroups', 'name', 'scalarType', 'implements', 'stmts']; } - public function getType() : string + public function getType(): string { return 'Stmt_Enum'; } @@ -15926,23 +15152,23 @@ use PHPUnitPHAR\PhpParser\Node; class Expression extends Node\Stmt { /** @var Node\Expr Expression */ - public $expr; + public Node\Expr $expr; /** * Constructs an expression statement. * - * @param Node\Expr $expr Expression - * @param array $attributes Additional attributes + * @param Node\Expr $expr Expression + * @param array $attributes Additional attributes */ public function __construct(Node\Expr $expr, array $attributes = []) { $this->attributes = $attributes; $this->expr = $expr; } - public function getSubNodeNames() : array + public function getSubNodeNames(): array { return ['expr']; } - public function getType() : string + public function getType(): string { return 'Stmt_Expression'; } @@ -15956,23 +15182,23 @@ use PHPUnitPHAR\PhpParser\Node; class Finally_ extends Node\Stmt { /** @var Node\Stmt[] Statements */ - public $stmts; + public array $stmts; /** * Constructs a finally node. * - * @param Node\Stmt[] $stmts Statements - * @param array $attributes Additional attributes + * @param Node\Stmt[] $stmts Statements + * @param array $attributes Additional attributes */ public function __construct(array $stmts = [], array $attributes = []) { $this->attributes = $attributes; $this->stmts = $stmts; } - public function getSubNodeNames() : array + public function getSubNodeNames(): array { return ['stmts']; } - public function getType() : string + public function getType(): string { return 'Stmt_Finally'; } @@ -15986,22 +15212,27 @@ use PHPUnitPHAR\PhpParser\Node; class For_ extends Node\Stmt { /** @var Node\Expr[] Init expressions */ - public $init; + public array $init; /** @var Node\Expr[] Loop conditions */ - public $cond; + public array $cond; /** @var Node\Expr[] Loop expressions */ - public $loop; + public array $loop; /** @var Node\Stmt[] Statements */ - public $stmts; + public array $stmts; /** * Constructs a for loop node. * - * @param array $subNodes Array of the following optional subnodes: - * 'init' => array(): Init expressions - * 'cond' => array(): Loop conditions - * 'loop' => array(): Loop expressions - * 'stmts' => array(): Statements - * @param array $attributes Additional attributes + * @param array{ + * init?: Node\Expr[], + * cond?: Node\Expr[], + * loop?: Node\Expr[], + * stmts?: Node\Stmt[], + * } $subNodes Array of the following optional subnodes: + * 'init' => array(): Init expressions + * 'cond' => array(): Loop conditions + * 'loop' => array(): Loop expressions + * 'stmts' => array(): Statements + * @param array $attributes Additional attributes */ public function __construct(array $subNodes = [], array $attributes = []) { @@ -16011,11 +15242,11 @@ class For_ extends Node\Stmt $this->loop = $subNodes['loop'] ?? []; $this->stmts = $subNodes['stmts'] ?? []; } - public function getSubNodeNames() : array + public function getSubNodeNames(): array { return ['init', 'cond', 'loop', 'stmts']; } - public function getType() : string + public function getType(): string { return 'Stmt_For'; } @@ -16029,25 +15260,29 @@ use PHPUnitPHAR\PhpParser\Node; class Foreach_ extends Node\Stmt { /** @var Node\Expr Expression to iterate */ - public $expr; + public Node\Expr $expr; /** @var null|Node\Expr Variable to assign key to */ - public $keyVar; + public ?Node\Expr $keyVar; /** @var bool Whether to assign value by reference */ - public $byRef; + public bool $byRef; /** @var Node\Expr Variable to assign value to */ - public $valueVar; + public Node\Expr $valueVar; /** @var Node\Stmt[] Statements */ - public $stmts; + public array $stmts; /** * Constructs a foreach node. * - * @param Node\Expr $expr Expression to iterate - * @param Node\Expr $valueVar Variable to assign value to - * @param array $subNodes Array of the following optional subnodes: - * 'keyVar' => null : Variable to assign key to - * 'byRef' => false : Whether to assign value by reference - * 'stmts' => array(): Statements - * @param array $attributes Additional attributes + * @param Node\Expr $expr Expression to iterate + * @param Node\Expr $valueVar Variable to assign value to + * @param array{ + * keyVar?: Node\Expr|null, + * byRef?: bool, + * stmts?: Node\Stmt[], + * } $subNodes Array of the following optional subnodes: + * 'keyVar' => null : Variable to assign key to + * 'byRef' => false : Whether to assign value by reference + * 'stmts' => array(): Statements + * @param array $attributes Additional attributes */ public function __construct(Node\Expr $expr, Node\Expr $valueVar, array $subNodes = [], array $attributes = []) { @@ -16058,11 +15293,11 @@ class Foreach_ extends Node\Stmt $this->valueVar = $valueVar; $this->stmts = $subNodes['stmts'] ?? []; } - public function getSubNodeNames() : array + public function getSubNodeNames(): array { return ['expr', 'keyVar', 'byRef', 'valueVar', 'stmts']; } - public function getType() : string + public function getType(): string { return 'Stmt_Foreach'; } @@ -16077,30 +15312,36 @@ use PHPUnitPHAR\PhpParser\Node\FunctionLike; class Function_ extends Node\Stmt implements FunctionLike { /** @var bool Whether function returns by reference */ - public $byRef; + public bool $byRef; /** @var Node\Identifier Name */ - public $name; + public Node\Identifier $name; /** @var Node\Param[] Parameters */ - public $params; + public array $params; /** @var null|Node\Identifier|Node\Name|Node\ComplexType Return type */ - public $returnType; + public ?Node $returnType; /** @var Node\Stmt[] Statements */ - public $stmts; + public array $stmts; /** @var Node\AttributeGroup[] PHP attribute groups */ - public $attrGroups; + public array $attrGroups; /** @var Node\Name|null Namespaced name (if using NameResolver) */ - public $namespacedName; + public ?Node\Name $namespacedName; /** * Constructs a function node. * * @param string|Node\Identifier $name Name - * @param array $subNodes Array of the following optional subnodes: - * 'byRef' => false : Whether to return by reference - * 'params' => array(): Parameters - * 'returnType' => null : Return type - * 'stmts' => array(): Statements - * 'attrGroups' => array(): PHP attribute groups - * @param array $attributes Additional attributes + * @param array{ + * byRef?: bool, + * params?: Node\Param[], + * returnType?: null|Node\Identifier|Node\Name|Node\ComplexType, + * stmts?: Node\Stmt[], + * attrGroups?: Node\AttributeGroup[], + * } $subNodes Array of the following optional subnodes: + * 'byRef' => false : Whether to return by reference + * 'params' => array(): Parameters + * 'returnType' => null : Return type + * 'stmts' => array(): Statements + * 'attrGroups' => array(): PHP attribute groups + * @param array $attributes Additional attributes */ public function __construct($name, array $subNodes = [], array $attributes = []) { @@ -16108,20 +15349,19 @@ class Function_ extends Node\Stmt implements FunctionLike $this->byRef = $subNodes['byRef'] ?? \false; $this->name = \is_string($name) ? new Node\Identifier($name) : $name; $this->params = $subNodes['params'] ?? []; - $returnType = $subNodes['returnType'] ?? null; - $this->returnType = \is_string($returnType) ? new Node\Identifier($returnType) : $returnType; + $this->returnType = $subNodes['returnType'] ?? null; $this->stmts = $subNodes['stmts'] ?? []; $this->attrGroups = $subNodes['attrGroups'] ?? []; } - public function getSubNodeNames() : array + public function getSubNodeNames(): array { return ['attrGroups', 'byRef', 'name', 'params', 'returnType', 'stmts']; } - public function returnsByRef() : bool + public function returnsByRef(): bool { return $this->byRef; } - public function getParams() : array + public function getParams(): array { return $this->params; } @@ -16129,16 +15369,16 @@ class Function_ extends Node\Stmt implements FunctionLike { return $this->returnType; } - public function getAttrGroups() : array + public function getAttrGroups(): array { return $this->attrGroups; } /** @return Node\Stmt[] */ - public function getStmts() : array + public function getStmts(): array { return $this->stmts; } - public function getType() : string + public function getType(): string { return 'Stmt_Function'; } @@ -16152,23 +15392,23 @@ use PHPUnitPHAR\PhpParser\Node; class Global_ extends Node\Stmt { /** @var Node\Expr[] Variables */ - public $vars; + public array $vars; /** * Constructs a global variables list node. * - * @param Node\Expr[] $vars Variables to unset - * @param array $attributes Additional attributes + * @param Node\Expr[] $vars Variables to unset + * @param array $attributes Additional attributes */ public function __construct(array $vars, array $attributes = []) { $this->attributes = $attributes; $this->vars = $vars; } - public function getSubNodeNames() : array + public function getSubNodeNames(): array { return ['vars']; } - public function getType() : string + public function getType(): string { return 'Stmt_Global'; } @@ -16183,23 +15423,23 @@ use PHPUnitPHAR\PhpParser\Node\Stmt; class Goto_ extends Stmt { /** @var Identifier Name of label to jump to */ - public $name; + public Identifier $name; /** * Constructs a goto node. * - * @param string|Identifier $name Name of label to jump to - * @param array $attributes Additional attributes + * @param string|Identifier $name Name of label to jump to + * @param array $attributes Additional attributes */ public function __construct($name, array $attributes = []) { $this->attributes = $attributes; $this->name = \is_string($name) ? new Identifier($name) : $name; } - public function getSubNodeNames() : array + public function getSubNodeNames(): array { return ['name']; } - public function getType() : string + public function getType(): string { return 'Stmt_Goto'; } @@ -16211,21 +15451,24 @@ namespace PHPUnitPHAR\PhpParser\Node\Stmt; use PHPUnitPHAR\PhpParser\Node\Name; use PHPUnitPHAR\PhpParser\Node\Stmt; +use PHPUnitPHAR\PhpParser\Node\UseItem; class GroupUse extends Stmt { - /** @var int Type of group use */ - public $type; + /** + * @var Use_::TYPE_* Type of group use + */ + public int $type; /** @var Name Prefix for uses */ - public $prefix; - /** @var UseUse[] Uses */ - public $uses; + public Name $prefix; + /** @var UseItem[] Uses */ + public array $uses; /** * Constructs a group use node. * - * @param Name $prefix Prefix for uses - * @param UseUse[] $uses Uses - * @param int $type Type of group use - * @param array $attributes Additional attributes + * @param Name $prefix Prefix for uses + * @param UseItem[] $uses Uses + * @param Use_::TYPE_* $type Type of group use + * @param array $attributes Additional attributes */ public function __construct(Name $prefix, array $uses, int $type = Use_::TYPE_NORMAL, array $attributes = []) { @@ -16234,11 +15477,11 @@ class GroupUse extends Stmt $this->prefix = $prefix; $this->uses = $uses; } - public function getSubNodeNames() : array + public function getSubNodeNames(): array { return ['type', 'prefix', 'uses']; } - public function getType() : string + public function getType(): string { return 'Stmt_GroupUse'; } @@ -16252,23 +15495,23 @@ use PHPUnitPHAR\PhpParser\Node\Stmt; class HaltCompiler extends Stmt { /** @var string Remaining text after halt compiler statement. */ - public $remaining; + public string $remaining; /** * Constructs a __halt_compiler node. * - * @param string $remaining Remaining text after halt compiler statement. - * @param array $attributes Additional attributes + * @param string $remaining Remaining text after halt compiler statement. + * @param array $attributes Additional attributes */ public function __construct(string $remaining, array $attributes = []) { $this->attributes = $attributes; $this->remaining = $remaining; } - public function getSubNodeNames() : array + public function getSubNodeNames(): array { return ['remaining']; } - public function getType() : string + public function getType(): string { return 'Stmt_HaltCompiler'; } @@ -16282,22 +15525,26 @@ use PHPUnitPHAR\PhpParser\Node; class If_ extends Node\Stmt { /** @var Node\Expr Condition expression */ - public $cond; + public Node\Expr $cond; /** @var Node\Stmt[] Statements */ - public $stmts; + public array $stmts; /** @var ElseIf_[] Elseif clauses */ - public $elseifs; + public array $elseifs; /** @var null|Else_ Else clause */ - public $else; + public ?Else_ $else; /** * Constructs an if node. * - * @param Node\Expr $cond Condition - * @param array $subNodes Array of the following optional subnodes: - * 'stmts' => array(): Statements - * 'elseifs' => array(): Elseif clauses - * 'else' => null : Else clause - * @param array $attributes Additional attributes + * @param Node\Expr $cond Condition + * @param array{ + * stmts?: Node\Stmt[], + * elseifs?: ElseIf_[], + * else?: Else_|null, + * } $subNodes Array of the following optional subnodes: + * 'stmts' => array(): Statements + * 'elseifs' => array(): Elseif clauses + * 'else' => null : Else clause + * @param array $attributes Additional attributes */ public function __construct(Node\Expr $cond, array $subNodes = [], array $attributes = []) { @@ -16307,11 +15554,11 @@ class If_ extends Node\Stmt $this->elseifs = $subNodes['elseifs'] ?? []; $this->else = $subNodes['else'] ?? null; } - public function getSubNodeNames() : array + public function getSubNodeNames(): array { return ['cond', 'stmts', 'elseifs', 'else']; } - public function getType() : string + public function getType(): string { return 'Stmt_If'; } @@ -16325,23 +15572,23 @@ use PHPUnitPHAR\PhpParser\Node\Stmt; class InlineHTML extends Stmt { /** @var string String */ - public $value; + public string $value; /** * Constructs an inline HTML node. * - * @param string $value String - * @param array $attributes Additional attributes + * @param string $value String + * @param array $attributes Additional attributes */ public function __construct(string $value, array $attributes = []) { $this->attributes = $attributes; $this->value = $value; } - public function getSubNodeNames() : array + public function getSubNodeNames(): array { return ['value']; } - public function getType() : string + public function getType(): string { return 'Stmt_InlineHTML'; } @@ -16355,16 +15602,20 @@ use PHPUnitPHAR\PhpParser\Node; class Interface_ extends ClassLike { /** @var Node\Name[] Extended interfaces */ - public $extends; + public array $extends; /** * Constructs a class node. * * @param string|Node\Identifier $name Name - * @param array $subNodes Array of the following optional subnodes: - * 'extends' => array(): Name of extended interfaces - * 'stmts' => array(): Statements - * 'attrGroups' => array(): PHP attribute groups - * @param array $attributes Additional attributes + * @param array{ + * extends?: Node\Name[], + * stmts?: Node\Stmt[], + * attrGroups?: Node\AttributeGroup[], + * } $subNodes Array of the following optional subnodes: + * 'extends' => array(): Name of extended interfaces + * 'stmts' => array(): Statements + * 'attrGroups' => array(): PHP attribute groups + * @param array $attributes Additional attributes */ public function __construct($name, array $subNodes = [], array $attributes = []) { @@ -16374,11 +15625,11 @@ class Interface_ extends ClassLike $this->stmts = $subNodes['stmts'] ?? []; $this->attrGroups = $subNodes['attrGroups'] ?? []; } - public function getSubNodeNames() : array + public function getSubNodeNames(): array { return ['attrGroups', 'name', 'extends', 'stmts']; } - public function getType() : string + public function getType(): string { return 'Stmt_Interface'; } @@ -16393,23 +15644,23 @@ use PHPUnitPHAR\PhpParser\Node\Stmt; class Label extends Stmt { /** @var Identifier Name */ - public $name; + public Identifier $name; /** * Constructs a label node. * - * @param string|Identifier $name Name - * @param array $attributes Additional attributes + * @param string|Identifier $name Name + * @param array $attributes Additional attributes */ public function __construct($name, array $attributes = []) { $this->attributes = $attributes; $this->name = \is_string($name) ? new Identifier($name) : $name; } - public function getSubNodeNames() : array + public function getSubNodeNames(): array { return ['name']; } - public function getType() : string + public function getType(): string { return 'Stmt_Label'; } @@ -16423,30 +15674,30 @@ use PHPUnitPHAR\PhpParser\Node; class Namespace_ extends Node\Stmt { /* For use in the "kind" attribute */ - const KIND_SEMICOLON = 1; - const KIND_BRACED = 2; + public const KIND_SEMICOLON = 1; + public const KIND_BRACED = 2; /** @var null|Node\Name Name */ - public $name; + public ?Node\Name $name; /** @var Node\Stmt[] Statements */ public $stmts; /** * Constructs a namespace node. * - * @param null|Node\Name $name Name - * @param null|Node\Stmt[] $stmts Statements - * @param array $attributes Additional attributes + * @param null|Node\Name $name Name + * @param null|Node\Stmt[] $stmts Statements + * @param array $attributes Additional attributes */ - public function __construct(?Node\Name $name = null, $stmts = [], array $attributes = []) + public function __construct(?Node\Name $name = null, ?array $stmts = [], array $attributes = []) { $this->attributes = $attributes; $this->name = $name; $this->stmts = $stmts; } - public function getSubNodeNames() : array + public function getSubNodeNames(): array { return ['name', 'stmts']; } - public function getType() : string + public function getType(): string { return 'Stmt_Namespace'; } @@ -16460,11 +15711,11 @@ use PHPUnitPHAR\PhpParser\Node; /** Nop/empty statement (;). */ class Nop extends Node\Stmt { - public function getSubNodeNames() : array + public function getSubNodeNames(): array { return []; } - public function getType() : string + public function getType(): string { return 'Stmt_Nop'; } @@ -16474,87 +15725,79 @@ class Nop extends Node\Stmt declare (strict_types=1); namespace PHPUnitPHAR\PhpParser\Node\Stmt; +use PHPUnitPHAR\PhpParser\Modifiers; use PHPUnitPHAR\PhpParser\Node; use PHPUnitPHAR\PhpParser\Node\ComplexType; use PHPUnitPHAR\PhpParser\Node\Identifier; use PHPUnitPHAR\PhpParser\Node\Name; +use PHPUnitPHAR\PhpParser\Node\PropertyItem; class Property extends Node\Stmt { /** @var int Modifiers */ - public $flags; - /** @var PropertyProperty[] Properties */ - public $props; + public int $flags; + /** @var PropertyItem[] Properties */ + public array $props; /** @var null|Identifier|Name|ComplexType Type declaration */ - public $type; + public ?Node $type; /** @var Node\AttributeGroup[] PHP attribute groups */ - public $attrGroups; + public array $attrGroups; /** * Constructs a class property list node. * - * @param int $flags Modifiers - * @param PropertyProperty[] $props Properties - * @param array $attributes Additional attributes - * @param null|string|Identifier|Name|ComplexType $type Type declaration - * @param Node\AttributeGroup[] $attrGroups PHP attribute groups + * @param int $flags Modifiers + * @param PropertyItem[] $props Properties + * @param array $attributes Additional attributes + * @param null|Identifier|Name|ComplexType $type Type declaration + * @param Node\AttributeGroup[] $attrGroups PHP attribute groups */ - public function __construct(int $flags, array $props, array $attributes = [], $type = null, array $attrGroups = []) + public function __construct(int $flags, array $props, array $attributes = [], ?Node $type = null, array $attrGroups = []) { $this->attributes = $attributes; $this->flags = $flags; $this->props = $props; - $this->type = \is_string($type) ? new Identifier($type) : $type; + $this->type = $type; $this->attrGroups = $attrGroups; } - public function getSubNodeNames() : array + public function getSubNodeNames(): array { return ['attrGroups', 'flags', 'type', 'props']; } /** * Whether the property is explicitly or implicitly public. - * - * @return bool */ - public function isPublic() : bool + public function isPublic(): bool { - return ($this->flags & Class_::MODIFIER_PUBLIC) !== 0 || ($this->flags & Class_::VISIBILITY_MODIFIER_MASK) === 0; + return ($this->flags & Modifiers::PUBLIC) !== 0 || ($this->flags & Modifiers::VISIBILITY_MASK) === 0; } /** * Whether the property is protected. - * - * @return bool */ - public function isProtected() : bool + public function isProtected(): bool { - return (bool) ($this->flags & Class_::MODIFIER_PROTECTED); + return (bool) ($this->flags & Modifiers::PROTECTED); } /** * Whether the property is private. - * - * @return bool */ - public function isPrivate() : bool + public function isPrivate(): bool { - return (bool) ($this->flags & Class_::MODIFIER_PRIVATE); + return (bool) ($this->flags & Modifiers::PRIVATE); } /** * Whether the property is static. - * - * @return bool */ - public function isStatic() : bool + public function isStatic(): bool { - return (bool) ($this->flags & Class_::MODIFIER_STATIC); + return (bool) ($this->flags & Modifiers::STATIC); } /** * Whether the property is readonly. - * - * @return bool */ - public function isReadonly() : bool + public function isReadonly(): bool { - return (bool) ($this->flags & Class_::MODIFIER_READONLY); + return (bool) ($this->flags & Modifiers::READONLY); } - public function getType() : string + public function getType(): string { return 'Stmt_Property'; } @@ -16562,37 +15805,9 @@ class Property extends Node\Stmt attributes = $attributes; - $this->name = \is_string($name) ? new Node\VarLikeIdentifier($name) : $name; - $this->default = $default; - } - public function getSubNodeNames() : array - { - return ['name', 'default']; - } - public function getType() : string - { - return 'Stmt_PropertyProperty'; - } -} +require __DIR__ . '/../PropertyItem.php'; $attributes Additional attributes */ public function __construct(?Node\Expr $expr = null, array $attributes = []) { $this->attributes = $attributes; $this->expr = $expr; } - public function getSubNodeNames() : array + public function getSubNodeNames(): array { return ['expr']; } - public function getType() : string + public function getType(): string { return 'Stmt_Return'; } @@ -16626,64 +15841,36 @@ class Return_ extends Node\Stmt attributes = $attributes; - $this->var = $var; - $this->default = $default; - } - public function getSubNodeNames() : array - { - return ['var', 'default']; - } - public function getType() : string - { - return 'Stmt_StaticVar'; - } -} +require __DIR__ . '/../StaticVar.php'; $attributes Additional attributes */ public function __construct(array $vars, array $attributes = []) { $this->attributes = $attributes; $this->vars = $vars; } - public function getSubNodeNames() : array + public function getSubNodeNames(): array { return ['vars']; } - public function getType() : string + public function getType(): string { return 'Stmt_Static'; } @@ -16697,15 +15884,15 @@ use PHPUnitPHAR\PhpParser\Node; class Switch_ extends Node\Stmt { /** @var Node\Expr Condition */ - public $cond; + public Node\Expr $cond; /** @var Case_[] Case list */ - public $cases; + public array $cases; /** * Constructs a case node. * - * @param Node\Expr $cond Condition - * @param Case_[] $cases Case list - * @param array $attributes Additional attributes + * @param Node\Expr $cond Condition + * @param Case_[] $cases Case list + * @param array $attributes Additional attributes */ public function __construct(Node\Expr $cond, array $cases, array $attributes = []) { @@ -16713,11 +15900,11 @@ class Switch_ extends Node\Stmt $this->cond = $cond; $this->cases = $cases; } - public function getSubNodeNames() : array + public function getSubNodeNames(): array { return ['cond', 'cases']; } - public function getType() : string + public function getType(): string { return 'Stmt_Switch'; } @@ -16727,49 +15914,19 @@ class Switch_ extends Node\Stmt declare (strict_types=1); namespace PHPUnitPHAR\PhpParser\Node\Stmt; -use PHPUnitPHAR\PhpParser\Node; -class Throw_ extends Node\Stmt -{ - /** @var Node\Expr Expression */ - public $expr; - /** - * Constructs a legacy throw statement node. - * - * @param Node\Expr $expr Expression - * @param array $attributes Additional attributes - */ - public function __construct(Node\Expr $expr, array $attributes = []) - { - $this->attributes = $attributes; - $this->expr = $expr; - } - public function getSubNodeNames() : array - { - return ['expr']; - } - public function getType() : string - { - return 'Stmt_Throw'; - } -} - $attributes Additional attributes */ public function __construct(array $traits, array $adaptations = [], array $attributes = []) { @@ -16777,11 +15934,11 @@ class TraitUse extends Node\Stmt $this->traits = $traits; $this->adaptations = $adaptations; } - public function getSubNodeNames() : array + public function getSubNodeNames(): array { return ['traits', 'adaptations']; } - public function getType() : string + public function getType(): string { return 'Stmt_TraitUse'; } @@ -16795,9 +15952,9 @@ use PHPUnitPHAR\PhpParser\Node; abstract class TraitUseAdaptation extends Node\Stmt { /** @var Node\Name|null Trait name */ - public $trait; + public ?Node\Name $trait; /** @var Node\Identifier Method name */ - public $method; + public Node\Identifier $method; } $attributes Additional attributes */ - public function __construct($trait, $method, $newModifier, $newName, array $attributes = []) + public function __construct(?Node\Name $trait, $method, ?int $newModifier, $newName, array $attributes = []) { $this->attributes = $attributes; $this->trait = $trait; @@ -16828,11 +15985,11 @@ class Alias extends Node\Stmt\TraitUseAdaptation $this->newModifier = $newModifier; $this->newName = \is_string($newName) ? new Node\Identifier($newName) : $newName; } - public function getSubNodeNames() : array + public function getSubNodeNames(): array { return ['trait', 'method', 'newModifier', 'newName']; } - public function getType() : string + public function getType(): string { return 'Stmt_TraitUseAdaptation_Alias'; } @@ -16846,14 +16003,14 @@ use PHPUnitPHAR\PhpParser\Node; class Precedence extends Node\Stmt\TraitUseAdaptation { /** @var Node\Name[] Overwritten traits */ - public $insteadof; + public array $insteadof; /** * Constructs a trait use precedence adaptation node. * - * @param Node\Name $trait Trait name - * @param string|Node\Identifier $method Method name - * @param Node\Name[] $insteadof Overwritten traits - * @param array $attributes Additional attributes + * @param Node\Name $trait Trait name + * @param string|Node\Identifier $method Method name + * @param Node\Name[] $insteadof Overwritten traits + * @param array $attributes Additional attributes */ public function __construct(Node\Name $trait, $method, array $insteadof, array $attributes = []) { @@ -16862,11 +16019,11 @@ class Precedence extends Node\Stmt\TraitUseAdaptation $this->method = \is_string($method) ? new Node\Identifier($method) : $method; $this->insteadof = $insteadof; } - public function getSubNodeNames() : array + public function getSubNodeNames(): array { return ['trait', 'method', 'insteadof']; } - public function getType() : string + public function getType(): string { return 'Stmt_TraitUseAdaptation_Precedence'; } @@ -16883,10 +16040,13 @@ class Trait_ extends ClassLike * Constructs a trait node. * * @param string|Node\Identifier $name Name - * @param array $subNodes Array of the following optional subnodes: - * 'stmts' => array(): Statements - * 'attrGroups' => array(): PHP attribute groups - * @param array $attributes Additional attributes + * @param array{ + * stmts?: Node\Stmt[], + * attrGroups?: Node\AttributeGroup[], + * } $subNodes Array of the following optional subnodes: + * 'stmts' => array(): Statements + * 'attrGroups' => array(): PHP attribute groups + * @param array $attributes Additional attributes */ public function __construct($name, array $subNodes = [], array $attributes = []) { @@ -16895,11 +16055,11 @@ class Trait_ extends ClassLike $this->stmts = $subNodes['stmts'] ?? []; $this->attrGroups = $subNodes['attrGroups'] ?? []; } - public function getSubNodeNames() : array + public function getSubNodeNames(): array { return ['attrGroups', 'name', 'stmts']; } - public function getType() : string + public function getType(): string { return 'Stmt_Trait'; } @@ -16913,18 +16073,18 @@ use PHPUnitPHAR\PhpParser\Node; class TryCatch extends Node\Stmt { /** @var Node\Stmt[] Statements */ - public $stmts; + public array $stmts; /** @var Catch_[] Catches */ - public $catches; + public array $catches; /** @var null|Finally_ Optional finally node */ - public $finally; + public ?Finally_ $finally; /** * Constructs a try catch node. * - * @param Node\Stmt[] $stmts Statements - * @param Catch_[] $catches Catches - * @param null|Finally_ $finally Optional finally node - * @param array $attributes Additional attributes + * @param Node\Stmt[] $stmts Statements + * @param Catch_[] $catches Catches + * @param null|Finally_ $finally Optional finally node + * @param array $attributes Additional attributes */ public function __construct(array $stmts, array $catches, ?Finally_ $finally = null, array $attributes = []) { @@ -16933,11 +16093,11 @@ class TryCatch extends Node\Stmt $this->catches = $catches; $this->finally = $finally; } - public function getSubNodeNames() : array + public function getSubNodeNames(): array { return ['stmts', 'catches', 'finally']; } - public function getType() : string + public function getType(): string { return 'Stmt_TryCatch'; } @@ -16951,23 +16111,23 @@ use PHPUnitPHAR\PhpParser\Node; class Unset_ extends Node\Stmt { /** @var Node\Expr[] Variables to unset */ - public $vars; + public array $vars; /** * Constructs an unset node. * - * @param Node\Expr[] $vars Variables to unset - * @param array $attributes Additional attributes + * @param Node\Expr[] $vars Variables to unset + * @param array $attributes Additional attributes */ public function __construct(array $vars, array $attributes = []) { $this->attributes = $attributes; $this->vars = $vars; } - public function getSubNodeNames() : array + public function getSubNodeNames(): array { return ['vars']; } - public function getType() : string + public function getType(): string { return 'Stmt_Unset'; } @@ -16975,60 +16135,16 @@ class Unset_ extends Node\Stmt attributes = $attributes; - $this->type = $type; - $this->name = $name; - $this->alias = \is_string($alias) ? new Identifier($alias) : $alias; - } - public function getSubNodeNames() : array - { - return ['type', 'name', 'alias']; - } - /** - * Get alias. If not explicitly given this is the last component of the used name. - * - * @return Identifier - */ - public function getAlias() : Identifier - { - if (null !== $this->alias) { - return $this->alias; - } - return new Identifier($this->name->getLast()); - } - public function getType() : string - { - return 'Stmt_UseUse'; - } -} +require __DIR__ . '/../UseItem.php'; $attributes Additional attributes */ public function __construct(array $uses, int $type = self::TYPE_NORMAL, array $attributes = []) { @@ -17060,11 +16176,11 @@ class Use_ extends Stmt $this->type = $type; $this->uses = $uses; } - public function getSubNodeNames() : array + public function getSubNodeNames(): array { return ['type', 'uses']; } - public function getType() : string + public function getType(): string { return 'Stmt_Use'; } @@ -17078,15 +16194,15 @@ use PHPUnitPHAR\PhpParser\Node; class While_ extends Node\Stmt { /** @var Node\Expr Condition */ - public $cond; + public Node\Expr $cond; /** @var Node\Stmt[] Statements */ - public $stmts; + public array $stmts; /** * Constructs a while node. * - * @param Node\Expr $cond Condition - * @param Node\Stmt[] $stmts Statements - * @param array $attributes Additional attributes + * @param Node\Expr $cond Condition + * @param Node\Stmt[] $stmts Statements + * @param array $attributes Additional attributes */ public function __construct(Node\Expr $cond, array $stmts = [], array $attributes = []) { @@ -17094,11 +16210,11 @@ class While_ extends Node\Stmt $this->cond = $cond; $this->stmts = $stmts; } - public function getSubNodeNames() : array + public function getSubNodeNames(): array { return ['cond', 'stmts']; } - public function getType() : string + public function getType(): string { return 'Stmt_While'; } @@ -17111,23 +16227,23 @@ namespace PHPUnitPHAR\PhpParser\Node; class UnionType extends ComplexType { /** @var (Identifier|Name|IntersectionType)[] Types */ - public $types; + public array $types; /** * Constructs a union type. * - * @param (Identifier|Name|IntersectionType)[] $types Types - * @param array $attributes Additional attributes + * @param (Identifier|Name|IntersectionType)[] $types Types + * @param array $attributes Additional attributes */ public function __construct(array $types, array $attributes = []) { $this->attributes = $attributes; $this->types = $types; } - public function getSubNodeNames() : array + public function getSubNodeNames(): array { return ['types']; } - public function getType() : string + public function getType(): string { return 'UnionType'; } @@ -17137,6 +16253,60 @@ class UnionType extends ComplexType declare (strict_types=1); namespace PHPUnitPHAR\PhpParser\Node; +use PHPUnitPHAR\PhpParser\Node; +use PHPUnitPHAR\PhpParser\NodeAbstract; +use PHPUnitPHAR\PhpParser\Node\Stmt\Use_; +class UseItem extends NodeAbstract +{ + /** + * @var Use_::TYPE_* One of the Stmt\Use_::TYPE_* constants. Will only differ from TYPE_UNKNOWN for mixed group uses + */ + public int $type; + /** @var Node\Name Namespace, class, function or constant to alias */ + public Name $name; + /** @var Identifier|null Alias */ + public ?Identifier $alias; + /** + * Constructs an alias (use) item node. + * + * @param Node\Name $name Namespace/Class to alias + * @param null|string|Identifier $alias Alias + * @param Use_::TYPE_* $type Type of the use element (for mixed group use only) + * @param array $attributes Additional attributes + */ + public function __construct(Node\Name $name, $alias = null, int $type = Use_::TYPE_UNKNOWN, array $attributes = []) + { + $this->attributes = $attributes; + $this->type = $type; + $this->name = $name; + $this->alias = \is_string($alias) ? new Identifier($alias) : $alias; + } + public function getSubNodeNames(): array + { + return ['type', 'name', 'alias']; + } + /** + * Get alias. If not explicitly given this is the last component of the used name. + */ + public function getAlias(): Identifier + { + if (null !== $this->alias) { + return $this->alias; + } + return new Identifier($this->name->getLast()); + } + public function getType(): string + { + return 'UseItem'; + } +} +// @deprecated compatibility alias +class_alias(UseItem::class, Stmt\UseUse::class); + $attributes Additional attributes */ public function __construct(array $attributes = []) { $this->attributes = $attributes; } - public function getType() : string + public function getType(): string { return 'VariadicPlaceholder'; } - public function getSubNodeNames() : array + public function getSubNodeNames(): array { return []; } @@ -17187,11 +16357,12 @@ namespace PHPUnitPHAR\PhpParser; abstract class NodeAbstract implements Node, \JsonSerializable { - protected $attributes; + /** @var array Attributes */ + protected array $attributes; /** * Creates a Node. * - * @param array $attributes Array of attributes + * @param array $attributes Array of attributes */ public function __construct(array $attributes = []) { @@ -17201,8 +16372,9 @@ abstract class NodeAbstract implements Node, \JsonSerializable * Gets line the node started in (alias of getStartLine). * * @return int Start line (or -1 if not available) + * @phpstan-return -1|positive-int */ - public function getLine() : int + public function getLine(): int { return $this->attributes['startLine'] ?? -1; } @@ -17212,8 +16384,9 @@ abstract class NodeAbstract implements Node, \JsonSerializable * Requires the 'startLine' attribute to be enabled in the lexer (enabled by default). * * @return int Start line (or -1 if not available) + * @phpstan-return -1|positive-int */ - public function getStartLine() : int + public function getStartLine(): int { return $this->attributes['startLine'] ?? -1; } @@ -17223,8 +16396,9 @@ abstract class NodeAbstract implements Node, \JsonSerializable * Requires the 'endLine' attribute to be enabled in the lexer (enabled by default). * * @return int End line (or -1 if not available) + * @phpstan-return -1|positive-int */ - public function getEndLine() : int + public function getEndLine(): int { return $this->attributes['endLine'] ?? -1; } @@ -17237,7 +16411,7 @@ abstract class NodeAbstract implements Node, \JsonSerializable * * @return int Token start position (or -1 if not available) */ - public function getStartTokenPos() : int + public function getStartTokenPos(): int { return $this->attributes['startTokenPos'] ?? -1; } @@ -17250,7 +16424,7 @@ abstract class NodeAbstract implements Node, \JsonSerializable * * @return int Token end position (or -1 if not available) */ - public function getEndTokenPos() : int + public function getEndTokenPos(): int { return $this->attributes['endTokenPos'] ?? -1; } @@ -17261,7 +16435,7 @@ abstract class NodeAbstract implements Node, \JsonSerializable * * @return int File start position (or -1 if not available) */ - public function getStartFilePos() : int + public function getStartFilePos(): int { return $this->attributes['startFilePos'] ?? -1; } @@ -17272,7 +16446,7 @@ abstract class NodeAbstract implements Node, \JsonSerializable * * @return int File end position (or -1 if not available) */ - public function getEndFilePos() : int + public function getEndFilePos(): int { return $this->attributes['endFilePos'] ?? -1; } @@ -17283,7 +16457,7 @@ abstract class NodeAbstract implements Node, \JsonSerializable * * @return Comment[] */ - public function getComments() : array + public function getComments(): array { return $this->attributes['comments'] ?? []; } @@ -17292,10 +16466,10 @@ abstract class NodeAbstract implements Node, \JsonSerializable * * @return null|Comment\Doc Doc comment object or null */ - public function getDocComment() + public function getDocComment(): ?Comment\Doc { $comments = $this->getComments(); - for ($i = \count($comments) - 1; $i >= 0; $i--) { + for ($i = count($comments) - 1; $i >= 0; $i--) { $comment = $comments[$i]; if ($comment instanceof Comment\Doc) { return $comment; @@ -17310,10 +16484,10 @@ abstract class NodeAbstract implements Node, \JsonSerializable * * @param Comment\Doc $docComment Doc comment to set */ - public function setDocComment(Comment\Doc $docComment) + public function setDocComment(Comment\Doc $docComment): void { $comments = $this->getComments(); - for ($i = \count($comments) - 1; $i >= 0; $i--) { + for ($i = count($comments) - 1; $i >= 0; $i--) { if ($comments[$i] instanceof Comment\Doc) { // Replace existing doc comment. $comments[$i] = $docComment; @@ -17325,35 +16499,35 @@ abstract class NodeAbstract implements Node, \JsonSerializable $comments[] = $docComment; $this->setAttribute('comments', $comments); } - public function setAttribute(string $key, $value) + public function setAttribute(string $key, $value): void { $this->attributes[$key] = $value; } - public function hasAttribute(string $key) : bool + public function hasAttribute(string $key): bool { - return \array_key_exists($key, $this->attributes); + return array_key_exists($key, $this->attributes); } public function getAttribute(string $key, $default = null) { - if (\array_key_exists($key, $this->attributes)) { + if (array_key_exists($key, $this->attributes)) { return $this->attributes[$key]; } return $default; } - public function getAttributes() : array + public function getAttributes(): array { return $this->attributes; } - public function setAttributes(array $attributes) + public function setAttributes(array $attributes): void { $this->attributes = $attributes; } /** - * @return array + * @return array */ - public function jsonSerialize() : array + public function jsonSerialize(): array { - return ['nodeType' => $this->getType()] + \get_object_vars($this); + return ['nodeType' => $this->getType()] + get_object_vars($this); } } \true, 'startLine' => \true, 'endLine' => \true, 'startFilePos' => \true, 'endFilePos' => \true, 'startTokenPos' => \true, 'endTokenPos' => \true]; /** * Constructs a NodeDumper. * @@ -17378,6 +16560,7 @@ class NodeDumper * * bool dumpComments: Whether comments should be dumped. * * bool dumpPositions: Whether line/offset information should be dumped. To dump offset * information, the code needs to be passed to dump(). + * * bool dumpOtherAttributes: Whether non-comment, non-position attributes should be dumped. * * @param array $options Options (see description) */ @@ -17385,124 +16568,178 @@ class NodeDumper { $this->dumpComments = !empty($options['dumpComments']); $this->dumpPositions = !empty($options['dumpPositions']); + $this->dumpOtherAttributes = !empty($options['dumpOtherAttributes']); } /** * Dumps a node or array. * - * @param array|Node $node Node or array to dump + * @param array|Node $node Node or array to dump * @param string|null $code Code corresponding to dumped AST. This only needs to be passed if * the dumpPositions option is enabled and the dumping of node offsets * is desired. * * @return string Dumped value */ - public function dump($node, ?string $code = null) : string + public function dump($node, ?string $code = null): string { $this->code = $code; - return $this->dumpRecursive($node); + $this->res = ''; + $this->nl = "\n"; + $this->dumpRecursive($node, \false); + return $this->res; } - protected function dumpRecursive($node) + /** @param mixed $node */ + protected function dumpRecursive($node, bool $indent = \true): void { + if ($indent) { + $this->nl .= " "; + } if ($node instanceof Node) { - $r = $node->getType(); - if ($this->dumpPositions && null !== ($p = $this->dumpPosition($node))) { - $r .= $p; + $this->res .= $node->getType(); + if ($this->dumpPositions && null !== $p = $this->dumpPosition($node)) { + $this->res .= $p; } - $r .= '('; + $this->res .= '('; foreach ($node->getSubNodeNames() as $key) { - $r .= "\n " . $key . ': '; + $this->res .= "{$this->nl} " . $key . ': '; $value = $node->{$key}; - if (null === $value) { - $r .= 'null'; - } elseif (\false === $value) { - $r .= 'false'; - } elseif (\true === $value) { - $r .= 'true'; - } elseif (\is_scalar($value)) { + if (\is_int($value)) { if ('flags' === $key || 'newModifier' === $key) { - $r .= $this->dumpFlags($value); - } elseif ('type' === $key && $node instanceof Include_) { - $r .= $this->dumpIncludeType($value); - } elseif ('type' === $key && ($node instanceof Use_ || $node instanceof UseUse || $node instanceof GroupUse)) { - $r .= $this->dumpUseType($value); - } else { - $r .= $value; + $this->res .= $this->dumpFlags($value); + continue; + } + if ('type' === $key && $node instanceof Include_) { + $this->res .= $this->dumpIncludeType($value); + continue; + } + if ('type' === $key && ($node instanceof Use_ || $node instanceof UseItem || $node instanceof GroupUse)) { + $this->res .= $this->dumpUseType($value); + continue; } - } else { - $r .= \str_replace("\n", "\n ", $this->dumpRecursive($value)); } + $this->dumpRecursive($value); } - if ($this->dumpComments && ($comments = $node->getComments())) { - $r .= "\n comments: " . \str_replace("\n", "\n ", $this->dumpRecursive($comments)); + if ($this->dumpComments && $comments = $node->getComments()) { + $this->res .= "{$this->nl} comments: "; + $this->dumpRecursive($comments); } + if ($this->dumpOtherAttributes) { + foreach ($node->getAttributes() as $key => $value) { + if (isset(self::IGNORE_ATTRIBUTES[$key])) { + continue; + } + $this->res .= "{$this->nl} {$key}: "; + if (\is_int($value)) { + if ('kind' === $key) { + if ($node instanceof Int_) { + $this->res .= $this->dumpIntKind($value); + continue; + } + if ($node instanceof String_ || $node instanceof InterpolatedString) { + $this->res .= $this->dumpStringKind($value); + continue; + } + if ($node instanceof Array_) { + $this->res .= $this->dumpArrayKind($value); + continue; + } + if ($node instanceof List_) { + $this->res .= $this->dumpListKind($value); + continue; + } + } + } + $this->dumpRecursive($value); + } + } + $this->res .= "{$this->nl})"; } elseif (\is_array($node)) { - $r = 'array('; + $this->res .= 'array('; foreach ($node as $key => $value) { - $r .= "\n " . $key . ': '; - if (null === $value) { - $r .= 'null'; - } elseif (\false === $value) { - $r .= 'false'; - } elseif (\true === $value) { - $r .= 'true'; - } elseif (\is_scalar($value)) { - $r .= $value; - } else { - $r .= \str_replace("\n", "\n ", $this->dumpRecursive($value)); - } + $this->res .= "{$this->nl} " . $key . ': '; + $this->dumpRecursive($value); } + $this->res .= "{$this->nl})"; } elseif ($node instanceof Comment) { - return $node->getReformattedText(); + $this->res .= \str_replace("\n", $this->nl, $node->getReformattedText()); + } elseif (\is_string($node)) { + $this->res .= \str_replace("\n", $this->nl, (string) $node); + } elseif (\is_int($node) || \is_float($node)) { + $this->res .= $node; + } elseif (null === $node) { + $this->res .= 'null'; + } elseif (\false === $node) { + $this->res .= 'false'; + } elseif (\true === $node) { + $this->res .= 'true'; } else { throw new \InvalidArgumentException('Can only dump nodes and arrays.'); } - return $r . "\n)"; + if ($indent) { + $this->nl = \substr($this->nl, 0, -4); + } } - protected function dumpFlags($flags) + protected function dumpFlags(int $flags): string { $strs = []; - if ($flags & Class_::MODIFIER_PUBLIC) { - $strs[] = 'MODIFIER_PUBLIC'; + if ($flags & Modifiers::PUBLIC) { + $strs[] = 'PUBLIC'; } - if ($flags & Class_::MODIFIER_PROTECTED) { - $strs[] = 'MODIFIER_PROTECTED'; + if ($flags & Modifiers::PROTECTED) { + $strs[] = 'PROTECTED'; } - if ($flags & Class_::MODIFIER_PRIVATE) { - $strs[] = 'MODIFIER_PRIVATE'; + if ($flags & Modifiers::PRIVATE) { + $strs[] = 'PRIVATE'; } - if ($flags & Class_::MODIFIER_ABSTRACT) { - $strs[] = 'MODIFIER_ABSTRACT'; + if ($flags & Modifiers::ABSTRACT) { + $strs[] = 'ABSTRACT'; } - if ($flags & Class_::MODIFIER_STATIC) { - $strs[] = 'MODIFIER_STATIC'; + if ($flags & Modifiers::STATIC) { + $strs[] = 'STATIC'; } - if ($flags & Class_::MODIFIER_FINAL) { - $strs[] = 'MODIFIER_FINAL'; + if ($flags & Modifiers::FINAL) { + $strs[] = 'FINAL'; } - if ($flags & Class_::MODIFIER_READONLY) { - $strs[] = 'MODIFIER_READONLY'; + if ($flags & Modifiers::READONLY) { + $strs[] = 'READONLY'; } if ($strs) { - return \implode(' | ', $strs) . ' (' . $flags . ')'; + return implode(' | ', $strs) . ' (' . $flags . ')'; } else { - return $flags; + return (string) $flags; } } - protected function dumpIncludeType($type) + /** @param array $map */ + private function dumpEnum(int $value, array $map): string { - $map = [Include_::TYPE_INCLUDE => 'TYPE_INCLUDE', Include_::TYPE_INCLUDE_ONCE => 'TYPE_INCLUDE_ONCE', Include_::TYPE_REQUIRE => 'TYPE_REQUIRE', Include_::TYPE_REQUIRE_ONCE => 'TYPE_REQUIRE_ONCE']; - if (!isset($map[$type])) { - return $type; + if (!isset($map[$value])) { + return (string) $value; } - return $map[$type] . ' (' . $type . ')'; + return $map[$value] . ' (' . $value . ')'; } - protected function dumpUseType($type) + private function dumpIncludeType(int $type): string { - $map = [Use_::TYPE_UNKNOWN => 'TYPE_UNKNOWN', Use_::TYPE_NORMAL => 'TYPE_NORMAL', Use_::TYPE_FUNCTION => 'TYPE_FUNCTION', Use_::TYPE_CONSTANT => 'TYPE_CONSTANT']; - if (!isset($map[$type])) { - return $type; - } - return $map[$type] . ' (' . $type . ')'; + return $this->dumpEnum($type, [Include_::TYPE_INCLUDE => 'TYPE_INCLUDE', Include_::TYPE_INCLUDE_ONCE => 'TYPE_INCLUDE_ONCE', Include_::TYPE_REQUIRE => 'TYPE_REQUIRE', Include_::TYPE_REQUIRE_ONCE => 'TYPE_REQUIRE_ONCE']); + } + private function dumpUseType(int $type): string + { + return $this->dumpEnum($type, [Use_::TYPE_UNKNOWN => 'TYPE_UNKNOWN', Use_::TYPE_NORMAL => 'TYPE_NORMAL', Use_::TYPE_FUNCTION => 'TYPE_FUNCTION', Use_::TYPE_CONSTANT => 'TYPE_CONSTANT']); + } + private function dumpIntKind(int $kind): string + { + return $this->dumpEnum($kind, [Int_::KIND_BIN => 'KIND_BIN', Int_::KIND_OCT => 'KIND_OCT', Int_::KIND_DEC => 'KIND_DEC', Int_::KIND_HEX => 'KIND_HEX']); + } + private function dumpStringKind(int $kind): string + { + return $this->dumpEnum($kind, [String_::KIND_SINGLE_QUOTED => 'KIND_SINGLE_QUOTED', String_::KIND_DOUBLE_QUOTED => 'KIND_DOUBLE_QUOTED', String_::KIND_HEREDOC => 'KIND_HEREDOC', String_::KIND_NOWDOC => 'KIND_NOWDOC']); + } + private function dumpArrayKind(int $kind): string + { + return $this->dumpEnum($kind, [Array_::KIND_LONG => 'KIND_LONG', Array_::KIND_SHORT => 'KIND_SHORT']); + } + private function dumpListKind(int $kind): string + { + return $this->dumpEnum($kind, [List_::KIND_LIST => 'KIND_LIST', List_::KIND_ARRAY => 'KIND_ARRAY']); } /** * Dump node position, if possible. @@ -17511,7 +16748,7 @@ class NodeDumper * * @return string|null Dump of position, or null if position information not available */ - protected function dumpPosition(Node $node) + protected function dumpPosition(Node $node): ?string { if (!$node->hasAttribute('startLine') || !$node->hasAttribute('endLine')) { return null; @@ -17525,12 +16762,12 @@ class NodeDumper return "[{$start} - {$end}]"; } // Copied from Error class - private function toColumn($code, $pos) + private function toColumn(string $code, int $pos): int { - if ($pos > \strlen($code)) { + if ($pos > strlen($code)) { throw new \RuntimeException('Invalid position information'); } - $lineStartPos = \strrpos($code, "\n", $pos - \strlen($code)); + $lineStartPos = strrpos($code, "\n", $pos - strlen($code)); if (\false === $lineStartPos) { $lineStartPos = -1; } @@ -17549,66 +16786,73 @@ class NodeFinder /** * Find all nodes satisfying a filter callback. * - * @param Node|Node[] $nodes Single node or array of nodes to search in - * @param callable $filter Filter callback: function(Node $node) : bool + * @param Node|Node[] $nodes Single node or array of nodes to search in + * @param callable $filter Filter callback: function(Node $node) : bool * * @return Node[] Found nodes satisfying the filter callback */ - public function find($nodes, callable $filter) : array + public function find($nodes, callable $filter): array { - if (!\is_array($nodes)) { + if ($nodes === []) { + return []; + } + if (!is_array($nodes)) { $nodes = [$nodes]; } $visitor = new FindingVisitor($filter); - $traverser = new NodeTraverser(); - $traverser->addVisitor($visitor); + $traverser = new NodeTraverser($visitor); $traverser->traverse($nodes); return $visitor->getFoundNodes(); } /** * Find all nodes that are instances of a certain class. + * @template TNode as Node * * @param Node|Node[] $nodes Single node or array of nodes to search in - * @param string $class Class name + * @param class-string $class Class name * - * @return Node[] Found nodes (all instances of $class) + * @return TNode[] Found nodes (all instances of $class) */ - public function findInstanceOf($nodes, string $class) : array + public function findInstanceOf($nodes, string $class): array { - return $this->find($nodes, function ($node) use($class) { + return $this->find($nodes, function ($node) use ($class) { return $node instanceof $class; }); } /** * Find first node satisfying a filter callback. * - * @param Node|Node[] $nodes Single node or array of nodes to search in - * @param callable $filter Filter callback: function(Node $node) : bool + * @param Node|Node[] $nodes Single node or array of nodes to search in + * @param callable $filter Filter callback: function(Node $node) : bool * * @return null|Node Found node (or null if none found) */ - public function findFirst($nodes, callable $filter) + public function findFirst($nodes, callable $filter): ?Node { - if (!\is_array($nodes)) { + if ($nodes === []) { + return null; + } + if (!is_array($nodes)) { $nodes = [$nodes]; } $visitor = new FirstFindingVisitor($filter); - $traverser = new NodeTraverser(); - $traverser->addVisitor($visitor); + $traverser = new NodeTraverser($visitor); $traverser->traverse($nodes); return $visitor->getFoundNode(); } /** * Find first node that is an instance of a certain class. * - * @param Node|Node[] $nodes Single node or array of nodes to search in - * @param string $class Class name + * @template TNode as Node * - * @return null|Node Found node, which is an instance of $class (or null if none found) + * @param Node|Node[] $nodes Single node or array of nodes to search in + * @param class-string $class Class name + * + * @return null|TNode Found node, which is an instance of $class (or null if none found) */ - public function findFirstInstanceOf($nodes, string $class) + public function findFirstInstanceOf($nodes, string $class): ?Node { - return $this->findFirst($nodes, function ($node) use($class) { + return $this->findFirst($nodes, function ($node) use ($class) { return $node instanceof $class; }); } @@ -17621,65 +16865,51 @@ namespace PHPUnitPHAR\PhpParser; class NodeTraverser implements NodeTraverserInterface { /** - * If NodeVisitor::enterNode() returns DONT_TRAVERSE_CHILDREN, child nodes - * of the current node will not be traversed for any visitors. - * - * For subsequent visitors enterNode() will still be called on the current - * node and leaveNode() will also be invoked for the current node. + * @deprecated Use NodeVisitor::DONT_TRAVERSE_CHILDREN instead. */ - const DONT_TRAVERSE_CHILDREN = 1; + public const DONT_TRAVERSE_CHILDREN = NodeVisitor::DONT_TRAVERSE_CHILDREN; /** - * If NodeVisitor::enterNode() or NodeVisitor::leaveNode() returns - * STOP_TRAVERSAL, traversal is aborted. - * - * The afterTraverse() method will still be invoked. + * @deprecated Use NodeVisitor::STOP_TRAVERSAL instead. */ - const STOP_TRAVERSAL = 2; + public const STOP_TRAVERSAL = NodeVisitor::STOP_TRAVERSAL; /** - * If NodeVisitor::leaveNode() returns REMOVE_NODE for a node that occurs - * in an array, it will be removed from the array. - * - * For subsequent visitors leaveNode() will still be invoked for the - * removed node. + * @deprecated Use NodeVisitor::REMOVE_NODE instead. */ - const REMOVE_NODE = 3; + public const REMOVE_NODE = NodeVisitor::REMOVE_NODE; /** - * If NodeVisitor::enterNode() returns DONT_TRAVERSE_CURRENT_AND_CHILDREN, child nodes - * of the current node will not be traversed for any visitors. - * - * For subsequent visitors enterNode() will not be called as well. - * leaveNode() will be invoked for visitors that has enterNode() method invoked. + * @deprecated Use NodeVisitor::DONT_TRAVERSE_CURRENT_AND_CHILDREN instead. */ - const DONT_TRAVERSE_CURRENT_AND_CHILDREN = 4; - /** @var NodeVisitor[] Visitors */ - protected $visitors = []; + public const DONT_TRAVERSE_CURRENT_AND_CHILDREN = NodeVisitor::DONT_TRAVERSE_CURRENT_AND_CHILDREN; + /** @var list Visitors */ + protected array $visitors = []; /** @var bool Whether traversal should be stopped */ - protected $stopTraversal; - public function __construct() + protected bool $stopTraversal; + /** + * Create a traverser with the given visitors. + * + * @param NodeVisitor ...$visitors Node visitors + */ + public function __construct(NodeVisitor ...$visitors) { - // for BC + $this->visitors = $visitors; } /** * Adds a visitor. * * @param NodeVisitor $visitor Visitor to add */ - public function addVisitor(NodeVisitor $visitor) + public function addVisitor(NodeVisitor $visitor): void { $this->visitors[] = $visitor; } /** * Removes an added visitor. - * - * @param NodeVisitor $visitor */ - public function removeVisitor(NodeVisitor $visitor) + public function removeVisitor(NodeVisitor $visitor): void { - foreach ($this->visitors as $index => $storedVisitor) { - if ($storedVisitor === $visitor) { - unset($this->visitors[$index]); - break; - } + $index = array_search($visitor, $this->visitors); + if ($index !== \false) { + array_splice($this->visitors, $index, 1, []); } } /** @@ -17689,17 +16919,18 @@ class NodeTraverser implements NodeTraverserInterface * * @return Node[] Traversed array of nodes */ - public function traverse(array $nodes) : array + public function traverse(array $nodes): array { $this->stopTraversal = \false; foreach ($this->visitors as $visitor) { - if (null !== ($return = $visitor->beforeTraverse($nodes))) { + if (null !== $return = $visitor->beforeTraverse($nodes)) { $nodes = $return; } } $nodes = $this->traverseArray($nodes); - foreach ($this->visitors as $visitor) { - if (null !== ($return = $visitor->afterTraverse($nodes))) { + for ($i = \count($this->visitors) - 1; $i >= 0; --$i) { + $visitor = $this->visitors[$i]; + if (null !== $return = $visitor->afterTraverse($nodes)) { $nodes = $return; } } @@ -17709,69 +16940,69 @@ class NodeTraverser implements NodeTraverserInterface * Recursively traverse a node. * * @param Node $node Node to traverse. - * - * @return Node Result of traversal (may be original node or new one) */ - protected function traverseNode(Node $node) : Node + protected function traverseNode(Node $node): void { foreach ($node->getSubNodeNames() as $name) { - $subNode =& $node->{$name}; + $subNode = $node->{$name}; if (\is_array($subNode)) { - $subNode = $this->traverseArray($subNode); + $node->{$name} = $this->traverseArray($subNode); if ($this->stopTraversal) { break; } } elseif ($subNode instanceof Node) { $traverseChildren = \true; - $breakVisitorIndex = null; + $visitorIndex = -1; foreach ($this->visitors as $visitorIndex => $visitor) { $return = $visitor->enterNode($subNode); if (null !== $return) { if ($return instanceof Node) { $this->ensureReplacementReasonable($subNode, $return); - $subNode = $return; - } elseif (self::DONT_TRAVERSE_CHILDREN === $return) { + $subNode = $node->{$name} = $return; + } elseif (NodeVisitor::DONT_TRAVERSE_CHILDREN === $return) { $traverseChildren = \false; - } elseif (self::DONT_TRAVERSE_CURRENT_AND_CHILDREN === $return) { + } elseif (NodeVisitor::DONT_TRAVERSE_CURRENT_AND_CHILDREN === $return) { $traverseChildren = \false; - $breakVisitorIndex = $visitorIndex; break; - } elseif (self::STOP_TRAVERSAL === $return) { + } elseif (NodeVisitor::STOP_TRAVERSAL === $return) { $this->stopTraversal = \true; break 2; + } elseif (NodeVisitor::REPLACE_WITH_NULL === $return) { + $node->{$name} = null; + continue 2; } else { - throw new \LogicException('enterNode() returned invalid value of type ' . \gettype($return)); + throw new \LogicException('enterNode() returned invalid value of type ' . gettype($return)); } } } if ($traverseChildren) { - $subNode = $this->traverseNode($subNode); + $this->traverseNode($subNode); if ($this->stopTraversal) { break; } } - foreach ($this->visitors as $visitorIndex => $visitor) { + for (; $visitorIndex >= 0; --$visitorIndex) { + $visitor = $this->visitors[$visitorIndex]; $return = $visitor->leaveNode($subNode); if (null !== $return) { if ($return instanceof Node) { $this->ensureReplacementReasonable($subNode, $return); - $subNode = $return; - } elseif (self::STOP_TRAVERSAL === $return) { + $subNode = $node->{$name} = $return; + } elseif (NodeVisitor::STOP_TRAVERSAL === $return) { $this->stopTraversal = \true; break 2; + } elseif (NodeVisitor::REPLACE_WITH_NULL === $return) { + $node->{$name} = null; + break; } elseif (\is_array($return)) { throw new \LogicException('leaveNode() may only return an array ' . 'if the parent structure is an array'); } else { - throw new \LogicException('leaveNode() returned invalid value of type ' . \gettype($return)); + throw new \LogicException('leaveNode() returned invalid value of type ' . gettype($return)); } } - if ($breakVisitorIndex === $visitorIndex) { - break; - } } } } - return $node; } /** * Recursively traverse array (usually of nodes). @@ -17780,76 +17011,81 @@ class NodeTraverser implements NodeTraverserInterface * * @return array Result of traversal (may be original array or changed one) */ - protected function traverseArray(array $nodes) : array + protected function traverseArray(array $nodes): array { $doNodes = []; - foreach ($nodes as $i => &$node) { + foreach ($nodes as $i => $node) { if ($node instanceof Node) { $traverseChildren = \true; - $breakVisitorIndex = null; + $visitorIndex = -1; foreach ($this->visitors as $visitorIndex => $visitor) { $return = $visitor->enterNode($node); if (null !== $return) { if ($return instanceof Node) { $this->ensureReplacementReasonable($node, $return); - $node = $return; - } elseif (self::DONT_TRAVERSE_CHILDREN === $return) { + $nodes[$i] = $node = $return; + } elseif (\is_array($return)) { + $doNodes[] = [$i, $return]; + continue 2; + } elseif (NodeVisitor::REMOVE_NODE === $return) { + $doNodes[] = [$i, []]; + continue 2; + } elseif (NodeVisitor::DONT_TRAVERSE_CHILDREN === $return) { $traverseChildren = \false; - } elseif (self::DONT_TRAVERSE_CURRENT_AND_CHILDREN === $return) { + } elseif (NodeVisitor::DONT_TRAVERSE_CURRENT_AND_CHILDREN === $return) { $traverseChildren = \false; - $breakVisitorIndex = $visitorIndex; break; - } elseif (self::STOP_TRAVERSAL === $return) { + } elseif (NodeVisitor::STOP_TRAVERSAL === $return) { $this->stopTraversal = \true; break 2; + } elseif (NodeVisitor::REPLACE_WITH_NULL === $return) { + throw new \LogicException('REPLACE_WITH_NULL can not be used if the parent structure is an array'); } else { - throw new \LogicException('enterNode() returned invalid value of type ' . \gettype($return)); + throw new \LogicException('enterNode() returned invalid value of type ' . gettype($return)); } } } if ($traverseChildren) { - $node = $this->traverseNode($node); + $this->traverseNode($node); if ($this->stopTraversal) { break; } } - foreach ($this->visitors as $visitorIndex => $visitor) { + for (; $visitorIndex >= 0; --$visitorIndex) { + $visitor = $this->visitors[$visitorIndex]; $return = $visitor->leaveNode($node); if (null !== $return) { if ($return instanceof Node) { $this->ensureReplacementReasonable($node, $return); - $node = $return; + $nodes[$i] = $node = $return; } elseif (\is_array($return)) { $doNodes[] = [$i, $return]; break; - } elseif (self::REMOVE_NODE === $return) { + } elseif (NodeVisitor::REMOVE_NODE === $return) { $doNodes[] = [$i, []]; break; - } elseif (self::STOP_TRAVERSAL === $return) { + } elseif (NodeVisitor::STOP_TRAVERSAL === $return) { $this->stopTraversal = \true; break 2; - } elseif (\false === $return) { - throw new \LogicException('bool(false) return from leaveNode() no longer supported. ' . 'Return NodeTraverser::REMOVE_NODE instead'); + } elseif (NodeVisitor::REPLACE_WITH_NULL === $return) { + throw new \LogicException('REPLACE_WITH_NULL can not be used if the parent structure is an array'); } else { - throw new \LogicException('leaveNode() returned invalid value of type ' . \gettype($return)); + throw new \LogicException('leaveNode() returned invalid value of type ' . gettype($return)); } } - if ($breakVisitorIndex === $visitorIndex) { - break; - } } } elseif (\is_array($node)) { throw new \LogicException('Invalid node structure: Contains nested arrays'); } } if (!empty($doNodes)) { - while (list($i, $replace) = \array_pop($doNodes)) { - \array_splice($nodes, $i, 1, $replace); + while (list($i, $replace) = array_pop($doNodes)) { + array_splice($nodes, $i, 1, $replace); } } return $nodes; } - private function ensureReplacementReasonable($old, $new) + private function ensureReplacementReasonable(Node $old, Node $new): void { if ($old instanceof Node\Stmt && $new instanceof Node\Expr) { throw new \LogicException("Trying to replace statement ({$old->getType()}) " . "with expression ({$new->getType()}). Are you missing a " . "Stmt_Expression wrapper?"); @@ -17871,13 +17107,11 @@ interface NodeTraverserInterface * * @param NodeVisitor $visitor Visitor to add */ - public function addVisitor(NodeVisitor $visitor); + public function addVisitor(NodeVisitor $visitor): void; /** * Removes an added visitor. - * - * @param NodeVisitor $visitor */ - public function removeVisitor(NodeVisitor $visitor); + public function removeVisitor(NodeVisitor $visitor): void; /** * Traverses an array of nodes using the registered visitors. * @@ -17885,7 +17119,7 @@ interface NodeTraverserInterface * * @return Node[] Traversed array of nodes */ - public function traverse(array $nodes) : array; + public function traverse(array $nodes): array; } $node stays as-is - * * NodeTraverser::DONT_TRAVERSE_CHILDREN + * * array (of Nodes) + * => The return value is merged into the parent array (at the position of the $node) + * * NodeVisitor::REMOVE_NODE + * => $node is removed from the parent array + * * NodeVisitor::REPLACE_WITH_NULL + * => $node is replaced with null + * * NodeVisitor::DONT_TRAVERSE_CHILDREN * => Children of $node are not traversed. $node stays as-is - * * NodeTraverser::STOP_TRAVERSAL + * * NodeVisitor::DONT_TRAVERSE_CURRENT_AND_CHILDREN + * => Further visitors for the current node are skipped, and its children are not + * traversed. $node stays as-is. + * * NodeVisitor::STOP_TRAVERSAL * => Traversal is aborted. $node stays as-is * * otherwise * => $node is set to the return value * * @param Node $node Node * - * @return null|int|Node Replacement node (or special return value) + * @return null|int|Node|Node[] Replacement node (or special return value) */ public function enterNode(Node $node); /** @@ -17930,9 +17210,11 @@ interface NodeVisitor * Return value semantics: * * null * => $node stays as-is - * * NodeTraverser::REMOVE_NODE + * * NodeVisitor::REMOVE_NODE * => $node is removed from the parent array - * * NodeTraverser::STOP_TRAVERSAL + * * NodeVisitor::REPLACE_WITH_NULL + * => $node is replaced with null + * * NodeVisitor::STOP_TRAVERSAL * => Traversal is aborted. $node stays as-is * * array (of Nodes) * => The return value is merged into the parent array (at the position of the $node) @@ -17983,6 +17265,80 @@ class CloningVisitor extends NodeVisitorAbstract declare (strict_types=1); namespace PHPUnitPHAR\PhpParser\NodeVisitor; +use PHPUnitPHAR\PhpParser\Comment; +use PHPUnitPHAR\PhpParser\Node; +use PHPUnitPHAR\PhpParser\NodeVisitorAbstract; +use PHPUnitPHAR\PhpParser\Token; +class CommentAnnotatingVisitor extends NodeVisitorAbstract +{ + /** @var int Last seen token start position */ + private int $pos = 0; + /** @var Token[] Token array */ + private array $tokens; + /** @var list Token positions of comments */ + private array $commentPositions = []; + /** + * Create a comment annotation visitor. + * + * @param Token[] $tokens Token array + */ + public function __construct(array $tokens) + { + $this->tokens = $tokens; + // Collect positions of comments. We use this to avoid traversing parts of the AST where + // there are no comments. + foreach ($tokens as $i => $token) { + if ($token->id === \T_COMMENT || $token->id === \T_DOC_COMMENT) { + $this->commentPositions[] = $i; + } + } + } + public function enterNode(Node $node) + { + $nextCommentPos = current($this->commentPositions); + if ($nextCommentPos === \false) { + // No more comments. + return self::STOP_TRAVERSAL; + } + $oldPos = $this->pos; + $this->pos = $pos = $node->getStartTokenPos(); + if ($nextCommentPos > $oldPos && $nextCommentPos < $pos) { + $comments = []; + while (--$pos >= $oldPos) { + $token = $this->tokens[$pos]; + if ($token->id === \T_DOC_COMMENT) { + $comments[] = new Comment\Doc($token->text, $token->line, $token->pos, $pos, $token->getEndLine(), $token->getEndPos() - 1, $pos); + continue; + } + if ($token->id === \T_COMMENT) { + $comments[] = new Comment($token->text, $token->line, $token->pos, $pos, $token->getEndLine(), $token->getEndPos() - 1, $pos); + continue; + } + if ($token->id !== \T_WHITESPACE) { + break; + } + } + if (!empty($comments)) { + $node->setAttribute('comments', array_reverse($comments)); + } + do { + $nextCommentPos = next($this->commentPositions); + } while ($nextCommentPos !== \false && $nextCommentPos < $this->pos); + } + $endPos = $node->getEndTokenPos(); + if ($nextCommentPos > $endPos) { + // Skip children if there are no comments located inside this node. + $this->pos = $endPos; + return self::DONT_TRAVERSE_CHILDREN; + } + return null; + } +} +filterCallback = $filterCallback; @@ -18006,11 +17362,11 @@ class FindingVisitor extends NodeVisitorAbstract * * @return Node[] Found nodes */ - public function getFoundNodes() : array + public function getFoundNodes(): array { return $this->foundNodes; } - public function beforeTraverse(array $nodes) + public function beforeTraverse(array $nodes): ?array { $this->foundNodes = []; return null; @@ -18030,7 +17386,7 @@ declare (strict_types=1); namespace PHPUnitPHAR\PhpParser\NodeVisitor; use PHPUnitPHAR\PhpParser\Node; -use PHPUnitPHAR\PhpParser\NodeTraverser; +use PHPUnitPHAR\PhpParser\NodeVisitor; use PHPUnitPHAR\PhpParser\NodeVisitorAbstract; /** * This visitor can be used to find the first node satisfying some criterion determined by @@ -18041,7 +17397,7 @@ class FirstFindingVisitor extends NodeVisitorAbstract /** @var callable Filter callback */ protected $filterCallback; /** @var null|Node Found node */ - protected $foundNode; + protected ?Node $foundNode; public function __construct(callable $filterCallback) { $this->filterCallback = $filterCallback; @@ -18053,11 +17409,11 @@ class FirstFindingVisitor extends NodeVisitorAbstract * * @return null|Node Found node (or null if not found) */ - public function getFoundNode() + public function getFoundNode(): ?Node { return $this->foundNode; } - public function beforeTraverse(array $nodes) + public function beforeTraverse(array $nodes): ?array { $this->foundNode = null; return null; @@ -18067,7 +17423,7 @@ class FirstFindingVisitor extends NodeVisitorAbstract $filterCallback = $this->filterCallback; if ($filterCallback($node)) { $this->foundNode = $node; - return NodeTraverser::STOP_TRAVERSAL; + return NodeVisitor::STOP_TRAVERSAL; } return null; } @@ -18088,11 +17444,11 @@ use PHPUnitPHAR\PhpParser\NodeVisitorAbstract; class NameResolver extends NodeVisitorAbstract { /** @var NameContext Naming context */ - protected $nameContext; + protected NameContext $nameContext; /** @var bool Whether to preserve original names */ - protected $preserveOriginalNames; + protected bool $preserveOriginalNames; /** @var bool Whether to replace resolved nodes in place, or to add resolvedNode attributes */ - protected $replaceNodes; + protected bool $replaceNodes; /** * Constructs a name resolution visitor. * @@ -18104,7 +17460,7 @@ class NameResolver extends NodeVisitorAbstract * namespacedName attribute, as usual.) * * @param ErrorHandler|null $errorHandler Error handler - * @param array $options Options + * @param array{preserveOriginalNames?: bool, replaceNodes?: bool} $options Options */ public function __construct(?ErrorHandler $errorHandler = null, array $options = []) { @@ -18114,14 +17470,12 @@ class NameResolver extends NodeVisitorAbstract } /** * Get name resolution context. - * - * @return NameContext */ - public function getNameContext() : NameContext + public function getNameContext(): NameContext { return $this->nameContext; } - public function beforeTraverse(array $nodes) + public function beforeTraverse(array $nodes): ?array { $this->nameContext->startNamespace(); return null; @@ -18148,6 +17502,8 @@ class NameResolver extends NodeVisitorAbstract $this->resolveAttrGroups($node); if (null !== $node->name) { $this->addNamespacedName($node); + } else { + $node->namespacedName = null; } } elseif ($node instanceof Stmt\Interface_) { foreach ($node->extends as &$interface) { @@ -18160,9 +17516,7 @@ class NameResolver extends NodeVisitorAbstract $interface = $this->resolveClassName($interface); } $this->resolveAttrGroups($node); - if (null !== $node->name) { - $this->addNamespacedName($node); - } + $this->addNamespacedName($node); } elseif ($node instanceof Stmt\Trait_) { $this->resolveAttrGroups($node); $this->addNamespacedName($node); @@ -18182,49 +17536,46 @@ class NameResolver extends NodeVisitorAbstract foreach ($node->consts as $const) { $this->addNamespacedName($const); } - } else { - if ($node instanceof Stmt\ClassConst) { - if (null !== $node->type) { - $node->type = $this->resolveType($node->type); + } elseif ($node instanceof Stmt\ClassConst) { + if (null !== $node->type) { + $node->type = $this->resolveType($node->type); + } + $this->resolveAttrGroups($node); + } elseif ($node instanceof Stmt\EnumCase) { + $this->resolveAttrGroups($node); + } elseif ($node instanceof Expr\StaticCall || $node instanceof Expr\StaticPropertyFetch || $node instanceof Expr\ClassConstFetch || $node instanceof Expr\New_ || $node instanceof Expr\Instanceof_) { + if ($node->class instanceof Name) { + $node->class = $this->resolveClassName($node->class); + } + } elseif ($node instanceof Stmt\Catch_) { + foreach ($node->types as &$type) { + $type = $this->resolveClassName($type); + } + } elseif ($node instanceof Expr\FuncCall) { + if ($node->name instanceof Name) { + $node->name = $this->resolveName($node->name, Stmt\Use_::TYPE_FUNCTION); + } + } elseif ($node instanceof Expr\ConstFetch) { + $node->name = $this->resolveName($node->name, Stmt\Use_::TYPE_CONSTANT); + } elseif ($node instanceof Stmt\TraitUse) { + foreach ($node->traits as &$trait) { + $trait = $this->resolveClassName($trait); + } + foreach ($node->adaptations as $adaptation) { + if (null !== $adaptation->trait) { + $adaptation->trait = $this->resolveClassName($adaptation->trait); } - $this->resolveAttrGroups($node); - } else { - if ($node instanceof Stmt\EnumCase) { - $this->resolveAttrGroups($node); - } elseif ($node instanceof Expr\StaticCall || $node instanceof Expr\StaticPropertyFetch || $node instanceof Expr\ClassConstFetch || $node instanceof Expr\New_ || $node instanceof Expr\Instanceof_) { - if ($node->class instanceof Name) { - $node->class = $this->resolveClassName($node->class); - } - } elseif ($node instanceof Stmt\Catch_) { - foreach ($node->types as &$type) { - $type = $this->resolveClassName($type); - } - } elseif ($node instanceof Expr\FuncCall) { - if ($node->name instanceof Name) { - $node->name = $this->resolveName($node->name, Stmt\Use_::TYPE_FUNCTION); - } - } elseif ($node instanceof Expr\ConstFetch) { - $node->name = $this->resolveName($node->name, Stmt\Use_::TYPE_CONSTANT); - } elseif ($node instanceof Stmt\TraitUse) { - foreach ($node->traits as &$trait) { - $trait = $this->resolveClassName($trait); - } - foreach ($node->adaptations as $adaptation) { - if (null !== $adaptation->trait) { - $adaptation->trait = $this->resolveClassName($adaptation->trait); - } - if ($adaptation instanceof Stmt\TraitUseAdaptation\Precedence) { - foreach ($adaptation->insteadof as &$insteadof) { - $insteadof = $this->resolveClassName($insteadof); - } - } + if ($adaptation instanceof Stmt\TraitUseAdaptation\Precedence) { + foreach ($adaptation->insteadof as &$insteadof) { + $insteadof = $this->resolveClassName($insteadof); } } } } return null; } - private function addAlias(Stmt\UseUse $use, int $type, ?Name $prefix = null) + /** @param Stmt\Use_::TYPE_* $type */ + private function addAlias(Node\UseItem $use, int $type, ?Name $prefix = null): void { // Add prefix for group uses $name = $prefix ? Name::concat($prefix, $use->name) : $use->name; @@ -18232,8 +17583,8 @@ class NameResolver extends NodeVisitorAbstract $type |= $use->type; $this->nameContext->addAlias($name, (string) $use->getAlias(), $type, $use->getAttributes()); } - /** @param Stmt\Function_|Stmt\ClassMethod|Expr\Closure $node */ - private function resolveSignature($node) + /** @param Stmt\Function_|Stmt\ClassMethod|Expr\Closure|Expr\ArrowFunction $node */ + private function resolveSignature($node): void { foreach ($node->params as $param) { $param->type = $this->resolveType($param->type); @@ -18241,7 +17592,12 @@ class NameResolver extends NodeVisitorAbstract } $node->returnType = $this->resolveType($node->returnType); } - private function resolveType($node) + /** + * @template T of Node\Identifier|Name|Node\ComplexType|null + * @param T $node + * @return T + */ + private function resolveType(?Node $node): ?Node { if ($node instanceof Name) { return $this->resolveClassName($node); @@ -18262,11 +17618,11 @@ class NameResolver extends NodeVisitorAbstract * Resolve name, according to name resolver options. * * @param Name $name Function or constant name to resolve - * @param int $type One of Stmt\Use_::TYPE_* + * @param Stmt\Use_::TYPE_* $type One of Stmt\Use_::TYPE_* * * @return Name Resolved name, or original name with attribute */ - protected function resolveName(Name $name, int $type) : Name + protected function resolveName(Name $name, int $type): Name { if (!$this->replaceNodes) { $resolvedName = $this->nameContext->getResolvedName($name, $type); @@ -18292,15 +17648,15 @@ class NameResolver extends NodeVisitorAbstract $name->setAttribute('namespacedName', FullyQualified::concat($this->nameContext->getNamespace(), $name, $name->getAttributes())); return $name; } - protected function resolveClassName(Name $name) + protected function resolveClassName(Name $name): Name { return $this->resolveName($name, Stmt\Use_::TYPE_NORMAL); } - protected function addNamespacedName(Node $node) + protected function addNamespacedName(Node $node): void { $node->namespacedName = Name::concat($this->nameContext->getNamespace(), (string) $node->name); } - protected function resolveAttrGroups(Node $node) + protected function resolveAttrGroups(Node $node): void { foreach ($node->attrGroups as $attrGroup) { foreach ($attrGroup->attrs as $attr) { @@ -18330,7 +17686,7 @@ final class NodeConnectingVisitor extends NodeVisitorAbstract /** * @var Node[] */ - private $stack = []; + private array $stack = []; /** * @var ?Node */ @@ -18343,7 +17699,7 @@ final class NodeConnectingVisitor extends NodeVisitorAbstract public function enterNode(Node $node) { if (!empty($this->stack)) { - $node->setAttribute('parent', $this->stack[\count($this->stack) - 1]); + $node->setAttribute('parent', $this->stack[count($this->stack) - 1]); } if ($this->previous !== null && $this->previous->getAttribute('parent') === $node->getAttribute('parent')) { $node->setAttribute('previous', $this->previous); @@ -18354,7 +17710,7 @@ final class NodeConnectingVisitor extends NodeVisitorAbstract public function leaveNode(Node $node) { $this->previous = $node; - \array_pop($this->stack); + array_pop($this->stack); } } stack = []; @@ -18402,7 +17758,7 @@ namespace PHPUnitPHAR\PhpParser; /** * @codeCoverageIgnore */ -class NodeVisitorAbstract implements NodeVisitor +abstract class NodeVisitorAbstract implements NodeVisitor { public function beforeTraverse(array $nodes) { @@ -18438,66 +17794,21 @@ interface Parser * @return Node\Stmt[]|null Array of statements (or null non-throwing error handler is used and * the parser was unable to recover from an error). */ - public function parse(string $code, ?ErrorHandler $errorHandler = null); -} -parsers = $parsers; - } - public function parse(string $code, ?ErrorHandler $errorHandler = null) - { - if (null === $errorHandler) { - $errorHandler = new ErrorHandler\Throwing(); - } - list($firstStmts, $firstError) = $this->tryParse($this->parsers[0], $errorHandler, $code); - if ($firstError === null) { - return $firstStmts; - } - for ($i = 1, $c = \count($this->parsers); $i < $c; ++$i) { - list($stmts, $error) = $this->tryParse($this->parsers[$i], $errorHandler, $code); - if ($error === null) { - return $stmts; - } - } - throw $firstError; - } - private function tryParse(Parser $parser, ErrorHandler $errorHandler, $code) - { - $stmts = null; - $error = null; - try { - $stmts = $parser->parse($code, $errorHandler); - } catch (Error $error) { - } - return [$stmts, $error]; - } + public function getTokens(): array; } '", "T_IS_GREATER_OR_EQUAL", "T_SL", "T_SR", "'+'", "'-'", "'.'", "'*'", "'/'", "'%'", "'!'", "T_INSTANCEOF", "'~'", "T_INC", "T_DEC", "T_INT_CAST", "T_DOUBLE_CAST", "T_STRING_CAST", "T_ARRAY_CAST", "T_OBJECT_CAST", "T_BOOL_CAST", "T_UNSET_CAST", "'@'", "T_POW", "'['", "T_NEW", "T_CLONE", "T_EXIT", "T_IF", "T_ELSEIF", "T_ELSE", "T_ENDIF", "T_LNUMBER", "T_DNUMBER", "T_STRING", "T_STRING_VARNAME", "T_VARIABLE", "T_NUM_STRING", "T_INLINE_HTML", "T_ENCAPSED_AND_WHITESPACE", "T_CONSTANT_ENCAPSED_STRING", "T_ECHO", "T_DO", "T_WHILE", "T_ENDWHILE", "T_FOR", "T_ENDFOR", "T_FOREACH", "T_ENDFOREACH", "T_DECLARE", "T_ENDDECLARE", "T_AS", "T_SWITCH", "T_MATCH", "T_ENDSWITCH", "T_CASE", "T_DEFAULT", "T_BREAK", "T_CONTINUE", "T_GOTO", "T_FUNCTION", "T_FN", "T_CONST", "T_RETURN", "T_TRY", "T_CATCH", "T_FINALLY", "T_USE", "T_INSTEADOF", "T_GLOBAL", "T_STATIC", "T_ABSTRACT", "T_FINAL", "T_PRIVATE", "T_PROTECTED", "T_PUBLIC", "T_READONLY", "T_VAR", "T_UNSET", "T_ISSET", "T_EMPTY", "T_HALT_COMPILER", "T_CLASS", "T_TRAIT", "T_INTERFACE", "T_EXTENDS", "T_IMPLEMENTS", "T_OBJECT_OPERATOR", "T_LIST", "T_ARRAY", "T_CALLABLE", "T_CLASS_C", "T_TRAIT_C", "T_METHOD_C", "T_FUNC_C", "T_LINE", "T_FILE", "T_START_HEREDOC", "T_END_HEREDOC", "T_DOLLAR_OPEN_CURLY_BRACES", "T_CURLY_OPEN", "T_PAAMAYIM_NEKUDOTAYIM", "T_NAMESPACE", "T_NS_C", "T_DIR", "T_NS_SEPARATOR", "T_ELLIPSIS", "T_NAME_FULLY_QUALIFIED", "T_NAME_QUALIFIED", "T_NAME_RELATIVE", "';'", "'{'", "'}'", "'('", "')'", "'\$'", "'`'", "']'", "'\"'", "T_ENUM", "T_NULLSAFE_OBJECT_OPERATOR", "T_ATTRIBUTE"); - protected $tokenToSymbol = array(0, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 56, 164, 168, 161, 55, 168, 168, 159, 160, 53, 50, 8, 51, 52, 54, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 31, 156, 44, 16, 46, 30, 68, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 70, 168, 163, 36, 168, 162, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 157, 35, 158, 58, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 1, 2, 3, 4, 5, 6, 7, 9, 10, 11, 12, 13, 14, 15, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 32, 33, 34, 37, 38, 39, 40, 41, 42, 43, 45, 47, 48, 49, 57, 59, 60, 61, 62, 63, 64, 65, 66, 67, 69, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 165, 131, 132, 133, 166, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 167); - protected $action = array(700, 670, 671, 672, 673, 674, 286, 675, 676, 677, 713, 714, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 0, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, -32766, -32766, -32766, -32766, -32766, -32766, -32766, -32766, -32766, -32767, -32767, -32767, -32767, 245, 246, 242, 243, 244, -32766, -32766, 678, -32766, -32766, -32766, -32766, -32766, -32766, -32766, -32766, -32766, 1229, 245, 246, 1230, 679, 680, 681, 682, 683, 684, 685, 899, 900, 747, -32766, -32766, -32766, -32766, -32766, -32766, 686, 687, 688, 689, 690, 691, 692, 693, 694, 695, 696, 716, 739, 717, 718, 719, 720, 708, 709, 710, 738, 711, 712, 697, 698, 699, 701, 702, 703, 741, 742, 743, 744, 745, 746, 875, 704, 705, 706, 707, 737, 728, 726, 727, 723, 724, 1046, 715, 721, 722, 729, 730, 732, 731, 733, 734, 55, 56, 425, 57, 58, 725, 736, 735, 755, 59, 60, -226, 61, -32766, -32766, -32766, -32766, -32766, -32766, -32766, -32766, -32766, -32766, 337, -32767, -32767, -32767, -32767, 29, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 620, -32766, -32766, -32766, -32766, 62, 63, 1046, -32766, -32766, -32766, 64, 419, 65, 294, 295, 66, 67, 68, 69, 70, 71, 72, 73, 823, 25, 302, 74, 418, 984, 986, 669, 668, 1100, 1101, 1078, 755, 755, 767, 1220, 768, 470, -32766, -32766, -32766, 341, 749, 824, 54, -32767, -32767, -32767, -32767, 98, 99, 100, 101, 102, 220, 221, 222, 362, 876, -32766, 27, -32766, -32766, -32766, -32766, -32766, 1046, 493, 126, 1080, 1079, 1081, 370, 1068, 930, 207, 478, 479, 952, 953, 954, 951, 950, 949, 128, 480, 481, 803, 1106, 1107, 1108, 1109, 1103, 1104, 319, 32, 297, 10, 211, -515, 1110, 1105, 669, 668, 1080, 1079, 1081, 220, 221, 222, 41, 364, 341, 334, 421, 336, 426, -128, -128, -128, 313, 1046, 469, -4, 824, 54, 812, 770, 207, 40, 21, 427, -128, 471, -128, 472, -128, 473, -128, 1046, 428, 220, 221, 222, -32766, 33, 34, 429, 361, 327, 52, 35, 474, -32766, -32766, -32766, 342, 357, 358, 475, 476, 48, 207, 249, 669, 668, 477, 443, 300, 795, 846, 430, 431, 28, -32766, 814, -32766, -32766, -32766, -32766, -32766, -32766, -32766, -32767, -32767, -32767, -32767, -32767, 952, 953, 954, 951, 950, 949, 422, 755, 424, 426, 826, 634, -128, -32766, -32766, 469, 824, 54, 288, 812, 1151, 755, 40, 21, 427, 317, 471, 345, 472, 129, 473, 9, 1186, 428, 769, 360, 324, 905, 33, 34, 429, 361, 1046, 415, 35, 474, 944, 1068, 315, 125, 357, 358, 475, 476, -32766, -32766, -32766, 926, 302, 477, 121, 1068, 759, 846, 430, 431, 669, 668, 423, 755, 1152, 809, 1046, 480, 766, -32766, 805, -32766, -32766, -32766, -32766, -261, 127, 347, 436, 841, 341, 1078, 1200, 426, 446, 826, 634, -4, 807, 469, 824, 54, 436, 812, 341, 755, 40, 21, 427, 444, 471, 130, 472, 1068, 473, 346, 767, 428, 768, -211, -211, -211, 33, 34, 429, 361, 308, 1076, 35, 474, -32766, -32766, -32766, 1046, 357, 358, 475, 476, -32766, -32766, -32766, 906, 120, 477, 539, 1068, 795, 846, 430, 431, 436, -32766, 341, -32766, -32766, -32766, 1046, 480, 810, -32766, 925, -32766, -32766, 754, 1080, 1079, 1081, 49, -32766, -32766, -32766, 749, 751, 426, 1201, 826, 634, -211, 30, 469, 669, 668, 436, 812, 341, 75, 40, 21, 427, -32766, 471, 1064, 472, 124, 473, 669, 668, 428, 212, -210, -210, -210, 33, 34, 429, 361, 51, 1186, 35, 474, 755, -32766, -32766, -32766, 357, 358, 475, 476, 213, 824, 54, 221, 222, 477, 20, 581, 795, 846, 430, 431, 220, 221, 222, 755, 222, 247, 78, 79, 80, 81, 341, 207, 517, 103, 104, 105, 752, 307, 131, 637, 1068, 207, 341, 207, 122, 826, 634, -210, 36, 106, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 1112, 307, 346, 436, 214, 341, 824, 54, 426, 123, 250, 129, 134, 106, 469, -32766, 572, 1112, 812, 245, 246, 40, 21, 427, 251, 471, 252, 472, 341, 473, 453, 22, 428, 207, 899, 900, 638, 33, 34, 429, 824, 54, -86, 35, 474, 220, 221, 222, 314, 357, 358, 100, 101, 102, 239, 240, 241, 645, 477, -230, 458, 589, 135, 374, 596, 597, 207, 760, 640, 648, 642, 941, 654, 929, 662, 822, 133, 307, 837, 426, -32766, 106, 749, 43, 44, 469, 45, 442, 46, 812, 826, 634, 40, 21, 427, 47, 471, 50, 472, 53, 473, 132, 608, 428, 302, 604, -280, -32766, 33, 34, 429, 824, 54, 426, 35, 474, 755, 957, -84, 469, 357, 358, 521, 812, 628, 363, 40, 21, 427, 477, 471, 575, 472, -515, 473, 847, 616, 428, -423, -32766, 11, 646, 33, 34, 429, 824, 54, 445, 35, 474, 462, 285, 578, 1111, 357, 358, 593, 369, 848, 594, 290, 826, 634, 477, 0, 0, 532, 0, 0, 325, 0, 0, 0, 0, 0, 651, 0, 0, 0, 322, 326, 0, 0, 0, 426, 0, 0, 0, 0, 323, 469, 316, 318, -516, 812, 862, 634, 40, 21, 427, 0, 471, 0, 472, 0, 473, 1158, 0, 428, 0, -414, 6, 7, 33, 34, 429, 824, 54, 426, 35, 474, 12, 14, 373, 469, 357, 358, -424, 812, 563, 754, 40, 21, 427, 477, 471, 248, 472, 839, 473, 38, 39, 428, 657, 658, 765, 813, 33, 34, 429, 821, 800, 815, 35, 474, 215, 216, 878, 869, 357, 358, 217, 870, 218, 798, 863, 826, 634, 477, 860, 858, 936, 937, 934, 820, 209, 804, 806, 808, 811, 933, 763, 764, 1100, 1101, 935, 659, 78, 335, 426, 359, 1102, 635, 639, 641, 469, 643, 644, 647, 812, 826, 634, 40, 21, 427, 649, 471, 650, 472, 652, 473, 653, 636, 428, 796, 1226, 1228, 762, 33, 34, 429, 215, 216, 845, 35, 474, 761, 217, 844, 218, 357, 358, 1227, 843, 1060, 831, 1048, 842, 1049, 477, 559, 209, 1106, 1107, 1108, 1109, 1103, 1104, 398, 1100, 1101, 829, 942, 867, 1110, 1105, 868, 1102, 457, 1225, 1194, 1192, 1177, 1157, 219, 1190, 1091, 917, 1198, 1188, 0, 826, 634, 24, -433, 26, 31, 37, 42, 76, 77, 210, 287, 292, 293, 308, 309, 310, 311, 339, 356, 416, 0, -227, -226, 16, 17, 18, 393, 454, 461, 463, 467, 553, 625, 1051, 559, 1054, 1106, 1107, 1108, 1109, 1103, 1104, 398, 907, 1116, 1050, 1026, 564, 1110, 1105, 1025, 1093, 1055, 0, 1044, 0, 1057, 1056, 219, 1059, 1058, 1075, 0, 1191, 1176, 1172, 1189, 1090, 1223, 1117, 1171, 600); - protected $actionCheck = array(2, 3, 4, 5, 6, 7, 14, 9, 10, 11, 12, 13, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 0, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 9, 10, 11, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 69, 70, 53, 54, 55, 9, 10, 57, 30, 116, 32, 33, 34, 35, 36, 37, 38, 80, 69, 70, 83, 71, 72, 73, 74, 75, 76, 77, 135, 136, 80, 33, 34, 35, 36, 37, 38, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 31, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 13, 134, 135, 136, 137, 138, 139, 140, 141, 142, 3, 4, 5, 6, 7, 148, 149, 150, 82, 12, 13, 160, 15, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 8, 44, 45, 46, 47, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 80, 33, 34, 35, 36, 50, 51, 13, 9, 10, 11, 56, 128, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 1, 70, 71, 72, 73, 59, 60, 37, 38, 78, 79, 80, 82, 82, 106, 85, 108, 86, 9, 10, 11, 161, 80, 1, 2, 44, 45, 46, 47, 48, 49, 50, 51, 52, 9, 10, 11, 106, 156, 30, 8, 32, 33, 34, 35, 36, 13, 116, 8, 153, 154, 155, 8, 122, 158, 30, 125, 126, 116, 117, 118, 119, 120, 121, 31, 134, 135, 156, 137, 138, 139, 140, 141, 142, 143, 145, 146, 8, 8, 133, 149, 150, 37, 38, 153, 154, 155, 9, 10, 11, 159, 8, 161, 162, 8, 164, 74, 75, 76, 77, 8, 13, 80, 0, 1, 2, 84, 158, 30, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 13, 98, 9, 10, 11, 9, 103, 104, 105, 106, 8, 70, 109, 110, 9, 10, 11, 8, 115, 116, 117, 118, 70, 30, 31, 37, 38, 124, 31, 8, 127, 128, 129, 130, 8, 30, 156, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 116, 117, 118, 119, 120, 121, 8, 82, 8, 74, 156, 157, 158, 33, 34, 80, 1, 2, 8, 84, 163, 82, 87, 88, 89, 133, 91, 70, 93, 152, 95, 108, 82, 98, 158, 8, 113, 160, 103, 104, 105, 106, 13, 108, 109, 110, 123, 122, 113, 157, 115, 116, 117, 118, 9, 10, 11, 156, 71, 124, 157, 122, 127, 128, 129, 130, 37, 38, 8, 82, 160, 156, 13, 134, 156, 30, 156, 32, 33, 34, 35, 158, 157, 148, 159, 122, 161, 80, 1, 74, 133, 156, 157, 158, 156, 80, 1, 2, 159, 84, 161, 82, 87, 88, 89, 157, 91, 157, 93, 122, 95, 161, 106, 98, 108, 100, 101, 102, 103, 104, 105, 106, 159, 116, 109, 110, 9, 10, 11, 13, 115, 116, 117, 118, 9, 10, 11, 160, 16, 124, 81, 122, 127, 128, 129, 130, 159, 30, 161, 32, 33, 34, 13, 134, 156, 30, 156, 32, 33, 153, 153, 154, 155, 70, 9, 10, 11, 80, 80, 74, 160, 156, 157, 158, 14, 80, 37, 38, 159, 84, 161, 152, 87, 88, 89, 30, 91, 160, 93, 14, 95, 37, 38, 98, 16, 100, 101, 102, 103, 104, 105, 106, 70, 82, 109, 110, 82, 33, 34, 35, 115, 116, 117, 118, 16, 1, 2, 10, 11, 124, 160, 85, 127, 128, 129, 130, 9, 10, 11, 82, 11, 14, 157, 9, 10, 11, 161, 30, 85, 53, 54, 55, 154, 57, 157, 31, 122, 30, 161, 30, 157, 156, 157, 158, 30, 69, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 144, 57, 161, 159, 16, 161, 1, 2, 74, 157, 16, 152, 157, 69, 80, 116, 161, 144, 84, 69, 70, 87, 88, 89, 16, 91, 16, 93, 161, 95, 75, 76, 98, 30, 135, 136, 31, 103, 104, 105, 1, 2, 31, 109, 110, 9, 10, 11, 31, 115, 116, 50, 51, 52, 50, 51, 52, 31, 124, 160, 75, 76, 101, 102, 111, 112, 30, 156, 157, 31, 31, 156, 157, 156, 157, 31, 31, 57, 38, 74, 33, 69, 80, 70, 70, 80, 70, 89, 70, 84, 156, 157, 87, 88, 89, 70, 91, 70, 93, 70, 95, 70, 96, 98, 71, 77, 82, 85, 103, 104, 105, 1, 2, 74, 109, 110, 82, 82, 97, 80, 115, 116, 85, 84, 92, 106, 87, 88, 89, 124, 91, 90, 93, 133, 95, 128, 94, 98, 147, 116, 97, 31, 103, 104, 105, 1, 2, 97, 109, 110, 97, 97, 100, 144, 115, 116, 100, 106, 128, 113, 161, 156, 157, 124, -1, -1, 151, -1, -1, 114, -1, -1, -1, -1, -1, 31, -1, -1, -1, 131, 131, -1, -1, -1, 74, -1, -1, -1, -1, 132, 80, 133, 133, 133, 84, 156, 157, 87, 88, 89, -1, 91, -1, 93, -1, 95, 144, -1, 98, -1, 147, 147, 147, 103, 104, 105, 1, 2, 74, 109, 110, 147, 147, 147, 80, 115, 116, 147, 84, 151, 153, 87, 88, 89, 124, 91, 31, 93, 152, 95, 156, 156, 98, 156, 156, 156, 156, 103, 104, 105, 156, 156, 156, 109, 110, 50, 51, 156, 156, 115, 116, 56, 156, 58, 156, 156, 156, 157, 124, 156, 156, 156, 156, 156, 156, 70, 156, 156, 156, 156, 156, 156, 156, 78, 79, 156, 158, 157, 157, 74, 157, 86, 157, 157, 157, 80, 157, 157, 157, 84, 156, 157, 87, 88, 89, 157, 91, 157, 93, 157, 95, 157, 157, 98, 158, 158, 158, 158, 103, 104, 105, 50, 51, 158, 109, 110, 158, 56, 158, 58, 115, 116, 158, 158, 158, 158, 158, 158, 158, 124, 135, 70, 137, 138, 139, 140, 141, 142, 143, 78, 79, 158, 158, 158, 149, 150, 158, 86, 158, 158, 158, 158, 158, 164, 159, 158, 158, 158, 158, 158, -1, 156, 157, 159, 162, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, -1, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 135, 160, 137, 138, 139, 140, 141, 142, 143, 160, 160, 160, 160, 160, 149, 150, 160, 160, 163, -1, 162, -1, 163, 163, 159, 163, 163, 163, -1, 163, 163, 163, 163, 163, 163, 163, 163, 163); - protected $actionBase = array(0, 229, 310, 390, 470, 103, 325, 325, 784, -2, -2, 149, -2, -2, -2, 660, 765, 799, 765, 589, 694, 870, 870, 870, 252, 404, 404, 404, 514, 177, 177, 918, 434, 118, 295, 313, 240, 491, 491, 491, 491, 138, 138, 491, 491, 491, 491, 491, 491, 491, 491, 491, 491, 491, 491, 491, 491, 491, 491, 491, 491, 491, 491, 491, 491, 491, 491, 491, 491, 491, 491, 491, 491, 491, 491, 491, 491, 491, 491, 491, 491, 491, 491, 491, 491, 491, 491, 491, 491, 491, 491, 491, 491, 491, 491, 491, 491, 491, 491, 491, 491, 491, 491, 491, 491, 491, 491, 491, 491, 491, 491, 491, 491, 491, 491, 491, 491, 491, 491, 491, 491, 491, 491, 491, 491, 491, 491, 491, 491, 491, 491, 491, 491, 491, 491, 491, 89, 206, 773, 550, 535, 775, 776, 777, 912, 709, 913, 856, 857, 700, 858, 859, 862, 863, 864, 855, 865, 935, 866, 599, 599, 599, 599, 599, 599, 599, 599, 599, 599, 599, 599, 322, 592, 285, 319, 232, 44, 691, 691, 691, 691, 691, 691, 691, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 582, 530, 530, 530, 594, 860, 658, 926, 926, 926, 926, 926, 926, 926, 926, 926, 926, 926, 926, 926, 926, 926, 926, 926, 926, 926, 926, 926, 926, 926, 926, 926, 926, 926, 926, 926, 926, 926, 926, 926, 926, 926, 926, 926, 926, 926, 926, 926, 926, 926, 926, 500, -21, -21, 492, 702, 420, 355, 216, 549, 151, 26, 26, 331, 331, 331, 331, 331, 46, 46, 5, 5, 5, 5, 153, 188, 188, 188, 188, 121, 121, 121, 121, 314, 314, 394, 394, 362, 300, 298, 499, 499, 499, 499, 499, 499, 499, 499, 499, 499, 67, 656, 656, 659, 659, 522, 554, 554, 554, 554, 679, -59, -59, 381, 462, 462, 462, 528, 717, 854, 382, 382, 382, 382, 382, 382, 561, 561, 561, -3, -3, -3, 692, 115, 137, 115, 137, 678, 732, 450, 732, 338, 677, -15, 510, 810, 468, 707, 850, 711, 853, 572, 735, 267, 529, 654, 674, 463, 529, 529, 529, 529, 654, 610, 640, 608, 463, 529, 463, 718, 323, 496, 89, 570, 507, 675, 778, 293, 670, 780, 290, 373, 332, 566, 278, 435, 733, 781, 914, 917, 385, 715, 675, 675, 675, 352, 511, 278, -8, 605, 605, 605, 605, 156, 605, 605, 605, 605, 251, 276, 375, 402, 779, 657, 657, 690, 872, 869, 869, 657, 689, 657, 690, 874, 874, 874, 874, 657, 657, 657, 657, 869, 869, 869, 688, 869, 239, 703, 704, 704, 874, 742, 743, 657, 657, 712, 869, 869, 869, 712, 695, 874, 701, 741, 277, 869, 874, 672, 689, 672, 657, 701, 672, 689, 689, 672, 22, 666, 668, 873, 875, 887, 790, 662, 685, 879, 880, 876, 878, 871, 699, 744, 745, 497, 669, 671, 673, 680, 719, 682, 713, 674, 667, 667, 667, 655, 720, 655, 667, 667, 667, 667, 667, 667, 667, 667, 916, 646, 731, 714, 653, 749, 553, 573, 791, 664, 811, 900, 893, 867, 919, 881, 898, 655, 920, 739, 247, 643, 882, 783, 786, 655, 883, 655, 792, 655, 902, 812, 686, 813, 814, 667, 910, 921, 923, 924, 925, 927, 928, 929, 930, 684, 931, 750, 696, 894, 299, 877, 718, 729, 705, 788, 751, 820, 328, 932, 823, 655, 655, 794, 785, 655, 795, 756, 740, 890, 757, 895, 933, 664, 708, 896, 655, 706, 825, 934, 328, 681, 683, 888, 661, 761, 886, 911, 885, 796, 649, 663, 829, 830, 831, 693, 763, 891, 892, 889, 764, 803, 665, 805, 697, 832, 807, 884, 768, 833, 834, 899, 676, 730, 710, 698, 687, 809, 835, 897, 769, 770, 771, 848, 772, 849, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 138, 138, 138, 138, -2, -2, -2, -2, 0, 0, -2, 0, 0, 0, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 0, 0, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 599, 599, 599, 599, 599, 599, 599, 599, 599, 599, 599, 599, 599, 599, 599, 599, 599, 599, 599, 599, 599, 599, 599, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 599, -21, -21, -21, -21, 599, -21, -21, -21, -21, -21, -21, -21, 599, 599, 599, 599, 599, 599, 599, 599, 599, 599, 599, 599, 599, 599, 599, 599, 599, 599, -21, 599, 599, 599, -21, 382, -21, 382, 382, 382, 382, 382, 382, 382, 382, 382, 382, 382, 382, 382, 382, 382, 382, 382, 382, 382, 382, 382, 382, 382, 382, 382, 382, 382, 382, 382, 382, 382, 382, 382, 382, 382, 382, 382, 382, 382, 382, 382, 382, 382, 382, 599, 0, 0, 599, -21, 599, -21, 599, -21, -21, 599, 599, 599, 599, 599, 599, 599, -21, -21, -21, -21, -21, -21, 0, 561, 561, 561, 561, -21, -21, -21, -21, 382, 382, 382, 382, 382, 382, 259, 382, 382, 382, 382, 382, 382, 382, 382, 382, 382, 382, 561, 561, -3, -3, 382, 382, 382, 382, 382, 259, 382, 382, 463, 689, 689, 689, 137, 137, 137, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 137, 463, 0, 463, 0, 382, 463, 689, 463, 657, 137, 689, 689, 463, 869, 616, 616, 616, 616, 328, 278, 0, 0, 689, 689, 0, 0, 0, 0, 0, 689, 0, 0, 0, 0, 0, 0, 869, 0, 0, 0, 0, 0, 667, 247, 0, 705, 335, 0, 0, 0, 0, 0, 0, 705, 335, 347, 347, 0, 684, 667, 667, 667, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 328); - protected $actionDefault = array(3, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 544, 544, 499, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 299, 299, 299, 32767, 32767, 32767, 532, 532, 532, 532, 532, 532, 532, 532, 532, 532, 532, 32767, 32767, 32767, 32767, 32767, 32767, 383, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 389, 549, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 364, 365, 367, 368, 298, 552, 533, 247, 390, 548, 297, 249, 327, 503, 32767, 32767, 32767, 329, 122, 258, 203, 502, 125, 296, 234, 382, 384, 328, 303, 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 302, 458, 361, 360, 359, 460, 32767, 459, 496, 496, 499, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 325, 487, 486, 326, 456, 330, 457, 333, 461, 464, 331, 332, 349, 350, 347, 348, 351, 462, 463, 480, 481, 478, 479, 301, 352, 353, 354, 355, 482, 483, 484, 485, 32767, 32767, 543, 543, 32767, 32767, 282, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 340, 341, 471, 472, 32767, 238, 238, 238, 238, 283, 238, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 335, 336, 334, 466, 467, 465, 432, 32767, 32767, 32767, 434, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 504, 32767, 32767, 32767, 32767, 32767, 517, 421, 171, 32767, 413, 32767, 171, 171, 171, 171, 32767, 222, 224, 167, 32767, 171, 32767, 490, 32767, 32767, 32767, 32767, 522, 345, 32767, 32767, 116, 32767, 32767, 32767, 559, 32767, 517, 32767, 116, 32767, 32767, 32767, 32767, 358, 337, 338, 339, 32767, 32767, 521, 515, 474, 475, 476, 477, 32767, 468, 469, 470, 473, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 429, 435, 435, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 520, 519, 32767, 414, 498, 188, 186, 186, 32767, 208, 208, 32767, 32767, 190, 491, 510, 32767, 190, 173, 32767, 400, 175, 498, 32767, 32767, 240, 32767, 240, 32767, 400, 240, 32767, 32767, 240, 32767, 415, 439, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 379, 380, 493, 506, 32767, 507, 32767, 413, 343, 344, 346, 322, 32767, 324, 369, 370, 371, 372, 373, 374, 375, 377, 32767, 419, 32767, 422, 32767, 32767, 32767, 257, 32767, 557, 32767, 32767, 306, 557, 32767, 32767, 32767, 551, 32767, 32767, 300, 32767, 32767, 32767, 32767, 253, 32767, 169, 32767, 541, 32767, 558, 32767, 515, 32767, 342, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 516, 32767, 32767, 32767, 32767, 229, 32767, 452, 32767, 116, 32767, 32767, 32767, 189, 32767, 32767, 304, 248, 32767, 32767, 550, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 114, 32767, 170, 32767, 32767, 32767, 191, 32767, 32767, 515, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 295, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 515, 32767, 32767, 233, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 415, 32767, 276, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 127, 127, 3, 127, 127, 260, 3, 260, 127, 260, 260, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 216, 219, 208, 208, 164, 127, 127, 268); - protected $goto = array(166, 140, 140, 140, 166, 187, 168, 144, 147, 141, 142, 143, 149, 163, 163, 163, 163, 144, 144, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 138, 159, 160, 161, 162, 184, 139, 185, 494, 495, 377, 496, 500, 501, 502, 503, 504, 505, 506, 507, 970, 164, 145, 146, 148, 171, 176, 186, 203, 253, 256, 258, 260, 263, 264, 265, 266, 267, 268, 269, 277, 278, 279, 280, 303, 304, 328, 329, 330, 394, 395, 396, 543, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 150, 151, 152, 167, 153, 169, 154, 204, 170, 155, 156, 157, 205, 158, 136, 621, 561, 757, 561, 561, 561, 561, 561, 561, 561, 561, 561, 561, 561, 561, 561, 561, 561, 561, 561, 561, 561, 561, 561, 561, 561, 561, 561, 561, 561, 561, 561, 561, 561, 561, 561, 561, 561, 561, 561, 561, 561, 561, 561, 561, 561, 561, 1113, 629, 1113, 1113, 1113, 1113, 1113, 1113, 1113, 1113, 1113, 1113, 1113, 1113, 1113, 1113, 1113, 1113, 1113, 1113, 1113, 1113, 1113, 1113, 1113, 1113, 1113, 1113, 1113, 1113, 1113, 1113, 1113, 1113, 1113, 1113, 1113, 1113, 1113, 1113, 1113, 1113, 1113, 1113, 1113, 1113, 758, 520, 531, 509, 656, 556, 1183, 750, 509, 592, 786, 1183, 888, 612, 613, 884, 617, 618, 624, 626, 631, 633, 817, 855, 855, 855, 855, 850, 856, 174, 891, 891, 1205, 1205, 177, 178, 179, 401, 402, 403, 404, 173, 202, 206, 208, 257, 259, 261, 262, 270, 271, 272, 273, 274, 275, 281, 282, 283, 284, 305, 306, 331, 332, 333, 406, 407, 408, 409, 175, 180, 254, 255, 181, 182, 183, 498, 498, 498, 498, 498, 498, 861, 498, 498, 498, 498, 498, 498, 498, 498, 498, 498, 510, 586, 538, 601, 602, 510, 545, 546, 547, 548, 549, 550, 551, 552, 554, 587, 1209, 560, 350, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 400, 607, 537, 537, 569, 533, 909, 535, 535, 497, 499, 525, 541, 570, 573, 584, 591, 298, 296, 296, 296, 298, 289, 299, 611, 378, 511, 614, 595, 947, 375, 511, 437, 437, 437, 437, 437, 437, 1163, 437, 437, 437, 437, 437, 437, 437, 437, 437, 437, 1077, 948, 338, 1175, 321, 1077, 898, 898, 898, 898, 606, 898, 898, 1217, 1217, 1202, 753, 576, 605, 756, 1077, 1077, 1077, 1077, 1077, 1077, 1069, 384, 384, 384, 391, 1217, 877, 859, 857, 859, 655, 466, 512, 886, 881, 753, 384, 753, 384, 968, 384, 895, 385, 588, 353, 414, 384, 1231, 1019, 542, 1197, 1197, 1197, 568, 1094, 386, 386, 386, 904, 915, 515, 1029, 19, 15, 372, 389, 915, 940, 448, 450, 632, 340, 1216, 1216, 1114, 615, 938, 840, 555, 775, 386, 913, 1070, 1073, 1074, 399, 1069, 1182, 660, 23, 1216, 773, 1182, 544, 603, 1066, 1219, 1071, 1174, 1071, 519, 1199, 1199, 1199, 1089, 1088, 1072, 343, 523, 534, 519, 519, 772, 351, 352, 13, 579, 583, 627, 1061, 388, 782, 562, 771, 515, 783, 1181, 3, 4, 918, 956, 865, 451, 574, 1160, 464, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 514, 529, 0, 0, 0, 0, 514, 0, 529, 0, 0, 0, 0, 610, 513, 516, 439, 440, 1067, 619, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 780, 1224, 0, 0, 0, 0, 0, 524, 0, 0, 0, 0, 0, 0, 0, 0, 0, 778, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 301, 301); - protected $gotoCheck = array(43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 57, 69, 15, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 128, 9, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 16, 102, 32, 69, 32, 32, 120, 6, 69, 32, 29, 120, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 50, 69, 69, 69, 69, 69, 69, 27, 77, 77, 77, 77, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 119, 119, 119, 119, 119, 119, 33, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 67, 110, 67, 67, 119, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 142, 57, 72, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 51, 51, 51, 51, 51, 51, 84, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 5, 5, 5, 5, 5, 5, 5, 63, 46, 124, 63, 129, 98, 63, 124, 57, 57, 57, 57, 57, 57, 133, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 98, 127, 82, 127, 57, 57, 57, 57, 57, 49, 57, 57, 144, 144, 140, 11, 40, 40, 14, 57, 57, 57, 57, 57, 57, 82, 13, 13, 13, 48, 144, 14, 14, 14, 14, 14, 57, 14, 14, 14, 11, 13, 11, 13, 102, 13, 79, 11, 70, 70, 70, 13, 13, 103, 2, 9, 9, 9, 2, 34, 125, 125, 125, 81, 13, 13, 34, 34, 34, 34, 17, 13, 8, 8, 8, 8, 18, 143, 143, 8, 8, 8, 9, 34, 25, 125, 85, 82, 82, 82, 125, 82, 121, 74, 34, 143, 24, 121, 47, 34, 116, 143, 82, 82, 82, 47, 121, 121, 121, 126, 126, 82, 58, 58, 58, 47, 47, 23, 72, 72, 58, 62, 62, 62, 114, 12, 23, 12, 23, 13, 26, 121, 30, 30, 86, 100, 71, 65, 66, 132, 109, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 9, 9, -1, -1, -1, -1, 9, -1, 9, -1, -1, -1, -1, 13, 9, 9, 9, 9, 13, 13, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 9, 9, -1, -1, -1, -1, -1, 102, -1, -1, -1, -1, -1, -1, -1, -1, -1, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 5, 5); - protected $gotoBase = array(0, 0, -172, 0, 0, 353, 201, 0, 477, 149, 0, 110, 195, 117, 426, 112, 203, 140, 171, 0, 0, 0, 0, 168, 164, 157, 119, 27, 0, 205, -118, 0, -428, 266, 51, 0, 0, 0, 0, 0, 388, 0, 0, -24, 0, 0, 345, 484, 146, 133, 209, 75, 0, 0, 0, 0, 0, 107, 161, 0, 0, 0, 222, -77, 0, 106, 97, -343, 0, -94, 135, 123, -129, 0, 129, 0, 0, -50, 0, 143, 0, 159, 64, 0, 338, 132, 122, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 98, 0, 121, 0, 165, 156, 0, 0, 0, 0, 0, 87, 273, 259, 0, 0, 114, 0, 150, 0, 0, -5, -91, 200, 0, 0, 84, 154, 202, 77, -48, 178, 0, 0, 93, 187, 0, 0, 0, 0, 0, 0, 136, 0, 286, 167, 102, 0, 0); - protected $gotoDefault = array(-32768, 468, 664, 2, 665, 835, 740, 748, 598, 482, 630, 582, 380, 1193, 792, 793, 794, 381, 368, 483, 379, 410, 405, 781, 774, 776, 784, 172, 411, 787, 1, 789, 518, 825, 1020, 365, 797, 366, 590, 799, 527, 801, 802, 137, 382, 383, 528, 484, 390, 577, 816, 276, 387, 818, 367, 819, 828, 371, 465, 455, 460, 530, 557, 609, 432, 447, 571, 565, 536, 1086, 566, 864, 349, 872, 661, 880, 883, 485, 558, 894, 452, 902, 1099, 397, 908, 914, 919, 291, 922, 417, 412, 585, 927, 928, 5, 932, 622, 623, 8, 312, 955, 599, 969, 420, 1039, 1041, 486, 487, 522, 459, 508, 526, 488, 1062, 441, 413, 1065, 433, 489, 490, 434, 435, 1083, 355, 1168, 354, 449, 320, 1155, 580, 1118, 456, 1208, 1164, 348, 491, 492, 376, 1187, 392, 1203, 438, 1210, 1218, 344, 540, 567); - protected $ruleToNonTerminal = array(0, 1, 3, 3, 2, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 8, 8, 9, 10, 11, 11, 12, 12, 13, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 18, 18, 19, 19, 21, 21, 17, 17, 22, 22, 23, 23, 24, 24, 25, 25, 20, 20, 26, 28, 28, 29, 30, 30, 32, 31, 31, 31, 31, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 14, 14, 54, 54, 56, 55, 55, 48, 48, 58, 58, 59, 59, 60, 60, 61, 61, 15, 16, 16, 16, 64, 64, 64, 65, 65, 68, 68, 66, 66, 70, 70, 41, 41, 50, 50, 53, 53, 53, 52, 52, 71, 42, 42, 42, 42, 72, 72, 73, 73, 74, 74, 39, 39, 35, 35, 75, 37, 37, 76, 36, 36, 38, 38, 49, 49, 49, 62, 62, 78, 78, 79, 79, 81, 81, 81, 80, 80, 63, 63, 82, 82, 82, 83, 83, 84, 84, 84, 44, 44, 85, 85, 85, 45, 45, 86, 86, 87, 87, 67, 88, 88, 88, 88, 93, 93, 94, 94, 95, 95, 95, 95, 95, 96, 97, 97, 92, 92, 89, 89, 91, 91, 99, 99, 98, 98, 98, 98, 98, 98, 90, 90, 101, 100, 100, 46, 46, 40, 40, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 34, 34, 47, 47, 106, 106, 107, 107, 107, 107, 113, 102, 102, 109, 109, 115, 115, 116, 117, 118, 118, 118, 118, 118, 118, 118, 69, 69, 57, 57, 57, 57, 103, 103, 122, 122, 119, 119, 123, 123, 123, 123, 104, 104, 104, 108, 108, 108, 114, 114, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 27, 27, 27, 27, 27, 27, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 112, 112, 105, 105, 105, 105, 129, 129, 132, 132, 131, 131, 133, 133, 51, 51, 51, 51, 135, 135, 134, 134, 134, 134, 134, 136, 136, 121, 121, 124, 124, 120, 120, 138, 137, 137, 137, 137, 125, 125, 125, 125, 111, 111, 126, 126, 126, 126, 77, 139, 139, 140, 140, 140, 110, 110, 141, 141, 142, 142, 142, 142, 142, 127, 127, 127, 127, 144, 145, 143, 143, 143, 143, 143, 143, 143, 146, 146, 146); - protected $ruleToLength = array(1, 1, 2, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 5, 4, 3, 4, 2, 3, 1, 1, 7, 6, 3, 1, 3, 1, 3, 1, 1, 3, 1, 3, 1, 2, 3, 1, 3, 3, 1, 3, 2, 0, 1, 1, 1, 1, 1, 3, 5, 8, 3, 5, 9, 3, 2, 3, 2, 3, 2, 3, 3, 3, 3, 1, 2, 2, 5, 7, 9, 5, 6, 3, 3, 2, 2, 1, 1, 1, 0, 2, 8, 0, 4, 1, 3, 0, 1, 0, 1, 0, 1, 1, 1, 10, 7, 6, 5, 1, 2, 2, 0, 2, 0, 2, 0, 2, 1, 3, 1, 4, 1, 4, 1, 1, 4, 1, 3, 3, 3, 4, 4, 5, 0, 2, 4, 3, 1, 1, 1, 4, 0, 2, 3, 0, 2, 4, 0, 2, 0, 3, 1, 2, 1, 1, 0, 1, 3, 4, 6, 1, 1, 1, 0, 1, 0, 2, 2, 3, 3, 1, 3, 1, 2, 2, 3, 1, 1, 2, 4, 3, 1, 1, 3, 2, 0, 1, 3, 3, 9, 3, 1, 3, 0, 2, 4, 5, 4, 4, 4, 3, 1, 1, 1, 3, 1, 1, 0, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 3, 3, 1, 0, 1, 1, 3, 3, 4, 4, 1, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 1, 3, 5, 4, 3, 4, 4, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 3, 2, 1, 2, 10, 11, 3, 3, 2, 4, 4, 3, 4, 4, 4, 4, 7, 3, 2, 0, 4, 1, 3, 2, 1, 2, 2, 4, 6, 2, 2, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 4, 4, 0, 2, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 2, 1, 3, 1, 4, 3, 1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 5, 4, 4, 3, 1, 3, 1, 1, 3, 3, 0, 2, 0, 1, 3, 1, 3, 1, 1, 1, 1, 1, 6, 4, 3, 4, 2, 4, 4, 1, 3, 1, 2, 1, 1, 4, 1, 1, 3, 6, 4, 4, 4, 4, 1, 4, 0, 1, 1, 3, 1, 1, 4, 3, 1, 1, 1, 0, 0, 2, 3, 1, 3, 1, 4, 2, 2, 2, 2, 1, 2, 1, 1, 1, 4, 3, 3, 3, 6, 3, 1, 1, 1); - protected function initReduceCallbacks() - { - $this->reduceCallbacks = [0 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 1 => function ($stackPos) { - $this->semValue = $this->handleNamespaces($this->semStack[$stackPos - (1 - 1)]); - }, 2 => function ($stackPos) { - if (\is_array($this->semStack[$stackPos - (2 - 2)])) { - $this->semValue = \array_merge($this->semStack[$stackPos - (2 - 1)], $this->semStack[$stackPos - (2 - 2)]); - } else { - $this->semStack[$stackPos - (2 - 1)][] = $this->semStack[$stackPos - (2 - 2)]; - $this->semValue = $this->semStack[$stackPos - (2 - 1)]; - } - }, 3 => function ($stackPos) { - $this->semValue = array(); - }, 4 => function ($stackPos) { - $startAttributes = $this->lookaheadStartAttributes; - if (isset($startAttributes['comments'])) { - $nop = new Stmt\Nop($this->createCommentNopAttributes($startAttributes['comments'])); - } else { - $nop = null; - } +class Php7 extends \PHPUnitPHAR\PhpParser\ParserAbstract +{ + public const YYERRTOK = 256; + public const T_THROW = 257; + public const T_INCLUDE = 258; + public const T_INCLUDE_ONCE = 259; + public const T_EVAL = 260; + public const T_REQUIRE = 261; + public const T_REQUIRE_ONCE = 262; + public const T_LOGICAL_OR = 263; + public const T_LOGICAL_XOR = 264; + public const T_LOGICAL_AND = 265; + public const T_PRINT = 266; + public const T_YIELD = 267; + public const T_DOUBLE_ARROW = 268; + public const T_YIELD_FROM = 269; + public const T_PLUS_EQUAL = 270; + public const T_MINUS_EQUAL = 271; + public const T_MUL_EQUAL = 272; + public const T_DIV_EQUAL = 273; + public const T_CONCAT_EQUAL = 274; + public const T_MOD_EQUAL = 275; + public const T_AND_EQUAL = 276; + public const T_OR_EQUAL = 277; + public const T_XOR_EQUAL = 278; + public const T_SL_EQUAL = 279; + public const T_SR_EQUAL = 280; + public const T_POW_EQUAL = 281; + public const T_COALESCE_EQUAL = 282; + public const T_COALESCE = 283; + public const T_BOOLEAN_OR = 284; + public const T_BOOLEAN_AND = 285; + public const T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG = 286; + public const T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG = 287; + public const T_IS_EQUAL = 288; + public const T_IS_NOT_EQUAL = 289; + public const T_IS_IDENTICAL = 290; + public const T_IS_NOT_IDENTICAL = 291; + public const T_SPACESHIP = 292; + public const T_IS_SMALLER_OR_EQUAL = 293; + public const T_IS_GREATER_OR_EQUAL = 294; + public const T_SL = 295; + public const T_SR = 296; + public const T_INSTANCEOF = 297; + public const T_INC = 298; + public const T_DEC = 299; + public const T_INT_CAST = 300; + public const T_DOUBLE_CAST = 301; + public const T_STRING_CAST = 302; + public const T_ARRAY_CAST = 303; + public const T_OBJECT_CAST = 304; + public const T_BOOL_CAST = 305; + public const T_UNSET_CAST = 306; + public const T_POW = 307; + public const T_NEW = 308; + public const T_CLONE = 309; + public const T_EXIT = 310; + public const T_IF = 311; + public const T_ELSEIF = 312; + public const T_ELSE = 313; + public const T_ENDIF = 314; + public const T_LNUMBER = 315; + public const T_DNUMBER = 316; + public const T_STRING = 317; + public const T_STRING_VARNAME = 318; + public const T_VARIABLE = 319; + public const T_NUM_STRING = 320; + public const T_INLINE_HTML = 321; + public const T_ENCAPSED_AND_WHITESPACE = 322; + public const T_CONSTANT_ENCAPSED_STRING = 323; + public const T_ECHO = 324; + public const T_DO = 325; + public const T_WHILE = 326; + public const T_ENDWHILE = 327; + public const T_FOR = 328; + public const T_ENDFOR = 329; + public const T_FOREACH = 330; + public const T_ENDFOREACH = 331; + public const T_DECLARE = 332; + public const T_ENDDECLARE = 333; + public const T_AS = 334; + public const T_SWITCH = 335; + public const T_MATCH = 336; + public const T_ENDSWITCH = 337; + public const T_CASE = 338; + public const T_DEFAULT = 339; + public const T_BREAK = 340; + public const T_CONTINUE = 341; + public const T_GOTO = 342; + public const T_FUNCTION = 343; + public const T_FN = 344; + public const T_CONST = 345; + public const T_RETURN = 346; + public const T_TRY = 347; + public const T_CATCH = 348; + public const T_FINALLY = 349; + public const T_USE = 350; + public const T_INSTEADOF = 351; + public const T_GLOBAL = 352; + public const T_STATIC = 353; + public const T_ABSTRACT = 354; + public const T_FINAL = 355; + public const T_PRIVATE = 356; + public const T_PROTECTED = 357; + public const T_PUBLIC = 358; + public const T_READONLY = 359; + public const T_VAR = 360; + public const T_UNSET = 361; + public const T_ISSET = 362; + public const T_EMPTY = 363; + public const T_HALT_COMPILER = 364; + public const T_CLASS = 365; + public const T_TRAIT = 366; + public const T_INTERFACE = 367; + public const T_ENUM = 368; + public const T_EXTENDS = 369; + public const T_IMPLEMENTS = 370; + public const T_OBJECT_OPERATOR = 371; + public const T_NULLSAFE_OBJECT_OPERATOR = 372; + public const T_LIST = 373; + public const T_ARRAY = 374; + public const T_CALLABLE = 375; + public const T_CLASS_C = 376; + public const T_TRAIT_C = 377; + public const T_METHOD_C = 378; + public const T_FUNC_C = 379; + public const T_LINE = 380; + public const T_FILE = 381; + public const T_START_HEREDOC = 382; + public const T_END_HEREDOC = 383; + public const T_DOLLAR_OPEN_CURLY_BRACES = 384; + public const T_CURLY_OPEN = 385; + public const T_PAAMAYIM_NEKUDOTAYIM = 386; + public const T_NAMESPACE = 387; + public const T_NS_C = 388; + public const T_DIR = 389; + public const T_NS_SEPARATOR = 390; + public const T_ELLIPSIS = 391; + public const T_NAME_FULLY_QUALIFIED = 392; + public const T_NAME_QUALIFIED = 393; + public const T_NAME_RELATIVE = 394; + public const T_ATTRIBUTE = 395; + protected int $tokenToSymbolMapSize = 396; + protected int $actionTableSize = 1268; + protected int $gotoTableSize = 730; + protected int $invalidSymbol = 168; + protected int $errorSymbol = 1; + protected int $defaultAction = -32766; + protected int $unexpectedTokenRule = 32767; + protected int $YY2TBLSTATE = 437; + protected int $numNonLeafStates = 743; + protected array $symbolToName = array("EOF", "error", "T_THROW", "T_INCLUDE", "T_INCLUDE_ONCE", "T_EVAL", "T_REQUIRE", "T_REQUIRE_ONCE", "','", "T_LOGICAL_OR", "T_LOGICAL_XOR", "T_LOGICAL_AND", "T_PRINT", "T_YIELD", "T_DOUBLE_ARROW", "T_YIELD_FROM", "'='", "T_PLUS_EQUAL", "T_MINUS_EQUAL", "T_MUL_EQUAL", "T_DIV_EQUAL", "T_CONCAT_EQUAL", "T_MOD_EQUAL", "T_AND_EQUAL", "T_OR_EQUAL", "T_XOR_EQUAL", "T_SL_EQUAL", "T_SR_EQUAL", "T_POW_EQUAL", "T_COALESCE_EQUAL", "'?'", "':'", "T_COALESCE", "T_BOOLEAN_OR", "T_BOOLEAN_AND", "'|'", "'^'", "T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG", "T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG", "T_IS_EQUAL", "T_IS_NOT_EQUAL", "T_IS_IDENTICAL", "T_IS_NOT_IDENTICAL", "T_SPACESHIP", "'<'", "T_IS_SMALLER_OR_EQUAL", "'>'", "T_IS_GREATER_OR_EQUAL", "T_SL", "T_SR", "'+'", "'-'", "'.'", "'*'", "'/'", "'%'", "'!'", "T_INSTANCEOF", "'~'", "T_INC", "T_DEC", "T_INT_CAST", "T_DOUBLE_CAST", "T_STRING_CAST", "T_ARRAY_CAST", "T_OBJECT_CAST", "T_BOOL_CAST", "T_UNSET_CAST", "'@'", "T_POW", "'['", "T_NEW", "T_CLONE", "T_EXIT", "T_IF", "T_ELSEIF", "T_ELSE", "T_ENDIF", "T_LNUMBER", "T_DNUMBER", "T_STRING", "T_STRING_VARNAME", "T_VARIABLE", "T_NUM_STRING", "T_INLINE_HTML", "T_ENCAPSED_AND_WHITESPACE", "T_CONSTANT_ENCAPSED_STRING", "T_ECHO", "T_DO", "T_WHILE", "T_ENDWHILE", "T_FOR", "T_ENDFOR", "T_FOREACH", "T_ENDFOREACH", "T_DECLARE", "T_ENDDECLARE", "T_AS", "T_SWITCH", "T_MATCH", "T_ENDSWITCH", "T_CASE", "T_DEFAULT", "T_BREAK", "T_CONTINUE", "T_GOTO", "T_FUNCTION", "T_FN", "T_CONST", "T_RETURN", "T_TRY", "T_CATCH", "T_FINALLY", "T_USE", "T_INSTEADOF", "T_GLOBAL", "T_STATIC", "T_ABSTRACT", "T_FINAL", "T_PRIVATE", "T_PROTECTED", "T_PUBLIC", "T_READONLY", "T_VAR", "T_UNSET", "T_ISSET", "T_EMPTY", "T_HALT_COMPILER", "T_CLASS", "T_TRAIT", "T_INTERFACE", "T_ENUM", "T_EXTENDS", "T_IMPLEMENTS", "T_OBJECT_OPERATOR", "T_NULLSAFE_OBJECT_OPERATOR", "T_LIST", "T_ARRAY", "T_CALLABLE", "T_CLASS_C", "T_TRAIT_C", "T_METHOD_C", "T_FUNC_C", "T_LINE", "T_FILE", "T_START_HEREDOC", "T_END_HEREDOC", "T_DOLLAR_OPEN_CURLY_BRACES", "T_CURLY_OPEN", "T_PAAMAYIM_NEKUDOTAYIM", "T_NAMESPACE", "T_NS_C", "T_DIR", "T_NS_SEPARATOR", "T_ELLIPSIS", "T_NAME_FULLY_QUALIFIED", "T_NAME_QUALIFIED", "T_NAME_RELATIVE", "T_ATTRIBUTE", "';'", "']'", "'('", "')'", "'{'", "'}'", "'`'", "'\"'", "'\$'"); + protected array $tokenToSymbol = array(0, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 56, 166, 168, 167, 55, 168, 168, 161, 162, 53, 50, 8, 51, 52, 54, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 31, 159, 44, 16, 46, 30, 68, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 70, 168, 160, 36, 168, 165, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 163, 35, 164, 58, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 1, 2, 3, 4, 5, 6, 7, 9, 10, 11, 12, 13, 14, 15, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 32, 33, 34, 37, 38, 39, 40, 41, 42, 43, 45, 47, 48, 49, 57, 59, 60, 61, 62, 63, 64, 65, 66, 67, 69, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158); + protected array $action = array(133, 134, 135, 586, 136, 137, 0, 755, 756, 757, 138, 38, -32766, -32766, -32766, 151, -32766, -32766, -32766, -32767, -32767, -32767, -32767, 102, 103, 104, 105, 106, 1116, 1117, 1118, 1115, 1114, 1113, 1119, 749, 748, -32766, -32766, -32766, -32766, -32766, -32766, -32766, -32766, -32766, -32767, -32767, -32767, -32767, -32767, 1252, 841, -32766, 1331, 758, -32766, -32766, -32766, -32766, -599, -32766, -32766, -32766, 104, 105, 106, -599, 1315, 265, 139, 406, 762, 763, 764, 765, 994, -32766, 431, -32766, -32766, -16, -32766, 242, 1031, 819, 766, 767, 768, 769, 770, 771, 772, 773, 774, 775, 795, 587, 796, 797, 798, 799, 787, 788, 347, 348, 790, 791, 776, 777, 778, 780, 781, 782, 358, 822, 823, 824, 825, 826, 588, 783, 784, 589, 590, -32766, 807, 805, 806, 818, 802, 803, 839, 830, 591, 592, 801, 593, 594, 595, 596, 597, 598, 830, 461, 462, 463, 1040, 804, 599, 600, 945, 140, 2, 133, 134, 135, 586, 136, 137, 1064, 755, 756, 757, 138, 38, -328, -110, -110, 1335, 291, 23, -110, -32766, -32766, -32766, 1334, 35, -110, 1116, 1117, 1118, 1115, 1114, 1113, 1119, 616, -32766, 129, 749, 748, 107, 108, 109, -32766, 275, -32766, -32766, -32766, -32766, -32766, -32766, -32766, 832, 995, -194, 145, 110, 300, 758, 840, 75, -32766, -32766, -32766, 1360, 142, 328, 1361, -599, 328, -599, 253, 265, 139, 406, 762, 763, 764, 765, 82, -272, 431, -32766, 328, -32766, -32766, -32766, -32766, 819, 766, 767, 768, 769, 770, 771, 772, 773, 774, 775, 795, 587, 796, 797, 798, 799, 787, 788, 347, 348, 790, 791, 776, 777, 778, 780, 781, 782, 358, 822, 823, 824, 825, 826, 588, 783, 784, 589, 590, 834, 807, 805, 806, 818, 802, 803, 716, 311, 591, 592, 801, 593, 594, 595, 596, 597, 598, -78, 83, 84, 85, -85, 804, 599, 600, 313, 149, 779, 750, 751, 752, 753, 754, 729, 755, 756, 757, 792, 793, 37, -328, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 325, 275, 485, -32766, -32766, -32766, -58, -32766, -32766, -32766, 963, 964, 127, 110, -194, 965, 341, 758, -32766, -32766, -32766, 959, -85, 292, -32766, 1092, -32766, -32766, -32766, -32766, -32766, 759, 760, 761, 762, 763, 764, 765, -193, -32766, 828, -32766, -32766, -32766, -367, 431, -367, 819, 766, 767, 768, 769, 770, 771, 772, 773, 774, 775, 795, 817, 796, 797, 798, 799, 787, 788, 789, 816, 790, 791, 776, 777, 778, 780, 781, 782, 821, 822, 823, 824, 825, 826, 827, 783, 784, 785, 786, -552, 807, 805, 806, 818, 802, 803, 342, 329, 794, 800, 801, 808, 809, 811, 810, 812, 813, 1037, 866, 610, 867, -32766, 804, 815, 814, 50, 51, 52, 516, 53, 54, 835, 1247, 1246, 1248, 55, 56, -110, 57, 1040, 924, 1094, -110, 1040, -110, 292, 486, 749, 748, 307, 384, 383, -110, -110, -110, -110, -110, -110, -110, -110, 425, 924, 284, -552, -552, 372, 291, 838, 924, 1252, 719, 470, 471, 58, 59, -32766, -32766, 21, -550, 60, 560, 61, 247, 248, 62, 63, 64, 65, 66, 67, 68, 69, -552, 28, 267, 70, 446, 517, 720, 1108, -342, 1279, 1280, 518, -193, 839, 376, 836, -548, 1277, 42, 25, 519, 391, 520, 241, 521, 924, 522, 947, 1245, 523, 524, 914, 660, 26, 44, 45, 447, 379, 378, -32766, 46, 525, 1027, 1026, 1025, 1028, 370, 340, 442, 1285, -550, -550, 914, 1238, 947, 527, 528, 529, 839, 914, 839, 1040, 443, 1350, 1243, -550, 359, 531, 532, 444, 1266, 1267, 1268, 1269, 1263, 1264, 299, -556, 445, -550, -548, -548, 1270, 1265, 291, 1039, 1247, 1246, 1248, 300, 749, 748, 71, 364, 845, -548, 323, 324, 328, -153, -153, -153, 152, 1247, 1246, 1248, 926, -555, 914, -548, 714, 1063, 154, -32766, -153, 1093, -153, 155, -153, 741, -153, 156, -596, 28, 268, 36, 250, 926, -32766, -596, 377, 714, 679, 680, 926, 839, 1273, 75, 714, 1277, 288, 963, 964, 328, -547, 393, 526, 7, 1037, -57, 1040, 900, 959, -110, -110, -110, 32, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 1040, 158, 382, 383, 866, 1238, 867, 924, 749, 748, 1252, 33, 425, 926, 150, 409, 924, 714, -153, 531, 532, -87, 1266, 1267, 1268, 1269, 1263, 1264, 124, 1154, 1156, -84, -4, 924, 1270, 1265, 125, 721, -547, -547, -546, 130, 749, 748, 73, -32766, 724, 839, -78, 324, 328, 1245, 131, -547, 300, -590, 1037, -590, -32766, -32766, -32766, 144, -32766, 159, -32766, -554, -32766, -547, 160, -32766, 380, 381, 924, 161, -32766, -32766, -32766, 162, 1040, -32766, -32766, -32766, 385, 386, 163, 1245, -32766, 422, 651, 652, 914, 839, -32766, -32766, -32766, -32766, -32766, -73, -32766, 914, -32766, 284, 731, -32766, -546, -546, -72, 48, -32766, -32766, -32766, -596, -71, -596, -32766, -32766, 914, -70, -69, -546, -32766, 422, -68, -67, -66, 74, -110, -110, 141, -32766, -50, -110, 328, -546, -65, -46, -18, -110, 377, 148, 438, 274, 285, 730, 733, 298, -32766, 923, 147, 963, 964, 289, 290, -549, 526, 914, -302, -298, 280, 530, 959, -110, -110, -110, 132, 980, 281, 300, 941, 714, 75, 301, 302, -32766, 926, 286, 328, 287, 714, 1245, 334, 293, 10, 294, 275, 1362, -32766, -32766, -32766, 110, -32766, 926, -32766, 707, -32766, 714, -4, -32766, 146, 830, 126, 689, -32766, -32766, -32766, 705, 20, -32766, -32766, -32766, 924, 839, 682, 1245, -32766, 422, 1123, -549, -549, 649, -32766, -32766, -32766, -32766, -32766, 565, -32766, 661, -32766, 467, 926, -32766, -549, -32766, 714, 666, -32766, -32766, -32766, -32766, 496, 667, -32766, -32766, -32766, 1245, -549, 683, -32766, 422, 924, 571, -32766, -32766, -32766, 838, -32766, -32766, -32766, 306, -32766, 735, 1278, -32766, 308, 0, 960, 491, -32766, -32766, -32766, -32766, 0, 0, -32766, -32766, 0, 1245, 578, 0, -32766, 422, -546, 305, -32766, -32766, -32766, 312, -32766, -32766, -32766, 0, -32766, 914, 40, -32766, 0, 0, 1284, 1286, -32766, -32766, -32766, -511, 0, -501, -32766, -32766, 8, -250, -250, -250, -32766, 422, 614, 377, 24, 49, 28, 267, 374, -32766, 943, 41, 300, -275, 963, 964, 738, 739, 839, 526, 858, 914, 1277, 905, 900, 959, -110, -110, -110, 1004, 981, 988, 978, 989, -546, -546, 903, -249, -249, -249, 976, 28, 268, 377, 1274, 288, 1097, 1100, 1101, -546, 1098, 1099, 1105, 839, 963, 964, 926, 1277, 1238, 526, 714, -250, 850, -546, 900, 959, -110, -110, -110, 303, 304, 1301, 1319, 532, 1353, 1266, 1267, 1268, 1269, 1263, 1264, 654, -273, -584, 375, -583, -582, 1270, 1265, -556, -555, -554, -553, 1238, -495, 694, 926, 73, 128, 1, 714, -249, 324, 328, 29, 30, 39, 43, 532, 47, 1266, 1267, 1268, 1269, 1263, 1264, 72, 76, 77, 78, 79, 80, 1270, 1265, 81, 143, 153, -32766, 157, 245, 330, 695, 73, 1245, 359, 360, 361, 324, 328, 362, -32766, -32766, -32766, 363, -32766, 364, -32766, 365, -32766, 366, 367, -32766, 696, 697, 368, 369, -32766, -32766, -32766, 371, 439, 559, -32766, -32766, -272, 13, 14, 15, -32766, 422, 1247, 1246, 1248, 16, 18, 408, 284, -32766, 487, 488, 495, 498, 499, 500, 501, 505, 506, 507, 514, 576, 700, 1256, 1194, 1275, 1066, 1065, 1046, 1233, 1042, -277, -102, 12, 17, 27, 297, 407, 607, 611, 640, 706, 1198, 1251, 1195, 1332, 0, 34, 0, 322, 373, 715, 718, 722, 723, 725, 726, 727, 728, 732, 717, 0, 901, 1357, 1359, 861, 860, 869, 953, 996, 868, 1358, 952, 950, 951, 954, 1226, 934, 944, 932, 986, 987, 638, 1356, 1313, 1302, 1320, 1329, 0, 1211, 0, 0, 328); + protected array $actionCheck = array(2, 3, 4, 5, 6, 7, 0, 9, 10, 11, 12, 13, 9, 10, 11, 14, 9, 10, 11, 44, 45, 46, 47, 48, 49, 50, 51, 52, 116, 117, 118, 119, 120, 121, 122, 37, 38, 30, 116, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 1, 1, 9, 1, 57, 9, 10, 11, 137, 1, 9, 10, 11, 50, 51, 52, 8, 1, 71, 72, 73, 74, 75, 76, 77, 31, 30, 80, 32, 33, 31, 30, 14, 1, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 116, 128, 129, 130, 131, 132, 133, 82, 80, 136, 137, 138, 139, 140, 141, 142, 143, 144, 80, 129, 130, 131, 138, 150, 151, 152, 1, 154, 8, 2, 3, 4, 5, 6, 7, 162, 9, 10, 11, 12, 13, 8, 117, 118, 1, 161, 8, 122, 9, 10, 11, 8, 8, 128, 116, 117, 118, 119, 120, 121, 122, 51, 137, 8, 37, 38, 53, 54, 55, 30, 57, 32, 33, 34, 35, 36, 37, 38, 80, 159, 8, 8, 69, 158, 57, 159, 161, 9, 10, 11, 80, 163, 167, 83, 160, 167, 162, 8, 71, 72, 73, 74, 75, 76, 77, 163, 162, 80, 30, 167, 32, 33, 34, 35, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 156, 128, 129, 130, 131, 132, 133, 163, 8, 136, 137, 138, 139, 140, 141, 142, 143, 144, 16, 9, 10, 11, 31, 150, 151, 152, 8, 154, 2, 3, 4, 5, 6, 7, 163, 9, 10, 11, 12, 13, 30, 162, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 8, 57, 31, 9, 10, 11, 16, 9, 10, 11, 117, 118, 14, 69, 162, 122, 8, 57, 9, 10, 11, 128, 97, 30, 30, 1, 32, 33, 34, 35, 36, 71, 72, 73, 74, 75, 76, 77, 8, 30, 80, 32, 33, 34, 106, 80, 108, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 70, 128, 129, 130, 131, 132, 133, 8, 70, 136, 137, 138, 139, 140, 141, 142, 143, 144, 116, 106, 1, 108, 116, 150, 151, 152, 2, 3, 4, 5, 6, 7, 80, 155, 156, 157, 12, 13, 101, 15, 138, 1, 164, 106, 138, 108, 30, 163, 37, 38, 113, 106, 107, 116, 117, 118, 119, 120, 121, 122, 123, 116, 1, 161, 134, 135, 8, 161, 155, 1, 1, 31, 134, 135, 50, 51, 9, 10, 101, 70, 56, 85, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 163, 70, 71, 72, 73, 74, 31, 123, 164, 78, 79, 80, 162, 82, 8, 156, 70, 86, 87, 88, 89, 8, 91, 97, 93, 1, 95, 122, 80, 98, 99, 84, 75, 76, 103, 104, 105, 106, 107, 116, 109, 110, 119, 120, 121, 122, 115, 116, 8, 146, 134, 135, 84, 122, 122, 124, 125, 126, 82, 84, 82, 138, 8, 85, 116, 149, 161, 136, 137, 8, 139, 140, 141, 142, 143, 144, 145, 161, 8, 163, 134, 135, 151, 152, 161, 137, 155, 156, 157, 158, 37, 38, 161, 161, 8, 149, 165, 166, 167, 75, 76, 77, 14, 155, 156, 157, 159, 161, 84, 163, 163, 1, 14, 137, 90, 159, 92, 14, 94, 163, 96, 14, 1, 70, 71, 147, 148, 159, 116, 8, 106, 163, 75, 76, 159, 82, 1, 161, 163, 86, 30, 117, 118, 167, 70, 106, 122, 108, 116, 16, 138, 127, 128, 129, 130, 131, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 138, 14, 106, 107, 106, 122, 108, 1, 37, 38, 1, 14, 116, 159, 101, 102, 1, 163, 164, 136, 137, 31, 139, 140, 141, 142, 143, 144, 16, 59, 60, 31, 0, 1, 151, 152, 16, 31, 134, 135, 70, 16, 37, 38, 161, 74, 31, 82, 31, 166, 167, 80, 16, 149, 158, 160, 116, 162, 87, 88, 89, 16, 91, 16, 93, 161, 95, 163, 16, 98, 106, 107, 1, 16, 103, 104, 105, 16, 138, 74, 109, 110, 106, 107, 16, 80, 115, 116, 111, 112, 84, 82, 87, 88, 89, 124, 91, 31, 93, 84, 95, 161, 31, 98, 134, 135, 31, 70, 103, 104, 105, 160, 31, 162, 109, 110, 84, 31, 31, 149, 115, 116, 31, 31, 31, 154, 117, 118, 163, 124, 31, 122, 167, 163, 31, 31, 31, 128, 106, 31, 108, 31, 31, 31, 31, 113, 137, 31, 31, 117, 118, 37, 37, 70, 122, 84, 35, 35, 35, 127, 128, 129, 130, 131, 31, 159, 35, 158, 38, 163, 161, 134, 135, 74, 159, 35, 167, 35, 163, 80, 35, 37, 150, 37, 57, 83, 87, 88, 89, 69, 91, 159, 93, 92, 95, 163, 164, 98, 70, 80, 163, 77, 103, 104, 105, 80, 97, 74, 109, 110, 1, 82, 94, 80, 115, 116, 82, 134, 135, 113, 87, 88, 89, 124, 91, 89, 93, 90, 95, 97, 159, 98, 149, 85, 163, 96, 103, 104, 105, 74, 97, 100, 109, 110, 137, 80, 163, 100, 115, 116, 1, 153, 87, 88, 89, 155, 91, 124, 93, 133, 95, 164, 166, 98, 114, -1, 128, 102, 103, 104, 105, 74, -1, -1, 109, 110, -1, 80, 81, -1, 115, 116, 70, 132, 87, 88, 89, 132, 91, 124, 93, -1, 95, 84, 159, 98, -1, -1, 146, 146, 103, 104, 105, 149, -1, 149, 109, 110, 149, 100, 101, 102, 115, 116, 153, 106, 149, 70, 70, 71, 149, 124, 154, 159, 158, 162, 117, 118, 159, 159, 82, 122, 159, 84, 86, 159, 127, 128, 129, 130, 131, 159, 159, 159, 159, 159, 134, 135, 159, 100, 101, 102, 159, 70, 71, 106, 160, 30, 159, 159, 159, 149, 159, 159, 159, 82, 117, 118, 159, 86, 122, 122, 163, 164, 160, 163, 127, 128, 129, 130, 131, 134, 135, 160, 160, 137, 160, 139, 140, 141, 142, 143, 144, 160, 162, 161, 149, 161, 161, 151, 152, 161, 161, 161, 161, 122, 161, 80, 159, 161, 163, 161, 163, 164, 166, 167, 161, 161, 161, 161, 137, 161, 139, 140, 141, 142, 143, 144, 161, 161, 161, 161, 161, 161, 151, 152, 161, 161, 161, 74, 161, 161, 161, 116, 161, 80, 161, 161, 161, 166, 167, 161, 87, 88, 89, 161, 91, 161, 93, 161, 95, 161, 161, 98, 137, 138, 161, 161, 103, 104, 105, 161, 161, 161, 109, 110, 162, 162, 162, 162, 115, 116, 155, 156, 157, 162, 162, 162, 161, 124, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, -1, 163, -1, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, -1, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, -1, 165, -1, -1, 167); + protected array $actionBase = array(0, -2, 152, 549, 727, 904, 944, 1022, 660, 310, 123, 899, 500, 710, 710, 766, 710, 472, 701, 820, 63, 305, 305, 820, 305, 493, 493, 493, 666, 666, 666, 666, 700, 700, 860, 860, 892, 828, 794, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 51, 45, 451, 692, 1049, 1055, 1051, 1056, 1047, 1046, 1050, 1052, 1057, 1094, 1095, 812, 1096, 1097, 1093, 1098, 1053, 928, 1048, 1054, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 44, 343, 499, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 52, 52, 52, 578, 578, 47, 354, 978, 943, 978, 978, 978, 978, 978, 978, 978, 978, 203, 665, 339, 164, 164, 7, 7, 7, 7, 7, 50, 369, 704, 704, -25, -25, -25, -25, 448, 635, 501, 409, 283, 338, 591, 334, 334, 14, 14, 557, 557, 9, 9, 557, 557, 557, 537, 537, 537, 537, 441, 471, 599, 345, 428, 802, 53, 53, 53, 53, 802, 802, 802, 802, 848, 791, 802, 802, 802, 778, 907, 907, 942, 138, 138, 138, 907, 593, 503, 503, 593, 238, 503, 67, 135, -78, 833, 377, 590, -78, 362, 732, 646, 59, 795, 659, 795, 1045, 430, 843, 843, 457, 799, 761, 900, 1072, 1058, 836, 1091, 842, 1092, 15, 370, 712, 1044, 1044, 1044, 1044, 1044, 1044, 1044, 1044, 1044, 1044, 1044, 1100, 443, 1045, 384, 1100, 1100, 1100, 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, 672, 384, 482, 582, 384, 840, 443, 51, 851, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 800, 316, 51, 45, 150, 150, 481, 83, 150, 150, 150, 150, 51, 51, 51, 51, 659, 822, 793, 671, 856, 375, 822, 822, 822, 270, 158, 69, 197, 816, 817, 564, 814, 814, 829, 945, 814, 824, 814, 829, 955, 814, 814, 945, 945, 861, 945, 180, 565, 353, 531, 579, 945, 279, 814, 814, 814, 814, 850, 945, 586, 814, 214, 198, 814, 814, 850, 846, 806, 145, 821, 945, 945, 945, 850, 490, 821, 821, 821, 864, 865, 801, 805, 337, 297, 611, 169, 825, 805, 805, 814, 538, 801, 805, 801, 805, 863, 805, 805, 805, 801, 805, 824, 431, 805, 742, 595, 163, 805, 6, 962, 963, 685, 964, 952, 965, 1006, 966, 967, 1063, 940, 975, 953, 970, 1007, 951, 950, 811, 707, 715, 854, 849, 938, 815, 815, 815, 935, 936, 815, 815, 815, 815, 815, 815, 815, 815, 707, 891, 866, 831, 981, 720, 731, 1034, 847, 1073, 1099, 980, 1036, 971, 830, 740, 1019, 982, 792, 1061, 985, 989, 1020, 1037, 868, 1038, 1074, 823, 1075, 1076, 909, 993, 1064, 815, 962, 967, 695, 953, 970, 951, 950, 798, 788, 786, 787, 782, 781, 770, 776, 803, 1039, 932, 929, 918, 991, 937, 707, 919, 1010, 1059, 1023, 1024, 1062, 827, 797, 921, 1077, 995, 996, 1000, 1065, 1040, 1066, 859, 1011, 858, 1025, 838, 1078, 1026, 1027, 1028, 1029, 1067, 1079, 1068, 931, 1069, 871, 832, 927, 834, 1080, 1, 835, 837, 841, 1005, 613, 976, 1070, 1081, 1082, 1030, 1031, 1032, 1083, 1084, 972, 877, 1012, 813, 1018, 1009, 878, 879, 623, 839, 1041, 818, 826, 810, 628, 632, 1085, 1086, 1087, 974, 807, 819, 880, 881, 1042, 809, 1043, 1088, 682, 884, 747, 1089, 1035, 752, 756, 281, 658, 335, 763, 796, 1071, 862, 845, 804, 1001, 756, 808, 888, 1090, 894, 895, 896, 1033, 898, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 456, 456, 456, 456, 456, 456, 305, 305, 305, 305, 305, 456, 456, 456, 456, 456, 456, 456, 305, 305, 0, 0, 305, 0, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 473, 473, 289, 289, 473, 473, 473, 473, 473, 473, 473, 473, 473, 473, 289, 0, 289, 289, 289, 289, 289, 289, 289, 289, 473, 861, 473, 473, 138, 138, 138, 138, 473, 473, 473, -88, -88, 473, 238, 473, 473, 138, 138, 473, 473, 473, 473, 473, 473, 473, 473, 473, 473, 473, 0, 0, 0, 384, 503, 473, 824, 824, 824, 824, 473, 473, 473, 473, 503, 503, 473, 473, 473, 0, 0, 0, 0, 0, 0, 0, 0, 384, 0, 0, 384, 0, 0, 824, 824, 473, 238, 861, 168, 473, 0, 0, 0, 0, 384, 824, 384, 443, 814, 503, 503, 814, 443, 443, 150, 51, 168, 608, 608, 608, 608, 0, 0, 659, 861, 861, 861, 861, 861, 861, 861, 861, 861, 861, 861, 824, 0, 861, 0, 824, 824, 824, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 824, 0, 0, 945, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 955, 0, 0, 0, 0, 0, 0, 824, 0, 0, 0, 0, 0, 0, 0, 0, 0, 815, 827, 0, 827, 0, 815, 815, 815, 0, 0, 0, 0, 839, 809); + protected array $actionDefault = array(3, 32767, 102, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 100, 32767, 32767, 32767, 32767, 602, 602, 602, 602, 32767, 32767, 254, 102, 32767, 32767, 470, 387, 387, 387, 32767, 32767, 544, 544, 544, 544, 544, 544, 32767, 32767, 32767, 32767, 32767, 32767, 470, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 100, 32767, 32767, 32767, 36, 7, 8, 10, 11, 49, 17, 324, 32767, 32767, 32767, 32767, 102, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 595, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 474, 453, 454, 456, 457, 386, 545, 601, 327, 598, 385, 145, 339, 329, 242, 330, 258, 475, 259, 476, 479, 480, 215, 287, 382, 149, 150, 417, 471, 419, 469, 473, 418, 392, 398, 399, 400, 401, 402, 403, 404, 405, 406, 407, 408, 409, 410, 390, 391, 472, 450, 449, 448, 32767, 32767, 415, 416, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 102, 32767, 420, 389, 423, 421, 422, 439, 440, 437, 438, 441, 32767, 32767, 32767, 32767, 442, 443, 444, 445, 316, 32767, 32767, 366, 364, 316, 111, 32767, 32767, 430, 431, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 487, 538, 447, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 102, 32767, 100, 540, 412, 414, 507, 425, 426, 424, 393, 32767, 514, 32767, 102, 32767, 516, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 539, 32767, 546, 546, 32767, 500, 100, 195, 32767, 32767, 515, 32767, 195, 195, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 609, 500, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 32767, 195, 110, 32767, 32767, 32767, 100, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 190, 32767, 268, 270, 102, 563, 195, 32767, 519, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 512, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 500, 435, 138, 32767, 138, 546, 427, 428, 429, 502, 546, 546, 546, 312, 289, 32767, 32767, 32767, 32767, 517, 100, 100, 100, 100, 512, 32767, 32767, 32767, 32767, 111, 486, 99, 99, 99, 99, 99, 103, 101, 32767, 32767, 32767, 32767, 223, 32767, 99, 32767, 101, 101, 32767, 32767, 223, 225, 212, 101, 227, 32767, 567, 568, 223, 101, 227, 227, 227, 247, 247, 489, 318, 101, 99, 101, 101, 197, 318, 318, 32767, 101, 489, 318, 489, 318, 199, 318, 318, 318, 489, 318, 32767, 101, 318, 214, 99, 99, 318, 32767, 32767, 32767, 502, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 222, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 533, 32767, 551, 565, 433, 434, 436, 550, 548, 458, 459, 460, 461, 462, 463, 464, 466, 597, 32767, 506, 32767, 32767, 32767, 338, 32767, 607, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 608, 32767, 546, 32767, 32767, 32767, 32767, 432, 9, 74, 495, 42, 43, 51, 57, 523, 524, 525, 526, 520, 521, 527, 522, 32767, 32767, 528, 573, 32767, 32767, 547, 600, 32767, 32767, 32767, 32767, 32767, 32767, 138, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 533, 32767, 136, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 529, 32767, 32767, 32767, 546, 32767, 32767, 32767, 32767, 314, 311, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 546, 32767, 32767, 32767, 32767, 32767, 291, 32767, 308, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 286, 32767, 32767, 381, 502, 294, 296, 297, 32767, 32767, 32767, 32767, 360, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 152, 152, 3, 3, 341, 152, 152, 152, 341, 341, 152, 341, 341, 341, 152, 152, 152, 152, 152, 152, 280, 185, 262, 265, 247, 247, 152, 352, 152); + protected array $goto = array(196, 196, 1038, 1069, 701, 353, 433, 665, 856, 710, 427, 321, 315, 316, 337, 580, 432, 338, 434, 642, 658, 659, 857, 676, 677, 678, 979, 167, 167, 167, 167, 221, 197, 193, 193, 177, 179, 216, 193, 193, 193, 193, 193, 194, 194, 194, 194, 194, 194, 188, 189, 190, 191, 192, 218, 216, 219, 539, 540, 423, 541, 544, 545, 546, 547, 548, 549, 550, 551, 1140, 168, 169, 170, 195, 171, 172, 173, 166, 174, 175, 176, 178, 215, 217, 220, 238, 243, 244, 255, 257, 258, 259, 260, 261, 262, 263, 264, 269, 270, 271, 272, 282, 283, 318, 319, 320, 428, 429, 430, 585, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 180, 237, 181, 198, 199, 200, 239, 188, 189, 190, 191, 192, 218, 1140, 201, 182, 183, 184, 202, 198, 185, 240, 203, 201, 165, 204, 205, 186, 206, 207, 208, 187, 209, 210, 211, 212, 213, 214, 859, 421, 1041, 1041, 625, 662, 685, 956, 251, 251, 1033, 1049, 1050, 279, 279, 279, 279, 344, 831, 852, 627, 627, 890, 604, 1276, 1276, 1276, 1276, 1276, 1276, 1276, 1276, 1276, 1276, 351, 249, 249, 249, 249, 246, 252, 345, 344, 577, 864, 460, 913, 908, 909, 922, 865, 910, 862, 911, 912, 863, 469, 469, 916, 897, 855, 897, 897, 357, 917, 469, 918, 1336, 1091, 1086, 1087, 1088, 852, 357, 357, 613, 628, 631, 632, 633, 634, 655, 656, 657, 712, 396, 698, 357, 357, 833, 1000, 357, 441, 1363, 354, 355, 872, 1244, 698, 1244, 1244, 426, 698, 615, 558, 1038, 1038, 1244, 357, 357, 1038, 884, 1038, 1038, 871, 575, 1038, 1038, 1038, 1038, 1038, 1038, 1038, 1038, 1038, 1038, 1038, 1328, 1328, 1328, 1328, 1137, 1244, 356, 356, 356, 356, 1244, 1244, 1244, 1244, 1111, 1112, 1244, 1244, 1244, 1220, 948, 563, 556, 1221, 1224, 949, 1225, 1062, 554, 1307, 554, 554, 482, 603, 1104, 930, 713, 465, 554, 931, 484, 5, 946, 6, 1189, 946, 511, 704, 664, 1102, 690, 343, 556, 563, 572, 573, 346, 583, 606, 620, 621, 1044, 1043, 458, 852, 1047, 1048, 22, 973, 973, 973, 973, 327, 310, 458, 967, 974, 1295, 1295, 440, 558, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1292, 1292, 837, 686, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 543, 543, 1323, 1324, 543, 543, 543, 543, 543, 543, 543, 543, 543, 543, 542, 542, 254, 254, 542, 670, 542, 542, 542, 542, 542, 542, 542, 542, 339, 837, 962, 837, 557, 567, 581, 618, 557, 849, 567, 877, 1237, 399, 464, 451, 451, 451, 451, 405, 1318, 619, 1318, 1318, 1239, 874, 472, 584, 473, 474, 1318, 1235, 1075, 882, 570, 1022, 1354, 1355, 737, 641, 643, 740, 1079, 663, 479, 1321, 1322, 687, 691, 1014, 699, 708, 1010, 503, 886, 504, 1330, 1330, 1330, 1330, 1122, 510, 880, 984, 410, 411, 0, 1346, 1346, 674, 1261, 675, 0, 414, 415, 416, 0, 688, 1240, 1241, 417, 0, 0, 1314, 349, 1346, 0, 847, 885, 873, 1074, 1078, 552, 552, 552, 552, 0, 608, 0, 0, 982, 0, 1349, 1349, 0, 0, 1242, 1304, 1305, 451, 451, 451, 451, 451, 451, 451, 451, 451, 451, 451, 935, 1127, 451, 0, 972, 1077, 0, 623, 0, 1316, 1316, 1077, 0, 1019, 0, 326, 276, 326, 326, 0, 0, 876, 0, 668, 998, 435, 1120, 889, 0, 870, 435, 398, 401, 564, 605, 609, 0, 1003, 1045, 1045, 975, 1234, 736, 669, 1056, 1052, 1053, 971, 412, 709, 555, 1012, 1007, 635, 637, 639, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1017, 1017); + protected array $gotoCheck = array(42, 42, 73, 127, 73, 97, 66, 66, 26, 9, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 86, 86, 27, 86, 86, 86, 49, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 15, 43, 89, 89, 56, 56, 89, 89, 5, 5, 89, 89, 89, 23, 23, 23, 23, 170, 6, 22, 108, 108, 45, 130, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 181, 5, 5, 5, 5, 5, 5, 170, 170, 174, 15, 83, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 149, 149, 15, 25, 25, 25, 25, 14, 65, 149, 65, 183, 15, 15, 15, 15, 22, 14, 14, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 62, 7, 14, 14, 7, 103, 14, 83, 14, 97, 97, 35, 73, 7, 73, 73, 13, 7, 13, 14, 73, 73, 73, 14, 14, 73, 35, 73, 73, 35, 104, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 9, 9, 9, 9, 150, 73, 24, 24, 24, 24, 73, 73, 73, 73, 144, 144, 73, 73, 73, 79, 79, 76, 76, 79, 79, 79, 79, 114, 19, 14, 19, 19, 84, 8, 8, 73, 8, 151, 19, 73, 84, 46, 9, 46, 151, 9, 8, 8, 64, 8, 14, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 118, 118, 19, 22, 119, 119, 76, 19, 19, 19, 19, 171, 171, 19, 19, 19, 172, 172, 113, 14, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 173, 173, 12, 116, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 175, 175, 180, 180, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 158, 158, 5, 5, 158, 120, 158, 158, 158, 158, 158, 158, 158, 158, 29, 12, 92, 12, 9, 9, 2, 2, 9, 18, 9, 39, 14, 9, 9, 23, 23, 23, 23, 28, 130, 80, 130, 130, 20, 37, 9, 9, 9, 9, 130, 162, 129, 9, 48, 110, 9, 9, 48, 48, 48, 99, 132, 48, 178, 178, 178, 48, 48, 48, 48, 48, 48, 155, 41, 155, 130, 130, 130, 130, 147, 155, 9, 96, 82, 82, -1, 184, 184, 82, 20, 82, -1, 82, 82, 82, -1, 82, 20, 20, 82, -1, -1, 130, 82, 184, -1, 20, 16, 16, 16, 16, 107, 107, 107, 107, -1, 107, -1, -1, 16, -1, 184, 184, -1, -1, 20, 20, 20, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 17, 17, 23, -1, 16, 130, -1, 17, -1, 130, 130, 130, -1, 17, -1, 24, 24, 24, 24, -1, -1, 17, -1, 17, 17, 117, 16, 16, -1, 17, 117, 59, 59, 59, 59, 59, -1, 50, 117, 117, 50, 17, 50, 117, 117, 117, 117, 93, 93, 93, 50, 50, 50, 85, 85, 85, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 107, 107); + protected array $gotoBase = array(0, 0, -287, 0, 0, 170, 161, 242, 315, -11, 0, 0, 85, -75, -73, -187, 57, 75, 121, 53, 52, 0, -97, 173, 293, 219, 4, 18, 103, 125, 0, 0, 0, 0, 0, -114, 0, 107, 0, 109, 0, 35, -1, 145, 0, 162, -409, 0, -258, 8, 568, 0, 0, 0, 0, 0, 127, 0, 0, 529, 0, 0, 206, 0, 96, 213, -235, 0, 0, 0, 0, 0, 0, -5, 0, 0, -36, 0, 0, -101, 98, -122, -7, -71, -150, 114, -702, 0, 0, -115, 0, 0, 94, 284, 0, 0, 42, -481, 0, 55, 0, 0, 0, 218, 235, 0, 0, 487, -58, 0, 86, 0, 0, 91, 43, 0, 100, 295, 71, 69, 123, 0, 0, 0, 0, 0, 0, 1, 0, 79, 178, 0, 22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 0, 0, 38, 0, 185, 48, 59, 0, 0, 0, -22, 0, 0, 168, 0, 0, 0, 83, 0, 0, 0, 0, 0, 0, 0, -119, 39, 126, 140, 177, 154, 0, 0, 165, 0, 23, 167, 0, 199, 181, 0, 0); + protected array $gotoDefault = array(-32768, 515, 744, 4, 745, 939, 820, 829, 601, 533, 711, 350, 629, 424, 1312, 915, 1126, 582, 848, 1253, 1227, 459, 851, 332, 734, 927, 898, 899, 402, 388, 394, 400, 653, 630, 497, 883, 455, 875, 489, 878, 454, 887, 164, 420, 513, 891, 3, 894, 561, 925, 977, 389, 902, 390, 681, 904, 566, 906, 907, 397, 403, 404, 1131, 574, 626, 919, 256, 568, 920, 387, 921, 929, 392, 395, 692, 468, 508, 502, 413, 1106, 569, 612, 650, 448, 476, 624, 636, 622, 483, 436, 418, 331, 961, 969, 490, 466, 983, 352, 991, 742, 1139, 644, 492, 999, 645, 1006, 1009, 534, 535, 481, 1021, 273, 1024, 493, 19, 671, 1035, 1036, 672, 646, 1058, 647, 673, 648, 1060, 475, 602, 1068, 456, 1076, 1300, 457, 1080, 266, 1083, 278, 419, 437, 1089, 1090, 9, 1096, 702, 703, 11, 277, 512, 1121, 693, 453, 1138, 452, 1208, 1210, 562, 494, 1228, 480, 295, 1231, 684, 509, 1236, 449, 1303, 450, 536, 477, 317, 537, 1347, 309, 335, 314, 553, 296, 336, 538, 478, 1309, 1317, 333, 31, 1337, 1348, 579, 617); + protected array $ruleToNonTerminal = array(0, 1, 3, 3, 2, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 9, 10, 11, 11, 11, 12, 12, 13, 13, 14, 15, 15, 16, 16, 17, 17, 18, 18, 21, 21, 22, 23, 23, 24, 24, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 29, 29, 30, 30, 32, 34, 34, 28, 36, 36, 33, 38, 38, 35, 35, 37, 37, 39, 39, 31, 40, 40, 41, 43, 44, 44, 45, 45, 46, 46, 48, 47, 47, 47, 47, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 25, 25, 50, 69, 69, 72, 72, 71, 70, 70, 63, 75, 75, 76, 76, 77, 77, 78, 78, 79, 79, 80, 80, 26, 26, 27, 27, 27, 27, 27, 88, 88, 90, 90, 83, 83, 91, 91, 92, 92, 92, 84, 84, 87, 87, 85, 85, 93, 94, 94, 57, 57, 65, 65, 68, 68, 68, 67, 95, 95, 96, 58, 58, 58, 58, 97, 97, 98, 98, 99, 99, 100, 101, 101, 102, 102, 103, 103, 55, 55, 51, 51, 105, 53, 53, 106, 52, 52, 54, 54, 64, 64, 64, 64, 81, 81, 109, 109, 111, 111, 112, 112, 112, 112, 110, 110, 110, 114, 114, 114, 114, 89, 89, 117, 117, 117, 118, 118, 115, 115, 119, 119, 121, 121, 122, 122, 116, 123, 123, 120, 124, 124, 124, 124, 113, 113, 82, 82, 82, 20, 20, 20, 126, 125, 125, 127, 127, 127, 127, 60, 128, 128, 129, 61, 131, 131, 132, 132, 133, 133, 86, 134, 134, 134, 134, 134, 134, 134, 139, 139, 140, 140, 141, 141, 141, 141, 141, 142, 143, 143, 138, 138, 135, 135, 137, 137, 145, 145, 144, 144, 144, 144, 144, 144, 144, 136, 146, 146, 148, 147, 147, 62, 104, 149, 149, 56, 56, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 156, 158, 158, 159, 150, 150, 155, 155, 160, 161, 161, 162, 163, 164, 164, 164, 164, 19, 19, 73, 73, 73, 73, 151, 151, 151, 151, 166, 166, 152, 152, 154, 154, 154, 157, 157, 172, 172, 172, 172, 172, 172, 172, 172, 172, 173, 173, 173, 108, 175, 175, 175, 175, 153, 153, 153, 153, 153, 153, 153, 153, 59, 59, 169, 169, 169, 169, 169, 176, 176, 165, 165, 165, 165, 177, 177, 177, 177, 177, 177, 74, 74, 66, 66, 66, 66, 130, 130, 130, 130, 180, 179, 168, 168, 168, 168, 168, 168, 168, 167, 167, 167, 178, 178, 178, 178, 107, 174, 182, 182, 181, 181, 183, 183, 183, 183, 183, 183, 183, 183, 171, 171, 171, 171, 170, 185, 184, 184, 184, 184, 184, 184, 184, 184, 186, 186, 186, 186); + protected array $ruleToLength = array(1, 1, 2, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 2, 1, 3, 4, 1, 2, 0, 1, 1, 1, 1, 4, 3, 5, 4, 3, 4, 2, 3, 1, 1, 7, 6, 2, 3, 1, 2, 3, 1, 2, 3, 1, 1, 3, 1, 3, 1, 2, 2, 3, 1, 3, 2, 3, 1, 3, 3, 2, 0, 1, 1, 1, 1, 1, 3, 7, 10, 5, 7, 9, 5, 3, 3, 3, 3, 3, 3, 1, 2, 5, 7, 9, 6, 5, 6, 3, 2, 1, 1, 1, 1, 0, 2, 1, 3, 8, 0, 4, 2, 1, 3, 0, 1, 0, 1, 0, 1, 3, 1, 1, 1, 8, 9, 7, 8, 7, 6, 8, 0, 2, 0, 2, 1, 2, 1, 2, 1, 1, 1, 0, 2, 0, 2, 0, 2, 2, 1, 3, 1, 4, 1, 4, 1, 1, 4, 2, 1, 3, 3, 3, 4, 4, 5, 0, 2, 4, 3, 1, 1, 7, 0, 2, 1, 3, 3, 4, 1, 4, 0, 2, 5, 0, 2, 6, 0, 2, 0, 3, 1, 2, 1, 1, 2, 0, 1, 3, 0, 2, 1, 1, 1, 1, 6, 8, 6, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 1, 3, 3, 3, 3, 3, 1, 3, 3, 1, 1, 2, 1, 1, 0, 1, 0, 2, 2, 2, 4, 3, 1, 1, 3, 1, 2, 2, 3, 2, 3, 1, 1, 2, 3, 1, 1, 3, 2, 0, 1, 5, 5, 6, 10, 3, 5, 1, 1, 3, 0, 2, 4, 5, 4, 4, 4, 3, 1, 1, 1, 1, 1, 1, 0, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 3, 1, 1, 3, 2, 2, 3, 1, 0, 1, 1, 3, 3, 3, 4, 4, 1, 1, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 5, 4, 3, 4, 4, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 3, 2, 1, 2, 4, 2, 2, 8, 9, 8, 9, 9, 10, 9, 10, 8, 3, 2, 2, 1, 1, 0, 4, 2, 1, 3, 2, 1, 2, 2, 2, 4, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 0, 3, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 5, 3, 3, 4, 1, 1, 3, 1, 1, 1, 1, 1, 3, 2, 3, 0, 1, 1, 3, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 4, 4, 1, 4, 4, 0, 1, 1, 1, 3, 3, 1, 4, 2, 2, 1, 3, 1, 4, 4, 3, 3, 3, 3, 1, 3, 1, 1, 3, 1, 1, 4, 1, 1, 1, 3, 1, 1, 2, 1, 3, 4, 3, 2, 0, 2, 2, 1, 2, 1, 1, 1, 4, 3, 3, 3, 3, 6, 3, 1, 1, 2, 1); + protected function initReduceCallbacks(): void + { + $this->reduceCallbacks = [0 => null, 1 => static function ($self, $stackPos) { + $self->semValue = $self->handleNamespaces($self->semStack[$stackPos - (1 - 1)]); + }, 2 => static function ($self, $stackPos) { + if ($self->semStack[$stackPos - (2 - 2)] !== null) { + $self->semStack[$stackPos - (2 - 1)][] = $self->semStack[$stackPos - (2 - 2)]; + } + $self->semValue = $self->semStack[$stackPos - (2 - 1)]; + }, 3 => static function ($self, $stackPos) { + $self->semValue = array(); + }, 4 => static function ($self, $stackPos) { + $nop = $self->maybeCreateZeroLengthNop($self->tokenPos); if ($nop !== null) { - $this->semStack[$stackPos - (1 - 1)][] = $nop; - } - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 5 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 6 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 7 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 8 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 9 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 10 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 11 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 12 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 13 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 14 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 15 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 16 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 17 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 18 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 19 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 20 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 21 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 22 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 23 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 24 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 25 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 26 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 27 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 28 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 29 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 30 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 31 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 32 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 33 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 34 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 35 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 36 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 37 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 38 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 39 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 40 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 41 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 42 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 43 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 44 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 45 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 46 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 47 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 48 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 49 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 50 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 51 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 52 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 53 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 54 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 55 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 56 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 57 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 58 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 59 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 60 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 61 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 62 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 63 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 64 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 65 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 66 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 67 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 68 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 69 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 70 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 71 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 72 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 73 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 74 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 75 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 76 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 77 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 78 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 79 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 80 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 81 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 82 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 83 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 84 => function ($stackPos) { - $this->semValue = new Node\Identifier($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 85 => function ($stackPos) { - $this->semValue = new Node\Identifier($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 86 => function ($stackPos) { - $this->semValue = new Node\Identifier($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 87 => function ($stackPos) { - $this->semValue = new Node\Identifier($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 88 => function ($stackPos) { - $this->semValue = new Name($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 89 => function ($stackPos) { - $this->semValue = new Name($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 90 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 91 => function ($stackPos) { - $this->semValue = new Name(\substr($this->semStack[$stackPos - (1 - 1)], 1), $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 92 => function ($stackPos) { - $this->semValue = new Expr\Variable(\substr($this->semStack[$stackPos - (1 - 1)], 1), $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 93 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 94 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 95 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 96 => function ($stackPos) { - $this->semValue = new Stmt\HaltCompiler($this->lexer->handleHaltCompiler(), $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 97 => function ($stackPos) { - $this->semValue = new Stmt\Namespace_($this->semStack[$stackPos - (3 - 2)], null, $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - $this->semValue->setAttribute('kind', Stmt\Namespace_::KIND_SEMICOLON); - $this->checkNamespace($this->semValue); - }, 98 => function ($stackPos) { - $this->semValue = new Stmt\Namespace_($this->semStack[$stackPos - (5 - 2)], $this->semStack[$stackPos - (5 - 4)], $this->startAttributeStack[$stackPos - (5 - 1)] + $this->endAttributes); - $this->semValue->setAttribute('kind', Stmt\Namespace_::KIND_BRACED); - $this->checkNamespace($this->semValue); - }, 99 => function ($stackPos) { - $this->semValue = new Stmt\Namespace_(null, $this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - $this->semValue->setAttribute('kind', Stmt\Namespace_::KIND_BRACED); - $this->checkNamespace($this->semValue); - }, 100 => function ($stackPos) { - $this->semValue = new Stmt\Use_($this->semStack[$stackPos - (3 - 2)], Stmt\Use_::TYPE_NORMAL, $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 101 => function ($stackPos) { - $this->semValue = new Stmt\Use_($this->semStack[$stackPos - (4 - 3)], $this->semStack[$stackPos - (4 - 2)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 102 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (2 - 1)]; - }, 103 => function ($stackPos) { - $this->semValue = new Stmt\Const_($this->semStack[$stackPos - (3 - 2)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 104 => function ($stackPos) { - $this->semValue = Stmt\Use_::TYPE_FUNCTION; - }, 105 => function ($stackPos) { - $this->semValue = Stmt\Use_::TYPE_CONSTANT; - }, 106 => function ($stackPos) { - $this->semValue = new Stmt\GroupUse($this->semStack[$stackPos - (7 - 3)], $this->semStack[$stackPos - (7 - 6)], $this->semStack[$stackPos - (7 - 2)], $this->startAttributeStack[$stackPos - (7 - 1)] + $this->endAttributes); - }, 107 => function ($stackPos) { - $this->semValue = new Stmt\GroupUse($this->semStack[$stackPos - (6 - 2)], $this->semStack[$stackPos - (6 - 5)], Stmt\Use_::TYPE_UNKNOWN, $this->startAttributeStack[$stackPos - (6 - 1)] + $this->endAttributes); - }, 108 => function ($stackPos) { - $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; - $this->semValue = $this->semStack[$stackPos - (3 - 1)]; - }, 109 => function ($stackPos) { - $this->semValue = array($this->semStack[$stackPos - (1 - 1)]); - }, 110 => function ($stackPos) { - $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; - $this->semValue = $this->semStack[$stackPos - (3 - 1)]; - }, 111 => function ($stackPos) { - $this->semValue = array($this->semStack[$stackPos - (1 - 1)]); - }, 112 => function ($stackPos) { - $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; - $this->semValue = $this->semStack[$stackPos - (3 - 1)]; - }, 113 => function ($stackPos) { - $this->semValue = array($this->semStack[$stackPos - (1 - 1)]); - }, 114 => function ($stackPos) { - $this->semValue = new Stmt\UseUse($this->semStack[$stackPos - (1 - 1)], null, Stmt\Use_::TYPE_UNKNOWN, $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - $this->checkUseUse($this->semValue, $stackPos - (1 - 1)); - }, 115 => function ($stackPos) { - $this->semValue = new Stmt\UseUse($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], Stmt\Use_::TYPE_UNKNOWN, $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - $this->checkUseUse($this->semValue, $stackPos - (3 - 3)); - }, 116 => function ($stackPos) { - $this->semValue = new Stmt\UseUse($this->semStack[$stackPos - (1 - 1)], null, Stmt\Use_::TYPE_UNKNOWN, $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - $this->checkUseUse($this->semValue, $stackPos - (1 - 1)); - }, 117 => function ($stackPos) { - $this->semValue = new Stmt\UseUse($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], Stmt\Use_::TYPE_UNKNOWN, $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - $this->checkUseUse($this->semValue, $stackPos - (3 - 3)); - }, 118 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - $this->semValue->type = Stmt\Use_::TYPE_NORMAL; - }, 119 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (2 - 2)]; - $this->semValue->type = $this->semStack[$stackPos - (2 - 1)]; - }, 120 => function ($stackPos) { - $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; - $this->semValue = $this->semStack[$stackPos - (3 - 1)]; - }, 121 => function ($stackPos) { - $this->semValue = array($this->semStack[$stackPos - (1 - 1)]); - }, 122 => function ($stackPos) { - $this->semValue = new Node\Const_($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 123 => function ($stackPos) { - $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; - $this->semValue = $this->semStack[$stackPos - (3 - 1)]; - }, 124 => function ($stackPos) { - $this->semValue = array($this->semStack[$stackPos - (1 - 1)]); - }, 125 => function ($stackPos) { - $this->semValue = new Node\Const_($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 126 => function ($stackPos) { - if (\is_array($this->semStack[$stackPos - (2 - 2)])) { - $this->semValue = \array_merge($this->semStack[$stackPos - (2 - 1)], $this->semStack[$stackPos - (2 - 2)]); - } else { - $this->semStack[$stackPos - (2 - 1)][] = $this->semStack[$stackPos - (2 - 2)]; - $this->semValue = $this->semStack[$stackPos - (2 - 1)]; - } - }, 127 => function ($stackPos) { - $this->semValue = array(); - }, 128 => function ($stackPos) { - $startAttributes = $this->lookaheadStartAttributes; - if (isset($startAttributes['comments'])) { - $nop = new Stmt\Nop($this->createCommentNopAttributes($startAttributes['comments'])); - } else { - $nop = null; - } + $self->semStack[$stackPos - (1 - 1)][] = $nop; + } + $self->semValue = $self->semStack[$stackPos - (1 - 1)]; + }, 5 => null, 6 => null, 7 => null, 8 => null, 9 => null, 10 => null, 11 => null, 12 => null, 13 => null, 14 => null, 15 => null, 16 => null, 17 => null, 18 => null, 19 => null, 20 => null, 21 => null, 22 => null, 23 => null, 24 => null, 25 => null, 26 => null, 27 => null, 28 => null, 29 => null, 30 => null, 31 => null, 32 => null, 33 => null, 34 => null, 35 => null, 36 => null, 37 => null, 38 => null, 39 => null, 40 => null, 41 => null, 42 => null, 43 => null, 44 => null, 45 => null, 46 => null, 47 => null, 48 => null, 49 => null, 50 => null, 51 => null, 52 => null, 53 => null, 54 => null, 55 => null, 56 => null, 57 => null, 58 => null, 59 => null, 60 => null, 61 => null, 62 => null, 63 => null, 64 => null, 65 => null, 66 => null, 67 => null, 68 => null, 69 => null, 70 => null, 71 => null, 72 => null, 73 => null, 74 => null, 75 => null, 76 => static function ($self, $stackPos) { + $self->semValue = $self->semStack[$stackPos - (1 - 1)]; + if ($self->semValue === "emitError(new Error('Cannot use "getAttributes($self->tokenStartStack[$stackPos - (1 - 1)], $self->tokenEndStack[$stackPos]))); + } + }, 77 => null, 78 => null, 79 => null, 80 => null, 81 => null, 82 => null, 83 => null, 84 => null, 85 => static function ($self, $stackPos) { + $self->semValue = new Node\Identifier($self->semStack[$stackPos - (1 - 1)], $self->getAttributes($self->tokenStartStack[$stackPos - (1 - 1)], $self->tokenEndStack[$stackPos])); + }, 86 => static function ($self, $stackPos) { + $self->semValue = new Node\Identifier($self->semStack[$stackPos - (1 - 1)], $self->getAttributes($self->tokenStartStack[$stackPos - (1 - 1)], $self->tokenEndStack[$stackPos])); + }, 87 => static function ($self, $stackPos) { + $self->semValue = new Node\Identifier($self->semStack[$stackPos - (1 - 1)], $self->getAttributes($self->tokenStartStack[$stackPos - (1 - 1)], $self->tokenEndStack[$stackPos])); + }, 88 => static function ($self, $stackPos) { + $self->semValue = new Node\Identifier($self->semStack[$stackPos - (1 - 1)], $self->getAttributes($self->tokenStartStack[$stackPos - (1 - 1)], $self->tokenEndStack[$stackPos])); + }, 89 => static function ($self, $stackPos) { + $self->semValue = new Name($self->semStack[$stackPos - (1 - 1)], $self->getAttributes($self->tokenStartStack[$stackPos - (1 - 1)], $self->tokenEndStack[$stackPos])); + }, 90 => static function ($self, $stackPos) { + $self->semValue = new Name($self->semStack[$stackPos - (1 - 1)], $self->getAttributes($self->tokenStartStack[$stackPos - (1 - 1)], $self->tokenEndStack[$stackPos])); + }, 91 => static function ($self, $stackPos) { + $self->semValue = new Name($self->semStack[$stackPos - (1 - 1)], $self->getAttributes($self->tokenStartStack[$stackPos - (1 - 1)], $self->tokenEndStack[$stackPos])); + }, 92 => static function ($self, $stackPos) { + $self->semValue = new Name($self->semStack[$stackPos - (1 - 1)], $self->getAttributes($self->tokenStartStack[$stackPos - (1 - 1)], $self->tokenEndStack[$stackPos])); + }, 93 => static function ($self, $stackPos) { + $self->semValue = new Name($self->semStack[$stackPos - (1 - 1)], $self->getAttributes($self->tokenStartStack[$stackPos - (1 - 1)], $self->tokenEndStack[$stackPos])); + }, 94 => null, 95 => static function ($self, $stackPos) { + $self->semValue = new Name(substr($self->semStack[$stackPos - (1 - 1)], 1), $self->getAttributes($self->tokenStartStack[$stackPos - (1 - 1)], $self->tokenEndStack[$stackPos])); + }, 96 => static function ($self, $stackPos) { + $self->semValue = new Expr\Variable(substr($self->semStack[$stackPos - (1 - 1)], 1), $self->getAttributes($self->tokenStartStack[$stackPos - (1 - 1)], $self->tokenEndStack[$stackPos])); + }, 97 => static function ($self, $stackPos) { + /* nothing */ + }, 98 => static function ($self, $stackPos) { + /* nothing */ + }, 99 => static function ($self, $stackPos) { + /* nothing */ + }, 100 => static function ($self, $stackPos) { + $self->emitError(new Error('A trailing comma is not allowed here', $self->getAttributes($self->tokenStartStack[$stackPos - (1 - 1)], $self->tokenEndStack[$stackPos]))); + }, 101 => null, 102 => null, 103 => static function ($self, $stackPos) { + $self->semValue = new Node\Attribute($self->semStack[$stackPos - (1 - 1)], [], $self->getAttributes($self->tokenStartStack[$stackPos - (1 - 1)], $self->tokenEndStack[$stackPos])); + }, 104 => static function ($self, $stackPos) { + $self->semValue = new Node\Attribute($self->semStack[$stackPos - (2 - 1)], $self->semStack[$stackPos - (2 - 2)], $self->getAttributes($self->tokenStartStack[$stackPos - (2 - 1)], $self->tokenEndStack[$stackPos])); + }, 105 => static function ($self, $stackPos) { + $self->semValue = array($self->semStack[$stackPos - (1 - 1)]); + }, 106 => static function ($self, $stackPos) { + $self->semStack[$stackPos - (3 - 1)][] = $self->semStack[$stackPos - (3 - 3)]; + $self->semValue = $self->semStack[$stackPos - (3 - 1)]; + }, 107 => static function ($self, $stackPos) { + $self->semValue = new Node\AttributeGroup($self->semStack[$stackPos - (4 - 2)], $self->getAttributes($self->tokenStartStack[$stackPos - (4 - 1)], $self->tokenEndStack[$stackPos])); + }, 108 => static function ($self, $stackPos) { + $self->semValue = array($self->semStack[$stackPos - (1 - 1)]); + }, 109 => static function ($self, $stackPos) { + $self->semStack[$stackPos - (2 - 1)][] = $self->semStack[$stackPos - (2 - 2)]; + $self->semValue = $self->semStack[$stackPos - (2 - 1)]; + }, 110 => static function ($self, $stackPos) { + $self->semValue = []; + }, 111 => null, 112 => null, 113 => null, 114 => null, 115 => static function ($self, $stackPos) { + $self->semValue = new Stmt\HaltCompiler($self->handleHaltCompiler(), $self->getAttributes($self->tokenStartStack[$stackPos - (4 - 1)], $self->tokenEndStack[$stackPos])); + }, 116 => static function ($self, $stackPos) { + $self->semValue = new Stmt\Namespace_($self->semStack[$stackPos - (3 - 2)], null, $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + $self->semValue->setAttribute('kind', Stmt\Namespace_::KIND_SEMICOLON); + $self->checkNamespace($self->semValue); + }, 117 => static function ($self, $stackPos) { + $self->semValue = new Stmt\Namespace_($self->semStack[$stackPos - (5 - 2)], $self->semStack[$stackPos - (5 - 4)], $self->getAttributes($self->tokenStartStack[$stackPos - (5 - 1)], $self->tokenEndStack[$stackPos])); + $self->semValue->setAttribute('kind', Stmt\Namespace_::KIND_BRACED); + $self->checkNamespace($self->semValue); + }, 118 => static function ($self, $stackPos) { + $self->semValue = new Stmt\Namespace_(null, $self->semStack[$stackPos - (4 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (4 - 1)], $self->tokenEndStack[$stackPos])); + $self->semValue->setAttribute('kind', Stmt\Namespace_::KIND_BRACED); + $self->checkNamespace($self->semValue); + }, 119 => static function ($self, $stackPos) { + $self->semValue = new Stmt\Use_($self->semStack[$stackPos - (3 - 2)], Stmt\Use_::TYPE_NORMAL, $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 120 => static function ($self, $stackPos) { + $self->semValue = new Stmt\Use_($self->semStack[$stackPos - (4 - 3)], $self->semStack[$stackPos - (4 - 2)], $self->getAttributes($self->tokenStartStack[$stackPos - (4 - 1)], $self->tokenEndStack[$stackPos])); + }, 121 => null, 122 => static function ($self, $stackPos) { + $self->semValue = new Stmt\Const_($self->semStack[$stackPos - (3 - 2)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 123 => static function ($self, $stackPos) { + $self->semValue = Stmt\Use_::TYPE_FUNCTION; + }, 124 => static function ($self, $stackPos) { + $self->semValue = Stmt\Use_::TYPE_CONSTANT; + }, 125 => static function ($self, $stackPos) { + $self->semValue = new Stmt\GroupUse($self->semStack[$stackPos - (7 - 3)], $self->semStack[$stackPos - (7 - 6)], $self->semStack[$stackPos - (7 - 2)], $self->getAttributes($self->tokenStartStack[$stackPos - (7 - 1)], $self->tokenEndStack[$stackPos])); + }, 126 => static function ($self, $stackPos) { + $self->semValue = new Stmt\GroupUse($self->semStack[$stackPos - (6 - 2)], $self->semStack[$stackPos - (6 - 5)], Stmt\Use_::TYPE_UNKNOWN, $self->getAttributes($self->tokenStartStack[$stackPos - (6 - 1)], $self->tokenEndStack[$stackPos])); + }, 127 => null, 128 => static function ($self, $stackPos) { + $self->semStack[$stackPos - (3 - 1)][] = $self->semStack[$stackPos - (3 - 3)]; + $self->semValue = $self->semStack[$stackPos - (3 - 1)]; + }, 129 => static function ($self, $stackPos) { + $self->semValue = array($self->semStack[$stackPos - (1 - 1)]); + }, 130 => null, 131 => static function ($self, $stackPos) { + $self->semStack[$stackPos - (3 - 1)][] = $self->semStack[$stackPos - (3 - 3)]; + $self->semValue = $self->semStack[$stackPos - (3 - 1)]; + }, 132 => static function ($self, $stackPos) { + $self->semValue = array($self->semStack[$stackPos - (1 - 1)]); + }, 133 => null, 134 => static function ($self, $stackPos) { + $self->semStack[$stackPos - (3 - 1)][] = $self->semStack[$stackPos - (3 - 3)]; + $self->semValue = $self->semStack[$stackPos - (3 - 1)]; + }, 135 => static function ($self, $stackPos) { + $self->semValue = array($self->semStack[$stackPos - (1 - 1)]); + }, 136 => static function ($self, $stackPos) { + $self->semValue = new Node\UseItem($self->semStack[$stackPos - (1 - 1)], null, Stmt\Use_::TYPE_UNKNOWN, $self->getAttributes($self->tokenStartStack[$stackPos - (1 - 1)], $self->tokenEndStack[$stackPos])); + $self->checkUseUse($self->semValue, $stackPos - (1 - 1)); + }, 137 => static function ($self, $stackPos) { + $self->semValue = new Node\UseItem($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], Stmt\Use_::TYPE_UNKNOWN, $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + $self->checkUseUse($self->semValue, $stackPos - (3 - 3)); + }, 138 => static function ($self, $stackPos) { + $self->semValue = new Node\UseItem($self->semStack[$stackPos - (1 - 1)], null, Stmt\Use_::TYPE_UNKNOWN, $self->getAttributes($self->tokenStartStack[$stackPos - (1 - 1)], $self->tokenEndStack[$stackPos])); + $self->checkUseUse($self->semValue, $stackPos - (1 - 1)); + }, 139 => static function ($self, $stackPos) { + $self->semValue = new Node\UseItem($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], Stmt\Use_::TYPE_UNKNOWN, $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + $self->checkUseUse($self->semValue, $stackPos - (3 - 3)); + }, 140 => static function ($self, $stackPos) { + $self->semValue = $self->semStack[$stackPos - (1 - 1)]; + $self->semValue->type = Stmt\Use_::TYPE_NORMAL; + }, 141 => static function ($self, $stackPos) { + $self->semValue = $self->semStack[$stackPos - (2 - 2)]; + $self->semValue->type = $self->semStack[$stackPos - (2 - 1)]; + }, 142 => null, 143 => static function ($self, $stackPos) { + $self->semStack[$stackPos - (3 - 1)][] = $self->semStack[$stackPos - (3 - 3)]; + $self->semValue = $self->semStack[$stackPos - (3 - 1)]; + }, 144 => static function ($self, $stackPos) { + $self->semValue = array($self->semStack[$stackPos - (1 - 1)]); + }, 145 => static function ($self, $stackPos) { + $self->semValue = new Node\Const_($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 146 => null, 147 => static function ($self, $stackPos) { + $self->semStack[$stackPos - (3 - 1)][] = $self->semStack[$stackPos - (3 - 3)]; + $self->semValue = $self->semStack[$stackPos - (3 - 1)]; + }, 148 => static function ($self, $stackPos) { + $self->semValue = array($self->semStack[$stackPos - (1 - 1)]); + }, 149 => static function ($self, $stackPos) { + $self->semValue = new Node\Const_(new Node\Identifier($self->semStack[$stackPos - (3 - 1)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos - (3 - 1)])), $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 150 => static function ($self, $stackPos) { + $self->semValue = new Node\Const_(new Node\Identifier($self->semStack[$stackPos - (3 - 1)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos - (3 - 1)])), $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 151 => static function ($self, $stackPos) { + if ($self->semStack[$stackPos - (2 - 2)] !== null) { + $self->semStack[$stackPos - (2 - 1)][] = $self->semStack[$stackPos - (2 - 2)]; + } + $self->semValue = $self->semStack[$stackPos - (2 - 1)]; + }, 152 => static function ($self, $stackPos) { + $self->semValue = array(); + }, 153 => static function ($self, $stackPos) { + $nop = $self->maybeCreateZeroLengthNop($self->tokenPos); if ($nop !== null) { - $this->semStack[$stackPos - (1 - 1)][] = $nop; - } - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 129 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 130 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 131 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 132 => function ($stackPos) { - throw new Error('__HALT_COMPILER() can only be used from the outermost scope', $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 133 => function ($stackPos) { - if ($this->semStack[$stackPos - (3 - 2)]) { - $this->semValue = $this->semStack[$stackPos - (3 - 2)]; - $attrs = $this->startAttributeStack[$stackPos - (3 - 1)]; - $stmts = $this->semValue; - if (!empty($attrs['comments'])) { - $stmts[0]->setAttribute('comments', \array_merge($attrs['comments'], $stmts[0]->getAttribute('comments', []))); - } - } else { - $startAttributes = $this->startAttributeStack[$stackPos - (3 - 1)]; - if (isset($startAttributes['comments'])) { - $this->semValue = new Stmt\Nop($startAttributes + $this->endAttributes); - } else { - $this->semValue = null; - } - if (null === $this->semValue) { - $this->semValue = array(); - } - } - }, 134 => function ($stackPos) { - $this->semValue = new Stmt\If_($this->semStack[$stackPos - (5 - 2)], ['stmts' => \is_array($this->semStack[$stackPos - (5 - 3)]) ? $this->semStack[$stackPos - (5 - 3)] : array($this->semStack[$stackPos - (5 - 3)]), 'elseifs' => $this->semStack[$stackPos - (5 - 4)], 'else' => $this->semStack[$stackPos - (5 - 5)]], $this->startAttributeStack[$stackPos - (5 - 1)] + $this->endAttributes); - }, 135 => function ($stackPos) { - $this->semValue = new Stmt\If_($this->semStack[$stackPos - (8 - 2)], ['stmts' => $this->semStack[$stackPos - (8 - 4)], 'elseifs' => $this->semStack[$stackPos - (8 - 5)], 'else' => $this->semStack[$stackPos - (8 - 6)]], $this->startAttributeStack[$stackPos - (8 - 1)] + $this->endAttributes); - }, 136 => function ($stackPos) { - $this->semValue = new Stmt\While_($this->semStack[$stackPos - (3 - 2)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 137 => function ($stackPos) { - $this->semValue = new Stmt\Do_($this->semStack[$stackPos - (5 - 4)], \is_array($this->semStack[$stackPos - (5 - 2)]) ? $this->semStack[$stackPos - (5 - 2)] : array($this->semStack[$stackPos - (5 - 2)]), $this->startAttributeStack[$stackPos - (5 - 1)] + $this->endAttributes); - }, 138 => function ($stackPos) { - $this->semValue = new Stmt\For_(['init' => $this->semStack[$stackPos - (9 - 3)], 'cond' => $this->semStack[$stackPos - (9 - 5)], 'loop' => $this->semStack[$stackPos - (9 - 7)], 'stmts' => $this->semStack[$stackPos - (9 - 9)]], $this->startAttributeStack[$stackPos - (9 - 1)] + $this->endAttributes); - }, 139 => function ($stackPos) { - $this->semValue = new Stmt\Switch_($this->semStack[$stackPos - (3 - 2)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 140 => function ($stackPos) { - $this->semValue = new Stmt\Break_(null, $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 141 => function ($stackPos) { - $this->semValue = new Stmt\Break_($this->semStack[$stackPos - (3 - 2)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 142 => function ($stackPos) { - $this->semValue = new Stmt\Continue_(null, $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 143 => function ($stackPos) { - $this->semValue = new Stmt\Continue_($this->semStack[$stackPos - (3 - 2)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 144 => function ($stackPos) { - $this->semValue = new Stmt\Return_(null, $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 145 => function ($stackPos) { - $this->semValue = new Stmt\Return_($this->semStack[$stackPos - (3 - 2)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 146 => function ($stackPos) { - $this->semValue = new Stmt\Global_($this->semStack[$stackPos - (3 - 2)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 147 => function ($stackPos) { - $this->semValue = new Stmt\Static_($this->semStack[$stackPos - (3 - 2)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 148 => function ($stackPos) { - $this->semValue = new Stmt\Echo_($this->semStack[$stackPos - (3 - 2)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 149 => function ($stackPos) { - $this->semValue = new Stmt\InlineHTML($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 150 => function ($stackPos) { - $this->semValue = new Stmt\Expression($this->semStack[$stackPos - (2 - 1)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 151 => function ($stackPos) { - $this->semValue = new Stmt\Expression($this->semStack[$stackPos - (2 - 1)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 152 => function ($stackPos) { - $this->semValue = new Stmt\Unset_($this->semStack[$stackPos - (5 - 3)], $this->startAttributeStack[$stackPos - (5 - 1)] + $this->endAttributes); - }, 153 => function ($stackPos) { - $this->semValue = new Stmt\Foreach_($this->semStack[$stackPos - (7 - 3)], $this->semStack[$stackPos - (7 - 5)][0], ['keyVar' => null, 'byRef' => $this->semStack[$stackPos - (7 - 5)][1], 'stmts' => $this->semStack[$stackPos - (7 - 7)]], $this->startAttributeStack[$stackPos - (7 - 1)] + $this->endAttributes); - }, 154 => function ($stackPos) { - $this->semValue = new Stmt\Foreach_($this->semStack[$stackPos - (9 - 3)], $this->semStack[$stackPos - (9 - 7)][0], ['keyVar' => $this->semStack[$stackPos - (9 - 5)], 'byRef' => $this->semStack[$stackPos - (9 - 7)][1], 'stmts' => $this->semStack[$stackPos - (9 - 9)]], $this->startAttributeStack[$stackPos - (9 - 1)] + $this->endAttributes); - }, 155 => function ($stackPos) { - $this->semValue = new Stmt\Declare_($this->semStack[$stackPos - (5 - 3)], $this->semStack[$stackPos - (5 - 5)], $this->startAttributeStack[$stackPos - (5 - 1)] + $this->endAttributes); - }, 156 => function ($stackPos) { - $this->semValue = new Stmt\TryCatch($this->semStack[$stackPos - (6 - 3)], $this->semStack[$stackPos - (6 - 5)], $this->semStack[$stackPos - (6 - 6)], $this->startAttributeStack[$stackPos - (6 - 1)] + $this->endAttributes); - $this->checkTryCatch($this->semValue); - }, 157 => function ($stackPos) { - $this->semValue = new Stmt\Throw_($this->semStack[$stackPos - (3 - 2)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 158 => function ($stackPos) { - $this->semValue = new Stmt\Goto_($this->semStack[$stackPos - (3 - 2)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 159 => function ($stackPos) { - $this->semValue = new Stmt\Label($this->semStack[$stackPos - (2 - 1)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 160 => function ($stackPos) { - $this->semValue = new Stmt\Expression($this->semStack[$stackPos - (2 - 1)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 161 => function ($stackPos) { - $this->semValue = array(); + $self->semStack[$stackPos - (1 - 1)][] = $nop; + } + $self->semValue = $self->semStack[$stackPos - (1 - 1)]; + }, 154 => null, 155 => null, 156 => null, 157 => static function ($self, $stackPos) { + throw new Error('__HALT_COMPILER() can only be used from the outermost scope', $self->getAttributes($self->tokenStartStack[$stackPos - (1 - 1)], $self->tokenEndStack[$stackPos])); + }, 158 => static function ($self, $stackPos) { + $self->semValue = new Stmt\Block($self->semStack[$stackPos - (3 - 2)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 159 => static function ($self, $stackPos) { + $self->semValue = new Stmt\If_($self->semStack[$stackPos - (7 - 3)], ['stmts' => $self->semStack[$stackPos - (7 - 5)], 'elseifs' => $self->semStack[$stackPos - (7 - 6)], 'else' => $self->semStack[$stackPos - (7 - 7)]], $self->getAttributes($self->tokenStartStack[$stackPos - (7 - 1)], $self->tokenEndStack[$stackPos])); + }, 160 => static function ($self, $stackPos) { + $self->semValue = new Stmt\If_($self->semStack[$stackPos - (10 - 3)], ['stmts' => $self->semStack[$stackPos - (10 - 6)], 'elseifs' => $self->semStack[$stackPos - (10 - 7)], 'else' => $self->semStack[$stackPos - (10 - 8)]], $self->getAttributes($self->tokenStartStack[$stackPos - (10 - 1)], $self->tokenEndStack[$stackPos])); + }, 161 => static function ($self, $stackPos) { + $self->semValue = new Stmt\While_($self->semStack[$stackPos - (5 - 3)], $self->semStack[$stackPos - (5 - 5)], $self->getAttributes($self->tokenStartStack[$stackPos - (5 - 1)], $self->tokenEndStack[$stackPos])); + }, 162 => static function ($self, $stackPos) { + $self->semValue = new Stmt\Do_($self->semStack[$stackPos - (7 - 5)], $self->semStack[$stackPos - (7 - 2)], $self->getAttributes($self->tokenStartStack[$stackPos - (7 - 1)], $self->tokenEndStack[$stackPos])); + }, 163 => static function ($self, $stackPos) { + $self->semValue = new Stmt\For_(['init' => $self->semStack[$stackPos - (9 - 3)], 'cond' => $self->semStack[$stackPos - (9 - 5)], 'loop' => $self->semStack[$stackPos - (9 - 7)], 'stmts' => $self->semStack[$stackPos - (9 - 9)]], $self->getAttributes($self->tokenStartStack[$stackPos - (9 - 1)], $self->tokenEndStack[$stackPos])); + }, 164 => static function ($self, $stackPos) { + $self->semValue = new Stmt\Switch_($self->semStack[$stackPos - (5 - 3)], $self->semStack[$stackPos - (5 - 5)], $self->getAttributes($self->tokenStartStack[$stackPos - (5 - 1)], $self->tokenEndStack[$stackPos])); + }, 165 => static function ($self, $stackPos) { + $self->semValue = new Stmt\Break_($self->semStack[$stackPos - (3 - 2)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 166 => static function ($self, $stackPos) { + $self->semValue = new Stmt\Continue_($self->semStack[$stackPos - (3 - 2)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 167 => static function ($self, $stackPos) { + $self->semValue = new Stmt\Return_($self->semStack[$stackPos - (3 - 2)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 168 => static function ($self, $stackPos) { + $self->semValue = new Stmt\Global_($self->semStack[$stackPos - (3 - 2)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 169 => static function ($self, $stackPos) { + $self->semValue = new Stmt\Static_($self->semStack[$stackPos - (3 - 2)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 170 => static function ($self, $stackPos) { + $self->semValue = new Stmt\Echo_($self->semStack[$stackPos - (3 - 2)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 171 => static function ($self, $stackPos) { + $self->semValue = new Stmt\InlineHTML($self->semStack[$stackPos - (1 - 1)], $self->getAttributes($self->tokenStartStack[$stackPos - (1 - 1)], $self->tokenEndStack[$stackPos])); + $self->semValue->setAttribute('hasLeadingNewline', $self->inlineHtmlHasLeadingNewline($stackPos - (1 - 1))); + }, 172 => static function ($self, $stackPos) { + $self->semValue = new Stmt\Expression($self->semStack[$stackPos - (2 - 1)], $self->getAttributes($self->tokenStartStack[$stackPos - (2 - 1)], $self->tokenEndStack[$stackPos])); + }, 173 => static function ($self, $stackPos) { + $self->semValue = new Stmt\Unset_($self->semStack[$stackPos - (5 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (5 - 1)], $self->tokenEndStack[$stackPos])); + }, 174 => static function ($self, $stackPos) { + $self->semValue = new Stmt\Foreach_($self->semStack[$stackPos - (7 - 3)], $self->semStack[$stackPos - (7 - 5)][0], ['keyVar' => null, 'byRef' => $self->semStack[$stackPos - (7 - 5)][1], 'stmts' => $self->semStack[$stackPos - (7 - 7)]], $self->getAttributes($self->tokenStartStack[$stackPos - (7 - 1)], $self->tokenEndStack[$stackPos])); + }, 175 => static function ($self, $stackPos) { + $self->semValue = new Stmt\Foreach_($self->semStack[$stackPos - (9 - 3)], $self->semStack[$stackPos - (9 - 7)][0], ['keyVar' => $self->semStack[$stackPos - (9 - 5)], 'byRef' => $self->semStack[$stackPos - (9 - 7)][1], 'stmts' => $self->semStack[$stackPos - (9 - 9)]], $self->getAttributes($self->tokenStartStack[$stackPos - (9 - 1)], $self->tokenEndStack[$stackPos])); + }, 176 => static function ($self, $stackPos) { + $self->semValue = new Stmt\Foreach_($self->semStack[$stackPos - (6 - 3)], new Expr\Error($self->getAttributes($self->tokenStartStack[$stackPos - (6 - 4)], $self->tokenEndStack[$stackPos - (6 - 4)])), ['stmts' => $self->semStack[$stackPos - (6 - 6)]], $self->getAttributes($self->tokenStartStack[$stackPos - (6 - 1)], $self->tokenEndStack[$stackPos])); + }, 177 => static function ($self, $stackPos) { + $self->semValue = new Stmt\Declare_($self->semStack[$stackPos - (5 - 3)], $self->semStack[$stackPos - (5 - 5)], $self->getAttributes($self->tokenStartStack[$stackPos - (5 - 1)], $self->tokenEndStack[$stackPos])); + }, 178 => static function ($self, $stackPos) { + $self->semValue = new Stmt\TryCatch($self->semStack[$stackPos - (6 - 3)], $self->semStack[$stackPos - (6 - 5)], $self->semStack[$stackPos - (6 - 6)], $self->getAttributes($self->tokenStartStack[$stackPos - (6 - 1)], $self->tokenEndStack[$stackPos])); + $self->checkTryCatch($self->semValue); + }, 179 => static function ($self, $stackPos) { + $self->semValue = new Stmt\Goto_($self->semStack[$stackPos - (3 - 2)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 180 => static function ($self, $stackPos) { + $self->semValue = new Stmt\Label($self->semStack[$stackPos - (2 - 1)], $self->getAttributes($self->tokenStartStack[$stackPos - (2 - 1)], $self->tokenEndStack[$stackPos])); + }, 181 => static function ($self, $stackPos) { + $self->semValue = null; /* means: no statement */ - }, 162 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 163 => function ($stackPos) { - $startAttributes = $this->startAttributeStack[$stackPos - (1 - 1)]; - if (isset($startAttributes['comments'])) { - $this->semValue = new Stmt\Nop($startAttributes + $this->endAttributes); + }, 182 => null, 183 => static function ($self, $stackPos) { + $self->semValue = $self->maybeCreateNop($self->tokenStartStack[$stackPos - (1 - 1)], $self->tokenEndStack[$stackPos]); + }, 184 => static function ($self, $stackPos) { + if ($self->semStack[$stackPos - (1 - 1)] instanceof Stmt\Block) { + $self->semValue = $self->semStack[$stackPos - (1 - 1)]->stmts; + } else if ($self->semStack[$stackPos - (1 - 1)] === null) { + $self->semValue = []; } else { - $this->semValue = null; - } - if ($this->semValue === null) { - $this->semValue = array(); - } - /* means: no statement */ - }, 164 => function ($stackPos) { - $this->semValue = array(); - }, 165 => function ($stackPos) { - $this->semStack[$stackPos - (2 - 1)][] = $this->semStack[$stackPos - (2 - 2)]; - $this->semValue = $this->semStack[$stackPos - (2 - 1)]; - }, 166 => function ($stackPos) { - $this->semValue = new Stmt\Catch_(array($this->semStack[$stackPos - (8 - 3)]), $this->semStack[$stackPos - (8 - 4)], $this->semStack[$stackPos - (8 - 7)], $this->startAttributeStack[$stackPos - (8 - 1)] + $this->endAttributes); - }, 167 => function ($stackPos) { - $this->semValue = null; - }, 168 => function ($stackPos) { - $this->semValue = new Stmt\Finally_($this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 169 => function ($stackPos) { - $this->semValue = array($this->semStack[$stackPos - (1 - 1)]); - }, 170 => function ($stackPos) { - $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; - $this->semValue = $this->semStack[$stackPos - (3 - 1)]; - }, 171 => function ($stackPos) { - $this->semValue = \false; - }, 172 => function ($stackPos) { - $this->semValue = \true; - }, 173 => function ($stackPos) { - $this->semValue = \false; - }, 174 => function ($stackPos) { - $this->semValue = \true; - }, 175 => function ($stackPos) { - $this->semValue = \false; - }, 176 => function ($stackPos) { - $this->semValue = \true; - }, 177 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 178 => function ($stackPos) { - $this->semValue = new Node\Identifier($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 179 => function ($stackPos) { - $this->semValue = new Stmt\Function_($this->semStack[$stackPos - (10 - 3)], ['byRef' => $this->semStack[$stackPos - (10 - 2)], 'params' => $this->semStack[$stackPos - (10 - 5)], 'returnType' => $this->semStack[$stackPos - (10 - 7)], 'stmts' => $this->semStack[$stackPos - (10 - 9)]], $this->startAttributeStack[$stackPos - (10 - 1)] + $this->endAttributes); - }, 180 => function ($stackPos) { - $this->semValue = new Stmt\Class_($this->semStack[$stackPos - (7 - 2)], ['type' => $this->semStack[$stackPos - (7 - 1)], 'extends' => $this->semStack[$stackPos - (7 - 3)], 'implements' => $this->semStack[$stackPos - (7 - 4)], 'stmts' => $this->semStack[$stackPos - (7 - 6)]], $this->startAttributeStack[$stackPos - (7 - 1)] + $this->endAttributes); - $this->checkClass($this->semValue, $stackPos - (7 - 2)); - }, 181 => function ($stackPos) { - $this->semValue = new Stmt\Interface_($this->semStack[$stackPos - (6 - 2)], ['extends' => $this->semStack[$stackPos - (6 - 3)], 'stmts' => $this->semStack[$stackPos - (6 - 5)]], $this->startAttributeStack[$stackPos - (6 - 1)] + $this->endAttributes); - $this->checkInterface($this->semValue, $stackPos - (6 - 2)); - }, 182 => function ($stackPos) { - $this->semValue = new Stmt\Trait_($this->semStack[$stackPos - (5 - 2)], ['stmts' => $this->semStack[$stackPos - (5 - 4)]], $this->startAttributeStack[$stackPos - (5 - 1)] + $this->endAttributes); - }, 183 => function ($stackPos) { - $this->semValue = 0; - }, 184 => function ($stackPos) { - $this->semValue = Stmt\Class_::MODIFIER_ABSTRACT; - }, 185 => function ($stackPos) { - $this->semValue = Stmt\Class_::MODIFIER_FINAL; - }, 186 => function ($stackPos) { - $this->semValue = null; - }, 187 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (2 - 2)]; - }, 188 => function ($stackPos) { - $this->semValue = array(); - }, 189 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (2 - 2)]; - }, 190 => function ($stackPos) { - $this->semValue = array(); - }, 191 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (2 - 2)]; - }, 192 => function ($stackPos) { - $this->semValue = array($this->semStack[$stackPos - (1 - 1)]); - }, 193 => function ($stackPos) { - $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; - $this->semValue = $this->semStack[$stackPos - (3 - 1)]; - }, 194 => function ($stackPos) { - $this->semValue = \is_array($this->semStack[$stackPos - (1 - 1)]) ? $this->semStack[$stackPos - (1 - 1)] : array($this->semStack[$stackPos - (1 - 1)]); - }, 195 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (4 - 2)]; - }, 196 => function ($stackPos) { - $this->semValue = \is_array($this->semStack[$stackPos - (1 - 1)]) ? $this->semStack[$stackPos - (1 - 1)] : array($this->semStack[$stackPos - (1 - 1)]); - }, 197 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (4 - 2)]; - }, 198 => function ($stackPos) { - $this->semValue = \is_array($this->semStack[$stackPos - (1 - 1)]) ? $this->semStack[$stackPos - (1 - 1)] : array($this->semStack[$stackPos - (1 - 1)]); - }, 199 => function ($stackPos) { - $this->semValue = null; - }, 200 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (4 - 2)]; - }, 201 => function ($stackPos) { - $this->semValue = array($this->semStack[$stackPos - (1 - 1)]); - }, 202 => function ($stackPos) { - $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; - $this->semValue = $this->semStack[$stackPos - (3 - 1)]; - }, 203 => function ($stackPos) { - $this->semValue = new Stmt\DeclareDeclare($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 204 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (3 - 2)]; - }, 205 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (4 - 3)]; - }, 206 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (4 - 2)]; - }, 207 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (5 - 3)]; - }, 208 => function ($stackPos) { - $this->semValue = array(); - }, 209 => function ($stackPos) { - $this->semStack[$stackPos - (2 - 1)][] = $this->semStack[$stackPos - (2 - 2)]; - $this->semValue = $this->semStack[$stackPos - (2 - 1)]; - }, 210 => function ($stackPos) { - $this->semValue = new Stmt\Case_($this->semStack[$stackPos - (4 - 2)], $this->semStack[$stackPos - (4 - 4)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 211 => function ($stackPos) { - $this->semValue = new Stmt\Case_(null, $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 212 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 213 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 214 => function ($stackPos) { - $this->semValue = \is_array($this->semStack[$stackPos - (1 - 1)]) ? $this->semStack[$stackPos - (1 - 1)] : array($this->semStack[$stackPos - (1 - 1)]); - }, 215 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (4 - 2)]; - }, 216 => function ($stackPos) { - $this->semValue = array(); - }, 217 => function ($stackPos) { - $this->semStack[$stackPos - (2 - 1)][] = $this->semStack[$stackPos - (2 - 2)]; - $this->semValue = $this->semStack[$stackPos - (2 - 1)]; - }, 218 => function ($stackPos) { - $this->semValue = new Stmt\ElseIf_($this->semStack[$stackPos - (3 - 2)], \is_array($this->semStack[$stackPos - (3 - 3)]) ? $this->semStack[$stackPos - (3 - 3)] : array($this->semStack[$stackPos - (3 - 3)]), $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 219 => function ($stackPos) { - $this->semValue = array(); - }, 220 => function ($stackPos) { - $this->semStack[$stackPos - (2 - 1)][] = $this->semStack[$stackPos - (2 - 2)]; - $this->semValue = $this->semStack[$stackPos - (2 - 1)]; - }, 221 => function ($stackPos) { - $this->semValue = new Stmt\ElseIf_($this->semStack[$stackPos - (4 - 2)], $this->semStack[$stackPos - (4 - 4)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 222 => function ($stackPos) { - $this->semValue = null; - }, 223 => function ($stackPos) { - $this->semValue = new Stmt\Else_(\is_array($this->semStack[$stackPos - (2 - 2)]) ? $this->semStack[$stackPos - (2 - 2)] : array($this->semStack[$stackPos - (2 - 2)]), $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 224 => function ($stackPos) { - $this->semValue = null; - }, 225 => function ($stackPos) { - $this->semValue = new Stmt\Else_($this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 226 => function ($stackPos) { - $this->semValue = array($this->semStack[$stackPos - (1 - 1)], \false); - }, 227 => function ($stackPos) { - $this->semValue = array($this->semStack[$stackPos - (2 - 2)], \true); - }, 228 => function ($stackPos) { - $this->semValue = array($this->semStack[$stackPos - (1 - 1)], \false); - }, 229 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 230 => function ($stackPos) { - $this->semValue = array(); - }, 231 => function ($stackPos) { - $this->semValue = array($this->semStack[$stackPos - (1 - 1)]); - }, 232 => function ($stackPos) { - $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; - $this->semValue = $this->semStack[$stackPos - (3 - 1)]; - }, 233 => function ($stackPos) { - $this->semValue = new Node\Param($this->semStack[$stackPos - (4 - 4)], null, $this->semStack[$stackPos - (4 - 1)], $this->semStack[$stackPos - (4 - 2)], $this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - $this->checkParam($this->semValue); - }, 234 => function ($stackPos) { - $this->semValue = new Node\Param($this->semStack[$stackPos - (6 - 4)], $this->semStack[$stackPos - (6 - 6)], $this->semStack[$stackPos - (6 - 1)], $this->semStack[$stackPos - (6 - 2)], $this->semStack[$stackPos - (6 - 3)], $this->startAttributeStack[$stackPos - (6 - 1)] + $this->endAttributes); - $this->checkParam($this->semValue); - }, 235 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 236 => function ($stackPos) { - $this->semValue = new Node\Identifier('array', $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 237 => function ($stackPos) { - $this->semValue = new Node\Identifier('callable', $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 238 => function ($stackPos) { - $this->semValue = null; - }, 239 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 240 => function ($stackPos) { - $this->semValue = null; - }, 241 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (2 - 2)]; - }, 242 => function ($stackPos) { - $this->semValue = array(); - }, 243 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (3 - 2)]; - }, 244 => function ($stackPos) { - $this->semValue = array(new Node\Arg($this->semStack[$stackPos - (3 - 2)], \false, \false, $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes)); - }, 245 => function ($stackPos) { - $this->semValue = array($this->semStack[$stackPos - (1 - 1)]); - }, 246 => function ($stackPos) { - $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; - $this->semValue = $this->semStack[$stackPos - (3 - 1)]; - }, 247 => function ($stackPos) { - $this->semValue = new Node\Arg($this->semStack[$stackPos - (1 - 1)], \false, \false, $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 248 => function ($stackPos) { - $this->semValue = new Node\Arg($this->semStack[$stackPos - (2 - 2)], \true, \false, $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 249 => function ($stackPos) { - $this->semValue = new Node\Arg($this->semStack[$stackPos - (2 - 2)], \false, \true, $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 250 => function ($stackPos) { - $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; - $this->semValue = $this->semStack[$stackPos - (3 - 1)]; - }, 251 => function ($stackPos) { - $this->semValue = array($this->semStack[$stackPos - (1 - 1)]); - }, 252 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 253 => function ($stackPos) { - $this->semValue = new Expr\Variable($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 254 => function ($stackPos) { - $this->semValue = new Expr\Variable($this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 255 => function ($stackPos) { - $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; - $this->semValue = $this->semStack[$stackPos - (3 - 1)]; - }, 256 => function ($stackPos) { - $this->semValue = array($this->semStack[$stackPos - (1 - 1)]); - }, 257 => function ($stackPos) { - $this->semValue = new Stmt\StaticVar($this->semStack[$stackPos - (1 - 1)], null, $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 258 => function ($stackPos) { - $this->semValue = new Stmt\StaticVar($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 259 => function ($stackPos) { - if ($this->semStack[$stackPos - (2 - 2)] !== null) { - $this->semStack[$stackPos - (2 - 1)][] = $this->semStack[$stackPos - (2 - 2)]; - $this->semValue = $this->semStack[$stackPos - (2 - 1)]; + $self->semValue = [$self->semStack[$stackPos - (1 - 1)]]; + } + }, 185 => static function ($self, $stackPos) { + $self->semValue = array(); + }, 186 => static function ($self, $stackPos) { + $self->semStack[$stackPos - (2 - 1)][] = $self->semStack[$stackPos - (2 - 2)]; + $self->semValue = $self->semStack[$stackPos - (2 - 1)]; + }, 187 => static function ($self, $stackPos) { + $self->semValue = array($self->semStack[$stackPos - (1 - 1)]); + }, 188 => static function ($self, $stackPos) { + $self->semStack[$stackPos - (3 - 1)][] = $self->semStack[$stackPos - (3 - 3)]; + $self->semValue = $self->semStack[$stackPos - (3 - 1)]; + }, 189 => static function ($self, $stackPos) { + $self->semValue = new Stmt\Catch_($self->semStack[$stackPos - (8 - 3)], $self->semStack[$stackPos - (8 - 4)], $self->semStack[$stackPos - (8 - 7)], $self->getAttributes($self->tokenStartStack[$stackPos - (8 - 1)], $self->tokenEndStack[$stackPos])); + }, 190 => static function ($self, $stackPos) { + $self->semValue = null; + }, 191 => static function ($self, $stackPos) { + $self->semValue = new Stmt\Finally_($self->semStack[$stackPos - (4 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (4 - 1)], $self->tokenEndStack[$stackPos])); + }, 192 => null, 193 => static function ($self, $stackPos) { + $self->semValue = array($self->semStack[$stackPos - (1 - 1)]); + }, 194 => static function ($self, $stackPos) { + $self->semStack[$stackPos - (3 - 1)][] = $self->semStack[$stackPos - (3 - 3)]; + $self->semValue = $self->semStack[$stackPos - (3 - 1)]; + }, 195 => static function ($self, $stackPos) { + $self->semValue = \false; + }, 196 => static function ($self, $stackPos) { + $self->semValue = \true; + }, 197 => static function ($self, $stackPos) { + $self->semValue = \false; + }, 198 => static function ($self, $stackPos) { + $self->semValue = \true; + }, 199 => static function ($self, $stackPos) { + $self->semValue = \false; + }, 200 => static function ($self, $stackPos) { + $self->semValue = \true; + }, 201 => static function ($self, $stackPos) { + $self->semValue = $self->semStack[$stackPos - (3 - 2)]; + }, 202 => static function ($self, $stackPos) { + $self->semValue = []; + }, 203 => null, 204 => static function ($self, $stackPos) { + $self->semValue = new Node\Identifier($self->semStack[$stackPos - (1 - 1)], $self->getAttributes($self->tokenStartStack[$stackPos - (1 - 1)], $self->tokenEndStack[$stackPos])); + }, 205 => static function ($self, $stackPos) { + $self->semValue = new Stmt\Function_($self->semStack[$stackPos - (8 - 3)], ['byRef' => $self->semStack[$stackPos - (8 - 2)], 'params' => $self->semStack[$stackPos - (8 - 5)], 'returnType' => $self->semStack[$stackPos - (8 - 7)], 'stmts' => $self->semStack[$stackPos - (8 - 8)], 'attrGroups' => []], $self->getAttributes($self->tokenStartStack[$stackPos - (8 - 1)], $self->tokenEndStack[$stackPos])); + }, 206 => static function ($self, $stackPos) { + $self->semValue = new Stmt\Function_($self->semStack[$stackPos - (9 - 4)], ['byRef' => $self->semStack[$stackPos - (9 - 3)], 'params' => $self->semStack[$stackPos - (9 - 6)], 'returnType' => $self->semStack[$stackPos - (9 - 8)], 'stmts' => $self->semStack[$stackPos - (9 - 9)], 'attrGroups' => $self->semStack[$stackPos - (9 - 1)]], $self->getAttributes($self->tokenStartStack[$stackPos - (9 - 1)], $self->tokenEndStack[$stackPos])); + }, 207 => static function ($self, $stackPos) { + $self->semValue = new Stmt\Class_($self->semStack[$stackPos - (7 - 2)], ['type' => $self->semStack[$stackPos - (7 - 1)], 'extends' => $self->semStack[$stackPos - (7 - 3)], 'implements' => $self->semStack[$stackPos - (7 - 4)], 'stmts' => $self->semStack[$stackPos - (7 - 6)], 'attrGroups' => []], $self->getAttributes($self->tokenStartStack[$stackPos - (7 - 1)], $self->tokenEndStack[$stackPos])); + $self->checkClass($self->semValue, $stackPos - (7 - 2)); + }, 208 => static function ($self, $stackPos) { + $self->semValue = new Stmt\Class_($self->semStack[$stackPos - (8 - 3)], ['type' => $self->semStack[$stackPos - (8 - 2)], 'extends' => $self->semStack[$stackPos - (8 - 4)], 'implements' => $self->semStack[$stackPos - (8 - 5)], 'stmts' => $self->semStack[$stackPos - (8 - 7)], 'attrGroups' => $self->semStack[$stackPos - (8 - 1)]], $self->getAttributes($self->tokenStartStack[$stackPos - (8 - 1)], $self->tokenEndStack[$stackPos])); + $self->checkClass($self->semValue, $stackPos - (8 - 3)); + }, 209 => static function ($self, $stackPos) { + $self->semValue = new Stmt\Interface_($self->semStack[$stackPos - (7 - 3)], ['extends' => $self->semStack[$stackPos - (7 - 4)], 'stmts' => $self->semStack[$stackPos - (7 - 6)], 'attrGroups' => $self->semStack[$stackPos - (7 - 1)]], $self->getAttributes($self->tokenStartStack[$stackPos - (7 - 1)], $self->tokenEndStack[$stackPos])); + $self->checkInterface($self->semValue, $stackPos - (7 - 3)); + }, 210 => static function ($self, $stackPos) { + $self->semValue = new Stmt\Trait_($self->semStack[$stackPos - (6 - 3)], ['stmts' => $self->semStack[$stackPos - (6 - 5)], 'attrGroups' => $self->semStack[$stackPos - (6 - 1)]], $self->getAttributes($self->tokenStartStack[$stackPos - (6 - 1)], $self->tokenEndStack[$stackPos])); + }, 211 => static function ($self, $stackPos) { + $self->semValue = new Stmt\Enum_($self->semStack[$stackPos - (8 - 3)], ['scalarType' => $self->semStack[$stackPos - (8 - 4)], 'implements' => $self->semStack[$stackPos - (8 - 5)], 'stmts' => $self->semStack[$stackPos - (8 - 7)], 'attrGroups' => $self->semStack[$stackPos - (8 - 1)]], $self->getAttributes($self->tokenStartStack[$stackPos - (8 - 1)], $self->tokenEndStack[$stackPos])); + $self->checkEnum($self->semValue, $stackPos - (8 - 3)); + }, 212 => static function ($self, $stackPos) { + $self->semValue = null; + }, 213 => static function ($self, $stackPos) { + $self->semValue = $self->semStack[$stackPos - (2 - 2)]; + }, 214 => static function ($self, $stackPos) { + $self->semValue = null; + }, 215 => static function ($self, $stackPos) { + $self->semValue = $self->semStack[$stackPos - (2 - 2)]; + }, 216 => static function ($self, $stackPos) { + $self->semValue = 0; + }, 217 => null, 218 => null, 219 => static function ($self, $stackPos) { + $self->checkClassModifier($self->semStack[$stackPos - (2 - 1)], $self->semStack[$stackPos - (2 - 2)], $stackPos - (2 - 2)); + $self->semValue = $self->semStack[$stackPos - (2 - 1)] | $self->semStack[$stackPos - (2 - 2)]; + }, 220 => static function ($self, $stackPos) { + $self->semValue = Modifiers::ABSTRACT; + }, 221 => static function ($self, $stackPos) { + $self->semValue = Modifiers::FINAL; + }, 222 => static function ($self, $stackPos) { + $self->semValue = Modifiers::READONLY; + }, 223 => static function ($self, $stackPos) { + $self->semValue = null; + }, 224 => static function ($self, $stackPos) { + $self->semValue = $self->semStack[$stackPos - (2 - 2)]; + }, 225 => static function ($self, $stackPos) { + $self->semValue = array(); + }, 226 => static function ($self, $stackPos) { + $self->semValue = $self->semStack[$stackPos - (2 - 2)]; + }, 227 => static function ($self, $stackPos) { + $self->semValue = array(); + }, 228 => static function ($self, $stackPos) { + $self->semValue = $self->semStack[$stackPos - (2 - 2)]; + }, 229 => null, 230 => static function ($self, $stackPos) { + $self->semValue = array($self->semStack[$stackPos - (1 - 1)]); + }, 231 => static function ($self, $stackPos) { + $self->semStack[$stackPos - (3 - 1)][] = $self->semStack[$stackPos - (3 - 3)]; + $self->semValue = $self->semStack[$stackPos - (3 - 1)]; + }, 232 => null, 233 => static function ($self, $stackPos) { + $self->semValue = $self->semStack[$stackPos - (4 - 2)]; + }, 234 => null, 235 => static function ($self, $stackPos) { + $self->semValue = $self->semStack[$stackPos - (4 - 2)]; + }, 236 => static function ($self, $stackPos) { + if ($self->semStack[$stackPos - (1 - 1)] instanceof Stmt\Block) { + $self->semValue = $self->semStack[$stackPos - (1 - 1)]->stmts; + } else if ($self->semStack[$stackPos - (1 - 1)] === null) { + $self->semValue = []; } else { - $this->semValue = $this->semStack[$stackPos - (2 - 1)]; - } - }, 260 => function ($stackPos) { - $this->semValue = array(); - }, 261 => function ($stackPos) { - $startAttributes = $this->lookaheadStartAttributes; - if (isset($startAttributes['comments'])) { - $nop = new Stmt\Nop($this->createCommentNopAttributes($startAttributes['comments'])); + $self->semValue = [$self->semStack[$stackPos - (1 - 1)]]; + } + }, 237 => static function ($self, $stackPos) { + $self->semValue = null; + }, 238 => static function ($self, $stackPos) { + $self->semValue = $self->semStack[$stackPos - (4 - 2)]; + }, 239 => null, 240 => static function ($self, $stackPos) { + $self->semValue = array($self->semStack[$stackPos - (1 - 1)]); + }, 241 => static function ($self, $stackPos) { + $self->semStack[$stackPos - (3 - 1)][] = $self->semStack[$stackPos - (3 - 3)]; + $self->semValue = $self->semStack[$stackPos - (3 - 1)]; + }, 242 => static function ($self, $stackPos) { + $self->semValue = new Node\DeclareItem($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 243 => static function ($self, $stackPos) { + $self->semValue = $self->semStack[$stackPos - (3 - 2)]; + }, 244 => static function ($self, $stackPos) { + $self->semValue = $self->semStack[$stackPos - (4 - 3)]; + }, 245 => static function ($self, $stackPos) { + $self->semValue = $self->semStack[$stackPos - (4 - 2)]; + }, 246 => static function ($self, $stackPos) { + $self->semValue = $self->semStack[$stackPos - (5 - 3)]; + }, 247 => static function ($self, $stackPos) { + $self->semValue = array(); + }, 248 => static function ($self, $stackPos) { + $self->semStack[$stackPos - (2 - 1)][] = $self->semStack[$stackPos - (2 - 2)]; + $self->semValue = $self->semStack[$stackPos - (2 - 1)]; + }, 249 => static function ($self, $stackPos) { + $self->semValue = new Stmt\Case_($self->semStack[$stackPos - (4 - 2)], $self->semStack[$stackPos - (4 - 4)], $self->getAttributes($self->tokenStartStack[$stackPos - (4 - 1)], $self->tokenEndStack[$stackPos])); + }, 250 => static function ($self, $stackPos) { + $self->semValue = new Stmt\Case_(null, $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 251 => null, 252 => null, 253 => static function ($self, $stackPos) { + $self->semValue = new Expr\Match_($self->semStack[$stackPos - (7 - 3)], $self->semStack[$stackPos - (7 - 6)], $self->getAttributes($self->tokenStartStack[$stackPos - (7 - 1)], $self->tokenEndStack[$stackPos])); + }, 254 => static function ($self, $stackPos) { + $self->semValue = []; + }, 255 => null, 256 => static function ($self, $stackPos) { + $self->semValue = array($self->semStack[$stackPos - (1 - 1)]); + }, 257 => static function ($self, $stackPos) { + $self->semStack[$stackPos - (3 - 1)][] = $self->semStack[$stackPos - (3 - 3)]; + $self->semValue = $self->semStack[$stackPos - (3 - 1)]; + }, 258 => static function ($self, $stackPos) { + $self->semValue = new Node\MatchArm($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 259 => static function ($self, $stackPos) { + $self->semValue = new Node\MatchArm(null, $self->semStack[$stackPos - (4 - 4)], $self->getAttributes($self->tokenStartStack[$stackPos - (4 - 1)], $self->tokenEndStack[$stackPos])); + }, 260 => static function ($self, $stackPos) { + $self->semValue = $self->semStack[$stackPos - (1 - 1)]; + }, 261 => static function ($self, $stackPos) { + $self->semValue = $self->semStack[$stackPos - (4 - 2)]; + }, 262 => static function ($self, $stackPos) { + $self->semValue = array(); + }, 263 => static function ($self, $stackPos) { + $self->semStack[$stackPos - (2 - 1)][] = $self->semStack[$stackPos - (2 - 2)]; + $self->semValue = $self->semStack[$stackPos - (2 - 1)]; + }, 264 => static function ($self, $stackPos) { + $self->semValue = new Stmt\ElseIf_($self->semStack[$stackPos - (5 - 3)], $self->semStack[$stackPos - (5 - 5)], $self->getAttributes($self->tokenStartStack[$stackPos - (5 - 1)], $self->tokenEndStack[$stackPos])); + }, 265 => static function ($self, $stackPos) { + $self->semValue = array(); + }, 266 => static function ($self, $stackPos) { + $self->semStack[$stackPos - (2 - 1)][] = $self->semStack[$stackPos - (2 - 2)]; + $self->semValue = $self->semStack[$stackPos - (2 - 1)]; + }, 267 => static function ($self, $stackPos) { + $self->semValue = new Stmt\ElseIf_($self->semStack[$stackPos - (6 - 3)], $self->semStack[$stackPos - (6 - 6)], $self->getAttributes($self->tokenStartStack[$stackPos - (6 - 1)], $self->tokenEndStack[$stackPos])); + $self->fixupAlternativeElse($self->semValue); + }, 268 => static function ($self, $stackPos) { + $self->semValue = null; + }, 269 => static function ($self, $stackPos) { + $self->semValue = new Stmt\Else_($self->semStack[$stackPos - (2 - 2)], $self->getAttributes($self->tokenStartStack[$stackPos - (2 - 1)], $self->tokenEndStack[$stackPos])); + }, 270 => static function ($self, $stackPos) { + $self->semValue = null; + }, 271 => static function ($self, $stackPos) { + $self->semValue = new Stmt\Else_($self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + $self->fixupAlternativeElse($self->semValue); + }, 272 => static function ($self, $stackPos) { + $self->semValue = array($self->semStack[$stackPos - (1 - 1)], \false); + }, 273 => static function ($self, $stackPos) { + $self->semValue = array($self->semStack[$stackPos - (2 - 2)], \true); + }, 274 => static function ($self, $stackPos) { + $self->semValue = array($self->semStack[$stackPos - (1 - 1)], \false); + }, 275 => static function ($self, $stackPos) { + $self->semValue = array($self->fixupArrayDestructuring($self->semStack[$stackPos - (1 - 1)]), \false); + }, 276 => null, 277 => static function ($self, $stackPos) { + $self->semValue = array(); + }, 278 => static function ($self, $stackPos) { + $self->semValue = array($self->semStack[$stackPos - (1 - 1)]); + }, 279 => static function ($self, $stackPos) { + $self->semStack[$stackPos - (3 - 1)][] = $self->semStack[$stackPos - (3 - 3)]; + $self->semValue = $self->semStack[$stackPos - (3 - 1)]; + }, 280 => static function ($self, $stackPos) { + $self->semValue = 0; + }, 281 => static function ($self, $stackPos) { + $self->checkModifier($self->semStack[$stackPos - (2 - 1)], $self->semStack[$stackPos - (2 - 2)], $stackPos - (2 - 2)); + $self->semValue = $self->semStack[$stackPos - (2 - 1)] | $self->semStack[$stackPos - (2 - 2)]; + }, 282 => static function ($self, $stackPos) { + $self->semValue = Modifiers::PUBLIC; + }, 283 => static function ($self, $stackPos) { + $self->semValue = Modifiers::PROTECTED; + }, 284 => static function ($self, $stackPos) { + $self->semValue = Modifiers::PRIVATE; + }, 285 => static function ($self, $stackPos) { + $self->semValue = Modifiers::READONLY; + }, 286 => static function ($self, $stackPos) { + $self->semValue = new Node\Param($self->semStack[$stackPos - (6 - 6)], null, $self->semStack[$stackPos - (6 - 3)], $self->semStack[$stackPos - (6 - 4)], $self->semStack[$stackPos - (6 - 5)], $self->getAttributes($self->tokenStartStack[$stackPos - (6 - 1)], $self->tokenEndStack[$stackPos]), $self->semStack[$stackPos - (6 - 2)], $self->semStack[$stackPos - (6 - 1)]); + $self->checkParam($self->semValue); + }, 287 => static function ($self, $stackPos) { + $self->semValue = new Node\Param($self->semStack[$stackPos - (8 - 6)], $self->semStack[$stackPos - (8 - 8)], $self->semStack[$stackPos - (8 - 3)], $self->semStack[$stackPos - (8 - 4)], $self->semStack[$stackPos - (8 - 5)], $self->getAttributes($self->tokenStartStack[$stackPos - (8 - 1)], $self->tokenEndStack[$stackPos]), $self->semStack[$stackPos - (8 - 2)], $self->semStack[$stackPos - (8 - 1)]); + $self->checkParam($self->semValue); + }, 288 => static function ($self, $stackPos) { + $self->semValue = new Node\Param(new Expr\Error($self->getAttributes($self->tokenStartStack[$stackPos - (6 - 1)], $self->tokenEndStack[$stackPos])), null, $self->semStack[$stackPos - (6 - 3)], $self->semStack[$stackPos - (6 - 4)], $self->semStack[$stackPos - (6 - 5)], $self->getAttributes($self->tokenStartStack[$stackPos - (6 - 1)], $self->tokenEndStack[$stackPos]), $self->semStack[$stackPos - (6 - 2)], $self->semStack[$stackPos - (6 - 1)]); + }, 289 => null, 290 => static function ($self, $stackPos) { + $self->semValue = new Node\NullableType($self->semStack[$stackPos - (2 - 2)], $self->getAttributes($self->tokenStartStack[$stackPos - (2 - 1)], $self->tokenEndStack[$stackPos])); + }, 291 => static function ($self, $stackPos) { + $self->semValue = new Node\UnionType($self->semStack[$stackPos - (1 - 1)], $self->getAttributes($self->tokenStartStack[$stackPos - (1 - 1)], $self->tokenEndStack[$stackPos])); + }, 292 => null, 293 => null, 294 => static function ($self, $stackPos) { + $self->semValue = new Node\Name('static', $self->getAttributes($self->tokenStartStack[$stackPos - (1 - 1)], $self->tokenEndStack[$stackPos])); + }, 295 => static function ($self, $stackPos) { + $self->semValue = $self->handleBuiltinTypes($self->semStack[$stackPos - (1 - 1)]); + }, 296 => static function ($self, $stackPos) { + $self->semValue = new Node\Identifier('array', $self->getAttributes($self->tokenStartStack[$stackPos - (1 - 1)], $self->tokenEndStack[$stackPos])); + }, 297 => static function ($self, $stackPos) { + $self->semValue = new Node\Identifier('callable', $self->getAttributes($self->tokenStartStack[$stackPos - (1 - 1)], $self->tokenEndStack[$stackPos])); + }, 298 => null, 299 => static function ($self, $stackPos) { + $self->semValue = $self->semStack[$stackPos - (3 - 2)]; + }, 300 => static function ($self, $stackPos) { + $self->semValue = array($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)]); + }, 301 => static function ($self, $stackPos) { + $self->semStack[$stackPos - (3 - 1)][] = $self->semStack[$stackPos - (3 - 3)]; + $self->semValue = $self->semStack[$stackPos - (3 - 1)]; + }, 302 => null, 303 => static function ($self, $stackPos) { + $self->semValue = $self->semStack[$stackPos - (3 - 2)]; + }, 304 => static function ($self, $stackPos) { + $self->semValue = array($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)]); + }, 305 => static function ($self, $stackPos) { + $self->semStack[$stackPos - (3 - 1)][] = $self->semStack[$stackPos - (3 - 3)]; + $self->semValue = $self->semStack[$stackPos - (3 - 1)]; + }, 306 => static function ($self, $stackPos) { + $self->semValue = array($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)]); + }, 307 => static function ($self, $stackPos) { + $self->semStack[$stackPos - (3 - 1)][] = $self->semStack[$stackPos - (3 - 3)]; + $self->semValue = $self->semStack[$stackPos - (3 - 1)]; + }, 308 => static function ($self, $stackPos) { + $self->semValue = new Node\IntersectionType($self->semStack[$stackPos - (1 - 1)], $self->getAttributes($self->tokenStartStack[$stackPos - (1 - 1)], $self->tokenEndStack[$stackPos])); + }, 309 => static function ($self, $stackPos) { + $self->semValue = array($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)]); + }, 310 => static function ($self, $stackPos) { + $self->semStack[$stackPos - (3 - 1)][] = $self->semStack[$stackPos - (3 - 3)]; + $self->semValue = $self->semStack[$stackPos - (3 - 1)]; + }, 311 => static function ($self, $stackPos) { + $self->semValue = new Node\IntersectionType($self->semStack[$stackPos - (1 - 1)], $self->getAttributes($self->tokenStartStack[$stackPos - (1 - 1)], $self->tokenEndStack[$stackPos])); + }, 312 => null, 313 => static function ($self, $stackPos) { + $self->semValue = new Node\NullableType($self->semStack[$stackPos - (2 - 2)], $self->getAttributes($self->tokenStartStack[$stackPos - (2 - 1)], $self->tokenEndStack[$stackPos])); + }, 314 => static function ($self, $stackPos) { + $self->semValue = new Node\UnionType($self->semStack[$stackPos - (1 - 1)], $self->getAttributes($self->tokenStartStack[$stackPos - (1 - 1)], $self->tokenEndStack[$stackPos])); + }, 315 => null, 316 => static function ($self, $stackPos) { + $self->semValue = null; + }, 317 => null, 318 => static function ($self, $stackPos) { + $self->semValue = null; + }, 319 => static function ($self, $stackPos) { + $self->semValue = $self->semStack[$stackPos - (2 - 2)]; + }, 320 => static function ($self, $stackPos) { + $self->semValue = null; + }, 321 => static function ($self, $stackPos) { + $self->semValue = array(); + }, 322 => static function ($self, $stackPos) { + $self->semValue = $self->semStack[$stackPos - (4 - 2)]; + }, 323 => static function ($self, $stackPos) { + $self->semValue = array($self->semStack[$stackPos - (3 - 2)]); + }, 324 => static function ($self, $stackPos) { + $self->semValue = new Node\VariadicPlaceholder($self->getAttributes($self->tokenStartStack[$stackPos - (1 - 1)], $self->tokenEndStack[$stackPos])); + }, 325 => static function ($self, $stackPos) { + $self->semValue = array($self->semStack[$stackPos - (1 - 1)]); + }, 326 => static function ($self, $stackPos) { + $self->semStack[$stackPos - (3 - 1)][] = $self->semStack[$stackPos - (3 - 3)]; + $self->semValue = $self->semStack[$stackPos - (3 - 1)]; + }, 327 => static function ($self, $stackPos) { + $self->semValue = new Node\Arg($self->semStack[$stackPos - (1 - 1)], \false, \false, $self->getAttributes($self->tokenStartStack[$stackPos - (1 - 1)], $self->tokenEndStack[$stackPos])); + }, 328 => static function ($self, $stackPos) { + $self->semValue = new Node\Arg($self->semStack[$stackPos - (2 - 2)], \true, \false, $self->getAttributes($self->tokenStartStack[$stackPos - (2 - 1)], $self->tokenEndStack[$stackPos])); + }, 329 => static function ($self, $stackPos) { + $self->semValue = new Node\Arg($self->semStack[$stackPos - (2 - 2)], \false, \true, $self->getAttributes($self->tokenStartStack[$stackPos - (2 - 1)], $self->tokenEndStack[$stackPos])); + }, 330 => static function ($self, $stackPos) { + $self->semValue = new Node\Arg($self->semStack[$stackPos - (3 - 3)], \false, \false, $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos]), $self->semStack[$stackPos - (3 - 1)]); + }, 331 => null, 332 => static function ($self, $stackPos) { + $self->semStack[$stackPos - (3 - 1)][] = $self->semStack[$stackPos - (3 - 3)]; + $self->semValue = $self->semStack[$stackPos - (3 - 1)]; + }, 333 => static function ($self, $stackPos) { + $self->semValue = array($self->semStack[$stackPos - (1 - 1)]); + }, 334 => null, 335 => null, 336 => static function ($self, $stackPos) { + $self->semStack[$stackPos - (3 - 1)][] = $self->semStack[$stackPos - (3 - 3)]; + $self->semValue = $self->semStack[$stackPos - (3 - 1)]; + }, 337 => static function ($self, $stackPos) { + $self->semValue = array($self->semStack[$stackPos - (1 - 1)]); + }, 338 => static function ($self, $stackPos) { + $self->semValue = new Node\StaticVar($self->semStack[$stackPos - (1 - 1)], null, $self->getAttributes($self->tokenStartStack[$stackPos - (1 - 1)], $self->tokenEndStack[$stackPos])); + }, 339 => static function ($self, $stackPos) { + $self->semValue = new Node\StaticVar($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 340 => static function ($self, $stackPos) { + if ($self->semStack[$stackPos - (2 - 2)] !== null) { + $self->semStack[$stackPos - (2 - 1)][] = $self->semStack[$stackPos - (2 - 2)]; + $self->semValue = $self->semStack[$stackPos - (2 - 1)]; } else { - $nop = null; + $self->semValue = $self->semStack[$stackPos - (2 - 1)]; } + }, 341 => static function ($self, $stackPos) { + $self->semValue = array(); + }, 342 => static function ($self, $stackPos) { + $nop = $self->maybeCreateZeroLengthNop($self->tokenPos); if ($nop !== null) { - $this->semStack[$stackPos - (1 - 1)][] = $nop; - } - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 262 => function ($stackPos) { - $this->semValue = new Stmt\Property($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 2)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - $this->checkProperty($this->semValue, $stackPos - (3 - 1)); - }, 263 => function ($stackPos) { - $this->semValue = new Stmt\ClassConst($this->semStack[$stackPos - (3 - 2)], 0, $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 264 => function ($stackPos) { - $this->semValue = new Stmt\ClassMethod($this->semStack[$stackPos - (9 - 4)], ['type' => $this->semStack[$stackPos - (9 - 1)], 'byRef' => $this->semStack[$stackPos - (9 - 3)], 'params' => $this->semStack[$stackPos - (9 - 6)], 'returnType' => $this->semStack[$stackPos - (9 - 8)], 'stmts' => $this->semStack[$stackPos - (9 - 9)]], $this->startAttributeStack[$stackPos - (9 - 1)] + $this->endAttributes); - $this->checkClassMethod($this->semValue, $stackPos - (9 - 1)); - }, 265 => function ($stackPos) { - $this->semValue = new Stmt\TraitUse($this->semStack[$stackPos - (3 - 2)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 266 => function ($stackPos) { - $this->semValue = array(); - }, 267 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (3 - 2)]; - }, 268 => function ($stackPos) { - $this->semValue = array(); - }, 269 => function ($stackPos) { - $this->semStack[$stackPos - (2 - 1)][] = $this->semStack[$stackPos - (2 - 2)]; - $this->semValue = $this->semStack[$stackPos - (2 - 1)]; - }, 270 => function ($stackPos) { - $this->semValue = new Stmt\TraitUseAdaptation\Precedence($this->semStack[$stackPos - (4 - 1)][0], $this->semStack[$stackPos - (4 - 1)][1], $this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 271 => function ($stackPos) { - $this->semValue = new Stmt\TraitUseAdaptation\Alias($this->semStack[$stackPos - (5 - 1)][0], $this->semStack[$stackPos - (5 - 1)][1], $this->semStack[$stackPos - (5 - 3)], $this->semStack[$stackPos - (5 - 4)], $this->startAttributeStack[$stackPos - (5 - 1)] + $this->endAttributes); - }, 272 => function ($stackPos) { - $this->semValue = new Stmt\TraitUseAdaptation\Alias($this->semStack[$stackPos - (4 - 1)][0], $this->semStack[$stackPos - (4 - 1)][1], $this->semStack[$stackPos - (4 - 3)], null, $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 273 => function ($stackPos) { - $this->semValue = new Stmt\TraitUseAdaptation\Alias($this->semStack[$stackPos - (4 - 1)][0], $this->semStack[$stackPos - (4 - 1)][1], null, $this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 274 => function ($stackPos) { - $this->semValue = new Stmt\TraitUseAdaptation\Alias($this->semStack[$stackPos - (4 - 1)][0], $this->semStack[$stackPos - (4 - 1)][1], null, $this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 275 => function ($stackPos) { - $this->semValue = array($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)]); - }, 276 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 277 => function ($stackPos) { - $this->semValue = array(null, $this->semStack[$stackPos - (1 - 1)]); - }, 278 => function ($stackPos) { - $this->semValue = null; - }, 279 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (3 - 2)]; - }, 280 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 281 => function ($stackPos) { - $this->semValue = 0; - }, 282 => function ($stackPos) { - $this->semValue = 0; - }, 283 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 284 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 285 => function ($stackPos) { - $this->checkModifier($this->semStack[$stackPos - (2 - 1)], $this->semStack[$stackPos - (2 - 2)], $stackPos - (2 - 2)); - $this->semValue = $this->semStack[$stackPos - (2 - 1)] | $this->semStack[$stackPos - (2 - 2)]; - }, 286 => function ($stackPos) { - $this->semValue = Stmt\Class_::MODIFIER_PUBLIC; - }, 287 => function ($stackPos) { - $this->semValue = Stmt\Class_::MODIFIER_PROTECTED; - }, 288 => function ($stackPos) { - $this->semValue = Stmt\Class_::MODIFIER_PRIVATE; - }, 289 => function ($stackPos) { - $this->semValue = Stmt\Class_::MODIFIER_STATIC; - }, 290 => function ($stackPos) { - $this->semValue = Stmt\Class_::MODIFIER_ABSTRACT; - }, 291 => function ($stackPos) { - $this->semValue = Stmt\Class_::MODIFIER_FINAL; - }, 292 => function ($stackPos) { - $this->semValue = array($this->semStack[$stackPos - (1 - 1)]); - }, 293 => function ($stackPos) { - $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; - $this->semValue = $this->semStack[$stackPos - (3 - 1)]; - }, 294 => function ($stackPos) { - $this->semValue = new Node\VarLikeIdentifier(\substr($this->semStack[$stackPos - (1 - 1)], 1), $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 295 => function ($stackPos) { - $this->semValue = new Stmt\PropertyProperty($this->semStack[$stackPos - (1 - 1)], null, $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 296 => function ($stackPos) { - $this->semValue = new Stmt\PropertyProperty($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 297 => function ($stackPos) { - $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; - $this->semValue = $this->semStack[$stackPos - (3 - 1)]; - }, 298 => function ($stackPos) { - $this->semValue = array($this->semStack[$stackPos - (1 - 1)]); - }, 299 => function ($stackPos) { - $this->semValue = array(); - }, 300 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 301 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 302 => function ($stackPos) { - $this->semValue = new Expr\Assign($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 303 => function ($stackPos) { - $this->semValue = new Expr\Assign($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 304 => function ($stackPos) { - $this->semValue = new Expr\AssignRef($this->semStack[$stackPos - (4 - 1)], $this->semStack[$stackPos - (4 - 4)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 305 => function ($stackPos) { - $this->semValue = new Expr\AssignRef($this->semStack[$stackPos - (4 - 1)], $this->semStack[$stackPos - (4 - 4)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 306 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 307 => function ($stackPos) { - $this->semValue = new Expr\Clone_($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 308 => function ($stackPos) { - $this->semValue = new Expr\AssignOp\Plus($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 309 => function ($stackPos) { - $this->semValue = new Expr\AssignOp\Minus($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 310 => function ($stackPos) { - $this->semValue = new Expr\AssignOp\Mul($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 311 => function ($stackPos) { - $this->semValue = new Expr\AssignOp\Div($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 312 => function ($stackPos) { - $this->semValue = new Expr\AssignOp\Concat($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 313 => function ($stackPos) { - $this->semValue = new Expr\AssignOp\Mod($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 314 => function ($stackPos) { - $this->semValue = new Expr\AssignOp\BitwiseAnd($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 315 => function ($stackPos) { - $this->semValue = new Expr\AssignOp\BitwiseOr($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 316 => function ($stackPos) { - $this->semValue = new Expr\AssignOp\BitwiseXor($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 317 => function ($stackPos) { - $this->semValue = new Expr\AssignOp\ShiftLeft($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 318 => function ($stackPos) { - $this->semValue = new Expr\AssignOp\ShiftRight($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 319 => function ($stackPos) { - $this->semValue = new Expr\AssignOp\Pow($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 320 => function ($stackPos) { - $this->semValue = new Expr\AssignOp\Coalesce($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 321 => function ($stackPos) { - $this->semValue = new Expr\PostInc($this->semStack[$stackPos - (2 - 1)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 322 => function ($stackPos) { - $this->semValue = new Expr\PreInc($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 323 => function ($stackPos) { - $this->semValue = new Expr\PostDec($this->semStack[$stackPos - (2 - 1)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 324 => function ($stackPos) { - $this->semValue = new Expr\PreDec($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 325 => function ($stackPos) { - $this->semValue = new Expr\BinaryOp\BooleanOr($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 326 => function ($stackPos) { - $this->semValue = new Expr\BinaryOp\BooleanAnd($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 327 => function ($stackPos) { - $this->semValue = new Expr\BinaryOp\LogicalOr($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 328 => function ($stackPos) { - $this->semValue = new Expr\BinaryOp\LogicalAnd($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 329 => function ($stackPos) { - $this->semValue = new Expr\BinaryOp\LogicalXor($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 330 => function ($stackPos) { - $this->semValue = new Expr\BinaryOp\BitwiseOr($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 331 => function ($stackPos) { - $this->semValue = new Expr\BinaryOp\BitwiseAnd($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 332 => function ($stackPos) { - $this->semValue = new Expr\BinaryOp\BitwiseAnd($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 333 => function ($stackPos) { - $this->semValue = new Expr\BinaryOp\BitwiseXor($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 334 => function ($stackPos) { - $this->semValue = new Expr\BinaryOp\Concat($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 335 => function ($stackPos) { - $this->semValue = new Expr\BinaryOp\Plus($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 336 => function ($stackPos) { - $this->semValue = new Expr\BinaryOp\Minus($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 337 => function ($stackPos) { - $this->semValue = new Expr\BinaryOp\Mul($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 338 => function ($stackPos) { - $this->semValue = new Expr\BinaryOp\Div($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 339 => function ($stackPos) { - $this->semValue = new Expr\BinaryOp\Mod($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 340 => function ($stackPos) { - $this->semValue = new Expr\BinaryOp\ShiftLeft($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 341 => function ($stackPos) { - $this->semValue = new Expr\BinaryOp\ShiftRight($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 342 => function ($stackPos) { - $this->semValue = new Expr\BinaryOp\Pow($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 343 => function ($stackPos) { - $this->semValue = new Expr\UnaryPlus($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 344 => function ($stackPos) { - $this->semValue = new Expr\UnaryMinus($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 345 => function ($stackPos) { - $this->semValue = new Expr\BooleanNot($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 346 => function ($stackPos) { - $this->semValue = new Expr\BitwiseNot($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 347 => function ($stackPos) { - $this->semValue = new Expr\BinaryOp\Identical($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 348 => function ($stackPos) { - $this->semValue = new Expr\BinaryOp\NotIdentical($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 349 => function ($stackPos) { - $this->semValue = new Expr\BinaryOp\Equal($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 350 => function ($stackPos) { - $this->semValue = new Expr\BinaryOp\NotEqual($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 351 => function ($stackPos) { - $this->semValue = new Expr\BinaryOp\Spaceship($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 352 => function ($stackPos) { - $this->semValue = new Expr\BinaryOp\Smaller($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 353 => function ($stackPos) { - $this->semValue = new Expr\BinaryOp\SmallerOrEqual($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 354 => function ($stackPos) { - $this->semValue = new Expr\BinaryOp\Greater($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 355 => function ($stackPos) { - $this->semValue = new Expr\BinaryOp\GreaterOrEqual($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 356 => function ($stackPos) { - $this->semValue = new Expr\Instanceof_($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 357 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 358 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (3 - 2)]; - }, 359 => function ($stackPos) { - $this->semValue = new Expr\Ternary($this->semStack[$stackPos - (5 - 1)], $this->semStack[$stackPos - (5 - 3)], $this->semStack[$stackPos - (5 - 5)], $this->startAttributeStack[$stackPos - (5 - 1)] + $this->endAttributes); - }, 360 => function ($stackPos) { - $this->semValue = new Expr\Ternary($this->semStack[$stackPos - (4 - 1)], null, $this->semStack[$stackPos - (4 - 4)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 361 => function ($stackPos) { - $this->semValue = new Expr\BinaryOp\Coalesce($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 362 => function ($stackPos) { - $this->semValue = new Expr\Isset_($this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 363 => function ($stackPos) { - $this->semValue = new Expr\Empty_($this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 364 => function ($stackPos) { - $this->semValue = new Expr\Include_($this->semStack[$stackPos - (2 - 2)], Expr\Include_::TYPE_INCLUDE, $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 365 => function ($stackPos) { - $this->semValue = new Expr\Include_($this->semStack[$stackPos - (2 - 2)], Expr\Include_::TYPE_INCLUDE_ONCE, $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 366 => function ($stackPos) { - $this->semValue = new Expr\Eval_($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 367 => function ($stackPos) { - $this->semValue = new Expr\Include_($this->semStack[$stackPos - (2 - 2)], Expr\Include_::TYPE_REQUIRE, $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 368 => function ($stackPos) { - $this->semValue = new Expr\Include_($this->semStack[$stackPos - (2 - 2)], Expr\Include_::TYPE_REQUIRE_ONCE, $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 369 => function ($stackPos) { - $this->semValue = new Expr\Cast\Int_($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 370 => function ($stackPos) { - $attrs = $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes; - $attrs['kind'] = $this->getFloatCastKind($this->semStack[$stackPos - (2 - 1)]); - $this->semValue = new Expr\Cast\Double($this->semStack[$stackPos - (2 - 2)], $attrs); - }, 371 => function ($stackPos) { - $this->semValue = new Expr\Cast\String_($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 372 => function ($stackPos) { - $this->semValue = new Expr\Cast\Array_($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 373 => function ($stackPos) { - $this->semValue = new Expr\Cast\Object_($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 374 => function ($stackPos) { - $this->semValue = new Expr\Cast\Bool_($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 375 => function ($stackPos) { - $this->semValue = new Expr\Cast\Unset_($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 376 => function ($stackPos) { - $attrs = $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes; - $attrs['kind'] = \strtolower($this->semStack[$stackPos - (2 - 1)]) === 'exit' ? Expr\Exit_::KIND_EXIT : Expr\Exit_::KIND_DIE; - $this->semValue = new Expr\Exit_($this->semStack[$stackPos - (2 - 2)], $attrs); - }, 377 => function ($stackPos) { - $this->semValue = new Expr\ErrorSuppress($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 378 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 379 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 380 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 381 => function ($stackPos) { - $this->semValue = new Expr\ShellExec($this->semStack[$stackPos - (3 - 2)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 382 => function ($stackPos) { - $this->semValue = new Expr\Print_($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 383 => function ($stackPos) { - $this->semValue = new Expr\Yield_(null, null, $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 384 => function ($stackPos) { - $this->semValue = new Expr\YieldFrom($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 385 => function ($stackPos) { - $this->semValue = new Expr\Closure(['static' => \false, 'byRef' => $this->semStack[$stackPos - (10 - 2)], 'params' => $this->semStack[$stackPos - (10 - 4)], 'uses' => $this->semStack[$stackPos - (10 - 6)], 'returnType' => $this->semStack[$stackPos - (10 - 7)], 'stmts' => $this->semStack[$stackPos - (10 - 9)]], $this->startAttributeStack[$stackPos - (10 - 1)] + $this->endAttributes); - }, 386 => function ($stackPos) { - $this->semValue = new Expr\Closure(['static' => \true, 'byRef' => $this->semStack[$stackPos - (11 - 3)], 'params' => $this->semStack[$stackPos - (11 - 5)], 'uses' => $this->semStack[$stackPos - (11 - 7)], 'returnType' => $this->semStack[$stackPos - (11 - 8)], 'stmts' => $this->semStack[$stackPos - (11 - 10)]], $this->startAttributeStack[$stackPos - (11 - 1)] + $this->endAttributes); - }, 387 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (3 - 2)]; - }, 388 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (3 - 2)]; - }, 389 => function ($stackPos) { - $this->semValue = new Expr\Yield_($this->semStack[$stackPos - (2 - 2)], null, $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 390 => function ($stackPos) { - $this->semValue = new Expr\Yield_($this->semStack[$stackPos - (4 - 4)], $this->semStack[$stackPos - (4 - 2)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 391 => function ($stackPos) { - $attrs = $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes; - $attrs['kind'] = Expr\Array_::KIND_LONG; - $this->semValue = new Expr\Array_($this->semStack[$stackPos - (4 - 3)], $attrs); - }, 392 => function ($stackPos) { - $attrs = $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes; + $self->semStack[$stackPos - (1 - 1)][] = $nop; + } + $self->semValue = $self->semStack[$stackPos - (1 - 1)]; + }, 343 => static function ($self, $stackPos) { + $self->semValue = new Stmt\Property($self->semStack[$stackPos - (5 - 2)], $self->semStack[$stackPos - (5 - 4)], $self->getAttributes($self->tokenStartStack[$stackPos - (5 - 1)], $self->tokenEndStack[$stackPos]), $self->semStack[$stackPos - (5 - 3)], $self->semStack[$stackPos - (5 - 1)]); + $self->checkProperty($self->semValue, $stackPos - (5 - 2)); + }, 344 => static function ($self, $stackPos) { + $self->semValue = new Stmt\ClassConst($self->semStack[$stackPos - (5 - 4)], $self->semStack[$stackPos - (5 - 2)], $self->getAttributes($self->tokenStartStack[$stackPos - (5 - 1)], $self->tokenEndStack[$stackPos]), $self->semStack[$stackPos - (5 - 1)]); + $self->checkClassConst($self->semValue, $stackPos - (5 - 2)); + }, 345 => static function ($self, $stackPos) { + $self->semValue = new Stmt\ClassConst($self->semStack[$stackPos - (6 - 5)], $self->semStack[$stackPos - (6 - 2)], $self->getAttributes($self->tokenStartStack[$stackPos - (6 - 1)], $self->tokenEndStack[$stackPos]), $self->semStack[$stackPos - (6 - 1)], $self->semStack[$stackPos - (6 - 4)]); + $self->checkClassConst($self->semValue, $stackPos - (6 - 2)); + }, 346 => static function ($self, $stackPos) { + $self->semValue = new Stmt\ClassMethod($self->semStack[$stackPos - (10 - 5)], ['type' => $self->semStack[$stackPos - (10 - 2)], 'byRef' => $self->semStack[$stackPos - (10 - 4)], 'params' => $self->semStack[$stackPos - (10 - 7)], 'returnType' => $self->semStack[$stackPos - (10 - 9)], 'stmts' => $self->semStack[$stackPos - (10 - 10)], 'attrGroups' => $self->semStack[$stackPos - (10 - 1)]], $self->getAttributes($self->tokenStartStack[$stackPos - (10 - 1)], $self->tokenEndStack[$stackPos])); + $self->checkClassMethod($self->semValue, $stackPos - (10 - 2)); + }, 347 => static function ($self, $stackPos) { + $self->semValue = new Stmt\TraitUse($self->semStack[$stackPos - (3 - 2)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 348 => static function ($self, $stackPos) { + $self->semValue = new Stmt\EnumCase($self->semStack[$stackPos - (5 - 3)], $self->semStack[$stackPos - (5 - 4)], $self->semStack[$stackPos - (5 - 1)], $self->getAttributes($self->tokenStartStack[$stackPos - (5 - 1)], $self->tokenEndStack[$stackPos])); + }, 349 => static function ($self, $stackPos) { + $self->semValue = null; + /* will be skipped */ + }, 350 => static function ($self, $stackPos) { + $self->semValue = array(); + }, 351 => static function ($self, $stackPos) { + $self->semValue = $self->semStack[$stackPos - (3 - 2)]; + }, 352 => static function ($self, $stackPos) { + $self->semValue = array(); + }, 353 => static function ($self, $stackPos) { + $self->semStack[$stackPos - (2 - 1)][] = $self->semStack[$stackPos - (2 - 2)]; + $self->semValue = $self->semStack[$stackPos - (2 - 1)]; + }, 354 => static function ($self, $stackPos) { + $self->semValue = new Stmt\TraitUseAdaptation\Precedence($self->semStack[$stackPos - (4 - 1)][0], $self->semStack[$stackPos - (4 - 1)][1], $self->semStack[$stackPos - (4 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (4 - 1)], $self->tokenEndStack[$stackPos])); + }, 355 => static function ($self, $stackPos) { + $self->semValue = new Stmt\TraitUseAdaptation\Alias($self->semStack[$stackPos - (5 - 1)][0], $self->semStack[$stackPos - (5 - 1)][1], $self->semStack[$stackPos - (5 - 3)], $self->semStack[$stackPos - (5 - 4)], $self->getAttributes($self->tokenStartStack[$stackPos - (5 - 1)], $self->tokenEndStack[$stackPos])); + }, 356 => static function ($self, $stackPos) { + $self->semValue = new Stmt\TraitUseAdaptation\Alias($self->semStack[$stackPos - (4 - 1)][0], $self->semStack[$stackPos - (4 - 1)][1], $self->semStack[$stackPos - (4 - 3)], null, $self->getAttributes($self->tokenStartStack[$stackPos - (4 - 1)], $self->tokenEndStack[$stackPos])); + }, 357 => static function ($self, $stackPos) { + $self->semValue = new Stmt\TraitUseAdaptation\Alias($self->semStack[$stackPos - (4 - 1)][0], $self->semStack[$stackPos - (4 - 1)][1], null, $self->semStack[$stackPos - (4 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (4 - 1)], $self->tokenEndStack[$stackPos])); + }, 358 => static function ($self, $stackPos) { + $self->semValue = new Stmt\TraitUseAdaptation\Alias($self->semStack[$stackPos - (4 - 1)][0], $self->semStack[$stackPos - (4 - 1)][1], null, $self->semStack[$stackPos - (4 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (4 - 1)], $self->tokenEndStack[$stackPos])); + }, 359 => static function ($self, $stackPos) { + $self->semValue = array($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)]); + }, 360 => null, 361 => static function ($self, $stackPos) { + $self->semValue = array(null, $self->semStack[$stackPos - (1 - 1)]); + }, 362 => static function ($self, $stackPos) { + $self->semValue = null; + }, 363 => null, 364 => null, 365 => static function ($self, $stackPos) { + $self->semValue = 0; + }, 366 => static function ($self, $stackPos) { + $self->semValue = 0; + }, 367 => null, 368 => null, 369 => static function ($self, $stackPos) { + $self->checkModifier($self->semStack[$stackPos - (2 - 1)], $self->semStack[$stackPos - (2 - 2)], $stackPos - (2 - 2)); + $self->semValue = $self->semStack[$stackPos - (2 - 1)] | $self->semStack[$stackPos - (2 - 2)]; + }, 370 => static function ($self, $stackPos) { + $self->semValue = Modifiers::PUBLIC; + }, 371 => static function ($self, $stackPos) { + $self->semValue = Modifiers::PROTECTED; + }, 372 => static function ($self, $stackPos) { + $self->semValue = Modifiers::PRIVATE; + }, 373 => static function ($self, $stackPos) { + $self->semValue = Modifiers::STATIC; + }, 374 => static function ($self, $stackPos) { + $self->semValue = Modifiers::ABSTRACT; + }, 375 => static function ($self, $stackPos) { + $self->semValue = Modifiers::FINAL; + }, 376 => static function ($self, $stackPos) { + $self->semValue = Modifiers::READONLY; + }, 377 => null, 378 => static function ($self, $stackPos) { + $self->semValue = array($self->semStack[$stackPos - (1 - 1)]); + }, 379 => static function ($self, $stackPos) { + $self->semStack[$stackPos - (3 - 1)][] = $self->semStack[$stackPos - (3 - 3)]; + $self->semValue = $self->semStack[$stackPos - (3 - 1)]; + }, 380 => static function ($self, $stackPos) { + $self->semValue = new Node\VarLikeIdentifier(substr($self->semStack[$stackPos - (1 - 1)], 1), $self->getAttributes($self->tokenStartStack[$stackPos - (1 - 1)], $self->tokenEndStack[$stackPos])); + }, 381 => static function ($self, $stackPos) { + $self->semValue = new Node\PropertyItem($self->semStack[$stackPos - (1 - 1)], null, $self->getAttributes($self->tokenStartStack[$stackPos - (1 - 1)], $self->tokenEndStack[$stackPos])); + }, 382 => static function ($self, $stackPos) { + $self->semValue = new Node\PropertyItem($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 383 => null, 384 => null, 385 => static function ($self, $stackPos) { + $self->semStack[$stackPos - (3 - 1)][] = $self->semStack[$stackPos - (3 - 3)]; + $self->semValue = $self->semStack[$stackPos - (3 - 1)]; + }, 386 => static function ($self, $stackPos) { + $self->semValue = array($self->semStack[$stackPos - (1 - 1)]); + }, 387 => static function ($self, $stackPos) { + $self->semValue = array(); + }, 388 => null, 389 => null, 390 => static function ($self, $stackPos) { + $self->semValue = new Expr\Assign($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 391 => static function ($self, $stackPos) { + $self->semValue = new Expr\Assign($self->fixupArrayDestructuring($self->semStack[$stackPos - (3 - 1)]), $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 392 => static function ($self, $stackPos) { + $self->semValue = new Expr\Assign($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 393 => static function ($self, $stackPos) { + $self->semValue = new Expr\AssignRef($self->semStack[$stackPos - (4 - 1)], $self->semStack[$stackPos - (4 - 4)], $self->getAttributes($self->tokenStartStack[$stackPos - (4 - 1)], $self->tokenEndStack[$stackPos])); + }, 394 => static function ($self, $stackPos) { + $self->semValue = new Expr\AssignRef($self->semStack[$stackPos - (4 - 1)], $self->semStack[$stackPos - (4 - 4)], $self->getAttributes($self->tokenStartStack[$stackPos - (4 - 1)], $self->tokenEndStack[$stackPos])); + if (!$self->phpVersion->allowsAssignNewByReference()) { + $self->emitError(new Error('Cannot assign new by reference', $self->getAttributes($self->tokenStartStack[$stackPos - (4 - 1)], $self->tokenEndStack[$stackPos]))); + } + }, 395 => null, 396 => null, 397 => static function ($self, $stackPos) { + $self->semValue = new Expr\Clone_($self->semStack[$stackPos - (2 - 2)], $self->getAttributes($self->tokenStartStack[$stackPos - (2 - 1)], $self->tokenEndStack[$stackPos])); + }, 398 => static function ($self, $stackPos) { + $self->semValue = new Expr\AssignOp\Plus($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 399 => static function ($self, $stackPos) { + $self->semValue = new Expr\AssignOp\Minus($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 400 => static function ($self, $stackPos) { + $self->semValue = new Expr\AssignOp\Mul($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 401 => static function ($self, $stackPos) { + $self->semValue = new Expr\AssignOp\Div($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 402 => static function ($self, $stackPos) { + $self->semValue = new Expr\AssignOp\Concat($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 403 => static function ($self, $stackPos) { + $self->semValue = new Expr\AssignOp\Mod($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 404 => static function ($self, $stackPos) { + $self->semValue = new Expr\AssignOp\BitwiseAnd($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 405 => static function ($self, $stackPos) { + $self->semValue = new Expr\AssignOp\BitwiseOr($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 406 => static function ($self, $stackPos) { + $self->semValue = new Expr\AssignOp\BitwiseXor($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 407 => static function ($self, $stackPos) { + $self->semValue = new Expr\AssignOp\ShiftLeft($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 408 => static function ($self, $stackPos) { + $self->semValue = new Expr\AssignOp\ShiftRight($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 409 => static function ($self, $stackPos) { + $self->semValue = new Expr\AssignOp\Pow($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 410 => static function ($self, $stackPos) { + $self->semValue = new Expr\AssignOp\Coalesce($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 411 => static function ($self, $stackPos) { + $self->semValue = new Expr\PostInc($self->semStack[$stackPos - (2 - 1)], $self->getAttributes($self->tokenStartStack[$stackPos - (2 - 1)], $self->tokenEndStack[$stackPos])); + }, 412 => static function ($self, $stackPos) { + $self->semValue = new Expr\PreInc($self->semStack[$stackPos - (2 - 2)], $self->getAttributes($self->tokenStartStack[$stackPos - (2 - 1)], $self->tokenEndStack[$stackPos])); + }, 413 => static function ($self, $stackPos) { + $self->semValue = new Expr\PostDec($self->semStack[$stackPos - (2 - 1)], $self->getAttributes($self->tokenStartStack[$stackPos - (2 - 1)], $self->tokenEndStack[$stackPos])); + }, 414 => static function ($self, $stackPos) { + $self->semValue = new Expr\PreDec($self->semStack[$stackPos - (2 - 2)], $self->getAttributes($self->tokenStartStack[$stackPos - (2 - 1)], $self->tokenEndStack[$stackPos])); + }, 415 => static function ($self, $stackPos) { + $self->semValue = new Expr\BinaryOp\BooleanOr($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 416 => static function ($self, $stackPos) { + $self->semValue = new Expr\BinaryOp\BooleanAnd($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 417 => static function ($self, $stackPos) { + $self->semValue = new Expr\BinaryOp\LogicalOr($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 418 => static function ($self, $stackPos) { + $self->semValue = new Expr\BinaryOp\LogicalAnd($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 419 => static function ($self, $stackPos) { + $self->semValue = new Expr\BinaryOp\LogicalXor($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 420 => static function ($self, $stackPos) { + $self->semValue = new Expr\BinaryOp\BitwiseOr($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 421 => static function ($self, $stackPos) { + $self->semValue = new Expr\BinaryOp\BitwiseAnd($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 422 => static function ($self, $stackPos) { + $self->semValue = new Expr\BinaryOp\BitwiseAnd($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 423 => static function ($self, $stackPos) { + $self->semValue = new Expr\BinaryOp\BitwiseXor($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 424 => static function ($self, $stackPos) { + $self->semValue = new Expr\BinaryOp\Concat($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 425 => static function ($self, $stackPos) { + $self->semValue = new Expr\BinaryOp\Plus($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 426 => static function ($self, $stackPos) { + $self->semValue = new Expr\BinaryOp\Minus($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 427 => static function ($self, $stackPos) { + $self->semValue = new Expr\BinaryOp\Mul($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 428 => static function ($self, $stackPos) { + $self->semValue = new Expr\BinaryOp\Div($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 429 => static function ($self, $stackPos) { + $self->semValue = new Expr\BinaryOp\Mod($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 430 => static function ($self, $stackPos) { + $self->semValue = new Expr\BinaryOp\ShiftLeft($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 431 => static function ($self, $stackPos) { + $self->semValue = new Expr\BinaryOp\ShiftRight($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 432 => static function ($self, $stackPos) { + $self->semValue = new Expr\BinaryOp\Pow($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 433 => static function ($self, $stackPos) { + $self->semValue = new Expr\UnaryPlus($self->semStack[$stackPos - (2 - 2)], $self->getAttributes($self->tokenStartStack[$stackPos - (2 - 1)], $self->tokenEndStack[$stackPos])); + }, 434 => static function ($self, $stackPos) { + $self->semValue = new Expr\UnaryMinus($self->semStack[$stackPos - (2 - 2)], $self->getAttributes($self->tokenStartStack[$stackPos - (2 - 1)], $self->tokenEndStack[$stackPos])); + }, 435 => static function ($self, $stackPos) { + $self->semValue = new Expr\BooleanNot($self->semStack[$stackPos - (2 - 2)], $self->getAttributes($self->tokenStartStack[$stackPos - (2 - 1)], $self->tokenEndStack[$stackPos])); + }, 436 => static function ($self, $stackPos) { + $self->semValue = new Expr\BitwiseNot($self->semStack[$stackPos - (2 - 2)], $self->getAttributes($self->tokenStartStack[$stackPos - (2 - 1)], $self->tokenEndStack[$stackPos])); + }, 437 => static function ($self, $stackPos) { + $self->semValue = new Expr\BinaryOp\Identical($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 438 => static function ($self, $stackPos) { + $self->semValue = new Expr\BinaryOp\NotIdentical($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 439 => static function ($self, $stackPos) { + $self->semValue = new Expr\BinaryOp\Equal($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 440 => static function ($self, $stackPos) { + $self->semValue = new Expr\BinaryOp\NotEqual($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 441 => static function ($self, $stackPos) { + $self->semValue = new Expr\BinaryOp\Spaceship($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 442 => static function ($self, $stackPos) { + $self->semValue = new Expr\BinaryOp\Smaller($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 443 => static function ($self, $stackPos) { + $self->semValue = new Expr\BinaryOp\SmallerOrEqual($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 444 => static function ($self, $stackPos) { + $self->semValue = new Expr\BinaryOp\Greater($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 445 => static function ($self, $stackPos) { + $self->semValue = new Expr\BinaryOp\GreaterOrEqual($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 446 => static function ($self, $stackPos) { + $self->semValue = new Expr\Instanceof_($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 447 => static function ($self, $stackPos) { + $self->semValue = $self->semStack[$stackPos - (3 - 2)]; + }, 448 => static function ($self, $stackPos) { + $self->semValue = new Expr\Ternary($self->semStack[$stackPos - (5 - 1)], $self->semStack[$stackPos - (5 - 3)], $self->semStack[$stackPos - (5 - 5)], $self->getAttributes($self->tokenStartStack[$stackPos - (5 - 1)], $self->tokenEndStack[$stackPos])); + }, 449 => static function ($self, $stackPos) { + $self->semValue = new Expr\Ternary($self->semStack[$stackPos - (4 - 1)], null, $self->semStack[$stackPos - (4 - 4)], $self->getAttributes($self->tokenStartStack[$stackPos - (4 - 1)], $self->tokenEndStack[$stackPos])); + }, 450 => static function ($self, $stackPos) { + $self->semValue = new Expr\BinaryOp\Coalesce($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 451 => static function ($self, $stackPos) { + $self->semValue = new Expr\Isset_($self->semStack[$stackPos - (4 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (4 - 1)], $self->tokenEndStack[$stackPos])); + }, 452 => static function ($self, $stackPos) { + $self->semValue = new Expr\Empty_($self->semStack[$stackPos - (4 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (4 - 1)], $self->tokenEndStack[$stackPos])); + }, 453 => static function ($self, $stackPos) { + $self->semValue = new Expr\Include_($self->semStack[$stackPos - (2 - 2)], Expr\Include_::TYPE_INCLUDE, $self->getAttributes($self->tokenStartStack[$stackPos - (2 - 1)], $self->tokenEndStack[$stackPos])); + }, 454 => static function ($self, $stackPos) { + $self->semValue = new Expr\Include_($self->semStack[$stackPos - (2 - 2)], Expr\Include_::TYPE_INCLUDE_ONCE, $self->getAttributes($self->tokenStartStack[$stackPos - (2 - 1)], $self->tokenEndStack[$stackPos])); + }, 455 => static function ($self, $stackPos) { + $self->semValue = new Expr\Eval_($self->semStack[$stackPos - (4 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (4 - 1)], $self->tokenEndStack[$stackPos])); + }, 456 => static function ($self, $stackPos) { + $self->semValue = new Expr\Include_($self->semStack[$stackPos - (2 - 2)], Expr\Include_::TYPE_REQUIRE, $self->getAttributes($self->tokenStartStack[$stackPos - (2 - 1)], $self->tokenEndStack[$stackPos])); + }, 457 => static function ($self, $stackPos) { + $self->semValue = new Expr\Include_($self->semStack[$stackPos - (2 - 2)], Expr\Include_::TYPE_REQUIRE_ONCE, $self->getAttributes($self->tokenStartStack[$stackPos - (2 - 1)], $self->tokenEndStack[$stackPos])); + }, 458 => static function ($self, $stackPos) { + $self->semValue = new Expr\Cast\Int_($self->semStack[$stackPos - (2 - 2)], $self->getAttributes($self->tokenStartStack[$stackPos - (2 - 1)], $self->tokenEndStack[$stackPos])); + }, 459 => static function ($self, $stackPos) { + $attrs = $self->getAttributes($self->tokenStartStack[$stackPos - (2 - 1)], $self->tokenEndStack[$stackPos]); + $attrs['kind'] = $self->getFloatCastKind($self->semStack[$stackPos - (2 - 1)]); + $self->semValue = new Expr\Cast\Double($self->semStack[$stackPos - (2 - 2)], $attrs); + }, 460 => static function ($self, $stackPos) { + $self->semValue = new Expr\Cast\String_($self->semStack[$stackPos - (2 - 2)], $self->getAttributes($self->tokenStartStack[$stackPos - (2 - 1)], $self->tokenEndStack[$stackPos])); + }, 461 => static function ($self, $stackPos) { + $self->semValue = new Expr\Cast\Array_($self->semStack[$stackPos - (2 - 2)], $self->getAttributes($self->tokenStartStack[$stackPos - (2 - 1)], $self->tokenEndStack[$stackPos])); + }, 462 => static function ($self, $stackPos) { + $self->semValue = new Expr\Cast\Object_($self->semStack[$stackPos - (2 - 2)], $self->getAttributes($self->tokenStartStack[$stackPos - (2 - 1)], $self->tokenEndStack[$stackPos])); + }, 463 => static function ($self, $stackPos) { + $self->semValue = new Expr\Cast\Bool_($self->semStack[$stackPos - (2 - 2)], $self->getAttributes($self->tokenStartStack[$stackPos - (2 - 1)], $self->tokenEndStack[$stackPos])); + }, 464 => static function ($self, $stackPos) { + $self->semValue = new Expr\Cast\Unset_($self->semStack[$stackPos - (2 - 2)], $self->getAttributes($self->tokenStartStack[$stackPos - (2 - 1)], $self->tokenEndStack[$stackPos])); + }, 465 => static function ($self, $stackPos) { + $attrs = $self->getAttributes($self->tokenStartStack[$stackPos - (2 - 1)], $self->tokenEndStack[$stackPos]); + $attrs['kind'] = (strtolower($self->semStack[$stackPos - (2 - 1)]) === 'exit') ? Expr\Exit_::KIND_EXIT : Expr\Exit_::KIND_DIE; + $self->semValue = new Expr\Exit_($self->semStack[$stackPos - (2 - 2)], $attrs); + }, 466 => static function ($self, $stackPos) { + $self->semValue = new Expr\ErrorSuppress($self->semStack[$stackPos - (2 - 2)], $self->getAttributes($self->tokenStartStack[$stackPos - (2 - 1)], $self->tokenEndStack[$stackPos])); + }, 467 => null, 468 => static function ($self, $stackPos) { + $self->semValue = new Expr\ShellExec($self->semStack[$stackPos - (3 - 2)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 469 => static function ($self, $stackPos) { + $self->semValue = new Expr\Print_($self->semStack[$stackPos - (2 - 2)], $self->getAttributes($self->tokenStartStack[$stackPos - (2 - 1)], $self->tokenEndStack[$stackPos])); + }, 470 => static function ($self, $stackPos) { + $self->semValue = new Expr\Yield_(null, null, $self->getAttributes($self->tokenStartStack[$stackPos - (1 - 1)], $self->tokenEndStack[$stackPos])); + }, 471 => static function ($self, $stackPos) { + $self->semValue = new Expr\Yield_($self->semStack[$stackPos - (2 - 2)], null, $self->getAttributes($self->tokenStartStack[$stackPos - (2 - 1)], $self->tokenEndStack[$stackPos])); + }, 472 => static function ($self, $stackPos) { + $self->semValue = new Expr\Yield_($self->semStack[$stackPos - (4 - 4)], $self->semStack[$stackPos - (4 - 2)], $self->getAttributes($self->tokenStartStack[$stackPos - (4 - 1)], $self->tokenEndStack[$stackPos])); + }, 473 => static function ($self, $stackPos) { + $self->semValue = new Expr\YieldFrom($self->semStack[$stackPos - (2 - 2)], $self->getAttributes($self->tokenStartStack[$stackPos - (2 - 1)], $self->tokenEndStack[$stackPos])); + }, 474 => static function ($self, $stackPos) { + $self->semValue = new Expr\Throw_($self->semStack[$stackPos - (2 - 2)], $self->getAttributes($self->tokenStartStack[$stackPos - (2 - 1)], $self->tokenEndStack[$stackPos])); + }, 475 => static function ($self, $stackPos) { + $self->semValue = new Expr\ArrowFunction(['static' => \false, 'byRef' => $self->semStack[$stackPos - (8 - 2)], 'params' => $self->semStack[$stackPos - (8 - 4)], 'returnType' => $self->semStack[$stackPos - (8 - 6)], 'expr' => $self->semStack[$stackPos - (8 - 8)], 'attrGroups' => []], $self->getAttributes($self->tokenStartStack[$stackPos - (8 - 1)], $self->tokenEndStack[$stackPos])); + }, 476 => static function ($self, $stackPos) { + $self->semValue = new Expr\ArrowFunction(['static' => \true, 'byRef' => $self->semStack[$stackPos - (9 - 3)], 'params' => $self->semStack[$stackPos - (9 - 5)], 'returnType' => $self->semStack[$stackPos - (9 - 7)], 'expr' => $self->semStack[$stackPos - (9 - 9)], 'attrGroups' => []], $self->getAttributes($self->tokenStartStack[$stackPos - (9 - 1)], $self->tokenEndStack[$stackPos])); + }, 477 => static function ($self, $stackPos) { + $self->semValue = new Expr\Closure(['static' => \false, 'byRef' => $self->semStack[$stackPos - (8 - 2)], 'params' => $self->semStack[$stackPos - (8 - 4)], 'uses' => $self->semStack[$stackPos - (8 - 6)], 'returnType' => $self->semStack[$stackPos - (8 - 7)], 'stmts' => $self->semStack[$stackPos - (8 - 8)], 'attrGroups' => []], $self->getAttributes($self->tokenStartStack[$stackPos - (8 - 1)], $self->tokenEndStack[$stackPos])); + }, 478 => static function ($self, $stackPos) { + $self->semValue = new Expr\Closure(['static' => \true, 'byRef' => $self->semStack[$stackPos - (9 - 3)], 'params' => $self->semStack[$stackPos - (9 - 5)], 'uses' => $self->semStack[$stackPos - (9 - 7)], 'returnType' => $self->semStack[$stackPos - (9 - 8)], 'stmts' => $self->semStack[$stackPos - (9 - 9)], 'attrGroups' => []], $self->getAttributes($self->tokenStartStack[$stackPos - (9 - 1)], $self->tokenEndStack[$stackPos])); + }, 479 => static function ($self, $stackPos) { + $self->semValue = new Expr\ArrowFunction(['static' => \false, 'byRef' => $self->semStack[$stackPos - (9 - 3)], 'params' => $self->semStack[$stackPos - (9 - 5)], 'returnType' => $self->semStack[$stackPos - (9 - 7)], 'expr' => $self->semStack[$stackPos - (9 - 9)], 'attrGroups' => $self->semStack[$stackPos - (9 - 1)]], $self->getAttributes($self->tokenStartStack[$stackPos - (9 - 1)], $self->tokenEndStack[$stackPos])); + }, 480 => static function ($self, $stackPos) { + $self->semValue = new Expr\ArrowFunction(['static' => \true, 'byRef' => $self->semStack[$stackPos - (10 - 4)], 'params' => $self->semStack[$stackPos - (10 - 6)], 'returnType' => $self->semStack[$stackPos - (10 - 8)], 'expr' => $self->semStack[$stackPos - (10 - 10)], 'attrGroups' => $self->semStack[$stackPos - (10 - 1)]], $self->getAttributes($self->tokenStartStack[$stackPos - (10 - 1)], $self->tokenEndStack[$stackPos])); + }, 481 => static function ($self, $stackPos) { + $self->semValue = new Expr\Closure(['static' => \false, 'byRef' => $self->semStack[$stackPos - (9 - 3)], 'params' => $self->semStack[$stackPos - (9 - 5)], 'uses' => $self->semStack[$stackPos - (9 - 7)], 'returnType' => $self->semStack[$stackPos - (9 - 8)], 'stmts' => $self->semStack[$stackPos - (9 - 9)], 'attrGroups' => $self->semStack[$stackPos - (9 - 1)]], $self->getAttributes($self->tokenStartStack[$stackPos - (9 - 1)], $self->tokenEndStack[$stackPos])); + }, 482 => static function ($self, $stackPos) { + $self->semValue = new Expr\Closure(['static' => \true, 'byRef' => $self->semStack[$stackPos - (10 - 4)], 'params' => $self->semStack[$stackPos - (10 - 6)], 'uses' => $self->semStack[$stackPos - (10 - 8)], 'returnType' => $self->semStack[$stackPos - (10 - 9)], 'stmts' => $self->semStack[$stackPos - (10 - 10)], 'attrGroups' => $self->semStack[$stackPos - (10 - 1)]], $self->getAttributes($self->tokenStartStack[$stackPos - (10 - 1)], $self->tokenEndStack[$stackPos])); + }, 483 => static function ($self, $stackPos) { + $self->semValue = array(new Stmt\Class_(null, ['type' => $self->semStack[$stackPos - (8 - 2)], 'extends' => $self->semStack[$stackPos - (8 - 4)], 'implements' => $self->semStack[$stackPos - (8 - 5)], 'stmts' => $self->semStack[$stackPos - (8 - 7)], 'attrGroups' => $self->semStack[$stackPos - (8 - 1)]], $self->getAttributes($self->tokenStartStack[$stackPos - (8 - 1)], $self->tokenEndStack[$stackPos])), $self->semStack[$stackPos - (8 - 3)]); + $self->checkClass($self->semValue[0], -1); + }, 484 => static function ($self, $stackPos) { + $self->semValue = new Expr\New_($self->semStack[$stackPos - (3 - 2)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 485 => static function ($self, $stackPos) { + list($class, $ctorArgs) = $self->semStack[$stackPos - (2 - 2)]; + $self->semValue = new Expr\New_($class, $ctorArgs, $self->getAttributes($self->tokenStartStack[$stackPos - (2 - 1)], $self->tokenEndStack[$stackPos])); + }, 486 => static function ($self, $stackPos) { + $self->semValue = new Expr\New_($self->semStack[$stackPos - (2 - 2)], [], $self->getAttributes($self->tokenStartStack[$stackPos - (2 - 1)], $self->tokenEndStack[$stackPos])); + }, 487 => null, 488 => null, 489 => static function ($self, $stackPos) { + $self->semValue = array(); + }, 490 => static function ($self, $stackPos) { + $self->semValue = $self->semStack[$stackPos - (4 - 3)]; + }, 491 => null, 492 => static function ($self, $stackPos) { + $self->semValue = array($self->semStack[$stackPos - (1 - 1)]); + }, 493 => static function ($self, $stackPos) { + $self->semStack[$stackPos - (3 - 1)][] = $self->semStack[$stackPos - (3 - 3)]; + $self->semValue = $self->semStack[$stackPos - (3 - 1)]; + }, 494 => static function ($self, $stackPos) { + $self->semValue = new Node\ClosureUse($self->semStack[$stackPos - (2 - 2)], $self->semStack[$stackPos - (2 - 1)], $self->getAttributes($self->tokenStartStack[$stackPos - (2 - 1)], $self->tokenEndStack[$stackPos])); + }, 495 => static function ($self, $stackPos) { + $self->semValue = new Name($self->semStack[$stackPos - (1 - 1)], $self->getAttributes($self->tokenStartStack[$stackPos - (1 - 1)], $self->tokenEndStack[$stackPos])); + }, 496 => static function ($self, $stackPos) { + $self->semValue = new Expr\FuncCall($self->semStack[$stackPos - (2 - 1)], $self->semStack[$stackPos - (2 - 2)], $self->getAttributes($self->tokenStartStack[$stackPos - (2 - 1)], $self->tokenEndStack[$stackPos])); + }, 497 => static function ($self, $stackPos) { + $self->semValue = new Expr\FuncCall($self->semStack[$stackPos - (2 - 1)], $self->semStack[$stackPos - (2 - 2)], $self->getAttributes($self->tokenStartStack[$stackPos - (2 - 1)], $self->tokenEndStack[$stackPos])); + }, 498 => static function ($self, $stackPos) { + $self->semValue = new Expr\FuncCall($self->semStack[$stackPos - (2 - 1)], $self->semStack[$stackPos - (2 - 2)], $self->getAttributes($self->tokenStartStack[$stackPos - (2 - 1)], $self->tokenEndStack[$stackPos])); + }, 499 => static function ($self, $stackPos) { + $self->semValue = new Expr\StaticCall($self->semStack[$stackPos - (4 - 1)], $self->semStack[$stackPos - (4 - 3)], $self->semStack[$stackPos - (4 - 4)], $self->getAttributes($self->tokenStartStack[$stackPos - (4 - 1)], $self->tokenEndStack[$stackPos])); + }, 500 => static function ($self, $stackPos) { + $self->semValue = new Name($self->semStack[$stackPos - (1 - 1)], $self->getAttributes($self->tokenStartStack[$stackPos - (1 - 1)], $self->tokenEndStack[$stackPos])); + }, 501 => null, 502 => static function ($self, $stackPos) { + $self->semValue = new Name($self->semStack[$stackPos - (1 - 1)], $self->getAttributes($self->tokenStartStack[$stackPos - (1 - 1)], $self->tokenEndStack[$stackPos])); + }, 503 => static function ($self, $stackPos) { + $self->semValue = new Name($self->semStack[$stackPos - (1 - 1)], $self->getAttributes($self->tokenStartStack[$stackPos - (1 - 1)], $self->tokenEndStack[$stackPos])); + }, 504 => static function ($self, $stackPos) { + $self->semValue = new Name\FullyQualified(substr($self->semStack[$stackPos - (1 - 1)], 1), $self->getAttributes($self->tokenStartStack[$stackPos - (1 - 1)], $self->tokenEndStack[$stackPos])); + }, 505 => static function ($self, $stackPos) { + $self->semValue = new Name\Relative(substr($self->semStack[$stackPos - (1 - 1)], 10), $self->getAttributes($self->tokenStartStack[$stackPos - (1 - 1)], $self->tokenEndStack[$stackPos])); + }, 506 => null, 507 => null, 508 => static function ($self, $stackPos) { + $self->semValue = $self->semStack[$stackPos - (3 - 2)]; + }, 509 => static function ($self, $stackPos) { + $self->semValue = new Expr\Error($self->getAttributes($self->tokenStartStack[$stackPos - (1 - 1)], $self->tokenEndStack[$stackPos])); + $self->errorState = 2; + }, 510 => null, 511 => null, 512 => static function ($self, $stackPos) { + $self->semValue = null; + }, 513 => static function ($self, $stackPos) { + $self->semValue = $self->semStack[$stackPos - (3 - 2)]; + }, 514 => static function ($self, $stackPos) { + $self->semValue = array(); + }, 515 => static function ($self, $stackPos) { + $self->semValue = array($self->semStack[$stackPos - (1 - 1)]); + foreach ($self->semValue as $s) { + if ($s instanceof Node\InterpolatedStringPart) { + $s->value = Node\Scalar\String_::parseEscapeSequences($s->value, '`', $self->phpVersion->supportsUnicodeEscapes()); + } + } + }, 516 => static function ($self, $stackPos) { + foreach ($self->semStack[$stackPos - (1 - 1)] as $s) { + if ($s instanceof Node\InterpolatedStringPart) { + $s->value = Node\Scalar\String_::parseEscapeSequences($s->value, '`', $self->phpVersion->supportsUnicodeEscapes()); + } + } + $self->semValue = $self->semStack[$stackPos - (1 - 1)]; + }, 517 => static function ($self, $stackPos) { + $self->semValue = array(); + }, 518 => null, 519 => static function ($self, $stackPos) { + $self->semValue = new Expr\ConstFetch($self->semStack[$stackPos - (1 - 1)], $self->getAttributes($self->tokenStartStack[$stackPos - (1 - 1)], $self->tokenEndStack[$stackPos])); + }, 520 => static function ($self, $stackPos) { + $self->semValue = new Scalar\MagicConst\Line($self->getAttributes($self->tokenStartStack[$stackPos - (1 - 1)], $self->tokenEndStack[$stackPos])); + }, 521 => static function ($self, $stackPos) { + $self->semValue = new Scalar\MagicConst\File($self->getAttributes($self->tokenStartStack[$stackPos - (1 - 1)], $self->tokenEndStack[$stackPos])); + }, 522 => static function ($self, $stackPos) { + $self->semValue = new Scalar\MagicConst\Dir($self->getAttributes($self->tokenStartStack[$stackPos - (1 - 1)], $self->tokenEndStack[$stackPos])); + }, 523 => static function ($self, $stackPos) { + $self->semValue = new Scalar\MagicConst\Class_($self->getAttributes($self->tokenStartStack[$stackPos - (1 - 1)], $self->tokenEndStack[$stackPos])); + }, 524 => static function ($self, $stackPos) { + $self->semValue = new Scalar\MagicConst\Trait_($self->getAttributes($self->tokenStartStack[$stackPos - (1 - 1)], $self->tokenEndStack[$stackPos])); + }, 525 => static function ($self, $stackPos) { + $self->semValue = new Scalar\MagicConst\Method($self->getAttributes($self->tokenStartStack[$stackPos - (1 - 1)], $self->tokenEndStack[$stackPos])); + }, 526 => static function ($self, $stackPos) { + $self->semValue = new Scalar\MagicConst\Function_($self->getAttributes($self->tokenStartStack[$stackPos - (1 - 1)], $self->tokenEndStack[$stackPos])); + }, 527 => static function ($self, $stackPos) { + $self->semValue = new Scalar\MagicConst\Namespace_($self->getAttributes($self->tokenStartStack[$stackPos - (1 - 1)], $self->tokenEndStack[$stackPos])); + }, 528 => static function ($self, $stackPos) { + $self->semValue = new Expr\ClassConstFetch($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 529 => static function ($self, $stackPos) { + $self->semValue = new Expr\ClassConstFetch($self->semStack[$stackPos - (5 - 1)], $self->semStack[$stackPos - (5 - 4)], $self->getAttributes($self->tokenStartStack[$stackPos - (5 - 1)], $self->tokenEndStack[$stackPos])); + }, 530 => static function ($self, $stackPos) { + $self->semValue = new Expr\ClassConstFetch($self->semStack[$stackPos - (3 - 1)], new Expr\Error($self->getAttributes($self->tokenStartStack[$stackPos - (3 - 3)], $self->tokenEndStack[$stackPos - (3 - 3)])), $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + $self->errorState = 2; + }, 531 => static function ($self, $stackPos) { + $attrs = $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos]); $attrs['kind'] = Expr\Array_::KIND_SHORT; - $this->semValue = new Expr\Array_($this->semStack[$stackPos - (3 - 2)], $attrs); - }, 393 => function ($stackPos) { - $this->semValue = new Expr\ArrayDimFetch($this->semStack[$stackPos - (4 - 1)], $this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 394 => function ($stackPos) { - $this->semValue = new Expr\ArrayDimFetch(Scalar\String_::fromString($this->semStack[$stackPos - (4 - 1)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes), $this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 395 => function ($stackPos) { - $this->semValue = new Expr\ArrayDimFetch($this->semStack[$stackPos - (4 - 1)], $this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 396 => function ($stackPos) { - $this->semValue = new Expr\ArrayDimFetch($this->semStack[$stackPos - (4 - 1)], $this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 397 => function ($stackPos) { - $this->semValue = array(new Stmt\Class_(null, ['type' => 0, 'extends' => $this->semStack[$stackPos - (7 - 3)], 'implements' => $this->semStack[$stackPos - (7 - 4)], 'stmts' => $this->semStack[$stackPos - (7 - 6)]], $this->startAttributeStack[$stackPos - (7 - 1)] + $this->endAttributes), $this->semStack[$stackPos - (7 - 2)]); - $this->checkClass($this->semValue[0], -1); - }, 398 => function ($stackPos) { - $this->semValue = new Expr\New_($this->semStack[$stackPos - (3 - 2)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 399 => function ($stackPos) { - list($class, $ctorArgs) = $this->semStack[$stackPos - (2 - 2)]; - $this->semValue = new Expr\New_($class, $ctorArgs, $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 400 => function ($stackPos) { - $this->semValue = array(); - }, 401 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (4 - 3)]; - }, 402 => function ($stackPos) { - $this->semValue = array($this->semStack[$stackPos - (1 - 1)]); - }, 403 => function ($stackPos) { - $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; - $this->semValue = $this->semStack[$stackPos - (3 - 1)]; - }, 404 => function ($stackPos) { - $this->semValue = new Expr\ClosureUse($this->semStack[$stackPos - (2 - 2)], $this->semStack[$stackPos - (2 - 1)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 405 => function ($stackPos) { - $this->semValue = new Name($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 406 => function ($stackPos) { - $this->semValue = new Expr\FuncCall($this->semStack[$stackPos - (2 - 1)], $this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 407 => function ($stackPos) { - $this->semValue = new Expr\FuncCall($this->semStack[$stackPos - (2 - 1)], $this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 408 => function ($stackPos) { - $this->semValue = new Expr\StaticCall($this->semStack[$stackPos - (4 - 1)], $this->semStack[$stackPos - (4 - 3)], $this->semStack[$stackPos - (4 - 4)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 409 => function ($stackPos) { - $this->semValue = new Expr\StaticCall($this->semStack[$stackPos - (6 - 1)], $this->semStack[$stackPos - (6 - 4)], $this->semStack[$stackPos - (6 - 6)], $this->startAttributeStack[$stackPos - (6 - 1)] + $this->endAttributes); - }, 410 => function ($stackPos) { - $this->semValue = $this->fixupPhp5StaticPropCall($this->semStack[$stackPos - (2 - 1)], $this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 411 => function ($stackPos) { - $this->semValue = new Expr\FuncCall($this->semStack[$stackPos - (2 - 1)], $this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 412 => function ($stackPos) { - $this->semValue = new Expr\ArrayDimFetch($this->semStack[$stackPos - (4 - 1)], $this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 413 => function ($stackPos) { - $this->semValue = new Name($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 414 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 415 => function ($stackPos) { - $this->semValue = new Name($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 416 => function ($stackPos) { - $this->semValue = new Name($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 417 => function ($stackPos) { - $this->semValue = new Name\FullyQualified(\substr($this->semStack[$stackPos - (1 - 1)], 1), $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 418 => function ($stackPos) { - $this->semValue = new Name\Relative(\substr($this->semStack[$stackPos - (1 - 1)], 10), $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 419 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 420 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 421 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 422 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 423 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 424 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 425 => function ($stackPos) { - $this->semValue = new Expr\PropertyFetch($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 426 => function ($stackPos) { - $this->semValue = new Expr\PropertyFetch($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 427 => function ($stackPos) { - $this->semValue = new Expr\ArrayDimFetch($this->semStack[$stackPos - (4 - 1)], $this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 428 => function ($stackPos) { - $this->semValue = new Expr\ArrayDimFetch($this->semStack[$stackPos - (4 - 1)], $this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 429 => function ($stackPos) { - $this->semValue = null; - }, 430 => function ($stackPos) { - $this->semValue = null; - }, 431 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 432 => function ($stackPos) { - $this->semValue = array(); - }, 433 => function ($stackPos) { - $this->semValue = array(new Scalar\EncapsedStringPart(Scalar\String_::parseEscapeSequences($this->semStack[$stackPos - (1 - 1)], '`', \false), $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes)); - }, 434 => function ($stackPos) { - foreach ($this->semStack[$stackPos - (1 - 1)] as $s) { - if ($s instanceof Node\Scalar\EncapsedStringPart) { - $s->value = Node\Scalar\String_::parseEscapeSequences($s->value, '`', \false); - } - } - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 435 => function ($stackPos) { - $this->semValue = array(); - }, 436 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 437 => function ($stackPos) { - $this->semValue = $this->parseLNumber($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes, \true); - }, 438 => function ($stackPos) { - $this->semValue = Scalar\DNumber::fromString($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 439 => function ($stackPos) { - $this->semValue = Scalar\String_::fromString($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes, \false); - }, 440 => function ($stackPos) { - $this->semValue = new Scalar\MagicConst\Line($this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 441 => function ($stackPos) { - $this->semValue = new Scalar\MagicConst\File($this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 442 => function ($stackPos) { - $this->semValue = new Scalar\MagicConst\Dir($this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 443 => function ($stackPos) { - $this->semValue = new Scalar\MagicConst\Class_($this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 444 => function ($stackPos) { - $this->semValue = new Scalar\MagicConst\Trait_($this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 445 => function ($stackPos) { - $this->semValue = new Scalar\MagicConst\Method($this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 446 => function ($stackPos) { - $this->semValue = new Scalar\MagicConst\Function_($this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 447 => function ($stackPos) { - $this->semValue = new Scalar\MagicConst\Namespace_($this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 448 => function ($stackPos) { - $this->semValue = $this->parseDocString($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 2)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes, $this->startAttributeStack[$stackPos - (3 - 3)] + $this->endAttributeStack[$stackPos - (3 - 3)], \false); - }, 449 => function ($stackPos) { - $this->semValue = $this->parseDocString($this->semStack[$stackPos - (2 - 1)], '', $this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes, $this->startAttributeStack[$stackPos - (2 - 2)] + $this->endAttributeStack[$stackPos - (2 - 2)], \false); - }, 450 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 451 => function ($stackPos) { - $this->semValue = new Expr\ClassConstFetch($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 452 => function ($stackPos) { - $this->semValue = new Expr\ConstFetch($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 453 => function ($stackPos) { - $this->semValue = new Expr\Array_($this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 454 => function ($stackPos) { - $this->semValue = new Expr\Array_($this->semStack[$stackPos - (3 - 2)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 455 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 456 => function ($stackPos) { - $this->semValue = new Expr\BinaryOp\BooleanOr($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 457 => function ($stackPos) { - $this->semValue = new Expr\BinaryOp\BooleanAnd($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 458 => function ($stackPos) { - $this->semValue = new Expr\BinaryOp\LogicalOr($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 459 => function ($stackPos) { - $this->semValue = new Expr\BinaryOp\LogicalAnd($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 460 => function ($stackPos) { - $this->semValue = new Expr\BinaryOp\LogicalXor($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 461 => function ($stackPos) { - $this->semValue = new Expr\BinaryOp\BitwiseOr($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 462 => function ($stackPos) { - $this->semValue = new Expr\BinaryOp\BitwiseAnd($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 463 => function ($stackPos) { - $this->semValue = new Expr\BinaryOp\BitwiseAnd($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 464 => function ($stackPos) { - $this->semValue = new Expr\BinaryOp\BitwiseXor($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 465 => function ($stackPos) { - $this->semValue = new Expr\BinaryOp\Concat($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 466 => function ($stackPos) { - $this->semValue = new Expr\BinaryOp\Plus($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 467 => function ($stackPos) { - $this->semValue = new Expr\BinaryOp\Minus($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 468 => function ($stackPos) { - $this->semValue = new Expr\BinaryOp\Mul($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 469 => function ($stackPos) { - $this->semValue = new Expr\BinaryOp\Div($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 470 => function ($stackPos) { - $this->semValue = new Expr\BinaryOp\Mod($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 471 => function ($stackPos) { - $this->semValue = new Expr\BinaryOp\ShiftLeft($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 472 => function ($stackPos) { - $this->semValue = new Expr\BinaryOp\ShiftRight($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 473 => function ($stackPos) { - $this->semValue = new Expr\BinaryOp\Pow($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 474 => function ($stackPos) { - $this->semValue = new Expr\UnaryPlus($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 475 => function ($stackPos) { - $this->semValue = new Expr\UnaryMinus($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 476 => function ($stackPos) { - $this->semValue = new Expr\BooleanNot($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 477 => function ($stackPos) { - $this->semValue = new Expr\BitwiseNot($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 478 => function ($stackPos) { - $this->semValue = new Expr\BinaryOp\Identical($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 479 => function ($stackPos) { - $this->semValue = new Expr\BinaryOp\NotIdentical($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 480 => function ($stackPos) { - $this->semValue = new Expr\BinaryOp\Equal($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 481 => function ($stackPos) { - $this->semValue = new Expr\BinaryOp\NotEqual($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 482 => function ($stackPos) { - $this->semValue = new Expr\BinaryOp\Smaller($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 483 => function ($stackPos) { - $this->semValue = new Expr\BinaryOp\SmallerOrEqual($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 484 => function ($stackPos) { - $this->semValue = new Expr\BinaryOp\Greater($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 485 => function ($stackPos) { - $this->semValue = new Expr\BinaryOp\GreaterOrEqual($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 486 => function ($stackPos) { - $this->semValue = new Expr\Ternary($this->semStack[$stackPos - (5 - 1)], $this->semStack[$stackPos - (5 - 3)], $this->semStack[$stackPos - (5 - 5)], $this->startAttributeStack[$stackPos - (5 - 1)] + $this->endAttributes); - }, 487 => function ($stackPos) { - $this->semValue = new Expr\Ternary($this->semStack[$stackPos - (4 - 1)], null, $this->semStack[$stackPos - (4 - 4)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 488 => function ($stackPos) { - $this->semValue = new Expr\ArrayDimFetch($this->semStack[$stackPos - (4 - 1)], $this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 489 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (3 - 2)]; - }, 490 => function ($stackPos) { - $this->semValue = new Expr\ConstFetch($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 491 => function ($stackPos) { - $this->semValue = new Expr\ClassConstFetch($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 492 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 493 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 494 => function ($stackPos) { - $attrs = $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes; + $self->semValue = new Expr\Array_($self->semStack[$stackPos - (3 - 2)], $attrs); + }, 532 => static function ($self, $stackPos) { + $attrs = $self->getAttributes($self->tokenStartStack[$stackPos - (4 - 1)], $self->tokenEndStack[$stackPos]); + $attrs['kind'] = Expr\Array_::KIND_LONG; + $self->semValue = new Expr\Array_($self->semStack[$stackPos - (4 - 3)], $attrs); + $self->createdArrays->attach($self->semValue); + }, 533 => static function ($self, $stackPos) { + $self->semValue = $self->semStack[$stackPos - (1 - 1)]; + $self->createdArrays->attach($self->semValue); + }, 534 => static function ($self, $stackPos) { + $self->semValue = Scalar\String_::fromString($self->semStack[$stackPos - (1 - 1)], $self->getAttributes($self->tokenStartStack[$stackPos - (1 - 1)], $self->tokenEndStack[$stackPos]), $self->phpVersion->supportsUnicodeEscapes()); + }, 535 => static function ($self, $stackPos) { + $attrs = $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos]); $attrs['kind'] = Scalar\String_::KIND_DOUBLE_QUOTED; - foreach ($this->semStack[$stackPos - (3 - 2)] as $s) { - if ($s instanceof Node\Scalar\EncapsedStringPart) { - $s->value = Node\Scalar\String_::parseEscapeSequences($s->value, '"', \true); - } - } - $this->semValue = new Scalar\Encapsed($this->semStack[$stackPos - (3 - 2)], $attrs); - }, 495 => function ($stackPos) { - $this->semValue = $this->parseDocString($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 2)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes, $this->startAttributeStack[$stackPos - (3 - 3)] + $this->endAttributeStack[$stackPos - (3 - 3)], \true); - }, 496 => function ($stackPos) { - $this->semValue = array(); - }, 497 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (2 - 1)]; - }, 498 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 499 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 500 => function ($stackPos) { - $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; - $this->semValue = $this->semStack[$stackPos - (3 - 1)]; - }, 501 => function ($stackPos) { - $this->semValue = array($this->semStack[$stackPos - (1 - 1)]); - }, 502 => function ($stackPos) { - $this->semValue = new Expr\ArrayItem($this->semStack[$stackPos - (3 - 3)], $this->semStack[$stackPos - (3 - 1)], \false, $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 503 => function ($stackPos) { - $this->semValue = new Expr\ArrayItem($this->semStack[$stackPos - (1 - 1)], null, \false, $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 504 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 505 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 506 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 507 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 508 => function ($stackPos) { - $this->semValue = new Expr\ArrayDimFetch($this->semStack[$stackPos - (6 - 2)], $this->semStack[$stackPos - (6 - 5)], $this->startAttributeStack[$stackPos - (6 - 1)] + $this->endAttributes); - }, 509 => function ($stackPos) { - $this->semValue = new Expr\ArrayDimFetch($this->semStack[$stackPos - (4 - 1)], $this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 510 => function ($stackPos) { - $this->semValue = new Expr\PropertyFetch($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 511 => function ($stackPos) { - $this->semValue = new Expr\MethodCall($this->semStack[$stackPos - (4 - 1)], $this->semStack[$stackPos - (4 - 3)], $this->semStack[$stackPos - (4 - 4)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 512 => function ($stackPos) { - $this->semValue = new Expr\FuncCall($this->semStack[$stackPos - (2 - 1)], $this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 513 => function ($stackPos) { - $this->semValue = new Expr\ArrayDimFetch($this->semStack[$stackPos - (4 - 1)], $this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 514 => function ($stackPos) { - $this->semValue = new Expr\ArrayDimFetch($this->semStack[$stackPos - (4 - 1)], $this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 515 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 516 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (3 - 2)]; - }, 517 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 518 => function ($stackPos) { - $this->semValue = new Expr\Variable($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 519 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 520 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 521 => function ($stackPos) { - $this->semValue = new Expr\StaticPropertyFetch($this->semStack[$stackPos - (4 - 1)], $this->semStack[$stackPos - (4 - 4)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 522 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 523 => function ($stackPos) { - $var = \substr($this->semStack[$stackPos - (1 - 1)], 1); - $this->semValue = \is_string($var) ? new Node\VarLikeIdentifier($var, $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes) : $var; - }, 524 => function ($stackPos) { - $this->semValue = new Expr\StaticPropertyFetch($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 525 => function ($stackPos) { - $this->semValue = new Expr\StaticPropertyFetch($this->semStack[$stackPos - (6 - 1)], $this->semStack[$stackPos - (6 - 5)], $this->startAttributeStack[$stackPos - (6 - 1)] + $this->endAttributes); - }, 526 => function ($stackPos) { - $this->semValue = new Expr\ArrayDimFetch($this->semStack[$stackPos - (4 - 1)], $this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 527 => function ($stackPos) { - $this->semValue = new Expr\ArrayDimFetch($this->semStack[$stackPos - (4 - 1)], $this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 528 => function ($stackPos) { - $this->semValue = new Expr\ArrayDimFetch($this->semStack[$stackPos - (4 - 1)], $this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 529 => function ($stackPos) { - $this->semValue = new Expr\ArrayDimFetch($this->semStack[$stackPos - (4 - 1)], $this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 530 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 531 => function ($stackPos) { - $this->semValue = new Expr\Variable($this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 532 => function ($stackPos) { - $this->semValue = null; - }, 533 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 534 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 535 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (3 - 2)]; - }, 536 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 537 => function ($stackPos) { - $this->semValue = new Expr\Error($this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - $this->errorState = 2; - }, 538 => function ($stackPos) { - $this->semValue = new Expr\List_($this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 539 => function ($stackPos) { - $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; - $this->semValue = $this->semStack[$stackPos - (3 - 1)]; - }, 540 => function ($stackPos) { - $this->semValue = array($this->semStack[$stackPos - (1 - 1)]); - }, 541 => function ($stackPos) { - $this->semValue = new Expr\ArrayItem($this->semStack[$stackPos - (1 - 1)], null, \false, $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 542 => function ($stackPos) { - $this->semValue = new Expr\ArrayItem($this->semStack[$stackPos - (1 - 1)], null, \false, $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 543 => function ($stackPos) { - $this->semValue = null; - }, 544 => function ($stackPos) { - $this->semValue = array(); - }, 545 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (2 - 1)]; - }, 546 => function ($stackPos) { - $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; - $this->semValue = $this->semStack[$stackPos - (3 - 1)]; - }, 547 => function ($stackPos) { - $this->semValue = array($this->semStack[$stackPos - (1 - 1)]); - }, 548 => function ($stackPos) { - $this->semValue = new Expr\ArrayItem($this->semStack[$stackPos - (3 - 3)], $this->semStack[$stackPos - (3 - 1)], \false, $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 549 => function ($stackPos) { - $this->semValue = new Expr\ArrayItem($this->semStack[$stackPos - (1 - 1)], null, \false, $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 550 => function ($stackPos) { - $this->semValue = new Expr\ArrayItem($this->semStack[$stackPos - (4 - 4)], $this->semStack[$stackPos - (4 - 1)], \true, $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 551 => function ($stackPos) { - $this->semValue = new Expr\ArrayItem($this->semStack[$stackPos - (2 - 2)], null, \true, $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 552 => function ($stackPos) { - $this->semValue = new Expr\ArrayItem($this->semStack[$stackPos - (2 - 2)], null, \false, $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes, \true); - }, 553 => function ($stackPos) { - $this->semStack[$stackPos - (2 - 1)][] = $this->semStack[$stackPos - (2 - 2)]; - $this->semValue = $this->semStack[$stackPos - (2 - 1)]; - }, 554 => function ($stackPos) { - $this->semStack[$stackPos - (2 - 1)][] = $this->semStack[$stackPos - (2 - 2)]; - $this->semValue = $this->semStack[$stackPos - (2 - 1)]; - }, 555 => function ($stackPos) { - $this->semValue = array($this->semStack[$stackPos - (1 - 1)]); - }, 556 => function ($stackPos) { - $this->semValue = array($this->semStack[$stackPos - (2 - 1)], $this->semStack[$stackPos - (2 - 2)]); - }, 557 => function ($stackPos) { - $this->semValue = new Scalar\EncapsedStringPart($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 558 => function ($stackPos) { - $this->semValue = new Expr\Variable($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 559 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 560 => function ($stackPos) { - $this->semValue = new Expr\ArrayDimFetch($this->semStack[$stackPos - (4 - 1)], $this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 561 => function ($stackPos) { - $this->semValue = new Expr\PropertyFetch($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 562 => function ($stackPos) { - $this->semValue = new Expr\Variable($this->semStack[$stackPos - (3 - 2)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 563 => function ($stackPos) { - $this->semValue = new Expr\Variable($this->semStack[$stackPos - (3 - 2)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 564 => function ($stackPos) { - $this->semValue = new Expr\ArrayDimFetch($this->semStack[$stackPos - (6 - 2)], $this->semStack[$stackPos - (6 - 4)], $this->startAttributeStack[$stackPos - (6 - 1)] + $this->endAttributes); - }, 565 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (3 - 2)]; - }, 566 => function ($stackPos) { - $this->semValue = new Scalar\String_($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 567 => function ($stackPos) { - $this->semValue = $this->parseNumString($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 568 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }]; + foreach ($self->semStack[$stackPos - (3 - 2)] as $s) { + if ($s instanceof Node\InterpolatedStringPart) { + $s->value = Node\Scalar\String_::parseEscapeSequences($s->value, '"', $self->phpVersion->supportsUnicodeEscapes()); + } + } + $self->semValue = new Scalar\InterpolatedString($self->semStack[$stackPos - (3 - 2)], $attrs); + }, 536 => static function ($self, $stackPos) { + $self->semValue = $self->parseLNumber($self->semStack[$stackPos - (1 - 1)], $self->getAttributes($self->tokenStartStack[$stackPos - (1 - 1)], $self->tokenEndStack[$stackPos]), $self->phpVersion->allowsInvalidOctals()); + }, 537 => static function ($self, $stackPos) { + $self->semValue = Scalar\Float_::fromString($self->semStack[$stackPos - (1 - 1)], $self->getAttributes($self->tokenStartStack[$stackPos - (1 - 1)], $self->tokenEndStack[$stackPos])); + }, 538 => null, 539 => null, 540 => null, 541 => static function ($self, $stackPos) { + $self->semValue = $self->parseDocString($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 2)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos]), $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 3)], $self->tokenEndStack[$stackPos - (3 - 3)]), \true); + }, 542 => static function ($self, $stackPos) { + $self->semValue = $self->parseDocString($self->semStack[$stackPos - (2 - 1)], '', $self->semStack[$stackPos - (2 - 2)], $self->getAttributes($self->tokenStartStack[$stackPos - (2 - 1)], $self->tokenEndStack[$stackPos]), $self->getAttributes($self->tokenStartStack[$stackPos - (2 - 2)], $self->tokenEndStack[$stackPos - (2 - 2)]), \true); + }, 543 => static function ($self, $stackPos) { + $self->semValue = $self->parseDocString($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 2)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos]), $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 3)], $self->tokenEndStack[$stackPos - (3 - 3)]), \true); + }, 544 => static function ($self, $stackPos) { + $self->semValue = null; + }, 545 => null, 546 => null, 547 => static function ($self, $stackPos) { + $self->semValue = $self->semStack[$stackPos - (3 - 2)]; + }, 548 => null, 549 => null, 550 => null, 551 => null, 552 => null, 553 => null, 554 => static function ($self, $stackPos) { + $self->semValue = $self->semStack[$stackPos - (3 - 2)]; + }, 555 => null, 556 => null, 557 => null, 558 => static function ($self, $stackPos) { + $self->semValue = new Expr\ArrayDimFetch($self->semStack[$stackPos - (4 - 1)], $self->semStack[$stackPos - (4 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (4 - 1)], $self->tokenEndStack[$stackPos])); + }, 559 => static function ($self, $stackPos) { + $self->semValue = new Expr\ArrayDimFetch($self->semStack[$stackPos - (4 - 1)], $self->semStack[$stackPos - (4 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (4 - 1)], $self->tokenEndStack[$stackPos])); + }, 560 => null, 561 => static function ($self, $stackPos) { + $self->semValue = new Expr\MethodCall($self->semStack[$stackPos - (4 - 1)], $self->semStack[$stackPos - (4 - 3)], $self->semStack[$stackPos - (4 - 4)], $self->getAttributes($self->tokenStartStack[$stackPos - (4 - 1)], $self->tokenEndStack[$stackPos])); + }, 562 => static function ($self, $stackPos) { + $self->semValue = new Expr\NullsafeMethodCall($self->semStack[$stackPos - (4 - 1)], $self->semStack[$stackPos - (4 - 3)], $self->semStack[$stackPos - (4 - 4)], $self->getAttributes($self->tokenStartStack[$stackPos - (4 - 1)], $self->tokenEndStack[$stackPos])); + }, 563 => static function ($self, $stackPos) { + $self->semValue = null; + }, 564 => null, 565 => null, 566 => null, 567 => static function ($self, $stackPos) { + $self->semValue = new Expr\PropertyFetch($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 568 => static function ($self, $stackPos) { + $self->semValue = new Expr\NullsafePropertyFetch($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 569 => null, 570 => static function ($self, $stackPos) { + $self->semValue = new Expr\Variable($self->semStack[$stackPos - (4 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (4 - 1)], $self->tokenEndStack[$stackPos])); + }, 571 => static function ($self, $stackPos) { + $self->semValue = new Expr\Variable($self->semStack[$stackPos - (2 - 2)], $self->getAttributes($self->tokenStartStack[$stackPos - (2 - 1)], $self->tokenEndStack[$stackPos])); + }, 572 => static function ($self, $stackPos) { + $self->semValue = new Expr\Variable(new Expr\Error($self->getAttributes($self->tokenStartStack[$stackPos - (2 - 1)], $self->tokenEndStack[$stackPos])), $self->getAttributes($self->tokenStartStack[$stackPos - (2 - 1)], $self->tokenEndStack[$stackPos])); + $self->errorState = 2; + }, 573 => static function ($self, $stackPos) { + $var = $self->semStack[$stackPos - (1 - 1)]->name; + $self->semValue = \is_string($var) ? new Node\VarLikeIdentifier($var, $self->getAttributes($self->tokenStartStack[$stackPos - (1 - 1)], $self->tokenEndStack[$stackPos])) : $var; + }, 574 => static function ($self, $stackPos) { + $self->semValue = new Expr\StaticPropertyFetch($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 575 => null, 576 => static function ($self, $stackPos) { + $self->semValue = new Expr\ArrayDimFetch($self->semStack[$stackPos - (4 - 1)], $self->semStack[$stackPos - (4 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (4 - 1)], $self->tokenEndStack[$stackPos])); + }, 577 => static function ($self, $stackPos) { + $self->semValue = new Expr\ArrayDimFetch($self->semStack[$stackPos - (4 - 1)], $self->semStack[$stackPos - (4 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (4 - 1)], $self->tokenEndStack[$stackPos])); + }, 578 => static function ($self, $stackPos) { + $self->semValue = new Expr\PropertyFetch($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 579 => static function ($self, $stackPos) { + $self->semValue = new Expr\NullsafePropertyFetch($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 580 => static function ($self, $stackPos) { + $self->semValue = new Expr\StaticPropertyFetch($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 581 => static function ($self, $stackPos) { + $self->semValue = new Expr\StaticPropertyFetch($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 582 => null, 583 => static function ($self, $stackPos) { + $self->semValue = $self->semStack[$stackPos - (3 - 2)]; + }, 584 => null, 585 => null, 586 => static function ($self, $stackPos) { + $self->semValue = $self->semStack[$stackPos - (3 - 2)]; + }, 587 => null, 588 => static function ($self, $stackPos) { + $self->semValue = new Expr\Error($self->getAttributes($self->tokenStartStack[$stackPos - (1 - 1)], $self->tokenEndStack[$stackPos])); + $self->errorState = 2; + }, 589 => static function ($self, $stackPos) { + $self->semValue = new Expr\List_($self->semStack[$stackPos - (4 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (4 - 1)], $self->tokenEndStack[$stackPos])); + $self->semValue->setAttribute('kind', Expr\List_::KIND_LIST); + $self->postprocessList($self->semValue); + }, 590 => static function ($self, $stackPos) { + $self->semValue = $self->semStack[$stackPos - (1 - 1)]; + $end = count($self->semValue) - 1; + if ($self->semValue[$end]->value instanceof Expr\Error) { + array_pop($self->semValue); + } + }, 591 => null, 592 => static function ($self, $stackPos) { + /* do nothing -- prevent default action of $$=$self->semStack[$1]. See $551. */ + }, 593 => static function ($self, $stackPos) { + $self->semStack[$stackPos - (3 - 1)][] = $self->semStack[$stackPos - (3 - 3)]; + $self->semValue = $self->semStack[$stackPos - (3 - 1)]; + }, 594 => static function ($self, $stackPos) { + $self->semValue = array($self->semStack[$stackPos - (1 - 1)]); + }, 595 => static function ($self, $stackPos) { + $self->semValue = new Node\ArrayItem($self->semStack[$stackPos - (1 - 1)], null, \false, $self->getAttributes($self->tokenStartStack[$stackPos - (1 - 1)], $self->tokenEndStack[$stackPos])); + }, 596 => static function ($self, $stackPos) { + $self->semValue = new Node\ArrayItem($self->semStack[$stackPos - (2 - 2)], null, \true, $self->getAttributes($self->tokenStartStack[$stackPos - (2 - 1)], $self->tokenEndStack[$stackPos])); + }, 597 => static function ($self, $stackPos) { + $self->semValue = new Node\ArrayItem($self->semStack[$stackPos - (1 - 1)], null, \false, $self->getAttributes($self->tokenStartStack[$stackPos - (1 - 1)], $self->tokenEndStack[$stackPos])); + }, 598 => static function ($self, $stackPos) { + $self->semValue = new Node\ArrayItem($self->semStack[$stackPos - (3 - 3)], $self->semStack[$stackPos - (3 - 1)], \false, $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 599 => static function ($self, $stackPos) { + $self->semValue = new Node\ArrayItem($self->semStack[$stackPos - (4 - 4)], $self->semStack[$stackPos - (4 - 1)], \true, $self->getAttributes($self->tokenStartStack[$stackPos - (4 - 1)], $self->tokenEndStack[$stackPos])); + }, 600 => static function ($self, $stackPos) { + $self->semValue = new Node\ArrayItem($self->semStack[$stackPos - (3 - 3)], $self->semStack[$stackPos - (3 - 1)], \false, $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 601 => static function ($self, $stackPos) { + $self->semValue = new Node\ArrayItem($self->semStack[$stackPos - (2 - 2)], null, \false, $self->getAttributes($self->tokenStartStack[$stackPos - (2 - 1)], $self->tokenEndStack[$stackPos]), \true); + }, 602 => static function ($self, $stackPos) { + /* Create an Error node now to remember the position. We'll later either report an error, + or convert this into a null element, depending on whether this is a creation or destructuring context. */ + $attrs = $self->createEmptyElemAttributes($self->tokenPos); + $self->semValue = new Node\ArrayItem(new Expr\Error($attrs), null, \false, $attrs); + }, 603 => static function ($self, $stackPos) { + $self->semStack[$stackPos - (2 - 1)][] = $self->semStack[$stackPos - (2 - 2)]; + $self->semValue = $self->semStack[$stackPos - (2 - 1)]; + }, 604 => static function ($self, $stackPos) { + $self->semStack[$stackPos - (2 - 1)][] = $self->semStack[$stackPos - (2 - 2)]; + $self->semValue = $self->semStack[$stackPos - (2 - 1)]; + }, 605 => static function ($self, $stackPos) { + $self->semValue = array($self->semStack[$stackPos - (1 - 1)]); + }, 606 => static function ($self, $stackPos) { + $self->semValue = array($self->semStack[$stackPos - (2 - 1)], $self->semStack[$stackPos - (2 - 2)]); + }, 607 => static function ($self, $stackPos) { + $attrs = $self->getAttributes($self->tokenStartStack[$stackPos - (1 - 1)], $self->tokenEndStack[$stackPos]); + $attrs['rawValue'] = $self->semStack[$stackPos - (1 - 1)]; + $self->semValue = new Node\InterpolatedStringPart($self->semStack[$stackPos - (1 - 1)], $attrs); + }, 608 => static function ($self, $stackPos) { + $self->semValue = new Expr\Variable($self->semStack[$stackPos - (1 - 1)], $self->getAttributes($self->tokenStartStack[$stackPos - (1 - 1)], $self->tokenEndStack[$stackPos])); + }, 609 => null, 610 => static function ($self, $stackPos) { + $self->semValue = new Expr\ArrayDimFetch($self->semStack[$stackPos - (4 - 1)], $self->semStack[$stackPos - (4 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (4 - 1)], $self->tokenEndStack[$stackPos])); + }, 611 => static function ($self, $stackPos) { + $self->semValue = new Expr\PropertyFetch($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 612 => static function ($self, $stackPos) { + $self->semValue = new Expr\NullsafePropertyFetch($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 613 => static function ($self, $stackPos) { + $self->semValue = new Expr\Variable($self->semStack[$stackPos - (3 - 2)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 614 => static function ($self, $stackPos) { + $self->semValue = new Expr\Variable($self->semStack[$stackPos - (3 - 2)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 615 => static function ($self, $stackPos) { + $self->semValue = new Expr\ArrayDimFetch($self->semStack[$stackPos - (6 - 2)], $self->semStack[$stackPos - (6 - 4)], $self->getAttributes($self->tokenStartStack[$stackPos - (6 - 1)], $self->tokenEndStack[$stackPos])); + }, 616 => static function ($self, $stackPos) { + $self->semValue = $self->semStack[$stackPos - (3 - 2)]; + }, 617 => static function ($self, $stackPos) { + $self->semValue = new Scalar\String_($self->semStack[$stackPos - (1 - 1)], $self->getAttributes($self->tokenStartStack[$stackPos - (1 - 1)], $self->tokenEndStack[$stackPos])); + }, 618 => static function ($self, $stackPos) { + $self->semValue = $self->parseNumString($self->semStack[$stackPos - (1 - 1)], $self->getAttributes($self->tokenStartStack[$stackPos - (1 - 1)], $self->tokenEndStack[$stackPos])); + }, 619 => static function ($self, $stackPos) { + $self->semValue = $self->parseNumString('-' . $self->semStack[$stackPos - (2 - 2)], $self->getAttributes($self->tokenStartStack[$stackPos - (2 - 1)], $self->tokenEndStack[$stackPos])); + }, 620 => null]; } } '", "T_IS_GREATER_OR_EQUAL", "T_SL", "T_SR", "'+'", "'-'", "'.'", "'*'", "'/'", "'%'", "'!'", "T_INSTANCEOF", "'~'", "T_INC", "T_DEC", "T_INT_CAST", "T_DOUBLE_CAST", "T_STRING_CAST", "T_ARRAY_CAST", "T_OBJECT_CAST", "T_BOOL_CAST", "T_UNSET_CAST", "'@'", "T_POW", "'['", "T_NEW", "T_CLONE", "T_EXIT", "T_IF", "T_ELSEIF", "T_ELSE", "T_ENDIF", "T_LNUMBER", "T_DNUMBER", "T_STRING", "T_STRING_VARNAME", "T_VARIABLE", "T_NUM_STRING", "T_INLINE_HTML", "T_ENCAPSED_AND_WHITESPACE", "T_CONSTANT_ENCAPSED_STRING", "T_ECHO", "T_DO", "T_WHILE", "T_ENDWHILE", "T_FOR", "T_ENDFOR", "T_FOREACH", "T_ENDFOREACH", "T_DECLARE", "T_ENDDECLARE", "T_AS", "T_SWITCH", "T_MATCH", "T_ENDSWITCH", "T_CASE", "T_DEFAULT", "T_BREAK", "T_CONTINUE", "T_GOTO", "T_FUNCTION", "T_FN", "T_CONST", "T_RETURN", "T_TRY", "T_CATCH", "T_FINALLY", "T_USE", "T_INSTEADOF", "T_GLOBAL", "T_STATIC", "T_ABSTRACT", "T_FINAL", "T_PRIVATE", "T_PROTECTED", "T_PUBLIC", "T_READONLY", "T_VAR", "T_UNSET", "T_ISSET", "T_EMPTY", "T_HALT_COMPILER", "T_CLASS", "T_TRAIT", "T_INTERFACE", "T_ENUM", "T_EXTENDS", "T_IMPLEMENTS", "T_OBJECT_OPERATOR", "T_NULLSAFE_OBJECT_OPERATOR", "T_LIST", "T_ARRAY", "T_CALLABLE", "T_CLASS_C", "T_TRAIT_C", "T_METHOD_C", "T_FUNC_C", "T_LINE", "T_FILE", "T_START_HEREDOC", "T_END_HEREDOC", "T_DOLLAR_OPEN_CURLY_BRACES", "T_CURLY_OPEN", "T_PAAMAYIM_NEKUDOTAYIM", "T_NAMESPACE", "T_NS_C", "T_DIR", "T_NS_SEPARATOR", "T_ELLIPSIS", "T_NAME_FULLY_QUALIFIED", "T_NAME_QUALIFIED", "T_NAME_RELATIVE", "T_ATTRIBUTE", "';'", "']'", "'{'", "'}'", "'('", "')'", "'`'", "'\"'", "'\$'"); - protected $tokenToSymbol = array(0, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 56, 166, 168, 167, 55, 168, 168, 163, 164, 53, 50, 8, 51, 52, 54, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 31, 159, 44, 16, 46, 30, 68, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 70, 168, 160, 36, 168, 165, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 161, 35, 162, 58, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 1, 2, 3, 4, 5, 6, 7, 9, 10, 11, 12, 13, 14, 15, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 32, 33, 34, 37, 38, 39, 40, 41, 42, 43, 45, 47, 48, 49, 57, 59, 60, 61, 62, 63, 64, 65, 66, 67, 69, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158); - protected $action = array(133, 134, 135, 579, 136, 137, 0, 748, 749, 750, 138, 38, 327, -32766, -32766, -32766, -32766, -32766, -32766, -32767, -32767, -32767, -32767, 102, 103, 104, 105, 106, 1109, 1110, 1111, 1108, 1107, 1106, 1112, 742, 741, -32766, 1232, -32766, -32766, -32766, -32766, -32766, -32766, -32766, -32767, -32767, -32767, -32767, -32767, 2, 107, 108, 109, 751, 274, 381, 380, -32766, -32766, -32766, -32766, 104, 105, 106, 1024, 422, 110, 265, 139, 403, 755, 756, 757, 758, 466, 467, 428, 938, 291, -32766, 287, -32766, -32766, 759, 760, 761, 762, 763, 764, 765, 766, 767, 768, 769, 789, 580, 790, 791, 792, 793, 781, 782, 344, 345, 784, 785, 770, 771, 772, 774, 775, 776, 355, 816, 817, 818, 819, 820, 581, 777, 778, 582, 583, 810, 801, 799, 800, 813, 796, 797, 687, -545, 584, 585, 795, 586, 587, 588, 589, 590, 591, -328, -593, -367, 1234, -367, 798, 592, 593, -593, 140, -32766, -32766, -32766, 133, 134, 135, 579, 136, 137, 1057, 748, 749, 750, 138, 38, 688, 1020, 1019, 1018, 1021, 390, -32766, 7, -32766, -32766, -32766, -32766, -32766, -32766, -32766, -32766, -32766, -32766, 379, 380, 1033, 689, 690, 742, 741, -32766, -32766, -32766, 422, -545, -545, -590, -32766, -32766, -32766, 1032, -32766, 127, -590, 1236, 1235, 1237, 1318, 751, -545, 290, -32766, 283, -32766, -32766, -32766, -32766, -32766, 1236, 1235, 1237, -545, 265, 139, 403, 755, 756, 757, 758, 16, 481, 428, 458, 459, 460, 298, 722, 35, 759, 760, 761, 762, 763, 764, 765, 766, 767, 768, 769, 789, 580, 790, 791, 792, 793, 781, 782, 344, 345, 784, 785, 770, 771, 772, 774, 775, 776, 355, 816, 817, 818, 819, 820, 581, 777, 778, 582, 583, 810, 801, 799, 800, 813, 796, 797, 129, 824, 584, 585, 795, 586, 587, 588, 589, 590, 591, -328, 83, 84, 85, -593, 798, 592, 593, -593, 149, 773, 743, 744, 745, 746, 747, 824, 748, 749, 750, 786, 787, 37, 145, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 291, 274, 835, 254, 1109, 1110, 1111, 1108, 1107, 1106, 1112, -590, 860, 110, 861, -590, 482, 751, -32766, -32766, -32766, -32766, -32766, 142, 603, 1085, 742, 741, 1262, 326, 987, 752, 753, 754, 755, 756, 757, 758, 309, -32766, 822, -32766, -32766, -32766, -32766, 242, 553, 759, 760, 761, 762, 763, 764, 765, 766, 767, 768, 769, 789, 812, 790, 791, 792, 793, 781, 782, 783, 811, 784, 785, 770, 771, 772, 774, 775, 776, 815, 816, 817, 818, 819, 820, 821, 777, 778, 779, 780, 810, 801, 799, 800, 813, 796, 797, 311, 940, 788, 794, 795, 802, 803, 805, 804, 806, 807, 323, 609, 1274, 1033, 833, 798, 809, 808, 50, 51, 52, 512, 53, 54, 860, 241, 861, 918, 55, 56, -111, 57, -32766, -32766, -32766, -111, 826, -111, 290, 1302, 1347, 356, 305, 1348, 339, -111, -111, -111, -111, -111, -111, -111, -111, -32766, -194, -32766, -32766, -32766, -193, 956, 957, 829, -86, 988, 958, 834, 58, 59, 340, 428, 952, -544, 60, 832, 61, 247, 248, 62, 63, 64, 65, 66, 67, 68, 69, 1241, 28, 267, 70, 444, 513, -342, -32766, 141, 1268, 1269, 514, 918, 833, 326, -272, 918, 1266, 42, 25, 515, 940, 516, 14, 517, 908, 518, 828, 369, 519, 520, 373, 709, 1033, 44, 45, 445, 376, 375, 388, 46, 521, 712, -86, 440, 1101, 367, 338, -543, 441, -544, -544, 830, 1227, 442, 523, 524, 525, 290, 1236, 1235, 1237, 361, 1030, 443, -544, 1087, 526, 527, 839, 1255, 1256, 1257, 1258, 1252, 1253, 297, -544, 151, -550, -584, 833, 1259, 1254, -584, 1033, 1236, 1235, 1237, 298, -154, -154, -154, 152, 71, 908, 321, 322, 326, 908, 920, 1030, 707, 833, 154, -154, 1337, -154, 155, -154, 283, -154, -543, -543, 82, 1232, 1086, 1322, 734, 156, 326, 374, 158, 1033, 1321, -194, -79, -543, -88, -193, 742, 741, 956, 957, 653, 26, -32766, 522, -51, -543, 33, -549, 894, 952, -111, -111, -111, 32, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, -59, 75, 28, 672, 673, 326, -58, 36, 250, 920, 124, 707, 125, 920, 833, 707, -154, 130, 1266, 131, -32766, -547, 144, -542, 150, 406, 1234, 377, 378, 1146, 1148, 382, 383, -32766, -32766, -32766, -85, -32766, 1056, -32766, -542, -32766, 644, 645, -32766, 159, 160, 161, 1232, -32766, -32766, -32766, 162, -79, 1227, -32766, -32766, 742, 741, 163, -302, -32766, 419, -75, -4, 918, -73, 287, 526, 527, -32766, 1255, 1256, 1257, 1258, 1252, 1253, -72, -71, -70, -69, -68, -67, 1259, 1254, -547, -547, -542, -542, 742, 741, -66, -47, -18, -32766, 73, 148, 918, 322, 326, 1234, 273, -542, 284, -542, -542, 723, -32766, -32766, -32766, 726, -32766, -547, -32766, -542, -32766, 917, 147, -32766, -542, 288, 289, -298, -32766, -32766, -32766, -32766, 713, 279, -32766, -32766, -542, 1234, 280, 285, -32766, 419, 48, 286, -32766, -32766, -32766, 332, -32766, -32766, -32766, 292, -32766, 908, 293, -32766, 934, 274, 1030, 918, -32766, -32766, -32766, 110, 682, 132, -32766, -32766, 833, 146, -32766, 559, -32766, 419, 659, 374, 824, 435, 1349, 74, 1033, -32766, 296, 654, 1116, 908, 956, 957, 306, 714, 698, 522, 555, 303, 13, 310, 852, 952, -111, -111, -111, 700, 463, 492, 953, 283, 299, 300, -32766, 49, 675, 918, 304, 660, 1234, 676, 936, 1273, -32766, 10, 1263, -32766, -32766, -32766, 642, -32766, 918, -32766, 920, -32766, 707, -4, -32766, 126, 34, 918, 565, -32766, -32766, -32766, -32766, 0, 908, -32766, -32766, 0, 1234, 918, 0, -32766, 419, 0, 0, -32766, -32766, -32766, 717, -32766, -32766, -32766, 920, -32766, 707, 1033, -32766, 724, 1275, 0, 487, -32766, -32766, -32766, -32766, 301, 302, -32766, -32766, -507, 1234, 571, -497, -32766, 419, 607, 8, -32766, -32766, -32766, 372, -32766, -32766, -32766, 17, -32766, 908, 371, -32766, 832, 298, 320, 128, -32766, -32766, -32766, 40, 370, 41, -32766, -32766, 908, -250, -250, -250, -32766, 419, 731, 374, 973, 908, 707, 732, 899, -32766, 997, 974, 728, 981, 956, 957, 971, 908, 982, 522, 897, 969, 1090, 1093, 894, 952, -111, -111, -111, 28, 1094, 1091, 1092, -249, -249, -249, 1241, 1098, 708, 374, 844, 833, 1288, 1306, 1340, 1266, 647, 1267, 711, 715, 956, 957, 716, 1241, 718, 522, 920, 719, 707, -250, 894, 952, -111, -111, -111, 720, -16, 721, 725, 710, -511, 920, 895, 707, -578, 1232, 1344, 1346, 855, 854, 920, 1227, 707, -577, 863, 946, 989, 862, 1345, 945, 943, 944, 920, 947, 707, -249, 527, 1218, 1255, 1256, 1257, 1258, 1252, 1253, 927, 937, 925, 979, 980, 631, 1259, 1254, 1343, 1300, -32766, 1289, 1307, 833, 1316, -275, 1234, -576, 73, -550, -549, 322, 326, -32766, -32766, -32766, -548, -32766, -491, -32766, 833, -32766, 1, 29, -32766, 30, 39, 43, 47, -32766, -32766, -32766, 72, 76, 77, -32766, -32766, 1232, -111, -111, 78, -32766, 419, -111, 79, 80, 81, 143, 153, -111, -32766, 157, 246, 328, 1232, -111, -111, 356, -32766, 357, -111, 358, 359, 360, 361, 362, -111, 363, 364, 365, 366, 368, 436, 0, -273, -32766, -272, 19, 20, 298, 21, 22, 24, 405, 75, 1203, 483, 484, 326, 491, 0, 494, 495, 496, 497, 501, 298, 502, 503, 510, 693, 75, 0, 1245, 1186, 326, 1264, 1059, 1058, 1039, 1222, 1035, -277, -103, 18, 23, 27, 295, 404, 600, 604, 633, 699, 1190, 1240, 1187, 1319, 0, 0, 0, 326); - protected $actionCheck = array(2, 3, 4, 5, 6, 7, 0, 9, 10, 11, 12, 13, 70, 9, 10, 11, 9, 10, 11, 44, 45, 46, 47, 48, 49, 50, 51, 52, 116, 117, 118, 119, 120, 121, 122, 37, 38, 30, 116, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 8, 53, 54, 55, 57, 57, 106, 107, 137, 9, 10, 11, 50, 51, 52, 1, 116, 69, 71, 72, 73, 74, 75, 76, 77, 134, 135, 80, 1, 30, 30, 30, 32, 33, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 80, 70, 136, 137, 138, 139, 140, 141, 142, 143, 144, 8, 1, 106, 80, 108, 150, 151, 152, 8, 154, 9, 10, 11, 2, 3, 4, 5, 6, 7, 164, 9, 10, 11, 12, 13, 116, 119, 120, 121, 122, 106, 30, 108, 32, 33, 34, 35, 36, 37, 38, 9, 10, 11, 106, 107, 138, 137, 138, 37, 38, 9, 10, 11, 116, 134, 135, 1, 9, 10, 11, 137, 30, 14, 8, 155, 156, 157, 1, 57, 149, 163, 30, 163, 32, 33, 34, 35, 36, 155, 156, 157, 161, 71, 72, 73, 74, 75, 76, 77, 8, 31, 80, 129, 130, 131, 158, 161, 8, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 8, 80, 136, 137, 138, 139, 140, 141, 142, 143, 144, 164, 9, 10, 11, 160, 150, 151, 152, 164, 154, 2, 3, 4, 5, 6, 7, 80, 9, 10, 11, 12, 13, 30, 8, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 30, 57, 1, 8, 116, 117, 118, 119, 120, 121, 122, 160, 106, 69, 108, 164, 161, 57, 9, 10, 11, 9, 10, 161, 1, 1, 37, 38, 1, 167, 31, 71, 72, 73, 74, 75, 76, 77, 8, 30, 80, 32, 33, 34, 35, 14, 85, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 8, 122, 136, 137, 138, 139, 140, 141, 142, 143, 144, 8, 51, 146, 138, 82, 150, 151, 152, 2, 3, 4, 5, 6, 7, 106, 97, 108, 1, 12, 13, 101, 15, 9, 10, 11, 106, 80, 108, 163, 1, 80, 163, 113, 83, 8, 116, 117, 118, 119, 120, 121, 122, 123, 30, 8, 32, 33, 34, 8, 117, 118, 80, 31, 159, 122, 159, 50, 51, 8, 80, 128, 70, 56, 155, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 1, 70, 71, 72, 73, 74, 162, 9, 161, 78, 79, 80, 1, 82, 167, 164, 1, 86, 87, 88, 89, 122, 91, 101, 93, 84, 95, 156, 8, 98, 99, 8, 161, 138, 103, 104, 105, 106, 107, 8, 109, 110, 31, 97, 8, 123, 115, 116, 70, 8, 134, 135, 156, 122, 8, 124, 125, 126, 163, 155, 156, 157, 163, 116, 8, 149, 162, 136, 137, 8, 139, 140, 141, 142, 143, 144, 145, 161, 14, 163, 160, 82, 151, 152, 164, 138, 155, 156, 157, 158, 75, 76, 77, 14, 163, 84, 165, 166, 167, 84, 159, 116, 161, 82, 14, 90, 85, 92, 14, 94, 163, 96, 134, 135, 161, 116, 159, 1, 161, 14, 167, 106, 14, 138, 8, 164, 16, 149, 31, 164, 37, 38, 117, 118, 75, 76, 137, 122, 31, 161, 14, 163, 127, 128, 129, 130, 131, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 16, 163, 70, 75, 76, 167, 16, 147, 148, 159, 16, 161, 16, 159, 82, 161, 162, 16, 86, 16, 74, 70, 16, 70, 101, 102, 80, 106, 107, 59, 60, 106, 107, 87, 88, 89, 31, 91, 1, 93, 70, 95, 111, 112, 98, 16, 16, 16, 116, 103, 104, 105, 16, 31, 122, 109, 110, 37, 38, 16, 35, 115, 116, 31, 0, 1, 31, 30, 136, 137, 124, 139, 140, 141, 142, 143, 144, 31, 31, 31, 31, 31, 31, 151, 152, 134, 135, 134, 135, 37, 38, 31, 31, 31, 74, 163, 31, 1, 166, 167, 80, 31, 149, 31, 134, 135, 31, 87, 88, 89, 31, 91, 161, 93, 161, 95, 31, 31, 98, 149, 37, 37, 35, 103, 104, 105, 74, 31, 35, 109, 110, 161, 80, 35, 35, 115, 116, 70, 35, 87, 88, 89, 35, 91, 124, 93, 37, 95, 84, 37, 98, 38, 57, 116, 1, 103, 104, 105, 69, 77, 31, 109, 110, 82, 70, 85, 89, 115, 116, 96, 106, 80, 108, 83, 154, 138, 124, 113, 90, 82, 84, 117, 118, 114, 31, 80, 122, 85, 132, 97, 132, 127, 128, 129, 130, 131, 92, 97, 97, 128, 163, 134, 135, 74, 70, 94, 1, 133, 100, 80, 100, 154, 146, 137, 150, 160, 87, 88, 89, 113, 91, 1, 93, 159, 95, 161, 162, 98, 161, 161, 1, 153, 103, 104, 105, 74, -1, 84, 109, 110, -1, 80, 1, -1, 115, 116, -1, -1, 87, 88, 89, 31, 91, 124, 93, 159, 95, 161, 138, 98, 31, 146, -1, 102, 103, 104, 105, 74, 134, 135, 109, 110, 149, 80, 81, 149, 115, 116, 153, 149, 87, 88, 89, 149, 91, 124, 93, 149, 95, 84, 149, 98, 155, 158, 161, 161, 103, 104, 105, 159, 161, 159, 109, 110, 84, 100, 101, 102, 115, 116, 159, 106, 159, 84, 161, 159, 159, 124, 159, 159, 162, 159, 117, 118, 159, 84, 159, 122, 159, 159, 159, 159, 127, 128, 129, 130, 131, 70, 159, 159, 159, 100, 101, 102, 1, 159, 161, 106, 160, 82, 160, 160, 160, 86, 160, 166, 161, 161, 117, 118, 161, 1, 161, 122, 159, 161, 161, 162, 127, 128, 129, 130, 131, 161, 31, 161, 161, 161, 165, 159, 162, 161, 163, 116, 162, 162, 162, 162, 159, 122, 161, 163, 162, 162, 162, 162, 162, 162, 162, 162, 159, 162, 161, 162, 137, 162, 139, 140, 141, 142, 143, 144, 162, 162, 162, 162, 162, 162, 151, 152, 162, 162, 74, 162, 162, 82, 162, 164, 80, 163, 163, 163, 163, 166, 167, 87, 88, 89, 163, 91, 163, 93, 82, 95, 163, 163, 98, 163, 163, 163, 163, 103, 104, 105, 163, 163, 163, 109, 110, 116, 117, 118, 163, 115, 116, 122, 163, 163, 163, 163, 163, 128, 124, 163, 163, 163, 116, 117, 118, 163, 137, 163, 122, 163, 163, 163, 163, 163, 128, 163, 163, 163, 163, 163, 163, -1, 164, 137, 164, 164, 164, 158, 164, 164, 164, 164, 163, 165, 164, 164, 167, 164, -1, 164, 164, 164, 164, 164, 158, 164, 164, 164, 164, 163, -1, 164, 164, 167, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, -1, -1, -1, 167); - protected $actionBase = array(0, -2, 154, 542, 752, 893, 929, 52, 374, 431, 398, 869, 793, 235, 307, 307, 793, 307, 784, 908, 908, 917, 908, 538, 841, 468, 468, 468, 708, 708, 708, 708, 740, 740, 849, 849, 881, 817, 634, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 348, 346, 370, 653, 1063, 1069, 1065, 1070, 1061, 1060, 1064, 1066, 1071, 946, 947, 774, 949, 950, 943, 952, 1067, 882, 1062, 1068, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 525, 191, 359, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 174, 174, 174, 620, 620, 51, 465, 356, 955, 955, 955, 955, 955, 955, 955, 955, 955, 955, 658, 184, 144, 144, 7, 7, 7, 7, 7, 1031, 371, 1048, -25, -25, -25, -25, 50, 725, 526, 449, 39, 317, 80, 474, 474, 13, 13, 512, 512, 422, 422, 512, 512, 512, 808, 808, 808, 808, 443, 505, 360, 308, -78, 209, 209, 209, 209, -78, -78, -78, -78, 803, 877, -78, -78, -78, 63, 641, 641, 822, -1, -1, -1, 641, 253, 790, 548, 253, 384, 548, 480, 402, 764, 759, -49, 447, 764, 639, 755, 198, 143, 825, 609, 825, 1059, 320, 768, 426, 749, 720, 874, 904, 1072, 796, 941, 798, 942, 106, -58, 710, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1073, 336, 1059, 423, 1073, 1073, 1073, 336, 336, 336, 336, 336, 336, 336, 336, 336, 336, 619, 423, 586, 616, 423, 795, 336, 348, 814, 348, 348, 348, 348, 348, 348, 348, 348, 348, 348, 750, 202, 348, 346, 78, 78, 484, 65, 78, 78, 78, 78, 348, 348, 348, 348, 609, 783, 766, 613, 813, 492, 783, 783, 783, 473, 135, 378, 488, 713, 775, 67, 779, 779, 785, 969, 969, 779, 769, 779, 785, 975, 779, 779, 969, 969, 823, 280, 563, 478, 550, 568, 969, 377, 779, 779, 779, 779, 746, 573, 779, 342, 314, 779, 779, 746, 744, 760, 43, 762, 969, 969, 969, 746, 547, 762, 762, 762, 839, 844, 794, 758, 444, 433, 588, 232, 801, 758, 758, 779, 558, 794, 758, 794, 758, 745, 758, 758, 758, 794, 758, 769, 502, 758, 717, 583, 224, 758, 6, 979, 980, 624, 981, 973, 987, 1019, 991, 992, 873, 965, 999, 974, 993, 972, 970, 773, 682, 684, 818, 811, 963, 777, 777, 777, 956, 777, 777, 777, 777, 777, 777, 777, 777, 682, 743, 829, 765, 1006, 689, 691, 754, 906, 901, 1030, 1004, 1049, 994, 828, 694, 1028, 1008, 846, 821, 1009, 1010, 1029, 1050, 1052, 910, 782, 911, 912, 876, 1012, 883, 777, 979, 992, 693, 974, 993, 972, 970, 748, 739, 737, 738, 736, 735, 723, 734, 753, 1053, 954, 907, 878, 1011, 957, 682, 879, 1023, 756, 1032, 1033, 827, 788, 778, 880, 913, 1014, 1015, 1016, 884, 1054, 887, 830, 1024, 951, 1035, 789, 918, 1037, 1038, 1039, 1040, 889, 919, 892, 916, 900, 845, 776, 1020, 761, 920, 591, 787, 791, 800, 1018, 606, 1000, 902, 921, 922, 1041, 1043, 1044, 923, 924, 995, 847, 1026, 799, 1027, 1022, 848, 850, 617, 797, 1055, 781, 786, 772, 621, 632, 925, 927, 931, 998, 763, 770, 853, 855, 1056, 771, 1057, 938, 635, 857, 718, 939, 1046, 719, 724, 637, 678, 672, 731, 792, 903, 826, 757, 780, 1017, 724, 767, 858, 940, 859, 860, 867, 1045, 868, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 458, 458, 458, 458, 458, 458, 307, 307, 307, 307, 307, 307, 307, 0, 0, 307, 0, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 66, 66, 291, 291, 291, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 0, 291, 291, 291, 291, 291, 291, 291, 291, 66, 823, 66, -1, -1, -1, -1, 66, 66, 66, -88, -88, 66, 384, 66, 66, -1, -1, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 0, 0, 423, 548, 66, 769, 769, 769, 769, 66, 66, 66, 66, 548, 548, 66, 66, 66, 0, 0, 0, 0, 0, 0, 0, 0, 423, 548, 0, 423, 0, 0, 769, 769, 66, 384, 823, 643, 66, 0, 0, 0, 0, 423, 769, 423, 336, 779, 548, 779, 336, 336, 78, 348, 643, 611, 611, 611, 611, 0, 0, 609, 823, 823, 823, 823, 823, 823, 823, 823, 823, 823, 823, 769, 0, 823, 0, 769, 769, 769, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 769, 0, 0, 969, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 975, 0, 0, 0, 0, 0, 0, 769, 0, 0, 0, 0, 0, 0, 0, 0, 0, 777, 788, 0, 788, 0, 777, 777, 777, 0, 0, 0, 0, 797, 771); - protected $actionDefault = array(3, 32767, 103, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 101, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 596, 596, 596, 596, 32767, 32767, 254, 103, 32767, 32767, 469, 387, 387, 387, 32767, 32767, 540, 540, 540, 540, 540, 540, 32767, 32767, 32767, 32767, 32767, 32767, 469, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 101, 32767, 32767, 32767, 37, 7, 8, 10, 11, 50, 17, 324, 32767, 32767, 32767, 32767, 103, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 589, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 473, 452, 453, 455, 456, 386, 541, 595, 327, 592, 385, 146, 339, 329, 242, 330, 258, 474, 259, 475, 478, 479, 215, 287, 382, 150, 151, 416, 470, 418, 468, 472, 417, 392, 397, 398, 399, 400, 401, 402, 403, 404, 405, 406, 407, 408, 409, 390, 391, 471, 449, 448, 447, 32767, 32767, 414, 415, 419, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 103, 32767, 389, 422, 420, 421, 438, 439, 436, 437, 440, 32767, 32767, 32767, 441, 442, 443, 444, 316, 32767, 32767, 366, 364, 316, 112, 32767, 32767, 429, 430, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 534, 446, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 103, 32767, 101, 536, 411, 413, 503, 424, 425, 423, 393, 32767, 510, 32767, 103, 32767, 512, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 535, 32767, 542, 542, 32767, 496, 101, 195, 32767, 32767, 32767, 195, 195, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 603, 496, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 32767, 195, 111, 32767, 32767, 32767, 101, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 190, 32767, 268, 270, 103, 557, 195, 32767, 515, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 508, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 496, 434, 139, 32767, 139, 542, 426, 427, 428, 498, 542, 542, 542, 312, 289, 32767, 32767, 32767, 32767, 513, 513, 101, 101, 101, 101, 508, 32767, 32767, 32767, 32767, 112, 100, 100, 100, 100, 100, 104, 102, 32767, 32767, 32767, 32767, 223, 100, 32767, 102, 102, 32767, 32767, 223, 225, 212, 102, 227, 32767, 561, 562, 223, 102, 227, 227, 227, 247, 247, 485, 318, 102, 100, 102, 102, 197, 318, 318, 32767, 102, 485, 318, 485, 318, 199, 318, 318, 318, 485, 318, 32767, 102, 318, 214, 100, 100, 318, 32767, 32767, 32767, 498, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 222, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 529, 32767, 546, 559, 432, 433, 435, 544, 457, 458, 459, 460, 461, 462, 463, 465, 591, 32767, 502, 32767, 32767, 32767, 338, 601, 32767, 601, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 602, 32767, 542, 32767, 32767, 32767, 32767, 431, 9, 76, 491, 43, 44, 52, 58, 519, 520, 521, 522, 516, 517, 523, 518, 32767, 32767, 524, 567, 32767, 32767, 543, 594, 32767, 32767, 32767, 32767, 32767, 32767, 139, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 529, 32767, 137, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 525, 32767, 32767, 32767, 542, 32767, 32767, 32767, 32767, 314, 311, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 542, 32767, 32767, 32767, 32767, 32767, 291, 32767, 308, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 286, 32767, 32767, 381, 498, 294, 296, 297, 32767, 32767, 32767, 32767, 360, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 153, 153, 3, 3, 341, 153, 153, 153, 341, 341, 153, 341, 341, 341, 153, 153, 153, 153, 153, 153, 280, 185, 262, 265, 247, 247, 153, 352, 153); - protected $goto = array(196, 196, 1031, 703, 694, 430, 658, 1062, 1334, 1334, 424, 313, 314, 335, 573, 319, 429, 336, 431, 635, 651, 652, 850, 669, 670, 671, 1334, 167, 167, 167, 167, 221, 197, 193, 193, 177, 179, 216, 193, 193, 193, 193, 193, 194, 194, 194, 194, 194, 194, 188, 189, 190, 191, 192, 218, 216, 219, 534, 535, 420, 536, 538, 539, 540, 541, 542, 543, 544, 545, 1132, 168, 169, 170, 195, 171, 172, 173, 166, 174, 175, 176, 178, 215, 217, 220, 238, 243, 244, 245, 257, 258, 259, 260, 261, 262, 263, 264, 268, 269, 270, 271, 281, 282, 316, 317, 318, 425, 426, 427, 578, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 180, 237, 181, 198, 199, 200, 239, 188, 189, 190, 191, 192, 218, 1132, 201, 182, 183, 184, 202, 198, 185, 240, 203, 201, 165, 204, 205, 186, 206, 207, 208, 187, 209, 210, 211, 212, 213, 214, 853, 851, 278, 278, 278, 278, 418, 620, 620, 350, 570, 597, 1265, 1265, 1265, 1265, 1265, 1265, 1265, 1265, 1265, 1265, 1283, 1283, 831, 618, 655, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 353, 353, 353, 353, 866, 557, 550, 858, 825, 907, 902, 903, 916, 859, 904, 856, 905, 906, 857, 878, 457, 910, 865, 884, 546, 546, 546, 546, 831, 601, 831, 1084, 1079, 1080, 1081, 341, 550, 557, 566, 567, 343, 576, 599, 613, 614, 407, 408, 972, 465, 465, 667, 15, 668, 1323, 411, 412, 413, 465, 681, 348, 1233, 414, 1233, 478, 569, 346, 439, 1031, 1031, 1233, 993, 480, 1031, 393, 1031, 1031, 1104, 1105, 1031, 1031, 1031, 1031, 1031, 1031, 1031, 1031, 1031, 1031, 1031, 1315, 1315, 1315, 1315, 1233, 657, 1333, 1333, 1055, 1233, 1233, 1233, 1233, 1037, 1036, 1233, 1233, 1233, 1034, 1034, 1181, 354, 678, 949, 1333, 437, 1026, 1042, 1043, 337, 691, 354, 354, 827, 923, 691, 1040, 1041, 924, 691, 663, 1336, 939, 871, 939, 354, 354, 1281, 1281, 354, 679, 1350, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 552, 537, 537, 911, 354, 912, 537, 537, 537, 537, 537, 537, 537, 537, 537, 537, 548, 564, 548, 574, 611, 730, 634, 636, 849, 548, 656, 475, 1308, 1309, 680, 684, 1007, 692, 701, 1003, 252, 252, 996, 970, 970, 968, 970, 729, 843, 549, 1005, 1000, 423, 455, 608, 1294, 846, 955, 966, 966, 966, 966, 325, 308, 455, 960, 967, 249, 249, 249, 249, 251, 253, 402, 351, 352, 683, 868, 551, 561, 449, 449, 449, 551, 1305, 561, 1305, 612, 396, 461, 1010, 1010, 1224, 1305, 395, 398, 558, 598, 602, 1015, 468, 577, 469, 470, 1310, 1311, 876, 552, 846, 1341, 1342, 964, 409, 702, 733, 324, 275, 324, 1317, 1317, 1317, 1317, 606, 621, 624, 625, 626, 627, 648, 649, 650, 705, 1068, 596, 1097, 874, 706, 476, 1228, 507, 697, 880, 1095, 1115, 432, 1301, 628, 630, 632, 432, 879, 867, 1067, 1071, 5, 1072, 6, 1038, 1038, 977, 0, 975, 662, 1049, 1045, 1046, 0, 0, 0, 0, 1226, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 928, 1120, 449, 965, 1070, 0, 0, 616, 1303, 1303, 1070, 1229, 1230, 1012, 499, 0, 500, 0, 0, 841, 0, 870, 506, 661, 991, 1113, 883, 1212, 941, 864, 0, 1213, 1216, 942, 1217, 0, 0, 1231, 1291, 1292, 0, 1223, 0, 0, 0, 846, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255); - protected $gotoCheck = array(42, 42, 72, 9, 72, 65, 65, 126, 181, 181, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 85, 85, 26, 85, 85, 85, 181, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 15, 27, 23, 23, 23, 23, 43, 107, 107, 96, 170, 129, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 168, 168, 12, 55, 55, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 24, 24, 24, 24, 35, 75, 75, 15, 6, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 35, 82, 15, 35, 45, 106, 106, 106, 106, 12, 106, 12, 15, 15, 15, 15, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 81, 81, 49, 148, 148, 81, 75, 81, 179, 81, 81, 81, 148, 81, 177, 72, 81, 72, 83, 103, 81, 82, 72, 72, 72, 102, 83, 72, 61, 72, 72, 143, 143, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 9, 9, 9, 9, 72, 63, 180, 180, 113, 72, 72, 72, 72, 117, 117, 72, 72, 72, 88, 88, 150, 14, 88, 88, 180, 112, 88, 88, 88, 29, 7, 14, 14, 7, 72, 7, 118, 118, 72, 7, 119, 180, 9, 39, 9, 14, 14, 169, 169, 14, 115, 14, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 14, 171, 171, 64, 14, 64, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 19, 48, 19, 2, 2, 48, 48, 48, 25, 19, 48, 174, 174, 174, 48, 48, 48, 48, 48, 48, 5, 5, 25, 25, 25, 25, 25, 25, 18, 25, 25, 25, 13, 19, 13, 14, 22, 91, 19, 19, 19, 19, 167, 167, 19, 19, 19, 5, 5, 5, 5, 5, 5, 28, 96, 96, 14, 37, 9, 9, 23, 23, 23, 9, 129, 9, 129, 79, 9, 9, 106, 106, 159, 129, 58, 58, 58, 58, 58, 109, 9, 9, 9, 9, 176, 176, 9, 14, 22, 9, 9, 92, 92, 92, 98, 24, 24, 24, 129, 129, 129, 129, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 128, 8, 8, 9, 8, 156, 20, 8, 8, 41, 8, 146, 116, 129, 84, 84, 84, 116, 16, 16, 16, 16, 46, 131, 46, 116, 116, 95, -1, 16, 116, 116, 116, 116, -1, -1, -1, -1, 14, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 17, 17, 23, 16, 129, -1, -1, 17, 129, 129, 129, 20, 20, 17, 154, -1, 154, -1, -1, 20, -1, 17, 154, 17, 17, 16, 16, 78, 78, 17, -1, 78, 78, 78, 78, -1, -1, 20, 20, 20, -1, 17, -1, -1, -1, 22, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 5, 5); - protected $gotoBase = array(0, 0, -339, 0, 0, 386, 195, 312, 472, -10, 0, 0, -109, 62, 13, -184, 46, 65, 86, 102, 93, 0, 125, 162, 197, 371, 18, 160, 83, 22, 0, 0, 0, 0, 0, -166, 0, 85, 0, 9, 0, 48, -1, 157, 0, 207, -232, 0, -340, 223, 0, 0, 0, 0, 0, 148, 0, 0, 396, 0, 0, 231, 0, 52, 334, -236, 0, 0, 0, 0, 0, 0, -5, 0, 0, -139, 0, 0, 149, 91, 112, -245, -58, -205, 15, -695, 0, 0, 28, 0, 0, 75, 154, 0, 0, 64, -310, 0, 55, 0, 0, 0, 235, 221, 0, 0, 196, -71, 0, 77, 0, 0, 37, 24, 0, 56, 219, 23, 40, 39, 0, 0, 0, 0, 0, 0, 5, 0, 106, 166, 0, 61, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 47, 0, 214, 0, 35, 0, 0, 0, 49, 0, 45, 0, 0, 71, 0, 0, 0, 0, 0, 0, 0, 88, -56, 95, 144, 111, 0, 0, 78, 0, 80, 229, 0, 222, -12, -299, 0, 0); - protected $gotoDefault = array(-32768, 511, 737, 4, 738, 932, 814, 823, 594, 528, 704, 347, 622, 421, 1299, 909, 1119, 575, 842, 1242, 1250, 456, 845, 330, 727, 891, 892, 893, 399, 385, 391, 397, 646, 623, 493, 877, 452, 869, 485, 872, 451, 881, 164, 417, 509, 885, 3, 888, 554, 919, 386, 896, 387, 674, 898, 560, 900, 901, 394, 400, 401, 1124, 568, 619, 913, 256, 562, 914, 384, 915, 922, 389, 392, 685, 464, 504, 498, 410, 1099, 563, 605, 643, 446, 472, 617, 629, 615, 479, 433, 415, 329, 954, 962, 486, 462, 976, 349, 984, 735, 1131, 637, 488, 992, 638, 999, 1002, 529, 530, 477, 1014, 272, 1017, 489, 12, 664, 1028, 1029, 665, 639, 1051, 640, 666, 641, 1053, 471, 595, 1061, 453, 1069, 1287, 454, 1073, 266, 1076, 277, 416, 434, 1082, 1083, 9, 1089, 695, 696, 11, 276, 508, 1114, 686, 450, 1130, 438, 1200, 1202, 556, 490, 1220, 1219, 677, 505, 1225, 447, 1290, 448, 531, 473, 315, 532, 307, 333, 312, 547, 294, 334, 533, 474, 1296, 1304, 331, 31, 1324, 1335, 342, 572, 610); - protected $ruleToNonTerminal = array(0, 1, 3, 3, 2, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 9, 10, 11, 11, 11, 12, 12, 13, 13, 14, 15, 15, 16, 16, 17, 17, 18, 18, 21, 21, 22, 23, 23, 24, 24, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 29, 29, 30, 30, 32, 34, 34, 28, 36, 36, 33, 38, 38, 35, 35, 37, 37, 39, 39, 31, 40, 40, 41, 43, 44, 44, 45, 45, 46, 46, 48, 47, 47, 47, 47, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 25, 25, 68, 68, 71, 71, 70, 69, 69, 62, 74, 74, 75, 75, 76, 76, 77, 77, 78, 78, 79, 79, 26, 26, 27, 27, 27, 27, 27, 87, 87, 89, 89, 82, 82, 90, 90, 91, 91, 91, 83, 83, 86, 86, 84, 84, 92, 93, 93, 56, 56, 64, 64, 67, 67, 67, 66, 94, 94, 95, 57, 57, 57, 57, 96, 96, 97, 97, 98, 98, 99, 100, 100, 101, 101, 102, 102, 54, 54, 50, 50, 104, 52, 52, 105, 51, 51, 53, 53, 63, 63, 63, 63, 80, 80, 108, 108, 110, 110, 111, 111, 111, 111, 109, 109, 109, 113, 113, 113, 113, 88, 88, 116, 116, 116, 117, 117, 114, 114, 118, 118, 120, 120, 121, 121, 115, 122, 122, 119, 123, 123, 123, 123, 112, 112, 81, 81, 81, 20, 20, 20, 125, 124, 124, 126, 126, 126, 126, 59, 127, 127, 128, 60, 130, 130, 131, 131, 132, 132, 85, 133, 133, 133, 133, 133, 133, 133, 138, 138, 139, 139, 140, 140, 140, 140, 140, 141, 142, 142, 137, 137, 134, 134, 136, 136, 144, 144, 143, 143, 143, 143, 143, 143, 143, 135, 145, 145, 147, 146, 146, 61, 103, 148, 148, 55, 55, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 155, 149, 149, 154, 154, 157, 158, 158, 159, 160, 161, 161, 161, 161, 19, 19, 72, 72, 72, 72, 150, 150, 150, 150, 163, 163, 151, 151, 153, 153, 153, 156, 156, 168, 168, 168, 168, 168, 168, 168, 168, 168, 169, 169, 169, 107, 171, 171, 171, 171, 152, 152, 152, 152, 152, 152, 152, 152, 58, 58, 166, 166, 166, 166, 172, 172, 162, 162, 162, 173, 173, 173, 173, 173, 173, 73, 73, 65, 65, 65, 65, 129, 129, 129, 129, 176, 175, 165, 165, 165, 165, 165, 165, 165, 164, 164, 164, 174, 174, 174, 174, 106, 170, 178, 178, 177, 177, 179, 179, 179, 179, 179, 179, 179, 179, 167, 167, 167, 167, 181, 182, 180, 180, 180, 180, 180, 180, 180, 180, 183, 183, 183, 183); - protected $ruleToLength = array(1, 1, 2, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 2, 1, 3, 4, 1, 2, 0, 1, 1, 1, 1, 1, 3, 5, 4, 3, 4, 2, 3, 1, 1, 7, 6, 2, 3, 1, 2, 3, 1, 2, 3, 1, 1, 3, 1, 3, 1, 2, 2, 3, 1, 3, 2, 3, 1, 3, 3, 2, 0, 1, 1, 1, 1, 1, 3, 7, 10, 5, 7, 9, 5, 3, 3, 3, 3, 3, 3, 1, 2, 5, 7, 9, 6, 5, 6, 3, 2, 1, 1, 1, 0, 2, 1, 3, 8, 0, 4, 2, 1, 3, 0, 1, 0, 1, 0, 1, 3, 1, 1, 1, 8, 9, 7, 8, 7, 6, 8, 0, 2, 0, 2, 1, 2, 1, 2, 1, 1, 1, 0, 2, 0, 2, 0, 2, 2, 1, 3, 1, 4, 1, 4, 1, 1, 4, 2, 1, 3, 3, 3, 4, 4, 5, 0, 2, 4, 3, 1, 1, 7, 0, 2, 1, 3, 3, 4, 1, 4, 0, 2, 5, 0, 2, 6, 0, 2, 0, 3, 1, 2, 1, 1, 2, 0, 1, 3, 0, 2, 1, 1, 1, 1, 6, 8, 6, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 1, 3, 3, 3, 3, 3, 1, 3, 3, 1, 1, 2, 1, 1, 0, 1, 0, 2, 2, 2, 4, 3, 1, 1, 3, 1, 2, 2, 3, 2, 3, 1, 1, 2, 3, 1, 1, 3, 2, 0, 1, 5, 5, 6, 10, 3, 5, 1, 1, 3, 0, 2, 4, 5, 4, 4, 4, 3, 1, 1, 1, 1, 1, 1, 0, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 3, 1, 1, 3, 2, 2, 3, 1, 0, 1, 1, 3, 3, 3, 4, 1, 1, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 5, 4, 3, 4, 4, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 3, 2, 1, 2, 4, 2, 2, 8, 9, 8, 9, 9, 10, 9, 10, 8, 3, 2, 0, 4, 2, 1, 3, 2, 1, 2, 2, 2, 4, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 0, 3, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 5, 3, 3, 4, 1, 1, 3, 1, 1, 1, 1, 1, 3, 2, 3, 0, 1, 1, 3, 1, 1, 1, 1, 1, 3, 1, 1, 4, 4, 1, 4, 4, 0, 1, 1, 1, 3, 3, 1, 4, 2, 2, 1, 3, 1, 4, 4, 3, 3, 3, 3, 1, 3, 1, 1, 3, 1, 1, 4, 1, 1, 1, 3, 1, 1, 2, 1, 3, 4, 3, 2, 0, 2, 2, 1, 2, 1, 1, 1, 4, 3, 3, 3, 3, 6, 3, 1, 1, 2, 1); - protected function initReduceCallbacks() - { - $this->reduceCallbacks = [0 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 1 => function ($stackPos) { - $this->semValue = $this->handleNamespaces($this->semStack[$stackPos - (1 - 1)]); - }, 2 => function ($stackPos) { - if (\is_array($this->semStack[$stackPos - (2 - 2)])) { - $this->semValue = \array_merge($this->semStack[$stackPos - (2 - 1)], $this->semStack[$stackPos - (2 - 2)]); - } else { - $this->semStack[$stackPos - (2 - 1)][] = $this->semStack[$stackPos - (2 - 2)]; - $this->semValue = $this->semStack[$stackPos - (2 - 1)]; - } - }, 3 => function ($stackPos) { - $this->semValue = array(); - }, 4 => function ($stackPos) { - $startAttributes = $this->lookaheadStartAttributes; - if (isset($startAttributes['comments'])) { - $nop = new Stmt\Nop($this->createCommentNopAttributes($startAttributes['comments'])); - } else { - $nop = null; - } +class Php8 extends \PHPUnitPHAR\PhpParser\ParserAbstract +{ + public const YYERRTOK = 256; + public const T_THROW = 257; + public const T_INCLUDE = 258; + public const T_INCLUDE_ONCE = 259; + public const T_EVAL = 260; + public const T_REQUIRE = 261; + public const T_REQUIRE_ONCE = 262; + public const T_LOGICAL_OR = 263; + public const T_LOGICAL_XOR = 264; + public const T_LOGICAL_AND = 265; + public const T_PRINT = 266; + public const T_YIELD = 267; + public const T_DOUBLE_ARROW = 268; + public const T_YIELD_FROM = 269; + public const T_PLUS_EQUAL = 270; + public const T_MINUS_EQUAL = 271; + public const T_MUL_EQUAL = 272; + public const T_DIV_EQUAL = 273; + public const T_CONCAT_EQUAL = 274; + public const T_MOD_EQUAL = 275; + public const T_AND_EQUAL = 276; + public const T_OR_EQUAL = 277; + public const T_XOR_EQUAL = 278; + public const T_SL_EQUAL = 279; + public const T_SR_EQUAL = 280; + public const T_POW_EQUAL = 281; + public const T_COALESCE_EQUAL = 282; + public const T_COALESCE = 283; + public const T_BOOLEAN_OR = 284; + public const T_BOOLEAN_AND = 285; + public const T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG = 286; + public const T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG = 287; + public const T_IS_EQUAL = 288; + public const T_IS_NOT_EQUAL = 289; + public const T_IS_IDENTICAL = 290; + public const T_IS_NOT_IDENTICAL = 291; + public const T_SPACESHIP = 292; + public const T_IS_SMALLER_OR_EQUAL = 293; + public const T_IS_GREATER_OR_EQUAL = 294; + public const T_SL = 295; + public const T_SR = 296; + public const T_INSTANCEOF = 297; + public const T_INC = 298; + public const T_DEC = 299; + public const T_INT_CAST = 300; + public const T_DOUBLE_CAST = 301; + public const T_STRING_CAST = 302; + public const T_ARRAY_CAST = 303; + public const T_OBJECT_CAST = 304; + public const T_BOOL_CAST = 305; + public const T_UNSET_CAST = 306; + public const T_POW = 307; + public const T_NEW = 308; + public const T_CLONE = 309; + public const T_EXIT = 310; + public const T_IF = 311; + public const T_ELSEIF = 312; + public const T_ELSE = 313; + public const T_ENDIF = 314; + public const T_LNUMBER = 315; + public const T_DNUMBER = 316; + public const T_STRING = 317; + public const T_STRING_VARNAME = 318; + public const T_VARIABLE = 319; + public const T_NUM_STRING = 320; + public const T_INLINE_HTML = 321; + public const T_ENCAPSED_AND_WHITESPACE = 322; + public const T_CONSTANT_ENCAPSED_STRING = 323; + public const T_ECHO = 324; + public const T_DO = 325; + public const T_WHILE = 326; + public const T_ENDWHILE = 327; + public const T_FOR = 328; + public const T_ENDFOR = 329; + public const T_FOREACH = 330; + public const T_ENDFOREACH = 331; + public const T_DECLARE = 332; + public const T_ENDDECLARE = 333; + public const T_AS = 334; + public const T_SWITCH = 335; + public const T_MATCH = 336; + public const T_ENDSWITCH = 337; + public const T_CASE = 338; + public const T_DEFAULT = 339; + public const T_BREAK = 340; + public const T_CONTINUE = 341; + public const T_GOTO = 342; + public const T_FUNCTION = 343; + public const T_FN = 344; + public const T_CONST = 345; + public const T_RETURN = 346; + public const T_TRY = 347; + public const T_CATCH = 348; + public const T_FINALLY = 349; + public const T_USE = 350; + public const T_INSTEADOF = 351; + public const T_GLOBAL = 352; + public const T_STATIC = 353; + public const T_ABSTRACT = 354; + public const T_FINAL = 355; + public const T_PRIVATE = 356; + public const T_PROTECTED = 357; + public const T_PUBLIC = 358; + public const T_READONLY = 359; + public const T_VAR = 360; + public const T_UNSET = 361; + public const T_ISSET = 362; + public const T_EMPTY = 363; + public const T_HALT_COMPILER = 364; + public const T_CLASS = 365; + public const T_TRAIT = 366; + public const T_INTERFACE = 367; + public const T_ENUM = 368; + public const T_EXTENDS = 369; + public const T_IMPLEMENTS = 370; + public const T_OBJECT_OPERATOR = 371; + public const T_NULLSAFE_OBJECT_OPERATOR = 372; + public const T_LIST = 373; + public const T_ARRAY = 374; + public const T_CALLABLE = 375; + public const T_CLASS_C = 376; + public const T_TRAIT_C = 377; + public const T_METHOD_C = 378; + public const T_FUNC_C = 379; + public const T_LINE = 380; + public const T_FILE = 381; + public const T_START_HEREDOC = 382; + public const T_END_HEREDOC = 383; + public const T_DOLLAR_OPEN_CURLY_BRACES = 384; + public const T_CURLY_OPEN = 385; + public const T_PAAMAYIM_NEKUDOTAYIM = 386; + public const T_NAMESPACE = 387; + public const T_NS_C = 388; + public const T_DIR = 389; + public const T_NS_SEPARATOR = 390; + public const T_ELLIPSIS = 391; + public const T_NAME_FULLY_QUALIFIED = 392; + public const T_NAME_QUALIFIED = 393; + public const T_NAME_RELATIVE = 394; + public const T_ATTRIBUTE = 395; + protected int $tokenToSymbolMapSize = 396; + protected int $actionTableSize = 1272; + protected int $gotoTableSize = 689; + protected int $invalidSymbol = 168; + protected int $errorSymbol = 1; + protected int $defaultAction = -32766; + protected int $unexpectedTokenRule = 32767; + protected int $YY2TBLSTATE = 437; + protected int $numNonLeafStates = 743; + protected array $symbolToName = array("EOF", "error", "T_THROW", "T_INCLUDE", "T_INCLUDE_ONCE", "T_EVAL", "T_REQUIRE", "T_REQUIRE_ONCE", "','", "T_LOGICAL_OR", "T_LOGICAL_XOR", "T_LOGICAL_AND", "T_PRINT", "T_YIELD", "T_DOUBLE_ARROW", "T_YIELD_FROM", "'='", "T_PLUS_EQUAL", "T_MINUS_EQUAL", "T_MUL_EQUAL", "T_DIV_EQUAL", "T_CONCAT_EQUAL", "T_MOD_EQUAL", "T_AND_EQUAL", "T_OR_EQUAL", "T_XOR_EQUAL", "T_SL_EQUAL", "T_SR_EQUAL", "T_POW_EQUAL", "T_COALESCE_EQUAL", "'?'", "':'", "T_COALESCE", "T_BOOLEAN_OR", "T_BOOLEAN_AND", "'|'", "'^'", "T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG", "T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG", "T_IS_EQUAL", "T_IS_NOT_EQUAL", "T_IS_IDENTICAL", "T_IS_NOT_IDENTICAL", "T_SPACESHIP", "'<'", "T_IS_SMALLER_OR_EQUAL", "'>'", "T_IS_GREATER_OR_EQUAL", "'.'", "T_SL", "T_SR", "'+'", "'-'", "'*'", "'/'", "'%'", "'!'", "T_INSTANCEOF", "'~'", "T_INC", "T_DEC", "T_INT_CAST", "T_DOUBLE_CAST", "T_STRING_CAST", "T_ARRAY_CAST", "T_OBJECT_CAST", "T_BOOL_CAST", "T_UNSET_CAST", "'@'", "T_POW", "'['", "T_NEW", "T_CLONE", "T_EXIT", "T_IF", "T_ELSEIF", "T_ELSE", "T_ENDIF", "T_LNUMBER", "T_DNUMBER", "T_STRING", "T_STRING_VARNAME", "T_VARIABLE", "T_NUM_STRING", "T_INLINE_HTML", "T_ENCAPSED_AND_WHITESPACE", "T_CONSTANT_ENCAPSED_STRING", "T_ECHO", "T_DO", "T_WHILE", "T_ENDWHILE", "T_FOR", "T_ENDFOR", "T_FOREACH", "T_ENDFOREACH", "T_DECLARE", "T_ENDDECLARE", "T_AS", "T_SWITCH", "T_MATCH", "T_ENDSWITCH", "T_CASE", "T_DEFAULT", "T_BREAK", "T_CONTINUE", "T_GOTO", "T_FUNCTION", "T_FN", "T_CONST", "T_RETURN", "T_TRY", "T_CATCH", "T_FINALLY", "T_USE", "T_INSTEADOF", "T_GLOBAL", "T_STATIC", "T_ABSTRACT", "T_FINAL", "T_PRIVATE", "T_PROTECTED", "T_PUBLIC", "T_READONLY", "T_VAR", "T_UNSET", "T_ISSET", "T_EMPTY", "T_HALT_COMPILER", "T_CLASS", "T_TRAIT", "T_INTERFACE", "T_ENUM", "T_EXTENDS", "T_IMPLEMENTS", "T_OBJECT_OPERATOR", "T_NULLSAFE_OBJECT_OPERATOR", "T_LIST", "T_ARRAY", "T_CALLABLE", "T_CLASS_C", "T_TRAIT_C", "T_METHOD_C", "T_FUNC_C", "T_LINE", "T_FILE", "T_START_HEREDOC", "T_END_HEREDOC", "T_DOLLAR_OPEN_CURLY_BRACES", "T_CURLY_OPEN", "T_PAAMAYIM_NEKUDOTAYIM", "T_NAMESPACE", "T_NS_C", "T_DIR", "T_NS_SEPARATOR", "T_ELLIPSIS", "T_NAME_FULLY_QUALIFIED", "T_NAME_QUALIFIED", "T_NAME_RELATIVE", "T_ATTRIBUTE", "';'", "']'", "'('", "')'", "'{'", "'}'", "'`'", "'\"'", "'\$'"); + protected array $tokenToSymbol = array(0, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 56, 166, 168, 167, 55, 168, 168, 161, 162, 53, 51, 8, 52, 48, 54, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 31, 159, 44, 16, 46, 30, 68, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 70, 168, 160, 36, 168, 165, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 163, 35, 164, 58, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 1, 2, 3, 4, 5, 6, 7, 9, 10, 11, 12, 13, 14, 15, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 32, 33, 34, 37, 38, 39, 40, 41, 42, 43, 45, 47, 49, 50, 57, 59, 60, 61, 62, 63, 64, 65, 66, 67, 69, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158); + protected array $action = array(133, 134, 135, 586, 136, 137, 0, 755, 756, 757, 138, 38, 329, -32766, -32766, -32766, -32766, -32766, -32766, 841, 830, -32767, -32767, -32767, -32767, 102, 103, 104, 1116, 1117, 1118, 1115, 1114, 1113, 1119, 749, 748, -32766, 1031, -32766, -32766, -32766, -32766, -32766, -32766, -32766, -32767, -32767, -32767, -32767, -32767, 1252, -32766, -32766, 1331, 758, 1116, 1117, 1118, 1115, 1114, 1113, 1119, 461, 462, 463, 2, 994, 1315, 265, 139, 406, 762, 763, 764, 765, 470, 471, 431, 839, 610, -16, 1350, 23, 293, 819, 766, 767, 768, 769, 770, 771, 772, 773, 774, 775, 795, 587, 796, 797, 798, 799, 787, 788, 347, 348, 790, 791, 776, 777, 778, 780, 781, 782, 358, 822, 823, 824, 825, 826, 588, 783, 784, 589, 590, 945, 807, 805, 806, 818, 802, 803, 839, 830, 591, 592, 801, 593, 594, 595, 596, 597, 598, -328, 36, 250, 35, -194, 804, 599, 600, -193, 140, -85, 133, 134, 135, 586, 136, 137, 1064, 755, 756, 757, 138, 38, 129, -110, -110, -590, -32766, -590, -110, -32766, -32766, -32766, 241, 840, -110, 145, 963, 964, -32766, -32766, -32766, 965, -599, -32766, 485, 749, 748, 959, 1040, -599, -32766, 995, -32766, -32766, -32766, -32766, -32766, -32766, -32766, -32766, -32766, -32766, -32766, -32766, 301, 758, 835, 75, -32766, -32766, -32766, 292, 142, 328, 242, -85, 328, 384, 383, 265, 139, 406, 762, 763, 764, 765, 82, 425, 431, -32766, 328, -32766, -32766, -32766, -32766, 819, 766, 767, 768, 769, 770, 771, 772, 773, 774, 775, 795, 587, 796, 797, 798, 799, 787, 788, 347, 348, 790, 791, 776, 777, 778, 780, 781, 782, 358, 822, 823, 824, 825, 826, 588, 783, 784, 589, 590, 253, 807, 805, 806, 818, 802, 803, 836, 729, 591, 592, 801, 593, 594, 595, 596, 597, 598, -328, 83, 84, 85, -194, 804, 599, 600, -193, 149, 779, 750, 751, 752, 753, 754, 151, 755, 756, 757, 792, 793, 37, 486, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, -599, 275, -599, -32766, -32766, -32766, -32766, -32766, -32766, 312, 1093, 127, 314, 110, 741, 1335, 21, 758, -32766, -32766, -32766, -272, 1334, -32766, -32766, 1092, -32766, -32766, -32766, -32766, -32766, 759, 760, 761, 762, 763, 764, 765, 1108, -32766, 828, -32766, -32766, -550, 560, 1040, 1273, 819, 766, 767, 768, 769, 770, 771, 772, 773, 774, 775, 795, 817, 796, 797, 798, 799, 787, 788, 789, 816, 790, 791, 776, 777, 778, 780, 781, 782, 821, 822, 823, 824, 825, 826, 827, 783, 784, 785, 786, 1037, 807, 805, 806, 818, 802, 803, 749, 748, 794, 800, 801, 808, 809, 811, 810, 812, 813, 1285, 325, -550, -550, 1040, 804, 815, 814, 50, 51, 52, 516, 53, 54, 866, 341, 867, -550, 55, 56, -110, 57, 839, 924, -367, -110, -367, -110, 293, -556, 152, -550, 308, 103, 104, -110, -110, -110, -110, -110, -110, -110, -110, 105, 106, 107, 108, 109, 947, 275, 342, 924, 1252, 719, -32766, -32766, -32766, 58, 59, -549, 372, 110, 60, 838, 61, 247, 248, 62, 63, 64, 65, 66, 67, 68, 69, -32766, 28, 267, 70, 446, 517, 720, 376, -342, 1279, 1280, 518, 359, 839, -548, 391, -546, 1277, 42, 25, 519, 947, 520, 616, 521, 924, 522, 442, 141, 523, 524, 914, 328, 443, 44, 45, 447, 379, 378, -32766, 46, 525, 1027, 1026, 1025, 1028, 370, 340, -549, -549, 444, 1360, 431, 1238, 1361, 527, 528, 529, 839, 914, 364, 1040, 445, -549, -32766, -32766, -32766, 531, 532, 845, 1266, 1267, 1268, 1269, 1263, 1264, 300, -549, -548, -548, -546, -546, 1270, 1265, 292, -32766, 1247, 1246, 1248, 301, 749, 748, 71, -548, -78, -546, 323, 324, 328, -153, -153, -153, 393, -32766, 7, -555, 926, -548, 914, -546, 714, 660, 26, -32766, -153, 832, -153, 866, -153, 867, -153, 382, 383, 28, 268, 1040, 154, 1247, 1246, 1248, 377, 425, 155, -596, 926, 839, 1094, 75, 714, 1277, -596, 963, 964, 328, -547, 156, 526, 158, 292, 1245, 33, 900, 959, -110, -110, -110, 32, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 679, 680, -58, 301, -57, 1238, 124, 924, 749, 748, 1252, 150, 409, 926, 125, 1243, 924, 714, -153, 531, 532, 834, 1266, 1267, 1268, 1269, 1263, 1264, 716, 1154, 1156, -87, -4, 924, 1270, 1265, 1039, 721, -547, -547, -546, 130, 749, 748, 73, -32766, 724, 131, -552, 324, 328, 1245, 144, -547, 1247, 1246, 1248, 159, -32766, -32766, -32766, 1037, -32766, 160, -32766, -554, -32766, -547, 161, -32766, 380, 381, 924, 162, -32766, -32766, -32766, 163, 49, -32766, -32766, -32766, -84, 1040, -78, 1245, -32766, 422, 48, 924, 914, 839, -32766, -32766, -32766, -32766, -32766, -73, -32766, 914, -32766, -72, 731, -32766, -546, -546, 283, -71, -32766, -32766, -32766, -70, -552, -552, -32766, -32766, 914, 385, 386, -546, -32766, 422, -596, -69, -596, 74, -110, -110, -68, -32766, -50, -110, -67, -546, 651, 652, -66, -110, 377, -65, 438, -552, 304, 305, -46, 299, -32766, -18, 148, 963, 964, 274, 302, 303, 526, 914, 284, 375, 730, 530, 959, -110, -110, -110, 132, 980, 733, 301, 923, 714, 75, 128, 914, -32766, 926, 147, 328, -302, 714, 1245, -298, 126, 10, 1063, 281, 282, -32766, -32766, -32766, 285, -32766, 926, -32766, 286, -32766, 714, -4, -32766, 334, 288, 275, 289, -32766, -32766, -32766, 294, 295, -32766, -32766, -32766, 924, 941, 287, 1245, -32766, 422, 110, 689, 146, 830, -32766, -32766, -32766, -32766, -32766, 565, -32766, 666, -32766, 1362, 926, -32766, 705, 839, 714, 1123, -32766, -32766, -32766, -32766, -32766, 667, -32766, -32766, 309, 1245, 661, 926, -32766, 422, 924, 714, -32766, -32766, -32766, 682, -32766, -32766, -32766, 707, -32766, 306, 960, -32766, 313, -32766, 683, 491, -32766, -32766, -32766, -32766, 20, 467, -32766, -32766, 496, 1245, 578, 571, -32766, 422, 301, 649, -32766, -32766, -32766, -511, -32766, -32766, -32766, 0, -32766, 914, 0, -32766, 0, 0, 1037, 0, -32766, -32766, -32766, 1284, 307, 1286, -32766, -32766, 0, -250, -250, -250, -32766, 422, 943, 377, 0, 0, 28, 267, 1040, -32766, 0, -501, 0, 614, 963, 964, 0, 8, 839, 526, 24, 914, 1277, 374, 900, 959, -110, -110, -110, 1274, 838, 283, 40, -584, 0, 41, 738, -249, -249, -249, 739, 28, 268, 377, 850, 287, 858, 905, 1004, 981, 988, 978, 989, 839, 963, 964, 926, 1277, 1238, 526, 714, -250, 903, 976, 900, 959, -110, -110, -110, 1097, 1100, 1101, 1098, 532, 1099, 1266, 1267, 1268, 1269, 1263, 1264, 1105, -583, 1301, 1319, 1353, 654, 1270, 1265, -582, -556, -555, -554, 1238, -553, 694, 926, 73, 34, -495, 714, -249, 324, 328, 1, 29, 30, 39, 532, 43, 1266, 1267, 1268, 1269, 1263, 1264, 47, 72, 76, 77, 78, 79, 1270, 1265, 80, 81, 143, -32766, 153, 157, 245, 695, 73, 1245, 330, 359, 360, 324, 328, 361, -32766, -32766, -32766, 362, -32766, 363, -32766, 364, -32766, 365, 366, -32766, 696, 697, 367, 368, -32766, -32766, -32766, 369, 371, 439, -32766, -32766, 559, 322, -275, -273, -32766, 422, 1247, 1246, 1248, -272, 13, 14, 283, -32766, 15, 16, 18, 408, 487, 488, 495, 498, 499, 500, 501, 505, 506, 507, 514, 576, 700, 1256, 1194, 1275, 1066, 1065, 1046, 1233, 1042, -277, -102, 12, 17, 27, 298, 407, 607, 611, 640, 706, 1198, 0, 1251, 1195, 1332, 0, 373, 715, 718, 722, 723, 725, 726, 727, 728, 732, 717, 0, 735, 901, 1357, 1359, 861, 860, 869, 953, 996, 868, 1358, 952, 950, 951, 954, 1226, 934, 944, 932, 986, 987, 638, 1356, 1313, 1302, 1320, 1329, 0, 1211, 0, 1278, 0, 328); + protected array $actionCheck = array(2, 3, 4, 5, 6, 7, 0, 9, 10, 11, 12, 13, 70, 9, 10, 11, 9, 10, 11, 1, 80, 44, 45, 46, 47, 48, 49, 50, 116, 117, 118, 119, 120, 121, 122, 37, 38, 30, 1, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 1, 9, 10, 1, 57, 116, 117, 118, 119, 120, 121, 122, 129, 130, 131, 8, 31, 1, 71, 72, 73, 74, 75, 76, 77, 134, 135, 80, 82, 1, 31, 85, 8, 30, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 1, 128, 129, 130, 131, 132, 133, 82, 80, 136, 137, 138, 139, 140, 141, 142, 143, 144, 8, 147, 148, 8, 8, 150, 151, 152, 8, 154, 31, 2, 3, 4, 5, 6, 7, 162, 9, 10, 11, 12, 13, 8, 117, 118, 160, 116, 162, 122, 9, 10, 11, 97, 159, 128, 8, 117, 118, 9, 10, 11, 122, 1, 137, 31, 37, 38, 128, 138, 8, 30, 159, 32, 33, 34, 35, 36, 37, 38, 30, 9, 32, 33, 34, 158, 57, 80, 161, 9, 10, 11, 161, 163, 167, 14, 97, 167, 106, 107, 71, 72, 73, 74, 75, 76, 77, 163, 116, 80, 30, 167, 32, 33, 34, 35, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 8, 128, 129, 130, 131, 132, 133, 156, 163, 136, 137, 138, 139, 140, 141, 142, 143, 144, 162, 9, 10, 11, 162, 150, 151, 152, 162, 154, 2, 3, 4, 5, 6, 7, 14, 9, 10, 11, 12, 13, 30, 163, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 160, 57, 162, 9, 10, 11, 9, 10, 11, 8, 159, 14, 8, 69, 163, 1, 101, 57, 9, 10, 11, 162, 8, 116, 30, 1, 32, 33, 34, 35, 36, 71, 72, 73, 74, 75, 76, 77, 123, 30, 80, 32, 33, 70, 85, 138, 1, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 116, 128, 129, 130, 131, 132, 133, 37, 38, 136, 137, 138, 139, 140, 141, 142, 143, 144, 146, 8, 134, 135, 138, 150, 151, 152, 2, 3, 4, 5, 6, 7, 106, 8, 108, 149, 12, 13, 101, 15, 82, 1, 106, 106, 108, 108, 30, 161, 14, 163, 113, 49, 50, 116, 117, 118, 119, 120, 121, 122, 123, 51, 52, 53, 54, 55, 122, 57, 8, 1, 1, 31, 9, 10, 11, 51, 52, 70, 8, 69, 56, 155, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 30, 70, 71, 72, 73, 74, 31, 8, 164, 78, 79, 80, 161, 82, 70, 8, 70, 86, 87, 88, 89, 122, 91, 52, 93, 1, 95, 8, 163, 98, 99, 84, 167, 8, 103, 104, 105, 106, 107, 116, 109, 110, 119, 120, 121, 122, 115, 116, 134, 135, 8, 80, 80, 122, 83, 124, 125, 126, 82, 84, 161, 138, 8, 149, 116, 51, 52, 136, 137, 8, 139, 140, 141, 142, 143, 144, 145, 163, 134, 135, 134, 135, 151, 152, 161, 137, 155, 156, 157, 158, 37, 38, 161, 149, 16, 149, 165, 166, 167, 75, 76, 77, 106, 116, 108, 161, 159, 163, 84, 163, 163, 75, 76, 137, 90, 80, 92, 106, 94, 108, 96, 106, 107, 70, 71, 138, 14, 155, 156, 157, 106, 116, 14, 1, 159, 82, 164, 161, 163, 86, 8, 117, 118, 167, 70, 14, 122, 14, 161, 80, 14, 127, 128, 129, 130, 131, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 75, 76, 16, 158, 16, 122, 16, 1, 37, 38, 1, 101, 102, 159, 16, 116, 1, 163, 164, 136, 137, 156, 139, 140, 141, 142, 143, 144, 163, 59, 60, 31, 0, 1, 151, 152, 137, 31, 134, 135, 70, 16, 37, 38, 161, 74, 31, 16, 70, 166, 167, 80, 16, 149, 155, 156, 157, 16, 87, 88, 89, 116, 91, 16, 93, 161, 95, 163, 16, 98, 106, 107, 1, 16, 103, 104, 105, 16, 70, 74, 109, 110, 31, 138, 31, 80, 115, 116, 70, 1, 84, 82, 87, 88, 89, 124, 91, 31, 93, 84, 95, 31, 31, 98, 134, 135, 161, 31, 103, 104, 105, 31, 134, 135, 109, 110, 84, 106, 107, 149, 115, 116, 160, 31, 162, 154, 117, 118, 31, 124, 31, 122, 31, 163, 111, 112, 31, 128, 106, 31, 108, 163, 134, 135, 31, 113, 137, 31, 31, 117, 118, 31, 134, 135, 122, 84, 31, 149, 31, 127, 128, 129, 130, 131, 31, 159, 31, 158, 31, 163, 161, 163, 84, 74, 159, 31, 167, 35, 163, 80, 35, 163, 150, 1, 35, 35, 87, 88, 89, 35, 91, 159, 93, 35, 95, 163, 164, 98, 35, 37, 57, 37, 103, 104, 105, 37, 37, 74, 109, 110, 1, 38, 30, 80, 115, 116, 69, 77, 70, 80, 87, 88, 89, 124, 91, 89, 93, 96, 95, 83, 159, 98, 80, 82, 163, 82, 103, 104, 105, 74, 85, 100, 109, 110, 114, 80, 90, 159, 115, 116, 1, 163, 87, 88, 89, 94, 91, 124, 93, 92, 95, 132, 128, 98, 132, 137, 100, 102, 103, 104, 105, 74, 97, 97, 109, 110, 97, 80, 81, 153, 115, 116, 158, 113, 87, 88, 89, 149, 91, 124, 93, -1, 95, 84, -1, 98, -1, -1, 116, -1, 103, 104, 105, 146, 133, 146, 109, 110, -1, 100, 101, 102, 115, 116, 154, 106, -1, -1, 70, 71, 138, 124, -1, 149, -1, 153, 117, 118, -1, 149, 82, 122, 149, 84, 86, 149, 127, 128, 129, 130, 131, 160, 155, 161, 159, 161, -1, 159, 159, 100, 101, 102, 159, 70, 71, 106, 160, 30, 159, 159, 159, 159, 159, 159, 159, 82, 117, 118, 159, 86, 122, 122, 163, 164, 159, 159, 127, 128, 129, 130, 131, 159, 159, 159, 159, 137, 159, 139, 140, 141, 142, 143, 144, 159, 161, 160, 160, 160, 160, 151, 152, 161, 161, 161, 161, 122, 161, 80, 159, 161, 163, 161, 163, 164, 166, 167, 161, 161, 161, 161, 137, 161, 139, 140, 141, 142, 143, 144, 161, 161, 161, 161, 161, 161, 151, 152, 161, 161, 161, 74, 161, 161, 161, 116, 161, 80, 161, 161, 161, 166, 167, 161, 87, 88, 89, 161, 91, 161, 93, 161, 95, 161, 161, 98, 137, 138, 161, 161, 103, 104, 105, 161, 161, 161, 109, 110, 161, 163, 162, 162, 115, 116, 155, 156, 157, 162, 162, 162, 161, 124, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, -1, 162, 162, 162, -1, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, -1, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, -1, 165, -1, 166, -1, 167); + protected array $actionBase = array(0, -2, 152, 549, 727, 904, 944, 1022, 390, 497, 560, 922, 500, 710, 710, 766, 710, 472, 701, 847, -60, 305, 305, 847, 305, 783, 783, 783, 666, 666, 666, 666, 700, 700, 860, 860, 892, 828, 794, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 18, 36, 79, 661, 1053, 1059, 1055, 1061, 1051, 1050, 1054, 1056, 1062, 1097, 1098, 839, 1099, 1100, 1096, 1101, 1057, 933, 1052, 1058, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 195, 342, 43, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 495, 495, 495, 578, 578, 354, 173, 978, 943, 978, 978, 978, 978, 978, 978, 978, 978, 203, 665, 339, 164, 164, 7, 7, 7, 7, 7, 50, 369, 704, 704, -23, -23, -23, -23, 448, 877, 501, 260, 368, 434, 54, 540, 640, 640, 316, 316, 512, 512, 316, 316, 316, 442, 442, 252, 252, 252, 252, 318, 469, 599, 358, 304, 823, 53, 53, 53, 53, 823, 823, 823, 823, 854, 1103, 823, 823, 823, 439, 471, 471, 703, 539, 539, 471, 536, -3, -3, 536, 63, -3, 67, 496, 473, 829, 115, 9, 473, 673, 713, 657, 185, 882, 659, 882, 1049, 376, 850, 850, 424, 808, 761, 929, 1074, 1063, 836, 1094, 861, 1095, -66, -58, 748, 1048, 1048, 1048, 1048, 1048, 1048, 1048, 1048, 1048, 1048, 1048, 1104, 402, 1049, 130, 1104, 1104, 1104, 402, 402, 402, 402, 402, 402, 402, 402, 402, 402, 718, 130, 561, 620, 130, 858, 402, 18, 869, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 811, 157, 18, 36, 124, 124, 196, 37, 124, 124, 124, 124, 18, 18, 18, 18, 659, 838, 821, 706, 867, 143, 838, 838, 838, 122, 135, 204, 139, 837, 840, 521, 834, 834, 848, 950, 834, 846, 834, 848, 962, 834, 834, 950, 950, 819, 950, 158, 544, 457, 524, 550, 950, 346, 834, 834, 834, 834, 827, 950, 567, 834, 271, 171, 834, 834, 827, 824, 820, 58, 866, 950, 950, 950, 827, 502, 866, 866, 866, 884, 888, 865, 815, 443, 349, 586, 138, 868, 815, 815, 834, 532, 865, 815, 865, 815, 855, 815, 815, 815, 865, 815, 846, 492, 815, 736, 579, 75, 815, 6, 963, 964, 695, 965, 953, 966, 1007, 967, 970, 1065, 945, 976, 955, 971, 1010, 952, 951, 832, 685, 693, 875, 833, 940, 842, 842, 842, 936, 937, 842, 842, 842, 842, 842, 842, 842, 842, 685, 876, 881, 831, 982, 720, 726, 1038, 852, 1076, 1102, 981, 1040, 972, 880, 731, 1025, 985, 1075, 1009, 989, 991, 1026, 1041, 894, 1042, 1077, 843, 1078, 1079, 891, 995, 1066, 842, 963, 970, 746, 955, 971, 952, 951, 803, 800, 792, 796, 787, 775, 765, 771, 812, 1043, 935, 879, 930, 993, 938, 685, 931, 1019, 942, 1027, 1028, 1064, 871, 841, 932, 1080, 996, 1000, 1001, 1067, 1044, 1068, 883, 1020, 1011, 1029, 874, 1081, 1030, 1031, 1032, 1033, 1069, 1082, 1070, 928, 1071, 895, 851, 1012, 826, 1083, 299, 849, 853, 864, 1006, 466, 980, 1072, 1084, 1085, 1034, 1035, 1036, 1086, 1087, 974, 896, 1023, 856, 1024, 1018, 897, 898, 637, 863, 1045, 844, 845, 859, 643, 656, 1088, 1089, 1090, 975, 822, 835, 899, 900, 1046, 857, 1047, 1091, 658, 910, 742, 1092, 1039, 747, 752, 603, 683, 681, 756, 862, 1073, 878, 825, 870, 1005, 752, 830, 911, 1093, 917, 918, 919, 1037, 920, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 456, 456, 456, 456, 456, 456, 305, 305, 305, 305, 305, 456, 456, 456, 456, 456, 456, 456, 305, 305, 0, 0, 305, 0, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 594, 594, 289, 289, 594, 594, 594, 594, 594, 594, 594, 594, 594, 594, 289, 0, 289, 289, 289, 289, 289, 289, 289, 289, 594, 819, 594, 594, 442, 442, 442, 442, 594, 594, 594, -88, -88, 442, 594, 63, 594, 594, 594, 594, 594, 594, 594, 594, 594, 0, 0, 594, 594, 594, 594, 0, 0, 0, 130, -3, 594, 846, 846, 846, 846, 594, 594, 594, 594, -3, -3, 594, 594, 594, 0, 0, 0, 0, 442, 442, 0, 130, 0, 0, 130, 0, 0, 846, 846, 594, 63, 819, 359, 594, 0, 0, 0, 0, 130, 846, 130, 402, 834, -3, -3, 834, 402, 402, 124, 18, 359, 605, 605, 605, 605, 0, 0, 659, 819, 819, 819, 819, 819, 819, 819, 819, 819, 819, 819, 846, 0, 819, 0, 846, 846, 846, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 846, 0, 0, 950, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 962, 0, 0, 0, 0, 0, 0, 846, 0, 0, 0, 0, 0, 0, 0, 0, 0, 842, 871, 0, 871, 0, 842, 842, 842, 0, 0, 0, 0, 863, 857); + protected array $actionDefault = array(3, 32767, 102, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 100, 32767, 32767, 32767, 32767, 602, 602, 602, 602, 32767, 32767, 254, 102, 32767, 32767, 470, 387, 387, 387, 32767, 32767, 544, 544, 544, 544, 544, 544, 32767, 32767, 32767, 32767, 32767, 32767, 470, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 100, 32767, 32767, 32767, 36, 7, 8, 10, 11, 49, 17, 324, 32767, 32767, 32767, 32767, 102, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 595, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 474, 453, 454, 456, 457, 386, 545, 601, 327, 598, 385, 145, 339, 329, 242, 330, 258, 475, 259, 476, 479, 480, 215, 287, 382, 149, 150, 417, 471, 419, 469, 473, 418, 392, 398, 399, 400, 401, 402, 403, 404, 405, 406, 407, 408, 409, 410, 390, 391, 472, 450, 449, 448, 32767, 32767, 415, 416, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 102, 32767, 420, 389, 423, 421, 422, 439, 440, 437, 438, 441, 32767, 32767, 32767, 32767, 442, 443, 444, 445, 316, 32767, 32767, 366, 364, 424, 316, 111, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 430, 431, 32767, 32767, 32767, 32767, 487, 538, 447, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 102, 32767, 100, 540, 412, 414, 507, 425, 426, 393, 32767, 514, 32767, 102, 32767, 516, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 539, 32767, 546, 546, 32767, 500, 100, 195, 32767, 32767, 515, 32767, 195, 195, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 609, 500, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 32767, 195, 110, 32767, 32767, 32767, 100, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 190, 32767, 268, 270, 102, 563, 195, 32767, 519, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 512, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 500, 435, 138, 32767, 138, 546, 427, 428, 429, 502, 546, 546, 546, 312, 289, 32767, 32767, 32767, 32767, 517, 100, 100, 100, 100, 512, 32767, 32767, 32767, 32767, 111, 486, 99, 99, 99, 99, 99, 103, 101, 32767, 32767, 32767, 32767, 223, 32767, 99, 32767, 101, 101, 32767, 32767, 223, 225, 212, 101, 227, 32767, 567, 568, 223, 101, 227, 227, 227, 247, 247, 489, 318, 101, 99, 101, 101, 197, 318, 318, 32767, 101, 489, 318, 489, 318, 199, 318, 318, 318, 489, 318, 32767, 101, 318, 214, 99, 99, 318, 32767, 32767, 32767, 502, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 222, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 533, 32767, 551, 565, 433, 434, 436, 550, 548, 458, 459, 460, 461, 462, 463, 464, 466, 597, 32767, 506, 32767, 32767, 32767, 338, 32767, 607, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 608, 32767, 546, 32767, 32767, 32767, 32767, 432, 9, 74, 495, 42, 43, 51, 57, 523, 524, 525, 526, 520, 521, 527, 522, 32767, 32767, 528, 573, 32767, 32767, 547, 600, 32767, 32767, 32767, 32767, 32767, 32767, 138, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 533, 32767, 136, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 529, 32767, 32767, 32767, 546, 32767, 32767, 32767, 32767, 314, 311, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 546, 32767, 32767, 32767, 32767, 32767, 291, 32767, 308, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 286, 32767, 32767, 381, 502, 294, 296, 297, 32767, 32767, 32767, 32767, 360, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 152, 152, 3, 3, 341, 152, 152, 152, 341, 341, 152, 341, 341, 341, 152, 152, 152, 152, 152, 152, 280, 185, 262, 265, 247, 247, 152, 352, 152); + protected array $goto = array(196, 196, 1038, 1069, 701, 353, 433, 665, 856, 710, 427, 321, 316, 317, 337, 580, 432, 338, 434, 642, 658, 659, 421, 676, 677, 678, 857, 167, 167, 167, 167, 221, 197, 193, 193, 177, 179, 216, 193, 193, 193, 193, 193, 194, 194, 194, 194, 194, 194, 188, 189, 190, 191, 192, 218, 216, 219, 539, 540, 423, 541, 544, 545, 546, 547, 548, 549, 550, 551, 1140, 168, 169, 170, 195, 171, 172, 173, 166, 174, 175, 176, 178, 215, 217, 220, 238, 243, 244, 255, 257, 258, 259, 260, 261, 262, 263, 264, 269, 270, 271, 272, 278, 290, 291, 319, 320, 428, 429, 430, 585, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 180, 237, 181, 198, 199, 200, 239, 188, 189, 190, 191, 192, 218, 1140, 201, 182, 183, 184, 202, 198, 185, 240, 203, 201, 165, 204, 205, 186, 206, 207, 208, 187, 209, 210, 211, 212, 213, 214, 859, 613, 628, 631, 632, 633, 634, 655, 656, 657, 712, 460, 979, 280, 280, 280, 280, 479, 1321, 1322, 627, 627, 831, 604, 1276, 1276, 1276, 1276, 1276, 1276, 1276, 1276, 1276, 1276, 398, 401, 564, 605, 609, 890, 552, 552, 552, 552, 864, 608, 913, 908, 909, 922, 865, 910, 862, 911, 912, 863, 465, 441, 916, 1041, 1041, 685, 956, 1189, 357, 1033, 1049, 1050, 1091, 1086, 1087, 1088, 1295, 1295, 357, 357, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 698, 357, 357, 833, 917, 357, 918, 1363, 354, 355, 577, 1244, 698, 1244, 1244, 426, 698, 615, 558, 1038, 1038, 1244, 357, 357, 5, 1038, 6, 1038, 1038, 1038, 1038, 1038, 1038, 1038, 1038, 1038, 625, 662, 1038, 1038, 1038, 1038, 1328, 1328, 1328, 1328, 351, 1244, 356, 356, 356, 356, 1244, 1244, 1244, 1244, 1111, 1112, 1244, 1244, 1244, 344, 563, 556, 897, 855, 897, 897, 1336, 554, 1307, 554, 554, 482, 603, 1104, 930, 713, 1000, 554, 931, 484, 396, 946, 345, 344, 946, 511, 704, 872, 1102, 690, 343, 556, 563, 572, 573, 346, 583, 606, 620, 621, 575, 852, 884, 458, 664, 871, 22, 1137, 973, 973, 973, 973, 1044, 1043, 458, 967, 974, 1292, 1292, 558, 1062, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 543, 543, 1047, 1048, 543, 543, 543, 543, 543, 543, 543, 543, 543, 543, 570, 469, 469, 440, 737, 641, 643, 670, 852, 663, 469, 327, 311, 687, 691, 1014, 699, 708, 1010, 686, 1017, 1017, 1220, 948, 1323, 1324, 1221, 1224, 949, 1225, 849, 557, 567, 581, 618, 557, 339, 567, 877, 1237, 399, 464, 451, 451, 451, 451, 405, 1318, 837, 1318, 1318, 251, 251, 472, 584, 473, 474, 1318, 962, 1022, 882, 542, 542, 1354, 1355, 542, 874, 542, 542, 542, 542, 542, 542, 542, 542, 971, 412, 709, 249, 249, 249, 249, 246, 252, 1330, 1330, 1330, 1330, 837, 880, 837, 410, 411, 635, 637, 639, 674, 619, 675, 1075, 414, 415, 416, 1235, 688, 740, 886, 417, 1079, 0, 1314, 349, 435, 984, 885, 873, 1074, 1078, 435, 1122, 503, 0, 504, 1239, 1045, 1045, 982, 852, 510, 0, 0, 669, 1056, 1052, 1053, 0, 451, 451, 451, 451, 451, 451, 451, 451, 451, 451, 451, 935, 1127, 451, 972, 0, 1077, 0, 623, 0, 1316, 1316, 1077, 0, 1019, 0, 0, 326, 276, 326, 326, 0, 876, 1261, 668, 998, 1120, 889, 1346, 1346, 870, 1240, 1241, 1003, 0, 0, 975, 0, 736, 0, 847, 0, 1234, 0, 0, 1346, 555, 1012, 1007, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1242, 1304, 1305, 1349, 1349, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 254, 254); + protected array $gotoCheck = array(42, 42, 73, 127, 73, 97, 66, 66, 26, 9, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 86, 86, 43, 86, 86, 86, 27, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 15, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 83, 49, 23, 23, 23, 23, 178, 178, 178, 108, 108, 6, 130, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 59, 59, 59, 59, 59, 45, 107, 107, 107, 107, 15, 107, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 151, 83, 15, 89, 89, 89, 89, 151, 14, 89, 89, 89, 15, 15, 15, 15, 172, 172, 14, 14, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 7, 14, 14, 7, 65, 14, 65, 14, 97, 97, 174, 73, 7, 73, 73, 13, 7, 13, 14, 73, 73, 73, 14, 14, 46, 73, 46, 73, 73, 73, 73, 73, 73, 73, 73, 73, 56, 56, 73, 73, 73, 73, 9, 9, 9, 9, 181, 73, 24, 24, 24, 24, 73, 73, 73, 73, 144, 144, 73, 73, 73, 170, 76, 76, 25, 25, 25, 25, 183, 19, 14, 19, 19, 84, 8, 8, 73, 8, 103, 19, 73, 84, 62, 9, 170, 170, 9, 8, 8, 35, 8, 14, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 104, 22, 35, 19, 64, 35, 76, 150, 19, 19, 19, 19, 118, 118, 19, 19, 19, 173, 173, 14, 114, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 175, 175, 119, 119, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 48, 149, 149, 113, 48, 48, 48, 120, 22, 48, 149, 171, 171, 48, 48, 48, 48, 48, 48, 116, 107, 107, 79, 79, 180, 180, 79, 79, 79, 79, 18, 9, 9, 2, 2, 9, 29, 9, 39, 14, 9, 9, 23, 23, 23, 23, 28, 130, 12, 130, 130, 5, 5, 9, 9, 9, 9, 130, 92, 110, 9, 158, 158, 9, 9, 158, 37, 158, 158, 158, 158, 158, 158, 158, 158, 93, 93, 93, 5, 5, 5, 5, 5, 5, 130, 130, 130, 130, 12, 9, 12, 82, 82, 85, 85, 85, 82, 80, 82, 129, 82, 82, 82, 162, 82, 99, 41, 82, 132, -1, 130, 82, 117, 96, 16, 16, 16, 16, 117, 147, 155, -1, 155, 20, 117, 117, 16, 22, 155, -1, -1, 117, 117, 117, 117, -1, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 17, 17, 23, 16, -1, 130, -1, 17, -1, 130, 130, 130, -1, 17, -1, -1, 24, 24, 24, 24, -1, 17, 20, 17, 17, 16, 16, 184, 184, 17, 20, 20, 50, -1, -1, 50, -1, 50, -1, 20, -1, 17, -1, -1, 184, 50, 50, 50, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 20, 20, 20, 184, 184, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 5, 5); + protected array $gotoBase = array(0, 0, -287, 0, 0, 446, 165, 242, 315, -11, 0, 0, 145, -75, -73, -187, 56, 75, 114, 53, 124, 0, 72, 173, 294, 310, 4, 22, 103, 133, 0, 0, 0, 0, 0, -35, 0, 121, 0, 109, 0, 60, -1, 3, 0, 179, -467, 0, -319, 157, 563, 0, 0, 0, 0, 0, 245, 0, 0, 152, 0, 0, 289, 0, 113, 239, -235, 0, 0, 0, 0, 0, 0, -5, 0, 0, -36, 0, 0, 8, 147, -196, -7, -106, -150, 7, -702, 0, 0, -59, 0, 0, 123, 164, 0, 0, 65, -481, 0, 92, 0, 0, 0, 292, 308, 0, 0, 175, -58, 0, 83, 0, 0, 120, 97, 0, 132, 235, 82, 99, 111, 0, 0, 0, 0, 0, 0, 1, 0, 119, 178, 0, 61, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 29, 0, 0, 70, 0, 363, 112, -49, 0, 0, 0, 18, 0, 0, 216, 0, 0, 0, 128, 0, 0, 0, 0, 0, 0, 0, 10, 84, -6, 127, 230, 141, 0, 0, -123, 0, 46, 265, 0, 286, 260, 0, 0); + protected array $gotoDefault = array(-32768, 515, 744, 4, 745, 939, 820, 829, 601, 533, 711, 350, 629, 424, 1312, 915, 1126, 582, 848, 1253, 1227, 459, 851, 332, 734, 927, 898, 899, 402, 388, 394, 400, 653, 630, 497, 883, 455, 875, 489, 878, 454, 887, 164, 420, 513, 891, 3, 894, 561, 925, 977, 389, 902, 390, 681, 904, 566, 906, 907, 397, 403, 404, 1131, 574, 626, 919, 256, 568, 920, 387, 921, 929, 392, 395, 692, 468, 508, 502, 413, 1106, 569, 612, 650, 448, 476, 624, 636, 622, 483, 436, 418, 331, 961, 969, 490, 466, 983, 352, 991, 742, 1139, 644, 492, 999, 645, 1006, 1009, 534, 535, 481, 1021, 273, 1024, 493, 19, 671, 1035, 1036, 672, 646, 1058, 647, 673, 648, 1060, 475, 602, 1068, 456, 1076, 1300, 457, 1080, 266, 1083, 279, 419, 437, 1089, 1090, 9, 1096, 702, 703, 11, 277, 512, 1121, 693, 453, 1138, 452, 1208, 1210, 562, 494, 1228, 480, 296, 1231, 684, 509, 1236, 449, 1303, 450, 536, 477, 318, 537, 1347, 310, 335, 315, 553, 297, 336, 538, 478, 1309, 1317, 333, 31, 1337, 1348, 579, 617); + protected array $ruleToNonTerminal = array(0, 1, 3, 3, 2, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 9, 10, 11, 11, 11, 12, 12, 13, 13, 14, 15, 15, 16, 16, 17, 17, 18, 18, 21, 21, 22, 23, 23, 24, 24, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 29, 29, 30, 30, 32, 34, 34, 28, 36, 36, 33, 38, 38, 35, 35, 37, 37, 39, 39, 31, 40, 40, 41, 43, 44, 44, 45, 45, 46, 46, 48, 47, 47, 47, 47, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 25, 25, 50, 69, 69, 72, 72, 71, 70, 70, 63, 75, 75, 76, 76, 77, 77, 78, 78, 79, 79, 80, 80, 26, 26, 27, 27, 27, 27, 27, 88, 88, 90, 90, 83, 83, 91, 91, 92, 92, 92, 84, 84, 87, 87, 85, 85, 93, 94, 94, 57, 57, 65, 65, 68, 68, 68, 67, 95, 95, 96, 58, 58, 58, 58, 97, 97, 98, 98, 99, 99, 100, 101, 101, 102, 102, 103, 103, 55, 55, 51, 51, 105, 53, 53, 106, 52, 52, 54, 54, 64, 64, 64, 64, 81, 81, 109, 109, 111, 111, 112, 112, 112, 112, 110, 110, 110, 114, 114, 114, 114, 89, 89, 117, 117, 117, 118, 118, 115, 115, 119, 119, 121, 121, 122, 122, 116, 123, 123, 120, 124, 124, 124, 124, 113, 113, 82, 82, 82, 20, 20, 20, 126, 125, 125, 127, 127, 127, 127, 60, 128, 128, 129, 61, 131, 131, 132, 132, 133, 133, 86, 134, 134, 134, 134, 134, 134, 134, 139, 139, 140, 140, 141, 141, 141, 141, 141, 142, 143, 143, 138, 138, 135, 135, 137, 137, 145, 145, 144, 144, 144, 144, 144, 144, 144, 136, 146, 146, 148, 147, 147, 62, 104, 149, 149, 56, 56, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 156, 158, 158, 159, 150, 150, 155, 155, 160, 161, 161, 162, 163, 164, 164, 164, 164, 19, 19, 73, 73, 73, 73, 151, 151, 151, 151, 166, 166, 152, 152, 154, 154, 154, 157, 157, 172, 172, 172, 172, 172, 172, 172, 172, 172, 173, 173, 173, 108, 175, 175, 175, 175, 153, 153, 153, 153, 153, 153, 153, 153, 59, 59, 169, 169, 169, 169, 169, 176, 176, 165, 165, 165, 165, 177, 177, 177, 177, 177, 177, 74, 74, 66, 66, 66, 66, 130, 130, 130, 130, 180, 179, 168, 168, 168, 168, 168, 168, 168, 167, 167, 167, 178, 178, 178, 178, 107, 174, 182, 182, 181, 181, 183, 183, 183, 183, 183, 183, 183, 183, 171, 171, 171, 171, 170, 185, 184, 184, 184, 184, 184, 184, 184, 184, 186, 186, 186, 186); + protected array $ruleToLength = array(1, 1, 2, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 2, 1, 3, 4, 1, 2, 0, 1, 1, 1, 1, 4, 3, 5, 4, 3, 4, 2, 3, 1, 1, 7, 6, 2, 3, 1, 2, 3, 1, 2, 3, 1, 1, 3, 1, 3, 1, 2, 2, 3, 1, 3, 2, 3, 1, 3, 3, 2, 0, 1, 1, 1, 1, 1, 3, 7, 10, 5, 7, 9, 5, 3, 3, 3, 3, 3, 3, 1, 2, 5, 7, 9, 6, 5, 6, 3, 2, 1, 1, 1, 1, 0, 2, 1, 3, 8, 0, 4, 2, 1, 3, 0, 1, 0, 1, 0, 1, 3, 1, 1, 1, 8, 9, 7, 8, 7, 6, 8, 0, 2, 0, 2, 1, 2, 1, 2, 1, 1, 1, 0, 2, 0, 2, 0, 2, 2, 1, 3, 1, 4, 1, 4, 1, 1, 4, 2, 1, 3, 3, 3, 4, 4, 5, 0, 2, 4, 3, 1, 1, 7, 0, 2, 1, 3, 3, 4, 1, 4, 0, 2, 5, 0, 2, 6, 0, 2, 0, 3, 1, 2, 1, 1, 2, 0, 1, 3, 0, 2, 1, 1, 1, 1, 6, 8, 6, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 1, 3, 3, 3, 3, 3, 1, 3, 3, 1, 1, 2, 1, 1, 0, 1, 0, 2, 2, 2, 4, 3, 1, 1, 3, 1, 2, 2, 3, 2, 3, 1, 1, 2, 3, 1, 1, 3, 2, 0, 1, 5, 5, 6, 10, 3, 5, 1, 1, 3, 0, 2, 4, 5, 4, 4, 4, 3, 1, 1, 1, 1, 1, 1, 0, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 3, 1, 1, 3, 2, 2, 3, 1, 0, 1, 1, 3, 3, 3, 4, 4, 1, 1, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 5, 4, 3, 4, 4, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 3, 2, 1, 2, 4, 2, 2, 8, 9, 8, 9, 9, 10, 9, 10, 8, 3, 2, 2, 1, 1, 0, 4, 2, 1, 3, 2, 1, 2, 2, 2, 4, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 0, 3, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 5, 3, 3, 4, 1, 1, 3, 1, 1, 1, 1, 1, 3, 2, 3, 0, 1, 1, 3, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 4, 4, 1, 4, 4, 0, 1, 1, 1, 3, 3, 1, 4, 2, 2, 1, 3, 1, 4, 4, 3, 3, 3, 3, 1, 3, 1, 1, 3, 1, 1, 4, 1, 1, 1, 3, 1, 1, 2, 1, 3, 4, 3, 2, 0, 2, 2, 1, 2, 1, 1, 1, 4, 3, 3, 3, 3, 6, 3, 1, 1, 2, 1); + protected function initReduceCallbacks(): void + { + $this->reduceCallbacks = [0 => null, 1 => static function ($self, $stackPos) { + $self->semValue = $self->handleNamespaces($self->semStack[$stackPos - (1 - 1)]); + }, 2 => static function ($self, $stackPos) { + if ($self->semStack[$stackPos - (2 - 2)] !== null) { + $self->semStack[$stackPos - (2 - 1)][] = $self->semStack[$stackPos - (2 - 2)]; + } + $self->semValue = $self->semStack[$stackPos - (2 - 1)]; + }, 3 => static function ($self, $stackPos) { + $self->semValue = array(); + }, 4 => static function ($self, $stackPos) { + $nop = $self->maybeCreateZeroLengthNop($self->tokenPos); if ($nop !== null) { - $this->semStack[$stackPos - (1 - 1)][] = $nop; - } - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 5 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 6 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 7 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 8 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 9 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 10 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 11 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 12 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 13 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 14 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 15 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 16 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 17 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 18 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 19 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 20 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 21 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 22 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 23 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 24 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 25 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 26 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 27 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 28 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 29 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 30 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 31 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 32 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 33 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 34 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 35 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 36 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 37 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 38 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 39 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 40 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 41 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 42 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 43 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 44 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 45 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 46 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 47 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 48 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 49 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 50 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 51 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 52 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 53 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 54 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 55 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 56 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 57 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 58 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 59 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 60 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 61 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 62 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 63 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 64 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 65 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 66 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 67 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 68 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 69 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 70 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 71 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 72 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 73 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 74 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 75 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 76 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 77 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 78 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 79 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 80 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 81 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 82 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 83 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 84 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 85 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 86 => function ($stackPos) { - $this->semValue = new Node\Identifier($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 87 => function ($stackPos) { - $this->semValue = new Node\Identifier($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 88 => function ($stackPos) { - $this->semValue = new Node\Identifier($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 89 => function ($stackPos) { - $this->semValue = new Node\Identifier($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 90 => function ($stackPos) { - $this->semValue = new Name($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 91 => function ($stackPos) { - $this->semValue = new Name($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 92 => function ($stackPos) { - $this->semValue = new Name($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 93 => function ($stackPos) { - $this->semValue = new Name($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 94 => function ($stackPos) { - $this->semValue = new Name($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 95 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 96 => function ($stackPos) { - $this->semValue = new Name(\substr($this->semStack[$stackPos - (1 - 1)], 1), $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 97 => function ($stackPos) { - $this->semValue = new Expr\Variable(\substr($this->semStack[$stackPos - (1 - 1)], 1), $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 98 => function ($stackPos) { + $self->semStack[$stackPos - (1 - 1)][] = $nop; + } + $self->semValue = $self->semStack[$stackPos - (1 - 1)]; + }, 5 => null, 6 => null, 7 => null, 8 => null, 9 => null, 10 => null, 11 => null, 12 => null, 13 => null, 14 => null, 15 => null, 16 => null, 17 => null, 18 => null, 19 => null, 20 => null, 21 => null, 22 => null, 23 => null, 24 => null, 25 => null, 26 => null, 27 => null, 28 => null, 29 => null, 30 => null, 31 => null, 32 => null, 33 => null, 34 => null, 35 => null, 36 => null, 37 => null, 38 => null, 39 => null, 40 => null, 41 => null, 42 => null, 43 => null, 44 => null, 45 => null, 46 => null, 47 => null, 48 => null, 49 => null, 50 => null, 51 => null, 52 => null, 53 => null, 54 => null, 55 => null, 56 => null, 57 => null, 58 => null, 59 => null, 60 => null, 61 => null, 62 => null, 63 => null, 64 => null, 65 => null, 66 => null, 67 => null, 68 => null, 69 => null, 70 => null, 71 => null, 72 => null, 73 => null, 74 => null, 75 => null, 76 => static function ($self, $stackPos) { + $self->semValue = $self->semStack[$stackPos - (1 - 1)]; + if ($self->semValue === "emitError(new Error('Cannot use "getAttributes($self->tokenStartStack[$stackPos - (1 - 1)], $self->tokenEndStack[$stackPos]))); + } + }, 77 => null, 78 => null, 79 => null, 80 => null, 81 => null, 82 => null, 83 => null, 84 => null, 85 => static function ($self, $stackPos) { + $self->semValue = new Node\Identifier($self->semStack[$stackPos - (1 - 1)], $self->getAttributes($self->tokenStartStack[$stackPos - (1 - 1)], $self->tokenEndStack[$stackPos])); + }, 86 => static function ($self, $stackPos) { + $self->semValue = new Node\Identifier($self->semStack[$stackPos - (1 - 1)], $self->getAttributes($self->tokenStartStack[$stackPos - (1 - 1)], $self->tokenEndStack[$stackPos])); + }, 87 => static function ($self, $stackPos) { + $self->semValue = new Node\Identifier($self->semStack[$stackPos - (1 - 1)], $self->getAttributes($self->tokenStartStack[$stackPos - (1 - 1)], $self->tokenEndStack[$stackPos])); + }, 88 => static function ($self, $stackPos) { + $self->semValue = new Node\Identifier($self->semStack[$stackPos - (1 - 1)], $self->getAttributes($self->tokenStartStack[$stackPos - (1 - 1)], $self->tokenEndStack[$stackPos])); + }, 89 => static function ($self, $stackPos) { + $self->semValue = new Name($self->semStack[$stackPos - (1 - 1)], $self->getAttributes($self->tokenStartStack[$stackPos - (1 - 1)], $self->tokenEndStack[$stackPos])); + }, 90 => static function ($self, $stackPos) { + $self->semValue = new Name($self->semStack[$stackPos - (1 - 1)], $self->getAttributes($self->tokenStartStack[$stackPos - (1 - 1)], $self->tokenEndStack[$stackPos])); + }, 91 => static function ($self, $stackPos) { + $self->semValue = new Name($self->semStack[$stackPos - (1 - 1)], $self->getAttributes($self->tokenStartStack[$stackPos - (1 - 1)], $self->tokenEndStack[$stackPos])); + }, 92 => static function ($self, $stackPos) { + $self->semValue = new Name($self->semStack[$stackPos - (1 - 1)], $self->getAttributes($self->tokenStartStack[$stackPos - (1 - 1)], $self->tokenEndStack[$stackPos])); + }, 93 => static function ($self, $stackPos) { + $self->semValue = new Name($self->semStack[$stackPos - (1 - 1)], $self->getAttributes($self->tokenStartStack[$stackPos - (1 - 1)], $self->tokenEndStack[$stackPos])); + }, 94 => null, 95 => static function ($self, $stackPos) { + $self->semValue = new Name(substr($self->semStack[$stackPos - (1 - 1)], 1), $self->getAttributes($self->tokenStartStack[$stackPos - (1 - 1)], $self->tokenEndStack[$stackPos])); + }, 96 => static function ($self, $stackPos) { + $self->semValue = new Expr\Variable(substr($self->semStack[$stackPos - (1 - 1)], 1), $self->getAttributes($self->tokenStartStack[$stackPos - (1 - 1)], $self->tokenEndStack[$stackPos])); + }, 97 => static function ($self, $stackPos) { /* nothing */ - }, 99 => function ($stackPos) { + }, 98 => static function ($self, $stackPos) { /* nothing */ - }, 100 => function ($stackPos) { + }, 99 => static function ($self, $stackPos) { /* nothing */ - }, 101 => function ($stackPos) { - $this->emitError(new Error('A trailing comma is not allowed here', $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes)); - }, 102 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 103 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 104 => function ($stackPos) { - $this->semValue = new Node\Attribute($this->semStack[$stackPos - (1 - 1)], [], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 105 => function ($stackPos) { - $this->semValue = new Node\Attribute($this->semStack[$stackPos - (2 - 1)], $this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 106 => function ($stackPos) { - $this->semValue = array($this->semStack[$stackPos - (1 - 1)]); - }, 107 => function ($stackPos) { - $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; - $this->semValue = $this->semStack[$stackPos - (3 - 1)]; - }, 108 => function ($stackPos) { - $this->semValue = new Node\AttributeGroup($this->semStack[$stackPos - (4 - 2)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 109 => function ($stackPos) { - $this->semValue = array($this->semStack[$stackPos - (1 - 1)]); - }, 110 => function ($stackPos) { - $this->semStack[$stackPos - (2 - 1)][] = $this->semStack[$stackPos - (2 - 2)]; - $this->semValue = $this->semStack[$stackPos - (2 - 1)]; - }, 111 => function ($stackPos) { - $this->semValue = []; - }, 112 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 113 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 114 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 115 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 116 => function ($stackPos) { - $this->semValue = new Stmt\HaltCompiler($this->lexer->handleHaltCompiler(), $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 117 => function ($stackPos) { - $this->semValue = new Stmt\Namespace_($this->semStack[$stackPos - (3 - 2)], null, $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - $this->semValue->setAttribute('kind', Stmt\Namespace_::KIND_SEMICOLON); - $this->checkNamespace($this->semValue); - }, 118 => function ($stackPos) { - $this->semValue = new Stmt\Namespace_($this->semStack[$stackPos - (5 - 2)], $this->semStack[$stackPos - (5 - 4)], $this->startAttributeStack[$stackPos - (5 - 1)] + $this->endAttributes); - $this->semValue->setAttribute('kind', Stmt\Namespace_::KIND_BRACED); - $this->checkNamespace($this->semValue); - }, 119 => function ($stackPos) { - $this->semValue = new Stmt\Namespace_(null, $this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - $this->semValue->setAttribute('kind', Stmt\Namespace_::KIND_BRACED); - $this->checkNamespace($this->semValue); - }, 120 => function ($stackPos) { - $this->semValue = new Stmt\Use_($this->semStack[$stackPos - (3 - 2)], Stmt\Use_::TYPE_NORMAL, $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 121 => function ($stackPos) { - $this->semValue = new Stmt\Use_($this->semStack[$stackPos - (4 - 3)], $this->semStack[$stackPos - (4 - 2)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 122 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (2 - 1)]; - }, 123 => function ($stackPos) { - $this->semValue = new Stmt\Const_($this->semStack[$stackPos - (3 - 2)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 124 => function ($stackPos) { - $this->semValue = Stmt\Use_::TYPE_FUNCTION; - }, 125 => function ($stackPos) { - $this->semValue = Stmt\Use_::TYPE_CONSTANT; - }, 126 => function ($stackPos) { - $this->semValue = new Stmt\GroupUse($this->semStack[$stackPos - (7 - 3)], $this->semStack[$stackPos - (7 - 6)], $this->semStack[$stackPos - (7 - 2)], $this->startAttributeStack[$stackPos - (7 - 1)] + $this->endAttributes); - }, 127 => function ($stackPos) { - $this->semValue = new Stmt\GroupUse($this->semStack[$stackPos - (6 - 2)], $this->semStack[$stackPos - (6 - 5)], Stmt\Use_::TYPE_UNKNOWN, $this->startAttributeStack[$stackPos - (6 - 1)] + $this->endAttributes); - }, 128 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (2 - 1)]; - }, 129 => function ($stackPos) { - $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; - $this->semValue = $this->semStack[$stackPos - (3 - 1)]; - }, 130 => function ($stackPos) { - $this->semValue = array($this->semStack[$stackPos - (1 - 1)]); - }, 131 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (2 - 1)]; - }, 132 => function ($stackPos) { - $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; - $this->semValue = $this->semStack[$stackPos - (3 - 1)]; - }, 133 => function ($stackPos) { - $this->semValue = array($this->semStack[$stackPos - (1 - 1)]); - }, 134 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (2 - 1)]; - }, 135 => function ($stackPos) { - $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; - $this->semValue = $this->semStack[$stackPos - (3 - 1)]; - }, 136 => function ($stackPos) { - $this->semValue = array($this->semStack[$stackPos - (1 - 1)]); - }, 137 => function ($stackPos) { - $this->semValue = new Stmt\UseUse($this->semStack[$stackPos - (1 - 1)], null, Stmt\Use_::TYPE_UNKNOWN, $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - $this->checkUseUse($this->semValue, $stackPos - (1 - 1)); - }, 138 => function ($stackPos) { - $this->semValue = new Stmt\UseUse($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], Stmt\Use_::TYPE_UNKNOWN, $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - $this->checkUseUse($this->semValue, $stackPos - (3 - 3)); - }, 139 => function ($stackPos) { - $this->semValue = new Stmt\UseUse($this->semStack[$stackPos - (1 - 1)], null, Stmt\Use_::TYPE_UNKNOWN, $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - $this->checkUseUse($this->semValue, $stackPos - (1 - 1)); - }, 140 => function ($stackPos) { - $this->semValue = new Stmt\UseUse($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], Stmt\Use_::TYPE_UNKNOWN, $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - $this->checkUseUse($this->semValue, $stackPos - (3 - 3)); - }, 141 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - $this->semValue->type = Stmt\Use_::TYPE_NORMAL; - }, 142 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (2 - 2)]; - $this->semValue->type = $this->semStack[$stackPos - (2 - 1)]; - }, 143 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (2 - 1)]; - }, 144 => function ($stackPos) { - $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; - $this->semValue = $this->semStack[$stackPos - (3 - 1)]; - }, 145 => function ($stackPos) { - $this->semValue = array($this->semStack[$stackPos - (1 - 1)]); - }, 146 => function ($stackPos) { - $this->semValue = new Node\Const_($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 147 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (2 - 1)]; - }, 148 => function ($stackPos) { - $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; - $this->semValue = $this->semStack[$stackPos - (3 - 1)]; - }, 149 => function ($stackPos) { - $this->semValue = array($this->semStack[$stackPos - (1 - 1)]); - }, 150 => function ($stackPos) { - $this->semValue = new Node\Const_(new Node\Identifier($this->semStack[$stackPos - (3 - 1)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributeStack[$stackPos - (3 - 1)]), $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 151 => function ($stackPos) { - $this->semValue = new Node\Const_(new Node\Identifier($this->semStack[$stackPos - (3 - 1)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributeStack[$stackPos - (3 - 1)]), $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 152 => function ($stackPos) { - if (\is_array($this->semStack[$stackPos - (2 - 2)])) { - $this->semValue = \array_merge($this->semStack[$stackPos - (2 - 1)], $this->semStack[$stackPos - (2 - 2)]); - } else { - $this->semStack[$stackPos - (2 - 1)][] = $this->semStack[$stackPos - (2 - 2)]; - $this->semValue = $this->semStack[$stackPos - (2 - 1)]; - } - }, 153 => function ($stackPos) { - $this->semValue = array(); - }, 154 => function ($stackPos) { - $startAttributes = $this->lookaheadStartAttributes; - if (isset($startAttributes['comments'])) { - $nop = new Stmt\Nop($this->createCommentNopAttributes($startAttributes['comments'])); - } else { - $nop = null; - } + }, 100 => static function ($self, $stackPos) { + $self->emitError(new Error('A trailing comma is not allowed here', $self->getAttributes($self->tokenStartStack[$stackPos - (1 - 1)], $self->tokenEndStack[$stackPos]))); + }, 101 => null, 102 => null, 103 => static function ($self, $stackPos) { + $self->semValue = new Node\Attribute($self->semStack[$stackPos - (1 - 1)], [], $self->getAttributes($self->tokenStartStack[$stackPos - (1 - 1)], $self->tokenEndStack[$stackPos])); + }, 104 => static function ($self, $stackPos) { + $self->semValue = new Node\Attribute($self->semStack[$stackPos - (2 - 1)], $self->semStack[$stackPos - (2 - 2)], $self->getAttributes($self->tokenStartStack[$stackPos - (2 - 1)], $self->tokenEndStack[$stackPos])); + }, 105 => static function ($self, $stackPos) { + $self->semValue = array($self->semStack[$stackPos - (1 - 1)]); + }, 106 => static function ($self, $stackPos) { + $self->semStack[$stackPos - (3 - 1)][] = $self->semStack[$stackPos - (3 - 3)]; + $self->semValue = $self->semStack[$stackPos - (3 - 1)]; + }, 107 => static function ($self, $stackPos) { + $self->semValue = new Node\AttributeGroup($self->semStack[$stackPos - (4 - 2)], $self->getAttributes($self->tokenStartStack[$stackPos - (4 - 1)], $self->tokenEndStack[$stackPos])); + }, 108 => static function ($self, $stackPos) { + $self->semValue = array($self->semStack[$stackPos - (1 - 1)]); + }, 109 => static function ($self, $stackPos) { + $self->semStack[$stackPos - (2 - 1)][] = $self->semStack[$stackPos - (2 - 2)]; + $self->semValue = $self->semStack[$stackPos - (2 - 1)]; + }, 110 => static function ($self, $stackPos) { + $self->semValue = []; + }, 111 => null, 112 => null, 113 => null, 114 => null, 115 => static function ($self, $stackPos) { + $self->semValue = new Stmt\HaltCompiler($self->handleHaltCompiler(), $self->getAttributes($self->tokenStartStack[$stackPos - (4 - 1)], $self->tokenEndStack[$stackPos])); + }, 116 => static function ($self, $stackPos) { + $self->semValue = new Stmt\Namespace_($self->semStack[$stackPos - (3 - 2)], null, $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + $self->semValue->setAttribute('kind', Stmt\Namespace_::KIND_SEMICOLON); + $self->checkNamespace($self->semValue); + }, 117 => static function ($self, $stackPos) { + $self->semValue = new Stmt\Namespace_($self->semStack[$stackPos - (5 - 2)], $self->semStack[$stackPos - (5 - 4)], $self->getAttributes($self->tokenStartStack[$stackPos - (5 - 1)], $self->tokenEndStack[$stackPos])); + $self->semValue->setAttribute('kind', Stmt\Namespace_::KIND_BRACED); + $self->checkNamespace($self->semValue); + }, 118 => static function ($self, $stackPos) { + $self->semValue = new Stmt\Namespace_(null, $self->semStack[$stackPos - (4 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (4 - 1)], $self->tokenEndStack[$stackPos])); + $self->semValue->setAttribute('kind', Stmt\Namespace_::KIND_BRACED); + $self->checkNamespace($self->semValue); + }, 119 => static function ($self, $stackPos) { + $self->semValue = new Stmt\Use_($self->semStack[$stackPos - (3 - 2)], Stmt\Use_::TYPE_NORMAL, $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 120 => static function ($self, $stackPos) { + $self->semValue = new Stmt\Use_($self->semStack[$stackPos - (4 - 3)], $self->semStack[$stackPos - (4 - 2)], $self->getAttributes($self->tokenStartStack[$stackPos - (4 - 1)], $self->tokenEndStack[$stackPos])); + }, 121 => null, 122 => static function ($self, $stackPos) { + $self->semValue = new Stmt\Const_($self->semStack[$stackPos - (3 - 2)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 123 => static function ($self, $stackPos) { + $self->semValue = Stmt\Use_::TYPE_FUNCTION; + }, 124 => static function ($self, $stackPos) { + $self->semValue = Stmt\Use_::TYPE_CONSTANT; + }, 125 => static function ($self, $stackPos) { + $self->semValue = new Stmt\GroupUse($self->semStack[$stackPos - (7 - 3)], $self->semStack[$stackPos - (7 - 6)], $self->semStack[$stackPos - (7 - 2)], $self->getAttributes($self->tokenStartStack[$stackPos - (7 - 1)], $self->tokenEndStack[$stackPos])); + }, 126 => static function ($self, $stackPos) { + $self->semValue = new Stmt\GroupUse($self->semStack[$stackPos - (6 - 2)], $self->semStack[$stackPos - (6 - 5)], Stmt\Use_::TYPE_UNKNOWN, $self->getAttributes($self->tokenStartStack[$stackPos - (6 - 1)], $self->tokenEndStack[$stackPos])); + }, 127 => null, 128 => static function ($self, $stackPos) { + $self->semStack[$stackPos - (3 - 1)][] = $self->semStack[$stackPos - (3 - 3)]; + $self->semValue = $self->semStack[$stackPos - (3 - 1)]; + }, 129 => static function ($self, $stackPos) { + $self->semValue = array($self->semStack[$stackPos - (1 - 1)]); + }, 130 => null, 131 => static function ($self, $stackPos) { + $self->semStack[$stackPos - (3 - 1)][] = $self->semStack[$stackPos - (3 - 3)]; + $self->semValue = $self->semStack[$stackPos - (3 - 1)]; + }, 132 => static function ($self, $stackPos) { + $self->semValue = array($self->semStack[$stackPos - (1 - 1)]); + }, 133 => null, 134 => static function ($self, $stackPos) { + $self->semStack[$stackPos - (3 - 1)][] = $self->semStack[$stackPos - (3 - 3)]; + $self->semValue = $self->semStack[$stackPos - (3 - 1)]; + }, 135 => static function ($self, $stackPos) { + $self->semValue = array($self->semStack[$stackPos - (1 - 1)]); + }, 136 => static function ($self, $stackPos) { + $self->semValue = new Node\UseItem($self->semStack[$stackPos - (1 - 1)], null, Stmt\Use_::TYPE_UNKNOWN, $self->getAttributes($self->tokenStartStack[$stackPos - (1 - 1)], $self->tokenEndStack[$stackPos])); + $self->checkUseUse($self->semValue, $stackPos - (1 - 1)); + }, 137 => static function ($self, $stackPos) { + $self->semValue = new Node\UseItem($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], Stmt\Use_::TYPE_UNKNOWN, $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + $self->checkUseUse($self->semValue, $stackPos - (3 - 3)); + }, 138 => static function ($self, $stackPos) { + $self->semValue = new Node\UseItem($self->semStack[$stackPos - (1 - 1)], null, Stmt\Use_::TYPE_UNKNOWN, $self->getAttributes($self->tokenStartStack[$stackPos - (1 - 1)], $self->tokenEndStack[$stackPos])); + $self->checkUseUse($self->semValue, $stackPos - (1 - 1)); + }, 139 => static function ($self, $stackPos) { + $self->semValue = new Node\UseItem($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], Stmt\Use_::TYPE_UNKNOWN, $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + $self->checkUseUse($self->semValue, $stackPos - (3 - 3)); + }, 140 => static function ($self, $stackPos) { + $self->semValue = $self->semStack[$stackPos - (1 - 1)]; + $self->semValue->type = Stmt\Use_::TYPE_NORMAL; + }, 141 => static function ($self, $stackPos) { + $self->semValue = $self->semStack[$stackPos - (2 - 2)]; + $self->semValue->type = $self->semStack[$stackPos - (2 - 1)]; + }, 142 => null, 143 => static function ($self, $stackPos) { + $self->semStack[$stackPos - (3 - 1)][] = $self->semStack[$stackPos - (3 - 3)]; + $self->semValue = $self->semStack[$stackPos - (3 - 1)]; + }, 144 => static function ($self, $stackPos) { + $self->semValue = array($self->semStack[$stackPos - (1 - 1)]); + }, 145 => static function ($self, $stackPos) { + $self->semValue = new Node\Const_($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 146 => null, 147 => static function ($self, $stackPos) { + $self->semStack[$stackPos - (3 - 1)][] = $self->semStack[$stackPos - (3 - 3)]; + $self->semValue = $self->semStack[$stackPos - (3 - 1)]; + }, 148 => static function ($self, $stackPos) { + $self->semValue = array($self->semStack[$stackPos - (1 - 1)]); + }, 149 => static function ($self, $stackPos) { + $self->semValue = new Node\Const_(new Node\Identifier($self->semStack[$stackPos - (3 - 1)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos - (3 - 1)])), $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 150 => static function ($self, $stackPos) { + $self->semValue = new Node\Const_(new Node\Identifier($self->semStack[$stackPos - (3 - 1)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos - (3 - 1)])), $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 151 => static function ($self, $stackPos) { + if ($self->semStack[$stackPos - (2 - 2)] !== null) { + $self->semStack[$stackPos - (2 - 1)][] = $self->semStack[$stackPos - (2 - 2)]; + } + $self->semValue = $self->semStack[$stackPos - (2 - 1)]; + }, 152 => static function ($self, $stackPos) { + $self->semValue = array(); + }, 153 => static function ($self, $stackPos) { + $nop = $self->maybeCreateZeroLengthNop($self->tokenPos); if ($nop !== null) { - $this->semStack[$stackPos - (1 - 1)][] = $nop; - } - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 155 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 156 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 157 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 158 => function ($stackPos) { - throw new Error('__HALT_COMPILER() can only be used from the outermost scope', $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 159 => function ($stackPos) { - if ($this->semStack[$stackPos - (3 - 2)]) { - $this->semValue = $this->semStack[$stackPos - (3 - 2)]; - $attrs = $this->startAttributeStack[$stackPos - (3 - 1)]; - $stmts = $this->semValue; - if (!empty($attrs['comments'])) { - $stmts[0]->setAttribute('comments', \array_merge($attrs['comments'], $stmts[0]->getAttribute('comments', []))); - } - } else { - $startAttributes = $this->startAttributeStack[$stackPos - (3 - 1)]; - if (isset($startAttributes['comments'])) { - $this->semValue = new Stmt\Nop($startAttributes + $this->endAttributes); - } else { - $this->semValue = null; - } - if (null === $this->semValue) { - $this->semValue = array(); - } - } - }, 160 => function ($stackPos) { - $this->semValue = new Stmt\If_($this->semStack[$stackPos - (7 - 3)], ['stmts' => \is_array($this->semStack[$stackPos - (7 - 5)]) ? $this->semStack[$stackPos - (7 - 5)] : array($this->semStack[$stackPos - (7 - 5)]), 'elseifs' => $this->semStack[$stackPos - (7 - 6)], 'else' => $this->semStack[$stackPos - (7 - 7)]], $this->startAttributeStack[$stackPos - (7 - 1)] + $this->endAttributes); - }, 161 => function ($stackPos) { - $this->semValue = new Stmt\If_($this->semStack[$stackPos - (10 - 3)], ['stmts' => $this->semStack[$stackPos - (10 - 6)], 'elseifs' => $this->semStack[$stackPos - (10 - 7)], 'else' => $this->semStack[$stackPos - (10 - 8)]], $this->startAttributeStack[$stackPos - (10 - 1)] + $this->endAttributes); - }, 162 => function ($stackPos) { - $this->semValue = new Stmt\While_($this->semStack[$stackPos - (5 - 3)], $this->semStack[$stackPos - (5 - 5)], $this->startAttributeStack[$stackPos - (5 - 1)] + $this->endAttributes); - }, 163 => function ($stackPos) { - $this->semValue = new Stmt\Do_($this->semStack[$stackPos - (7 - 5)], \is_array($this->semStack[$stackPos - (7 - 2)]) ? $this->semStack[$stackPos - (7 - 2)] : array($this->semStack[$stackPos - (7 - 2)]), $this->startAttributeStack[$stackPos - (7 - 1)] + $this->endAttributes); - }, 164 => function ($stackPos) { - $this->semValue = new Stmt\For_(['init' => $this->semStack[$stackPos - (9 - 3)], 'cond' => $this->semStack[$stackPos - (9 - 5)], 'loop' => $this->semStack[$stackPos - (9 - 7)], 'stmts' => $this->semStack[$stackPos - (9 - 9)]], $this->startAttributeStack[$stackPos - (9 - 1)] + $this->endAttributes); - }, 165 => function ($stackPos) { - $this->semValue = new Stmt\Switch_($this->semStack[$stackPos - (5 - 3)], $this->semStack[$stackPos - (5 - 5)], $this->startAttributeStack[$stackPos - (5 - 1)] + $this->endAttributes); - }, 166 => function ($stackPos) { - $this->semValue = new Stmt\Break_($this->semStack[$stackPos - (3 - 2)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 167 => function ($stackPos) { - $this->semValue = new Stmt\Continue_($this->semStack[$stackPos - (3 - 2)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 168 => function ($stackPos) { - $this->semValue = new Stmt\Return_($this->semStack[$stackPos - (3 - 2)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 169 => function ($stackPos) { - $this->semValue = new Stmt\Global_($this->semStack[$stackPos - (3 - 2)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 170 => function ($stackPos) { - $this->semValue = new Stmt\Static_($this->semStack[$stackPos - (3 - 2)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 171 => function ($stackPos) { - $this->semValue = new Stmt\Echo_($this->semStack[$stackPos - (3 - 2)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 172 => function ($stackPos) { - $this->semValue = new Stmt\InlineHTML($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 173 => function ($stackPos) { - $e = $this->semStack[$stackPos - (2 - 1)]; - if ($e instanceof Expr\Throw_) { - // For backwards-compatibility reasons, convert throw in statement position into - // Stmt\Throw_ rather than Stmt\Expression(Expr\Throw_). - $this->semValue = new Stmt\Throw_($e->expr, $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - } else { - $this->semValue = new Stmt\Expression($e, $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - } - }, 174 => function ($stackPos) { - $this->semValue = new Stmt\Unset_($this->semStack[$stackPos - (5 - 3)], $this->startAttributeStack[$stackPos - (5 - 1)] + $this->endAttributes); - }, 175 => function ($stackPos) { - $this->semValue = new Stmt\Foreach_($this->semStack[$stackPos - (7 - 3)], $this->semStack[$stackPos - (7 - 5)][0], ['keyVar' => null, 'byRef' => $this->semStack[$stackPos - (7 - 5)][1], 'stmts' => $this->semStack[$stackPos - (7 - 7)]], $this->startAttributeStack[$stackPos - (7 - 1)] + $this->endAttributes); - }, 176 => function ($stackPos) { - $this->semValue = new Stmt\Foreach_($this->semStack[$stackPos - (9 - 3)], $this->semStack[$stackPos - (9 - 7)][0], ['keyVar' => $this->semStack[$stackPos - (9 - 5)], 'byRef' => $this->semStack[$stackPos - (9 - 7)][1], 'stmts' => $this->semStack[$stackPos - (9 - 9)]], $this->startAttributeStack[$stackPos - (9 - 1)] + $this->endAttributes); - }, 177 => function ($stackPos) { - $this->semValue = new Stmt\Foreach_($this->semStack[$stackPos - (6 - 3)], new Expr\Error($this->startAttributeStack[$stackPos - (6 - 4)] + $this->endAttributeStack[$stackPos - (6 - 4)]), ['stmts' => $this->semStack[$stackPos - (6 - 6)]], $this->startAttributeStack[$stackPos - (6 - 1)] + $this->endAttributes); - }, 178 => function ($stackPos) { - $this->semValue = new Stmt\Declare_($this->semStack[$stackPos - (5 - 3)], $this->semStack[$stackPos - (5 - 5)], $this->startAttributeStack[$stackPos - (5 - 1)] + $this->endAttributes); - }, 179 => function ($stackPos) { - $this->semValue = new Stmt\TryCatch($this->semStack[$stackPos - (6 - 3)], $this->semStack[$stackPos - (6 - 5)], $this->semStack[$stackPos - (6 - 6)], $this->startAttributeStack[$stackPos - (6 - 1)] + $this->endAttributes); - $this->checkTryCatch($this->semValue); - }, 180 => function ($stackPos) { - $this->semValue = new Stmt\Goto_($this->semStack[$stackPos - (3 - 2)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 181 => function ($stackPos) { - $this->semValue = new Stmt\Label($this->semStack[$stackPos - (2 - 1)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 182 => function ($stackPos) { - $this->semValue = array(); + $self->semStack[$stackPos - (1 - 1)][] = $nop; + } + $self->semValue = $self->semStack[$stackPos - (1 - 1)]; + }, 154 => null, 155 => null, 156 => null, 157 => static function ($self, $stackPos) { + throw new Error('__HALT_COMPILER() can only be used from the outermost scope', $self->getAttributes($self->tokenStartStack[$stackPos - (1 - 1)], $self->tokenEndStack[$stackPos])); + }, 158 => static function ($self, $stackPos) { + $self->semValue = new Stmt\Block($self->semStack[$stackPos - (3 - 2)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 159 => static function ($self, $stackPos) { + $self->semValue = new Stmt\If_($self->semStack[$stackPos - (7 - 3)], ['stmts' => $self->semStack[$stackPos - (7 - 5)], 'elseifs' => $self->semStack[$stackPos - (7 - 6)], 'else' => $self->semStack[$stackPos - (7 - 7)]], $self->getAttributes($self->tokenStartStack[$stackPos - (7 - 1)], $self->tokenEndStack[$stackPos])); + }, 160 => static function ($self, $stackPos) { + $self->semValue = new Stmt\If_($self->semStack[$stackPos - (10 - 3)], ['stmts' => $self->semStack[$stackPos - (10 - 6)], 'elseifs' => $self->semStack[$stackPos - (10 - 7)], 'else' => $self->semStack[$stackPos - (10 - 8)]], $self->getAttributes($self->tokenStartStack[$stackPos - (10 - 1)], $self->tokenEndStack[$stackPos])); + }, 161 => static function ($self, $stackPos) { + $self->semValue = new Stmt\While_($self->semStack[$stackPos - (5 - 3)], $self->semStack[$stackPos - (5 - 5)], $self->getAttributes($self->tokenStartStack[$stackPos - (5 - 1)], $self->tokenEndStack[$stackPos])); + }, 162 => static function ($self, $stackPos) { + $self->semValue = new Stmt\Do_($self->semStack[$stackPos - (7 - 5)], $self->semStack[$stackPos - (7 - 2)], $self->getAttributes($self->tokenStartStack[$stackPos - (7 - 1)], $self->tokenEndStack[$stackPos])); + }, 163 => static function ($self, $stackPos) { + $self->semValue = new Stmt\For_(['init' => $self->semStack[$stackPos - (9 - 3)], 'cond' => $self->semStack[$stackPos - (9 - 5)], 'loop' => $self->semStack[$stackPos - (9 - 7)], 'stmts' => $self->semStack[$stackPos - (9 - 9)]], $self->getAttributes($self->tokenStartStack[$stackPos - (9 - 1)], $self->tokenEndStack[$stackPos])); + }, 164 => static function ($self, $stackPos) { + $self->semValue = new Stmt\Switch_($self->semStack[$stackPos - (5 - 3)], $self->semStack[$stackPos - (5 - 5)], $self->getAttributes($self->tokenStartStack[$stackPos - (5 - 1)], $self->tokenEndStack[$stackPos])); + }, 165 => static function ($self, $stackPos) { + $self->semValue = new Stmt\Break_($self->semStack[$stackPos - (3 - 2)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 166 => static function ($self, $stackPos) { + $self->semValue = new Stmt\Continue_($self->semStack[$stackPos - (3 - 2)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 167 => static function ($self, $stackPos) { + $self->semValue = new Stmt\Return_($self->semStack[$stackPos - (3 - 2)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 168 => static function ($self, $stackPos) { + $self->semValue = new Stmt\Global_($self->semStack[$stackPos - (3 - 2)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 169 => static function ($self, $stackPos) { + $self->semValue = new Stmt\Static_($self->semStack[$stackPos - (3 - 2)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 170 => static function ($self, $stackPos) { + $self->semValue = new Stmt\Echo_($self->semStack[$stackPos - (3 - 2)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 171 => static function ($self, $stackPos) { + $self->semValue = new Stmt\InlineHTML($self->semStack[$stackPos - (1 - 1)], $self->getAttributes($self->tokenStartStack[$stackPos - (1 - 1)], $self->tokenEndStack[$stackPos])); + $self->semValue->setAttribute('hasLeadingNewline', $self->inlineHtmlHasLeadingNewline($stackPos - (1 - 1))); + }, 172 => static function ($self, $stackPos) { + $self->semValue = new Stmt\Expression($self->semStack[$stackPos - (2 - 1)], $self->getAttributes($self->tokenStartStack[$stackPos - (2 - 1)], $self->tokenEndStack[$stackPos])); + }, 173 => static function ($self, $stackPos) { + $self->semValue = new Stmt\Unset_($self->semStack[$stackPos - (5 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (5 - 1)], $self->tokenEndStack[$stackPos])); + }, 174 => static function ($self, $stackPos) { + $self->semValue = new Stmt\Foreach_($self->semStack[$stackPos - (7 - 3)], $self->semStack[$stackPos - (7 - 5)][0], ['keyVar' => null, 'byRef' => $self->semStack[$stackPos - (7 - 5)][1], 'stmts' => $self->semStack[$stackPos - (7 - 7)]], $self->getAttributes($self->tokenStartStack[$stackPos - (7 - 1)], $self->tokenEndStack[$stackPos])); + }, 175 => static function ($self, $stackPos) { + $self->semValue = new Stmt\Foreach_($self->semStack[$stackPos - (9 - 3)], $self->semStack[$stackPos - (9 - 7)][0], ['keyVar' => $self->semStack[$stackPos - (9 - 5)], 'byRef' => $self->semStack[$stackPos - (9 - 7)][1], 'stmts' => $self->semStack[$stackPos - (9 - 9)]], $self->getAttributes($self->tokenStartStack[$stackPos - (9 - 1)], $self->tokenEndStack[$stackPos])); + }, 176 => static function ($self, $stackPos) { + $self->semValue = new Stmt\Foreach_($self->semStack[$stackPos - (6 - 3)], new Expr\Error($self->getAttributes($self->tokenStartStack[$stackPos - (6 - 4)], $self->tokenEndStack[$stackPos - (6 - 4)])), ['stmts' => $self->semStack[$stackPos - (6 - 6)]], $self->getAttributes($self->tokenStartStack[$stackPos - (6 - 1)], $self->tokenEndStack[$stackPos])); + }, 177 => static function ($self, $stackPos) { + $self->semValue = new Stmt\Declare_($self->semStack[$stackPos - (5 - 3)], $self->semStack[$stackPos - (5 - 5)], $self->getAttributes($self->tokenStartStack[$stackPos - (5 - 1)], $self->tokenEndStack[$stackPos])); + }, 178 => static function ($self, $stackPos) { + $self->semValue = new Stmt\TryCatch($self->semStack[$stackPos - (6 - 3)], $self->semStack[$stackPos - (6 - 5)], $self->semStack[$stackPos - (6 - 6)], $self->getAttributes($self->tokenStartStack[$stackPos - (6 - 1)], $self->tokenEndStack[$stackPos])); + $self->checkTryCatch($self->semValue); + }, 179 => static function ($self, $stackPos) { + $self->semValue = new Stmt\Goto_($self->semStack[$stackPos - (3 - 2)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 180 => static function ($self, $stackPos) { + $self->semValue = new Stmt\Label($self->semStack[$stackPos - (2 - 1)], $self->getAttributes($self->tokenStartStack[$stackPos - (2 - 1)], $self->tokenEndStack[$stackPos])); + }, 181 => static function ($self, $stackPos) { + $self->semValue = null; /* means: no statement */ - }, 183 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 184 => function ($stackPos) { - $startAttributes = $this->startAttributeStack[$stackPos - (1 - 1)]; - if (isset($startAttributes['comments'])) { - $this->semValue = new Stmt\Nop($startAttributes + $this->endAttributes); + }, 182 => null, 183 => static function ($self, $stackPos) { + $self->semValue = $self->maybeCreateNop($self->tokenStartStack[$stackPos - (1 - 1)], $self->tokenEndStack[$stackPos]); + }, 184 => static function ($self, $stackPos) { + if ($self->semStack[$stackPos - (1 - 1)] instanceof Stmt\Block) { + $self->semValue = $self->semStack[$stackPos - (1 - 1)]->stmts; + } else if ($self->semStack[$stackPos - (1 - 1)] === null) { + $self->semValue = []; } else { - $this->semValue = null; - } - if ($this->semValue === null) { - $this->semValue = array(); - } - /* means: no statement */ - }, 185 => function ($stackPos) { - $this->semValue = array(); - }, 186 => function ($stackPos) { - $this->semStack[$stackPos - (2 - 1)][] = $this->semStack[$stackPos - (2 - 2)]; - $this->semValue = $this->semStack[$stackPos - (2 - 1)]; - }, 187 => function ($stackPos) { - $this->semValue = array($this->semStack[$stackPos - (1 - 1)]); - }, 188 => function ($stackPos) { - $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; - $this->semValue = $this->semStack[$stackPos - (3 - 1)]; - }, 189 => function ($stackPos) { - $this->semValue = new Stmt\Catch_($this->semStack[$stackPos - (8 - 3)], $this->semStack[$stackPos - (8 - 4)], $this->semStack[$stackPos - (8 - 7)], $this->startAttributeStack[$stackPos - (8 - 1)] + $this->endAttributes); - }, 190 => function ($stackPos) { - $this->semValue = null; - }, 191 => function ($stackPos) { - $this->semValue = new Stmt\Finally_($this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 192 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (2 - 1)]; - }, 193 => function ($stackPos) { - $this->semValue = array($this->semStack[$stackPos - (1 - 1)]); - }, 194 => function ($stackPos) { - $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; - $this->semValue = $this->semStack[$stackPos - (3 - 1)]; - }, 195 => function ($stackPos) { - $this->semValue = \false; - }, 196 => function ($stackPos) { - $this->semValue = \true; - }, 197 => function ($stackPos) { - $this->semValue = \false; - }, 198 => function ($stackPos) { - $this->semValue = \true; - }, 199 => function ($stackPos) { - $this->semValue = \false; - }, 200 => function ($stackPos) { - $this->semValue = \true; - }, 201 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (3 - 2)]; - }, 202 => function ($stackPos) { - $this->semValue = []; - }, 203 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 204 => function ($stackPos) { - $this->semValue = new Node\Identifier($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 205 => function ($stackPos) { - $this->semValue = new Stmt\Function_($this->semStack[$stackPos - (8 - 3)], ['byRef' => $this->semStack[$stackPos - (8 - 2)], 'params' => $this->semStack[$stackPos - (8 - 5)], 'returnType' => $this->semStack[$stackPos - (8 - 7)], 'stmts' => $this->semStack[$stackPos - (8 - 8)], 'attrGroups' => []], $this->startAttributeStack[$stackPos - (8 - 1)] + $this->endAttributes); - }, 206 => function ($stackPos) { - $this->semValue = new Stmt\Function_($this->semStack[$stackPos - (9 - 4)], ['byRef' => $this->semStack[$stackPos - (9 - 3)], 'params' => $this->semStack[$stackPos - (9 - 6)], 'returnType' => $this->semStack[$stackPos - (9 - 8)], 'stmts' => $this->semStack[$stackPos - (9 - 9)], 'attrGroups' => $this->semStack[$stackPos - (9 - 1)]], $this->startAttributeStack[$stackPos - (9 - 1)] + $this->endAttributes); - }, 207 => function ($stackPos) { - $this->semValue = new Stmt\Class_($this->semStack[$stackPos - (7 - 2)], ['type' => $this->semStack[$stackPos - (7 - 1)], 'extends' => $this->semStack[$stackPos - (7 - 3)], 'implements' => $this->semStack[$stackPos - (7 - 4)], 'stmts' => $this->semStack[$stackPos - (7 - 6)], 'attrGroups' => []], $this->startAttributeStack[$stackPos - (7 - 1)] + $this->endAttributes); - $this->checkClass($this->semValue, $stackPos - (7 - 2)); - }, 208 => function ($stackPos) { - $this->semValue = new Stmt\Class_($this->semStack[$stackPos - (8 - 3)], ['type' => $this->semStack[$stackPos - (8 - 2)], 'extends' => $this->semStack[$stackPos - (8 - 4)], 'implements' => $this->semStack[$stackPos - (8 - 5)], 'stmts' => $this->semStack[$stackPos - (8 - 7)], 'attrGroups' => $this->semStack[$stackPos - (8 - 1)]], $this->startAttributeStack[$stackPos - (8 - 1)] + $this->endAttributes); - $this->checkClass($this->semValue, $stackPos - (8 - 3)); - }, 209 => function ($stackPos) { - $this->semValue = new Stmt\Interface_($this->semStack[$stackPos - (7 - 3)], ['extends' => $this->semStack[$stackPos - (7 - 4)], 'stmts' => $this->semStack[$stackPos - (7 - 6)], 'attrGroups' => $this->semStack[$stackPos - (7 - 1)]], $this->startAttributeStack[$stackPos - (7 - 1)] + $this->endAttributes); - $this->checkInterface($this->semValue, $stackPos - (7 - 3)); - }, 210 => function ($stackPos) { - $this->semValue = new Stmt\Trait_($this->semStack[$stackPos - (6 - 3)], ['stmts' => $this->semStack[$stackPos - (6 - 5)], 'attrGroups' => $this->semStack[$stackPos - (6 - 1)]], $this->startAttributeStack[$stackPos - (6 - 1)] + $this->endAttributes); - }, 211 => function ($stackPos) { - $this->semValue = new Stmt\Enum_($this->semStack[$stackPos - (8 - 3)], ['scalarType' => $this->semStack[$stackPos - (8 - 4)], 'implements' => $this->semStack[$stackPos - (8 - 5)], 'stmts' => $this->semStack[$stackPos - (8 - 7)], 'attrGroups' => $this->semStack[$stackPos - (8 - 1)]], $this->startAttributeStack[$stackPos - (8 - 1)] + $this->endAttributes); - $this->checkEnum($this->semValue, $stackPos - (8 - 3)); - }, 212 => function ($stackPos) { - $this->semValue = null; - }, 213 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (2 - 2)]; - }, 214 => function ($stackPos) { - $this->semValue = null; - }, 215 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (2 - 2)]; - }, 216 => function ($stackPos) { - $this->semValue = 0; - }, 217 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (2 - 1)]; - }, 218 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 219 => function ($stackPos) { - $this->checkClassModifier($this->semStack[$stackPos - (2 - 1)], $this->semStack[$stackPos - (2 - 2)], $stackPos - (2 - 2)); - $this->semValue = $this->semStack[$stackPos - (2 - 1)] | $this->semStack[$stackPos - (2 - 2)]; - }, 220 => function ($stackPos) { - $this->semValue = Stmt\Class_::MODIFIER_ABSTRACT; - }, 221 => function ($stackPos) { - $this->semValue = Stmt\Class_::MODIFIER_FINAL; - }, 222 => function ($stackPos) { - $this->semValue = Stmt\Class_::MODIFIER_READONLY; - }, 223 => function ($stackPos) { - $this->semValue = null; - }, 224 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (2 - 2)]; - }, 225 => function ($stackPos) { - $this->semValue = array(); - }, 226 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (2 - 2)]; - }, 227 => function ($stackPos) { - $this->semValue = array(); - }, 228 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (2 - 2)]; - }, 229 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (2 - 1)]; - }, 230 => function ($stackPos) { - $this->semValue = array($this->semStack[$stackPos - (1 - 1)]); - }, 231 => function ($stackPos) { - $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; - $this->semValue = $this->semStack[$stackPos - (3 - 1)]; - }, 232 => function ($stackPos) { - $this->semValue = \is_array($this->semStack[$stackPos - (1 - 1)]) ? $this->semStack[$stackPos - (1 - 1)] : array($this->semStack[$stackPos - (1 - 1)]); - }, 233 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (4 - 2)]; - }, 234 => function ($stackPos) { - $this->semValue = \is_array($this->semStack[$stackPos - (1 - 1)]) ? $this->semStack[$stackPos - (1 - 1)] : array($this->semStack[$stackPos - (1 - 1)]); - }, 235 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (4 - 2)]; - }, 236 => function ($stackPos) { - $this->semValue = \is_array($this->semStack[$stackPos - (1 - 1)]) ? $this->semStack[$stackPos - (1 - 1)] : array($this->semStack[$stackPos - (1 - 1)]); - }, 237 => function ($stackPos) { - $this->semValue = null; - }, 238 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (4 - 2)]; - }, 239 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (2 - 1)]; - }, 240 => function ($stackPos) { - $this->semValue = array($this->semStack[$stackPos - (1 - 1)]); - }, 241 => function ($stackPos) { - $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; - $this->semValue = $this->semStack[$stackPos - (3 - 1)]; - }, 242 => function ($stackPos) { - $this->semValue = new Stmt\DeclareDeclare($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 243 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (3 - 2)]; - }, 244 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (4 - 3)]; - }, 245 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (4 - 2)]; - }, 246 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (5 - 3)]; - }, 247 => function ($stackPos) { - $this->semValue = array(); - }, 248 => function ($stackPos) { - $this->semStack[$stackPos - (2 - 1)][] = $this->semStack[$stackPos - (2 - 2)]; - $this->semValue = $this->semStack[$stackPos - (2 - 1)]; - }, 249 => function ($stackPos) { - $this->semValue = new Stmt\Case_($this->semStack[$stackPos - (4 - 2)], $this->semStack[$stackPos - (4 - 4)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 250 => function ($stackPos) { - $this->semValue = new Stmt\Case_(null, $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 251 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 252 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 253 => function ($stackPos) { - $this->semValue = new Expr\Match_($this->semStack[$stackPos - (7 - 3)], $this->semStack[$stackPos - (7 - 6)], $this->startAttributeStack[$stackPos - (7 - 1)] + $this->endAttributes); - }, 254 => function ($stackPos) { - $this->semValue = []; - }, 255 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (2 - 1)]; - }, 256 => function ($stackPos) { - $this->semValue = array($this->semStack[$stackPos - (1 - 1)]); - }, 257 => function ($stackPos) { - $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; - $this->semValue = $this->semStack[$stackPos - (3 - 1)]; - }, 258 => function ($stackPos) { - $this->semValue = new Node\MatchArm($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 259 => function ($stackPos) { - $this->semValue = new Node\MatchArm(null, $this->semStack[$stackPos - (4 - 4)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 260 => function ($stackPos) { - $this->semValue = \is_array($this->semStack[$stackPos - (1 - 1)]) ? $this->semStack[$stackPos - (1 - 1)] : array($this->semStack[$stackPos - (1 - 1)]); - }, 261 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (4 - 2)]; - }, 262 => function ($stackPos) { - $this->semValue = array(); - }, 263 => function ($stackPos) { - $this->semStack[$stackPos - (2 - 1)][] = $this->semStack[$stackPos - (2 - 2)]; - $this->semValue = $this->semStack[$stackPos - (2 - 1)]; - }, 264 => function ($stackPos) { - $this->semValue = new Stmt\ElseIf_($this->semStack[$stackPos - (5 - 3)], \is_array($this->semStack[$stackPos - (5 - 5)]) ? $this->semStack[$stackPos - (5 - 5)] : array($this->semStack[$stackPos - (5 - 5)]), $this->startAttributeStack[$stackPos - (5 - 1)] + $this->endAttributes); - }, 265 => function ($stackPos) { - $this->semValue = array(); - }, 266 => function ($stackPos) { - $this->semStack[$stackPos - (2 - 1)][] = $this->semStack[$stackPos - (2 - 2)]; - $this->semValue = $this->semStack[$stackPos - (2 - 1)]; - }, 267 => function ($stackPos) { - $this->semValue = new Stmt\ElseIf_($this->semStack[$stackPos - (6 - 3)], $this->semStack[$stackPos - (6 - 6)], $this->startAttributeStack[$stackPos - (6 - 1)] + $this->endAttributes); - $this->fixupAlternativeElse($this->semValue); - }, 268 => function ($stackPos) { - $this->semValue = null; - }, 269 => function ($stackPos) { - $this->semValue = new Stmt\Else_(\is_array($this->semStack[$stackPos - (2 - 2)]) ? $this->semStack[$stackPos - (2 - 2)] : array($this->semStack[$stackPos - (2 - 2)]), $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 270 => function ($stackPos) { - $this->semValue = null; - }, 271 => function ($stackPos) { - $this->semValue = new Stmt\Else_($this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - $this->fixupAlternativeElse($this->semValue); - }, 272 => function ($stackPos) { - $this->semValue = array($this->semStack[$stackPos - (1 - 1)], \false); - }, 273 => function ($stackPos) { - $this->semValue = array($this->semStack[$stackPos - (2 - 2)], \true); - }, 274 => function ($stackPos) { - $this->semValue = array($this->semStack[$stackPos - (1 - 1)], \false); - }, 275 => function ($stackPos) { - $this->semValue = array($this->semStack[$stackPos - (1 - 1)], \false); - }, 276 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (2 - 1)]; - }, 277 => function ($stackPos) { - $this->semValue = array(); - }, 278 => function ($stackPos) { - $this->semValue = array($this->semStack[$stackPos - (1 - 1)]); - }, 279 => function ($stackPos) { - $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; - $this->semValue = $this->semStack[$stackPos - (3 - 1)]; - }, 280 => function ($stackPos) { - $this->semValue = 0; - }, 281 => function ($stackPos) { - $this->checkModifier($this->semStack[$stackPos - (2 - 1)], $this->semStack[$stackPos - (2 - 2)], $stackPos - (2 - 2)); - $this->semValue = $this->semStack[$stackPos - (2 - 1)] | $this->semStack[$stackPos - (2 - 2)]; - }, 282 => function ($stackPos) { - $this->semValue = Stmt\Class_::MODIFIER_PUBLIC; - }, 283 => function ($stackPos) { - $this->semValue = Stmt\Class_::MODIFIER_PROTECTED; - }, 284 => function ($stackPos) { - $this->semValue = Stmt\Class_::MODIFIER_PRIVATE; - }, 285 => function ($stackPos) { - $this->semValue = Stmt\Class_::MODIFIER_READONLY; - }, 286 => function ($stackPos) { - $this->semValue = new Node\Param($this->semStack[$stackPos - (6 - 6)], null, $this->semStack[$stackPos - (6 - 3)], $this->semStack[$stackPos - (6 - 4)], $this->semStack[$stackPos - (6 - 5)], $this->startAttributeStack[$stackPos - (6 - 1)] + $this->endAttributes, $this->semStack[$stackPos - (6 - 2)], $this->semStack[$stackPos - (6 - 1)]); - $this->checkParam($this->semValue); - }, 287 => function ($stackPos) { - $this->semValue = new Node\Param($this->semStack[$stackPos - (8 - 6)], $this->semStack[$stackPos - (8 - 8)], $this->semStack[$stackPos - (8 - 3)], $this->semStack[$stackPos - (8 - 4)], $this->semStack[$stackPos - (8 - 5)], $this->startAttributeStack[$stackPos - (8 - 1)] + $this->endAttributes, $this->semStack[$stackPos - (8 - 2)], $this->semStack[$stackPos - (8 - 1)]); - $this->checkParam($this->semValue); - }, 288 => function ($stackPos) { - $this->semValue = new Node\Param(new Expr\Error($this->startAttributeStack[$stackPos - (6 - 1)] + $this->endAttributes), null, $this->semStack[$stackPos - (6 - 3)], $this->semStack[$stackPos - (6 - 4)], $this->semStack[$stackPos - (6 - 5)], $this->startAttributeStack[$stackPos - (6 - 1)] + $this->endAttributes, $this->semStack[$stackPos - (6 - 2)], $this->semStack[$stackPos - (6 - 1)]); - }, 289 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 290 => function ($stackPos) { - $this->semValue = new Node\NullableType($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 291 => function ($stackPos) { - $this->semValue = new Node\UnionType($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 292 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 293 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 294 => function ($stackPos) { - $this->semValue = new Node\Name('static', $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 295 => function ($stackPos) { - $this->semValue = $this->handleBuiltinTypes($this->semStack[$stackPos - (1 - 1)]); - }, 296 => function ($stackPos) { - $this->semValue = new Node\Identifier('array', $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 297 => function ($stackPos) { - $this->semValue = new Node\Identifier('callable', $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 298 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 299 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (3 - 2)]; - }, 300 => function ($stackPos) { - $this->semValue = array($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)]); - }, 301 => function ($stackPos) { - $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; - $this->semValue = $this->semStack[$stackPos - (3 - 1)]; - }, 302 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 303 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (3 - 2)]; - }, 304 => function ($stackPos) { - $this->semValue = array($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)]); - }, 305 => function ($stackPos) { - $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; - $this->semValue = $this->semStack[$stackPos - (3 - 1)]; - }, 306 => function ($stackPos) { - $this->semValue = array($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)]); - }, 307 => function ($stackPos) { - $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; - $this->semValue = $this->semStack[$stackPos - (3 - 1)]; - }, 308 => function ($stackPos) { - $this->semValue = new Node\IntersectionType($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 309 => function ($stackPos) { - $this->semValue = array($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)]); - }, 310 => function ($stackPos) { - $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; - $this->semValue = $this->semStack[$stackPos - (3 - 1)]; - }, 311 => function ($stackPos) { - $this->semValue = new Node\IntersectionType($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 312 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 313 => function ($stackPos) { - $this->semValue = new Node\NullableType($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 314 => function ($stackPos) { - $this->semValue = new Node\UnionType($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 315 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 316 => function ($stackPos) { - $this->semValue = null; - }, 317 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 318 => function ($stackPos) { - $this->semValue = null; - }, 319 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (2 - 2)]; - }, 320 => function ($stackPos) { - $this->semValue = null; - }, 321 => function ($stackPos) { - $this->semValue = array(); - }, 322 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (4 - 2)]; - }, 323 => function ($stackPos) { - $this->semValue = array($this->semStack[$stackPos - (3 - 2)]); - }, 324 => function ($stackPos) { - $this->semValue = new Node\VariadicPlaceholder($this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 325 => function ($stackPos) { - $this->semValue = array($this->semStack[$stackPos - (1 - 1)]); - }, 326 => function ($stackPos) { - $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; - $this->semValue = $this->semStack[$stackPos - (3 - 1)]; - }, 327 => function ($stackPos) { - $this->semValue = new Node\Arg($this->semStack[$stackPos - (1 - 1)], \false, \false, $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 328 => function ($stackPos) { - $this->semValue = new Node\Arg($this->semStack[$stackPos - (2 - 2)], \true, \false, $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 329 => function ($stackPos) { - $this->semValue = new Node\Arg($this->semStack[$stackPos - (2 - 2)], \false, \true, $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 330 => function ($stackPos) { - $this->semValue = new Node\Arg($this->semStack[$stackPos - (3 - 3)], \false, \false, $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes, $this->semStack[$stackPos - (3 - 1)]); - }, 331 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (2 - 1)]; - }, 332 => function ($stackPos) { - $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; - $this->semValue = $this->semStack[$stackPos - (3 - 1)]; - }, 333 => function ($stackPos) { - $this->semValue = array($this->semStack[$stackPos - (1 - 1)]); - }, 334 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 335 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (2 - 1)]; - }, 336 => function ($stackPos) { - $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; - $this->semValue = $this->semStack[$stackPos - (3 - 1)]; - }, 337 => function ($stackPos) { - $this->semValue = array($this->semStack[$stackPos - (1 - 1)]); - }, 338 => function ($stackPos) { - $this->semValue = new Stmt\StaticVar($this->semStack[$stackPos - (1 - 1)], null, $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 339 => function ($stackPos) { - $this->semValue = new Stmt\StaticVar($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 340 => function ($stackPos) { - if ($this->semStack[$stackPos - (2 - 2)] !== null) { - $this->semStack[$stackPos - (2 - 1)][] = $this->semStack[$stackPos - (2 - 2)]; - $this->semValue = $this->semStack[$stackPos - (2 - 1)]; + $self->semValue = [$self->semStack[$stackPos - (1 - 1)]]; + } + }, 185 => static function ($self, $stackPos) { + $self->semValue = array(); + }, 186 => static function ($self, $stackPos) { + $self->semStack[$stackPos - (2 - 1)][] = $self->semStack[$stackPos - (2 - 2)]; + $self->semValue = $self->semStack[$stackPos - (2 - 1)]; + }, 187 => static function ($self, $stackPos) { + $self->semValue = array($self->semStack[$stackPos - (1 - 1)]); + }, 188 => static function ($self, $stackPos) { + $self->semStack[$stackPos - (3 - 1)][] = $self->semStack[$stackPos - (3 - 3)]; + $self->semValue = $self->semStack[$stackPos - (3 - 1)]; + }, 189 => static function ($self, $stackPos) { + $self->semValue = new Stmt\Catch_($self->semStack[$stackPos - (8 - 3)], $self->semStack[$stackPos - (8 - 4)], $self->semStack[$stackPos - (8 - 7)], $self->getAttributes($self->tokenStartStack[$stackPos - (8 - 1)], $self->tokenEndStack[$stackPos])); + }, 190 => static function ($self, $stackPos) { + $self->semValue = null; + }, 191 => static function ($self, $stackPos) { + $self->semValue = new Stmt\Finally_($self->semStack[$stackPos - (4 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (4 - 1)], $self->tokenEndStack[$stackPos])); + }, 192 => null, 193 => static function ($self, $stackPos) { + $self->semValue = array($self->semStack[$stackPos - (1 - 1)]); + }, 194 => static function ($self, $stackPos) { + $self->semStack[$stackPos - (3 - 1)][] = $self->semStack[$stackPos - (3 - 3)]; + $self->semValue = $self->semStack[$stackPos - (3 - 1)]; + }, 195 => static function ($self, $stackPos) { + $self->semValue = \false; + }, 196 => static function ($self, $stackPos) { + $self->semValue = \true; + }, 197 => static function ($self, $stackPos) { + $self->semValue = \false; + }, 198 => static function ($self, $stackPos) { + $self->semValue = \true; + }, 199 => static function ($self, $stackPos) { + $self->semValue = \false; + }, 200 => static function ($self, $stackPos) { + $self->semValue = \true; + }, 201 => static function ($self, $stackPos) { + $self->semValue = $self->semStack[$stackPos - (3 - 2)]; + }, 202 => static function ($self, $stackPos) { + $self->semValue = []; + }, 203 => null, 204 => static function ($self, $stackPos) { + $self->semValue = new Node\Identifier($self->semStack[$stackPos - (1 - 1)], $self->getAttributes($self->tokenStartStack[$stackPos - (1 - 1)], $self->tokenEndStack[$stackPos])); + }, 205 => static function ($self, $stackPos) { + $self->semValue = new Stmt\Function_($self->semStack[$stackPos - (8 - 3)], ['byRef' => $self->semStack[$stackPos - (8 - 2)], 'params' => $self->semStack[$stackPos - (8 - 5)], 'returnType' => $self->semStack[$stackPos - (8 - 7)], 'stmts' => $self->semStack[$stackPos - (8 - 8)], 'attrGroups' => []], $self->getAttributes($self->tokenStartStack[$stackPos - (8 - 1)], $self->tokenEndStack[$stackPos])); + }, 206 => static function ($self, $stackPos) { + $self->semValue = new Stmt\Function_($self->semStack[$stackPos - (9 - 4)], ['byRef' => $self->semStack[$stackPos - (9 - 3)], 'params' => $self->semStack[$stackPos - (9 - 6)], 'returnType' => $self->semStack[$stackPos - (9 - 8)], 'stmts' => $self->semStack[$stackPos - (9 - 9)], 'attrGroups' => $self->semStack[$stackPos - (9 - 1)]], $self->getAttributes($self->tokenStartStack[$stackPos - (9 - 1)], $self->tokenEndStack[$stackPos])); + }, 207 => static function ($self, $stackPos) { + $self->semValue = new Stmt\Class_($self->semStack[$stackPos - (7 - 2)], ['type' => $self->semStack[$stackPos - (7 - 1)], 'extends' => $self->semStack[$stackPos - (7 - 3)], 'implements' => $self->semStack[$stackPos - (7 - 4)], 'stmts' => $self->semStack[$stackPos - (7 - 6)], 'attrGroups' => []], $self->getAttributes($self->tokenStartStack[$stackPos - (7 - 1)], $self->tokenEndStack[$stackPos])); + $self->checkClass($self->semValue, $stackPos - (7 - 2)); + }, 208 => static function ($self, $stackPos) { + $self->semValue = new Stmt\Class_($self->semStack[$stackPos - (8 - 3)], ['type' => $self->semStack[$stackPos - (8 - 2)], 'extends' => $self->semStack[$stackPos - (8 - 4)], 'implements' => $self->semStack[$stackPos - (8 - 5)], 'stmts' => $self->semStack[$stackPos - (8 - 7)], 'attrGroups' => $self->semStack[$stackPos - (8 - 1)]], $self->getAttributes($self->tokenStartStack[$stackPos - (8 - 1)], $self->tokenEndStack[$stackPos])); + $self->checkClass($self->semValue, $stackPos - (8 - 3)); + }, 209 => static function ($self, $stackPos) { + $self->semValue = new Stmt\Interface_($self->semStack[$stackPos - (7 - 3)], ['extends' => $self->semStack[$stackPos - (7 - 4)], 'stmts' => $self->semStack[$stackPos - (7 - 6)], 'attrGroups' => $self->semStack[$stackPos - (7 - 1)]], $self->getAttributes($self->tokenStartStack[$stackPos - (7 - 1)], $self->tokenEndStack[$stackPos])); + $self->checkInterface($self->semValue, $stackPos - (7 - 3)); + }, 210 => static function ($self, $stackPos) { + $self->semValue = new Stmt\Trait_($self->semStack[$stackPos - (6 - 3)], ['stmts' => $self->semStack[$stackPos - (6 - 5)], 'attrGroups' => $self->semStack[$stackPos - (6 - 1)]], $self->getAttributes($self->tokenStartStack[$stackPos - (6 - 1)], $self->tokenEndStack[$stackPos])); + }, 211 => static function ($self, $stackPos) { + $self->semValue = new Stmt\Enum_($self->semStack[$stackPos - (8 - 3)], ['scalarType' => $self->semStack[$stackPos - (8 - 4)], 'implements' => $self->semStack[$stackPos - (8 - 5)], 'stmts' => $self->semStack[$stackPos - (8 - 7)], 'attrGroups' => $self->semStack[$stackPos - (8 - 1)]], $self->getAttributes($self->tokenStartStack[$stackPos - (8 - 1)], $self->tokenEndStack[$stackPos])); + $self->checkEnum($self->semValue, $stackPos - (8 - 3)); + }, 212 => static function ($self, $stackPos) { + $self->semValue = null; + }, 213 => static function ($self, $stackPos) { + $self->semValue = $self->semStack[$stackPos - (2 - 2)]; + }, 214 => static function ($self, $stackPos) { + $self->semValue = null; + }, 215 => static function ($self, $stackPos) { + $self->semValue = $self->semStack[$stackPos - (2 - 2)]; + }, 216 => static function ($self, $stackPos) { + $self->semValue = 0; + }, 217 => null, 218 => null, 219 => static function ($self, $stackPos) { + $self->checkClassModifier($self->semStack[$stackPos - (2 - 1)], $self->semStack[$stackPos - (2 - 2)], $stackPos - (2 - 2)); + $self->semValue = $self->semStack[$stackPos - (2 - 1)] | $self->semStack[$stackPos - (2 - 2)]; + }, 220 => static function ($self, $stackPos) { + $self->semValue = Modifiers::ABSTRACT; + }, 221 => static function ($self, $stackPos) { + $self->semValue = Modifiers::FINAL; + }, 222 => static function ($self, $stackPos) { + $self->semValue = Modifiers::READONLY; + }, 223 => static function ($self, $stackPos) { + $self->semValue = null; + }, 224 => static function ($self, $stackPos) { + $self->semValue = $self->semStack[$stackPos - (2 - 2)]; + }, 225 => static function ($self, $stackPos) { + $self->semValue = array(); + }, 226 => static function ($self, $stackPos) { + $self->semValue = $self->semStack[$stackPos - (2 - 2)]; + }, 227 => static function ($self, $stackPos) { + $self->semValue = array(); + }, 228 => static function ($self, $stackPos) { + $self->semValue = $self->semStack[$stackPos - (2 - 2)]; + }, 229 => null, 230 => static function ($self, $stackPos) { + $self->semValue = array($self->semStack[$stackPos - (1 - 1)]); + }, 231 => static function ($self, $stackPos) { + $self->semStack[$stackPos - (3 - 1)][] = $self->semStack[$stackPos - (3 - 3)]; + $self->semValue = $self->semStack[$stackPos - (3 - 1)]; + }, 232 => null, 233 => static function ($self, $stackPos) { + $self->semValue = $self->semStack[$stackPos - (4 - 2)]; + }, 234 => null, 235 => static function ($self, $stackPos) { + $self->semValue = $self->semStack[$stackPos - (4 - 2)]; + }, 236 => static function ($self, $stackPos) { + if ($self->semStack[$stackPos - (1 - 1)] instanceof Stmt\Block) { + $self->semValue = $self->semStack[$stackPos - (1 - 1)]->stmts; + } else if ($self->semStack[$stackPos - (1 - 1)] === null) { + $self->semValue = []; } else { - $this->semValue = $this->semStack[$stackPos - (2 - 1)]; - } - }, 341 => function ($stackPos) { - $this->semValue = array(); - }, 342 => function ($stackPos) { - $startAttributes = $this->lookaheadStartAttributes; - if (isset($startAttributes['comments'])) { - $nop = new Stmt\Nop($this->createCommentNopAttributes($startAttributes['comments'])); + $self->semValue = [$self->semStack[$stackPos - (1 - 1)]]; + } + }, 237 => static function ($self, $stackPos) { + $self->semValue = null; + }, 238 => static function ($self, $stackPos) { + $self->semValue = $self->semStack[$stackPos - (4 - 2)]; + }, 239 => null, 240 => static function ($self, $stackPos) { + $self->semValue = array($self->semStack[$stackPos - (1 - 1)]); + }, 241 => static function ($self, $stackPos) { + $self->semStack[$stackPos - (3 - 1)][] = $self->semStack[$stackPos - (3 - 3)]; + $self->semValue = $self->semStack[$stackPos - (3 - 1)]; + }, 242 => static function ($self, $stackPos) { + $self->semValue = new Node\DeclareItem($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 243 => static function ($self, $stackPos) { + $self->semValue = $self->semStack[$stackPos - (3 - 2)]; + }, 244 => static function ($self, $stackPos) { + $self->semValue = $self->semStack[$stackPos - (4 - 3)]; + }, 245 => static function ($self, $stackPos) { + $self->semValue = $self->semStack[$stackPos - (4 - 2)]; + }, 246 => static function ($self, $stackPos) { + $self->semValue = $self->semStack[$stackPos - (5 - 3)]; + }, 247 => static function ($self, $stackPos) { + $self->semValue = array(); + }, 248 => static function ($self, $stackPos) { + $self->semStack[$stackPos - (2 - 1)][] = $self->semStack[$stackPos - (2 - 2)]; + $self->semValue = $self->semStack[$stackPos - (2 - 1)]; + }, 249 => static function ($self, $stackPos) { + $self->semValue = new Stmt\Case_($self->semStack[$stackPos - (4 - 2)], $self->semStack[$stackPos - (4 - 4)], $self->getAttributes($self->tokenStartStack[$stackPos - (4 - 1)], $self->tokenEndStack[$stackPos])); + }, 250 => static function ($self, $stackPos) { + $self->semValue = new Stmt\Case_(null, $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 251 => null, 252 => null, 253 => static function ($self, $stackPos) { + $self->semValue = new Expr\Match_($self->semStack[$stackPos - (7 - 3)], $self->semStack[$stackPos - (7 - 6)], $self->getAttributes($self->tokenStartStack[$stackPos - (7 - 1)], $self->tokenEndStack[$stackPos])); + }, 254 => static function ($self, $stackPos) { + $self->semValue = []; + }, 255 => null, 256 => static function ($self, $stackPos) { + $self->semValue = array($self->semStack[$stackPos - (1 - 1)]); + }, 257 => static function ($self, $stackPos) { + $self->semStack[$stackPos - (3 - 1)][] = $self->semStack[$stackPos - (3 - 3)]; + $self->semValue = $self->semStack[$stackPos - (3 - 1)]; + }, 258 => static function ($self, $stackPos) { + $self->semValue = new Node\MatchArm($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 259 => static function ($self, $stackPos) { + $self->semValue = new Node\MatchArm(null, $self->semStack[$stackPos - (4 - 4)], $self->getAttributes($self->tokenStartStack[$stackPos - (4 - 1)], $self->tokenEndStack[$stackPos])); + }, 260 => static function ($self, $stackPos) { + $self->semValue = $self->semStack[$stackPos - (1 - 1)]; + }, 261 => static function ($self, $stackPos) { + $self->semValue = $self->semStack[$stackPos - (4 - 2)]; + }, 262 => static function ($self, $stackPos) { + $self->semValue = array(); + }, 263 => static function ($self, $stackPos) { + $self->semStack[$stackPos - (2 - 1)][] = $self->semStack[$stackPos - (2 - 2)]; + $self->semValue = $self->semStack[$stackPos - (2 - 1)]; + }, 264 => static function ($self, $stackPos) { + $self->semValue = new Stmt\ElseIf_($self->semStack[$stackPos - (5 - 3)], $self->semStack[$stackPos - (5 - 5)], $self->getAttributes($self->tokenStartStack[$stackPos - (5 - 1)], $self->tokenEndStack[$stackPos])); + }, 265 => static function ($self, $stackPos) { + $self->semValue = array(); + }, 266 => static function ($self, $stackPos) { + $self->semStack[$stackPos - (2 - 1)][] = $self->semStack[$stackPos - (2 - 2)]; + $self->semValue = $self->semStack[$stackPos - (2 - 1)]; + }, 267 => static function ($self, $stackPos) { + $self->semValue = new Stmt\ElseIf_($self->semStack[$stackPos - (6 - 3)], $self->semStack[$stackPos - (6 - 6)], $self->getAttributes($self->tokenStartStack[$stackPos - (6 - 1)], $self->tokenEndStack[$stackPos])); + $self->fixupAlternativeElse($self->semValue); + }, 268 => static function ($self, $stackPos) { + $self->semValue = null; + }, 269 => static function ($self, $stackPos) { + $self->semValue = new Stmt\Else_($self->semStack[$stackPos - (2 - 2)], $self->getAttributes($self->tokenStartStack[$stackPos - (2 - 1)], $self->tokenEndStack[$stackPos])); + }, 270 => static function ($self, $stackPos) { + $self->semValue = null; + }, 271 => static function ($self, $stackPos) { + $self->semValue = new Stmt\Else_($self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + $self->fixupAlternativeElse($self->semValue); + }, 272 => static function ($self, $stackPos) { + $self->semValue = array($self->semStack[$stackPos - (1 - 1)], \false); + }, 273 => static function ($self, $stackPos) { + $self->semValue = array($self->semStack[$stackPos - (2 - 2)], \true); + }, 274 => static function ($self, $stackPos) { + $self->semValue = array($self->semStack[$stackPos - (1 - 1)], \false); + }, 275 => static function ($self, $stackPos) { + $self->semValue = array($self->fixupArrayDestructuring($self->semStack[$stackPos - (1 - 1)]), \false); + }, 276 => null, 277 => static function ($self, $stackPos) { + $self->semValue = array(); + }, 278 => static function ($self, $stackPos) { + $self->semValue = array($self->semStack[$stackPos - (1 - 1)]); + }, 279 => static function ($self, $stackPos) { + $self->semStack[$stackPos - (3 - 1)][] = $self->semStack[$stackPos - (3 - 3)]; + $self->semValue = $self->semStack[$stackPos - (3 - 1)]; + }, 280 => static function ($self, $stackPos) { + $self->semValue = 0; + }, 281 => static function ($self, $stackPos) { + $self->checkModifier($self->semStack[$stackPos - (2 - 1)], $self->semStack[$stackPos - (2 - 2)], $stackPos - (2 - 2)); + $self->semValue = $self->semStack[$stackPos - (2 - 1)] | $self->semStack[$stackPos - (2 - 2)]; + }, 282 => static function ($self, $stackPos) { + $self->semValue = Modifiers::PUBLIC; + }, 283 => static function ($self, $stackPos) { + $self->semValue = Modifiers::PROTECTED; + }, 284 => static function ($self, $stackPos) { + $self->semValue = Modifiers::PRIVATE; + }, 285 => static function ($self, $stackPos) { + $self->semValue = Modifiers::READONLY; + }, 286 => static function ($self, $stackPos) { + $self->semValue = new Node\Param($self->semStack[$stackPos - (6 - 6)], null, $self->semStack[$stackPos - (6 - 3)], $self->semStack[$stackPos - (6 - 4)], $self->semStack[$stackPos - (6 - 5)], $self->getAttributes($self->tokenStartStack[$stackPos - (6 - 1)], $self->tokenEndStack[$stackPos]), $self->semStack[$stackPos - (6 - 2)], $self->semStack[$stackPos - (6 - 1)]); + $self->checkParam($self->semValue); + }, 287 => static function ($self, $stackPos) { + $self->semValue = new Node\Param($self->semStack[$stackPos - (8 - 6)], $self->semStack[$stackPos - (8 - 8)], $self->semStack[$stackPos - (8 - 3)], $self->semStack[$stackPos - (8 - 4)], $self->semStack[$stackPos - (8 - 5)], $self->getAttributes($self->tokenStartStack[$stackPos - (8 - 1)], $self->tokenEndStack[$stackPos]), $self->semStack[$stackPos - (8 - 2)], $self->semStack[$stackPos - (8 - 1)]); + $self->checkParam($self->semValue); + }, 288 => static function ($self, $stackPos) { + $self->semValue = new Node\Param(new Expr\Error($self->getAttributes($self->tokenStartStack[$stackPos - (6 - 1)], $self->tokenEndStack[$stackPos])), null, $self->semStack[$stackPos - (6 - 3)], $self->semStack[$stackPos - (6 - 4)], $self->semStack[$stackPos - (6 - 5)], $self->getAttributes($self->tokenStartStack[$stackPos - (6 - 1)], $self->tokenEndStack[$stackPos]), $self->semStack[$stackPos - (6 - 2)], $self->semStack[$stackPos - (6 - 1)]); + }, 289 => null, 290 => static function ($self, $stackPos) { + $self->semValue = new Node\NullableType($self->semStack[$stackPos - (2 - 2)], $self->getAttributes($self->tokenStartStack[$stackPos - (2 - 1)], $self->tokenEndStack[$stackPos])); + }, 291 => static function ($self, $stackPos) { + $self->semValue = new Node\UnionType($self->semStack[$stackPos - (1 - 1)], $self->getAttributes($self->tokenStartStack[$stackPos - (1 - 1)], $self->tokenEndStack[$stackPos])); + }, 292 => null, 293 => null, 294 => static function ($self, $stackPos) { + $self->semValue = new Node\Name('static', $self->getAttributes($self->tokenStartStack[$stackPos - (1 - 1)], $self->tokenEndStack[$stackPos])); + }, 295 => static function ($self, $stackPos) { + $self->semValue = $self->handleBuiltinTypes($self->semStack[$stackPos - (1 - 1)]); + }, 296 => static function ($self, $stackPos) { + $self->semValue = new Node\Identifier('array', $self->getAttributes($self->tokenStartStack[$stackPos - (1 - 1)], $self->tokenEndStack[$stackPos])); + }, 297 => static function ($self, $stackPos) { + $self->semValue = new Node\Identifier('callable', $self->getAttributes($self->tokenStartStack[$stackPos - (1 - 1)], $self->tokenEndStack[$stackPos])); + }, 298 => null, 299 => static function ($self, $stackPos) { + $self->semValue = $self->semStack[$stackPos - (3 - 2)]; + }, 300 => static function ($self, $stackPos) { + $self->semValue = array($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)]); + }, 301 => static function ($self, $stackPos) { + $self->semStack[$stackPos - (3 - 1)][] = $self->semStack[$stackPos - (3 - 3)]; + $self->semValue = $self->semStack[$stackPos - (3 - 1)]; + }, 302 => null, 303 => static function ($self, $stackPos) { + $self->semValue = $self->semStack[$stackPos - (3 - 2)]; + }, 304 => static function ($self, $stackPos) { + $self->semValue = array($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)]); + }, 305 => static function ($self, $stackPos) { + $self->semStack[$stackPos - (3 - 1)][] = $self->semStack[$stackPos - (3 - 3)]; + $self->semValue = $self->semStack[$stackPos - (3 - 1)]; + }, 306 => static function ($self, $stackPos) { + $self->semValue = array($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)]); + }, 307 => static function ($self, $stackPos) { + $self->semStack[$stackPos - (3 - 1)][] = $self->semStack[$stackPos - (3 - 3)]; + $self->semValue = $self->semStack[$stackPos - (3 - 1)]; + }, 308 => static function ($self, $stackPos) { + $self->semValue = new Node\IntersectionType($self->semStack[$stackPos - (1 - 1)], $self->getAttributes($self->tokenStartStack[$stackPos - (1 - 1)], $self->tokenEndStack[$stackPos])); + }, 309 => static function ($self, $stackPos) { + $self->semValue = array($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)]); + }, 310 => static function ($self, $stackPos) { + $self->semStack[$stackPos - (3 - 1)][] = $self->semStack[$stackPos - (3 - 3)]; + $self->semValue = $self->semStack[$stackPos - (3 - 1)]; + }, 311 => static function ($self, $stackPos) { + $self->semValue = new Node\IntersectionType($self->semStack[$stackPos - (1 - 1)], $self->getAttributes($self->tokenStartStack[$stackPos - (1 - 1)], $self->tokenEndStack[$stackPos])); + }, 312 => null, 313 => static function ($self, $stackPos) { + $self->semValue = new Node\NullableType($self->semStack[$stackPos - (2 - 2)], $self->getAttributes($self->tokenStartStack[$stackPos - (2 - 1)], $self->tokenEndStack[$stackPos])); + }, 314 => static function ($self, $stackPos) { + $self->semValue = new Node\UnionType($self->semStack[$stackPos - (1 - 1)], $self->getAttributes($self->tokenStartStack[$stackPos - (1 - 1)], $self->tokenEndStack[$stackPos])); + }, 315 => null, 316 => static function ($self, $stackPos) { + $self->semValue = null; + }, 317 => null, 318 => static function ($self, $stackPos) { + $self->semValue = null; + }, 319 => static function ($self, $stackPos) { + $self->semValue = $self->semStack[$stackPos - (2 - 2)]; + }, 320 => static function ($self, $stackPos) { + $self->semValue = null; + }, 321 => static function ($self, $stackPos) { + $self->semValue = array(); + }, 322 => static function ($self, $stackPos) { + $self->semValue = $self->semStack[$stackPos - (4 - 2)]; + }, 323 => static function ($self, $stackPos) { + $self->semValue = array($self->semStack[$stackPos - (3 - 2)]); + }, 324 => static function ($self, $stackPos) { + $self->semValue = new Node\VariadicPlaceholder($self->getAttributes($self->tokenStartStack[$stackPos - (1 - 1)], $self->tokenEndStack[$stackPos])); + }, 325 => static function ($self, $stackPos) { + $self->semValue = array($self->semStack[$stackPos - (1 - 1)]); + }, 326 => static function ($self, $stackPos) { + $self->semStack[$stackPos - (3 - 1)][] = $self->semStack[$stackPos - (3 - 3)]; + $self->semValue = $self->semStack[$stackPos - (3 - 1)]; + }, 327 => static function ($self, $stackPos) { + $self->semValue = new Node\Arg($self->semStack[$stackPos - (1 - 1)], \false, \false, $self->getAttributes($self->tokenStartStack[$stackPos - (1 - 1)], $self->tokenEndStack[$stackPos])); + }, 328 => static function ($self, $stackPos) { + $self->semValue = new Node\Arg($self->semStack[$stackPos - (2 - 2)], \true, \false, $self->getAttributes($self->tokenStartStack[$stackPos - (2 - 1)], $self->tokenEndStack[$stackPos])); + }, 329 => static function ($self, $stackPos) { + $self->semValue = new Node\Arg($self->semStack[$stackPos - (2 - 2)], \false, \true, $self->getAttributes($self->tokenStartStack[$stackPos - (2 - 1)], $self->tokenEndStack[$stackPos])); + }, 330 => static function ($self, $stackPos) { + $self->semValue = new Node\Arg($self->semStack[$stackPos - (3 - 3)], \false, \false, $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos]), $self->semStack[$stackPos - (3 - 1)]); + }, 331 => null, 332 => static function ($self, $stackPos) { + $self->semStack[$stackPos - (3 - 1)][] = $self->semStack[$stackPos - (3 - 3)]; + $self->semValue = $self->semStack[$stackPos - (3 - 1)]; + }, 333 => static function ($self, $stackPos) { + $self->semValue = array($self->semStack[$stackPos - (1 - 1)]); + }, 334 => null, 335 => null, 336 => static function ($self, $stackPos) { + $self->semStack[$stackPos - (3 - 1)][] = $self->semStack[$stackPos - (3 - 3)]; + $self->semValue = $self->semStack[$stackPos - (3 - 1)]; + }, 337 => static function ($self, $stackPos) { + $self->semValue = array($self->semStack[$stackPos - (1 - 1)]); + }, 338 => static function ($self, $stackPos) { + $self->semValue = new Node\StaticVar($self->semStack[$stackPos - (1 - 1)], null, $self->getAttributes($self->tokenStartStack[$stackPos - (1 - 1)], $self->tokenEndStack[$stackPos])); + }, 339 => static function ($self, $stackPos) { + $self->semValue = new Node\StaticVar($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 340 => static function ($self, $stackPos) { + if ($self->semStack[$stackPos - (2 - 2)] !== null) { + $self->semStack[$stackPos - (2 - 1)][] = $self->semStack[$stackPos - (2 - 2)]; + $self->semValue = $self->semStack[$stackPos - (2 - 1)]; } else { - $nop = null; + $self->semValue = $self->semStack[$stackPos - (2 - 1)]; } + }, 341 => static function ($self, $stackPos) { + $self->semValue = array(); + }, 342 => static function ($self, $stackPos) { + $nop = $self->maybeCreateZeroLengthNop($self->tokenPos); if ($nop !== null) { - $this->semStack[$stackPos - (1 - 1)][] = $nop; - } - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 343 => function ($stackPos) { - $this->semValue = new Stmt\Property($this->semStack[$stackPos - (5 - 2)], $this->semStack[$stackPos - (5 - 4)], $this->startAttributeStack[$stackPos - (5 - 1)] + $this->endAttributes, $this->semStack[$stackPos - (5 - 3)], $this->semStack[$stackPos - (5 - 1)]); - $this->checkProperty($this->semValue, $stackPos - (5 - 2)); - }, 344 => function ($stackPos) { - $this->semValue = new Stmt\ClassConst($this->semStack[$stackPos - (5 - 4)], $this->semStack[$stackPos - (5 - 2)], $this->startAttributeStack[$stackPos - (5 - 1)] + $this->endAttributes, $this->semStack[$stackPos - (5 - 1)]); - $this->checkClassConst($this->semValue, $stackPos - (5 - 2)); - }, 345 => function ($stackPos) { - $this->semValue = new Stmt\ClassConst($this->semStack[$stackPos - (6 - 5)], $this->semStack[$stackPos - (6 - 2)], $this->startAttributeStack[$stackPos - (6 - 1)] + $this->endAttributes, $this->semStack[$stackPos - (6 - 1)], $this->semStack[$stackPos - (6 - 4)]); - $this->checkClassConst($this->semValue, $stackPos - (6 - 2)); - }, 346 => function ($stackPos) { - $this->semValue = new Stmt\ClassMethod($this->semStack[$stackPos - (10 - 5)], ['type' => $this->semStack[$stackPos - (10 - 2)], 'byRef' => $this->semStack[$stackPos - (10 - 4)], 'params' => $this->semStack[$stackPos - (10 - 7)], 'returnType' => $this->semStack[$stackPos - (10 - 9)], 'stmts' => $this->semStack[$stackPos - (10 - 10)], 'attrGroups' => $this->semStack[$stackPos - (10 - 1)]], $this->startAttributeStack[$stackPos - (10 - 1)] + $this->endAttributes); - $this->checkClassMethod($this->semValue, $stackPos - (10 - 2)); - }, 347 => function ($stackPos) { - $this->semValue = new Stmt\TraitUse($this->semStack[$stackPos - (3 - 2)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 348 => function ($stackPos) { - $this->semValue = new Stmt\EnumCase($this->semStack[$stackPos - (5 - 3)], $this->semStack[$stackPos - (5 - 4)], $this->semStack[$stackPos - (5 - 1)], $this->startAttributeStack[$stackPos - (5 - 1)] + $this->endAttributes); - }, 349 => function ($stackPos) { - $this->semValue = null; + $self->semStack[$stackPos - (1 - 1)][] = $nop; + } + $self->semValue = $self->semStack[$stackPos - (1 - 1)]; + }, 343 => static function ($self, $stackPos) { + $self->semValue = new Stmt\Property($self->semStack[$stackPos - (5 - 2)], $self->semStack[$stackPos - (5 - 4)], $self->getAttributes($self->tokenStartStack[$stackPos - (5 - 1)], $self->tokenEndStack[$stackPos]), $self->semStack[$stackPos - (5 - 3)], $self->semStack[$stackPos - (5 - 1)]); + $self->checkProperty($self->semValue, $stackPos - (5 - 2)); + }, 344 => static function ($self, $stackPos) { + $self->semValue = new Stmt\ClassConst($self->semStack[$stackPos - (5 - 4)], $self->semStack[$stackPos - (5 - 2)], $self->getAttributes($self->tokenStartStack[$stackPos - (5 - 1)], $self->tokenEndStack[$stackPos]), $self->semStack[$stackPos - (5 - 1)]); + $self->checkClassConst($self->semValue, $stackPos - (5 - 2)); + }, 345 => static function ($self, $stackPos) { + $self->semValue = new Stmt\ClassConst($self->semStack[$stackPos - (6 - 5)], $self->semStack[$stackPos - (6 - 2)], $self->getAttributes($self->tokenStartStack[$stackPos - (6 - 1)], $self->tokenEndStack[$stackPos]), $self->semStack[$stackPos - (6 - 1)], $self->semStack[$stackPos - (6 - 4)]); + $self->checkClassConst($self->semValue, $stackPos - (6 - 2)); + }, 346 => static function ($self, $stackPos) { + $self->semValue = new Stmt\ClassMethod($self->semStack[$stackPos - (10 - 5)], ['type' => $self->semStack[$stackPos - (10 - 2)], 'byRef' => $self->semStack[$stackPos - (10 - 4)], 'params' => $self->semStack[$stackPos - (10 - 7)], 'returnType' => $self->semStack[$stackPos - (10 - 9)], 'stmts' => $self->semStack[$stackPos - (10 - 10)], 'attrGroups' => $self->semStack[$stackPos - (10 - 1)]], $self->getAttributes($self->tokenStartStack[$stackPos - (10 - 1)], $self->tokenEndStack[$stackPos])); + $self->checkClassMethod($self->semValue, $stackPos - (10 - 2)); + }, 347 => static function ($self, $stackPos) { + $self->semValue = new Stmt\TraitUse($self->semStack[$stackPos - (3 - 2)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 348 => static function ($self, $stackPos) { + $self->semValue = new Stmt\EnumCase($self->semStack[$stackPos - (5 - 3)], $self->semStack[$stackPos - (5 - 4)], $self->semStack[$stackPos - (5 - 1)], $self->getAttributes($self->tokenStartStack[$stackPos - (5 - 1)], $self->tokenEndStack[$stackPos])); + }, 349 => static function ($self, $stackPos) { + $self->semValue = null; /* will be skipped */ - }, 350 => function ($stackPos) { - $this->semValue = array(); - }, 351 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (3 - 2)]; - }, 352 => function ($stackPos) { - $this->semValue = array(); - }, 353 => function ($stackPos) { - $this->semStack[$stackPos - (2 - 1)][] = $this->semStack[$stackPos - (2 - 2)]; - $this->semValue = $this->semStack[$stackPos - (2 - 1)]; - }, 354 => function ($stackPos) { - $this->semValue = new Stmt\TraitUseAdaptation\Precedence($this->semStack[$stackPos - (4 - 1)][0], $this->semStack[$stackPos - (4 - 1)][1], $this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 355 => function ($stackPos) { - $this->semValue = new Stmt\TraitUseAdaptation\Alias($this->semStack[$stackPos - (5 - 1)][0], $this->semStack[$stackPos - (5 - 1)][1], $this->semStack[$stackPos - (5 - 3)], $this->semStack[$stackPos - (5 - 4)], $this->startAttributeStack[$stackPos - (5 - 1)] + $this->endAttributes); - }, 356 => function ($stackPos) { - $this->semValue = new Stmt\TraitUseAdaptation\Alias($this->semStack[$stackPos - (4 - 1)][0], $this->semStack[$stackPos - (4 - 1)][1], $this->semStack[$stackPos - (4 - 3)], null, $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 357 => function ($stackPos) { - $this->semValue = new Stmt\TraitUseAdaptation\Alias($this->semStack[$stackPos - (4 - 1)][0], $this->semStack[$stackPos - (4 - 1)][1], null, $this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 358 => function ($stackPos) { - $this->semValue = new Stmt\TraitUseAdaptation\Alias($this->semStack[$stackPos - (4 - 1)][0], $this->semStack[$stackPos - (4 - 1)][1], null, $this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 359 => function ($stackPos) { - $this->semValue = array($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)]); - }, 360 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 361 => function ($stackPos) { - $this->semValue = array(null, $this->semStack[$stackPos - (1 - 1)]); - }, 362 => function ($stackPos) { - $this->semValue = null; - }, 363 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 364 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 365 => function ($stackPos) { - $this->semValue = 0; - }, 366 => function ($stackPos) { - $this->semValue = 0; - }, 367 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 368 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 369 => function ($stackPos) { - $this->checkModifier($this->semStack[$stackPos - (2 - 1)], $this->semStack[$stackPos - (2 - 2)], $stackPos - (2 - 2)); - $this->semValue = $this->semStack[$stackPos - (2 - 1)] | $this->semStack[$stackPos - (2 - 2)]; - }, 370 => function ($stackPos) { - $this->semValue = Stmt\Class_::MODIFIER_PUBLIC; - }, 371 => function ($stackPos) { - $this->semValue = Stmt\Class_::MODIFIER_PROTECTED; - }, 372 => function ($stackPos) { - $this->semValue = Stmt\Class_::MODIFIER_PRIVATE; - }, 373 => function ($stackPos) { - $this->semValue = Stmt\Class_::MODIFIER_STATIC; - }, 374 => function ($stackPos) { - $this->semValue = Stmt\Class_::MODIFIER_ABSTRACT; - }, 375 => function ($stackPos) { - $this->semValue = Stmt\Class_::MODIFIER_FINAL; - }, 376 => function ($stackPos) { - $this->semValue = Stmt\Class_::MODIFIER_READONLY; - }, 377 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (2 - 1)]; - }, 378 => function ($stackPos) { - $this->semValue = array($this->semStack[$stackPos - (1 - 1)]); - }, 379 => function ($stackPos) { - $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; - $this->semValue = $this->semStack[$stackPos - (3 - 1)]; - }, 380 => function ($stackPos) { - $this->semValue = new Node\VarLikeIdentifier(\substr($this->semStack[$stackPos - (1 - 1)], 1), $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 381 => function ($stackPos) { - $this->semValue = new Stmt\PropertyProperty($this->semStack[$stackPos - (1 - 1)], null, $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 382 => function ($stackPos) { - $this->semValue = new Stmt\PropertyProperty($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 383 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (2 - 1)]; - }, 384 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (2 - 1)]; - }, 385 => function ($stackPos) { - $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; - $this->semValue = $this->semStack[$stackPos - (3 - 1)]; - }, 386 => function ($stackPos) { - $this->semValue = array($this->semStack[$stackPos - (1 - 1)]); - }, 387 => function ($stackPos) { - $this->semValue = array(); - }, 388 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 389 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 390 => function ($stackPos) { - $this->semValue = new Expr\Assign($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 391 => function ($stackPos) { - $this->semValue = new Expr\Assign($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 392 => function ($stackPos) { - $this->semValue = new Expr\Assign($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 393 => function ($stackPos) { - $this->semValue = new Expr\AssignRef($this->semStack[$stackPos - (4 - 1)], $this->semStack[$stackPos - (4 - 4)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 394 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 395 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 396 => function ($stackPos) { - $this->semValue = new Expr\Clone_($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 397 => function ($stackPos) { - $this->semValue = new Expr\AssignOp\Plus($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 398 => function ($stackPos) { - $this->semValue = new Expr\AssignOp\Minus($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 399 => function ($stackPos) { - $this->semValue = new Expr\AssignOp\Mul($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 400 => function ($stackPos) { - $this->semValue = new Expr\AssignOp\Div($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 401 => function ($stackPos) { - $this->semValue = new Expr\AssignOp\Concat($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 402 => function ($stackPos) { - $this->semValue = new Expr\AssignOp\Mod($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 403 => function ($stackPos) { - $this->semValue = new Expr\AssignOp\BitwiseAnd($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 404 => function ($stackPos) { - $this->semValue = new Expr\AssignOp\BitwiseOr($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 405 => function ($stackPos) { - $this->semValue = new Expr\AssignOp\BitwiseXor($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 406 => function ($stackPos) { - $this->semValue = new Expr\AssignOp\ShiftLeft($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 407 => function ($stackPos) { - $this->semValue = new Expr\AssignOp\ShiftRight($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 408 => function ($stackPos) { - $this->semValue = new Expr\AssignOp\Pow($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 409 => function ($stackPos) { - $this->semValue = new Expr\AssignOp\Coalesce($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 410 => function ($stackPos) { - $this->semValue = new Expr\PostInc($this->semStack[$stackPos - (2 - 1)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 411 => function ($stackPos) { - $this->semValue = new Expr\PreInc($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 412 => function ($stackPos) { - $this->semValue = new Expr\PostDec($this->semStack[$stackPos - (2 - 1)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 413 => function ($stackPos) { - $this->semValue = new Expr\PreDec($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 414 => function ($stackPos) { - $this->semValue = new Expr\BinaryOp\BooleanOr($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 415 => function ($stackPos) { - $this->semValue = new Expr\BinaryOp\BooleanAnd($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 416 => function ($stackPos) { - $this->semValue = new Expr\BinaryOp\LogicalOr($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 417 => function ($stackPos) { - $this->semValue = new Expr\BinaryOp\LogicalAnd($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 418 => function ($stackPos) { - $this->semValue = new Expr\BinaryOp\LogicalXor($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 419 => function ($stackPos) { - $this->semValue = new Expr\BinaryOp\BitwiseOr($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 420 => function ($stackPos) { - $this->semValue = new Expr\BinaryOp\BitwiseAnd($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 421 => function ($stackPos) { - $this->semValue = new Expr\BinaryOp\BitwiseAnd($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 422 => function ($stackPos) { - $this->semValue = new Expr\BinaryOp\BitwiseXor($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 423 => function ($stackPos) { - $this->semValue = new Expr\BinaryOp\Concat($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 424 => function ($stackPos) { - $this->semValue = new Expr\BinaryOp\Plus($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 425 => function ($stackPos) { - $this->semValue = new Expr\BinaryOp\Minus($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 426 => function ($stackPos) { - $this->semValue = new Expr\BinaryOp\Mul($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 427 => function ($stackPos) { - $this->semValue = new Expr\BinaryOp\Div($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 428 => function ($stackPos) { - $this->semValue = new Expr\BinaryOp\Mod($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 429 => function ($stackPos) { - $this->semValue = new Expr\BinaryOp\ShiftLeft($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 430 => function ($stackPos) { - $this->semValue = new Expr\BinaryOp\ShiftRight($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 431 => function ($stackPos) { - $this->semValue = new Expr\BinaryOp\Pow($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 432 => function ($stackPos) { - $this->semValue = new Expr\UnaryPlus($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 433 => function ($stackPos) { - $this->semValue = new Expr\UnaryMinus($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 434 => function ($stackPos) { - $this->semValue = new Expr\BooleanNot($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 435 => function ($stackPos) { - $this->semValue = new Expr\BitwiseNot($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 436 => function ($stackPos) { - $this->semValue = new Expr\BinaryOp\Identical($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 437 => function ($stackPos) { - $this->semValue = new Expr\BinaryOp\NotIdentical($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 438 => function ($stackPos) { - $this->semValue = new Expr\BinaryOp\Equal($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 439 => function ($stackPos) { - $this->semValue = new Expr\BinaryOp\NotEqual($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 440 => function ($stackPos) { - $this->semValue = new Expr\BinaryOp\Spaceship($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 441 => function ($stackPos) { - $this->semValue = new Expr\BinaryOp\Smaller($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 442 => function ($stackPos) { - $this->semValue = new Expr\BinaryOp\SmallerOrEqual($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 443 => function ($stackPos) { - $this->semValue = new Expr\BinaryOp\Greater($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 444 => function ($stackPos) { - $this->semValue = new Expr\BinaryOp\GreaterOrEqual($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 445 => function ($stackPos) { - $this->semValue = new Expr\Instanceof_($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 446 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (3 - 2)]; - }, 447 => function ($stackPos) { - $this->semValue = new Expr\Ternary($this->semStack[$stackPos - (5 - 1)], $this->semStack[$stackPos - (5 - 3)], $this->semStack[$stackPos - (5 - 5)], $this->startAttributeStack[$stackPos - (5 - 1)] + $this->endAttributes); - }, 448 => function ($stackPos) { - $this->semValue = new Expr\Ternary($this->semStack[$stackPos - (4 - 1)], null, $this->semStack[$stackPos - (4 - 4)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 449 => function ($stackPos) { - $this->semValue = new Expr\BinaryOp\Coalesce($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 450 => function ($stackPos) { - $this->semValue = new Expr\Isset_($this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 451 => function ($stackPos) { - $this->semValue = new Expr\Empty_($this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 452 => function ($stackPos) { - $this->semValue = new Expr\Include_($this->semStack[$stackPos - (2 - 2)], Expr\Include_::TYPE_INCLUDE, $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 453 => function ($stackPos) { - $this->semValue = new Expr\Include_($this->semStack[$stackPos - (2 - 2)], Expr\Include_::TYPE_INCLUDE_ONCE, $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 454 => function ($stackPos) { - $this->semValue = new Expr\Eval_($this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 455 => function ($stackPos) { - $this->semValue = new Expr\Include_($this->semStack[$stackPos - (2 - 2)], Expr\Include_::TYPE_REQUIRE, $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 456 => function ($stackPos) { - $this->semValue = new Expr\Include_($this->semStack[$stackPos - (2 - 2)], Expr\Include_::TYPE_REQUIRE_ONCE, $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 457 => function ($stackPos) { - $this->semValue = new Expr\Cast\Int_($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 458 => function ($stackPos) { - $attrs = $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes; - $attrs['kind'] = $this->getFloatCastKind($this->semStack[$stackPos - (2 - 1)]); - $this->semValue = new Expr\Cast\Double($this->semStack[$stackPos - (2 - 2)], $attrs); - }, 459 => function ($stackPos) { - $this->semValue = new Expr\Cast\String_($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 460 => function ($stackPos) { - $this->semValue = new Expr\Cast\Array_($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 461 => function ($stackPos) { - $this->semValue = new Expr\Cast\Object_($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 462 => function ($stackPos) { - $this->semValue = new Expr\Cast\Bool_($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 463 => function ($stackPos) { - $this->semValue = new Expr\Cast\Unset_($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 464 => function ($stackPos) { - $attrs = $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes; - $attrs['kind'] = \strtolower($this->semStack[$stackPos - (2 - 1)]) === 'exit' ? Expr\Exit_::KIND_EXIT : Expr\Exit_::KIND_DIE; - $this->semValue = new Expr\Exit_($this->semStack[$stackPos - (2 - 2)], $attrs); - }, 465 => function ($stackPos) { - $this->semValue = new Expr\ErrorSuppress($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 466 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 467 => function ($stackPos) { - $this->semValue = new Expr\ShellExec($this->semStack[$stackPos - (3 - 2)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 468 => function ($stackPos) { - $this->semValue = new Expr\Print_($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 469 => function ($stackPos) { - $this->semValue = new Expr\Yield_(null, null, $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 470 => function ($stackPos) { - $this->semValue = new Expr\Yield_($this->semStack[$stackPos - (2 - 2)], null, $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 471 => function ($stackPos) { - $this->semValue = new Expr\Yield_($this->semStack[$stackPos - (4 - 4)], $this->semStack[$stackPos - (4 - 2)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 472 => function ($stackPos) { - $this->semValue = new Expr\YieldFrom($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 473 => function ($stackPos) { - $this->semValue = new Expr\Throw_($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 474 => function ($stackPos) { - $this->semValue = new Expr\ArrowFunction(['static' => \false, 'byRef' => $this->semStack[$stackPos - (8 - 2)], 'params' => $this->semStack[$stackPos - (8 - 4)], 'returnType' => $this->semStack[$stackPos - (8 - 6)], 'expr' => $this->semStack[$stackPos - (8 - 8)], 'attrGroups' => []], $this->startAttributeStack[$stackPos - (8 - 1)] + $this->endAttributes); - }, 475 => function ($stackPos) { - $this->semValue = new Expr\ArrowFunction(['static' => \true, 'byRef' => $this->semStack[$stackPos - (9 - 3)], 'params' => $this->semStack[$stackPos - (9 - 5)], 'returnType' => $this->semStack[$stackPos - (9 - 7)], 'expr' => $this->semStack[$stackPos - (9 - 9)], 'attrGroups' => []], $this->startAttributeStack[$stackPos - (9 - 1)] + $this->endAttributes); - }, 476 => function ($stackPos) { - $this->semValue = new Expr\Closure(['static' => \false, 'byRef' => $this->semStack[$stackPos - (8 - 2)], 'params' => $this->semStack[$stackPos - (8 - 4)], 'uses' => $this->semStack[$stackPos - (8 - 6)], 'returnType' => $this->semStack[$stackPos - (8 - 7)], 'stmts' => $this->semStack[$stackPos - (8 - 8)], 'attrGroups' => []], $this->startAttributeStack[$stackPos - (8 - 1)] + $this->endAttributes); - }, 477 => function ($stackPos) { - $this->semValue = new Expr\Closure(['static' => \true, 'byRef' => $this->semStack[$stackPos - (9 - 3)], 'params' => $this->semStack[$stackPos - (9 - 5)], 'uses' => $this->semStack[$stackPos - (9 - 7)], 'returnType' => $this->semStack[$stackPos - (9 - 8)], 'stmts' => $this->semStack[$stackPos - (9 - 9)], 'attrGroups' => []], $this->startAttributeStack[$stackPos - (9 - 1)] + $this->endAttributes); - }, 478 => function ($stackPos) { - $this->semValue = new Expr\ArrowFunction(['static' => \false, 'byRef' => $this->semStack[$stackPos - (9 - 3)], 'params' => $this->semStack[$stackPos - (9 - 5)], 'returnType' => $this->semStack[$stackPos - (9 - 7)], 'expr' => $this->semStack[$stackPos - (9 - 9)], 'attrGroups' => $this->semStack[$stackPos - (9 - 1)]], $this->startAttributeStack[$stackPos - (9 - 1)] + $this->endAttributes); - }, 479 => function ($stackPos) { - $this->semValue = new Expr\ArrowFunction(['static' => \true, 'byRef' => $this->semStack[$stackPos - (10 - 4)], 'params' => $this->semStack[$stackPos - (10 - 6)], 'returnType' => $this->semStack[$stackPos - (10 - 8)], 'expr' => $this->semStack[$stackPos - (10 - 10)], 'attrGroups' => $this->semStack[$stackPos - (10 - 1)]], $this->startAttributeStack[$stackPos - (10 - 1)] + $this->endAttributes); - }, 480 => function ($stackPos) { - $this->semValue = new Expr\Closure(['static' => \false, 'byRef' => $this->semStack[$stackPos - (9 - 3)], 'params' => $this->semStack[$stackPos - (9 - 5)], 'uses' => $this->semStack[$stackPos - (9 - 7)], 'returnType' => $this->semStack[$stackPos - (9 - 8)], 'stmts' => $this->semStack[$stackPos - (9 - 9)], 'attrGroups' => $this->semStack[$stackPos - (9 - 1)]], $this->startAttributeStack[$stackPos - (9 - 1)] + $this->endAttributes); - }, 481 => function ($stackPos) { - $this->semValue = new Expr\Closure(['static' => \true, 'byRef' => $this->semStack[$stackPos - (10 - 4)], 'params' => $this->semStack[$stackPos - (10 - 6)], 'uses' => $this->semStack[$stackPos - (10 - 8)], 'returnType' => $this->semStack[$stackPos - (10 - 9)], 'stmts' => $this->semStack[$stackPos - (10 - 10)], 'attrGroups' => $this->semStack[$stackPos - (10 - 1)]], $this->startAttributeStack[$stackPos - (10 - 1)] + $this->endAttributes); - }, 482 => function ($stackPos) { - $this->semValue = array(new Stmt\Class_(null, ['type' => $this->semStack[$stackPos - (8 - 2)], 'extends' => $this->semStack[$stackPos - (8 - 4)], 'implements' => $this->semStack[$stackPos - (8 - 5)], 'stmts' => $this->semStack[$stackPos - (8 - 7)], 'attrGroups' => $this->semStack[$stackPos - (8 - 1)]], $this->startAttributeStack[$stackPos - (8 - 1)] + $this->endAttributes), $this->semStack[$stackPos - (8 - 3)]); - $this->checkClass($this->semValue[0], -1); - }, 483 => function ($stackPos) { - $this->semValue = new Expr\New_($this->semStack[$stackPos - (3 - 2)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 484 => function ($stackPos) { - list($class, $ctorArgs) = $this->semStack[$stackPos - (2 - 2)]; - $this->semValue = new Expr\New_($class, $ctorArgs, $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 485 => function ($stackPos) { - $this->semValue = array(); - }, 486 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (4 - 3)]; - }, 487 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (2 - 1)]; - }, 488 => function ($stackPos) { - $this->semValue = array($this->semStack[$stackPos - (1 - 1)]); - }, 489 => function ($stackPos) { - $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; - $this->semValue = $this->semStack[$stackPos - (3 - 1)]; - }, 490 => function ($stackPos) { - $this->semValue = new Expr\ClosureUse($this->semStack[$stackPos - (2 - 2)], $this->semStack[$stackPos - (2 - 1)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 491 => function ($stackPos) { - $this->semValue = new Name($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 492 => function ($stackPos) { - $this->semValue = new Expr\FuncCall($this->semStack[$stackPos - (2 - 1)], $this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 493 => function ($stackPos) { - $this->semValue = new Expr\FuncCall($this->semStack[$stackPos - (2 - 1)], $this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 494 => function ($stackPos) { - $this->semValue = new Expr\FuncCall($this->semStack[$stackPos - (2 - 1)], $this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 495 => function ($stackPos) { - $this->semValue = new Expr\StaticCall($this->semStack[$stackPos - (4 - 1)], $this->semStack[$stackPos - (4 - 3)], $this->semStack[$stackPos - (4 - 4)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 496 => function ($stackPos) { - $this->semValue = new Name($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 497 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 498 => function ($stackPos) { - $this->semValue = new Name($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 499 => function ($stackPos) { - $this->semValue = new Name($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 500 => function ($stackPos) { - $this->semValue = new Name\FullyQualified(\substr($this->semStack[$stackPos - (1 - 1)], 1), $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 501 => function ($stackPos) { - $this->semValue = new Name\Relative(\substr($this->semStack[$stackPos - (1 - 1)], 10), $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 502 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 503 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 504 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (3 - 2)]; - }, 505 => function ($stackPos) { - $this->semValue = new Expr\Error($this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - $this->errorState = 2; - }, 506 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 507 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 508 => function ($stackPos) { - $this->semValue = null; - }, 509 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (3 - 2)]; - }, 510 => function ($stackPos) { - $this->semValue = array(); - }, 511 => function ($stackPos) { - $this->semValue = array(new Scalar\EncapsedStringPart(Scalar\String_::parseEscapeSequences($this->semStack[$stackPos - (1 - 1)], '`'), $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes)); - }, 512 => function ($stackPos) { - foreach ($this->semStack[$stackPos - (1 - 1)] as $s) { - if ($s instanceof Node\Scalar\EncapsedStringPart) { - $s->value = Node\Scalar\String_::parseEscapeSequences($s->value, '`', \true); - } - } - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 513 => function ($stackPos) { - $this->semValue = array(); - }, 514 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 515 => function ($stackPos) { - $this->semValue = new Expr\ConstFetch($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 516 => function ($stackPos) { - $this->semValue = new Scalar\MagicConst\Line($this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 517 => function ($stackPos) { - $this->semValue = new Scalar\MagicConst\File($this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 518 => function ($stackPos) { - $this->semValue = new Scalar\MagicConst\Dir($this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 519 => function ($stackPos) { - $this->semValue = new Scalar\MagicConst\Class_($this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 520 => function ($stackPos) { - $this->semValue = new Scalar\MagicConst\Trait_($this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 521 => function ($stackPos) { - $this->semValue = new Scalar\MagicConst\Method($this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 522 => function ($stackPos) { - $this->semValue = new Scalar\MagicConst\Function_($this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 523 => function ($stackPos) { - $this->semValue = new Scalar\MagicConst\Namespace_($this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 524 => function ($stackPos) { - $this->semValue = new Expr\ClassConstFetch($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 525 => function ($stackPos) { - $this->semValue = new Expr\ClassConstFetch($this->semStack[$stackPos - (5 - 1)], $this->semStack[$stackPos - (5 - 4)], $this->startAttributeStack[$stackPos - (5 - 1)] + $this->endAttributes); - }, 526 => function ($stackPos) { - $this->semValue = new Expr\ClassConstFetch($this->semStack[$stackPos - (3 - 1)], new Expr\Error($this->startAttributeStack[$stackPos - (3 - 3)] + $this->endAttributeStack[$stackPos - (3 - 3)]), $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - $this->errorState = 2; - }, 527 => function ($stackPos) { - $attrs = $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes; + }, 350 => static function ($self, $stackPos) { + $self->semValue = array(); + }, 351 => static function ($self, $stackPos) { + $self->semValue = $self->semStack[$stackPos - (3 - 2)]; + }, 352 => static function ($self, $stackPos) { + $self->semValue = array(); + }, 353 => static function ($self, $stackPos) { + $self->semStack[$stackPos - (2 - 1)][] = $self->semStack[$stackPos - (2 - 2)]; + $self->semValue = $self->semStack[$stackPos - (2 - 1)]; + }, 354 => static function ($self, $stackPos) { + $self->semValue = new Stmt\TraitUseAdaptation\Precedence($self->semStack[$stackPos - (4 - 1)][0], $self->semStack[$stackPos - (4 - 1)][1], $self->semStack[$stackPos - (4 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (4 - 1)], $self->tokenEndStack[$stackPos])); + }, 355 => static function ($self, $stackPos) { + $self->semValue = new Stmt\TraitUseAdaptation\Alias($self->semStack[$stackPos - (5 - 1)][0], $self->semStack[$stackPos - (5 - 1)][1], $self->semStack[$stackPos - (5 - 3)], $self->semStack[$stackPos - (5 - 4)], $self->getAttributes($self->tokenStartStack[$stackPos - (5 - 1)], $self->tokenEndStack[$stackPos])); + }, 356 => static function ($self, $stackPos) { + $self->semValue = new Stmt\TraitUseAdaptation\Alias($self->semStack[$stackPos - (4 - 1)][0], $self->semStack[$stackPos - (4 - 1)][1], $self->semStack[$stackPos - (4 - 3)], null, $self->getAttributes($self->tokenStartStack[$stackPos - (4 - 1)], $self->tokenEndStack[$stackPos])); + }, 357 => static function ($self, $stackPos) { + $self->semValue = new Stmt\TraitUseAdaptation\Alias($self->semStack[$stackPos - (4 - 1)][0], $self->semStack[$stackPos - (4 - 1)][1], null, $self->semStack[$stackPos - (4 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (4 - 1)], $self->tokenEndStack[$stackPos])); + }, 358 => static function ($self, $stackPos) { + $self->semValue = new Stmt\TraitUseAdaptation\Alias($self->semStack[$stackPos - (4 - 1)][0], $self->semStack[$stackPos - (4 - 1)][1], null, $self->semStack[$stackPos - (4 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (4 - 1)], $self->tokenEndStack[$stackPos])); + }, 359 => static function ($self, $stackPos) { + $self->semValue = array($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)]); + }, 360 => null, 361 => static function ($self, $stackPos) { + $self->semValue = array(null, $self->semStack[$stackPos - (1 - 1)]); + }, 362 => static function ($self, $stackPos) { + $self->semValue = null; + }, 363 => null, 364 => null, 365 => static function ($self, $stackPos) { + $self->semValue = 0; + }, 366 => static function ($self, $stackPos) { + $self->semValue = 0; + }, 367 => null, 368 => null, 369 => static function ($self, $stackPos) { + $self->checkModifier($self->semStack[$stackPos - (2 - 1)], $self->semStack[$stackPos - (2 - 2)], $stackPos - (2 - 2)); + $self->semValue = $self->semStack[$stackPos - (2 - 1)] | $self->semStack[$stackPos - (2 - 2)]; + }, 370 => static function ($self, $stackPos) { + $self->semValue = Modifiers::PUBLIC; + }, 371 => static function ($self, $stackPos) { + $self->semValue = Modifiers::PROTECTED; + }, 372 => static function ($self, $stackPos) { + $self->semValue = Modifiers::PRIVATE; + }, 373 => static function ($self, $stackPos) { + $self->semValue = Modifiers::STATIC; + }, 374 => static function ($self, $stackPos) { + $self->semValue = Modifiers::ABSTRACT; + }, 375 => static function ($self, $stackPos) { + $self->semValue = Modifiers::FINAL; + }, 376 => static function ($self, $stackPos) { + $self->semValue = Modifiers::READONLY; + }, 377 => null, 378 => static function ($self, $stackPos) { + $self->semValue = array($self->semStack[$stackPos - (1 - 1)]); + }, 379 => static function ($self, $stackPos) { + $self->semStack[$stackPos - (3 - 1)][] = $self->semStack[$stackPos - (3 - 3)]; + $self->semValue = $self->semStack[$stackPos - (3 - 1)]; + }, 380 => static function ($self, $stackPos) { + $self->semValue = new Node\VarLikeIdentifier(substr($self->semStack[$stackPos - (1 - 1)], 1), $self->getAttributes($self->tokenStartStack[$stackPos - (1 - 1)], $self->tokenEndStack[$stackPos])); + }, 381 => static function ($self, $stackPos) { + $self->semValue = new Node\PropertyItem($self->semStack[$stackPos - (1 - 1)], null, $self->getAttributes($self->tokenStartStack[$stackPos - (1 - 1)], $self->tokenEndStack[$stackPos])); + }, 382 => static function ($self, $stackPos) { + $self->semValue = new Node\PropertyItem($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 383 => null, 384 => null, 385 => static function ($self, $stackPos) { + $self->semStack[$stackPos - (3 - 1)][] = $self->semStack[$stackPos - (3 - 3)]; + $self->semValue = $self->semStack[$stackPos - (3 - 1)]; + }, 386 => static function ($self, $stackPos) { + $self->semValue = array($self->semStack[$stackPos - (1 - 1)]); + }, 387 => static function ($self, $stackPos) { + $self->semValue = array(); + }, 388 => null, 389 => null, 390 => static function ($self, $stackPos) { + $self->semValue = new Expr\Assign($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 391 => static function ($self, $stackPos) { + $self->semValue = new Expr\Assign($self->fixupArrayDestructuring($self->semStack[$stackPos - (3 - 1)]), $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 392 => static function ($self, $stackPos) { + $self->semValue = new Expr\Assign($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 393 => static function ($self, $stackPos) { + $self->semValue = new Expr\AssignRef($self->semStack[$stackPos - (4 - 1)], $self->semStack[$stackPos - (4 - 4)], $self->getAttributes($self->tokenStartStack[$stackPos - (4 - 1)], $self->tokenEndStack[$stackPos])); + }, 394 => static function ($self, $stackPos) { + $self->semValue = new Expr\AssignRef($self->semStack[$stackPos - (4 - 1)], $self->semStack[$stackPos - (4 - 4)], $self->getAttributes($self->tokenStartStack[$stackPos - (4 - 1)], $self->tokenEndStack[$stackPos])); + if (!$self->phpVersion->allowsAssignNewByReference()) { + $self->emitError(new Error('Cannot assign new by reference', $self->getAttributes($self->tokenStartStack[$stackPos - (4 - 1)], $self->tokenEndStack[$stackPos]))); + } + }, 395 => null, 396 => null, 397 => static function ($self, $stackPos) { + $self->semValue = new Expr\Clone_($self->semStack[$stackPos - (2 - 2)], $self->getAttributes($self->tokenStartStack[$stackPos - (2 - 1)], $self->tokenEndStack[$stackPos])); + }, 398 => static function ($self, $stackPos) { + $self->semValue = new Expr\AssignOp\Plus($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 399 => static function ($self, $stackPos) { + $self->semValue = new Expr\AssignOp\Minus($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 400 => static function ($self, $stackPos) { + $self->semValue = new Expr\AssignOp\Mul($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 401 => static function ($self, $stackPos) { + $self->semValue = new Expr\AssignOp\Div($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 402 => static function ($self, $stackPos) { + $self->semValue = new Expr\AssignOp\Concat($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 403 => static function ($self, $stackPos) { + $self->semValue = new Expr\AssignOp\Mod($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 404 => static function ($self, $stackPos) { + $self->semValue = new Expr\AssignOp\BitwiseAnd($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 405 => static function ($self, $stackPos) { + $self->semValue = new Expr\AssignOp\BitwiseOr($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 406 => static function ($self, $stackPos) { + $self->semValue = new Expr\AssignOp\BitwiseXor($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 407 => static function ($self, $stackPos) { + $self->semValue = new Expr\AssignOp\ShiftLeft($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 408 => static function ($self, $stackPos) { + $self->semValue = new Expr\AssignOp\ShiftRight($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 409 => static function ($self, $stackPos) { + $self->semValue = new Expr\AssignOp\Pow($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 410 => static function ($self, $stackPos) { + $self->semValue = new Expr\AssignOp\Coalesce($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 411 => static function ($self, $stackPos) { + $self->semValue = new Expr\PostInc($self->semStack[$stackPos - (2 - 1)], $self->getAttributes($self->tokenStartStack[$stackPos - (2 - 1)], $self->tokenEndStack[$stackPos])); + }, 412 => static function ($self, $stackPos) { + $self->semValue = new Expr\PreInc($self->semStack[$stackPos - (2 - 2)], $self->getAttributes($self->tokenStartStack[$stackPos - (2 - 1)], $self->tokenEndStack[$stackPos])); + }, 413 => static function ($self, $stackPos) { + $self->semValue = new Expr\PostDec($self->semStack[$stackPos - (2 - 1)], $self->getAttributes($self->tokenStartStack[$stackPos - (2 - 1)], $self->tokenEndStack[$stackPos])); + }, 414 => static function ($self, $stackPos) { + $self->semValue = new Expr\PreDec($self->semStack[$stackPos - (2 - 2)], $self->getAttributes($self->tokenStartStack[$stackPos - (2 - 1)], $self->tokenEndStack[$stackPos])); + }, 415 => static function ($self, $stackPos) { + $self->semValue = new Expr\BinaryOp\BooleanOr($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 416 => static function ($self, $stackPos) { + $self->semValue = new Expr\BinaryOp\BooleanAnd($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 417 => static function ($self, $stackPos) { + $self->semValue = new Expr\BinaryOp\LogicalOr($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 418 => static function ($self, $stackPos) { + $self->semValue = new Expr\BinaryOp\LogicalAnd($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 419 => static function ($self, $stackPos) { + $self->semValue = new Expr\BinaryOp\LogicalXor($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 420 => static function ($self, $stackPos) { + $self->semValue = new Expr\BinaryOp\BitwiseOr($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 421 => static function ($self, $stackPos) { + $self->semValue = new Expr\BinaryOp\BitwiseAnd($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 422 => static function ($self, $stackPos) { + $self->semValue = new Expr\BinaryOp\BitwiseAnd($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 423 => static function ($self, $stackPos) { + $self->semValue = new Expr\BinaryOp\BitwiseXor($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 424 => static function ($self, $stackPos) { + $self->semValue = new Expr\BinaryOp\Concat($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 425 => static function ($self, $stackPos) { + $self->semValue = new Expr\BinaryOp\Plus($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 426 => static function ($self, $stackPos) { + $self->semValue = new Expr\BinaryOp\Minus($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 427 => static function ($self, $stackPos) { + $self->semValue = new Expr\BinaryOp\Mul($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 428 => static function ($self, $stackPos) { + $self->semValue = new Expr\BinaryOp\Div($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 429 => static function ($self, $stackPos) { + $self->semValue = new Expr\BinaryOp\Mod($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 430 => static function ($self, $stackPos) { + $self->semValue = new Expr\BinaryOp\ShiftLeft($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 431 => static function ($self, $stackPos) { + $self->semValue = new Expr\BinaryOp\ShiftRight($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 432 => static function ($self, $stackPos) { + $self->semValue = new Expr\BinaryOp\Pow($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 433 => static function ($self, $stackPos) { + $self->semValue = new Expr\UnaryPlus($self->semStack[$stackPos - (2 - 2)], $self->getAttributes($self->tokenStartStack[$stackPos - (2 - 1)], $self->tokenEndStack[$stackPos])); + }, 434 => static function ($self, $stackPos) { + $self->semValue = new Expr\UnaryMinus($self->semStack[$stackPos - (2 - 2)], $self->getAttributes($self->tokenStartStack[$stackPos - (2 - 1)], $self->tokenEndStack[$stackPos])); + }, 435 => static function ($self, $stackPos) { + $self->semValue = new Expr\BooleanNot($self->semStack[$stackPos - (2 - 2)], $self->getAttributes($self->tokenStartStack[$stackPos - (2 - 1)], $self->tokenEndStack[$stackPos])); + }, 436 => static function ($self, $stackPos) { + $self->semValue = new Expr\BitwiseNot($self->semStack[$stackPos - (2 - 2)], $self->getAttributes($self->tokenStartStack[$stackPos - (2 - 1)], $self->tokenEndStack[$stackPos])); + }, 437 => static function ($self, $stackPos) { + $self->semValue = new Expr\BinaryOp\Identical($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 438 => static function ($self, $stackPos) { + $self->semValue = new Expr\BinaryOp\NotIdentical($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 439 => static function ($self, $stackPos) { + $self->semValue = new Expr\BinaryOp\Equal($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 440 => static function ($self, $stackPos) { + $self->semValue = new Expr\BinaryOp\NotEqual($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 441 => static function ($self, $stackPos) { + $self->semValue = new Expr\BinaryOp\Spaceship($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 442 => static function ($self, $stackPos) { + $self->semValue = new Expr\BinaryOp\Smaller($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 443 => static function ($self, $stackPos) { + $self->semValue = new Expr\BinaryOp\SmallerOrEqual($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 444 => static function ($self, $stackPos) { + $self->semValue = new Expr\BinaryOp\Greater($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 445 => static function ($self, $stackPos) { + $self->semValue = new Expr\BinaryOp\GreaterOrEqual($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 446 => static function ($self, $stackPos) { + $self->semValue = new Expr\Instanceof_($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 447 => static function ($self, $stackPos) { + $self->semValue = $self->semStack[$stackPos - (3 - 2)]; + }, 448 => static function ($self, $stackPos) { + $self->semValue = new Expr\Ternary($self->semStack[$stackPos - (5 - 1)], $self->semStack[$stackPos - (5 - 3)], $self->semStack[$stackPos - (5 - 5)], $self->getAttributes($self->tokenStartStack[$stackPos - (5 - 1)], $self->tokenEndStack[$stackPos])); + }, 449 => static function ($self, $stackPos) { + $self->semValue = new Expr\Ternary($self->semStack[$stackPos - (4 - 1)], null, $self->semStack[$stackPos - (4 - 4)], $self->getAttributes($self->tokenStartStack[$stackPos - (4 - 1)], $self->tokenEndStack[$stackPos])); + }, 450 => static function ($self, $stackPos) { + $self->semValue = new Expr\BinaryOp\Coalesce($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 451 => static function ($self, $stackPos) { + $self->semValue = new Expr\Isset_($self->semStack[$stackPos - (4 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (4 - 1)], $self->tokenEndStack[$stackPos])); + }, 452 => static function ($self, $stackPos) { + $self->semValue = new Expr\Empty_($self->semStack[$stackPos - (4 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (4 - 1)], $self->tokenEndStack[$stackPos])); + }, 453 => static function ($self, $stackPos) { + $self->semValue = new Expr\Include_($self->semStack[$stackPos - (2 - 2)], Expr\Include_::TYPE_INCLUDE, $self->getAttributes($self->tokenStartStack[$stackPos - (2 - 1)], $self->tokenEndStack[$stackPos])); + }, 454 => static function ($self, $stackPos) { + $self->semValue = new Expr\Include_($self->semStack[$stackPos - (2 - 2)], Expr\Include_::TYPE_INCLUDE_ONCE, $self->getAttributes($self->tokenStartStack[$stackPos - (2 - 1)], $self->tokenEndStack[$stackPos])); + }, 455 => static function ($self, $stackPos) { + $self->semValue = new Expr\Eval_($self->semStack[$stackPos - (4 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (4 - 1)], $self->tokenEndStack[$stackPos])); + }, 456 => static function ($self, $stackPos) { + $self->semValue = new Expr\Include_($self->semStack[$stackPos - (2 - 2)], Expr\Include_::TYPE_REQUIRE, $self->getAttributes($self->tokenStartStack[$stackPos - (2 - 1)], $self->tokenEndStack[$stackPos])); + }, 457 => static function ($self, $stackPos) { + $self->semValue = new Expr\Include_($self->semStack[$stackPos - (2 - 2)], Expr\Include_::TYPE_REQUIRE_ONCE, $self->getAttributes($self->tokenStartStack[$stackPos - (2 - 1)], $self->tokenEndStack[$stackPos])); + }, 458 => static function ($self, $stackPos) { + $self->semValue = new Expr\Cast\Int_($self->semStack[$stackPos - (2 - 2)], $self->getAttributes($self->tokenStartStack[$stackPos - (2 - 1)], $self->tokenEndStack[$stackPos])); + }, 459 => static function ($self, $stackPos) { + $attrs = $self->getAttributes($self->tokenStartStack[$stackPos - (2 - 1)], $self->tokenEndStack[$stackPos]); + $attrs['kind'] = $self->getFloatCastKind($self->semStack[$stackPos - (2 - 1)]); + $self->semValue = new Expr\Cast\Double($self->semStack[$stackPos - (2 - 2)], $attrs); + }, 460 => static function ($self, $stackPos) { + $self->semValue = new Expr\Cast\String_($self->semStack[$stackPos - (2 - 2)], $self->getAttributes($self->tokenStartStack[$stackPos - (2 - 1)], $self->tokenEndStack[$stackPos])); + }, 461 => static function ($self, $stackPos) { + $self->semValue = new Expr\Cast\Array_($self->semStack[$stackPos - (2 - 2)], $self->getAttributes($self->tokenStartStack[$stackPos - (2 - 1)], $self->tokenEndStack[$stackPos])); + }, 462 => static function ($self, $stackPos) { + $self->semValue = new Expr\Cast\Object_($self->semStack[$stackPos - (2 - 2)], $self->getAttributes($self->tokenStartStack[$stackPos - (2 - 1)], $self->tokenEndStack[$stackPos])); + }, 463 => static function ($self, $stackPos) { + $self->semValue = new Expr\Cast\Bool_($self->semStack[$stackPos - (2 - 2)], $self->getAttributes($self->tokenStartStack[$stackPos - (2 - 1)], $self->tokenEndStack[$stackPos])); + }, 464 => static function ($self, $stackPos) { + $self->semValue = new Expr\Cast\Unset_($self->semStack[$stackPos - (2 - 2)], $self->getAttributes($self->tokenStartStack[$stackPos - (2 - 1)], $self->tokenEndStack[$stackPos])); + }, 465 => static function ($self, $stackPos) { + $attrs = $self->getAttributes($self->tokenStartStack[$stackPos - (2 - 1)], $self->tokenEndStack[$stackPos]); + $attrs['kind'] = (strtolower($self->semStack[$stackPos - (2 - 1)]) === 'exit') ? Expr\Exit_::KIND_EXIT : Expr\Exit_::KIND_DIE; + $self->semValue = new Expr\Exit_($self->semStack[$stackPos - (2 - 2)], $attrs); + }, 466 => static function ($self, $stackPos) { + $self->semValue = new Expr\ErrorSuppress($self->semStack[$stackPos - (2 - 2)], $self->getAttributes($self->tokenStartStack[$stackPos - (2 - 1)], $self->tokenEndStack[$stackPos])); + }, 467 => null, 468 => static function ($self, $stackPos) { + $self->semValue = new Expr\ShellExec($self->semStack[$stackPos - (3 - 2)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 469 => static function ($self, $stackPos) { + $self->semValue = new Expr\Print_($self->semStack[$stackPos - (2 - 2)], $self->getAttributes($self->tokenStartStack[$stackPos - (2 - 1)], $self->tokenEndStack[$stackPos])); + }, 470 => static function ($self, $stackPos) { + $self->semValue = new Expr\Yield_(null, null, $self->getAttributes($self->tokenStartStack[$stackPos - (1 - 1)], $self->tokenEndStack[$stackPos])); + }, 471 => static function ($self, $stackPos) { + $self->semValue = new Expr\Yield_($self->semStack[$stackPos - (2 - 2)], null, $self->getAttributes($self->tokenStartStack[$stackPos - (2 - 1)], $self->tokenEndStack[$stackPos])); + }, 472 => static function ($self, $stackPos) { + $self->semValue = new Expr\Yield_($self->semStack[$stackPos - (4 - 4)], $self->semStack[$stackPos - (4 - 2)], $self->getAttributes($self->tokenStartStack[$stackPos - (4 - 1)], $self->tokenEndStack[$stackPos])); + }, 473 => static function ($self, $stackPos) { + $self->semValue = new Expr\YieldFrom($self->semStack[$stackPos - (2 - 2)], $self->getAttributes($self->tokenStartStack[$stackPos - (2 - 1)], $self->tokenEndStack[$stackPos])); + }, 474 => static function ($self, $stackPos) { + $self->semValue = new Expr\Throw_($self->semStack[$stackPos - (2 - 2)], $self->getAttributes($self->tokenStartStack[$stackPos - (2 - 1)], $self->tokenEndStack[$stackPos])); + }, 475 => static function ($self, $stackPos) { + $self->semValue = new Expr\ArrowFunction(['static' => \false, 'byRef' => $self->semStack[$stackPos - (8 - 2)], 'params' => $self->semStack[$stackPos - (8 - 4)], 'returnType' => $self->semStack[$stackPos - (8 - 6)], 'expr' => $self->semStack[$stackPos - (8 - 8)], 'attrGroups' => []], $self->getAttributes($self->tokenStartStack[$stackPos - (8 - 1)], $self->tokenEndStack[$stackPos])); + }, 476 => static function ($self, $stackPos) { + $self->semValue = new Expr\ArrowFunction(['static' => \true, 'byRef' => $self->semStack[$stackPos - (9 - 3)], 'params' => $self->semStack[$stackPos - (9 - 5)], 'returnType' => $self->semStack[$stackPos - (9 - 7)], 'expr' => $self->semStack[$stackPos - (9 - 9)], 'attrGroups' => []], $self->getAttributes($self->tokenStartStack[$stackPos - (9 - 1)], $self->tokenEndStack[$stackPos])); + }, 477 => static function ($self, $stackPos) { + $self->semValue = new Expr\Closure(['static' => \false, 'byRef' => $self->semStack[$stackPos - (8 - 2)], 'params' => $self->semStack[$stackPos - (8 - 4)], 'uses' => $self->semStack[$stackPos - (8 - 6)], 'returnType' => $self->semStack[$stackPos - (8 - 7)], 'stmts' => $self->semStack[$stackPos - (8 - 8)], 'attrGroups' => []], $self->getAttributes($self->tokenStartStack[$stackPos - (8 - 1)], $self->tokenEndStack[$stackPos])); + }, 478 => static function ($self, $stackPos) { + $self->semValue = new Expr\Closure(['static' => \true, 'byRef' => $self->semStack[$stackPos - (9 - 3)], 'params' => $self->semStack[$stackPos - (9 - 5)], 'uses' => $self->semStack[$stackPos - (9 - 7)], 'returnType' => $self->semStack[$stackPos - (9 - 8)], 'stmts' => $self->semStack[$stackPos - (9 - 9)], 'attrGroups' => []], $self->getAttributes($self->tokenStartStack[$stackPos - (9 - 1)], $self->tokenEndStack[$stackPos])); + }, 479 => static function ($self, $stackPos) { + $self->semValue = new Expr\ArrowFunction(['static' => \false, 'byRef' => $self->semStack[$stackPos - (9 - 3)], 'params' => $self->semStack[$stackPos - (9 - 5)], 'returnType' => $self->semStack[$stackPos - (9 - 7)], 'expr' => $self->semStack[$stackPos - (9 - 9)], 'attrGroups' => $self->semStack[$stackPos - (9 - 1)]], $self->getAttributes($self->tokenStartStack[$stackPos - (9 - 1)], $self->tokenEndStack[$stackPos])); + }, 480 => static function ($self, $stackPos) { + $self->semValue = new Expr\ArrowFunction(['static' => \true, 'byRef' => $self->semStack[$stackPos - (10 - 4)], 'params' => $self->semStack[$stackPos - (10 - 6)], 'returnType' => $self->semStack[$stackPos - (10 - 8)], 'expr' => $self->semStack[$stackPos - (10 - 10)], 'attrGroups' => $self->semStack[$stackPos - (10 - 1)]], $self->getAttributes($self->tokenStartStack[$stackPos - (10 - 1)], $self->tokenEndStack[$stackPos])); + }, 481 => static function ($self, $stackPos) { + $self->semValue = new Expr\Closure(['static' => \false, 'byRef' => $self->semStack[$stackPos - (9 - 3)], 'params' => $self->semStack[$stackPos - (9 - 5)], 'uses' => $self->semStack[$stackPos - (9 - 7)], 'returnType' => $self->semStack[$stackPos - (9 - 8)], 'stmts' => $self->semStack[$stackPos - (9 - 9)], 'attrGroups' => $self->semStack[$stackPos - (9 - 1)]], $self->getAttributes($self->tokenStartStack[$stackPos - (9 - 1)], $self->tokenEndStack[$stackPos])); + }, 482 => static function ($self, $stackPos) { + $self->semValue = new Expr\Closure(['static' => \true, 'byRef' => $self->semStack[$stackPos - (10 - 4)], 'params' => $self->semStack[$stackPos - (10 - 6)], 'uses' => $self->semStack[$stackPos - (10 - 8)], 'returnType' => $self->semStack[$stackPos - (10 - 9)], 'stmts' => $self->semStack[$stackPos - (10 - 10)], 'attrGroups' => $self->semStack[$stackPos - (10 - 1)]], $self->getAttributes($self->tokenStartStack[$stackPos - (10 - 1)], $self->tokenEndStack[$stackPos])); + }, 483 => static function ($self, $stackPos) { + $self->semValue = array(new Stmt\Class_(null, ['type' => $self->semStack[$stackPos - (8 - 2)], 'extends' => $self->semStack[$stackPos - (8 - 4)], 'implements' => $self->semStack[$stackPos - (8 - 5)], 'stmts' => $self->semStack[$stackPos - (8 - 7)], 'attrGroups' => $self->semStack[$stackPos - (8 - 1)]], $self->getAttributes($self->tokenStartStack[$stackPos - (8 - 1)], $self->tokenEndStack[$stackPos])), $self->semStack[$stackPos - (8 - 3)]); + $self->checkClass($self->semValue[0], -1); + }, 484 => static function ($self, $stackPos) { + $self->semValue = new Expr\New_($self->semStack[$stackPos - (3 - 2)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 485 => static function ($self, $stackPos) { + list($class, $ctorArgs) = $self->semStack[$stackPos - (2 - 2)]; + $self->semValue = new Expr\New_($class, $ctorArgs, $self->getAttributes($self->tokenStartStack[$stackPos - (2 - 1)], $self->tokenEndStack[$stackPos])); + }, 486 => static function ($self, $stackPos) { + $self->semValue = new Expr\New_($self->semStack[$stackPos - (2 - 2)], [], $self->getAttributes($self->tokenStartStack[$stackPos - (2 - 1)], $self->tokenEndStack[$stackPos])); + }, 487 => null, 488 => null, 489 => static function ($self, $stackPos) { + $self->semValue = array(); + }, 490 => static function ($self, $stackPos) { + $self->semValue = $self->semStack[$stackPos - (4 - 3)]; + }, 491 => null, 492 => static function ($self, $stackPos) { + $self->semValue = array($self->semStack[$stackPos - (1 - 1)]); + }, 493 => static function ($self, $stackPos) { + $self->semStack[$stackPos - (3 - 1)][] = $self->semStack[$stackPos - (3 - 3)]; + $self->semValue = $self->semStack[$stackPos - (3 - 1)]; + }, 494 => static function ($self, $stackPos) { + $self->semValue = new Node\ClosureUse($self->semStack[$stackPos - (2 - 2)], $self->semStack[$stackPos - (2 - 1)], $self->getAttributes($self->tokenStartStack[$stackPos - (2 - 1)], $self->tokenEndStack[$stackPos])); + }, 495 => static function ($self, $stackPos) { + $self->semValue = new Name($self->semStack[$stackPos - (1 - 1)], $self->getAttributes($self->tokenStartStack[$stackPos - (1 - 1)], $self->tokenEndStack[$stackPos])); + }, 496 => static function ($self, $stackPos) { + $self->semValue = new Expr\FuncCall($self->semStack[$stackPos - (2 - 1)], $self->semStack[$stackPos - (2 - 2)], $self->getAttributes($self->tokenStartStack[$stackPos - (2 - 1)], $self->tokenEndStack[$stackPos])); + }, 497 => static function ($self, $stackPos) { + $self->semValue = new Expr\FuncCall($self->semStack[$stackPos - (2 - 1)], $self->semStack[$stackPos - (2 - 2)], $self->getAttributes($self->tokenStartStack[$stackPos - (2 - 1)], $self->tokenEndStack[$stackPos])); + }, 498 => static function ($self, $stackPos) { + $self->semValue = new Expr\FuncCall($self->semStack[$stackPos - (2 - 1)], $self->semStack[$stackPos - (2 - 2)], $self->getAttributes($self->tokenStartStack[$stackPos - (2 - 1)], $self->tokenEndStack[$stackPos])); + }, 499 => static function ($self, $stackPos) { + $self->semValue = new Expr\StaticCall($self->semStack[$stackPos - (4 - 1)], $self->semStack[$stackPos - (4 - 3)], $self->semStack[$stackPos - (4 - 4)], $self->getAttributes($self->tokenStartStack[$stackPos - (4 - 1)], $self->tokenEndStack[$stackPos])); + }, 500 => static function ($self, $stackPos) { + $self->semValue = new Name($self->semStack[$stackPos - (1 - 1)], $self->getAttributes($self->tokenStartStack[$stackPos - (1 - 1)], $self->tokenEndStack[$stackPos])); + }, 501 => null, 502 => static function ($self, $stackPos) { + $self->semValue = new Name($self->semStack[$stackPos - (1 - 1)], $self->getAttributes($self->tokenStartStack[$stackPos - (1 - 1)], $self->tokenEndStack[$stackPos])); + }, 503 => static function ($self, $stackPos) { + $self->semValue = new Name($self->semStack[$stackPos - (1 - 1)], $self->getAttributes($self->tokenStartStack[$stackPos - (1 - 1)], $self->tokenEndStack[$stackPos])); + }, 504 => static function ($self, $stackPos) { + $self->semValue = new Name\FullyQualified(substr($self->semStack[$stackPos - (1 - 1)], 1), $self->getAttributes($self->tokenStartStack[$stackPos - (1 - 1)], $self->tokenEndStack[$stackPos])); + }, 505 => static function ($self, $stackPos) { + $self->semValue = new Name\Relative(substr($self->semStack[$stackPos - (1 - 1)], 10), $self->getAttributes($self->tokenStartStack[$stackPos - (1 - 1)], $self->tokenEndStack[$stackPos])); + }, 506 => null, 507 => null, 508 => static function ($self, $stackPos) { + $self->semValue = $self->semStack[$stackPos - (3 - 2)]; + }, 509 => static function ($self, $stackPos) { + $self->semValue = new Expr\Error($self->getAttributes($self->tokenStartStack[$stackPos - (1 - 1)], $self->tokenEndStack[$stackPos])); + $self->errorState = 2; + }, 510 => null, 511 => null, 512 => static function ($self, $stackPos) { + $self->semValue = null; + }, 513 => static function ($self, $stackPos) { + $self->semValue = $self->semStack[$stackPos - (3 - 2)]; + }, 514 => static function ($self, $stackPos) { + $self->semValue = array(); + }, 515 => static function ($self, $stackPos) { + $self->semValue = array($self->semStack[$stackPos - (1 - 1)]); + foreach ($self->semValue as $s) { + if ($s instanceof Node\InterpolatedStringPart) { + $s->value = Node\Scalar\String_::parseEscapeSequences($s->value, '`', $self->phpVersion->supportsUnicodeEscapes()); + } + } + }, 516 => static function ($self, $stackPos) { + foreach ($self->semStack[$stackPos - (1 - 1)] as $s) { + if ($s instanceof Node\InterpolatedStringPart) { + $s->value = Node\Scalar\String_::parseEscapeSequences($s->value, '`', $self->phpVersion->supportsUnicodeEscapes()); + } + } + $self->semValue = $self->semStack[$stackPos - (1 - 1)]; + }, 517 => static function ($self, $stackPos) { + $self->semValue = array(); + }, 518 => null, 519 => static function ($self, $stackPos) { + $self->semValue = new Expr\ConstFetch($self->semStack[$stackPos - (1 - 1)], $self->getAttributes($self->tokenStartStack[$stackPos - (1 - 1)], $self->tokenEndStack[$stackPos])); + }, 520 => static function ($self, $stackPos) { + $self->semValue = new Scalar\MagicConst\Line($self->getAttributes($self->tokenStartStack[$stackPos - (1 - 1)], $self->tokenEndStack[$stackPos])); + }, 521 => static function ($self, $stackPos) { + $self->semValue = new Scalar\MagicConst\File($self->getAttributes($self->tokenStartStack[$stackPos - (1 - 1)], $self->tokenEndStack[$stackPos])); + }, 522 => static function ($self, $stackPos) { + $self->semValue = new Scalar\MagicConst\Dir($self->getAttributes($self->tokenStartStack[$stackPos - (1 - 1)], $self->tokenEndStack[$stackPos])); + }, 523 => static function ($self, $stackPos) { + $self->semValue = new Scalar\MagicConst\Class_($self->getAttributes($self->tokenStartStack[$stackPos - (1 - 1)], $self->tokenEndStack[$stackPos])); + }, 524 => static function ($self, $stackPos) { + $self->semValue = new Scalar\MagicConst\Trait_($self->getAttributes($self->tokenStartStack[$stackPos - (1 - 1)], $self->tokenEndStack[$stackPos])); + }, 525 => static function ($self, $stackPos) { + $self->semValue = new Scalar\MagicConst\Method($self->getAttributes($self->tokenStartStack[$stackPos - (1 - 1)], $self->tokenEndStack[$stackPos])); + }, 526 => static function ($self, $stackPos) { + $self->semValue = new Scalar\MagicConst\Function_($self->getAttributes($self->tokenStartStack[$stackPos - (1 - 1)], $self->tokenEndStack[$stackPos])); + }, 527 => static function ($self, $stackPos) { + $self->semValue = new Scalar\MagicConst\Namespace_($self->getAttributes($self->tokenStartStack[$stackPos - (1 - 1)], $self->tokenEndStack[$stackPos])); + }, 528 => static function ($self, $stackPos) { + $self->semValue = new Expr\ClassConstFetch($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 529 => static function ($self, $stackPos) { + $self->semValue = new Expr\ClassConstFetch($self->semStack[$stackPos - (5 - 1)], $self->semStack[$stackPos - (5 - 4)], $self->getAttributes($self->tokenStartStack[$stackPos - (5 - 1)], $self->tokenEndStack[$stackPos])); + }, 530 => static function ($self, $stackPos) { + $self->semValue = new Expr\ClassConstFetch($self->semStack[$stackPos - (3 - 1)], new Expr\Error($self->getAttributes($self->tokenStartStack[$stackPos - (3 - 3)], $self->tokenEndStack[$stackPos - (3 - 3)])), $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + $self->errorState = 2; + }, 531 => static function ($self, $stackPos) { + $attrs = $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos]); $attrs['kind'] = Expr\Array_::KIND_SHORT; - $this->semValue = new Expr\Array_($this->semStack[$stackPos - (3 - 2)], $attrs); - }, 528 => function ($stackPos) { - $attrs = $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes; + $self->semValue = new Expr\Array_($self->semStack[$stackPos - (3 - 2)], $attrs); + }, 532 => static function ($self, $stackPos) { + $attrs = $self->getAttributes($self->tokenStartStack[$stackPos - (4 - 1)], $self->tokenEndStack[$stackPos]); $attrs['kind'] = Expr\Array_::KIND_LONG; - $this->semValue = new Expr\Array_($this->semStack[$stackPos - (4 - 3)], $attrs); - }, 529 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 530 => function ($stackPos) { - $this->semValue = Scalar\String_::fromString($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 531 => function ($stackPos) { - $attrs = $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes; + $self->semValue = new Expr\Array_($self->semStack[$stackPos - (4 - 3)], $attrs); + $self->createdArrays->attach($self->semValue); + }, 533 => static function ($self, $stackPos) { + $self->semValue = $self->semStack[$stackPos - (1 - 1)]; + $self->createdArrays->attach($self->semValue); + }, 534 => static function ($self, $stackPos) { + $self->semValue = Scalar\String_::fromString($self->semStack[$stackPos - (1 - 1)], $self->getAttributes($self->tokenStartStack[$stackPos - (1 - 1)], $self->tokenEndStack[$stackPos]), $self->phpVersion->supportsUnicodeEscapes()); + }, 535 => static function ($self, $stackPos) { + $attrs = $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos]); $attrs['kind'] = Scalar\String_::KIND_DOUBLE_QUOTED; - foreach ($this->semStack[$stackPos - (3 - 2)] as $s) { - if ($s instanceof Node\Scalar\EncapsedStringPart) { - $s->value = Node\Scalar\String_::parseEscapeSequences($s->value, '"', \true); - } - } - $this->semValue = new Scalar\Encapsed($this->semStack[$stackPos - (3 - 2)], $attrs); - }, 532 => function ($stackPos) { - $this->semValue = $this->parseLNumber($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 533 => function ($stackPos) { - $this->semValue = Scalar\DNumber::fromString($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 534 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 535 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 536 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 537 => function ($stackPos) { - $this->semValue = $this->parseDocString($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 2)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes, $this->startAttributeStack[$stackPos - (3 - 3)] + $this->endAttributeStack[$stackPos - (3 - 3)], \true); - }, 538 => function ($stackPos) { - $this->semValue = $this->parseDocString($this->semStack[$stackPos - (2 - 1)], '', $this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes, $this->startAttributeStack[$stackPos - (2 - 2)] + $this->endAttributeStack[$stackPos - (2 - 2)], \true); - }, 539 => function ($stackPos) { - $this->semValue = $this->parseDocString($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 2)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes, $this->startAttributeStack[$stackPos - (3 - 3)] + $this->endAttributeStack[$stackPos - (3 - 3)], \true); - }, 540 => function ($stackPos) { - $this->semValue = null; - }, 541 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 542 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 543 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (3 - 2)]; - }, 544 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 545 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 546 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 547 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 548 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 549 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (3 - 2)]; - }, 550 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 551 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 552 => function ($stackPos) { - $this->semValue = new Expr\ArrayDimFetch($this->semStack[$stackPos - (4 - 1)], $this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 553 => function ($stackPos) { - $this->semValue = new Expr\ArrayDimFetch($this->semStack[$stackPos - (4 - 1)], $this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 554 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 555 => function ($stackPos) { - $this->semValue = new Expr\MethodCall($this->semStack[$stackPos - (4 - 1)], $this->semStack[$stackPos - (4 - 3)], $this->semStack[$stackPos - (4 - 4)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 556 => function ($stackPos) { - $this->semValue = new Expr\NullsafeMethodCall($this->semStack[$stackPos - (4 - 1)], $this->semStack[$stackPos - (4 - 3)], $this->semStack[$stackPos - (4 - 4)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 557 => function ($stackPos) { - $this->semValue = null; - }, 558 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 559 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 560 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 561 => function ($stackPos) { - $this->semValue = new Expr\PropertyFetch($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 562 => function ($stackPos) { - $this->semValue = new Expr\NullsafePropertyFetch($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 563 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 564 => function ($stackPos) { - $this->semValue = new Expr\Variable($this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 565 => function ($stackPos) { - $this->semValue = new Expr\Variable($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 566 => function ($stackPos) { - $this->semValue = new Expr\Variable(new Expr\Error($this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes), $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - $this->errorState = 2; - }, 567 => function ($stackPos) { - $var = $this->semStack[$stackPos - (1 - 1)]->name; - $this->semValue = \is_string($var) ? new Node\VarLikeIdentifier($var, $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes) : $var; - }, 568 => function ($stackPos) { - $this->semValue = new Expr\StaticPropertyFetch($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 569 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 570 => function ($stackPos) { - $this->semValue = new Expr\ArrayDimFetch($this->semStack[$stackPos - (4 - 1)], $this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 571 => function ($stackPos) { - $this->semValue = new Expr\ArrayDimFetch($this->semStack[$stackPos - (4 - 1)], $this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 572 => function ($stackPos) { - $this->semValue = new Expr\PropertyFetch($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 573 => function ($stackPos) { - $this->semValue = new Expr\NullsafePropertyFetch($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 574 => function ($stackPos) { - $this->semValue = new Expr\StaticPropertyFetch($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 575 => function ($stackPos) { - $this->semValue = new Expr\StaticPropertyFetch($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 576 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 577 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (3 - 2)]; - }, 578 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 579 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 580 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (3 - 2)]; - }, 581 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 582 => function ($stackPos) { - $this->semValue = new Expr\Error($this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - $this->errorState = 2; - }, 583 => function ($stackPos) { - $this->semValue = new Expr\List_($this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 584 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - $end = \count($this->semValue) - 1; - if ($this->semValue[$end] === null) { - \array_pop($this->semValue); - } - }, 585 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 586 => function ($stackPos) { - /* do nothing -- prevent default action of $$=$this->semStack[$1]. See $551. */ - }, 587 => function ($stackPos) { - $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; - $this->semValue = $this->semStack[$stackPos - (3 - 1)]; - }, 588 => function ($stackPos) { - $this->semValue = array($this->semStack[$stackPos - (1 - 1)]); - }, 589 => function ($stackPos) { - $this->semValue = new Expr\ArrayItem($this->semStack[$stackPos - (1 - 1)], null, \false, $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 590 => function ($stackPos) { - $this->semValue = new Expr\ArrayItem($this->semStack[$stackPos - (2 - 2)], null, \true, $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 591 => function ($stackPos) { - $this->semValue = new Expr\ArrayItem($this->semStack[$stackPos - (1 - 1)], null, \false, $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 592 => function ($stackPos) { - $this->semValue = new Expr\ArrayItem($this->semStack[$stackPos - (3 - 3)], $this->semStack[$stackPos - (3 - 1)], \false, $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 593 => function ($stackPos) { - $this->semValue = new Expr\ArrayItem($this->semStack[$stackPos - (4 - 4)], $this->semStack[$stackPos - (4 - 1)], \true, $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 594 => function ($stackPos) { - $this->semValue = new Expr\ArrayItem($this->semStack[$stackPos - (3 - 3)], $this->semStack[$stackPos - (3 - 1)], \false, $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 595 => function ($stackPos) { - $this->semValue = new Expr\ArrayItem($this->semStack[$stackPos - (2 - 2)], null, \false, $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes, \true); - }, 596 => function ($stackPos) { - $this->semValue = null; - }, 597 => function ($stackPos) { - $this->semStack[$stackPos - (2 - 1)][] = $this->semStack[$stackPos - (2 - 2)]; - $this->semValue = $this->semStack[$stackPos - (2 - 1)]; - }, 598 => function ($stackPos) { - $this->semStack[$stackPos - (2 - 1)][] = $this->semStack[$stackPos - (2 - 2)]; - $this->semValue = $this->semStack[$stackPos - (2 - 1)]; - }, 599 => function ($stackPos) { - $this->semValue = array($this->semStack[$stackPos - (1 - 1)]); - }, 600 => function ($stackPos) { - $this->semValue = array($this->semStack[$stackPos - (2 - 1)], $this->semStack[$stackPos - (2 - 2)]); - }, 601 => function ($stackPos) { - $this->semValue = new Scalar\EncapsedStringPart($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 602 => function ($stackPos) { - $this->semValue = new Expr\Variable($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 603 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 604 => function ($stackPos) { - $this->semValue = new Expr\ArrayDimFetch($this->semStack[$stackPos - (4 - 1)], $this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 605 => function ($stackPos) { - $this->semValue = new Expr\PropertyFetch($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 606 => function ($stackPos) { - $this->semValue = new Expr\NullsafePropertyFetch($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 607 => function ($stackPos) { - $this->semValue = new Expr\Variable($this->semStack[$stackPos - (3 - 2)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 608 => function ($stackPos) { - $this->semValue = new Expr\Variable($this->semStack[$stackPos - (3 - 2)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 609 => function ($stackPos) { - $this->semValue = new Expr\ArrayDimFetch($this->semStack[$stackPos - (6 - 2)], $this->semStack[$stackPos - (6 - 4)], $this->startAttributeStack[$stackPos - (6 - 1)] + $this->endAttributes); - }, 610 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (3 - 2)]; - }, 611 => function ($stackPos) { - $this->semValue = new Scalar\String_($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 612 => function ($stackPos) { - $this->semValue = $this->parseNumString($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 613 => function ($stackPos) { - $this->semValue = $this->parseNumString('-' . $this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 614 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }]; + foreach ($self->semStack[$stackPos - (3 - 2)] as $s) { + if ($s instanceof Node\InterpolatedStringPart) { + $s->value = Node\Scalar\String_::parseEscapeSequences($s->value, '"', $self->phpVersion->supportsUnicodeEscapes()); + } + } + $self->semValue = new Scalar\InterpolatedString($self->semStack[$stackPos - (3 - 2)], $attrs); + }, 536 => static function ($self, $stackPos) { + $self->semValue = $self->parseLNumber($self->semStack[$stackPos - (1 - 1)], $self->getAttributes($self->tokenStartStack[$stackPos - (1 - 1)], $self->tokenEndStack[$stackPos]), $self->phpVersion->allowsInvalidOctals()); + }, 537 => static function ($self, $stackPos) { + $self->semValue = Scalar\Float_::fromString($self->semStack[$stackPos - (1 - 1)], $self->getAttributes($self->tokenStartStack[$stackPos - (1 - 1)], $self->tokenEndStack[$stackPos])); + }, 538 => null, 539 => null, 540 => null, 541 => static function ($self, $stackPos) { + $self->semValue = $self->parseDocString($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 2)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos]), $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 3)], $self->tokenEndStack[$stackPos - (3 - 3)]), \true); + }, 542 => static function ($self, $stackPos) { + $self->semValue = $self->parseDocString($self->semStack[$stackPos - (2 - 1)], '', $self->semStack[$stackPos - (2 - 2)], $self->getAttributes($self->tokenStartStack[$stackPos - (2 - 1)], $self->tokenEndStack[$stackPos]), $self->getAttributes($self->tokenStartStack[$stackPos - (2 - 2)], $self->tokenEndStack[$stackPos - (2 - 2)]), \true); + }, 543 => static function ($self, $stackPos) { + $self->semValue = $self->parseDocString($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 2)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos]), $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 3)], $self->tokenEndStack[$stackPos - (3 - 3)]), \true); + }, 544 => static function ($self, $stackPos) { + $self->semValue = null; + }, 545 => null, 546 => null, 547 => static function ($self, $stackPos) { + $self->semValue = $self->semStack[$stackPos - (3 - 2)]; + }, 548 => null, 549 => null, 550 => null, 551 => null, 552 => null, 553 => null, 554 => static function ($self, $stackPos) { + $self->semValue = $self->semStack[$stackPos - (3 - 2)]; + }, 555 => null, 556 => null, 557 => null, 558 => static function ($self, $stackPos) { + $self->semValue = new Expr\ArrayDimFetch($self->semStack[$stackPos - (4 - 1)], $self->semStack[$stackPos - (4 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (4 - 1)], $self->tokenEndStack[$stackPos])); + }, 559 => static function ($self, $stackPos) { + $self->semValue = new Expr\ArrayDimFetch($self->semStack[$stackPos - (4 - 1)], $self->semStack[$stackPos - (4 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (4 - 1)], $self->tokenEndStack[$stackPos])); + }, 560 => null, 561 => static function ($self, $stackPos) { + $self->semValue = new Expr\MethodCall($self->semStack[$stackPos - (4 - 1)], $self->semStack[$stackPos - (4 - 3)], $self->semStack[$stackPos - (4 - 4)], $self->getAttributes($self->tokenStartStack[$stackPos - (4 - 1)], $self->tokenEndStack[$stackPos])); + }, 562 => static function ($self, $stackPos) { + $self->semValue = new Expr\NullsafeMethodCall($self->semStack[$stackPos - (4 - 1)], $self->semStack[$stackPos - (4 - 3)], $self->semStack[$stackPos - (4 - 4)], $self->getAttributes($self->tokenStartStack[$stackPos - (4 - 1)], $self->tokenEndStack[$stackPos])); + }, 563 => static function ($self, $stackPos) { + $self->semValue = null; + }, 564 => null, 565 => null, 566 => null, 567 => static function ($self, $stackPos) { + $self->semValue = new Expr\PropertyFetch($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 568 => static function ($self, $stackPos) { + $self->semValue = new Expr\NullsafePropertyFetch($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 569 => null, 570 => static function ($self, $stackPos) { + $self->semValue = new Expr\Variable($self->semStack[$stackPos - (4 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (4 - 1)], $self->tokenEndStack[$stackPos])); + }, 571 => static function ($self, $stackPos) { + $self->semValue = new Expr\Variable($self->semStack[$stackPos - (2 - 2)], $self->getAttributes($self->tokenStartStack[$stackPos - (2 - 1)], $self->tokenEndStack[$stackPos])); + }, 572 => static function ($self, $stackPos) { + $self->semValue = new Expr\Variable(new Expr\Error($self->getAttributes($self->tokenStartStack[$stackPos - (2 - 1)], $self->tokenEndStack[$stackPos])), $self->getAttributes($self->tokenStartStack[$stackPos - (2 - 1)], $self->tokenEndStack[$stackPos])); + $self->errorState = 2; + }, 573 => static function ($self, $stackPos) { + $var = $self->semStack[$stackPos - (1 - 1)]->name; + $self->semValue = \is_string($var) ? new Node\VarLikeIdentifier($var, $self->getAttributes($self->tokenStartStack[$stackPos - (1 - 1)], $self->tokenEndStack[$stackPos])) : $var; + }, 574 => static function ($self, $stackPos) { + $self->semValue = new Expr\StaticPropertyFetch($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 575 => null, 576 => static function ($self, $stackPos) { + $self->semValue = new Expr\ArrayDimFetch($self->semStack[$stackPos - (4 - 1)], $self->semStack[$stackPos - (4 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (4 - 1)], $self->tokenEndStack[$stackPos])); + }, 577 => static function ($self, $stackPos) { + $self->semValue = new Expr\ArrayDimFetch($self->semStack[$stackPos - (4 - 1)], $self->semStack[$stackPos - (4 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (4 - 1)], $self->tokenEndStack[$stackPos])); + }, 578 => static function ($self, $stackPos) { + $self->semValue = new Expr\PropertyFetch($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 579 => static function ($self, $stackPos) { + $self->semValue = new Expr\NullsafePropertyFetch($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 580 => static function ($self, $stackPos) { + $self->semValue = new Expr\StaticPropertyFetch($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 581 => static function ($self, $stackPos) { + $self->semValue = new Expr\StaticPropertyFetch($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 582 => null, 583 => static function ($self, $stackPos) { + $self->semValue = $self->semStack[$stackPos - (3 - 2)]; + }, 584 => null, 585 => null, 586 => static function ($self, $stackPos) { + $self->semValue = $self->semStack[$stackPos - (3 - 2)]; + }, 587 => null, 588 => static function ($self, $stackPos) { + $self->semValue = new Expr\Error($self->getAttributes($self->tokenStartStack[$stackPos - (1 - 1)], $self->tokenEndStack[$stackPos])); + $self->errorState = 2; + }, 589 => static function ($self, $stackPos) { + $self->semValue = new Expr\List_($self->semStack[$stackPos - (4 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (4 - 1)], $self->tokenEndStack[$stackPos])); + $self->semValue->setAttribute('kind', Expr\List_::KIND_LIST); + $self->postprocessList($self->semValue); + }, 590 => static function ($self, $stackPos) { + $self->semValue = $self->semStack[$stackPos - (1 - 1)]; + $end = count($self->semValue) - 1; + if ($self->semValue[$end]->value instanceof Expr\Error) { + array_pop($self->semValue); + } + }, 591 => null, 592 => static function ($self, $stackPos) { + /* do nothing -- prevent default action of $$=$self->semStack[$1]. See $551. */ + }, 593 => static function ($self, $stackPos) { + $self->semStack[$stackPos - (3 - 1)][] = $self->semStack[$stackPos - (3 - 3)]; + $self->semValue = $self->semStack[$stackPos - (3 - 1)]; + }, 594 => static function ($self, $stackPos) { + $self->semValue = array($self->semStack[$stackPos - (1 - 1)]); + }, 595 => static function ($self, $stackPos) { + $self->semValue = new Node\ArrayItem($self->semStack[$stackPos - (1 - 1)], null, \false, $self->getAttributes($self->tokenStartStack[$stackPos - (1 - 1)], $self->tokenEndStack[$stackPos])); + }, 596 => static function ($self, $stackPos) { + $self->semValue = new Node\ArrayItem($self->semStack[$stackPos - (2 - 2)], null, \true, $self->getAttributes($self->tokenStartStack[$stackPos - (2 - 1)], $self->tokenEndStack[$stackPos])); + }, 597 => static function ($self, $stackPos) { + $self->semValue = new Node\ArrayItem($self->semStack[$stackPos - (1 - 1)], null, \false, $self->getAttributes($self->tokenStartStack[$stackPos - (1 - 1)], $self->tokenEndStack[$stackPos])); + }, 598 => static function ($self, $stackPos) { + $self->semValue = new Node\ArrayItem($self->semStack[$stackPos - (3 - 3)], $self->semStack[$stackPos - (3 - 1)], \false, $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 599 => static function ($self, $stackPos) { + $self->semValue = new Node\ArrayItem($self->semStack[$stackPos - (4 - 4)], $self->semStack[$stackPos - (4 - 1)], \true, $self->getAttributes($self->tokenStartStack[$stackPos - (4 - 1)], $self->tokenEndStack[$stackPos])); + }, 600 => static function ($self, $stackPos) { + $self->semValue = new Node\ArrayItem($self->semStack[$stackPos - (3 - 3)], $self->semStack[$stackPos - (3 - 1)], \false, $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 601 => static function ($self, $stackPos) { + $self->semValue = new Node\ArrayItem($self->semStack[$stackPos - (2 - 2)], null, \false, $self->getAttributes($self->tokenStartStack[$stackPos - (2 - 1)], $self->tokenEndStack[$stackPos]), \true); + }, 602 => static function ($self, $stackPos) { + /* Create an Error node now to remember the position. We'll later either report an error, + or convert this into a null element, depending on whether this is a creation or destructuring context. */ + $attrs = $self->createEmptyElemAttributes($self->tokenPos); + $self->semValue = new Node\ArrayItem(new Expr\Error($attrs), null, \false, $attrs); + }, 603 => static function ($self, $stackPos) { + $self->semStack[$stackPos - (2 - 1)][] = $self->semStack[$stackPos - (2 - 2)]; + $self->semValue = $self->semStack[$stackPos - (2 - 1)]; + }, 604 => static function ($self, $stackPos) { + $self->semStack[$stackPos - (2 - 1)][] = $self->semStack[$stackPos - (2 - 2)]; + $self->semValue = $self->semStack[$stackPos - (2 - 1)]; + }, 605 => static function ($self, $stackPos) { + $self->semValue = array($self->semStack[$stackPos - (1 - 1)]); + }, 606 => static function ($self, $stackPos) { + $self->semValue = array($self->semStack[$stackPos - (2 - 1)], $self->semStack[$stackPos - (2 - 2)]); + }, 607 => static function ($self, $stackPos) { + $attrs = $self->getAttributes($self->tokenStartStack[$stackPos - (1 - 1)], $self->tokenEndStack[$stackPos]); + $attrs['rawValue'] = $self->semStack[$stackPos - (1 - 1)]; + $self->semValue = new Node\InterpolatedStringPart($self->semStack[$stackPos - (1 - 1)], $attrs); + }, 608 => static function ($self, $stackPos) { + $self->semValue = new Expr\Variable($self->semStack[$stackPos - (1 - 1)], $self->getAttributes($self->tokenStartStack[$stackPos - (1 - 1)], $self->tokenEndStack[$stackPos])); + }, 609 => null, 610 => static function ($self, $stackPos) { + $self->semValue = new Expr\ArrayDimFetch($self->semStack[$stackPos - (4 - 1)], $self->semStack[$stackPos - (4 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (4 - 1)], $self->tokenEndStack[$stackPos])); + }, 611 => static function ($self, $stackPos) { + $self->semValue = new Expr\PropertyFetch($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 612 => static function ($self, $stackPos) { + $self->semValue = new Expr\NullsafePropertyFetch($self->semStack[$stackPos - (3 - 1)], $self->semStack[$stackPos - (3 - 3)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 613 => static function ($self, $stackPos) { + $self->semValue = new Expr\Variable($self->semStack[$stackPos - (3 - 2)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 614 => static function ($self, $stackPos) { + $self->semValue = new Expr\Variable($self->semStack[$stackPos - (3 - 2)], $self->getAttributes($self->tokenStartStack[$stackPos - (3 - 1)], $self->tokenEndStack[$stackPos])); + }, 615 => static function ($self, $stackPos) { + $self->semValue = new Expr\ArrayDimFetch($self->semStack[$stackPos - (6 - 2)], $self->semStack[$stackPos - (6 - 4)], $self->getAttributes($self->tokenStartStack[$stackPos - (6 - 1)], $self->tokenEndStack[$stackPos])); + }, 616 => static function ($self, $stackPos) { + $self->semValue = $self->semStack[$stackPos - (3 - 2)]; + }, 617 => static function ($self, $stackPos) { + $self->semValue = new Scalar\String_($self->semStack[$stackPos - (1 - 1)], $self->getAttributes($self->tokenStartStack[$stackPos - (1 - 1)], $self->tokenEndStack[$stackPos])); + }, 618 => static function ($self, $stackPos) { + $self->semValue = $self->parseNumString($self->semStack[$stackPos - (1 - 1)], $self->getAttributes($self->tokenStartStack[$stackPos - (1 - 1)], $self->tokenEndStack[$stackPos])); + }, 619 => static function ($self, $stackPos) { + $self->semValue = $self->parseNumString('-' . $self->semStack[$stackPos - (2 - 2)], $self->getAttributes($self->tokenStartStack[$stackPos - (2 - 1)], $self->tokenEndStack[$stackPos])); + }, 620 => null]; } } Map of PHP token IDs to drop */ + protected array $dropTokens; + /** @var int[] Map of external symbols (static::T_*) to internal symbols */ + protected array $tokenToSymbol; /** @var string[] Map of symbols to their names */ - protected $symbolToName; - /** @var array Names of the production rules (only necessary for debugging) */ - protected $productions; + protected array $symbolToName; + /** @var array Names of the production rules (only necessary for debugging) */ + protected array $productions; /** @var int[] Map of states to a displacement into the $action table. The corresponding action for this * state/symbol pair is $action[$actionBase[$state] + $symbol]. If $actionBase[$state] is 0, the * action is defaulted, i.e. $actionDefault[$state] should be used instead. */ - protected $actionBase; + protected array $actionBase; /** @var int[] Table of actions. Indexed according to $actionBase comment. */ - protected $action; + protected array $action; /** @var int[] Table indexed analogously to $action. If $actionCheck[$actionBase[$state] + $symbol] != $symbol * then the action is defaulted, i.e. $actionDefault[$state] should be used instead. */ - protected $actionCheck; + protected array $actionCheck; /** @var int[] Map of states to their default action */ - protected $actionDefault; + protected array $actionDefault; /** @var callable[] Semantic action callbacks */ - protected $reduceCallbacks; + protected array $reduceCallbacks; /** @var int[] Map of non-terminals to a displacement into the $goto table. The corresponding goto state for this * non-terminal/state pair is $goto[$gotoBase[$nonTerminal] + $state] (unless defaulted) */ - protected $gotoBase; + protected array $gotoBase; /** @var int[] Table of states to goto after reduction. Indexed according to $gotoBase comment. */ - protected $goto; + protected array $goto; /** @var int[] Table indexed analogously to $goto. If $gotoCheck[$gotoBase[$nonTerminal] + $state] != $nonTerminal * then the goto state is defaulted, i.e. $gotoDefault[$nonTerminal] should be used. */ - protected $gotoCheck; + protected array $gotoCheck; /** @var int[] Map of non-terminals to the default state to goto after their reduction */ - protected $gotoDefault; + protected array $gotoDefault; /** @var int[] Map of rules to the non-terminal on their left-hand side, i.e. the non-terminal to use for * determining the state to goto after reduction. */ - protected $ruleToNonTerminal; + protected array $ruleToNonTerminal; /** @var int[] Map of rules to the length of their right-hand side, which is the number of elements that have to * be popped from the stack(s) on reduction. */ - protected $ruleToLength; + protected array $ruleToLength; /* * The following members are part of the parser state: */ - /** @var Lexer Lexer that is used when parsing */ - protected $lexer; /** @var mixed Temporary value containing the result of last semantic action (reduction) */ protected $semValue; - /** @var array Semantic value stack (contains values of tokens and semantic action results) */ - protected $semStack; - /** @var array[] Start attribute stack */ - protected $startAttributeStack; - /** @var array[] End attribute stack */ - protected $endAttributeStack; - /** @var array End attributes of last *shifted* token */ - protected $endAttributes; - /** @var array Start attributes of last *read* token */ - protected $lookaheadStartAttributes; + /** @var mixed[] Semantic value stack (contains values of tokens and semantic action results) */ + protected array $semStack; + /** @var int[] Token start position stack */ + protected array $tokenStartStack; + /** @var int[] Token end position stack */ + protected array $tokenEndStack; /** @var ErrorHandler Error handler */ - protected $errorHandler; + protected ErrorHandler $errorHandler; /** @var int Error state, used to avoid error floods */ - protected $errorState; + protected int $errorState; + /** @var \SplObjectStorage|null Array nodes created during parsing, for postprocessing of empty elements. */ + protected ?\SplObjectStorage $createdArrays; + /** @var Token[] Tokens for the current parse */ + protected array $tokens; + /** @var int Current position in token array */ + protected int $tokenPos; /** * Initialize $reduceCallbacks map. */ - protected abstract function initReduceCallbacks(); + abstract protected function initReduceCallbacks(): void; /** * Creates a parser instance. * - * Options: Currently none. + * Options: + * * phpVersion: ?PhpVersion, * * @param Lexer $lexer A lexer - * @param array $options Options array. + * @param PhpVersion $phpVersion PHP version to target, defaults to latest supported. This + * option is best-effort: Even if specified, parsing will generally assume the latest + * supported version and only adjust behavior in minor ways, for example by omitting + * errors in older versions and interpreting type hints as a name or identifier depending + * on version. */ - public function __construct(Lexer $lexer, array $options = []) + public function __construct(Lexer $lexer, ?PhpVersion $phpVersion = null) { $this->lexer = $lexer; - if (isset($options['throwOnError'])) { - throw new \LogicException('"throwOnError" is no longer supported, use "errorHandler" instead'); - } + $this->phpVersion = $phpVersion ?? PhpVersion::getNewestSupported(); $this->initReduceCallbacks(); + $this->phpTokenToSymbol = $this->createTokenMap(); + $this->dropTokens = array_fill_keys([\T_WHITESPACE, \T_OPEN_TAG, \T_COMMENT, \T_DOC_COMMENT, \T_BAD_CHARACTER], \true); } /** * Parses PHP code into a node tree. @@ -21543,32 +20436,49 @@ abstract class ParserAbstract implements Parser * @return Node\Stmt[]|null Array of statements (or null non-throwing error handler is used and * the parser was unable to recover from an error). */ - public function parse(string $code, ?ErrorHandler $errorHandler = null) + public function parse(string $code, ?ErrorHandler $errorHandler = null): ?array { $this->errorHandler = $errorHandler ?: new ErrorHandler\Throwing(); - $this->lexer->startLexing($code, $this->errorHandler); + $this->createdArrays = new \SplObjectStorage(); + $this->tokens = $this->lexer->tokenize($code, $this->errorHandler); $result = $this->doParse(); + // Report errors for any empty elements used inside arrays. This is delayed until after the main parse, + // because we don't know a priori whether a given array expression will be used in a destructuring context + // or not. + foreach ($this->createdArrays as $node) { + foreach ($node->items as $item) { + if ($item->value instanceof Expr\Error) { + $this->errorHandler->handleError(new Error('Cannot use empty array elements in arrays', $item->getAttributes())); + } + } + } // Clear out some of the interior state, so we don't hold onto unnecessary // memory between uses of the parser - $this->startAttributeStack = []; - $this->endAttributeStack = []; + $this->tokenStartStack = []; + $this->tokenEndStack = []; $this->semStack = []; $this->semValue = null; + $this->createdArrays = null; + if ($result !== null) { + $traverser = new NodeTraverser(new CommentAnnotatingVisitor($this->tokens)); + $traverser->traverse($result); + } return $result; } - protected function doParse() + public function getTokens(): array + { + return $this->tokens; + } + /** @return Stmt[]|null */ + protected function doParse(): ?array { // We start off with no lookahead-token $symbol = self::SYMBOL_NONE; - // The attributes for a node are taken from the first and last token of the node. - // From the first token only the startAttributes are taken and from the last only - // the endAttributes. Both are merged using the array union operator (+). - $startAttributes = []; - $endAttributes = []; - $this->endAttributes = $endAttributes; + $tokenValue = null; + $this->tokenPos = -1; // Keep stack of start and end attributes - $this->startAttributeStack = []; - $this->endAttributeStack = [$endAttributes]; + $this->tokenStartStack = []; + $this->tokenEndStack = [0]; // Start off in the initial state and keep a stack of previous states $state = 0; $stateStack = [$state]; @@ -21583,18 +20493,16 @@ abstract class ParserAbstract implements Parser $rule = $this->actionDefault[$state]; } else { if ($symbol === self::SYMBOL_NONE) { - // Fetch the next token id from the lexer and fetch additional info by-ref. - // The end attributes are fetched into a temporary variable and only set once the token is really - // shifted (not during read). Otherwise you would sometimes get off-by-one errors, when a rule is - // reduced after a token was read but not yet shifted. - $tokenId = $this->lexer->getNextToken($tokenValue, $startAttributes, $endAttributes); - // map the lexer token id to the internally used symbols - $symbol = $tokenId >= 0 && $tokenId < $this->tokenToSymbolMapSize ? $this->tokenToSymbol[$tokenId] : $this->invalidSymbol; - if ($symbol === $this->invalidSymbol) { - throw new \RangeException(\sprintf('The lexer returned an invalid token (id=%d, value=%s)', $tokenId, $tokenValue)); + do { + $token = $this->tokens[++$this->tokenPos]; + $tokenId = $token->id; + } while (isset($this->dropTokens[$tokenId])); + // Map the lexer token id to the internally used symbols. + $tokenValue = $token->text; + if (!isset($this->phpTokenToSymbol[$tokenId])) { + throw new \RangeException(sprintf('The lexer returned an invalid token (id=%d, value=%s)', $tokenId, $tokenValue)); } - // Allow productions to access the start attributes of the lookahead token. - $this->lookaheadStartAttributes = $startAttributes; + $symbol = $this->phpTokenToSymbol[$tokenId]; //$this->traceRead($symbol); } $idx = $this->actionBase[$state] + $symbol; @@ -21612,9 +20520,8 @@ abstract class ParserAbstract implements Parser ++$stackPos; $stateStack[$stackPos] = $state = $action; $this->semStack[$stackPos] = $tokenValue; - $this->startAttributeStack[$stackPos] = $startAttributes; - $this->endAttributeStack[$stackPos] = $endAttributes; - $this->endAttributes = $endAttributes; + $this->tokenStartStack[$stackPos] = $this->tokenPos; + $this->tokenEndStack[$stackPos] = $this->tokenPos; $symbol = self::SYMBOL_NONE; if ($this->errorState) { --$this->errorState; @@ -21636,22 +20543,28 @@ abstract class ParserAbstract implements Parser /* accept */ //$this->traceAccept(); return $this->semValue; - } elseif ($rule !== $this->unexpectedTokenRule) { + } + if ($rule !== $this->unexpectedTokenRule) { /* reduce */ //$this->traceReduce($rule); + $ruleLength = $this->ruleToLength[$rule]; try { - $this->reduceCallbacks[$rule]($stackPos); + $callback = $this->reduceCallbacks[$rule]; + if ($callback !== null) { + $callback($this, $stackPos); + } elseif ($ruleLength > 0) { + $this->semValue = $this->semStack[$stackPos - $ruleLength + 1]; + } } catch (Error $e) { - if (-1 === $e->getStartLine() && isset($startAttributes['startLine'])) { - $e->setStartLine($startAttributes['startLine']); + if (-1 === $e->getStartLine()) { + $e->setStartLine($this->tokens[$this->tokenPos]->line); } $this->emitError($e); // Can't recover from this type of error return null; } /* Goto - shift nonterminal */ - $lastEndAttributes = $this->endAttributeStack[$stackPos]; - $ruleLength = $this->ruleToLength[$rule]; + $lastTokenEnd = $this->tokenEndStack[$stackPos]; $stackPos -= $ruleLength; $nonTerminal = $this->ruleToNonTerminal[$rule]; $idx = $this->gotoBase[$nonTerminal] + $stateStack[$stackPos]; @@ -21663,18 +20576,19 @@ abstract class ParserAbstract implements Parser ++$stackPos; $stateStack[$stackPos] = $state; $this->semStack[$stackPos] = $this->semValue; - $this->endAttributeStack[$stackPos] = $lastEndAttributes; + $this->tokenEndStack[$stackPos] = $lastTokenEnd; if ($ruleLength === 0) { // Empty productions use the start attributes of the lookahead token. - $this->startAttributeStack[$stackPos] = $this->lookaheadStartAttributes; + $this->tokenStartStack[$stackPos] = $this->tokenPos; } } else { /* error */ switch ($this->errorState) { case 0: $msg = $this->getErrorMessage($symbol, $state); - $this->emitError(new Error($msg, $startAttributes + $endAttributes)); + $this->emitError(new Error($msg, $this->getAttributesForToken($this->tokenPos))); // Break missing intentionally + // no break case 1: case 2: $this->errorState = 3; @@ -21693,9 +20607,8 @@ abstract class ParserAbstract implements Parser $stateStack[$stackPos] = $state = $action; // We treat the error symbol as being empty, so we reset the end attributes // to the end attributes of the last non-error symbol - $this->startAttributeStack[$stackPos] = $this->lookaheadStartAttributes; - $this->endAttributeStack[$stackPos] = $this->endAttributeStack[$stackPos - 1]; - $this->endAttributes = $this->endAttributeStack[$stackPos - 1]; + $this->tokenStartStack[$stackPos] = $this->tokenPos; + $this->tokenEndStack[$stackPos] = $this->tokenEndStack[$stackPos - 1]; break; case 3: if ($symbol === 0) { @@ -21716,7 +20629,7 @@ abstract class ParserAbstract implements Parser } throw new \RuntimeException('Reached end of parser loop'); } - protected function emitError(Error $error) + protected function emitError(Error $error): void { $this->errorHandler->handleError($error); } @@ -21724,15 +20637,15 @@ abstract class ParserAbstract implements Parser * Format error message including expected tokens. * * @param int $symbol Unexpected symbol - * @param int $state State at time of error + * @param int $state State at time of error * * @return string Formatted error message */ - protected function getErrorMessage(int $symbol, int $state) : string + protected function getErrorMessage(int $symbol, int $state): string { $expectedString = ''; if ($expected = $this->getExpectedTokens($state)) { - $expectedString = ', expecting ' . \implode(' or ', $expected); + $expectedString = ', expecting ' . implode(' or ', $expected); } return 'Syntax error, unexpected ' . $this->symbolToName[$symbol] . $expectedString; } @@ -21743,7 +20656,7 @@ abstract class ParserAbstract implements Parser * * @return string[] Expected tokens. If too many, an empty array is returned. */ - protected function getExpectedTokens(int $state) : array + protected function getExpectedTokens(int $state): array { $expected = []; $base = $this->actionBase[$state]; @@ -21751,7 +20664,7 @@ abstract class ParserAbstract implements Parser $idx = $base + $symbol; if ($idx >= 0 && $idx < $this->actionTableSize && $this->actionCheck[$idx] === $symbol || $state < $this->YY2TBLSTATE && ($idx = $this->actionBase[$state + $this->numNonLeafStates] + $symbol) >= 0 && $idx < $this->actionTableSize && $this->actionCheck[$idx] === $symbol) { if ($this->action[$idx] !== $this->unexpectedTokenRule && $this->action[$idx] !== $this->defaultAction && $symbol !== $this->errorSymbol) { - if (\count($expected) === 4) { + if (count($expected) === 4) { /* Too many expected tokens */ return []; } @@ -21761,36 +20674,63 @@ abstract class ParserAbstract implements Parser } return $expected; } + /** + * Get attributes for a node with the given start and end token positions. + * + * @param int $tokenStartPos Token position the node starts at + * @param int $tokenEndPos Token position the node ends at + * @return array Attributes + */ + protected function getAttributes(int $tokenStartPos, int $tokenEndPos): array + { + $startToken = $this->tokens[$tokenStartPos]; + $afterEndToken = $this->tokens[$tokenEndPos + 1]; + return ['startLine' => $startToken->line, 'startTokenPos' => $tokenStartPos, 'startFilePos' => $startToken->pos, 'endLine' => $afterEndToken->line, 'endTokenPos' => $tokenEndPos, 'endFilePos' => $afterEndToken->pos - 1]; + } + /** + * Get attributes for a single token at the given token position. + * + * @return array Attributes + */ + protected function getAttributesForToken(int $tokenPos): array + { + if ($tokenPos < \count($this->tokens) - 1) { + return $this->getAttributes($tokenPos, $tokenPos); + } + // Get attributes for the sentinel token. + $token = $this->tokens[$tokenPos]; + return ['startLine' => $token->line, 'startTokenPos' => $tokenPos, 'startFilePos' => $token->pos, 'endLine' => $token->line, 'endTokenPos' => $tokenPos, 'endFilePos' => $token->pos]; + } /* * Tracing functions used for debugging the parser. */ /* - protected function traceNewState($state, $symbol) { + protected function traceNewState($state, $symbol): void { echo '% State ' . $state . ', Lookahead ' . ($symbol == self::SYMBOL_NONE ? '--none--' : $this->symbolToName[$symbol]) . "\n"; } - protected function traceRead($symbol) { + protected function traceRead($symbol): void { echo '% Reading ' . $this->symbolToName[$symbol] . "\n"; } - protected function traceShift($symbol) { + protected function traceShift($symbol): void { echo '% Shift ' . $this->symbolToName[$symbol] . "\n"; } - protected function traceAccept() { + protected function traceAccept(): void { echo "% Accepted.\n"; } - protected function traceReduce($n) { + protected function traceReduce($n): void { echo '% Reduce by (' . $n . ') ' . $this->productions[$n] . "\n"; } - protected function tracePop($state) { + protected function tracePop($state): void { echo '% Recovering, uncovered state ' . $state . "\n"; } - protected function traceDiscard($symbol) { + protected function traceDiscard($symbol): void { echo '% Discard ' . $this->symbolToName[$symbol] . "\n"; } */ @@ -21803,14 +20743,15 @@ abstract class ParserAbstract implements Parser * @param Node\Stmt[] $stmts * @return Node\Stmt[] */ - protected function handleNamespaces(array $stmts) : array + protected function handleNamespaces(array $stmts): array { $hasErrored = \false; $style = $this->getNamespacingStyle($stmts); if (null === $style) { // not namespaced, nothing to do return $stmts; - } elseif ('brace' === $style) { + } + if ('brace' === $style) { // For braced namespaces we only have to check that there are no invalid statements between the namespaces $afterFirstNamespace = \false; foreach ($stmts as $stmt) { @@ -21856,7 +20797,7 @@ abstract class ParserAbstract implements Parser return $resultStmts; } } - private function fixupNamespaceAttributes(Node\Stmt\Namespace_ $stmt) + private function fixupNamespaceAttributes(Node\Stmt\Namespace_ $stmt): void { // We moved the statements into the namespace node, as such the end of the namespace node // needs to be extended to the end of the statements. @@ -21866,13 +20807,29 @@ abstract class ParserAbstract implements Parser // We only move the builtin end attributes here. This is the best we can do with the // knowledge we have. $endAttributes = ['endLine', 'endFilePos', 'endTokenPos']; - $lastStmt = $stmt->stmts[\count($stmt->stmts) - 1]; + $lastStmt = $stmt->stmts[count($stmt->stmts) - 1]; foreach ($endAttributes as $endAttribute) { if ($lastStmt->hasAttribute($endAttribute)) { $stmt->setAttribute($endAttribute, $lastStmt->getAttribute($endAttribute)); } } } + /** @return array */ + private function getNamespaceErrorAttributes(Namespace_ $node): array + { + $attrs = $node->getAttributes(); + // Adjust end attributes to only cover the "namespace" keyword, not the whole namespace. + if (isset($attrs['startLine'])) { + $attrs['endLine'] = $attrs['startLine']; + } + if (isset($attrs['startTokenPos'])) { + $attrs['endTokenPos'] = $attrs['startTokenPos']; + } + if (isset($attrs['startFilePos'])) { + $attrs['endFilePos'] = $attrs['startFilePos'] + \strlen('namespace') - 1; + } + return $attrs; + } /** * Determine namespacing style (semicolon or brace) * @@ -21880,20 +20837,20 @@ abstract class ParserAbstract implements Parser * * @return null|string One of "semicolon", "brace" or null (no namespaces) */ - private function getNamespacingStyle(array $stmts) + private function getNamespacingStyle(array $stmts): ?string { $style = null; $hasNotAllowedStmts = \false; foreach ($stmts as $i => $stmt) { if ($stmt instanceof Node\Stmt\Namespace_) { - $currentStyle = null === $stmt->stmts ? 'semicolon' : 'brace'; + $currentStyle = (null === $stmt->stmts) ? 'semicolon' : 'brace'; if (null === $style) { $style = $currentStyle; if ($hasNotAllowedStmts) { - $this->emitError(new Error('Namespace declaration statement has to be the very first statement in the script', $stmt->getLine())); + $this->emitError(new Error('Namespace declaration statement has to be the very first statement in the script', $this->getNamespaceErrorAttributes($stmt))); } } elseif ($style !== $currentStyle) { - $this->emitError(new Error('Cannot mix bracketed namespace declarations with unbracketed namespace declarations', $stmt->getLine())); + $this->emitError(new Error('Cannot mix bracketed namespace declarations with unbracketed namespace declarations', $this->getNamespaceErrorAttributes($stmt))); // Treat like semicolon style for namespace normalization return 'semicolon'; } @@ -21904,7 +20861,7 @@ abstract class ParserAbstract implements Parser continue; } /* There may be a hashbang line at the very start of the file */ - if ($i === 0 && $stmt instanceof Node\Stmt\InlineHTML && \preg_match('/\\A#!.*\\r?\\n\\z/', $stmt->value)) { + if ($i === 0 && $stmt instanceof Node\Stmt\InlineHTML && preg_match('/\A#!.*\r?\n\z/', $stmt->value)) { continue; } /* Everything else if forbidden before namespace declarations */ @@ -21912,64 +20869,14 @@ abstract class ParserAbstract implements Parser } return $style; } - /** - * Fix up parsing of static property calls in PHP 5. - * - * In PHP 5 A::$b[c][d] and A::$b[c][d]() have very different interpretation. The former is - * interpreted as (A::$b)[c][d], while the latter is the same as A::{$b[c][d]}(). We parse the - * latter as the former initially and this method fixes the AST into the correct form when we - * encounter the "()". - * - * @param Node\Expr\StaticPropertyFetch|Node\Expr\ArrayDimFetch $prop - * @param Node\Arg[] $args - * @param array $attributes - * - * @return Expr\StaticCall - */ - protected function fixupPhp5StaticPropCall($prop, array $args, array $attributes) : Expr\StaticCall - { - if ($prop instanceof Node\Expr\StaticPropertyFetch) { - $name = $prop->name instanceof VarLikeIdentifier ? $prop->name->toString() : $prop->name; - $var = new Expr\Variable($name, $prop->name->getAttributes()); - return new Expr\StaticCall($prop->class, $var, $args, $attributes); - } elseif ($prop instanceof Node\Expr\ArrayDimFetch) { - $tmp = $prop; - while ($tmp->var instanceof Node\Expr\ArrayDimFetch) { - $tmp = $tmp->var; - } - /** @var Expr\StaticPropertyFetch $staticProp */ - $staticProp = $tmp->var; - // Set start attributes to attributes of innermost node - $tmp = $prop; - $this->fixupStartAttributes($tmp, $staticProp->name); - while ($tmp->var instanceof Node\Expr\ArrayDimFetch) { - $tmp = $tmp->var; - $this->fixupStartAttributes($tmp, $staticProp->name); - } - $name = $staticProp->name instanceof VarLikeIdentifier ? $staticProp->name->toString() : $staticProp->name; - $tmp->var = new Expr\Variable($name, $staticProp->name->getAttributes()); - return new Expr\StaticCall($staticProp->class, $prop, $args, $attributes); - } else { - throw new \Exception(); - } - } - protected function fixupStartAttributes(Node $to, Node $from) - { - $startAttributes = ['startLine', 'startFilePos', 'startTokenPos']; - foreach ($startAttributes as $startAttribute) { - if ($from->hasAttribute($startAttribute)) { - $to->setAttribute($startAttribute, $from->getAttribute($startAttribute)); - } - } - } + /** @return Name|Identifier */ protected function handleBuiltinTypes(Name $name) { - $builtinTypes = ['bool' => \true, 'int' => \true, 'float' => \true, 'string' => \true, 'iterable' => \true, 'void' => \true, 'object' => \true, 'null' => \true, 'false' => \true, 'mixed' => \true, 'never' => \true, 'true' => \true]; if (!$name->isUnqualified()) { return $name; } $lowerName = $name->toLowerString(); - if (!isset($builtinTypes[$lowerName])) { + if (!$this->phpVersion->supportsBuiltinType($lowerName)) { return $name; } return new Node\Identifier($lowerName, $name->getAttributes()); @@ -21977,87 +20884,94 @@ abstract class ParserAbstract implements Parser /** * Get combined start and end attributes at a stack location * - * @param int $pos Stack location + * @param int $stackPos Stack location * - * @return array Combined start and end attributes + * @return array Combined start and end attributes */ - protected function getAttributesAt(int $pos) : array + protected function getAttributesAt(int $stackPos): array { - return $this->startAttributeStack[$pos] + $this->endAttributeStack[$pos]; + return $this->getAttributes($this->tokenStartStack[$stackPos], $this->tokenEndStack[$stackPos]); } - protected function getFloatCastKind(string $cast) : int + protected function getFloatCastKind(string $cast): int { - $cast = \strtolower($cast); - if (\strpos($cast, 'float') !== \false) { + $cast = strtolower($cast); + if (strpos($cast, 'float') !== \false) { return Double::KIND_FLOAT; } - if (\strpos($cast, 'real') !== \false) { + if (strpos($cast, 'real') !== \false) { return Double::KIND_REAL; } return Double::KIND_DOUBLE; } - protected function parseLNumber($str, $attributes, $allowInvalidOctal = \false) + /** @param array $attributes */ + protected function parseLNumber(string $str, array $attributes, bool $allowInvalidOctal = \false): Int_ { try { - return LNumber::fromString($str, $attributes, $allowInvalidOctal); + return Int_::fromString($str, $attributes, $allowInvalidOctal); } catch (Error $error) { $this->emitError($error); // Use dummy value - return new LNumber(0, $attributes); + return new Int_(0, $attributes); } } /** * Parse a T_NUM_STRING token into either an integer or string node. * - * @param string $str Number string - * @param array $attributes Attributes + * @param string $str Number string + * @param array $attributes Attributes * - * @return LNumber|String_ Integer or string node. + * @return Int_|String_ Integer or string node. */ protected function parseNumString(string $str, array $attributes) { - if (!\preg_match('/^(?:0|-?[1-9][0-9]*)$/', $str)) { + if (!preg_match('/^(?:0|-?[1-9][0-9]*)$/', $str)) { return new String_($str, $attributes); } $num = +$str; - if (!\is_int($num)) { + if (!is_int($num)) { return new String_($str, $attributes); } - return new LNumber($num, $attributes); + return new Int_($num, $attributes); } - protected function stripIndentation(string $string, int $indentLen, string $indentChar, bool $newlineAtStart, bool $newlineAtEnd, array $attributes) + /** @param array $attributes */ + protected function stripIndentation(string $string, int $indentLen, string $indentChar, bool $newlineAtStart, bool $newlineAtEnd, array $attributes): string { if ($indentLen === 0) { return $string; } - $start = $newlineAtStart ? '(?:(?<=\\n)|\\A)' : '(?<=\\n)'; - $end = $newlineAtEnd ? '(?:(?=[\\r\\n])|\\z)' : '(?=[\\r\\n])'; - $regex = '/' . $start . '([ \\t]*)(' . $end . ')?/'; - return \preg_replace_callback($regex, function ($matches) use($indentLen, $indentChar, $attributes) { - $prefix = \substr($matches[1], 0, $indentLen); - if (\false !== \strpos($prefix, $indentChar === " " ? "\t" : " ")) { + $start = $newlineAtStart ? '(?:(?<=\n)|\A)' : '(?<=\n)'; + $end = $newlineAtEnd ? '(?:(?=[\r\n])|\z)' : '(?=[\r\n])'; + $regex = '/' . $start . '([ \t]*)(' . $end . ')?/'; + return preg_replace_callback($regex, function ($matches) use ($indentLen, $indentChar, $attributes) { + $prefix = substr($matches[1], 0, $indentLen); + if (\false !== strpos($prefix, ($indentChar === " ") ? "\t" : " ")) { $this->emitError(new Error('Invalid indentation - tabs and spaces cannot be mixed', $attributes)); - } elseif (\strlen($prefix) < $indentLen && !isset($matches[2])) { + } elseif (strlen($prefix) < $indentLen && !isset($matches[2])) { $this->emitError(new Error('Invalid body indentation level ' . '(expecting an indentation level of at least ' . $indentLen . ')', $attributes)); } - return \substr($matches[0], \strlen($prefix)); + return substr($matches[0], strlen($prefix)); }, $string); } - protected function parseDocString(string $startToken, $contents, string $endToken, array $attributes, array $endTokenAttributes, bool $parseUnicodeEscape) + /** + * @param string|(Expr|InterpolatedStringPart)[] $contents + * @param array $attributes + * @param array $endTokenAttributes + */ + protected function parseDocString(string $startToken, $contents, string $endToken, array $attributes, array $endTokenAttributes, bool $parseUnicodeEscape): Expr { - $kind = \strpos($startToken, "'") === \false ? String_::KIND_HEREDOC : String_::KIND_NOWDOC; - $regex = '/\\A[bB]?<<<[ \\t]*[\'"]?([a-zA-Z_\\x7f-\\xff][a-zA-Z0-9_\\x7f-\\xff]*)[\'"]?(?:\\r\\n|\\n|\\r)\\z/'; - $result = \preg_match($regex, $startToken, $matches); - \assert($result === 1); + $kind = (strpos($startToken, "'") === \false) ? String_::KIND_HEREDOC : String_::KIND_NOWDOC; + $regex = '/\A[bB]?<<<[ \t]*[\'"]?([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)[\'"]?(?:\r\n|\n|\r)\z/'; + $result = preg_match($regex, $startToken, $matches); + assert($result === 1); $label = $matches[1]; - $result = \preg_match('/\\A[ \\t]*/', $endToken, $matches); - \assert($result === 1); + $result = preg_match('/\A[ \t]*/', $endToken, $matches); + assert($result === 1); $indentation = $matches[0]; $attributes['kind'] = $kind; $attributes['docLabel'] = $label; $attributes['docIndentation'] = $indentation; - $indentHasSpaces = \false !== \strpos($indentation, " "); - $indentHasTabs = \false !== \strpos($indentation, "\t"); + $indentHasSpaces = \false !== strpos($indentation, " "); + $indentHasTabs = \false !== strpos($indentation, "\t"); if ($indentHasSpaces && $indentHasTabs) { $this->emitError(new Error('Invalid indentation - tabs and spaces cannot be mixed', $endTokenAttributes)); // Proceed processing as if this doc string is not indented @@ -22067,67 +20981,136 @@ abstract class ParserAbstract implements Parser $indentChar = $indentHasSpaces ? " " : "\t"; if (\is_string($contents)) { if ($contents === '') { + $attributes['rawValue'] = $contents; return new String_('', $attributes); } $contents = $this->stripIndentation($contents, $indentLen, $indentChar, \true, \true, $attributes); - $contents = \preg_replace('~(\\r\\n|\\n|\\r)\\z~', '', $contents); + $contents = preg_replace('~(\r\n|\n|\r)\z~', '', $contents); + $attributes['rawValue'] = $contents; if ($kind === String_::KIND_HEREDOC) { $contents = String_::parseEscapeSequences($contents, null, $parseUnicodeEscape); } return new String_($contents, $attributes); } else { - \assert(\count($contents) > 0); - if (!$contents[0] instanceof Node\Scalar\EncapsedStringPart) { + assert(count($contents) > 0); + if (!$contents[0] instanceof Node\InterpolatedStringPart) { // If there is no leading encapsed string part, pretend there is an empty one $this->stripIndentation('', $indentLen, $indentChar, \true, \false, $contents[0]->getAttributes()); } $newContents = []; foreach ($contents as $i => $part) { - if ($part instanceof Node\Scalar\EncapsedStringPart) { + if ($part instanceof Node\InterpolatedStringPart) { $isLast = $i === \count($contents) - 1; $part->value = $this->stripIndentation($part->value, $indentLen, $indentChar, $i === 0, $isLast, $part->getAttributes()); - $part->value = String_::parseEscapeSequences($part->value, null, $parseUnicodeEscape); if ($isLast) { - $part->value = \preg_replace('~(\\r\\n|\\n|\\r)\\z~', '', $part->value); + $part->value = preg_replace('~(\r\n|\n|\r)\z~', '', $part->value); } + $part->setAttribute('rawValue', $part->value); + $part->value = String_::parseEscapeSequences($part->value, null, $parseUnicodeEscape); if ('' === $part->value) { continue; } } $newContents[] = $part; } - return new Encapsed($newContents, $attributes); + return new InterpolatedString($newContents, $attributes); } } + protected function createCommentFromToken(Token $token, int $tokenPos): Comment + { + assert($token->id === \T_COMMENT || $token->id == \T_DOC_COMMENT); + return (\T_DOC_COMMENT === $token->id) ? new Comment\Doc($token->text, $token->line, $token->pos, $tokenPos, $token->getEndLine(), $token->getEndPos() - 1, $tokenPos) : new Comment($token->text, $token->line, $token->pos, $tokenPos, $token->getEndLine(), $token->getEndPos() - 1, $tokenPos); + } /** - * Create attributes for a zero-length common-capturing nop. - * - * @param Comment[] $comments - * @return array + * Get last comment before the given token position, if any */ - protected function createCommentNopAttributes(array $comments) + protected function getCommentBeforeToken(int $tokenPos): ?Comment { - $comment = $comments[\count($comments) - 1]; + while (--$tokenPos >= 0) { + $token = $this->tokens[$tokenPos]; + if (!isset($this->dropTokens[$token->id])) { + break; + } + if ($token->id === \T_COMMENT || $token->id === \T_DOC_COMMENT) { + return $this->createCommentFromToken($token, $tokenPos); + } + } + return null; + } + /** + * Create a zero-length nop to capture preceding comments, if any. + */ + protected function maybeCreateZeroLengthNop(int $tokenPos): ?Nop + { + $comment = $this->getCommentBeforeToken($tokenPos); + if ($comment === null) { + return null; + } $commentEndLine = $comment->getEndLine(); $commentEndFilePos = $comment->getEndFilePos(); $commentEndTokenPos = $comment->getEndTokenPos(); - $attributes = ['comments' => $comments]; - if (-1 !== $commentEndLine) { - $attributes['startLine'] = $commentEndLine; - $attributes['endLine'] = $commentEndLine; + $attributes = ['startLine' => $commentEndLine, 'endLine' => $commentEndLine, 'startFilePos' => $commentEndFilePos + 1, 'endFilePos' => $commentEndFilePos, 'startTokenPos' => $commentEndTokenPos + 1, 'endTokenPos' => $commentEndTokenPos]; + return new Nop($attributes); + } + protected function maybeCreateNop(int $tokenStartPos, int $tokenEndPos): ?Nop + { + if ($this->getCommentBeforeToken($tokenStartPos) === null) { + return null; } - if (-1 !== $commentEndFilePos) { - $attributes['startFilePos'] = $commentEndFilePos + 1; - $attributes['endFilePos'] = $commentEndFilePos; + return new Nop($this->getAttributes($tokenStartPos, $tokenEndPos)); + } + protected function handleHaltCompiler(): string + { + // Prevent the lexer from returning any further tokens. + $nextToken = $this->tokens[$this->tokenPos + 1]; + $this->tokenPos = \count($this->tokens) - 2; + // Return text after __halt_compiler. + return ($nextToken->id === \T_INLINE_HTML) ? $nextToken->text : ''; + } + protected function inlineHtmlHasLeadingNewline(int $stackPos): bool + { + $tokenPos = $this->tokenStartStack[$stackPos]; + $token = $this->tokens[$tokenPos]; + assert($token->id == \T_INLINE_HTML); + if ($tokenPos > 0) { + $prevToken = $this->tokens[$tokenPos - 1]; + assert($prevToken->id == \T_CLOSE_TAG); + return \false !== strpos($prevToken->text, "\n") || \false !== strpos($prevToken->text, "\r"); } - if (-1 !== $commentEndTokenPos) { - $attributes['startTokenPos'] = $commentEndTokenPos + 1; - $attributes['endTokenPos'] = $commentEndTokenPos; + return \true; + } + /** + * @return array + */ + protected function createEmptyElemAttributes(int $tokenPos): array + { + return $this->getAttributesForToken($tokenPos); + } + protected function fixupArrayDestructuring(Array_ $node): Expr\List_ + { + $this->createdArrays->detach($node); + return new Expr\List_(array_map(function (Node\ArrayItem $item) { + if ($item->value instanceof Expr\Error) { + // We used Error as a placeholder for empty elements, which are legal for destructuring. + return null; + } + if ($item->value instanceof Array_) { + return new Node\ArrayItem($this->fixupArrayDestructuring($item->value), $item->key, $item->byRef, $item->getAttributes()); + } + return $item; + }, $node->items), ['kind' => Expr\List_::KIND_ARRAY] + $node->getAttributes()); + } + protected function postprocessList(Expr\List_ $node): void + { + foreach ($node->items as $i => $item) { + if ($item->value instanceof Expr\Error) { + // We used Error as a placeholder for empty elements, which are legal for destructuring. + $node->items[$i] = null; + } } - return $attributes; } /** @param ElseIf_|Else_ $node */ - protected function fixupAlternativeElse($node) + protected function fixupAlternativeElse($node): void { // Make sure a trailing nop statement carrying comments is part of the node. $numStmts = \count($node->stmts); @@ -22144,38 +21127,38 @@ abstract class ParserAbstract implements Parser } } } - protected function checkClassModifier($a, $b, $modifierPos) + protected function checkClassModifier(int $a, int $b, int $modifierPos): void { try { - Class_::verifyClassModifier($a, $b); + Modifiers::verifyClassModifier($a, $b); } catch (Error $error) { $error->setAttributes($this->getAttributesAt($modifierPos)); $this->emitError($error); } } - protected function checkModifier($a, $b, $modifierPos) + protected function checkModifier(int $a, int $b, int $modifierPos): void { // Jumping through some hoops here because verifyModifier() is also used elsewhere try { - Class_::verifyModifier($a, $b); + Modifiers::verifyModifier($a, $b); } catch (Error $error) { $error->setAttributes($this->getAttributesAt($modifierPos)); $this->emitError($error); } } - protected function checkParam(Param $node) + protected function checkParam(Param $node): void { if ($node->variadic && null !== $node->default) { $this->emitError(new Error('Variadic parameter cannot have a default value', $node->default->getAttributes())); } } - protected function checkTryCatch(TryCatch $node) + protected function checkTryCatch(TryCatch $node): void { if (empty($node->catches) && null === $node->finally) { $this->emitError(new Error('Cannot use try without catch or finally', $node->getAttributes())); } } - protected function checkNamespace(Namespace_ $node) + protected function checkNamespace(Namespace_ $node): void { if (null !== $node->stmts) { foreach ($node->stmts as $stmt) { @@ -22185,152 +21168,385 @@ abstract class ParserAbstract implements Parser } } } - private function checkClassName($name, $namePos) + private function checkClassName(?Identifier $name, int $namePos): void { if (null !== $name && $name->isSpecialClassName()) { - $this->emitError(new Error(\sprintf('Cannot use \'%s\' as class name as it is reserved', $name), $this->getAttributesAt($namePos))); + $this->emitError(new Error(sprintf('Cannot use \'%s\' as class name as it is reserved', $name), $this->getAttributesAt($namePos))); } } - private function checkImplementedInterfaces(array $interfaces) + /** @param Name[] $interfaces */ + private function checkImplementedInterfaces(array $interfaces): void { foreach ($interfaces as $interface) { if ($interface->isSpecialClassName()) { - $this->emitError(new Error(\sprintf('Cannot use \'%s\' as interface name as it is reserved', $interface), $interface->getAttributes())); + $this->emitError(new Error(sprintf('Cannot use \'%s\' as interface name as it is reserved', $interface), $interface->getAttributes())); } } } - protected function checkClass(Class_ $node, $namePos) + protected function checkClass(Class_ $node, int $namePos): void { $this->checkClassName($node->name, $namePos); if ($node->extends && $node->extends->isSpecialClassName()) { - $this->emitError(new Error(\sprintf('Cannot use \'%s\' as class name as it is reserved', $node->extends), $node->extends->getAttributes())); + $this->emitError(new Error(sprintf('Cannot use \'%s\' as class name as it is reserved', $node->extends), $node->extends->getAttributes())); } $this->checkImplementedInterfaces($node->implements); } - protected function checkInterface(Interface_ $node, $namePos) + protected function checkInterface(Interface_ $node, int $namePos): void { $this->checkClassName($node->name, $namePos); $this->checkImplementedInterfaces($node->extends); } - protected function checkEnum(Enum_ $node, $namePos) + protected function checkEnum(Enum_ $node, int $namePos): void { $this->checkClassName($node->name, $namePos); $this->checkImplementedInterfaces($node->implements); } - protected function checkClassMethod(ClassMethod $node, $modifierPos) + protected function checkClassMethod(ClassMethod $node, int $modifierPos): void { - if ($node->flags & Class_::MODIFIER_STATIC) { + if ($node->flags & Modifiers::STATIC) { switch ($node->name->toLowerString()) { case '__construct': - $this->emitError(new Error(\sprintf('Constructor %s() cannot be static', $node->name), $this->getAttributesAt($modifierPos))); + $this->emitError(new Error(sprintf('Constructor %s() cannot be static', $node->name), $this->getAttributesAt($modifierPos))); break; case '__destruct': - $this->emitError(new Error(\sprintf('Destructor %s() cannot be static', $node->name), $this->getAttributesAt($modifierPos))); + $this->emitError(new Error(sprintf('Destructor %s() cannot be static', $node->name), $this->getAttributesAt($modifierPos))); break; case '__clone': - $this->emitError(new Error(\sprintf('Clone method %s() cannot be static', $node->name), $this->getAttributesAt($modifierPos))); + $this->emitError(new Error(sprintf('Clone method %s() cannot be static', $node->name), $this->getAttributesAt($modifierPos))); break; } } - if ($node->flags & Class_::MODIFIER_READONLY) { - $this->emitError(new Error(\sprintf('Method %s() cannot be readonly', $node->name), $this->getAttributesAt($modifierPos))); + if ($node->flags & Modifiers::READONLY) { + $this->emitError(new Error(sprintf('Method %s() cannot be readonly', $node->name), $this->getAttributesAt($modifierPos))); } } - protected function checkClassConst(ClassConst $node, $modifierPos) + protected function checkClassConst(ClassConst $node, int $modifierPos): void { - if ($node->flags & Class_::MODIFIER_STATIC) { + if ($node->flags & Modifiers::STATIC) { $this->emitError(new Error("Cannot use 'static' as constant modifier", $this->getAttributesAt($modifierPos))); } - if ($node->flags & Class_::MODIFIER_ABSTRACT) { + if ($node->flags & Modifiers::ABSTRACT) { $this->emitError(new Error("Cannot use 'abstract' as constant modifier", $this->getAttributesAt($modifierPos))); } - if ($node->flags & Class_::MODIFIER_READONLY) { + if ($node->flags & Modifiers::READONLY) { $this->emitError(new Error("Cannot use 'readonly' as constant modifier", $this->getAttributesAt($modifierPos))); } } - protected function checkProperty(Property $node, $modifierPos) + protected function checkProperty(Property $node, int $modifierPos): void { - if ($node->flags & Class_::MODIFIER_ABSTRACT) { + if ($node->flags & Modifiers::ABSTRACT) { $this->emitError(new Error('Properties cannot be declared abstract', $this->getAttributesAt($modifierPos))); } - if ($node->flags & Class_::MODIFIER_FINAL) { + if ($node->flags & Modifiers::FINAL) { $this->emitError(new Error('Properties cannot be declared final', $this->getAttributesAt($modifierPos))); } } - protected function checkUseUse(UseUse $node, $namePos) + protected function checkUseUse(UseItem $node, int $namePos): void { if ($node->alias && $node->alias->isSpecialClassName()) { - $this->emitError(new Error(\sprintf('Cannot use %s as %s because \'%2$s\' is a special class name', $node->name, $node->alias), $this->getAttributesAt($namePos))); + $this->emitError(new Error(sprintf('Cannot use %s as %s because \'%2$s\' is a special class name', $node->name, $node->alias), $this->getAttributesAt($namePos))); } } + /** + * Creates the token map. + * + * The token map maps the PHP internal token identifiers + * to the identifiers used by the Parser. Additionally it + * maps T_OPEN_TAG_WITH_ECHO to T_ECHO and T_CLOSE_TAG to ';'. + * + * @return array The token map + */ + protected function createTokenMap(): array + { + $tokenMap = []; + for ($i = 0; $i < 1000; ++$i) { + if ($i < 256) { + // Single-char tokens use an identity mapping. + $tokenMap[$i] = $i; + } elseif (\T_DOUBLE_COLON === $i) { + // T_DOUBLE_COLON is equivalent to T_PAAMAYIM_NEKUDOTAYIM + $tokenMap[$i] = static::T_PAAMAYIM_NEKUDOTAYIM; + } elseif (\T_OPEN_TAG_WITH_ECHO === $i) { + // T_OPEN_TAG_WITH_ECHO with dropped T_OPEN_TAG results in T_ECHO + $tokenMap[$i] = static::T_ECHO; + } elseif (\T_CLOSE_TAG === $i) { + // T_CLOSE_TAG is equivalent to ';' + $tokenMap[$i] = ord(';'); + } elseif ('UNKNOWN' !== $name = token_name($i)) { + if (defined($name = static::class . '::' . $name)) { + // Other tokens can be mapped directly + $tokenMap[$i] = constant($name); + } + } + } + // Assign tokens for which we define compatibility constants, as token_name() does not know them. + $tokenMap[\T_FN] = static::T_FN; + $tokenMap[\T_COALESCE_EQUAL] = static::T_COALESCE_EQUAL; + $tokenMap[\T_NAME_QUALIFIED] = static::T_NAME_QUALIFIED; + $tokenMap[\T_NAME_FULLY_QUALIFIED] = static::T_NAME_FULLY_QUALIFIED; + $tokenMap[\T_NAME_RELATIVE] = static::T_NAME_RELATIVE; + $tokenMap[\T_MATCH] = static::T_MATCH; + $tokenMap[\T_NULLSAFE_OBJECT_OPERATOR] = static::T_NULLSAFE_OBJECT_OPERATOR; + $tokenMap[\T_ATTRIBUTE] = static::T_ATTRIBUTE; + $tokenMap[\T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG] = static::T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG; + $tokenMap[\T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG] = static::T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG; + $tokenMap[\T_ENUM] = static::T_ENUM; + $tokenMap[\T_READONLY] = static::T_READONLY; + // We have create a map from PHP token IDs to external symbol IDs. + // Now map them to the internal symbol ID. + $fullTokenMap = []; + foreach ($tokenMap as $phpToken => $extSymbol) { + $intSymbol = $this->tokenToSymbol[$extSymbol]; + if ($intSymbol === $this->invalidSymbol) { + continue; + } + $fullTokenMap[$phpToken] = $intSymbol; + } + return $fullTokenMap; + } } isHostVersion()) { + $lexer = new Lexer(); + } else { + $lexer = new Lexer\Emulative($version); } - switch ($kind) { - case self::PREFER_PHP7: - return new Parser\Multiple([new Parser\Php7($lexer, $parserOptions), new Parser\Php5($lexer, $parserOptions)]); - case self::PREFER_PHP5: - return new Parser\Multiple([new Parser\Php5($lexer, $parserOptions), new Parser\Php7($lexer, $parserOptions)]); - case self::ONLY_PHP7: - return new Parser\Php7($lexer, $parserOptions); - case self::ONLY_PHP5: - return new Parser\Php5($lexer, $parserOptions); - default: - throw new \LogicException('Kind must be one of ::PREFER_PHP7, ::PREFER_PHP5, ::ONLY_PHP7 or ::ONLY_PHP5'); + if ($version->id >= 80000) { + return new Php8($lexer, $version); } + return new Php7($lexer, $version); } /** * Create a parser targeting the newest version supported by this library. Code for older * versions will be accepted if there have been no relevant backwards-compatibility breaks in * PHP. - * - * All supported lexer attributes (comments, startLine, endLine, startTokenPos, endTokenPos, - * startFilePos, endFilePos) will be enabled. */ - public function createForNewestSupportedVersion() : Parser + public function createForNewestSupportedVersion(): Parser { - return new Php7(new Emulative($this->getLexerOptions())); + return $this->createForVersion(PhpVersion::getNewestSupported()); } /** * Create a parser targeting the host PHP version, that is the PHP version we're currently * running on. This parser will not use any token emulation. - * - * All supported lexer attributes (comments, startLine, endLine, startTokenPos, endTokenPos, - * startFilePos, endFilePos) will be enabled. */ - public function createForHostVersion() : Parser + public function createForHostVersion(): Parser + { + return $this->createForVersion(PhpVersion::getHostVersion()); + } +} + 50100, 'callable' => 50400, 'bool' => 70000, 'int' => 70000, 'float' => 70000, 'string' => 70000, 'iterable' => 70100, 'void' => 70100, 'object' => 70200, 'null' => 80000, 'false' => 80000, 'mixed' => 80000, 'never' => 80100, 'true' => 80200]; + private function __construct(int $id) + { + $this->id = $id; + } + /** + * Create a PhpVersion object from major and minor version components. + */ + public static function fromComponents(int $major, int $minor): self + { + return new self($major * 10000 + $minor * 100); + } + /** + * Get the newest PHP version supported by this library. Support for this version may be partial, + * if it is still under development. + */ + public static function getNewestSupported(): self + { + return self::fromComponents(8, 3); + } + /** + * Get the host PHP version, that is the PHP version we're currently running on. + */ + public static function getHostVersion(): self + { + return self::fromComponents(\PHP_MAJOR_VERSION, \PHP_MINOR_VERSION); + } + /** + * Parse the version from a string like "8.1". + */ + public static function fromString(string $version): self + { + if (!preg_match('/^(\d+)\.(\d+)/', $version, $matches)) { + throw new \LogicException("Invalid PHP version \"{$version}\""); + } + return self::fromComponents((int) $matches[1], (int) $matches[2]); + } + /** + * Check whether two versions are the same. + */ + public function equals(PhpVersion $other): bool { - return new Php7(new Lexer($this->getLexerOptions())); + return $this->id === $other->id; } - private function getLexerOptions() : array + /** + * Check whether this version is greater than or equal to the argument. + */ + public function newerOrEqual(PhpVersion $other): bool + { + return $this->id >= $other->id; + } + /** + * Check whether this version is older than the argument. + */ + public function older(PhpVersion $other): bool + { + return $this->id < $other->id; + } + /** + * Check whether this is the host PHP version. + */ + public function isHostVersion(): bool + { + return $this->equals(self::getHostVersion()); + } + /** + * Check whether this PHP version supports the given builtin type. Type name must be lowercase. + */ + public function supportsBuiltinType(string $type): bool + { + $minVersion = self::BUILTIN_TYPE_VERSIONS[$type] ?? null; + return $minVersion !== null && $this->id >= $minVersion; + } + /** + * Whether this version supports [] array literals. + */ + public function supportsShortArraySyntax(): bool + { + return $this->id >= 50400; + } + /** + * Whether this version supports [] for destructuring. + */ + public function supportsShortArrayDestructuring(): bool + { + return $this->id >= 70100; + } + /** + * Whether this version supports flexible heredoc/nowdoc. + */ + public function supportsFlexibleHeredoc(): bool + { + return $this->id >= 70300; + } + /** + * Whether this version supports trailing commas in parameter lists. + */ + public function supportsTrailingCommaInParamList(): bool + { + return $this->id >= 80000; + } + /** + * Whether this version allows "$var =& new Obj". + */ + public function allowsAssignNewByReference(): bool + { + return $this->id < 70000; + } + /** + * Whether this version allows invalid octals like "08". + */ + public function allowsInvalidOctals(): bool + { + return $this->id < 70000; + } + /** + * Whether this version allows DEL (\x7f) to occur in identifiers. + */ + public function allowsDelInIdentifiers(): bool + { + return $this->id < 70100; + } + /** + * Whether this version supports yield in expression context without parentheses. + */ + public function supportsYieldWithoutParentheses(): bool { - return ['usedAttributes' => ['comments', 'startLine', 'endLine', 'startTokenPos', 'endTokenPos', 'startFilePos', 'endFilePos']]; + return $this->id >= 70000; } + /** + * Whether this version supports unicode escape sequences in strings. + */ + public function supportsUnicodeEscapes(): bool + { + return $this->id >= 70000; + } +} +pAttrGroups($node->attrGroups, \true) . $this->pModifiers($node->flags) . ($node->type ? $this->p($node->type) . ' ' : '') . ($node->byRef ? '&' : '') . ($node->variadic ? '...' : '') . $this->p($node->var) . ($node->default ? ' = ' . $this->p($node->default) : ''); } - protected function pArg(Node\Arg $node) + protected function pArg(Node\Arg $node): string { return ($node->name ? $node->name->toString() . ': ' : '') . ($node->byRef ? '&' : '') . ($node->unpack ? '...' : '') . $this->p($node->value); } - protected function pVariadicPlaceholder(Node\VariadicPlaceholder $node) + protected function pVariadicPlaceholder(Node\VariadicPlaceholder $node): string { return '...'; } - protected function pConst(Node\Const_ $node) + protected function pConst(Node\Const_ $node): string { return $node->name . ' = ' . $this->p($node->value); } - protected function pNullableType(Node\NullableType $node) + protected function pNullableType(Node\NullableType $node): string { return '?' . $this->p($node->type); } - protected function pUnionType(Node\UnionType $node) + protected function pUnionType(Node\UnionType $node): string { $types = []; foreach ($node->types as $typeNode) { @@ -22380,127 +21596,141 @@ class Standard extends PrettyPrinterAbstract } $types[] = $this->p($typeNode); } - return \implode('|', $types); + return implode('|', $types); } - protected function pIntersectionType(Node\IntersectionType $node) + protected function pIntersectionType(Node\IntersectionType $node): string { return $this->pImplode($node->types, '&'); } - protected function pIdentifier(Node\Identifier $node) + protected function pIdentifier(Node\Identifier $node): string { return $node->name; } - protected function pVarLikeIdentifier(Node\VarLikeIdentifier $node) + protected function pVarLikeIdentifier(Node\VarLikeIdentifier $node): string { return '$' . $node->name; } - protected function pAttribute(Node\Attribute $node) + protected function pAttribute(Node\Attribute $node): string { return $this->p($node->name) . ($node->args ? '(' . $this->pCommaSeparated($node->args) . ')' : ''); } - protected function pAttributeGroup(Node\AttributeGroup $node) + protected function pAttributeGroup(Node\AttributeGroup $node): string { return '#[' . $this->pCommaSeparated($node->attrs) . ']'; } // Names - protected function pName(Name $node) + protected function pName(Name $node): string { - return \implode('\\', $node->parts); + return $node->name; } - protected function pName_FullyQualified(Name\FullyQualified $node) + protected function pName_FullyQualified(Name\FullyQualified $node): string { - return '\\' . \implode('\\', $node->parts); + return '\\' . $node->name; } - protected function pName_Relative(Name\Relative $node) + protected function pName_Relative(Name\Relative $node): string { - return 'namespace\\' . \implode('\\', $node->parts); + return 'namespace\\' . $node->name; } // Magic Constants - protected function pScalar_MagicConst_Class(MagicConst\Class_ $node) + protected function pScalar_MagicConst_Class(MagicConst\Class_ $node): string { return '__CLASS__'; } - protected function pScalar_MagicConst_Dir(MagicConst\Dir $node) + protected function pScalar_MagicConst_Dir(MagicConst\Dir $node): string { return '__DIR__'; } - protected function pScalar_MagicConst_File(MagicConst\File $node) + protected function pScalar_MagicConst_File(MagicConst\File $node): string { return '__FILE__'; } - protected function pScalar_MagicConst_Function(MagicConst\Function_ $node) + protected function pScalar_MagicConst_Function(MagicConst\Function_ $node): string { return '__FUNCTION__'; } - protected function pScalar_MagicConst_Line(MagicConst\Line $node) + protected function pScalar_MagicConst_Line(MagicConst\Line $node): string { return '__LINE__'; } - protected function pScalar_MagicConst_Method(MagicConst\Method $node) + protected function pScalar_MagicConst_Method(MagicConst\Method $node): string { return '__METHOD__'; } - protected function pScalar_MagicConst_Namespace(MagicConst\Namespace_ $node) + protected function pScalar_MagicConst_Namespace(MagicConst\Namespace_ $node): string { return '__NAMESPACE__'; } - protected function pScalar_MagicConst_Trait(MagicConst\Trait_ $node) + protected function pScalar_MagicConst_Trait(MagicConst\Trait_ $node): string { return '__TRAIT__'; } // Scalars - protected function pScalar_String(Scalar\String_ $node) + private function indentString(string $str): string + { + return str_replace("\n", $this->nl, $str); + } + protected function pScalar_String(Scalar\String_ $node): string { $kind = $node->getAttribute('kind', Scalar\String_::KIND_SINGLE_QUOTED); switch ($kind) { case Scalar\String_::KIND_NOWDOC: $label = $node->getAttribute('docLabel'); if ($label && !$this->containsEndLabel($node->value, $label)) { + $shouldIdent = $this->phpVersion->supportsFlexibleHeredoc(); + $nl = $shouldIdent ? $this->nl : $this->newline; if ($node->value === '') { - return "<<<'{$label}'\n{$label}" . $this->docStringEndToken; + return "<<<'{$label}'{$nl}{$label}{$this->docStringEndToken}"; + } + // Make sure trailing \r is not combined with following \n into CRLF. + if ($node->value[strlen($node->value) - 1] !== "\r") { + $value = $shouldIdent ? $this->indentString($node->value) : $node->value; + return "<<<'{$label}'{$nl}{$value}{$nl}{$label}{$this->docStringEndToken}"; } - return "<<<'{$label}'\n{$node->value}\n{$label}" . $this->docStringEndToken; } /* break missing intentionally */ + // no break case Scalar\String_::KIND_SINGLE_QUOTED: return $this->pSingleQuotedString($node->value); case Scalar\String_::KIND_HEREDOC: $label = $node->getAttribute('docLabel'); - if ($label && !$this->containsEndLabel($node->value, $label)) { - if ($node->value === '') { - return "<<<{$label}\n{$label}" . $this->docStringEndToken; + $escaped = $this->escapeString($node->value, null); + if ($label && !$this->containsEndLabel($escaped, $label)) { + $nl = $this->phpVersion->supportsFlexibleHeredoc() ? $this->nl : $this->newline; + if ($escaped === '') { + return "<<<{$label}{$nl}{$label}{$this->docStringEndToken}"; } - $escaped = $this->escapeString($node->value, null); - return "<<<{$label}\n" . $escaped . "\n{$label}" . $this->docStringEndToken; + return "<<<{$label}{$nl}{$escaped}{$nl}{$label}{$this->docStringEndToken}"; } /* break missing intentionally */ + // no break case Scalar\String_::KIND_DOUBLE_QUOTED: return '"' . $this->escapeString($node->value, '"') . '"'; } throw new \Exception('Invalid string kind'); } - protected function pScalar_Encapsed(Scalar\Encapsed $node) + protected function pScalar_InterpolatedString(Scalar\InterpolatedString $node): string { if ($node->getAttribute('kind') === Scalar\String_::KIND_HEREDOC) { $label = $node->getAttribute('docLabel'); if ($label && !$this->encapsedContainsEndLabel($node->parts, $label)) { - if (\count($node->parts) === 1 && $node->parts[0] instanceof Scalar\EncapsedStringPart && $node->parts[0]->value === '') { - return "<<<{$label}\n{$label}" . $this->docStringEndToken; + $nl = $this->phpVersion->supportsFlexibleHeredoc() ? $this->nl : $this->newline; + if (count($node->parts) === 1 && $node->parts[0] instanceof Node\InterpolatedStringPart && $node->parts[0]->value === '') { + return "<<<{$label}{$nl}{$label}{$this->docStringEndToken}"; } - return "<<<{$label}\n" . $this->pEncapsList($node->parts, null) . "\n{$label}" . $this->docStringEndToken; + return "<<<{$label}{$nl}" . $this->pEncapsList($node->parts, null) . "{$nl}{$label}{$this->docStringEndToken}"; } } return '"' . $this->pEncapsList($node->parts, '"') . '"'; } - protected function pScalar_LNumber(Scalar\LNumber $node) + protected function pScalar_Int(Scalar\Int_ $node): string { if ($node->value === -\PHP_INT_MAX - 1) { // PHP_INT_MIN cannot be represented as a literal, // because the sign is not part of the literal return '(-' . \PHP_INT_MAX . '-1)'; } - $kind = $node->getAttribute('kind', Scalar\LNumber::KIND_DEC); - if (Scalar\LNumber::KIND_DEC === $kind) { + $kind = $node->getAttribute('kind', Scalar\Int_::KIND_DEC); + if (Scalar\Int_::KIND_DEC === $kind) { return (string) $node->value; } if ($node->value < 0) { @@ -22511,351 +21741,345 @@ class Standard extends PrettyPrinterAbstract $str = (string) $node->value; } switch ($kind) { - case Scalar\LNumber::KIND_BIN: - return $sign . '0b' . \base_convert($str, 10, 2); - case Scalar\LNumber::KIND_OCT: - return $sign . '0' . \base_convert($str, 10, 8); - case Scalar\LNumber::KIND_HEX: - return $sign . '0x' . \base_convert($str, 10, 16); + case Scalar\Int_::KIND_BIN: + return $sign . '0b' . base_convert($str, 10, 2); + case Scalar\Int_::KIND_OCT: + return $sign . '0' . base_convert($str, 10, 8); + case Scalar\Int_::KIND_HEX: + return $sign . '0x' . base_convert($str, 10, 16); } throw new \Exception('Invalid number kind'); } - protected function pScalar_DNumber(Scalar\DNumber $node) + protected function pScalar_Float(Scalar\Float_ $node): string { - if (!\is_finite($node->value)) { + if (!is_finite($node->value)) { if ($node->value === \INF) { - return '\\INF'; - } elseif ($node->value === -\INF) { - return '-\\INF'; + return '1.0E+1000'; + } + if ($node->value === -\INF) { + return '-1.0E+1000'; } else { - return '\\NAN'; + return '\NAN'; } } // Try to find a short full-precision representation - $stringValue = \sprintf('%.16G', $node->value); - if ($node->value !== (double) $stringValue) { - $stringValue = \sprintf('%.17G', $node->value); + $stringValue = sprintf('%.16G', $node->value); + if ($node->value !== (float) $stringValue) { + $stringValue = sprintf('%.17G', $node->value); } // %G is locale dependent and there exists no locale-independent alternative. We don't want // mess with switching locales here, so let's assume that a comma is the only non-standard // decimal separator we may encounter... - $stringValue = \str_replace(',', '.', $stringValue); + $stringValue = str_replace(',', '.', $stringValue); // ensure that number is really printed as float - return \preg_match('/^-?[0-9]+$/', $stringValue) ? $stringValue . '.0' : $stringValue; - } - protected function pScalar_EncapsedStringPart(Scalar\EncapsedStringPart $node) - { - throw new \LogicException('Cannot directly print EncapsedStringPart'); + return preg_match('/^-?[0-9]+$/', $stringValue) ? $stringValue . '.0' : $stringValue; } // Assignments - protected function pExpr_Assign(Expr\Assign $node) + protected function pExpr_Assign(Expr\Assign $node, int $precedence, int $lhsPrecedence): string { - return $this->pInfixOp(Expr\Assign::class, $node->var, ' = ', $node->expr); + return $this->pPrefixOp(Expr\Assign::class, $this->p($node->var) . ' = ', $node->expr, $precedence, $lhsPrecedence); } - protected function pExpr_AssignRef(Expr\AssignRef $node) + protected function pExpr_AssignRef(Expr\AssignRef $node, int $precedence, int $lhsPrecedence): string { - return $this->pInfixOp(Expr\AssignRef::class, $node->var, ' =& ', $node->expr); + return $this->pPrefixOp(Expr\AssignRef::class, $this->p($node->var) . ' =& ', $node->expr, $precedence, $lhsPrecedence); } - protected function pExpr_AssignOp_Plus(AssignOp\Plus $node) + protected function pExpr_AssignOp_Plus(AssignOp\Plus $node, int $precedence, int $lhsPrecedence): string { - return $this->pInfixOp(AssignOp\Plus::class, $node->var, ' += ', $node->expr); + return $this->pPrefixOp(AssignOp\Plus::class, $this->p($node->var) . ' += ', $node->expr, $precedence, $lhsPrecedence); } - protected function pExpr_AssignOp_Minus(AssignOp\Minus $node) + protected function pExpr_AssignOp_Minus(AssignOp\Minus $node, int $precedence, int $lhsPrecedence): string { - return $this->pInfixOp(AssignOp\Minus::class, $node->var, ' -= ', $node->expr); + return $this->pPrefixOp(AssignOp\Minus::class, $this->p($node->var) . ' -= ', $node->expr, $precedence, $lhsPrecedence); } - protected function pExpr_AssignOp_Mul(AssignOp\Mul $node) + protected function pExpr_AssignOp_Mul(AssignOp\Mul $node, int $precedence, int $lhsPrecedence): string { - return $this->pInfixOp(AssignOp\Mul::class, $node->var, ' *= ', $node->expr); + return $this->pPrefixOp(AssignOp\Mul::class, $this->p($node->var) . ' *= ', $node->expr, $precedence, $lhsPrecedence); } - protected function pExpr_AssignOp_Div(AssignOp\Div $node) + protected function pExpr_AssignOp_Div(AssignOp\Div $node, int $precedence, int $lhsPrecedence): string { - return $this->pInfixOp(AssignOp\Div::class, $node->var, ' /= ', $node->expr); + return $this->pPrefixOp(AssignOp\Div::class, $this->p($node->var) . ' /= ', $node->expr, $precedence, $lhsPrecedence); } - protected function pExpr_AssignOp_Concat(AssignOp\Concat $node) + protected function pExpr_AssignOp_Concat(AssignOp\Concat $node, int $precedence, int $lhsPrecedence): string { - return $this->pInfixOp(AssignOp\Concat::class, $node->var, ' .= ', $node->expr); + return $this->pPrefixOp(AssignOp\Concat::class, $this->p($node->var) . ' .= ', $node->expr, $precedence, $lhsPrecedence); } - protected function pExpr_AssignOp_Mod(AssignOp\Mod $node) + protected function pExpr_AssignOp_Mod(AssignOp\Mod $node, int $precedence, int $lhsPrecedence): string { - return $this->pInfixOp(AssignOp\Mod::class, $node->var, ' %= ', $node->expr); + return $this->pPrefixOp(AssignOp\Mod::class, $this->p($node->var) . ' %= ', $node->expr, $precedence, $lhsPrecedence); } - protected function pExpr_AssignOp_BitwiseAnd(AssignOp\BitwiseAnd $node) + protected function pExpr_AssignOp_BitwiseAnd(AssignOp\BitwiseAnd $node, int $precedence, int $lhsPrecedence): string { - return $this->pInfixOp(AssignOp\BitwiseAnd::class, $node->var, ' &= ', $node->expr); + return $this->pPrefixOp(AssignOp\BitwiseAnd::class, $this->p($node->var) . ' &= ', $node->expr, $precedence, $lhsPrecedence); } - protected function pExpr_AssignOp_BitwiseOr(AssignOp\BitwiseOr $node) + protected function pExpr_AssignOp_BitwiseOr(AssignOp\BitwiseOr $node, int $precedence, int $lhsPrecedence): string { - return $this->pInfixOp(AssignOp\BitwiseOr::class, $node->var, ' |= ', $node->expr); + return $this->pPrefixOp(AssignOp\BitwiseOr::class, $this->p($node->var) . ' |= ', $node->expr, $precedence, $lhsPrecedence); } - protected function pExpr_AssignOp_BitwiseXor(AssignOp\BitwiseXor $node) + protected function pExpr_AssignOp_BitwiseXor(AssignOp\BitwiseXor $node, int $precedence, int $lhsPrecedence): string { - return $this->pInfixOp(AssignOp\BitwiseXor::class, $node->var, ' ^= ', $node->expr); + return $this->pPrefixOp(AssignOp\BitwiseXor::class, $this->p($node->var) . ' ^= ', $node->expr, $precedence, $lhsPrecedence); } - protected function pExpr_AssignOp_ShiftLeft(AssignOp\ShiftLeft $node) + protected function pExpr_AssignOp_ShiftLeft(AssignOp\ShiftLeft $node, int $precedence, int $lhsPrecedence): string { - return $this->pInfixOp(AssignOp\ShiftLeft::class, $node->var, ' <<= ', $node->expr); + return $this->pPrefixOp(AssignOp\ShiftLeft::class, $this->p($node->var) . ' <<= ', $node->expr, $precedence, $lhsPrecedence); } - protected function pExpr_AssignOp_ShiftRight(AssignOp\ShiftRight $node) + protected function pExpr_AssignOp_ShiftRight(AssignOp\ShiftRight $node, int $precedence, int $lhsPrecedence): string { - return $this->pInfixOp(AssignOp\ShiftRight::class, $node->var, ' >>= ', $node->expr); + return $this->pPrefixOp(AssignOp\ShiftRight::class, $this->p($node->var) . ' >>= ', $node->expr, $precedence, $lhsPrecedence); } - protected function pExpr_AssignOp_Pow(AssignOp\Pow $node) + protected function pExpr_AssignOp_Pow(AssignOp\Pow $node, int $precedence, int $lhsPrecedence): string { - return $this->pInfixOp(AssignOp\Pow::class, $node->var, ' **= ', $node->expr); + return $this->pPrefixOp(AssignOp\Pow::class, $this->p($node->var) . ' **= ', $node->expr, $precedence, $lhsPrecedence); } - protected function pExpr_AssignOp_Coalesce(AssignOp\Coalesce $node) + protected function pExpr_AssignOp_Coalesce(AssignOp\Coalesce $node, int $precedence, int $lhsPrecedence): string { - return $this->pInfixOp(AssignOp\Coalesce::class, $node->var, ' ??= ', $node->expr); + return $this->pPrefixOp(AssignOp\Coalesce::class, $this->p($node->var) . ' ??= ', $node->expr, $precedence, $lhsPrecedence); } // Binary expressions - protected function pExpr_BinaryOp_Plus(BinaryOp\Plus $node) + protected function pExpr_BinaryOp_Plus(BinaryOp\Plus $node, int $precedence, int $lhsPrecedence): string { - return $this->pInfixOp(BinaryOp\Plus::class, $node->left, ' + ', $node->right); + return $this->pInfixOp(BinaryOp\Plus::class, $node->left, ' + ', $node->right, $precedence, $lhsPrecedence); } - protected function pExpr_BinaryOp_Minus(BinaryOp\Minus $node) + protected function pExpr_BinaryOp_Minus(BinaryOp\Minus $node, int $precedence, int $lhsPrecedence): string { - return $this->pInfixOp(BinaryOp\Minus::class, $node->left, ' - ', $node->right); + return $this->pInfixOp(BinaryOp\Minus::class, $node->left, ' - ', $node->right, $precedence, $lhsPrecedence); } - protected function pExpr_BinaryOp_Mul(BinaryOp\Mul $node) + protected function pExpr_BinaryOp_Mul(BinaryOp\Mul $node, int $precedence, int $lhsPrecedence): string { - return $this->pInfixOp(BinaryOp\Mul::class, $node->left, ' * ', $node->right); + return $this->pInfixOp(BinaryOp\Mul::class, $node->left, ' * ', $node->right, $precedence, $lhsPrecedence); } - protected function pExpr_BinaryOp_Div(BinaryOp\Div $node) + protected function pExpr_BinaryOp_Div(BinaryOp\Div $node, int $precedence, int $lhsPrecedence): string { - return $this->pInfixOp(BinaryOp\Div::class, $node->left, ' / ', $node->right); + return $this->pInfixOp(BinaryOp\Div::class, $node->left, ' / ', $node->right, $precedence, $lhsPrecedence); } - protected function pExpr_BinaryOp_Concat(BinaryOp\Concat $node) + protected function pExpr_BinaryOp_Concat(BinaryOp\Concat $node, int $precedence, int $lhsPrecedence): string { - return $this->pInfixOp(BinaryOp\Concat::class, $node->left, ' . ', $node->right); + return $this->pInfixOp(BinaryOp\Concat::class, $node->left, ' . ', $node->right, $precedence, $lhsPrecedence); } - protected function pExpr_BinaryOp_Mod(BinaryOp\Mod $node) + protected function pExpr_BinaryOp_Mod(BinaryOp\Mod $node, int $precedence, int $lhsPrecedence): string { - return $this->pInfixOp(BinaryOp\Mod::class, $node->left, ' % ', $node->right); + return $this->pInfixOp(BinaryOp\Mod::class, $node->left, ' % ', $node->right, $precedence, $lhsPrecedence); } - protected function pExpr_BinaryOp_BooleanAnd(BinaryOp\BooleanAnd $node) + protected function pExpr_BinaryOp_BooleanAnd(BinaryOp\BooleanAnd $node, int $precedence, int $lhsPrecedence): string { - return $this->pInfixOp(BinaryOp\BooleanAnd::class, $node->left, ' && ', $node->right); + return $this->pInfixOp(BinaryOp\BooleanAnd::class, $node->left, ' && ', $node->right, $precedence, $lhsPrecedence); } - protected function pExpr_BinaryOp_BooleanOr(BinaryOp\BooleanOr $node) + protected function pExpr_BinaryOp_BooleanOr(BinaryOp\BooleanOr $node, int $precedence, int $lhsPrecedence): string { - return $this->pInfixOp(BinaryOp\BooleanOr::class, $node->left, ' || ', $node->right); + return $this->pInfixOp(BinaryOp\BooleanOr::class, $node->left, ' || ', $node->right, $precedence, $lhsPrecedence); } - protected function pExpr_BinaryOp_BitwiseAnd(BinaryOp\BitwiseAnd $node) + protected function pExpr_BinaryOp_BitwiseAnd(BinaryOp\BitwiseAnd $node, int $precedence, int $lhsPrecedence): string { - return $this->pInfixOp(BinaryOp\BitwiseAnd::class, $node->left, ' & ', $node->right); + return $this->pInfixOp(BinaryOp\BitwiseAnd::class, $node->left, ' & ', $node->right, $precedence, $lhsPrecedence); } - protected function pExpr_BinaryOp_BitwiseOr(BinaryOp\BitwiseOr $node) + protected function pExpr_BinaryOp_BitwiseOr(BinaryOp\BitwiseOr $node, int $precedence, int $lhsPrecedence): string { - return $this->pInfixOp(BinaryOp\BitwiseOr::class, $node->left, ' | ', $node->right); + return $this->pInfixOp(BinaryOp\BitwiseOr::class, $node->left, ' | ', $node->right, $precedence, $lhsPrecedence); } - protected function pExpr_BinaryOp_BitwiseXor(BinaryOp\BitwiseXor $node) + protected function pExpr_BinaryOp_BitwiseXor(BinaryOp\BitwiseXor $node, int $precedence, int $lhsPrecedence): string { - return $this->pInfixOp(BinaryOp\BitwiseXor::class, $node->left, ' ^ ', $node->right); + return $this->pInfixOp(BinaryOp\BitwiseXor::class, $node->left, ' ^ ', $node->right, $precedence, $lhsPrecedence); } - protected function pExpr_BinaryOp_ShiftLeft(BinaryOp\ShiftLeft $node) + protected function pExpr_BinaryOp_ShiftLeft(BinaryOp\ShiftLeft $node, int $precedence, int $lhsPrecedence): string { - return $this->pInfixOp(BinaryOp\ShiftLeft::class, $node->left, ' << ', $node->right); + return $this->pInfixOp(BinaryOp\ShiftLeft::class, $node->left, ' << ', $node->right, $precedence, $lhsPrecedence); } - protected function pExpr_BinaryOp_ShiftRight(BinaryOp\ShiftRight $node) + protected function pExpr_BinaryOp_ShiftRight(BinaryOp\ShiftRight $node, int $precedence, int $lhsPrecedence): string { - return $this->pInfixOp(BinaryOp\ShiftRight::class, $node->left, ' >> ', $node->right); + return $this->pInfixOp(BinaryOp\ShiftRight::class, $node->left, ' >> ', $node->right, $precedence, $lhsPrecedence); } - protected function pExpr_BinaryOp_Pow(BinaryOp\Pow $node) + protected function pExpr_BinaryOp_Pow(BinaryOp\Pow $node, int $precedence, int $lhsPrecedence): string { - return $this->pInfixOp(BinaryOp\Pow::class, $node->left, ' ** ', $node->right); + return $this->pInfixOp(BinaryOp\Pow::class, $node->left, ' ** ', $node->right, $precedence, $lhsPrecedence); } - protected function pExpr_BinaryOp_LogicalAnd(BinaryOp\LogicalAnd $node) + protected function pExpr_BinaryOp_LogicalAnd(BinaryOp\LogicalAnd $node, int $precedence, int $lhsPrecedence): string { - return $this->pInfixOp(BinaryOp\LogicalAnd::class, $node->left, ' and ', $node->right); + return $this->pInfixOp(BinaryOp\LogicalAnd::class, $node->left, ' and ', $node->right, $precedence, $lhsPrecedence); } - protected function pExpr_BinaryOp_LogicalOr(BinaryOp\LogicalOr $node) + protected function pExpr_BinaryOp_LogicalOr(BinaryOp\LogicalOr $node, int $precedence, int $lhsPrecedence): string { - return $this->pInfixOp(BinaryOp\LogicalOr::class, $node->left, ' or ', $node->right); + return $this->pInfixOp(BinaryOp\LogicalOr::class, $node->left, ' or ', $node->right, $precedence, $lhsPrecedence); } - protected function pExpr_BinaryOp_LogicalXor(BinaryOp\LogicalXor $node) + protected function pExpr_BinaryOp_LogicalXor(BinaryOp\LogicalXor $node, int $precedence, int $lhsPrecedence): string { - return $this->pInfixOp(BinaryOp\LogicalXor::class, $node->left, ' xor ', $node->right); + return $this->pInfixOp(BinaryOp\LogicalXor::class, $node->left, ' xor ', $node->right, $precedence, $lhsPrecedence); } - protected function pExpr_BinaryOp_Equal(BinaryOp\Equal $node) + protected function pExpr_BinaryOp_Equal(BinaryOp\Equal $node, int $precedence, int $lhsPrecedence): string { - return $this->pInfixOp(BinaryOp\Equal::class, $node->left, ' == ', $node->right); + return $this->pInfixOp(BinaryOp\Equal::class, $node->left, ' == ', $node->right, $precedence, $lhsPrecedence); } - protected function pExpr_BinaryOp_NotEqual(BinaryOp\NotEqual $node) + protected function pExpr_BinaryOp_NotEqual(BinaryOp\NotEqual $node, int $precedence, int $lhsPrecedence): string { - return $this->pInfixOp(BinaryOp\NotEqual::class, $node->left, ' != ', $node->right); + return $this->pInfixOp(BinaryOp\NotEqual::class, $node->left, ' != ', $node->right, $precedence, $lhsPrecedence); } - protected function pExpr_BinaryOp_Identical(BinaryOp\Identical $node) + protected function pExpr_BinaryOp_Identical(BinaryOp\Identical $node, int $precedence, int $lhsPrecedence): string { - return $this->pInfixOp(BinaryOp\Identical::class, $node->left, ' === ', $node->right); + return $this->pInfixOp(BinaryOp\Identical::class, $node->left, ' === ', $node->right, $precedence, $lhsPrecedence); } - protected function pExpr_BinaryOp_NotIdentical(BinaryOp\NotIdentical $node) + protected function pExpr_BinaryOp_NotIdentical(BinaryOp\NotIdentical $node, int $precedence, int $lhsPrecedence): string { - return $this->pInfixOp(BinaryOp\NotIdentical::class, $node->left, ' !== ', $node->right); + return $this->pInfixOp(BinaryOp\NotIdentical::class, $node->left, ' !== ', $node->right, $precedence, $lhsPrecedence); } - protected function pExpr_BinaryOp_Spaceship(BinaryOp\Spaceship $node) + protected function pExpr_BinaryOp_Spaceship(BinaryOp\Spaceship $node, int $precedence, int $lhsPrecedence): string { - return $this->pInfixOp(BinaryOp\Spaceship::class, $node->left, ' <=> ', $node->right); + return $this->pInfixOp(BinaryOp\Spaceship::class, $node->left, ' <=> ', $node->right, $precedence, $lhsPrecedence); } - protected function pExpr_BinaryOp_Greater(BinaryOp\Greater $node) + protected function pExpr_BinaryOp_Greater(BinaryOp\Greater $node, int $precedence, int $lhsPrecedence): string { - return $this->pInfixOp(BinaryOp\Greater::class, $node->left, ' > ', $node->right); + return $this->pInfixOp(BinaryOp\Greater::class, $node->left, ' > ', $node->right, $precedence, $lhsPrecedence); } - protected function pExpr_BinaryOp_GreaterOrEqual(BinaryOp\GreaterOrEqual $node) + protected function pExpr_BinaryOp_GreaterOrEqual(BinaryOp\GreaterOrEqual $node, int $precedence, int $lhsPrecedence): string { - return $this->pInfixOp(BinaryOp\GreaterOrEqual::class, $node->left, ' >= ', $node->right); + return $this->pInfixOp(BinaryOp\GreaterOrEqual::class, $node->left, ' >= ', $node->right, $precedence, $lhsPrecedence); } - protected function pExpr_BinaryOp_Smaller(BinaryOp\Smaller $node) + protected function pExpr_BinaryOp_Smaller(BinaryOp\Smaller $node, int $precedence, int $lhsPrecedence): string { - return $this->pInfixOp(BinaryOp\Smaller::class, $node->left, ' < ', $node->right); + return $this->pInfixOp(BinaryOp\Smaller::class, $node->left, ' < ', $node->right, $precedence, $lhsPrecedence); } - protected function pExpr_BinaryOp_SmallerOrEqual(BinaryOp\SmallerOrEqual $node) + protected function pExpr_BinaryOp_SmallerOrEqual(BinaryOp\SmallerOrEqual $node, int $precedence, int $lhsPrecedence): string { - return $this->pInfixOp(BinaryOp\SmallerOrEqual::class, $node->left, ' <= ', $node->right); + return $this->pInfixOp(BinaryOp\SmallerOrEqual::class, $node->left, ' <= ', $node->right, $precedence, $lhsPrecedence); } - protected function pExpr_BinaryOp_Coalesce(BinaryOp\Coalesce $node) + protected function pExpr_BinaryOp_Coalesce(BinaryOp\Coalesce $node, int $precedence, int $lhsPrecedence): string { - return $this->pInfixOp(BinaryOp\Coalesce::class, $node->left, ' ?? ', $node->right); + return $this->pInfixOp(BinaryOp\Coalesce::class, $node->left, ' ?? ', $node->right, $precedence, $lhsPrecedence); } - protected function pExpr_Instanceof(Expr\Instanceof_ $node) + protected function pExpr_Instanceof(Expr\Instanceof_ $node, int $precedence, int $lhsPrecedence): string { - list($precedence, $associativity) = $this->precedenceMap[Expr\Instanceof_::class]; - return $this->pPrec($node->expr, $precedence, $associativity, -1) . ' instanceof ' . $this->pNewVariable($node->class); + return $this->pPostfixOp(Expr\Instanceof_::class, $node->expr, ' instanceof ' . $this->pNewOperand($node->class), $precedence, $lhsPrecedence); } // Unary expressions - protected function pExpr_BooleanNot(Expr\BooleanNot $node) + protected function pExpr_BooleanNot(Expr\BooleanNot $node, int $precedence, int $lhsPrecedence): string { - return $this->pPrefixOp(Expr\BooleanNot::class, '!', $node->expr); + return $this->pPrefixOp(Expr\BooleanNot::class, '!', $node->expr, $precedence, $lhsPrecedence); } - protected function pExpr_BitwiseNot(Expr\BitwiseNot $node) + protected function pExpr_BitwiseNot(Expr\BitwiseNot $node, int $precedence, int $lhsPrecedence): string { - return $this->pPrefixOp(Expr\BitwiseNot::class, '~', $node->expr); + return $this->pPrefixOp(Expr\BitwiseNot::class, '~', $node->expr, $precedence, $lhsPrecedence); } - protected function pExpr_UnaryMinus(Expr\UnaryMinus $node) + protected function pExpr_UnaryMinus(Expr\UnaryMinus $node, int $precedence, int $lhsPrecedence): string { - if ($node->expr instanceof Expr\UnaryMinus || $node->expr instanceof Expr\PreDec) { - // Enforce -(-$expr) instead of --$expr - return '-(' . $this->p($node->expr) . ')'; - } - return $this->pPrefixOp(Expr\UnaryMinus::class, '-', $node->expr); + return $this->pPrefixOp(Expr\UnaryMinus::class, '-', $node->expr, $precedence, $lhsPrecedence); } - protected function pExpr_UnaryPlus(Expr\UnaryPlus $node) + protected function pExpr_UnaryPlus(Expr\UnaryPlus $node, int $precedence, int $lhsPrecedence): string { - if ($node->expr instanceof Expr\UnaryPlus || $node->expr instanceof Expr\PreInc) { - // Enforce +(+$expr) instead of ++$expr - return '+(' . $this->p($node->expr) . ')'; - } - return $this->pPrefixOp(Expr\UnaryPlus::class, '+', $node->expr); + return $this->pPrefixOp(Expr\UnaryPlus::class, '+', $node->expr, $precedence, $lhsPrecedence); } - protected function pExpr_PreInc(Expr\PreInc $node) + protected function pExpr_PreInc(Expr\PreInc $node): string { - return $this->pPrefixOp(Expr\PreInc::class, '++', $node->var); + return '++' . $this->p($node->var); } - protected function pExpr_PreDec(Expr\PreDec $node) + protected function pExpr_PreDec(Expr\PreDec $node): string { - return $this->pPrefixOp(Expr\PreDec::class, '--', $node->var); + return '--' . $this->p($node->var); } - protected function pExpr_PostInc(Expr\PostInc $node) + protected function pExpr_PostInc(Expr\PostInc $node): string { - return $this->pPostfixOp(Expr\PostInc::class, $node->var, '++'); + return $this->p($node->var) . '++'; } - protected function pExpr_PostDec(Expr\PostDec $node) + protected function pExpr_PostDec(Expr\PostDec $node): string { - return $this->pPostfixOp(Expr\PostDec::class, $node->var, '--'); + return $this->p($node->var) . '--'; } - protected function pExpr_ErrorSuppress(Expr\ErrorSuppress $node) + protected function pExpr_ErrorSuppress(Expr\ErrorSuppress $node, int $precedence, int $lhsPrecedence): string { - return $this->pPrefixOp(Expr\ErrorSuppress::class, '@', $node->expr); + return $this->pPrefixOp(Expr\ErrorSuppress::class, '@', $node->expr, $precedence, $lhsPrecedence); } - protected function pExpr_YieldFrom(Expr\YieldFrom $node) + protected function pExpr_YieldFrom(Expr\YieldFrom $node, int $precedence, int $lhsPrecedence): string { - return $this->pPrefixOp(Expr\YieldFrom::class, 'yield from ', $node->expr); + return $this->pPrefixOp(Expr\YieldFrom::class, 'yield from ', $node->expr, $precedence, $lhsPrecedence); } - protected function pExpr_Print(Expr\Print_ $node) + protected function pExpr_Print(Expr\Print_ $node, int $precedence, int $lhsPrecedence): string { - return $this->pPrefixOp(Expr\Print_::class, 'print ', $node->expr); + return $this->pPrefixOp(Expr\Print_::class, 'print ', $node->expr, $precedence, $lhsPrecedence); } // Casts - protected function pExpr_Cast_Int(Cast\Int_ $node) + protected function pExpr_Cast_Int(Cast\Int_ $node, int $precedence, int $lhsPrecedence): string { - return $this->pPrefixOp(Cast\Int_::class, '(int) ', $node->expr); + return $this->pPrefixOp(Cast\Int_::class, '(int) ', $node->expr, $precedence, $lhsPrecedence); } - protected function pExpr_Cast_Double(Cast\Double $node) + protected function pExpr_Cast_Double(Cast\Double $node, int $precedence, int $lhsPrecedence): string { $kind = $node->getAttribute('kind', Cast\Double::KIND_DOUBLE); if ($kind === Cast\Double::KIND_DOUBLE) { $cast = '(double)'; } elseif ($kind === Cast\Double::KIND_FLOAT) { $cast = '(float)'; - } elseif ($kind === Cast\Double::KIND_REAL) { + } else { + assert($kind === Cast\Double::KIND_REAL); $cast = '(real)'; } - return $this->pPrefixOp(Cast\Double::class, $cast . ' ', $node->expr); + return $this->pPrefixOp(Cast\Double::class, $cast . ' ', $node->expr, $precedence, $lhsPrecedence); } - protected function pExpr_Cast_String(Cast\String_ $node) + protected function pExpr_Cast_String(Cast\String_ $node, int $precedence, int $lhsPrecedence): string { - return $this->pPrefixOp(Cast\String_::class, '(string) ', $node->expr); + return $this->pPrefixOp(Cast\String_::class, '(string) ', $node->expr, $precedence, $lhsPrecedence); } - protected function pExpr_Cast_Array(Cast\Array_ $node) + protected function pExpr_Cast_Array(Cast\Array_ $node, int $precedence, int $lhsPrecedence): string { - return $this->pPrefixOp(Cast\Array_::class, '(array) ', $node->expr); + return $this->pPrefixOp(Cast\Array_::class, '(array) ', $node->expr, $precedence, $lhsPrecedence); } - protected function pExpr_Cast_Object(Cast\Object_ $node) + protected function pExpr_Cast_Object(Cast\Object_ $node, int $precedence, int $lhsPrecedence): string { - return $this->pPrefixOp(Cast\Object_::class, '(object) ', $node->expr); + return $this->pPrefixOp(Cast\Object_::class, '(object) ', $node->expr, $precedence, $lhsPrecedence); } - protected function pExpr_Cast_Bool(Cast\Bool_ $node) + protected function pExpr_Cast_Bool(Cast\Bool_ $node, int $precedence, int $lhsPrecedence): string { - return $this->pPrefixOp(Cast\Bool_::class, '(bool) ', $node->expr); + return $this->pPrefixOp(Cast\Bool_::class, '(bool) ', $node->expr, $precedence, $lhsPrecedence); } - protected function pExpr_Cast_Unset(Cast\Unset_ $node) + protected function pExpr_Cast_Unset(Cast\Unset_ $node, int $precedence, int $lhsPrecedence): string { - return $this->pPrefixOp(Cast\Unset_::class, '(unset) ', $node->expr); + return $this->pPrefixOp(Cast\Unset_::class, '(unset) ', $node->expr, $precedence, $lhsPrecedence); } // Function calls and similar constructs - protected function pExpr_FuncCall(Expr\FuncCall $node) + protected function pExpr_FuncCall(Expr\FuncCall $node): string { return $this->pCallLhs($node->name) . '(' . $this->pMaybeMultiline($node->args) . ')'; } - protected function pExpr_MethodCall(Expr\MethodCall $node) + protected function pExpr_MethodCall(Expr\MethodCall $node): string { return $this->pDereferenceLhs($node->var) . '->' . $this->pObjectProperty($node->name) . '(' . $this->pMaybeMultiline($node->args) . ')'; } - protected function pExpr_NullsafeMethodCall(Expr\NullsafeMethodCall $node) + protected function pExpr_NullsafeMethodCall(Expr\NullsafeMethodCall $node): string { return $this->pDereferenceLhs($node->var) . '?->' . $this->pObjectProperty($node->name) . '(' . $this->pMaybeMultiline($node->args) . ')'; } - protected function pExpr_StaticCall(Expr\StaticCall $node) + protected function pExpr_StaticCall(Expr\StaticCall $node): string { - return $this->pStaticDereferenceLhs($node->class) . '::' . ($node->name instanceof Expr ? $node->name instanceof Expr\Variable ? $this->p($node->name) : '{' . $this->p($node->name) . '}' : $node->name) . '(' . $this->pMaybeMultiline($node->args) . ')'; + return $this->pStaticDereferenceLhs($node->class) . '::' . (($node->name instanceof Expr) ? ($node->name instanceof Expr\Variable) ? $this->p($node->name) : ('{' . $this->p($node->name) . '}') : $node->name) . '(' . $this->pMaybeMultiline($node->args) . ')'; } - protected function pExpr_Empty(Expr\Empty_ $node) + protected function pExpr_Empty(Expr\Empty_ $node): string { return 'empty(' . $this->p($node->expr) . ')'; } - protected function pExpr_Isset(Expr\Isset_ $node) + protected function pExpr_Isset(Expr\Isset_ $node): string { return 'isset(' . $this->pCommaSeparated($node->vars) . ')'; } - protected function pExpr_Eval(Expr\Eval_ $node) + protected function pExpr_Eval(Expr\Eval_ $node): string { return 'eval(' . $this->p($node->expr) . ')'; } - protected function pExpr_Include(Expr\Include_ $node) + protected function pExpr_Include(Expr\Include_ $node, int $precedence, int $lhsPrecedence): string { static $map = [Expr\Include_::TYPE_INCLUDE => 'include', Expr\Include_::TYPE_INCLUDE_ONCE => 'include_once', Expr\Include_::TYPE_REQUIRE => 'require', Expr\Include_::TYPE_REQUIRE_ONCE => 'require_once']; - return $map[$node->type] . ' ' . $this->p($node->expr); + return $this->pPrefixOp(Expr\Include_::class, $map[$node->type] . ' ', $node->expr, $precedence, $lhsPrecedence); } - protected function pExpr_List(Expr\List_ $node) + protected function pExpr_List(Expr\List_ $node): string { - return 'list(' . $this->pCommaSeparated($node->items) . ')'; + $syntax = $node->getAttribute('kind', $this->phpVersion->supportsShortArrayDestructuring() ? Expr\List_::KIND_ARRAY : Expr\List_::KIND_LIST); + if ($syntax === Expr\List_::KIND_ARRAY) { + return '[' . $this->pMaybeMultiline($node->items, \true) . ']'; + } else { + return 'list(' . $this->pMaybeMultiline($node->items, \true) . ')'; + } } // Other - protected function pExpr_Error(Expr\Error $node) + protected function pExpr_Error(Expr\Error $node): string { throw new \LogicException('Cannot pretty-print AST with Error nodes'); } - protected function pExpr_Variable(Expr\Variable $node) + protected function pExpr_Variable(Expr\Variable $node): string { if ($node->name instanceof Expr) { return '${' . $this->p($node->name) . '}'; @@ -22863,321 +22087,353 @@ class Standard extends PrettyPrinterAbstract return '$' . $node->name; } } - protected function pExpr_Array(Expr\Array_ $node) + protected function pExpr_Array(Expr\Array_ $node): string { - $syntax = $node->getAttribute('kind', $this->options['shortArraySyntax'] ? Expr\Array_::KIND_SHORT : Expr\Array_::KIND_LONG); + $syntax = $node->getAttribute('kind', $this->shortArraySyntax ? Expr\Array_::KIND_SHORT : Expr\Array_::KIND_LONG); if ($syntax === Expr\Array_::KIND_SHORT) { return '[' . $this->pMaybeMultiline($node->items, \true) . ']'; } else { return 'array(' . $this->pMaybeMultiline($node->items, \true) . ')'; } } - protected function pExpr_ArrayItem(Expr\ArrayItem $node) + protected function pKey(?Node $node): string + { + if ($node === null) { + return ''; + } + // => is not really an operator and does not typically participate in precedence resolution. + // However, there is an exception if yield expressions with keys are involved: + // [yield $a => $b] is interpreted as [(yield $a => $b)], so we need to ensure that + // [(yield $a) => $b] is printed with parentheses. We approximate this by lowering the LHS + // precedence to that of yield (which will also print unnecessary parentheses for rare low + // precedence unary operators like include). + $yieldPrecedence = $this->precedenceMap[Expr\Yield_::class][0]; + return $this->p($node, self::MAX_PRECEDENCE, $yieldPrecedence) . ' => '; + } + protected function pArrayItem(Node\ArrayItem $node): string { - return (null !== $node->key ? $this->p($node->key) . ' => ' : '') . ($node->byRef ? '&' : '') . ($node->unpack ? '...' : '') . $this->p($node->value); + return $this->pKey($node->key) . ($node->byRef ? '&' : '') . ($node->unpack ? '...' : '') . $this->p($node->value); } - protected function pExpr_ArrayDimFetch(Expr\ArrayDimFetch $node) + protected function pExpr_ArrayDimFetch(Expr\ArrayDimFetch $node): string { - return $this->pDereferenceLhs($node->var) . '[' . (null !== $node->dim ? $this->p($node->dim) : '') . ']'; + return $this->pDereferenceLhs($node->var) . '[' . ((null !== $node->dim) ? $this->p($node->dim) : '') . ']'; } - protected function pExpr_ConstFetch(Expr\ConstFetch $node) + protected function pExpr_ConstFetch(Expr\ConstFetch $node): string { return $this->p($node->name); } - protected function pExpr_ClassConstFetch(Expr\ClassConstFetch $node) + protected function pExpr_ClassConstFetch(Expr\ClassConstFetch $node): string { return $this->pStaticDereferenceLhs($node->class) . '::' . $this->pObjectProperty($node->name); } - protected function pExpr_PropertyFetch(Expr\PropertyFetch $node) + protected function pExpr_PropertyFetch(Expr\PropertyFetch $node): string { return $this->pDereferenceLhs($node->var) . '->' . $this->pObjectProperty($node->name); } - protected function pExpr_NullsafePropertyFetch(Expr\NullsafePropertyFetch $node) + protected function pExpr_NullsafePropertyFetch(Expr\NullsafePropertyFetch $node): string { return $this->pDereferenceLhs($node->var) . '?->' . $this->pObjectProperty($node->name); } - protected function pExpr_StaticPropertyFetch(Expr\StaticPropertyFetch $node) + protected function pExpr_StaticPropertyFetch(Expr\StaticPropertyFetch $node): string { return $this->pStaticDereferenceLhs($node->class) . '::$' . $this->pObjectProperty($node->name); } - protected function pExpr_ShellExec(Expr\ShellExec $node) + protected function pExpr_ShellExec(Expr\ShellExec $node): string { return '`' . $this->pEncapsList($node->parts, '`') . '`'; } - protected function pExpr_Closure(Expr\Closure $node) + protected function pExpr_Closure(Expr\Closure $node): string { - return $this->pAttrGroups($node->attrGroups, \true) . ($node->static ? 'static ' : '') . 'function ' . ($node->byRef ? '&' : '') . '(' . $this->pCommaSeparated($node->params) . ')' . (!empty($node->uses) ? ' use(' . $this->pCommaSeparated($node->uses) . ')' : '') . (null !== $node->returnType ? ' : ' . $this->p($node->returnType) : '') . ' {' . $this->pStmts($node->stmts) . $this->nl . '}'; + return $this->pAttrGroups($node->attrGroups, \true) . $this->pStatic($node->static) . 'function ' . ($node->byRef ? '&' : '') . '(' . $this->pMaybeMultiline($node->params, $this->phpVersion->supportsTrailingCommaInParamList()) . ')' . ((!empty($node->uses)) ? ' use (' . $this->pCommaSeparated($node->uses) . ')' : '') . ((null !== $node->returnType) ? ': ' . $this->p($node->returnType) : '') . ' {' . $this->pStmts($node->stmts) . $this->nl . '}'; } - protected function pExpr_Match(Expr\Match_ $node) + protected function pExpr_Match(Expr\Match_ $node): string { return 'match (' . $this->p($node->cond) . ') {' . $this->pCommaSeparatedMultiline($node->arms, \true) . $this->nl . '}'; } - protected function pMatchArm(Node\MatchArm $node) + protected function pMatchArm(Node\MatchArm $node): string { - return ($node->conds ? $this->pCommaSeparated($node->conds) : 'default') . ' => ' . $this->p($node->body); + $result = ''; + if ($node->conds) { + for ($i = 0, $c = \count($node->conds); $i + 1 < $c; $i++) { + $result .= $this->p($node->conds[$i]) . ', '; + } + $result .= $this->pKey($node->conds[$i]); + } else { + $result = 'default => '; + } + return $result . $this->p($node->body); } - protected function pExpr_ArrowFunction(Expr\ArrowFunction $node) + protected function pExpr_ArrowFunction(Expr\ArrowFunction $node, int $precedence, int $lhsPrecedence): string { - return $this->pAttrGroups($node->attrGroups, \true) . ($node->static ? 'static ' : '') . 'fn' . ($node->byRef ? '&' : '') . '(' . $this->pCommaSeparated($node->params) . ')' . (null !== $node->returnType ? ': ' . $this->p($node->returnType) : '') . ' => ' . $this->p($node->expr); + return $this->pPrefixOp(Expr\ArrowFunction::class, $this->pAttrGroups($node->attrGroups, \true) . $this->pStatic($node->static) . 'fn' . ($node->byRef ? '&' : '') . '(' . $this->pMaybeMultiline($node->params, $this->phpVersion->supportsTrailingCommaInParamList()) . ')' . ((null !== $node->returnType) ? ': ' . $this->p($node->returnType) : '') . ' => ', $node->expr, $precedence, $lhsPrecedence); } - protected function pExpr_ClosureUse(Expr\ClosureUse $node) + protected function pClosureUse(Node\ClosureUse $node): string { return ($node->byRef ? '&' : '') . $this->p($node->var); } - protected function pExpr_New(Expr\New_ $node) + protected function pExpr_New(Expr\New_ $node): string { if ($node->class instanceof Stmt\Class_) { $args = $node->args ? '(' . $this->pMaybeMultiline($node->args) . ')' : ''; return 'new ' . $this->pClassCommon($node->class, $args); } - return 'new ' . $this->pNewVariable($node->class) . '(' . $this->pMaybeMultiline($node->args) . ')'; + return 'new ' . $this->pNewOperand($node->class) . '(' . $this->pMaybeMultiline($node->args) . ')'; } - protected function pExpr_Clone(Expr\Clone_ $node) + protected function pExpr_Clone(Expr\Clone_ $node, int $precedence, int $lhsPrecedence): string { - return 'clone ' . $this->p($node->expr); + return $this->pPrefixOp(Expr\Clone_::class, 'clone ', $node->expr, $precedence, $lhsPrecedence); } - protected function pExpr_Ternary(Expr\Ternary $node) + protected function pExpr_Ternary(Expr\Ternary $node, int $precedence, int $lhsPrecedence): string { // a bit of cheating: we treat the ternary as a binary op where the ?...: part is the operator. // this is okay because the part between ? and : never needs parentheses. - return $this->pInfixOp(Expr\Ternary::class, $node->cond, ' ?' . (null !== $node->if ? ' ' . $this->p($node->if) . ' ' : '') . ': ', $node->else); + return $this->pInfixOp(Expr\Ternary::class, $node->cond, ' ?' . ((null !== $node->if) ? ' ' . $this->p($node->if) . ' ' : '') . ': ', $node->else, $precedence, $lhsPrecedence); } - protected function pExpr_Exit(Expr\Exit_ $node) + protected function pExpr_Exit(Expr\Exit_ $node): string { $kind = $node->getAttribute('kind', Expr\Exit_::KIND_DIE); - return ($kind === Expr\Exit_::KIND_EXIT ? 'exit' : 'die') . (null !== $node->expr ? '(' . $this->p($node->expr) . ')' : ''); + return (($kind === Expr\Exit_::KIND_EXIT) ? 'exit' : 'die') . ((null !== $node->expr) ? '(' . $this->p($node->expr) . ')' : ''); } - protected function pExpr_Throw(Expr\Throw_ $node) + protected function pExpr_Throw(Expr\Throw_ $node, int $precedence, int $lhsPrecedence): string { - return 'throw ' . $this->p($node->expr); + return $this->pPrefixOp(Expr\Throw_::class, 'throw ', $node->expr, $precedence, $lhsPrecedence); } - protected function pExpr_Yield(Expr\Yield_ $node) + protected function pExpr_Yield(Expr\Yield_ $node, int $precedence, int $lhsPrecedence): string { if ($node->value === null) { - return 'yield'; + $opPrecedence = $this->precedenceMap[Expr\Yield_::class][0]; + return ($opPrecedence >= $lhsPrecedence) ? '(yield)' : 'yield'; } else { - // this is a bit ugly, but currently there is no way to detect whether the parentheses are necessary - return '(yield ' . ($node->key !== null ? $this->p($node->key) . ' => ' : '') . $this->p($node->value) . ')'; + if (!$this->phpVersion->supportsYieldWithoutParentheses()) { + return '(yield ' . $this->pKey($node->key) . $this->p($node->value) . ')'; + } + return $this->pPrefixOp(Expr\Yield_::class, 'yield ' . $this->pKey($node->key), $node->value, $precedence, $lhsPrecedence); } } // Declarations - protected function pStmt_Namespace(Stmt\Namespace_ $node) + protected function pStmt_Namespace(Stmt\Namespace_ $node): string { if ($this->canUseSemicolonNamespaces) { return 'namespace ' . $this->p($node->name) . ';' . $this->nl . $this->pStmts($node->stmts, \false); } else { - return 'namespace' . (null !== $node->name ? ' ' . $this->p($node->name) : '') . ' {' . $this->pStmts($node->stmts) . $this->nl . '}'; + return 'namespace' . ((null !== $node->name) ? ' ' . $this->p($node->name) : '') . ' {' . $this->pStmts($node->stmts) . $this->nl . '}'; } } - protected function pStmt_Use(Stmt\Use_ $node) + protected function pStmt_Use(Stmt\Use_ $node): string { return 'use ' . $this->pUseType($node->type) . $this->pCommaSeparated($node->uses) . ';'; } - protected function pStmt_GroupUse(Stmt\GroupUse $node) + protected function pStmt_GroupUse(Stmt\GroupUse $node): string { - return 'use ' . $this->pUseType($node->type) . $this->pName($node->prefix) . '\\{' . $this->pCommaSeparated($node->uses) . '};'; + return 'use ' . $this->pUseType($node->type) . $this->pName($node->prefix) . '\{' . $this->pCommaSeparated($node->uses) . '};'; } - protected function pStmt_UseUse(Stmt\UseUse $node) + protected function pUseItem(Node\UseItem $node): string { - return $this->pUseType($node->type) . $this->p($node->name) . (null !== $node->alias ? ' as ' . $node->alias : ''); + return $this->pUseType($node->type) . $this->p($node->name) . ((null !== $node->alias) ? ' as ' . $node->alias : ''); } - protected function pUseType($type) + protected function pUseType(int $type): string { - return $type === Stmt\Use_::TYPE_FUNCTION ? 'function ' : ($type === Stmt\Use_::TYPE_CONSTANT ? 'const ' : ''); + return ($type === Stmt\Use_::TYPE_FUNCTION) ? 'function ' : (($type === Stmt\Use_::TYPE_CONSTANT) ? 'const ' : ''); } - protected function pStmt_Interface(Stmt\Interface_ $node) + protected function pStmt_Interface(Stmt\Interface_ $node): string { - return $this->pAttrGroups($node->attrGroups) . 'interface ' . $node->name . (!empty($node->extends) ? ' extends ' . $this->pCommaSeparated($node->extends) : '') . $this->nl . '{' . $this->pStmts($node->stmts) . $this->nl . '}'; + return $this->pAttrGroups($node->attrGroups) . 'interface ' . $node->name . ((!empty($node->extends)) ? ' extends ' . $this->pCommaSeparated($node->extends) : '') . $this->nl . '{' . $this->pStmts($node->stmts) . $this->nl . '}'; } - protected function pStmt_Enum(Stmt\Enum_ $node) + protected function pStmt_Enum(Stmt\Enum_ $node): string { - return $this->pAttrGroups($node->attrGroups) . 'enum ' . $node->name . ($node->scalarType ? " : {$node->scalarType}" : '') . (!empty($node->implements) ? ' implements ' . $this->pCommaSeparated($node->implements) : '') . $this->nl . '{' . $this->pStmts($node->stmts) . $this->nl . '}'; + return $this->pAttrGroups($node->attrGroups) . 'enum ' . $node->name . ($node->scalarType ? ' : ' . $this->p($node->scalarType) : '') . ((!empty($node->implements)) ? ' implements ' . $this->pCommaSeparated($node->implements) : '') . $this->nl . '{' . $this->pStmts($node->stmts) . $this->nl . '}'; } - protected function pStmt_Class(Stmt\Class_ $node) + protected function pStmt_Class(Stmt\Class_ $node): string { return $this->pClassCommon($node, ' ' . $node->name); } - protected function pStmt_Trait(Stmt\Trait_ $node) + protected function pStmt_Trait(Stmt\Trait_ $node): string { return $this->pAttrGroups($node->attrGroups) . 'trait ' . $node->name . $this->nl . '{' . $this->pStmts($node->stmts) . $this->nl . '}'; } - protected function pStmt_EnumCase(Stmt\EnumCase $node) + protected function pStmt_EnumCase(Stmt\EnumCase $node): string { return $this->pAttrGroups($node->attrGroups) . 'case ' . $node->name . ($node->expr ? ' = ' . $this->p($node->expr) : '') . ';'; } - protected function pStmt_TraitUse(Stmt\TraitUse $node) + protected function pStmt_TraitUse(Stmt\TraitUse $node): string { - return 'use ' . $this->pCommaSeparated($node->traits) . (empty($node->adaptations) ? ';' : ' {' . $this->pStmts($node->adaptations) . $this->nl . '}'); + return 'use ' . $this->pCommaSeparated($node->traits) . (empty($node->adaptations) ? ';' : (' {' . $this->pStmts($node->adaptations) . $this->nl . '}')); } - protected function pStmt_TraitUseAdaptation_Precedence(Stmt\TraitUseAdaptation\Precedence $node) + protected function pStmt_TraitUseAdaptation_Precedence(Stmt\TraitUseAdaptation\Precedence $node): string { return $this->p($node->trait) . '::' . $node->method . ' insteadof ' . $this->pCommaSeparated($node->insteadof) . ';'; } - protected function pStmt_TraitUseAdaptation_Alias(Stmt\TraitUseAdaptation\Alias $node) + protected function pStmt_TraitUseAdaptation_Alias(Stmt\TraitUseAdaptation\Alias $node): string { - return (null !== $node->trait ? $this->p($node->trait) . '::' : '') . $node->method . ' as' . (null !== $node->newModifier ? ' ' . \rtrim($this->pModifiers($node->newModifier), ' ') : '') . (null !== $node->newName ? ' ' . $node->newName : '') . ';'; + return ((null !== $node->trait) ? $this->p($node->trait) . '::' : '') . $node->method . ' as' . ((null !== $node->newModifier) ? ' ' . rtrim($this->pModifiers($node->newModifier), ' ') : '') . ((null !== $node->newName) ? ' ' . $node->newName : '') . ';'; } - protected function pStmt_Property(Stmt\Property $node) + protected function pStmt_Property(Stmt\Property $node): string { - return $this->pAttrGroups($node->attrGroups) . (0 === $node->flags ? 'var ' : $this->pModifiers($node->flags)) . ($node->type ? $this->p($node->type) . ' ' : '') . $this->pCommaSeparated($node->props) . ';'; + return $this->pAttrGroups($node->attrGroups) . ((0 === $node->flags) ? 'var ' : $this->pModifiers($node->flags)) . ($node->type ? $this->p($node->type) . ' ' : '') . $this->pCommaSeparated($node->props) . ';'; } - protected function pStmt_PropertyProperty(Stmt\PropertyProperty $node) + protected function pPropertyItem(Node\PropertyItem $node): string { - return '$' . $node->name . (null !== $node->default ? ' = ' . $this->p($node->default) : ''); + return '$' . $node->name . ((null !== $node->default) ? ' = ' . $this->p($node->default) : ''); } - protected function pStmt_ClassMethod(Stmt\ClassMethod $node) + protected function pStmt_ClassMethod(Stmt\ClassMethod $node): string { - return $this->pAttrGroups($node->attrGroups) . $this->pModifiers($node->flags) . 'function ' . ($node->byRef ? '&' : '') . $node->name . '(' . $this->pMaybeMultiline($node->params) . ')' . (null !== $node->returnType ? ' : ' . $this->p($node->returnType) : '') . (null !== $node->stmts ? $this->nl . '{' . $this->pStmts($node->stmts) . $this->nl . '}' : ';'); + return $this->pAttrGroups($node->attrGroups) . $this->pModifiers($node->flags) . 'function ' . ($node->byRef ? '&' : '') . $node->name . '(' . $this->pMaybeMultiline($node->params, $this->phpVersion->supportsTrailingCommaInParamList()) . ')' . ((null !== $node->returnType) ? ': ' . $this->p($node->returnType) : '') . ((null !== $node->stmts) ? $this->nl . '{' . $this->pStmts($node->stmts) . $this->nl . '}' : ';'); } - protected function pStmt_ClassConst(Stmt\ClassConst $node) + protected function pStmt_ClassConst(Stmt\ClassConst $node): string { - return $this->pAttrGroups($node->attrGroups) . $this->pModifiers($node->flags) . 'const ' . (null !== $node->type ? $this->p($node->type) . ' ' : '') . $this->pCommaSeparated($node->consts) . ';'; + return $this->pAttrGroups($node->attrGroups) . $this->pModifiers($node->flags) . 'const ' . ((null !== $node->type) ? $this->p($node->type) . ' ' : '') . $this->pCommaSeparated($node->consts) . ';'; } - protected function pStmt_Function(Stmt\Function_ $node) + protected function pStmt_Function(Stmt\Function_ $node): string { - return $this->pAttrGroups($node->attrGroups) . 'function ' . ($node->byRef ? '&' : '') . $node->name . '(' . $this->pCommaSeparated($node->params) . ')' . (null !== $node->returnType ? ' : ' . $this->p($node->returnType) : '') . $this->nl . '{' . $this->pStmts($node->stmts) . $this->nl . '}'; + return $this->pAttrGroups($node->attrGroups) . 'function ' . ($node->byRef ? '&' : '') . $node->name . '(' . $this->pMaybeMultiline($node->params, $this->phpVersion->supportsTrailingCommaInParamList()) . ')' . ((null !== $node->returnType) ? ': ' . $this->p($node->returnType) : '') . $this->nl . '{' . $this->pStmts($node->stmts) . $this->nl . '}'; } - protected function pStmt_Const(Stmt\Const_ $node) + protected function pStmt_Const(Stmt\Const_ $node): string { return 'const ' . $this->pCommaSeparated($node->consts) . ';'; } - protected function pStmt_Declare(Stmt\Declare_ $node) + protected function pStmt_Declare(Stmt\Declare_ $node): string { - return 'declare (' . $this->pCommaSeparated($node->declares) . ')' . (null !== $node->stmts ? ' {' . $this->pStmts($node->stmts) . $this->nl . '}' : ';'); + return 'declare (' . $this->pCommaSeparated($node->declares) . ')' . ((null !== $node->stmts) ? ' {' . $this->pStmts($node->stmts) . $this->nl . '}' : ';'); } - protected function pStmt_DeclareDeclare(Stmt\DeclareDeclare $node) + protected function pDeclareItem(Node\DeclareItem $node): string { return $node->key . '=' . $this->p($node->value); } // Control flow - protected function pStmt_If(Stmt\If_ $node) + protected function pStmt_If(Stmt\If_ $node): string { - return 'if (' . $this->p($node->cond) . ') {' . $this->pStmts($node->stmts) . $this->nl . '}' . ($node->elseifs ? ' ' . $this->pImplode($node->elseifs, ' ') : '') . (null !== $node->else ? ' ' . $this->p($node->else) : ''); + return 'if (' . $this->p($node->cond) . ') {' . $this->pStmts($node->stmts) . $this->nl . '}' . ($node->elseifs ? ' ' . $this->pImplode($node->elseifs, ' ') : '') . ((null !== $node->else) ? ' ' . $this->p($node->else) : ''); } - protected function pStmt_ElseIf(Stmt\ElseIf_ $node) + protected function pStmt_ElseIf(Stmt\ElseIf_ $node): string { return 'elseif (' . $this->p($node->cond) . ') {' . $this->pStmts($node->stmts) . $this->nl . '}'; } - protected function pStmt_Else(Stmt\Else_ $node) + protected function pStmt_Else(Stmt\Else_ $node): string { + if (\count($node->stmts) === 1 && $node->stmts[0] instanceof Stmt\If_) { + // Print as "else if" rather than "else { if }" + return 'else ' . $this->p($node->stmts[0]); + } return 'else {' . $this->pStmts($node->stmts) . $this->nl . '}'; } - protected function pStmt_For(Stmt\For_ $node) + protected function pStmt_For(Stmt\For_ $node): string { - return 'for (' . $this->pCommaSeparated($node->init) . ';' . (!empty($node->cond) ? ' ' : '') . $this->pCommaSeparated($node->cond) . ';' . (!empty($node->loop) ? ' ' : '') . $this->pCommaSeparated($node->loop) . ') {' . $this->pStmts($node->stmts) . $this->nl . '}'; + return 'for (' . $this->pCommaSeparated($node->init) . ';' . ((!empty($node->cond)) ? ' ' : '') . $this->pCommaSeparated($node->cond) . ';' . ((!empty($node->loop)) ? ' ' : '') . $this->pCommaSeparated($node->loop) . ') {' . $this->pStmts($node->stmts) . $this->nl . '}'; } - protected function pStmt_Foreach(Stmt\Foreach_ $node) + protected function pStmt_Foreach(Stmt\Foreach_ $node): string { - return 'foreach (' . $this->p($node->expr) . ' as ' . (null !== $node->keyVar ? $this->p($node->keyVar) . ' => ' : '') . ($node->byRef ? '&' : '') . $this->p($node->valueVar) . ') {' . $this->pStmts($node->stmts) . $this->nl . '}'; + return 'foreach (' . $this->p($node->expr) . ' as ' . ((null !== $node->keyVar) ? $this->p($node->keyVar) . ' => ' : '') . ($node->byRef ? '&' : '') . $this->p($node->valueVar) . ') {' . $this->pStmts($node->stmts) . $this->nl . '}'; } - protected function pStmt_While(Stmt\While_ $node) + protected function pStmt_While(Stmt\While_ $node): string { return 'while (' . $this->p($node->cond) . ') {' . $this->pStmts($node->stmts) . $this->nl . '}'; } - protected function pStmt_Do(Stmt\Do_ $node) + protected function pStmt_Do(Stmt\Do_ $node): string { return 'do {' . $this->pStmts($node->stmts) . $this->nl . '} while (' . $this->p($node->cond) . ');'; } - protected function pStmt_Switch(Stmt\Switch_ $node) + protected function pStmt_Switch(Stmt\Switch_ $node): string { return 'switch (' . $this->p($node->cond) . ') {' . $this->pStmts($node->cases) . $this->nl . '}'; } - protected function pStmt_Case(Stmt\Case_ $node) + protected function pStmt_Case(Stmt\Case_ $node): string { - return (null !== $node->cond ? 'case ' . $this->p($node->cond) : 'default') . ':' . $this->pStmts($node->stmts); + return ((null !== $node->cond) ? 'case ' . $this->p($node->cond) : 'default') . ':' . $this->pStmts($node->stmts); } - protected function pStmt_TryCatch(Stmt\TryCatch $node) + protected function pStmt_TryCatch(Stmt\TryCatch $node): string { - return 'try {' . $this->pStmts($node->stmts) . $this->nl . '}' . ($node->catches ? ' ' . $this->pImplode($node->catches, ' ') : '') . ($node->finally !== null ? ' ' . $this->p($node->finally) : ''); + return 'try {' . $this->pStmts($node->stmts) . $this->nl . '}' . ($node->catches ? ' ' . $this->pImplode($node->catches, ' ') : '') . (($node->finally !== null) ? ' ' . $this->p($node->finally) : ''); } - protected function pStmt_Catch(Stmt\Catch_ $node) + protected function pStmt_Catch(Stmt\Catch_ $node): string { - return 'catch (' . $this->pImplode($node->types, '|') . ($node->var !== null ? ' ' . $this->p($node->var) : '') . ') {' . $this->pStmts($node->stmts) . $this->nl . '}'; + return 'catch (' . $this->pImplode($node->types, '|') . (($node->var !== null) ? ' ' . $this->p($node->var) : '') . ') {' . $this->pStmts($node->stmts) . $this->nl . '}'; } - protected function pStmt_Finally(Stmt\Finally_ $node) + protected function pStmt_Finally(Stmt\Finally_ $node): string { return 'finally {' . $this->pStmts($node->stmts) . $this->nl . '}'; } - protected function pStmt_Break(Stmt\Break_ $node) + protected function pStmt_Break(Stmt\Break_ $node): string { - return 'break' . ($node->num !== null ? ' ' . $this->p($node->num) : '') . ';'; + return 'break' . (($node->num !== null) ? ' ' . $this->p($node->num) : '') . ';'; } - protected function pStmt_Continue(Stmt\Continue_ $node) + protected function pStmt_Continue(Stmt\Continue_ $node): string { - return 'continue' . ($node->num !== null ? ' ' . $this->p($node->num) : '') . ';'; + return 'continue' . (($node->num !== null) ? ' ' . $this->p($node->num) : '') . ';'; } - protected function pStmt_Return(Stmt\Return_ $node) + protected function pStmt_Return(Stmt\Return_ $node): string { - return 'return' . (null !== $node->expr ? ' ' . $this->p($node->expr) : '') . ';'; + return 'return' . ((null !== $node->expr) ? ' ' . $this->p($node->expr) : '') . ';'; } - protected function pStmt_Throw(Stmt\Throw_ $node) - { - return 'throw ' . $this->p($node->expr) . ';'; - } - protected function pStmt_Label(Stmt\Label $node) + protected function pStmt_Label(Stmt\Label $node): string { return $node->name . ':'; } - protected function pStmt_Goto(Stmt\Goto_ $node) + protected function pStmt_Goto(Stmt\Goto_ $node): string { return 'goto ' . $node->name . ';'; } // Other - protected function pStmt_Expression(Stmt\Expression $node) + protected function pStmt_Expression(Stmt\Expression $node): string { return $this->p($node->expr) . ';'; } - protected function pStmt_Echo(Stmt\Echo_ $node) + protected function pStmt_Echo(Stmt\Echo_ $node): string { return 'echo ' . $this->pCommaSeparated($node->exprs) . ';'; } - protected function pStmt_Static(Stmt\Static_ $node) + protected function pStmt_Static(Stmt\Static_ $node): string { return 'static ' . $this->pCommaSeparated($node->vars) . ';'; } - protected function pStmt_Global(Stmt\Global_ $node) + protected function pStmt_Global(Stmt\Global_ $node): string { return 'global ' . $this->pCommaSeparated($node->vars) . ';'; } - protected function pStmt_StaticVar(Stmt\StaticVar $node) + protected function pStaticVar(Node\StaticVar $node): string { - return $this->p($node->var) . (null !== $node->default ? ' = ' . $this->p($node->default) : ''); + return $this->p($node->var) . ((null !== $node->default) ? ' = ' . $this->p($node->default) : ''); } - protected function pStmt_Unset(Stmt\Unset_ $node) + protected function pStmt_Unset(Stmt\Unset_ $node): string { return 'unset(' . $this->pCommaSeparated($node->vars) . ');'; } - protected function pStmt_InlineHTML(Stmt\InlineHTML $node) + protected function pStmt_InlineHTML(Stmt\InlineHTML $node): string { - $newline = $node->getAttribute('hasLeadingNewline', \true) ? "\n" : ''; + $newline = $node->getAttribute('hasLeadingNewline', \true) ? $this->newline : ''; return '?>' . $newline . $node->value . 'remaining; } - protected function pStmt_Nop(Stmt\Nop $node) + protected function pStmt_Nop(Stmt\Nop $node): string { return ''; } + protected function pStmt_Block(Stmt\Block $node): string + { + return '{' . $this->pStmts($node->stmts) . $this->nl . '}'; + } // Helpers - protected function pClassCommon(Stmt\Class_ $node, $afterClassToken) + protected function pClassCommon(Stmt\Class_ $node, string $afterClassToken): string { - return $this->pAttrGroups($node->attrGroups, $node->name === null) . $this->pModifiers($node->flags) . 'class' . $afterClassToken . (null !== $node->extends ? ' extends ' . $this->p($node->extends) : '') . (!empty($node->implements) ? ' implements ' . $this->pCommaSeparated($node->implements) : '') . $this->nl . '{' . $this->pStmts($node->stmts) . $this->nl . '}'; + return $this->pAttrGroups($node->attrGroups, $node->name === null) . $this->pModifiers($node->flags) . 'class' . $afterClassToken . ((null !== $node->extends) ? ' extends ' . $this->p($node->extends) : '') . ((!empty($node->implements)) ? ' implements ' . $this->pCommaSeparated($node->implements) : '') . $this->nl . '{' . $this->pStmts($node->stmts) . $this->nl . '}'; } - protected function pObjectProperty($node) + protected function pObjectProperty(Node $node): string { if ($node instanceof Expr) { return '{' . $this->p($node) . '}'; } else { - return $node; + assert($node instanceof Node\Identifier); + return $node->name; } } - protected function pEncapsList(array $encapsList, $quote) + /** @param (Expr|Node\InterpolatedStringPart)[] $encapsList */ + protected function pEncapsList(array $encapsList, ?string $quote): string { $return = ''; foreach ($encapsList as $element) { - if ($element instanceof Scalar\EncapsedStringPart) { + if ($element instanceof Node\InterpolatedStringPart) { $return .= $this->escapeString($element->value, $quote); } else { $return .= '{' . $this->p($element) . '}'; @@ -23185,59 +22441,68 @@ class Standard extends PrettyPrinterAbstract } return $return; } - protected function pSingleQuotedString(string $string) + protected function pSingleQuotedString(string $string): string { - return '\'' . \addcslashes($string, '\'\\') . '\''; + // It is idiomatic to only escape backslashes when necessary, i.e. when followed by ', \ or + // the end of the string ('Foo\Bar' instead of 'Foo\\Bar'). However, we also don't want to + // produce an odd number of backslashes, so '\\\\a' should not get rendered as '\\\a', even + // though that would be legal. + $regex = '/\'|\\\\(?=[\'\\\\]|$)|(?<=\\\\)\\\\/'; + return '\'' . preg_replace($regex, '\\\\$0', $string) . '\''; } - protected function escapeString($string, $quote) + protected function escapeString(string $string, ?string $quote): string { if (null === $quote) { // For doc strings, don't escape newlines - $escaped = \addcslashes($string, "\t\f\v\$\\"); + $escaped = addcslashes($string, "\t\f\v\$\\"); + // But do escape isolated \r. Combined with the terminating newline, it might get + // interpreted as \r\n and dropped from the string contents. + $escaped = preg_replace('/\r(?!\n)/', '\r', $escaped); + if ($this->phpVersion->supportsFlexibleHeredoc()) { + $escaped = $this->indentString($escaped); + } } else { - $escaped = \addcslashes($string, "\n\r\t\f\v\$" . $quote . "\\"); + $escaped = addcslashes($string, "\n\r\t\f\v\$" . $quote . "\\"); } // Escape control characters and non-UTF-8 characters. // Regex based on https://stackoverflow.com/a/11709412/385378. $regex = '/( - [\\x00-\\x08\\x0E-\\x1F] # Control characters - | [\\xC0-\\xC1] # Invalid UTF-8 Bytes - | [\\xF5-\\xFF] # Invalid UTF-8 Bytes - | \\xE0(?=[\\x80-\\x9F]) # Overlong encoding of prior code point - | \\xF0(?=[\\x80-\\x8F]) # Overlong encoding of prior code point - | [\\xC2-\\xDF](?![\\x80-\\xBF]) # Invalid UTF-8 Sequence Start - | [\\xE0-\\xEF](?![\\x80-\\xBF]{2}) # Invalid UTF-8 Sequence Start - | [\\xF0-\\xF4](?![\\x80-\\xBF]{3}) # Invalid UTF-8 Sequence Start - | (?<=[\\x00-\\x7F\\xF5-\\xFF])[\\x80-\\xBF] # Invalid UTF-8 Sequence Middle - | (? $part) { - $atStart = $i === 0; - $atEnd = $i === \count($parts) - 1; - if ($part instanceof Scalar\EncapsedStringPart && $this->containsEndLabel($part->value, $label, $atStart, $atEnd)) { + if ($part instanceof Node\InterpolatedStringPart && $this->containsEndLabel($this->escapeString($part->value, null), $label, $i === 0)) { return \true; } } return \false; } - protected function pDereferenceLhs(Node $node) + protected function pDereferenceLhs(Node $node): string { if (!$this->dereferenceLhsRequiresParens($node)) { return $this->p($node); @@ -23245,7 +22510,7 @@ class Standard extends PrettyPrinterAbstract return '(' . $this->p($node) . ')'; } } - protected function pStaticDereferenceLhs(Node $node) + protected function pStaticDereferenceLhs(Node $node): string { if (!$this->staticDereferenceLhsRequiresParens($node)) { return $this->p($node); @@ -23253,7 +22518,7 @@ class Standard extends PrettyPrinterAbstract return '(' . $this->p($node) . ')'; } } - protected function pCallLhs(Node $node) + protected function pCallLhs(Node $node): string { if (!$this->callLhsRequiresParens($node)) { return $this->p($node); @@ -23261,7 +22526,7 @@ class Standard extends PrettyPrinterAbstract return '(' . $this->p($node) . ')'; } } - protected function pNewVariable(Node $node) : string + protected function pNewOperand(Node $node): string { if (!$this->newOperandRequiresParens($node)) { return $this->p($node); @@ -23271,9 +22536,8 @@ class Standard extends PrettyPrinterAbstract } /** * @param Node[] $nodes - * @return bool */ - protected function hasNodeWithComments(array $nodes) + protected function hasNodeWithComments(array $nodes): bool { foreach ($nodes as $node) { if ($node && $node->getComments()) { @@ -23282,7 +22546,8 @@ class Standard extends PrettyPrinterAbstract } return \false; } - protected function pMaybeMultiline(array $nodes, bool $trailingComma = \false) + /** @param Node[] $nodes */ + protected function pMaybeMultiline(array $nodes, bool $trailingComma = \false): string { if (!$this->hasNodeWithComments($nodes)) { return $this->pCommaSeparated($nodes); @@ -23290,7 +22555,8 @@ class Standard extends PrettyPrinterAbstract return $this->pCommaSeparatedMultiline($nodes, $trailingComma) . $this->nl; } } - protected function pAttrGroups(array $nodes, bool $inline = \false) : string + /** @param Node\AttributeGroup[] $nodes */ + protected function pAttrGroups(array $nodes, bool $inline = \false): string { $result = ''; $sep = $inline ? ' ' : $this->nl; @@ -23306,166 +22572,201 @@ declare (strict_types=1); namespace PHPUnitPHAR\PhpParser; use PHPUnitPHAR\PhpParser\Internal\DiffElem; +use PHPUnitPHAR\PhpParser\Internal\Differ; use PHPUnitPHAR\PhpParser\Internal\PrintableNewAnonClassNode; use PHPUnitPHAR\PhpParser\Internal\TokenStream; +use PHPUnitPHAR\PhpParser\Node\AttributeGroup; use PHPUnitPHAR\PhpParser\Node\Expr; use PHPUnitPHAR\PhpParser\Node\Expr\AssignOp; use PHPUnitPHAR\PhpParser\Node\Expr\BinaryOp; use PHPUnitPHAR\PhpParser\Node\Expr\Cast; +use PHPUnitPHAR\PhpParser\Node\IntersectionType; +use PHPUnitPHAR\PhpParser\Node\MatchArm; +use PHPUnitPHAR\PhpParser\Node\Param; use PHPUnitPHAR\PhpParser\Node\Scalar; use PHPUnitPHAR\PhpParser\Node\Stmt; -abstract class PrettyPrinterAbstract +use PHPUnitPHAR\PhpParser\Node\UnionType; +abstract class PrettyPrinterAbstract implements PrettyPrinter { - const FIXUP_PREC_LEFT = 0; + protected const FIXUP_PREC_LEFT = 0; // LHS operand affected by precedence - const FIXUP_PREC_RIGHT = 1; + protected const FIXUP_PREC_RIGHT = 1; // RHS operand affected by precedence - const FIXUP_CALL_LHS = 2; + protected const FIXUP_PREC_UNARY = 2; + // Only operand affected by precedence + protected const FIXUP_CALL_LHS = 3; // LHS of call - const FIXUP_DEREF_LHS = 3; + protected const FIXUP_DEREF_LHS = 4; // LHS of dereferencing operation - const FIXUP_BRACED_NAME = 4; + protected const FIXUP_STATIC_DEREF_LHS = 5; + // LHS of static dereferencing operation + protected const FIXUP_BRACED_NAME = 6; // Name operand that may require bracing - const FIXUP_VAR_BRACED_NAME = 5; + protected const FIXUP_VAR_BRACED_NAME = 7; // Name operand that may require ${} bracing - const FIXUP_ENCAPSED = 6; + protected const FIXUP_ENCAPSED = 8; // Encapsed string part - const FIXUP_NEW = 7; + protected const FIXUP_NEW = 9; // New/instanceof operand - const FIXUP_STATIC_DEREF_LHS = 8; - // LHS of static dereferencing operation - protected $precedenceMap = [ - // [precedence, associativity] - // where for precedence -1 is %left, 0 is %nonassoc and 1 is %right - BinaryOp\Pow::class => [0, 1], - Expr\BitwiseNot::class => [10, 1], - Expr\PreInc::class => [10, 1], - Expr\PreDec::class => [10, 1], - Expr\PostInc::class => [10, -1], - Expr\PostDec::class => [10, -1], - Expr\UnaryPlus::class => [10, 1], - Expr\UnaryMinus::class => [10, 1], - Cast\Int_::class => [10, 1], - Cast\Double::class => [10, 1], - Cast\String_::class => [10, 1], - Cast\Array_::class => [10, 1], - Cast\Object_::class => [10, 1], - Cast\Bool_::class => [10, 1], - Cast\Unset_::class => [10, 1], - Expr\ErrorSuppress::class => [10, 1], - Expr\Instanceof_::class => [20, 0], - Expr\BooleanNot::class => [30, 1], - BinaryOp\Mul::class => [40, -1], - BinaryOp\Div::class => [40, -1], - BinaryOp\Mod::class => [40, -1], - BinaryOp\Plus::class => [50, -1], - BinaryOp\Minus::class => [50, -1], - BinaryOp\Concat::class => [50, -1], - BinaryOp\ShiftLeft::class => [60, -1], - BinaryOp\ShiftRight::class => [60, -1], - BinaryOp\Smaller::class => [70, 0], - BinaryOp\SmallerOrEqual::class => [70, 0], - BinaryOp\Greater::class => [70, 0], - BinaryOp\GreaterOrEqual::class => [70, 0], - BinaryOp\Equal::class => [80, 0], - BinaryOp\NotEqual::class => [80, 0], - BinaryOp\Identical::class => [80, 0], - BinaryOp\NotIdentical::class => [80, 0], - BinaryOp\Spaceship::class => [80, 0], - BinaryOp\BitwiseAnd::class => [90, -1], - BinaryOp\BitwiseXor::class => [100, -1], - BinaryOp\BitwiseOr::class => [110, -1], - BinaryOp\BooleanAnd::class => [120, -1], - BinaryOp\BooleanOr::class => [130, -1], - BinaryOp\Coalesce::class => [140, 1], - Expr\Ternary::class => [150, 0], - // parser uses %left for assignments, but they really behave as %right - Expr\Assign::class => [160, 1], - Expr\AssignRef::class => [160, 1], - AssignOp\Plus::class => [160, 1], - AssignOp\Minus::class => [160, 1], - AssignOp\Mul::class => [160, 1], - AssignOp\Div::class => [160, 1], - AssignOp\Concat::class => [160, 1], - AssignOp\Mod::class => [160, 1], - AssignOp\BitwiseAnd::class => [160, 1], - AssignOp\BitwiseOr::class => [160, 1], - AssignOp\BitwiseXor::class => [160, 1], - AssignOp\ShiftLeft::class => [160, 1], - AssignOp\ShiftRight::class => [160, 1], - AssignOp\Pow::class => [160, 1], - AssignOp\Coalesce::class => [160, 1], - Expr\YieldFrom::class => [165, 1], - Expr\Print_::class => [168, 1], - BinaryOp\LogicalAnd::class => [170, -1], - BinaryOp\LogicalXor::class => [180, -1], - BinaryOp\LogicalOr::class => [190, -1], - Expr\Include_::class => [200, -1], + protected const MAX_PRECEDENCE = 1000; + /** @var array */ + protected array $precedenceMap = [ + // [precedence, precedenceLHS, precedenceRHS] + // Where the latter two are the precedences to use for the LHS and RHS of a binary operator, + // where 1 is added to one of the sides depending on associativity. This information is not + // used for unary operators and set to -1. + Expr\Clone_::class => [-10, 0, 1], + BinaryOp\Pow::class => [0, 0, 1], + Expr\BitwiseNot::class => [10, -1, -1], + Expr\UnaryPlus::class => [10, -1, -1], + Expr\UnaryMinus::class => [10, -1, -1], + Cast\Int_::class => [10, -1, -1], + Cast\Double::class => [10, -1, -1], + Cast\String_::class => [10, -1, -1], + Cast\Array_::class => [10, -1, -1], + Cast\Object_::class => [10, -1, -1], + Cast\Bool_::class => [10, -1, -1], + Cast\Unset_::class => [10, -1, -1], + Expr\ErrorSuppress::class => [10, -1, -1], + Expr\Instanceof_::class => [20, -1, -1], + Expr\BooleanNot::class => [30, -1, -1], + BinaryOp\Mul::class => [40, 41, 40], + BinaryOp\Div::class => [40, 41, 40], + BinaryOp\Mod::class => [40, 41, 40], + BinaryOp\Plus::class => [50, 51, 50], + BinaryOp\Minus::class => [50, 51, 50], + BinaryOp\Concat::class => [50, 51, 50], + BinaryOp\ShiftLeft::class => [60, 61, 60], + BinaryOp\ShiftRight::class => [60, 61, 60], + BinaryOp\Smaller::class => [70, 70, 70], + BinaryOp\SmallerOrEqual::class => [70, 70, 70], + BinaryOp\Greater::class => [70, 70, 70], + BinaryOp\GreaterOrEqual::class => [70, 70, 70], + BinaryOp\Equal::class => [80, 80, 80], + BinaryOp\NotEqual::class => [80, 80, 80], + BinaryOp\Identical::class => [80, 80, 80], + BinaryOp\NotIdentical::class => [80, 80, 80], + BinaryOp\Spaceship::class => [80, 80, 80], + BinaryOp\BitwiseAnd::class => [90, 91, 90], + BinaryOp\BitwiseXor::class => [100, 101, 100], + BinaryOp\BitwiseOr::class => [110, 111, 110], + BinaryOp\BooleanAnd::class => [120, 121, 120], + BinaryOp\BooleanOr::class => [130, 131, 130], + BinaryOp\Coalesce::class => [140, 140, 141], + Expr\Ternary::class => [150, 150, 150], + Expr\Assign::class => [160, -1, -1], + Expr\AssignRef::class => [160, -1, -1], + AssignOp\Plus::class => [160, -1, -1], + AssignOp\Minus::class => [160, -1, -1], + AssignOp\Mul::class => [160, -1, -1], + AssignOp\Div::class => [160, -1, -1], + AssignOp\Concat::class => [160, -1, -1], + AssignOp\Mod::class => [160, -1, -1], + AssignOp\BitwiseAnd::class => [160, -1, -1], + AssignOp\BitwiseOr::class => [160, -1, -1], + AssignOp\BitwiseXor::class => [160, -1, -1], + AssignOp\ShiftLeft::class => [160, -1, -1], + AssignOp\ShiftRight::class => [160, -1, -1], + AssignOp\Pow::class => [160, -1, -1], + AssignOp\Coalesce::class => [160, -1, -1], + Expr\YieldFrom::class => [170, -1, -1], + Expr\Yield_::class => [175, -1, -1], + Expr\Print_::class => [180, -1, -1], + BinaryOp\LogicalAnd::class => [190, 191, 190], + BinaryOp\LogicalXor::class => [200, 201, 200], + BinaryOp\LogicalOr::class => [210, 211, 210], + Expr\Include_::class => [220, -1, -1], + Expr\ArrowFunction::class => [230, -1, -1], + Expr\Throw_::class => [240, -1, -1], ]; /** @var int Current indentation level. */ - protected $indentLevel; + protected int $indentLevel; + /** @var string Newline style. Does not include current indentation. */ + protected string $newline; /** @var string Newline including current indentation. */ - protected $nl; - /** @var string Token placed at end of doc string to ensure it is followed by a newline. */ - protected $docStringEndToken; + protected string $nl; + /** @var string|null Token placed at end of doc string to ensure it is followed by a newline. + * Null if flexible doc strings are used. */ + protected ?string $docStringEndToken; /** @var bool Whether semicolon namespaces can be used (i.e. no global namespace is used) */ - protected $canUseSemicolonNamespaces; - /** @var array Pretty printer options */ - protected $options; - /** @var TokenStream Original tokens for use in format-preserving pretty print */ - protected $origTokens; - /** @var Internal\Differ Differ for node lists */ - protected $nodeListDiffer; - /** @var bool[] Map determining whether a certain character is a label character */ - protected $labelCharMap; + protected bool $canUseSemicolonNamespaces; + /** @var bool Whether to use short array syntax if the node specifies no preference */ + protected bool $shortArraySyntax; + /** @var PhpVersion PHP version to target */ + protected PhpVersion $phpVersion; + /** @var TokenStream|null Original tokens for use in format-preserving pretty print */ + protected ?TokenStream $origTokens; + /** @var Internal\Differ Differ for node lists */ + protected Differ $nodeListDiffer; + /** @var array Map determining whether a certain character is a label character */ + protected array $labelCharMap; /** - * @var int[][] Map from token classes and subnode names to FIXUP_* constants. This is used - * during format-preserving prints to place additional parens/braces if necessary. + * @var array> Map from token classes and subnode names to FIXUP_* constants. + * This is used during format-preserving prints to place additional parens/braces if necessary. */ - protected $fixupMap; + protected array $fixupMap; /** - * @var int[][] Map from "{$node->getType()}->{$subNode}" to ['left' => $l, 'right' => $r], - * where $l and $r specify the token type that needs to be stripped when removing - * this node. + * @var array Map from "{$node->getType()}->{$subNode}" + * to ['left' => $l, 'right' => $r], where $l and $r specify the token type that needs to be stripped + * when removing this node. */ - protected $removalMap; + protected array $removalMap; /** - * @var mixed[] Map from "{$node->getType()}->{$subNode}" to [$find, $beforeToken, $extraLeft, $extraRight]. - * $find is an optional token after which the insertion occurs. $extraLeft/Right - * are optionally added before/after the main insertions. + * @var array Map from + * "{$node->getType()}->{$subNode}" to [$find, $beforeToken, $extraLeft, $extraRight]. + * $find is an optional token after which the insertion occurs. $extraLeft/Right + * are optionally added before/after the main insertions. */ - protected $insertionMap; + protected array $insertionMap; /** - * @var string[] Map From "{$node->getType()}->{$subNode}" to string that should be inserted - * between elements of this list subnode. + * @var array Map From "{$class}->{$subNode}" to string that should be inserted + * between elements of this list subnode. */ - protected $listInsertionMap; - protected $emptyListInsertionMap; - /** @var int[] Map from "{$node->getType()}->{$subNode}" to token before which the modifiers - * should be reprinted. */ - protected $modifierChangeMap; + protected array $listInsertionMap; + /** + * @var array + */ + protected array $emptyListInsertionMap; + /** @var array Map from "{$class}->{$subNode}" to [$printFn, $token] + * where $printFn is the function to print the modifiers and $token is the token before which + * the modifiers should be reprinted. */ + protected array $modifierChangeMap; /** * Creates a pretty printer instance using the given options. * * Supported options: - * * bool $shortArraySyntax = false: Whether to use [] instead of array() as the default array - * syntax, if the node does not specify a format. - * - * @param array $options Dictionary of formatting options + * * PhpVersion $phpVersion: The PHP version to target (default to PHP 7.4). This option + * controls compatibility of the generated code with older PHP + * versions in cases where a simple stylistic choice exists (e.g. + * array() vs []). It is safe to pretty-print an AST for a newer + * PHP version while specifying an older target (but the result will + * of course not be compatible with the older version in that case). + * * string $newline: The newline style to use. Should be "\n" (default) or "\r\n". + * * bool $shortArraySyntax: Whether to use [] instead of array() as the default array + * syntax, if the node does not specify a format. Defaults to whether + * the phpVersion support short array syntax. + * + * @param array{ + * phpVersion?: PhpVersion, newline?: string, shortArraySyntax?: bool + * } $options Dictionary of formatting options */ public function __construct(array $options = []) { - $this->docStringEndToken = '_DOC_STRING_END_' . \mt_rand(); - $defaultOptions = ['shortArraySyntax' => \false]; - $this->options = $options + $defaultOptions; + $this->phpVersion = $options['phpVersion'] ?? PhpVersion::fromComponents(7, 4); + $this->newline = $options['newline'] ?? "\n"; + if ($this->newline !== "\n" && $this->newline != "\r\n") { + throw new \LogicException('Option "newline" must be one of "\n" or "\r\n"'); + } + $this->shortArraySyntax = $options['shortArraySyntax'] ?? $this->phpVersion->supportsShortArraySyntax(); + $this->docStringEndToken = $this->phpVersion->supportsFlexibleHeredoc() ? null : ('_DOC_STRING_END_' . mt_rand()); } /** * Reset pretty printing state. */ - protected function resetState() + protected function resetState(): void { $this->indentLevel = 0; - $this->nl = "\n"; + $this->nl = $this->newline; $this->origTokens = null; } /** @@ -23473,15 +22774,15 @@ abstract class PrettyPrinterAbstract * * @param int $level Level in number of spaces */ - protected function setIndentLevel(int $level) + protected function setIndentLevel(int $level): void { $this->indentLevel = $level; - $this->nl = "\n" . \str_repeat(' ', $level); + $this->nl = $this->newline . \str_repeat(' ', $level); } /** * Increase indentation level. */ - protected function indent() + protected function indent(): void { $this->indentLevel += 4; $this->nl .= ' '; @@ -23489,11 +22790,11 @@ abstract class PrettyPrinterAbstract /** * Decrease indentation level. */ - protected function outdent() + protected function outdent(): void { - \assert($this->indentLevel >= 4); + assert($this->indentLevel >= 4); $this->indentLevel -= 4; - $this->nl = "\n" . \str_repeat(' ', $this->indentLevel); + $this->nl = $this->newline . str_repeat(' ', $this->indentLevel); } /** * Pretty prints an array of statements. @@ -23502,11 +22803,11 @@ abstract class PrettyPrinterAbstract * * @return string Pretty printed statements */ - public function prettyPrint(array $stmts) : string + public function prettyPrint(array $stmts): string { $this->resetState(); $this->preprocessNodes($stmts); - return \ltrim($this->handleMagicTokens($this->pStmts($stmts, \false))); + return ltrim($this->handleMagicTokens($this->pStmts($stmts, \false))); } /** * Pretty prints an expression. @@ -23515,7 +22816,7 @@ abstract class PrettyPrinterAbstract * * @return string Pretty printed node */ - public function prettyPrintExpr(Expr $node) : string + public function prettyPrintExpr(Expr $node): string { $this->resetState(); return $this->handleMagicTokens($this->p($node)); @@ -23527,17 +22828,17 @@ abstract class PrettyPrinterAbstract * * @return string Pretty printed statements */ - public function prettyPrintFile(array $stmts) : string + public function prettyPrintFile(array $stmts): string { if (!$stmts) { - return "newline . $this->newline; } - $p = "prettyPrint($stmts); + $p = "newline . $this->newline . $this->prettyPrint($stmts); if ($stmts[0] instanceof Stmt\InlineHTML) { - $p = \preg_replace('/^<\\?php\\s+\\?>\\n?/', '', $p); + $p = preg_replace('/^<\?php\s+\?>\r?\n?/', '', $p); } - if ($stmts[\count($stmts) - 1] instanceof Stmt\InlineHTML) { - $p = \preg_replace('/<\\?php$/', '', \rtrim($p)); + if ($stmts[count($stmts) - 1] instanceof Stmt\InlineHTML) { + $p = preg_replace('/<\?php$/', '', rtrim($p)); } return $p; } @@ -23546,7 +22847,7 @@ abstract class PrettyPrinterAbstract * * @param Node[] $nodes Array of nodes */ - protected function preprocessNodes(array $nodes) + protected function preprocessNodes(array $nodes): void { /* We can use semicolon-namespaces unless there is a global namespace declaration */ $this->canUseSemicolonNamespaces = \true; @@ -23558,27 +22859,26 @@ abstract class PrettyPrinterAbstract } } /** - * Handles (and removes) no-indent and doc-string-end tokens. - * - * @param string $str - * @return string + * Handles (and removes) doc-string-end tokens. */ - protected function handleMagicTokens(string $str) : string + protected function handleMagicTokens(string $str): string { - // Replace doc-string-end tokens with nothing or a newline - $str = \str_replace($this->docStringEndToken . ";\n", ";\n", $str); - $str = \str_replace($this->docStringEndToken, "\n", $str); + if ($this->docStringEndToken !== null) { + // Replace doc-string-end tokens with nothing or a newline + $str = str_replace($this->docStringEndToken . ';' . $this->newline, ';' . $this->newline, $str); + $str = str_replace($this->docStringEndToken, $this->newline, $str); + } return $str; } /** * Pretty prints an array of nodes (statements) and indents them optionally. * - * @param Node[] $nodes Array of nodes - * @param bool $indent Whether to indent the printed nodes + * @param Node[] $nodes Array of nodes + * @param bool $indent Whether to indent the printed nodes * * @return string Pretty printed statements */ - protected function pStmts(array $nodes, bool $indent = \true) : string + protected function pStmts(array $nodes, bool $indent = \true): string { if ($indent) { $this->indent(); @@ -23602,78 +22902,90 @@ abstract class PrettyPrinterAbstract /** * Pretty-print an infix operation while taking precedence into account. * - * @param string $class Node class of operator - * @param Node $leftNode Left-hand side node + * @param string $class Node class of operator + * @param Node $leftNode Left-hand side node * @param string $operatorString String representation of the operator - * @param Node $rightNode Right-hand side node + * @param Node $rightNode Right-hand side node + * @param int $precedence Precedence of parent operator + * @param int $lhsPrecedence Precedence for unary operator on LHS of binary operator * * @return string Pretty printed infix operation */ - protected function pInfixOp(string $class, Node $leftNode, string $operatorString, Node $rightNode) : string + protected function pInfixOp(string $class, Node $leftNode, string $operatorString, Node $rightNode, int $precedence, int $lhsPrecedence): string { - list($precedence, $associativity) = $this->precedenceMap[$class]; - return $this->pPrec($leftNode, $precedence, $associativity, -1) . $operatorString . $this->pPrec($rightNode, $precedence, $associativity, 1); + list($opPrecedence, $newPrecedenceLHS, $newPrecedenceRHS) = $this->precedenceMap[$class]; + $prefix = ''; + $suffix = ''; + if ($opPrecedence >= $precedence) { + $prefix = '('; + $suffix = ')'; + $lhsPrecedence = self::MAX_PRECEDENCE; + } + return $prefix . $this->p($leftNode, $newPrecedenceLHS, $newPrecedenceLHS) . $operatorString . $this->p($rightNode, $newPrecedenceRHS, $lhsPrecedence) . $suffix; } /** * Pretty-print a prefix operation while taking precedence into account. * - * @param string $class Node class of operator + * @param string $class Node class of operator * @param string $operatorString String representation of the operator - * @param Node $node Node + * @param Node $node Node + * @param int $precedence Precedence of parent operator + * @param int $lhsPrecedence Precedence for unary operator on LHS of binary operator * * @return string Pretty printed prefix operation */ - protected function pPrefixOp(string $class, string $operatorString, Node $node) : string + protected function pPrefixOp(string $class, string $operatorString, Node $node, int $precedence, int $lhsPrecedence): string { - list($precedence, $associativity) = $this->precedenceMap[$class]; - return $operatorString . $this->pPrec($node, $precedence, $associativity, 1); + $opPrecedence = $this->precedenceMap[$class][0]; + $prefix = ''; + $suffix = ''; + if ($opPrecedence >= $lhsPrecedence) { + $prefix = '('; + $suffix = ')'; + $lhsPrecedence = self::MAX_PRECEDENCE; + } + $printedArg = $this->p($node, $opPrecedence, $lhsPrecedence); + if ($operatorString === '+' && $printedArg[0] === '+' || $operatorString === '-' && $printedArg[0] === '-') { + // Avoid printing +(+$a) as ++$a and similar. + $printedArg = '(' . $printedArg . ')'; + } + return $prefix . $operatorString . $printedArg . $suffix; } /** * Pretty-print a postfix operation while taking precedence into account. * - * @param string $class Node class of operator + * @param string $class Node class of operator * @param string $operatorString String representation of the operator - * @param Node $node Node + * @param Node $node Node + * @param int $precedence Precedence of parent operator + * @param int $lhsPrecedence Precedence for unary operator on LHS of binary operator * * @return string Pretty printed postfix operation */ - protected function pPostfixOp(string $class, Node $node, string $operatorString) : string + protected function pPostfixOp(string $class, Node $node, string $operatorString, int $precedence, int $lhsPrecedence): string { - list($precedence, $associativity) = $this->precedenceMap[$class]; - return $this->pPrec($node, $precedence, $associativity, -1) . $operatorString; - } - /** - * Prints an expression node with the least amount of parentheses necessary to preserve the meaning. - * - * @param Node $node Node to pretty print - * @param int $parentPrecedence Precedence of the parent operator - * @param int $parentAssociativity Associativity of parent operator - * (-1 is left, 0 is nonassoc, 1 is right) - * @param int $childPosition Position of the node relative to the operator - * (-1 is left, 1 is right) - * - * @return string The pretty printed node - */ - protected function pPrec(Node $node, int $parentPrecedence, int $parentAssociativity, int $childPosition) : string - { - $class = \get_class($node); - if (isset($this->precedenceMap[$class])) { - $childPrecedence = $this->precedenceMap[$class][0]; - if ($childPrecedence > $parentPrecedence || $parentPrecedence === $childPrecedence && $parentAssociativity !== $childPosition) { - return '(' . $this->p($node) . ')'; - } + $opPrecedence = $this->precedenceMap[$class][0]; + $prefix = ''; + $suffix = ''; + if ($opPrecedence >= $precedence) { + $prefix = '('; + $suffix = ')'; + $lhsPrecedence = self::MAX_PRECEDENCE; } - return $this->p($node); + if ($opPrecedence < $lhsPrecedence) { + $lhsPrecedence = $opPrecedence; + } + return $prefix . $this->p($node, $opPrecedence, $lhsPrecedence) . $operatorString . $suffix; } /** * Pretty prints an array of nodes and implodes the printed values. * * @param Node[] $nodes Array of Nodes to be printed - * @param string $glue Character to implode with + * @param string $glue Character to implode with * - * @return string Imploded pretty printed nodes + * @return string Imploded pretty printed nodes> $pre */ - protected function pImplode(array $nodes, string $glue = '') : string + protected function pImplode(array $nodes, string $glue = ''): string { $pNodes = []; foreach ($nodes as $node) { @@ -23683,7 +22995,7 @@ abstract class PrettyPrinterAbstract $pNodes[] = $this->p($node); } } - return \implode($glue, $pNodes); + return implode($glue, $pNodes); } /** * Pretty prints an array of nodes and implodes the printed values with commas. @@ -23692,7 +23004,7 @@ abstract class PrettyPrinterAbstract * * @return string Comma separated pretty printed nodes */ - protected function pCommaSeparated(array $nodes) : string + protected function pCommaSeparated(array $nodes): string { return $this->pImplode($nodes, ', '); } @@ -23701,16 +23013,16 @@ abstract class PrettyPrinterAbstract * * The result includes a leading newline and one level of indentation (same as pStmts). * - * @param Node[] $nodes Array of Nodes to be printed - * @param bool $trailingComma Whether to use a trailing comma + * @param Node[] $nodes Array of Nodes to be printed + * @param bool $trailingComma Whether to use a trailing comma * * @return string Comma separated pretty printed nodes in multiline style */ - protected function pCommaSeparatedMultiline(array $nodes, bool $trailingComma) : string + protected function pCommaSeparatedMultiline(array $nodes, bool $trailingComma): string { $this->indent(); $result = ''; - $lastIdx = \count($nodes) - 1; + $lastIdx = count($nodes) - 1; foreach ($nodes as $idx => $node) { if ($node !== null) { $comments = $node->getComments(); @@ -23735,13 +23047,13 @@ abstract class PrettyPrinterAbstract * * @return string Reformatted text of comments */ - protected function pComments(array $comments) : string + protected function pComments(array $comments): string { $formattedComments = []; foreach ($comments as $comment) { - $formattedComments[] = \str_replace("\n", $this->nl, $comment->getReformattedText()); + $formattedComments[] = str_replace("\n", $this->nl, $comment->getReformattedText()); } - return \implode($this->nl, $formattedComments); + return implode($this->nl, $formattedComments); } /** * Perform a format-preserving pretty print of an AST. @@ -23754,13 +23066,11 @@ abstract class PrettyPrinterAbstract * * The CloningVisitor must be run on the AST prior to modification. * * The original tokens must be provided, using the getTokens() method on the lexer. * - * @param Node[] $stmts Modified AST with links to original AST - * @param Node[] $origStmts Original AST with token offset information - * @param array $origTokens Tokens of the original code - * - * @return string + * @param Node[] $stmts Modified AST with links to original AST + * @param Node[] $origStmts Original AST with token offset information + * @param Token[] $origTokens Tokens of the original code */ - public function printFormatPreserving(array $stmts, array $origStmts, array $origTokens) : string + public function printFormatPreserving(array $stmts, array $origStmts, array $origTokens): string { $this->initializeNodeListDiffer(); $this->initializeLabelCharMap(); @@ -23776,17 +23086,17 @@ abstract class PrettyPrinterAbstract $pos = 0; $result = $this->pArray($stmts, $origStmts, $pos, 0, 'File', 'stmts', null); if (null !== $result) { - $result .= $this->origTokens->getTokenCode($pos, \count($origTokens), 0); + $result .= $this->origTokens->getTokenCode($pos, count($origTokens) - 1, 0); } else { // Fallback // TODO Add pStmts($stmts, \false); + $result = "newline . $this->pStmts($stmts, \false); } - return \ltrim($this->handleMagicTokens($result)); + return $this->handleMagicTokens($result); } - protected function pFallback(Node $node) + protected function pFallback(Node $node, int $precedence, int $lhsPrecedence): string { - return $this->{'p' . $node->getType()}($node); + return $this->{'p' . $node->getType()}($node, $precedence, $lhsPrecedence); } /** * Pretty prints a node. @@ -23794,20 +23104,22 @@ abstract class PrettyPrinterAbstract * This method also handles formatting preservation for nodes. * * @param Node $node Node to be pretty printed + * @param int $precedence Precedence of parent operator + * @param int $lhsPrecedence Precedence for unary operator on LHS of binary operator * @param bool $parentFormatPreserved Whether parent node has preserved formatting * * @return string Pretty printed node */ - protected function p(Node $node, $parentFormatPreserved = \false) : string + protected function p(Node $node, int $precedence = self::MAX_PRECEDENCE, int $lhsPrecedence = self::MAX_PRECEDENCE, bool $parentFormatPreserved = \false): string { // No orig tokens means this is a normal pretty print without preservation of formatting if (!$this->origTokens) { - return $this->{'p' . $node->getType()}($node); + return $this->{'p' . $node->getType()}($node, $precedence, $lhsPrecedence); } - /** @var Node $origNode */ + /** @var Node|null $origNode */ $origNode = $node->getAttribute('origNode'); if (null === $origNode) { - return $this->pFallback($node); + return $this->pFallback($node, $precedence, $lhsPrecedence); } $class = \get_class($node); \assert($class === \get_class($origNode)); @@ -23817,14 +23129,16 @@ abstract class PrettyPrinterAbstract $fallbackNode = $node; if ($node instanceof Expr\New_ && $node->class instanceof Stmt\Class_) { // Normalize node structure of anonymous classes + assert($origNode instanceof Expr\New_); $node = PrintableNewAnonClassNode::fromNewNode($node); $origNode = PrintableNewAnonClassNode::fromNewNode($origNode); + $class = PrintableNewAnonClassNode::class; } // InlineHTML node does not contain closing and opening PHP tags. If the parent formatting // is not preserved, then we need to use the fallback code to make sure the tags are // printed. if ($node instanceof Stmt\InlineHTML && !$parentFormatPreserved) { - return $this->pFallback($fallbackNode); + return $this->pFallback($fallbackNode, $precedence, $lhsPrecedence); } $indentAdjustment = $this->indentLevel - $this->origTokens->getIndentationBefore($startPos); $type = $node->getType(); @@ -23839,30 +23153,24 @@ abstract class PrettyPrinterAbstract // Unchanged, can reuse old code continue; } - if (\is_array($subNode) && \is_array($origSubNode)) { + if (is_array($subNode) && is_array($origSubNode)) { // Array subnode changed, we might be able to reconstruct it - $listResult = $this->pArray($subNode, $origSubNode, $pos, $indentAdjustment, $type, $subNodeName, $fixupInfo[$subNodeName] ?? null); + $listResult = $this->pArray($subNode, $origSubNode, $pos, $indentAdjustment, $class, $subNodeName, $fixupInfo[$subNodeName] ?? null); if (null === $listResult) { - return $this->pFallback($fallbackNode); + return $this->pFallback($fallbackNode, $precedence, $lhsPrecedence); } $result .= $listResult; continue; } - if (\is_int($subNode) && \is_int($origSubNode)) { - // Check if this is a modifier change - $key = $type . '->' . $subNodeName; - if (!isset($this->modifierChangeMap[$key])) { - return $this->pFallback($fallbackNode); - } - $findToken = $this->modifierChangeMap[$key]; - $result .= $this->pModifiers($subNode); - $pos = $this->origTokens->findRight($pos, $findToken); - continue; + // Check if this is a modifier change + $key = $class . '->' . $subNodeName; + if (!isset($this->modifierChangeMap[$key])) { + return $this->pFallback($fallbackNode, $precedence, $lhsPrecedence); } - // If a non-node, non-array subnode changed, we don't be able to do a partial - // reconstructions, as we don't have enough offset information. Pretty print the - // whole node instead. - return $this->pFallback($fallbackNode); + [$printFn, $findToken] = $this->modifierChangeMap[$key]; + $result .= $this->{$printFn}($subNode); + $pos = $this->origTokens->findRight($pos, $findToken); + continue; } $extraLeft = ''; $extraRight = ''; @@ -23878,11 +23186,11 @@ abstract class PrettyPrinterAbstract // A node has been inserted, check if we have insertion information for it $key = $type . '->' . $subNodeName; if (!isset($this->insertionMap[$key])) { - return $this->pFallback($fallbackNode); + return $this->pFallback($fallbackNode, $precedence, $lhsPrecedence); } list($findToken, $beforeToken, $extraLeft, $extraRight) = $this->insertionMap[$key]; if (null !== $findToken) { - $subStartPos = $this->origTokens->findRight($pos, $findToken) + (int) (!$beforeToken); + $subStartPos = $this->origTokens->findRight($pos, $findToken) + (int) !$beforeToken; } else { $subStartPos = $pos; } @@ -23896,7 +23204,7 @@ abstract class PrettyPrinterAbstract // A node has been removed, check if we have removal information for it $key = $type . '->' . $subNodeName; if (!isset($this->removalMap[$key])) { - return $this->pFallback($fallbackNode); + return $this->pFallback($fallbackNode, $precedence, $lhsPrecedence); } // Adjust positions to account for additional tokens that must be skipped $removalInfo = $this->removalMap[$key]; @@ -23919,7 +23227,7 @@ abstract class PrettyPrinterAbstract $fixup = $fixupInfo[$subNodeName]; $res = $this->pFixup($fixup, $subNode, $class, $subStartPos, $subEndPos); } else { - $res = $this->p($subNode, \true); + $res = $this->p($subNode, self::MAX_PRECEDENCE, self::MAX_PRECEDENCE, \true); } $this->safeAppend($result, $res); $this->setIndentLevel($origIndentLevel); @@ -23933,20 +23241,20 @@ abstract class PrettyPrinterAbstract /** * Perform a format-preserving pretty print of an array. * - * @param array $nodes New nodes - * @param array $origNodes Original nodes - * @param int $pos Current token position (updated by reference) - * @param int $indentAdjustment Adjustment for indentation - * @param string $parentNodeType Type of the containing node. - * @param string $subNodeName Name of array subnode. - * @param null|int $fixup Fixup information for array item nodes + * @param Node[] $nodes New nodes + * @param Node[] $origNodes Original nodes + * @param int $pos Current token position (updated by reference) + * @param int $indentAdjustment Adjustment for indentation + * @param string $parentNodeClass Class of the containing node. + * @param string $subNodeName Name of array subnode. + * @param null|int $fixup Fixup information for array item nodes * * @return null|string Result of pretty print or null if cannot preserve formatting */ - protected function pArray(array $nodes, array $origNodes, int &$pos, int $indentAdjustment, string $parentNodeType, string $subNodeName, $fixup) + protected function pArray(array $nodes, array $origNodes, int &$pos, int $indentAdjustment, string $parentNodeClass, string $subNodeName, ?int $fixup): ?string { $diff = $this->nodeListDiffer->diffWithReplacements($origNodes, $nodes); - $mapKey = $parentNodeType . '->' . $subNodeName; + $mapKey = $parentNodeClass . '->' . $subNodeName; $insertStr = $this->listInsertionMap[$mapKey] ?? null; $isStmtList = $subNodeName === 'stmts'; $beforeFirstKeepOrReplace = \true; @@ -23973,9 +23281,9 @@ abstract class PrettyPrinterAbstract $result = ''; foreach ($diff as $i => $diffElem) { $diffType = $diffElem->type; - /** @var Node|null $arrItem */ + /** @var Node|string|null $arrItem */ $arrItem = $diffElem->new; - /** @var Node|null $origArrItem */ + /** @var Node|string|null $origArrItem */ $origArrItem = $diffElem->old; if ($diffType === DiffElem::TYPE_KEEP || $diffType === DiffElem::TYPE_REPLACE) { $beforeFirstKeepOrReplace = \false; @@ -24006,8 +23314,8 @@ abstract class PrettyPrinterAbstract $commentStartPos = $itemStartPos; } if ($skipRemovedNode) { - if ($isStmtList && ($this->origTokens->haveBracesInRange($pos, $itemStartPos) || $this->origTokens->haveTagInRange($pos, $itemStartPos))) { - // We'd remove the brace of a code block. + if ($isStmtList && $this->origTokens->haveTagInRange($pos, $itemStartPos)) { + // We'd remove an opening/closing PHP tag. // TODO: Preserve formatting. $this->setIndentLevel($origIndentLevel); return null; @@ -24024,7 +23332,7 @@ abstract class PrettyPrinterAbstract $result .= $this->pComments($delayedAddComments) . $this->nl; } } - $this->safeAppend($result, $this->p($delayedAddNode, \true)); + $this->safeAppend($result, $this->p($delayedAddNode, self::MAX_PRECEDENCE, self::MAX_PRECEDENCE, \true)); if ($insertNewline) { $result .= $insertStr . $this->nl; } else { @@ -24047,9 +23355,14 @@ abstract class PrettyPrinterAbstract // We don't have insertion information for this list type return null; } + if (!$arrItem instanceof Node) { + // We only support list insertion of nodes. + return null; + } // We go multiline if the original code was multiline, // or if it's an array item with a comment above it. - if ($insertStr === ', ' && ($this->isMultiline($origNodes) || $arrItem->getComments())) { + // Match always uses multiline formatting. + if ($insertStr === ', ' && ($this->isMultiline($origNodes) || $arrItem->getComments() || $parentNodeClass === Expr\Match_::class)) { $insertStr = ','; $insertNewline = \true; } @@ -24089,12 +23402,10 @@ abstract class PrettyPrinterAbstract // instead of the other way around. $result .= $this->origTokens->getTokenCode($pos, $itemStartPos, $indentAdjustment); $skipRemovedNode = \true; - } else { - if ($isStmtList && ($this->origTokens->haveBracesInRange($pos, $itemStartPos) || $this->origTokens->haveTagInRange($pos, $itemStartPos))) { - // We'd remove the brace of a code block. - // TODO: Preserve formatting. - return null; - } + } else if ($isStmtList && $this->origTokens->haveTagInRange($pos, $itemStartPos)) { + // We'd remove an opening/closing PHP tag. + // TODO: Preserve formatting. + return null; } $pos = $itemEndPos + 1; continue; @@ -24104,7 +23415,7 @@ abstract class PrettyPrinterAbstract if (null !== $fixup && $arrItem->getAttribute('origNode') !== $origArrItem) { $res = $this->pFixup($fixup, $arrItem, null, $itemStartPos, $itemEndPos); } else { - $res = $this->p($arrItem, \true); + $res = $this->p($arrItem, self::MAX_PRECEDENCE, self::MAX_PRECEDENCE, \true); } $this->safeAppend($result, $res); $this->setIndentLevel($origIndentLevel); @@ -24133,10 +23444,10 @@ abstract class PrettyPrinterAbstract $result .= $this->nl; } } - $result .= $this->p($delayedAddNode, \true); + $result .= $this->p($delayedAddNode, self::MAX_PRECEDENCE, self::MAX_PRECEDENCE, \true); $first = \false; } - $result .= $extraRight === "\n" ? $this->nl : $extraRight; + $result .= ($extraRight === "\n") ? $this->nl : $extraRight; } return $result; } @@ -24147,22 +23458,34 @@ abstract class PrettyPrinterAbstract * are required to preserve program semantics in a certain context (e.g. to maintain precedence * or because only certain expressions are allowed in certain places). * - * @param int $fixup Fixup type - * @param Node $subNode Subnode to print + * @param int $fixup Fixup type + * @param Node $subNode Subnode to print * @param string|null $parentClass Class of parent node - * @param int $subStartPos Original start pos of subnode - * @param int $subEndPos Original end pos of subnode + * @param int $subStartPos Original start pos of subnode + * @param int $subEndPos Original end pos of subnode * * @return string Result of fixed-up print of subnode */ - protected function pFixup(int $fixup, Node $subNode, $parentClass, int $subStartPos, int $subEndPos) : string + protected function pFixup(int $fixup, Node $subNode, ?string $parentClass, int $subStartPos, int $subEndPos): string { switch ($fixup) { case self::FIXUP_PREC_LEFT: + // We use a conservative approximation where lhsPrecedence == precedence. + if (!$this->origTokens->haveParens($subStartPos, $subEndPos)) { + $precedence = $this->precedenceMap[$parentClass][1]; + return $this->p($subNode, $precedence, $precedence); + } + break; case self::FIXUP_PREC_RIGHT: if (!$this->origTokens->haveParens($subStartPos, $subEndPos)) { - list($precedence, $associativity) = $this->precedenceMap[$parentClass]; - return $this->pPrec($subNode, $precedence, $associativity, $fixup === self::FIXUP_PREC_LEFT ? -1 : 1); + $precedence = $this->precedenceMap[$parentClass][2]; + return $this->p($subNode, $precedence, $precedence); + } + break; + case self::FIXUP_PREC_UNARY: + if (!$this->origTokens->haveParens($subStartPos, $subEndPos)) { + $precedence = $this->precedenceMap[$parentClass][0]; + return $this->p($subNode, $precedence, $precedence); } break; case self::FIXUP_CALL_LHS: @@ -24188,11 +23511,11 @@ abstract class PrettyPrinterAbstract case self::FIXUP_BRACED_NAME: case self::FIXUP_VAR_BRACED_NAME: if ($subNode instanceof Expr && !$this->origTokens->haveBraces($subStartPos, $subEndPos)) { - return ($fixup === self::FIXUP_VAR_BRACED_NAME ? '$' : '') . '{' . $this->p($subNode) . '}'; + return (($fixup === self::FIXUP_VAR_BRACED_NAME) ? '$' : '') . '{' . $this->p($subNode) . '}'; } break; case self::FIXUP_ENCAPSED: - if (!$subNode instanceof Scalar\EncapsedStringPart && !$this->origTokens->haveBraces($subStartPos, $subEndPos)) { + if (!$subNode instanceof Node\InterpolatedStringPart && !$this->origTokens->haveBraces($subStartPos, $subEndPos)) { return '{' . $this->p($subNode) . '}'; } break; @@ -24207,11 +23530,8 @@ abstract class PrettyPrinterAbstract * * Example: "echo" and "$x" result in "echo$x", but "echo" and "x" result in "echo x". * Without safeAppend the result would be "echox", which does not preserve semantics. - * - * @param string $str - * @param string $append */ - protected function safeAppend(string &$str, string $append) + protected function safeAppend(string &$str, string $append): void { if ($str === "") { $str = $append; @@ -24233,7 +23553,7 @@ abstract class PrettyPrinterAbstract * * @return bool Whether parentheses are required */ - protected function callLhsRequiresParens(Node $node) : bool + protected function callLhsRequiresParens(Node $node): bool { return !($node instanceof Node\Name || $node instanceof Expr\Variable || $node instanceof Expr\ArrayDimFetch || $node instanceof Expr\FuncCall || $node instanceof Expr\MethodCall || $node instanceof Expr\NullsafeMethodCall || $node instanceof Expr\StaticCall || $node instanceof Expr\Array_); } @@ -24244,7 +23564,7 @@ abstract class PrettyPrinterAbstract * * @return bool Whether parentheses are required */ - protected function dereferenceLhsRequiresParens(Node $node) : bool + protected function dereferenceLhsRequiresParens(Node $node): bool { // A constant can occur on the LHS of an array/object deref, but not a static deref. return $this->staticDereferenceLhsRequiresParens($node) && !$node instanceof Expr\ConstFetch; @@ -24256,7 +23576,7 @@ abstract class PrettyPrinterAbstract * * @return bool Whether parentheses are required */ - protected function staticDereferenceLhsRequiresParens(Node $node) : bool + protected function staticDereferenceLhsRequiresParens(Node $node): bool { return !($node instanceof Expr\Variable || $node instanceof Node\Name || $node instanceof Expr\ArrayDimFetch || $node instanceof Expr\PropertyFetch || $node instanceof Expr\NullsafePropertyFetch || $node instanceof Expr\StaticPropertyFetch || $node instanceof Expr\FuncCall || $node instanceof Expr\MethodCall || $node instanceof Expr\NullsafeMethodCall || $node instanceof Expr\StaticCall || $node instanceof Expr\Array_ || $node instanceof Scalar\String_ || $node instanceof Expr\ClassConstFetch); } @@ -24267,7 +23587,7 @@ abstract class PrettyPrinterAbstract * * @return bool Whether parentheses are required */ - protected function newOperandRequiresParens(Node $node) : bool + protected function newOperandRequiresParens(Node $node): bool { if ($node instanceof Node\Name || $node instanceof Expr\Variable) { return \false; @@ -24287,9 +23607,13 @@ abstract class PrettyPrinterAbstract * * @return string Printed modifiers */ - protected function pModifiers(int $modifiers) + protected function pModifiers(int $modifiers): string + { + return (($modifiers & Modifiers::FINAL) ? 'final ' : '') . (($modifiers & Modifiers::ABSTRACT) ? 'abstract ' : '') . (($modifiers & Modifiers::PUBLIC) ? 'public ' : '') . (($modifiers & Modifiers::PROTECTED) ? 'protected ' : '') . (($modifiers & Modifiers::PRIVATE) ? 'private ' : '') . (($modifiers & Modifiers::STATIC) ? 'static ' : '') . (($modifiers & Modifiers::READONLY) ? 'readonly ' : ''); + } + protected function pStatic(bool $static): string { - return ($modifiers & Stmt\Class_::MODIFIER_PUBLIC ? 'public ' : '') . ($modifiers & Stmt\Class_::MODIFIER_PROTECTED ? 'protected ' : '') . ($modifiers & Stmt\Class_::MODIFIER_PRIVATE ? 'private ' : '') . ($modifiers & Stmt\Class_::MODIFIER_STATIC ? 'static ' : '') . ($modifiers & Stmt\Class_::MODIFIER_ABSTRACT ? 'abstract ' : '') . ($modifiers & Stmt\Class_::MODIFIER_FINAL ? 'final ' : '') . ($modifiers & Stmt\Class_::MODIFIER_READONLY ? 'readonly ' : ''); + return $static ? 'static ' : ''; } /** * Determine whether a list of nodes uses multiline formatting. @@ -24298,7 +23622,7 @@ abstract class PrettyPrinterAbstract * * @return bool Whether multiline formatting is used */ - protected function isMultiline(array $nodes) : bool + protected function isMultiline(array $nodes): bool { if (\count($nodes) < 2) { return \false; @@ -24311,7 +23635,7 @@ abstract class PrettyPrinterAbstract $endPos = $node->getEndTokenPos() + 1; if ($pos >= 0) { $text = $this->origTokens->getTokenCode($pos, $endPos, 0); - if (\false === \strpos($text, "\n")) { + if (\false === strpos($text, "\n")) { // We require that a newline is present between *every* item. If the formatting // is inconsistent, with only some items having newlines, we don't consider it // as multiline @@ -24327,17 +23651,18 @@ abstract class PrettyPrinterAbstract * * The label char map determines whether a certain character may occur in a label. */ - protected function initializeLabelCharMap() + protected function initializeLabelCharMap(): void { - if ($this->labelCharMap) { + if (isset($this->labelCharMap)) { return; } $this->labelCharMap = []; for ($i = 0; $i < 256; $i++) { - // Since PHP 7.1 The lower range is 0x80. However, we also want to support code for - // older versions. - $chr = \chr($i); - $this->labelCharMap[$chr] = $i >= 0x7f || \ctype_alnum($chr); + $chr = chr($i); + $this->labelCharMap[$chr] = $i >= 0x80 || ctype_alnum($chr); + } + if ($this->phpVersion->allowsDelInIdentifiers()) { + $this->labelCharMap[""] = \true; } } /** @@ -24345,9 +23670,9 @@ abstract class PrettyPrinterAbstract * * The node list differ is used to determine differences between two array subnodes. */ - protected function initializeNodeListDiffer() + protected function initializeNodeListDiffer(): void { - if ($this->nodeListDiffer) { + if (isset($this->nodeListDiffer)) { return; } $this->nodeListDiffer = new Internal\Differ(function ($a, $b) { @@ -24364,23 +23689,19 @@ abstract class PrettyPrinterAbstract * The fixup map is used to determine whether a certain subnode of a certain node may require * some kind of "fixup" operation, e.g. the addition of parenthesis or braces. */ - protected function initializeFixupMap() + protected function initializeFixupMap(): void { - if ($this->fixupMap) { + if (isset($this->fixupMap)) { return; } - $this->fixupMap = [Expr\PreInc::class => ['var' => self::FIXUP_PREC_RIGHT], Expr\PreDec::class => ['var' => self::FIXUP_PREC_RIGHT], Expr\PostInc::class => ['var' => self::FIXUP_PREC_LEFT], Expr\PostDec::class => ['var' => self::FIXUP_PREC_LEFT], Expr\Instanceof_::class => ['expr' => self::FIXUP_PREC_LEFT, 'class' => self::FIXUP_NEW], Expr\Ternary::class => ['cond' => self::FIXUP_PREC_LEFT, 'else' => self::FIXUP_PREC_RIGHT], Expr\FuncCall::class => ['name' => self::FIXUP_CALL_LHS], Expr\StaticCall::class => ['class' => self::FIXUP_STATIC_DEREF_LHS], Expr\ArrayDimFetch::class => ['var' => self::FIXUP_DEREF_LHS], Expr\ClassConstFetch::class => ['class' => self::FIXUP_STATIC_DEREF_LHS, 'name' => self::FIXUP_BRACED_NAME], Expr\New_::class => ['class' => self::FIXUP_NEW], Expr\MethodCall::class => ['var' => self::FIXUP_DEREF_LHS, 'name' => self::FIXUP_BRACED_NAME], Expr\NullsafeMethodCall::class => ['var' => self::FIXUP_DEREF_LHS, 'name' => self::FIXUP_BRACED_NAME], Expr\StaticPropertyFetch::class => ['class' => self::FIXUP_STATIC_DEREF_LHS, 'name' => self::FIXUP_VAR_BRACED_NAME], Expr\PropertyFetch::class => ['var' => self::FIXUP_DEREF_LHS, 'name' => self::FIXUP_BRACED_NAME], Expr\NullsafePropertyFetch::class => ['var' => self::FIXUP_DEREF_LHS, 'name' => self::FIXUP_BRACED_NAME], Scalar\Encapsed::class => ['parts' => self::FIXUP_ENCAPSED]]; + $this->fixupMap = [Expr\Instanceof_::class => ['expr' => self::FIXUP_PREC_UNARY, 'class' => self::FIXUP_NEW], Expr\Ternary::class => ['cond' => self::FIXUP_PREC_LEFT, 'else' => self::FIXUP_PREC_RIGHT], Expr\Yield_::class => ['value' => self::FIXUP_PREC_UNARY], Expr\FuncCall::class => ['name' => self::FIXUP_CALL_LHS], Expr\StaticCall::class => ['class' => self::FIXUP_STATIC_DEREF_LHS], Expr\ArrayDimFetch::class => ['var' => self::FIXUP_DEREF_LHS], Expr\ClassConstFetch::class => ['class' => self::FIXUP_STATIC_DEREF_LHS, 'name' => self::FIXUP_BRACED_NAME], Expr\New_::class => ['class' => self::FIXUP_NEW], Expr\MethodCall::class => ['var' => self::FIXUP_DEREF_LHS, 'name' => self::FIXUP_BRACED_NAME], Expr\NullsafeMethodCall::class => ['var' => self::FIXUP_DEREF_LHS, 'name' => self::FIXUP_BRACED_NAME], Expr\StaticPropertyFetch::class => ['class' => self::FIXUP_STATIC_DEREF_LHS, 'name' => self::FIXUP_VAR_BRACED_NAME], Expr\PropertyFetch::class => ['var' => self::FIXUP_DEREF_LHS, 'name' => self::FIXUP_BRACED_NAME], Expr\NullsafePropertyFetch::class => ['var' => self::FIXUP_DEREF_LHS, 'name' => self::FIXUP_BRACED_NAME], Scalar\InterpolatedString::class => ['parts' => self::FIXUP_ENCAPSED]]; $binaryOps = [BinaryOp\Pow::class, BinaryOp\Mul::class, BinaryOp\Div::class, BinaryOp\Mod::class, BinaryOp\Plus::class, BinaryOp\Minus::class, BinaryOp\Concat::class, BinaryOp\ShiftLeft::class, BinaryOp\ShiftRight::class, BinaryOp\Smaller::class, BinaryOp\SmallerOrEqual::class, BinaryOp\Greater::class, BinaryOp\GreaterOrEqual::class, BinaryOp\Equal::class, BinaryOp\NotEqual::class, BinaryOp\Identical::class, BinaryOp\NotIdentical::class, BinaryOp\Spaceship::class, BinaryOp\BitwiseAnd::class, BinaryOp\BitwiseXor::class, BinaryOp\BitwiseOr::class, BinaryOp\BooleanAnd::class, BinaryOp\BooleanOr::class, BinaryOp\Coalesce::class, BinaryOp\LogicalAnd::class, BinaryOp\LogicalXor::class, BinaryOp\LogicalOr::class]; foreach ($binaryOps as $binaryOp) { $this->fixupMap[$binaryOp] = ['left' => self::FIXUP_PREC_LEFT, 'right' => self::FIXUP_PREC_RIGHT]; } - $assignOps = [Expr\Assign::class, Expr\AssignRef::class, AssignOp\Plus::class, AssignOp\Minus::class, AssignOp\Mul::class, AssignOp\Div::class, AssignOp\Concat::class, AssignOp\Mod::class, AssignOp\BitwiseAnd::class, AssignOp\BitwiseOr::class, AssignOp\BitwiseXor::class, AssignOp\ShiftLeft::class, AssignOp\ShiftRight::class, AssignOp\Pow::class, AssignOp\Coalesce::class]; - foreach ($assignOps as $assignOp) { - $this->fixupMap[$assignOp] = ['var' => self::FIXUP_PREC_LEFT, 'expr' => self::FIXUP_PREC_RIGHT]; - } - $prefixOps = [Expr\BitwiseNot::class, Expr\BooleanNot::class, Expr\UnaryPlus::class, Expr\UnaryMinus::class, Cast\Int_::class, Cast\Double::class, Cast\String_::class, Cast\Array_::class, Cast\Object_::class, Cast\Bool_::class, Cast\Unset_::class, Expr\ErrorSuppress::class, Expr\YieldFrom::class, Expr\Print_::class, Expr\Include_::class]; + $prefixOps = [Expr\Clone_::class, Expr\BitwiseNot::class, Expr\BooleanNot::class, Expr\UnaryPlus::class, Expr\UnaryMinus::class, Cast\Int_::class, Cast\Double::class, Cast\String_::class, Cast\Array_::class, Cast\Object_::class, Cast\Bool_::class, Cast\Unset_::class, Expr\ErrorSuppress::class, Expr\YieldFrom::class, Expr\Print_::class, Expr\Include_::class, Expr\Assign::class, Expr\AssignRef::class, AssignOp\Plus::class, AssignOp\Minus::class, AssignOp\Mul::class, AssignOp\Div::class, AssignOp\Concat::class, AssignOp\Mod::class, AssignOp\BitwiseAnd::class, AssignOp\BitwiseOr::class, AssignOp\BitwiseXor::class, AssignOp\ShiftLeft::class, AssignOp\ShiftRight::class, AssignOp\Pow::class, AssignOp\Coalesce::class, Expr\ArrowFunction::class, Expr\Throw_::class]; foreach ($prefixOps as $prefixOp) { - $this->fixupMap[$prefixOp] = ['expr' => self::FIXUP_PREC_RIGHT]; + $this->fixupMap[$prefixOp] = ['expr' => self::FIXUP_PREC_UNARY]; } } /** @@ -24389,9 +23710,9 @@ abstract class PrettyPrinterAbstract * The removal map is used to determine which additional tokens should be removed when a * certain node is replaced by null. */ - protected function initializeRemovalMap() + protected function initializeRemovalMap(): void { - if ($this->removalMap) { + if (isset($this->removalMap)) { return; } $stripBoth = ['left' => \T_WHITESPACE, 'right' => \T_WHITESPACE]; @@ -24400,20 +23721,20 @@ abstract class PrettyPrinterAbstract $stripDoubleArrow = ['right' => \T_DOUBLE_ARROW]; $stripColon = ['left' => ':']; $stripEquals = ['left' => '=']; - $this->removalMap = ['Expr_ArrayDimFetch->dim' => $stripBoth, 'Expr_ArrayItem->key' => $stripDoubleArrow, 'Expr_ArrowFunction->returnType' => $stripColon, 'Expr_Closure->returnType' => $stripColon, 'Expr_Exit->expr' => $stripBoth, 'Expr_Ternary->if' => $stripBoth, 'Expr_Yield->key' => $stripDoubleArrow, 'Expr_Yield->value' => $stripBoth, 'Param->type' => $stripRight, 'Param->default' => $stripEquals, 'Stmt_Break->num' => $stripBoth, 'Stmt_Catch->var' => $stripLeft, 'Stmt_ClassConst->type' => $stripRight, 'Stmt_ClassMethod->returnType' => $stripColon, 'Stmt_Class->extends' => ['left' => \T_EXTENDS], 'Stmt_Enum->scalarType' => $stripColon, 'Stmt_EnumCase->expr' => $stripEquals, 'Expr_PrintableNewAnonClass->extends' => ['left' => \T_EXTENDS], 'Stmt_Continue->num' => $stripBoth, 'Stmt_Foreach->keyVar' => $stripDoubleArrow, 'Stmt_Function->returnType' => $stripColon, 'Stmt_If->else' => $stripLeft, 'Stmt_Namespace->name' => $stripLeft, 'Stmt_Property->type' => $stripRight, 'Stmt_PropertyProperty->default' => $stripEquals, 'Stmt_Return->expr' => $stripBoth, 'Stmt_StaticVar->default' => $stripEquals, 'Stmt_TraitUseAdaptation_Alias->newName' => $stripLeft, 'Stmt_TryCatch->finally' => $stripLeft]; + $this->removalMap = ['Expr_ArrayDimFetch->dim' => $stripBoth, 'ArrayItem->key' => $stripDoubleArrow, 'Expr_ArrowFunction->returnType' => $stripColon, 'Expr_Closure->returnType' => $stripColon, 'Expr_Exit->expr' => $stripBoth, 'Expr_Ternary->if' => $stripBoth, 'Expr_Yield->key' => $stripDoubleArrow, 'Expr_Yield->value' => $stripBoth, 'Param->type' => $stripRight, 'Param->default' => $stripEquals, 'Stmt_Break->num' => $stripBoth, 'Stmt_Catch->var' => $stripLeft, 'Stmt_ClassConst->type' => $stripRight, 'Stmt_ClassMethod->returnType' => $stripColon, 'Stmt_Class->extends' => ['left' => \T_EXTENDS], 'Stmt_Enum->scalarType' => $stripColon, 'Stmt_EnumCase->expr' => $stripEquals, 'Expr_PrintableNewAnonClass->extends' => ['left' => \T_EXTENDS], 'Stmt_Continue->num' => $stripBoth, 'Stmt_Foreach->keyVar' => $stripDoubleArrow, 'Stmt_Function->returnType' => $stripColon, 'Stmt_If->else' => $stripLeft, 'Stmt_Namespace->name' => $stripLeft, 'Stmt_Property->type' => $stripRight, 'PropertyItem->default' => $stripEquals, 'Stmt_Return->expr' => $stripBoth, 'Stmt_StaticVar->default' => $stripEquals, 'Stmt_TraitUseAdaptation_Alias->newName' => $stripLeft, 'Stmt_TryCatch->finally' => $stripLeft]; } - protected function initializeInsertionMap() + protected function initializeInsertionMap(): void { - if ($this->insertionMap) { + if (isset($this->insertionMap)) { return; } // TODO: "yield" where both key and value are inserted doesn't work // [$find, $beforeToken, $extraLeft, $extraRight] $this->insertionMap = [ 'Expr_ArrayDimFetch->dim' => ['[', \false, null, null], - 'Expr_ArrayItem->key' => [null, \false, null, ' => '], - 'Expr_ArrowFunction->returnType' => [')', \false, ' : ', null], - 'Expr_Closure->returnType' => [')', \false, ' : ', null], + 'ArrayItem->key' => [null, \false, null, ' => '], + 'Expr_ArrowFunction->returnType' => [')', \false, ': ', null], + 'Expr_Closure->returnType' => [')', \false, ': ', null], 'Expr_Ternary->if' => ['?', \false, ' ', ' '], 'Expr_Yield->key' => [\T_YIELD, \false, null, ' => '], 'Expr_Yield->value' => [\T_YIELD, \false, ' ', null], @@ -24421,205 +23742,274 @@ abstract class PrettyPrinterAbstract 'Param->default' => [null, \false, ' = ', null], 'Stmt_Break->num' => [\T_BREAK, \false, ' ', null], 'Stmt_Catch->var' => [null, \false, ' ', null], - 'Stmt_ClassMethod->returnType' => [')', \false, ' : ', null], + 'Stmt_ClassMethod->returnType' => [')', \false, ': ', null], 'Stmt_ClassConst->type' => [\T_CONST, \false, ' ', null], 'Stmt_Class->extends' => [null, \false, ' extends ', null], 'Stmt_Enum->scalarType' => [null, \false, ' : ', null], 'Stmt_EnumCase->expr' => [null, \false, ' = ', null], - 'Expr_PrintableNewAnonClass->extends' => [null, ' extends ', null], + 'Expr_PrintableNewAnonClass->extends' => [null, \false, ' extends ', null], 'Stmt_Continue->num' => [\T_CONTINUE, \false, ' ', null], 'Stmt_Foreach->keyVar' => [\T_AS, \false, null, ' => '], - 'Stmt_Function->returnType' => [')', \false, ' : ', null], + 'Stmt_Function->returnType' => [')', \false, ': ', null], 'Stmt_If->else' => [null, \false, ' ', null], 'Stmt_Namespace->name' => [\T_NAMESPACE, \false, ' ', null], 'Stmt_Property->type' => [\T_VARIABLE, \true, null, ' '], - 'Stmt_PropertyProperty->default' => [null, \false, ' = ', null], + 'PropertyItem->default' => [null, \false, ' = ', null], 'Stmt_Return->expr' => [\T_RETURN, \false, ' ', null], 'Stmt_StaticVar->default' => [null, \false, ' = ', null], //'Stmt_TraitUseAdaptation_Alias->newName' => [T_AS, false, ' ', null], // TODO 'Stmt_TryCatch->finally' => [null, \false, ' ', null], ]; } - protected function initializeListInsertionMap() + protected function initializeListInsertionMap(): void { - if ($this->listInsertionMap) { + if (isset($this->listInsertionMap)) { return; } $this->listInsertionMap = [ // special //'Expr_ShellExec->parts' => '', // TODO These need to be treated more carefully - //'Scalar_Encapsed->parts' => '', - 'Stmt_Catch->types' => '|', - 'UnionType->types' => '|', - 'IntersectionType->types' => '&', - 'Stmt_If->elseifs' => ' ', - 'Stmt_TryCatch->catches' => ' ', + //'Scalar_InterpolatedString->parts' => '', + Stmt\Catch_::class . '->types' => '|', + UnionType::class . '->types' => '|', + IntersectionType::class . '->types' => '&', + Stmt\If_::class . '->elseifs' => ' ', + Stmt\TryCatch::class . '->catches' => ' ', // comma-separated lists - 'Expr_Array->items' => ', ', - 'Expr_ArrowFunction->params' => ', ', - 'Expr_Closure->params' => ', ', - 'Expr_Closure->uses' => ', ', - 'Expr_FuncCall->args' => ', ', - 'Expr_Isset->vars' => ', ', - 'Expr_List->items' => ', ', - 'Expr_MethodCall->args' => ', ', - 'Expr_NullsafeMethodCall->args' => ', ', - 'Expr_New->args' => ', ', - 'Expr_PrintableNewAnonClass->args' => ', ', - 'Expr_StaticCall->args' => ', ', - 'Stmt_ClassConst->consts' => ', ', - 'Stmt_ClassMethod->params' => ', ', - 'Stmt_Class->implements' => ', ', - 'Stmt_Enum->implements' => ', ', - 'Expr_PrintableNewAnonClass->implements' => ', ', - 'Stmt_Const->consts' => ', ', - 'Stmt_Declare->declares' => ', ', - 'Stmt_Echo->exprs' => ', ', - 'Stmt_For->init' => ', ', - 'Stmt_For->cond' => ', ', - 'Stmt_For->loop' => ', ', - 'Stmt_Function->params' => ', ', - 'Stmt_Global->vars' => ', ', - 'Stmt_GroupUse->uses' => ', ', - 'Stmt_Interface->extends' => ', ', - 'Stmt_Match->arms' => ', ', - 'Stmt_Property->props' => ', ', - 'Stmt_StaticVar->vars' => ', ', - 'Stmt_TraitUse->traits' => ', ', - 'Stmt_TraitUseAdaptation_Precedence->insteadof' => ', ', - 'Stmt_Unset->vars' => ', ', - 'Stmt_Use->uses' => ', ', - 'MatchArm->conds' => ', ', - 'AttributeGroup->attrs' => ', ', + Expr\Array_::class . '->items' => ', ', + Expr\ArrowFunction::class . '->params' => ', ', + Expr\Closure::class . '->params' => ', ', + Expr\Closure::class . '->uses' => ', ', + Expr\FuncCall::class . '->args' => ', ', + Expr\Isset_::class . '->vars' => ', ', + Expr\List_::class . '->items' => ', ', + Expr\MethodCall::class . '->args' => ', ', + Expr\NullsafeMethodCall::class . '->args' => ', ', + Expr\New_::class . '->args' => ', ', + PrintableNewAnonClassNode::class . '->args' => ', ', + Expr\StaticCall::class . '->args' => ', ', + Stmt\ClassConst::class . '->consts' => ', ', + Stmt\ClassMethod::class . '->params' => ', ', + Stmt\Class_::class . '->implements' => ', ', + Stmt\Enum_::class . '->implements' => ', ', + PrintableNewAnonClassNode::class . '->implements' => ', ', + Stmt\Const_::class . '->consts' => ', ', + Stmt\Declare_::class . '->declares' => ', ', + Stmt\Echo_::class . '->exprs' => ', ', + Stmt\For_::class . '->init' => ', ', + Stmt\For_::class . '->cond' => ', ', + Stmt\For_::class . '->loop' => ', ', + Stmt\Function_::class . '->params' => ', ', + Stmt\Global_::class . '->vars' => ', ', + Stmt\GroupUse::class . '->uses' => ', ', + Stmt\Interface_::class . '->extends' => ', ', + Expr\Match_::class . '->arms' => ', ', + Stmt\Property::class . '->props' => ', ', + Stmt\StaticVar::class . '->vars' => ', ', + Stmt\TraitUse::class . '->traits' => ', ', + Stmt\TraitUseAdaptation\Precedence::class . '->insteadof' => ', ', + Stmt\Unset_::class . '->vars' => ', ', + Stmt\UseUse::class . '->uses' => ', ', + MatchArm::class . '->conds' => ', ', + AttributeGroup::class . '->attrs' => ', ', // statement lists - 'Expr_Closure->stmts' => "\n", - 'Stmt_Case->stmts' => "\n", - 'Stmt_Catch->stmts' => "\n", - 'Stmt_Class->stmts' => "\n", - 'Stmt_Enum->stmts' => "\n", - 'Expr_PrintableNewAnonClass->stmts' => "\n", - 'Stmt_Interface->stmts' => "\n", - 'Stmt_Trait->stmts' => "\n", - 'Stmt_ClassMethod->stmts' => "\n", - 'Stmt_Declare->stmts' => "\n", - 'Stmt_Do->stmts' => "\n", - 'Stmt_ElseIf->stmts' => "\n", - 'Stmt_Else->stmts' => "\n", - 'Stmt_Finally->stmts' => "\n", - 'Stmt_Foreach->stmts' => "\n", - 'Stmt_For->stmts' => "\n", - 'Stmt_Function->stmts' => "\n", - 'Stmt_If->stmts' => "\n", - 'Stmt_Namespace->stmts' => "\n", - 'Stmt_Class->attrGroups' => "\n", - 'Stmt_Enum->attrGroups' => "\n", - 'Stmt_EnumCase->attrGroups' => "\n", - 'Stmt_Interface->attrGroups' => "\n", - 'Stmt_Trait->attrGroups' => "\n", - 'Stmt_Function->attrGroups' => "\n", - 'Stmt_ClassMethod->attrGroups' => "\n", - 'Stmt_ClassConst->attrGroups' => "\n", - 'Stmt_Property->attrGroups' => "\n", - 'Expr_PrintableNewAnonClass->attrGroups' => ' ', - 'Expr_Closure->attrGroups' => ' ', - 'Expr_ArrowFunction->attrGroups' => ' ', - 'Param->attrGroups' => ' ', - 'Stmt_Switch->cases' => "\n", - 'Stmt_TraitUse->adaptations' => "\n", - 'Stmt_TryCatch->stmts' => "\n", - 'Stmt_While->stmts' => "\n", + Expr\Closure::class . '->stmts' => "\n", + Stmt\Case_::class . '->stmts' => "\n", + Stmt\Catch_::class . '->stmts' => "\n", + Stmt\Class_::class . '->stmts' => "\n", + Stmt\Enum_::class . '->stmts' => "\n", + PrintableNewAnonClassNode::class . '->stmts' => "\n", + Stmt\Interface_::class . '->stmts' => "\n", + Stmt\Trait_::class . '->stmts' => "\n", + Stmt\ClassMethod::class . '->stmts' => "\n", + Stmt\Declare_::class . '->stmts' => "\n", + Stmt\Do_::class . '->stmts' => "\n", + Stmt\ElseIf_::class . '->stmts' => "\n", + Stmt\Else_::class . '->stmts' => "\n", + Stmt\Finally_::class . '->stmts' => "\n", + Stmt\Foreach_::class . '->stmts' => "\n", + Stmt\For_::class . '->stmts' => "\n", + Stmt\Function_::class . '->stmts' => "\n", + Stmt\If_::class . '->stmts' => "\n", + Stmt\Namespace_::class . '->stmts' => "\n", + Stmt\Block::class . '->stmts' => "\n", + // Attribute groups + Stmt\Class_::class . '->attrGroups' => "\n", + Stmt\Enum_::class . '->attrGroups' => "\n", + Stmt\EnumCase::class . '->attrGroups' => "\n", + Stmt\Interface_::class . '->attrGroups' => "\n", + Stmt\Trait_::class . '->attrGroups' => "\n", + Stmt\Function_::class . '->attrGroups' => "\n", + Stmt\ClassMethod::class . '->attrGroups' => "\n", + Stmt\ClassConst::class . '->attrGroups' => "\n", + Stmt\Property::class . '->attrGroups' => "\n", + PrintableNewAnonClassNode::class . '->attrGroups' => ' ', + Expr\Closure::class . '->attrGroups' => ' ', + Expr\ArrowFunction::class . '->attrGroups' => ' ', + Param::class . '->attrGroups' => ' ', + Stmt\Switch_::class . '->cases' => "\n", + Stmt\TraitUse::class . '->adaptations' => "\n", + Stmt\TryCatch::class . '->stmts' => "\n", + Stmt\While_::class . '->stmts' => "\n", // dummy for top-level context 'File->stmts' => "\n", ]; } - protected function initializeEmptyListInsertionMap() + protected function initializeEmptyListInsertionMap(): void { - if ($this->emptyListInsertionMap) { + if (isset($this->emptyListInsertionMap)) { return; } // TODO Insertion into empty statement lists. // [$find, $extraLeft, $extraRight] - $this->emptyListInsertionMap = ['Expr_ArrowFunction->params' => ['(', '', ''], 'Expr_Closure->uses' => [')', ' use(', ')'], 'Expr_Closure->params' => ['(', '', ''], 'Expr_FuncCall->args' => ['(', '', ''], 'Expr_MethodCall->args' => ['(', '', ''], 'Expr_NullsafeMethodCall->args' => ['(', '', ''], 'Expr_New->args' => ['(', '', ''], 'Expr_PrintableNewAnonClass->args' => ['(', '', ''], 'Expr_PrintableNewAnonClass->implements' => [null, ' implements ', ''], 'Expr_StaticCall->args' => ['(', '', ''], 'Stmt_Class->implements' => [null, ' implements ', ''], 'Stmt_Enum->implements' => [null, ' implements ', ''], 'Stmt_ClassMethod->params' => ['(', '', ''], 'Stmt_Interface->extends' => [null, ' extends ', ''], 'Stmt_Function->params' => ['(', '', ''], 'Stmt_Interface->attrGroups' => [null, '', "\n"], 'Stmt_Class->attrGroups' => [null, '', "\n"], 'Stmt_ClassConst->attrGroups' => [null, '', "\n"], 'Stmt_ClassMethod->attrGroups' => [null, '', "\n"], 'Stmt_Function->attrGroups' => [null, '', "\n"], 'Stmt_Property->attrGroups' => [null, '', "\n"], 'Stmt_Trait->attrGroups' => [null, '', "\n"], 'Expr_ArrowFunction->attrGroups' => [null, '', ' '], 'Expr_Closure->attrGroups' => [null, '', ' '], 'Expr_PrintableNewAnonClass->attrGroups' => [\T_NEW, ' ', '']]; + $this->emptyListInsertionMap = [Expr\ArrowFunction::class . '->params' => ['(', '', ''], Expr\Closure::class . '->uses' => [')', ' use (', ')'], Expr\Closure::class . '->params' => ['(', '', ''], Expr\FuncCall::class . '->args' => ['(', '', ''], Expr\MethodCall::class . '->args' => ['(', '', ''], Expr\NullsafeMethodCall::class . '->args' => ['(', '', ''], Expr\New_::class . '->args' => ['(', '', ''], PrintableNewAnonClassNode::class . '->args' => ['(', '', ''], PrintableNewAnonClassNode::class . '->implements' => [null, ' implements ', ''], Expr\StaticCall::class . '->args' => ['(', '', ''], Stmt\Class_::class . '->implements' => [null, ' implements ', ''], Stmt\Enum_::class . '->implements' => [null, ' implements ', ''], Stmt\ClassMethod::class . '->params' => ['(', '', ''], Stmt\Interface_::class . '->extends' => [null, ' extends ', ''], Stmt\Function_::class . '->params' => ['(', '', ''], Stmt\Interface_::class . '->attrGroups' => [null, '', "\n"], Stmt\Class_::class . '->attrGroups' => [null, '', "\n"], Stmt\ClassConst::class . '->attrGroups' => [null, '', "\n"], Stmt\ClassMethod::class . '->attrGroups' => [null, '', "\n"], Stmt\Function_::class . '->attrGroups' => [null, '', "\n"], Stmt\Property::class . '->attrGroups' => [null, '', "\n"], Stmt\Trait_::class . '->attrGroups' => [null, '', "\n"], Expr\ArrowFunction::class . '->attrGroups' => [null, '', ' '], Expr\Closure::class . '->attrGroups' => [null, '', ' '], PrintableNewAnonClassNode::class . '->attrGroups' => [\T_NEW, ' ', '']]; } - protected function initializeModifierChangeMap() + protected function initializeModifierChangeMap(): void { - if ($this->modifierChangeMap) { + if (isset($this->modifierChangeMap)) { return; } - $this->modifierChangeMap = ['Stmt_ClassConst->flags' => \T_CONST, 'Stmt_ClassMethod->flags' => \T_FUNCTION, 'Stmt_Class->flags' => \T_CLASS, 'Stmt_Property->flags' => \T_VARIABLE, 'Expr_PrintableNewAnonClass->flags' => \T_CLASS, 'Param->flags' => \T_VARIABLE]; + $this->modifierChangeMap = [Stmt\ClassConst::class . '->flags' => ['pModifiers', \T_CONST], Stmt\ClassMethod::class . '->flags' => ['pModifiers', \T_FUNCTION], Stmt\Class_::class . '->flags' => ['pModifiers', \T_CLASS], Stmt\Property::class . '->flags' => ['pModifiers', \T_VARIABLE], PrintableNewAnonClassNode::class . '->flags' => ['pModifiers', \T_CLASS], Param::class . '->flags' => ['pModifiers', \T_VARIABLE], Expr\Closure::class . '->static' => ['pStatic', \T_FUNCTION], Expr\ArrowFunction::class . '->static' => ['pStatic', \T_FN]]; // List of integer subnodes that are not modifiers: // Expr_Include->type // Stmt_GroupUse->type // Stmt_Use->type - // Stmt_UseUse->type + // UseItem->type + } +} +pos + \strlen($this->text); + } + /** Get 1-based end line number of the token. */ + public function getEndLine(): int + { + return $this->line + \substr_count($this->text, "\n"); } } -Object Enumerator +. +declare (strict_types=1); +namespace PHPUnitPHAR\PhpParser; + +if (!\function_exists('PHPUnitPHAR\PhpParser\defineCompatibilityTokens')) { + function defineCompatibilityTokens(): void + { + $compatTokens = [ + // PHP 8.0 + 'T_NAME_QUALIFIED', + 'T_NAME_FULLY_QUALIFIED', + 'T_NAME_RELATIVE', + 'T_MATCH', + 'T_NULLSAFE_OBJECT_OPERATOR', + 'T_ATTRIBUTE', + // PHP 8.1 + 'T_ENUM', + 'T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG', + 'T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG', + 'T_READONLY', + ]; + // PHP-Parser might be used together with another library that also emulates some or all + // of these tokens. Perform a sanity-check that all already defined tokens have been + // assigned a unique ID. + $usedTokenIds = []; + foreach ($compatTokens as $token) { + if (\defined($token)) { + $tokenId = \constant($token); + if (!\is_int($tokenId)) { + throw new \Error(sprintf('Token %s has ID of type %s, should be int. ' . 'You may be using a library with broken token emulation', $token, \gettype($tokenId))); + } + $clashingToken = $usedTokenIds[$tokenId] ?? null; + if ($clashingToken !== null) { + throw new \Error(sprintf('Token %s has same ID as token %s, ' . 'you may be using a library with broken token emulation', $token, $clashingToken)); + } + $usedTokenIds[$tokenId] = $token; + } + } + // Now define any tokens that have not yet been emulated. Try to assign IDs from -1 + // downwards, but skip any IDs that may already be in use. + $newTokenId = -1; + foreach ($compatTokens as $token) { + if (!\defined($token)) { + while (isset($usedTokenIds[$newTokenId])) { + $newTokenId--; + } + \define($token, $newTokenId); + $newTokenId--; + } + } + } + defineCompatibilityTokens(); +} +BSD 3-Clause License + +Copyright (c) 2016-2023, Sebastian Bergmann All rights reserved. Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the - distribution. - - * Neither the name of Sebastian Bergmann nor the names of his - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN -ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. -Object Reflector +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +BSD 3-Clause License -Copyright (c) 2017-2020, Sebastian Bergmann . +Copyright (c) 2017-2023, Sebastian Bergmann All rights reserved. Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the - distribution. - - * Neither the name of Sebastian Bergmann nor the names of his - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN -ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. Phar.io - Manifest Copyright (c) 2016-2019 Arne Blankerts , Sebastian Heuer , Sebastian Bergmann , and contributors @@ -24672,7 +24062,7 @@ use Throwable; use function sprintf; class ManifestDocumentMapper { - public function map(ManifestDocument $document) : Manifest + public function map(ManifestDocument $document): Manifest { try { $contains = $document->getContainsElement(); @@ -24685,7 +24075,7 @@ class ManifestDocumentMapper throw new ManifestDocumentMapperException($e->getMessage(), (int) $e->getCode(), $e); } } - private function mapType(ContainsElement $contains) : Type + private function mapType(ContainsElement $contains): Type { switch ($contains->getType()) { case 'application': @@ -24697,7 +24087,7 @@ class ManifestDocumentMapper } throw new ManifestDocumentMapperException(sprintf('Unsupported type %s', $contains->getType())); } - private function mapCopyright(CopyrightElement $copyright) : CopyrightInformation + private function mapCopyright(CopyrightElement $copyright): CopyrightInformation { $authors = new AuthorCollection(); foreach ($copyright->getAuthorElements() as $authorElement) { @@ -24707,7 +24097,7 @@ class ManifestDocumentMapper $license = new License($licenseElement->getType(), new Url($licenseElement->getUrl())); return new CopyrightInformation($authors, $license); } - private function mapRequirements(RequiresElement $requires) : RequirementCollection + private function mapRequirements(RequiresElement $requires): RequirementCollection { $collection = new RequirementCollection(); $phpElement = $requires->getPHPElement(); @@ -24726,7 +24116,7 @@ class ManifestDocumentMapper } return $collection; } - private function mapBundledComponents(ManifestDocument $document) : BundledComponentCollection + private function mapBundledComponents(ManifestDocument $document): BundledComponentCollection { $collection = new BundledComponentCollection(); if (!$document->hasBundlesElement()) { @@ -24737,7 +24127,7 @@ class ManifestDocumentMapper } return $collection; } - private function mapExtension(ExtensionElement $extension) : Extension + private function mapExtension(ExtensionElement $extension): Extension { try { $versionConstraint = (new VersionConstraintParser())->parse($extension->getCompatible()); @@ -24764,7 +24154,7 @@ namespace PHPUnitPHAR\PharIo\Manifest; use function sprintf; class ManifestLoader { - public static function fromFile(string $filename) : Manifest + public static function fromFile(string $filename): Manifest { try { return (new ManifestDocumentMapper())->map(ManifestDocument::fromFile($filename)); @@ -24772,11 +24162,11 @@ class ManifestLoader throw new ManifestLoaderException(sprintf('Loading %s failed.', $filename), (int) $e->getCode(), $e); } } - public static function fromPhar(string $filename) : Manifest + public static function fromPhar(string $filename): Manifest { return self::fromFile('phar://' . $filename . '/manifest.xml'); } - public static function fromString(string $manifest) : Manifest + public static function fromString(string $manifest): Manifest { try { return (new ManifestDocumentMapper())->map(ManifestDocument::fromString($manifest)); @@ -24811,11 +24201,11 @@ class ManifestSerializer { /** @var XMLWriter */ private $xmlWriter; - public function serializeToFile(Manifest $manifest, string $filename) : void + public function serializeToFile(Manifest $manifest, string $filename): void { file_put_contents($filename, $this->serializeToString($manifest)); } - public function serializeToString(Manifest $manifest) : string + public function serializeToString(Manifest $manifest): string { $this->startDocument(); $this->addContains($manifest->getName(), $manifest->getVersion(), $manifest->getType()); @@ -24824,7 +24214,7 @@ class ManifestSerializer $this->addBundles($manifest->getBundledComponents()); return $this->finishDocument(); } - private function startDocument() : void + private function startDocument(): void { $xmlWriter = new XMLWriter(); $xmlWriter->openMemory(); @@ -24835,35 +24225,43 @@ class ManifestSerializer $xmlWriter->writeAttribute('xmlns', 'https://phar.io/xml/manifest/1.0'); $this->xmlWriter = $xmlWriter; } - private function finishDocument() : string + private function finishDocument(): string { $this->xmlWriter->endElement(); $this->xmlWriter->endDocument(); return $this->xmlWriter->outputMemory(); } - private function addContains(ApplicationName $name, Version $version, Type $type) : void + private function addContains(ApplicationName $name, Version $version, Type $type): void { $this->xmlWriter->startElement('contains'); $this->xmlWriter->writeAttribute('name', $name->asString()); $this->xmlWriter->writeAttribute('version', $version->getVersionString()); switch (\true) { case $type->isApplication(): - $this->xmlWriter->writeAttribute('type', 'application'); - break; + { + $this->xmlWriter->writeAttribute('type', 'application'); + break; + } case $type->isLibrary(): - $this->xmlWriter->writeAttribute('type', 'library'); - break; + { + $this->xmlWriter->writeAttribute('type', 'library'); + break; + } case $type->isExtension(): - $this->xmlWriter->writeAttribute('type', 'extension'); - /* @var $type Extension */ - $this->addExtension($type->getApplicationName(), $type->getVersionConstraint()); - break; + { + $this->xmlWriter->writeAttribute('type', 'extension'); + /* @var $type Extension */ + $this->addExtension($type->getApplicationName(), $type->getVersionConstraint()); + break; + } default: - $this->xmlWriter->writeAttribute('type', 'custom'); + { + $this->xmlWriter->writeAttribute('type', 'custom'); + } } $this->xmlWriter->endElement(); } - private function addCopyright(CopyrightInformation $copyrightInformation) : void + private function addCopyright(CopyrightInformation $copyrightInformation): void { $this->xmlWriter->startElement('copyright'); foreach ($copyrightInformation->getAuthors() as $author) { @@ -24879,7 +24277,7 @@ class ManifestSerializer $this->xmlWriter->endElement(); $this->xmlWriter->endElement(); } - private function addRequirements(RequirementCollection $requirementCollection) : void + private function addRequirements(RequirementCollection $requirementCollection): void { $phpRequirement = new AnyVersionConstraint(); $extensions = []; @@ -24903,7 +24301,7 @@ class ManifestSerializer $this->xmlWriter->endElement(); $this->xmlWriter->endElement(); } - private function addBundles(BundledComponentCollection $bundledComponentCollection) : void + private function addBundles(BundledComponentCollection $bundledComponentCollection): void { if (count($bundledComponentCollection) === 0) { return; @@ -24917,7 +24315,7 @@ class ManifestSerializer } $this->xmlWriter->endElement(); } - private function addExtension(ApplicationName $applicationName, VersionConstraint $versionConstraint) : void + private function addExtension(ApplicationName $applicationName, VersionConstraint $versionConstraint): void { $this->xmlWriter->startElement('extension'); $this->xmlWriter->writeAttribute('for', $applicationName->asString()); @@ -25068,7 +24466,7 @@ class ManifestDocumentLoadingException extends \Exception implements Exception /** * @return LibXMLError[] */ - public function getLibxmlErrors() : array + public function getLibxmlErrors(): array { return $this->libxmlErrors; } @@ -25160,7 +24558,7 @@ namespace PHPUnitPHAR\PharIo\Manifest; class Application extends Type { - public function isApplication() : bool + public function isApplication(): bool { return \true; } @@ -25190,17 +24588,17 @@ class ApplicationName $this->ensureValidFormat($name); $this->name = $name; } - public function asString() : string + public function asString(): string { return $this->name; } - public function isEqual(ApplicationName $name) : bool + public function isEqual(ApplicationName $name): bool { return $this->name === $name->name; } - private function ensureValidFormat(string $name) : void + private function ensureValidFormat(string $name): void { - if (!preg_match('#\\w/\\w#', $name)) { + if (!preg_match('#\w/\w#', $name)) { throw new InvalidApplicationNameException(sprintf('Format of name "%s" is not valid - expected: vendor/packagename', $name), InvalidApplicationNameException::InvalidFormat); } } @@ -25231,25 +24629,25 @@ class Author $this->name = $name; $this->email = $email; } - public function asString() : string + public function asString(): string { if (!$this->hasEmail()) { return $this->name; } return sprintf('%s <%s>', $this->name, $this->email->asString()); } - public function getName() : string + public function getName(): string { return $this->name; } /** * @psalm-assert-if-true Email $this->email */ - public function hasEmail() : bool + public function hasEmail(): bool { return $this->email !== null; } - public function getEmail() : Email + public function getEmail(): Email { if (!$this->hasEmail()) { throw new NoEmailAddressException(); @@ -25279,22 +24677,22 @@ class AuthorCollection implements Countable, IteratorAggregate { /** @var Author[] */ private $authors = []; - public function add(Author $author) : void + public function add(Author $author): void { $this->authors[] = $author; } /** * @return Author[] */ - public function getAuthors() : array + public function getAuthors(): array { return $this->authors; } - public function count() : int + public function count(): int { return count($this->authors); } - public function getIterator() : AuthorCollectionIterator + public function getIterator(): AuthorCollectionIterator { return new AuthorCollectionIterator($this); } @@ -25326,23 +24724,23 @@ class AuthorCollectionIterator implements Iterator { $this->authors = $authors->getAuthors(); } - public function rewind() : void + public function rewind(): void { $this->position = 0; } - public function valid() : bool + public function valid(): bool { return $this->position < count($this->authors); } - public function key() : int + public function key(): int { return $this->position; } - public function current() : Author + public function current(): Author { return $this->authors[$this->position]; } - public function next() : void + public function next(): void { $this->position++; } @@ -25373,11 +24771,11 @@ class BundledComponent $this->name = $name; $this->version = $version; } - public function getName() : string + public function getName(): string { return $this->name; } - public function getVersion() : Version + public function getVersion(): Version { return $this->version; } @@ -25404,22 +24802,22 @@ class BundledComponentCollection implements Countable, IteratorAggregate { /** @var BundledComponent[] */ private $bundledComponents = []; - public function add(BundledComponent $bundledComponent) : void + public function add(BundledComponent $bundledComponent): void { $this->bundledComponents[] = $bundledComponent; } /** * @return BundledComponent[] */ - public function getBundledComponents() : array + public function getBundledComponents(): array { return $this->bundledComponents; } - public function count() : int + public function count(): int { return count($this->bundledComponents); } - public function getIterator() : BundledComponentCollectionIterator + public function getIterator(): BundledComponentCollectionIterator { return new BundledComponentCollectionIterator($this); } @@ -25451,23 +24849,23 @@ class BundledComponentCollectionIterator implements Iterator { $this->bundledComponents = $bundledComponents->getBundledComponents(); } - public function rewind() : void + public function rewind(): void { $this->position = 0; } - public function valid() : bool + public function valid(): bool { return $this->position < count($this->bundledComponents); } - public function key() : int + public function key(): int { return $this->position; } - public function current() : BundledComponent + public function current(): BundledComponent { return $this->bundledComponents[$this->position]; } - public function next() : void + public function next(): void { $this->position++; } @@ -25497,11 +24895,11 @@ class CopyrightInformation $this->authors = $authors; $this->license = $license; } - public function getAuthors() : AuthorCollection + public function getAuthors(): AuthorCollection { return $this->authors; } - public function getLicense() : License + public function getLicense(): License { return $this->license; } @@ -25531,11 +24929,11 @@ class Email $this->ensureEmailIsValid($email); $this->email = $email; } - public function asString() : string + public function asString(): string { return $this->email; } - private function ensureEmailIsValid(string $url) : void + private function ensureEmailIsValid(string $url): void { if (filter_var($url, FILTER_VALIDATE_EMAIL) === \false) { throw new InvalidEmailException(); @@ -25569,23 +24967,23 @@ class Extension extends Type $this->application = $application; $this->versionConstraint = $versionConstraint; } - public function getApplicationName() : ApplicationName + public function getApplicationName(): ApplicationName { return $this->application; } - public function getVersionConstraint() : VersionConstraint + public function getVersionConstraint(): VersionConstraint { return $this->versionConstraint; } - public function isExtension() : bool + public function isExtension(): bool { return \true; } - public function isExtensionFor(ApplicationName $name) : bool + public function isExtensionFor(ApplicationName $name): bool { return $this->application->isEqual($name); } - public function isCompatibleWith(ApplicationName $name, Version $version) : bool + public function isCompatibleWith(ApplicationName $name, Version $version): bool { return $this->isExtensionFor($name) && $this->versionConstraint->complies($version); } @@ -25606,7 +25004,7 @@ namespace PHPUnitPHAR\PharIo\Manifest; class Library extends Type { - public function isLibrary() : bool + public function isLibrary(): bool { return \true; } @@ -25636,11 +25034,11 @@ class License $this->name = $name; $this->url = $url; } - public function getName() : string + public function getName(): string { return $this->name; } - public function getUrl() : Url + public function getUrl(): Url { return $this->url; } @@ -25683,43 +25081,43 @@ class Manifest $this->requirements = $requirements; $this->bundledComponents = $bundledComponents; } - public function getName() : ApplicationName + public function getName(): ApplicationName { return $this->name; } - public function getVersion() : Version + public function getVersion(): Version { return $this->version; } - public function getType() : Type + public function getType(): Type { return $this->type; } - public function getCopyrightInformation() : CopyrightInformation + public function getCopyrightInformation(): CopyrightInformation { return $this->copyrightInformation; } - public function getRequirements() : RequirementCollection + public function getRequirements(): RequirementCollection { return $this->requirements; } - public function getBundledComponents() : BundledComponentCollection + public function getBundledComponents(): BundledComponentCollection { return $this->bundledComponents; } - public function isApplication() : bool + public function isApplication(): bool { return $this->type->isApplication(); } - public function isLibrary() : bool + public function isLibrary(): bool { return $this->type->isLibrary(); } - public function isExtension() : bool + public function isExtension(): bool { return $this->type->isExtension(); } - public function isExtensionFor(ApplicationName $application, ?Version $version = null) : bool + public function isExtensionFor(ApplicationName $application, ?Version $version = null): bool { if (!$this->isExtension()) { return \false; @@ -25754,7 +25152,7 @@ class PhpExtensionRequirement implements Requirement { $this->extension = $extension; } - public function asString() : string + public function asString(): string { return $this->extension; } @@ -25782,7 +25180,7 @@ class PhpVersionRequirement implements Requirement { $this->versionConstraint = $versionConstraint; } - public function getVersionConstraint() : VersionConstraint + public function getVersionConstraint(): VersionConstraint { return $this->versionConstraint; } @@ -25826,22 +25224,22 @@ class RequirementCollection implements Countable, IteratorAggregate { /** @var Requirement[] */ private $requirements = []; - public function add(Requirement $requirement) : void + public function add(Requirement $requirement): void { $this->requirements[] = $requirement; } /** * @return Requirement[] */ - public function getRequirements() : array + public function getRequirements(): array { return $this->requirements; } - public function count() : int + public function count(): int { return count($this->requirements); } - public function getIterator() : RequirementCollectionIterator + public function getIterator(): RequirementCollectionIterator { return new RequirementCollectionIterator($this); } @@ -25873,23 +25271,23 @@ class RequirementCollectionIterator implements Iterator { $this->requirements = $requirements->getRequirements(); } - public function rewind() : void + public function rewind(): void { $this->position = 0; } - public function valid() : bool + public function valid(): bool { return $this->position < count($this->requirements); } - public function key() : int + public function key(): int { return $this->position; } - public function current() : Requirement + public function current(): Requirement { return $this->requirements[$this->position]; } - public function next() : void + public function next(): void { $this->position++; } @@ -25911,30 +25309,30 @@ namespace PHPUnitPHAR\PharIo\Manifest; use PHPUnitPHAR\PharIo\Version\VersionConstraint; abstract class Type { - public static function application() : Application + public static function application(): Application { return new Application(); } - public static function library() : Library + public static function library(): Library { return new Library(); } - public static function extension(ApplicationName $application, VersionConstraint $versionConstraint) : Extension + public static function extension(ApplicationName $application, VersionConstraint $versionConstraint): Extension { return new Extension($application, $versionConstraint); } /** @psalm-assert-if-true Application $this */ - public function isApplication() : bool + public function isApplication(): bool { return \false; } /** @psalm-assert-if-true Library $this */ - public function isLibrary() : bool + public function isLibrary(): bool { return \false; } /** @psalm-assert-if-true Extension $this */ - public function isExtension() : bool + public function isExtension(): bool { return \false; } @@ -25964,14 +25362,14 @@ class Url $this->ensureUrlIsValid($url); $this->url = $url; } - public function asString() : string + public function asString(): string { return $this->url; } /** * @throws InvalidUrlException */ - private function ensureUrlIsValid(string $url) : void + private function ensureUrlIsValid(string $url): void { if (filter_var($url, FILTER_VALIDATE_URL) === \false) { throw new InvalidUrlException(); @@ -25994,15 +25392,15 @@ namespace PHPUnitPHAR\PharIo\Manifest; class AuthorElement extends ManifestElement { - public function getName() : string + public function getName(): string { return $this->getAttributeValue('name'); } - public function getEmail() : string + public function getEmail(): string { return $this->getAttributeValue('email'); } - public function hasEMail() : bool + public function hasEMail(): bool { return $this->hasAttribute('email'); } @@ -26023,7 +25421,7 @@ namespace PHPUnitPHAR\PharIo\Manifest; class AuthorElementCollection extends ElementCollection { - public function current() : AuthorElement + public function current(): AuthorElement { return new AuthorElement($this->getCurrentElement()); } @@ -26044,7 +25442,7 @@ namespace PHPUnitPHAR\PharIo\Manifest; class BundlesElement extends ManifestElement { - public function getComponentElements() : ComponentElementCollection + public function getComponentElements(): ComponentElementCollection { return new ComponentElementCollection($this->getChildrenByName('component')); } @@ -26065,11 +25463,11 @@ namespace PHPUnitPHAR\PharIo\Manifest; class ComponentElement extends ManifestElement { - public function getName() : string + public function getName(): string { return $this->getAttributeValue('name'); } - public function getVersion() : string + public function getVersion(): string { return $this->getAttributeValue('version'); } @@ -26090,7 +25488,7 @@ namespace PHPUnitPHAR\PharIo\Manifest; class ComponentElementCollection extends ElementCollection { - public function current() : ComponentElement + public function current(): ComponentElement { return new ComponentElement($this->getCurrentElement()); } @@ -26111,19 +25509,19 @@ namespace PHPUnitPHAR\PharIo\Manifest; class ContainsElement extends ManifestElement { - public function getName() : string + public function getName(): string { return $this->getAttributeValue('name'); } - public function getVersion() : string + public function getVersion(): string { return $this->getAttributeValue('version'); } - public function getType() : string + public function getType(): string { return $this->getAttributeValue('type'); } - public function getExtensionElement() : ExtensionElement + public function getExtensionElement(): ExtensionElement { return new ExtensionElement($this->getChildByName('extension')); } @@ -26144,11 +25542,11 @@ namespace PHPUnitPHAR\PharIo\Manifest; class CopyrightElement extends ManifestElement { - public function getAuthorElements() : AuthorElementCollection + public function getAuthorElements(): AuthorElementCollection { return new AuthorElementCollection($this->getChildrenByName('author')); } - public function getLicenseElement() : LicenseElement + public function getLicenseElement(): LicenseElement { return new LicenseElement($this->getChildByName('license')); } @@ -26187,32 +25585,32 @@ abstract class ElementCollection implements Iterator $this->importNodes($nodeList); } #[ReturnTypeWillChange] - public abstract function current(); - public function next() : void + abstract public function current(); + public function next(): void { $this->position++; } - public function key() : int + public function key(): int { return $this->position; } - public function valid() : bool + public function valid(): bool { return $this->position < count($this->nodes); } - public function rewind() : void + public function rewind(): void { $this->position = 0; } - protected function getCurrentElement() : DOMElement + protected function getCurrentElement(): DOMElement { return $this->nodes[$this->position]; } - private function importNodes(DOMNodeList $nodeList) : void + private function importNodes(DOMNodeList $nodeList): void { foreach ($nodeList as $node) { if (!$node instanceof DOMElement) { - throw new ElementCollectionException(sprintf('\\DOMElement expected, got \\%s', get_class($node))); + throw new ElementCollectionException(sprintf('\DOMElement expected, got \%s', get_class($node))); } $this->nodes[] = $node; } @@ -26234,7 +25632,7 @@ namespace PHPUnitPHAR\PharIo\Manifest; class ExtElement extends ManifestElement { - public function getName() : string + public function getName(): string { return $this->getAttributeValue('name'); } @@ -26255,7 +25653,7 @@ namespace PHPUnitPHAR\PharIo\Manifest; class ExtElementCollection extends ElementCollection { - public function current() : ExtElement + public function current(): ExtElement { return new ExtElement($this->getCurrentElement()); } @@ -26276,11 +25674,11 @@ namespace PHPUnitPHAR\PharIo\Manifest; class ExtensionElement extends ManifestElement { - public function getFor() : string + public function getFor(): string { return $this->getAttributeValue('for'); } - public function getCompatible() : string + public function getCompatible(): string { return $this->getAttributeValue('compatible'); } @@ -26301,11 +25699,11 @@ namespace PHPUnitPHAR\PharIo\Manifest; class LicenseElement extends ManifestElement { - public function getType() : string + public function getType(): string { return $this->getAttributeValue('type'); } - public function getUrl() : string + public function getUrl(): string { return $this->getAttributeValue('url'); } @@ -26339,14 +25737,14 @@ class ManifestDocument public const XMLNS = 'https://phar.io/xml/manifest/1.0'; /** @var DOMDocument */ private $dom; - public static function fromFile(string $filename) : ManifestDocument + public static function fromFile(string $filename): ManifestDocument { if (!is_file($filename)) { throw new ManifestDocumentException(sprintf('File "%s" not found', $filename)); } return self::fromString(file_get_contents($filename)); } - public static function fromString(string $xmlString) : ManifestDocument + public static function fromString(string $xmlString): ManifestDocument { $prev = libxml_use_internal_errors(\true); libxml_clear_errors(); @@ -26368,34 +25766,34 @@ class ManifestDocument $this->ensureCorrectDocumentType($dom); $this->dom = $dom; } - public function getContainsElement() : ContainsElement + public function getContainsElement(): ContainsElement { return new ContainsElement($this->fetchElementByName('contains')); } - public function getCopyrightElement() : CopyrightElement + public function getCopyrightElement(): CopyrightElement { return new CopyrightElement($this->fetchElementByName('copyright')); } - public function getRequiresElement() : RequiresElement + public function getRequiresElement(): RequiresElement { return new RequiresElement($this->fetchElementByName('requires')); } - public function hasBundlesElement() : bool + public function hasBundlesElement(): bool { return $this->dom->getElementsByTagNameNS(self::XMLNS, 'bundles')->length === 1; } - public function getBundlesElement() : BundlesElement + public function getBundlesElement(): BundlesElement { return new BundlesElement($this->fetchElementByName('bundles')); } - private function ensureCorrectDocumentType(DOMDocument $dom) : void + private function ensureCorrectDocumentType(DOMDocument $dom): void { $root = $dom->documentElement; if ($root->localName !== 'phar' || $root->namespaceURI !== self::XMLNS) { throw new ManifestDocumentException('Not a phar.io manifest document'); } } - private function fetchElementByName(string $elementName) : DOMElement + private function fetchElementByName(string $elementName): DOMElement { $element = $this->dom->getElementsByTagNameNS(self::XMLNS, $elementName)->item(0); if (!$element instanceof DOMElement) { @@ -26430,18 +25828,18 @@ class ManifestElement { $this->element = $element; } - protected function getAttributeValue(string $name) : string + protected function getAttributeValue(string $name): string { if (!$this->element->hasAttribute($name)) { throw new ManifestElementException(sprintf('Attribute %s not set on element %s', $name, $this->element->localName)); } return $this->element->getAttribute($name); } - protected function hasAttribute(string $name) : bool + protected function hasAttribute(string $name): bool { return $this->element->hasAttribute($name); } - protected function getChildByName(string $elementName) : DOMElement + protected function getChildByName(string $elementName): DOMElement { $element = $this->element->getElementsByTagNameNS(self::XMLNS, $elementName)->item(0); if (!$element instanceof DOMElement) { @@ -26449,7 +25847,7 @@ class ManifestElement } return $element; } - protected function getChildrenByName(string $elementName) : DOMNodeList + protected function getChildrenByName(string $elementName): DOMNodeList { $elementList = $this->element->getElementsByTagNameNS(self::XMLNS, $elementName); if ($elementList->length === 0) { @@ -26457,7 +25855,7 @@ class ManifestElement } return $elementList; } - protected function hasChild(string $elementName) : bool + protected function hasChild(string $elementName): bool { return $this->element->getElementsByTagNameNS(self::XMLNS, $elementName)->length !== 0; } @@ -26478,15 +25876,15 @@ namespace PHPUnitPHAR\PharIo\Manifest; class PhpElement extends ManifestElement { - public function getVersion() : string + public function getVersion(): string { return $this->getAttributeValue('version'); } - public function hasExtElements() : bool + public function hasExtElements(): bool { return $this->hasChild('ext'); } - public function getExtElements() : ExtElementCollection + public function getExtElements(): ExtElementCollection { return new ExtElementCollection($this->getChildrenByName('ext')); } @@ -26507,7 +25905,7 @@ namespace PHPUnitPHAR\PharIo\Manifest; class RequiresElement extends ManifestElement { - public function getPHPElement() : PhpElement + public function getPHPElement(): PhpElement { return new PhpElement($this->getChildByName('php')); } @@ -26533,11 +25931,11 @@ class BuildMetaData { $this->value = $value; } - public function asString() : string + public function asString(): string { return $this->value; } - public function equals(BuildMetaData $other) : bool + public function equals(BuildMetaData $other): bool { return $this->asString() === $other->asString(); } @@ -26594,19 +25992,19 @@ class PreReleaseSuffix { $this->parseValue($value); } - public function asString() : string + public function asString(): string { return $this->full; } - public function getValue() : string + public function getValue(): string { return $this->value; } - public function getNumber() : ?int + public function getNumber(): ?int { return $this->number; } - public function isGreaterThan(PreReleaseSuffix $suffix) : bool + public function isGreaterThan(PreReleaseSuffix $suffix): bool { if ($this->valueScore > $suffix->valueScore) { return \true; @@ -26616,14 +26014,14 @@ class PreReleaseSuffix } return $this->getNumber() > $suffix->getNumber(); } - private function mapValueToScore(string $value) : int + private function mapValueToScore(string $value): int { $value = \strtolower($value); return self::valueScoreMap[$value]; } - private function parseValue(string $value) : void + private function parseValue(string $value): void { - $regex = '/-?((dev|beta|b|rc|alpha|a|patch|p|pl)\\.?(\\d*)).*$/i'; + $regex = '/-?((dev|beta|b|rc|alpha|a|patch|p|pl)\.?(\d*)).*$/i'; if (\preg_match($regex, $value, $matches) !== 1) { throw new InvalidPreReleaseSuffixException(\sprintf('Invalid label %s', $value)); } @@ -26670,18 +26068,18 @@ class Version /** * @throws NoPreReleaseSuffixException */ - public function getPreReleaseSuffix() : PreReleaseSuffix + public function getPreReleaseSuffix(): PreReleaseSuffix { if ($this->preReleaseSuffix === null) { throw new NoPreReleaseSuffixException('No pre-release suffix set'); } return $this->preReleaseSuffix; } - public function getOriginalString() : string + public function getOriginalString(): string { return $this->originalVersionString; } - public function getVersionString() : string + public function getVersionString(): string { $str = \sprintf('%d.%d.%d', $this->getMajor()->getValue() ?? 0, $this->getMinor()->getValue() ?? 0, $this->getPatch()->getValue() ?? 0); if (!$this->hasPreReleaseSuffix()) { @@ -26689,11 +26087,11 @@ class Version } return $str . '-' . $this->getPreReleaseSuffix()->asString(); } - public function hasPreReleaseSuffix() : bool + public function hasPreReleaseSuffix(): bool { return $this->preReleaseSuffix !== null; } - public function equals(Version $other) : bool + public function equals(Version $other): bool { if ($this->getVersionString() !== $other->getVersionString()) { return \false; @@ -26706,7 +26104,7 @@ class Version } return \true; } - public function isGreaterThan(Version $version) : bool + public function isGreaterThan(Version $version): bool { if ($version->getMajor()->getValue() > $this->getMajor()->getValue()) { return \false; @@ -26737,15 +26135,15 @@ class Version } return $this->getPreReleaseSuffix()->isGreaterThan($version->getPreReleaseSuffix()); } - public function getMajor() : VersionNumber + public function getMajor(): VersionNumber { return $this->major; } - public function getMinor() : VersionNumber + public function getMinor(): VersionNumber { return $this->minor; } - public function getPatch() : VersionNumber + public function getPatch(): VersionNumber { return $this->patch; } @@ -26753,14 +26151,14 @@ class Version * @psalm-assert-if-true BuildMetaData $this->buildMetadata * @psalm-assert-if-true BuildMetaData $this->getBuildMetaData() */ - public function hasBuildMetaData() : bool + public function hasBuildMetaData(): bool { return $this->buildMetadata !== null; } /** * @throws NoBuildMetaDataException */ - public function getBuildMetaData() : BuildMetaData + public function getBuildMetaData(): BuildMetaData { if (!$this->hasBuildMetaData()) { throw new NoBuildMetaDataException('No build metadata set'); @@ -26772,7 +26170,7 @@ class Version * * @throws InvalidPreReleaseSuffixException */ - private function parseVersion(array $matches) : void + private function parseVersion(array $matches): void { $this->major = new VersionNumber((int) $matches['Major']); $this->minor = new VersionNumber((int) $matches['Minor']); @@ -26789,22 +26187,22 @@ class Version * * @throws InvalidVersionException */ - private function ensureVersionStringIsValid($version) : void + private function ensureVersionStringIsValid($version): void { $regex = '/^v? - (?P0|[1-9]\\d*) - \\. - (?P0|[1-9]\\d*) - (\\. - (?P0|[1-9]\\d*) + (?P0|[1-9]\d*) + \. + (?P0|[1-9]\d*) + (\. + (?P0|[1-9]\d*) )? (?: - - (?(?:(dev|beta|b|rc|alpha|a|patch|p|pl)\\.?\\d*)) + (?(?:(dev|beta|b|rc|alpha|a|patch|p|pl)\.?\d*)) )? (?: - \\+ - (?P[0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-@]+)*) + \+ + (?P[0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-@]+)*) )? $/xi'; if (\preg_match($regex, $version, $matches) !== 1) { @@ -26831,12 +26229,12 @@ class VersionConstraintParser /** * @throws UnsupportedVersionConstraintException */ - public function parse(string $value) : VersionConstraint + public function parse(string $value): VersionConstraint { if (\strpos($value, '|') !== \false) { return $this->handleOrGroup($value); } - if (!\preg_match('/^[\\^~*]?v?[\\d.*]+(?:-.*)?$/i', $value)) { + if (!\preg_match('/^[\^~*]?v?[\d.*]+(?:-.*)?$/i', $value)) { throw new UnsupportedVersionConstraintException(\sprintf('Version constraint %s is not supported.', $value)); } switch ($value[0]) { @@ -26857,15 +26255,15 @@ class VersionConstraintParser } return new ExactVersionConstraint($constraint->getVersionString()); } - private function handleOrGroup(string $value) : OrVersionConstraintGroup + private function handleOrGroup(string $value): OrVersionConstraintGroup { $constraints = []; - foreach (\preg_split('{\\s*\\|\\|?\\s*}', \trim($value)) as $groupSegment) { + foreach (\preg_split('{\s*\|\|?\s*}', \trim($value)) as $groupSegment) { $constraints[] = $this->parse(\trim($groupSegment)); } return new OrVersionConstraintGroup($value, $constraints); } - private function handleTildeOperator(string $value) : AndVersionConstraintGroup + private function handleTildeOperator(string $value): AndVersionConstraintGroup { $constraintValue = new VersionConstraintValue(\substr($value, 1)); if ($constraintValue->getPatch()->isAny()) { @@ -26874,7 +26272,7 @@ class VersionConstraintParser $constraints = [new GreaterThanOrEqualToVersionConstraint($value, new Version(\substr($value, 1))), new SpecificMajorAndMinorVersionConstraint($value, $constraintValue->getMajor()->getValue() ?? 0, $constraintValue->getMinor()->getValue() ?? 0)]; return new AndVersionConstraintGroup($value, $constraints); } - private function handleCaretOperator(string $value) : AndVersionConstraintGroup + private function handleCaretOperator(string $value): AndVersionConstraintGroup { $constraintValue = new VersionConstraintValue(\substr($value, 1)); $constraints = [new GreaterThanOrEqualToVersionConstraint($value, new Version(\substr($value, 1)))]; @@ -26910,57 +26308,57 @@ class VersionConstraintValue $this->versionString = $versionString; $this->parseVersion($versionString); } - public function getLabel() : string + public function getLabel(): string { return $this->label; } - public function getBuildMetaData() : string + public function getBuildMetaData(): string { return $this->buildMetaData; } - public function getVersionString() : string + public function getVersionString(): string { return $this->versionString; } - public function getMajor() : VersionNumber + public function getMajor(): VersionNumber { return $this->major; } - public function getMinor() : VersionNumber + public function getMinor(): VersionNumber { return $this->minor; } - public function getPatch() : VersionNumber + public function getPatch(): VersionNumber { return $this->patch; } - private function parseVersion(string $versionString) : void + private function parseVersion(string $versionString): void { $this->extractBuildMetaData($versionString); $this->extractLabel($versionString); $this->stripPotentialVPrefix($versionString); $versionSegments = \explode('.', $versionString); $this->major = new VersionNumber(\is_numeric($versionSegments[0]) ? (int) $versionSegments[0] : null); - $minorValue = isset($versionSegments[1]) && \is_numeric($versionSegments[1]) ? (int) $versionSegments[1] : null; - $patchValue = isset($versionSegments[2]) && \is_numeric($versionSegments[2]) ? (int) $versionSegments[2] : null; + $minorValue = (isset($versionSegments[1]) && \is_numeric($versionSegments[1])) ? (int) $versionSegments[1] : null; + $patchValue = (isset($versionSegments[2]) && \is_numeric($versionSegments[2])) ? (int) $versionSegments[2] : null; $this->minor = new VersionNumber($minorValue); $this->patch = new VersionNumber($patchValue); } - private function extractBuildMetaData(string &$versionString) : void + private function extractBuildMetaData(string &$versionString): void { - if (\preg_match('/\\+(.*)/', $versionString, $matches) === 1) { + if (\preg_match('/\+(.*)/', $versionString, $matches) === 1) { $this->buildMetaData = $matches[1]; $versionString = \str_replace($matches[0], '', $versionString); } } - private function extractLabel(string &$versionString) : void + private function extractLabel(string &$versionString): void { if (\preg_match('/-(.*)/', $versionString, $matches) === 1) { $this->label = $matches[1]; $versionString = \str_replace($matches[0], '', $versionString); } } - private function stripPotentialVPrefix(string &$versionString) : void + private function stripPotentialVPrefix(string &$versionString): void { if ($versionString[0] !== 'v') { return; @@ -26989,11 +26387,11 @@ class VersionNumber { $this->value = $value; } - public function isAny() : bool + public function isAny(): bool { return $this->value === null; } - public function getValue() : ?int + public function getValue(): ?int { return $this->value; } @@ -27019,7 +26417,7 @@ abstract class AbstractVersionConstraint implements VersionConstraint { $this->originalValue = $originalValue; } - public function asString() : string + public function asString(): string { return $this->originalValue; } @@ -27049,7 +26447,7 @@ class AndVersionConstraintGroup extends AbstractVersionConstraint parent::__construct($originalValue); $this->constraints = $constraints; } - public function complies(Version $version) : bool + public function complies(Version $version): bool { foreach ($this->constraints as $constraint) { if (!$constraint->complies($version)) { @@ -27074,11 +26472,11 @@ namespace PHPUnitPHAR\PharIo\Version; class AnyVersionConstraint implements VersionConstraint { - public function complies(Version $version) : bool + public function complies(Version $version): bool { return \true; } - public function asString() : string + public function asString(): string { return '*'; } @@ -27098,7 +26496,7 @@ namespace PHPUnitPHAR\PharIo\Version; class ExactVersionConstraint extends AbstractVersionConstraint { - public function complies(Version $version) : bool + public function complies(Version $version): bool { $other = $version->getVersionString(); if ($version->hasBuildMetaData()) { @@ -27129,7 +26527,7 @@ class GreaterThanOrEqualToVersionConstraint extends AbstractVersionConstraint parent::__construct($originalValue); $this->minimalVersion = $minimalVersion; } - public function complies(Version $version) : bool + public function complies(Version $version): bool { return $version->getVersionString() === $this->minimalVersion->getVersionString() || $version->isGreaterThan($this->minimalVersion); } @@ -27160,7 +26558,7 @@ class OrVersionConstraintGroup extends AbstractVersionConstraint parent::__construct($originalValue); $this->constraints = $constraints; } - public function complies(Version $version) : bool + public function complies(Version $version): bool { foreach ($this->constraints as $constraint) { if ($constraint->complies($version)) { @@ -27195,7 +26593,7 @@ class SpecificMajorAndMinorVersionConstraint extends AbstractVersionConstraint $this->major = $major; $this->minor = $minor; } - public function complies(Version $version) : bool + public function complies(Version $version): bool { if ($version->getMajor()->getValue() !== $this->major) { return \false; @@ -27225,7 +26623,7 @@ class SpecificMajorVersionConstraint extends AbstractVersionConstraint parent::__construct($originalValue); $this->major = $major; } - public function complies(Version $version) : bool + public function complies(Version $version): bool { return $version->getMajor()->getValue() === $this->major; } @@ -27245,8 +26643,8 @@ namespace PHPUnitPHAR\PharIo\Version; interface VersionConstraint { - public function complies(Version $version) : bool; - public function asString() : string; + public function complies(Version $version): bool; + public function asString(): string; } > */ - private $filter; + private array $linesToBeIgnored = []; /** - * @var Wizard - */ - private $wizard; - /** - * @var bool - */ - private $checkForUnintentionallyCoveredCode = \false; - /** - * @var bool - */ - private $includeUncoveredFiles = \true; - /** - * @var bool - */ - private $processUncoveredFiles = \false; - /** - * @var bool + * @psalm-var array */ - private $ignoreDeprecatedCode = \false; - /** - * @var null|PhptTestCase|string|TestCase - */ - private $currentId; - /** - * Code coverage data. - * - * @var ProcessedCodeCoverageData - */ - private $data; - /** - * @var bool - */ - private $useAnnotationsForIgnoringCode = \true; - /** - * Test data. - * - * @var array - */ - private $tests = []; + private array $tests = []; /** * @psalm-var list */ - private $parentClassesExcludedFromUnintentionallyCoveredCodeCheck = []; - /** - * @var ?FileAnalyser - */ - private $analyser; - /** - * @var ?string - */ - private $cacheDirectory; - /** - * @var ?Directory - */ - private $cachedReport; + private array $parentClassesExcludedFromUnintentionallyCoveredCodeCheck = []; + private ?FileAnalyser $analyser = null; + private ?string $cacheDirectory = null; + private ?Directory $cachedReport = null; public function __construct(Driver $driver, Filter $filter) { $this->driver = $driver; @@ -27429,7 +26794,7 @@ final class CodeCoverage /** * Returns the code coverage information as a graph of node objects. */ - public function getReport() : Directory + public function getReport(): Directory { if ($this->cachedReport === null) { $this->cachedReport = (new Builder($this->analyser()))->build($this); @@ -27439,9 +26804,10 @@ final class CodeCoverage /** * Clears collected code coverage data. */ - public function clear() : void + public function clear(): void { $this->currentId = null; + $this->currentSize = null; $this->data = new ProcessedCodeCoverageData(); $this->tests = []; $this->cachedReport = null; @@ -27449,26 +26815,24 @@ final class CodeCoverage /** * @internal */ - public function clearCache() : void + public function clearCache(): void { $this->cachedReport = null; } /** * Returns the filter object used. */ - public function filter() : Filter + public function filter(): Filter { return $this->filter; } /** * Returns the collected code coverage data. */ - public function getData(bool $raw = \false) : ProcessedCodeCoverageData + public function getData(bool $raw = \false): ProcessedCodeCoverageData { if (!$raw) { - if ($this->processUncoveredFiles) { - $this->processUncoveredFilesFromFilter(); - } elseif ($this->includeUncoveredFiles) { + if ($this->includeUncoveredFiles) { $this->addUncoveredFilesFromFilter(); } } @@ -27477,65 +26841,55 @@ final class CodeCoverage /** * Sets the coverage data. */ - public function setData(ProcessedCodeCoverageData $data) : void + public function setData(ProcessedCodeCoverageData $data): void { $this->data = $data; } /** - * Returns the test data. + * @psalm-return array */ - public function getTests() : array + public function getTests(): array { return $this->tests; } /** - * Sets the test data. + * @psalm-param array $tests */ - public function setTests(array $tests) : void + public function setTests(array $tests): void { $this->tests = $tests; } - /** - * Start collection of code coverage information. - * - * @param PhptTestCase|string|TestCase $id - */ - public function start($id, bool $clear = \false) : void + public function start(string $id, ?TestSize $size = null, bool $clear = \false): void { if ($clear) { $this->clear(); } $this->currentId = $id; + $this->currentSize = $size; $this->driver->start(); $this->cachedReport = null; } /** - * Stop collection of code coverage information. - * - * @param array|false $linesToBeCovered + * @psalm-param array> $linesToBeIgnored */ - public function stop(bool $append = \true, $linesToBeCovered = [], array $linesToBeUsed = []) : RawCodeCoverageData + public function stop(bool $append = \true, ?TestStatus $status = null, array|false $linesToBeCovered = [], array $linesToBeUsed = [], array $linesToBeIgnored = []): RawCodeCoverageData { - if (!is_array($linesToBeCovered) && $linesToBeCovered !== \false) { - throw new InvalidArgumentException('$linesToBeCovered must be an array or false'); - } $data = $this->driver->stop(); - $this->append($data, null, $append, $linesToBeCovered, $linesToBeUsed); + $this->linesToBeIgnored = array_merge_recursive($this->linesToBeIgnored, $linesToBeIgnored); + $this->append($data, null, $append, $status, $linesToBeCovered, $linesToBeUsed, $linesToBeIgnored); $this->currentId = null; + $this->currentSize = null; $this->cachedReport = null; return $data; } /** - * Appends code coverage data. - * - * @param PhptTestCase|string|TestCase $id - * @param array|false $linesToBeCovered + * @psalm-param array> $linesToBeIgnored * * @throws ReflectionException * @throws TestIdMissingException * @throws UnintentionallyCoveredCodeException */ - public function append(RawCodeCoverageData $rawData, $id = null, bool $append = \true, $linesToBeCovered = [], array $linesToBeUsed = []) : void + public function append(RawCodeCoverageData $rawData, ?string $id = null, bool $append = \true, ?TestStatus $status = null, array|false $linesToBeCovered = [], array $linesToBeUsed = [], array $linesToBeIgnored = []): void { if ($id === null) { $id = $this->currentId; @@ -27544,113 +26898,93 @@ final class CodeCoverage throw new TestIdMissingException(); } $this->cachedReport = null; + if ($status === null) { + $status = TestStatus::unknown(); + } + $size = $this->currentSize; + if ($size === null) { + $size = TestSize::unknown(); + } $this->applyFilter($rawData); $this->applyExecutableLinesFilter($rawData); if ($this->useAnnotationsForIgnoringCode) { - $this->applyIgnoredLinesFilter($rawData); + $this->applyIgnoredLinesFilter($rawData, $linesToBeIgnored); } $this->data->initializeUnseenData($rawData); if (!$append) { return; } - if ($id !== self::UNCOVERED_FILES) { - $this->applyCoversAnnotationFilter($rawData, $linesToBeCovered, $linesToBeUsed); - if (empty($rawData->lineCoverage())) { - return; - } - $size = 'unknown'; - $status = -1; - $fromTestcase = \false; - if ($id instanceof TestCase) { - $fromTestcase = \true; - $_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) { - $fromTestcase = \true; - $size = 'large'; - $id = $id->getName(); - } - $this->tests[$id] = ['size' => $size, 'status' => $status, 'fromTestcase' => $fromTestcase]; - $this->data->markCodeAsExecutedByTestCase($id, $rawData); + if ($id === self::UNCOVERED_FILES) { + return; + } + $this->applyCoversAndUsesFilter($rawData, $linesToBeCovered, $linesToBeUsed, $size); + if (empty($rawData->lineCoverage())) { + return; } + $this->tests[$id] = ['size' => $size->asString(), 'status' => $status->asString()]; + $this->data->markCodeAsExecutedByTestCase($id, $rawData); } /** * Merges the data from another instance. */ - public function merge(self $that) : void + public function merge(self $that): void { $this->filter->includeFiles($that->filter()->files()); $this->data->merge($that->data); $this->tests = array_merge($this->tests, $that->getTests()); $this->cachedReport = null; } - public function enableCheckForUnintentionallyCoveredCode() : void + public function enableCheckForUnintentionallyCoveredCode(): void { $this->checkForUnintentionallyCoveredCode = \true; } - public function disableCheckForUnintentionallyCoveredCode() : void + public function disableCheckForUnintentionallyCoveredCode(): void { $this->checkForUnintentionallyCoveredCode = \false; } - public function includeUncoveredFiles() : void + public function includeUncoveredFiles(): void { $this->includeUncoveredFiles = \true; } - public function excludeUncoveredFiles() : void + public function excludeUncoveredFiles(): void { $this->includeUncoveredFiles = \false; } - public function processUncoveredFiles() : void - { - $this->processUncoveredFiles = \true; - } - public function doNotProcessUncoveredFiles() : void - { - $this->processUncoveredFiles = \false; - } - public function enableAnnotationsForIgnoringCode() : void + public function enableAnnotationsForIgnoringCode(): void { $this->useAnnotationsForIgnoringCode = \true; } - public function disableAnnotationsForIgnoringCode() : void + public function disableAnnotationsForIgnoringCode(): void { $this->useAnnotationsForIgnoringCode = \false; } - public function ignoreDeprecatedCode() : void + public function ignoreDeprecatedCode(): void { $this->ignoreDeprecatedCode = \true; } - public function doNotIgnoreDeprecatedCode() : void + public function doNotIgnoreDeprecatedCode(): void { $this->ignoreDeprecatedCode = \false; } /** * @psalm-assert-if-true !null $this->cacheDirectory */ - public function cachesStaticAnalysis() : bool + public function cachesStaticAnalysis(): bool { return $this->cacheDirectory !== null; } - public function cacheStaticAnalysis(string $directory) : void + public function cacheStaticAnalysis(string $directory): void { $this->cacheDirectory = $directory; } - public function doNotCacheStaticAnalysis() : void + public function doNotCacheStaticAnalysis(): void { $this->cacheDirectory = null; } /** * @throws StaticAnalysisCacheNotConfiguredException */ - public function cacheDirectory() : string + public function cacheDirectory(): string { if (!$this->cachesStaticAnalysis()) { throw new StaticAnalysisCacheNotConfiguredException('The static analysis cache is not configured'); @@ -27660,35 +26994,31 @@ final class CodeCoverage /** * @psalm-param class-string $className */ - public function excludeSubclassesOfThisClassFromUnintentionallyCoveredCodeCheck(string $className) : void + public function excludeSubclassesOfThisClassFromUnintentionallyCoveredCodeCheck(string $className): void { $this->parentClassesExcludedFromUnintentionallyCoveredCodeCheck[] = $className; } - public function enableBranchAndPathCoverage() : void + public function enableBranchAndPathCoverage(): void { $this->driver->enableBranchAndPathCoverage(); } - public function disableBranchAndPathCoverage() : void + public function disableBranchAndPathCoverage(): void { $this->driver->disableBranchAndPathCoverage(); } - public function collectsBranchAndPathCoverage() : bool + public function collectsBranchAndPathCoverage(): bool { return $this->driver->collectsBranchAndPathCoverage(); } - public function detectsDeadCode() : bool + public function detectsDeadCode(): bool { return $this->driver->detectsDeadCode(); } /** - * Applies the @covers annotation filtering. - * - * @param array|false $linesToBeCovered - * * @throws ReflectionException * @throws UnintentionallyCoveredCodeException */ - private function applyCoversAnnotationFilter(RawCodeCoverageData $rawData, $linesToBeCovered, array $linesToBeUsed) : void + private function applyCoversAndUsesFilter(RawCodeCoverageData $rawData, array|false $linesToBeCovered, array $linesToBeUsed, TestSize $size): void { if ($linesToBeCovered === \false) { $rawData->clear(); @@ -27697,7 +27027,7 @@ final class CodeCoverage if (empty($linesToBeCovered)) { return; } - if ($this->checkForUnintentionallyCoveredCode && (!$this->currentId instanceof TestCase || !$this->currentId->isMedium() && !$this->currentId->isLarge())) { + if ($this->checkForUnintentionallyCoveredCode && !$size->isMedium() && !$size->isLarge()) { $this->performUnintentionallyCoveredCodeCheck($rawData, $linesToBeCovered, $linesToBeUsed); } $rawLineData = $rawData->lineCoverage(); @@ -27712,7 +27042,7 @@ final class CodeCoverage } } } - private function applyFilter(RawCodeCoverageData $data) : void + private function applyFilter(RawCodeCoverageData $data): void { if ($this->filter->isEmpty()) { return; @@ -27723,7 +27053,7 @@ final class CodeCoverage } } } - private function applyExecutableLinesFilter(RawCodeCoverageData $data) : void + private function applyExecutableLinesFilter(RawCodeCoverageData $data): void { foreach (array_keys($data->lineCoverage()) as $filename) { if (!$this->filter->isFile($filename)) { @@ -27734,46 +27064,38 @@ final class CodeCoverage $data->markExecutableLineByBranch($filename, $linesToBranchMap); } } - private function applyIgnoredLinesFilter(RawCodeCoverageData $data) : void + /** + * @psalm-param array> $linesToBeIgnored + */ + private function applyIgnoredLinesFilter(RawCodeCoverageData $data, array $linesToBeIgnored): void { foreach (array_keys($data->lineCoverage()) as $filename) { if (!$this->filter->isFile($filename)) { continue; } - $data->removeCoverageDataForLines($filename, $this->analyser()->ignoredLinesFor($filename)); - } - } - /** - * @throws UnintentionallyCoveredCodeException - */ - private function addUncoveredFilesFromFilter() : void - { - $uncoveredFiles = array_diff($this->filter->files(), $this->data->coveredFiles()); - foreach ($uncoveredFiles as $uncoveredFile) { - if ($this->filter->isFile($uncoveredFile)) { - $this->append(RawCodeCoverageData::fromUncoveredFile($uncoveredFile, $this->analyser()), self::UNCOVERED_FILES); + if (isset($linesToBeIgnored[$filename])) { + $data->removeCoverageDataForLines($filename, $linesToBeIgnored[$filename]); } + $data->removeCoverageDataForLines($filename, $this->analyser()->ignoredLinesFor($filename)); } } /** * @throws UnintentionallyCoveredCodeException */ - private function processUncoveredFilesFromFilter() : void + private function addUncoveredFilesFromFilter(): void { $uncoveredFiles = array_diff($this->filter->files(), $this->data->coveredFiles()); - $this->driver->start(); foreach ($uncoveredFiles as $uncoveredFile) { - if ($this->filter->isFile($uncoveredFile)) { - include_once $uncoveredFile; + if (is_file($uncoveredFile)) { + $this->append(RawCodeCoverageData::fromUncoveredFile($uncoveredFile, $this->analyser()), self::UNCOVERED_FILES, linesToBeIgnored: $this->linesToBeIgnored); } } - $this->append($this->driver->stop(), self::UNCOVERED_FILES); } /** * @throws ReflectionException * @throws UnintentionallyCoveredCodeException */ - private function performUnintentionallyCoveredCodeCheck(RawCodeCoverageData $data, array $linesToBeCovered, array $linesToBeUsed) : void + private function performUnintentionallyCoveredCodeCheck(RawCodeCoverageData $data, array $linesToBeCovered, array $linesToBeUsed): void { $allowedLines = $this->getAllowedLines($linesToBeCovered, $linesToBeUsed); $unintentionallyCoveredUnits = []; @@ -27789,7 +27111,7 @@ final class CodeCoverage throw new UnintentionallyCoveredCodeException($unintentionallyCoveredUnits); } } - private function getAllowedLines(array $linesToBeCovered, array $linesToBeUsed) : array + private function getAllowedLines(array $linesToBeCovered, array $linesToBeUsed): array { $allowedLines = []; foreach (array_keys($linesToBeCovered) as $file) { @@ -27810,32 +27132,39 @@ final class CodeCoverage return $allowedLines; } /** + * @param list $unintentionallyCoveredUnits + * * @throws ReflectionException + * + * @return list */ - private function processUnintentionallyCoveredUnits(array $unintentionallyCoveredUnits) : array + 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) { + $processed = []; + foreach ($unintentionallyCoveredUnits as $unintentionallyCoveredUnit) { + $tmp = explode('::', $unintentionallyCoveredUnit); + if (count($tmp) !== 2) { + $processed[] = $unintentionallyCoveredUnit; continue; } try { - $class = new ReflectionClass($unit[0]); + $class = new ReflectionClass($tmp[0]); foreach ($this->parentClassesExcludedFromUnintentionallyCoveredCodeCheck as $parentClass) { if ($class->isSubclassOf($parentClass)) { - unset($unintentionallyCoveredUnits[$k]); - break; + continue 2; } } } catch (\ReflectionException $e) { throw new ReflectionException($e->getMessage(), $e->getCode(), $e); } + $processed[] = $tmp[0]; } - return array_values($unintentionallyCoveredUnits); + $processed = array_unique($processed); + sort($processed); + return $processed; } - private function analyser() : FileAnalyser + private function analyser(): FileAnalyser { if ($this->analyser !== null) { return $this->analyser; @@ -27849,6 +27178,479 @@ final class CodeCoverage } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnitPHAR\SebastianBergmann\CodeCoverage\Data; + +use function array_key_exists; +use function array_keys; +use function array_merge; +use function array_unique; +use function count; +use function is_array; +use function ksort; +use PHPUnitPHAR\SebastianBergmann\CodeCoverage\Driver\Driver; +/** + * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage + * + * @psalm-import-type XdebugFunctionCoverageType from \SebastianBergmann\CodeCoverage\Driver\XdebugDriver + * + * @psalm-type TestIdType = string + */ +final class ProcessedCodeCoverageData +{ + /** + * Line coverage data. + * An array of filenames, each having an array of linenumbers, each executable line having an array of testcase ids. + * + * @psalm-var array>> + */ + private array $lineCoverage = []; + /** + * Function coverage data. + * Maintains base format of raw data (@see https://xdebug.org/docs/code_coverage), but each 'hit' entry is an array + * of testcase ids. + * + * @psalm-var array, + * out: array, + * out_hit: array, + * }>, + * paths: array, + * hit: list, + * }>, + * hit: list + * }>> + */ + private array $functionCoverage = []; + public function initializeUnseenData(RawCodeCoverageData $rawData): void + { + foreach ($rawData->lineCoverage() as $file => $lines) { + if (!isset($this->lineCoverage[$file])) { + $this->lineCoverage[$file] = []; + foreach ($lines as $k => $v) { + $this->lineCoverage[$file][$k] = ($v === Driver::LINE_NOT_EXECUTABLE) ? null : []; + } + } + } + foreach ($rawData->functionCoverage() as $file => $functions) { + foreach ($functions as $functionName => $functionData) { + if (isset($this->functionCoverage[$file][$functionName])) { + $this->initPreviouslySeenFunction($file, $functionName, $functionData); + } else { + $this->initPreviouslyUnseenFunction($file, $functionName, $functionData); + } + } + } + } + public function markCodeAsExecutedByTestCase(string $testCaseId, RawCodeCoverageData $executedCode): void + { + foreach ($executedCode->lineCoverage() as $file => $lines) { + foreach ($lines as $k => $v) { + if ($v === Driver::LINE_EXECUTED) { + $this->lineCoverage[$file][$k][] = $testCaseId; + } + } + } + foreach ($executedCode->functionCoverage() as $file => $functions) { + foreach ($functions as $functionName => $functionData) { + foreach ($functionData['branches'] as $branchId => $branchData) { + if ($branchData['hit'] === Driver::BRANCH_HIT) { + $this->functionCoverage[$file][$functionName]['branches'][$branchId]['hit'][] = $testCaseId; + } + } + foreach ($functionData['paths'] as $pathId => $pathData) { + if ($pathData['hit'] === Driver::BRANCH_HIT) { + $this->functionCoverage[$file][$functionName]['paths'][$pathId]['hit'][] = $testCaseId; + } + } + } + } + } + public function setLineCoverage(array $lineCoverage): void + { + $this->lineCoverage = $lineCoverage; + } + public function lineCoverage(): array + { + ksort($this->lineCoverage); + return $this->lineCoverage; + } + public function setFunctionCoverage(array $functionCoverage): void + { + $this->functionCoverage = $functionCoverage; + } + public function functionCoverage(): array + { + ksort($this->functionCoverage); + return $this->functionCoverage; + } + public function coveredFiles(): array + { + ksort($this->lineCoverage); + return array_keys($this->lineCoverage); + } + public function renameFile(string $oldFile, string $newFile): void + { + $this->lineCoverage[$newFile] = $this->lineCoverage[$oldFile]; + if (isset($this->functionCoverage[$oldFile])) { + $this->functionCoverage[$newFile] = $this->functionCoverage[$oldFile]; + } + unset($this->lineCoverage[$oldFile], $this->functionCoverage[$oldFile]); + } + public function merge(self $newData): void + { + foreach ($newData->lineCoverage as $file => $lines) { + if (!isset($this->lineCoverage[$file])) { + $this->lineCoverage[$file] = $lines; + continue; + } + // we should compare the lines if any of two contains data + $compareLineNumbers = array_unique(array_merge(array_keys($this->lineCoverage[$file]), array_keys($newData->lineCoverage[$file]))); + foreach ($compareLineNumbers as $line) { + $thatPriority = $this->priorityForLine($newData->lineCoverage[$file], $line); + $thisPriority = $this->priorityForLine($this->lineCoverage[$file], $line); + if ($thatPriority > $thisPriority) { + $this->lineCoverage[$file][$line] = $newData->lineCoverage[$file][$line]; + } elseif ($thatPriority === $thisPriority && is_array($this->lineCoverage[$file][$line])) { + $this->lineCoverage[$file][$line] = array_unique(array_merge($this->lineCoverage[$file][$line], $newData->lineCoverage[$file][$line])); + } + } + } + foreach ($newData->functionCoverage as $file => $functions) { + if (!isset($this->functionCoverage[$file])) { + $this->functionCoverage[$file] = $functions; + continue; + } + foreach ($functions as $functionName => $functionData) { + if (isset($this->functionCoverage[$file][$functionName])) { + $this->initPreviouslySeenFunction($file, $functionName, $functionData); + } else { + $this->initPreviouslyUnseenFunction($file, $functionName, $functionData); + } + foreach ($functionData['branches'] as $branchId => $branchData) { + $this->functionCoverage[$file][$functionName]['branches'][$branchId]['hit'] = array_unique(array_merge($this->functionCoverage[$file][$functionName]['branches'][$branchId]['hit'], $branchData['hit'])); + } + foreach ($functionData['paths'] as $pathId => $pathData) { + $this->functionCoverage[$file][$functionName]['paths'][$pathId]['hit'] = array_unique(array_merge($this->functionCoverage[$file][$functionName]['paths'][$pathId]['hit'], $pathData['hit'])); + } + } + } + } + /** + * 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. + */ + private function priorityForLine(array $data, int $line): int + { + 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; + } + /** + * For a function we have never seen before, copy all data over and simply init the 'hit' array. + * + * @psalm-param XdebugFunctionCoverageType $functionData + */ + private function initPreviouslyUnseenFunction(string $file, string $functionName, array $functionData): void + { + $this->functionCoverage[$file][$functionName] = $functionData; + foreach (array_keys($functionData['branches']) as $branchId) { + $this->functionCoverage[$file][$functionName]['branches'][$branchId]['hit'] = []; + } + foreach (array_keys($functionData['paths']) as $pathId) { + $this->functionCoverage[$file][$functionName]['paths'][$pathId]['hit'] = []; + } + } + /** + * For a function we have seen before, only copy over and init the 'hit' array for any unseen branches and paths. + * Techniques such as mocking and where the contents of a file are different vary during tests (e.g. compiling + * containers) mean that the functions inside a file cannot be relied upon to be static. + * + * @psalm-param XdebugFunctionCoverageType $functionData + */ + private function initPreviouslySeenFunction(string $file, string $functionName, array $functionData): void + { + foreach ($functionData['branches'] as $branchId => $branchData) { + if (!isset($this->functionCoverage[$file][$functionName]['branches'][$branchId])) { + $this->functionCoverage[$file][$functionName]['branches'][$branchId] = $branchData; + $this->functionCoverage[$file][$functionName]['branches'][$branchId]['hit'] = []; + } + } + foreach ($functionData['paths'] as $pathId => $pathData) { + if (!isset($this->functionCoverage[$file][$functionName]['paths'][$pathId])) { + $this->functionCoverage[$file][$functionName]['paths'][$pathId] = $pathData; + $this->functionCoverage[$file][$functionName]['paths'][$pathId]['hit'] = []; + } + } + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnitPHAR\SebastianBergmann\CodeCoverage\Data; + +use function array_diff; +use function array_diff_key; +use function array_flip; +use function array_intersect; +use function array_intersect_key; +use function count; +use function explode; +use function file_get_contents; +use function in_array; +use function is_file; +use function preg_replace; +use function range; +use function str_ends_with; +use function str_starts_with; +use function trim; +use PHPUnitPHAR\SebastianBergmann\CodeCoverage\Driver\Driver; +use PHPUnitPHAR\SebastianBergmann\CodeCoverage\StaticAnalysis\FileAnalyser; +/** + * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage + * + * @psalm-import-type XdebugFunctionsCoverageType from \SebastianBergmann\CodeCoverage\Driver\XdebugDriver + * @psalm-import-type XdebugCodeCoverageWithoutPathCoverageType from \SebastianBergmann\CodeCoverage\Driver\XdebugDriver + * @psalm-import-type XdebugCodeCoverageWithPathCoverageType from \SebastianBergmann\CodeCoverage\Driver\XdebugDriver + */ +final class RawCodeCoverageData +{ + /** + * @var array> + */ + private static array $emptyLineCache = []; + /** + * @psalm-var XdebugCodeCoverageWithoutPathCoverageType + */ + private array $lineCoverage; + /** + * @psalm-var array + */ + private array $functionCoverage; + /** + * @psalm-param XdebugCodeCoverageWithoutPathCoverageType $rawCoverage + */ + public static function fromXdebugWithoutPathCoverage(array $rawCoverage): self + { + return new self($rawCoverage, []); + } + /** + * @psalm-param XdebugCodeCoverageWithPathCoverageType $rawCoverage + */ + public static function fromXdebugWithPathCoverage(array $rawCoverage): self + { + $lineCoverage = []; + $functionCoverage = []; + foreach ($rawCoverage as $file => $fileCoverageData) { + // Xdebug annotates the function name of traits, strip that off + foreach ($fileCoverageData['functions'] as $existingKey => $data) { + if (str_ends_with($existingKey, '}') && !str_starts_with($existingKey, '{')) { + // don't want to catch {main} + $newKey = preg_replace('/\{.*}$/', '', $existingKey); + $fileCoverageData['functions'][$newKey] = $data; + unset($fileCoverageData['functions'][$existingKey]); + } + } + $lineCoverage[$file] = $fileCoverageData['lines']; + $functionCoverage[$file] = $fileCoverageData['functions']; + } + return new self($lineCoverage, $functionCoverage); + } + public static function fromUncoveredFile(string $filename, FileAnalyser $analyser): self + { + $lineCoverage = []; + foreach ($analyser->executableLinesIn($filename) as $line => $branch) { + $lineCoverage[$line] = Driver::LINE_NOT_EXECUTED; + } + return new self([$filename => $lineCoverage], []); + } + /** + * @psalm-param XdebugCodeCoverageWithoutPathCoverageType $lineCoverage + * @psalm-param array $functionCoverage + */ + private function __construct(array $lineCoverage, array $functionCoverage) + { + $this->lineCoverage = $lineCoverage; + $this->functionCoverage = $functionCoverage; + $this->skipEmptyLines(); + } + public function clear(): void + { + $this->lineCoverage = $this->functionCoverage = []; + } + /** + * @psalm-return XdebugCodeCoverageWithoutPathCoverageType + */ + public function lineCoverage(): array + { + return $this->lineCoverage; + } + /** + * @psalm-return array + */ + public function functionCoverage(): array + { + return $this->functionCoverage; + } + public function removeCoverageDataForFile(string $filename): void + { + unset($this->lineCoverage[$filename], $this->functionCoverage[$filename]); + } + /** + * @param int[] $lines + */ + public function keepLineCoverageDataOnlyForLines(string $filename, array $lines): void + { + if (!isset($this->lineCoverage[$filename])) { + return; + } + $this->lineCoverage[$filename] = array_intersect_key($this->lineCoverage[$filename], array_flip($lines)); + } + /** + * @param int[] $linesToBranchMap + */ + public function markExecutableLineByBranch(string $filename, array $linesToBranchMap): void + { + if (!isset($this->lineCoverage[$filename])) { + return; + } + $linesByBranch = []; + foreach ($linesToBranchMap as $line => $branch) { + $linesByBranch[$branch][] = $line; + } + foreach ($this->lineCoverage[$filename] as $line => $lineStatus) { + if (!isset($linesToBranchMap[$line])) { + continue; + } + $branch = $linesToBranchMap[$line]; + if (!isset($linesByBranch[$branch])) { + continue; + } + foreach ($linesByBranch[$branch] as $lineInBranch) { + $this->lineCoverage[$filename][$lineInBranch] = $lineStatus; + } + if (Driver::LINE_EXECUTED === $lineStatus) { + unset($linesByBranch[$branch]); + } + } + } + /** + * @param int[] $lines + */ + public function keepFunctionCoverageDataOnlyForLines(string $filename, array $lines): void + { + if (!isset($this->functionCoverage[$filename])) { + return; + } + foreach ($this->functionCoverage[$filename] as $functionName => $functionData) { + foreach ($functionData['branches'] as $branchId => $branch) { + if (count(array_diff(range($branch['line_start'], $branch['line_end']), $lines)) > 0) { + unset($this->functionCoverage[$filename][$functionName]['branches'][$branchId]); + foreach ($functionData['paths'] as $pathId => $path) { + if (in_array($branchId, $path['path'], \true)) { + unset($this->functionCoverage[$filename][$functionName]['paths'][$pathId]); + } + } + } + } + } + } + /** + * @param int[] $lines + */ + public function removeCoverageDataForLines(string $filename, array $lines): void + { + if (empty($lines)) { + return; + } + if (!isset($this->lineCoverage[$filename])) { + return; + } + $this->lineCoverage[$filename] = array_diff_key($this->lineCoverage[$filename], array_flip($lines)); + if (isset($this->functionCoverage[$filename])) { + foreach ($this->functionCoverage[$filename] as $functionName => $functionData) { + foreach ($functionData['branches'] as $branchId => $branch) { + if (count(array_intersect($lines, range($branch['line_start'], $branch['line_end']))) > 0) { + unset($this->functionCoverage[$filename][$functionName]['branches'][$branchId]); + foreach ($functionData['paths'] as $pathId => $path) { + if (in_array($branchId, $path['path'], \true)) { + unset($this->functionCoverage[$filename][$functionName]['paths'][$pathId]); + } + } + } + } + } + } + } + /** + * At the end of a file, the PHP interpreter always sees an implicit return. Where this occurs in a file that has + * e.g. a class definition, that line cannot be invoked from a test and results in confusing coverage. This engine + * implementation detail therefore needs to be masked which is done here by simply ensuring that all empty lines + * are skipped over for coverage purposes. + * + * @see https://github.com/sebastianbergmann/php-code-coverage/issues/799 + */ + private function skipEmptyLines(): void + { + foreach ($this->lineCoverage as $filename => $coverage) { + foreach ($this->getEmptyLinesForFile($filename) as $emptyLine) { + unset($this->lineCoverage[$filename][$emptyLine]); + } + } + } + private function getEmptyLinesForFile(string $filename): array + { + if (!isset(self::$emptyLineCache[$filename])) { + self::$emptyLineCache[$filename] = []; + if (is_file($filename)) { + $sourceLines = explode("\n", file_get_contents($filename)); + foreach ($sourceLines as $line => $source) { + if (trim($source) === '') { + self::$emptyLineCache[$filename][] = $line + 1; + } + } + } + } + return self::$emptyLineCache[$filename]; + } +} +forLineCoverage($filter); - } - /** - * @throws NoCodeCoverageDriverWithPathCoverageSupportAvailableException - * @throws Xdebug2NotEnabledException - * @throws Xdebug3NotEnabledException - * @throws XdebugNotAvailableException - * - * @deprecated Use DriverSelector::forLineAndPathCoverage() instead - */ - public static function forLineAndPathCoverage(Filter $filter) : self - { - return (new Selector())->forLineAndPathCoverage($filter); - } - public function canCollectBranchAndPathCoverage() : bool + private bool $collectBranchAndPathCoverage = \false; + private bool $detectDeadCode = \false; + public function canCollectBranchAndPathCoverage(): bool { return \false; } - public function collectsBranchAndPathCoverage() : bool + public function collectsBranchAndPathCoverage(): bool { return $this->collectBranchAndPathCoverage; } /** * @throws BranchAndPathCoverageNotSupportedException */ - public function enableBranchAndPathCoverage() : void + public function enableBranchAndPathCoverage(): void { if (!$this->canCollectBranchAndPathCoverage()) { throw new BranchAndPathCoverageNotSupportedException(sprintf('%s does not support branch and path coverage', $this->nameAndVersion())); } $this->collectBranchAndPathCoverage = \true; } - public function disableBranchAndPathCoverage() : void + public function disableBranchAndPathCoverage(): void { $this->collectBranchAndPathCoverage = \false; } - public function canDetectDeadCode() : bool + public function canDetectDeadCode(): bool { return \false; } - public function detectsDeadCode() : bool + public function detectsDeadCode(): bool { return $this->detectDeadCode; } /** * @throws DeadCodeDetectionNotSupportedException */ - public function enableDeadCodeDetection() : void + public function enableDeadCodeDetection(): void { if (!$this->canDetectDeadCode()) { throw new DeadCodeDetectionNotSupportedException(sprintf('%s does not support dead code detection', $this->nameAndVersion())); } $this->detectDeadCode = \true; } - public function disableDeadCodeDetection() : void + public function disableDeadCodeDetection(): void { $this->detectDeadCode = \false; } - public abstract function nameAndVersion() : string; - public abstract function start() : void; - public abstract function stop() : RawCodeCoverageData; + abstract public function nameAndVersion(): string; + abstract public function start(): void; + abstract public function stop(): RawCodeCoverageData; } ensurePcovIsAvailable(); $this->filter = $filter; } - public function start() : void + public function start(): void { start(); } - public function stop() : RawCodeCoverageData + public function stop(): RawCodeCoverageData { stop(); $filesToCollectCoverageFor = waiting(); @@ -28045,89 +27807,18 @@ final class PcovDriver extends Driver } return RawCodeCoverageData::fromXdebugWithoutPathCoverage($collected); } - public function nameAndVersion() : string + public function nameAndVersion(): string { return 'PCOV ' . phpversion('pcov'); } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnitPHAR\SebastianBergmann\CodeCoverage\Driver; - -use const PHP_SAPI; -use const PHP_VERSION; -use function array_diff; -use function array_keys; -use function array_merge; -use function get_included_files; -use function phpdbg_end_oplog; -use function phpdbg_get_executable; -use function phpdbg_start_oplog; -use PHPUnitPHAR\SebastianBergmann\CodeCoverage\RawCodeCoverageData; -/** - * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage - */ -final class PhpdbgDriver extends Driver -{ /** - * @throws PhpdbgNotAvailableException + * @throws PcovNotAvailableException */ - public function __construct() - { - if (PHP_SAPI !== 'phpdbg') { - throw new PhpdbgNotAvailableException(); - } - } - public function start() : void - { - phpdbg_start_oplog(); - } - public function stop() : RawCodeCoverageData - { - 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 RawCodeCoverageData::fromXdebugWithoutPathCoverage($this->detectExecutedLines($fetchedLines, $dbgData)); - } - public function nameAndVersion() : string - { - return 'PHPDBG ' . PHP_VERSION; - } - private function detectExecutedLines(array $sourceLines, array $dbgData) : array + private function ensurePcovIsAvailable(): void { - 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; - } - } + if (!extension_loaded('pcov')) { + throw new PcovNotAvailableException(); } - return $sourceLines; } } hasPHPDBGCodeCoverage()) { - return new PhpdbgDriver(); - } if ($runtime->hasPCOV()) { return new PcovDriver($filter); } if ($runtime->hasXdebug()) { - if (version_compare(phpversion('xdebug'), '3', '>=')) { - $driver = new Xdebug3Driver($filter); - } else { - $driver = new Xdebug2Driver($filter); - } + $driver = new XdebugDriver($filter); $driver->enableDeadCodeDetection(); return $driver; } @@ -28181,18 +27861,13 @@ final class Selector } /** * @throws NoCodeCoverageDriverWithPathCoverageSupportAvailableException - * @throws Xdebug2NotEnabledException - * @throws Xdebug3NotEnabledException * @throws XdebugNotAvailableException + * @throws XdebugNotEnabledException */ - public function forLineAndPathCoverage(Filter $filter) : Driver + public function forLineAndPathCoverage(Filter $filter): Driver { if ((new Runtime())->hasXdebug()) { - if (version_compare(phpversion('xdebug'), '3', '>=')) { - $driver = new Xdebug3Driver($filter); - } else { - $driver = new Xdebug2Driver($filter); - } + $driver = new XdebugDriver($filter); $driver->enableDeadCodeDetection(); $driver->enableBranchAndPathCoverage(); return $driver; @@ -28218,63 +27893,74 @@ use const XDEBUG_CC_DEAD_CODE; use const XDEBUG_CC_UNUSED; use const XDEBUG_FILTER_CODE_COVERAGE; use const XDEBUG_PATH_INCLUDE; -use const XDEBUG_PATH_WHITELIST; -use function defined; +use function explode; use function extension_loaded; +use function getenv; +use function in_array; use function ini_get; use function phpversion; -use function sprintf; use function version_compare; use function xdebug_get_code_coverage; +use function xdebug_info; use function xdebug_set_filter; use function xdebug_start_code_coverage; use function xdebug_stop_code_coverage; +use PHPUnitPHAR\SebastianBergmann\CodeCoverage\Data\RawCodeCoverageData; use PHPUnitPHAR\SebastianBergmann\CodeCoverage\Filter; -use PHPUnitPHAR\SebastianBergmann\CodeCoverage\RawCodeCoverageData; /** * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage - */ -final class Xdebug2Driver extends Driver + * + * @see https://xdebug.org/docs/code_coverage#xdebug_get_code_coverage + * + * @psalm-type XdebugLinesCoverageType = array + * @psalm-type XdebugBranchCoverageType = array{ + * op_start: int, + * op_end: int, + * line_start: int, + * line_end: int, + * hit: int, + * out: array, + * out_hit: array, + * } + * @psalm-type XdebugPathCoverageType = array{ + * path: array, + * hit: int, + * } + * @psalm-type XdebugFunctionCoverageType = array{ + * branches: array, + * paths: array, + * } + * @psalm-type XdebugFunctionsCoverageType = array + * @psalm-type XdebugPathAndBranchesCoverageType = array{ + * lines: XdebugLinesCoverageType, + * functions: XdebugFunctionsCoverageType, + * } + * @psalm-type XdebugCodeCoverageWithoutPathCoverageType = array + * @psalm-type XdebugCodeCoverageWithPathCoverageType = array + */ +final class XdebugDriver extends Driver { /** - * @var bool - */ - private $pathCoverageIsMixedCoverage; - /** - * @throws WrongXdebugVersionException - * @throws Xdebug2NotEnabledException * @throws XdebugNotAvailableException + * @throws XdebugNotEnabledException */ public function __construct(Filter $filter) { - if (!extension_loaded('xdebug')) { - throw new XdebugNotAvailableException(); - } - if (version_compare(phpversion('xdebug'), '3', '>=')) { - throw new WrongXdebugVersionException(sprintf('This driver requires Xdebug 2 but version %s is loaded', phpversion('xdebug'))); - } - if (!ini_get('xdebug.coverage_enable')) { - throw new Xdebug2NotEnabledException(); - } + $this->ensureXdebugIsAvailable(); + $this->ensureXdebugCodeCoverageFeatureIsEnabled(); if (!$filter->isEmpty()) { - if (defined('XDEBUG_PATH_WHITELIST')) { - $listType = XDEBUG_PATH_WHITELIST; - } else { - $listType = XDEBUG_PATH_INCLUDE; - } - xdebug_set_filter(XDEBUG_FILTER_CODE_COVERAGE, $listType, $filter->files()); + xdebug_set_filter(XDEBUG_FILTER_CODE_COVERAGE, XDEBUG_PATH_INCLUDE, $filter->files()); } - $this->pathCoverageIsMixedCoverage = version_compare(phpversion('xdebug'), '2.9.6', '<'); } - public function canCollectBranchAndPathCoverage() : bool + public function canCollectBranchAndPathCoverage(): bool { return \true; } - public function canDetectDeadCode() : bool + public function canDetectDeadCode(): bool { return \true; } - public function start() : void + public function start(): void { $flags = XDEBUG_CC_UNUSED; if ($this->detectsDeadCode() || $this->collectsBranchAndPathCoverage()) { @@ -28285,115 +27971,48 @@ final class Xdebug2Driver extends Driver } xdebug_start_code_coverage($flags); } - public function stop() : RawCodeCoverageData + public function stop(): RawCodeCoverageData { $data = xdebug_get_code_coverage(); xdebug_stop_code_coverage(); if ($this->collectsBranchAndPathCoverage()) { - if ($this->pathCoverageIsMixedCoverage) { - return RawCodeCoverageData::fromXdebugWithMixedCoverage($data); - } + /* @var XdebugCodeCoverageWithPathCoverageType $data */ return RawCodeCoverageData::fromXdebugWithPathCoverage($data); } + /* @var XdebugCodeCoverageWithoutPathCoverageType $data */ return RawCodeCoverageData::fromXdebugWithoutPathCoverage($data); } - public function nameAndVersion() : string + public function nameAndVersion(): string { return 'Xdebug ' . phpversion('xdebug'); } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnitPHAR\SebastianBergmann\CodeCoverage\Driver; - -use const XDEBUG_CC_BRANCH_CHECK; -use const XDEBUG_CC_DEAD_CODE; -use const XDEBUG_CC_UNUSED; -use const XDEBUG_FILTER_CODE_COVERAGE; -use const XDEBUG_PATH_INCLUDE; -use function explode; -use function extension_loaded; -use function getenv; -use function in_array; -use function ini_get; -use function phpversion; -use function sprintf; -use function version_compare; -use function xdebug_get_code_coverage; -use function xdebug_set_filter; -use function xdebug_start_code_coverage; -use function xdebug_stop_code_coverage; -use PHPUnitPHAR\SebastianBergmann\CodeCoverage\Filter; -use PHPUnitPHAR\SebastianBergmann\CodeCoverage\RawCodeCoverageData; -/** - * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage - */ -final class Xdebug3Driver extends Driver -{ /** - * @throws WrongXdebugVersionException - * @throws Xdebug3NotEnabledException * @throws XdebugNotAvailableException */ - public function __construct(Filter $filter) + private function ensureXdebugIsAvailable(): void { if (!extension_loaded('xdebug')) { throw new XdebugNotAvailableException(); } - if (version_compare(phpversion('xdebug'), '3', '<')) { - throw new WrongXdebugVersionException(sprintf('This driver requires Xdebug 3 but version %s is loaded', phpversion('xdebug'))); + } + /** + * @throws XdebugNotEnabledException + */ + private function ensureXdebugCodeCoverageFeatureIsEnabled(): void + { + if (version_compare(phpversion('xdebug'), '3.1', '>=')) { + if (!in_array('coverage', xdebug_info('mode'), \true)) { + throw new XdebugNotEnabledException(); + } + return; } $mode = getenv('XDEBUG_MODE'); if ($mode === \false || $mode === '') { $mode = ini_get('xdebug.mode'); } if ($mode === \false || !in_array('coverage', explode(',', $mode), \true)) { - throw new Xdebug3NotEnabledException(); - } - if (!$filter->isEmpty()) { - xdebug_set_filter(XDEBUG_FILTER_CODE_COVERAGE, XDEBUG_PATH_INCLUDE, $filter->files()); - } - } - public function canCollectBranchAndPathCoverage() : bool - { - return \true; - } - public function canDetectDeadCode() : bool - { - return \true; - } - public function start() : void - { - $flags = XDEBUG_CC_UNUSED; - if ($this->detectsDeadCode() || $this->collectsBranchAndPathCoverage()) { - $flags |= XDEBUG_CC_DEAD_CODE; + throw new XdebugNotEnabledException(); } - if ($this->collectsBranchAndPathCoverage()) { - $flags |= XDEBUG_CC_BRANCH_CHECK; - } - xdebug_start_code_coverage($flags); - } - public function stop() : RawCodeCoverageData - { - $data = xdebug_get_code_coverage(); - xdebug_stop_code_coverage(); - if ($this->collectsBranchAndPathCoverage()) { - return RawCodeCoverageData::fromXdebugWithPathCoverage($data); - } - return RawCodeCoverageData::fromXdebugWithoutPathCoverage($data); - } - public function nameAndVersion() : string - { - return 'Xdebug ' . phpversion('xdebug'); } } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnitPHAR\SebastianBergmann\CodeCoverage; + final class InvalidArgumentException extends \InvalidArgumentException implements Exception { } @@ -28587,28 +28223,6 @@ final class PcovNotAvailableException extends RuntimeException implements Except } - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnitPHAR\SebastianBergmann\CodeCoverage\Driver; - -use RuntimeException; -use PHPUnitPHAR\SebastianBergmann\CodeCoverage\Exception; -final class PhpdbgNotAvailableException extends RuntimeException implements Exception -{ - public function __construct() - { - parent::__construct('The PHPDBG SAPI is not available'); - } -} - + */ + private readonly array $unintentionallyCoveredUnits; + /** + * @param list $unintentionallyCoveredUnits */ - private $unintentionallyCoveredUnits; public function __construct(array $unintentionallyCoveredUnits) { $this->unintentionallyCoveredUnits = $unintentionallyCoveredUnits; parent::__construct($this->toString()); } - public function getUnintentionallyCoveredUnits() : array + /** + * @return list + */ + public function getUnintentionallyCoveredUnits(): array { return $this->unintentionallyCoveredUnits; } - private function toString() : string + private function toString(): string { $message = ''; foreach ($this->unintentionallyCoveredUnits as $unit) { @@ -28759,51 +28379,11 @@ namespace PHPUnitPHAR\SebastianBergmann\CodeCoverage\Driver; use RuntimeException; use PHPUnitPHAR\SebastianBergmann\CodeCoverage\Exception; -final class WrongXdebugVersionException extends RuntimeException implements Exception -{ -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnitPHAR\SebastianBergmann\CodeCoverage\Driver; - -use RuntimeException; -use PHPUnitPHAR\SebastianBergmann\CodeCoverage\Exception; -final class Xdebug2NotEnabledException extends RuntimeException implements Exception -{ - public function __construct() - { - parent::__construct('xdebug.coverage_enable=On has to be set'); - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnitPHAR\SebastianBergmann\CodeCoverage\Driver; - -use RuntimeException; -use PHPUnitPHAR\SebastianBergmann\CodeCoverage\Exception; -final class Xdebug3NotEnabledException extends RuntimeException implements Exception +final class XdebugNotAvailableException extends RuntimeException implements Exception { public function __construct() { - parent::__construct('XDEBUG_MODE=coverage or xdebug.mode=coverage has to be set'); + parent::__construct('The Xdebug extension is not available'); } } */ - private $files = []; + private array $files = []; /** * @psalm-var array */ - private $isFileCache = []; - public function includeDirectory(string $directory, string $suffix = '.php', string $prefix = '') : void + private array $isFileCache = []; + /** + * @deprecated + */ + public function includeDirectory(string $directory, string $suffix = '.php', string $prefix = ''): void { foreach ((new FileIteratorFacade())->getFilesAsArray($directory, $suffix, $prefix) as $file) { $this->includeFile($file); @@ -28882,13 +28466,13 @@ final class Filter /** * @psalm-param list $files */ - public function includeFiles(array $filenames) : void + public function includeFiles(array $filenames): void { foreach ($filenames as $filename) { $this->includeFile($filename); } } - public function includeFile(string $filename) : void + public function includeFile(string $filename): void { $filename = realpath($filename); if (!$filename) { @@ -28896,13 +28480,19 @@ final class Filter } $this->files[$filename] = \true; } - public function excludeDirectory(string $directory, string $suffix = '.php', string $prefix = '') : void + /** + * @deprecated + */ + public function excludeDirectory(string $directory, string $suffix = '.php', string $prefix = ''): void { foreach ((new FileIteratorFacade())->getFilesAsArray($directory, $suffix, $prefix) as $file) { $this->excludeFile($file); } } - public function excludeFile(string $filename) : void + /** + * @deprecated + */ + public function excludeFile(string $filename): void { $filename = realpath($filename); if (!$filename || !isset($this->files[$filename])) { @@ -28910,12 +28500,12 @@ final class Filter } unset($this->files[$filename]); } - public function isFile(string $filename) : bool + public function isFile(string $filename): bool { if (isset($this->isFileCache[$filename])) { return $this->isFileCache[$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) { + if ($filename === '-' || str_starts_with($filename, 'vfs://') || str_contains($filename, 'xdebug://debug-eval') || str_contains($filename, 'eval()\'d code') || str_contains($filename, 'runtime-created function') || str_contains($filename, 'runkit created function') || str_contains($filename, 'assert code') || str_contains($filename, 'regexp code') || str_contains($filename, 'Standard input code')) { $isFile = \false; } else { $isFile = is_file($filename); @@ -28923,25 +28513,25 @@ final class Filter $this->isFileCache[$filename] = $isFile; return $isFile; } - public function isExcluded(string $filename) : bool + public function isExcluded(string $filename): bool { return !isset($this->files[$filename]) || !$this->isFile($filename); } /** * @psalm-return list */ - public function files() : array + public function files(): array { return array_keys($this->files); } - public function isEmpty() : bool + public function isEmpty(): bool { return empty($this->files); } } BSD 3-Clause License -Copyright (c) 2009-2023, Sebastian Bergmann +Copyright (c) 2009-2024, Sebastian Bergmann All rights reserved. Redistribution and use in source and binary forms, with or without @@ -28983,168 +28573,166 @@ namespace PHPUnitPHAR\SebastianBergmann\CodeCoverage\Node; use const DIRECTORY_SEPARATOR; use function array_merge; +use function str_ends_with; use function str_replace; use function substr; use Countable; use PHPUnitPHAR\SebastianBergmann\CodeCoverage\Util\Percentage; /** * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage + * + * @psalm-import-type LinesOfCodeType from \SebastianBergmann\CodeCoverage\StaticAnalysis\FileAnalyser + * @psalm-import-type ProcessedFunctionType from \SebastianBergmann\CodeCoverage\Node\File + * @psalm-import-type ProcessedClassType from \SebastianBergmann\CodeCoverage\Node\File + * @psalm-import-type ProcessedTraitType from \SebastianBergmann\CodeCoverage\Node\File */ abstract class AbstractNode implements Countable { - /** - * @var string - */ - private $name; - /** - * @var string - */ - private $pathAsString; - /** - * @var array - */ - private $pathAsArray; - /** - * @var AbstractNode - */ - private $parent; - /** - * @var string - */ - private $id; + private readonly string $name; + private string $pathAsString; + private array $pathAsArray; + private readonly ?AbstractNode $parent; + private string $id; public function __construct(string $name, ?self $parent = null) { - if (substr($name, -1) === DIRECTORY_SEPARATOR) { + if (str_ends_with($name, DIRECTORY_SEPARATOR)) { $name = substr($name, 0, -1); } $this->name = $name; $this->parent = $parent; + $this->processId(); + $this->processPath(); } - public function name() : string + public function name(): string { return $this->name; } - public function id() : string + public function id(): string { - if ($this->id === null) { - $parent = $this->parent(); - if ($parent === null) { - $this->id = 'index'; - } else { - $parentId = $parent->id(); - if ($parentId === 'index') { - $this->id = str_replace(':', '_', $this->name); - } else { - $this->id = $parentId . '/' . $this->name; - } - } - } return $this->id; } - public function pathAsString() : string + public function pathAsString(): string { - if ($this->pathAsString === null) { - if ($this->parent === null) { - $this->pathAsString = $this->name; - } else { - $this->pathAsString = $this->parent->pathAsString() . DIRECTORY_SEPARATOR . $this->name; - } - } return $this->pathAsString; } - public function pathAsArray() : array + public function pathAsArray(): array { - if ($this->pathAsArray === null) { - if ($this->parent === null) { - $this->pathAsArray = []; - } else { - $this->pathAsArray = $this->parent->pathAsArray(); - } - $this->pathAsArray[] = $this; - } return $this->pathAsArray; } - public function parent() : ?self + public function parent(): ?self { return $this->parent; } - public function percentageOfTestedClasses() : Percentage + public function percentageOfTestedClasses(): Percentage { return Percentage::fromFractionAndTotal($this->numberOfTestedClasses(), $this->numberOfClasses()); } - public function percentageOfTestedTraits() : Percentage + public function percentageOfTestedTraits(): Percentage { return Percentage::fromFractionAndTotal($this->numberOfTestedTraits(), $this->numberOfTraits()); } - public function percentageOfTestedClassesAndTraits() : Percentage + public function percentageOfTestedClassesAndTraits(): Percentage { return Percentage::fromFractionAndTotal($this->numberOfTestedClassesAndTraits(), $this->numberOfClassesAndTraits()); } - public function percentageOfTestedFunctions() : Percentage + public function percentageOfTestedFunctions(): Percentage { return Percentage::fromFractionAndTotal($this->numberOfTestedFunctions(), $this->numberOfFunctions()); } - public function percentageOfTestedMethods() : Percentage + public function percentageOfTestedMethods(): Percentage { return Percentage::fromFractionAndTotal($this->numberOfTestedMethods(), $this->numberOfMethods()); } - public function percentageOfTestedFunctionsAndMethods() : Percentage + public function percentageOfTestedFunctionsAndMethods(): Percentage { return Percentage::fromFractionAndTotal($this->numberOfTestedFunctionsAndMethods(), $this->numberOfFunctionsAndMethods()); } - public function percentageOfExecutedLines() : Percentage + public function percentageOfExecutedLines(): Percentage { return Percentage::fromFractionAndTotal($this->numberOfExecutedLines(), $this->numberOfExecutableLines()); } - public function percentageOfExecutedBranches() : Percentage + public function percentageOfExecutedBranches(): Percentage { return Percentage::fromFractionAndTotal($this->numberOfExecutedBranches(), $this->numberOfExecutableBranches()); } - public function percentageOfExecutedPaths() : Percentage + public function percentageOfExecutedPaths(): Percentage { return Percentage::fromFractionAndTotal($this->numberOfExecutedPaths(), $this->numberOfExecutablePaths()); } - public function numberOfClassesAndTraits() : int + public function numberOfClassesAndTraits(): int { return $this->numberOfClasses() + $this->numberOfTraits(); } - public function numberOfTestedClassesAndTraits() : int + public function numberOfTestedClassesAndTraits(): int { return $this->numberOfTestedClasses() + $this->numberOfTestedTraits(); } - public function classesAndTraits() : array + public function classesAndTraits(): array { return array_merge($this->classes(), $this->traits()); } - public function numberOfFunctionsAndMethods() : int + public function numberOfFunctionsAndMethods(): int { return $this->numberOfFunctions() + $this->numberOfMethods(); } - public function numberOfTestedFunctionsAndMethods() : int + public function numberOfTestedFunctionsAndMethods(): int { return $this->numberOfTestedFunctions() + $this->numberOfTestedMethods(); } - public abstract function classes() : array; - public abstract function traits() : array; - public abstract function functions() : array; /** - * @psalm-return array{linesOfCode: int, commentLinesOfCode: int, nonCommentLinesOfCode: int} + * @psalm-return array + */ + abstract public function classes(): array; + /** + * @psalm-return array */ - public abstract function linesOfCode() : array; - public abstract function numberOfExecutableLines() : int; - public abstract function numberOfExecutedLines() : int; - public abstract function numberOfExecutableBranches() : int; - public abstract function numberOfExecutedBranches() : int; - public abstract function numberOfExecutablePaths() : int; - public abstract function numberOfExecutedPaths() : int; - public abstract function numberOfClasses() : int; - public abstract function numberOfTestedClasses() : int; - public abstract function numberOfTraits() : int; - public abstract function numberOfTestedTraits() : int; - public abstract function numberOfMethods() : int; - public abstract function numberOfTestedMethods() : int; - public abstract function numberOfFunctions() : int; - public abstract function numberOfTestedFunctions() : int; + abstract public function traits(): array; + /** + * @psalm-return array + */ + abstract public function functions(): array; + /** + * @psalm-return LinesOfCodeType + */ + abstract public function linesOfCode(): array; + abstract public function numberOfExecutableLines(): int; + abstract public function numberOfExecutedLines(): int; + abstract public function numberOfExecutableBranches(): int; + abstract public function numberOfExecutedBranches(): int; + abstract public function numberOfExecutablePaths(): int; + abstract public function numberOfExecutedPaths(): int; + abstract public function numberOfClasses(): int; + abstract public function numberOfTestedClasses(): int; + abstract public function numberOfTraits(): int; + abstract public function numberOfTestedTraits(): int; + abstract public function numberOfMethods(): int; + abstract public function numberOfTestedMethods(): int; + abstract public function numberOfFunctions(): int; + abstract public function numberOfTestedFunctions(): int; + private function processId(): void + { + if ($this->parent === null) { + $this->id = 'index'; + return; + } + $parentId = $this->parent->id(); + if ($parentId === 'index') { + $this->id = str_replace(':', '_', $this->name); + } else { + $this->id = $parentId . '/' . $this->name; + } + } + private function processPath(): void + { + if ($this->parent === null) { + $this->pathAsArray = [$this]; + $this->pathAsString = $this->name; + return; + } + $this->pathAsArray = $this->parent->pathAsArray(); + $this->pathAsString = $this->parent->pathAsString() . DIRECTORY_SEPARATOR . $this->name; + $this->pathAsArray[] = $this; + } } analyser = $analyser; } - public function build(CodeCoverage $coverage) : Directory + public function build(CodeCoverage $coverage): Directory { $data = clone $coverage->getData(); // clone because path munging is destructive to the original data @@ -29195,11 +28783,14 @@ final class Builder $this->addItems($root, $this->buildDirectoryStructure($data), $coverage->getTests()); return $root; } - private function addItems(Directory $root, array $items, array $tests) : void + /** + * @psalm-param array $tests + */ + private function addItems(Directory $root, array $items, array $tests): void { foreach ($items as $key => $value) { $key = (string) $key; - if (substr($key, -2) === '/f') { + if (str_ends_with($key, '/f')) { $key = substr($key, 0, -2); $filename = $root->pathAsString() . DIRECTORY_SEPARATOR . $key; if (is_file($filename)) { @@ -29250,8 +28841,10 @@ final class Builder * ) * ) * + * + * @psalm-return array, functionCoverage: array>}>> */ - private function buildDirectoryStructure(ProcessedCodeCoverageData $data) : array + private function buildDirectoryStructure(ProcessedCodeCoverageData $data): array { $result = []; foreach ($data->coveredFiles() as $originalPath) { @@ -29306,7 +28899,7 @@ final class Builder * ) * */ - private function reducePaths(ProcessedCodeCoverageData $coverage) : string + private function reducePaths(ProcessedCodeCoverageData $coverage): string { if (empty($coverage->coveredFiles())) { return '.'; @@ -29321,7 +28914,7 @@ final class Builder $max = count($paths); for ($i = 0; $i < $max; $i++) { // strip phar:// prefixes - if (strpos($paths[$i], 'phar://') === 0) { + if (str_starts_with($paths[$i], 'phar://')) { $paths[$i] = substr($paths[$i], 7); $paths[$i] = str_replace('/', DIRECTORY_SEPARATOR, $paths[$i]); } @@ -29376,20 +28969,14 @@ use function sprintf; */ final class CrapIndex { - /** - * @var int - */ - private $cyclomaticComplexity; - /** - * @var float - */ - private $codeCoverage; + private readonly int $cyclomaticComplexity; + private readonly float $codeCoverage; public function __construct(int $cyclomaticComplexity, float $codeCoverage) { $this->cyclomaticComplexity = $cyclomaticComplexity; $this->codeCoverage = $codeCoverage; } - public function asString() : string + public function asString(): string { if ($this->codeCoverage === 0.0) { return (string) ($this->cyclomaticComplexity ** 2 + $this->cyclomaticComplexity); @@ -29419,98 +29006,46 @@ use IteratorAggregate; use RecursiveIteratorIterator; /** * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage + * + * @psalm-import-type LinesOfCodeType from \SebastianBergmann\CodeCoverage\StaticAnalysis\FileAnalyser */ 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; - /** - * @psalm-var null|array{linesOfCode: int, commentLinesOfCode: int, nonCommentLinesOfCode: int} - */ - private $linesOfCode; - /** - * @var int - */ - private $numFiles = -1; - /** - * @var int - */ - private $numExecutableLines = -1; - /** - * @var int - */ - private $numExecutedLines = -1; - /** - * @var int - */ - private $numExecutableBranches = -1; - /** - * @var int - */ - private $numExecutedBranches = -1; - /** - * @var int - */ - private $numExecutablePaths = -1; - /** - * @var int - */ - private $numExecutedPaths = -1; - /** - * @var int - */ - private $numClasses = -1; - /** - * @var int - */ - private $numTestedClasses = -1; - /** - * @var int - */ - private $numTraits = -1; - /** - * @var int + * @var list */ - private $numTestedTraits = -1; + private array $children = []; /** - * @var int + * @var list */ - private $numMethods = -1; + private array $directories = []; /** - * @var int + * @var list */ - private $numTestedMethods = -1; + private array $files = []; + private ?array $classes = null; + private ?array $traits = null; + private ?array $functions = null; /** - * @var int - */ - private $numFunctions = -1; - /** - * @var int + * @psalm-var null|LinesOfCodeType */ - private $numTestedFunctions = -1; - public function count() : int + private ?array $linesOfCode = null; + private int $numFiles = -1; + private int $numExecutableLines = -1; + private int $numExecutedLines = -1; + private int $numExecutableBranches = -1; + private int $numExecutedBranches = -1; + private int $numExecutablePaths = -1; + private int $numExecutedPaths = -1; + private int $numClasses = -1; + private int $numTestedClasses = -1; + private int $numTraits = -1; + private int $numTestedTraits = -1; + private int $numMethods = -1; + private int $numTestedMethods = -1; + private int $numFunctions = -1; + private int $numTestedFunctions = -1; + public function count(): int { if ($this->numFiles === -1) { $this->numFiles = 0; @@ -29520,37 +29055,37 @@ final class Directory extends AbstractNode implements IteratorAggregate } return $this->numFiles; } - public function getIterator() : RecursiveIteratorIterator + public function getIterator(): RecursiveIteratorIterator { return new RecursiveIteratorIterator(new Iterator($this), RecursiveIteratorIterator::SELF_FIRST); } - public function addDirectory(string $name) : self + public function addDirectory(string $name): self { $directory = new self($name, $this); $this->children[] = $directory; $this->directories[] =& $this->children[count($this->children) - 1]; return $directory; } - public function addFile(File $file) : void + public function addFile(File $file): void { $this->children[] = $file; $this->files[] =& $this->children[count($this->children) - 1]; $this->numExecutableLines = -1; $this->numExecutedLines = -1; } - public function directories() : array + public function directories(): array { return $this->directories; } - public function files() : array + public function files(): array { return $this->files; } - public function children() : array + public function children(): array { return $this->children; } - public function classes() : array + public function classes(): array { if ($this->classes === null) { $this->classes = []; @@ -29560,7 +29095,7 @@ final class Directory extends AbstractNode implements IteratorAggregate } return $this->classes; } - public function traits() : array + public function traits(): array { if ($this->traits === null) { $this->traits = []; @@ -29570,7 +29105,7 @@ final class Directory extends AbstractNode implements IteratorAggregate } return $this->traits; } - public function functions() : array + public function functions(): array { if ($this->functions === null) { $this->functions = []; @@ -29581,9 +29116,9 @@ final class Directory extends AbstractNode implements IteratorAggregate return $this->functions; } /** - * @psalm-return array{linesOfCode: int, commentLinesOfCode: int, nonCommentLinesOfCode: int} + * @psalm-return LinesOfCodeType */ - public function linesOfCode() : array + public function linesOfCode(): array { if ($this->linesOfCode === null) { $this->linesOfCode = ['linesOfCode' => 0, 'commentLinesOfCode' => 0, 'nonCommentLinesOfCode' => 0]; @@ -29596,7 +29131,7 @@ final class Directory extends AbstractNode implements IteratorAggregate } return $this->linesOfCode; } - public function numberOfExecutableLines() : int + public function numberOfExecutableLines(): int { if ($this->numExecutableLines === -1) { $this->numExecutableLines = 0; @@ -29606,7 +29141,7 @@ final class Directory extends AbstractNode implements IteratorAggregate } return $this->numExecutableLines; } - public function numberOfExecutedLines() : int + public function numberOfExecutedLines(): int { if ($this->numExecutedLines === -1) { $this->numExecutedLines = 0; @@ -29616,7 +29151,7 @@ final class Directory extends AbstractNode implements IteratorAggregate } return $this->numExecutedLines; } - public function numberOfExecutableBranches() : int + public function numberOfExecutableBranches(): int { if ($this->numExecutableBranches === -1) { $this->numExecutableBranches = 0; @@ -29626,7 +29161,7 @@ final class Directory extends AbstractNode implements IteratorAggregate } return $this->numExecutableBranches; } - public function numberOfExecutedBranches() : int + public function numberOfExecutedBranches(): int { if ($this->numExecutedBranches === -1) { $this->numExecutedBranches = 0; @@ -29636,7 +29171,7 @@ final class Directory extends AbstractNode implements IteratorAggregate } return $this->numExecutedBranches; } - public function numberOfExecutablePaths() : int + public function numberOfExecutablePaths(): int { if ($this->numExecutablePaths === -1) { $this->numExecutablePaths = 0; @@ -29646,7 +29181,7 @@ final class Directory extends AbstractNode implements IteratorAggregate } return $this->numExecutablePaths; } - public function numberOfExecutedPaths() : int + public function numberOfExecutedPaths(): int { if ($this->numExecutedPaths === -1) { $this->numExecutedPaths = 0; @@ -29656,7 +29191,7 @@ final class Directory extends AbstractNode implements IteratorAggregate } return $this->numExecutedPaths; } - public function numberOfClasses() : int + public function numberOfClasses(): int { if ($this->numClasses === -1) { $this->numClasses = 0; @@ -29666,7 +29201,7 @@ final class Directory extends AbstractNode implements IteratorAggregate } return $this->numClasses; } - public function numberOfTestedClasses() : int + public function numberOfTestedClasses(): int { if ($this->numTestedClasses === -1) { $this->numTestedClasses = 0; @@ -29676,7 +29211,7 @@ final class Directory extends AbstractNode implements IteratorAggregate } return $this->numTestedClasses; } - public function numberOfTraits() : int + public function numberOfTraits(): int { if ($this->numTraits === -1) { $this->numTraits = 0; @@ -29686,7 +29221,7 @@ final class Directory extends AbstractNode implements IteratorAggregate } return $this->numTraits; } - public function numberOfTestedTraits() : int + public function numberOfTestedTraits(): int { if ($this->numTestedTraits === -1) { $this->numTestedTraits = 0; @@ -29696,7 +29231,7 @@ final class Directory extends AbstractNode implements IteratorAggregate } return $this->numTestedTraits; } - public function numberOfMethods() : int + public function numberOfMethods(): int { if ($this->numMethods === -1) { $this->numMethods = 0; @@ -29706,7 +29241,7 @@ final class Directory extends AbstractNode implements IteratorAggregate } return $this->numMethods; } - public function numberOfTestedMethods() : int + public function numberOfTestedMethods(): int { if ($this->numTestedMethods === -1) { $this->numTestedMethods = 0; @@ -29716,7 +29251,7 @@ final class Directory extends AbstractNode implements IteratorAggregate } return $this->numTestedMethods; } - public function numberOfFunctions() : int + public function numberOfFunctions(): int { if ($this->numFunctions === -1) { $this->numFunctions = 0; @@ -29726,7 +29261,7 @@ final class Directory extends AbstractNode implements IteratorAggregate } return $this->numFunctions; } - public function numberOfTestedFunctions() : int + public function numberOfTestedFunctions(): int { if ($this->numTestedFunctions === -1) { $this->numTestedFunctions = 0; @@ -29755,95 +29290,128 @@ use function count; use function range; /** * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage + * + * @psalm-import-type CodeUnitFunctionType from \SebastianBergmann\CodeCoverage\StaticAnalysis\CodeUnitFindingVisitor + * @psalm-import-type CodeUnitMethodType from \SebastianBergmann\CodeCoverage\StaticAnalysis\CodeUnitFindingVisitor + * @psalm-import-type CodeUnitClassType from \SebastianBergmann\CodeCoverage\StaticAnalysis\CodeUnitFindingVisitor + * @psalm-import-type CodeUnitTraitType from \SebastianBergmann\CodeCoverage\StaticAnalysis\CodeUnitFindingVisitor + * @psalm-import-type LinesOfCodeType from \SebastianBergmann\CodeCoverage\StaticAnalysis\FileAnalyser + * @psalm-import-type LinesType from \SebastianBergmann\CodeCoverage\StaticAnalysis\FileAnalyser + * + * @psalm-type ProcessedFunctionType = array{ + * functionName: string, + * namespace: string, + * signature: string, + * startLine: int, + * endLine: int, + * executableLines: int, + * executedLines: int, + * executableBranches: int, + * executedBranches: int, + * executablePaths: int, + * executedPaths: int, + * ccn: int, + * coverage: int|float, + * crap: int|string, + * link: string + * } + * @psalm-type ProcessedMethodType = array{ + * methodName: string, + * visibility: string, + * signature: string, + * startLine: int, + * endLine: int, + * executableLines: int, + * executedLines: int, + * executableBranches: int, + * executedBranches: int, + * executablePaths: int, + * executedPaths: int, + * ccn: int, + * coverage: float|int, + * crap: int|string, + * link: string + * } + * @psalm-type ProcessedClassType = array{ + * className: string, + * namespace: string, + * methods: array, + * startLine: int, + * executableLines: int, + * executedLines: int, + * executableBranches: int, + * executedBranches: int, + * executablePaths: int, + * executedPaths: int, + * ccn: int, + * coverage: int|float, + * crap: int|string, + * link: string + * } + * @psalm-type ProcessedTraitType = array{ + * traitName: string, + * namespace: string, + * methods: array, + * startLine: int, + * executableLines: int, + * executedLines: int, + * executableBranches: int, + * executedBranches: int, + * executablePaths: int, + * executedPaths: int, + * ccn: int, + * coverage: float|int, + * crap: int|string, + * link: string + * } */ final class File extends AbstractNode { /** - * @var array - */ - private $lineCoverageData; - /** - * @var array - */ - private $functionCoverageData; - /** - * @var array - */ - private $testData; - /** - * @var int - */ - private $numExecutableLines = 0; - /** - * @var int - */ - private $numExecutedLines = 0; - /** - * @var int - */ - private $numExecutableBranches = 0; - /** - * @var int - */ - private $numExecutedBranches = 0; - /** - * @var int - */ - private $numExecutablePaths = 0; - /** - * @var int - */ - private $numExecutedPaths = 0; - /** - * @var array - */ - private $classes = []; - /** - * @var array - */ - private $traits = []; - /** - * @var array - */ - private $functions = []; - /** - * @psalm-var array{linesOfCode: int, commentLinesOfCode: int, nonCommentLinesOfCode: int} - */ - private $linesOfCode; - /** - * @var int - */ - private $numClasses; - /** - * @var int - */ - private $numTestedClasses = 0; - /** - * @var int + * @psalm-var array> */ - private $numTraits; + private array $lineCoverageData; + private array $functionCoverageData; + private readonly array $testData; + private int $numExecutableLines = 0; + private int $numExecutedLines = 0; + private int $numExecutableBranches = 0; + private int $numExecutedBranches = 0; + private int $numExecutablePaths = 0; + private int $numExecutedPaths = 0; /** - * @var int + * @psalm-var array */ - private $numTestedTraits = 0; + private array $classes = []; /** - * @var int + * @psalm-var array */ - private $numMethods; + private array $traits = []; /** - * @var int + * @psalm-var array */ - private $numTestedMethods; + private array $functions = []; /** - * @var int + * @psalm-var LinesOfCodeType */ - private $numTestedFunctions; + private readonly array $linesOfCode; + private ?int $numClasses = null; + private int $numTestedClasses = 0; + private ?int $numTraits = null; + private int $numTestedTraits = 0; + private ?int $numMethods = null; + private ?int $numTestedMethods = null; + private ?int $numTestedFunctions = null; /** - * @var array + * @var array */ - private $codeUnitsByLine = []; + private array $codeUnitsByLine = []; /** - * @psalm-param array{linesOfCode: int, commentLinesOfCode: int, nonCommentLinesOfCode: int} $linesOfCode + * @psalm-param array> $lineCoverageData + * @psalm-param LinesOfCodeType $linesOfCode + * @psalm-param array $classes + * @psalm-param array $traits + * @psalm-param array $functions */ public function __construct(string $name, AbstractNode $parent, array $lineCoverageData, array $functionCoverageData, array $testData, array $classes, array $traits, array $functions, array $linesOfCode) { @@ -29854,66 +29422,66 @@ final class File extends AbstractNode $this->linesOfCode = $linesOfCode; $this->calculateStatistics($classes, $traits, $functions); } - public function count() : int + public function count(): int { return 1; } - public function lineCoverageData() : array + /** + * @psalm-return array> + */ + public function lineCoverageData(): array { return $this->lineCoverageData; } - public function functionCoverageData() : array + public function functionCoverageData(): array { return $this->functionCoverageData; } - public function testData() : array + public function testData(): array { return $this->testData; } - public function classes() : array + public function classes(): array { return $this->classes; } - public function traits() : array + public function traits(): array { return $this->traits; } - public function functions() : array + public function functions(): array { return $this->functions; } - /** - * @psalm-return array{linesOfCode: int, commentLinesOfCode: int, nonCommentLinesOfCode: int} - */ - public function linesOfCode() : array + public function linesOfCode(): array { return $this->linesOfCode; } - public function numberOfExecutableLines() : int + public function numberOfExecutableLines(): int { return $this->numExecutableLines; } - public function numberOfExecutedLines() : int + public function numberOfExecutedLines(): int { return $this->numExecutedLines; } - public function numberOfExecutableBranches() : int + public function numberOfExecutableBranches(): int { return $this->numExecutableBranches; } - public function numberOfExecutedBranches() : int + public function numberOfExecutedBranches(): int { return $this->numExecutedBranches; } - public function numberOfExecutablePaths() : int + public function numberOfExecutablePaths(): int { return $this->numExecutablePaths; } - public function numberOfExecutedPaths() : int + public function numberOfExecutedPaths(): int { return $this->numExecutedPaths; } - public function numberOfClasses() : int + public function numberOfClasses(): int { if ($this->numClasses === null) { $this->numClasses = 0; @@ -29928,11 +29496,11 @@ final class File extends AbstractNode } return $this->numClasses; } - public function numberOfTestedClasses() : int + public function numberOfTestedClasses(): int { return $this->numTestedClasses; } - public function numberOfTraits() : int + public function numberOfTraits(): int { if ($this->numTraits === null) { $this->numTraits = 0; @@ -29947,11 +29515,11 @@ final class File extends AbstractNode } return $this->numTraits; } - public function numberOfTestedTraits() : int + public function numberOfTestedTraits(): int { return $this->numTestedTraits; } - public function numberOfMethods() : int + public function numberOfMethods(): int { if ($this->numMethods === null) { $this->numMethods = 0; @@ -29972,7 +29540,7 @@ final class File extends AbstractNode } return $this->numMethods; } - public function numberOfTestedMethods() : int + public function numberOfTestedMethods(): int { if ($this->numTestedMethods === null) { $this->numTestedMethods = 0; @@ -29993,11 +29561,11 @@ final class File extends AbstractNode } return $this->numTestedMethods; } - public function numberOfFunctions() : int + public function numberOfFunctions(): int { return count($this->functions); } - public function numberOfTestedFunctions() : int + public function numberOfTestedFunctions(): int { if ($this->numTestedFunctions === null) { $this->numTestedFunctions = 0; @@ -30009,7 +29577,12 @@ final class File extends AbstractNode } return $this->numTestedFunctions; } - private function calculateStatistics(array $classes, array $traits, array $functions) : void + /** + * @psalm-param array $classes + * @psalm-param array $traits + * @psalm-param array $functions + */ + private function calculateStatistics(array $classes, array $traits, array $functions): void { foreach (range(1, $this->linesOfCode['linesOfCode']) as $lineNumber) { $this->codeUnitsByLine[$lineNumber] = []; @@ -30084,7 +29657,10 @@ final class File extends AbstractNode } } } - private function processClasses(array $classes) : void + /** + * @psalm-param array $classes + */ + private function processClasses(array $classes): void { $link = $this->id() . '.html#'; foreach ($classes as $className => $class) { @@ -30106,7 +29682,10 @@ final class File extends AbstractNode } } } - private function processTraits(array $traits) : void + /** + * @psalm-param array $traits + */ + private function processTraits(array $traits): void { $link = $this->id() . '.html#'; foreach ($traits as $traitName => $trait) { @@ -30128,7 +29707,10 @@ final class File extends AbstractNode } } } - private function processFunctions(array $functions) : void + /** + * @psalm-param array $functions + */ + private function processFunctions(array $functions): void { $link = $this->id() . '.html#'; foreach ($functions as $functionName => $function) { @@ -30154,7 +29736,12 @@ final class File extends AbstractNode $this->numExecutedPaths += $this->functions[$functionName]['executedPaths']; } } - private function newMethod(string $className, string $methodName, array $method, string $link) : array + /** + * @psalm-param CodeUnitMethodType $method + * + * @psalm-return ProcessedMethodType + */ + private function newMethod(string $className, string $methodName, array $method, string $link): array { $methodData = ['methodName' => $methodName, 'visibility' => $method['visibility'], 'signature' => $method['signature'], 'startLine' => $method['startLine'], 'endLine' => $method['endLine'], 'executableLines' => 0, 'executedLines' => 0, 'executableBranches' => 0, 'executedBranches' => 0, 'executablePaths' => 0, 'executedPaths' => 0, 'ccn' => $method['ccn'], 'coverage' => 0, 'crap' => 0, 'link' => $link . $method['startLine']]; $key = $className . '->' . $methodName; @@ -30193,14 +29780,11 @@ use RecursiveIterator; */ final class Iterator implements RecursiveIterator { + private int $position; /** - * @var int - */ - private $position; - /** - * @var AbstractNode[] + * @var list */ - private $nodes; + private readonly array $nodes; public function __construct(Directory $node) { $this->nodes = $node->children(); @@ -30208,493 +29792,55 @@ final class Iterator implements RecursiveIterator /** * Rewinds the Iterator to the first element. */ - public function rewind() : void + public function rewind(): void { $this->position = 0; } /** * Checks if there is a current element after calls to rewind() or next(). */ - public function valid() : bool + public function valid(): bool { return $this->position < count($this->nodes); } /** * Returns the key of the current element. */ - public function key() : int + public function key(): int { return $this->position; } /** * Returns the current element. */ - public function current() : ?AbstractNode + public function current(): ?AbstractNode { return $this->valid() ? $this->nodes[$this->position] : null; } /** * Moves forward to next element. */ - public function next() : void + public function next(): void { $this->position++; } /** * Returns the sub iterator for the current element. */ - public function getChildren() : self + public function getChildren(): self { return new self($this->nodes[$this->position]); } /** * Checks whether the current element has children. */ - public function hasChildren() : bool + public function hasChildren(): bool { return $this->nodes[$this->position] instanceof Directory; } } - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnitPHAR\SebastianBergmann\CodeCoverage; - -use function array_key_exists; -use function array_keys; -use function array_merge; -use function array_unique; -use function count; -use function is_array; -use function ksort; -use PHPUnitPHAR\SebastianBergmann\CodeCoverage\Driver\Driver; -/** - * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage - */ -final class ProcessedCodeCoverageData -{ - /** - * Line coverage data. - * An array of filenames, each having an array of linenumbers, each executable line having an array of testcase ids. - * - * @var array - */ - private $lineCoverage = []; - /** - * Function coverage data. - * Maintains base format of raw data (@see https://xdebug.org/docs/code_coverage), but each 'hit' entry is an array - * of testcase ids. - * - * @var array - */ - private $functionCoverage = []; - public function initializeUnseenData(RawCodeCoverageData $rawData) : void - { - foreach ($rawData->lineCoverage() as $file => $lines) { - if (!isset($this->lineCoverage[$file])) { - $this->lineCoverage[$file] = []; - foreach ($lines as $k => $v) { - $this->lineCoverage[$file][$k] = $v === Driver::LINE_NOT_EXECUTABLE ? null : []; - } - } - } - foreach ($rawData->functionCoverage() as $file => $functions) { - foreach ($functions as $functionName => $functionData) { - if (isset($this->functionCoverage[$file][$functionName])) { - $this->initPreviouslySeenFunction($file, $functionName, $functionData); - } else { - $this->initPreviouslyUnseenFunction($file, $functionName, $functionData); - } - } - } - } - public function markCodeAsExecutedByTestCase(string $testCaseId, RawCodeCoverageData $executedCode) : void - { - foreach ($executedCode->lineCoverage() as $file => $lines) { - foreach ($lines as $k => $v) { - if ($v === Driver::LINE_EXECUTED) { - $this->lineCoverage[$file][$k][] = $testCaseId; - } - } - } - foreach ($executedCode->functionCoverage() as $file => $functions) { - foreach ($functions as $functionName => $functionData) { - foreach ($functionData['branches'] as $branchId => $branchData) { - if ($branchData['hit'] === Driver::BRANCH_HIT) { - $this->functionCoverage[$file][$functionName]['branches'][$branchId]['hit'][] = $testCaseId; - } - } - foreach ($functionData['paths'] as $pathId => $pathData) { - if ($pathData['hit'] === Driver::BRANCH_HIT) { - $this->functionCoverage[$file][$functionName]['paths'][$pathId]['hit'][] = $testCaseId; - } - } - } - } - } - public function setLineCoverage(array $lineCoverage) : void - { - $this->lineCoverage = $lineCoverage; - } - public function lineCoverage() : array - { - ksort($this->lineCoverage); - return $this->lineCoverage; - } - public function setFunctionCoverage(array $functionCoverage) : void - { - $this->functionCoverage = $functionCoverage; - } - public function functionCoverage() : array - { - ksort($this->functionCoverage); - return $this->functionCoverage; - } - public function coveredFiles() : array - { - ksort($this->lineCoverage); - return array_keys($this->lineCoverage); - } - public function renameFile(string $oldFile, string $newFile) : void - { - $this->lineCoverage[$newFile] = $this->lineCoverage[$oldFile]; - if (isset($this->functionCoverage[$oldFile])) { - $this->functionCoverage[$newFile] = $this->functionCoverage[$oldFile]; - } - unset($this->lineCoverage[$oldFile], $this->functionCoverage[$oldFile]); - } - public function merge(self $newData) : void - { - foreach ($newData->lineCoverage as $file => $lines) { - if (!isset($this->lineCoverage[$file])) { - $this->lineCoverage[$file] = $lines; - continue; - } - // we should compare the lines if any of two contains data - $compareLineNumbers = array_unique(array_merge(array_keys($this->lineCoverage[$file]), array_keys($newData->lineCoverage[$file]))); - foreach ($compareLineNumbers as $line) { - $thatPriority = $this->priorityForLine($newData->lineCoverage[$file], $line); - $thisPriority = $this->priorityForLine($this->lineCoverage[$file], $line); - if ($thatPriority > $thisPriority) { - $this->lineCoverage[$file][$line] = $newData->lineCoverage[$file][$line]; - } elseif ($thatPriority === $thisPriority && is_array($this->lineCoverage[$file][$line])) { - $this->lineCoverage[$file][$line] = array_unique(array_merge($this->lineCoverage[$file][$line], $newData->lineCoverage[$file][$line])); - } - } - } - foreach ($newData->functionCoverage as $file => $functions) { - if (!isset($this->functionCoverage[$file])) { - $this->functionCoverage[$file] = $functions; - continue; - } - foreach ($functions as $functionName => $functionData) { - if (isset($this->functionCoverage[$file][$functionName])) { - $this->initPreviouslySeenFunction($file, $functionName, $functionData); - } else { - $this->initPreviouslyUnseenFunction($file, $functionName, $functionData); - } - foreach ($functionData['branches'] as $branchId => $branchData) { - $this->functionCoverage[$file][$functionName]['branches'][$branchId]['hit'] = array_unique(array_merge($this->functionCoverage[$file][$functionName]['branches'][$branchId]['hit'], $branchData['hit'])); - } - foreach ($functionData['paths'] as $pathId => $pathData) { - $this->functionCoverage[$file][$functionName]['paths'][$pathId]['hit'] = array_unique(array_merge($this->functionCoverage[$file][$functionName]['paths'][$pathId]['hit'], $pathData['hit'])); - } - } - } - } - /** - * 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. - */ - private function priorityForLine(array $data, int $line) : int - { - 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; - } - /** - * For a function we have never seen before, copy all data over and simply init the 'hit' array. - */ - private function initPreviouslyUnseenFunction(string $file, string $functionName, array $functionData) : void - { - $this->functionCoverage[$file][$functionName] = $functionData; - foreach (array_keys($functionData['branches']) as $branchId) { - $this->functionCoverage[$file][$functionName]['branches'][$branchId]['hit'] = []; - } - foreach (array_keys($functionData['paths']) as $pathId) { - $this->functionCoverage[$file][$functionName]['paths'][$pathId]['hit'] = []; - } - } - /** - * For a function we have seen before, only copy over and init the 'hit' array for any unseen branches and paths. - * Techniques such as mocking and where the contents of a file are different vary during tests (e.g. compiling - * containers) mean that the functions inside a file cannot be relied upon to be static. - */ - private function initPreviouslySeenFunction(string $file, string $functionName, array $functionData) : void - { - foreach ($functionData['branches'] as $branchId => $branchData) { - if (!isset($this->functionCoverage[$file][$functionName]['branches'][$branchId])) { - $this->functionCoverage[$file][$functionName]['branches'][$branchId] = $branchData; - $this->functionCoverage[$file][$functionName]['branches'][$branchId]['hit'] = []; - } - } - foreach ($functionData['paths'] as $pathId => $pathData) { - if (!isset($this->functionCoverage[$file][$functionName]['paths'][$pathId])) { - $this->functionCoverage[$file][$functionName]['paths'][$pathId] = $pathData; - $this->functionCoverage[$file][$functionName]['paths'][$pathId]['hit'] = []; - } - } - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnitPHAR\SebastianBergmann\CodeCoverage; - -use function array_diff; -use function array_diff_key; -use function array_flip; -use function array_intersect; -use function array_intersect_key; -use function count; -use function explode; -use function file_get_contents; -use function in_array; -use function is_file; -use function range; -use function trim; -use PHPUnitPHAR\SebastianBergmann\CodeCoverage\Driver\Driver; -use PHPUnitPHAR\SebastianBergmann\CodeCoverage\StaticAnalysis\FileAnalyser; -/** - * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage - */ -final class RawCodeCoverageData -{ - /** - * @var array> - */ - private static $emptyLineCache = []; - /** - * @var array - * - * @see https://xdebug.org/docs/code_coverage for format - */ - private $lineCoverage; - /** - * @var array - * - * @see https://xdebug.org/docs/code_coverage for format - */ - private $functionCoverage; - public static function fromXdebugWithoutPathCoverage(array $rawCoverage) : self - { - return new self($rawCoverage, []); - } - public static function fromXdebugWithPathCoverage(array $rawCoverage) : self - { - $lineCoverage = []; - $functionCoverage = []; - foreach ($rawCoverage as $file => $fileCoverageData) { - $lineCoverage[$file] = $fileCoverageData['lines']; - $functionCoverage[$file] = $fileCoverageData['functions']; - } - return new self($lineCoverage, $functionCoverage); - } - public static function fromXdebugWithMixedCoverage(array $rawCoverage) : self - { - $lineCoverage = []; - $functionCoverage = []; - foreach ($rawCoverage as $file => $fileCoverageData) { - if (!isset($fileCoverageData['functions'])) { - // Current file does not have functions, so line coverage - // is stored in $fileCoverageData, not in $fileCoverageData['lines'] - $lineCoverage[$file] = $fileCoverageData; - continue; - } - $lineCoverage[$file] = $fileCoverageData['lines']; - $functionCoverage[$file] = $fileCoverageData['functions']; - } - return new self($lineCoverage, $functionCoverage); - } - public static function fromUncoveredFile(string $filename, FileAnalyser $analyser) : self - { - $lineCoverage = []; - foreach ($analyser->executableLinesIn($filename) as $line => $branch) { - $lineCoverage[$line] = Driver::LINE_NOT_EXECUTED; - } - return new self([$filename => $lineCoverage], []); - } - private function __construct(array $lineCoverage, array $functionCoverage) - { - $this->lineCoverage = $lineCoverage; - $this->functionCoverage = $functionCoverage; - $this->skipEmptyLines(); - } - public function clear() : void - { - $this->lineCoverage = $this->functionCoverage = []; - } - public function lineCoverage() : array - { - return $this->lineCoverage; - } - public function functionCoverage() : array - { - return $this->functionCoverage; - } - public function removeCoverageDataForFile(string $filename) : void - { - unset($this->lineCoverage[$filename], $this->functionCoverage[$filename]); - } - /** - * @param int[] $lines - */ - public function keepLineCoverageDataOnlyForLines(string $filename, array $lines) : void - { - if (!isset($this->lineCoverage[$filename])) { - return; - } - $this->lineCoverage[$filename] = array_intersect_key($this->lineCoverage[$filename], array_flip($lines)); - } - /** - * @param int[] $linesToBranchMap - */ - public function markExecutableLineByBranch(string $filename, array $linesToBranchMap) : void - { - if (!isset($this->lineCoverage[$filename])) { - return; - } - $linesByBranch = []; - foreach ($linesToBranchMap as $line => $branch) { - $linesByBranch[$branch][] = $line; - } - foreach ($this->lineCoverage[$filename] as $line => $lineStatus) { - if (!isset($linesToBranchMap[$line])) { - continue; - } - $branch = $linesToBranchMap[$line]; - if (!isset($linesByBranch[$branch])) { - continue; - } - foreach ($linesByBranch[$branch] as $lineInBranch) { - $this->lineCoverage[$filename][$lineInBranch] = $lineStatus; - } - if (Driver::LINE_EXECUTED === $lineStatus) { - unset($linesByBranch[$branch]); - } - } - } - /** - * @param int[] $lines - */ - public function keepFunctionCoverageDataOnlyForLines(string $filename, array $lines) : void - { - if (!isset($this->functionCoverage[$filename])) { - return; - } - foreach ($this->functionCoverage[$filename] as $functionName => $functionData) { - foreach ($functionData['branches'] as $branchId => $branch) { - if (count(array_diff(range($branch['line_start'], $branch['line_end']), $lines)) > 0) { - unset($this->functionCoverage[$filename][$functionName]['branches'][$branchId]); - foreach ($functionData['paths'] as $pathId => $path) { - if (in_array($branchId, $path['path'], \true)) { - unset($this->functionCoverage[$filename][$functionName]['paths'][$pathId]); - } - } - } - } - } - } - /** - * @param int[] $lines - */ - public function removeCoverageDataForLines(string $filename, array $lines) : void - { - if (empty($lines)) { - return; - } - if (!isset($this->lineCoverage[$filename])) { - return; - } - $this->lineCoverage[$filename] = array_diff_key($this->lineCoverage[$filename], array_flip($lines)); - if (isset($this->functionCoverage[$filename])) { - foreach ($this->functionCoverage[$filename] as $functionName => $functionData) { - foreach ($functionData['branches'] as $branchId => $branch) { - if (count(array_intersect($lines, range($branch['line_start'], $branch['line_end']))) > 0) { - unset($this->functionCoverage[$filename][$functionName]['branches'][$branchId]); - foreach ($functionData['paths'] as $pathId => $path) { - if (in_array($branchId, $path['path'], \true)) { - unset($this->functionCoverage[$filename][$functionName]['paths'][$pathId]); - } - } - } - } - } - } - } - /** - * At the end of a file, the PHP interpreter always sees an implicit return. Where this occurs in a file that has - * e.g. a class definition, that line cannot be invoked from a test and results in confusing coverage. This engine - * implementation detail therefore needs to be masked which is done here by simply ensuring that all empty lines - * are skipped over for coverage purposes. - * - * @see https://github.com/sebastianbergmann/php-code-coverage/issues/799 - */ - private function skipEmptyLines() : void - { - foreach ($this->lineCoverage as $filename => $coverage) { - foreach ($this->getEmptyLinesForFile($filename) as $emptyLine) { - unset($this->lineCoverage[$filename][$emptyLine]); - } - } - } - private function getEmptyLinesForFile(string $filename) : array - { - if (!isset(self::$emptyLineCache[$filename])) { - self::$emptyLineCache[$filename] = []; - if (is_file($filename)) { - $sourceLines = explode("\n", file_get_contents($filename)); - foreach ($sourceLines as $line => $source) { - if (trim($source) === '') { - self::$emptyLineCache[$filename][] = $line + 1; - } - } - } - } - return self::$emptyLineCache[$filename]; - } -} -appendChild($xmlMetrics); $buffer = $xmlDocument->saveXML(); if ($target !== null) { - if (!strpos($target, '://') !== \false) { + if (!str_contains($target, '://')) { Filesystem::createDirectory(dirname($target)); } if (@file_put_contents($target, $buffer) === \false) { @@ -30903,8 +30049,8 @@ use function dirname; use function file_put_contents; use function preg_match; use function range; +use function str_contains; use function str_replace; -use function strpos; use function time; use DOMImplementation; use PHPUnitPHAR\SebastianBergmann\CodeCoverage\CodeCoverage; @@ -30916,7 +30062,7 @@ final class Cobertura /** * @throws WriteOperationFailedException */ - public function process(CodeCoverage $coverage, ?string $target = null) : string + public function process(CodeCoverage $coverage, ?string $target = null): string { $time = (string) time(); $report = $coverage->getReport(); @@ -30929,11 +30075,11 @@ final class Cobertura $coverageElement = $document->createElement('coverage'); $linesValid = $report->numberOfExecutableLines(); $linesCovered = $report->numberOfExecutedLines(); - $lineRate = $linesValid === 0 ? 0 : $linesCovered / $linesValid; + $lineRate = ($linesValid === 0) ? 0 : ($linesCovered / $linesValid); $coverageElement->setAttribute('line-rate', (string) $lineRate); $branchesValid = $report->numberOfExecutableBranches(); $branchesCovered = $report->numberOfExecutedBranches(); - $branchRate = $branchesValid === 0 ? 0 : $branchesCovered / $branchesValid; + $branchRate = ($branchesValid === 0) ? 0 : ($branchesCovered / $branchesValid); $coverageElement->setAttribute('branch-rate', (string) $branchRate); $coverageElement->setAttribute('lines-covered', (string) $report->numberOfExecutedLines()); $coverageElement->setAttribute('lines-valid', (string) $report->numberOfExecutableLines()); @@ -30959,11 +30105,11 @@ final class Cobertura $packageElement->setAttribute('name', str_replace($report->pathAsString() . \DIRECTORY_SEPARATOR, '', $item->pathAsString())); $linesValid = $item->numberOfExecutableLines(); $linesCovered = $item->numberOfExecutedLines(); - $lineRate = $linesValid === 0 ? 0 : $linesCovered / $linesValid; + $lineRate = ($linesValid === 0) ? 0 : ($linesCovered / $linesValid); $packageElement->setAttribute('line-rate', (string) $lineRate); $branchesValid = $item->numberOfExecutableBranches(); $branchesCovered = $item->numberOfExecutedBranches(); - $branchRate = $branchesValid === 0 ? 0 : $branchesCovered / $branchesValid; + $branchRate = ($branchesValid === 0) ? 0 : ($branchesCovered / $branchesValid); $packageElement->setAttribute('branch-rate', (string) $branchRate); $packageElement->setAttribute('complexity', ''); $packagesElement->appendChild($packageElement); @@ -30979,10 +30125,10 @@ final class Cobertura } $linesValid = $class['executableLines']; $linesCovered = $class['executedLines']; - $lineRate = $linesValid === 0 ? 0 : $linesCovered / $linesValid; + $lineRate = ($linesValid === 0) ? 0 : ($linesCovered / $linesValid); $branchesValid = $class['executableBranches']; $branchesCovered = $class['executedBranches']; - $branchRate = $branchesValid === 0 ? 0 : $branchesCovered / $branchesValid; + $branchRate = ($branchesValid === 0) ? 0 : ($branchesCovered / $branchesValid); $classElement = $document->createElement('class'); $classElement->setAttribute('name', $className); $classElement->setAttribute('filename', str_replace($report->pathAsString() . \DIRECTORY_SEPARATOR, '', $item->pathAsString())); @@ -31001,10 +30147,10 @@ final class Cobertura preg_match("/\\((.*?)\\)/", $method['signature'], $signature); $linesValid = $method['executableLines']; $linesCovered = $method['executedLines']; - $lineRate = $linesValid === 0 ? 0 : $linesCovered / $linesValid; + $lineRate = ($linesValid === 0) ? 0 : ($linesCovered / $linesValid); $branchesValid = $method['executableBranches']; $branchesCovered = $method['executedBranches']; - $branchRate = $branchesValid === 0 ? 0 : $branchesCovered / $branchesValid; + $branchRate = ($branchesValid === 0) ? 0 : ($branchesCovered / $branchesValid); $methodElement = $document->createElement('method'); $methodElement->setAttribute('name', $methodName); $methodElement->setAttribute('signature', $signature[1]); @@ -31014,7 +30160,7 @@ final class Cobertura $methodLinesElement = $document->createElement('lines'); $methodElement->appendChild($methodLinesElement); foreach (range($method['startLine'], $method['endLine']) as $line) { - if (!isset($coverageData[$line]) || $coverageData[$line] === null) { + if (!isset($coverageData[$line])) { continue; } $methodLineElement = $document->createElement('line'); @@ -31053,12 +30199,12 @@ final class Cobertura $functionsComplexity += $function['ccn']; $linesValid = $function['executableLines']; $linesCovered = $function['executedLines']; - $lineRate = $linesValid === 0 ? 0 : $linesCovered / $linesValid; + $lineRate = ($linesValid === 0) ? 0 : ($linesCovered / $linesValid); $functionsLinesValid += $linesValid; $functionsLinesCovered += $linesCovered; $branchesValid = $function['executableBranches']; $branchesCovered = $function['executedBranches']; - $branchRate = $branchesValid === 0 ? 0 : $branchesCovered / $branchesValid; + $branchRate = ($branchesValid === 0) ? 0 : ($branchesCovered / $branchesValid); $functionsBranchesValid += $branchesValid; $functionsBranchesCovered += $branchesValid; $methodElement = $document->createElement('method'); @@ -31070,7 +30216,7 @@ final class Cobertura $methodLinesElement = $document->createElement('lines'); $methodElement->appendChild($methodLinesElement); foreach (range($function['startLine'], $function['endLine']) as $line) { - if (!isset($coverageData[$line]) || $coverageData[$line] === null) { + if (!isset($coverageData[$line])) { continue; } $methodLineElement = $document->createElement('line'); @@ -31087,7 +30233,7 @@ final class Cobertura continue; } $lineRate = $functionsLinesCovered / $functionsLinesValid; - $branchRate = $functionsBranchesValid === 0 ? 0 : $functionsBranchesCovered / $functionsBranchesValid; + $branchRate = ($functionsBranchesValid === 0) ? 0 : ($functionsBranchesCovered / $functionsBranchesValid); $classElement->setAttribute('line-rate', (string) $lineRate); $classElement->setAttribute('branch-rate', (string) $branchRate); $classElement->setAttribute('complexity', (string) $functionsComplexity); @@ -31096,7 +30242,7 @@ final class Cobertura $coverageElement->setAttribute('complexity', (string) $complexity); $buffer = $document->saveXML(); if ($target !== null) { - if (!strpos($target, '://') !== \false) { + if (!str_contains($target, '://')) { Filesystem::createDirectory(dirname($target)); } if (@file_put_contents($target, $buffer) === \false) { @@ -31125,7 +30271,7 @@ use function file_put_contents; use function htmlspecialchars; use function is_string; use function round; -use function strpos; +use function str_contains; use DOMDocument; use PHPUnitPHAR\SebastianBergmann\CodeCoverage\CodeCoverage; use PHPUnitPHAR\SebastianBergmann\CodeCoverage\Driver\WriteOperationFailedException; @@ -31133,10 +30279,7 @@ use PHPUnitPHAR\SebastianBergmann\CodeCoverage\Node\File; use PHPUnitPHAR\SebastianBergmann\CodeCoverage\Util\Filesystem; final class Crap4j { - /** - * @var int - */ - private $threshold; + private readonly int $threshold; public function __construct(int $threshold = 30) { $this->threshold = $threshold; @@ -31144,7 +30287,7 @@ final class Crap4j /** * @throws WriteOperationFailedException */ - public function process(CodeCoverage $coverage, ?string $target = null, ?string $name = null) : string + public function process(CodeCoverage $coverage, ?string $target = null, ?string $name = null): string { $document = new DOMDocument('1.0', 'UTF-8'); $document->formatOutput = \true; @@ -31209,7 +30352,7 @@ final class Crap4j $root->appendChild($methodsNode); $buffer = $document->saveXML(); if ($target !== null) { - if (!strpos($target, '://') !== \false) { + if (!str_contains($target, '://')) { Filesystem::createDirectory(dirname($target)); } if (@file_put_contents($target, $buffer) === \false) { @@ -31218,7 +30361,7 @@ final class Crap4j } return $buffer; } - private function crapLoad(float $crapValue, int $cyclomaticComplexity, float $coveragePercent) : float + private function crapLoad(float $crapValue, int $cyclomaticComplexity, float $coveragePercent): float { $crapLoad = 0; if ($crapValue >= $this->threshold) { @@ -31227,7 +30370,7 @@ final class Crap4j } return $crapLoad; } - private function roundValue(float $value) : float + private function roundValue(float $value): float { return round($value, 2); } @@ -31245,52 +30388,146 @@ declare (strict_types=1); */ namespace PHPUnitPHAR\SebastianBergmann\CodeCoverage\Report\Html; -use const DIRECTORY_SEPARATOR; -use function copy; -use function date; -use function dirname; -use function substr; -use PHPUnitPHAR\SebastianBergmann\CodeCoverage\CodeCoverage; -use PHPUnitPHAR\SebastianBergmann\CodeCoverage\InvalidArgumentException; -use PHPUnitPHAR\SebastianBergmann\CodeCoverage\Node\Directory as DirectoryNode; -use PHPUnitPHAR\SebastianBergmann\CodeCoverage\Util\Filesystem; -final class Facade +/** + * @psalm-immutable + */ +final class Colors { - /** - * @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 = '') + private readonly string $successLow; + private readonly string $successMedium; + private readonly string $successHigh; + private readonly string $warning; + private readonly string $danger; + public static function default(): self { - if ($lowUpperBound > $highLowerBound) { - throw new InvalidArgumentException('$lowUpperBound must not be larger than $highLowerBound'); - } - $this->generator = $generator; - $this->highLowerBound = $highLowerBound; - $this->lowUpperBound = $lowUpperBound; - $this->templatePath = __DIR__ . '/Renderer/Template/'; + return new self('#dff0d8', '#c3e3b5', '#99cb84', '#fcf8e3', '#f2dede'); } - public function process(CodeCoverage $coverage, string $target) : void + public static function from(string $successLow, string $successMedium, string $successHigh, string $warning, string $danger): self { - $target = $this->directory($target); - $report = $coverage->getReport(); - $date = date('D M j G:i:s T Y'); - $dashboard = new Dashboard($this->templatePath, $this->generator, $date, $this->lowUpperBound, $this->highLowerBound, $coverage->collectsBranchAndPathCoverage()); - $directory = new Directory($this->templatePath, $this->generator, $date, $this->lowUpperBound, $this->highLowerBound, $coverage->collectsBranchAndPathCoverage()); - $file = new File($this->templatePath, $this->generator, $date, $this->lowUpperBound, $this->highLowerBound, $coverage->collectsBranchAndPathCoverage()); - $directory->render($report, $target . 'index.html'); + return new self($successLow, $successMedium, $successHigh, $warning, $danger); + } + private function __construct(string $successLow, string $successMedium, string $successHigh, string $warning, string $danger) + { + $this->successLow = $successLow; + $this->successMedium = $successMedium; + $this->successHigh = $successHigh; + $this->warning = $warning; + $this->danger = $danger; + } + public function successLow(): string + { + return $this->successLow; + } + public function successMedium(): string + { + return $this->successMedium; + } + public function successHigh(): string + { + return $this->successHigh; + } + public function warning(): string + { + return $this->warning; + } + public function danger(): string + { + return $this->danger; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnitPHAR\SebastianBergmann\CodeCoverage\Report\Html; + +use function is_file; +use PHPUnitPHAR\SebastianBergmann\CodeCoverage\InvalidArgumentException; +/** + * @psalm-immutable + */ +final class CustomCssFile +{ + private readonly string $path; + public static function default(): self + { + return new self(__DIR__ . '/Renderer/Template/css/custom.css'); + } + /** + * @throws InvalidArgumentException + */ + public static function from(string $path): self + { + if (!is_file($path)) { + throw new InvalidArgumentException('$path does not exist'); + } + return new self($path); + } + private function __construct(string $path) + { + $this->path = $path; + } + public function path(): string + { + return $this->path; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnitPHAR\SebastianBergmann\CodeCoverage\Report\Html; + +use const DIRECTORY_SEPARATOR; +use function copy; +use function date; +use function dirname; +use function str_ends_with; +use PHPUnitPHAR\SebastianBergmann\CodeCoverage\CodeCoverage; +use PHPUnitPHAR\SebastianBergmann\CodeCoverage\FileCouldNotBeWrittenException; +use PHPUnitPHAR\SebastianBergmann\CodeCoverage\Node\Directory as DirectoryNode; +use PHPUnitPHAR\SebastianBergmann\CodeCoverage\Report\Thresholds; +use PHPUnitPHAR\SebastianBergmann\CodeCoverage\Util\Filesystem; +use PHPUnitPHAR\SebastianBergmann\Template\Exception; +use PHPUnitPHAR\SebastianBergmann\Template\Template; +final class Facade +{ + private readonly string $templatePath; + private readonly string $generator; + private readonly Colors $colors; + private readonly Thresholds $thresholds; + private readonly CustomCssFile $customCssFile; + public function __construct(string $generator = '', ?Colors $colors = null, ?Thresholds $thresholds = null, ?CustomCssFile $customCssFile = null) + { + $this->generator = $generator; + $this->colors = $colors ?? Colors::default(); + $this->thresholds = $thresholds ?? Thresholds::default(); + $this->customCssFile = $customCssFile ?? CustomCssFile::default(); + $this->templatePath = __DIR__ . '/Renderer/Template/'; + } + public function process(CodeCoverage $coverage, string $target): void + { + $target = $this->directory($target); + $report = $coverage->getReport(); + $date = date('D M j G:i:s T Y'); + $dashboard = new Dashboard($this->templatePath, $this->generator, $date, $this->thresholds, $coverage->collectsBranchAndPathCoverage()); + $directory = new Directory($this->templatePath, $this->generator, $date, $this->thresholds, $coverage->collectsBranchAndPathCoverage()); + $file = new File($this->templatePath, $this->generator, $date, $this->thresholds, $coverage->collectsBranchAndPathCoverage()); + $directory->render($report, $target . 'index.html'); $dashboard->render($report, $target . 'dashboard.html'); foreach ($report as $node) { $id = $node->id(); @@ -31305,14 +30542,14 @@ final class Facade } } $this->copyFiles($target); + $this->renderCss($target); } - private function copyFiles(string $target) : void + private function copyFiles(string $target): void { $dir = $this->directory($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->customCssFile->path(), $dir . 'custom.css'); copy($this->templatePath . 'css/octicons.css', $dir . 'octicons.css'); $dir = $this->directory($target . '_icons'); copy($this->templatePath . 'icons/file-code.svg', $dir . 'file-code.svg'); @@ -31325,9 +30562,19 @@ final class Facade copy($this->templatePath . 'js/nv.d3.min.js', $dir . 'nv.d3.min.js'); copy($this->templatePath . 'js/file.js', $dir . 'file.js'); } - private function directory(string $directory) : string + private function renderCss(string $target): void { - if (substr($directory, -1, 1) != DIRECTORY_SEPARATOR) { + $template = new Template($this->templatePath . 'css/style.css', '{{', '}}'); + $template->setVar(['success-low' => $this->colors->successLow(), 'success-medium' => $this->colors->successMedium(), 'success-high' => $this->colors->successHigh(), 'warning' => $this->colors->warning(), 'danger' => $this->colors->danger()]); + try { + $template->renderTo($this->directory($target . '_css') . 'style.css'); + } catch (Exception $e) { + throw new FileCouldNotBeWrittenException($e->getMessage(), $e->getCode(), $e); + } + } + private function directory(string $directory): string + { + if (!str_ends_with($directory, DIRECTORY_SEPARATOR)) { $directory .= DIRECTORY_SEPARATOR; } Filesystem::createDirectory($directory); @@ -31355,6 +30602,7 @@ use function substr_count; use PHPUnitPHAR\SebastianBergmann\CodeCoverage\Node\AbstractNode; use PHPUnitPHAR\SebastianBergmann\CodeCoverage\Node\Directory as DirectoryNode; use PHPUnitPHAR\SebastianBergmann\CodeCoverage\Node\File as FileNode; +use PHPUnitPHAR\SebastianBergmann\CodeCoverage\Report\Thresholds; use PHPUnitPHAR\SebastianBergmann\CodeCoverage\Version; use PHPUnitPHAR\SebastianBergmann\Environment\Runtime; use PHPUnitPHAR\SebastianBergmann\Template\Template; @@ -31363,45 +30611,22 @@ use PHPUnitPHAR\SebastianBergmann\Template\Template; */ abstract class Renderer { - /** - * @var string - */ - protected $templatePath; - /** - * @var string - */ - protected $generator; - /** - * @var string - */ - protected $date; - /** - * @var int - */ - protected $lowUpperBound; - /** - * @var int - */ - protected $highLowerBound; - /** - * @var bool - */ - protected $hasBranchCoverage; - /** - * @var string - */ - protected $version; - public function __construct(string $templatePath, string $generator, string $date, int $lowUpperBound, int $highLowerBound, bool $hasBranchCoverage) + protected string $templatePath; + protected string $generator; + protected string $date; + protected Thresholds $thresholds; + protected bool $hasBranchCoverage; + protected string $version; + public function __construct(string $templatePath, string $generator, string $date, Thresholds $thresholds, bool $hasBranchCoverage) { $this->templatePath = $templatePath; $this->generator = $generator; $this->date = $date; - $this->lowUpperBound = $lowUpperBound; - $this->highLowerBound = $highLowerBound; + $this->thresholds = $thresholds; $this->version = Version::id(); $this->hasBranchCoverage = $hasBranchCoverage; } - protected function renderItemTemplate(Template $template, array $data) : string + protected function renderItemTemplate(Template $template, array $data): string { $numSeparator = ' / '; if (isset($data['numClasses']) && $data['numClasses'] > 0) { @@ -31457,11 +30682,11 @@ abstract class Renderer $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, 'paths_bar' => $pathsBar, 'paths_executed_percent' => $data['pathsExecutedPercentAsString'], 'paths_level' => $pathsLevel, 'paths_number' => $pathsNumber, 'branches_bar' => $branchesBar, 'branches_executed_percent' => $data['branchesExecutedPercentAsString'], 'branches_level' => $branchesLevel, 'branches_number' => $branchesNumber, '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(Template $template, AbstractNode $node) : void + protected function setCommonTemplateVariables(Template $template, AbstractNode $node): void { - $template->setVar(['id' => $node->id(), 'full_path' => $node->pathAsString(), 'path_to_root' => $this->pathToRoot($node), 'breadcrumbs' => $this->breadcrumbs($node), 'date' => $this->date, 'version' => $this->version, 'runtime' => $this->runtimeString(), 'generator' => $this->generator, 'low_upper_bound' => $this->lowUpperBound, 'high_lower_bound' => $this->highLowerBound]); + $template->setVar(['id' => $node->id(), 'full_path' => $node->pathAsString(), 'path_to_root' => $this->pathToRoot($node), 'breadcrumbs' => $this->breadcrumbs($node), 'date' => $this->date, 'version' => $this->version, 'runtime' => $this->runtimeString(), 'generator' => $this->generator, 'low_upper_bound' => $this->thresholds->lowUpperBound(), 'high_lower_bound' => $this->thresholds->highLowerBound()]); } - protected function breadcrumbs(AbstractNode $node) : string + protected function breadcrumbs(AbstractNode $node): string { $breadcrumbs = ''; $path = $node->pathAsArray(); @@ -31482,7 +30707,7 @@ abstract class Renderer } return $breadcrumbs; } - protected function activeBreadcrumb(AbstractNode $node) : string + protected function activeBreadcrumb(AbstractNode $node): string { $buffer = sprintf(' ' . "\n", $node->name()); if ($node instanceof DirectoryNode) { @@ -31490,11 +30715,11 @@ abstract class Renderer } return $buffer; } - protected function inactiveBreadcrumb(AbstractNode $node, string $pathToRoot) : string + protected function inactiveBreadcrumb(AbstractNode $node, string $pathToRoot): string { return sprintf(' ' . "\n", $pathToRoot, $node->name()); } - protected function pathToRoot(AbstractNode $node) : string + protected function pathToRoot(AbstractNode $node): string { $id = $node->id(); $depth = substr_count($id, '/'); @@ -31503,7 +30728,7 @@ abstract class Renderer } return str_repeat('../', $depth); } - protected function coverageBar(float $percent) : string + protected function coverageBar(float $percent): string { $level = $this->colorLevel($percent); $templateName = $this->templatePath . ($this->hasBranchCoverage ? 'coverage_bar_branch.html' : 'coverage_bar.html'); @@ -31511,17 +30736,17 @@ abstract class Renderer $template->setVar(['level' => $level, 'percent' => sprintf('%.2F', $percent)]); return $template->render(); } - protected function colorLevel(float $percent) : string + protected function colorLevel(float $percent): string { - if ($percent <= $this->lowUpperBound) { + if ($percent <= $this->thresholds->lowUpperBound()) { return 'danger'; } - if ($percent > $this->lowUpperBound && $percent < $this->highLowerBound) { + if ($percent > $this->thresholds->lowUpperBound() && $percent < $this->thresholds->highLowerBound()) { return 'warning'; } return 'success'; } - private function runtimeString() : string + private function runtimeString(): string { $runtime = new Runtime(); return sprintf('%s %s', $runtime->getVendorUrl(), $runtime->getName(), $runtime->getVersion()); @@ -31549,15 +30774,17 @@ use function floor; use function json_encode; use function sprintf; use function str_replace; +use PHPUnitPHAR\SebastianBergmann\CodeCoverage\FileCouldNotBeWrittenException; use PHPUnitPHAR\SebastianBergmann\CodeCoverage\Node\AbstractNode; use PHPUnitPHAR\SebastianBergmann\CodeCoverage\Node\Directory as DirectoryNode; +use PHPUnitPHAR\SebastianBergmann\Template\Exception; use PHPUnitPHAR\SebastianBergmann\Template\Template; /** * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage */ final class Dashboard extends Renderer { - public function render(DirectoryNode $node, string $file) : void + public function render(DirectoryNode $node, string $file): void { $classes = $node->classesAndTraits(); $templateName = $this->templatePath . ($this->hasBranchCoverage ? 'dashboard_branch.html' : 'dashboard.html'); @@ -31569,16 +30796,20 @@ final class Dashboard extends Renderer $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); + try { + $template->renderTo($file); + } catch (Exception $e) { + throw new FileCouldNotBeWrittenException($e->getMessage(), $e->getCode(), $e); + } } - protected function activeBreadcrumb(AbstractNode $node) : string + protected function activeBreadcrumb(AbstractNode $node): string { return sprintf(' ' . "\n" . ' ' . "\n", $node->name()); } /** * Returns the data for the Class/Method Complexity charts. */ - private function complexity(array $classes, string $baseLink) : array + private function complexity(array $classes, string $baseLink): array { $result = ['class' => [], 'method' => []]; foreach ($classes as $className => $class) { @@ -31595,7 +30826,7 @@ final class Dashboard extends Renderer /** * Returns the data for the Class / Method Coverage Distribution chart. */ - private function coverageDistribution(array $classes) : array + private 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) { @@ -31625,14 +30856,14 @@ final class Dashboard extends Renderer /** * Returns the classes / methods with insufficient coverage. */ - private function insufficientCoverage(array $classes, string $baseLink) : array + private 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) { + if ($method['coverage'] < $this->thresholds->highLowerBound()) { $key = $methodName; if ($className !== '*') { $key = $className . '::' . $methodName; @@ -31640,7 +30871,7 @@ final class Dashboard extends Renderer $leastTestedMethods[$key] = $method['coverage']; } } - if ($class['coverage'] < $this->highLowerBound) { + if ($class['coverage'] < $this->thresholds->highLowerBound()) { $leastTestedClasses[$className] = $class['coverage']; } } @@ -31658,14 +30889,14 @@ final class Dashboard extends Renderer /** * Returns the project risks according to the CRAP index. */ - private function projectRisks(array $classes, string $baseLink) : array + private 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) { + if ($method['coverage'] < $this->thresholds->highLowerBound() && $method['ccn'] > 1) { $key = $methodName; if ($className !== '*') { $key = $className . '::' . $methodName; @@ -31673,7 +30904,7 @@ final class Dashboard extends Renderer $methodRisks[$key] = $method['crap']; } } - if ($class['coverage'] < $this->highLowerBound && $class['ccn'] > count($class['methods'])) { + if ($class['coverage'] < $this->thresholds->highLowerBound() && $class['ccn'] > count($class['methods'])) { $classRisks[$className] = $class['crap']; } } @@ -31705,15 +30936,17 @@ namespace PHPUnitPHAR\SebastianBergmann\CodeCoverage\Report\Html; use function count; use function sprintf; use function str_repeat; +use PHPUnitPHAR\SebastianBergmann\CodeCoverage\FileCouldNotBeWrittenException; use PHPUnitPHAR\SebastianBergmann\CodeCoverage\Node\AbstractNode as Node; use PHPUnitPHAR\SebastianBergmann\CodeCoverage\Node\Directory as DirectoryNode; +use PHPUnitPHAR\SebastianBergmann\Template\Exception; use PHPUnitPHAR\SebastianBergmann\Template\Template; /** * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage */ final class Directory extends Renderer { - public function render(DirectoryNode $node, string $file) : void + public function render(DirectoryNode $node, string $file): void { $templateName = $this->templatePath . ($this->hasBranchCoverage ? 'directory_branch.html' : 'directory.html'); $template = new Template($templateName, '{{', '}}'); @@ -31726,9 +30959,13 @@ final class Directory extends Renderer $items .= $this->renderItem($item); } $template->setVar(['id' => $node->id(), 'items' => $items]); - $template->renderTo($file); + try { + $template->renderTo($file); + } catch (Exception $e) { + throw new FileCouldNotBeWrittenException($e->getMessage(), $e->getCode(), $e); + } } - private function renderItem(Node $node, bool $total = \false) : string + private function renderItem(Node $node, bool $total = \false): string { $data = ['numClasses' => $node->numberOfClassesAndTraits(), 'numTestedClasses' => $node->numberOfTestedClassesAndTraits(), 'numMethods' => $node->numberOfFunctionsAndMethods(), 'numTestedMethods' => $node->numberOfTestedFunctionsAndMethods(), 'linesExecutedPercent' => $node->percentageOfExecutedLines()->asFloat(), 'linesExecutedPercentAsString' => $node->percentageOfExecutedLines()->asString(), 'numExecutedLines' => $node->numberOfExecutedLines(), 'numExecutableLines' => $node->numberOfExecutableLines(), 'branchesExecutedPercent' => $node->percentageOfExecutedBranches()->asFloat(), 'branchesExecutedPercentAsString' => $node->percentageOfExecutedBranches()->asString(), 'numExecutedBranches' => $node->numberOfExecutedBranches(), 'numExecutableBranches' => $node->numberOfExecutableBranches(), 'pathsExecutedPercent' => $node->percentageOfExecutedPaths()->asFloat(), 'pathsExecutedPercentAsString' => $node->percentageOfExecutedPaths()->asString(), 'numExecutedPaths' => $node->numberOfExecutedPaths(), 'numExecutablePaths' => $node->numberOfExecutablePaths(), 'testedMethodsPercent' => $node->percentageOfTestedFunctionsAndMethods()->asFloat(), 'testedMethodsPercentAsString' => $node->percentageOfTestedFunctionsAndMethods()->asString(), 'testedClassesPercent' => $node->percentageOfTestedClassesAndTraits()->asFloat(), 'testedClassesPercentAsString' => $node->percentageOfTestedClassesAndTraits()->asString()]; if ($total) { @@ -31837,9 +31074,7 @@ use function array_keys; use function array_merge; use function array_pop; use function array_unique; -use function constant; use function count; -use function defined; use function explode; use function file_get_contents; use function htmlspecialchars; @@ -31848,13 +31083,14 @@ use function ksort; use function range; use function sort; use function sprintf; +use function str_ends_with; use function str_replace; -use function substr; use function token_get_all; use function trim; -use PHPUnit\Runner\BaseTestRunner; +use PHPUnitPHAR\SebastianBergmann\CodeCoverage\FileCouldNotBeWrittenException; use PHPUnitPHAR\SebastianBergmann\CodeCoverage\Node\File as FileNode; use PHPUnitPHAR\SebastianBergmann\CodeCoverage\Util\Percentage; +use PHPUnitPHAR\SebastianBergmann\Template\Exception; use PHPUnitPHAR\SebastianBergmann\Template\Template; /** * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage @@ -31864,30 +31100,36 @@ final class File extends Renderer /** * @psalm-var array */ - private static $keywordTokens = []; - /** - * @var array - */ - private static $formattedSourceCache = []; - /** - * @var int - */ - private $htmlSpecialCharsFlags = ENT_COMPAT | ENT_HTML401 | ENT_SUBSTITUTE; - public function render(FileNode $node, string $file) : void + private const KEYWORD_TOKENS = [T_ABSTRACT => \true, T_ARRAY => \true, T_AS => \true, T_BREAK => \true, T_CALLABLE => \true, T_CASE => \true, T_CATCH => \true, T_CLASS => \true, T_CLONE => \true, T_CONST => \true, T_CONTINUE => \true, T_DECLARE => \true, T_DEFAULT => \true, T_DO => \true, T_ECHO => \true, T_ELSE => \true, T_ELSEIF => \true, T_EMPTY => \true, T_ENDDECLARE => \true, T_ENDFOR => \true, T_ENDFOREACH => \true, T_ENDIF => \true, T_ENDSWITCH => \true, T_ENDWHILE => \true, \T_ENUM => \true, T_EVAL => \true, T_EXIT => \true, T_EXTENDS => \true, T_FINAL => \true, T_FINALLY => \true, \T_FN => \true, T_FOR => \true, T_FOREACH => \true, T_FUNCTION => \true, T_GLOBAL => \true, T_GOTO => \true, T_HALT_COMPILER => \true, T_IF => \true, T_IMPLEMENTS => \true, T_INCLUDE => \true, T_INCLUDE_ONCE => \true, T_INSTANCEOF => \true, T_INSTEADOF => \true, T_INTERFACE => \true, T_ISSET => \true, T_LIST => \true, \T_MATCH => \true, T_NAMESPACE => \true, T_NEW => \true, T_PRINT => \true, T_PRIVATE => \true, T_PROTECTED => \true, T_PUBLIC => \true, \T_READONLY => \true, T_REQUIRE => \true, T_REQUIRE_ONCE => \true, T_RETURN => \true, T_STATIC => \true, T_SWITCH => \true, T_THROW => \true, T_TRAIT => \true, T_TRY => \true, T_UNSET => \true, T_USE => \true, T_VAR => \true, T_WHILE => \true, T_YIELD => \true, T_YIELD_FROM => \true]; + private static array $formattedSourceCache = []; + private int $htmlSpecialCharsFlags = ENT_COMPAT | ENT_HTML401 | ENT_SUBSTITUTE; + public function render(FileNode $node, string $file): void { $templateName = $this->templatePath . ($this->hasBranchCoverage ? 'file_branch.html' : 'file.html'); $template = new Template($templateName, '{{', '}}'); $this->setCommonTemplateVariables($template, $node); $template->setVar(['items' => $this->renderItems($node), 'lines' => $this->renderSourceWithLineCoverage($node), 'legend' => '

    Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

    ', 'structure' => '']); - $template->renderTo($file . '.html'); + try { + $template->renderTo($file . '.html'); + } catch (Exception $e) { + throw new FileCouldNotBeWrittenException($e->getMessage(), $e->getCode(), $e); + } if ($this->hasBranchCoverage) { $template->setVar(['items' => $this->renderItems($node), 'lines' => $this->renderSourceWithBranchCoverage($node), 'legend' => '

    Fully coveredPartially coveredNot covered

    ', 'structure' => $this->renderBranchStructure($node)]); - $template->renderTo($file . '_branch.html'); + try { + $template->renderTo($file . '_branch.html'); + } catch (Exception $e) { + throw new FileCouldNotBeWrittenException($e->getMessage(), $e->getCode(), $e); + } $template->setVar(['items' => $this->renderItems($node), 'lines' => $this->renderSourceWithPathCoverage($node), 'legend' => '

    Fully coveredPartially coveredNot covered

    ', 'structure' => $this->renderPathStructure($node)]); - $template->renderTo($file . '_path.html'); + try { + $template->renderTo($file . '_path.html'); + } catch (Exception $e) { + throw new FileCouldNotBeWrittenException($e->getMessage(), $e->getCode(), $e); + } } } - private function renderItems(FileNode $node) : string + private function renderItems(FileNode $node): string { $templateName = $this->templatePath . ($this->hasBranchCoverage ? 'file_item_branch.html' : 'file_item.html'); $template = new Template($templateName, '{{', '}}'); @@ -31899,7 +31141,7 @@ final class File extends Renderer $items .= $this->renderTraitOrClassItems($node->classes(), $template, $methodItemTemplate); return $items; } - private function renderTraitOrClassItems(array $items, Template $template, Template $methodItemTemplate) : string + private function renderTraitOrClassItems(array $items, Template $template, Template $methodItemTemplate): string { $buffer = ''; if (empty($items)) { @@ -31918,7 +31160,7 @@ final class File extends Renderer } if ($item['executableLines'] > 0) { $numClasses = 1; - $numTestedClasses = $numTestedMethods === $numMethods ? 1 : 0; + $numTestedClasses = ($numTestedMethods === $numMethods) ? 1 : 0; $linesExecutedPercentAsString = Percentage::fromFractionAndTotal($item['executedLines'], $item['executableLines'])->asString(); $branchesExecutedPercentAsString = Percentage::fromFractionAndTotal($item['executedBranches'], $item['executableBranches'])->asString(); $pathsExecutedPercentAsString = Percentage::fromFractionAndTotal($item['executedPaths'], $item['executablePaths'])->asString(); @@ -31930,7 +31172,7 @@ final class File extends Renderer $pathsExecutedPercentAsString = 'n/a'; } $testedMethodsPercentage = Percentage::fromFractionAndTotal($numTestedMethods, $numMethods); - $testedClassesPercentage = Percentage::fromFractionAndTotal($numTestedMethods === $numMethods ? 1 : 0, 1); + $testedClassesPercentage = Percentage::fromFractionAndTotal(($numTestedMethods === $numMethods) ? 1 : 0, 1); $buffer .= $this->renderItemTemplate($template, ['name' => $this->abbreviateClassName($name), 'numClasses' => $numClasses, 'numTestedClasses' => $numTestedClasses, 'numMethods' => $numMethods, 'numTestedMethods' => $numTestedMethods, 'linesExecutedPercent' => Percentage::fromFractionAndTotal($item['executedLines'], $item['executableLines'])->asFloat(), 'linesExecutedPercentAsString' => $linesExecutedPercentAsString, 'numExecutedLines' => $item['executedLines'], 'numExecutableLines' => $item['executableLines'], 'branchesExecutedPercent' => Percentage::fromFractionAndTotal($item['executedBranches'], $item['executableBranches'])->asFloat(), 'branchesExecutedPercentAsString' => $branchesExecutedPercentAsString, 'numExecutedBranches' => $item['executedBranches'], 'numExecutableBranches' => $item['executableBranches'], 'pathsExecutedPercent' => Percentage::fromFractionAndTotal($item['executedPaths'], $item['executablePaths'])->asFloat(), 'pathsExecutedPercentAsString' => $pathsExecutedPercentAsString, 'numExecutedPaths' => $item['executedPaths'], 'numExecutablePaths' => $item['executablePaths'], 'testedMethodsPercent' => $testedMethodsPercentage->asFloat(), 'testedMethodsPercentAsString' => $testedMethodsPercentage->asString(), 'testedClassesPercent' => $testedClassesPercentage->asFloat(), 'testedClassesPercentAsString' => $testedClassesPercentage->asString(), 'crap' => $item['crap']]); foreach ($item['methods'] as $method) { $buffer .= $this->renderFunctionOrMethodItem($methodItemTemplate, $method, ' '); @@ -31938,7 +31180,7 @@ final class File extends Renderer } return $buffer; } - private function renderFunctionItems(array $functions, Template $template) : string + private function renderFunctionItems(array $functions, Template $template): string { if (empty($functions)) { return ''; @@ -31949,7 +31191,7 @@ final class File extends Renderer } return $buffer; } - private function renderFunctionOrMethodItem(Template $template, array $item, string $indent = '') : string + private function renderFunctionOrMethodItem(Template $template, array $item, string $indent = ''): string { $numMethods = 0; $numTestedMethods = 0; @@ -31965,7 +31207,7 @@ final class File extends Renderer $testedMethodsPercentage = Percentage::fromFractionAndTotal($numTestedMethods, 1); return $this->renderItemTemplate($template, ['name' => sprintf('%s%s', $indent, $item['startLine'], htmlspecialchars($item['signature'], $this->htmlSpecialCharsFlags), $item['functionName'] ?? $item['methodName']), 'numMethods' => $numMethods, 'numTestedMethods' => $numTestedMethods, 'linesExecutedPercent' => $executedLinesPercentage->asFloat(), 'linesExecutedPercentAsString' => $executedLinesPercentage->asString(), 'numExecutedLines' => $item['executedLines'], 'numExecutableLines' => $item['executableLines'], 'branchesExecutedPercent' => $executedBranchesPercentage->asFloat(), 'branchesExecutedPercentAsString' => $executedBranchesPercentage->asString(), 'numExecutedBranches' => $item['executedBranches'], 'numExecutableBranches' => $item['executableBranches'], 'pathsExecutedPercent' => $executedPathsPercentage->asFloat(), 'pathsExecutedPercentAsString' => $executedPathsPercentage->asString(), 'numExecutedPaths' => $item['executedPaths'], 'numExecutablePaths' => $item['executablePaths'], 'testedMethodsPercent' => $testedMethodsPercentage->asFloat(), 'testedMethodsPercentAsString' => $testedMethodsPercentage->asString(), 'crap' => $item['crap']]); } - private function renderSourceWithLineCoverage(FileNode $node) : string + private function renderSourceWithLineCoverage(FileNode $node): string { $linesTemplate = new Template($this->templatePath . 'lines.html.dist', '{{', '}}'); $singleLineTemplate = new Template($this->templatePath . 'line.html.dist', '{{', '}}'); @@ -32014,7 +31256,7 @@ final class File extends Renderer $linesTemplate->setVar(['lines' => $lines]); return $linesTemplate->render(); } - private function renderSourceWithBranchCoverage(FileNode $node) : string + private function renderSourceWithBranchCoverage(FileNode $node): string { $linesTemplate = new Template($this->templatePath . 'lines.html.dist', '{{', '}}'); $singleLineTemplate = new Template($this->templatePath . 'line.html.dist', '{{', '}}'); @@ -32074,7 +31316,7 @@ final class File extends Renderer $linesTemplate->setVar(['lines' => $lines]); return $linesTemplate->render(); } - private function renderSourceWithPathCoverage(FileNode $node) : string + private function renderSourceWithPathCoverage(FileNode $node): string { $linesTemplate = new Template($this->templatePath . 'lines.html.dist', '{{', '}}'); $singleLineTemplate = new Template($this->templatePath . 'line.html.dist', '{{', '}}'); @@ -32137,7 +31379,7 @@ final class File extends Renderer $linesTemplate->setVar(['lines' => $lines]); return $linesTemplate->render(); } - private function renderBranchStructure(FileNode $node) : string + private function renderBranchStructure(FileNode $node): string { $branchesTemplate = new Template($this->templatePath . 'branches.html.dist', '{{', '}}'); $coverageData = $node->functionCoverageData(); @@ -32162,7 +31404,7 @@ final class File extends Renderer $branchesTemplate->setVar(['branches' => $branches]); return $branchesTemplate->render(); } - private function renderBranchLines(array $branch, array $codeLines, array $testData) : string + private function renderBranchLines(array $branch, array $codeLines, array $testData): string { $linesTemplate = new Template($this->templatePath . 'lines.html.dist', '{{', '}}'); $singleLineTemplate = new Template($this->templatePath . 'line.html.dist', '{{', '}}'); @@ -32211,7 +31453,7 @@ final class File extends Renderer $linesTemplate->setVar(['lines' => $lines]); return $linesTemplate->render(); } - private function renderPathStructure(FileNode $node) : string + private function renderPathStructure(FileNode $node): string { $pathsTemplate = new Template($this->templatePath . 'paths.html.dist', '{{', '}}'); $coverageData = $node->functionCoverageData(); @@ -32239,7 +31481,7 @@ final class File extends Renderer $pathsTemplate->setVar(['paths' => $paths]); return $pathsTemplate->render(); } - private function renderPathLines(array $path, array $branches, array $codeLines, array $testData) : string + private function renderPathLines(array $path, array $branches, array $codeLines, array $testData): string { $linesTemplate = new Template($this->templatePath . 'lines.html.dist', '{{', '}}'); $singleLineTemplate = new Template($this->templatePath . 'line.html.dist', '{{', '}}'); @@ -32296,12 +31538,12 @@ final class File extends Renderer $linesTemplate->setVar(['lines' => $lines]); return $linesTemplate->render(); } - private function renderLine(Template $template, int $lineNumber, string $lineContent, string $class, string $popover) : string + private function renderLine(Template $template, int $lineNumber, string $lineContent, string $class, string $popover): string { $template->setVar(['lineNumber' => $lineNumber, 'lineContent' => $lineContent, 'class' => $class, 'popover' => $popover]); return $template->render(); } - private function loadFile(string $file) : array + private function loadFile(string $file): array { if (isset(self::$formattedSourceCache[$file])) { return self::$formattedSourceCache[$file]; @@ -32311,7 +31553,7 @@ final class File extends Renderer $result = ['']; $i = 0; $stringFlag = \false; - $fileEndsWithNewLine = substr($buffer, -1) === "\n"; + $fileEndsWithNewLine = str_ends_with($buffer, "\n"); unset($buffer); foreach ($tokens as $j => $token) { if (is_string($token)) { @@ -32358,7 +31600,7 @@ final class File extends Renderer self::$formattedSourceCache[$file] = $result; return $result; } - private function abbreviateClassName(string $className) : string + private function abbreviateClassName(string $className): string { $tmp = explode('\\', $className); if (count($tmp) > 1) { @@ -32366,7 +31608,7 @@ final class File extends Renderer } return $className; } - private function abbreviateMethodName(string $methodName) : string + private function abbreviateMethodName(string $methodName): string { $parts = explode('->', $methodName); if (count($parts) === 2) { @@ -32374,72 +31616,35 @@ final class File extends Renderer } return $methodName; } - private function createPopoverContentForTest(string $test, array $testData) : string + private function createPopoverContentForTest(string $test, array $testData): string { $testCSS = ''; - if ($testData['fromTestcase']) { - switch ($testData['status']) { - case BaseTestRunner::STATUS_PASSED: - switch ($testData['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 BaseTestRunner::STATUS_SKIPPED: - case BaseTestRunner::STATUS_INCOMPLETE: - case BaseTestRunner::STATUS_RISKY: - case BaseTestRunner::STATUS_WARNING: - $testCSS = ' class="warning"'; - break; - case BaseTestRunner::STATUS_FAILURE: - case BaseTestRunner::STATUS_ERROR: - $testCSS = ' class="danger"'; - break; - } + switch ($testData['status']) { + case 'success': + $testCSS = match ($testData['size']) { + 'small' => ' class="covered-by-small-tests"', + 'medium' => ' class="covered-by-medium-tests"', + // no break + default => ' class="covered-by-large-tests"', + }; + break; + case 'failure': + $testCSS = ' class="danger"'; + break; } return sprintf('%s', $testCSS, htmlspecialchars($test, $this->htmlSpecialCharsFlags)); } - private function isComment(int $token) : bool + private function isComment(int $token): bool { return $token === T_COMMENT || $token === T_DOC_COMMENT; } - private function isInlineHtml(int $token) : bool + private function isInlineHtml(int $token): bool { return $token === T_INLINE_HTML; } - private function isKeyword(int $token) : bool + private function isKeyword(int $token): bool { - return isset(self::keywordTokens()[$token]); - } - /** - * @psalm-return array - */ - private static function keywordTokens() : array - { - if (self::$keywordTokens !== []) { - return self::$keywordTokens; - } - self::$keywordTokens = [T_ABSTRACT => \true, T_ARRAY => \true, T_AS => \true, T_BREAK => \true, T_CALLABLE => \true, T_CASE => \true, T_CATCH => \true, T_CLASS => \true, T_CLONE => \true, T_CONST => \true, T_CONTINUE => \true, T_DECLARE => \true, T_DEFAULT => \true, T_DO => \true, T_ECHO => \true, T_ELSE => \true, T_ELSEIF => \true, T_EMPTY => \true, T_ENDDECLARE => \true, T_ENDFOR => \true, T_ENDFOREACH => \true, T_ENDIF => \true, T_ENDSWITCH => \true, T_ENDWHILE => \true, T_EVAL => \true, T_EXIT => \true, T_EXTENDS => \true, T_FINAL => \true, T_FINALLY => \true, T_FOR => \true, T_FOREACH => \true, T_FUNCTION => \true, T_GLOBAL => \true, T_GOTO => \true, T_HALT_COMPILER => \true, T_IF => \true, T_IMPLEMENTS => \true, T_INCLUDE => \true, T_INCLUDE_ONCE => \true, T_INSTANCEOF => \true, T_INSTEADOF => \true, T_INTERFACE => \true, T_ISSET => \true, T_LIST => \true, T_NAMESPACE => \true, T_NEW => \true, T_PRINT => \true, T_PRIVATE => \true, T_PROTECTED => \true, T_PUBLIC => \true, T_REQUIRE => \true, T_REQUIRE_ONCE => \true, T_RETURN => \true, T_STATIC => \true, T_SWITCH => \true, T_THROW => \true, T_TRAIT => \true, T_TRY => \true, T_UNSET => \true, T_USE => \true, T_VAR => \true, T_WHILE => \true, T_YIELD => \true, T_YIELD_FROM => \true]; - if (defined('T_FN')) { - self::$keywordTokens[constant('T_FN')] = \true; - } - if (defined('T_MATCH')) { - self::$keywordTokens[constant('T_MATCH')] = \true; - } - if (defined('T_ENUM')) { - self::$keywordTokens[constant('T_ENUM')] = \true; - } - if (defined('T_READONLY')) { - self::$keywordTokens[constant('T_READONLY')] = \true; - } - return self::$keywordTokens; + return isset(self::KEYWORD_TOKENS[$token]); } }
    @@ -32514,23 +31719,23 @@ body { } .table tbody tr.covered-by-large-tests, li.covered-by-large-tests, tr.success, td.success, li.success, span.success { - background-color: #dff0d8; + background-color: {{success-low}}; } .table tbody tr.covered-by-medium-tests, li.covered-by-medium-tests { - background-color: #c3e3b5; + background-color: {{success-medium}}; } .table tbody tr.covered-by-small-tests, li.covered-by-small-tests { - background-color: #99cb84; + background-color: {{success-high}}; } -.table tbody tr.danger, .table tbody td.danger, li.danger, span.danger { - background-color: #f2dede; +.table tbody tr.warning, .table tbody td.warning, li.warning, span.warning { + background-color: {{warning}}; } -.table tbody tr.warning, .table tbody td.warning, li.warning, span.warning { - background-color: #fcf8e3; +.table tbody tr.danger, .table tbody td.danger, li.danger, span.danger { + background-color: {{danger}}; } .table tbody td.info { @@ -32612,23 +31817,23 @@ table + .structure-heading { } .covered-by-small-tests { - background-color: #99cb84; + background-color: {{success-high}}; } .covered-by-medium-tests { - background-color: #c3e3b5; + background-color: {{success-medium}}; } .covered-by-large-tests { - background-color: #dff0d8; + background-color: {{success-low}}; } .not-covered { - background-color: #f2dede; + background-color: {{danger}}; } .not-coverable { - background-color: #fcf8e3; + background-color: {{warning}}; } @@ -33659,18 +32864,18 @@ namespace PHPUnitPHAR\SebastianBergmann\CodeCoverage\Report; use function dirname; use function file_put_contents; use function serialize; -use function strpos; +use function str_contains; use PHPUnitPHAR\SebastianBergmann\CodeCoverage\CodeCoverage; use PHPUnitPHAR\SebastianBergmann\CodeCoverage\Driver\WriteOperationFailedException; use PHPUnitPHAR\SebastianBergmann\CodeCoverage\Util\Filesystem; final class PHP { - public function process(CodeCoverage $coverage, ?string $target = null) : string + public function process(CodeCoverage $coverage, ?string $target = null): string { $coverage->clearCache(); $buffer = "lowUpperBound = $lowUpperBound; - $this->highLowerBound = $highLowerBound; + $this->thresholds = $thresholds; $this->showUncoveredFiles = $showUncoveredFiles; $this->showOnlySummary = $showOnlySummary; } - public function process(CodeCoverage $coverage, bool $showColors = \false) : string + public function process(CodeCoverage $coverage, bool $showColors = \false): string { $hasBranchCoverage = !empty($coverage->getData(\true)->functionCoverage()); $output = PHP_EOL . PHP_EOL; $report = $coverage->getReport(); - $colors = ['header' => '', 'classes' => '', 'methods' => '', 'lines' => '', 'branches' => '', 'paths' => '', 'reset' => '', 'eol' => '']; + $colors = ['header' => '', 'classes' => '', 'methods' => '', 'lines' => '', 'branches' => '', 'paths' => '', 'reset' => '']; if ($showColors) { $colors['classes'] = $this->coverageColor($report->numberOfTestedClassesAndTraits(), $report->numberOfClassesAndTraits()); $colors['methods'] = $this->coverageColor($report->numberOfTestedMethods(), $report->numberOfMethods()); @@ -33767,7 +32954,6 @@ final class Text $colors['paths'] = $this->coverageColor($report->numberOfExecutedPaths(), $report->numberOfExecutablePaths()); $colors['reset'] = self::COLOR_RESET; $colors['header'] = self::COLOR_HEADER; - $colors['eol'] = self::COLOR_EOL; } $classes = sprintf(' Classes: %6s (%d/%d)', Percentage::fromFractionAndTotal($report->numberOfTestedClassesAndTraits(), $report->numberOfClassesAndTraits())->asString(), $report->numberOfTestedClassesAndTraits(), $report->numberOfClassesAndTraits()); $methods = sprintf(' Methods: %6s (%d/%d)', Percentage::fromFractionAndTotal($report->numberOfTestedMethods(), $report->numberOfMethods())->asString(), $report->numberOfTestedMethods(), $report->numberOfMethods()); @@ -33858,29 +33044,77 @@ final class Text } return $output . PHP_EOL; } - private function coverageColor(int $numberOfCoveredElements, int $totalNumberOfElements) : string + private function coverageColor(int $numberOfCoveredElements, int $totalNumberOfElements): string { $coverage = Percentage::fromFractionAndTotal($numberOfCoveredElements, $totalNumberOfElements); - if ($coverage->asFloat() >= $this->highLowerBound) { + if ($coverage->asFloat() >= $this->thresholds->highLowerBound()) { return self::COLOR_GREEN; } - if ($coverage->asFloat() > $this->lowUpperBound) { + if ($coverage->asFloat() > $this->thresholds->lowUpperBound()) { return self::COLOR_YELLOW; } return self::COLOR_RED; } - private function printCoverageCounts(int $numberOfCoveredElements, int $totalNumberOfElements, int $precision) : string + private function printCoverageCounts(int $numberOfCoveredElements, int $totalNumberOfElements, int $precision): string { $format = '%' . $precision . 's'; return Percentage::fromFractionAndTotal($numberOfCoveredElements, $totalNumberOfElements)->asFixedWidthString() . ' (' . sprintf($format, $numberOfCoveredElements) . '/' . sprintf($format, $totalNumberOfElements) . ')'; } + private function format(string $color, int $padding, false|string $string): string + { + if ($color === '') { + return (string) $string . PHP_EOL; + } + return $color . str_pad((string) $string, $padding) . self::COLOR_RESET . PHP_EOL; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnitPHAR\SebastianBergmann\CodeCoverage\Report; + +use PHPUnitPHAR\SebastianBergmann\CodeCoverage\InvalidArgumentException; +/** + * @psalm-immutable + */ +final class Thresholds +{ + private readonly int $lowUpperBound; + private readonly int $highLowerBound; + public static function default(): self + { + return new self(50, 90); + } /** - * @param false|string $string + * @throws InvalidArgumentException */ - private function format(string $color, int $padding, $string) : string + public static function from(int $lowUpperBound, int $highLowerBound): self { - $reset = $color ? self::COLOR_RESET : ''; - return $color . str_pad((string) $string, $padding) . $reset . PHP_EOL; + if ($lowUpperBound > $highLowerBound) { + throw new InvalidArgumentException('$lowUpperBound must not be larger than $highLowerBound'); + } + return new self($lowUpperBound, $highLowerBound); + } + private function __construct(int $lowUpperBound, int $highLowerBound) + { + $this->lowUpperBound = $lowUpperBound; + $this->highLowerBound = $highLowerBound; + } + public function lowUpperBound(): int + { + return $this->lowUpperBound; + } + public function highLowerBound(): int + { + return $this->highLowerBound; } } contextNode = $contextNode; } - public function setRuntimeInformation(Runtime $runtime) : void + public function setRuntimeInformation(Runtime $runtime): void { $runtimeNode = $this->nodeByName('runtime'); $runtimeNode->setAttribute('name', $runtime->getName()); $runtimeNode->setAttribute('version', $runtime->getVersion()); $runtimeNode->setAttribute('url', $runtime->getVendorUrl()); $driverNode = $this->nodeByName('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')); @@ -33934,16 +33160,16 @@ final class BuildInformation $driverNode->setAttribute('version', phpversion('pcov')); } } - public function setBuildTime(DateTimeImmutable $date) : void + public function setBuildTime(DateTimeImmutable $date): void { $this->contextNode->setAttribute('time', $date->format('D M j G:i:s T Y')); } - public function setGeneratorVersions(string $phpUnitVersion, string $coverageVersion) : void + public function setGeneratorVersions(string $phpUnitVersion, string $coverageVersion): void { $this->contextNode->setAttribute('phpunit', $phpUnitVersion); $this->contextNode->setAttribute('coverage', $coverageVersion); } - private function nodeByName(string $name) : DOMElement + private function nodeByName(string $name): DOMElement { $node = $this->contextNode->getElementsByTagNameNS('https://schema.phpunit.de/coverage/1.0', $name)->item(0); if (!$node) { @@ -33973,18 +33199,9 @@ use XMLWriter; */ final class Coverage { - /** - * @var XMLWriter - */ - private $writer; - /** - * @var DOMElement - */ - private $contextNode; - /** - * @var bool - */ - private $finalized = \false; + private readonly XMLWriter $writer; + private readonly DOMElement $contextNode; + private bool $finalized = \false; public function __construct(DOMElement $context, string $line) { $this->contextNode = $context; @@ -33996,7 +33213,7 @@ final class Coverage /** * @throws ReportAlreadyFinalizedException */ - public function addTest(string $test) : void + public function addTest(string $test): void { if ($this->finalized) { throw new ReportAlreadyFinalizedException(); @@ -34005,7 +33222,7 @@ final class Coverage $this->writer->writeAttribute('by', $test); $this->writer->endElement(); } - public function finalize() : void + public function finalize(): void { $this->writer->endElement(); $fragment = $this->contextNode->ownerDocument->createDocumentFragment(); @@ -34076,18 +33293,9 @@ use PHPUnitPHAR\SebastianBergmann\CodeCoverage\XmlException; use PHPUnitPHAR\SebastianBergmann\Environment\Runtime; final class Facade { - /** - * @var string - */ - private $target; - /** - * @var Project - */ - private $project; - /** - * @var string - */ - private $phpUnitVersion; + private string $target; + private Project $project; + private readonly string $phpUnitVersion; public function __construct(string $version) { $this->phpUnitVersion = $version; @@ -34095,7 +33303,7 @@ final class Facade /** * @throws XmlException */ - public function process(CodeCoverage $coverage, string $target) : void + public function process(CodeCoverage $coverage, string $target): void { if (substr($target, -1, 1) !== DIRECTORY_SEPARATOR) { $target .= DIRECTORY_SEPARATOR; @@ -34109,7 +33317,7 @@ final class Facade $this->processDirectory($report, $this->project); $this->saveDocument($this->project->asDom(), 'index'); } - private function setBuildInformation() : void + private function setBuildInformation(): void { $buildNode = $this->project->buildInformation(); $buildNode->setRuntimeInformation(new Runtime()); @@ -34120,7 +33328,7 @@ final class Facade * @throws PathExistsButIsNotDirectoryException * @throws WriteOperationFailedException */ - private function initTargetDirectory(string $directory) : void + private function initTargetDirectory(string $directory): void { if (is_file($directory)) { if (!is_dir($directory)) { @@ -34135,7 +33343,7 @@ final class Facade /** * @throws XmlException */ - private function processDirectory(DirectoryNode $directory, Node $context) : void + private function processDirectory(DirectoryNode $directory, Node $context): void { $directoryName = $directory->name(); if ($this->project->projectSourceDirectory() === $directoryName) { @@ -34153,7 +33361,7 @@ final class Facade /** * @throws XmlException */ - private function processFile(FileNode $file, Directory $context) : void + private function processFile(FileNode $file, Directory $context): void { $fileObject = $context->addFile($file->name(), $file->id() . '.xml'); $this->setTotals($file, $fileObject->totals()); @@ -34179,7 +33387,7 @@ final class Facade $fileReport->source()->setSourceCode(file_get_contents($file->pathAsString())); $this->saveDocument($fileReport->asDom(), $file->id()); } - private function processUnit(array $unit, Report $report) : void + private function processUnit(array $unit, Report $report): void { if (isset($unit['className'])) { $unitObject = $report->classObject($unit['className']); @@ -34197,7 +33405,7 @@ final class Facade $methodObject->setTotals((string) $method['executableLines'], (string) $method['executedLines'], (string) $method['coverage']); } } - private function processFunction(array $function, Report $report) : void + private function processFunction(array $function, Report $report): void { $functionObject = $report->functionObject($function['functionName']); $functionObject->setSignature($function['signature']); @@ -34205,14 +33413,14 @@ final class Facade $functionObject->setCrap($function['crap']); $functionObject->setTotals((string) $function['executableLines'], (string) $function['executedLines'], (string) $function['coverage']); } - private function processTests(array $tests) : void + private function processTests(array $tests): void { $testsObject = $this->project->tests(); foreach ($tests as $test => $result) { $testsObject->addTest($test, $result); } } - private function setTotals(AbstractNode $node, Totals $totals) : void + private function setTotals(AbstractNode $node, Totals $totals): void { $loc = $node->linesOfCode(); $totals->setNumLines($loc['linesOfCode'], $loc['commentLinesOfCode'], $loc['nonCommentLinesOfCode'], $node->numberOfExecutableLines(), $node->numberOfExecutedLines()); @@ -34221,14 +33429,14 @@ final class Facade $totals->setNumMethods($node->numberOfMethods(), $node->numberOfTestedMethods()); $totals->setNumFunctions($node->numberOfFunctions(), $node->numberOfTestedFunctions()); } - private function targetDirectory() : string + private function targetDirectory(): string { return $this->target; } /** * @throws XmlException */ - private function saveDocument(DOMDocument $document, string $name) : void + private function saveDocument(DOMDocument $document, string $name): void { $filename = sprintf('%s/%s.xml', $this->targetDirectory(), $name); $document->formatOutput = \true; @@ -34241,7 +33449,7 @@ final class Facade * * @see https://bugs.php.net/bug.php?id=79191 */ - private function documentAsString(DOMDocument $document) : string + private function documentAsString(DOMDocument $document): string { $xmlErrorHandling = libxml_use_internal_errors(\true); $xml = $document->saveXML(); @@ -34277,20 +33485,14 @@ use DOMElement; */ class File { - /** - * @var DOMDocument - */ - private $dom; - /** - * @var DOMElement - */ - private $contextNode; + private readonly DOMDocument $dom; + private readonly DOMElement $contextNode; public function __construct(DOMElement $context) { $this->dom = $context->ownerDocument; $this->contextNode = $context; } - public function totals() : Totals + public function totals(): Totals { $totalsContainer = $this->contextNode->firstChild; if (!$totalsContainer) { @@ -34298,7 +33500,7 @@ class File } return new Totals($totalsContainer); } - public function lineCoverage(string $line) : Coverage + public function lineCoverage(string $line): Coverage { $coverage = $this->contextNode->getElementsByTagNameNS('https://schema.phpunit.de/coverage/1.0', 'coverage')->item(0); if (!$coverage) { @@ -34307,11 +33509,11 @@ class File $lineNode = $coverage->appendChild($this->dom->createElementNS('https://schema.phpunit.de/coverage/1.0', 'line')); return new Coverage($lineNode, $line); } - protected function contextNode() : DOMElement + protected function contextNode(): DOMElement { return $this->contextNode; } - protected function dom() : DOMDocument + protected function dom(): DOMDocument { return $this->dom; } @@ -34335,37 +33537,34 @@ use DOMElement; */ final class Method { - /** - * @var DOMElement - */ - private $contextNode; + private readonly DOMElement $contextNode; public function __construct(DOMElement $context, string $name) { $this->contextNode = $context; $this->setName($name); } - public function setSignature(string $signature) : void + public function setSignature(string $signature): void { $this->contextNode->setAttribute('signature', $signature); } - public function setLines(string $start, ?string $end = null) : void + 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 + 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 + public function setCrap(string $crap): void { $this->contextNode->setAttribute('crap', $crap); } - private function setName(string $name) : void + private function setName(string $name): void { $this->contextNode->setAttribute('name', $name); } @@ -34390,23 +33589,17 @@ use DOMElement; */ abstract class Node { - /** - * @var DOMDocument - */ - private $dom; - /** - * @var DOMElement - */ - private $contextNode; + private DOMDocument $dom; + private DOMElement $contextNode; public function __construct(DOMElement $context) { $this->setContextNode($context); } - public function dom() : DOMDocument + public function dom(): DOMDocument { return $this->dom; } - public function totals() : Totals + public function totals(): Totals { $totalsContainer = $this->contextNode()->firstChild; if (!$totalsContainer) { @@ -34414,14 +33607,14 @@ abstract class Node } return new Totals($totalsContainer); } - public function addDirectory(string $name) : Directory + public function addDirectory(string $name): Directory { $dirNode = $this->dom()->createElementNS('https://schema.phpunit.de/coverage/1.0', 'directory'); $dirNode->setAttribute('name', $name); $this->contextNode()->appendChild($dirNode); return new Directory($dirNode); } - public function addFile(string $name, string $href) : File + public function addFile(string $name, string $href): File { $fileNode = $this->dom()->createElementNS('https://schema.phpunit.de/coverage/1.0', 'file'); $fileNode->setAttribute('name', $name); @@ -34429,12 +33622,12 @@ abstract class Node $this->contextNode()->appendChild($fileNode); return new File($fileNode); } - protected function setContextNode(DOMElement $context) : void + protected function setContextNode(DOMElement $context): void { $this->dom = $context->ownerDocument; $this->contextNode = $context; } - protected function contextNode() : DOMElement + protected function contextNode(): DOMElement { return $this->contextNode; } @@ -34463,11 +33656,11 @@ final class Project extends Node $this->init(); $this->setProjectSourceDirectory($directory); } - public function projectSourceDirectory() : string + public function projectSourceDirectory(): string { return $this->contextNode()->getAttribute('source'); } - public function buildInformation() : BuildInformation + public function buildInformation(): BuildInformation { $buildNode = $this->dom()->getElementsByTagNameNS('https://schema.phpunit.de/coverage/1.0', 'build')->item(0); if (!$buildNode) { @@ -34475,7 +33668,7 @@ final class Project extends Node } return new BuildInformation($buildNode); } - public function tests() : Tests + public function tests(): Tests { $testsNode = $this->contextNode()->getElementsByTagNameNS('https://schema.phpunit.de/coverage/1.0', 'tests')->item(0); if (!$testsNode) { @@ -34483,17 +33676,17 @@ final class Project extends Node } return new Tests($testsNode); } - public function asDom() : DOMDocument + public function asDom(): DOMDocument { return $this->dom(); } - private function init() : void + private function init(): void { $dom = new DOMDocument(); $dom->loadXML(''); $this->setContextNode($dom->getElementsByTagNameNS('https://schema.phpunit.de/coverage/1.0', 'project')->item(0)); } - private function setProjectSourceDirectory(string $name) : void + private function setProjectSourceDirectory(string $name): void { $this->contextNode()->setAttribute('source', $name); } @@ -34527,24 +33720,24 @@ final class Report extends File parent::__construct($contextNode); $this->setName($name); } - public function asDom() : DOMDocument + public function asDom(): DOMDocument { return $this->dom(); } - public function functionObject($name) : Method + public function functionObject($name): Method { $node = $this->contextNode()->appendChild($this->dom()->createElementNS('https://schema.phpunit.de/coverage/1.0', 'function')); return new Method($node, $name); } - public function classObject($name) : Unit + public function classObject($name): Unit { return $this->unitObject('class', $name); } - public function traitObject($name) : Unit + public function traitObject($name): Unit { return $this->unitObject('trait', $name); } - public function source() : Source + public function source(): Source { $source = $this->contextNode()->getElementsByTagNameNS('https://schema.phpunit.de/coverage/1.0', 'source')->item(0); if (!$source) { @@ -34552,12 +33745,12 @@ final class Report extends File } return new Source($source); } - private function setName(string $name) : void + private function setName(string $name): void { $this->contextNode()->setAttribute('name', basename($name)); $this->contextNode()->setAttribute('path', dirname($name)); } - private function unitObject(string $tagName, $name) : Unit + private function unitObject(string $tagName, $name): Unit { $node = $this->contextNode()->appendChild($this->dom()->createElementNS('https://schema.phpunit.de/coverage/1.0', $tagName)); return new Unit($node, $name); @@ -34585,13 +33778,12 @@ use PHPUnitPHAR\TheSeer\Tokenizer\XMLSerializer; */ final class Source { - /** @var DOMElement */ - private $context; + private readonly DOMElement $context; public function __construct(DOMElement $context) { $this->context = $context; } - public function setSourceCode(string $source) : void + public function setSourceCode(string $source): void { $context = $this->context; $tokens = (new Tokenizer())->parse($source); @@ -34615,38 +33807,25 @@ namespace PHPUnitPHAR\SebastianBergmann\CodeCoverage\Report\Xml; use DOMElement; /** * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage + * + * @psalm-import-type TestType from \SebastianBergmann\CodeCoverage\CodeCoverage */ 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', - ]; + private readonly DOMElement $contextNode; public function __construct(DOMElement $context) { $this->contextNode = $context; } - public function addTest(string $test, array $result) : void + /** + * @param TestType $result + */ + 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']]); + $node->setAttribute('status', $result['status']); } } container = $container; @@ -34710,42 +33871,42 @@ final class Totals $container->appendChild($this->classesNode); $container->appendChild($this->traitsNode); } - public function container() : DOMNode + public function container(): DOMNode { return $this->container; } - public function setNumLines(int $loc, int $cloc, int $ncloc, int $executable, int $executed) : void + 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', Percentage::fromFractionAndTotal($executed, $executable)->asFloat())); + $this->linesNode->setAttribute('percent', ($executable === 0) ? '0' : sprintf('%01.2F', Percentage::fromFractionAndTotal($executed, $executable)->asFloat())); } - public function setNumClasses(int $count, int $tested) : void + 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', Percentage::fromFractionAndTotal($tested, $count)->asFloat())); + $this->classesNode->setAttribute('percent', ($count === 0) ? '0' : sprintf('%01.2F', Percentage::fromFractionAndTotal($tested, $count)->asFloat())); } - public function setNumTraits(int $count, int $tested) : void + 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', Percentage::fromFractionAndTotal($tested, $count)->asFloat())); + $this->traitsNode->setAttribute('percent', ($count === 0) ? '0' : sprintf('%01.2F', Percentage::fromFractionAndTotal($tested, $count)->asFloat())); } - public function setNumMethods(int $count, int $tested) : void + 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', Percentage::fromFractionAndTotal($tested, $count)->asFloat())); + $this->methodsNode->setAttribute('percent', ($count === 0) ? '0' : sprintf('%01.2F', Percentage::fromFractionAndTotal($tested, $count)->asFloat())); } - public function setNumFunctions(int $count, int $tested) : void + 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', Percentage::fromFractionAndTotal($tested, $count)->asFloat())); + $this->functionsNode->setAttribute('percent', ($count === 0) ? '0' : sprintf('%01.2F', Percentage::fromFractionAndTotal($tested, $count)->asFloat())); } } contextNode = $context; $this->setName($name); } - public function setLines(int $start, int $executable, int $executed) : void + 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 + public function setCrap(float $crap): void { $this->contextNode->setAttribute('crap', (string) $crap); } - public function setNamespace(string $namespace) : void + public function setNamespace(string $namespace): void { $node = $this->contextNode->getElementsByTagNameNS('https://schema.phpunit.de/coverage/1.0', 'namespace')->item(0); if (!$node) { @@ -34794,12 +33952,12 @@ final class Unit } $node->setAttribute('name', $namespace); } - public function addMethod(string $name) : Method + 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 + private function setName(string $name): void { $this->contextNode->setAttribute('name', $name); } @@ -34820,7 +33978,7 @@ namespace PHPUnitPHAR\SebastianBergmann\CodeCoverage\StaticAnalysis; use PHPUnitPHAR\SebastianBergmann\CodeCoverage\Filter; final class CacheWarmer { - public function warmCache(string $cacheDirectory, bool $useAnnotationsForIgnoringCode, bool $ignoreDeprecatedCode, Filter $filter) : void + public function warmCache(string $cacheDirectory, bool $useAnnotationsForIgnoringCode, bool $ignoreDeprecatedCode, Filter $filter): void { $analyser = new CachingFileAnalyser($cacheDirectory, new ParsingFileAnalyser($useAnnotationsForIgnoringCode, $ignoreDeprecatedCode), $useAnnotationsForIgnoringCode, $ignoreDeprecatedCode); foreach ($filter->files() as $file) { @@ -34852,33 +34010,17 @@ use PHPUnitPHAR\SebastianBergmann\CodeCoverage\Util\Filesystem; use PHPUnitPHAR\SebastianBergmann\FileIterator\Facade as FileIteratorFacade; /** * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage + * + * @psalm-import-type LinesOfCodeType from \SebastianBergmann\CodeCoverage\StaticAnalysis\FileAnalyser */ final class CachingFileAnalyser implements FileAnalyser { - /** - * @var ?string - */ - private static $cacheVersion; - /** - * @var string - */ - private $directory; - /** - * @var FileAnalyser - */ - private $analyser; - /** - * @var bool - */ - private $useAnnotationsForIgnoringCode; - /** - * @var bool - */ - private $ignoreDeprecatedCode; - /** - * @var array - */ - private $cache = []; + private static ?string $cacheVersion = null; + private readonly string $directory; + private readonly FileAnalyser $analyser; + private readonly bool $useAnnotationsForIgnoringCode; + private readonly bool $ignoreDeprecatedCode; + private array $cache = []; public function __construct(string $directory, FileAnalyser $analyser, bool $useAnnotationsForIgnoringCode, bool $ignoreDeprecatedCode) { Filesystem::createDirectory($directory); @@ -34887,21 +34029,21 @@ final class CachingFileAnalyser implements FileAnalyser $this->useAnnotationsForIgnoringCode = $useAnnotationsForIgnoringCode; $this->ignoreDeprecatedCode = $ignoreDeprecatedCode; } - public function classesIn(string $filename) : array + public function classesIn(string $filename): array { if (!isset($this->cache[$filename])) { $this->process($filename); } return $this->cache[$filename]['classesIn']; } - public function traitsIn(string $filename) : array + public function traitsIn(string $filename): array { if (!isset($this->cache[$filename])) { $this->process($filename); } return $this->cache[$filename]['traitsIn']; } - public function functionsIn(string $filename) : array + public function functionsIn(string $filename): array { if (!isset($this->cache[$filename])) { $this->process($filename); @@ -34909,30 +34051,30 @@ final class CachingFileAnalyser implements FileAnalyser return $this->cache[$filename]['functionsIn']; } /** - * @psalm-return array{linesOfCode: int, commentLinesOfCode: int, nonCommentLinesOfCode: int} + * @psalm-return LinesOfCodeType */ - public function linesOfCodeFor(string $filename) : array + public function linesOfCodeFor(string $filename): array { if (!isset($this->cache[$filename])) { $this->process($filename); } return $this->cache[$filename]['linesOfCodeFor']; } - public function executableLinesIn(string $filename) : array + public function executableLinesIn(string $filename): array { if (!isset($this->cache[$filename])) { $this->process($filename); } return $this->cache[$filename]['executableLinesIn']; } - public function ignoredLinesFor(string $filename) : array + public function ignoredLinesFor(string $filename): array { if (!isset($this->cache[$filename])) { $this->process($filename); } return $this->cache[$filename]['ignoredLinesFor']; } - public function process(string $filename) : void + public function process(string $filename): void { $cache = $this->read($filename); if ($cache !== \false) { @@ -34942,10 +34084,7 @@ final class CachingFileAnalyser implements FileAnalyser $this->cache[$filename] = ['classesIn' => $this->analyser->classesIn($filename), 'traitsIn' => $this->analyser->traitsIn($filename), 'functionsIn' => $this->analyser->functionsIn($filename), 'linesOfCodeFor' => $this->analyser->linesOfCodeFor($filename), 'ignoredLinesFor' => $this->analyser->ignoredLinesFor($filename), 'executableLinesIn' => $this->analyser->executableLinesIn($filename)]; $this->write($filename, $this->cache[$filename]); } - /** - * @return mixed - */ - private function read(string $filename) + private function read(string $filename): array|false { $cacheFile = $this->cacheFile($filename); if (!is_file($cacheFile)) { @@ -34953,19 +34092,16 @@ final class CachingFileAnalyser implements FileAnalyser } return unserialize(file_get_contents($cacheFile), ['allowed_classes' => \false]); } - /** - * @param mixed $data - */ - private function write(string $filename, $data) : void + private function write(string $filename, array $data): void { file_put_contents($this->cacheFile($filename), serialize($data)); } - private function cacheFile(string $filename) : string + private function cacheFile(string $filename): string { $cacheKey = md5(implode("\x00", [$filename, file_get_contents($filename), self::cacheVersion(), $this->useAnnotationsForIgnoringCode, $this->ignoreDeprecatedCode])); return $this->directory . \DIRECTORY_SEPARATOR . $cacheKey; } - private static function cacheVersion() : string + private static function cacheVersion(): string { if (self::$cacheVersion !== null) { return self::$cacheVersion; @@ -35009,28 +34145,61 @@ use PHPUnitPHAR\PhpParser\Node\Stmt\Function_; use PHPUnitPHAR\PhpParser\Node\Stmt\Interface_; use PHPUnitPHAR\PhpParser\Node\Stmt\Trait_; use PHPUnitPHAR\PhpParser\Node\UnionType; -use PHPUnitPHAR\PhpParser\NodeAbstract; use PHPUnitPHAR\PhpParser\NodeTraverser; use PHPUnitPHAR\PhpParser\NodeVisitorAbstract; use PHPUnitPHAR\SebastianBergmann\Complexity\CyclomaticComplexityCalculatingVisitor; /** * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage + * + * @psalm-type CodeUnitFunctionType = array{ + * name: string, + * namespacedName: string, + * namespace: string, + * signature: string, + * startLine: int, + * endLine: int, + * ccn: int + * } + * @psalm-type CodeUnitMethodType = array{ + * methodName: string, + * signature: string, + * visibility: string, + * startLine: int, + * endLine: int, + * ccn: int + * } + * @psalm-type CodeUnitClassType = array{ + * name: string, + * namespacedName: string, + * namespace: string, + * startLine: int, + * endLine: int, + * methods: array + * } + * @psalm-type CodeUnitTraitType = array{ + * name: string, + * namespacedName: string, + * namespace: string, + * startLine: int, + * endLine: int, + * methods: array + * } */ final class CodeUnitFindingVisitor extends NodeVisitorAbstract { /** - * @psalm-var array}> + * @psalm-var array */ - private $classes = []; + private array $classes = []; /** - * @psalm-var array}> + * @psalm-var array */ - private $traits = []; + private array $traits = []; /** - * @psalm-var array + * @psalm-var array */ - private $functions = []; - public function enterNode(Node $node) : void + private array $functions = []; + public function enterNode(Node $node): void { if ($node instanceof Class_) { if ($node->isAnonymous()) { @@ -35055,32 +34224,28 @@ final class CodeUnitFindingVisitor extends NodeVisitorAbstract $this->processFunction($node); } /** - * @psalm-return array}> + * @psalm-return array */ - public function classes() : array + public function classes(): array { return $this->classes; } /** - * @psalm-return array}> + * @psalm-return array */ - public function traits() : array + public function traits(): array { return $this->traits; } /** - * @psalm-return array + * @psalm-return array */ - public function functions() : array + public function functions(): array { return $this->functions; } - /** - * @psalm-param ClassMethod|Function_ $node - */ - private function cyclomaticComplexity(Node $node) : int + private function cyclomaticComplexity(ClassMethod|Function_ $node): int { - assert($node instanceof ClassMethod || $node instanceof Function_); $nodes = $node->getStmts(); if ($nodes === null) { return 0; @@ -35092,12 +34257,8 @@ final class CodeUnitFindingVisitor extends NodeVisitorAbstract $traverser->traverse($nodes); return $cyclomaticComplexityCalculatingVisitor->cyclomaticComplexity(); } - /** - * @psalm-param ClassMethod|Function_ $node - */ - private function signature(Node $node) : string + private function signature(ClassMethod|Function_ $node): string { - assert($node instanceof ClassMethod || $node instanceof Function_); $signature = ($node->returnsByRef() ? '&' : '') . $node->name->toString() . '('; $parameters = []; foreach ($node->getParams() as $parameter) { @@ -35117,12 +34278,8 @@ final class CodeUnitFindingVisitor extends NodeVisitorAbstract } return $signature; } - /** - * @psalm-param Identifier|Name|ComplexType $type - */ - private function type(Node $type) : string + private function type(ComplexType|Identifier|Name $type): string { - assert($type instanceof Identifier || $type instanceof Name || $type instanceof ComplexType); if ($type instanceof NullableType) { return '?' . $type->type; } @@ -35134,7 +34291,7 @@ final class CodeUnitFindingVisitor extends NodeVisitorAbstract } return $type->toString(); } - private function visibility(ClassMethod $node) : string + private function visibility(ClassMethod $node): string { if ($node->isPrivate()) { return 'private'; @@ -35144,19 +34301,19 @@ final class CodeUnitFindingVisitor extends NodeVisitorAbstract } return 'public'; } - private function processClass(Class_ $node) : void + private function processClass(Class_ $node): void { $name = $node->name->toString(); $namespacedName = $node->namespacedName->toString(); $this->classes[$namespacedName] = ['name' => $name, 'namespacedName' => $namespacedName, 'namespace' => $this->namespace($namespacedName, $name), 'startLine' => $node->getStartLine(), 'endLine' => $node->getEndLine(), 'methods' => []]; } - private function processTrait(Trait_ $node) : void + private function processTrait(Trait_ $node): void { $name = $node->name->toString(); $namespacedName = $node->namespacedName->toString(); $this->traits[$namespacedName] = ['name' => $name, 'namespacedName' => $namespacedName, 'namespace' => $this->namespace($namespacedName, $name), 'startLine' => $node->getStartLine(), 'endLine' => $node->getEndLine(), 'methods' => []]; } - private function processMethod(ClassMethod $node) : void + private function processMethod(ClassMethod $node): void { $parentNode = $node->getAttribute('parent'); if ($parentNode instanceof Interface_) { @@ -35178,7 +34335,7 @@ final class CodeUnitFindingVisitor extends NodeVisitorAbstract } $storage[$parentNamespacedName]['methods'][$node->name->toString()] = ['methodName' => $node->name->toString(), 'signature' => $this->signature($node), 'visibility' => $this->visibility($node), 'startLine' => $node->getStartLine(), 'endLine' => $node->getEndLine(), 'ccn' => $this->cyclomaticComplexity($node)]; } - private function processFunction(Function_ $node) : void + private function processFunction(Function_ $node): void { assert(isset($node->name)); assert(isset($node->namespacedName)); @@ -35187,11 +34344,11 @@ final class CodeUnitFindingVisitor extends NodeVisitorAbstract $namespacedName = $node->namespacedName->toString(); $this->functions[$namespacedName] = ['name' => $name, 'namespacedName' => $namespacedName, 'namespace' => $this->namespace($namespacedName, $name), 'signature' => $this->signature($node), 'startLine' => $node->getStartLine(), 'endLine' => $node->getEndLine(), 'ccn' => $this->cyclomaticComplexity($node)]; } - private function namespace(string $namespacedName, string $name) : string + private function namespace(string $namespacedName, string $name): string { return trim(rtrim($namespacedName, $name), '\\'); } - private function unionTypeAsString(UnionType $node) : string + private function unionTypeAsString(UnionType $node): string { $types = []; foreach ($node->types as $type) { @@ -35203,7 +34360,7 @@ final class CodeUnitFindingVisitor extends NodeVisitorAbstract } return implode('|', $types); } - private function intersectionTypeAsString(IntersectionType $node) : string + private function intersectionTypeAsString(IntersectionType $node): string { $types = []; foreach ($node->types as $type) { @@ -35211,10 +34368,7 @@ final class CodeUnitFindingVisitor extends NodeVisitorAbstract } return implode('&', $types); } - /** - * @psalm-param Identifier|Name $node $node - */ - private function typeAsString(NodeAbstract $node) : string + private function typeAsString(Identifier|Name $node): string { if ($node instanceof Name) { return $node->toCodeString(); @@ -35251,34 +34405,30 @@ use PHPUnitPHAR\PhpParser\Node; use PHPUnitPHAR\PhpParser\NodeVisitorAbstract; /** * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage + * + * @psalm-import-type LinesType from \SebastianBergmann\CodeCoverage\StaticAnalysis\FileAnalyser */ final class ExecutableLinesFindingVisitor extends NodeVisitorAbstract { + private int $nextBranch = 0; + private readonly string $source; /** - * @var int - */ - private $nextBranch = 0; - /** - * @var string + * @psalm-var LinesType */ - private $source; + private array $executableLinesGroupedByBranch = []; /** - * @var array + * @psalm-var array */ - private $executableLinesGroupedByBranch = []; + private array $unsets = []; /** - * @var array + * @psalm-var array */ - private $unsets = []; - /** - * @var array - */ - private $commentsToCheckForUnset = []; + private array $commentsToCheckForUnset = []; public function __construct(string $source) { $this->source = $source; } - public function enterNode(Node $node) : void + public function enterNode(Node $node): void { foreach ($node->getComments() as $comment) { $commentLine = $comment->getStartLine(); @@ -35306,7 +34456,13 @@ final class ExecutableLinesFindingVisitor extends NodeVisitorAbstract } return; } - if ($node instanceof Node\Stmt\Declare_ || $node instanceof Node\Stmt\DeclareDeclare || $node instanceof Node\Stmt\Else_ || $node instanceof Node\Stmt\EnumCase || $node instanceof Node\Stmt\Finally_ || $node instanceof Node\Stmt\GroupUse || $node instanceof Node\Stmt\Label || $node instanceof Node\Stmt\Namespace_ || $node instanceof Node\Stmt\Nop || $node instanceof Node\Stmt\Switch_ || $node instanceof Node\Stmt\TryCatch || $node instanceof Node\Stmt\Use_ || $node instanceof Node\Stmt\UseUse || $node instanceof Node\Expr\ConstFetch || $node instanceof Node\Expr\Match_ || $node instanceof Node\Expr\Variable || $node instanceof Node\Expr\Throw_ || $node instanceof Node\ComplexType || $node instanceof Node\Const_ || $node instanceof Node\Identifier || $node instanceof Node\Name || $node instanceof Node\Param || $node instanceof Node\Scalar) { + if ($node instanceof Node\Stmt\Declare_ || $node instanceof Node\Stmt\DeclareDeclare || $node instanceof Node\Stmt\Else_ || $node instanceof Node\Stmt\EnumCase || $node instanceof Node\Stmt\Finally_ || $node instanceof Node\Stmt\GroupUse || $node instanceof Node\Stmt\Label || $node instanceof Node\Stmt\Namespace_ || $node instanceof Node\Stmt\Nop || $node instanceof Node\Stmt\Switch_ || $node instanceof Node\Stmt\TryCatch || $node instanceof Node\Stmt\Use_ || $node instanceof Node\Stmt\UseUse || $node instanceof Node\Expr\ConstFetch || $node instanceof Node\Expr\Variable || $node instanceof Node\Expr\Throw_ || $node instanceof Node\ComplexType || $node instanceof Node\Const_ || $node instanceof Node\Identifier || $node instanceof Node\Name || $node instanceof Node\Param || $node instanceof Node\Scalar) { + return; + } + if ($node instanceof Node\Expr\Match_) { + foreach ($node->arms as $arm) { + $this->setLineBranch($arm->body->getStartLine(), $arm->body->getEndLine(), ++$this->nextBranch); + } return; } /* @@ -35327,6 +34483,16 @@ final class ExecutableLinesFindingVisitor extends NodeVisitorAbstract return; } if ($node instanceof Node\Stmt\Enum_ || $node instanceof Node\Stmt\Function_ || $node instanceof Node\Stmt\Class_ || $node instanceof Node\Stmt\ClassMethod || $node instanceof Node\Expr\Closure || $node instanceof Node\Stmt\Trait_) { + if ($node instanceof Node\Stmt\Function_ || $node instanceof Node\Stmt\ClassMethod) { + $unsets = []; + foreach ($node->getParams() as $param) { + foreach (range($param->getStartLine(), $param->getEndLine()) as $line) { + $unsets[$line] = \true; + } + } + unset($unsets[$node->getEndLine()]); + $this->unsets += $unsets; + } $isConcreteClassLike = $node instanceof Node\Stmt\Enum_ || $node instanceof Node\Stmt\Class_ || $node instanceof Node\Stmt\Trait_; if (null !== $node->stmts) { foreach ($node->stmts as $stmt) { @@ -35346,7 +34512,7 @@ final class ExecutableLinesFindingVisitor extends NodeVisitorAbstract } $hasEmptyBody = [] === $node->stmts || null === $node->stmts || 1 === count($node->stmts) && $node->stmts[0] instanceof Node\Stmt\Nop; if ($hasEmptyBody) { - if ($node->getEndLine() === $node->getStartLine()) { + if ($node->getEndLine() === $node->getStartLine() && isset($this->executableLinesGroupedByBranch[$node->getStartLine()])) { return; } $this->setLineBranch($node->getEndLine(), $node->getEndLine(), ++$this->nextBranch); @@ -35446,22 +34612,25 @@ final class ExecutableLinesFindingVisitor extends NodeVisitorAbstract } $this->setLineBranch($node->getStartLine(), $node->getEndLine(), ++$this->nextBranch); } - public function afterTraverse(array $nodes) : void + public function afterTraverse(array $nodes): void { $lines = explode("\n", $this->source); foreach ($lines as $lineNumber => $line) { $lineNumber++; - if (1 === preg_match('/^\\s*$/', $line) || isset($this->commentsToCheckForUnset[$lineNumber]) && 1 === preg_match(sprintf('/^\\s*%s\\s*$/', preg_quote($this->commentsToCheckForUnset[$lineNumber], '/')), $line)) { + if (1 === preg_match('/^\s*$/', $line) || isset($this->commentsToCheckForUnset[$lineNumber]) && 1 === preg_match(sprintf('/^\s*%s\s*$/', preg_quote($this->commentsToCheckForUnset[$lineNumber], '/')), $line)) { unset($this->executableLinesGroupedByBranch[$lineNumber]); } } $this->executableLinesGroupedByBranch = array_diff_key($this->executableLinesGroupedByBranch, $this->unsets); } - public function executableLinesGroupedByBranch() : array + /** + * @psalm-return LinesType + */ + public function executableLinesGroupedByBranch(): array { return $this->executableLinesGroupedByBranch; } - private function setLineBranch(int $start, int $end, int $branch) : void + private function setLineBranch(int $start, int $end, int $branch): void { foreach (range($start, $end) as $line) { $this->executableLinesGroupedByBranch[$line] = $branch; @@ -35483,18 +34652,47 @@ namespace PHPUnitPHAR\SebastianBergmann\CodeCoverage\StaticAnalysis; /** * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage + * + * @psalm-import-type CodeUnitFunctionType from \SebastianBergmann\CodeCoverage\StaticAnalysis\CodeUnitFindingVisitor + * @psalm-import-type CodeUnitMethodType from \SebastianBergmann\CodeCoverage\StaticAnalysis\CodeUnitFindingVisitor + * @psalm-import-type CodeUnitClassType from \SebastianBergmann\CodeCoverage\StaticAnalysis\CodeUnitFindingVisitor + * @psalm-import-type CodeUnitTraitType from \SebastianBergmann\CodeCoverage\StaticAnalysis\CodeUnitFindingVisitor + * @psalm-import-type LinesOfCodeType from \SebastianBergmann\CodeCoverage\StaticAnalysis\FileAnalyser + * @psalm-import-type LinesType from \SebastianBergmann\CodeCoverage\StaticAnalysis\FileAnalyser + * + * @psalm-type LinesOfCodeType = array{ + * linesOfCode: int, + * commentLinesOfCode: int, + * nonCommentLinesOfCode: int + * } + * @psalm-type LinesType = array */ interface FileAnalyser { - public function classesIn(string $filename) : array; - public function traitsIn(string $filename) : array; - public function functionsIn(string $filename) : array; /** - * @psalm-return array{linesOfCode: int, commentLinesOfCode: int, nonCommentLinesOfCode: int} + * @psalm-return array */ - public function linesOfCodeFor(string $filename) : array; - public function executableLinesIn(string $filename) : array; - public function ignoredLinesFor(string $filename) : array; + public function classesIn(string $filename): array; + /** + * @psalm-return array + */ + public function traitsIn(string $filename): array; + /** + * @psalm-return array + */ + public function functionsIn(string $filename): array; + /** + * @psalm-return LinesOfCodeType + */ + public function linesOfCodeFor(string $filename): array; + /** + * @psalm-return LinesType + */ + public function executableLinesIn(string $filename): array; + /** + * @psalm-return LinesType + */ + public function ignoredLinesFor(string $filename): array; } + * @psalm-var array */ - private $ignoredLines = []; - /** - * @var bool - */ - private $useAnnotationsForIgnoringCode; - /** - * @var bool - */ - private $ignoreDeprecated; + private array $ignoredLines = []; + private readonly bool $useAnnotationsForIgnoringCode; + private readonly bool $ignoreDeprecated; public function __construct(bool $useAnnotationsForIgnoringCode, bool $ignoreDeprecated) { $this->useAnnotationsForIgnoringCode = $useAnnotationsForIgnoringCode; $this->ignoreDeprecated = $ignoreDeprecated; } - public function enterNode(Node $node) : void + public function enterNode(Node $node): void { - if (!$node instanceof Class_ && !$node instanceof Trait_ && !$node instanceof Interface_ && !$node instanceof ClassMethod && !$node instanceof Function_ && !$node instanceof Attribute) { + if (!$node instanceof Class_ && !$node instanceof Trait_ && !$node instanceof Interface_ && !$node instanceof Enum_ && !$node instanceof ClassMethod && !$node instanceof Function_ && !$node instanceof Attribute) { return; } if ($node instanceof Class_ && $node->isAnonymous()) { @@ -35563,26 +34754,38 @@ final class IgnoredLinesFindingVisitor extends NodeVisitorAbstract if ($node instanceof Interface_) { return; } + if ($node instanceof Attribute && $node->name->toString() === 'PHPUnit\Framework\Attributes\CodeCoverageIgnore') { + $attributeGroup = $node->getAttribute('parent'); + $attributedNode = $attributeGroup->getAttribute('parent'); + for ($line = $attributedNode->getStartLine(); $line <= $attributedNode->getEndLine(); $line++) { + $this->ignoredLines[] = $line; + } + return; + } $this->processDocComment($node); } /** - * @psalm-return list + * @psalm-return array */ - public function ignoredLines() : array + public function ignoredLines(): array { return $this->ignoredLines; } - private function processDocComment(Node $node) : void + private function processDocComment(Node $node): void { $docComment = $node->getDocComment(); if ($docComment === null) { return; } - if (strpos($docComment->getText(), '@codeCoverageIgnore') !== \false) { - $this->ignoredLines = array_merge($this->ignoredLines, range($node->getStartLine(), $node->getEndLine())); + if (str_contains($docComment->getText(), '@codeCoverageIgnore')) { + for ($line = $node->getStartLine(); $line <= $node->getEndLine(); $line++) { + $this->ignoredLines[] = $line; + } } - if ($this->ignoreDeprecated && strpos($docComment->getText(), '@deprecated') !== \false) { - $this->ignoredLines = array_merge($this->ignoredLines, range($node->getStartLine(), $node->getEndLine())); + if ($this->ignoreDeprecated && str_contains($docComment->getText(), '@deprecated')) { + for ($line = $node->getStartLine(); $line <= $node->getEndLine(); $line++) { + $this->ignoredLines[] = $line; + } } } } @@ -35620,75 +34823,73 @@ use PHPUnitPHAR\SebastianBergmann\CodeCoverage\ParserException; use PHPUnitPHAR\SebastianBergmann\LinesOfCode\LineCountingVisitor; /** * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage + * + * @psalm-import-type CodeUnitFunctionType from \SebastianBergmann\CodeCoverage\StaticAnalysis\CodeUnitFindingVisitor + * @psalm-import-type CodeUnitMethodType from \SebastianBergmann\CodeCoverage\StaticAnalysis\CodeUnitFindingVisitor + * @psalm-import-type CodeUnitClassType from \SebastianBergmann\CodeCoverage\StaticAnalysis\CodeUnitFindingVisitor + * @psalm-import-type CodeUnitTraitType from \SebastianBergmann\CodeCoverage\StaticAnalysis\CodeUnitFindingVisitor + * @psalm-import-type LinesOfCodeType from \SebastianBergmann\CodeCoverage\StaticAnalysis\FileAnalyser + * @psalm-import-type LinesType from \SebastianBergmann\CodeCoverage\StaticAnalysis\FileAnalyser */ final class ParsingFileAnalyser implements FileAnalyser { /** - * @var array + * @psalm-var array> */ - private $classes = []; + private array $classes = []; /** - * @var array + * @psalm-var array> */ - private $traits = []; + private array $traits = []; /** - * @var array + * @psalm-var array> */ - private $functions = []; + private array $functions = []; /** - * @var array + * @var array */ - private $linesOfCode = []; + private array $linesOfCode = []; /** - * @var array + * @var array */ - private $ignoredLines = []; + private array $ignoredLines = []; /** - * @var array - */ - private $executableLines = []; - /** - * @var bool - */ - private $useAnnotationsForIgnoringCode; - /** - * @var bool + * @var array */ - private $ignoreDeprecatedCode; + private array $executableLines = []; + private readonly bool $useAnnotationsForIgnoringCode; + private readonly bool $ignoreDeprecatedCode; public function __construct(bool $useAnnotationsForIgnoringCode, bool $ignoreDeprecatedCode) { $this->useAnnotationsForIgnoringCode = $useAnnotationsForIgnoringCode; $this->ignoreDeprecatedCode = $ignoreDeprecatedCode; } - public function classesIn(string $filename) : array + public function classesIn(string $filename): array { $this->analyse($filename); return $this->classes[$filename]; } - public function traitsIn(string $filename) : array + public function traitsIn(string $filename): array { $this->analyse($filename); return $this->traits[$filename]; } - public function functionsIn(string $filename) : array + public function functionsIn(string $filename): array { $this->analyse($filename); return $this->functions[$filename]; } - /** - * @psalm-return array{linesOfCode: int, commentLinesOfCode: int, nonCommentLinesOfCode: int} - */ - public function linesOfCodeFor(string $filename) : array + public function linesOfCodeFor(string $filename): array { $this->analyse($filename); return $this->linesOfCode[$filename]; } - public function executableLinesIn(string $filename) : array + public function executableLinesIn(string $filename): array { $this->analyse($filename); return $this->executableLines[$filename]; } - public function ignoredLinesFor(string $filename) : array + public function ignoredLinesFor(string $filename): array { $this->analyse($filename); return $this->ignoredLines[$filename]; @@ -35696,7 +34897,7 @@ final class ParsingFileAnalyser implements FileAnalyser /** * @throws ParserException */ - private function analyse(string $filename) : void + private function analyse(string $filename): void { if (isset($this->classes[$filename])) { return; @@ -35706,6 +34907,7 @@ final class ParsingFileAnalyser implements FileAnalyser if ($linesOfCode === 0 && !empty($source)) { $linesOfCode = 1; } + assert($linesOfCode > 0); $parser = (new ParserFactory())->createForHostVersion(); try { $nodes = $parser->parse($source); @@ -35739,7 +34941,7 @@ final class ParsingFileAnalyser implements FileAnalyser $result = $lineCountingVisitor->result(); $this->linesOfCode[$filename] = ['linesOfCode' => $result->linesOfCode(), 'commentLinesOfCode' => $result->commentLinesOfCode(), 'nonCommentLinesOfCode' => $result->nonCommentLinesOfCode()]; } - private function findLinesIgnoredByLineBasedAnnotations(string $filename, string $source, bool $useAnnotationsForIgnoringCode) : void + private function findLinesIgnoredByLineBasedAnnotations(string $filename, string $source, bool $useAnnotationsForIgnoringCode): void { if (!$useAnnotationsForIgnoringCode) { return; @@ -35769,6 +34971,412 @@ final class ParsingFileAnalyser implements FileAnalyser } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnitPHAR\SebastianBergmann\CodeCoverage\Test\TestSize; + +/** + * @psalm-immutable + */ +abstract class Known extends TestSize +{ + /** + * @psalm-assert-if-true Known $this + */ + public function isKnown(): bool + { + return \true; + } + abstract public function isGreaterThan(self $other): bool; +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnitPHAR\SebastianBergmann\CodeCoverage\Test\TestSize; + +/** + * @psalm-immutable + */ +final class Large extends Known +{ + /** + * @psalm-assert-if-true Large $this + */ + public function isLarge(): bool + { + return \true; + } + public function isGreaterThan(TestSize $other): bool + { + return !$other->isLarge(); + } + public function asString(): string + { + return 'large'; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnitPHAR\SebastianBergmann\CodeCoverage\Test\TestSize; + +/** + * @psalm-immutable + */ +final class Medium extends Known +{ + /** + * @psalm-assert-if-true Medium $this + */ + public function isMedium(): bool + { + return \true; + } + public function isGreaterThan(TestSize $other): bool + { + return $other->isSmall(); + } + public function asString(): string + { + return 'medium'; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnitPHAR\SebastianBergmann\CodeCoverage\Test\TestSize; + +/** + * @psalm-immutable + */ +final class Small extends Known +{ + /** + * @psalm-assert-if-true Small $this + */ + public function isSmall(): bool + { + return \true; + } + public function isGreaterThan(TestSize $other): bool + { + return \false; + } + public function asString(): string + { + return 'small'; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnitPHAR\SebastianBergmann\CodeCoverage\Test\TestSize; + +/** + * @psalm-immutable + */ +abstract class TestSize +{ + public static function unknown(): self + { + return new Unknown(); + } + public static function small(): self + { + return new Small(); + } + public static function medium(): self + { + return new Medium(); + } + public static function large(): self + { + return new Large(); + } + /** + * @psalm-assert-if-true Known $this + */ + public function isKnown(): bool + { + return \false; + } + /** + * @psalm-assert-if-true Unknown $this + */ + public function isUnknown(): bool + { + return \false; + } + /** + * @psalm-assert-if-true Small $this + */ + public function isSmall(): bool + { + return \false; + } + /** + * @psalm-assert-if-true Medium $this + */ + public function isMedium(): bool + { + return \false; + } + /** + * @psalm-assert-if-true Large $this + */ + public function isLarge(): bool + { + return \false; + } + abstract public function asString(): string; +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnitPHAR\SebastianBergmann\CodeCoverage\Test\TestSize; + +/** + * @psalm-immutable + */ +final class Unknown extends TestSize +{ + /** + * @psalm-assert-if-true Unknown $this + */ + public function isUnknown(): bool + { + return \true; + } + public function asString(): string + { + return 'unknown'; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnitPHAR\SebastianBergmann\CodeCoverage\Test\TestStatus; + +/** + * @psalm-immutable + */ +final class Failure extends Known +{ + /** + * @psalm-assert-if-true Failure $this + */ + public function isFailure(): bool + { + return \true; + } + public function asString(): string + { + return 'failure'; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnitPHAR\SebastianBergmann\CodeCoverage\Test\TestStatus; + +/** + * @psalm-immutable + */ +abstract class Known extends TestStatus +{ + /** + * @psalm-assert-if-true Known $this + */ + public function isKnown(): bool + { + return \true; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnitPHAR\SebastianBergmann\CodeCoverage\Test\TestStatus; + +/** + * @psalm-immutable + */ +final class Success extends Known +{ + /** + * @psalm-assert-if-true Success $this + */ + public function isSuccess(): bool + { + return \true; + } + public function asString(): string + { + return 'success'; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnitPHAR\SebastianBergmann\CodeCoverage\Test\TestStatus; + +/** + * @psalm-immutable + */ +abstract class TestStatus +{ + public static function unknown(): self + { + return new Unknown(); + } + public static function success(): self + { + return new Success(); + } + public static function failure(): self + { + return new Failure(); + } + /** + * @psalm-assert-if-true Known $this + */ + public function isKnown(): bool + { + return \false; + } + /** + * @psalm-assert-if-true Unknown $this + */ + public function isUnknown(): bool + { + return \false; + } + /** + * @psalm-assert-if-true Success $this + */ + public function isSuccess(): bool + { + return \false; + } + /** + * @psalm-assert-if-true Failure $this + */ + public function isFailure(): bool + { + return \false; + } + abstract public function asString(): string; +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnitPHAR\SebastianBergmann\CodeCoverage\Test\TestStatus; + +/** + * @psalm-immutable + */ +final class Unknown extends TestStatus +{ + /** + * @psalm-assert-if-true Unknown $this + */ + public function isUnknown(): bool + { + return \true; + } + public function asString(): string + { + return 'unknown'; + } +} +fraction = $fraction; $this->total = $total; } - public function asFloat() : float + public function asFloat(): float { if ($this->total > 0) { return $this->fraction / $this->total * 100; } return 100.0; } - public function asString() : string + public function asString(): string { if ($this->total > 0) { return sprintf('%01.2F%%', $this->asFloat()); } return ''; } - public function asFixedWidthString() : string + public function asFixedWidthString(): string { if ($this->total > 0) { return sprintf('%6.2F%%', $this->asFloat()); @@ -35873,17 +35475,79 @@ namespace PHPUnitPHAR\SebastianBergmann\CodeCoverage; use function dirname; use PHPUnitPHAR\SebastianBergmann\Version as VersionId; final class Version +{ + private static string $version = ''; + public static function id(): string + { + if (self::$version === '') { + self::$version = (new VersionId('10.1.15', dirname(__DIR__)))->asString(); + } + return self::$version; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnitPHAR\SebastianBergmann\FileIterator; + +use function assert; +use function str_starts_with; +use RecursiveDirectoryIterator; +use RecursiveFilterIterator; +use SplFileInfo; +/** + * @internal This class is not covered by the backward compatibility promise for phpunit/php-file-iterator + */ +final class ExcludeIterator extends RecursiveFilterIterator { /** - * @var string + * @psalm-var list + */ + private array $exclude; + /** + * @psalm-param list $exclude */ - private static $version; - public static function id() : string + public function __construct(RecursiveDirectoryIterator $iterator, array $exclude) + { + parent::__construct($iterator); + $this->exclude = $exclude; + } + public function accept(): bool { - if (self::$version === null) { - self::$version = (new VersionId('9.2.31', dirname(__DIR__)))->getVersion(); + $current = $this->current(); + assert($current instanceof SplFileInfo); + $path = $current->getRealPath(); + if ($path === \false) { + return \false; } - return self::$version; + foreach ($this->exclude as $exclude) { + if (str_starts_with($path, $exclude)) { + return \false; + } + } + return \true; + } + public function hasChildren(): bool + { + return $this->getInnerIterator()->hasChildren(); + } + public function getChildren(): self + { + return new self($this->getInnerIterator()->getChildren(), $this->exclude); + } + public function getInnerIterator(): RecursiveDirectoryIterator + { + $innerIterator = parent::getInnerIterator(); + assert($innerIterator instanceof RecursiveDirectoryIterator); + return $innerIterator; } } |non-empty-string $paths + * @psalm-param list|string $suffixes + * @psalm-param list|string $prefixes + * @psalm-param list $exclude + * + * @psalm-return list */ - public function getFilesAsArray($paths, $suffixes = '', $prefixes = '', array $exclude = [], bool $commonPath = \false) : array + public function getFilesAsArray(array|string $paths, array|string $suffixes = '', array|string $prefixes = '', array $exclude = []): array { - if (is_string($paths)) { - $paths = [$paths]; - } $iterator = (new Factory())->getFileIterator($paths, $suffixes, $prefixes, $exclude); $files = []; foreach ($iterator as $file) { + assert($file instanceof SplFileInfo); $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; - } } |non-empty-string $paths + * @psalm-param list|string $suffixes + * @psalm-param list|string $prefixes + * @psalm-param list $exclude */ - public function getFileIterator($paths, $suffixes = '', $prefixes = '', array $exclude = []) : AppendIterator + public function getFileIterator(array|string $paths, array|string $suffixes = '', array|string $prefixes = '', array $exclude = []): AppendIterator { if (is_string($paths)) { $paths = [$paths]; } - $paths = $this->getPathsAfterResolvingWildcards($paths); - $exclude = $this->getPathsAfterResolvingWildcards($exclude); + $paths = $this->resolveWildcards($paths); + $exclude = $this->resolveWildcards($exclude); if (is_string($prefixes)) { if ($prefixes !== '') { $prefixes = [$prefixes]; @@ -36033,22 +35657,29 @@ class Factory $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)); + $iterator->append(new Iterator($path, new RecursiveIteratorIterator(new ExcludeIterator(new RecursiveDirectoryIterator($path, FilesystemIterator::FOLLOW_SYMLINKS | FilesystemIterator::SKIP_DOTS), $exclude)), $suffixes, $prefixes)); } } return $iterator; } - protected function getPathsAfterResolvingWildcards(array $paths) : array + /** + * @psalm-param list $paths + * + * @psalm-return list + */ + private function resolveWildcards(array $paths): array { $_paths = [[]]; foreach ($paths as $path) { if ($locals = glob($path, GLOB_ONLYDIR)) { - $_paths[] = array_map('\\realpath', $locals); + $_paths[] = array_map('\realpath', $locals); } else { + // @codeCoverageIgnoreStart $_paths[] = [realpath($path)]; + // @codeCoverageIgnoreEnd } } - return array_filter(array_merge(...$_paths)); + return array_values(array_filter(array_merge(...$_paths))); } } + * + * @internal This class is not covered by the backward compatibility promise for phpunit/php-file-iterator + */ +final class Iterator extends FilterIterator { public const PREFIX = 0; public const SUFFIX = 1; + private string|false $basePath; /** - * @var string - */ - private $basePath; - /** - * @var array + * @psalm-var list */ - private $suffixes = []; + private array $suffixes; /** - * @var array + * @psalm-var list */ - private $prefixes = []; + private array $prefixes; /** - * @var array + * @psalm-param list $suffixes + * @psalm-param list $prefixes */ - private $exclude = []; - public function __construct(string $basePath, \Iterator $iterator, array $suffixes = [], array $prefixes = [], array $exclude = []) + public function __construct(string $basePath, \Iterator $iterator, array $suffixes = [], array $prefixes = []) { $this->basePath = realpath($basePath); $this->prefixes = $prefixes; $this->suffixes = $suffixes; - $this->exclude = array_filter(array_map('realpath', $exclude)); parent::__construct($iterator); } - public function accept() : bool + public function accept(): bool { $current = $this->getInnerIterator()->current(); + assert($current instanceof SplFileInfo); $filename = $current->getFilename(); $realPath = $current->getRealPath(); if ($realPath === \false) { + // @codeCoverageIgnoreStart return \false; + // @codeCoverageIgnoreEnd } return $this->acceptPath($realPath) && $this->acceptPrefix($filename) && $this->acceptSuffix($filename); } - private function acceptPath(string $path) : bool + 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))) { + if (preg_match('=/\.[^/]*/=', str_replace((string) $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 + private function acceptPrefix(string $filename): bool { return $this->acceptSubString($filename, $this->prefixes, self::PREFIX); } - private function acceptSuffix(string $filename) : bool + private function acceptSuffix(string $filename): bool { return $this->acceptSubString($filename, $this->suffixes, self::SUFFIX); } - private function acceptSubString(string $filename, array $subStrings, int $type) : bool + /** + * @psalm-param list $subStrings + */ + 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; + if ($type === self::PREFIX && str_starts_with($filename, $string) || $type === self::SUFFIX && str_ends_with($filename, $string)) { + return \true; } } - return $matched; + return \false; } } -php-file-iterator +BSD 3-Clause License -Copyright (c) 2009-2021, Sebastian Bergmann . +Copyright (c) 2009-2023, Sebastian Bergmann All rights reserved. Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the - distribution. - - * Neither the name of Sebastian Bergmann nor the names of his - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN -ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. canInvokeWithTimeout()) { throw new ProcessControlExtensionNotLoadedException('The pcntl (process control) extension for PHP is required'); } - pcntl_signal(SIGALRM, function () : void { - throw new TimeoutException(sprintf('Execution aborted after %d second%s', $this->timeout, $this->timeout === 1 ? '' : 's')); + pcntl_signal(SIGALRM, function (): void { + throw new TimeoutException(sprintf('Execution aborted after %d second%s', $this->timeout, ($this->timeout === 1) ? '' : 's')); }, \true); $this->timeout = $timeout; pcntl_async_signals(\true); @@ -36227,7 +35851,7 @@ final class Invoker pcntl_alarm(0); } } - public function canInvokeWithTimeout() : bool + public function canInvokeWithTimeout(): bool { return function_exists('pcntl_signal') && function_exists('pcntl_async_signals') && function_exists('pcntl_alarm'); } @@ -36283,39 +35907,35 @@ use RuntimeException; final class TimeoutException extends RuntimeException implements Exception { } -phpunit/php-text-template +BSD 3-Clause License -Copyright (c) 2009-2020, Sebastian Bergmann . +Copyright (c) 2009-2023, Sebastian Bergmann All rights reserved. Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the - distribution. - - * Neither the name of Sebastian Bergmann nor the names of his - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN -ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - private $values = []; + private array $values = []; /** * @throws InvalidArgumentException */ @@ -36365,29 +35977,34 @@ final class Template /** * @throws InvalidArgumentException */ - public function setFile(string $file) : void + public function setFile(string $file): void { - $distFile = $file . '.dist'; - if (file_exists($file)) { + if (is_file($file)) { $this->template = file_get_contents($file); - } elseif (file_exists($distFile)) { + return; + } + $distFile = $file . '.dist'; + if (is_file($distFile)) { $this->template = file_get_contents($distFile); - } else { - throw new InvalidArgumentException(sprintf('Failed to load template "%s"', $file)); + return; } + throw new InvalidArgumentException(sprintf('Failed to load template "%s"', $file)); } - public function setVar(array $values, bool $merge = \true) : void + /** + * @psalm-param array $values + */ + public function setVar(array $values, bool $merge = \true): void { if (!$merge || empty($this->values)) { $this->values = $values; - } else { - $this->values = array_merge($this->values, $values); + return; } + $this->values = array_merge($this->values, $values); } - public function render() : string + public function render(): string { $keys = []; - foreach ($this->values as $key => $value) { + foreach (array_keys($this->values) as $key) { $keys[] = $this->openDelimiter . $key . $this->closeDelimiter; } return str_replace($keys, $this->values, $this->template); @@ -36395,9 +36012,9 @@ final class Template /** * @codeCoverageIgnore */ - public function renderTo(string $target) : void + public function renderTo(string $target): void { - if (!file_put_contents($target, $this->render())) { + if (!@file_put_contents($target, $this->render())) { throw new RuntimeException(sprintf('Writing rendered result to "%s" failed', $target)); } } @@ -36472,31 +36089,16 @@ use function sprintf; */ final class Duration { - /** - * @var float - */ - private $nanoseconds; - /** - * @var int - */ - private $hours; - /** - * @var int - */ - private $minutes; - /** - * @var int - */ - private $seconds; - /** - * @var int - */ - private $milliseconds; - public static function fromMicroseconds(float $microseconds) : self + private readonly float $nanoseconds; + private readonly int $hours; + private readonly int $minutes; + private readonly int $seconds; + private readonly int $milliseconds; + public static function fromMicroseconds(float $microseconds): self { return new self($microseconds * 1000); } - public static function fromNanoseconds(float $nanoseconds) : self + public static function fromNanoseconds(float $nanoseconds): self { return new self($nanoseconds); } @@ -36516,23 +36118,23 @@ final class Duration $this->seconds = (int) $seconds; $this->milliseconds = (int) $milliseconds; } - public function asNanoseconds() : float + public function asNanoseconds(): float { return $this->nanoseconds; } - public function asMicroseconds() : float + public function asMicroseconds(): float { return $this->nanoseconds / 1000; } - public function asMilliseconds() : float + public function asMilliseconds(): float { return $this->nanoseconds / 1000000; } - public function asSeconds() : float + public function asSeconds(): float { return $this->nanoseconds / 1000000000; } - public function asString() : string + public function asString(): string { $result = ''; if ($this->hours > 0) { @@ -36546,39 +36148,35 @@ final class Duration return $result; } } -phpunit/php-timer +BSD 3-Clause License -Copyright (c) 2010-2020, Sebastian Bergmann . +Copyright (c) 2010-2023, Sebastian Bergmann All rights reserved. Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the - distribution. - - * Neither the name of Sebastian Bergmann nor the names of his - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN -ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ private const SIZES = ['GB' => 1073741824, 'MB' => 1048576, 'KB' => 1024]; - public function resourceUsage(Duration $duration) : string + public function resourceUsage(Duration $duration): string { return sprintf('Time: %s, Memory: %s', $duration->asString(), $this->bytesToString(memory_get_peak_usage(\true))); } /** * @throws TimeSinceStartOfRequestNotAvailableException */ - public function resourceUsageSinceStartOfRequest() : string + public function resourceUsageSinceStartOfRequest(): string { if (!isset($_SERVER['REQUEST_TIME_FLOAT'])) { throw new TimeSinceStartOfRequestNotAvailableException('Cannot determine time at which the request started because $_SERVER[\'REQUEST_TIME_FLOAT\'] is not available'); @@ -36619,15 +36217,15 @@ final class ResourceUsageFormatter } return $this->resourceUsage(Duration::fromMicroseconds(1000000 * (microtime(\true) - $_SERVER['REQUEST_TIME_FLOAT']))); } - private function bytesToString(int $bytes) : string + private function bytesToString(int $bytes): string { foreach (self::SIZES as $unit => $value) { if ($bytes >= $value) { - return sprintf('%.2f %s', $bytes >= 1024 ? $bytes / $value : $bytes, $unit); + return sprintf('%.2f %s', $bytes / $value, $unit); } } // @codeCoverageIgnoreStart - return $bytes . ' byte' . ($bytes !== 1 ? 's' : ''); + return $bytes . ' byte' . (($bytes !== 1) ? 's' : ''); // @codeCoverageIgnoreEnd } } @@ -36651,15 +36249,15 @@ final class Timer /** * @psalm-var list */ - private $startTimes = []; - public function start() : void + private array $startTimes = []; + public function start(): void { $this->startTimes[] = (float) hrtime(\true); } /** * @throws NoActiveTimerException */ - public function stop() : Duration + public function stop(): Duration { if (empty($this->startTimes)) { throw new NoActiveTimerException('Timer::start() has to be called before Timer::stop()'); @@ -36718,21930 +36316,14539 @@ use RuntimeException; final class TimeSinceStartOfRequestNotAvailableException extends RuntimeException implements Exception { } -fqsen = $fqsen; - if (isset($matches[2])) { - $this->name = $matches[2]; - } else { - $matches = explode('\\', $fqsen); - $name = end($matches); - assert(is_string($name)); - $this->name = trim($name, '()'); - } - } - /** - * converts this class to string. - */ - public function __toString() : string - { - return $this->fqsen; - } - /** - * Returns the name of the element without path. - */ - public function getName() : string - { - return $this->name; - } -} -The MIT License (MIT) - -Copyright (c) 2015 phpDocumentor - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - -lineNumber = $lineNumber; - $this->columnNumber = $columnNumber; - } - /** - * Returns the line number that is covered by this location. - */ - public function getLineNumber() : int - { - return $this->lineNumber; - } - /** - * Returns the column number (character position on a line) for this location object. - */ - public function getColumnNumber() : int - { - return $this->columnNumber; - } -} -summary = $summary; - $this->description = $description ?: new DocBlock\Description(''); - foreach ($tags as $tag) { - $this->addTag($tag); - } - $this->context = $context; - $this->location = $location; - $this->isTemplateEnd = $isTemplateEnd; - $this->isTemplateStart = $isTemplateStart; - } - public function getSummary() : string - { - return $this->summary; - } - public function getDescription() : DocBlock\Description - { - return $this->description; - } - /** - * Returns the current context. - */ - public function getContext() : ?Types\Context - { - return $this->context; - } - /** - * Returns the current location. - */ - public function getLocation() : ?Location - { - return $this->location; - } - /** - * Returns whether this DocBlock is the start of a Template section. - * - * A Docblock may serve as template for a series of subsequent DocBlocks. This is indicated by a special marker - * (`#@+`) that is appended directly after the opening `/**` of a DocBlock. - * - * An example of such an opening is: - * - * ``` - * /**#@+ - * * My DocBlock - * * / - * ``` - * - * The description and tags (not the summary!) are copied onto all subsequent DocBlocks and also applied to all - * elements that follow until another DocBlock is found that contains the closing marker (`#@-`). - * - * @see self::isTemplateEnd() for the check whether a closing marker was provided. - */ - public function isTemplateStart() : bool - { - return $this->isTemplateStart; - } - /** - * Returns whether this DocBlock is the end of a Template section. - * - * @see self::isTemplateStart() for a more complete description of the Docblock Template functionality. - */ - public function isTemplateEnd() : bool - { - return $this->isTemplateEnd; - } - /** - * Returns the tags for this DocBlock. - * - * @return Tag[] - */ - public function getTags() : array - { - return $this->tags; - } - /** - * Returns an array of tags matching the given name. If no tags are found - * an empty array is returned. - * - * @param string $name String to search by. - * - * @return Tag[] - */ - public function getTagsByName(string $name) : array - { - $result = []; - foreach ($this->getTags() as $tag) { - if ($tag->getName() !== $name) { - continue; - } - $result[] = $tag; - } - return $result; - } - /** - * Returns an array of tags with type matching the given name. If no tags are found - * an empty array is returned. - * - * @param string $name String to search by. - * - * @return TagWithType[] - */ - public function getTagsWithTypeByName(string $name) : array - { - $result = []; - foreach ($this->getTagsByName($name) as $tag) { - if (!$tag instanceof TagWithType) { - continue; - } - $result[] = $tag; - } - return $result; - } - /** - * Checks if a tag of a certain type is present in this DocBlock. - * - * @param string $name Tag name to check for. - */ - public function hasTag(string $name) : bool - { - foreach ($this->getTags() as $tag) { - if ($tag->getName() === $name) { - return \true; - } - } - return \false; - } - /** - * Remove a tag from this DocBlock. - * - * @param Tag $tagToRemove The tag to remove. - */ - public function removeTag(Tag $tagToRemove) : void - { - foreach ($this->tags as $key => $tag) { - if ($tag === $tagToRemove) { - unset($this->tags[$key]); - break; - } - } - } - /** - * Adds a tag to this DocBlock. - * - * @param Tag $tag The tag to add. - */ - private function addTag(Tag $tag) : void - { - $this->tags[] = $tag; - } -} -create('This is a {@see Description}', $context); - * - * The description factory will interpret the given body and create a body template and list of tags from them, and pass - * that onto the constructor if this class. - * - * > The $context variable is a class of type {@see \phpDocumentor\Reflection\Types\Context} and contains the namespace - * > and the namespace aliases that apply to this DocBlock. These are used by the Factory to resolve and expand partial - * > type names and FQSENs. - * - * If you do not want to use the DescriptionFactory you can pass a body template and tag listing like this: - * - * $description = new Description( - * 'This is a %1$s', - * [ new See(new Fqsen('\phpDocumentor\Reflection\DocBlock\Description')) ] - * ); - * - * It is generally recommended to use the Factory as that will also apply escaping rules, while the Description object - * is mainly responsible for rendering. - * - * @see DescriptionFactory to create a new Description. - * @see Description\Formatter for the formatting of the body and tags. - */ -class Description -{ - /** @var string */ - private $bodyTemplate; - /** @var Tag[] */ - private $tags; - /** - * Initializes a Description with its body (template) and a listing of the tags used in the body template. - * - * @param Tag[] $tags - */ - public function __construct(string $bodyTemplate, array $tags = []) - { - $this->bodyTemplate = $bodyTemplate; - $this->tags = $tags; - } - /** - * Returns the body template. - */ - public function getBodyTemplate() : string - { - return $this->bodyTemplate; - } - /** - * Returns the tags for this DocBlock. - * - * @return Tag[] - */ - public function getTags() : array + + + + + This Schema file defines the rules by which the XML configuration file of PHPUnit 10.5 may be structured. + + + + + + Root Element + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The main type specifying the document structure + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event; + +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class CollectingDispatcher implements \PHPUnit\Event\Dispatcher +{ + private \PHPUnit\Event\EventCollection $events; + public function __construct() { - return $this->tags; + $this->events = new \PHPUnit\Event\EventCollection(); } - /** - * Renders this description as a string where the provided formatter will format the tags in the expected string - * format. - */ - public function render(?Formatter $formatter = null) : string + public function dispatch(\PHPUnit\Event\Event $event): void { - if ($formatter === null) { - $formatter = new PassthroughFormatter(); - } - $tags = []; - foreach ($this->tags as $tag) { - $tags[] = '{' . $formatter->format($tag) . '}'; - } - return vsprintf($this->bodyTemplate, $tags); + $this->events->add($event); } - /** - * Returns a plain string representation of this description. - */ - public function __toString() : string + public function flush(): \PHPUnit\Event\EventCollection { - return $this->render(); + $events = $this->events; + $this->events = new \PHPUnit\Event\EventCollection(); + return $events; } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. - * - * @link http://phpdoc.org */ -namespace PHPUnitPHAR\phpDocumentor\Reflection\DocBlock; +namespace PHPUnit\Event; -use PHPUnitPHAR\phpDocumentor\Reflection\Types\Context as TypeContext; -use PHPUnitPHAR\phpDocumentor\Reflection\Utils; -use function count; -use function implode; -use function ltrim; -use function min; -use function str_replace; -use function strlen; -use function strpos; -use function substr; -use function trim; -use const PREG_SPLIT_DELIM_CAPTURE; /** - * Creates a new Description object given a body of text. - * - * Descriptions in phpDocumentor are somewhat complex entities as they can contain one or more tags inside their - * body that can be replaced with a readable output. The replacing is done by passing a Formatter object to the - * Description object's `render` method. - * - * In addition to the above does a Description support two types of escape sequences: - * - * 1. `{@}` to escape the `@` character to prevent it from being interpreted as part of a tag, i.e. `{{@}link}` - * 2. `{}` to escape the `}` character, this can be used if you want to use the `}` character in the description - * of an inline tag. - * - * If a body consists of multiple lines then this factory will also remove any superfluous whitespace at the beginning - * of each line while maintaining any indentation that is used. This will prevent formatting parsers from tripping - * over unexpected spaces as can be observed with tag descriptions. + * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -class DescriptionFactory +final class DeferringDispatcher implements \PHPUnit\Event\SubscribableDispatcher { - /** @var TagFactory */ - private $tagFactory; - /** - * Initializes this factory with the means to construct (inline) tags. - */ - public function __construct(TagFactory $tagFactory) + private readonly \PHPUnit\Event\SubscribableDispatcher $dispatcher; + private \PHPUnit\Event\EventCollection $events; + private bool $recording = \true; + public function __construct(\PHPUnit\Event\SubscribableDispatcher $dispatcher) { - $this->tagFactory = $tagFactory; + $this->dispatcher = $dispatcher; + $this->events = new \PHPUnit\Event\EventCollection(); } - /** - * Returns the parsed text of this description. - */ - public function create(string $contents, ?TypeContext $context = null) : Description + public function registerTracer(\PHPUnit\Event\Tracer\Tracer $tracer): void { - $tokens = $this->lex($contents); - $count = count($tokens); - $tagCount = 0; - $tags = []; - for ($i = 1; $i < $count; $i += 2) { - $tags[] = $this->tagFactory->create($tokens[$i], $context); - $tokens[$i] = '%' . ++$tagCount . '$s'; - } - //In order to allow "literal" inline tags, the otherwise invalid - //sequence "{@}" is changed to "@", and "{}" is changed to "}". - //"%" is escaped to "%%" because of vsprintf. - //See unit tests for examples. - for ($i = 0; $i < $count; $i += 2) { - $tokens[$i] = str_replace(['{@}', '{}', '%'], ['@', '}', '%%'], $tokens[$i]); - } - return new Description(implode('', $tokens), $tags); + $this->dispatcher->registerTracer($tracer); } - /** - * Strips the contents from superfluous whitespace and splits the description into a series of tokens. - * - * @return string[] A series of tokens of which the description text is composed. - */ - private function lex(string $contents) : array + public function registerSubscriber(\PHPUnit\Event\Subscriber $subscriber): void { - $contents = $this->removeSuperfluousStartingWhitespace($contents); - // performance optimalization; if there is no inline tag, don't bother splitting it up. - if (strpos($contents, '{@') === \false) { - return [$contents]; - } - return Utils::pregSplit('/\\{ - # "{@}" is not a valid inline tag. This ensures that we do not treat it as one, but treat it literally. - (?!@\\}) - # We want to capture the whole tag line, but without the inline tag delimiters. - (\\@ - # Match everything up to the next delimiter. - [^{}]* - # Nested inline tag content should not be captured, or it will appear in the result separately. - (?: - # Match nested inline tags. - (?: - # Because we did not catch the tag delimiters earlier, we must be explicit with them here. - # Notice that this also matches "{}", as a way to later introduce it as an escape sequence. - \\{(?1)?\\} - | - # Make sure we match hanging "{". - \\{ - ) - # Match content after the nested inline tag. - [^{}]* - )* # If there are more inline tags, match them as well. We use "*" since there may not be any - # nested inline tags. - ) - \\}/Sux', $contents, 0, PREG_SPLIT_DELIM_CAPTURE); + $this->dispatcher->registerSubscriber($subscriber); } - /** - * Removes the superfluous from a multi-line description. - * - * When a description has more than one line then it can happen that the second and subsequent lines have an - * additional indentation. This is commonly in use with tags like this: - * - * {@}since 1.1.0 This is an example - * description where we have an - * indentation in the second and - * subsequent lines. - * - * If we do not normalize the indentation then we have superfluous whitespace on the second and subsequent - * lines and this may cause rendering issues when, for example, using a Markdown converter. - */ - private function removeSuperfluousStartingWhitespace(string $contents) : string + public function dispatch(\PHPUnit\Event\Event $event): void { - $lines = Utils::pregSplit("/\r\n?|\n/", $contents); - // if there is only one line then we don't have lines with superfluous whitespace and - // can use the contents as-is - if (count($lines) <= 1) { - return $contents; - } - // determine how many whitespace characters need to be stripped - $startingSpaceCount = 9999999; - for ($i = 1, $iMax = count($lines); $i < $iMax; ++$i) { - // lines with a no length do not count as they are not indented at all - if (trim($lines[$i]) === '') { - continue; - } - // determine the number of prefixing spaces by checking the difference in line length before and after - // an ltrim - $startingSpaceCount = min($startingSpaceCount, strlen($lines[$i]) - strlen(ltrim($lines[$i]))); + if ($this->recording) { + $this->events->add($event); + return; } - // strip the number of spaces from each line - if ($startingSpaceCount > 0) { - for ($i = 1, $iMax = count($lines); $i < $iMax; ++$i) { - $lines[$i] = substr($lines[$i], $startingSpaceCount); - } + $this->dispatcher->dispatch($event); + } + public function flush(): void + { + $this->recording = \false; + foreach ($this->events as $event) { + $this->dispatcher->dispatch($event); } - return implode("\n", $lines); + $this->events = new \PHPUnit\Event\EventCollection(); } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. - * - * @link http://phpdoc.org */ -namespace PHPUnitPHAR\phpDocumentor\Reflection\DocBlock; +namespace PHPUnit\Event; -use PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\Tags\Example; -use function array_slice; -use function file; -use function getcwd; -use function implode; -use function is_readable; -use function rtrim; +use function array_key_exists; +use function dirname; use function sprintf; -use function trim; -use const DIRECTORY_SEPARATOR; +use function str_starts_with; +use Throwable; /** - * Class used to find an example file's location based on a given ExampleDescriptor. + * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -class ExampleFinder +final class DirectDispatcher implements \PHPUnit\Event\SubscribableDispatcher { - /** @var string */ - private $sourceDirectory = ''; - /** @var string[] */ - private $exampleDirectories = []; - /** - * Attempts to find the example contents for the given descriptor. - */ - public function find(Example $example) : string - { - $filename = $example->getFilePath(); - $file = $this->getExampleFileContents($filename); - if (!$file) { - return sprintf('** File not found : %s **', $filename); - } - return implode('', array_slice($file, $example->getStartingLine() - 1, $example->getLineCount())); - } + private readonly \PHPUnit\Event\TypeMap $typeMap; /** - * Registers the project's root directory where an 'examples' folder can be expected. + * @psalm-var array> */ - public function setSourceDirectory(string $directory = '') : void - { - $this->sourceDirectory = $directory; - } + private array $subscribers = []; /** - * Returns the project's root directory where an 'examples' folder can be expected. + * @psalm-var list */ - public function getSourceDirectory() : string + private array $tracers = []; + public function __construct(\PHPUnit\Event\TypeMap $map) { - return $this->sourceDirectory; + $this->typeMap = $map; } - /** - * Registers a series of directories that may contain examples. - * - * @param string[] $directories - */ - public function setExampleDirectories(array $directories) : void + public function registerTracer(\PHPUnit\Event\Tracer\Tracer $tracer): void { - $this->exampleDirectories = $directories; + $this->tracers[] = $tracer; } /** - * Returns a series of directories that may contain examples. - * - * @return string[] + * @throws MapError + * @throws UnknownSubscriberTypeException */ - public function getExampleDirectories() : array + public function registerSubscriber(\PHPUnit\Event\Subscriber $subscriber): void { - return $this->exampleDirectories; + if (!$this->typeMap->isKnownSubscriberType($subscriber)) { + throw new \PHPUnit\Event\UnknownSubscriberTypeException(sprintf('Subscriber "%s" does not implement any known interface - did you forget to register it?', $subscriber::class)); + } + $eventClassName = $this->typeMap->map($subscriber); + if (!array_key_exists($eventClassName, $this->subscribers)) { + $this->subscribers[$eventClassName] = []; + } + $this->subscribers[$eventClassName][] = $subscriber; } /** - * Attempts to find the requested example file and returns its contents or null if no file was found. - * - * This method will try several methods in search of the given example file, the first one it encounters is - * returned: - * - * 1. Iterates through all examples folders for the given filename - * 2. Checks the source folder for the given filename - * 3. Checks the 'examples' folder in the current working directory for examples - * 4. Checks the path relative to the current working directory for the given filename - * - * @return string[] all lines of the example file + * @throws Throwable + * @throws UnknownEventTypeException */ - private function getExampleFileContents(string $filename) : ?array + public function dispatch(\PHPUnit\Event\Event $event): void { - $normalizedPath = null; - foreach ($this->exampleDirectories as $directory) { - $exampleFileFromConfig = $this->constructExamplePath($directory, $filename); - if (is_readable($exampleFileFromConfig)) { - $normalizedPath = $exampleFileFromConfig; - break; + $eventClassName = $event::class; + if (!$this->typeMap->isKnownEventType($event)) { + throw new \PHPUnit\Event\UnknownEventTypeException(sprintf('Unknown event type "%s"', $eventClassName)); + } + foreach ($this->tracers as $tracer) { + try { + $tracer->trace($event); + // @codeCoverageIgnoreStart + } catch (Throwable $t) { + $this->handleThrowable($t); } + // @codeCoverageIgnoreEnd + } + if (!array_key_exists($eventClassName, $this->subscribers)) { + return; } - if (!$normalizedPath) { - if (is_readable($this->getExamplePathFromSource($filename))) { - $normalizedPath = $this->getExamplePathFromSource($filename); - } elseif (is_readable($this->getExamplePathFromExampleDirectory($filename))) { - $normalizedPath = $this->getExamplePathFromExampleDirectory($filename); - } elseif (is_readable($filename)) { - $normalizedPath = $filename; + foreach ($this->subscribers[$eventClassName] as $subscriber) { + try { + $subscriber->notify($event); + } catch (Throwable $t) { + $this->handleThrowable($t); } } - $lines = $normalizedPath && is_readable($normalizedPath) ? file($normalizedPath) : \false; - return $lines !== \false ? $lines : null; - } - /** - * Get example filepath based on the example directory inside your project. - */ - private function getExamplePathFromExampleDirectory(string $file) : string - { - return getcwd() . DIRECTORY_SEPARATOR . 'examples' . DIRECTORY_SEPARATOR . $file; } /** - * Returns a path to the example file in the given directory.. + * @throws Throwable */ - private function constructExamplePath(string $directory, string $file) : string + public function handleThrowable(Throwable $t): void { - return rtrim($directory, '\\/') . DIRECTORY_SEPARATOR . $file; + if ($this->isThrowableFromThirdPartySubscriber($t)) { + \PHPUnit\Event\Facade::emitter()->testRunnerTriggeredWarning(sprintf('Exception in third-party event subscriber: %s%s%s', $t->getMessage(), \PHP_EOL, $t->getTraceAsString())); + return; + } + // @codeCoverageIgnoreStart + throw $t; + // @codeCoverageIgnoreEnd } - /** - * Get example filepath based on sourcecode. - */ - private function getExamplePathFromSource(string $file) : string + private function isThrowableFromThirdPartySubscriber(Throwable $t): bool { - return sprintf('%s%s%s', trim($this->getSourceDirectory(), '\\/'), DIRECTORY_SEPARATOR, trim($file, '"')); + return !str_starts_with($t->getFile(), dirname(__DIR__, 2)); } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. - * - * @link http://phpdoc.org */ -namespace PHPUnitPHAR\phpDocumentor\Reflection\DocBlock; +namespace PHPUnit\Event; -use PHPUnitPHAR\phpDocumentor\Reflection\DocBlock; -use PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\Tags\Formatter; -use PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\Tags\Formatter\PassthroughFormatter; -use function sprintf; -use function str_repeat; -use function str_replace; -use function strlen; -use function wordwrap; /** - * Converts a DocBlock back from an object to a complete DocComment including Asterisks. + * @internal This interface is not covered by the backward compatibility promise for PHPUnit */ -class Serializer +interface Dispatcher { - /** @var string The string to indent the comment with. */ - protected $indentString = ' '; - /** @var int The number of times the indent string is repeated. */ - protected $indent = 0; - /** @var bool Whether to indent the first line with the given indent amount and string. */ - protected $isFirstLineIndented = \true; - /** @var int|null The max length of a line. */ - protected $lineLength; - /** @var Formatter A custom tag formatter. */ - protected $tagFormatter; - /** @var string */ - private $lineEnding; - /** - * Create a Serializer instance. - * - * @param int $indent The number of times the indent string is repeated. - * @param string $indentString The string to indent the comment with. - * @param bool $indentFirstLine Whether to indent the first line. - * @param int|null $lineLength The max length of a line or NULL to disable line wrapping. - * @param Formatter $tagFormatter A custom tag formatter, defaults to PassthroughFormatter. - * @param string $lineEnding Line ending used in the output, by default \n is used. - */ - public function __construct(int $indent = 0, string $indentString = ' ', bool $indentFirstLine = \true, ?int $lineLength = null, ?Formatter $tagFormatter = null, string $lineEnding = "\n") - { - $this->indent = $indent; - $this->indentString = $indentString; - $this->isFirstLineIndented = $indentFirstLine; - $this->lineLength = $lineLength; - $this->tagFormatter = $tagFormatter ?: new PassthroughFormatter(); - $this->lineEnding = $lineEnding; - } /** - * Generate a DocBlock comment. - * - * @param DocBlock $docblock The DocBlock to serialize. - * - * @return string The serialized doc block. + * @throws UnknownEventTypeException */ - public function getDocComment(DocBlock $docblock) : string - { - $indent = str_repeat($this->indentString, $this->indent); - $firstIndent = $this->isFirstLineIndented ? $indent : ''; - // 3 === strlen(' * ') - $wrapLength = $this->lineLength ? $this->lineLength - strlen($indent) - 3 : null; - $text = $this->removeTrailingSpaces($indent, $this->addAsterisksForEachLine($indent, $this->getSummaryAndDescriptionTextBlock($docblock, $wrapLength))); - $comment = $firstIndent . "/**\n"; - if ($text) { - $comment .= $indent . ' * ' . $text . "\n"; - $comment .= $indent . " *\n"; - } - $comment = $this->addTagBlock($docblock, $wrapLength, $indent, $comment); - return str_replace("\n", $this->lineEnding, $comment . $indent . ' */'); - } - private function removeTrailingSpaces(string $indent, string $text) : string - { - return str_replace(sprintf("\n%s * \n", $indent), sprintf("\n%s *\n", $indent), $text); - } - private function addAsterisksForEachLine(string $indent, string $text) : string - { - return str_replace("\n", sprintf("\n%s * ", $indent), $text); - } - private function getSummaryAndDescriptionTextBlock(DocBlock $docblock, ?int $wrapLength) : string - { - $text = $docblock->getSummary() . ((string) $docblock->getDescription() ? "\n\n" . $docblock->getDescription() : ''); - if ($wrapLength !== null) { - $text = wordwrap($text, $wrapLength); - return $text; - } - return $text; - } - private function addTagBlock(DocBlock $docblock, ?int $wrapLength, string $indent, string $comment) : string - { - foreach ($docblock->getTags() as $tag) { - $tagText = $this->tagFormatter->format($tag); - if ($wrapLength !== null) { - $tagText = wordwrap($tagText, $wrapLength); - } - $tagText = str_replace("\n", sprintf("\n%s * ", $indent), $tagText); - $comment .= sprintf("%s * %s\n", $indent, $tagText); - } - return $comment; - } + public function dispatch(\PHPUnit\Event\Event $event): void; } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. - * - * @link http://phpdoc.org */ -namespace PHPUnitPHAR\phpDocumentor\Reflection\DocBlock; +namespace PHPUnit\Event; -use InvalidArgumentException; -use PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\Tags\Author; -use PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\Tags\Covers; -use PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\Tags\Deprecated; -use PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\Tags\Generic; -use PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\Tags\InvalidTag; -use PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\Tags\Link as LinkTag; -use PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\Tags\Method; -use PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\Tags\Param; -use PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\Tags\Property; -use PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\Tags\PropertyRead; -use PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\Tags\PropertyWrite; -use PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\Tags\Return_; -use PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\Tags\See as SeeTag; -use PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\Tags\Since; -use PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\Tags\Source; -use PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\Tags\Throws; -use PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\Tags\Uses; -use PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\Tags\Var_; -use PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\Tags\Version; -use PHPUnitPHAR\phpDocumentor\Reflection\FqsenResolver; -use PHPUnitPHAR\phpDocumentor\Reflection\Types\Context as TypeContext; -use ReflectionMethod; -use ReflectionNamedType; -use ReflectionParameter; -use PHPUnitPHAR\Webmozart\Assert\Assert; -use function array_merge; -use function array_slice; -use function call_user_func_array; -use function count; -use function get_class; -use function preg_match; -use function strpos; -use function trim; /** - * Creates a Tag object given the contents of a tag. - * - * This Factory is capable of determining the appropriate class for a tag and instantiate it using its `create` - * factory method. The `create` factory method of a Tag can have a variable number of arguments; this way you can - * pass the dependencies that you need to construct a tag object. - * - * > Important: each parameter in addition to the body variable for the `create` method must default to null, otherwise - * > it violates the constraint with the interface; it is recommended to use the {@see Assert::notNull()} method to - * > verify that a dependency is actually passed. - * - * This Factory also features a Service Locator component that is used to pass the right dependencies to the - * `create` method of a tag; each dependency should be registered as a service or as a parameter. - * - * When you want to use a Tag of your own with custom handling you need to call the `registerTagHandler` method, pass - * the name of the tag and a Fully Qualified Class Name pointing to a class that implements the Tag interface. - */ -final class StandardTagFactory implements TagFactory -{ - /** PCRE regular expression matching a tag name. */ - public const REGEX_TAGNAME = '[\\w\\-\\_\\\\:]+'; - /** - * @var array> An array with a tag as a key, and an - * FQCN to a class that handles it as an array value. - */ - private $tagHandlerMappings = [ - 'author' => Author::class, - 'covers' => Covers::class, - 'deprecated' => Deprecated::class, - // 'example' => '\phpDocumentor\Reflection\DocBlock\Tags\Example', - 'link' => LinkTag::class, - 'method' => Method::class, - 'param' => Param::class, - 'property-read' => PropertyRead::class, - 'property' => Property::class, - 'property-write' => PropertyWrite::class, - 'return' => Return_::class, - 'see' => SeeTag::class, - 'since' => Since::class, - 'source' => Source::class, - 'throw' => Throws::class, - 'throws' => Throws::class, - 'uses' => Uses::class, - 'var' => Var_::class, - 'version' => Version::class, - ]; + * @internal This interface is not covered by the backward compatibility promise for PHPUnit + */ +interface SubscribableDispatcher extends \PHPUnit\Event\Dispatcher +{ /** - * @var array> An array with a anotation s a key, and an - * FQCN to a class that handles it as an array value. + * @throws UnknownSubscriberTypeException */ - private $annotationMappings = []; + public function registerSubscriber(\PHPUnit\Event\Subscriber $subscriber): void; + public function registerTracer(\PHPUnit\Event\Tracer\Tracer $tracer): void; +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event; + +use PHPUnit\Event\Code\ClassMethod; +use PHPUnit\Event\Code\ComparisonFailure; +use PHPUnit\Event\Code\Throwable; +use PHPUnit\Event\Test\DataProviderMethodCalled; +use PHPUnit\Event\Test\DataProviderMethodFinished; +use PHPUnit\Event\TestSuite\Filtered as TestSuiteFiltered; +use PHPUnit\Event\TestSuite\Finished as TestSuiteFinished; +use PHPUnit\Event\TestSuite\Loaded as TestSuiteLoaded; +use PHPUnit\Event\TestSuite\Skipped as TestSuiteSkipped; +use PHPUnit\Event\TestSuite\Sorted as TestSuiteSorted; +use PHPUnit\Event\TestSuite\Started as TestSuiteStarted; +use PHPUnit\Event\TestSuite\TestSuite; +use PHPUnit\Framework\Constraint; +use PHPUnit\TextUI\Configuration\Configuration; +use PHPUnit\Util\Exporter; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class DispatchingEmitter implements \PHPUnit\Event\Emitter +{ + private readonly \PHPUnit\Event\Dispatcher $dispatcher; + private readonly \PHPUnit\Event\Telemetry\System $system; + private readonly \PHPUnit\Event\Telemetry\Snapshot $startSnapshot; + private \PHPUnit\Event\Telemetry\Snapshot $previousSnapshot; + private bool $exportObjects = \false; + public function __construct(\PHPUnit\Event\Dispatcher $dispatcher, \PHPUnit\Event\Telemetry\System $system) + { + $this->dispatcher = $dispatcher; + $this->system = $system; + $this->startSnapshot = $system->snapshot(); + $this->previousSnapshot = $this->startSnapshot; + } /** - * @var ReflectionParameter[][] a lazy-loading cache containing parameters - * for each tagHandler that has been used. + * @deprecated */ - private $tagHandlerParameterCache = []; - /** @var FqsenResolver */ - private $fqsenResolver; + public function exportObjects(): void + { + $this->exportObjects = \true; + } /** - * @var mixed[] an array representing a simple Service Locator where we can store parameters and - * services that can be inserted into the Factory Methods of Tag Handlers. + * @deprecated */ - private $serviceLocator = []; + public function exportsObjects(): bool + { + return $this->exportObjects; + } /** - * Initialize this tag factory with the means to resolve an FQSEN and optionally a list of tag handlers. - * - * If no tag handlers are provided than the default list in the {@see self::$tagHandlerMappings} property - * is used. - * - * @see self::registerTagHandler() to add a new tag handler to the existing default list. - * - * @param array> $tagHandlers + * @throws InvalidArgumentException + * @throws UnknownEventTypeException */ - public function __construct(FqsenResolver $fqsenResolver, ?array $tagHandlers = null) + public function applicationStarted(): void { - $this->fqsenResolver = $fqsenResolver; - if ($tagHandlers !== null) { - $this->tagHandlerMappings = $tagHandlers; - } - $this->addService($fqsenResolver, FqsenResolver::class); + $this->dispatcher->dispatch(new \PHPUnit\Event\Application\Started($this->telemetryInfo(), new \PHPUnit\Event\Runtime\Runtime())); } - public function create(string $tagLine, ?TypeContext $context = null) : Tag + /** + * @throws InvalidArgumentException + * @throws UnknownEventTypeException + */ + public function testRunnerStarted(): void { - if (!$context) { - $context = new TypeContext(''); - } - [$tagName, $tagBody] = $this->extractTagParts($tagLine); - return $this->createTag(trim($tagBody), $tagName, $context); + $this->dispatcher->dispatch(new \PHPUnit\Event\TestRunner\Started($this->telemetryInfo())); } /** - * @param mixed $value + * @throws InvalidArgumentException + * @throws UnknownEventTypeException */ - public function addParameter(string $name, $value) : void + public function testRunnerConfigured(Configuration $configuration): void { - $this->serviceLocator[$name] = $value; + $this->dispatcher->dispatch(new \PHPUnit\Event\TestRunner\Configured($this->telemetryInfo(), $configuration)); } - public function addService(object $service, ?string $alias = null) : void + /** + * @throws InvalidArgumentException + * @throws UnknownEventTypeException + */ + public function testRunnerBootstrapFinished(string $filename): void { - $this->serviceLocator[$alias ?: get_class($service)] = $service; + $this->dispatcher->dispatch(new \PHPUnit\Event\TestRunner\BootstrapFinished($this->telemetryInfo(), $filename)); } - public function registerTagHandler(string $tagName, string $handler) : void + /** + * @throws InvalidArgumentException + * @throws UnknownEventTypeException + */ + public function testRunnerLoadedExtensionFromPhar(string $filename, string $name, string $version): void { - Assert::stringNotEmpty($tagName); - Assert::classExists($handler); - Assert::implementsInterface($handler, Tag::class); - if (strpos($tagName, '\\') && $tagName[0] !== '\\') { - throw new InvalidArgumentException('A namespaced tag must have a leading backslash as it must be fully qualified'); - } - $this->tagHandlerMappings[$tagName] = $handler; + $this->dispatcher->dispatch(new \PHPUnit\Event\TestRunner\ExtensionLoadedFromPhar($this->telemetryInfo(), $filename, $name, $version)); } /** - * Extracts all components for a tag. + * @psalm-param class-string $className + * @psalm-param array $parameters * - * @return string[] + * @throws InvalidArgumentException + * @throws UnknownEventTypeException */ - private function extractTagParts(string $tagLine) : array + public function testRunnerBootstrappedExtension(string $className, array $parameters): void { - $matches = []; - if (!preg_match('/^@(' . self::REGEX_TAGNAME . ')((?:[\\s\\(\\{])\\s*([^\\s].*)|$)/us', $tagLine, $matches)) { - throw new InvalidArgumentException('The tag "' . $tagLine . '" does not seem to be wellformed, please check it for errors'); - } - if (count($matches) < 3) { - $matches[] = ''; - } - return array_slice($matches, 1); + $this->dispatcher->dispatch(new \PHPUnit\Event\TestRunner\ExtensionBootstrapped($this->telemetryInfo(), $className, $parameters)); } /** - * Creates a new tag object with the given name and body or returns null if the tag name was recognized but the - * body was invalid. + * @throws InvalidArgumentException + * @throws UnknownEventTypeException */ - private function createTag(string $body, string $name, TypeContext $context) : Tag + public function dataProviderMethodCalled(ClassMethod $testMethod, ClassMethod $dataProviderMethod): void { - $handlerClassName = $this->findHandlerClassName($name, $context); - $arguments = $this->getArgumentsForParametersFromWiring($this->fetchParametersForHandlerFactoryMethod($handlerClassName), $this->getServiceLocatorWithDynamicParameters($context, $name, $body)); - try { - $callable = [$handlerClassName, 'create']; - Assert::isCallable($callable); - /** @phpstan-var callable(string): ?Tag $callable */ - $tag = call_user_func_array($callable, $arguments); - return $tag ?? InvalidTag::create($body, $name); - } catch (InvalidArgumentException $e) { - return InvalidTag::create($body, $name)->withError($e); - } + $this->dispatcher->dispatch(new DataProviderMethodCalled($this->telemetryInfo(), $testMethod, $dataProviderMethod)); } /** - * Determines the Fully Qualified Class Name of the Factory or Tag (containing a Factory Method `create`). - * - * @return class-string + * @throws InvalidArgumentException + * @throws UnknownEventTypeException */ - private function findHandlerClassName(string $tagName, TypeContext $context) : string + public function dataProviderMethodFinished(ClassMethod $testMethod, ClassMethod ...$calledMethods): void { - $handlerClassName = Generic::class; - if (isset($this->tagHandlerMappings[$tagName])) { - $handlerClassName = $this->tagHandlerMappings[$tagName]; - } elseif ($this->isAnnotation($tagName)) { - // TODO: Annotation support is planned for a later stage and as such is disabled for now - $tagName = (string) $this->fqsenResolver->resolve($tagName, $context); - if (isset($this->annotationMappings[$tagName])) { - $handlerClassName = $this->annotationMappings[$tagName]; - } - } - return $handlerClassName; + $this->dispatcher->dispatch(new DataProviderMethodFinished($this->telemetryInfo(), $testMethod, ...$calledMethods)); } /** - * Retrieves the arguments that need to be passed to the Factory Method with the given Parameters. - * - * @param ReflectionParameter[] $parameters - * @param mixed[] $locator - * - * @return mixed[] A series of values that can be passed to the Factory Method of the tag whose parameters - * is provided with this method. + * @throws InvalidArgumentException + * @throws UnknownEventTypeException */ - private function getArgumentsForParametersFromWiring(array $parameters, array $locator) : array + public function testSuiteLoaded(TestSuite $testSuite): void { - $arguments = []; - foreach ($parameters as $parameter) { - $type = $parameter->getType(); - $typeHint = null; - if ($type instanceof ReflectionNamedType) { - $typeHint = $type->getName(); - if ($typeHint === 'self') { - $declaringClass = $parameter->getDeclaringClass(); - if ($declaringClass !== null) { - $typeHint = $declaringClass->getName(); - } - } - } - if (isset($locator[$typeHint])) { - $arguments[] = $locator[$typeHint]; - continue; - } - $parameterName = $parameter->getName(); - if (isset($locator[$parameterName])) { - $arguments[] = $locator[$parameterName]; - continue; - } - $arguments[] = null; - } - return $arguments; + $this->dispatcher->dispatch(new TestSuiteLoaded($this->telemetryInfo(), $testSuite)); } /** - * Retrieves a series of ReflectionParameter objects for the static 'create' method of the given - * tag handler class name. - * - * @param class-string $handlerClassName - * - * @return ReflectionParameter[] + * @throws InvalidArgumentException + * @throws UnknownEventTypeException */ - private function fetchParametersForHandlerFactoryMethod(string $handlerClassName) : array + public function testSuiteFiltered(TestSuite $testSuite): void { - if (!isset($this->tagHandlerParameterCache[$handlerClassName])) { - $methodReflection = new ReflectionMethod($handlerClassName, 'create'); - $this->tagHandlerParameterCache[$handlerClassName] = $methodReflection->getParameters(); - } - return $this->tagHandlerParameterCache[$handlerClassName]; + $this->dispatcher->dispatch(new TestSuiteFiltered($this->telemetryInfo(), $testSuite)); } /** - * Returns a copy of this class' Service Locator with added dynamic parameters, - * such as the tag's name, body and Context. - * - * @param TypeContext $context The Context (namespace and aliasses) that may be - * passed and is used to resolve FQSENs. - * @param string $tagName The name of the tag that may be - * passed onto the factory method of the Tag class. - * @param string $tagBody The body of the tag that may be - * passed onto the factory method of the Tag class. - * - * @return mixed[] + * @throws InvalidArgumentException + * @throws UnknownEventTypeException */ - private function getServiceLocatorWithDynamicParameters(TypeContext $context, string $tagName, string $tagBody) : array + public function testSuiteSorted(int $executionOrder, int $executionOrderDefects, bool $resolveDependencies): void { - return array_merge($this->serviceLocator, ['name' => $tagName, 'body' => $tagBody, TypeContext::class => $context]); + $this->dispatcher->dispatch(new TestSuiteSorted($this->telemetryInfo(), $executionOrder, $executionOrderDefects, $resolveDependencies)); } /** - * Returns whether the given tag belongs to an annotation. - * - * @todo this method should be populated once we implement Annotation notation support. + * @throws InvalidArgumentException + * @throws UnknownEventTypeException */ - private function isAnnotation(string $tagContent) : bool + public function testRunnerEventFacadeSealed(): void { - // 1. Contains a namespace separator - // 2. Contains parenthesis - // 3. Is present in a list of known annotations (make the algorithm smart by first checking is the last part - // of the annotation class name matches the found tag name - return \false; + $this->dispatcher->dispatch(new \PHPUnit\Event\TestRunner\EventFacadeSealed($this->telemetryInfo())); } -} -dispatcher->dispatch(new \PHPUnit\Event\TestRunner\ExecutionStarted($this->telemetryInfo(), $testSuite)); + } /** - * Adds a parameter to the service locator that can be injected in a tag's factory method. - * - * When calling a tag's "create" method we always check the signature for dependencies to inject. One way is to - * typehint a parameter in the signature so that we can use that interface or class name to inject a dependency - * (see {@see addService()} for more information on that). - * - * Another way is to check the name of the argument against the names in the Service Locator. With this method - * you can add a variable that will be inserted when a tag's create method is not typehinted and has a matching - * name. - * - * Be aware that there are two reserved names: - * - * - name, representing the name of the tag. - * - body, representing the complete body of the tag. - * - * These parameters are injected at the last moment and will override any existing parameter with those names. - * - * @param mixed $value + * @throws InvalidArgumentException + * @throws UnknownEventTypeException */ - public function addParameter(string $name, $value) : void; + public function testRunnerDisabledGarbageCollection(): void + { + $this->dispatcher->dispatch(new \PHPUnit\Event\TestRunner\GarbageCollectionDisabled($this->telemetryInfo())); + } /** - * Factory method responsible for instantiating the correct sub type. - * - * @param string $tagLine The text for this tag, including description. - * - * @return Tag A new tag object. - * - * @throws InvalidArgumentException If an invalid tag line was presented. + * @throws InvalidArgumentException + * @throws UnknownEventTypeException */ - public function create(string $tagLine, ?TypeContext $context = null) : Tag; + public function testRunnerTriggeredGarbageCollection(): void + { + $this->dispatcher->dispatch(new \PHPUnit\Event\TestRunner\GarbageCollectionTriggered($this->telemetryInfo())); + } /** - * Registers a service with the Service Locator using the FQCN of the class or the alias, if provided. - * - * When calling a tag's "create" method we always check the signature for dependencies to inject. If a parameter - * has a typehint then the ServiceLocator is queried to see if a Service is registered for that typehint. - * - * Because interfaces are regularly used as type-hints this method provides an alias parameter; if the FQCN of the - * interface is passed as alias then every time that interface is requested the provided service will be returned. + * @throws InvalidArgumentException + * @throws UnknownEventTypeException */ - public function addService(object $service) : void; + public function testSuiteSkipped(TestSuite $testSuite, string $message): void + { + $this->dispatcher->dispatch(new TestSuiteSkipped($this->telemetryInfo(), $testSuite, $message)); + } /** - * Registers a handler for tags. - * - * If you want to use your own tags then you can use this method to instruct the TagFactory - * to register the name of a tag with the FQCN of a 'Tag Handler'. The Tag handler should implement - * the {@see Tag} interface (and thus the create method). - * - * @param string $tagName Name of tag to register a handler for. When registering a namespaced - * tag, the full name, along with a prefixing slash MUST be provided. - * @param class-string $handler FQCN of handler. - * - * @throws InvalidArgumentException If the tag name is not a string. - * @throws InvalidArgumentException If the tag name is namespaced (contains backslashes) but - * does not start with a backslash. - * @throws InvalidArgumentException If the handler is not a string. - * @throws InvalidArgumentException If the handler is not an existing class. - * @throws InvalidArgumentException If the handler does not implement the {@see Tag} interface. + * @throws InvalidArgumentException + * @throws UnknownEventTypeException */ - public function registerTagHandler(string $tagName, string $handler) : void; -} -dispatcher->dispatch(new TestSuiteStarted($this->telemetryInfo(), $testSuite)); + } /** - * Initializes this tag with the author name and e-mail. + * @throws InvalidArgumentException + * @throws UnknownEventTypeException */ - public function __construct(string $authorName, string $authorEmail) + public function testPreparationStarted(\PHPUnit\Event\Code\Test $test): void { - if ($authorEmail && !filter_var($authorEmail, FILTER_VALIDATE_EMAIL)) { - throw new InvalidArgumentException('The author tag does not have a valid e-mail address'); - } - $this->authorName = $authorName; - $this->authorEmail = $authorEmail; + $this->dispatcher->dispatch(new \PHPUnit\Event\Test\PreparationStarted($this->telemetryInfo(), $test)); } /** - * Gets the author's name. - * - * @return string The author's name. + * @throws InvalidArgumentException + * @throws UnknownEventTypeException */ - public function getAuthorName() : string + public function testPreparationFailed(\PHPUnit\Event\Code\Test $test): void { - return $this->authorName; + $this->dispatcher->dispatch(new \PHPUnit\Event\Test\PreparationFailed($this->telemetryInfo(), $test)); } /** - * Returns the author's email. + * @psalm-param class-string $testClassName * - * @return string The author's email. + * @throws InvalidArgumentException + * @throws UnknownEventTypeException */ - public function getEmail() : string + public function testBeforeFirstTestMethodCalled(string $testClassName, ClassMethod $calledMethod): void { - return $this->authorEmail; + $this->dispatcher->dispatch(new \PHPUnit\Event\Test\BeforeFirstTestMethodCalled($this->telemetryInfo(), $testClassName, $calledMethod)); } /** - * Returns this tag in string form. + * @psalm-param class-string $testClassName + * + * @throws InvalidArgumentException + * @throws UnknownEventTypeException */ - public function __toString() : string + public function testBeforeFirstTestMethodErrored(string $testClassName, ClassMethod $calledMethod, Throwable $throwable): void { - if ($this->authorEmail) { - $authorEmail = '<' . $this->authorEmail . '>'; - } else { - $authorEmail = ''; - } - $authorName = $this->authorName; - return $authorName . ($authorEmail !== '' ? ($authorName !== '' ? ' ' : '') . $authorEmail : ''); + $this->dispatcher->dispatch(new \PHPUnit\Event\Test\BeforeFirstTestMethodErrored($this->telemetryInfo(), $testClassName, $calledMethod, $throwable)); } /** - * Attempts to create a new Author object based on the tag body. + * @psalm-param class-string $testClassName + * + * @throws InvalidArgumentException + * @throws UnknownEventTypeException */ - public static function create(string $body) : ?self + public function testBeforeFirstTestMethodFinished(string $testClassName, ClassMethod ...$calledMethods): void { - $splitTagContent = preg_match('/^([^\\<]*)(?:\\<([^\\>]*)\\>)?$/u', $body, $matches); - if (!$splitTagContent) { - return null; - } - $authorName = trim($matches[1]); - $email = isset($matches[2]) ? trim($matches[2]) : ''; - return new static($authorName, $email); + $this->dispatcher->dispatch(new \PHPUnit\Event\Test\BeforeFirstTestMethodFinished($this->telemetryInfo(), $testClassName, ...$calledMethods)); } -} -name; + $this->dispatcher->dispatch(new \PHPUnit\Event\Test\BeforeTestMethodCalled($this->telemetryInfo(), $testClassName, $calledMethod)); } - public function getDescription() : ?Description + /** + * @psalm-param class-string $testClassName + * + * @throws InvalidArgumentException + * @throws UnknownEventTypeException + */ + public function testBeforeTestMethodFinished(string $testClassName, ClassMethod ...$calledMethods): void { - return $this->description; + $this->dispatcher->dispatch(new \PHPUnit\Event\Test\BeforeTestMethodFinished($this->telemetryInfo(), $testClassName, ...$calledMethods)); } - public function render(?Formatter $formatter = null) : string + /** + * @psalm-param class-string $testClassName + * + * @throws InvalidArgumentException + * @throws UnknownEventTypeException + */ + public function testPreConditionCalled(string $testClassName, ClassMethod $calledMethod): void { - if ($formatter === null) { - $formatter = new Formatter\PassthroughFormatter(); - } - return $formatter->format($this); + $this->dispatcher->dispatch(new \PHPUnit\Event\Test\PreConditionCalled($this->telemetryInfo(), $testClassName, $calledMethod)); } -} -refers = $refers; - $this->description = $description; + $this->dispatcher->dispatch(new \PHPUnit\Event\Test\PreConditionFinished($this->telemetryInfo(), $testClassName, ...$calledMethods)); } - public static function create(string $body, ?DescriptionFactory $descriptionFactory = null, ?FqsenResolver $resolver = null, ?TypeContext $context = null) : self + /** + * @throws InvalidArgumentException + * @throws UnknownEventTypeException + */ + public function testPrepared(\PHPUnit\Event\Code\Test $test): void { - Assert::stringNotEmpty($body); - Assert::notNull($descriptionFactory); - Assert::notNull($resolver); - $parts = Utils::pregSplit('/\\s+/Su', $body, 2); - return new static(self::resolveFqsen($parts[0], $resolver, $context), $descriptionFactory->create($parts[1] ?? '', $context)); + $this->dispatcher->dispatch(new \PHPUnit\Event\Test\Prepared($this->telemetryInfo(), $test)); } - private static function resolveFqsen(string $parts, ?FqsenResolver $fqsenResolver, ?TypeContext $context) : Fqsen + /** + * @psalm-param class-string $className + * + * @throws InvalidArgumentException + * @throws UnknownEventTypeException + */ + public function testRegisteredComparator(string $className): void { - Assert::notNull($fqsenResolver); - $fqsenParts = explode('::', $parts); - $resolved = $fqsenResolver->resolve($fqsenParts[0], $context); - if (!array_key_exists(1, $fqsenParts)) { - return $resolved; - } - return new Fqsen($resolved . '::' . $fqsenParts[1]); + $this->dispatcher->dispatch(new \PHPUnit\Event\Test\ComparatorRegistered($this->telemetryInfo(), $className)); } /** - * Returns the structural element this tag refers to. + * @throws InvalidArgumentException + * @throws UnknownEventTypeException + * + * @deprecated */ - public function getReference() : Fqsen + public function testAssertionSucceeded(mixed $value, Constraint\Constraint $constraint, string $message): void { - return $this->refers; + $this->dispatcher->dispatch(new \PHPUnit\Event\Test\AssertionSucceeded($this->telemetryInfo(), Exporter::export($value, $this->exportObjects), $constraint->toString($this->exportObjects), $constraint->count(), $message)); } /** - * Returns a string representation of this tag. + * @throws InvalidArgumentException + * @throws UnknownEventTypeException + * + * @deprecated */ - public function __toString() : string + public function testAssertionFailed(mixed $value, Constraint\Constraint $constraint, string $message): void { - if ($this->description) { - $description = $this->description->render(); - } else { - $description = ''; - } - $refers = (string) $this->refers; - return $refers . ($description !== '' ? ($refers !== '' ? ' ' : '') . $description : ''); + $this->dispatcher->dispatch(new \PHPUnit\Event\Test\AssertionFailed($this->telemetryInfo(), Exporter::export($value, $this->exportObjects), $constraint->toString($this->exportObjects), $constraint->count(), $message)); } -} -version = $version; - $this->description = $description; + $this->dispatcher->dispatch(new \PHPUnit\Event\Test\MockObjectCreated($this->telemetryInfo(), $className)); } /** - * @return static + * @psalm-param list $interfaces + * + * @throws InvalidArgumentException + * @throws UnknownEventTypeException */ - public static function create(?string $body, ?DescriptionFactory $descriptionFactory = null, ?TypeContext $context = null) : self + public function testCreatedMockObjectForIntersectionOfInterfaces(array $interfaces): void { - if (empty($body)) { - return new static(); - } - $matches = []; - if (!preg_match('/^(' . self::REGEX_VECTOR . ')\\s*(.+)?$/sux', $body, $matches)) { - return new static(null, $descriptionFactory !== null ? $descriptionFactory->create($body, $context) : null); - } - Assert::notNull($descriptionFactory); - return new static($matches[1], $descriptionFactory->create($matches[2] ?? '', $context)); + $this->dispatcher->dispatch(new \PHPUnit\Event\Test\MockObjectForIntersectionOfInterfacesCreated($this->telemetryInfo(), $interfaces)); } /** - * Gets the version section of the tag. + * @psalm-param trait-string $traitName + * + * @throws InvalidArgumentException + * @throws UnknownEventTypeException */ - public function getVersion() : ?string + public function testCreatedMockObjectForTrait(string $traitName): void { - return $this->version; + $this->dispatcher->dispatch(new \PHPUnit\Event\Test\MockObjectForTraitCreated($this->telemetryInfo(), $traitName)); } /** - * Returns a string representation for this tag. + * @psalm-param class-string $className + * + * @throws InvalidArgumentException + * @throws UnknownEventTypeException */ - public function __toString() : string + public function testCreatedMockObjectForAbstractClass(string $className): void { - if ($this->description) { - $description = $this->description->render(); - } else { - $description = ''; - } - $version = (string) $this->version; - return $version . ($description !== '' ? ($version !== '' ? ' ' : '') . $description : ''); + $this->dispatcher->dispatch(new \PHPUnit\Event\Test\MockObjectForAbstractClassCreated($this->telemetryInfo(), $className)); } -} -filePath = $filePath; - $this->startingLine = $startingLine; - $this->lineCount = $lineCount; - if ($content !== null) { - $this->content = trim($content); - } - $this->isURI = $isURI; + $this->dispatcher->dispatch(new \PHPUnit\Event\Test\MockObjectFromWsdlCreated($this->telemetryInfo(), $wsdlFile, $originalClassName, $mockClassName, $methods, $callOriginalConstructor, $options)); } - public function getContent() : string + /** + * @psalm-param class-string $className + * + * @throws InvalidArgumentException + * @throws UnknownEventTypeException + */ + public function testCreatedPartialMockObject(string $className, string ...$methodNames): void { - if ($this->content === null || $this->content === '') { - $filePath = $this->filePath; - if ($this->isURI) { - $filePath = $this->isUriRelative($this->filePath) ? str_replace('%2F', '/', rawurlencode($this->filePath)) : $this->filePath; - } - return trim($filePath); - } - return $this->content; + $this->dispatcher->dispatch(new \PHPUnit\Event\Test\PartialMockObjectCreated($this->telemetryInfo(), $className, ...$methodNames)); } - public function getDescription() : ?string + /** + * @psalm-param class-string $className + * + * @throws InvalidArgumentException + * @throws UnknownEventTypeException + */ + public function testCreatedTestProxy(string $className, array $constructorArguments): void { - return $this->content; + $this->dispatcher->dispatch(new \PHPUnit\Event\Test\TestProxyCreated($this->telemetryInfo(), $className, Exporter::export($constructorArguments, $this->exportObjects))); } - public static function create(string $body) : ?Tag + /** + * @psalm-param class-string $className + * + * @throws InvalidArgumentException + * @throws UnknownEventTypeException + */ + public function testCreatedStub(string $className): void { - // File component: File path in quotes or File URI / Source information - if (!preg_match('/^\\s*(?:(\\"[^\\"]+\\")|(\\S+))(?:\\s+(.*))?$/sux', $body, $matches)) { - return null; - } - $filePath = null; - $fileUri = null; - if ($matches[1] !== '') { - $filePath = $matches[1]; - } else { - $fileUri = $matches[2]; - } - $startingLine = 1; - $lineCount = 0; - $description = null; - if (array_key_exists(3, $matches)) { - $description = $matches[3]; - // Starting line / Number of lines / Description - if (preg_match('/^([1-9]\\d*)(?:\\s+((?1))\\s*)?(.*)$/sux', $matches[3], $contentMatches)) { - $startingLine = (int) $contentMatches[1]; - if (isset($contentMatches[2])) { - $lineCount = (int) $contentMatches[2]; - } - if (array_key_exists(3, $contentMatches)) { - $description = $contentMatches[3]; - } - } - } - return new static($filePath ?? $fileUri ?? '', $fileUri !== null, $startingLine, $lineCount, $description); + $this->dispatcher->dispatch(new \PHPUnit\Event\Test\TestStubCreated($this->telemetryInfo(), $className)); } /** - * Returns the file path. + * @psalm-param list $interfaces * - * @return string Path to a file to use as an example. - * May also be an absolute URI. + * @throws InvalidArgumentException + * @throws UnknownEventTypeException */ - public function getFilePath() : string + public function testCreatedStubForIntersectionOfInterfaces(array $interfaces): void { - return trim($this->filePath, '"'); + $this->dispatcher->dispatch(new \PHPUnit\Event\Test\TestStubForIntersectionOfInterfacesCreated($this->telemetryInfo(), $interfaces)); } /** - * Returns a string representation for this tag. + * @throws InvalidArgumentException + * @throws UnknownEventTypeException */ - public function __toString() : string + public function testErrored(\PHPUnit\Event\Code\Test $test, Throwable $throwable): void { - $filePath = $this->filePath; - $isDefaultLine = $this->startingLine === 1 && $this->lineCount === 0; - $startingLine = !$isDefaultLine ? (string) $this->startingLine : ''; - $lineCount = !$isDefaultLine ? (string) $this->lineCount : ''; - $content = (string) $this->content; - return $filePath . ($startingLine !== '' ? ($filePath !== '' ? ' ' : '') . $startingLine : '') . ($lineCount !== '' ? ($filePath !== '' || $startingLine !== '' ? ' ' : '') . $lineCount : '') . ($content !== '' ? ($filePath !== '' || $startingLine !== '' || $lineCount !== '' ? ' ' : '') . $content : ''); + $this->dispatcher->dispatch(new \PHPUnit\Event\Test\Errored($this->telemetryInfo(), $test, $throwable)); } /** - * Returns true if the provided URI is relative or contains a complete scheme (and thus is absolute). + * @throws InvalidArgumentException + * @throws UnknownEventTypeException */ - private function isUriRelative(string $uri) : bool + public function testFailed(\PHPUnit\Event\Code\Test $test, Throwable $throwable, ?ComparisonFailure $comparisonFailure): void { - return strpos($uri, ':') === \false; + $this->dispatcher->dispatch(new \PHPUnit\Event\Test\Failed($this->telemetryInfo(), $test, $throwable, $comparisonFailure)); } - public function getStartingLine() : int + /** + * @throws InvalidArgumentException + * @throws UnknownEventTypeException + */ + public function testPassed(\PHPUnit\Event\Code\Test $test): void { - return $this->startingLine; + $this->dispatcher->dispatch(new \PHPUnit\Event\Test\Passed($this->telemetryInfo(), $test)); } - public function getLineCount() : int + /** + * @throws InvalidArgumentException + * @throws UnknownEventTypeException + */ + public function testConsideredRisky(\PHPUnit\Event\Code\Test $test, string $message): void { - return $this->lineCount; + $this->dispatcher->dispatch(new \PHPUnit\Event\Test\ConsideredRisky($this->telemetryInfo(), $test, $message)); } - public function getName() : string + /** + * @throws InvalidArgumentException + * @throws UnknownEventTypeException + */ + public function testMarkedAsIncomplete(\PHPUnit\Event\Code\Test $test, Throwable $throwable): void { - return 'example'; + $this->dispatcher->dispatch(new \PHPUnit\Event\Test\MarkedIncomplete($this->telemetryInfo(), $test, $throwable)); } - public function render(?Formatter $formatter = null) : string + /** + * @throws InvalidArgumentException + * @throws UnknownEventTypeException + */ + public function testSkipped(\PHPUnit\Event\Code\Test $test, string $message): void { - if ($formatter === null) { - $formatter = new Formatter\PassthroughFormatter(); - } - return $formatter->format($this); + $this->dispatcher->dispatch(new \PHPUnit\Event\Test\Skipped($this->telemetryInfo(), $test, $message)); } -} -dispatcher->dispatch(new \PHPUnit\Event\Test\PhpunitDeprecationTriggered($this->telemetryInfo(), $test, $message)); + } /** - * Formats a tag into a string representation according to a specific format, such as Markdown. + * @psalm-param non-empty-string $message + * @psalm-param non-empty-string $file + * @psalm-param positive-int $line + * + * @throws InvalidArgumentException + * @throws UnknownEventTypeException */ - public function format(Tag $tag) : string; -} -dispatcher->dispatch(new \PHPUnit\Event\Test\PhpDeprecationTriggered($this->telemetryInfo(), $test, $message, $file, $line, $suppressed, $ignoredByBaseline, $ignoredByTest)); + } /** - * @param Tag[] $tags All tags that should later be aligned with the formatter. + * @psalm-param non-empty-string $message + * @psalm-param non-empty-string $file + * @psalm-param positive-int $line + * + * @throws InvalidArgumentException + * @throws UnknownEventTypeException */ - public function __construct(array $tags) + public function testTriggeredDeprecation(\PHPUnit\Event\Code\Test $test, string $message, string $file, int $line, bool $suppressed, bool $ignoredByBaseline, bool $ignoredByTest): void { - foreach ($tags as $tag) { - $this->maxLen = max($this->maxLen, strlen($tag->getName())); - } + $this->dispatcher->dispatch(new \PHPUnit\Event\Test\DeprecationTriggered($this->telemetryInfo(), $test, $message, $file, $line, $suppressed, $ignoredByBaseline, $ignoredByTest)); } /** - * Formats the given tag to return a simple plain text version. + * @psalm-param non-empty-string $message + * @psalm-param non-empty-string $file + * @psalm-param positive-int $line + * + * @throws InvalidArgumentException + * @throws UnknownEventTypeException */ - public function format(Tag $tag) : string + public function testTriggeredError(\PHPUnit\Event\Code\Test $test, string $message, string $file, int $line, bool $suppressed): void { - return '@' . $tag->getName() . str_repeat(' ', $this->maxLen - strlen($tag->getName()) + 1) . $tag; + $this->dispatcher->dispatch(new \PHPUnit\Event\Test\ErrorTriggered($this->telemetryInfo(), $test, $message, $file, $line, $suppressed)); } -} -getName() . ' ' . $tag); + $this->dispatcher->dispatch(new \PHPUnit\Event\Test\NoticeTriggered($this->telemetryInfo(), $test, $message, $file, $line, $suppressed, $ignoredByBaseline)); } -} -validateTagName($name); - $this->name = $name; - $this->description = $description; + $this->dispatcher->dispatch(new \PHPUnit\Event\Test\PhpNoticeTriggered($this->telemetryInfo(), $test, $message, $file, $line, $suppressed, $ignoredByBaseline)); } /** - * Creates a new tag that represents any unknown tag type. + * @psalm-param non-empty-string $message + * @psalm-param non-empty-string $file + * @psalm-param positive-int $line * - * @return static + * @throws InvalidArgumentException + * @throws UnknownEventTypeException */ - public static function create(string $body, string $name = '', ?DescriptionFactory $descriptionFactory = null, ?TypeContext $context = null) : self + public function testTriggeredWarning(\PHPUnit\Event\Code\Test $test, string $message, string $file, int $line, bool $suppressed, bool $ignoredByBaseline): void { - Assert::stringNotEmpty($name); - Assert::notNull($descriptionFactory); - $description = $body !== '' ? $descriptionFactory->create($body, $context) : null; - return new static($name, $description); + $this->dispatcher->dispatch(new \PHPUnit\Event\Test\WarningTriggered($this->telemetryInfo(), $test, $message, $file, $line, $suppressed, $ignoredByBaseline)); } /** - * Returns the tag as a serialized string + * @psalm-param non-empty-string $message + * @psalm-param non-empty-string $file + * @psalm-param positive-int $line + * + * @throws InvalidArgumentException + * @throws UnknownEventTypeException */ - public function __toString() : string + public function testTriggeredPhpWarning(\PHPUnit\Event\Code\Test $test, string $message, string $file, int $line, bool $suppressed, bool $ignoredByBaseline): void { - if ($this->description) { - $description = $this->description->render(); - } else { - $description = ''; - } - return $description; + $this->dispatcher->dispatch(new \PHPUnit\Event\Test\PhpWarningTriggered($this->telemetryInfo(), $test, $message, $file, $line, $suppressed, $ignoredByBaseline)); + } + /** + * @psalm-param non-empty-string $message + * + * @throws InvalidArgumentException + * @throws UnknownEventTypeException + */ + public function testTriggeredPhpunitError(\PHPUnit\Event\Code\Test $test, string $message): void + { + $this->dispatcher->dispatch(new \PHPUnit\Event\Test\PhpunitErrorTriggered($this->telemetryInfo(), $test, $message)); } /** - * Validates if the tag name matches the expected format, otherwise throws an exception. + * @psalm-param non-empty-string $message + * + * @throws InvalidArgumentException + * @throws UnknownEventTypeException */ - private function validateTagName(string $name) : void + public function testTriggeredPhpunitWarning(\PHPUnit\Event\Code\Test $test, string $message): void { - if (!preg_match('/^' . StandardTagFactory::REGEX_TAGNAME . '$/u', $name)) { - throw new InvalidArgumentException('The tag name "' . $name . '" is not wellformed. Tags may only consist of letters, underscores, ' . 'hyphens and backslashes.'); - } + $this->dispatcher->dispatch(new \PHPUnit\Event\Test\PhpunitWarningTriggered($this->telemetryInfo(), $test, $message)); } -} -name = $name; - $this->body = $body; + $this->dispatcher->dispatch(new \PHPUnit\Event\Test\PrintedUnexpectedOutput($this->telemetryInfo(), $output)); } - public function getException() : ?Throwable + /** + * @throws InvalidArgumentException + * @throws UnknownEventTypeException + */ + public function testFinished(\PHPUnit\Event\Code\Test $test, int $numberOfAssertionsPerformed): void { - return $this->throwable; + $this->dispatcher->dispatch(new \PHPUnit\Event\Test\Finished($this->telemetryInfo(), $test, $numberOfAssertionsPerformed)); } - public function getName() : string + /** + * @psalm-param class-string $testClassName + * + * @throws InvalidArgumentException + * @throws UnknownEventTypeException + */ + public function testPostConditionCalled(string $testClassName, ClassMethod $calledMethod): void { - return $this->name; + $this->dispatcher->dispatch(new \PHPUnit\Event\Test\PostConditionCalled($this->telemetryInfo(), $testClassName, $calledMethod)); } - public static function create(string $body, string $name = '') : self + /** + * @psalm-param class-string $testClassName + * + * @throws InvalidArgumentException + * @throws UnknownEventTypeException + */ + public function testPostConditionFinished(string $testClassName, ClassMethod ...$calledMethods): void { - return new self($name, $body); + $this->dispatcher->dispatch(new \PHPUnit\Event\Test\PostConditionFinished($this->telemetryInfo(), $testClassName, ...$calledMethods)); } - public function withError(Throwable $exception) : self + /** + * @psalm-param class-string $testClassName + * + * @throws InvalidArgumentException + * @throws UnknownEventTypeException + */ + public function testAfterTestMethodCalled(string $testClassName, ClassMethod $calledMethod): void { - $this->flattenExceptionBacktrace($exception); - $tag = new self($this->name, $this->body); - $tag->throwable = $exception; - return $tag; + $this->dispatcher->dispatch(new \PHPUnit\Event\Test\AfterTestMethodCalled($this->telemetryInfo(), $testClassName, $calledMethod)); } /** - * Removes all complex types from backtrace + * @psalm-param class-string $testClassName * - * Not all objects are serializable. So we need to remove them from the - * stored exception to be sure that we do not break existing library usage. + * @throws InvalidArgumentException + * @throws UnknownEventTypeException */ - private function flattenExceptionBacktrace(Throwable $exception) : void + public function testAfterTestMethodFinished(string $testClassName, ClassMethod ...$calledMethods): void { - $traceProperty = (new ReflectionClass(Exception::class))->getProperty('trace'); - $traceProperty->setAccessible(\true); - do { - $trace = $exception->getTrace(); - if (isset($trace[0]['args'])) { - $trace = array_map(function (array $call) : array { - $call['args'] = array_map([$this, 'flattenArguments'], $call['args'] ?? []); - return $call; - }, $trace); - } - $traceProperty->setValue($exception, $trace); - $exception = $exception->getPrevious(); - } while ($exception !== null); - $traceProperty->setAccessible(\false); + $this->dispatcher->dispatch(new \PHPUnit\Event\Test\AfterTestMethodFinished($this->telemetryInfo(), $testClassName, ...$calledMethods)); } /** - * @param mixed $value + * @psalm-param class-string $testClassName * - * @return mixed + * @throws InvalidArgumentException + * @throws UnknownEventTypeException + */ + public function testAfterLastTestMethodCalled(string $testClassName, ClassMethod $calledMethod): void + { + $this->dispatcher->dispatch(new \PHPUnit\Event\Test\AfterLastTestMethodCalled($this->telemetryInfo(), $testClassName, $calledMethod)); + } + /** + * @psalm-param class-string $testClassName * - * @throws ReflectionException + * @throws InvalidArgumentException + * @throws UnknownEventTypeException */ - private function flattenArguments($value) + public function testAfterLastTestMethodFinished(string $testClassName, ClassMethod ...$calledMethods): void { - if ($value instanceof Closure) { - $closureReflection = new ReflectionFunction($value); - $value = sprintf('(Closure at %s:%s)', $closureReflection->getFileName(), $closureReflection->getStartLine()); - } elseif (is_object($value)) { - $value = sprintf('object(%s)', get_class($value)); - } elseif (is_resource($value)) { - $value = sprintf('resource(%s)', get_resource_type($value)); - } elseif (is_array($value)) { - $value = array_map([$this, 'flattenArguments'], $value); - } - return $value; + $this->dispatcher->dispatch(new \PHPUnit\Event\Test\AfterLastTestMethodFinished($this->telemetryInfo(), $testClassName, ...$calledMethods)); } - public function render(?Formatter $formatter = null) : string + /** + * @throws InvalidArgumentException + * @throws UnknownEventTypeException + */ + public function testSuiteFinished(TestSuite $testSuite): void { - if ($formatter === null) { - $formatter = new Formatter\PassthroughFormatter(); - } - return $formatter->format($this); + $this->dispatcher->dispatch(new TestSuiteFinished($this->telemetryInfo(), $testSuite)); } - public function __toString() : string + /** + * @throws InvalidArgumentException + * @throws UnknownEventTypeException + */ + public function testRunnerTriggeredDeprecation(string $message): void { - return $this->body; + $this->dispatcher->dispatch(new \PHPUnit\Event\TestRunner\DeprecationTriggered($this->telemetryInfo(), $message)); } -} -link = $link; - $this->description = $description; + $this->dispatcher->dispatch(new \PHPUnit\Event\TestRunner\WarningTriggered($this->telemetryInfo(), $message)); + } + /** + * @throws InvalidArgumentException + * @throws UnknownEventTypeException + */ + public function testRunnerEnabledGarbageCollection(): void + { + $this->dispatcher->dispatch(new \PHPUnit\Event\TestRunner\GarbageCollectionEnabled($this->telemetryInfo())); + } + /** + * @throws InvalidArgumentException + * @throws UnknownEventTypeException + */ + public function testRunnerExecutionAborted(): void + { + $this->dispatcher->dispatch(new \PHPUnit\Event\TestRunner\ExecutionAborted($this->telemetryInfo())); } - public static function create(string $body, ?DescriptionFactory $descriptionFactory = null, ?TypeContext $context = null) : self + /** + * @throws InvalidArgumentException + * @throws UnknownEventTypeException + */ + public function testRunnerExecutionFinished(): void { - Assert::notNull($descriptionFactory); - $parts = Utils::pregSplit('/\\s+/Su', $body, 2); - $description = isset($parts[1]) ? $descriptionFactory->create($parts[1], $context) : null; - return new static($parts[0], $description); + $this->dispatcher->dispatch(new \PHPUnit\Event\TestRunner\ExecutionFinished($this->telemetryInfo())); } /** - * Gets the link + * @throws InvalidArgumentException + * @throws UnknownEventTypeException */ - public function getLink() : string + public function testRunnerFinished(): void { - return $this->link; + $this->dispatcher->dispatch(new \PHPUnit\Event\TestRunner\Finished($this->telemetryInfo())); } /** - * Returns a string representation for this tag. + * @throws InvalidArgumentException + * @throws UnknownEventTypeException */ - public function __toString() : string + public function applicationFinished(int $shellExitCode): void { - if ($this->description) { - $description = $this->description->render(); - } else { - $description = ''; - } - $link = $this->link; - return $link . ($description !== '' ? ($link !== '' ? ' ' : '') . $description : ''); + $this->dispatcher->dispatch(new \PHPUnit\Event\Application\Finished($this->telemetryInfo(), $shellExitCode)); + } + /** + * @throws InvalidArgumentException + */ + private function telemetryInfo(): \PHPUnit\Event\Telemetry\Info + { + $current = $this->system->snapshot(); + $info = new \PHPUnit\Event\Telemetry\Info($current, $current->time()->duration($this->startSnapshot->time()), $current->memoryUsage()->diff($this->startSnapshot->memoryUsage()), $current->time()->duration($this->previousSnapshot->time()), $current->memoryUsage()->diff($this->previousSnapshot->memoryUsage())); + $this->previousSnapshot = $current; + return $info; } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. - * - * @link http://phpdoc.org */ -namespace PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\Tags; +namespace PHPUnit\Event; -use InvalidArgumentException; -use PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\Description; -use PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\DescriptionFactory; -use PHPUnitPHAR\phpDocumentor\Reflection\Type; -use PHPUnitPHAR\phpDocumentor\Reflection\TypeResolver; -use PHPUnitPHAR\phpDocumentor\Reflection\Types\Context as TypeContext; -use PHPUnitPHAR\phpDocumentor\Reflection\Types\Mixed_; -use PHPUnitPHAR\phpDocumentor\Reflection\Types\Void_; -use PHPUnitPHAR\Webmozart\Assert\Assert; -use function array_keys; -use function explode; -use function implode; -use function is_string; -use function preg_match; -use function sort; -use function strpos; -use function substr; -use function trim; -use function var_export; +use PHPUnit\Event\Code\ClassMethod; +use PHPUnit\Event\Code\ComparisonFailure; +use PHPUnit\Event\Code\Throwable; +use PHPUnit\Event\TestSuite\TestSuite; +use PHPUnit\Framework\Constraint; +use PHPUnit\TextUI\Configuration\Configuration; /** - * Reflection class for an {@}method in a Docblock. + * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class Method extends BaseTag implements Factory\StaticMethod +interface Emitter { - /** @var string */ - protected $name = 'method'; - /** @var string */ - private $methodName; /** - * @phpstan-var array - * @var array> + * @deprecated */ - private $arguments; - /** @var bool */ - private $isStatic; - /** @var Type */ - private $returnType; + public function exportObjects(): void; /** - * @param array> $arguments - * @phpstan-param array $arguments + * @deprecated */ - public function __construct(string $methodName, array $arguments = [], ?Type $returnType = null, bool $static = \false, ?Description $description = null) - { - Assert::stringNotEmpty($methodName); - if ($returnType === null) { - $returnType = new Void_(); - } - $this->methodName = $methodName; - $this->arguments = $this->filterArguments($arguments); - $this->returnType = $returnType; - $this->isStatic = $static; - $this->description = $description; - } - public static function create(string $body, ?TypeResolver $typeResolver = null, ?DescriptionFactory $descriptionFactory = null, ?TypeContext $context = null) : ?self - { - Assert::stringNotEmpty($body); - Assert::notNull($typeResolver); - Assert::notNull($descriptionFactory); - // 1. none or more whitespace - // 2. optionally the keyword "static" followed by whitespace - // 3. optionally a word with underscores followed by whitespace : as - // type for the return value - // 4. then optionally a word with underscores followed by () and - // whitespace : as method name as used by phpDocumentor - // 5. then a word with underscores, followed by ( and any character - // until a ) and whitespace : as method name with signature - // 6. any remaining text : as description - if (!preg_match('/^ - # Static keyword - # Declares a static method ONLY if type is also present - (?: - (static) - \\s+ - )? - # Return type - (?: - ( - (?:[\\w\\|_\\\\]*\\$this[\\w\\|_\\\\]*) - | - (?: - (?:[\\w\\|_\\\\]+) - # array notation - (?:\\[\\])* - )*+ - ) - \\s+ - )? - # Method name - ([\\w_]+) - # Arguments - (?: - \\(([^\\)]*)\\) - )? - \\s* - # Description - (.*) - $/sux', $body, $matches)) { - return null; - } - [, $static, $returnType, $methodName, $argumentLines, $description] = $matches; - $static = $static === 'static'; - if ($returnType === '') { - $returnType = 'void'; - } - $returnType = $typeResolver->resolve($returnType, $context); - $description = $descriptionFactory->create($description, $context); - /** @phpstan-var array $arguments */ - $arguments = []; - if ($argumentLines !== '') { - $argumentsExploded = explode(',', $argumentLines); - foreach ($argumentsExploded as $argument) { - $argument = explode(' ', self::stripRestArg(trim($argument)), 2); - if (strpos($argument[0], '$') === 0) { - $argumentName = substr($argument[0], 1); - $argumentType = new Mixed_(); - } else { - $argumentType = $typeResolver->resolve($argument[0], $context); - $argumentName = ''; - if (isset($argument[1])) { - $argument[1] = self::stripRestArg($argument[1]); - $argumentName = substr($argument[1], 1); - } - } - $arguments[] = ['name' => $argumentName, 'type' => $argumentType]; - } - } - return new static($methodName, $arguments, $returnType, $static, $description); - } + public function exportsObjects(): bool; + public function applicationStarted(): void; + public function testRunnerStarted(): void; + public function testRunnerConfigured(Configuration $configuration): void; + public function testRunnerBootstrapFinished(string $filename): void; + public function testRunnerLoadedExtensionFromPhar(string $filename, string $name, string $version): void; /** - * Retrieves the method name. + * @psalm-param class-string $className + * @psalm-param array $parameters */ - public function getMethodName() : string - { - return $this->methodName; - } + public function testRunnerBootstrappedExtension(string $className, array $parameters): void; + public function dataProviderMethodCalled(ClassMethod $testMethod, ClassMethod $dataProviderMethod): void; + public function dataProviderMethodFinished(ClassMethod $testMethod, ClassMethod ...$calledMethods): void; + public function testSuiteLoaded(TestSuite $testSuite): void; + public function testSuiteFiltered(TestSuite $testSuite): void; + public function testSuiteSorted(int $executionOrder, int $executionOrderDefects, bool $resolveDependencies): void; + public function testRunnerEventFacadeSealed(): void; + public function testRunnerExecutionStarted(TestSuite $testSuite): void; + public function testRunnerDisabledGarbageCollection(): void; + public function testRunnerTriggeredGarbageCollection(): void; + public function testSuiteSkipped(TestSuite $testSuite, string $message): void; + public function testSuiteStarted(TestSuite $testSuite): void; + public function testPreparationStarted(\PHPUnit\Event\Code\Test $test): void; + public function testPreparationFailed(\PHPUnit\Event\Code\Test $test): void; /** - * @return array> - * @phpstan-return array + * @psalm-param class-string $testClassName */ - public function getArguments() : array - { - return $this->arguments; - } + public function testBeforeFirstTestMethodCalled(string $testClassName, ClassMethod $calledMethod): void; /** - * Checks whether the method tag describes a static method or not. - * - * @return bool TRUE if the method declaration is for a static method, FALSE otherwise. + * @psalm-param class-string $testClassName */ - public function isStatic() : bool - { - return $this->isStatic; - } - public function getReturnType() : Type - { - return $this->returnType; - } - public function __toString() : string - { - $arguments = []; - foreach ($this->arguments as $argument) { - $arguments[] = $argument['type'] . ' $' . $argument['name']; - } - $argumentStr = '(' . implode(', ', $arguments) . ')'; - if ($this->description) { - $description = $this->description->render(); - } else { - $description = ''; - } - $static = $this->isStatic ? 'static' : ''; - $returnType = (string) $this->returnType; - $methodName = $this->methodName; - return $static . ($returnType !== '' ? ($static !== '' ? ' ' : '') . $returnType : '') . ($methodName !== '' ? ($static !== '' || $returnType !== '' ? ' ' : '') . $methodName : '') . $argumentStr . ($description !== '' ? ' ' . $description : ''); - } + public function testBeforeFirstTestMethodErrored(string $testClassName, ClassMethod $calledMethod, Throwable $throwable): void; /** - * @param mixed[][]|string[] $arguments - * @phpstan-param array $arguments - * - * @return mixed[][] - * @phpstan-return array + * @psalm-param class-string $testClassName */ - private function filterArguments(array $arguments = []) : array - { - $result = []; - foreach ($arguments as $argument) { - if (is_string($argument)) { - $argument = ['name' => $argument]; - } - if (!isset($argument['type'])) { - $argument['type'] = new Mixed_(); - } - $keys = array_keys($argument); - sort($keys); - if ($keys !== ['name', 'type']) { - throw new InvalidArgumentException('Arguments can only have the "name" and "type" fields, found: ' . var_export($keys, \true)); - } - $result[] = $argument; - } - return $result; - } - private static function stripRestArg(string $argument) : string - { - if (strpos($argument, '...') === 0) { - $argument = trim(substr($argument, 3)); - } - return $argument; - } -} -name = 'param'; - $this->variableName = $variableName; - $this->type = $type; - $this->isVariadic = $isVariadic; - $this->description = $description; - $this->isReference = $isReference; - } - public static function create(string $body, ?TypeResolver $typeResolver = null, ?DescriptionFactory $descriptionFactory = null, ?TypeContext $context = null) : self - { - Assert::stringNotEmpty($body); - Assert::notNull($typeResolver); - Assert::notNull($descriptionFactory); - [$firstPart, $body] = self::extractTypeFromBody($body); - $type = null; - $parts = Utils::pregSplit('/(\\s+)/Su', $body, 2, PREG_SPLIT_DELIM_CAPTURE); - $variableName = ''; - $isVariadic = \false; - $isReference = \false; - // if the first item that is encountered is not a variable; it is a type - if ($firstPart && !self::strStartsWithVariable($firstPart)) { - $type = $typeResolver->resolve($firstPart, $context); - } else { - // first part is not a type; we should prepend it to the parts array for further processing - array_unshift($parts, $firstPart); - } - // if the next item starts with a $ or ...$ or &$ or &...$ it must be the variable name - if (isset($parts[0]) && self::strStartsWithVariable($parts[0])) { - $variableName = array_shift($parts); - if ($type) { - array_shift($parts); - } - Assert::notNull($variableName); - if (strpos($variableName, '$') === 0) { - $variableName = substr($variableName, 1); - } elseif (strpos($variableName, '&$') === 0) { - $isReference = \true; - $variableName = substr($variableName, 2); - } elseif (strpos($variableName, '...$') === 0) { - $isVariadic = \true; - $variableName = substr($variableName, 4); - } elseif (strpos($variableName, '&...$') === 0) { - $isVariadic = \true; - $isReference = \true; - $variableName = substr($variableName, 5); - } - } - $description = $descriptionFactory->create(implode('', $parts), $context); - return new static($variableName, $type, $isVariadic, $description, $isReference); - } + public function testBeforeFirstTestMethodFinished(string $testClassName, ClassMethod ...$calledMethods): void; /** - * Returns the variable's name. + * @psalm-param class-string $testClassName */ - public function getVariableName() : ?string - { - return $this->variableName; - } + public function testBeforeTestMethodCalled(string $testClassName, ClassMethod $calledMethod): void; /** - * Returns whether this tag is variadic. + * @psalm-param class-string $testClassName */ - public function isVariadic() : bool - { - return $this->isVariadic; - } + public function testBeforeTestMethodFinished(string $testClassName, ClassMethod ...$calledMethods): void; /** - * Returns whether this tag is passed by reference. + * @psalm-param class-string $testClassName */ - public function isReference() : bool - { - return $this->isReference; - } + public function testPreConditionCalled(string $testClassName, ClassMethod $calledMethod): void; /** - * Returns a string representation for this tag. + * @psalm-param class-string $testClassName */ - public function __toString() : string - { - if ($this->description) { - $description = $this->description->render(); - } else { - $description = ''; - } - $variableName = ''; - if ($this->variableName) { - $variableName .= ($this->isReference ? '&' : '') . ($this->isVariadic ? '...' : ''); - $variableName .= '$' . $this->variableName; - } - $type = (string) $this->type; - return $type . ($variableName !== '' ? ($type !== '' ? ' ' : '') . $variableName : '') . ($description !== '' ? ($type !== '' || $variableName !== '' ? ' ' : '') . $description : ''); - } - private static function strStartsWithVariable(string $str) : bool - { - return strpos($str, '$') === 0 || strpos($str, '...$') === 0 || strpos($str, '&$') === 0 || strpos($str, '&...$') === 0; - } + public function testPreConditionFinished(string $testClassName, ClassMethod ...$calledMethods): void; + public function testPrepared(\PHPUnit\Event\Code\Test $test): void; + /** + * @psalm-param class-string $className + */ + public function testRegisteredComparator(string $className): void; + /** + * @deprecated + */ + public function testAssertionSucceeded(mixed $value, Constraint\Constraint $constraint, string $message): void; + /** + * @deprecated + */ + public function testAssertionFailed(mixed $value, Constraint\Constraint $constraint, string $message): void; + /** + * @psalm-param class-string $className + */ + public function testCreatedMockObject(string $className): void; + /** + * @psalm-param list $interfaces + */ + public function testCreatedMockObjectForIntersectionOfInterfaces(array $interfaces): void; + /** + * @psalm-param trait-string $traitName + */ + public function testCreatedMockObjectForTrait(string $traitName): void; + /** + * @psalm-param class-string $className + */ + public function testCreatedMockObjectForAbstractClass(string $className): void; + /** + * @psalm-param class-string $originalClassName + * @psalm-param class-string $mockClassName + */ + public function testCreatedMockObjectFromWsdl(string $wsdlFile, string $originalClassName, string $mockClassName, array $methods, bool $callOriginalConstructor, array $options): void; + /** + * @psalm-param class-string $className + */ + public function testCreatedPartialMockObject(string $className, string ...$methodNames): void; + /** + * @psalm-param class-string $className + */ + public function testCreatedTestProxy(string $className, array $constructorArguments): void; + /** + * @psalm-param class-string $className + */ + public function testCreatedStub(string $className): void; + /** + * @psalm-param list $interfaces + */ + public function testCreatedStubForIntersectionOfInterfaces(array $interfaces): void; + public function testErrored(\PHPUnit\Event\Code\Test $test, Throwable $throwable): void; + public function testFailed(\PHPUnit\Event\Code\Test $test, Throwable $throwable, ?ComparisonFailure $comparisonFailure): void; + public function testPassed(\PHPUnit\Event\Code\Test $test): void; + /** + * @psalm-param non-empty-string $message + */ + public function testConsideredRisky(\PHPUnit\Event\Code\Test $test, string $message): void; + public function testMarkedAsIncomplete(\PHPUnit\Event\Code\Test $test, Throwable $throwable): void; + /** + * @psalm-param non-empty-string $message + */ + public function testSkipped(\PHPUnit\Event\Code\Test $test, string $message): void; + /** + * @psalm-param non-empty-string $message + */ + public function testTriggeredPhpunitDeprecation(\PHPUnit\Event\Code\Test $test, string $message): void; + /** + * @psalm-param non-empty-string $message + * @psalm-param non-empty-string $file + * @psalm-param positive-int $line + */ + public function testTriggeredPhpDeprecation(\PHPUnit\Event\Code\Test $test, string $message, string $file, int $line, bool $suppressed, bool $ignoredByBaseline, bool $ignoredByTest): void; + /** + * @psalm-param non-empty-string $message + * @psalm-param non-empty-string $file + * @psalm-param positive-int $line + */ + public function testTriggeredDeprecation(\PHPUnit\Event\Code\Test $test, string $message, string $file, int $line, bool $suppressed, bool $ignoredByBaseline, bool $ignoredByTest): void; + /** + * @psalm-param non-empty-string $message + * @psalm-param non-empty-string $file + * @psalm-param positive-int $line + */ + public function testTriggeredError(\PHPUnit\Event\Code\Test $test, string $message, string $file, int $line, bool $suppressed): void; + /** + * @psalm-param non-empty-string $message + * @psalm-param non-empty-string $file + * @psalm-param positive-int $line + */ + public function testTriggeredNotice(\PHPUnit\Event\Code\Test $test, string $message, string $file, int $line, bool $suppressed, bool $ignoredByBaseline): void; + /** + * @psalm-param non-empty-string $message + * @psalm-param non-empty-string $file + * @psalm-param positive-int $line + */ + public function testTriggeredPhpNotice(\PHPUnit\Event\Code\Test $test, string $message, string $file, int $line, bool $suppressed, bool $ignoredByBaseline): void; + /** + * @psalm-param non-empty-string $message + * @psalm-param non-empty-string $file + * @psalm-param positive-int $line + */ + public function testTriggeredWarning(\PHPUnit\Event\Code\Test $test, string $message, string $file, int $line, bool $suppressed, bool $ignoredByBaseline): void; + /** + * @psalm-param non-empty-string $message + * @psalm-param non-empty-string $file + * @psalm-param positive-int $line + */ + public function testTriggeredPhpWarning(\PHPUnit\Event\Code\Test $test, string $message, string $file, int $line, bool $suppressed, bool $ignoredByBaseline): void; + /** + * @psalm-param non-empty-string $message + */ + public function testTriggeredPhpunitError(\PHPUnit\Event\Code\Test $test, string $message): void; + /** + * @psalm-param non-empty-string $message + */ + public function testTriggeredPhpunitWarning(\PHPUnit\Event\Code\Test $test, string $message): void; + /** + * @psalm-param non-empty-string $output + */ + public function testPrintedUnexpectedOutput(string $output): void; + public function testFinished(\PHPUnit\Event\Code\Test $test, int $numberOfAssertionsPerformed): void; + /** + * @psalm-param class-string $testClassName + */ + public function testPostConditionCalled(string $testClassName, ClassMethod $calledMethod): void; + /** + * @psalm-param class-string $testClassName + */ + public function testPostConditionFinished(string $testClassName, ClassMethod ...$calledMethods): void; + /** + * @psalm-param class-string $testClassName + */ + public function testAfterTestMethodCalled(string $testClassName, ClassMethod $calledMethod): void; + /** + * @psalm-param class-string $testClassName + */ + public function testAfterTestMethodFinished(string $testClassName, ClassMethod ...$calledMethods): void; + /** + * @psalm-param class-string $testClassName + */ + public function testAfterLastTestMethodCalled(string $testClassName, ClassMethod $calledMethod): void; + /** + * @psalm-param class-string $testClassName + */ + public function testAfterLastTestMethodFinished(string $testClassName, ClassMethod ...$calledMethods): void; + public function testSuiteFinished(TestSuite $testSuite): void; + public function testRunnerTriggeredDeprecation(string $message): void; + public function testRunnerTriggeredWarning(string $message): void; + public function testRunnerEnabledGarbageCollection(): void; + public function testRunnerExecutionAborted(): void; + public function testRunnerExecutionFinished(): void; + public function testRunnerFinished(): void; + public function applicationFinished(int $shellExitCode): void; } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. - * - * @link http://phpdoc.org */ -namespace PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\Tags; +namespace PHPUnit\Event\Application; -use PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\Description; -use PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\DescriptionFactory; -use PHPUnitPHAR\phpDocumentor\Reflection\Type; -use PHPUnitPHAR\phpDocumentor\Reflection\TypeResolver; -use PHPUnitPHAR\phpDocumentor\Reflection\Types\Context as TypeContext; -use PHPUnitPHAR\phpDocumentor\Reflection\Utils; -use PHPUnitPHAR\Webmozart\Assert\Assert; -use function array_shift; -use function array_unshift; -use function implode; -use function strpos; -use function substr; -use const PREG_SPLIT_DELIM_CAPTURE; +use function sprintf; +use PHPUnit\Event\Event; +use PHPUnit\Event\Telemetry; /** - * Reflection class for a {@}property tag in a Docblock. + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -final class Property extends TagWithType implements Factory\StaticMethod +final class Finished implements Event { - /** @var string|null */ - protected $variableName; - public function __construct(?string $variableName, ?Type $type = null, ?Description $description = null) + private readonly Telemetry\Info $telemetryInfo; + private readonly int $shellExitCode; + public function __construct(Telemetry\Info $telemetryInfo, int $shellExitCode) { - Assert::string($variableName); - $this->name = 'property'; - $this->variableName = $variableName; - $this->type = $type; - $this->description = $description; + $this->telemetryInfo = $telemetryInfo; + $this->shellExitCode = $shellExitCode; } - public static function create(string $body, ?TypeResolver $typeResolver = null, ?DescriptionFactory $descriptionFactory = null, ?TypeContext $context = null) : self - { - Assert::stringNotEmpty($body); - Assert::notNull($typeResolver); - Assert::notNull($descriptionFactory); - [$firstPart, $body] = self::extractTypeFromBody($body); - $type = null; - $parts = Utils::pregSplit('/(\\s+)/Su', $body, 2, PREG_SPLIT_DELIM_CAPTURE); - $variableName = ''; - // if the first item that is encountered is not a variable; it is a type - if ($firstPart && $firstPart[0] !== '$') { - $type = $typeResolver->resolve($firstPart, $context); - } else { - // first part is not a type; we should prepend it to the parts array for further processing - array_unshift($parts, $firstPart); - } - // if the next item starts with a $ it must be the variable name - if (isset($parts[0]) && strpos($parts[0], '$') === 0) { - $variableName = array_shift($parts); - if ($type) { - array_shift($parts); - } - Assert::notNull($variableName); - $variableName = substr($variableName, 1); - } - $description = $descriptionFactory->create(implode('', $parts), $context); - return new static($variableName, $type, $description); + public function telemetryInfo(): Telemetry\Info + { + return $this->telemetryInfo; } - /** - * Returns the variable's name. - */ - public function getVariableName() : ?string + public function shellExitCode(): int { - return $this->variableName; + return $this->shellExitCode; } - /** - * Returns a string representation for this tag. - */ - public function __toString() : string + public function asString(): string { - if ($this->description) { - $description = $this->description->render(); - } else { - $description = ''; - } - if ($this->variableName) { - $variableName = '$' . $this->variableName; - } else { - $variableName = ''; - } - $type = (string) $this->type; - return $type . ($variableName !== '' ? ($type !== '' ? ' ' : '') . $variableName : '') . ($description !== '' ? ($type !== '' || $variableName !== '' ? ' ' : '') . $description : ''); + return sprintf('PHPUnit Finished (Shell Exit Code: %d)', $this->shellExitCode); } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. - * - * @link http://phpdoc.org */ -namespace PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\Tags; +namespace PHPUnit\Event\Application; -use PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\Description; -use PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\DescriptionFactory; -use PHPUnitPHAR\phpDocumentor\Reflection\Type; -use PHPUnitPHAR\phpDocumentor\Reflection\TypeResolver; -use PHPUnitPHAR\phpDocumentor\Reflection\Types\Context as TypeContext; -use PHPUnitPHAR\phpDocumentor\Reflection\Utils; -use PHPUnitPHAR\Webmozart\Assert\Assert; -use function array_shift; -use function array_unshift; -use function implode; -use function strpos; -use function substr; -use const PREG_SPLIT_DELIM_CAPTURE; +use PHPUnit\Event\Subscriber; /** - * Reflection class for a {@}property-read tag in a Docblock. + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -final class PropertyRead extends TagWithType implements Factory\StaticMethod +interface FinishedSubscriber extends Subscriber { - /** @var string|null */ - protected $variableName; - public function __construct(?string $variableName, ?Type $type = null, ?Description $description = null) - { - Assert::string($variableName); - $this->name = 'property-read'; - $this->variableName = $variableName; - $this->type = $type; - $this->description = $description; - } - public static function create(string $body, ?TypeResolver $typeResolver = null, ?DescriptionFactory $descriptionFactory = null, ?TypeContext $context = null) : self - { - Assert::stringNotEmpty($body); - Assert::notNull($typeResolver); - Assert::notNull($descriptionFactory); - [$firstPart, $body] = self::extractTypeFromBody($body); - $type = null; - $parts = Utils::pregSplit('/(\\s+)/Su', $body, 2, PREG_SPLIT_DELIM_CAPTURE); - $variableName = ''; - // if the first item that is encountered is not a variable; it is a type - if ($firstPart && $firstPart[0] !== '$') { - $type = $typeResolver->resolve($firstPart, $context); - } else { - // first part is not a type; we should prepend it to the parts array for further processing - array_unshift($parts, $firstPart); - } - // if the next item starts with a $ it must be the variable name - if (isset($parts[0]) && strpos($parts[0], '$') === 0) { - $variableName = array_shift($parts); - if ($type) { - array_shift($parts); - } - Assert::notNull($variableName); - $variableName = substr($variableName, 1); - } - $description = $descriptionFactory->create(implode('', $parts), $context); - return new static($variableName, $type, $description); - } - /** - * Returns the variable's name. - */ - public function getVariableName() : ?string - { - return $this->variableName; - } - /** - * Returns a string representation for this tag. - */ - public function __toString() : string - { - if ($this->description) { - $description = $this->description->render(); - } else { - $description = ''; - } - if ($this->variableName) { - $variableName = '$' . $this->variableName; - } else { - $variableName = ''; - } - $type = (string) $this->type; - return $type . ($variableName !== '' ? ($type !== '' ? ' ' : '') . $variableName : '') . ($description !== '' ? ($type !== '' || $variableName !== '' ? ' ' : '') . $description : ''); - } + public function notify(\PHPUnit\Event\Application\Finished $event): void; } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. - * - * @link http://phpdoc.org */ -namespace PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\Tags; +namespace PHPUnit\Event\Application; -use PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\Description; -use PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\DescriptionFactory; -use PHPUnitPHAR\phpDocumentor\Reflection\Type; -use PHPUnitPHAR\phpDocumentor\Reflection\TypeResolver; -use PHPUnitPHAR\phpDocumentor\Reflection\Types\Context as TypeContext; -use PHPUnitPHAR\phpDocumentor\Reflection\Utils; -use PHPUnitPHAR\Webmozart\Assert\Assert; -use function array_shift; -use function array_unshift; -use function implode; -use function strpos; -use function substr; -use const PREG_SPLIT_DELIM_CAPTURE; +use function sprintf; +use PHPUnit\Event\Event; +use PHPUnit\Event\Runtime\Runtime; +use PHPUnit\Event\Telemetry; /** - * Reflection class for a {@}property-write tag in a Docblock. + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -final class PropertyWrite extends TagWithType implements Factory\StaticMethod +final class Started implements Event { - /** @var string */ - protected $variableName; - public function __construct(?string $variableName, ?Type $type = null, ?Description $description = null) + private readonly Telemetry\Info $telemetryInfo; + private readonly Runtime $runtime; + public function __construct(Telemetry\Info $telemetryInfo, Runtime $runtime) { - Assert::string($variableName); - $this->name = 'property-write'; - $this->variableName = $variableName; - $this->type = $type; - $this->description = $description; + $this->telemetryInfo = $telemetryInfo; + $this->runtime = $runtime; } - public static function create(string $body, ?TypeResolver $typeResolver = null, ?DescriptionFactory $descriptionFactory = null, ?TypeContext $context = null) : self - { - Assert::stringNotEmpty($body); - Assert::notNull($typeResolver); - Assert::notNull($descriptionFactory); - [$firstPart, $body] = self::extractTypeFromBody($body); - $type = null; - $parts = Utils::pregSplit('/(\\s+)/Su', $body, 2, PREG_SPLIT_DELIM_CAPTURE); - $variableName = ''; - // if the first item that is encountered is not a variable; it is a type - if ($firstPart && $firstPart[0] !== '$') { - $type = $typeResolver->resolve($firstPart, $context); - } else { - // first part is not a type; we should prepend it to the parts array for further processing - array_unshift($parts, $firstPart); - } - // if the next item starts with a $ it must be the variable name - if (isset($parts[0]) && strpos($parts[0], '$') === 0) { - $variableName = array_shift($parts); - if ($type) { - array_shift($parts); - } - Assert::notNull($variableName); - $variableName = substr($variableName, 1); - } - $description = $descriptionFactory->create(implode('', $parts), $context); - return new static($variableName, $type, $description); + public function telemetryInfo(): Telemetry\Info + { + return $this->telemetryInfo; } - /** - * Returns the variable's name. - */ - public function getVariableName() : ?string + public function runtime(): Runtime { - return $this->variableName; + return $this->runtime; } - /** - * Returns a string representation for this tag. - */ - public function __toString() : string + public function asString(): string { - if ($this->description) { - $description = $this->description->render(); - } else { - $description = ''; - } - if ($this->variableName) { - $variableName = '$' . $this->variableName; - } else { - $variableName = ''; - } - $type = (string) $this->type; - return $type . ($variableName !== '' ? ($type !== '' ? ' ' : '') . $variableName : '') . ($description !== '' ? ($type !== '' || $variableName !== '' ? ' ' : '') . $description : ''); + return sprintf('PHPUnit Started (%s)', $this->runtime->asString()); } } * - * @link http://phpdoc.org + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. */ -namespace PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\Tags\Reference; +namespace PHPUnit\Event\Application; -use PHPUnitPHAR\phpDocumentor\Reflection\Fqsen as RealFqsen; +use PHPUnit\Event\Subscriber; /** - * Fqsen reference used by {@see \phpDocumentor\Reflection\DocBlock\Tags\See} + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -final class Fqsen implements Reference +interface StartedSubscriber extends Subscriber { - /** @var RealFqsen */ - private $fqsen; - public function __construct(RealFqsen $fqsen) - { - $this->fqsen = $fqsen; - } - /** - * @return string string representation of the referenced fqsen - */ - public function __toString() : string - { - return (string) $this->fqsen; - } + public function notify(\PHPUnit\Event\Application\Started $event): void; } * - * @link http://phpdoc.org + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. */ -namespace PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\Tags\Reference; +namespace PHPUnit\Event; /** - * Interface for references in {@see \phpDocumentor\Reflection\DocBlock\Tags\See} + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -interface Reference +interface Event { - public function __toString() : string; + public function telemetryInfo(): \PHPUnit\Event\Telemetry\Info; + public function asString(): string; } * - * @link http://phpdoc.org + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. */ -namespace PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\Tags\Reference; +namespace PHPUnit\Event; -use PHPUnitPHAR\Webmozart\Assert\Assert; +use function count; +use Countable; +use IteratorAggregate; /** - * Url reference used by {@see \phpDocumentor\Reflection\DocBlock\Tags\See} + * @template-implements IteratorAggregate + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -final class Url implements Reference +final class EventCollection implements Countable, IteratorAggregate { - /** @var string */ - private $uri; - public function __construct(string $uri) + /** + * @psalm-var list + */ + private array $events = []; + public function add(\PHPUnit\Event\Event ...$events): void { - Assert::stringNotEmpty($uri); - $this->uri = $uri; + foreach ($events as $event) { + $this->events[] = $event; + } } - public function __toString() : string + /** + * @psalm-return list + */ + public function asArray(): array { - return $this->uri; + return $this->events; } -} -name = 'return'; - $this->type = $type; - $this->description = $description; + return count($this->events); } - public static function create(string $body, ?TypeResolver $typeResolver = null, ?DescriptionFactory $descriptionFactory = null, ?TypeContext $context = null) : self + public function isEmpty(): bool { - Assert::notNull($typeResolver); - Assert::notNull($descriptionFactory); - [$type, $description] = self::extractTypeFromBody($body); - $type = $typeResolver->resolve($type, $context); - $description = $descriptionFactory->create($description, $context); - return new static($type, $description); + return $this->count() === 0; } - public function __toString() : string + public function isNotEmpty(): bool { - if ($this->description) { - $description = $this->description->render(); - } else { - $description = ''; - } - $type = $this->type ? '' . $this->type : 'mixed'; - return $type . ($description !== '' ? ' ' . $description : ''); + return $this->count() > 0; + } + public function getIterator(): \PHPUnit\Event\EventCollectionIterator + { + return new \PHPUnit\Event\EventCollectionIterator($this); } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. - * - * @link http://phpdoc.org */ -namespace PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\Tags; +namespace PHPUnit\Event; -use PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\Description; -use PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\DescriptionFactory; -use PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\Tags\Reference\Fqsen as FqsenRef; -use PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\Tags\Reference\Reference; -use PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\Tags\Reference\Url; -use PHPUnitPHAR\phpDocumentor\Reflection\Fqsen; -use PHPUnitPHAR\phpDocumentor\Reflection\FqsenResolver; -use PHPUnitPHAR\phpDocumentor\Reflection\Types\Context as TypeContext; -use PHPUnitPHAR\phpDocumentor\Reflection\Utils; -use PHPUnitPHAR\Webmozart\Assert\Assert; -use function array_key_exists; -use function explode; -use function preg_match; +use function count; +use Iterator; /** - * Reflection class for an {@}see tag in a Docblock. + * @template-implements Iterator + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -final class See extends BaseTag implements Factory\StaticMethod +final class EventCollectionIterator implements Iterator { - /** @var string */ - protected $name = 'see'; - /** @var Reference */ - protected $refers; /** - * Initializes this tag. + * @psalm-var list */ - public function __construct(Reference $refers, ?Description $description = null) + private readonly array $events; + private int $position = 0; + public function __construct(\PHPUnit\Event\EventCollection $events) { - $this->refers = $refers; - $this->description = $description; + $this->events = $events->asArray(); } - public static function create(string $body, ?FqsenResolver $typeResolver = null, ?DescriptionFactory $descriptionFactory = null, ?TypeContext $context = null) : self + public function rewind(): void { - Assert::notNull($descriptionFactory); - $parts = Utils::pregSplit('/\\s+/Su', $body, 2); - $description = isset($parts[1]) ? $descriptionFactory->create($parts[1], $context) : null; - // https://tools.ietf.org/html/rfc2396#section-3 - if (preg_match('#\\w://\\w#', $parts[0])) { - return new static(new Url($parts[0]), $description); - } - return new static(new FqsenRef(self::resolveFqsen($parts[0], $typeResolver, $context)), $description); + $this->position = 0; } - private static function resolveFqsen(string $parts, ?FqsenResolver $fqsenResolver, ?TypeContext $context) : Fqsen + public function valid(): bool { - Assert::notNull($fqsenResolver); - $fqsenParts = explode('::', $parts); - $resolved = $fqsenResolver->resolve($fqsenParts[0], $context); - if (!array_key_exists(1, $fqsenParts)) { - return $resolved; - } - return new Fqsen($resolved . '::' . $fqsenParts[1]); + return $this->position < count($this->events); } - /** - * Returns the ref of this tag. - */ - public function getReference() : Reference + public function key(): int { - return $this->refers; + return $this->position; } - /** - * Returns a string representation of this tag. - */ - public function __toString() : string + public function current(): \PHPUnit\Event\Event { - if ($this->description) { - $description = $this->description->render(); - } else { - $description = ''; - } - $refers = (string) $this->refers; - return $refers . ($description !== '' ? ($refers !== '' ? ' ' : '') . $description : ''); + return $this->events[$this->position]; + } + public function next(): void + { + $this->position++; } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. - * - * @link http://phpdoc.org */ -namespace PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\Tags; +namespace PHPUnit\Event\Test; -use PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\Description; -use PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\DescriptionFactory; -use PHPUnitPHAR\phpDocumentor\Reflection\Types\Context as TypeContext; -use PHPUnitPHAR\Webmozart\Assert\Assert; -use function preg_match; +use function sprintf; +use PHPUnit\Event\Event; +use PHPUnit\Event\Telemetry; /** - * Reflection class for a {@}since tag in a Docblock. + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @deprecated */ -final class Since extends BaseTag implements Factory\StaticMethod +final class AssertionFailed implements Event { - /** @var string */ - protected $name = 'since'; - /** - * PCRE regular expression matching a version vector. - * Assumes the "x" modifier. - */ - public const REGEX_VECTOR = '(?: - # Normal release vectors. - \\d\\S* - | - # VCS version vectors. Per PHPCS, they are expected to - # follow the form of the VCS name, followed by ":", followed - # by the version vector itself. - # By convention, popular VCSes like CVS, SVN and GIT use "$" - # around the actual version vector. - [^\\s\\:]+\\:\\s*\\$[^\\$]+\\$ - )'; - /** @var string|null The version vector. */ - private $version; - public function __construct(?string $version = null, ?Description $description = null) + private readonly Telemetry\Info $telemetryInfo; + private readonly string $value; + private readonly string $constraint; + private readonly int $count; + private readonly string $message; + public function __construct(Telemetry\Info $telemetryInfo, string $value, string $constraint, int $count, string $message) { - Assert::nullOrNotEmpty($version); - $this->version = $version; - $this->description = $description; + $this->telemetryInfo = $telemetryInfo; + $this->value = $value; + $this->constraint = $constraint; + $this->count = $count; + $this->message = $message; } - public static function create(?string $body, ?DescriptionFactory $descriptionFactory = null, ?TypeContext $context = null) : ?self + public function telemetryInfo(): Telemetry\Info { - if (empty($body)) { - return new static(); - } - $matches = []; - if (!preg_match('/^(' . self::REGEX_VECTOR . ')\\s*(.+)?$/sux', $body, $matches)) { - return null; - } - Assert::notNull($descriptionFactory); - return new static($matches[1], $descriptionFactory->create($matches[2] ?? '', $context)); + return $this->telemetryInfo; } - /** - * Gets the version section of the tag. - */ - public function getVersion() : ?string + public function value(): string { - return $this->version; + return $this->value; } - /** - * Returns a string representation for this tag. - */ - public function __toString() : string + public function count(): int { - if ($this->description) { - $description = $this->description->render(); - } else { - $description = ''; + return $this->count; + } + public function message(): string + { + return $this->message; + } + public function asString(): string + { + $message = ''; + if (!empty($this->message)) { + $message = sprintf(', Message: %s', $this->message); } - $version = (string) $this->version; - return $version . ($description !== '' ? ($version !== '' ? ' ' : '') . $description : ''); + return sprintf('Assertion Failed (Constraint: %s, Value: %s%s)', $this->constraint, $this->value, $message); } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. - * - * @link http://phpdoc.org */ -namespace PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\Tags; +namespace PHPUnit\Event\Test; -use PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\Description; -use PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\DescriptionFactory; -use PHPUnitPHAR\phpDocumentor\Reflection\Types\Context as TypeContext; -use PHPUnitPHAR\Webmozart\Assert\Assert; -use function preg_match; +use PHPUnit\Event\Subscriber; /** - * Reflection class for a {@}source tag in a Docblock. + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @deprecated */ -final class Source extends BaseTag implements Factory\StaticMethod +interface AssertionFailedSubscriber extends Subscriber { - /** @var string */ - protected $name = 'source'; - /** @var int The starting line, relative to the structural element's location. */ - private $startingLine; - /** @var int|null The number of lines, relative to the starting line. NULL means "to the end". */ - private $lineCount; - /** - * @param int|string $startingLine should be a to int convertible value - * @param int|string|null $lineCount should be a to int convertible value - */ - public function __construct($startingLine, $lineCount = null, ?Description $description = null) - { - Assert::integerish($startingLine); - Assert::nullOrIntegerish($lineCount); - $this->startingLine = (int) $startingLine; - $this->lineCount = $lineCount !== null ? (int) $lineCount : null; - $this->description = $description; - } - public static function create(string $body, ?DescriptionFactory $descriptionFactory = null, ?TypeContext $context = null) : self - { - Assert::stringNotEmpty($body); - Assert::notNull($descriptionFactory); - $startingLine = 1; - $lineCount = null; - $description = null; - // Starting line / Number of lines / Description - if (preg_match('/^([1-9]\\d*)\\s*(?:((?1))\\s+)?(.*)$/sux', $body, $matches)) { - $startingLine = (int) $matches[1]; - if (isset($matches[2]) && $matches[2] !== '') { - $lineCount = (int) $matches[2]; - } - $description = $matches[3]; - } - return new static($startingLine, $lineCount, $descriptionFactory->create($description ?? '', $context)); - } - /** - * Gets the starting line. - * - * @return int The starting line, relative to the structural element's - * location. - */ - public function getStartingLine() : int - { - return $this->startingLine; - } - /** - * Returns the number of lines. - * - * @return int|null The number of lines, relative to the starting line. NULL - * means "to the end". - */ - public function getLineCount() : ?int - { - return $this->lineCount; - } - public function __toString() : string - { - if ($this->description) { - $description = $this->description->render(); - } else { - $description = ''; - } - $startingLine = (string) $this->startingLine; - $lineCount = $this->lineCount !== null ? ' ' . $this->lineCount : ''; - return $startingLine . $lineCount . ($description !== '' ? ' ' . $description : ''); - } + public function notify(\PHPUnit\Event\Test\AssertionFailed $event): void; } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. - * - * @link http://phpdoc.org */ -namespace PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\Tags; +namespace PHPUnit\Event\Test; -use PHPUnitPHAR\phpDocumentor\Reflection\Type; -use function in_array; -use function strlen; -use function substr; -use function trim; -abstract class TagWithType extends BaseTag +use function sprintf; +use PHPUnit\Event\Event; +use PHPUnit\Event\Telemetry; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @deprecated + */ +final class AssertionSucceeded implements Event { - /** @var ?Type */ - protected $type; - /** - * Returns the type section of the variable. - */ - public function getType() : ?Type + private readonly Telemetry\Info $telemetryInfo; + private readonly string $value; + private readonly string $constraint; + private readonly int $count; + private readonly string $message; + public function __construct(Telemetry\Info $telemetryInfo, string $value, string $constraint, int $count, string $message) { - return $this->type; + $this->telemetryInfo = $telemetryInfo; + $this->value = $value; + $this->constraint = $constraint; + $this->count = $count; + $this->message = $message; } - /** - * @return string[] - */ - protected static function extractTypeFromBody(string $body) : array + public function telemetryInfo(): Telemetry\Info { - $type = ''; - $nestingLevel = 0; - for ($i = 0, $iMax = strlen($body); $i < $iMax; $i++) { - $character = $body[$i]; - if ($nestingLevel === 0 && trim($character) === '') { - break; - } - $type .= $character; - if (in_array($character, ['<', '(', '[', '{'])) { - $nestingLevel++; - continue; - } - if (in_array($character, ['>', ')', ']', '}'])) { - $nestingLevel--; - continue; - } + return $this->telemetryInfo; + } + public function value(): string + { + return $this->value; + } + public function count(): int + { + return $this->count; + } + public function message(): string + { + return $this->message; + } + public function asString(): string + { + $message = ''; + if (!empty($this->message)) { + $message = sprintf(', Message: %s', $this->message); } - $description = trim(substr($body, strlen($type))); - return [$type, $description]; + return sprintf('Assertion Succeeded (Constraint: %s, Value: %s%s)', $this->constraint, $this->value, $message); } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. - * - * @link http://phpdoc.org */ -namespace PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\Tags; +namespace PHPUnit\Event\Test; -use PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\Description; -use PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\DescriptionFactory; -use PHPUnitPHAR\phpDocumentor\Reflection\Type; -use PHPUnitPHAR\phpDocumentor\Reflection\TypeResolver; -use PHPUnitPHAR\phpDocumentor\Reflection\Types\Context as TypeContext; -use PHPUnitPHAR\Webmozart\Assert\Assert; +use PHPUnit\Event\Subscriber; /** - * Reflection class for a {@}throws tag in a Docblock. + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @deprecated */ -final class Throws extends TagWithType implements Factory\StaticMethod +interface AssertionSucceededSubscriber extends Subscriber { - public function __construct(Type $type, ?Description $description = null) - { - $this->name = 'throws'; - $this->type = $type; - $this->description = $description; - } - public static function create(string $body, ?TypeResolver $typeResolver = null, ?DescriptionFactory $descriptionFactory = null, ?TypeContext $context = null) : self - { - Assert::notNull($typeResolver); - Assert::notNull($descriptionFactory); - [$type, $description] = self::extractTypeFromBody($body); - $type = $typeResolver->resolve($type, $context); - $description = $descriptionFactory->create($description, $context); - return new static($type, $description); - } - public function __toString() : string - { - if ($this->description) { - $description = $this->description->render(); - } else { - $description = ''; - } - $type = (string) $this->type; - return $type . ($description !== '' ? ($type !== '' ? ' ' : '') . $description : ''); - } + public function notify(\PHPUnit\Event\Test\AssertionSucceeded $event): void; } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. - * - * @link http://phpdoc.org */ -namespace PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\Tags; +namespace PHPUnit\Event\Test; -use PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\Description; -use PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\DescriptionFactory; -use PHPUnitPHAR\phpDocumentor\Reflection\Fqsen; -use PHPUnitPHAR\phpDocumentor\Reflection\FqsenResolver; -use PHPUnitPHAR\phpDocumentor\Reflection\Types\Context as TypeContext; -use PHPUnitPHAR\phpDocumentor\Reflection\Utils; -use PHPUnitPHAR\Webmozart\Assert\Assert; -use function array_key_exists; -use function explode; +use function sprintf; +use PHPUnit\Event\Event; +use PHPUnit\Event\Telemetry; /** - * Reflection class for a {@}uses tag in a Docblock. + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -final class Uses extends BaseTag implements Factory\StaticMethod +final class ComparatorRegistered implements Event { - /** @var string */ - protected $name = 'uses'; - /** @var Fqsen */ - protected $refers; + private readonly Telemetry\Info $telemetryInfo; /** - * Initializes this tag. + * @psalm-var class-string */ - public function __construct(Fqsen $refers, ?Description $description = null) - { - $this->refers = $refers; - $this->description = $description; - } - public static function create(string $body, ?FqsenResolver $resolver = null, ?DescriptionFactory $descriptionFactory = null, ?TypeContext $context = null) : self + private readonly string $className; + /** + * @psalm-param class-string $className + */ + public function __construct(Telemetry\Info $telemetryInfo, string $className) { - Assert::notNull($resolver); - Assert::notNull($descriptionFactory); - $parts = Utils::pregSplit('/\\s+/Su', $body, 2); - return new static(self::resolveFqsen($parts[0], $resolver, $context), $descriptionFactory->create($parts[1] ?? '', $context)); + $this->telemetryInfo = $telemetryInfo; + $this->className = $className; } - private static function resolveFqsen(string $parts, ?FqsenResolver $fqsenResolver, ?TypeContext $context) : Fqsen + public function telemetryInfo(): Telemetry\Info { - Assert::notNull($fqsenResolver); - $fqsenParts = explode('::', $parts); - $resolved = $fqsenResolver->resolve($fqsenParts[0], $context); - if (!array_key_exists(1, $fqsenParts)) { - return $resolved; - } - return new Fqsen($resolved . '::' . $fqsenParts[1]); + return $this->telemetryInfo; } /** - * Returns the structural element this tag refers to. + * @psalm-return class-string */ - public function getReference() : Fqsen + public function className(): string { - return $this->refers; + return $this->className; } - /** - * Returns a string representation of this tag. - */ - public function __toString() : string + public function asString(): string { - if ($this->description) { - $description = $this->description->render(); - } else { - $description = ''; - } - $refers = (string) $this->refers; - return $refers . ($description !== '' ? ($refers !== '' ? ' ' : '') . $description : ''); + return sprintf('Comparator Registered (%s)', $this->className); } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. - * - * @link http://phpdoc.org */ -namespace PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\Tags; +namespace PHPUnit\Event\Test; -use PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\Description; -use PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\DescriptionFactory; -use PHPUnitPHAR\phpDocumentor\Reflection\Type; -use PHPUnitPHAR\phpDocumentor\Reflection\TypeResolver; -use PHPUnitPHAR\phpDocumentor\Reflection\Types\Context as TypeContext; -use PHPUnitPHAR\phpDocumentor\Reflection\Utils; -use PHPUnitPHAR\Webmozart\Assert\Assert; -use function array_shift; -use function array_unshift; -use function implode; -use function strpos; -use function substr; -use const PREG_SPLIT_DELIM_CAPTURE; +use PHPUnit\Event\Subscriber; /** - * Reflection class for a {@}var tag in a Docblock. + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -final class Var_ extends TagWithType implements Factory\StaticMethod +interface ComparatorRegisteredSubscriber extends Subscriber { - /** @var string|null */ - protected $variableName = ''; - public function __construct(?string $variableName, ?Type $type = null, ?Description $description = null) - { - Assert::string($variableName); - $this->name = 'var'; - $this->variableName = $variableName; - $this->type = $type; - $this->description = $description; - } - public static function create(string $body, ?TypeResolver $typeResolver = null, ?DescriptionFactory $descriptionFactory = null, ?TypeContext $context = null) : self - { - Assert::stringNotEmpty($body); - Assert::notNull($typeResolver); - Assert::notNull($descriptionFactory); - [$firstPart, $body] = self::extractTypeFromBody($body); - $parts = Utils::pregSplit('/(\\s+)/Su', $body, 2, PREG_SPLIT_DELIM_CAPTURE); - $type = null; - $variableName = ''; - // if the first item that is encountered is not a variable; it is a type - if ($firstPart && $firstPart[0] !== '$') { - $type = $typeResolver->resolve($firstPart, $context); - } else { - // first part is not a type; we should prepend it to the parts array for further processing - array_unshift($parts, $firstPart); - } - // if the next item starts with a $ it must be the variable name - if (isset($parts[0]) && strpos($parts[0], '$') === 0) { - $variableName = array_shift($parts); - if ($type) { - array_shift($parts); - } - Assert::notNull($variableName); - $variableName = substr($variableName, 1); - } - $description = $descriptionFactory->create(implode('', $parts), $context); - return new static($variableName, $type, $description); - } - /** - * Returns the variable's name. - */ - public function getVariableName() : ?string - { - return $this->variableName; - } - /** - * Returns a string representation for this tag. - */ - public function __toString() : string - { - if ($this->description) { - $description = $this->description->render(); - } else { - $description = ''; - } - if ($this->variableName) { - $variableName = '$' . $this->variableName; - } else { - $variableName = ''; - } - $type = (string) $this->type; - return $type . ($variableName !== '' ? ($type !== '' ? ' ' : '') . $variableName : '') . ($description !== '' ? ($type !== '' || $variableName !== '' ? ' ' : '') . $description : ''); - } + public function notify(\PHPUnit\Event\Test\ComparatorRegistered $event): void; } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. - * - * @link http://phpdoc.org */ -namespace PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\Tags; +namespace PHPUnit\Event\Test; -use PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\Description; -use PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\DescriptionFactory; -use PHPUnitPHAR\phpDocumentor\Reflection\Types\Context as TypeContext; -use PHPUnitPHAR\Webmozart\Assert\Assert; -use function preg_match; +use function sprintf; +use PHPUnit\Event\Code; +use PHPUnit\Event\Event; +use PHPUnit\Event\Telemetry; /** - * Reflection class for a {@}version tag in a Docblock. + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -final class Version extends BaseTag implements Factory\StaticMethod +final class AfterLastTestMethodCalled implements Event { - /** @var string */ - protected $name = 'version'; - /** - * PCRE regular expression matching a version vector. - * Assumes the "x" modifier. - */ - public const REGEX_VECTOR = '(?: - # Normal release vectors. - \\d\\S* - | - # VCS version vectors. Per PHPCS, they are expected to - # follow the form of the VCS name, followed by ":", followed - # by the version vector itself. - # By convention, popular VCSes like CVS, SVN and GIT use "$" - # around the actual version vector. - [^\\s\\:]+\\:\\s*\\$[^\\$]+\\$ - )'; - /** @var string|null The version vector. */ - private $version; - public function __construct(?string $version = null, ?Description $description = null) + private readonly Telemetry\Info $telemetryInfo; + /** + * @psalm-var class-string + */ + private readonly string $testClassName; + private readonly Code\ClassMethod $calledMethod; + /** + * @psalm-param class-string $testClassName + */ + public function __construct(Telemetry\Info $telemetryInfo, string $testClassName, Code\ClassMethod $calledMethod) { - Assert::nullOrStringNotEmpty($version); - $this->version = $version; - $this->description = $description; + $this->telemetryInfo = $telemetryInfo; + $this->testClassName = $testClassName; + $this->calledMethod = $calledMethod; } - public static function create(?string $body, ?DescriptionFactory $descriptionFactory = null, ?TypeContext $context = null) : ?self + public function telemetryInfo(): Telemetry\Info { - if (empty($body)) { - return new static(); - } - $matches = []; - if (!preg_match('/^(' . self::REGEX_VECTOR . ')\\s*(.+)?$/sux', $body, $matches)) { - return null; - } - $description = null; - if ($descriptionFactory !== null) { - $description = $descriptionFactory->create($matches[2] ?? '', $context); - } - return new static($matches[1], $description); + return $this->telemetryInfo; } /** - * Gets the version section of the tag. + * @psalm-return class-string */ - public function getVersion() : ?string + public function testClassName(): string { - return $this->version; + return $this->testClassName; } - /** - * Returns a string representation for this tag. - */ - public function __toString() : string + public function calledMethod(): Code\ClassMethod { - if ($this->description) { - $description = $this->description->render(); - } else { - $description = ''; - } - $version = (string) $this->version; - return $version . ($description !== '' ? ($version !== '' ? ' ' : '') . $description : ''); + return $this->calledMethod; + } + public function asString(): string + { + return sprintf('After Last Test Method Called (%s::%s)', $this->calledMethod->className(), $this->calledMethod->methodName()); } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. + */ +namespace PHPUnit\Event\Test; + +use PHPUnit\Event\Subscriber; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +interface AfterLastTestMethodCalledSubscriber extends Subscriber +{ + public function notify(\PHPUnit\Event\Test\AfterLastTestMethodCalled $event): void; +} + * - * @link http://phpdoc.org + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. */ -namespace PHPUnitPHAR\phpDocumentor\Reflection; +namespace PHPUnit\Event\Test; -use InvalidArgumentException; -use LogicException; -use PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\DescriptionFactory; -use PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\StandardTagFactory; -use PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\Tag; -use PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\TagFactory; -use PHPUnitPHAR\Webmozart\Assert\Assert; -use function array_shift; -use function count; -use function explode; -use function is_object; -use function method_exists; -use function preg_match; -use function preg_replace; -use function str_replace; -use function strpos; -use function substr; -use function trim; -final class DocBlockFactory implements DocBlockFactoryInterface +use const PHP_EOL; +use function sprintf; +use PHPUnit\Event\Code; +use PHPUnit\Event\Event; +use PHPUnit\Event\Telemetry; +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class AfterLastTestMethodFinished implements Event { - /** @var DocBlock\DescriptionFactory */ - private $descriptionFactory; - /** @var DocBlock\TagFactory */ - private $tagFactory; - /** - * Initializes this factory with the required subcontractors. - */ - public function __construct(DescriptionFactory $descriptionFactory, TagFactory $tagFactory) - { - $this->descriptionFactory = $descriptionFactory; - $this->tagFactory = $tagFactory; - } - /** - * Factory method for easy instantiation. - * - * @param array> $additionalTags - */ - public static function createInstance(array $additionalTags = []) : self - { - $fqsenResolver = new FqsenResolver(); - $tagFactory = new StandardTagFactory($fqsenResolver); - $descriptionFactory = new DescriptionFactory($tagFactory); - $tagFactory->addService($descriptionFactory); - $tagFactory->addService(new TypeResolver($fqsenResolver)); - $docBlockFactory = new self($descriptionFactory, $tagFactory); - foreach ($additionalTags as $tagName => $tagHandler) { - $docBlockFactory->registerTagHandler($tagName, $tagHandler); - } - return $docBlockFactory; - } + private readonly Telemetry\Info $telemetryInfo; /** - * @param object|string $docblock A string containing the DocBlock to parse or an object supporting the - * getDocComment method (such as a ReflectionClass object). + * @psalm-var class-string */ - public function create($docblock, ?Types\Context $context = null, ?Location $location = null) : DocBlock - { - if (is_object($docblock)) { - if (!method_exists($docblock, 'getDocComment')) { - $exceptionMessage = 'Invalid object passed; the given object must support the getDocComment method'; - throw new InvalidArgumentException($exceptionMessage); - } - $docblock = $docblock->getDocComment(); - Assert::string($docblock); - } - Assert::stringNotEmpty($docblock); - if ($context === null) { - $context = new Types\Context(''); - } - $parts = $this->splitDocBlock($this->stripDocComment($docblock)); - [$templateMarker, $summary, $description, $tags] = $parts; - return new DocBlock($summary, $description ? $this->descriptionFactory->create($description, $context) : null, $this->parseTagBlock($tags, $context), $context, $location, $templateMarker === '#@+', $templateMarker === '#@-'); - } + private readonly string $testClassName; /** - * @param class-string $handler + * @psalm-var list */ - public function registerTagHandler(string $tagName, string $handler) : void - { - $this->tagFactory->registerTagHandler($tagName, $handler); - } + private readonly array $calledMethods; /** - * Strips the asterisks from the DocBlock comment. - * - * @param string $comment String containing the comment text. + * @psalm-param class-string $testClassName */ - private function stripDocComment(string $comment) : string + public function __construct(Telemetry\Info $telemetryInfo, string $testClassName, Code\ClassMethod ...$calledMethods) { - $comment = preg_replace('#[ \\t]*(?:\\/\\*\\*|\\*\\/|\\*)?[ \\t]?(.*)?#u', '$1', $comment); - Assert::string($comment); - $comment = trim($comment); - // reg ex above is not able to remove */ from a single line docblock - if (substr($comment, -2) === '*/') { - $comment = trim(substr($comment, 0, -2)); - } - return str_replace(["\r\n", "\r"], "\n", $comment); + $this->telemetryInfo = $telemetryInfo; + $this->testClassName = $testClassName; + $this->calledMethods = $calledMethods; } - // phpcs:disable - /** - * Splits the DocBlock into a template marker, summary, description and block of tags. - * - * @param string $comment Comment to split into the sub-parts. - * - * @return string[] containing the template marker (if any), summary, description and a string containing the tags. - * - * @author Mike van Riel for extending the regex with template marker support. - * - * @author Richard van Velzen (@_richardJ) Special thanks to Richard for the regex responsible for the split. - */ - private function splitDocBlock(string $comment) : array + public function telemetryInfo(): Telemetry\Info { - // phpcs:enable - // Performance improvement cheat: if the first character is an @ then only tags are in this DocBlock. This - // method does not split tags so we return this verbatim as the fourth result (tags). This saves us the - // performance impact of running a regular expression - if (strpos($comment, '@') === 0) { - return ['', '', '', $comment]; - } - // clears all extra horizontal whitespace from the line endings to prevent parsing issues - $comment = preg_replace('/\\h*$/Sum', '', $comment); - Assert::string($comment); - /* - * Splits the docblock into a template marker, summary, description and tags section. - * - * - The template marker is empty, #@+ or #@- if the DocBlock starts with either of those (a newline may - * occur after it and will be stripped). - * - The short description is started from the first character until a dot is encountered followed by a - * newline OR two consecutive newlines (horizontal whitespace is taken into account to consider spacing - * errors). This is optional. - * - The long description, any character until a new line is encountered followed by an @ and word - * characters (a tag). This is optional. - * - Tags; the remaining characters - * - * Big thanks to RichardJ for contributing this Regular Expression - */ - preg_match('/ - \\A - # 1. Extract the template marker - (?:(\\#\\@\\+|\\#\\@\\-)\\n?)? - - # 2. Extract the summary - (?: - (?! @\\pL ) # The summary may not start with an @ - ( - [^\\n.]+ - (?: - (?! \\. \\n | \\n{2} ) # End summary upon a dot followed by newline or two newlines - [\\n.]* (?! [ \\t]* @\\pL ) # End summary when an @ is found as first character on a new line - [^\\n.]+ # Include anything else - )* - \\.? - )? - ) - - # 3. Extract the description - (?: - \\s* # Some form of whitespace _must_ precede a description because a summary must be there - (?! @\\pL ) # The description may not start with an @ - ( - [^\\n]+ - (?: \\n+ - (?! [ \\t]* @\\pL ) # End description when an @ is found as first character on a new line - [^\\n]+ # Include anything else - )* - ) - )? - - # 4. Extract the tags (anything that follows) - (\\s+ [\\s\\S]*)? # everything that follows - /ux', $comment, $matches); - array_shift($matches); - while (count($matches) < 4) { - $matches[] = ''; - } - return $matches; + return $this->telemetryInfo; } /** - * Creates the tag objects. - * - * @param string $tags Tag block to parse. - * @param Types\Context $context Context of the parsed Tag - * - * @return DocBlock\Tag[] + * @psalm-return class-string */ - private function parseTagBlock(string $tags, Types\Context $context) : array + public function testClassName(): string { - $tags = $this->filterTagBlock($tags); - if ($tags === null) { - return []; - } - $result = []; - $lines = $this->splitTagBlockIntoTagLines($tags); - foreach ($lines as $key => $tagLine) { - $result[$key] = $this->tagFactory->create(trim($tagLine), $context); - } - return $result; + return $this->testClassName; } /** - * @return string[] + * @psalm-return list */ - private function splitTagBlockIntoTagLines(string $tags) : array + public function calledMethods(): array { - $result = []; - foreach (explode("\n", $tags) as $tagLine) { - if ($tagLine !== '' && strpos($tagLine, '@') === 0) { - $result[] = $tagLine; - } else { - $result[count($result) - 1] .= "\n" . $tagLine; - } - } - return $result; + return $this->calledMethods; } - private function filterTagBlock(string $tags) : ?string + public function asString(): string { - $tags = trim($tags); - if (!$tags) { - return null; + $buffer = 'After Last Test Method Finished:'; + foreach ($this->calledMethods as $calledMethod) { + $buffer .= sprintf(PHP_EOL . '- %s::%s', $calledMethod->className(), $calledMethod->methodName()); } - if ($tags[0] !== '@') { - // @codeCoverageIgnoreStart - // Can't simulate this; this only happens if there is an error with the parsing of the DocBlock that - // we didn't foresee. - throw new LogicException('A tag block started with text instead of an at-sign(@): ' . $tags); - // @codeCoverageIgnoreEnd - } - return $tags; - } -} -> $additionalTags - */ - public static function createInstance(array $additionalTags = []) : DocBlockFactory; - /** - * @param string|object $docblock - */ - public function create($docblock, ?Types\Context $context = null, ?Location $location = null) : DocBlock; -} - * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. - * - * @link http://phpdoc.org */ -namespace PHPUnitPHAR\phpDocumentor\Reflection; +namespace PHPUnit\Event\Test; -use PHPUnitPHAR\phpDocumentor\Reflection\Exception\PcreException; -use PHPUnitPHAR\Webmozart\Assert\Assert; -use function preg_last_error; -use function preg_split as php_preg_split; -abstract class Utils +use PHPUnit\Event\Subscriber; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +interface AfterLastTestMethodFinishedSubscriber extends Subscriber { - /** - * Wrapper function for phps preg_split - * - * This function is inspired by {@link https://github.com/thecodingmachine/safe/blob/master/generated/pcre.php}. But - * since this library is all about performance we decided to strip everything we don't need. Reducing the amount - * of files that have to be loaded, ect. - * - * @param string $pattern The pattern to search for, as a string. - * @param string $subject The input string. - * @param int $limit If specified, then only substrings up to limit are returned with the - * rest of the string being placed in the last substring. A limit of -1 or 0 means "no limit". - * @param int $flags flags can be any combination of the following flags (combined with the | bitwise operator): - * *PREG_SPLIT_NO_EMPTY* - * If this flag is set, only non-empty pieces will be returned by preg_split(). - * *PREG_SPLIT_DELIM_CAPTURE* - * If this flag is set, parenthesized expression in the delimiter pattern will be captured - * and returned as well. - * *PREG_SPLIT_OFFSET_CAPTURE* - * If this flag is set, for every occurring match the appendant string offset will also be returned. - * Note that this changes the return value in an array where every element is an array consisting of the - * matched string at offset 0 and its string offset into subject at offset 1. - * - * @return string[] Returns an array containing substrings of subject - * split along boundaries matched by pattern - * - * @throws PcreException - */ - public static function pregSplit(string $pattern, string $subject, int $limit = -1, int $flags = 0) : array - { - $parts = php_preg_split($pattern, $subject, $limit, $flags); - if ($parts === \false) { - throw PcreException::createFromPhpError(preg_last_error()); - } - Assert::allString($parts); - return $parts; - } + public function notify(\PHPUnit\Event\Test\AfterLastTestMethodFinished $event): void; } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. - * - * @link http://phpdoc.org */ -namespace PHPUnitPHAR\phpDocumentor\Reflection; +namespace PHPUnit\Event\Test; -use InvalidArgumentException; -use PHPUnitPHAR\phpDocumentor\Reflection\Types\Context; -use function explode; -use function implode; -use function strpos; +use function sprintf; +use PHPUnit\Event\Code; +use PHPUnit\Event\Event; +use PHPUnit\Event\Telemetry; /** - * Resolver for Fqsen using Context information - * * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -class FqsenResolver +final class AfterTestMethodCalled implements Event { - /** @var string Definition of the NAMESPACE operator in PHP */ - private const OPERATOR_NAMESPACE = '\\'; - public function resolve(string $fqsen, ?Context $context = null) : Fqsen - { - if ($context === null) { - $context = new Context(''); - } - if ($this->isFqsen($fqsen)) { - return new Fqsen($fqsen); - } - return $this->resolvePartialStructuralElementName($fqsen, $context); - } + private readonly Telemetry\Info $telemetryInfo; + /** + * @psalm-var class-string + */ + private readonly string $testClassName; + private readonly Code\ClassMethod $calledMethod; /** - * Tests whether the given type is a Fully Qualified Structural Element Name. + * @psalm-param class-string $testClassName */ - private function isFqsen(string $type) : bool + public function __construct(Telemetry\Info $telemetryInfo, string $testClassName, Code\ClassMethod $calledMethod) + { + $this->telemetryInfo = $telemetryInfo; + $this->testClassName = $testClassName; + $this->calledMethod = $calledMethod; + } + public function telemetryInfo(): Telemetry\Info { - return strpos($type, self::OPERATOR_NAMESPACE) === 0; + return $this->telemetryInfo; } /** - * Resolves a partial Structural Element Name (i.e. `Reflection\DocBlock`) to its FQSEN representation - * (i.e. `\phpDocumentor\Reflection\DocBlock`) based on the Namespace and aliases mentioned in the Context. - * - * @throws InvalidArgumentException When type is not a valid FQSEN. + * @psalm-return class-string */ - private function resolvePartialStructuralElementName(string $type, Context $context) : Fqsen + public function testClassName(): string { - $typeParts = explode(self::OPERATOR_NAMESPACE, $type, 2); - $namespaceAliases = $context->getNamespaceAliases(); - // if the first segment is not an alias; prepend namespace name and return - if (!isset($namespaceAliases[$typeParts[0]])) { - $namespace = $context->getNamespace(); - if ($namespace !== '') { - $namespace .= self::OPERATOR_NAMESPACE; - } - return new Fqsen(self::OPERATOR_NAMESPACE . $namespace . $type); - } - $typeParts[0] = $namespaceAliases[$typeParts[0]]; - return new Fqsen(self::OPERATOR_NAMESPACE . implode(self::OPERATOR_NAMESPACE, $typeParts)); + return $this->testClassName; + } + public function calledMethod(): Code\ClassMethod + { + return $this->calledMethod; + } + public function asString(): string + { + return sprintf('After Test Method Called (%s::%s)', $this->calledMethod->className(), $this->calledMethod->methodName()); } } -The MIT License (MIT) - -Copyright (c) 2010 Mike van Riel - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. - * - * @link http://phpdoc.org */ -namespace PHPUnitPHAR\phpDocumentor\Reflection; - -interface PseudoType extends Type -{ - public function underlyingType() : Type; -} -items = $items; - } - /** - * @return ArrayShapeItem[] - */ - public function getItems() : array - { - return $this->items; - } - public function underlyingType() : Type - { - return new Array_(new Mixed_(), new ArrayKey()); - } - public function __toString() : string - { - return 'array{' . implode(', ', $this->items) . '}'; - } + public function notify(\PHPUnit\Event\Test\AfterTestMethodCalled $event): void; } * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. */ -declare (strict_types=1); -namespace PHPUnitPHAR\phpDocumentor\Reflection\PseudoTypes; +namespace PHPUnit\Event\Test; -use PHPUnitPHAR\phpDocumentor\Reflection\Type; -use PHPUnitPHAR\phpDocumentor\Reflection\Types\Mixed_; +use const PHP_EOL; use function sprintf; -final class ArrayShapeItem +use PHPUnit\Event\Code; +use PHPUnit\Event\Event; +use PHPUnit\Event\Telemetry; +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class AfterTestMethodFinished implements Event { - /** @var string|null */ - private $key; - /** @var Type */ - private $value; - /** @var bool */ - private $optional; - public function __construct(?string $key, ?Type $value, bool $optional) + private readonly Telemetry\Info $telemetryInfo; + /** + * @psalm-var class-string + */ + private readonly string $testClassName; + /** + * @psalm-var list + */ + private readonly array $calledMethods; + /** + * @psalm-param class-string $testClassName + */ + public function __construct(Telemetry\Info $telemetryInfo, string $testClassName, Code\ClassMethod ...$calledMethods) { - $this->key = $key; - $this->value = $value ?? new Mixed_(); - $this->optional = $optional; + $this->telemetryInfo = $telemetryInfo; + $this->testClassName = $testClassName; + $this->calledMethods = $calledMethods; } - public function getKey() : ?string + public function telemetryInfo(): Telemetry\Info { - return $this->key; + return $this->telemetryInfo; } - public function getValue() : Type + /** + * @psalm-return class-string + */ + public function testClassName(): string { - return $this->value; + return $this->testClassName; } - public function isOptional() : bool + /** + * @psalm-return list + */ + public function calledMethods(): array { - return $this->optional; + return $this->calledMethods; } - public function __toString() : string + public function asString(): string { - if ($this->key !== null) { - return sprintf('%s%s: %s', $this->key, $this->optional ? '?' : '', (string) $this->value); + $buffer = 'After Test Method Finished:'; + foreach ($this->calledMethods as $calledMethod) { + $buffer .= sprintf(PHP_EOL . '- %s::%s', $calledMethod->className(), $calledMethod->methodName()); } - return (string) $this->value; + return $buffer; } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. - * - * @link http://phpdoc.org */ -namespace PHPUnitPHAR\phpDocumentor\Reflection\PseudoTypes; +namespace PHPUnit\Event\Test; -use PHPUnitPHAR\phpDocumentor\Reflection\PseudoType; -use PHPUnitPHAR\phpDocumentor\Reflection\Type; -use PHPUnitPHAR\phpDocumentor\Reflection\Types\String_; +use PHPUnit\Event\Subscriber; /** - * Value Object representing the type 'string'. - * - * @psalm-immutable + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -final class CallableString extends String_ implements PseudoType +interface AfterTestMethodFinishedSubscriber extends Subscriber { - public function underlyingType() : Type - { - return new String_(); - } - /** - * Returns a rendered output of the Type as it would be used in a DocBlock. - */ - public function __toString() : string - { - return 'callable-string'; - } + public function notify(\PHPUnit\Event\Test\AfterTestMethodFinished $event): void; } * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. */ -declare (strict_types=1); -namespace PHPUnitPHAR\phpDocumentor\Reflection\PseudoTypes; +namespace PHPUnit\Event\Test; -use PHPUnitPHAR\phpDocumentor\Reflection\PseudoType; -use PHPUnitPHAR\phpDocumentor\Reflection\Type; -use PHPUnitPHAR\phpDocumentor\Reflection\Types\Mixed_; use function sprintf; -/** @psalm-immutable */ -final class ConstExpression implements PseudoType +use PHPUnit\Event\Code; +use PHPUnit\Event\Event; +use PHPUnit\Event\Telemetry; +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class BeforeFirstTestMethodCalled implements Event { - /** @var Type */ - private $owner; - /** @var string */ - private $expression; - public function __construct(Type $owner, string $expression) + private readonly Telemetry\Info $telemetryInfo; + /** + * @psalm-var class-string + */ + private readonly string $testClassName; + private readonly Code\ClassMethod $calledMethod; + /** + * @psalm-param class-string $testClassName + */ + public function __construct(Telemetry\Info $telemetryInfo, string $testClassName, Code\ClassMethod $calledMethod) { - $this->owner = $owner; - $this->expression = $expression; + $this->telemetryInfo = $telemetryInfo; + $this->testClassName = $testClassName; + $this->calledMethod = $calledMethod; } - public function getOwner() : Type + public function telemetryInfo(): Telemetry\Info { - return $this->owner; + return $this->telemetryInfo; } - public function getExpression() : string + /** + * @psalm-return class-string + */ + public function testClassName(): string { - return $this->expression; + return $this->testClassName; } - public function underlyingType() : Type + public function calledMethod(): Code\ClassMethod { - return new Mixed_(); + return $this->calledMethod; } - public function __toString() : string + public function asString(): string { - return sprintf('%s::%s', (string) $this->owner, $this->expression); + return sprintf('Before First Test Method Called (%s::%s)', $this->calledMethod->className(), $this->calledMethod->methodName()); } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. - * - * @link https://phpdoc.org */ -namespace PHPUnitPHAR\phpDocumentor\Reflection\PseudoTypes; +namespace PHPUnit\Event\Test; -use PHPUnitPHAR\phpDocumentor\Reflection\PseudoType; -use PHPUnitPHAR\phpDocumentor\Reflection\Type; -use PHPUnitPHAR\phpDocumentor\Reflection\Types\Boolean; -use function class_alias; +use PHPUnit\Event\Subscriber; /** - * Value Object representing the PseudoType 'False', which is a Boolean type. - * - * @psalm-immutable + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -final class False_ extends Boolean implements PseudoType +interface BeforeFirstTestMethodCalledSubscriber extends Subscriber { - public function underlyingType() : Type - { - return new Boolean(); - } - public function __toString() : string - { - return 'false'; - } + public function notify(\PHPUnit\Event\Test\BeforeFirstTestMethodCalled $event): void; } -class_alias(False_::class, 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\False_', \false); * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. */ -declare (strict_types=1); -namespace PHPUnitPHAR\phpDocumentor\Reflection\PseudoTypes; +namespace PHPUnit\Event\Test; -use PHPUnitPHAR\phpDocumentor\Reflection\PseudoType; -use PHPUnitPHAR\phpDocumentor\Reflection\Type; -use PHPUnitPHAR\phpDocumentor\Reflection\Types\Float_; -/** @psalm-immutable */ -class FloatValue implements PseudoType +use function sprintf; +use PHPUnit\Event\Code; +use PHPUnit\Event\Code\Throwable; +use PHPUnit\Event\Event; +use PHPUnit\Event\Telemetry; +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class BeforeFirstTestMethodErrored implements Event { - /** @var float */ - private $value; - public function __construct(float $value) + private readonly Telemetry\Info $telemetryInfo; + /** + * @psalm-var class-string + */ + private readonly string $testClassName; + private readonly Code\ClassMethod $calledMethod; + private readonly Throwable $throwable; + /** + * @psalm-param class-string $testClassName + */ + public function __construct(Telemetry\Info $telemetryInfo, string $testClassName, Code\ClassMethod $calledMethod, Throwable $throwable) { - $this->value = $value; + $this->telemetryInfo = $telemetryInfo; + $this->testClassName = $testClassName; + $this->calledMethod = $calledMethod; + $this->throwable = $throwable; } - public function getValue() : float + public function telemetryInfo(): Telemetry\Info { - return $this->value; + return $this->telemetryInfo; + } + /** + * @psalm-return class-string + */ + public function testClassName(): string + { + return $this->testClassName; + } + public function calledMethod(): Code\ClassMethod + { + return $this->calledMethod; } - public function underlyingType() : Type + public function throwable(): Throwable { - return new Float_(); + return $this->throwable; } - public function __toString() : string + public function asString(): string { - return (string) $this->value; + $message = $this->throwable->message(); + if (!empty($message)) { + $message = \PHP_EOL . $message; + } + return sprintf('Before First Test Method Errored (%s::%s)%s', $this->calledMethod->className(), $this->calledMethod->methodName(), $message); } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. - * - * @link http://phpdoc.org */ -namespace PHPUnitPHAR\phpDocumentor\Reflection\PseudoTypes; +namespace PHPUnit\Event\Test; -use PHPUnitPHAR\phpDocumentor\Reflection\PseudoType; -use PHPUnitPHAR\phpDocumentor\Reflection\Type; -use PHPUnitPHAR\phpDocumentor\Reflection\Types\String_; +use PHPUnit\Event\Subscriber; /** - * Value Object representing the type 'string'. - * - * @psalm-immutable + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -final class HtmlEscapedString extends String_ implements PseudoType +interface BeforeFirstTestMethodErroredSubscriber extends Subscriber { - public function underlyingType() : Type - { - return new String_(); - } - /** - * Returns a rendered output of the Type as it would be used in a DocBlock. - */ - public function __toString() : string - { - return 'html-escaped-string'; - } + public function notify(\PHPUnit\Event\Test\BeforeFirstTestMethodErrored $event): void; } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. - * - * @link http://phpdoc.org */ -namespace PHPUnitPHAR\phpDocumentor\Reflection\PseudoTypes; +namespace PHPUnit\Event\Test; -use PHPUnitPHAR\phpDocumentor\Reflection\PseudoType; -use PHPUnitPHAR\phpDocumentor\Reflection\Type; -use PHPUnitPHAR\phpDocumentor\Reflection\Types\Integer; +use const PHP_EOL; +use function sprintf; +use PHPUnit\Event\Code; +use PHPUnit\Event\Event; +use PHPUnit\Event\Telemetry; /** - * Value Object representing the type 'int'. - * * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -final class IntegerRange extends Integer implements PseudoType +final class BeforeFirstTestMethodFinished implements Event { - /** @var string */ - private $minValue; - /** @var string */ - private $maxValue; - public function __construct(string $minValue, string $maxValue) - { - $this->minValue = $minValue; - $this->maxValue = $maxValue; - } - public function underlyingType() : Type + private readonly Telemetry\Info $telemetryInfo; + /** + * @psalm-var class-string + */ + private readonly string $testClassName; + /** + * @psalm-var list + */ + private readonly array $calledMethods; + /** + * @psalm-param class-string $testClassName + */ + public function __construct(Telemetry\Info $telemetryInfo, string $testClassName, Code\ClassMethod ...$calledMethods) { - return new Integer(); + $this->telemetryInfo = $telemetryInfo; + $this->testClassName = $testClassName; + $this->calledMethods = $calledMethods; } - public function getMinValue() : string + public function telemetryInfo(): Telemetry\Info { - return $this->minValue; + return $this->telemetryInfo; } - public function getMaxValue() : string + /** + * @psalm-return class-string + */ + public function testClassName(): string { - return $this->maxValue; + return $this->testClassName; } /** - * Returns a rendered output of the Type as it would be used in a DocBlock. + * @psalm-return list */ - public function __toString() : string + public function calledMethods(): array { - return 'int<' . $this->minValue . ', ' . $this->maxValue . '>'; + return $this->calledMethods; + } + public function asString(): string + { + $buffer = 'Before First Test Method Finished:'; + foreach ($this->calledMethods as $calledMethod) { + $buffer .= sprintf(PHP_EOL . '- %s::%s', $calledMethod->className(), $calledMethod->methodName()); + } + return $buffer; } } * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. */ -declare (strict_types=1); -namespace PHPUnitPHAR\phpDocumentor\Reflection\PseudoTypes; +namespace PHPUnit\Event\Test; -use PHPUnitPHAR\phpDocumentor\Reflection\PseudoType; -use PHPUnitPHAR\phpDocumentor\Reflection\Type; -use PHPUnitPHAR\phpDocumentor\Reflection\Types\Integer; -/** @psalm-immutable */ -final class IntegerValue implements PseudoType +use PHPUnit\Event\Subscriber; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +interface BeforeFirstTestMethodFinishedSubscriber extends Subscriber { - /** @var int */ - private $value; - public function __construct(int $value) - { - $this->value = $value; - } - public function getValue() : int - { - return $this->value; - } - public function underlyingType() : Type - { - return new Integer(); - } - public function __toString() : string - { - return (string) $this->value; - } + public function notify(\PHPUnit\Event\Test\BeforeFirstTestMethodFinished $event): void; } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. - * - * @link http://phpdoc.org */ -namespace PHPUnitPHAR\phpDocumentor\Reflection\PseudoTypes; +namespace PHPUnit\Event\Test; -use PHPUnitPHAR\phpDocumentor\Reflection\PseudoType; -use PHPUnitPHAR\phpDocumentor\Reflection\Type; -use PHPUnitPHAR\phpDocumentor\Reflection\Types\Array_; -use PHPUnitPHAR\phpDocumentor\Reflection\Types\Integer; -use PHPUnitPHAR\phpDocumentor\Reflection\Types\Mixed_; +use function sprintf; +use PHPUnit\Event\Code; +use PHPUnit\Event\Event; +use PHPUnit\Event\Telemetry; /** - * Value Object representing the type 'list'. - * * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -final class List_ extends Array_ implements PseudoType +final class BeforeTestMethodCalled implements Event { - public function underlyingType() : Type + private readonly Telemetry\Info $telemetryInfo; + /** + * @psalm-var class-string + */ + private readonly string $testClassName; + private readonly Code\ClassMethod $calledMethod; + /** + * @psalm-param class-string $testClassName + */ + public function __construct(Telemetry\Info $telemetryInfo, string $testClassName, Code\ClassMethod $calledMethod) { - return new Array_(); + $this->telemetryInfo = $telemetryInfo; + $this->testClassName = $testClassName; + $this->calledMethod = $calledMethod; } - public function __construct(?Type $valueType = null) + public function telemetryInfo(): Telemetry\Info { - parent::__construct($valueType, new Integer()); + return $this->telemetryInfo; } /** - * Returns a rendered output of the Type as it would be used in a DocBlock. + * @psalm-return class-string */ - public function __toString() : string + public function testClassName(): string { - if ($this->valueType instanceof Mixed_) { - return 'list'; - } - return 'list<' . $this->valueType . '>'; + return $this->testClassName; + } + public function calledMethod(): Code\ClassMethod + { + return $this->calledMethod; + } + public function asString(): string + { + return sprintf('Before Test Method Called (%s::%s)', $this->calledMethod->className(), $this->calledMethod->methodName()); } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. - * - * @link http://phpdoc.org */ -namespace PHPUnitPHAR\phpDocumentor\Reflection\PseudoTypes; +namespace PHPUnit\Event\Test; -use PHPUnitPHAR\phpDocumentor\Reflection\PseudoType; -use PHPUnitPHAR\phpDocumentor\Reflection\Type; -use PHPUnitPHAR\phpDocumentor\Reflection\Types\String_; +use PHPUnit\Event\Subscriber; /** - * Value Object representing the type 'string'. - * - * @psalm-immutable + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -final class LiteralString extends String_ implements PseudoType +interface BeforeTestMethodCalledSubscriber extends Subscriber { - public function underlyingType() : Type - { - return new String_(); - } - /** - * Returns a rendered output of the Type as it would be used in a DocBlock. - */ - public function __toString() : string - { - return 'literal-string'; - } + public function notify(\PHPUnit\Event\Test\BeforeTestMethodCalled $event): void; } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. - * - * @link http://phpdoc.org */ -namespace PHPUnitPHAR\phpDocumentor\Reflection\PseudoTypes; +namespace PHPUnit\Event\Test; -use PHPUnitPHAR\phpDocumentor\Reflection\PseudoType; -use PHPUnitPHAR\phpDocumentor\Reflection\Type; -use PHPUnitPHAR\phpDocumentor\Reflection\Types\String_; +use const PHP_EOL; +use function sprintf; +use PHPUnit\Event\Code; +use PHPUnit\Event\Event; +use PHPUnit\Event\Telemetry; /** - * Value Object representing the type 'string'. - * * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -final class LowercaseString extends String_ implements PseudoType +final class BeforeTestMethodFinished implements Event { - public function underlyingType() : Type + private readonly Telemetry\Info $telemetryInfo; + /** + * @psalm-var class-string + */ + private readonly string $testClassName; + /** + * @psalm-var list + */ + private readonly array $calledMethods; + /** + * @psalm-param class-string $testClassName + */ + public function __construct(Telemetry\Info $telemetryInfo, string $testClassName, Code\ClassMethod ...$calledMethods) { - return new String_(); + $this->telemetryInfo = $telemetryInfo; + $this->testClassName = $testClassName; + $this->calledMethods = $calledMethods; + } + public function telemetryInfo(): Telemetry\Info + { + return $this->telemetryInfo; } /** - * Returns a rendered output of the Type as it would be used in a DocBlock. + * @psalm-return class-string */ - public function __toString() : string + public function testClassName(): string { - return 'lowercase-string'; + return $this->testClassName; } -} + /** + * @psalm-return list + */ + public function calledMethods(): array + { + return $this->calledMethods; + } + public function asString(): string + { + $buffer = 'Before Test Method Finished:'; + foreach ($this->calledMethods as $calledMethod) { + $buffer .= sprintf(PHP_EOL . '- %s::%s', $calledMethod->className(), $calledMethod->methodName()); + } + return $buffer; + } +} * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. - * - * @link http://phpdoc.org */ -namespace PHPUnitPHAR\phpDocumentor\Reflection\PseudoTypes; +namespace PHPUnit\Event\Test; -use PHPUnitPHAR\phpDocumentor\Reflection\PseudoType; -use PHPUnitPHAR\phpDocumentor\Reflection\Type; -use PHPUnitPHAR\phpDocumentor\Reflection\Types\Integer; +use PHPUnit\Event\Subscriber; /** - * Value Object representing the type 'int'. - * - * @psalm-immutable + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -final class NegativeInteger extends Integer implements PseudoType +interface BeforeTestMethodFinishedSubscriber extends Subscriber { - public function underlyingType() : Type - { - return new Integer(); - } - /** - * Returns a rendered output of the Type as it would be used in a DocBlock. - */ - public function __toString() : string - { - return 'negative-int'; - } + public function notify(\PHPUnit\Event\Test\BeforeTestMethodFinished $event): void; } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. - * - * @link http://phpdoc.org */ -namespace PHPUnitPHAR\phpDocumentor\Reflection\PseudoTypes; +namespace PHPUnit\Event\Test; -use PHPUnitPHAR\phpDocumentor\Reflection\PseudoType; -use PHPUnitPHAR\phpDocumentor\Reflection\Type; -use PHPUnitPHAR\phpDocumentor\Reflection\Types\Array_; -use PHPUnitPHAR\phpDocumentor\Reflection\Types\Integer; -use PHPUnitPHAR\phpDocumentor\Reflection\Types\Mixed_; +use function sprintf; +use PHPUnit\Event\Code; +use PHPUnit\Event\Event; +use PHPUnit\Event\Telemetry; /** - * Value Object representing the type 'non-empty-list'. - * * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -final class NonEmptyList extends Array_ implements PseudoType +final class PostConditionCalled implements Event { - public function underlyingType() : Type + private readonly Telemetry\Info $telemetryInfo; + /** + * @psalm-var class-string + */ + private readonly string $testClassName; + private readonly Code\ClassMethod $calledMethod; + /** + * @psalm-param class-string $testClassName + */ + public function __construct(Telemetry\Info $telemetryInfo, string $testClassName, Code\ClassMethod $calledMethod) { - return new Array_(); + $this->telemetryInfo = $telemetryInfo; + $this->testClassName = $testClassName; + $this->calledMethod = $calledMethod; } - public function __construct(?Type $valueType = null) + public function telemetryInfo(): Telemetry\Info { - parent::__construct($valueType, new Integer()); + return $this->telemetryInfo; } /** - * Returns a rendered output of the Type as it would be used in a DocBlock. + * @psalm-return class-string */ - public function __toString() : string + public function testClassName(): string { - if ($this->valueType instanceof Mixed_) { - return 'non-empty-list'; - } - return 'non-empty-list<' . $this->valueType . '>'; + return $this->testClassName; + } + public function calledMethod(): Code\ClassMethod + { + return $this->calledMethod; + } + public function asString(): string + { + return sprintf('Post Condition Method Called (%s::%s)', $this->calledMethod->className(), $this->calledMethod->methodName()); } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. - * - * @link http://phpdoc.org */ -namespace PHPUnitPHAR\phpDocumentor\Reflection\PseudoTypes; +namespace PHPUnit\Event\Test; -use PHPUnitPHAR\phpDocumentor\Reflection\PseudoType; -use PHPUnitPHAR\phpDocumentor\Reflection\Type; -use PHPUnitPHAR\phpDocumentor\Reflection\Types\String_; +use PHPUnit\Event\Subscriber; /** - * Value Object representing the type 'string'. - * - * @psalm-immutable + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -final class NonEmptyLowercaseString extends String_ implements PseudoType +interface PostConditionCalledSubscriber extends Subscriber { - public function underlyingType() : Type - { - return new String_(); - } - /** - * Returns a rendered output of the Type as it would be used in a DocBlock. - */ - public function __toString() : string - { - return 'non-empty-lowercase-string'; - } + public function notify(\PHPUnit\Event\Test\PostConditionCalled $event): void; } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. - * - * @link http://phpdoc.org */ -namespace PHPUnitPHAR\phpDocumentor\Reflection\PseudoTypes; +namespace PHPUnit\Event\Test; -use PHPUnitPHAR\phpDocumentor\Reflection\PseudoType; -use PHPUnitPHAR\phpDocumentor\Reflection\Type; -use PHPUnitPHAR\phpDocumentor\Reflection\Types\String_; +use const PHP_EOL; +use function sprintf; +use PHPUnit\Event\Code; +use PHPUnit\Event\Event; +use PHPUnit\Event\Telemetry; /** - * Value Object representing the type 'string'. - * * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -final class NonEmptyString extends String_ implements PseudoType +final class PostConditionFinished implements Event { - public function underlyingType() : Type + private readonly Telemetry\Info $telemetryInfo; + /** + * @psalm-var class-string + */ + private readonly string $testClassName; + /** + * @psalm-var list + */ + private readonly array $calledMethods; + /** + * @psalm-param class-string $testClassName + */ + public function __construct(Telemetry\Info $telemetryInfo, string $testClassName, Code\ClassMethod ...$calledMethods) + { + $this->telemetryInfo = $telemetryInfo; + $this->testClassName = $testClassName; + $this->calledMethods = $calledMethods; + } + public function telemetryInfo(): Telemetry\Info { - return new String_(); + return $this->telemetryInfo; } /** - * Returns a rendered output of the Type as it would be used in a DocBlock. + * @psalm-return class-string + */ + public function testClassName(): string + { + return $this->testClassName; + } + /** + * @psalm-return list */ - public function __toString() : string + public function calledMethods(): array + { + return $this->calledMethods; + } + public function asString(): string { - return 'non-empty-string'; + $buffer = 'Post Condition Method Finished:'; + foreach ($this->calledMethods as $calledMethod) { + $buffer .= sprintf(PHP_EOL . '- %s::%s', $calledMethod->className(), $calledMethod->methodName()); + } + return $buffer; } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. - * - * @link http://phpdoc.org */ -namespace PHPUnitPHAR\phpDocumentor\Reflection\PseudoTypes; +namespace PHPUnit\Event\Test; -use PHPUnitPHAR\phpDocumentor\Reflection\PseudoType; -use PHPUnitPHAR\phpDocumentor\Reflection\Type; -use PHPUnitPHAR\phpDocumentor\Reflection\Types\String_; +use PHPUnit\Event\Subscriber; /** - * Value Object representing the type 'string'. - * - * @psalm-immutable + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -final class NumericString extends String_ implements PseudoType +interface PostConditionFinishedSubscriber extends Subscriber { - public function underlyingType() : Type - { - return new String_(); - } - /** - * Returns a rendered output of the Type as it would be used in a DocBlock. - */ - public function __toString() : string - { - return 'numeric-string'; - } + public function notify(\PHPUnit\Event\Test\PostConditionFinished $event): void; } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. - * - * @link http://phpdoc.org */ -namespace PHPUnitPHAR\phpDocumentor\Reflection\PseudoTypes; +namespace PHPUnit\Event\Test; -use PHPUnitPHAR\phpDocumentor\Reflection\PseudoType; -use PHPUnitPHAR\phpDocumentor\Reflection\Type; -use PHPUnitPHAR\phpDocumentor\Reflection\Types\AggregatedType; -use PHPUnitPHAR\phpDocumentor\Reflection\Types\Compound; -use PHPUnitPHAR\phpDocumentor\Reflection\Types\Float_; -use PHPUnitPHAR\phpDocumentor\Reflection\Types\Integer; +use function sprintf; +use PHPUnit\Event\Code; +use PHPUnit\Event\Event; +use PHPUnit\Event\Telemetry; /** - * Value Object representing the 'numeric' pseudo-type, which is either a numeric-string, integer or float. - * * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -final class Numeric_ extends AggregatedType implements PseudoType +final class PreConditionCalled implements Event { - public function __construct() + private readonly Telemetry\Info $telemetryInfo; + /** + * @psalm-var class-string + */ + private readonly string $testClassName; + private readonly Code\ClassMethod $calledMethod; + /** + * @psalm-param class-string $testClassName + */ + public function __construct(Telemetry\Info $telemetryInfo, string $testClassName, Code\ClassMethod $calledMethod) { - AggregatedType::__construct([new NumericString(), new Integer(), new Float_()], '|'); + $this->telemetryInfo = $telemetryInfo; + $this->testClassName = $testClassName; + $this->calledMethod = $calledMethod; } - public function underlyingType() : Type + public function telemetryInfo(): Telemetry\Info { - return new Compound([new NumericString(), new Integer(), new Float_()]); + return $this->telemetryInfo; } /** - * Returns a rendered output of the Type as it would be used in a DocBlock. + * @psalm-return class-string */ - public function __toString() : string + public function testClassName(): string + { + return $this->testClassName; + } + public function calledMethod(): Code\ClassMethod + { + return $this->calledMethod; + } + public function asString(): string { - return 'numeric'; + return sprintf('Pre Condition Method Called (%s::%s)', $this->calledMethod->className(), $this->calledMethod->methodName()); } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. - * - * @link http://phpdoc.org */ -namespace PHPUnitPHAR\phpDocumentor\Reflection\PseudoTypes; +namespace PHPUnit\Event\Test; -use PHPUnitPHAR\phpDocumentor\Reflection\PseudoType; -use PHPUnitPHAR\phpDocumentor\Reflection\Type; -use PHPUnitPHAR\phpDocumentor\Reflection\Types\Integer; +use PHPUnit\Event\Subscriber; /** - * Value Object representing the type 'int'. - * - * @psalm-immutable + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -final class PositiveInteger extends Integer implements PseudoType +interface PreConditionCalledSubscriber extends Subscriber { - public function underlyingType() : Type - { - return new Integer(); - } - /** - * Returns a rendered output of the Type as it would be used in a DocBlock. - */ - public function __toString() : string - { - return 'positive-int'; - } + public function notify(\PHPUnit\Event\Test\PreConditionCalled $event): void; } * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. */ -declare (strict_types=1); -namespace PHPUnitPHAR\phpDocumentor\Reflection\PseudoTypes; +namespace PHPUnit\Event\Test; -use PHPUnitPHAR\phpDocumentor\Reflection\PseudoType; -use PHPUnitPHAR\phpDocumentor\Reflection\Type; -use PHPUnitPHAR\phpDocumentor\Reflection\Types\String_; +use const PHP_EOL; use function sprintf; -/** @psalm-immutable */ -class StringValue implements PseudoType +use PHPUnit\Event\Code; +use PHPUnit\Event\Event; +use PHPUnit\Event\Telemetry; +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class PreConditionFinished implements Event { - /** @var string */ - private $value; - public function __construct(string $value) + private readonly Telemetry\Info $telemetryInfo; + /** + * @psalm-var class-string + */ + private readonly string $testClassName; + /** + * @psalm-var list + */ + private readonly array $calledMethods; + /** + * @psalm-param class-string $testClassName + */ + public function __construct(Telemetry\Info $telemetryInfo, string $testClassName, Code\ClassMethod ...$calledMethods) { - $this->value = $value; + $this->telemetryInfo = $telemetryInfo; + $this->testClassName = $testClassName; + $this->calledMethods = $calledMethods; } - public function getValue() : string + public function telemetryInfo(): Telemetry\Info { - return $this->value; + return $this->telemetryInfo; + } + /** + * @psalm-return class-string + */ + public function testClassName(): string + { + return $this->testClassName; } - public function underlyingType() : Type + /** + * @psalm-return list + */ + public function calledMethods(): array { - return new String_(); + return $this->calledMethods; } - public function __toString() : string + public function asString(): string { - return sprintf('"%s"', $this->value); + $buffer = 'Pre Condition Method Finished:'; + foreach ($this->calledMethods as $calledMethod) { + $buffer .= sprintf(PHP_EOL . '- %s::%s', $calledMethod->className(), $calledMethod->methodName()); + } + return $buffer; } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. - * - * @link http://phpdoc.org */ -namespace PHPUnitPHAR\phpDocumentor\Reflection\PseudoTypes; +namespace PHPUnit\Event\Test; -use PHPUnitPHAR\phpDocumentor\Reflection\PseudoType; -use PHPUnitPHAR\phpDocumentor\Reflection\Type; -use PHPUnitPHAR\phpDocumentor\Reflection\Types\String_; +use PHPUnit\Event\Subscriber; /** - * Value Object representing the type 'string'. - * - * @psalm-immutable + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -final class TraitString extends String_ implements PseudoType +interface PreConditionFinishedSubscriber extends Subscriber { - public function underlyingType() : Type - { - return new String_(); - } - /** - * Returns a rendered output of the Type as it would be used in a DocBlock. - */ - public function __toString() : string - { - return 'trait-string'; - } + public function notify(\PHPUnit\Event\Test\PreConditionFinished $event): void; } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. - * - * @link https://phpdoc.org */ -namespace PHPUnitPHAR\phpDocumentor\Reflection\PseudoTypes; +namespace PHPUnit\Event\Test; -use PHPUnitPHAR\phpDocumentor\Reflection\PseudoType; -use PHPUnitPHAR\phpDocumentor\Reflection\Type; -use PHPUnitPHAR\phpDocumentor\Reflection\Types\Boolean; -use function class_alias; +use const PHP_EOL; +use function sprintf; +use PHPUnit\Event\Code; +use PHPUnit\Event\Event; +use PHPUnit\Event\Telemetry; /** - * Value Object representing the PseudoType 'False', which is a Boolean type. - * * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -final class True_ extends Boolean implements PseudoType +final class ConsideredRisky implements Event { - public function underlyingType() : Type + private readonly Telemetry\Info $telemetryInfo; + private readonly Code\Test $test; + /** + * @psalm-var non-empty-string + */ + private readonly string $message; + /** + * @psalm-param non-empty-string $message + */ + public function __construct(Telemetry\Info $telemetryInfo, Code\Test $test, string $message) { - return new Boolean(); + $this->telemetryInfo = $telemetryInfo; + $this->test = $test; + $this->message = $message; } - public function __toString() : string + public function telemetryInfo(): Telemetry\Info { - return 'true'; + return $this->telemetryInfo; + } + public function test(): Code\Test + { + return $this->test; + } + /** + * @psalm-return non-empty-string + */ + public function message(): string + { + return $this->message; + } + public function asString(): string + { + return sprintf('Test Considered Risky (%s)%s%s', $this->test->id(), PHP_EOL, $this->message); } } -class_alias(True_::class, 'PHPUnitPHAR\\phpDocumentor\\Reflection\\Types\\True_', \false); * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. - * - * @link http://phpdoc.org */ -namespace PHPUnitPHAR\phpDocumentor\Reflection; +namespace PHPUnit\Event\Test; +use PHPUnit\Event\Subscriber; /** - * @psalm-immutable + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -interface Type +interface ConsideredRiskySubscriber extends Subscriber { - /** - * Returns a rendered output of the Type as it would be used in a DocBlock. - */ - public function __toString() : string; + public function notify(\PHPUnit\Event\Test\ConsideredRisky $event): void; } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. - * - * @link http://phpdoc.org */ -namespace PHPUnitPHAR\phpDocumentor\Reflection; +namespace PHPUnit\Event\Test; -use PHPUnitPHAR\Doctrine\Deprecations\Deprecation; -use InvalidArgumentException; -use PHPUnitPHAR\phpDocumentor\Reflection\PseudoTypes\ArrayShape; -use PHPUnitPHAR\phpDocumentor\Reflection\PseudoTypes\ArrayShapeItem; -use PHPUnitPHAR\phpDocumentor\Reflection\PseudoTypes\CallableString; -use PHPUnitPHAR\phpDocumentor\Reflection\PseudoTypes\ConstExpression; -use PHPUnitPHAR\phpDocumentor\Reflection\PseudoTypes\False_; -use PHPUnitPHAR\phpDocumentor\Reflection\PseudoTypes\FloatValue; -use PHPUnitPHAR\phpDocumentor\Reflection\PseudoTypes\HtmlEscapedString; -use PHPUnitPHAR\phpDocumentor\Reflection\PseudoTypes\IntegerRange; -use PHPUnitPHAR\phpDocumentor\Reflection\PseudoTypes\IntegerValue; -use PHPUnitPHAR\phpDocumentor\Reflection\PseudoTypes\List_; -use PHPUnitPHAR\phpDocumentor\Reflection\PseudoTypes\LiteralString; -use PHPUnitPHAR\phpDocumentor\Reflection\PseudoTypes\LowercaseString; -use PHPUnitPHAR\phpDocumentor\Reflection\PseudoTypes\NegativeInteger; -use PHPUnitPHAR\phpDocumentor\Reflection\PseudoTypes\NonEmptyList; -use PHPUnitPHAR\phpDocumentor\Reflection\PseudoTypes\NonEmptyLowercaseString; -use PHPUnitPHAR\phpDocumentor\Reflection\PseudoTypes\NonEmptyString; -use PHPUnitPHAR\phpDocumentor\Reflection\PseudoTypes\Numeric_; -use PHPUnitPHAR\phpDocumentor\Reflection\PseudoTypes\NumericString; -use PHPUnitPHAR\phpDocumentor\Reflection\PseudoTypes\PositiveInteger; -use PHPUnitPHAR\phpDocumentor\Reflection\PseudoTypes\StringValue; -use PHPUnitPHAR\phpDocumentor\Reflection\PseudoTypes\TraitString; -use PHPUnitPHAR\phpDocumentor\Reflection\PseudoTypes\True_; -use PHPUnitPHAR\phpDocumentor\Reflection\Types\AggregatedType; -use PHPUnitPHAR\phpDocumentor\Reflection\Types\Array_; -use PHPUnitPHAR\phpDocumentor\Reflection\Types\ArrayKey; -use PHPUnitPHAR\phpDocumentor\Reflection\Types\Boolean; -use PHPUnitPHAR\phpDocumentor\Reflection\Types\Callable_; -use PHPUnitPHAR\phpDocumentor\Reflection\Types\CallableParameter; -use PHPUnitPHAR\phpDocumentor\Reflection\Types\ClassString; -use PHPUnitPHAR\phpDocumentor\Reflection\Types\Collection; -use PHPUnitPHAR\phpDocumentor\Reflection\Types\Compound; -use PHPUnitPHAR\phpDocumentor\Reflection\Types\Context; -use PHPUnitPHAR\phpDocumentor\Reflection\Types\Expression; -use PHPUnitPHAR\phpDocumentor\Reflection\Types\Float_; -use PHPUnitPHAR\phpDocumentor\Reflection\Types\Integer; -use PHPUnitPHAR\phpDocumentor\Reflection\Types\InterfaceString; -use PHPUnitPHAR\phpDocumentor\Reflection\Types\Intersection; -use PHPUnitPHAR\phpDocumentor\Reflection\Types\Iterable_; -use PHPUnitPHAR\phpDocumentor\Reflection\Types\Mixed_; -use PHPUnitPHAR\phpDocumentor\Reflection\Types\Never_; -use PHPUnitPHAR\phpDocumentor\Reflection\Types\Null_; -use PHPUnitPHAR\phpDocumentor\Reflection\Types\Nullable; -use PHPUnitPHAR\phpDocumentor\Reflection\Types\Object_; -use PHPUnitPHAR\phpDocumentor\Reflection\Types\Parent_; -use PHPUnitPHAR\phpDocumentor\Reflection\Types\Resource_; -use PHPUnitPHAR\phpDocumentor\Reflection\Types\Scalar; -use PHPUnitPHAR\phpDocumentor\Reflection\Types\Self_; -use PHPUnitPHAR\phpDocumentor\Reflection\Types\Static_; -use PHPUnitPHAR\phpDocumentor\Reflection\Types\String_; -use PHPUnitPHAR\phpDocumentor\Reflection\Types\This; -use PHPUnitPHAR\phpDocumentor\Reflection\Types\Void_; -use PHPUnitPHAR\PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprFloatNode; -use PHPUnitPHAR\PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprIntegerNode; -use PHPUnitPHAR\PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprStringNode; -use PHPUnitPHAR\PHPStan\PhpDocParser\Ast\ConstExpr\ConstFetchNode; -use PHPUnitPHAR\PHPStan\PhpDocParser\Ast\Type\ArrayShapeItemNode; -use PHPUnitPHAR\PHPStan\PhpDocParser\Ast\Type\ArrayShapeNode; -use PHPUnitPHAR\PHPStan\PhpDocParser\Ast\Type\ArrayTypeNode; -use PHPUnitPHAR\PHPStan\PhpDocParser\Ast\Type\CallableTypeNode; -use PHPUnitPHAR\PHPStan\PhpDocParser\Ast\Type\CallableTypeParameterNode; -use PHPUnitPHAR\PHPStan\PhpDocParser\Ast\Type\ConditionalTypeForParameterNode; -use PHPUnitPHAR\PHPStan\PhpDocParser\Ast\Type\ConditionalTypeNode; -use PHPUnitPHAR\PHPStan\PhpDocParser\Ast\Type\ConstTypeNode; -use PHPUnitPHAR\PHPStan\PhpDocParser\Ast\Type\GenericTypeNode; -use PHPUnitPHAR\PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode; -use PHPUnitPHAR\PHPStan\PhpDocParser\Ast\Type\IntersectionTypeNode; -use PHPUnitPHAR\PHPStan\PhpDocParser\Ast\Type\NullableTypeNode; -use PHPUnitPHAR\PHPStan\PhpDocParser\Ast\Type\OffsetAccessTypeNode; -use PHPUnitPHAR\PHPStan\PhpDocParser\Ast\Type\ThisTypeNode; -use PHPUnitPHAR\PHPStan\PhpDocParser\Ast\Type\TypeNode; -use PHPUnitPHAR\PHPStan\PhpDocParser\Ast\Type\UnionTypeNode; -use PHPUnitPHAR\PHPStan\PhpDocParser\Lexer\Lexer; -use PHPUnitPHAR\PHPStan\PhpDocParser\Parser\ConstExprParser; -use PHPUnitPHAR\PHPStan\PhpDocParser\Parser\ParserException; -use PHPUnitPHAR\PHPStan\PhpDocParser\Parser\TokenIterator; -use PHPUnitPHAR\PHPStan\PhpDocParser\Parser\TypeParser; -use RuntimeException; -use function array_filter; -use function array_key_exists; -use function array_map; -use function array_reverse; -use function class_exists; -use function class_implements; -use function get_class; -use function in_array; +use const PHP_EOL; use function sprintf; -use function strpos; -use function strtolower; -use function trim; -final class TypeResolver +use PHPUnit\Event\Code\Test; +use PHPUnit\Event\Event; +use PHPUnit\Event\Telemetry; +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class DeprecationTriggered implements Event { - /** @var string Definition of the NAMESPACE operator in PHP */ - private const OPERATOR_NAMESPACE = '\\'; - /** - * @var array List of recognized keywords and unto which Value Object they map - * @psalm-var array> - */ - private $keywords = ['string' => String_::class, 'class-string' => ClassString::class, 'interface-string' => InterfaceString::class, 'html-escaped-string' => HtmlEscapedString::class, 'lowercase-string' => LowercaseString::class, 'non-empty-lowercase-string' => NonEmptyLowercaseString::class, 'non-empty-string' => NonEmptyString::class, 'numeric-string' => NumericString::class, 'numeric' => Numeric_::class, 'trait-string' => TraitString::class, 'int' => Integer::class, 'integer' => Integer::class, 'positive-int' => PositiveInteger::class, 'negative-int' => NegativeInteger::class, 'bool' => Boolean::class, 'boolean' => Boolean::class, 'real' => Float_::class, 'float' => Float_::class, 'double' => Float_::class, 'object' => Object_::class, 'mixed' => Mixed_::class, 'array' => Array_::class, 'array-key' => ArrayKey::class, 'resource' => Resource_::class, 'void' => Void_::class, 'null' => Null_::class, 'scalar' => Scalar::class, 'callback' => Callable_::class, 'callable' => Callable_::class, 'callable-string' => CallableString::class, 'false' => False_::class, 'true' => True_::class, 'literal-string' => LiteralString::class, 'self' => Self_::class, '$this' => This::class, 'static' => Static_::class, 'parent' => Parent_::class, 'iterable' => Iterable_::class, 'never' => Never_::class, 'list' => List_::class, 'non-empty-list' => NonEmptyList::class]; - /** - * @psalm-readonly - * @var FqsenResolver - */ - private $fqsenResolver; + private readonly Telemetry\Info $telemetryInfo; + private readonly Test $test; /** - * @psalm-readonly - * @var TypeParser - */ - private $typeParser; - /** - * @psalm-readonly - * @var Lexer - */ - private $lexer; - /** - * Initializes this TypeResolver with the means to create and resolve Fqsen objects. + * @psalm-var non-empty-string */ - public function __construct(?FqsenResolver $fqsenResolver = null) - { - $this->fqsenResolver = $fqsenResolver ?: new FqsenResolver(); - $this->typeParser = new TypeParser(new ConstExprParser()); - $this->lexer = new Lexer(); - } + private readonly string $message; /** - * Analyzes the given type and returns the FQCN variant. - * - * When a type is provided this method checks whether it is not a keyword or - * Fully Qualified Class Name. If so it will use the given namespace and - * aliases to expand the type to a FQCN representation. - * - * This method only works as expected if the namespace and aliases are set; - * no dynamic reflection is being performed here. - * - * @uses Context::getNamespace() to determine with what to prefix the type name. - * @uses Context::getNamespaceAliases() to check whether the first part of the relative type name should not be - * replaced with another namespace. - * - * @param string $type The relative or absolute type. + * @psalm-var non-empty-string */ - public function resolve(string $type, ?Context $context = null) : Type - { - $type = trim($type); - if (!$type) { - throw new InvalidArgumentException('Attempted to resolve "' . $type . '" but it appears to be empty'); - } - if ($context === null) { - $context = new Context(''); - } - $tokens = $this->lexer->tokenize($type); - $tokenIterator = new TokenIterator($tokens); - $ast = $this->parse($tokenIterator); - $type = $this->createType($ast, $context); - return $this->tryParseRemainingCompoundTypes($tokenIterator, $context, $type); - } - public function createType(?TypeNode $type, Context $context) : Type - { - if ($type === null) { - return new Mixed_(); - } - switch (get_class($type)) { - case ArrayTypeNode::class: - return new Array_($this->createType($type->type, $context)); - case ArrayShapeNode::class: - return new ArrayShape(...array_map(function (ArrayShapeItemNode $item) use($context) : ArrayShapeItem { - return new ArrayShapeItem((string) $item->keyName, $this->createType($item->valueType, $context), $item->optional); - }, $type->items)); - case CallableTypeNode::class: - return $this->createFromCallable($type, $context); - case ConstTypeNode::class: - return $this->createFromConst($type, $context); - case GenericTypeNode::class: - return $this->createFromGeneric($type, $context); - case IdentifierTypeNode::class: - return $this->resolveSingleType($type->name, $context); - case IntersectionTypeNode::class: - return new Intersection(array_filter(array_map(function (TypeNode $nestedType) use($context) : Type { - $type = $this->createType($nestedType, $context); - if ($type instanceof AggregatedType) { - return new Expression($type); - } - return $type; - }, $type->types))); - case NullableTypeNode::class: - $nestedType = $this->createType($type->type, $context); - return new Nullable($nestedType); - case UnionTypeNode::class: - return new Compound(array_filter(array_map(function (TypeNode $nestedType) use($context) : Type { - $type = $this->createType($nestedType, $context); - if ($type instanceof AggregatedType) { - return new Expression($type); - } - return $type; - }, $type->types))); - case ThisTypeNode::class: - return new This(); - case ConditionalTypeNode::class: - case ConditionalTypeForParameterNode::class: - case OffsetAccessTypeNode::class: - default: - return new Mixed_(); - } - } - private function createFromGeneric(GenericTypeNode $type, Context $context) : Type - { - switch (strtolower($type->type->name)) { - case 'array': - return $this->createArray($type->genericTypes, $context); - case 'class-string': - $subType = $this->createType($type->genericTypes[0], $context); - if (!$subType instanceof Object_ || $subType->getFqsen() === null) { - throw new RuntimeException($subType . ' is not a class string'); - } - return new ClassString($subType->getFqsen()); - case 'interface-string': - $subType = $this->createType($type->genericTypes[0], $context); - if (!$subType instanceof Object_ || $subType->getFqsen() === null) { - throw new RuntimeException($subType . ' is not a class string'); - } - return new InterfaceString($subType->getFqsen()); - case 'list': - return new List_($this->createType($type->genericTypes[0], $context)); - case 'non-empty-list': - return new NonEmptyList($this->createType($type->genericTypes[0], $context)); - case 'int': - if (isset($type->genericTypes[1]) === \false) { - throw new RuntimeException('int has not the correct format'); - } - return new IntegerRange((string) $type->genericTypes[0], (string) $type->genericTypes[1]); - case 'iterable': - return new Iterable_(...array_reverse(array_map(function (TypeNode $genericType) use($context) : Type { - return $this->createType($genericType, $context); - }, $type->genericTypes))); - default: - $collectionType = $this->createType($type->type, $context); - if ($collectionType instanceof Object_ === \false) { - throw new RuntimeException(sprintf('%s is not a collection', (string) $collectionType)); - } - return new Collection($collectionType->getFqsen(), ...array_reverse(array_map(function (TypeNode $genericType) use($context) : Type { - return $this->createType($genericType, $context); - }, $type->genericTypes))); - } - } - private function createFromCallable(CallableTypeNode $type, Context $context) : Callable_ - { - return new Callable_(array_map(function (CallableTypeParameterNode $param) use($context) : CallableParameter { - return new CallableParameter($this->createType($param->type, $context), $param->parameterName !== '' ? trim($param->parameterName, '$') : null, $param->isReference, $param->isVariadic, $param->isOptional); - }, $type->parameters), $this->createType($type->returnType, $context)); - } - private function createFromConst(ConstTypeNode $type, Context $context) : Type - { - switch (\true) { - case $type->constExpr instanceof ConstExprIntegerNode: - return new IntegerValue((int) $type->constExpr->value); - case $type->constExpr instanceof ConstExprFloatNode: - return new FloatValue((float) $type->constExpr->value); - case $type->constExpr instanceof ConstExprStringNode: - return new StringValue($type->constExpr->value); - case $type->constExpr instanceof ConstFetchNode: - return new ConstExpression($this->resolve($type->constExpr->className, $context), $type->constExpr->name); - default: - throw new RuntimeException(sprintf('Unsupported constant type %s', get_class($type))); - } - } + private readonly string $file; /** - * resolve the given type into a type object - * - * @param string $type the type string, representing a single type - * - * @return Type|Array_|Object_ - * - * @psalm-mutation-free + * @psalm-var positive-int */ - private function resolveSingleType(string $type, Context $context) : object - { - switch (\true) { - case $this->isKeyword($type): - return $this->resolveKeyword($type); - case $this->isFqsen($type): - return $this->resolveTypedObject($type); - case $this->isPartialStructuralElementName($type): - return $this->resolveTypedObject($type, $context); - // @codeCoverageIgnoreStart - default: - // I haven't got the foggiest how the logic would come here but added this as a defense. - throw new RuntimeException('Unable to resolve type "' . $type . '", there is no known method to resolve it'); - } - // @codeCoverageIgnoreEnd - } + private readonly int $line; + private readonly bool $suppressed; + private readonly bool $ignoredByBaseline; + private readonly bool $ignoredByTest; /** - * Adds a keyword to the list of Keywords and associates it with a specific Value Object. - * - * @psalm-param class-string $typeClassName + * @psalm-param non-empty-string $message + * @psalm-param non-empty-string $file + * @psalm-param positive-int $line */ - public function addKeyword(string $keyword, string $typeClassName) : void + public function __construct(Telemetry\Info $telemetryInfo, Test $test, string $message, string $file, int $line, bool $suppressed, bool $ignoredByBaseline, bool $ignoredByTest) { - if (!class_exists($typeClassName)) { - throw new InvalidArgumentException('The Value Object that needs to be created with a keyword "' . $keyword . '" must be an existing class' . ' but we could not find the class ' . $typeClassName); - } - $interfaces = class_implements($typeClassName); - if ($interfaces === \false) { - throw new InvalidArgumentException('The Value Object that needs to be created with a keyword "' . $keyword . '" must be an existing class' . ' but we could not find the class ' . $typeClassName); - } - if (!in_array(Type::class, $interfaces, \true)) { - throw new InvalidArgumentException('The class "' . $typeClassName . '" must implement the interface "phpDocumentor\\Reflection\\Type"'); - } - $this->keywords[$keyword] = $typeClassName; + $this->telemetryInfo = $telemetryInfo; + $this->test = $test; + $this->message = $message; + $this->file = $file; + $this->line = $line; + $this->suppressed = $suppressed; + $this->ignoredByBaseline = $ignoredByBaseline; + $this->ignoredByTest = $ignoredByTest; } - /** - * Detects whether the given type represents a PHPDoc keyword. - * - * @param string $type A relative or absolute type as defined in the phpDocumentor documentation. - * - * @psalm-mutation-free - */ - private function isKeyword(string $type) : bool + public function telemetryInfo(): Telemetry\Info { - return array_key_exists(strtolower($type), $this->keywords); + return $this->telemetryInfo; } - /** - * Detects whether the given type represents a relative structural element name. - * - * @param string $type A relative or absolute type as defined in the phpDocumentor documentation. - * - * @psalm-mutation-free - */ - private function isPartialStructuralElementName(string $type) : bool + public function test(): Test { - return isset($type[0]) && $type[0] !== self::OPERATOR_NAMESPACE && !$this->isKeyword($type); + return $this->test; } /** - * Tests whether the given type is a Fully Qualified Structural Element Name. - * - * @psalm-mutation-free + * @psalm-return non-empty-string */ - private function isFqsen(string $type) : bool + public function message(): string { - return strpos($type, self::OPERATOR_NAMESPACE) === 0; + return $this->message; } /** - * Resolves the given keyword (such as `string`) into a Type object representing that keyword. - * - * @psalm-mutation-free + * @psalm-return non-empty-string */ - private function resolveKeyword(string $type) : Type + public function file(): string { - $className = $this->keywords[strtolower($type)]; - return new $className(); + return $this->file; } /** - * Resolves the given FQSEN string into an FQSEN object. - * - * @psalm-mutation-free + * @psalm-return positive-int */ - private function resolveTypedObject(string $type, ?Context $context = null) : Object_ + public function line(): int { - return new Object_($this->fqsenResolver->resolve($type, $context)); + return $this->line; } - /** @param TypeNode[] $typeNodes */ - private function createArray(array $typeNodes, Context $context) : Array_ + public function wasSuppressed(): bool { - $types = array_reverse(array_map(function (TypeNode $node) use($context) : Type { - return $this->createType($node, $context); - }, $typeNodes)); - if (isset($types[1]) === \false) { - return new Array_(...$types); - } - if ($this->validArrayKeyType($types[1]) || $types[1] instanceof ArrayKey) { - return new Array_(...$types); - } - if ($types[1] instanceof Compound && $types[1]->getIterator()->count() === 2) { - if ($this->validArrayKeyType($types[1]->get(0)) && $this->validArrayKeyType($types[1]->get(1))) { - return new Array_(...$types); - } - } - throw new RuntimeException('An array can have only integers or strings as keys'); + return $this->suppressed; } - private function validArrayKeyType(?Type $type) : bool + public function ignoredByBaseline(): bool { - return $type instanceof String_ || $type instanceof Integer; + return $this->ignoredByBaseline; } - private function parse(TokenIterator $tokenIterator) : TypeNode + public function ignoredByTest(): bool { - try { - $ast = $this->typeParser->parse($tokenIterator); - } catch (ParserException $e) { - throw new RuntimeException($e->getMessage(), 0, $e); - } - return $ast; + return $this->ignoredByTest; } - /** - * Will try to parse unsupported type notations by phpstan - * - * The phpstan parser doesn't support the illegal nullable combinations like this library does. - * This method will warn the user about those notations but for bc purposes we will still have it here. - */ - private function tryParseRemainingCompoundTypes(TokenIterator $tokenIterator, Context $context, Type $type) : Type + public function asString(): string { - if ($tokenIterator->isCurrentTokenType(Lexer::TOKEN_UNION) || $tokenIterator->isCurrentTokenType(Lexer::TOKEN_INTERSECTION)) { - Deprecation::trigger('phpdocumentor/type-resolver', 'https://github.com/phpDocumentor/TypeResolver/issues/184', 'Legacy nullable type detected, please update your code as - you are using nullable types in a docblock. support will be removed in v2.0.0'); + $message = $this->message; + if (!empty($message)) { + $message = PHP_EOL . $message; } - $continue = \true; - while ($continue) { - $continue = \false; - while ($tokenIterator->tryConsumeTokenType(Lexer::TOKEN_UNION)) { - $ast = $this->parse($tokenIterator); - $type2 = $this->createType($ast, $context); - $type = new Compound([$type, $type2]); - $continue = \true; - } - while ($tokenIterator->tryConsumeTokenType(Lexer::TOKEN_INTERSECTION)) { - $ast = $this->typeParser->parse($tokenIterator); - $type2 = $this->createType($ast, $context); - $type = new Intersection([$type, $type2]); - $continue = \true; - } + $status = ''; + if ($this->ignoredByTest) { + $status = 'Test-Ignored '; + } elseif ($this->ignoredByBaseline) { + $status = 'Baseline-Ignored '; + } elseif ($this->suppressed) { + $status = 'Suppressed '; } - return $type; + return sprintf('Test Triggered %sDeprecation (%s)%s', $status, $this->test->id(), $message); } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. - * - * @link http://phpdoc.org */ -namespace PHPUnitPHAR\phpDocumentor\Reflection\Types; +namespace PHPUnit\Event\Test; -use PHPUnitPHAR\phpDocumentor\Reflection\Type; +use PHPUnit\Event\Subscriber; /** - * Represents a list of values. This is an abstract class for Array_ and Collection. + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +interface DeprecationTriggeredSubscriber extends Subscriber +{ + public function notify(\PHPUnit\Event\Test\DeprecationTriggered $event): void; +} + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\Test; + +use const PHP_EOL; +use function sprintf; +use PHPUnit\Event\Code\Test; +use PHPUnit\Event\Event; +use PHPUnit\Event\Telemetry; +/** * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -abstract class AbstractList implements Type +final class ErrorTriggered implements Event { - /** @var Type */ - protected $valueType; - /** @var Type|null */ - protected $keyType; - /** @var Type */ - protected $defaultKeyType; + private readonly Telemetry\Info $telemetryInfo; + private readonly Test $test; + /** + * @psalm-var non-empty-string + */ + private readonly string $message; + /** + * @psalm-var non-empty-string + */ + private readonly string $file; + /** + * @psalm-var positive-int + */ + private readonly int $line; + private readonly bool $suppressed; /** - * Initializes this representation of an array with the given Type. + * @psalm-param non-empty-string $message + * @psalm-param non-empty-string $file + * @psalm-param positive-int $line */ - public function __construct(?Type $valueType = null, ?Type $keyType = null) + public function __construct(Telemetry\Info $telemetryInfo, Test $test, string $message, string $file, int $line, bool $suppressed) { - if ($valueType === null) { - $valueType = new Mixed_(); - } - $this->valueType = $valueType; - $this->defaultKeyType = new Compound([new String_(), new Integer()]); - $this->keyType = $keyType; + $this->telemetryInfo = $telemetryInfo; + $this->test = $test; + $this->message = $message; + $this->file = $file; + $this->line = $line; + $this->suppressed = $suppressed; + } + public function telemetryInfo(): Telemetry\Info + { + return $this->telemetryInfo; + } + public function test(): Test + { + return $this->test; } /** - * Returns the type for the keys of this array. + * @psalm-return non-empty-string */ - public function getKeyType() : Type + public function message(): string { - return $this->keyType ?? $this->defaultKeyType; + return $this->message; } /** - * Returns the type for the values of this array. + * @psalm-return non-empty-string */ - public function getValueType() : Type + public function file(): string { - return $this->valueType; + return $this->file; } /** - * Returns a rendered output of the Type as it would be used in a DocBlock. + * @psalm-return positive-int */ - public function __toString() : string + public function line(): int { - if ($this->keyType) { - return 'array<' . $this->keyType . ',' . $this->valueType . '>'; - } - if ($this->valueType instanceof Mixed_) { - return 'array'; - } - if ($this->valueType instanceof Compound) { - return '(' . $this->valueType . ')[]'; + return $this->line; + } + public function wasSuppressed(): bool + { + return $this->suppressed; + } + public function asString(): string + { + $message = $this->message; + if (!empty($message)) { + $message = PHP_EOL . $message; } - return $this->valueType . '[]'; + return sprintf('Test Triggered %sError (%s)%s', $this->wasSuppressed() ? 'Suppressed ' : '', $this->test->id(), $message); } } * - * @link http://phpdoc.org + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. */ -declare (strict_types=1); -namespace PHPUnitPHAR\phpDocumentor\Reflection\Types; +namespace PHPUnit\Event\Test; -use ArrayIterator; -use IteratorAggregate; -use PHPUnitPHAR\phpDocumentor\Reflection\Type; -use function array_key_exists; -use function implode; +use PHPUnit\Event\Subscriber; /** - * Base class for aggregated types like Compound and Intersection + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +interface ErrorTriggeredSubscriber extends Subscriber +{ + public function notify(\PHPUnit\Event\Test\ErrorTriggered $event): void; +} + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\Test; + +use const PHP_EOL; +use function sprintf; +use PHPUnit\Event\Code\Test; +use PHPUnit\Event\Event; +use PHPUnit\Event\Telemetry; +/** * @psalm-immutable - * @template-implements IteratorAggregate + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -abstract class AggregatedType implements Type, IteratorAggregate +final class NoticeTriggered implements Event { + private readonly Telemetry\Info $telemetryInfo; + private readonly Test $test; /** - * @psalm-allow-private-mutation - * @var array + * @psalm-var non-empty-string */ - private $types = []; - /** @var string */ - private $token; + private readonly string $message; /** - * @param array $types + * @psalm-var non-empty-string */ - public function __construct(array $types, string $token) - { - foreach ($types as $type) { - $this->add($type); - } - $this->token = $token; - } + private readonly string $file; /** - * Returns the type at the given index. + * @psalm-var positive-int */ - public function get(int $index) : ?Type - { - if (!$this->has($index)) { - return null; - } - return $this->types[$index]; - } + private readonly int $line; + private readonly bool $suppressed; + private readonly bool $ignoredByBaseline; /** - * Tests if this compound type has a type with the given index. + * @psalm-param non-empty-string $message + * @psalm-param non-empty-string $file + * @psalm-param positive-int $line */ - public function has(int $index) : bool + public function __construct(Telemetry\Info $telemetryInfo, Test $test, string $message, string $file, int $line, bool $suppressed, bool $ignoredByBaseline) { - return array_key_exists($index, $this->types); + $this->telemetryInfo = $telemetryInfo; + $this->test = $test; + $this->message = $message; + $this->file = $file; + $this->line = $line; + $this->suppressed = $suppressed; + $this->ignoredByBaseline = $ignoredByBaseline; } - /** - * Tests if this compound type contains the given type. - */ - public function contains(Type $type) : bool + public function telemetryInfo(): Telemetry\Info { - foreach ($this->types as $typePart) { - // if the type is duplicate; do not add it - if ((string) $typePart === (string) $type) { - return \true; - } - } - return \false; + return $this->telemetryInfo; + } + public function test(): Test + { + return $this->test; } /** - * Returns a rendered output of the Type as it would be used in a DocBlock. + * @psalm-return non-empty-string */ - public function __toString() : string + public function message(): string { - return implode($this->token, $this->types); + return $this->message; } /** - * @return ArrayIterator + * @psalm-return non-empty-string */ - public function getIterator() : ArrayIterator + public function file(): string { - return new ArrayIterator($this->types); + return $this->file; } /** - * @psalm-suppress ImpureMethodCall + * @psalm-return positive-int */ - private function add(Type $type) : void + public function line(): int { - if ($type instanceof static) { - foreach ($type->getIterator() as $subType) { - $this->add($subType); - } - return; - } - // if the type is duplicate; do not add it - if ($this->contains($type)) { - return; - } - $this->types[] = $type; + return $this->line; } -} -suppressed; } - public function underlyingType() : Type + public function ignoredByBaseline(): bool { - return new Compound([new String_(), new Integer()]); + return $this->ignoredByBaseline; } - public function __toString() : string + public function asString(): string { - return 'array-key'; + $message = $this->message; + if (!empty($message)) { + $message = PHP_EOL . $message; + } + $status = ''; + if ($this->ignoredByBaseline) { + $status = 'Baseline-Ignored '; + } elseif ($this->suppressed) { + $status = 'Suppressed '; + } + return sprintf('Test Triggered %sNotice (%s)%s', $status, $this->test->id(), $message); } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. - * - * @link http://phpdoc.org */ -namespace PHPUnitPHAR\phpDocumentor\Reflection\Types; +namespace PHPUnit\Event\Test; +use PHPUnit\Event\Subscriber; /** - * Represents an array type as described in the PSR-5, the PHPDoc Standard. - * - * An array can be represented in two forms: - * - * 1. Untyped (`array`), where the key and value type is unknown and hence classified as 'Mixed_'. - * 2. Types (`string[]`), where the value type is provided by preceding an opening and closing square bracket with a - * type name. - * - * @psalm-immutable + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -class Array_ extends AbstractList +interface NoticeTriggeredSubscriber extends Subscriber { + public function notify(\PHPUnit\Event\Test\NoticeTriggered $event): void; } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. - * - * @link http://phpdoc.org */ -namespace PHPUnitPHAR\phpDocumentor\Reflection\Types; +namespace PHPUnit\Event\Test; -use PHPUnitPHAR\phpDocumentor\Reflection\Type; +use const PHP_EOL; +use function sprintf; +use PHPUnit\Event\Code\Test; +use PHPUnit\Event\Event; +use PHPUnit\Event\Telemetry; /** - * Value Object representing a Boolean type. - * * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -class Boolean implements Type +final class PhpDeprecationTriggered implements Event { + private readonly Telemetry\Info $telemetryInfo; + private readonly Test $test; /** - * Returns a rendered output of the Type as it would be used in a DocBlock. + * @psalm-var non-empty-string */ - public function __toString() : string - { - return 'bool'; - } -} -type = $type; - $this->isReference = $isReference; - $this->isVariadic = $isVariadic; - $this->isOptional = $isOptional; - $this->name = $name; + $this->telemetryInfo = $telemetryInfo; + $this->test = $test; + $this->message = $message; + $this->file = $file; + $this->line = $line; + $this->suppressed = $suppressed; + $this->ignoredByBaseline = $ignoredByBaseline; + $this->ignoredByTest = $ignoredByTest; } - public function getName() : ?string + public function telemetryInfo(): Telemetry\Info { - return $this->name; + return $this->telemetryInfo; } - public function getType() : Type + public function test(): Test { - return $this->type; + return $this->test; + } + /** + * @psalm-return non-empty-string + */ + public function message(): string + { + return $this->message; + } + /** + * @psalm-return non-empty-string + */ + public function file(): string + { + return $this->file; } - public function isReference() : bool + /** + * @psalm-return positive-int + */ + public function line(): int + { + return $this->line; + } + public function wasSuppressed(): bool + { + return $this->suppressed; + } + public function ignoredByBaseline(): bool { - return $this->isReference; + return $this->ignoredByBaseline; } - public function isVariadic() : bool + public function ignoredByTest(): bool { - return $this->isVariadic; + return $this->ignoredByTest; } - public function isOptional() : bool + public function asString(): string { - return $this->isOptional; + $message = $this->message; + if (!empty($message)) { + $message = PHP_EOL . $message; + } + $status = ''; + if ($this->ignoredByTest) { + $status = 'Test-Ignored '; + } elseif ($this->ignoredByBaseline) { + $status = 'Baseline-Ignored '; + } elseif ($this->suppressed) { + $status = 'Suppressed '; + } + return sprintf('Test Triggered %sPHP Deprecation (%s)%s', $status, $this->test->id(), $message); } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. - * - * @link http://phpdoc.org */ -namespace PHPUnitPHAR\phpDocumentor\Reflection\Types; +namespace PHPUnit\Event\Test; -use PHPUnitPHAR\phpDocumentor\Reflection\Type; +use PHPUnit\Event\Subscriber; /** - * Value Object representing a Callable type. - * - * @psalm-immutable + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -final class Callable_ implements Type +interface PhpDeprecationTriggeredSubscriber extends Subscriber { - /** @var Type|null */ - private $returnType; - /** @var CallableParameter[] */ - private $parameters; - /** - * @param CallableParameter[] $parameters - */ - public function __construct(array $parameters = [], ?Type $returnType = null) - { - $this->parameters = $parameters; - $this->returnType = $returnType; - } - /** @return CallableParameter[] */ - public function getParameters() : array - { - return $this->parameters; - } - public function getReturnType() : ?Type - { - return $this->returnType; - } - /** - * Returns a rendered output of the Type as it would be used in a DocBlock. - */ - public function __toString() : string - { - return 'callable'; - } + public function notify(\PHPUnit\Event\Test\PhpDeprecationTriggered $event): void; } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. - * - * @link http://phpdoc.org */ -namespace PHPUnitPHAR\phpDocumentor\Reflection\Types; +namespace PHPUnit\Event\Test; -use PHPUnitPHAR\phpDocumentor\Reflection\Fqsen; -use PHPUnitPHAR\phpDocumentor\Reflection\PseudoType; -use PHPUnitPHAR\phpDocumentor\Reflection\Type; +use const PHP_EOL; +use function sprintf; +use PHPUnit\Event\Code\Test; +use PHPUnit\Event\Event; +use PHPUnit\Event\Telemetry; /** - * Value Object representing the type 'string'. - * * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -final class ClassString extends String_ implements PseudoType +final class PhpNoticeTriggered implements Event { - /** @var Fqsen|null */ - private $fqsen; + private readonly Telemetry\Info $telemetryInfo; + private readonly Test $test; + /** + * @psalm-var non-empty-string + */ + private readonly string $message; + /** + * @psalm-var non-empty-string + */ + private readonly string $file; + /** + * @psalm-var positive-int + */ + private readonly int $line; + private readonly bool $suppressed; + private readonly bool $ignoredByBaseline; /** - * Initializes this representation of a class string with the given Fqsen. + * @psalm-param non-empty-string $message + * @psalm-param non-empty-string $file + * @psalm-param positive-int $line */ - public function __construct(?Fqsen $fqsen = null) + public function __construct(Telemetry\Info $telemetryInfo, Test $test, string $message, string $file, int $line, bool $suppressed, bool $ignoredByBaseline) { - $this->fqsen = $fqsen; + $this->telemetryInfo = $telemetryInfo; + $this->test = $test; + $this->message = $message; + $this->file = $file; + $this->line = $line; + $this->suppressed = $suppressed; + $this->ignoredByBaseline = $ignoredByBaseline; } - public function underlyingType() : Type + public function telemetryInfo(): Telemetry\Info { - return new String_(); + return $this->telemetryInfo; } - /** - * Returns the FQSEN associated with this object. - */ - public function getFqsen() : ?Fqsen + public function test(): Test { - return $this->fqsen; + return $this->test; } /** - * Returns a rendered output of the Type as it would be used in a DocBlock. + * @psalm-return non-empty-string */ - public function __toString() : string + public function message(): string { - if ($this->fqsen === null) { - return 'class-string'; - } - return 'class-string<' . (string) $this->fqsen . '>'; + return $this->message; } -} -` - * 2. `ACollectionObject` - * - * - ACollectionObject can be 'array' or an object that can act as an array - * - aValueType and aKeyType can be any type expression - * - * @psalm-immutable - */ -final class Collection extends AbstractList -{ - /** @var Fqsen|null */ - private $fqsen; /** - * Initializes this representation of an array with the given Type or Fqsen. + * @psalm-return non-empty-string */ - public function __construct(?Fqsen $fqsen, Type $valueType, ?Type $keyType = null) + public function file(): string { - parent::__construct($valueType, $keyType); - $this->fqsen = $fqsen; + return $this->file; } /** - * Returns the FQSEN associated with this object. + * @psalm-return positive-int */ - public function getFqsen() : ?Fqsen + public function line(): int { - return $this->fqsen; + return $this->line; } - /** - * Returns a rendered output of the Type as it would be used in a DocBlock. - */ - public function __toString() : string + public function wasSuppressed(): bool + { + return $this->suppressed; + } + public function ignoredByBaseline(): bool + { + return $this->ignoredByBaseline; + } + public function asString(): string { - $objectType = (string) ($this->fqsen ?? 'object'); - if ($this->keyType === null) { - return $objectType . '<' . $this->valueType . '>'; + $message = $this->message; + if (!empty($message)) { + $message = PHP_EOL . $message; } - return $objectType . '<' . $this->keyType . ',' . $this->valueType . '>'; + $status = ''; + if ($this->ignoredByBaseline) { + $status = 'Baseline-Ignored '; + } elseif ($this->suppressed) { + $status = 'Suppressed '; + } + return sprintf('Test Triggered %sPHP Notice (%s)%s', $status, $this->test->id(), $message); } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. - * - * @link http://phpdoc.org */ -namespace PHPUnitPHAR\phpDocumentor\Reflection\Types; +namespace PHPUnit\Event\Test; -use PHPUnitPHAR\phpDocumentor\Reflection\Type; +use PHPUnit\Event\Subscriber; /** - * Value Object representing a Compound Type. - * - * A Compound Type is not so much a special keyword or object reference but is a series of Types that are separated - * using an OR operator (`|`). This combination of types signifies that whatever is associated with this compound type - * may contain a value with any of the given types. - * - * @psalm-immutable + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -final class Compound extends AggregatedType +interface PhpNoticeTriggeredSubscriber extends Subscriber { - /** - * Initializes a compound type (i.e. `string|int`) and tests if the provided types all implement the Type interface. - * - * @param array $types - */ - public function __construct(array $types) - { - parent::__construct($types, '|'); - } + public function notify(\PHPUnit\Event\Test\PhpNoticeTriggered $event): void; } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. - * - * @link http://phpdoc.org */ -namespace PHPUnitPHAR\phpDocumentor\Reflection\Types; +namespace PHPUnit\Event\Test; -use function strlen; -use function substr; -use function trim; +use const PHP_EOL; +use function sprintf; +use PHPUnit\Event\Code\Test; +use PHPUnit\Event\Event; +use PHPUnit\Event\Telemetry; /** - * Provides information about the Context in which the DocBlock occurs that receives this context. - * - * A DocBlock does not know of its own accord in which namespace it occurs and which namespace aliases are applicable - * for the block of code in which it is in. This information is however necessary to resolve Class names in tags since - * you can provide a short form or make use of namespace aliases. - * - * The phpDocumentor Reflection component knows how to create this class but if you use the DocBlock parser from your - * own application it is possible to generate a Context class using the ContextFactory; this will analyze the file in - * which an associated class resides for its namespace and imports. - * - * @see ContextFactory::createFromClassReflector() - * @see ContextFactory::createForNamespace() - * * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -final class Context +final class PhpWarningTriggered implements Event { - /** @var string The current namespace. */ - private $namespace; - /** - * @var string[] List of namespace aliases => Fully Qualified Namespace. - * @psalm-var array - */ - private $namespaceAliases; + private readonly Telemetry\Info $telemetryInfo; + private readonly Test $test; /** - * Initializes the new context and normalizes all passed namespaces to be in Qualified Namespace Name (QNN) - * format (without a preceding `\`). - * - * @param string $namespace The namespace where this DocBlock resides in. - * @param string[] $namespaceAliases List of namespace aliases => Fully Qualified Namespace. - * @psalm-param array $namespaceAliases + * @psalm-var non-empty-string */ - public function __construct(string $namespace, array $namespaceAliases = []) - { - $this->namespace = $namespace !== 'global' && $namespace !== 'default' ? trim($namespace, '\\') : ''; - foreach ($namespaceAliases as $alias => $fqnn) { - if ($fqnn[0] === '\\') { - $fqnn = substr($fqnn, 1); - } - if ($fqnn[strlen($fqnn) - 1] === '\\') { - $fqnn = substr($fqnn, 0, -1); - } - $namespaceAliases[$alias] = $fqnn; - } - $this->namespaceAliases = $namespaceAliases; - } + private readonly string $message; /** - * Returns the Qualified Namespace Name (thus without `\` in front) where the associated element is in. + * @psalm-var non-empty-string */ - public function getNamespace() : string - { - return $this->namespace; - } + private readonly string $file; /** - * Returns a list of Qualified Namespace Names (thus without `\` in front) that are imported, the keys represent - * the alias for the imported Namespace. - * - * @return string[] - * @psalm-return array + * @psalm-var positive-int */ - public function getNamespaceAliases() : array - { - return $this->namespaceAliases; - } -} - $reflector */ - return $this->createFromReflectionClass($reflector); - } - if ($reflector instanceof ReflectionParameter) { - return $this->createFromReflectionParameter($reflector); - } - if ($reflector instanceof ReflectionMethod) { - return $this->createFromReflectionMethod($reflector); - } - if ($reflector instanceof ReflectionProperty) { - return $this->createFromReflectionProperty($reflector); - } - if ($reflector instanceof ReflectionClassConstant) { - return $this->createFromReflectionClassConstant($reflector); - } - throw new UnexpectedValueException('Unhandled \\Reflector instance given: ' . get_class($reflector)); - } - private function createFromReflectionParameter(ReflectionParameter $parameter) : Context + public function __construct(Telemetry\Info $telemetryInfo, Test $test, string $message, string $file, int $line, bool $suppressed, bool $ignoredByBaseline) { - $class = $parameter->getDeclaringClass(); - if (!$class) { - throw new InvalidArgumentException('Unable to get class of ' . $parameter->getName()); - } - return $this->createFromReflectionClass($class); - } - private function createFromReflectionMethod(ReflectionMethod $method) : Context - { - $class = $method->getDeclaringClass(); - return $this->createFromReflectionClass($class); + $this->telemetryInfo = $telemetryInfo; + $this->test = $test; + $this->message = $message; + $this->file = $file; + $this->line = $line; + $this->suppressed = $suppressed; + $this->ignoredByBaseline = $ignoredByBaseline; } - private function createFromReflectionProperty(ReflectionProperty $property) : Context + public function telemetryInfo(): Telemetry\Info { - $class = $property->getDeclaringClass(); - return $this->createFromReflectionClass($class); + return $this->telemetryInfo; } - private function createFromReflectionClassConstant(ReflectionClassConstant $constant) : Context + public function test(): Test { - //phpcs:ignore SlevomatCodingStandard.Commenting.InlineDocCommentDeclaration.MissingVariable - /** @phpstan-var ReflectionClass $class */ - $class = $constant->getDeclaringClass(); - return $this->createFromReflectionClass($class); + return $this->test; } /** - * @phpstan-param ReflectionClass $class + * @psalm-return non-empty-string */ - private function createFromReflectionClass(ReflectionClass $class) : Context + public function message(): string { - $fileName = $class->getFileName(); - $namespace = $class->getNamespaceName(); - if (is_string($fileName) && file_exists($fileName)) { - $contents = file_get_contents($fileName); - if ($contents === \false) { - throw new RuntimeException('Unable to read file "' . $fileName . '"'); - } - return $this->createForNamespace($namespace, $contents); - } - return new Context($namespace, []); + return $this->message; } /** - * Build a Context for a namespace in the provided file contents. - * - * @see Context for more information on Contexts. - * - * @param string $namespace It does not matter if a `\` precedes the namespace name, - * this method first normalizes. - * @param string $fileContents The file's contents to retrieve the aliases from with the given namespace. + * @psalm-return non-empty-string */ - public function createForNamespace(string $namespace, string $fileContents) : Context + public function file(): string { - $namespace = trim($namespace, '\\'); - $useStatements = []; - $currentNamespace = ''; - $tokens = new ArrayIterator(token_get_all($fileContents)); - while ($tokens->valid()) { - $currentToken = $tokens->current(); - switch ($currentToken[0]) { - case T_NAMESPACE: - $currentNamespace = $this->parseNamespace($tokens); - break; - case T_CLASS: - case T_TRAIT: - // Fast-forward the iterator through the class so that any - // T_USE tokens found within are skipped - these are not - // valid namespace use statements so should be ignored. - $braceLevel = 0; - $firstBraceFound = \false; - while ($tokens->valid() && ($braceLevel > 0 || !$firstBraceFound)) { - $currentToken = $tokens->current(); - if ($currentToken === '{' || in_array($currentToken[0], [T_CURLY_OPEN, T_DOLLAR_OPEN_CURLY_BRACES], \true)) { - if (!$firstBraceFound) { - $firstBraceFound = \true; - } - ++$braceLevel; - } - if ($currentToken === '}') { - --$braceLevel; - } - $tokens->next(); - } - break; - case T_USE: - if ($currentNamespace === $namespace) { - $useStatements += $this->parseUseStatement($tokens); - } - break; - } - $tokens->next(); - } - return new Context($namespace, $useStatements); + return $this->file; } /** - * Deduce the name from tokens when we are at the T_NAMESPACE token. - * - * @param ArrayIterator $tokens + * @psalm-return positive-int */ - private function parseNamespace(ArrayIterator $tokens) : string + public function line(): int { - // skip to the first string or namespace separator - $this->skipToNextStringOrNamespaceSeparator($tokens); - $name = ''; - $acceptedTokens = [T_STRING, T_NS_SEPARATOR, T_NAME_QUALIFIED]; - while ($tokens->valid() && in_array($tokens->current()[0], $acceptedTokens, \true)) { - $name .= $tokens->current()[1]; - $tokens->next(); - } - return $name; + return $this->line; } - /** - * Deduce the names of all imports when we are at the T_USE token. - * - * @param ArrayIterator $tokens - * - * @return string[] - * @psalm-return array - */ - private function parseUseStatement(ArrayIterator $tokens) : array + public function wasSuppressed(): bool { - $uses = []; - while ($tokens->valid()) { - $this->skipToNextStringOrNamespaceSeparator($tokens); - $uses += $this->extractUseStatements($tokens); - $currentToken = $tokens->current(); - if ($currentToken[0] === self::T_LITERAL_END_OF_USE) { - return $uses; - } - } - return $uses; + return $this->suppressed; } - /** - * Fast-forwards the iterator as longs as we don't encounter a T_STRING or T_NS_SEPARATOR token. - * - * @param ArrayIterator $tokens - */ - private function skipToNextStringOrNamespaceSeparator(ArrayIterator $tokens) : void + public function ignoredByBaseline(): bool { - while ($tokens->valid()) { - $currentToken = $tokens->current(); - if (in_array($currentToken[0], [T_STRING, T_NS_SEPARATOR], \true)) { - break; - } - if ($currentToken[0] === T_NAME_QUALIFIED) { - break; - } - if (defined('T_NAME_FULLY_QUALIFIED') && $currentToken[0] === T_NAME_FULLY_QUALIFIED) { - break; - } - $tokens->next(); - } + return $this->ignoredByBaseline; } - /** - * Deduce the namespace name and alias of an import when we are at the T_USE token or have not reached the end of - * a USE statement yet. This will return a key/value array of the alias => namespace. - * - * @param ArrayIterator $tokens - * - * @return string[] - * @psalm-return array - * - * @psalm-suppress TypeDoesNotContainType - */ - private function extractUseStatements(ArrayIterator $tokens) : array - { - $extractedUseStatements = []; - $groupedNs = ''; - $currentNs = ''; - $currentAlias = ''; - $state = 'start'; - while ($tokens->valid()) { - $currentToken = $tokens->current(); - $tokenId = is_string($currentToken) ? $currentToken : $currentToken[0]; - $tokenValue = is_string($currentToken) ? null : $currentToken[1]; - switch ($state) { - case 'start': - switch ($tokenId) { - case T_STRING: - case T_NS_SEPARATOR: - $currentNs .= (string) $tokenValue; - $currentAlias = $tokenValue; - break; - case T_NAME_QUALIFIED: - case T_NAME_FULLY_QUALIFIED: - $currentNs .= (string) $tokenValue; - $currentAlias = substr((string) $tokenValue, (int) strrpos((string) $tokenValue, '\\') + 1); - break; - case T_CURLY_OPEN: - case '{': - $state = 'grouped'; - $groupedNs = $currentNs; - break; - case T_AS: - $state = 'start-alias'; - break; - case self::T_LITERAL_USE_SEPARATOR: - case self::T_LITERAL_END_OF_USE: - $state = 'end'; - break; - default: - break; - } - break; - case 'start-alias': - switch ($tokenId) { - case T_STRING: - $currentAlias = $tokenValue; - break; - case self::T_LITERAL_USE_SEPARATOR: - case self::T_LITERAL_END_OF_USE: - $state = 'end'; - break; - default: - break; - } - break; - case 'grouped': - switch ($tokenId) { - case T_STRING: - case T_NS_SEPARATOR: - $currentNs .= (string) $tokenValue; - $currentAlias = $tokenValue; - break; - case T_AS: - $state = 'grouped-alias'; - break; - case self::T_LITERAL_USE_SEPARATOR: - $state = 'grouped'; - $extractedUseStatements[(string) $currentAlias] = $currentNs; - $currentNs = $groupedNs; - $currentAlias = ''; - break; - case self::T_LITERAL_END_OF_USE: - $state = 'end'; - break; - default: - break; - } - break; - case 'grouped-alias': - switch ($tokenId) { - case T_STRING: - $currentAlias = $tokenValue; - break; - case self::T_LITERAL_USE_SEPARATOR: - $state = 'grouped'; - $extractedUseStatements[(string) $currentAlias] = $currentNs; - $currentNs = $groupedNs; - $currentAlias = ''; - break; - case self::T_LITERAL_END_OF_USE: - $state = 'end'; - break; - default: - break; - } - } - if ($state === 'end') { - break; - } - $tokens->next(); + public function asString(): string + { + $message = $this->message; + if (!empty($message)) { + $message = PHP_EOL . $message; } - if ($groupedNs !== $currentNs) { - $extractedUseStatements[(string) $currentAlias] = $currentNs; + $status = ''; + if ($this->ignoredByBaseline) { + $status = 'Baseline-Ignored '; + } elseif ($this->suppressed) { + $status = 'Suppressed '; } - return $extractedUseStatements; + return sprintf('Test Triggered %sPHP Warning (%s)%s', $status, $this->test->id(), $message); } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. - * - * @link http://phpdoc.org */ -namespace PHPUnitPHAR\phpDocumentor\Reflection\Types; +namespace PHPUnit\Event\Test; -use PHPUnitPHAR\phpDocumentor\Reflection\Type; +use PHPUnit\Event\Subscriber; /** - * Represents an expression type as described in the PSR-5, the PHPDoc Standard. - * - * @psalm-immutable + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -final class Expression implements Type +interface PhpWarningTriggeredSubscriber extends Subscriber { - /** @var Type */ - protected $valueType; - /** - * Initializes this representation of an array with the given Type. - */ - public function __construct(Type $valueType) - { - $this->valueType = $valueType; - } - /** - * Returns the value for the keys of this array. - */ - public function getValueType() : Type - { - return $this->valueType; - } - /** - * Returns a rendered output of the Type as it would be used in a DocBlock. - */ - public function __toString() : string - { - return '(' . $this->valueType . ')'; - } + public function notify(\PHPUnit\Event\Test\PhpWarningTriggered $event): void; } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. - * - * @link http://phpdoc.org */ -namespace PHPUnitPHAR\phpDocumentor\Reflection\Types; +namespace PHPUnit\Event\Test; -use PHPUnitPHAR\phpDocumentor\Reflection\Type; +use const PHP_EOL; +use function sprintf; +use PHPUnit\Event\Code\Test; +use PHPUnit\Event\Event; +use PHPUnit\Event\Telemetry; /** - * Value Object representing a Float. - * * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -class Float_ implements Type +final class PhpunitDeprecationTriggered implements Event { + private readonly Telemetry\Info $telemetryInfo; + private readonly Test $test; + /** + * @psalm-var non-empty-string + */ + private readonly string $message; + /** + * @psalm-param non-empty-string $message + */ + public function __construct(Telemetry\Info $telemetryInfo, Test $test, string $message) + { + $this->telemetryInfo = $telemetryInfo; + $this->test = $test; + $this->message = $message; + } + public function telemetryInfo(): Telemetry\Info + { + return $this->telemetryInfo; + } + public function test(): Test + { + return $this->test; + } /** - * Returns a rendered output of the Type as it would be used in a DocBlock. + * @psalm-return non-empty-string */ - public function __toString() : string + public function message(): string { - return 'float'; + return $this->message; + } + public function asString(): string + { + $message = $this->message; + if (!empty($message)) { + $message = PHP_EOL . $message; + } + return sprintf('Test Triggered PHPUnit Deprecation (%s)%s', $this->test->id(), $message); } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. - * - * @link http://phpdoc.org */ -namespace PHPUnitPHAR\phpDocumentor\Reflection\Types; +namespace PHPUnit\Event\Test; -use PHPUnitPHAR\phpDocumentor\Reflection\Type; +use PHPUnit\Event\Subscriber; /** - * Value object representing Integer type - * - * @psalm-immutable + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -class Integer implements Type +interface PhpunitDeprecationTriggeredSubscriber extends Subscriber { - /** - * Returns a rendered output of the Type as it would be used in a DocBlock. - */ - public function __toString() : string - { - return 'int'; - } + public function notify(\PHPUnit\Event\Test\PhpunitDeprecationTriggered $event): void; } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. - * - * @link http://phpdoc.org */ -namespace PHPUnitPHAR\phpDocumentor\Reflection\Types; +namespace PHPUnit\Event\Test; -use PHPUnitPHAR\phpDocumentor\Reflection\Fqsen; -use PHPUnitPHAR\phpDocumentor\Reflection\Type; +use const PHP_EOL; +use function sprintf; +use function trim; +use PHPUnit\Event\Code\Test; +use PHPUnit\Event\Event; +use PHPUnit\Event\Telemetry; /** - * Value Object representing the type 'string'. - * * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -final class InterfaceString implements Type +final class PhpunitErrorTriggered implements Event { - /** @var Fqsen|null */ - private $fqsen; + private readonly Telemetry\Info $telemetryInfo; + private readonly Test $test; /** - * Initializes this representation of a class string with the given Fqsen. + * @psalm-var non-empty-string */ - public function __construct(?Fqsen $fqsen = null) - { - $this->fqsen = $fqsen; - } + private readonly string $message; /** - * Returns the FQSEN associated with this object. + * @psalm-param non-empty-string $message */ - public function getFqsen() : ?Fqsen + public function __construct(Telemetry\Info $telemetryInfo, Test $test, string $message) { - return $this->fqsen; + $this->telemetryInfo = $telemetryInfo; + $this->test = $test; + $this->message = $message; } - /** - * Returns a rendered output of the Type as it would be used in a DocBlock. - */ - public function __toString() : string + public function telemetryInfo(): Telemetry\Info { - if ($this->fqsen === null) { - return 'interface-string'; - } - return 'interface-string<' . (string) $this->fqsen . '>'; + return $this->telemetryInfo; + } + public function test(): Test + { + return $this->test; } -} - $types + * @psalm-return non-empty-string */ - public function __construct(array $types) + public function message(): string { - parent::__construct($types, '&'); + return $this->message; + } + public function asString(): string + { + $message = trim($this->message); + if (!empty($message)) { + $message = PHP_EOL . $message; + } + return sprintf('Test Triggered PHPUnit Error (%s)%s', $this->test->id(), $message); } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. - * - * @link http://phpdoc.org */ -namespace PHPUnitPHAR\phpDocumentor\Reflection\Types; +namespace PHPUnit\Event\Test; +use PHPUnit\Event\Subscriber; /** - * Value Object representing iterable type - * - * @psalm-immutable + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -final class Iterable_ extends AbstractList +interface PhpunitErrorTriggeredSubscriber extends Subscriber { - /** - * Returns a rendered output of the Type as it would be used in a DocBlock. - */ - public function __toString() : string - { - if ($this->keyType) { - return 'iterable<' . $this->keyType . ',' . $this->valueType . '>'; - } - if ($this->valueType instanceof Mixed_) { - return 'iterable'; - } - return 'iterable<' . $this->valueType . '>'; - } + public function notify(\PHPUnit\Event\Test\PhpunitErrorTriggered $event): void; } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. - * - * @link http://phpdoc.org */ -namespace PHPUnitPHAR\phpDocumentor\Reflection\Types; +namespace PHPUnit\Event\Test; -use PHPUnitPHAR\phpDocumentor\Reflection\Type; +use const PHP_EOL; +use function sprintf; +use PHPUnit\Event\Code\Test; +use PHPUnit\Event\Event; +use PHPUnit\Event\Telemetry; /** - * Value Object representing an unknown, or mixed, type. - * * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -final class Mixed_ implements Type +final class PhpunitWarningTriggered implements Event { + private readonly Telemetry\Info $telemetryInfo; + private readonly Test $test; + /** + * @psalm-var non-empty-string + */ + private readonly string $message; /** - * Returns a rendered output of the Type as it would be used in a DocBlock. + * @psalm-param non-empty-string $message */ - public function __toString() : string + public function __construct(Telemetry\Info $telemetryInfo, Test $test, string $message) { - return 'mixed'; + $this->telemetryInfo = $telemetryInfo; + $this->test = $test; + $this->message = $message; + } + public function telemetryInfo(): Telemetry\Info + { + return $this->telemetryInfo; + } + public function test(): Test + { + return $this->test; } -} -message; + } + public function asString(): string + { + $message = $this->message; + if (!empty($message)) { + $message = PHP_EOL . $message; + } + return sprintf('Test Triggered PHPUnit Warning (%s)%s', $this->test->id(), $message); } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. - * - * @link http://phpdoc.org */ -namespace PHPUnitPHAR\phpDocumentor\Reflection\Types; +namespace PHPUnit\Event\Test; -use PHPUnitPHAR\phpDocumentor\Reflection\Type; +use PHPUnit\Event\Subscriber; /** - * Value Object representing a null value or type. - * - * @psalm-immutable + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -final class Null_ implements Type +interface PhpunitWarningTriggeredSubscriber extends Subscriber { - /** - * Returns a rendered output of the Type as it would be used in a DocBlock. - */ - public function __toString() : string - { - return 'null'; - } + public function notify(\PHPUnit\Event\Test\PhpunitWarningTriggered $event): void; } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. - * - * @link http://phpdoc.org */ -namespace PHPUnitPHAR\phpDocumentor\Reflection\Types; +namespace PHPUnit\Event\Test; -use PHPUnitPHAR\phpDocumentor\Reflection\Type; +use const PHP_EOL; +use function sprintf; +use PHPUnit\Event\Code\Test; +use PHPUnit\Event\Event; +use PHPUnit\Event\Telemetry; /** - * Value Object representing a nullable type. The real type is wrapped. - * * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -final class Nullable implements Type +final class WarningTriggered implements Event { - /** @var Type The actual type that is wrapped */ - private $realType; + private readonly Telemetry\Info $telemetryInfo; + private readonly Test $test; /** - * Initialises this nullable type using the real type embedded + * @psalm-var non-empty-string */ - public function __construct(Type $realType) - { - $this->realType = $realType; - } + private readonly string $message; /** - * Provide access to the actual type directly, if needed. + * @psalm-var non-empty-string + */ + private readonly string $file; + /** + * @psalm-var positive-int + */ + private readonly int $line; + private readonly bool $suppressed; + private readonly bool $ignoredByBaseline; + /** + * @psalm-param non-empty-string $message + * @psalm-param non-empty-string $file + * @psalm-param positive-int $line */ - public function getActualType() : Type + public function __construct(Telemetry\Info $telemetryInfo, Test $test, string $message, string $file, int $line, bool $suppressed, bool $ignoredByBaseline) + { + $this->telemetryInfo = $telemetryInfo; + $this->test = $test; + $this->message = $message; + $this->file = $file; + $this->line = $line; + $this->suppressed = $suppressed; + $this->ignoredByBaseline = $ignoredByBaseline; + } + public function telemetryInfo(): Telemetry\Info + { + return $this->telemetryInfo; + } + public function test(): Test { - return $this->realType; + return $this->test; } /** - * Returns a rendered output of the Type as it would be used in a DocBlock. + * @psalm-return non-empty-string */ - public function __toString() : string + public function message(): string { - return '?' . $this->realType->__toString(); + return $this->message; } -} -fqsen = $fqsen; + return $this->file; } /** - * Returns the FQSEN associated with this object. + * @psalm-return positive-int */ - public function getFqsen() : ?Fqsen + public function line(): int + { + return $this->line; + } + public function wasSuppressed(): bool { - return $this->fqsen; + return $this->suppressed; } - public function __toString() : string + public function ignoredByBaseline(): bool { - if ($this->fqsen) { - return (string) $this->fqsen; + return $this->ignoredByBaseline; + } + public function asString(): string + { + $message = $this->message; + if (!empty($message)) { + $message = PHP_EOL . $message; } - return 'object'; + $status = ''; + if ($this->ignoredByBaseline) { + $status = 'Baseline-Ignored '; + } elseif ($this->suppressed) { + $status = 'Suppressed '; + } + return sprintf('Test Triggered %sWarning (%s)%s', $status, $this->test->id(), $message); } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. - * - * @link http://phpdoc.org */ -namespace PHPUnitPHAR\phpDocumentor\Reflection\Types; +namespace PHPUnit\Event\Test; -use PHPUnitPHAR\phpDocumentor\Reflection\Type; +use PHPUnit\Event\Subscriber; /** - * Value Object representing the 'parent' type. - * - * Parent, as a Type, represents the parent class of class in which the associated element was defined. - * - * @psalm-immutable + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -final class Parent_ implements Type +interface WarningTriggeredSubscriber extends Subscriber { - /** - * Returns a rendered output of the Type as it would be used in a DocBlock. - */ - public function __toString() : string - { - return 'parent'; - } + public function notify(\PHPUnit\Event\Test\WarningTriggered $event): void; } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. - * - * @link http://phpdoc.org */ -namespace PHPUnitPHAR\phpDocumentor\Reflection\Types; +namespace PHPUnit\Event\Test; -use PHPUnitPHAR\phpDocumentor\Reflection\Type; +use function sprintf; +use PHPUnit\Event\Code\ClassMethod; +use PHPUnit\Event\Event; +use PHPUnit\Event\Telemetry\Info; /** - * Value Object representing the 'resource' Type. - * * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -final class Resource_ implements Type +final class DataProviderMethodCalled implements Event { - /** - * Returns a rendered output of the Type as it would be used in a DocBlock. - */ - public function __toString() : string + private readonly Info $telemetryInfo; + private readonly ClassMethod $testMethod; + private readonly ClassMethod $dataProviderMethod; + public function __construct(Info $telemetryInfo, ClassMethod $testMethod, ClassMethod $dataProviderMethod) + { + $this->telemetryInfo = $telemetryInfo; + $this->testMethod = $testMethod; + $this->dataProviderMethod = $dataProviderMethod; + } + public function telemetryInfo(): Info + { + return $this->telemetryInfo; + } + public function testMethod(): ClassMethod + { + return $this->testMethod; + } + public function dataProviderMethod(): ClassMethod + { + return $this->dataProviderMethod; + } + public function asString(): string { - return 'resource'; + return sprintf('Data Provider Method Called (%s::%s for test method %s::%s)', $this->dataProviderMethod->className(), $this->dataProviderMethod->methodName(), $this->testMethod->className(), $this->testMethod->methodName()); } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. - * - * @link http://phpdoc.org */ -namespace PHPUnitPHAR\phpDocumentor\Reflection\Types; +namespace PHPUnit\Event\Test; -use PHPUnitPHAR\phpDocumentor\Reflection\Type; +use PHPUnit\Event\Subscriber; /** - * Value Object representing the 'scalar' pseudo-type, which is either a string, integer, float or boolean. - * - * @psalm-immutable + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -final class Scalar implements Type +interface DataProviderMethodCalledSubscriber extends Subscriber { - /** - * Returns a rendered output of the Type as it would be used in a DocBlock. - */ - public function __toString() : string - { - return 'scalar'; - } + public function notify(\PHPUnit\Event\Test\DataProviderMethodCalled $event): void; } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. - * - * @link http://phpdoc.org */ -namespace PHPUnitPHAR\phpDocumentor\Reflection\Types; +namespace PHPUnit\Event\Test; -use PHPUnitPHAR\phpDocumentor\Reflection\Type; +use const PHP_EOL; +use function sprintf; +use PHPUnit\Event\Code; +use PHPUnit\Event\Code\ClassMethod; +use PHPUnit\Event\Event; +use PHPUnit\Event\Telemetry; /** - * Value Object representing the 'self' type. - * - * Self, as a Type, represents the class in which the associated element was defined. - * * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -final class Self_ implements Type +final class DataProviderMethodFinished implements Event { + private readonly Telemetry\Info $telemetryInfo; + private readonly ClassMethod $testMethod; + /** + * @psalm-var list + */ + private readonly array $calledMethods; + public function __construct(Telemetry\Info $telemetryInfo, ClassMethod $testMethod, ClassMethod ...$calledMethods) + { + $this->telemetryInfo = $telemetryInfo; + $this->testMethod = $testMethod; + $this->calledMethods = $calledMethods; + } + public function telemetryInfo(): Telemetry\Info + { + return $this->telemetryInfo; + } + public function testMethod(): ClassMethod + { + return $this->testMethod; + } /** - * Returns a rendered output of the Type as it would be used in a DocBlock. + * @psalm-return list */ - public function __toString() : string + public function calledMethods(): array { - return 'self'; + return $this->calledMethods; + } + public function asString(): string + { + $buffer = sprintf('Data Provider Method Finished for %s::%s:', $this->testMethod->className(), $this->testMethod->methodName()); + foreach ($this->calledMethods as $calledMethod) { + $buffer .= sprintf(PHP_EOL . '- %s::%s', $calledMethod->className(), $calledMethod->methodName()); + } + return $buffer; } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. - * - * @link http://phpdoc.org */ -namespace PHPUnitPHAR\phpDocumentor\Reflection\Types; +namespace PHPUnit\Event\Test; -use PHPUnitPHAR\phpDocumentor\Reflection\Type; +use PHPUnit\Event\Subscriber; /** - * Value Object representing the 'static' type. - * - * Self, as a Type, represents the class in which the associated element was called. This differs from self as self does - * not take inheritance into account but static means that the return type is always that of the class of the called - * element. - * - * See the documentation on late static binding in the PHP Documentation for more information on the difference between - * static and self. - * - * @psalm-immutable + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -final class Static_ implements Type +interface DataProviderMethodFinishedSubscriber extends Subscriber { - /** - * Returns a rendered output of the Type as it would be used in a DocBlock. - */ - public function __toString() : string - { - return 'static'; - } + public function notify(\PHPUnit\Event\Test\DataProviderMethodFinished $event): void; } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. - * - * @link http://phpdoc.org */ -namespace PHPUnitPHAR\phpDocumentor\Reflection\Types; +namespace PHPUnit\Event\Test; -use PHPUnitPHAR\phpDocumentor\Reflection\Type; +use function sprintf; +use PHPUnit\Event\Code; +use PHPUnit\Event\Event; +use PHPUnit\Event\Telemetry; /** - * Value Object representing the type 'string'. - * * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -class String_ implements Type +final class Finished implements Event { - /** - * Returns a rendered output of the Type as it would be used in a DocBlock. - */ - public function __toString() : string + private readonly Telemetry\Info $telemetryInfo; + private readonly Code\Test $test; + private readonly int $numberOfAssertionsPerformed; + public function __construct(Telemetry\Info $telemetryInfo, Code\Test $test, int $numberOfAssertionsPerformed) { - return 'string'; + $this->telemetryInfo = $telemetryInfo; + $this->test = $test; + $this->numberOfAssertionsPerformed = $numberOfAssertionsPerformed; + } + public function telemetryInfo(): Telemetry\Info + { + return $this->telemetryInfo; + } + public function test(): Code\Test + { + return $this->test; + } + public function numberOfAssertionsPerformed(): int + { + return $this->numberOfAssertionsPerformed; + } + public function asString(): string + { + return sprintf('Test Finished (%s)', $this->test->id()); } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. - * - * @link http://phpdoc.org */ -namespace PHPUnitPHAR\phpDocumentor\Reflection\Types; +namespace PHPUnit\Event\Test; -use PHPUnitPHAR\phpDocumentor\Reflection\Type; +use PHPUnit\Event\Subscriber; /** - * Value Object representing the '$this' pseudo-type. - * - * $this, as a Type, represents the instance of the class associated with the element as it was called. $this is - * commonly used when documenting fluent interfaces since it represents that the same object is returned. - * - * @psalm-immutable + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -final class This implements Type +interface FinishedSubscriber extends Subscriber { - /** - * Returns a rendered output of the Type as it would be used in a DocBlock. - */ - public function __toString() : string - { - return '$this'; - } + public function notify(\PHPUnit\Event\Test\Finished $event): void; } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. - * - * @link http://phpdoc.org */ -namespace PHPUnitPHAR\phpDocumentor\Reflection\Types; +namespace PHPUnit\Event\Test; -use PHPUnitPHAR\phpDocumentor\Reflection\Type; +use function sprintf; +use PHPUnit\Event\Code; +use PHPUnit\Event\Event; +use PHPUnit\Event\Telemetry; /** - * Value Object representing the return-type 'void'. - * - * Void is generally only used when working with return types as it signifies that the method intentionally does not - * return any value. - * * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -final class Void_ implements Type +final class PreparationFailed implements Event { - /** - * Returns a rendered output of the Type as it would be used in a DocBlock. - */ - public function __toString() : string + private readonly Telemetry\Info $telemetryInfo; + private readonly Code\Test $test; + public function __construct(Telemetry\Info $telemetryInfo, Code\Test $test) { - return 'void'; + $this->telemetryInfo = $telemetryInfo; + $this->test = $test; + } + public function telemetryInfo(): Telemetry\Info + { + return $this->telemetryInfo; + } + public function test(): Code\Test + { + return $this->test; + } + public function asString(): string + { + return sprintf('Test Preparation Failed (%s)', $this->test->id()); } } -Copyright (c) 2013 Konstantin Kudryashov -Copyright (c) 2013 Marcello Duarte - -Permission is hereby granted, free of charge, to any person -obtaining a copy of this software and associated documentation -files (the "Software"), to deal in the Software without -restriction, including without limitation the rights to use, -copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the -Software is furnished to do so, subject to the following -conditions: + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\Test; -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -OTHER DEALINGS IN THE SOFTWARE. +use PHPUnit\Event\Subscriber; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +interface PreparationFailedSubscriber extends Subscriber +{ + public function notify(\PHPUnit\Event\Test\PreparationFailed $event): void; +} - * Marcello Duarte + * This file is part of PHPUnit. + * + * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace Prophecy; +namespace PHPUnit\Event\Test; -use Prophecy\Argument\Token; +use function sprintf; +use PHPUnit\Event\Code; +use PHPUnit\Event\Event; +use PHPUnit\Event\Telemetry; /** - * Argument tokens shortcuts. + * @psalm-immutable * - * @author Konstantin Kudryashov + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -class Argument +final class PreparationStarted implements Event { - /** - * Checks that argument is exact value or object. - * - * @param mixed $value - * - * @return Token\ExactValueToken - */ - public static function exact($value) - { - return new Token\ExactValueToken($value); - } - /** - * Checks that argument is of specific type or instance of specific class. - * - * @param string $type Type name (`integer`, `string`) or full class name - * - * @return Token\TypeToken - */ - public static function type($type) - { - return new Token\TypeToken($type); - } - /** - * Checks that argument object has specific state. - * - * @param string $methodName - * @param mixed $value - * - * @return Token\ObjectStateToken - */ - public static function which($methodName, $value) - { - return new Token\ObjectStateToken($methodName, $value); - } - /** - * Checks that argument matches provided callback. - * - * @param callable $callback - * @param string|null $customStringRepresentation Customize the __toString() representation of this token - * - * @return Token\CallbackToken - */ - public static function that($callback, ?string $customStringRepresentation = null) - { - return new Token\CallbackToken($callback, $customStringRepresentation); - } - /** - * Matches any single value. - * - * @return Token\AnyValueToken - */ - public static function any() - { - return new Token\AnyValueToken(); - } - /** - * Matches all values to the rest of the signature. - * - * @return Token\AnyValuesToken - */ - public static function cetera() - { - return new Token\AnyValuesToken(); - } - /** - * Checks that argument matches all tokens - * - * @param mixed ...$tokens a list of tokens - * - * @return Token\LogicalAndToken - */ - public static function allOf(...$tokens) - { - return new Token\LogicalAndToken($tokens); - } - /** - * Checks that argument array or countable object has exact number of elements. - * - * @param integer $value array elements count - * - * @return Token\ArrayCountToken - */ - public static function size($value) - { - return new Token\ArrayCountToken($value); - } - /** - * Checks that argument array contains (key, value) pair - * - * @param mixed $key exact value or token - * @param mixed $value exact value or token - * - * @return Token\ArrayEntryToken - */ - public static function withEntry($key, $value) - { - return new Token\ArrayEntryToken($key, $value); - } - /** - * Checks that arguments array entries all match value - * - * @param mixed $value - * - * @return Token\ArrayEveryEntryToken - */ - public static function withEveryEntry($value) - { - return new Token\ArrayEveryEntryToken($value); - } - /** - * Checks that argument array contains value - * - * @param mixed $value - * - * @return Token\ArrayEntryToken - */ - public static function containing($value) - { - return new Token\ArrayEntryToken(self::any(), $value); - } - /** - * Checks that argument array has key - * - * @param mixed $key exact value or token - * - * @return Token\ArrayEntryToken - */ - public static function withKey($key) - { - return new Token\ArrayEntryToken($key, self::any()); - } - /** - * Checks that argument does not match the value|token. - * - * @param mixed $value either exact value or argument token - * - * @return Token\LogicalNotToken - */ - public static function not($value) - { - return new Token\LogicalNotToken($value); - } - /** - * @param string $value - * - * @return Token\StringContainsToken - */ - public static function containingString($value) - { - return new Token\StringContainsToken($value); - } - /** - * Checks that argument is identical value. - * - * @param mixed $value - * - * @return Token\IdenticalValueToken - */ - public static function is($value) + private readonly Telemetry\Info $telemetryInfo; + private readonly Code\Test $test; + public function __construct(Telemetry\Info $telemetryInfo, Code\Test $test) { - return new Token\IdenticalValueToken($value); + $this->telemetryInfo = $telemetryInfo; + $this->test = $test; } - /** - * Check that argument is same value when rounding to the - * given precision. - * - * @param float $value - * @param int $precision - * - * @return Token\ApproximateValueToken - */ - public static function approximate($value, $precision = 0) + public function telemetryInfo(): Telemetry\Info { - return new Token\ApproximateValueToken($value, $precision); + return $this->telemetryInfo; } - /** - * Checks that argument is in array. - * - * @param array $value - * - * @return Token\InArrayToken - */ - public static function in($value) + public function test(): Code\Test { - return new Token\InArrayToken($value); + return $this->test; } - /** - * Checks that argument is not in array. - * - * @param array $value - * - * @return Token\NotInArrayToken - */ - public static function notIn($value) + public function asString(): string { - return new Token\NotInArrayToken($value); + return sprintf('Test Preparation Started (%s)', $this->test->id()); } } - * Marcello Duarte + * This file is part of PHPUnit. + * + * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace Prophecy\Argument; +namespace PHPUnit\Event\Test; +use PHPUnit\Event\Subscriber; /** - * Arguments wildcarding. - * - * @author Konstantin Kudryashov + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -class ArgumentsWildcard +interface PreparationStartedSubscriber extends Subscriber { - /** - * @var list - */ - private $tokens = array(); - /** - * @var string|null - */ - private $string; - /** - * Initializes wildcard. - * - * @param array $arguments Array of argument tokens or values - */ - public function __construct(array $arguments) - { - foreach ($arguments as $argument) { - if (!$argument instanceof \Prophecy\Argument\Token\TokenInterface) { - $argument = new \Prophecy\Argument\Token\ExactValueToken($argument); - } - $this->tokens[] = $argument; - } - } - /** - * Calculates wildcard match score for provided arguments. - * - * @param array $arguments - * - * @return false|int False OR integer score (higher - better) - */ - public function scoreArguments(array $arguments) - { - if (0 == \count($arguments) && 0 == \count($this->tokens)) { - return 1; - } - $arguments = \array_values($arguments); - $totalScore = 0; - foreach ($this->tokens as $i => $token) { - $argument = isset($arguments[$i]) ? $arguments[$i] : null; - if (1 >= ($score = $token->scoreArgument($argument))) { - return \false; - } - $totalScore += $score; - if (\true === $token->isLast()) { - return $totalScore; - } - } - if (\count($arguments) > \count($this->tokens)) { - return \false; - } - return $totalScore; - } - /** - * Returns string representation for wildcard. - * - * @return string - */ - public function __toString() - { - if (null === $this->string) { - $this->string = \implode(', ', \array_map(function ($token) { - return (string) $token; - }, $this->tokens)); - } - return $this->string; - } - /** - * @return list - */ - public function getTokens() - { - return $this->tokens; - } + public function notify(\PHPUnit\Event\Test\PreparationStarted $event): void; } - * Marcello Duarte + * This file is part of PHPUnit. + * + * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace Prophecy\Argument\Token; +namespace PHPUnit\Event\Test; +use function sprintf; +use PHPUnit\Event\Code; +use PHPUnit\Event\Event; +use PHPUnit\Event\Telemetry; /** - * Any single value token. + * @psalm-immutable * - * @author Konstantin Kudryashov + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -class AnyValueToken implements \Prophecy\Argument\Token\TokenInterface +final class Prepared implements Event { - /** - * Always scores 3 for any argument. - * - * @param mixed $argument - * - * @return int - */ - public function scoreArgument($argument) + private readonly Telemetry\Info $telemetryInfo; + private readonly Code\Test $test; + public function __construct(Telemetry\Info $telemetryInfo, Code\Test $test) { - return 3; + $this->telemetryInfo = $telemetryInfo; + $this->test = $test; } - /** - * Returns false. - * - * @return bool - */ - public function isLast() + public function telemetryInfo(): Telemetry\Info { - return \false; + return $this->telemetryInfo; } - /** - * Returns string representation for token. - * - * @return string - */ - public function __toString() + public function test(): Code\Test { - return '*'; + return $this->test; + } + public function asString(): string + { + return sprintf('Test Prepared (%s)', $this->test->id()); } } - * Marcello Duarte + * This file is part of PHPUnit. + * + * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace Prophecy\Argument\Token; +namespace PHPUnit\Event\Test; +use PHPUnit\Event\Subscriber; /** - * Any values token. - * - * @author Konstantin Kudryashov + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -class AnyValuesToken implements \Prophecy\Argument\Token\TokenInterface +interface PreparedSubscriber extends Subscriber { - /** - * Always scores 2 for any argument. - * - * @param $argument - * - * @return int - */ - public function scoreArgument($argument) - { - return 2; - } - /** - * Returns true to stop wildcard from processing other tokens. - * - * @return bool - */ - public function isLast() - { - return \true; - } - /** - * Returns string representation for token. - * - * @return string - */ - public function __toString() - { - return '* [, ...]'; - } + public function notify(\PHPUnit\Event\Test\Prepared $event): void; } - * Marcello Duarte + * This file is part of PHPUnit. + * + * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace Prophecy\Argument\Token; +namespace PHPUnit\Event\Test; +use const PHP_EOL; +use function sprintf; +use function trim; +use PHPUnit\Event\Code; +use PHPUnit\Event\Code\Throwable; +use PHPUnit\Event\Event; +use PHPUnit\Event\Telemetry; /** - * Approximate value token + * @psalm-immutable * - * @author Daniel Leech + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -class ApproximateValueToken implements \Prophecy\Argument\Token\TokenInterface +final class Errored implements Event { - private $value; - private $precision; - /** - * @param float $value - * @param int $precision - */ - public function __construct($value, $precision = 0) + private readonly Telemetry\Info $telemetryInfo; + private readonly Code\Test $test; + private readonly Throwable $throwable; + public function __construct(Telemetry\Info $telemetryInfo, Code\Test $test, Throwable $throwable) { - $this->value = $value; - $this->precision = $precision; + $this->telemetryInfo = $telemetryInfo; + $this->test = $test; + $this->throwable = $throwable; } - /** - * {@inheritdoc} - */ - public function scoreArgument($argument) + public function telemetryInfo(): Telemetry\Info { - if (!\is_float($argument) && !\is_int($argument) && !\is_numeric($argument)) { - return \false; - } - return \round((float) $argument, $this->precision) === \round($this->value, $this->precision) ? 10 : \false; + return $this->telemetryInfo; } - /** - * {@inheritdoc} - */ - public function isLast() + public function test(): Code\Test { - return \false; + return $this->test; } - /** - * Returns string representation for token. - * - * @return string - */ - public function __toString() + public function throwable(): Throwable { - return \sprintf('≅%s', \round($this->value, $this->precision)); + return $this->throwable; + } + public function asString(): string + { + $message = trim($this->throwable->message()); + if (!empty($message)) { + $message = PHP_EOL . $message; + } + return sprintf('Test Errored (%s)%s', $this->test->id(), $message); } } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\Test; + +use PHPUnit\Event\Subscriber; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +interface ErroredSubscriber extends Subscriber +{ + public function notify(\PHPUnit\Event\Test\Errored $event): void; +} + - * Marcello Duarte + * This file is part of PHPUnit. + * + * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace Prophecy\Argument\Token; +namespace PHPUnit\Event\Test; +use const PHP_EOL; +use function sprintf; +use function trim; +use PHPUnit\Event\Code; +use PHPUnit\Event\Code\ComparisonFailure; +use PHPUnit\Event\Code\Throwable; +use PHPUnit\Event\Event; +use PHPUnit\Event\Telemetry; /** - * Array elements count token. + * @psalm-immutable * - * @author Boris Mikhaylov + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -class ArrayCountToken implements \Prophecy\Argument\Token\TokenInterface +final class Failed implements Event { - private $count; - /** - * @param integer $value - */ - public function __construct($value) + private readonly Telemetry\Info $telemetryInfo; + private readonly Code\Test $test; + private readonly Throwable $throwable; + private readonly ?ComparisonFailure $comparisonFailure; + public function __construct(Telemetry\Info $telemetryInfo, Code\Test $test, Throwable $throwable, ?ComparisonFailure $comparisonFailure) { - $this->count = $value; + $this->telemetryInfo = $telemetryInfo; + $this->test = $test; + $this->throwable = $throwable; + $this->comparisonFailure = $comparisonFailure; } - /** - * Scores 6 when argument has preset number of elements. - * - * @param mixed $argument - * - * @return false|int - */ - public function scoreArgument($argument) + public function telemetryInfo(): Telemetry\Info { - return $this->isCountable($argument) && $this->hasProperCount($argument) ? 6 : \false; + return $this->telemetryInfo; } - /** - * Returns false. - * - * @return boolean - */ - public function isLast() + public function test(): Code\Test { - return \false; + return $this->test; } - /** - * Returns string representation for token. - * - * @return string - */ - public function __toString() + public function throwable(): Throwable { - return \sprintf('count(%s)', $this->count); + return $this->throwable; } /** - * Returns true if object is either array or instance of \Countable - * - * @param mixed $argument - * @return bool - * - * @phpstan-assert-if-true array|\Countable $argument + * @psalm-assert-if-true !null $this->comparisonFailure */ - private function isCountable($argument) + public function hasComparisonFailure(): bool { - return \is_array($argument) || $argument instanceof \Countable; + return $this->comparisonFailure !== null; } /** - * Returns true if $argument has expected number of elements - * - * @param array|\Countable $argument - * - * @return bool + * @throws NoComparisonFailureException */ - private function hasProperCount($argument) + public function comparisonFailure(): ComparisonFailure + { + if ($this->comparisonFailure === null) { + throw new \PHPUnit\Event\Test\NoComparisonFailureException(); + } + return $this->comparisonFailure; + } + public function asString(): string { - return $this->count === \count($argument); + $message = trim($this->throwable->message()); + if (!empty($message)) { + $message = PHP_EOL . $message; + } + return sprintf('Test Failed (%s)%s', $this->test->id(), $message); } } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\Test; + +use PHPUnit\Event\Subscriber; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +interface FailedSubscriber extends Subscriber +{ + public function notify(\PHPUnit\Event\Test\Failed $event): void; +} + - * Marcello Duarte + * This file is part of PHPUnit. + * + * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace Prophecy\Argument\Token; +namespace PHPUnit\Event\Test; -use Prophecy\Exception\InvalidArgumentException; +use const PHP_EOL; +use function sprintf; +use function trim; +use PHPUnit\Event\Code; +use PHPUnit\Event\Code\Throwable; +use PHPUnit\Event\Event; +use PHPUnit\Event\Telemetry; /** - * Array entry token. + * @psalm-immutable * - * @author Boris Mikhaylov + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -class ArrayEntryToken implements \Prophecy\Argument\Token\TokenInterface +final class MarkedIncomplete implements Event { - /** @var TokenInterface */ - private $key; - /** @var TokenInterface */ - private $value; - /** - * @param mixed $key exact value or token - * @param mixed $value exact value or token - */ - public function __construct($key, $value) + private readonly Telemetry\Info $telemetryInfo; + private readonly Code\Test $test; + private readonly Throwable $throwable; + public function __construct(Telemetry\Info $telemetryInfo, Code\Test $test, Throwable $throwable) { - $this->key = $this->wrapIntoExactValueToken($key); - $this->value = $this->wrapIntoExactValueToken($value); + $this->telemetryInfo = $telemetryInfo; + $this->test = $test; + $this->throwable = $throwable; } - /** - * Scores half of combined scores from key and value tokens for same entry. Capped at 8. - * If argument implements \ArrayAccess without \Traversable, then key token is restricted to ExactValueToken. - * - * @param mixed $argument - * - * @throws InvalidArgumentException - * @return false|int - */ - public function scoreArgument($argument) - { - if ($argument instanceof \Traversable) { - $argument = \iterator_to_array($argument); - } - if ($argument instanceof \ArrayAccess) { - $argument = $this->convertArrayAccessToEntry($argument); - } - if (!\is_array($argument) || empty($argument)) { - return \false; - } - $keyScores = \array_map(array($this->key, 'scoreArgument'), \array_keys($argument)); - $valueScores = \array_map(array($this->value, 'scoreArgument'), $argument); - /** @var callable(int|false, int|false): (int|false) $scoreEntry */ - $scoreEntry = function ($value, $key) { - return $value && $key ? \min(8, ($key + $value) / 2) : \false; - }; - return \max(\array_map($scoreEntry, $valueScores, $keyScores)); - } - /** - * Returns false. - * - * @return boolean - */ - public function isLast() - { - return \false; - } - /** - * Returns string representation for token. - * - * @return string - */ - public function __toString() - { - return \sprintf('[..., %s => %s, ...]', $this->key, $this->value); - } - /** - * Returns key - * - * @return TokenInterface - */ - public function getKey() + public function telemetryInfo(): Telemetry\Info { - return $this->key; + return $this->telemetryInfo; } - /** - * Returns value - * - * @return TokenInterface - */ - public function getValue() + public function test(): Code\Test { - return $this->value; + return $this->test; } - /** - * Wraps non token $value into ExactValueToken - * - * @param mixed $value - * @return TokenInterface - */ - private function wrapIntoExactValueToken($value) + public function throwable(): Throwable { - return $value instanceof \Prophecy\Argument\Token\TokenInterface ? $value : new \Prophecy\Argument\Token\ExactValueToken($value); + return $this->throwable; } - /** - * Converts instance of \ArrayAccess to key => value array entry - * - * @param \ArrayAccess $object - * - * @return array - * @throws InvalidArgumentException - */ - private function convertArrayAccessToEntry(\ArrayAccess $object) + public function asString(): string { - if (!$this->key instanceof \Prophecy\Argument\Token\ExactValueToken) { - throw new InvalidArgumentException(\sprintf('You can only use exact value tokens to match key of ArrayAccess object' . \PHP_EOL . 'But you used `%s`.', $this->key)); - } - $key = $this->key->getValue(); - if (!\is_int($key) && !\is_string($key)) { - throw new InvalidArgumentException(\sprintf('You can only use integer or string keys to match key of ArrayAccess object' . \PHP_EOL . 'But you used `%s`.', $this->key)); + $message = trim($this->throwable->message()); + if (!empty($message)) { + $message = PHP_EOL . $message; } - return $object->offsetExists($key) ? array($key => $object[$key]) : array(); + return sprintf('Test Marked Incomplete (%s)%s', $this->test->id(), $message); } } - * Marcello Duarte + * This file is part of PHPUnit. + * + * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace Prophecy\Argument\Token; +namespace PHPUnit\Event\Test; +use PHPUnit\Event\Subscriber; /** - * Array every entry token. - * - * @author Adrien Brault + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -class ArrayEveryEntryToken implements \Prophecy\Argument\Token\TokenInterface +interface MarkedIncompleteSubscriber extends Subscriber { - /** - * @var TokenInterface - */ - private $value; - /** - * @param mixed $value exact value or token - */ - public function __construct($value) - { - if (!$value instanceof \Prophecy\Argument\Token\TokenInterface) { - $value = new \Prophecy\Argument\Token\ExactValueToken($value); - } - $this->value = $value; - } - /** - * {@inheritdoc} - */ - public function scoreArgument($argument) - { - if (!$argument instanceof \Traversable && !\is_array($argument)) { - return \false; - } - $scores = array(); - foreach ($argument as $key => $argumentEntry) { - $scores[] = $this->value->scoreArgument($argumentEntry); - } - if (empty($scores) || \in_array(\false, $scores, \true)) { - return \false; - } - return \array_sum($scores) / \count($scores); - } - /** - * {@inheritdoc} - */ - public function isLast() - { - return \false; - } - /** - * {@inheritdoc} - */ - public function __toString() - { - return \sprintf('[%s, ..., %s]', $this->value, $this->value); - } - /** - * @return TokenInterface - */ - public function getValue() - { - return $this->value; - } + public function notify(\PHPUnit\Event\Test\MarkedIncomplete $event): void; } - * Marcello Duarte + * This file is part of PHPUnit. + * + * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace Prophecy\Argument\Token; +namespace PHPUnit\Event\Test; -use Prophecy\Exception\InvalidArgumentException; +use function sprintf; +use PHPUnit\Event\Code; +use PHPUnit\Event\Event; +use PHPUnit\Event\Telemetry; /** - * Callback-verified token. + * @psalm-immutable * - * @author Konstantin Kudryashov + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -class CallbackToken implements \Prophecy\Argument\Token\TokenInterface +final class Passed implements Event { - private $callback; - /** - * @var string|null - */ - private $customStringRepresentation; - /** - * Initializes token. - * - * @param callable $callback - * @param string|null $customStringRepresentation Customize the __toString() representation of this token - * - * @throws \Prophecy\Exception\InvalidArgumentException - */ - public function __construct($callback, ?string $customStringRepresentation = null) + private readonly Telemetry\Info $telemetryInfo; + private readonly Code\Test $test; + public function __construct(Telemetry\Info $telemetryInfo, Code\Test $test) { - if (!\is_callable($callback)) { - throw new InvalidArgumentException(\sprintf('Callable expected as an argument to CallbackToken, but got %s.', \gettype($callback))); - } - $this->callback = $callback; - $this->customStringRepresentation = $customStringRepresentation; + $this->telemetryInfo = $telemetryInfo; + $this->test = $test; } - /** - * Scores 7 if callback returns true, false otherwise. - * - * @param mixed $argument - * - * @return false|int - */ - public function scoreArgument($argument) + public function telemetryInfo(): Telemetry\Info { - return \call_user_func($this->callback, $argument) ? 7 : \false; + return $this->telemetryInfo; } - /** - * Returns false. - * - * @return bool - */ - public function isLast() + public function test(): Code\Test { - return \false; + return $this->test; } - /** - * Returns string representation for token. - * - * @return string - */ - public function __toString() + public function asString(): string { - if ($this->customStringRepresentation !== null) { - return $this->customStringRepresentation; - } - return 'callback()'; + return sprintf('Test Passed (%s)', $this->test->id()); } } - * Marcello Duarte + * This file is part of PHPUnit. + * + * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace Prophecy\Argument\Token; +namespace PHPUnit\Event\Test; -use Prophecy\Comparator\FactoryProvider; -use PHPUnitPHAR\SebastianBergmann\Comparator\ComparisonFailure; -use PHPUnitPHAR\SebastianBergmann\Comparator\Factory as ComparatorFactory; -use Prophecy\Util\StringUtil; +use PHPUnit\Event\Subscriber; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +interface PassedSubscriber extends Subscriber +{ + public function notify(\PHPUnit\Event\Test\Passed $event): void; +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\Test; + +use const PHP_EOL; +use function sprintf; +use PHPUnit\Event\Code; +use PHPUnit\Event\Event; +use PHPUnit\Event\Telemetry; /** - * Exact value token. + * @psalm-immutable * - * @author Konstantin Kudryashov + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -class ExactValueToken implements \Prophecy\Argument\Token\TokenInterface +final class Skipped implements Event { - private $value; - /** - * @var string|null - */ - private $string; - private $util; - private $comparatorFactory; - /** - * Initializes token. - * - * @param mixed $value - */ - public function __construct($value, StringUtil $util = null, ComparatorFactory $comparatorFactory = null) + private readonly Telemetry\Info $telemetryInfo; + private readonly Code\Test $test; + private readonly string $message; + public function __construct(Telemetry\Info $telemetryInfo, Code\Test $test, string $message) { - $this->value = $value; - $this->util = $util ?: new StringUtil(); - $this->comparatorFactory = $comparatorFactory ?: FactoryProvider::getInstance(); + $this->telemetryInfo = $telemetryInfo; + $this->test = $test; + $this->message = $message; } - /** - * Scores 10 if argument matches preset value. - * - * @param mixed $argument - * - * @return false|int - */ - public function scoreArgument($argument) + public function telemetryInfo(): Telemetry\Info { - if (\is_object($argument) && \is_object($this->value)) { - $comparator = $this->comparatorFactory->getComparatorFor($argument, $this->value); - try { - $comparator->assertEquals($argument, $this->value); - return 10; - } catch (ComparisonFailure $failure) { - return \false; - } - } - // If either one is an object it should be castable to a string - if (\is_object($argument) xor \is_object($this->value)) { - if (\is_object($argument) && !\method_exists($argument, '__toString')) { - return \false; - } - if (\is_object($this->value) && !\method_exists($this->value, '__toString')) { - return \false; - } - if (\is_numeric($argument) xor \is_numeric($this->value)) { - return \strval($argument) == \strval($this->value) ? 10 : \false; - } - } elseif (\is_numeric($argument) && \is_numeric($this->value)) { - // noop - } elseif (\gettype($argument) !== \gettype($this->value)) { - return \false; - } - return $argument == $this->value ? 10 : \false; + return $this->telemetryInfo; } - /** - * Returns preset value against which token checks arguments. - * - * @return mixed - */ - public function getValue() + public function test(): Code\Test { - return $this->value; + return $this->test; } - /** - * Returns false. - * - * @return bool - */ - public function isLast() + public function message(): string { - return \false; + return $this->message; } - /** - * Returns string representation for token. - * - * @return string - */ - public function __toString() + public function asString(): string { - if (null === $this->string) { - $this->string = \sprintf('exact(%s)', $this->util->stringify($this->value)); + $message = $this->message; + if (!empty($message)) { + $message = PHP_EOL . $message; } - return $this->string; + return sprintf('Test Skipped (%s)%s', $this->test->id(), $message); } } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\Test; + +use PHPUnit\Event\Subscriber; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +interface SkippedSubscriber extends Subscriber +{ + public function notify(\PHPUnit\Event\Test\Skipped $event): void; +} + - * Marcello Duarte + * This file is part of PHPUnit. + * + * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace Prophecy\Argument\Token; +namespace PHPUnit\Event\Test; -use Prophecy\Util\StringUtil; +use function sprintf; +use PHPUnit\Event\Event; +use PHPUnit\Event\Telemetry; /** - * Identical value token. + * @psalm-immutable * - * @author Florian Voutzinos + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -class IdenticalValueToken implements \Prophecy\Argument\Token\TokenInterface +final class PrintedUnexpectedOutput implements Event { - private $value; + private readonly Telemetry\Info $telemetryInfo; /** - * @var string|null + * @psalm-var non-empty-string */ - private $string; - private $util; + private readonly string $output; /** - * Initializes token. - * - * @param mixed $value + * @psalm-param non-empty-string $output */ - public function __construct($value, StringUtil $util = null) + public function __construct(Telemetry\Info $telemetryInfo, string $output) { - $this->value = $value; - $this->util = $util ?: new StringUtil(); + $this->telemetryInfo = $telemetryInfo; + $this->output = $output; } - /** - * Scores 11 if argument matches preset value. - * - * @param mixed $argument - * - * @return false|int - */ - public function scoreArgument($argument) + public function telemetryInfo(): Telemetry\Info { - return $argument === $this->value ? 11 : \false; + return $this->telemetryInfo; } /** - * Returns false. - * - * @return bool + * @psalm-return non-empty-string */ - public function isLast() + public function output(): string { - return \false; + return $this->output; } - /** - * Returns string representation for token. - * - * @return string - */ - public function __toString() + public function asString(): string { - if (null === $this->string) { - $this->string = \sprintf('identical(%s)', $this->util->stringify($this->value)); - } - return $this->string; + return sprintf('Test Printed Unexpected Output%s%s', \PHP_EOL, $this->output); } } - * Marcello Duarte + * This file is part of PHPUnit. + * + * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace Prophecy\Argument\Token; +namespace PHPUnit\Event\Test; +use PHPUnit\Event\Subscriber; /** - * Check if values is in array - * - * @author Vinícius Alonso + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -class InArrayToken implements \Prophecy\Argument\Token\TokenInterface +interface PrintedUnexpectedOutputSubscriber extends Subscriber { - private $token = array(); - private $strict; - /** - * @param array $arguments tokens - * @param bool $strict - */ - public function __construct(array $arguments, $strict = \true) - { - $this->token = $arguments; - $this->strict = $strict; - } - /** - * Return scores 8 score if argument is in array. - * - * @param $argument - * - * @return bool|int - */ - public function scoreArgument($argument) - { - if (\count($this->token) === 0) { - return \false; - } - if (\in_array($argument, $this->token, $this->strict)) { - return 8; - } - return \false; - } - /** - * Returns false. - * - * @return boolean - */ - public function isLast() - { - return \false; - } - /** - * Returns string representation for token. - * - * @return string - */ - public function __toString() - { - $arrayAsString = \implode(', ', $this->token); - return "[{$arrayAsString}]"; - } + public function notify(\PHPUnit\Event\Test\PrintedUnexpectedOutput $event): void; } - * Marcello Duarte + * This file is part of PHPUnit. + * + * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace Prophecy\Argument\Token; +namespace PHPUnit\Event\Test; +use function sprintf; +use PHPUnit\Event\Event; +use PHPUnit\Event\Telemetry; /** - * Logical AND token. + * @psalm-immutable * - * @author Boris Mikhaylov + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -class LogicalAndToken implements \Prophecy\Argument\Token\TokenInterface +final class MockObjectCreated implements Event { + private readonly Telemetry\Info $telemetryInfo; /** - * @var list + * @psalm-var class-string */ - private $tokens = array(); + private readonly string $className; /** - * @param array $arguments exact values or tokens + * @psalm-param class-string $className */ - public function __construct(array $arguments) + public function __construct(Telemetry\Info $telemetryInfo, string $className) { - foreach ($arguments as $argument) { - if (!$argument instanceof \Prophecy\Argument\Token\TokenInterface) { - $argument = new \Prophecy\Argument\Token\ExactValueToken($argument); - } - $this->tokens[] = $argument; - } + $this->telemetryInfo = $telemetryInfo; + $this->className = $className; } - /** - * Scores maximum score from scores returned by tokens for this argument if all of them score. - * - * @param mixed $argument - * - * @return false|int - */ - public function scoreArgument($argument) + public function telemetryInfo(): Telemetry\Info { - if (0 === \count($this->tokens)) { - return \false; - } - $maxScore = 0; - foreach ($this->tokens as $token) { - $score = $token->scoreArgument($argument); - if (\false === $score) { - return \false; - } - $maxScore = \max($score, $maxScore); - } - return $maxScore; + return $this->telemetryInfo; } /** - * Returns false. - * - * @return boolean + * @psalm-return class-string */ - public function isLast() + public function className(): string { - return \false; + return $this->className; } - /** - * Returns string representation for token. - * - * @return string - */ - public function __toString() + public function asString(): string { - return \sprintf('bool(%s)', \implode(' AND ', $this->tokens)); + return sprintf('Mock Object Created (%s)', $this->className); } } - * Marcello Duarte + * This file is part of PHPUnit. + * + * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace Prophecy\Argument\Token; +namespace PHPUnit\Event\Test; +use PHPUnit\Event\Subscriber; /** - * Logical NOT token. - * - * @author Boris Mikhaylov + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -class LogicalNotToken implements \Prophecy\Argument\Token\TokenInterface +interface MockObjectCreatedSubscriber extends Subscriber { - /** @var TokenInterface */ - private $token; - /** - * @param mixed $value exact value or token - */ - public function __construct($value) - { - $this->token = $value instanceof \Prophecy\Argument\Token\TokenInterface ? $value : new \Prophecy\Argument\Token\ExactValueToken($value); - } - /** - * Scores 4 when preset token does not match the argument. - * - * @param mixed $argument - * - * @return false|int - */ - public function scoreArgument($argument) - { - return \false === $this->token->scoreArgument($argument) ? 4 : \false; - } - /** - * Returns true if preset token is last. - * - * @return bool - */ - public function isLast() - { - return $this->token->isLast(); - } - /** - * Returns originating token. - * - * @return TokenInterface - */ - public function getOriginatingToken() - { - return $this->token; - } - /** - * Returns string representation for token. - * - * @return string - */ - public function __toString() - { - return \sprintf('not(%s)', $this->token); - } + public function notify(\PHPUnit\Event\Test\MockObjectCreated $event): void; } - * Marcello Duarte + * This file is part of PHPUnit. + * + * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace Prophecy\Argument\Token; +namespace PHPUnit\Event\Test; +use function sprintf; +use PHPUnit\Event\Event; +use PHPUnit\Event\Telemetry; /** - * Check if values is not in array + * @psalm-immutable * - * @author Vinícius Alonso + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -class NotInArrayToken implements \Prophecy\Argument\Token\TokenInterface +final class MockObjectForAbstractClassCreated implements Event { - private $token = array(); - private $strict; + private readonly Telemetry\Info $telemetryInfo; /** - * @param array $arguments tokens - * @param bool $strict + * @psalm-var class-string */ - public function __construct(array $arguments, $strict = \true) - { - $this->token = $arguments; - $this->strict = $strict; - } + private readonly string $className; /** - * Return scores 8 score if argument is in array. - * - * @param $argument - * - * @return bool|int + * @psalm-param class-string $className */ - public function scoreArgument($argument) + public function __construct(Telemetry\Info $telemetryInfo, string $className) { - if (\count($this->token) === 0) { - return \false; - } - if (!\in_array($argument, $this->token, $this->strict)) { - return 8; - } - return \false; + $this->telemetryInfo = $telemetryInfo; + $this->className = $className; } - /** - * Returns false. - * - * @return boolean - */ - public function isLast() + public function telemetryInfo(): Telemetry\Info { - return \false; + return $this->telemetryInfo; } /** - * Returns string representation for token. - * - * @return string + * @psalm-return class-string */ - public function __toString() + public function className(): string + { + return $this->className; + } + public function asString(): string { - $arrayAsString = \implode(', ', $this->token); - return "[{$arrayAsString}]"; + return sprintf('Mock Object Created (%s)', $this->className); } } - * Marcello Duarte + * This file is part of PHPUnit. + * + * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace Prophecy\Argument\Token; +namespace PHPUnit\Event\Test; -use Prophecy\Comparator\FactoryProvider; -use PHPUnitPHAR\SebastianBergmann\Comparator\ComparisonFailure; -use PHPUnitPHAR\SebastianBergmann\Comparator\Factory as ComparatorFactory; -use Prophecy\Util\StringUtil; +use PHPUnit\Event\Subscriber; /** - * Object state-checker token. - * - * @author Konstantin Kudryashov + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -class ObjectStateToken implements \Prophecy\Argument\Token\TokenInterface +interface MockObjectForAbstractClassCreatedSubscriber extends Subscriber { - private $name; - private $value; - private $util; - private $comparatorFactory; - /** - * Initializes token. - * - * @param string $methodName - * @param mixed $value Expected return value - */ - public function __construct($methodName, $value, StringUtil $util = null, ComparatorFactory $comparatorFactory = null) - { - $this->name = $methodName; - $this->value = $value; - $this->util = $util ?: new StringUtil(); - $this->comparatorFactory = $comparatorFactory ?: FactoryProvider::getInstance(); - } - /** - * Scores 8 if argument is an object, which method returns expected value. - * - * @param mixed $argument - * - * @return bool|int - */ - public function scoreArgument($argument) - { - $methodCallable = array($argument, $this->name); - if (\is_object($argument) && \method_exists($argument, $this->name) && \is_callable($methodCallable)) { - $actual = \call_user_func($methodCallable); - $comparator = $this->comparatorFactory->getComparatorFor($this->value, $actual); - try { - $comparator->assertEquals($this->value, $actual); - return 8; - } catch (ComparisonFailure $failure) { - return \false; - } - } - if (\is_object($argument) && \property_exists($argument, $this->name)) { - return $argument->{$this->name} === $this->value ? 8 : \false; - } - return \false; - } - /** - * Returns false. - * - * @return bool - */ - public function isLast() - { - return \false; - } - /** - * Returns string representation for token. - * - * @return string - */ - public function __toString() - { - return \sprintf('state(%s(), %s)', $this->name, $this->util->stringify($this->value)); - } + public function notify(\PHPUnit\Event\Test\MockObjectForAbstractClassCreated $event): void; } - * Marcello Duarte + * This file is part of PHPUnit. + * + * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace Prophecy\Argument\Token; +namespace PHPUnit\Event\Test; +use function implode; +use function sprintf; +use PHPUnit\Event\Event; +use PHPUnit\Event\Telemetry; /** - * String contains token. + * @psalm-immutable * - * @author Peter Mitchell + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -class StringContainsToken implements \Prophecy\Argument\Token\TokenInterface +final class MockObjectForIntersectionOfInterfacesCreated implements Event { - private $value; + private readonly Telemetry\Info $telemetryInfo; /** - * Initializes token. - * - * @param string $value + * @psalm-var list */ - public function __construct($value) - { - $this->value = $value; - } - public function scoreArgument($argument) - { - return \is_string($argument) && \strpos($argument, $this->value) !== \false ? 6 : \false; - } + private readonly array $interfaces; /** - * Returns preset value against which token checks arguments. - * - * @return mixed + * @psalm-param list $interfaces */ - public function getValue() + public function __construct(Telemetry\Info $telemetryInfo, array $interfaces) { - return $this->value; + $this->telemetryInfo = $telemetryInfo; + $this->interfaces = $interfaces; } - /** - * Returns false. - * - * @return bool - */ - public function isLast() + public function telemetryInfo(): Telemetry\Info { - return \false; + return $this->telemetryInfo; } /** - * Returns string representation for token. - * - * @return string + * @return list */ - public function __toString() + public function interfaces(): array + { + return $this->interfaces; + } + public function asString(): string { - return \sprintf('contains("%s")', $this->value); + return sprintf('Mock Object Created (%s)', implode('&', $this->interfaces)); } } - * Marcello Duarte + * This file is part of PHPUnit. + * + * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace Prophecy\Argument\Token; +namespace PHPUnit\Event\Test; +use PHPUnit\Event\Subscriber; /** - * Argument token interface. - * - * @author Konstantin Kudryashov + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -interface TokenInterface +interface MockObjectForIntersectionOfInterfacesCreatedSubscriber extends Subscriber { - /** - * Calculates token match score for provided argument. - * - * @param mixed $argument - * - * @return false|int - */ - public function scoreArgument($argument); - /** - * Returns true if this token prevents check of other tokens (is last one). - * - * @return bool - */ - public function isLast(); - /** - * Returns string representation for token. - * - * @return string - */ - public function __toString(); + public function notify(\PHPUnit\Event\Test\MockObjectForIntersectionOfInterfacesCreated $event): void; } - * Marcello Duarte + * This file is part of PHPUnit. + * + * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace Prophecy\Argument\Token; +namespace PHPUnit\Event\Test; -use Prophecy\Exception\InvalidArgumentException; +use function sprintf; +use PHPUnit\Event\Event; +use PHPUnit\Event\Telemetry; /** - * Value type token. + * @psalm-immutable * - * @author Konstantin Kudryashov + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -class TypeToken implements \Prophecy\Argument\Token\TokenInterface +final class MockObjectForTraitCreated implements Event { - private $type; + private readonly Telemetry\Info $telemetryInfo; /** - * @param string $type + * @psalm-var trait-string */ - public function __construct($type) - { - $checker = "is_{$type}"; - if (!\function_exists($checker) && !\interface_exists($type) && !\class_exists($type)) { - throw new InvalidArgumentException(\sprintf('Type or class name expected as an argument to TypeToken, but got %s.', $type)); - } - $this->type = $type; - } + private readonly string $traitName; /** - * Scores 5 if argument has the same type this token was constructed with. - * - * @param $argument - * - * @return bool|int + * @psalm-param trait-string $traitName */ - public function scoreArgument($argument) + public function __construct(Telemetry\Info $telemetryInfo, string $traitName) { - $checker = "is_{$this->type}"; - if (\function_exists($checker)) { - return \call_user_func($checker, $argument) ? 5 : \false; - } - return $argument instanceof $this->type ? 5 : \false; + $this->telemetryInfo = $telemetryInfo; + $this->traitName = $traitName; } - /** - * Returns false. - * - * @return bool - */ - public function isLast() + public function telemetryInfo(): Telemetry\Info { - return \false; + return $this->telemetryInfo; } /** - * Returns string representation for token. - * - * @return string + * @psalm-return trait-string */ - public function __toString() + public function traitName(): string + { + return $this->traitName; + } + public function asString(): string { - return \sprintf('type(%s)', $this->type); + return sprintf('Mock Object Created (%s)', $this->traitName); } } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\Test; + +use PHPUnit\Event\Subscriber; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +interface MockObjectForTraitCreatedSubscriber extends Subscriber +{ + public function notify(\PHPUnit\Event\Test\MockObjectForTraitCreated $event): void; +} + - * Marcello Duarte + * This file is part of PHPUnit. + * + * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace Prophecy\Call; +namespace PHPUnit\Event\Test; -use Exception; -use Prophecy\Argument\ArgumentsWildcard; +use function sprintf; +use PHPUnit\Event\Event; +use PHPUnit\Event\Telemetry; /** - * Call object. + * @psalm-immutable * - * @author Konstantin Kudryashov + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -class Call +final class MockObjectFromWsdlCreated implements Event { - private $methodName; - private $arguments; - private $returnValue; - private $exception; + private readonly Telemetry\Info $telemetryInfo; + private readonly string $wsdlFile; /** - * @var string|null + * @psalm-var class-string */ - private $file; + private readonly string $originalClassName; /** - * @var int|null + * @psalm-var class-string */ - private $line; + private readonly string $mockClassName; /** - * @var \SplObjectStorage + * @psalm-var list */ - private $scores; + private readonly array $methods; + private readonly bool $callOriginalConstructor; + private readonly array $options; /** - * Initializes call. - * - * @param string $methodName - * @param array $arguments - * @param mixed $returnValue - * @param Exception|null $exception - * @param null|string $file - * @param null|int $line + * @psalm-param class-string $originalClassName + * @psalm-param class-string $mockClassName */ - public function __construct($methodName, array $arguments, $returnValue, Exception $exception = null, $file, $line) + public function __construct(Telemetry\Info $telemetryInfo, string $wsdlFile, string $originalClassName, string $mockClassName, array $methods, bool $callOriginalConstructor, array $options) { - $this->methodName = $methodName; - $this->arguments = $arguments; - $this->returnValue = $returnValue; - $this->exception = $exception; - $this->scores = new \SplObjectStorage(); - if ($file) { - $this->file = $file; - $this->line = \intval($line); - } + $this->telemetryInfo = $telemetryInfo; + $this->wsdlFile = $wsdlFile; + $this->originalClassName = $originalClassName; + $this->mockClassName = $mockClassName; + $this->methods = $methods; + $this->callOriginalConstructor = $callOriginalConstructor; + $this->options = $options; } - /** - * Returns called method name. - * - * @return string - */ - public function getMethodName() + public function telemetryInfo(): Telemetry\Info { - return $this->methodName; + return $this->telemetryInfo; } - /** - * Returns called method arguments. - * - * @return array - */ - public function getArguments() + public function wsdlFile(): string { - return $this->arguments; + return $this->wsdlFile; } /** - * Returns called method return value. - * - * @return null|mixed + * @psalm-return class-string */ - public function getReturnValue() + public function originalClassName(): string { - return $this->returnValue; + return $this->originalClassName; } /** - * Returns exception that call thrown. - * - * @return null|Exception + * @psalm-return class-string */ - public function getException() + public function mockClassName(): string { - return $this->exception; + return $this->mockClassName; } /** - * Returns callee filename. - * - * @return string|null + * @psalm-return list */ - public function getFile() + public function methods(): array { - return $this->file; + return $this->methods; } - /** - * Returns callee line number. - * - * @return int|null - */ - public function getLine() + public function callOriginalConstructor(): bool { - return $this->line; + return $this->callOriginalConstructor; } - /** - * Returns short notation for callee place. - * - * @return string - */ - public function getCallPlace() + public function options(): array { - if (null === $this->file) { - return 'unknown'; - } - return \sprintf('%s:%d', $this->file, $this->line); + return $this->options; } - /** - * Adds the wildcard match score for the provided wildcard. - * - * @param ArgumentsWildcard $wildcard - * @param false|int $score - * - * @return $this - */ - public function addScore(ArgumentsWildcard $wildcard, $score) + public function asString(): string { - $this->scores[$wildcard] = $score; - return $this; - } - /** - * Returns wildcard match score for the provided wildcard. The score is - * calculated if not already done. - * - * @param ArgumentsWildcard $wildcard - * - * @return false|int False OR integer score (higher - better) - */ - public function getScore(ArgumentsWildcard $wildcard) - { - if (isset($this->scores[$wildcard])) { - return $this->scores[$wildcard]; - } - return $this->scores[$wildcard] = $wildcard->scoreArguments($this->getArguments()); + return sprintf('Mock Object Created (%s)', $this->wsdlFile); } } - * Marcello Duarte + * This file is part of PHPUnit. + * + * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace Prophecy\Call; +namespace PHPUnit\Event\Test; -use Prophecy\Exception\Prophecy\MethodProphecyException; -use Prophecy\Prophecy\MethodProphecy; -use Prophecy\Prophecy\ObjectProphecy; -use Prophecy\Argument\ArgumentsWildcard; -use Prophecy\Util\StringUtil; -use Prophecy\Exception\Call\UnexpectedCallException; -use SplObjectStorage; +use PHPUnit\Event\Subscriber; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +interface MockObjectFromWsdlCreatedSubscriber extends Subscriber +{ + public function notify(\PHPUnit\Event\Test\MockObjectFromWsdlCreated $event): void; +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\Test; + +use function sprintf; +use PHPUnit\Event\Event; +use PHPUnit\Event\Telemetry; /** - * Calls receiver & manager. + * @psalm-immutable * - * @author Konstantin Kudryashov + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -class CallCenter +final class PartialMockObjectCreated implements Event { - private $util; - /** - * @var Call[] - */ - private $recordedCalls = array(); + private readonly Telemetry\Info $telemetryInfo; /** - * @var SplObjectStorage> - */ - private $unexpectedCalls; - /** - * Initializes call center. - * - * @param StringUtil $util + * @psalm-var class-string */ - public function __construct(StringUtil $util = null) - { - $this->util = $util ?: new StringUtil(); - $this->unexpectedCalls = new SplObjectStorage(); - } + private readonly string $className; /** - * Makes and records specific method call for object prophecy. - * - * @param ObjectProphecy $prophecy - * @param string $methodName - * @param array $arguments - * - * @return mixed Returns null if no promise for prophecy found or promise return value. - * - * @throws \Prophecy\Exception\Call\UnexpectedCallException If no appropriate method prophecy found + * @psalm-var list */ - public function makeCall(ObjectProphecy $prophecy, $methodName, array $arguments) - { - // For efficiency exclude 'args' from the generated backtrace - // Limit backtrace to last 3 calls as we don't use the rest - $backtrace = \debug_backtrace(\DEBUG_BACKTRACE_IGNORE_ARGS, 3); - $file = $line = null; - if (isset($backtrace[2]) && isset($backtrace[2]['file']) && isset($backtrace[2]['line'])) { - $file = $backtrace[2]['file']; - $line = $backtrace[2]['line']; - } - // If no method prophecies defined, then it's a dummy, so we'll just return null - if ('__destruct' === \strtolower($methodName) || 0 == \count($prophecy->getMethodProphecies())) { - $this->recordedCalls[] = new \Prophecy\Call\Call($methodName, $arguments, null, null, $file, $line); - return null; - } - // There are method prophecies, so it's a fake/stub. Searching prophecy for this call - $matches = $this->findMethodProphecies($prophecy, $methodName, $arguments); - // If fake/stub doesn't have method prophecy for this call - throw exception - if (!\count($matches)) { - $this->unexpectedCalls->attach(new \Prophecy\Call\Call($methodName, $arguments, null, null, $file, $line), $prophecy); - $this->recordedCalls[] = new \Prophecy\Call\Call($methodName, $arguments, null, null, $file, $line); - return null; - } - // Sort matches by their score value - @\usort($matches, function ($match1, $match2) { - return $match2[0] - $match1[0]; - }); - $score = $matches[0][0]; - // If Highest rated method prophecy has a promise - execute it or return null instead - $methodProphecy = $matches[0][1]; - $returnValue = null; - $exception = null; - if ($promise = $methodProphecy->getPromise()) { - try { - $returnValue = $promise->execute($arguments, $prophecy, $methodProphecy); - } catch (\Exception $e) { - $exception = $e; - } - } - if ($methodProphecy->hasReturnVoid() && $returnValue !== null) { - throw new MethodProphecyException("The method \"{$methodName}\" has a void return type, but the promise returned a value", $methodProphecy); - } - $this->recordedCalls[] = $call = new \Prophecy\Call\Call($methodName, $arguments, $returnValue, $exception, $file, $line); - $call->addScore($methodProphecy->getArgumentsWildcard(), $score); - if (null !== $exception) { - throw $exception; - } - return $returnValue; - } + private readonly array $methodNames; /** - * Searches for calls by method name & arguments wildcard. - * - * @param string $methodName - * @param ArgumentsWildcard $wildcard - * - * @return list + * @psalm-param class-string $className */ - public function findCalls($methodName, ArgumentsWildcard $wildcard) + public function __construct(Telemetry\Info $telemetryInfo, string $className, string ...$methodNames) { - $methodName = \strtolower($methodName); - return \array_values(\array_filter($this->recordedCalls, function (\Prophecy\Call\Call $call) use($methodName, $wildcard) { - return $methodName === \strtolower($call->getMethodName()) && 0 < $call->getScore($wildcard); - })); + $this->telemetryInfo = $telemetryInfo; + $this->className = $className; + $this->methodNames = $methodNames; } - /** - * @return void - * @throws UnexpectedCallException - */ - public function checkUnexpectedCalls() + public function telemetryInfo(): Telemetry\Info { - foreach ($this->unexpectedCalls as $call) { - $prophecy = $this->unexpectedCalls[$call]; - // If fake/stub doesn't have method prophecy for this call - throw exception - if (!\count($this->findMethodProphecies($prophecy, $call->getMethodName(), $call->getArguments()))) { - throw $this->createUnexpectedCallException($prophecy, $call->getMethodName(), $call->getArguments()); - } - } + return $this->telemetryInfo; } /** - * @param ObjectProphecy $prophecy - * @param string $methodName - * @param array $arguments - * - * @return UnexpectedCallException + * @psalm-return class-string */ - private function createUnexpectedCallException(ObjectProphecy $prophecy, $methodName, array $arguments) + public function className(): string { - $classname = \get_class($prophecy->reveal()); - $indentationLength = 8; - // looks good - $argstring = \implode(",\n", $this->indentArguments(\array_map(array($this->util, 'stringify'), $arguments), $indentationLength)); - $expected = array(); - foreach (\array_merge(...\array_values($prophecy->getMethodProphecies())) as $methodProphecy) { - $expected[] = \sprintf(" - %s(\n" . "%s\n" . " )", $methodProphecy->getMethodName(), \implode(",\n", $this->indentArguments(\array_map('strval', $methodProphecy->getArgumentsWildcard()->getTokens()), $indentationLength))); - } - return new UnexpectedCallException(\sprintf("Unexpected method call on %s:\n" . " - %s(\n" . "%s\n" . " )\n" . "expected calls were:\n" . "%s", $classname, $methodName, $argstring, \implode("\n", $expected)), $prophecy, $methodName, $arguments); + return $this->className; } /** - * @param string[] $arguments - * @param int $indentationLength - * - * @return string[] + * @psalm-return list */ - private function indentArguments(array $arguments, $indentationLength) + public function methodNames(): array { - return \preg_replace_callback('/^/m', function () use($indentationLength) { - return \str_repeat(' ', $indentationLength); - }, $arguments); + return $this->methodNames; } - /** - * @param ObjectProphecy $prophecy - * @param string $methodName - * @param array $arguments - * - * @return array - * - * @phpstan-return list - */ - private function findMethodProphecies(ObjectProphecy $prophecy, $methodName, array $arguments) + public function asString(): string { - $matches = array(); - foreach ($prophecy->getMethodProphecies($methodName) as $methodProphecy) { - if (0 < ($score = $methodProphecy->getArgumentsWildcard()->scoreArguments($arguments))) { - $matches[] = array($score, $methodProphecy); - } - } - return $matches; + return sprintf('Partial Mock Object Created (%s)', $this->className); } } - * Marcello Duarte + * This file is part of PHPUnit. + * + * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace Prophecy\Comparator; +namespace PHPUnit\Event\Test; -use PHPUnitPHAR\SebastianBergmann\Comparator\Comparator; -use PHPUnitPHAR\SebastianBergmann\Comparator\ComparisonFailure; +use PHPUnit\Event\Subscriber; /** - * Closure comparator. - * - * @author Konstantin Kudryashov + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -final class ClosureComparator extends Comparator +interface PartialMockObjectCreatedSubscriber extends Subscriber { - /** - * @param mixed $expected - * @param mixed $actual - */ - public function accepts($expected, $actual) : bool - { - return \is_object($expected) && $expected instanceof \Closure && \is_object($actual) && $actual instanceof \Closure; - } - /** - * @param mixed $expected - * @param mixed $actual - * @param float $delta - * @param bool $canonicalize - * @param bool $ignoreCase - */ - public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = \false, $ignoreCase = \false) : void - { - if ($expected !== $actual) { - // Support for sebastian/comparator < 5 - if ((new \ReflectionMethod(ComparisonFailure::class, '__construct'))->getNumberOfParameters() >= 6) { - // @phpstan-ignore-next-line - throw new ComparisonFailure($expected, $actual, '', '', \false, 'all closures are different if not identical'); - } - throw new ComparisonFailure( - $expected, - $actual, - // we don't need a diff - '', - '', - 'all closures are different if not identical' - ); - } - } + public function notify(\PHPUnit\Event\Test\PartialMockObjectCreated $event): void; } - * Marcello Duarte + * This file is part of PHPUnit. + * + * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace Prophecy\Comparator; +namespace PHPUnit\Event\Test; -use PHPUnitPHAR\SebastianBergmann\Comparator\Factory as BaseFactory; +use function sprintf; +use PHPUnit\Event\Event; +use PHPUnit\Event\Telemetry; /** - * Prophecy comparator factory. - * - * @author Konstantin Kudryashov + * @psalm-immutable * - * @deprecated Use "Prophecy\Comparator\FactoryProvider" instead to get a "SebastianBergmann\Comparator\Factory" instance. + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -final class Factory extends BaseFactory +final class TestProxyCreated implements Event { + private readonly Telemetry\Info $telemetryInfo; /** - * @var Factory + * @psalm-var class-string */ - private static $instance; - public function __construct() + private readonly string $className; + private readonly string $constructorArguments; + /** + * @psalm-param class-string $className + */ + public function __construct(Telemetry\Info $telemetryInfo, string $className, string $constructorArguments) + { + $this->telemetryInfo = $telemetryInfo; + $this->className = $className; + $this->constructorArguments = $constructorArguments; + } + public function telemetryInfo(): Telemetry\Info { - parent::__construct(); - $this->register(new \Prophecy\Comparator\ClosureComparator()); - $this->register(new \Prophecy\Comparator\ProphecyComparator()); + return $this->telemetryInfo; } /** - * @return Factory + * @psalm-return class-string */ - public static function getInstance() + public function className(): string { - if (self::$instance === null) { - self::$instance = new \Prophecy\Comparator\Factory(); - } - return self::$instance; + return $this->className; + } + public function constructorArguments(): string + { + return $this->constructorArguments; + } + public function asString(): string + { + return sprintf('Test Proxy Created (%s)', $this->className); } } - * Marcello Duarte + * This file is part of PHPUnit. + * + * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace Prophecy\Comparator; +namespace PHPUnit\Event\Test; -use PHPUnitPHAR\SebastianBergmann\Comparator\Factory; +use PHPUnit\Event\Subscriber; /** - * Prophecy comparator factory. - * - * @author Konstantin Kudryashov + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -final class FactoryProvider +interface TestProxyCreatedSubscriber extends Subscriber { - /** - * @var Factory|null - */ - private static $instance; - private function __construct() - { - } - public static function getInstance() : Factory - { - if (self::$instance === null) { - self::$instance = new Factory(); - self::$instance->register(new \Prophecy\Comparator\ClosureComparator()); - self::$instance->register(new \Prophecy\Comparator\ProphecyComparator()); - } - return self::$instance; - } + public function notify(\PHPUnit\Event\Test\TestProxyCreated $event): void; } - * Marcello Duarte + * This file is part of PHPUnit. + * + * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace Prophecy\Comparator; +namespace PHPUnit\Event\Test; -use Prophecy\Prophecy\ProphecyInterface; -use PHPUnitPHAR\SebastianBergmann\Comparator\ObjectComparator; +use function sprintf; +use PHPUnit\Event\Event; +use PHPUnit\Event\Telemetry; /** - * @final + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -class ProphecyComparator extends ObjectComparator +final class TestStubCreated implements Event { + private readonly Telemetry\Info $telemetryInfo; + /** + * @var class-string + */ + private readonly string $className; /** - * @param mixed $expected - * @param mixed $actual + * @psalm-param class-string $className */ - public function accepts($expected, $actual) : bool + public function __construct(Telemetry\Info $telemetryInfo, string $className) + { + $this->telemetryInfo = $telemetryInfo; + $this->className = $className; + } + public function telemetryInfo(): Telemetry\Info { - return \is_object($expected) && \is_object($actual) && $actual instanceof ProphecyInterface; + return $this->telemetryInfo; } /** - * @param mixed $expected - * @param mixed $actual - * @param float $delta - * @param bool $canonicalize - * @param bool $ignoreCase - * @param array $processed - * - * @phpstan-param list $processed + * @return class-string */ - public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = \false, $ignoreCase = \false, array &$processed = array()) : void + public function className(): string { - \assert($actual instanceof ProphecyInterface); - parent::assertEquals($expected, $actual->reveal(), $delta, $canonicalize, $ignoreCase, $processed); + return $this->className; + } + public function asString(): string + { + return sprintf('Test Stub Created (%s)', $this->className); } } - * Marcello Duarte + * This file is part of PHPUnit. + * + * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace Prophecy\Doubler; +namespace PHPUnit\Event\Test; -use ReflectionClass; +use PHPUnit\Event\Subscriber; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +interface TestStubCreatedSubscriber extends Subscriber +{ + public function notify(\PHPUnit\Event\Test\TestStubCreated $event): void; +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\Test; + +use function implode; +use function sprintf; +use PHPUnit\Event\Event; +use PHPUnit\Event\Telemetry; /** - * Cached class doubler. - * Prevents mirroring/creation of the same structure twice. + * @psalm-immutable * - * @author Konstantin Kudryashov + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -class CachedDoubler extends \Prophecy\Doubler\Doubler +final class TestStubForIntersectionOfInterfacesCreated implements Event { + private readonly Telemetry\Info $telemetryInfo; /** - * @var array + * @psalm-var list */ - private static $classes = array(); - protected function createDoubleClass(ReflectionClass $class = null, array $interfaces) - { - $classId = $this->generateClassId($class, $interfaces); - if (isset(self::$classes[$classId])) { - return self::$classes[$classId]; - } - return self::$classes[$classId] = parent::createDoubleClass($class, $interfaces); - } + private readonly array $interfaces; /** - * @param ReflectionClass|null $class - * @param ReflectionClass[] $interfaces - * - * @return string + * @psalm-param list $interfaces */ - private function generateClassId(ReflectionClass $class = null, array $interfaces) + public function __construct(Telemetry\Info $telemetryInfo, array $interfaces) { - $parts = array(); - if (null !== $class) { - $parts[] = $class->getName(); - } - foreach ($interfaces as $interface) { - $parts[] = $interface->getName(); - } - foreach ($this->getClassPatches() as $patch) { - $parts[] = \get_class($patch); - } - \sort($parts); - return \md5(\implode('', $parts)); + $this->telemetryInfo = $telemetryInfo; + $this->interfaces = $interfaces; + } + public function telemetryInfo(): Telemetry\Info + { + return $this->telemetryInfo; } /** - * @return void + * @return list */ - public function resetCache() + public function interfaces(): array + { + return $this->interfaces; + } + public function asString(): string { - self::$classes = array(); + return sprintf('Test Stub Created (%s)', implode('&', $this->interfaces)); } } - * Marcello Duarte + * This file is part of PHPUnit. + * + * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace Prophecy\Doubler\ClassPatch; +namespace PHPUnit\Event\Test; -use Prophecy\Doubler\Generator\Node\ClassNode; +use PHPUnit\Event\Subscriber; /** - * Class patch interface. - * Class patches extend doubles functionality or help - * Prophecy to avoid some internal PHP bugs. - * - * @author Konstantin Kudryashov + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -interface ClassPatchInterface +interface TestStubForIntersectionOfInterfacesCreatedSubscriber extends Subscriber { - /** - * Checks if patch supports specific class node. - * - * @param ClassNode $node - * - * @return bool - */ - public function supports(ClassNode $node); - /** - * Applies patch to the specific class node. - * - * @param ClassNode $node - * @return void - */ - public function apply(ClassNode $node); - /** - * Returns patch priority, which determines when patch will be applied. - * - * @return int Priority number (higher - earlier) - */ - public function getPriority(); + public function notify(\PHPUnit\Event\Test\TestStubForIntersectionOfInterfacesCreated $event): void; } - * Marcello Duarte + * This file is part of PHPUnit. + * + * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace Prophecy\Doubler\ClassPatch; +namespace PHPUnit\Event\TestRunner; -use Prophecy\Doubler\Generator\Node\ClassNode; -use Prophecy\Doubler\Generator\Node\MethodNode; +use function sprintf; +use PHPUnit\Event\Event; +use PHPUnit\Event\Telemetry; /** - * Disable constructor. - * Makes all constructor arguments optional. + * @psalm-immutable * - * @author Konstantin Kudryashov + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -class DisableConstructorPatch implements \Prophecy\Doubler\ClassPatch\ClassPatchInterface +final class BootstrapFinished implements Event { - /** - * Checks if class has `__construct` method. - * - * @param ClassNode $node - * - * @return bool - */ - public function supports(ClassNode $node) + private readonly Telemetry\Info $telemetryInfo; + private readonly string $filename; + public function __construct(Telemetry\Info $telemetryInfo, string $filename) { - return \true; + $this->telemetryInfo = $telemetryInfo; + $this->filename = $filename; } - /** - * Makes all class constructor arguments optional. - * - * @param ClassNode $node - */ - public function apply(ClassNode $node) + public function telemetryInfo(): Telemetry\Info { - if (!$node->isExtendable('__construct')) { - return; - } - if (!$node->hasMethod('__construct')) { - $node->addMethod(new MethodNode('__construct', '')); - return; - } - $constructor = $node->getMethod('__construct'); - \assert($constructor !== null); - foreach ($constructor->getArguments() as $argument) { - $argument->setDefault(null); - } - $constructor->setCode(<<telemetryInfo; } - /** - * Returns patch priority, which determines when patch will be applied. - * - * @return int Priority number (higher - earlier) - */ - public function getPriority() + public function filename(): string + { + return $this->filename; + } + public function asString(): string { - return 100; + return sprintf('Bootstrap Finished (%s)', $this->filename); } } - * Marcello Duarte + * This file is part of PHPUnit. + * + * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace Prophecy\Doubler\ClassPatch; +namespace PHPUnit\Event\TestRunner; -use Prophecy\Doubler\Generator\Node\ClassNode; +use PHPUnit\Event\Subscriber; /** - * Remove method functionality from the double which will clash with php keywords. + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +interface BootstrapFinishedSubscriber extends Subscriber +{ + public function notify(\PHPUnit\Event\TestRunner\BootstrapFinished $event): void; +} + * - * @author Milan Magudia + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\TestRunner; + +use PHPUnit\Event\Event; +use PHPUnit\Event\Telemetry; +use PHPUnit\TextUI\Configuration\Configuration; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -class KeywordPatch implements \Prophecy\Doubler\ClassPatch\ClassPatchInterface +final class Configured implements Event { - /** - * Support any class - * - * @param ClassNode $node - * - * @return boolean - */ - public function supports(ClassNode $node) + private readonly Telemetry\Info $telemetryInfo; + private readonly Configuration $configuration; + public function __construct(Telemetry\Info $telemetryInfo, Configuration $configuration) { - return \true; + $this->telemetryInfo = $telemetryInfo; + $this->configuration = $configuration; } - /** - * Remove methods that clash with php keywords - * - * @param ClassNode $node - */ - public function apply(ClassNode $node) + public function telemetryInfo(): Telemetry\Info { - $methodNames = \array_keys($node->getMethods()); - $methodsToRemove = \array_intersect($methodNames, $this->getKeywords()); - foreach ($methodsToRemove as $methodName) { - $node->removeMethod($methodName); - } + return $this->telemetryInfo; } - /** - * Returns patch priority, which determines when patch will be applied. - * - * @return int Priority number (higher - earlier) - */ - public function getPriority() + public function configuration(): Configuration { - return 49; + return $this->configuration; } - /** - * Returns array of php keywords. - * - * @return list - */ - private function getKeywords() + public function asString(): string { - return ['__halt_compiler']; + return 'Test Runner Configured'; } } - * Marcello Duarte + * This file is part of PHPUnit. + * + * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace Prophecy\Doubler\ClassPatch; +namespace PHPUnit\Event\TestRunner; -use Prophecy\Doubler\Generator\Node\ArgumentNode; -use Prophecy\Doubler\Generator\Node\ClassNode; -use Prophecy\Doubler\Generator\Node\MethodNode; -use Prophecy\PhpDocumentor\ClassAndInterfaceTagRetriever; -use Prophecy\PhpDocumentor\MethodTagRetrieverInterface; +use PHPUnit\Event\Subscriber; /** - * Discover Magical API using "@method" PHPDoc format. - * - * @author Thomas Tourlourat - * @author Kévin Dunglas - * @author Théo FIDRY + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -class MagicCallPatch implements \Prophecy\Doubler\ClassPatch\ClassPatchInterface +interface ConfiguredSubscriber extends Subscriber { - const MAGIC_METHODS_WITH_ARGUMENTS = ['__call', '__callStatic', '__get', '__isset', '__set', '__set_state', '__unserialize', '__unset']; - private $tagRetriever; - public function __construct(MethodTagRetrieverInterface $tagRetriever = null) - { - $this->tagRetriever = null === $tagRetriever ? new ClassAndInterfaceTagRetriever() : $tagRetriever; - } - /** - * Support any class - * - * @param ClassNode $node - * - * @return boolean - */ - public function supports(ClassNode $node) - { - return \true; - } - /** - * Discover Magical API - * - * @param ClassNode $node - */ - public function apply(ClassNode $node) - { - $types = \array_filter($node->getInterfaces(), function ($interface) { - return 0 !== \strpos($interface, 'Prophecy\\'); - }); - $types[] = $node->getParentClass(); - foreach ($types as $type) { - $reflectionClass = new \ReflectionClass($type); - while ($reflectionClass) { - $tagList = $this->tagRetriever->getTagList($reflectionClass); - foreach ($tagList as $tag) { - $methodName = $tag->getMethodName(); - if (empty($methodName)) { - continue; - } - if (!$reflectionClass->hasMethod($methodName)) { - $methodNode = new MethodNode($methodName); - // only magic methods can have a contract that needs to be enforced - if (\in_array($methodName, self::MAGIC_METHODS_WITH_ARGUMENTS)) { - foreach ($tag->getArguments() as $argument) { - $argumentNode = new ArgumentNode($argument['name']); - $methodNode->addArgument($argumentNode); - } - } - $methodNode->setStatic($tag->isStatic()); - $node->addMethod($methodNode); - } - } - $reflectionClass = $reflectionClass->getParentClass(); - } - } - } - /** - * Returns patch priority, which determines when patch will be applied. - * - * @return integer Priority number (higher - earlier) - */ - public function getPriority() - { - return 50; - } + public function notify(\PHPUnit\Event\TestRunner\Configured $event): void; } - * Marcello Duarte + * This file is part of PHPUnit. + * + * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace Prophecy\Doubler\ClassPatch; +namespace PHPUnit\Event\TestRunner; -use Prophecy\Doubler\Generator\Node\ArgumentTypeNode; -use Prophecy\Doubler\Generator\Node\ClassNode; -use Prophecy\Doubler\Generator\Node\MethodNode; -use Prophecy\Doubler\Generator\Node\ArgumentNode; -use Prophecy\Doubler\Generator\Node\ReturnTypeNode; +use function sprintf; +use PHPUnit\Event\Event; +use PHPUnit\Event\Telemetry; /** - * Add Prophecy functionality to the double. - * This is a core class patch for Prophecy. + * @psalm-immutable * - * @author Konstantin Kudryashov + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -class ProphecySubjectPatch implements \Prophecy\Doubler\ClassPatch\ClassPatchInterface +final class DeprecationTriggered implements Event { - /** - * Always returns true. - * - * @param ClassNode $node - * - * @return bool - */ - public function supports(ClassNode $node) + private readonly Telemetry\Info $telemetryInfo; + private readonly string $message; + public function __construct(Telemetry\Info $telemetryInfo, string $message) { - return \true; + $this->telemetryInfo = $telemetryInfo; + $this->message = $message; } - /** - * Apply Prophecy functionality to class node. - * - * @param ClassNode $node - */ - public function apply(ClassNode $node) + public function telemetryInfo(): Telemetry\Info { - $node->addInterface('Prophecy\\Prophecy\\ProphecySubjectInterface'); - $node->addProperty('objectProphecyClosure', 'private'); - foreach ($node->getMethods() as $name => $method) { - if ('__construct' === \strtolower($name)) { - continue; - } - if (!$method->getReturnTypeNode()->hasReturnStatement()) { - $method->setCode('$this->getProphecy()->makeProphecyMethodCall(__FUNCTION__, func_get_args());'); - } else { - $method->setCode('return $this->getProphecy()->makeProphecyMethodCall(__FUNCTION__, func_get_args());'); - } - } - $prophecySetter = new MethodNode('setProphecy'); - $prophecyArgument = new ArgumentNode('prophecy'); - $prophecyArgument->setTypeNode(new ArgumentTypeNode('Prophecy\\Prophecy\\ProphecyInterface')); - $prophecySetter->addArgument($prophecyArgument); - $prophecySetter->setCode(<<objectProphecyClosure) { - \$this->objectProphecyClosure = static function () use (\$prophecy) { - return \$prophecy; - }; -} -PHP -); - $prophecyGetter = new MethodNode('getProphecy'); - $prophecyGetter->setCode('return \\call_user_func($this->objectProphecyClosure);'); - if ($node->hasMethod('__call')) { - $__call = $node->getMethod('__call'); - \assert($__call !== null); - } else { - $__call = new MethodNode('__call'); - $__call->addArgument(new ArgumentNode('name')); - $__call->addArgument(new ArgumentNode('arguments')); - $node->addMethod($__call, \true); - } - $__call->setCode(<<addMethod($prophecySetter, \true); - $node->addMethod($prophecyGetter, \true); + return $this->telemetryInfo; } - /** - * Returns patch priority, which determines when patch will be applied. - * - * @return int Priority number (higher - earlier) - */ - public function getPriority() + public function message(): string { - return 0; + return $this->message; + } + public function asString(): string + { + return sprintf('Test Runner Triggered Deprecation (%s)', $this->message); } } - * Marcello Duarte + * This file is part of PHPUnit. + * + * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace Prophecy\Doubler\ClassPatch; +namespace PHPUnit\Event\TestRunner; -use Prophecy\Doubler\Generator\Node\ClassNode; +use PHPUnit\Event\Subscriber; /** - * ReflectionClass::newInstance patch. - * Makes first argument of newInstance optional, since it works but signature is misleading - * - * @author Florian Klein + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -class ReflectionClassNewInstancePatch implements \Prophecy\Doubler\ClassPatch\ClassPatchInterface +interface DeprecationTriggeredSubscriber extends Subscriber { - /** - * Supports ReflectionClass - * - * @param ClassNode $node - * - * @return bool - */ - public function supports(ClassNode $node) - { - return 'ReflectionClass' === $node->getParentClass(); - } - /** - * Updates newInstance's first argument to make it optional - * - * @param ClassNode $node - */ - public function apply(ClassNode $node) - { - $method = $node->getMethod('newInstance'); - \assert($method !== null); - foreach ($method->getArguments() as $argument) { - $argument->setDefault(null); - } - } - /** - * Returns patch priority, which determines when patch will be applied. - * - * @return int Priority number (higher = earlier) - */ - public function getPriority() - { - return 50; - } + public function notify(\PHPUnit\Event\TestRunner\DeprecationTriggered $event): void; } - * Marcello Duarte + * This file is part of PHPUnit. + * + * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace Prophecy\Doubler\ClassPatch; +namespace PHPUnit\Event\TestRunner; -use Prophecy\Doubler\Generator\Node\ClassNode; -use Prophecy\Doubler\Generator\Node\MethodNode; +use PHPUnit\Event\Event; +use PHPUnit\Event\Telemetry; /** - * SplFileInfo patch. - * Makes SplFileInfo and derivative classes usable with Prophecy. + * @psalm-immutable * - * @author Konstantin Kudryashov + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -class SplFileInfoPatch implements \Prophecy\Doubler\ClassPatch\ClassPatchInterface +final class EventFacadeSealed implements Event { - /** - * Supports everything that extends SplFileInfo. - * - * @param ClassNode $node - * - * @return bool - */ - public function supports(ClassNode $node) - { - return 'SplFileInfo' === $node->getParentClass() || \is_subclass_of($node->getParentClass(), 'SplFileInfo'); - } - /** - * Updated constructor code to call parent one with dummy file argument. - * - * @param ClassNode $node - */ - public function apply(ClassNode $node) - { - if ($node->hasMethod('__construct')) { - $constructor = $node->getMethod('__construct'); - \assert($constructor !== null); - } else { - $constructor = new MethodNode('__construct'); - $node->addMethod($constructor); - } - if ($this->nodeIsDirectoryIterator($node)) { - $constructor->setCode('return parent::__construct("' . __DIR__ . '");'); - return; - } - if ($this->nodeIsSplFileObject($node)) { - $filePath = \str_replace('\\', '\\\\', __FILE__); - $constructor->setCode('return parent::__construct("' . $filePath . '");'); - return; - } - if ($this->nodeIsSymfonySplFileInfo($node)) { - $filePath = \str_replace('\\', '\\\\', __FILE__); - $constructor->setCode('return parent::__construct("' . $filePath . '", "", "");'); - return; - } - $constructor->useParentCode(); - } - /** - * Returns patch priority, which determines when patch will be applied. - * - * @return int Priority number (higher - earlier) - */ - public function getPriority() - { - return 50; - } - /** - * @param ClassNode $node - * @return boolean - */ - private function nodeIsDirectoryIterator(ClassNode $node) + private readonly Telemetry\Info $telemetryInfo; + public function __construct(Telemetry\Info $telemetryInfo) { - $parent = $node->getParentClass(); - return 'DirectoryIterator' === $parent || \is_subclass_of($parent, 'DirectoryIterator'); + $this->telemetryInfo = $telemetryInfo; } - /** - * @param ClassNode $node - * @return boolean - */ - private function nodeIsSplFileObject(ClassNode $node) + public function telemetryInfo(): Telemetry\Info { - $parent = $node->getParentClass(); - return 'SplFileObject' === $parent || \is_subclass_of($parent, 'SplFileObject'); + return $this->telemetryInfo; } - /** - * @param ClassNode $node - * @return boolean - */ - private function nodeIsSymfonySplFileInfo(ClassNode $node) + public function asString(): string { - $parent = $node->getParentClass(); - return 'Symfony\\Component\\Finder\\SplFileInfo' === $parent; + return 'Event Facade Sealed'; } } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\TestRunner; -use Prophecy\Doubler\Generator\Node\ClassNode; -use Prophecy\Exception\Doubler\ClassCreatorException; -class ThrowablePatch implements \Prophecy\Doubler\ClassPatch\ClassPatchInterface +use PHPUnit\Event\Subscriber; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +interface EventFacadeSealedSubscriber extends Subscriber { - /** - * Checks if patch supports specific class node. - * - * @param ClassNode $node - * @return bool - */ - public function supports(ClassNode $node) - { - return $this->implementsAThrowableInterface($node) && $this->doesNotExtendAThrowableClass($node); - } - /** - * @param ClassNode $node - * @return bool - */ - private function implementsAThrowableInterface(ClassNode $node) - { - foreach ($node->getInterfaces() as $type) { - if (\is_a($type, 'Throwable', \true)) { - return \true; - } - } - return \false; - } - /** - * @param ClassNode $node - * @return bool - */ - private function doesNotExtendAThrowableClass(ClassNode $node) - { - return !\is_a($node->getParentClass(), 'Throwable', \true); - } - /** - * Applies patch to the specific class node. - * - * @param ClassNode $node - * - * @return void - */ - public function apply(ClassNode $node) - { - $this->checkItCanBeDoubled($node); - $this->setParentClassToException($node); - } - private function checkItCanBeDoubled(ClassNode $node) : void - { - $className = $node->getParentClass(); - if ($className !== 'stdClass') { - throw new ClassCreatorException(\sprintf('Cannot double concrete class %s as well as implement Traversable', $className), $node); - } - } - private function setParentClassToException(ClassNode $node) : void - { - $node->setParentClass('Exception'); - $node->removeMethod('getMessage'); - $node->removeMethod('getCode'); - $node->removeMethod('getFile'); - $node->removeMethod('getLine'); - $node->removeMethod('getTrace'); - $node->removeMethod('getPrevious'); - $node->removeMethod('getNext'); - $node->removeMethod('getTraceAsString'); - } - /** - * Returns patch priority, which determines when patch will be applied. - * - * @return int Priority number (higher - earlier) - */ - public function getPriority() - { - return 100; - } + public function notify(\PHPUnit\Event\TestRunner\EventFacadeSealed $event): void; } - * Marcello Duarte + * This file is part of PHPUnit. + * + * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace Prophecy\Doubler\ClassPatch; +namespace PHPUnit\Event\TestRunner; -use Prophecy\Doubler\Generator\Node\ClassNode; -use Prophecy\Doubler\Generator\Node\MethodNode; -use Prophecy\Doubler\Generator\Node\ReturnTypeNode; +use PHPUnit\Event\Event; +use PHPUnit\Event\Telemetry; /** - * Traversable interface patch. - * Forces classes that implement interfaces, that extend Traversable to also implement Iterator. + * @psalm-immutable * - * @author Konstantin Kudryashov + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -class TraversablePatch implements \Prophecy\Doubler\ClassPatch\ClassPatchInterface +final class ExecutionAborted implements Event { - /** - * Supports nodetree, that implement Traversable, but not Iterator or IteratorAggregate. - * - * @param ClassNode $node - * - * @return bool - */ - public function supports(ClassNode $node) + private readonly Telemetry\Info $telemetryInfo; + public function __construct(Telemetry\Info $telemetryInfo) { - if (\in_array('Iterator', $node->getInterfaces())) { - return \false; - } - if (\in_array('IteratorAggregate', $node->getInterfaces())) { - return \false; - } - foreach ($node->getInterfaces() as $interface) { - if ('Traversable' !== $interface && !\is_subclass_of($interface, 'Traversable')) { - continue; - } - if ('Iterator' === $interface || \is_subclass_of($interface, 'Iterator')) { - continue; - } - if ('IteratorAggregate' === $interface || \is_subclass_of($interface, 'IteratorAggregate')) { - continue; - } - return \true; - } - return \false; + $this->telemetryInfo = $telemetryInfo; } - /** - * Forces class to implement Iterator interface. - * - * @param ClassNode $node - */ - public function apply(ClassNode $node) + public function telemetryInfo(): Telemetry\Info { - $node->addInterface('Iterator'); - $currentMethod = new MethodNode('current'); - \PHP_VERSION_ID >= 80100 && $currentMethod->setReturnTypeNode(new ReturnTypeNode('mixed')); - $node->addMethod($currentMethod); - $keyMethod = new MethodNode('key'); - \PHP_VERSION_ID >= 80100 && $keyMethod->setReturnTypeNode(new ReturnTypeNode('mixed')); - $node->addMethod($keyMethod); - $nextMethod = new MethodNode('next'); - \PHP_VERSION_ID >= 80100 && $nextMethod->setReturnTypeNode(new ReturnTypeNode('void')); - $node->addMethod($nextMethod); - $rewindMethod = new MethodNode('rewind'); - \PHP_VERSION_ID >= 80100 && $rewindMethod->setReturnTypeNode(new ReturnTypeNode('void')); - $node->addMethod($rewindMethod); - $validMethod = new MethodNode('valid'); - \PHP_VERSION_ID >= 80100 && $validMethod->setReturnTypeNode(new ReturnTypeNode('bool')); - $node->addMethod($validMethod); + return $this->telemetryInfo; } - /** - * Returns patch priority, which determines when patch will be applied. - * - * @return int Priority number (higher - earlier) - */ - public function getPriority() + public function asString(): string { - return 100; + return 'Test Runner Execution Aborted'; } } - * Marcello Duarte + * This file is part of PHPUnit. + * + * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace Prophecy\Doubler; +namespace PHPUnit\Event\TestRunner; +use PHPUnit\Event\Subscriber; /** - * Core double interface. - * All doubled classes will implement this one. - * - * @author Konstantin Kudryashov + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -interface DoubleInterface +interface ExecutionAbortedSubscriber extends Subscriber { + public function notify(\PHPUnit\Event\TestRunner\ExecutionAborted $event): void; } - * Marcello Duarte + * This file is part of PHPUnit. + * + * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace Prophecy\Doubler; +namespace PHPUnit\Event\TestRunner; -use PHPUnitPHAR\Doctrine\Instantiator\Instantiator; -use Prophecy\Doubler\ClassPatch\ClassPatchInterface; -use Prophecy\Doubler\Generator\ClassMirror; -use Prophecy\Doubler\Generator\ClassCreator; -use Prophecy\Exception\InvalidArgumentException; -use ReflectionClass; +use PHPUnit\Event\Event; +use PHPUnit\Event\Telemetry; /** - * Cached class doubler. - * Prevents mirroring/creation of the same structure twice. + * @psalm-immutable * - * @author Konstantin Kudryashov + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -class Doubler +final class ExecutionFinished implements Event { - private $mirror; - private $creator; - private $namer; - /** - * @var list - */ - private $patches = array(); - /** - * @var Instantiator|null - */ - private $instantiator; - public function __construct(ClassMirror $mirror = null, ClassCreator $creator = null, \Prophecy\Doubler\NameGenerator $namer = null) - { - $this->mirror = $mirror ?: new ClassMirror(); - $this->creator = $creator ?: new ClassCreator(); - $this->namer = $namer ?: new \Prophecy\Doubler\NameGenerator(); - } - /** - * Returns list of registered class patches. - * - * @return list - */ - public function getClassPatches() - { - return $this->patches; - } - /** - * Registers new class patch. - * - * @param ClassPatchInterface $patch - * - * @return void - */ - public function registerClassPatch(ClassPatchInterface $patch) + private readonly Telemetry\Info $telemetryInfo; + public function __construct(Telemetry\Info $telemetryInfo) { - $this->patches[] = $patch; - @\usort($this->patches, function (ClassPatchInterface $patch1, ClassPatchInterface $patch2) { - return $patch2->getPriority() - $patch1->getPriority(); - }); + $this->telemetryInfo = $telemetryInfo; } - /** - * Creates double from specific class or/and list of interfaces. - * - * @template T of object - * - * @param ReflectionClass|null $class - * @param ReflectionClass[] $interfaces Array of ReflectionClass instances - * @param array|null $args Constructor arguments - * - * @return T&DoubleInterface - * - * @throws \Prophecy\Exception\InvalidArgumentException - */ - public function double(ReflectionClass $class = null, array $interfaces, array $args = null) + public function telemetryInfo(): Telemetry\Info { - foreach ($interfaces as $interface) { - if (!$interface instanceof ReflectionClass) { - throw new InvalidArgumentException(\sprintf("[ReflectionClass \$interface1 [, ReflectionClass \$interface2]] array expected as\n" . "a second argument to `Doubler::double(...)`, but got %s.", \is_object($interface) ? \get_class($interface) . ' class' : \gettype($interface))); - } - } - $classname = $this->createDoubleClass($class, $interfaces); - $reflection = new ReflectionClass($classname); - if (null !== $args) { - return $reflection->newInstanceArgs($args); - } - if (null === ($constructor = $reflection->getConstructor()) || $constructor->isPublic() && !$constructor->isFinal()) { - return $reflection->newInstance(); - } - if (!$this->instantiator) { - $this->instantiator = new Instantiator(); - } - return $this->instantiator->instantiate($classname); + return $this->telemetryInfo; } - /** - * Creates double class and returns its FQN. - * - * @template T of object - * - * @param ReflectionClass|null $class - * @param ReflectionClass[] $interfaces - * - * @return class-string - */ - protected function createDoubleClass(ReflectionClass $class = null, array $interfaces) + public function asString(): string { - $name = $this->namer->name($class, $interfaces); - $node = $this->mirror->reflect($class, $interfaces); - foreach ($this->patches as $patch) { - if ($patch->supports($node)) { - $patch->apply($node); - } - } - $node->addInterface(\Prophecy\Doubler\DoubleInterface::class); - $this->creator->create($name, $node); - \assert(\class_exists($name, \false)); - return $name; + return 'Test Runner Execution Finished'; } } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\TestRunner; + +use PHPUnit\Event\Subscriber; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +interface ExecutionFinishedSubscriber extends Subscriber +{ + public function notify(\PHPUnit\Event\TestRunner\ExecutionFinished $event): void; +} + - * Marcello Duarte + * This file is part of PHPUnit. + * + * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace Prophecy\Doubler\Generator; +namespace PHPUnit\Event\TestRunner; -use Prophecy\Doubler\Generator\Node\ReturnTypeNode; -use Prophecy\Doubler\Generator\Node\TypeNodeAbstract; +use function sprintf; +use PHPUnit\Event\Event; +use PHPUnit\Event\Telemetry; +use PHPUnit\Event\TestSuite\TestSuite; /** - * Class code creator. - * Generates PHP code for specific class node tree. + * @psalm-immutable * - * @author Konstantin Kudryashov + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -class ClassCodeGenerator +final class ExecutionStarted implements Event { - // Used to accept an optional first argument with the deprecated Prophecy\Doubler\Generator\TypeHintReference so careful when adding a new argument in a minor version. - public function __construct() - { - } - /** - * Generates PHP code for class node. - * - * @param string $classname - * @param Node\ClassNode $class - * - * @return string - */ - public function generate($classname, \Prophecy\Doubler\Generator\Node\ClassNode $class) + private readonly Telemetry\Info $telemetryInfo; + private readonly TestSuite $testSuite; + public function __construct(Telemetry\Info $telemetryInfo, TestSuite $testSuite) { - $parts = \explode('\\', $classname); - $classname = \array_pop($parts); - $namespace = \implode('\\', $parts); - $code = \sprintf("%sclass %s extends \\%s implements %s {\n", $class->isReadOnly() ? 'readonly ' : '', $classname, $class->getParentClass(), \implode(', ', \array_map(function ($interface) { - return '\\' . $interface; - }, $class->getInterfaces()))); - foreach ($class->getProperties() as $name => $visibility) { - $code .= \sprintf("%s \$%s;\n", $visibility, $name); - } - $code .= "\n"; - foreach ($class->getMethods() as $method) { - $code .= $this->generateMethod($method) . "\n"; - } - $code .= "\n}"; - return \sprintf("namespace %s {\n%s\n}", $namespace, $code); + $this->telemetryInfo = $telemetryInfo; + $this->testSuite = $testSuite; } - private function generateMethod(\Prophecy\Doubler\Generator\Node\MethodNode $method) : string + public function telemetryInfo(): Telemetry\Info { - $php = \sprintf("%s %s function %s%s(%s)%s {\n", $method->getVisibility(), $method->isStatic() ? 'static' : '', $method->returnsReference() ? '&' : '', $method->getName(), \implode(', ', $this->generateArguments($method->getArguments())), ($ret = $this->generateTypes($method->getReturnTypeNode())) ? ': ' . $ret : ''); - $php .= $method->getCode() . "\n"; - return $php . '}'; + return $this->telemetryInfo; } - private function generateTypes(TypeNodeAbstract $typeNode) : string + public function testSuite(): TestSuite { - if (!$typeNode->getTypes()) { - return ''; - } - // When we require PHP 8 we can stop generating ?foo nullables and remove this first block - if ($typeNode->canUseNullShorthand()) { - return \sprintf('?%s', $typeNode->getNonNullTypes()[0]); - } else { - return \join('|', $typeNode->getTypes()); - } + return $this->testSuite; } - /** - * @param list $arguments - * - * @return list - */ - private function generateArguments(array $arguments) : array + public function asString(): string { - return \array_map(function (\Prophecy\Doubler\Generator\Node\ArgumentNode $argument) { - $php = $this->generateTypes($argument->getTypeNode()); - $php .= ' ' . ($argument->isPassedByReference() ? '&' : ''); - $php .= $argument->isVariadic() ? '...' : ''; - $php .= '$' . $argument->getName(); - if ($argument->isOptional() && !$argument->isVariadic()) { - $php .= ' = ' . \var_export($argument->getDefault(), \true); - } - return $php; - }, $arguments); + return sprintf('Test Runner Execution Started (%d test%s)', $this->testSuite->count(), ($this->testSuite->count() !== 1) ? 's' : ''); } } - * Marcello Duarte + * This file is part of PHPUnit. + * + * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace Prophecy\Doubler\Generator; +namespace PHPUnit\Event\TestRunner; -use Prophecy\Exception\Doubler\ClassCreatorException; +use PHPUnit\Event\Subscriber; /** - * Class creator. - * Creates specific class in current environment. - * - * @author Konstantin Kudryashov + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -class ClassCreator +interface ExecutionStartedSubscriber extends Subscriber { - private $generator; - public function __construct(\Prophecy\Doubler\Generator\ClassCodeGenerator $generator = null) - { - $this->generator = $generator ?: new \Prophecy\Doubler\Generator\ClassCodeGenerator(); - } - /** - * Creates class. - * - * @param string $classname - * @param Node\ClassNode $class - * - * @return mixed - * - * @throws \Prophecy\Exception\Doubler\ClassCreatorException - */ - public function create($classname, \Prophecy\Doubler\Generator\Node\ClassNode $class) - { - $code = $this->generator->generate($classname, $class); - $return = eval($code); - if (!\class_exists($classname, \false)) { - if (\count($class->getInterfaces())) { - throw new ClassCreatorException(\sprintf('Could not double `%s` and implement interfaces: [%s].', $class->getParentClass(), \implode(', ', $class->getInterfaces())), $class); - } - throw new ClassCreatorException(\sprintf('Could not double `%s`.', $class->getParentClass()), $class); - } - return $return; - } + public function notify(\PHPUnit\Event\TestRunner\ExecutionStarted $event): void; } - * Marcello Duarte + * This file is part of PHPUnit. + * + * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace Prophecy\Doubler\Generator; +namespace PHPUnit\Event\TestRunner; -use Prophecy\Doubler\Generator\Node\ArgumentTypeNode; -use Prophecy\Doubler\Generator\Node\ReturnTypeNode; -use Prophecy\Exception\InvalidArgumentException; -use Prophecy\Exception\Doubler\ClassMirrorException; -use ReflectionClass; -use ReflectionIntersectionType; -use ReflectionMethod; -use ReflectionNamedType; -use ReflectionParameter; -use ReflectionType; -use ReflectionUnionType; +use function sprintf; +use PHPUnit\Event\Event; +use PHPUnit\Event\Telemetry; /** - * Class mirror. - * Core doubler class. Mirrors specific class and/or interfaces into class node tree. + * @psalm-immutable * - * @author Konstantin Kudryashov + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -class ClassMirror +final class ExtensionBootstrapped implements Event { - private const REFLECTABLE_METHODS = array('__construct', '__destruct', '__sleep', '__wakeup', '__toString', '__call', '__invoke'); + private readonly Telemetry\Info $telemetryInfo; /** - * Reflects provided arguments into class node. - * - * @param ReflectionClass|null $class - * @param ReflectionClass[] $interfaces - * - * @return Node\ClassNode - * + * @psalm-var class-string */ - public function reflect(?ReflectionClass $class, array $interfaces) - { - $node = new \Prophecy\Doubler\Generator\Node\ClassNode(); - if (null !== $class) { - if (\true === $class->isInterface()) { - throw new InvalidArgumentException(\sprintf("Could not reflect %s as a class, because it\n" . "is interface - use the second argument instead.", $class->getName())); - } - $this->reflectClassToNode($class, $node); - } - foreach ($interfaces as $interface) { - if (!$interface instanceof ReflectionClass) { - throw new InvalidArgumentException(\sprintf("[ReflectionClass \$interface1 [, ReflectionClass \$interface2]] array expected as\n" . "a second argument to `ClassMirror::reflect(...)`, but got %s.", \is_object($interface) ? \get_class($interface) . ' class' : \gettype($interface))); - } - if (\false === $interface->isInterface()) { - throw new InvalidArgumentException(\sprintf("Could not reflect %s as an interface, because it\n" . "is class - use the first argument instead.", $interface->getName())); - } - $this->reflectInterfaceToNode($interface, $node); - } - $node->addInterface('Prophecy\\Doubler\\Generator\\ReflectionInterface'); - return $node; - } + private readonly string $className; /** - * @param ReflectionClass $class + * @psalm-var array */ - private function reflectClassToNode(ReflectionClass $class, \Prophecy\Doubler\Generator\Node\ClassNode $node) : void - { - if (\true === $class->isFinal()) { - throw new ClassMirrorException(\sprintf('Could not reflect class %s as it is marked final.', $class->getName()), $class); - } - if (\method_exists(ReflectionClass::class, 'isReadOnly')) { - $node->setReadOnly($class->isReadOnly()); - } - $node->setParentClass($class->getName()); - foreach ($class->getMethods(ReflectionMethod::IS_ABSTRACT) as $method) { - if (\false === $method->isProtected()) { - continue; - } - $this->reflectMethodToNode($method, $node); - } - foreach ($class->getMethods(ReflectionMethod::IS_PUBLIC) as $method) { - if (0 === \strpos($method->getName(), '_') && !\in_array($method->getName(), self::REFLECTABLE_METHODS)) { - continue; - } - if (\true === $method->isFinal()) { - $node->addUnextendableMethod($method->getName()); - continue; - } - $this->reflectMethodToNode($method, $node); - } - } + private readonly array $parameters; /** - * @param ReflectionClass $interface + * @psalm-param class-string $className + * @psalm-param array $parameters */ - private function reflectInterfaceToNode(ReflectionClass $interface, \Prophecy\Doubler\Generator\Node\ClassNode $node) : void + public function __construct(Telemetry\Info $telemetryInfo, string $className, array $parameters) { - $node->addInterface($interface->getName()); - foreach ($interface->getMethods() as $method) { - $this->reflectMethodToNode($method, $node); - } + $this->telemetryInfo = $telemetryInfo; + $this->className = $className; + $this->parameters = $parameters; } - private function reflectMethodToNode(ReflectionMethod $method, \Prophecy\Doubler\Generator\Node\ClassNode $classNode) : void + public function telemetryInfo(): Telemetry\Info { - $node = new \Prophecy\Doubler\Generator\Node\MethodNode($method->getName()); - if (\true === $method->isProtected()) { - $node->setVisibility('protected'); - } - if (\true === $method->isStatic()) { - $node->setStatic(); - } - if (\true === $method->returnsReference()) { - $node->setReturnsReference(); - } - if ($method->hasReturnType()) { - \assert($method->getReturnType() !== null); - $returnTypes = $this->getTypeHints($method->getReturnType(), $method->getDeclaringClass(), $method->getReturnType()->allowsNull()); - $node->setReturnTypeNode(new ReturnTypeNode(...$returnTypes)); - } elseif (\method_exists($method, 'hasTentativeReturnType') && $method->hasTentativeReturnType()) { - \assert($method->getTentativeReturnType() !== null); - $returnTypes = $this->getTypeHints($method->getTentativeReturnType(), $method->getDeclaringClass(), $method->getTentativeReturnType()->allowsNull()); - $node->setReturnTypeNode(new ReturnTypeNode(...$returnTypes)); - } - if (\is_array($params = $method->getParameters()) && \count($params)) { - foreach ($params as $param) { - $this->reflectArgumentToNode($param, $method->getDeclaringClass(), $node); - } - } - $classNode->addMethod($node); + return $this->telemetryInfo; } /** - * @param ReflectionClass $declaringClass - * - * @return void + * @psalm-return class-string */ - private function reflectArgumentToNode(ReflectionParameter $parameter, ReflectionClass $declaringClass, \Prophecy\Doubler\Generator\Node\MethodNode $methodNode) : void - { - $name = $parameter->getName() == '...' ? '__dot_dot_dot__' : $parameter->getName(); - $node = new \Prophecy\Doubler\Generator\Node\ArgumentNode($name); - $typeHints = $this->getTypeHints($parameter->getType(), $declaringClass, $parameter->allowsNull()); - $node->setTypeNode(new ArgumentTypeNode(...$typeHints)); - if ($parameter->isVariadic()) { - $node->setAsVariadic(); - } - if ($this->hasDefaultValue($parameter)) { - $node->setDefault($this->getDefaultValue($parameter)); - } - if ($parameter->isPassedByReference()) { - $node->setAsPassedByReference(); - } - $methodNode->addArgument($node); - } - private function hasDefaultValue(ReflectionParameter $parameter) : bool + public function className(): string { - if ($parameter->isVariadic()) { - return \false; - } - if ($parameter->isDefaultValueAvailable()) { - return \true; - } - return $parameter->isOptional() || $parameter->allowsNull() && $parameter->getType() && \PHP_VERSION_ID < 80100; + return $this->className; } /** - * @return mixed + * @psalm-return array */ - private function getDefaultValue(ReflectionParameter $parameter) + public function parameters(): array { - if (!$parameter->isDefaultValueAvailable()) { - return null; - } - return $parameter->getDefaultValue(); + return $this->parameters; } - /** - * @param ReflectionClass $class - * - * @return list - */ - private function getTypeHints(?ReflectionType $type, ReflectionClass $class, bool $allowsNull) : array + public function asString(): string { - $types = []; - if ($type instanceof ReflectionNamedType) { - $types = [$type->getName()]; - } elseif ($type instanceof ReflectionUnionType) { - $types = $type->getTypes(); - if (\PHP_VERSION_ID >= 80200) { - foreach ($types as $reflectionType) { - if ($reflectionType instanceof ReflectionIntersectionType) { - throw new ClassMirrorException('Doubling intersection types is not supported', $class); - } - } - } - } elseif ($type instanceof ReflectionIntersectionType) { - throw new ClassMirrorException('Doubling intersection types is not supported', $class); - } elseif (\is_object($type)) { - throw new ClassMirrorException('Unknown reflection type ' . \get_class($type), $class); - } - $types = \array_map(function (string $type) use($class) { - if ($type === 'self') { - return $class->getName(); - } - if ($type === 'parent') { - if (\false === $class->getParentClass()) { - throw new ClassMirrorException(\sprintf('Invalid type "parent" in class "%s" without a parent', $class->getName()), $class); - } - return $class->getParentClass()->getName(); - } - return $type; - }, $types); - if ($types && $types != ['mixed'] && $allowsNull) { - $types[] = 'null'; - } - return $types; + return sprintf('Extension Bootstrapped (%s)', $this->className); } } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\TestRunner; + +use PHPUnit\Event\Subscriber; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +interface ExtensionBootstrappedSubscriber extends Subscriber +{ + public function notify(\PHPUnit\Event\TestRunner\ExtensionBootstrapped $event): void; +} + - * Marcello Duarte + * This file is part of PHPUnit. + * + * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace Prophecy\Doubler\Generator\Node; +namespace PHPUnit\Event\TestRunner; +use function sprintf; +use PHPUnit\Event\Event; +use PHPUnit\Event\Telemetry; /** - * Argument node. + * @psalm-immutable * - * @author Konstantin Kudryashov + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -class ArgumentNode +final class ExtensionLoadedFromPhar implements Event { - private $name; - /** - * @var mixed - */ - private $default; - /** - * @var bool - */ - private $optional = \false; - /** - * @var bool - */ - private $byReference = \false; - /** - * @var bool - */ - private $isVariadic = \false; - /** @var ArgumentTypeNode */ - private $typeNode; - /** - * @param string $name - */ - public function __construct($name) + private readonly Telemetry\Info $telemetryInfo; + private readonly string $filename; + private readonly string $name; + private readonly string $version; + public function __construct(Telemetry\Info $telemetryInfo, string $filename, string $name, string $version) { + $this->telemetryInfo = $telemetryInfo; + $this->filename = $filename; $this->name = $name; - $this->typeNode = new \Prophecy\Doubler\Generator\Node\ArgumentTypeNode(); - } - /** - * @return string - */ - public function getName() - { - return $this->name; - } - /** - * @return void - */ - public function setTypeNode(\Prophecy\Doubler\Generator\Node\ArgumentTypeNode $typeNode) - { - $this->typeNode = $typeNode; - } - public function getTypeNode() : \Prophecy\Doubler\Generator\Node\ArgumentTypeNode - { - return $this->typeNode; - } - /** - * @return bool - */ - public function hasDefault() - { - return $this->isOptional() && !$this->isVariadic(); - } - /** - * @return mixed - */ - public function getDefault() - { - return $this->default; - } - /** - * @param mixed $default - * - * @return void - */ - public function setDefault($default = null) - { - $this->optional = \true; - $this->default = $default; - } - /** - * @return bool - */ - public function isOptional() - { - return $this->optional; - } - /** - * @param bool $byReference - * - * @return void - */ - public function setAsPassedByReference($byReference = \true) - { - $this->byReference = $byReference; - } - /** - * @return bool - */ - public function isPassedByReference() - { - return $this->byReference; - } - /** - * @param bool $isVariadic - * - * @return void - */ - public function setAsVariadic($isVariadic = \true) - { - $this->isVariadic = $isVariadic; + $this->version = $version; } - /** - * @return bool - */ - public function isVariadic() + public function telemetryInfo(): Telemetry\Info { - return $this->isVariadic; + return $this->telemetryInfo; } - /** - * @deprecated use getArgumentTypeNode instead - * @return string|null - */ - public function getTypeHint() + public function filename(): string { - $type = $this->typeNode->getNonNullTypes() ? $this->typeNode->getNonNullTypes()[0] : null; - return $type ? \ltrim($type, '\\') : null; + return $this->filename; } - /** - * @deprecated use setArgumentTypeNode instead - * @param string|null $typeHint - * - * @return void - */ - public function setTypeHint($typeHint = null) + public function name(): string { - $this->typeNode = $typeHint === null ? new \Prophecy\Doubler\Generator\Node\ArgumentTypeNode() : new \Prophecy\Doubler\Generator\Node\ArgumentTypeNode($typeHint); + return $this->name; } - /** - * @deprecated use getArgumentTypeNode instead - * @return bool - */ - public function isNullable() + public function version(): string { - return $this->typeNode->canUseNullShorthand(); + return $this->version; } - /** - * @deprecated use getArgumentTypeNode instead - * @param bool $isNullable - * - * @return void - */ - public function setAsNullable($isNullable = \true) + public function asString(): string { - $nonNullTypes = $this->typeNode->getNonNullTypes(); - $this->typeNode = $isNullable ? new \Prophecy\Doubler\Generator\Node\ArgumentTypeNode('null', ...$nonNullTypes) : new \Prophecy\Doubler\Generator\Node\ArgumentTypeNode(...$nonNullTypes); + return sprintf('Extension Loaded from PHAR (%s %s)', $this->name, $this->version); } } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\TestRunner; -use Prophecy\Exception\Doubler\DoubleException; -class ArgumentTypeNode extends \Prophecy\Doubler\Generator\Node\TypeNodeAbstract +use PHPUnit\Event\Subscriber; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +interface ExtensionLoadedFromPharSubscriber extends Subscriber { + public function notify(\PHPUnit\Event\TestRunner\ExtensionLoadedFromPhar $event): void; } - * Marcello Duarte + * This file is part of PHPUnit. + * + * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace Prophecy\Doubler\Generator\Node; +namespace PHPUnit\Event\TestRunner; -use Prophecy\Exception\Doubler\MethodNotExtendableException; -use Prophecy\Exception\InvalidArgumentException; +use PHPUnit\Event\Event; +use PHPUnit\Event\Telemetry; /** - * Class node. + * @psalm-immutable * - * @author Konstantin Kudryashov + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -class ClassNode +final class Finished implements Event { - /** - * @var class-string - */ - private $parentClass = 'stdClass'; - /** - * @var list - */ - private $interfaces = array(); - /** - * @var array - * - * @phpstan-var array - */ - private $properties = array(); - /** - * @var list - */ - private $unextendableMethods = array(); - /** - * @var bool - */ - private $readOnly = \false; - /** - * @var array - */ - private $methods = array(); - /** - * @return class-string - */ - public function getParentClass() - { - return $this->parentClass; - } - /** - * @param class-string|null $class - * - * @return void - */ - public function setParentClass($class) - { - $this->parentClass = $class ?: 'stdClass'; - } - /** - * @return list - */ - public function getInterfaces() - { - return $this->interfaces; - } - /** - * @param class-string $interface - * - * @return void - */ - public function addInterface($interface) - { - if ($this->hasInterface($interface)) { - return; - } - \array_unshift($this->interfaces, $interface); - } - /** - * @param class-string $interface - * - * @return bool - */ - public function hasInterface($interface) + private readonly Telemetry\Info $telemetryInfo; + public function __construct(Telemetry\Info $telemetryInfo) { - return \in_array($interface, $this->interfaces); + $this->telemetryInfo = $telemetryInfo; } - /** - * @return array - * - * @phpstan-return array - */ - public function getProperties() - { - return $this->properties; - } - /** - * @param string $name - * @param string $visibility - * - * @return void - * - * @phpstan-param 'public'|'private'|'protected' $visibility - */ - public function addProperty($name, $visibility = 'public') + public function telemetryInfo(): Telemetry\Info { - $visibility = \strtolower($visibility); - if (!\in_array($visibility, array('public', 'private', 'protected'), \true)) { - throw new InvalidArgumentException(\sprintf('`%s` property visibility is not supported.', $visibility)); - } - $this->properties[$name] = $visibility; + return $this->telemetryInfo; } - /** - * @return array - */ - public function getMethods() + public function asString(): string { - return $this->methods; + return 'Test Runner Finished'; } - /** - * @param MethodNode $method - * @param bool $force - * - * @return void - */ - public function addMethod(\Prophecy\Doubler\Generator\Node\MethodNode $method, $force = \false) +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\TestRunner; + +use PHPUnit\Event\Subscriber; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +interface FinishedSubscriber extends Subscriber +{ + public function notify(\PHPUnit\Event\TestRunner\Finished $event): void; +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\TestRunner; + +use PHPUnit\Event\Event; +use PHPUnit\Event\Telemetry; +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class GarbageCollectionDisabled implements Event +{ + private readonly Telemetry\Info $telemetryInfo; + public function __construct(Telemetry\Info $telemetryInfo) { - if (!$this->isExtendable($method->getName())) { - $message = \sprintf('Method `%s` is not extendable, so can not be added.', $method->getName()); - throw new MethodNotExtendableException($message, $this->getParentClass(), $method->getName()); - } - if ($force || !isset($this->methods[$method->getName()])) { - $this->methods[$method->getName()] = $method; - } + $this->telemetryInfo = $telemetryInfo; } - /** - * @param string $name - * - * @return void - */ - public function removeMethod($name) + public function telemetryInfo(): Telemetry\Info { - unset($this->methods[$name]); + return $this->telemetryInfo; } - /** - * @param string $name - * - * @return MethodNode|null - */ - public function getMethod($name) + public function asString(): string { - return $this->hasMethod($name) ? $this->methods[$name] : null; + return 'Test Runner Disabled Garbage Collection'; } - /** - * @param string $name - * - * @return bool - */ - public function hasMethod($name) +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\TestRunner; + +use PHPUnit\Event\Subscriber; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +interface GarbageCollectionDisabledSubscriber extends Subscriber +{ + public function notify(\PHPUnit\Event\TestRunner\GarbageCollectionDisabled $event): void; +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\TestRunner; + +use PHPUnit\Event\Event; +use PHPUnit\Event\Telemetry; +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class GarbageCollectionEnabled implements Event +{ + private readonly Telemetry\Info $telemetryInfo; + public function __construct(Telemetry\Info $telemetryInfo) { - return isset($this->methods[$name]); + $this->telemetryInfo = $telemetryInfo; } - /** - * @return list - */ - public function getUnextendableMethods() + public function telemetryInfo(): Telemetry\Info { - return $this->unextendableMethods; + return $this->telemetryInfo; } - /** - * @param string $unextendableMethod - * - * @return void - */ - public function addUnextendableMethod($unextendableMethod) + public function asString(): string { - if (!$this->isExtendable($unextendableMethod)) { - return; - } - $this->unextendableMethods[] = $unextendableMethod; + return 'Test Runner Enabled Garbage Collection'; } - /** - * @param string $method - * - * @return bool - */ - public function isExtendable($method) +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\TestRunner; + +use PHPUnit\Event\Subscriber; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +interface GarbageCollectionEnabledSubscriber extends Subscriber +{ + public function notify(\PHPUnit\Event\TestRunner\GarbageCollectionEnabled $event): void; +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\TestRunner; + +use PHPUnit\Event\Event; +use PHPUnit\Event\Telemetry; +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class GarbageCollectionTriggered implements Event +{ + private readonly Telemetry\Info $telemetryInfo; + public function __construct(Telemetry\Info $telemetryInfo) { - return !\in_array($method, $this->unextendableMethods); + $this->telemetryInfo = $telemetryInfo; } - /** - * @return bool - */ - public function isReadOnly() + public function telemetryInfo(): Telemetry\Info { - return $this->readOnly; + return $this->telemetryInfo; } - /** - * @param bool $readOnly - * - * @return void - */ - public function setReadOnly($readOnly) + public function asString(): string { - $this->readOnly = $readOnly; + return 'Test Runner Triggered Garbage Collection'; } } - * Marcello Duarte + * This file is part of PHPUnit. + * + * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace Prophecy\Doubler\Generator\Node; +namespace PHPUnit\Event\TestRunner; -use Prophecy\Exception\InvalidArgumentException; +use PHPUnit\Event\Subscriber; /** - * Method node. - * - * @author Konstantin Kudryashov + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -class MethodNode +interface GarbageCollectionTriggeredSubscriber extends Subscriber { - private $name; - private $code; - /** - * @var string - * - * @phpstan-var 'public'|'private'|'protected' - */ - private $visibility = 'public'; - /** - * @var bool - */ - private $static = \false; - /** - * @var bool - */ - private $returnsReference = \false; - /** @var ReturnTypeNode */ - private $returnTypeNode; - /** - * @var list - */ - private $arguments = array(); - // Used to accept an optional third argument with the deprecated Prophecy\Doubler\Generator\TypeHintReference so careful when adding a new argument in a minor version. - /** - * @param string $name - * @param string|null $code - */ - public function __construct($name, $code = null) - { - $this->name = $name; - $this->code = $code; - $this->returnTypeNode = new \Prophecy\Doubler\Generator\Node\ReturnTypeNode(); - } - /** - * @return string - * - * @phpstan-return 'public'|'private'|'protected' - */ - public function getVisibility() - { - return $this->visibility; - } - /** - * @param string $visibility - * - * @return void - */ - public function setVisibility($visibility) - { - $visibility = \strtolower($visibility); - if (!\in_array($visibility, array('public', 'private', 'protected'), \true)) { - throw new InvalidArgumentException(\sprintf('`%s` method visibility is not supported.', $visibility)); - } - $this->visibility = $visibility; - } - /** - * @return bool - */ - public function isStatic() - { - return $this->static; - } - /** - * @param bool $static - * - * @return void - */ - public function setStatic($static = \true) - { - $this->static = (bool) $static; - } - /** - * @return bool - */ - public function returnsReference() - { - return $this->returnsReference; - } - /** - * @return void - */ - public function setReturnsReference() - { - $this->returnsReference = \true; - } - /** - * @return string - */ - public function getName() - { - return $this->name; - } - /** - * @return void - */ - public function addArgument(\Prophecy\Doubler\Generator\Node\ArgumentNode $argument) - { - $this->arguments[] = $argument; - } - /** - * @return list - */ - public function getArguments() - { - return $this->arguments; - } - /** - * @deprecated use getReturnTypeNode instead - * @return bool - */ - public function hasReturnType() - { - return (bool) $this->returnTypeNode->getNonNullTypes(); - } - public function setReturnTypeNode(\Prophecy\Doubler\Generator\Node\ReturnTypeNode $returnTypeNode) : void - { - $this->returnTypeNode = $returnTypeNode; - } - /** - * @deprecated use setReturnTypeNode instead - * @param string $type - * - * @return void - */ - public function setReturnType($type = null) - { - $this->returnTypeNode = $type === '' || $type === null ? new \Prophecy\Doubler\Generator\Node\ReturnTypeNode() : new \Prophecy\Doubler\Generator\Node\ReturnTypeNode($type); - } - /** - * @deprecated use setReturnTypeNode instead - * @param bool $bool - * - * @return void - */ - public function setNullableReturnType($bool = \true) - { - if ($bool) { - $this->returnTypeNode = new \Prophecy\Doubler\Generator\Node\ReturnTypeNode('null', ...$this->returnTypeNode->getTypes()); - } else { - $this->returnTypeNode = new \Prophecy\Doubler\Generator\Node\ReturnTypeNode(...$this->returnTypeNode->getNonNullTypes()); - } - } - /** - * @deprecated use getReturnTypeNode instead - * @return string|null - */ - public function getReturnType() - { - if ($types = $this->returnTypeNode->getNonNullTypes()) { - return $types[0]; - } - return null; - } - public function getReturnTypeNode() : \Prophecy\Doubler\Generator\Node\ReturnTypeNode - { - return $this->returnTypeNode; - } - /** - * @deprecated use getReturnTypeNode instead - * @return bool - */ - public function hasNullableReturnType() - { - return $this->returnTypeNode->canUseNullShorthand(); - } - /** - * @param string $code - * - * @return void - */ - public function setCode($code) - { - $this->code = $code; - } - /** - * @return string - */ - public function getCode() - { - if ($this->returnsReference) { - return "throw new \\Prophecy\\Exception\\Doubler\\ReturnByReferenceException('Returning by reference not supported', get_class(\$this), '{$this->name}');"; - } - return (string) $this->code; - } - /** - * @return void - */ - public function useParentCode() - { - $this->code = \sprintf('return parent::%s(%s);', $this->getName(), \implode(', ', \array_map(array($this, 'generateArgument'), $this->arguments))); - } - /** - * @return string - */ - private function generateArgument(\Prophecy\Doubler\Generator\Node\ArgumentNode $arg) - { - $argument = '$' . $arg->getName(); - if ($arg->isVariadic()) { - $argument = '...' . $argument; - } - return $argument; - } -} -types['void']) && \count($this->types) !== 1) { - throw new DoubleException('void cannot be part of a union'); - } - if (isset($this->types['never']) && \count($this->types) !== 1) { - throw new DoubleException('never cannot be part of a union'); - } - parent::guardIsValidType(); - } - /** - * @deprecated use hasReturnStatement - * - * @return bool - */ - public function isVoid() - { - return $this->types == ['void' => 'void']; - } - public function hasReturnStatement() : bool - { - return $this->types !== ['void' => 'void'] && $this->types !== ['never' => 'never']; - } + public function notify(\PHPUnit\Event\TestRunner\GarbageCollectionTriggered $event): void; } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\TestRunner; -use Prophecy\Exception\Doubler\DoubleException; -abstract class TypeNodeAbstract +use PHPUnit\Event\Event; +use PHPUnit\Event\Telemetry; +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class Started implements Event { - /** @var array */ - protected $types = []; - public function __construct(string ...$types) - { - foreach ($types as $type) { - $type = $this->getRealType($type); - $this->types[$type] = $type; - } - $this->guardIsValidType(); - } - public function canUseNullShorthand() : bool + private readonly Telemetry\Info $telemetryInfo; + public function __construct(Telemetry\Info $telemetryInfo) { - return isset($this->types['null']) && \count($this->types) === 2; + $this->telemetryInfo = $telemetryInfo; } - /** - * @return list - */ - public function getTypes() : array - { - return \array_values($this->types); - } - /** - * @return list - */ - public function getNonNullTypes() : array + public function telemetryInfo(): Telemetry\Info { - $nonNullTypes = $this->types; - unset($nonNullTypes['null']); - return \array_values($nonNullTypes); - } - protected function prefixWithNsSeparator(string $type) : string - { - return '\\' . \ltrim($type, '\\'); - } - protected function getRealType(string $type) : string - { - switch ($type) { - // type aliases - case 'double': - case 'real': - return 'float'; - case 'boolean': - return 'bool'; - case 'integer': - return 'int'; - // built in types - case 'self': - case 'static': - case 'array': - case 'callable': - case 'bool': - case 'false': - case 'true': - case 'float': - case 'int': - case 'string': - case 'iterable': - case 'object': - case 'null': - return $type; - case 'mixed': - return \PHP_VERSION_ID < 80000 ? $this->prefixWithNsSeparator($type) : $type; - default: - return $this->prefixWithNsSeparator($type); - } + return $this->telemetryInfo; } - /** - * @return void - */ - protected function guardIsValidType() + public function asString(): string { - if (\PHP_VERSION_ID < 80200) { - if ($this->types == ['null' => 'null']) { - throw new DoubleException('Type cannot be standalone null'); - } - if ($this->types == ['false' => 'false']) { - throw new DoubleException('Type cannot be standalone false'); - } - if ($this->types == ['false' => 'false', 'null' => 'null']) { - throw new DoubleException('Type cannot be nullable false'); - } - if ($this->types == ['true' => 'true']) { - throw new DoubleException('Type cannot be standalone true'); - } - if ($this->types == ['true' => 'true', 'null' => 'null']) { - throw new DoubleException('Type cannot be nullable true'); - } - } - if (\PHP_VERSION_ID >= 80000 && isset($this->types['mixed']) && \count($this->types) !== 1) { - throw new DoubleException('mixed cannot be part of a union'); - } + return 'Test Runner Started'; } } - * Marcello Duarte + * This file is part of PHPUnit. + * + * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace Prophecy\Doubler\Generator; - -/** - * Reflection interface. - * All reflected classes implement this interface. - * - * @author Konstantin Kudryashov - */ -interface ReflectionInterface -{ -} -= 80000; - default: - return \false; - } - } - /** - * @param string $type - * - * @return bool - */ - public function isBuiltInReturnTypeHint($type) - { - if ($type === 'void') { - return \true; - } - return $this->isBuiltInParamTypeHint($type); - } + public function notify(\PHPUnit\Event\TestRunner\Started $event): void; } - * Marcello Duarte + * This file is part of PHPUnit. + * + * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace Prophecy\Doubler; +namespace PHPUnit\Event\TestRunner; -use Prophecy\Exception\Doubler\DoubleException; -use Prophecy\Exception\Doubler\ClassNotFoundException; -use Prophecy\Exception\Doubler\InterfaceNotFoundException; -use ReflectionClass; +use function sprintf; +use PHPUnit\Event\Event; +use PHPUnit\Event\Telemetry; /** - * Lazy double. - * Gives simple interface to describe double before creating it. - * - * @template T of object + * @psalm-immutable * - * @author Konstantin Kudryashov + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -class LazyDouble +final class WarningTriggered implements Event { - private $doubler; - /** - * @var ReflectionClass|null - */ - private $class; - /** - * @var list> - */ - private $interfaces = array(); - /** - * @var array|null - */ - private $arguments = null; - /** - * @var (T&DoubleInterface)|null - */ - private $double; - public function __construct(\Prophecy\Doubler\Doubler $doubler) + private readonly Telemetry\Info $telemetryInfo; + private readonly string $message; + public function __construct(Telemetry\Info $telemetryInfo, string $message) { - $this->doubler = $doubler; - } - /** - * Tells doubler to use specific class as parent one for double. - * - * @param class-string|ReflectionClass $class - * - * @return void - * - * @template U of object - * @phpstan-param class-string|ReflectionClass $class - * @phpstan-this-out static - * - * @throws ClassNotFoundException - * @throws DoubleException - */ - public function setParentClass($class) - { - if (null !== $this->double) { - throw new DoubleException('Can not extend class with already instantiated double.'); - } - if (!$class instanceof ReflectionClass) { - if (!\class_exists($class)) { - throw new ClassNotFoundException(\sprintf('Class %s not found.', $class), $class); - } - $class = new ReflectionClass($class); - } - /** @var static $this */ - $this->class = $class; + $this->telemetryInfo = $telemetryInfo; + $this->message = $message; } - /** - * Tells doubler to implement specific interface with double. - * - * @param class-string|ReflectionClass $interface - * - * @return void - * - * @template U of object - * @phpstan-param class-string|ReflectionClass $interface - * @phpstan-this-out static - * - * @throws InterfaceNotFoundException - * @throws DoubleException - */ - public function addInterface($interface) + public function telemetryInfo(): Telemetry\Info { - if (null !== $this->double) { - throw new DoubleException('Can not implement interface with already instantiated double.'); - } - if (!$interface instanceof ReflectionClass) { - if (!\interface_exists($interface)) { - throw new InterfaceNotFoundException(\sprintf('Interface %s not found.', $interface), $interface); - } - $interface = new ReflectionClass($interface); - } - $this->interfaces[] = $interface; + return $this->telemetryInfo; } - /** - * Sets constructor arguments. - * - * @param array|null $arguments - * - * @return void - */ - public function setArguments(array $arguments = null) + public function message(): string { - $this->arguments = $arguments; + return $this->message; } - /** - * Creates double instance or returns already created one. - * - * @return T&DoubleInterface - */ - public function getInstance() + public function asString(): string { - if (null === $this->double) { - if (null !== $this->arguments) { - return $this->double = $this->doubler->double($this->class, $this->interfaces, $this->arguments); - } - $this->double = $this->doubler->double($this->class, $this->interfaces); - } - return $this->double; + return sprintf('Test Runner Triggered Warning (%s)', $this->message); } } - * Marcello Duarte + * This file is part of PHPUnit. + * + * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace Prophecy\Doubler; +namespace PHPUnit\Event\TestRunner; -use ReflectionClass; +use PHPUnit\Event\Subscriber; /** - * Name generator. - * Generates classname for double. - * - * @author Konstantin Kudryashov + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -class NameGenerator +interface WarningTriggeredSubscriber extends Subscriber { - /** - * @var int - */ - private static $counter = 1; - /** - * Generates name. - * - * @param ReflectionClass|null $class - * @param ReflectionClass[] $interfaces - * - * @return string - */ - public function name(ReflectionClass $class = null, array $interfaces) - { - $parts = array(); - if (null !== $class) { - $parts[] = $class->getName(); - } else { - foreach ($interfaces as $interface) { - $parts[] = $interface->getShortName(); - } - } - if (!\count($parts)) { - $parts[] = 'stdClass'; - } - return \sprintf('Double\\%s\\P%d', \implode('\\', $parts), self::$counter++); - } + public function notify(\PHPUnit\Event\TestRunner\WarningTriggered $event): void; } - * Marcello Duarte + * This file is part of PHPUnit. + * + * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace Prophecy\Exception\Call; +namespace PHPUnit\Event\TestSuite; -use Prophecy\Exception\Prophecy\ObjectProphecyException; -use Prophecy\Prophecy\ObjectProphecy; -class UnexpectedCallException extends ObjectProphecyException +use function sprintf; +use PHPUnit\Event\Event; +use PHPUnit\Event\Telemetry; +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class Filtered implements Event { - private $methodName; - private $arguments; - /** - * @param string $message - * @param ObjectProphecy $objectProphecy - * @param string $methodName - * @param array $arguments - */ - public function __construct($message, ObjectProphecy $objectProphecy, $methodName, array $arguments) + private readonly Telemetry\Info $telemetryInfo; + private readonly \PHPUnit\Event\TestSuite\TestSuite $testSuite; + public function __construct(Telemetry\Info $telemetryInfo, \PHPUnit\Event\TestSuite\TestSuite $testSuite) { - parent::__construct($message, $objectProphecy); - $this->methodName = $methodName; - $this->arguments = $arguments; + $this->telemetryInfo = $telemetryInfo; + $this->testSuite = $testSuite; } - /** - * @return string - */ - public function getMethodName() + public function telemetryInfo(): Telemetry\Info { - return $this->methodName; + return $this->telemetryInfo; } - /** - * @return array - */ - public function getArguments() + public function testSuite(): \PHPUnit\Event\TestSuite\TestSuite { - return $this->arguments; + return $this->testSuite; + } + public function asString(): string + { + return sprintf('Test Suite Filtered (%d test%s)', $this->testSuite->count(), ($this->testSuite->count() !== 1) ? 's' : ''); } } - * Marcello Duarte + * This file is part of PHPUnit. + * + * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace Prophecy\Exception\Doubler; +namespace PHPUnit\Event\TestSuite; -use Prophecy\Doubler\Generator\Node\ClassNode; -class ClassCreatorException extends \RuntimeException implements \Prophecy\Exception\Doubler\DoublerException +use PHPUnit\Event\Subscriber; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +interface FilteredSubscriber extends Subscriber { - private $node; - /** - * @param string $message - * @param ClassNode $node - */ - public function __construct($message, ClassNode $node) - { - parent::__construct($message); - $this->node = $node; - } - /** - * @return ClassNode - */ - public function getClassNode() - { - return $this->node; - } + public function notify(\PHPUnit\Event\TestSuite\Filtered $event): void; } - * Marcello Duarte + * This file is part of PHPUnit. + * + * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace Prophecy\Exception\Doubler; +namespace PHPUnit\Event\TestSuite; -use ReflectionClass; -class ClassMirrorException extends \RuntimeException implements \Prophecy\Exception\Doubler\DoublerException +use function sprintf; +use PHPUnit\Event\Event; +use PHPUnit\Event\Telemetry; +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class Finished implements Event { - private $class; - /** - * @param string $message - * @param ReflectionClass $class - */ - public function __construct($message, ReflectionClass $class) + private readonly Telemetry\Info $telemetryInfo; + private readonly \PHPUnit\Event\TestSuite\TestSuite $testSuite; + public function __construct(Telemetry\Info $telemetryInfo, \PHPUnit\Event\TestSuite\TestSuite $testSuite) { - parent::__construct($message); - $this->class = $class; + $this->telemetryInfo = $telemetryInfo; + $this->testSuite = $testSuite; } - /** - * @return ReflectionClass - */ - public function getReflectedClass() + public function telemetryInfo(): Telemetry\Info { - return $this->class; + return $this->telemetryInfo; + } + public function testSuite(): \PHPUnit\Event\TestSuite\TestSuite + { + return $this->testSuite; + } + public function asString(): string + { + return sprintf('Test Suite Finished (%s, %d test%s)', $this->testSuite->name(), $this->testSuite->count(), ($this->testSuite->count() !== 1) ? 's' : ''); } } - * Marcello Duarte + * This file is part of PHPUnit. + * + * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace Prophecy\Exception\Doubler; +namespace PHPUnit\Event\TestSuite; -class ClassNotFoundException extends \Prophecy\Exception\Doubler\DoubleException +use PHPUnit\Event\Subscriber; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +interface FinishedSubscriber extends Subscriber { - private $classname; - /** - * @param string $message - * @param string $classname - */ - public function __construct($message, $classname) - { - parent::__construct($message); - $this->classname = $classname; - } - /** - * @return string - */ - public function getClassname() - { - return $this->classname; - } + public function notify(\PHPUnit\Event\TestSuite\Finished $event): void; } - * Marcello Duarte + * This file is part of PHPUnit. + * + * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace Prophecy\Exception\Doubler; +namespace PHPUnit\Event\TestSuite; -use RuntimeException; -class DoubleException extends RuntimeException implements \Prophecy\Exception\Doubler\DoublerException +use function sprintf; +use PHPUnit\Event\Event; +use PHPUnit\Event\Telemetry; +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class Loaded implements Event { + private readonly Telemetry\Info $telemetryInfo; + private readonly \PHPUnit\Event\TestSuite\TestSuite $testSuite; + public function __construct(Telemetry\Info $telemetryInfo, \PHPUnit\Event\TestSuite\TestSuite $testSuite) + { + $this->telemetryInfo = $telemetryInfo; + $this->testSuite = $testSuite; + } + public function telemetryInfo(): Telemetry\Info + { + return $this->telemetryInfo; + } + public function testSuite(): \PHPUnit\Event\TestSuite\TestSuite + { + return $this->testSuite; + } + public function asString(): string + { + return sprintf('Test Suite Loaded (%d test%s)', $this->testSuite->count(), ($this->testSuite->count() !== 1) ? 's' : ''); + } } - * Marcello Duarte + * This file is part of PHPUnit. + * + * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace Prophecy\Exception\Doubler; +namespace PHPUnit\Event\TestSuite; -use Prophecy\Exception\Exception; -interface DoublerException extends Exception +use PHPUnit\Event\Subscriber; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +interface LoadedSubscriber extends Subscriber { + public function notify(\PHPUnit\Event\TestSuite\Loaded $event): void; } - * Marcello Duarte + * This file is part of PHPUnit. + * + * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace Prophecy\Exception\Doubler; +namespace PHPUnit\Event\TestSuite; -class InterfaceNotFoundException extends \Prophecy\Exception\Doubler\ClassNotFoundException +use function sprintf; +use PHPUnit\Event\Event; +use PHPUnit\Event\Telemetry; +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class Skipped implements Event { - /** - * @return string - */ - public function getInterfaceName() + private readonly Telemetry\Info $telemetryInfo; + private readonly \PHPUnit\Event\TestSuite\TestSuite $testSuite; + private readonly string $message; + public function __construct(Telemetry\Info $telemetryInfo, \PHPUnit\Event\TestSuite\TestSuite $testSuite, string $message) { - return $this->getClassname(); + $this->telemetryInfo = $telemetryInfo; + $this->testSuite = $testSuite; + $this->message = $message; } -} -methodName = $methodName; - $this->className = $className; + return $this->telemetryInfo; } - /** - * @return string - */ - public function getMethodName() + public function testSuite(): \PHPUnit\Event\TestSuite\TestSuite { - return $this->methodName; + return $this->testSuite; } - /** - * @return string - */ - public function getClassName() + public function message(): string { - return $this->className; + return $this->message; + } + public function asString(): string + { + return sprintf('Test Suite Skipped (%s, %s)', $this->testSuite->name(), $this->message); } } - * Marcello Duarte + * This file is part of PHPUnit. + * + * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace Prophecy\Exception\Doubler; +namespace PHPUnit\Event\TestSuite; -use Prophecy\Argument\ArgumentsWildcard; -class MethodNotFoundException extends \Prophecy\Exception\Doubler\DoubleException +use PHPUnit\Event\Subscriber; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +interface SkippedSubscriber extends Subscriber { - /** - * @var string|object - */ - private $classname; - /** - * @var string - */ - private $methodName; - /** - * @var null|ArgumentsWildcard|array - */ - private $arguments; - /** - * @param string $message - * @param string|object $classname - * @param string $methodName - * @param null|ArgumentsWildcard|array $arguments - */ - public function __construct($message, $classname, $methodName, $arguments = null) - { - parent::__construct($message); - $this->classname = $classname; - $this->methodName = $methodName; - $this->arguments = $arguments; - } - /** - * @return object|string - */ - public function getClassname() - { - return $this->classname; - } - /** - * @return string - */ - public function getMethodName() - { - return $this->methodName; - } - /** - * @return null|ArgumentsWildcard|array - */ - public function getArguments() - { - return $this->arguments; - } + public function notify(\PHPUnit\Event\TestSuite\Skipped $event): void; } - * Marcello Duarte + * This file is part of PHPUnit. + * + * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace Prophecy\Exception\Doubler; +namespace PHPUnit\Event\TestSuite; -class ReturnByReferenceException extends \Prophecy\Exception\Doubler\DoubleException +use PHPUnit\Event\Event; +use PHPUnit\Event\Telemetry; +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class Sorted implements Event { - private $classname; - private $methodName; - /** - * @param string $message - * @param string $classname - * @param string $methodName - */ - public function __construct($message, $classname, $methodName) + private readonly Telemetry\Info $telemetryInfo; + private readonly int $executionOrder; + private readonly int $executionOrderDefects; + private readonly bool $resolveDependencies; + public function __construct(Telemetry\Info $telemetryInfo, int $executionOrder, int $executionOrderDefects, bool $resolveDependencies) { - parent::__construct($message); - $this->classname = $classname; - $this->methodName = $methodName; + $this->telemetryInfo = $telemetryInfo; + $this->executionOrder = $executionOrder; + $this->executionOrderDefects = $executionOrderDefects; + $this->resolveDependencies = $resolveDependencies; } - /** - * @return string - */ - public function getClassname() + public function telemetryInfo(): Telemetry\Info { - return $this->classname; + return $this->telemetryInfo; } - /** - * @return string - */ - public function getMethodName() + public function executionOrder(): int { - return $this->methodName; + return $this->executionOrder; + } + public function executionOrderDefects(): int + { + return $this->executionOrderDefects; + } + public function resolveDependencies(): bool + { + return $this->resolveDependencies; + } + public function asString(): string + { + return 'Test Suite Sorted'; } } - * Marcello Duarte + * This file is part of PHPUnit. + * + * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace Prophecy\Exception; +namespace PHPUnit\Event\TestSuite; +use PHPUnit\Event\Subscriber; /** - * Core Prophecy exception interface. - * All Prophecy exceptions implement it. - * - * @author Konstantin Kudryashov + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -interface Exception extends \Throwable +interface SortedSubscriber extends Subscriber { + public function notify(\PHPUnit\Event\TestSuite\Sorted $event): void; } - * Marcello Duarte + * This file is part of PHPUnit. + * + * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace Prophecy\Exception; +namespace PHPUnit\Event\TestSuite; -class InvalidArgumentException extends \InvalidArgumentException implements \Prophecy\Exception\Exception -{ -} - - * Marcello Duarte +use function sprintf; +use PHPUnit\Event\Event; +use PHPUnit\Event\Telemetry; +/** + * @psalm-immutable * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -namespace Prophecy\Exception\Prediction; - -use Prophecy\Prophecy\ObjectProphecy; -class AggregateException extends \RuntimeException implements \Prophecy\Exception\Prediction\PredictionException +final class Started implements Event { - /** - * @var list - */ - private $exceptions = array(); - /** - * @var ObjectProphecy|null - */ - private $objectProphecy; - /** - * @return void - */ - public function append(\Prophecy\Exception\Prediction\PredictionException $exception) + private readonly Telemetry\Info $telemetryInfo; + private readonly \PHPUnit\Event\TestSuite\TestSuite $testSuite; + public function __construct(Telemetry\Info $telemetryInfo, \PHPUnit\Event\TestSuite\TestSuite $testSuite) { - $message = $exception->getMessage(); - $message = \strtr($message, array("\n" => "\n ")) . "\n"; - $message = empty($this->exceptions) ? $message : "\n" . $message; - $this->message = \rtrim($this->message . $message); - $this->exceptions[] = $exception; + $this->telemetryInfo = $telemetryInfo; + $this->testSuite = $testSuite; } - /** - * @return list - */ - public function getExceptions() + public function telemetryInfo(): Telemetry\Info { - return $this->exceptions; + return $this->telemetryInfo; } - /** - * @param ObjectProphecy $objectProphecy - * - * @return void - */ - public function setObjectProphecy(ObjectProphecy $objectProphecy) + public function testSuite(): \PHPUnit\Event\TestSuite\TestSuite { - $this->objectProphecy = $objectProphecy; + return $this->testSuite; } - /** - * @return ObjectProphecy|null - */ - public function getObjectProphecy() + public function asString(): string { - return $this->objectProphecy; + return sprintf('Test Suite Started (%s, %d test%s)', $this->testSuite->name(), $this->testSuite->count(), ($this->testSuite->count() !== 1) ? 's' : ''); } } - * Marcello Duarte + * This file is part of PHPUnit. + * + * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace Prophecy\Exception\Prediction; +namespace PHPUnit\Event\TestSuite; -use RuntimeException; +use PHPUnit\Event\Subscriber; /** - * Basic failed prediction exception. - * Use it for custom prediction failures. - * - * @author Konstantin Kudryashov + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -class FailedPredictionException extends RuntimeException implements \Prophecy\Exception\Prediction\PredictionException +interface StartedSubscriber extends Subscriber { + public function notify(\PHPUnit\Event\TestSuite\Started $event): void; } - * Marcello Duarte + * This file is part of PHPUnit. + * + * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace Prophecy\Exception\Prediction; +namespace PHPUnit\Event; -use Prophecy\Exception\Prophecy\MethodProphecyException; -class NoCallsException extends MethodProphecyException implements \Prophecy\Exception\Prediction\PredictionException +use RuntimeException; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class EventAlreadyAssignedException extends RuntimeException implements \PHPUnit\Event\Exception { } - * Marcello Duarte + * This file is part of PHPUnit. + * + * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace Prophecy\Exception\Prediction; +namespace PHPUnit\Event; -use Prophecy\Exception\Exception; -interface PredictionException extends Exception +use RuntimeException; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class EventFacadeIsSealedException extends RuntimeException implements \PHPUnit\Event\Exception { } - * Marcello Duarte + * This file is part of PHPUnit. + * + * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace Prophecy\Exception\Prediction; +namespace PHPUnit\Event; -use Prophecy\Call\Call; -use Prophecy\Prophecy\MethodProphecy; -class UnexpectedCallsCountException extends \Prophecy\Exception\Prediction\UnexpectedCallsException +interface Exception extends \PHPUnit\Exception { - private $expectedCount; - /** - * @param string $message - * @param MethodProphecy $methodProphecy - * @param int $count - * @param list $calls - */ - public function __construct($message, MethodProphecy $methodProphecy, $count, array $calls) - { - parent::__construct($message, $methodProphecy, $calls); - $this->expectedCount = \intval($count); - } - /** - * @return int - */ - public function getExpectedCount() - { - return $this->expectedCount; - } } - * Marcello Duarte + * This file is part of PHPUnit. + * + * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace Prophecy\Exception\Prediction; +namespace PHPUnit\Event; -use Prophecy\Call\Call; -use Prophecy\Prophecy\MethodProphecy; -use Prophecy\Exception\Prophecy\MethodProphecyException; -class UnexpectedCallsException extends MethodProphecyException implements \Prophecy\Exception\Prediction\PredictionException +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class InvalidArgumentException extends \InvalidArgumentException implements \PHPUnit\Event\Exception { - private $calls = array(); - /** - * @param string $message - * @param MethodProphecy $methodProphecy - * @param list $calls - */ - public function __construct($message, MethodProphecy $methodProphecy, array $calls) - { - parent::__construct($message, $methodProphecy); - $this->calls = $calls; - } - /** - * @return list - */ - public function getCalls() - { - return $this->calls; - } } - * Marcello Duarte + * This file is part of PHPUnit. + * + * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace Prophecy\Exception\Prophecy; +namespace PHPUnit\Event; -use Prophecy\Prophecy\MethodProphecy; -class MethodProphecyException extends \Prophecy\Exception\Prophecy\ObjectProphecyException +use RuntimeException; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class InvalidEventException extends RuntimeException implements \PHPUnit\Event\Exception { - private $methodProphecy; - /** - * @param string $message - */ - public function __construct($message, MethodProphecy $methodProphecy) - { - parent::__construct($message, $methodProphecy->getObjectProphecy()); - $this->methodProphecy = $methodProphecy; - } - /** - * @return MethodProphecy - */ - public function getMethodProphecy() - { - return $this->methodProphecy; - } } - * Marcello Duarte + * This file is part of PHPUnit. + * + * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace Prophecy\Exception\Prophecy; +namespace PHPUnit\Event; -use Prophecy\Prophecy\ObjectProphecy; -class ObjectProphecyException extends \RuntimeException implements \Prophecy\Exception\Prophecy\ProphecyException +use RuntimeException; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class InvalidSubscriberException extends RuntimeException implements \PHPUnit\Event\Exception { - private $objectProphecy; - /** - * @param string $message - * @param ObjectProphecy $objectProphecy - */ - public function __construct($message, ObjectProphecy $objectProphecy) - { - parent::__construct($message); - $this->objectProphecy = $objectProphecy; - } - /** - * @return ObjectProphecy - */ - public function getObjectProphecy() - { - return $this->objectProphecy; - } } - * Marcello Duarte + * This file is part of PHPUnit. + * + * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace Prophecy\Exception\Prophecy; +namespace PHPUnit\Event; -use Prophecy\Exception\Exception; -interface ProphecyException extends Exception +use RuntimeException; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class MapError extends RuntimeException implements \PHPUnit\Event\Exception { } - * Marcello Duarte + * This file is part of PHPUnit. + * + * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace Prophecy\PhpDocumentor; +namespace PHPUnit\Event\TestData; -use PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\Tags\Method; +use PHPUnit\Event\Exception; +use RuntimeException; /** - * @author Théo FIDRY - * - * @internal + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -final class ClassAndInterfaceTagRetriever implements \Prophecy\PhpDocumentor\MethodTagRetrieverInterface +final class MoreThanOneDataSetFromDataProviderException extends RuntimeException implements Exception { - /** - * @var MethodTagRetrieverInterface - */ - private $classRetriever; - public function __construct(\Prophecy\PhpDocumentor\MethodTagRetrieverInterface $classRetriever = null) - { - if (null !== $classRetriever) { - $this->classRetriever = $classRetriever; - return; - } - $this->classRetriever = new \Prophecy\PhpDocumentor\ClassTagRetriever(); - } - public function getTagList(\ReflectionClass $reflectionClass) - { - return \array_merge($this->classRetriever->getTagList($reflectionClass), $this->getInterfacesTagList($reflectionClass)); - } - /** - * @param \ReflectionClass $reflectionClass - * - * @return list - */ - private function getInterfacesTagList(\ReflectionClass $reflectionClass) - { - $interfaces = $reflectionClass->getInterfaces(); - $tagList = array(); - foreach ($interfaces as $interface) { - $tagList = \array_merge($tagList, $this->classRetriever->getTagList($interface)); - } - return $tagList; - } } - * Marcello Duarte + * This file is part of PHPUnit. + * + * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace Prophecy\PhpDocumentor; +namespace PHPUnit\Event\Test; -use PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\Tags\Method; -use PHPUnitPHAR\phpDocumentor\Reflection\DocBlockFactory; -use PHPUnitPHAR\phpDocumentor\Reflection\Types\ContextFactory; +use PHPUnit\Event\Exception; +use RuntimeException; /** - * @author Théo FIDRY - * - * @internal + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -final class ClassTagRetriever implements \Prophecy\PhpDocumentor\MethodTagRetrieverInterface +final class NoComparisonFailureException extends RuntimeException implements Exception { - private $docBlockFactory; - private $contextFactory; - public function __construct() - { - $this->docBlockFactory = DocBlockFactory::createInstance(); - $this->contextFactory = new ContextFactory(); - } - public function getTagList(\ReflectionClass $reflectionClass) - { - try { - $phpdoc = $this->docBlockFactory->create($reflectionClass, $this->contextFactory->createFromReflector($reflectionClass)); - $methods = array(); - foreach ($phpdoc->getTagsByName('method') as $tag) { - if ($tag instanceof Method) { - $methods[] = $tag; - } - } - return $methods; - } catch (\InvalidArgumentException $e) { - return array(); - } - } } - * Marcello Duarte + * This file is part of PHPUnit. + * + * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace Prophecy\PhpDocumentor; +namespace PHPUnit\Event\TestData; -use PHPUnitPHAR\phpDocumentor\Reflection\DocBlock\Tags\Method; +use PHPUnit\Event\Exception; +use RuntimeException; /** - * @author Théo FIDRY - * - * @internal + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -interface MethodTagRetrieverInterface +final class NoDataSetFromDataProviderException extends RuntimeException implements Exception { - /** - * @param \ReflectionClass $reflectionClass - * - * @return list - */ - public function getTagList(\ReflectionClass $reflectionClass); } - * Marcello Duarte + * This file is part of PHPUnit. + * + * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace Prophecy\Prediction; +namespace PHPUnit\Event; -use Prophecy\Call\Call; -use Prophecy\Prophecy\ObjectProphecy; -use Prophecy\Prophecy\MethodProphecy; -use Prophecy\Argument\ArgumentsWildcard; -use Prophecy\Argument\Token\AnyValuesToken; -use Prophecy\Util\StringUtil; -use Prophecy\Exception\Prediction\NoCallsException; +use RuntimeException; /** - * Tests that there was at least one call. - * - * @author Konstantin Kudryashov + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -class CallPrediction implements \Prophecy\Prediction\PredictionInterface +final class NoPreviousThrowableException extends RuntimeException implements \PHPUnit\Event\Exception { - private $util; - public function __construct(StringUtil $util = null) - { - $this->util = $util ?: new StringUtil(); - } - public function check(array $calls, ObjectProphecy $object, MethodProphecy $method) - { - if (\count($calls)) { - return; - } - $methodCalls = $object->findProphecyMethodCalls($method->getMethodName(), new ArgumentsWildcard(array(new AnyValuesToken()))); - if (\count($methodCalls)) { - throw new NoCallsException(\sprintf("No calls have been made that match:\n" . " %s->%s(%s)\n" . "but expected at least one.\n" . "Recorded `%s(...)` calls:\n%s", \get_class($object->reveal()), $method->getMethodName(), $method->getArgumentsWildcard(), $method->getMethodName(), $this->util->stringifyCalls($methodCalls)), $method); - } - throw new NoCallsException(\sprintf("No calls have been made that match:\n" . " %s->%s(%s)\n" . "but expected at least one.", \get_class($object->reveal()), $method->getMethodName(), $method->getArgumentsWildcard()), $method); - } } - * Marcello Duarte + * This file is part of PHPUnit. + * + * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace Prophecy\Prediction; +namespace PHPUnit\Event\Code; -use Prophecy\Call\Call; -use Prophecy\Prophecy\ObjectProphecy; -use Prophecy\Prophecy\MethodProphecy; -use Prophecy\Argument\ArgumentsWildcard; -use Prophecy\Argument\Token\AnyValuesToken; -use Prophecy\Util\StringUtil; -use Prophecy\Exception\Prediction\UnexpectedCallsCountException; +use PHPUnit\Event\Exception; +use RuntimeException; /** - * Tests that there was exact amount of calls made. - * - * @author Konstantin Kudryashov + * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -class CallTimesPrediction implements \Prophecy\Prediction\PredictionInterface +final class NoTestCaseObjectOnCallStackException extends RuntimeException implements Exception { - private $times; - private $util; - /** - * @param int $times - */ - public function __construct($times, StringUtil $util = null) - { - $this->times = \intval($times); - $this->util = $util ?: new StringUtil(); - } - public function check(array $calls, ObjectProphecy $object, MethodProphecy $method) + public function __construct() { - if ($this->times == \count($calls)) { - return; - } - $methodCalls = $object->findProphecyMethodCalls($method->getMethodName(), new ArgumentsWildcard(array(new AnyValuesToken()))); - if (\count($calls)) { - $message = \sprintf("Expected exactly %d calls that match:\n" . " %s->%s(%s)\n" . "but %d were made:\n%s", $this->times, \get_class($object->reveal()), $method->getMethodName(), $method->getArgumentsWildcard(), \count($calls), $this->util->stringifyCalls($calls)); - } elseif (\count($methodCalls)) { - $message = \sprintf("Expected exactly %d calls that match:\n" . " %s->%s(%s)\n" . "but none were made.\n" . "Recorded `%s(...)` calls:\n%s", $this->times, \get_class($object->reveal()), $method->getMethodName(), $method->getArgumentsWildcard(), $method->getMethodName(), $this->util->stringifyCalls($methodCalls)); - } else { - $message = \sprintf("Expected exactly %d calls that match:\n" . " %s->%s(%s)\n" . "but none were made.", $this->times, \get_class($object->reveal()), $method->getMethodName(), $method->getArgumentsWildcard()); - } - throw new UnexpectedCallsCountException($message, $method, $this->times, $calls); + parent::__construct('Cannot find TestCase object on call stack'); } } - * Marcello Duarte + * This file is part of PHPUnit. + * + * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace Prophecy\Prediction; +namespace PHPUnit\Event; -use Prophecy\Call\Call; -use Prophecy\Prophecy\ObjectProphecy; -use Prophecy\Prophecy\MethodProphecy; -use Prophecy\Exception\InvalidArgumentException; -use Closure; -use ReflectionFunction; /** - * Executes preset callback. - * - * @author Konstantin Kudryashov + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -class CallbackPrediction implements \Prophecy\Prediction\PredictionInterface +final class RuntimeException extends \RuntimeException implements \PHPUnit\Event\Exception { - private $callback; - /** - * @param callable $callback Custom callback - * - * @throws \Prophecy\Exception\InvalidArgumentException - */ - public function __construct($callback) - { - if (!\is_callable($callback)) { - throw new InvalidArgumentException(\sprintf('Callable expected as an argument to CallbackPrediction, but got %s.', \gettype($callback))); - } - $this->callback = $callback; - } - public function check(array $calls, ObjectProphecy $object, MethodProphecy $method) - { - $callback = $this->callback; - if ($callback instanceof Closure && \method_exists('Closure', 'bind') && (new ReflectionFunction($callback))->getClosureThis() !== null) { - $callback = Closure::bind($callback, $object) ?? $this->callback; - } - \call_user_func($callback, $calls, $object, $method); - } } - * Marcello Duarte + * This file is part of PHPUnit. + * + * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace Prophecy\Prediction; +namespace PHPUnit\Event; -use Prophecy\Call\Call; -use Prophecy\Prophecy\ObjectProphecy; -use Prophecy\Prophecy\MethodProphecy; -use Prophecy\Util\StringUtil; -use Prophecy\Exception\Prediction\UnexpectedCallsException; +use RuntimeException; /** - * Tests that there were no calls made. - * - * @author Konstantin Kudryashov + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -class NoCallsPrediction implements \Prophecy\Prediction\PredictionInterface +final class SubscriberTypeAlreadyRegisteredException extends RuntimeException implements \PHPUnit\Event\Exception { - private $util; - public function __construct(StringUtil $util = null) - { - $this->util = $util ?: new StringUtil(); - } - public function check(array $calls, ObjectProphecy $object, MethodProphecy $method) - { - if (!\count($calls)) { - return; - } - $verb = \count($calls) === 1 ? 'was' : 'were'; - throw new UnexpectedCallsException(\sprintf("No calls expected that match:\n" . " %s->%s(%s)\n" . "but %d %s made:\n%s", \get_class($object->reveal()), $method->getMethodName(), $method->getArgumentsWildcard(), \count($calls), $verb, $this->util->stringifyCalls($calls)), $method, $calls); - } } - * Marcello Duarte + * This file is part of PHPUnit. + * + * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace Prophecy\Prediction; +namespace PHPUnit\Event; -use Prophecy\Call\Call; -use Prophecy\Exception\Prediction\PredictionException; -use Prophecy\Prophecy\ObjectProphecy; -use Prophecy\Prophecy\MethodProphecy; +use RuntimeException; /** - * Prediction interface. - * Predictions are logical test blocks, tied to `should...` keyword. - * - * @author Konstantin Kudryashov + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -interface PredictionInterface +final class UnknownEventException extends RuntimeException implements \PHPUnit\Event\Exception { - /** - * Tests that double fulfilled prediction. - * - * @param Call[] $calls - * @param ObjectProphecy $object - * @param MethodProphecy $method - * - * @throws PredictionException - * @return void - */ - public function check(array $calls, ObjectProphecy $object, MethodProphecy $method); } - * Marcello Duarte + * This file is part of PHPUnit. + * + * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace Prophecy\Promise; +namespace PHPUnit\Event; -use Prophecy\Prophecy\ObjectProphecy; -use Prophecy\Prophecy\MethodProphecy; -use Prophecy\Exception\InvalidArgumentException; -use Closure; -use ReflectionFunction; +use RuntimeException; /** - * Evaluates promise callback. - * - * @author Konstantin Kudryashov + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -class CallbackPromise implements \Prophecy\Promise\PromiseInterface +final class UnknownEventTypeException extends RuntimeException implements \PHPUnit\Event\Exception { - private $callback; - /** - * Initializes callback promise. - * - * @param callable $callback Custom callback - * - * @throws \Prophecy\Exception\InvalidArgumentException - */ - public function __construct($callback) - { - if (!\is_callable($callback)) { - throw new InvalidArgumentException(\sprintf('Callable expected as an argument to CallbackPromise, but got %s.', \gettype($callback))); - } - $this->callback = $callback; - } - public function execute(array $args, ObjectProphecy $object, MethodProphecy $method) - { - $callback = $this->callback; - if ($callback instanceof Closure && \method_exists('Closure', 'bind') && (new ReflectionFunction($callback))->getClosureThis() !== null) { - $callback = Closure::bind($callback, $object) ?? $this->callback; - } - return \call_user_func($callback, $args, $object, $method); - } } - * Marcello Duarte + * This file is part of PHPUnit. + * + * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace Prophecy\Promise; +namespace PHPUnit\Event; -use Prophecy\Prophecy\ObjectProphecy; -use Prophecy\Prophecy\MethodProphecy; +use RuntimeException; /** - * Promise interface. - * Promises are logical blocks, tied to `will...` keyword. - * - * @author Konstantin Kudryashov + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -interface PromiseInterface +final class UnknownSubscriberException extends RuntimeException implements \PHPUnit\Event\Exception { - /** - * Evaluates promise. - * - * @param array $args - * @param ObjectProphecy $object - * @param MethodProphecy $method - * - * @return mixed - */ - public function execute(array $args, ObjectProphecy $object, MethodProphecy $method); } - * Marcello Duarte + * This file is part of PHPUnit. + * + * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace Prophecy\Promise; +namespace PHPUnit\Event; -use Prophecy\Exception\InvalidArgumentException; -use Prophecy\Prophecy\ObjectProphecy; -use Prophecy\Prophecy\MethodProphecy; +use RuntimeException; /** - * Returns nth argument if has one, null otherwise. + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class UnknownSubscriberTypeException extends RuntimeException implements \PHPUnit\Event\Exception +{ +} + * - * @author Konstantin Kudryashov + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event; + +use function gc_status; +use PHPUnit\Event\Telemetry\HRTime; +use PHPUnit\Event\Telemetry\Php81GarbageCollectorStatusProvider; +use PHPUnit\Event\Telemetry\Php83GarbageCollectorStatusProvider; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -class ReturnArgumentPromise implements \Prophecy\Promise\PromiseInterface +final class Facade { + private static ?self $instance = null; + private \PHPUnit\Event\Emitter $emitter; + private ?\PHPUnit\Event\TypeMap $typeMap = null; + private ?\PHPUnit\Event\DeferringDispatcher $deferringDispatcher = null; + private bool $sealed = \false; + public static function instance(): self + { + if (self::$instance === null) { + self::$instance = new self(); + } + return self::$instance; + } + public static function emitter(): \PHPUnit\Event\Emitter + { + return self::instance()->emitter; + } + public function __construct() + { + $this->emitter = $this->createDispatchingEmitter(); + } /** - * @var int + * @throws EventFacadeIsSealedException + * @throws UnknownSubscriberTypeException */ - private $index; + public function registerSubscribers(\PHPUnit\Event\Subscriber ...$subscribers): void + { + foreach ($subscribers as $subscriber) { + $this->registerSubscriber($subscriber); + } + } /** - * Initializes callback promise. - * - * @param int $index The zero-indexed number of the argument to return + * @throws EventFacadeIsSealedException + * @throws UnknownSubscriberTypeException + */ + public function registerSubscriber(\PHPUnit\Event\Subscriber $subscriber): void + { + if ($this->sealed) { + throw new \PHPUnit\Event\EventFacadeIsSealedException(); + } + $this->deferredDispatcher()->registerSubscriber($subscriber); + } + /** + * @throws EventFacadeIsSealedException + */ + public function registerTracer(\PHPUnit\Event\Tracer\Tracer $tracer): void + { + if ($this->sealed) { + throw new \PHPUnit\Event\EventFacadeIsSealedException(); + } + $this->deferredDispatcher()->registerTracer($tracer); + } + /** + * @codeCoverageIgnore * - * @throws \Prophecy\Exception\InvalidArgumentException + * @noinspection PhpUnused */ - public function __construct($index = 0) + public function initForIsolation(HRTime $offset, bool $exportObjects): \PHPUnit\Event\CollectingDispatcher + { + $dispatcher = new \PHPUnit\Event\CollectingDispatcher(); + $this->emitter = new \PHPUnit\Event\DispatchingEmitter($dispatcher, new \PHPUnit\Event\Telemetry\System(new \PHPUnit\Event\Telemetry\SystemStopWatchWithOffset($offset), new \PHPUnit\Event\Telemetry\SystemMemoryMeter(), $this->garbageCollectorStatusProvider())); + if ($exportObjects) { + $this->emitter->exportObjects(); + } + $this->sealed = \true; + return $dispatcher; + } + public function forward(\PHPUnit\Event\EventCollection $events): void + { + $dispatcher = $this->deferredDispatcher(); + foreach ($events as $event) { + $dispatcher->dispatch($event); + } + } + public function seal(): void + { + $this->deferredDispatcher()->flush(); + $this->sealed = \true; + $this->emitter->testRunnerEventFacadeSealed(); + } + private function createDispatchingEmitter(): \PHPUnit\Event\DispatchingEmitter + { + return new \PHPUnit\Event\DispatchingEmitter($this->deferredDispatcher(), $this->createTelemetrySystem()); + } + private function createTelemetrySystem(): \PHPUnit\Event\Telemetry\System + { + return new \PHPUnit\Event\Telemetry\System(new \PHPUnit\Event\Telemetry\SystemStopWatch(), new \PHPUnit\Event\Telemetry\SystemMemoryMeter(), $this->garbageCollectorStatusProvider()); + } + private function deferredDispatcher(): \PHPUnit\Event\DeferringDispatcher + { + if ($this->deferringDispatcher === null) { + $this->deferringDispatcher = new \PHPUnit\Event\DeferringDispatcher(new \PHPUnit\Event\DirectDispatcher($this->typeMap())); + } + return $this->deferringDispatcher; + } + private function typeMap(): \PHPUnit\Event\TypeMap + { + if ($this->typeMap === null) { + $typeMap = new \PHPUnit\Event\TypeMap(); + $this->registerDefaultTypes($typeMap); + $this->typeMap = $typeMap; + } + return $this->typeMap; + } + private function registerDefaultTypes(\PHPUnit\Event\TypeMap $typeMap): void { - if (!\is_int($index) || $index < 0) { - throw new InvalidArgumentException(\sprintf('Zero-based index expected as argument to ReturnArgumentPromise, but got %s.', $index)); + $defaultEvents = [\PHPUnit\Event\Application\Started::class, \PHPUnit\Event\Application\Finished::class, \PHPUnit\Event\Test\DataProviderMethodCalled::class, \PHPUnit\Event\Test\DataProviderMethodFinished::class, \PHPUnit\Event\Test\MarkedIncomplete::class, \PHPUnit\Event\Test\AfterLastTestMethodCalled::class, \PHPUnit\Event\Test\AfterLastTestMethodFinished::class, \PHPUnit\Event\Test\AfterTestMethodCalled::class, \PHPUnit\Event\Test\AfterTestMethodFinished::class, \PHPUnit\Event\Test\AssertionSucceeded::class, \PHPUnit\Event\Test\AssertionFailed::class, \PHPUnit\Event\Test\BeforeFirstTestMethodCalled::class, \PHPUnit\Event\Test\BeforeFirstTestMethodErrored::class, \PHPUnit\Event\Test\BeforeFirstTestMethodFinished::class, \PHPUnit\Event\Test\BeforeTestMethodCalled::class, \PHPUnit\Event\Test\BeforeTestMethodFinished::class, \PHPUnit\Event\Test\ComparatorRegistered::class, \PHPUnit\Event\Test\ConsideredRisky::class, \PHPUnit\Event\Test\DeprecationTriggered::class, \PHPUnit\Event\Test\Errored::class, \PHPUnit\Event\Test\ErrorTriggered::class, \PHPUnit\Event\Test\Failed::class, \PHPUnit\Event\Test\Finished::class, \PHPUnit\Event\Test\NoticeTriggered::class, \PHPUnit\Event\Test\Passed::class, \PHPUnit\Event\Test\PhpDeprecationTriggered::class, \PHPUnit\Event\Test\PhpNoticeTriggered::class, \PHPUnit\Event\Test\PhpunitDeprecationTriggered::class, \PHPUnit\Event\Test\PhpunitErrorTriggered::class, \PHPUnit\Event\Test\PhpunitWarningTriggered::class, \PHPUnit\Event\Test\PhpWarningTriggered::class, \PHPUnit\Event\Test\PostConditionCalled::class, \PHPUnit\Event\Test\PostConditionFinished::class, \PHPUnit\Event\Test\PreConditionCalled::class, \PHPUnit\Event\Test\PreConditionFinished::class, \PHPUnit\Event\Test\PreparationStarted::class, \PHPUnit\Event\Test\Prepared::class, \PHPUnit\Event\Test\PreparationFailed::class, \PHPUnit\Event\Test\PrintedUnexpectedOutput::class, \PHPUnit\Event\Test\Skipped::class, \PHPUnit\Event\Test\WarningTriggered::class, \PHPUnit\Event\Test\MockObjectCreated::class, \PHPUnit\Event\Test\MockObjectForAbstractClassCreated::class, \PHPUnit\Event\Test\MockObjectForIntersectionOfInterfacesCreated::class, \PHPUnit\Event\Test\MockObjectForTraitCreated::class, \PHPUnit\Event\Test\MockObjectFromWsdlCreated::class, \PHPUnit\Event\Test\PartialMockObjectCreated::class, \PHPUnit\Event\Test\TestProxyCreated::class, \PHPUnit\Event\Test\TestStubCreated::class, \PHPUnit\Event\Test\TestStubForIntersectionOfInterfacesCreated::class, \PHPUnit\Event\TestRunner\BootstrapFinished::class, \PHPUnit\Event\TestRunner\Configured::class, \PHPUnit\Event\TestRunner\EventFacadeSealed::class, \PHPUnit\Event\TestRunner\ExecutionAborted::class, \PHPUnit\Event\TestRunner\ExecutionFinished::class, \PHPUnit\Event\TestRunner\ExecutionStarted::class, \PHPUnit\Event\TestRunner\ExtensionLoadedFromPhar::class, \PHPUnit\Event\TestRunner\ExtensionBootstrapped::class, \PHPUnit\Event\TestRunner\Finished::class, \PHPUnit\Event\TestRunner\Started::class, \PHPUnit\Event\TestRunner\DeprecationTriggered::class, \PHPUnit\Event\TestRunner\WarningTriggered::class, \PHPUnit\Event\TestRunner\GarbageCollectionDisabled::class, \PHPUnit\Event\TestRunner\GarbageCollectionTriggered::class, \PHPUnit\Event\TestRunner\GarbageCollectionEnabled::class, \PHPUnit\Event\TestSuite\Filtered::class, \PHPUnit\Event\TestSuite\Finished::class, \PHPUnit\Event\TestSuite\Loaded::class, \PHPUnit\Event\TestSuite\Skipped::class, \PHPUnit\Event\TestSuite\Sorted::class, \PHPUnit\Event\TestSuite\Started::class]; + foreach ($defaultEvents as $eventClass) { + $typeMap->addMapping($eventClass . 'Subscriber', $eventClass); } - $this->index = $index; } - public function execute(array $args, ObjectProphecy $object, MethodProphecy $method) + private function garbageCollectorStatusProvider(): \PHPUnit\Event\Telemetry\GarbageCollectorStatusProvider { - return \count($args) > $this->index ? $args[$this->index] : null; + if (!isset(gc_status()['running'])) { + // @codeCoverageIgnoreStart + return new Php81GarbageCollectorStatusProvider(); + // @codeCoverageIgnoreEnd + } + return new Php83GarbageCollectorStatusProvider(); } } - * Marcello Duarte + * This file is part of PHPUnit. + * + * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace Prophecy\Promise; +namespace PHPUnit\Event; -use Prophecy\Prophecy\ObjectProphecy; -use Prophecy\Prophecy\MethodProphecy; /** - * Returns saved values one by one until last one, then continuously returns last value. - * - * @author Konstantin Kudryashov + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -class ReturnPromise implements \Prophecy\Promise\PromiseInterface +interface Subscriber { - private $returnValues = array(); - /** - * Initializes promise. - * - * @param array $returnValues Array of values - */ - public function __construct(array $returnValues) - { - $this->returnValues = $returnValues; - } - public function execute(array $args, ObjectProphecy $object, MethodProphecy $method) - { - $value = \array_shift($this->returnValues); - if (!\count($this->returnValues)) { - $this->returnValues[] = $value; - } - return $value; - } } - * Marcello Duarte + * This file is part of PHPUnit. + * + * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace Prophecy\Promise; +namespace PHPUnit\Event\Tracer; -use PHPUnitPHAR\Doctrine\Instantiator\Instantiator; -use Prophecy\Prophecy\ObjectProphecy; -use Prophecy\Prophecy\MethodProphecy; -use Prophecy\Exception\InvalidArgumentException; -use ReflectionClass; +use PHPUnit\Event\Event; /** - * Throws predefined exception. - * - * @author Konstantin Kudryashov + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -class ThrowPromise implements \Prophecy\Promise\PromiseInterface +interface Tracer { - private $exception; - /** - * @var Instantiator|null - */ - private $instantiator; - /** - * Initializes promise. - * - * @param string|\Throwable $exception Exception class name or instance - * - * @throws \Prophecy\Exception\InvalidArgumentException - * - * @phpstan-param class-string<\Throwable>|\Throwable $exception - */ - public function __construct($exception) - { - if (\is_string($exception)) { - if (!\class_exists($exception) && !\interface_exists($exception) || !$this->isAValidThrowable($exception)) { - throw new InvalidArgumentException(\sprintf('Exception / Throwable class or instance expected as argument to ThrowPromise, but got %s.', $exception)); - } - } elseif (!$exception instanceof \Exception && !$exception instanceof \Throwable) { - throw new InvalidArgumentException(\sprintf('Exception / Throwable class or instance expected as argument to ThrowPromise, but got %s.', \is_object($exception) ? \get_class($exception) : \gettype($exception))); - } - $this->exception = $exception; - } - public function execute(array $args, ObjectProphecy $object, MethodProphecy $method) - { - if (\is_string($this->exception)) { - $classname = $this->exception; - $reflection = new ReflectionClass($classname); - $constructor = $reflection->getConstructor(); - if ($constructor === null || $constructor->isPublic() && 0 == $constructor->getNumberOfRequiredParameters()) { - throw $reflection->newInstance(); - } - if (!$this->instantiator) { - $this->instantiator = new Instantiator(); - } - throw $this->instantiator->instantiate($classname); - } - throw $this->exception; - } - /** - * @param string $exception - * - * @return bool - */ - private function isAValidThrowable($exception) - { - return \is_a($exception, 'Exception', \true) || \is_a($exception, 'Throwable', \true); - } + public function trace(Event $event): void; } - * Marcello Duarte + * This file is part of PHPUnit. + * + * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace Prophecy\Prophecy; +namespace PHPUnit\Event; -use Prophecy\Argument; -use Prophecy\Exception\Prediction\PredictionException; -use Prophecy\Prophet; -use Prophecy\Promise; -use Prophecy\Prediction; -use Prophecy\Exception\Doubler\MethodNotFoundException; -use Prophecy\Exception\InvalidArgumentException; -use Prophecy\Exception\Prophecy\MethodProphecyException; -use ReflectionNamedType; -use ReflectionUnionType; +use function array_key_exists; +use function class_exists; +use function class_implements; +use function in_array; +use function interface_exists; +use function sprintf; /** - * Method prophecy. - * - * @author Konstantin Kudryashov + * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -class MethodProphecy +final class TypeMap { - private $objectProphecy; - private $methodName; - /** - * @var Argument\ArgumentsWildcard - */ - private $argumentsWildcard; - /** - * @var Promise\PromiseInterface|null - */ - private $promise; - /** - * @var Prediction\PredictionInterface|null - */ - private $prediction; - /** - * @var list - */ - private $checkedPredictions = array(); - /** - * @var bool - */ - private $bound = \false; /** - * @var bool + * @psalm-var array */ - private $voidReturnType = \false; + private array $mapping = []; /** - * @param ObjectProphecy $objectProphecy - * @param string $methodName - * @param Argument\ArgumentsWildcard|array $arguments - * - * @throws \Prophecy\Exception\Doubler\MethodNotFoundException If method not found + * @psalm-param class-string $subscriberInterface + * @psalm-param class-string $eventClass * - * @internal + * @throws EventAlreadyAssignedException + * @throws InvalidEventException + * @throws InvalidSubscriberException + * @throws SubscriberTypeAlreadyRegisteredException + * @throws UnknownEventException + * @throws UnknownSubscriberException */ - public function __construct(\Prophecy\Prophecy\ObjectProphecy $objectProphecy, $methodName, $arguments) + public function addMapping(string $subscriberInterface, string $eventClass): void { - $double = $objectProphecy->reveal(); - if (!\method_exists($double, $methodName)) { - throw new MethodNotFoundException(\sprintf('Method `%s::%s()` is not defined.', \get_class($double), $methodName), \get_class($double), $methodName, $arguments); - } - $this->objectProphecy = $objectProphecy; - $this->methodName = $methodName; - $reflectedMethod = new \ReflectionMethod($double, $methodName); - if ($reflectedMethod->isFinal()) { - throw new MethodProphecyException(\sprintf("Can not add prophecy for a method `%s::%s()`\n" . "as it is a final method.", \get_class($double), $methodName), $this); - } - $this->withArguments($arguments); - $hasTentativeReturnType = \method_exists($reflectedMethod, 'hasTentativeReturnType') && $reflectedMethod->hasTentativeReturnType(); - if (\true === $reflectedMethod->hasReturnType() || $hasTentativeReturnType) { - if ($hasTentativeReturnType) { - $reflectionType = $reflectedMethod->getTentativeReturnType(); - } else { - $reflectionType = $reflectedMethod->getReturnType(); + $this->ensureSubscriberInterfaceExists($subscriberInterface); + $this->ensureSubscriberInterfaceExtendsInterface($subscriberInterface); + $this->ensureEventClassExists($eventClass); + $this->ensureEventClassImplementsEventInterface($eventClass); + $this->ensureSubscriberWasNotAlreadyRegistered($subscriberInterface); + $this->ensureEventWasNotAlreadyAssigned($eventClass); + $this->mapping[$subscriberInterface] = $eventClass; + } + public function isKnownSubscriberType(\PHPUnit\Event\Subscriber $subscriber): bool + { + foreach (class_implements($subscriber) as $interface) { + if (array_key_exists($interface, $this->mapping)) { + return \true; } - if ($reflectionType instanceof ReflectionNamedType) { - $types = [$reflectionType]; - } elseif ($reflectionType instanceof ReflectionUnionType) { - $types = $reflectionType->getTypes(); - } else { - throw new MethodProphecyException(\sprintf("Can not add prophecy for a method `%s::%s()`\nas its return type is not supported by Prophecy yet.", \get_class($double), $methodName), $this); - } - $types = \array_map(function (ReflectionNamedType $type) { - return $type->getName(); - }, $types); - \usort($types, static function (string $type1, string $type2) { - // null is lowest priority - if ($type2 == 'null') { - return -1; - } elseif ($type1 == 'null') { - return 1; - } - // objects are higher priority than scalars - $isObject = static function ($type) { - return \class_exists($type) || \interface_exists($type); - }; - if ($isObject($type1) && !$isObject($type2)) { - return -1; - } elseif (!$isObject($type1) && $isObject($type2)) { - return 1; - } - // don't sort both-scalars or both-objects - return 0; - }); - $defaultType = $types[0]; - if ('void' === $defaultType) { - $this->voidReturnType = \true; - } - $this->will(function ($args, \Prophecy\Prophecy\ObjectProphecy $object, \Prophecy\Prophecy\MethodProphecy $method) use($defaultType) { - switch ($defaultType) { - case 'void': - return; - case 'string': - return ''; - case 'float': - return 0.0; - case 'int': - return 0; - case 'bool': - return \false; - case 'array': - return array(); - case 'true': - return \true; - case 'false': - return \false; - case 'null': - return null; - case 'callable': - case 'Closure': - return function () { - }; - case 'Traversable': - case 'Generator': - return (function () { - yield; - })(); - case 'object': - $prophet = new Prophet(); - return $prophet->prophesize()->reveal(); - default: - if (!\class_exists($defaultType) && !\interface_exists($defaultType)) { - throw new MethodProphecyException(\sprintf('Cannot create a return value for the method as the type "%s" is not supported. Configure an explicit return value instead.', $defaultType), $method); - } - $prophet = new Prophet(); - return $prophet->prophesize($defaultType)->reveal(); - } - }); } + return \false; + } + public function isKnownEventType(\PHPUnit\Event\Event $event): bool + { + return in_array($event::class, $this->mapping, \true); } /** - * Sets argument wildcard. - * - * @param array|Argument\ArgumentsWildcard $arguments - * - * @return $this + * @psalm-return class-string * - * @throws \Prophecy\Exception\InvalidArgumentException + * @throws MapError */ - public function withArguments($arguments) + public function map(\PHPUnit\Event\Subscriber $subscriber): string { - if (\is_array($arguments)) { - $arguments = new Argument\ArgumentsWildcard($arguments); - } - if (!$arguments instanceof Argument\ArgumentsWildcard) { - throw new InvalidArgumentException(\sprintf("Either an array or an instance of ArgumentsWildcard expected as\n" . 'a `MethodProphecy::withArguments()` argument, but got %s.', \gettype($arguments))); + foreach (class_implements($subscriber) as $interface) { + if (array_key_exists($interface, $this->mapping)) { + return $this->mapping[$interface]; + } } - $this->argumentsWildcard = $arguments; - return $this; + throw new \PHPUnit\Event\MapError(sprintf('Subscriber "%s" does not implement a known interface', $subscriber::class)); } /** - * Sets custom promise to the prophecy. - * - * @param callable|Promise\PromiseInterface $promise - * - * @return $this + * @psalm-param class-string $subscriberInterface * - * @throws \Prophecy\Exception\InvalidArgumentException + * @throws UnknownSubscriberException */ - public function will($promise) + private function ensureSubscriberInterfaceExists(string $subscriberInterface): void { - if (\is_callable($promise)) { - $promise = new Promise\CallbackPromise($promise); - } - if (!$promise instanceof Promise\PromiseInterface) { - throw new InvalidArgumentException(\sprintf('Expected callable or instance of PromiseInterface, but got %s.', \gettype($promise))); + if (!interface_exists($subscriberInterface)) { + throw new \PHPUnit\Event\UnknownSubscriberException(sprintf('Subscriber "%s" does not exist or is not an interface', $subscriberInterface)); } - $this->bindToObjectProphecy(); - $this->promise = $promise; - return $this; } /** - * Sets return promise to the prophecy. - * - * @see \Prophecy\Promise\ReturnPromise + * @psalm-param class-string $eventClass * - * @param mixed ...$return a list of return values - * - * @return $this + * @throws UnknownEventException */ - public function willReturn(...$return) + private function ensureEventClassExists(string $eventClass): void { - if ($this->voidReturnType) { - throw new MethodProphecyException("The method \"{$this->methodName}\" has a void return type, and so cannot return anything", $this); + if (!class_exists($eventClass)) { + throw new \PHPUnit\Event\UnknownEventException(sprintf('Event class "%s" does not exist', $eventClass)); } - return $this->will(new Promise\ReturnPromise($return)); } /** - * @param array $items - * @param mixed $return - * - * @return $this + * @psalm-param class-string $subscriberInterface * - * @throws \Prophecy\Exception\InvalidArgumentException + * @throws InvalidSubscriberException */ - public function willYield($items, $return = null) + private function ensureSubscriberInterfaceExtendsInterface(string $subscriberInterface): void { - if ($this->voidReturnType) { - throw new MethodProphecyException("The method \"{$this->methodName}\" has a void return type, and so cannot yield anything", $this); + if (!in_array(\PHPUnit\Event\Subscriber::class, class_implements($subscriberInterface), \true)) { + throw new \PHPUnit\Event\InvalidSubscriberException(sprintf('Subscriber "%s" does not extend Subscriber interface', $subscriberInterface)); } - if (!\is_array($items)) { - throw new InvalidArgumentException(\sprintf('Expected array, but got %s.', \gettype($items))); - } - $generator = function () use($items, $return) { - yield from $items; - return $return; - }; - return $this->will($generator); } /** - * Sets return argument promise to the prophecy. - * - * @param int $index The zero-indexed number of the argument to return - * - * @see \Prophecy\Promise\ReturnArgumentPromise + * @psalm-param class-string $eventClass * - * @return $this + * @throws InvalidEventException */ - public function willReturnArgument($index = 0) + private function ensureEventClassImplementsEventInterface(string $eventClass): void { - if ($this->voidReturnType) { - throw new MethodProphecyException("The method \"{$this->methodName}\" has a void return type", $this); + if (!in_array(\PHPUnit\Event\Event::class, class_implements($eventClass), \true)) { + throw new \PHPUnit\Event\InvalidEventException(sprintf('Event "%s" does not implement Event interface', $eventClass)); } - return $this->will(new Promise\ReturnArgumentPromise($index)); } /** - * Sets throw promise to the prophecy. - * - * @see \Prophecy\Promise\ThrowPromise - * - * @param string|\Throwable $exception Exception class or instance - * - * @return $this + * @psalm-param class-string $subscriberInterface * - * @phpstan-param class-string<\Throwable>|\Throwable $exception + * @throws SubscriberTypeAlreadyRegisteredException */ - public function willThrow($exception) + private function ensureSubscriberWasNotAlreadyRegistered(string $subscriberInterface): void { - return $this->will(new Promise\ThrowPromise($exception)); + if (array_key_exists($subscriberInterface, $this->mapping)) { + throw new \PHPUnit\Event\SubscriberTypeAlreadyRegisteredException(sprintf('Subscriber type "%s" already registered', $subscriberInterface)); + } } /** - * Sets custom prediction to the prophecy. - * - * @param callable|Prediction\PredictionInterface $prediction - * - * @return $this + * @psalm-param class-string $eventClass * - * @throws \Prophecy\Exception\InvalidArgumentException + * @throws EventAlreadyAssignedException */ - public function should($prediction) + private function ensureEventWasNotAlreadyAssigned(string $eventClass): void { - if (\is_callable($prediction)) { - $prediction = new Prediction\CallbackPrediction($prediction); - } - if (!$prediction instanceof Prediction\PredictionInterface) { - throw new InvalidArgumentException(\sprintf('Expected callable or instance of PredictionInterface, but got %s.', \gettype($prediction))); + if (in_array($eventClass, $this->mapping, \true)) { + throw new \PHPUnit\Event\EventAlreadyAssignedException(sprintf('Event "%s" already assigned', $eventClass)); } - $this->bindToObjectProphecy(); - $this->prediction = $prediction; - return $this; } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\Code; + +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class ClassMethod +{ /** - * Sets call prediction to the prophecy. - * - * @see \Prophecy\Prediction\CallPrediction - * - * @return $this + * @psalm-var class-string */ - public function shouldBeCalled() - { - return $this->should(new Prediction\CallPrediction()); - } + private readonly string $className; /** - * Sets no calls prediction to the prophecy. - * - * @see \Prophecy\Prediction\NoCallsPrediction - * - * @return $this + * @psalm-var non-empty-string */ - public function shouldNotBeCalled() - { - return $this->should(new Prediction\NoCallsPrediction()); - } + private readonly string $methodName; /** - * Sets call times prediction to the prophecy. - * - * @see \Prophecy\Prediction\CallTimesPrediction - * - * @param int $count - * - * @return $this + * @psalm-param class-string $className + * @psalm-param non-empty-string $methodName */ - public function shouldBeCalledTimes($count) + public function __construct(string $className, string $methodName) { - return $this->should(new Prediction\CallTimesPrediction($count)); + $this->className = $className; + $this->methodName = $methodName; } /** - * Sets call times prediction to the prophecy. - * - * @see \Prophecy\Prediction\CallTimesPrediction - * - * @return $this + * @psalm-return class-string */ - public function shouldBeCalledOnce() + public function className(): string { - return $this->shouldBeCalledTimes(1); + return $this->className; } /** - * Checks provided prediction immediately. - * - * @param callable|Prediction\PredictionInterface $prediction - * - * @return $this - * - * @throws \Prophecy\Exception\InvalidArgumentException - * @throws PredictionException + * @psalm-return non-empty-string */ - public function shouldHave($prediction) + public function methodName(): string + { + return $this->methodName; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\Code; + +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class ComparisonFailure +{ + private readonly string $expected; + private readonly string $actual; + private readonly string $diff; + public function __construct(string $expected, string $actual, string $diff) + { + $this->expected = $expected; + $this->actual = $actual; + $this->diff = $diff; + } + public function expected(): string + { + return $this->expected; + } + public function actual(): string + { + return $this->actual; + } + public function diff(): string + { + return $this->diff; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\Code; + +use function is_bool; +use function is_scalar; +use function print_r; +use PHPUnit\Framework\ExpectationFailedException; +use Throwable; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class ComparisonFailureBuilder +{ + public static function from(Throwable $t): ?\PHPUnit\Event\Code\ComparisonFailure { - if (\is_callable($prediction)) { - $prediction = new Prediction\CallbackPrediction($prediction); + if (!$t instanceof ExpectationFailedException) { + return null; } - if (!$prediction instanceof Prediction\PredictionInterface) { - throw new InvalidArgumentException(\sprintf('Expected callable or instance of PredictionInterface, but got %s.', \gettype($prediction))); + if (!$t->getComparisonFailure()) { + return null; } - if (null === $this->promise && !$this->voidReturnType) { - $this->willReturn(); + $expectedAsString = $t->getComparisonFailure()->getExpectedAsString(); + if (empty($expectedAsString)) { + $expectedAsString = self::mapScalarValueToString($t->getComparisonFailure()->getExpected()); } - $calls = $this->getObjectProphecy()->findProphecyMethodCalls($this->getMethodName(), $this->getArgumentsWildcard()); - try { - $prediction->check($calls, $this->getObjectProphecy(), $this); - $this->checkedPredictions[] = $prediction; - } catch (\Exception $e) { - $this->checkedPredictions[] = $prediction; - throw $e; + $actualAsString = $t->getComparisonFailure()->getActualAsString(); + if (empty($actualAsString)) { + $actualAsString = self::mapScalarValueToString($t->getComparisonFailure()->getActual()); } - return $this; + return new \PHPUnit\Event\Code\ComparisonFailure($expectedAsString, $actualAsString, $t->getComparisonFailure()->getDiff()); } - /** - * Checks call prediction. - * - * @see \Prophecy\Prediction\CallPrediction - * - * @return $this - * - * @throws PredictionException - */ - public function shouldHaveBeenCalled() + private static function mapScalarValueToString(mixed $value): string { - return $this->shouldHave(new Prediction\CallPrediction()); + if ($value === null) { + return 'null'; + } + if (is_bool($value)) { + return $value ? 'true' : 'false'; + } + if (is_scalar($value)) { + return print_r($value, \true); + } + return ''; } - /** - * Checks no calls prediction. - * - * @see \Prophecy\Prediction\NoCallsPrediction - * - * @return $this - * - * @throws PredictionException - */ - public function shouldNotHaveBeenCalled() +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\Runtime; + +use const PHP_OS; +use const PHP_OS_FAMILY; +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class OperatingSystem +{ + private readonly string $operatingSystem; + private readonly string $operatingSystemFamily; + public function __construct() { - return $this->shouldHave(new Prediction\NoCallsPrediction()); + $this->operatingSystem = PHP_OS; + $this->operatingSystemFamily = PHP_OS_FAMILY; } - /** - * Checks no calls prediction. - * - * @see \Prophecy\Prediction\NoCallsPrediction - * @deprecated - * - * @return $this - */ - public function shouldNotBeenCalled() + public function operatingSystem(): string { - return $this->shouldNotHaveBeenCalled(); + return $this->operatingSystem; } - /** - * Checks call times prediction. - * - * @see \Prophecy\Prediction\CallTimesPrediction - * - * @param int $count - * - * @return $this - */ - public function shouldHaveBeenCalledTimes($count) + public function operatingSystemFamily(): string { - return $this->shouldHave(new Prediction\CallTimesPrediction($count)); + return $this->operatingSystemFamily; } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\Runtime; + +use const PHP_EXTRA_VERSION; +use const PHP_MAJOR_VERSION; +use const PHP_MINOR_VERSION; +use const PHP_RELEASE_VERSION; +use const PHP_SAPI; +use const PHP_VERSION; +use const PHP_VERSION_ID; +use function array_merge; +use function get_loaded_extensions; +use function sort; +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class PHP +{ + private readonly string $version; + private readonly int $versionId; + private readonly int $majorVersion; + private readonly int $minorVersion; + private readonly int $releaseVersion; + private readonly string $extraVersion; + private readonly string $sapi; /** - * Checks call times prediction. - * - * @see \Prophecy\Prediction\CallTimesPrediction - * - * @return $this + * @psalm-var list */ - public function shouldHaveBeenCalledOnce() + private readonly array $extensions; + public function __construct() { - return $this->shouldHaveBeenCalledTimes(1); + $this->version = PHP_VERSION; + $this->versionId = PHP_VERSION_ID; + $this->majorVersion = PHP_MAJOR_VERSION; + $this->minorVersion = PHP_MINOR_VERSION; + $this->releaseVersion = PHP_RELEASE_VERSION; + $this->extraVersion = PHP_EXTRA_VERSION; + $this->sapi = PHP_SAPI; + $extensions = array_merge(get_loaded_extensions(\true), get_loaded_extensions()); + sort($extensions); + $this->extensions = $extensions; } - /** - * Checks currently registered [with should(...)] prediction. - * - * @return void - * - * @throws PredictionException - */ - public function checkPrediction() + public function version(): string { - if (null === $this->prediction) { - return; - } - $this->shouldHave($this->prediction); + return $this->version; } - /** - * Returns currently registered promise. - * - * @return null|Promise\PromiseInterface - */ - public function getPromise() + public function sapi(): string { - return $this->promise; + return $this->sapi; } - /** - * Returns currently registered prediction. - * - * @return null|Prediction\PredictionInterface - */ - public function getPrediction() + public function majorVersion(): int { - return $this->prediction; + return $this->majorVersion; } - /** - * Returns predictions that were checked on this object. - * - * @return list - */ - public function getCheckedPredictions() + public function minorVersion(): int { - return $this->checkedPredictions; + return $this->minorVersion; } - /** - * Returns object prophecy this method prophecy is tied to. - * - * @return ObjectProphecy - */ - public function getObjectProphecy() + public function releaseVersion(): int { - return $this->objectProphecy; + return $this->releaseVersion; } - /** - * Returns method name. - * - * @return string - */ - public function getMethodName() + public function extraVersion(): string { - return $this->methodName; + return $this->extraVersion; } - /** - * Returns arguments wildcard. - * - * @return Argument\ArgumentsWildcard - */ - public function getArgumentsWildcard() + public function versionId(): int { - return $this->argumentsWildcard; + return $this->versionId; } /** - * @return bool + * @psalm-return list */ - public function hasReturnVoid() + public function extensions(): array { - return $this->voidReturnType; + return $this->extensions; } - /** - * @return void - */ - private function bindToObjectProphecy() +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\Runtime; + +use PHPUnit\Runner\Version; +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class PHPUnit +{ + private readonly string $versionId; + private readonly string $releaseSeries; + public function __construct() { - if ($this->bound) { - return; - } - $this->getObjectProphecy()->addMethodProphecy($this); - $this->bound = \true; + $this->versionId = Version::id(); + $this->releaseSeries = Version::series(); + } + public function versionId(): string + { + return $this->versionId; + } + public function releaseSeries(): string + { + return $this->releaseSeries; } } - * Marcello Duarte + * This file is part of PHPUnit. + * + * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace Prophecy\Prophecy; +namespace PHPUnit\Event\Runtime; -use Prophecy\Comparator\FactoryProvider; -use PHPUnitPHAR\SebastianBergmann\Comparator\ComparisonFailure; -use PHPUnitPHAR\SebastianBergmann\Comparator\Factory as ComparatorFactory; -use Prophecy\Call\Call; -use Prophecy\Doubler\LazyDouble; -use Prophecy\Argument\ArgumentsWildcard; -use Prophecy\Call\CallCenter; -use Prophecy\Exception\Prophecy\ObjectProphecyException; -use Prophecy\Exception\Prophecy\MethodProphecyException; -use Prophecy\Exception\Prediction\AggregateException; -use Prophecy\Exception\Prediction\PredictionException; +use function sprintf; /** - * @author Konstantin Kudryashov + * @psalm-immutable * - * @template-covariant T of object - * @template-implements ProphecyInterface + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -class ObjectProphecy implements \Prophecy\Prophecy\ProphecyInterface +final class Runtime { + private readonly \PHPUnit\Event\Runtime\OperatingSystem $operatingSystem; + private readonly \PHPUnit\Event\Runtime\PHP $php; + private readonly \PHPUnit\Event\Runtime\PHPUnit $phpunit; + public function __construct() + { + $this->operatingSystem = new \PHPUnit\Event\Runtime\OperatingSystem(); + $this->php = new \PHPUnit\Event\Runtime\PHP(); + $this->phpunit = new \PHPUnit\Event\Runtime\PHPUnit(); + } + public function asString(): string + { + $php = $this->php(); + return sprintf('PHPUnit %s using PHP %s (%s) on %s', $this->phpunit()->versionId(), $php->version(), $php->sapi(), $this->operatingSystem()->operatingSystem()); + } + public function operatingSystem(): \PHPUnit\Event\Runtime\OperatingSystem + { + return $this->operatingSystem; + } + public function php(): \PHPUnit\Event\Runtime\PHP + { + return $this->php; + } + public function phpunit(): \PHPUnit\Event\Runtime\PHPUnit + { + return $this->phpunit; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\Telemetry; + +use function floor; +use function sprintf; +use PHPUnit\Event\InvalidArgumentException; +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class Duration +{ + private readonly int $seconds; + private readonly int $nanoseconds; /** - * @var LazyDouble - */ - private $lazyDouble; - private $callCenter; - private $revealer; - private $comparatorFactory; - /** - * @var array> - */ - private $methodProphecies = array(); - /** - * @param LazyDouble $lazyDouble + * @throws InvalidArgumentException */ - public function __construct(LazyDouble $lazyDouble, CallCenter $callCenter = null, \Prophecy\Prophecy\RevealerInterface $revealer = null, ComparatorFactory $comparatorFactory = null) + public static function fromSecondsAndNanoseconds(int $seconds, int $nanoseconds): self { - $this->lazyDouble = $lazyDouble; - $this->callCenter = $callCenter ?: new CallCenter(); - $this->revealer = $revealer ?: new \Prophecy\Prophecy\Revealer(); - $this->comparatorFactory = $comparatorFactory ?: FactoryProvider::getInstance(); + return new self($seconds, $nanoseconds); } /** - * Forces double to extend specific class. - * - * @param string $class - * - * @return $this - * - * @template U of object - * @phpstan-param class-string $class - * @phpstan-this-out static + * @throws InvalidArgumentException */ - public function willExtend($class) + private function __construct(int $seconds, int $nanoseconds) { - $this->lazyDouble->setParentClass($class); - return $this; + $this->ensureNotNegative($seconds, 'seconds'); + $this->ensureNotNegative($nanoseconds, 'nanoseconds'); + $this->ensureNanoSecondsInRange($nanoseconds); + $this->seconds = $seconds; + $this->nanoseconds = $nanoseconds; + } + public function seconds(): int + { + return $this->seconds; + } + public function nanoseconds(): int + { + return $this->nanoseconds; + } + public function asFloat(): float + { + return $this->seconds() + $this->nanoseconds() / 1000000000; + } + public function asString(): string + { + $seconds = $this->seconds(); + $minutes = 0; + $hours = 0; + if ($seconds > 60 * 60) { + $hours = floor($seconds / 60 / 60); + $seconds -= $hours * 60 * 60; + } + if ($seconds > 60) { + $minutes = floor($seconds / 60); + $seconds -= $minutes * 60; + } + return sprintf('%02d:%02d:%02d.%09d', $hours, $minutes, $seconds, $this->nanoseconds()); + } + public function equals(self $other): bool + { + return $this->seconds === $other->seconds && $this->nanoseconds === $other->nanoseconds; + } + public function isLessThan(self $other): bool + { + if ($this->seconds < $other->seconds) { + return \true; + } + if ($this->seconds > $other->seconds) { + return \false; + } + return $this->nanoseconds < $other->nanoseconds; + } + public function isGreaterThan(self $other): bool + { + if ($this->seconds > $other->seconds) { + return \true; + } + if ($this->seconds < $other->seconds) { + return \false; + } + return $this->nanoseconds > $other->nanoseconds; } /** - * Forces double to implement specific interface. - * - * @param string $interface - * - * @return $this - * - * @template U of object - * @phpstan-param class-string $interface - * @phpstan-this-out static + * @throws InvalidArgumentException */ - public function willImplement($interface) + private function ensureNotNegative(int $value, string $type): void { - $this->lazyDouble->addInterface($interface); - return $this; + if ($value < 0) { + throw new InvalidArgumentException(sprintf('Value for %s must not be negative.', $type)); + } } /** - * Sets constructor arguments. - * - * @param array $arguments - * - * @return $this + * @throws InvalidArgumentException */ - public function willBeConstructedWith(array $arguments = null) + private function ensureNanoSecondsInRange(int $nanoseconds): void { - $this->lazyDouble->setArguments($arguments); - return $this; + if ($nanoseconds > 999999999) { + throw new InvalidArgumentException('Value for nanoseconds must not be greater than 999999999.'); + } + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\Telemetry; + +use PHPUnit\Event\RuntimeException; +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class GarbageCollectorStatus +{ + private readonly int $runs; + private readonly int $collected; + private readonly int $threshold; + private readonly int $roots; + private readonly ?float $applicationTime; + private readonly ?float $collectorTime; + private readonly ?float $destructorTime; + private readonly ?float $freeTime; + private readonly ?bool $running; + private readonly ?bool $protected; + private readonly ?bool $full; + private readonly ?int $bufferSize; + public function __construct(int $runs, int $collected, int $threshold, int $roots, ?float $applicationTime, ?float $collectorTime, ?float $destructorTime, ?float $freeTime, ?bool $running, ?bool $protected, ?bool $full, ?int $bufferSize) + { + $this->runs = $runs; + $this->collected = $collected; + $this->threshold = $threshold; + $this->roots = $roots; + $this->applicationTime = $applicationTime; + $this->collectorTime = $collectorTime; + $this->destructorTime = $destructorTime; + $this->freeTime = $freeTime; + $this->running = $running; + $this->protected = $protected; + $this->full = $full; + $this->bufferSize = $bufferSize; + } + public function runs(): int + { + return $this->runs; + } + public function collected(): int + { + return $this->collected; + } + public function threshold(): int + { + return $this->threshold; + } + public function roots(): int + { + return $this->roots; } /** - * Reveals double. - * - * @return object - * - * @throws \Prophecy\Exception\Prophecy\ObjectProphecyException If double doesn't implement needed interface - * - * @phpstan-return T + * @psalm-assert-if-true !null $this->applicationTime + * @psalm-assert-if-true !null $this->collectorTime + * @psalm-assert-if-true !null $this->destructorTime + * @psalm-assert-if-true !null $this->freeTime + * @psalm-assert-if-true !null $this->running + * @psalm-assert-if-true !null $this->protected + * @psalm-assert-if-true !null $this->full + * @psalm-assert-if-true !null $this->bufferSize */ - public function reveal() + public function hasExtendedInformation(): bool { - $double = $this->lazyDouble->getInstance(); - if (!$double instanceof \Prophecy\Prophecy\ProphecySubjectInterface) { - throw new ObjectProphecyException("Generated double must implement ProphecySubjectInterface, but it does not.\n" . 'It seems you have wrongly configured doubler without required ClassPatch.', $this); - } - $double->setProphecy($this); - return $double; + return $this->running !== null; } /** - * Adds method prophecy to object prophecy. - * - * @param MethodProphecy $methodProphecy - * - * @return void + * @throws RuntimeException on PHP < 8.3 */ - public function addMethodProphecy(\Prophecy\Prophecy\MethodProphecy $methodProphecy) + public function applicationTime(): float { - $methodName = \strtolower($methodProphecy->getMethodName()); - if (!isset($this->methodProphecies[$methodName])) { - $this->methodProphecies[$methodName] = array(); + if ($this->applicationTime === null) { + throw new RuntimeException('Information not available'); } - $this->methodProphecies[$methodName][] = $methodProphecy; + return $this->applicationTime; } /** - * Returns either all or related to single method prophecies. - * - * @param null|string $methodName - * - * @return MethodProphecy[]|array - * - * @phpstan-return ($methodName is string ? list : array>) + * @throws RuntimeException on PHP < 8.3 */ - public function getMethodProphecies($methodName = null) + public function collectorTime(): float { - if (null === $methodName) { - return $this->methodProphecies; - } - $methodName = \strtolower($methodName); - if (!isset($this->methodProphecies[$methodName])) { - return array(); + if ($this->collectorTime === null) { + throw new RuntimeException('Information not available'); } - return $this->methodProphecies[$methodName]; + return $this->collectorTime; } /** - * Makes specific method call. - * - * @param string $methodName - * @param array $arguments - * - * @return mixed + * @throws RuntimeException on PHP < 8.3 */ - public function makeProphecyMethodCall($methodName, array $arguments) + public function destructorTime(): float { - $arguments = $this->revealer->reveal($arguments); - \assert(\is_array($arguments)); - $return = $this->callCenter->makeCall($this, $methodName, $arguments); - return $this->revealer->reveal($return); + if ($this->destructorTime === null) { + throw new RuntimeException('Information not available'); + } + return $this->destructorTime; } /** - * Finds calls by method name & arguments wildcard. - * - * @param string $methodName - * @param ArgumentsWildcard $wildcard - * - * @return list + * @throws RuntimeException on PHP < 8.3 */ - public function findProphecyMethodCalls($methodName, ArgumentsWildcard $wildcard) + public function freeTime(): float { - return $this->callCenter->findCalls($methodName, $wildcard); + if ($this->freeTime === null) { + throw new RuntimeException('Information not available'); + } + return $this->freeTime; } /** - * Checks that registered method predictions do not fail. - * - * @return void - * - * @throws \Prophecy\Exception\Prediction\AggregateException If any of registered predictions fail - * @throws \Prophecy\Exception\Call\UnexpectedCallException + * @throws RuntimeException on PHP < 8.3 */ - public function checkProphecyMethodsPredictions() + public function isRunning(): bool { - $exception = new AggregateException(\sprintf("%s:\n", \get_class($this->reveal()))); - $exception->setObjectProphecy($this); - $this->callCenter->checkUnexpectedCalls(); - foreach ($this->methodProphecies as $prophecies) { - foreach ($prophecies as $prophecy) { - try { - $prophecy->checkPrediction(); - } catch (PredictionException $e) { - $exception->append($e); - } - } - } - if (\count($exception->getExceptions())) { - throw $exception; + if ($this->running === null) { + throw new RuntimeException('Information not available'); } + return $this->running; } /** - * Creates new method prophecy using specified method name and arguments. - * - * @param string $methodName - * @param array $arguments - * - * @return MethodProphecy + * @throws RuntimeException on PHP < 8.3 */ - public function __call($methodName, array $arguments) + public function isProtected(): bool { - $arguments = $this->revealer->reveal($arguments); - \assert(\is_array($arguments)); - $arguments = new ArgumentsWildcard($arguments); - foreach ($this->getMethodProphecies($methodName) as $prophecy) { - $argumentsWildcard = $prophecy->getArgumentsWildcard(); - $comparator = $this->comparatorFactory->getComparatorFor($argumentsWildcard, $arguments); - try { - $comparator->assertEquals($argumentsWildcard, $arguments); - return $prophecy; - } catch (ComparisonFailure $failure) { - } + if ($this->protected === null) { + throw new RuntimeException('Information not available'); } - return new \Prophecy\Prophecy\MethodProphecy($this, $methodName, $arguments); + return $this->protected; } /** - * Tries to get property value from double. - * - * @param string $name - * - * @return mixed + * @throws RuntimeException on PHP < 8.3 */ - public function __get($name) + public function isFull(): bool { - return $this->reveal()->{$name}; + if ($this->full === null) { + throw new RuntimeException('Information not available'); + } + return $this->full; } /** - * Tries to set property value to double. - * - * @param string $name - * @param mixed $value - * - * @return void + * @throws RuntimeException on PHP < 8.3 */ - public function __set($name, $value) + public function bufferSize(): int { - $this->reveal()->{$name} = $this->revealer->reveal($value); + if ($this->bufferSize === null) { + throw new RuntimeException('Information not available'); + } + return $this->bufferSize; } } - * Marcello Duarte + * This file is part of PHPUnit. + * + * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace Prophecy\Prophecy; +namespace PHPUnit\Event\Telemetry; /** - * Core Prophecy interface. - * - * @author Konstantin Kudryashov - * - * @template-covariant T of object + * @internal This interface is not covered by the backward compatibility promise for PHPUnit */ -interface ProphecyInterface +interface GarbageCollectorStatusProvider { - /** - * Reveals prophecy object (double) . - * - * @return object - * - * @phpstan-return T - */ - public function reveal(); + public function status(): \PHPUnit\Event\Telemetry\GarbageCollectorStatus; } - * Marcello Duarte + * This file is part of PHPUnit. + * + * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace Prophecy\Prophecy; +namespace PHPUnit\Event\Telemetry; +use function sprintf; +use PHPUnit\Event\InvalidArgumentException; /** - * Controllable doubles interface. + * @psalm-immutable * - * @author Konstantin Kudryashov + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -interface ProphecySubjectInterface +final class HRTime { + private readonly int $seconds; + private readonly int $nanoseconds; /** - * Sets subject prophecy. - * - * @param ProphecyInterface $prophecy - * - * @return void + * @throws InvalidArgumentException */ - public function setProphecy(\Prophecy\Prophecy\ProphecyInterface $prophecy); + public static function fromSecondsAndNanoseconds(int $seconds, int $nanoseconds): self + { + return new self($seconds, $nanoseconds); + } /** - * Returns subject prophecy. - * - * @return ProphecyInterface + * @throws InvalidArgumentException + */ + private function __construct(int $seconds, int $nanoseconds) + { + $this->ensureNotNegative($seconds, 'seconds'); + $this->ensureNotNegative($nanoseconds, 'nanoseconds'); + $this->ensureNanoSecondsInRange($nanoseconds); + $this->seconds = $seconds; + $this->nanoseconds = $nanoseconds; + } + public function seconds(): int + { + return $this->seconds; + } + public function nanoseconds(): int + { + return $this->nanoseconds; + } + public function duration(self $start): \PHPUnit\Event\Telemetry\Duration + { + $seconds = $this->seconds - $start->seconds(); + $nanoseconds = $this->nanoseconds - $start->nanoseconds(); + if ($nanoseconds < 0) { + $seconds--; + $nanoseconds += 1000000000; + } + if ($seconds < 0) { + return \PHPUnit\Event\Telemetry\Duration::fromSecondsAndNanoseconds(0, 0); + } + return \PHPUnit\Event\Telemetry\Duration::fromSecondsAndNanoseconds($seconds, $nanoseconds); + } + /** + * @throws InvalidArgumentException + */ + private function ensureNotNegative(int $value, string $type): void + { + if ($value < 0) { + throw new InvalidArgumentException(sprintf('Value for %s must not be negative.', $type)); + } + } + /** + * @throws InvalidArgumentException */ - public function getProphecy(); + private function ensureNanoSecondsInRange(int $nanoseconds): void + { + if ($nanoseconds > 999999999) { + throw new InvalidArgumentException('Value for nanoseconds must not be greater than 999999999.'); + } + } } - * Marcello Duarte + * This file is part of PHPUnit. + * + * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace Prophecy\Prophecy; +namespace PHPUnit\Event\Telemetry; +use function sprintf; /** - * Basic prophecies revealer. + * @psalm-immutable * - * @author Konstantin Kudryashov + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -class Revealer implements \Prophecy\Prophecy\RevealerInterface +final class Info { - /** - * Unwraps value(s). - * - * @param mixed $value - * - * @return mixed - */ - public function reveal($value) + private readonly \PHPUnit\Event\Telemetry\Snapshot $current; + private readonly \PHPUnit\Event\Telemetry\Duration $durationSinceStart; + private readonly \PHPUnit\Event\Telemetry\MemoryUsage $memorySinceStart; + private readonly \PHPUnit\Event\Telemetry\Duration $durationSincePrevious; + private readonly \PHPUnit\Event\Telemetry\MemoryUsage $memorySincePrevious; + public function __construct(\PHPUnit\Event\Telemetry\Snapshot $current, \PHPUnit\Event\Telemetry\Duration $durationSinceStart, \PHPUnit\Event\Telemetry\MemoryUsage $memorySinceStart, \PHPUnit\Event\Telemetry\Duration $durationSincePrevious, \PHPUnit\Event\Telemetry\MemoryUsage $memorySincePrevious) { - if (\is_array($value)) { - return \array_map(array($this, __FUNCTION__), $value); - } - if (!\is_object($value)) { - return $value; - } - if ($value instanceof \Prophecy\Prophecy\ProphecyInterface) { - $value = $value->reveal(); - } - return $value; + $this->current = $current; + $this->durationSinceStart = $durationSinceStart; + $this->memorySinceStart = $memorySinceStart; + $this->durationSincePrevious = $durationSincePrevious; + $this->memorySincePrevious = $memorySincePrevious; + } + public function time(): \PHPUnit\Event\Telemetry\HRTime + { + return $this->current->time(); + } + public function memoryUsage(): \PHPUnit\Event\Telemetry\MemoryUsage + { + return $this->current->memoryUsage(); + } + public function peakMemoryUsage(): \PHPUnit\Event\Telemetry\MemoryUsage + { + return $this->current->peakMemoryUsage(); + } + public function durationSinceStart(): \PHPUnit\Event\Telemetry\Duration + { + return $this->durationSinceStart; + } + public function memoryUsageSinceStart(): \PHPUnit\Event\Telemetry\MemoryUsage + { + return $this->memorySinceStart; + } + public function durationSincePrevious(): \PHPUnit\Event\Telemetry\Duration + { + return $this->durationSincePrevious; + } + public function memoryUsageSincePrevious(): \PHPUnit\Event\Telemetry\MemoryUsage + { + return $this->memorySincePrevious; + } + public function garbageCollectorStatus(): \PHPUnit\Event\Telemetry\GarbageCollectorStatus + { + return $this->current->garbageCollectorStatus(); + } + public function asString(): string + { + return sprintf('[%s / %s] [%d bytes]', $this->durationSinceStart()->asString(), $this->durationSincePrevious()->asString(), $this->memoryUsage()->bytes()); } } - * Marcello Duarte + * This file is part of PHPUnit. + * + * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace Prophecy\Prophecy; +namespace PHPUnit\Event\Telemetry; /** - * Prophecies revealer interface. - * - * @author Konstantin Kudryashov + * @internal This interface is not covered by the backward compatibility promise for PHPUnit */ -interface RevealerInterface +interface MemoryMeter { - /** - * Unwraps value(s). - * - * @param mixed $value - * - * @return mixed - */ - public function reveal($value); + public function memoryUsage(): \PHPUnit\Event\Telemetry\MemoryUsage; + public function peakMemoryUsage(): \PHPUnit\Event\Telemetry\MemoryUsage; } - * Marcello Duarte + * This file is part of PHPUnit. + * + * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace Prophecy; +namespace PHPUnit\Event\Telemetry; -use Prophecy\Doubler\CachedDoubler; -use Prophecy\Doubler\Doubler; -use Prophecy\Doubler\LazyDouble; -use Prophecy\Doubler\ClassPatch; -use Prophecy\Exception\Doubler\ClassNotFoundException; -use Prophecy\Prophecy\ObjectProphecy; -use Prophecy\Prophecy\RevealerInterface; -use Prophecy\Prophecy\Revealer; -use Prophecy\Call\CallCenter; -use Prophecy\Util\StringUtil; -use Prophecy\Exception\Prediction\PredictionException; -use Prophecy\Exception\Prediction\AggregateException; /** - * Prophet creates prophecies. + * @psalm-immutable * - * @author Konstantin Kudryashov + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -class Prophet +final class MemoryUsage { - /** - * @var Doubler - */ - private $doubler; - private $revealer; - private $util; - /** - * @var list> - */ - private $prophecies = array(); - public function __construct(Doubler $doubler = null, RevealerInterface $revealer = null, StringUtil $util = null) + private readonly int $bytes; + public static function fromBytes(int $bytes): self { - if (null === $doubler) { - $doubler = new CachedDoubler(); - $doubler->registerClassPatch(new ClassPatch\SplFileInfoPatch()); - $doubler->registerClassPatch(new ClassPatch\TraversablePatch()); - $doubler->registerClassPatch(new ClassPatch\ThrowablePatch()); - $doubler->registerClassPatch(new ClassPatch\DisableConstructorPatch()); - $doubler->registerClassPatch(new ClassPatch\ProphecySubjectPatch()); - $doubler->registerClassPatch(new ClassPatch\ReflectionClassNewInstancePatch()); - $doubler->registerClassPatch(new ClassPatch\MagicCallPatch()); - $doubler->registerClassPatch(new ClassPatch\KeywordPatch()); - } - $this->doubler = $doubler; - $this->revealer = $revealer ?: new Revealer(); - $this->util = $util ?: new StringUtil(); + return new self($bytes); } - /** - * Creates new object prophecy. - * - * @param null|string $classOrInterface Class or interface name - * - * @return ObjectProphecy - * - * @template T of object - * @phpstan-param class-string|null $classOrInterface - * @phpstan-return ($classOrInterface is null ? ObjectProphecy : ObjectProphecy) - */ - public function prophesize($classOrInterface = null) + private function __construct(int $bytes) { - $this->prophecies[] = $prophecy = new ObjectProphecy(new LazyDouble($this->doubler), new CallCenter($this->util), $this->revealer); - if ($classOrInterface) { - if (\class_exists($classOrInterface)) { - return $prophecy->willExtend($classOrInterface); - } - if (\interface_exists($classOrInterface)) { - return $prophecy->willImplement($classOrInterface); - } - throw new ClassNotFoundException(\sprintf('Cannot prophesize class %s, because it cannot be found.', $classOrInterface), $classOrInterface); - } - return $prophecy; + $this->bytes = $bytes; } - /** - * Returns all created object prophecies. - * - * @return list> - */ - public function getProphecies() + public function bytes(): int { - return $this->prophecies; + return $this->bytes; } - /** - * Returns Doubler instance assigned to this Prophet. - * - * @return Doubler - */ - public function getDoubler() + public function diff(self $other): self { - return $this->doubler; - } - /** - * Checks all predictions defined by prophecies of this Prophet. - * - * @return void - * - * @throws Exception\Prediction\AggregateException If any prediction fails - */ - public function checkPredictions() - { - $exception = new AggregateException("Some predictions failed:\n"); - foreach ($this->prophecies as $prophecy) { - try { - $prophecy->checkProphecyMethodsPredictions(); - } catch (PredictionException $e) { - $exception->append($e); - } - } - if (\count($exception->getExceptions())) { - throw $exception; - } + return self::fromBytes($this->bytes - $other->bytes); } } - * Marcello Duarte + * This file is part of PHPUnit. + * + * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ +namespace PHPUnit\Event\Telemetry; + +use function gc_status; /** - * This class is a modification from sebastianbergmann/exporter - * @see https://github.com/sebastianbergmann/exporter + * @internal This class is not covered by the backward compatibility promise for PHPUnit + * + * @codeCoverageIgnore */ -class ExportUtil +final class Php81GarbageCollectorStatusProvider implements \PHPUnit\Event\Telemetry\GarbageCollectorStatusProvider { - /** - * Exports a value as a string - * - * The output of this method is similar to the output of print_r(), but - * improved in various aspects: - * - * - NULL is rendered as "null" (instead of "") - * - TRUE is rendered as "true" (instead of "1") - * - FALSE is rendered as "false" (instead of "") - * - Strings are always quoted with single quotes - * - Carriage returns and newlines are normalized to \n - * - Recursion and repeated rendering is treated properly - * - * @param mixed $value - * @param int $indentation The indentation level of the 2nd+ line - * @return string - */ - public static function export($value, $indentation = 0) - { - return self::recursiveExport($value, $indentation); - } - /** - * Converts an object to an array containing all of its private, protected - * and public properties. - * - * @param mixed $value - * @return array - */ - public static function toArray($value) - { - if (!\is_object($value)) { - return (array) $value; - } - $array = array(); - foreach ((array) $value as $key => $val) { - // properties are transformed to keys in the following way: - // private $property => "\0Classname\0property" - // protected $property => "\0*\0property" - // public $property => "property" - if (\preg_match('/^\\0.+\\0(.+)$/', $key, $matches)) { - $key = $matches[1]; - } - // See https://github.com/php/php-src/commit/5721132 - if ($key === "\x00gcdata") { - 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) { - // Use the same identifier that would be printed alongside the object's representation elsewhere. - $array[\spl_object_id($val)] = array('obj' => $val, 'inf' => $value->getInfo()); - } - } - return $array; - } - /** - * Recursive implementation of export - * - * @param mixed $value The value to export - * @param int $indentation The indentation level of the 2nd+ line - * @param \SebastianBergmann\RecursionContext\Context $processed Previously processed objects - * @return string - * @see SebastianBergmann\Exporter\Exporter::export - */ - protected static function recursiveExport(&$value, $indentation, $processed = null) + public function status(): \PHPUnit\Event\Telemetry\GarbageCollectorStatus { - if ($value === null) { - return 'null'; - } - if ($value === \true) { - return 'true'; - } - if ($value === \false) { - return 'false'; - } - if (\is_float($value) && \floatval(\intval($value)) === $value) { - return "{$value}.0"; - } - if (\is_resource($value)) { - return \sprintf('resource(%d) of type (%s)', (int) $value, \get_resource_type($value)); - } - if (\is_string($value)) { - // Match for most non printable chars somewhat taking multibyte chars into account - if (\preg_match('/[^\\x09-\\x0d\\x20-\\xff]/', $value)) { - return 'Binary String: 0x' . \bin2hex($value); - } - return "'" . \str_replace(array("\r\n", "\n\r", "\r"), array("\n", "\n", "\n"), $value) . "'"; - } - $whitespace = \str_repeat(' ', 4 * $indentation); - if (!$processed) { - $processed = new Context(); - } - if (\is_array($value)) { - if (($key = $processed->contains($value)) !== \false) { - return 'Array &' . $key; - } - \assert(\is_array($value)); - $array = $value; - $key = $processed->add($value); - $values = ''; - if (\count($array) > 0) { - foreach ($array as $k => $v) { - $values .= \sprintf('%s %s => %s' . "\n", $whitespace, self::recursiveExport($k, $indentation), self::recursiveExport($value[$k], $indentation + 1, $processed)); - } - $values = "\n" . $values . $whitespace; - } - return \sprintf('Array &%s (%s)', $key, $values); - } - if (\is_object($value)) { - $class = \get_class($value); - if ($processed->contains($value)) { - \assert(\is_object($value)); - return \sprintf('%s#%d Object', $class, \spl_object_id($value)); - } - $processed->add($value); - \assert(\is_object($value)); - $values = ''; - $array = self::toArray($value); - if (\count($array) > 0) { - foreach ($array as $k => $v) { - $values .= \sprintf('%s %s => %s' . "\n", $whitespace, self::recursiveExport($k, $indentation), self::recursiveExport($v, $indentation + 1, $processed)); - } - $values = "\n" . $values . $whitespace; - } - return \sprintf('%s#%d Object (%s)', $class, \spl_object_id($value), $values); - } - return \var_export($value, \true); + $status = gc_status(); + return new \PHPUnit\Event\Telemetry\GarbageCollectorStatus($status['runs'], $status['collected'], $status['threshold'], $status['roots'], null, null, null, null, null, null, null, null); } } - * Marcello Duarte + * This file is part of PHPUnit. + * + * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace Prophecy\Util; +namespace PHPUnit\Event\Telemetry; -use Prophecy\Call\Call; +use function gc_status; /** - * String utility. - * - * @author Konstantin Kudryashov + * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -class StringUtil +final class Php83GarbageCollectorStatusProvider implements \PHPUnit\Event\Telemetry\GarbageCollectorStatusProvider { - private $verbose; - /** - * @param bool $verbose - */ - public function __construct($verbose = \true) - { - $this->verbose = $verbose; - } - /** - * Stringifies any provided value. - * - * @param mixed $value - * @param boolean $exportObject - * - * @return string - */ - public function stringify($value, $exportObject = \true) - { - if (\is_array($value)) { - if (\range(0, \count($value) - 1) === \array_keys($value)) { - return '[' . \implode(', ', \array_map(array($this, __FUNCTION__), $value)) . ']'; - } - $stringify = array($this, __FUNCTION__); - return '[' . \implode(', ', \array_map(function ($item, $key) use($stringify) { - return (\is_integer($key) ? $key : '"' . $key . '"') . ' => ' . \call_user_func($stringify, $item); - }, $value, \array_keys($value))) . ']'; - } - if (\is_resource($value)) { - return \get_resource_type($value) . ':' . $value; - } - if (\is_object($value)) { - return $exportObject ? \Prophecy\Util\ExportUtil::export($value) : \sprintf('%s#%s', \get_class($value), \spl_object_id($value)); - } - if (\is_bool($value)) { - return $value ? 'true' : 'false'; - } - if (\is_string($value)) { - $str = \sprintf('"%s"', \str_replace("\n", '\\n', $value)); - if (!$this->verbose && 50 <= \strlen($str)) { - return \substr($str, 0, 50) . '"...'; - } - return $str; - } - if (null === $value) { - return 'null'; - } - \assert(\is_int($value) || \is_float($value)); - return (string) $value; - } - /** - * Stringifies provided array of calls. - * - * @param Call[] $calls Array of Call instances - * - * @return string - */ - public function stringifyCalls(array $calls) + public function status(): \PHPUnit\Event\Telemetry\GarbageCollectorStatus { - $self = $this; - return \implode(\PHP_EOL, \array_map(function (Call $call) use($self) { - return \sprintf(' - %s(%s) @ %s', $call->getMethodName(), \implode(', ', \array_map(array($self, 'stringify'), $call->getArguments())), \str_replace(\GETCWD() . \DIRECTORY_SEPARATOR, '', $call->getCallPlace())); - }, $calls)); + $status = gc_status(); + return new \PHPUnit\Event\Telemetry\GarbageCollectorStatus($status['runs'], $status['collected'], $status['threshold'], $status['roots'], $status['application_time'], $status['collector_time'], $status['destructor_time'], $status['free_time'], $status['running'], $status['protected'], $status['full'], $status['buffer_size']); } } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\Telemetry; /** - * Inspired by https://github.com/nikic/PHP-Parser/tree/36a6dcd04e7b0285e8f0868f44bd4927802f7df1 + * @psalm-immutable * - * Copyright (c) 2011, Nikita Popov - * All rights reserved. + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -abstract class AbstractNodeVisitor implements NodeVisitor +final class Snapshot { - public function beforeTraverse(array $nodes) : ?array + private readonly \PHPUnit\Event\Telemetry\HRTime $time; + private readonly \PHPUnit\Event\Telemetry\MemoryUsage $memoryUsage; + private readonly \PHPUnit\Event\Telemetry\MemoryUsage $peakMemoryUsage; + private readonly \PHPUnit\Event\Telemetry\GarbageCollectorStatus $garbageCollectorStatus; + public function __construct(\PHPUnit\Event\Telemetry\HRTime $time, \PHPUnit\Event\Telemetry\MemoryUsage $memoryUsage, \PHPUnit\Event\Telemetry\MemoryUsage $peakMemoryUsage, \PHPUnit\Event\Telemetry\GarbageCollectorStatus $garbageCollectorStatus) { - return null; + $this->time = $time; + $this->memoryUsage = $memoryUsage; + $this->peakMemoryUsage = $peakMemoryUsage; + $this->garbageCollectorStatus = $garbageCollectorStatus; } - public function enterNode(Node $node) + public function time(): \PHPUnit\Event\Telemetry\HRTime { - return null; + return $this->time; } - public function leaveNode(Node $node) + public function memoryUsage(): \PHPUnit\Event\Telemetry\MemoryUsage { - return null; + return $this->memoryUsage; } - public function afterTraverse(array $nodes) : ?array + public function peakMemoryUsage(): \PHPUnit\Event\Telemetry\MemoryUsage { - return null; + return $this->peakMemoryUsage; + } + public function garbageCollectorStatus(): \PHPUnit\Event\Telemetry\GarbageCollectorStatus + { + return $this->garbageCollectorStatus; } } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\Telemetry; -final class Attribute +/** + * @internal This interface is not covered by the backward compatibility promise for PHPUnit + */ +interface StopWatch { - public const START_LINE = 'startLine'; - public const END_LINE = 'endLine'; - public const START_INDEX = 'startIndex'; - public const END_INDEX = 'endIndex'; - public const ORIGINAL_NODE = 'originalNode'; + public function current(): \PHPUnit\Event\Telemetry\HRTime; } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\Telemetry; -use PHPUnitPHAR\PHPStan\PhpDocParser\Ast\NodeAttributes; -use function sprintf; -class ConstExprArrayItemNode implements ConstExprNode +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class System { - use NodeAttributes; - /** @var ConstExprNode|null */ - public $key; - /** @var ConstExprNode */ - public $value; - public function __construct(?ConstExprNode $key, ConstExprNode $value) + private readonly \PHPUnit\Event\Telemetry\StopWatch $stopWatch; + private readonly \PHPUnit\Event\Telemetry\MemoryMeter $memoryMeter; + private readonly \PHPUnit\Event\Telemetry\GarbageCollectorStatusProvider $garbageCollectorStatusProvider; + public function __construct(\PHPUnit\Event\Telemetry\StopWatch $stopWatch, \PHPUnit\Event\Telemetry\MemoryMeter $memoryMeter, \PHPUnit\Event\Telemetry\GarbageCollectorStatusProvider $garbageCollectorStatusProvider) { - $this->key = $key; - $this->value = $value; + $this->stopWatch = $stopWatch; + $this->memoryMeter = $memoryMeter; + $this->garbageCollectorStatusProvider = $garbageCollectorStatusProvider; } - public function __toString() : string + public function snapshot(): \PHPUnit\Event\Telemetry\Snapshot { - if ($this->key !== null) { - return sprintf('%s => %s', $this->key, $this->value); - } - return (string) $this->value; + return new \PHPUnit\Event\Telemetry\Snapshot($this->stopWatch->current(), $this->memoryMeter->memoryUsage(), $this->memoryMeter->peakMemoryUsage(), $this->garbageCollectorStatusProvider->status()); } } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\Telemetry; -use PHPUnitPHAR\PHPStan\PhpDocParser\Ast\NodeAttributes; -use function implode; -class ConstExprArrayNode implements ConstExprNode +use function memory_get_peak_usage; +use function memory_get_usage; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class SystemMemoryMeter implements \PHPUnit\Event\Telemetry\MemoryMeter { - use NodeAttributes; - /** @var ConstExprArrayItemNode[] */ - public $items; - /** - * @param ConstExprArrayItemNode[] $items - */ - public function __construct(array $items) + public function memoryUsage(): \PHPUnit\Event\Telemetry\MemoryUsage { - $this->items = $items; + return \PHPUnit\Event\Telemetry\MemoryUsage::fromBytes(memory_get_usage(\true)); } - public function __toString() : string + public function peakMemoryUsage(): \PHPUnit\Event\Telemetry\MemoryUsage { - return '[' . implode(', ', $this->items) . ']'; + return \PHPUnit\Event\Telemetry\MemoryUsage::fromBytes(memory_get_peak_usage(\true)); } } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\Telemetry; -use PHPUnitPHAR\PHPStan\PhpDocParser\Ast\NodeAttributes; -class ConstExprFalseNode implements ConstExprNode +use function hrtime; +use PHPUnit\Event\InvalidArgumentException; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class SystemStopWatch implements \PHPUnit\Event\Telemetry\StopWatch { - use NodeAttributes; - public function __toString() : string + /** + * @throws InvalidArgumentException + */ + public function current(): \PHPUnit\Event\Telemetry\HRTime { - return 'false'; + return \PHPUnit\Event\Telemetry\HRTime::fromSecondsAndNanoseconds(...hrtime()); } } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\Telemetry; -use PHPUnitPHAR\PHPStan\PhpDocParser\Ast\NodeAttributes; -class ConstExprFloatNode implements ConstExprNode +use function hrtime; +use PHPUnit\Event\InvalidArgumentException; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + * + * @codeCoverageIgnore + */ +final class SystemStopWatchWithOffset implements \PHPUnit\Event\Telemetry\StopWatch { - use NodeAttributes; - /** @var string */ - public $value; - public function __construct(string $value) + private ?\PHPUnit\Event\Telemetry\HRTime $offset; + public function __construct(\PHPUnit\Event\Telemetry\HRTime $offset) { - $this->value = $value; + $this->offset = $offset; } - public function __toString() : string + /** + * @throws InvalidArgumentException + */ + public function current(): \PHPUnit\Event\Telemetry\HRTime { - return $this->value; + if ($this->offset !== null) { + $offset = $this->offset; + $this->offset = null; + return $offset; + } + return \PHPUnit\Event\Telemetry\HRTime::fromSecondsAndNanoseconds(...hrtime()); } } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\Code; -use PHPUnitPHAR\PHPStan\PhpDocParser\Ast\NodeAttributes; -class ConstExprIntegerNode implements ConstExprNode +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class Phpt extends \PHPUnit\Event\Code\Test { - use NodeAttributes; - /** @var string */ - public $value; - public function __construct(string $value) + /** + * @psalm-assert-if-true Phpt $this + */ + public function isPhpt(): bool { - $this->value = $value; + return \true; } - public function __toString() : string + /** + * @psalm-return non-empty-string + */ + public function id(): string { - return $this->value; + return $this->file(); } -} -file(); } } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\Code; -use PHPUnitPHAR\PHPStan\PhpDocParser\Ast\NodeAttributes; -class ConstExprStringNode implements ConstExprNode +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +abstract class Test { - use NodeAttributes; - /** @var string */ - public $value; - public function __construct(string $value) + /** + * @psalm-var non-empty-string + */ + private readonly string $file; + /** + * @psalm-param non-empty-string $file + */ + public function __construct(string $file) { - $this->value = $value; + $this->file = $file; } - public function __toString() : string + /** + * @psalm-return non-empty-string + */ + public function file(): string { - return $this->value; + return $this->file; } -} - + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\Code; -use PHPUnitPHAR\PHPStan\PhpDocParser\Ast\NodeAttributes; -class ConstFetchNode implements ConstExprNode +use function count; +use Countable; +use IteratorAggregate; +/** + * @template-implements IteratorAggregate + * + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class TestCollection implements Countable, IteratorAggregate { - use NodeAttributes; - /** @var string class name for class constants or empty string for non-class constants */ - public $className; - /** @var string */ - public $name; - public function __construct(string $className, string $name) + /** + * @psalm-var list + */ + private readonly array $tests; + /** + * @psalm-param list $tests + */ + public static function fromArray(array $tests): self { - $this->className = $className; - $this->name = $name; + return new self(...$tests); } - public function __toString() : string + private function __construct(\PHPUnit\Event\Code\Test ...$tests) { - if ($this->className === '') { - return $this->name; - } - return "{$this->className}::{$this->name}"; + $this->tests = $tests; + } + /** + * @psalm-return list + */ + public function asArray(): array + { + return $this->tests; + } + public function count(): int + { + return count($this->tests); + } + public function getIterator(): \PHPUnit\Event\Code\TestCollectionIterator + { + return new \PHPUnit\Event\Code\TestCollectionIterator($this); } } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\Code; -use PHPUnitPHAR\PHPStan\PhpDocParser\Ast\NodeAttributes; -use function sprintf; -use function str_replace; -use function strlen; -use function substr; -class DoctrineConstExprStringNode extends ConstExprStringNode +use function count; +use Iterator; +/** + * @template-implements Iterator + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class TestCollectionIterator implements Iterator { - use NodeAttributes; - /** @var string */ - public $value; - public function __construct(string $value) + /** + * @psalm-var list + */ + private readonly array $tests; + private int $position = 0; + public function __construct(\PHPUnit\Event\Code\TestCollection $tests) { - parent::__construct($value); - $this->value = $value; + $this->tests = $tests->asArray(); + } + public function rewind(): void + { + $this->position = 0; + } + public function valid(): bool + { + return $this->position < count($this->tests); } - public function __toString() : string + public function key(): int { - return self::escape($this->value); + return $this->position; } - public static function unescape(string $value) : string + public function current(): \PHPUnit\Event\Code\Test { - // from https://github.com/doctrine/annotations/blob/a9ec7af212302a75d1f92fa65d3abfbd16245a2a/lib/Doctrine/Common/Annotations/DocLexer.php#L103-L107 - return str_replace('""', '"', substr($value, 1, strlen($value) - 2)); + return $this->tests[$this->position]; } - private static function escape(string $value) : string + public function next(): void { - // from https://github.com/phpstan/phpdoc-parser/issues/205#issuecomment-1662323656 - return sprintf('"%s"', str_replace('"', '""', $value)); + $this->position++; } } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\TestData; -use PHPUnitPHAR\PHPStan\PhpDocParser\Ast\NodeAttributes; -use function addcslashes; -use function assert; -use function dechex; -use function ord; -use function preg_replace_callback; -use function sprintf; -use function str_pad; -use function strlen; -use const STR_PAD_LEFT; -class QuoteAwareConstExprStringNode extends ConstExprStringNode implements ConstExprNode +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class DataFromDataProvider extends \PHPUnit\Event\TestData\TestData { - public const SINGLE_QUOTED = 1; - public const DOUBLE_QUOTED = 2; - use NodeAttributes; - /** @var self::SINGLE_QUOTED|self::DOUBLE_QUOTED */ - public $quoteType; - /** - * @param self::SINGLE_QUOTED|self::DOUBLE_QUOTED $quoteType - */ - public function __construct(string $value, int $quoteType) + private readonly int|string $dataSetName; + private readonly string $dataAsStringForResultOutput; + public static function from(int|string $dataSetName, string $data, string $dataAsStringForResultOutput): self { - parent::__construct($value); - $this->quoteType = $quoteType; + return new self($dataSetName, $data, $dataAsStringForResultOutput); } - public function __toString() : string + protected function __construct(int|string $dataSetName, string $data, string $dataAsStringForResultOutput) { - if ($this->quoteType === self::SINGLE_QUOTED) { - // from https://github.com/nikic/PHP-Parser/blob/0ffddce52d816f72d0efc4d9b02e276d3309ef01/lib/PhpParser/PrettyPrinter/Standard.php#L1007 - return sprintf("'%s'", addcslashes($this->value, '\'\\')); - } - // from https://github.com/nikic/PHP-Parser/blob/0ffddce52d816f72d0efc4d9b02e276d3309ef01/lib/PhpParser/PrettyPrinter/Standard.php#L1010-L1040 - return sprintf('"%s"', $this->escapeDoubleQuotedString()); + $this->dataSetName = $dataSetName; + $this->dataAsStringForResultOutput = $dataAsStringForResultOutput; + parent::__construct($data); } - private function escapeDoubleQuotedString() : string + public function dataSetName(): int|string { - $quote = '"'; - $escaped = addcslashes($this->value, "\n\r\t\f\v\$" . $quote . '\\'); - // Escape control characters and non-UTF-8 characters. - // Regex based on https://stackoverflow.com/a/11709412/385378. - $regex = '/( - [\\x00-\\x08\\x0E-\\x1F] # Control characters - | [\\xC0-\\xC1] # Invalid UTF-8 Bytes - | [\\xF5-\\xFF] # Invalid UTF-8 Bytes - | \\xE0(?=[\\x80-\\x9F]) # Overlong encoding of prior code point - | \\xF0(?=[\\x80-\\x8F]) # Overlong encoding of prior code point - | [\\xC2-\\xDF](?![\\x80-\\xBF]) # Invalid UTF-8 Sequence Start - | [\\xE0-\\xEF](?![\\x80-\\xBF]{2}) # Invalid UTF-8 Sequence Start - | [\\xF0-\\xF4](?![\\x80-\\xBF]{3}) # Invalid UTF-8 Sequence Start - | (?<=[\\x00-\\x7F\\xF5-\\xFF])[\\x80-\\xBF] # Invalid UTF-8 Sequence Middle - | (?dataSetName; + } + /** + * @internal This method is not covered by the backward compatibility promise for PHPUnit + */ + public function dataAsStringForResultOutput(): string + { + return $this->dataAsStringForResultOutput; + } + /** + * @psalm-assert-if-true DataFromDataProvider $this + */ + public function isFromDataProvider(): bool + { + return \true; } } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\TestData; -interface Node +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class DataFromTestDependency extends \PHPUnit\Event\TestData\TestData { - public function __toString() : string; - /** - * @param mixed $value - */ - public function setAttribute(string $key, $value) : void; - public function hasAttribute(string $key) : bool; + public static function from(string $data): self + { + return new self($data); + } /** - * @return mixed + * @psalm-assert-if-true DataFromTestDependency $this */ - public function getAttribute(string $key); + public function isFromTestDependency(): bool + { + return \true; + } } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\TestData; -use function array_key_exists; -trait NodeAttributes +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +abstract class TestData { - /** @var array */ - private $attributes = []; - /** - * @param mixed $value - */ - public function setAttribute(string $key, $value) : void + private readonly string $data; + protected function __construct(string $data) { - $this->attributes[$key] = $value; + $this->data = $data; } - public function hasAttribute(string $key) : bool + public function data(): string { - return array_key_exists($key, $this->attributes); + return $this->data; } /** - * @return mixed + * @psalm-assert-if-true DataFromDataProvider $this */ - public function getAttribute(string $key) + public function isFromDataProvider(): bool { - if ($this->hasAttribute($key)) { - return $this->attributes[$key]; - } - return null; + return \false; + } + /** + * @psalm-assert-if-true DataFromTestDependency $this + */ + public function isFromTestDependency(): bool + { + return \false; } } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\TestData; -use LogicException; -use PHPUnitPHAR\PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprNode; -use PHPUnitPHAR\PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocChildNode; -use PHPUnitPHAR\PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagValueNode; -use PHPUnitPHAR\PHPStan\PhpDocParser\Ast\Type\TypeNode; -use function array_keys; -use function array_pop; -use function array_splice; use function count; -use function get_class; -use function get_object_vars; -use function gettype; -use function is_array; -use function sprintf; +use Countable; +use IteratorAggregate; /** - * Inspired by https://github.com/nikic/PHP-Parser/tree/36a6dcd04e7b0285e8f0868f44bd4927802f7df1 + * @template-implements IteratorAggregate * - * Copyright (c) 2011, Nikita Popov - * All rights reserved. + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -final class NodeTraverser +final class TestDataCollection implements Countable, IteratorAggregate { /** - * If NodeVisitor::enterNode() returns DONT_TRAVERSE_CHILDREN, child nodes - * of the current node will not be traversed for any visitors. - * - * For subsequent visitors enterNode() will still be called on the current - * node and leaveNode() will also be invoked for the current node. + * @psalm-var list */ - public const DONT_TRAVERSE_CHILDREN = 1; + private readonly array $data; + private ?\PHPUnit\Event\TestData\DataFromDataProvider $fromDataProvider = null; /** - * If NodeVisitor::enterNode() or NodeVisitor::leaveNode() returns - * STOP_TRAVERSAL, traversal is aborted. + * @psalm-param list $data * - * The afterTraverse() method will still be invoked. + * @throws MoreThanOneDataSetFromDataProviderException */ - public const STOP_TRAVERSAL = 2; + public static function fromArray(array $data): self + { + return new self(...$data); + } /** - * If NodeVisitor::leaveNode() returns REMOVE_NODE for a node that occurs - * in an array, it will be removed from the array. - * - * For subsequent visitors leaveNode() will still be invoked for the - * removed node. + * @throws MoreThanOneDataSetFromDataProviderException */ - public const REMOVE_NODE = 3; - /** - * If NodeVisitor::enterNode() returns DONT_TRAVERSE_CURRENT_AND_CHILDREN, child nodes - * of the current node will not be traversed for any visitors. - * - * For subsequent visitors enterNode() will not be called as well. - * leaveNode() will be invoked for visitors that has enterNode() method invoked. - */ - public const DONT_TRAVERSE_CURRENT_AND_CHILDREN = 4; - /** @var list Visitors */ - private $visitors = []; - /** @var bool Whether traversal should be stopped */ - private $stopTraversal; + private function __construct(\PHPUnit\Event\TestData\TestData ...$data) + { + $this->ensureNoMoreThanOneDataFromDataProvider($data); + $this->data = $data; + } /** - * @param list $visitors + * @psalm-return list */ - public function __construct(array $visitors) + public function asArray(): array { - $this->visitors = $visitors; + return $this->data; + } + public function count(): int + { + return count($this->data); } /** - * Traverses an array of nodes using the registered visitors. - * - * @param Node[] $nodes Array of nodes - * - * @return Node[] Traversed array of nodes + * @psalm-assert-if-true !null $this->fromDataProvider */ - public function traverse(array $nodes) : array + public function hasDataFromDataProvider(): bool { - $this->stopTraversal = \false; - foreach ($this->visitors as $visitor) { - $return = $visitor->beforeTraverse($nodes); - if ($return === null) { - continue; - } - $nodes = $return; - } - $nodes = $this->traverseArray($nodes); - foreach ($this->visitors as $visitor) { - $return = $visitor->afterTraverse($nodes); - if ($return === null) { - continue; - } - $nodes = $return; - } - return $nodes; + return $this->fromDataProvider !== null; } /** - * Recursively traverse a node. - * - * @param Node $node Node to traverse. - * - * @return Node Result of traversal (may be original node or new one) + * @throws NoDataSetFromDataProviderException */ - private function traverseNode(Node $node) : Node + public function dataFromDataProvider(): \PHPUnit\Event\TestData\DataFromDataProvider { - $subNodeNames = array_keys(get_object_vars($node)); - foreach ($subNodeNames as $name) { - $subNode =& $node->{$name}; - if (is_array($subNode)) { - $subNode = $this->traverseArray($subNode); - if ($this->stopTraversal) { - break; - } - } elseif ($subNode instanceof Node) { - $traverseChildren = \true; - $breakVisitorIndex = null; - foreach ($this->visitors as $visitorIndex => $visitor) { - $return = $visitor->enterNode($subNode); - if ($return === null) { - continue; - } - if ($return instanceof Node) { - $this->ensureReplacementReasonable($subNode, $return); - $subNode = $return; - } elseif ($return === self::DONT_TRAVERSE_CHILDREN) { - $traverseChildren = \false; - } elseif ($return === self::DONT_TRAVERSE_CURRENT_AND_CHILDREN) { - $traverseChildren = \false; - $breakVisitorIndex = $visitorIndex; - break; - } elseif ($return === self::STOP_TRAVERSAL) { - $this->stopTraversal = \true; - break 2; - } else { - throw new LogicException('enterNode() returned invalid value of type ' . gettype($return)); - } - } - if ($traverseChildren) { - $subNode = $this->traverseNode($subNode); - if ($this->stopTraversal) { - break; - } - } - foreach ($this->visitors as $visitorIndex => $visitor) { - $return = $visitor->leaveNode($subNode); - if ($return !== null) { - if ($return instanceof Node) { - $this->ensureReplacementReasonable($subNode, $return); - $subNode = $return; - } elseif ($return === self::STOP_TRAVERSAL) { - $this->stopTraversal = \true; - break 2; - } elseif (is_array($return)) { - throw new LogicException('leaveNode() may only return an array ' . 'if the parent structure is an array'); - } else { - throw new LogicException('leaveNode() returned invalid value of type ' . gettype($return)); - } - } - if ($breakVisitorIndex === $visitorIndex) { - break; - } - } - } + if (!$this->hasDataFromDataProvider()) { + throw new \PHPUnit\Event\TestData\NoDataSetFromDataProviderException(); } - return $node; + return $this->fromDataProvider; + } + public function getIterator(): \PHPUnit\Event\TestData\TestDataCollectionIterator + { + return new \PHPUnit\Event\TestData\TestDataCollectionIterator($this); } /** - * Recursively traverse array (usually of nodes). + * @psalm-param list $data * - * @param mixed[] $nodes Array to traverse - * - * @return mixed[] Result of traversal (may be original array or changed one) + * @throws MoreThanOneDataSetFromDataProviderException */ - private function traverseArray(array $nodes) : array + private function ensureNoMoreThanOneDataFromDataProvider(array $data): void { - $doNodes = []; - foreach ($nodes as $i => &$node) { - if ($node instanceof Node) { - $traverseChildren = \true; - $breakVisitorIndex = null; - foreach ($this->visitors as $visitorIndex => $visitor) { - $return = $visitor->enterNode($node); - if ($return === null) { - continue; - } - if ($return instanceof Node) { - $this->ensureReplacementReasonable($node, $return); - $node = $return; - } elseif (is_array($return)) { - $doNodes[] = [$i, $return]; - continue 2; - } elseif ($return === self::REMOVE_NODE) { - $doNodes[] = [$i, []]; - continue 2; - } elseif ($return === self::DONT_TRAVERSE_CHILDREN) { - $traverseChildren = \false; - } elseif ($return === self::DONT_TRAVERSE_CURRENT_AND_CHILDREN) { - $traverseChildren = \false; - $breakVisitorIndex = $visitorIndex; - break; - } elseif ($return === self::STOP_TRAVERSAL) { - $this->stopTraversal = \true; - break 2; - } else { - throw new LogicException('enterNode() returned invalid value of type ' . gettype($return)); - } - } - if ($traverseChildren) { - $node = $this->traverseNode($node); - if ($this->stopTraversal) { - break; - } - } - foreach ($this->visitors as $visitorIndex => $visitor) { - $return = $visitor->leaveNode($node); - if ($return !== null) { - if ($return instanceof Node) { - $this->ensureReplacementReasonable($node, $return); - $node = $return; - } elseif (is_array($return)) { - $doNodes[] = [$i, $return]; - break; - } elseif ($return === self::REMOVE_NODE) { - $doNodes[] = [$i, []]; - break; - } elseif ($return === self::STOP_TRAVERSAL) { - $this->stopTraversal = \true; - break 2; - } else { - throw new LogicException('leaveNode() returned invalid value of type ' . gettype($return)); - } - } - if ($breakVisitorIndex === $visitorIndex) { - break; - } + foreach ($data as $_data) { + if ($_data->isFromDataProvider()) { + if ($this->fromDataProvider !== null) { + throw new \PHPUnit\Event\TestData\MoreThanOneDataSetFromDataProviderException(); } - } elseif (is_array($node)) { - throw new LogicException('Invalid node structure: Contains nested arrays'); - } - } - if (count($doNodes) > 0) { - while ([$i, $replace] = array_pop($doNodes)) { - array_splice($nodes, $i, 1, $replace); + $this->fromDataProvider = $_data; } } - return $nodes; - } - private function ensureReplacementReasonable(Node $old, Node $new) : void - { - if ($old instanceof TypeNode && !$new instanceof TypeNode) { - throw new LogicException(sprintf('Trying to replace TypeNode with %s', get_class($new))); - } - if ($old instanceof ConstExprNode && !$new instanceof ConstExprNode) { - throw new LogicException(sprintf('Trying to replace ConstExprNode with %s', get_class($new))); - } - if ($old instanceof PhpDocChildNode && !$new instanceof PhpDocChildNode) { - throw new LogicException(sprintf('Trying to replace PhpDocChildNode with %s', get_class($new))); - } - if ($old instanceof PhpDocTagValueNode && !$new instanceof PhpDocTagValueNode) { - throw new LogicException(sprintf('Trying to replace PhpDocTagValueNode with %s', get_class($new))); - } } } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\TestData; +use function count; +use Iterator; /** - * Inspired by https://github.com/nikic/PHP-Parser/tree/36a6dcd04e7b0285e8f0868f44bd4927802f7df1 + * @template-implements Iterator * - * Copyright (c) 2011, Nikita Popov - * All rights reserved. + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -interface NodeVisitor +final class TestDataCollectionIterator implements Iterator { /** - * Called once before traversal. - * - * Return value semantics: - * * null: $nodes stays as-is - * * otherwise: $nodes is set to the return value - * - * @param Node[] $nodes Array of nodes - * - * @return Node[]|null Array of nodes - */ - public function beforeTraverse(array $nodes) : ?array; - /** - * Called when entering a node. - * - * Return value semantics: - * * null - * => $node stays as-is - * * array (of Nodes) - * => The return value is merged into the parent array (at the position of the $node) - * * NodeTraverser::REMOVE_NODE - * => $node is removed from the parent array - * * NodeTraverser::DONT_TRAVERSE_CHILDREN - * => Children of $node are not traversed. $node stays as-is - * * NodeTraverser::DONT_TRAVERSE_CURRENT_AND_CHILDREN - * => Further visitors for the current node are skipped, and its children are not - * traversed. $node stays as-is. - * * NodeTraverser::STOP_TRAVERSAL - * => Traversal is aborted. $node stays as-is - * * otherwise - * => $node is set to the return value - * - * @param Node $node Node - * - * @return Node|Node[]|NodeTraverser::*|null Replacement node (or special return value) - */ - public function enterNode(Node $node); - /** - * Called when leaving a node. - * - * Return value semantics: - * * null - * => $node stays as-is - * * NodeTraverser::REMOVE_NODE - * => $node is removed from the parent array - * * NodeTraverser::STOP_TRAVERSAL - * => Traversal is aborted. $node stays as-is - * * array (of Nodes) - * => The return value is merged into the parent array (at the position of the $node) - * * otherwise - * => $node is set to the return value - * - * @param Node $node Node - * - * @return Node|Node[]|NodeTraverser::REMOVE_NODE|NodeTraverser::STOP_TRAVERSAL|null Replacement node (or special return value) - */ - public function leaveNode(Node $node); - /** - * Called once after traversal. - * - * Return value semantics: - * * null: $nodes stays as-is - * * otherwise: $nodes is set to the return value - * - * @param Node[] $nodes Array of nodes - * - * @return Node[]|null Array of nodes + * @psalm-var list */ - public function afterTraverse(array $nodes) : ?array; -} -setAttribute(Attribute::ORIGINAL_NODE, $originalNode); - return $node; - } -} -type = $type; - $this->parameter = $parameter; - $this->method = $method; - $this->isNegated = $isNegated; - $this->isEquality = $isEquality; - $this->description = $description; + $this->data = $data->asArray(); } - public function __toString() : string + public function rewind(): void { - $isNegated = $this->isNegated ? '!' : ''; - $isEquality = $this->isEquality ? '=' : ''; - return trim("{$isNegated}{$isEquality}{$this->type} {$this->parameter}->{$this->method}() {$this->description}"); + $this->position = 0; } -} -type = $type; - $this->parameter = $parameter; - $this->property = $property; - $this->isNegated = $isNegated; - $this->isEquality = $isEquality; - $this->description = $description; + return $this->position < count($this->data); } - public function __toString() : string + public function key(): int { - $isNegated = $this->isNegated ? '!' : ''; - $isEquality = $this->isEquality ? '=' : ''; - return trim("{$isNegated}{$isEquality}{$this->type} {$this->parameter}->{$this->property} {$this->description}"); + return $this->position; } -} -type = $type; - $this->parameter = $parameter; - $this->isNegated = $isNegated; - $this->isEquality = $isEquality; - $this->description = $description; + return $this->data[$this->position]; } - public function __toString() : string + public function next(): void { - $isNegated = $this->isNegated ? '!' : ''; - $isEquality = $this->isEquality ? '=' : ''; - return trim("{$isNegated}{$isEquality}{$this->type} {$this->parameter} {$this->description}"); + $this->position++; } } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\Code; -use PHPUnitPHAR\PHPStan\PhpDocParser\Ast\NodeAttributes; -use function trim; -class DeprecatedTagValueNode implements PhpDocTagValueNode +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class TestDox { - use NodeAttributes; - /** @var string (may be empty) */ - public $description; - public function __construct(string $description) + private readonly string $prettifiedClassName; + private readonly string $prettifiedMethodName; + private readonly string $prettifiedAndColorizedMethodName; + public function __construct(string $prettifiedClassName, string $prettifiedMethodName, string $prettifiedAndColorizedMethodName) { - $this->description = $description; + $this->prettifiedClassName = $prettifiedClassName; + $this->prettifiedMethodName = $prettifiedMethodName; + $this->prettifiedAndColorizedMethodName = $prettifiedAndColorizedMethodName; } - public function __toString() : string + public function prettifiedClassName(): string { - return trim($this->description); + return $this->prettifiedClassName; + } + public function prettifiedMethodName(bool $colorize = \false): string + { + if ($colorize) { + return $this->prettifiedAndColorizedMethodName; + } + return $this->prettifiedMethodName; } } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\Code; -use PHPUnitPHAR\PHPStan\PhpDocParser\Ast\Node; -use PHPUnitPHAR\PHPStan\PhpDocParser\Ast\NodeAttributes; -use function implode; -class DoctrineAnnotation implements Node +use PHPUnit\Event\TestData\MoreThanOneDataSetFromDataProviderException; +use PHPUnit\Framework\TestCase; +use PHPUnit\Logging\TestDox\NamePrettifier; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class TestDoxBuilder { - use NodeAttributes; - /** @var string */ - public $name; - /** @var list */ - public $arguments; /** - * @param list $arguments + * @throws MoreThanOneDataSetFromDataProviderException */ - public function __construct(string $name, array $arguments) + public static function fromTestCase(TestCase $testCase): \PHPUnit\Event\Code\TestDox { - $this->name = $name; - $this->arguments = $arguments; + $prettifier = new NamePrettifier(); + return new \PHPUnit\Event\Code\TestDox($prettifier->prettifyTestClassName($testCase::class), $prettifier->prettifyTestCase($testCase, \false), $prettifier->prettifyTestCase($testCase, \true)); } - public function __toString() : string + /** + * @psalm-param class-string $className + * @psalm-param non-empty-string $methodName + */ + public static function fromClassNameAndMethodName(string $className, string $methodName): \PHPUnit\Event\Code\TestDox { - $arguments = implode(', ', $this->arguments); - return $this->name . '(' . $arguments . ')'; + $prettifier = new NamePrettifier(); + $prettifiedMethodName = $prettifier->prettifyTestMethodName($methodName); + return new \PHPUnit\Event\Code\TestDox($prettifier->prettifyTestClassName($className), $prettifiedMethodName, $prettifiedMethodName); } } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\Code; -use PHPUnitPHAR\PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprNode; -use PHPUnitPHAR\PHPStan\PhpDocParser\Ast\Node; -use PHPUnitPHAR\PHPStan\PhpDocParser\Ast\NodeAttributes; -use PHPUnitPHAR\PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode; +use function assert; +use function is_int; +use function sprintf; +use PHPUnit\Event\TestData\TestDataCollection; +use PHPUnit\Metadata\MetadataCollection; /** - * @phpstan-type ValueType = DoctrineAnnotation|IdentifierTypeNode|DoctrineArray|ConstExprNode + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -class DoctrineArgument implements Node +final class TestMethod extends \PHPUnit\Event\Code\Test { - use NodeAttributes; - /** @var IdentifierTypeNode|null */ - public $key; - /** @var ValueType */ - public $value; /** - * @param ValueType $value + * @psalm-var class-string */ - public function __construct(?IdentifierTypeNode $key, $value) - { - $this->key = $key; - $this->value = $value; - } - public function __toString() : string + private readonly string $className; + /** + * @psalm-var non-empty-string + */ + private readonly string $methodName; + /** + * @psalm-var non-negative-int + */ + private readonly int $line; + private readonly \PHPUnit\Event\Code\TestDox $testDox; + private readonly MetadataCollection $metadata; + private readonly TestDataCollection $testData; + /** + * @psalm-param class-string $className + * @psalm-param non-empty-string $methodName + * @psalm-param non-empty-string $file + * @psalm-param non-negative-int $line + */ + public function __construct(string $className, string $methodName, string $file, int $line, \PHPUnit\Event\Code\TestDox $testDox, MetadataCollection $metadata, TestDataCollection $testData) { - if ($this->key === null) { - return (string) $this->value; - } - return $this->key . '=' . $this->value; + parent::__construct($file); + $this->className = $className; + $this->methodName = $methodName; + $this->line = $line; + $this->testDox = $testDox; + $this->metadata = $metadata; + $this->testData = $testData; } -} - */ - public $items; /** - * @param list $items + * @psalm-return class-string */ - public function __construct(array $items) + public function className(): string { - $this->items = $items; + return $this->className; } - public function __toString() : string + /** + * @psalm-return non-empty-string + */ + public function methodName(): string { - $items = implode(', ', $this->items); - return '{' . $items . '}'; + return $this->methodName; } -} -key = $key; - $this->value = $value; + return $this->line; } - public function __toString() : string + public function testDox(): \PHPUnit\Event\Code\TestDox { - if ($this->key === null) { - return (string) $this->value; - } - return $this->key . '=' . $this->value; + return $this->testDox; } -} -annotation = $annotation; - $this->description = $description; + return $this->metadata; } - public function __toString() : string + public function testData(): TestDataCollection { - return trim("{$this->annotation} {$this->description}"); + return $this->testData; } -} -type = $type; - $this->description = $description; + return \true; } - public function __toString() : string + /** + * @psalm-return non-empty-string + */ + public function id(): string { - return trim("{$this->type} {$this->description}"); + $buffer = $this->className . '::' . $this->methodName; + if ($this->testData()->hasDataFromDataProvider()) { + $buffer .= '#' . $this->testData->dataFromDataProvider()->dataSetName(); + } + return $buffer; } -} -value = $value; + return $this->className . '::' . $this->name(); } - public function __toString() : string + /** + * @psalm-return non-empty-string + */ + public function name(): string { - return $this->value; + if (!$this->testData->hasDataFromDataProvider()) { + return $this->methodName; + } + $dataSetName = $this->testData->dataFromDataProvider()->dataSetName(); + if (is_int($dataSetName)) { + $dataSetName = sprintf(' with data set #%d', $dataSetName); + } else { + $dataSetName = sprintf(' with data set "%s"', $dataSetName); + } + return $this->methodName . $dataSetName; } } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\Code; -use PHPUnitPHAR\PHPStan\PhpDocParser\Ast\NodeAttributes; -use PHPUnitPHAR\PHPStan\PhpDocParser\Ast\Type\GenericTypeNode; -use function trim; -class ImplementsTagValueNode implements PhpDocTagValueNode +use const DEBUG_BACKTRACE_IGNORE_ARGS; +use const DEBUG_BACKTRACE_PROVIDE_OBJECT; +use function assert; +use function debug_backtrace; +use function is_numeric; +use PHPUnit\Event\Facade as EventFacade; +use PHPUnit\Event\TestData\DataFromDataProvider; +use PHPUnit\Event\TestData\DataFromTestDependency; +use PHPUnit\Event\TestData\MoreThanOneDataSetFromDataProviderException; +use PHPUnit\Event\TestData\TestDataCollection; +use PHPUnit\Framework\TestCase; +use PHPUnit\Metadata\Parser\Registry as MetadataRegistry; +use PHPUnit\Util\Exporter; +use PHPUnit\Util\Reflection; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class TestMethodBuilder { - use NodeAttributes; - /** @var GenericTypeNode */ - public $type; - /** @var string (may be empty) */ - public $description; - public function __construct(GenericTypeNode $type, string $description) + /** + * @throws MoreThanOneDataSetFromDataProviderException + */ + public static function fromTestCase(TestCase $testCase): \PHPUnit\Event\Code\TestMethod { - $this->type = $type; - $this->description = $description; + $methodName = $testCase->name(); + assert(!empty($methodName)); + $location = Reflection::sourceLocationFor($testCase::class, $methodName); + return new \PHPUnit\Event\Code\TestMethod($testCase::class, $methodName, $location['file'], $location['line'], \PHPUnit\Event\Code\TestDoxBuilder::fromTestCase($testCase), MetadataRegistry::parser()->forClassAndMethod($testCase::class, $methodName), self::dataFor($testCase)); + } + /** + * @throws NoTestCaseObjectOnCallStackException + */ + public static function fromCallStack(): \PHPUnit\Event\Code\TestMethod + { + foreach (debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT | DEBUG_BACKTRACE_IGNORE_ARGS) as $frame) { + if (isset($frame['object']) && $frame['object'] instanceof TestCase) { + return $frame['object']->valueObjectForEvents(); + } + } + throw new \PHPUnit\Event\Code\NoTestCaseObjectOnCallStackException(); } - public function __toString() : string + /** + * @throws MoreThanOneDataSetFromDataProviderException + */ + private static function dataFor(TestCase $testCase): TestDataCollection { - return trim("{$this->type} {$this->description}"); + $testData = []; + if ($testCase->usesDataProvider()) { + $dataSetName = $testCase->dataName(); + if (is_numeric($dataSetName)) { + $dataSetName = (int) $dataSetName; + } + $testData[] = DataFromDataProvider::from($dataSetName, Exporter::export($testCase->providedData(), EventFacade::emitter()->exportsObjects()), $testCase->dataSetAsStringWithData()); + } + if ($testCase->hasDependencyInput()) { + $testData[] = DataFromTestDependency::from(Exporter::export($testCase->dependencyInput(), EventFacade::emitter()->exportsObjects())); + } + return TestDataCollection::fromArray($testData); } } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\TestSuite; -use PHPUnitPHAR\PHPStan\PhpDocParser\Ast\NodeAttributes; -use PHPUnitPHAR\PHPStan\PhpDocParser\Parser\ParserException; -use function sprintf; -use function trigger_error; -use const E_USER_WARNING; +use PHPUnit\Event\Code\TestCollection; /** - * @property ParserException $exception + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -class InvalidTagValueNode implements PhpDocTagValueNode +abstract class TestSuite { - use NodeAttributes; - /** @var string (may be empty) */ - public $value; - /** @var mixed[] */ - private $exceptionArgs; - public function __construct(string $value, ParserException $exception) + /** + * @psalm-var non-empty-string + */ + private readonly string $name; + private readonly int $count; + private readonly TestCollection $tests; + /** + * @psalm-param non-empty-string $name + */ + public function __construct(string $name, int $size, TestCollection $tests) { - $this->value = $value; - $this->exceptionArgs = [$exception->getCurrentTokenValue(), $exception->getCurrentTokenType(), $exception->getCurrentOffset(), $exception->getExpectedTokenType(), $exception->getExpectedTokenValue(), $exception->getCurrentTokenLine()]; + $this->name = $name; + $this->count = $size; + $this->tests = $tests; } - public function __get(string $name) : ?ParserException + /** + * @psalm-return non-empty-string + */ + public function name(): string { - if ($name !== 'exception') { - trigger_error(sprintf('Undefined property: %s::$%s', self::class, $name), E_USER_WARNING); - return null; - } - return new ParserException(...$this->exceptionArgs); + return $this->name; } - public function __toString() : string + public function count(): int { - return $this->value; + return $this->count; } -} -isStatic = $isStatic; - $this->returnType = $returnType; - $this->methodName = $methodName; - $this->parameters = $parameters; - $this->description = $description; - $this->templateTypes = $templateTypes; + return $this->tests; } - public function __toString() : string + /** + * @psalm-assert-if-true TestSuiteWithName $this + */ + public function isWithName(): bool { - $static = $this->isStatic ? 'static ' : ''; - $returnType = $this->returnType !== null ? "{$this->returnType} " : ''; - $parameters = implode(', ', $this->parameters); - $description = $this->description !== '' ? " {$this->description}" : ''; - $templateTypes = count($this->templateTypes) > 0 ? '<' . implode(', ', $this->templateTypes) . '>' : ''; - return "{$static}{$returnType}{$this->methodName}{$templateTypes}({$parameters}){$description}"; + return \false; } -} -type = $type; - $this->isReference = $isReference; - $this->isVariadic = $isVariadic; - $this->parameterName = $parameterName; - $this->defaultValue = $defaultValue; + return \false; } - public function __toString() : string + /** + * @psalm-assert-if-true TestSuiteForTestMethodWithDataProvider $this + */ + public function isForTestMethodWithDataProvider(): bool { - $type = $this->type !== null ? "{$this->type} " : ''; - $isReference = $this->isReference ? '&' : ''; - $isVariadic = $this->isVariadic ? '...' : ''; - $default = $this->defaultValue !== null ? " = {$this->defaultValue}" : ''; - return "{$type}{$isReference}{$isVariadic}{$this->parameterName}{$default}"; + return \false; } } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\TestSuite; -use PHPUnitPHAR\PHPStan\PhpDocParser\Ast\NodeAttributes; -use PHPUnitPHAR\PHPStan\PhpDocParser\Ast\Type\TypeNode; -use function trim; -class MixinTagValueNode implements PhpDocTagValueNode +use function explode; +use PHPUnit\Event\Code\Test; +use PHPUnit\Event\Code\TestCollection; +use PHPUnit\Event\RuntimeException; +use PHPUnit\Framework\DataProviderTestSuite; +use PHPUnit\Framework\TestCase; +use PHPUnit\Framework\TestSuite as FrameworkTestSuite; +use PHPUnit\Runner\PhptTestCase; +use ReflectionClass; +use ReflectionException; +use ReflectionMethod; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class TestSuiteBuilder { - use NodeAttributes; - /** @var TypeNode */ - public $type; - /** @var string (may be empty) */ - public $description; - public function __construct(TypeNode $type, string $description) + /** + * @throws RuntimeException + */ + public static function from(FrameworkTestSuite $testSuite): \PHPUnit\Event\TestSuite\TestSuite { - $this->type = $type; - $this->description = $description; + $tests = []; + self::process($testSuite, $tests); + if ($testSuite instanceof DataProviderTestSuite) { + [$className, $methodName] = explode('::', $testSuite->name()); + try { + $reflector = new ReflectionMethod($className, $methodName); + return new \PHPUnit\Event\TestSuite\TestSuiteForTestMethodWithDataProvider($testSuite->name(), $testSuite->count(), TestCollection::fromArray($tests), $className, $methodName, $reflector->getFileName(), $reflector->getStartLine()); + // @codeCoverageIgnoreStart + } catch (ReflectionException $e) { + throw new RuntimeException($e->getMessage(), $e->getCode(), $e); + } + // @codeCoverageIgnoreEnd + } + if ($testSuite->isForTestClass()) { + try { + $reflector = new ReflectionClass($testSuite->name()); + return new \PHPUnit\Event\TestSuite\TestSuiteForTestClass($testSuite->name(), $testSuite->count(), TestCollection::fromArray($tests), $reflector->getFileName(), $reflector->getStartLine()); + // @codeCoverageIgnoreStart + } catch (ReflectionException $e) { + throw new RuntimeException($e->getMessage(), $e->getCode(), $e); + } + // @codeCoverageIgnoreEnd + } + return new \PHPUnit\Event\TestSuite\TestSuiteWithName($testSuite->name(), $testSuite->count(), TestCollection::fromArray($tests)); } - public function __toString() : string + /** + * @psalm-param list $tests + */ + private static function process(FrameworkTestSuite $testSuite, array &$tests): void { - return trim("{$this->type} {$this->description}"); + foreach ($testSuite->getIterator() as $test) { + if ($test instanceof FrameworkTestSuite) { + self::process($test, $tests); + continue; + } + if ($test instanceof TestCase || $test instanceof PhptTestCase) { + $tests[] = $test->valueObjectForEvents(); + } + } } } type = $type; - $this->parameterName = $parameterName; - $this->description = $description; +/* + * This file is part of PHPUnit. + * + * (c) Sebastian Bergmann + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\TestSuite; + +use PHPUnit\Event\Code\TestCollection; +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class TestSuiteForTestClass extends \PHPUnit\Event\TestSuite\TestSuite +{ + /** + * @psalm-var class-string + */ + private readonly string $className; + private readonly string $file; + private readonly int $line; + /** + * @psalm-param class-string $name + */ + public function __construct(string $name, int $size, TestCollection $tests, string $file, int $line) + { + parent::__construct($name, $size, $tests); + $this->className = $name; + $this->file = $file; + $this->line = $line; + } + /** + * @psalm-return class-string + */ + public function className(): string + { + return $this->className; + } + public function file(): string + { + return $this->file; + } + public function line(): int + { + return $this->line; } - public function __toString() : string + /** + * @psalm-assert-if-true TestSuiteForTestClass $this + */ + public function isForTestClass(): bool { - return trim("{$this->type} {$this->parameterName} {$this->description}"); + return \true; } } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\TestSuite; -use PHPUnitPHAR\PHPStan\PhpDocParser\Ast\NodeAttributes; -use function trim; -class ParamImmediatelyInvokedCallableTagValueNode implements PhpDocTagValueNode +use PHPUnit\Event\Code\TestCollection; +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class TestSuiteForTestMethodWithDataProvider extends \PHPUnit\Event\TestSuite\TestSuite { - use NodeAttributes; - /** @var string */ - public $parameterName; - /** @var string (may be empty) */ - public $description; - public function __construct(string $parameterName, string $description) + /** + * @psalm-var class-string + */ + private readonly string $className; + /** + * @psalm-var non-empty-string + */ + private readonly string $methodName; + private readonly string $file; + private readonly int $line; + /** + * @psalm-param non-empty-string $name + * @psalm-param class-string $className + * @psalm-param non-empty-string $methodName + */ + public function __construct(string $name, int $size, TestCollection $tests, string $className, string $methodName, string $file, int $line) { - $this->parameterName = $parameterName; - $this->description = $description; + parent::__construct($name, $size, $tests); + $this->className = $className; + $this->methodName = $methodName; + $this->file = $file; + $this->line = $line; + } + /** + * @psalm-return class-string + */ + public function className(): string + { + return $this->className; + } + /** + * @psalm-return non-empty-string + */ + public function methodName(): string + { + return $this->methodName; + } + public function file(): string + { + return $this->file; + } + public function line(): int + { + return $this->line; } - public function __toString() : string + /** + * @psalm-assert-if-true TestSuiteForTestMethodWithDataProvider $this + */ + public function isForTestMethodWithDataProvider(): bool { - return trim("{$this->parameterName} {$this->description}"); + return \true; } } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\TestSuite; -use PHPUnitPHAR\PHPStan\PhpDocParser\Ast\NodeAttributes; -use function trim; -class ParamLaterInvokedCallableTagValueNode implements PhpDocTagValueNode +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class TestSuiteWithName extends \PHPUnit\Event\TestSuite\TestSuite { - use NodeAttributes; - /** @var string */ - public $parameterName; - /** @var string (may be empty) */ - public $description; - public function __construct(string $parameterName, string $description) - { - $this->parameterName = $parameterName; - $this->description = $description; - } - public function __toString() : string + /** + * @psalm-assert-if-true TestSuiteWithName $this + */ + public function isWithName(): bool { - return trim("{$this->parameterName} {$this->description}"); + return \true; } } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\Code; -use PHPUnitPHAR\PHPStan\PhpDocParser\Ast\NodeAttributes; -use PHPUnitPHAR\PHPStan\PhpDocParser\Ast\Type\TypeNode; -use function trim; -class ParamOutTagValueNode implements PhpDocTagValueNode +use const PHP_EOL; +use PHPUnit\Event\NoPreviousThrowableException; +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class Throwable { - use NodeAttributes; - /** @var TypeNode */ - public $type; - /** @var string */ - public $parameterName; - /** @var string (may be empty) */ - public $description; - public function __construct(TypeNode $type, string $parameterName, string $description) + /** + * @psalm-var class-string + */ + private readonly string $className; + private readonly string $message; + private readonly string $description; + private readonly string $stackTrace; + private readonly ?\PHPUnit\Event\Code\Throwable $previous; + /** + * @psalm-param class-string $className + */ + public function __construct(string $className, string $message, string $description, string $stackTrace, ?self $previous) { - $this->type = $type; - $this->parameterName = $parameterName; + $this->className = $className; + $this->message = $message; $this->description = $description; + $this->stackTrace = $stackTrace; + $this->previous = $previous; + } + /** + * @throws NoPreviousThrowableException + */ + public function asString(): string + { + $buffer = $this->description(); + if (!empty($this->stackTrace())) { + $buffer .= PHP_EOL . $this->stackTrace(); + } + if ($this->hasPrevious()) { + $buffer .= PHP_EOL . 'Caused by' . PHP_EOL . $this->previous()->asString(); + } + return $buffer; + } + /** + * @psalm-return class-string + */ + public function className(): string + { + return $this->className; + } + public function message(): string + { + return $this->message; + } + public function description(): string + { + return $this->description; + } + public function stackTrace(): string + { + return $this->stackTrace; + } + /** + * @psalm-assert-if-true !null $this->previous + */ + public function hasPrevious(): bool + { + return $this->previous !== null; } - public function __toString() : string + /** + * @throws NoPreviousThrowableException + */ + public function previous(): self { - return trim("{$this->type} {$this->parameterName} {$this->description}"); + if ($this->previous === null) { + throw new NoPreviousThrowableException(); + } + return $this->previous; } } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\Code; -use PHPUnitPHAR\PHPStan\PhpDocParser\Ast\NodeAttributes; -use PHPUnitPHAR\PHPStan\PhpDocParser\Ast\Type\TypeNode; -use function trim; -class ParamTagValueNode implements PhpDocTagValueNode -{ - use NodeAttributes; - /** @var TypeNode */ - public $type; - /** @var bool */ - public $isReference; - /** @var bool */ - public $isVariadic; - /** @var string */ - public $parameterName; - /** @var string (may be empty) */ - public $description; - public function __construct(TypeNode $type, bool $isVariadic, string $parameterName, string $description, bool $isReference = \false) - { - $this->type = $type; - $this->isReference = $isReference; - $this->isVariadic = $isVariadic; - $this->parameterName = $parameterName; - $this->description = $description; - } - public function __toString() : string +use PHPUnit\Event\NoPreviousThrowableException; +use PHPUnit\Framework\Exception; +use PHPUnit\Util\Filter; +use PHPUnit\Util\ThrowableToStringMapper; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class ThrowableBuilder +{ + /** + * @throws Exception + * @throws NoPreviousThrowableException + */ + public static function from(\Throwable $t): \PHPUnit\Event\Code\Throwable { - $reference = $this->isReference ? '&' : ''; - $variadic = $this->isVariadic ? '...' : ''; - return trim("{$this->type} {$reference}{$variadic}{$this->parameterName} {$this->description}"); + $previous = $t->getPrevious(); + if ($previous !== null) { + $previous = self::from($previous); + } + return new \PHPUnit\Event\Code\Throwable($t::class, $t->getMessage(), ThrowableToStringMapper::map($t), Filter::getFilteredStacktrace($t, \false), $previous); } } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit; -use PHPUnitPHAR\PHPStan\PhpDocParser\Ast\Node; -interface PhpDocChildNode extends Node +use Throwable; +interface Exception extends Throwable { } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; -use PHPUnitPHAR\PHPStan\PhpDocParser\Ast\Node; -use PHPUnitPHAR\PHPStan\PhpDocParser\Ast\NodeAttributes; -use function array_column; -use function array_filter; -use function array_map; -use function implode; -class PhpDocNode implements Node +use function class_exists; +use function count; +use function file_get_contents; +use function interface_exists; +use function is_bool; +use ArrayAccess; +use Countable; +use Generator; +use PHPUnit\Event; +use PHPUnit\Framework\Constraint\ArrayHasKey; +use PHPUnit\Framework\Constraint\Callback; +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\IsEqualCanonicalizing; +use PHPUnit\Framework\Constraint\IsEqualIgnoringCase; +use PHPUnit\Framework\Constraint\IsEqualWithDelta; +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\IsList; +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\ObjectEquals; +use PHPUnit\Framework\Constraint\ObjectHasProperty; +use PHPUnit\Framework\Constraint\RegularExpression; +use PHPUnit\Framework\Constraint\SameSize; +use PHPUnit\Framework\Constraint\StringContains; +use PHPUnit\Framework\Constraint\StringEndsWith; +use PHPUnit\Framework\Constraint\StringEqualsStringIgnoringLineEndings; +use PHPUnit\Framework\Constraint\StringMatchesFormatDescription; +use PHPUnit\Framework\Constraint\StringStartsWith; +use PHPUnit\Framework\Constraint\TraversableContainsEqual; +use PHPUnit\Framework\Constraint\TraversableContainsIdentical; +use PHPUnit\Framework\Constraint\TraversableContainsOnly; +use PHPUnit\Util\Xml\Loader as XmlLoader; +use PHPUnit\Util\Xml\XmlException; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +abstract class Assert { - use NodeAttributes; - /** @var PhpDocChildNode[] */ - public $children; + private static int $count = 0; /** - * @param PhpDocChildNode[] $children + * Asserts that an array has a specified key. + * + * @throws Exception + * @throws ExpectationFailedException */ - public function __construct(array $children) + final public static function assertArrayHasKey(int|string $key, array|ArrayAccess $array, string $message = ''): void { - $this->children = $children; + $constraint = new ArrayHasKey($key); + static::assertThat($array, $constraint, $message); } /** - * @return PhpDocTagNode[] + * Asserts that an array does not have a specified key. + * + * @throws Exception + * @throws ExpectationFailedException */ - public function getTags() : array + final public static function assertArrayNotHasKey(int|string $key, array|ArrayAccess $array, string $message = ''): void { - return array_filter($this->children, static function (PhpDocChildNode $child) : bool { - return $child instanceof PhpDocTagNode; - }); + $constraint = new LogicalNot(new ArrayHasKey($key)); + static::assertThat($array, $constraint, $message); } /** - * @return PhpDocTagNode[] + * @throws ExpectationFailedException */ - public function getTagsByName(string $tagName) : array + final public static function assertIsList(mixed $array, string $message = ''): void { - return array_filter($this->getTags(), static function (PhpDocTagNode $tag) use($tagName) : bool { - return $tag->name === $tagName; - }); + static::assertThat($array, new IsList(), $message); } /** - * @return VarTagValueNode[] + * Asserts that a haystack contains a needle. + * + * @throws Exception + * @throws ExpectationFailedException */ - public function getVarTagValues(string $tagName = '@var') : array + final public static function assertContains(mixed $needle, iterable $haystack, string $message = ''): void { - return array_filter(array_column($this->getTagsByName($tagName), 'value'), static function (PhpDocTagValueNode $value) : bool { - return $value instanceof VarTagValueNode; - }); + $constraint = new TraversableContainsIdentical($needle); + static::assertThat($haystack, $constraint, $message); } /** - * @return ParamTagValueNode[] + * @throws ExpectationFailedException */ - public function getParamTagValues(string $tagName = '@param') : array + final public static function assertContainsEquals(mixed $needle, iterable $haystack, string $message = ''): void { - return array_filter(array_column($this->getTagsByName($tagName), 'value'), static function (PhpDocTagValueNode $value) : bool { - return $value instanceof ParamTagValueNode; - }); + $constraint = new TraversableContainsEqual($needle); + static::assertThat($haystack, $constraint, $message); } /** - * @return TypelessParamTagValueNode[] + * Asserts that a haystack does not contain a needle. + * + * @throws Exception + * @throws ExpectationFailedException */ - public function getTypelessParamTagValues(string $tagName = '@param') : array + final public static function assertNotContains(mixed $needle, iterable $haystack, string $message = ''): void { - return array_filter(array_column($this->getTagsByName($tagName), 'value'), static function (PhpDocTagValueNode $value) : bool { - return $value instanceof TypelessParamTagValueNode; - }); + $constraint = new LogicalNot(new TraversableContainsIdentical($needle)); + static::assertThat($haystack, $constraint, $message); } /** - * @return ParamImmediatelyInvokedCallableTagValueNode[] + * @throws ExpectationFailedException */ - public function getParamImmediatelyInvokedCallableTagValues(string $tagName = '@param-immediately-invoked-callable') : array + final public static function assertNotContainsEquals(mixed $needle, iterable $haystack, string $message = ''): void { - return array_filter(array_column($this->getTagsByName($tagName), 'value'), static function (PhpDocTagValueNode $value) : bool { - return $value instanceof ParamImmediatelyInvokedCallableTagValueNode; - }); + $constraint = new LogicalNot(new TraversableContainsEqual($needle)); + static::assertThat($haystack, $constraint, $message); } /** - * @return ParamLaterInvokedCallableTagValueNode[] + * Asserts that a haystack contains only values of a given type. + * + * @throws Exception + * @throws ExpectationFailedException */ - public function getParamLaterInvokedCallableTagValues(string $tagName = '@param-later-invoked-callable') : array + final public static function assertContainsOnly(string $type, iterable $haystack, ?bool $isNativeType = null, string $message = ''): void { - return array_filter(array_column($this->getTagsByName($tagName), 'value'), static function (PhpDocTagValueNode $value) : bool { - return $value instanceof ParamLaterInvokedCallableTagValueNode; - }); + if ($isNativeType === null) { + $isNativeType = self::isNativeType($type); + } + static::assertThat($haystack, new TraversableContainsOnly($type, $isNativeType), $message); } /** - * @return ParamClosureThisTagValueNode[] + * Asserts that a haystack contains only instances of a given class name. + * + * @throws Exception + * @throws ExpectationFailedException */ - public function getParamClosureThisTagValues(string $tagName = '@param-closure-this') : array + final public static function assertContainsOnlyInstancesOf(string $className, iterable $haystack, string $message = ''): void { - return array_filter(array_column($this->getTagsByName($tagName), 'value'), static function (PhpDocTagValueNode $value) : bool { - return $value instanceof ParamClosureThisTagValueNode; - }); + static::assertThat($haystack, new TraversableContainsOnly($className, \false), $message); } /** - * @return TemplateTagValueNode[] + * Asserts that a haystack does not contain only values of a given type. + * + * @throws Exception + * @throws ExpectationFailedException */ - public function getTemplateTagValues(string $tagName = '@template') : array + final public static function assertNotContainsOnly(string $type, iterable $haystack, ?bool $isNativeType = null, string $message = ''): void { - return array_filter(array_column($this->getTagsByName($tagName), 'value'), static function (PhpDocTagValueNode $value) : bool { - return $value instanceof TemplateTagValueNode; - }); + if ($isNativeType === null) { + $isNativeType = self::isNativeType($type); + } + static::assertThat($haystack, new LogicalNot(new TraversableContainsOnly($type, $isNativeType)), $message); } /** - * @return ExtendsTagValueNode[] + * Asserts the number of elements of an array, Countable or Traversable. + * + * @throws Exception + * @throws ExpectationFailedException + * @throws GeneratorNotSupportedException */ - public function getExtendsTagValues(string $tagName = '@extends') : array + final public static function assertCount(int $expectedCount, Countable|iterable $haystack, string $message = ''): void { - return array_filter(array_column($this->getTagsByName($tagName), 'value'), static function (PhpDocTagValueNode $value) : bool { - return $value instanceof ExtendsTagValueNode; - }); + if ($haystack instanceof Generator) { + throw \PHPUnit\Framework\GeneratorNotSupportedException::fromParameterName('$haystack'); + } + static::assertThat($haystack, new Count($expectedCount), $message); } /** - * @return ImplementsTagValueNode[] + * Asserts the number of elements of an array, Countable or Traversable. + * + * @throws Exception + * @throws ExpectationFailedException + * @throws GeneratorNotSupportedException */ - public function getImplementsTagValues(string $tagName = '@implements') : array + final public static function assertNotCount(int $expectedCount, Countable|iterable $haystack, string $message = ''): void { - return array_filter(array_column($this->getTagsByName($tagName), 'value'), static function (PhpDocTagValueNode $value) : bool { - return $value instanceof ImplementsTagValueNode; - }); + if ($haystack instanceof Generator) { + throw \PHPUnit\Framework\GeneratorNotSupportedException::fromParameterName('$haystack'); + } + $constraint = new LogicalNot(new Count($expectedCount)); + static::assertThat($haystack, $constraint, $message); } /** - * @return UsesTagValueNode[] + * Asserts that two variables are equal. + * + * @throws ExpectationFailedException */ - public function getUsesTagValues(string $tagName = '@use') : array + final public static function assertEquals(mixed $expected, mixed $actual, string $message = ''): void { - return array_filter(array_column($this->getTagsByName($tagName), 'value'), static function (PhpDocTagValueNode $value) : bool { - return $value instanceof UsesTagValueNode; - }); + $constraint = new IsEqual($expected); + static::assertThat($actual, $constraint, $message); } /** - * @return ReturnTagValueNode[] + * Asserts that two variables are equal (canonicalizing). + * + * @throws ExpectationFailedException */ - public function getReturnTagValues(string $tagName = '@return') : array + final public static function assertEqualsCanonicalizing(mixed $expected, mixed $actual, string $message = ''): void { - return array_filter(array_column($this->getTagsByName($tagName), 'value'), static function (PhpDocTagValueNode $value) : bool { - return $value instanceof ReturnTagValueNode; - }); + $constraint = new IsEqualCanonicalizing($expected); + static::assertThat($actual, $constraint, $message); } /** - * @return ThrowsTagValueNode[] + * Asserts that two variables are equal (ignoring case). + * + * @throws ExpectationFailedException */ - public function getThrowsTagValues(string $tagName = '@throws') : array + final public static function assertEqualsIgnoringCase(mixed $expected, mixed $actual, string $message = ''): void { - return array_filter(array_column($this->getTagsByName($tagName), 'value'), static function (PhpDocTagValueNode $value) : bool { - return $value instanceof ThrowsTagValueNode; - }); + $constraint = new IsEqualIgnoringCase($expected); + static::assertThat($actual, $constraint, $message); } /** - * @return MixinTagValueNode[] + * Asserts that two variables are equal (with delta). + * + * @throws ExpectationFailedException */ - public function getMixinTagValues(string $tagName = '@mixin') : array + final public static function assertEqualsWithDelta(mixed $expected, mixed $actual, float $delta, string $message = ''): void { - return array_filter(array_column($this->getTagsByName($tagName), 'value'), static function (PhpDocTagValueNode $value) : bool { - return $value instanceof MixinTagValueNode; - }); + $constraint = new IsEqualWithDelta($expected, $delta); + static::assertThat($actual, $constraint, $message); } /** - * @return RequireExtendsTagValueNode[] + * Asserts that two variables are not equal. + * + * @throws ExpectationFailedException */ - public function getRequireExtendsTagValues(string $tagName = '@phpstan-require-extends') : array + final public static function assertNotEquals(mixed $expected, mixed $actual, string $message = ''): void { - return array_filter(array_column($this->getTagsByName($tagName), 'value'), static function (PhpDocTagValueNode $value) : bool { - return $value instanceof RequireExtendsTagValueNode; - }); + $constraint = new LogicalNot(new IsEqual($expected)); + static::assertThat($actual, $constraint, $message); } /** - * @return RequireImplementsTagValueNode[] + * Asserts that two variables are not equal (canonicalizing). + * + * @throws ExpectationFailedException */ - public function getRequireImplementsTagValues(string $tagName = '@phpstan-require-implements') : array + final public static function assertNotEqualsCanonicalizing(mixed $expected, mixed $actual, string $message = ''): void { - return array_filter(array_column($this->getTagsByName($tagName), 'value'), static function (PhpDocTagValueNode $value) : bool { - return $value instanceof RequireImplementsTagValueNode; - }); + $constraint = new LogicalNot(new IsEqualCanonicalizing($expected)); + static::assertThat($actual, $constraint, $message); } /** - * @return DeprecatedTagValueNode[] + * Asserts that two variables are not equal (ignoring case). + * + * @throws ExpectationFailedException */ - public function getDeprecatedTagValues() : array + final public static function assertNotEqualsIgnoringCase(mixed $expected, mixed $actual, string $message = ''): void { - return array_filter(array_column($this->getTagsByName('@deprecated'), 'value'), static function (PhpDocTagValueNode $value) : bool { - return $value instanceof DeprecatedTagValueNode; - }); + $constraint = new LogicalNot(new IsEqualIgnoringCase($expected)); + static::assertThat($actual, $constraint, $message); } /** - * @return PropertyTagValueNode[] + * Asserts that two variables are not equal (with delta). + * + * @throws ExpectationFailedException */ - public function getPropertyTagValues(string $tagName = '@property') : array + final public static function assertNotEqualsWithDelta(mixed $expected, mixed $actual, float $delta, string $message = ''): void { - return array_filter(array_column($this->getTagsByName($tagName), 'value'), static function (PhpDocTagValueNode $value) : bool { - return $value instanceof PropertyTagValueNode; - }); + $constraint = new LogicalNot(new IsEqualWithDelta($expected, $delta)); + static::assertThat($actual, $constraint, $message); } /** - * @return PropertyTagValueNode[] + * @throws ExpectationFailedException */ - public function getPropertyReadTagValues(string $tagName = '@property-read') : array + final public static function assertObjectEquals(object $expected, object $actual, string $method = 'equals', string $message = ''): void { - return array_filter(array_column($this->getTagsByName($tagName), 'value'), static function (PhpDocTagValueNode $value) : bool { - return $value instanceof PropertyTagValueNode; - }); + static::assertThat($actual, static::objectEquals($expected, $method), $message); } /** - * @return PropertyTagValueNode[] + * Asserts that a variable is empty. + * + * @throws ExpectationFailedException + * @throws GeneratorNotSupportedException + * + * @psalm-assert empty $actual */ - public function getPropertyWriteTagValues(string $tagName = '@property-write') : array + final public static function assertEmpty(mixed $actual, string $message = ''): void { - return array_filter(array_column($this->getTagsByName($tagName), 'value'), static function (PhpDocTagValueNode $value) : bool { - return $value instanceof PropertyTagValueNode; - }); + if ($actual instanceof Generator) { + throw \PHPUnit\Framework\GeneratorNotSupportedException::fromParameterName('$actual'); + } + static::assertThat($actual, static::isEmpty(), $message); } /** - * @return MethodTagValueNode[] + * Asserts that a variable is not empty. + * + * @throws ExpectationFailedException + * @throws GeneratorNotSupportedException + * + * @psalm-assert !empty $actual */ - public function getMethodTagValues(string $tagName = '@method') : array + final public static function assertNotEmpty(mixed $actual, string $message = ''): void { - return array_filter(array_column($this->getTagsByName($tagName), 'value'), static function (PhpDocTagValueNode $value) : bool { - return $value instanceof MethodTagValueNode; - }); + if ($actual instanceof Generator) { + throw \PHPUnit\Framework\GeneratorNotSupportedException::fromParameterName('$actual'); + } + static::assertThat($actual, static::logicalNot(static::isEmpty()), $message); } /** - * @return TypeAliasTagValueNode[] + * Asserts that a value is greater than another value. + * + * @throws ExpectationFailedException */ - public function getTypeAliasTagValues(string $tagName = '@phpstan-type') : array + final public static function assertGreaterThan(mixed $expected, mixed $actual, string $message = ''): void { - return array_filter(array_column($this->getTagsByName($tagName), 'value'), static function (PhpDocTagValueNode $value) : bool { - return $value instanceof TypeAliasTagValueNode; - }); + static::assertThat($actual, static::greaterThan($expected), $message); } /** - * @return TypeAliasImportTagValueNode[] + * Asserts that a value is greater than or equal to another value. + * + * @throws ExpectationFailedException */ - public function getTypeAliasImportTagValues(string $tagName = '@phpstan-import-type') : array + final public static function assertGreaterThanOrEqual(mixed $expected, mixed $actual, string $message = ''): void { - return array_filter(array_column($this->getTagsByName($tagName), 'value'), static function (PhpDocTagValueNode $value) : bool { - return $value instanceof TypeAliasImportTagValueNode; - }); + static::assertThat($actual, static::greaterThanOrEqual($expected), $message); } /** - * @return AssertTagValueNode[] + * Asserts that a value is smaller than another value. + * + * @throws ExpectationFailedException */ - public function getAssertTagValues(string $tagName = '@phpstan-assert') : array + final public static function assertLessThan(mixed $expected, mixed $actual, string $message = ''): void { - return array_filter(array_column($this->getTagsByName($tagName), 'value'), static function (PhpDocTagValueNode $value) : bool { - return $value instanceof AssertTagValueNode; - }); + static::assertThat($actual, static::lessThan($expected), $message); } /** - * @return AssertTagPropertyValueNode[] + * Asserts that a value is smaller than or equal to another value. + * + * @throws ExpectationFailedException */ - public function getAssertPropertyTagValues(string $tagName = '@phpstan-assert') : array + final public static function assertLessThanOrEqual(mixed $expected, mixed $actual, string $message = ''): void { - return array_filter(array_column($this->getTagsByName($tagName), 'value'), static function (PhpDocTagValueNode $value) : bool { - return $value instanceof AssertTagPropertyValueNode; - }); + static::assertThat($actual, static::lessThanOrEqual($expected), $message); } /** - * @return AssertTagMethodValueNode[] + * Asserts that the contents of one file is equal to the contents of another + * file. + * + * @throws ExpectationFailedException */ - public function getAssertMethodTagValues(string $tagName = '@phpstan-assert') : array + final public static function assertFileEquals(string $expected, string $actual, string $message = ''): void { - return array_filter(array_column($this->getTagsByName($tagName), 'value'), static function (PhpDocTagValueNode $value) : bool { - return $value instanceof AssertTagMethodValueNode; - }); + static::assertFileExists($expected, $message); + static::assertFileExists($actual, $message); + $constraint = new IsEqual(file_get_contents($expected)); + static::assertThat(file_get_contents($actual), $constraint, $message); } /** - * @return SelfOutTagValueNode[] + * Asserts that the contents of one file is equal to the contents of another + * file (canonicalizing). + * + * @throws ExpectationFailedException */ - public function getSelfOutTypeTagValues(string $tagName = '@phpstan-this-out') : array + final public static function assertFileEqualsCanonicalizing(string $expected, string $actual, string $message = ''): void { - return array_filter(array_column($this->getTagsByName($tagName), 'value'), static function (PhpDocTagValueNode $value) : bool { - return $value instanceof SelfOutTagValueNode; - }); + static::assertFileExists($expected, $message); + static::assertFileExists($actual, $message); + $constraint = new IsEqualCanonicalizing(file_get_contents($expected)); + static::assertThat(file_get_contents($actual), $constraint, $message); } /** - * @return ParamOutTagValueNode[] + * Asserts that the contents of one file is equal to the contents of another + * file (ignoring case). + * + * @throws ExpectationFailedException */ - public function getParamOutTypeTagValues(string $tagName = '@param-out') : array + final public static function assertFileEqualsIgnoringCase(string $expected, string $actual, string $message = ''): void { - return array_filter(array_column($this->getTagsByName($tagName), 'value'), static function (PhpDocTagValueNode $value) : bool { - return $value instanceof ParamOutTagValueNode; - }); + static::assertFileExists($expected, $message); + static::assertFileExists($actual, $message); + $constraint = new IsEqualIgnoringCase(file_get_contents($expected)); + static::assertThat(file_get_contents($actual), $constraint, $message); } - public function __toString() : string + /** + * Asserts that the contents of one file is not equal to the contents of + * another file. + * + * @throws ExpectationFailedException + */ + final public static function assertFileNotEquals(string $expected, string $actual, string $message = ''): void { - $children = array_map(static function (PhpDocChildNode $child) : string { - $s = (string) $child; - return $s === '' ? '' : ' ' . $s; - }, $this->children); - return "/**\n *" . implode("\n *", $children) . "\n */"; + static::assertFileExists($expected, $message); + static::assertFileExists($actual, $message); + $constraint = new LogicalNot(new IsEqual(file_get_contents($expected))); + static::assertThat(file_get_contents($actual), $constraint, $message); } -} -name = $name; - $this->value = $value; + static::assertFileExists($expected, $message); + static::assertFileExists($actual, $message); + $constraint = new LogicalNot(new IsEqualCanonicalizing(file_get_contents($expected))); + static::assertThat(file_get_contents($actual), $constraint, $message); } - public function __toString() : string + /** + * Asserts that the contents of one file is not equal to the contents of another + * file (ignoring case). + * + * @throws ExpectationFailedException + */ + final public static function assertFileNotEqualsIgnoringCase(string $expected, string $actual, string $message = ''): void { - if ($this->value instanceof DoctrineTagValueNode) { - return (string) $this->value; - } - return trim("{$this->name} {$this->value}"); + static::assertFileExists($expected, $message); + static::assertFileExists($actual, $message); + $constraint = new LogicalNot(new IsEqualIgnoringCase(file_get_contents($expected))); + static::assertThat(file_get_contents($actual), $constraint, $message); } -} -text = $text; + static::assertFileExists($expectedFile, $message); + $constraint = new IsEqual(file_get_contents($expectedFile)); + static::assertThat($actualString, $constraint, $message); } - public function __toString() : string + /** + * Asserts that the contents of a string is equal + * to the contents of a file (canonicalizing). + * + * @throws ExpectationFailedException + */ + final public static function assertStringEqualsFileCanonicalizing(string $expectedFile, string $actualString, string $message = ''): void { - return $this->text; + static::assertFileExists($expectedFile, $message); + $constraint = new IsEqualCanonicalizing(file_get_contents($expectedFile)); + static::assertThat($actualString, $constraint, $message); } -} -type = $type; - $this->propertyName = $propertyName; - $this->description = $description; + static::assertFileExists($expectedFile, $message); + $constraint = new IsEqualIgnoringCase(file_get_contents($expectedFile)); + static::assertThat($actualString, $constraint, $message); } - public function __toString() : string + /** + * Asserts that the contents of a string is not equal + * to the contents of a file. + * + * @throws ExpectationFailedException + */ + final public static function assertStringNotEqualsFile(string $expectedFile, string $actualString, string $message = ''): void { - return trim("{$this->type} {$this->propertyName} {$this->description}"); + static::assertFileExists($expectedFile, $message); + $constraint = new LogicalNot(new IsEqual(file_get_contents($expectedFile))); + static::assertThat($actualString, $constraint, $message); } -} -type = $type; - $this->description = $description; + static::assertFileExists($expectedFile, $message); + $constraint = new LogicalNot(new IsEqualCanonicalizing(file_get_contents($expectedFile))); + static::assertThat($actualString, $constraint, $message); } - public function __toString() : string + /** + * Asserts that the contents of a string is not equal + * to the contents of a file (ignoring case). + * + * @throws ExpectationFailedException + */ + final public static function assertStringNotEqualsFileIgnoringCase(string $expectedFile, string $actualString, string $message = ''): void { - return trim("{$this->type} {$this->description}"); + static::assertFileExists($expectedFile, $message); + $constraint = new LogicalNot(new IsEqualIgnoringCase(file_get_contents($expectedFile))); + static::assertThat($actualString, $constraint, $message); } -} -type = $type; - $this->description = $description; + static::assertThat($filename, new IsReadable(), $message); } - public function __toString() : string + /** + * Asserts that a file/dir exists and is not readable. + * + * @throws ExpectationFailedException + */ + final public static function assertIsNotReadable(string $filename, string $message = ''): void { - return trim("{$this->type} {$this->description}"); + static::assertThat($filename, new LogicalNot(new IsReadable()), $message); } -} -type = $type; - $this->description = $description; + static::assertThat($filename, new IsWritable(), $message); } - public function __toString() : string + /** + * Asserts that a file/dir exists and is not writable. + * + * @throws ExpectationFailedException + */ + final public static function assertIsNotWritable(string $filename, string $message = ''): void { - return trim("{$this->type} {$this->description}"); + static::assertThat($filename, new LogicalNot(new IsWritable()), $message); } -} -type = $type; - $this->description = $description; + static::assertThat($directory, new DirectoryExists(), $message); } - public function __toString() : string + /** + * Asserts that a directory does not exist. + * + * @throws ExpectationFailedException + */ + final public static function assertDirectoryDoesNotExist(string $directory, string $message = ''): void { - return trim($this->type . ' ' . $this->description); + static::assertThat($directory, new LogicalNot(new DirectoryExists()), $message); } -} -name = $name; - $this->bound = $bound; - $this->default = $default; - $this->description = $description; + self::assertDirectoryExists($directory, $message); + self::assertIsReadable($directory, $message); } - public function __toString() : string + /** + * Asserts that a directory exists and is not readable. + * + * @throws ExpectationFailedException + */ + final public static function assertDirectoryIsNotReadable(string $directory, string $message = ''): void { - $bound = $this->bound !== null ? " of {$this->bound}" : ''; - $default = $this->default !== null ? " = {$this->default}" : ''; - return trim("{$this->name}{$bound}{$default} {$this->description}"); + self::assertDirectoryExists($directory, $message); + self::assertIsNotReadable($directory, $message); } -} -type = $type; - $this->description = $description; + self::assertDirectoryExists($directory, $message); + self::assertIsWritable($directory, $message); } - public function __toString() : string + /** + * Asserts that a directory exists and is not writable. + * + * @throws ExpectationFailedException + */ + final public static function assertDirectoryIsNotWritable(string $directory, string $message = ''): void { - return trim("{$this->type} {$this->description}"); + self::assertDirectoryExists($directory, $message); + self::assertIsNotWritable($directory, $message); } -} -importedAlias = $importedAlias; - $this->importedFrom = $importedFrom; - $this->importedAs = $importedAs; + static::assertThat($filename, new FileExists(), $message); } - public function __toString() : string + /** + * Asserts that a file does not exist. + * + * @throws ExpectationFailedException + */ + final public static function assertFileDoesNotExist(string $filename, string $message = ''): void { - return trim("{$this->importedAlias} from {$this->importedFrom}" . ($this->importedAs !== null ? " as {$this->importedAs}" : '')); + static::assertThat($filename, new LogicalNot(new FileExists()), $message); } -} -alias = $alias; - $this->type = $type; + self::assertFileExists($file, $message); + self::assertIsReadable($file, $message); } - public function __toString() : string + /** + * Asserts that a file exists and is not readable. + * + * @throws ExpectationFailedException + */ + final public static function assertFileIsNotReadable(string $file, string $message = ''): void { - return trim("{$this->alias} {$this->type}"); - } -} -isReference = $isReference; - $this->isVariadic = $isVariadic; - $this->parameterName = $parameterName; - $this->description = $description; + self::assertFileExists($file, $message); + self::assertIsNotReadable($file, $message); } - public function __toString() : string + /** + * Asserts that a file exists and is writable. + * + * @throws ExpectationFailedException + */ + final public static function assertFileIsWritable(string $file, string $message = ''): void { - $reference = $this->isReference ? '&' : ''; - $variadic = $this->isVariadic ? '...' : ''; - return trim("{$reference}{$variadic}{$this->parameterName} {$this->description}"); + self::assertFileExists($file, $message); + self::assertIsWritable($file, $message); } -} -type = $type; - $this->description = $description; + self::assertFileExists($file, $message); + self::assertIsNotWritable($file, $message); } - public function __toString() : string + /** + * Asserts that a condition is true. + * + * @throws ExpectationFailedException + * + * @psalm-assert true $condition + */ + final public static function assertTrue(mixed $condition, string $message = ''): void { - return trim("{$this->type} {$this->description}"); + static::assertThat($condition, static::isTrue(), $message); } -} -type = $type; - $this->variableName = $variableName; - $this->description = $description; + static::assertThat($condition, static::logicalNot(static::isTrue()), $message); } - public function __toString() : string + /** + * Asserts that a condition is false. + * + * @throws ExpectationFailedException + * + * @psalm-assert false $condition + */ + final public static function assertFalse(mixed $condition, string $message = ''): void { - return trim("{$this->type} " . trim("{$this->variableName} {$this->description}")); + static::assertThat($condition, static::isFalse(), $message); } -} -keyName = $keyName; - $this->optional = $optional; - $this->valueType = $valueType; + static::assertThat($condition, static::logicalNot(static::isFalse()), $message); } - public function __toString() : string + /** + * Asserts that a variable is null. + * + * @throws ExpectationFailedException + * + * @psalm-assert null $actual + */ + final public static function assertNull(mixed $actual, string $message = ''): void { - if ($this->keyName !== null) { - return sprintf('%s%s: %s', (string) $this->keyName, $this->optional ? '?' : '', (string) $this->valueType); - } - return (string) $this->valueType; + static::assertThat($actual, static::isNull(), $message); } -} -items = $items; - $this->sealed = $sealed; - $this->kind = $kind; + static::assertThat($actual, static::logicalNot(static::isNull()), $message); } - public function __toString() : string + /** + * Asserts that a variable is finite. + * + * @throws ExpectationFailedException + */ + final public static function assertFinite(mixed $actual, string $message = ''): void { - $items = $this->items; - if (!$this->sealed) { - $items[] = '...'; - } - return $this->kind . '{' . implode(', ', $items) . '}'; + static::assertThat($actual, static::isFinite(), $message); } -} -type = $type; + static::assertThat($actual, static::isInfinite(), $message); } - public function __toString() : string + /** + * Asserts that a variable is nan. + * + * @throws ExpectationFailedException + */ + final public static function assertNan(mixed $actual, string $message = ''): void { - if ($this->type instanceof CallableTypeNode || $this->type instanceof ConstTypeNode || $this->type instanceof NullableTypeNode) { - return '(' . $this->type . ')[]'; - } - return $this->type . '[]'; + static::assertThat($actual, static::isNan(), $message); } -} -identifier = $identifier; - $this->parameters = $parameters; - $this->returnType = $returnType; - $this->templateTypes = $templateTypes; + static::assertThat($object, new ObjectHasProperty($propertyName), $message); } - public function __toString() : string + /** + * Asserts that an object does not have a specified property. + * + * @throws ExpectationFailedException + */ + final public static function assertObjectNotHasProperty(string $propertyName, object $object, string $message = ''): void { - $returnType = $this->returnType; - if ($returnType instanceof self) { - $returnType = "({$returnType})"; - } - $template = $this->templateTypes !== [] ? '<' . implode(', ', $this->templateTypes) . '>' : ''; - $parameters = implode(', ', $this->parameters); - return "{$this->identifier}{$template}({$parameters}): {$returnType}"; + static::assertThat($object, new LogicalNot(new ObjectHasProperty($propertyName)), $message); } -} -type = $type; - $this->isReference = $isReference; - $this->isVariadic = $isVariadic; - $this->parameterName = $parameterName; - $this->isOptional = $isOptional; + static::assertThat($actual, new IsIdentical($expected), $message); } - public function __toString() : string + /** + * 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 + */ + final public static function assertNotSame(mixed $expected, mixed $actual, string $message = ''): void { - $type = "{$this->type} "; - $isReference = $this->isReference ? '&' : ''; - $isVariadic = $this->isVariadic ? '...' : ''; - $isOptional = $this->isOptional ? '=' : ''; - return trim("{$type}{$isReference}{$isVariadic}{$this->parameterName}") . $isOptional; - } -} -parameterName = $parameterName; - $this->targetType = $targetType; - $this->if = $if; - $this->else = $else; - $this->negated = $negated; + if (is_bool($expected) && is_bool($actual)) { + static::assertNotEquals($expected, $actual, $message); + } + static::assertThat($actual, new LogicalNot(new IsIdentical($expected)), $message); } - public function __toString() : string + /** + * Asserts that a variable is of a given type. + * + * @throws Exception + * @throws ExpectationFailedException + * @throws UnknownClassOrInterfaceException + * + * @psalm-template ExpectedType of object + * + * @psalm-param class-string $expected + * + * @psalm-assert =ExpectedType $actual + */ + final public static function assertInstanceOf(string $expected, mixed $actual, string $message = ''): void { - return sprintf('(%s %s %s ? %s : %s)', $this->parameterName, $this->negated ? 'is not' : 'is', $this->targetType, $this->if, $this->else); - } -} -subjectType = $subjectType; - $this->targetType = $targetType; - $this->if = $if; - $this->else = $else; - $this->negated = $negated; + if (!class_exists($expected) && !interface_exists($expected)) { + throw new \PHPUnit\Framework\UnknownClassOrInterfaceException($expected); + } + static::assertThat($actual, new IsInstanceOf($expected), $message); } - public function __toString() : string + /** + * Asserts that a variable is not of a given type. + * + * @throws Exception + * @throws ExpectationFailedException + * + * @psalm-template ExpectedType of object + * + * @psalm-param class-string $expected + * + * @psalm-assert !ExpectedType $actual + */ + final public static function assertNotInstanceOf(string $expected, mixed $actual, string $message = ''): void { - return sprintf('(%s %s %s ? %s : %s)', $this->subjectType, $this->negated ? 'is not' : 'is', $this->targetType, $this->if, $this->else); + if (!class_exists($expected) && !interface_exists($expected)) { + throw new \PHPUnit\Framework\UnknownClassOrInterfaceException($expected); + } + static::assertThat($actual, new LogicalNot(new IsInstanceOf($expected)), $message); } -} -constExpr = $constExpr; + static::assertThat($actual, new IsType(IsType::TYPE_ARRAY), $message); } - public function __toString() : string + /** + * Asserts that a variable is of type bool. + * + * @throws Exception + * @throws ExpectationFailedException + * + * @psalm-assert bool $actual + */ + final public static function assertIsBool(mixed $actual, string $message = ''): void { - return $this->constExpr->__toString(); + static::assertThat($actual, new IsType(IsType::TYPE_BOOL), $message); } -} -type = $type; - $this->genericTypes = $genericTypes; - $this->variances = $variances; - } - public function __toString() : string - { - $genericTypes = []; - foreach ($this->genericTypes as $index => $type) { - $variance = $this->variances[$index] ?? self::VARIANCE_INVARIANT; - if ($variance === self::VARIANCE_INVARIANT) { - $genericTypes[] = (string) $type; - } elseif ($variance === self::VARIANCE_BIVARIANT) { - $genericTypes[] = '*'; - } else { - $genericTypes[] = sprintf('%s %s', $variance, $type); - } - } - return $this->type . '<' . implode(', ', $genericTypes) . '>'; + static::assertThat($actual, new IsType(IsType::TYPE_FLOAT), $message); } -} -name = $name; + static::assertThat($actual, new IsType(IsType::TYPE_INT), $message); } - public function __toString() : string + /** + * Asserts that a variable is of type numeric. + * + * @throws Exception + * @throws ExpectationFailedException + * + * @psalm-assert numeric $actual + */ + final public static function assertIsNumeric(mixed $actual, string $message = ''): void { - return $this->name; + static::assertThat($actual, new IsType(IsType::TYPE_NUMERIC), $message); } -} -types = $types; + static::assertThat($actual, new IsType(IsType::TYPE_OBJECT), $message); } - public function __toString() : string + /** + * Asserts that a variable is of type resource. + * + * @throws Exception + * @throws ExpectationFailedException + * + * @psalm-assert resource $actual + */ + final public static function assertIsResource(mixed $actual, string $message = ''): void { - return '(' . implode(' & ', array_map(static function (TypeNode $type) : string { - if ($type instanceof NullableTypeNode) { - return '(' . $type . ')'; - } - return (string) $type; - }, $this->types)) . ')'; + static::assertThat($actual, new IsType(IsType::TYPE_RESOURCE), $message); } -} -exceptionArgs = [$exception->getCurrentTokenValue(), $exception->getCurrentTokenType(), $exception->getCurrentOffset(), $exception->getExpectedTokenType(), $exception->getExpectedTokenValue(), $exception->getCurrentTokenLine()]; + static::assertThat($actual, new IsType(IsType::TYPE_CLOSED_RESOURCE), $message); } - public function getException() : ParserException + /** + * Asserts that a variable is of type string. + * + * @throws Exception + * @throws ExpectationFailedException + * + * @psalm-assert string $actual + */ + final public static function assertIsString(mixed $actual, string $message = ''): void { - return new ParserException(...$this->exceptionArgs); + static::assertThat($actual, new IsType(IsType::TYPE_STRING), $message); } - public function __toString() : string + /** + * Asserts that a variable is of type scalar. + * + * @throws Exception + * @throws ExpectationFailedException + * + * @psalm-assert scalar $actual + */ + final public static function assertIsScalar(mixed $actual, string $message = ''): void { - return '*Invalid type*'; + static::assertThat($actual, new IsType(IsType::TYPE_SCALAR), $message); } -} -type = $type; + static::assertThat($actual, new IsType(IsType::TYPE_CALLABLE), $message); } - public function __toString() : string + /** + * Asserts that a variable is of type iterable. + * + * @throws Exception + * @throws ExpectationFailedException + * + * @psalm-assert iterable $actual + */ + final public static function assertIsIterable(mixed $actual, string $message = ''): void { - return '?' . $this->type; + static::assertThat($actual, new IsType(IsType::TYPE_ITERABLE), $message); } -} -keyName = $keyName; - $this->optional = $optional; - $this->valueType = $valueType; + static::assertThat($actual, new LogicalNot(new IsType(IsType::TYPE_ARRAY)), $message); } - public function __toString() : string + /** + * Asserts that a variable is not of type bool. + * + * @throws Exception + * @throws ExpectationFailedException + * + * @psalm-assert !bool $actual + */ + final public static function assertIsNotBool(mixed $actual, string $message = ''): void { - if ($this->keyName !== null) { - return sprintf('%s%s: %s', (string) $this->keyName, $this->optional ? '?' : '', (string) $this->valueType); - } - return (string) $this->valueType; + static::assertThat($actual, new LogicalNot(new IsType(IsType::TYPE_BOOL)), $message); } -} -items = $items; + static::assertThat($actual, new LogicalNot(new IsType(IsType::TYPE_FLOAT)), $message); } - public function __toString() : string + /** + * Asserts that a variable is not of type int. + * + * @throws Exception + * @throws ExpectationFailedException + * + * @psalm-assert !int $actual + */ + final public static function assertIsNotInt(mixed $actual, string $message = ''): void { - $items = $this->items; - return 'object{' . implode(', ', $items) . '}'; + static::assertThat($actual, new LogicalNot(new IsType(IsType::TYPE_INT)), $message); } -} -type = $type; - $this->offset = $offset; + static::assertThat($actual, new LogicalNot(new IsType(IsType::TYPE_NUMERIC)), $message); } - public function __toString() : string + /** + * Asserts that a variable is not of type object. + * + * @throws Exception + * @throws ExpectationFailedException + * + * @psalm-assert !object $actual + */ + final public static function assertIsNotObject(mixed $actual, string $message = ''): void { - if ($this->type instanceof CallableTypeNode || $this->type instanceof ConstTypeNode || $this->type instanceof NullableTypeNode) { - return '(' . $this->type . ')[' . $this->offset . ']'; - } - return $this->type . '[' . $this->offset . ']'; + static::assertThat($actual, new LogicalNot(new IsType(IsType::TYPE_OBJECT)), $message); } -} -types = $types; + static::assertThat($actual, new LogicalNot(new IsType(IsType::TYPE_CLOSED_RESOURCE)), $message); } - public function __toString() : string + /** + * Asserts that a variable is not of type string. + * + * @throws Exception + * @throws ExpectationFailedException + * + * @psalm-assert !string $actual + */ + final public static function assertIsNotString(mixed $actual, string $message = ''): void { - return '(' . implode(' | ', array_map(static function (TypeNode $type) : string { - if ($type instanceof NullableTypeNode) { - return '(' . $type . ')'; - } - return (string) $type; - }, $this->types)) . ')'; - } -} -MIT License - -Copyright (c) 2016 Ondřej Mirtes - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. '\'&\'', self::TOKEN_UNION => '\'|\'', self::TOKEN_INTERSECTION => '\'&\'', self::TOKEN_NULLABLE => '\'?\'', self::TOKEN_NEGATED => '\'!\'', self::TOKEN_OPEN_PARENTHESES => '\'(\'', self::TOKEN_CLOSE_PARENTHESES => '\')\'', self::TOKEN_OPEN_ANGLE_BRACKET => '\'<\'', self::TOKEN_CLOSE_ANGLE_BRACKET => '\'>\'', self::TOKEN_OPEN_SQUARE_BRACKET => '\'[\'', self::TOKEN_CLOSE_SQUARE_BRACKET => '\']\'', self::TOKEN_OPEN_CURLY_BRACKET => '\'{\'', self::TOKEN_CLOSE_CURLY_BRACKET => '\'}\'', self::TOKEN_COMMA => '\',\'', self::TOKEN_COLON => '\':\'', self::TOKEN_VARIADIC => '\'...\'', self::TOKEN_DOUBLE_COLON => '\'::\'', self::TOKEN_DOUBLE_ARROW => '\'=>\'', self::TOKEN_ARROW => '\'->\'', self::TOKEN_EQUAL => '\'=\'', self::TOKEN_OPEN_PHPDOC => '\'/**\'', self::TOKEN_CLOSE_PHPDOC => '\'*/\'', self::TOKEN_PHPDOC_TAG => 'TOKEN_PHPDOC_TAG', self::TOKEN_DOCTRINE_TAG => 'TOKEN_DOCTRINE_TAG', self::TOKEN_PHPDOC_EOL => 'TOKEN_PHPDOC_EOL', self::TOKEN_FLOAT => 'TOKEN_FLOAT', self::TOKEN_INTEGER => 'TOKEN_INTEGER', self::TOKEN_SINGLE_QUOTED_STRING => 'TOKEN_SINGLE_QUOTED_STRING', self::TOKEN_DOUBLE_QUOTED_STRING => 'TOKEN_DOUBLE_QUOTED_STRING', self::TOKEN_DOCTRINE_ANNOTATION_STRING => 'TOKEN_DOCTRINE_ANNOTATION_STRING', self::TOKEN_IDENTIFIER => 'type', self::TOKEN_THIS_VARIABLE => '\'$this\'', self::TOKEN_VARIABLE => 'variable', self::TOKEN_HORIZONTAL_WS => 'TOKEN_HORIZONTAL_WS', self::TOKEN_OTHER => 'TOKEN_OTHER', self::TOKEN_END => 'TOKEN_END', self::TOKEN_WILDCARD => '*']; - public const VALUE_OFFSET = 0; - public const TYPE_OFFSET = 1; - public const LINE_OFFSET = 2; - /** @var bool */ - private $parseDoctrineAnnotations; - /** @var string|null */ - private $regexp; - public function __construct(bool $parseDoctrineAnnotations = \false) - { - $this->parseDoctrineAnnotations = $parseDoctrineAnnotations; - } - /** - * @return list - */ - public function tokenize(string $s) : array - { - if ($this->regexp === null) { - $this->regexp = $this->generateRegexp(); - } - preg_match_all($this->regexp, $s, $matches, PREG_SET_ORDER); - $tokens = []; - $line = 1; - foreach ($matches as $match) { - $type = (int) $match['MARK']; - $tokens[] = [$match[0], $type, $line]; - if ($type !== self::TOKEN_PHPDOC_EOL) { - continue; - } - $line++; - } - $tokens[] = ['', self::TOKEN_END, $line]; - return $tokens; - } - private function generateRegexp() : string - { - $patterns = [ - self::TOKEN_HORIZONTAL_WS => '[\\x09\\x20]++', - self::TOKEN_IDENTIFIER => '(?:[\\\\]?+[a-z_\\x80-\\xFF][0-9a-z_\\x80-\\xFF-]*+)++', - self::TOKEN_THIS_VARIABLE => '\\$this(?![0-9a-z_\\x80-\\xFF])', - self::TOKEN_VARIABLE => '\\$[a-z_\\x80-\\xFF][0-9a-z_\\x80-\\xFF]*+', - // '&' followed by TOKEN_VARIADIC, TOKEN_VARIABLE, TOKEN_EQUAL, TOKEN_EQUAL or TOKEN_CLOSE_PARENTHESES - self::TOKEN_REFERENCE => '&(?=\\s*+(?:[.,=)]|(?:\\$(?!this(?![0-9a-z_\\x80-\\xFF])))))', - self::TOKEN_UNION => '\\|', - self::TOKEN_INTERSECTION => '&', - self::TOKEN_NULLABLE => '\\?', - self::TOKEN_NEGATED => '!', - self::TOKEN_OPEN_PARENTHESES => '\\(', - self::TOKEN_CLOSE_PARENTHESES => '\\)', - self::TOKEN_OPEN_ANGLE_BRACKET => '<', - self::TOKEN_CLOSE_ANGLE_BRACKET => '>', - self::TOKEN_OPEN_SQUARE_BRACKET => '\\[', - self::TOKEN_CLOSE_SQUARE_BRACKET => '\\]', - self::TOKEN_OPEN_CURLY_BRACKET => '\\{', - self::TOKEN_CLOSE_CURLY_BRACKET => '\\}', - self::TOKEN_COMMA => ',', - self::TOKEN_VARIADIC => '\\.\\.\\.', - self::TOKEN_DOUBLE_COLON => '::', - self::TOKEN_DOUBLE_ARROW => '=>', - self::TOKEN_ARROW => '->', - self::TOKEN_EQUAL => '=', - self::TOKEN_COLON => ':', - self::TOKEN_OPEN_PHPDOC => '/\\*\\*(?=\\s)\\x20?+', - self::TOKEN_CLOSE_PHPDOC => '\\*/', - self::TOKEN_PHPDOC_TAG => '@(?:[a-z][a-z0-9-\\\\]+:)?[a-z][a-z0-9-\\\\]*+', - self::TOKEN_PHPDOC_EOL => '\\r?+\\n[\\x09\\x20]*+(?:\\*(?!/)\\x20?+)?', - self::TOKEN_FLOAT => '[+\\-]?(?:(?:[0-9]++(_[0-9]++)*\\.[0-9]*+(_[0-9]++)*(?:e[+\\-]?[0-9]++(_[0-9]++)*)?)|(?:[0-9]*+(_[0-9]++)*\\.[0-9]++(_[0-9]++)*(?:e[+\\-]?[0-9]++(_[0-9]++)*)?)|(?:[0-9]++(_[0-9]++)*e[+\\-]?[0-9]++(_[0-9]++)*))', - self::TOKEN_INTEGER => '[+\\-]?(?:(?:0b[0-1]++(_[0-1]++)*)|(?:0o[0-7]++(_[0-7]++)*)|(?:0x[0-9a-f]++(_[0-9a-f]++)*)|(?:[0-9]++(_[0-9]++)*))', - self::TOKEN_SINGLE_QUOTED_STRING => '\'(?:\\\\[^\\r\\n]|[^\'\\r\\n\\\\])*+\'', - self::TOKEN_DOUBLE_QUOTED_STRING => '"(?:\\\\[^\\r\\n]|[^"\\r\\n\\\\])*+"', - self::TOKEN_WILDCARD => '\\*', - ]; - if ($this->parseDoctrineAnnotations) { - $patterns[self::TOKEN_DOCTRINE_TAG] = '@[a-z_\\\\][a-z0-9_\\:\\\\]*[a-z_][a-z0-9_]*'; - $patterns[self::TOKEN_DOCTRINE_ANNOTATION_STRING] = '"(?:""|[^"])*+"'; - } - // anything but TOKEN_CLOSE_PHPDOC or TOKEN_HORIZONTAL_WS or TOKEN_EOL - $patterns[self::TOKEN_OTHER] = '(?:(?!\\*/)[^\\s])++'; - foreach ($patterns as $type => &$pattern) { - $pattern = '(?:' . $pattern . ')(*MARK:' . $type . ')'; - } - return '~' . implode('|', $patterns) . '~Asi'; + static::assertThat($actual, new LogicalNot(new IsType(IsType::TYPE_STRING)), $message); } -} -unescapeStrings = $unescapeStrings; - $this->quoteAwareConstExprString = $quoteAwareConstExprString; - $this->useLinesAttributes = $usedAttributes['lines'] ?? \false; - $this->useIndexAttributes = $usedAttributes['indexes'] ?? \false; - $this->parseDoctrineStrings = \false; + static::assertThat($actual, new LogicalNot(new IsType(IsType::TYPE_SCALAR)), $message); } /** - * @internal + * Asserts that a variable is not of type callable. + * + * @throws Exception + * @throws ExpectationFailedException + * + * @psalm-assert !callable $actual */ - public function toDoctrine() : self + final public static function assertIsNotCallable(mixed $actual, string $message = ''): void { - $self = new self($this->unescapeStrings, $this->quoteAwareConstExprString, ['lines' => $this->useLinesAttributes, 'indexes' => $this->useIndexAttributes]); - $self->parseDoctrineStrings = \true; - return $self; + static::assertThat($actual, new LogicalNot(new IsType(IsType::TYPE_CALLABLE)), $message); } - public function parse(TokenIterator $tokens, bool $trimStrings = \false) : Ast\ConstExpr\ConstExprNode + /** + * Asserts that a variable is not of type iterable. + * + * @throws Exception + * @throws ExpectationFailedException + * + * @psalm-assert !iterable $actual + */ + final public static function assertIsNotIterable(mixed $actual, string $message = ''): void { - $startLine = $tokens->currentTokenLine(); - $startIndex = $tokens->currentTokenIndex(); - if ($tokens->isCurrentTokenType(Lexer::TOKEN_FLOAT)) { - $value = $tokens->currentTokenValue(); - $tokens->next(); - return $this->enrichWithAttributes($tokens, new Ast\ConstExpr\ConstExprFloatNode(str_replace('_', '', $value)), $startLine, $startIndex); - } - if ($tokens->isCurrentTokenType(Lexer::TOKEN_INTEGER)) { - $value = $tokens->currentTokenValue(); - $tokens->next(); - return $this->enrichWithAttributes($tokens, new Ast\ConstExpr\ConstExprIntegerNode(str_replace('_', '', $value)), $startLine, $startIndex); - } - if ($this->parseDoctrineStrings && $tokens->isCurrentTokenType(Lexer::TOKEN_DOCTRINE_ANNOTATION_STRING)) { - $value = $tokens->currentTokenValue(); - $tokens->next(); - return $this->enrichWithAttributes($tokens, new Ast\ConstExpr\DoctrineConstExprStringNode(Ast\ConstExpr\DoctrineConstExprStringNode::unescape($value)), $startLine, $startIndex); - } - if ($tokens->isCurrentTokenType(Lexer::TOKEN_SINGLE_QUOTED_STRING, Lexer::TOKEN_DOUBLE_QUOTED_STRING)) { - if ($this->parseDoctrineStrings) { - if ($tokens->isCurrentTokenType(Lexer::TOKEN_SINGLE_QUOTED_STRING)) { - throw new ParserException($tokens->currentTokenValue(), $tokens->currentTokenType(), $tokens->currentTokenOffset(), Lexer::TOKEN_DOUBLE_QUOTED_STRING, null, $tokens->currentTokenLine()); - } - $value = $tokens->currentTokenValue(); - $tokens->next(); - return $this->enrichWithAttributes($tokens, $this->parseDoctrineString($value, $tokens), $startLine, $startIndex); - } - $value = $tokens->currentTokenValue(); - $type = $tokens->currentTokenType(); - if ($trimStrings) { - if ($this->unescapeStrings) { - $value = StringUnescaper::unescapeString($value); - } else { - $value = substr($value, 1, -1); - } - } - $tokens->next(); - if ($this->quoteAwareConstExprString) { - return $this->enrichWithAttributes($tokens, new Ast\ConstExpr\QuoteAwareConstExprStringNode($value, $type === Lexer::TOKEN_SINGLE_QUOTED_STRING ? Ast\ConstExpr\QuoteAwareConstExprStringNode::SINGLE_QUOTED : Ast\ConstExpr\QuoteAwareConstExprStringNode::DOUBLE_QUOTED), $startLine, $startIndex); - } - return $this->enrichWithAttributes($tokens, new Ast\ConstExpr\ConstExprStringNode($value), $startLine, $startIndex); - } elseif ($tokens->isCurrentTokenType(Lexer::TOKEN_IDENTIFIER)) { - $identifier = $tokens->currentTokenValue(); - $tokens->next(); - switch (strtolower($identifier)) { - case 'true': - return $this->enrichWithAttributes($tokens, new Ast\ConstExpr\ConstExprTrueNode(), $startLine, $startIndex); - case 'false': - return $this->enrichWithAttributes($tokens, new Ast\ConstExpr\ConstExprFalseNode(), $startLine, $startIndex); - case 'null': - return $this->enrichWithAttributes($tokens, new Ast\ConstExpr\ConstExprNullNode(), $startLine, $startIndex); - case 'array': - $tokens->consumeTokenType(Lexer::TOKEN_OPEN_PARENTHESES); - return $this->parseArray($tokens, Lexer::TOKEN_CLOSE_PARENTHESES, $startIndex); - } - if ($tokens->tryConsumeTokenType(Lexer::TOKEN_DOUBLE_COLON)) { - $classConstantName = ''; - $lastType = null; - while (\true) { - if ($lastType !== Lexer::TOKEN_IDENTIFIER && $tokens->currentTokenType() === Lexer::TOKEN_IDENTIFIER) { - $classConstantName .= $tokens->currentTokenValue(); - $tokens->consumeTokenType(Lexer::TOKEN_IDENTIFIER); - $lastType = Lexer::TOKEN_IDENTIFIER; - continue; - } - if ($lastType !== Lexer::TOKEN_WILDCARD && $tokens->tryConsumeTokenType(Lexer::TOKEN_WILDCARD)) { - $classConstantName .= '*'; - $lastType = Lexer::TOKEN_WILDCARD; - if ($tokens->getSkippedHorizontalWhiteSpaceIfAny() !== '') { - break; - } - continue; - } - if ($lastType === null) { - // trigger parse error if nothing valid was consumed - $tokens->consumeTokenType(Lexer::TOKEN_WILDCARD); - } - break; - } - return $this->enrichWithAttributes($tokens, new Ast\ConstExpr\ConstFetchNode($identifier, $classConstantName), $startLine, $startIndex); - } - return $this->enrichWithAttributes($tokens, new Ast\ConstExpr\ConstFetchNode('', $identifier), $startLine, $startIndex); - } elseif ($tokens->tryConsumeTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) { - return $this->parseArray($tokens, Lexer::TOKEN_CLOSE_SQUARE_BRACKET, $startIndex); - } - throw new ParserException($tokens->currentTokenValue(), $tokens->currentTokenType(), $tokens->currentTokenOffset(), Lexer::TOKEN_IDENTIFIER, null, $tokens->currentTokenLine()); + static::assertThat($actual, new LogicalNot(new IsType(IsType::TYPE_ITERABLE)), $message); } - private function parseArray(TokenIterator $tokens, int $endToken, int $startIndex) : Ast\ConstExpr\ConstExprArrayNode + /** + * Asserts that a string matches a given regular expression. + * + * @throws ExpectationFailedException + */ + final public static function assertMatchesRegularExpression(string $pattern, string $string, string $message = ''): void { - $items = []; - $startLine = $tokens->currentTokenLine(); - if (!$tokens->tryConsumeTokenType($endToken)) { - do { - $items[] = $this->parseArrayItem($tokens); - } while ($tokens->tryConsumeTokenType(Lexer::TOKEN_COMMA) && !$tokens->isCurrentTokenType($endToken)); - $tokens->consumeTokenType($endToken); - } - return $this->enrichWithAttributes($tokens, new Ast\ConstExpr\ConstExprArrayNode($items), $startLine, $startIndex); + static::assertThat($string, new RegularExpression($pattern), $message); } /** - * This method is supposed to be called with TokenIterator after reading TOKEN_DOUBLE_QUOTED_STRING and shifting - * to the next token. + * Asserts that a string does not match a given regular expression. + * + * @throws ExpectationFailedException */ - public function parseDoctrineString(string $text, TokenIterator $tokens) : Ast\ConstExpr\DoctrineConstExprStringNode + final public static function assertDoesNotMatchRegularExpression(string $pattern, string $string, string $message = ''): void { - // Because of how Lexer works, a valid Doctrine string - // can consist of a sequence of TOKEN_DOUBLE_QUOTED_STRING and TOKEN_DOCTRINE_ANNOTATION_STRING - while ($tokens->isCurrentTokenType(Lexer::TOKEN_DOUBLE_QUOTED_STRING, Lexer::TOKEN_DOCTRINE_ANNOTATION_STRING)) { - $text .= $tokens->currentTokenValue(); - $tokens->next(); - } - return new Ast\ConstExpr\DoctrineConstExprStringNode(Ast\ConstExpr\DoctrineConstExprStringNode::unescape($text)); + static::assertThat($string, new LogicalNot(new RegularExpression($pattern)), $message); } - private function parseArrayItem(TokenIterator $tokens) : Ast\ConstExpr\ConstExprArrayItemNode + /** + * Assert that the size of two arrays (or `Countable` or `Traversable` objects) + * is the same. + * + * @throws Exception + * @throws ExpectationFailedException + * @throws GeneratorNotSupportedException + */ + final public static function assertSameSize(Countable|iterable $expected, Countable|iterable $actual, string $message = ''): void { - $startLine = $tokens->currentTokenLine(); - $startIndex = $tokens->currentTokenIndex(); - $expr = $this->parse($tokens); - if ($tokens->tryConsumeTokenType(Lexer::TOKEN_DOUBLE_ARROW)) { - $key = $expr; - $value = $this->parse($tokens); - } else { - $key = null; - $value = $expr; + if ($expected instanceof Generator) { + throw \PHPUnit\Framework\GeneratorNotSupportedException::fromParameterName('$expected'); } - return $this->enrichWithAttributes($tokens, new Ast\ConstExpr\ConstExprArrayItemNode($key, $value), $startLine, $startIndex); + if ($actual instanceof Generator) { + throw \PHPUnit\Framework\GeneratorNotSupportedException::fromParameterName('$actual'); + } + static::assertThat($actual, new SameSize($expected), $message); } /** - * @template T of Ast\ConstExpr\ConstExprNode - * @param T $node - * @return T + * Assert that the size of two arrays (or `Countable` or `Traversable` objects) + * is not the same. + * + * @throws Exception + * @throws ExpectationFailedException + * @throws GeneratorNotSupportedException */ - private function enrichWithAttributes(TokenIterator $tokens, Ast\ConstExpr\ConstExprNode $node, int $startLine, int $startIndex) : Ast\ConstExpr\ConstExprNode + final public static function assertNotSameSize(Countable|iterable $expected, Countable|iterable $actual, string $message = ''): void { - if ($this->useLinesAttributes) { - $node->setAttribute(Ast\Attribute::START_LINE, $startLine); - $node->setAttribute(Ast\Attribute::END_LINE, $tokens->currentTokenLine()); + if ($expected instanceof Generator) { + throw \PHPUnit\Framework\GeneratorNotSupportedException::fromParameterName('$expected'); } - if ($this->useIndexAttributes) { - $node->setAttribute(Ast\Attribute::START_INDEX, $startIndex); - $node->setAttribute(Ast\Attribute::END_INDEX, $tokens->endIndexOfLastRelevantToken()); + if ($actual instanceof Generator) { + throw \PHPUnit\Framework\GeneratorNotSupportedException::fromParameterName('$actual'); } - return $node; + static::assertThat($actual, new LogicalNot(new SameSize($expected)), $message); } -} -currentTokenValue = $currentTokenValue; - $this->currentTokenType = $currentTokenType; - $this->currentOffset = $currentOffset; - $this->expectedTokenType = $expectedTokenType; - $this->expectedTokenValue = $expectedTokenValue; - $this->currentTokenLine = $currentTokenLine; - parent::__construct(sprintf('Unexpected token %s, expected %s%s at offset %d%s', $this->formatValue($currentTokenValue), Lexer::TOKEN_LABELS[$expectedTokenType], $expectedTokenValue !== null ? sprintf(' (%s)', $this->formatValue($expectedTokenValue)) : '', $currentOffset, $currentTokenLine === null ? '' : sprintf(' on line %d', $currentTokenLine))); + static::assertThat($haystack, new StringContains($needle, \false, \true), $message); } - public function getCurrentTokenValue() : string + /** + * Asserts that two strings are equal except for line endings. + * + * @throws ExpectationFailedException + */ + final public static function assertStringEqualsStringIgnoringLineEndings(string $expected, string $actual, string $message = ''): void { - return $this->currentTokenValue; + static::assertThat($actual, new StringEqualsStringIgnoringLineEndings($expected), $message); } - public function getCurrentTokenType() : int + /** + * Asserts that a string matches a given format string. + * + * @throws ExpectationFailedException + */ + final public static function assertFileMatchesFormat(string $format, string $actualFile, string $message = ''): void { - return $this->currentTokenType; + static::assertFileExists($actualFile, $message); + static::assertThat(file_get_contents($actualFile), new StringMatchesFormatDescription($format), $message); } - public function getCurrentOffset() : int + /** + * Asserts that a string matches a given format string. + * + * @throws ExpectationFailedException + */ + final public static function assertFileMatchesFormatFile(string $formatFile, string $actualFile, string $message = ''): void { - return $this->currentOffset; + static::assertFileExists($formatFile, $message); + static::assertFileExists($actualFile, $message); + static::assertThat(file_get_contents($actualFile), new StringMatchesFormatDescription(file_get_contents($formatFile)), $message); } - public function getExpectedTokenType() : int + /** + * Asserts that a string matches a given format string. + * + * @throws ExpectationFailedException + */ + final public static function assertStringMatchesFormat(string $format, string $string, string $message = ''): void { - return $this->expectedTokenType; + static::assertThat($string, new StringMatchesFormatDescription($format), $message); } - public function getExpectedTokenValue() : ?string + /** + * Asserts that a string does not match a given format string. + * + * @throws ExpectationFailedException + * + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/5472 + */ + final public static function assertStringNotMatchesFormat(string $format, string $string, string $message = ''): void { - return $this->expectedTokenValue; + static::assertThat($string, new LogicalNot(new StringMatchesFormatDescription($format)), $message); } - public function getCurrentTokenLine() : ?int + /** + * Asserts that a string matches a given format file. + * + * @throws ExpectationFailedException + */ + final public static function assertStringMatchesFormatFile(string $formatFile, string $string, string $message = ''): void { - return $this->currentTokenLine; + static::assertFileExists($formatFile, $message); + static::assertThat($string, new StringMatchesFormatDescription(file_get_contents($formatFile)), $message); } - private function formatValue(string $value) : string + /** + * Asserts that a string does not match a given format string. + * + * @throws ExpectationFailedException + * + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/5472 + */ + final public static function assertStringNotMatchesFormatFile(string $formatFile, string $string, string $message = ''): void { - $json = json_encode($value, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_INVALID_UTF8_SUBSTITUTE); - assert($json !== \false); - return $json; - } -} -typeParser = $typeParser; - $this->constantExprParser = $constantExprParser; - $this->doctrineConstantExprParser = $constantExprParser->toDoctrine(); - $this->requireWhitespaceBeforeDescription = $requireWhitespaceBeforeDescription; - $this->preserveTypeAliasesWithInvalidTypes = $preserveTypeAliasesWithInvalidTypes; - $this->parseDoctrineAnnotations = $parseDoctrineAnnotations; - $this->useLinesAttributes = $usedAttributes['lines'] ?? \false; - $this->useIndexAttributes = $usedAttributes['indexes'] ?? \false; - $this->textBetweenTagsBelongsToDescription = $textBetweenTagsBelongsToDescription; - } - public function parse(TokenIterator $tokens) : Ast\PhpDoc\PhpDocNode - { - $tokens->consumeTokenType(Lexer::TOKEN_OPEN_PHPDOC); - $tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL); - $children = []; - if ($this->parseDoctrineAnnotations) { - if (!$tokens->isCurrentTokenType(Lexer::TOKEN_CLOSE_PHPDOC)) { - $lastChild = $this->parseChild($tokens); - $children[] = $lastChild; - while (!$tokens->isCurrentTokenType(Lexer::TOKEN_CLOSE_PHPDOC)) { - if ($lastChild instanceof Ast\PhpDoc\PhpDocTagNode && ($lastChild->value instanceof Doctrine\DoctrineTagValueNode || $lastChild->value instanceof Ast\PhpDoc\GenericTagValueNode)) { - $tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL); - if ($tokens->isCurrentTokenType(Lexer::TOKEN_CLOSE_PHPDOC)) { - break; - } - $lastChild = $this->parseChild($tokens); - $children[] = $lastChild; - continue; - } - if (!$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL)) { - break; - } - if ($tokens->isCurrentTokenType(Lexer::TOKEN_CLOSE_PHPDOC)) { - break; - } - $lastChild = $this->parseChild($tokens); - $children[] = $lastChild; - } - } - } else { - if (!$tokens->isCurrentTokenType(Lexer::TOKEN_CLOSE_PHPDOC)) { - $children[] = $this->parseChild($tokens); - while ($tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL) && !$tokens->isCurrentTokenType(Lexer::TOKEN_CLOSE_PHPDOC)) { - $children[] = $this->parseChild($tokens); - } - } - } - try { - $tokens->consumeTokenType(Lexer::TOKEN_CLOSE_PHPDOC); - } catch (ParserException $e) { - $name = ''; - $startLine = $tokens->currentTokenLine(); - $startIndex = $tokens->currentTokenIndex(); - if (count($children) > 0) { - $lastChild = $children[count($children) - 1]; - if ($lastChild instanceof Ast\PhpDoc\PhpDocTagNode) { - $name = $lastChild->name; - $startLine = $tokens->currentTokenLine(); - $startIndex = $tokens->currentTokenIndex(); - } - } - $tag = new Ast\PhpDoc\PhpDocTagNode($name, $this->enrichWithAttributes($tokens, new Ast\PhpDoc\InvalidTagValueNode($e->getMessage(), $e), $startLine, $startIndex)); - $tokens->forwardToTheEnd(); - return $this->enrichWithAttributes($tokens, new Ast\PhpDoc\PhpDocNode([$this->enrichWithAttributes($tokens, $tag, $startLine, $startIndex)]), 1, 0); - } - return $this->enrichWithAttributes($tokens, new Ast\PhpDoc\PhpDocNode(array_values($children)), 1, 0); + static::assertFileExists($formatFile, $message); + static::assertThat($string, new LogicalNot(new StringMatchesFormatDescription(file_get_contents($formatFile))), $message); } - /** @phpstan-impure */ - private function parseChild(TokenIterator $tokens) : Ast\PhpDoc\PhpDocChildNode + /** + * Asserts that a string starts with a given prefix. + * + * @psalm-param non-empty-string $prefix + * + * @throws ExpectationFailedException + * @throws InvalidArgumentException + */ + final public static function assertStringStartsWith(string $prefix, string $string, string $message = ''): void { - if ($tokens->isCurrentTokenType(Lexer::TOKEN_PHPDOC_TAG)) { - $startLine = $tokens->currentTokenLine(); - $startIndex = $tokens->currentTokenIndex(); - return $this->enrichWithAttributes($tokens, $this->parseTag($tokens), $startLine, $startIndex); - } - if ($tokens->isCurrentTokenType(Lexer::TOKEN_DOCTRINE_TAG)) { - $startLine = $tokens->currentTokenLine(); - $startIndex = $tokens->currentTokenIndex(); - $tag = $tokens->currentTokenValue(); - $tokens->next(); - $tagStartLine = $tokens->currentTokenLine(); - $tagStartIndex = $tokens->currentTokenIndex(); - return $this->enrichWithAttributes($tokens, new Ast\PhpDoc\PhpDocTagNode($tag, $this->enrichWithAttributes($tokens, $this->parseDoctrineTagValue($tokens, $tag), $tagStartLine, $tagStartIndex)), $startLine, $startIndex); - } - $startLine = $tokens->currentTokenLine(); - $startIndex = $tokens->currentTokenIndex(); - $text = $this->parseText($tokens); - return $this->enrichWithAttributes($tokens, $text, $startLine, $startIndex); + static::assertThat($string, new StringStartsWith($prefix), $message); } /** - * @template T of Ast\Node - * @param T $tag - * @return T + * Asserts that a string starts not with a given prefix. + * + * @psalm-param non-empty-string $prefix + * + * @throws ExpectationFailedException + * @throws InvalidArgumentException */ - private function enrichWithAttributes(TokenIterator $tokens, Ast\Node $tag, int $startLine, int $startIndex) : Ast\Node + final public static function assertStringStartsNotWith(string $prefix, string $string, string $message = ''): void { - if ($this->useLinesAttributes) { - $tag->setAttribute(Ast\Attribute::START_LINE, $startLine); - $tag->setAttribute(Ast\Attribute::END_LINE, $tokens->currentTokenLine()); - } - if ($this->useIndexAttributes) { - $tag->setAttribute(Ast\Attribute::START_INDEX, $startIndex); - $tag->setAttribute(Ast\Attribute::END_INDEX, $tokens->endIndexOfLastRelevantToken()); - } - return $tag; + static::assertThat($string, new LogicalNot(new StringStartsWith($prefix)), $message); } - private function parseText(TokenIterator $tokens) : Ast\PhpDoc\PhpDocTextNode + /** + * @throws ExpectationFailedException + */ + final public static function assertStringContainsString(string $needle, string $haystack, string $message = ''): void { - $text = ''; - $endTokens = [Lexer::TOKEN_PHPDOC_EOL, Lexer::TOKEN_CLOSE_PHPDOC, Lexer::TOKEN_END]; - if ($this->textBetweenTagsBelongsToDescription) { - $endTokens = [Lexer::TOKEN_CLOSE_PHPDOC, Lexer::TOKEN_END]; - } - $savepoint = \false; - // if the next token is EOL, everything below is skipped and empty string is returned - while ($this->textBetweenTagsBelongsToDescription || !$tokens->isCurrentTokenType(Lexer::TOKEN_PHPDOC_EOL)) { - $tmpText = $tokens->getSkippedHorizontalWhiteSpaceIfAny() . $tokens->joinUntil(Lexer::TOKEN_PHPDOC_EOL, ...$endTokens); - $text .= $tmpText; - // stop if we're not at EOL - meaning it's the end of PHPDoc - if (!$tokens->isCurrentTokenType(Lexer::TOKEN_PHPDOC_EOL)) { - break; - } - if ($this->textBetweenTagsBelongsToDescription) { - if (!$savepoint) { - $tokens->pushSavePoint(); - $savepoint = \true; - } elseif ($tmpText !== '') { - $tokens->dropSavePoint(); - $tokens->pushSavePoint(); - } - } - $tokens->pushSavePoint(); - $tokens->next(); - // if we're at EOL, check what's next - // if next is a PHPDoc tag, EOL, or end of PHPDoc, stop - if ($tokens->isCurrentTokenType(Lexer::TOKEN_PHPDOC_TAG, Lexer::TOKEN_DOCTRINE_TAG, ...$endTokens)) { - $tokens->rollback(); - break; - } - // otherwise if the next is text, continue building the description string - $tokens->dropSavePoint(); - $text .= $tokens->getDetectedNewline() ?? "\n"; - } - if ($savepoint) { - $tokens->rollback(); - $text = rtrim($text, $tokens->getDetectedNewline() ?? "\n"); - } - return new Ast\PhpDoc\PhpDocTextNode(trim($text, " \t")); + $constraint = new StringContains($needle); + static::assertThat($haystack, $constraint, $message); } - private function parseOptionalDescriptionAfterDoctrineTag(TokenIterator $tokens) : string + /** + * @throws ExpectationFailedException + */ + final public static function assertStringContainsStringIgnoringCase(string $needle, string $haystack, string $message = ''): void { - $text = ''; - $endTokens = [Lexer::TOKEN_PHPDOC_EOL, Lexer::TOKEN_CLOSE_PHPDOC, Lexer::TOKEN_END]; - if ($this->textBetweenTagsBelongsToDescription) { - $endTokens = [Lexer::TOKEN_CLOSE_PHPDOC, Lexer::TOKEN_END]; - } - $savepoint = \false; - // if the next token is EOL, everything below is skipped and empty string is returned - while ($this->textBetweenTagsBelongsToDescription || !$tokens->isCurrentTokenType(Lexer::TOKEN_PHPDOC_EOL)) { - $tmpText = $tokens->getSkippedHorizontalWhiteSpaceIfAny() . $tokens->joinUntil(Lexer::TOKEN_PHPDOC_TAG, Lexer::TOKEN_DOCTRINE_TAG, Lexer::TOKEN_PHPDOC_EOL, ...$endTokens); - $text .= $tmpText; - // stop if we're not at EOL - meaning it's the end of PHPDoc - if (!$tokens->isCurrentTokenType(Lexer::TOKEN_PHPDOC_EOL)) { - if (!$tokens->isPrecededByHorizontalWhitespace()) { - return trim($text . $this->parseText($tokens)->text, " \t"); - } - if ($tokens->isCurrentTokenType(Lexer::TOKEN_PHPDOC_TAG)) { - $tokens->pushSavePoint(); - $child = $this->parseChild($tokens); - if ($child instanceof Ast\PhpDoc\PhpDocTagNode) { - if ($child->value instanceof Ast\PhpDoc\GenericTagValueNode || $child->value instanceof Doctrine\DoctrineTagValueNode) { - $tokens->rollback(); - break; - } - if ($child->value instanceof Ast\PhpDoc\InvalidTagValueNode) { - $tokens->rollback(); - $tokens->pushSavePoint(); - $tokens->next(); - if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_PARENTHESES)) { - $tokens->rollback(); - break; - } - $tokens->rollback(); - return trim($text . $this->parseText($tokens)->text, " \t"); - } - } - $tokens->rollback(); - return trim($text . $this->parseText($tokens)->text, " \t"); - } - break; - } - if ($this->textBetweenTagsBelongsToDescription) { - if (!$savepoint) { - $tokens->pushSavePoint(); - $savepoint = \true; - } elseif ($tmpText !== '') { - $tokens->dropSavePoint(); - $tokens->pushSavePoint(); - } - } - $tokens->pushSavePoint(); - $tokens->next(); - // if we're at EOL, check what's next - // if next is a PHPDoc tag, EOL, or end of PHPDoc, stop - if ($tokens->isCurrentTokenType(Lexer::TOKEN_PHPDOC_TAG, Lexer::TOKEN_DOCTRINE_TAG, ...$endTokens)) { - $tokens->rollback(); - break; - } - // otherwise if the next is text, continue building the description string - $tokens->dropSavePoint(); - $text .= $tokens->getDetectedNewline() ?? "\n"; - } - if ($savepoint) { - $tokens->rollback(); - $text = rtrim($text, $tokens->getDetectedNewline() ?? "\n"); - } - return trim($text, " \t"); + $constraint = new StringContains($needle, \true); + static::assertThat($haystack, $constraint, $message); } - public function parseTag(TokenIterator $tokens) : Ast\PhpDoc\PhpDocTagNode + /** + * @throws ExpectationFailedException + */ + final public static function assertStringNotContainsString(string $needle, string $haystack, string $message = ''): void { - $tag = $tokens->currentTokenValue(); - $tokens->next(); - $value = $this->parseTagValue($tokens, $tag); - return new Ast\PhpDoc\PhpDocTagNode($tag, $value); + $constraint = new LogicalNot(new StringContains($needle)); + static::assertThat($haystack, $constraint, $message); } - public function parseTagValue(TokenIterator $tokens, string $tag) : Ast\PhpDoc\PhpDocTagValueNode + /** + * @throws ExpectationFailedException + */ + final public static function assertStringNotContainsStringIgnoringCase(string $needle, string $haystack, string $message = ''): void { - $startLine = $tokens->currentTokenLine(); - $startIndex = $tokens->currentTokenIndex(); - try { - $tokens->pushSavePoint(); - switch ($tag) { - case '@param': - case '@phpstan-param': - case '@psalm-param': - case '@phan-param': - $tagValue = $this->parseParamTagValue($tokens); - break; - case '@param-immediately-invoked-callable': - case '@phpstan-param-immediately-invoked-callable': - $tagValue = $this->parseParamImmediatelyInvokedCallableTagValue($tokens); - break; - case '@param-later-invoked-callable': - case '@phpstan-param-later-invoked-callable': - $tagValue = $this->parseParamLaterInvokedCallableTagValue($tokens); - break; - case '@param-closure-this': - case '@phpstan-param-closure-this': - $tagValue = $this->parseParamClosureThisTagValue($tokens); - break; - case '@var': - case '@phpstan-var': - case '@psalm-var': - case '@phan-var': - $tagValue = $this->parseVarTagValue($tokens); - break; - case '@return': - case '@phpstan-return': - case '@psalm-return': - case '@phan-return': - case '@phan-real-return': - $tagValue = $this->parseReturnTagValue($tokens); - break; - case '@throws': - case '@phpstan-throws': - $tagValue = $this->parseThrowsTagValue($tokens); - break; - case '@mixin': - case '@phan-mixin': - $tagValue = $this->parseMixinTagValue($tokens); - break; - case '@psalm-require-extends': - case '@phpstan-require-extends': - $tagValue = $this->parseRequireExtendsTagValue($tokens); - break; - case '@psalm-require-implements': - case '@phpstan-require-implements': - $tagValue = $this->parseRequireImplementsTagValue($tokens); - break; - case '@deprecated': - $tagValue = $this->parseDeprecatedTagValue($tokens); - break; - case '@property': - case '@property-read': - case '@property-write': - case '@phpstan-property': - case '@phpstan-property-read': - case '@phpstan-property-write': - case '@psalm-property': - case '@psalm-property-read': - case '@psalm-property-write': - case '@phan-property': - case '@phan-property-read': - case '@phan-property-write': - $tagValue = $this->parsePropertyTagValue($tokens); - break; - case '@method': - case '@phpstan-method': - case '@psalm-method': - case '@phan-method': - $tagValue = $this->parseMethodTagValue($tokens); - break; - case '@template': - case '@phpstan-template': - case '@psalm-template': - case '@phan-template': - case '@template-covariant': - case '@phpstan-template-covariant': - case '@psalm-template-covariant': - case '@template-contravariant': - case '@phpstan-template-contravariant': - case '@psalm-template-contravariant': - $tagValue = $this->typeParser->parseTemplateTagValue($tokens, function ($tokens) { - return $this->parseOptionalDescription($tokens); - }); - break; - case '@extends': - case '@phpstan-extends': - case '@phan-extends': - case '@phan-inherits': - case '@template-extends': - $tagValue = $this->parseExtendsTagValue('@extends', $tokens); - break; - case '@implements': - case '@phpstan-implements': - case '@template-implements': - $tagValue = $this->parseExtendsTagValue('@implements', $tokens); - break; - case '@use': - case '@phpstan-use': - case '@template-use': - $tagValue = $this->parseExtendsTagValue('@use', $tokens); - break; - case '@phpstan-type': - case '@psalm-type': - case '@phan-type': - $tagValue = $this->parseTypeAliasTagValue($tokens); - break; - case '@phpstan-import-type': - case '@psalm-import-type': - $tagValue = $this->parseTypeAliasImportTagValue($tokens); - break; - case '@phpstan-assert': - case '@phpstan-assert-if-true': - case '@phpstan-assert-if-false': - case '@psalm-assert': - case '@psalm-assert-if-true': - case '@psalm-assert-if-false': - case '@phan-assert': - case '@phan-assert-if-true': - case '@phan-assert-if-false': - $tagValue = $this->parseAssertTagValue($tokens); - break; - case '@phpstan-this-out': - case '@phpstan-self-out': - case '@psalm-this-out': - case '@psalm-self-out': - $tagValue = $this->parseSelfOutTagValue($tokens); - break; - case '@param-out': - case '@phpstan-param-out': - case '@psalm-param-out': - $tagValue = $this->parseParamOutTagValue($tokens); - break; - default: - if ($this->parseDoctrineAnnotations) { - if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_PARENTHESES)) { - $tagValue = $this->parseDoctrineTagValue($tokens, $tag); - } else { - $tagValue = new Ast\PhpDoc\GenericTagValueNode($this->parseOptionalDescriptionAfterDoctrineTag($tokens)); - } - break; - } - $tagValue = new Ast\PhpDoc\GenericTagValueNode($this->parseOptionalDescription($tokens)); - break; - } - $tokens->dropSavePoint(); - } catch (ParserException $e) { - $tokens->rollback(); - $tagValue = new Ast\PhpDoc\InvalidTagValueNode($this->parseOptionalDescription($tokens), $e); - } - return $this->enrichWithAttributes($tokens, $tagValue, $startLine, $startIndex); + $constraint = new LogicalNot(new StringContains($needle, \true)); + static::assertThat($haystack, $constraint, $message); } - private function parseDoctrineTagValue(TokenIterator $tokens, string $tag) : Ast\PhpDoc\PhpDocTagValueNode + /** + * Asserts that a string ends with a given suffix. + * + * @psalm-param non-empty-string $suffix + * + * @throws ExpectationFailedException + * @throws InvalidArgumentException + */ + final public static function assertStringEndsWith(string $suffix, string $string, string $message = ''): void { - $startLine = $tokens->currentTokenLine(); - $startIndex = $tokens->currentTokenIndex(); - return new Doctrine\DoctrineTagValueNode($this->enrichWithAttributes($tokens, new Doctrine\DoctrineAnnotation($tag, $this->parseDoctrineArguments($tokens, \false)), $startLine, $startIndex), $this->parseOptionalDescriptionAfterDoctrineTag($tokens)); + static::assertThat($string, new StringEndsWith($suffix), $message); } /** - * @return list + * Asserts that a string ends not with a given suffix. + * + * @psalm-param non-empty-string $suffix + * + * @throws ExpectationFailedException + * @throws InvalidArgumentException */ - private function parseDoctrineArguments(TokenIterator $tokens, bool $deep) : array + final public static function assertStringEndsNotWith(string $suffix, string $string, string $message = ''): void { - if (!$tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_PARENTHESES)) { - return []; - } - if (!$deep) { - $tokens->addEndOfLineToSkippedTokens(); - } - $arguments = []; - try { - $tokens->consumeTokenType(Lexer::TOKEN_OPEN_PARENTHESES); - do { - if ($tokens->isCurrentTokenType(Lexer::TOKEN_CLOSE_PARENTHESES)) { - break; - } - $arguments[] = $this->parseDoctrineArgument($tokens); - } while ($tokens->tryConsumeTokenType(Lexer::TOKEN_COMMA)); - } finally { - if (!$deep) { - $tokens->removeEndOfLineFromSkippedTokens(); - } - } - $tokens->consumeTokenType(Lexer::TOKEN_CLOSE_PARENTHESES); - return $arguments; + static::assertThat($string, new LogicalNot(new StringEndsWith($suffix)), $message); } - private function parseDoctrineArgument(TokenIterator $tokens) : Doctrine\DoctrineArgument + /** + * Asserts that two XML files are equal. + * + * @throws Exception + * @throws ExpectationFailedException + * @throws XmlException + */ + final public static function assertXmlFileEqualsXmlFile(string $expectedFile, string $actualFile, string $message = ''): void { - if (!$tokens->isCurrentTokenType(Lexer::TOKEN_IDENTIFIER)) { - $startLine = $tokens->currentTokenLine(); - $startIndex = $tokens->currentTokenIndex(); - return $this->enrichWithAttributes($tokens, new Doctrine\DoctrineArgument(null, $this->parseDoctrineArgumentValue($tokens)), $startLine, $startIndex); - } - $startLine = $tokens->currentTokenLine(); - $startIndex = $tokens->currentTokenIndex(); - try { - $tokens->pushSavePoint(); - $currentValue = $tokens->currentTokenValue(); - $tokens->consumeTokenType(Lexer::TOKEN_IDENTIFIER); - $key = $this->enrichWithAttributes($tokens, new IdentifierTypeNode($currentValue), $startLine, $startIndex); - $tokens->consumeTokenType(Lexer::TOKEN_EQUAL); - $value = $this->parseDoctrineArgumentValue($tokens); - $tokens->dropSavePoint(); - return $this->enrichWithAttributes($tokens, new Doctrine\DoctrineArgument($key, $value), $startLine, $startIndex); - } catch (ParserException $e) { - $tokens->rollback(); - return $this->enrichWithAttributes($tokens, new Doctrine\DoctrineArgument(null, $this->parseDoctrineArgumentValue($tokens)), $startLine, $startIndex); - } + $expected = (new XmlLoader())->loadFile($expectedFile); + $actual = (new XmlLoader())->loadFile($actualFile); + static::assertEquals($expected, $actual, $message); } /** - * @return DoctrineValueType + * Asserts that two XML files are not equal. + * + * @throws \PHPUnit\Util\Exception + * @throws ExpectationFailedException */ - private function parseDoctrineArgumentValue(TokenIterator $tokens) + final public static function assertXmlFileNotEqualsXmlFile(string $expectedFile, string $actualFile, string $message = ''): void { - $startLine = $tokens->currentTokenLine(); - $startIndex = $tokens->currentTokenIndex(); - if ($tokens->isCurrentTokenType(Lexer::TOKEN_PHPDOC_TAG, Lexer::TOKEN_DOCTRINE_TAG)) { - $name = $tokens->currentTokenValue(); - $tokens->next(); - return $this->enrichWithAttributes($tokens, new Doctrine\DoctrineAnnotation($name, $this->parseDoctrineArguments($tokens, \true)), $startLine, $startIndex); - } - if ($tokens->tryConsumeTokenType(Lexer::TOKEN_OPEN_CURLY_BRACKET)) { - $items = []; - do { - if ($tokens->isCurrentTokenType(Lexer::TOKEN_CLOSE_CURLY_BRACKET)) { - break; - } - $items[] = $this->parseDoctrineArrayItem($tokens); - } while ($tokens->tryConsumeTokenType(Lexer::TOKEN_COMMA)); - $tokens->consumeTokenType(Lexer::TOKEN_CLOSE_CURLY_BRACKET); - return $this->enrichWithAttributes($tokens, new Doctrine\DoctrineArray($items), $startLine, $startIndex); - } - $currentTokenValue = $tokens->currentTokenValue(); - $tokens->pushSavePoint(); - // because of ConstFetchNode - if ($tokens->tryConsumeTokenType(Lexer::TOKEN_IDENTIFIER)) { - $identifier = $this->enrichWithAttributes($tokens, new Ast\Type\IdentifierTypeNode($currentTokenValue), $startLine, $startIndex); - if (!$tokens->isCurrentTokenType(Lexer::TOKEN_DOUBLE_COLON)) { - $tokens->dropSavePoint(); - return $identifier; - } - $tokens->rollback(); - // because of ConstFetchNode - } else { - $tokens->dropSavePoint(); - // because of ConstFetchNode - } - $currentTokenValue = $tokens->currentTokenValue(); - $currentTokenType = $tokens->currentTokenType(); - $currentTokenOffset = $tokens->currentTokenOffset(); - $currentTokenLine = $tokens->currentTokenLine(); - try { - $constExpr = $this->doctrineConstantExprParser->parse($tokens, \true); - if ($constExpr instanceof Ast\ConstExpr\ConstExprArrayNode) { - throw new ParserException($currentTokenValue, $currentTokenType, $currentTokenOffset, Lexer::TOKEN_IDENTIFIER, null, $currentTokenLine); - } - return $constExpr; - } catch (LogicException $e) { - throw new ParserException($currentTokenValue, $currentTokenType, $currentTokenOffset, Lexer::TOKEN_IDENTIFIER, null, $currentTokenLine); - } + $expected = (new XmlLoader())->loadFile($expectedFile); + $actual = (new XmlLoader())->loadFile($actualFile); + static::assertNotEquals($expected, $actual, $message); } - private function parseDoctrineArrayItem(TokenIterator $tokens) : Doctrine\DoctrineArrayItem + /** + * Asserts that two XML documents are equal. + * + * @throws ExpectationFailedException + * @throws XmlException + */ + final public static function assertXmlStringEqualsXmlFile(string $expectedFile, string $actualXml, string $message = ''): void { - $startLine = $tokens->currentTokenLine(); - $startIndex = $tokens->currentTokenIndex(); - try { - $tokens->pushSavePoint(); - $key = $this->parseDoctrineArrayKey($tokens); - if (!$tokens->tryConsumeTokenType(Lexer::TOKEN_EQUAL)) { - if (!$tokens->tryConsumeTokenType(Lexer::TOKEN_COLON)) { - $tokens->consumeTokenType(Lexer::TOKEN_EQUAL); - // will throw exception - } - } - $value = $this->parseDoctrineArgumentValue($tokens); - $tokens->dropSavePoint(); - return $this->enrichWithAttributes($tokens, new Doctrine\DoctrineArrayItem($key, $value), $startLine, $startIndex); - } catch (ParserException $e) { - $tokens->rollback(); - return $this->enrichWithAttributes($tokens, new Doctrine\DoctrineArrayItem(null, $this->parseDoctrineArgumentValue($tokens)), $startLine, $startIndex); - } + $expected = (new XmlLoader())->loadFile($expectedFile); + $actual = (new XmlLoader())->load($actualXml); + static::assertEquals($expected, $actual, $message); + } + /** + * Asserts that two XML documents are not equal. + * + * @throws ExpectationFailedException + * @throws XmlException + */ + final public static function assertXmlStringNotEqualsXmlFile(string $expectedFile, string $actualXml, string $message = ''): void + { + $expected = (new XmlLoader())->loadFile($expectedFile); + $actual = (new XmlLoader())->load($actualXml); + static::assertNotEquals($expected, $actual, $message); } /** - * @return ConstExprIntegerNode|ConstExprStringNode|IdentifierTypeNode|ConstFetchNode + * Asserts that two XML documents are equal. + * + * @throws ExpectationFailedException + * @throws XmlException */ - private function parseDoctrineArrayKey(TokenIterator $tokens) + final public static function assertXmlStringEqualsXmlString(string $expectedXml, string $actualXml, string $message = ''): void { - $startLine = $tokens->currentTokenLine(); - $startIndex = $tokens->currentTokenIndex(); - if ($tokens->isCurrentTokenType(Lexer::TOKEN_INTEGER)) { - $key = new Ast\ConstExpr\ConstExprIntegerNode(str_replace('_', '', $tokens->currentTokenValue())); - $tokens->next(); - } elseif ($tokens->isCurrentTokenType(Lexer::TOKEN_DOCTRINE_ANNOTATION_STRING)) { - $key = new Ast\ConstExpr\DoctrineConstExprStringNode(Ast\ConstExpr\DoctrineConstExprStringNode::unescape($tokens->currentTokenValue())); - $tokens->next(); - } elseif ($tokens->isCurrentTokenType(Lexer::TOKEN_DOUBLE_QUOTED_STRING)) { - $value = $tokens->currentTokenValue(); - $tokens->next(); - $key = $this->doctrineConstantExprParser->parseDoctrineString($value, $tokens); - } else { - $currentTokenValue = $tokens->currentTokenValue(); - $tokens->pushSavePoint(); - // because of ConstFetchNode - if (!$tokens->tryConsumeTokenType(Lexer::TOKEN_IDENTIFIER)) { - $tokens->dropSavePoint(); - throw new ParserException($tokens->currentTokenValue(), $tokens->currentTokenType(), $tokens->currentTokenOffset(), Lexer::TOKEN_IDENTIFIER, null, $tokens->currentTokenLine()); - } - if (!$tokens->isCurrentTokenType(Lexer::TOKEN_DOUBLE_COLON)) { - $tokens->dropSavePoint(); - return $this->enrichWithAttributes($tokens, new IdentifierTypeNode($currentTokenValue), $startLine, $startIndex); - } - $tokens->rollback(); - $constExpr = $this->doctrineConstantExprParser->parse($tokens, \true); - if (!$constExpr instanceof Ast\ConstExpr\ConstFetchNode) { - throw new ParserException($tokens->currentTokenValue(), $tokens->currentTokenType(), $tokens->currentTokenOffset(), Lexer::TOKEN_IDENTIFIER, null, $tokens->currentTokenLine()); - } - return $constExpr; - } - return $this->enrichWithAttributes($tokens, $key, $startLine, $startIndex); + $expected = (new XmlLoader())->load($expectedXml); + $actual = (new XmlLoader())->load($actualXml); + static::assertEquals($expected, $actual, $message); } /** - * @return Ast\PhpDoc\ParamTagValueNode|Ast\PhpDoc\TypelessParamTagValueNode + * Asserts that two XML documents are not equal. + * + * @throws ExpectationFailedException + * @throws XmlException */ - private function parseParamTagValue(TokenIterator $tokens) : Ast\PhpDoc\PhpDocTagValueNode + final public static function assertXmlStringNotEqualsXmlString(string $expectedXml, string $actualXml, string $message = ''): void { - if ($tokens->isCurrentTokenType(Lexer::TOKEN_REFERENCE, Lexer::TOKEN_VARIADIC, Lexer::TOKEN_VARIABLE)) { - $type = null; - } else { - $type = $this->typeParser->parse($tokens); - } - $isReference = $tokens->tryConsumeTokenType(Lexer::TOKEN_REFERENCE); - $isVariadic = $tokens->tryConsumeTokenType(Lexer::TOKEN_VARIADIC); - $parameterName = $this->parseRequiredVariableName($tokens); - $description = $this->parseOptionalDescription($tokens); - if ($type !== null) { - return new Ast\PhpDoc\ParamTagValueNode($type, $isVariadic, $parameterName, $description, $isReference); - } - return new Ast\PhpDoc\TypelessParamTagValueNode($isVariadic, $parameterName, $description, $isReference); + $expected = (new XmlLoader())->load($expectedXml); + $actual = (new XmlLoader())->load($actualXml); + static::assertNotEquals($expected, $actual, $message); } - private function parseParamImmediatelyInvokedCallableTagValue(TokenIterator $tokens) : Ast\PhpDoc\ParamImmediatelyInvokedCallableTagValueNode + /** + * Evaluates a PHPUnit\Framework\Constraint matcher object. + * + * @throws ExpectationFailedException + */ + final public static function assertThat(mixed $value, Constraint $constraint, string $message = ''): void { - $parameterName = $this->parseRequiredVariableName($tokens); - $description = $this->parseOptionalDescription($tokens); - return new Ast\PhpDoc\ParamImmediatelyInvokedCallableTagValueNode($parameterName, $description); + self::$count += count($constraint); + $hasFailed = \true; + try { + $constraint->evaluate($value, $message); + $hasFailed = \false; + } finally { + if ($hasFailed) { + Event\Facade::emitter()->testAssertionFailed($value, $constraint, $message); + } else { + Event\Facade::emitter()->testAssertionSucceeded($value, $constraint, $message); + } + } } - private function parseParamLaterInvokedCallableTagValue(TokenIterator $tokens) : Ast\PhpDoc\ParamLaterInvokedCallableTagValueNode + /** + * Asserts that a string is a valid JSON string. + * + * @throws ExpectationFailedException + */ + final public static function assertJson(string $actual, string $message = ''): void { - $parameterName = $this->parseRequiredVariableName($tokens); - $description = $this->parseOptionalDescription($tokens); - return new Ast\PhpDoc\ParamLaterInvokedCallableTagValueNode($parameterName, $description); + static::assertThat($actual, static::isJson(), $message); } - private function parseParamClosureThisTagValue(TokenIterator $tokens) : Ast\PhpDoc\ParamClosureThisTagValueNode + /** + * Asserts that two given JSON encoded objects or arrays are equal. + * + * @throws ExpectationFailedException + */ + final public static function assertJsonStringEqualsJsonString(string $expectedJson, string $actualJson, string $message = ''): void { - $type = $this->typeParser->parse($tokens); - $parameterName = $this->parseRequiredVariableName($tokens); - $description = $this->parseOptionalDescription($tokens); - return new Ast\PhpDoc\ParamClosureThisTagValueNode($type, $parameterName, $description); + static::assertJson($expectedJson, $message); + static::assertJson($actualJson, $message); + static::assertThat($actualJson, new JsonMatches($expectedJson), $message); } - private function parseVarTagValue(TokenIterator $tokens) : Ast\PhpDoc\VarTagValueNode + /** + * Asserts that two given JSON encoded objects or arrays are not equal. + * + * @throws ExpectationFailedException + */ + final public static function assertJsonStringNotEqualsJsonString(string $expectedJson, string $actualJson, string $message = ''): void { - $type = $this->typeParser->parse($tokens); - $variableName = $this->parseOptionalVariableName($tokens); - $description = $this->parseOptionalDescription($tokens, $variableName === ''); - return new Ast\PhpDoc\VarTagValueNode($type, $variableName, $description); + static::assertJson($expectedJson, $message); + static::assertJson($actualJson, $message); + static::assertThat($actualJson, new LogicalNot(new JsonMatches($expectedJson)), $message); } - private function parseReturnTagValue(TokenIterator $tokens) : Ast\PhpDoc\ReturnTagValueNode + /** + * Asserts that the generated JSON encoded object and the content of the given file are equal. + * + * @throws ExpectationFailedException + */ + final public static function assertJsonStringEqualsJsonFile(string $expectedFile, string $actualJson, string $message = ''): void { - $type = $this->typeParser->parse($tokens); - $description = $this->parseOptionalDescription($tokens, \true); - return new Ast\PhpDoc\ReturnTagValueNode($type, $description); + static::assertFileExists($expectedFile, $message); + $expectedJson = file_get_contents($expectedFile); + static::assertJson($expectedJson, $message); + static::assertJson($actualJson, $message); + static::assertThat($actualJson, new JsonMatches($expectedJson), $message); } - private function parseThrowsTagValue(TokenIterator $tokens) : Ast\PhpDoc\ThrowsTagValueNode + /** + * Asserts that the generated JSON encoded object and the content of the given file are not equal. + * + * @throws ExpectationFailedException + */ + final public static function assertJsonStringNotEqualsJsonFile(string $expectedFile, string $actualJson, string $message = ''): void { - $type = $this->typeParser->parse($tokens); - $description = $this->parseOptionalDescription($tokens, \true); - return new Ast\PhpDoc\ThrowsTagValueNode($type, $description); + 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); } - private function parseMixinTagValue(TokenIterator $tokens) : Ast\PhpDoc\MixinTagValueNode + /** + * Asserts that two JSON files are equal. + * + * @throws ExpectationFailedException + */ + final public static function assertJsonFileEqualsJsonFile(string $expectedFile, string $actualFile, string $message = ''): void { - $type = $this->typeParser->parse($tokens); - $description = $this->parseOptionalDescription($tokens, \true); - return new Ast\PhpDoc\MixinTagValueNode($type, $description); + 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); } - private function parseRequireExtendsTagValue(TokenIterator $tokens) : Ast\PhpDoc\RequireExtendsTagValueNode + /** + * Asserts that two JSON files are not equal. + * + * @throws ExpectationFailedException + */ + final public static function assertJsonFileNotEqualsJsonFile(string $expectedFile, string $actualFile, string $message = ''): void { - $type = $this->typeParser->parse($tokens); - $description = $this->parseOptionalDescription($tokens, \true); - return new Ast\PhpDoc\RequireExtendsTagValueNode($type, $description); + 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); } - private function parseRequireImplementsTagValue(TokenIterator $tokens) : Ast\PhpDoc\RequireImplementsTagValueNode + /** + * @throws Exception + */ + final public static function logicalAnd(mixed ...$constraints): LogicalAnd { - $type = $this->typeParser->parse($tokens); - $description = $this->parseOptionalDescription($tokens, \true); - return new Ast\PhpDoc\RequireImplementsTagValueNode($type, $description); + return LogicalAnd::fromConstraints(...$constraints); } - private function parseDeprecatedTagValue(TokenIterator $tokens) : Ast\PhpDoc\DeprecatedTagValueNode + final public static function logicalOr(mixed ...$constraints): LogicalOr { - $description = $this->parseOptionalDescription($tokens); - return new Ast\PhpDoc\DeprecatedTagValueNode($description); + return LogicalOr::fromConstraints(...$constraints); } - private function parsePropertyTagValue(TokenIterator $tokens) : Ast\PhpDoc\PropertyTagValueNode + final public static function logicalNot(Constraint $constraint): LogicalNot { - $type = $this->typeParser->parse($tokens); - $parameterName = $this->parseRequiredVariableName($tokens); - $description = $this->parseOptionalDescription($tokens); - return new Ast\PhpDoc\PropertyTagValueNode($type, $parameterName, $description); + return new LogicalNot($constraint); } - private function parseMethodTagValue(TokenIterator $tokens) : Ast\PhpDoc\MethodTagValueNode + final public static function logicalXor(mixed ...$constraints): LogicalXor { - $staticKeywordOrReturnTypeOrMethodName = $this->typeParser->parse($tokens); - if ($staticKeywordOrReturnTypeOrMethodName instanceof Ast\Type\IdentifierTypeNode && $staticKeywordOrReturnTypeOrMethodName->name === 'static') { - $isStatic = \true; - $returnTypeOrMethodName = $this->typeParser->parse($tokens); - } else { - $isStatic = \false; - $returnTypeOrMethodName = $staticKeywordOrReturnTypeOrMethodName; - } - if ($tokens->isCurrentTokenType(Lexer::TOKEN_IDENTIFIER)) { - $returnType = $returnTypeOrMethodName; - $methodName = $tokens->currentTokenValue(); - $tokens->next(); - } elseif ($returnTypeOrMethodName instanceof Ast\Type\IdentifierTypeNode) { - $returnType = $isStatic ? $staticKeywordOrReturnTypeOrMethodName : null; - $methodName = $returnTypeOrMethodName->name; - $isStatic = \false; - } else { - $tokens->consumeTokenType(Lexer::TOKEN_IDENTIFIER); - // will throw exception - exit; - } - $templateTypes = []; - if ($tokens->tryConsumeTokenType(Lexer::TOKEN_OPEN_ANGLE_BRACKET)) { - do { - $startLine = $tokens->currentTokenLine(); - $startIndex = $tokens->currentTokenIndex(); - $templateTypes[] = $this->enrichWithAttributes($tokens, $this->typeParser->parseTemplateTagValue($tokens), $startLine, $startIndex); - } while ($tokens->tryConsumeTokenType(Lexer::TOKEN_COMMA)); - $tokens->consumeTokenType(Lexer::TOKEN_CLOSE_ANGLE_BRACKET); - } - $parameters = []; - $tokens->consumeTokenType(Lexer::TOKEN_OPEN_PARENTHESES); - if (!$tokens->isCurrentTokenType(Lexer::TOKEN_CLOSE_PARENTHESES)) { - $parameters[] = $this->parseMethodTagValueParameter($tokens); - while ($tokens->tryConsumeTokenType(Lexer::TOKEN_COMMA)) { - $parameters[] = $this->parseMethodTagValueParameter($tokens); - } - } - $tokens->consumeTokenType(Lexer::TOKEN_CLOSE_PARENTHESES); - $description = $this->parseOptionalDescription($tokens); - return new Ast\PhpDoc\MethodTagValueNode($isStatic, $returnType, $methodName, $parameters, $description, $templateTypes); - } - private function parseMethodTagValueParameter(TokenIterator $tokens) : Ast\PhpDoc\MethodTagValueParameterNode - { - $startLine = $tokens->currentTokenLine(); - $startIndex = $tokens->currentTokenIndex(); - switch ($tokens->currentTokenType()) { - case Lexer::TOKEN_IDENTIFIER: - case Lexer::TOKEN_OPEN_PARENTHESES: - case Lexer::TOKEN_NULLABLE: - $parameterType = $this->typeParser->parse($tokens); - break; - default: - $parameterType = null; - } - $isReference = $tokens->tryConsumeTokenType(Lexer::TOKEN_REFERENCE); - $isVariadic = $tokens->tryConsumeTokenType(Lexer::TOKEN_VARIADIC); - $parameterName = $tokens->currentTokenValue(); - $tokens->consumeTokenType(Lexer::TOKEN_VARIABLE); - if ($tokens->tryConsumeTokenType(Lexer::TOKEN_EQUAL)) { - $defaultValue = $this->constantExprParser->parse($tokens); - } else { - $defaultValue = null; - } - return $this->enrichWithAttributes($tokens, new Ast\PhpDoc\MethodTagValueParameterNode($parameterType, $isReference, $isVariadic, $parameterName, $defaultValue), $startLine, $startIndex); - } - private function parseExtendsTagValue(string $tagName, TokenIterator $tokens) : Ast\PhpDoc\PhpDocTagValueNode - { - $startLine = $tokens->currentTokenLine(); - $startIndex = $tokens->currentTokenIndex(); - $baseType = new IdentifierTypeNode($tokens->currentTokenValue()); - $tokens->consumeTokenType(Lexer::TOKEN_IDENTIFIER); - $type = $this->typeParser->parseGeneric($tokens, $this->typeParser->enrichWithAttributes($tokens, $baseType, $startLine, $startIndex)); - $description = $this->parseOptionalDescription($tokens); - switch ($tagName) { - case '@extends': - return new Ast\PhpDoc\ExtendsTagValueNode($type, $description); - case '@implements': - return new Ast\PhpDoc\ImplementsTagValueNode($type, $description); - case '@use': - return new Ast\PhpDoc\UsesTagValueNode($type, $description); - } - throw new ShouldNotHappenException(); - } - private function parseTypeAliasTagValue(TokenIterator $tokens) : Ast\PhpDoc\TypeAliasTagValueNode - { - $alias = $tokens->currentTokenValue(); - $tokens->consumeTokenType(Lexer::TOKEN_IDENTIFIER); - // support phan-type/psalm-type syntax - $tokens->tryConsumeTokenType(Lexer::TOKEN_EQUAL); - if ($this->preserveTypeAliasesWithInvalidTypes) { - $startLine = $tokens->currentTokenLine(); - $startIndex = $tokens->currentTokenIndex(); - try { - $type = $this->typeParser->parse($tokens); - if (!$tokens->isCurrentTokenType(Lexer::TOKEN_CLOSE_PHPDOC)) { - if (!$tokens->isCurrentTokenType(Lexer::TOKEN_PHPDOC_EOL)) { - throw new ParserException($tokens->currentTokenValue(), $tokens->currentTokenType(), $tokens->currentTokenOffset(), Lexer::TOKEN_PHPDOC_EOL, null, $tokens->currentTokenLine()); - } - } - return new Ast\PhpDoc\TypeAliasTagValueNode($alias, $type); - } catch (ParserException $e) { - $this->parseOptionalDescription($tokens); - return new Ast\PhpDoc\TypeAliasTagValueNode($alias, $this->enrichWithAttributes($tokens, new Ast\Type\InvalidTypeNode($e), $startLine, $startIndex)); - } - } - $type = $this->typeParser->parse($tokens); - return new Ast\PhpDoc\TypeAliasTagValueNode($alias, $type); + return LogicalXor::fromConstraints(...$constraints); } - private function parseTypeAliasImportTagValue(TokenIterator $tokens) : Ast\PhpDoc\TypeAliasImportTagValueNode + final public static function anything(): IsAnything { - $importedAlias = $tokens->currentTokenValue(); - $tokens->consumeTokenType(Lexer::TOKEN_IDENTIFIER); - $tokens->consumeTokenValue(Lexer::TOKEN_IDENTIFIER, 'from'); - $identifierStartLine = $tokens->currentTokenLine(); - $identifierStartIndex = $tokens->currentTokenIndex(); - $importedFrom = $tokens->currentTokenValue(); - $tokens->consumeTokenType(Lexer::TOKEN_IDENTIFIER); - $importedFromType = $this->enrichWithAttributes($tokens, new IdentifierTypeNode($importedFrom), $identifierStartLine, $identifierStartIndex); - $importedAs = null; - if ($tokens->tryConsumeTokenValue('as')) { - $importedAs = $tokens->currentTokenValue(); - $tokens->consumeTokenType(Lexer::TOKEN_IDENTIFIER); - } - return new Ast\PhpDoc\TypeAliasImportTagValueNode($importedAlias, $importedFromType, $importedAs); + return new IsAnything(); } - /** - * @return Ast\PhpDoc\AssertTagValueNode|Ast\PhpDoc\AssertTagPropertyValueNode|Ast\PhpDoc\AssertTagMethodValueNode - */ - private function parseAssertTagValue(TokenIterator $tokens) : Ast\PhpDoc\PhpDocTagValueNode + final public static function isTrue(): IsTrue { - $isNegated = $tokens->tryConsumeTokenType(Lexer::TOKEN_NEGATED); - $isEquality = $tokens->tryConsumeTokenType(Lexer::TOKEN_EQUAL); - $type = $this->typeParser->parse($tokens); - $parameter = $this->parseAssertParameter($tokens); - $description = $this->parseOptionalDescription($tokens); - if (array_key_exists('method', $parameter)) { - return new Ast\PhpDoc\AssertTagMethodValueNode($type, $parameter['parameter'], $parameter['method'], $isNegated, $description, $isEquality); - } elseif (array_key_exists('property', $parameter)) { - return new Ast\PhpDoc\AssertTagPropertyValueNode($type, $parameter['parameter'], $parameter['property'], $isNegated, $description, $isEquality); - } - return new Ast\PhpDoc\AssertTagValueNode($type, $parameter['parameter'], $isNegated, $description, $isEquality); + return new IsTrue(); } /** - * @return array{parameter: string}|array{parameter: string, property: string}|array{parameter: string, method: string} + * @psalm-template CallbackInput of mixed + * + * @psalm-param callable(CallbackInput $callback): bool $callback + * + * @psalm-return Callback */ - private function parseAssertParameter(TokenIterator $tokens) : array + final public static function callback(callable $callback): Callback { - if ($tokens->isCurrentTokenType(Lexer::TOKEN_THIS_VARIABLE)) { - $parameter = '$this'; - $tokens->next(); - } else { - $parameter = $tokens->currentTokenValue(); - $tokens->consumeTokenType(Lexer::TOKEN_VARIABLE); - } - if ($tokens->isCurrentTokenType(Lexer::TOKEN_ARROW)) { - $tokens->consumeTokenType(Lexer::TOKEN_ARROW); - $propertyOrMethod = $tokens->currentTokenValue(); - $tokens->consumeTokenType(Lexer::TOKEN_IDENTIFIER); - if ($tokens->tryConsumeTokenType(Lexer::TOKEN_OPEN_PARENTHESES)) { - $tokens->consumeTokenType(Lexer::TOKEN_CLOSE_PARENTHESES); - return ['parameter' => $parameter, 'method' => $propertyOrMethod]; - } - return ['parameter' => $parameter, 'property' => $propertyOrMethod]; - } - return ['parameter' => $parameter]; + return new Callback($callback); } - private function parseSelfOutTagValue(TokenIterator $tokens) : Ast\PhpDoc\SelfOutTagValueNode + final public static function isFalse(): IsFalse { - $type = $this->typeParser->parse($tokens); - $description = $this->parseOptionalDescription($tokens); - return new Ast\PhpDoc\SelfOutTagValueNode($type, $description); + return new IsFalse(); } - private function parseParamOutTagValue(TokenIterator $tokens) : Ast\PhpDoc\ParamOutTagValueNode + final public static function isJson(): IsJson { - $type = $this->typeParser->parse($tokens); - $parameterName = $this->parseRequiredVariableName($tokens); - $description = $this->parseOptionalDescription($tokens); - return new Ast\PhpDoc\ParamOutTagValueNode($type, $parameterName, $description); + return new IsJson(); } - private function parseOptionalVariableName(TokenIterator $tokens) : string + final public static function isNull(): IsNull { - if ($tokens->isCurrentTokenType(Lexer::TOKEN_VARIABLE)) { - $parameterName = $tokens->currentTokenValue(); - $tokens->next(); - } elseif ($tokens->isCurrentTokenType(Lexer::TOKEN_THIS_VARIABLE)) { - $parameterName = '$this'; - $tokens->next(); - } else { - $parameterName = ''; - } - return $parameterName; + return new IsNull(); } - private function parseRequiredVariableName(TokenIterator $tokens) : string + final public static function isFinite(): IsFinite { - $parameterName = $tokens->currentTokenValue(); - $tokens->consumeTokenType(Lexer::TOKEN_VARIABLE); - return $parameterName; + return new IsFinite(); } - private function parseOptionalDescription(TokenIterator $tokens, bool $limitStartToken = \false) : string + final public static function isInfinite(): IsInfinite { - if ($limitStartToken) { - foreach (self::DISALLOWED_DESCRIPTION_START_TOKENS as $disallowedStartToken) { - if (!$tokens->isCurrentTokenType($disallowedStartToken)) { - continue; - } - $tokens->consumeTokenType(Lexer::TOKEN_OTHER); - // will throw exception - } - if ($this->requireWhitespaceBeforeDescription && !$tokens->isCurrentTokenType(Lexer::TOKEN_PHPDOC_EOL, Lexer::TOKEN_CLOSE_PHPDOC, Lexer::TOKEN_END) && !$tokens->isPrecededByHorizontalWhitespace()) { - $tokens->consumeTokenType(Lexer::TOKEN_HORIZONTAL_WS); - // will throw exception - } - } - return $this->parseText($tokens)->text; + return new IsInfinite(); } -} - '\\', 'n' => "\n", 'r' => "\r", 't' => "\t", 'f' => "\f", 'v' => "\v", 'e' => "\x1b"]; - public static function unescapeString(string $string) : string + final public static function isNan(): IsNan { - $quote = $string[0]; - if ($quote === '\'') { - return str_replace(['\\\\', '\\\''], ['\\', '\''], substr($string, 1, -1)); - } - return self::parseEscapeSequences(substr($string, 1, -1), '"'); + return new IsNan(); } - /** - * Implementation based on https://github.com/nikic/PHP-Parser/blob/b0edd4c41111042d43bb45c6c657b2e0db367d9e/lib/PhpParser/Node/Scalar/String_.php#L90-L130 - */ - private static function parseEscapeSequences(string $str, string $quote) : string + final public static function containsEqual(mixed $value): TraversableContainsEqual { - $str = str_replace('\\' . $quote, $quote, $str); - return preg_replace_callback('~\\\\([\\\\nrtfve]|[xX][0-9a-fA-F]{1,2}|[0-7]{1,3}|u\\{([0-9a-fA-F]+)\\})~', static function ($matches) { - $str = $matches[1]; - if (isset(self::REPLACEMENTS[$str])) { - return self::REPLACEMENTS[$str]; - } - if ($str[0] === 'x' || $str[0] === 'X') { - return chr((int) hexdec(substr($str, 1))); - } - if ($str[0] === 'u') { - return self::codePointToUtf8((int) hexdec($matches[2])); - } - return chr((int) octdec($str)); - }, $str); + return new TraversableContainsEqual($value); } - /** - * Implementation based on https://github.com/nikic/PHP-Parser/blob/b0edd4c41111042d43bb45c6c657b2e0db367d9e/lib/PhpParser/Node/Scalar/String_.php#L132-L154 - */ - private static function codePointToUtf8(int $num) : string + final public static function containsIdentical(mixed $value): TraversableContainsIdentical { - if ($num <= 0x7f) { - return chr($num); - } - if ($num <= 0x7ff) { - return chr(($num >> 6) + 0xc0) . chr(($num & 0x3f) + 0x80); - } - if ($num <= 0xffff) { - return chr(($num >> 12) + 0xe0) . chr(($num >> 6 & 0x3f) + 0x80) . chr(($num & 0x3f) + 0x80); - } - if ($num <= 0x1fffff) { - return chr(($num >> 18) + 0xf0) . chr(($num >> 12 & 0x3f) + 0x80) . chr(($num >> 6 & 0x3f) + 0x80) . chr(($num & 0x3f) + 0x80); - } - // Invalid UTF-8 codepoint escape sequence: Codepoint too large - return "�"; + return new TraversableContainsIdentical($value); } -} - */ - private $tokens; - /** @var int */ - private $index; - /** @var int[] */ - private $savePoints = []; - /** @var list */ - private $skippedTokenTypes = [Lexer::TOKEN_HORIZONTAL_WS]; - /** @var string|null */ - private $newline = null; /** - * @param list $tokens + * @throws Exception */ - public function __construct(array $tokens, int $index = 0) + final public static function containsOnly(string $type): TraversableContainsOnly { - $this->tokens = $tokens; - $this->index = $index; - $this->skipIrrelevantTokens(); + return new TraversableContainsOnly($type); } /** - * @return list + * @throws Exception */ - public function getTokens() : array + final public static function containsOnlyInstancesOf(string $className): TraversableContainsOnly { - return $this->tokens; + return new TraversableContainsOnly($className, \false); } - public function getContentBetween(int $startPos, int $endPos) : string + final public static function arrayHasKey(int|string $key): ArrayHasKey { - if ($startPos < 0 || $endPos > count($this->tokens)) { - throw new LogicException(); - } - $content = ''; - for ($i = $startPos; $i < $endPos; $i++) { - $content .= $this->tokens[$i][Lexer::VALUE_OFFSET]; - } - return $content; + return new ArrayHasKey($key); } - public function getTokenCount() : int + final public static function isList(): IsList { - return count($this->tokens); + return new IsList(); } - public function currentTokenValue() : string + final public static function equalTo(mixed $value): IsEqual { - return $this->tokens[$this->index][Lexer::VALUE_OFFSET]; + return new IsEqual($value, 0.0, \false, \false); } - public function currentTokenType() : int + final public static function equalToCanonicalizing(mixed $value): IsEqualCanonicalizing { - return $this->tokens[$this->index][Lexer::TYPE_OFFSET]; + return new IsEqualCanonicalizing($value); } - public function currentTokenOffset() : int + final public static function equalToIgnoringCase(mixed $value): IsEqualIgnoringCase { - $offset = 0; - for ($i = 0; $i < $this->index; $i++) { - $offset += strlen($this->tokens[$i][Lexer::VALUE_OFFSET]); - } - return $offset; + return new IsEqualIgnoringCase($value); } - public function currentTokenLine() : int + final public static function equalToWithDelta(mixed $value, float $delta): IsEqualWithDelta { - return $this->tokens[$this->index][Lexer::LINE_OFFSET]; + return new IsEqualWithDelta($value, $delta); } - public function currentTokenIndex() : int + final public static function isEmpty(): IsEmpty { - return $this->index; + return new IsEmpty(); } - public function endIndexOfLastRelevantToken() : int + final public static function isWritable(): IsWritable { - $endIndex = $this->currentTokenIndex(); - $endIndex--; - while (in_array($this->tokens[$endIndex][Lexer::TYPE_OFFSET], $this->skippedTokenTypes, \true)) { - if (!isset($this->tokens[$endIndex - 1])) { - break; - } - $endIndex--; - } - return $endIndex; + return new IsWritable(); } - public function isCurrentTokenValue(string $tokenValue) : bool + final public static function isReadable(): IsReadable { - return $this->tokens[$this->index][Lexer::VALUE_OFFSET] === $tokenValue; + return new IsReadable(); } - public function isCurrentTokenType(int ...$tokenType) : bool + final public static function directoryExists(): DirectoryExists { - return in_array($this->tokens[$this->index][Lexer::TYPE_OFFSET], $tokenType, \true); + return new DirectoryExists(); } - public function isPrecededByHorizontalWhitespace() : bool + final public static function fileExists(): FileExists { - return ($this->tokens[$this->index - 1][Lexer::TYPE_OFFSET] ?? -1) === Lexer::TOKEN_HORIZONTAL_WS; + return new FileExists(); } - /** - * @throws ParserException - */ - public function consumeTokenType(int $tokenType) : void + final public static function greaterThan(mixed $value): GreaterThan { - if ($this->tokens[$this->index][Lexer::TYPE_OFFSET] !== $tokenType) { - $this->throwError($tokenType); - } - if ($tokenType === Lexer::TOKEN_PHPDOC_EOL) { - if ($this->newline === null) { - $this->detectNewline(); - } - } - $this->index++; - $this->skipIrrelevantTokens(); + return new GreaterThan($value); } - /** - * @throws ParserException - */ - public function consumeTokenValue(int $tokenType, string $tokenValue) : void + final public static function greaterThanOrEqual(mixed $value): LogicalOr { - if ($this->tokens[$this->index][Lexer::TYPE_OFFSET] !== $tokenType || $this->tokens[$this->index][Lexer::VALUE_OFFSET] !== $tokenValue) { - $this->throwError($tokenType, $tokenValue); - } - $this->index++; - $this->skipIrrelevantTokens(); + return static::logicalOr(new IsEqual($value), new GreaterThan($value)); } - /** @phpstan-impure */ - public function tryConsumeTokenValue(string $tokenValue) : bool + final public static function identicalTo(mixed $value): IsIdentical { - if ($this->tokens[$this->index][Lexer::VALUE_OFFSET] !== $tokenValue) { - return \false; - } - $this->index++; - $this->skipIrrelevantTokens(); - return \true; + return new IsIdentical($value); } - /** @phpstan-impure */ - public function tryConsumeTokenType(int $tokenType) : bool + /** + * @throws UnknownClassOrInterfaceException + */ + final public static function isInstanceOf(string $className): IsInstanceOf { - if ($this->tokens[$this->index][Lexer::TYPE_OFFSET] !== $tokenType) { - return \false; - } - if ($tokenType === Lexer::TOKEN_PHPDOC_EOL) { - if ($this->newline === null) { - $this->detectNewline(); - } - } - $this->index++; - $this->skipIrrelevantTokens(); - return \true; + return new IsInstanceOf($className); } - private function detectNewline() : void + /** + * @psalm-param 'array'|'boolean'|'bool'|'double'|'float'|'integer'|'int'|'null'|'numeric'|'object'|'real'|'resource'|'resource (closed)'|'string'|'scalar'|'callable'|'iterable' $type + * + * @throws Exception + */ + final public static function isType(string $type): IsType { - $value = $this->currentTokenValue(); - if (substr($value, 0, 2) === "\r\n") { - $this->newline = "\r\n"; - } elseif (substr($value, 0, 1) === "\n") { - $this->newline = "\n"; - } + return new IsType($type); } - public function getSkippedHorizontalWhiteSpaceIfAny() : string + final public static function lessThan(mixed $value): LessThan { - if ($this->index > 0 && $this->tokens[$this->index - 1][Lexer::TYPE_OFFSET] === Lexer::TOKEN_HORIZONTAL_WS) { - return $this->tokens[$this->index - 1][Lexer::VALUE_OFFSET]; - } - return ''; + return new LessThan($value); } - /** @phpstan-impure */ - public function joinUntil(int ...$tokenType) : string + final public static function lessThanOrEqual(mixed $value): LogicalOr { - $s = ''; - while (!in_array($this->tokens[$this->index][Lexer::TYPE_OFFSET], $tokenType, \true)) { - $s .= $this->tokens[$this->index++][Lexer::VALUE_OFFSET]; - } - return $s; + return static::logicalOr(new IsEqual($value), new LessThan($value)); } - public function next() : void + final public static function matchesRegularExpression(string $pattern): RegularExpression { - $this->index++; - $this->skipIrrelevantTokens(); + return new RegularExpression($pattern); } - private function skipIrrelevantTokens() : void + final public static function matches(string $string): StringMatchesFormatDescription { - if (!isset($this->tokens[$this->index])) { - return; - } - while (in_array($this->tokens[$this->index][Lexer::TYPE_OFFSET], $this->skippedTokenTypes, \true)) { - if (!isset($this->tokens[$this->index + 1])) { - break; - } - $this->index++; - } + return new StringMatchesFormatDescription($string); } - public function addEndOfLineToSkippedTokens() : void + /** + * @psalm-param non-empty-string $prefix + * + * @throws InvalidArgumentException + */ + final public static function stringStartsWith(string $prefix): StringStartsWith { - $this->skippedTokenTypes = [Lexer::TOKEN_HORIZONTAL_WS, Lexer::TOKEN_PHPDOC_EOL]; + return new StringStartsWith($prefix); } - public function removeEndOfLineFromSkippedTokens() : void + final public static function stringContains(string $string, bool $case = \true): StringContains { - $this->skippedTokenTypes = [Lexer::TOKEN_HORIZONTAL_WS]; + return new StringContains($string, $case); } - /** @phpstan-impure */ - public function forwardToTheEnd() : void + /** + * @psalm-param non-empty-string $suffix + * + * @throws InvalidArgumentException + */ + final public static function stringEndsWith(string $suffix): StringEndsWith { - $lastToken = count($this->tokens) - 1; - $this->index = $lastToken; + return new StringEndsWith($suffix); } - public function pushSavePoint() : void + final public static function stringEqualsStringIgnoringLineEndings(string $string): StringEqualsStringIgnoringLineEndings { - $this->savePoints[] = $this->index; + return new StringEqualsStringIgnoringLineEndings($string); } - public function dropSavePoint() : void + final public static function countOf(int $count): Count { - array_pop($this->savePoints); + return new Count($count); } - public function rollback() : void + final public static function objectEquals(object $object, string $method = 'equals'): ObjectEquals { - $index = array_pop($this->savePoints); - assert($index !== null); - $this->index = $index; + return new ObjectEquals($object, $method); } /** - * @throws ParserException + * Fails a test with the given message. + * + * @throws AssertionFailedError */ - private function throwError(int $expectedTokenType, ?string $expectedTokenValue = null) : void + final public static function fail(string $message = ''): never { - throw new ParserException($this->currentTokenValue(), $this->currentTokenType(), $this->currentTokenOffset(), $expectedTokenType, $expectedTokenValue, $this->currentTokenLine()); + self::$count++; + throw new \PHPUnit\Framework\AssertionFailedError($message); } /** - * Check whether the position is directly preceded by a certain token type. + * Mark the test as incomplete. * - * During this check TOKEN_HORIZONTAL_WS and TOKEN_PHPDOC_EOL are skipped + * @throws IncompleteTestError */ - public function hasTokenImmediatelyBefore(int $pos, int $expectedTokenType) : bool + final public static function markTestIncomplete(string $message = ''): never { - $tokens = $this->tokens; - $pos--; - for (; $pos >= 0; $pos--) { - $token = $tokens[$pos]; - $type = $token[Lexer::TYPE_OFFSET]; - if ($type === $expectedTokenType) { - return \true; - } - if (!in_array($type, [Lexer::TOKEN_HORIZONTAL_WS, Lexer::TOKEN_PHPDOC_EOL], \true)) { - break; - } - } - return \false; + throw new \PHPUnit\Framework\IncompleteTestError($message); } /** - * Check whether the position is directly followed by a certain token type. + * Mark the test as skipped. * - * During this check TOKEN_HORIZONTAL_WS and TOKEN_PHPDOC_EOL are skipped + * @throws SkippedWithMessageException */ - public function hasTokenImmediatelyAfter(int $pos, int $expectedTokenType) : bool + final public static function markTestSkipped(string $message = ''): never { - $tokens = $this->tokens; - $pos++; - for ($c = count($tokens); $pos < $c; $pos++) { - $token = $tokens[$pos]; - $type = $token[Lexer::TYPE_OFFSET]; - if ($type === $expectedTokenType) { - return \true; - } - if (!in_array($type, [Lexer::TOKEN_HORIZONTAL_WS, Lexer::TOKEN_PHPDOC_EOL], \true)) { - break; - } - } - return \false; + throw new \PHPUnit\Framework\SkippedWithMessageException($message); } - public function getDetectedNewline() : ?string + /** + * Return the current assertion count. + */ + final public static function getCount(): int { - return $this->newline; + return self::$count; } /** - * Whether the given position is immediately surrounded by parenthesis. + * Reset the assertion counter. */ - public function hasParentheses(int $startPos, int $endPos) : bool + final public static function resetCount(): void { - return $this->hasTokenImmediatelyBefore($startPos, Lexer::TOKEN_OPEN_PARENTHESES) && $this->hasTokenImmediatelyAfter($endPos, Lexer::TOKEN_CLOSE_PARENTHESES); + self::$count = 0; + } + private static function isNativeType(string $type): bool + { + return match ($type) { + 'numeric', 'integer', 'int', 'iterable', 'float', 'string', 'boolean', 'bool', 'null', 'array', 'object', 'resource', 'scalar' => \true, + default => \false, + }; } } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; -use LogicException; -use PHPUnitPHAR\PHPStan\PhpDocParser\Ast; -use PHPUnitPHAR\PHPStan\PhpDocParser\Ast\PhpDoc\TemplateTagValueNode; -use PHPUnitPHAR\PHPStan\PhpDocParser\Lexer\Lexer; -use function in_array; -use function str_replace; -use function strlen; -use function strpos; -use function substr_compare; -use function trim; -class TypeParser -{ - /** @var ConstExprParser|null */ - private $constExprParser; - /** @var bool */ - private $quoteAwareConstExprString; - /** @var bool */ - private $useLinesAttributes; - /** @var bool */ - private $useIndexAttributes; +use function func_get_args; +use function function_exists; +use ArrayAccess; +use Countable; +use PHPUnit\Framework\Constraint\ArrayHasKey; +use PHPUnit\Framework\Constraint\Callback; +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\IsEqualCanonicalizing; +use PHPUnit\Framework\Constraint\IsEqualIgnoringCase; +use PHPUnit\Framework\Constraint\IsEqualWithDelta; +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\IsList; +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\ObjectEquals; +use PHPUnit\Framework\Constraint\RegularExpression; +use PHPUnit\Framework\Constraint\StringContains; +use PHPUnit\Framework\Constraint\StringEndsWith; +use PHPUnit\Framework\Constraint\StringEqualsStringIgnoringLineEndings; +use PHPUnit\Framework\Constraint\StringMatchesFormatDescription; +use PHPUnit\Framework\Constraint\StringStartsWith; +use PHPUnit\Framework\Constraint\TraversableContainsEqual; +use PHPUnit\Framework\Constraint\TraversableContainsIdentical; +use PHPUnit\Framework\Constraint\TraversableContainsOnly; +use PHPUnit\Framework\MockObject\Rule\AnyInvokedCount as AnyInvokedCountMatcher; +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; +use PHPUnit\Util\Xml\XmlException; +use Throwable; +if (!function_exists('PHPUnit\Framework\assertArrayHasKey')) { /** - * @param array{lines?: bool, indexes?: bool} $usedAttributes + * Asserts that an array has a specified key. + * + * @throws Exception + * @throws ExpectationFailedException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertArrayHasKey */ - public function __construct(?ConstExprParser $constExprParser = null, bool $quoteAwareConstExprString = \false, array $usedAttributes = []) + function assertArrayHasKey(int|string $key, array|ArrayAccess $array, string $message = ''): void { - $this->constExprParser = $constExprParser; - $this->quoteAwareConstExprString = $quoteAwareConstExprString; - $this->useLinesAttributes = $usedAttributes['lines'] ?? \false; - $this->useIndexAttributes = $usedAttributes['indexes'] ?? \false; + \PHPUnit\Framework\Assert::assertArrayHasKey(...func_get_args()); } - /** @phpstan-impure */ - public function parse(TokenIterator $tokens) : Ast\Type\TypeNode +} +if (!function_exists('PHPUnit\Framework\assertArrayNotHasKey')) { + /** + * Asserts that an array does not have a specified key. + * + * @throws Exception + * @throws ExpectationFailedException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertArrayNotHasKey + */ + function assertArrayNotHasKey(int|string $key, array|ArrayAccess $array, string $message = ''): void { - $startLine = $tokens->currentTokenLine(); - $startIndex = $tokens->currentTokenIndex(); - if ($tokens->isCurrentTokenType(Lexer::TOKEN_NULLABLE)) { - $type = $this->parseNullable($tokens); - } else { - $type = $this->parseAtomic($tokens); - if ($tokens->isCurrentTokenType(Lexer::TOKEN_UNION)) { - $type = $this->parseUnion($tokens, $type); - } elseif ($tokens->isCurrentTokenType(Lexer::TOKEN_INTERSECTION)) { - $type = $this->parseIntersection($tokens, $type); - } - } - return $this->enrichWithAttributes($tokens, $type, $startLine, $startIndex); + \PHPUnit\Framework\Assert::assertArrayNotHasKey(...func_get_args()); } +} +if (!function_exists('PHPUnit\Framework\assertIsList')) { /** - * @internal - * @template T of Ast\Node - * @param T $type - * @return T + * @throws ExpectationFailedException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertIsList */ - public function enrichWithAttributes(TokenIterator $tokens, Ast\Node $type, int $startLine, int $startIndex) : Ast\Node + function assertIsList(mixed $array, string $message = ''): void { - if ($this->useLinesAttributes) { - $type->setAttribute(Ast\Attribute::START_LINE, $startLine); - $type->setAttribute(Ast\Attribute::END_LINE, $tokens->currentTokenLine()); - } - if ($this->useIndexAttributes) { - $type->setAttribute(Ast\Attribute::START_INDEX, $startIndex); - $type->setAttribute(Ast\Attribute::END_INDEX, $tokens->endIndexOfLastRelevantToken()); - } - return $type; + \PHPUnit\Framework\Assert::assertIsList(...func_get_args()); } - /** @phpstan-impure */ - private function subParse(TokenIterator $tokens) : Ast\Type\TypeNode +} +if (!function_exists('PHPUnit\Framework\assertContains')) { + /** + * Asserts that a haystack contains a needle. + * + * @throws Exception + * @throws ExpectationFailedException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertContains + */ + function assertContains(mixed $needle, iterable $haystack, string $message = ''): void { - $startLine = $tokens->currentTokenLine(); - $startIndex = $tokens->currentTokenIndex(); - if ($tokens->isCurrentTokenType(Lexer::TOKEN_NULLABLE)) { - $type = $this->parseNullable($tokens); - } elseif ($tokens->isCurrentTokenType(Lexer::TOKEN_VARIABLE)) { - $type = $this->parseConditionalForParameter($tokens, $tokens->currentTokenValue()); - } else { - $type = $this->parseAtomic($tokens); - if ($tokens->isCurrentTokenValue('is')) { - $type = $this->parseConditional($tokens, $type); - } else { - $tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL); - if ($tokens->isCurrentTokenType(Lexer::TOKEN_UNION)) { - $type = $this->subParseUnion($tokens, $type); - } elseif ($tokens->isCurrentTokenType(Lexer::TOKEN_INTERSECTION)) { - $type = $this->subParseIntersection($tokens, $type); - } - } - } - return $this->enrichWithAttributes($tokens, $type, $startLine, $startIndex); - } - /** @phpstan-impure */ - private function parseAtomic(TokenIterator $tokens) : Ast\Type\TypeNode - { - $startLine = $tokens->currentTokenLine(); - $startIndex = $tokens->currentTokenIndex(); - if ($tokens->tryConsumeTokenType(Lexer::TOKEN_OPEN_PARENTHESES)) { - $tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL); - $type = $this->subParse($tokens); - $tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL); - $tokens->consumeTokenType(Lexer::TOKEN_CLOSE_PARENTHESES); - if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) { - $type = $this->tryParseArrayOrOffsetAccess($tokens, $type); - } - return $this->enrichWithAttributes($tokens, $type, $startLine, $startIndex); - } - if ($tokens->tryConsumeTokenType(Lexer::TOKEN_THIS_VARIABLE)) { - $type = $this->enrichWithAttributes($tokens, new Ast\Type\ThisTypeNode(), $startLine, $startIndex); - if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) { - $type = $this->tryParseArrayOrOffsetAccess($tokens, $type); - } - return $this->enrichWithAttributes($tokens, $type, $startLine, $startIndex); - } - $currentTokenValue = $tokens->currentTokenValue(); - $tokens->pushSavePoint(); - // because of ConstFetchNode - if ($tokens->tryConsumeTokenType(Lexer::TOKEN_IDENTIFIER)) { - $type = $this->enrichWithAttributes($tokens, new Ast\Type\IdentifierTypeNode($currentTokenValue), $startLine, $startIndex); - if (!$tokens->isCurrentTokenType(Lexer::TOKEN_DOUBLE_COLON)) { - $tokens->dropSavePoint(); - // because of ConstFetchNode - if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_ANGLE_BRACKET)) { - $tokens->pushSavePoint(); - $isHtml = $this->isHtml($tokens); - $tokens->rollback(); - if ($isHtml) { - return $type; - } - $origType = $type; - $type = $this->tryParseCallable($tokens, $type, \true); - if ($type === $origType) { - $type = $this->parseGeneric($tokens, $type); - if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) { - $type = $this->tryParseArrayOrOffsetAccess($tokens, $type); - } - } - } elseif ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_PARENTHESES)) { - $type = $this->tryParseCallable($tokens, $type, \false); - } elseif ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) { - $type = $this->tryParseArrayOrOffsetAccess($tokens, $type); - } elseif (in_array($type->name, ['array', 'list', 'object'], \true) && $tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_CURLY_BRACKET) && !$tokens->isPrecededByHorizontalWhitespace()) { - if ($type->name === 'object') { - $type = $this->parseObjectShape($tokens); - } else { - $type = $this->parseArrayShape($tokens, $type, $type->name); - } - if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) { - $type = $this->tryParseArrayOrOffsetAccess($tokens, $this->enrichWithAttributes($tokens, $type, $startLine, $startIndex)); - } - } - return $this->enrichWithAttributes($tokens, $type, $startLine, $startIndex); - } else { - $tokens->rollback(); - // because of ConstFetchNode - } - } else { - $tokens->dropSavePoint(); - // because of ConstFetchNode - } - $currentTokenValue = $tokens->currentTokenValue(); - $currentTokenType = $tokens->currentTokenType(); - $currentTokenOffset = $tokens->currentTokenOffset(); - $currentTokenLine = $tokens->currentTokenLine(); - if ($this->constExprParser === null) { - throw new ParserException($currentTokenValue, $currentTokenType, $currentTokenOffset, Lexer::TOKEN_IDENTIFIER, null, $currentTokenLine); - } - try { - $constExpr = $this->constExprParser->parse($tokens, \true); - if ($constExpr instanceof Ast\ConstExpr\ConstExprArrayNode) { - throw new ParserException($currentTokenValue, $currentTokenType, $currentTokenOffset, Lexer::TOKEN_IDENTIFIER, null, $currentTokenLine); - } - return $this->enrichWithAttributes($tokens, new Ast\Type\ConstTypeNode($constExpr), $startLine, $startIndex); - } catch (LogicException $e) { - throw new ParserException($currentTokenValue, $currentTokenType, $currentTokenOffset, Lexer::TOKEN_IDENTIFIER, null, $currentTokenLine); - } - } - /** @phpstan-impure */ - private function parseUnion(TokenIterator $tokens, Ast\Type\TypeNode $type) : Ast\Type\TypeNode - { - $types = [$type]; - while ($tokens->tryConsumeTokenType(Lexer::TOKEN_UNION)) { - $types[] = $this->parseAtomic($tokens); - } - return new Ast\Type\UnionTypeNode($types); - } - /** @phpstan-impure */ - private function subParseUnion(TokenIterator $tokens, Ast\Type\TypeNode $type) : Ast\Type\TypeNode - { - $types = [$type]; - while ($tokens->tryConsumeTokenType(Lexer::TOKEN_UNION)) { - $tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL); - $types[] = $this->parseAtomic($tokens); - $tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL); - } - return new Ast\Type\UnionTypeNode($types); - } - /** @phpstan-impure */ - private function parseIntersection(TokenIterator $tokens, Ast\Type\TypeNode $type) : Ast\Type\TypeNode - { - $types = [$type]; - while ($tokens->tryConsumeTokenType(Lexer::TOKEN_INTERSECTION)) { - $types[] = $this->parseAtomic($tokens); - } - return new Ast\Type\IntersectionTypeNode($types); - } - /** @phpstan-impure */ - private function subParseIntersection(TokenIterator $tokens, Ast\Type\TypeNode $type) : Ast\Type\TypeNode - { - $types = [$type]; - while ($tokens->tryConsumeTokenType(Lexer::TOKEN_INTERSECTION)) { - $tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL); - $types[] = $this->parseAtomic($tokens); - $tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL); - } - return new Ast\Type\IntersectionTypeNode($types); - } - /** @phpstan-impure */ - private function parseConditional(TokenIterator $tokens, Ast\Type\TypeNode $subjectType) : Ast\Type\TypeNode - { - $tokens->consumeTokenType(Lexer::TOKEN_IDENTIFIER); - $negated = \false; - if ($tokens->isCurrentTokenValue('not')) { - $negated = \true; - $tokens->consumeTokenType(Lexer::TOKEN_IDENTIFIER); - } - $targetType = $this->parse($tokens); - $tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL); - $tokens->consumeTokenType(Lexer::TOKEN_NULLABLE); - $tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL); - $ifType = $this->parse($tokens); - $tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL); - $tokens->consumeTokenType(Lexer::TOKEN_COLON); - $tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL); - $elseType = $this->subParse($tokens); - return new Ast\Type\ConditionalTypeNode($subjectType, $targetType, $ifType, $elseType, $negated); - } - /** @phpstan-impure */ - private function parseConditionalForParameter(TokenIterator $tokens, string $parameterName) : Ast\Type\TypeNode - { - $tokens->consumeTokenType(Lexer::TOKEN_VARIABLE); - $tokens->consumeTokenValue(Lexer::TOKEN_IDENTIFIER, 'is'); - $negated = \false; - if ($tokens->isCurrentTokenValue('not')) { - $negated = \true; - $tokens->consumeTokenType(Lexer::TOKEN_IDENTIFIER); - } - $targetType = $this->parse($tokens); - $tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL); - $tokens->consumeTokenType(Lexer::TOKEN_NULLABLE); - $tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL); - $ifType = $this->parse($tokens); - $tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL); - $tokens->consumeTokenType(Lexer::TOKEN_COLON); - $tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL); - $elseType = $this->subParse($tokens); - return new Ast\Type\ConditionalTypeForParameterNode($parameterName, $targetType, $ifType, $elseType, $negated); - } - /** @phpstan-impure */ - private function parseNullable(TokenIterator $tokens) : Ast\Type\TypeNode - { - $tokens->consumeTokenType(Lexer::TOKEN_NULLABLE); - $type = $this->parseAtomic($tokens); - return new Ast\Type\NullableTypeNode($type); - } - /** @phpstan-impure */ - public function isHtml(TokenIterator $tokens) : bool - { - $tokens->consumeTokenType(Lexer::TOKEN_OPEN_ANGLE_BRACKET); - if (!$tokens->isCurrentTokenType(Lexer::TOKEN_IDENTIFIER)) { - return \false; - } - $htmlTagName = $tokens->currentTokenValue(); - $tokens->next(); - if (!$tokens->tryConsumeTokenType(Lexer::TOKEN_CLOSE_ANGLE_BRACKET)) { - return \false; - } - $endTag = ''; - $endTagSearchOffset = -strlen($endTag); - while (!$tokens->isCurrentTokenType(Lexer::TOKEN_END)) { - if ($tokens->tryConsumeTokenType(Lexer::TOKEN_OPEN_ANGLE_BRACKET) && strpos($tokens->currentTokenValue(), '/' . $htmlTagName . '>') !== \false || substr_compare($tokens->currentTokenValue(), $endTag, $endTagSearchOffset) === 0) { - return \true; - } - $tokens->next(); - } - return \false; - } - /** @phpstan-impure */ - public function parseGeneric(TokenIterator $tokens, Ast\Type\IdentifierTypeNode $baseType) : Ast\Type\GenericTypeNode - { - $tokens->consumeTokenType(Lexer::TOKEN_OPEN_ANGLE_BRACKET); - $startLine = $baseType->getAttribute(Ast\Attribute::START_LINE); - $startIndex = $baseType->getAttribute(Ast\Attribute::START_INDEX); - $genericTypes = []; - $variances = []; - $isFirst = \true; - while ($isFirst || $tokens->tryConsumeTokenType(Lexer::TOKEN_COMMA)) { - $tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL); - // trailing comma case - if (!$isFirst && $tokens->isCurrentTokenType(Lexer::TOKEN_CLOSE_ANGLE_BRACKET)) { - break; - } - $isFirst = \false; - [$genericTypes[], $variances[]] = $this->parseGenericTypeArgument($tokens); - $tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL); - } - $type = new Ast\Type\GenericTypeNode($baseType, $genericTypes, $variances); - if ($startLine !== null && $startIndex !== null) { - $type = $this->enrichWithAttributes($tokens, $type, $startLine, $startIndex); - } - $tokens->consumeTokenType(Lexer::TOKEN_CLOSE_ANGLE_BRACKET); - return $type; + \PHPUnit\Framework\Assert::assertContains(...func_get_args()); } +} +if (!function_exists('PHPUnit\Framework\assertContainsEquals')) { /** - * @phpstan-impure - * @return array{Ast\Type\TypeNode, Ast\Type\GenericTypeNode::VARIANCE_*} + * @throws ExpectationFailedException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertContainsEquals */ - public function parseGenericTypeArgument(TokenIterator $tokens) : array + function assertContainsEquals(mixed $needle, iterable $haystack, string $message = ''): void { - $startLine = $tokens->currentTokenLine(); - $startIndex = $tokens->currentTokenIndex(); - if ($tokens->tryConsumeTokenType(Lexer::TOKEN_WILDCARD)) { - return [$this->enrichWithAttributes($tokens, new Ast\Type\IdentifierTypeNode('mixed'), $startLine, $startIndex), Ast\Type\GenericTypeNode::VARIANCE_BIVARIANT]; - } - if ($tokens->tryConsumeTokenValue('contravariant')) { - $variance = Ast\Type\GenericTypeNode::VARIANCE_CONTRAVARIANT; - } elseif ($tokens->tryConsumeTokenValue('covariant')) { - $variance = Ast\Type\GenericTypeNode::VARIANCE_COVARIANT; - } else { - $variance = Ast\Type\GenericTypeNode::VARIANCE_INVARIANT; - } - $type = $this->parse($tokens); - return [$type, $variance]; + \PHPUnit\Framework\Assert::assertContainsEquals(...func_get_args()); } +} +if (!function_exists('PHPUnit\Framework\assertNotContains')) { /** - * @throws ParserException - * @param ?callable(TokenIterator): string $parseDescription + * Asserts that a haystack does not contain a needle. + * + * @throws Exception + * @throws ExpectationFailedException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertNotContains */ - public function parseTemplateTagValue(TokenIterator $tokens, ?callable $parseDescription = null) : TemplateTagValueNode + function assertNotContains(mixed $needle, iterable $haystack, string $message = ''): void { - $name = $tokens->currentTokenValue(); - $tokens->consumeTokenType(Lexer::TOKEN_IDENTIFIER); - if ($tokens->tryConsumeTokenValue('of') || $tokens->tryConsumeTokenValue('as')) { - $bound = $this->parse($tokens); - } else { - $bound = null; - } - if ($tokens->tryConsumeTokenValue('=')) { - $default = $this->parse($tokens); - } else { - $default = null; - } - if ($parseDescription !== null) { - $description = $parseDescription($tokens); - } else { - $description = ''; - } - return new Ast\PhpDoc\TemplateTagValueNode($name, $bound, $description, $default); + \PHPUnit\Framework\Assert::assertNotContains(...func_get_args()); } - /** @phpstan-impure */ - private function parseCallable(TokenIterator $tokens, Ast\Type\IdentifierTypeNode $identifier, bool $hasTemplate) : Ast\Type\TypeNode +} +if (!function_exists('PHPUnit\Framework\assertNotContainsEquals')) { + /** + * @throws ExpectationFailedException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertNotContainsEquals + */ + function assertNotContainsEquals(mixed $needle, iterable $haystack, string $message = ''): void { - $templates = $hasTemplate ? $this->parseCallableTemplates($tokens) : []; - $tokens->consumeTokenType(Lexer::TOKEN_OPEN_PARENTHESES); - $tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL); - $parameters = []; - if (!$tokens->isCurrentTokenType(Lexer::TOKEN_CLOSE_PARENTHESES)) { - $parameters[] = $this->parseCallableParameter($tokens); - $tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL); - while ($tokens->tryConsumeTokenType(Lexer::TOKEN_COMMA)) { - $tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL); - if ($tokens->isCurrentTokenType(Lexer::TOKEN_CLOSE_PARENTHESES)) { - break; - } - $parameters[] = $this->parseCallableParameter($tokens); - $tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL); - } - } - $tokens->consumeTokenType(Lexer::TOKEN_CLOSE_PARENTHESES); - $tokens->consumeTokenType(Lexer::TOKEN_COLON); - $startLine = $tokens->currentTokenLine(); - $startIndex = $tokens->currentTokenIndex(); - $returnType = $this->enrichWithAttributes($tokens, $this->parseCallableReturnType($tokens), $startLine, $startIndex); - return new Ast\Type\CallableTypeNode($identifier, $parameters, $returnType, $templates); + \PHPUnit\Framework\Assert::assertNotContainsEquals(...func_get_args()); } +} +if (!function_exists('PHPUnit\Framework\assertContainsOnly')) { /** - * @return Ast\PhpDoc\TemplateTagValueNode[] + * Asserts that a haystack contains only values of a given type. + * + * @throws Exception + * @throws ExpectationFailedException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit * - * @phpstan-impure + * @see Assert::assertContainsOnly */ - private function parseCallableTemplates(TokenIterator $tokens) : array + function assertContainsOnly(string $type, iterable $haystack, ?bool $isNativeType = null, string $message = ''): void { - $tokens->consumeTokenType(Lexer::TOKEN_OPEN_ANGLE_BRACKET); - $templates = []; - $isFirst = \true; - while ($isFirst || $tokens->tryConsumeTokenType(Lexer::TOKEN_COMMA)) { - $tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL); - // trailing comma case - if (!$isFirst && $tokens->isCurrentTokenType(Lexer::TOKEN_CLOSE_ANGLE_BRACKET)) { - break; - } - $isFirst = \false; - $templates[] = $this->parseCallableTemplateArgument($tokens); - $tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL); - } - $tokens->consumeTokenType(Lexer::TOKEN_CLOSE_ANGLE_BRACKET); - return $templates; + \PHPUnit\Framework\Assert::assertContainsOnly(...func_get_args()); } - private function parseCallableTemplateArgument(TokenIterator $tokens) : Ast\PhpDoc\TemplateTagValueNode +} +if (!function_exists('PHPUnit\Framework\assertContainsOnlyInstancesOf')) { + /** + * Asserts that a haystack contains only instances of a given class name. + * + * @throws Exception + * @throws ExpectationFailedException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertContainsOnlyInstancesOf + */ + function assertContainsOnlyInstancesOf(string $className, iterable $haystack, string $message = ''): void { - $startLine = $tokens->currentTokenLine(); - $startIndex = $tokens->currentTokenIndex(); - return $this->enrichWithAttributes($tokens, $this->parseTemplateTagValue($tokens), $startLine, $startIndex); + \PHPUnit\Framework\Assert::assertContainsOnlyInstancesOf(...func_get_args()); } - /** @phpstan-impure */ - private function parseCallableParameter(TokenIterator $tokens) : Ast\Type\CallableTypeParameterNode +} +if (!function_exists('PHPUnit\Framework\assertNotContainsOnly')) { + /** + * Asserts that a haystack does not contain only values of a given type. + * + * @throws Exception + * @throws ExpectationFailedException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertNotContainsOnly + */ + function assertNotContainsOnly(string $type, iterable $haystack, ?bool $isNativeType = null, string $message = ''): void { - $startLine = $tokens->currentTokenLine(); - $startIndex = $tokens->currentTokenIndex(); - $type = $this->parse($tokens); - $isReference = $tokens->tryConsumeTokenType(Lexer::TOKEN_REFERENCE); - $isVariadic = $tokens->tryConsumeTokenType(Lexer::TOKEN_VARIADIC); - if ($tokens->isCurrentTokenType(Lexer::TOKEN_VARIABLE)) { - $parameterName = $tokens->currentTokenValue(); - $tokens->consumeTokenType(Lexer::TOKEN_VARIABLE); - } else { - $parameterName = ''; - } - $isOptional = $tokens->tryConsumeTokenType(Lexer::TOKEN_EQUAL); - return $this->enrichWithAttributes($tokens, new Ast\Type\CallableTypeParameterNode($type, $isReference, $isVariadic, $parameterName, $isOptional), $startLine, $startIndex); + \PHPUnit\Framework\Assert::assertNotContainsOnly(...func_get_args()); } - /** @phpstan-impure */ - private function parseCallableReturnType(TokenIterator $tokens) : Ast\Type\TypeNode +} +if (!function_exists('PHPUnit\Framework\assertCount')) { + /** + * Asserts the number of elements of an array, Countable or Traversable. + * + * @throws Exception + * @throws ExpectationFailedException + * @throws GeneratorNotSupportedException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertCount + */ + function assertCount(int $expectedCount, Countable|iterable $haystack, string $message = ''): void { - $startLine = $tokens->currentTokenLine(); - $startIndex = $tokens->currentTokenIndex(); - if ($tokens->isCurrentTokenType(Lexer::TOKEN_NULLABLE)) { - return $this->parseNullable($tokens); - } elseif ($tokens->tryConsumeTokenType(Lexer::TOKEN_OPEN_PARENTHESES)) { - $type = $this->subParse($tokens); - $tokens->consumeTokenType(Lexer::TOKEN_CLOSE_PARENTHESES); - if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) { - $type = $this->tryParseArrayOrOffsetAccess($tokens, $type); - } - return $type; - } elseif ($tokens->tryConsumeTokenType(Lexer::TOKEN_THIS_VARIABLE)) { - $type = new Ast\Type\ThisTypeNode(); - if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) { - $type = $this->tryParseArrayOrOffsetAccess($tokens, $this->enrichWithAttributes($tokens, $type, $startLine, $startIndex)); - } - return $type; - } else { - $currentTokenValue = $tokens->currentTokenValue(); - $tokens->pushSavePoint(); - // because of ConstFetchNode - if ($tokens->tryConsumeTokenType(Lexer::TOKEN_IDENTIFIER)) { - $type = new Ast\Type\IdentifierTypeNode($currentTokenValue); - if (!$tokens->isCurrentTokenType(Lexer::TOKEN_DOUBLE_COLON)) { - if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_ANGLE_BRACKET)) { - $type = $this->parseGeneric($tokens, $this->enrichWithAttributes($tokens, $type, $startLine, $startIndex)); - if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) { - $type = $this->tryParseArrayOrOffsetAccess($tokens, $this->enrichWithAttributes($tokens, $type, $startLine, $startIndex)); - } - } elseif ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) { - $type = $this->tryParseArrayOrOffsetAccess($tokens, $this->enrichWithAttributes($tokens, $type, $startLine, $startIndex)); - } elseif (in_array($type->name, ['array', 'list', 'object'], \true) && $tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_CURLY_BRACKET) && !$tokens->isPrecededByHorizontalWhitespace()) { - if ($type->name === 'object') { - $type = $this->parseObjectShape($tokens); - } else { - $type = $this->parseArrayShape($tokens, $this->enrichWithAttributes($tokens, $type, $startLine, $startIndex), $type->name); - } - if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) { - $type = $this->tryParseArrayOrOffsetAccess($tokens, $this->enrichWithAttributes($tokens, $type, $startLine, $startIndex)); - } - } - return $type; - } else { - $tokens->rollback(); - // because of ConstFetchNode - } - } else { - $tokens->dropSavePoint(); - // because of ConstFetchNode - } - } - $currentTokenValue = $tokens->currentTokenValue(); - $currentTokenType = $tokens->currentTokenType(); - $currentTokenOffset = $tokens->currentTokenOffset(); - $currentTokenLine = $tokens->currentTokenLine(); - if ($this->constExprParser === null) { - throw new ParserException($currentTokenValue, $currentTokenType, $currentTokenOffset, Lexer::TOKEN_IDENTIFIER, null, $currentTokenLine); - } - try { - $constExpr = $this->constExprParser->parse($tokens, \true); - if ($constExpr instanceof Ast\ConstExpr\ConstExprArrayNode) { - throw new ParserException($currentTokenValue, $currentTokenType, $currentTokenOffset, Lexer::TOKEN_IDENTIFIER, null, $currentTokenLine); - } - $type = new Ast\Type\ConstTypeNode($constExpr); - if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) { - $type = $this->tryParseArrayOrOffsetAccess($tokens, $this->enrichWithAttributes($tokens, $type, $startLine, $startIndex)); - } - return $type; - } catch (LogicException $e) { - throw new ParserException($currentTokenValue, $currentTokenType, $currentTokenOffset, Lexer::TOKEN_IDENTIFIER, null, $currentTokenLine); - } + \PHPUnit\Framework\Assert::assertCount(...func_get_args()); } - /** @phpstan-impure */ - private function tryParseCallable(TokenIterator $tokens, Ast\Type\IdentifierTypeNode $identifier, bool $hasTemplate) : Ast\Type\TypeNode +} +if (!function_exists('PHPUnit\Framework\assertNotCount')) { + /** + * Asserts the number of elements of an array, Countable or Traversable. + * + * @throws Exception + * @throws ExpectationFailedException + * @throws GeneratorNotSupportedException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertNotCount + */ + function assertNotCount(int $expectedCount, Countable|iterable $haystack, string $message = ''): void { - try { - $tokens->pushSavePoint(); - $type = $this->parseCallable($tokens, $identifier, $hasTemplate); - $tokens->dropSavePoint(); - } catch (ParserException $e) { - $tokens->rollback(); - $type = $identifier; - } - return $type; + \PHPUnit\Framework\Assert::assertNotCount(...func_get_args()); } - /** @phpstan-impure */ - private function tryParseArrayOrOffsetAccess(TokenIterator $tokens, Ast\Type\TypeNode $type) : Ast\Type\TypeNode +} +if (!function_exists('PHPUnit\Framework\assertEquals')) { + /** + * Asserts that two variables are equal. + * + * @throws ExpectationFailedException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertEquals + */ + function assertEquals(mixed $expected, mixed $actual, string $message = ''): void { - $startLine = $type->getAttribute(Ast\Attribute::START_LINE); - $startIndex = $type->getAttribute(Ast\Attribute::START_INDEX); - try { - while ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) { - $tokens->pushSavePoint(); - $canBeOffsetAccessType = !$tokens->isPrecededByHorizontalWhitespace(); - $tokens->consumeTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET); - if ($canBeOffsetAccessType && !$tokens->isCurrentTokenType(Lexer::TOKEN_CLOSE_SQUARE_BRACKET)) { - $offset = $this->parse($tokens); - $tokens->consumeTokenType(Lexer::TOKEN_CLOSE_SQUARE_BRACKET); - $tokens->dropSavePoint(); - $type = new Ast\Type\OffsetAccessTypeNode($type, $offset); - if ($startLine !== null && $startIndex !== null) { - $type = $this->enrichWithAttributes($tokens, $type, $startLine, $startIndex); - } - } else { - $tokens->consumeTokenType(Lexer::TOKEN_CLOSE_SQUARE_BRACKET); - $tokens->dropSavePoint(); - $type = new Ast\Type\ArrayTypeNode($type); - if ($startLine !== null && $startIndex !== null) { - $type = $this->enrichWithAttributes($tokens, $type, $startLine, $startIndex); - } - } - } - } catch (ParserException $e) { - $tokens->rollback(); - } - return $type; + \PHPUnit\Framework\Assert::assertEquals(...func_get_args()); } +} +if (!function_exists('PHPUnit\Framework\assertEqualsCanonicalizing')) { /** - * @phpstan-impure - * @param Ast\Type\ArrayShapeNode::KIND_* $kind + * Asserts that two variables are equal (canonicalizing). + * + * @throws ExpectationFailedException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertEqualsCanonicalizing */ - private function parseArrayShape(TokenIterator $tokens, Ast\Type\TypeNode $type, string $kind) : Ast\Type\ArrayShapeNode + function assertEqualsCanonicalizing(mixed $expected, mixed $actual, string $message = ''): void { - $tokens->consumeTokenType(Lexer::TOKEN_OPEN_CURLY_BRACKET); - $items = []; - $sealed = \true; - do { - $tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL); - if ($tokens->tryConsumeTokenType(Lexer::TOKEN_CLOSE_CURLY_BRACKET)) { - return new Ast\Type\ArrayShapeNode($items, \true, $kind); - } - if ($tokens->tryConsumeTokenType(Lexer::TOKEN_VARIADIC)) { - $sealed = \false; - $tokens->tryConsumeTokenType(Lexer::TOKEN_COMMA); - break; - } - $items[] = $this->parseArrayShapeItem($tokens); - $tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL); - } while ($tokens->tryConsumeTokenType(Lexer::TOKEN_COMMA)); - $tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL); - $tokens->consumeTokenType(Lexer::TOKEN_CLOSE_CURLY_BRACKET); - return new Ast\Type\ArrayShapeNode($items, $sealed, $kind); + \PHPUnit\Framework\Assert::assertEqualsCanonicalizing(...func_get_args()); } - /** @phpstan-impure */ - private function parseArrayShapeItem(TokenIterator $tokens) : Ast\Type\ArrayShapeItemNode +} +if (!function_exists('PHPUnit\Framework\assertEqualsIgnoringCase')) { + /** + * Asserts that two variables are equal (ignoring case). + * + * @throws ExpectationFailedException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertEqualsIgnoringCase + */ + function assertEqualsIgnoringCase(mixed $expected, mixed $actual, string $message = ''): void { - $startLine = $tokens->currentTokenLine(); - $startIndex = $tokens->currentTokenIndex(); - try { - $tokens->pushSavePoint(); - $key = $this->parseArrayShapeKey($tokens); - $optional = $tokens->tryConsumeTokenType(Lexer::TOKEN_NULLABLE); - $tokens->consumeTokenType(Lexer::TOKEN_COLON); - $value = $this->parse($tokens); - $tokens->dropSavePoint(); - return $this->enrichWithAttributes($tokens, new Ast\Type\ArrayShapeItemNode($key, $optional, $value), $startLine, $startIndex); - } catch (ParserException $e) { - $tokens->rollback(); - $value = $this->parse($tokens); - return $this->enrichWithAttributes($tokens, new Ast\Type\ArrayShapeItemNode(null, \false, $value), $startLine, $startIndex); - } - } - /** - * @phpstan-impure - * @return Ast\ConstExpr\ConstExprIntegerNode|Ast\ConstExpr\ConstExprStringNode|Ast\Type\IdentifierTypeNode - */ - private function parseArrayShapeKey(TokenIterator $tokens) - { - $startIndex = $tokens->currentTokenIndex(); - $startLine = $tokens->currentTokenLine(); - if ($tokens->isCurrentTokenType(Lexer::TOKEN_INTEGER)) { - $key = new Ast\ConstExpr\ConstExprIntegerNode(str_replace('_', '', $tokens->currentTokenValue())); - $tokens->next(); - } elseif ($tokens->isCurrentTokenType(Lexer::TOKEN_SINGLE_QUOTED_STRING)) { - if ($this->quoteAwareConstExprString) { - $key = new Ast\ConstExpr\QuoteAwareConstExprStringNode(StringUnescaper::unescapeString($tokens->currentTokenValue()), Ast\ConstExpr\QuoteAwareConstExprStringNode::SINGLE_QUOTED); - } else { - $key = new Ast\ConstExpr\ConstExprStringNode(trim($tokens->currentTokenValue(), "'")); - } - $tokens->next(); - } elseif ($tokens->isCurrentTokenType(Lexer::TOKEN_DOUBLE_QUOTED_STRING)) { - if ($this->quoteAwareConstExprString) { - $key = new Ast\ConstExpr\QuoteAwareConstExprStringNode(StringUnescaper::unescapeString($tokens->currentTokenValue()), Ast\ConstExpr\QuoteAwareConstExprStringNode::DOUBLE_QUOTED); - } else { - $key = new Ast\ConstExpr\ConstExprStringNode(trim($tokens->currentTokenValue(), '"')); - } - $tokens->next(); - } else { - $key = new Ast\Type\IdentifierTypeNode($tokens->currentTokenValue()); - $tokens->consumeTokenType(Lexer::TOKEN_IDENTIFIER); - } - return $this->enrichWithAttributes($tokens, $key, $startLine, $startIndex); + \PHPUnit\Framework\Assert::assertEqualsIgnoringCase(...func_get_args()); } +} +if (!function_exists('PHPUnit\Framework\assertEqualsWithDelta')) { /** - * @phpstan-impure + * Asserts that two variables are equal (with delta). + * + * @throws ExpectationFailedException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertEqualsWithDelta */ - private function parseObjectShape(TokenIterator $tokens) : Ast\Type\ObjectShapeNode + function assertEqualsWithDelta(mixed $expected, mixed $actual, float $delta, string $message = ''): void { - $tokens->consumeTokenType(Lexer::TOKEN_OPEN_CURLY_BRACKET); - $items = []; - do { - $tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL); - if ($tokens->tryConsumeTokenType(Lexer::TOKEN_CLOSE_CURLY_BRACKET)) { - return new Ast\Type\ObjectShapeNode($items); - } - $items[] = $this->parseObjectShapeItem($tokens); - $tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL); - } while ($tokens->tryConsumeTokenType(Lexer::TOKEN_COMMA)); - $tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL); - $tokens->consumeTokenType(Lexer::TOKEN_CLOSE_CURLY_BRACKET); - return new Ast\Type\ObjectShapeNode($items); - } - /** @phpstan-impure */ - private function parseObjectShapeItem(TokenIterator $tokens) : Ast\Type\ObjectShapeItemNode - { - $startLine = $tokens->currentTokenLine(); - $startIndex = $tokens->currentTokenIndex(); - $key = $this->parseObjectShapeKey($tokens); - $optional = $tokens->tryConsumeTokenType(Lexer::TOKEN_NULLABLE); - $tokens->consumeTokenType(Lexer::TOKEN_COLON); - $value = $this->parse($tokens); - return $this->enrichWithAttributes($tokens, new Ast\Type\ObjectShapeItemNode($key, $optional, $value), $startLine, $startIndex); - } - /** - * @phpstan-impure - * @return Ast\ConstExpr\ConstExprStringNode|Ast\Type\IdentifierTypeNode - */ - private function parseObjectShapeKey(TokenIterator $tokens) - { - $startLine = $tokens->currentTokenLine(); - $startIndex = $tokens->currentTokenIndex(); - if ($tokens->isCurrentTokenType(Lexer::TOKEN_SINGLE_QUOTED_STRING)) { - if ($this->quoteAwareConstExprString) { - $key = new Ast\ConstExpr\QuoteAwareConstExprStringNode(StringUnescaper::unescapeString($tokens->currentTokenValue()), Ast\ConstExpr\QuoteAwareConstExprStringNode::SINGLE_QUOTED); - } else { - $key = new Ast\ConstExpr\ConstExprStringNode(trim($tokens->currentTokenValue(), "'")); - } - $tokens->next(); - } elseif ($tokens->isCurrentTokenType(Lexer::TOKEN_DOUBLE_QUOTED_STRING)) { - if ($this->quoteAwareConstExprString) { - $key = new Ast\ConstExpr\QuoteAwareConstExprStringNode(StringUnescaper::unescapeString($tokens->currentTokenValue()), Ast\ConstExpr\QuoteAwareConstExprStringNode::DOUBLE_QUOTED); - } else { - $key = new Ast\ConstExpr\ConstExprStringNode(trim($tokens->currentTokenValue(), '"')); - } - $tokens->next(); - } else { - $key = new Ast\Type\IdentifierTypeNode($tokens->currentTokenValue()); - $tokens->consumeTokenType(Lexer::TOKEN_IDENTIFIER); - } - return $this->enrichWithAttributes($tokens, $key, $startLine, $startIndex); + \PHPUnit\Framework\Assert::assertEqualsWithDelta(...func_get_args()); } } -type = $type; - $this->old = $old; - $this->new = $new; + \PHPUnit\Framework\Assert::assertNotEquals(...func_get_args()); } } -isEqual = $isEqual; + \PHPUnit\Framework\Assert::assertNotEqualsCanonicalizing(...func_get_args()); } +} +if (!function_exists('PHPUnit\Framework\assertNotEqualsIgnoringCase')) { /** - * Calculate diff (edit script) from $old to $new. + * Asserts that two variables are not equal (ignoring case). * - * @param T[] $old Original array - * @param T[] $new New array + * @throws ExpectationFailedException * - * @return DiffElem[] Diff (edit script) + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertNotEqualsIgnoringCase */ - public function diff(array $old, array $new) : array + function assertNotEqualsIgnoringCase(mixed $expected, mixed $actual, string $message = ''): void { - [$trace, $x, $y] = $this->calculateTrace($old, $new); - return $this->extractDiff($trace, $x, $y, $old, $new); + \PHPUnit\Framework\Assert::assertNotEqualsIgnoringCase(...func_get_args()); } +} +if (!function_exists('PHPUnit\Framework\assertNotEqualsWithDelta')) { /** - * Calculate diff, including "replace" operations. + * Asserts that two variables are not equal (with delta). * - * If a sequence of remove operations is followed by the same number of add operations, these - * will be coalesced into replace operations. + * @throws ExpectationFailedException * - * @param T[] $old Original array - * @param T[] $new New array + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit * - * @return DiffElem[] Diff (edit script), including replace operations + * @see Assert::assertNotEqualsWithDelta */ - public function diffWithReplacements(array $old, array $new) : array + function assertNotEqualsWithDelta(mixed $expected, mixed $actual, float $delta, string $message = ''): void { - return $this->coalesceReplacements($this->diff($old, $new)); + \PHPUnit\Framework\Assert::assertNotEqualsWithDelta(...func_get_args()); } +} +if (!function_exists('PHPUnit\Framework\assertObjectEquals')) { /** - * @param T[] $old - * @param T[] $new - * @return array{array>, int, int} + * @throws ExpectationFailedException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertObjectEquals */ - private function calculateTrace(array $old, array $new) : array + function assertObjectEquals(object $expected, object $actual, string $method = 'equals', string $message = ''): void { - $n = count($old); - $m = count($new); - $max = $n + $m; - $v = [1 => 0]; - $trace = []; - for ($d = 0; $d <= $max; $d++) { - $trace[] = $v; - for ($k = -$d; $k <= $d; $k += 2) { - if ($k === -$d || $k !== $d && $v[$k - 1] < $v[$k + 1]) { - $x = $v[$k + 1]; - } else { - $x = $v[$k - 1] + 1; - } - $y = $x - $k; - while ($x < $n && $y < $m && ($this->isEqual)($old[$x], $new[$y])) { - $x++; - $y++; - } - $v[$k] = $x; - if ($x >= $n && $y >= $m) { - return [$trace, $x, $y]; - } - } - } - throw new Exception('Should not happen'); + \PHPUnit\Framework\Assert::assertObjectEquals(...func_get_args()); } +} +if (!function_exists('PHPUnit\Framework\assertEmpty')) { /** - * @param array> $trace - * @param T[] $old - * @param T[] $new - * @return DiffElem[] + * Asserts that a variable is empty. + * + * @throws ExpectationFailedException + * @throws GeneratorNotSupportedException + * + * @psalm-assert empty $actual + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertEmpty */ - private function extractDiff(array $trace, int $x, int $y, array $old, array $new) : array + function assertEmpty(mixed $actual, string $message = ''): void { - $result = []; - for ($d = count($trace) - 1; $d >= 0; $d--) { - $v = $trace[$d]; - $k = $x - $y; - if ($k === -$d || $k !== $d && $v[$k - 1] < $v[$k + 1]) { - $prevK = $k + 1; - } else { - $prevK = $k - 1; - } - $prevX = $v[$prevK]; - $prevY = $prevX - $prevK; - while ($x > $prevX && $y > $prevY) { - $result[] = new DiffElem(DiffElem::TYPE_KEEP, $old[$x - 1], $new[$y - 1]); - $x--; - $y--; - } - if ($d === 0) { - break; - } - while ($x > $prevX) { - $result[] = new DiffElem(DiffElem::TYPE_REMOVE, $old[$x - 1], null); - $x--; - } - while ($y > $prevY) { - $result[] = new DiffElem(DiffElem::TYPE_ADD, null, $new[$y - 1]); - $y--; - } - } - return array_reverse($result); + \PHPUnit\Framework\Assert::assertEmpty(...func_get_args()); } +} +if (!function_exists('PHPUnit\Framework\assertNotEmpty')) { /** - * Coalesce equal-length sequences of remove+add into a replace operation. + * Asserts that a variable is not empty. * - * @param DiffElem[] $diff - * @return DiffElem[] + * @throws ExpectationFailedException + * @throws GeneratorNotSupportedException + * + * @psalm-assert !empty $actual + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertNotEmpty */ - private function coalesceReplacements(array $diff) : array + function assertNotEmpty(mixed $actual, string $message = ''): void { - $newDiff = []; - $c = count($diff); - for ($i = 0; $i < $c; $i++) { - $diffType = $diff[$i]->type; - if ($diffType !== DiffElem::TYPE_REMOVE) { - $newDiff[] = $diff[$i]; - continue; - } - $j = $i; - while ($j < $c && $diff[$j]->type === DiffElem::TYPE_REMOVE) { - $j++; - } - $k = $j; - while ($k < $c && $diff[$k]->type === DiffElem::TYPE_ADD) { - $k++; - } - if ($j - $i === $k - $j) { - $len = $j - $i; - for ($n = 0; $n < $len; $n++) { - $newDiff[] = new DiffElem(DiffElem::TYPE_REPLACE, $diff[$i + $n]->old, $diff[$j + $n]->new); - } - } else { - for (; $i < $k; $i++) { - $newDiff[] = $diff[$i]; - } - } - $i = $k - 1; - } - return $newDiff; + \PHPUnit\Framework\Assert::assertNotEmpty(...func_get_args()); } } - */ - private $differ; +if (!function_exists('PHPUnit\Framework\assertGreaterThan')) { /** - * Map From "{$class}->{$subNode}" to string that should be inserted - * between elements of this list subnode + * Asserts that a value is greater than another value. + * + * @throws ExpectationFailedException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit * - * @var array + * @see Assert::assertGreaterThan */ - private $listInsertionMap = [PhpDocNode::class . '->children' => "\n * ", UnionTypeNode::class . '->types' => '|', IntersectionTypeNode::class . '->types' => '&', ArrayShapeNode::class . '->items' => ', ', ObjectShapeNode::class . '->items' => ', ', CallableTypeNode::class . '->parameters' => ', ', CallableTypeNode::class . '->templateTypes' => ', ', GenericTypeNode::class . '->genericTypes' => ', ', ConstExprArrayNode::class . '->items' => ', ', MethodTagValueNode::class . '->parameters' => ', ', DoctrineArray::class . '->items' => ', ', DoctrineAnnotation::class . '->arguments' => ', ']; + function assertGreaterThan(mixed $expected, mixed $actual, string $message = ''): void + { + \PHPUnit\Framework\Assert::assertGreaterThan(...func_get_args()); + } +} +if (!function_exists('PHPUnit\Framework\assertGreaterThanOrEqual')) { /** - * [$find, $extraLeft, $extraRight] + * Asserts that a value is greater than or equal to another value. + * + * @throws ExpectationFailedException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit * - * @var array + * @see Assert::assertGreaterThanOrEqual */ - private $emptyListInsertionMap = [CallableTypeNode::class . '->parameters' => ['(', '', ''], ArrayShapeNode::class . '->items' => ['{', '', ''], ObjectShapeNode::class . '->items' => ['{', '', ''], DoctrineArray::class . '->items' => ['{', '', ''], DoctrineAnnotation::class . '->arguments' => ['(', '', '']]; - /** @var array>> */ - private $parenthesesMap = [CallableTypeNode::class . '->returnType' => [CallableTypeNode::class, UnionTypeNode::class, IntersectionTypeNode::class], ArrayTypeNode::class . '->type' => [CallableTypeNode::class, UnionTypeNode::class, IntersectionTypeNode::class, ConstTypeNode::class, NullableTypeNode::class], OffsetAccessTypeNode::class . '->type' => [CallableTypeNode::class, UnionTypeNode::class, IntersectionTypeNode::class, ConstTypeNode::class, NullableTypeNode::class]]; - /** @var array>> */ - private $parenthesesListMap = [IntersectionTypeNode::class . '->types' => [IntersectionTypeNode::class, UnionTypeNode::class, NullableTypeNode::class], UnionTypeNode::class . '->types' => [IntersectionTypeNode::class, UnionTypeNode::class, NullableTypeNode::class]]; - public function printFormatPreserving(PhpDocNode $node, PhpDocNode $originalNode, TokenIterator $originalTokens) : string + function assertGreaterThanOrEqual(mixed $expected, mixed $actual, string $message = ''): void { - $this->differ = new Differ(static function ($a, $b) { - if ($a instanceof Node && $b instanceof Node) { - return $a === $b->getAttribute(Attribute::ORIGINAL_NODE); - } - return \false; - }); - $tokenIndex = 0; - $result = $this->printArrayFormatPreserving($node->children, $originalNode->children, $originalTokens, $tokenIndex, PhpDocNode::class, 'children'); - if ($result !== null) { - return $result . $originalTokens->getContentBetween($tokenIndex, $originalTokens->getTokenCount()); - } - return $this->print($node); - } - public function print(Node $node) : string - { - if ($node instanceof PhpDocNode) { - return "/**\n *" . implode("\n *", array_map(function (PhpDocChildNode $child) : string { - $s = $this->print($child); - return $s === '' ? '' : ' ' . $s; - }, $node->children)) . "\n */"; - } - if ($node instanceof PhpDocTextNode) { - return $node->text; - } - if ($node instanceof PhpDocTagNode) { - if ($node->value instanceof DoctrineTagValueNode) { - return $this->print($node->value); - } - return trim(sprintf('%s %s', $node->name, $this->print($node->value))); - } - if ($node instanceof PhpDocTagValueNode) { - return $this->printTagValue($node); - } - if ($node instanceof TypeNode) { - return $this->printType($node); - } - if ($node instanceof ConstExprNode) { - return $this->printConstExpr($node); - } - if ($node instanceof MethodTagValueParameterNode) { - $type = $node->type !== null ? $this->print($node->type) . ' ' : ''; - $isReference = $node->isReference ? '&' : ''; - $isVariadic = $node->isVariadic ? '...' : ''; - $default = $node->defaultValue !== null ? ' = ' . $this->print($node->defaultValue) : ''; - return "{$type}{$isReference}{$isVariadic}{$node->parameterName}{$default}"; - } - if ($node instanceof CallableTypeParameterNode) { - $type = $this->print($node->type) . ' '; - $isReference = $node->isReference ? '&' : ''; - $isVariadic = $node->isVariadic ? '...' : ''; - $isOptional = $node->isOptional ? '=' : ''; - return trim("{$type}{$isReference}{$isVariadic}{$node->parameterName}") . $isOptional; - } - if ($node instanceof DoctrineAnnotation) { - return (string) $node; - } - if ($node instanceof DoctrineArgument) { - return (string) $node; - } - if ($node instanceof DoctrineArray) { - return (string) $node; - } - if ($node instanceof DoctrineArrayItem) { - return (string) $node; - } - throw new LogicException(sprintf('Unknown node type %s', get_class($node))); - } - private function printTagValue(PhpDocTagValueNode $node) : string - { - // only nodes that contain another node are handled here - // the rest falls back on (string) $node - if ($node instanceof AssertTagMethodValueNode) { - $isNegated = $node->isNegated ? '!' : ''; - $isEquality = $node->isEquality ? '=' : ''; - $type = $this->printType($node->type); - return trim("{$isNegated}{$isEquality}{$type} {$node->parameter}->{$node->method}() {$node->description}"); - } - if ($node instanceof AssertTagPropertyValueNode) { - $isNegated = $node->isNegated ? '!' : ''; - $isEquality = $node->isEquality ? '=' : ''; - $type = $this->printType($node->type); - return trim("{$isNegated}{$isEquality}{$type} {$node->parameter}->{$node->property} {$node->description}"); - } - if ($node instanceof AssertTagValueNode) { - $isNegated = $node->isNegated ? '!' : ''; - $isEquality = $node->isEquality ? '=' : ''; - $type = $this->printType($node->type); - return trim("{$isNegated}{$isEquality}{$type} {$node->parameter} {$node->description}"); - } - if ($node instanceof ExtendsTagValueNode || $node instanceof ImplementsTagValueNode) { - $type = $this->printType($node->type); - return trim("{$type} {$node->description}"); - } - if ($node instanceof MethodTagValueNode) { - $static = $node->isStatic ? 'static ' : ''; - $returnType = $node->returnType !== null ? $this->printType($node->returnType) . ' ' : ''; - $parameters = implode(', ', array_map(function (MethodTagValueParameterNode $parameter) : string { - return $this->print($parameter); - }, $node->parameters)); - $description = $node->description !== '' ? " {$node->description}" : ''; - $templateTypes = count($node->templateTypes) > 0 ? '<' . implode(', ', array_map(function (TemplateTagValueNode $templateTag) : string { - return $this->print($templateTag); - }, $node->templateTypes)) . '>' : ''; - return "{$static}{$returnType}{$node->methodName}{$templateTypes}({$parameters}){$description}"; - } - if ($node instanceof MixinTagValueNode) { - $type = $this->printType($node->type); - return trim("{$type} {$node->description}"); - } - if ($node instanceof RequireExtendsTagValueNode) { - $type = $this->printType($node->type); - return trim("{$type} {$node->description}"); - } - if ($node instanceof RequireImplementsTagValueNode) { - $type = $this->printType($node->type); - return trim("{$type} {$node->description}"); - } - if ($node instanceof ParamOutTagValueNode) { - $type = $this->printType($node->type); - return trim("{$type} {$node->parameterName} {$node->description}"); - } - if ($node instanceof ParamTagValueNode) { - $reference = $node->isReference ? '&' : ''; - $variadic = $node->isVariadic ? '...' : ''; - $type = $this->printType($node->type); - return trim("{$type} {$reference}{$variadic}{$node->parameterName} {$node->description}"); - } - if ($node instanceof ParamImmediatelyInvokedCallableTagValueNode) { - return trim("{$node->parameterName} {$node->description}"); - } - if ($node instanceof ParamLaterInvokedCallableTagValueNode) { - return trim("{$node->parameterName} {$node->description}"); - } - if ($node instanceof ParamClosureThisTagValueNode) { - return trim("{$node->type} {$node->parameterName} {$node->description}"); - } - if ($node instanceof PropertyTagValueNode) { - $type = $this->printType($node->type); - return trim("{$type} {$node->propertyName} {$node->description}"); - } - if ($node instanceof ReturnTagValueNode) { - $type = $this->printType($node->type); - return trim("{$type} {$node->description}"); - } - if ($node instanceof SelfOutTagValueNode) { - $type = $this->printType($node->type); - return trim($type . ' ' . $node->description); - } - if ($node instanceof TemplateTagValueNode) { - $bound = $node->bound !== null ? ' of ' . $this->printType($node->bound) : ''; - $default = $node->default !== null ? ' = ' . $this->printType($node->default) : ''; - return trim("{$node->name}{$bound}{$default} {$node->description}"); - } - if ($node instanceof ThrowsTagValueNode) { - $type = $this->printType($node->type); - return trim("{$type} {$node->description}"); - } - if ($node instanceof TypeAliasImportTagValueNode) { - return trim("{$node->importedAlias} from " . $this->printType($node->importedFrom) . ($node->importedAs !== null ? " as {$node->importedAs}" : '')); - } - if ($node instanceof TypeAliasTagValueNode) { - $type = $this->printType($node->type); - return trim("{$node->alias} {$type}"); - } - if ($node instanceof UsesTagValueNode) { - $type = $this->printType($node->type); - return trim("{$type} {$node->description}"); - } - if ($node instanceof VarTagValueNode) { - $type = $this->printType($node->type); - return trim("{$type} " . trim("{$node->variableName} {$node->description}")); - } - return (string) $node; - } - private function printType(TypeNode $node) : string - { - if ($node instanceof ArrayShapeNode) { - $items = array_map(function (ArrayShapeItemNode $item) : string { - return $this->printType($item); - }, $node->items); - if (!$node->sealed) { - $items[] = '...'; - } - return $node->kind . '{' . implode(', ', $items) . '}'; - } - if ($node instanceof ArrayShapeItemNode) { - if ($node->keyName !== null) { - return sprintf('%s%s: %s', $this->print($node->keyName), $node->optional ? '?' : '', $this->printType($node->valueType)); - } - return $this->printType($node->valueType); - } - if ($node instanceof ArrayTypeNode) { - return $this->printOffsetAccessType($node->type) . '[]'; - } - if ($node instanceof CallableTypeNode) { - if ($node->returnType instanceof CallableTypeNode || $node->returnType instanceof UnionTypeNode || $node->returnType instanceof IntersectionTypeNode) { - $returnType = $this->wrapInParentheses($node->returnType); - } else { - $returnType = $this->printType($node->returnType); - } - $template = $node->templateTypes !== [] ? '<' . implode(', ', array_map(function (TemplateTagValueNode $templateNode) : string { - return $this->print($templateNode); - }, $node->templateTypes)) . '>' : ''; - $parameters = implode(', ', array_map(function (CallableTypeParameterNode $parameterNode) : string { - return $this->print($parameterNode); - }, $node->parameters)); - return "{$node->identifier}{$template}({$parameters}): {$returnType}"; - } - if ($node instanceof ConditionalTypeForParameterNode) { - return sprintf('(%s %s %s ? %s : %s)', $node->parameterName, $node->negated ? 'is not' : 'is', $this->printType($node->targetType), $this->printType($node->if), $this->printType($node->else)); - } - if ($node instanceof ConditionalTypeNode) { - return sprintf('(%s %s %s ? %s : %s)', $this->printType($node->subjectType), $node->negated ? 'is not' : 'is', $this->printType($node->targetType), $this->printType($node->if), $this->printType($node->else)); - } - if ($node instanceof ConstTypeNode) { - return $this->printConstExpr($node->constExpr); - } - if ($node instanceof GenericTypeNode) { - $genericTypes = []; - foreach ($node->genericTypes as $index => $type) { - $variance = $node->variances[$index] ?? GenericTypeNode::VARIANCE_INVARIANT; - if ($variance === GenericTypeNode::VARIANCE_INVARIANT) { - $genericTypes[] = $this->printType($type); - } elseif ($variance === GenericTypeNode::VARIANCE_BIVARIANT) { - $genericTypes[] = '*'; - } else { - $genericTypes[] = sprintf('%s %s', $variance, $this->print($type)); - } - } - return $node->type . '<' . implode(', ', $genericTypes) . '>'; - } - if ($node instanceof IdentifierTypeNode) { - return $node->name; - } - if ($node instanceof IntersectionTypeNode || $node instanceof UnionTypeNode) { - $items = []; - foreach ($node->types as $type) { - if ($type instanceof IntersectionTypeNode || $type instanceof UnionTypeNode || $type instanceof NullableTypeNode) { - $items[] = $this->wrapInParentheses($type); - continue; - } - $items[] = $this->printType($type); - } - return implode($node instanceof IntersectionTypeNode ? '&' : '|', $items); - } - if ($node instanceof InvalidTypeNode) { - return (string) $node; - } - if ($node instanceof NullableTypeNode) { - if ($node->type instanceof IntersectionTypeNode || $node->type instanceof UnionTypeNode) { - return '?(' . $this->printType($node->type) . ')'; - } - return '?' . $this->printType($node->type); - } - if ($node instanceof ObjectShapeNode) { - $items = array_map(function (ObjectShapeItemNode $item) : string { - return $this->printType($item); - }, $node->items); - return 'object{' . implode(', ', $items) . '}'; - } - if ($node instanceof ObjectShapeItemNode) { - if ($node->keyName !== null) { - return sprintf('%s%s: %s', $this->print($node->keyName), $node->optional ? '?' : '', $this->printType($node->valueType)); - } - return $this->printType($node->valueType); - } - if ($node instanceof OffsetAccessTypeNode) { - return $this->printOffsetAccessType($node->type) . '[' . $this->printType($node->offset) . ']'; - } - if ($node instanceof ThisTypeNode) { - return (string) $node; - } - throw new LogicException(sprintf('Unknown node type %s', get_class($node))); + \PHPUnit\Framework\Assert::assertGreaterThanOrEqual(...func_get_args()); } - private function wrapInParentheses(TypeNode $node) : string +} +if (!function_exists('PHPUnit\Framework\assertLessThan')) { + /** + * Asserts that a value is smaller than another value. + * + * @throws ExpectationFailedException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertLessThan + */ + function assertLessThan(mixed $expected, mixed $actual, string $message = ''): void { - return '(' . $this->printType($node) . ')'; + \PHPUnit\Framework\Assert::assertLessThan(...func_get_args()); } - private function printOffsetAccessType(TypeNode $type) : string +} +if (!function_exists('PHPUnit\Framework\assertLessThanOrEqual')) { + /** + * Asserts that a value is smaller than or equal to another value. + * + * @throws ExpectationFailedException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertLessThanOrEqual + */ + function assertLessThanOrEqual(mixed $expected, mixed $actual, string $message = ''): void { - if ($type instanceof CallableTypeNode || $type instanceof UnionTypeNode || $type instanceof IntersectionTypeNode || $type instanceof ConstTypeNode || $type instanceof NullableTypeNode) { - return $this->wrapInParentheses($type); - } - return $this->printType($type); + \PHPUnit\Framework\Assert::assertLessThanOrEqual(...func_get_args()); } - private function printConstExpr(ConstExprNode $node) : string +} +if (!function_exists('PHPUnit\Framework\assertFileEquals')) { + /** + * Asserts that the contents of one file is equal to the contents of another + * file. + * + * @throws ExpectationFailedException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertFileEquals + */ + function assertFileEquals(string $expected, string $actual, string $message = ''): void { - // this is fine - ConstExprNode classes do not contain nodes that need smart printer logic - return (string) $node; + \PHPUnit\Framework\Assert::assertFileEquals(...func_get_args()); } +} +if (!function_exists('PHPUnit\Framework\assertFileEqualsCanonicalizing')) { /** - * @param Node[] $nodes - * @param Node[] $originalNodes + * Asserts that the contents of one file is equal to the contents of another + * file (canonicalizing). + * + * @throws ExpectationFailedException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertFileEqualsCanonicalizing */ - private function printArrayFormatPreserving(array $nodes, array $originalNodes, TokenIterator $originalTokens, int &$tokenIndex, string $parentNodeClass, string $subNodeName) : ?string + function assertFileEqualsCanonicalizing(string $expected, string $actual, string $message = ''): void { - $diff = $this->differ->diffWithReplacements($originalNodes, $nodes); - $mapKey = $parentNodeClass . '->' . $subNodeName; - $insertStr = $this->listInsertionMap[$mapKey] ?? null; - $result = ''; - $beforeFirstKeepOrReplace = \true; - $delayedAdd = []; - $insertNewline = \false; - [$isMultiline, $beforeAsteriskIndent, $afterAsteriskIndent] = $this->isMultiline($tokenIndex, $originalNodes, $originalTokens); - if ($insertStr === "\n * ") { - $insertStr = sprintf('%s%s*%s', $originalTokens->getDetectedNewline() ?? "\n", $beforeAsteriskIndent, $afterAsteriskIndent); - } - foreach ($diff as $i => $diffElem) { - $diffType = $diffElem->type; - $newNode = $diffElem->new; - $originalNode = $diffElem->old; - if ($diffType === DiffElem::TYPE_KEEP || $diffType === DiffElem::TYPE_REPLACE) { - $beforeFirstKeepOrReplace = \false; - if (!$newNode instanceof Node || !$originalNode instanceof Node) { - return null; - } - $itemStartPos = $originalNode->getAttribute(Attribute::START_INDEX); - $itemEndPos = $originalNode->getAttribute(Attribute::END_INDEX); - if ($itemStartPos < 0 || $itemEndPos < 0 || $itemStartPos < $tokenIndex) { - throw new LogicException(); - } - $result .= $originalTokens->getContentBetween($tokenIndex, $itemStartPos); - if (count($delayedAdd) > 0) { - foreach ($delayedAdd as $delayedAddNode) { - $parenthesesNeeded = isset($this->parenthesesListMap[$mapKey]) && in_array(get_class($delayedAddNode), $this->parenthesesListMap[$mapKey], \true); - if ($parenthesesNeeded) { - $result .= '('; - } - $result .= $this->printNodeFormatPreserving($delayedAddNode, $originalTokens); - if ($parenthesesNeeded) { - $result .= ')'; - } - if ($insertNewline) { - $result .= $insertStr . sprintf('%s%s*%s', $originalTokens->getDetectedNewline() ?? "\n", $beforeAsteriskIndent, $afterAsteriskIndent); - } else { - $result .= $insertStr; - } - } - $delayedAdd = []; - } - $parenthesesNeeded = isset($this->parenthesesListMap[$mapKey]) && in_array(get_class($newNode), $this->parenthesesListMap[$mapKey], \true) && !in_array(get_class($originalNode), $this->parenthesesListMap[$mapKey], \true); - $addParentheses = $parenthesesNeeded && !$originalTokens->hasParentheses($itemStartPos, $itemEndPos); - if ($addParentheses) { - $result .= '('; - } - $result .= $this->printNodeFormatPreserving($newNode, $originalTokens); - if ($addParentheses) { - $result .= ')'; - } - $tokenIndex = $itemEndPos + 1; - } elseif ($diffType === DiffElem::TYPE_ADD) { - if ($insertStr === null) { - return null; - } - if (!$newNode instanceof Node) { - return null; - } - if ($insertStr === ', ' && $isMultiline) { - $insertStr = ','; - $insertNewline = \true; - } - if ($beforeFirstKeepOrReplace) { - // Will be inserted at the next "replace" or "keep" element - $delayedAdd[] = $newNode; - continue; - } - $itemEndPos = $tokenIndex - 1; - if ($insertNewline) { - $result .= $insertStr . sprintf('%s%s*%s', $originalTokens->getDetectedNewline() ?? "\n", $beforeAsteriskIndent, $afterAsteriskIndent); - } else { - $result .= $insertStr; - } - $parenthesesNeeded = isset($this->parenthesesListMap[$mapKey]) && in_array(get_class($newNode), $this->parenthesesListMap[$mapKey], \true); - if ($parenthesesNeeded) { - $result .= '('; - } - $result .= $this->printNodeFormatPreserving($newNode, $originalTokens); - if ($parenthesesNeeded) { - $result .= ')'; - } - $tokenIndex = $itemEndPos + 1; - } elseif ($diffType === DiffElem::TYPE_REMOVE) { - if (!$originalNode instanceof Node) { - return null; - } - $itemStartPos = $originalNode->getAttribute(Attribute::START_INDEX); - $itemEndPos = $originalNode->getAttribute(Attribute::END_INDEX); - if ($itemStartPos < 0 || $itemEndPos < 0) { - throw new LogicException(); - } - if ($i === 0) { - // If we're removing from the start, keep the tokens before the node and drop those after it, - // instead of the other way around. - $originalTokensArray = $originalTokens->getTokens(); - for ($j = $tokenIndex; $j < $itemStartPos; $j++) { - if ($originalTokensArray[$j][Lexer::TYPE_OFFSET] === Lexer::TOKEN_PHPDOC_EOL) { - break; - } - $result .= $originalTokensArray[$j][Lexer::VALUE_OFFSET]; - } - } - $tokenIndex = $itemEndPos + 1; - } - } - if (count($delayedAdd) > 0) { - if (!isset($this->emptyListInsertionMap[$mapKey])) { - return null; - } - [$findToken, $extraLeft, $extraRight] = $this->emptyListInsertionMap[$mapKey]; - if ($findToken !== null) { - $originalTokensArray = $originalTokens->getTokens(); - for (; $tokenIndex < count($originalTokensArray); $tokenIndex++) { - $result .= $originalTokensArray[$tokenIndex][Lexer::VALUE_OFFSET]; - if ($originalTokensArray[$tokenIndex][Lexer::VALUE_OFFSET] !== $findToken) { - continue; - } - $tokenIndex++; - break; - } - } - $first = \true; - $result .= $extraLeft; - foreach ($delayedAdd as $delayedAddNode) { - if (!$first) { - $result .= $insertStr; - if ($insertNewline) { - $result .= sprintf('%s%s*%s', $originalTokens->getDetectedNewline() ?? "\n", $beforeAsteriskIndent, $afterAsteriskIndent); - } - } - $result .= $this->printNodeFormatPreserving($delayedAddNode, $originalTokens); - $first = \false; - } - $result .= $extraRight; - } - return $result; + \PHPUnit\Framework\Assert::assertFileEqualsCanonicalizing(...func_get_args()); } +} +if (!function_exists('PHPUnit\Framework\assertFileEqualsIgnoringCase')) { /** - * @param Node[] $nodes - * @return array{bool, string, string} + * Asserts that the contents of one file is equal to the contents of another + * file (ignoring case). + * + * @throws ExpectationFailedException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertFileEqualsIgnoringCase */ - private function isMultiline(int $initialIndex, array $nodes, TokenIterator $originalTokens) : array + function assertFileEqualsIgnoringCase(string $expected, string $actual, string $message = ''): void { - $isMultiline = count($nodes) > 1; - $pos = $initialIndex; - $allText = ''; - /** @var Node|null $node */ - foreach ($nodes as $node) { - if (!$node instanceof Node) { - continue; - } - $endPos = $node->getAttribute(Attribute::END_INDEX) + 1; - $text = $originalTokens->getContentBetween($pos, $endPos); - $allText .= $text; - if (strpos($text, "\n") === \false) { - // We require that a newline is present between *every* item. If the formatting - // is inconsistent, with only some items having newlines, we don't consider it - // as multiline - $isMultiline = \false; - } - $pos = $endPos; - } - $c = preg_match_all('~\\n(?[\\x09\\x20]*)\\*(?\\x20*)~', $allText, $matches, PREG_SET_ORDER); - if ($c === 0) { - return [$isMultiline, '', '']; - } - $before = ''; - $after = ''; - foreach ($matches as $match) { - if (strlen($match['before']) > strlen($before)) { - $before = $match['before']; - } - if (strlen($match['after']) <= strlen($after)) { - continue; - } - $after = $match['after']; - } - return [$isMultiline, $before, $after]; + \PHPUnit\Framework\Assert::assertFileEqualsIgnoringCase(...func_get_args()); } - private function printNodeFormatPreserving(Node $node, TokenIterator $originalTokens) : string +} +if (!function_exists('PHPUnit\Framework\assertFileNotEquals')) { + /** + * Asserts that the contents of one file is not equal to the contents of + * another file. + * + * @throws ExpectationFailedException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertFileNotEquals + */ + function assertFileNotEquals(string $expected, string $actual, string $message = ''): void { - /** @var Node|null $originalNode */ - $originalNode = $node->getAttribute(Attribute::ORIGINAL_NODE); - if ($originalNode === null) { - return $this->print($node); - } - $class = get_class($node); - if ($class !== get_class($originalNode)) { - throw new LogicException(); - } - $startPos = $originalNode->getAttribute(Attribute::START_INDEX); - $endPos = $originalNode->getAttribute(Attribute::END_INDEX); - if ($startPos < 0 || $endPos < 0) { - throw new LogicException(); - } - $result = ''; - $pos = $startPos; - $subNodeNames = array_keys(get_object_vars($node)); - foreach ($subNodeNames as $subNodeName) { - $subNode = $node->{$subNodeName}; - $origSubNode = $originalNode->{$subNodeName}; - if (!$subNode instanceof Node && $subNode !== null || !$origSubNode instanceof Node && $origSubNode !== null) { - if ($subNode === $origSubNode) { - // Unchanged, can reuse old code - continue; - } - if (is_array($subNode) && is_array($origSubNode)) { - // Array subnode changed, we might be able to reconstruct it - $listResult = $this->printArrayFormatPreserving($subNode, $origSubNode, $originalTokens, $pos, $class, $subNodeName); - if ($listResult === null) { - return $this->print($node); - } - $result .= $listResult; - continue; - } - return $this->print($node); - } - if ($origSubNode === null) { - if ($subNode === null) { - // Both null, nothing to do - continue; - } - return $this->print($node); - } - $subStartPos = $origSubNode->getAttribute(Attribute::START_INDEX); - $subEndPos = $origSubNode->getAttribute(Attribute::END_INDEX); - if ($subStartPos < 0 || $subEndPos < 0) { - throw new LogicException(); - } - if ($subEndPos < $subStartPos) { - return $this->print($node); - } - if ($subNode === null) { - return $this->print($node); - } - $result .= $originalTokens->getContentBetween($pos, $subStartPos); - $mapKey = get_class($node) . '->' . $subNodeName; - $parenthesesNeeded = isset($this->parenthesesMap[$mapKey]) && in_array(get_class($subNode), $this->parenthesesMap[$mapKey], \true); - if ($subNode->getAttribute(Attribute::ORIGINAL_NODE) !== null) { - $parenthesesNeeded = $parenthesesNeeded && !in_array(get_class($subNode->getAttribute(Attribute::ORIGINAL_NODE)), $this->parenthesesMap[$mapKey], \true); - } - $addParentheses = $parenthesesNeeded && !$originalTokens->hasParentheses($subStartPos, $subEndPos); - if ($addParentheses) { - $result .= '('; - } - $result .= $this->printNodeFormatPreserving($subNode, $originalTokens); - if ($addParentheses) { - $result .= ')'; - } - $pos = $subEndPos + 1; - } - return $result . $originalTokens->getContentBetween($pos, $endPos + 1); + \PHPUnit\Framework\Assert::assertFileNotEquals(...func_get_args()); } } - - - - - This Schema file defines the rules by which the XML configuration file of PHPUnit 9.6 may be structured. - - - - - - Root Element - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - The main type specifying the document structure - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit; - -use Throwable; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -interface Exception extends Throwable -{ -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework; - -use const DEBUG_BACKTRACE_IGNORE_ARGS; -use const PHP_EOL; -use function array_shift; -use function array_unshift; -use function assert; -use function class_exists; -use function count; -use function debug_backtrace; -use function explode; -use function file_get_contents; -use function func_get_args; -use function implode; -use function interface_exists; -use function is_array; -use function is_bool; -use function is_int; -use function is_iterable; -use function is_object; -use function is_string; -use function preg_match; -use function preg_split; -use function sprintf; -use function strpos; -use ArrayAccess; -use Countable; -use DOMAttr; -use DOMDocument; -use DOMElement; -use Generator; -use PHPUnit\Framework\Constraint\ArrayHasKey; -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\IsEqualCanonicalizing; -use PHPUnit\Framework\Constraint\IsEqualIgnoringCase; -use PHPUnit\Framework\Constraint\IsEqualWithDelta; -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\ObjectEquals; -use PHPUnit\Framework\Constraint\ObjectHasAttribute; -use PHPUnit\Framework\Constraint\ObjectHasProperty; -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\TraversableContainsEqual; -use PHPUnit\Framework\Constraint\TraversableContainsIdentical; -use PHPUnit\Framework\Constraint\TraversableContainsOnly; -use PHPUnit\Util\Type; -use PHPUnit\Util\Xml; -use PHPUnit\Util\Xml\Loader as XmlLoader; -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -abstract class Assert -{ - /** - * @var int - */ - private static $count = 0; +if (!function_exists('PHPUnit\Framework\assertFileNotEqualsCanonicalizing')) { /** - * Asserts that an array has a specified key. - * - * @param int|string $key - * @param array|ArrayAccess $array + * Asserts that the contents of one file is not equal to the contents of another + * file (canonicalizing). * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws Exception * @throws ExpectationFailedException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertFileNotEqualsCanonicalizing */ - public static function assertArrayHasKey($key, $array, string $message = '') : void + function assertFileNotEqualsCanonicalizing(string $expected, string $actual, string $message = ''): void { - if (!(is_int($key) || is_string($key))) { - throw \PHPUnit\Framework\InvalidArgumentException::create(1, 'integer or string'); - } - if (!(is_array($array) || $array instanceof ArrayAccess)) { - throw \PHPUnit\Framework\InvalidArgumentException::create(2, 'array or ArrayAccess'); - } - $constraint = new ArrayHasKey($key); - static::assertThat($array, $constraint, $message); + \PHPUnit\Framework\Assert::assertFileNotEqualsCanonicalizing(...func_get_args()); } +} +if (!function_exists('PHPUnit\Framework\assertFileNotEqualsIgnoringCase')) { /** - * Asserts that an array does not have a specified key. - * - * @param int|string $key - * @param array|ArrayAccess $array + * Asserts that the contents of one file is not equal to the contents of another + * file (ignoring case). * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws Exception * @throws ExpectationFailedException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertFileNotEqualsIgnoringCase */ - public static function assertArrayNotHasKey($key, $array, string $message = '') : void + function assertFileNotEqualsIgnoringCase(string $expected, string $actual, string $message = ''): void { - if (!(is_int($key) || is_string($key))) { - throw \PHPUnit\Framework\InvalidArgumentException::create(1, 'integer or string'); - } - if (!(is_array($array) || $array instanceof ArrayAccess)) { - throw \PHPUnit\Framework\InvalidArgumentException::create(2, 'array or ArrayAccess'); - } - $constraint = new LogicalNot(new ArrayHasKey($key)); - static::assertThat($array, $constraint, $message); + \PHPUnit\Framework\Assert::assertFileNotEqualsIgnoringCase(...func_get_args()); } +} +if (!function_exists('PHPUnit\Framework\assertStringEqualsFile')) { /** - * Asserts that a haystack contains a needle. + * Asserts that the contents of a string is equal + * to the contents of a file. * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws Exception * @throws ExpectationFailedException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertStringEqualsFile */ - public static function assertContains($needle, iterable $haystack, string $message = '') : void - { - $constraint = new TraversableContainsIdentical($needle); - static::assertThat($haystack, $constraint, $message); - } - public static function assertContainsEquals($needle, iterable $haystack, string $message = '') : void + function assertStringEqualsFile(string $expectedFile, string $actualString, string $message = ''): void { - $constraint = new TraversableContainsEqual($needle); - static::assertThat($haystack, $constraint, $message); + \PHPUnit\Framework\Assert::assertStringEqualsFile(...func_get_args()); } +} +if (!function_exists('PHPUnit\Framework\assertStringEqualsFileCanonicalizing')) { /** - * Asserts that a haystack does not contain a needle. + * Asserts that the contents of a string is equal + * to the contents of a file (canonicalizing). * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws Exception * @throws ExpectationFailedException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertStringEqualsFileCanonicalizing */ - public static function assertNotContains($needle, iterable $haystack, string $message = '') : void - { - $constraint = new LogicalNot(new TraversableContainsIdentical($needle)); - static::assertThat($haystack, $constraint, $message); - } - public static function assertNotContainsEquals($needle, iterable $haystack, string $message = '') : void + function assertStringEqualsFileCanonicalizing(string $expectedFile, string $actualString, string $message = ''): void { - $constraint = new LogicalNot(new TraversableContainsEqual($needle)); - static::assertThat($haystack, $constraint, $message); + \PHPUnit\Framework\Assert::assertStringEqualsFileCanonicalizing(...func_get_args()); } +} +if (!function_exists('PHPUnit\Framework\assertStringEqualsFileIgnoringCase')) { /** - * Asserts that a haystack contains only values of a given type. + * Asserts that the contents of a string is equal + * to the contents of a file (ignoring case). * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException * @throws ExpectationFailedException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertStringEqualsFileIgnoringCase */ - public static function assertContainsOnly(string $type, iterable $haystack, ?bool $isNativeType = null, string $message = '') : void + function assertStringEqualsFileIgnoringCase(string $expectedFile, string $actualString, string $message = ''): void { - if ($isNativeType === null) { - $isNativeType = Type::isType($type); - } - static::assertThat($haystack, new TraversableContainsOnly($type, $isNativeType), $message); + \PHPUnit\Framework\Assert::assertStringEqualsFileIgnoringCase(...func_get_args()); } +} +if (!function_exists('PHPUnit\Framework\assertStringNotEqualsFile')) { /** - * Asserts that a haystack contains only instances of a given class name. + * Asserts that the contents of a string is not equal + * to the contents of a file. * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException * @throws ExpectationFailedException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertStringNotEqualsFile */ - public static function assertContainsOnlyInstancesOf(string $className, iterable $haystack, string $message = '') : void + function assertStringNotEqualsFile(string $expectedFile, string $actualString, string $message = ''): void { - static::assertThat($haystack, new TraversableContainsOnly($className, \false), $message); + \PHPUnit\Framework\Assert::assertStringNotEqualsFile(...func_get_args()); } +} +if (!function_exists('PHPUnit\Framework\assertStringNotEqualsFileCanonicalizing')) { /** - * Asserts that a haystack does not contain only values of a given type. + * Asserts that the contents of a string is not equal + * to the contents of a file (canonicalizing). * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException * @throws ExpectationFailedException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertStringNotEqualsFileCanonicalizing */ - public static function assertNotContainsOnly(string $type, iterable $haystack, ?bool $isNativeType = null, string $message = '') : void + function assertStringNotEqualsFileCanonicalizing(string $expectedFile, string $actualString, string $message = ''): void { - if ($isNativeType === null) { - $isNativeType = Type::isType($type); - } - static::assertThat($haystack, new LogicalNot(new TraversableContainsOnly($type, $isNativeType)), $message); + \PHPUnit\Framework\Assert::assertStringNotEqualsFileCanonicalizing(...func_get_args()); } +} +if (!function_exists('PHPUnit\Framework\assertStringNotEqualsFileIgnoringCase')) { /** - * Asserts the number of elements of an array, Countable or Traversable. - * - * @param Countable|iterable $haystack + * Asserts that the contents of a string is not equal + * to the contents of a file (ignoring case). * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws Exception * @throws ExpectationFailedException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertStringNotEqualsFileIgnoringCase */ - public static function assertCount(int $expectedCount, $haystack, string $message = '') : void + function assertStringNotEqualsFileIgnoringCase(string $expectedFile, string $actualString, string $message = ''): void { - if ($haystack instanceof Generator) { - self::createWarning('Passing an argument of type Generator for the $haystack parameter is deprecated. Support for this will be removed in PHPUnit 10.'); - } - if (!$haystack instanceof Countable && !is_iterable($haystack)) { - throw \PHPUnit\Framework\InvalidArgumentException::create(2, 'countable or iterable'); - } - static::assertThat($haystack, new Count($expectedCount), $message); + \PHPUnit\Framework\Assert::assertStringNotEqualsFileIgnoringCase(...func_get_args()); } +} +if (!function_exists('PHPUnit\Framework\assertIsReadable')) { /** - * Asserts the number of elements of an array, Countable or Traversable. - * - * @param Countable|iterable $haystack + * Asserts that a file/dir is readable. * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws Exception * @throws ExpectationFailedException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertIsReadable */ - public static function assertNotCount(int $expectedCount, $haystack, string $message = '') : void + function assertIsReadable(string $filename, string $message = ''): void { - if ($haystack instanceof Generator) { - self::createWarning('Passing an argument of type Generator for the $haystack parameter is deprecated. Support for this will be removed in PHPUnit 10.'); - } - if (!$haystack instanceof Countable && !is_iterable($haystack)) { - throw \PHPUnit\Framework\InvalidArgumentException::create(2, 'countable or iterable'); - } - $constraint = new LogicalNot(new Count($expectedCount)); - static::assertThat($haystack, $constraint, $message); + \PHPUnit\Framework\Assert::assertIsReadable(...func_get_args()); } +} +if (!function_exists('PHPUnit\Framework\assertIsNotReadable')) { /** - * Asserts that two variables are equal. + * Asserts that a file/dir exists and is not readable. * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException * @throws ExpectationFailedException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertIsNotReadable */ - public static function assertEquals($expected, $actual, string $message = '') : void + function assertIsNotReadable(string $filename, string $message = ''): void { - $constraint = new IsEqual($expected); - static::assertThat($actual, $constraint, $message); + \PHPUnit\Framework\Assert::assertIsNotReadable(...func_get_args()); } +} +if (!function_exists('PHPUnit\Framework\assertIsWritable')) { /** - * Asserts that two variables are equal (canonicalizing). + * Asserts that a file/dir exists and is writable. * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException * @throws ExpectationFailedException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertIsWritable */ - public static function assertEqualsCanonicalizing($expected, $actual, string $message = '') : void + function assertIsWritable(string $filename, string $message = ''): void { - $constraint = new IsEqualCanonicalizing($expected); - static::assertThat($actual, $constraint, $message); + \PHPUnit\Framework\Assert::assertIsWritable(...func_get_args()); } +} +if (!function_exists('PHPUnit\Framework\assertIsNotWritable')) { /** - * Asserts that two variables are equal (ignoring case). + * Asserts that a file/dir exists and is not writable. * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException * @throws ExpectationFailedException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertIsNotWritable */ - public static function assertEqualsIgnoringCase($expected, $actual, string $message = '') : void + function assertIsNotWritable(string $filename, string $message = ''): void { - $constraint = new IsEqualIgnoringCase($expected); - static::assertThat($actual, $constraint, $message); + \PHPUnit\Framework\Assert::assertIsNotWritable(...func_get_args()); } +} +if (!function_exists('PHPUnit\Framework\assertDirectoryExists')) { /** - * Asserts that two variables are equal (with delta). + * Asserts that a directory exists. * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException * @throws ExpectationFailedException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertDirectoryExists */ - public static function assertEqualsWithDelta($expected, $actual, float $delta, string $message = '') : void + function assertDirectoryExists(string $directory, string $message = ''): void { - $constraint = new IsEqualWithDelta($expected, $delta); - static::assertThat($actual, $constraint, $message); + \PHPUnit\Framework\Assert::assertDirectoryExists(...func_get_args()); } +} +if (!function_exists('PHPUnit\Framework\assertDirectoryDoesNotExist')) { /** - * Asserts that two variables are not equal. + * Asserts that a directory does not exist. * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException * @throws ExpectationFailedException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertDirectoryDoesNotExist */ - public static function assertNotEquals($expected, $actual, string $message = '') : void + function assertDirectoryDoesNotExist(string $directory, string $message = ''): void { - $constraint = new LogicalNot(new IsEqual($expected)); - static::assertThat($actual, $constraint, $message); + \PHPUnit\Framework\Assert::assertDirectoryDoesNotExist(...func_get_args()); } +} +if (!function_exists('PHPUnit\Framework\assertDirectoryIsReadable')) { /** - * Asserts that two variables are not equal (canonicalizing). + * Asserts that a directory exists and is readable. * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException * @throws ExpectationFailedException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertDirectoryIsReadable */ - public static function assertNotEqualsCanonicalizing($expected, $actual, string $message = '') : void + function assertDirectoryIsReadable(string $directory, string $message = ''): void { - $constraint = new LogicalNot(new IsEqualCanonicalizing($expected)); - static::assertThat($actual, $constraint, $message); + \PHPUnit\Framework\Assert::assertDirectoryIsReadable(...func_get_args()); } +} +if (!function_exists('PHPUnit\Framework\assertDirectoryIsNotReadable')) { /** - * Asserts that two variables are not equal (ignoring case). + * Asserts that a directory exists and is not readable. * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException * @throws ExpectationFailedException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertDirectoryIsNotReadable */ - public static function assertNotEqualsIgnoringCase($expected, $actual, string $message = '') : void + function assertDirectoryIsNotReadable(string $directory, string $message = ''): void { - $constraint = new LogicalNot(new IsEqualIgnoringCase($expected)); - static::assertThat($actual, $constraint, $message); + \PHPUnit\Framework\Assert::assertDirectoryIsNotReadable(...func_get_args()); } +} +if (!function_exists('PHPUnit\Framework\assertDirectoryIsWritable')) { /** - * Asserts that two variables are not equal (with delta). + * Asserts that a directory exists and is writable. * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException * @throws ExpectationFailedException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertDirectoryIsWritable */ - public static function assertNotEqualsWithDelta($expected, $actual, float $delta, string $message = '') : void + function assertDirectoryIsWritable(string $directory, string $message = ''): void { - $constraint = new LogicalNot(new IsEqualWithDelta($expected, $delta)); - static::assertThat($actual, $constraint, $message); + \PHPUnit\Framework\Assert::assertDirectoryIsWritable(...func_get_args()); } +} +if (!function_exists('PHPUnit\Framework\assertDirectoryIsNotWritable')) { /** + * Asserts that a directory exists and is not writable. + * * @throws ExpectationFailedException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertDirectoryIsNotWritable */ - public static function assertObjectEquals(object $expected, object $actual, string $method = 'equals', string $message = '') : void + function assertDirectoryIsNotWritable(string $directory, string $message = ''): void { - static::assertThat($actual, static::objectEquals($expected, $method), $message); + \PHPUnit\Framework\Assert::assertDirectoryIsNotWritable(...func_get_args()); } +} +if (!function_exists('PHPUnit\Framework\assertFileExists')) { /** - * Asserts that a variable is empty. + * Asserts that a file exists. * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException * @throws ExpectationFailedException * - * @psalm-assert empty $actual + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertFileExists */ - public static function assertEmpty($actual, string $message = '') : void + function assertFileExists(string $filename, string $message = ''): void { - if ($actual instanceof Generator) { - self::createWarning('Passing an argument of type Generator for the $actual parameter is deprecated. Support for this will be removed in PHPUnit 10.'); - } - static::assertThat($actual, static::isEmpty(), $message); + \PHPUnit\Framework\Assert::assertFileExists(...func_get_args()); } +} +if (!function_exists('PHPUnit\Framework\assertFileDoesNotExist')) { /** - * Asserts that a variable is not empty. + * Asserts that a file does not exist. * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException * @throws ExpectationFailedException * - * @psalm-assert !empty $actual + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertFileDoesNotExist */ - public static function assertNotEmpty($actual, string $message = '') : void + function assertFileDoesNotExist(string $filename, string $message = ''): void { - if ($actual instanceof Generator) { - self::createWarning('Passing an argument of type Generator for the $actual parameter is deprecated. Support for this will be removed in PHPUnit 10.'); - } - static::assertThat($actual, static::logicalNot(static::isEmpty()), $message); + \PHPUnit\Framework\Assert::assertFileDoesNotExist(...func_get_args()); } +} +if (!function_exists('PHPUnit\Framework\assertFileIsReadable')) { /** - * Asserts that a value is greater than another value. + * Asserts that a file exists and is readable. * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException * @throws ExpectationFailedException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertFileIsReadable */ - public static function assertGreaterThan($expected, $actual, string $message = '') : void + function assertFileIsReadable(string $file, string $message = ''): void { - static::assertThat($actual, static::greaterThan($expected), $message); + \PHPUnit\Framework\Assert::assertFileIsReadable(...func_get_args()); } +} +if (!function_exists('PHPUnit\Framework\assertFileIsNotReadable')) { /** - * Asserts that a value is greater than or equal to another value. + * Asserts that a file exists and is not readable. * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException * @throws ExpectationFailedException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertFileIsNotReadable */ - public static function assertGreaterThanOrEqual($expected, $actual, string $message = '') : void + function assertFileIsNotReadable(string $file, string $message = ''): void { - static::assertThat($actual, static::greaterThanOrEqual($expected), $message); + \PHPUnit\Framework\Assert::assertFileIsNotReadable(...func_get_args()); } +} +if (!function_exists('PHPUnit\Framework\assertFileIsWritable')) { /** - * Asserts that a value is smaller than another value. + * Asserts that a file exists and is writable. * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException * @throws ExpectationFailedException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertFileIsWritable */ - public static function assertLessThan($expected, $actual, string $message = '') : void + function assertFileIsWritable(string $file, string $message = ''): void { - static::assertThat($actual, static::lessThan($expected), $message); + \PHPUnit\Framework\Assert::assertFileIsWritable(...func_get_args()); } +} +if (!function_exists('PHPUnit\Framework\assertFileIsNotWritable')) { /** - * Asserts that a value is smaller than or equal to another value. + * Asserts that a file exists and is not writable. * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException * @throws ExpectationFailedException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertFileIsNotWritable */ - public static function assertLessThanOrEqual($expected, $actual, string $message = '') : void + function assertFileIsNotWritable(string $file, string $message = ''): void { - static::assertThat($actual, static::lessThanOrEqual($expected), $message); + \PHPUnit\Framework\Assert::assertFileIsNotWritable(...func_get_args()); } +} +if (!function_exists('PHPUnit\Framework\assertTrue')) { /** - * Asserts that the contents of one file is equal to the contents of another - * file. + * Asserts that a condition is true. * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException * @throws ExpectationFailedException + * + * @psalm-assert true $condition + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertTrue */ - public static function assertFileEquals(string $expected, string $actual, string $message = '') : void + function assertTrue(mixed $condition, string $message = ''): void { - static::assertFileExists($expected, $message); - static::assertFileExists($actual, $message); - $constraint = new IsEqual(file_get_contents($expected)); - static::assertThat(file_get_contents($actual), $constraint, $message); + \PHPUnit\Framework\Assert::assertTrue(...func_get_args()); } +} +if (!function_exists('PHPUnit\Framework\assertNotTrue')) { /** - * Asserts that the contents of one file is equal to the contents of another - * file (canonicalizing). + * Asserts that a condition is not true. * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException * @throws ExpectationFailedException + * + * @psalm-assert !true $condition + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertNotTrue */ - public static function assertFileEqualsCanonicalizing(string $expected, string $actual, string $message = '') : void + function assertNotTrue(mixed $condition, string $message = ''): void { - static::assertFileExists($expected, $message); - static::assertFileExists($actual, $message); - $constraint = new IsEqualCanonicalizing(file_get_contents($expected)); - static::assertThat(file_get_contents($actual), $constraint, $message); + \PHPUnit\Framework\Assert::assertNotTrue(...func_get_args()); } +} +if (!function_exists('PHPUnit\Framework\assertFalse')) { /** - * Asserts that the contents of one file is equal to the contents of another - * file (ignoring case). + * Asserts that a condition is false. * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException * @throws ExpectationFailedException + * + * @psalm-assert false $condition + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertFalse */ - public static function assertFileEqualsIgnoringCase(string $expected, string $actual, string $message = '') : void + function assertFalse(mixed $condition, string $message = ''): void { - static::assertFileExists($expected, $message); - static::assertFileExists($actual, $message); - $constraint = new IsEqualIgnoringCase(file_get_contents($expected)); - static::assertThat(file_get_contents($actual), $constraint, $message); + \PHPUnit\Framework\Assert::assertFalse(...func_get_args()); } +} +if (!function_exists('PHPUnit\Framework\assertNotFalse')) { /** - * Asserts that the contents of one file is not equal to the contents of - * another file. + * Asserts that a condition is not false. * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException * @throws ExpectationFailedException + * + * @psalm-assert !false $condition + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertNotFalse */ - public static function assertFileNotEquals(string $expected, string $actual, string $message = '') : void + function assertNotFalse(mixed $condition, string $message = ''): void { - static::assertFileExists($expected, $message); - static::assertFileExists($actual, $message); - $constraint = new LogicalNot(new IsEqual(file_get_contents($expected))); - static::assertThat(file_get_contents($actual), $constraint, $message); + \PHPUnit\Framework\Assert::assertNotFalse(...func_get_args()); } +} +if (!function_exists('PHPUnit\Framework\assertNull')) { /** - * Asserts that the contents of one file is not equal to the contents of another - * file (canonicalizing). + * Asserts that a variable is null. * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException * @throws ExpectationFailedException + * + * @psalm-assert null $actual + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertNull */ - public static function assertFileNotEqualsCanonicalizing(string $expected, string $actual, string $message = '') : void + function assertNull(mixed $actual, string $message = ''): void { - static::assertFileExists($expected, $message); - static::assertFileExists($actual, $message); - $constraint = new LogicalNot(new IsEqualCanonicalizing(file_get_contents($expected))); - static::assertThat(file_get_contents($actual), $constraint, $message); + \PHPUnit\Framework\Assert::assertNull(...func_get_args()); } +} +if (!function_exists('PHPUnit\Framework\assertNotNull')) { /** - * Asserts that the contents of one file is not equal to the contents of another - * file (ignoring case). + * Asserts that a variable is not null. * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException * @throws ExpectationFailedException + * + * @psalm-assert !null $actual + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertNotNull */ - public static function assertFileNotEqualsIgnoringCase(string $expected, string $actual, string $message = '') : void + function assertNotNull(mixed $actual, string $message = ''): void { - static::assertFileExists($expected, $message); - static::assertFileExists($actual, $message); - $constraint = new LogicalNot(new IsEqualIgnoringCase(file_get_contents($expected))); - static::assertThat(file_get_contents($actual), $constraint, $message); + \PHPUnit\Framework\Assert::assertNotNull(...func_get_args()); } +} +if (!function_exists('PHPUnit\Framework\assertFinite')) { /** - * Asserts that the contents of a string is equal - * to the contents of a file. + * Asserts that a variable is finite. * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException * @throws ExpectationFailedException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertFinite */ - public static function assertStringEqualsFile(string $expectedFile, string $actualString, string $message = '') : void + function assertFinite(mixed $actual, string $message = ''): void { - static::assertFileExists($expectedFile, $message); - $constraint = new IsEqual(file_get_contents($expectedFile)); - static::assertThat($actualString, $constraint, $message); + \PHPUnit\Framework\Assert::assertFinite(...func_get_args()); } +} +if (!function_exists('PHPUnit\Framework\assertInfinite')) { /** - * Asserts that the contents of a string is equal - * to the contents of a file (canonicalizing). + * Asserts that a variable is infinite. * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException * @throws ExpectationFailedException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertInfinite */ - public static function assertStringEqualsFileCanonicalizing(string $expectedFile, string $actualString, string $message = '') : void + function assertInfinite(mixed $actual, string $message = ''): void { - static::assertFileExists($expectedFile, $message); - $constraint = new IsEqualCanonicalizing(file_get_contents($expectedFile)); - static::assertThat($actualString, $constraint, $message); + \PHPUnit\Framework\Assert::assertInfinite(...func_get_args()); } +} +if (!function_exists('PHPUnit\Framework\assertNan')) { /** - * Asserts that the contents of a string is equal - * to the contents of a file (ignoring case). + * Asserts that a variable is nan. * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException * @throws ExpectationFailedException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertNan */ - public static function assertStringEqualsFileIgnoringCase(string $expectedFile, string $actualString, string $message = '') : void + function assertNan(mixed $actual, string $message = ''): void { - static::assertFileExists($expectedFile, $message); - $constraint = new IsEqualIgnoringCase(file_get_contents($expectedFile)); - static::assertThat($actualString, $constraint, $message); + \PHPUnit\Framework\Assert::assertNan(...func_get_args()); } +} +if (!function_exists('PHPUnit\Framework\assertObjectHasProperty')) { /** - * Asserts that the contents of a string is not equal - * to the contents of a file. + * Asserts that an object has a specified property. * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException * @throws ExpectationFailedException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertObjectHasProperty */ - public static function assertStringNotEqualsFile(string $expectedFile, string $actualString, string $message = '') : void + function assertObjectHasProperty(string $propertyName, object $object, string $message = ''): void { - static::assertFileExists($expectedFile, $message); - $constraint = new LogicalNot(new IsEqual(file_get_contents($expectedFile))); - static::assertThat($actualString, $constraint, $message); + \PHPUnit\Framework\Assert::assertObjectHasProperty(...func_get_args()); } +} +if (!function_exists('PHPUnit\Framework\assertObjectNotHasProperty')) { /** - * Asserts that the contents of a string is not equal - * to the contents of a file (canonicalizing). + * Asserts that an object does not have a specified property. * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException * @throws ExpectationFailedException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertObjectNotHasProperty */ - public static function assertStringNotEqualsFileCanonicalizing(string $expectedFile, string $actualString, string $message = '') : void + function assertObjectNotHasProperty(string $propertyName, object $object, string $message = ''): void { - static::assertFileExists($expectedFile, $message); - $constraint = new LogicalNot(new IsEqualCanonicalizing(file_get_contents($expectedFile))); - static::assertThat($actualString, $constraint, $message); + \PHPUnit\Framework\Assert::assertObjectNotHasProperty(...func_get_args()); } +} +if (!function_exists('PHPUnit\Framework\assertSame')) { /** - * Asserts that the contents of a string is not equal - * to the contents of a file (ignoring case). + * Asserts that two variables have the same type and value. + * Used on objects, it asserts that two variables reference + * the same object. * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException * @throws ExpectationFailedException + * + * @psalm-template ExpectedType + * + * @psalm-param ExpectedType $expected + * + * @psalm-assert =ExpectedType $actual + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertSame */ - public static function assertStringNotEqualsFileIgnoringCase(string $expectedFile, string $actualString, string $message = '') : void + function assertSame(mixed $expected, mixed $actual, string $message = ''): void { - static::assertFileExists($expectedFile, $message); - $constraint = new LogicalNot(new IsEqualIgnoringCase(file_get_contents($expectedFile))); - static::assertThat($actualString, $constraint, $message); + \PHPUnit\Framework\Assert::assertSame(...func_get_args()); } +} +if (!function_exists('PHPUnit\Framework\assertNotSame')) { /** - * Asserts that a file/dir is readable. + * 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 \SebastianBergmann\RecursionContext\InvalidArgumentException * @throws ExpectationFailedException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertNotSame */ - public static function assertIsReadable(string $filename, string $message = '') : void + function assertNotSame(mixed $expected, mixed $actual, string $message = ''): void { - static::assertThat($filename, new IsReadable(), $message); + \PHPUnit\Framework\Assert::assertNotSame(...func_get_args()); } +} +if (!function_exists('PHPUnit\Framework\assertInstanceOf')) { /** - * Asserts that a file/dir exists and is not readable. + * Asserts that a variable is of a given type. * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws Exception * @throws ExpectationFailedException + * @throws UnknownClassOrInterfaceException + * + * @psalm-template ExpectedType of object + * + * @psalm-param class-string $expected + * + * @psalm-assert =ExpectedType $actual + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertInstanceOf */ - public static function assertIsNotReadable(string $filename, string $message = '') : void + function assertInstanceOf(string $expected, mixed $actual, string $message = ''): void { - static::assertThat($filename, new LogicalNot(new IsReadable()), $message); + \PHPUnit\Framework\Assert::assertInstanceOf(...func_get_args()); } +} +if (!function_exists('PHPUnit\Framework\assertNotInstanceOf')) { /** - * Asserts that a file/dir exists and is not readable. + * Asserts that a variable is not of a given type. * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws Exception * @throws ExpectationFailedException * - * @codeCoverageIgnore + * @psalm-template ExpectedType of object + * + * @psalm-param class-string $expected + * + * @psalm-assert !ExpectedType $actual * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4062 + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertNotInstanceOf */ - public static function assertNotIsReadable(string $filename, string $message = '') : void + function assertNotInstanceOf(string $expected, mixed $actual, string $message = ''): void { - self::createWarning('assertNotIsReadable() is deprecated and will be removed in PHPUnit 10. Refactor your code to use assertIsNotReadable() instead.'); - static::assertThat($filename, new LogicalNot(new IsReadable()), $message); + \PHPUnit\Framework\Assert::assertNotInstanceOf(...func_get_args()); } +} +if (!function_exists('PHPUnit\Framework\assertIsArray')) { /** - * Asserts that a file/dir exists and is writable. + * Asserts that a variable is of type array. * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws Exception * @throws ExpectationFailedException + * + * @psalm-assert array $actual + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertIsArray */ - public static function assertIsWritable(string $filename, string $message = '') : void + function assertIsArray(mixed $actual, string $message = ''): void { - static::assertThat($filename, new IsWritable(), $message); + \PHPUnit\Framework\Assert::assertIsArray(...func_get_args()); } +} +if (!function_exists('PHPUnit\Framework\assertIsBool')) { /** - * Asserts that a file/dir exists and is not writable. + * Asserts that a variable is of type bool. * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws Exception * @throws ExpectationFailedException + * + * @psalm-assert bool $actual + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertIsBool */ - public static function assertIsNotWritable(string $filename, string $message = '') : void + function assertIsBool(mixed $actual, string $message = ''): void { - static::assertThat($filename, new LogicalNot(new IsWritable()), $message); + \PHPUnit\Framework\Assert::assertIsBool(...func_get_args()); } +} +if (!function_exists('PHPUnit\Framework\assertIsFloat')) { /** - * Asserts that a file/dir exists and is not writable. + * Asserts that a variable is of type float. * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws Exception * @throws ExpectationFailedException * - * @codeCoverageIgnore + * @psalm-assert float $actual + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4065 + * @see Assert::assertIsFloat */ - public static function assertNotIsWritable(string $filename, string $message = '') : void + function assertIsFloat(mixed $actual, string $message = ''): void { - self::createWarning('assertNotIsWritable() is deprecated and will be removed in PHPUnit 10. Refactor your code to use assertIsNotWritable() instead.'); - static::assertThat($filename, new LogicalNot(new IsWritable()), $message); + \PHPUnit\Framework\Assert::assertIsFloat(...func_get_args()); } +} +if (!function_exists('PHPUnit\Framework\assertIsInt')) { /** - * Asserts that a directory exists. + * Asserts that a variable is of type int. * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws Exception * @throws ExpectationFailedException + * + * @psalm-assert int $actual + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertIsInt */ - public static function assertDirectoryExists(string $directory, string $message = '') : void + function assertIsInt(mixed $actual, string $message = ''): void { - static::assertThat($directory, new DirectoryExists(), $message); + \PHPUnit\Framework\Assert::assertIsInt(...func_get_args()); } +} +if (!function_exists('PHPUnit\Framework\assertIsNumeric')) { /** - * Asserts that a directory does not exist. + * Asserts that a variable is of type numeric. * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws Exception * @throws ExpectationFailedException + * + * @psalm-assert numeric $actual + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertIsNumeric */ - public static function assertDirectoryDoesNotExist(string $directory, string $message = '') : void + function assertIsNumeric(mixed $actual, string $message = ''): void { - static::assertThat($directory, new LogicalNot(new DirectoryExists()), $message); + \PHPUnit\Framework\Assert::assertIsNumeric(...func_get_args()); } +} +if (!function_exists('PHPUnit\Framework\assertIsObject')) { /** - * Asserts that a directory does not exist. + * Asserts that a variable is of type object. * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws Exception * @throws ExpectationFailedException * - * @codeCoverageIgnore + * @psalm-assert object $actual + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4068 + * @see Assert::assertIsObject */ - public static function assertDirectoryNotExists(string $directory, string $message = '') : void + function assertIsObject(mixed $actual, string $message = ''): void { - self::createWarning('assertDirectoryNotExists() is deprecated and will be removed in PHPUnit 10. Refactor your code to use assertDirectoryDoesNotExist() instead.'); - static::assertThat($directory, new LogicalNot(new DirectoryExists()), $message); + \PHPUnit\Framework\Assert::assertIsObject(...func_get_args()); } +} +if (!function_exists('PHPUnit\Framework\assertIsResource')) { /** - * Asserts that a directory exists and is readable. + * Asserts that a variable is of type resource. * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws Exception * @throws ExpectationFailedException + * + * @psalm-assert resource $actual + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertIsResource */ - public static function assertDirectoryIsReadable(string $directory, string $message = '') : void + function assertIsResource(mixed $actual, string $message = ''): void { - self::assertDirectoryExists($directory, $message); - self::assertIsReadable($directory, $message); + \PHPUnit\Framework\Assert::assertIsResource(...func_get_args()); } +} +if (!function_exists('PHPUnit\Framework\assertIsClosedResource')) { /** - * Asserts that a directory exists and is not readable. + * Asserts that a variable is of type resource and is closed. * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws Exception * @throws ExpectationFailedException + * + * @psalm-assert resource $actual + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertIsClosedResource */ - public static function assertDirectoryIsNotReadable(string $directory, string $message = '') : void + function assertIsClosedResource(mixed $actual, string $message = ''): void { - self::assertDirectoryExists($directory, $message); - self::assertIsNotReadable($directory, $message); + \PHPUnit\Framework\Assert::assertIsClosedResource(...func_get_args()); } +} +if (!function_exists('PHPUnit\Framework\assertIsString')) { /** - * Asserts that a directory exists and is not readable. + * Asserts that a variable is of type string. * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws Exception * @throws ExpectationFailedException * - * @codeCoverageIgnore + * @psalm-assert string $actual + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4071 + * @see Assert::assertIsString */ - public static function assertDirectoryNotIsReadable(string $directory, string $message = '') : void + function assertIsString(mixed $actual, string $message = ''): void { - self::createWarning('assertDirectoryNotIsReadable() is deprecated and will be removed in PHPUnit 10. Refactor your code to use assertDirectoryIsNotReadable() instead.'); - self::assertDirectoryExists($directory, $message); - self::assertIsNotReadable($directory, $message); + \PHPUnit\Framework\Assert::assertIsString(...func_get_args()); } +} +if (!function_exists('PHPUnit\Framework\assertIsScalar')) { /** - * Asserts that a directory exists and is writable. + * Asserts that a variable is of type scalar. * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws Exception * @throws ExpectationFailedException + * + * @psalm-assert scalar $actual + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertIsScalar */ - public static function assertDirectoryIsWritable(string $directory, string $message = '') : void + function assertIsScalar(mixed $actual, string $message = ''): void { - self::assertDirectoryExists($directory, $message); - self::assertIsWritable($directory, $message); + \PHPUnit\Framework\Assert::assertIsScalar(...func_get_args()); } +} +if (!function_exists('PHPUnit\Framework\assertIsCallable')) { /** - * Asserts that a directory exists and is not writable. + * Asserts that a variable is of type callable. * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws Exception * @throws ExpectationFailedException + * + * @psalm-assert callable $actual + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertIsCallable */ - public static function assertDirectoryIsNotWritable(string $directory, string $message = '') : void + function assertIsCallable(mixed $actual, string $message = ''): void { - self::assertDirectoryExists($directory, $message); - self::assertIsNotWritable($directory, $message); + \PHPUnit\Framework\Assert::assertIsCallable(...func_get_args()); } +} +if (!function_exists('PHPUnit\Framework\assertIsIterable')) { /** - * Asserts that a directory exists and is not writable. + * Asserts that a variable is of type iterable. * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws Exception * @throws ExpectationFailedException * - * @codeCoverageIgnore + * @psalm-assert iterable $actual + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4074 + * @see Assert::assertIsIterable */ - public static function assertDirectoryNotIsWritable(string $directory, string $message = '') : void + function assertIsIterable(mixed $actual, string $message = ''): void { - self::createWarning('assertDirectoryNotIsWritable() is deprecated and will be removed in PHPUnit 10. Refactor your code to use assertDirectoryIsNotWritable() instead.'); - self::assertDirectoryExists($directory, $message); - self::assertIsNotWritable($directory, $message); + \PHPUnit\Framework\Assert::assertIsIterable(...func_get_args()); } +} +if (!function_exists('PHPUnit\Framework\assertIsNotArray')) { /** - * Asserts that a file exists. + * Asserts that a variable is not of type array. * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws Exception * @throws ExpectationFailedException + * + * @psalm-assert !array $actual + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertIsNotArray */ - public static function assertFileExists(string $filename, string $message = '') : void + function assertIsNotArray(mixed $actual, string $message = ''): void { - static::assertThat($filename, new FileExists(), $message); + \PHPUnit\Framework\Assert::assertIsNotArray(...func_get_args()); } +} +if (!function_exists('PHPUnit\Framework\assertIsNotBool')) { /** - * Asserts that a file does not exist. + * Asserts that a variable is not of type bool. * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws Exception * @throws ExpectationFailedException + * + * @psalm-assert !bool $actual + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertIsNotBool */ - public static function assertFileDoesNotExist(string $filename, string $message = '') : void + function assertIsNotBool(mixed $actual, string $message = ''): void { - static::assertThat($filename, new LogicalNot(new FileExists()), $message); + \PHPUnit\Framework\Assert::assertIsNotBool(...func_get_args()); } +} +if (!function_exists('PHPUnit\Framework\assertIsNotFloat')) { /** - * Asserts that a file does not exist. + * Asserts that a variable is not of type float. * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws Exception * @throws ExpectationFailedException * - * @codeCoverageIgnore + * @psalm-assert !float $actual + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4077 + * @see Assert::assertIsNotFloat */ - public static function assertFileNotExists(string $filename, string $message = '') : void + function assertIsNotFloat(mixed $actual, string $message = ''): void { - self::createWarning('assertFileNotExists() is deprecated and will be removed in PHPUnit 10. Refactor your code to use assertFileDoesNotExist() instead.'); - static::assertThat($filename, new LogicalNot(new FileExists()), $message); + \PHPUnit\Framework\Assert::assertIsNotFloat(...func_get_args()); } +} +if (!function_exists('PHPUnit\Framework\assertIsNotInt')) { /** - * Asserts that a file exists and is readable. + * Asserts that a variable is not of type int. * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws Exception * @throws ExpectationFailedException + * + * @psalm-assert !int $actual + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertIsNotInt */ - public static function assertFileIsReadable(string $file, string $message = '') : void + function assertIsNotInt(mixed $actual, string $message = ''): void { - self::assertFileExists($file, $message); - self::assertIsReadable($file, $message); + \PHPUnit\Framework\Assert::assertIsNotInt(...func_get_args()); } +} +if (!function_exists('PHPUnit\Framework\assertIsNotNumeric')) { /** - * Asserts that a file exists and is not readable. + * Asserts that a variable is not of type numeric. * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws Exception * @throws ExpectationFailedException + * + * @psalm-assert !numeric $actual + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertIsNotNumeric */ - public static function assertFileIsNotReadable(string $file, string $message = '') : void + function assertIsNotNumeric(mixed $actual, string $message = ''): void { - self::assertFileExists($file, $message); - self::assertIsNotReadable($file, $message); + \PHPUnit\Framework\Assert::assertIsNotNumeric(...func_get_args()); } +} +if (!function_exists('PHPUnit\Framework\assertIsNotObject')) { /** - * Asserts that a file exists and is not readable. + * Asserts that a variable is not of type object. * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws Exception * @throws ExpectationFailedException * - * @codeCoverageIgnore + * @psalm-assert !object $actual + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4080 + * @see Assert::assertIsNotObject */ - public static function assertFileNotIsReadable(string $file, string $message = '') : void + function assertIsNotObject(mixed $actual, string $message = ''): void { - self::createWarning('assertFileNotIsReadable() is deprecated and will be removed in PHPUnit 10. Refactor your code to use assertFileIsNotReadable() instead.'); - self::assertFileExists($file, $message); - self::assertIsNotReadable($file, $message); + \PHPUnit\Framework\Assert::assertIsNotObject(...func_get_args()); } +} +if (!function_exists('PHPUnit\Framework\assertIsNotResource')) { /** - * Asserts that a file exists and is writable. + * Asserts that a variable is not of type resource. * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws Exception * @throws ExpectationFailedException + * + * @psalm-assert !resource $actual + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertIsNotResource */ - public static function assertFileIsWritable(string $file, string $message = '') : void + function assertIsNotResource(mixed $actual, string $message = ''): void { - self::assertFileExists($file, $message); - self::assertIsWritable($file, $message); + \PHPUnit\Framework\Assert::assertIsNotResource(...func_get_args()); } +} +if (!function_exists('PHPUnit\Framework\assertIsNotClosedResource')) { /** - * Asserts that a file exists and is not writable. + * Asserts that a variable is not of type resource. * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws Exception * @throws ExpectationFailedException + * + * @psalm-assert !resource $actual + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertIsNotClosedResource */ - public static function assertFileIsNotWritable(string $file, string $message = '') : void + function assertIsNotClosedResource(mixed $actual, string $message = ''): void { - self::assertFileExists($file, $message); - self::assertIsNotWritable($file, $message); + \PHPUnit\Framework\Assert::assertIsNotClosedResource(...func_get_args()); } +} +if (!function_exists('PHPUnit\Framework\assertIsNotString')) { /** - * Asserts that a file exists and is not writable. + * Asserts that a variable is not of type string. * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws Exception * @throws ExpectationFailedException * - * @codeCoverageIgnore + * @psalm-assert !string $actual * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4083 + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertIsNotString */ - public static function assertFileNotIsWritable(string $file, string $message = '') : void + function assertIsNotString(mixed $actual, string $message = ''): void { - self::createWarning('assertFileNotIsWritable() is deprecated and will be removed in PHPUnit 10. Refactor your code to use assertFileIsNotWritable() instead.'); - self::assertFileExists($file, $message); - self::assertIsNotWritable($file, $message); + \PHPUnit\Framework\Assert::assertIsNotString(...func_get_args()); } +} +if (!function_exists('PHPUnit\Framework\assertIsNotScalar')) { /** - * Asserts that a condition is true. + * Asserts that a variable is not of type scalar. * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws Exception * @throws ExpectationFailedException * - * @psalm-assert true $condition + * @psalm-assert !scalar $actual + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertIsNotScalar */ - public static function assertTrue($condition, string $message = '') : void + function assertIsNotScalar(mixed $actual, string $message = ''): void { - static::assertThat($condition, static::isTrue(), $message); + \PHPUnit\Framework\Assert::assertIsNotScalar(...func_get_args()); } +} +if (!function_exists('PHPUnit\Framework\assertIsNotCallable')) { /** - * Asserts that a condition is not true. + * Asserts that a variable is not of type callable. * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws Exception * @throws ExpectationFailedException * - * @psalm-assert !true $condition + * @psalm-assert !callable $actual + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertIsNotCallable */ - public static function assertNotTrue($condition, string $message = '') : void + function assertIsNotCallable(mixed $actual, string $message = ''): void { - static::assertThat($condition, static::logicalNot(static::isTrue()), $message); + \PHPUnit\Framework\Assert::assertIsNotCallable(...func_get_args()); } +} +if (!function_exists('PHPUnit\Framework\assertIsNotIterable')) { /** - * Asserts that a condition is false. + * Asserts that a variable is not of type iterable. * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws Exception * @throws ExpectationFailedException * - * @psalm-assert false $condition + * @psalm-assert !iterable $actual + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertIsNotIterable */ - public static function assertFalse($condition, string $message = '') : void + function assertIsNotIterable(mixed $actual, string $message = ''): void { - static::assertThat($condition, static::isFalse(), $message); + \PHPUnit\Framework\Assert::assertIsNotIterable(...func_get_args()); } +} +if (!function_exists('PHPUnit\Framework\assertMatchesRegularExpression')) { /** - * Asserts that a condition is not false. + * Asserts that a string matches a given regular expression. * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException * @throws ExpectationFailedException * - * @psalm-assert !false $condition + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertMatchesRegularExpression */ - public static function assertNotFalse($condition, string $message = '') : void + function assertMatchesRegularExpression(string $pattern, string $string, string $message = ''): void { - static::assertThat($condition, static::logicalNot(static::isFalse()), $message); + \PHPUnit\Framework\Assert::assertMatchesRegularExpression(...func_get_args()); } +} +if (!function_exists('PHPUnit\Framework\assertDoesNotMatchRegularExpression')) { /** - * Asserts that a variable is null. + * Asserts that a string does not match a given regular expression. * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException * @throws ExpectationFailedException * - * @psalm-assert null $actual + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertDoesNotMatchRegularExpression */ - public static function assertNull($actual, string $message = '') : void + function assertDoesNotMatchRegularExpression(string $pattern, string $string, string $message = ''): void { - static::assertThat($actual, static::isNull(), $message); + \PHPUnit\Framework\Assert::assertDoesNotMatchRegularExpression(...func_get_args()); } +} +if (!function_exists('PHPUnit\Framework\assertSameSize')) { /** - * Asserts that a variable is not null. + * Assert that the size of two arrays (or `Countable` or `Traversable` objects) + * is the same. * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws Exception * @throws ExpectationFailedException + * @throws GeneratorNotSupportedException * - * @psalm-assert !null $actual + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertSameSize */ - public static function assertNotNull($actual, string $message = '') : void + function assertSameSize(Countable|iterable $expected, Countable|iterable $actual, string $message = ''): void { - static::assertThat($actual, static::logicalNot(static::isNull()), $message); + \PHPUnit\Framework\Assert::assertSameSize(...func_get_args()); } +} +if (!function_exists('PHPUnit\Framework\assertNotSameSize')) { /** - * Asserts that a variable is finite. + * Assert that the size of two arrays (or `Countable` or `Traversable` objects) + * is not the same. * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws Exception * @throws ExpectationFailedException + * @throws GeneratorNotSupportedException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertNotSameSize */ - public static function assertFinite($actual, string $message = '') : void + function assertNotSameSize(Countable|iterable $expected, Countable|iterable $actual, string $message = ''): void { - static::assertThat($actual, static::isFinite(), $message); + \PHPUnit\Framework\Assert::assertNotSameSize(...func_get_args()); } +} +if (!function_exists('PHPUnit\Framework\assertStringContainsStringIgnoringLineEndings')) { /** - * Asserts that a variable is infinite. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException * @throws ExpectationFailedException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertStringContainsStringIgnoringLineEndings */ - public static function assertInfinite($actual, string $message = '') : void + function assertStringContainsStringIgnoringLineEndings(string $needle, string $haystack, string $message = ''): void { - static::assertThat($actual, static::isInfinite(), $message); + \PHPUnit\Framework\Assert::assertStringContainsStringIgnoringLineEndings(...func_get_args()); } +} +if (!function_exists('PHPUnit\Framework\assertStringEqualsStringIgnoringLineEndings')) { /** - * Asserts that a variable is nan. + * Asserts that two strings are equal except for line endings. * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException * @throws ExpectationFailedException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertStringEqualsStringIgnoringLineEndings */ - public static function assertNan($actual, string $message = '') : void + function assertStringEqualsStringIgnoringLineEndings(string $expected, string $actual, string $message = ''): void { - static::assertThat($actual, static::isNan(), $message); + \PHPUnit\Framework\Assert::assertStringEqualsStringIgnoringLineEndings(...func_get_args()); } +} +if (!function_exists('PHPUnit\Framework\assertFileMatchesFormat')) { /** - * Asserts that a class has a specified attribute. + * Asserts that a string matches a given format string. * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws Exception * @throws ExpectationFailedException * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4601 + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertFileMatchesFormat */ - public static function assertClassHasAttribute(string $attributeName, string $className, string $message = '') : void + function assertFileMatchesFormat(string $format, string $actualFile, string $message = ''): void { - self::createWarning('assertClassHasAttribute() is deprecated and will be removed in PHPUnit 10.'); - if (!self::isValidClassAttributeName($attributeName)) { - throw \PHPUnit\Framework\InvalidArgumentException::create(1, 'valid attribute name'); - } - if (!class_exists($className)) { - throw \PHPUnit\Framework\InvalidArgumentException::create(2, 'class name'); - } - static::assertThat($className, new ClassHasAttribute($attributeName), $message); + \PHPUnit\Framework\Assert::assertFileMatchesFormat(...func_get_args()); } +} +if (!function_exists('PHPUnit\Framework\assertFileMatchesFormatFile')) { /** - * Asserts that a class does not have a specified attribute. + * Asserts that a string matches a given format string. * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws Exception * @throws ExpectationFailedException * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4601 + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertFileMatchesFormatFile */ - public static function assertClassNotHasAttribute(string $attributeName, string $className, string $message = '') : void + function assertFileMatchesFormatFile(string $formatFile, string $actualFile, string $message = ''): void { - self::createWarning('assertClassNotHasAttribute() is deprecated and will be removed in PHPUnit 10.'); - if (!self::isValidClassAttributeName($attributeName)) { - throw \PHPUnit\Framework\InvalidArgumentException::create(1, 'valid attribute name'); - } - if (!class_exists($className)) { - throw \PHPUnit\Framework\InvalidArgumentException::create(2, 'class name'); - } - static::assertThat($className, new LogicalNot(new ClassHasAttribute($attributeName)), $message); + \PHPUnit\Framework\Assert::assertFileMatchesFormatFile(...func_get_args()); } +} +if (!function_exists('PHPUnit\Framework\assertStringMatchesFormat')) { /** - * Asserts that a class has a specified static attribute. + * Asserts that a string matches a given format string. * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws Exception * @throws ExpectationFailedException * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4601 + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertStringMatchesFormat */ - public static function assertClassHasStaticAttribute(string $attributeName, string $className, string $message = '') : void + function assertStringMatchesFormat(string $format, string $string, string $message = ''): void { - self::createWarning('assertClassHasStaticAttribute() is deprecated and will be removed in PHPUnit 10.'); - if (!self::isValidClassAttributeName($attributeName)) { - throw \PHPUnit\Framework\InvalidArgumentException::create(1, 'valid attribute name'); - } - if (!class_exists($className)) { - throw \PHPUnit\Framework\InvalidArgumentException::create(2, 'class name'); - } - static::assertThat($className, new ClassHasStaticAttribute($attributeName), $message); + \PHPUnit\Framework\Assert::assertStringMatchesFormat(...func_get_args()); } +} +if (!function_exists('PHPUnit\Framework\assertStringNotMatchesFormat')) { /** - * Asserts that a class does not have a specified static attribute. + * Asserts that a string does not match a given format string. * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws Exception * @throws ExpectationFailedException * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4601 + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/5472 + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertStringNotMatchesFormat */ - public static function assertClassNotHasStaticAttribute(string $attributeName, string $className, string $message = '') : void + function assertStringNotMatchesFormat(string $format, string $string, string $message = ''): void { - self::createWarning('assertClassNotHasStaticAttribute() is deprecated and will be removed in PHPUnit 10.'); - if (!self::isValidClassAttributeName($attributeName)) { - throw \PHPUnit\Framework\InvalidArgumentException::create(1, 'valid attribute name'); - } - if (!class_exists($className)) { - throw \PHPUnit\Framework\InvalidArgumentException::create(2, 'class name'); - } - static::assertThat($className, new LogicalNot(new ClassHasStaticAttribute($attributeName)), $message); + \PHPUnit\Framework\Assert::assertStringNotMatchesFormat(...func_get_args()); } +} +if (!function_exists('PHPUnit\Framework\assertStringMatchesFormatFile')) { /** - * Asserts that an object has a specified attribute. - * - * @param object $object + * Asserts that a string matches a given format file. * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws Exception * @throws ExpectationFailedException * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4601 + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertStringMatchesFormatFile */ - public static function assertObjectHasAttribute(string $attributeName, $object, string $message = '') : void + function assertStringMatchesFormatFile(string $formatFile, string $string, string $message = ''): void { - self::createWarning('assertObjectHasAttribute() is deprecated and will be removed in PHPUnit 10. Refactor your test to use assertObjectHasProperty() instead.'); - if (!self::isValidObjectAttributeName($attributeName)) { - throw \PHPUnit\Framework\InvalidArgumentException::create(1, 'valid attribute name'); - } - if (!is_object($object)) { - throw \PHPUnit\Framework\InvalidArgumentException::create(2, 'object'); - } - static::assertThat($object, new ObjectHasAttribute($attributeName), $message); + \PHPUnit\Framework\Assert::assertStringMatchesFormatFile(...func_get_args()); } +} +if (!function_exists('PHPUnit\Framework\assertStringNotMatchesFormatFile')) { /** - * Asserts that an object does not have a specified attribute. - * - * @param object $object + * Asserts that a string does not match a given format string. * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws Exception * @throws ExpectationFailedException * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4601 + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/5472 + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertStringNotMatchesFormatFile */ - public static function assertObjectNotHasAttribute(string $attributeName, $object, string $message = '') : void + function assertStringNotMatchesFormatFile(string $formatFile, string $string, string $message = ''): void { - self::createWarning('assertObjectNotHasAttribute() is deprecated and will be removed in PHPUnit 10. Refactor your test to use assertObjectNotHasProperty() instead.'); - if (!self::isValidObjectAttributeName($attributeName)) { - throw \PHPUnit\Framework\InvalidArgumentException::create(1, 'valid attribute name'); - } - if (!is_object($object)) { - throw \PHPUnit\Framework\InvalidArgumentException::create(2, 'object'); - } - static::assertThat($object, new LogicalNot(new ObjectHasAttribute($attributeName)), $message); - } - /** - * Asserts that an object has a specified property. - * - * @throws ExpectationFailedException - */ - public static final function assertObjectHasProperty(string $propertyName, object $object, string $message = '') : void - { - static::assertThat($object, new ObjectHasProperty($propertyName), $message); + \PHPUnit\Framework\Assert::assertStringNotMatchesFormatFile(...func_get_args()); } +} +if (!function_exists('PHPUnit\Framework\assertStringStartsWith')) { /** - * Asserts that an object does not have a specified property. + * Asserts that a string starts with a given prefix. * - * @throws ExpectationFailedException - */ - public static final function assertObjectNotHasProperty(string $propertyName, object $object, string $message = '') : void - { - static::assertThat($object, new LogicalNot(new ObjectHasProperty($propertyName)), $message); - } - /** - * Asserts that two variables have the same type and value. - * Used on objects, it asserts that two variables reference - * the same object. + * @psalm-param non-empty-string $prefix * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException * @throws ExpectationFailedException + * @throws InvalidArgumentException * - * @psalm-template ExpectedType - * - * @psalm-param ExpectedType $expected + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit * - * @psalm-assert =ExpectedType $actual + * @see Assert::assertStringStartsWith */ - public static function assertSame($expected, $actual, string $message = '') : void + function assertStringStartsWith(string $prefix, string $string, string $message = ''): void { - static::assertThat($actual, new IsIdentical($expected), $message); + \PHPUnit\Framework\Assert::assertStringStartsWith(...func_get_args()); } +} +if (!function_exists('PHPUnit\Framework\assertStringStartsNotWith')) { /** - * 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. + * Asserts that a string starts not with a given prefix. * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - */ - 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 is of a given type. + * @psalm-param non-empty-string $prefix * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws Exception * @throws ExpectationFailedException + * @throws InvalidArgumentException * - * @psalm-template ExpectedType of object - * - * @psalm-param class-string $expected + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit * - * @psalm-assert =ExpectedType $actual + * @see Assert::assertStringStartsNotWith */ - public static function assertInstanceOf(string $expected, $actual, string $message = '') : void + function assertStringStartsNotWith(string $prefix, string $string, string $message = ''): void { - if (!class_exists($expected) && !interface_exists($expected)) { - throw \PHPUnit\Framework\InvalidArgumentException::create(1, 'class or interface name'); - } - static::assertThat($actual, new IsInstanceOf($expected), $message); + \PHPUnit\Framework\Assert::assertStringStartsNotWith(...func_get_args()); } +} +if (!function_exists('PHPUnit\Framework\assertStringContainsString')) { /** - * Asserts that a variable is not of a given type. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws Exception * @throws ExpectationFailedException * - * @psalm-template ExpectedType of object - * - * @psalm-param class-string $expected + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit * - * @psalm-assert !ExpectedType $actual + * @see Assert::assertStringContainsString */ - public static function assertNotInstanceOf(string $expected, $actual, string $message = '') : void + function assertStringContainsString(string $needle, string $haystack, string $message = ''): void { - if (!class_exists($expected) && !interface_exists($expected)) { - throw \PHPUnit\Framework\InvalidArgumentException::create(1, 'class or interface name'); - } - static::assertThat($actual, new LogicalNot(new IsInstanceOf($expected)), $message); + \PHPUnit\Framework\Assert::assertStringContainsString(...func_get_args()); } +} +if (!function_exists('PHPUnit\Framework\assertStringContainsStringIgnoringCase')) { /** - * Asserts that a variable is of type array. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException * @throws ExpectationFailedException * - * @psalm-assert array $actual + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertStringContainsStringIgnoringCase */ - public static function assertIsArray($actual, string $message = '') : void + function assertStringContainsStringIgnoringCase(string $needle, string $haystack, string $message = ''): void { - static::assertThat($actual, new IsType(IsType::TYPE_ARRAY), $message); + \PHPUnit\Framework\Assert::assertStringContainsStringIgnoringCase(...func_get_args()); } +} +if (!function_exists('PHPUnit\Framework\assertStringNotContainsString')) { /** - * Asserts that a variable is of type bool. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException * @throws ExpectationFailedException * - * @psalm-assert bool $actual + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertStringNotContainsString */ - public static function assertIsBool($actual, string $message = '') : void + function assertStringNotContainsString(string $needle, string $haystack, string $message = ''): void { - static::assertThat($actual, new IsType(IsType::TYPE_BOOL), $message); + \PHPUnit\Framework\Assert::assertStringNotContainsString(...func_get_args()); } +} +if (!function_exists('PHPUnit\Framework\assertStringNotContainsStringIgnoringCase')) { /** - * Asserts that a variable is of type float. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException * @throws ExpectationFailedException * - * @psalm-assert float $actual + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertStringNotContainsStringIgnoringCase */ - public static function assertIsFloat($actual, string $message = '') : void + function assertStringNotContainsStringIgnoringCase(string $needle, string $haystack, string $message = ''): void { - static::assertThat($actual, new IsType(IsType::TYPE_FLOAT), $message); + \PHPUnit\Framework\Assert::assertStringNotContainsStringIgnoringCase(...func_get_args()); } +} +if (!function_exists('PHPUnit\Framework\assertStringEndsWith')) { /** - * Asserts that a variable is of type int. + * Asserts that a string ends with a given suffix. + * + * @psalm-param non-empty-string $suffix * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException * @throws ExpectationFailedException + * @throws InvalidArgumentException * - * @psalm-assert int $actual + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertStringEndsWith */ - public static function assertIsInt($actual, string $message = '') : void + function assertStringEndsWith(string $suffix, string $string, string $message = ''): void { - static::assertThat($actual, new IsType(IsType::TYPE_INT), $message); + \PHPUnit\Framework\Assert::assertStringEndsWith(...func_get_args()); } +} +if (!function_exists('PHPUnit\Framework\assertStringEndsNotWith')) { /** - * Asserts that a variable is of type numeric. + * Asserts that a string ends not with a given suffix. + * + * @psalm-param non-empty-string $suffix * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException * @throws ExpectationFailedException + * @throws InvalidArgumentException * - * @psalm-assert numeric $actual + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertStringEndsNotWith */ - public static function assertIsNumeric($actual, string $message = '') : void + function assertStringEndsNotWith(string $suffix, string $string, string $message = ''): void { - static::assertThat($actual, new IsType(IsType::TYPE_NUMERIC), $message); + \PHPUnit\Framework\Assert::assertStringEndsNotWith(...func_get_args()); } +} +if (!function_exists('PHPUnit\Framework\assertXmlFileEqualsXmlFile')) { /** - * Asserts that a variable is of type object. + * Asserts that two XML files are equal. * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws Exception * @throws ExpectationFailedException + * @throws XmlException * - * @psalm-assert object $actual + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertXmlFileEqualsXmlFile */ - public static function assertIsObject($actual, string $message = '') : void + function assertXmlFileEqualsXmlFile(string $expectedFile, string $actualFile, string $message = ''): void { - static::assertThat($actual, new IsType(IsType::TYPE_OBJECT), $message); + \PHPUnit\Framework\Assert::assertXmlFileEqualsXmlFile(...func_get_args()); } +} +if (!function_exists('PHPUnit\Framework\assertXmlFileNotEqualsXmlFile')) { /** - * Asserts that a variable is of type resource. + * Asserts that two XML files are not equal. * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws \PHPUnit\Util\Exception * @throws ExpectationFailedException * - * @psalm-assert resource $actual + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertXmlFileNotEqualsXmlFile */ - public static function assertIsResource($actual, string $message = '') : void + function assertXmlFileNotEqualsXmlFile(string $expectedFile, string $actualFile, string $message = ''): void { - static::assertThat($actual, new IsType(IsType::TYPE_RESOURCE), $message); + \PHPUnit\Framework\Assert::assertXmlFileNotEqualsXmlFile(...func_get_args()); } +} +if (!function_exists('PHPUnit\Framework\assertXmlStringEqualsXmlFile')) { /** - * Asserts that a variable is of type resource and is closed. + * Asserts that two XML documents are equal. * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException * @throws ExpectationFailedException + * @throws XmlException * - * @psalm-assert resource $actual + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertXmlStringEqualsXmlFile */ - public static function assertIsClosedResource($actual, string $message = '') : void + function assertXmlStringEqualsXmlFile(string $expectedFile, string $actualXml, string $message = ''): void { - static::assertThat($actual, new IsType(IsType::TYPE_CLOSED_RESOURCE), $message); + \PHPUnit\Framework\Assert::assertXmlStringEqualsXmlFile(...func_get_args()); } +} +if (!function_exists('PHPUnit\Framework\assertXmlStringNotEqualsXmlFile')) { /** - * Asserts that a variable is of type string. + * Asserts that two XML documents are not equal. * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException * @throws ExpectationFailedException + * @throws XmlException * - * @psalm-assert string $actual + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertXmlStringNotEqualsXmlFile */ - public static function assertIsString($actual, string $message = '') : void + function assertXmlStringNotEqualsXmlFile(string $expectedFile, string $actualXml, string $message = ''): void { - static::assertThat($actual, new IsType(IsType::TYPE_STRING), $message); + \PHPUnit\Framework\Assert::assertXmlStringNotEqualsXmlFile(...func_get_args()); } +} +if (!function_exists('PHPUnit\Framework\assertXmlStringEqualsXmlString')) { /** - * Asserts that a variable is of type scalar. + * Asserts that two XML documents are equal. * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException * @throws ExpectationFailedException + * @throws XmlException * - * @psalm-assert scalar $actual + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertXmlStringEqualsXmlString */ - public static function assertIsScalar($actual, string $message = '') : void + function assertXmlStringEqualsXmlString(string $expectedXml, string $actualXml, string $message = ''): void { - static::assertThat($actual, new IsType(IsType::TYPE_SCALAR), $message); + \PHPUnit\Framework\Assert::assertXmlStringEqualsXmlString(...func_get_args()); } +} +if (!function_exists('PHPUnit\Framework\assertXmlStringNotEqualsXmlString')) { /** - * Asserts that a variable is of type callable. + * Asserts that two XML documents are not equal. * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException * @throws ExpectationFailedException + * @throws XmlException * - * @psalm-assert callable $actual + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertXmlStringNotEqualsXmlString */ - public static function assertIsCallable($actual, string $message = '') : void + function assertXmlStringNotEqualsXmlString(string $expectedXml, string $actualXml, string $message = ''): void { - static::assertThat($actual, new IsType(IsType::TYPE_CALLABLE), $message); + \PHPUnit\Framework\Assert::assertXmlStringNotEqualsXmlString(...func_get_args()); } +} +if (!function_exists('PHPUnit\Framework\assertThat')) { /** - * Asserts that a variable is of type iterable. + * Evaluates a PHPUnit\Framework\Constraint matcher object. * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException * @throws ExpectationFailedException * - * @psalm-assert iterable $actual + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertThat */ - public static function assertIsIterable($actual, string $message = '') : void + function assertThat(mixed $value, Constraint $constraint, string $message = ''): void { - static::assertThat($actual, new IsType(IsType::TYPE_ITERABLE), $message); + \PHPUnit\Framework\Assert::assertThat(...func_get_args()); } +} +if (!function_exists('PHPUnit\Framework\assertJson')) { /** - * Asserts that a variable is not of type array. + * Asserts that a string is a valid JSON string. * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException * @throws ExpectationFailedException * - * @psalm-assert !array $actual + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertJson */ - public static function assertIsNotArray($actual, string $message = '') : void + function assertJson(string $actual, string $message = ''): void { - static::assertThat($actual, new LogicalNot(new IsType(IsType::TYPE_ARRAY)), $message); + \PHPUnit\Framework\Assert::assertJson(...func_get_args()); } +} +if (!function_exists('PHPUnit\Framework\assertJsonStringEqualsJsonString')) { /** - * Asserts that a variable is not of type bool. + * Asserts that two given JSON encoded objects or arrays are equal. * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException * @throws ExpectationFailedException * - * @psalm-assert !bool $actual + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertJsonStringEqualsJsonString */ - public static function assertIsNotBool($actual, string $message = '') : void + function assertJsonStringEqualsJsonString(string $expectedJson, string $actualJson, string $message = ''): void { - static::assertThat($actual, new LogicalNot(new IsType(IsType::TYPE_BOOL)), $message); + \PHPUnit\Framework\Assert::assertJsonStringEqualsJsonString(...func_get_args()); } +} +if (!function_exists('PHPUnit\Framework\assertJsonStringNotEqualsJsonString')) { /** - * Asserts that a variable is not of type float. + * Asserts that two given JSON encoded objects or arrays are not equal. * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException * @throws ExpectationFailedException * - * @psalm-assert !float $actual + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertJsonStringNotEqualsJsonString */ - public static function assertIsNotFloat($actual, string $message = '') : void + function assertJsonStringNotEqualsJsonString(string $expectedJson, string $actualJson, string $message = ''): void { - static::assertThat($actual, new LogicalNot(new IsType(IsType::TYPE_FLOAT)), $message); + \PHPUnit\Framework\Assert::assertJsonStringNotEqualsJsonString(...func_get_args()); } +} +if (!function_exists('PHPUnit\Framework\assertJsonStringEqualsJsonFile')) { /** - * Asserts that a variable is not of type int. + * Asserts that the generated JSON encoded object and the content of the given file are equal. * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException * @throws ExpectationFailedException * - * @psalm-assert !int $actual + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertJsonStringEqualsJsonFile */ - public static function assertIsNotInt($actual, string $message = '') : void + function assertJsonStringEqualsJsonFile(string $expectedFile, string $actualJson, string $message = ''): void { - static::assertThat($actual, new LogicalNot(new IsType(IsType::TYPE_INT)), $message); + \PHPUnit\Framework\Assert::assertJsonStringEqualsJsonFile(...func_get_args()); } +} +if (!function_exists('PHPUnit\Framework\assertJsonStringNotEqualsJsonFile')) { /** - * Asserts that a variable is not of type numeric. + * Asserts that the generated JSON encoded object and the content of the given file are not equal. * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException * @throws ExpectationFailedException * - * @psalm-assert !numeric $actual + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertJsonStringNotEqualsJsonFile */ - public static function assertIsNotNumeric($actual, string $message = '') : void + function assertJsonStringNotEqualsJsonFile(string $expectedFile, string $actualJson, string $message = ''): void { - static::assertThat($actual, new LogicalNot(new IsType(IsType::TYPE_NUMERIC)), $message); + \PHPUnit\Framework\Assert::assertJsonStringNotEqualsJsonFile(...func_get_args()); } +} +if (!function_exists('PHPUnit\Framework\assertJsonFileEqualsJsonFile')) { /** - * Asserts that a variable is not of type object. + * Asserts that two JSON files are equal. * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException * @throws ExpectationFailedException * - * @psalm-assert !object $actual + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertJsonFileEqualsJsonFile */ - public static function assertIsNotObject($actual, string $message = '') : void + function assertJsonFileEqualsJsonFile(string $expectedFile, string $actualFile, string $message = ''): void { - static::assertThat($actual, new LogicalNot(new IsType(IsType::TYPE_OBJECT)), $message); + \PHPUnit\Framework\Assert::assertJsonFileEqualsJsonFile(...func_get_args()); } +} +if (!function_exists('PHPUnit\Framework\assertJsonFileNotEqualsJsonFile')) { /** - * Asserts that a variable is not of type resource. + * Asserts that two JSON files are not equal. * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException * @throws ExpectationFailedException * - * @psalm-assert !resource $actual + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertJsonFileNotEqualsJsonFile */ - public static function assertIsNotResource($actual, string $message = '') : void + function assertJsonFileNotEqualsJsonFile(string $expectedFile, string $actualFile, string $message = ''): void { - static::assertThat($actual, new LogicalNot(new IsType(IsType::TYPE_RESOURCE)), $message); + \PHPUnit\Framework\Assert::assertJsonFileNotEqualsJsonFile(...func_get_args()); } - /** - * Asserts that a variable is not of type resource. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - * - * @psalm-assert !resource $actual - */ - public static function assertIsNotClosedResource($actual, string $message = '') : void +} +if (!function_exists('PHPUnit\Framework\logicalAnd')) { + function logicalAnd(mixed ...$constraints): LogicalAnd { - static::assertThat($actual, new LogicalNot(new IsType(IsType::TYPE_CLOSED_RESOURCE)), $message); + return \PHPUnit\Framework\Assert::logicalAnd(...func_get_args()); } - /** - * Asserts that a variable is not of type string. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - * - * @psalm-assert !string $actual - */ - public static function assertIsNotString($actual, string $message = '') : void +} +if (!function_exists('PHPUnit\Framework\logicalOr')) { + function logicalOr(mixed ...$constraints): LogicalOr { - static::assertThat($actual, new LogicalNot(new IsType(IsType::TYPE_STRING)), $message); + return \PHPUnit\Framework\Assert::logicalOr(...func_get_args()); } - /** - * Asserts that a variable is not of type scalar. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - * - * @psalm-assert !scalar $actual - */ - public static function assertIsNotScalar($actual, string $message = '') : void +} +if (!function_exists('PHPUnit\Framework\logicalNot')) { + function logicalNot(Constraint $constraint): LogicalNot { - static::assertThat($actual, new LogicalNot(new IsType(IsType::TYPE_SCALAR)), $message); + return \PHPUnit\Framework\Assert::logicalNot(...func_get_args()); } - /** - * Asserts that a variable is not of type callable. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - * - * @psalm-assert !callable $actual - */ - public static function assertIsNotCallable($actual, string $message = '') : void +} +if (!function_exists('PHPUnit\Framework\logicalXor')) { + function logicalXor(mixed ...$constraints): LogicalXor { - static::assertThat($actual, new LogicalNot(new IsType(IsType::TYPE_CALLABLE)), $message); + return \PHPUnit\Framework\Assert::logicalXor(...func_get_args()); } - /** - * Asserts that a variable is not of type iterable. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - * - * @psalm-assert !iterable $actual - */ - public static function assertIsNotIterable($actual, string $message = '') : void +} +if (!function_exists('PHPUnit\Framework\anything')) { + function anything(): IsAnything { - static::assertThat($actual, new LogicalNot(new IsType(IsType::TYPE_ITERABLE)), $message); + return \PHPUnit\Framework\Assert::anything(...func_get_args()); } - /** - * Asserts that a string matches a given regular expression. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - */ - public static function assertMatchesRegularExpression(string $pattern, string $string, string $message = '') : void +} +if (!function_exists('PHPUnit\Framework\isTrue')) { + function isTrue(): IsTrue { - static::assertThat($string, new RegularExpression($pattern), $message); + return \PHPUnit\Framework\Assert::isTrue(...func_get_args()); } - /** - * Asserts that a string matches a given regular expression. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - * - * @codeCoverageIgnore - * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4086 - */ - public static function assertRegExp(string $pattern, string $string, string $message = '') : void +} +if (!function_exists('PHPUnit\Framework\isFalse')) { + function isFalse(): IsFalse { - self::createWarning('assertRegExp() is deprecated and will be removed in PHPUnit 10. Refactor your code to use assertMatchesRegularExpression() instead.'); - static::assertThat($string, new RegularExpression($pattern), $message); + return \PHPUnit\Framework\Assert::isFalse(...func_get_args()); } - /** - * Asserts that a string does not match a given regular expression. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - */ - public static function assertDoesNotMatchRegularExpression(string $pattern, string $string, string $message = '') : void +} +if (!function_exists('PHPUnit\Framework\isJson')) { + function isJson(): IsJson { - static::assertThat($string, new LogicalNot(new RegularExpression($pattern)), $message); + return \PHPUnit\Framework\Assert::isJson(...func_get_args()); } - /** - * Asserts that a string does not match a given regular expression. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - * - * @codeCoverageIgnore - * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4089 - */ - public static function assertNotRegExp(string $pattern, string $string, string $message = '') : void +} +if (!function_exists('PHPUnit\Framework\isNull')) { + function isNull(): IsNull { - self::createWarning('assertNotRegExp() is deprecated and will be removed in PHPUnit 10. Refactor your code to use assertDoesNotMatchRegularExpression() instead.'); - static::assertThat($string, new LogicalNot(new RegularExpression($pattern)), $message); + return \PHPUnit\Framework\Assert::isNull(...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 \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws Exception - * @throws ExpectationFailedException - */ - public static function assertSameSize($expected, $actual, string $message = '') : void +} +if (!function_exists('PHPUnit\Framework\isFinite')) { + function isFinite(): IsFinite { - if ($expected instanceof Generator) { - self::createWarning('Passing an argument of type Generator for the $expected parameter is deprecated. Support for this will be removed in PHPUnit 10.'); - } - if ($actual instanceof Generator) { - self::createWarning('Passing an argument of type Generator for the $actual parameter is deprecated. Support for this will be removed in PHPUnit 10.'); - } - if (!$expected instanceof Countable && !is_iterable($expected)) { - throw \PHPUnit\Framework\InvalidArgumentException::create(1, 'countable or iterable'); - } - if (!$actual instanceof Countable && !is_iterable($actual)) { - throw \PHPUnit\Framework\InvalidArgumentException::create(2, 'countable or iterable'); - } - static::assertThat($actual, new SameSize($expected), $message); + return \PHPUnit\Framework\Assert::isFinite(...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 \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws Exception - * @throws ExpectationFailedException - */ - public static function assertNotSameSize($expected, $actual, string $message = '') : void +} +if (!function_exists('PHPUnit\Framework\isInfinite')) { + function isInfinite(): IsInfinite { - if ($expected instanceof Generator) { - self::createWarning('Passing an argument of type Generator for the $expected parameter is deprecated. Support for this will be removed in PHPUnit 10.'); - } - if ($actual instanceof Generator) { - self::createWarning('Passing an argument of type Generator for the $actual parameter is deprecated. Support for this will be removed in PHPUnit 10.'); - } - if (!$expected instanceof Countable && !is_iterable($expected)) { - throw \PHPUnit\Framework\InvalidArgumentException::create(1, 'countable or iterable'); - } - if (!$actual instanceof Countable && !is_iterable($actual)) { - throw \PHPUnit\Framework\InvalidArgumentException::create(2, 'countable or iterable'); - } - static::assertThat($actual, new LogicalNot(new SameSize($expected)), $message); + return \PHPUnit\Framework\Assert::isInfinite(...func_get_args()); } - /** - * Asserts that a string matches a given format string. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - */ - public static function assertStringMatchesFormat(string $format, string $string, string $message = '') : void +} +if (!function_exists('PHPUnit\Framework\isNan')) { + function isNan(): IsNan { - static::assertThat($string, new StringMatchesFormatDescription($format), $message); + return \PHPUnit\Framework\Assert::isNan(...func_get_args()); } - /** - * Asserts that a string does not match a given format string. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - */ - public static function assertStringNotMatchesFormat(string $format, string $string, string $message = '') : void +} +if (!function_exists('PHPUnit\Framework\containsEqual')) { + function containsEqual(mixed $value): TraversableContainsEqual { - static::assertThat($string, new LogicalNot(new StringMatchesFormatDescription($format)), $message); + return \PHPUnit\Framework\Assert::containsEqual(...func_get_args()); } - /** - * Asserts that a string matches a given format file. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - */ - public static function assertStringMatchesFormatFile(string $formatFile, string $string, string $message = '') : void +} +if (!function_exists('PHPUnit\Framework\containsIdentical')) { + function containsIdentical(mixed $value): TraversableContainsIdentical { - static::assertFileExists($formatFile, $message); - static::assertThat($string, new StringMatchesFormatDescription(file_get_contents($formatFile)), $message); + return \PHPUnit\Framework\Assert::containsIdentical(...func_get_args()); } - /** - * Asserts that a string does not match a given format string. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - */ - public static function assertStringNotMatchesFormatFile(string $formatFile, string $string, string $message = '') : void +} +if (!function_exists('PHPUnit\Framework\containsOnly')) { + function containsOnly(string $type): TraversableContainsOnly { - static::assertFileExists($formatFile, $message); - static::assertThat($string, new LogicalNot(new StringMatchesFormatDescription(file_get_contents($formatFile))), $message); + return \PHPUnit\Framework\Assert::containsOnly(...func_get_args()); } - /** - * Asserts that a string starts with a given prefix. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - */ - public static function assertStringStartsWith(string $prefix, string $string, string $message = '') : void +} +if (!function_exists('PHPUnit\Framework\containsOnlyInstancesOf')) { + function containsOnlyInstancesOf(string $className): TraversableContainsOnly { - static::assertThat($string, new StringStartsWith($prefix), $message); + return \PHPUnit\Framework\Assert::containsOnlyInstancesOf(...func_get_args()); } - /** - * Asserts that a string starts not with a given prefix. - * - * @param string $prefix - * @param string $string - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - */ - public static function assertStringStartsNotWith($prefix, $string, string $message = '') : void +} +if (!function_exists('PHPUnit\Framework\arrayHasKey')) { + function arrayHasKey(int|string $key): ArrayHasKey { - static::assertThat($string, new LogicalNot(new StringStartsWith($prefix)), $message); + return \PHPUnit\Framework\Assert::arrayHasKey(...func_get_args()); } - /** - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - */ - public static function assertStringContainsString(string $needle, string $haystack, string $message = '') : void +} +if (!function_exists('PHPUnit\Framework\isList')) { + function isList(): IsList { - $constraint = new StringContains($needle, \false); - static::assertThat($haystack, $constraint, $message); + return \PHPUnit\Framework\Assert::isList(...func_get_args()); } - /** - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - */ - public static function assertStringContainsStringIgnoringCase(string $needle, string $haystack, string $message = '') : void +} +if (!function_exists('PHPUnit\Framework\equalTo')) { + function equalTo(mixed $value): IsEqual { - $constraint = new StringContains($needle, \true); - static::assertThat($haystack, $constraint, $message); + return \PHPUnit\Framework\Assert::equalTo(...func_get_args()); } - /** - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - */ - public static function assertStringNotContainsString(string $needle, string $haystack, string $message = '') : void +} +if (!function_exists('PHPUnit\Framework\equalToCanonicalizing')) { + function equalToCanonicalizing(mixed $value): IsEqualCanonicalizing { - $constraint = new LogicalNot(new StringContains($needle)); - static::assertThat($haystack, $constraint, $message); + return \PHPUnit\Framework\Assert::equalToCanonicalizing(...func_get_args()); } - /** - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - */ - public static function assertStringNotContainsStringIgnoringCase(string $needle, string $haystack, string $message = '') : void +} +if (!function_exists('PHPUnit\Framework\equalToIgnoringCase')) { + function equalToIgnoringCase(mixed $value): IsEqualIgnoringCase { - $constraint = new LogicalNot(new StringContains($needle, \true)); - static::assertThat($haystack, $constraint, $message); + return \PHPUnit\Framework\Assert::equalToIgnoringCase(...func_get_args()); } - /** - * Asserts that a string ends with a given suffix. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - */ - public static function assertStringEndsWith(string $suffix, string $string, string $message = '') : void +} +if (!function_exists('PHPUnit\Framework\equalToWithDelta')) { + function equalToWithDelta(mixed $value, float $delta): IsEqualWithDelta { - static::assertThat($string, new StringEndsWith($suffix), $message); + return \PHPUnit\Framework\Assert::equalToWithDelta(...func_get_args()); } - /** - * Asserts that a string ends not with a given suffix. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - */ - public static function assertStringEndsNotWith(string $suffix, string $string, string $message = '') : void +} +if (!function_exists('PHPUnit\Framework\isEmpty')) { + function isEmpty(): IsEmpty { - static::assertThat($string, new LogicalNot(new StringEndsWith($suffix)), $message); + return \PHPUnit\Framework\Assert::isEmpty(...func_get_args()); } - /** - * Asserts that two XML files are equal. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws Exception - * @throws ExpectationFailedException - */ - public static function assertXmlFileEqualsXmlFile(string $expectedFile, string $actualFile, string $message = '') : void +} +if (!function_exists('PHPUnit\Framework\isWritable')) { + function isWritable(): IsWritable { - $expected = (new XmlLoader())->loadFile($expectedFile); - $actual = (new XmlLoader())->loadFile($actualFile); - static::assertEquals($expected, $actual, $message); + return \PHPUnit\Framework\Assert::isWritable(...func_get_args()); } - /** - * Asserts that two XML files are not equal. - * - * @throws \PHPUnit\Util\Exception - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - */ - public static function assertXmlFileNotEqualsXmlFile(string $expectedFile, string $actualFile, string $message = '') : void +} +if (!function_exists('PHPUnit\Framework\isReadable')) { + function isReadable(): IsReadable { - $expected = (new XmlLoader())->loadFile($expectedFile); - $actual = (new XmlLoader())->loadFile($actualFile); - static::assertNotEquals($expected, $actual, $message); + return \PHPUnit\Framework\Assert::isReadable(...func_get_args()); } - /** - * Asserts that two XML documents are equal. - * - * @param DOMDocument|string $actualXml - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - * @throws Xml\Exception - */ - public static function assertXmlStringEqualsXmlFile(string $expectedFile, $actualXml, string $message = '') : void +} +if (!function_exists('PHPUnit\Framework\directoryExists')) { + function directoryExists(): DirectoryExists { - if (!is_string($actualXml)) { - self::createWarning('Passing an argument of type DOMDocument for the $actualXml parameter is deprecated. Support for this will be removed in PHPUnit 10.'); - $actual = $actualXml; - } else { - $actual = (new XmlLoader())->load($actualXml); - } - $expected = (new XmlLoader())->loadFile($expectedFile); - static::assertEquals($expected, $actual, $message); + return \PHPUnit\Framework\Assert::directoryExists(...func_get_args()); } - /** - * Asserts that two XML documents are not equal. - * - * @param DOMDocument|string $actualXml - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - * @throws Xml\Exception - */ - public static function assertXmlStringNotEqualsXmlFile(string $expectedFile, $actualXml, string $message = '') : void +} +if (!function_exists('PHPUnit\Framework\fileExists')) { + function fileExists(): FileExists { - if (!is_string($actualXml)) { - self::createWarning('Passing an argument of type DOMDocument for the $actualXml parameter is deprecated. Support for this will be removed in PHPUnit 10.'); - $actual = $actualXml; - } else { - $actual = (new XmlLoader())->load($actualXml); - } - $expected = (new XmlLoader())->loadFile($expectedFile); - static::assertNotEquals($expected, $actual, $message); - } - /** - * Asserts that two XML documents are equal. - * - * @param DOMDocument|string $expectedXml - * @param DOMDocument|string $actualXml - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - * @throws Xml\Exception - */ - public static function assertXmlStringEqualsXmlString($expectedXml, $actualXml, string $message = '') : void - { - if (!is_string($expectedXml)) { - self::createWarning('Passing an argument of type DOMDocument for the $expectedXml parameter is deprecated. Support for this will be removed in PHPUnit 10.'); - $expected = $expectedXml; - } else { - $expected = (new XmlLoader())->load($expectedXml); - } - if (!is_string($actualXml)) { - self::createWarning('Passing an argument of type DOMDocument for the $actualXml parameter is deprecated. Support for this will be removed in PHPUnit 10.'); - $actual = $actualXml; - } else { - $actual = (new XmlLoader())->load($actualXml); - } - static::assertEquals($expected, $actual, $message); + return \PHPUnit\Framework\Assert::fileExists(...func_get_args()); } - /** - * Asserts that two XML documents are not equal. - * - * @param DOMDocument|string $expectedXml - * @param DOMDocument|string $actualXml - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - * @throws Xml\Exception - */ - public static function assertXmlStringNotEqualsXmlString($expectedXml, $actualXml, string $message = '') : void +} +if (!function_exists('PHPUnit\Framework\greaterThan')) { + function greaterThan(mixed $value): GreaterThan { - if (!is_string($expectedXml)) { - self::createWarning('Passing an argument of type DOMDocument for the $expectedXml parameter is deprecated. Support for this will be removed in PHPUnit 10.'); - $expected = $expectedXml; - } else { - $expected = (new XmlLoader())->load($expectedXml); - } - if (!is_string($actualXml)) { - self::createWarning('Passing an argument of type DOMDocument for the $actualXml parameter is deprecated. Support for this will be removed in PHPUnit 10.'); - $actual = $actualXml; - } else { - $actual = (new XmlLoader())->load($actualXml); - } - static::assertNotEquals($expected, $actual, $message); - } - /** - * Asserts that a hierarchy of DOMElements matches. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws AssertionFailedError - * @throws ExpectationFailedException - * - * @codeCoverageIgnore - * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4091 - */ - public static function assertEqualXMLStructure(DOMElement $expectedElement, DOMElement $actualElement, bool $checkAttributes = \false, string $message = '') : void - { - self::createWarning('assertEqualXMLStructure() is deprecated and will be removed in PHPUnit 10.'); - $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); - } + return \PHPUnit\Framework\Assert::greaterThan(...func_get_args()); } - /** - * Evaluates a PHPUnit\Framework\Constraint matcher object. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - */ - public static function assertThat($value, Constraint $constraint, string $message = '') : void +} +if (!function_exists('PHPUnit\Framework\greaterThanOrEqual')) { + function greaterThanOrEqual(mixed $value): LogicalOr { - self::$count += count($constraint); - $constraint->evaluate($value, $message); + return \PHPUnit\Framework\Assert::greaterThanOrEqual(...func_get_args()); } - /** - * Asserts that a string is a valid JSON string. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - */ - public static function assertJson(string $actualJson, string $message = '') : void +} +if (!function_exists('PHPUnit\Framework\identicalTo')) { + function identicalTo(mixed $value): IsIdentical { - static::assertThat($actualJson, static::isJson(), $message); + return \PHPUnit\Framework\Assert::identicalTo(...func_get_args()); } - /** - * Asserts that two given JSON encoded objects or arrays are equal. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - */ - public static function assertJsonStringEqualsJsonString(string $expectedJson, string $actualJson, string $message = '') : void +} +if (!function_exists('PHPUnit\Framework\isInstanceOf')) { + function isInstanceOf(string $className): IsInstanceOf { - static::assertJson($expectedJson, $message); - static::assertJson($actualJson, $message); - static::assertThat($actualJson, new JsonMatches($expectedJson), $message); + return \PHPUnit\Framework\Assert::isInstanceOf(...func_get_args()); } - /** - * Asserts that two given JSON encoded objects or arrays are not equal. - * - * @param string $expectedJson - * @param string $actualJson - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - */ - public static function assertJsonStringNotEqualsJsonString($expectedJson, $actualJson, string $message = '') : void +} +if (!function_exists('PHPUnit\Framework\isType')) { + function isType(string $type): IsType { - static::assertJson($expectedJson, $message); - static::assertJson($actualJson, $message); - static::assertThat($actualJson, new LogicalNot(new JsonMatches($expectedJson)), $message); + return \PHPUnit\Framework\Assert::isType(...func_get_args()); } - /** - * Asserts that the generated JSON encoded object and the content of the given file are equal. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - */ - public static function assertJsonStringEqualsJsonFile(string $expectedFile, string $actualJson, string $message = '') : void +} +if (!function_exists('PHPUnit\Framework\lessThan')) { + function lessThan(mixed $value): LessThan { - static::assertFileExists($expectedFile, $message); - $expectedJson = file_get_contents($expectedFile); - static::assertJson($expectedJson, $message); - static::assertJson($actualJson, $message); - static::assertThat($actualJson, new JsonMatches($expectedJson), $message); + return \PHPUnit\Framework\Assert::lessThan(...func_get_args()); } - /** - * Asserts that the generated JSON encoded object and the content of the given file are not equal. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - */ - public static function assertJsonStringNotEqualsJsonFile(string $expectedFile, string $actualJson, string $message = '') : void +} +if (!function_exists('PHPUnit\Framework\lessThanOrEqual')) { + function lessThanOrEqual(mixed $value): LogicalOr { - 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); + return \PHPUnit\Framework\Assert::lessThanOrEqual(...func_get_args()); } - /** - * Asserts that two JSON files are equal. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - */ - public static function assertJsonFileEqualsJsonFile(string $expectedFile, string $actualFile, string $message = '') : void +} +if (!function_exists('PHPUnit\Framework\matchesRegularExpression')) { + function matchesRegularExpression(string $pattern): RegularExpression { - 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); + return \PHPUnit\Framework\Assert::matchesRegularExpression(...func_get_args()); } - /** - * Asserts that two JSON files are not equal. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - */ - public static function assertJsonFileNotEqualsJsonFile(string $expectedFile, string $actualFile, string $message = '') : void +} +if (!function_exists('PHPUnit\Framework\matches')) { + function matches(string $string): StringMatchesFormatDescription { - 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); + return \PHPUnit\Framework\Assert::matches(...func_get_args()); } - /** - * @throws Exception - */ - public static function logicalAnd() : LogicalAnd +} +if (!function_exists('PHPUnit\Framework\stringStartsWith')) { + function stringStartsWith(string $prefix): StringStartsWith { - $constraints = func_get_args(); - $constraint = new LogicalAnd(); - $constraint->setConstraints($constraints); - return $constraint; + return \PHPUnit\Framework\Assert::stringStartsWith(...func_get_args()); } - public static function logicalOr() : LogicalOr +} +if (!function_exists('PHPUnit\Framework\stringContains')) { + function stringContains(string $string, bool $case = \true): StringContains { - $constraints = func_get_args(); - $constraint = new LogicalOr(); - $constraint->setConstraints($constraints); - return $constraint; + return \PHPUnit\Framework\Assert::stringContains(...func_get_args()); } - public static function logicalNot(Constraint $constraint) : LogicalNot +} +if (!function_exists('PHPUnit\Framework\stringEndsWith')) { + function stringEndsWith(string $suffix): StringEndsWith { - return new LogicalNot($constraint); + return \PHPUnit\Framework\Assert::stringEndsWith(...func_get_args()); } - public static function logicalXor() : LogicalXor +} +if (!function_exists('PHPUnit\Framework\stringEqualsStringIgnoringLineEndings')) { + function stringEqualsStringIgnoringLineEndings(string $string): StringEqualsStringIgnoringLineEndings { - $constraints = func_get_args(); - $constraint = new LogicalXor(); - $constraint->setConstraints($constraints); - return $constraint; + return \PHPUnit\Framework\Assert::stringEqualsStringIgnoringLineEndings(...func_get_args()); } - public static function anything() : IsAnything +} +if (!function_exists('PHPUnit\Framework\countOf')) { + function countOf(int $count): Count { - return new IsAnything(); + return \PHPUnit\Framework\Assert::countOf(...func_get_args()); } - public static function isTrue() : IsTrue +} +if (!function_exists('PHPUnit\Framework\objectEquals')) { + function objectEquals(object $object, string $method = 'equals'): ObjectEquals { - return new IsTrue(); + return \PHPUnit\Framework\Assert::objectEquals(...func_get_args()); } +} +if (!function_exists('PHPUnit\Framework\callback')) { /** * @psalm-template CallbackInput of mixed * @@ -58649,265 +50856,387 @@ abstract class Assert * * @psalm-return Callback */ - public static function callback(callable $callback) : Callback - { - return new Callback($callback); - } - public static function isFalse() : IsFalse + function callback(callable $callback): Callback { - 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(); - } - public static function containsEqual($value) : TraversableContainsEqual - { - return new TraversableContainsEqual($value); - } - public static function containsIdentical($value) : TraversableContainsIdentical - { - return new TraversableContainsIdentical($value); - } - public static function containsOnly(string $type) : TraversableContainsOnly - { - return new TraversableContainsOnly($type); - } - public static function containsOnlyInstancesOf(string $className) : TraversableContainsOnly - { - return new TraversableContainsOnly($className, \false); + return \PHPUnit\Framework\Assert::callback($callback); } +} +if (!function_exists('PHPUnit\Framework\any')) { /** - * @param int|string $key + * Returns a matcher that matches when the method is executed + * zero or more times. */ - public static function arrayHasKey($key) : ArrayHasKey - { - return new ArrayHasKey($key); - } - public static function equalTo($value) : IsEqual - { - return new IsEqual($value, 0.0, \false, \false); - } - public static function equalToCanonicalizing($value) : IsEqualCanonicalizing - { - return new IsEqualCanonicalizing($value); - } - public static function equalToIgnoringCase($value) : IsEqualIgnoringCase - { - return new IsEqualIgnoringCase($value); - } - public static function equalToWithDelta($value, float $delta) : IsEqualWithDelta - { - return new IsEqualWithDelta($value, $delta); - } - 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 + function any(): AnyInvokedCountMatcher { - return new FileExists(); + return new AnyInvokedCountMatcher(); } - public static function greaterThan($value) : GreaterThan +} +if (!function_exists('PHPUnit\Framework\never')) { + /** + * Returns a matcher that matches when the method is never executed. + */ + function never(): InvokedCountMatcher { - return new GreaterThan($value); + return new InvokedCountMatcher(0); } - public static function greaterThanOrEqual($value) : LogicalOr +} +if (!function_exists('PHPUnit\Framework\atLeast')) { + /** + * Returns a matcher that matches when the method is executed + * at least N times. + */ + function atLeast(int $requiredInvocations): InvokedAtLeastCountMatcher { - return static::logicalOr(new IsEqual($value), new GreaterThan($value)); + return new InvokedAtLeastCountMatcher($requiredInvocations); } +} +if (!function_exists('PHPUnit\Framework\atLeastOnce')) { /** - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4601 + * Returns a matcher that matches when the method is executed at least once. */ - public static function classHasAttribute(string $attributeName) : ClassHasAttribute + function atLeastOnce(): InvokedAtLeastOnceMatcher { - self::createWarning('classHasAttribute() is deprecated and will be removed in PHPUnit 10.'); - return new ClassHasAttribute($attributeName); + return new InvokedAtLeastOnceMatcher(); } +} +if (!function_exists('PHPUnit\Framework\once')) { /** - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4601 + * Returns a matcher that matches when the method is executed exactly once. */ - public static function classHasStaticAttribute(string $attributeName) : ClassHasStaticAttribute + function once(): InvokedCountMatcher { - self::createWarning('classHasStaticAttribute() is deprecated and will be removed in PHPUnit 10.'); - return new ClassHasStaticAttribute($attributeName); + return new InvokedCountMatcher(1); } +} +if (!function_exists('PHPUnit\Framework\exactly')) { /** - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4601 + * Returns a matcher that matches when the method is executed + * exactly $count times. */ - public static function objectHasAttribute($attributeName) : ObjectHasAttribute + function exactly(int $count): InvokedCountMatcher { - self::createWarning('objectHasAttribute() is deprecated and will be removed in PHPUnit 10.'); - return new ObjectHasAttribute($attributeName); + return new InvokedCountMatcher($count); } - public static function identicalTo($value) : IsIdentical +} +if (!function_exists('PHPUnit\Framework\atMost')) { + /** + * Returns a matcher that matches when the method is executed + * at most N times. + */ + function atMost(int $allowedInvocations): InvokedAtMostCountMatcher { - return new IsIdentical($value); + return new InvokedAtMostCountMatcher($allowedInvocations); } - public static function isInstanceOf(string $className) : IsInstanceOf +} +if (!function_exists('PHPUnit\Framework\returnValue')) { + function returnValue(mixed $value): ReturnStub { - return new IsInstanceOf($className); + return new ReturnStub($value); } - public static function isType(string $type) : IsType +} +if (!function_exists('PHPUnit\Framework\returnValueMap')) { + function returnValueMap(array $valueMap): ReturnValueMapStub { - return new IsType($type); + return new ReturnValueMapStub($valueMap); } - public static function lessThan($value) : LessThan +} +if (!function_exists('PHPUnit\Framework\returnArgument')) { + function returnArgument(int $argumentIndex): ReturnArgumentStub { - return new LessThan($value); + return new ReturnArgumentStub($argumentIndex); } - public static function lessThanOrEqual($value) : LogicalOr +} +if (!function_exists('PHPUnit\Framework\returnCallback')) { + function returnCallback(callable $callback): ReturnCallbackStub { - return static::logicalOr(new IsEqual($value), new LessThan($value)); + return new ReturnCallbackStub($callback); } - public static function matchesRegularExpression(string $pattern) : RegularExpression +} +if (!function_exists('PHPUnit\Framework\returnSelf')) { + /** + * Returns the current object. + * + * This method is useful when mocking a fluent interface. + */ + function returnSelf(): ReturnSelfStub { - return new RegularExpression($pattern); + return new ReturnSelfStub(); } - public static function matches(string $string) : StringMatchesFormatDescription +} +if (!function_exists('PHPUnit\Framework\throwException')) { + function throwException(Throwable $exception): ExceptionStub { - return new StringMatchesFormatDescription($string); + return new ExceptionStub($exception); } - public static function stringStartsWith($prefix) : StringStartsWith +} +if (!function_exists('PHPUnit\Framework\onConsecutiveCalls')) { + function onConsecutiveCalls(): ConsecutiveCallsStub { - return new StringStartsWith($prefix); + $arguments = func_get_args(); + return new ConsecutiveCallsStub($arguments); } - public static function stringContains(string $string, bool $case = \true) : StringContains +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Attributes; + +use Attribute; +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +#[Attribute(Attribute::TARGET_METHOD)] +final class After +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Attributes; + +use Attribute; +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +#[Attribute(Attribute::TARGET_METHOD)] +final class AfterClass +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Attributes; + +use Attribute; +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +#[Attribute(Attribute::TARGET_CLASS | Attribute::TARGET_METHOD)] +final class BackupGlobals +{ + private readonly bool $enabled; + public function __construct(bool $enabled) { - return new StringContains($string, $case); + $this->enabled = $enabled; } - public static function stringEndsWith(string $suffix) : StringEndsWith + public function enabled(): bool { - return new StringEndsWith($suffix); + return $this->enabled; } - public static function countOf(int $count) : Count +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Attributes; + +use Attribute; +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +#[Attribute(Attribute::TARGET_CLASS | Attribute::TARGET_METHOD)] +final class BackupStaticProperties +{ + private readonly bool $enabled; + public function __construct(bool $enabled) { - return new Count($count); + $this->enabled = $enabled; } - public static function objectEquals(object $object, string $method = 'equals') : ObjectEquals + public function enabled(): bool { - return new ObjectEquals($object, $method); + return $this->enabled; } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Attributes; + +use Attribute; +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +#[Attribute(Attribute::TARGET_METHOD)] +final class Before +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Attributes; + +use Attribute; +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +#[Attribute(Attribute::TARGET_METHOD)] +final class BeforeClass +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Attributes; + +use Attribute; +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/5236 + */ +#[Attribute(Attribute::TARGET_CLASS | Attribute::TARGET_METHOD)] +final class CodeCoverageIgnore +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Attributes; + +use Attribute; +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +#[Attribute(Attribute::TARGET_CLASS | Attribute::IS_REPEATABLE)] +final class CoversClass +{ /** - * Fails a test with the given message. - * - * @throws AssertionFailedError - * - * @psalm-return never-return + * @psalm-var class-string */ - public static function fail(string $message = '') : void - { - self::$count++; - throw new \PHPUnit\Framework\AssertionFailedError($message); - } + private readonly string $className; /** - * Mark the test as incomplete. - * - * @throws IncompleteTestError - * - * @psalm-return never-return + * @psalm-param class-string $className */ - public static function markTestIncomplete(string $message = '') : void + public function __construct(string $className) { - throw new \PHPUnit\Framework\IncompleteTestError($message); + $this->className = $className; } /** - * Mark the test as skipped. - * - * @throws SkippedTestError - * @throws SyntheticSkippedError - * - * @psalm-return never-return + * @psalm-return class-string */ - public static function markTestSkipped(string $message = '') : void + public function className(): string { - if ($hint = self::detectLocationHint($message)) { - $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); - array_unshift($trace, $hint); - throw new \PHPUnit\Framework\SyntheticSkippedError($hint['message'], 0, $hint['file'], (int) $hint['line'], $trace); - } - throw new \PHPUnit\Framework\SkippedTestError($message); + return $this->className; } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Attributes; + +use Attribute; +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +#[Attribute(Attribute::TARGET_CLASS | Attribute::IS_REPEATABLE)] +final class CoversFunction +{ /** - * Return the current assertion count. + * @psalm-var non-empty-string */ - public static function getCount() : int - { - return self::$count; - } + private readonly string $functionName; /** - * Reset the assertion counter. + * @psalm-param non-empty-string $functionName */ - public static function resetCount() : void + public function __construct(string $functionName) { - 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); + $this->functionName = $functionName; } /** - * @codeCoverageIgnore + * @psalm-return non-empty-string */ - private static function createWarning(string $warning) : void + public function functionName(): string { - foreach (debug_backtrace() as $step) { - if (isset($step['object']) && $step['object'] instanceof \PHPUnit\Framework\TestCase) { - assert($step['object'] instanceof \PHPUnit\Framework\TestCase); - $step['object']->addWarning($warning); - break; - } - } + return $this->functionName; } } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Attributes; + +use Attribute; +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +#[Attribute(Attribute::TARGET_METHOD | Attribute::IS_REPEATABLE)] +final class DataProvider +{ /** - * Asserts that an array has a specified key. - * - * @param int|string $key - * @param array|ArrayAccess $array - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * @throws Exception - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertArrayHasKey + * @psalm-var non-empty-string */ - function assertArrayHasKey($key, $array, string $message = '') : void - { - \PHPUnit\Framework\Assert::assertArrayHasKey(...func_get_args()); - } -} -if (!function_exists('PHPUnit\\Framework\\assertArrayNotHasKey')) { + private readonly string $methodName; /** - * Asserts that an array does not have a specified key. - * - * @param int|string $key - * @param array|ArrayAccess $array - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * @throws Exception - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertArrayNotHasKey + * @psalm-param non-empty-string $methodName */ - function assertArrayNotHasKey($key, $array, string $message = '') : void + public function __construct(string $methodName) { - \PHPUnit\Framework\Assert::assertArrayNotHasKey(...func_get_args()); + $this->methodName = $methodName; } -} -if (!function_exists('PHPUnit\\Framework\\assertContains')) { /** - * Asserts that a haystack contains a needle. - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * @throws Exception - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertContains + * @psalm-return non-empty-string */ - function assertContains($needle, iterable $haystack, string $message = '') : void - { - \PHPUnit\Framework\Assert::assertContains(...func_get_args()); - } -} -if (!function_exists('PHPUnit\\Framework\\assertContainsEquals')) { - function assertContainsEquals($needle, iterable $haystack, string $message = '') : void + public function methodName(): string { - \PHPUnit\Framework\Assert::assertContainsEquals(...func_get_args()); + return $this->methodName; } } -if (!function_exists('PHPUnit\\Framework\\assertNotContains')) { + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Attributes; + +use Attribute; +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +#[Attribute(Attribute::TARGET_METHOD | Attribute::IS_REPEATABLE)] +final class DataProviderExternal +{ /** - * Asserts that a haystack does not contain a needle. - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * @throws Exception - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertNotContains + * @psalm-var class-string */ - function assertNotContains($needle, iterable $haystack, string $message = '') : void - { - \PHPUnit\Framework\Assert::assertNotContains(...func_get_args()); - } -} -if (!function_exists('PHPUnit\\Framework\\assertNotContainsEquals')) { - function assertNotContainsEquals($needle, iterable $haystack, string $message = '') : void - { - \PHPUnit\Framework\Assert::assertNotContainsEquals(...func_get_args()); - } -} -if (!function_exists('PHPUnit\\Framework\\assertContainsOnly')) { + private readonly string $className; /** - * Asserts that a haystack contains only values of a given type. - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertContainsOnly + * @psalm-var non-empty-string + */ + private readonly string $methodName; + /** + * @psalm-param class-string $className + * @psalm-param non-empty-string $methodName */ - function assertContainsOnly(string $type, iterable $haystack, ?bool $isNativeType = null, string $message = '') : void + public function __construct(string $className, string $methodName) { - \PHPUnit\Framework\Assert::assertContainsOnly(...func_get_args()); + $this->className = $className; + $this->methodName = $methodName; } -} -if (!function_exists('PHPUnit\\Framework\\assertContainsOnlyInstancesOf')) { /** - * Asserts that a haystack contains only instances of a given class name. - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertContainsOnlyInstancesOf + * @psalm-return class-string */ - function assertContainsOnlyInstancesOf(string $className, iterable $haystack, string $message = '') : void + public function className(): string { - \PHPUnit\Framework\Assert::assertContainsOnlyInstancesOf(...func_get_args()); + return $this->className; } -} -if (!function_exists('PHPUnit\\Framework\\assertNotContainsOnly')) { /** - * Asserts that a haystack does not contain only values of a given type. - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertNotContainsOnly + * @psalm-return non-empty-string */ - function assertNotContainsOnly(string $type, iterable $haystack, ?bool $isNativeType = null, string $message = '') : void + public function methodName(): string { - \PHPUnit\Framework\Assert::assertNotContainsOnly(...func_get_args()); + return $this->methodName; } } -if (!function_exists('PHPUnit\\Framework\\assertCount')) { + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Attributes; + +use Attribute; +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +#[Attribute(Attribute::TARGET_METHOD | Attribute::IS_REPEATABLE)] +final class Depends +{ /** - * Asserts the number of elements of an array, Countable or Traversable. - * - * @param Countable|iterable $haystack - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * @throws Exception - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertCount + * @psalm-var non-empty-string + */ + private readonly string $methodName; + /** + * @psalm-param non-empty-string $methodName */ - function assertCount(int $expectedCount, $haystack, string $message = '') : void + public function __construct(string $methodName) { - \PHPUnit\Framework\Assert::assertCount(...func_get_args()); + $this->methodName = $methodName; } -} -if (!function_exists('PHPUnit\\Framework\\assertNotCount')) { /** - * Asserts the number of elements of an array, Countable or Traversable. - * - * @param Countable|iterable $haystack - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * @throws Exception - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertNotCount + * @psalm-return non-empty-string */ - function assertNotCount(int $expectedCount, $haystack, string $message = '') : void + public function methodName(): string { - \PHPUnit\Framework\Assert::assertNotCount(...func_get_args()); + return $this->methodName; } } -if (!function_exists('PHPUnit\\Framework\\assertEquals')) { + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Attributes; + +use Attribute; +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +#[Attribute(Attribute::TARGET_METHOD | Attribute::IS_REPEATABLE)] +final class DependsExternal +{ /** - * Asserts that two variables are equal. - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertEquals + * @psalm-var class-string */ - function assertEquals($expected, $actual, string $message = '') : void + private readonly string $className; + /** + * @psalm-var non-empty-string + */ + private readonly string $methodName; + /** + * @psalm-param class-string $className + * @psalm-param non-empty-string $methodName + */ + public function __construct(string $className, string $methodName) { - \PHPUnit\Framework\Assert::assertEquals(...func_get_args()); + $this->className = $className; + $this->methodName = $methodName; } -} -if (!function_exists('PHPUnit\\Framework\\assertEqualsCanonicalizing')) { /** - * Asserts that two variables are equal (canonicalizing). - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertEqualsCanonicalizing + * @psalm-return class-string */ - function assertEqualsCanonicalizing($expected, $actual, string $message = '') : void + public function className(): string { - \PHPUnit\Framework\Assert::assertEqualsCanonicalizing(...func_get_args()); + return $this->className; } -} -if (!function_exists('PHPUnit\\Framework\\assertEqualsIgnoringCase')) { /** - * Asserts that two variables are equal (ignoring case). - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertEqualsIgnoringCase + * @psalm-return non-empty-string */ - function assertEqualsIgnoringCase($expected, $actual, string $message = '') : void + public function methodName(): string { - \PHPUnit\Framework\Assert::assertEqualsIgnoringCase(...func_get_args()); + return $this->methodName; } } -if (!function_exists('PHPUnit\\Framework\\assertEqualsWithDelta')) { + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Attributes; + +use Attribute; +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +#[Attribute(Attribute::TARGET_METHOD | Attribute::IS_REPEATABLE)] +final class DependsExternalUsingDeepClone +{ /** - * Asserts that two variables are equal (with delta). - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertEqualsWithDelta + * @psalm-var class-string + */ + private readonly string $className; + /** + * @psalm-var non-empty-string */ - function assertEqualsWithDelta($expected, $actual, float $delta, string $message = '') : void + private readonly string $methodName; + /** + * @psalm-param class-string $className + * @psalm-param non-empty-string $methodName + */ + public function __construct(string $className, string $methodName) { - \PHPUnit\Framework\Assert::assertEqualsWithDelta(...func_get_args()); + $this->className = $className; + $this->methodName = $methodName; } -} -if (!function_exists('PHPUnit\\Framework\\assertNotEquals')) { /** - * Asserts that two variables are not equal. - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertNotEquals + * @psalm-return class-string */ - function assertNotEquals($expected, $actual, string $message = '') : void + public function className(): string { - \PHPUnit\Framework\Assert::assertNotEquals(...func_get_args()); + return $this->className; } -} -if (!function_exists('PHPUnit\\Framework\\assertNotEqualsCanonicalizing')) { /** - * Asserts that two variables are not equal (canonicalizing). - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertNotEqualsCanonicalizing + * @psalm-return non-empty-string */ - function assertNotEqualsCanonicalizing($expected, $actual, string $message = '') : void + public function methodName(): string { - \PHPUnit\Framework\Assert::assertNotEqualsCanonicalizing(...func_get_args()); + return $this->methodName; } } -if (!function_exists('PHPUnit\\Framework\\assertNotEqualsIgnoringCase')) { + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Attributes; + +use Attribute; +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +#[Attribute(Attribute::TARGET_METHOD | Attribute::IS_REPEATABLE)] +final class DependsExternalUsingShallowClone +{ /** - * Asserts that two variables are not equal (ignoring case). - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertNotEqualsIgnoringCase + * @psalm-var class-string + */ + private readonly string $className; + /** + * @psalm-var non-empty-string + */ + private readonly string $methodName; + /** + * @psalm-param class-string $className + * @psalm-param non-empty-string $methodName */ - function assertNotEqualsIgnoringCase($expected, $actual, string $message = '') : void + public function __construct(string $className, string $methodName) { - \PHPUnit\Framework\Assert::assertNotEqualsIgnoringCase(...func_get_args()); + $this->className = $className; + $this->methodName = $methodName; } -} -if (!function_exists('PHPUnit\\Framework\\assertNotEqualsWithDelta')) { /** - * Asserts that two variables are not equal (with delta). - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertNotEqualsWithDelta + * @psalm-return class-string */ - function assertNotEqualsWithDelta($expected, $actual, float $delta, string $message = '') : void + public function className(): string { - \PHPUnit\Framework\Assert::assertNotEqualsWithDelta(...func_get_args()); + return $this->className; } -} -if (!function_exists('PHPUnit\\Framework\\assertObjectEquals')) { /** - * @throws ExpectationFailedException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertObjectEquals + * @psalm-return non-empty-string */ - function assertObjectEquals(object $expected, object $actual, string $method = 'equals', string $message = '') : void + public function methodName(): string { - \PHPUnit\Framework\Assert::assertObjectEquals(...func_get_args()); + return $this->methodName; } } -if (!function_exists('PHPUnit\\Framework\\assertEmpty')) { + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Attributes; + +use Attribute; +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +#[Attribute(Attribute::TARGET_METHOD | Attribute::IS_REPEATABLE)] +final class DependsOnClass +{ /** - * Asserts that a variable is empty. - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @psalm-assert empty $actual - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertEmpty + * @psalm-var class-string + */ + private readonly string $className; + /** + * @psalm-param class-string $className */ - function assertEmpty($actual, string $message = '') : void + public function __construct(string $className) { - \PHPUnit\Framework\Assert::assertEmpty(...func_get_args()); + $this->className = $className; } -} -if (!function_exists('PHPUnit\\Framework\\assertNotEmpty')) { /** - * Asserts that a variable is not empty. - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @psalm-assert !empty $actual - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertNotEmpty + * @psalm-return class-string */ - function assertNotEmpty($actual, string $message = '') : void + public function className(): string { - \PHPUnit\Framework\Assert::assertNotEmpty(...func_get_args()); + return $this->className; } } -if (!function_exists('PHPUnit\\Framework\\assertGreaterThan')) { + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Attributes; + +use Attribute; +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +#[Attribute(Attribute::TARGET_METHOD | Attribute::IS_REPEATABLE)] +final class DependsOnClassUsingDeepClone +{ /** - * Asserts that a value is greater than another value. - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertGreaterThan + * @psalm-var class-string + */ + private readonly string $className; + /** + * @psalm-param class-string $className */ - function assertGreaterThan($expected, $actual, string $message = '') : void + public function __construct(string $className) { - \PHPUnit\Framework\Assert::assertGreaterThan(...func_get_args()); + $this->className = $className; } -} -if (!function_exists('PHPUnit\\Framework\\assertGreaterThanOrEqual')) { /** - * Asserts that a value is greater than or equal to another value. - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertGreaterThanOrEqual + * @psalm-return class-string */ - function assertGreaterThanOrEqual($expected, $actual, string $message = '') : void + public function className(): string { - \PHPUnit\Framework\Assert::assertGreaterThanOrEqual(...func_get_args()); + return $this->className; } } -if (!function_exists('PHPUnit\\Framework\\assertLessThan')) { - /** - * Asserts that a value is smaller than another value. - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertLessThan + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Attributes; + +use Attribute; +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +#[Attribute(Attribute::TARGET_METHOD | Attribute::IS_REPEATABLE)] +final class DependsOnClassUsingShallowClone +{ + /** + * @psalm-var class-string */ - function assertLessThan($expected, $actual, string $message = '') : void - { - \PHPUnit\Framework\Assert::assertLessThan(...func_get_args()); - } -} -if (!function_exists('PHPUnit\\Framework\\assertLessThanOrEqual')) { + private readonly string $className; /** - * Asserts that a value is smaller than or equal to another value. - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertLessThanOrEqual + * @psalm-param class-string $className */ - function assertLessThanOrEqual($expected, $actual, string $message = '') : void + public function __construct(string $className) { - \PHPUnit\Framework\Assert::assertLessThanOrEqual(...func_get_args()); + $this->className = $className; } -} -if (!function_exists('PHPUnit\\Framework\\assertFileEquals')) { /** - * Asserts that the contents of one file is equal to the contents of another - * file. - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertFileEquals + * @psalm-return class-string */ - function assertFileEquals(string $expected, string $actual, string $message = '') : void + public function className(): string { - \PHPUnit\Framework\Assert::assertFileEquals(...func_get_args()); + return $this->className; } } -if (!function_exists('PHPUnit\\Framework\\assertFileEqualsCanonicalizing')) { + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Attributes; + +use Attribute; +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +#[Attribute(Attribute::TARGET_METHOD | Attribute::IS_REPEATABLE)] +final class DependsUsingDeepClone +{ /** - * Asserts that the contents of one file is equal to the contents of another - * file (canonicalizing). - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertFileEqualsCanonicalizing + * @psalm-var non-empty-string */ - function assertFileEqualsCanonicalizing(string $expected, string $actual, string $message = '') : void - { - \PHPUnit\Framework\Assert::assertFileEqualsCanonicalizing(...func_get_args()); - } -} -if (!function_exists('PHPUnit\\Framework\\assertFileEqualsIgnoringCase')) { + private readonly string $methodName; /** - * Asserts that the contents of one file is equal to the contents of another - * file (ignoring case). - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertFileEqualsIgnoringCase + * @psalm-param non-empty-string $methodName */ - function assertFileEqualsIgnoringCase(string $expected, string $actual, string $message = '') : void + public function __construct(string $methodName) { - \PHPUnit\Framework\Assert::assertFileEqualsIgnoringCase(...func_get_args()); + $this->methodName = $methodName; } -} -if (!function_exists('PHPUnit\\Framework\\assertFileNotEquals')) { /** - * Asserts that the contents of one file is not equal to the contents of - * another file. - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertFileNotEquals + * @psalm-return non-empty-string */ - function assertFileNotEquals(string $expected, string $actual, string $message = '') : void + public function methodName(): string { - \PHPUnit\Framework\Assert::assertFileNotEquals(...func_get_args()); + return $this->methodName; } } -if (!function_exists('PHPUnit\\Framework\\assertFileNotEqualsCanonicalizing')) { + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Attributes; + +use Attribute; +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +#[Attribute(Attribute::TARGET_METHOD | Attribute::IS_REPEATABLE)] +final class DependsUsingShallowClone +{ /** - * Asserts that the contents of one file is not equal to the contents of another - * file (canonicalizing). - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertFileNotEqualsCanonicalizing + * @psalm-var non-empty-string */ - function assertFileNotEqualsCanonicalizing(string $expected, string $actual, string $message = '') : void - { - \PHPUnit\Framework\Assert::assertFileNotEqualsCanonicalizing(...func_get_args()); - } -} -if (!function_exists('PHPUnit\\Framework\\assertFileNotEqualsIgnoringCase')) { + private readonly string $methodName; /** - * Asserts that the contents of one file is not equal to the contents of another - * file (ignoring case). - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertFileNotEqualsIgnoringCase + * @psalm-param non-empty-string $methodName */ - function assertFileNotEqualsIgnoringCase(string $expected, string $actual, string $message = '') : void + public function __construct(string $methodName) { - \PHPUnit\Framework\Assert::assertFileNotEqualsIgnoringCase(...func_get_args()); + $this->methodName = $methodName; } -} -if (!function_exists('PHPUnit\\Framework\\assertStringEqualsFile')) { /** - * Asserts that the contents of a string is equal - * to the contents of a file. - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertStringEqualsFile + * @psalm-return non-empty-string */ - function assertStringEqualsFile(string $expectedFile, string $actualString, string $message = '') : void + public function methodName(): string { - \PHPUnit\Framework\Assert::assertStringEqualsFile(...func_get_args()); + return $this->methodName; } } -if (!function_exists('PHPUnit\\Framework\\assertStringEqualsFileCanonicalizing')) { + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Attributes; + +use Attribute; +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +#[Attribute(Attribute::TARGET_CLASS | Attribute::TARGET_METHOD)] +final class DoesNotPerformAssertions +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Attributes; + +use Attribute; +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +#[Attribute(Attribute::TARGET_CLASS | Attribute::TARGET_METHOD | Attribute::IS_REPEATABLE)] +final class ExcludeGlobalVariableFromBackup +{ /** - * Asserts that the contents of a string is equal - * to the contents of a file (canonicalizing). - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertStringEqualsFileCanonicalizing + * @psalm-var non-empty-string */ - function assertStringEqualsFileCanonicalizing(string $expectedFile, string $actualString, string $message = '') : void - { - \PHPUnit\Framework\Assert::assertStringEqualsFileCanonicalizing(...func_get_args()); - } -} -if (!function_exists('PHPUnit\\Framework\\assertStringEqualsFileIgnoringCase')) { + private readonly string $globalVariableName; /** - * Asserts that the contents of a string is equal - * to the contents of a file (ignoring case). - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertStringEqualsFileIgnoringCase + * @psalm-param non-empty-string $globalVariableName */ - function assertStringEqualsFileIgnoringCase(string $expectedFile, string $actualString, string $message = '') : void + public function __construct(string $globalVariableName) { - \PHPUnit\Framework\Assert::assertStringEqualsFileIgnoringCase(...func_get_args()); + $this->globalVariableName = $globalVariableName; } -} -if (!function_exists('PHPUnit\\Framework\\assertStringNotEqualsFile')) { /** - * Asserts that the contents of a string is not equal - * to the contents of a file. - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertStringNotEqualsFile + * @psalm-return non-empty-string */ - function assertStringNotEqualsFile(string $expectedFile, string $actualString, string $message = '') : void + public function globalVariableName(): string { - \PHPUnit\Framework\Assert::assertStringNotEqualsFile(...func_get_args()); + return $this->globalVariableName; } } -if (!function_exists('PHPUnit\\Framework\\assertStringNotEqualsFileCanonicalizing')) { + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Attributes; + +use Attribute; +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +#[Attribute(Attribute::TARGET_CLASS | Attribute::TARGET_METHOD | Attribute::IS_REPEATABLE)] +final class ExcludeStaticPropertyFromBackup +{ /** - * Asserts that the contents of a string is not equal - * to the contents of a file (canonicalizing). - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertStringNotEqualsFileCanonicalizing + * @psalm-var class-string */ - function assertStringNotEqualsFileCanonicalizing(string $expectedFile, string $actualString, string $message = '') : void - { - \PHPUnit\Framework\Assert::assertStringNotEqualsFileCanonicalizing(...func_get_args()); - } -} -if (!function_exists('PHPUnit\\Framework\\assertStringNotEqualsFileIgnoringCase')) { + private readonly string $className; /** - * Asserts that the contents of a string is not equal - * to the contents of a file (ignoring case). - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertStringNotEqualsFileIgnoringCase + * @psalm-var non-empty-string */ - function assertStringNotEqualsFileIgnoringCase(string $expectedFile, string $actualString, string $message = '') : void - { - \PHPUnit\Framework\Assert::assertStringNotEqualsFileIgnoringCase(...func_get_args()); - } -} -if (!function_exists('PHPUnit\\Framework\\assertIsReadable')) { + private readonly string $propertyName; /** - * Asserts that a file/dir is readable. - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertIsReadable + * @psalm-param class-string $className + * @psalm-param non-empty-string $propertyName */ - function assertIsReadable(string $filename, string $message = '') : void + public function __construct(string $className, string $propertyName) { - \PHPUnit\Framework\Assert::assertIsReadable(...func_get_args()); + $this->className = $className; + $this->propertyName = $propertyName; } -} -if (!function_exists('PHPUnit\\Framework\\assertIsNotReadable')) { /** - * Asserts that a file/dir exists and is not readable. - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertIsNotReadable + * @psalm-return class-string */ - function assertIsNotReadable(string $filename, string $message = '') : void + public function className(): string { - \PHPUnit\Framework\Assert::assertIsNotReadable(...func_get_args()); + return $this->className; } -} -if (!function_exists('PHPUnit\\Framework\\assertNotIsReadable')) { /** - * Asserts that a file/dir exists and is not readable. - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @codeCoverageIgnore - * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4062 - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertNotIsReadable + * @psalm-return non-empty-string */ - function assertNotIsReadable(string $filename, string $message = '') : void + public function propertyName(): string { - \PHPUnit\Framework\Assert::assertNotIsReadable(...func_get_args()); + return $this->propertyName; } } -if (!function_exists('PHPUnit\\Framework\\assertIsWritable')) { + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Attributes; + +use Attribute; +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +#[Attribute(Attribute::TARGET_CLASS | Attribute::TARGET_METHOD | Attribute::IS_REPEATABLE)] +final class Group +{ /** - * Asserts that a file/dir exists and is writable. - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertIsWritable + * @psalm-var non-empty-string */ - function assertIsWritable(string $filename, string $message = '') : void - { - \PHPUnit\Framework\Assert::assertIsWritable(...func_get_args()); - } -} -if (!function_exists('PHPUnit\\Framework\\assertIsNotWritable')) { + private readonly string $name; /** - * Asserts that a file/dir exists and is not writable. - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertIsNotWritable + * @psalm-param non-empty-string $name */ - function assertIsNotWritable(string $filename, string $message = '') : void + public function __construct(string $name) { - \PHPUnit\Framework\Assert::assertIsNotWritable(...func_get_args()); + $this->name = $name; } -} -if (!function_exists('PHPUnit\\Framework\\assertNotIsWritable')) { /** - * Asserts that a file/dir exists and is not writable. - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @codeCoverageIgnore - * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4065 - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertNotIsWritable + * @psalm-return non-empty-string */ - function assertNotIsWritable(string $filename, string $message = '') : void + public function name(): string { - \PHPUnit\Framework\Assert::assertNotIsWritable(...func_get_args()); + return $this->name; } } -if (!function_exists('PHPUnit\\Framework\\assertDirectoryExists')) { + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Attributes; + +use Attribute; +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/5513 + */ +#[Attribute(Attribute::TARGET_CLASS | Attribute::IS_REPEATABLE)] +final class IgnoreClassForCodeCoverage +{ /** - * Asserts that a directory exists. - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertDirectoryExists + * @psalm-var class-string */ - function assertDirectoryExists(string $directory, string $message = '') : void - { - \PHPUnit\Framework\Assert::assertDirectoryExists(...func_get_args()); - } -} -if (!function_exists('PHPUnit\\Framework\\assertDirectoryDoesNotExist')) { + private readonly string $className; /** - * Asserts that a directory does not exist. - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertDirectoryDoesNotExist + * @psalm-param class-string $className */ - function assertDirectoryDoesNotExist(string $directory, string $message = '') : void + public function __construct(string $className) { - \PHPUnit\Framework\Assert::assertDirectoryDoesNotExist(...func_get_args()); + $this->className = $className; } -} -if (!function_exists('PHPUnit\\Framework\\assertDirectoryNotExists')) { /** - * Asserts that a directory does not exist. - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @codeCoverageIgnore - * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4068 - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertDirectoryNotExists + * @psalm-return class-string */ - function assertDirectoryNotExists(string $directory, string $message = '') : void + public function className(): string { - \PHPUnit\Framework\Assert::assertDirectoryNotExists(...func_get_args()); + return $this->className; } } -if (!function_exists('PHPUnit\\Framework\\assertDirectoryIsReadable')) { + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Attributes; + +use Attribute; +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +#[Attribute(Attribute::TARGET_CLASS | Attribute::TARGET_METHOD)] +final class IgnoreDeprecations +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Attributes; + +use Attribute; +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/5513 + */ +#[Attribute(Attribute::TARGET_CLASS | Attribute::IS_REPEATABLE)] +final class IgnoreFunctionForCodeCoverage +{ /** - * Asserts that a directory exists and is readable. - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertDirectoryIsReadable + * @psalm-var non-empty-string */ - function assertDirectoryIsReadable(string $directory, string $message = '') : void - { - \PHPUnit\Framework\Assert::assertDirectoryIsReadable(...func_get_args()); - } -} -if (!function_exists('PHPUnit\\Framework\\assertDirectoryIsNotReadable')) { + private readonly string $functionName; /** - * Asserts that a directory exists and is not readable. - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertDirectoryIsNotReadable + * @psalm-param non-empty-string $functionName */ - function assertDirectoryIsNotReadable(string $directory, string $message = '') : void + public function __construct(string $functionName) { - \PHPUnit\Framework\Assert::assertDirectoryIsNotReadable(...func_get_args()); + $this->functionName = $functionName; } -} -if (!function_exists('PHPUnit\\Framework\\assertDirectoryNotIsReadable')) { /** - * Asserts that a directory exists and is not readable. - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @codeCoverageIgnore - * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4071 - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertDirectoryNotIsReadable + * @psalm-return non-empty-string */ - function assertDirectoryNotIsReadable(string $directory, string $message = '') : void + public function functionName(): string { - \PHPUnit\Framework\Assert::assertDirectoryNotIsReadable(...func_get_args()); + return $this->functionName; } } -if (!function_exists('PHPUnit\\Framework\\assertDirectoryIsWritable')) { + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Attributes; + +use Attribute; +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/5513 + */ +#[Attribute(Attribute::TARGET_CLASS | Attribute::IS_REPEATABLE)] +final class IgnoreMethodForCodeCoverage +{ /** - * Asserts that a directory exists and is writable. - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertDirectoryIsWritable + * @psalm-var class-string */ - function assertDirectoryIsWritable(string $directory, string $message = '') : void - { - \PHPUnit\Framework\Assert::assertDirectoryIsWritable(...func_get_args()); - } -} -if (!function_exists('PHPUnit\\Framework\\assertDirectoryIsNotWritable')) { + private readonly string $className; /** - * Asserts that a directory exists and is not writable. - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertDirectoryIsNotWritable + * @psalm-var non-empty-string */ - function assertDirectoryIsNotWritable(string $directory, string $message = '') : void - { - \PHPUnit\Framework\Assert::assertDirectoryIsNotWritable(...func_get_args()); - } -} -if (!function_exists('PHPUnit\\Framework\\assertDirectoryNotIsWritable')) { + private readonly string $methodName; /** - * Asserts that a directory exists and is not writable. - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @codeCoverageIgnore - * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4074 - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertDirectoryNotIsWritable + * @psalm-param class-string $className + * @psalm-param non-empty-string $methodName */ - function assertDirectoryNotIsWritable(string $directory, string $message = '') : void + public function __construct(string $className, string $methodName) { - \PHPUnit\Framework\Assert::assertDirectoryNotIsWritable(...func_get_args()); + $this->className = $className; + $this->methodName = $methodName; } -} -if (!function_exists('PHPUnit\\Framework\\assertFileExists')) { /** - * Asserts that a file exists. - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertFileExists + * @psalm-return class-string */ - function assertFileExists(string $filename, string $message = '') : void + public function className(): string { - \PHPUnit\Framework\Assert::assertFileExists(...func_get_args()); + return $this->className; } -} -if (!function_exists('PHPUnit\\Framework\\assertFileDoesNotExist')) { /** - * Asserts that a file does not exist. - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertFileDoesNotExist + * @psalm-return non-empty-string */ - function assertFileDoesNotExist(string $filename, string $message = '') : void + public function methodName(): string { - \PHPUnit\Framework\Assert::assertFileDoesNotExist(...func_get_args()); + return $this->methodName; } } -if (!function_exists('PHPUnit\\Framework\\assertFileNotExists')) { - /** - * Asserts that a file does not exist. - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @codeCoverageIgnore - * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4077 - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertFileNotExists - */ - function assertFileNotExists(string $filename, string $message = '') : void + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Attributes; + +use Attribute; +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +#[Attribute(Attribute::TARGET_CLASS)] +final class Large +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Attributes; + +use Attribute; +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +#[Attribute(Attribute::TARGET_CLASS)] +final class Medium +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Attributes; + +use Attribute; +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +#[Attribute(Attribute::TARGET_METHOD)] +final class PostCondition +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Attributes; + +use Attribute; +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +#[Attribute(Attribute::TARGET_METHOD)] +final class PreCondition +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Attributes; + +use Attribute; +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +#[Attribute(Attribute::TARGET_CLASS | Attribute::TARGET_METHOD)] +final class PreserveGlobalState +{ + private readonly bool $enabled; + public function __construct(bool $enabled) { - \PHPUnit\Framework\Assert::assertFileNotExists(...func_get_args()); + $this->enabled = $enabled; } -} -if (!function_exists('PHPUnit\\Framework\\assertFileIsReadable')) { - /** - * Asserts that a file exists and is readable. - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertFileIsReadable - */ - function assertFileIsReadable(string $file, string $message = '') : void + public function enabled(): bool { - \PHPUnit\Framework\Assert::assertFileIsReadable(...func_get_args()); + return $this->enabled; } } -if (!function_exists('PHPUnit\\Framework\\assertFileIsNotReadable')) { + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Attributes; + +use Attribute; +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +#[Attribute(Attribute::TARGET_CLASS | Attribute::TARGET_METHOD | Attribute::IS_REPEATABLE)] +final class RequiresFunction +{ /** - * Asserts that a file exists and is not readable. - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertFileIsNotReadable + * @psalm-var non-empty-string */ - function assertFileIsNotReadable(string $file, string $message = '') : void - { - \PHPUnit\Framework\Assert::assertFileIsNotReadable(...func_get_args()); - } -} -if (!function_exists('PHPUnit\\Framework\\assertFileNotIsReadable')) { + private readonly string $functionName; /** - * Asserts that a file exists and is not readable. - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @codeCoverageIgnore - * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4080 - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertFileNotIsReadable + * @psalm-param non-empty-string $functionName */ - function assertFileNotIsReadable(string $file, string $message = '') : void + public function __construct(string $functionName) { - \PHPUnit\Framework\Assert::assertFileNotIsReadable(...func_get_args()); + $this->functionName = $functionName; } -} -if (!function_exists('PHPUnit\\Framework\\assertFileIsWritable')) { /** - * Asserts that a file exists and is writable. - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertFileIsWritable + * @psalm-return non-empty-string */ - function assertFileIsWritable(string $file, string $message = '') : void + public function functionName(): string { - \PHPUnit\Framework\Assert::assertFileIsWritable(...func_get_args()); + return $this->functionName; } } -if (!function_exists('PHPUnit\\Framework\\assertFileIsNotWritable')) { + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Attributes; + +use Attribute; +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +#[Attribute(Attribute::TARGET_CLASS | Attribute::TARGET_METHOD | Attribute::IS_REPEATABLE)] +final class RequiresMethod +{ /** - * Asserts that a file exists and is not writable. - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertFileIsNotWritable + * @psalm-var class-string */ - function assertFileIsNotWritable(string $file, string $message = '') : void - { - \PHPUnit\Framework\Assert::assertFileIsNotWritable(...func_get_args()); - } -} -if (!function_exists('PHPUnit\\Framework\\assertFileNotIsWritable')) { + private readonly string $className; /** - * Asserts that a file exists and is not writable. - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @codeCoverageIgnore - * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4083 - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertFileNotIsWritable + * @psalm-var non-empty-string + */ + private readonly string $methodName; + /** + * @psalm-param class-string $className + * @psalm-param non-empty-string $methodName */ - function assertFileNotIsWritable(string $file, string $message = '') : void + public function __construct(string $className, string $methodName) { - \PHPUnit\Framework\Assert::assertFileNotIsWritable(...func_get_args()); + $this->className = $className; + $this->methodName = $methodName; } -} -if (!function_exists('PHPUnit\\Framework\\assertTrue')) { /** - * Asserts that a condition is true. - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @psalm-assert true $condition - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertTrue + * @psalm-return class-string */ - function assertTrue($condition, string $message = '') : void + public function className(): string { - \PHPUnit\Framework\Assert::assertTrue(...func_get_args()); + return $this->className; } -} -if (!function_exists('PHPUnit\\Framework\\assertNotTrue')) { /** - * Asserts that a condition is not true. - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @psalm-assert !true $condition - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertNotTrue + * @psalm-return non-empty-string */ - function assertNotTrue($condition, string $message = '') : void + public function methodName(): string { - \PHPUnit\Framework\Assert::assertNotTrue(...func_get_args()); + return $this->methodName; } } -if (!function_exists('PHPUnit\\Framework\\assertFalse')) { + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Attributes; + +use Attribute; +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +#[Attribute(Attribute::TARGET_CLASS | Attribute::TARGET_METHOD)] +final class RequiresOperatingSystem +{ /** - * Asserts that a condition is false. - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @psalm-assert false $condition - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertFalse + * @psalm-var non-empty-string + */ + private readonly string $regularExpression; + /** + * @psalm-param non-empty-string $regularExpression */ - function assertFalse($condition, string $message = '') : void + public function __construct(string $regularExpression) { - \PHPUnit\Framework\Assert::assertFalse(...func_get_args()); + $this->regularExpression = $regularExpression; } -} -if (!function_exists('PHPUnit\\Framework\\assertNotFalse')) { /** - * Asserts that a condition is not false. - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @psalm-assert !false $condition - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertNotFalse + * @psalm-return non-empty-string */ - function assertNotFalse($condition, string $message = '') : void + public function regularExpression(): string { - \PHPUnit\Framework\Assert::assertNotFalse(...func_get_args()); + return $this->regularExpression; } } -if (!function_exists('PHPUnit\\Framework\\assertNull')) { + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Attributes; + +use Attribute; +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +#[Attribute(Attribute::TARGET_CLASS | Attribute::TARGET_METHOD)] +final class RequiresOperatingSystemFamily +{ /** - * Asserts that a variable is null. - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @psalm-assert null $actual - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertNull + * @psalm-var non-empty-string + */ + private readonly string $operatingSystemFamily; + /** + * @psalm-param non-empty-string $operatingSystemFamily */ - function assertNull($actual, string $message = '') : void + public function __construct(string $operatingSystemFamily) { - \PHPUnit\Framework\Assert::assertNull(...func_get_args()); + $this->operatingSystemFamily = $operatingSystemFamily; } -} -if (!function_exists('PHPUnit\\Framework\\assertNotNull')) { /** - * Asserts that a variable is not null. - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @psalm-assert !null $actual - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertNotNull + * @psalm-return non-empty-string */ - function assertNotNull($actual, string $message = '') : void + public function operatingSystemFamily(): string { - \PHPUnit\Framework\Assert::assertNotNull(...func_get_args()); + return $this->operatingSystemFamily; } } -if (!function_exists('PHPUnit\\Framework\\assertFinite')) { + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Attributes; + +use Attribute; +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +#[Attribute(Attribute::TARGET_CLASS | Attribute::TARGET_METHOD)] +final class RequiresPhp +{ /** - * Asserts that a variable is finite. - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertFinite + * @psalm-var non-empty-string + */ + private readonly string $versionRequirement; + /** + * @psalm-param non-empty-string $versionRequirement */ - function assertFinite($actual, string $message = '') : void + public function __construct(string $versionRequirement) { - \PHPUnit\Framework\Assert::assertFinite(...func_get_args()); + $this->versionRequirement = $versionRequirement; } -} -if (!function_exists('PHPUnit\\Framework\\assertInfinite')) { /** - * Asserts that a variable is infinite. - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertInfinite + * @psalm-return non-empty-string */ - function assertInfinite($actual, string $message = '') : void + public function versionRequirement(): string { - \PHPUnit\Framework\Assert::assertInfinite(...func_get_args()); + return $this->versionRequirement; } } -if (!function_exists('PHPUnit\\Framework\\assertNan')) { + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Attributes; + +use Attribute; +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +#[Attribute(Attribute::TARGET_CLASS | Attribute::TARGET_METHOD | Attribute::IS_REPEATABLE)] +final class RequiresPhpExtension +{ /** - * Asserts that a variable is nan. - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertNan + * @psalm-var non-empty-string + */ + private readonly string $extension; + /** + * @psalm-var null|non-empty-string + */ + private readonly ?string $versionRequirement; + /** + * @psalm-param non-empty-string $extension + * @psalm-param null|non-empty-string $versionRequirement */ - function assertNan($actual, string $message = '') : void + public function __construct(string $extension, ?string $versionRequirement = null) { - \PHPUnit\Framework\Assert::assertNan(...func_get_args()); + $this->extension = $extension; + $this->versionRequirement = $versionRequirement; } -} -if (!function_exists('PHPUnit\\Framework\\assertClassHasAttribute')) { /** - * Asserts that a class has a specified attribute. - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * @throws Exception - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertClassHasAttribute + * @psalm-return non-empty-string */ - function assertClassHasAttribute(string $attributeName, string $className, string $message = '') : void + public function extension(): string { - \PHPUnit\Framework\Assert::assertClassHasAttribute(...func_get_args()); + return $this->extension; } -} -if (!function_exists('PHPUnit\\Framework\\assertClassNotHasAttribute')) { /** - * Asserts that a class does not have a specified attribute. - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * @throws Exception - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertClassNotHasAttribute + * @psalm-return null|non-empty-string */ - function assertClassNotHasAttribute(string $attributeName, string $className, string $message = '') : void + public function versionRequirement(): ?string { - \PHPUnit\Framework\Assert::assertClassNotHasAttribute(...func_get_args()); + return $this->versionRequirement; } } -if (!function_exists('PHPUnit\\Framework\\assertClassHasStaticAttribute')) { - /** - * Asserts that a class has a specified static attribute. - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * @throws Exception - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertClassHasStaticAttribute + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Attributes; + +use Attribute; +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +#[Attribute(Attribute::TARGET_CLASS | Attribute::TARGET_METHOD)] +final class RequiresPhpunit +{ + /** + * @psalm-var non-empty-string */ - function assertClassHasStaticAttribute(string $attributeName, string $className, string $message = '') : void - { - \PHPUnit\Framework\Assert::assertClassHasStaticAttribute(...func_get_args()); - } -} -if (!function_exists('PHPUnit\\Framework\\assertClassNotHasStaticAttribute')) { + private readonly string $versionRequirement; /** - * Asserts that a class does not have a specified static attribute. - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * @throws Exception - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertClassNotHasStaticAttribute + * @psalm-param non-empty-string $versionRequirement */ - function assertClassNotHasStaticAttribute(string $attributeName, string $className, string $message = '') : void + public function __construct(string $versionRequirement) { - \PHPUnit\Framework\Assert::assertClassNotHasStaticAttribute(...func_get_args()); + $this->versionRequirement = $versionRequirement; } -} -if (!function_exists('PHPUnit\\Framework\\assertObjectHasAttribute')) { /** - * Asserts that an object has a specified attribute. - * - * @param object $object - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * @throws Exception - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertObjectHasAttribute + * @psalm-return non-empty-string */ - function assertObjectHasAttribute(string $attributeName, $object, string $message = '') : void + public function versionRequirement(): string { - \PHPUnit\Framework\Assert::assertObjectHasAttribute(...func_get_args()); + return $this->versionRequirement; } } -if (!function_exists('PHPUnit\\Framework\\assertObjectNotHasAttribute')) { + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Attributes; + +use Attribute; +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +#[Attribute(Attribute::TARGET_CLASS | Attribute::TARGET_METHOD | Attribute::IS_REPEATABLE)] +final class RequiresSetting +{ /** - * Asserts that an object does not have a specified attribute. - * - * @param object $object - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * @throws Exception - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertObjectNotHasAttribute + * @psalm-var non-empty-string */ - function assertObjectNotHasAttribute(string $attributeName, $object, string $message = '') : void - { - \PHPUnit\Framework\Assert::assertObjectNotHasAttribute(...func_get_args()); - } -} -if (!function_exists('PHPUnit\\Framework\\assertObjectHasProperty')) { + private readonly string $setting; /** - * Asserts that an object has a specified property. - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * @throws Exception - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertObjectHasProperty + * @psalm-var non-empty-string */ - function assertObjectHasProperty(string $attributeName, object $object, string $message = '') : void - { - \PHPUnit\Framework\Assert::assertObjectHasProperty(...func_get_args()); - } -} -if (!function_exists('PHPUnit\\Framework\\assertObjectNotHasProperty')) { + private readonly string $value; /** - * Asserts that an object does not have a specified property. - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * @throws Exception - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertObjectNotHasProperty + * @psalm-param non-empty-string $setting + * @psalm-param non-empty-string $value */ - function assertObjectNotHasProperty(string $attributeName, object $object, string $message = '') : void + public function __construct(string $setting, string $value) { - \PHPUnit\Framework\Assert::assertObjectNotHasProperty(...func_get_args()); + $this->setting = $setting; + $this->value = $value; } -} -if (!function_exists('PHPUnit\\Framework\\assertSame')) { /** - * 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 InvalidArgumentException - * - * @psalm-template ExpectedType - * - * @psalm-param ExpectedType $expected - * - * @psalm-assert =ExpectedType $actual - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertSame + * @psalm-return non-empty-string */ - function assertSame($expected, $actual, string $message = '') : void + public function setting(): string { - \PHPUnit\Framework\Assert::assertSame(...func_get_args()); + return $this->setting; } -} -if (!function_exists('PHPUnit\\Framework\\assertNotSame')) { /** - * 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 InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertNotSame + * @psalm-return non-empty-string */ - function assertNotSame($expected, $actual, string $message = '') : void + public function value(): string { - \PHPUnit\Framework\Assert::assertNotSame(...func_get_args()); + return $this->value; } } -if (!function_exists('PHPUnit\\Framework\\assertInstanceOf')) { + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Attributes; + +use Attribute; +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +#[Attribute(Attribute::TARGET_CLASS)] +final class RunClassInSeparateProcess +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Attributes; + +use Attribute; +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +#[Attribute(Attribute::TARGET_METHOD)] +final class RunInSeparateProcess +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Attributes; + +use Attribute; +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +#[Attribute(Attribute::TARGET_CLASS)] +final class RunTestsInSeparateProcesses +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Attributes; + +use Attribute; +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +#[Attribute(Attribute::TARGET_CLASS)] +final class Small +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Attributes; + +use Attribute; +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +#[Attribute(Attribute::TARGET_METHOD)] +final class Test +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Attributes; + +use Attribute; +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +#[Attribute(Attribute::TARGET_CLASS | Attribute::TARGET_METHOD)] +final class TestDox +{ /** - * Asserts that a variable is of a given type. - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * @throws Exception - * - * @psalm-template ExpectedType of object - * - * @psalm-param class-string $expected - * - * @psalm-assert =ExpectedType $actual - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertInstanceOf + * @psalm-var non-empty-string + */ + private readonly string $text; + /** + * @psalm-param non-empty-string $text */ - function assertInstanceOf(string $expected, $actual, string $message = '') : void + public function __construct(string $text) { - \PHPUnit\Framework\Assert::assertInstanceOf(...func_get_args()); + $this->text = $text; } -} -if (!function_exists('PHPUnit\\Framework\\assertNotInstanceOf')) { /** - * Asserts that a variable is not of a given type. - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * @throws Exception - * - * @psalm-template ExpectedType of object - * - * @psalm-param class-string $expected - * - * @psalm-assert !ExpectedType $actual - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertNotInstanceOf + * @psalm-return non-empty-string */ - function assertNotInstanceOf(string $expected, $actual, string $message = '') : void + public function text(): string { - \PHPUnit\Framework\Assert::assertNotInstanceOf(...func_get_args()); + return $this->text; } } -if (!function_exists('PHPUnit\\Framework\\assertIsArray')) { - /** - * Asserts that a variable is of type array. - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @psalm-assert array $actual - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertIsArray - */ - function assertIsArray($actual, string $message = '') : void + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Attributes; + +use Attribute; +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +#[Attribute(Attribute::TARGET_METHOD | Attribute::IS_REPEATABLE)] +final class TestWith +{ + private readonly array $data; + public function __construct(array $data) { - \PHPUnit\Framework\Assert::assertIsArray(...func_get_args()); + $this->data = $data; + } + public function data(): array + { + return $this->data; } } -if (!function_exists('PHPUnit\\Framework\\assertIsBool')) { + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Attributes; + +use Attribute; +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +#[Attribute(Attribute::TARGET_METHOD | Attribute::IS_REPEATABLE)] +final class TestWithJson +{ /** - * Asserts that a variable is of type bool. - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @psalm-assert bool $actual - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertIsBool + * @psalm-var non-empty-string + */ + private readonly string $json; + /** + * @psalm-param non-empty-string $json */ - function assertIsBool($actual, string $message = '') : void + public function __construct(string $json) { - \PHPUnit\Framework\Assert::assertIsBool(...func_get_args()); + $this->json = $json; } -} -if (!function_exists('PHPUnit\\Framework\\assertIsFloat')) { /** - * Asserts that a variable is of type float. - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @psalm-assert float $actual - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertIsFloat + * @psalm-return non-empty-string */ - function assertIsFloat($actual, string $message = '') : void + public function json(): string { - \PHPUnit\Framework\Assert::assertIsFloat(...func_get_args()); + return $this->json; } } -if (!function_exists('PHPUnit\\Framework\\assertIsInt')) { + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Attributes; + +use Attribute; +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +#[Attribute(Attribute::TARGET_CLASS | Attribute::TARGET_METHOD | Attribute::IS_REPEATABLE)] +final class Ticket +{ /** - * Asserts that a variable is of type int. - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @psalm-assert int $actual - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertIsInt + * @psalm-var non-empty-string */ - function assertIsInt($actual, string $message = '') : void + private readonly string $text; + /** + * @psalm-param non-empty-string $text + */ + public function __construct(string $text) { - \PHPUnit\Framework\Assert::assertIsInt(...func_get_args()); + $this->text = $text; } -} -if (!function_exists('PHPUnit\\Framework\\assertIsNumeric')) { /** - * Asserts that a variable is of type numeric. - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @psalm-assert numeric $actual - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertIsNumeric + * @psalm-return non-empty-string */ - function assertIsNumeric($actual, string $message = '') : void + public function text(): string { - \PHPUnit\Framework\Assert::assertIsNumeric(...func_get_args()); + return $this->text; } } -if (!function_exists('PHPUnit\\Framework\\assertIsObject')) { + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Attributes; + +use Attribute; +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +#[Attribute(Attribute::TARGET_CLASS | Attribute::IS_REPEATABLE)] +final class UsesClass +{ /** - * Asserts that a variable is of type object. - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @psalm-assert object $actual - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertIsObject + * @psalm-var class-string */ - function assertIsObject($actual, string $message = '') : void + private readonly string $className; + /** + * @psalm-param class-string $className + */ + public function __construct(string $className) { - \PHPUnit\Framework\Assert::assertIsObject(...func_get_args()); + $this->className = $className; } -} -if (!function_exists('PHPUnit\\Framework\\assertIsResource')) { /** - * Asserts that a variable is of type resource. - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @psalm-assert resource $actual - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertIsResource + * @psalm-return class-string */ - function assertIsResource($actual, string $message = '') : void + public function className(): string { - \PHPUnit\Framework\Assert::assertIsResource(...func_get_args()); + return $this->className; } } -if (!function_exists('PHPUnit\\Framework\\assertIsClosedResource')) { + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Attributes; + +use Attribute; +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +#[Attribute(Attribute::TARGET_CLASS | Attribute::IS_REPEATABLE)] +final class UsesFunction +{ /** - * Asserts that a variable is of type resource and is closed. - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @psalm-assert resource $actual - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertIsClosedResource + * @psalm-var non-empty-string + */ + private readonly string $functionName; + /** + * @psalm-param non-empty-string $functionName */ - function assertIsClosedResource($actual, string $message = '') : void + public function __construct(string $functionName) { - \PHPUnit\Framework\Assert::assertIsClosedResource(...func_get_args()); + $this->functionName = $functionName; } -} -if (!function_exists('PHPUnit\\Framework\\assertIsString')) { /** - * Asserts that a variable is of type string. - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @psalm-assert string $actual - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertIsString + * @psalm-return non-empty-string */ - function assertIsString($actual, string $message = '') : void + public function functionName(): string { - \PHPUnit\Framework\Assert::assertIsString(...func_get_args()); + return $this->functionName; } } -if (!function_exists('PHPUnit\\Framework\\assertIsScalar')) { + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Attributes; + +use Attribute; +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +#[Attribute(Attribute::TARGET_METHOD)] +final class WithoutErrorHandler +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Constraint; + +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class IsFalse extends \PHPUnit\Framework\Constraint\Constraint +{ /** - * Asserts that a variable is of type scalar. - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @psalm-assert scalar $actual - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertIsScalar + * Returns a string representation of the constraint. */ - function assertIsScalar($actual, string $message = '') : void + public function toString(): string { - \PHPUnit\Framework\Assert::assertIsScalar(...func_get_args()); + return 'is false'; } -} -if (!function_exists('PHPUnit\\Framework\\assertIsCallable')) { /** - * Asserts that a variable is of type callable. - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @psalm-assert callable $actual - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertIsCallable + * Evaluates the constraint for parameter $other. Returns true if the + * constraint is met, false otherwise. */ - function assertIsCallable($actual, string $message = '') : void + protected function matches(mixed $other): bool { - \PHPUnit\Framework\Assert::assertIsCallable(...func_get_args()); + return $other === \false; } } -if (!function_exists('PHPUnit\\Framework\\assertIsIterable')) { + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Constraint; + +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class IsTrue extends \PHPUnit\Framework\Constraint\Constraint +{ /** - * Asserts that a variable is of type iterable. - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @psalm-assert iterable $actual - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertIsIterable + * Returns a string representation of the constraint. */ - function assertIsIterable($actual, string $message = '') : void + public function toString(): string { - \PHPUnit\Framework\Assert::assertIsIterable(...func_get_args()); + return 'is true'; } -} -if (!function_exists('PHPUnit\\Framework\\assertIsNotArray')) { /** - * Asserts that a variable is not of type array. - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @psalm-assert !array $actual - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertIsNotArray + * Evaluates the constraint for parameter $other. Returns true if the + * constraint is met, false otherwise. */ - function assertIsNotArray($actual, string $message = '') : void + protected function matches(mixed $other): bool { - \PHPUnit\Framework\Assert::assertIsNotArray(...func_get_args()); + return $other === \true; } } -if (!function_exists('PHPUnit\\Framework\\assertIsNotBool')) { + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Constraint; + +/** + * @psalm-template CallbackInput of mixed + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class Callback extends \PHPUnit\Framework\Constraint\Constraint +{ /** - * Asserts that a variable is not of type bool. - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @psalm-assert !bool $actual - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertIsNotBool + * @psalm-var callable(CallbackInput $input): bool */ - function assertIsNotBool($actual, string $message = '') : void - { - \PHPUnit\Framework\Assert::assertIsNotBool(...func_get_args()); - } -} -if (!function_exists('PHPUnit\\Framework\\assertIsNotFloat')) { + private readonly mixed $callback; /** - * Asserts that a variable is not of type float. - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @psalm-assert !float $actual - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertIsNotFloat + * @psalm-param callable(CallbackInput $input): bool $callback */ - function assertIsNotFloat($actual, string $message = '') : void + public function __construct(callable $callback) { - \PHPUnit\Framework\Assert::assertIsNotFloat(...func_get_args()); + $this->callback = $callback; } -} -if (!function_exists('PHPUnit\\Framework\\assertIsNotInt')) { /** - * Asserts that a variable is not of type int. - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @psalm-assert !int $actual - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertIsNotInt + * Returns a string representation of the constraint. */ - function assertIsNotInt($actual, string $message = '') : void + public function toString(): string { - \PHPUnit\Framework\Assert::assertIsNotInt(...func_get_args()); + return 'is accepted by specified callback'; } -} -if (!function_exists('PHPUnit\\Framework\\assertIsNotNumeric')) { /** - * Asserts that a variable is not of type numeric. - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @psalm-assert !numeric $actual - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * Evaluates the constraint for parameter $value. Returns true if the + * constraint is met, false otherwise. * - * @see Assert::assertIsNotNumeric + * @psalm-param CallbackInput $other */ - function assertIsNotNumeric($actual, string $message = '') : void + protected function matches(mixed $other): bool { - \PHPUnit\Framework\Assert::assertIsNotNumeric(...func_get_args()); + return ($this->callback)($other); } } -if (!function_exists('PHPUnit\\Framework\\assertIsNotObject')) { - /** - * Asserts that a variable is not of type object. - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @psalm-assert !object $actual - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertIsNotObject - */ - function assertIsNotObject($actual, string $message = '') : void + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Constraint; + +use function count; +use function is_countable; +use function iterator_count; +use function sprintf; +use EmptyIterator; +use Generator; +use Iterator; +use IteratorAggregate; +use PHPUnit\Framework\Exception; +use PHPUnit\Framework\GeneratorNotSupportedException; +use Traversable; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +class Count extends \PHPUnit\Framework\Constraint\Constraint +{ + private readonly int $expectedCount; + public function __construct(int $expected) { - \PHPUnit\Framework\Assert::assertIsNotObject(...func_get_args()); + $this->expectedCount = $expected; + } + public function toString(): string + { + return sprintf('count matches %d', $this->expectedCount); } -} -if (!function_exists('PHPUnit\\Framework\\assertIsNotResource')) { /** - * Asserts that a variable is not of type resource. - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @psalm-assert !resource $actual - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * Evaluates the constraint for parameter $other. Returns true if the + * constraint is met, false otherwise. * - * @see Assert::assertIsNotResource + * @throws Exception */ - function assertIsNotResource($actual, string $message = '') : void + protected function matches(mixed $other): bool { - \PHPUnit\Framework\Assert::assertIsNotResource(...func_get_args()); + return $this->expectedCount === $this->getCountOf($other); } -} -if (!function_exists('PHPUnit\\Framework\\assertIsNotClosedResource')) { /** - * Asserts that a variable is not of type resource. - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @psalm-assert !resource $actual - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertIsNotClosedResource + * @throws Exception */ - function assertIsNotClosedResource($actual, string $message = '') : void + protected function getCountOf(mixed $other): ?int { - \PHPUnit\Framework\Assert::assertIsNotClosedResource(...func_get_args()); + if (is_countable($other)) { + return count($other); + } + if ($other instanceof EmptyIterator) { + return 0; + } + if ($other instanceof Traversable) { + while ($other instanceof IteratorAggregate) { + try { + $other = $other->getIterator(); + } catch (\Exception $e) { + throw new Exception($e->getMessage(), $e->getCode(), $e); + } + } + $iterator = $other; + if ($iterator instanceof Generator) { + throw new GeneratorNotSupportedException(); + } + 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; } -} -if (!function_exists('PHPUnit\\Framework\\assertIsNotString')) { /** - * Asserts that a variable is not of type string. - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @psalm-assert !string $actual + * Returns the description of the failure. * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * The beginning of failure messages is "Failed asserting that" in most + * cases. This method should return the second part of that sentence. * - * @see Assert::assertIsNotString + * @throws Exception */ - function assertIsNotString($actual, string $message = '') : void + protected function failureDescription(mixed $other): string { - \PHPUnit\Framework\Assert::assertIsNotString(...func_get_args()); + return sprintf('actual size %d matches expected size %d', (int) $this->getCountOf($other), $this->expectedCount); } } -if (!function_exists('PHPUnit\\Framework\\assertIsNotScalar')) { + + * + * 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\Exporter; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class GreaterThan extends \PHPUnit\Framework\Constraint\Constraint +{ + private readonly mixed $value; + public function __construct(mixed $value) + { + $this->value = $value; + } /** - * Asserts that a variable is not of type scalar. - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @psalm-assert !scalar $actual - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertIsNotScalar + * Returns a string representation of the constraint. */ - function assertIsNotScalar($actual, string $message = '') : void + public function toString(bool $exportObjects = \false): string { - \PHPUnit\Framework\Assert::assertIsNotScalar(...func_get_args()); + return 'is greater than ' . Exporter::export($this->value, $exportObjects); } -} -if (!function_exists('PHPUnit\\Framework\\assertIsNotCallable')) { /** - * Asserts that a variable is not of type callable. - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @psalm-assert !callable $actual - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertIsNotCallable + * Evaluates the constraint for parameter $other. Returns true if the + * constraint is met, false otherwise. */ - function assertIsNotCallable($actual, string $message = '') : void + protected function matches(mixed $other): bool { - \PHPUnit\Framework\Assert::assertIsNotCallable(...func_get_args()); + return $this->value < $other; } } -if (!function_exists('PHPUnit\\Framework\\assertIsNotIterable')) { + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Constraint; + +use function count; +use function gettype; +use function sprintf; +use function str_starts_with; +use Countable; +use EmptyIterator; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class IsEmpty extends \PHPUnit\Framework\Constraint\Constraint +{ /** - * Asserts that a variable is not of type iterable. - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @psalm-assert !iterable $actual - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertIsNotIterable + * Returns a string representation of the constraint. */ - function assertIsNotIterable($actual, string $message = '') : void + public function toString(): string { - \PHPUnit\Framework\Assert::assertIsNotIterable(...func_get_args()); + return 'is empty'; } -} -if (!function_exists('PHPUnit\\Framework\\assertMatchesRegularExpression')) { /** - * Asserts that a string matches a given regular expression. - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertMatchesRegularExpression + * Evaluates the constraint for parameter $other. Returns true if the + * constraint is met, false otherwise. */ - function assertMatchesRegularExpression(string $pattern, string $string, string $message = '') : void + protected function matches(mixed $other): bool { - \PHPUnit\Framework\Assert::assertMatchesRegularExpression(...func_get_args()); + if ($other instanceof EmptyIterator) { + return \true; + } + if ($other instanceof Countable) { + return count($other) === 0; + } + return empty($other); } -} -if (!function_exists('PHPUnit\\Framework\\assertRegExp')) { /** - * Asserts that a string matches a given regular expression. - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @codeCoverageIgnore - * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4086 - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * Returns the description of the failure. * - * @see Assert::assertRegExp + * The beginning of failure messages is "Failed asserting that" in most + * cases. This method should return the second part of that sentence. */ - function assertRegExp(string $pattern, string $string, string $message = '') : void + protected function failureDescription(mixed $other): string { - \PHPUnit\Framework\Assert::assertRegExp(...func_get_args()); + $type = gettype($other); + return sprintf('%s %s %s', (str_starts_with($type, 'a') || str_starts_with($type, 'o')) ? 'an' : 'a', $type, $this->toString(\true)); } } -if (!function_exists('PHPUnit\\Framework\\assertDoesNotMatchRegularExpression')) { + + * + * 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\Exporter; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class LessThan extends \PHPUnit\Framework\Constraint\Constraint +{ + private readonly mixed $value; + public function __construct(mixed $value) + { + $this->value = $value; + } /** - * Asserts that a string does not match a given regular expression. - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertDoesNotMatchRegularExpression + * Returns a string representation of the constraint. */ - function assertDoesNotMatchRegularExpression(string $pattern, string $string, string $message = '') : void + public function toString(bool $exportObjects = \false): string { - \PHPUnit\Framework\Assert::assertDoesNotMatchRegularExpression(...func_get_args()); + return 'is less than ' . Exporter::export($this->value, $exportObjects); } -} -if (!function_exists('PHPUnit\\Framework\\assertNotRegExp')) { /** - * Asserts that a string does not match a given regular expression. - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @codeCoverageIgnore - * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4089 - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertNotRegExp + * Evaluates the constraint for parameter $other. Returns true if the + * constraint is met, false otherwise. */ - function assertNotRegExp(string $pattern, string $string, string $message = '') : void + protected function matches(mixed $other): bool { - \PHPUnit\Framework\Assert::assertNotRegExp(...func_get_args()); + return $this->value > $other; } } -if (!function_exists('PHPUnit\\Framework\\assertSameSize')) { + + * + * 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\Exception; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class SameSize extends \PHPUnit\Framework\Constraint\Count +{ /** - * Assert that the size of two arrays (or `Countable` or `Traversable` objects) - * is the same. - * - * @param Countable|iterable $expected - * @param Countable|iterable $actual + * @psalm-param Countable|iterable $expected * - * @throws ExpectationFailedException - * @throws InvalidArgumentException * @throws Exception - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertSameSize */ - function assertSameSize($expected, $actual, string $message = '') : void + public function __construct($expected) { - \PHPUnit\Framework\Assert::assertSameSize(...func_get_args()); + parent::__construct((int) $this->getCountOf($expected)); } } -if (!function_exists('PHPUnit\\Framework\\assertNotSameSize')) { - /** - * 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 InvalidArgumentException - * @throws Exception - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertNotSameSize - */ - function assertNotSameSize($expected, $actual, string $message = '') : void - { - \PHPUnit\Framework\Assert::assertNotSameSize(...func_get_args()); - } -} -if (!function_exists('PHPUnit\\Framework\\assertStringMatchesFormat')) { + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Constraint; + +use function gettype; +use function sprintf; +use function strtolower; +use Countable; +use PHPUnit\Framework\ExpectationFailedException; +use PHPUnit\Framework\SelfDescribing; +use PHPUnit\Util\Exporter; +use PHPUnitPHAR\SebastianBergmann\Comparator\ComparisonFailure; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +abstract class Constraint implements Countable, SelfDescribing +{ /** - * Asserts that a string matches a given format string. + * Evaluates the constraint for parameter $other. * - * @throws ExpectationFailedException - * @throws InvalidArgumentException + * If $returnResult is set to false (the default), an exception is thrown + * in case of a failure. null is returned otherwise. * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * 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. * - * @see Assert::assertStringMatchesFormat + * @throws ExpectationFailedException */ - function assertStringMatchesFormat(string $format, string $string, string $message = '') : void + public function evaluate(mixed $other, string $description = '', bool $returnResult = \false): ?bool { - \PHPUnit\Framework\Assert::assertStringMatchesFormat(...func_get_args()); + $success = \false; + if ($this->matches($other)) { + $success = \true; + } + if ($returnResult) { + return $success; + } + if (!$success) { + $this->fail($other, $description); + } + return null; } -} -if (!function_exists('PHPUnit\\Framework\\assertStringNotMatchesFormat')) { /** - * Asserts that a string does not match a given format string. - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertStringNotMatchesFormat + * Counts the number of constraint elements. */ - function assertStringNotMatchesFormat(string $format, string $string, string $message = '') : void + public function count(): int { - \PHPUnit\Framework\Assert::assertStringNotMatchesFormat(...func_get_args()); + return 1; } -} -if (!function_exists('PHPUnit\\Framework\\assertStringMatchesFormatFile')) { /** - * Asserts that a string matches a given format file. - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertStringMatchesFormatFile + * @deprecated */ - function assertStringMatchesFormatFile(string $formatFile, string $string, string $message = '') : void + protected function exporter(): \PHPUnitPHAR\SebastianBergmann\Exporter\Exporter { - \PHPUnit\Framework\Assert::assertStringMatchesFormatFile(...func_get_args()); + return new \PHPUnitPHAR\SebastianBergmann\Exporter\Exporter(); } -} -if (!function_exists('PHPUnit\\Framework\\assertStringNotMatchesFormatFile')) { /** - * Asserts that a string does not match a given format string. - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * Evaluates the constraint for parameter $other. Returns true if the + * constraint is met, false otherwise. * - * @see Assert::assertStringNotMatchesFormatFile + * This method can be overridden to implement the evaluation algorithm. */ - function assertStringNotMatchesFormatFile(string $formatFile, string $string, string $message = '') : void + protected function matches(mixed $other): bool { - \PHPUnit\Framework\Assert::assertStringNotMatchesFormatFile(...func_get_args()); + return \false; } -} -if (!function_exists('PHPUnit\\Framework\\assertStringStartsWith')) { /** - * Asserts that a string starts with a given prefix. + * Throws an exception for the given compared value and test description. * * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertStringStartsWith */ - function assertStringStartsWith(string $prefix, string $string, string $message = '') : void + protected function fail(mixed $other, string $description, ?ComparisonFailure $comparisonFailure = null): never { - \PHPUnit\Framework\Assert::assertStringStartsWith(...func_get_args()); + $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); } -} -if (!function_exists('PHPUnit\\Framework\\assertStringStartsNotWith')) { /** - * Asserts that a string starts not with a given prefix. - * - * @param string $prefix - * @param string $string - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * Return additional failure description where needed. * - * @see Assert::assertStringStartsNotWith + * The function can be overridden to provide additional failure + * information like a diff */ - function assertStringStartsNotWith($prefix, $string, string $message = '') : void + protected function additionalFailureDescription(mixed $other): string { - \PHPUnit\Framework\Assert::assertStringStartsNotWith(...func_get_args()); + return ''; } -} -if (!function_exists('PHPUnit\\Framework\\assertStringContainsString')) { /** - * @throws ExpectationFailedException - * @throws InvalidArgumentException + * Returns the description of the failure. * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * The beginning of failure messages is "Failed asserting that" in most + * cases. This method should return the second part of that sentence. * - * @see Assert::assertStringContainsString + * To provide additional failure information additionalFailureDescription + * can be used. */ - function assertStringContainsString(string $needle, string $haystack, string $message = '') : void + protected function failureDescription(mixed $other): string { - \PHPUnit\Framework\Assert::assertStringContainsString(...func_get_args()); + return Exporter::export($other, \true) . ' ' . $this->toString(\true); } -} -if (!function_exists('PHPUnit\\Framework\\assertStringContainsStringIgnoringCase')) { /** - * @throws ExpectationFailedException - * @throws InvalidArgumentException + * Returns a custom string representation of the constraint object when it + * appears in context of an $operator expression. * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * The purpose of this method is to provide meaningful descriptive string + * in context of operators such as LogicalNot. Native PHPUnit constraints + * are supported out of the box by LogicalNot, but externally developed + * ones had no way to provide correct strings in this context. * - * @see Assert::assertStringContainsStringIgnoringCase + * The method shall return empty string, when it does not handle + * customization by itself. */ - function assertStringContainsStringIgnoringCase(string $needle, string $haystack, string $message = '') : void + protected function toStringInContext(\PHPUnit\Framework\Constraint\Operator $operator, mixed $role): string { - \PHPUnit\Framework\Assert::assertStringContainsStringIgnoringCase(...func_get_args()); + return ''; } -} -if (!function_exists('PHPUnit\\Framework\\assertStringNotContainsString')) { /** - * @throws ExpectationFailedException - * @throws InvalidArgumentException + * Returns the description of the failure when this constraint appears in + * context of an $operator expression. * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * The purpose of this method is to provide meaningful failure description + * in context of operators such as LogicalNot. Native PHPUnit constraints + * are supported out of the box by LogicalNot, but externally developed + * ones had no way to provide correct messages in this context. * - * @see Assert::assertStringNotContainsString + * The method shall return empty string, when it does not handle + * customization by itself. */ - function assertStringNotContainsString(string $needle, string $haystack, string $message = '') : void + protected function failureDescriptionInContext(\PHPUnit\Framework\Constraint\Operator $operator, mixed $role, mixed $other): string { - \PHPUnit\Framework\Assert::assertStringNotContainsString(...func_get_args()); + $string = $this->toStringInContext($operator, $role); + if ($string === '') { + return ''; + } + return Exporter::export($other, \true) . ' ' . $string; } -} -if (!function_exists('PHPUnit\\Framework\\assertStringNotContainsStringIgnoringCase')) { /** - * @throws ExpectationFailedException - * @throws InvalidArgumentException + * Reduces the sub-expression starting at $this by skipping degenerate + * sub-expression and returns first descendant constraint that starts + * a non-reducible sub-expression. * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * Returns $this for terminal constraints and for operators that start + * non-reducible sub-expression, or the nearest descendant of $this that + * starts a non-reducible sub-expression. * - * @see Assert::assertStringNotContainsStringIgnoringCase - */ - function assertStringNotContainsStringIgnoringCase(string $needle, string $haystack, string $message = '') : void - { - \PHPUnit\Framework\Assert::assertStringNotContainsStringIgnoringCase(...func_get_args()); - } -} -if (!function_exists('PHPUnit\\Framework\\assertStringEndsWith')) { - /** - * Asserts that a string ends with a given suffix. + * A constraint expression may be modelled as a tree with non-terminal + * nodes (operators) and terminal nodes. For example: * - * @throws ExpectationFailedException - * @throws InvalidArgumentException + * LogicalOr (operator, non-terminal) + * + LogicalAnd (operator, non-terminal) + * | + IsType('int') (terminal) + * | + GreaterThan(10) (terminal) + * + LogicalNot (operator, non-terminal) + * + IsType('array') (terminal) * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * A degenerate sub-expression is a part of the tree, that effectively does + * not contribute to the evaluation of the expression it appears in. An example + * of degenerate sub-expression is a BinaryOperator constructed with single + * operand or nested BinaryOperators, each with single operand. An + * expression involving a degenerate sub-expression is equivalent to a + * reduced expression with the degenerate sub-expression removed, for example * - * @see Assert::assertStringEndsWith - */ - function assertStringEndsWith(string $suffix, string $string, string $message = '') : void - { - \PHPUnit\Framework\Assert::assertStringEndsWith(...func_get_args()); - } -} -if (!function_exists('PHPUnit\\Framework\\assertStringEndsNotWith')) { - /** - * Asserts that a string ends not with a given suffix. + * LogicalAnd (operator) + * + LogicalOr (degenerate operator) + * | + LogicalAnd (degenerate operator) + * | + IsType('int') (terminal) + * + GreaterThan(10) (terminal) * - * @throws ExpectationFailedException - * @throws InvalidArgumentException + * is equivalent to * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * LogicalAnd (operator) + * + IsType('int') (terminal) + * + GreaterThan(10) (terminal) * - * @see Assert::assertStringEndsNotWith - */ - function assertStringEndsNotWith(string $suffix, string $string, string $message = '') : void - { - \PHPUnit\Framework\Assert::assertStringEndsNotWith(...func_get_args()); - } -} -if (!function_exists('PHPUnit\\Framework\\assertXmlFileEqualsXmlFile')) { - /** - * Asserts that two XML files are equal. + * because the subexpression * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * @throws Exception + * + LogicalOr + * + LogicalAnd + * + - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * is degenerate. Calling reduce() on the LogicalOr object above, as well + * as on LogicalAnd, shall return the IsType('int') instance. * - * @see Assert::assertXmlFileEqualsXmlFile - */ - function assertXmlFileEqualsXmlFile(string $expectedFile, string $actualFile, string $message = '') : void - { - \PHPUnit\Framework\Assert::assertXmlFileEqualsXmlFile(...func_get_args()); - } -} -if (!function_exists('PHPUnit\\Framework\\assertXmlFileNotEqualsXmlFile')) { - /** - * Asserts that two XML files are not equal. + * Other specific reductions can be implemented, for example cascade of + * LogicalNot operators * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * @throws \PHPUnit\Util\Exception + * + LogicalNot + * + LogicalNot + * +LogicalNot + * + IsTrue * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * can be reduced to * - * @see Assert::assertXmlFileNotEqualsXmlFile + * LogicalNot + * + IsTrue */ - function assertXmlFileNotEqualsXmlFile(string $expectedFile, string $actualFile, string $message = '') : void + protected function reduce(): self { - \PHPUnit\Framework\Assert::assertXmlFileNotEqualsXmlFile(...func_get_args()); + return $this; } -} -if (!function_exists('PHPUnit\\Framework\\assertXmlStringEqualsXmlFile')) { /** - * Asserts that two XML documents are equal. - * - * @param DOMDocument|string $actualXml - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * @throws \PHPUnit\Util\Xml\Exception - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertXmlStringEqualsXmlFile + * @psalm-return non-empty-string */ - function assertXmlStringEqualsXmlFile(string $expectedFile, $actualXml, string $message = '') : void + protected function valueToTypeStringFragment(mixed $value): string { - \PHPUnit\Framework\Assert::assertXmlStringEqualsXmlFile(...func_get_args()); + $type = strtolower(gettype($value)); + if ($type === 'double') { + $type = 'float'; + } + if ($type === 'resource (closed)') { + $type = 'closed resource'; + } + return match ($type) { + 'array', 'integer', 'object' => 'an ' . $type . ' ', + 'boolean', 'closed resource', 'float', 'resource', 'string' => 'a ' . $type . ' ', + 'null' => 'null ', + default => 'a value of ' . $type . ' ', + }; } } -if (!function_exists('PHPUnit\\Framework\\assertXmlStringNotEqualsXmlFile')) { - /** - * Asserts that two XML documents are not equal. - * - * @param DOMDocument|string $actualXml - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * @throws \PHPUnit\Util\Xml\Exception - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertXmlStringNotEqualsXmlFile - */ - function assertXmlStringNotEqualsXmlFile(string $expectedFile, $actualXml, string $message = '') : void + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Constraint; + +use function is_string; +use function sprintf; +use function str_contains; +use function trim; +use PHPUnit\Framework\ExpectationFailedException; +use PHPUnit\Util\Exporter; +use PHPUnitPHAR\SebastianBergmann\Comparator\ComparisonFailure; +use PHPUnitPHAR\SebastianBergmann\Comparator\Factory as ComparatorFactory; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class IsEqual extends \PHPUnit\Framework\Constraint\Constraint +{ + private readonly mixed $value; + private readonly float $delta; + private readonly bool $canonicalize; + private readonly bool $ignoreCase; + public function __construct(mixed $value, float $delta = 0.0, bool $canonicalize = \false, bool $ignoreCase = \false) { - \PHPUnit\Framework\Assert::assertXmlStringNotEqualsXmlFile(...func_get_args()); + $this->value = $value; + $this->delta = $delta; + $this->canonicalize = $canonicalize; + $this->ignoreCase = $ignoreCase; } -} -if (!function_exists('PHPUnit\\Framework\\assertXmlStringEqualsXmlString')) { /** - * Asserts that two XML documents are equal. - * - * @param DOMDocument|string $expectedXml - * @param DOMDocument|string $actualXml + * Evaluates the constraint for parameter $other. * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * @throws \PHPUnit\Util\Xml\Exception + * If $returnResult is set to false (the default), an exception is thrown + * in case of a failure. null is returned otherwise. * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * 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. * - * @see Assert::assertXmlStringEqualsXmlString + * @throws ExpectationFailedException */ - function assertXmlStringEqualsXmlString($expectedXml, $actualXml, string $message = '') : void + public function evaluate(mixed $other, string $description = '', bool $returnResult = \false): ?bool { - \PHPUnit\Framework\Assert::assertXmlStringEqualsXmlString(...func_get_args()); + // 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; } -} -if (!function_exists('PHPUnit\\Framework\\assertXmlStringNotEqualsXmlString')) { /** - * Asserts that two XML documents are not equal. - * - * @param DOMDocument|string $expectedXml - * @param DOMDocument|string $actualXml - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * @throws \PHPUnit\Util\Xml\Exception - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertXmlStringNotEqualsXmlString + * Returns a string representation of the constraint. */ - function assertXmlStringNotEqualsXmlString($expectedXml, $actualXml, string $message = '') : void + public function toString(bool $exportObjects = \false): string { - \PHPUnit\Framework\Assert::assertXmlStringNotEqualsXmlString(...func_get_args()); + $delta = ''; + if (is_string($this->value)) { + if (str_contains($this->value, "\n")) { + return 'is equal to '; + } + 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', Exporter::export($this->value, $exportObjects), $delta); } } -if (!function_exists('PHPUnit\\Framework\\assertEqualXMLStructure')) { - /** - * Asserts that a hierarchy of DOMElements matches. - * - * @throws AssertionFailedError - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @codeCoverageIgnore - * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4091 - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertEqualXMLStructure - */ - function assertEqualXMLStructure(DOMElement $expectedElement, DOMElement $actualElement, bool $checkAttributes = \false, string $message = '') : void + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Constraint; + +use function is_string; +use function sprintf; +use function str_contains; +use function trim; +use PHPUnit\Framework\ExpectationFailedException; +use PHPUnit\Util\Exporter; +use PHPUnitPHAR\SebastianBergmann\Comparator\ComparisonFailure; +use PHPUnitPHAR\SebastianBergmann\Comparator\Factory as ComparatorFactory; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class IsEqualCanonicalizing extends \PHPUnit\Framework\Constraint\Constraint +{ + private readonly mixed $value; + public function __construct(mixed $value) { - \PHPUnit\Framework\Assert::assertEqualXMLStructure(...func_get_args()); + $this->value = $value; } -} -if (!function_exists('PHPUnit\\Framework\\assertThat')) { /** - * Evaluates a PHPUnit\Framework\Constraint matcher object. + * Evaluates the constraint for parameter $other. * - * @throws ExpectationFailedException - * @throws InvalidArgumentException + * If $returnResult is set to false (the default), an exception is thrown + * in case of a failure. null is returned otherwise. * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * 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. * - * @see Assert::assertThat + * @throws ExpectationFailedException */ - function assertThat($value, Constraint $constraint, string $message = '') : void + public function evaluate(mixed $other, string $description = '', bool $returnResult = \false): ?bool { - \PHPUnit\Framework\Assert::assertThat(...func_get_args()); + // 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, 0.0, \true); + } catch (ComparisonFailure $f) { + if ($returnResult) { + return \false; + } + throw new ExpectationFailedException(trim($description . "\n" . $f->getMessage()), $f); + } + return \true; } -} -if (!function_exists('PHPUnit\\Framework\\assertJson')) { /** - * Asserts that a string is a valid JSON string. - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertJson + * Returns a string representation of the constraint. */ - function assertJson(string $actualJson, string $message = '') : void + public function toString(bool $exportObjects = \false): string { - \PHPUnit\Framework\Assert::assertJson(...func_get_args()); + if (is_string($this->value)) { + if (str_contains($this->value, "\n")) { + return 'is equal to '; + } + return sprintf("is equal to '%s'", $this->value); + } + return sprintf('is equal to %s', Exporter::export($this->value, $exportObjects)); } } -if (!function_exists('PHPUnit\\Framework\\assertJsonStringEqualsJsonString')) { - /** - * Asserts that two given JSON encoded objects or arrays are equal. - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertJsonStringEqualsJsonString - */ - function assertJsonStringEqualsJsonString(string $expectedJson, string $actualJson, string $message = '') : void + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Constraint; + +use function is_string; +use function sprintf; +use function str_contains; +use function trim; +use PHPUnit\Framework\ExpectationFailedException; +use PHPUnit\Util\Exporter; +use PHPUnitPHAR\SebastianBergmann\Comparator\ComparisonFailure; +use PHPUnitPHAR\SebastianBergmann\Comparator\Factory as ComparatorFactory; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class IsEqualIgnoringCase extends \PHPUnit\Framework\Constraint\Constraint +{ + private readonly mixed $value; + public function __construct(mixed $value) { - \PHPUnit\Framework\Assert::assertJsonStringEqualsJsonString(...func_get_args()); + $this->value = $value; } -} -if (!function_exists('PHPUnit\\Framework\\assertJsonStringNotEqualsJsonString')) { /** - * Asserts that two given JSON encoded objects or arrays are not equal. - * - * @param string $expectedJson - * @param string $actualJson + * Evaluates the constraint for parameter $other. * - * @throws ExpectationFailedException - * @throws InvalidArgumentException + * If $returnResult is set to false (the default), an exception is thrown + * in case of a failure. null is returned otherwise. * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * 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. * - * @see Assert::assertJsonStringNotEqualsJsonString + * @throws ExpectationFailedException */ - function assertJsonStringNotEqualsJsonString($expectedJson, $actualJson, string $message = '') : void + public function evaluate(mixed $other, string $description = '', bool $returnResult = \false): ?bool { - \PHPUnit\Framework\Assert::assertJsonStringNotEqualsJsonString(...func_get_args()); - } -} -if (!function_exists('PHPUnit\\Framework\\assertJsonStringEqualsJsonFile')) { - /** - * Asserts that the generated JSON encoded object and the content of the given file are equal. - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertJsonStringEqualsJsonFile - */ - function assertJsonStringEqualsJsonFile(string $expectedFile, string $actualJson, string $message = '') : void - { - \PHPUnit\Framework\Assert::assertJsonStringEqualsJsonFile(...func_get_args()); + // 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, 0.0, \false, \true); + } catch (ComparisonFailure $f) { + if ($returnResult) { + return \false; + } + throw new ExpectationFailedException(trim($description . "\n" . $f->getMessage()), $f); + } + return \true; } -} -if (!function_exists('PHPUnit\\Framework\\assertJsonStringNotEqualsJsonFile')) { /** - * Asserts that the generated JSON encoded object and the content of the given file are not equal. - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertJsonStringNotEqualsJsonFile + * Returns a string representation of the constraint. */ - function assertJsonStringNotEqualsJsonFile(string $expectedFile, string $actualJson, string $message = '') : void + public function toString(bool $exportObjects = \false): string { - \PHPUnit\Framework\Assert::assertJsonStringNotEqualsJsonFile(...func_get_args()); + if (is_string($this->value)) { + if (str_contains($this->value, "\n")) { + return 'is equal to '; + } + return sprintf("is equal to '%s'", $this->value); + } + return sprintf('is equal to %s', Exporter::export($this->value, $exportObjects)); } } -if (!function_exists('PHPUnit\\Framework\\assertJsonFileEqualsJsonFile')) { - /** - * Asserts that two JSON files are equal. - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertJsonFileEqualsJsonFile - */ - function assertJsonFileEqualsJsonFile(string $expectedFile, string $actualFile, string $message = '') : void + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Constraint; + +use function sprintf; +use function trim; +use PHPUnit\Framework\ExpectationFailedException; +use PHPUnit\Util\Exporter; +use PHPUnitPHAR\SebastianBergmann\Comparator\ComparisonFailure; +use PHPUnitPHAR\SebastianBergmann\Comparator\Factory as ComparatorFactory; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class IsEqualWithDelta extends \PHPUnit\Framework\Constraint\Constraint +{ + private readonly mixed $value; + private readonly float $delta; + public function __construct(mixed $value, float $delta) { - \PHPUnit\Framework\Assert::assertJsonFileEqualsJsonFile(...func_get_args()); + $this->value = $value; + $this->delta = $delta; } -} -if (!function_exists('PHPUnit\\Framework\\assertJsonFileNotEqualsJsonFile')) { /** - * Asserts that two JSON files are not equal. + * Evaluates the constraint for parameter $other. * - * @throws ExpectationFailedException - * @throws InvalidArgumentException + * If $returnResult is set to false (the default), an exception is thrown + * in case of a failure. null is returned otherwise. * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * 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. * - * @see Assert::assertJsonFileNotEqualsJsonFile + * @throws ExpectationFailedException */ - function assertJsonFileNotEqualsJsonFile(string $expectedFile, string $actualFile, string $message = '') : void - { - \PHPUnit\Framework\Assert::assertJsonFileNotEqualsJsonFile(...func_get_args()); - } -} -if (!function_exists('PHPUnit\\Framework\\logicalAnd')) { - function logicalAnd() : LogicalAnd - { - return \PHPUnit\Framework\Assert::logicalAnd(...func_get_args()); - } -} -if (!function_exists('PHPUnit\\Framework\\logicalOr')) { - function logicalOr() : LogicalOr - { - return \PHPUnit\Framework\Assert::logicalOr(...func_get_args()); - } -} -if (!function_exists('PHPUnit\\Framework\\logicalNot')) { - function logicalNot(Constraint $constraint) : LogicalNot - { - return \PHPUnit\Framework\Assert::logicalNot(...func_get_args()); - } -} -if (!function_exists('PHPUnit\\Framework\\logicalXor')) { - function logicalXor() : LogicalXor - { - return \PHPUnit\Framework\Assert::logicalXor(...func_get_args()); - } -} -if (!function_exists('PHPUnit\\Framework\\anything')) { - function anything() : IsAnything - { - return \PHPUnit\Framework\Assert::anything(...func_get_args()); - } -} -if (!function_exists('PHPUnit\\Framework\\isTrue')) { - function isTrue() : IsTrue - { - return \PHPUnit\Framework\Assert::isTrue(...func_get_args()); - } -} -if (!function_exists('PHPUnit\\Framework\\callback')) { - function callback(callable $callback) : Callback - { - return \PHPUnit\Framework\Assert::callback(...func_get_args()); - } -} -if (!function_exists('PHPUnit\\Framework\\isFalse')) { - function isFalse() : IsFalse - { - return \PHPUnit\Framework\Assert::isFalse(...func_get_args()); - } -} -if (!function_exists('PHPUnit\\Framework\\isJson')) { - function isJson() : IsJson - { - return \PHPUnit\Framework\Assert::isJson(...func_get_args()); - } -} -if (!function_exists('PHPUnit\\Framework\\isNull')) { - function isNull() : IsNull - { - return \PHPUnit\Framework\Assert::isNull(...func_get_args()); - } -} -if (!function_exists('PHPUnit\\Framework\\isFinite')) { - function isFinite() : IsFinite - { - return \PHPUnit\Framework\Assert::isFinite(...func_get_args()); - } -} -if (!function_exists('PHPUnit\\Framework\\isInfinite')) { - function isInfinite() : IsInfinite - { - return \PHPUnit\Framework\Assert::isInfinite(...func_get_args()); - } -} -if (!function_exists('PHPUnit\\Framework\\isNan')) { - function isNan() : IsNan - { - return \PHPUnit\Framework\Assert::isNan(...func_get_args()); - } -} -if (!function_exists('PHPUnit\\Framework\\containsEqual')) { - function containsEqual($value) : TraversableContainsEqual - { - return \PHPUnit\Framework\Assert::containsEqual(...func_get_args()); - } -} -if (!function_exists('PHPUnit\\Framework\\containsIdentical')) { - function containsIdentical($value) : TraversableContainsIdentical - { - return \PHPUnit\Framework\Assert::containsIdentical(...func_get_args()); - } -} -if (!function_exists('PHPUnit\\Framework\\containsOnly')) { - function containsOnly(string $type) : TraversableContainsOnly - { - return \PHPUnit\Framework\Assert::containsOnly(...func_get_args()); - } -} -if (!function_exists('PHPUnit\\Framework\\containsOnlyInstancesOf')) { - function containsOnlyInstancesOf(string $className) : TraversableContainsOnly - { - return \PHPUnit\Framework\Assert::containsOnlyInstancesOf(...func_get_args()); - } -} -if (!function_exists('PHPUnit\\Framework\\arrayHasKey')) { - function arrayHasKey($key) : ArrayHasKey - { - return \PHPUnit\Framework\Assert::arrayHasKey(...func_get_args()); - } -} -if (!function_exists('PHPUnit\\Framework\\equalTo')) { - function equalTo($value) : IsEqual - { - return \PHPUnit\Framework\Assert::equalTo(...func_get_args()); - } -} -if (!function_exists('PHPUnit\\Framework\\equalToCanonicalizing')) { - function equalToCanonicalizing($value) : IsEqualCanonicalizing - { - return \PHPUnit\Framework\Assert::equalToCanonicalizing(...func_get_args()); - } -} -if (!function_exists('PHPUnit\\Framework\\equalToIgnoringCase')) { - function equalToIgnoringCase($value) : IsEqualIgnoringCase - { - return \PHPUnit\Framework\Assert::equalToIgnoringCase(...func_get_args()); - } -} -if (!function_exists('PHPUnit\\Framework\\equalToWithDelta')) { - function equalToWithDelta($value, float $delta) : IsEqualWithDelta - { - return \PHPUnit\Framework\Assert::equalToWithDelta(...func_get_args()); - } -} -if (!function_exists('PHPUnit\\Framework\\isEmpty')) { - function isEmpty() : IsEmpty - { - return \PHPUnit\Framework\Assert::isEmpty(...func_get_args()); - } -} -if (!function_exists('PHPUnit\\Framework\\isWritable')) { - function isWritable() : IsWritable - { - return \PHPUnit\Framework\Assert::isWritable(...func_get_args()); - } -} -if (!function_exists('PHPUnit\\Framework\\isReadable')) { - function isReadable() : IsReadable - { - return \PHPUnit\Framework\Assert::isReadable(...func_get_args()); - } -} -if (!function_exists('PHPUnit\\Framework\\directoryExists')) { - function directoryExists() : DirectoryExists - { - return \PHPUnit\Framework\Assert::directoryExists(...func_get_args()); - } -} -if (!function_exists('PHPUnit\\Framework\\fileExists')) { - function fileExists() : FileExists - { - return \PHPUnit\Framework\Assert::fileExists(...func_get_args()); - } -} -if (!function_exists('PHPUnit\\Framework\\greaterThan')) { - function greaterThan($value) : GreaterThan - { - return \PHPUnit\Framework\Assert::greaterThan(...func_get_args()); - } -} -if (!function_exists('PHPUnit\\Framework\\greaterThanOrEqual')) { - function greaterThanOrEqual($value) : LogicalOr - { - return \PHPUnit\Framework\Assert::greaterThanOrEqual(...func_get_args()); - } -} -if (!function_exists('PHPUnit\\Framework\\classHasAttribute')) { - function classHasAttribute(string $attributeName) : ClassHasAttribute - { - return \PHPUnit\Framework\Assert::classHasAttribute(...func_get_args()); - } -} -if (!function_exists('PHPUnit\\Framework\\classHasStaticAttribute')) { - function classHasStaticAttribute(string $attributeName) : ClassHasStaticAttribute - { - return \PHPUnit\Framework\Assert::classHasStaticAttribute(...func_get_args()); - } -} -if (!function_exists('PHPUnit\\Framework\\objectHasAttribute')) { - function objectHasAttribute($attributeName) : ObjectHasAttribute - { - return \PHPUnit\Framework\Assert::objectHasAttribute(...func_get_args()); - } -} -if (!function_exists('PHPUnit\\Framework\\identicalTo')) { - function identicalTo($value) : IsIdentical - { - return \PHPUnit\Framework\Assert::identicalTo(...func_get_args()); - } -} -if (!function_exists('PHPUnit\\Framework\\isInstanceOf')) { - function isInstanceOf(string $className) : IsInstanceOf - { - return \PHPUnit\Framework\Assert::isInstanceOf(...func_get_args()); - } -} -if (!function_exists('PHPUnit\\Framework\\isType')) { - function isType(string $type) : IsType - { - return \PHPUnit\Framework\Assert::isType(...func_get_args()); - } -} -if (!function_exists('PHPUnit\\Framework\\lessThan')) { - function lessThan($value) : LessThan - { - return \PHPUnit\Framework\Assert::lessThan(...func_get_args()); - } -} -if (!function_exists('PHPUnit\\Framework\\lessThanOrEqual')) { - function lessThanOrEqual($value) : LogicalOr - { - return \PHPUnit\Framework\Assert::lessThanOrEqual(...func_get_args()); - } -} -if (!function_exists('PHPUnit\\Framework\\matchesRegularExpression')) { - function matchesRegularExpression(string $pattern) : RegularExpression - { - return \PHPUnit\Framework\Assert::matchesRegularExpression(...func_get_args()); - } -} -if (!function_exists('PHPUnit\\Framework\\matches')) { - function matches(string $string) : StringMatchesFormatDescription - { - return \PHPUnit\Framework\Assert::matches(...func_get_args()); - } -} -if (!function_exists('PHPUnit\\Framework\\stringStartsWith')) { - function stringStartsWith($prefix) : StringStartsWith - { - return \PHPUnit\Framework\Assert::stringStartsWith(...func_get_args()); - } -} -if (!function_exists('PHPUnit\\Framework\\stringContains')) { - function stringContains(string $string, bool $case = \true) : StringContains - { - return \PHPUnit\Framework\Assert::stringContains(...func_get_args()); - } -} -if (!function_exists('PHPUnit\\Framework\\stringEndsWith')) { - function stringEndsWith(string $suffix) : StringEndsWith + public function evaluate(mixed $other, string $description = '', bool $returnResult = \false): ?bool { - return \PHPUnit\Framework\Assert::stringEndsWith(...func_get_args()); - } -} -if (!function_exists('PHPUnit\\Framework\\countOf')) { - function countOf(int $count) : Count - { - return \PHPUnit\Framework\Assert::countOf(...func_get_args()); - } -} -if (!function_exists('PHPUnit\\Framework\\objectEquals')) { - function objectEquals(object $object, string $method = 'equals') : ObjectEquals - { - return \PHPUnit\Framework\Assert::objectEquals(...func_get_args()); + // 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); + } catch (ComparisonFailure $f) { + if ($returnResult) { + return \false; + } + throw new ExpectationFailedException(trim($description . "\n" . $f->getMessage()), $f); + } + return \true; } -} -if (!function_exists('PHPUnit\\Framework\\any')) { /** - * Returns a matcher that matches when the method is executed - * zero or more times. + * Returns a string representation of the constraint. */ - function any() : AnyInvokedCountMatcher + public function toString(bool $exportObjects = \false): string { - return new AnyInvokedCountMatcher(); + return sprintf('is equal to %s with delta <%F>', Exporter::export($this->value, $exportObjects), $this->delta); } } -if (!function_exists('PHPUnit\\Framework\\never')) { - /** - * Returns a matcher that matches when the method is never executed. - */ - function never() : InvokedCountMatcher + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Constraint; + +use function sprintf; +use PHPUnit\Util\Filter; +use Throwable; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class Exception extends \PHPUnit\Framework\Constraint\Constraint +{ + private readonly string $className; + public function __construct(string $className) { - return new InvokedCountMatcher(0); + $this->className = $className; } -} -if (!function_exists('PHPUnit\\Framework\\atLeast')) { /** - * Returns a matcher that matches when the method is executed - * at least N times. + * Returns a string representation of the constraint. */ - function atLeast(int $requiredInvocations) : InvokedAtLeastCountMatcher + public function toString(): string { - return new InvokedAtLeastCountMatcher($requiredInvocations); + return sprintf('exception of type "%s"', $this->className); } -} -if (!function_exists('PHPUnit\\Framework\\atLeastOnce')) { /** - * Returns a matcher that matches when the method is executed at least once. + * Evaluates the constraint for parameter $other. Returns true if the + * constraint is met, false otherwise. */ - function atLeastOnce() : InvokedAtLeastOnceMatcher + protected function matches(mixed $other): bool { - return new InvokedAtLeastOnceMatcher(); + return $other instanceof $this->className; } -} -if (!function_exists('PHPUnit\\Framework\\once')) { /** - * Returns a matcher that matches when the method is executed exactly once. + * 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. + * + * @throws \PHPUnit\Framework\Exception */ - function once() : InvokedCountMatcher + protected function failureDescription(mixed $other): string { - return new InvokedCountMatcher(1); + if ($other === null) { + return sprintf('exception of type "%s" is thrown', $this->className); + } + $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', $other::class, $this->className, $message); } } -if (!function_exists('PHPUnit\\Framework\\exactly')) { - /** - * Returns a matcher that matches when the method is executed - * exactly $count times. - */ - function exactly(int $count) : InvokedCountMatcher + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Constraint; + +use function sprintf; +use PHPUnit\Util\Exporter; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class ExceptionCode extends \PHPUnit\Framework\Constraint\Constraint +{ + private readonly int|string $expectedCode; + public function __construct(int|string $expected) { - return new InvokedCountMatcher($count); + $this->expectedCode = $expected; } -} -if (!function_exists('PHPUnit\\Framework\\atMost')) { - /** - * Returns a matcher that matches when the method is executed - * at most N times. - */ - function atMost(int $allowedInvocations) : InvokedAtMostCountMatcher + public function toString(): string { - return new InvokedAtMostCountMatcher($allowedInvocations); + return 'exception code is ' . $this->expectedCode; } -} -if (!function_exists('PHPUnit\\Framework\\at')) { /** - * Returns a matcher that matches when the method is executed - * at the given index. + * Evaluates the constraint for parameter $other. Returns true if the + * constraint is met, false otherwise. */ - function at(int $index) : InvokedAtIndexMatcher + protected function matches(mixed $other): bool { - return new InvokedAtIndexMatcher($index); + return (string) $other === (string) $this->expectedCode; } -} -if (!function_exists('PHPUnit\\Framework\\returnValue')) { - function returnValue($value) : ReturnStub + /** + * 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. + */ + protected function failureDescription(mixed $other): string { - return new ReturnStub($value); + return sprintf('%s is equal to expected exception code %s', Exporter::export($other, \true), Exporter::export($this->expectedCode, \true)); } } -if (!function_exists('PHPUnit\\Framework\\returnValueMap')) { - function returnValueMap(array $valueMap) : ReturnValueMapStub + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Constraint; + +use function sprintf; +use function str_contains; +use PHPUnit\Util\Exporter; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class ExceptionMessageIsOrContains extends \PHPUnit\Framework\Constraint\Constraint +{ + private readonly string $expectedMessage; + public function __construct(string $expectedMessage) { - return new ReturnValueMapStub($valueMap); + $this->expectedMessage = $expectedMessage; } -} -if (!function_exists('PHPUnit\\Framework\\returnArgument')) { - function returnArgument(int $argumentIndex) : ReturnArgumentStub + public function toString(): string { - return new ReturnArgumentStub($argumentIndex); + if ($this->expectedMessage === '') { + return 'exception message is empty'; + } + return 'exception message contains ' . Exporter::export($this->expectedMessage); } -} -if (!function_exists('PHPUnit\\Framework\\returnCallback')) { - function returnCallback($callback) : ReturnCallbackStub + protected function matches(mixed $other): bool { - return new ReturnCallbackStub($callback); + if ($this->expectedMessage === '') { + return $other === ''; + } + return str_contains((string) $other, $this->expectedMessage); } -} -if (!function_exists('PHPUnit\\Framework\\returnSelf')) { /** - * Returns the current object. + * Returns the description of the failure. * - * This method is useful when mocking a fluent interface. + * The beginning of failure messages is "Failed asserting that" in most + * cases. This method should return the second part of that sentence. */ - function returnSelf() : ReturnSelfStub - { - return new ReturnSelfStub(); - } -} -if (!function_exists('PHPUnit\\Framework\\throwException')) { - function throwException(Throwable $exception) : ExceptionStub - { - return new ExceptionStub($exception); - } -} -if (!function_exists('PHPUnit\\Framework\\onConsecutiveCalls')) { - function onConsecutiveCalls() : ConsecutiveCallsStub + protected function failureDescription(mixed $other): string { - $args = func_get_args(); - return new ConsecutiveCallsStub($args); + if ($this->expectedMessage === '') { + return sprintf("exception message is empty but is '%s'", $other); + } + return sprintf("exception message '%s' contains '%s'", $other, $this->expectedMessage); } } regularExpression = $regularExpression; + } + public function toString(): string + { + return 'exception message matches ' . Exporter::export($this->regularExpression); } /** * Evaluates the constraint for parameter $other. Returns true if the * constraint is met, false otherwise. * - * @param mixed $other value or object to evaluate + * @throws \PHPUnit\Framework\Exception + * @throws Exception */ - protected function matches($other) : bool + protected function matches(mixed $other): bool { - return $other === \false; + $match = @preg_match($this->regularExpression, (string) $other); + if ($match === \false) { + throw new \PHPUnit\Framework\Exception(sprintf('Invalid expected exception message regular expression given: %s', $this->regularExpression)); + } + 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. + */ + protected function failureDescription(mixed $other): string + { + return sprintf("exception message '%s' matches '%s'", $other, $this->regularExpression); } } callback = $callback; + return 'file exists'; } /** - * Returns a string representation of the constraint. + * Evaluates the constraint for parameter $other. Returns true if the + * constraint is met, false otherwise. */ - public function toString() : string + protected function matches(mixed $other): bool { - return 'is accepted by specified callback'; + return file_exists($other); } /** - * Evaluates the constraint for parameter $value. Returns true if the - * constraint is met, false otherwise. - * - * @param mixed $other value or object to evaluate + * Returns the description of the failure. * - * @psalm-param CallbackInput $other + * The beginning of failure messages is "Failed asserting that" in most + * cases. This method should return the second part of that sentence. */ - protected function matches($other) : bool + protected function failureDescription(mixed $other): string { - return ($this->callback)($other); + return sprintf('file "%s" exists', $other); } } expectedCount = $expected; - } - public function toString() : string + public function toString(): string { - return sprintf('count matches %d', $this->expectedCount); + return 'is readable'; } /** * Evaluates the constraint for parameter $other. Returns true if the * constraint is met, false otherwise. - * - * @throws Exception - */ - protected function matches($other) : bool - { - return $this->expectedCount === $this->getCountOf($other); - } - /** - * @throws Exception - */ - 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) { - try { - $other = $other->getIterator(); - } catch (\Exception $e) { - throw new Exception($e->getMessage(), $e->getCode(), $e); - } - } - $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 + protected function matches(mixed $other): bool { - for ($count = 0; $generator->valid(); $generator->next()) { - $count++; - } - return $count; + 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('actual size %d matches expected size %d', (int) $this->getCountOf($other), $this->expectedCount); - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -use PHPUnitPHAR\SebastianBergmann\RecursionContext\InvalidArgumentException; -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -final class GreaterThan extends \PHPUnit\Framework\Constraint\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 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 + protected function failureDescription(mixed $other): string { - return $this->value < $other; + return sprintf('"%s" is readable', $other); } } toString()); + return sprintf('"%s" is writable', $other); } } value = $value; + return $returnResult ? \true : null; } /** * Returns a string representation of the constraint. - * - * @throws InvalidArgumentException */ - public function toString() : string + public function toString(): string { - return 'is less than ' . $this->exporter()->export($this->value); + return 'is anything'; } /** - * Evaluates the constraint for parameter $other. Returns true if the - * constraint is met, false otherwise. - * - * @param mixed $other value or object to evaluate + * Counts the number of constraint elements. */ - protected function matches($other) : bool - { - return $this->value > $other; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -final class SameSize extends \PHPUnit\Framework\Constraint\Count -{ - public function __construct(iterable $expected) + public function count(): int { - parent::__construct((int) $this->getCountOf($expected)); + return 0; } } value = $value; + } /** * Evaluates the constraint for parameter $other. * @@ -62251,208 +54386,58 @@ abstract class Constraint implements Countable, SelfDescribing * failure. * * @throws ExpectationFailedException - * @throws InvalidArgumentException */ - public function evaluate($other, string $description = '', bool $returnResult = \false) : ?bool + public function evaluate(mixed $other, string $description = '', bool $returnResult = \false): ?bool { - $success = \false; - if ($this->matches($other)) { - $success = \true; - } + $success = $this->value === $other; if ($returnResult) { return $success; } if (!$success) { - $this->fail($other, $description); + $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 or enums, make sure a diff is generated + if (is_array($this->value) && is_array($other) || $this->value instanceof UnitEnum && $other instanceof UnitEnum) { + $f = new ComparisonFailure($this->value, $other, Exporter::export($this->value, \true), Exporter::export($other, \true)); + } + $this->fail($other, $description, $f); } return null; } /** - * 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 - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @psalm-return never-return + * Returns a string representation of the constraint. */ - protected function fail($other, $description, ?ComparisonFailure $comparisonFailure = null) : void + public function toString(bool $exportObjects = \false): string { - $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; + if (is_object($this->value)) { + return 'is identical to an object of class "' . $this->value::class . '"'; } - 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 ''; + return 'is identical to ' . Exporter::export($this->value, $exportObjects); } /** * 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 InvalidArgumentException - */ - protected function failureDescription($other) : string - { - return $this->exporter()->export($other) . ' ' . $this->toString(); - } - /** - * Returns a custom string representation of the constraint object when it - * appears in context of an $operator expression. - * - * The purpose of this method is to provide meaningful descriptive string - * in context of operators such as LogicalNot. Native PHPUnit constraints - * are supported out of the box by LogicalNot, but externally developed - * ones had no way to provide correct strings in this context. - * - * The method shall return empty string, when it does not handle - * customization by itself. - * - * @param Operator $operator the $operator of the expression - * @param mixed $role role of $this constraint in the $operator expression - */ - protected function toStringInContext(\PHPUnit\Framework\Constraint\Operator $operator, $role) : string - { - return ''; - } - /** - * Returns the description of the failure when this constraint appears in - * context of an $operator expression. - * - * The purpose of this method is to provide meaningful failure description - * in context of operators such as LogicalNot. Native PHPUnit constraints - * are supported out of the box by LogicalNot, but externally developed - * ones had no way to provide correct messages in this context. - * - * The method shall return empty string, when it does not handle - * customization by itself. - * - * @param Operator $operator the $operator of the expression - * @param mixed $role role of $this constraint in the $operator expression - * @param mixed $other evaluated value or object */ - protected function failureDescriptionInContext(\PHPUnit\Framework\Constraint\Operator $operator, $role, $other) : string + protected function failureDescription(mixed $other): string { - $string = $this->toStringInContext($operator, $role); - if ($string === '') { - return ''; + if (is_object($this->value) && is_object($other)) { + return 'two variables reference the same object'; } - return $this->exporter()->export($other) . ' ' . $string; - } - /** - * Reduces the sub-expression starting at $this by skipping degenerate - * sub-expression and returns first descendant constraint that starts - * a non-reducible sub-expression. - * - * Returns $this for terminal constraints and for operators that start - * non-reducible sub-expression, or the nearest descendant of $this that - * starts a non-reducible sub-expression. - * - * A constraint expression may be modelled as a tree with non-terminal - * nodes (operators) and terminal nodes. For example: - * - * LogicalOr (operator, non-terminal) - * + LogicalAnd (operator, non-terminal) - * | + IsType('int') (terminal) - * | + GreaterThan(10) (terminal) - * + LogicalNot (operator, non-terminal) - * + IsType('array') (terminal) - * - * A degenerate sub-expression is a part of the tree, that effectively does - * not contribute to the evaluation of the expression it appears in. An example - * of degenerate sub-expression is a BinaryOperator constructed with single - * operand or nested BinaryOperators, each with single operand. An - * expression involving a degenerate sub-expression is equivalent to a - * reduced expression with the degenerate sub-expression removed, for example - * - * LogicalAnd (operator) - * + LogicalOr (degenerate operator) - * | + LogicalAnd (degenerate operator) - * | + IsType('int') (terminal) - * + GreaterThan(10) (terminal) - * - * is equivalent to - * - * LogicalAnd (operator) - * + IsType('int') (terminal) - * + GreaterThan(10) (terminal) - * - * because the subexpression - * - * + LogicalOr - * + LogicalAnd - * + - - * - * is degenerate. Calling reduce() on the LogicalOr object above, as well - * as on LogicalAnd, shall return the IsType('int') instance. - * - * Other specific reductions can be implemented, for example cascade of - * LogicalNot operators - * - * + LogicalNot - * + LogicalNot - * +LogicalNot - * + IsTrue - * - * can be reduced to - * - * LogicalNot - * + IsTrue - */ - protected function reduce() : self - { - return $this; + if (explode(' ', gettype($this->value), 2)[0] === 'resource' && explode(' ', gettype($other), 2)[0] === 'resource') { + return 'two variables reference the same resource'; + } + 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); } } value = $value; + } /** - * @var mixed - */ - private $value; - /** - * @var float - */ - private $delta; - /** - * @var bool - */ - private $canonicalize; - /** - * @var bool + * Returns a string representation of the object. */ - private $ignoreCase; - public function __construct($value, float $delta = 0.0, bool $canonicalize = \false, bool $ignoreCase = \false) + public function toString(): string { - $this->value = $value; - $this->delta = $delta; - $this->canonicalize = $canonicalize; - $this->ignoreCase = $ignoreCase; + return sprintf('matches JSON string "%s"', $this->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. + * Evaluates the constraint for parameter $other. Returns true if the + * constraint is met, false otherwise. * - * @throws ExpectationFailedException + * This method can be overridden to implement the evaluation algorithm. */ - public function evaluate($other, string $description = '', bool $returnResult = \false) : ?bool + protected function matches(mixed $other): bool { - // 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; + [$error, $recodedOther] = Json::canonicalize($other); + if ($error) { + return \false; } - $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); + [$error, $recodedValue] = Json::canonicalize($this->value); + if ($error) { + return \false; } - return \true; + return $recodedOther == $recodedValue; } /** - * Returns a string representation of the constraint. + * Throws an exception for the given compared value and test description. * - * @throws InvalidArgumentException + * @throws ExpectationFailedException + * @throws InvalidJsonException */ - public function toString() : string + protected function fail(mixed $other, string $description, ?ComparisonFailure $comparisonFailure = null): never { - $delta = ''; - if (is_string($this->value)) { - if (strpos($this->value, "\n") !== \false) { - return 'is equal to '; + if ($comparisonFailure === null) { + [$error, $recodedOther] = Json::canonicalize($other); + if ($error) { + parent::fail($other, $description); } - return sprintf("is equal to '%s'", $this->value); - } - if ($this->delta != 0) { - $delta = sprintf(' with delta <%F>', $this->delta); + [$error, $recodedValue] = Json::canonicalize($this->value); + if ($error) { + parent::fail($other, $description); + } + $comparisonFailure = new ComparisonFailure(json_decode($this->value), json_decode($other), Json::prettify($recodedValue), Json::prettify($recodedOther), 'Failed asserting that two json values are equal.'); } - return sprintf('is equal to %s%s', $this->exporter()->export($this->value), $delta); + parent::fail($other, $description, $comparisonFailure); } } 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 + * Returns a string representation of the constraint. */ - public function evaluate($other, string $description = '', bool $returnResult = \false) : ?bool + public function toString(): string { - // 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, 0.0, \true, \false); - } catch (ComparisonFailure $f) { - if ($returnResult) { - return \false; - } - throw new ExpectationFailedException(trim($description . "\n" . $f->getMessage()), $f); - } - return \true; + return 'is finite'; } /** - * Returns a string representation of the constraint. - * - * @throws InvalidArgumentException + * Evaluates the constraint for parameter $other. Returns true if the + * constraint is met, false otherwise. */ - public function toString() : string + protected function matches(mixed $other): bool { - if (is_string($this->value)) { - if (strpos($this->value, "\n") !== \false) { - return 'is equal to '; - } - return sprintf("is equal to '%s'", $this->value); - } - return sprintf('is equal to %s', $this->exporter()->export($this->value)); + return is_finite($other); } } 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 + * Returns a string representation of the constraint. */ - public function evaluate($other, string $description = '', bool $returnResult = \false) : ?bool + public function toString(): string { - // 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, 0.0, \false, \true); - } catch (ComparisonFailure $f) { - if ($returnResult) { - return \false; - } - throw new ExpectationFailedException(trim($description . "\n" . $f->getMessage()), $f); - } - return \true; + return 'is infinite'; } /** - * Returns a string representation of the constraint. - * - * @throws InvalidArgumentException + * Evaluates the constraint for parameter $other. Returns true if the + * constraint is met, false otherwise. */ - public function toString() : string + protected function matches(mixed $other): bool { - if (is_string($this->value)) { - if (strpos($this->value, "\n") !== \false) { - return 'is equal to '; - } - return sprintf("is equal to '%s'", $this->value); - } - return sprintf('is equal to %s', $this->exporter()->export($this->value)); + return is_infinite($other); } } value = $value; - $this->delta = $delta; - } - /** - * 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 + * Returns a string representation of the constraint. */ - public function evaluate($other, string $description = '', bool $returnResult = \false) : ?bool + public function toString(): string { - // 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); - } catch (ComparisonFailure $f) { - if ($returnResult) { - return \false; - } - throw new ExpectationFailedException(trim($description . "\n" . $f->getMessage()), $f); - } - return \true; + return 'is nan'; } /** - * Returns a string representation of the constraint. - * - * @throws InvalidArgumentException + * Evaluates the constraint for parameter $other. Returns true if the + * constraint is met, false otherwise. */ - public function toString() : string + protected function matches(mixed $other): bool { - return sprintf('is equal to %s with delta <%F>', $this->exporter()->export($this->value), $this->delta); + return is_nan($other); } } className = $className; + $this->expected = $object; + $this->method = $method; } - /** - * Returns a string representation of the constraint. - */ - public function toString() : string + public function toString(): string { - return sprintf('exception of type "%s"', $this->className); + return 'two objects are equal'; } /** - * Evaluates the constraint for parameter $other. Returns true if the - * constraint is met, false otherwise. - * - * @param mixed $other value or object to evaluate + * @throws ActualValueIsNotAnObjectException + * @throws ComparisonMethodDoesNotAcceptParameterTypeException + * @throws ComparisonMethodDoesNotDeclareBoolReturnTypeException + * @throws ComparisonMethodDoesNotDeclareExactlyOneParameterException + * @throws ComparisonMethodDoesNotDeclareParameterTypeException + * @throws ComparisonMethodDoesNotExistException */ - protected function matches($other) : bool + protected function matches(mixed $other): bool { - return $other instanceof $this->className; + if (!is_object($other)) { + throw new ActualValueIsNotAnObjectException(); + } + $object = new ReflectionObject($other); + if (!$object->hasMethod($this->method)) { + throw new ComparisonMethodDoesNotExistException($other::class, $this->method); + } + $method = $object->getMethod($this->method); + if (!$method->hasReturnType()) { + throw new ComparisonMethodDoesNotDeclareBoolReturnTypeException($other::class, $this->method); + } + $returnType = $method->getReturnType(); + if (!$returnType instanceof ReflectionNamedType) { + throw new ComparisonMethodDoesNotDeclareBoolReturnTypeException($other::class, $this->method); + } + if ($returnType->allowsNull()) { + throw new ComparisonMethodDoesNotDeclareBoolReturnTypeException($other::class, $this->method); + } + if ($returnType->getName() !== 'bool') { + throw new ComparisonMethodDoesNotDeclareBoolReturnTypeException($other::class, $this->method); + } + if ($method->getNumberOfParameters() !== 1 || $method->getNumberOfRequiredParameters() !== 1) { + throw new ComparisonMethodDoesNotDeclareExactlyOneParameterException($other::class, $this->method); + } + $parameter = $method->getParameters()[0]; + if (!$parameter->hasType()) { + throw new ComparisonMethodDoesNotDeclareParameterTypeException($other::class, $this->method); + } + $type = $parameter->getType(); + if (!$type instanceof ReflectionNamedType) { + throw new ComparisonMethodDoesNotDeclareParameterTypeException($other::class, $this->method); + } + $typeName = $type->getName(); + if ($typeName === 'self') { + $typeName = $other::class; + } + if (!$this->expected instanceof $typeName) { + throw new ComparisonMethodDoesNotAcceptParameterTypeException($other::class, $this->method, $this->expected::class); + } + return $other->{$this->method}($this->expected); } - /** - * 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 + protected function failureDescription(mixed $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); + return $this->toString(\true); } } expectedCode = $expected; + $this->propertyName = $propertyName; } - public function toString() : string + /** + * Returns a string representation of the constraint. + */ + public function toString(): string { - return 'exception code is '; + return sprintf('has property "%s"', $this->propertyName); } /** * Evaluates the constraint for parameter $other. Returns true if the * constraint is met, false otherwise. * - * @param Throwable $other + * @param mixed $other value or object to evaluate */ - protected function matches($other) : bool + protected function matches(mixed $other): bool { - return (string) $other->getCode() === (string) $this->expectedCode; + if (!is_object($other)) { + return \false; + } + return (new ReflectionObject($other))->hasProperty($this->propertyName); } /** * Returns the description of the failure. @@ -62919,12 +54769,13 @@ final class ExceptionCode extends \PHPUnit\Framework\Constraint\Constraint * cases. This method should return the second part of that sentence. * * @param mixed $other evaluated value or object - * - * @throws InvalidArgumentException */ - protected function failureDescription($other) : string + protected function failureDescription(mixed $other): string { - return sprintf('%s is equal to expected exception code %s', $this->exporter()->export($other->getCode()), $this->exporter()->export($this->expectedCode)); + if (is_object($other)) { + return sprintf('object of class "%s" %s', $other::class, $this->toString(\true)); + } + return sprintf('"%s" (%s) %s', $other, gettype($other), $this->toString(\true)); } } */ - private $expectedMessage; - public function __construct(string $expected) + private readonly array $constraints; + protected function __construct(mixed ...$constraints) { - $this->expectedMessage = $expected; + $this->constraints = array_map(fn($constraint): \PHPUnit\Framework\Constraint\Constraint => $this->checkConstraint($constraint), $constraints); } - public function toString() : string + /** + * Returns the number of operands (constraints). + */ + final public function arity(): int { - if ($this->expectedMessage === '') { - return 'exception message is empty'; - } - return 'exception message contains '; + return count($this->constraints); } /** - * Evaluates the constraint for parameter $other. Returns true if the - * constraint is met, false otherwise. - * - * @param Throwable $other + * Returns a string representation of the constraint. */ - protected function matches($other) : bool + public function toString(): string { - if ($this->expectedMessage === '') { - return $other->getMessage() === ''; + $reduced = $this->reduce(); + if ($reduced !== $this) { + return $reduced->toString(); + } + $text = ''; + foreach ($this->constraints as $key => $constraint) { + $constraint = $constraint->reduce(); + $text .= $this->constraintToString($constraint, $key); } - return strpos((string) $other->getMessage(), $this->expectedMessage) !== \false; + return $text; } /** - * 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 + * Counts the number of constraint elements. */ - protected function failureDescription($other) : string + public function count(): int { - if ($this->expectedMessage === '') { - return sprintf("exception message is empty but is '%s'", $other->getMessage()); + $count = 0; + foreach ($this->constraints as $constraint) { + $count += count($constraint); } - return sprintf("exception message '%s' contains '%s'", $other->getMessage(), $this->expectedMessage); + return $count; } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -use function sprintf; -use Exception; -use PHPUnit\Util\RegularExpression as RegularExpressionUtil; -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -final class ExceptionMessageRegularExpression extends \PHPUnit\Framework\Constraint\Constraint -{ /** - * @var string + * @psalm-return list */ - private $expectedMessageRegExp; - public function __construct(string $expected) + final protected function constraints(): array { - $this->expectedMessageRegExp = $expected; + return $this->constraints; } - public function toString() : string + /** + * Returns true if the $constraint needs to be wrapped with braces. + */ + final protected function constraintNeedsParentheses(\PHPUnit\Framework\Constraint\Constraint $constraint): bool { - return 'exception message matches '; + return $this->arity() > 1 && parent::constraintNeedsParentheses($constraint); } /** - * Evaluates the constraint for parameter $other. Returns true if the - * constraint is met, false otherwise. - * - * @param \PHPUnit\Framework\Exception $other + * Reduces the sub-expression starting at $this by skipping degenerate + * sub-expression and returns first descendant constraint that starts + * a non-reducible sub-expression. * - * @throws \PHPUnit\Framework\Exception - * @throws Exception + * See Constraint::reduce() for more. */ - protected function matches($other) : bool + protected function reduce(): \PHPUnit\Framework\Constraint\Constraint { - $match = RegularExpressionUtil::safeMatch($this->expectedMessageRegExp, $other->getMessage()); - if ($match === \false) { - throw new \PHPUnit\Framework\Exception("Invalid expected exception message regex given: '{$this->expectedMessageRegExp}'"); + if ($this->arity() === 1 && $this->constraints[0] instanceof \PHPUnit\Framework\Constraint\Operator) { + return $this->constraints[0]->reduce(); } - return $match === 1; + return parent::reduce(); } /** - * 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 + * Returns string representation of given operand in context of this operator. */ - protected function failureDescription($other) : string + private function constraintToString(\PHPUnit\Framework\Constraint\Constraint $constraint, int $position): string { - return sprintf("exception message '%s' matches '%s'", $other->getMessage(), $this->expectedMessageRegExp); + $prefix = ''; + if ($position > 0) { + $prefix = ' ' . $this->operator() . ' '; + } + if ($this->constraintNeedsParentheses($constraint)) { + return $prefix . '( ' . $constraint->toString() . ' )'; + } + $string = $constraint->toStringInContext($this, $position); + if ($string === '') { + $string = $constraint->toString(); + } + return $prefix . $string; } } constraints() as $constraint) { + if (!$constraint->evaluate($other, '', \true)) { + return \false; + } + } + return [] !== $this->constraints(); } } '/\b' . preg_quote($s, '/') . '/', $positives); + if (count($matches) > 0) { + $nonInput = $matches[2]; + $negatedString = preg_replace('/' . preg_quote($nonInput, '/') . '/', preg_replace($positives, $negatives, $nonInput), $string); + } else { + $negatedString = preg_replace($positives, $negatives, $string); + } + return $negatedString; + } /** - * Returns a string representation of the constraint. + * Returns the name of this operator. */ - public function toString() : string + public function operator(): string { - return 'file exists'; + return 'not'; + } + /** + * Returns this operator's precedence. + * + * @see https://www.php.net/manual/en/language.operators.precedence.php + */ + public function precedence(): int + { + return 5; } /** * Evaluates the constraint for parameter $other. Returns true if the * constraint is met, false otherwise. * - * @param mixed $other value or object to evaluate + * @throws ExpectationFailedException */ - protected function matches($other) : bool + protected function matches(mixed $other): bool { - return file_exists($other); + return !$this->constraint()->evaluate($other, '', \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. + * Applies additional transformation to strings returned by toString() or + * failureDescription(). + */ + protected function transformString(string $string): string + { + return self::negate($string); + } + /** + * Reduces the sub-expression starting at $this by skipping degenerate + * sub-expression and returns first descendant constraint that starts + * a non-reducible sub-expression. * - * @param mixed $other evaluated value or object + * See Constraint::reduce() for more. */ - protected function failureDescription($other) : string + protected function reduce(): \PHPUnit\Framework\Constraint\Constraint { - return sprintf('file "%s" exists', $other); + $constraint = $this->constraint(); + if ($constraint instanceof self) { + return $constraint->constraint()->reduce(); + } + return parent::reduce(); } } constraints() as $constraint) { + if ($constraint->evaluate($other, '', \true)) { + return \true; + } + } + return \false; } } constraints(); + $initial = array_shift($constraints); + if ($initial === null) { + return \false; + } + return array_reduce($constraints, static fn(bool $matches, \PHPUnit\Framework\Constraint\Constraint $constraint): bool => $matches xor $constraint->evaluate($other, '', \true), $initial->evaluate($other, '', \true)); } } arity() > 1 && $this->precedence() <= $constraint->precedence(); } } constraint = $this->checkConstraint($constraint); + } /** - * @var mixed + * Returns the number of operands (constraints). */ - private $value; - public function __construct($value) + public function arity(): int { - $this->value = $value; + return 1; } /** - * 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 InvalidArgumentException + * Returns a string representation of the constraint. */ - public function evaluate($other, string $description = '', bool $returnResult = \false) : ?bool + public function toString(): string { - $success = $this->value === $other; - if ($returnResult) { - return $success; + $reduced = $this->reduce(); + if ($reduced !== $this) { + return $reduced->toString(); } - 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); + $constraint = $this->constraint->reduce(); + if ($this->constraintNeedsParentheses($constraint)) { + return $this->operator() . '( ' . $constraint->toString() . ' )'; } - return null; + $string = $constraint->toStringInContext($this, 0); + if ($string === '') { + return $this->transformString($constraint->toString()); + } + return $string; } /** - * Returns a string representation of the constraint. - * - * @throws InvalidArgumentException + * Counts the number of constraint elements. */ - public function toString() : string + public function count(): int { - 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); + 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 InvalidArgumentException */ - protected function failureDescription($other) : string + protected function failureDescription(mixed $other): string { - if (is_object($this->value) && is_object($other)) { - return 'two variables reference the same object'; + $reduced = $this->reduce(); + if ($reduced !== $this) { + return $reduced->failureDescription($other); } - if (is_string($this->value) && is_string($other)) { - return 'two strings are identical'; + $constraint = $this->constraint->reduce(); + if ($this->constraintNeedsParentheses($constraint)) { + return $this->operator() . '( ' . $constraint->failureDescription($other) . ' )'; } - if (is_array($this->value) && is_array($other)) { - return 'two arrays are identical'; + $string = $constraint->failureDescriptionInContext($this, 0, $other); + if ($string === '') { + return $this->transformString($constraint->failureDescription($other)); } - return parent::failureDescription($other); + return $string; + } + /** + * Transforms string returned by the memeber constraint's toString() or + * failureDescription() such that it reflects constraint's participation in + * this expression. + * + * The method may be overwritten in a subclass to apply default + * transformation in case the operand constraint does not provide its own + * custom strings via toStringInContext() or failureDescriptionInContext(). + */ + protected function transformString(string $string): string + { + return $string; + } + /** + * Provides access to $this->constraint for subclasses. + */ + final protected function constraint(): \PHPUnit\Framework\Constraint\Constraint + { + return $this->constraint; + } + /** + * Returns true if the $constraint needs to be wrapped with parentheses. + */ + protected function constraintNeedsParentheses(\PHPUnit\Framework\Constraint\Constraint $constraint): bool + { + $constraint = $constraint->reduce(); + return $constraint instanceof self || parent::constraintNeedsParentheses($constraint); } } value = $value; - } - /** - * Returns a string representation of the object. + * Returns a string representation of the constraint. */ - public function toString() : string + public function toString(): string { - return sprintf('matches JSON string "%s"', $this->value); + return 'is valid JSON'; } /** * 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 + protected function matches(mixed $other): bool { - [$error, $recodedOther] = Json::canonicalize($other); - if ($error) { + if (!is_string($other) || $other === '') { return \false; } - [$error, $recodedValue] = Json::canonicalize($this->value); - if ($error) { + json_decode($other); + if (json_last_error()) { return \false; } - return $recodedOther == $recodedValue; + return \true; } /** - * 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 - * - * @throws Exception - * @throws ExpectationFailedException - * @throws InvalidArgumentException + * Returns the description of the failure. * - * @psalm-return never-return + * The beginning of failure messages is "Failed asserting that" in most + * cases. This method should return the second part of that sentence. */ - protected function fail($other, $description, ?ComparisonFailure $comparisonFailure = null) : void + protected function failureDescription(mixed $other): string { - if ($comparisonFailure === null) { - [$error, $recodedOther] = Json::canonicalize($other); - if ($error) { - parent::fail($other, $description); - } - [$error, $recodedValue] = Json::canonicalize($this->value); - if ($error) { - parent::fail($other, $description); - } - $comparisonFailure = new ComparisonFailure(json_decode($this->value), json_decode($other), Json::prettify($recodedValue), Json::prettify($recodedOther), \false, 'Failed asserting that two json values are equal.'); + if (!is_string($other)) { + return $this->valueToTypeStringFragment($other) . 'is valid JSON'; } - parent::fail($other, $description, $comparisonFailure); + if ($other === '') { + return 'an empty string is valid JSON'; + } + return sprintf('a string is valid JSON (%s)', $this->determineJsonError($other)); + } + private function determineJsonError(string $json): string + { + json_decode($json); + return match (json_last_error()) { + \JSON_ERROR_NONE => '', + \JSON_ERROR_DEPTH => 'Maximum stack depth exceeded', + \JSON_ERROR_STATE_MISMATCH => 'Underflow or the modes mismatch', + \JSON_ERROR_CTRL_CHAR => 'Unexpected control character found', + \JSON_ERROR_SYNTAX => 'Syntax error, malformed JSON', + \JSON_ERROR_UTF8 => 'Malformed UTF-8 characters, possibly incorrectly encoded', + default => 'Unknown error', + }; } } pattern = $pattern; + } /** - * Translates JSON error to a human readable string. + * Returns a string representation of the constraint. */ - public static function determineJsonError(string $error, string $prefix = '') : ?string + public function toString(): 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'; - } + return sprintf('matches PCRE pattern "%s"', $this->pattern); } /** - * Translates a given type to a human readable message prefix. + * Evaluates the constraint for parameter $other. Returns true if the + * constraint is met, false otherwise. */ - public static function translateTypeToPrefix(string $type) : string + protected function matches(mixed $other): bool { - 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; + return preg_match($this->pattern, $other) > 0; } } normalizeLineEndings($needle); + } + $this->needle = $needle; + $this->ignoreCase = $ignoreCase; + $this->ignoreLineEndings = $ignoreLineEndings; + } /** * Returns a string representation of the constraint. */ - public function toString() : string + public function toString(): string { - return 'is finite'; + $needle = $this->needle; + if ($this->ignoreCase) { + $needle = mb_strtolower($this->needle, 'UTF-8'); + } + return sprintf('contains "%s" [%s](length: %s)', $needle, $this->getDetectedEncoding($needle), strlen($needle)); + } + public function failureDescription(mixed $other): string + { + $stringifiedHaystack = Exporter::export($other, \true); + $haystackEncoding = $this->getDetectedEncoding($other); + $haystackLength = $this->getHaystackLength($other); + $haystackInformation = sprintf('%s [%s](length: %s) ', $stringifiedHaystack, $haystackEncoding, $haystackLength); + $needleInformation = $this->toString(\true); + return $haystackInformation . $needleInformation; } /** * 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 + protected function matches(mixed $other): bool { - return is_finite($other); + $haystack = $other; + if ('' === $this->needle) { + return \true; + } + if (!is_string($haystack)) { + return \false; + } + if ($this->ignoreLineEndings) { + $haystack = $this->normalizeLineEndings($haystack); + } + if ($this->ignoreCase) { + /* + * We must use the multibyte-safe version, so we can accurately compare non-latin uppercase characters with + * their lowercase equivalents. + */ + return mb_stripos($haystack, $this->needle, 0, 'UTF-8') !== \false; + } + /* + * Use the non-multibyte safe functions to see if the string is contained in $other. + * + * This function is very fast, and we don't care about the character position in the string. + * + * Additionally, we want this method to be binary safe, so we can check if some binary data is in other binary + * data. + */ + return str_contains($haystack, $this->needle); + } + private function getDetectedEncoding(mixed $other): string + { + if ($this->ignoreCase) { + return 'Encoding ignored'; + } + if (!is_string($other)) { + return 'Encoding detection failed'; + } + $detectedEncoding = mb_detect_encoding($other, null, \true); + if ($detectedEncoding === \false) { + return 'Encoding detection failed'; + } + return $detectedEncoding; + } + private function getHaystackLength(mixed $haystack): int + { + if (!is_string($haystack)) { + return 0; + } + if ($this->ignoreLineEndings) { + $haystack = $this->normalizeLineEndings($haystack); + } + return strlen($haystack); + } + private function normalizeLineEndings(string $string): string + { + return strtr($string, ["\r\n" => "\n", "\r" => "\n"]); } } suffix = $suffix; + } /** * Returns a string representation of the constraint. */ - public function toString() : string + public function toString(): string { - return 'is infinite'; + 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 + protected function matches(mixed $other): bool { - return is_infinite($other); + return str_ends_with((string) $other, $this->suffix); } } string = $this->normalizeLineEndings($string); + } /** * Returns a string representation of the constraint. */ - public function toString() : string + public function toString(): string { - return 'is nan'; + return sprintf('is equal to "%s" ignoring line endings', $this->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 + protected function matches(mixed $other): bool { - return is_nan($other); + return $this->string === $this->normalizeLineEndings((string) $other); + } + private function normalizeLineEndings(string $string): string + { + return strtr($string, ["\r\n" => "\n", "\r" => "\n"]); } } attributeName = $attributeName; + $this->formatDescription = $formatDescription; } - /** - * Returns a string representation of the constraint. - */ - public function toString() : string + public function toString(): string { - return sprintf('has attribute "%s"', $this->attributeName); + return 'matches format description:' . \PHP_EOL . $this->formatDescription; } /** * 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 + protected function matches(mixed $other): bool { - try { - return (new ReflectionClass($other))->hasProperty($this->attributeName); - // @codeCoverageIgnoreStart - } catch (ReflectionException $e) { - throw new Exception($e->getMessage(), $e->getCode(), $e); + $other = $this->convertNewlines($other); + $matches = preg_match($this->regularExpressionForFormatDescription($this->convertNewlines($this->formatDescription)), $other); + return $matches > 0; + } + protected function failureDescription(mixed $other): string + { + return 'string matches format description'; + } + protected function additionalFailureDescription(mixed $other): string + { + $from = explode("\n", $this->formatDescription); + $to = explode("\n", $this->convertNewlines($other)); + foreach ($from as $index => $line) { + if (isset($to[$index]) && $line !== $to[$index]) { + $line = $this->regularExpressionForFormatDescription($line); + if (preg_match($line, $to[$index]) > 0) { + $from[$index] = $to[$index]; + } + } } - // @codeCoverageIgnoreEnd + $from = implode("\n", $from); + $to = implode("\n", $to); + return $this->differ()->diff($from, $to); } - /** - * 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 + private function regularExpressionForFormatDescription(string $string): string { - return sprintf('%sclass "%s" %s', is_object($other) ? 'object of ' : '', is_object($other) ? get_class($other) : $other, $this->toString()); + $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(string $text): string + { + return preg_replace('/\r\n/', "\n", $text); } - protected function attributeName() : string + private function differ(): Differ { - return $this->attributeName; + return new Differ(new UnifiedDiffOutputBuilder("--- Expected\n+++ Actual\n")); } } prefix = $prefix; + } /** * Returns a string representation of the constraint. */ - public function toString() : string + public function toString(): string { - return sprintf('has static attribute "%s"', $this->attributeName()); + 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 + protected function matches(mixed $other): bool { - try { - $class = new ReflectionClass($other); - if ($class->hasProperty($this->attributeName())) { - return $class->getProperty($this->attributeName())->isStatic(); - } - // @codeCoverageIgnoreStart - } catch (ReflectionException $e) { - throw new Exception($e->getMessage(), $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd - return \false; + return str_starts_with((string) $other, $this->prefix); } } expected = $object; - $this->method = $method; + $this->key = $key; } - public function toString() : string + /** + * Returns a string representation of the constraint. + */ + public function toString(): string { - return 'two objects are equal'; + return 'has the key ' . Exporter::export($this->key); } /** - * @throws ActualValueIsNotAnObjectException - * @throws ComparisonMethodDoesNotAcceptParameterTypeException - * @throws ComparisonMethodDoesNotDeclareBoolReturnTypeException - * @throws ComparisonMethodDoesNotDeclareExactlyOneParameterException - * @throws ComparisonMethodDoesNotDeclareParameterTypeException - * @throws ComparisonMethodDoesNotExistException + * Evaluates the constraint for parameter $other. Returns true if the + * constraint is met, false otherwise. */ - protected function matches($other) : bool + protected function matches(mixed $other): bool { - if (!is_object($other)) { - throw new ActualValueIsNotAnObjectException(); - } - $object = new ReflectionObject($other); - if (!$object->hasMethod($this->method)) { - throw new ComparisonMethodDoesNotExistException(get_class($other), $this->method); - } - /** @noinspection PhpUnhandledExceptionInspection */ - $method = $object->getMethod($this->method); - if (!$method->hasReturnType()) { - throw new ComparisonMethodDoesNotDeclareBoolReturnTypeException(get_class($other), $this->method); - } - $returnType = $method->getReturnType(); - if (!$returnType instanceof ReflectionNamedType) { - throw new ComparisonMethodDoesNotDeclareBoolReturnTypeException(get_class($other), $this->method); - } - if ($returnType->allowsNull()) { - throw new ComparisonMethodDoesNotDeclareBoolReturnTypeException(get_class($other), $this->method); - } - if ($returnType->getName() !== 'bool') { - throw new ComparisonMethodDoesNotDeclareBoolReturnTypeException(get_class($other), $this->method); - } - if ($method->getNumberOfParameters() !== 1 || $method->getNumberOfRequiredParameters() !== 1) { - throw new ComparisonMethodDoesNotDeclareExactlyOneParameterException(get_class($other), $this->method); - } - $parameter = $method->getParameters()[0]; - if (!$parameter->hasType()) { - throw new ComparisonMethodDoesNotDeclareParameterTypeException(get_class($other), $this->method); - } - $type = $parameter->getType(); - if (!$type instanceof ReflectionNamedType) { - throw new ComparisonMethodDoesNotDeclareParameterTypeException(get_class($other), $this->method); - } - $typeName = $type->getName(); - if ($typeName === 'self') { - $typeName = get_class($other); + if (is_array($other)) { + return array_key_exists($this->key, $other); } - if (!$this->expected instanceof $typeName) { - throw new ComparisonMethodDoesNotAcceptParameterTypeException(get_class($other), $this->method, get_class($this->expected)); + if ($other instanceof ArrayAccess) { + return $other->offsetExists($this->key); } - return $other->{$this->method}($this->expected); + return \false; } - protected function failureDescription($other) : string + /** + * Returns the description of the failure. + * + * The beginning of failure messages is "Failed asserting that" in most + * cases. This method should return the second part of that sentence. + */ + protected function failureDescription(mixed $other): string { - return $this->toString(); + return 'an array ' . $this->toString(\true); } } hasProperty($this->attributeName()); + return $this->valueToTypeStringFragment($other) . $this->toString(\true); } } propertyName = $propertyName; + $this->value = $value; } /** * Returns a string representation of the constraint. */ - public function toString() : string - { - return sprintf('has property "%s"', $this->propertyName); - } - /** - * 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 + public function toString(bool $exportObjects = \false): string { - if (!is_object($other)) { - return \false; - } - return (new ReflectionObject($other))->hasProperty($this->propertyName); + return 'contains ' . Exporter::export($this->value, $exportObjects); } /** * 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 + protected function failureDescription(mixed $other): string { - if (is_object($other)) { - return sprintf('object of class "%s" %s', get_class($other), $this->toString()); - } - return sprintf('"%s" (%s) %s', $other, gettype($other), $this->toString()); + return sprintf('%s %s', is_array($other) ? 'an array' : 'a traversable', $this->toString(\true)); + } + protected function value(): mixed + { + return $this->value; } } constraints = $constraints; - return $constraint; - } - /** - * @param mixed[] $constraints - */ - public function setConstraints(array $constraints) : void - { - $this->constraints = array_map(function ($constraint) : \PHPUnit\Framework\Constraint\Constraint { - return $this->checkConstraint($constraint); - }, array_values($constraints)); - } - /** - * Returns the number of operands (constraints). + * Evaluates the constraint for parameter $other. Returns true if the + * constraint is met, false otherwise. */ - public final function arity() : int + protected function matches(mixed $other): bool { - return count($this->constraints); + if ($other instanceof SplObjectStorage) { + return $other->contains($this->value()); + } + foreach ($other as $element) { + /* @noinspection TypeUnsafeComparisonInspection */ + if ($this->value() == $element) { + return \true; + } + } + return \false; } +} + + * + * 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; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class TraversableContainsIdentical extends \PHPUnit\Framework\Constraint\TraversableContains +{ /** - * Returns a string representation of the constraint. + * Evaluates the constraint for parameter $other. Returns true if the + * constraint is met, false otherwise. */ - public function toString() : string + protected function matches(mixed $other): bool { - $reduced = $this->reduce(); - if ($reduced !== $this) { - return $reduced->toString(); + if ($other instanceof SplObjectStorage) { + return $other->contains($this->value()); } - $text = ''; - foreach ($this->constraints as $key => $constraint) { - $constraint = $constraint->reduce(); - $text .= $this->constraintToString($constraint, $key); - } - return $text; - } - /** - * Counts the number of constraint elements. - */ - public function count() : int - { - $count = 0; - foreach ($this->constraints as $constraint) { - $count += count($constraint); - } - return $count; - } - /** - * Returns the nested constraints. - */ - protected final function constraints() : array - { - return $this->constraints; - } - /** - * Returns true if the $constraint needs to be wrapped with braces. - */ - protected final function constraintNeedsParentheses(\PHPUnit\Framework\Constraint\Constraint $constraint) : bool - { - return $this->arity() > 1 && parent::constraintNeedsParentheses($constraint); - } - /** - * Reduces the sub-expression starting at $this by skipping degenerate - * sub-expression and returns first descendant constraint that starts - * a non-reducible sub-expression. - * - * See Constraint::reduce() for more. - */ - protected function reduce() : \PHPUnit\Framework\Constraint\Constraint - { - if ($this->arity() === 1 && $this->constraints[0] instanceof \PHPUnit\Framework\Constraint\Operator) { - return $this->constraints[0]->reduce(); - } - return parent::reduce(); - } - /** - * Returns string representation of given operand in context of this operator. - * - * @param Constraint $constraint operand constraint - * @param int $position position of $constraint in this expression - */ - private function constraintToString(\PHPUnit\Framework\Constraint\Constraint $constraint, int $position) : string - { - $prefix = ''; - if ($position > 0) { - $prefix = ' ' . $this->operator() . ' '; - } - if ($this->constraintNeedsParentheses($constraint)) { - return $prefix . '( ' . $constraint->toString() . ' )'; - } - $string = $constraint->toStringInContext($this, $position); - if ($string === '') { - $string = $constraint->toString(); + foreach ($other as $element) { + if ($this->value() === $element) { + return \true; + } } - return $prefix . $string; + return \false; } } constraint = new \PHPUnit\Framework\Constraint\IsType($type); + } else { + $this->constraint = new \PHPUnit\Framework\Constraint\IsInstanceOf($type); + } + $this->type = $type; } /** - * Returns this operator's precedence. + * Evaluates the constraint for parameter $other. * - * @see https://www.php.net/manual/en/language.operators.precedence.php + * 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 precedence() : int + public function evaluate(mixed $other, string $description = '', bool $returnResult = \false): bool { - return 22; + $success = \true; + foreach ($other as $item) { + if (!$this->constraint->evaluate($item, '', \true)) { + $success = \false; + break; + } + } + if (!$success && !$returnResult) { + $this->fail($other, $description); + } + return $success; } /** - * Evaluates the constraint for parameter $other. Returns true if the - * constraint is met, false otherwise. - * - * @param mixed $other value or object to evaluate + * Returns a string representation of the constraint. */ - protected function matches($other) : bool + public function toString(): string { - foreach ($this->constraints() as $constraint) { - if (!$constraint->evaluate($other, '', \true)) { - return \false; - } - } - return [] !== $this->constraints(); + return 'contains only values of type "' . $this->type . '"'; } } 0) { - $nonInput = $matches[2]; - $negatedString = preg_replace('/' . preg_quote($nonInput, '/') . '/', preg_replace($positives, $negatives, $nonInput), $string); - } else { - $negatedString = preg_replace($positives, $negatives, $string); - } - return $negatedString; - } /** - * Returns the name of this operator. + * @psalm-var class-string */ - public function operator() : string - { - return 'not'; - } + private readonly string $name; /** - * Returns this operator's precedence. - * - * @see https://www.php.net/manual/en/language.operators.precedence.php + * @psalm-var 'class'|'interface' + */ + private readonly string $type; + /** + * @throws UnknownClassOrInterfaceException */ - public function precedence() : int + public function __construct(string $name) { - return 5; + if (class_exists($name)) { + $this->type = 'class'; + } elseif (interface_exists($name)) { + $this->type = 'interface'; + } else { + throw new UnknownClassOrInterfaceException($name); + } + $this->name = $name; } /** - * Evaluates the constraint for parameter $other. Returns true if the - * constraint is met, false otherwise. - * - * @param mixed $other value or object to evaluate + * Returns a string representation of the constraint. */ - protected function matches($other) : bool + public function toString(): string { - return !$this->constraint()->evaluate($other, '', \true); + return sprintf('is an instance of %s %s', $this->type, $this->name); } /** - * Applies additional transformation to strings returned by toString() or - * failureDescription(). + * Evaluates the constraint for parameter $other. Returns true if the + * constraint is met, false otherwise. */ - protected function transformString(string $string) : string + protected function matches(mixed $other): bool { - return self::negate($string); + return $other instanceof $this->name; } /** - * Reduces the sub-expression starting at $this by skipping degenerate - * sub-expression and returns first descendant constraint that starts - * a non-reducible sub-expression. + * Returns the description of the failure. * - * See Constraint::reduce() for more. + * The beginning of failure messages is "Failed asserting that" in most + * cases. This method should return the second part of that sentence. */ - protected function reduce() : \PHPUnit\Framework\Constraint\Constraint + protected function failureDescription(mixed $other): string { - $constraint = $this->constraint(); - if ($constraint instanceof self) { - return $constraint->constraint()->reduce(); - } - return parent::reduce(); + return $this->valueToTypeStringFragment($other) . $this->toString(\true); } } constraints() as $constraint) { - if ($constraint->evaluate($other, '', \true)) { - return \true; - } - } - return \false; + return $other === null; } } constraints(); - $initial = array_shift($constraints); - if ($initial === null) { - return \false; - } - return array_reduce($constraints, static function (bool $matches, \PHPUnit\Framework\Constraint\Constraint $constraint) use($other) : bool { - return $matches xor $constraint->evaluate($other, '', \true); - }, $initial->evaluate($other, '', \true)); - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -abstract class Operator extends \PHPUnit\Framework\Constraint\Constraint -{ + public const TYPE_FLOAT = 'float'; /** - * Returns the name of this operator. + * @var string */ - public abstract function operator() : string; + public const TYPE_INT = 'int'; /** - * Returns this operator's precedence. - * - * @see https://www.php.net/manual/en/language.operators.precedence.php + * @var string */ - public abstract function precedence() : int; + public const TYPE_NULL = 'null'; /** - * Returns the number of operands. + * @var string */ - public abstract function arity() : int; + public const TYPE_NUMERIC = 'numeric'; /** - * Validates $constraint argument. + * @var string */ - protected function checkConstraint($constraint) : \PHPUnit\Framework\Constraint\Constraint - { - if (!$constraint instanceof \PHPUnit\Framework\Constraint\Constraint) { - return new \PHPUnit\Framework\Constraint\IsEqual($constraint); - } - return $constraint; - } + public const TYPE_OBJECT = 'object'; /** - * Returns true if the $constraint needs to be wrapped with braces. + * @var string */ - protected function constraintNeedsParentheses(\PHPUnit\Framework\Constraint\Constraint $constraint) : bool - { - return $constraint instanceof self && $constraint->arity() > 1 && $this->precedence() <= $constraint->precedence(); - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -use function count; -use PHPUnitPHAR\SebastianBergmann\RecursionContext\InvalidArgumentException; -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -abstract class UnaryOperator extends \PHPUnit\Framework\Constraint\Operator -{ + public const TYPE_RESOURCE = 'resource'; /** - * @var Constraint + * @var string */ - private $constraint; + public const TYPE_CLOSED_RESOURCE = 'resource (closed)'; /** - * @param Constraint|mixed $constraint + * @var string */ - public function __construct($constraint) - { - $this->constraint = $this->checkConstraint($constraint); - } + public const TYPE_STRING = 'string'; /** - * Returns the number of operands (constraints). + * @var string */ - public function arity() : int - { - return 1; - } + public const TYPE_SCALAR = 'scalar'; /** - * Returns a string representation of the constraint. + * @var string */ - public function toString() : string - { - $reduced = $this->reduce(); - if ($reduced !== $this) { - return $reduced->toString(); - } - $constraint = $this->constraint->reduce(); - if ($this->constraintNeedsParentheses($constraint)) { - return $this->operator() . '( ' . $constraint->toString() . ' )'; - } - $string = $constraint->toStringInContext($this, 0); - if ($string === '') { - return $this->transformString($constraint->toString()); - } - return $string; - } + public const TYPE_CALLABLE = 'callable'; /** - * Counts the number of constraint elements. + * @var string */ - public function count() : int - { - return count($this->constraint); - } + public const TYPE_ITERABLE = 'iterable'; /** - * 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 InvalidArgumentException + * @psalm-var array */ - protected function failureDescription($other) : string - { - $reduced = $this->reduce(); - if ($reduced !== $this) { - return $reduced->failureDescription($other); - } - $constraint = $this->constraint->reduce(); - if ($this->constraintNeedsParentheses($constraint)) { - return $this->operator() . '( ' . $constraint->failureDescription($other) . ' )'; - } - $string = $constraint->failureDescriptionInContext($this, 0, $other); - if ($string === '') { - return $this->transformString($constraint->failureDescription($other)); - } - return $string; - } + 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, 'resource (closed)' => \true, 'string' => \true, 'scalar' => \true, 'callable' => \true, 'iterable' => \true]; /** - * Transforms string returned by the memeber constraint's toString() or - * failureDescription() such that it reflects constraint's participation in - * this expression. - * - * The method may be overwritten in a subclass to apply default - * transformation in case the operand constraint does not provide its own - * custom strings via toStringInContext() or failureDescriptionInContext(). + * @psalm-var 'array'|'boolean'|'bool'|'double'|'float'|'integer'|'int'|'null'|'numeric'|'object'|'real'|'resource'|'resource (closed)'|'string'|'scalar'|'callable'|'iterable' + */ + private readonly string $type; + /** + * @psalm-param 'array'|'boolean'|'bool'|'double'|'float'|'integer'|'int'|'null'|'numeric'|'object'|'real'|'resource'|'resource (closed)'|'string'|'scalar'|'callable'|'iterable' $type * - * @param string $string the string to be transformed + * @throws UnknownTypeException */ - protected function transformString(string $string) : string + public function __construct(string $type) { - return $string; + if (!isset(self::KNOWN_TYPES[$type])) { + throw new UnknownTypeException($type); + } + $this->type = $type; } /** - * Provides access to $this->constraint for subclasses. + * Returns a string representation of the constraint. */ - protected final function constraint() : \PHPUnit\Framework\Constraint\Constraint + public function toString(): string { - return $this->constraint; + return sprintf('is of type %s', $this->type); } /** - * Returns true if the $constraint needs to be wrapped with parentheses. + * Evaluates the constraint for parameter $other. Returns true if the + * constraint is met, false otherwise. */ - protected function constraintNeedsParentheses(\PHPUnit\Framework\Constraint\Constraint $constraint) : bool + protected function matches(mixed $other): bool { - $constraint = $constraint->reduce(); - return $constraint instanceof self || parent::constraintNeedsParentheses($constraint); + 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': + $type = gettype($other); + return $type === 'resource' || $type === 'resource (closed)'; + case 'resource (closed)': + return gettype($other) === 'resource (closed)'; + case 'scalar': + return is_scalar($other); + case 'callable': + return is_callable($other); + case 'iterable': + return is_iterable($other); + default: + return \false; + } } } + */ + private array $dependencies = []; + private ?array $providedTests = null; + /** + * @psalm-param list $dependencies */ - public function toString() : string + public function setDependencies(array $dependencies): void { - return 'is valid JSON'; + $this->dependencies = $dependencies; + foreach ($this->tests() as $test) { + if (!$test instanceof \PHPUnit\Framework\TestCase) { + continue; + } + $test->setDependencies($dependencies); + } } /** - * Evaluates the constraint for parameter $other. Returns true if the - * constraint is met, false otherwise. - * - * @param mixed $other value or object to evaluate + * @psalm-return list */ - protected function matches($other) : bool + public function provides(): array { - if ($other === '') { - return \false; - } - json_decode($other); - if (json_last_error()) { - return \false; + if ($this->providedTests === null) { + $this->providedTests = [new \PHPUnit\Framework\ExecutionOrderDependency($this->name())]; } - return \true; + return $this->providedTests; } /** - * 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 InvalidArgumentException + * @psalm-return list */ - protected function failureDescription($other) : string + public function requires(): array { - if ($other === '') { - return 'an empty string is valid JSON'; - } - json_decode($other); - $error = (string) \PHPUnit\Framework\Constraint\JsonMatchesErrorMessageProvider::determineJsonError((string) json_last_error()); - return sprintf('%s is valid JSON (%s)', $this->exporter()->shortenedExport($other), $error); + // A DataProviderTestSuite does not have to traverse its child tests + // as these are inherited and cannot reference dataProvider rows directly + return $this->dependencies; + } + /** + * Returns the size of each test created using the data provider(s). + */ + public function size(): TestSize + { + [$className, $methodName] = explode('::', $this->name()); + return (new Groups())->size($className, $methodName); } } 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 + * Wrapper for getMessage() which is declared as final. */ - protected function matches($other) : bool + public function toString(): string { - return preg_match($this->pattern, $other) > 0; + return $this->getMessage(); } } string = $string; - $this->ignoreCase = $ignoreCase; - } - /** - * Returns a string representation of the constraint. - */ - public function toString() : string - { - if ($this->ignoreCase) { - $string = mb_strtolower($this->string, 'UTF-8'); - } 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) { - /* - * We must use the multi byte safe version so we can accurately compare non latin upper characters with - * their lowercase equivalents. - */ - return mb_stripos($other, $this->string, 0, 'UTF-8') !== \false; - } - /* - * Use the non multi byte safe functions to see if the string is contained in $other. - * - * This function is very fast and we don't care about the character position in the string. - * - * Additionally, we want this method to be binary safe so we can check if some binary data is in other binary - * data. - */ - return strpos($other, $this->string) !== \false; - } } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use function array_keys; +use function get_object_vars; +use RuntimeException; +use Throwable; +/** + * 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 +{ + protected array $serializableTrace; + public function __construct(string $message = '', int $code = 0, ?Throwable $previous = null) { - $this->suffix = $suffix; + parent::__construct($message, $code, $previous); + $this->serializableTrace = $this->getTrace(); + foreach (array_keys($this->serializableTrace) as $key) { + unset($this->serializableTrace[$key]['args']); + } } - /** - * Returns a string representation of the constraint. - */ - public function toString() : string + public function __sleep(): array { - return 'ends with "' . $this->suffix . '"'; + return array_keys(get_object_vars($this)); } /** - * Evaluates the constraint for parameter $other. Returns true if the - * constraint is met, false otherwise. - * - * @param mixed $other value or object to evaluate + * Returns the serializable trace (without 'args'). */ - protected function matches($other) : bool + public function getSerializableTrace(): array { - return substr($other, 0 - strlen($this->suffix)) === $this->suffix; + return $this->serializableTrace; } } 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(new UnifiedDiffOutputBuilder("--- Expected\n+++ Actual\n")))->diff($this->string, $other); - } - private function createPatternFromFormat(string $string) : string + protected ?ComparisonFailure $comparisonFailure = null; + public function __construct(string $message, ?ComparisonFailure $comparisonFailure = null, ?Exception $previous = null) { - $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'; + $this->comparisonFailure = $comparisonFailure; + parent::__construct($message, 0, $previous); } - private function convertNewlines(string $text) : string + public function getComparisonFailure(): ?ComparisonFailure { - return preg_replace('/\\r\\n/', "\n", $text); + return $this->comparisonFailure; } } 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 + public static function fromParameterName(string $parameterName): self { - return strpos((string) $other, $this->prefix) === 0; + return new self(sprintf('Passing an argument of type Generator for the %s parameter is not supported', $parameterName)); } } key = $key; - } - /** - * Returns a string representation of the constraint. - * - * @throws 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 InvalidArgumentException - */ - protected function failureDescription($other) : string - { - return 'an array ' . $this->toString(); - } } value = $value; - } - /** - * Returns a string representation of the constraint. - * - * @throws InvalidArgumentException - */ - public function toString() : string - { - return 'contains ' . $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 InvalidArgumentException - */ - protected function failureDescription($other) : string - { - return sprintf('%s %s', is_array($other) ? 'an array' : 'a traversable', $this->toString()); - } - protected function value() - { - return $this->value; - } } contains($this->value()); - } - foreach ($other as $element) { - /* @noinspection TypeUnsafeComparisonInspection */ - if ($this->value() == $element) { - return \true; - } - } - return \false; - } } contains($this->value()); - } - foreach ($other as $element) { - if ($this->value() === $element) { - return \true; - } - } - return \false; - } } constraint = new \PHPUnit\Framework\Constraint\IsType($type); - } else { - $this->constraint = new \PHPUnit\Framework\Constraint\IsInstanceOf($type); - } - $this->type = $type; - } - /** - * Evaluates the constraint for parameter $other. - * - * If $returnResult is set to false (the default), an exception is thrown - * in case of a failure. null is returned otherwise. - * - * If $returnResult is true, the result of the evaluation is returned as - * a boolean value instead: true in case of success, false in case of a - * failure. - * - * @param mixed|Traversable $other - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - */ - public function evaluate($other, string $description = '', bool $returnResult = \false) : ?bool - { - $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); - } - return null; - } - /** - * Returns a string representation of the constraint. - */ - public function toString() : string - { - return 'contains only values of type "' . $this->type . '"'; - } } 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 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'; - } } + * + * 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 ActualValueIsNotAnObjectException extends \PHPUnit\Framework\Exception +{ + public function __construct() { - return 'is null'; + parent::__construct('Actual value is not an object'); } - /** - * 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 +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use function sprintf; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class ComparisonMethodDoesNotAcceptParameterTypeException extends \PHPUnit\Framework\Exception +{ + public function __construct(string $className, string $methodName, string $type) { - return $other === null; + parent::__construct(sprintf('%s is not an accepted argument type for comparison method %s::%s().', $type, $className, $methodName)); } } - */ - 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, 'resource (closed)' => \true, 'string' => \true, 'scalar' => \true, 'callable' => \true, 'iterable' => \true]; - /** - * @var string - */ - private $type; - /** - * @throws Exception - */ - public function __construct(string $type) - { - if (!isset(self::KNOWN_TYPES[$type])) { - throw new 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 + public function __construct(string $className, string $methodName) { - 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': - $type = gettype($other); - return $type === 'resource' || $type === 'resource (closed)'; - case 'resource (closed)': - return gettype($other) === 'resource (closed)'; - case 'scalar': - return is_scalar($other); - case 'callable': - return is_callable($other); - case 'iterable': - return is_iterable($other); - default: - return \false; - } + parent::__construct(sprintf('Comparison method %s::%s() does not declare bool return type.', $className, $methodName)); } } - */ - private $dependencies = []; - /** - * @param list $dependencies - */ - public function setDependencies(array $dependencies) : void - { - $this->dependencies = $dependencies; - foreach ($this->tests as $test) { - if (!$test instanceof \PHPUnit\Framework\TestCase) { - // @codeCoverageIgnoreStart - continue; - // @codeCoverageIgnoreStart - } - $test->setDependencies($dependencies); - } - } - /** - * @return list - */ - public function provides() : array - { - if ($this->providedTests === null) { - $this->providedTests = [new \PHPUnit\Framework\ExecutionOrderDependency($this->getName())]; - } - return $this->providedTests; - } - /** - * @return list - */ - public function requires() : array - { - // A DataProviderTestSuite does not have to traverse its child tests - // as these are inherited and cannot reference dataProvider rows directly - return $this->dependencies; - } - /** - * Returns the size of the each test created using the data provider(s). - * - * @throws InvalidArgumentException - */ - public function getSize() : int + public function __construct(string $className, string $methodName) { - [$className, $methodName] = explode('::', $this->getName()); - return TestUtil::getSize($className, $methodName); + parent::__construct(sprintf('Comparison method %s::%s() does not declare exactly one parameter.', $className, $methodName)); } } file = $file; - $this->line = $line; + parent::__construct(sprintf('Comparison method %s::%s() does not exist.', $className, $methodName)); } } syntheticFile = $file; + $this->syntheticLine = $line; + $this->syntheticTrace = $trace; + $this->diff = $diff; + } + public function syntheticFile(): string + { + return $this->syntheticFile; + } + public function syntheticLine(): int + { + return $this->syntheticLine; + } + public function syntheticTrace(): array + { + return $this->syntheticTrace; + } + public function diff(): string + { + return $this->diff; + } } message = $message; - parent::__construct('Error'); - } - public function getMessage() : string - { - return $this->message; - } - /** - * Returns a string representation of the test case. - */ - public function toString() : string - { - return 'Error'; - } - /** - * @throws Exception - * - * @psalm-return never-return - */ - protected function runTest() : void - { - throw new \PHPUnit\Framework\Error($this->message); - } } getMessage() . PHP_EOL; - } } getMessage(); - } } getMessage() . PHP_EOL; + parent::__construct(sprintf('Type "%s" is not known', $name)); } } className(), 'class', $metadata->deepClone(), $metadata->shallowClone()); + } + public static function forMethod(DependsOnMethod $metadata): self + { + return new self($metadata->className(), $metadata->methodName(), $metadata->deepClone(), $metadata->shallowClone()); + } + /** + * @psalm-param list $dependencies + * + * @psalm-return list + */ + public static function filterInvalid(array $dependencies): array + { + return array_values(array_filter($dependencies, static fn(self $d) => $d->isValid())); + } + /** + * @psalm-param list $existing + * @psalm-param list $additional + * + * @psalm-return list + */ + public static function mergeUnique(array $existing, array $additional): array + { + $existingTargets = array_map(static fn($dependency) => $dependency->getTarget(), $existing); + foreach ($additional as $dependency) { + $additionalTarget = $dependency->getTarget(); + if (in_array($additionalTarget, $existingTargets, \true)) { + continue; + } + $existingTargets[] = $additionalTarget; + $existing[] = $dependency; + } + return $existing; + } + /** + * @psalm-param list $left + * @psalm-param list $right + * + * @psalm-return list + */ + public static function diff(array $left, array $right): array + { + if ($right === []) { + return $left; + } + if ($left === []) { + return []; + } + $diff = []; + $rightTargets = array_map(static fn($dependency) => $dependency->getTarget(), $right); + foreach ($left as $dependency) { + if (in_array($dependency->getTarget(), $rightTargets, \true)) { + continue; + } + $diff[] = $dependency; + } + return $diff; + } + public function __construct(string $classOrCallableName, ?string $methodName = null, bool $deepClone = \false, bool $shallowClone = \false) + { + $this->deepClone = $deepClone; + $this->shallowClone = $shallowClone; + if ($classOrCallableName === '') { + return; + } + if (str_contains($classOrCallableName, '::')) { + [$this->className, $this->methodName] = explode('::', $classOrCallableName); + } else { + $this->className = $classOrCallableName; + $this->methodName = (!empty($methodName)) ? $methodName : 'class'; + } + } + public function __toString(): string + { + return $this->getTarget(); + } + public function isValid(): bool { - parent::__construct(sprintf('Comparison method %s::%s() does not declare bool return type.', $className, $methodName), 0, null); + // Invalid dependencies can be declared and are skipped by the runner + return $this->className !== '' && $this->methodName !== ''; + } + public function shallowClone(): bool + { + return $this->shallowClone; + } + public function deepClone(): bool + { + return $this->deepClone; + } + public function targetIsClass(): bool + { + return $this->methodName === 'class'; + } + public function getTarget(): string + { + return $this->isValid() ? $this->className . '::' . $this->methodName : ''; } - public function __toString() : string + public function getTargetClassName(): string { - return $this->getMessage() . PHP_EOL; + return $this->className; } } + */ + private readonly array $defaultParameterValues; + /** + * @psalm-var non-negative-int + */ + private readonly int $numberOfParameters; + private readonly Type $returnType; + /** + * @psalm-param non-empty-string $name + * @psalm-param array $defaultParameterValues + * @psalm-param non-negative-int $numberOfParameters + */ + public function __construct(string $name, array $defaultParameterValues, int $numberOfParameters, Type $returnType) + { + $this->name = $name; + $this->defaultParameterValues = $defaultParameterValues; + $this->numberOfParameters = $numberOfParameters; + $this->returnType = $returnType; + } + /** + * @psalm-return non-empty-string + */ + public function name(): string + { + return $this->name; + } + /** + * @psalm-return array + */ + public function defaultParameterValues(): array + { + return $this->defaultParameterValues; + } + /** + * @psalm-return non-negative-int + */ + public function numberOfParameters(): int + { + return $this->numberOfParameters; + } + public function mayReturn(mixed $value): bool { - parent::__construct(sprintf('Comparison method %s::%s() does not declare exactly one parameter.', $className, $methodName), 0, null); + return $this->returnType->isAssignable(Type::fromValue($value, \false)); } - public function __toString() : string + public function returnTypeDeclaration(): string { - return $this->getMessage() . PHP_EOL; + return $this->returnType->asString(); } } getMessage() . PHP_EOL; - } } getMessage() . PHP_EOL; + parent::__construct(sprintf('Trying to configure method "%s" with onlyMethods(), but it does not exist in class "%s"', $methodName, $type)); } } getMessage(); + parent::__construct(sprintf('Method %s may not return value of type %s, its declared return type is "%s"', $method->name(), get_debug_type($value), $method->returnTypeDeclaration())); } } serializableTrace = $this->getTrace(); - foreach (array_keys($this->serializableTrace) as $key) { - unset($this->serializableTrace[$key]['args']); - } - } - public function __toString() : string - { - $string = \PHPUnit\Framework\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 + public function __construct(string $id) { - return $this->serializableTrace; + parent::__construct(sprintf('No builder found for match builder identification <%s>', $id)); } } comparisonFailure = $comparisonFailure; - parent::__construct($message, 0, $previous); - } - public function getComparisonFailure() : ?ComparisonFailure + public function __construct(string $id) { - return $this->comparisonFailure; + parent::__construct(sprintf('Matcher with id <%s> is already registered', $id)); } } className(), $invocation->methodName())); + } } diff = $diff; - } - public function getDiff() : string - { - return $this->diff; - } } 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 + public function __construct(string $className) { - return $this->syntheticTrace; + parent::__construct(sprintf('Class "%s" is declared "readonly" and cannot be doubled', $className)); } } $methods + */ + public function __construct(array $methods) + { + parent::__construct(sprintf('Cannot double using a method list that contains duplicates: "%s" (duplicate: "%s")', implode(', ', $methods), implode(', ', array_unique(array_diff_assoc($methods, array_unique($methods)))))); + } } getMessage(); + parent::__construct(sprintf('Cannot double method with invalid name "%s"', $method)); } } + * @psalm-param class-string|trait-string $name */ - private $originalException; - 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 = \PHPUnit\Framework\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 (array_keys($this->serializableTrace) as $key) { - unset($this->serializableTrace[$key]['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 + public function __construct(string $name) { - // drop once PHP 7.3 support is removed - if (PHP_VERSION_ID < 70400) { - static $originalExceptions; - $instanceId = spl_object_hash($this); - if ($exceptionToStore) { - $originalExceptions[$instanceId] = $exceptionToStore; - } - return $originalExceptions[$instanceId] ?? null; - } - if ($exceptionToStore) { - $this->originalException = WeakReference::create($exceptionToStore); - } - return $this->originalException !== null ? $this->originalException->get() : null; + parent::__construct(sprintf('The name "%s" is already in use', $name)); } } $dependencies - * - * @psalm-return list - */ - public static function filterInvalid(array $dependencies) : array - { - return array_values(array_filter($dependencies, static function (self $d) { - return $d->isValid(); - })); - } - /** - * @psalm-param list $existing - * @psalm-param list $additional - * - * @psalm-return list - */ - public static function mergeUnique(array $existing, array $additional) : array - { - $existingTargets = array_map(static function ($dependency) { - return $dependency->getTarget(); - }, $existing); - foreach ($additional as $dependency) { - if (in_array($dependency->getTarget(), $existingTargets, \true)) { - continue; - } - $existingTargets[] = $dependency->getTarget(); - $existing[] = $dependency; - } - return $existing; - } - /** - * @psalm-param list $left - * @psalm-param list $right - * - * @psalm-return list - */ - public static function diff(array $left, array $right) : array - { - if ($right === []) { - return $left; - } - if ($left === []) { - return []; - } - $diff = []; - $rightTargets = array_map(static function ($dependency) { - return $dependency->getTarget(); - }, $right); - foreach ($left as $dependency) { - if (in_array($dependency->getTarget(), $rightTargets, \true)) { - continue; - } - $diff[] = $dependency; - } - return $diff; - } - public function __construct(string $classOrCallableName, ?string $methodName = null, ?string $option = null) - { - if ($classOrCallableName === '') { - return; - } - if (strpos($classOrCallableName, '::') !== \false) { - [$this->className, $this->methodName] = explode('::', $classOrCallableName); - } else { - $this->className = $classOrCallableName; - $this->methodName = !empty($methodName) ? $methodName : 'class'; - } - if ($option === 'clone') { - $this->useDeepClone = \true; - } elseif ($option === 'shallowClone') { - $this->useShallowClone = \true; - } - } - public function __toString() : string - { - return $this->getTarget(); - } - public function isValid() : bool - { - // Invalid dependencies can be declared and are skipped by the runner - return $this->className !== '' && $this->methodName !== ''; - } - public function useShallowClone() : bool - { - return $this->useShallowClone; - } - public function useDeepClone() : bool - { - return $this->useDeepClone; - } - public function targetIsClass() : bool - { - return $this->methodName === 'class'; - } - public function getTarget() : string - { - return $this->isValid() ? $this->className . '::' . $this->methodName : ''; - } - public function getTargetClassName() : string + public function __construct() { - return $this->className; + parent::__construct('Proxying to original methods requires invoking the original constructor'); } } message = $message; - } - public function getMessage() : string - { - return $this->message; - } - /** - * Returns a string representation of the test case. - * - * @throws InvalidArgumentException - */ - public function toString() : string - { - return $this->getName(); - } - /** - * @throws Exception - */ - protected function runTest() : void - { - $this->markTestIncomplete($this->message); - } } __phpunit_originalObject = $originalObject; - } - /** @noinspection MagicMethodsValidityInspection */ - public function __phpunit_setReturnValueGeneration(bool $returnValueGeneration) : void - { - $this->__phpunit_returnValueGeneration = $returnValueGeneration; - } - /** @noinspection MagicMethodsValidityInspection */ - public function __phpunit_getInvocationHandler() : \PHPUnit\Framework\MockObject\InvocationHandler - { - if ($this->__phpunit_invocationMocker === null) { - $this->__phpunit_invocationMocker = new \PHPUnit\Framework\MockObject\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 + public function __construct(string $className) { - return $this->__phpunit_getInvocationHandler()->expects($matcher); + parent::__construct(sprintf('Class "%s" does not exist', $className)); } } expects(new AnyInvokedCount()); - return call_user_func_array([$expects, 'method'], func_get_args()); + parent::__construct(sprintf('Trait "%s" does not exist', $traitName)); } } \true, '__DIR__' => \true, '__FILE__' => \true, '__FUNCTION__' => \true, '__LINE__' => \true, '__METHOD__' => \true, '__NAMESPACE__' => \true, '__TRAIT__' => \true, '__clone' => \true, '__halt_compiler' => \true]; /** - * @var ConfigurableMethod[] + * @psalm-var array */ - private $configurableMethods; - public function __construct(InvocationHandler $handler, Matcher $matcher, ConfigurableMethod ...$configurableMethods) - { - $this->invocationHandler = $handler; - $this->matcher = $matcher; - $this->configurableMethods = $configurableMethods; - } + private static array $cache = []; /** - * @throws MatcherAlreadyRegisteredException + * Returns a test double for the specified class. * - * @return $this + * @throws ClassIsEnumerationException + * @throws ClassIsFinalException + * @throws ClassIsReadonlyException + * @throws DuplicateMethodException + * @throws InvalidMethodNameException + * @throws NameAlreadyInUseException + * @throws OriginalConstructorInvocationRequiredException + * @throws ReflectionException + * @throws RuntimeException + * @throws UnknownTypeException */ - public function id($id) : self + public function testDouble(string $type, bool $mockObject, ?array $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|Stub { - $this->invocationHandler->registerMatcher($id, $this->matcher); - return $this; + if ($type === Traversable::class) { + $type = Iterator::class; + } + if (!$allowMockingUnknownTypes) { + $this->ensureKnownType($type, $callAutoload); + } + $this->ensureValidMethods($methods); + $this->ensureNameForTestDoubleClassIsAvailable($mockClassName); + if (!$callOriginalConstructor && $callOriginalMethods) { + throw new \PHPUnit\Framework\MockObject\Generator\OriginalConstructorInvocationRequiredException(); + } + $mock = $this->generate($type, $mockObject, $methods, $mockClassName, $callOriginalClone, $callAutoload, $cloneArguments, $callOriginalMethods); + $object = $this->getObject($mock, $type, $callOriginalConstructor, $arguments, $callOriginalMethods, $proxyTarget, $returnValueGeneration); + assert($object instanceof $type); + if ($mockObject) { + assert($object instanceof MockObject); + } else { + assert($object instanceof Stub); + } + return $object; } /** - * @return $this + * @psalm-param list $interfaces + * + * @throws RuntimeException + * @throws UnknownTypeException */ - public function will(Stub $stub) : \PHPUnit\Framework\MockObject\Builder\Identity + public function testDoubleForInterfaceIntersection(array $interfaces, bool $mockObject, bool $callAutoload = \true): MockObject|Stub { - $this->matcher->setStub($stub); - return $this; + if (count($interfaces) < 2) { + throw new \PHPUnit\Framework\MockObject\Generator\RuntimeException('At least two interfaces must be specified'); + } + foreach ($interfaces as $interface) { + if (!interface_exists($interface, $callAutoload)) { + throw new \PHPUnit\Framework\MockObject\Generator\UnknownTypeException($interface); + } + } + sort($interfaces); + $methods = []; + foreach ($interfaces as $interface) { + $methods = array_merge($methods, $this->namesOfMethodsIn($interface)); + } + if (count(array_unique($methods)) < count($methods)) { + throw new \PHPUnit\Framework\MockObject\Generator\RuntimeException('Interfaces must not declare the same method'); + } + $unqualifiedNames = []; + foreach ($interfaces as $interface) { + $parts = explode('\\', $interface); + $unqualifiedNames[] = array_pop($parts); + } + sort($unqualifiedNames); + do { + $intersectionName = sprintf('Intersection_%s_%s', implode('_', $unqualifiedNames), substr(md5((string) mt_rand()), 0, 8)); + } while (interface_exists($intersectionName, \false)); + $template = $this->loadTemplate('intersection.tpl'); + $template->setVar(['intersection' => $intersectionName, 'interfaces' => implode(', ', $interfaces)]); + eval($template->render()); + return $this->testDouble($intersectionName, $mockObject); } /** - * @param mixed $value - * @param mixed[] $nextValues + * Returns a mock object for the specified abstract class with all abstract + * methods of the class mocked. * - * @throws IncompatibleReturnValueException + * Concrete methods to mock can be specified with the $mockedMethods parameter. + * + * @throws ClassIsEnumerationException + * @throws ClassIsFinalException + * @throws ClassIsReadonlyException + * @throws DuplicateMethodException + * @throws InvalidArgumentException + * @throws InvalidMethodNameException + * @throws NameAlreadyInUseException + * @throws OriginalConstructorInvocationRequiredException + * @throws ReflectionException + * @throws RuntimeException + * @throws UnknownClassException + * @throws UnknownTypeException + * + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/5241 */ - public function willReturn($value, ...$nextValues) : self + public function mockObjectForAbstractClass(string $originalClassName, array $arguments = [], string $mockClassName = '', bool $callOriginalConstructor = \true, bool $callOriginalClone = \true, bool $callAutoload = \true, ?array $mockedMethods = null, bool $cloneArguments = \true): MockObject { - if (count($nextValues) === 0) { - $this->ensureTypeOfReturnValues([$value]); - $stub = $value instanceof Stub ? $value : new ReturnStub($value); - } else { - $values = array_merge([$value], $nextValues); - $this->ensureTypeOfReturnValues($values); - $stub = new ConsecutiveCalls($values); - } - return $this->will($stub); - } - 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); - } - 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); + if (class_exists($originalClassName, $callAutoload) || interface_exists($originalClassName, $callAutoload)) { + $reflector = $this->reflectClass($originalClassName); + $methods = $mockedMethods; + foreach ($reflector->getMethods() as $method) { + if ($method->isAbstract() && !in_array($method->getName(), $methods ?? [], \true)) { + $methods[] = $method->getName(); + } + } + if (empty($methods)) { + $methods = null; + } + $mockObject = $this->testDouble($originalClassName, \true, $methods, $arguments, $mockClassName, $callOriginalConstructor, $callOriginalClone, $callAutoload, $cloneArguments); + assert($mockObject instanceof $originalClassName); + assert($mockObject instanceof MockObject); + return $mockObject; + } + throw new \PHPUnit\Framework\MockObject\Generator\UnknownClassException($originalClassName); } /** - * @return $this + * 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. + * + * @psalm-param trait-string $traitName + * + * @throws ClassIsEnumerationException + * @throws ClassIsFinalException + * @throws ClassIsReadonlyException + * @throws DuplicateMethodException + * @throws InvalidArgumentException + * @throws InvalidMethodNameException + * @throws NameAlreadyInUseException + * @throws OriginalConstructorInvocationRequiredException + * @throws ReflectionException + * @throws RuntimeException + * @throws UnknownClassException + * @throws UnknownTraitException + * @throws UnknownTypeException + * + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/5243 */ - public function after($id) : self + public function mockObjectForTrait(string $traitName, array $arguments = [], string $mockClassName = '', bool $callOriginalConstructor = \true, bool $callOriginalClone = \true, bool $callAutoload = \true, ?array $mockedMethods = null, bool $cloneArguments = \true): MockObject { - $this->matcher->setAfterMatchBuilderId($id); - return $this; + if (!trait_exists($traitName, $callAutoload)) { + throw new \PHPUnit\Framework\MockObject\Generator\UnknownTraitException($traitName); + } + $className = $this->generateClassName($traitName, '', 'Trait_'); + $classTemplate = $this->loadTemplate('trait_class.tpl'); + $classTemplate->setVar(['prologue' => 'abstract ', 'class_name' => $className['className'], 'trait_name' => $traitName]); + $mockTrait = new \PHPUnit\Framework\MockObject\Generator\MockTrait($classTemplate->render(), $className['className']); + $mockTrait->generate(); + return $this->mockObjectForAbstractClass($className['className'], $arguments, $mockClassName, $callOriginalConstructor, $callOriginalClone, $callAutoload, $mockedMethods, $cloneArguments); } /** - * @param mixed[] $arguments + * Returns an object for the specified trait. * - * @throws \PHPUnit\Framework\Exception - * @throws MethodNameNotConfiguredException - * @throws MethodParametersAlreadyConfiguredException + * @psalm-param trait-string $traitName * - * @return $this + * @throws ReflectionException + * @throws RuntimeException + * @throws UnknownTraitException + * + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/5244 */ - public function with(...$arguments) : self + public function objectForTrait(string $traitName, string $traitClassName = '', bool $callAutoload = \true, bool $callOriginalConstructor = \false, array $arguments = []): object { - $this->ensureParametersCanBeConfigured(); - $this->matcher->setParametersRule(new Rule\Parameters($arguments)); - return $this; + if (!trait_exists($traitName, $callAutoload)) { + throw new \PHPUnit\Framework\MockObject\Generator\UnknownTraitException($traitName); + } + $className = $this->generateClassName($traitName, $traitClassName, 'Trait_'); + $classTemplate = $this->loadTemplate('trait_class.tpl'); + $classTemplate->setVar(['prologue' => '', 'class_name' => $className['className'], 'trait_name' => $traitName]); + return $this->getObject(new \PHPUnit\Framework\MockObject\Generator\MockTrait($classTemplate->render(), $className['className']), '', $callOriginalConstructor, $arguments); } /** - * @param array ...$arguments + * @throws ClassIsEnumerationException + * @throws ClassIsFinalException + * @throws ClassIsReadonlyException + * @throws ReflectionException + * @throws RuntimeException * - * @throws \PHPUnit\Framework\Exception - * @throws MethodNameNotConfiguredException - * @throws MethodParametersAlreadyConfiguredException + * @todo This method is only public because it is used to test generated code in PHPT tests * - * @return $this + * @see https://github.com/sebastianbergmann/phpunit/issues/5476 + */ + public function generate(string $type, bool $mockObject, ?array $methods = null, string $mockClassName = '', bool $callOriginalClone = \true, bool $callAutoload = \true, bool $cloneArguments = \true, bool $callOriginalMethods = \false): \PHPUnit\Framework\MockObject\Generator\MockClass + { + if ($mockClassName !== '') { + return $this->generateCodeForTestDoubleClass($type, $mockObject, $methods, $mockClassName, $callOriginalClone, $callAutoload, $cloneArguments, $callOriginalMethods); + } + $key = md5($type . ($mockObject ? 'MockObject' : 'TestStub') . serialize($methods) . serialize($callOriginalClone) . serialize($cloneArguments) . serialize($callOriginalMethods)); + if (!isset(self::$cache[$key])) { + self::$cache[$key] = $this->generateCodeForTestDoubleClass($type, $mockObject, $methods, $mockClassName, $callOriginalClone, $callAutoload, $cloneArguments, $callOriginalMethods); + } + return self::$cache[$key]; + } + /** + * @throws RuntimeException + * @throws SoapExtensionNotAvailableException * - * @deprecated + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/5242 */ - public function withConsecutive(...$arguments) : self + public function generateClassFromWsdl(string $wsdlFile, string $className, array $methods = [], array $options = []): string { - $this->ensureParametersCanBeConfigured(); - $this->matcher->setParametersRule(new Rule\ConsecutiveParameters($arguments)); - return $this; + if (!extension_loaded('soap')) { + throw new \PHPUnit\Framework\MockObject\Generator\SoapExtensionNotAvailableException(); + } + $options['cache_wsdl'] = WSDL_CACHE_NONE; + try { + $client = new SoapClient($wsdlFile, $options); + $_methods = array_unique($client->__getFunctions()); + unset($client); + } catch (SoapFault $e) { + throw new \PHPUnit\Framework\MockObject\Generator\RuntimeException($e->getMessage(), $e->getCode(), $e); + } + sort($_methods); + $methodTemplate = $this->loadTemplate('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)) { + $arguments = explode(',', str_replace(')', '', substr($method, $nameEnd + 1))); + foreach (range(0, count($arguments) - 1) as $i) { + $parameterStart = strpos($arguments[$i], '$'); + if (!$parameterStart) { + continue; + } + $arguments[$i] = substr($arguments[$i], $parameterStart); + } + $methodTemplate->setVar(['method_name' => $name, 'arguments' => implode(', ', $arguments)]); + $methodsBuffer .= $methodTemplate->render(); + } + } + $optionsBuffer = '['; + foreach ($options as $key => $value) { + $optionsBuffer .= $key . ' => ' . $value; + } + $optionsBuffer .= ']'; + $classTemplate = $this->loadTemplate('wsdl_class.tpl'); + $namespace = ''; + if (str_contains($className, '\\')) { + $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 MethodNameNotConfiguredException - * @throws MethodParametersAlreadyConfiguredException + * @throws ReflectionException * - * @return $this + * @psalm-return list */ - public function withAnyParameters() : self + public function mockClassMethods(string $className, bool $callOriginalMethods, bool $cloneArguments): array { - $this->ensureParametersCanBeConfigured(); - $this->matcher->setParametersRule(new Rule\AnyParameters()); - return $this; + $class = $this->reflectClass($className); + $methods = []; + foreach ($class->getMethods() as $method) { + if (($method->isPublic() || $method->isAbstract()) && $this->canMethodBeDoubled($method)) { + $methods[] = \PHPUnit\Framework\MockObject\Generator\MockMethod::fromReflection($method, $callOriginalMethods, $cloneArguments); + } + } + return $methods; } /** - * @param Constraint|string $constraint + * @psalm-param class-string $interfaceName * - * @throws InvalidArgumentException - * @throws MethodCannotBeConfiguredException - * @throws MethodNameAlreadyConfiguredException + * @throws ReflectionException * - * @return $this + * @psalm-return list */ - public function method($constraint) : self + private function userDefinedInterfaceMethods(string $interfaceName): array { - if ($this->matcher->hasMethodNameRule()) { - throw new MethodNameAlreadyConfiguredException(); + $interface = $this->reflectClass($interfaceName); + $methods = []; + foreach ($interface->getMethods() as $method) { + if (!$method->isUserDefined()) { + continue; + } + $methods[] = $method; } - $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 MethodCannotBeConfiguredException($constraint); + return $methods; + } + /** + * @throws ReflectionException + * @throws RuntimeException + */ + private function getObject(\PHPUnit\Framework\MockObject\Generator\MockType $mockClass, string $type = '', bool $callOriginalConstructor = \false, array $arguments = [], bool $callOriginalMethods = \false, ?object $proxyTarget = null, bool $returnValueGeneration = \true): object + { + $className = $mockClass->generate(); + $object = $this->instantiate($className, $callOriginalConstructor, $arguments); + if ($callOriginalMethods) { + $this->instantiateProxyTarget($proxyTarget, $object, $type, $arguments); } - $this->matcher->setMethodNameRule(new Rule\MethodName($constraint)); - return $this; + if ($object instanceof StubInternal) { + $object->__phpunit_setReturnValueGeneration($returnValueGeneration); + } + return $object; } /** - * @throws MethodNameNotConfiguredException - * @throws MethodParametersAlreadyConfiguredException + * @throws ClassIsEnumerationException + * @throws ClassIsFinalException + * @throws ClassIsReadonlyException + * @throws ReflectionException + * @throws RuntimeException */ - private function ensureParametersCanBeConfigured() : void + private function generateCodeForTestDoubleClass(string $type, bool $mockObject, ?array $explicitMethods, string $mockClassName, bool $callOriginalClone, bool $callAutoload, bool $cloneArguments, bool $callOriginalMethods): \PHPUnit\Framework\MockObject\Generator\MockClass { - if (!$this->matcher->hasMethodNameRule()) { - throw new MethodNameNotConfiguredException(); + $classTemplate = $this->loadTemplate('test_double_class.tpl'); + $additionalInterfaces = []; + $doubledCloneMethod = \false; + $proxiedCloneMethod = \false; + $isClass = \false; + $isInterface = \false; + $class = null; + $mockMethods = new \PHPUnit\Framework\MockObject\Generator\MockMethodSet(); + $testDoubleClassPrefix = $mockObject ? 'MockObject_' : 'TestStub_'; + $_mockClassName = $this->generateClassName($type, $mockClassName, $testDoubleClassPrefix); + if (class_exists($_mockClassName['fullClassName'], $callAutoload)) { + $isClass = \true; + } elseif (interface_exists($_mockClassName['fullClassName'], $callAutoload)) { + $isInterface = \true; } - if ($this->matcher->hasParametersRule()) { - throw new MethodParametersAlreadyConfiguredException(); + 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}"; + } + $doubledCloneMethod = \true; + } else { + $class = $this->reflectClass($_mockClassName['fullClassName']); + if ($class->isEnum()) { + throw new \PHPUnit\Framework\MockObject\Generator\ClassIsEnumerationException($_mockClassName['fullClassName']); + } + if ($class->isFinal()) { + throw new \PHPUnit\Framework\MockObject\Generator\ClassIsFinalException($_mockClassName['fullClassName']); + } + if (method_exists($class, 'isReadOnly') && $class->isReadOnly()) { + throw new \PHPUnit\Framework\MockObject\Generator\ClassIsReadonlyException($_mockClassName['fullClassName']); + } + // @see https://github.com/sebastianbergmann/phpunit/issues/2995 + if ($isInterface && $class->implementsInterface(Throwable::class)) { + $actualClassName = Exception::class; + $additionalInterfaces[] = $class->getName(); + $isInterface = \false; + $class = $this->reflectClass($actualClassName); + foreach ($this->userDefinedInterfaceMethods($_mockClassName['fullClassName']) as $method) { + $methodName = $method->getName(); + if ($class->hasMethod($methodName)) { + $classMethod = $class->getMethod($methodName); + if (!$this->canMethodBeDoubled($classMethod)) { + continue; + } + } + $mockMethods->addMethods(\PHPUnit\Framework\MockObject\Generator\MockMethod::fromReflection($method, $callOriginalMethods, $cloneArguments)); + } + $_mockClassName = $this->generateClassName($actualClassName, $_mockClassName['className'], $testDoubleClassPrefix); + } + // @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')) { + $cloneMethod = $class->getMethod('__clone'); + if (!$cloneMethod->isFinal()) { + if ($callOriginalClone && !$isInterface) { + $proxiedCloneMethod = \true; + } else { + $doubledCloneMethod = \true; + } + } + } else { + $doubledCloneMethod = \true; + } + } + if ($isClass && $explicitMethods === []) { + $mockMethods->addMethods(...$this->mockClassMethods($_mockClassName['fullClassName'], $callOriginalMethods, $cloneArguments)); + } + if ($isInterface && ($explicitMethods === [] || $explicitMethods === null)) { + $mockMethods->addMethods(...$this->interfaceMethods($_mockClassName['fullClassName'], $cloneArguments)); + } + if (is_array($explicitMethods)) { + foreach ($explicitMethods as $methodName) { + if ($class !== null && $class->hasMethod($methodName)) { + $method = $class->getMethod($methodName); + if ($this->canMethodBeDoubled($method)) { + $mockMethods->addMethods(\PHPUnit\Framework\MockObject\Generator\MockMethod::fromReflection($method, $callOriginalMethods, $cloneArguments)); + } + } else { + $mockMethods->addMethods(\PHPUnit\Framework\MockObject\Generator\MockMethod::fromName($_mockClassName['fullClassName'], $methodName, $cloneArguments)); + } + } + } + $mockedMethods = ''; + $configurable = []; + foreach ($mockMethods->asArray() as $mockMethod) { + $mockedMethods .= $mockMethod->generateCode(); + $configurable[] = new ConfigurableMethod($mockMethod->methodName(), $mockMethod->defaultParameterValues(), $mockMethod->numberOfParameters(), $mockMethod->returnType()); + } + /** @psalm-var trait-string[] $traits */ + $traits = [StubApi::class]; + if ($mockObject) { + $traits[] = MockObjectApi::class; + } + if (!$mockMethods->hasMethod('method') && (!isset($class) || !$class->hasMethod('method'))) { + $traits[] = Method::class; + } + if ($doubledCloneMethod) { + $traits[] = DoubledCloneMethod::class; + } + if ($proxiedCloneMethod) { + $traits[] = ProxiedCloneMethod::class; + } + $useStatements = ''; + foreach ($traits as $trait) { + $useStatements .= sprintf(' use %s;' . PHP_EOL, $trait); } + unset($traits); + $classTemplate->setVar(['prologue' => $prologue ?? '', 'epilogue' => $epilogue ?? '', 'class_declaration' => $this->generateTestDoubleClassDeclaration($mockObject, $_mockClassName, $isInterface, $additionalInterfaces), 'use_statements' => $useStatements, 'mock_class_name' => $_mockClassName['className'], 'mocked_methods' => $mockedMethods]); + return new \PHPUnit\Framework\MockObject\Generator\MockClass($classTemplate->render(), $_mockClassName['className'], $configurable); } - private function getConfiguredMethod() : ?ConfigurableMethod + private function generateClassName(string $type, string $className, string $prefix): array { - $configuredMethod = null; - foreach ($this->configurableMethods as $configurableMethod) { - if ($this->matcher->getMethodNameRule()->matchesName($configurableMethod->getName())) { - if ($configuredMethod !== null) { - return null; + 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 generateTestDoubleClassDeclaration(bool $mockObject, array $mockClassName, bool $isInterface, array $additionalInterfaces = []): string + { + if ($mockObject) { + $additionalInterfaces[] = MockObjectInternal::class; + } else { + $additionalInterfaces[] = StubInternal::class; + } + $buffer = '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'] . '\\'; } - $configuredMethod = $configurableMethod; + $buffer .= $mockClassName['originalClassName']; } + } else { + $buffer .= sprintf('%s extends %s%s implements %s', $mockClassName['className'], (!empty($mockClassName['namespaceName'])) ? $mockClassName['namespaceName'] . '\\' : '', $mockClassName['originalClassName'], $interfaces); } - return $configuredMethod; + return $buffer; + } + private function canMethodBeDoubled(ReflectionMethod $method): bool + { + if ($method->isConstructor()) { + return \false; + } + if ($method->isDestructor()) { + return \false; + } + if ($method->isFinal()) { + return \false; + } + if ($method->isPrivate()) { + return \false; + } + return !$this->isMethodNameExcluded($method->getName()); + } + private function isMethodNameExcluded(string $name): bool + { + return isset(self::EXCLUDED_METHOD_NAMES[$name]); } /** - * @throws IncompatibleReturnValueException + * @throws UnknownTypeException */ - private function ensureTypeOfReturnValues(array $values) : void + private function ensureKnownType(string $type, bool $callAutoload): void { - $configuredMethod = $this->getConfiguredMethod(); - if ($configuredMethod === null) { + if (!class_exists($type, $callAutoload) && !interface_exists($type, $callAutoload)) { + throw new \PHPUnit\Framework\MockObject\Generator\UnknownTypeException($type); + } + } + /** + * @throws DuplicateMethodException + * @throws InvalidMethodNameException + */ + private function ensureValidMethods(?array $methods): void + { + if ($methods === null) { return; } - foreach ($values as $value) { - if (!$configuredMethod->mayReturn($value)) { - throw new IncompatibleReturnValueException($configuredMethod, $value); + foreach ($methods as $method) { + if (!preg_match('~[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*~', (string) $method)) { + throw new \PHPUnit\Framework\MockObject\Generator\InvalidMethodNameException((string) $method); } } + if ($methods !== array_unique($methods)) { + throw new \PHPUnit\Framework\MockObject\Generator\DuplicateMethodException($methods); + } + } + /** + * @throws NameAlreadyInUseException + * @throws ReflectionException + */ + private function ensureNameForTestDoubleClassIsAvailable(string $className): void + { + if ($className === '') { + return; + } + if (class_exists($className, \false) || interface_exists($className, \false) || trait_exists($className, \false)) { + throw new \PHPUnit\Framework\MockObject\Generator\NameAlreadyInUseException($className); + } } -} - - * - * 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; -use Throwable; -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -interface InvocationStubber -{ - public function will(Stub $stub) : \PHPUnit\Framework\MockObject\Builder\Identity; - /** @return self */ - public function willReturn($value, ...$nextValues); /** - * @param mixed $reference + * @psalm-param class-string $className * - * @return self + * @throws ReflectionException */ - public function willReturnReference(&$reference); + private function instantiate(string $className, bool $callOriginalConstructor, array $arguments): object + { + if ($callOriginalConstructor) { + if (count($arguments) === 0) { + return new $className(); + } + try { + return (new ReflectionClass($className))->newInstanceArgs($arguments); + // @codeCoverageIgnoreStart + } catch (\ReflectionException $e) { + throw new \PHPUnit\Framework\MockObject\Generator\ReflectionException($e->getMessage(), $e->getCode(), $e); + } + // @codeCoverageIgnoreEnd + } + try { + return (new ReflectionClass($className))->newInstanceWithoutConstructor(); + // @codeCoverageIgnoreStart + } catch (\ReflectionException $e) { + throw new \PHPUnit\Framework\MockObject\Generator\ReflectionException($e->getMessage(), $e->getCode(), $e); + // @codeCoverageIgnoreEnd + } + } /** - * @param array> $valueMap + * @psalm-param class-string $type * - * @return self + * @throws ReflectionException */ - public function willReturnMap(array $valueMap); + private function instantiateProxyTarget(?object $proxyTarget, object $object, string $type, array $arguments): void + { + if (!is_object($proxyTarget)) { + assert(class_exists($type)); + if (count($arguments) === 0) { + $proxyTarget = new $type(); + } else { + $class = new ReflectionClass($type); + try { + $proxyTarget = $class->newInstanceArgs($arguments); + // @codeCoverageIgnoreStart + } catch (\ReflectionException $e) { + throw new \PHPUnit\Framework\MockObject\Generator\ReflectionException($e->getMessage(), $e->getCode(), $e); + } + // @codeCoverageIgnoreEnd + } + } + $object->__phpunit_setOriginalObject($proxyTarget); + } /** - * @param int $argumentIndex + * @psalm-param class-string $className * - * @return self + * @throws ReflectionException */ - public function willReturnArgument($argumentIndex); + private function reflectClass(string $className): ReflectionClass + { + try { + $class = new ReflectionClass($className); + // @codeCoverageIgnoreStart + } catch (\ReflectionException $e) { + throw new \PHPUnit\Framework\MockObject\Generator\ReflectionException($e->getMessage(), $e->getCode(), $e); + } + // @codeCoverageIgnoreEnd + return $class; + } /** - * @param callable $callback + * @psalm-param class-string $classOrInterfaceName + * + * @psalm-return list * - * @return self + * @throws ReflectionException */ - public function willReturnCallback($callback); - /** @return self */ - public function willReturnSelf(); + private function namesOfMethodsIn(string $classOrInterfaceName): array + { + $class = $this->reflectClass($classOrInterfaceName); + $methods = []; + foreach ($class->getMethods() as $method) { + if ($method->isPublic() || $method->isAbstract()) { + $methods[] = $method->getName(); + } + } + return $methods; + } /** - * @param mixed $values + * @psalm-param class-string $interfaceName + * + * @psalm-return list * - * @return self + * @throws ReflectionException */ - public function willReturnOnConsecutiveCalls(...$values); - /** @return self */ - public function willThrowException(Throwable $exception); + private function interfaceMethods(string $interfaceName, bool $cloneArguments): array + { + $class = $this->reflectClass($interfaceName); + $methods = []; + foreach ($class->getMethods() as $method) { + $methods[] = \PHPUnit\Framework\MockObject\Generator\MockMethod::fromReflection($method, \false, $cloneArguments); + } + return $methods; + } } + */ + private readonly array $configurableMethods; + /** + * @psalm-param class-string $mockName + * @psalm-param list $configurableMethods + */ + public function __construct(string $classCode, string $mockName, array $configurableMethods) + { + $this->classCode = $classCode; + $this->mockName = $mockName; + $this->configurableMethods = $configurableMethods; + } + /** + * @psalm-return class-string */ - public function method($constraint); + 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 classCode(): string + { + return $this->classCode; + } } - * // match first parameter with value 2 - * $b->with(2); - * // match first parameter with value 'smock' and second identical to 42 - * $b->with('smock', new PHPUnit\Framework\Constraint\IsEqual(42)); - * - * - * @return ParametersMatch + * @psalm-var non-empty-string */ - public function with(...$arguments); + private readonly string $methodName; + private readonly bool $cloneArguments; + private readonly string $modifier; + private readonly string $argumentsForDeclaration; + private readonly string $argumentsForCall; + private readonly Type $returnType; + private readonly string $reference; + private readonly bool $callOriginalMethod; + private readonly bool $static; + private readonly ?string $deprecation; /** - * Sets a rule which allows any kind of parameters. - * - * Some examples: - * - * // match any number of parameters - * $b->withAnyParameters(); - * - * - * @return ParametersMatch + * @psalm-var array */ - public function withAnyParameters(); -} - - * - * 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 \PHPUnit\Framework\MockObject\Builder\Identity -{ + private readonly array $defaultParameterValues; /** - * Stubs the matching method with the stub object $stub. Any invocations of - * the matched method will now be handled by the stub instead. + * @psalm-var non-negative-int */ - public function will(BaseStub $stub) : \PHPUnit\Framework\MockObject\Builder\Identity; -} -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::methodParametersForDeclaration($method), self::methodParametersForCall($method), self::methodParametersDefaultValues($method), count($method->getParameters()), (new ReflectionMapper())->fromReturnType($method), $reference, $callOriginalMethod, $method->isStatic(), $deprecation); + } + /** + * @param class-string $className + * @param non-empty-string $methodName + */ + public static function fromName(string $className, string $methodName, bool $cloneArguments): self + { + return new self($className, $methodName, $cloneArguments, 'public', '', '', [], 0, new UnknownType(), '', \false, \false, null); + } + /** + * @psalm-param class-string $className + * @psalm-param non-empty-string $methodName + * @psalm-param array $defaultParameterValues + * @psalm-param non-negative-int $numberOfParameters + */ + private function __construct(string $className, string $methodName, bool $cloneArguments, string $modifier, string $argumentsForDeclaration, string $argumentsForCall, array $defaultParameterValues, int $numberOfParameters, Type $returnType, string $reference, bool $callOriginalMethod, bool $static, ?string $deprecation) + { + $this->className = $className; + $this->methodName = $methodName; + $this->cloneArguments = $cloneArguments; + $this->modifier = $modifier; + $this->argumentsForDeclaration = $argumentsForDeclaration; + $this->argumentsForCall = $argumentsForCall; + $this->defaultParameterValues = $defaultParameterValues; + $this->numberOfParameters = $numberOfParameters; + $this->returnType = $returnType; + $this->reference = $reference; + $this->callOriginalMethod = $callOriginalMethod; + $this->static = $static; + $this->deprecation = $deprecation; + } + /** + * @psalm-return non-empty-string + */ + public function methodName(): string + { + return $this->methodName; + } + /** + * @throws RuntimeException + */ + public function generateCode(): string + { + if ($this->static) { + $templateFile = 'doubled_static_method.tpl'; + } else { + $templateFile = sprintf('%s_method.tpl', $this->callOriginalMethod ? 'proxied' : 'doubled'); + } + $deprecation = $this->deprecation; + $returnResult = ''; + if (!$this->returnType->isNever() && !$this->returnType->isVoid()) { + $returnResult = <<<'EOT' -declare (strict_types=1); -/* - * This file is part of PHPUnit. - * - * (c) Sebastian Bergmann - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject; -use PHPUnitPHAR\SebastianBergmann\Type\Type; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class ConfigurableMethod -{ + return $__phpunit_result; +EOT; + } + if (null !== $this->deprecation) { + $deprecation = "The {$this->className}::{$this->methodName} method is deprecated ({$this->deprecation})."; + $deprecationTemplate = $this->loadTemplate('deprecation.tpl'); + $deprecationTemplate->setVar(['deprecation' => var_export($deprecation, \true)]); + $deprecation = $deprecationTemplate->render(); + } + $template = $this->loadTemplate($templateFile); + $template->setVar(['arguments_decl' => $this->argumentsForDeclaration, 'arguments_call' => $this->argumentsForCall, 'return_declaration' => (!empty($this->returnType->asString())) ? ': ' . $this->returnType->asString() : '', 'return_type' => $this->returnType->asString(), '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_result' => $returnResult]); + return $template->render(); + } + public function returnType(): Type + { + return $this->returnType; + } /** - * @var string + * @psalm-return array */ - private $name; + public function defaultParameterValues(): array + { + return $this->defaultParameterValues; + } /** - * @var Type + * @psalm-return non-negative-int */ - private $returnType; - public function __construct(string $name, Type $returnType) + public function numberOfParameters(): int { - $this->name = $name; - $this->returnType = $returnType; + return $this->numberOfParameters; } - public function getName() : string + /** + * Returns the parameters of a function or method. + * + * @throws RuntimeException + */ + private static function methodParametersForDeclaration(ReflectionMethod $method): string { - return $this->name; + $parameters = []; + $types = (new ReflectionMapper())->fromParameterTypes($method); + foreach ($method->getParameters() as $i => $parameter) { + $name = '$' . $parameter->getName(); + /* Note: PHP extensions may use empty names for reference arguments + * or "..." for methods taking a variable number of arguments. + */ + if ($name === '$' || $name === '$...') { + $name = '$arg' . $i; + } + $default = ''; + $reference = ''; + $typeDeclaration = ''; + if (!$types[$i]->type()->isUnknown()) { + $typeDeclaration = $types[$i]->type()->asString() . ' '; + } + if ($parameter->isPassedByReference()) { + $reference = '&'; + } + if ($parameter->isVariadic()) { + $name = '...' . $name; + } elseif ($parameter->isDefaultValueAvailable()) { + $default = ' = ' . self::exportDefaultValue($parameter); + } elseif ($parameter->isOptional()) { + $default = ' = null'; + } + $parameters[] = $typeDeclaration . $reference . $name . $default; + } + return implode(', ', $parameters); } - public function mayReturn($value) : bool + /** + * Returns the parameters of a function or method. + * + * @throws ReflectionException + */ + private static function methodParametersForCall(ReflectionMethod $method): string { - if ($value === null && $this->returnType->allowsNull()) { - return \true; + $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()) { + continue; + } + if ($parameter->isPassedByReference()) { + $parameters[] = '&' . $name; + } else { + $parameters[] = $name; + } } - return $this->returnType->isAssignable(Type::fromValue($value, \false)); + return implode(', ', $parameters); } - public function getReturnTypeDeclaration() : string + /** + * @throws ReflectionException + */ + private static function exportDefaultValue(ReflectionParameter $parameter): string { - return $this->returnType->asString(); + try { + $defaultValue = $parameter->getDefaultValue(); + if (!is_object($defaultValue)) { + return var_export($defaultValue, \true); + } + $parameterAsString = $parameter->__toString(); + return explode(' = ', substr(substr($parameterAsString, strpos($parameterAsString, ' ') + strlen(' ')), 0, -2))[1]; + // @codeCoverageIgnoreStart + } catch (\ReflectionException $e) { + throw new \PHPUnit\Framework\MockObject\Generator\ReflectionException($e->getMessage(), $e->getCode(), $e); + } + // @codeCoverageIgnoreEnd } -} - - * - * 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 \PHPUnit\Framework\MockObject\Exception -{ -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject; - -use function sprintf; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class CannotUseAddMethodsException extends \PHPUnit\Framework\Exception implements \PHPUnit\Framework\MockObject\Exception -{ - public function __construct(string $type, string $methodName) + /** + * @psalm-return array + */ + private static function methodParametersDefaultValues(ReflectionMethod $method): array { - parent::__construct(sprintf('Trying to configure method "%s" with addMethods(), but it exists in class "%s". Use onlyMethods() for methods that exist in the class', $methodName, $type)); + $result = []; + foreach ($method->getParameters() as $i => $parameter) { + if (!$parameter->isDefaultValueAvailable()) { + continue; + } + $result[$i] = $parameter->getDefaultValue(); + } + return $result; } } + */ + private array $methods = []; + public function addMethods(\PHPUnit\Framework\MockObject\Generator\MockMethod ...$methods): void { - parent::__construct(sprintf('Trying to configure method "%s" with onlyMethods(), but it does not exist in class "%s". Use addMethods() for methods that do not exist in the class', $methodName, $type)); + foreach ($methods as $method) { + $this->methods[strtolower($method->methodName())] = $method; + } } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject; - -use function sprintf; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class ClassAlreadyExistsException extends \PHPUnit\Framework\Exception implements \PHPUnit\Framework\MockObject\Exception -{ - public function __construct(string $className) + /** + * @psalm-return list + */ + public function asArray(): array { - parent::__construct(sprintf('Class "%s" already exists', $className)); + return array_values($this->methods); } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject; - -use function sprintf; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class ClassIsFinalException extends \PHPUnit\Framework\Exception implements \PHPUnit\Framework\MockObject\Exception -{ - public function __construct(string $className) + public function hasMethod(string $methodName): bool { - parent::__construct(sprintf('Class "%s" is declared "final" and cannot be doubled', $className)); + return array_key_exists(strtolower($methodName), $this->methods); } } classCode = $classCode; + $this->mockName = $mockName; + } + /** + * @psalm-return class-string + */ + public function generate(): string + { + if (!class_exists($this->mockName, \false)) { + eval($this->classCode); + } + return $this->mockName; } } $methods + * @psalm-var array */ - public function __construct(array $methods) + private static array $templates = []; + /** + * @psalm-suppress MissingThrowsDocblock + */ + private function loadTemplate(string $template): Template { - parent::__construct(sprintf('Cannot double using a method list that contains duplicates: "%s" (duplicate: "%s")', implode(', ', $methods), implode(', ', array_unique(array_diff_assoc($methods, array_unique($methods)))))); + $filename = __DIR__ . '/templates/' . $template; + if (!isset(self::$templates[$filename])) { + self::$templates[$filename] = new Template($filename); + } + return self::$templates[$filename]; } } - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject; + @trigger_error({deprecation}, E_USER_DEPRECATED); -use Throwable; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -interface Exception extends Throwable -{ -} - $__phpunit_definedVariableValue) { + if ((new ReflectionParameter([__CLASS__, __FUNCTION__], $__phpunit_definedVariableName))->isVariadic()) { + foreach ($__phpunit_definedVariableValue as $__phpunit_key => $__phpunit_namedValue) { + if (is_string($__phpunit_key)) { + $__phpunit_namedVariadicParameters[$__phpunit_key] = $__phpunit_namedValue; + } + } + } + } -declare (strict_types=1); -/* - * This file is part of PHPUnit. - * - * (c) Sebastian Bergmann - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject; + $__phpunit_arguments = [{arguments_call}]; + $__phpunit_count = func_num_args(); -use function get_class; -use function gettype; -use function is_object; -use function sprintf; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class IncompatibleReturnValueException extends \PHPUnit\Framework\Exception implements \PHPUnit\Framework\MockObject\Exception -{ - /** - * @param mixed $value - */ - public function __construct(\PHPUnit\Framework\MockObject\ConfigurableMethod $method, $value) - { - parent::__construct(sprintf('Method %s may not return value of type %s, its declared return type is "%s"', $method->getName(), is_object($value) ? get_class($value) : gettype($value), $method->getReturnTypeDeclaration())); + if ($__phpunit_count > {arguments_count}) { + $__phpunit_arguments_tmp = func_get_args(); + + for ($__phpunit_i = {arguments_count}; $__phpunit_i < $__phpunit_count; $__phpunit_i++) { + $__phpunit_arguments[] = $__phpunit_arguments_tmp[$__phpunit_i]; + } + } + + $__phpunit_arguments = array_merge($__phpunit_arguments, $__phpunit_namedVariadicParameters); + + $__phpunit_result = $this->__phpunit_getInvocationHandler()->invoke( + new \PHPUnit\Framework\MockObject\Invocation( + '{class_name}', '{method_name}', $__phpunit_arguments, '{return_type}', $this, {clone_arguments} + ) + );{return_result} } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject; + {modifier} function {reference}{method_name}({arguments_decl}){return_declaration} + { + throw new \PHPUnit\Framework\MockObject\BadMethodCallException('Static method "{method_name}" cannot be invoked on mock object'); + } +declare(strict_types=1); -use function sprintf; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class InvalidMethodNameException extends \PHPUnit\Framework\Exception implements \PHPUnit\Framework\MockObject\Exception +interface {intersection} extends {interfaces} { - public function __construct(string $method) +} + + {modifier} function {reference}{method_name}({arguments_decl}){return_declaration} { - parent::__construct(sprintf('Cannot double method with invalid name "%s"', $method)); + $__phpunit_definedVariables = get_defined_vars(); + $__phpunit_namedVariadicParameters = []; + + foreach ($__phpunit_definedVariables as $__phpunit_definedVariableName => $__phpunit_definedVariableValue) { + if ((new ReflectionParameter([__CLASS__, __FUNCTION__], $__phpunit_definedVariableName))->isVariadic()) { + foreach ($__phpunit_definedVariableValue as $__phpunit_key => $__phpunit_namedValue) { + if (is_string($__phpunit_key)) { + $__phpunit_namedVariadicParameters[$__phpunit_key] = $__phpunit_namedValue; + } + } + } + } + + $__phpunit_arguments = [{arguments_call}]; + $__phpunit_count = func_num_args(); + + if ($__phpunit_count > {arguments_count}) { + $__phpunit_arguments_tmp = func_get_args(); + + for ($__phpunit_i = {arguments_count}; $__phpunit_i < $__phpunit_count; $__phpunit_i++) { + $__phpunit_arguments[] = $__phpunit_arguments_tmp[$__phpunit_i]; + } + } + + $__phpunit_arguments = array_merge($__phpunit_arguments, $__phpunit_namedVariadicParameters); + + $this->__phpunit_getInvocationHandler()->invoke( + new \PHPUnit\Framework\MockObject\Invocation( + '{class_name}', '{method_name}', $__phpunit_arguments, '{return_type}', $this, {clone_arguments}, true + ) + ); + + $__phpunit_result = call_user_func_array([$this->__phpunit_originalObject, "{method_name}"], $__phpunit_arguments);{return_result} } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject; +{prologue}{class_declaration} +{ +{use_statements}{mocked_methods}}{epilogue} +declare(strict_types=1); -use function sprintf; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class MatchBuilderNotFoundException extends \PHPUnit\Framework\Exception implements \PHPUnit\Framework\MockObject\Exception +{prologue}class {class_name} { - public function __construct(string $id) + use {trait_name}; +} +declare(strict_types=1); + +{namespace}class {class_name} extends \SoapClient +{ + public function __construct($wsdl, array $options) + { + parent::__construct('{wsdl}', $options); + } +{methods}} + + public function {method_name}({arguments}) { - parent::__construct(sprintf('No builder found for match builder identification <%s>', $id)); } -} + */ + private array $methods = []; + private bool $emptyMethodsArray = \false; + /** + * @psalm-var ?class-string + */ + private ?string $mockClassName = null; + private array $constructorArgs = []; + private bool $originalConstructor = \true; + private bool $originalClone = \true; + private bool $autoload = \true; + private bool $cloneArguments = \false; + private bool $callOriginalMethods = \false; + private ?object $proxyTarget = null; + private bool $allowMockingUnknownTypes = \true; + private bool $returnValueGeneration = \true; + private readonly Generator $generator; + /** + * @psalm-param class-string|trait-string $type + */ + public function __construct(TestCase $testCase, string $type) { - parent::__construct(sprintf('Matcher with id <%s> is already registered', $id)); + $this->testCase = $testCase; + $this->type = $type; + $this->generator = new Generator(); } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject; - -use function sprintf; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class MethodCannotBeConfiguredException extends \PHPUnit\Framework\Exception implements \PHPUnit\Framework\MockObject\Exception -{ - public function __construct(string $method) + /** + * Creates a mock object using a fluent interface. + * + * @throws ClassIsEnumerationException + * @throws ClassIsFinalException + * @throws ClassIsReadonlyException + * @throws DuplicateMethodException + * @throws InvalidArgumentException + * @throws InvalidMethodNameException + * @throws NameAlreadyInUseException + * @throws OriginalConstructorInvocationRequiredException + * @throws ReflectionException + * @throws RuntimeException + * @throws UnknownTypeException + * + * @psalm-return MockObject&MockedType + */ + public function getMock(): \PHPUnit\Framework\MockObject\MockObject { - parent::__construct(sprintf('Trying to configure method "%s" which cannot be configured because it does not exist, has not been specified, is final, or is static', $method)); + $object = $this->generator->testDouble($this->type, \true, (!$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); + assert($object instanceof $this->type); + assert($object instanceof \PHPUnit\Framework\MockObject\MockObject); + $this->testCase->registerMockObject($object); + return $object; } -} - - * - * 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 MethodNameAlreadyConfiguredException extends \PHPUnit\Framework\Exception implements \PHPUnit\Framework\MockObject\Exception -{ - public function __construct() + /** + * Creates a mock object for an abstract class using a fluent interface. + * + * @psalm-return MockObject&MockedType + * + * @throws Exception + * @throws ReflectionException + * @throws RuntimeException + * + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/5305 + */ + public function getMockForAbstractClass(): \PHPUnit\Framework\MockObject\MockObject { - parent::__construct('Method name is already configured'); + $object = $this->generator->mockObjectForAbstractClass($this->type, $this->constructorArgs, $this->mockClassName ?? '', $this->originalConstructor, $this->originalClone, $this->autoload, $this->methods, $this->cloneArguments); + assert($object instanceof \PHPUnit\Framework\MockObject\MockObject); + $this->testCase->registerMockObject($object); + return $object; } -} - - * - * 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 MethodNameNotConfiguredException extends \PHPUnit\Framework\Exception implements \PHPUnit\Framework\MockObject\Exception -{ - public function __construct() + /** + * Creates a mock object for a trait using a fluent interface. + * + * @psalm-return MockObject&MockedType + * + * @throws Exception + * @throws ReflectionException + * @throws RuntimeException + * + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/5306 + */ + public function getMockForTrait(): \PHPUnit\Framework\MockObject\MockObject { - parent::__construct('Method name is not configured'); + assert(trait_exists($this->type)); + $object = $this->generator->mockObjectForTrait($this->type, $this->constructorArgs, $this->mockClassName ?? '', $this->originalConstructor, $this->originalClone, $this->autoload, $this->methods, $this->cloneArguments); + assert($object instanceof \PHPUnit\Framework\MockObject\MockObject); + $this->testCase->registerMockObject($object); + return $object; } -} - - * - * 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 MethodParametersAlreadyConfiguredException extends \PHPUnit\Framework\Exception implements \PHPUnit\Framework\MockObject\Exception -{ - public function __construct() + /** + * Specifies the subset of methods to mock, requiring each to exist in the class. + * + * @psalm-param list $methods + * + * @throws CannotUseOnlyMethodsException + * @throws ReflectionException + * + * @return $this + */ + public function onlyMethods(array $methods): self { - parent::__construct('Method parameters already configured'); + if (empty($methods)) { + $this->emptyMethodsArray = \true; + return $this; + } + try { + $reflector = new ReflectionClass($this->type); + // @codeCoverageIgnoreStart + } catch (\ReflectionException $e) { + throw new ReflectionException($e->getMessage(), $e->getCode(), $e); + // @codeCoverageIgnoreEnd + } + foreach ($methods as $method) { + if (!$reflector->hasMethod($method)) { + throw new \PHPUnit\Framework\MockObject\CannotUseOnlyMethodsException($this->type, $method); + } + } + $this->methods = array_merge($this->methods, $methods); + return $this; } -} - - * - * 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 OriginalConstructorInvocationRequiredException extends \PHPUnit\Framework\Exception implements \PHPUnit\Framework\MockObject\Exception -{ - public function __construct() + /** + * Specifies methods that don't exist in the class which you want to mock. + * + * @psalm-param list $methods + * + * @throws CannotUseAddMethodsException + * @throws ReflectionException + * @throws RuntimeException + * + * @return $this + * + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/5320 + */ + public function addMethods(array $methods): self { - parent::__construct('Proxying to original methods requires invoking the original constructor'); + if (empty($methods)) { + $this->emptyMethodsArray = \true; + return $this; + } + try { + $reflector = new ReflectionClass($this->type); + // @codeCoverageIgnoreStart + } catch (\ReflectionException $e) { + throw new ReflectionException($e->getMessage(), $e->getCode(), $e); + // @codeCoverageIgnoreEnd + } + foreach ($methods as $method) { + if ($reflector->hasMethod($method)) { + throw new CannotUseAddMethodsException($this->type, $method); + } + } + $this->methods = array_merge($this->methods, $methods); + return $this; + } + /** + * Specifies the arguments for the constructor. + * + * @return $this + */ + public function setConstructorArgs(array $arguments): self + { + $this->constructorArgs = $arguments; + return $this; + } + /** + * Specifies the name for the mock class. + * + * @psalm-param class-string $name + * + * @return $this + */ + public function setMockClassName(string $name): self + { + $this->mockClassName = $name; + return $this; + } + /** + * Disables the invocation of the original constructor. + * + * @return $this + */ + public function disableOriginalConstructor(): self + { + $this->originalConstructor = \false; + return $this; + } + /** + * Enables the invocation of the original constructor. + * + * @return $this + */ + public function enableOriginalConstructor(): self + { + $this->originalConstructor = \true; + return $this; + } + /** + * Disables the invocation of the original clone constructor. + * + * @return $this + */ + public function disableOriginalClone(): self + { + $this->originalClone = \false; + return $this; + } + /** + * Enables the invocation of the original clone constructor. + * + * @return $this + */ + public function enableOriginalClone(): self + { + $this->originalClone = \true; + return $this; + } + /** + * Disables the use of class autoloading while creating the mock object. + * + * @return $this + * + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/5309 + */ + public function disableAutoload(): self + { + $this->autoload = \false; + return $this; + } + /** + * Enables the use of class autoloading while creating the mock object. + * + * @return $this + * + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/5309 + */ + public function enableAutoload(): self + { + $this->autoload = \true; + return $this; + } + /** + * Disables the cloning of arguments passed to mocked methods. + * + * @return $this + * + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/5315 + */ + public function disableArgumentCloning(): self + { + $this->cloneArguments = \false; + return $this; + } + /** + * Enables the cloning of arguments passed to mocked methods. + * + * @return $this + * + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/5315 + */ + public function enableArgumentCloning(): self + { + $this->cloneArguments = \true; + return $this; + } + /** + * Enables the invocation of the original methods. + * + * @return $this + * + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/5307 + */ + public function enableProxyingToOriginalMethods(): self + { + $this->callOriginalMethods = \true; + return $this; + } + /** + * Disables the invocation of the original methods. + * + * @return $this + * + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/5307 + */ + public function disableProxyingToOriginalMethods(): self + { + $this->callOriginalMethods = \false; + $this->proxyTarget = null; + return $this; + } + /** + * Sets the proxy target. + * + * @return $this + * + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/5307 + */ + public function setProxyTarget(object $object): self + { + $this->proxyTarget = $object; + return $this; + } + /** + * @return $this + * + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/5308 + */ + public function allowMockingUnknownTypes(): self + { + $this->allowMockingUnknownTypes = \true; + return $this; + } + /** + * @return $this + * + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/5308 + */ + public function disallowMockingUnknownTypes(): self + { + $this->allowMockingUnknownTypes = \false; + return $this; + } + /** + * @return $this + */ + public function enableAutoReturnValueGeneration(): self + { + $this->returnValueGeneration = \true; + return $this; + } + /** + * @return $this + */ + public function disableAutoReturnValueGeneration(): self + { + $this->returnValueGeneration = \false; + return $this; } } - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject; - -use function sprintf; /** - * @internal This class is not covered by the backward compatibility promise for PHPUnit + * @internal This trait is not covered by the backward compatibility promise for PHPUnit */ -final class ReturnValueNotConfiguredException extends \PHPUnit\Framework\Exception implements \PHPUnit\Framework\MockObject\Exception +trait DoubledCloneMethod { - public function __construct(\PHPUnit\Framework\MockObject\Invocation $invocation) + public function __clone(): void { - parent::__construct(sprintf('Return value inference disabled and no expectation set up for %s::%s()', $invocation->getClassName(), $invocation->getMethodName())); + $this->__phpunit_invocationMocker = clone $this->__phpunit_getInvocationHandler(); } } expects(new AnyInvokedCount()); + return call_user_func_array([$expects, 'method'], func_get_args()); + } } __phpunit_getInvocationHandler()->hasMatchers(); + } + /** @noinspection MagicMethodsValidityInspection */ + public function __phpunit_setOriginalObject(object $originalObject): void + { + $this->__phpunit_originalObject = $originalObject; + } + /** @noinspection MagicMethodsValidityInspection */ + public function __phpunit_verify(bool $unsetInvocationMocker = \true): void + { + $this->__phpunit_getInvocationHandler()->verify(); + if ($unsetInvocationMocker) { + $this->__phpunit_unsetInvocationMocker(); + } + } + abstract public function __phpunit_getInvocationHandler(): \PHPUnit\Framework\MockObject\InvocationHandler; + abstract public function __phpunit_unsetInvocationMocker(): void; + public function expects(InvocationOrder $matcher): InvocationMockerBuilder + { + return $this->__phpunit_getInvocationHandler()->expects($matcher); } } __phpunit_invocationMocker = clone $this->__phpunit_getInvocationHandler(); + parent::__clone(); } } + */ + private static array $__phpunit_configurableMethods; + private bool $__phpunit_returnValueGeneration = \true; + private ?\PHPUnit\Framework\MockObject\InvocationHandler $__phpunit_invocationMocker = null; + /** @noinspection MagicMethodsValidityInspection */ + public static function __phpunit_initConfigurableMethods(\PHPUnit\Framework\MockObject\ConfigurableMethod ...$configurableMethods): void { - parent::__construct(sprintf('Trait "%s" does not exist', $traitName)); + static::$__phpunit_configurableMethods = $configurableMethods; + } + /** @noinspection MagicMethodsValidityInspection */ + public function __phpunit_setReturnValueGeneration(bool $returnValueGeneration): void + { + $this->__phpunit_returnValueGeneration = $returnValueGeneration; + } + /** @noinspection MagicMethodsValidityInspection */ + public function __phpunit_getInvocationHandler(): \PHPUnit\Framework\MockObject\InvocationHandler + { + if ($this->__phpunit_invocationMocker === null) { + $this->__phpunit_invocationMocker = new \PHPUnit\Framework\MockObject\InvocationHandler(static::$__phpunit_configurableMethods, $this->__phpunit_returnValueGeneration); + } + return $this->__phpunit_invocationMocker; + } + /** @noinspection MagicMethodsValidityInspection */ + public function __phpunit_unsetInvocationMocker(): void + { + $this->__phpunit_invocationMocker = null; } } __phpunit_invocationMocker = clone $this->__phpunit_getInvocationHandler(); - } -} -EOT; - private const MOCKED_CLONE_METHOD_WITHOUT_RETURN_TYPE_TRAIT = <<<'EOT' -namespace PHPUnit\Framework\MockObject; - -trait MockedCloneMethodWithoutReturnType -{ - public function __clone() - { - $this->__phpunit_invocationMocker = clone $this->__phpunit_getInvocationHandler(); - } -} -EOT; - private const UNMOCKED_CLONE_METHOD_WITH_VOID_RETURN_TYPE_TRAIT = <<<'EOT' -namespace PHPUnit\Framework\MockObject; - -trait UnmockedCloneMethodWithVoidReturnType -{ - public function __clone(): void - { - $this->__phpunit_invocationMocker = clone $this->__phpunit_getInvocationHandler(); - - parent::__clone(); - } -} -EOT; - private const UNMOCKED_CLONE_METHOD_WITHOUT_RETURN_TYPE_TRAIT = <<<'EOT' -namespace PHPUnit\Framework\MockObject; - -trait UnmockedCloneMethodWithoutReturnType +use PHPUnit\Framework\MockObject\ConfigurableMethod; +use PHPUnit\Framework\MockObject\IncompatibleReturnValueException; +use PHPUnit\Framework\MockObject\InvocationHandler; +use PHPUnit\Framework\MockObject\Matcher; +use PHPUnit\Framework\MockObject\MatcherAlreadyRegisteredException; +use PHPUnit\Framework\MockObject\MethodCannotBeConfiguredException; +use PHPUnit\Framework\MockObject\MethodNameAlreadyConfiguredException; +use PHPUnit\Framework\MockObject\MethodNameNotConfiguredException; +use PHPUnit\Framework\MockObject\MethodParametersAlreadyConfiguredException; +use PHPUnit\Framework\MockObject\Rule; +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; +use Throwable; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class InvocationMocker implements \PHPUnit\Framework\MockObject\Builder\InvocationStubber, \PHPUnit\Framework\MockObject\Builder\MethodNameMatch { - public function __clone() - { - $this->__phpunit_invocationMocker = clone $this->__phpunit_getInvocationHandler(); - - parent::__clone(); - } -} -EOT; - /** - * @var array - */ - private const EXCLUDED_METHOD_NAMES = ['__CLASS__' => \true, '__DIR__' => \true, '__FILE__' => \true, '__FUNCTION__' => \true, '__LINE__' => \true, '__METHOD__' => \true, '__NAMESPACE__' => \true, '__TRAIT__' => \true, '__clone' => \true, '__halt_compiler' => \true]; + private readonly InvocationHandler $invocationHandler; + private readonly Matcher $matcher; /** - * @var array + * @psalm-var list */ - private static $cache = []; + private readonly array $configurableMethods; /** - * @var Template[] + * @psalm-var ?array */ - private static $templates = []; + private ?array $configurableMethodNames = null; + public function __construct(InvocationHandler $handler, Matcher $matcher, ConfigurableMethod ...$configurableMethods) + { + $this->invocationHandler = $handler; + $this->matcher = $matcher; + $this->configurableMethods = $configurableMethods; + } /** - * Returns a mock object for the specified class. - * - * @param null|array $methods + * @throws MatcherAlreadyRegisteredException * - * @throws ClassAlreadyExistsException - * @throws ClassIsFinalException - * @throws ClassIsReadonlyException - * @throws DuplicateMethodException - * @throws InvalidArgumentException - * @throws InvalidMethodNameException - * @throws OriginalConstructorInvocationRequiredException - * @throws ReflectionException - * @throws RuntimeException - * @throws UnknownTypeException + * @return $this */ - public function getMock(string $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) : \PHPUnit\Framework\MockObject\MockObject + public function id(string $id): self { - if (!is_array($methods) && null !== $methods) { - throw InvalidArgumentException::create(2, 'array'); - } - if ($type === 'Traversable' || $type === '\\Traversable') { - $type = 'Iterator'; - } - if (!$allowMockingUnknownTypes && !class_exists($type, $callAutoload) && !interface_exists($type, $callAutoload)) { - throw new \PHPUnit\Framework\MockObject\UnknownTypeException($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 \PHPUnit\Framework\MockObject\InvalidMethodNameException((string) $method); - } - } - if ($methods !== array_unique($methods)) { - throw new \PHPUnit\Framework\MockObject\DuplicateMethodException($methods); - } - } - if ($mockClassName !== '' && class_exists($mockClassName, \false)) { - try { - $reflector = new ReflectionClass($mockClassName); - // @codeCoverageIgnoreStart - } catch (\ReflectionException $e) { - throw new \PHPUnit\Framework\MockObject\ReflectionException($e->getMessage(), $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd - if (!$reflector->implementsInterface(\PHPUnit\Framework\MockObject\MockObject::class)) { - throw new \PHPUnit\Framework\MockObject\ClassAlreadyExistsException($mockClassName); - } - } - if (!$callOriginalConstructor && $callOriginalMethods) { - throw new \PHPUnit\Framework\MockObject\OriginalConstructorInvocationRequiredException(); - } - $mock = $this->generate($type, $methods, $mockClassName, $callOriginalClone, $callAutoload, $cloneArguments, $callOriginalMethods); - return $this->getObject($mock, $type, $callOriginalConstructor, $callAutoload, $arguments, $callOriginalMethods, $proxyTarget, $returnValueGeneration); + $this->invocationHandler->registerMatcher($id, $this->matcher); + return $this; } /** - * @psalm-param list $interfaces - * - * @throws RuntimeException - * @throws UnknownTypeException + * @return $this */ - public function getMockForInterfaces(array $interfaces, bool $callAutoload = \true) : \PHPUnit\Framework\MockObject\MockObject + public function will(Stub $stub): \PHPUnit\Framework\MockObject\Builder\Identity { - if (count($interfaces) < 2) { - throw new \PHPUnit\Framework\MockObject\RuntimeException('At least two interfaces must be specified'); - } - foreach ($interfaces as $interface) { - if (!interface_exists($interface, $callAutoload)) { - throw new \PHPUnit\Framework\MockObject\UnknownTypeException($interface); - } - } - sort($interfaces); - $methods = []; - foreach ($interfaces as $interface) { - $methods = array_merge($methods, $this->getClassMethods($interface)); - } - if (count(array_unique($methods)) < count($methods)) { - throw new \PHPUnit\Framework\MockObject\RuntimeException('Interfaces must not declare the same method'); - } - $unqualifiedNames = []; - foreach ($interfaces as $interface) { - $parts = explode('\\', $interface); - $unqualifiedNames[] = array_pop($parts); - } - sort($unqualifiedNames); - do { - $intersectionName = sprintf('Intersection_%s_%s', implode('_', $unqualifiedNames), substr(md5((string) mt_rand()), 0, 8)); - } while (interface_exists($intersectionName, \false)); - $template = $this->getTemplate('intersection.tpl'); - $template->setVar(['intersection' => $intersectionName, 'interfaces' => implode(', ', $interfaces)]); - eval($template->render()); - return $this->getMock($intersectionName); + $this->matcher->setStub($stub); + return $this; } /** - * 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. - * - * @psalm-template RealInstanceType of object - * - * @psalm-param class-string $originalClassName - * - * @psalm-return MockObject&RealInstanceType - * - * @throws ClassAlreadyExistsException - * @throws ClassIsFinalException - * @throws ClassIsReadonlyException - * @throws DuplicateMethodException - * @throws InvalidArgumentException - * @throws InvalidMethodNameException - * @throws OriginalConstructorInvocationRequiredException - * @throws ReflectionException - * @throws RuntimeException - * @throws UnknownClassException - * @throws UnknownTypeException + * @throws IncompatibleReturnValueException */ - public function getMockForAbstractClass(string $originalClassName, array $arguments = [], string $mockClassName = '', bool $callOriginalConstructor = \true, bool $callOriginalClone = \true, bool $callAutoload = \true, ?array $mockedMethods = null, bool $cloneArguments = \true) : \PHPUnit\Framework\MockObject\MockObject + public function willReturn(mixed $value, mixed ...$nextValues): self { - if (class_exists($originalClassName, $callAutoload) || interface_exists($originalClassName, $callAutoload)) { - try { - $reflector = new ReflectionClass($originalClassName); - // @codeCoverageIgnoreStart - } catch (\ReflectionException $e) { - throw new \PHPUnit\Framework\MockObject\ReflectionException($e->getMessage(), $e->getCode(), $e); + if (count($nextValues) === 0) { + $this->ensureTypeOfReturnValues([$value]); + $stub = ($value instanceof Stub) ? $value : new ReturnStub($value); + return $this->will($stub); + } + $values = array_merge([$value], $nextValues); + $this->ensureTypeOfReturnValues($values); + $stub = new ConsecutiveCalls($values); + return $this->will($stub); + } + public function willReturnReference(mixed &$reference): self + { + $stub = new ReturnReference($reference); + return $this->will($stub); + } + public function willReturnMap(array $valueMap): self + { + $method = $this->configuredMethod(); + assert($method instanceof ConfigurableMethod); + $numberOfParameters = $method->numberOfParameters(); + $defaultValues = $method->defaultParameterValues(); + $hasDefaultValues = !empty($defaultValues); + $_valueMap = []; + foreach ($valueMap as $mapping) { + $numberOfConfiguredParameters = count($mapping) - 1; + if ($numberOfConfiguredParameters === $numberOfParameters || !$hasDefaultValues) { + $_valueMap[] = $mapping; + continue; } - // @codeCoverageIgnoreEnd - $methods = $mockedMethods; - foreach ($reflector->getMethods() as $method) { - if ($method->isAbstract() && !in_array($method->getName(), $methods ?? [], \true)) { - $methods[] = $method->getName(); + $_mapping = []; + $returnValue = array_pop($mapping); + foreach (range(0, $numberOfParameters - 1) as $i) { + if (isset($mapping[$i])) { + $_mapping[] = $mapping[$i]; + continue; + } + if (isset($defaultValues[$i])) { + $_mapping[] = $defaultValues[$i]; } } - if (empty($methods)) { - $methods = null; - } - return $this->getMock($originalClassName, $methods, $arguments, $mockClassName, $callOriginalConstructor, $callOriginalClone, $callAutoload, $cloneArguments); + $_mapping[] = $returnValue; + $_valueMap[] = $_mapping; } - throw new \PHPUnit\Framework\MockObject\UnknownClassException($originalClassName); + $stub = new ReturnValueMap($_valueMap); + return $this->will($stub); } - /** - * 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. - * - * @psalm-param trait-string $traitName - * - * @throws ClassAlreadyExistsException - * @throws ClassIsFinalException - * @throws ClassIsReadonlyException - * @throws DuplicateMethodException - * @throws InvalidArgumentException - * @throws InvalidMethodNameException - * @throws OriginalConstructorInvocationRequiredException - * @throws ReflectionException - * @throws RuntimeException - * @throws UnknownClassException - * @throws UnknownTraitException - * @throws UnknownTypeException - */ - public function getMockForTrait(string $traitName, array $arguments = [], string $mockClassName = '', bool $callOriginalConstructor = \true, bool $callOriginalClone = \true, bool $callAutoload = \true, ?array $mockedMethods = null, bool $cloneArguments = \true) : \PHPUnit\Framework\MockObject\MockObject + public function willReturnArgument(int $argumentIndex): self { - if (!trait_exists($traitName, $callAutoload)) { - throw new \PHPUnit\Framework\MockObject\UnknownTraitException($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 \PHPUnit\Framework\MockObject\MockTrait($classTemplate->render(), $className['className']); - $mockTrait->generate(); - return $this->getMockForAbstractClass($className['className'], $arguments, $mockClassName, $callOriginalConstructor, $callOriginalClone, $callAutoload, $mockedMethods, $cloneArguments); + $stub = new ReturnArgument($argumentIndex); + return $this->will($stub); } - /** - * Returns an object for the specified trait. - * - * @psalm-param trait-string $traitName - * - * @throws ReflectionException - * @throws RuntimeException - * @throws UnknownTraitException - */ - public function getObjectForTrait(string $traitName, string $traitClassName = '', bool $callAutoload = \true, bool $callOriginalConstructor = \false, array $arguments = []) : object + public function willReturnCallback(callable $callback): self { - if (!trait_exists($traitName, $callAutoload)) { - throw new \PHPUnit\Framework\MockObject\UnknownTraitException($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 \PHPUnit\Framework\MockObject\MockTrait($classTemplate->render(), $className['className']), '', $callOriginalConstructor, $callAutoload, $arguments); + $stub = new ReturnCallback($callback); + return $this->will($stub); } - /** - * @throws ClassIsFinalException - * @throws ClassIsReadonlyException - * @throws ReflectionException - * @throws RuntimeException - */ - public function generate(string $type, ?array $methods = null, string $mockClassName = '', bool $callOriginalClone = \true, bool $callAutoload = \true, bool $cloneArguments = \true, bool $callOriginalMethods = \false) : \PHPUnit\Framework\MockObject\MockClass + public function willReturnSelf(): self { - if ($mockClassName !== '') { - return $this->generateMock($type, $methods, $mockClassName, $callOriginalClone, $callAutoload, $cloneArguments, $callOriginalMethods); - } - $key = md5($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]; + $stub = new ReturnSelf(); + return $this->will($stub); } - /** - * @throws RuntimeException - * @throws SoapExtensionNotAvailableException - */ - public function generateClassFromWsdl(string $wsdlFile, string $className, array $methods = [], array $options = []) : string + public function willReturnOnConsecutiveCalls(mixed ...$values): self { - if (!extension_loaded('soap')) { - throw new \PHPUnit\Framework\MockObject\SoapExtensionNotAvailableException(); - } - $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 \PHPUnit\Framework\MockObject\RuntimeException($e->getMessage(), $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) { - $parameterStart = strpos($args[$i], '$'); - if (!$parameterStart) { - continue; - } - $args[$i] = substr($args[$i], $parameterStart); - } - $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(); + $stub = new ConsecutiveCalls($values); + return $this->will($stub); + } + public function willThrowException(Throwable $exception): self + { + $stub = new Exception($exception); + return $this->will($stub); } /** - * @throws ReflectionException - * - * @return string[] + * @return $this */ - public function getClassMethods(string $className) : array + public function after(string $id): self { - try { - $class = new ReflectionClass($className); - // @codeCoverageIgnoreStart - } catch (\ReflectionException $e) { - throw new \PHPUnit\Framework\MockObject\ReflectionException($e->getMessage(), $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd - $methods = []; - foreach ($class->getMethods() as $method) { - if ($method->isPublic() || $method->isAbstract()) { - $methods[] = $method->getName(); - } - } - return $methods; + $this->matcher->setAfterMatchBuilderId($id); + return $this; } /** - * @throws ReflectionException + * @throws \PHPUnit\Framework\Exception + * @throws MethodNameNotConfiguredException + * @throws MethodParametersAlreadyConfiguredException * - * @return MockMethod[] + * @return $this */ - public function mockClassMethods(string $className, bool $callOriginalMethods, bool $cloneArguments) : array + public function with(mixed ...$arguments): self { - try { - $class = new ReflectionClass($className); - // @codeCoverageIgnoreStart - } catch (\ReflectionException $e) { - throw new \PHPUnit\Framework\MockObject\ReflectionException($e->getMessage(), $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd - $methods = []; - foreach ($class->getMethods() as $method) { - if (($method->isPublic() || $method->isAbstract()) && $this->canMockMethod($method)) { - $methods[] = \PHPUnit\Framework\MockObject\MockMethod::fromReflection($method, $callOriginalMethods, $cloneArguments); - } - } - return $methods; + $this->ensureParametersCanBeConfigured(); + $this->matcher->setParametersRule(new Rule\Parameters($arguments)); + return $this; } /** - * @throws ReflectionException + * @throws MethodNameNotConfiguredException + * @throws MethodParametersAlreadyConfiguredException * - * @return MockMethod[] + * @return $this */ - public function mockInterfaceMethods(string $interfaceName, bool $cloneArguments) : array + public function withAnyParameters(): self { - try { - $class = new ReflectionClass($interfaceName); - // @codeCoverageIgnoreStart - } catch (\ReflectionException $e) { - throw new \PHPUnit\Framework\MockObject\ReflectionException($e->getMessage(), $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd - $methods = []; - foreach ($class->getMethods() as $method) { - $methods[] = \PHPUnit\Framework\MockObject\MockMethod::fromReflection($method, \false, $cloneArguments); - } - return $methods; + $this->ensureParametersCanBeConfigured(); + $this->matcher->setParametersRule(new Rule\AnyParameters()); + return $this; } /** - * @psalm-param class-string $interfaceName - * - * @throws ReflectionException + * @throws InvalidArgumentException + * @throws MethodCannotBeConfiguredException + * @throws MethodNameAlreadyConfiguredException * - * @return ReflectionMethod[] + * @return $this */ - private function userDefinedInterfaceMethods(string $interfaceName) : array + public function method(Constraint|string $constraint): self { - try { - // @codeCoverageIgnoreStart - $interface = new ReflectionClass($interfaceName); - } catch (\ReflectionException $e) { - throw new \PHPUnit\Framework\MockObject\ReflectionException($e->getMessage(), $e->getCode(), $e); + if ($this->matcher->hasMethodNameRule()) { + throw new MethodNameAlreadyConfiguredException(); } - // @codeCoverageIgnoreEnd - $methods = []; - foreach ($interface->getMethods() as $method) { - if (!$method->isUserDefined()) { - continue; + if (is_string($constraint)) { + $this->configurableMethodNames ??= array_flip(array_map(static fn(ConfigurableMethod $configurable) => strtolower($configurable->name()), $this->configurableMethods)); + if (!array_key_exists(strtolower($constraint), $this->configurableMethodNames)) { + throw new MethodCannotBeConfiguredException($constraint); } - $methods[] = $method; } - return $methods; + $this->matcher->setMethodNameRule(new Rule\MethodName($constraint)); + return $this; } /** - * @throws ReflectionException - * @throws RuntimeException + * @throws MethodNameNotConfiguredException + * @throws MethodParametersAlreadyConfiguredException */ - private function getObject(\PHPUnit\Framework\MockObject\MockType $mockClass, $type = '', bool $callOriginalConstructor = \false, bool $callAutoload = \false, array $arguments = [], bool $callOriginalMethods = \false, ?object $proxyTarget = null, bool $returnValueGeneration = \true) + private function ensureParametersCanBeConfigured(): void { - $className = $mockClass->generate(); - if ($callOriginalConstructor) { - if (count($arguments) === 0) { - $object = new $className(); - } else { - try { - $class = new ReflectionClass($className); - // @codeCoverageIgnoreStart - } catch (\ReflectionException $e) { - throw new \PHPUnit\Framework\MockObject\ReflectionException($e->getMessage(), $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd - $object = $class->newInstanceArgs($arguments); - } - } else { - try { - $object = (new Instantiator())->instantiate($className); - } catch (InstantiatorException $e) { - throw new \PHPUnit\Framework\MockObject\RuntimeException($e->getMessage()); - } - } - if ($callOriginalMethods) { - if (!is_object($proxyTarget)) { - if (count($arguments) === 0) { - $proxyTarget = new $type(); - } else { - try { - $class = new ReflectionClass($type); - // @codeCoverageIgnoreStart - } catch (\ReflectionException $e) { - throw new \PHPUnit\Framework\MockObject\ReflectionException($e->getMessage(), $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd - $proxyTarget = $class->newInstanceArgs($arguments); - } - } - $object->__phpunit_setOriginalObject($proxyTarget); + if (!$this->matcher->hasMethodNameRule()) { + throw new MethodNameNotConfiguredException(); } - if ($object instanceof \PHPUnit\Framework\MockObject\MockObject) { - $object->__phpunit_setReturnValueGeneration($returnValueGeneration); + if ($this->matcher->hasParametersRule()) { + throw new MethodParametersAlreadyConfiguredException(); } - return $object; } - /** - * @throws ClassIsFinalException - * @throws ClassIsReadonlyException - * @throws ReflectionException - * @throws RuntimeException - */ - private function generateMock(string $type, ?array $explicitMethods, string $mockClassName, bool $callOriginalClone, bool $callAutoload, bool $cloneArguments, bool $callOriginalMethods) : \PHPUnit\Framework\MockObject\MockClass + private function configuredMethod(): ?ConfigurableMethod { - $classTemplate = $this->getTemplate('mocked_class.tpl'); - $additionalInterfaces = []; - $mockedCloneMethod = \false; - $unmockedCloneMethod = \false; - $isClass = \false; - $isInterface = \false; - $class = null; - $mockMethods = new \PHPUnit\Framework\MockObject\MockMethodSet(); - $_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']); - // @codeCoverageIgnoreStart - } catch (\ReflectionException $e) { - throw new \PHPUnit\Framework\MockObject\ReflectionException($e->getMessage(), $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd - if ($class->isFinal()) { - throw new \PHPUnit\Framework\MockObject\ClassIsFinalException($_mockClassName['fullClassName']); - } - if (method_exists($class, 'isReadOnly') && $class->isReadOnly()) { - throw new \PHPUnit\Framework\MockObject\ClassIsReadonlyException($_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); - // @codeCoverageIgnoreStart - } catch (\ReflectionException $e) { - throw new \PHPUnit\Framework\MockObject\ReflectionException($e->getMessage(), $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd - foreach ($this->userDefinedInterfaceMethods($_mockClassName['fullClassName']) as $method) { - $methodName = $method->getName(); - if ($class->hasMethod($methodName)) { - try { - $classMethod = $class->getMethod($methodName); - // @codeCoverageIgnoreStart - } catch (\ReflectionException $e) { - throw new \PHPUnit\Framework\MockObject\ReflectionException($e->getMessage(), $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd - if (!$this->canMockMethod($classMethod)) { - continue; - } - } - $mockMethods->addMethods(\PHPUnit\Framework\MockObject\MockMethod::fromReflection($method, $callOriginalMethods, $cloneArguments)); - } - $_mockClassName = $this->generateClassName($actualClassName, $_mockClassName['className'], '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'); - // @codeCoverageIgnoreStart - } catch (\ReflectionException $e) { - throw new \PHPUnit\Framework\MockObject\ReflectionException($e->getMessage(), $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd - if (!$cloneMethod->isFinal()) { - if ($callOriginalClone && !$isInterface) { - $unmockedCloneMethod = \true; - } else { - $mockedCloneMethod = \true; - } - } - } else { - $mockedCloneMethod = \true; - } - } - if ($isClass && $explicitMethods === []) { - $mockMethods->addMethods(...$this->mockClassMethods($_mockClassName['fullClassName'], $callOriginalMethods, $cloneArguments)); - } - if ($isInterface && ($explicitMethods === [] || $explicitMethods === null)) { - $mockMethods->addMethods(...$this->mockInterfaceMethods($_mockClassName['fullClassName'], $cloneArguments)); - } - if (is_array($explicitMethods)) { - foreach ($explicitMethods as $methodName) { - if ($class !== null && $class->hasMethod($methodName)) { - try { - $method = $class->getMethod($methodName); - // @codeCoverageIgnoreStart - } catch (\ReflectionException $e) { - throw new \PHPUnit\Framework\MockObject\ReflectionException($e->getMessage(), $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd - if ($this->canMockMethod($method)) { - $mockMethods->addMethods(\PHPUnit\Framework\MockObject\MockMethod::fromReflection($method, $callOriginalMethods, $cloneArguments)); - } - } else { - $mockMethods->addMethods(\PHPUnit\Framework\MockObject\MockMethod::fromName($_mockClassName['fullClassName'], $methodName, $cloneArguments)); + $configuredMethod = null; + foreach ($this->configurableMethods as $configurableMethod) { + if ($this->matcher->methodNameRule()->matchesName($configurableMethod->name())) { + if ($configuredMethod !== null) { + return null; } + $configuredMethod = $configurableMethod; } } - $mockedMethods = ''; - $configurable = []; - foreach ($mockMethods->asArray() as $mockMethod) { - $mockedMethods .= $mockMethod->generateCode(); - $configurable[] = new \PHPUnit\Framework\MockObject\ConfigurableMethod($mockMethod->getName(), $mockMethod->getReturnType()); - } - $method = ''; - if (!$mockMethods->hasMethod('method') && (!isset($class) || !$class->hasMethod('method'))) { - $method = PHP_EOL . ' use \\PHPUnit\\Framework\\MockObject\\Method;'; - } - $cloneTrait = ''; - if ($mockedCloneMethod) { - $cloneTrait = $this->mockedCloneMethod(); - } - if ($unmockedCloneMethod) { - $cloneTrait = $this->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' => $method]); - return new \PHPUnit\Framework\MockObject\MockClass($classTemplate->render(), $_mockClassName['className'], $configurable); + return $configuredMethod; } - private function generateClassName(string $type, string $className, string $prefix) : array + /** + * @throws IncompatibleReturnValueException + */ + private function ensureTypeOfReturnValues(array $values): void { - 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; + $configuredMethod = $this->configuredMethod(); + if ($configuredMethod === null) { + return; } - if ($className === '') { - do { - $className = $prefix . $type . '_' . substr(md5((string) mt_rand()), 0, 8); - } while (class_exists($className, \false)); + foreach ($values as $value) { + if (!$configuredMethod->mayReturn($value)) { + throw new IncompatibleReturnValueException($configuredMethod, $value); + } } - return ['className' => $className, 'originalClassName' => $type, 'fullClassName' => $fullClassName, 'namespaceName' => $namespaceName]; - } - private function generateMockClassDeclaration(array $mockClassName, bool $isInterface, array $additionalInterfaces = []) : string - { - $buffer = 'class '; - $additionalInterfaces[] = \PHPUnit\Framework\MockObject\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 !($this->isConstructor($method) || $method->isFinal() || $method->isPrivate() || $this->isMethodNameExcluded($method->getName())); - } - private function isMethodNameExcluded(string $name) : bool - { - return isset(self::EXCLUDED_METHOD_NAMES[$name]); - } - /** - * @throws RuntimeException - */ - private function getTemplate(string $template) : Template - { - $filename = __DIR__ . DIRECTORY_SEPARATOR . 'Generator' . DIRECTORY_SEPARATOR . $template; - if (!isset(self::$templates[$filename])) { - try { - self::$templates[$filename] = new Template($filename); - } catch (TemplateException $e) { - throw new \PHPUnit\Framework\MockObject\RuntimeException($e->getMessage(), $e->getCode(), $e); - } - } - return self::$templates[$filename]; - } - /** - * @see https://github.com/sebastianbergmann/phpunit/issues/4139#issuecomment-605409765 - */ - private function isConstructor(ReflectionMethod $method) : bool - { - $methodName = strtolower($method->getName()); - if ($methodName === '__construct') { - return \true; - } - if (PHP_MAJOR_VERSION >= 8) { - return \false; - } - $className = strtolower($method->getDeclaringClass()->getName()); - return $methodName === $className; - } - private function mockedCloneMethod() : string - { - if (PHP_MAJOR_VERSION >= 8) { - if (!trait_exists('\\PHPUnit\\Framework\\MockObject\\MockedCloneMethodWithVoidReturnType')) { - eval(self::MOCKED_CLONE_METHOD_WITH_VOID_RETURN_TYPE_TRAIT); - } - return PHP_EOL . ' use \\PHPUnit\\Framework\\MockObject\\MockedCloneMethodWithVoidReturnType;'; - } - if (!trait_exists('\\PHPUnit\\Framework\\MockObject\\MockedCloneMethodWithoutReturnType')) { - eval(self::MOCKED_CLONE_METHOD_WITHOUT_RETURN_TYPE_TRAIT); - } - return PHP_EOL . ' use \\PHPUnit\\Framework\\MockObject\\MockedCloneMethodWithoutReturnType;'; - } - private function unmockedCloneMethod() : string - { - if (PHP_MAJOR_VERSION >= 8) { - if (!trait_exists('\\PHPUnit\\Framework\\MockObject\\UnmockedCloneMethodWithVoidReturnType')) { - eval(self::UNMOCKED_CLONE_METHOD_WITH_VOID_RETURN_TYPE_TRAIT); - } - return PHP_EOL . ' use \\PHPUnit\\Framework\\MockObject\\UnmockedCloneMethodWithVoidReturnType;'; - } - if (!trait_exists('\\PHPUnit\\Framework\\MockObject\\UnmockedCloneMethodWithoutReturnType')) { - eval(self::UNMOCKED_CLONE_METHOD_WITHOUT_RETURN_TYPE_TRAIT); - } - return PHP_EOL . ' use \\PHPUnit\\Framework\\MockObject\\UnmockedCloneMethodWithoutReturnType;'; } } + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject\Builder; -interface {intersection} extends {interfaces} +use PHPUnit\Framework\MockObject\Stub\Stub; +use Throwable; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +interface InvocationStubber { + public function will(Stub $stub): \PHPUnit\Framework\MockObject\Builder\Identity; + public function willReturn(mixed $value, mixed ...$nextValues): self; + public function willReturnReference(mixed &$reference): self; + /** + * @psalm-param array> $valueMap + */ + public function willReturnMap(array $valueMap): self; + public function willReturnArgument(int $argumentIndex): self; + public function willReturnCallback(callable $callback): self; + public function willReturnSelf(): self; + public function willReturnOnConsecutiveCalls(mixed ...$values): self; + public function willThrowException(Throwable $exception): self; } -declare(strict_types=1); - -{prologue}{class_declaration} -{ - use \PHPUnit\Framework\MockObject\Api;{method}{clone} -{mocked_methods}}{epilogue} - - {modifier} function {reference}{method_name}({arguments_decl}){return_declaration} - {{deprecation} - $__phpunit_arguments = [{arguments_call}]; - $__phpunit_count = func_num_args(); - - if ($__phpunit_count > {arguments_count}) { - $__phpunit_arguments_tmp = func_get_args(); - - for ($__phpunit_i = {arguments_count}; $__phpunit_i < $__phpunit_count; $__phpunit_i++) { - $__phpunit_arguments[] = $__phpunit_arguments_tmp[$__phpunit_i]; - } - } - - $__phpunit_result = $this->__phpunit_getInvocationHandler()->invoke( - new \PHPUnit\Framework\MockObject\Invocation( - '{class_name}', '{method_name}', $__phpunit_arguments, '{return_type}', $this, {clone_arguments} - ) - ); - - return $__phpunit_result; - } - - {modifier} function {reference}{method_name}({arguments_decl}){return_declaration} - {{deprecation} - $__phpunit_arguments = [{arguments_call}]; - $__phpunit_count = func_num_args(); - - if ($__phpunit_count > {arguments_count}) { - $__phpunit_arguments_tmp = func_get_args(); - - for ($__phpunit_i = {arguments_count}; $__phpunit_i < $__phpunit_count; $__phpunit_i++) { - $__phpunit_arguments[] = $__phpunit_arguments_tmp[$__phpunit_i]; - } - } - - $this->__phpunit_getInvocationHandler()->invoke( - new \PHPUnit\Framework\MockObject\Invocation( - '{class_name}', '{method_name}', $__phpunit_arguments, '{return_type}', $this, {clone_arguments} - ) - ); - } + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject\Builder; - {modifier} function {reference}{method_name}({arguments_decl}){return_declaration} - { - $__phpunit_arguments = [{arguments_call}]; - $__phpunit_count = func_num_args(); +use PHPUnit\Framework\Constraint\Constraint; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +interface MethodNameMatch extends \PHPUnit\Framework\MockObject\Builder\ParametersMatch +{ + /** + * Adds a new method name match and returns the parameter match object for + * further matching possibilities. + */ + public function method(Constraint|string $constraint): self; +} + {arguments_count}) { - $__phpunit_arguments_tmp = func_get_args(); +declare (strict_types=1); +/* + * This file is part of PHPUnit. + * + * (c) Sebastian Bergmann + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject\Builder; - for ($__phpunit_i = {arguments_count}; $__phpunit_i < $__phpunit_count; $__phpunit_i++) { - $__phpunit_arguments[] = $__phpunit_arguments_tmp[$__phpunit_i]; - } - } +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +interface ParametersMatch extends \PHPUnit\Framework\MockObject\Builder\Stub +{ + /** + * Defines the expectation which must occur before the current is valid. + */ + public function after(string $id): \PHPUnit\Framework\MockObject\Builder\Stub; + /** + * 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: + * + * // 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)); + * + */ + public function with(mixed ...$arguments): self; + /** + * Sets a rule which allows any kind of parameters. + * + * Some examples: + * + * // match any number of parameters + * $b->withAnyParameters(); + * + */ + public function withAnyParameters(): self; +} +__phpunit_getInvocationHandler()->invoke( - new \PHPUnit\Framework\MockObject\Invocation( - '{class_name}', '{method_name}', $__phpunit_arguments, '{return_type}', $this, {clone_arguments}, true - ) - ); +declare (strict_types=1); +/* + * This file is part of PHPUnit. + * + * (c) Sebastian Bergmann + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject\Builder; - return call_user_func_array(array($this->__phpunit_originalObject, "{method_name}"), $__phpunit_arguments); - } +use PHPUnit\Framework\MockObject\Stub\Stub as BaseStub; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +interface Stub extends \PHPUnit\Framework\MockObject\Builder\Identity +{ + /** + * Stubs the matching method with the stub object $stub. Any invocations of + * the matched method will now be handled by the stub instead. + */ + public function will(BaseStub $stub): \PHPUnit\Framework\MockObject\Builder\Identity; +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject; - if ($__phpunit_count > {arguments_count}) { - $__phpunit_arguments_tmp = func_get_args(); +use PHPUnit\Framework\MockObject\Builder\InvocationMocker; +use PHPUnit\Framework\MockObject\Rule\InvocationOrder; +/** + * @method InvocationMocker method($constraint) + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +interface MockObject extends \PHPUnit\Framework\MockObject\Stub +{ + public function expects(InvocationOrder $invocationRule): InvocationMocker; +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject; - $this->__phpunit_getInvocationHandler()->invoke( - new \PHPUnit\Framework\MockObject\Invocation( - '{class_name}', '{method_name}', $__phpunit_arguments, '{return_type}', $this, {clone_arguments}, true - ) - ); +/** + * @internal This interface is not covered by the backward compatibility promise for PHPUnit + */ +interface MockObjectInternal extends \PHPUnit\Framework\MockObject\MockObject, \PHPUnit\Framework\MockObject\StubInternal +{ + public function __phpunit_hasMatchers(): bool; + public function __phpunit_setOriginalObject(object $originalObject): void; + public function __phpunit_verify(bool $unsetInvocationMocker = \true): void; +} +__phpunit_originalObject, "{method_name}"), $__phpunit_arguments); - } -declare(strict_types=1); +declare (strict_types=1); +/* + * This file is part of PHPUnit. + * + * (c) Sebastian Bergmann + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject; -{prologue}class {class_name} +use PHPUnit\Framework\MockObject\Builder\InvocationStubber; +/** + * @method InvocationStubber method($constraint) + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +interface Stub { - use {trait_name}; } -declare(strict_types=1); + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject; - public function {method_name}({arguments}) - { - } +/** + * @internal This interface is not covered by the backward compatibility promise for PHPUnit + */ +interface StubInternal extends \PHPUnit\Framework\MockObject\Stub +{ + public static function __phpunit_initConfigurableMethods(\PHPUnit\Framework\MockObject\ConfigurableMethod ...$configurableMethods): void; + public function __phpunit_getInvocationHandler(): \PHPUnit\Framework\MockObject\InvocationHandler; + public function __phpunit_setReturnValueGeneration(bool $returnValueGeneration): void; + public function __phpunit_unsetInvocationMocker(): void; +} className = $className; $this->methodName = $methodName; - $this->parameters = $parameters; $this->object = $object; $this->proxiedCall = $proxiedCall; if (strtolower($methodName) === '__tostring') { $returnType = 'string'; } - if (strpos($returnType, '?') === 0) { + if (str_starts_with($returnType, '?')) { $returnType = substr($returnType, 1); $this->isReturnTypeNullable = \true; + } else { + $this->isReturnTypeNullable = \false; } $this->returnType = $returnType; if (!$cloneObjects) { + $this->parameters = $parameters; return; } - foreach ($this->parameters as $key => $value) { + foreach ($parameters as $key => $value) { if (is_object($value)) { - $this->parameters[$key] = Cloner::clone($value); + $parameters[$key] = Cloner::clone($value); } } + $this->parameters = $parameters; } - public function getClassName() : string + /** + * @psalm-return class-string + */ + public function className(): string { return $this->className; } - public function getMethodName() : string + /** + * @psalm-return non-empty-string + */ + public function methodName(): string { return $this->methodName; } - public function getParameters() : array + public function parameters(): array { return $this->parameters; } /** - * @throws RuntimeException - * - * @return mixed Mocked return value + * @throws Exception */ - public function generateReturnValue() + public function generateReturnValue(): mixed { + if ($this->returnType === 'never') { + throw new \PHPUnit\Framework\MockObject\NeverReturningMethodException($this->className, $this->methodName); + } if ($this->isReturnTypeNullable || $this->proxiedCall) { return null; } - $intersection = \false; - $union = \false; - $unionContainsIntersections = \false; - if (strpos($this->returnType, '|') !== \false) { - $types = explode('|', $this->returnType); - $union = \true; - if (strpos($this->returnType, '(') !== \false) { - $unionContainsIntersections = \true; - } - } elseif (strpos($this->returnType, '&') !== \false) { - $types = explode('&', $this->returnType); - $intersection = \true; - } else { - $types = [$this->returnType]; - } - $types = array_map('strtolower', $types); - if (!$intersection && !$unionContainsIntersections) { - if (in_array('', $types, \true) || in_array('null', $types, \true) || in_array('mixed', $types, \true) || in_array('void', $types, \true)) { - return null; - } - if (in_array('true', $types, \true)) { - return \true; - } - if (in_array('false', $types, \true) || in_array('bool', $types, \true)) { - return \false; - } - if (in_array('float', $types, \true)) { - return 0.0; - } - if (in_array('int', $types, \true)) { - return 0; - } - if (in_array('string', $types, \true)) { - return ''; - } - if (in_array('array', $types, \true)) { - return []; - } - if (in_array('static', $types, \true)) { - try { - return (new Instantiator())->instantiate(get_class($this->object)); - } catch (Throwable $t) { - throw new \PHPUnit\Framework\MockObject\RuntimeException($t->getMessage(), (int) $t->getCode(), $t); - } - } - if (in_array('object', $types, \true)) { - return new stdClass(); - } - if (in_array('callable', $types, \true) || in_array('closure', $types, \true)) { - return static function () : void { - }; - } - if (in_array('traversable', $types, \true) || in_array('generator', $types, \true) || in_array('iterable', $types, \true)) { - $generator = static function () : \Generator { - yield from []; - }; - return $generator(); - } - if (!$union) { - try { - return (new \PHPUnit\Framework\MockObject\Generator())->getMock($this->returnType, [], [], '', \false); - } catch (Throwable $t) { - if ($t instanceof \PHPUnit\Framework\MockObject\Exception) { - throw $t; - } - throw new \PHPUnit\Framework\MockObject\RuntimeException($t->getMessage(), (int) $t->getCode(), $t); - } - } - } - if ($intersection && $this->onlyInterfaces($types)) { - try { - return (new \PHPUnit\Framework\MockObject\Generator())->getMockForInterfaces($types); - } catch (Throwable $t) { - throw new \PHPUnit\Framework\MockObject\RuntimeException(sprintf('Return value for %s::%s() cannot be generated: %s', $this->className, $this->methodName, $t->getMessage()), (int) $t->getCode()); - } - } - $reason = ''; - if ($union) { - $reason = ' because the declared return type is a union'; - } elseif ($intersection) { - $reason = ' because the declared return type is an intersection'; - } - throw new \PHPUnit\Framework\MockObject\RuntimeException(sprintf('Return value for %s::%s() cannot be generated%s, please configure a return value for this method', $this->className, $this->methodName, $reason)); + return (new \PHPUnit\Framework\MockObject\ReturnValueGenerator())->generate($this->className, $this->methodName, $this->object::class, $this->returnType); } - public function toString() : string + 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 + public function object(): \PHPUnit\Framework\MockObject\MockObjectInternal|\PHPUnit\Framework\MockObject\StubInternal { return $this->object; } - /** - * @psalm-param non-empty-list $types - */ - private function onlyInterfaces(array $types) : bool - { - foreach ($types as $type) { - if (!interface_exists($type)) { - return \false; - } - } - return \true; - } } */ - private $matcherMap = []; + private array $matchers = []; /** - * @var ConfigurableMethod[] + * @psalm-var array */ - private $configurableMethods; + private array $matcherMap = []; /** - * @var bool + * @psalm-var list */ - private $returnValueGeneration; + private readonly array $configurableMethods; + private readonly bool $returnValueGeneration; /** - * @var Throwable + * @psalm-param list $configurableMethods */ - private $deferredError; public function __construct(array $configurableMethods, bool $returnValueGeneration) { $this->configurableMethods = $configurableMethods; $this->returnValueGeneration = $returnValueGeneration; } - public function hasMatchers() : bool + public function hasMatchers(): bool { foreach ($this->matchers as $matcher) { if ($matcher->hasMatchers()) { @@ -69134,43 +60416,35 @@ final class InvocationHandler } /** * 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) : ?\PHPUnit\Framework\MockObject\Matcher + public function lookupMatcher(string $id): ?\PHPUnit\Framework\MockObject\Matcher { - if (isset($this->matcherMap[$id])) { - return $this->matcherMap[$id]; - } - return null; + return $this->matcherMap[$id] ?? 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 MatcherAlreadyRegisteredException */ - public function registerMatcher(string $id, \PHPUnit\Framework\MockObject\Matcher $matcher) : void + public function registerMatcher(string $id, \PHPUnit\Framework\MockObject\Matcher $matcher): void { if (isset($this->matcherMap[$id])) { throw new \PHPUnit\Framework\MockObject\MatcherAlreadyRegisteredException($id); } $this->matcherMap[$id] = $matcher; } - public function expects(InvocationOrder $rule) : InvocationMocker + public function expects(InvocationOrder $rule): InvocationMocker { $matcher = new \PHPUnit\Framework\MockObject\Matcher($rule); $this->addMatcher($matcher); return new InvocationMocker($this, $matcher, ...$this->configurableMethods); } /** + * @throws \PHPUnit\Framework\MockObject\Exception * @throws Exception - * @throws RuntimeException */ - public function invoke(\PHPUnit\Framework\MockObject\Invocation $invocation) + public function invoke(\PHPUnit\Framework\MockObject\Invocation $invocation): mixed { $exception = null; $hasReturnValue = \false; @@ -69195,37 +60469,23 @@ final class InvocationHandler return $returnValue; } if (!$this->returnValueGeneration) { - $exception = new \PHPUnit\Framework\MockObject\ReturnValueNotConfiguredException($invocation); - if (strtolower($invocation->getMethodName()) === '__tostring') { - $this->deferredError = $exception; + if (strtolower($invocation->methodName()) === '__tostring') { return ''; } - throw $exception; + throw new \PHPUnit\Framework\MockObject\ReturnValueNotConfiguredException($invocation); } return $invocation->generateReturnValue(); } - public function matches(\PHPUnit\Framework\MockObject\Invocation $invocation) : bool - { - foreach ($this->matchers as $matcher) { - if (!$matcher->matches($invocation)) { - return \false; - } - } - return \true; - } /** * @throws Throwable */ - public function verify() : void + public function verify(): void { foreach ($this->matchers as $matcher) { $matcher->verify(); } - if ($this->deferredError) { - throw $this->deferredError; - } } - private function addMatcher(\PHPUnit\Framework\MockObject\Matcher $matcher) : void + private function addMatcher(\PHPUnit\Framework\MockObject\Matcher $matcher): void { $this->matchers[] = $matcher; } @@ -69243,8 +60503,6 @@ declare (strict_types=1); */ namespace PHPUnit\Framework\MockObject; -use function assert; -use function implode; use function sprintf; use PHPUnit\Framework\ExpectationFailedException; use PHPUnit\Framework\MockObject\Rule\AnyInvokedCount; @@ -69255,99 +60513,74 @@ 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; -use PHPUnitPHAR\SebastianBergmann\RecursionContext\InvalidArgumentException; +use PHPUnit\Util\ThrowableToStringMapper; /** * @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; + private readonly InvocationOrder $invocationRule; + private ?string $afterMatchBuilderId = null; + private ?MethodName $methodNameRule = null; + private ?ParametersRule $parametersRule = null; + private ?Stub $stub = null; public function __construct(InvocationOrder $rule) { $this->invocationRule = $rule; } - public function hasMatchers() : bool + public function hasMatchers(): bool { return !$this->invocationRule instanceof AnyInvokedCount; } - public function hasMethodNameRule() : bool + public function hasMethodNameRule(): bool { return $this->methodNameRule !== null; } - public function getMethodNameRule() : MethodName + public function methodNameRule(): MethodName { return $this->methodNameRule; } - public function setMethodNameRule(MethodName $rule) : void + public function setMethodNameRule(MethodName $rule): void { $this->methodNameRule = $rule; } - public function hasParametersRule() : bool + public function hasParametersRule(): bool { return $this->parametersRule !== null; } - public function setParametersRule(ParametersRule $rule) : void + public function setParametersRule(ParametersRule $rule): void { $this->parametersRule = $rule; } - public function setStub(Stub $stub) : void + public function setStub(Stub $stub): void { $this->stub = $stub; } - public function setAfterMatchBuilderId(string $id) : void + public function setAfterMatchBuilderId(string $id): void { $this->afterMatchBuilderId = $id; } /** + * @throws Exception * @throws ExpectationFailedException * @throws MatchBuilderNotFoundException * @throws MethodNameNotConfiguredException * @throws RuntimeException */ - public function invoked(\PHPUnit\Framework\MockObject\Invocation $invocation) + public function invoked(\PHPUnit\Framework\MockObject\Invocation $invocation): mixed { if ($this->methodNameRule === null) { throw new \PHPUnit\Framework\MockObject\MethodNameNotConfiguredException(); } if ($this->afterMatchBuilderId !== null) { - $matcher = $invocation->getObject()->__phpunit_getInvocationHandler()->lookupMatcher($this->afterMatchBuilderId); + $matcher = $invocation->object()->__phpunit_getInvocationHandler()->lookupMatcher($this->afterMatchBuilderId); if (!$matcher) { throw new \PHPUnit\Framework\MockObject\MatchBuilderNotFoundException($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); - } + $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()); } @@ -69358,19 +60591,17 @@ final class Matcher } /** * @throws ExpectationFailedException - * @throws InvalidArgumentException * @throws MatchBuilderNotFoundException * @throws MethodNameNotConfiguredException * @throws RuntimeException */ - public function matches(\PHPUnit\Framework\MockObject\Invocation $invocation) : bool + public function matches(\PHPUnit\Framework\MockObject\Invocation $invocation): bool { if ($this->afterMatchBuilderId !== null) { - $matcher = $invocation->getObject()->__phpunit_getInvocationHandler()->lookupMatcher($this->afterMatchBuilderId); + $matcher = $invocation->object()->__phpunit_getInvocationHandler()->lookupMatcher($this->afterMatchBuilderId); if (!$matcher) { throw new \PHPUnit\Framework\MockObject\MatchBuilderNotFoundException($this->afterMatchBuilderId); } - assert($matcher instanceof self); if (!$matcher->invocationRule->hasBeenInvoked()) { return \false; } @@ -69392,10 +60623,9 @@ final class Matcher } /** * @throws ExpectationFailedException - * @throws InvalidArgumentException * @throws MethodNameNotConfiguredException */ - public function verify() : void + public function verify(): void { if ($this->methodNameRule === null) { throw new \PHPUnit\Framework\MockObject\MethodNameNotConfiguredException(); @@ -69412,28 +60642,8 @@ final class Matcher $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(); + throw new ExpectationFailedException(sprintf("Expectation failed for %s when %s.\n%s", $this->methodNameRule->toString(), $this->invocationRule->toString(), ThrowableToStringMapper::map($e))); } - 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); } } methodName = $methodName; } - public function toString() : string + public function toString(): string { return sprintf('is "%s"', $this->methodName); } - protected function matches($other) : bool + protected function matches(mixed $other): bool { - if (!is_string($other)) { - return \false; - } - return strtolower($this->methodName) === strtolower($other); + return strtolower($this->methodName) === strtolower((string) $other); } } |string|string[] $type - */ - public function __construct(TestCase $testCase, $type) - { - $this->testCase = $testCase; - $this->type = $type; - $this->generator = new \PHPUnit\Framework\MockObject\Generator(); - } - /** - * Creates a mock object using a fluent interface. - * - * @throws ClassAlreadyExistsException - * @throws ClassIsFinalException - * @throws ClassIsReadonlyException - * @throws DuplicateMethodException - * @throws InvalidArgumentException - * @throws InvalidMethodNameException - * @throws OriginalConstructorInvocationRequiredException - * @throws ReflectionException - * @throws RuntimeException - * @throws UnknownTypeException - * - * @psalm-return MockObject&MockedType - */ - public function getMock() : \PHPUnit\Framework\MockObject\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. - * - * @psalm-return MockObject&MockedType - * - * @throws Exception - * @throws ReflectionException - * @throws RuntimeException - */ - public function getMockForAbstractClass() : \PHPUnit\Framework\MockObject\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. - * - * @psalm-return MockObject&MockedType - * - * @throws Exception - * @throws ReflectionException - * @throws RuntimeException - */ - public function getMockForTrait() : \PHPUnit\Framework\MockObject\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 - * - * @return $this - */ - public function setMethods(?array $methods = null) : self + public function generate(string $className, string $methodName, string $stubClassName, string $returnType): mixed { - if ($methods === null) { - $this->methods = $methods; + $intersection = \false; + $union = \false; + if (str_contains($returnType, '|')) { + $types = explode('|', $returnType); + $union = \true; + foreach (array_keys($types) as $key) { + if (str_starts_with($types[$key], '(') && str_ends_with($types[$key], ')')) { + $types[$key] = substr($types[$key], 1, -1); + } + } + } elseif (str_contains($returnType, '&')) { + $types = explode('&', $returnType); + $intersection = \true; } else { - $this->methods = array_merge($this->methods ?? [], $methods); + $types = [$returnType]; } - return $this; + if (!$intersection) { + $lowerTypes = array_map('strtolower', $types); + if (in_array('', $lowerTypes, \true) || in_array('null', $lowerTypes, \true) || in_array('mixed', $lowerTypes, \true) || in_array('void', $lowerTypes, \true)) { + return null; + } + if (in_array('true', $lowerTypes, \true)) { + return \true; + } + if (in_array('false', $lowerTypes, \true) || in_array('bool', $lowerTypes, \true)) { + return \false; + } + if (in_array('float', $lowerTypes, \true)) { + return 0.0; + } + if (in_array('int', $lowerTypes, \true)) { + return 0; + } + if (in_array('string', $lowerTypes, \true)) { + return ''; + } + if (in_array('array', $lowerTypes, \true)) { + return []; + } + if (in_array('static', $lowerTypes, \true)) { + return $this->newInstanceOf($stubClassName, $className, $methodName); + } + if (in_array('object', $lowerTypes, \true)) { + return new stdClass(); + } + if (in_array('callable', $lowerTypes, \true) || in_array('closure', $lowerTypes, \true)) { + return static function (): void { + }; + } + if (in_array('traversable', $lowerTypes, \true) || in_array('generator', $lowerTypes, \true) || in_array('iterable', $lowerTypes, \true)) { + $generator = static function (): \Generator { + yield from []; + }; + return $generator(); + } + if (!$union) { + return $this->testDoubleFor($returnType, $className, $methodName); + } + } + if ($union) { + foreach ($types as $type) { + if (str_contains($type, '&')) { + $_types = explode('&', $type); + if ($this->onlyInterfaces($_types)) { + return $this->testDoubleForIntersectionOfInterfaces($_types, $className, $methodName); + } + } + } + } + if ($intersection && $this->onlyInterfaces($types)) { + return $this->testDoubleForIntersectionOfInterfaces($types, $className, $methodName); + } + $reason = ''; + if ($union) { + $reason = ' because the declared return type is a union'; + } elseif ($intersection) { + $reason = ' because the declared return type is an intersection'; + } + throw new \PHPUnit\Framework\MockObject\RuntimeException(sprintf('Return value for %s::%s() cannot be generated%s, please configure a return value for this method', $className, $methodName, $reason)); } /** - * Specifies the subset of methods to mock, requiring each to exist in the class. - * - * @param string[] $methods - * - * @throws CannotUseOnlyMethodsException - * @throws ReflectionException - * - * @return $this + * @psalm-param non-empty-list $types */ - public function onlyMethods(array $methods) : self + private function onlyInterfaces(array $types): bool { - if (empty($methods)) { - $this->emptyMethodsArray = \true; - return $this; - } - try { - $reflector = new ReflectionClass($this->type); - // @codeCoverageIgnoreStart - } catch (\ReflectionException $e) { - throw new \PHPUnit\Framework\MockObject\ReflectionException($e->getMessage(), $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd - foreach ($methods as $method) { - if (!$reflector->hasMethod($method)) { - throw new \PHPUnit\Framework\MockObject\CannotUseOnlyMethodsException($this->type, $method); + foreach ($types as $type) { + if (!interface_exists($type)) { + return \false; } } - $this->methods = array_merge($this->methods ?? [], $methods); - return $this; + return \true; } /** - * Specifies methods that don't exist in the class which you want to mock. - * - * @param string[] $methods + * @psalm-param class-string $stubClassName + * @psalm-param class-string $className + * @psalm-param non-empty-string $methodName * - * @throws CannotUseAddMethodsException - * @throws ReflectionException * @throws RuntimeException - * - * @return $this */ - public function addMethods(array $methods) : self + private function newInstanceOf(string $stubClassName, string $className, string $methodName): \PHPUnit\Framework\MockObject\Stub { - if (empty($methods)) { - $this->emptyMethodsArray = \true; - return $this; - } try { - $reflector = new ReflectionClass($this->type); - // @codeCoverageIgnoreStart - } catch (\ReflectionException $e) { - throw new \PHPUnit\Framework\MockObject\ReflectionException($e->getMessage(), $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd - foreach ($methods as $method) { - if ($reflector->hasMethod($method)) { - throw new \PHPUnit\Framework\MockObject\CannotUseAddMethodsException($this->type, $method); - } + return (new ReflectionClass($stubClassName))->newInstanceWithoutConstructor(); + } catch (Throwable $t) { + throw new \PHPUnit\Framework\MockObject\RuntimeException(sprintf('Return value for %s::%s() cannot be generated: %s', $className, $methodName, $t->getMessage())); } - $this->methods = array_merge($this->methods ?? [], $methods); - return $this; } /** - * Specifies the subset of methods to not mock. Default is to mock all of them. - * - * @deprecated https://github.com/sebastianbergmann/phpunit/pull/3687 + * @psalm-param class-string $type + * @psalm-param class-string $className + * @psalm-param non-empty-string $methodName * - * @throws ReflectionException + * @throws RuntimeException */ - public function setMethodsExcept(array $methods = []) : self + private function testDoubleFor(string $type, string $className, string $methodName): \PHPUnit\Framework\MockObject\Stub { - return $this->setMethods(array_diff($this->generator->getClassMethods($this->type), $methods)); + try { + return (new Generator())->testDouble($type, \false, [], [], '', \false); + } catch (Throwable $t) { + throw new \PHPUnit\Framework\MockObject\RuntimeException(sprintf('Return value for %s::%s() cannot be generated: %s', $className, $methodName, $t->getMessage())); + } } /** - * Specifies the arguments for the constructor. + * @psalm-param non-empty-list $types + * @psalm-param class-string $className + * @psalm-param non-empty-string $methodName * - * @return $this + * @throws RuntimeException */ - public function setConstructorArgs(array $args) : self + private function testDoubleForIntersectionOfInterfaces(array $types, string $className, string $methodName): \PHPUnit\Framework\MockObject\Stub { - $this->constructorArgs = $args; - return $this; + try { + return (new Generator())->testDoubleForInterfaceIntersection($types, \false); + } catch (Throwable $t) { + throw new \PHPUnit\Framework\MockObject\RuntimeException(sprintf('Return value for %s::%s() cannot be generated: %s', $className, $methodName, $t->getMessage())); + } } - /** - * Specifies the name for the mock class. - * - * @return $this - */ - public function setMockClassName(string $name) : self +} + + * + * 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 \PHPUnit\Framework\MockObject\Rule\InvocationOrder +{ + public function toString(): string { - $this->mockClassName = $name; - return $this; + return 'invoked zero or more times'; } - /** - * Disables the invocation of the original constructor. - * - * @return $this - */ - public function disableOriginalConstructor() : self + public function verify(): void { - $this->originalConstructor = \false; - return $this; } - /** - * Enables the invocation of the original constructor. - * - * @return $this - */ - public function enableOriginalConstructor() : self + public function matches(BaseInvocation $invocation): bool { - $this->originalConstructor = \true; - return $this; + return \true; } - /** - * Disables the invocation of the original clone constructor. - * - * @return $this - */ - public function disableOriginalClone() : self +} + + * + * 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 \PHPUnit\Framework\MockObject\Rule\ParametersRule +{ + public function apply(BaseInvocation $invocation): void { - $this->originalClone = \false; - return $this; } - /** - * Enables the invocation of the original clone constructor. - * - * @return $this - */ - public function enableOriginalClone() : self + public function verify(): void { - $this->originalClone = \true; - return $this; } +} + + * + * 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 function count; +use PHPUnit\Framework\MockObject\Invocation as BaseInvocation; +use PHPUnit\Framework\SelfDescribing; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +abstract class InvocationOrder implements SelfDescribing +{ /** - * Disables the use of class autoloading while creating the mock object. - * - * @return $this + * @psalm-var list */ - public function disableAutoload() : self + private array $invocations = []; + public function numberOfInvocations(): int { - $this->autoload = \false; - return $this; + return count($this->invocations); } - /** - * Enables the use of class autoloading while creating the mock object. - * - * @return $this - */ - public function enableAutoload() : self + public function hasBeenInvoked(): bool { - $this->autoload = \true; - return $this; + return count($this->invocations) > 0; } - /** - * Disables the cloning of arguments passed to mocked methods. - * - * @return $this - */ - public function disableArgumentCloning() : self + final public function invoked(BaseInvocation $invocation): void { - $this->cloneArguments = \false; - return $this; + $this->invocations[] = $invocation; + $this->invokedDo($invocation); } - /** - * Enables the cloning of arguments passed to mocked methods. - * - * @return $this - */ - public function enableArgumentCloning() : self + abstract public function matches(BaseInvocation $invocation): bool; + abstract public function verify(): void; + protected function invokedDo(BaseInvocation $invocation): void { - $this->cloneArguments = \true; - return $this; } - /** - * Enables the invocation of the original methods. - * - * @return $this - */ - public function enableProxyingToOriginalMethods() : self +} + + * + * 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 function sprintf; +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 \PHPUnit\Framework\MockObject\Rule\InvocationOrder +{ + private readonly int $requiredInvocations; + public function __construct(int $requiredInvocations) { - $this->callOriginalMethods = \true; - return $this; + $this->requiredInvocations = $requiredInvocations; } - /** - * Disables the invocation of the original methods. - * - * @return $this - */ - public function disableProxyingToOriginalMethods() : self + public function toString(): string { - $this->callOriginalMethods = \false; - $this->proxyTarget = null; - return $this; + return sprintf('invoked at least %d time%s', $this->requiredInvocations, ($this->requiredInvocations !== 1) ? 's' : ''); } /** - * Sets the proxy target. + * Verifies that the current expectation is valid. If everything is OK the + * code should just return, if not it must throw an exception. * - * @return $this + * @throws ExpectationFailedException */ - public function setProxyTarget(object $object) : self + public function verify(): void { - $this->proxyTarget = $object; - return $this; + $actualInvocations = $this->numberOfInvocations(); + if ($actualInvocations < $this->requiredInvocations) { + throw new ExpectationFailedException(sprintf('Expected invocation at least %d time%s but it occurred %d time%s.', $this->requiredInvocations, ($this->requiredInvocations !== 1) ? 's' : '', $actualInvocations, ($actualInvocations !== 1) ? 's' : '')); + } } - /** - * @return $this - */ - public function allowMockingUnknownTypes() : self + public function matches(BaseInvocation $invocation): bool { - $this->allowMockingUnknownTypes = \true; - return $this; + return \true; } - /** - * @return $this - */ - public function disallowMockingUnknownTypes() : self +} + + * + * 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 \PHPUnit\Framework\MockObject\Rule\InvocationOrder +{ + public function toString(): string { - $this->allowMockingUnknownTypes = \false; - return $this; + return 'invoked at least once'; } /** - * @return $this + * 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 enableAutoReturnValueGeneration() : self + public function verify(): void { - $this->returnValueGeneration = \true; - return $this; + $count = $this->numberOfInvocations(); + if ($count < 1) { + throw new ExpectationFailedException('Expected invocation at least once but it never occurred.'); + } } - /** - * @return $this - */ - public function disableAutoReturnValueGeneration() : self + public function matches(BaseInvocation $invocation): bool { - $this->returnValueGeneration = \false; - return $this; + return \true; } } classCode = $classCode; - $this->mockName = $mockName; - $this->configurableMethods = $configurableMethods; + $this->allowedInvocations = $allowedInvocations; + } + public function toString(): string + { + return sprintf('invoked at most %d time%s', $this->allowedInvocations, ($this->allowedInvocations !== 1) ? 's' : ''); } /** - * @psalm-return class-string + * 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 generate() : string + public function verify(): void { - if (!class_exists($this->mockName, \false)) { - eval($this->classCode); - call_user_func([$this->mockName, '__phpunit_initConfigurableMethods'], ...$this->configurableMethods); + $actualInvocations = $this->numberOfInvocations(); + if ($actualInvocations > $this->allowedInvocations) { + throw new ExpectationFailedException(sprintf('Expected invocation at most %d time%s but it occurred %d time%s.', $this->allowedInvocations, ($this->allowedInvocations !== 1) ? 's' : '', $actualInvocations, ($actualInvocations !== 1) ? 's' : '')); } - return $this->mockName; } - public function getClassCode() : string + public function matches(BaseInvocation $invocation): bool { - return $this->classCode; + return \true; } } expectedCount = $expectedCount; + } + public function isNever(): bool + { + return $this->expectedCount === 0; + } + public function toString(): string + { + return sprintf('invoked %d time%s', $this->expectedCount, ($this->expectedCount !== 1) ? 's' : ''); + } + public function matches(BaseInvocation $invocation): bool + { + return \true; + } /** - * @var 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 + * 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 */ - private $argumentsForCall; + public function verify(): void + { + $actualCount = $this->numberOfInvocations(); + if ($actualCount !== $this->expectedCount) { + throw new ExpectationFailedException(sprintf('Method was expected to be called %d time%s, actually called %d time%s.', $this->expectedCount, ($this->expectedCount !== 1) ? 's' : '', $actualCount, ($actualCount !== 1) ? 's' : '')); + } + } /** - * @var Type + * @throws ExpectationFailedException */ - private $returnType; - /** - * @var string - */ - private $reference; - /** - * @var bool - */ - private $callOriginalMethod; - /** - * @var bool - */ - private $static; - /** - * @var ?string - */ - private $deprecation; - /** - * @throws ReflectionException - * @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::getMethodParametersForDeclaration($method), self::getMethodParametersForCall($method), (new ReflectionMapper())->fromReturnType($method), $reference, $callOriginalMethod, $method->isStatic(), $deprecation); - } - public static function fromName(string $fullClassName, string $methodName, bool $cloneArguments) : self - { - return new self($fullClassName, $methodName, $cloneArguments, 'public', '', '', new UnknownType(), '', \false, \false, null); - } - 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) - { - $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; - } - public function getName() : string - { - return $this->methodName; - } - /** - * @throws RuntimeException - */ - public function generateCode() : string + protected function invokedDo(BaseInvocation $invocation): void { - if ($this->static) { - $templateFile = 'mocked_static_method.tpl'; - } elseif ($this->returnType->isNever() || $this->returnType->isVoid()) { - $templateFile = sprintf('%s_method_never_or_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(); + $count = $this->numberOfInvocations(); + if ($count > $this->expectedCount) { + $message = $invocation->toString() . ' '; + $message .= match ($this->expectedCount) { + 0 => 'was not expected to be called.', + 1 => 'was not expected to be called more than once.', + default => sprintf('was not expected to be called more than %d times.', $this->expectedCount), + }; + throw new ExpectationFailedException($message); } - $template = $this->getTemplate($templateFile); - $template->setVar(['arguments_decl' => $this->argumentsForDeclaration, 'arguments_call' => $this->argumentsForCall, 'return_declaration' => !empty($this->returnType->asString()) ? ': ' . $this->returnType->asString() : '', 'return_type' => $this->returnType->asString(), '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; } +} + + * + * 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 function is_string; +use PHPUnit\Framework\Constraint\Constraint; +use PHPUnit\Framework\ExpectationFailedException; +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 +{ + private readonly Constraint $constraint; /** - * @throws RuntimeException + * @throws InvalidArgumentException */ - private function getTemplate(string $template) : Template + public function __construct(Constraint|string $constraint) { - $filename = __DIR__ . DIRECTORY_SEPARATOR . 'Generator' . DIRECTORY_SEPARATOR . $template; - if (!isset(self::$templates[$filename])) { - try { - self::$templates[$filename] = new Template($filename); - } catch (TemplateException $e) { - throw new \PHPUnit\Framework\MockObject\RuntimeException($e->getMessage(), $e->getCode(), $e); - } + if (is_string($constraint)) { + $constraint = new MethodNameConstraint($constraint); } - return self::$templates[$filename]; + $this->constraint = $constraint; } - /** - * Returns the parameters of a function or method. - * - * @throws RuntimeException - */ - private static function getMethodParametersForDeclaration(ReflectionMethod $method) : string + public function toString(): string { - $parameters = []; - $types = (new ReflectionMapper())->fromParameterTypes($method); - foreach ($method->getParameters() as $i => $parameter) { - $name = '$' . $parameter->getName(); - /* Note: PHP extensions may use empty names for reference arguments - * or "..." for methods taking a variable number of arguments. - */ - if ($name === '$' || $name === '$...') { - $name = '$arg' . $i; - } - $default = ''; - $reference = ''; - $typeDeclaration = ''; - if (!$types[$i]->type()->isUnknown()) { - $typeDeclaration = $types[$i]->type()->asString() . ' '; - } - if ($parameter->isPassedByReference()) { - $reference = '&'; - } - if ($parameter->isVariadic()) { - $name = '...' . $name; - } elseif ($parameter->isDefaultValueAvailable()) { - $default = ' = ' . self::exportDefaultValue($parameter); - } elseif ($parameter->isOptional()) { - $default = ' = null'; - } - $parameters[] = $typeDeclaration . $reference . $name . $default; - } - return implode(', ', $parameters); + return 'method name ' . $this->constraint->toString(); } /** - * Returns the parameters of a function or method. - * - * @throws ReflectionException + * @throws ExpectationFailedException */ - private static function getMethodParametersForCall(ReflectionMethod $method) : string + public function matches(BaseInvocation $invocation): bool { - $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()) { - continue; - } - if ($parameter->isPassedByReference()) { - $parameters[] = '&' . $name; - } else { - $parameters[] = $name; - } - } - return implode(', ', $parameters); + return $this->matchesName($invocation->methodName()); } /** - * @throws ReflectionException + * @throws ExpectationFailedException */ - private static function exportDefaultValue(ReflectionParameter $parameter) : string + public function matchesName(string $methodName): bool { - try { - $defaultValue = $parameter->getDefaultValue(); - if (!is_object($defaultValue)) { - return (string) var_export($defaultValue, \true); - } - $parameterAsString = $parameter->__toString(); - return (string) explode(' = ', substr(substr($parameterAsString, strpos($parameterAsString, ' ') + strlen(' ')), 0, -2))[1]; - // @codeCoverageIgnoreStart - } catch (\ReflectionException $e) { - throw new \PHPUnit\Framework\MockObject\ReflectionException($e->getMessage(), $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd + return (bool) $this->constraint->evaluate($methodName, '', \true); } } + */ + private array $parameters = []; + private ?BaseInvocation $invocation = null; + private null|bool|ExpectationFailedException $parameterVerificationResult; + /** + * @throws \PHPUnit\Framework\Exception */ - private $methods = []; - public function addMethods(\PHPUnit\Framework\MockObject\MockMethod ...$methods) : void + public function __construct(array $parameters) { - foreach ($methods as $method) { - $this->methods[strtolower($method->getName())] = $method; + foreach ($parameters as $parameter) { + if (!$parameter instanceof Constraint) { + $parameter = new IsEqual($parameter); + } + $this->parameters[] = $parameter; } } /** - * @return MockMethod[] + * @throws Exception */ - public function asArray() : array + public function apply(BaseInvocation $invocation): void { - return array_values($this->methods); + $this->invocation = $invocation; + $this->parameterVerificationResult = null; + try { + $this->parameterVerificationResult = $this->doVerify(); + } catch (ExpectationFailedException $e) { + $this->parameterVerificationResult = $e; + throw $this->parameterVerificationResult; + } } - public function hasMethod(string $methodName) : bool + /** + * 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 + */ + public function verify(): void { - return array_key_exists(strtolower($methodName), $this->methods); + $this->doVerify(); + } + /** + * @throws ExpectationFailedException + */ + 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->parameters()) < 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 && $this->parameters[0]::class === 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->parameters()[$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; } } classCode = $classCode; - $this->mockName = $mockName; + $this->stack = $stack; } - /** - * @psalm-return class-string - */ - public function generate() : string + public function invoke(Invocation $invocation): mixed { - if (!class_exists($this->mockName, \false)) { - eval($this->classCode); + $value = array_shift($this->stack); + if ($value instanceof \PHPUnit\Framework\MockObject\Stub\Stub) { + $value = $value->invoke($invocation); } - return $this->mockName; - } - public function getClassCode() : string - { - return $this->classCode; + return $value; } } exception = $exception; + } /** - * @psalm-return class-string + * @throws Throwable */ - public function generate() : string; + public function invoke(Invocation $invocation): never + { + throw $this->exception; + } } argumentIndex = $argumentIndex; } - public function matches(BaseInvocation $invocation) : bool - { - return \true; - } - protected function invokedDo(BaseInvocation $invocation) : void + public function invoke(Invocation $invocation): mixed { + return $invocation->parameters()[$this->argumentIndex] ?? null; } } callback = $callback; } - public function verify() : void + public function invoke(Invocation $invocation): mixed { + return call_user_func_array($this->callback, $invocation->parameters()); } } $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 InvalidArgumentException - */ - public function apply(BaseInvocation $invocation) : void - { - $this->invocations[] = $invocation; - $callIndex = count($this->invocations) - 1; - $this->verifyInvocation($invocation, $callIndex); - } - /** - * @throws ExpectationFailedException - * @throws InvalidArgumentException - */ - public function verify() : void + private mixed $reference; + public function __construct(mixed &$reference) { - foreach ($this->invocations as $callIndex => $invocation) { - $this->verifyInvocation($invocation, $callIndex); - } + $this->reference =& $reference; } - /** - * Verify a single invocation. - * - * @param int $callIndex - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - */ - private function verifyInvocation(BaseInvocation $invocation, $callIndex) : void + public function invoke(Invocation $invocation): mixed { - if (!isset($this->parameterGroups[$callIndex])) { - // no parameter assertion for this call index - return; - } - $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())); - } + return $this->reference; } } invocations); - } - public function hasBeenInvoked() : bool - { - return count($this->invocations) > 0; + return $invocation->object(); } - public final function invoked(BaseInvocation $invocation) - { - $this->invocations[] = $invocation; - return $this->invokedDo($invocation); - } - public abstract function matches(BaseInvocation $invocation) : bool; - protected abstract function invokedDo(BaseInvocation $invocation); } 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 + private readonly mixed $value; + public function __construct(mixed $value) { - if ($this->currentIndex < $this->sequenceIndex) { - throw new ExpectationFailedException(sprintf('The expected invocation at index %s was never reached.', $this->sequenceIndex)); - } + $this->value = $value; } - protected function invokedDo(BaseInvocation $invocation) : void + public function invoke(Invocation $invocation): mixed { + return $this->value; } } requiredInvocations = $requiredInvocations; - } - public function toString() : string + private readonly array $valueMap; + public function __construct(array $valueMap) { - return 'invoked at least ' . $this->requiredInvocations . ' times'; + $this->valueMap = $valueMap; } - /** - * 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 + public function invoke(Invocation $invocation): mixed { - $count = $this->getInvocationCount(); - if ($count < $this->requiredInvocations) { - throw new ExpectationFailedException('Expected invocation at least ' . $this->requiredInvocations . ' times but it occurred ' . $count . ' time(s).'); + $parameterCount = count($invocation->parameters()); + foreach ($this->valueMap as $map) { + if (!is_array($map) || $parameterCount !== count($map) - 1) { + continue; + } + $return = array_pop($map); + if ($invocation->parameters() === $map) { + return $return; + } } - } - public function matches(BaseInvocation $invocation) : bool - { - return \true; - } - protected function invokedDo(BaseInvocation $invocation) : void - { + return null; } } 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 - { - } + public function invoke(Invocation $invocation): mixed; } */ - public function __construct($allowedInvocations) - { - $this->allowedInvocations = $allowedInvocations; - } - public function toString() : string - { - return 'invoked at most ' . $this->allowedInvocations . ' times'; - } + public function provides(): array; /** - * 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 + * @psalm-return list */ - 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 - { - } + public function requires(): array; } 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 + * Returns a string representation of the object. */ - 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); - } - } + public function toString(): string; } constraint = $constraint; - } - public function toString() : string - { - return 'method name ' . $this->constraint->toString(); - } - /** - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - */ - public function matches(BaseInvocation $invocation) : bool - { - return $this->matchesName($invocation->getMethodName()); - } - /** - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ExpectationFailedException - */ - public function matchesName(string $methodName) : bool - { - return (bool) $this->constraint->evaluate($methodName, '', \true); - } + public function run(): void; } parameters[] = $parameter; + $className = $theClass->getName(); + $data = (new DataProvider())->providedData($className, $methodName); + if ($data !== null) { + return $this->buildDataProviderTestSuite($methodName, $className, $data, $this->shouldTestMethodBeRunInSeparateProcess($className, $methodName), $this->shouldGlobalStateBePreserved($className, $methodName), $this->shouldAllTestMethodsOfTestClassBeRunInSingleSeparateProcess($className), $this->backupSettings($className, $methodName)); } + $test = new $className($methodName); + assert($test instanceof \PHPUnit\Framework\TestCase); + $this->configureTestCase($test, $this->shouldTestMethodBeRunInSeparateProcess($className, $methodName), $this->shouldGlobalStateBePreserved($className, $methodName), $this->shouldAllTestMethodsOfTestClassBeRunInSingleSeparateProcess($className), $this->backupSettings($className, $methodName)); + return $test; } - public function toString() : string + /** + * @psalm-param class-string $className + * @psalm-param non-empty-string $methodName + * @psalm-param array{backupGlobals: ?bool, backupGlobalsExcludeList: list, backupStaticProperties: ?bool, backupStaticPropertiesExcludeList: array>} $backupSettings + */ + private function buildDataProviderTestSuite(string $methodName, string $className, array $data, bool $runTestInSeparateProcess, ?bool $preserveGlobalState, bool $runClassInSeparateProcess, array $backupSettings): \PHPUnit\Framework\DataProviderTestSuite { - $text = 'with parameter'; - foreach ($this->parameters as $index => $parameter) { - if ($index > 0) { - $text .= ' and'; - } - $text .= ' ' . $index . ' ' . $parameter->toString(); + $dataProviderTestSuite = \PHPUnit\Framework\DataProviderTestSuite::empty($className . '::' . $methodName); + $groups = (new Groups())->groups($className, $methodName); + foreach ($data as $_dataName => $_data) { + $_test = new $className($methodName); + assert($_test instanceof \PHPUnit\Framework\TestCase); + $_test->setData($_dataName, $_data); + $this->configureTestCase($_test, $runTestInSeparateProcess, $preserveGlobalState, $runClassInSeparateProcess, $backupSettings); + $dataProviderTestSuite->addTest($_test, $groups); } - return $text; + return $dataProviderTestSuite; } /** - * @throws Exception + * @psalm-param array{backupGlobals: ?bool, backupGlobalsExcludeList: list, backupStaticProperties: ?bool, backupStaticPropertiesExcludeList: array>} $backupSettings */ - public function apply(BaseInvocation $invocation) : void + private function configureTestCase(\PHPUnit\Framework\TestCase $test, bool $runTestInSeparateProcess, ?bool $preserveGlobalState, bool $runClassInSeparateProcess, array $backupSettings): void { - $this->invocation = $invocation; - $this->parameterVerificationResult = null; - try { - $this->parameterVerificationResult = $this->doVerify(); - } catch (ExpectationFailedException $e) { - $this->parameterVerificationResult = $e; - throw $this->parameterVerificationResult; + if ($runTestInSeparateProcess) { + $test->setRunTestInSeparateProcess(\true); + } + if ($runClassInSeparateProcess) { + $test->setRunClassInSeparateProcess(\true); + } + if ($preserveGlobalState !== null) { + $test->setPreserveGlobalState($preserveGlobalState); + } + if ($backupSettings['backupGlobals'] !== null) { + $test->setBackupGlobals($backupSettings['backupGlobals']); + } else { + $test->setBackupGlobals(ConfigurationRegistry::get()->backupGlobals()); + } + $test->setBackupGlobalsExcludeList($backupSettings['backupGlobalsExcludeList']); + if ($backupSettings['backupStaticProperties'] !== null) { + $test->setBackupStaticProperties($backupSettings['backupStaticProperties']); + } else { + $test->setBackupStaticProperties(ConfigurationRegistry::get()->backupStaticProperties()); } + $test->setBackupStaticPropertiesExcludeList($backupSettings['backupStaticPropertiesExcludeList']); } /** - * 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. + * @psalm-param class-string $className + * @psalm-param non-empty-string $methodName * - * @throws ExpectationFailedException - * @throws InvalidArgumentException + * @psalm-return array{backupGlobals: ?bool, backupGlobalsExcludeList: list, backupStaticProperties: ?bool, backupStaticPropertiesExcludeList: array>} */ - public function verify() : void + private function backupSettings(string $className, string $methodName): array { - $this->doVerify(); + $metadataForClass = MetadataRegistry::parser()->forClass($className); + $metadataForMethod = MetadataRegistry::parser()->forMethod($className, $methodName); + $metadataForClassAndMethod = MetadataRegistry::parser()->forClassAndMethod($className, $methodName); + $backupGlobals = null; + $backupGlobalsExcludeList = []; + if ($metadataForMethod->isBackupGlobals()->isNotEmpty()) { + $metadata = $metadataForMethod->isBackupGlobals()->asArray()[0]; + assert($metadata instanceof BackupGlobals); + if ($metadata->enabled()) { + $backupGlobals = \true; + } + } elseif ($metadataForClass->isBackupGlobals()->isNotEmpty()) { + $metadata = $metadataForClass->isBackupGlobals()->asArray()[0]; + assert($metadata instanceof BackupGlobals); + if ($metadata->enabled()) { + $backupGlobals = \true; + } + } + foreach ($metadataForClassAndMethod->isExcludeGlobalVariableFromBackup() as $metadata) { + assert($metadata instanceof ExcludeGlobalVariableFromBackup); + $backupGlobalsExcludeList[] = $metadata->globalVariableName(); + } + $backupStaticProperties = null; + $backupStaticPropertiesExcludeList = []; + if ($metadataForMethod->isBackupStaticProperties()->isNotEmpty()) { + $metadata = $metadataForMethod->isBackupStaticProperties()->asArray()[0]; + assert($metadata instanceof BackupStaticProperties); + if ($metadata->enabled()) { + $backupStaticProperties = \true; + } + } elseif ($metadataForClass->isBackupStaticProperties()->isNotEmpty()) { + $metadata = $metadataForClass->isBackupStaticProperties()->asArray()[0]; + assert($metadata instanceof BackupStaticProperties); + if ($metadata->enabled()) { + $backupStaticProperties = \true; + } + } + foreach ($metadataForClassAndMethod->isExcludeStaticPropertyFromBackup() as $metadata) { + assert($metadata instanceof ExcludeStaticPropertyFromBackup); + if (!isset($backupStaticPropertiesExcludeList[$metadata->className()])) { + $backupStaticPropertiesExcludeList[$metadata->className()] = []; + } + $backupStaticPropertiesExcludeList[$metadata->className()][] = $metadata->propertyName(); + } + return ['backupGlobals' => $backupGlobals, 'backupGlobalsExcludeList' => $backupGlobalsExcludeList, 'backupStaticProperties' => $backupStaticProperties, 'backupStaticPropertiesExcludeList' => $backupStaticPropertiesExcludeList]; } /** - * @throws ExpectationFailedException - * @throws InvalidArgumentException + * @psalm-param class-string $className + * @psalm-param non-empty-string $methodName */ - private function doVerify() : bool + private function shouldGlobalStateBePreserved(string $className, string $methodName): ?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())); + $metadataForMethod = MetadataRegistry::parser()->forMethod($className, $methodName); + if ($metadataForMethod->isPreserveGlobalState()->isNotEmpty()) { + $metadata = $metadataForMethod->isPreserveGlobalState()->asArray()[0]; + assert($metadata instanceof PreserveGlobalState); + return $metadata->enabled(); } - 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())); + $metadataForClass = MetadataRegistry::parser()->forClass($className); + if ($metadataForClass->isPreserveGlobalState()->isNotEmpty()) { + $metadata = $metadataForClass->isPreserveGlobalState()->asArray()[0]; + assert($metadata instanceof PreserveGlobalState); + return $metadata->enabled(); } - return \true; + return null; } /** - * @throws ExpectationFailedException + * @psalm-param class-string $className + * @psalm-param non-empty-string $methodName */ - private function guardAgainstDuplicateEvaluationOfParameterConstraints() : bool + private function shouldTestMethodBeRunInSeparateProcess(string $className, string $methodName): bool { - if ($this->parameterVerificationResult instanceof ExpectationFailedException) { - throw $this->parameterVerificationResult; + if (MetadataRegistry::parser()->forClass($className)->isRunTestsInSeparateProcesses()->isNotEmpty()) { + return \true; } - return (bool) $this->parameterVerificationResult; + if (MetadataRegistry::parser()->forMethod($className, $methodName)->isRunInSeparateProcess()->isNotEmpty()) { + return \true; + } + return \false; } -} - - * - * 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; -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -interface ParametersRule extends SelfDescribing, Verifiable -{ /** - * @throws ExpectationFailedException if the invocation violates the rule + * @psalm-param class-string $className */ - public function apply(BaseInvocation $invocation) : void; - public function verify() : void; -} - - * - * 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) - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -interface Stub -{ - public function __phpunit_getInvocationHandler() : \PHPUnit\Framework\MockObject\InvocationHandler; - public function __phpunit_hasMatchers() : bool; - public function __phpunit_setReturnValueGeneration(bool $returnValueGeneration) : void; + private function shouldAllTestMethodsOfTestClassBeRunInSingleSeparateProcess(string $className): bool + { + return MetadataRegistry::parser()->forClass($className)->isRunClassInSeparateProcess()->isNotEmpty(); + } } */ - private $stack; + private array $backupGlobalsExcludeList = []; + private ?bool $backupStaticProperties = null; /** - * @var mixed + * @psalm-var array> */ - private $value; - public function __construct(array $stack) + private array $backupStaticPropertiesExcludeList = []; + private ?Snapshot $snapshot = null; + private ?bool $runClassInSeparateProcess = null; + private ?bool $runTestInSeparateProcess = null; + private bool $preserveGlobalState = \false; + private bool $inIsolation = \false; + private ?string $expectedException = null; + private ?string $expectedExceptionMessage = null; + private ?string $expectedExceptionMessageRegExp = null; + private null|int|string $expectedExceptionCode = null; + /** + * @psalm-var list + */ + private array $providedTests = []; + private array $data = []; + private int|string $dataName = ''; + /** + * @psalm-var non-empty-string + */ + private string $name; + /** + * @psalm-var list + */ + private array $groups = []; + /** + * @psalm-var list + */ + private array $dependencies = []; + private array $dependencyInput = []; + /** + * @psalm-var array + */ + private array $iniSettings = []; + private array $locale = []; + /** + * @psalm-var list + */ + private array $mockObjects = []; + private bool $registerMockObjectsFromTestArgumentsRecursively = \false; + private TestStatus $status; + private int $numberOfAssertionsPerformed = 0; + private mixed $testResult = null; + private string $output = ''; + private ?string $outputExpectedRegex = null; + private ?string $outputExpectedString = null; + private bool $outputBufferingActive = \false; + private int $outputBufferingLevel; + private bool $outputRetrievedForAssertion = \false; + private bool $doesNotPerformAssertions = \false; + /** + * @psalm-var list + */ + private array $customComparators = []; + private ?Event\Code\TestMethod $testValueObjectForEvents = null; + private bool $wasPrepared = \false; + /** + * @psalm-var array + */ + private array $failureTypes = []; + /** + * Returns a matcher that matches when the method is executed + * zero or more times. + */ + final public static function any(): AnyInvokedCountMatcher { - $this->stack = $stack; + return new AnyInvokedCountMatcher(); } - public function invoke(Invocation $invocation) + /** + * Returns a matcher that matches when the method is never executed. + */ + final public static function never(): InvokedCountMatcher { - $this->value = array_shift($this->stack); - if ($this->value instanceof \PHPUnit\Framework\MockObject\Stub\Stub) { - $this->value = $this->value->invoke($invocation); - } - return $this->value; + return new InvokedCountMatcher(0); } - public function toString() : string + /** + * Returns a matcher that matches when the method is executed + * at least N times. + */ + final public static function atLeast(int $requiredInvocations): InvokedAtLeastCountMatcher { - $exporter = new Exporter(); - return sprintf('return user-specified value %s', $exporter->export($this->value)); + return new InvokedAtLeastCountMatcher($requiredInvocations); } -} - - * - * 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 function sprintf; -use PHPUnit\Framework\MockObject\Invocation; -use PHPUnitPHAR\SebastianBergmann\Exporter\Exporter; -use Throwable; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class Exception implements \PHPUnit\Framework\MockObject\Stub\Stub -{ - private $exception; - public function __construct(Throwable $exception) + /** + * Returns a matcher that matches when the method is executed at least once. + */ + final public static function atLeastOnce(): InvokedAtLeastOnceMatcher { - $this->exception = $exception; + return new InvokedAtLeastOnceMatcher(); } /** - * @throws Throwable + * Returns a matcher that matches when the method is executed exactly once. */ - public function invoke(Invocation $invocation) : void + final public static function once(): InvokedCountMatcher { - throw $this->exception; + return new InvokedCountMatcher(1); } - public function toString() : string + /** + * Returns a matcher that matches when the method is executed + * exactly $count times. + */ + final public static function exactly(int $count): InvokedCountMatcher { - $exporter = new Exporter(); - return sprintf('raise user-specified exception %s', $exporter->export($this->exception)); + return new InvokedCountMatcher($count); } -} - - * - * 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 function sprintf; -use PHPUnit\Framework\MockObject\Invocation; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class ReturnArgument implements \PHPUnit\Framework\MockObject\Stub\Stub -{ /** - * @var int + * Returns a matcher that matches when the method is executed + * at most N times. */ - private $argumentIndex; - public function __construct($argumentIndex) + final public static function atMost(int $allowedInvocations): InvokedAtMostCountMatcher { - $this->argumentIndex = $argumentIndex; + return new InvokedAtMostCountMatcher($allowedInvocations); } - public function invoke(Invocation $invocation) + /** + * @deprecated Use $double->willReturn() instead of $double->will($this->returnValue()) + * @see https://github.com/sebastianbergmann/phpunit/issues/5423 + * + * @codeCoverageIgnore + */ + final public static function returnValue(mixed $value): ReturnStub { - if (isset($invocation->getParameters()[$this->argumentIndex])) { - return $invocation->getParameters()[$this->argumentIndex]; - } + return new ReturnStub($value); } - public function toString() : string + /** + * @deprecated Use $double->willReturnMap() instead of $double->will($this->returnValueMap()) + * @see https://github.com/sebastianbergmann/phpunit/issues/5423 + * + * @codeCoverageIgnore + */ + final public static function returnValueMap(array $valueMap): ReturnValueMapStub { - return sprintf('return argument #%d', $this->argumentIndex); + return new ReturnValueMapStub($valueMap); } -} - - * - * 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 function call_user_func_array; -use function get_class; -use function is_array; -use function is_object; -use function sprintf; -use PHPUnit\Framework\MockObject\Invocation; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class ReturnCallback implements \PHPUnit\Framework\MockObject\Stub\Stub -{ - private $callback; - public function __construct($callback) + /** + * @deprecated Use $double->willReturnArgument() instead of $double->will($this->returnArgument()) + * @see https://github.com/sebastianbergmann/phpunit/issues/5423 + * + * @codeCoverageIgnore + */ + final public static function returnArgument(int $argumentIndex): ReturnArgumentStub { - $this->callback = $callback; + return new ReturnArgumentStub($argumentIndex); } - public function invoke(Invocation $invocation) + /** + * @deprecated Use $double->willReturnCallback() instead of $double->will($this->returnCallback()) + * @see https://github.com/sebastianbergmann/phpunit/issues/5423 + * + * @codeCoverageIgnore + */ + final public static function returnCallback(callable $callback): ReturnCallbackStub { - return call_user_func_array($this->callback, $invocation->getParameters()); + return new ReturnCallbackStub($callback); } - public function toString() : string + /** + * @deprecated Use $double->willReturnSelf() instead of $double->will($this->returnSelf()) + * @see https://github.com/sebastianbergmann/phpunit/issues/5423 + * + * @codeCoverageIgnore + */ + final public static function returnSelf(): ReturnSelfStub { - 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'; + return new ReturnSelfStub(); + } + final public static function throwException(Throwable $exception): ExceptionStub + { + return new ExceptionStub($exception); } -} - - * - * 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 function sprintf; -use PHPUnit\Framework\MockObject\Invocation; -use PHPUnitPHAR\SebastianBergmann\Exporter\Exporter; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class ReturnReference implements \PHPUnit\Framework\MockObject\Stub\Stub -{ /** - * @var mixed + * @deprecated Use $double->willReturn() instead of $double->will($this->onConsecutiveCalls()) + * @see https://github.com/sebastianbergmann/phpunit/issues/5423 + * @see https://github.com/sebastianbergmann/phpunit/issues/5425 + * + * @codeCoverageIgnore */ - private $reference; - public function __construct(&$reference) + final public static function onConsecutiveCalls(mixed ...$arguments): ConsecutiveCallsStub { - $this->reference =& $reference; + return new ConsecutiveCallsStub($arguments); } - public function invoke(Invocation $invocation) + /** + * @psalm-param non-empty-string $name + * + * @internal This method is not covered by the backward compatibility promise for PHPUnit + */ + public function __construct(string $name) { - return $this->reference; + $this->setName($name); + $this->status = TestStatus::unknown(); } - public function toString() : string + /** + * This method is called before the first test of this test class is run. + * + * @codeCoverageIgnore + */ + public static function setUpBeforeClass(): void { - $exporter = new Exporter(); - return sprintf('return user-specified reference %s', $exporter->export($this->reference)); } -} - - * - * 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 \PHPUnit\Framework\MockObject\Stub\Stub -{ /** - * @throws RuntimeException + * This method is called after the last test of this test class is run. + * + * @codeCoverageIgnore */ - public function invoke(Invocation $invocation) + public static function tearDownAfterClass(): void { - return $invocation->getObject(); } - public function toString() : string + /** + * This method is called before each test. + * + * @codeCoverageIgnore + */ + protected function setUp(): void { - return 'return the current object'; } -} - - * - * 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 function sprintf; -use PHPUnit\Framework\MockObject\Invocation; -use PHPUnitPHAR\SebastianBergmann\Exporter\Exporter; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class ReturnStub implements \PHPUnit\Framework\MockObject\Stub\Stub -{ /** - * @var mixed + * Performs assertions shared by all tests of a test case. + * + * This method is called between setUp() and test. + * + * @codeCoverageIgnore */ - private $value; - public function __construct($value) + protected function assertPreConditions(): void { - $this->value = $value; } - public function invoke(Invocation $invocation) + /** + * Performs assertions shared by all tests of a test case. + * + * This method is called between test and tearDown(). + * + * @codeCoverageIgnore + */ + protected function assertPostConditions(): void { - return $this->value; } - public function toString() : string + /** + * This method is called after each test. + * + * @codeCoverageIgnore + */ + protected function tearDown(): void { - $exporter = new Exporter(); - return sprintf('return user-specified value %s', $exporter->export($this->value)); } -} - - * - * 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 function array_pop; -use function count; -use function is_array; -use PHPUnit\Framework\MockObject\Invocation; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class ReturnValueMap implements \PHPUnit\Framework\MockObject\Stub\Stub -{ /** - * @var array + * Returns a string representation of the test case. + * + * @throws Exception + * + * @internal This method is not covered by the backward compatibility promise for PHPUnit */ - private $valueMap; - public function __construct(array $valueMap) + public function toString(): string { - $this->valueMap = $valueMap; + $buffer = sprintf('%s::%s', (new ReflectionClass($this))->getName(), $this->name); + return $buffer . $this->dataSetAsStringWithData(); } - public function invoke(Invocation $invocation) + /** + * @internal This method is not covered by the backward compatibility promise for PHPUnit + */ + final public function count(): int { - $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; - } - } + return 1; } - public function toString() : string + final public function getActualOutputForAssertion(): string { - return 'return value from a map'; + $this->outputRetrievedForAssertion = \true; + return $this->output(); + } + final public function expectOutputRegex(string $expectedRegex): void + { + $this->outputExpectedRegex = $expectedRegex; + } + final public function expectOutputString(string $expectedString): void + { + $this->outputExpectedString = $expectedString; } -} - - * - * 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. + * @psalm-param class-string $exception + */ + final public function expectException(string $exception): void + { + $this->expectedException = $exception; + } + final public function expectExceptionCode(int|string $code): void + { + $this->expectedExceptionCode = $code; + } + final public function expectExceptionMessage(string $message): void + { + $this->expectedExceptionMessage = $message; + } + final public function expectExceptionMessageMatches(string $regularExpression): void + { + $this->expectedExceptionMessageRegExp = $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. + */ + final public function expectExceptionObject(\Exception $exception): void + { + $this->expectException($exception::class); + $this->expectExceptionMessage($exception->getMessage()); + $this->expectExceptionCode($exception->getCode()); + } + final public function expectNotToPerformAssertions(): void + { + $this->doesNotPerformAssertions = \true; + } + /** + * @internal This method is not covered by the backward compatibility promise for PHPUnit + */ + final public function status(): TestStatus + { + return $this->status; + } + /** + * @throws \PHPUnit\Runner\Exception + * @throws \PHPUnit\Util\Exception + * @throws \SebastianBergmann\CodeCoverage\InvalidArgumentException + * @throws \SebastianBergmann\Template\InvalidArgumentException + * @throws CodeCoverageException + * @throws Exception + * @throws MoreThanOneDataSetFromDataProviderException + * @throws NoPreviousThrowableException + * @throws ProcessIsolationException + * @throws StaticAnalysisCacheNotConfiguredException + * @throws UnintentionallyCoveredCodeException * - * @param Invocation $invocation The invocation which was mocked and matched by the current method and argument matchers + * @internal This method is not covered by the backward compatibility promise for PHPUnit */ - public function invoke(Invocation $invocation); -} - - * - * 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 -{ + final public function run(): void + { + if (!$this->handleDependencies()) { + return; + } + if (!$this->shouldRunInSeparateProcess()) { + (new \PHPUnit\Framework\TestRunner())->run($this); + } else { + (new \PHPUnit\Framework\TestRunner())->runInSeparateProcess($this, $this->runClassInSeparateProcess && !$this->runTestInSeparateProcess, $this->preserveGlobalState); + } + } /** - * Verifies that the current expectation is valid. If everything is OK the - * code should just return, if not it must throw an exception. + * Returns a builder object to create mock objects using a fluent interface. * - * @throws ExpectationFailedException + * @psalm-template RealInstanceType of object + * + * @psalm-param class-string $className + * + * @psalm-return MockBuilder */ - public function verify() : void; -} - - * - * 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 Reorderable -{ - public function sortId() : string; + final public function getMockBuilder(string $className): MockBuilder + { + return new MockBuilder($this, $className); + } /** - * @return list + * @internal This method is not covered by the backward compatibility promise for PHPUnit */ - public function provides() : array; + final public function groups(): array + { + return $this->groups; + } /** - * @return list + * @internal This method is not covered by the backward compatibility promise for PHPUnit */ - public function requires() : array; -} - - * - * 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 -{ + final public function setGroups(array $groups): void + { + $this->groups = $groups; + } /** - * Returns a string representation of the object. + * @internal This method is not covered by the backward compatibility promise for PHPUnit */ - public function toString() : string; -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework; - -use Throwable; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -interface SkippedTest extends Throwable -{ -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework; - -use PHPUnitPHAR\SebastianBergmann\RecursionContext\InvalidArgumentException; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class SkippedTestCase extends \PHPUnit\Framework\TestCase -{ + final public function nameWithDataSet(): string + { + return $this->name . $this->dataSetAsString(); + } /** - * @var ?bool + * @psalm-return non-empty-string + * + * @internal This method is not covered by the backward compatibility promise for PHPUnit */ - protected $backupGlobals = \false; + final public function name(): string + { + return $this->name; + } /** - * @var ?bool + * @internal This method is not covered by the backward compatibility promise for PHPUnit */ - protected $backupStaticAttributes = \false; + final public function size(): TestSize + { + return (new Groups())->size(static::class, $this->name); + } /** - * @var ?bool + * @internal This method is not covered by the backward compatibility promise for PHPUnit */ - protected $runTestInSeparateProcess = \false; + final public function hasUnexpectedOutput(): bool + { + if ($this->output === '') { + return \false; + } + if ($this->expectsOutput()) { + return \false; + } + return \true; + } /** - * @var string + * @internal This method is not covered by the backward compatibility promise for PHPUnit */ - private $message; - public function __construct(string $className, string $methodName, string $message = '') + final public function output(): string { - parent::__construct($className . '::' . $methodName); - $this->message = $message; + if (!$this->outputBufferingActive) { + return $this->output; + } + return (string) ob_get_contents(); } - public function getMessage() : string + /** + * @internal This method is not covered by the backward compatibility promise for PHPUnit + */ + final public function doesNotPerformAssertions(): bool { - return $this->message; + return $this->doesNotPerformAssertions; } /** - * Returns a string representation of the test case. - * - * @throws InvalidArgumentException + * @internal This method is not covered by the backward compatibility promise for PHPUnit */ - public function toString() : string + final public function expectsOutput(): bool { - return $this->getName(); + return $this->hasExpectationOnOutput() || $this->outputRetrievedForAssertion; } /** - * @throws Exception + * @internal This method is not covered by the backward compatibility promise for PHPUnit + * + * @deprecated + * + * @codeCoverageIgnore */ - protected function runTest() : void + final public function registerMockObjectsFromTestArgumentsRecursively(): void { - $this->markTestSkipped($this->message); + $this->registerMockObjectsFromTestArgumentsRecursively = \true; } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework; - -use Countable; -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -interface Test extends Countable -{ /** - * Runs a test and collects its result in a TestResult instance. + * @throws Throwable + * + * @internal This method is not covered by the backward compatibility promise for PHPUnit */ - public function run(?\PHPUnit\Framework\TestResult $result = null) : \PHPUnit\Framework\TestResult; -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework; - -use function assert; -use function count; -use function get_class; -use function sprintf; -use function trim; -use PHPUnit\Util\Filter; -use PHPUnit\Util\InvalidDataSetException; -use PHPUnit\Util\Test as TestUtil; -use ReflectionClass; -use Throwable; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class TestBuilder -{ - public function build(ReflectionClass $theClass, string $methodName) : \PHPUnit\Framework\Test + final public function runBare(): void { - $className = $theClass->getName(); - if (!$theClass->isInstantiable()) { - return new \PHPUnit\Framework\ErrorTestCase(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 \PHPUnit\Framework\Exception('No valid test provided.'); - } - $parameters = $constructor->getParameters(); - // TestCase() or TestCase($name) - if (count($parameters) < 2) { - $test = $this->buildTestWithoutData($className); - } else { - try { - $data = TestUtil::getProvidedData($className, $methodName); - } catch (\PHPUnit\Framework\IncompleteTestError $e) { - $message = sprintf("Test for %s::%s marked incomplete by data provider\n%s", $className, $methodName, $this->throwableToString($e)); - $data = new \PHPUnit\Framework\IncompleteTestCase($className, $methodName, $message); - } catch (\PHPUnit\Framework\SkippedTestError $e) { - $message = sprintf("Test for %s::%s skipped by data provider\n%s", $className, $methodName, $this->throwableToString($e)); - $data = new \PHPUnit\Framework\SkippedTestCase($className, $methodName, $message); - } catch (Throwable $t) { - $message = sprintf("The data provider specified for %s::%s is invalid.\n%s", $className, $methodName, $this->throwableToString($t)); - $data = new \PHPUnit\Framework\ErrorTestCase($message); + $emitter = Event\Facade::emitter(); + $emitter->testPreparationStarted($this->valueObjectForEvents()); + $this->snapshotGlobalState(); + $this->startOutputBuffering(); + clearstatcache(); + $hookMethods = (new HookMethods())->hookMethods(static::class); + $hasMetRequirements = \false; + $this->numberOfAssertionsPerformed = 0; + $currentWorkingDirectory = getcwd(); + try { + $this->checkRequirements(); + $hasMetRequirements = \true; + if ($this->inIsolation) { + // @codeCoverageIgnoreStart + $this->invokeBeforeClassHookMethods($hookMethods, $emitter); + // @codeCoverageIgnoreEnd + } + if (method_exists(static::class, $this->name) && MetadataRegistry::parser()->forClassAndMethod(static::class, $this->name)->isDoesNotPerformAssertions()->isNotEmpty()) { + $this->doesNotPerformAssertions = \true; } - // Test method with @dataProvider. - if (isset($data)) { - $test = $this->buildDataProviderTestSuite($methodName, $className, $data, $runTestInSeparateProcess, $preserveGlobalState, $runClassInSeparateProcess, $backupSettings); + $this->invokeBeforeTestHookMethods($hookMethods, $emitter); + $this->invokePreConditionHookMethods($hookMethods, $emitter); + $emitter->testPrepared($this->valueObjectForEvents()); + $this->wasPrepared = \true; + $this->testResult = $this->runTest(); + $this->verifyMockObjects(); + $this->invokePostConditionHookMethods($hookMethods, $emitter); + $this->status = TestStatus::success(); + } catch (\PHPUnit\Framework\IncompleteTest $e) { + $this->status = TestStatus::incomplete($e->getMessage()); + $emitter->testMarkedAsIncomplete($this->valueObjectForEvents(), Event\Code\ThrowableBuilder::from($e)); + } catch (\PHPUnit\Framework\SkippedTest $e) { + $this->status = TestStatus::skipped($e->getMessage()); + $emitter->testSkipped($this->valueObjectForEvents(), $e->getMessage()); + } catch (AssertionError|\PHPUnit\Framework\AssertionFailedError $e) { + if (!$this->wasPrepared) { + $this->wasPrepared = \true; + $emitter->testPreparationFailed($this->valueObjectForEvents()); + } + $this->status = TestStatus::failure($e->getMessage()); + $emitter->testFailed($this->valueObjectForEvents(), Event\Code\ThrowableBuilder::from($e), Event\Code\ComparisonFailureBuilder::from($e)); + } catch (TimeoutException $e) { + $this->status = TestStatus::risky($e->getMessage()); + } catch (Throwable $_e) { + if ($this->isRegisteredFailure($_e)) { + $this->status = TestStatus::failure($_e->getMessage()); + $emitter->testFailed($this->valueObjectForEvents(), Event\Code\ThrowableBuilder::from($_e), null); } else { - $test = $this->buildTestWithoutData($className); + $e = $this->transformException($_e); + $this->status = TestStatus::error($e->getMessage()); + $emitter->testErrored($this->valueObjectForEvents(), Event\Code\ThrowableBuilder::from($e)); } } - if ($test instanceof \PHPUnit\Framework\TestCase) { - $test->setName($methodName); - $this->configureTestCase($test, $runTestInSeparateProcess, $preserveGlobalState, $runClassInSeparateProcess, $backupSettings); + $outputBufferingStopped = \false; + if (!isset($e) && $this->hasExpectationOnOutput() && $this->stopOutputBuffering()) { + $outputBufferingStopped = \true; + $this->performAssertionsOnOutput(); } - return $test; - } - /** @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) : \PHPUnit\Framework\DataProviderTestSuite - { - $dataProviderTestSuite = new \PHPUnit\Framework\DataProviderTestSuite($className . '::' . $methodName); - $groups = TestUtil::getGroups($className, $methodName); - if ($data instanceof \PHPUnit\Framework\ErrorTestCase || $data instanceof \PHPUnit\Framework\SkippedTestCase || $data instanceof \PHPUnit\Framework\IncompleteTestCase) { - $dataProviderTestSuite->addTest($data, $groups); - } else { - foreach ($data as $_dataName => $_data) { - $_test = new $className($methodName, $_data, $_dataName); - assert($_test instanceof \PHPUnit\Framework\TestCase); - $this->configureTestCase($_test, $runTestInSeparateProcess, $preserveGlobalState, $runClassInSeparateProcess, $backupSettings); - $dataProviderTestSuite->addTest($_test, $groups); + if ($this->status->isSuccess()) { + $emitter->testPassed($this->valueObjectForEvents()); + if (!$this->usesDataProvider()) { + PassedTests::instance()->testMethodPassed($this->valueObjectForEvents(), $this->testResult); } } - return $dataProviderTestSuite; - } - private function configureTestCase(\PHPUnit\Framework\TestCase $test, bool $runTestInSeparateProcess, ?bool $preserveGlobalState, bool $runClassInSeparateProcess, array $backupSettings) : void - { - if ($runTestInSeparateProcess) { - $test->setRunTestInSeparateProcess(\true); - if ($preserveGlobalState !== null) { - $test->setPreserveGlobalState($preserveGlobalState); - } + try { + $this->mockObjects = []; + } catch (Throwable $t) { + Event\Facade::emitter()->testErrored($this->valueObjectForEvents(), Event\Code\ThrowableBuilder::from($t)); } - if ($runClassInSeparateProcess) { - $test->setRunClassInSeparateProcess(\true); - if ($preserveGlobalState !== null) { - $test->setPreserveGlobalState($preserveGlobalState); + // Tear down the fixture. An exception raised in tearDown() will be + // caught and passed on when no exception was raised before. + try { + if ($hasMetRequirements) { + $this->invokeAfterTestHookMethods($hookMethods, $emitter); + if ($this->inIsolation) { + // @codeCoverageIgnoreStart + $this->invokeAfterClassHookMethods($hookMethods, $emitter); + // @codeCoverageIgnoreEnd + } + } + } catch (AssertionError|\PHPUnit\Framework\AssertionFailedError $e) { + $this->status = TestStatus::failure($e->getMessage()); + $emitter->testFailed($this->valueObjectForEvents(), Event\Code\ThrowableBuilder::from($e), Event\Code\ComparisonFailureBuilder::from($e)); + } catch (Throwable $exceptionRaisedDuringTearDown) { + if (!isset($e)) { + $this->status = TestStatus::error($exceptionRaisedDuringTearDown->getMessage()); + $e = $exceptionRaisedDuringTearDown; + $emitter->testErrored($this->valueObjectForEvents(), Event\Code\ThrowableBuilder::from($exceptionRaisedDuringTearDown)); } } - if ($backupSettings['backupGlobals'] !== null) { - $test->setBackupGlobals($backupSettings['backupGlobals']); + if (!$outputBufferingStopped) { + $this->stopOutputBuffering(); + } + clearstatcache(); + if ($currentWorkingDirectory !== getcwd()) { + chdir($currentWorkingDirectory); } - if ($backupSettings['backupStaticAttributes'] !== null) { - $test->setBackupStaticAttributes($backupSettings['backupStaticAttributes']); + $this->restoreGlobalState(); + $this->unregisterCustomComparators(); + $this->cleanupIniSettings(); + $this->cleanupLocaleSettings(); + libxml_clear_errors(); + $this->testValueObjectForEvents = null; + if (isset($e)) { + $this->onNotSuccessfulTest($e); } } - private function throwableToString(Throwable $t) : string + /** + * @psalm-param non-empty-string $name + * + * @internal This method is not covered by the backward compatibility promise for PHPUnit + */ + final public function setName(string $name): void { - $message = $t->getMessage(); - if (empty(trim($message))) { - $message = ''; - } - if ($t instanceof InvalidDataSetException) { - return sprintf("%s\n%s", $message, Filter::getFilteredStacktrace($t)); + $this->name = $name; + if (is_callable($this->sortId(), \true)) { + $this->providedTests = [new \PHPUnit\Framework\ExecutionOrderDependency($this->sortId())]; } - return sprintf("%s: %s\n%s", get_class($t), $message, Filter::getFilteredStacktrace($t)); } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework; - -use const LC_ALL; -use const LC_COLLATE; -use const LC_CTYPE; -use const LC_MONETARY; -use const LC_NUMERIC; -use const LC_TIME; -use const PATHINFO_FILENAME; -use const PHP_EOL; -use const PHP_URL_PATH; -use function array_filter; -use function array_flip; -use function array_keys; -use function array_merge; -use function array_pop; -use function array_search; -use function array_unique; -use function array_values; -use function basename; -use function call_user_func; -use function chdir; -use function class_exists; -use function clearstatcache; -use function count; -use function debug_backtrace; -use function defined; -use function explode; -use function get_class; -use function get_include_path; -use function getcwd; -use function implode; -use function in_array; -use function ini_set; -use function is_array; -use function is_callable; -use function is_int; -use function is_object; -use function is_string; -use function libxml_clear_errors; -use function method_exists; -use function ob_end_clean; -use function ob_get_contents; -use function ob_get_level; -use function ob_start; -use function parse_url; -use function pathinfo; -use function preg_replace; -use function serialize; -use function setlocale; -use function sprintf; -use function strpos; -use function substr; -use function sys_get_temp_dir; -use function tempnam; -use function trim; -use function var_export; -use PHPUnitPHAR\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\Constraint\LogicalOr; -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\Cloner; -use PHPUnit\Util\Exception as UtilException; -use PHPUnit\Util\GlobalState; -use PHPUnit\Util\PHP\AbstractPhpProcess; -use PHPUnit\Util\Test as TestUtil; -use Prophecy\Exception\Doubler\ClassNotFoundException; -use Prophecy\Exception\Doubler\DoubleException; -use Prophecy\Exception\Doubler\InterfaceNotFoundException; -use Prophecy\Exception\Prediction\PredictionException; -use Prophecy\Prophecy\MethodProphecy; -use Prophecy\Prophecy\ObjectProphecy; -use Prophecy\Prophet; -use ReflectionClass; -use ReflectionException; -use PHPUnitPHAR\SebastianBergmann\CodeCoverage\UnintentionallyCoveredCodeException; -use PHPUnitPHAR\SebastianBergmann\Comparator\Comparator; -use PHPUnitPHAR\SebastianBergmann\Comparator\Factory as ComparatorFactory; -use PHPUnitPHAR\SebastianBergmann\Diff\Differ; -use PHPUnitPHAR\SebastianBergmann\Exporter\Exporter; -use PHPUnitPHAR\SebastianBergmann\GlobalState\ExcludeList; -use PHPUnitPHAR\SebastianBergmann\GlobalState\Restorer; -use PHPUnitPHAR\SebastianBergmann\GlobalState\Snapshot; -use PHPUnitPHAR\SebastianBergmann\ObjectEnumerator\Enumerator; -use PHPUnitPHAR\SebastianBergmann\RecursionContext\InvalidArgumentException; -use PHPUnitPHAR\SebastianBergmann\Template\Template; -use SoapClient; -use Throwable; -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -abstract class TestCase extends \PHPUnit\Framework\Assert implements \PHPUnit\Framework\Reorderable, \PHPUnit\Framework\SelfDescribing, \PHPUnit\Framework\Test -{ - private const LOCALE_CATEGORIES = [LC_ALL, LC_COLLATE, LC_CTYPE, LC_MONETARY, LC_NUMERIC, LC_TIME]; - /** - * @var ?bool - */ - protected $backupGlobals; - /** - * @var string[] - */ - protected $backupGlobalsExcludeList = []; /** - * @var string[] + * @psalm-param list $dependencies * - * @deprecated Use $backupGlobalsExcludeList instead - */ - protected $backupGlobalsBlacklist = []; - /** - * @var ?bool - */ - protected $backupStaticAttributes; - /** - * @var array> + * @internal This method is not covered by the backward compatibility promise for PHPUnit */ - protected $backupStaticAttributesExcludeList = []; + final public function setDependencies(array $dependencies): void + { + $this->dependencies = $dependencies; + } /** - * @var array> + * @internal This method is not covered by the backward compatibility promise for PHPUnit * - * @deprecated Use $backupStaticAttributesExcludeList instead - */ - protected $backupStaticAttributesBlacklist = []; - /** - * @var ?bool - */ - protected $runTestInSeparateProcess; - /** - * @var bool - */ - protected $preserveGlobalState = \true; - /** - * @var list - */ - protected $providedTests = []; - /** - * @var ?bool - */ - private $runClassInSeparateProcess; - /** - * @var bool - */ - private $inIsolation = \false; - /** - * @var array - */ - private $data; - /** - * @var int|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 list - */ - private $dependencies = []; - /** - * @var array - */ - private $dependencyInput = []; - /** - * @var array - */ - 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 Prophet - */ - private $prophet; - /** - * @var bool - */ - private $beStrictAboutChangesToGlobalState = \false; - /** - * @var bool - */ - private $registerMockObjectsFromTestArgumentsRecursively = \false; - /** - * @var string[] - */ - private $warnings = []; - /** - * @var string[] - */ - private $groups = []; - /** - * @var bool + * @codeCoverageIgnore */ - private $doesNotPerformAssertions = \false; + final public function setDependencyInput(array $dependencyInput): void + { + $this->dependencyInput = $dependencyInput; + } /** - * @var Comparator[] + * @internal This method is not covered by the backward compatibility promise for PHPUnit */ - private $customComparators = []; + final public function dependencyInput(): array + { + return $this->dependencyInput; + } /** - * @var string[] + * @internal This method is not covered by the backward compatibility promise for PHPUnit */ - private $doubledTypes = []; + final public function hasDependencyInput(): bool + { + return !empty($this->dependencyInput); + } /** - * Returns a matcher that matches when the method is executed - * zero or more times. + * @internal This method is not covered by the backward compatibility promise for PHPUnit */ - public static function any() : AnyInvokedCountMatcher + final public function setBackupGlobals(bool $backupGlobals): void { - return new AnyInvokedCountMatcher(); + $this->backupGlobals = $backupGlobals; } /** - * Returns a matcher that matches when the method is never executed. + * @internal This method is not covered by the backward compatibility promise for PHPUnit */ - public static function never() : InvokedCountMatcher + final public function setBackupGlobalsExcludeList(array $backupGlobalsExcludeList): void { - return new InvokedCountMatcher(0); + $this->backupGlobalsExcludeList = $backupGlobalsExcludeList; } /** - * Returns a matcher that matches when the method is executed - * at least N times. + * @internal This method is not covered by the backward compatibility promise for PHPUnit */ - public static function atLeast(int $requiredInvocations) : InvokedAtLeastCountMatcher + final public function setBackupStaticProperties(bool $backupStaticProperties): void { - return new InvokedAtLeastCountMatcher($requiredInvocations); + $this->backupStaticProperties = $backupStaticProperties; } /** - * Returns a matcher that matches when the method is executed at least once. + * @internal This method is not covered by the backward compatibility promise for PHPUnit */ - public static function atLeastOnce() : InvokedAtLeastOnceMatcher + final public function setBackupStaticPropertiesExcludeList(array $backupStaticPropertiesExcludeList): void { - return new InvokedAtLeastOnceMatcher(); + $this->backupStaticPropertiesExcludeList = $backupStaticPropertiesExcludeList; } /** - * Returns a matcher that matches when the method is executed exactly once. + * @internal This method is not covered by the backward compatibility promise for PHPUnit */ - public static function once() : InvokedCountMatcher + final public function setRunTestInSeparateProcess(bool $runTestInSeparateProcess): void { - return new InvokedCountMatcher(1); + if ($this->runTestInSeparateProcess === null) { + $this->runTestInSeparateProcess = $runTestInSeparateProcess; + } } /** - * Returns a matcher that matches when the method is executed - * exactly $count times. + * @internal This method is not covered by the backward compatibility promise for PHPUnit */ - public static function exactly(int $count) : InvokedCountMatcher + final public function setRunClassInSeparateProcess(bool $runClassInSeparateProcess): void { - return new InvokedCountMatcher($count); + $this->runClassInSeparateProcess = $runClassInSeparateProcess; } /** - * Returns a matcher that matches when the method is executed - * at most N times. + * @internal This method is not covered by the backward compatibility promise for PHPUnit */ - public static function atMost(int $allowedInvocations) : InvokedAtMostCountMatcher + final public function setPreserveGlobalState(bool $preserveGlobalState): void { - return new InvokedAtMostCountMatcher($allowedInvocations); + $this->preserveGlobalState = $preserveGlobalState; } /** - * Returns a matcher that matches when the method is executed - * at the given index. - * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4297 + * @internal This method is not covered by the backward compatibility promise for PHPUnit * * @codeCoverageIgnore */ - public static function at(int $index) : InvokedAtIndexMatcher - { - $stack = debug_backtrace(); - while (!empty($stack)) { - $frame = array_pop($stack); - if (isset($frame['object']) && $frame['object'] instanceof self) { - $frame['object']->addWarning('The at() matcher has been deprecated. It will be removed in PHPUnit 10. Please refactor your test to not rely on the order in which methods are invoked.'); - break; - } - } - return new InvokedAtIndexMatcher($index); - } - public static function returnValue($value) : ReturnStub + final public function setInIsolation(bool $inIsolation): void { - return new ReturnStub($value); + $this->inIsolation = $inIsolation; } - public static function returnValueMap(array $valueMap) : ReturnValueMapStub + /** + * @internal This method is not covered by the backward compatibility promise for PHPUnit + * + * @codeCoverageIgnore + */ + final public function result(): mixed { - return new ReturnValueMapStub($valueMap); + return $this->testResult; } - public static function returnArgument(int $argumentIndex) : ReturnArgumentStub + /** + * @internal This method is not covered by the backward compatibility promise for PHPUnit + */ + final public function setResult(mixed $result): void { - return new ReturnArgumentStub($argumentIndex); + $this->testResult = $result; } - public static function returnCallback($callback) : ReturnCallbackStub + /** + * @internal This method is not covered by the backward compatibility promise for PHPUnit + */ + final public function registerMockObject(MockObject $mockObject): void { - return new ReturnCallbackStub($callback); + assert($mockObject instanceof MockObjectInternal); + $this->mockObjects[] = $mockObject; } /** - * Returns the current object. - * - * This method is useful when mocking a fluent interface. + * @internal This method is not covered by the backward compatibility promise for PHPUnit */ - public static function returnSelf() : ReturnSelfStub + final public function addToAssertionCount(int $count): void { - return new ReturnSelfStub(); + $this->numberOfAssertionsPerformed += $count; } - public static function throwException(Throwable $exception) : ExceptionStub + /** + * @internal This method is not covered by the backward compatibility promise for PHPUnit + */ + final public function numberOfAssertionsPerformed(): int { - return new ExceptionStub($exception); + return $this->numberOfAssertionsPerformed; } - public static function onConsecutiveCalls(...$args) : ConsecutiveCallsStub + /** + * @internal This method is not covered by the backward compatibility promise for PHPUnit + */ + final public function usesDataProvider(): bool { - return new ConsecutiveCallsStub($args); + return !empty($this->data); } /** - * @param int|string $dataName - * * @internal This method is not covered by the backward compatibility promise for PHPUnit */ - public function __construct(?string $name = null, array $data = [], $dataName = '') + final public function dataName(): int|string { - if ($name !== null) { - $this->setName($name); - } - $this->data = $data; - $this->dataName = $dataName; + return $this->dataName; } /** - * This method is called before the first test of this test class is run. + * @internal This method is not covered by the backward compatibility promise for PHPUnit */ - public static function setUpBeforeClass() : void + final public function dataSetAsString(): 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); + } + } + return $buffer; } /** - * This method is called after the last test of this test class is run. + * @internal This method is not covered by the backward compatibility promise for PHPUnit */ - public static function tearDownAfterClass() : void + final public function dataSetAsStringWithData(): string { + if (empty($this->data)) { + return ''; + } + return $this->dataSetAsString() . sprintf(' (%s)', (new Exporter())->shortenedRecursiveExport($this->data)); } /** - * This method is called before each test. + * @internal This method is not covered by the backward compatibility promise for PHPUnit */ - protected function setUp() : void + final public function providedData(): array { + return $this->data; } /** - * Performs assertions shared by all tests of a test case. - * - * This method is called between setUp() and test. + * @internal This method is not covered by the backward compatibility promise for PHPUnit */ - protected function assertPreConditions() : void + final public function sortId(): string { + $id = $this->name; + if (!str_contains($id, '::')) { + $id = static::class . '::' . $id; + } + if ($this->usesDataProvider()) { + $id .= $this->dataSetAsString(); + } + return $id; } /** - * Performs assertions shared by all tests of a test case. + * @psalm-return list * - * This method is called between test and tearDown(). + * @internal This method is not covered by the backward compatibility promise for PHPUnit */ - protected function assertPostConditions() : void + final public function provides(): array { + return $this->providedTests; } /** - * This method is called after each test. + * @psalm-return list + * + * @internal This method is not covered by the backward compatibility promise for PHPUnit */ - protected function tearDown() : void + final public function requires(): array { + return $this->dependencies; } /** - * Returns a string representation of the test case. + * @throws RuntimeException * - * @throws Exception - * @throws InvalidArgumentException + * @internal This method is not covered by the backward compatibility promise for PHPUnit */ - public function toString() : string + final public function setData(int|string $dataName, array $data): void { + $this->dataName = $dataName; + $this->data = $data; + if (array_is_list($data)) { + return; + } try { - $class = new ReflectionClass($this); + $reflector = new ReflectionMethod($this, $this->name); + $parameters = array_map(static fn(ReflectionParameter $parameter) => $parameter->name, $reflector->getParameters()); + foreach (array_keys($data) as $parameter) { + if (is_string($parameter) && !in_array($parameter, $parameters, \true)) { + Event\Facade::emitter()->testTriggeredPhpunitDeprecation($this->valueObjectForEvents(), sprintf('Providing invalid named argument $%s for method %s::%s() is deprecated and will not be supported in PHPUnit 11.0.', $parameter, $this::class, $this->name)); + } + } // @codeCoverageIgnoreStart } catch (ReflectionException $e) { - throw new \PHPUnit\Framework\Exception($e->getMessage(), $e->getCode(), $e); + throw new RuntimeException($e->getMessage(), $e->getCode(), $e); } // @codeCoverageIgnoreEnd - $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 + * @internal This method is not covered by the backward compatibility promise for PHPUnit + * + * @throws MoreThanOneDataSetFromDataProviderException */ - public function expectException(string $exception) : void + final public function valueObjectForEvents(): Event\Code\TestMethod { - // @codeCoverageIgnoreStart - switch ($exception) { - case Deprecated::class: - $this->addWarning('Expecting E_DEPRECATED and E_USER_DEPRECATED is deprecated and will no longer be possible in PHPUnit 10.'); - break; - case Error::class: - $this->addWarning('Expecting E_ERROR and E_USER_ERROR is deprecated and will no longer be possible in PHPUnit 10.'); - break; - case Notice::class: - $this->addWarning('Expecting E_STRICT, E_NOTICE, and E_USER_NOTICE is deprecated and will no longer be possible in PHPUnit 10.'); - break; - case WarningError::class: - $this->addWarning('Expecting E_WARNING and E_USER_WARNING is deprecated and will no longer be possible in PHPUnit 10.'); - break; + if ($this->testValueObjectForEvents !== null) { + return $this->testValueObjectForEvents; } - // @codeCoverageIgnoreEnd - $this->expectedException = $exception; + $this->testValueObjectForEvents = Event\Code\TestMethodBuilder::fromTestCase($this); + return $this->testValueObjectForEvents; } /** - * @param int|string $code + * @internal This method is not covered by the backward compatibility promise for PHPUnit */ - public function expectExceptionCode($code) : void - { - $this->expectedExceptionCode = $code; - } - public function expectExceptionMessage(string $message) : void + final public function wasPrepared(): bool { - $this->expectedExceptionMessage = $message; + return $this->wasPrepared; } - public function expectExceptionMessageMatches(string $regularExpression) : void + final protected function registerComparator(Comparator $comparator): void { - $this->expectedExceptionMessageRegExp = $regularExpression; + ComparatorFactory::getInstance()->register($comparator); + Event\Facade::emitter()->testRegisteredComparator($comparator::class); + $this->customComparators[] = $comparator; } /** - * 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. + * @psalm-param class-string $classOrInterface */ - public function expectExceptionObject(\Exception $exception) : void - { - $this->expectException(get_class($exception)); - $this->expectExceptionMessage($exception->getMessage()); - $this->expectExceptionCode($exception->getCode()); - } - public function expectNotToPerformAssertions() : void + final protected function registerFailureType(string $classOrInterface): void { - $this->doesNotPerformAssertions = \true; + $this->failureTypes[$classOrInterface] = \true; } /** - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/5062 + * @throws AssertionFailedError + * @throws Exception + * @throws ExpectationFailedException + * @throws Throwable + * + * @internal This method is not covered by the backward compatibility promise for PHPUnit */ - public function expectDeprecation() : void + protected function runTest(): mixed { - $this->addWarning('Expecting E_DEPRECATED and E_USER_DEPRECATED is deprecated and will no longer be possible in PHPUnit 10.'); - $this->expectedException = Deprecated::class; + $testArguments = array_merge($this->data, $this->dependencyInput); + $this->registerMockObjectsFromTestArguments($testArguments); + try { + $testResult = $this->{$this->name}(...array_values($testArguments)); + } catch (Throwable $exception) { + if (!$this->shouldExceptionExpectationsBeVerified($exception)) { + throw $exception; + } + $this->verifyExceptionExpectations($exception); + return null; + } + $this->expectedExceptionWasNotRaised(); + return $testResult; } /** - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/5062 + * 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 + * + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/5214 + * + * @codeCoverageIgnore */ - public function expectDeprecationMessage(string $message) : void + protected function iniSet(string $varName, string $newValue): void { - $this->addWarning('Expecting E_DEPRECATED and E_USER_DEPRECATED is deprecated and will no longer be possible in PHPUnit 10.'); - $this->expectExceptionMessage($message); + $currentValue = ini_set($varName, $newValue); + if ($currentValue !== \false) { + $this->iniSettings[$varName] = $currentValue; + } else { + throw new \PHPUnit\Framework\Exception(sprintf('INI setting "%s" could not be set to "%s".', $varName, $newValue)); + } } /** - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/5062 + * 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 + * + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/5216 + * + * @codeCoverageIgnore */ - public function expectDeprecationMessageMatches(string $regularExpression) : void + protected function setLocale(mixed ...$arguments): void { - $this->addWarning('Expecting E_DEPRECATED and E_USER_DEPRECATED is deprecated and will no longer be possible in PHPUnit 10.'); - $this->expectExceptionMessageMatches($regularExpression); + if (count($arguments) < 2) { + throw new \PHPUnit\Framework\Exception(); + } + [$category, $locale] = $arguments; + if (!in_array($category, self::LOCALE_CATEGORIES, \true)) { + throw new \PHPUnit\Framework\Exception(); + } + if (!is_array($locale) && !is_string($locale)) { + throw new \PHPUnit\Framework\Exception(); + } + $this->locale[$category] = setlocale($category, '0'); + $result = setlocale(...$arguments); + if ($result === \false) { + throw new \PHPUnit\Framework\Exception('The locale functionality is not implemented on your platform, ' . 'the specified locale does not exist or the category name is ' . 'invalid.'); + } } /** - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/5062 + * Creates a mock object for the specified interface or class. + * + * @psalm-template RealInstanceType of object + * + * @psalm-param class-string $originalClassName + * + * @psalm-return MockObject&RealInstanceType + * + * @throws InvalidArgumentException + * @throws MockObjectException + * @throws NoPreviousThrowableException */ - public function expectNotice() : void + protected function createMock(string $originalClassName): MockObject { - $this->addWarning('Expecting E_STRICT, E_NOTICE, and E_USER_NOTICE is deprecated and will no longer be possible in PHPUnit 10.'); - $this->expectedException = Notice::class; + $mock = (new MockGenerator())->testDouble($originalClassName, \true, callOriginalConstructor: \false, callOriginalClone: \false, cloneArguments: \false, allowMockingUnknownTypes: \false); + assert($mock instanceof $originalClassName); + assert($mock instanceof MockObject); + $this->registerMockObject($mock); + Event\Facade::emitter()->testCreatedMockObject($originalClassName); + return $mock; } /** - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/5062 + * @psalm-param list $interfaces + * + * @throws MockObjectException */ - public function expectNoticeMessage(string $message) : void + protected function createMockForIntersectionOfInterfaces(array $interfaces): MockObject { - $this->addWarning('Expecting E_STRICT, E_NOTICE, and E_USER_NOTICE is deprecated and will no longer be possible in PHPUnit 10.'); - $this->expectExceptionMessage($message); + $mock = (new MockGenerator())->testDoubleForInterfaceIntersection($interfaces, \true); + assert($mock instanceof MockObject); + $this->registerMockObject($mock); + Event\Facade::emitter()->testCreatedMockObjectForIntersectionOfInterfaces($interfaces); + return $mock; } /** - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/5062 + * Creates (and configures) a mock object for the specified interface or class. + * + * @psalm-template RealInstanceType of object + * + * @psalm-param class-string $originalClassName + * + * @psalm-return MockObject&RealInstanceType + * + * @throws InvalidArgumentException + * @throws MockObjectException + * @throws NoPreviousThrowableException */ - public function expectNoticeMessageMatches(string $regularExpression) : void + protected function createConfiguredMock(string $originalClassName, array $configuration): MockObject { - $this->addWarning('Expecting E_STRICT, E_NOTICE, and E_USER_NOTICE is deprecated and will no longer be possible in PHPUnit 10.'); - $this->expectExceptionMessageMatches($regularExpression); + $o = $this->createMock($originalClassName); + foreach ($configuration as $method => $return) { + $o->method($method)->willReturn($return); + } + return $o; } /** - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/5062 + * Creates a partial mock object for the specified interface or class. + * + * @psalm-param list $methods + * + * @psalm-template RealInstanceType of object + * + * @psalm-param class-string $originalClassName + * + * @psalm-return MockObject&RealInstanceType + * + * @throws InvalidArgumentException + * @throws MockObjectException */ - public function expectWarning() : void + protected function createPartialMock(string $originalClassName, array $methods): MockObject { - $this->addWarning('Expecting E_WARNING and E_USER_WARNING is deprecated and will no longer be possible in PHPUnit 10.'); - $this->expectedException = WarningError::class; + $partialMock = $this->getMockBuilder($originalClassName)->disableOriginalConstructor()->disableOriginalClone()->disableArgumentCloning()->disallowMockingUnknownTypes()->onlyMethods($methods)->getMock(); + Event\Facade::emitter()->testCreatedPartialMockObject($originalClassName, ...$methods); + return $partialMock; } /** - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/5062 + * Creates a test proxy for the specified class. + * + * @psalm-template RealInstanceType of object + * + * @psalm-param class-string $originalClassName + * + * @psalm-return MockObject&RealInstanceType + * + * @throws InvalidArgumentException + * @throws MockObjectException + * + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/5240 */ - public function expectWarningMessage(string $message) : void + protected function createTestProxy(string $originalClassName, array $constructorArguments = []): MockObject { - $this->addWarning('Expecting E_WARNING and E_USER_WARNING is deprecated and will no longer be possible in PHPUnit 10.'); - $this->expectExceptionMessage($message); + $testProxy = $this->getMockBuilder($originalClassName)->setConstructorArgs($constructorArguments)->enableProxyingToOriginalMethods()->getMock(); + Event\Facade::emitter()->testCreatedTestProxy($originalClassName, $constructorArguments); + return $testProxy; } /** - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/5062 + * Creates 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). + * + * @psalm-template RealInstanceType of object + * + * @psalm-param class-string $originalClassName + * + * @psalm-return MockObject&RealInstanceType + * + * @throws InvalidArgumentException + * @throws MockObjectException + * + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/5241 */ - public function expectWarningMessageMatches(string $regularExpression) : void + protected function getMockForAbstractClass(string $originalClassName, array $arguments = [], string $mockClassName = '', bool $callOriginalConstructor = \true, bool $callOriginalClone = \true, bool $callAutoload = \true, array $mockedMethods = [], bool $cloneArguments = \false): MockObject { - $this->addWarning('Expecting E_WARNING and E_USER_WARNING is deprecated and will no longer be possible in PHPUnit 10.'); - $this->expectExceptionMessageMatches($regularExpression); + $mockObject = (new MockGenerator())->mockObjectForAbstractClass($originalClassName, $arguments, $mockClassName, $callOriginalConstructor, $callOriginalClone, $callAutoload, $mockedMethods, $cloneArguments); + $this->registerMockObject($mockObject); + Event\Facade::emitter()->testCreatedMockObjectForAbstractClass($originalClassName); + assert($mockObject instanceof $originalClassName); + assert($mockObject instanceof MockObject); + return $mockObject; } /** - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/5062 + * Creates a mock object based on the given WSDL file. + * + * @throws MockObjectException + * + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/5242 */ - public function expectError() : void + protected function getMockFromWsdl(string $wsdlFile, string $originalClassName = '', string $mockClassName = '', array $methods = [], bool $callOriginalConstructor = \true, array $options = []): MockObject { - $this->addWarning('Expecting E_ERROR and E_USER_ERROR is deprecated and will no longer be possible in PHPUnit 10.'); - $this->expectedException = Error::class; + if ($originalClassName === '') { + $fileName = pathinfo(basename(parse_url($wsdlFile, PHP_URL_PATH)), PATHINFO_FILENAME); + $originalClassName = preg_replace('/\W/', '', $fileName); + } + if (!class_exists($originalClassName)) { + eval((new MockGenerator())->generateClassFromWsdl($wsdlFile, $originalClassName, $methods, $options)); + } + $mockObject = (new MockGenerator())->testDouble($originalClassName, \true, $methods, ['', $options], $mockClassName, $callOriginalConstructor, \false, \false); + Event\Facade::emitter()->testCreatedMockObjectFromWsdl($wsdlFile, $originalClassName, $mockClassName, $methods, $callOriginalConstructor, $options); + assert($mockObject instanceof MockObject); + $this->registerMockObject($mockObject); + return $mockObject; } /** - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/5062 + * Creates 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. + * + * @psalm-param trait-string $traitName + * + * @throws InvalidArgumentException + * @throws MockObjectException + * + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/5243 */ - public function expectErrorMessage(string $message) : void + protected function getMockForTrait(string $traitName, array $arguments = [], string $mockClassName = '', bool $callOriginalConstructor = \true, bool $callOriginalClone = \true, bool $callAutoload = \true, array $mockedMethods = [], bool $cloneArguments = \false): MockObject { - $this->addWarning('Expecting E_ERROR and E_USER_ERROR is deprecated and will no longer be possible in PHPUnit 10.'); - $this->expectExceptionMessage($message); + $mockObject = (new MockGenerator())->mockObjectForTrait($traitName, $arguments, $mockClassName, $callOriginalConstructor, $callOriginalClone, $callAutoload, $mockedMethods, $cloneArguments); + $this->registerMockObject($mockObject); + Event\Facade::emitter()->testCreatedMockObjectForTrait($traitName); + return $mockObject; } /** - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/5062 + * Creates an object that uses the specified trait. + * + * @psalm-param trait-string $traitName + * + * @throws MockObjectException + * + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/5244 */ - public function expectErrorMessageMatches(string $regularExpression) : void + protected function getObjectForTrait(string $traitName, array $arguments = [], string $traitClassName = '', bool $callOriginalConstructor = \true, bool $callOriginalClone = \true, bool $callAutoload = \true): object { - $this->addWarning('Expecting E_ERROR and E_USER_ERROR is deprecated and will no longer be possible in PHPUnit 10.'); - $this->expectExceptionMessageMatches($regularExpression); + return (new MockGenerator())->objectForTrait($traitName, $traitClassName, $callAutoload, $callOriginalConstructor, $arguments); } - public function getStatus() : int + protected function transformException(Throwable $t): Throwable { - return $this->status; + return $t; } - public function markAsRisky() : void + /** + * This method is called when a test method did not execute successfully. + * + * @throws Throwable + */ + protected function onNotSuccessfulTest(Throwable $t): never { - $this->status = BaseTestRunner::STATUS_RISKY; + throw $t; } - public function getStatusMessage() : string - { - return $this->statusMessage; - } - public function hasFailed() : bool + /** + * @throws Throwable + */ + private function verifyMockObjects(): void { - $status = $this->getStatus(); - return $status === BaseTestRunner::STATUS_FAILURE || $status === BaseTestRunner::STATUS_ERROR; + foreach ($this->mockObjects as $mockObject) { + if ($mockObject->__phpunit_hasMatchers()) { + $this->numberOfAssertionsPerformed++; + } + $mockObject->__phpunit_verify($this->shouldInvocationMockerBeReset($mockObject)); + } } /** - * 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 \SebastianBergmann\CodeCoverage\InvalidArgumentException - * @throws CodeCoverageException - * @throws InvalidArgumentException - * @throws UnintentionallyCoveredCodeException - * @throws UtilException + * @throws SkippedTest */ - public function run(?\PHPUnit\Framework\TestResult $result = null) : \PHPUnit\Framework\TestResult + private function checkRequirements(): void { - if ($result === null) { - $result = $this->createResult(); + if (!$this->name || !method_exists($this, $this->name)) { + return; } - if (!$this instanceof \PHPUnit\Framework\ErrorTestCase && !$this instanceof \PHPUnit\Framework\WarningTestCase) { - $this->setTestResultObject($result); + $missingRequirements = (new Requirements())->requirementsNotSatisfiedFor(static::class, $this->name); + if (!empty($missingRequirements)) { + $this->markTestSkipped(implode(PHP_EOL, $missingRequirements)); } - if (!$this instanceof \PHPUnit\Framework\ErrorTestCase && !$this instanceof \PHPUnit\Framework\WarningTestCase && !$this instanceof \PHPUnit\Framework\SkippedTestCase && !$this->handleDependencies()) { - return $result; + } + private function handleDependencies(): bool + { + if ([] === $this->dependencies || $this->inIsolation) { + return \true; } - if ($this->runInSeparateProcess()) { - $runEntireClass = $this->runClassInSeparateProcess && !$this->runTestInSeparateProcess; - try { - $class = new ReflectionClass($this); - // @codeCoverageIgnoreStart - } catch (ReflectionException $e) { - throw new \PHPUnit\Framework\Exception($e->getMessage(), $e->getCode(), $e); + $passedTests = PassedTests::instance(); + foreach ($this->dependencies as $dependency) { + if (!$dependency->isValid()) { + $this->markErrorForInvalidDependency(); + return \false; } - // @codeCoverageIgnoreEnd - if ($runEntireClass) { - $template = new Template(__DIR__ . '/../Util/PHP/Template/TestCaseClass.tpl'); - } else { - $template = new Template(__DIR__ . '/../Util/PHP/Template/TestCaseMethod.tpl'); + if ($dependency->targetIsClass()) { + $dependencyClassName = $dependency->getTargetClassName(); + if (!class_exists($dependencyClassName)) { + $this->markErrorForInvalidDependency($dependency); + return \false; + } + if (!$passedTests->hasTestClassPassed($dependencyClassName)) { + $this->markSkippedForMissingDependency($dependency); + return \false; + } + continue; } - 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"; + $dependencyTarget = $dependency->getTarget(); + if (!$passedTests->hasTestMethodPassed($dependencyTarget)) { + if (!$this->isCallableTestMethod($dependencyTarget)) { + $this->markErrorForInvalidDependency($dependency); } else { - $globals = ''; + $this->markSkippedForMissingDependency($dependency); } - $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 = '\'\''; + return \false; } - if (defined('__PHPUNIT_PHAR__')) { - $phar = var_export(__PHPUNIT_PHAR__, \true); - } else { - $phar = '\'\''; + if ($passedTests->isGreaterThan($dependencyTarget, $this->size())) { + Event\Facade::emitter()->testConsideredRisky($this->valueObjectForEvents(), 'This test depends on a test that is larger than itself'); + return \false; } - $codeCoverage = $result->getCodeCoverage(); - $codeCoverageFilter = null; - $cachesStaticAnalysis = 'false'; - $codeCoverageCacheDirectory = null; - $driverMethod = 'forLineCoverage'; - if ($codeCoverage) { - $codeCoverageFilter = $codeCoverage->filter(); - if ($codeCoverage->collectsBranchAndPathCoverage()) { - $driverMethod = 'forLineAndPathCoverage'; - } - if ($codeCoverage->cachesStaticAnalysis()) { - $cachesStaticAnalysis = 'true'; - $codeCoverageCacheDirectory = $codeCoverage->cacheDirectory(); - } + $returnValue = $passedTests->returnValue($dependencyTarget); + if ($dependency->deepClone()) { + $deepCopy = new DeepCopy(); + $deepCopy->skipUncloneable(\false); + $this->dependencyInput[$dependencyTarget] = $deepCopy->copy($returnValue); + } elseif ($dependency->shallowClone()) { + $this->dependencyInput[$dependencyTarget] = clone $returnValue; + } else { + $this->dependencyInput[$dependencyTarget] = $returnValue; } - $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); - $codeCoverageCacheDirectory = var_export(serialize($codeCoverageCacheDirectory), \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 . ".'"; - $codeCoverageCacheDirectory = "'." . $codeCoverageCacheDirectory . ".'"; - $configurationFilePath = $GLOBALS['__PHPUNIT_CONFIGURATION_FILE'] ?? ''; - $processResultFile = tempnam(sys_get_temp_dir(), 'phpunit_'); - $var = ['composerAutoload' => $composerAutoload, 'phar' => $phar, 'filename' => $class->getFileName(), 'className' => $class->getName(), 'collectCodeCoverageInformation' => $coverage, 'cachesStaticAnalysis' => $cachesStaticAnalysis, 'codeCoverageCacheDirectory' => $codeCoverageCacheDirectory, 'driverMethod' => $driverMethod, '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), 'processResultFile' => $processResultFile]; - if (!$runEntireClass) { - $var['methodName'] = $this->name; - } - $template->setVar($var); - $php = AbstractPhpProcess::factory(); - $php->runTestJob($template->render(), $this, $result, $processResultFile); - } else { - $result->run($this); } - $this->result = null; - return $result; - } - /** - * Returns a builder object to create mock objects using a fluent interface. - * - * @psalm-template RealInstanceType of object - * - * @psalm-param class-string $className - * - * @psalm-return MockBuilder - */ - public function getMockBuilder(string $className) : MockBuilder - { - $this->recordDoubledType($className); - return new MockBuilder($this, $className); - } - public function registerComparator(Comparator $comparator) : void - { - ComparatorFactory::getInstance()->register($comparator); - $this->customComparators[] = $comparator; + $this->testValueObjectForEvents = null; + return \true; } /** - * @return string[] - * - * @internal This method is not covered by the backward compatibility promise for PHPUnit + * @throws Exception + * @throws MoreThanOneDataSetFromDataProviderException + * @throws NoPreviousThrowableException */ - public function doubledTypes() : array + private function markErrorForInvalidDependency(?\PHPUnit\Framework\ExecutionOrderDependency $dependency = null): void { - return array_unique($this->doubledTypes); + $message = 'This test has an invalid dependency'; + if ($dependency !== null) { + $message = sprintf('This test depends on "%s" which does not exist', $dependency->targetIsClass() ? $dependency->getTargetClassName() : $dependency->getTarget()); + } + $exception = new \PHPUnit\Framework\InvalidDependencyException($message); + Event\Facade::emitter()->testErrored($this->valueObjectForEvents(), Event\Code\ThrowableBuilder::from($exception)); + $this->status = TestStatus::error($message); } /** - * @internal This method is not covered by the backward compatibility promise for PHPUnit + * @throws MoreThanOneDataSetFromDataProviderException */ - public function getGroups() : array + private function markSkippedForMissingDependency(\PHPUnit\Framework\ExecutionOrderDependency $dependency): void { - return $this->groups; + $message = sprintf('This test depends on "%s" to pass', $dependency->getTarget()); + Event\Facade::emitter()->testSkipped($this->valueObjectForEvents(), $message); + $this->status = TestStatus::skipped($message); } - /** - * @internal This method is not covered by the backward compatibility promise for PHPUnit - */ - public function setGroups(array $groups) : void + private function startOutputBuffering(): void { - $this->groups = $groups; + ob_start(); + $this->outputBufferingActive = \true; + $this->outputBufferingLevel = ob_get_level(); } /** - * @throws InvalidArgumentException - * - * @internal This method is not covered by the backward compatibility promise for PHPUnit + * @throws MoreThanOneDataSetFromDataProviderException */ - public function getName(bool $withDataSet = \true) : string + private function stopOutputBuffering(): bool { - if ($withDataSet) { - return $this->name . $this->getDataSetAsString(\false); + $bufferingLevel = ob_get_level(); + if ($bufferingLevel !== $this->outputBufferingLevel) { + if ($bufferingLevel > $this->outputBufferingLevel) { + $message = 'Test code or tested code did not close its own output buffers'; + } else { + $message = 'Test code or tested code closed output buffers other than its own'; + } + while (ob_get_level() >= $this->outputBufferingLevel) { + ob_end_clean(); + } + Event\Facade::emitter()->testConsideredRisky($this->valueObjectForEvents(), $message); + $this->status = TestStatus::risky($message); + return \false; } - return $this->name; - } - /** - * Returns the size of the test. - * - * @throws InvalidArgumentException - * - * @internal This method is not covered by the backward compatibility promise for PHPUnit - */ - public function getSize() : int - { - return TestUtil::getSize(static::class, $this->getName(\false)); + $this->output = ob_get_clean(); + $this->outputBufferingActive = \false; + $this->outputBufferingLevel = ob_get_level(); + return \true; } - /** - * @throws InvalidArgumentException - * - * @internal This method is not covered by the backward compatibility promise for PHPUnit - */ - public function hasSize() : bool + private function snapshotGlobalState(): void { - return $this->getSize() !== TestUtil::UNKNOWN; + if ($this->runTestInSeparateProcess || $this->inIsolation || !$this->backupGlobals && !$this->backupStaticProperties) { + return; + } + $snapshot = $this->createGlobalStateSnapshot($this->backupGlobals === \true); + $this->snapshot = $snapshot; } /** - * @throws InvalidArgumentException - * - * @internal This method is not covered by the backward compatibility promise for PHPUnit + * @throws MoreThanOneDataSetFromDataProviderException */ - public function isSmall() : bool + private function restoreGlobalState(): void { - return $this->getSize() === TestUtil::SMALL; + if (!$this->snapshot instanceof Snapshot) { + return; + } + if (ConfigurationRegistry::get()->beStrictAboutChangesToGlobalState()) { + $this->compareGlobalStateSnapshots($this->snapshot, $this->createGlobalStateSnapshot($this->backupGlobals === \true)); + } + $restorer = new Restorer(); + if ($this->backupGlobals) { + $restorer->restoreGlobalVariables($this->snapshot); + } + if ($this->backupStaticProperties) { + $restorer->restoreStaticProperties($this->snapshot); + } + $this->snapshot = null; } - /** - * @throws InvalidArgumentException - * - * @internal This method is not covered by the backward compatibility promise for PHPUnit - */ - public function isMedium() : bool + private function createGlobalStateSnapshot(bool $backupGlobals): Snapshot { - return $this->getSize() === TestUtil::MEDIUM; + $excludeList = new GlobalStateExcludeList(); + foreach ($this->backupGlobalsExcludeList as $globalVariable) { + $excludeList->addGlobalVariable($globalVariable); + } + if (!defined('PHPUNIT_TESTSUITE')) { + $excludeList->addClassNamePrefix('PHPUnit'); + $excludeList->addClassNamePrefix('PHPUnitPHAR\SebastianBergmann\CodeCoverage'); + $excludeList->addClassNamePrefix('PHPUnitPHAR\SebastianBergmann\FileIterator'); + $excludeList->addClassNamePrefix('PHPUnitPHAR\SebastianBergmann\Invoker'); + $excludeList->addClassNamePrefix('PHPUnitPHAR\SebastianBergmann\Template'); + $excludeList->addClassNamePrefix('PHPUnitPHAR\SebastianBergmann\Timer'); + $excludeList->addStaticProperty(ComparatorFactory::class, 'instance'); + foreach ($this->backupStaticPropertiesExcludeList as $class => $properties) { + foreach ($properties as $property) { + $excludeList->addStaticProperty($class, $property); + } + } + } + return new Snapshot($excludeList, $backupGlobals, (bool) $this->backupStaticProperties, \false, \false, \false, \false, \false, \false, \false); } /** - * @throws InvalidArgumentException - * - * @internal This method is not covered by the backward compatibility promise for PHPUnit + * @throws MoreThanOneDataSetFromDataProviderException */ - public function isLarge() : bool + private function compareGlobalStateSnapshots(Snapshot $before, Snapshot $after): void { - return $this->getSize() === TestUtil::LARGE; + $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->backupStaticProperties) { + $this->compareGlobalStateSnapshotPart($before->staticProperties(), $after->staticProperties(), "--- Static properties before the test\n+++ Static properties after the test\n"); + } } /** - * @internal This method is not covered by the backward compatibility promise for PHPUnit + * @throws MoreThanOneDataSetFromDataProviderException */ - public function getActualOutput() : string + private function compareGlobalStateSnapshotPart(array $before, array $after, string $header): void { - if (!$this->outputBufferingActive) { - return $this->output; + if ($before != $after) { + $differ = new Differ(new UnifiedDiffOutputBuilder($header)); + $exporter = new Exporter(); + Event\Facade::emitter()->testConsideredRisky($this->valueObjectForEvents(), 'This test modified global state but was not expected to do so' . PHP_EOL . trim($differ->diff($exporter->export($before), $exporter->export($after)))); } - return (string) ob_get_contents(); } - /** - * @internal This method is not covered by the backward compatibility promise for PHPUnit - */ - public function hasOutput() : bool + private function shouldInvocationMockerBeReset(MockObject $mock): bool { - if ($this->output === '') { + $enumerator = new Enumerator(); + if (in_array($mock, $enumerator->enumerate($this->dependencyInput), \true)) { return \false; } - if ($this->hasExpectationOnOutput()) { - return \false; + if (!is_array($this->testResult) && !is_object($this->testResult)) { + return \true; } - 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 !in_array($mock, $enumerator->enumerate($this->testResult), \true); } /** - * @return null|int|string - * - * @internal This method is not covered by the backward compatibility promise for PHPUnit + * @deprecated */ - public function getExpectedExceptionCode() + private function registerMockObjectsFromTestArguments(array $testArguments, Context $context = new Context()): void { - return $this->expectedExceptionCode; + 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) { + $testArgument = Cloner::clone($testArgument); + $this->registerMockObject($testArgument); + } elseif (is_array($testArgument) && !$context->contains($testArgument)) { + $testArgumentCopy = $testArgument; + $context->add($testArgument); + $this->registerMockObjectsFromTestArguments($testArgumentCopy, $context); + } + } + } } - /** - * @internal This method is not covered by the backward compatibility promise for PHPUnit - */ - public function getExpectedExceptionMessage() : ?string + private function unregisterCustomComparators(): void { - return $this->expectedExceptionMessage; + $factory = ComparatorFactory::getInstance(); + foreach ($this->customComparators as $comparator) { + $factory->unregister($comparator); + } + $this->customComparators = []; } - /** - * @internal This method is not covered by the backward compatibility promise for PHPUnit - */ - public function getExpectedExceptionMessageRegExp() : ?string + private function cleanupIniSettings(): void { - return $this->expectedExceptionMessageRegExp; + foreach ($this->iniSettings as $varName => $oldValue) { + ini_set($varName, $oldValue); + } + $this->iniSettings = []; } - /** - * @internal This method is not covered by the backward compatibility promise for PHPUnit - */ - public function setRegisterMockObjectsFromTestArgumentsRecursively(bool $flag) : void + private function cleanupLocaleSettings(): void { - $this->registerMockObjectsFromTestArgumentsRecursively = $flag; + foreach ($this->locale as $category => $locale) { + setlocale($category, $locale); + } + $this->locale = []; } /** - * @throws Throwable - * - * @internal This method is not covered by the backward compatibility promise for PHPUnit + * @throws Exception */ - public function runBare() : void + private function shouldExceptionExpectationsBeVerified(Throwable $throwable): bool { - $this->numAssertions = 0; - $this->snapshotGlobalState(); - $this->startOutputBuffering(); - clearstatcache(); - $currentWorkingDirectory = getcwd(); - $hookMethods = TestUtil::getHookMethods(static::class); - $hasMetRequirements = \false; - try { - $this->checkRequirements(); - $hasMetRequirements = \true; - if ($this->inIsolation) { - foreach ($hookMethods['beforeClass'] as $method) { - $this->{$method}(); - } - } - $this->setDoesNotPerformAssertionsFromAnnotation(); - foreach ($hookMethods['before'] as $method) { - $this->{$method}(); - } - foreach ($hookMethods['preCondition'] as $method) { - $this->{$method}(); - } - $this->testResult = $this->runTest(); - $this->verifyMockObjects(); - foreach ($hookMethods['postCondition'] as $method) { - $this->{$method}(); - } - if (!empty($this->warnings)) { - throw new \PHPUnit\Framework\Warning(implode("\n", array_unique($this->warnings))); - } - $this->status = BaseTestRunner::STATUS_PASSED; - } catch (\PHPUnit\Framework\IncompleteTest $e) { - $this->status = BaseTestRunner::STATUS_INCOMPLETE; - $this->statusMessage = $e->getMessage(); - } catch (\PHPUnit\Framework\SkippedTest $e) { - $this->status = BaseTestRunner::STATUS_SKIPPED; - $this->statusMessage = $e->getMessage(); - } catch (\PHPUnit\Framework\Warning $e) { - $this->status = BaseTestRunner::STATUS_WARNING; - $this->statusMessage = $e->getMessage(); - } catch (\PHPUnit\Framework\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 (\PHPUnit\Framework\RiskyTestError $_e) { - $e = $e ?? $_e; - } - if (isset($_e)) { - $this->status = BaseTestRunner::STATUS_ERROR; - $this->statusMessage = $_e->getMessage(); + $result = \false; + if ($this->expectedException !== null || $this->expectedExceptionCode !== null || $this->expectedExceptionMessage !== null || $this->expectedExceptionMessageRegExp !== null) { + $result = \true; } - clearstatcache(); - if ($currentWorkingDirectory !== getcwd()) { - chdir($currentWorkingDirectory); + if ($throwable instanceof \PHPUnit\Framework\Exception) { + $result = \false; } - $this->restoreGlobalState(); - $this->unregisterCustomComparators(); - $this->cleanupIniSettings(); - $this->cleanupLocaleSettings(); - libxml_clear_errors(); - // Perform assertion on output. - if (!isset($e)) { + if (is_string($this->expectedException)) { try { - if ($this->outputExpectedRegex !== null) { - $this->assertMatchesRegularExpression($this->outputExpectedRegex, $this->output); - } elseif ($this->outputExpectedString !== null) { - $this->assertEquals($this->outputExpectedString, $this->output); - } - } catch (Throwable $_e) { - $e = $_e; + $reflector = new ReflectionClass($this->expectedException); + // @codeCoverageIgnoreStart + } catch (ReflectionException $e) { + throw new \PHPUnit\Framework\Exception($e->getMessage(), $e->getCode(), $e); } - } - // Workaround for missing "finally". - if (isset($e)) { - if ($e instanceof PredictionException) { - $e = new \PHPUnit\Framework\AssertionFailedError($e->getMessage()); + // @codeCoverageIgnoreEnd + if ($this->expectedException === 'PHPUnit\Framework\Exception' || $this->expectedException === '\PHPUnit\Framework\Exception' || $reflector->isSubclassOf(\PHPUnit\Framework\Exception::class)) { + $result = \true; } - $this->onNotSuccessfulTest($e); } + return $result; } - /** - * @internal This method is not covered by the backward compatibility promise for PHPUnit - */ - public function setName(string $name) : void + private function shouldRunInSeparateProcess(): bool { - $this->name = $name; - if (is_callable($this->sortId(), \true)) { - $this->providedTests = [new \PHPUnit\Framework\ExecutionOrderDependency($this->sortId())]; + if ($this->inIsolation) { + return \false; } - } - /** - * @param list $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 setDependencyInput(array $dependencyInput) : void - { - $this->dependencyInput = $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; + if ($this->runTestInSeparateProcess) { + return \true; } - } - /** - * @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; + if ($this->runClassInSeparateProcess) { + return \true; } + return ConfigurationRegistry::get()->processIsolation(); } - /** - * @internal This method is not covered by the backward compatibility promise for PHPUnit - */ - public function setRunTestInSeparateProcess(bool $runTestInSeparateProcess) : void + private function isCallableTestMethod(string $dependency): bool { - if ($this->runTestInSeparateProcess === null) { - $this->runTestInSeparateProcess = $runTestInSeparateProcess; + [$className, $methodName] = explode('::', $dependency); + if (!class_exists($className)) { + return \false; } - } - /** - * @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; + $class = new ReflectionClass($className); + if (!$class->isSubclassOf(__CLASS__)) { + return \false; } + if (!$class->hasMethod($methodName)) { + return \false; + } + return TestUtil::isTestMethod($class->getMethod($methodName)); } /** - * @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 + * @throws Exception + * @throws ExpectationFailedException + * @throws MoreThanOneDataSetFromDataProviderException + * @throws NoPreviousThrowableException */ - public function setResult($result) : void + private function performAssertionsOnOutput(): void { - $this->testResult = $result; + try { + if ($this->outputExpectedRegex !== null) { + $this->assertMatchesRegularExpression($this->outputExpectedRegex, $this->output); + } elseif ($this->outputExpectedString !== null) { + $this->assertSame($this->outputExpectedString, $this->output); + } + } catch (\PHPUnit\Framework\ExpectationFailedException $e) { + $this->status = TestStatus::failure($e->getMessage()); + Event\Facade::emitter()->testFailed($this->valueObjectForEvents(), Event\Code\ThrowableBuilder::from($e), Event\Code\ComparisonFailureBuilder::from($e)); + throw $e; + } } /** - * @internal This method is not covered by the backward compatibility promise for PHPUnit + * @throws Throwable + * + * @codeCoverageIgnore */ - public function setOutputCallback(callable $callback) : void + private function invokeBeforeClassHookMethods(array $hookMethods, Event\Emitter $emitter): void { - $this->outputCallback = $callback; + $this->invokeHookMethods($hookMethods['beforeClass'], $emitter, 'testBeforeFirstTestMethodCalled', 'testBeforeFirstTestMethodFinished'); } /** - * @internal This method is not covered by the backward compatibility promise for PHPUnit + * @throws Throwable */ - public function getTestResultObject() : ?\PHPUnit\Framework\TestResult + private function invokeBeforeTestHookMethods(array $hookMethods, Event\Emitter $emitter): void { - return $this->result; + $this->invokeHookMethods($hookMethods['before'], $emitter, 'testBeforeTestMethodCalled', 'testBeforeTestMethodFinished'); } /** - * @internal This method is not covered by the backward compatibility promise for PHPUnit + * @throws Throwable */ - public function setTestResultObject(\PHPUnit\Framework\TestResult $result) : void + private function invokePreConditionHookMethods(array $hookMethods, Event\Emitter $emitter): void { - $this->result = $result; + $this->invokeHookMethods($hookMethods['preCondition'], $emitter, 'testPreConditionCalled', 'testPreConditionFinished'); } /** - * @internal This method is not covered by the backward compatibility promise for PHPUnit + * @throws Throwable */ - public function registerMockObject(MockObject $mockObject) : void + private function invokePostConditionHookMethods(array $hookMethods, Event\Emitter $emitter): void { - $this->mockObjects[] = $mockObject; + $this->invokeHookMethods($hookMethods['postCondition'], $emitter, 'testPostConditionCalled', 'testPostConditionFinished'); } /** - * @internal This method is not covered by the backward compatibility promise for PHPUnit + * @throws Throwable */ - public function addToAssertionCount(int $count) : void + private function invokeAfterTestHookMethods(array $hookMethods, Event\Emitter $emitter): void { - $this->numAssertions += $count; + $this->invokeHookMethods($hookMethods['after'], $emitter, 'testAfterTestMethodCalled', 'testAfterTestMethodFinished'); } /** - * Returns the number of assertions performed by this test. + * @throws Throwable * - * @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 + * @codeCoverageIgnore */ - public function usesDataProvider() : bool + private function invokeAfterClassHookMethods(array $hookMethods, Event\Emitter $emitter): void { - return !empty($this->data); + $this->invokeHookMethods($hookMethods['afterClass'], $emitter, 'testAfterLastTestMethodCalled', 'testAfterLastTestMethodFinished'); } /** - * @return int|string + * @psalm-param list $hookMethods + * @psalm-param 'testBeforeFirstTestMethodCalled'|'testBeforeTestMethodCalled'|'testPreConditionCalled'|'testPostConditionCalled'|'testAfterTestMethodCalled'|'testAfterLastTestMethodCalled' $calledMethod + * @psalm-param 'testBeforeFirstTestMethodFinished'|'testBeforeTestMethodFinished'|'testPreConditionFinished'|'testPostConditionFinished'|'testAfterTestMethodFinished'|'testAfterLastTestMethodFinished' $finishedMethod * - * @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 + * @throws Throwable */ - public function getDataSetAsString(bool $includeData = \true) : string + private function invokeHookMethods(array $hookMethods, Event\Emitter $emitter, string $calledMethod, string $finishedMethod): void { - $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); + $methodsInvoked = []; + foreach ($hookMethods as $methodName) { + if ($this->methodDoesNotExistOrIsDeclaredInTestCase($methodName)) { + continue; } - if ($includeData) { - $exporter = new Exporter(); - $buffer .= sprintf(' (%s)', $exporter->shortenedRecursiveExport($this->data)); + try { + $this->{$methodName}(); + } catch (Throwable $t) { + } + $methodInvoked = new Event\Code\ClassMethod(static::class, $methodName); + $emitter->{$calledMethod}(static::class, $methodInvoked); + $methodsInvoked[] = $methodInvoked; + if (isset($t)) { + break; } } - 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; - } - public function sortId() : string - { - $id = $this->name; - if (strpos($id, '::') === \false) { - $id = static::class . '::' . $id; + if (!empty($methodsInvoked)) { + $emitter->{$finishedMethod}(static::class, ...$methodsInvoked); } - if ($this->usesDataProvider()) { - $id .= $this->getDataSetAsString(\false); + if (isset($t)) { + throw $t; } - return $id; } - /** - * Returns the normalized test name as class::method. - * - * @return list - */ - public function provides() : array + private function methodDoesNotExistOrIsDeclaredInTestCase(string $methodName): bool { - return $this->providedTests; + $reflector = new ReflectionObject($this); + return !$reflector->hasMethod($methodName) || $reflector->getMethod($methodName)->getDeclaringClass()->getName() === self::class; } /** - * Returns a list of normalized dependency names, class::method. - * - * This list can differ from the raw dependencies as the resolver has - * no need for the [!][shallow]clone prefix that is filtered out - * during normalization. - * - * @return list + * @throws ExpectationFailedException */ - public function requires() : array + private function verifyExceptionExpectations(\Exception|Throwable $exception): void { - return $this->dependencies; + if ($this->expectedException !== null) { + $this->assertThat($exception, new ExceptionConstraint($this->expectedException)); + } + if ($this->expectedExceptionMessage !== null) { + $this->assertThat($exception->getMessage(), new ExceptionMessageIsOrContains($this->expectedExceptionMessage)); + } + if ($this->expectedExceptionMessageRegExp !== null) { + $this->assertThat($exception->getMessage(), new ExceptionMessageMatchesRegularExpression($this->expectedExceptionMessageRegExp)); + } + if ($this->expectedExceptionCode !== null) { + $this->assertThat($exception->getCode(), new ExceptionCode($this->expectedExceptionCode)); + } } /** - * Override to run the test and assert its state. - * - * @throws \SebastianBergmann\ObjectEnumerator\InvalidArgumentException * @throws AssertionFailedError - * @throws Exception - * @throws ExpectationFailedException - * @throws Throwable */ - protected function runTest() + private function expectedExceptionWasNotRaised(): void { - if (trim($this->name) === '') { - throw new \PHPUnit\Framework\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) { - if ($this->expectedException === Error::class) { - $this->assertThat($exception, LogicalOr::fromConstraints(new ExceptionConstraint(Error::class), new ExceptionConstraint(\Error::class))); - } else { - $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++; + $this->numberOfAssertionsPerformed++; throw new \PHPUnit\Framework\AssertionFailedError(sprintf('Failed asserting that exception with message "%s" is thrown', $this->expectedExceptionMessage)); } elseif ($this->expectedExceptionMessageRegExp !== null) { - $this->numAssertions++; + $this->numberOfAssertionsPerformed++; throw new \PHPUnit\Framework\AssertionFailedError(sprintf('Failed asserting that exception with message matching "%s" is thrown', $this->expectedExceptionMessageRegExp)); } elseif ($this->expectedExceptionCode !== null) { - $this->numAssertions++; + $this->numberOfAssertionsPerformed++; throw new \PHPUnit\Framework\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 + private function isRegisteredFailure(Throwable $t): bool { - $currentValue = ini_set($varName, $newValue); - if ($currentValue !== \false) { - $this->iniSettings[$varName] = $currentValue; - } else { - throw new \PHPUnit\Framework\Exception(sprintf('INI setting "%s" could not be set to "%s".', $varName, $newValue)); + foreach (array_keys($this->failureTypes) as $failureType) { + if ($t instanceof $failureType) { + return \true; + } } + return \false; } /** - * 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 + * @internal This method is not covered by the backward compatibility promise for PHPUnit */ - protected function setLocale(...$args) : void + private function hasExpectationOnOutput(): bool { - if (count($args) < 2) { - throw new \PHPUnit\Framework\Exception(); - } - [$category, $locale] = $args; - if (!in_array($category, self::LOCALE_CATEGORIES, \true)) { - throw new \PHPUnit\Framework\Exception(); - } - if (!is_array($locale) && !is_string($locale)) { - throw new \PHPUnit\Framework\Exception(); - } - $this->locale[$category] = setlocale($category, 0); - $result = setlocale(...$args); - if ($result === \false) { - throw new \PHPUnit\Framework\Exception('The locale functionality is not implemented on your platform, ' . 'the specified locale does not exist or the category name is ' . 'invalid.'); - } + return is_string($this->outputExpectedString) || is_string($this->outputExpectedRegex); } /** - * Makes configurable stub for the specified class. + * Creates a test stub for the specified interface or class. * * @psalm-template RealInstanceType of object * - * @psalm-param class-string $originalClassName + * @psalm-param class-string $originalClassName + * + * @psalm-return Stub&RealInstanceType * - * @psalm-return Stub&RealInstanceType + * @throws InvalidArgumentException + * @throws MockObjectException + * @throws NoPreviousThrowableException */ - protected function createStub(string $originalClassName) : Stub + protected static function createStub(string $originalClassName): Stub { - return $this->createMockObject($originalClassName); + $stub = (new MockGenerator())->testDouble($originalClassName, \true, callOriginalConstructor: \false, callOriginalClone: \false, cloneArguments: \false, allowMockingUnknownTypes: \false); + Event\Facade::emitter()->testCreatedStub($originalClassName); + assert($stub instanceof $originalClassName); + assert($stub instanceof Stub); + return $stub; } /** - * Returns a mock object for the specified class. - * - * @psalm-template RealInstanceType of object - * - * @psalm-param class-string $originalClassName + * @psalm-param list $interfaces * - * @psalm-return MockObject&RealInstanceType + * @throws MockObjectException */ - protected function createMock(string $originalClassName) : MockObject + protected static function createStubForIntersectionOfInterfaces(array $interfaces): Stub { - return $this->createMockObject($originalClassName); + $stub = (new MockGenerator())->testDoubleForInterfaceIntersection($interfaces, \true); + Event\Facade::emitter()->testCreatedStubForIntersectionOfInterfaces($interfaces); + return $stub; } /** - * Returns a configured mock object for the specified class. + * Creates (and configures) a test stub for the specified interface or class. * * @psalm-template RealInstanceType of object * * @psalm-param class-string $originalClassName * - * @psalm-return MockObject&RealInstanceType + * @psalm-return Stub&RealInstanceType + * + * @throws InvalidArgumentException + * @throws MockObjectException + * @throws NoPreviousThrowableException */ - protected function createConfiguredMock(string $originalClassName, array $configuration) : MockObject + final protected static function createConfiguredStub(string $originalClassName, array $configuration): Stub { - $o = $this->createMockObject($originalClassName); + $o = self::createStub($originalClassName); foreach ($configuration as $method => $return) { $o->method($method)->willReturn($return); } return $o; } - /** - * Returns a partial mock object for the specified class. - * - * @param string[] $methods - * - * @psalm-template RealInstanceType of object - * - * @psalm-param class-string $originalClassName - * - * @psalm-return MockObject&RealInstanceType - */ - protected function createPartialMock(string $originalClassName, array $methods) : MockObject - { - try { - $reflector = new ReflectionClass($originalClassName); - // @codeCoverageIgnoreStart - } catch (ReflectionException $e) { - throw new \PHPUnit\Framework\Exception($e->getMessage(), $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd - $mockedMethodsThatDontExist = array_filter($methods, static function (string $method) use($reflector) { - return !$reflector->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), $originalClassName)); - } - 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 $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 null|array $methods $methods - * - * @psalm-template RealInstanceType of object - * - * @psalm-param class-string|string $originalClassName - * - * @psalm-return class-string - * - * @deprecated - */ - protected function getMockClass(string $originalClassName, $methods = [], array $arguments = [], string $mockClassName = '', bool $callOriginalConstructor = \false, bool $callOriginalClone = \true, bool $callAutoload = \true, bool $cloneArguments = \false) : string - { - $this->addWarning('PHPUnit\\Framework\\TestCase::getMockClass() is deprecated and will be removed in PHPUnit 10.'); - $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). - * - * @psalm-template RealInstanceType of object - * - * @psalm-param class-string $originalClassName - * - * @psalm-return MockObject&RealInstanceType - */ - protected function getMockForAbstractClass(string $originalClassName, array $arguments = [], string $mockClassName = '', bool $callOriginalConstructor = \true, bool $callOriginalClone = \true, bool $callAutoload = \true, array $mockedMethods = [], bool $cloneArguments = \false) : MockObject +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use const PHP_EOL; +use function assert; +use function defined; +use function error_clear_last; +use function extension_loaded; +use function get_include_path; +use function hrtime; +use function serialize; +use function sprintf; +use function sys_get_temp_dir; +use function tempnam; +use function unlink; +use function var_export; +use AssertionError; +use PHPUnit\Event; +use PHPUnit\Event\NoPreviousThrowableException; +use PHPUnit\Event\TestData\MoreThanOneDataSetFromDataProviderException; +use PHPUnit\Metadata\Api\CodeCoverage as CodeCoverageMetadataApi; +use PHPUnit\Metadata\Parser\Registry as MetadataRegistry; +use PHPUnit\Runner\CodeCoverage; +use PHPUnit\Runner\ErrorHandler; +use PHPUnit\TextUI\Configuration\Configuration; +use PHPUnit\TextUI\Configuration\Registry as ConfigurationRegistry; +use PHPUnit\Util\GlobalState; +use PHPUnit\Util\PHP\AbstractPhpProcess; +use ReflectionClass; +use PHPUnitPHAR\SebastianBergmann\CodeCoverage\Exception as OriginalCodeCoverageException; +use PHPUnitPHAR\SebastianBergmann\CodeCoverage\InvalidArgumentException; +use PHPUnitPHAR\SebastianBergmann\CodeCoverage\StaticAnalysisCacheNotConfiguredException; +use PHPUnitPHAR\SebastianBergmann\CodeCoverage\UnintentionallyCoveredCodeException; +use PHPUnitPHAR\SebastianBergmann\Invoker\Invoker; +use PHPUnitPHAR\SebastianBergmann\Invoker\TimeoutException; +use PHPUnitPHAR\SebastianBergmann\Template\Template; +use Throwable; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class TestRunner +{ + private ?bool $timeLimitCanBeEnforced = null; + private readonly Configuration $configuration; + public function __construct() { - $this->recordDoubledType($originalClassName); - $mockObject = $this->getMockObjectGenerator()->getMockForAbstractClass($originalClassName, $arguments, $mockClassName, $callOriginalConstructor, $callOriginalClone, $callAutoload, $mockedMethods, $cloneArguments); - $this->registerMockObject($mockObject); - return $mockObject; + $this->configuration = ConfigurationRegistry::get(); } /** - * Returns a mock object based on the given WSDL file. - * - * @psalm-template RealInstanceType of object - * - * @psalm-param class-string|string $originalClassName - * - * @psalm-return MockObject&RealInstanceType + * @throws \PHPUnit\Runner\Exception + * @throws CodeCoverageException + * @throws InvalidArgumentException + * @throws MoreThanOneDataSetFromDataProviderException + * @throws UnintentionallyCoveredCodeException */ - protected function getMockFromWsdl(string $wsdlFile, string $originalClassName = '', string $mockClassName = '', array $methods = [], bool $callOriginalConstructor = \true, array $options = []) : MockObject + public function run(\PHPUnit\Framework\TestCase $test): void { - $this->recordDoubledType(SoapClient::class); - if ($originalClassName === '') { - $fileName = pathinfo(basename(parse_url($wsdlFile, PHP_URL_PATH)), PATHINFO_FILENAME); - $originalClassName = preg_replace('/\\W/', '', $fileName); - } - if (!class_exists($originalClassName)) { - eval($this->getMockObjectGenerator()->generateClassFromWsdl($wsdlFile, $originalClassName, $methods, $options)); + \PHPUnit\Framework\Assert::resetCount(); + if ($this->configuration->registerMockObjectsFromTestArgumentsRecursively()) { + $test->registerMockObjectsFromTestArgumentsRecursively(); } - $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. - * - * @psalm-param trait-string $traitName - */ - protected function getMockForTrait(string $traitName, array $arguments = [], string $mockClassName = '', bool $callOriginalConstructor = \true, bool $callOriginalClone = \true, bool $callAutoload = \true, array $mockedMethods = [], bool $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. - * - * @psalm-param trait-string $traitName - */ - protected function getObjectForTrait(string $traitName, array $arguments = [], string $traitClassName = '', bool $callOriginalConstructor = \true, bool $callOriginalClone = \true, bool $callAutoload = \true) : object - { - $this->recordDoubledType($traitName); - return $this->getMockObjectGenerator()->getObjectForTrait($traitName, $traitClassName, $callAutoload, $callOriginalConstructor, $arguments); - } - /** - * @throws ClassNotFoundException - * @throws DoubleException - * @throws InterfaceNotFoundException - * - * @psalm-param class-string|null $classOrInterface - * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4141 - */ - protected function prophesize(?string $classOrInterface = null) : ObjectProphecy - { - if (!class_exists(Prophet::class)) { - throw new \PHPUnit\Framework\Exception('This test uses TestCase::prophesize(), but phpspec/prophecy is not installed. Please run "composer require --dev phpspec/prophecy".'); + $shouldCodeCoverageBeCollected = (new CodeCoverageMetadataApi())->shouldCodeCoverageBeCollectedFor($test::class, $test->name()); + $error = \false; + $failure = \false; + $incomplete = \false; + $risky = \false; + $skipped = \false; + error_clear_last(); + if ($this->shouldErrorHandlerBeUsed($test)) { + ErrorHandler::instance()->enable(); } - $this->addWarning('PHPUnit\\Framework\\TestCase::prophesize() is deprecated and will be removed in PHPUnit 10. Please use the trait provided by phpspec/prophecy-phpunit.'); - if (is_string($classOrInterface)) { - $this->recordDoubledType($classOrInterface); + $collectCodeCoverage = CodeCoverage::instance()->isActive() && $shouldCodeCoverageBeCollected; + if ($collectCodeCoverage) { + CodeCoverage::instance()->start($test); } - 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() : \PHPUnit\Framework\TestResult - { - return new \PHPUnit\Framework\TestResult(); - } - /** - * This method is called when a test method did not execute successfully. - * - * @throws Throwable - */ - protected function onNotSuccessfulTest(Throwable $t) : void - { - throw $t; - } - protected function recordDoubledType(string $originalClassName) : void - { - $this->doubledTypes[] = $originalClassName; - } - /** - * @throws Throwable - */ - private function verifyMockObjects() : void - { - foreach ($this->mockObjects as $mockObject) { - if ($mockObject->__phpunit_hasMatchers()) { - $this->numAssertions++; + try { + if ($this->canTimeLimitBeEnforced() && $this->shouldTimeLimitBeEnforced($test)) { + $risky = $this->runTestWithTimeout($test); + } else { + $test->runBare(); } - $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) { - /* @var MethodProphecy $methodProphecy */ - $this->numAssertions += count($methodProphecy->getCheckedPredictions()); - } - } - } + } catch (\PHPUnit\Framework\AssertionFailedError $e) { + $failure = \true; + if ($e instanceof \PHPUnit\Framework\IncompleteTestError) { + $incomplete = \true; + } elseif ($e instanceof \PHPUnit\Framework\SkippedTest) { + $skipped = \true; } + } catch (AssertionError $e) { + $test->addToAssertionCount(1); + $failure = \true; + $frame = $e->getTrace()[0]; + assert(isset($frame['file'])); + assert(isset($frame['line'])); + $e = new \PHPUnit\Framework\AssertionFailedError(sprintf('%s in %s:%s', $e->getMessage(), $frame['file'], $frame['line'])); + } catch (Throwable $e) { + $error = \true; } - } - /** - * @throws SkippedTestError - * @throws SyntheticSkippedError - * @throws Warning - */ - private function checkRequirements() : void - { - if (!$this->name || !method_exists($this, $this->name)) { - return; - } - $missingRequirements = TestUtil::getMissingRequirements(static::class, $this->name); - if (!empty($missingRequirements)) { - $this->markTestSkipped(implode(PHP_EOL, $missingRequirements)); - } - } - private function handleDependencies() : bool - { - if ([] === $this->dependencies || $this->inIsolation) { - return \true; + $test->addToAssertionCount(\PHPUnit\Framework\Assert::getCount()); + if ($this->configuration->reportUselessTests() && !$test->doesNotPerformAssertions() && $test->numberOfAssertionsPerformed() === 0) { + $risky = \true; } - $passed = $this->result->passed(); - $passedKeys = array_keys($passed); - $numKeys = count($passedKeys); - for ($i = 0; $i < $numKeys; $i++) { - $pos = strpos($passedKeys[$i], ' with data set'); - if ($pos !== \false) { - $passedKeys[$i] = substr($passedKeys[$i], 0, $pos); - } + if (!$error && !$failure && !$incomplete && !$skipped && !$risky && $this->configuration->requireCoverageMetadata() && !$this->hasCoverageMetadata($test::class, $test->name())) { + Event\Facade::emitter()->testConsideredRisky($test->valueObjectForEvents(), 'This test does not define a code coverage target but is expected to do so'); + $risky = \true; } - $passedKeys = array_flip(array_unique($passedKeys)); - foreach ($this->dependencies as $dependency) { - if (!$dependency->isValid()) { - $this->markSkippedForNotSpecifyingDependency(); - return \false; - } - if ($dependency->targetIsClass()) { - $dependencyClassName = $dependency->getTargetClassName(); - if (array_search($dependencyClassName, $this->result->passedClasses(), \true) === \false) { - $this->markSkippedForMissingDependency($dependency); - return \false; - } - continue; - } - $dependencyTarget = $dependency->getTarget(); - if (!isset($passedKeys[$dependencyTarget])) { - if (!$this->isCallableTestMethod($dependencyTarget)) { - $this->markWarningForUncallableDependency($dependency); - } else { - $this->markSkippedForMissingDependency($dependency); - } - return \false; - } - if (isset($passed[$dependencyTarget])) { - if ($passed[$dependencyTarget]['size'] != TestUtil::UNKNOWN && $this->getSize() != TestUtil::UNKNOWN && $passed[$dependencyTarget]['size'] > $this->getSize()) { - $this->result->addError($this, new \PHPUnit\Framework\SkippedTestError('This test depends on a test that is larger than itself.'), 0); - return \false; - } - if ($dependency->useDeepClone()) { - $deepCopy = new DeepCopy(); - $deepCopy->skipUncloneable(\false); - $this->dependencyInput[$dependencyTarget] = $deepCopy->copy($passed[$dependencyTarget]['result']); - } elseif ($dependency->useShallowClone()) { - $this->dependencyInput[$dependencyTarget] = clone $passed[$dependencyTarget]['result']; - } else { - $this->dependencyInput[$dependencyTarget] = $passed[$dependencyTarget]['result']; + if ($collectCodeCoverage) { + $append = !$risky && !$incomplete && !$skipped; + $linesToBeCovered = []; + $linesToBeUsed = []; + if ($append) { + try { + $linesToBeCovered = (new CodeCoverageMetadataApi())->linesToBeCovered($test::class, $test->name()); + $linesToBeUsed = (new CodeCoverageMetadataApi())->linesToBeUsed($test::class, $test->name()); + } catch (\PHPUnit\Framework\InvalidCoversTargetException $cce) { + Event\Facade::emitter()->testTriggeredPhpunitWarning($test->valueObjectForEvents(), $cce->getMessage()); + $append = \false; } - } else { - $this->dependencyInput[$dependencyTarget] = null; - } - } - return \true; - } - private function markSkippedForNotSpecifyingDependency() : void - { - $this->status = BaseTestRunner::STATUS_SKIPPED; - $this->result->startTest($this); - $this->result->addError($this, new \PHPUnit\Framework\SkippedTestError('This method has an invalid @depends annotation.'), 0); - $this->result->endTest($this, 0); - } - private function markSkippedForMissingDependency(\PHPUnit\Framework\ExecutionOrderDependency $dependency) : void - { - $this->status = BaseTestRunner::STATUS_SKIPPED; - $this->result->startTest($this); - $this->result->addError($this, new \PHPUnit\Framework\SkippedTestError(sprintf('This test depends on "%s" to pass.', $dependency->getTarget())), 0); - $this->result->endTest($this, 0); - } - private function markWarningForUncallableDependency(\PHPUnit\Framework\ExecutionOrderDependency $dependency) : void - { - $this->status = BaseTestRunner::STATUS_WARNING; - $this->result->startTest($this); - $this->result->addWarning($this, new \PHPUnit\Framework\Warning(sprintf('This test depends on "%s" which does not exist.', $dependency->getTarget())), 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 \PHPUnit\Framework\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 InvalidArgumentException - * @throws RiskyTestError - */ - private function restoreGlobalState() : void - { - if (!$this->snapshot instanceof Snapshot) { - return; - } - if ($this->beStrictAboutChangesToGlobalState) { try { - $this->compareGlobalStateSnapshots($this->snapshot, $this->createGlobalStateSnapshot($this->backupGlobals === \true)); - } catch (\PHPUnit\Framework\RiskyTestError $rte) { - // Intentionally left empty + CodeCoverage::instance()->stop($append, $linesToBeCovered, $linesToBeUsed); + } catch (UnintentionallyCoveredCodeException $cce) { + Event\Facade::emitter()->testConsideredRisky($test->valueObjectForEvents(), 'This test executed code that is not listed as code to be covered or used:' . PHP_EOL . $cce->getMessage()); + } catch (OriginalCodeCoverageException $cce) { + $error = \true; + $e = $e ?? $cce; } } - $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 - { - $excludeList = new ExcludeList(); - foreach ($this->backupGlobalsExcludeList as $globalVariable) { - $excludeList->addGlobalVariable($globalVariable); + ErrorHandler::instance()->disable(); + if (!$error && !$incomplete && !$skipped && $this->configuration->reportUselessTests() && !$test->doesNotPerformAssertions() && $test->numberOfAssertionsPerformed() === 0) { + Event\Facade::emitter()->testConsideredRisky($test->valueObjectForEvents(), 'This test did not perform any assertions'); } - if (!empty($this->backupGlobalsBlacklist)) { - $this->addWarning('PHPUnit\\Framework\\TestCase::$backupGlobalsBlacklist is deprecated and will be removed in PHPUnit 10. Please use PHPUnit\\Framework\\TestCase::$backupGlobalsExcludeList instead.'); - foreach ($this->backupGlobalsBlacklist as $globalVariable) { - $excludeList->addGlobalVariable($globalVariable); - } + if ($test->doesNotPerformAssertions() && $test->numberOfAssertionsPerformed() > 0) { + Event\Facade::emitter()->testConsideredRisky($test->valueObjectForEvents(), sprintf('This test is not expected to perform assertions but performed %d assertion%s', $test->numberOfAssertionsPerformed(), ($test->numberOfAssertionsPerformed() > 1) ? 's' : '')); } - if (!defined('PHPUNIT_TESTSUITE')) { - $excludeList->addClassNamePrefix('PHPUnit'); - $excludeList->addClassNamePrefix('PHPUnitPHAR\\SebastianBergmann\\CodeCoverage'); - $excludeList->addClassNamePrefix('PHPUnitPHAR\\SebastianBergmann\\FileIterator'); - $excludeList->addClassNamePrefix('PHPUnitPHAR\\SebastianBergmann\\Invoker'); - $excludeList->addClassNamePrefix('PHPUnitPHAR\\SebastianBergmann\\Template'); - $excludeList->addClassNamePrefix('PHPUnitPHAR\\SebastianBergmann\\Timer'); - $excludeList->addClassNamePrefix('PHPUnitPHAR\\Doctrine\\Instantiator'); - $excludeList->addClassNamePrefix('Prophecy'); - $excludeList->addStaticAttribute(ComparatorFactory::class, 'instance'); - foreach ($this->backupStaticAttributesExcludeList as $class => $attributes) { - foreach ($attributes as $attribute) { - $excludeList->addStaticAttribute($class, $attribute); - } - } - if (!empty($this->backupStaticAttributesBlacklist)) { - $this->addWarning('PHPUnit\\Framework\\TestCase::$backupStaticAttributesBlacklist is deprecated and will be removed in PHPUnit 10. Please use PHPUnit\\Framework\\TestCase::$backupStaticAttributesExcludeList instead.'); - foreach ($this->backupStaticAttributesBlacklist as $class => $attributes) { - foreach ($attributes as $attribute) { - $excludeList->addStaticAttribute($class, $attribute); - } - } - } + if ($test->hasUnexpectedOutput()) { + Event\Facade::emitter()->testPrintedUnexpectedOutput($test->output()); } - return new Snapshot($excludeList, $backupGlobals, (bool) $this->backupStaticAttributes, \false, \false, \false, \false, \false, \false, \false); - } - /** - * @throws InvalidArgumentException - * @throws RiskyTestError - */ - 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->configuration->disallowTestOutput() && $test->hasUnexpectedOutput()) { + Event\Facade::emitter()->testConsideredRisky($test->valueObjectForEvents(), sprintf('This test printed output: %s', $test->output())); } - if ($this->backupStaticAttributes) { - $this->compareGlobalStateSnapshotPart($before->staticAttributes(), $after->staticAttributes(), "--- Static attributes before the test\n+++ Static attributes after the test\n"); + if ($test->wasPrepared()) { + Event\Facade::emitter()->testFinished($test->valueObjectForEvents(), $test->numberOfAssertionsPerformed()); } } /** - * @throws RiskyTestError + * @throws \PHPUnit\Runner\Exception + * @throws \PHPUnit\Util\Exception + * @throws \SebastianBergmann\Template\InvalidArgumentException + * @throws Exception + * @throws MoreThanOneDataSetFromDataProviderException + * @throws NoPreviousThrowableException + * @throws ProcessIsolationException + * @throws StaticAnalysisCacheNotConfiguredException */ - private function compareGlobalStateSnapshotPart(array $before, array $after, string $header) : void + public function runInSeparateProcess(\PHPUnit\Framework\TestCase $test, bool $runEntireClass, bool $preserveGlobalState): void { - if ($before != $after) { - $differ = new Differ($header); - $exporter = new Exporter(); - $diff = $differ->diff($exporter->export($before), $exporter->export($after)); - throw new \PHPUnit\Framework\RiskyTestError($diff); + $class = new ReflectionClass($test); + if ($runEntireClass) { + $template = new Template(__DIR__ . '/../Util/PHP/Template/TestCaseClass.tpl'); + } else { + $template = new Template(__DIR__ . '/../Util/PHP/Template/TestCaseMethod.tpl'); } - } - private function getProphet() : Prophet - { - if ($this->prophet === null) { - $this->prophet = new Prophet(); + $bootstrap = ''; + $constants = ''; + $globals = ''; + $includedFiles = ''; + $iniSettings = ''; + if (ConfigurationRegistry::get()->hasBootstrap()) { + $bootstrap = ConfigurationRegistry::get()->bootstrap(); + } + if ($preserveGlobalState) { + $constants = GlobalState::getConstantsAsString(); + $globals = GlobalState::getGlobalsAsString(); + $includedFiles = GlobalState::getIncludedFilesAsString(); + $iniSettings = GlobalState::getIniSettingsAsString(); + } + $exportObjects = Event\Facade::emitter()->exportsObjects() ? 'true' : 'false'; + $coverage = CodeCoverage::instance()->isActive() ? 'true' : 'false'; + $linesToBeIgnored = var_export(CodeCoverage::instance()->linesToBeIgnored(), \true); + if (defined('PHPUNIT_COMPOSER_INSTALL')) { + $composerAutoload = var_export(PHPUNIT_COMPOSER_INSTALL, \true); + } else { + $composerAutoload = '\'\''; } - return $this->prophet; + if (defined('__PHPUNIT_PHAR__')) { + $phar = var_export(__PHPUNIT_PHAR__, \true); + } else { + $phar = '\'\''; + } + $data = var_export(serialize($test->providedData()), \true); + $dataName = var_export($test->dataName(), \true); + $dependencyInput = var_export(serialize($test->dependencyInput()), \true); + $includePath = var_export(get_include_path(), \true); + // must do these fixes because TestCaseMethod.tpl has unserialize('{data}') in it, and we can't break BC + // the lines above used to use addcslashes() rather than var_export(), which breaks null byte escape sequences + $data = "'." . $data . ".'"; + $dataName = "'.(" . $dataName . ").'"; + $dependencyInput = "'." . $dependencyInput . ".'"; + $includePath = "'." . $includePath . ".'"; + $offset = hrtime(); + $serializedConfiguration = $this->saveConfigurationForChildProcess(); + $processResultFile = tempnam(sys_get_temp_dir(), 'phpunit_'); + $var = ['bootstrap' => $bootstrap, 'composerAutoload' => $composerAutoload, 'phar' => $phar, 'filename' => $class->getFileName(), 'className' => $class->getName(), 'collectCodeCoverageInformation' => $coverage, 'linesToBeIgnored' => $linesToBeIgnored, 'data' => $data, 'dataName' => $dataName, 'dependencyInput' => $dependencyInput, 'constants' => $constants, 'globals' => $globals, 'include_path' => $includePath, 'included_files' => $includedFiles, 'iniSettings' => $iniSettings, 'name' => $test->name(), 'offsetSeconds' => $offset[0], 'offsetNanoseconds' => $offset[1], 'serializedConfiguration' => $serializedConfiguration, 'processResultFile' => $processResultFile, 'exportObjects' => $exportObjects]; + if (!$runEntireClass) { + $var['methodName'] = $test->name(); + } + $template->setVar($var); + $php = AbstractPhpProcess::factory(); + $php->runTestJob($template->render(), $test, $processResultFile); + @unlink($serializedConfiguration); } /** - * @throws \SebastianBergmann\ObjectEnumerator\InvalidArgumentException + * @psalm-param class-string $className + * @psalm-param non-empty-string $methodName */ - private function shouldInvocationMockerBeReset(MockObject $mock) : bool + private function hasCoverageMetadata(string $className, string $methodName): bool { - $enumerator = new Enumerator(); - foreach ($enumerator->enumerate($this->dependencyInput) as $object) { - if ($mock === $object) { - return \false; + foreach (MetadataRegistry::parser()->forClassAndMethod($className, $methodName) as $metadata) { + if ($metadata->isCovers()) { + return \true; } - } - 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 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); - } + if ($metadata->isCoversClass()) { + return \true; } - } else { - foreach ($testArguments as $testArgument) { - if ($testArgument instanceof MockObject) { - $testArgument = Cloner::clone($testArgument); - $this->registerMockObject($testArgument); - } elseif (is_array($testArgument) && !in_array($testArgument, $visited, \true)) { - $visited[] = $testArgument; - $this->registerMockObjectsFromTestArguments($testArgument, $visited); - } + if ($metadata->isCoversFunction()) { + return \true; + } + if ($metadata->isCoversNothing()) { + return \true; } } + return \false; } - private function setDoesNotPerformAssertionsFromAnnotation() : void + private function canTimeLimitBeEnforced(): bool { - $annotations = TestUtil::parseTestMethodAnnotations(static::class, $this->name); - if (isset($annotations['method']['doesNotPerformAssertions'])) { - $this->doesNotPerformAssertions = \true; + if ($this->timeLimitCanBeEnforced !== null) { + return $this->timeLimitCanBeEnforced; } + $this->timeLimitCanBeEnforced = (new Invoker())->canInvokeWithTimeout(); + return $this->timeLimitCanBeEnforced; } - private function unregisterCustomComparators() : void + private function shouldTimeLimitBeEnforced(\PHPUnit\Framework\TestCase $test): bool { - $factory = ComparatorFactory::getInstance(); - foreach ($this->customComparators as $comparator) { - $factory->unregister($comparator); + if (!$this->configuration->enforceTimeLimit()) { + return \false; } - $this->customComparators = []; - } - private function cleanupIniSettings() : void - { - foreach ($this->iniSettings as $varName => $oldValue) { - ini_set($varName, $oldValue); + if (!($this->configuration->defaultTimeLimit() || $test->size()->isKnown())) { + return \false; } - $this->iniSettings = []; - } - private function cleanupLocaleSettings() : void - { - foreach ($this->locale as $category => $locale) { - setlocale($category, $locale); + if (extension_loaded('xdebug') && xdebug_is_debugger_active()) { + return \false; } - $this->locale = []; + return \true; } /** - * @throws Exception + * @throws Throwable */ - private function checkExceptionExpectations(Throwable $throwable) : bool + private function runTestWithTimeout(\PHPUnit\Framework\TestCase $test): bool { - $result = \false; - if ($this->expectedException !== null || $this->expectedExceptionCode !== null || $this->expectedExceptionMessage !== null || $this->expectedExceptionMessageRegExp !== null) { - $result = \true; - } - if ($throwable instanceof \PHPUnit\Framework\Exception) { - $result = \false; + $_timeout = $this->configuration->defaultTimeLimit(); + $testSize = $test->size(); + if ($testSize->isSmall()) { + $_timeout = $this->configuration->timeoutForSmallTests(); + } elseif ($testSize->isMedium()) { + $_timeout = $this->configuration->timeoutForMediumTests(); + } elseif ($testSize->isLarge()) { + $_timeout = $this->configuration->timeoutForLargeTests(); } - if (is_string($this->expectedException)) { - try { - $reflector = new ReflectionClass($this->expectedException); - // @codeCoverageIgnoreStart - } catch (ReflectionException $e) { - throw new \PHPUnit\Framework\Exception($e->getMessage(), $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd - if ($this->expectedException === 'PHPUnit\\Framework\\Exception' || $this->expectedException === '\\PHPUnit\\Framework\\Exception' || $reflector->isSubclassOf(\PHPUnit\Framework\Exception::class)) { - $result = \true; - } + try { + (new Invoker())->invoke([$test, 'runBare'], [], $_timeout); + } catch (TimeoutException) { + Event\Facade::emitter()->testConsideredRisky($test->valueObjectForEvents(), sprintf('This test was aborted after %d second%s', $_timeout, ($_timeout !== 1) ? 's' : '')); + return \true; } - return $result; - } - private function runInSeparateProcess() : bool - { - return ($this->runTestInSeparateProcess || $this->runClassInSeparateProcess) && !$this->inIsolation && !$this instanceof PhptTestCase; + return \false; } - private function isCallableTestMethod(string $dependency) : bool + /** + * @throws ProcessIsolationException + */ + private function saveConfigurationForChildProcess(): string { - [$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; + $path = tempnam(sys_get_temp_dir(), 'phpunit_'); + if ($path === \false) { + throw new \PHPUnit\Framework\ProcessIsolationException(); } - try { - $method = $class->getMethod($methodName); - } catch (ReflectionException $e) { - return \false; + if (!ConfigurationRegistry::saveTo($path)) { + throw new \PHPUnit\Framework\ProcessIsolationException(); } - return TestUtil::isTestMethod($method); + return $path; } - /** - * @psalm-template RealInstanceType of object - * - * @psalm-param class-string $originalClassName - * - * @psalm-return MockObject&RealInstanceType - */ - private function createMockObject(string $originalClassName) : MockObject + private function shouldErrorHandlerBeUsed(\PHPUnit\Framework\TestCase $test): bool { - return $this->getMockBuilder($originalClassName)->disableOriginalConstructor()->disableOriginalClone()->disableArgumentCloning()->disallowMockingUnknownTypes()->getMock(); + if (MetadataRegistry::parser()->forMethod($test::class, $test->name())->isWithoutErrorHandler()->isNotEmpty()) { + return \false; + } + return \true; } } toString(); - if ($e instanceof \PHPUnit\Framework\ExpectationFailedException && $e->getComparisonFailure()) { - $buffer .= $e->getComparisonFailure()->getDiff(); - } - if ($e instanceof \PHPUnit\Framework\PHPTAssertionFailedError) { - $buffer .= $e->getDiff(); - } - if (!empty($buffer)) { - $buffer = trim($buffer) . "\n"; - } - return $buffer; - } - if ($e instanceof Error) { - return $e->getMessage() . "\n"; - } - if ($e instanceof \PHPUnit\Framework\ExceptionWrapper) { - return $e->getClassName() . ': ' . $e->getMessage() . "\n"; - } - return get_class($e) . ': ' . $e->getMessage() . "\n"; - } - /** - * Constructs a TestFailure with the given test and exception. - */ - public function __construct(\PHPUnit\Framework\Test $failedTest, Throwable $t) - { - if ($failedTest instanceof \PHPUnit\Framework\SelfDescribing) { - $this->testName = $failedTest->toString(); - } else { - $this->testName = get_class($failedTest); - } - if (!$failedTest instanceof \PHPUnit\Framework\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 + * @psalm-assert-if-true Known $this */ - public function failedTest() : ?\PHPUnit\Framework\Test + public function isKnown(): bool { - return $this->failedTest; + return \true; } + abstract public function isGreaterThan(self $other): bool; +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\TestSize; + +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + * + * @psalm-immutable + */ +final class Large extends \PHPUnit\Framework\TestSize\Known +{ /** - * Gets the thrown exception. + * @psalm-assert-if-true Large $this */ - public function thrownException() : Throwable + public function isLarge(): bool { - return $this->thrownException; + return \true; } - /** - * Returns the exception's message. - */ - public function exceptionMessage() : string + public function isGreaterThan(\PHPUnit\Framework\TestSize\TestSize $other): bool { - return $this->thrownException()->getMessage(); + return !$other->isLarge(); } - /** - * Returns true if the thrown exception - * is of type AssertionFailedError. - */ - public function isFailure() : bool + public function asString(): string { - return $this->thrownException() instanceof \PHPUnit\Framework\AssertionFailedError; + return 'large'; } } isSmall(); + } + public function asString(): string + { + return 'medium'; + } } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\TestSize; + +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + * + * @psalm-immutable + */ +abstract class TestSize +{ + public static function unknown(): self + { + return new \PHPUnit\Framework\TestSize\Unknown(); } - public function addFailure(\PHPUnit\Framework\Test $test, \PHPUnit\Framework\AssertionFailedError $e, float $time) : void + public static function small(): self { + return new \PHPUnit\Framework\TestSize\Small(); } - public function addIncompleteTest(\PHPUnit\Framework\Test $test, Throwable $t, float $time) : void + public static function medium(): self { + return new \PHPUnit\Framework\TestSize\Medium(); } - public function addRiskyTest(\PHPUnit\Framework\Test $test, Throwable $t, float $time) : void + public static function large(): self { + return new \PHPUnit\Framework\TestSize\Large(); } - public function addSkippedTest(\PHPUnit\Framework\Test $test, Throwable $t, float $time) : void + /** + * @psalm-assert-if-true Known $this + */ + public function isKnown(): bool { + return \false; } - public function startTestSuite(\PHPUnit\Framework\TestSuite $suite) : void + /** + * @psalm-assert-if-true Unknown $this + */ + public function isUnknown(): bool { + return \false; } - public function endTestSuite(\PHPUnit\Framework\TestSuite $suite) : void + /** + * @psalm-assert-if-true Small $this + */ + public function isSmall(): bool { + return \false; } - public function startTest(\PHPUnit\Framework\Test $test) : void + /** + * @psalm-assert-if-true Medium $this + */ + public function isMedium(): bool { + return \false; } - public function endTest(\PHPUnit\Framework\Test $test, float $time) : void + /** + * @psalm-assert-if-true Large $this + */ + public function isLarge(): bool { + return \false; } + abstract public function asString(): string; } - */ - private $passedTestClasses = []; - /** - * @var bool - */ - private $currentTestSuiteFailed = \false; - /** - * @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; - /** - * Code Coverage information. - * - * @var CodeCoverage - */ - private $codeCoverage; - /** - * @var bool - */ - private $convertDeprecationsToExceptions = \false; - /** - * @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 bool - */ - private $forceCoversAnnotation = \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. + * @psalm-assert-if-true Unknown $this */ - public function addListener(\PHPUnit\Framework\TestListener $listener) : void + public function isUnknown(): bool { - $this->listeners[] = $listener; + return \true; } - /** - * @deprecated Use the `TestHook` interfaces instead - * - * @codeCoverageIgnore - * - * Unregisters a TestListener. - */ - public function removeListener(\PHPUnit\Framework\TestListener $listener) : void + public function asString(): string { - foreach ($this->listeners as $key => $_listener) { - if ($listener === $_listener) { - unset($this->listeners[$key]); - } - } + return 'unknown'; } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\TestStatus; + +/** + * @psalm-immutable + * + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class Deprecation extends \PHPUnit\Framework\TestStatus\Known +{ /** - * @deprecated Use the `TestHook` interfaces instead - * - * @codeCoverageIgnore - * - * Flushes all flushable TestListeners. + * @psalm-assert-if-true Deprecation $this */ - public function flushListeners() : void + public function isDeprecation(): bool { - foreach ($this->listeners as $listener) { - if ($listener instanceof Printer) { - $listener->flush(); - } - } + return \true; } - /** - * Adds an error to the list of errors. - */ - public function addError(\PHPUnit\Framework\Test $test, Throwable $t, float $time) : void + public function asInt(): int { - if ($t instanceof \PHPUnit\Framework\RiskyTestError) { - $this->recordRisky($test, $t); - $notifyMethod = 'addRiskyTest'; - if ($test instanceof \PHPUnit\Framework\TestCase) { - $test->markAsRisky(); - } - if ($this->stopOnRisky || $this->stopOnDefect) { - $this->stop(); - } - } elseif ($t instanceof \PHPUnit\Framework\IncompleteTest) { - $this->recordNotImplemented($test, $t); - $notifyMethod = 'addIncompleteTest'; - if ($this->stopOnIncomplete) { - $this->stop(); - } - } elseif ($t instanceof \PHPUnit\Framework\SkippedTest) { - $this->recordSkipped($test, $t); - $notifyMethod = 'addSkippedTest'; - if ($this->stopOnSkipped) { - $this->stop(); - } - } else { - $this->recordError($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 \PHPUnit\Framework\ExceptionWrapper($t); - } - foreach ($this->listeners as $listener) { - $listener->{$notifyMethod}($test, $t, $time); - } - $this->lastTestFailed = \true; - $this->time += $time; + return 4; } - /** - * Adds a warning to the list of warnings. - * The passed in exception caused the warning. - */ - public function addWarning(\PHPUnit\Framework\Test $test, \PHPUnit\Framework\Warning $e, float $time) : void + public function asString(): string { - if ($this->stopOnWarning || $this->stopOnDefect) { - $this->stop(); - } - $this->recordWarning($test, $e); - foreach ($this->listeners as $listener) { - $listener->addWarning($test, $e, $time); - } - $this->time += $time; + return 'deprecation'; } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\TestStatus; + +/** + * @psalm-immutable + * + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class Error extends \PHPUnit\Framework\TestStatus\Known +{ /** - * Adds a failure to the list of failures. - * The passed in exception caused the failure. + * @psalm-assert-if-true Error $this */ - public function addFailure(\PHPUnit\Framework\Test $test, \PHPUnit\Framework\AssertionFailedError $e, float $time) : void + public function isError(): bool { - if ($e instanceof \PHPUnit\Framework\RiskyTestError || $e instanceof \PHPUnit\Framework\OutputError) { - $this->recordRisky($test, $e); - $notifyMethod = 'addRiskyTest'; - if ($test instanceof \PHPUnit\Framework\TestCase) { - $test->markAsRisky(); - } - if ($this->stopOnRisky || $this->stopOnDefect) { - $this->stop(); - } - } elseif ($e instanceof \PHPUnit\Framework\IncompleteTest) { - $this->recordNotImplemented($test, $e); - $notifyMethod = 'addIncompleteTest'; - if ($this->stopOnIncomplete) { - $this->stop(); - } - } elseif ($e instanceof \PHPUnit\Framework\SkippedTest) { - $this->recordSkipped($test, $e); - $notifyMethod = 'addSkippedTest'; - if ($this->stopOnSkipped) { - $this->stop(); - } - } else { - $this->failures[] = new \PHPUnit\Framework\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; + return \true; } - /** - * Informs the result that a test suite will be started. - */ - public function startTestSuite(\PHPUnit\Framework\TestSuite $suite) : void + public function asInt(): int { - $this->currentTestSuiteFailed = \false; - foreach ($this->listeners as $listener) { - $listener->startTestSuite($suite); - } + return 8; } - /** - * Informs the result that a test suite was completed. - */ - public function endTestSuite(\PHPUnit\Framework\TestSuite $suite) : void + public function asString(): string { - if (!$this->currentTestSuiteFailed) { - $this->passedTestClasses[] = $suite->getName(); - } - foreach ($this->listeners as $listener) { - $listener->endTestSuite($suite); - } + return 'error'; } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\TestStatus; + +/** + * @psalm-immutable + * + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class Failure extends \PHPUnit\Framework\TestStatus\Known +{ /** - * Informs the result that a test will be started. + * @psalm-assert-if-true Failure $this */ - public function startTest(\PHPUnit\Framework\Test $test) : void + public function isFailure(): bool { - $this->lastTestFailed = \false; - $this->runTests += count($test); - foreach ($this->listeners as $listener) { - $listener->startTest($test); - } + return \true; } - /** - * Informs the result that a test was completed. - * - * @throws InvalidArgumentException - */ - public function endTest(\PHPUnit\Framework\Test $test, float $time) : void + public function asInt(): int { - foreach ($this->listeners as $listener) { - $listener->endTest($test, $time); - } - if (!$this->lastTestFailed && $test instanceof \PHPUnit\Framework\TestCase) { - $class = get_class($test); - $key = $class . '::' . $test->getName(); - $this->passed[$key] = ['result' => $test->getResult(), 'size' => TestUtil::getSize($class, $test->getName(\false))]; - $this->time += $time; - } - if ($this->lastTestFailed && $test instanceof \PHPUnit\Framework\TestCase) { - $this->currentTestSuiteFailed = \true; - } + return 7; } - /** - * Returns true if no risky test occurred. - */ - public function allHarmless() : bool + public function asString(): string { - return $this->riskyCount() === 0; + return 'failure'; } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\TestStatus; + +/** + * @psalm-immutable + * + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class Incomplete extends \PHPUnit\Framework\TestStatus\Known +{ /** - * Gets the number of risky tests. + * @psalm-assert-if-true Incomplete $this */ - public function riskyCount() : int + public function isIncomplete(): bool { - return count($this->risky); + return \true; } - /** - * Returns true if no incomplete test occurred. - */ - public function allCompletelyImplemented() : bool + public function asInt(): int + { + return 2; + } + public function asString(): string { - return $this->notImplementedCount() === 0; + return 'incomplete'; } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\TestStatus; + +/** + * @psalm-immutable + * + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +abstract class Known extends \PHPUnit\Framework\TestStatus\TestStatus +{ /** - * Gets the number of incomplete tests. + * @psalm-assert-if-true Known $this */ - public function notImplementedCount() : int + public function isKnown(): bool { - return count($this->notImplemented); + return \true; } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\TestStatus; + +/** + * @psalm-immutable + * + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class Notice extends \PHPUnit\Framework\TestStatus\Known +{ /** - * Returns an array of TestFailure objects for the risky tests. - * - * @return TestFailure[] + * @psalm-assert-if-true Notice $this */ - public function risky() : array + public function isNotice(): bool + { + return \true; + } + public function asInt(): int + { + return 3; + } + public function asString(): string { - return $this->risky; + return 'notice'; } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\TestStatus; + +/** + * @psalm-immutable + * + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class Risky extends \PHPUnit\Framework\TestStatus\Known +{ /** - * Returns an array of TestFailure objects for the incomplete tests. - * - * @return TestFailure[] + * @psalm-assert-if-true Risky $this */ - public function notImplemented() : array + public function isRisky(): bool { - return $this->notImplemented; + return \true; + } + public function asInt(): int + { + return 5; } + public function asString(): string + { + return 'risky'; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\TestStatus; + +/** + * @psalm-immutable + * + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class Skipped extends \PHPUnit\Framework\TestStatus\Known +{ /** - * Returns true if no test has been skipped. + * @psalm-assert-if-true Skipped $this */ - public function noneSkipped() : bool + public function isSkipped(): bool + { + return \true; + } + public function asInt(): int { - return $this->skippedCount() === 0; + return 1; + } + public function asString(): string + { + return 'skipped'; } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\TestStatus; + +/** + * @psalm-immutable + * + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class Success extends \PHPUnit\Framework\TestStatus\Known +{ /** - * Gets the number of skipped tests. + * @psalm-assert-if-true Success $this */ - public function skippedCount() : int + public function isSuccess(): bool + { + return \true; + } + public function asInt(): int + { + return 0; + } + public function asString(): string + { + return 'success'; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\TestStatus; + +/** + * @psalm-immutable + * + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +abstract class TestStatus +{ + private readonly string $message; + public static function from(int $status): self + { + return match ($status) { + 0 => self::success(), + 1 => self::skipped(), + 2 => self::incomplete(), + 3 => self::notice(), + 4 => self::deprecation(), + 5 => self::risky(), + 6 => self::warning(), + 7 => self::failure(), + 8 => self::error(), + default => self::unknown(), + }; + } + public static function unknown(): self + { + return new \PHPUnit\Framework\TestStatus\Unknown(); + } + public static function success(): self + { + return new \PHPUnit\Framework\TestStatus\Success(); + } + public static function skipped(string $message = ''): self + { + return new \PHPUnit\Framework\TestStatus\Skipped($message); + } + public static function incomplete(string $message = ''): self + { + return new \PHPUnit\Framework\TestStatus\Incomplete($message); + } + public static function notice(string $message = ''): self + { + return new \PHPUnit\Framework\TestStatus\Notice($message); + } + public static function deprecation(string $message = ''): self + { + return new \PHPUnit\Framework\TestStatus\Deprecation($message); + } + public static function failure(string $message = ''): self + { + return new \PHPUnit\Framework\TestStatus\Failure($message); + } + public static function error(string $message = ''): self + { + return new \PHPUnit\Framework\TestStatus\Error($message); + } + public static function warning(string $message = ''): self + { + return new \PHPUnit\Framework\TestStatus\Warning($message); + } + public static function risky(string $message = ''): self + { + return new \PHPUnit\Framework\TestStatus\Risky($message); + } + private function __construct(string $message = '') { - return count($this->skipped); + $this->message = $message; } /** - * Returns an array of TestFailure objects for the skipped tests. - * - * @return TestFailure[] + * @psalm-assert-if-true Known $this */ - public function skipped() : array + public function isKnown(): bool { - return $this->skipped; + return \false; } /** - * Gets the number of detected errors. + * @psalm-assert-if-true Unknown $this */ - public function errorCount() : int + public function isUnknown(): bool { - return count($this->errors); + return \false; } /** - * Returns an array of TestFailure objects for the errors. - * - * @return TestFailure[] + * @psalm-assert-if-true Success $this */ - public function errors() : array + public function isSuccess(): bool { - return $this->errors; + return \false; } /** - * Gets the number of detected failures. + * @psalm-assert-if-true Skipped $this */ - public function failureCount() : int + public function isSkipped(): bool { - return count($this->failures); + return \false; } /** - * Returns an array of TestFailure objects for the failures. - * - * @return TestFailure[] + * @psalm-assert-if-true Incomplete $this */ - public function failures() : array + public function isIncomplete(): bool { - return $this->failures; + return \false; } /** - * Gets the number of detected warnings. + * @psalm-assert-if-true Notice $this */ - public function warningCount() : int + public function isNotice(): bool { - return count($this->warnings); + return \false; } /** - * Returns an array of TestFailure objects for the warnings. - * - * @return TestFailure[] + * @psalm-assert-if-true Deprecation $this */ - public function warnings() : array + public function isDeprecation(): bool { - return $this->warnings; + return \false; } /** - * Returns the names of the tests that have passed. + * @psalm-assert-if-true Failure $this */ - public function passed() : array + public function isFailure(): bool { - return $this->passed; + return \false; } /** - * Returns the names of the TestSuites that have passed. - * - * This enables @depends-annotations for TestClassName::class + * @psalm-assert-if-true Error $this */ - public function passedClasses() : array + public function isError(): bool { - return $this->passedTestClasses; + return \false; } /** - * Returns whether code coverage information should be collected. + * @psalm-assert-if-true Warning $this */ - public function getCollectCodeCoverageInformation() : bool + public function isWarning(): bool { - return $this->codeCoverage !== null; + return \false; } /** - * Runs a TestCase. - * - * @throws \SebastianBergmann\CodeCoverage\InvalidArgumentException - * @throws CodeCoverageException - * @throws InvalidArgumentException - * @throws UnintentionallyCoveredCodeException + * @psalm-assert-if-true Risky $this */ - public function run(\PHPUnit\Framework\Test $test) : void + public function isRisky(): bool { - \PHPUnit\Framework\Assert::resetCount(); - $size = TestUtil::UNKNOWN; - if ($test instanceof \PHPUnit\Framework\TestCase) { - $test->setRegisterMockObjectsFromTestArgumentsRecursively($this->registerMockObjectsFromTestArgumentsRecursively); - $isAnyCoverageRequired = TestUtil::requiresCodeCoverageDataCollection($test); - $size = $test->getSize(); - } - $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 \PHPUnit\Framework\ErrorTestCase && !$test instanceof \PHPUnit\Framework\WarningTestCase && $isAnyCoverageRequired; - if ($collectCodeCoverage) { - $this->codeCoverage->start($test); - } - $monitorFunctions = $this->beStrictAboutResourceUsageDuringSmallTests && !$test instanceof \PHPUnit\Framework\ErrorTestCase && !$test instanceof \PHPUnit\Framework\WarningTestCase && $size === TestUtil::SMALL && function_exists('xdebug_start_function_monitor'); - if ($monitorFunctions) { - /* @noinspection ForgottenDebugOutputInspection */ - xdebug_start_function_monitor(ResourceOperations::getFunctions()); - } - $timer = new Timer(); - $timer->start(); - try { - $invoker = new Invoker(); - if (!$test instanceof \PHPUnit\Framework\ErrorTestCase && !$test instanceof \PHPUnit\Framework\WarningTestCase && $this->shouldTimeLimitBeEnforced($size) && $invoker->canInvokeWithTimeout()) { - switch ($size) { - case TestUtil::SMALL: - $_timeout = $this->timeoutForSmallTests; - break; - case TestUtil::MEDIUM: - $_timeout = $this->timeoutForMediumTests; - break; - case TestUtil::LARGE: - $_timeout = $this->timeoutForLargeTests; - break; - default: - $_timeout = $this->defaultTimeLimit; - } - $invoker->invoke([$test, 'runBare'], [], $_timeout); - } else { - $test->runBare(); - } - } catch (TimeoutException $e) { - $this->addFailure($test, new \PHPUnit\Framework\RiskyTestError($e->getMessage()), $_timeout); - $risky = \true; - } catch (\PHPUnit\Framework\AssertionFailedError $e) { - $failure = \true; - if ($e instanceof \PHPUnit\Framework\RiskyTestError) { - $risky = \true; - } elseif ($e instanceof \PHPUnit\Framework\IncompleteTestError) { - $incomplete = \true; - } elseif ($e instanceof \PHPUnit\Framework\SkippedTestError) { - $skipped = \true; - } - } catch (AssertionError $e) { - $test->addToAssertionCount(1); - $failure = \true; - $frame = $e->getTrace()[0]; - $e = new \PHPUnit\Framework\AssertionFailedError(sprintf('%s in %s:%s', $e->getMessage(), $frame['file'] ?? $e->getFile(), $frame['line'] ?? $e->getLine()), 0, $e); - } catch (\PHPUnit\Framework\Warning $e) { - $warning = \true; - } catch (\PHPUnit\Framework\Exception $e) { - $error = \true; - } catch (Throwable $e) { - $e = new \PHPUnit\Framework\ExceptionWrapper($e); - $error = \true; - } - $time = $timer->stop()->asSeconds(); - $test->addToAssertionCount(\PHPUnit\Framework\Assert::getCount()); - if ($monitorFunctions) { - $excludeList = new ExcludeList(); - /** @noinspection ForgottenDebugOutputInspection */ - $functions = xdebug_get_monitored_functions(); - /* @noinspection ForgottenDebugOutputInspection */ - xdebug_stop_function_monitor(); - foreach ($functions as $function) { - if (!$excludeList->isExcluded($function['filename'])) { - $this->addFailure($test, new \PHPUnit\Framework\RiskyTestError(sprintf('%s() used in %s:%s', $function['function'], $function['filename'], $function['lineno'])), $time); - } - } - } - if ($this->beStrictAboutTestsThatDoNotTestAnything && !$test->doesNotPerformAssertions() && $test->getNumAssertions() === 0) { - $risky = \true; - } - if ($this->forceCoversAnnotation && !$error && !$failure && !$warning && !$incomplete && !$skipped && !$risky) { - $annotations = TestUtil::parseTestMethodAnnotations(get_class($test), $test->getName(\false)); - if (!isset($annotations['class']['covers']) && !isset($annotations['method']['covers']) && !isset($annotations['class']['coversNothing']) && !isset($annotations['method']['coversNothing'])) { - $this->addFailure($test, new \PHPUnit\Framework\MissingCoversAnnotationException('This test does not have a @covers annotation but is expected to have one'), $time); - $risky = \true; - } - } - if ($collectCodeCoverage) { - $append = !$risky && !$incomplete && !$skipped; - $linesToBeCovered = []; - $linesToBeUsed = []; - if ($append && $test instanceof \PHPUnit\Framework\TestCase) { - try { - $linesToBeCovered = TestUtil::getLinesToBeCovered(get_class($test), $test->getName(\false)); - $linesToBeUsed = TestUtil::getLinesToBeUsed(get_class($test), $test->getName(\false)); - } catch (\PHPUnit\Framework\InvalidCoversTargetException $cce) { - $this->addWarning($test, new \PHPUnit\Framework\Warning($cce->getMessage()), $time); - } - } - try { - $this->codeCoverage->stop($append, $linesToBeCovered, $linesToBeUsed); - } catch (UnintentionallyCoveredCodeException $cce) { - $unintentionallyCoveredCodeError = new \PHPUnit\Framework\UnintentionallyCoveredCodeError('This test executed code that is not listed as code to be covered or used:' . PHP_EOL . $cce->getMessage()); - } 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 (isset($unintentionallyCoveredCodeError)) { - $this->addFailure($test, $unintentionallyCoveredCodeError, $time); - } elseif ($this->beStrictAboutTestsThatDoNotTestAnything && !$test->doesNotPerformAssertions() && $test->getNumAssertions() === 0) { - try { - $reflected = new ReflectionClass($test); - // @codeCoverageIgnoreStart - } catch (ReflectionException $e) { - throw new \PHPUnit\Framework\Exception($e->getMessage(), $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd - $name = $test->getName(\false); - if ($name && $reflected->hasMethod($name)) { - try { - $reflected = $reflected->getMethod($name); - // @codeCoverageIgnoreStart - } catch (ReflectionException $e) { - throw new \PHPUnit\Framework\Exception($e->getMessage(), $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd - } - $this->addFailure($test, new \PHPUnit\Framework\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 \PHPUnit\Framework\RiskyTestError(sprintf('This test is annotated with "@doesNotPerformAssertions" but performed %d assertions', $test->getNumAssertions())), $time); - } elseif ($this->beStrictAboutOutputDuringTests && $test->hasOutput()) { - $this->addFailure($test, new \PHPUnit\Framework\OutputError(sprintf('This test printed output: %s', $test->getActualOutput())), $time); - } elseif ($this->beStrictAboutTodoAnnotatedTests && $test instanceof \PHPUnit\Framework\TestCase) { - $annotations = TestUtil::parseTestMethodAnnotations(get_class($test), $test->getName(\false)); - if (isset($annotations['method']['todo'])) { - $this->addFailure($test, new \PHPUnit\Framework\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; - } - public function forceCoversAnnotation() : void - { - $this->forceCoversAnnotation = \true; - } - public function forcesCoversAnnotation() : bool - { - return $this->forceCoversAnnotation; - } - /** - * 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); + return \false; } - public function wasSuccessfulAndNoTestIsRiskyOrSkippedOrIncomplete() : bool + public function message(): string { - return $this->wasSuccessful() && $this->allHarmless() && $this->allCompletelyImplemented() && $this->noneSkipped(); + return $this->message; } - /** - * Sets the default timeout for tests. - */ - public function setDefaultTimeLimit(int $timeout) : void + public function isMoreImportantThan(self $other): bool { - $this->defaultTimeLimit = $timeout; + return $this->asInt() > $other->asInt(); } + abstract public function asInt(): int; + abstract public function asString(): string; +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\TestStatus; + +/** + * @psalm-immutable + * + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class Unknown extends \PHPUnit\Framework\TestStatus\TestStatus +{ /** - * Sets the timeout for small tests. + * @psalm-assert-if-true Unknown $this */ - public function setTimeoutForSmallTests(int $timeout) : void + public function isUnknown(): bool { - $this->timeoutForSmallTests = $timeout; + return \true; } - /** - * Sets the timeout for medium tests. - */ - public function setTimeoutForMediumTests(int $timeout) : void + public function asInt(): int { - $this->timeoutForMediumTests = $timeout; + return -1; } - /** - * Sets the timeout for large tests. - */ - public function setTimeoutForLargeTests(int $timeout) : void + public function asString(): string { - $this->timeoutForLargeTests = $timeout; + return 'unknown'; } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\TestStatus; + +/** + * @psalm-immutable + * + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class Warning extends \PHPUnit\Framework\TestStatus\Known +{ /** - * Returns the set timeout for large tests. + * @psalm-assert-if-true Warning $this */ - public function getTimeoutForLargeTests() : int - { - return $this->timeoutForLargeTests; - } - public function setRegisterMockObjectsFromTestArgumentsRecursively(bool $flag) : void - { - $this->registerMockObjectsFromTestArgumentsRecursively = $flag; - } - private function recordError(\PHPUnit\Framework\Test $test, Throwable $t) : void - { - $this->errors[] = new \PHPUnit\Framework\TestFailure($test, $t); - } - private function recordNotImplemented(\PHPUnit\Framework\Test $test, Throwable $t) : void - { - $this->notImplemented[] = new \PHPUnit\Framework\TestFailure($test, $t); - } - private function recordRisky(\PHPUnit\Framework\Test $test, Throwable $t) : void - { - $this->risky[] = new \PHPUnit\Framework\TestFailure($test, $t); - } - private function recordSkipped(\PHPUnit\Framework\Test $test, Throwable $t) : void + public function isWarning(): bool { - $this->skipped[] = new \PHPUnit\Framework\TestFailure($test, $t); + return \true; } - private function recordWarning(\PHPUnit\Framework\Test $test, Throwable $t) : void + public function asInt(): int { - $this->warnings[] = new \PHPUnit\Framework\TestFailure($test, $t); + return 6; } - private function shouldTimeLimitBeEnforced(int $size) : bool + public function asString(): string { - if (!$this->enforceTimeLimit) { - return \false; - } - if (!($this->defaultTimeLimit || $size !== TestUtil::UNKNOWN)) { - return \false; - } - if (!extension_loaded('pcntl')) { - return \false; - } - if (!class_exists(Invoker::class)) { - return \false; - } - if (extension_loaded('xdebug') && xdebug_is_debugger_active()) { - return \false; - } - return \true; + return 'warning'; } } > */ - 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 = []; - /** - * @var null|list - */ - protected $providedTests; - /** - * @var null|list - */ - protected $requiredTests; + private array $groups = []; /** - * @var bool + * @psalm-var ?list */ - private $beStrictAboutChangesToGlobalState; + private ?array $requiredTests = null; /** - * @var Factory + * @psalm-var list */ - private $iteratorFilter; + private array $tests = []; /** - * @var int + * @psalm-var ?list */ - private $declaredClassesPointer; + private ?array $providedTests = null; + private ?Factory $iteratorFilter = null; + private bool $wasRun = \false; /** - * @psalm-var array + * @psalm-param non-empty-string $name */ - private $warnings = []; + public static function empty(string $name): static + { + return new static($name); + } /** - * 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 + * @psalm-param class-string $className */ - public function __construct($theClass = '', string $name = '') + public static function fromClassName(string $className): static { - if (!is_string($theClass) && !$theClass instanceof ReflectionClass) { - throw \PHPUnit\Framework\InvalidArgumentException::create(1, 'ReflectionClass object or string'); - } - $this->declaredClassesPointer = count(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 \PHPUnit\Framework\Exception($e->getMessage(), $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd - } else { - $this->setName($theClass); - return; - } - } - if (!$theClass->isSubclassOf(\PHPUnit\Framework\TestCase::class)) { - $this->setName((string) $theClass); - return; - } - if ($name !== '') { - $this->setName($name); - } else { - $this->setName($theClass->getName()); - } - $constructor = $theClass->getConstructor(); + assert(class_exists($className)); + $class = new ReflectionClass($className); + return static::fromClassReflector($class); + } + public static function fromClassReflector(ReflectionClass $class): static + { + $testSuite = new static($class->getName()); + $constructor = $class->getConstructor(); if ($constructor !== null && !$constructor->isPublic()) { - $this->addTest(new \PHPUnit\Framework\WarningTestCase(sprintf('Class "%s" has no public constructor.', $theClass->getName()))); - return; + Event\Facade::emitter()->testRunnerTriggeredWarning(sprintf('Class "%s" has no public constructor.', $class->getName())); + return $testSuite; } - foreach ((new Reflection())->publicMethodsInTestClass($theClass) as $method) { + foreach (Reflection::publicMethodsInTestClass($class) as $method) { + if ($method->getDeclaringClass()->getName() === \PHPUnit\Framework\Assert::class) { + continue; + } + if ($method->getDeclaringClass()->getName() === \PHPUnit\Framework\TestCase::class) { + continue; + } if (!TestUtil::isTestMethod($method)) { continue; } - $this->addTestMethod($theClass, $method); + $testSuite->addTestMethod($class, $method); } - if (empty($this->tests)) { - $this->addTest(new \PHPUnit\Framework\WarningTestCase(sprintf('No tests found in class "%s".', $theClass->getName()))); + if ($testSuite->isEmpty()) { + Event\Facade::emitter()->testRunnerTriggeredWarning(sprintf('No tests found in class "%s".', $class->getName())); } - $this->testCase = \true; + return $testSuite; + } + /** + * @psalm-param non-empty-string $name + */ + final private function __construct(string $name) + { + $this->name = $name; } /** * Returns a string representation of the test suite. */ - public function toString() : string + public function toString(): string { - return $this->getName(); + return $this->name(); } /** * Adds a test to the suite. - * - * @param array $groups */ - public function addTest(\PHPUnit\Framework\Test $test, $groups = []) : void + public function addTest(\PHPUnit\Framework\Test $test, array $groups = []): void { - try { - $class = new ReflectionClass($test); - // @codeCoverageIgnoreStart - } catch (ReflectionException $e) { - throw new \PHPUnit\Framework\Exception($e->getMessage(), $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd + $class = new ReflectionClass($test); if (!$class->isAbstract()) { $this->tests[] = $test; $this->clearCaches(); if ($test instanceof self && empty($groups)) { - $groups = $test->getGroups(); + $groups = $test->groups(); } if ($this->containsOnlyVirtualGroups($groups)) { $groups[] = 'default'; @@ -75231,51 +64912,17 @@ class TestSuite implements IteratorAggregate, \PHPUnit\Framework\Reorderable, \P /** * Adds the tests from the given class to the suite. * - * @psalm-param object|class-string $testClass - * * @throws Exception */ - public function addTestSuite($testClass) : void + public function addTestSuite(ReflectionClass $testClass): void { - if (!(is_object($testClass) || is_string($testClass) && class_exists($testClass))) { - throw \PHPUnit\Framework\InvalidArgumentException::create(1, 'class name or object'); - } - if (!is_object($testClass)) { - try { - $testClass = new ReflectionClass($testClass); - // @codeCoverageIgnoreStart - } catch (ReflectionException $e) { - throw new \PHPUnit\Framework\Exception($e->getMessage(), $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd + if ($testClass->isAbstract()) { + throw new \PHPUnit\Framework\Exception(sprintf('Class %s is abstract', $testClass->getName())); } - 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); - // @codeCoverageIgnoreStart - } catch (ReflectionException $e) { - throw new \PHPUnit\Framework\Exception($e->getMessage(), $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd - if ($method->isStatic()) { - $this->addTest($method->invoke(null, $testClass->getName())); - $suiteMethod = \true; - } - } - if (!$suiteMethod && !$testClass->isAbstract() && $testClass->isSubclassOf(\PHPUnit\Framework\TestCase::class)) { - $this->addTest(new self($testClass)); - } - } else { - throw new \PHPUnit\Framework\Exception(); + if (!$testClass->isSubclassOf(\PHPUnit\Framework\TestCase::class)) { + throw new \PHPUnit\Framework\Exception(sprintf('Class %s is not a subclass of %s', $testClass->getName(), \PHPUnit\Framework\TestCase::class)); } - } - public function addWarning(string $warning) : void - { - $this->warnings[] = $warning; + $this->addTest(self::fromClassReflector($testClass)); } /** * Wraps both addTest() and addTestSuite @@ -75287,102 +64934,28 @@ class TestSuite implements IteratorAggregate, \PHPUnit\Framework\Reorderable, \P * * @throws Exception */ - public function addTestFile(string $filename) : void + public function addTestFile(string $filename): void { - if (is_file($filename) && substr($filename, -5) === '.phpt') { - $this->addTest(new PhptTestCase($filename)); - $this->declaredClassesPointer = count(get_declared_classes()); - return; - } - $numTests = count($this->tests); - // 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_slice(get_declared_classes(), $this->declaredClassesPointer); - // 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->declaredClassesPointer = count(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); - // @codeCoverageIgnoreStart - } catch (ReflectionException $e) { - throw new \PHPUnit\Framework\Exception($e->getMessage(), $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd - if ($class->getFileName() == $filename) { - $newClasses = [$className]; - unset($this->foundClasses[$i]); - break; - } - } - } - foreach ($newClasses as $className) { + if (str_ends_with($filename, '.phpt') && is_file($filename)) { try { - $class = new ReflectionClass($className); - // @codeCoverageIgnoreStart - } catch (ReflectionException $e) { - throw new \PHPUnit\Framework\Exception($e->getMessage(), $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd - if (dirname($class->getFileName()) === __DIR__) { - continue; - } - if ($class->isAbstract() && $class->isSubclassOf(\PHPUnit\Framework\TestCase::class)) { - $this->addWarning(sprintf('Abstract test case classes with "Test" suffix are deprecated (%s)', $class->getName())); - } - if (!$class->isAbstract()) { - if ($class->hasMethod(BaseTestRunner::SUITE_METHODNAME)) { - try { - $method = $class->getMethod(BaseTestRunner::SUITE_METHODNAME); - // @codeCoverageIgnoreStart - } catch (ReflectionException $e) { - throw new \PHPUnit\Framework\Exception($e->getMessage(), $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd - if ($method->isStatic()) { - $this->addTest($method->invoke(null, $className)); - } - } elseif ($class->implementsInterface(\PHPUnit\Framework\Test::class)) { - // Do we have modern namespacing ('Foo\Bar\WhizBangTest') or old-school namespacing ('Foo_Bar_WhizBangTest')? - $isPsr0 = !$class->inNamespace() && strpos($class->getName(), '_') !== \false; - $expectedClassName = $isPsr0 ? $className : $shortName; - if (($pos = strpos($expectedClassName, '.')) !== \false) { - $expectedClassName = substr($expectedClassName, 0, $pos); - } - if ($class->getShortName() !== $expectedClassName) { - $this->addWarning(sprintf("Test case class not matching filename is deprecated\n in %s\n Class name was '%s', expected '%s'", $filename, $class->getShortName(), $expectedClassName)); - } - $this->addTestSuite($class); - } + $this->addTest(new PhptTestCase($filename)); + } catch (RunnerException $e) { + Event\Facade::emitter()->testRunnerTriggeredWarning($e->getMessage()); } + return; } - if (count($this->tests) > ++$numTests) { - $this->addWarning(sprintf("Multiple test case classes per file is deprecated\n in %s", $filename)); + try { + $this->addTestSuite((new TestSuiteLoader())->load($filename)); + } catch (RunnerException $e) { + Event\Facade::emitter()->testRunnerTriggeredWarning($e->getMessage()); } - $this->numTests = -1; } /** * Wrapper for addTestFile() that adds multiple test files. * * @throws Exception */ - public function addTestFiles(iterable $fileNames) : void + public function addTestFiles(iterable $fileNames): void { foreach ($fileNames as $filename) { $this->addTestFile((string) $filename); @@ -75390,21 +64963,28 @@ class TestSuite implements IteratorAggregate, \PHPUnit\Framework\Reorderable, \P } /** * Counts the number of test cases that will be run by this test. - * - * @todo refactor usage of numTests in DefaultResultPrinter */ - public function count() : int + public function count(): int + { + $numTests = 0; + foreach ($this as $test) { + $numTests += count($test); + } + return $numTests; + } + public function isEmpty(): bool { - $this->numTests = 0; foreach ($this as $test) { - $this->numTests += count($test); + if (count($test) !== 0) { + return \false; + } } - return $this->numTests; + return \true; } /** - * Returns the name of the suite. + * @psalm-return non-empty-string */ - public function getName() : string + public function name(): string { return $this->name; } @@ -75413,184 +64993,88 @@ class TestSuite implements IteratorAggregate, \PHPUnit\Framework\Reorderable, \P * * @psalm-return list */ - public function getGroups() : array + public function groups(): array { - return array_map(static function ($key) : string { - return (string) $key; - }, array_keys($this->groups)); + return array_map('strval', array_keys($this->groups)); } - public function getGroupDetails() : array + public function groupDetails(): 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 \SebastianBergmann\CodeCoverage\InvalidArgumentException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException * @throws CodeCoverageException + * @throws Event\RuntimeException + * @throws Exception + * @throws InvalidArgumentException + * @throws NoPreviousThrowableException * @throws UnintentionallyCoveredCodeException - * @throws Warning */ - public function run(?\PHPUnit\Framework\TestResult $result = null) : \PHPUnit\Framework\TestResult + public function run(): void { - if ($result === null) { - $result = $this->createResult(); + if ($this->wasRun) { + // @codeCoverageIgnoreStart + throw new \PHPUnit\Framework\Exception('The tests aggregated by this TestSuite were already run'); + // @codeCoverageIgnoreEnd } - if (count($this) === 0) { - return $result; + $this->wasRun = \true; + if ($this->isEmpty()) { + return; } - /** @psalm-var class-string $className */ - $className = $this->name; - $hookMethods = TestUtil::getHookMethods($className); - $result->startTestSuite($this); - $test = null; - if ($this->testCase && class_exists($this->name, \false)) { - try { - foreach ($hookMethods['beforeClass'] as $beforeClassMethod) { - if (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 (\PHPUnit\Framework\SkippedTestError|\PHPUnit\Framework\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) { - $errorAdded = \false; - foreach ($this->tests() as $test) { - if ($result->shouldStop()) { - break; - } - $result->startTest($test); - if (!$errorAdded) { - $result->addError($test, $t, 0); - $errorAdded = \true; - } else { - $result->addFailure($test, new \PHPUnit\Framework\SkippedTestError('Test skipped because of an error in hook method'), 0); - } - $result->endTest($test, 0); - } - $result->endTestSuite($this); - return $result; - } + $emitter = Event\Facade::emitter(); + $testSuiteValueObjectForEvents = Event\TestSuite\TestSuiteBuilder::from($this); + $emitter->testSuiteStarted($testSuiteValueObjectForEvents); + if (!$this->invokeMethodsBeforeFirstTest($emitter, $testSuiteValueObjectForEvents)) { + return; } + /** @psalm-var list $tests */ + $tests = []; foreach ($this as $test) { - if ($result->shouldStop()) { - break; - } - if ($test instanceof \PHPUnit\Framework\TestCase || $test instanceof self) { - $test->setBeStrictAboutChangesToGlobalState($this->beStrictAboutChangesToGlobalState); - $test->setBackupGlobals($this->backupGlobals); - $test->setBackupStaticAttributes($this->backupStaticAttributes); - $test->setRunTestInSeparateProcess($this->runTestInSeparateProcess); - } - $test->run($result); + $tests[] = $test; } - if ($this->testCase && class_exists($this->name, \false)) { - foreach ($hookMethods['afterClass'] as $afterClassMethod) { - if (method_exists($this->name, $afterClassMethod)) { - try { - call_user_func([$this->name, $afterClassMethod]); - } catch (Throwable $t) { - $message = "Exception in {$this->name}::{$afterClassMethod}" . PHP_EOL . $t->getMessage(); - $error = new \PHPUnit\Framework\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); - } - } + $tests = array_reverse($tests); + $this->tests = []; + $this->groups = []; + while (($test = array_pop($tests)) !== null) { + if (TestResultFacade::shouldStop()) { + $emitter->testRunnerExecutionAborted(); + break; } + $test->run(); } - $result->endTestSuite($this); - return $result; - } - public function setRunTestInSeparateProcess(bool $runTestInSeparateProcess) : void - { - $this->runTestInSeparateProcess = $runTestInSeparateProcess; - } - public function setName(string $name) : void - { - $this->name = $name; + $this->invokeMethodsAfterLastTest($emitter); + $emitter->testSuiteFinished($testSuiteValueObjectForEvents); } /** * Returns the tests as an enumeration. * - * @return Test[] + * @psalm-return list */ - public function tests() : array + public function tests(): array { return $this->tests; } /** * Set tests of the test suite. * - * @param Test[] $tests + * @psalm-param list $tests */ - public function setTests(array $tests) : void + public function setTests(array $tests): void { $this->tests = $tests; } /** * Mark the test suite as skipped. * - * @param string $message - * * @throws SkippedTestSuiteError - * - * @psalm-return never-return */ - public function markTestSuiteSkipped($message = '') : void + public function markTestSuiteSkipped(string $message = ''): never { throw new \PHPUnit\Framework\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 + public function getIterator(): Iterator { $iterator = new \PHPUnit\Framework\TestSuiteIterator($this); if ($this->iteratorFilter !== null) { @@ -75598,7 +65082,7 @@ class TestSuite implements IteratorAggregate, \PHPUnit\Framework\Reorderable, \P } return $iterator; } - public function injectFilter(Factory $filter) : void + public function injectFilter(Factory $filter): void { $this->iteratorFilter = $filter; foreach ($this as $test) { @@ -75608,16 +65092,9 @@ class TestSuite implements IteratorAggregate, \PHPUnit\Framework\Reorderable, \P } } /** - * @psalm-return array - */ - public function warnings() : array - { - return array_unique($this->warnings); - } - /** - * @return list + * @psalm-return list */ - public function provides() : array + public function provides(): array { if ($this->providedTests === null) { $this->providedTests = []; @@ -75626,9 +65103,7 @@ class TestSuite implements IteratorAggregate, \PHPUnit\Framework\Reorderable, \P } foreach ($this->tests as $test) { if (!$test instanceof \PHPUnit\Framework\Reorderable) { - // @codeCoverageIgnoreStart continue; - // @codeCoverageIgnoreEnd } $this->providedTests = \PHPUnit\Framework\ExecutionOrderDependency::mergeUnique($this->providedTests, $test->provides()); } @@ -75636,17 +65111,15 @@ class TestSuite implements IteratorAggregate, \PHPUnit\Framework\Reorderable, \P return $this->providedTests; } /** - * @return list + * @psalm-return list */ - public function requires() : array + public function requires(): array { if ($this->requiredTests === null) { $this->requiredTests = []; foreach ($this->tests as $test) { if (!$test instanceof \PHPUnit\Framework\Reorderable) { - // @codeCoverageIgnoreStart continue; - // @codeCoverageIgnoreEnd } $this->requiredTests = \PHPUnit\Framework\ExecutionOrderDependency::mergeUnique(\PHPUnit\Framework\ExecutionOrderDependency::filterInvalid($this->requiredTests), $test->requires()); } @@ -75654,104 +65127,191 @@ class TestSuite implements IteratorAggregate, \PHPUnit\Framework\Reorderable, \P } return $this->requiredTests; } - public function sortId() : string + public function sortId(): string { - return $this->getName() . '::class'; + return $this->name() . '::class'; } /** - * Creates a default TestResult object. + * @psalm-assert-if-true class-string $this->name */ - protected function createResult() : \PHPUnit\Framework\TestResult + public function isForTestClass(): bool { - return new \PHPUnit\Framework\TestResult(); + return class_exists($this->name, \false) && is_subclass_of($this->name, \PHPUnit\Framework\TestCase::class); } /** + * @throws Event\TestData\MoreThanOneDataSetFromDataProviderException * @throws Exception */ - protected function addTestMethod(ReflectionClass $class, ReflectionMethod $method) : void + protected function addTestMethod(ReflectionClass $class, ReflectionMethod $method): void { + $className = $class->getName(); $methodName = $method->getName(); - $test = (new \PHPUnit\Framework\TestBuilder())->build($class, $methodName); + assert(!empty($methodName)); + try { + $test = (new \PHPUnit\Framework\TestBuilder())->build($class, $methodName); + } catch (\PHPUnit\Framework\InvalidDataProviderException $e) { + Event\Facade::emitter()->testTriggeredPhpunitError(new TestMethod($className, $methodName, $class->getFileName(), $method->getStartLine(), Event\Code\TestDoxBuilder::fromClassNameAndMethodName($className, $methodName), MetadataCollection::fromArray([]), Event\TestData\TestDataCollection::fromArray([])), sprintf("The data provider specified for %s::%s is invalid\n%s", $className, $methodName, $this->throwableToString($e))); + return; + } if ($test instanceof \PHPUnit\Framework\TestCase || $test instanceof \PHPUnit\Framework\DataProviderTestSuite) { - $test->setDependencies(TestUtil::getDependencies($class->getName(), $methodName)); + $test->setDependencies(Dependencies::dependencies($class->getName(), $methodName)); } - $this->addTest($test, TestUtil::getGroups($class->getName(), $methodName)); + $this->addTest($test, (new Groups())->groups($class->getName(), $methodName)); } - private function clearCaches() : void + private function clearCaches(): void { - $this->numTests = -1; $this->providedTests = null; $this->requiredTests = null; } - private function containsOnlyVirtualGroups(array $groups) : bool + private function containsOnlyVirtualGroups(array $groups): bool { foreach ($groups as $group) { - if (strpos($group, '__phpunit_') !== 0) { + if (!str_starts_with($group, '__phpunit_')) { return \false; } } return \true; } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework; - -use function assert; -use function count; -use RecursiveIterator; -/** - * @template-implements RecursiveIterator - * - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class TestSuiteIterator implements RecursiveIterator -{ + private function methodDoesNotExistOrIsDeclaredInTestCase(string $methodName): bool + { + $reflector = new ReflectionClass($this->name); + return !$reflector->hasMethod($methodName) || $reflector->getMethod($methodName)->getDeclaringClass()->getName() === \PHPUnit\Framework\TestCase::class; + } /** - * @var int + * @throws Exception */ - private $position = 0; + private function throwableToString(Throwable $t): string + { + $message = $t->getMessage(); + if (empty(trim($message))) { + $message = ''; + } + if ($t instanceof \PHPUnit\Framework\InvalidDataProviderException) { + return sprintf("%s\n%s", $message, Filter::getFilteredStacktrace($t)); + } + return sprintf("%s: %s\n%s", $t::class, $message, Filter::getFilteredStacktrace($t)); + } + /** + * @throws Exception + * @throws NoPreviousThrowableException + */ + private function invokeMethodsBeforeFirstTest(Event\Emitter $emitter, Event\TestSuite\TestSuite $testSuiteValueObjectForEvents): bool + { + if (!$this->isForTestClass()) { + return \true; + } + $methodsCalledBeforeFirstTest = []; + $beforeClassMethods = (new HookMethods())->hookMethods($this->name)['beforeClass']; + try { + foreach ($beforeClassMethods as $beforeClassMethod) { + if ($this->methodDoesNotExistOrIsDeclaredInTestCase($beforeClassMethod)) { + continue; + } + if ($missingRequirements = (new Requirements())->requirementsNotSatisfiedFor($this->name, $beforeClassMethod)) { + $this->markTestSuiteSkipped(implode(PHP_EOL, $missingRequirements)); + } + $methodCalledBeforeFirstTest = new Event\Code\ClassMethod($this->name, $beforeClassMethod); + $emitter->testBeforeFirstTestMethodCalled($this->name, $methodCalledBeforeFirstTest); + $methodsCalledBeforeFirstTest[] = $methodCalledBeforeFirstTest; + call_user_func([$this->name, $beforeClassMethod]); + } + } catch (\PHPUnit\Framework\SkippedTest|\PHPUnit\Framework\SkippedTestSuiteError $e) { + $emitter->testSuiteSkipped($testSuiteValueObjectForEvents, $e->getMessage()); + return \false; + } catch (Throwable $t) { + assert(isset($methodCalledBeforeFirstTest)); + $emitter->testBeforeFirstTestMethodErrored($this->name, $methodCalledBeforeFirstTest, Event\Code\ThrowableBuilder::from($t)); + if (!empty($methodsCalledBeforeFirstTest)) { + $emitter->testBeforeFirstTestMethodFinished($this->name, ...$methodsCalledBeforeFirstTest); + } + return \false; + } + if (!empty($methodsCalledBeforeFirstTest)) { + $emitter->testBeforeFirstTestMethodFinished($this->name, ...$methodsCalledBeforeFirstTest); + } + return \true; + } + private function invokeMethodsAfterLastTest(Event\Emitter $emitter): void + { + if (!$this->isForTestClass()) { + return; + } + $methodsCalledAfterLastTest = []; + $afterClassMethods = (new HookMethods())->hookMethods($this->name)['afterClass']; + foreach ($afterClassMethods as $afterClassMethod) { + if ($this->methodDoesNotExistOrIsDeclaredInTestCase($afterClassMethod)) { + continue; + } + try { + call_user_func([$this->name, $afterClassMethod]); + $methodCalledAfterLastTest = new Event\Code\ClassMethod($this->name, $afterClassMethod); + $emitter->testAfterLastTestMethodCalled($this->name, $methodCalledAfterLastTest); + $methodsCalledAfterLastTest[] = $methodCalledAfterLastTest; + } catch (Throwable) { + // @todo + } + } + if (!empty($methodsCalledAfterLastTest)) { + $emitter->testAfterLastTestMethodFinished($this->name, ...$methodsCalledAfterLastTest); + } + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use function assert; +use function count; +use RecursiveIterator; +/** + * @template-implements RecursiveIterator + * + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class TestSuiteIterator implements RecursiveIterator +{ + private int $position = 0; /** - * @var Test[] + * @psalm-var list */ - private $tests; + private readonly array $tests; public function __construct(\PHPUnit\Framework\TestSuite $testSuite) { $this->tests = $testSuite->tests(); } - public function rewind() : void + public function rewind(): void { $this->position = 0; } - public function valid() : bool + public function valid(): bool { return $this->position < count($this->tests); } - public function key() : int + public function key(): int { return $this->position; } - public function current() : \PHPUnit\Framework\Test + public function current(): \PHPUnit\Framework\Test { return $this->tests[$this->position]; } - public function next() : void + public function next(): void { $this->position++; } /** * @throws NoChildTestSuiteException */ - public function getChildren() : self + public function getChildren(): self { if (!$this->hasChildren()) { throw new \PHPUnit\Framework\NoChildTestSuiteException('The current item is not a TestSuite instance and therefore does not have any children.'); @@ -75760,7 +65320,7 @@ final class TestSuiteIterator implements RecursiveIterator assert($current instanceof \PHPUnit\Framework\TestSuite); return new self($current); } - public function hasChildren() : bool + public function hasChildren(): bool { return $this->valid() && $this->current() instanceof \PHPUnit\Framework\TestSuite; } @@ -75776,53 +65336,47 @@ declare (strict_types=1); * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\Framework; +namespace PHPUnit\Logging; +use const FILE_APPEND; +use const LOCK_EX; +use const PHP_EOL; +use function file_put_contents; +use function implode; +use function preg_split; +use function str_repeat; +use function strlen; +use PHPUnit\Event\Event; +use PHPUnit\Event\Tracer\Tracer; /** * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class WarningTestCase extends \PHPUnit\Framework\TestCase +final class EventLogger implements Tracer { - /** - * @var ?bool - */ - protected $backupGlobals = \false; - /** - * @var ?bool - */ - protected $backupStaticAttributes = \false; - /** - * @var ?bool - */ - protected $runTestInSeparateProcess = \false; - /** - * @var string - */ - private $message; - public function __construct(string $message = '') + private readonly string $path; + private readonly bool $includeTelemetryInfo; + public function __construct(string $path, bool $includeTelemetryInfo) { - $this->message = $message; - parent::__construct('Warning'); - } - public function getMessage() : string - { - return $this->message; + $this->path = $path; + $this->includeTelemetryInfo = $includeTelemetryInfo; } - /** - * Returns a string representation of the test case. - */ - public function toString() : string + public function trace(Event $event): void { - return 'Warning'; + $telemetryInfo = $this->telemetryInfo($event); + $indentation = PHP_EOL . str_repeat(' ', strlen($telemetryInfo)); + $lines = preg_split('/\r\n|\r|\n/', $event->asString()); + $flags = FILE_APPEND; + if (!(\PHP_OS_FAMILY === 'Windows' || \PHP_OS_FAMILY === 'Darwin') || $this->path !== 'php://stdout') { + $flags |= LOCK_EX; + } + file_put_contents($this->path, $telemetryInfo . implode($indentation, $lines) . PHP_EOL, $flags); } - /** - * @throws Exception - * - * @psalm-return never-return - */ - protected function runTest() : void + private function telemetryInfo(Event $event): string { - throw new \PHPUnit\Framework\Warning($this->message); + if (!$this->includeTelemetryInfo) { + return ''; + } + return $event->telemetryInfo()->asString() . ' '; } } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Logging\JUnit; + +use function assert; +use function basename; +use function is_int; +use function sprintf; +use function str_replace; +use function trim; +use DOMDocument; +use DOMElement; +use PHPUnit\Event\Code\Test; +use PHPUnit\Event\Code\TestMethod; +use PHPUnit\Event\EventFacadeIsSealedException; +use PHPUnit\Event\Facade; +use PHPUnit\Event\InvalidArgumentException; +use PHPUnit\Event\Telemetry\HRTime; +use PHPUnit\Event\Telemetry\Info; +use PHPUnit\Event\Test\Errored; +use PHPUnit\Event\Test\Failed; +use PHPUnit\Event\Test\Finished; +use PHPUnit\Event\Test\MarkedIncomplete; +use PHPUnit\Event\Test\PreparationStarted; +use PHPUnit\Event\Test\Prepared; +use PHPUnit\Event\Test\Skipped; +use PHPUnit\Event\TestSuite\Started; +use PHPUnit\Event\UnknownSubscriberTypeException; +use PHPUnit\TextUI\Output\Printer; +use PHPUnit\Util\Xml; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class JunitXmlLogger +{ + private readonly Printer $printer; + private DOMDocument $document; + private DOMElement $root; /** - * @var int - */ - public const STATUS_UNKNOWN = -1; - /** - * @var int - */ - public const STATUS_PASSED = 0; - /** - * @var int + * @var DOMElement[] */ - public const STATUS_SKIPPED = 1; + private array $testSuites = []; /** - * @var int + * @psalm-var array */ - public const STATUS_INCOMPLETE = 2; + private array $testSuiteTests = [0]; /** - * @var int + * @psalm-var array */ - public const STATUS_FAILURE = 3; + private array $testSuiteAssertions = [0]; /** - * @var int + * @psalm-var array */ - public const STATUS_ERROR = 4; + private array $testSuiteErrors = [0]; /** - * @var int + * @psalm-var array */ - public const STATUS_RISKY = 5; + private array $testSuiteFailures = [0]; /** - * @var int + * @psalm-var array */ - public const STATUS_WARNING = 6; + private array $testSuiteSkipped = [0]; /** - * @var string + * @psalm-var array */ - public const SUITE_METHODNAME = 'suite'; + private array $testSuiteTimes = [0]; + private int $testSuiteLevel = 0; + private ?DOMElement $currentTestCase = null; + private ?HRTime $time = null; + private bool $prepared = \false; + private bool $preparationFailed = \false; /** - * Returns the loader to be used. + * @throws EventFacadeIsSealedException + * @throws UnknownSubscriberTypeException */ - public function getLoader() : \PHPUnit\Runner\TestSuiteLoader + public function __construct(Printer $printer, Facade $facade) { - return new \PHPUnit\Runner\StandardTestSuiteLoader(); + $this->printer = $printer; + $this->registerSubscribers($facade); + $this->createDocument(); } - /** - * 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 $suiteClassFile, $suffixes = '') : ?TestSuite + public function flush(): void { - if (is_dir($suiteClassFile)) { - /** @var string[] $files */ - $files = (new FileIteratorFacade())->getFilesAsArray($suiteClassFile, $suffixes); - $suite = new TestSuite($suiteClassFile); - $suite->addTestFiles($files); - return $suite; - } - if (is_file($suiteClassFile) && substr($suiteClassFile, -5, 5) === '.phpt') { - $suite = new TestSuite(); - $suite->addTestFile($suiteClassFile); - return $suite; + $this->printer->print($this->document->saveXML()); + $this->printer->flush(); + } + public function testSuiteStarted(Started $event): void + { + $testSuite = $this->document->createElement('testsuite'); + $testSuite->setAttribute('name', $event->testSuite()->name()); + if ($event->testSuite()->isForTestClass()) { + $testSuite->setAttribute('file', $event->testSuite()->file()); } - try { - $testClass = $this->loadSuiteClass($suiteClassFile); - } catch (\PHPUnit\Exception $e) { - $this->runFailed($e->getMessage()); - return null; + if ($this->testSuiteLevel > 0) { + $this->testSuites[$this->testSuiteLevel]->appendChild($testSuite); + } else { + $this->root->appendChild($testSuite); } - 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) { - $test = new TestSuite($testClass); + $this->testSuiteLevel++; + $this->testSuites[$this->testSuiteLevel] = $testSuite; + $this->testSuiteTests[$this->testSuiteLevel] = 0; + $this->testSuiteAssertions[$this->testSuiteLevel] = 0; + $this->testSuiteErrors[$this->testSuiteLevel] = 0; + $this->testSuiteFailures[$this->testSuiteLevel] = 0; + $this->testSuiteSkipped[$this->testSuiteLevel] = 0; + $this->testSuiteTimes[$this->testSuiteLevel] = 0; + } + public function testSuiteFinished(): 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('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->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->clearStatus(); - return $test; + $this->testSuiteLevel--; } /** - * Returns the loaded ReflectionClass for a suite name. + * @throws InvalidArgumentException */ - protected function loadSuiteClass(string $suiteClassFile) : ReflectionClass + public function testPreparationStarted(PreparationStarted $event): void { - return $this->getLoader()->load($suiteClassFile); + $this->createTestCase($event); } /** - * Clears the status message. + * @throws InvalidArgumentException */ - protected function clearStatus() : void + public function testPreparationFailed(): void { + $this->preparationFailed = \true; } /** - * Override to define how to handle a failed loading of - * a test suite. + * @throws InvalidArgumentException */ - protected abstract function runFailed(string $message) : void; -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Runner; - -use const DIRECTORY_SEPARATOR; -use const LOCK_EX; -use function assert; -use function dirname; -use function file_get_contents; -use function file_put_contents; -use function in_array; -use function is_array; -use function is_dir; -use function is_file; -use function json_decode; -use function json_encode; -use function sprintf; -use PHPUnit\Util\Filesystem; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class DefaultTestResultCache implements \PHPUnit\Runner\TestResultCache -{ + public function testPrepared(): void + { + $this->prepared = \true; + } /** - * @var int + * @throws InvalidArgumentException */ - private const VERSION = 1; + public function testFinished(Finished $event): void + { + if (!$this->prepared || $this->preparationFailed) { + return; + } + $this->handleFinish($event->telemetryInfo(), $event->numberOfAssertionsPerformed()); + } /** - * @psalm-var list + * @throws InvalidArgumentException */ - private const ALLOWED_TEST_STATUSES = [\PHPUnit\Runner\BaseTestRunner::STATUS_SKIPPED, \PHPUnit\Runner\BaseTestRunner::STATUS_INCOMPLETE, \PHPUnit\Runner\BaseTestRunner::STATUS_FAILURE, \PHPUnit\Runner\BaseTestRunner::STATUS_ERROR, \PHPUnit\Runner\BaseTestRunner::STATUS_RISKY, \PHPUnit\Runner\BaseTestRunner::STATUS_WARNING]; + public function testMarkedIncomplete(MarkedIncomplete $event): void + { + $this->handleIncompleteOrSkipped($event); + } /** - * @var string + * @throws InvalidArgumentException */ - private const DEFAULT_RESULT_CACHE_FILENAME = '.phpunit.result.cache'; + public function testSkipped(Skipped $event): void + { + $this->handleIncompleteOrSkipped($event); + } /** - * @var string + * @throws InvalidArgumentException */ - private $cacheFilename; + public function testErrored(Errored $event): void + { + $this->handleFault($event, 'error'); + $this->testSuiteErrors[$this->testSuiteLevel]++; + } /** - * @psalm-var array + * @throws InvalidArgumentException */ - private $defects = []; + public function testFailed(Failed $event): void + { + $this->handleFault($event, 'failure'); + $this->testSuiteFailures[$this->testSuiteLevel]++; + } /** - * @psalm-var array + * @throws InvalidArgumentException */ - private $times = []; - public function __construct(?string $filepath = null) + private function handleFinish(Info $telemetryInfo, int $numberOfAssertionsPerformed): void { - if ($filepath !== null && is_dir($filepath)) { - $filepath .= DIRECTORY_SEPARATOR . self::DEFAULT_RESULT_CACHE_FILENAME; - } - $this->cacheFilename = $filepath ?? $_ENV['PHPUNIT_RESULT_CACHE'] ?? self::DEFAULT_RESULT_CACHE_FILENAME; + assert($this->currentTestCase !== null); + assert($this->time !== null); + $time = $telemetryInfo->time()->duration($this->time)->asFloat(); + $this->testSuiteAssertions[$this->testSuiteLevel] += $numberOfAssertionsPerformed; + $this->currentTestCase->setAttribute('assertions', (string) $numberOfAssertionsPerformed); + $this->currentTestCase->setAttribute('time', sprintf('%F', $time)); + $this->testSuites[$this->testSuiteLevel]->appendChild($this->currentTestCase); + $this->testSuiteTests[$this->testSuiteLevel]++; + $this->testSuiteTimes[$this->testSuiteLevel] += $time; + $this->currentTestCase = null; + $this->time = null; + $this->prepared = \false; } - public function setState(string $testName, int $state) : void + /** + * @throws EventFacadeIsSealedException + * @throws UnknownSubscriberTypeException + */ + private function registerSubscribers(Facade $facade): void { - if (!in_array($state, self::ALLOWED_TEST_STATUSES, \true)) { - return; - } - $this->defects[$testName] = $state; + $facade->registerSubscribers(new \PHPUnit\Logging\JUnit\TestSuiteStartedSubscriber($this), new \PHPUnit\Logging\JUnit\TestSuiteFinishedSubscriber($this), new \PHPUnit\Logging\JUnit\TestPreparationStartedSubscriber($this), new \PHPUnit\Logging\JUnit\TestPreparationFailedSubscriber($this), new \PHPUnit\Logging\JUnit\TestPreparedSubscriber($this), new \PHPUnit\Logging\JUnit\TestFinishedSubscriber($this), new \PHPUnit\Logging\JUnit\TestErroredSubscriber($this), new \PHPUnit\Logging\JUnit\TestFailedSubscriber($this), new \PHPUnit\Logging\JUnit\TestMarkedIncompleteSubscriber($this), new \PHPUnit\Logging\JUnit\TestSkippedSubscriber($this), new \PHPUnit\Logging\JUnit\TestRunnerExecutionFinishedSubscriber($this)); } - public function getState(string $testName) : int + private function createDocument(): void { - return $this->defects[$testName] ?? \PHPUnit\Runner\BaseTestRunner::STATUS_UNKNOWN; + $this->document = new DOMDocument('1.0', 'UTF-8'); + $this->document->formatOutput = \true; + $this->root = $this->document->createElement('testsuites'); + $this->document->appendChild($this->root); } - public function setTime(string $testName, float $time) : void + /** + * @throws InvalidArgumentException + */ + private function handleFault(Errored|Failed $event, string $type): void { - $this->times[$testName] = $time; + if (!$this->prepared) { + $this->createTestCase($event); + } + assert($this->currentTestCase !== null); + $buffer = $this->testAsString($event->test()); + $throwable = $event->throwable(); + $buffer .= trim($throwable->description() . \PHP_EOL . $throwable->stackTrace()); + $fault = $this->document->createElement($type, Xml::prepareString($buffer)); + $fault->setAttribute('type', $throwable->className()); + $this->currentTestCase->appendChild($fault); + if (!$this->prepared) { + $this->handleFinish($event->telemetryInfo(), 0); + } } - public function getTime(string $testName) : float + /** + * @throws InvalidArgumentException + */ + private function handleIncompleteOrSkipped(MarkedIncomplete|Skipped $event): void { - return $this->times[$testName] ?? 0.0; + if (!$this->prepared) { + $this->createTestCase($event); + } + assert($this->currentTestCase !== null); + $skipped = $this->document->createElement('skipped'); + $this->currentTestCase->appendChild($skipped); + $this->testSuiteSkipped[$this->testSuiteLevel]++; + if (!$this->prepared) { + $this->handleFinish($event->telemetryInfo(), 0); + } } - public function load() : void + /** + * @throws InvalidArgumentException + */ + private function testAsString(Test $test): string { - if (!is_file($this->cacheFilename)) { - return; + if ($test->isPhpt()) { + return basename($test->file()); } - $data = json_decode(file_get_contents($this->cacheFilename), \true); - if ($data === null) { - return; + assert($test instanceof TestMethod); + return sprintf('%s::%s%s', $test->className(), $this->name($test), \PHP_EOL); + } + /** + * @throws InvalidArgumentException + */ + private function name(Test $test): string + { + if ($test->isPhpt()) { + return basename($test->file()); } - if (!isset($data['version'])) { - return; + assert($test instanceof TestMethod); + if (!$test->testData()->hasDataFromDataProvider()) { + return $test->methodName(); } - if ($data['version'] !== self::VERSION) { - return; + $dataSetName = $test->testData()->dataFromDataProvider()->dataSetName(); + if (is_int($dataSetName)) { + return sprintf('%s with data set #%d', $test->methodName(), $dataSetName); } - assert(isset($data['defects']) && is_array($data['defects'])); - assert(isset($data['times']) && is_array($data['times'])); - $this->defects = $data['defects']; - $this->times = $data['times']; + return sprintf('%s with data set "%s"', $test->methodName(), $dataSetName); } /** - * @throws Exception + * @throws InvalidArgumentException + * + * @psalm-assert !null $this->currentTestCase */ - public function persist() : void + private function createTestCase(Errored|Failed|MarkedIncomplete|PreparationStarted|Prepared|Skipped $event): void { - if (!Filesystem::createDirectory(dirname($this->cacheFilename))) { - throw new \PHPUnit\Runner\Exception(sprintf('Cannot create directory "%s" for result cache file', $this->cacheFilename)); + $testCase = $this->document->createElement('testcase'); + $test = $event->test(); + $testCase->setAttribute('name', $this->name($test)); + $testCase->setAttribute('file', $test->file()); + if ($test->isTestMethod()) { + assert($test instanceof TestMethod); + $testCase->setAttribute('line', (string) $test->line()); + $testCase->setAttribute('class', $test->className()); + $testCase->setAttribute('classname', str_replace('\\', '.', $test->className())); } - file_put_contents($this->cacheFilename, json_encode(['version' => self::VERSION, 'defects' => $this->defects, 'times' => $this->times]), LOCK_EX); + $this->currentTestCase = $testCase; + $this->time = $event->telemetryInfo()->time(); } } logger = $logger; + } + protected function logger(): \PHPUnit\Logging\JUnit\JunitXmlLogger + { + return $this->logger; + } } createInstance($extensionConfiguration); - if (!$extension instanceof Hook) { - throw new Exception(sprintf('Class "%s" does not implement a PHPUnit\\Runner\\Hook interface', $extensionConfiguration->className())); - } - $runner->addExtension($extension); - } - /** - * @throws Exception - * - * @deprecated + * @throws InvalidArgumentException */ - public function createTestListenerInstance(Extension $listenerConfiguration) : TestListener + public function notify(Errored $event): void { - $listener = $this->createInstance($listenerConfiguration); - if (!$listener instanceof TestListener) { - throw new Exception(sprintf('Class "%s" does not implement the PHPUnit\\Framework\\TestListener interface', $listenerConfiguration->className())); - } - return $listener; + $this->logger()->testErrored($event); } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Logging\JUnit; + +use PHPUnit\Event\InvalidArgumentException; +use PHPUnit\Event\Test\Failed; +use PHPUnit\Event\Test\FailedSubscriber; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class TestFailedSubscriber extends \PHPUnit\Logging\JUnit\Subscriber implements FailedSubscriber +{ /** - * @throws Exception + * @throws InvalidArgumentException */ - private function createInstance(Extension $extensionConfiguration) : object + public function notify(Failed $event): void { - $this->ensureClassExists($extensionConfiguration); - try { - $reflector = new ReflectionClass($extensionConfiguration->className()); - } catch (ReflectionException $e) { - throw new Exception($e->getMessage(), $e->getCode(), $e); - } - if (!$extensionConfiguration->hasArguments()) { - return $reflector->newInstance(); - } - return $reflector->newInstanceArgs($extensionConfiguration->arguments()); + $this->logger()->testFailed($event); } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Logging\JUnit; + +use PHPUnit\Event\InvalidArgumentException; +use PHPUnit\Event\Test\Finished; +use PHPUnit\Event\Test\FinishedSubscriber; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class TestFinishedSubscriber extends \PHPUnit\Logging\JUnit\Subscriber implements FinishedSubscriber +{ /** - * @throws Exception + * @throws InvalidArgumentException */ - private function ensureClassExists(Extension $extensionConfiguration) : void + public function notify(Finished $event): void { - if (class_exists($extensionConfiguration->className(), \false)) { - return; - } - if ($extensionConfiguration->hasSourceFile()) { - /** - * @noinspection PhpIncludeInspection - * - * @psalm-suppress UnresolvableInclude - */ - require_once $extensionConfiguration->sourceFile(); - } - if (!class_exists($extensionConfiguration->className())) { - throw new Exception(sprintf('Class "%s" does not exist', $extensionConfiguration->className())); - } + $this->logger()->testFinished($event); } } , notLoadedExtensions: list} + * @throws InvalidArgumentException */ - public function loadPharExtensionsInDirectory(string $directory) : array - { - $loadedExtensions = []; - $notLoadedExtensions = []; - foreach ((new FileIteratorFacade())->getFilesAsArray($directory, '.phar') as $file) { - if (!is_file('phar://' . $file . '/manifest.xml')) { - $notLoadedExtensions[] = $file . ' is not an extension for PHPUnit'; - continue; - } - try { - $applicationName = new ApplicationName('phpunit/phpunit'); - $version = new PharIoVersion($this->phpunitVersion()); - $manifest = ManifestLoader::fromFile('phar://' . $file . '/manifest.xml'); - if (!$manifest->isExtensionFor($applicationName)) { - $notLoadedExtensions[] = $file . ' is not an extension for PHPUnit'; - continue; - } - if (!$manifest->isExtensionFor($applicationName, $version)) { - $notLoadedExtensions[] = $file . ' is not compatible with this version of PHPUnit'; - continue; - } - } catch (ManifestException $e) { - $notLoadedExtensions[] = $file . ': ' . $e->getMessage(); - continue; - } - /** - * @noinspection PhpIncludeInspection - * - * @psalm-suppress UnresolvableInclude - */ - require $file; - $loadedExtensions[] = $manifest->getName()->asString() . ' ' . $manifest->getVersion()->getVersionString(); - } - return ['loadedExtensions' => $loadedExtensions, 'notLoadedExtensions' => $notLoadedExtensions]; - } - private function phpunitVersion() : string + public function notify(MarkedIncomplete $event): void { - $version = Version::id(); - if (strpos($version, '-') === \false) { - return $version; - } - $parts = explode('.', explode('-', $version)[0]); - if (count($parts) === 2) { - $parts[] = 0; - } - return implode('.', $parts); + $this->logger()->testMarkedIncomplete($event); } } groupTests, \true); + $this->logger()->testPreparationFailed(); } } - */ - private $filters = []; - /** - * @param array|string $args - * - * @throws Exception + * @throws InvalidArgumentException */ - public function addFilter(ReflectionClass $filter, $args) : void + public function notify(PreparationStarted $event): void { - if (!$filter->isSubclassOf(RecursiveFilterIterator::class)) { - throw new Exception(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); - } - assert($iterator instanceof FilterIterator); - return $iterator; + $this->logger()->testPreparationStarted($event); } } 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 + public function notify(Prepared $event): void { - $test = $this->getInnerIterator()->current(); - if ($test instanceof TestSuite) { - return \true; - } - return $this->doAccept(spl_object_hash($test)); + $this->logger()->testPrepared(); } - protected abstract function doAccept(string $hash); } groupTests, \true); + $this->logger()->flush(); } } setFilter($filter); - } - /** - * @throws InvalidArgumentException - */ - public function accept() : bool - { - $test = $this->getInnerIterator()->current(); - if ($test instanceof TestSuite) { - return \true; - } - $tmp = Test::describe($test); - if ($test instanceof ErrorTestCase || $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 + public function notify(Skipped $event): 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 = (int) $matches[2]; - $this->filterMax = (int) $matches[3]; - } else { - $filter = sprintf('%s.*with data set #%s$', $matches[1], $matches[2]); - } - } 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; + $this->logger()->testSkipped($event); } } logger()->testSuiteFinished(); + } } logger()->testSuiteStarted($event); + } } logger = $logger; + } + protected function logger(): \PHPUnit\Logging\TeamCity\TeamCityLogger + { + return $this->logger; + } } logger()->testConsideredRisky($event); + } } logger()->testErrored($event); + } } logger()->testFailed($event); + } } logger()->testFinished($event); + } } logger()->testMarkedIncomplete($event); + } } logger()->testPrepared($event); + } } logger()->flush(); + } } logger()->testSkipped($event); + } } logger()->testSuiteFinished($event); + } } logger()->testSuiteStarted($event); + } } hooks[] = $hook; + $this->printer = $printer; + $this->registerSubscribers($facade); + $this->setFlowId(); } - public function startTest(Test $test) : void + public function testSuiteStarted(TestSuiteStarted $event): void { - foreach ($this->hooks as $hook) { - if ($hook instanceof \PHPUnit\Runner\BeforeTestHook) { - $hook->executeBeforeTest(TestUtil::describeAsString($test)); - } + $testSuite = $event->testSuite(); + if (!$this->isSummaryTestCountPrinted) { + $this->isSummaryTestCountPrinted = \true; + $this->writeMessage('testCount', ['count' => $testSuite->count()]); + } + $parameters = ['name' => $testSuite->name()]; + if ($testSuite->isForTestClass()) { + assert($testSuite instanceof TestSuiteForTestClass); + $parameters['locationHint'] = sprintf('php_qn://%s::\%s', $testSuite->file(), $testSuite->name()); + } elseif ($testSuite->isForTestMethodWithDataProvider()) { + assert($testSuite instanceof TestSuiteForTestMethodWithDataProvider); + $parameters['locationHint'] = sprintf('php_qn://%s::\%s', $testSuite->file(), $testSuite->name()); + $parameters['name'] = $testSuite->methodName(); } - $this->lastTestWasNotSuccessful = \false; + $this->writeMessage('testSuiteStarted', $parameters); } - public function addError(Test $test, Throwable $t, float $time) : void + public function testSuiteFinished(TestSuiteFinished $event): void { - foreach ($this->hooks as $hook) { - if ($hook instanceof \PHPUnit\Runner\AfterTestErrorHook) { - $hook->executeAfterTestError(TestUtil::describeAsString($test), $t->getMessage(), $time); - } + $testSuite = $event->testSuite(); + $parameters = ['name' => $testSuite->name()]; + if ($testSuite->isForTestMethodWithDataProvider()) { + assert($testSuite instanceof TestSuiteForTestMethodWithDataProvider); + $parameters['name'] = $testSuite->methodName(); } - $this->lastTestWasNotSuccessful = \true; + $this->writeMessage('testSuiteFinished', $parameters); } - public function addWarning(Test $test, Warning $e, float $time) : void + public function testPrepared(Prepared $event): void { - foreach ($this->hooks as $hook) { - if ($hook instanceof \PHPUnit\Runner\AfterTestWarningHook) { - $hook->executeAfterTestWarning(TestUtil::describeAsString($test), $e->getMessage(), $time); - } + $test = $event->test(); + $parameters = ['name' => $test->name()]; + if ($test->isTestMethod()) { + assert($test instanceof TestMethod); + $parameters['locationHint'] = sprintf('php_qn://%s::\%s::%s', $test->file(), $test->className(), $test->name()); } - $this->lastTestWasNotSuccessful = \true; + $this->writeMessage('testStarted', $parameters); + $this->time = $event->telemetryInfo()->time(); } - public function addFailure(Test $test, AssertionFailedError $e, float $time) : void + /** + * @throws InvalidArgumentException + */ + public function testMarkedIncomplete(MarkedIncomplete $event): void { - foreach ($this->hooks as $hook) { - if ($hook instanceof \PHPUnit\Runner\AfterTestFailureHook) { - $hook->executeAfterTestFailure(TestUtil::describeAsString($test), $e->getMessage(), $time); - } + if ($this->time === null) { + $this->time = $event->telemetryInfo()->time(); } - $this->lastTestWasNotSuccessful = \true; + $this->writeMessage('testIgnored', ['name' => $event->test()->name(), 'message' => $event->throwable()->message(), 'details' => $this->details($event->throwable()), 'duration' => $this->duration($event)]); } - public function addIncompleteTest(Test $test, Throwable $t, float $time) : void + /** + * @throws InvalidArgumentException + */ + public function testSkipped(Skipped $event): void { - foreach ($this->hooks as $hook) { - if ($hook instanceof \PHPUnit\Runner\AfterIncompleteTestHook) { - $hook->executeAfterIncompleteTest(TestUtil::describeAsString($test), $t->getMessage(), $time); - } + if ($this->time === null) { + $this->time = $event->telemetryInfo()->time(); } - $this->lastTestWasNotSuccessful = \true; + $parameters = ['name' => $event->test()->name(), 'message' => $event->message()]; + $parameters['duration'] = $this->duration($event); + $this->writeMessage('testIgnored', $parameters); } - public function addRiskyTest(Test $test, Throwable $t, float $time) : void + /** + * @throws InvalidArgumentException + */ + public function testErrored(Errored $event): void { - foreach ($this->hooks as $hook) { - if ($hook instanceof \PHPUnit\Runner\AfterRiskyTestHook) { - $hook->executeAfterRiskyTest(TestUtil::describeAsString($test), $t->getMessage(), $time); - } + if ($this->time === null) { + $this->time = $event->telemetryInfo()->time(); + } + $this->writeMessage('testFailed', ['name' => $event->test()->name(), 'message' => $this->message($event->throwable()), 'details' => $this->details($event->throwable()), 'duration' => $this->duration($event)]); + } + /** + * @throws InvalidArgumentException + */ + public function testFailed(Failed $event): void + { + if ($this->time === null) { + $this->time = $event->telemetryInfo()->time(); } - $this->lastTestWasNotSuccessful = \true; + $parameters = ['name' => $event->test()->name(), 'message' => $this->message($event->throwable()), 'details' => $this->details($event->throwable()), 'duration' => $this->duration($event)]; + if ($event->hasComparisonFailure()) { + $parameters['type'] = 'comparisonFailure'; + $parameters['actual'] = $event->comparisonFailure()->actual(); + $parameters['expected'] = $event->comparisonFailure()->expected(); + } + $this->writeMessage('testFailed', $parameters); } - public function addSkippedTest(Test $test, Throwable $t, float $time) : void + /** + * @throws InvalidArgumentException + */ + public function testConsideredRisky(ConsideredRisky $event): void { - foreach ($this->hooks as $hook) { - if ($hook instanceof \PHPUnit\Runner\AfterSkippedTestHook) { - $hook->executeAfterSkippedTest(TestUtil::describeAsString($test), $t->getMessage(), $time); - } + if ($this->time === null) { + $this->time = $event->telemetryInfo()->time(); } - $this->lastTestWasNotSuccessful = \true; + $this->writeMessage('testFailed', ['name' => $event->test()->name(), 'message' => $event->message(), 'details' => '', 'duration' => $this->duration($event)]); + } + /** + * @throws InvalidArgumentException + */ + public function testFinished(Finished $event): void + { + $this->writeMessage('testFinished', ['name' => $event->test()->name(), 'duration' => $this->duration($event)]); + $this->time = null; } - public function endTest(Test $test, float $time) : void + public function flush(): void { - if (!$this->lastTestWasNotSuccessful) { - foreach ($this->hooks as $hook) { - if ($hook instanceof \PHPUnit\Runner\AfterSuccessfulTestHook) { - $hook->executeAfterSuccessfulTest(TestUtil::describeAsString($test), $time); - } - } + $this->printer->flush(); + } + /** + * @throws EventFacadeIsSealedException + * @throws UnknownSubscriberTypeException + */ + private function registerSubscribers(Facade $facade): void + { + $facade->registerSubscribers(new \PHPUnit\Logging\TeamCity\TestSuiteStartedSubscriber($this), new \PHPUnit\Logging\TeamCity\TestSuiteFinishedSubscriber($this), new \PHPUnit\Logging\TeamCity\TestPreparedSubscriber($this), new \PHPUnit\Logging\TeamCity\TestFinishedSubscriber($this), new \PHPUnit\Logging\TeamCity\TestErroredSubscriber($this), new \PHPUnit\Logging\TeamCity\TestFailedSubscriber($this), new \PHPUnit\Logging\TeamCity\TestMarkedIncompleteSubscriber($this), new \PHPUnit\Logging\TeamCity\TestSkippedSubscriber($this), new \PHPUnit\Logging\TeamCity\TestConsideredRiskySubscriber($this), new \PHPUnit\Logging\TeamCity\TestRunnerExecutionFinishedSubscriber($this)); + } + private function setFlowId(): void + { + if (stripos(ini_get('disable_functions'), 'getmypid') === \false) { + $this->flowId = getmypid(); } - foreach ($this->hooks as $hook) { - if ($hook instanceof \PHPUnit\Runner\AfterTestHook) { - $hook->executeAfterTest(TestUtil::describeAsString($test), $time); - } + } + private function writeMessage(string $eventName, array $parameters = []): void + { + $this->printer->print(sprintf("\n##teamcity[%s", $eventName)); + if ($this->flowId !== null) { + $parameters['flowId'] = $this->flowId; + } + foreach ($parameters as $key => $value) { + $this->printer->print(sprintf(" %s='%s'", $key, $this->escape((string) $value))); + } + $this->printer->print("]\n"); + } + /** + * @throws InvalidArgumentException + */ + private function duration(Event $event): int + { + if ($this->time === null) { + return 0; } + return (int) round($event->telemetryInfo()->time()->duration($this->time)->asFloat() * 1000); + } + private function escape(string $string): string + { + return str_replace(['|', "'", "\n", "\r", ']', '['], ['||', "|'", '|n', '|r', '|]', '|['], $string); } - public function startTestSuite(TestSuite $suite) : void + private function message(Throwable $throwable): string { + if (is_a($throwable->className(), FrameworkException::class, \true)) { + return $throwable->message(); + } + $buffer = $throwable->className(); + if (!empty($throwable->message())) { + $buffer .= ': ' . $throwable->message(); + } + return $buffer; } - public function endTestSuite(TestSuite $suite) : void + private function details(Throwable $throwable): string { + $buffer = $throwable->stackTrace(); + while ($throwable->hasPrevious()) { + $throwable = $throwable->previous(); + $buffer .= sprintf("\nCaused by\n%s\n%s", $throwable->description(), $throwable->stackTrace()); + } + return $buffer; } } + + + + Test Documentation + + + +EOT; + /** + * @var string + */ + private const CLASS_HEADER = <<<'EOT' + +

    %s

    +
      + +EOT; + /** + * @var string + */ + private const CLASS_FOOTER = <<<'EOT' +
    +EOT; + /** + * @var string + */ + private const PAGE_FOOTER = <<<'EOT' + + + +EOT; + /** + * @psalm-param array $tests + */ + public function render(array $tests): string { + $buffer = self::PAGE_HEADER; + foreach ($tests as $prettifiedClassName => $_tests) { + $buffer .= sprintf(self::CLASS_HEADER, $prettifiedClassName); + foreach ($this->reduce($_tests) as $prettifiedMethodName => $outcome) { + $buffer .= sprintf("
  • %s
  • \n", $outcome, $prettifiedMethodName); + } + $buffer .= self::CLASS_FOOTER; + } + return $buffer . self::PAGE_FOOTER; } - public function persist() : void + /** + * @psalm-return array + */ + private function reduce(\PHPUnit\Logging\TestDox\TestResultCollection $tests): array { + $result = []; + foreach ($tests as $test) { + $prettifiedMethodName = $test->test()->testDox()->prettifiedMethodName(); + if (!isset($result[$prettifiedMethodName])) { + $result[$prettifiedMethodName] = $test->status()->isSuccess() ? 'success' : 'defect'; + continue; + } + if ($test->status()->isSuccess()) { + continue; + } + $result[$prettifiedMethodName] = 'defect'; + } + return $result; } } */ - private $output = ''; + private static array $strings = []; /** - * Constructs a test case with the given filename. - * - * @throws Exception + * @psalm-param class-string $className */ - public function __construct(string $filename, ?AbstractPhpProcess $phpUtil = null) + public function prettifyTestClassName(string $className): string { - if (!is_file($filename)) { - throw new \PHPUnit\Runner\Exception(sprintf('File "%s" does not exist.', $filename)); + if (class_exists($className)) { + $classLevelTestDox = MetadataRegistry::parser()->forClass($className)->isTestDox(); + if ($classLevelTestDox->isNotEmpty()) { + $classLevelTestDox = $classLevelTestDox->asArray()[0]; + assert($classLevelTestDox instanceof TestDox); + return $classLevelTestDox->text(); + } } - $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 \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws Exception - * @throws InvalidArgumentException - * @throws UnintentionallyCoveredCodeException - */ - public function run(?TestResult $result = null) : TestResult - { - if ($result === null) { - $result = new TestResult(); + $parts = explode('\\', $className); + $className = array_pop($parts); + if (str_ends_with($className, 'Test')) { + $className = substr($className, 0, strlen($className) - strlen('Test')); } - try { - $sections = $this->parse(); - } catch (\PHPUnit\Runner\Exception $e) { - $result->startTest($this); - $result->addFailure($this, new SkippedTestError($e->getMessage()), 0); - $result->endTest($this, 0); - return $result; + if (str_starts_with($className, 'Tests')) { + $className = substr($className, strlen('Tests')); + } elseif (str_starts_with($className, 'Test')) { + $className = substr($className, strlen('Test')); } - $code = $this->render($sections['FILE']); - $xfail = \false; - $settings = $this->parseIniSection($this->settings($result->getCollectCodeCoverageInformation())); - $result->startTest($this); - if (isset($sections['INI'])) { - $settings = $this->parseIniSection($sections['INI'], $settings); + if (empty($className)) { + $className = 'UnnamedTests'; } - if (isset($sections['ENV'])) { - $env = $this->parseEnvSection($sections['ENV']); - $this->phpUtil->setEnv($env); + if (!empty($parts)) { + $parts[] = $className; + $fullyQualifiedName = implode('\\', $parts); + } else { + $fullyQualifiedName = $className; } - $this->phpUtil->setUseStderrRedirection(\true); - if ($result->enforcesTimeLimit()) { - $this->phpUtil->setTimeout($result->getTimeoutForLargeTests()); + $result = preg_replace('/(?<=[[:lower:]])(?=[[:upper:]])/u', ' ', $className); + if ($fullyQualifiedName !== $className) { + return $result . ' (' . $fullyQualifiedName . ')'; } - $skip = $this->runSkip($sections, $result, $settings); - if ($skip) { - return $result; + return $result; + } + // NOTE: this method is on a hot path and very performance sensitive. change with care. + public function prettifyTestMethodName(string $name): string + { + if ($name === '') { + return ''; } - if (isset($sections['XFAIL'])) { - $xfail = trim($sections['XFAIL']); + $string = rtrim($name, '0123456789'); + if (array_key_exists($string, self::$strings)) { + $name = $string; + } elseif ($string === $name) { + self::$strings[$string] = 1; } - if (isset($sections['STDIN'])) { - $this->phpUtil->setStdin($sections['STDIN']); + if (str_starts_with($name, 'test_')) { + $name = substr($name, 5); + } elseif (str_starts_with($name, 'test')) { + $name = substr($name, 4); } - if (isset($sections['ARGS'])) { - $this->phpUtil->setArgs($sections['ARGS']); + if ($name === '') { + return ''; } - if ($result->getCollectCodeCoverageInformation()) { - $codeCoverageCacheDirectory = null; - $pathCoverage = \false; - $codeCoverage = $result->getCodeCoverage(); - if ($codeCoverage) { - if ($codeCoverage->cachesStaticAnalysis()) { - $codeCoverageCacheDirectory = $codeCoverage->cacheDirectory(); + $name[0] = strtoupper($name[0]); + $noUnderscore = str_replace('_', ' ', $name); + if ($noUnderscore !== $name) { + return trim($noUnderscore); + } + $wasNumeric = \false; + $buffer = ''; + $len = strlen($name); + for ($i = 0; $i < $len; $i++) { + if ($i > 0 && $name[$i] >= 'A' && $name[$i] <= 'Z') { + $buffer .= ' ' . strtolower($name[$i]); + } else { + $isNumeric = $name[$i] >= '0' && $name[$i] <= '9'; + if (!$wasNumeric && $isNumeric) { + $buffer .= ' '; + $wasNumeric = \true; + } + if ($wasNumeric && !$isNumeric) { + $wasNumeric = \false; } - $pathCoverage = $codeCoverage->collectsBranchAndPathCoverage(); + $buffer .= $name[$i]; } - $this->renderForCoverage($code, $pathCoverage, $codeCoverageCacheDirectory); } - $timer = new Timer(); - $timer->start(); - $jobResult = $this->phpUtil->runJob($code, $this->stringifyIni($settings)); - $time = $timer->stop()->asSeconds(); - $this->output = $jobResult['stdout'] ?? ''; - if (isset($codeCoverage) && ($coverage = $this->cleanupForCoverage())) { - $codeCoverage->append($coverage, $this, \true, [], []); + return $buffer; + } + public function prettifyTestCase(TestCase $test, bool $colorize): string + { + $annotationWithPlaceholders = \false; + $methodLevelTestDox = MetadataRegistry::parser()->forMethod($test::class, $test->name())->isTestDox()->isMethodLevel(); + if ($methodLevelTestDox->isNotEmpty()) { + $methodLevelTestDox = $methodLevelTestDox->asArray()[0]; + assert($methodLevelTestDox instanceof TestDox); + $result = $methodLevelTestDox->text(); + if (str_contains($result, '$')) { + $annotation = $result; + $providedData = $this->mapTestMethodParameterNamesToProvidedDataValues($test, $colorize); + $variables = array_map(static fn(string $variable): string => sprintf('/%s(?=\b)/', preg_quote($variable, '/')), array_keys($providedData)); + $result = trim(preg_replace($variables, $providedData, $annotation)); + $annotationWithPlaceholders = \true; + } + } else { + $result = $this->prettifyTestMethodName($test->name()); } - 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); + if (!$annotationWithPlaceholders && $test->usesDataProvider()) { + $result .= $this->prettifyDataSet($test, $colorize); } - $this->runClean($sections, $result->getCollectCodeCoverageInformation()); - $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); - } - public function sortId() : string - { - return $this->filename; - } - /** - * @return list - */ - public function provides() : array - { - return []; - } - /** - * @return list - */ - public function requires() : array - { - return []; - } - /** - * Parse --INI-- section key value pairs and return as array. - * - * @param array|string $content - */ - private function parseIniSection($content, array $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 \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws Exception - * @throws ExpectationFailedException - */ - private function assertPhptExpectation(array $sections, string $output) : void - { - $assertions = ['EXPECT' => 'assertEquals', 'EXPECTF' => 'assertStringMatchesFormat', 'EXPECTREGEX' => 'assertMatchesRegularExpression']; - $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 === '') { - throw new \PHPUnit\Runner\Exception('No PHPT expectation found'); - } - Assert::$sectionAssertion($expected, $actual); - return; - } - } - throw new \PHPUnit\Runner\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, bool $collectCoverage) : void - { - $this->phpUtil->setStdin(''); - $this->phpUtil->setArgs(''); - if (isset($sections['CLEAN'])) { - $cleanCode = $this->render($sections['CLEAN']); - $this->phpUtil->runJob($cleanCode, $this->settings($collectCoverage)); - } - } - /** - * @throws Exception - */ - private function parse() : array + public function prettifyDataSet(TestCase $test, bool $colorize): string { - $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 \PHPUnit\Runner\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 \PHPUnit\Runner\Exception('Invalid PHPT file'); + if (!$colorize) { + return $test->dataSetAsString(); } - foreach ($unsupportedSections as $section) { - if (isset($sections[$section])) { - throw new \PHPUnit\Runner\Exception("PHPUnit does not support PHPT {$section} sections"); - } + if (is_int($test->dataName())) { + return Color::dim(' with data set ') . Color::colorize('fg-cyan', (string) $test->dataName()); } - return $sections; + return Color::dim(' with ') . Color::colorize('fg-cyan', Color::visualizeWhitespace($test->dataName())); } - /** - * @throws Exception - */ - private function parseExternal(array &$sections) : void + private function mapTestMethodParameterNamesToProvidedDataValues(TestCase $test, bool $colorize): array { - $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 \PHPUnit\Runner\Exception(sprintf('Could not load --%s-- %s for PHPT file', $section . '_EXTERNAL', $testDirectory . $externalFilename)); - } - $sections[$section] = file_get_contents($testDirectory . $externalFilename); + assert(method_exists($test, $test->name())); + /** @noinspection PhpUnhandledExceptionInspection */ + $reflector = new ReflectionMethod($test::class, $test->name()); + $providedData = []; + $providedDataValues = array_values($test->providedData()); + $i = 0; + $providedData['$_dataName'] = $test->dataName(); + foreach ($reflector->getParameters() as $parameter) { + if (!array_key_exists($i, $providedDataValues) && $parameter->isDefaultValueAvailable()) { + $providedDataValues[$i] = $parameter->getDefaultValue(); } - } - } - 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; + $value = $providedDataValues[$i++] ?? null; + if (is_object($value)) { + $reflector = new ReflectionObject($value); + if ($reflector->isEnum()) { + $enumReflector = new ReflectionEnum($value); + if ($enumReflector->isBacked()) { + $value = $value->value; + } else { + $value = $value->name; } - } - 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, bool $pathCoverage, ?string $codeCoverageCacheDirectory) : void - { - $files = $this->getCoverageFiles(); - $template = new Template(__DIR__ . '/../Util/PHP/Template/PhptTestCase.tpl'); - $composerAutoload = '\'\''; - if (defined('PHPUNIT_COMPOSER_INSTALL')) { - $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"; - } - if ($codeCoverageCacheDirectory === null) { - $codeCoverageCacheDirectory = 'null'; - } else { - $codeCoverageCacheDirectory = "'" . $codeCoverageCacheDirectory . "'"; - } - $template->setVar(['composerAutoload' => $composerAutoload, 'phar' => $phar, 'globals' => $globals, 'job' => $files['job'], 'coverageFile' => $files['coverage'], 'driverMethod' => $pathCoverage ? 'forLineAndPathCoverage' : 'forLineCoverage', 'codeCoverageCacheDirectory' => $codeCoverageCacheDirectory]); - file_put_contents($files['job'], $job); - $job = $template->render(); - } - private function cleanupForCoverage() : RawCodeCoverageData - { - $coverage = RawCodeCoverageData::fromXdebugWithoutPathCoverage([]); - $files = $this->getCoverageFiles(); - if (is_file($files['coverage'])) { - $buffer = @file_get_contents($files['coverage']); - if ($buffer !== \false) { - $coverage = @unserialize($buffer); - if ($coverage === \false) { - $coverage = RawCodeCoverageData::fromXdebugWithoutPathCoverage([]); + } elseif ($reflector->hasMethod('__toString')) { + $value = (string) $value; + } else { + $value = $value::class; } } - } - 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; + if (!is_scalar($value)) { + $value = gettype($value); + if ($value === 'NULL') { + $value = 'null'; } - 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 (is_bool($value) || is_int($value) || is_float($value)) { + $value = (new Exporter())->export($value); } - if ($block === 'diff') { - if (strpos($line, '+') === 0) { - $needle = $this->getCleanDiffLine($previousLine); - break; - } - if (strpos($line, '-') === 0) { - $needle = $this->getCleanDiffLine($line); - break; + if ($value === '') { + if ($colorize) { + $value = Color::colorize('dim,underlined', 'empty'); + } else { + $value = "''"; } } - if (!empty($line)) { - $previousLine = $line; - } + $providedData['$' . $parameter->getName()] = $value; } - return $this->getLocationHint($needle, $sections); - } - private function getCleanDiffLine(string $line) : string - { - if (preg_match('/^[\\-+]([\'\\"]?)(.*)\\1$/', $line, $matches)) { - $line = $matches[2]; + if ($colorize) { + $providedData = array_map(static fn($value) => Color::colorize('fg-cyan', Color::visualizeWhitespace((string) $value, \true)), $providedData); } - return $line; + return $providedData; } - private function getLocationHint(string $needle, array $sections, ?string $sectionName = null) : array +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Logging\TestDox; + +use function sprintf; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class PlainTextRenderer +{ + /** + * @psalm-param array $tests + */ + public function render(array $tests): string { - $needle = trim($needle); - if (empty($needle)) { - return [['file' => realpath($this->filename), 'line' => 1]]; - } - if ($sectionName) { - $search = [$sectionName]; - } else { - $search = [ - // 'FILE', - 'EXPECT', - 'EXPECTF', - 'EXPECTREGEX', - ]; - } - $sectionOffset = null; - 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++; + $buffer = ''; + foreach ($tests as $prettifiedClassName => $_tests) { + $buffer .= $prettifiedClassName . "\n"; + foreach ($this->reduce($_tests) as $prettifiedMethodName => $outcome) { + $buffer .= sprintf(' [%s] %s' . "\n", $outcome, $prettifiedMethodName); } + $buffer .= "\n"; } - 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]]; + return $buffer; } /** - * @psalm-return list + * @psalm-return array */ - private function settings(bool $collectCoverage) : array + private function reduce(\PHPUnit\Logging\TestDox\TestResultCollection $tests): array { - $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', 'open_basedir=', 'output_buffering=Off', 'output_handler=', 'report_memleaks=0', 'report_zend_debug=0']; - if (extension_loaded('pcov')) { - if ($collectCoverage) { - $settings[] = 'pcov.enabled=1'; - } else { - $settings[] = 'pcov.enabled=0'; + $result = []; + foreach ($tests as $test) { + $prettifiedMethodName = $test->test()->testDox()->prettifiedMethodName(); + $success = \true; + if ($test->status()->isError() || $test->status()->isFailure() || $test->status()->isIncomplete() || $test->status()->isSkipped()) { + $success = \false; } - } - if (extension_loaded('xdebug')) { - if (version_compare(phpversion('xdebug'), '3', '>=')) { - if ($collectCoverage) { - $settings[] = 'xdebug.mode=coverage'; - } else { - $settings[] = 'xdebug.mode=off'; - } - } else { - $settings[] = 'xdebug.default_enable=0'; - if ($collectCoverage) { - $settings[] = 'xdebug.coverage_enable=1'; - } + if (!isset($result[$prettifiedMethodName])) { + $result[$prettifiedMethodName] = $success ? 'x' : ' '; + continue; + } + if ($success) { + continue; } + $result[$prettifiedMethodName] = ' '; } - return $settings; + return $result; } } 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, \PHPUnit\Runner\BaseTestRunner::STATUS_INCOMPLETE); - } - public function executeAfterRiskyTest(string $test, string $message, float $time) : void + private readonly \PHPUnit\Logging\TestDox\TestResultCollector $collector; + public function __construct(\PHPUnit\Logging\TestDox\TestResultCollector $collector) { - $testName = $this->getTestName($test); - $this->cache->setTime($testName, round($time, 3)); - $this->cache->setState($testName, \PHPUnit\Runner\BaseTestRunner::STATUS_RISKY); + $this->collector = $collector; } - public function executeAfterSkippedTest(string $test, string $message, float $time) : void + protected function collector(): \PHPUnit\Logging\TestDox\TestResultCollector { - $testName = $this->getTestName($test); - $this->cache->setTime($testName, round($time, 3)); - $this->cache->setState($testName, \PHPUnit\Runner\BaseTestRunner::STATUS_SKIPPED); + return $this->collector; } - 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, \PHPUnit\Runner\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, \PHPUnit\Runner\BaseTestRunner::STATUS_FAILURE); - } - public function executeAfterTestWarning(string $test, string $message, float $time) : void +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Logging\TestDox; + +use PHPUnit\Event\Test\ConsideredRisky; +use PHPUnit\Event\Test\ConsideredRiskySubscriber; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class TestConsideredRiskySubscriber extends \PHPUnit\Logging\TestDox\Subscriber implements ConsideredRiskySubscriber +{ + public function notify(ConsideredRisky $event): void { - $testName = $this->getTestName($test); - $this->cache->setTime($testName, round($time, 3)); - $this->cache->setState($testName, \PHPUnit\Runner\BaseTestRunner::STATUS_WARNING); + $this->collector()->testConsideredRisky($event); } - public function executeAfterLastTest() : void +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Logging\TestDox; + +use PHPUnit\Event\Test\Errored; +use PHPUnit\Event\Test\ErroredSubscriber; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class TestErroredSubscriber extends \PHPUnit\Logging\TestDox\Subscriber implements ErroredSubscriber +{ + public function notify(Errored $event): void { - $this->flush(); + $this->collector()->testErrored($event); } - /** - * @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 +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Logging\TestDox; + +use PHPUnit\Event\Test\Failed; +use PHPUnit\Event\Test\FailedSubscriber; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class TestFailedSubscriber extends \PHPUnit\Logging\TestDox\Subscriber implements FailedSubscriber +{ + public function notify(Failed $event): void { - $matches = []; - if (preg_match('/^(?\\S+::\\S+)(?:(? with data set (?:#\\d+|"[^"]+"))\\s\\()?/', $test, $matches)) { - $test = $matches['name'] . ($matches['dataname'] ?? ''); - } - return $test; + $this->collector()->testFailed($event); } } getMessage(), $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd - if ($class->isSubclassOf(TestCase::class)) { - if ($class->isAbstract()) { - throw new \PHPUnit\Runner\Exception(sprintf('Class %s declared in %s is abstract', $suiteClassName, $suiteClassFile)); - } - return $class; - } - if ($class->hasMethod('suite')) { - try { - $method = $class->getMethod('suite'); - // @codeCoverageIgnoreStart - } catch (ReflectionException $e) { - throw new \PHPUnit\Runner\Exception(sprintf('Method %s::suite() declared in %s is abstract', $suiteClassName, $suiteClassFile)); - } - if (!$method->isPublic()) { - throw new \PHPUnit\Runner\Exception(sprintf('Method %s::suite() declared in %s is not public', $suiteClassName, $suiteClassFile)); - } - if (!$method->isStatic()) { - throw new \PHPUnit\Runner\Exception(sprintf('Method %s::suite() declared in %s is not static', $suiteClassName, $suiteClassFile)); - } - } - return $class; + $this->collector()->testFinished($event); } - public function reload(ReflectionClass $aClass) : ReflectionClass +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Logging\TestDox; + +use PHPUnit\Event\Test\MarkedIncomplete; +use PHPUnit\Event\Test\MarkedIncompleteSubscriber; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class TestMarkedIncompleteSubscriber extends \PHPUnit\Logging\TestDox\Subscriber implements MarkedIncompleteSubscriber +{ + public function notify(MarkedIncomplete $event): void { - return $aClass; + $this->collector()->testMarkedIncomplete($event); } } collector()->testPassed($event); + } } collector()->testPrepared($event); + } } collector()->testSkipped($event); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Logging\TestDox; + +use PHPUnit\Event\Test\DeprecationTriggered; +use PHPUnit\Event\Test\DeprecationTriggeredSubscriber; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class TestTriggeredDeprecationSubscriber extends \PHPUnit\Logging\TestDox\Subscriber implements DeprecationTriggeredSubscriber +{ + public function notify(DeprecationTriggered $event): void + { + $this->collector()->testTriggeredDeprecation($event); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Logging\TestDox; + +use PHPUnit\Event\Test\NoticeTriggered; +use PHPUnit\Event\Test\NoticeTriggeredSubscriber; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class TestTriggeredNoticeSubscriber extends \PHPUnit\Logging\TestDox\Subscriber implements NoticeTriggeredSubscriber +{ + public function notify(NoticeTriggered $event): void + { + $this->collector()->testTriggeredNotice($event); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Logging\TestDox; + +use PHPUnit\Event\Test\PhpDeprecationTriggered; +use PHPUnit\Event\Test\PhpDeprecationTriggeredSubscriber; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class TestTriggeredPhpDeprecationSubscriber extends \PHPUnit\Logging\TestDox\Subscriber implements PhpDeprecationTriggeredSubscriber +{ + public function notify(PhpDeprecationTriggered $event): void + { + $this->collector()->testTriggeredPhpDeprecation($event); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Logging\TestDox; + +use PHPUnit\Event\Test\PhpNoticeTriggered; +use PHPUnit\Event\Test\PhpNoticeTriggeredSubscriber; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class TestTriggeredPhpNoticeSubscriber extends \PHPUnit\Logging\TestDox\Subscriber implements PhpNoticeTriggeredSubscriber +{ + public function notify(PhpNoticeTriggered $event): void + { + $this->collector()->testTriggeredPhpNotice($event); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Logging\TestDox; + +use PHPUnit\Event\Test\PhpWarningTriggered; +use PHPUnit\Event\Test\PhpWarningTriggeredSubscriber; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class TestTriggeredPhpWarningSubscriber extends \PHPUnit\Logging\TestDox\Subscriber implements PhpWarningTriggeredSubscriber +{ + public function notify(PhpWarningTriggered $event): void + { + $this->collector()->testTriggeredPhpWarning($event); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Logging\TestDox; + +use PHPUnit\Event\Test\PhpunitDeprecationTriggered; +use PHPUnit\Event\Test\PhpunitDeprecationTriggeredSubscriber; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class TestTriggeredPhpunitDeprecationSubscriber extends \PHPUnit\Logging\TestDox\Subscriber implements PhpunitDeprecationTriggeredSubscriber +{ + public function notify(PhpunitDeprecationTriggered $event): void + { + $this->collector()->testTriggeredPhpunitDeprecation($event); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Logging\TestDox; + +use PHPUnit\Event\Test\PhpunitErrorTriggered; +use PHPUnit\Event\Test\PhpunitErrorTriggeredSubscriber; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class TestTriggeredPhpunitErrorSubscriber extends \PHPUnit\Logging\TestDox\Subscriber implements PhpunitErrorTriggeredSubscriber +{ + public function notify(PhpunitErrorTriggered $event): void + { + $this->collector()->testTriggeredPhpunitError($event); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Logging\TestDox; + +use PHPUnit\Event\Test\PhpunitWarningTriggered; +use PHPUnit\Event\Test\PhpunitWarningTriggeredSubscriber; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class TestTriggeredPhpunitWarningSubscriber extends \PHPUnit\Logging\TestDox\Subscriber implements PhpunitWarningTriggeredSubscriber +{ + public function notify(PhpunitWarningTriggered $event): void + { + $this->collector()->testTriggeredPhpunitWarning($event); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Logging\TestDox; + +use PHPUnit\Event\Test\WarningTriggered; +use PHPUnit\Event\Test\WarningTriggeredSubscriber; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class TestTriggeredWarningSubscriber extends \PHPUnit\Logging\TestDox\Subscriber implements WarningTriggeredSubscriber +{ + public function notify(WarningTriggered $event): void + { + $this->collector()->testTriggeredWarning($event); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Logging\TestDox; + +use PHPUnit\Event\Code\TestMethod; +use PHPUnit\Event\Code\Throwable; +use PHPUnit\Framework\TestStatus\TestStatus; +/** + * @psalm-immutable + * + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class TestResult +{ + private readonly TestMethod $test; + private readonly TestStatus $status; + private readonly ?Throwable $throwable; + public function __construct(TestMethod $test, TestStatus $status, ?Throwable $throwable) + { + $this->test = $test; + $this->status = $status; + $this->throwable = $throwable; + } + public function test(): TestMethod + { + return $this->test; + } + public function status(): TestStatus + { + return $this->status; + } /** - * @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 + * @psalm-assert-if-true !null $this->throwable */ - public const ORDER_DURATION = 4; + public function hasThrowable(): bool + { + return $this->throwable !== null; + } + public function throwable(): ?Throwable + { + return $this->throwable; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Logging\TestDox; + +use IteratorAggregate; +/** + * @template-implements IteratorAggregate + * + * @psalm-immutable + * + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class TestResultCollection implements IteratorAggregate +{ /** - * Order tests by @size annotation 'small', 'medium', 'large'. - * - * @var int + * @psalm-var list */ - public const ORDER_SIZE = 5; + private readonly array $testResults; /** - * List of sorting weights for all test result codes. A higher number gives higher priority. + * @psalm-param list $testResults */ - private const DEFECT_SORT_WEIGHT = [\PHPUnit\Runner\BaseTestRunner::STATUS_ERROR => 6, \PHPUnit\Runner\BaseTestRunner::STATUS_FAILURE => 5, \PHPUnit\Runner\BaseTestRunner::STATUS_WARNING => 4, \PHPUnit\Runner\BaseTestRunner::STATUS_INCOMPLETE => 3, \PHPUnit\Runner\BaseTestRunner::STATUS_RISKY => 2, \PHPUnit\Runner\BaseTestRunner::STATUS_SKIPPED => 1, \PHPUnit\Runner\BaseTestRunner::STATUS_UNKNOWN => 0]; - private const SIZE_SORT_WEIGHT = [TestUtil::SMALL => 1, TestUtil::MEDIUM => 2, TestUtil::LARGE => 3, TestUtil::UNKNOWN => 4]; + public static function fromArray(array $testResults): self + { + return new self(...$testResults); + } + private function __construct(\PHPUnit\Logging\TestDox\TestResult ...$testResults) + { + $this->testResults = $testResults; + } /** - * @var array Associative array of (string => DEFECT_SORT_WEIGHT) elements + * @psalm-return list */ - private $defectSortOrder = []; + public function asArray(): array + { + return $this->testResults; + } + public function getIterator(): \PHPUnit\Logging\TestDox\TestResultCollectionIterator + { + return new \PHPUnit\Logging\TestDox\TestResultCollectionIterator($this); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Logging\TestDox; + +use function count; +use Iterator; +/** + * @template-implements Iterator + * + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class TestResultCollectionIterator implements Iterator +{ /** - * @var TestResultCache + * @psalm-var list */ - private $cache; + private readonly array $testResults; + private int $position = 0; + public function __construct(\PHPUnit\Logging\TestDox\TestResultCollection $testResults) + { + $this->testResults = $testResults->asArray(); + } + public function rewind(): void + { + $this->position = 0; + } + public function valid(): bool + { + return $this->position < count($this->testResults); + } + public function key(): int + { + return $this->position; + } + public function current(): \PHPUnit\Logging\TestDox\TestResult + { + return $this->testResults[$this->position]; + } + public function next(): void + { + $this->position++; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Logging\TestDox; + +use function array_keys; +use function array_merge; +use function assert; +use function is_subclass_of; +use function ksort; +use function uksort; +use function usort; +use PHPUnit\Event\Code\TestMethod; +use PHPUnit\Event\Code\Throwable; +use PHPUnit\Event\EventFacadeIsSealedException; +use PHPUnit\Event\Facade; +use PHPUnit\Event\InvalidArgumentException; +use PHPUnit\Event\Test\ConsideredRisky; +use PHPUnit\Event\Test\DeprecationTriggered; +use PHPUnit\Event\Test\Errored; +use PHPUnit\Event\Test\Failed; +use PHPUnit\Event\Test\Finished; +use PHPUnit\Event\Test\MarkedIncomplete; +use PHPUnit\Event\Test\NoticeTriggered; +use PHPUnit\Event\Test\Passed; +use PHPUnit\Event\Test\PhpDeprecationTriggered; +use PHPUnit\Event\Test\PhpNoticeTriggered; +use PHPUnit\Event\Test\PhpunitDeprecationTriggered; +use PHPUnit\Event\Test\PhpunitErrorTriggered; +use PHPUnit\Event\Test\PhpunitWarningTriggered; +use PHPUnit\Event\Test\PhpWarningTriggered; +use PHPUnit\Event\Test\Prepared; +use PHPUnit\Event\Test\Skipped; +use PHPUnit\Event\Test\WarningTriggered; +use PHPUnit\Event\UnknownSubscriberTypeException; +use PHPUnit\Framework\TestStatus\TestStatus; +use PHPUnit\Logging\TestDox\TestResult as TestDoxTestMethod; +use ReflectionMethod; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class TestResultCollector +{ /** - * @var array A list of normalized names of tests before reordering + * @psalm-var array> */ - private $originalExecutionOrder = []; + private array $tests = []; + private ?TestStatus $status = null; + private ?Throwable $throwable = null; + private bool $prepared = \false; /** - * @var array A list of normalized names of tests affected by reordering + * @throws EventFacadeIsSealedException + * @throws UnknownSubscriberTypeException */ - private $executionOrder = []; - public function __construct(?\PHPUnit\Runner\TestResultCache $cache = null) + public function __construct(Facade $facade) { - $this->cache = $cache ?? new \PHPUnit\Runner\NullTestResultCache(); + $this->registerSubscribers($facade); } /** - * @throws Exception - * @throws InvalidArgumentException + * @psalm-return array */ - public function reorderTestsInSuite(Test $suite, int $order, bool $resolveDependencies, int $orderDefects, bool $isRootTestSuite = \true) : void + public function testMethodsGroupedByClass(): array { - $allowedOrders = [self::ORDER_DEFAULT, self::ORDER_REVERSED, self::ORDER_RANDOMIZED, self::ORDER_DURATION, self::ORDER_SIZE]; - if (!in_array($order, $allowedOrders, \true)) { - throw new \PHPUnit\Runner\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 \PHPUnit\Runner\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); + $result = []; + foreach ($this->tests as $prettifiedClassName => $tests) { + $testsByDeclaringClass = []; + foreach ($tests as $test) { + $declaringClassName = (new ReflectionMethod($test->test()->className(), $test->test()->methodName()))->getDeclaringClass()->getName(); + if (!isset($testsByDeclaringClass[$declaringClassName])) { + $testsByDeclaringClass[$declaringClassName] = []; + } + $testsByDeclaringClass[$declaringClassName][] = $test; } - if ($orderDefects === self::ORDER_DEFECTS_FIRST) { - $this->addSuiteToDefectSortOrder($suite); + foreach (array_keys($testsByDeclaringClass) as $declaringClassName) { + usort($testsByDeclaringClass[$declaringClassName], static function (TestDoxTestMethod $a, TestDoxTestMethod $b): int { + return $a->test()->line() <=> $b->test()->line(); + }); } - $this->sort($suite, $order, $resolveDependencies, $orderDefects); + uksort( + $testsByDeclaringClass, + /** + * @psalm-param class-string $a + * @psalm-param class-string $b + */ + static function (string $a, string $b): int { + if (is_subclass_of($b, $a)) { + return -1; + } + if (is_subclass_of($a, $b)) { + return 1; + } + return 0; + } + ); + $tests = []; + foreach ($testsByDeclaringClass as $_tests) { + $tests = array_merge($tests, $_tests); + } + $result[$prettifiedClassName] = \PHPUnit\Logging\TestDox\TestResultCollection::fromArray($tests); } - if ($isRootTestSuite) { - $this->executionOrder = $this->calculateTestExecutionOrder($suite); + ksort($result); + return $result; + } + public function testPrepared(Prepared $event): void + { + if (!$event->test()->isTestMethod()) { + return; } + $this->status = TestStatus::unknown(); + $this->throwable = null; + $this->prepared = \true; } - public function getOriginalExecutionOrder() : array + public function testErrored(Errored $event): void { - return $this->originalExecutionOrder; + if (!$event->test()->isTestMethod()) { + return; + } + $this->status = TestStatus::error($event->throwable()->message()); + $this->throwable = $event->throwable(); + if (!$this->prepared) { + $test = $event->test(); + assert($test instanceof TestMethod); + $this->process($test); + } } - public function getExecutionOrder() : array + public function testFailed(Failed $event): void { - return $this->executionOrder; + if (!$event->test()->isTestMethod()) { + return; + } + $this->status = TestStatus::failure($event->throwable()->message()); + $this->throwable = $event->throwable(); } - private function sort(TestSuite $suite, int $order, bool $resolveDependencies, int $orderDefects) : void + public function testPassed(Passed $event): void { - if (empty($suite->tests())) { + if (!$event->test()->isTestMethod()) { 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())); + $this->updateTestStatus(TestStatus::success()); + } + public function testSkipped(Skipped $event): void + { + if (!$event->test()->isTestMethod()) { + return; } - if ($orderDefects === self::ORDER_DEFECTS_FIRST && $this->cache !== null) { - $suite->setTests($this->sortDefectsFirst($suite->tests())); + $this->updateTestStatus(TestStatus::skipped($event->message())); + } + public function testMarkedIncomplete(MarkedIncomplete $event): void + { + if (!$event->test()->isTestMethod()) { + return; } - if ($resolveDependencies && !$suite instanceof DataProviderTestSuite) { - /** @var TestCase[] $tests */ - $tests = $suite->tests(); - $suite->setTests($this->resolveDependencies($tests)); + $this->updateTestStatus(TestStatus::incomplete($event->throwable()->message())); + $this->throwable = $event->throwable(); + } + public function testConsideredRisky(ConsideredRisky $event): void + { + if (!$event->test()->isTestMethod()) { + return; } + $this->updateTestStatus(TestStatus::risky()); } - /** - * @throws InvalidArgumentException - */ - private function addSuiteToDefectSortOrder(TestSuite $suite) : void + public function testTriggeredDeprecation(DeprecationTriggered $event): void { - $max = 0; - foreach ($suite->tests() as $test) { - if (!$test instanceof Reorderable) { - continue; - } - if (!isset($this->defectSortOrder[$test->sortId()])) { - $this->defectSortOrder[$test->sortId()] = self::DEFECT_SORT_WEIGHT[$this->cache->getState($test->sortId())]; - $max = max($max, $this->defectSortOrder[$test->sortId()]); - } + if (!$event->test()->isTestMethod()) { + return; } - $this->defectSortOrder[$suite->sortId()] = $max; + $this->updateTestStatus(TestStatus::deprecation()); } - private function reverse(array $tests) : array + public function testTriggeredNotice(NoticeTriggered $event): void { - return array_reverse($tests); + if (!$event->test()->isTestMethod()) { + return; + } + $this->updateTestStatus(TestStatus::notice()); } - private function randomize(array $tests) : array + public function testTriggeredWarning(WarningTriggered $event): void { - shuffle($tests); - return $tests; + if (!$event->test()->isTestMethod()) { + return; + } + $this->updateTestStatus(TestStatus::warning()); } - private function sortDefectsFirst(array $tests) : array + public function testTriggeredPhpDeprecation(PhpDeprecationTriggered $event): void { - usort( - $tests, - /** - * @throws InvalidArgumentException - */ - function ($left, $right) { - return $this->cmpDefectPriorityAndTime($left, $right); - } - ); - return $tests; + if (!$event->test()->isTestMethod()) { + return; + } + $this->updateTestStatus(TestStatus::deprecation()); } - private function sortByDuration(array $tests) : array + public function testTriggeredPhpNotice(PhpNoticeTriggered $event): void { - usort( - $tests, - /** - * @throws InvalidArgumentException - */ - function ($left, $right) { - return $this->cmpDuration($left, $right); - } - ); - return $tests; + if (!$event->test()->isTestMethod()) { + return; + } + $this->updateTestStatus(TestStatus::notice()); } - private function sortBySize(array $tests) : array + public function testTriggeredPhpWarning(PhpWarningTriggered $event): void { - usort( - $tests, - /** - * @throws InvalidArgumentException - */ - function ($left, $right) { - return $this->cmpSize($left, $right); - } - ); - return $tests; + if (!$event->test()->isTestMethod()) { + return; + } + $this->updateTestStatus(TestStatus::warning()); } - /** - * 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 InvalidArgumentException - */ - private function cmpDefectPriorityAndTime(Test $a, Test $b) : int + public function testTriggeredPhpunitDeprecation(PhpunitDeprecationTriggered $event): void { - if (!($a instanceof Reorderable && $b instanceof Reorderable)) { - return 0; + if (!$event->test()->isTestMethod()) { + return; } - $priorityA = $this->defectSortOrder[$a->sortId()] ?? 0; - $priorityB = $this->defectSortOrder[$b->sortId()] ?? 0; - if ($priorityB <=> $priorityA) { - // Sort defect weight descending - return $priorityB <=> $priorityA; + $this->updateTestStatus(TestStatus::deprecation()); + } + public function testTriggeredPhpunitError(PhpunitErrorTriggered $event): void + { + if (!$event->test()->isTestMethod()) { + return; } - if ($priorityA || $priorityB) { - return $this->cmpDuration($a, $b); + $this->updateTestStatus(TestStatus::error()); + } + public function testTriggeredPhpunitWarning(PhpunitWarningTriggered $event): void + { + if (!$event->test()->isTestMethod()) { + return; } - // do not change execution order - return 0; + $this->updateTestStatus(TestStatus::warning()); } /** - * Compares test duration for sorting tests by duration ascending. - * * @throws InvalidArgumentException */ - private function cmpDuration(Test $a, Test $b) : int + public function testFinished(Finished $event): void { - if (!($a instanceof Reorderable && $b instanceof Reorderable)) { - return 0; + if (!$event->test()->isTestMethod()) { + return; } - return $this->cache->getTime($a->sortId()) <=> $this->cache->getTime($b->sortId()); + $test = $event->test(); + assert($test instanceof TestMethod); + $this->process($test); + $this->status = null; + $this->throwable = null; + $this->prepared = \false; } /** - * Compares test size for sorting tests small->medium->large->unknown. + * @throws EventFacadeIsSealedException + * @throws UnknownSubscriberTypeException */ - private function cmpSize(Test $a, Test $b) : int + private function registerSubscribers(Facade $facade): void { - $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]; + $facade->registerSubscribers(new \PHPUnit\Logging\TestDox\TestConsideredRiskySubscriber($this), new \PHPUnit\Logging\TestDox\TestErroredSubscriber($this), new \PHPUnit\Logging\TestDox\TestFailedSubscriber($this), new \PHPUnit\Logging\TestDox\TestFinishedSubscriber($this), new \PHPUnit\Logging\TestDox\TestMarkedIncompleteSubscriber($this), new \PHPUnit\Logging\TestDox\TestPassedSubscriber($this), new \PHPUnit\Logging\TestDox\TestPreparedSubscriber($this), new \PHPUnit\Logging\TestDox\TestSkippedSubscriber($this), new \PHPUnit\Logging\TestDox\TestTriggeredDeprecationSubscriber($this), new \PHPUnit\Logging\TestDox\TestTriggeredNoticeSubscriber($this), new \PHPUnit\Logging\TestDox\TestTriggeredPhpDeprecationSubscriber($this), new \PHPUnit\Logging\TestDox\TestTriggeredPhpNoticeSubscriber($this), new \PHPUnit\Logging\TestDox\TestTriggeredPhpunitDeprecationSubscriber($this), new \PHPUnit\Logging\TestDox\TestTriggeredPhpunitErrorSubscriber($this), new \PHPUnit\Logging\TestDox\TestTriggeredPhpunitWarningSubscriber($this), new \PHPUnit\Logging\TestDox\TestTriggeredPhpWarningSubscriber($this), new \PHPUnit\Logging\TestDox\TestTriggeredWarningSubscriber($this)); } - /** - * 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 $tests - * - * @return array - */ - private function resolveDependencies(array $tests) : array + private function updateTestStatus(TestStatus $status): void { - $newTestOrder = []; - $i = 0; - $provided = []; - do { - if ([] === array_diff($tests[$i]->requires(), $provided)) { - $provided = array_merge($provided, $tests[$i]->provides()); - $newTestOrder = array_merge($newTestOrder, array_splice($tests, $i, 1)); - $i = 0; - } else { - $i++; - } - } while (!empty($tests) && $i < count($tests)); - return array_merge($newTestOrder, $tests); + if ($this->status !== null && $this->status->isMoreImportantThan($status)) { + return; + } + $this->status = $status; } - /** - * @throws InvalidArgumentException - */ - private function calculateTestExecutionOrder(Test $suite) : array + private function process(TestMethod $test): void { - $tests = []; - if ($suite instanceof TestSuite) { - foreach ($suite->tests() as $test) { - if (!$test instanceof TestSuite && $test instanceof Reorderable) { - $tests[] = $test->sortId(); - } else { - $tests = array_merge($tests, $this->calculateTestExecutionOrder($test)); - } - } + if (!isset($this->tests[$test->testDox()->prettifiedClassName()])) { + $this->tests[$test->testDox()->prettifiedClassName()] = []; } - return $tests; + $this->tests[$test->testDox()->prettifiedClassName()][] = new TestDoxTestMethod($test, $this->status, $this->throwable); } } getVersion(); - assert(!empty(self::$version)); - } - return self::$version; - } - /** - * @psalm-return non-empty-string - */ - 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)); - } - /** - * @psalm-return non-empty-string - */ - public static function getVersionString() : string - { - return 'PHPUnit ' . self::id() . ' by Sebastian Bergmann and contributors.'; + return \true; } } parse($parameters, self::SHORT_OPTIONS, array_merge(self::LONG_OPTIONS, $additionalLongOptions)); - } catch (CliParserException $e) { - throw new \PHPUnit\TextUI\CliArguments\Exception($e->getMessage(), $e->getCode(), $e); - } - $argument = null; - $atLeastVersion = null; - $backupGlobals = null; - $backupStaticAttributes = null; - $beStrictAboutChangesToGlobalState = null; - $beStrictAboutResourceUsageDuringSmallTests = null; - $bootstrap = null; - $cacheResult = null; - $cacheResultFile = null; - $checkVersion = null; - $colors = null; - $columns = null; - $configuration = null; - $coverageCacheDirectory = null; - $warmCoverageCache = null; - $coverageFilter = null; - $coverageClover = null; - $coverageCobertura = null; - $coverageCrap4J = null; - $coverageHtml = null; - $coveragePhp = null; - $coverageText = null; - $coverageTextShowUncoveredFiles = null; - $coverageTextShowOnlySummary = null; - $coverageXml = null; - $pathCoverage = null; - $debug = null; - $defaultTimeLimit = null; - $disableCodeCoverageIgnore = null; - $disallowTestOutput = null; - $disallowTodoAnnotatedTests = null; - $enforceTimeLimit = null; - $excludeGroups = null; - $executionOrder = null; - $executionOrderDefects = null; - $extensions = []; - $unavailableExtensions = []; - $failOnEmptyTestSuite = null; - $failOnIncomplete = null; - $failOnRisky = null; - $failOnSkipped = null; - $failOnWarning = null; - $filter = null; - $generateConfiguration = null; - $migrateConfiguration = null; - $groups = null; - $testsCovering = null; - $testsUsing = null; - $help = null; - $includePath = null; - $iniSettings = []; - $junitLogfile = null; - $listGroups = null; - $listSuites = null; - $listTests = null; - $listTestsXml = null; - $loader = null; - $noCoverage = null; - $noExtensions = null; - $noInteraction = null; - $noLogging = null; - $printer = null; - $processIsolation = null; - $randomOrderSeed = null; - $repeat = null; - $reportUselessTests = null; - $resolveDependencies = null; - $reverseList = null; - $stderr = null; - $strictCoverage = null; - $stopOnDefect = null; - $stopOnError = null; - $stopOnFailure = null; - $stopOnIncomplete = null; - $stopOnRisky = null; - $stopOnSkipped = null; - $stopOnWarning = null; - $teamcityLogfile = null; - $testdoxExcludeGroups = null; - $testdoxGroups = null; - $testdoxHtmlFile = null; - $testdoxTextFile = null; - $testdoxXmlFile = null; - $testSuffixes = null; - $testSuite = null; - $unrecognizedOptions = []; - $unrecognizedOrderBy = null; - $useDefaultConfiguration = null; - $verbose = null; - $version = null; - $xdebugFilterFile = null; - if (isset($options[1][0])) { - $argument = $options[1][0]; - } - foreach ($options[0] as $option) { - switch ($option[0]) { - case '--colors': - $colors = $option[1] ?: DefaultResultPrinter::COLOR_AUTO; - break; - case '--bootstrap': - $bootstrap = $option[1]; - break; - case '--cache-result': - $cacheResult = \true; - break; - case '--do-not-cache-result': - $cacheResult = \false; - break; - case '--cache-result-file': - $cacheResultFile = $option[1]; - break; - case '--columns': - if (is_numeric($option[1])) { - $columns = (int) $option[1]; - } elseif ($option[1] === 'max') { - $columns = 'max'; - } - break; - case 'c': - case '--configuration': - $configuration = $option[1]; - break; - case '--coverage-cache': - $coverageCacheDirectory = $option[1]; - break; - case '--warm-coverage-cache': - $warmCoverageCache = \true; - break; - case '--coverage-clover': - $coverageClover = $option[1]; - break; - case '--coverage-cobertura': - $coverageCobertura = $option[1]; - break; - case '--coverage-crap4j': - $coverageCrap4J = $option[1]; - break; - case '--coverage-html': - $coverageHtml = $option[1]; - break; - case '--coverage-php': - $coveragePhp = $option[1]; - break; - case '--coverage-text': - if ($option[1] === null) { - $option[1] = 'php://stdout'; - } - $coverageText = $option[1]; - $coverageTextShowUncoveredFiles = \false; - $coverageTextShowOnlySummary = \false; - break; - case '--coverage-xml': - $coverageXml = $option[1]; - break; - case '--path-coverage': - $pathCoverage = \true; - break; - case 'd': - $tmp = explode('=', $option[1]); - if (isset($tmp[0])) { - if (isset($tmp[1])) { - $iniSettings[$tmp[0]] = $tmp[1]; - } else { - $iniSettings[$tmp[0]] = '1'; - } - } - break; - case '--debug': - $debug = \true; - break; - case 'h': - case '--help': - $help = \true; - break; - case '--filter': - $filter = $option[1]; - break; - case '--testsuite': - $testSuite = $option[1]; - break; - case '--generate-configuration': - $generateConfiguration = \true; - break; - case '--migrate-configuration': - $migrateConfiguration = \true; - break; - case '--group': - $groups = explode(',', $option[1]); - break; - case '--exclude-group': - $excludeGroups = explode(',', $option[1]); - break; - case '--covers': - $testsCovering = array_map('strtolower', explode(',', $option[1])); - break; - case '--uses': - $testsUsing = array_map('strtolower', explode(',', $option[1])); - break; - case '--test-suffix': - $testSuffixes = explode(',', $option[1]); - break; - case '--include-path': - $includePath = $option[1]; - break; - case '--list-groups': - $listGroups = \true; - break; - case '--list-suites': - $listSuites = \true; - break; - case '--list-tests': - $listTests = \true; - break; - case '--list-tests-xml': - $listTestsXml = $option[1]; - break; - case '--printer': - $printer = $option[1]; - break; - case '--loader': - $loader = $option[1]; - break; - case '--log-junit': - $junitLogfile = $option[1]; - break; - case '--log-teamcity': - $teamcityLogfile = $option[1]; - break; - case '--order-by': - foreach (explode(',', $option[1]) as $order) { - switch ($order) { - case 'default': - $executionOrder = TestSuiteSorter::ORDER_DEFAULT; - $executionOrderDefects = TestSuiteSorter::ORDER_DEFAULT; - $resolveDependencies = \true; - break; - case 'defects': - $executionOrderDefects = TestSuiteSorter::ORDER_DEFECTS_FIRST; - break; - case 'depends': - $resolveDependencies = \true; - break; - case 'duration': - $executionOrder = TestSuiteSorter::ORDER_DURATION; - break; - case 'no-depends': - $resolveDependencies = \false; - break; - case 'random': - $executionOrder = TestSuiteSorter::ORDER_RANDOMIZED; - break; - case 'reverse': - $executionOrder = TestSuiteSorter::ORDER_REVERSED; - break; - case 'size': - $executionOrder = TestSuiteSorter::ORDER_SIZE; - break; - default: - $unrecognizedOrderBy = $order; - } - } - break; - case '--process-isolation': - $processIsolation = \true; - break; - case '--repeat': - $repeat = (int) $option[1]; - break; - case '--stderr': - $stderr = \true; - break; - case '--stop-on-defect': - $stopOnDefect = \true; - break; - case '--stop-on-error': - $stopOnError = \true; - break; - case '--stop-on-failure': - $stopOnFailure = \true; - break; - case '--stop-on-warning': - $stopOnWarning = \true; - break; - case '--stop-on-incomplete': - $stopOnIncomplete = \true; - break; - case '--stop-on-risky': - $stopOnRisky = \true; - break; - case '--stop-on-skipped': - $stopOnSkipped = \true; - break; - case '--fail-on-empty-test-suite': - $failOnEmptyTestSuite = \true; - break; - case '--fail-on-incomplete': - $failOnIncomplete = \true; - break; - case '--fail-on-risky': - $failOnRisky = \true; - break; - case '--fail-on-skipped': - $failOnSkipped = \true; - break; - case '--fail-on-warning': - $failOnWarning = \true; - break; - case '--teamcity': - $printer = TeamCity::class; - break; - case '--testdox': - $printer = CliTestDoxPrinter::class; - break; - case '--testdox-group': - $testdoxGroups = explode(',', $option[1]); - break; - case '--testdox-exclude-group': - $testdoxExcludeGroups = explode(',', $option[1]); - break; - case '--testdox-html': - $testdoxHtmlFile = $option[1]; - break; - case '--testdox-text': - $testdoxTextFile = $option[1]; - break; - case '--testdox-xml': - $testdoxXmlFile = $option[1]; - break; - case '--no-configuration': - $useDefaultConfiguration = \false; - break; - case '--extensions': - foreach (explode(',', $option[1]) as $extensionClass) { - if (!class_exists($extensionClass)) { - $unavailableExtensions[] = $extensionClass; - continue; - } - $extensions[] = new Extension($extensionClass, '', []); - } - break; - case '--no-extensions': - $noExtensions = \true; - break; - case '--no-coverage': - $noCoverage = \true; - break; - case '--no-logging': - $noLogging = \true; - break; - case '--no-interaction': - $noInteraction = \true; - break; - case '--globals-backup': - $backupGlobals = \true; - break; - case '--static-backup': - $backupStaticAttributes = \true; - break; - case 'v': - case '--verbose': - $verbose = \true; - break; - case '--atleast-version': - $atLeastVersion = $option[1]; - break; - case '--version': - $version = \true; - break; - case '--dont-report-useless-tests': - $reportUselessTests = \false; - break; - case '--strict-coverage': - $strictCoverage = \true; - break; - case '--disable-coverage-ignore': - $disableCodeCoverageIgnore = \true; - break; - case '--strict-global-state': - $beStrictAboutChangesToGlobalState = \true; - break; - case '--disallow-test-output': - $disallowTestOutput = \true; - break; - case '--disallow-resource-usage': - $beStrictAboutResourceUsageDuringSmallTests = \true; - break; - case '--default-time-limit': - $defaultTimeLimit = (int) $option[1]; - break; - case '--enforce-time-limit': - $enforceTimeLimit = \true; - break; - case '--disallow-todo-tests': - $disallowTodoAnnotatedTests = \true; - break; - case '--reverse-list': - $reverseList = \true; - break; - case '--check-version': - $checkVersion = \true; - break; - case '--coverage-filter': - case '--whitelist': - if ($coverageFilter === null) { - $coverageFilter = []; - } - $coverageFilter[] = $option[1]; - break; - case '--random-order': - $executionOrder = TestSuiteSorter::ORDER_RANDOMIZED; - break; - case '--random-order-seed': - $randomOrderSeed = (int) $option[1]; - break; - case '--resolve-dependencies': - $resolveDependencies = \true; - break; - case '--ignore-dependencies': - $resolveDependencies = \false; - break; - case '--reverse-order': - $executionOrder = TestSuiteSorter::ORDER_REVERSED; - break; - case '--dump-xdebug-filter': - $xdebugFilterFile = $option[1]; - break; - default: - $unrecognizedOptions[str_replace('--', '', $option[0])] = $option[1]; - } - } - if (empty($extensions)) { - $extensions = null; - } - if (empty($unavailableExtensions)) { - $unavailableExtensions = null; - } - if (empty($iniSettings)) { - $iniSettings = null; - } - if (empty($coverageFilter)) { - $coverageFilter = null; - } - return new \PHPUnit\TextUI\CliArguments\Configuration($argument, $atLeastVersion, $backupGlobals, $backupStaticAttributes, $beStrictAboutChangesToGlobalState, $beStrictAboutResourceUsageDuringSmallTests, $bootstrap, $cacheResult, $cacheResultFile, $checkVersion, $colors, $columns, $configuration, $coverageClover, $coverageCobertura, $coverageCrap4J, $coverageHtml, $coveragePhp, $coverageText, $coverageTextShowUncoveredFiles, $coverageTextShowOnlySummary, $coverageXml, $pathCoverage, $coverageCacheDirectory, $warmCoverageCache, $debug, $defaultTimeLimit, $disableCodeCoverageIgnore, $disallowTestOutput, $disallowTodoAnnotatedTests, $enforceTimeLimit, $excludeGroups, $executionOrder, $executionOrderDefects, $extensions, $unavailableExtensions, $failOnEmptyTestSuite, $failOnIncomplete, $failOnRisky, $failOnSkipped, $failOnWarning, $filter, $generateConfiguration, $migrateConfiguration, $groups, $testsCovering, $testsUsing, $help, $includePath, $iniSettings, $junitLogfile, $listGroups, $listSuites, $listTests, $listTestsXml, $loader, $noCoverage, $noExtensions, $noInteraction, $noLogging, $printer, $processIsolation, $randomOrderSeed, $repeat, $reportUselessTests, $resolveDependencies, $reverseList, $stderr, $strictCoverage, $stopOnDefect, $stopOnError, $stopOnFailure, $stopOnIncomplete, $stopOnRisky, $stopOnSkipped, $stopOnWarning, $teamcityLogfile, $testdoxExcludeGroups, $testdoxGroups, $testdoxHtmlFile, $testdoxTextFile, $testdoxXmlFile, $testSuffixes, $testSuite, $unrecognizedOptions, $unrecognizedOrderBy, $useDefaultConfiguration, $verbose, $version, $coverageFilter, $xdebugFilterFile); + return \true; } } >|false + * + * @throws CodeCoverageException */ - private $columns; + public function linesToBeCovered(string $className, string $methodName): array|false + { + if (!$this->shouldCodeCoverageBeCollectedFor($className, $methodName)) { + return \false; + } + $metadataForClass = Registry::parser()->forClass($className); + $classShortcut = null; + if ($metadataForClass->isCoversDefaultClass()->isNotEmpty()) { + if (count($metadataForClass->isCoversDefaultClass()) > 1) { + throw new CodeCoverageException(sprintf('More than one @coversDefaultClass annotation for class or interface "%s"', $className)); + } + $metadata = $metadataForClass->isCoversDefaultClass()->asArray()[0]; + assert($metadata instanceof CoversDefaultClass); + $classShortcut = $metadata->className(); + } + $codeUnits = CodeUnitCollection::fromList(); + $mapper = new Mapper(); + foreach (Registry::parser()->forClassAndMethod($className, $methodName) as $metadata) { + if (!$metadata->isCoversClass() && !$metadata->isCoversFunction() && !$metadata->isCovers()) { + continue; + } + assert($metadata instanceof CoversClass || $metadata instanceof CoversFunction || $metadata instanceof Covers); + if ($metadata->isCoversClass() || $metadata->isCoversFunction()) { + $codeUnits = $codeUnits->mergeWith($this->mapToCodeUnits($metadata)); + } elseif ($metadata->isCovers()) { + assert($metadata instanceof Covers); + $target = $metadata->target(); + if (interface_exists($target)) { + throw new InvalidCoversTargetException(sprintf('Trying to @cover interface "%s".', $target)); + } + if ($classShortcut !== null && str_starts_with($target, '::')) { + $target = $classShortcut . $target; + } + try { + $codeUnits = $codeUnits->mergeWith($mapper->stringToCodeUnits($target)); + } catch (InvalidCodeUnitException $e) { + throw new InvalidCoversTargetException(sprintf('"@covers %s" is invalid', $target), $e->getCode(), $e); + } + } + } + return $mapper->codeUnitsToSourceLines($codeUnits); + } /** - * @var ?string + * @psalm-param class-string $className + * @psalm-param non-empty-string $methodName + * + * @psalm-return array> + * + * @throws CodeCoverageException */ - private $configuration; + public function linesToBeUsed(string $className, string $methodName): array + { + $metadataForClass = Registry::parser()->forClass($className); + $classShortcut = null; + if ($metadataForClass->isUsesDefaultClass()->isNotEmpty()) { + if (count($metadataForClass->isUsesDefaultClass()) > 1) { + throw new CodeCoverageException(sprintf('More than one @usesDefaultClass annotation for class or interface "%s"', $className)); + } + $metadata = $metadataForClass->isUsesDefaultClass()->asArray()[0]; + assert($metadata instanceof UsesDefaultClass); + $classShortcut = $metadata->className(); + } + $codeUnits = CodeUnitCollection::fromList(); + $mapper = new Mapper(); + foreach (Registry::parser()->forClassAndMethod($className, $methodName) as $metadata) { + if (!$metadata->isUsesClass() && !$metadata->isUsesFunction() && !$metadata->isUses()) { + continue; + } + assert($metadata instanceof UsesClass || $metadata instanceof UsesFunction || $metadata instanceof Uses); + if ($metadata->isUsesClass() || $metadata->isUsesFunction()) { + $codeUnits = $codeUnits->mergeWith($this->mapToCodeUnits($metadata)); + } elseif ($metadata->isUses()) { + assert($metadata instanceof Uses); + $target = $metadata->target(); + if ($classShortcut !== null && str_starts_with($target, '::')) { + $target = $classShortcut . $target; + } + try { + $codeUnits = $codeUnits->mergeWith($mapper->stringToCodeUnits($target)); + } catch (InvalidCodeUnitException $e) { + throw new InvalidCoversTargetException(sprintf('"@uses %s" is invalid', $target), $e->getCode(), $e); + } + } + } + return $mapper->codeUnitsToSourceLines($codeUnits); + } /** - * @var null|string[] + * @psalm-return array> */ - private $coverageFilter; + public function linesToBeIgnored(TestSuite $testSuite): array + { + $codeUnits = CodeUnitCollection::fromList(); + $mapper = new Mapper(); + foreach ($this->testCaseClassesIn($testSuite) as $testCaseClassName) { + $codeUnits = $codeUnits->mergeWith($this->codeUnitsIgnoredBy($testCaseClassName)); + } + return $mapper->codeUnitsToSourceLines($codeUnits); + } /** - * @var ?string + * @psalm-param class-string $className + * @psalm-param non-empty-string $methodName */ - private $coverageClover; + public function shouldCodeCoverageBeCollectedFor(string $className, string $methodName): bool + { + $metadataForClass = Registry::parser()->forClass($className); + $metadataForMethod = Registry::parser()->forMethod($className, $methodName); + if ($metadataForMethod->isCoversNothing()->isNotEmpty()) { + return \false; + } + if ($metadataForMethod->isCovers()->isNotEmpty() || $metadataForMethod->isCoversClass()->isNotEmpty() || $metadataForMethod->isCoversFunction()->isNotEmpty()) { + return \true; + } + if ($metadataForClass->isCoversNothing()->isNotEmpty()) { + return \false; + } + return \true; + } /** - * @var ?string + * @psalm-return list */ - private $coverageCobertura; + private function testCaseClassesIn(TestSuite $testSuite): array + { + $classNames = []; + foreach (new RecursiveIteratorIterator($testSuite) as $test) { + $classNames[] = $test::class; + } + return array_values(array_unique($classNames)); + } /** - * @var ?string + * @psalm-param class-string $className */ - private $coverageCrap4J; + private function codeUnitsIgnoredBy(string $className): CodeUnitCollection + { + $codeUnits = CodeUnitCollection::fromList(); + $mapper = new Mapper(); + foreach (Registry::parser()->forClass($className) as $metadata) { + if ($metadata instanceof IgnoreClassForCodeCoverage) { + $codeUnits = $codeUnits->mergeWith($mapper->stringToCodeUnits($metadata->className())); + } + if ($metadata instanceof IgnoreMethodForCodeCoverage) { + $codeUnits = $codeUnits->mergeWith($mapper->stringToCodeUnits($metadata->className() . '::' . $metadata->methodName())); + } + if ($metadata instanceof IgnoreFunctionForCodeCoverage) { + $codeUnits = $codeUnits->mergeWith($mapper->stringToCodeUnits('::' . $metadata->functionName())); + } + } + return $codeUnits; + } /** - * @var ?string + * @throws InvalidCoversTargetException */ - private $coverageHtml; - /** - * @var ?string - */ - private $coveragePhp; + private function mapToCodeUnits(CoversClass|CoversFunction|UsesClass|UsesFunction $metadata): CodeUnitCollection + { + $mapper = new Mapper(); + try { + return $mapper->stringToCodeUnits($metadata->asStringForCodeUnitMapper()); + } catch (CodeUnitException $e) { + if ($metadata->isCoversClass() || $metadata->isUsesClass()) { + if (interface_exists($metadata->className())) { + $type = 'Interface'; + } else { + $type = 'Class'; + } + } else { + $type = 'Function'; + } + throw new InvalidCoversTargetException(sprintf('%s "%s" is not a valid target for code coverage', $type, $metadata->asStringForCodeUnitMapper()), $e->getCode(), $e); + } + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Metadata\Api; + +use function array_key_exists; +use function assert; +use function explode; +use function is_array; +use function is_int; +use function json_decode; +use function json_last_error; +use function json_last_error_msg; +use function preg_match; +use function preg_replace; +use function rtrim; +use function sprintf; +use function str_replace; +use function strlen; +use function substr; +use function trim; +use PHPUnit\Event; +use PHPUnit\Event\Code\TestMethod; +use PHPUnit\Event\TestData\MoreThanOneDataSetFromDataProviderException; +use PHPUnit\Event\TestData\TestDataCollection; +use PHPUnit\Framework\InvalidDataProviderException; +use PHPUnit\Metadata\DataProvider as DataProviderMetadata; +use PHPUnit\Metadata\MetadataCollection; +use PHPUnit\Metadata\Parser\Registry as MetadataRegistry; +use PHPUnit\Metadata\TestWith; +use PHPUnit\Util\Reflection; +use ReflectionClass; +use ReflectionMethod; +use Throwable; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class DataProvider +{ /** - * @var ?string + * @psalm-param class-string $className + * @psalm-param non-empty-string $methodName + * + * @throws InvalidDataProviderException */ - private $coverageText; + public function providedData(string $className, string $methodName): ?array + { + $dataProvider = MetadataRegistry::parser()->forMethod($className, $methodName)->isDataProvider(); + $testWith = MetadataRegistry::parser()->forMethod($className, $methodName)->isTestWith(); + if ($dataProvider->isEmpty() && $testWith->isEmpty()) { + return $this->dataProvidedByTestWithAnnotation($className, $methodName); + } + if ($dataProvider->isNotEmpty()) { + $data = $this->dataProvidedByMethods($className, $methodName, $dataProvider); + } else { + $data = $this->dataProvidedByMetadata($testWith); + } + if ($data === []) { + throw new InvalidDataProviderException('Empty data set provided by data provider'); + } + foreach ($data as $key => $value) { + if (!is_array($value)) { + throw new InvalidDataProviderException(sprintf('Data set %s is invalid', is_int($key) ? '#' . $key : ('"' . $key . '"'))); + } + } + return $data; + } /** - * @var ?bool + * @psalm-param class-string $className + * @psalm-param non-empty-string $methodName + * + * @throws InvalidDataProviderException */ - private $coverageTextShowUncoveredFiles; + private function dataProvidedByMethods(string $className, string $methodName, MetadataCollection $dataProvider): array + { + $testMethod = new Event\Code\ClassMethod($className, $methodName); + $methodsCalled = []; + $result = []; + foreach ($dataProvider as $_dataProvider) { + assert($_dataProvider instanceof DataProviderMetadata); + $dataProviderMethod = new Event\Code\ClassMethod($_dataProvider->className(), $_dataProvider->methodName()); + Event\Facade::emitter()->dataProviderMethodCalled($testMethod, $dataProviderMethod); + $methodsCalled[] = $dataProviderMethod; + try { + $class = new ReflectionClass($_dataProvider->className()); + $method = $class->getMethod($_dataProvider->methodName()); + $object = null; + if (!$method->isPublic()) { + Event\Facade::emitter()->testTriggeredPhpunitDeprecation($this->valueObjectForTestMethodWithoutTestData($className, $methodName), sprintf('Data Provider method %s::%s() is not public', $_dataProvider->className(), $_dataProvider->methodName())); + } + if (!$method->isStatic()) { + Event\Facade::emitter()->testTriggeredPhpunitDeprecation($this->valueObjectForTestMethodWithoutTestData($className, $methodName), sprintf('Data Provider method %s::%s() is not static', $_dataProvider->className(), $_dataProvider->methodName())); + $object = $class->newInstanceWithoutConstructor(); + } + if ($method->getNumberOfParameters() === 0) { + $data = $method->invoke($object); + } else { + Event\Facade::emitter()->testTriggeredPhpunitDeprecation($this->valueObjectForTestMethodWithoutTestData($className, $methodName), sprintf('Data Provider method %s::%s() expects an argument', $_dataProvider->className(), $_dataProvider->methodName())); + $data = $method->invoke($object, $_dataProvider->methodName()); + } + } catch (Throwable $e) { + Event\Facade::emitter()->dataProviderMethodFinished($testMethod, ...$methodsCalled); + throw new InvalidDataProviderException($e->getMessage(), $e->getCode(), $e); + } + foreach ($data as $key => $value) { + if (is_int($key)) { + $result[] = $value; + } elseif (array_key_exists($key, $result)) { + Event\Facade::emitter()->dataProviderMethodFinished($testMethod, ...$methodsCalled); + throw new InvalidDataProviderException(sprintf('The key "%s" has already been defined by a previous data provider', $key)); + } else { + $result[$key] = $value; + } + } + } + Event\Facade::emitter()->dataProviderMethodFinished($testMethod, ...$methodsCalled); + return $result; + } + private function dataProvidedByMetadata(MetadataCollection $testWith): array + { + $result = []; + foreach ($testWith as $_testWith) { + assert($_testWith instanceof TestWith); + $result[] = $_testWith->data(); + } + return $result; + } /** - * @var ?bool + * @psalm-param class-string $className + * + * @throws InvalidDataProviderException */ - private $coverageTextShowOnlySummary; + private function dataProvidedByTestWithAnnotation(string $className, string $methodName): ?array + { + $docComment = (new ReflectionMethod($className, $methodName))->getDocComment(); + if ($docComment === \false) { + return null; + } + $docComment = str_replace("\r\n", "\n", $docComment); + $docComment = preg_replace('/\n\s*\*\s?/', "\n", $docComment); + $docComment = substr($docComment, 0, -1); + $docComment = rtrim($docComment, "\n"); + if (!preg_match('/@testWith\s+/', $docComment, $matches, \PREG_OFFSET_CAPTURE)) { + return null; + } + $offset = strlen($matches[0][0]) + (int) $matches[0][1]; + $annotationContent = substr($docComment, $offset); + $data = []; + foreach (explode("\n", $annotationContent) as $candidateRow) { + $candidateRow = trim($candidateRow); + if ($candidateRow === '' || $candidateRow[0] !== '[') { + break; + } + $dataSet = json_decode($candidateRow, \true); + if (json_last_error() !== \JSON_ERROR_NONE) { + throw new InvalidDataProviderException('The data set for the @testWith annotation cannot be parsed: ' . json_last_error_msg()); + } + $data[] = $dataSet; + } + if (!$data) { + throw new InvalidDataProviderException('The data set for the @testWith annotation cannot be parsed.'); + } + return $data; + } /** - * @var ?string + * @psalm-param class-string $className + * @psalm-param non-empty-string $methodName + * + * @throws MoreThanOneDataSetFromDataProviderException */ - private $coverageXml; + private function valueObjectForTestMethodWithoutTestData(string $className, string $methodName): TestMethod + { + $location = Reflection::sourceLocationFor($className, $methodName); + return new TestMethod($className, $methodName, $location['file'], $location['line'], Event\Code\TestDoxBuilder::fromClassNameAndMethodName($className, $methodName), MetadataRegistry::parser()->forClassAndMethod($className, $methodName), TestDataCollection::fromArray([])); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Metadata\Api; + +use function assert; +use PHPUnit\Framework\ExecutionOrderDependency; +use PHPUnit\Metadata\DependsOnClass; +use PHPUnit\Metadata\DependsOnMethod; +use PHPUnit\Metadata\Parser\Registry; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class Dependencies +{ /** - * @var ?bool + * @psalm-param class-string $className + * @psalm-param non-empty-string $methodName + * + * @psalm-return list */ - private $pathCoverage; + public static function dependencies(string $className, string $methodName): array + { + $dependencies = []; + foreach (Registry::parser()->forClassAndMethod($className, $methodName)->isDepends() as $metadata) { + if ($metadata->isDependsOnClass()) { + assert($metadata instanceof DependsOnClass); + $dependencies[] = ExecutionOrderDependency::forClass($metadata); + continue; + } + assert($metadata instanceof DependsOnMethod); + if (empty($metadata->methodName())) { + $dependencies[] = ExecutionOrderDependency::invalid(); + continue; + } + $dependencies[] = ExecutionOrderDependency::forMethod($metadata); + } + return $dependencies; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Metadata\Api; + +use function array_flip; +use function array_key_exists; +use function array_unique; +use function assert; +use function strtolower; +use function trim; +use PHPUnit\Framework\TestSize\TestSize; +use PHPUnit\Metadata\Covers; +use PHPUnit\Metadata\CoversClass; +use PHPUnit\Metadata\CoversFunction; +use PHPUnit\Metadata\Group; +use PHPUnit\Metadata\Parser\Registry; +use PHPUnit\Metadata\Uses; +use PHPUnit\Metadata\UsesClass; +use PHPUnit\Metadata\UsesFunction; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class Groups +{ /** - * @var ?string + * @var array> */ - private $coverageCacheDirectory; + private static array $groupCache = []; /** - * @var ?bool + * @psalm-param class-string $className + * @psalm-param non-empty-string $methodName + * + * @psalm-return array */ - private $warmCoverageCache; + public function groups(string $className, string $methodName, bool $includeVirtual = \true): array + { + $key = $className . '::' . $methodName . '::' . $includeVirtual; + if (array_key_exists($key, self::$groupCache)) { + return self::$groupCache[$key]; + } + $groups = []; + foreach (Registry::parser()->forClassAndMethod($className, $methodName)->isGroup() as $group) { + assert($group instanceof Group); + $groups[] = $group->groupName(); + } + if ($groups === []) { + $groups[] = 'default'; + } + if (!$includeVirtual) { + return self::$groupCache[$key] = array_unique($groups); + } + foreach (Registry::parser()->forClassAndMethod($className, $methodName) as $metadata) { + if ($metadata->isCoversClass() || $metadata->isCoversFunction()) { + assert($metadata instanceof CoversClass || $metadata instanceof CoversFunction); + $groups[] = '__phpunit_covers_' . $this->canonicalizeName($metadata->asStringForCodeUnitMapper()); + continue; + } + if ($metadata->isCovers()) { + assert($metadata instanceof Covers); + $groups[] = '__phpunit_covers_' . $this->canonicalizeName($metadata->target()); + continue; + } + if ($metadata->isUsesClass() || $metadata->isUsesFunction()) { + assert($metadata instanceof UsesClass || $metadata instanceof UsesFunction); + $groups[] = '__phpunit_uses_' . $this->canonicalizeName($metadata->asStringForCodeUnitMapper()); + continue; + } + if ($metadata->isUses()) { + assert($metadata instanceof Uses); + $groups[] = '__phpunit_uses_' . $this->canonicalizeName($metadata->target()); + } + } + return self::$groupCache[$key] = array_unique($groups); + } /** - * @var ?bool + * @psalm-param class-string $className + * @psalm-param non-empty-string $methodName */ - private $debug; + public function size(string $className, string $methodName): TestSize + { + $groups = array_flip($this->groups($className, $methodName)); + if (isset($groups['large'])) { + return TestSize::large(); + } + if (isset($groups['medium'])) { + return TestSize::medium(); + } + if (isset($groups['small'])) { + return TestSize::small(); + } + return TestSize::unknown(); + } + private function canonicalizeName(string $name): string + { + return strtolower(trim($name, '\\')); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Metadata\Api; + +use function array_unshift; +use function assert; +use function class_exists; +use PHPUnit\Metadata\Parser\Registry; +use PHPUnit\Util\Reflection; +use ReflectionClass; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class HookMethods +{ /** - * @var ?int + * @psalm-var array, before: list, preCondition: list, postCondition: list, after: list, afterClass: list}> */ - private $defaultTimeLimit; + private static array $hookMethods = []; /** - * @var ?bool + * @psalm-param class-string $className + * + * @psalm-return array{beforeClass: list, before: list, preCondition: list, postCondition: list, after: list, afterClass: list} */ - private $disableCodeCoverageIgnore; + public function hookMethods(string $className): array + { + if (!class_exists($className)) { + return self::emptyHookMethodsArray(); + } + if (isset(self::$hookMethods[$className])) { + return self::$hookMethods[$className]; + } + self::$hookMethods[$className] = self::emptyHookMethodsArray(); + foreach (Reflection::methodsInTestClass(new ReflectionClass($className)) as $method) { + $methodName = $method->getName(); + assert(!empty($methodName)); + $metadata = Registry::parser()->forMethod($className, $methodName); + if ($method->isStatic()) { + if ($metadata->isBeforeClass()->isNotEmpty()) { + array_unshift(self::$hookMethods[$className]['beforeClass'], $methodName); + } + if ($metadata->isAfterClass()->isNotEmpty()) { + self::$hookMethods[$className]['afterClass'][] = $methodName; + } + } + if ($metadata->isBefore()->isNotEmpty()) { + array_unshift(self::$hookMethods[$className]['before'], $methodName); + } + if ($metadata->isPreCondition()->isNotEmpty()) { + array_unshift(self::$hookMethods[$className]['preCondition'], $methodName); + } + if ($metadata->isPostCondition()->isNotEmpty()) { + self::$hookMethods[$className]['postCondition'][] = $methodName; + } + if ($metadata->isAfter()->isNotEmpty()) { + self::$hookMethods[$className]['after'][] = $methodName; + } + } + return self::$hookMethods[$className]; + } /** - * @var ?bool + * @psalm-return array{beforeClass: list, before: list, preCondition: list, postCondition: list, after: list, afterClass: list} */ - private $disallowTestOutput; + private function emptyHookMethodsArray(): array + { + return ['beforeClass' => ['setUpBeforeClass'], 'before' => ['setUp'], 'preCondition' => ['assertPreConditions'], 'postCondition' => ['assertPostConditions'], 'after' => ['tearDown'], 'afterClass' => ['tearDownAfterClass']]; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Metadata\Api; + +use const PHP_OS; +use const PHP_OS_FAMILY; +use const PHP_VERSION; +use function addcslashes; +use function assert; +use function extension_loaded; +use function function_exists; +use function ini_get; +use function method_exists; +use function phpversion; +use function preg_match; +use function sprintf; +use PHPUnit\Metadata\Parser\Registry; +use PHPUnit\Metadata\RequiresFunction; +use PHPUnit\Metadata\RequiresMethod; +use PHPUnit\Metadata\RequiresOperatingSystem; +use PHPUnit\Metadata\RequiresOperatingSystemFamily; +use PHPUnit\Metadata\RequiresPhp; +use PHPUnit\Metadata\RequiresPhpExtension; +use PHPUnit\Metadata\RequiresPhpunit; +use PHPUnit\Metadata\RequiresSetting; +use PHPUnit\Runner\Version; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class Requirements +{ /** - * @var ?bool + * @psalm-param class-string $className + * @psalm-param non-empty-string $methodName + * + * @psalm-return list */ - private $disallowTodoAnnotatedTests; + public function requirementsNotSatisfiedFor(string $className, string $methodName): array + { + $notSatisfied = []; + foreach (Registry::parser()->forClassAndMethod($className, $methodName) as $metadata) { + if ($metadata->isRequiresPhp()) { + assert($metadata instanceof RequiresPhp); + if (!$metadata->versionRequirement()->isSatisfiedBy(PHP_VERSION)) { + $notSatisfied[] = sprintf('PHP %s is required.', $metadata->versionRequirement()->asString()); + } + } + if ($metadata->isRequiresPhpExtension()) { + assert($metadata instanceof RequiresPhpExtension); + if (!extension_loaded($metadata->extension()) || $metadata->hasVersionRequirement() && !$metadata->versionRequirement()->isSatisfiedBy(phpversion($metadata->extension()))) { + $notSatisfied[] = sprintf('PHP extension %s%s is required.', $metadata->extension(), $metadata->hasVersionRequirement() ? ' ' . $metadata->versionRequirement()->asString() : ''); + } + } + if ($metadata->isRequiresPhpunit()) { + assert($metadata instanceof RequiresPhpunit); + if (!$metadata->versionRequirement()->isSatisfiedBy(Version::id())) { + $notSatisfied[] = sprintf('PHPUnit %s is required.', $metadata->versionRequirement()->asString()); + } + } + if ($metadata->isRequiresOperatingSystemFamily()) { + assert($metadata instanceof RequiresOperatingSystemFamily); + if ($metadata->operatingSystemFamily() !== PHP_OS_FAMILY) { + $notSatisfied[] = sprintf('Operating system %s is required.', $metadata->operatingSystemFamily()); + } + } + if ($metadata->isRequiresOperatingSystem()) { + assert($metadata instanceof RequiresOperatingSystem); + $pattern = sprintf('/%s/i', addcslashes($metadata->operatingSystem(), '/')); + if (!preg_match($pattern, PHP_OS)) { + $notSatisfied[] = sprintf('Operating system %s is required.', $metadata->operatingSystem()); + } + } + if ($metadata->isRequiresFunction()) { + assert($metadata instanceof RequiresFunction); + if (!function_exists($metadata->functionName())) { + $notSatisfied[] = sprintf('Function %s() is required.', $metadata->functionName()); + } + } + if ($metadata->isRequiresMethod()) { + assert($metadata instanceof RequiresMethod); + if (!method_exists($metadata->className(), $metadata->methodName())) { + $notSatisfied[] = sprintf('Method %s::%s() is required.', $metadata->className(), $metadata->methodName()); + } + } + if ($metadata->isRequiresSetting()) { + assert($metadata instanceof RequiresSetting); + if (ini_get($metadata->setting()) !== $metadata->value()) { + $notSatisfied[] = sprintf('Setting "%s" is required to be "%s".', $metadata->setting(), $metadata->value()); + } + } + } + return $notSatisfied; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Metadata; + +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class BackupGlobals extends \PHPUnit\Metadata\Metadata +{ + private readonly bool $enabled; /** - * @var ?bool + * @psalm-param 0|1 $level */ - private $enforceTimeLimit; + protected function __construct(int $level, bool $enabled) + { + parent::__construct($level); + $this->enabled = $enabled; + } /** - * @var null|string[] + * @psalm-assert-if-true BackupGlobals $this */ - private $excludeGroups; + public function isBackupGlobals(): bool + { + return \true; + } + public function enabled(): bool + { + return $this->enabled; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Metadata; + +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class BackupStaticProperties extends \PHPUnit\Metadata\Metadata +{ + private readonly bool $enabled; /** - * @var ?int + * @psalm-param 0|1 $level */ - private $executionOrder; + protected function __construct(int $level, bool $enabled) + { + parent::__construct($level); + $this->enabled = $enabled; + } /** - * @var ?int + * @psalm-assert-if-true BackupStaticProperties $this */ - private $executionOrderDefects; + public function isBackupStaticProperties(): bool + { + return \true; + } + public function enabled(): bool + { + return $this->enabled; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Metadata; + +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class Before extends \PHPUnit\Metadata\Metadata +{ /** - * @var null|Extension[] + * @psalm-assert-if-true Before $this */ - private $extensions; + public function isBefore(): bool + { + return \true; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Metadata; + +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class BeforeClass extends \PHPUnit\Metadata\Metadata +{ /** - * @var null|string[] + * @psalm-assert-if-true BeforeClass $this */ - private $unavailableExtensions; + public function isBeforeClass(): bool + { + return \true; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Metadata; + +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class Covers extends \PHPUnit\Metadata\Metadata +{ /** - * @var ?bool + * @psalm-var non-empty-string */ - private $failOnEmptyTestSuite; + private readonly string $target; /** - * @var ?bool + * @psalm-param 0|1 $level + * @psalm-param non-empty-string $target */ - private $failOnIncomplete; + protected function __construct(int $level, string $target) + { + parent::__construct($level); + $this->target = $target; + } /** - * @var ?bool + * @psalm-assert-if-true Covers $this */ - private $failOnRisky; + public function isCovers(): bool + { + return \true; + } /** - * @var ?bool + * @psalm-return non-empty-string */ - private $failOnSkipped; + public function target(): string + { + return $this->target; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Metadata; + +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class CoversClass extends \PHPUnit\Metadata\Metadata +{ /** - * @var ?bool + * @psalm-var class-string */ - private $failOnWarning; + private readonly string $className; /** - * @var ?string + * @psalm-param 0|1 $level + * @psalm-param class-string $className */ - private $filter; + protected function __construct(int $level, string $className) + { + parent::__construct($level); + $this->className = $className; + } /** - * @var ?bool + * @psalm-assert-if-true CoversClass $this */ - private $generateConfiguration; + public function isCoversClass(): bool + { + return \true; + } /** - * @var ?bool + * @psalm-return class-string */ - private $migrateConfiguration; + public function className(): string + { + return $this->className; + } /** - * @var null|string[] + * @psalm-return class-string + * + * @internal This method is not covered by the backward compatibility promise for PHPUnit */ - private $groups; + public function asStringForCodeUnitMapper(): string + { + return $this->className; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Metadata; + +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class CoversDefaultClass extends \PHPUnit\Metadata\Metadata +{ /** - * @var null|string[] + * @psalm-var class-string */ - private $testsCovering; + private readonly string $className; /** - * @var null|string[] + * @psalm-param 0|1 $level + * @psalm-param class-string $className */ - private $testsUsing; + protected function __construct(int $level, string $className) + { + parent::__construct($level); + $this->className = $className; + } /** - * @var ?bool + * @psalm-assert-if-true CoversDefaultClass $this */ - private $help; + public function isCoversDefaultClass(): bool + { + return \true; + } /** - * @var ?string + * @psalm-return class-string */ - private $includePath; + public function className(): string + { + return $this->className; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Metadata; + +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class CoversFunction extends \PHPUnit\Metadata\Metadata +{ /** - * @var null|string[] + * @psalm-var non-empty-string */ - private $iniSettings; + private readonly string $functionName; /** - * @var ?string + * @psalm-param 0|1 $level + * @psalm-param non-empty-string $functionName */ - private $junitLogfile; + protected function __construct(int $level, string $functionName) + { + parent::__construct($level); + $this->functionName = $functionName; + } /** - * @var ?bool + * @psalm-assert-if-true CoversFunction $this */ - private $listGroups; + public function isCoversFunction(): bool + { + return \true; + } /** - * @var ?bool + * @psalm-return non-empty-string */ - private $listSuites; + public function functionName(): string + { + return $this->functionName; + } /** - * @var ?bool - */ - private $listTests; - /** - * @var ?string - */ - private $listTestsXml; - /** - * @var ?string - */ - private $loader; - /** - * @var ?bool - */ - private $noCoverage; - /** - * @var ?bool - */ - private $noExtensions; - /** - * @var ?bool - */ - private $noInteraction; - /** - * @var ?bool - */ - private $noLogging; - /** - * @var ?string - */ - private $printer; - /** - * @var ?bool - */ - private $processIsolation; - /** - * @var ?int - */ - private $randomOrderSeed; - /** - * @var ?int - */ - private $repeat; - /** - * @var ?bool + * @internal This method is not covered by the backward compatibility promise for PHPUnit */ - private $reportUselessTests; + public function asStringForCodeUnitMapper(): string + { + return '::' . $this->functionName; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Metadata; + +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class CoversNothing extends \PHPUnit\Metadata\Metadata +{ /** - * @var ?bool + * @psalm-assert-if-true CoversNothing $this */ - private $resolveDependencies; + public function isCoversNothing(): bool + { + return \true; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Metadata; + +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class DataProvider extends \PHPUnit\Metadata\Metadata +{ /** - * @var ?bool + * @psalm-var class-string */ - private $reverseList; + private readonly string $className; /** - * @var ?bool + * @psalm-var non-empty-string */ - private $stderr; + private readonly string $methodName; /** - * @var ?bool + * @psalm-param 0|1 $level + * @psalm-param class-string $className + * @psalm-param non-empty-string $methodName */ - private $strictCoverage; + protected function __construct(int $level, string $className, string $methodName) + { + parent::__construct($level); + $this->className = $className; + $this->methodName = $methodName; + } /** - * @var ?bool + * @psalm-assert-if-true DataProvider $this */ - private $stopOnDefect; + public function isDataProvider(): bool + { + return \true; + } /** - * @var ?bool + * @psalm-return class-string */ - private $stopOnError; + public function className(): string + { + return $this->className; + } /** - * @var ?bool + * @psalm-return non-empty-string */ - private $stopOnFailure; + public function methodName(): string + { + return $this->methodName; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Metadata; + +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class DependsOnClass extends \PHPUnit\Metadata\Metadata +{ /** - * @var ?bool + * @psalm-var class-string */ - private $stopOnIncomplete; + private readonly string $className; + private readonly bool $deepClone; + private readonly bool $shallowClone; /** - * @var ?bool + * @psalm-param 0|1 $level + * @psalm-param class-string $className */ - private $stopOnRisky; + protected function __construct(int $level, string $className, bool $deepClone, bool $shallowClone) + { + parent::__construct($level); + $this->className = $className; + $this->deepClone = $deepClone; + $this->shallowClone = $shallowClone; + } /** - * @var ?bool + * @psalm-assert-if-true DependsOnClass $this */ - private $stopOnSkipped; + public function isDependsOnClass(): bool + { + return \true; + } /** - * @var ?bool + * @psalm-return class-string */ - private $stopOnWarning; + public function className(): string + { + return $this->className; + } + public function deepClone(): bool + { + return $this->deepClone; + } + public function shallowClone(): bool + { + return $this->shallowClone; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Metadata; + +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class DependsOnMethod extends \PHPUnit\Metadata\Metadata +{ /** - * @var ?string + * @psalm-var class-string */ - private $teamcityLogfile; + private readonly string $className; /** - * @var null|string[] + * @psalm-var non-empty-string */ - private $testdoxExcludeGroups; + private readonly string $methodName; + private readonly bool $deepClone; + private readonly bool $shallowClone; /** - * @var null|string[] + * @psalm-param 0|1 $level + * @psalm-param class-string $className + * @psalm-param non-empty-string $methodName */ - private $testdoxGroups; + protected function __construct(int $level, string $className, string $methodName, bool $deepClone, bool $shallowClone) + { + parent::__construct($level); + $this->className = $className; + $this->methodName = $methodName; + $this->deepClone = $deepClone; + $this->shallowClone = $shallowClone; + } /** - * @var ?string + * @psalm-assert-if-true DependsOnMethod $this */ - private $testdoxHtmlFile; + public function isDependsOnMethod(): bool + { + return \true; + } /** - * @var ?string + * @psalm-return class-string */ - private $testdoxTextFile; + public function className(): string + { + return $this->className; + } /** - * @var ?string + * @psalm-return non-empty-string */ - private $testdoxXmlFile; + public function methodName(): string + { + return $this->methodName; + } + public function deepClone(): bool + { + return $this->deepClone; + } + public function shallowClone(): bool + { + return $this->shallowClone; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Metadata; + +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class DoesNotPerformAssertions extends \PHPUnit\Metadata\Metadata +{ /** - * @var null|string[] + * @psalm-assert-if-true DoesNotPerformAssertions $this */ - private $testSuffixes; + public function isDoesNotPerformAssertions(): bool + { + return \true; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Metadata; + +use function sprintf; +use PHPUnit\Exception; +use RuntimeException; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class AnnotationsAreNotSupportedForInternalClassesException extends RuntimeException implements Exception +{ /** - * @var ?string + * @psalm-param class-string $className */ - private $testSuite; + public function __construct(string $className) + { + parent::__construct(sprintf('Annotations can only be parsed for user-defined classes, trying to parse annotations for class "%s"', $className)); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Metadata; + +interface Exception extends \PHPUnit\Exception +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Metadata; + +use RuntimeException; +final class InvalidVersionRequirementException extends RuntimeException implements \PHPUnit\Metadata\Exception +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Metadata; + +use RuntimeException; +final class NoVersionRequirementException extends RuntimeException implements \PHPUnit\Metadata\Exception +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Metadata; + +use PHPUnit\Exception; +use RuntimeException; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class ReflectionException extends RuntimeException implements Exception +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Metadata; + +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class ExcludeGlobalVariableFromBackup extends \PHPUnit\Metadata\Metadata +{ /** - * @var string[] + * @psalm-var non-empty-string */ - private $unrecognizedOptions; + private readonly string $globalVariableName; /** - * @var ?string + * @psalm-param 0|1 $level + * @psalm-param non-empty-string $globalVariableName */ - private $unrecognizedOrderBy; + protected function __construct(int $level, string $globalVariableName) + { + parent::__construct($level); + $this->globalVariableName = $globalVariableName; + } /** - * @var ?bool + * @psalm-assert-if-true ExcludeGlobalVariableFromBackup $this */ - private $useDefaultConfiguration; + public function isExcludeGlobalVariableFromBackup(): bool + { + return \true; + } /** - * @var ?bool + * @psalm-return non-empty-string */ - private $verbose; + public function globalVariableName(): string + { + return $this->globalVariableName; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Metadata; + +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class ExcludeStaticPropertyFromBackup extends \PHPUnit\Metadata\Metadata +{ /** - * @var ?bool + * @psalm-var class-string */ - private $version; + private readonly string $className; /** - * @var ?string + * @psalm-var non-empty-string */ - private $xdebugFilterFile; + private readonly string $propertyName; /** - * @param null|int|string $columns + * @psalm-param 0|1 $level + * @psalm-param class-string $className + * @psalm-param non-empty-string $propertyName */ - public function __construct(?string $argument, ?string $atLeastVersion, ?bool $backupGlobals, ?bool $backupStaticAttributes, ?bool $beStrictAboutChangesToGlobalState, ?bool $beStrictAboutResourceUsageDuringSmallTests, ?string $bootstrap, ?bool $cacheResult, ?string $cacheResultFile, ?bool $checkVersion, ?string $colors, $columns, ?string $configuration, ?string $coverageClover, ?string $coverageCobertura, ?string $coverageCrap4J, ?string $coverageHtml, ?string $coveragePhp, ?string $coverageText, ?bool $coverageTextShowUncoveredFiles, ?bool $coverageTextShowOnlySummary, ?string $coverageXml, ?bool $pathCoverage, ?string $coverageCacheDirectory, ?bool $warmCoverageCache, ?bool $debug, ?int $defaultTimeLimit, ?bool $disableCodeCoverageIgnore, ?bool $disallowTestOutput, ?bool $disallowTodoAnnotatedTests, ?bool $enforceTimeLimit, ?array $excludeGroups, ?int $executionOrder, ?int $executionOrderDefects, ?array $extensions, ?array $unavailableExtensions, ?bool $failOnEmptyTestSuite, ?bool $failOnIncomplete, ?bool $failOnRisky, ?bool $failOnSkipped, ?bool $failOnWarning, ?string $filter, ?bool $generateConfiguration, ?bool $migrateConfiguration, ?array $groups, ?array $testsCovering, ?array $testsUsing, ?bool $help, ?string $includePath, ?array $iniSettings, ?string $junitLogfile, ?bool $listGroups, ?bool $listSuites, ?bool $listTests, ?string $listTestsXml, ?string $loader, ?bool $noCoverage, ?bool $noExtensions, ?bool $noInteraction, ?bool $noLogging, ?string $printer, ?bool $processIsolation, ?int $randomOrderSeed, ?int $repeat, ?bool $reportUselessTests, ?bool $resolveDependencies, ?bool $reverseList, ?bool $stderr, ?bool $strictCoverage, ?bool $stopOnDefect, ?bool $stopOnError, ?bool $stopOnFailure, ?bool $stopOnIncomplete, ?bool $stopOnRisky, ?bool $stopOnSkipped, ?bool $stopOnWarning, ?string $teamcityLogfile, ?array $testdoxExcludeGroups, ?array $testdoxGroups, ?string $testdoxHtmlFile, ?string $testdoxTextFile, ?string $testdoxXmlFile, ?array $testSuffixes, ?string $testSuite, array $unrecognizedOptions, ?string $unrecognizedOrderBy, ?bool $useDefaultConfiguration, ?bool $verbose, ?bool $version, ?array $coverageFilter, ?string $xdebugFilterFile) - { - $this->argument = $argument; - $this->atLeastVersion = $atLeastVersion; - $this->backupGlobals = $backupGlobals; - $this->backupStaticAttributes = $backupStaticAttributes; - $this->beStrictAboutChangesToGlobalState = $beStrictAboutChangesToGlobalState; - $this->beStrictAboutResourceUsageDuringSmallTests = $beStrictAboutResourceUsageDuringSmallTests; - $this->bootstrap = $bootstrap; - $this->cacheResult = $cacheResult; - $this->cacheResultFile = $cacheResultFile; - $this->checkVersion = $checkVersion; - $this->colors = $colors; - $this->columns = $columns; - $this->configuration = $configuration; - $this->coverageFilter = $coverageFilter; - $this->coverageClover = $coverageClover; - $this->coverageCobertura = $coverageCobertura; - $this->coverageCrap4J = $coverageCrap4J; - $this->coverageHtml = $coverageHtml; - $this->coveragePhp = $coveragePhp; - $this->coverageText = $coverageText; - $this->coverageTextShowUncoveredFiles = $coverageTextShowUncoveredFiles; - $this->coverageTextShowOnlySummary = $coverageTextShowOnlySummary; - $this->coverageXml = $coverageXml; - $this->pathCoverage = $pathCoverage; - $this->coverageCacheDirectory = $coverageCacheDirectory; - $this->warmCoverageCache = $warmCoverageCache; - $this->debug = $debug; - $this->defaultTimeLimit = $defaultTimeLimit; - $this->disableCodeCoverageIgnore = $disableCodeCoverageIgnore; - $this->disallowTestOutput = $disallowTestOutput; - $this->disallowTodoAnnotatedTests = $disallowTodoAnnotatedTests; - $this->enforceTimeLimit = $enforceTimeLimit; - $this->excludeGroups = $excludeGroups; - $this->executionOrder = $executionOrder; - $this->executionOrderDefects = $executionOrderDefects; - $this->extensions = $extensions; - $this->unavailableExtensions = $unavailableExtensions; - $this->failOnEmptyTestSuite = $failOnEmptyTestSuite; - $this->failOnIncomplete = $failOnIncomplete; - $this->failOnRisky = $failOnRisky; - $this->failOnSkipped = $failOnSkipped; - $this->failOnWarning = $failOnWarning; - $this->filter = $filter; - $this->generateConfiguration = $generateConfiguration; - $this->migrateConfiguration = $migrateConfiguration; - $this->groups = $groups; - $this->testsCovering = $testsCovering; - $this->testsUsing = $testsUsing; - $this->help = $help; - $this->includePath = $includePath; - $this->iniSettings = $iniSettings; - $this->junitLogfile = $junitLogfile; - $this->listGroups = $listGroups; - $this->listSuites = $listSuites; - $this->listTests = $listTests; - $this->listTestsXml = $listTestsXml; - $this->loader = $loader; - $this->noCoverage = $noCoverage; - $this->noExtensions = $noExtensions; - $this->noInteraction = $noInteraction; - $this->noLogging = $noLogging; - $this->printer = $printer; - $this->processIsolation = $processIsolation; - $this->randomOrderSeed = $randomOrderSeed; - $this->repeat = $repeat; - $this->reportUselessTests = $reportUselessTests; - $this->resolveDependencies = $resolveDependencies; - $this->reverseList = $reverseList; - $this->stderr = $stderr; - $this->strictCoverage = $strictCoverage; - $this->stopOnDefect = $stopOnDefect; - $this->stopOnError = $stopOnError; - $this->stopOnFailure = $stopOnFailure; - $this->stopOnIncomplete = $stopOnIncomplete; - $this->stopOnRisky = $stopOnRisky; - $this->stopOnSkipped = $stopOnSkipped; - $this->stopOnWarning = $stopOnWarning; - $this->teamcityLogfile = $teamcityLogfile; - $this->testdoxExcludeGroups = $testdoxExcludeGroups; - $this->testdoxGroups = $testdoxGroups; - $this->testdoxHtmlFile = $testdoxHtmlFile; - $this->testdoxTextFile = $testdoxTextFile; - $this->testdoxXmlFile = $testdoxXmlFile; - $this->testSuffixes = $testSuffixes; - $this->testSuite = $testSuite; - $this->unrecognizedOptions = $unrecognizedOptions; - $this->unrecognizedOrderBy = $unrecognizedOrderBy; - $this->useDefaultConfiguration = $useDefaultConfiguration; - $this->verbose = $verbose; - $this->version = $version; - $this->xdebugFilterFile = $xdebugFilterFile; - } - public function hasArgument() : bool + protected function __construct(int $level, string $className, string $propertyName) { - return $this->argument !== null; + parent::__construct($level); + $this->className = $className; + $this->propertyName = $propertyName; } /** - * @throws Exception + * @psalm-assert-if-true ExcludeStaticPropertyFromBackup $this */ - public function argument() : string - { - if ($this->argument === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->argument; - } - public function hasAtLeastVersion() : bool + public function isExcludeStaticPropertyFromBackup(): bool { - return $this->atLeastVersion !== null; + return \true; } /** - * @throws Exception + * @psalm-return class-string */ - public function atLeastVersion() : string - { - if ($this->atLeastVersion === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->atLeastVersion; - } - public function hasBackupGlobals() : bool + public function className(): string { - return $this->backupGlobals !== null; + return $this->className; } /** - * @throws Exception + * @psalm-return non-empty-string */ - public function backupGlobals() : bool - { - if ($this->backupGlobals === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->backupGlobals; - } - public function hasBackupStaticAttributes() : bool + public function propertyName(): string { - return $this->backupStaticAttributes !== null; + return $this->propertyName; } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Metadata; + +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class Group extends \PHPUnit\Metadata\Metadata +{ /** - * @throws Exception + * @psalm-var non-empty-string */ - public function backupStaticAttributes() : bool - { - if ($this->backupStaticAttributes === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->backupStaticAttributes; - } - public function hasBeStrictAboutChangesToGlobalState() : bool - { - return $this->beStrictAboutChangesToGlobalState !== null; - } + private readonly string $groupName; /** - * @throws Exception + * @psalm-param 0|1 $level + * @psalm-param non-empty-string $groupName */ - public function beStrictAboutChangesToGlobalState() : bool - { - if ($this->beStrictAboutChangesToGlobalState === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->beStrictAboutChangesToGlobalState; - } - public function hasBeStrictAboutResourceUsageDuringSmallTests() : bool + protected function __construct(int $level, string $groupName) { - return $this->beStrictAboutResourceUsageDuringSmallTests !== null; + parent::__construct($level); + $this->groupName = $groupName; } /** - * @throws Exception + * @psalm-assert-if-true Group $this */ - public function beStrictAboutResourceUsageDuringSmallTests() : bool - { - if ($this->beStrictAboutResourceUsageDuringSmallTests === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->beStrictAboutResourceUsageDuringSmallTests; - } - public function hasBootstrap() : bool + public function isGroup(): bool { - return $this->bootstrap !== null; + return \true; } /** - * @throws Exception + * @psalm-return non-empty-string */ - public function bootstrap() : string - { - if ($this->bootstrap === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->bootstrap; - } - public function hasCacheResult() : bool + public function groupName(): string { - return $this->cacheResult !== null; + return $this->groupName; } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Metadata; + +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/5513 + */ +final class IgnoreClassForCodeCoverage extends \PHPUnit\Metadata\Metadata +{ /** - * @throws Exception + * @psalm-var class-string + */ + private readonly string $className; + /** + * @psalm-param 0|1 $level + * @psalm-param class-string $className */ - public function cacheResult() : bool + protected function __construct(int $level, string $className) { - if ($this->cacheResult === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->cacheResult; + parent::__construct($level); + $this->className = $className; } - public function hasCacheResultFile() : bool + /** + * @psalm-assert-if-true IgnoreClassForCodeCoverage $this + */ + public function isIgnoreClassForCodeCoverage(): bool { - return $this->cacheResultFile !== null; + return \true; } /** - * @throws Exception + * @psalm-return class-string */ - public function cacheResultFile() : string + public function className(): string { - if ($this->cacheResultFile === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->cacheResultFile; - } - public function hasCheckVersion() : bool - { - return $this->checkVersion !== null; + return $this->className; } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Metadata; + +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class IgnoreDeprecations extends \PHPUnit\Metadata\Metadata +{ /** - * @throws Exception + * @psalm-assert-if-true IgnoreDeprecations $this */ - public function checkVersion() : bool + public function isIgnoreDeprecations(): bool { - if ($this->checkVersion === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->checkVersion; + return \true; } - public function hasColors() : bool +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Metadata; + +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/5513 + */ +final class IgnoreFunctionForCodeCoverage extends \PHPUnit\Metadata\Metadata +{ + /** + * @psalm-var non-empty-string + */ + private readonly string $functionName; + /** + * @psalm-param 0|1 $level + * @psalm-param non-empty-string $functionName + */ + protected function __construct(int $level, string $functionName) { - return $this->colors !== null; + parent::__construct($level); + $this->functionName = $functionName; } /** - * @throws Exception + * @psalm-assert-if-true IgnoreFunctionForCodeCoverage $this */ - public function colors() : string + public function isIgnoreFunctionForCodeCoverage(): bool { - if ($this->colors === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->colors; + return \true; } - public function hasColumns() : bool + /** + * @psalm-return non-empty-string + */ + public function functionName(): string { - return $this->columns !== null; + return $this->functionName; } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Metadata; + +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/5513 + */ +final class IgnoreMethodForCodeCoverage extends \PHPUnit\Metadata\Metadata +{ /** - * @throws Exception + * @psalm-var class-string + */ + private readonly string $className; + /** + * @psalm-var non-empty-string + */ + private readonly string $methodName; + /** + * @psalm-param 0|1 $level + * @psalm-param class-string $className + * @psalm-param non-empty-string $methodName */ - public function columns() + protected function __construct(int $level, string $className, string $methodName) { - if ($this->columns === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->columns; + parent::__construct($level); + $this->className = $className; + $this->methodName = $methodName; } - public function hasConfiguration() : bool + /** + * @psalm-assert-if-true IgnoreMethodForCodeCoverage $this + */ + public function isIgnoreMethodForCodeCoverage(): bool { - return $this->configuration !== null; + return \true; } /** - * @throws Exception + * @psalm-return class-string */ - public function configuration() : string + public function className(): string { - if ($this->configuration === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->configuration; + return $this->className; } - public function hasCoverageFilter() : bool + /** + * @psalm-return non-empty-string + */ + public function methodName(): string { - return $this->coverageFilter !== null; + return $this->methodName; } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Metadata; + +use PHPUnit\Metadata\Version\Requirement; +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +abstract class Metadata +{ + private const CLASS_LEVEL = 0; + private const METHOD_LEVEL = 1; /** - * @throws Exception + * @psalm-var 0|1 */ - public function coverageFilter() : array + private readonly int $level; + public static function after(): \PHPUnit\Metadata\After { - if ($this->coverageFilter === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->coverageFilter; + return new \PHPUnit\Metadata\After(self::METHOD_LEVEL); } - public function hasCoverageClover() : bool + public static function afterClass(): \PHPUnit\Metadata\AfterClass { - return $this->coverageClover !== null; + return new \PHPUnit\Metadata\AfterClass(self::METHOD_LEVEL); } - /** - * @throws Exception - */ - public function coverageClover() : string + public static function backupGlobalsOnClass(bool $enabled): \PHPUnit\Metadata\BackupGlobals { - if ($this->coverageClover === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->coverageClover; + return new \PHPUnit\Metadata\BackupGlobals(self::CLASS_LEVEL, $enabled); } - public function hasCoverageCobertura() : bool + public static function backupGlobalsOnMethod(bool $enabled): \PHPUnit\Metadata\BackupGlobals { - return $this->coverageCobertura !== null; + return new \PHPUnit\Metadata\BackupGlobals(self::METHOD_LEVEL, $enabled); } - /** - * @throws Exception - */ - public function coverageCobertura() : string + public static function backupStaticPropertiesOnClass(bool $enabled): \PHPUnit\Metadata\BackupStaticProperties { - if ($this->coverageCobertura === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->coverageCobertura; + return new \PHPUnit\Metadata\BackupStaticProperties(self::CLASS_LEVEL, $enabled); } - public function hasCoverageCrap4J() : bool + public static function backupStaticPropertiesOnMethod(bool $enabled): \PHPUnit\Metadata\BackupStaticProperties { - return $this->coverageCrap4J !== null; + return new \PHPUnit\Metadata\BackupStaticProperties(self::METHOD_LEVEL, $enabled); } - /** - * @throws Exception - */ - public function coverageCrap4J() : string + public static function before(): \PHPUnit\Metadata\Before { - if ($this->coverageCrap4J === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->coverageCrap4J; + return new \PHPUnit\Metadata\Before(self::METHOD_LEVEL); } - public function hasCoverageHtml() : bool + public static function beforeClass(): \PHPUnit\Metadata\BeforeClass { - return $this->coverageHtml !== null; + return new \PHPUnit\Metadata\BeforeClass(self::METHOD_LEVEL); } /** - * @throws Exception + * @psalm-param class-string $className */ - public function coverageHtml() : string + public static function coversClass(string $className): \PHPUnit\Metadata\CoversClass { - if ($this->coverageHtml === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->coverageHtml; + return new \PHPUnit\Metadata\CoversClass(self::CLASS_LEVEL, $className); } - public function hasCoveragePhp() : bool + /** + * @psalm-param non-empty-string $functionName + */ + public static function coversFunction(string $functionName): \PHPUnit\Metadata\CoversFunction { - return $this->coveragePhp !== null; + return new \PHPUnit\Metadata\CoversFunction(self::CLASS_LEVEL, $functionName); } /** - * @throws Exception + * @psalm-param non-empty-string $target */ - public function coveragePhp() : string + public static function coversOnClass(string $target): \PHPUnit\Metadata\Covers { - if ($this->coveragePhp === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->coveragePhp; + return new \PHPUnit\Metadata\Covers(self::CLASS_LEVEL, $target); } - public function hasCoverageText() : bool + /** + * @psalm-param non-empty-string $target + */ + public static function coversOnMethod(string $target): \PHPUnit\Metadata\Covers { - return $this->coverageText !== null; + return new \PHPUnit\Metadata\Covers(self::METHOD_LEVEL, $target); } /** - * @throws Exception + * @psalm-param class-string $className */ - public function coverageText() : string + public static function coversDefaultClass(string $className): \PHPUnit\Metadata\CoversDefaultClass { - if ($this->coverageText === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->coverageText; + return new \PHPUnit\Metadata\CoversDefaultClass(self::CLASS_LEVEL, $className); } - public function hasCoverageTextShowUncoveredFiles() : bool + public static function coversNothingOnClass(): \PHPUnit\Metadata\CoversNothing { - return $this->coverageTextShowUncoveredFiles !== null; + return new \PHPUnit\Metadata\CoversNothing(self::CLASS_LEVEL); + } + public static function coversNothingOnMethod(): \PHPUnit\Metadata\CoversNothing + { + return new \PHPUnit\Metadata\CoversNothing(self::METHOD_LEVEL); } /** - * @throws Exception + * @psalm-param class-string $className + * @psalm-param non-empty-string $methodName */ - public function coverageTextShowUncoveredFiles() : bool + public static function dataProvider(string $className, string $methodName): \PHPUnit\Metadata\DataProvider { - if ($this->coverageTextShowUncoveredFiles === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->coverageTextShowUncoveredFiles; + return new \PHPUnit\Metadata\DataProvider(self::METHOD_LEVEL, $className, $methodName); } - public function hasCoverageTextShowOnlySummary() : bool + /** + * @psalm-param class-string $className + */ + public static function dependsOnClass(string $className, bool $deepClone, bool $shallowClone): \PHPUnit\Metadata\DependsOnClass { - return $this->coverageTextShowOnlySummary !== null; + return new \PHPUnit\Metadata\DependsOnClass(self::METHOD_LEVEL, $className, $deepClone, $shallowClone); } /** - * @throws Exception + * @psalm-param class-string $className + * @psalm-param non-empty-string $methodName */ - public function coverageTextShowOnlySummary() : bool + public static function dependsOnMethod(string $className, string $methodName, bool $deepClone, bool $shallowClone): \PHPUnit\Metadata\DependsOnMethod { - if ($this->coverageTextShowOnlySummary === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->coverageTextShowOnlySummary; + return new \PHPUnit\Metadata\DependsOnMethod(self::METHOD_LEVEL, $className, $methodName, $deepClone, $shallowClone); } - public function hasCoverageXml() : bool + public static function doesNotPerformAssertionsOnClass(): \PHPUnit\Metadata\DoesNotPerformAssertions { - return $this->coverageXml !== null; + return new \PHPUnit\Metadata\DoesNotPerformAssertions(self::CLASS_LEVEL); } - /** - * @throws Exception - */ - public function coverageXml() : string + public static function doesNotPerformAssertionsOnMethod(): \PHPUnit\Metadata\DoesNotPerformAssertions { - if ($this->coverageXml === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->coverageXml; + return new \PHPUnit\Metadata\DoesNotPerformAssertions(self::METHOD_LEVEL); } - public function hasPathCoverage() : bool + /** + * @psalm-param non-empty-string $globalVariableName + */ + public static function excludeGlobalVariableFromBackupOnClass(string $globalVariableName): \PHPUnit\Metadata\ExcludeGlobalVariableFromBackup { - return $this->pathCoverage !== null; + return new \PHPUnit\Metadata\ExcludeGlobalVariableFromBackup(self::CLASS_LEVEL, $globalVariableName); } /** - * @throws Exception + * @psalm-param non-empty-string $globalVariableName */ - public function pathCoverage() : bool + public static function excludeGlobalVariableFromBackupOnMethod(string $globalVariableName): \PHPUnit\Metadata\ExcludeGlobalVariableFromBackup { - if ($this->pathCoverage === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->pathCoverage; + return new \PHPUnit\Metadata\ExcludeGlobalVariableFromBackup(self::METHOD_LEVEL, $globalVariableName); } - public function hasCoverageCacheDirectory() : bool + /** + * @psalm-param class-string $className + * @psalm-param non-empty-string $propertyName + */ + public static function excludeStaticPropertyFromBackupOnClass(string $className, string $propertyName): \PHPUnit\Metadata\ExcludeStaticPropertyFromBackup { - return $this->coverageCacheDirectory !== null; + return new \PHPUnit\Metadata\ExcludeStaticPropertyFromBackup(self::CLASS_LEVEL, $className, $propertyName); } /** - * @throws Exception + * @psalm-param class-string $className + * @psalm-param non-empty-string $propertyName */ - public function coverageCacheDirectory() : string + public static function excludeStaticPropertyFromBackupOnMethod(string $className, string $propertyName): \PHPUnit\Metadata\ExcludeStaticPropertyFromBackup { - if ($this->coverageCacheDirectory === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->coverageCacheDirectory; + return new \PHPUnit\Metadata\ExcludeStaticPropertyFromBackup(self::METHOD_LEVEL, $className, $propertyName); } - public function hasWarmCoverageCache() : bool + /** + * @psalm-param non-empty-string $groupName + */ + public static function groupOnClass(string $groupName): \PHPUnit\Metadata\Group { - return $this->warmCoverageCache !== null; + return new \PHPUnit\Metadata\Group(self::CLASS_LEVEL, $groupName); } /** - * @throws Exception + * @psalm-param non-empty-string $groupName */ - public function warmCoverageCache() : bool + public static function groupOnMethod(string $groupName): \PHPUnit\Metadata\Group { - if ($this->warmCoverageCache === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->warmCoverageCache; + return new \PHPUnit\Metadata\Group(self::METHOD_LEVEL, $groupName); + } + public static function ignoreDeprecationsOnClass(): \PHPUnit\Metadata\IgnoreDeprecations + { + return new \PHPUnit\Metadata\IgnoreDeprecations(self::CLASS_LEVEL); } - public function hasDebug() : bool + public static function ignoreDeprecationsOnMethod(): \PHPUnit\Metadata\IgnoreDeprecations { - return $this->debug !== null; + return new \PHPUnit\Metadata\IgnoreDeprecations(self::METHOD_LEVEL); } /** - * @throws Exception + * @psalm-param class-string $className */ - public function debug() : bool + public static function ignoreClassForCodeCoverage(string $className): \PHPUnit\Metadata\IgnoreClassForCodeCoverage { - if ($this->debug === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->debug; + return new \PHPUnit\Metadata\IgnoreClassForCodeCoverage(self::CLASS_LEVEL, $className); } - public function hasDefaultTimeLimit() : bool + /** + * @psalm-param class-string $className + * @psalm-param non-empty-string $methodName + */ + public static function ignoreMethodForCodeCoverage(string $className, string $methodName): \PHPUnit\Metadata\IgnoreMethodForCodeCoverage { - return $this->defaultTimeLimit !== null; + return new \PHPUnit\Metadata\IgnoreMethodForCodeCoverage(self::CLASS_LEVEL, $className, $methodName); } /** - * @throws Exception + * @psalm-param non-empty-string $functionName */ - public function defaultTimeLimit() : int + public static function ignoreFunctionForCodeCoverage(string $functionName): \PHPUnit\Metadata\IgnoreFunctionForCodeCoverage { - if ($this->defaultTimeLimit === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->defaultTimeLimit; + return new \PHPUnit\Metadata\IgnoreFunctionForCodeCoverage(self::CLASS_LEVEL, $functionName); } - public function hasDisableCodeCoverageIgnore() : bool + public static function postCondition(): \PHPUnit\Metadata\PostCondition { - return $this->disableCodeCoverageIgnore !== null; + return new \PHPUnit\Metadata\PostCondition(self::METHOD_LEVEL); } - /** - * @throws Exception - */ - public function disableCodeCoverageIgnore() : bool + public static function preCondition(): \PHPUnit\Metadata\PreCondition { - if ($this->disableCodeCoverageIgnore === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->disableCodeCoverageIgnore; + return new \PHPUnit\Metadata\PreCondition(self::METHOD_LEVEL); } - public function hasDisallowTestOutput() : bool + public static function preserveGlobalStateOnClass(bool $enabled): \PHPUnit\Metadata\PreserveGlobalState { - return $this->disallowTestOutput !== null; + return new \PHPUnit\Metadata\PreserveGlobalState(self::CLASS_LEVEL, $enabled); } - /** - * @throws Exception - */ - public function disallowTestOutput() : bool + public static function preserveGlobalStateOnMethod(bool $enabled): \PHPUnit\Metadata\PreserveGlobalState { - if ($this->disallowTestOutput === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->disallowTestOutput; + return new \PHPUnit\Metadata\PreserveGlobalState(self::METHOD_LEVEL, $enabled); } - public function hasDisallowTodoAnnotatedTests() : bool + /** + * @psalm-param non-empty-string $functionName + */ + public static function requiresFunctionOnClass(string $functionName): \PHPUnit\Metadata\RequiresFunction { - return $this->disallowTodoAnnotatedTests !== null; + return new \PHPUnit\Metadata\RequiresFunction(self::CLASS_LEVEL, $functionName); } /** - * @throws Exception + * @psalm-param non-empty-string $functionName */ - public function disallowTodoAnnotatedTests() : bool + public static function requiresFunctionOnMethod(string $functionName): \PHPUnit\Metadata\RequiresFunction { - if ($this->disallowTodoAnnotatedTests === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->disallowTodoAnnotatedTests; + return new \PHPUnit\Metadata\RequiresFunction(self::METHOD_LEVEL, $functionName); } - public function hasEnforceTimeLimit() : bool + /** + * @psalm-param class-string $className + * @psalm-param non-empty-string $methodName + */ + public static function requiresMethodOnClass(string $className, string $methodName): \PHPUnit\Metadata\RequiresMethod { - return $this->enforceTimeLimit !== null; + return new \PHPUnit\Metadata\RequiresMethod(self::CLASS_LEVEL, $className, $methodName); } /** - * @throws Exception + * @psalm-param class-string $className + * @psalm-param non-empty-string $methodName */ - public function enforceTimeLimit() : bool + public static function requiresMethodOnMethod(string $className, string $methodName): \PHPUnit\Metadata\RequiresMethod { - if ($this->enforceTimeLimit === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->enforceTimeLimit; + return new \PHPUnit\Metadata\RequiresMethod(self::METHOD_LEVEL, $className, $methodName); } - public function hasExcludeGroups() : bool + /** + * @psalm-param non-empty-string $operatingSystem + */ + public static function requiresOperatingSystemOnClass(string $operatingSystem): \PHPUnit\Metadata\RequiresOperatingSystem { - return $this->excludeGroups !== null; + return new \PHPUnit\Metadata\RequiresOperatingSystem(self::CLASS_LEVEL, $operatingSystem); } /** - * @throws Exception + * @psalm-param non-empty-string $operatingSystem */ - public function excludeGroups() : array + public static function requiresOperatingSystemOnMethod(string $operatingSystem): \PHPUnit\Metadata\RequiresOperatingSystem { - if ($this->excludeGroups === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->excludeGroups; + return new \PHPUnit\Metadata\RequiresOperatingSystem(self::METHOD_LEVEL, $operatingSystem); } - public function hasExecutionOrder() : bool + /** + * @psalm-param non-empty-string $operatingSystemFamily + */ + public static function requiresOperatingSystemFamilyOnClass(string $operatingSystemFamily): \PHPUnit\Metadata\RequiresOperatingSystemFamily { - return $this->executionOrder !== null; + return new \PHPUnit\Metadata\RequiresOperatingSystemFamily(self::CLASS_LEVEL, $operatingSystemFamily); } /** - * @throws Exception + * @psalm-param non-empty-string $operatingSystemFamily */ - public function executionOrder() : int + public static function requiresOperatingSystemFamilyOnMethod(string $operatingSystemFamily): \PHPUnit\Metadata\RequiresOperatingSystemFamily { - if ($this->executionOrder === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->executionOrder; + return new \PHPUnit\Metadata\RequiresOperatingSystemFamily(self::METHOD_LEVEL, $operatingSystemFamily); } - public function hasExecutionOrderDefects() : bool + public static function requiresPhpOnClass(Requirement $versionRequirement): \PHPUnit\Metadata\RequiresPhp { - return $this->executionOrderDefects !== null; + return new \PHPUnit\Metadata\RequiresPhp(self::CLASS_LEVEL, $versionRequirement); } - /** - * @throws Exception - */ - public function executionOrderDefects() : int + public static function requiresPhpOnMethod(Requirement $versionRequirement): \PHPUnit\Metadata\RequiresPhp { - if ($this->executionOrderDefects === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->executionOrderDefects; + return new \PHPUnit\Metadata\RequiresPhp(self::METHOD_LEVEL, $versionRequirement); } - public function hasFailOnEmptyTestSuite() : bool + /** + * @psalm-param non-empty-string $extension + */ + public static function requiresPhpExtensionOnClass(string $extension, ?Requirement $versionRequirement): \PHPUnit\Metadata\RequiresPhpExtension { - return $this->failOnEmptyTestSuite !== null; + return new \PHPUnit\Metadata\RequiresPhpExtension(self::CLASS_LEVEL, $extension, $versionRequirement); } /** - * @throws Exception + * @psalm-param non-empty-string $extension */ - public function failOnEmptyTestSuite() : bool + public static function requiresPhpExtensionOnMethod(string $extension, ?Requirement $versionRequirement): \PHPUnit\Metadata\RequiresPhpExtension { - if ($this->failOnEmptyTestSuite === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->failOnEmptyTestSuite; + return new \PHPUnit\Metadata\RequiresPhpExtension(self::METHOD_LEVEL, $extension, $versionRequirement); } - public function hasFailOnIncomplete() : bool + public static function requiresPhpunitOnClass(Requirement $versionRequirement): \PHPUnit\Metadata\RequiresPhpunit { - return $this->failOnIncomplete !== null; + return new \PHPUnit\Metadata\RequiresPhpunit(self::CLASS_LEVEL, $versionRequirement); } - /** - * @throws Exception - */ - public function failOnIncomplete() : bool + public static function requiresPhpunitOnMethod(Requirement $versionRequirement): \PHPUnit\Metadata\RequiresPhpunit { - if ($this->failOnIncomplete === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->failOnIncomplete; + return new \PHPUnit\Metadata\RequiresPhpunit(self::METHOD_LEVEL, $versionRequirement); } - public function hasFailOnRisky() : bool + /** + * @psalm-param non-empty-string $setting + * @psalm-param non-empty-string $value + */ + public static function requiresSettingOnClass(string $setting, string $value): \PHPUnit\Metadata\RequiresSetting { - return $this->failOnRisky !== null; + return new \PHPUnit\Metadata\RequiresSetting(self::CLASS_LEVEL, $setting, $value); } /** - * @throws Exception + * @psalm-param non-empty-string $setting + * @psalm-param non-empty-string $value */ - public function failOnRisky() : bool + public static function requiresSettingOnMethod(string $setting, string $value): \PHPUnit\Metadata\RequiresSetting { - if ($this->failOnRisky === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->failOnRisky; + return new \PHPUnit\Metadata\RequiresSetting(self::METHOD_LEVEL, $setting, $value); } - public function hasFailOnSkipped() : bool + public static function runClassInSeparateProcess(): \PHPUnit\Metadata\RunClassInSeparateProcess { - return $this->failOnSkipped !== null; + return new \PHPUnit\Metadata\RunClassInSeparateProcess(self::CLASS_LEVEL); } - /** - * @throws Exception - */ - public function failOnSkipped() : bool + public static function runTestsInSeparateProcesses(): \PHPUnit\Metadata\RunTestsInSeparateProcesses { - if ($this->failOnSkipped === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->failOnSkipped; + return new \PHPUnit\Metadata\RunTestsInSeparateProcesses(self::CLASS_LEVEL); } - public function hasFailOnWarning() : bool + public static function runInSeparateProcess(): \PHPUnit\Metadata\RunInSeparateProcess { - return $this->failOnWarning !== null; + return new \PHPUnit\Metadata\RunInSeparateProcess(self::METHOD_LEVEL); } - /** - * @throws Exception - */ - public function failOnWarning() : bool + public static function test(): \PHPUnit\Metadata\Test { - if ($this->failOnWarning === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->failOnWarning; + return new \PHPUnit\Metadata\Test(self::METHOD_LEVEL); } - public function hasFilter() : bool + /** + * @psalm-param non-empty-string $text + */ + public static function testDoxOnClass(string $text): \PHPUnit\Metadata\TestDox { - return $this->filter !== null; + return new \PHPUnit\Metadata\TestDox(self::CLASS_LEVEL, $text); } /** - * @throws Exception + * @psalm-param non-empty-string $text */ - public function filter() : string + public static function testDoxOnMethod(string $text): \PHPUnit\Metadata\TestDox { - if ($this->filter === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->filter; + return new \PHPUnit\Metadata\TestDox(self::METHOD_LEVEL, $text); } - public function hasGenerateConfiguration() : bool + public static function testWith(array $data): \PHPUnit\Metadata\TestWith { - return $this->generateConfiguration !== null; + return new \PHPUnit\Metadata\TestWith(self::METHOD_LEVEL, $data); } /** - * @throws Exception + * @psalm-param class-string $className */ - public function generateConfiguration() : bool + public static function usesClass(string $className): \PHPUnit\Metadata\UsesClass { - if ($this->generateConfiguration === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->generateConfiguration; + return new \PHPUnit\Metadata\UsesClass(self::CLASS_LEVEL, $className); } - public function hasMigrateConfiguration() : bool + /** + * @psalm-param non-empty-string $functionName + */ + public static function usesFunction(string $functionName): \PHPUnit\Metadata\UsesFunction { - return $this->migrateConfiguration !== null; + return new \PHPUnit\Metadata\UsesFunction(self::CLASS_LEVEL, $functionName); } /** - * @throws Exception + * @psalm-param non-empty-string $target */ - public function migrateConfiguration() : bool + public static function usesOnClass(string $target): \PHPUnit\Metadata\Uses { - if ($this->migrateConfiguration === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->migrateConfiguration; + return new \PHPUnit\Metadata\Uses(self::CLASS_LEVEL, $target); } - public function hasGroups() : bool + /** + * @psalm-param non-empty-string $target + */ + public static function usesOnMethod(string $target): \PHPUnit\Metadata\Uses { - return $this->groups !== null; + return new \PHPUnit\Metadata\Uses(self::METHOD_LEVEL, $target); } /** - * @throws Exception + * @psalm-param class-string $className */ - public function groups() : array + public static function usesDefaultClass(string $className): \PHPUnit\Metadata\UsesDefaultClass { - if ($this->groups === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->groups; + return new \PHPUnit\Metadata\UsesDefaultClass(self::CLASS_LEVEL, $className); } - public function hasTestsCovering() : bool + public static function withoutErrorHandler(): \PHPUnit\Metadata\WithoutErrorHandler { - return $this->testsCovering !== null; + return new \PHPUnit\Metadata\WithoutErrorHandler(self::METHOD_LEVEL); } /** - * @throws Exception + * @psalm-param 0|1 $level */ - public function testsCovering() : array + protected function __construct(int $level) { - if ($this->testsCovering === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->testsCovering; + $this->level = $level; } - public function hasTestsUsing() : bool + public function isClassLevel(): bool { - return $this->testsUsing !== null; + return $this->level === self::CLASS_LEVEL; + } + public function isMethodLevel(): bool + { + return $this->level === self::METHOD_LEVEL; } /** - * @throws Exception + * @psalm-assert-if-true After $this */ - public function testsUsing() : array + public function isAfter(): bool { - if ($this->testsUsing === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->testsUsing; + return \false; } - public function hasHelp() : bool + /** + * @psalm-assert-if-true AfterClass $this + */ + public function isAfterClass(): bool { - return $this->help !== null; + return \false; } /** - * @throws Exception + * @psalm-assert-if-true BackupGlobals $this */ - public function help() : bool + public function isBackupGlobals(): bool { - if ($this->help === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->help; + return \false; } - public function hasIncludePath() : bool + /** + * @psalm-assert-if-true BackupStaticProperties $this + */ + public function isBackupStaticProperties(): bool { - return $this->includePath !== null; + return \false; } /** - * @throws Exception + * @psalm-assert-if-true BeforeClass $this */ - public function includePath() : string + public function isBeforeClass(): bool { - if ($this->includePath === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->includePath; + return \false; } - public function hasIniSettings() : bool + /** + * @psalm-assert-if-true Before $this + */ + public function isBefore(): bool { - return $this->iniSettings !== null; + return \false; } /** - * @throws Exception + * @psalm-assert-if-true Covers $this */ - public function iniSettings() : array + public function isCovers(): bool { - if ($this->iniSettings === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->iniSettings; + return \false; } - public function hasJunitLogfile() : bool + /** + * @psalm-assert-if-true CoversClass $this + */ + public function isCoversClass(): bool { - return $this->junitLogfile !== null; + return \false; } /** - * @throws Exception + * @psalm-assert-if-true CoversDefaultClass $this */ - public function junitLogfile() : string + public function isCoversDefaultClass(): bool { - if ($this->junitLogfile === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->junitLogfile; + return \false; } - public function hasListGroups() : bool + /** + * @psalm-assert-if-true CoversFunction $this + */ + public function isCoversFunction(): bool { - return $this->listGroups !== null; + return \false; } /** - * @throws Exception + * @psalm-assert-if-true CoversNothing $this */ - public function listGroups() : bool + public function isCoversNothing(): bool { - if ($this->listGroups === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->listGroups; + return \false; } - public function hasListSuites() : bool + /** + * @psalm-assert-if-true DataProvider $this + */ + public function isDataProvider(): bool { - return $this->listSuites !== null; + return \false; } /** - * @throws Exception + * @psalm-assert-if-true DependsOnClass $this */ - public function listSuites() : bool + public function isDependsOnClass(): bool { - if ($this->listSuites === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->listSuites; + return \false; } - public function hasListTests() : bool + /** + * @psalm-assert-if-true DependsOnMethod $this + */ + public function isDependsOnMethod(): bool { - return $this->listTests !== null; + return \false; } /** - * @throws Exception + * @psalm-assert-if-true DoesNotPerformAssertions $this */ - public function listTests() : bool + public function isDoesNotPerformAssertions(): bool { - if ($this->listTests === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->listTests; + return \false; } - public function hasListTestsXml() : bool + /** + * @psalm-assert-if-true ExcludeGlobalVariableFromBackup $this + */ + public function isExcludeGlobalVariableFromBackup(): bool { - return $this->listTestsXml !== null; + return \false; } /** - * @throws Exception + * @psalm-assert-if-true ExcludeStaticPropertyFromBackup $this */ - public function listTestsXml() : string + public function isExcludeStaticPropertyFromBackup(): bool { - if ($this->listTestsXml === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->listTestsXml; + return \false; } - public function hasLoader() : bool + /** + * @psalm-assert-if-true Group $this + */ + public function isGroup(): bool { - return $this->loader !== null; + return \false; } /** - * @throws Exception + * @psalm-assert-if-true IgnoreDeprecations $this */ - public function loader() : string + public function isIgnoreDeprecations(): bool { - if ($this->loader === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->loader; + return \false; } - public function hasNoCoverage() : bool + /** + * @psalm-assert-if-true IgnoreClassForCodeCoverage $this + */ + public function isIgnoreClassForCodeCoverage(): bool { - return $this->noCoverage !== null; + return \false; } /** - * @throws Exception + * @psalm-assert-if-true IgnoreMethodForCodeCoverage $this */ - public function noCoverage() : bool + public function isIgnoreMethodForCodeCoverage(): bool { - if ($this->noCoverage === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->noCoverage; + return \false; } - public function hasNoExtensions() : bool + /** + * @psalm-assert-if-true IgnoreFunctionForCodeCoverage $this + */ + public function isIgnoreFunctionForCodeCoverage(): bool { - return $this->noExtensions !== null; + return \false; } /** - * @throws Exception + * @psalm-assert-if-true RunClassInSeparateProcess $this */ - public function noExtensions() : bool + public function isRunClassInSeparateProcess(): bool { - if ($this->noExtensions === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->noExtensions; + return \false; } - public function hasExtensions() : bool + /** + * @psalm-assert-if-true RunInSeparateProcess $this + */ + public function isRunInSeparateProcess(): bool { - return $this->extensions !== null; + return \false; } /** - * @throws Exception + * @psalm-assert-if-true RunTestsInSeparateProcesses $this */ - public function extensions() : array + public function isRunTestsInSeparateProcesses(): bool { - if ($this->extensions === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->extensions; + return \false; } - public function hasUnavailableExtensions() : bool + /** + * @psalm-assert-if-true Test $this + */ + public function isTest(): bool { - return $this->unavailableExtensions !== null; + return \false; } /** - * @throws Exception + * @psalm-assert-if-true PreCondition $this */ - public function unavailableExtensions() : array + public function isPreCondition(): bool { - if ($this->unavailableExtensions === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->unavailableExtensions; + return \false; } - public function hasNoInteraction() : bool + /** + * @psalm-assert-if-true PostCondition $this + */ + public function isPostCondition(): bool { - return $this->noInteraction !== null; + return \false; } /** - * @throws Exception + * @psalm-assert-if-true PreserveGlobalState $this */ - public function noInteraction() : bool + public function isPreserveGlobalState(): bool { - if ($this->noInteraction === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->noInteraction; + return \false; } - public function hasNoLogging() : bool + /** + * @psalm-assert-if-true RequiresMethod $this + */ + public function isRequiresMethod(): bool { - return $this->noLogging !== null; + return \false; } /** - * @throws Exception + * @psalm-assert-if-true RequiresFunction $this */ - public function noLogging() : bool + public function isRequiresFunction(): bool { - if ($this->noLogging === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->noLogging; + return \false; } - public function hasPrinter() : bool + /** + * @psalm-assert-if-true RequiresOperatingSystem $this + */ + public function isRequiresOperatingSystem(): bool { - return $this->printer !== null; + return \false; } /** - * @throws Exception + * @psalm-assert-if-true RequiresOperatingSystemFamily $this */ - public function printer() : string + public function isRequiresOperatingSystemFamily(): bool { - if ($this->printer === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->printer; + return \false; } - public function hasProcessIsolation() : bool + /** + * @psalm-assert-if-true RequiresPhp $this + */ + public function isRequiresPhp(): bool { - return $this->processIsolation !== null; + return \false; } /** - * @throws Exception + * @psalm-assert-if-true RequiresPhpExtension $this */ - public function processIsolation() : bool + public function isRequiresPhpExtension(): bool { - if ($this->processIsolation === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->processIsolation; + return \false; } - public function hasRandomOrderSeed() : bool + /** + * @psalm-assert-if-true RequiresPhpunit $this + */ + public function isRequiresPhpunit(): bool { - return $this->randomOrderSeed !== null; + return \false; } /** - * @throws Exception + * @psalm-assert-if-true RequiresSetting $this */ - public function randomOrderSeed() : int + public function isRequiresSetting(): bool { - if ($this->randomOrderSeed === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->randomOrderSeed; + return \false; } - public function hasRepeat() : bool + /** + * @psalm-assert-if-true TestDox $this + */ + public function isTestDox(): bool { - return $this->repeat !== null; + return \false; } /** - * @throws Exception + * @psalm-assert-if-true TestWith $this */ - public function repeat() : int + public function isTestWith(): bool { - if ($this->repeat === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->repeat; + return \false; } - public function hasReportUselessTests() : bool + /** + * @psalm-assert-if-true Uses $this + */ + public function isUses(): bool { - return $this->reportUselessTests !== null; + return \false; } /** - * @throws Exception + * @psalm-assert-if-true UsesClass $this */ - public function reportUselessTests() : bool + public function isUsesClass(): bool { - if ($this->reportUselessTests === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->reportUselessTests; + return \false; } - public function hasResolveDependencies() : bool + /** + * @psalm-assert-if-true UsesDefaultClass $this + */ + public function isUsesDefaultClass(): bool { - return $this->resolveDependencies !== null; + return \false; } /** - * @throws Exception + * @psalm-assert-if-true UsesFunction $this */ - public function resolveDependencies() : bool + public function isUsesFunction(): bool { - if ($this->resolveDependencies === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->resolveDependencies; + return \false; } - public function hasReverseList() : bool + /** + * @psalm-assert-if-true WithoutErrorHandler $this + */ + public function isWithoutErrorHandler(): bool { - return $this->reverseList !== null; + return \false; } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Metadata; + +use function array_filter; +use function array_merge; +use function count; +use Countable; +use IteratorAggregate; +/** + * @template-implements IteratorAggregate + * + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class MetadataCollection implements Countable, IteratorAggregate +{ /** - * @throws Exception + * @psalm-var list + */ + private readonly array $metadata; + /** + * @psalm-param list $metadata */ - public function reverseList() : bool + public static function fromArray(array $metadata): self { - if ($this->reverseList === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->reverseList; + return new self(...$metadata); } - public function hasStderr() : bool + private function __construct(\PHPUnit\Metadata\Metadata ...$metadata) { - return $this->stderr !== null; + $this->metadata = $metadata; } /** - * @throws Exception + * @psalm-return list */ - public function stderr() : bool + public function asArray(): array { - if ($this->stderr === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->stderr; + return $this->metadata; } - public function hasStrictCoverage() : bool + public function count(): int { - return $this->strictCoverage !== null; + return count($this->metadata); } - /** - * @throws Exception - */ - public function strictCoverage() : bool + public function isEmpty(): bool { - if ($this->strictCoverage === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->strictCoverage; + return $this->count() === 0; } - public function hasStopOnDefect() : bool + public function isNotEmpty(): bool { - return $this->stopOnDefect !== null; + return $this->count() > 0; } - /** - * @throws Exception - */ - public function stopOnDefect() : bool + public function getIterator(): \PHPUnit\Metadata\MetadataCollectionIterator { - if ($this->stopOnDefect === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->stopOnDefect; + return new \PHPUnit\Metadata\MetadataCollectionIterator($this); } - public function hasStopOnError() : bool + public function mergeWith(self $other): self { - return $this->stopOnError !== null; + return new self(...array_merge($this->asArray(), $other->asArray())); } - /** - * @throws Exception - */ - public function stopOnError() : bool + public function isClassLevel(): self { - if ($this->stopOnError === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->stopOnError; + return new self(...array_filter($this->metadata, static fn(\PHPUnit\Metadata\Metadata $metadata): bool => $metadata->isClassLevel())); } - public function hasStopOnFailure() : bool + public function isMethodLevel(): self { - return $this->stopOnFailure !== null; + return new self(...array_filter($this->metadata, static fn(\PHPUnit\Metadata\Metadata $metadata): bool => $metadata->isMethodLevel())); } - /** - * @throws Exception - */ - public function stopOnFailure() : bool + public function isAfter(): self { - if ($this->stopOnFailure === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->stopOnFailure; + return new self(...array_filter($this->metadata, static fn(\PHPUnit\Metadata\Metadata $metadata): bool => $metadata->isAfter())); } - public function hasStopOnIncomplete() : bool + public function isAfterClass(): self { - return $this->stopOnIncomplete !== null; + return new self(...array_filter($this->metadata, static fn(\PHPUnit\Metadata\Metadata $metadata): bool => $metadata->isAfterClass())); } - /** - * @throws Exception - */ - public function stopOnIncomplete() : bool + public function isBackupGlobals(): self { - if ($this->stopOnIncomplete === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->stopOnIncomplete; + return new self(...array_filter($this->metadata, static fn(\PHPUnit\Metadata\Metadata $metadata): bool => $metadata->isBackupGlobals())); } - public function hasStopOnRisky() : bool + public function isBackupStaticProperties(): self { - return $this->stopOnRisky !== null; + return new self(...array_filter($this->metadata, static fn(\PHPUnit\Metadata\Metadata $metadata): bool => $metadata->isBackupStaticProperties())); } - /** - * @throws Exception - */ - public function stopOnRisky() : bool + public function isBeforeClass(): self { - if ($this->stopOnRisky === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->stopOnRisky; + return new self(...array_filter($this->metadata, static fn(\PHPUnit\Metadata\Metadata $metadata): bool => $metadata->isBeforeClass())); } - public function hasStopOnSkipped() : bool + public function isBefore(): self { - return $this->stopOnSkipped !== null; + return new self(...array_filter($this->metadata, static fn(\PHPUnit\Metadata\Metadata $metadata): bool => $metadata->isBefore())); } - /** - * @throws Exception - */ - public function stopOnSkipped() : bool + public function isCovers(): self { - if ($this->stopOnSkipped === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->stopOnSkipped; + return new self(...array_filter($this->metadata, static fn(\PHPUnit\Metadata\Metadata $metadata): bool => $metadata->isCovers())); } - public function hasStopOnWarning() : bool + public function isCoversClass(): self { - return $this->stopOnWarning !== null; + return new self(...array_filter($this->metadata, static fn(\PHPUnit\Metadata\Metadata $metadata): bool => $metadata->isCoversClass())); } - /** - * @throws Exception - */ - public function stopOnWarning() : bool + public function isCoversDefaultClass(): self { - if ($this->stopOnWarning === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->stopOnWarning; + return new self(...array_filter($this->metadata, static fn(\PHPUnit\Metadata\Metadata $metadata): bool => $metadata->isCoversDefaultClass())); } - public function hasTeamcityLogfile() : bool + public function isCoversFunction(): self { - return $this->teamcityLogfile !== null; + return new self(...array_filter($this->metadata, static fn(\PHPUnit\Metadata\Metadata $metadata): bool => $metadata->isCoversFunction())); } - /** - * @throws Exception - */ - public function teamcityLogfile() : string + public function isExcludeGlobalVariableFromBackup(): self { - if ($this->teamcityLogfile === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->teamcityLogfile; + return new self(...array_filter($this->metadata, static fn(\PHPUnit\Metadata\Metadata $metadata): bool => $metadata->isExcludeGlobalVariableFromBackup())); } - public function hasTestdoxExcludeGroups() : bool + public function isExcludeStaticPropertyFromBackup(): self { - return $this->testdoxExcludeGroups !== null; + return new self(...array_filter($this->metadata, static fn(\PHPUnit\Metadata\Metadata $metadata): bool => $metadata->isExcludeStaticPropertyFromBackup())); } - /** - * @throws Exception - */ - public function testdoxExcludeGroups() : array + public function isCoversNothing(): self { - if ($this->testdoxExcludeGroups === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->testdoxExcludeGroups; + return new self(...array_filter($this->metadata, static fn(\PHPUnit\Metadata\Metadata $metadata): bool => $metadata->isCoversNothing())); } - public function hasTestdoxGroups() : bool + public function isDataProvider(): self { - return $this->testdoxGroups !== null; + return new self(...array_filter($this->metadata, static fn(\PHPUnit\Metadata\Metadata $metadata): bool => $metadata->isDataProvider())); } - /** - * @throws Exception - */ - public function testdoxGroups() : array + public function isDepends(): self { - if ($this->testdoxGroups === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->testdoxGroups; + return new self(...array_filter($this->metadata, static fn(\PHPUnit\Metadata\Metadata $metadata): bool => $metadata->isDependsOnClass() || $metadata->isDependsOnMethod())); } - public function hasTestdoxHtmlFile() : bool + public function isDependsOnClass(): self { - return $this->testdoxHtmlFile !== null; + return new self(...array_filter($this->metadata, static fn(\PHPUnit\Metadata\Metadata $metadata): bool => $metadata->isDependsOnClass())); } - /** - * @throws Exception - */ - public function testdoxHtmlFile() : string + public function isDependsOnMethod(): self { - if ($this->testdoxHtmlFile === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->testdoxHtmlFile; + return new self(...array_filter($this->metadata, static fn(\PHPUnit\Metadata\Metadata $metadata): bool => $metadata->isDependsOnMethod())); } - public function hasTestdoxTextFile() : bool + public function isDoesNotPerformAssertions(): self { - return $this->testdoxTextFile !== null; + return new self(...array_filter($this->metadata, static fn(\PHPUnit\Metadata\Metadata $metadata): bool => $metadata->isDoesNotPerformAssertions())); } - /** - * @throws Exception - */ - public function testdoxTextFile() : string + public function isGroup(): self { - if ($this->testdoxTextFile === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->testdoxTextFile; + return new self(...array_filter($this->metadata, static fn(\PHPUnit\Metadata\Metadata $metadata): bool => $metadata->isGroup())); } - public function hasTestdoxXmlFile() : bool + public function isIgnoreDeprecations(): self { - return $this->testdoxXmlFile !== null; + return new self(...array_filter($this->metadata, static fn(\PHPUnit\Metadata\Metadata $metadata): bool => $metadata->isIgnoreDeprecations())); } /** - * @throws Exception + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/5513 */ - public function testdoxXmlFile() : string + public function isIgnoreClassForCodeCoverage(): self { - if ($this->testdoxXmlFile === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->testdoxXmlFile; + return new self(...array_filter($this->metadata, static fn(\PHPUnit\Metadata\Metadata $metadata): bool => $metadata->isIgnoreClassForCodeCoverage())); } - public function hasTestSuffixes() : bool + /** + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/5513 + */ + public function isIgnoreMethodForCodeCoverage(): self { - return $this->testSuffixes !== null; + return new self(...array_filter($this->metadata, static fn(\PHPUnit\Metadata\Metadata $metadata): bool => $metadata->isIgnoreMethodForCodeCoverage())); } /** - * @throws Exception + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/5513 */ - public function testSuffixes() : array + public function isIgnoreFunctionForCodeCoverage(): self { - if ($this->testSuffixes === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->testSuffixes; + return new self(...array_filter($this->metadata, static fn(\PHPUnit\Metadata\Metadata $metadata): bool => $metadata->isIgnoreFunctionForCodeCoverage())); } - public function hasTestSuite() : bool + public function isRunClassInSeparateProcess(): self { - return $this->testSuite !== null; + return new self(...array_filter($this->metadata, static fn(\PHPUnit\Metadata\Metadata $metadata): bool => $metadata->isRunClassInSeparateProcess())); } - /** - * @throws Exception - */ - public function testSuite() : string + public function isRunInSeparateProcess(): self { - if ($this->testSuite === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->testSuite; + return new self(...array_filter($this->metadata, static fn(\PHPUnit\Metadata\Metadata $metadata): bool => $metadata->isRunInSeparateProcess())); } - public function unrecognizedOptions() : array + public function isRunTestsInSeparateProcesses(): self { - return $this->unrecognizedOptions; + return new self(...array_filter($this->metadata, static fn(\PHPUnit\Metadata\Metadata $metadata): bool => $metadata->isRunTestsInSeparateProcesses())); } - public function hasUnrecognizedOrderBy() : bool + public function isTest(): self { - return $this->unrecognizedOrderBy !== null; + return new self(...array_filter($this->metadata, static fn(\PHPUnit\Metadata\Metadata $metadata): bool => $metadata->isTest())); } - /** - * @throws Exception - */ - public function unrecognizedOrderBy() : string + public function isPreCondition(): self { - if ($this->unrecognizedOrderBy === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->unrecognizedOrderBy; + return new self(...array_filter($this->metadata, static fn(\PHPUnit\Metadata\Metadata $metadata): bool => $metadata->isPreCondition())); } - public function hasUseDefaultConfiguration() : bool + public function isPostCondition(): self { - return $this->useDefaultConfiguration !== null; + return new self(...array_filter($this->metadata, static fn(\PHPUnit\Metadata\Metadata $metadata): bool => $metadata->isPostCondition())); } - /** - * @throws Exception - */ - public function useDefaultConfiguration() : bool + public function isPreserveGlobalState(): self { - if ($this->useDefaultConfiguration === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->useDefaultConfiguration; + return new self(...array_filter($this->metadata, static fn(\PHPUnit\Metadata\Metadata $metadata): bool => $metadata->isPreserveGlobalState())); } - public function hasVerbose() : bool + public function isRequiresMethod(): self { - return $this->verbose !== null; + return new self(...array_filter($this->metadata, static fn(\PHPUnit\Metadata\Metadata $metadata): bool => $metadata->isRequiresMethod())); } - /** - * @throws Exception - */ - public function verbose() : bool + public function isRequiresFunction(): self { - if ($this->verbose === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->verbose; + return new self(...array_filter($this->metadata, static fn(\PHPUnit\Metadata\Metadata $metadata): bool => $metadata->isRequiresFunction())); } - public function hasVersion() : bool + public function isRequiresOperatingSystem(): self { - return $this->version !== null; + return new self(...array_filter($this->metadata, static fn(\PHPUnit\Metadata\Metadata $metadata): bool => $metadata->isRequiresOperatingSystem())); } - /** - * @throws Exception - */ - public function version() : bool + public function isRequiresOperatingSystemFamily(): self { - if ($this->version === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->version; + return new self(...array_filter($this->metadata, static fn(\PHPUnit\Metadata\Metadata $metadata): bool => $metadata->isRequiresOperatingSystemFamily())); } - public function hasXdebugFilterFile() : bool + public function isRequiresPhp(): self { - return $this->xdebugFilterFile !== null; + return new self(...array_filter($this->metadata, static fn(\PHPUnit\Metadata\Metadata $metadata): bool => $metadata->isRequiresPhp())); } - /** - * @throws Exception - */ - public function xdebugFilterFile() : string + public function isRequiresPhpExtension(): self { - if ($this->xdebugFilterFile === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->xdebugFilterFile; + return new self(...array_filter($this->metadata, static fn(\PHPUnit\Metadata\Metadata $metadata): bool => $metadata->isRequiresPhpExtension())); + } + public function isRequiresPhpunit(): self + { + return new self(...array_filter($this->metadata, static fn(\PHPUnit\Metadata\Metadata $metadata): bool => $metadata->isRequiresPhpunit())); + } + public function isRequiresSetting(): self + { + return new self(...array_filter($this->metadata, static fn(\PHPUnit\Metadata\Metadata $metadata): bool => $metadata->isRequiresSetting())); + } + public function isTestDox(): self + { + return new self(...array_filter($this->metadata, static fn(\PHPUnit\Metadata\Metadata $metadata): bool => $metadata->isTestDox())); + } + public function isTestWith(): self + { + return new self(...array_filter($this->metadata, static fn(\PHPUnit\Metadata\Metadata $metadata): bool => $metadata->isTestWith())); + } + public function isUses(): self + { + return new self(...array_filter($this->metadata, static fn(\PHPUnit\Metadata\Metadata $metadata): bool => $metadata->isUses())); + } + public function isUsesClass(): self + { + return new self(...array_filter($this->metadata, static fn(\PHPUnit\Metadata\Metadata $metadata): bool => $metadata->isUsesClass())); + } + public function isUsesDefaultClass(): self + { + return new self(...array_filter($this->metadata, static fn(\PHPUnit\Metadata\Metadata $metadata): bool => $metadata->isUsesDefaultClass())); + } + public function isUsesFunction(): self + { + return new self(...array_filter($this->metadata, static fn(\PHPUnit\Metadata\Metadata $metadata): bool => $metadata->isUsesFunction())); + } + public function isWithoutErrorHandler(): self + { + return new self(...array_filter($this->metadata, static fn(\PHPUnit\Metadata\Metadata $metadata): bool => $metadata->isWithoutErrorHandler())); } } * - * (c) Sebastian Bergmann - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TextUI\CliArguments; - -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -final class Mapper +final class MetadataCollectionIterator implements Iterator { /** - * @throws Exception + * @psalm-var list */ - public function mapToLegacyArray(\PHPUnit\TextUI\CliArguments\Configuration $arguments) : array + private readonly array $metadata; + private int $position = 0; + public function __construct(\PHPUnit\Metadata\MetadataCollection $metadata) { - $result = ['extensions' => [], 'listGroups' => \false, 'listSuites' => \false, 'listTests' => \false, 'listTestsXml' => \false, 'loader' => null, 'useDefaultConfiguration' => \true, 'loadedExtensions' => [], 'unavailableExtensions' => [], 'notLoadedExtensions' => []]; - if ($arguments->hasColors()) { - $result['colors'] = $arguments->colors(); - } - if ($arguments->hasBootstrap()) { - $result['bootstrap'] = $arguments->bootstrap(); - } - if ($arguments->hasCacheResult()) { - $result['cacheResult'] = $arguments->cacheResult(); - } - if ($arguments->hasCacheResultFile()) { - $result['cacheResultFile'] = $arguments->cacheResultFile(); - } - if ($arguments->hasColumns()) { - $result['columns'] = $arguments->columns(); - } - if ($arguments->hasConfiguration()) { - $result['configuration'] = $arguments->configuration(); - } - if ($arguments->hasCoverageCacheDirectory()) { - $result['coverageCacheDirectory'] = $arguments->coverageCacheDirectory(); - } - if ($arguments->hasWarmCoverageCache()) { - $result['warmCoverageCache'] = $arguments->warmCoverageCache(); - } - if ($arguments->hasCoverageClover()) { - $result['coverageClover'] = $arguments->coverageClover(); - } - if ($arguments->hasCoverageCobertura()) { - $result['coverageCobertura'] = $arguments->coverageCobertura(); - } - if ($arguments->hasCoverageCrap4J()) { - $result['coverageCrap4J'] = $arguments->coverageCrap4J(); - } - if ($arguments->hasCoverageHtml()) { - $result['coverageHtml'] = $arguments->coverageHtml(); - } - if ($arguments->hasCoveragePhp()) { - $result['coveragePHP'] = $arguments->coveragePhp(); - } - if ($arguments->hasCoverageText()) { - $result['coverageText'] = $arguments->coverageText(); - } - if ($arguments->hasCoverageTextShowUncoveredFiles()) { - $result['coverageTextShowUncoveredFiles'] = $arguments->hasCoverageTextShowUncoveredFiles(); - } - if ($arguments->hasCoverageTextShowOnlySummary()) { - $result['coverageTextShowOnlySummary'] = $arguments->coverageTextShowOnlySummary(); - } - if ($arguments->hasCoverageXml()) { - $result['coverageXml'] = $arguments->coverageXml(); - } - if ($arguments->hasPathCoverage()) { - $result['pathCoverage'] = $arguments->pathCoverage(); - } - if ($arguments->hasDebug()) { - $result['debug'] = $arguments->debug(); - } - if ($arguments->hasHelp()) { - $result['help'] = $arguments->help(); - } - if ($arguments->hasFilter()) { - $result['filter'] = $arguments->filter(); - } - if ($arguments->hasTestSuite()) { - $result['testsuite'] = $arguments->testSuite(); - } - if ($arguments->hasGroups()) { - $result['groups'] = $arguments->groups(); - } - if ($arguments->hasExcludeGroups()) { - $result['excludeGroups'] = $arguments->excludeGroups(); - } - if ($arguments->hasTestsCovering()) { - $result['testsCovering'] = $arguments->testsCovering(); - } - if ($arguments->hasTestsUsing()) { - $result['testsUsing'] = $arguments->testsUsing(); - } - if ($arguments->hasTestSuffixes()) { - $result['testSuffixes'] = $arguments->testSuffixes(); - } - if ($arguments->hasIncludePath()) { - $result['includePath'] = $arguments->includePath(); - } - if ($arguments->hasListGroups()) { - $result['listGroups'] = $arguments->listGroups(); - } - if ($arguments->hasListSuites()) { - $result['listSuites'] = $arguments->listSuites(); - } - if ($arguments->hasListTests()) { - $result['listTests'] = $arguments->listTests(); - } - if ($arguments->hasListTestsXml()) { - $result['listTestsXml'] = $arguments->listTestsXml(); - } - if ($arguments->hasPrinter()) { - $result['printer'] = $arguments->printer(); - } - if ($arguments->hasLoader()) { - $result['loader'] = $arguments->loader(); - } - if ($arguments->hasJunitLogfile()) { - $result['junitLogfile'] = $arguments->junitLogfile(); - } - if ($arguments->hasTeamcityLogfile()) { - $result['teamcityLogfile'] = $arguments->teamcityLogfile(); - } - if ($arguments->hasExecutionOrder()) { - $result['executionOrder'] = $arguments->executionOrder(); - } - if ($arguments->hasExecutionOrderDefects()) { - $result['executionOrderDefects'] = $arguments->executionOrderDefects(); - } - if ($arguments->hasExtensions()) { - $result['extensions'] = $arguments->extensions(); - } - if ($arguments->hasUnavailableExtensions()) { - $result['unavailableExtensions'] = $arguments->unavailableExtensions(); - } - if ($arguments->hasResolveDependencies()) { - $result['resolveDependencies'] = $arguments->resolveDependencies(); - } - if ($arguments->hasProcessIsolation()) { - $result['processIsolation'] = $arguments->processIsolation(); - } - if ($arguments->hasRepeat()) { - $result['repeat'] = $arguments->repeat(); - } - if ($arguments->hasStderr()) { - $result['stderr'] = $arguments->stderr(); - } - if ($arguments->hasStopOnDefect()) { - $result['stopOnDefect'] = $arguments->stopOnDefect(); - } - if ($arguments->hasStopOnError()) { - $result['stopOnError'] = $arguments->stopOnError(); - } - if ($arguments->hasStopOnFailure()) { - $result['stopOnFailure'] = $arguments->stopOnFailure(); - } - if ($arguments->hasStopOnWarning()) { - $result['stopOnWarning'] = $arguments->stopOnWarning(); - } - if ($arguments->hasStopOnIncomplete()) { - $result['stopOnIncomplete'] = $arguments->stopOnIncomplete(); - } - if ($arguments->hasStopOnRisky()) { - $result['stopOnRisky'] = $arguments->stopOnRisky(); - } - if ($arguments->hasStopOnSkipped()) { - $result['stopOnSkipped'] = $arguments->stopOnSkipped(); - } - if ($arguments->hasFailOnEmptyTestSuite()) { - $result['failOnEmptyTestSuite'] = $arguments->failOnEmptyTestSuite(); - } - if ($arguments->hasFailOnIncomplete()) { - $result['failOnIncomplete'] = $arguments->failOnIncomplete(); - } - if ($arguments->hasFailOnRisky()) { - $result['failOnRisky'] = $arguments->failOnRisky(); - } - if ($arguments->hasFailOnSkipped()) { - $result['failOnSkipped'] = $arguments->failOnSkipped(); - } - if ($arguments->hasFailOnWarning()) { - $result['failOnWarning'] = $arguments->failOnWarning(); - } - if ($arguments->hasTestdoxGroups()) { - $result['testdoxGroups'] = $arguments->testdoxGroups(); - } - if ($arguments->hasTestdoxExcludeGroups()) { - $result['testdoxExcludeGroups'] = $arguments->testdoxExcludeGroups(); - } - if ($arguments->hasTestdoxHtmlFile()) { - $result['testdoxHTMLFile'] = $arguments->testdoxHtmlFile(); - } - if ($arguments->hasTestdoxTextFile()) { - $result['testdoxTextFile'] = $arguments->testdoxTextFile(); - } - if ($arguments->hasTestdoxXmlFile()) { - $result['testdoxXMLFile'] = $arguments->testdoxXmlFile(); - } - if ($arguments->hasUseDefaultConfiguration()) { - $result['useDefaultConfiguration'] = $arguments->useDefaultConfiguration(); - } - if ($arguments->hasNoExtensions()) { - $result['noExtensions'] = $arguments->noExtensions(); - } - if ($arguments->hasNoCoverage()) { - $result['noCoverage'] = $arguments->noCoverage(); - } - if ($arguments->hasNoLogging()) { - $result['noLogging'] = $arguments->noLogging(); - } - if ($arguments->hasNoInteraction()) { - $result['noInteraction'] = $arguments->noInteraction(); - } - if ($arguments->hasBackupGlobals()) { - $result['backupGlobals'] = $arguments->backupGlobals(); - } - if ($arguments->hasBackupStaticAttributes()) { - $result['backupStaticAttributes'] = $arguments->backupStaticAttributes(); - } - if ($arguments->hasVerbose()) { - $result['verbose'] = $arguments->verbose(); - } - if ($arguments->hasReportUselessTests()) { - $result['reportUselessTests'] = $arguments->reportUselessTests(); - } - if ($arguments->hasStrictCoverage()) { - $result['strictCoverage'] = $arguments->strictCoverage(); - } - if ($arguments->hasDisableCodeCoverageIgnore()) { - $result['disableCodeCoverageIgnore'] = $arguments->disableCodeCoverageIgnore(); - } - if ($arguments->hasBeStrictAboutChangesToGlobalState()) { - $result['beStrictAboutChangesToGlobalState'] = $arguments->beStrictAboutChangesToGlobalState(); - } - if ($arguments->hasDisallowTestOutput()) { - $result['disallowTestOutput'] = $arguments->disallowTestOutput(); - } - if ($arguments->hasBeStrictAboutResourceUsageDuringSmallTests()) { - $result['beStrictAboutResourceUsageDuringSmallTests'] = $arguments->beStrictAboutResourceUsageDuringSmallTests(); - } - if ($arguments->hasDefaultTimeLimit()) { - $result['defaultTimeLimit'] = $arguments->defaultTimeLimit(); - } - if ($arguments->hasEnforceTimeLimit()) { - $result['enforceTimeLimit'] = $arguments->enforceTimeLimit(); - } - if ($arguments->hasDisallowTodoAnnotatedTests()) { - $result['disallowTodoAnnotatedTests'] = $arguments->disallowTodoAnnotatedTests(); - } - if ($arguments->hasReverseList()) { - $result['reverseList'] = $arguments->reverseList(); - } - if ($arguments->hasCoverageFilter()) { - $result['coverageFilter'] = $arguments->coverageFilter(); - } - if ($arguments->hasRandomOrderSeed()) { - $result['randomOrderSeed'] = $arguments->randomOrderSeed(); - } - if ($arguments->hasXdebugFilterFile()) { - $result['xdebugFilterFile'] = $arguments->xdebugFilterFile(); - } - return $result; + $this->metadata = $metadata->asArray(); + } + public function rewind(): void + { + $this->position = 0; + } + public function valid(): bool + { + return $this->position < count($this->metadata); + } + public function key(): int + { + return $this->position; + } + public function current(): \PHPUnit\Metadata\Metadata + { + return $this->metadata[$this->position]; + } + public function next(): void + { + $this->position++; } } PHP(?:Unit)?)\s+(?P[<>=!]{0,2})\s*(?P[\d\.-]+(dev|(RC|alpha|beta)[\d\.])?)[ \t]*\r?$/m'; + private const REGEX_REQUIRES_VERSION_CONSTRAINT = '/@requires\s+(?PPHP(?:Unit)?)\s+(?P[\d\t \-.|~^]+)[ \t]*\r?$/m'; + private const REGEX_REQUIRES_OS = '/@requires\s+(?POS(?:FAMILY)?)\s+(?P.+?)[ \t]*\r?$/m'; + private const REGEX_REQUIRES_SETTING = '/@requires\s+(?Psetting)\s+(?P([^ ]+?))\s*(?P[\w\.-]+[\w\.]?)?[ \t]*\r?$/m'; + private const REGEX_REQUIRES = '/@requires\s+(?Pfunction|extension)\s+(?P([^\s<>=!]+))\s*(?P[<>=!]{0,2})\s*(?P[\d\.-]+[\d\.]?)?[ \t]*\r?$/m'; + private readonly string $docComment; /** - * @var array - */ - protected $arguments = []; - /** - * @var array - */ - protected $longOptions = []; - /** - * @var bool + * @psalm-var array> pre-parsed annotations indexed by name and occurrence index */ - private $versionStringPrinted = \false; + private readonly array $symbolAnnotations; /** - * @psalm-var list + * @psalm-var null|(array{ + * __OFFSET: array&array{__FILE: string}, + * setting?: array, + * extension_versions?: array + * }&array< + * string, + * string|array{version: string, operator: string}|array{constraint: string}|array + * >) */ - private $warnings = []; + private ?array $parsedRequirements = null; + private readonly int $startLine; + private readonly string $fileName; /** - * @throws Exception + * @throws AnnotationsAreNotSupportedForInternalClassesException */ - public static function main(bool $exit = \true) : int + public static function ofClass(ReflectionClass $class): self { - try { - return (new static())->run($_SERVER['argv'], $exit); - } catch (Throwable $t) { - throw new \PHPUnit\TextUI\RuntimeException($t->getMessage(), (int) $t->getCode(), $t); + if ($class->isInternal()) { + throw new AnnotationsAreNotSupportedForInternalClassesException($class->getName()); } + return new self((string) $class->getDocComment(), self::extractAnnotationsFromReflector($class), $class->getStartLine(), $class->getFileName()); } /** - * @throws Exception + * @throws AnnotationsAreNotSupportedForInternalClassesException */ - public function run(array $argv, bool $exit = \true) : int + public static function ofMethod(ReflectionMethod $method): self { - $this->handleArguments($argv); - $runner = $this->createRunner(); - if ($this->arguments['test'] instanceof TestSuite) { - $suite = $this->arguments['test']; - } else { - $suite = $runner->getTest($this->arguments['test'], $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 ($method->getDeclaringClass()->isInternal()) { + throw new AnnotationsAreNotSupportedForInternalClassesException($method->getDeclaringClass()->getName()); } - if ($this->arguments['listTestsXml']) { - return $this->handleListTestsXml($suite, $this->arguments['listTestsXml'], $exit); - } - unset($this->arguments['test'], $this->arguments['testFile']); - try { - $result = $runner->run($suite, $this->arguments, $this->warnings, $exit); - } catch (Throwable $t) { - print $t->getMessage() . PHP_EOL; - } - $return = \PHPUnit\TextUI\TestRunner::FAILURE_EXIT; - if (isset($result) && $result->wasSuccessful()) { - $return = \PHPUnit\TextUI\TestRunner::SUCCESS_EXIT; - } elseif (!isset($result) || $result->errorCount() > 0) { - $return = \PHPUnit\TextUI\TestRunner::EXCEPTION_EXIT; - } - if ($exit) { - exit($return); - } - return $return; + return new self((string) $method->getDocComment(), self::extractAnnotationsFromReflector($method), $method->getStartLine(), $method->getFileName()); } /** - * Create a TestRunner, override in subclasses. + * Note: we do not preserve an instance of the reflection object, since it cannot be safely (de-)serialized. + * + * @param array> $symbolAnnotations */ - protected function createRunner() : \PHPUnit\TextUI\TestRunner + private function __construct(string $docComment, array $symbolAnnotations, int $startLine, string $fileName) { - return new \PHPUnit\TextUI\TestRunner($this->arguments['loader']); + $this->docComment = $docComment; + $this->symbolAnnotations = $symbolAnnotations; + $this->startLine = $startLine; + $this->fileName = $fileName; } /** - * Handles the command-line arguments. - * - * A child class of PHPUnit\TextUI\Command can hook into the argument - * parsing by adding the switch(es) to the $longOptions array and point to a - * callback method that handles the switch(es) in the child class like this - * - * - * longOptions['my-switch'] = 'myHandler'; - * // my-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); - * } - * - * } - * + * @psalm-return array{ + * __OFFSET: array&array{__FILE: string}, + * setting?: array, + * extension_versions?: array + * }&array< + * string, + * string|array{version: string, operator: string}|array{constraint: string}|array + * > * - * @throws Exception + * @throws InvalidVersionRequirementException */ - protected function handleArguments(array $argv) : void + public function requirements(): array { - try { - $arguments = (new Builder())->fromParameters($argv, array_keys($this->longOptions)); - } catch (ArgumentsException $e) { - $this->exitWithErrorMessage($e->getMessage()); - } - assert(isset($arguments) && $arguments instanceof Configuration); - if ($arguments->hasGenerateConfiguration() && $arguments->generateConfiguration()) { - $this->generateConfiguration(); - } - if ($arguments->hasAtLeastVersion()) { - if (version_compare(Version::id(), $arguments->atLeastVersion(), '>=')) { - exit(\PHPUnit\TextUI\TestRunner::SUCCESS_EXIT); - } - exit(\PHPUnit\TextUI\TestRunner::FAILURE_EXIT); - } - if ($arguments->hasVersion() && $arguments->version()) { - $this->printVersionString(); - exit(\PHPUnit\TextUI\TestRunner::SUCCESS_EXIT); - } - if ($arguments->hasCheckVersion() && $arguments->checkVersion()) { - $this->handleVersionCheck(); - } - if ($arguments->hasHelp()) { - $this->showHelp(); - exit(\PHPUnit\TextUI\TestRunner::SUCCESS_EXIT); - } - if ($arguments->hasUnrecognizedOrderBy()) { - $this->exitWithErrorMessage(sprintf('unrecognized --order-by option: %s', $arguments->unrecognizedOrderBy())); + if ($this->parsedRequirements !== null) { + return $this->parsedRequirements; } - if ($arguments->hasIniSettings()) { - foreach ($arguments->iniSettings() as $name => $value) { - ini_set($name, $value); + $offset = $this->startLine; + $requires = []; + $recordedSettings = []; + $extensionVersions = []; + $recordedOffsets = ['__FILE' => realpath($this->fileName)]; + // Trim docblock markers, split it into lines and rewind offset to start of docblock + $lines = preg_replace(['#^/\*{2}#', '#\*/$#'], '', 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 ($arguments->hasIncludePath()) { - ini_set('include_path', $arguments->includePath() . PATH_SEPARATOR . ini_get('include_path')); - } - $this->arguments = (new Mapper())->mapToLegacyArray($arguments); - $this->handleCustomOptions($arguments->unrecognizedOptions()); - $this->handleCustomTestSuite(); - if (!isset($this->arguments['testSuffixes'])) { - $this->arguments['testSuffixes'] = ['Test.php', '.phpt']; - } - if (!isset($this->arguments['test']) && $arguments->hasArgument()) { - $this->arguments['test'] = realpath($arguments->argument()); - if ($this->arguments['test'] === \false) { - $this->exitWithErrorMessage(sprintf('Cannot open file "%s".', $arguments->argument())); + if (preg_match(self::REGEX_REQUIRES_VERSION, $line, $matches)) { + $requires[$matches['name']] = ['version' => $matches['version'], 'operator' => $matches['operator']]; + $recordedOffsets[$matches['name']] = $offset; } - } - if ($this->arguments['loader'] !== null) { - $this->arguments['loader'] = $this->handleLoader($this->arguments['loader']); - } - if (isset($this->arguments['configuration'])) { - if (is_dir($this->arguments['configuration'])) { - $candidate = $this->configurationFileInDirectory($this->arguments['configuration']); - if ($candidate !== null) { - $this->arguments['configuration'] = $candidate; + 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 (PharIoVersionException $e) { + throw new InvalidVersionRequirementException($e->getMessage(), $e->getCode(), $e); } } - } elseif ($this->arguments['useDefaultConfiguration']) { - $candidate = $this->configurationFileInDirectory(getcwd()); - if ($candidate !== null) { - $this->arguments['configuration'] = $candidate; - } - } - if ($arguments->hasMigrateConfiguration() && $arguments->migrateConfiguration()) { - if (!isset($this->arguments['configuration'])) { - print 'No configuration file found to migrate.' . PHP_EOL; - exit(\PHPUnit\TextUI\TestRunner::EXCEPTION_EXIT); - } - $this->migrateConfiguration(realpath($this->arguments['configuration'])); - } - if (isset($this->arguments['configuration'])) { - try { - $this->arguments['configurationObject'] = (new Loader())->load($this->arguments['configuration']); - } catch (Throwable $e) { - print $e->getMessage() . PHP_EOL; - exit(\PHPUnit\TextUI\TestRunner::FAILURE_EXIT); - } - $phpunitConfiguration = $this->arguments['configurationObject']->phpunit(); - (new PhpHandler())->handle($this->arguments['configurationObject']->php()); - if (isset($this->arguments['bootstrap'])) { - $this->handleBootstrap($this->arguments['bootstrap']); - } elseif ($phpunitConfiguration->hasBootstrap()) { - $this->handleBootstrap($phpunitConfiguration->bootstrap()); - } - if (!isset($this->arguments['stderr'])) { - $this->arguments['stderr'] = $phpunitConfiguration->stderr(); - } - if (!isset($this->arguments['noExtensions']) && $phpunitConfiguration->hasExtensionsDirectory() && extension_loaded('phar')) { - $result = (new PharLoader())->loadPharExtensionsInDirectory($phpunitConfiguration->extensionsDirectory()); - $this->arguments['loadedExtensions'] = $result['loadedExtensions']; - $this->arguments['notLoadedExtensions'] = $result['notLoadedExtensions']; - unset($result); - } - if (!isset($this->arguments['columns'])) { - $this->arguments['columns'] = $phpunitConfiguration->columns(); - } - if (!isset($this->arguments['printer']) && $phpunitConfiguration->hasPrinterClass()) { - $file = $phpunitConfiguration->hasPrinterFile() ? $phpunitConfiguration->printerFile() : ''; - $this->arguments['printer'] = $this->handlePrinter($phpunitConfiguration->printerClass(), $file); - } - if ($phpunitConfiguration->hasTestSuiteLoaderClass()) { - $file = $phpunitConfiguration->hasTestSuiteLoaderFile() ? $phpunitConfiguration->testSuiteLoaderFile() : ''; - $this->arguments['loader'] = $this->handleLoader($phpunitConfiguration->testSuiteLoaderClass(), $file); - } - if (!isset($this->arguments['testsuite']) && $phpunitConfiguration->hasDefaultTestSuite()) { - $this->arguments['testsuite'] = $phpunitConfiguration->defaultTestSuite(); + if (preg_match(self::REGEX_REQUIRES_SETTING, $line, $matches)) { + $recordedSettings[$matches['setting']] = $matches['value']; + $recordedOffsets['__SETTING_' . $matches['setting']] = $offset; } - if (!isset($this->arguments['test'])) { - try { - $this->arguments['test'] = (new \PHPUnit\TextUI\TestSuiteMapper())->map($this->arguments['configurationObject']->testSuite(), $this->arguments['testsuite'] ?? ''); - } catch (\PHPUnit\TextUI\Exception $e) { - $this->printVersionString(); - print $e->getMessage() . PHP_EOL; - exit(\PHPUnit\TextUI\TestRunner::EXCEPTION_EXIT); + 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']]; } } - } 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['configurationObject'], $this->arguments['warmCoverageCache'])) { - $this->handleWarmCoverageCache($this->arguments['configurationObject']); - } - if (!isset($this->arguments['test'])) { - $this->showHelp(); - exit(\PHPUnit\TextUI\TestRunner::EXCEPTION_EXIT); + $offset++; } + return $this->parsedRequirements = array_merge($requires, ['__OFFSET' => $recordedOffsets], array_filter(['setting' => $recordedSettings, 'extension_versions' => $extensionVersions])); + } + public function symbolAnnotations(): array + { + return $this->symbolAnnotations; } /** - * Handles the loading of the PHPUnit\Runner\TestSuiteLoader implementation. - * - * @deprecated see https://github.com/sebastianbergmann/phpunit/issues/4039 + * @psalm-return array> */ - protected function handleLoader(string $loaderClass, string $loaderFile = '') : ?TestSuiteLoader + private static function parseDocBlock(string $docBlock): array { - $this->warnings[] = 'Using a custom test suite loader is deprecated'; - if (!class_exists($loaderClass, \false)) { - if ($loaderFile == '') { - $loaderFile = Filesystem::classNameToFilename($loaderClass); - } - $loaderFile = stream_resolve_include_path($loaderFile); - if ($loaderFile) { - /** - * @noinspection PhpIncludeInspection - * - * @psalm-suppress UnresolvableInclude - */ - require $loaderFile; - } - } - if (class_exists($loaderClass, \false)) { - try { - $class = new ReflectionClass($loaderClass); - // @codeCoverageIgnoreStart - } catch (\ReflectionException $e) { - throw new \PHPUnit\TextUI\ReflectionException($e->getMessage(), $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd - if ($class->implementsInterface(TestSuiteLoader::class) && $class->isInstantiable()) { - $object = $class->newInstance(); - assert($object instanceof TestSuiteLoader); - return $object; + // Strip away the docblock header and footer to ease parsing of one line annotations + $docBlock = substr($docBlock, 3, -2); + $annotations = []; + if (preg_match_all('/@(?P[A-Za-z_-]+)(?:[ \t]+(?P.*?))?[ \t]*\r?$/m', $docBlock, $matches)) { + $numMatches = count($matches[0]); + for ($i = 0; $i < $numMatches; $i++) { + $annotations[$matches['name'][$i]][] = $matches['value'][$i]; } } - if ($loaderClass == StandardTestSuiteLoader::class) { - return null; + return $annotations; + } + private static function extractAnnotationsFromReflector(ReflectionClass|ReflectionFunctionAbstract $reflector): array + { + $annotations = []; + if ($reflector instanceof ReflectionClass) { + $annotations = array_merge($annotations, ...array_map(static fn(ReflectionClass $trait): array => self::parseDocBlock((string) $trait->getDocComment()), array_values($reflector->getTraits()))); } - $this->exitWithErrorMessage(sprintf('Could not use "%s" as loader.', $loaderClass)); - return null; + return array_merge($annotations, self::parseDocBlock((string) $reflector->getDocComment())); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Metadata\Annotation\Parser; + +use function array_key_exists; +use PHPUnit\Metadata\AnnotationsAreNotSupportedForInternalClassesException; +use PHPUnit\Metadata\ReflectionException; +use ReflectionClass; +use ReflectionMethod; +/** + * 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 +{ + private static ?\PHPUnit\Metadata\Annotation\Parser\Registry $instance = null; + /** + * @psalm-var array indexed by class name + */ + private array $classDocBlocks = []; + /** + * @psalm-var array> indexed by class name and method name + */ + private array $methodDocBlocks = []; + public static function getInstance(): self + { + return self::$instance ?? self::$instance = new self(); } /** - * Handles the loading of the PHPUnit\Util\Printer implementation. + * @psalm-param class-string $class * - * @return null|Printer|string + * @throws AnnotationsAreNotSupportedForInternalClassesException + * @throws ReflectionException */ - protected function handlePrinter(string $printerClass, string $printerFile = '') + public function forClassName(string $class): \PHPUnit\Metadata\Annotation\Parser\DocBlock { - if (!class_exists($printerClass, \false)) { - if ($printerFile === '') { - $printerFile = Filesystem::classNameToFilename($printerClass); - } - $printerFile = stream_resolve_include_path($printerFile); - if ($printerFile) { - /** - * @noinspection PhpIncludeInspection - * - * @psalm-suppress UnresolvableInclude - */ - require $printerFile; - } - } - if (!class_exists($printerClass)) { - $this->exitWithErrorMessage(sprintf('Could not use "%s" as printer: class does not exist', $printerClass)); + if (array_key_exists($class, $this->classDocBlocks)) { + return $this->classDocBlocks[$class]; } try { - $class = new ReflectionClass($printerClass); + $reflection = new ReflectionClass($class); // @codeCoverageIgnoreStart } catch (\ReflectionException $e) { - throw new \PHPUnit\TextUI\ReflectionException($e->getMessage(), $e->getCode(), $e); - // @codeCoverageIgnoreEnd - } - if (!$class->implementsInterface(\PHPUnit\TextUI\ResultPrinter::class)) { - $this->exitWithErrorMessage(sprintf('Could not use "%s" as printer: class does not implement %s', $printerClass, \PHPUnit\TextUI\ResultPrinter::class)); + throw new ReflectionException($e->getMessage(), $e->getCode(), $e); } - if (!$class->isInstantiable()) { - $this->exitWithErrorMessage(sprintf('Could not use "%s" as printer: class cannot be instantiated', $printerClass)); - } - if ($class->isSubclassOf(\PHPUnit\TextUI\ResultPrinter::class)) { - return $printerClass; - } - $outputStream = isset($this->arguments['stderr']) ? 'php://stderr' : null; - return $class->newInstance($outputStream); + // @codeCoverageIgnoreEnd + return $this->classDocBlocks[$class] = \PHPUnit\Metadata\Annotation\Parser\DocBlock::ofClass($reflection); } /** - * Loads a bootstrap file. + * @psalm-param class-string $classInHierarchy + * + * @throws AnnotationsAreNotSupportedForInternalClassesException + * @throws ReflectionException */ - protected function handleBootstrap(string $filename) : void - { - try { - FileLoader::checkAndLoad($filename); - } catch (Throwable $t) { - if ($t instanceof \PHPUnit\Exception) { - $this->exitWithErrorMessage($t->getMessage()); - } - $message = sprintf('Error in bootstrap script: %s:%s%s%s%s', get_class($t), PHP_EOL, $t->getMessage(), PHP_EOL, $t->getTraceAsString()); - while ($t = $t->getPrevious()) { - $message .= sprintf('%s%sPrevious error: %s:%s%s%s%s', PHP_EOL, PHP_EOL, get_class($t), PHP_EOL, $t->getMessage(), PHP_EOL, $t->getTraceAsString()); - } - $this->exitWithErrorMessage($message); - } - } - protected function handleVersionCheck() : void + public function forMethod(string $classInHierarchy, string $method): \PHPUnit\Metadata\Annotation\Parser\DocBlock { - $this->printVersionString(); - $latestVersion = file_get_contents('https://phar.phpunit.de/latest-version-of/phpunit'); - $latestCompatibleVersion = file_get_contents('https://phar.phpunit.de/latest-version-of/phpunit-' . explode('.', Version::series())[0]); - $notLatest = version_compare($latestVersion, Version::id(), '>'); - $notLatestCompatible = version_compare($latestCompatibleVersion, Version::id(), '>'); - if ($notLatest || $notLatestCompatible) { - print 'You are not using the latest version of PHPUnit.' . PHP_EOL; - } else { - print 'You are using the latest version of PHPUnit.' . PHP_EOL; - } - if ($notLatestCompatible) { - printf('The latest version compatible with PHPUnit %s is PHPUnit %s.' . PHP_EOL, Version::id(), $latestCompatibleVersion); + if (isset($this->methodDocBlocks[$classInHierarchy][$method])) { + return $this->methodDocBlocks[$classInHierarchy][$method]; } - if ($notLatest) { - printf('The latest version is PHPUnit %s.' . PHP_EOL, $latestVersion); + try { + $reflection = new ReflectionMethod($classInHierarchy, $method); + // @codeCoverageIgnoreStart + } catch (\ReflectionException $e) { + throw new ReflectionException($e->getMessage(), $e->getCode(), $e); } - exit(\PHPUnit\TextUI\TestRunner::SUCCESS_EXIT); - } - /** - * Show the help message. - */ - protected function showHelp() : void - { - $this->printVersionString(); - (new \PHPUnit\TextUI\Help())->writeToConsole(); + // @codeCoverageIgnoreEnd + return $this->methodDocBlocks[$classInHierarchy][$method] = \PHPUnit\Metadata\Annotation\Parser\DocBlock::ofMethod($reflection); } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Metadata\Parser; + +use function array_merge; +use function assert; +use function count; +use function explode; +use function method_exists; +use function preg_replace; +use function rtrim; +use function sprintf; +use function str_contains; +use function str_starts_with; +use function strlen; +use function substr; +use function trim; +use PHPUnit\Event\Facade as EventFacade; +use PHPUnit\Metadata\Annotation\Parser\Registry as AnnotationRegistry; +use PHPUnit\Metadata\AnnotationsAreNotSupportedForInternalClassesException; +use PHPUnit\Metadata\InvalidVersionRequirementException; +use PHPUnit\Metadata\Metadata; +use PHPUnit\Metadata\MetadataCollection; +use PHPUnit\Metadata\ReflectionException; +use PHPUnit\Metadata\Version\ComparisonRequirement; +use PHPUnit\Metadata\Version\ConstraintRequirement; +use PHPUnit\Util\InvalidVersionOperatorException; +use PHPUnit\Util\VersionComparisonOperator; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class AnnotationParser implements \PHPUnit\Metadata\Parser\Parser +{ /** - * Custom callback for test suite discovery. + * @psalm-param class-string $className + * + * @throws AnnotationsAreNotSupportedForInternalClassesException + * @throws InvalidVersionOperatorException + * @throws ReflectionException */ - 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 + public function forClass(string $className): MetadataCollection { - $this->printVersionString(); - print $message . PHP_EOL; - exit(\PHPUnit\TextUI\TestRunner::FAILURE_EXIT); - } - private function handleListGroups(TestSuite $suite, bool $exit) : int - { - $this->printVersionString(); - $this->warnAboutConflictingOptions('listGroups', ['filter', 'groups', 'excludeGroups', 'testsuite']); - print 'Available test group(s):' . PHP_EOL; - $groups = $suite->getGroups(); - sort($groups); - foreach ($groups as $group) { - if (strpos($group, '__phpunit_') === 0) { - continue; + $result = []; + foreach (AnnotationRegistry::getInstance()->forClassName($className)->symbolAnnotations() as $annotation => $values) { + switch ($annotation) { + case 'backupGlobals': + $result[] = Metadata::backupGlobalsOnClass($this->stringToBool($values[0])); + break; + case 'backupStaticAttributes': + case 'backupStaticProperties': + $result[] = Metadata::backupStaticPropertiesOnClass($this->stringToBool($values[0])); + break; + case 'covers': + foreach ($values as $value) { + $value = $this->cleanUpCoversOrUsesTarget($value); + $result[] = Metadata::coversOnClass($value); + } + break; + case 'coversDefaultClass': + foreach ($values as $value) { + $result[] = Metadata::coversDefaultClass($value); + } + break; + case 'coversNothing': + $result[] = Metadata::coversNothingOnClass(); + break; + case 'doesNotPerformAssertions': + $result[] = Metadata::doesNotPerformAssertionsOnClass(); + break; + case 'group': + case 'ticket': + foreach ($values as $value) { + $result[] = Metadata::groupOnClass($value); + } + break; + case 'large': + $result[] = Metadata::groupOnClass('large'); + break; + case 'medium': + $result[] = Metadata::groupOnClass('medium'); + break; + case 'preserveGlobalState': + $result[] = Metadata::preserveGlobalStateOnClass($this->stringToBool($values[0])); + break; + case 'runClassInSeparateProcess': + $result[] = Metadata::runClassInSeparateProcess(); + break; + case 'runTestsInSeparateProcesses': + $result[] = Metadata::runTestsInSeparateProcesses(); + break; + case 'small': + $result[] = Metadata::groupOnClass('small'); + break; + case 'testdox': + $result[] = Metadata::testDoxOnClass($values[0]); + break; + case 'uses': + foreach ($values as $value) { + $value = $this->cleanUpCoversOrUsesTarget($value); + $result[] = Metadata::usesOnClass($value); + } + break; + case 'usesDefaultClass': + foreach ($values as $value) { + $result[] = Metadata::usesDefaultClass($value); + } + break; } - printf(' - %s' . PHP_EOL, $group); } - if ($exit) { - exit(\PHPUnit\TextUI\TestRunner::SUCCESS_EXIT); + try { + $result = array_merge($result, $this->parseRequirements(AnnotationRegistry::getInstance()->forClassName($className)->requirements(), 'class')); + } catch (InvalidVersionRequirementException $e) { + EventFacade::emitter()->testRunnerTriggeredWarning(sprintf('Class %s is annotated using an invalid version requirement: %s', $className, $e->getMessage())); } - return \PHPUnit\TextUI\TestRunner::SUCCESS_EXIT; + return MetadataCollection::fromArray($result); } /** - * @throws \PHPUnit\Framework\Exception - * @throws XmlConfiguration\Exception + * @psalm-param class-string $className + * @psalm-param non-empty-string $methodName + * + * @throws AnnotationsAreNotSupportedForInternalClassesException + * @throws InvalidVersionOperatorException + * @throws ReflectionException */ - private function handleListSuites(bool $exit) : int + public function forMethod(string $className, string $methodName): MetadataCollection { - $this->printVersionString(); - $this->warnAboutConflictingOptions('listSuites', ['filter', 'groups', 'excludeGroups', 'testsuite']); - print 'Available test suite(s):' . PHP_EOL; - foreach ($this->arguments['configurationObject']->testSuite() as $testSuite) { - printf(' - %s' . PHP_EOL, $testSuite->name()); + $result = []; + foreach (AnnotationRegistry::getInstance()->forMethod($className, $methodName)->symbolAnnotations() as $annotation => $values) { + switch ($annotation) { + case 'after': + $result[] = Metadata::after(); + break; + case 'afterClass': + $result[] = Metadata::afterClass(); + break; + case 'backupGlobals': + $result[] = Metadata::backupGlobalsOnMethod($this->stringToBool($values[0])); + break; + case 'backupStaticAttributes': + case 'backupStaticProperties': + $result[] = Metadata::backupStaticPropertiesOnMethod($this->stringToBool($values[0])); + break; + case 'before': + $result[] = Metadata::before(); + break; + case 'beforeClass': + $result[] = Metadata::beforeClass(); + break; + case 'covers': + foreach ($values as $value) { + $value = $this->cleanUpCoversOrUsesTarget($value); + $result[] = Metadata::coversOnMethod($value); + } + break; + case 'coversNothing': + $result[] = Metadata::coversNothingOnMethod(); + break; + case 'dataProvider': + foreach ($values as $value) { + $value = rtrim($value, " ()\n\r\t\v\x00"); + if (str_contains($value, '::')) { + $result[] = Metadata::dataProvider(...explode('::', $value)); + continue; + } + $result[] = Metadata::dataProvider($className, $value); + } + break; + case 'depends': + foreach ($values as $value) { + $deepClone = \false; + $shallowClone = \false; + if (str_starts_with($value, 'clone ')) { + $deepClone = \true; + $value = substr($value, strlen('clone ')); + } elseif (str_starts_with($value, '!clone ')) { + $value = substr($value, strlen('!clone ')); + } elseif (str_starts_with($value, 'shallowClone ')) { + $shallowClone = \true; + $value = substr($value, strlen('shallowClone ')); + } elseif (str_starts_with($value, '!shallowClone ')) { + $value = substr($value, strlen('!shallowClone ')); + } + if (str_contains($value, '::')) { + [$_className, $_methodName] = explode('::', $value); + assert($_className !== ''); + assert($_methodName !== ''); + if ($_methodName === 'class') { + $result[] = Metadata::dependsOnClass($_className, $deepClone, $shallowClone); + continue; + } + $result[] = Metadata::dependsOnMethod($_className, $_methodName, $deepClone, $shallowClone); + continue; + } + $result[] = Metadata::dependsOnMethod($className, $value, $deepClone, $shallowClone); + } + break; + case 'doesNotPerformAssertions': + $result[] = Metadata::doesNotPerformAssertionsOnMethod(); + break; + case 'excludeGlobalVariableFromBackup': + foreach ($values as $value) { + $result[] = Metadata::excludeGlobalVariableFromBackupOnMethod($value); + } + break; + case 'excludeStaticPropertyFromBackup': + foreach ($values as $value) { + $tmp = explode(' ', $value); + if (count($tmp) !== 2) { + continue; + } + $result[] = Metadata::excludeStaticPropertyFromBackupOnMethod(trim($tmp[0]), trim($tmp[1])); + } + break; + case 'group': + case 'ticket': + foreach ($values as $value) { + $result[] = Metadata::groupOnMethod($value); + } + break; + case 'large': + $result[] = Metadata::groupOnMethod('large'); + break; + case 'medium': + $result[] = Metadata::groupOnMethod('medium'); + break; + case 'postCondition': + $result[] = Metadata::postCondition(); + break; + case 'preCondition': + $result[] = Metadata::preCondition(); + break; + case 'preserveGlobalState': + $result[] = Metadata::preserveGlobalStateOnMethod($this->stringToBool($values[0])); + break; + case 'runInSeparateProcess': + $result[] = Metadata::runInSeparateProcess(); + break; + case 'small': + $result[] = Metadata::groupOnMethod('small'); + break; + case 'test': + $result[] = Metadata::test(); + break; + case 'testdox': + $result[] = Metadata::testDoxOnMethod($values[0]); + break; + case 'uses': + foreach ($values as $value) { + $value = $this->cleanUpCoversOrUsesTarget($value); + $result[] = Metadata::usesOnMethod($value); + } + break; + } } - if ($exit) { - exit(\PHPUnit\TextUI\TestRunner::SUCCESS_EXIT); + if (method_exists($className, $methodName)) { + try { + $result = array_merge($result, $this->parseRequirements(AnnotationRegistry::getInstance()->forMethod($className, $methodName)->requirements(), 'method')); + } catch (InvalidVersionRequirementException $e) { + EventFacade::emitter()->testRunnerTriggeredWarning(sprintf('Method %s::%s is annotated using an invalid version requirement: %s', $className, $methodName, $e->getMessage())); + } } - return \PHPUnit\TextUI\TestRunner::SUCCESS_EXIT; + return MetadataCollection::fromArray($result); } /** - * @throws InvalidArgumentException + * @psalm-param class-string $className + * @psalm-param non-empty-string $methodName + * + * @throws AnnotationsAreNotSupportedForInternalClassesException + * @throws InvalidVersionOperatorException + * @throws ReflectionException */ - private function handleListTests(TestSuite $suite, bool $exit) : int + public function forClassAndMethod(string $className, string $methodName): MetadataCollection { - $this->printVersionString(); - $this->warnAboutConflictingOptions('listTests', ['filter', 'groups', 'excludeGroups']); - $renderer = new TextTestListRenderer(); - print $renderer->render($suite); - if ($exit) { - exit(\PHPUnit\TextUI\TestRunner::SUCCESS_EXIT); - } - return \PHPUnit\TextUI\TestRunner::SUCCESS_EXIT; + return $this->forClass($className)->mergeWith($this->forMethod($className, $methodName)); } - /** - * @throws InvalidArgumentException - */ - private function handleListTestsXml(TestSuite $suite, string $target, bool $exit) : int + private function stringToBool(string $value): bool { - $this->printVersionString(); - $this->warnAboutConflictingOptions('listTestsXml', ['filter', 'groups', 'excludeGroups']); - $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(\PHPUnit\TextUI\TestRunner::SUCCESS_EXIT); + if ($value === 'enabled') { + return \true; } - return \PHPUnit\TextUI\TestRunner::SUCCESS_EXIT; + return \false; } - private function generateConfiguration() : void + private function cleanUpCoversOrUsesTarget(string $value): string { - $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)); - print 'Cache directory (relative to path shown above; default: .phpunit.cache): '; - $cacheDirectory = trim(fgets(STDIN)); - if ($bootstrapScript === '') { - $bootstrapScript = 'vendor/autoload.php'; - } - if ($testsDirectory === '') { - $testsDirectory = 'tests'; - } - if ($src === '') { - $src = 'src'; - } - if ($cacheDirectory === '') { - $cacheDirectory = '.phpunit.cache'; - } - $generator = new Generator(); - file_put_contents('phpunit.xml', $generator->generateDefaultConfiguration(Version::series(), $bootstrapScript, $testsDirectory, $src, $cacheDirectory)); - print PHP_EOL . 'Generated phpunit.xml in ' . getcwd() . '.' . PHP_EOL; - print 'Make sure to exclude the ' . $cacheDirectory . ' directory from version control.' . PHP_EOL; - exit(\PHPUnit\TextUI\TestRunner::SUCCESS_EXIT); + $value = preg_replace('/[\s()]+$/', '', $value); + return explode(' ', $value, 2)[0]; } - private function migrateConfiguration(string $filename) : void + /** + * @psalm-return list + * + * @throws InvalidVersionOperatorException + */ + private function parseRequirements(array $requirements, string $level): array { - $this->printVersionString(); - $result = (new SchemaDetector())->detect($filename); - if (!$result->detected()) { - print $filename . ' does not validate against any known schema.' . PHP_EOL; - exit(\PHPUnit\TextUI\TestRunner::EXCEPTION_EXIT); + $result = []; + if (!empty($requirements['PHP'])) { + $versionRequirement = new ComparisonRequirement($requirements['PHP']['version'], new VersionComparisonOperator(empty($requirements['PHP']['operator']) ? '>=' : $requirements['PHP']['operator'])); + if ($level === 'class') { + $result[] = Metadata::requiresPhpOnClass($versionRequirement); + } else { + $result[] = Metadata::requiresPhpOnMethod($versionRequirement); + } + } elseif (!empty($requirements['PHP_constraint'])) { + $versionRequirement = new ConstraintRequirement($requirements['PHP_constraint']['constraint']); + if ($level === 'class') { + $result[] = Metadata::requiresPhpOnClass($versionRequirement); + } else { + $result[] = Metadata::requiresPhpOnMethod($versionRequirement); + } } - /** @psalm-suppress MissingThrowsDocblock */ - if ($result->version() === Version::series()) { - print $filename . ' does not need to be migrated.' . PHP_EOL; - exit(\PHPUnit\TextUI\TestRunner::EXCEPTION_EXIT); + if (!empty($requirements['extensions'])) { + foreach ($requirements['extensions'] as $extension) { + if (isset($requirements['extension_versions'][$extension])) { + continue; + } + if ($level === 'class') { + $result[] = Metadata::requiresPhpExtensionOnClass($extension, null); + } else { + $result[] = Metadata::requiresPhpExtensionOnMethod($extension, null); + } + } } - copy($filename, $filename . '.bak'); - print 'Created backup: ' . $filename . '.bak' . PHP_EOL; - try { - file_put_contents($filename, (new Migrator())->migrate($filename)); - print 'Migrated configuration: ' . $filename . PHP_EOL; - } catch (Throwable $t) { - print 'Migration failed: ' . $t->getMessage() . PHP_EOL; - exit(\PHPUnit\TextUI\TestRunner::EXCEPTION_EXIT); + if (!empty($requirements['extension_versions'])) { + foreach ($requirements['extension_versions'] as $extension => $version) { + $versionRequirement = new ComparisonRequirement($version['version'], new VersionComparisonOperator(empty($version['operator']) ? '>=' : $version['operator'])); + if ($level === 'class') { + $result[] = Metadata::requiresPhpExtensionOnClass($extension, $versionRequirement); + } else { + $result[] = Metadata::requiresPhpExtensionOnMethod($extension, $versionRequirement); + } + } } - exit(\PHPUnit\TextUI\TestRunner::SUCCESS_EXIT); - } - private function handleCustomOptions(array $unrecognizedOptions) : void - { - foreach ($unrecognizedOptions as $name => $value) { - if (isset($this->longOptions[$name])) { - $handler = $this->longOptions[$name]; + if (!empty($requirements['PHPUnit'])) { + $versionRequirement = new ComparisonRequirement($requirements['PHPUnit']['version'], new VersionComparisonOperator(empty($requirements['PHPUnit']['operator']) ? '>=' : $requirements['PHPUnit']['operator'])); + if ($level === 'class') { + $result[] = Metadata::requiresPhpunitOnClass($versionRequirement); + } else { + $result[] = Metadata::requiresPhpunitOnMethod($versionRequirement); } - $name .= '='; - if (isset($this->longOptions[$name])) { - $handler = $this->longOptions[$name]; + } elseif (!empty($requirements['PHPUnit_constraint'])) { + $versionRequirement = new ConstraintRequirement($requirements['PHPUnit_constraint']['constraint']); + if ($level === 'class') { + $result[] = Metadata::requiresPhpunitOnClass($versionRequirement); + } else { + $result[] = Metadata::requiresPhpunitOnMethod($versionRequirement); } - if (isset($handler) && is_callable([$this, $handler])) { - $this->{$handler}($value); - unset($handler); + } + if (!empty($requirements['OSFAMILY'])) { + if ($level === 'class') { + $result[] = Metadata::requiresOperatingSystemFamilyOnClass($requirements['OSFAMILY']); + } else { + $result[] = Metadata::requiresOperatingSystemFamilyOnMethod($requirements['OSFAMILY']); } } - } - private function handleWarmCoverageCache(\PHPUnit\TextUI\XmlConfiguration\Configuration $configuration) : void - { - $this->printVersionString(); - if (isset($this->arguments['coverageCacheDirectory'])) { - $cacheDirectory = $this->arguments['coverageCacheDirectory']; - } elseif ($configuration->codeCoverage()->hasCacheDirectory()) { - $cacheDirectory = $configuration->codeCoverage()->cacheDirectory()->path(); - } else { - print 'Cache for static analysis has not been configured' . PHP_EOL; - exit(\PHPUnit\TextUI\TestRunner::EXCEPTION_EXIT); - } - $filter = new Filter(); - if ($configuration->codeCoverage()->hasNonEmptyListOfFilesToBeIncludedInCodeCoverageReport()) { - (new FilterMapper())->map($filter, $configuration->codeCoverage()); - } elseif (isset($this->arguments['coverageFilter'])) { - if (!is_array($this->arguments['coverageFilter'])) { - $coverageFilterDirectories = [$this->arguments['coverageFilter']]; + if (!empty($requirements['OS'])) { + if ($level === 'class') { + $result[] = Metadata::requiresOperatingSystemOnClass($requirements['OS']); } else { - $coverageFilterDirectories = $this->arguments['coverageFilter']; + $result[] = Metadata::requiresOperatingSystemOnMethod($requirements['OS']); + } + } + if (!empty($requirements['functions'])) { + foreach ($requirements['functions'] as $function) { + $pieces = explode('::', $function); + if (count($pieces) === 2) { + if ($level === 'class') { + $result[] = Metadata::requiresMethodOnClass($pieces[0], $pieces[1]); + } else { + $result[] = Metadata::requiresMethodOnMethod($pieces[0], $pieces[1]); + } + } elseif ($level === 'class') { + $result[] = Metadata::requiresFunctionOnClass($function); + } else { + $result[] = Metadata::requiresFunctionOnMethod($function); + } } - foreach ($coverageFilterDirectories as $coverageFilterDirectory) { - $filter->includeDirectory($coverageFilterDirectory); + } + if (!empty($requirements['setting'])) { + foreach ($requirements['setting'] as $setting => $value) { + if ($level === 'class') { + $result[] = Metadata::requiresSettingOnClass($setting, $value); + } else { + $result[] = Metadata::requiresSettingOnMethod($setting, $value); + } } - } else { - print 'Filter for code coverage has not been configured' . PHP_EOL; - exit(\PHPUnit\TextUI\TestRunner::EXCEPTION_EXIT); } - $timer = new Timer(); - $timer->start(); - print 'Warming cache for static analysis ... '; - (new CacheWarmer())->warmCache($cacheDirectory, !$configuration->codeCoverage()->disableCodeCoverageIgnore(), $configuration->codeCoverage()->ignoreDeprecatedCodeUnits(), $filter); - print 'done [' . $timer->stop()->asString() . ']' . PHP_EOL; - exit(\PHPUnit\TextUI\TestRunner::SUCCESS_EXIT); + return $result; } - private function configurationFileInDirectory(string $directory) : ?string +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Metadata\Parser; + +use const JSON_THROW_ON_ERROR; +use function assert; +use function json_decode; +use function str_starts_with; +use PHPUnit\Framework\Attributes\After; +use PHPUnit\Framework\Attributes\AfterClass; +use PHPUnit\Framework\Attributes\BackupGlobals; +use PHPUnit\Framework\Attributes\BackupStaticProperties; +use PHPUnit\Framework\Attributes\Before; +use PHPUnit\Framework\Attributes\BeforeClass; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\CoversFunction; +use PHPUnit\Framework\Attributes\CoversNothing; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\DataProviderExternal; +use PHPUnit\Framework\Attributes\Depends; +use PHPUnit\Framework\Attributes\DependsExternal; +use PHPUnit\Framework\Attributes\DependsExternalUsingDeepClone; +use PHPUnit\Framework\Attributes\DependsExternalUsingShallowClone; +use PHPUnit\Framework\Attributes\DependsOnClass; +use PHPUnit\Framework\Attributes\DependsOnClassUsingDeepClone; +use PHPUnit\Framework\Attributes\DependsOnClassUsingShallowClone; +use PHPUnit\Framework\Attributes\DependsUsingDeepClone; +use PHPUnit\Framework\Attributes\DependsUsingShallowClone; +use PHPUnit\Framework\Attributes\DoesNotPerformAssertions; +use PHPUnit\Framework\Attributes\ExcludeGlobalVariableFromBackup; +use PHPUnit\Framework\Attributes\ExcludeStaticPropertyFromBackup; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\Attributes\IgnoreClassForCodeCoverage; +use PHPUnit\Framework\Attributes\IgnoreDeprecations; +use PHPUnit\Framework\Attributes\IgnoreFunctionForCodeCoverage; +use PHPUnit\Framework\Attributes\IgnoreMethodForCodeCoverage; +use PHPUnit\Framework\Attributes\Large; +use PHPUnit\Framework\Attributes\Medium; +use PHPUnit\Framework\Attributes\PostCondition; +use PHPUnit\Framework\Attributes\PreCondition; +use PHPUnit\Framework\Attributes\PreserveGlobalState; +use PHPUnit\Framework\Attributes\RequiresFunction; +use PHPUnit\Framework\Attributes\RequiresMethod; +use PHPUnit\Framework\Attributes\RequiresOperatingSystem; +use PHPUnit\Framework\Attributes\RequiresOperatingSystemFamily; +use PHPUnit\Framework\Attributes\RequiresPhp; +use PHPUnit\Framework\Attributes\RequiresPhpExtension; +use PHPUnit\Framework\Attributes\RequiresPhpunit; +use PHPUnit\Framework\Attributes\RequiresSetting; +use PHPUnit\Framework\Attributes\RunClassInSeparateProcess; +use PHPUnit\Framework\Attributes\RunInSeparateProcess; +use PHPUnit\Framework\Attributes\RunTestsInSeparateProcesses; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\Test; +use PHPUnit\Framework\Attributes\TestDox; +use PHPUnit\Framework\Attributes\TestWith; +use PHPUnit\Framework\Attributes\TestWithJson; +use PHPUnit\Framework\Attributes\Ticket; +use PHPUnit\Framework\Attributes\UsesClass; +use PHPUnit\Framework\Attributes\UsesFunction; +use PHPUnit\Framework\Attributes\WithoutErrorHandler; +use PHPUnit\Metadata\Metadata; +use PHPUnit\Metadata\MetadataCollection; +use PHPUnit\Metadata\Version\ConstraintRequirement; +use ReflectionClass; +use ReflectionMethod; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class AttributeParser implements \PHPUnit\Metadata\Parser\Parser +{ + /** + * @psalm-param class-string $className + */ + public function forClass(string $className): MetadataCollection { - $candidates = [$directory . '/phpunit.xml', $directory . '/phpunit.xml.dist']; - foreach ($candidates as $candidate) { - if (is_file($candidate)) { - return realpath($candidate); + $result = []; + foreach ((new ReflectionClass($className))->getAttributes() as $attribute) { + if (!str_starts_with($attribute->getName(), 'PHPUnit\Framework\Attributes\\')) { + continue; + } + $attributeInstance = $attribute->newInstance(); + switch ($attribute->getName()) { + case BackupGlobals::class: + assert($attributeInstance instanceof BackupGlobals); + $result[] = Metadata::backupGlobalsOnClass($attributeInstance->enabled()); + break; + case BackupStaticProperties::class: + assert($attributeInstance instanceof BackupStaticProperties); + $result[] = Metadata::backupStaticPropertiesOnClass($attributeInstance->enabled()); + break; + case CoversClass::class: + assert($attributeInstance instanceof CoversClass); + $result[] = Metadata::coversClass($attributeInstance->className()); + break; + case CoversFunction::class: + assert($attributeInstance instanceof CoversFunction); + $result[] = Metadata::coversFunction($attributeInstance->functionName()); + break; + case CoversNothing::class: + $result[] = Metadata::coversNothingOnClass(); + break; + case DoesNotPerformAssertions::class: + $result[] = Metadata::doesNotPerformAssertionsOnClass(); + break; + case ExcludeGlobalVariableFromBackup::class: + assert($attributeInstance instanceof ExcludeGlobalVariableFromBackup); + $result[] = Metadata::excludeGlobalVariableFromBackupOnClass($attributeInstance->globalVariableName()); + break; + case ExcludeStaticPropertyFromBackup::class: + assert($attributeInstance instanceof ExcludeStaticPropertyFromBackup); + $result[] = Metadata::excludeStaticPropertyFromBackupOnClass($attributeInstance->className(), $attributeInstance->propertyName()); + break; + case Group::class: + assert($attributeInstance instanceof Group); + $result[] = Metadata::groupOnClass($attributeInstance->name()); + break; + case Large::class: + $result[] = Metadata::groupOnClass('large'); + break; + case Medium::class: + $result[] = Metadata::groupOnClass('medium'); + break; + case IgnoreClassForCodeCoverage::class: + assert($attributeInstance instanceof IgnoreClassForCodeCoverage); + $result[] = Metadata::ignoreClassForCodeCoverage($attributeInstance->className()); + break; + case IgnoreDeprecations::class: + assert($attributeInstance instanceof IgnoreDeprecations); + $result[] = Metadata::ignoreDeprecationsOnClass(); + break; + case IgnoreMethodForCodeCoverage::class: + assert($attributeInstance instanceof IgnoreMethodForCodeCoverage); + $result[] = Metadata::ignoreMethodForCodeCoverage($attributeInstance->className(), $attributeInstance->methodName()); + break; + case IgnoreFunctionForCodeCoverage::class: + assert($attributeInstance instanceof IgnoreFunctionForCodeCoverage); + $result[] = Metadata::ignoreFunctionForCodeCoverage($attributeInstance->functionName()); + break; + case PreserveGlobalState::class: + assert($attributeInstance instanceof PreserveGlobalState); + $result[] = Metadata::preserveGlobalStateOnClass($attributeInstance->enabled()); + break; + case RequiresMethod::class: + assert($attributeInstance instanceof RequiresMethod); + $result[] = Metadata::requiresMethodOnClass($attributeInstance->className(), $attributeInstance->methodName()); + break; + case RequiresFunction::class: + assert($attributeInstance instanceof RequiresFunction); + $result[] = Metadata::requiresFunctionOnClass($attributeInstance->functionName()); + break; + case RequiresOperatingSystem::class: + assert($attributeInstance instanceof RequiresOperatingSystem); + $result[] = Metadata::requiresOperatingSystemOnClass($attributeInstance->regularExpression()); + break; + case RequiresOperatingSystemFamily::class: + assert($attributeInstance instanceof RequiresOperatingSystemFamily); + $result[] = Metadata::requiresOperatingSystemFamilyOnClass($attributeInstance->operatingSystemFamily()); + break; + case RequiresPhp::class: + assert($attributeInstance instanceof RequiresPhp); + $result[] = Metadata::requiresPhpOnClass(ConstraintRequirement::from($attributeInstance->versionRequirement())); + break; + case RequiresPhpExtension::class: + assert($attributeInstance instanceof RequiresPhpExtension); + $versionConstraint = null; + $versionRequirement = $attributeInstance->versionRequirement(); + if ($versionRequirement !== null) { + $versionConstraint = ConstraintRequirement::from($versionRequirement); + } + $result[] = Metadata::requiresPhpExtensionOnClass($attributeInstance->extension(), $versionConstraint); + break; + case RequiresPhpunit::class: + assert($attributeInstance instanceof RequiresPhpunit); + $result[] = Metadata::requiresPhpunitOnClass(ConstraintRequirement::from($attributeInstance->versionRequirement())); + break; + case RequiresSetting::class: + assert($attributeInstance instanceof RequiresSetting); + $result[] = Metadata::requiresSettingOnClass($attributeInstance->setting(), $attributeInstance->value()); + break; + case RunClassInSeparateProcess::class: + $result[] = Metadata::runClassInSeparateProcess(); + break; + case RunTestsInSeparateProcesses::class: + $result[] = Metadata::runTestsInSeparateProcesses(); + break; + case Small::class: + $result[] = Metadata::groupOnClass('small'); + break; + case TestDox::class: + assert($attributeInstance instanceof TestDox); + $result[] = Metadata::testDoxOnClass($attributeInstance->text()); + break; + case Ticket::class: + assert($attributeInstance instanceof Ticket); + $result[] = Metadata::groupOnClass($attributeInstance->text()); + break; + case UsesClass::class: + assert($attributeInstance instanceof UsesClass); + $result[] = Metadata::usesClass($attributeInstance->className()); + break; + case UsesFunction::class: + assert($attributeInstance instanceof UsesFunction); + $result[] = Metadata::usesFunction($attributeInstance->functionName()); + break; } } - return null; + return MetadataCollection::fromArray($result); } /** - * @psalm-param "listGroups"|"listSuites"|"listTests"|"listTestsXml"|"filter"|"groups"|"excludeGroups"|"testsuite" $key - * @psalm-param list<"listGroups"|"listSuites"|"listTests"|"listTestsXml"|"filter"|"groups"|"excludeGroups"|"testsuite"> $keys + * @psalm-param class-string $className + * @psalm-param non-empty-string $methodName */ - private function warnAboutConflictingOptions(string $key, array $keys) : void + public function forMethod(string $className, string $methodName): MetadataCollection { - $warningPrinted = \false; - foreach ($keys as $_key) { - if (!empty($this->arguments[$_key])) { - printf('The %s and %s options cannot be combined, %s is ignored' . PHP_EOL, $this->mapKeyToOptionForWarning($_key), $this->mapKeyToOptionForWarning($key), $this->mapKeyToOptionForWarning($_key)); - $warningPrinted = \true; + $result = []; + foreach ((new ReflectionMethod($className, $methodName))->getAttributes() as $attribute) { + if (!str_starts_with($attribute->getName(), 'PHPUnit\Framework\Attributes\\')) { + continue; + } + $attributeInstance = $attribute->newInstance(); + switch ($attribute->getName()) { + case After::class: + $result[] = Metadata::after(); + break; + case AfterClass::class: + $result[] = Metadata::afterClass(); + break; + case BackupGlobals::class: + assert($attributeInstance instanceof BackupGlobals); + $result[] = Metadata::backupGlobalsOnMethod($attributeInstance->enabled()); + break; + case BackupStaticProperties::class: + assert($attributeInstance instanceof BackupStaticProperties); + $result[] = Metadata::backupStaticPropertiesOnMethod($attributeInstance->enabled()); + break; + case Before::class: + $result[] = Metadata::before(); + break; + case BeforeClass::class: + $result[] = Metadata::beforeClass(); + break; + case CoversNothing::class: + $result[] = Metadata::coversNothingOnMethod(); + break; + case DataProvider::class: + assert($attributeInstance instanceof DataProvider); + $result[] = Metadata::dataProvider($className, $attributeInstance->methodName()); + break; + case DataProviderExternal::class: + assert($attributeInstance instanceof DataProviderExternal); + $result[] = Metadata::dataProvider($attributeInstance->className(), $attributeInstance->methodName()); + break; + case Depends::class: + assert($attributeInstance instanceof Depends); + $result[] = Metadata::dependsOnMethod($className, $attributeInstance->methodName(), \false, \false); + break; + case DependsUsingDeepClone::class: + assert($attributeInstance instanceof DependsUsingDeepClone); + $result[] = Metadata::dependsOnMethod($className, $attributeInstance->methodName(), \true, \false); + break; + case DependsUsingShallowClone::class: + assert($attributeInstance instanceof DependsUsingShallowClone); + $result[] = Metadata::dependsOnMethod($className, $attributeInstance->methodName(), \false, \true); + break; + case DependsExternal::class: + assert($attributeInstance instanceof DependsExternal); + $result[] = Metadata::dependsOnMethod($attributeInstance->className(), $attributeInstance->methodName(), \false, \false); + break; + case DependsExternalUsingDeepClone::class: + assert($attributeInstance instanceof DependsExternalUsingDeepClone); + $result[] = Metadata::dependsOnMethod($attributeInstance->className(), $attributeInstance->methodName(), \true, \false); + break; + case DependsExternalUsingShallowClone::class: + assert($attributeInstance instanceof DependsExternalUsingShallowClone); + $result[] = Metadata::dependsOnMethod($attributeInstance->className(), $attributeInstance->methodName(), \false, \true); + break; + case DependsOnClass::class: + assert($attributeInstance instanceof DependsOnClass); + $result[] = Metadata::dependsOnClass($attributeInstance->className(), \false, \false); + break; + case DependsOnClassUsingDeepClone::class: + assert($attributeInstance instanceof DependsOnClassUsingDeepClone); + $result[] = Metadata::dependsOnClass($attributeInstance->className(), \true, \false); + break; + case DependsOnClassUsingShallowClone::class: + assert($attributeInstance instanceof DependsOnClassUsingShallowClone); + $result[] = Metadata::dependsOnClass($attributeInstance->className(), \false, \true); + break; + case DoesNotPerformAssertions::class: + assert($attributeInstance instanceof DoesNotPerformAssertions); + $result[] = Metadata::doesNotPerformAssertionsOnMethod(); + break; + case ExcludeGlobalVariableFromBackup::class: + assert($attributeInstance instanceof ExcludeGlobalVariableFromBackup); + $result[] = Metadata::excludeGlobalVariableFromBackupOnMethod($attributeInstance->globalVariableName()); + break; + case ExcludeStaticPropertyFromBackup::class: + assert($attributeInstance instanceof ExcludeStaticPropertyFromBackup); + $result[] = Metadata::excludeStaticPropertyFromBackupOnMethod($attributeInstance->className(), $attributeInstance->propertyName()); + break; + case Group::class: + assert($attributeInstance instanceof Group); + $result[] = Metadata::groupOnMethod($attributeInstance->name()); + break; + case IgnoreDeprecations::class: + assert($attributeInstance instanceof IgnoreDeprecations); + $result[] = Metadata::ignoreDeprecationsOnMethod(); + break; + case PostCondition::class: + $result[] = Metadata::postCondition(); + break; + case PreCondition::class: + $result[] = Metadata::preCondition(); + break; + case PreserveGlobalState::class: + assert($attributeInstance instanceof PreserveGlobalState); + $result[] = Metadata::preserveGlobalStateOnMethod($attributeInstance->enabled()); + break; + case RequiresMethod::class: + assert($attributeInstance instanceof RequiresMethod); + $result[] = Metadata::requiresMethodOnMethod($attributeInstance->className(), $attributeInstance->methodName()); + break; + case RequiresFunction::class: + assert($attributeInstance instanceof RequiresFunction); + $result[] = Metadata::requiresFunctionOnMethod($attributeInstance->functionName()); + break; + case RequiresOperatingSystem::class: + assert($attributeInstance instanceof RequiresOperatingSystem); + $result[] = Metadata::requiresOperatingSystemOnMethod($attributeInstance->regularExpression()); + break; + case RequiresOperatingSystemFamily::class: + assert($attributeInstance instanceof RequiresOperatingSystemFamily); + $result[] = Metadata::requiresOperatingSystemFamilyOnMethod($attributeInstance->operatingSystemFamily()); + break; + case RequiresPhp::class: + assert($attributeInstance instanceof RequiresPhp); + $result[] = Metadata::requiresPhpOnMethod(ConstraintRequirement::from($attributeInstance->versionRequirement())); + break; + case RequiresPhpExtension::class: + assert($attributeInstance instanceof RequiresPhpExtension); + $versionConstraint = null; + $versionRequirement = $attributeInstance->versionRequirement(); + if ($versionRequirement !== null) { + $versionConstraint = ConstraintRequirement::from($versionRequirement); + } + $result[] = Metadata::requiresPhpExtensionOnMethod($attributeInstance->extension(), $versionConstraint); + break; + case RequiresPhpunit::class: + assert($attributeInstance instanceof RequiresPhpunit); + $result[] = Metadata::requiresPhpunitOnMethod(ConstraintRequirement::from($attributeInstance->versionRequirement())); + break; + case RequiresSetting::class: + assert($attributeInstance instanceof RequiresSetting); + $result[] = Metadata::requiresSettingOnMethod($attributeInstance->setting(), $attributeInstance->value()); + break; + case RunInSeparateProcess::class: + $result[] = Metadata::runInSeparateProcess(); + break; + case Test::class: + $result[] = Metadata::test(); + break; + case TestDox::class: + assert($attributeInstance instanceof TestDox); + $result[] = Metadata::testDoxOnMethod($attributeInstance->text()); + break; + case TestWith::class: + assert($attributeInstance instanceof TestWith); + $result[] = Metadata::testWith($attributeInstance->data()); + break; + case TestWithJson::class: + assert($attributeInstance instanceof TestWithJson); + $result[] = Metadata::testWith(json_decode($attributeInstance->json(), \true, 512, JSON_THROW_ON_ERROR)); + break; + case Ticket::class: + assert($attributeInstance instanceof Ticket); + $result[] = Metadata::groupOnMethod($attributeInstance->text()); + break; + case WithoutErrorHandler::class: + assert($attributeInstance instanceof WithoutErrorHandler); + $result[] = Metadata::withoutErrorHandler(); + break; } } - if ($warningPrinted) { - print PHP_EOL; - } + return MetadataCollection::fromArray($result); } /** - * @psalm-param "listGroups"|"listSuites"|"listTests"|"listTestsXml"|"filter"|"groups"|"excludeGroups"|"testsuite" $key + * @psalm-param class-string $className + * @psalm-param non-empty-string $methodName */ - private function mapKeyToOptionForWarning(string $key) : string + public function forClassAndMethod(string $className, string $methodName): MetadataCollection { - switch ($key) { - case 'listGroups': - return '--list-groups'; - case 'listSuites': - return '--list-suites'; - case 'listTests': - return '--list-tests'; - case 'listTestsXml': - return '--list-tests-xml'; - case 'filter': - return '--filter'; - case 'groups': - return '--group'; - case 'excludeGroups': - return '--exclude-group'; - case 'testsuite': - return '--testsuite'; - } + return $this->forClass($className)->mergeWith($this->forMethod($className, $methodName)); } } reader = $reader; + } /** - * @var bool + * @psalm-param class-string $className */ - protected $lastTestFailed = \false; + public function forClass(string $className): MetadataCollection + { + if (isset($this->classCache[$className])) { + return $this->classCache[$className]; + } + $this->classCache[$className] = $this->reader->forClass($className); + return $this->classCache[$className]; + } /** - * @var int + * @psalm-param class-string $className + * @psalm-param non-empty-string $methodName */ - protected $numAssertions = 0; + public function forMethod(string $className, string $methodName): MetadataCollection + { + $key = $className . '::' . $methodName; + if (isset($this->methodCache[$key])) { + return $this->methodCache[$key]; + } + $this->methodCache[$key] = $this->reader->forMethod($className, $methodName); + return $this->methodCache[$key]; + } /** - * @var int + * @psalm-param class-string $className + * @psalm-param non-empty-string $methodName */ - protected $numTests = -1; + public function forClassAndMethod(string $className, string $methodName): MetadataCollection + { + $key = $className . '::' . $methodName; + if (isset($this->classAndMethodCache[$key])) { + return $this->classAndMethodCache[$key]; + } + $this->classAndMethodCache[$key] = $this->forClass($className)->mergeWith($this->forMethod($className, $methodName)); + return $this->classAndMethodCache[$key]; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Metadata\Parser; + +use PHPUnit\Metadata\MetadataCollection; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +interface Parser +{ /** - * @var int + * @psalm-param class-string $className */ - protected $numTestsRun = 0; + public function forClass(string $className): MetadataCollection; /** - * @var int + * @psalm-param class-string $className + * @psalm-param non-empty-string $methodName */ - protected $numTestsWidth; + public function forMethod(string $className, string $methodName): MetadataCollection; /** - * @var bool + * @psalm-param class-string $className + * @psalm-param non-empty-string $methodName */ - protected $colors = \false; + public function forClassAndMethod(string $className, string $methodName): MetadataCollection; +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Metadata\Parser; + +use PHPUnit\Metadata\MetadataCollection; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class ParserChain implements \PHPUnit\Metadata\Parser\Parser +{ + private readonly \PHPUnit\Metadata\Parser\Parser $attributeReader; + private readonly \PHPUnit\Metadata\Parser\Parser $annotationReader; + public function __construct(\PHPUnit\Metadata\Parser\Parser $attributeReader, \PHPUnit\Metadata\Parser\Parser $annotationReader) + { + $this->attributeReader = $attributeReader; + $this->annotationReader = $annotationReader; + } /** - * @var bool + * @psalm-param class-string $className */ - protected $debug = \false; + public function forClass(string $className): MetadataCollection + { + $metadata = $this->attributeReader->forClass($className); + if (!$metadata->isEmpty()) { + return $metadata; + } + return $this->annotationReader->forClass($className); + } /** - * @var bool + * @psalm-param class-string $className + * @psalm-param non-empty-string $methodName */ - protected $verbose = \false; + public function forMethod(string $className, string $methodName): MetadataCollection + { + $metadata = $this->attributeReader->forMethod($className, $methodName); + if (!$metadata->isEmpty()) { + return $metadata; + } + return $this->annotationReader->forMethod($className, $methodName); + } /** - * @var int + * @psalm-param class-string $className + * @psalm-param non-empty-string $methodName */ - private $numberOfColumns; - /** - * @var bool + public function forClassAndMethod(string $className, string $methodName): MetadataCollection + { + return $this->forClass($className)->mergeWith($this->forMethod($className, $methodName)); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Metadata\Parser; + +/** + * Attribute and annotation 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 +{ + private static ?\PHPUnit\Metadata\Parser\Parser $instance = null; + public static function parser(): \PHPUnit\Metadata\Parser\Parser + { + return self::$instance ?? self::$instance = self::build(); + } + private static function build(): \PHPUnit\Metadata\Parser\Parser + { + return new \PHPUnit\Metadata\Parser\CachingParser(new \PHPUnit\Metadata\Parser\ParserChain(new \PHPUnit\Metadata\Parser\AttributeParser(), new \PHPUnit\Metadata\Parser\AnnotationParser())); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Metadata; + +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class PostCondition extends \PHPUnit\Metadata\Metadata +{ + /** + * @psalm-assert-if-true PostCondition $this */ - private $reverse; + public function isPostCondition(): bool + { + return \true; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Metadata; + +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class PreCondition extends \PHPUnit\Metadata\Metadata +{ /** - * @var bool + * @psalm-assert-if-true PreCondition $this */ - private $defectListPrinted = \false; + public function isPreCondition(): bool + { + return \true; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Metadata; + +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class PreserveGlobalState extends \PHPUnit\Metadata\Metadata +{ + private readonly bool $enabled; /** - * @var Timer + * @psalm-param 0|1 $level */ - private $timer; + protected function __construct(int $level, bool $enabled) + { + parent::__construct($level); + $this->enabled = $enabled; + } /** - * Constructor. - * - * @param null|resource|string $out - * @param int|string $numberOfColumns - * - * @throws Exception + * @psalm-assert-if-true PreserveGlobalState $this */ - public function __construct($out = null, bool $verbose = \false, string $colors = self::COLOR_DEFAULT, bool $debug = \false, $numberOfColumns = 80, bool $reverse = \false) + public function isPreserveGlobalState(): bool { - 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; - } - $this->timer = new Timer(); - $this->timer->start(); + return \true; } - public function printResult(TestResult $result) : void + public function enabled(): bool { - $this->printHeader($result); - $this->printErrors($result); - $this->printWarnings($result); - $this->printFailures($result); - $this->printRisky($result); - if ($this->verbose) { - $this->printIncompletes($result); - $this->printSkipped($result); - } - $this->printFooter($result); + return $this->enabled; } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Metadata; + +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class RequiresFunction extends \PHPUnit\Metadata\Metadata +{ /** - * An error occurred. + * @psalm-var non-empty-string */ - public function addError(Test $test, Throwable $t, float $time) : void - { - $this->writeProgressWithColor('fg-red, bold', 'E'); - $this->lastTestFailed = \true; - } + private readonly string $functionName; /** - * A failure occurred. + * @psalm-param 0|1 $level + * @psalm-param non-empty-string $functionName */ - public function addFailure(Test $test, AssertionFailedError $e, float $time) : void + protected function __construct(int $level, string $functionName) { - $this->writeProgressWithColor('bg-red, fg-white', 'F'); - $this->lastTestFailed = \true; + parent::__construct($level); + $this->functionName = $functionName; } /** - * A warning occurred. + * @psalm-assert-if-true RequiresFunction $this */ - public function addWarning(Test $test, Warning $e, float $time) : void + public function isRequiresFunction(): bool { - $this->writeProgressWithColor('fg-yellow, bold', 'W'); - $this->lastTestFailed = \true; + return \true; } /** - * Incomplete test. + * @psalm-return non-empty-string */ - public function addIncompleteTest(Test $test, Throwable $t, float $time) : void + public function functionName(): string { - $this->writeProgressWithColor('fg-yellow, bold', 'I'); - $this->lastTestFailed = \true; + return $this->functionName; } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Metadata; + +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class RequiresMethod extends \PHPUnit\Metadata\Metadata +{ /** - * Risky test. + * @psalm-var class-string */ - public function addRiskyTest(Test $test, Throwable $t, float $time) : void - { - $this->writeProgressWithColor('fg-yellow, bold', 'R'); - $this->lastTestFailed = \true; - } + private readonly string $className; /** - * Skipped test. + * @psalm-var non-empty-string */ - public function addSkippedTest(Test $test, Throwable $t, float $time) : void - { - $this->writeProgressWithColor('fg-cyan, bold', 'S'); - $this->lastTestFailed = \true; - } + private readonly string $methodName; /** - * A testsuite started. + * @psalm-param 0|1 $level + * @psalm-param class-string $className + * @psalm-param non-empty-string $methodName */ - public function startTestSuite(TestSuite $suite) : void + protected function __construct(int $level, string $className, string $methodName) { - if ($this->numTests == -1) { - $this->numTests = count($suite); - $this->numTestsWidth = strlen((string) $this->numTests); - $this->maxColumn = $this->numberOfColumns - strlen(' / (XXX%)') - 2 * $this->numTestsWidth; - } + parent::__construct($level); + $this->className = $className; + $this->methodName = $methodName; } /** - * A testsuite ended. + * @psalm-assert-if-true RequiresMethod $this */ - public function endTestSuite(TestSuite $suite) : void + public function isRequiresMethod(): bool { + return \true; } /** - * A test started. + * @psalm-return class-string */ - public function startTest(Test $test) : void + public function className(): string { - if ($this->debug) { - $this->write(sprintf("Test '%s' started\n", \PHPUnit\Util\Test::describeAsString($test))); - } + return $this->className; } /** - * A test ended. + * @psalm-return non-empty-string */ - public function endTest(Test $test, float $time) : void + public function methodName(): string { - 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()); - } + return $this->methodName; } - protected function printDefects(array $defects, string $type) : void +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Metadata; + +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class RequiresOperatingSystem extends \PHPUnit\Metadata\Metadata +{ + /** + * @psalm-var non-empty-string + */ + private readonly string $operatingSystem; + /** + * @psalm-param 0|1 $level + * @psalm-param non-empty-string $operatingSystem + */ + public function __construct(int $level, string $operatingSystem) { - $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; + parent::__construct($level); + $this->operatingSystem = $operatingSystem; } - protected function printDefect(TestFailure $defect, int $count) : void + /** + * @psalm-assert-if-true RequiresOperatingSystem $this + */ + public function isRequiresOperatingSystem(): bool { - $this->printDefectHeader($defect, $count); - $this->printDefectTrace($defect); + return \true; } - protected function printDefectHeader(TestFailure $defect, int $count) : void + /** + * @psalm-return non-empty-string + */ + public function operatingSystem(): string { - $this->write(sprintf("\n%d) %s\n", $count, $defect->getTestName())); + return $this->operatingSystem; } - protected function printDefectTrace(TestFailure $defect) : void +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Metadata; + +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class RequiresOperatingSystemFamily extends \PHPUnit\Metadata\Metadata +{ + /** + * @psalm-var non-empty-string + */ + private readonly string $operatingSystemFamily; + /** + * @psalm-param 0|1 $level + * @psalm-param non-empty-string $operatingSystemFamily + */ + protected function __construct(int $level, string $operatingSystemFamily) { - $e = $defect->thrownException(); - $this->write((string) $e); - while ($e = $e->getPrevious()) { - $this->write("\nCaused by\n" . trim((string) $e) . "\n"); - } + parent::__construct($level); + $this->operatingSystemFamily = $operatingSystemFamily; } - protected function printErrors(TestResult $result) : void + /** + * @psalm-assert-if-true RequiresOperatingSystemFamily $this + */ + public function isRequiresOperatingSystemFamily(): bool { - $this->printDefects($result->errors(), 'error'); + return \true; } - protected function printFailures(TestResult $result) : void + /** + * @psalm-return non-empty-string + */ + public function operatingSystemFamily(): string { - $this->printDefects($result->failures(), 'failure'); + return $this->operatingSystemFamily; } - protected function printWarnings(TestResult $result) : void +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Metadata; + +use PHPUnit\Metadata\Version\Requirement; +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class RequiresPhp extends \PHPUnit\Metadata\Metadata +{ + private readonly Requirement $versionRequirement; + /** + * @psalm-param 0|1 $level + */ + protected function __construct(int $level, Requirement $versionRequirement) { - $this->printDefects($result->warnings(), 'warning'); + parent::__construct($level); + $this->versionRequirement = $versionRequirement; } - protected function printIncompletes(TestResult $result) : void + /** + * @psalm-assert-if-true RequiresPhp $this + */ + public function isRequiresPhp(): bool { - $this->printDefects($result->notImplemented(), 'incomplete test'); + return \true; } - protected function printRisky(TestResult $result) : void + public function versionRequirement(): Requirement { - $this->printDefects($result->risky(), 'risky test'); + return $this->versionRequirement; } - protected function printSkipped(TestResult $result) : void +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Metadata; + +use PHPUnit\Metadata\Version\Requirement; +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class RequiresPhpExtension extends \PHPUnit\Metadata\Metadata +{ + /** + * @psalm-var non-empty-string + */ + private readonly string $extension; + private readonly ?Requirement $versionRequirement; + /** + * @psalm-param 0|1 $level + * @psalm-param non-empty-string $extension + */ + protected function __construct(int $level, string $extension, ?Requirement $versionRequirement) { - $this->printDefects($result->skipped(), 'skipped test'); + parent::__construct($level); + $this->extension = $extension; + $this->versionRequirement = $versionRequirement; } - protected function printHeader(TestResult $result) : void + /** + * @psalm-assert-if-true RequiresPhpExtension $this + */ + public function isRequiresPhpExtension(): bool { - if (count($result) > 0) { - $this->write(PHP_EOL . PHP_EOL . (new ResourceUsageFormatter())->resourceUsage($this->timer->stop()) . PHP_EOL . PHP_EOL); - } + return \true; } - protected function printFooter(TestResult $result) : void + /** + * @psalm-return non-empty-string + */ + public function extension(): string { - if (count($result) === 0) { - $this->writeWithColor('fg-black, bg-yellow', 'No tests executed!'); - return; - } - if ($result->wasSuccessfulAndNoTestIsRiskyOrSkippedOrIncomplete()) { - $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')); - return; - } - $color = 'fg-black, bg-yellow'; - if ($result->wasSuccessful()) { - 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(); - } - } + return $this->extension; } - protected function writeNewLine() : void + /** + * @psalm-assert-if-true !null $this->versionRequirement + */ + public function hasVersionRequirement(): bool { - $this->column = 0; - $this->write("\n"); + return $this->versionRequirement !== null; } /** - * Formats a buffer with a specified ANSI color sequence if colors are - * enabled. + * @throws NoVersionRequirementException */ - protected function colorizeTextBox(string $color, string $buffer) : string + public function versionRequirement(): Requirement { - if (!$this->colors) { - return $buffer; + if ($this->versionRequirement === null) { + throw new \PHPUnit\Metadata\NoVersionRequirementException(); } - $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); + return $this->versionRequirement; } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Metadata; + +use PHPUnit\Metadata\Version\Requirement; +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class RequiresPhpunit extends \PHPUnit\Metadata\Metadata +{ + private readonly Requirement $versionRequirement; /** - * Writes a buffer out with a color sequence if colors are enabled. + * @psalm-param 0|1 $level */ - protected function writeWithColor(string $color, string $buffer, bool $lf = \true) : void + protected function __construct(int $level, Requirement $versionRequirement) { - $this->write($this->colorizeTextBox($color, $buffer)); - if ($lf) { - $this->write(PHP_EOL); - } + parent::__construct($level); + $this->versionRequirement = $versionRequirement; } /** - * Writes progress with a color sequence if colors are enabled. + * @psalm-assert-if-true RequiresPhpunit $this */ - protected function writeProgressWithColor(string $color, string $buffer) : void + public function isRequiresPhpunit(): bool { - $buffer = $this->colorizeTextBox($color, $buffer); - $this->writeProgress($buffer); + return \true; } - private function writeCountString(int $count, string $name, string $color, bool $always = \false) : void + public function versionRequirement(): Requirement { - static $first = \true; - if ($always || $count > 0) { - $this->writeWithColor($color, sprintf('%s%s: %d', !$first ? ', ' : '', $name, $count), \false); - $first = \false; - } + return $this->versionRequirement; } } setting = $setting; + $this->value = $value; + } + /** + * @psalm-assert-if-true RequiresSetting $this + */ + public function isRequiresSetting(): bool + { + return \true; + } + /** + * @psalm-return non-empty-string + */ + public function setting(): string + { + return $this->setting; + } + /** + * @psalm-return non-empty-string + */ + public function value(): string + { + return $this->value; + } } getNumberOfColumns(); - } - if ($withColor === null) { - $this->hasColor = (new Console())->hasColorSupport(); - } else { - $this->hasColor = $withColor; - } - foreach ($this->elements() as $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; + parent::__construct($level); + $this->text = $text; } /** - * Write the help file to the CLI, adapting width and colors to the console. + * @psalm-assert-if-true TestDox $this */ - public function writeToConsole() : void - { - if ($this->hasColor) { - $this->writeWithColor(); - } else { - $this->writePlaintext(); - } - } - private function writePlaintext() : void - { - foreach ($this->elements() 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 + public function isTestDox(): bool { - foreach ($this->elements() 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; - } + return \true; } /** - * @psalm-return array> + * @psalm-return non-empty-string */ - private function elements() : array + public function text(): string { - $elements = ['Usage' => [['text' => 'phpunit [options] UnitTest.php'], ['text' => 'phpunit [options] ']], 'Code Coverage Options' => [['arg' => '--coverage-clover ', 'desc' => 'Generate code coverage report in Clover XML format'], ['arg' => '--coverage-cobertura ', 'desc' => 'Generate code coverage report in Cobertura XML format'], ['arg' => '--coverage-crap4j ', 'desc' => 'Generate code coverage report in Crap4J XML format'], ['arg' => '--coverage-html ', 'desc' => 'Generate code coverage report in HTML format'], ['arg' => '--coverage-php ', 'desc' => 'Export PHP_CodeCoverage object to file'], ['arg' => '--coverage-text=', 'desc' => 'Generate code coverage report in text format [default: standard output]'], ['arg' => '--coverage-xml ', 'desc' => 'Generate code coverage report in PHPUnit XML format'], ['arg' => '--coverage-cache ', 'desc' => 'Cache static analysis results'], ['arg' => '--warm-coverage-cache', 'desc' => 'Warm static analysis cache'], ['arg' => '--coverage-filter ', 'desc' => 'Include in code coverage analysis'], ['arg' => '--path-coverage', 'desc' => 'Perform path coverage analysis'], ['arg' => '--disable-coverage-ignore', 'desc' => 'Disable annotations for ignoring code coverage'], ['arg' => '--no-coverage', 'desc' => 'Ignore code coverage configuration']], 'Logging Options' => [['arg' => '--log-junit ', 'desc' => 'Log test execution in JUnit XML format to file'], ['arg' => '--log-teamcity ', 'desc' => 'Log test execution in TeamCity format to file'], ['arg' => '--testdox-html ', 'desc' => 'Write agile documentation in HTML format to file'], ['arg' => '--testdox-text ', 'desc' => 'Write agile documentation in Text format to file'], ['arg' => '--testdox-xml ', 'desc' => 'Write agile documentation in XML format to file'], ['arg' => '--reverse-list', 'desc' => 'Print defects in reverse order'], ['arg' => '--no-logging', 'desc' => 'Ignore logging configuration']], 'Test Selection Options' => [['arg' => '--list-suites', 'desc' => 'List available test suites'], ['arg' => '--testsuite ', 'desc' => 'Filter which testsuite to run'], ['arg' => '--list-groups', 'desc' => 'List available test groups'], ['arg' => '--group ', 'desc' => 'Only runs tests from the specified group(s)'], ['arg' => '--exclude-group ', 'desc' => 'Exclude tests from the specified group(s)'], ['arg' => '--covers ', 'desc' => 'Only runs tests annotated with "@covers "'], ['arg' => '--uses ', 'desc' => 'Only runs tests annotated with "@uses "'], ['arg' => '--list-tests', 'desc' => 'List available tests'], ['arg' => '--list-tests-xml ', 'desc' => 'List available tests in XML format'], ['arg' => '--filter ', 'desc' => 'Filter which tests to run'], ['arg' => '--test-suffix ', '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 ', '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 ', 'desc' => 'Use colors in output ("never", "auto" or "always")'], ['arg' => '--columns ', '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-incomplete', 'desc' => 'Treat incomplete tests as failures'], ['arg' => '--fail-on-risky', 'desc' => 'Treat risky tests as failures'], ['arg' => '--fail-on-skipped', 'desc' => 'Treat skipped tests as failures'], ['arg' => '--fail-on-warning', 'desc' => 'Treat tests with warnings as failures'], ['arg' => '-v|--verbose', 'desc' => 'Output more verbose information'], ['arg' => '--debug', 'desc' => 'Display debugging information'], ['spacer' => ''], ['arg' => '--repeat ', '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 ', 'desc' => 'TestListener implementation to use'], ['spacer' => ''], ['arg' => '--order-by ', 'desc' => 'Run tests in order: default|defects|duration|no-depends|random|reverse|size'], ['arg' => '--random-order-seed ', 'desc' => 'Use a specific random seed 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 ', 'desc' => 'A PHP script that is included as early as possible'], ['arg' => '--bootstrap ', 'desc' => 'A PHP script that is included before the tests run'], ['arg' => '-c|--configuration ', 'desc' => 'Read configuration from XML file'], ['arg' => '--no-configuration', 'desc' => 'Ignore default configuration file (phpunit.xml)'], ['arg' => '--extensions ', 'desc' => 'A comma separated list of PHPUnit extensions to load'], ['arg' => '--no-extensions', 'desc' => 'Do not load PHPUnit extensions'], ['arg' => '--include-path ', 'desc' => 'Prepend PHP\'s include_path with given path(s)'], ['arg' => '-d ', 'desc' => 'Sets a php.ini value'], ['arg' => '--cache-result-file ', 'desc' => 'Specify result cache path and filename'], ['arg' => '--generate-configuration', 'desc' => 'Generate configuration file with suggested settings'], ['arg' => '--migrate-configuration', 'desc' => 'Migrate configuration file to current format']]]; - if (defined('__PHPUNIT_PHAR__')) { - $elements['PHAR Options'] = [['arg' => '--manifest', 'desc' => 'Print Software Bill of Materials (SBOM) in plain-text format'], ['arg' => '--sbom', 'desc' => 'Print Software Bill of Materials (SBOM) in CycloneDX XML format'], ['arg' => '--composer-lock', 'desc' => 'Print composer.lock file used to build the PHAR']]; - } - $elements['Miscellaneous Options'] = [['arg' => '-h|--help', 'desc' => 'Prints this usage information'], ['arg' => '--version', 'desc' => 'Prints the version and exits'], ['arg' => '--atleast-version ', 'desc' => 'Checks that version is greater than min and exits'], ['arg' => '--check-version', 'desc' => 'Checks whether PHPUnit is the latest version and exits']]; - return $elements; + return $this->text; } } - * + private readonly array $data; + /** + * @psalm-param 0|1 $level + */ + protected function __construct(int $level, array $data) + { + parent::__construct($level); + $this->data = $data; + } + /** + * @psalm-assert-if-true TestWith $this + */ + public function isTestWith(): bool + { + return \true; + } + public function data(): array + { + return $this->data; + } +} + + * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\TextUI; +namespace PHPUnit\Metadata; -use const PHP_EOL; -use const PHP_SAPI; -use const PHP_VERSION; -use function array_diff; -use function array_map; -use function array_merge; -use function assert; -use function class_exists; -use function count; -use function dirname; -use function file_put_contents; -use function htmlspecialchars; -use function is_array; -use function is_int; -use function is_string; -use function mt_srand; -use function range; -use function realpath; -use function sort; -use function sprintf; -use function time; -use PHPUnit\Framework\Exception; -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\Extension\ExtensionHandler; -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\TextUI\XmlConfiguration\CodeCoverage\FilterMapper; -use PHPUnit\TextUI\XmlConfiguration\Configuration; -use PHPUnit\TextUI\XmlConfiguration\Loader; -use PHPUnit\TextUI\XmlConfiguration\PhpHandler; -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 PHPUnit\Util\Xml\SchemaDetector; -use ReflectionClass; -use ReflectionException; -use PHPUnitPHAR\SebastianBergmann\CodeCoverage\CodeCoverage; -use PHPUnitPHAR\SebastianBergmann\CodeCoverage\Driver\Selector; -use PHPUnitPHAR\SebastianBergmann\CodeCoverage\Exception as CodeCoverageException; -use PHPUnitPHAR\SebastianBergmann\CodeCoverage\Filter as CodeCoverageFilter; -use PHPUnitPHAR\SebastianBergmann\CodeCoverage\Report\Clover as CloverReport; -use PHPUnitPHAR\SebastianBergmann\CodeCoverage\Report\Cobertura as CoberturaReport; -use PHPUnitPHAR\SebastianBergmann\CodeCoverage\Report\Crap4j as Crap4jReport; -use PHPUnitPHAR\SebastianBergmann\CodeCoverage\Report\Html\Facade as HtmlReport; -use PHPUnitPHAR\SebastianBergmann\CodeCoverage\Report\PHP as PhpReport; -use PHPUnitPHAR\SebastianBergmann\CodeCoverage\Report\Text as TextReport; -use PHPUnitPHAR\SebastianBergmann\CodeCoverage\Report\Xml\Facade as XmlReport; -use PHPUnitPHAR\SebastianBergmann\Comparator\Comparator; -use PHPUnitPHAR\SebastianBergmann\Environment\Runtime; -use PHPUnitPHAR\SebastianBergmann\Invoker\Invoker; -use PHPUnitPHAR\SebastianBergmann\Timer\Timer; /** - * @internal This class is not covered by the backward compatibility promise for PHPUnit + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -final class TestRunner extends BaseTestRunner +final class Uses extends \PHPUnit\Metadata\Metadata { - public const SUCCESS_EXIT = 0; - public const FAILURE_EXIT = 1; - public const EXCEPTION_EXIT = 2; - /** - * @var CodeCoverageFilter - */ - private $codeCoverageFilter; - /** - * @var TestSuiteLoader - */ - private $loader; - /** - * @var ResultPrinter - */ - private $printer; - /** - * @var bool - */ - private $messagePrinted = \false; /** - * @var Hook[] + * @psalm-var non-empty-string */ - private $extensions = []; + private readonly string $target; /** - * @var Timer + * @psalm-param 0|1 $level + * @psalm-param non-empty-string $target */ - private $timer; - public function __construct(?TestSuiteLoader $loader = null, ?CodeCoverageFilter $filter = null) + protected function __construct(int $level, string $target) { - if ($filter === null) { - $filter = new CodeCoverageFilter(); - } - $this->codeCoverageFilter = $filter; - $this->loader = $loader; - $this->timer = new Timer(); + parent::__construct($level); + $this->target = $target; } /** - * @throws \PHPUnit\Runner\Exception - * @throws Exception - * @throws XmlConfiguration\Exception + * @psalm-assert-if-true Uses $this */ - public function run(TestSuite $suite, array $arguments = [], array $warnings = [], bool $exit = \true) : TestResult + public function isUses(): bool { - if (isset($arguments['configuration'])) { - $GLOBALS['__PHPUNIT_CONFIGURATION_FILE'] = $arguments['configuration']; - } - $this->handleConfiguration($arguments); - $warnings = array_merge($warnings, $arguments['warnings']); - if (is_int($arguments['columns']) && $arguments['columns'] < 16) { - $arguments['columns'] = 16; - $tooFewColumnsRequested = \true; - } - if (isset($arguments['bootstrap'])) { - $GLOBALS['__PHPUNIT_BOOTSTRAP'] = $arguments['bootstrap']; - } - 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['configurationObject'])) { - assert($arguments['configurationObject'] instanceof Configuration); - $cacheLocation = $arguments['configurationObject']->filename(); - } 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(\true); - } - 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 \PHPUnit\TextUI\ResultPrinter) { - $this->printer = $arguments['printer']; - } elseif (is_string($arguments['printer']) && class_exists($arguments['printer'], \false)) { - try { - $reflector = new ReflectionClass($arguments['printer']); - if ($reflector->implementsInterface(\PHPUnit\TextUI\ResultPrinter::class)) { - $this->printer = $this->createPrinter($arguments['printer'], $arguments); - } - // @codeCoverageIgnoreStart - } catch (ReflectionException $e) { - throw new Exception($e->getMessage(), $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd - } - } else { - $this->printer = $this->createPrinter(\PHPUnit\TextUI\DefaultResultPrinter::class, $arguments); - } - } - if (isset($originalExecutionOrder) && $this->printer instanceof CliTestDoxPrinter) { - assert($this->printer instanceof CliTestDoxPrinter); - $this->printer->setOriginalExecutionOrder($originalExecutionOrder); - $this->printer->setShowProgressAnimation(!$arguments['noInteraction']); - } - $this->write(Version::getVersionString() . "\n"); - foreach ($arguments['listeners'] as $listener) { - $result->addListener($listener); - } - $result->addListener($this->printer); - $coverageFilterFromConfigurationFile = \false; - $coverageFilterFromOption = \false; - $codeCoverageReports = 0; - 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['coverageCobertura'])) { - $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 ($codeCoverageReports > 0 || isset($arguments['xdebugFilterFile'])) { - if (isset($arguments['coverageFilter'])) { - if (!is_array($arguments['coverageFilter'])) { - $coverageFilterDirectories = [$arguments['coverageFilter']]; - } else { - $coverageFilterDirectories = $arguments['coverageFilter']; - } - foreach ($coverageFilterDirectories as $coverageFilterDirectory) { - $this->codeCoverageFilter->includeDirectory($coverageFilterDirectory); - } - $coverageFilterFromOption = \true; - } - if (isset($arguments['configurationObject'])) { - assert($arguments['configurationObject'] instanceof Configuration); - $codeCoverageConfiguration = $arguments['configurationObject']->codeCoverage(); - if ($codeCoverageConfiguration->hasNonEmptyListOfFilesToBeIncludedInCodeCoverageReport()) { - $coverageFilterFromConfigurationFile = \true; - (new FilterMapper())->map($this->codeCoverageFilter, $codeCoverageConfiguration); - } - } - } - if ($codeCoverageReports > 0) { - try { - if (isset($codeCoverageConfiguration) && ($codeCoverageConfiguration->pathCoverage() || isset($arguments['pathCoverage']) && $arguments['pathCoverage'] === \true)) { - $codeCoverageDriver = (new Selector())->forLineAndPathCoverage($this->codeCoverageFilter); - } else { - $codeCoverageDriver = (new Selector())->forLineCoverage($this->codeCoverageFilter); - } - $codeCoverage = new CodeCoverage($codeCoverageDriver, $this->codeCoverageFilter); - if (isset($codeCoverageConfiguration) && $codeCoverageConfiguration->hasCacheDirectory()) { - $codeCoverage->cacheStaticAnalysis($codeCoverageConfiguration->cacheDirectory()->path()); - } - if (isset($arguments['coverageCacheDirectory'])) { - $codeCoverage->cacheStaticAnalysis($arguments['coverageCacheDirectory']); - } - $codeCoverage->excludeSubclassesOfThisClassFromUnintentionallyCoveredCodeCheck(Comparator::class); - if ($arguments['strictCoverage']) { - $codeCoverage->enableCheckForUnintentionallyCoveredCode(); - } - if (isset($arguments['ignoreDeprecatedCodeUnitsFromCodeCoverage'])) { - if ($arguments['ignoreDeprecatedCodeUnitsFromCodeCoverage']) { - $codeCoverage->ignoreDeprecatedCode(); - } else { - $codeCoverage->doNotIgnoreDeprecatedCode(); - } - } - if (isset($arguments['disableCodeCoverageIgnore'])) { - if ($arguments['disableCodeCoverageIgnore']) { - $codeCoverage->disableAnnotationsForIgnoringCode(); - } else { - $codeCoverage->enableAnnotationsForIgnoringCode(); - } - } - if (isset($arguments['configurationObject'])) { - $codeCoverageConfiguration = $arguments['configurationObject']->codeCoverage(); - if ($codeCoverageConfiguration->hasNonEmptyListOfFilesToBeIncludedInCodeCoverageReport()) { - if ($codeCoverageConfiguration->includeUncoveredFiles()) { - $codeCoverage->includeUncoveredFiles(); - } else { - $codeCoverage->excludeUncoveredFiles(); - } - if ($codeCoverageConfiguration->processUncoveredFiles()) { - $codeCoverage->processUncoveredFiles(); - } else { - $codeCoverage->doNotProcessUncoveredFiles(); - } - } - } - if ($this->codeCoverageFilter->isEmpty()) { - if (!$coverageFilterFromConfigurationFile && !$coverageFilterFromOption) { - $warnings[] = 'No filter is configured, code coverage will not be processed'; - } else { - $warnings[] = 'Incorrect filter configuration, code coverage will not be processed'; - } - unset($codeCoverage); - } - } catch (CodeCoverageException $e) { - $warnings[] = $e->getMessage(); - } - } - if ($arguments['verbose']) { - if (PHP_SAPI === 'phpdbg') { - $this->writeMessage('Runtime', 'PHPDBG ' . PHP_VERSION); - } else { - $runtime = 'PHP ' . PHP_VERSION; - if (isset($codeCoverageDriver)) { - $runtime .= ' with ' . $codeCoverageDriver->nameAndVersion(); - } - $this->writeMessage('Runtime', $runtime); - } - if (isset($arguments['configurationObject'])) { - assert($arguments['configurationObject'] instanceof Configuration); - $this->writeMessage('Configuration', $arguments['configurationObject']->filename()); - } - 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)) { - $warnings[] = 'Less than 16 columns requested, number of columns set to 16'; - } - if ((new Runtime())->discardsComments()) { - $warnings[] = 'opcache.save_comments=0 set; annotations will not work'; - } - if (isset($arguments['conflictBetweenPrinterClassAndTestdox'])) { - $warnings[] = 'Directives printerClass and testdox are mutually exclusive'; - } - $warnings = array_merge($warnings, $suite->warnings()); - sort($warnings); - foreach ($warnings as $warning) { - $this->writeMessage('Warning', $warning); - } - if (isset($arguments['configurationObject'])) { - assert($arguments['configurationObject'] instanceof Configuration); - if ($arguments['configurationObject']->hasValidationErrors()) { - if ((new SchemaDetector())->detect($arguments['configurationObject']->filename())->detected()) { - $this->writeMessage('Warning', 'Your XML configuration validates against a deprecated schema.'); - $this->writeMessage('Suggestion', 'Migrate your XML configuration using "--migrate-configuration"!'); - } else { - $this->write("\n Warning - The configuration file did not pass validation!\n The following problems have been detected:\n"); - $this->write($arguments['configurationObject']->validationErrors()); - $this->write("\n Test results may not be as expected.\n\n"); - } - } - } - if (isset($arguments['xdebugFilterFile'], $codeCoverageConfiguration)) { - $this->write(PHP_EOL . 'Please note that --dump-xdebug-filter and --prepend are deprecated and will be removed in PHPUnit 10.' . PHP_EOL); - $script = (new XdebugFilterScriptGenerator())->generate($codeCoverageConfiguration); - 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 . PHP_EOL, $arguments['xdebugFilterFile'])); - exit(self::SUCCESS_EXIT); - } - $this->write("\n"); - if (isset($codeCoverage)) { - $result->setCodeCoverage($codeCoverage); - } - $result->beStrictAboutTestsThatDoNotTestAnything($arguments['reportUselessTests']); - $result->beStrictAboutOutputDuringTests($arguments['disallowTestOutput']); - $result->beStrictAboutTodoAnnotatedTests($arguments['disallowTodoAnnotatedTests']); - $result->beStrictAboutResourceUsageDuringSmallTests($arguments['beStrictAboutResourceUsageDuringSmallTests']); - if ($arguments['enforceTimeLimit'] === \true && !(new Invoker())->canInvokeWithTimeout()) { - $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 (isset($arguments['forceCoversAnnotation']) && $arguments['forceCoversAnnotation'] === \true) { - $result->forceCoversAnnotation(); - } - $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(); - $this->printer->printResult($result); - if (isset($codeCoverage)) { - 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['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['coverageCobertura'])) { - $this->codeCoverageGenerationStart('Cobertura XML'); - try { - $writer = new CoberturaReport(); - $writer->process($codeCoverage, $arguments['coverageCobertura']); - $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 PHPUnit %s', Version::id())); - $writer->process($codeCoverage, $arguments['coverageHtml']); - $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'] !== \PHPUnit\TextUI\DefaultResultPrinter::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 (isset($arguments['failOnEmptyTestSuite']) && $arguments['failOnEmptyTestSuite'] === \true && count($result) === 0) { - exit(self::FAILURE_EXIT); - } - if ($result->wasSuccessfulIgnoringWarnings()) { - if ($arguments['failOnRisky'] && !$result->allHarmless()) { - exit(self::FAILURE_EXIT); - } - if ($arguments['failOnWarning'] && $result->warningCount() > 0) { - exit(self::FAILURE_EXIT); - } - if ($arguments['failOnIncomplete'] && $result->notImplementedCount() > 0) { - exit(self::FAILURE_EXIT); - } - if ($arguments['failOnSkipped'] && $result->skippedCount() > 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; + return \true; } /** - * Returns the loader to be used. + * @psalm-return non-empty-string */ - public function getLoader() : TestSuiteLoader - { - if ($this->loader === null) { - $this->loader = new StandardTestSuiteLoader(); - } - return $this->loader; - } - public function addExtension(Hook $extension) : void + public function target(): string { - $this->extensions[] = $extension; + return $this->target; } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Metadata; + +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class UsesClass extends \PHPUnit\Metadata\Metadata +{ /** - * Override to define how to handle a failed loading of - * a test suite. + * @psalm-var class-string */ - 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; - } - } + private readonly string $className; /** - * @throws Exception - * @throws XmlConfiguration\Exception + * @psalm-param 0|1 $level + * @psalm-param class-string $className */ - private function handleConfiguration(array &$arguments) : void - { - if (!isset($arguments['configurationObject']) && isset($arguments['configuration'])) { - $arguments['configurationObject'] = (new Loader())->load($arguments['configuration']); - } - if (!isset($arguments['warnings'])) { - $arguments['warnings'] = []; - } - $arguments['debug'] = $arguments['debug'] ?? \false; - $arguments['filter'] = $arguments['filter'] ?? \false; - $arguments['listeners'] = $arguments['listeners'] ?? []; - if (isset($arguments['configurationObject'])) { - (new PhpHandler())->handle($arguments['configurationObject']->php()); - $codeCoverageConfiguration = $arguments['configurationObject']->codeCoverage(); - if (!isset($arguments['noCoverage'])) { - if (!isset($arguments['coverageClover']) && $codeCoverageConfiguration->hasClover()) { - $arguments['coverageClover'] = $codeCoverageConfiguration->clover()->target()->path(); - } - if (!isset($arguments['coverageCobertura']) && $codeCoverageConfiguration->hasCobertura()) { - $arguments['coverageCobertura'] = $codeCoverageConfiguration->cobertura()->target()->path(); - } - if (!isset($arguments['coverageCrap4J']) && $codeCoverageConfiguration->hasCrap4j()) { - $arguments['coverageCrap4J'] = $codeCoverageConfiguration->crap4j()->target()->path(); - if (!isset($arguments['crap4jThreshold'])) { - $arguments['crap4jThreshold'] = $codeCoverageConfiguration->crap4j()->threshold(); - } - } - if (!isset($arguments['coverageHtml']) && $codeCoverageConfiguration->hasHtml()) { - $arguments['coverageHtml'] = $codeCoverageConfiguration->html()->target()->path(); - if (!isset($arguments['reportLowUpperBound'])) { - $arguments['reportLowUpperBound'] = $codeCoverageConfiguration->html()->lowUpperBound(); - } - if (!isset($arguments['reportHighLowerBound'])) { - $arguments['reportHighLowerBound'] = $codeCoverageConfiguration->html()->highLowerBound(); - } - } - if (!isset($arguments['coveragePHP']) && $codeCoverageConfiguration->hasPhp()) { - $arguments['coveragePHP'] = $codeCoverageConfiguration->php()->target()->path(); - } - if (!isset($arguments['coverageText']) && $codeCoverageConfiguration->hasText()) { - $arguments['coverageText'] = $codeCoverageConfiguration->text()->target()->path(); - $arguments['coverageTextShowUncoveredFiles'] = $codeCoverageConfiguration->text()->showUncoveredFiles(); - $arguments['coverageTextShowOnlySummary'] = $codeCoverageConfiguration->text()->showOnlySummary(); - } - if (!isset($arguments['coverageXml']) && $codeCoverageConfiguration->hasXml()) { - $arguments['coverageXml'] = $codeCoverageConfiguration->xml()->target()->path(); - } - } - $phpunitConfiguration = $arguments['configurationObject']->phpunit(); - $arguments['backupGlobals'] = $arguments['backupGlobals'] ?? $phpunitConfiguration->backupGlobals(); - $arguments['backupStaticAttributes'] = $arguments['backupStaticAttributes'] ?? $phpunitConfiguration->backupStaticAttributes(); - $arguments['beStrictAboutChangesToGlobalState'] = $arguments['beStrictAboutChangesToGlobalState'] ?? $phpunitConfiguration->beStrictAboutChangesToGlobalState(); - $arguments['cacheResult'] = $arguments['cacheResult'] ?? $phpunitConfiguration->cacheResult(); - $arguments['colors'] = $arguments['colors'] ?? $phpunitConfiguration->colors(); - $arguments['convertDeprecationsToExceptions'] = $arguments['convertDeprecationsToExceptions'] ?? $phpunitConfiguration->convertDeprecationsToExceptions(); - $arguments['convertErrorsToExceptions'] = $arguments['convertErrorsToExceptions'] ?? $phpunitConfiguration->convertErrorsToExceptions(); - $arguments['convertNoticesToExceptions'] = $arguments['convertNoticesToExceptions'] ?? $phpunitConfiguration->convertNoticesToExceptions(); - $arguments['convertWarningsToExceptions'] = $arguments['convertWarningsToExceptions'] ?? $phpunitConfiguration->convertWarningsToExceptions(); - $arguments['processIsolation'] = $arguments['processIsolation'] ?? $phpunitConfiguration->processIsolation(); - $arguments['stopOnDefect'] = $arguments['stopOnDefect'] ?? $phpunitConfiguration->stopOnDefect(); - $arguments['stopOnError'] = $arguments['stopOnError'] ?? $phpunitConfiguration->stopOnError(); - $arguments['stopOnFailure'] = $arguments['stopOnFailure'] ?? $phpunitConfiguration->stopOnFailure(); - $arguments['stopOnWarning'] = $arguments['stopOnWarning'] ?? $phpunitConfiguration->stopOnWarning(); - $arguments['stopOnIncomplete'] = $arguments['stopOnIncomplete'] ?? $phpunitConfiguration->stopOnIncomplete(); - $arguments['stopOnRisky'] = $arguments['stopOnRisky'] ?? $phpunitConfiguration->stopOnRisky(); - $arguments['stopOnSkipped'] = $arguments['stopOnSkipped'] ?? $phpunitConfiguration->stopOnSkipped(); - $arguments['failOnEmptyTestSuite'] = $arguments['failOnEmptyTestSuite'] ?? $phpunitConfiguration->failOnEmptyTestSuite(); - $arguments['failOnIncomplete'] = $arguments['failOnIncomplete'] ?? $phpunitConfiguration->failOnIncomplete(); - $arguments['failOnRisky'] = $arguments['failOnRisky'] ?? $phpunitConfiguration->failOnRisky(); - $arguments['failOnSkipped'] = $arguments['failOnSkipped'] ?? $phpunitConfiguration->failOnSkipped(); - $arguments['failOnWarning'] = $arguments['failOnWarning'] ?? $phpunitConfiguration->failOnWarning(); - $arguments['enforceTimeLimit'] = $arguments['enforceTimeLimit'] ?? $phpunitConfiguration->enforceTimeLimit(); - $arguments['defaultTimeLimit'] = $arguments['defaultTimeLimit'] ?? $phpunitConfiguration->defaultTimeLimit(); - $arguments['timeoutForSmallTests'] = $arguments['timeoutForSmallTests'] ?? $phpunitConfiguration->timeoutForSmallTests(); - $arguments['timeoutForMediumTests'] = $arguments['timeoutForMediumTests'] ?? $phpunitConfiguration->timeoutForMediumTests(); - $arguments['timeoutForLargeTests'] = $arguments['timeoutForLargeTests'] ?? $phpunitConfiguration->timeoutForLargeTests(); - $arguments['reportUselessTests'] = $arguments['reportUselessTests'] ?? $phpunitConfiguration->beStrictAboutTestsThatDoNotTestAnything(); - $arguments['strictCoverage'] = $arguments['strictCoverage'] ?? $phpunitConfiguration->beStrictAboutCoversAnnotation(); - $arguments['ignoreDeprecatedCodeUnitsFromCodeCoverage'] = $arguments['ignoreDeprecatedCodeUnitsFromCodeCoverage'] ?? $codeCoverageConfiguration->ignoreDeprecatedCodeUnits(); - $arguments['disallowTestOutput'] = $arguments['disallowTestOutput'] ?? $phpunitConfiguration->beStrictAboutOutputDuringTests(); - $arguments['disallowTodoAnnotatedTests'] = $arguments['disallowTodoAnnotatedTests'] ?? $phpunitConfiguration->beStrictAboutTodoAnnotatedTests(); - $arguments['beStrictAboutResourceUsageDuringSmallTests'] = $arguments['beStrictAboutResourceUsageDuringSmallTests'] ?? $phpunitConfiguration->beStrictAboutResourceUsageDuringSmallTests(); - $arguments['verbose'] = $arguments['verbose'] ?? $phpunitConfiguration->verbose(); - $arguments['reverseDefectList'] = $arguments['reverseDefectList'] ?? $phpunitConfiguration->reverseDefectList(); - $arguments['forceCoversAnnotation'] = $arguments['forceCoversAnnotation'] ?? $phpunitConfiguration->forceCoversAnnotation(); - $arguments['disableCodeCoverageIgnore'] = $arguments['disableCodeCoverageIgnore'] ?? $codeCoverageConfiguration->disableCodeCoverageIgnore(); - $arguments['registerMockObjectsFromTestArgumentsRecursively'] = $arguments['registerMockObjectsFromTestArgumentsRecursively'] ?? $phpunitConfiguration->registerMockObjectsFromTestArgumentsRecursively(); - $arguments['noInteraction'] = $arguments['noInteraction'] ?? $phpunitConfiguration->noInteraction(); - $arguments['executionOrder'] = $arguments['executionOrder'] ?? $phpunitConfiguration->executionOrder(); - $arguments['resolveDependencies'] = $arguments['resolveDependencies'] ?? $phpunitConfiguration->resolveDependencies(); - if (!isset($arguments['bootstrap']) && $phpunitConfiguration->hasBootstrap()) { - $arguments['bootstrap'] = $phpunitConfiguration->bootstrap(); - } - if (!isset($arguments['cacheResultFile']) && $phpunitConfiguration->hasCacheResultFile()) { - $arguments['cacheResultFile'] = $phpunitConfiguration->cacheResultFile(); - } - if (!isset($arguments['executionOrderDefects'])) { - $arguments['executionOrderDefects'] = $phpunitConfiguration->defectsFirst() ? TestSuiteSorter::ORDER_DEFECTS_FIRST : TestSuiteSorter::ORDER_DEFAULT; - } - if ($phpunitConfiguration->conflictBetweenPrinterClassAndTestdox()) { - $arguments['conflictBetweenPrinterClassAndTestdox'] = \true; - } - $groupCliArgs = []; - if (!empty($arguments['groups'])) { - $groupCliArgs = $arguments['groups']; - } - $groupConfiguration = $arguments['configurationObject']->groups(); - if (!isset($arguments['groups']) && $groupConfiguration->hasInclude()) { - $arguments['groups'] = $groupConfiguration->include()->asArrayOfStrings(); - } - if (!isset($arguments['excludeGroups']) && $groupConfiguration->hasExclude()) { - $arguments['excludeGroups'] = array_diff($groupConfiguration->exclude()->asArrayOfStrings(), $groupCliArgs); - } - if (!isset($arguments['noExtensions'])) { - $extensionHandler = new ExtensionHandler(); - foreach ($arguments['configurationObject']->extensions() as $extension) { - $extensionHandler->registerExtension($extension, $this); - } - foreach ($arguments['configurationObject']->listeners() as $listener) { - $arguments['listeners'][] = $extensionHandler->createTestListenerInstance($listener); - } - unset($extensionHandler); - } - foreach ($arguments['unavailableExtensions'] as $extension) { - $arguments['warnings'][] = sprintf('Extension "%s" is not available', $extension); - } - $loggingConfiguration = $arguments['configurationObject']->logging(); - if (!isset($arguments['noLogging'])) { - if ($loggingConfiguration->hasText()) { - $arguments['listeners'][] = new \PHPUnit\TextUI\DefaultResultPrinter($loggingConfiguration->text()->target()->path(), \true); - } - if (!isset($arguments['teamcityLogfile']) && $loggingConfiguration->hasTeamCity()) { - $arguments['teamcityLogfile'] = $loggingConfiguration->teamCity()->target()->path(); - } - if (!isset($arguments['junitLogfile']) && $loggingConfiguration->hasJunit()) { - $arguments['junitLogfile'] = $loggingConfiguration->junit()->target()->path(); - } - if (!isset($arguments['testdoxHTMLFile']) && $loggingConfiguration->hasTestDoxHtml()) { - $arguments['testdoxHTMLFile'] = $loggingConfiguration->testDoxHtml()->target()->path(); - } - if (!isset($arguments['testdoxTextFile']) && $loggingConfiguration->hasTestDoxText()) { - $arguments['testdoxTextFile'] = $loggingConfiguration->testDoxText()->target()->path(); - } - if (!isset($arguments['testdoxXMLFile']) && $loggingConfiguration->hasTestDoxXml()) { - $arguments['testdoxXMLFile'] = $loggingConfiguration->testDoxXml()->target()->path(); - } - } - $testdoxGroupConfiguration = $arguments['configurationObject']->testdoxGroups(); - if (!isset($arguments['testdoxGroups']) && $testdoxGroupConfiguration->hasInclude()) { - $arguments['testdoxGroups'] = $testdoxGroupConfiguration->include()->asArrayOfStrings(); - } - if (!isset($arguments['testdoxExcludeGroups']) && $testdoxGroupConfiguration->hasExclude()) { - $arguments['testdoxExcludeGroups'] = $testdoxGroupConfiguration->exclude()->asArrayOfStrings(); - } - } - $extensionHandler = new ExtensionHandler(); - foreach ($arguments['extensions'] as $extension) { - $extensionHandler->registerExtension($extension, $this); - } - unset($extensionHandler); - $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['colors'] = $arguments['colors'] ?? \PHPUnit\TextUI\DefaultResultPrinter::COLOR_DEFAULT; - $arguments['columns'] = $arguments['columns'] ?? 80; - $arguments['convertDeprecationsToExceptions'] = $arguments['convertDeprecationsToExceptions'] ?? \false; - $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['failOnIncomplete'] = $arguments['failOnIncomplete'] ?? \false; - $arguments['failOnRisky'] = $arguments['failOnRisky'] ?? \false; - $arguments['failOnSkipped'] = $arguments['failOnSkipped'] ?? \false; - $arguments['failOnWarning'] = $arguments['failOnWarning'] ?? \false; - $arguments['groups'] = $arguments['groups'] ?? []; - $arguments['noInteraction'] = $arguments['noInteraction'] ?? \false; - $arguments['processIsolation'] = $arguments['processIsolation'] ?? \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; - if ($arguments['reportLowUpperBound'] > $arguments['reportHighLowerBound']) { - $arguments['reportLowUpperBound'] = 50; - $arguments['reportHighLowerBound'] = 90; - } - } - private function processSuiteFilters(TestSuite $suite, array $arguments) : void - { - if (!$arguments['filter'] && empty($arguments['groups']) && empty($arguments['excludeGroups']) && empty($arguments['testsCovering']) && empty($arguments['testsUsing'])) { - 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 (!empty($arguments['testsCovering'])) { - $filterFactory->addFilter(new ReflectionClass(IncludeGroupFilterIterator::class), array_map(static function (string $name) : string { - return '__phpunit_covers_' . $name; - }, $arguments['testsCovering'])); - } - if (!empty($arguments['testsUsing'])) { - $filterFactory->addFilter(new ReflectionClass(IncludeGroupFilterIterator::class), array_map(static function (string $name) : string { - return '__phpunit_uses_' . $name; - }, $arguments['testsUsing'])); - } - 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) : \PHPUnit\TextUI\ResultPrinter + protected function __construct(int $level, string $className) { - $object = new $class(isset($arguments['stderr']) && $arguments['stderr'] === \true ? 'php://stderr' : null, $arguments['verbose'], $arguments['colors'], $arguments['debug'], $arguments['columns'], $arguments['reverseList']); - assert($object instanceof \PHPUnit\TextUI\ResultPrinter); - return $object; + parent::__construct($level); + $this->className = $className; } - private function codeCoverageGenerationStart(string $format) : void + /** + * @psalm-assert-if-true UsesClass $this + */ + public function isUsesClass(): bool { - $this->write(sprintf("\nGenerating code coverage report in %s format ... ", $format)); - $this->timer->start(); + return \true; } - private function codeCoverageGenerationSucceeded() : void + /** + * @psalm-return class-string + */ + public function className(): string { - $this->write(sprintf("done [%s]\n", $this->timer->stop()->asString())); + return $this->className; } - private function codeCoverageGenerationFailed(\Exception $e) : void + /** + * @psalm-return class-string + * + * @internal This method is not covered by the backward compatibility promise for PHPUnit + */ + public function asStringForCodeUnitMapper(): string { - $this->write(sprintf("failed [%s]\n%s\n", $this->timer->stop()->asString(), $e->getMessage())); + return $this->className; } } name(), $filterAsArray, \true)) { - continue; - } - $testSuite = new TestSuiteObject($testSuiteConfiguration->name()); - $testSuiteEmpty = \true; - $exclude = []; - foreach ($testSuiteConfiguration->exclude()->asArray() as $file) { - $exclude[] = $file->path(); - } - foreach ($testSuiteConfiguration->directories() as $directory) { - if (!version_compare(PHP_VERSION, $directory->phpVersion(), $directory->phpVersionOperator()->asString())) { - continue; - } - $files = (new Facade())->getFilesAsArray($directory->path(), $directory->suffix(), $directory->prefix(), $exclude); - if (!empty($files)) { - $testSuite->addTestFiles($files); - $testSuiteEmpty = \false; - } elseif (strpos($directory->path(), '*') === \false && !is_dir($directory->path())) { - throw new \PHPUnit\TextUI\TestDirectoryNotFoundException($directory->path()); - } - } - foreach ($testSuiteConfiguration->files() as $file) { - if (!is_file($file->path())) { - throw new \PHPUnit\TextUI\TestFileNotFoundException($file->path()); - } - if (!version_compare(PHP_VERSION, $file->phpVersion(), $file->phpVersionOperator()->asString())) { - continue; - } - $testSuite->addTestFile($file->path()); - $testSuiteEmpty = \false; - } - if (!$testSuiteEmpty) { - $result->addTest($testSuite); - } - } - return $result; - } catch (FrameworkException $e) { - throw new \PHPUnit\TextUI\RuntimeException($e->getMessage(), $e->getCode(), $e); - } + parent::__construct($level); + $this->className = $className; + } + /** + * @psalm-assert-if-true UsesDefaultClass $this + */ + public function isUsesDefaultClass(): bool + { + return \true; + } + /** + * @psalm-return class-string + */ + public function className(): string + { + return $this->className; } } cacheDirectory = $cacheDirectory; - $this->directories = $directories; - $this->files = $files; - $this->excludeDirectories = $excludeDirectories; - $this->excludeFiles = $excludeFiles; - $this->pathCoverage = $pathCoverage; - $this->includeUncoveredFiles = $includeUncoveredFiles; - $this->processUncoveredFiles = $processUncoveredFiles; - $this->ignoreDeprecatedCodeUnits = $ignoreDeprecatedCodeUnits; - $this->disableCodeCoverageIgnore = $disableCodeCoverageIgnore; - $this->clover = $clover; - $this->cobertura = $cobertura; - $this->crap4j = $crap4j; - $this->html = $html; - $this->php = $php; - $this->text = $text; - $this->xml = $xml; + parent::__construct($level); + $this->functionName = $functionName; } /** - * @psalm-assert-if-true !null $this->cacheDirectory + * @psalm-assert-if-true UsesFunction $this */ - public function hasCacheDirectory() : bool + public function isUsesFunction(): bool { - return $this->cacheDirectory !== null; + return \true; } /** - * @throws Exception + * @psalm-return non-empty-string */ - public function cacheDirectory() : Directory - { - if (!$this->hasCacheDirectory()) { - throw new Exception('No cache directory has been configured'); - } - return $this->cacheDirectory; - } - public function hasNonEmptyListOfFilesToBeIncludedInCodeCoverageReport() : bool + public function functionName(): string { - return count($this->directories) > 0 || count($this->files) > 0; + return $this->functionName; } - public function directories() : DirectoryCollection + /** + * @internal This method is not covered by the backward compatibility promise for PHPUnit + */ + public function asStringForCodeUnitMapper(): string { - return $this->directories; + return '::' . $this->functionName; } - public function files() : FileCollection +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Metadata\Version; + +use function version_compare; +use PHPUnit\Util\VersionComparisonOperator; +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class ComparisonRequirement extends \PHPUnit\Metadata\Version\Requirement +{ + private readonly string $version; + private readonly VersionComparisonOperator $operator; + public function __construct(string $version, VersionComparisonOperator $operator) { - return $this->files; + $this->version = $version; + $this->operator = $operator; } - public function excludeDirectories() : DirectoryCollection + public function isSatisfiedBy(string $version): bool { - return $this->excludeDirectories; + return version_compare($version, $this->version, $this->operator->asString()); } - public function excludeFiles() : FileCollection + public function asString(): string { - return $this->excludeFiles; + return $this->operator->asString() . ' ' . $this->version; } - public function pathCoverage() : bool +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Metadata\Version; + +use function preg_replace; +use PHPUnitPHAR\PharIo\Version\Version; +use PHPUnitPHAR\PharIo\Version\VersionConstraint; +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class ConstraintRequirement extends \PHPUnit\Metadata\Version\Requirement +{ + private readonly VersionConstraint $constraint; + public function __construct(VersionConstraint $constraint) { - return $this->pathCoverage; + $this->constraint = $constraint; } - public function includeUncoveredFiles() : bool + /** + * @psalm-suppress ImpureMethodCall + */ + public function isSatisfiedBy(string $version): bool { - return $this->includeUncoveredFiles; + return $this->constraint->complies(new Version($this->sanitize($version))); } - public function ignoreDeprecatedCodeUnits() : bool + /** + * @psalm-suppress ImpureMethodCall + */ + public function asString(): string { - return $this->ignoreDeprecatedCodeUnits; + return $this->constraint->asString(); } - public function disableCodeCoverageIgnore() : bool + private function sanitize(string $version): string { - return $this->disableCodeCoverageIgnore; - } - public function processUncoveredFiles() : bool - { - return $this->processUncoveredFiles; - } - /** - * @psalm-assert-if-true !null $this->clover - */ - public function hasClover() : bool - { - return $this->clover !== null; - } - /** - * @throws Exception - */ - public function clover() : Clover - { - if (!$this->hasClover()) { - throw new Exception('Code Coverage report "Clover XML" has not been configured'); - } - return $this->clover; - } - /** - * @psalm-assert-if-true !null $this->cobertura - */ - public function hasCobertura() : bool - { - return $this->cobertura !== null; - } - /** - * @throws Exception - */ - public function cobertura() : Cobertura - { - if (!$this->hasCobertura()) { - throw new Exception('Code Coverage report "Cobertura XML" has not been configured'); - } - return $this->cobertura; - } - /** - * @psalm-assert-if-true !null $this->crap4j - */ - public function hasCrap4j() : bool - { - return $this->crap4j !== null; - } - /** - * @throws Exception - */ - public function crap4j() : Crap4j - { - if (!$this->hasCrap4j()) { - throw new Exception('Code Coverage report "Crap4J" has not been configured'); - } - return $this->crap4j; - } - /** - * @psalm-assert-if-true !null $this->html - */ - public function hasHtml() : bool - { - return $this->html !== null; - } - /** - * @throws Exception - */ - public function html() : Html - { - if (!$this->hasHtml()) { - throw new Exception('Code Coverage report "HTML" has not been configured'); - } - return $this->html; - } - /** - * @psalm-assert-if-true !null $this->php - */ - public function hasPhp() : bool - { - return $this->php !== null; - } - /** - * @throws Exception - */ - public function php() : Php - { - if (!$this->hasPhp()) { - throw new Exception('Code Coverage report "PHP" has not been configured'); - } - return $this->php; - } - /** - * @psalm-assert-if-true !null $this->text - */ - public function hasText() : bool - { - return $this->text !== null; - } - /** - * @throws Exception - */ - public function text() : Text - { - if (!$this->hasText()) { - throw new Exception('Code Coverage report "Text" has not been configured'); - } - return $this->text; - } - /** - * @psalm-assert-if-true !null $this->xml - */ - public function hasXml() : bool - { - return $this->xml !== null; - } - /** - * @throws Exception - */ - public function xml() : Xml - { - if (!$this->hasXml()) { - throw new Exception('Code Coverage report "XML" has not been configured'); - } - return $this->xml; + return preg_replace('/^(\d+\.\d+(?:.\d+)?).*$/', '$1', $version); } } [<>=!]{0,2})\s*(?P[\d\.-]+(dev|(RC|alpha|beta)[\d\.])?)[ \t]*\r?$/m'; /** - * @var string - */ - private $path; - /** - * @var string - */ - private $prefix; - /** - * @var string - */ - private $suffix; - /** - * @var string + * @throws InvalidVersionOperatorException + * @throws InvalidVersionRequirementException */ - private $group; - public function __construct(string $path, string $prefix, string $suffix, string $group) - { - $this->path = $path; - $this->prefix = $prefix; - $this->suffix = $suffix; - $this->group = $group; - } - public function path() : string - { - return $this->path; - } - public function prefix() : string - { - return $this->prefix; - } - public function suffix() : string - { - return $this->suffix; - } - public function group() : string + public static function from(string $versionRequirement): self { - return $this->group; + try { + return new \PHPUnit\Metadata\Version\ConstraintRequirement((new VersionConstraintParser())->parse($versionRequirement)); + } catch (UnsupportedVersionConstraintException) { + if (preg_match(self::VERSION_COMPARISON, $versionRequirement, $matches)) { + return new \PHPUnit\Metadata\Version\ComparisonRequirement($matches['version'], new VersionComparisonOperator((!empty($matches['operator'])) ? $matches['operator'] : '>=')); + } + } + throw new InvalidVersionRequirementException(); } + abstract public function isSatisfiedBy(string $version): bool; + abstract public function asString(): string; } + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -final class DirectoryCollection implements Countable, IteratorAggregate +final class WithoutErrorHandler extends \PHPUnit\Metadata\Metadata { /** - * @var Directory[] - */ - private $directories; - /** - * @param Directory[] $directories - */ - public static function fromArray(array $directories) : self - { - return new self(...$directories); - } - private function __construct(\PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Filter\Directory ...$directories) - { - $this->directories = $directories; - } - /** - * @return Directory[] + * @psalm-assert-if-true WithoutErrorHandler $this */ - public function asArray() : array - { - return $this->directories; - } - public function count() : int - { - return count($this->directories); - } - public function getIterator() : \PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Filter\DirectoryCollectionIterator + public function isWithoutErrorHandler(): bool { - return new \PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Filter\DirectoryCollectionIterator($this); + return \true; } } */ -final class DirectoryCollectionIterator implements Countable, Iterator +final class Baseline { + public const VERSION = 1; /** - * @var Directory[] - */ - private $directories; - /** - * @var int + * @psalm-var array>> */ - private $position; - public function __construct(\PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Filter\DirectoryCollection $directories) - { - $this->directories = $directories->asArray(); - } - public function count() : int - { - return iterator_count($this); - } - public function rewind() : void - { - $this->position = 0; - } - public function valid() : bool - { - return $this->position < count($this->directories); - } - public function key() : int + private array $issues = []; + public function add(\PHPUnit\Runner\Baseline\Issue $issue): void { - return $this->position; + if (!isset($this->issues[$issue->file()])) { + $this->issues[$issue->file()] = []; + } + if (!isset($this->issues[$issue->file()][$issue->line()])) { + $this->issues[$issue->file()][$issue->line()] = []; + } + $this->issues[$issue->file()][$issue->line()][] = $issue; } - public function current() : \PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Filter\Directory + public function has(\PHPUnit\Runner\Baseline\Issue $issue): bool { - return $this->directories[$this->position]; + if (!isset($this->issues[$issue->file()][$issue->line()])) { + return \false; + } + foreach ($this->issues[$issue->file()][$issue->line()] as $_issue) { + if ($_issue->equals($issue)) { + return \true; + } + } + return \false; } - public function next() : void + /** + * @psalm-return array>> + */ + public function groupedByFileAndLine(): array { - $this->position++; + return $this->issues; } } directories() as $directory) { - $filter->includeDirectory($directory->path(), $directory->suffix(), $directory->prefix()); - } - foreach ($configuration->files() as $file) { - $filter->includeFile($file->path()); - } - foreach ($configuration->excludeDirectories() as $directory) { - $filter->excludeDirectory($directory->path(), $directory->suffix(), $directory->prefix()); - } - foreach ($configuration->excludeFiles() as $file) { - $filter->excludeFile($file->path()); - } - } } target = $target; - } - public function target() : File + public function __construct(string $file, int $line) { - return $this->target; + parent::__construct(sprintf('File "%s" does not have line %d', $file, $line)); } } target = $target; + $facade->registerSubscribers(new \PHPUnit\Runner\Baseline\TestTriggeredDeprecationSubscriber($this), new \PHPUnit\Runner\Baseline\TestTriggeredNoticeSubscriber($this), new \PHPUnit\Runner\Baseline\TestTriggeredPhpDeprecationSubscriber($this), new \PHPUnit\Runner\Baseline\TestTriggeredPhpNoticeSubscriber($this), new \PHPUnit\Runner\Baseline\TestTriggeredPhpWarningSubscriber($this), new \PHPUnit\Runner\Baseline\TestTriggeredWarningSubscriber($this)); + $this->baseline = new \PHPUnit\Runner\Baseline\Baseline(); + $this->source = $source; } - public function target() : File + public function baseline(): \PHPUnit\Runner\Baseline\Baseline { - return $this->target; + return $this->baseline; + } + /** + * @throws FileDoesNotExistException + * @throws FileDoesNotHaveLineException + */ + public function testTriggeredIssue(DeprecationTriggered|NoticeTriggered|PhpDeprecationTriggered|PhpNoticeTriggered|PhpWarningTriggered|WarningTriggered $event): void + { + if (!$this->source->ignoreSuppressionOfPhpWarnings() && $event->wasSuppressed()) { + return; + } + if ($this->source->restrictWarnings() && !(new SourceFilter())->includes($this->source, $event->file())) { + return; + } + $this->baseline->add(\PHPUnit\Runner\Baseline\Issue::from($event->file(), $event->line(), null, $event->message())); } } target = $target; - $this->threshold = $threshold; + if ($hash === null) { + $hash = self::calculateHash($file, $line); + } + return new self($file, $line, $hash, $description); } - public function target() : File + /** + * @psalm-param non-empty-string $file + * @psalm-param positive-int $line + * @psalm-param non-empty-string $hash + * @psalm-param non-empty-string $description + */ + private function __construct(string $file, int $line, string $hash, string $description) { - return $this->target; + $this->file = $file; + $this->line = $line; + $this->hash = $hash; + $this->description = $description; + } + /** + * @psalm-return non-empty-string + */ + public function file(): string + { + return $this->file; } - public function threshold() : int + /** + * @psalm-return positive-int + */ + public function line(): int { - return $this->threshold; + return $this->line; + } + /** + * @psalm-return non-empty-string + */ + public function hash(): string + { + return $this->hash; + } + /** + * @psalm-return non-empty-string + */ + public function description(): string + { + return $this->description; + } + public function equals(self $other): bool + { + return $this->file() === $other->file() && $this->line() === $other->line() && $this->hash() === $other->hash() && $this->description() === $other->description(); + } + /** + * @psalm-param non-empty-string $file + * @psalm-param positive-int $line + * + * @psalm-return non-empty-string + * + * @throws FileDoesNotExistException + * @throws FileDoesNotHaveLineException + */ + private static function calculateHash(string $file, int $line): string + { + $lines = @file($file, \FILE_IGNORE_NEW_LINES); + if ($lines === \false && !is_file($file)) { + throw new FileDoesNotExistException($file); + } + $key = $line - 1; + if (!isset($lines[$key])) { + throw new \PHPUnit\Runner\Baseline\FileDoesNotHaveLineException($file, $line); + } + $hash = sha1($lines[$key]); + assert($hash !== ''); + return $hash; } } target = $target; - $this->lowUpperBound = $lowUpperBound; - $this->highLowerBound = $highLowerBound; - } - public function target() : Directory - { - return $this->target; - } - public function lowUpperBound() : int + public function read(string $baselineFile): \PHPUnit\Runner\Baseline\Baseline { - return $this->lowUpperBound; - } - public function highLowerBound() : int - { - return $this->highLowerBound; + if (!file_exists($baselineFile)) { + throw new \PHPUnit\Runner\Baseline\CannotLoadBaselineException(sprintf('Cannot read baseline %s, file does not exist', $baselineFile)); + } + try { + $document = (new XmlLoader())->loadFile($baselineFile); + } catch (XmlException $e) { + throw new \PHPUnit\Runner\Baseline\CannotLoadBaselineException(sprintf('Cannot read baseline: %s', trim($e->getMessage()))); + } + $version = (int) $document->documentElement->getAttribute('version'); + if ($version !== \PHPUnit\Runner\Baseline\Baseline::VERSION) { + throw new \PHPUnit\Runner\Baseline\CannotLoadBaselineException(sprintf('Cannot read baseline %s, version %d is not supported', $baselineFile, $version)); + } + $baseline = new \PHPUnit\Runner\Baseline\Baseline(); + $baselineDirectory = dirname(realpath($baselineFile)); + $xpath = new DOMXPath($document); + foreach ($xpath->query('file') as $fileElement) { + assert($fileElement instanceof DOMElement); + $file = $baselineDirectory . \DIRECTORY_SEPARATOR . str_replace('/', \DIRECTORY_SEPARATOR, $fileElement->getAttribute('path')); + foreach ($xpath->query('line', $fileElement) as $lineElement) { + assert($lineElement instanceof DOMElement); + $line = (int) $lineElement->getAttribute('number'); + $hash = $lineElement->getAttribute('hash'); + foreach ($xpath->query('issue', $lineElement) as $issueElement) { + assert($issueElement instanceof DOMElement); + $description = $issueElement->textContent; + assert($line > 0); + assert(!empty($hash)); + assert(!empty($description)); + $baseline->add(\PHPUnit\Runner\Baseline\Issue::from($file, $line, $hash, $description)); + } + } + } + return $baseline; } } target = $target; + $this->baselineDirectory = $baselineDirectory; } - public function target() : File + /** + * @psalm-param non-empty-string $filename + * + * @psalm-return non-empty-string + */ + public function calculate(string $filename): string { - return $this->target; + $result = implode('/', $this->parts($filename)); + assert($result !== ''); + return $result; + } + /** + * @psalm-param non-empty-string $filename + * + * @psalm-return list + */ + public function parts(string $filename): array + { + $schemePosition = strpos($filename, '://'); + if ($schemePosition !== \false) { + $filename = substr($filename, $schemePosition + 3); + assert($filename !== ''); + } + $parentParts = explode('/', trim(str_replace('\\', '/', $this->baselineDirectory), '/')); + $parentPartsCount = count($parentParts); + $filenameParts = explode('/', trim(str_replace('\\', '/', $filename), '/')); + $filenamePartsCount = count($filenameParts); + $i = 0; + for (; $i < $filenamePartsCount; $i++) { + if ($parentPartsCount < $i + 1) { + break; + } + $parentPath = implode('/', array_slice($parentParts, 0, $i + 1)); + $filenamePath = implode('/', array_slice($filenameParts, 0, $i + 1)); + if ($parentPath !== $filenamePath) { + break; + } + } + if ($i === 0) { + return [$filename]; + } + $dotsCount = $parentPartsCount - $i; + assert($dotsCount >= 0); + return array_merge(array_fill(0, $dotsCount, '..'), array_slice($filenameParts, $i)); } } target = $target; - $this->showUncoveredFiles = $showUncoveredFiles; - $this->showOnlySummary = $showOnlySummary; - } - public function target() : File + private readonly \PHPUnit\Runner\Baseline\Generator $generator; + public function __construct(\PHPUnit\Runner\Baseline\Generator $generator) { - return $this->target; - } - public function showUncoveredFiles() : bool - { - return $this->showUncoveredFiles; + $this->generator = $generator; } - public function showOnlySummary() : bool + protected function generator(): \PHPUnit\Runner\Baseline\Generator { - return $this->showOnlySummary; + return $this->generator; } } target = $target; - } - public function target() : Directory + public function notify(DeprecationTriggered $event): void { - return $this->target; + $this->generator()->testTriggeredIssue($event); } } filename = $filename; - $this->validationResult = $validationResult; - $this->extensions = $extensions; - $this->codeCoverage = $codeCoverage; - $this->groups = $groups; - $this->testdoxGroups = $testdoxGroups; - $this->listeners = $listeners; - $this->logging = $logging; - $this->php = $php; - $this->phpunit = $phpunit; - $this->testSuite = $testSuite; - } - public function filename() : string - { - return $this->filename; - } - public function hasValidationErrors() : bool - { - return $this->validationResult->hasValidationErrors(); - } - public function validationErrors() : string - { - return $this->validationResult->asString(); - } - public function extensions() : \PHPUnit\TextUI\XmlConfiguration\ExtensionCollection - { - return $this->extensions; - } - public function codeCoverage() : CodeCoverage - { - return $this->codeCoverage; - } - public function groups() : \PHPUnit\TextUI\XmlConfiguration\Groups - { - return $this->groups; - } - public function testdoxGroups() : \PHPUnit\TextUI\XmlConfiguration\Groups - { - return $this->testdoxGroups; - } - public function listeners() : \PHPUnit\TextUI\XmlConfiguration\ExtensionCollection - { - return $this->listeners; - } - public function logging() : Logging - { - return $this->logging; - } - public function php() : \PHPUnit\TextUI\XmlConfiguration\Php - { - return $this->php; - } - public function phpunit() : \PHPUnit\TextUI\XmlConfiguration\PHPUnit - { - return $this->phpunit; - } - public function testSuite() : \PHPUnit\TextUI\XmlConfiguration\TestSuiteCollection + public function notify(NoticeTriggered $event): void { - return $this->testSuite; + $this->generator()->testTriggeredIssue($event); } } generator()->testTriggeredIssue($event); + } } path = $path; - } - public function path() : string + public function notify(PhpNoticeTriggered $event): void { - return $this->path; + $this->generator()->testTriggeredIssue($event); } } */ -final class DirectoryCollection implements Countable, IteratorAggregate +final class TestTriggeredPhpWarningSubscriber extends \PHPUnit\Runner\Baseline\Subscriber implements PhpWarningTriggeredSubscriber { /** - * @var Directory[] + * @throws FileDoesNotExistException + * @throws FileDoesNotHaveLineException */ - private $directories; - /** - * @param Directory[] $directories - */ - public static function fromArray(array $directories) : self - { - return new self(...$directories); - } - private function __construct(\PHPUnit\TextUI\XmlConfiguration\Directory ...$directories) - { - $this->directories = $directories; - } - /** - * @return Directory[] - */ - public function asArray() : array - { - return $this->directories; - } - public function count() : int - { - return count($this->directories); - } - public function getIterator() : \PHPUnit\TextUI\XmlConfiguration\DirectoryCollectionIterator - { - return new \PHPUnit\TextUI\XmlConfiguration\DirectoryCollectionIterator($this); - } - public function isEmpty() : bool + public function notify(PhpWarningTriggered $event): void { - return $this->count() === 0; + $this->generator()->testTriggeredIssue($event); } } */ -final class DirectoryCollectionIterator implements Countable, Iterator +final class TestTriggeredWarningSubscriber extends \PHPUnit\Runner\Baseline\Subscriber implements WarningTriggeredSubscriber { /** - * @var Directory[] - */ - private $directories; - /** - * @var int + * @throws FileDoesNotExistException + * @throws FileDoesNotHaveLineException */ - private $position; - public function __construct(\PHPUnit\TextUI\XmlConfiguration\DirectoryCollection $directories) - { - $this->directories = $directories->asArray(); - } - public function count() : int - { - return iterator_count($this); - } - public function rewind() : void - { - $this->position = 0; - } - public function valid() : bool - { - return $this->position < count($this->directories); - } - public function key() : int - { - return $this->position; - } - public function current() : \PHPUnit\TextUI\XmlConfiguration\Directory - { - return $this->directories[$this->position]; - } - public function next() : void + public function notify(WarningTriggered $event): void { - $this->position++; + $this->generator()->testTriggeredIssue($event); } } path = $path; - } - public function path() : string + public function write(string $baselineFile, \PHPUnit\Runner\Baseline\Baseline $baseline): void { - return $this->path; + $pathCalculator = new \PHPUnit\Runner\Baseline\RelativePathCalculator(dirname($baselineFile)); + $writer = new XMLWriter(); + $writer->openMemory(); + $writer->setIndent(\true); + $writer->startDocument(); + $writer->startElement('files'); + $writer->writeAttribute('version', (string) \PHPUnit\Runner\Baseline\Baseline::VERSION); + foreach ($baseline->groupedByFileAndLine() as $file => $lines) { + assert(!empty($file)); + $writer->startElement('file'); + $writer->writeAttribute('path', $pathCalculator->calculate($file)); + foreach ($lines as $line => $issues) { + $writer->startElement('line'); + $writer->writeAttribute('number', (string) $line); + $writer->writeAttribute('hash', $issues[0]->hash()); + foreach ($issues as $issue) { + $writer->startElement('issue'); + $writer->writeCData($issue->description()); + $writer->endElement(); + } + $writer->endElement(); + } + $writer->endElement(); + } + $writer->endElement(); + file_put_contents($baselineFile, $writer->outputMemory()); } } + * @codeCoverageIgnore */ -final class FileCollection implements Countable, IteratorAggregate +final class CodeCoverage { + private static ?self $instance = null; + private ?\PHPUnitPHAR\SebastianBergmann\CodeCoverage\CodeCoverage $codeCoverage = null; + private ?Driver $driver = null; + private bool $collecting = \false; + private ?TestCase $test = null; + private ?Timer $timer = null; /** - * @var File[] + * @psalm-var array> */ - private $files; + private array $linesToBeIgnored = []; + public static function instance(): self + { + if (self::$instance === null) { + self::$instance = new self(); + } + return self::$instance; + } + public function init(Configuration $configuration, CodeCoverageFilterRegistry $codeCoverageFilterRegistry, bool $extensionRequiresCodeCoverageCollection): void + { + $codeCoverageFilterRegistry->init($configuration); + if (!$configuration->hasCoverageReport() && !$extensionRequiresCodeCoverageCollection) { + return; + } + $this->activate($codeCoverageFilterRegistry->get(), $configuration->pathCoverage()); + if (!$this->isActive()) { + return; + } + if ($configuration->hasCoverageCacheDirectory()) { + $this->codeCoverage()->cacheStaticAnalysis($configuration->coverageCacheDirectory()); + } + $this->codeCoverage()->excludeSubclassesOfThisClassFromUnintentionallyCoveredCodeCheck(Comparator::class); + if ($configuration->strictCoverage()) { + $this->codeCoverage()->enableCheckForUnintentionallyCoveredCode(); + } + if ($configuration->ignoreDeprecatedCodeUnitsFromCodeCoverage()) { + $this->codeCoverage()->ignoreDeprecatedCode(); + } else { + $this->codeCoverage()->doNotIgnoreDeprecatedCode(); + } + if ($configuration->disableCodeCoverageIgnore()) { + $this->codeCoverage()->disableAnnotationsForIgnoringCode(); + } else { + $this->codeCoverage()->enableAnnotationsForIgnoringCode(); + } + if ($configuration->includeUncoveredFiles()) { + $this->codeCoverage()->includeUncoveredFiles(); + } else { + $this->codeCoverage()->excludeUncoveredFiles(); + } + if ($codeCoverageFilterRegistry->get()->isEmpty()) { + if (!$codeCoverageFilterRegistry->configured()) { + EventFacade::emitter()->testRunnerTriggeredWarning('No filter is configured, code coverage will not be processed'); + } else { + EventFacade::emitter()->testRunnerTriggeredWarning('Incorrect filter configuration, code coverage will not be processed'); + } + $this->deactivate(); + } + } /** - * @param File[] $files + * @psalm-assert-if-true !null $this->instance */ - public static function fromArray(array $files) : self + public function isActive(): bool { - return new self(...$files); + return $this->codeCoverage !== null; } - private function __construct(\PHPUnit\TextUI\XmlConfiguration\File ...$files) + public function codeCoverage(): \PHPUnitPHAR\SebastianBergmann\CodeCoverage\CodeCoverage { - $this->files = $files; + return $this->codeCoverage; + } + public function driver(): Driver + { + return $this->driver; } /** - * @return File[] + * @throws MoreThanOneDataSetFromDataProviderException */ - public function asArray() : array + public function start(TestCase $test): void { - return $this->files; + if ($this->collecting) { + return; + } + $size = TestSize::unknown(); + if ($test->size()->isSmall()) { + $size = TestSize::small(); + } elseif ($test->size()->isMedium()) { + $size = TestSize::medium(); + } elseif ($test->size()->isLarge()) { + $size = TestSize::large(); + } + $this->test = $test; + $this->codeCoverage->start($test->valueObjectForEvents()->id(), $size); + $this->collecting = \true; } - public function count() : int + public function stop(bool $append = \true, array|false $linesToBeCovered = [], array $linesToBeUsed = []): void { - return count($this->files); + if (!$this->collecting) { + return; + } + $status = TestStatus::unknown(); + if ($this->test !== null) { + if ($this->test->status()->isSuccess()) { + $status = TestStatus::success(); + } else { + $status = TestStatus::failure(); + } + } + /* @noinspection UnusedFunctionResultInspection */ + $this->codeCoverage->stop($append, $status, $linesToBeCovered, $linesToBeUsed, $this->linesToBeIgnored); + $this->test = null; + $this->collecting = \false; } - public function getIterator() : \PHPUnit\TextUI\XmlConfiguration\FileCollectionIterator + public function deactivate(): void { - return new \PHPUnit\TextUI\XmlConfiguration\FileCollectionIterator($this); + $this->driver = null; + $this->codeCoverage = null; + $this->test = null; } - public function isEmpty() : bool + public function generateReports(Printer $printer, Configuration $configuration): void { - return $this->count() === 0; + if (!$this->isActive()) { + return; + } + if ($configuration->hasCoveragePhp()) { + $this->codeCoverageGenerationStart($printer, 'PHP'); + try { + $writer = new PhpReport(); + $writer->process($this->codeCoverage(), $configuration->coveragePhp()); + $this->codeCoverageGenerationSucceeded($printer); + unset($writer); + } catch (CodeCoverageException $e) { + $this->codeCoverageGenerationFailed($printer, $e); + } + } + if ($configuration->hasCoverageClover()) { + $this->codeCoverageGenerationStart($printer, 'Clover XML'); + try { + $writer = new CloverReport(); + $writer->process($this->codeCoverage(), $configuration->coverageClover()); + $this->codeCoverageGenerationSucceeded($printer); + unset($writer); + } catch (CodeCoverageException $e) { + $this->codeCoverageGenerationFailed($printer, $e); + } + } + if ($configuration->hasCoverageCobertura()) { + $this->codeCoverageGenerationStart($printer, 'Cobertura XML'); + try { + $writer = new CoberturaReport(); + $writer->process($this->codeCoverage(), $configuration->coverageCobertura()); + $this->codeCoverageGenerationSucceeded($printer); + unset($writer); + } catch (CodeCoverageException $e) { + $this->codeCoverageGenerationFailed($printer, $e); + } + } + if ($configuration->hasCoverageCrap4j()) { + $this->codeCoverageGenerationStart($printer, 'Crap4J XML'); + try { + $writer = new Crap4jReport($configuration->coverageCrap4jThreshold()); + $writer->process($this->codeCoverage(), $configuration->coverageCrap4j()); + $this->codeCoverageGenerationSucceeded($printer); + unset($writer); + } catch (CodeCoverageException $e) { + $this->codeCoverageGenerationFailed($printer, $e); + } + } + if ($configuration->hasCoverageHtml()) { + $this->codeCoverageGenerationStart($printer, 'HTML'); + try { + $customCssFile = CustomCssFile::default(); + if ($configuration->hasCoverageHtmlCustomCssFile()) { + $customCssFile = CustomCssFile::from($configuration->coverageHtmlCustomCssFile()); + } + $writer = new HtmlReport(sprintf(' and PHPUnit %s', \PHPUnit\Runner\Version::id()), Colors::from($configuration->coverageHtmlColorSuccessLow(), $configuration->coverageHtmlColorSuccessMedium(), $configuration->coverageHtmlColorSuccessHigh(), $configuration->coverageHtmlColorWarning(), $configuration->coverageHtmlColorDanger()), Thresholds::from($configuration->coverageHtmlLowUpperBound(), $configuration->coverageHtmlHighLowerBound()), $customCssFile); + $writer->process($this->codeCoverage(), $configuration->coverageHtml()); + $this->codeCoverageGenerationSucceeded($printer); + unset($writer); + } catch (CodeCoverageException $e) { + $this->codeCoverageGenerationFailed($printer, $e); + } + } + if ($configuration->hasCoverageText()) { + $processor = new TextReport(Thresholds::default(), $configuration->coverageTextShowUncoveredFiles(), $configuration->coverageTextShowOnlySummary()); + $textReport = $processor->process($this->codeCoverage(), $configuration->colors()); + if ($configuration->coverageText() === 'php://stdout') { + $printer->print($textReport); + } else { + file_put_contents($configuration->coverageText(), $textReport); + } + } + if ($configuration->hasCoverageXml()) { + $this->codeCoverageGenerationStart($printer, 'PHPUnit XML'); + try { + $writer = new XmlReport(\PHPUnit\Runner\Version::id()); + $writer->process($this->codeCoverage(), $configuration->coverageXml()); + $this->codeCoverageGenerationSucceeded($printer); + unset($writer); + } catch (CodeCoverageException $e) { + $this->codeCoverageGenerationFailed($printer, $e); + } + } + } + /** + * @psalm-param array> $linesToBeIgnored + */ + public function ignoreLines(array $linesToBeIgnored): void + { + $this->linesToBeIgnored = $linesToBeIgnored; + } + /** + * @psalm-return array> + */ + public function linesToBeIgnored(): array + { + return $this->linesToBeIgnored; + } + private function activate(Filter $filter, bool $pathCoverage): void + { + try { + if ($pathCoverage) { + $this->driver = (new Selector())->forLineAndPathCoverage($filter); + } else { + $this->driver = (new Selector())->forLineCoverage($filter); + } + $this->codeCoverage = new \PHPUnitPHAR\SebastianBergmann\CodeCoverage\CodeCoverage($this->driver, $filter); + } catch (CodeCoverageException $e) { + EventFacade::emitter()->testRunnerTriggeredWarning($e->getMessage()); + } + } + private function codeCoverageGenerationStart(Printer $printer, string $format): void + { + $printer->print(sprintf("\nGenerating code coverage report in %s format ... ", $format)); + $this->timer()->start(); + } + /** + * @throws NoActiveTimerException + */ + private function codeCoverageGenerationSucceeded(Printer $printer): void + { + $printer->print(sprintf("done [%s]\n", $this->timer()->stop()->asString())); + } + /** + * @throws NoActiveTimerException + */ + private function codeCoverageGenerationFailed(Printer $printer, CodeCoverageException $e): void + { + $printer->print(sprintf("failed [%s]\n%s\n", $this->timer()->stop()->asString(), $e->getMessage())); + } + private function timer(): Timer + { + if ($this->timer === null) { + $this->timer = new Timer(); + } + return $this->timer; } } */ -final class FileCollectionIterator implements Countable, Iterator +final class ErrorHandler { - /** - * @var File[] - */ - private $files; - /** - * @var int - */ - private $position; - public function __construct(\PHPUnit\TextUI\XmlConfiguration\FileCollection $files) - { - $this->files = $files->asArray(); - } - public function count() : int + private const UNHANDLEABLE_LEVELS = E_ERROR | E_PARSE | E_CORE_ERROR | E_CORE_WARNING | E_COMPILE_ERROR | E_COMPILE_WARNING; + private const INSUPPRESSIBLE_LEVELS = E_ERROR | E_PARSE | E_CORE_ERROR | E_COMPILE_ERROR | E_USER_ERROR | E_RECOVERABLE_ERROR; + private static ?self $instance = null; + private ?Baseline $baseline = null; + private bool $enabled = \false; + private ?int $originalErrorReportingLevel = null; + public static function instance(): self { - return iterator_count($this); + return self::$instance ?? self::$instance = new self(); } - public function rewind() : void + /** + * @throws NoTestCaseObjectOnCallStackException + */ + public function __invoke(int $errorNumber, string $errorString, string $errorFile, int $errorLine): bool { - $this->position = 0; + $suppressed = (error_reporting() & ~self::INSUPPRESSIBLE_LEVELS) === 0; + if ($suppressed && (new ExcludeList())->isExcluded($errorFile)) { + return \false; + } + $test = Event\Code\TestMethodBuilder::fromCallStack(); + $ignoredByBaseline = $this->ignoredByBaseline($errorFile, $errorLine, $errorString); + $ignoredByTest = $test->metadata()->isIgnoreDeprecations()->isNotEmpty(); + switch ($errorNumber) { + case E_NOTICE: + case E_STRICT: + Event\Facade::emitter()->testTriggeredPhpNotice($test, $errorString, $errorFile, $errorLine, $suppressed, $ignoredByBaseline); + break; + case E_USER_NOTICE: + Event\Facade::emitter()->testTriggeredNotice($test, $errorString, $errorFile, $errorLine, $suppressed, $ignoredByBaseline); + break; + case E_WARNING: + Event\Facade::emitter()->testTriggeredPhpWarning($test, $errorString, $errorFile, $errorLine, $suppressed, $ignoredByBaseline); + break; + case E_USER_WARNING: + Event\Facade::emitter()->testTriggeredWarning($test, $errorString, $errorFile, $errorLine, $suppressed, $ignoredByBaseline); + break; + case E_DEPRECATED: + Event\Facade::emitter()->testTriggeredPhpDeprecation($test, $errorString, $errorFile, $errorLine, $suppressed, $ignoredByBaseline, $ignoredByTest); + break; + case E_USER_DEPRECATED: + Event\Facade::emitter()->testTriggeredDeprecation($test, $errorString, $errorFile, $errorLine, $suppressed, $ignoredByBaseline, $ignoredByTest); + break; + case E_USER_ERROR: + Event\Facade::emitter()->testTriggeredError($test, $errorString, $errorFile, $errorLine, $suppressed); + throw new \PHPUnit\Runner\ErrorException('E_USER_ERROR was triggered'); + default: + return \false; + } + return \false; } - public function valid() : bool + public function enable(): void { - return $this->position < count($this->files); + if ($this->enabled) { + return; + } + $oldErrorHandler = set_error_handler($this); + if ($oldErrorHandler !== null) { + restore_error_handler(); + return; + } + $this->enabled = \true; + $this->originalErrorReportingLevel = error_reporting(); + error_reporting($this->originalErrorReportingLevel & self::UNHANDLEABLE_LEVELS); } - public function key() : int + public function disable(): void { - return $this->position; + if (!$this->enabled) { + return; + } + restore_error_handler(); + error_reporting(error_reporting() | $this->originalErrorReportingLevel); + $this->enabled = \false; + $this->originalErrorReportingLevel = null; } - public function current() : \PHPUnit\TextUI\XmlConfiguration\File + public function use(Baseline $baseline): void { - return $this->files[$this->position]; + $this->baseline = $baseline; } - public function next() : void + /** + * @psalm-param non-empty-string $file + * @psalm-param positive-int $line + * @psalm-param non-empty-string $description + */ + private function ignoredByBaseline(string $file, int $line, string $description): bool { - $this->position++; + if ($this->baseline === null) { + return \false; + } + return $this->baseline->has(Issue::from($file, $line, null, $description)); } } - - - - {tests_directory} - - + public function __construct(string $className, string $file) + { + parent::__construct(sprintf('Class %s cannot be found in %s', $className, $file)); + } +} + - - {src_directory} - - - +declare (strict_types=1); +/* + * This file is part of PHPUnit. + * + * (c) Sebastian Bergmann + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Runner; -EOT; - public function generateDefaultConfiguration(string $phpunitVersion, string $bootstrapScript, string $testsDirectory, string $srcDirectory, string $cacheDirectory) : string +use function sprintf; +use RuntimeException; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class ClassDoesNotExtendTestCaseException extends RuntimeException implements \PHPUnit\Runner\Exception +{ + public function __construct(string $className, string $file) { - return str_replace(['{phpunit_version}', '{bootstrap_script}', '{tests_directory}', '{src_directory}', '{cache_directory}'], [$phpunitVersion, $bootstrapScript, $testsDirectory, $srcDirectory, $cacheDirectory], self::TEMPLATE); + parent::__construct(sprintf('Class %s declared in %s does not extend PHPUnit\Framework\TestCase', $className, $file)); } } name = $name; + parent::__construct(sprintf('Class %s declared in %s is abstract', $className, $file)); } - public function name() : string +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Runner; + +use function sprintf; +use RuntimeException; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class DirectoryDoesNotExistException extends RuntimeException implements \PHPUnit\Runner\Exception +{ + public function __construct(string $directory) { - return $this->name; + parent::__construct(sprintf('Directory "%s" does not exist and could not be created', $directory)); } } * - * @template-implements IteratorAggregate + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. */ -final class GroupCollection implements IteratorAggregate +namespace PHPUnit\Runner; + +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +interface Exception extends \PHPUnit\Exception { - /** - * @var Group[] - */ - private $groups; - /** - * @param Group[] $groups - */ - public static function fromArray(array $groups) : self - { - return new self(...$groups); - } - private function __construct(\PHPUnit\TextUI\XmlConfiguration\Group ...$groups) - { - $this->groups = $groups; - } - /** - * @return Group[] - */ - public function asArray() : array - { - return $this->groups; - } - /** - * @return string[] - */ - public function asArrayOfStrings() : array - { - $result = []; - foreach ($this->groups as $group) { - $result[] = $group->name(); - } - return $result; - } - public function isEmpty() : bool - { - return empty($this->groups); - } - public function getIterator() : \PHPUnit\TextUI\XmlConfiguration\GroupCollectionIterator - { - return new \PHPUnit\TextUI\XmlConfiguration\GroupCollectionIterator($this); - } } */ -final class GroupCollectionIterator implements Countable, Iterator +final class FileDoesNotExistException extends RuntimeException implements \PHPUnit\Runner\Exception { - /** - * @var Group[] - */ - private $groups; - /** - * @var int - */ - private $position; - public function __construct(\PHPUnit\TextUI\XmlConfiguration\GroupCollection $groups) - { - $this->groups = $groups->asArray(); - } - public function count() : int - { - return iterator_count($this); - } - public function rewind() : void - { - $this->position = 0; - } - public function valid() : bool - { - return $this->position < count($this->groups); - } - public function key() : int - { - return $this->position; - } - public function current() : \PHPUnit\TextUI\XmlConfiguration\Group - { - return $this->groups[$this->position]; - } - public function next() : void + public function __construct(string $file) { - $this->position++; + parent::__construct(sprintf('File "%s" does not exist', $file)); } } include = $include; - $this->exclude = $exclude; - } - public function hasInclude() : bool - { - return !$this->include->isEmpty(); - } - public function include() : \PHPUnit\TextUI\XmlConfiguration\GroupCollection - { - return $this->include; - } - public function hasExclude() : bool - { - return !$this->exclude->isEmpty(); - } - public function exclude() : \PHPUnit\TextUI\XmlConfiguration\GroupCollection - { - return $this->exclude; - } } loadFile($filename, \false, \true, \true); - } catch (XmlException $e) { - throw new \PHPUnit\TextUI\XmlConfiguration\Exception($e->getMessage(), $e->getCode(), $e); - } - $xpath = new DOMXPath($document); - try { - $xsdFilename = (new SchemaFinder())->find(Version::series()); - } catch (XmlException $e) { - throw new \PHPUnit\TextUI\XmlConfiguration\Exception($e->getMessage(), $e->getCode(), $e); - } - return new \PHPUnit\TextUI\XmlConfiguration\Configuration($filename, (new Validator())->validate($document, $xsdFilename), $this->extensions($filename, $xpath), $this->codeCoverage($filename, $xpath, $document), $this->groups($xpath), $this->testdoxGroups($xpath), $this->listeners($filename, $xpath), $this->logging($filename, $xpath), $this->php($filename, $xpath), $this->phpunit($filename, $document), $this->testSuite($filename, $xpath)); - } - public function logging(string $filename, DOMXPath $xpath) : Logging +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Runner; + +use RuntimeException; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class NoIgnoredEventException extends RuntimeException implements \PHPUnit\Runner\Exception +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Runner; + +use function sprintf; +use RuntimeException; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class ParameterDoesNotExistException extends RuntimeException implements \PHPUnit\Runner\Exception +{ + public function __construct(string $name) { - if ($xpath->query('logging/log')->length !== 0) { - return $this->legacyLogging($filename, $xpath); - } - $junit = null; - $element = $this->element($xpath, 'logging/junit'); - if ($element) { - $junit = new Junit(new \PHPUnit\TextUI\XmlConfiguration\File($this->toAbsolutePath($filename, (string) $this->getStringAttribute($element, 'outputFile')))); - } - $text = null; - $element = $this->element($xpath, 'logging/text'); - if ($element) { - $text = new Text(new \PHPUnit\TextUI\XmlConfiguration\File($this->toAbsolutePath($filename, (string) $this->getStringAttribute($element, 'outputFile')))); - } - $teamCity = null; - $element = $this->element($xpath, 'logging/teamcity'); - if ($element) { - $teamCity = new TeamCity(new \PHPUnit\TextUI\XmlConfiguration\File($this->toAbsolutePath($filename, (string) $this->getStringAttribute($element, 'outputFile')))); - } - $testDoxHtml = null; - $element = $this->element($xpath, 'logging/testdoxHtml'); - if ($element) { - $testDoxHtml = new TestDoxHtml(new \PHPUnit\TextUI\XmlConfiguration\File($this->toAbsolutePath($filename, (string) $this->getStringAttribute($element, 'outputFile')))); - } - $testDoxText = null; - $element = $this->element($xpath, 'logging/testdoxText'); - if ($element) { - $testDoxText = new TestDoxText(new \PHPUnit\TextUI\XmlConfiguration\File($this->toAbsolutePath($filename, (string) $this->getStringAttribute($element, 'outputFile')))); - } - $testDoxXml = null; - $element = $this->element($xpath, 'logging/testdoxXml'); - if ($element) { - $testDoxXml = new TestDoxXml(new \PHPUnit\TextUI\XmlConfiguration\File($this->toAbsolutePath($filename, (string) $this->getStringAttribute($element, 'outputFile')))); - } - return new Logging($junit, $text, $teamCity, $testDoxHtml, $testDoxText, $testDoxXml); + parent::__construct(sprintf('Parameter "%s" does not exist', $name)); } - public function legacyLogging(string $filename, DOMXPath $xpath) : Logging +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Runner; + +use function sprintf; +use RuntimeException; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class PhptExternalFileCannotBeLoadedException extends RuntimeException implements \PHPUnit\Runner\Exception +{ + public function __construct(string $section, string $file) { - $junit = null; - $teamCity = null; - $testDoxHtml = null; - $testDoxText = null; - $testDoxXml = null; - $text = null; - foreach ($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($filename, $target); - switch ($type) { - case 'plain': - $text = new Text(new \PHPUnit\TextUI\XmlConfiguration\File($target)); - break; - case 'junit': - $junit = new Junit(new \PHPUnit\TextUI\XmlConfiguration\File($target)); - break; - case 'teamcity': - $teamCity = new TeamCity(new \PHPUnit\TextUI\XmlConfiguration\File($target)); - break; - case 'testdox-html': - $testDoxHtml = new TestDoxHtml(new \PHPUnit\TextUI\XmlConfiguration\File($target)); - break; - case 'testdox-text': - $testDoxText = new TestDoxText(new \PHPUnit\TextUI\XmlConfiguration\File($target)); - break; - case 'testdox-xml': - $testDoxXml = new TestDoxXml(new \PHPUnit\TextUI\XmlConfiguration\File($target)); - break; - } - } - return new Logging($junit, $text, $teamCity, $testDoxHtml, $testDoxText, $testDoxXml); + parent::__construct(sprintf('Could not load --%s-- %s for PHPT file', $section . '_EXTERNAL', $file)); } - private function extensions(string $filename, DOMXPath $xpath) : \PHPUnit\TextUI\XmlConfiguration\ExtensionCollection +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Runner; + +use RuntimeException; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class ReflectionException extends RuntimeException implements \PHPUnit\Runner\Exception +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Runner; + +use function sprintf; +use RuntimeException; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class UnsupportedPhptSectionException extends RuntimeException implements \PHPUnit\Runner\Exception +{ + public function __construct(string $section) { - $extensions = []; - foreach ($xpath->query('extensions/extension') as $extension) { - assert($extension instanceof DOMElement); - $extensions[] = $this->getElementConfigurationParameters($filename, $extension); - } - return \PHPUnit\TextUI\XmlConfiguration\ExtensionCollection::fromArray($extensions); + parent::__construct(sprintf('PHPUnit does not support PHPT %s sections', $section)); } - private function getElementConfigurationParameters(string $filename, DOMElement $element) : \PHPUnit\TextUI\XmlConfiguration\Extension +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Runner\Extension; + +use PHPUnit\TextUI\Configuration\Configuration; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +interface Extension +{ + public function bootstrap(Configuration $configuration, \PHPUnit\Runner\Extension\Facade $facade, \PHPUnit\Runner\Extension\ParameterCollection $parameters): void; +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Runner\Extension; + +use function assert; +use function class_exists; +use function class_implements; +use function in_array; +use function sprintf; +use PHPUnit\Event\Facade as EventFacade; +use PHPUnit\TextUI\Configuration\Configuration; +use ReflectionClass; +use Throwable; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class ExtensionBootstrapper +{ + private readonly Configuration $configuration; + private readonly \PHPUnit\Runner\Extension\Facade $facade; + public function __construct(Configuration $configuration, \PHPUnit\Runner\Extension\Facade $facade) { - /** @psalm-var class-string $class */ - $class = (string) $element->getAttribute('class'); - $file = ''; - $arguments = $this->getConfigurationArguments($filename, $element->childNodes); - if ($element->getAttribute('file')) { - $file = $this->toAbsolutePath($filename, (string) $element->getAttribute('file'), \true); - } - return new \PHPUnit\TextUI\XmlConfiguration\Extension($class, $file, $arguments); + $this->configuration = $configuration; + $this->facade = $facade; } - private function toAbsolutePath(string $filename, string $path, bool $useIncludePath = \false) : string + /** + * @psalm-param class-string $className + * @psalm-param array $parameters + */ + public function bootstrap(string $className, array $parameters): void { - $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($filename) . DIRECTORY_SEPARATOR . $path; - if ($useIncludePath && !is_file($file)) { - $includePathFile = stream_resolve_include_path($path); - if ($includePathFile) { - $file = $includePathFile; - } + if (!class_exists($className)) { + EventFacade::emitter()->testRunnerTriggeredWarning(sprintf('Cannot bootstrap extension because class %s does not exist', $className)); + return; } - return $file; - } - private function getConfigurationArguments(string $filename, DOMNodeList $nodes) : array - { - $arguments = []; - if ($nodes->length === 0) { - return $arguments; + if (!in_array(\PHPUnit\Runner\Extension\Extension::class, class_implements($className), \true)) { + EventFacade::emitter()->testRunnerTriggeredWarning(sprintf('Cannot bootstrap extension because class %s does not implement interface %s', $className, \PHPUnit\Runner\Extension\Extension::class)); + return; } - 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($filename, (string) $argument->textContent); - } else { - $arguments[] = Xml::xmlToVariable($argument); - } - } + try { + $instance = (new ReflectionClass($className))->newInstance(); + assert($instance instanceof \PHPUnit\Runner\Extension\Extension); + $instance->bootstrap($this->configuration, $this->facade, \PHPUnit\Runner\Extension\ParameterCollection::fromArray($parameters)); + } catch (Throwable $t) { + EventFacade::emitter()->testRunnerTriggeredWarning(sprintf('Bootstrapping of extension %s failed: %s%s%s', $className, $t->getMessage(), \PHP_EOL, $t->getTraceAsString())); + return; } - return $arguments; + EventFacade::emitter()->testRunnerBootstrappedExtension($className, $parameters); } - private function codeCoverage(string $filename, DOMXPath $xpath, DOMDocument $document) : CodeCoverage +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Runner\Extension; + +use PHPUnit\Event\EventFacadeIsSealedException; +use PHPUnit\Event\Facade as EventFacade; +use PHPUnit\Event\Subscriber; +use PHPUnit\Event\Tracer\Tracer; +use PHPUnit\Event\UnknownSubscriberTypeException; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class Facade +{ + private bool $replacesOutput = \false; + private bool $replacesProgressOutput = \false; + private bool $replacesResultOutput = \false; + private bool $requiresCodeCoverageCollection = \false; + private bool $requiresExportOfObjects = \false; + /** + * @throws EventFacadeIsSealedException + * @throws UnknownSubscriberTypeException + */ + public function registerSubscribers(Subscriber ...$subscribers): void { - if ($xpath->query('filter/whitelist')->length !== 0) { - return $this->legacyCodeCoverage($filename, $xpath, $document); - } - $cacheDirectory = null; - $pathCoverage = \false; - $includeUncoveredFiles = \true; - $processUncoveredFiles = \false; - $ignoreDeprecatedCodeUnits = \false; - $disableCodeCoverageIgnore = \false; - $element = $this->element($xpath, 'coverage'); - if ($element) { - $cacheDirectory = $this->getStringAttribute($element, 'cacheDirectory'); - if ($cacheDirectory !== null) { - $cacheDirectory = new \PHPUnit\TextUI\XmlConfiguration\Directory($this->toAbsolutePath($filename, $cacheDirectory)); - } - $pathCoverage = $this->getBooleanAttribute($element, 'pathCoverage', \false); - $includeUncoveredFiles = $this->getBooleanAttribute($element, 'includeUncoveredFiles', \true); - $processUncoveredFiles = $this->getBooleanAttribute($element, 'processUncoveredFiles', \false); - $ignoreDeprecatedCodeUnits = $this->getBooleanAttribute($element, 'ignoreDeprecatedCodeUnits', \false); - $disableCodeCoverageIgnore = $this->getBooleanAttribute($element, 'disableCodeCoverageIgnore', \false); - } - $clover = null; - $element = $this->element($xpath, 'coverage/report/clover'); - if ($element) { - $clover = new Clover(new \PHPUnit\TextUI\XmlConfiguration\File($this->toAbsolutePath($filename, (string) $this->getStringAttribute($element, 'outputFile')))); - } - $cobertura = null; - $element = $this->element($xpath, 'coverage/report/cobertura'); - if ($element) { - $cobertura = new Cobertura(new \PHPUnit\TextUI\XmlConfiguration\File($this->toAbsolutePath($filename, (string) $this->getStringAttribute($element, 'outputFile')))); - } - $crap4j = null; - $element = $this->element($xpath, 'coverage/report/crap4j'); - if ($element) { - $crap4j = new Crap4j(new \PHPUnit\TextUI\XmlConfiguration\File($this->toAbsolutePath($filename, (string) $this->getStringAttribute($element, 'outputFile'))), $this->getIntegerAttribute($element, 'threshold', 30)); - } - $html = null; - $element = $this->element($xpath, 'coverage/report/html'); - if ($element) { - $html = new CodeCoverageHtml(new \PHPUnit\TextUI\XmlConfiguration\Directory($this->toAbsolutePath($filename, (string) $this->getStringAttribute($element, 'outputDirectory'))), $this->getIntegerAttribute($element, 'lowUpperBound', 50), $this->getIntegerAttribute($element, 'highLowerBound', 90)); - } - $php = null; - $element = $this->element($xpath, 'coverage/report/php'); - if ($element) { - $php = new CodeCoveragePhp(new \PHPUnit\TextUI\XmlConfiguration\File($this->toAbsolutePath($filename, (string) $this->getStringAttribute($element, 'outputFile')))); - } - $text = null; - $element = $this->element($xpath, 'coverage/report/text'); - if ($element) { - $text = new CodeCoverageText(new \PHPUnit\TextUI\XmlConfiguration\File($this->toAbsolutePath($filename, (string) $this->getStringAttribute($element, 'outputFile'))), $this->getBooleanAttribute($element, 'showUncoveredFiles', \false), $this->getBooleanAttribute($element, 'showOnlySummary', \false)); - } - $xml = null; - $element = $this->element($xpath, 'coverage/report/xml'); - if ($element) { - $xml = new CodeCoverageXml(new \PHPUnit\TextUI\XmlConfiguration\Directory($this->toAbsolutePath($filename, (string) $this->getStringAttribute($element, 'outputDirectory')))); - } - return new CodeCoverage($cacheDirectory, $this->readFilterDirectories($filename, $xpath, 'coverage/include/directory'), $this->readFilterFiles($filename, $xpath, 'coverage/include/file'), $this->readFilterDirectories($filename, $xpath, 'coverage/exclude/directory'), $this->readFilterFiles($filename, $xpath, 'coverage/exclude/file'), $pathCoverage, $includeUncoveredFiles, $processUncoveredFiles, $ignoreDeprecatedCodeUnits, $disableCodeCoverageIgnore, $clover, $cobertura, $crap4j, $html, $php, $text, $xml); + EventFacade::instance()->registerSubscribers(...$subscribers); } /** - * @deprecated + * @throws EventFacadeIsSealedException + * @throws UnknownSubscriberTypeException */ - private function legacyCodeCoverage(string $filename, DOMXPath $xpath, DOMDocument $document) : CodeCoverage + public function registerSubscriber(Subscriber $subscriber): void { - $ignoreDeprecatedCodeUnits = $this->getBooleanAttribute($document->documentElement, 'ignoreDeprecatedCodeUnitsFromCodeCoverage', \false); - $disableCodeCoverageIgnore = $this->getBooleanAttribute($document->documentElement, 'disableCodeCoverageIgnore', \false); - $includeUncoveredFiles = \true; - $processUncoveredFiles = \false; - $element = $this->element($xpath, 'filter/whitelist'); - if ($element) { - if ($element->hasAttribute('addUncoveredFilesFromWhitelist')) { - $includeUncoveredFiles = (bool) $this->getBoolean((string) $element->getAttribute('addUncoveredFilesFromWhitelist'), \true); - } - if ($element->hasAttribute('processUncoveredFilesFromWhitelist')) { - $processUncoveredFiles = (bool) $this->getBoolean((string) $element->getAttribute('processUncoveredFilesFromWhitelist'), \false); - } - } - $clover = null; - $cobertura = null; - $crap4j = null; - $html = null; - $php = null; - $text = null; - $xml = null; - foreach ($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($filename, $target); - switch ($type) { - case 'coverage-clover': - $clover = new Clover(new \PHPUnit\TextUI\XmlConfiguration\File($target)); - break; - case 'coverage-cobertura': - $cobertura = new Cobertura(new \PHPUnit\TextUI\XmlConfiguration\File($target)); - break; - case 'coverage-crap4j': - $crap4j = new Crap4j(new \PHPUnit\TextUI\XmlConfiguration\File($target), $this->getIntegerAttribute($log, 'threshold', 30)); - break; - case 'coverage-html': - $html = new CodeCoverageHtml(new \PHPUnit\TextUI\XmlConfiguration\Directory($target), $this->getIntegerAttribute($log, 'lowUpperBound', 50), $this->getIntegerAttribute($log, 'highLowerBound', 90)); - break; - case 'coverage-php': - $php = new CodeCoveragePhp(new \PHPUnit\TextUI\XmlConfiguration\File($target)); - break; - case 'coverage-text': - $text = new CodeCoverageText(new \PHPUnit\TextUI\XmlConfiguration\File($target), $this->getBooleanAttribute($log, 'showUncoveredFiles', \false), $this->getBooleanAttribute($log, 'showOnlySummary', \false)); - break; - case 'coverage-xml': - $xml = new CodeCoverageXml(new \PHPUnit\TextUI\XmlConfiguration\Directory($target)); - break; - } - } - return new CodeCoverage(null, $this->readFilterDirectories($filename, $xpath, 'filter/whitelist/directory'), $this->readFilterFiles($filename, $xpath, 'filter/whitelist/file'), $this->readFilterDirectories($filename, $xpath, 'filter/whitelist/exclude/directory'), $this->readFilterFiles($filename, $xpath, 'filter/whitelist/exclude/file'), \false, $includeUncoveredFiles, $processUncoveredFiles, $ignoreDeprecatedCodeUnits, $disableCodeCoverageIgnore, $clover, $cobertura, $crap4j, $html, $php, $text, $xml); + EventFacade::instance()->registerSubscriber($subscriber); } /** - * 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\TextUI\XmlConfigurationTest::testPHPConfigurationIsReadCorrectly - * - * @param bool|string $default - * - * @return bool|string + * @throws EventFacadeIsSealedException */ - private function getBoolean(string $value, $default) + public function registerTracer(Tracer $tracer): void { - if (strtolower($value) === 'false') { - return \false; - } - if (strtolower($value) === 'true') { - return \true; - } - return $default; + EventFacade::instance()->registerTracer($tracer); } - private function readFilterDirectories(string $filename, DOMXPath $xpath, string $query) : FilterDirectoryCollection + public function replaceOutput(): void { - $directories = []; - foreach ($xpath->query($query) as $directoryNode) { - assert($directoryNode instanceof DOMElement); - $directoryPath = (string) $directoryNode->textContent; - if (!$directoryPath) { - continue; - } - $directories[] = new FilterDirectory($this->toAbsolutePath($filename, $directoryPath), $directoryNode->hasAttribute('prefix') ? (string) $directoryNode->getAttribute('prefix') : '', $directoryNode->hasAttribute('suffix') ? (string) $directoryNode->getAttribute('suffix') : '.php', $directoryNode->hasAttribute('group') ? (string) $directoryNode->getAttribute('group') : 'DEFAULT'); - } - return FilterDirectoryCollection::fromArray($directories); + $this->replacesOutput = \true; } - private function readFilterFiles(string $filename, DOMXPath $xpath, string $query) : \PHPUnit\TextUI\XmlConfiguration\FileCollection + public function replacesOutput(): bool { - $files = []; - foreach ($xpath->query($query) as $file) { - assert($file instanceof DOMNode); - $filePath = (string) $file->textContent; - if ($filePath) { - $files[] = new \PHPUnit\TextUI\XmlConfiguration\File($this->toAbsolutePath($filename, $filePath)); - } - } - return \PHPUnit\TextUI\XmlConfiguration\FileCollection::fromArray($files); + return $this->replacesOutput; } - private function groups(DOMXPath $xpath) : \PHPUnit\TextUI\XmlConfiguration\Groups + public function replaceProgressOutput(): void { - return $this->parseGroupConfiguration($xpath, 'groups'); + $this->replacesProgressOutput = \true; } - private function testdoxGroups(DOMXPath $xpath) : \PHPUnit\TextUI\XmlConfiguration\Groups + public function replacesProgressOutput(): bool { - return $this->parseGroupConfiguration($xpath, 'testdoxGroups'); + return $this->replacesOutput || $this->replacesProgressOutput; } - private function parseGroupConfiguration(DOMXPath $xpath, string $root) : \PHPUnit\TextUI\XmlConfiguration\Groups + public function replaceResultOutput(): void { - $include = []; - $exclude = []; - foreach ($xpath->query($root . '/include/group') as $group) { - assert($group instanceof DOMNode); - $include[] = new \PHPUnit\TextUI\XmlConfiguration\Group((string) $group->textContent); - } - foreach ($xpath->query($root . '/exclude/group') as $group) { - assert($group instanceof DOMNode); - $exclude[] = new \PHPUnit\TextUI\XmlConfiguration\Group((string) $group->textContent); - } - return new \PHPUnit\TextUI\XmlConfiguration\Groups(\PHPUnit\TextUI\XmlConfiguration\GroupCollection::fromArray($include), \PHPUnit\TextUI\XmlConfiguration\GroupCollection::fromArray($exclude)); + $this->replacesResultOutput = \true; } - private function listeners(string $filename, DOMXPath $xpath) : \PHPUnit\TextUI\XmlConfiguration\ExtensionCollection + public function replacesResultOutput(): bool { - $listeners = []; - foreach ($xpath->query('listeners/listener') as $listener) { - assert($listener instanceof DOMElement); - $listeners[] = $this->getElementConfigurationParameters($filename, $listener); - } - return \PHPUnit\TextUI\XmlConfiguration\ExtensionCollection::fromArray($listeners); + return $this->replacesOutput || $this->replacesResultOutput; } - private function getBooleanAttribute(DOMElement $element, string $attribute, bool $default) : bool + public function requireCodeCoverageCollection(): void { - if (!$element->hasAttribute($attribute)) { - return $default; - } - return (bool) $this->getBoolean((string) $element->getAttribute($attribute), \false); + $this->requiresCodeCoverageCollection = \true; } - private function getIntegerAttribute(DOMElement $element, string $attribute, int $default) : int + public function requiresCodeCoverageCollection(): bool { - if (!$element->hasAttribute($attribute)) { - return $default; - } - return $this->getInteger((string) $element->getAttribute($attribute), $default); + return $this->requiresCodeCoverageCollection; } - private function getStringAttribute(DOMElement $element, string $attribute) : ?string + /** + * @deprecated + */ + public function requireExportOfObjects(): void { - if (!$element->hasAttribute($attribute)) { - return null; - } - return (string) $element->getAttribute($attribute); + $this->requiresExportOfObjects = \true; } - private function getInteger(string $value, int $default) : int + /** + * @deprecated + */ + public function requiresExportOfObjects(): bool { - if (is_numeric($value)) { - return (int) $value; - } - return $default; + return $this->requiresExportOfObjects; } - private function php(string $filename, DOMXPath $xpath) : \PHPUnit\TextUI\XmlConfiguration\Php +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Runner\Extension; + +use function array_key_exists; +use PHPUnit\Runner\ParameterDoesNotExistException; +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class ParameterCollection +{ + private readonly array $parameters; + /** + * @psalm-param array $parameters + */ + public static function fromArray(array $parameters): self { - $includePaths = []; - foreach ($xpath->query('php/includePath') as $includePath) { - assert($includePath instanceof DOMNode); - $path = (string) $includePath->textContent; - if ($path) { - $includePaths[] = new \PHPUnit\TextUI\XmlConfiguration\Directory($this->toAbsolutePath($filename, $path)); - } - } - $iniSettings = []; - foreach ($xpath->query('php/ini') as $ini) { - assert($ini instanceof DOMElement); - $iniSettings[] = new \PHPUnit\TextUI\XmlConfiguration\IniSetting((string) $ini->getAttribute('name'), (string) $ini->getAttribute('value')); - } - $constants = []; - foreach ($xpath->query('php/const') as $const) { - assert($const instanceof DOMElement); - $value = (string) $const->getAttribute('value'); - $constants[] = new \PHPUnit\TextUI\XmlConfiguration\Constant((string) $const->getAttribute('name'), $this->getBoolean($value, $value)); - } - $variables = ['var' => [], 'env' => [], 'post' => [], 'get' => [], 'cookie' => [], 'server' => [], 'files' => [], 'request' => []]; - foreach (['var', 'env', 'post', 'get', 'cookie', 'server', 'files', 'request'] as $array) { - foreach ($xpath->query('php/' . $array) as $var) { - assert($var instanceof DOMElement); - $name = (string) $var->getAttribute('name'); - $value = (string) $var->getAttribute('value'); - $force = \false; - $verbatim = \false; - if ($var->hasAttribute('force')) { - $force = (bool) $this->getBoolean($var->getAttribute('force'), \false); - } - if ($var->hasAttribute('verbatim')) { - $verbatim = $this->getBoolean($var->getAttribute('verbatim'), \false); - } - if (!$verbatim) { - $value = $this->getBoolean($value, $value); - } - $variables[$array][] = new \PHPUnit\TextUI\XmlConfiguration\Variable($name, $value, $force); - } - } - return new \PHPUnit\TextUI\XmlConfiguration\Php(\PHPUnit\TextUI\XmlConfiguration\DirectoryCollection::fromArray($includePaths), \PHPUnit\TextUI\XmlConfiguration\IniSettingCollection::fromArray($iniSettings), \PHPUnit\TextUI\XmlConfiguration\ConstantCollection::fromArray($constants), \PHPUnit\TextUI\XmlConfiguration\VariableCollection::fromArray($variables['var']), \PHPUnit\TextUI\XmlConfiguration\VariableCollection::fromArray($variables['env']), \PHPUnit\TextUI\XmlConfiguration\VariableCollection::fromArray($variables['post']), \PHPUnit\TextUI\XmlConfiguration\VariableCollection::fromArray($variables['get']), \PHPUnit\TextUI\XmlConfiguration\VariableCollection::fromArray($variables['cookie']), \PHPUnit\TextUI\XmlConfiguration\VariableCollection::fromArray($variables['server']), \PHPUnit\TextUI\XmlConfiguration\VariableCollection::fromArray($variables['files']), \PHPUnit\TextUI\XmlConfiguration\VariableCollection::fromArray($variables['request'])); + return new self($parameters); } - private function phpunit(string $filename, DOMDocument $document) : \PHPUnit\TextUI\XmlConfiguration\PHPUnit + private function __construct(array $parameters) { - $executionOrder = TestSuiteSorter::ORDER_DEFAULT; - $defectsFirst = \false; - $resolveDependencies = $this->getBooleanAttribute($document->documentElement, 'resolveDependencies', \true); - if ($document->documentElement->hasAttribute('executionOrder')) { - foreach (explode(',', $document->documentElement->getAttribute('executionOrder')) as $order) { - switch ($order) { - case 'default': - $executionOrder = TestSuiteSorter::ORDER_DEFAULT; - $defectsFirst = \false; - $resolveDependencies = \true; - break; - case 'depends': - $resolveDependencies = \true; - break; - case 'no-depends': - $resolveDependencies = \false; - break; - case 'defects': - $defectsFirst = \true; - break; - case 'duration': - $executionOrder = TestSuiteSorter::ORDER_DURATION; - break; - case 'random': - $executionOrder = TestSuiteSorter::ORDER_RANDOMIZED; - break; - case 'reverse': - $executionOrder = TestSuiteSorter::ORDER_REVERSED; - break; - case 'size': - $executionOrder = TestSuiteSorter::ORDER_SIZE; - break; - } - } - } - $printerClass = $this->getStringAttribute($document->documentElement, 'printerClass'); - $testdox = $this->getBooleanAttribute($document->documentElement, 'testdox', \false); - $conflictBetweenPrinterClassAndTestdox = \false; - if ($testdox) { - if ($printerClass !== null) { - $conflictBetweenPrinterClassAndTestdox = \true; - } - $printerClass = CliTestDoxPrinter::class; - } - $cacheResultFile = $this->getStringAttribute($document->documentElement, 'cacheResultFile'); - if ($cacheResultFile !== null) { - $cacheResultFile = $this->toAbsolutePath($filename, $cacheResultFile); - } - $bootstrap = $this->getStringAttribute($document->documentElement, 'bootstrap'); - if ($bootstrap !== null) { - $bootstrap = $this->toAbsolutePath($filename, $bootstrap); - } - $extensionsDirectory = $this->getStringAttribute($document->documentElement, 'extensionsDirectory'); - if ($extensionsDirectory !== null) { - $extensionsDirectory = $this->toAbsolutePath($filename, $extensionsDirectory); - } - $testSuiteLoaderFile = $this->getStringAttribute($document->documentElement, 'testSuiteLoaderFile'); - if ($testSuiteLoaderFile !== null) { - $testSuiteLoaderFile = $this->toAbsolutePath($filename, $testSuiteLoaderFile); - } - $printerFile = $this->getStringAttribute($document->documentElement, 'printerFile'); - if ($printerFile !== null) { - $printerFile = $this->toAbsolutePath($filename, $printerFile); - } - return new \PHPUnit\TextUI\XmlConfiguration\PHPUnit($this->getBooleanAttribute($document->documentElement, 'cacheResult', \true), $cacheResultFile, $this->getColumns($document), $this->getColors($document), $this->getBooleanAttribute($document->documentElement, 'stderr', \false), $this->getBooleanAttribute($document->documentElement, 'noInteraction', \false), $this->getBooleanAttribute($document->documentElement, 'verbose', \false), $this->getBooleanAttribute($document->documentElement, 'reverseDefectList', \false), $this->getBooleanAttribute($document->documentElement, 'convertDeprecationsToExceptions', \false), $this->getBooleanAttribute($document->documentElement, 'convertErrorsToExceptions', \true), $this->getBooleanAttribute($document->documentElement, 'convertNoticesToExceptions', \true), $this->getBooleanAttribute($document->documentElement, 'convertWarningsToExceptions', \true), $this->getBooleanAttribute($document->documentElement, 'forceCoversAnnotation', \false), $bootstrap, $this->getBooleanAttribute($document->documentElement, 'processIsolation', \false), $this->getBooleanAttribute($document->documentElement, 'failOnEmptyTestSuite', \false), $this->getBooleanAttribute($document->documentElement, 'failOnIncomplete', \false), $this->getBooleanAttribute($document->documentElement, 'failOnRisky', \false), $this->getBooleanAttribute($document->documentElement, 'failOnSkipped', \false), $this->getBooleanAttribute($document->documentElement, 'failOnWarning', \false), $this->getBooleanAttribute($document->documentElement, 'stopOnDefect', \false), $this->getBooleanAttribute($document->documentElement, 'stopOnError', \false), $this->getBooleanAttribute($document->documentElement, 'stopOnFailure', \false), $this->getBooleanAttribute($document->documentElement, 'stopOnWarning', \false), $this->getBooleanAttribute($document->documentElement, 'stopOnIncomplete', \false), $this->getBooleanAttribute($document->documentElement, 'stopOnRisky', \false), $this->getBooleanAttribute($document->documentElement, 'stopOnSkipped', \false), $extensionsDirectory, $this->getStringAttribute($document->documentElement, 'testSuiteLoaderClass'), $testSuiteLoaderFile, $printerClass, $printerFile, $this->getBooleanAttribute($document->documentElement, 'beStrictAboutChangesToGlobalState', \false), $this->getBooleanAttribute($document->documentElement, 'beStrictAboutOutputDuringTests', \false), $this->getBooleanAttribute($document->documentElement, 'beStrictAboutResourceUsageDuringSmallTests', \false), $this->getBooleanAttribute($document->documentElement, 'beStrictAboutTestsThatDoNotTestAnything', \true), $this->getBooleanAttribute($document->documentElement, 'beStrictAboutTodoAnnotatedTests', \false), $this->getBooleanAttribute($document->documentElement, 'beStrictAboutCoversAnnotation', \false), $this->getBooleanAttribute($document->documentElement, 'enforceTimeLimit', \false), $this->getIntegerAttribute($document->documentElement, 'defaultTimeLimit', 1), $this->getIntegerAttribute($document->documentElement, 'timeoutForSmallTests', 1), $this->getIntegerAttribute($document->documentElement, 'timeoutForMediumTests', 10), $this->getIntegerAttribute($document->documentElement, 'timeoutForLargeTests', 60), $this->getStringAttribute($document->documentElement, 'defaultTestSuite'), $executionOrder, $resolveDependencies, $defectsFirst, $this->getBooleanAttribute($document->documentElement, 'backupGlobals', \false), $this->getBooleanAttribute($document->documentElement, 'backupStaticAttributes', \false), $this->getBooleanAttribute($document->documentElement, 'registerMockObjectsFromTestArgumentsRecursively', \false), $conflictBetweenPrinterClassAndTestdox); + $this->parameters = $parameters; } - private function getColors(DOMDocument $document) : string + public function has(string $name): bool { - $colors = DefaultResultPrinter::COLOR_DEFAULT; - if ($document->documentElement->hasAttribute('colors')) { - /* only allow boolean for compatibility with previous versions - 'always' only allowed from command line */ - if ($this->getBoolean($document->documentElement->getAttribute('colors'), \false)) { - $colors = DefaultResultPrinter::COLOR_AUTO; - } else { - $colors = DefaultResultPrinter::COLOR_NEVER; - } - } - return $colors; + return array_key_exists($name, $this->parameters); } /** - * @return int|string + * @throws ParameterDoesNotExistException */ - private function getColumns(DOMDocument $document) + public function get(string $name): string { - $columns = 80; - if ($document->documentElement->hasAttribute('columns')) { - $columns = (string) $document->documentElement->getAttribute('columns'); - if ($columns !== 'max') { - $columns = $this->getInteger($columns, 80); - } + if (!$this->has($name)) { + throw new ParameterDoesNotExistException($name); } - return $columns; - } - private function testSuite(string $filename, DOMXPath $xpath) : \PHPUnit\TextUI\XmlConfiguration\TestSuiteCollection - { - $testSuites = []; - foreach ($this->getTestSuiteElements($xpath) as $element) { - $exclude = []; - foreach ($element->getElementsByTagName('exclude') as $excludeNode) { - $excludeFile = (string) $excludeNode->textContent; - if ($excludeFile) { - $exclude[] = new \PHPUnit\TextUI\XmlConfiguration\File($this->toAbsolutePath($filename, $excludeFile)); - } - } - $directories = []; - foreach ($element->getElementsByTagName('directory') as $directoryNode) { - assert($directoryNode instanceof DOMElement); - $directory = (string) $directoryNode->textContent; - if (empty($directory)) { - continue; - } - $prefix = ''; - if ($directoryNode->hasAttribute('prefix')) { - $prefix = (string) $directoryNode->getAttribute('prefix'); - } - $suffix = 'Test.php'; - if ($directoryNode->hasAttribute('suffix')) { - $suffix = (string) $directoryNode->getAttribute('suffix'); - } - $phpVersion = PHP_VERSION; - if ($directoryNode->hasAttribute('phpVersion')) { - $phpVersion = (string) $directoryNode->getAttribute('phpVersion'); - } - $phpVersionOperator = new VersionComparisonOperator('>='); - if ($directoryNode->hasAttribute('phpVersionOperator')) { - $phpVersionOperator = new VersionComparisonOperator((string) $directoryNode->getAttribute('phpVersionOperator')); - } - $directories[] = new \PHPUnit\TextUI\XmlConfiguration\TestDirectory($this->toAbsolutePath($filename, $directory), $prefix, $suffix, $phpVersion, $phpVersionOperator); - } - $files = []; - foreach ($element->getElementsByTagName('file') as $fileNode) { - assert($fileNode instanceof DOMElement); - $file = (string) $fileNode->textContent; - if (empty($file)) { - continue; - } - $phpVersion = PHP_VERSION; - if ($fileNode->hasAttribute('phpVersion')) { - $phpVersion = (string) $fileNode->getAttribute('phpVersion'); - } - $phpVersionOperator = new VersionComparisonOperator('>='); - if ($fileNode->hasAttribute('phpVersionOperator')) { - $phpVersionOperator = new VersionComparisonOperator((string) $fileNode->getAttribute('phpVersionOperator')); - } - $files[] = new \PHPUnit\TextUI\XmlConfiguration\TestFile($this->toAbsolutePath($filename, $file), $phpVersion, $phpVersionOperator); - } - $testSuites[] = new TestSuiteConfiguration((string) $element->getAttribute('name'), \PHPUnit\TextUI\XmlConfiguration\TestDirectoryCollection::fromArray($directories), \PHPUnit\TextUI\XmlConfiguration\TestFileCollection::fromArray($files), \PHPUnit\TextUI\XmlConfiguration\FileCollection::fromArray($exclude)); - } - return \PHPUnit\TextUI\XmlConfiguration\TestSuiteCollection::fromArray($testSuites); - } - /** - * @return DOMElement[] - */ - private function getTestSuiteElements(DOMXPath $xpath) : array - { - /** @var DOMElement[] $elements */ - $elements = []; - $testSuiteNodes = $xpath->query('testsuites/testsuite'); - if ($testSuiteNodes->length === 0) { - $testSuiteNodes = $xpath->query('testsuite'); - } - if ($testSuiteNodes->length === 1) { - $element = $testSuiteNodes->item(0); - assert($element instanceof DOMElement); - $elements[] = $element; - } else { - foreach ($testSuiteNodes as $testSuiteNode) { - assert($testSuiteNode instanceof DOMElement); - $elements[] = $testSuiteNode; - } - } - return $elements; - } - private function element(DOMXPath $xpath, string $element) : ?DOMElement - { - $nodes = $xpath->query($element); - if ($nodes->length === 1) { - $node = $nodes->item(0); - assert($node instanceof DOMElement); - return $node; - } - return null; + return $this->parameters[$name]; } } */ - private $target; - public function __construct(File $target) + public function loadPharExtensionsInDirectory(string $directory): array { - $this->target = $target; + $pharExtensionLoaded = extension_loaded('phar'); + $loadedExtensions = []; + foreach ((new FileIteratorFacade())->getFilesAsArray($directory, '.phar') as $file) { + if (!$pharExtensionLoaded) { + Event\Facade::emitter()->testRunnerTriggeredWarning(sprintf('Cannot load extension from %s because the PHAR extension is not available', $file)); + continue; + } + if (!is_file('phar://' . $file . '/manifest.xml')) { + Event\Facade::emitter()->testRunnerTriggeredWarning(sprintf('%s is not an extension for PHPUnit', $file)); + continue; + } + try { + $applicationName = new ApplicationName('phpunit/phpunit'); + $version = new PharIoVersion($this->phpunitVersion()); + $manifest = ManifestLoader::fromFile('phar://' . $file . '/manifest.xml'); + if (!$manifest->isExtensionFor($applicationName)) { + Event\Facade::emitter()->testRunnerTriggeredWarning(sprintf('%s is not an extension for PHPUnit', $file)); + continue; + } + if (!$manifest->isExtensionFor($applicationName, $version)) { + Event\Facade::emitter()->testRunnerTriggeredWarning(sprintf('%s is not compatible with PHPUnit %s', $file, Version::series())); + continue; + } + } catch (ManifestException $e) { + Event\Facade::emitter()->testRunnerTriggeredWarning(sprintf('Cannot load extension from %s: %s', $file, $e->getMessage())); + continue; + } + try { + /** @psalm-suppress UnresolvableInclude */ + @require $file; + } catch (Throwable $t) { + Event\Facade::emitter()->testRunnerTriggeredWarning(sprintf('Cannot load extension from %s: %s', $file, $t->getMessage())); + continue; + } + $loadedExtensions[] = $manifest->getName()->asString() . ' ' . $manifest->getVersion()->getVersionString(); + Event\Facade::emitter()->testRunnerLoadedExtensionFromPhar($file, $manifest->getName()->asString(), $manifest->getVersion()->getVersionString()); + } + return $loadedExtensions; } - public function target() : File + private function phpunitVersion(): string { - return $this->target; + $version = Version::id(); + if (!str_contains($version, '-')) { + return $version; + } + $parts = explode('.', explode('-', $version)[0]); + if (count($parts) === 2) { + $parts[] = 0; + } + return implode('.', $parts); } } junit = $junit; - $this->text = $text; - $this->teamCity = $teamCity; - $this->testDoxHtml = $testDoxHtml; - $this->testDoxText = $testDoxText; - $this->testDoxXml = $testDoxXml; - } - public function hasJunit() : bool - { - return $this->junit !== null; - } - public function junit() : \PHPUnit\TextUI\XmlConfiguration\Logging\Junit - { - if ($this->junit === null) { - throw new Exception('Logger "JUnit XML" is not configured'); - } - return $this->junit; - } - public function hasText() : bool - { - return $this->text !== null; - } - public function text() : \PHPUnit\TextUI\XmlConfiguration\Logging\Text - { - if ($this->text === null) { - throw new Exception('Logger "Text" is not configured'); - } - return $this->text; - } - public function hasTeamCity() : bool - { - return $this->teamCity !== null; - } - public function teamCity() : \PHPUnit\TextUI\XmlConfiguration\Logging\TeamCity - { - if ($this->teamCity === null) { - throw new Exception('Logger "Team City" is not configured'); - } - return $this->teamCity; - } - public function hasTestDoxHtml() : bool - { - return $this->testDoxHtml !== null; - } - public function testDoxHtml() : TestDoxHtml - { - if ($this->testDoxHtml === null) { - throw new Exception('Logger "TestDox HTML" is not configured'); - } - return $this->testDoxHtml; - } - public function hasTestDoxText() : bool - { - return $this->testDoxText !== null; - } - public function testDoxText() : TestDoxText - { - if ($this->testDoxText === null) { - throw new Exception('Logger "TestDox Text" is not configured'); - } - return $this->testDoxText; - } - public function hasTestDoxXml() : bool - { - return $this->testDoxXml !== null; - } - public function testDoxXml() : TestDoxXml + protected function doAccept(int $id): bool { - if ($this->testDoxXml === null) { - throw new Exception('Logger "TestDox XML" is not configured'); - } - return $this->testDoxXml; + return !in_array($id, $this->groupTests, \true); } } */ - private $target; - public function __construct(File $target) + private array $filters = []; + /** + * @psalm-param list $testIds + */ + public function addTestIdFilter(array $testIds): void { - $this->target = $target; + $this->filters[] = [new ReflectionClass(\PHPUnit\Runner\Filter\TestIdFilterIterator::class), $testIds]; } - public function target() : File + /** + * @psalm-param list $groups + */ + public function addExcludeGroupFilter(array $groups): void { - return $this->target; + $this->filters[] = [new ReflectionClass(\PHPUnit\Runner\Filter\ExcludeGroupFilterIterator::class), $groups]; } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TextUI\XmlConfiguration\Logging\TestDox; - -use PHPUnit\TextUI\XmlConfiguration\File; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - * - * @psalm-immutable - */ -final class Html -{ /** - * @var File + * @psalm-param list $groups */ - private $target; - public function __construct(File $target) + public function addIncludeGroupFilter(array $groups): void { - $this->target = $target; + $this->filters[] = [new ReflectionClass(\PHPUnit\Runner\Filter\IncludeGroupFilterIterator::class), $groups]; + } + /** + * @psalm-param non-empty-string $name + */ + public function addNameFilter(string $name): void + { + $this->filters[] = [new ReflectionClass(\PHPUnit\Runner\Filter\NameFilterIterator::class), $name]; } - public function target() : File + public function factory(Iterator $iterator, TestSuite $suite): FilterIterator { - return $this->target; + foreach ($this->filters as $filter) { + [$class, $arguments] = $filter; + $iterator = $class->newInstance($iterator, $arguments, $suite); + } + assert($iterator instanceof FilterIterator); + return $iterator; } } */ - private $target; - public function __construct(File $target) + protected array $groupTests = []; + /** + * @psalm-param RecursiveIterator $iterator + * @psalm-param list $groups + */ + public function __construct(RecursiveIterator $iterator, array $groups, TestSuite $suite) { - $this->target = $target; + parent::__construct($iterator); + foreach ($suite->groupDetails() as $group => $tests) { + if (in_array((string) $group, $groups, \true)) { + $testHashes = array_map('spl_object_id', $tests); + array_push($this->groupTests, ...$testHashes); + } + } } - public function target() : File + public function accept(): bool { - return $this->target; + $test = $this->getInnerIterator()->current(); + if ($test instanceof TestSuite) { + return \true; + } + return $this->doAccept(spl_object_id($test)); } + abstract protected function doAccept(int $id): bool; } target = $target; - } - public function target() : File + protected function doAccept(int $id): bool { - return $this->target; + return in_array($id, $this->groupTests, \true); } } $iterator + * @psalm-param non-empty-string $filter + * + * @throws Exception */ - private $target; - public function __construct(File $target) + public function __construct(RecursiveIterator $iterator, string $filter) { - $this->target = $target; + parent::__construct($iterator); + $this->setFilter($filter); } - public function target() : File + public function accept(): bool { - return $this->target; + $test = $this->getInnerIterator()->current(); + if ($test instanceof TestSuite) { + return \true; + } + $tmp = $this->describe($test); + if ($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; } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TextUI\XmlConfiguration; - -use function version_compare; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class MigrationBuilder -{ - private const AVAILABLE_MIGRATIONS = ['8.5' => [\PHPUnit\TextUI\XmlConfiguration\RemoveLogTypes::class], '9.2' => [\PHPUnit\TextUI\XmlConfiguration\RemoveCacheTokensAttribute::class, \PHPUnit\TextUI\XmlConfiguration\IntroduceCoverageElement::class, \PHPUnit\TextUI\XmlConfiguration\MoveAttributesFromRootToCoverage::class, \PHPUnit\TextUI\XmlConfiguration\MoveAttributesFromFilterWhitelistToCoverage::class, \PHPUnit\TextUI\XmlConfiguration\MoveWhitelistIncludesToCoverage::class, \PHPUnit\TextUI\XmlConfiguration\MoveWhitelistExcludesToCoverage::class, \PHPUnit\TextUI\XmlConfiguration\RemoveEmptyFilter::class, \PHPUnit\TextUI\XmlConfiguration\CoverageCloverToReport::class, \PHPUnit\TextUI\XmlConfiguration\CoverageCrap4jToReport::class, \PHPUnit\TextUI\XmlConfiguration\CoverageHtmlToReport::class, \PHPUnit\TextUI\XmlConfiguration\CoveragePhpToReport::class, \PHPUnit\TextUI\XmlConfiguration\CoverageTextToReport::class, \PHPUnit\TextUI\XmlConfiguration\CoverageXmlToReport::class, \PHPUnit\TextUI\XmlConfiguration\ConvertLogTypes::class, \PHPUnit\TextUI\XmlConfiguration\UpdateSchemaLocationTo93::class]]; /** - * @throws MigrationBuilderException + * @throws Exception */ - public function build(string $fromVersion) : array + private function setFilter(string $filter): void { - $stack = []; - foreach (self::AVAILABLE_MIGRATIONS as $version => $migrations) { - if (version_compare($version, $fromVersion, '<')) { - continue; - } - foreach ($migrations as $migration) { - $stack[] = new $migration(); + if (@preg_match($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 = (int) $matches[2]; + $this->filterMax = (int) $matches[3]; + } else { + $filter = sprintf('%s.*with data set #%s$', $matches[1], $matches[2]); + } + } 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)); } - return $stack; + $this->filter = $filter; + } + /** + * @psalm-return array{0: string, 1: string} + */ + private function describe(Test $test): array + { + if ($test instanceof TestCase) { + return [$test::class, $test->nameWithDataSet()]; + } + if ($test instanceof SelfDescribing) { + return ['', $test->toString()]; + } + return ['', $test::class]; } } - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TextUI\XmlConfiguration; - -use PHPUnit\Exception; -use RuntimeException; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class MigrationException extends RuntimeException implements Exception -{ -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TextUI\XmlConfiguration; +namespace PHPUnit\Runner\Filter; -use DOMDocument; -use DOMElement; +use function in_array; +use PHPUnit\Event\TestData\MoreThanOneDataSetFromDataProviderException; +use PHPUnit\Event\TestData\NoDataSetFromDataProviderException; +use PHPUnit\Framework\Test; +use PHPUnit\Framework\TestCase; +use PHPUnit\Framework\TestSuite; +use PHPUnit\Runner\PhptTestCase; +use RecursiveFilterIterator; +use RecursiveIterator; /** * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class ConvertLogTypes implements \PHPUnit\TextUI\XmlConfiguration\Migration +final class TestIdFilterIterator extends RecursiveFilterIterator { - public function migrate(DOMDocument $document) : void + /** + * @psalm-var non-empty-list + */ + private readonly array $testIds; + /** + * @psalm-param RecursiveIterator $iterator + * @psalm-param non-empty-list $testIds + */ + public function __construct(RecursiveIterator $iterator, array $testIds) { - $logging = $document->getElementsByTagName('logging')->item(0); - if (!$logging instanceof DOMElement) { - return; + parent::__construct($iterator); + $this->testIds = $testIds; + } + public function accept(): bool + { + $test = $this->getInnerIterator()->current(); + if ($test instanceof TestSuite) { + return \true; } - $types = ['junit' => 'junit', 'teamcity' => 'teamcity', 'testdox-html' => 'testdoxHtml', 'testdox-text' => 'testdoxText', 'testdox-xml' => 'testdoxXml', 'plain' => 'text']; - $logNodes = []; - foreach ($logging->getElementsByTagName('log') as $logNode) { - if (!isset($types[$logNode->getAttribute('type')])) { - continue; - } - $logNodes[] = $logNode; + if (!$test instanceof TestCase && !$test instanceof PhptTestCase) { + return \false; } - foreach ($logNodes as $oldNode) { - $newLogNode = $document->createElement($types[$oldNode->getAttribute('type')]); - $newLogNode->setAttribute('outputFile', $oldNode->getAttribute('target')); - $logging->replaceChild($newLogNode, $oldNode); + try { + return in_array($test->valueObjectForEvents()->id(), $this->testIds, \true); + } catch (MoreThanOneDataSetFromDataProviderException|NoDataSetFromDataProviderException) { + return \false; } } } @@ -85778,23 +75226,62 @@ declare (strict_types=1); * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\TextUI\XmlConfiguration; +namespace PHPUnit\Runner\GarbageCollection; -use DOMElement; +use function gc_collect_cycles; +use function gc_disable; +use function gc_enable; +use PHPUnit\Event\EventFacadeIsSealedException; +use PHPUnit\Event\Facade; +use PHPUnit\Event\UnknownSubscriberTypeException; /** * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class CoverageCloverToReport extends \PHPUnit\TextUI\XmlConfiguration\LogToReportMigration +final class GarbageCollectionHandler { - protected function forType() : string + private readonly Facade $facade; + private readonly int $threshold; + private int $tests = 0; + /** + * @throws EventFacadeIsSealedException + * @throws UnknownSubscriberTypeException + */ + public function __construct(Facade $facade, int $threshold) { - return 'coverage-clover'; + $this->facade = $facade; + $this->threshold = $threshold; + $this->registerSubscribers(); } - protected function toReportFormat(DOMElement $logNode) : DOMElement + public function executionStarted(): void { - $clover = $logNode->ownerDocument->createElement('clover'); - $clover->setAttribute('outputFile', $logNode->getAttribute('target')); - return $clover; + gc_disable(); + $this->facade->emitter()->testRunnerDisabledGarbageCollection(); + gc_collect_cycles(); + $this->facade->emitter()->testRunnerTriggeredGarbageCollection(); + } + public function executionFinished(): void + { + gc_collect_cycles(); + $this->facade->emitter()->testRunnerTriggeredGarbageCollection(); + gc_enable(); + $this->facade->emitter()->testRunnerEnabledGarbageCollection(); + } + public function testFinished(): void + { + $this->tests++; + if ($this->tests === $this->threshold) { + gc_collect_cycles(); + $this->facade->emitter()->testRunnerTriggeredGarbageCollection(); + $this->tests = 0; + } + } + /** + * @throws EventFacadeIsSealedException + * @throws UnknownSubscriberTypeException + */ + private function registerSubscribers(): void + { + $this->facade->registerSubscribers(new \PHPUnit\Runner\GarbageCollection\ExecutionStartedSubscriber($this), new \PHPUnit\Runner\GarbageCollection\ExecutionFinishedSubscriber($this), new \PHPUnit\Runner\GarbageCollection\TestFinishedSubscriber($this)); } } ownerDocument->createElement('crap4j'); - $crap4j->setAttribute('outputFile', $logNode->getAttribute('target')); - $this->migrateAttributes($logNode, $crap4j, ['threshold']); - return $crap4j; + $this->handler()->executionFinished(); } } ownerDocument->createElement('html'); - $html->setAttribute('outputDirectory', $logNode->getAttribute('target')); - $this->migrateAttributes($logNode, $html, ['lowUpperBound', 'highLowerBound']); - return $html; + $this->handler()->executionStarted(); } } handler = $handler; } - protected function toReportFormat(DOMElement $logNode) : DOMElement + protected function handler(): \PHPUnit\Runner\GarbageCollection\GarbageCollectionHandler { - $php = $logNode->ownerDocument->createElement('php'); - $php->setAttribute('outputFile', $logNode->getAttribute('target')); - return $php; + return $this->handler; } } ownerDocument->createElement('text'); - $text->setAttribute('outputFile', $logNode->getAttribute('target')); - $this->migrateAttributes($logNode, $text, ['showUncoveredFiles', 'showOnlySummary']); - return $text; + $this->handler()->testFinished(); } } ownerDocument->createElement('xml'); - $xml->setAttribute('outputDirectory', $logNode->getAttribute('target')); - return $xml; + if (!is_file($filename)) { + throw new \PHPUnit\Runner\FileDoesNotExistException($filename); + } + $this->filename = $filename; + $this->phpUtil = $phpUtil ?: AbstractPhpProcess::factory(); } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TextUI\XmlConfiguration; - -use DOMDocument; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class IntroduceCoverageElement implements \PHPUnit\TextUI\XmlConfiguration\Migration -{ - public function migrate(DOMDocument $document) : void + /** + * Counts the number of test cases executed by run(TestResult result). + */ + public function count(): int { - $coverage = $document->createElement('coverage'); - $document->documentElement->insertBefore($coverage, $document->documentElement->firstChild); + return 1; } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TextUI\XmlConfiguration; - -use function sprintf; -use DOMDocument; -use DOMElement; -use DOMXPath; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -abstract class LogToReportMigration implements \PHPUnit\TextUI\XmlConfiguration\Migration -{ /** - * @throws MigrationException + * Runs a test and collects its result in a TestResult instance. + * + * @throws \PHPUnit\Framework\Exception + * @throws \SebastianBergmann\Template\InvalidArgumentException + * @throws Exception + * @throws InvalidArgumentException + * @throws NoPreviousThrowableException + * @throws ReflectionException + * @throws StaticAnalysisCacheNotConfiguredException + * @throws TestIdMissingException + * @throws UnintentionallyCoveredCodeException + * + * @noinspection RepetitiveMethodCallsInspection */ - public function migrate(DOMDocument $document) : void + public function run(): void { - $coverage = $document->getElementsByTagName('coverage')->item(0); - if (!$coverage instanceof DOMElement) { - throw new \PHPUnit\TextUI\XmlConfiguration\MigrationException('Unexpected state - No coverage element'); + $emitter = EventFacade::emitter(); + $emitter->testPreparationStarted($this->valueObjectForEvents()); + try { + $sections = $this->parse(); + } catch (\PHPUnit\Runner\Exception $e) { + $emitter->testPrepared($this->valueObjectForEvents()); + $emitter->testErrored($this->valueObjectForEvents(), ThrowableBuilder::from($e)); + $emitter->testFinished($this->valueObjectForEvents(), 0); + return; } - $logNode = $this->findLogNode($document); - if ($logNode === null) { + $code = $this->render($sections['FILE']); + $xfail = \false; + $settings = $this->parseIniSection($this->settings(\PHPUnit\Runner\CodeCoverage::instance()->isActive())); + $emitter->testPrepared($this->valueObjectForEvents()); + 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 ($this->shouldTestBeSkipped($sections, $settings)) { return; } - $reportChild = $this->toReportFormat($logNode); - $report = $coverage->getElementsByTagName('report')->item(0); - if ($report === null) { - $report = $coverage->appendChild($document->createElement('report')); + if (isset($sections['XFAIL'])) { + $xfail = trim($sections['XFAIL']); } - $report->appendChild($reportChild); - $logNode->parentNode->removeChild($logNode); - } - protected function migrateAttributes(DOMElement $src, DOMElement $dest, array $attributes) : void - { - foreach ($attributes as $attr) { - if (!$src->hasAttribute($attr)) { - continue; + if (isset($sections['STDIN'])) { + $this->phpUtil->setStdin($sections['STDIN']); + } + if (isset($sections['ARGS'])) { + $this->phpUtil->setArgs($sections['ARGS']); + } + if (\PHPUnit\Runner\CodeCoverage::instance()->isActive()) { + $codeCoverageCacheDirectory = null; + if (\PHPUnit\Runner\CodeCoverage::instance()->codeCoverage()->cachesStaticAnalysis()) { + $codeCoverageCacheDirectory = \PHPUnit\Runner\CodeCoverage::instance()->codeCoverage()->cacheDirectory(); } - $dest->setAttribute($attr, $src->getAttribute($attr)); - $src->removeAttribute($attr); + $this->renderForCoverage($code, \PHPUnit\Runner\CodeCoverage::instance()->codeCoverage()->collectsBranchAndPathCoverage(), $codeCoverageCacheDirectory); + } + $jobResult = $this->phpUtil->runJob($code, $this->stringifyIni($settings)); + $this->output = $jobResult['stdout'] ?? ''; + if (\PHPUnit\Runner\CodeCoverage::instance()->isActive()) { + $coverage = $this->cleanupForCoverage(); + \PHPUnit\Runner\CodeCoverage::instance()->codeCoverage()->start($this->filename, TestSize::large()); + \PHPUnit\Runner\CodeCoverage::instance()->codeCoverage()->append($coverage, $this->filename, \true, TestStatus::unknown()); + } + 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, (string) $trace[0]['file'], (int) $trace[0]['line'], $trace, $comparisonFailure ? $diff : ''); + } + if ($failure instanceof IncompleteTestError) { + $emitter->testMarkedAsIncomplete($this->valueObjectForEvents(), ThrowableBuilder::from($failure)); + } else { + $emitter->testFailed($this->valueObjectForEvents(), ThrowableBuilder::from($failure), null); + } + } catch (Throwable $t) { + $emitter->testErrored($this->valueObjectForEvents(), ThrowableBuilder::from($t)); } + $this->runClean($sections, \PHPUnit\Runner\CodeCoverage::instance()->isActive()); + $emitter->testFinished($this->valueObjectForEvents(), 1); } - protected abstract function forType() : string; - protected abstract function toReportFormat(DOMElement $logNode) : DOMElement; - private function findLogNode(DOMDocument $document) : ?DOMElement + /** + * Returns the name of the test case. + */ + public function getName(): string { - $logNode = (new DOMXPath($document))->query(sprintf('//logging/log[@type="%s"]', $this->forType()))->item(0); - if (!$logNode instanceof DOMElement) { - return null; - } - return $logNode; + return $this->toString(); } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TextUI\XmlConfiguration; - -use DOMDocument; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -interface Migration -{ - public function migrate(DOMDocument $document) : void; -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TextUI\XmlConfiguration; - -use DOMDocument; -use DOMElement; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class MoveAttributesFromFilterWhitelistToCoverage implements \PHPUnit\TextUI\XmlConfiguration\Migration -{ /** - * @throws MigrationException + * Returns a string representation of the test case. */ - public function migrate(DOMDocument $document) : void + public function toString(): string { - $whitelist = $document->getElementsByTagName('whitelist')->item(0); - if (!$whitelist) { - return; - } - $coverage = $document->getElementsByTagName('coverage')->item(0); - if (!$coverage instanceof DOMElement) { - throw new \PHPUnit\TextUI\XmlConfiguration\MigrationException('Unexpected state - No coverage element'); - } - $map = ['addUncoveredFilesFromWhitelist' => 'includeUncoveredFiles', 'processUncoveredFilesFromWhitelist' => 'processUncoveredFiles']; - foreach ($map as $old => $new) { - if (!$whitelist->hasAttribute($old)) { - continue; - } - $coverage->setAttribute($new, $whitelist->getAttribute($old)); - $whitelist->removeAttribute($old); - } + return $this->filename; + } + public function usesDataProvider(): bool + { + return \false; + } + public function numberOfAssertionsPerformed(): int + { + return 1; + } + public function output(): string + { + return $this->output; + } + public function hasOutput(): bool + { + return !empty($this->output); + } + public function sortId(): string + { + return $this->filename; } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TextUI\XmlConfiguration; - -use DOMDocument; -use DOMElement; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class MoveAttributesFromRootToCoverage implements \PHPUnit\TextUI\XmlConfiguration\Migration -{ /** - * @throws MigrationException + * @psalm-return list */ - public function migrate(DOMDocument $document) : void + public function provides(): array { - $map = ['disableCodeCoverageIgnore' => 'disableCodeCoverageIgnore', 'ignoreDeprecatedCodeUnitsFromCodeCoverage' => 'ignoreDeprecatedCodeUnits']; - $root = $document->documentElement; - $coverage = $document->getElementsByTagName('coverage')->item(0); - if (!$coverage instanceof DOMElement) { - throw new \PHPUnit\TextUI\XmlConfiguration\MigrationException('Unexpected state - No coverage element'); - } - foreach ($map as $old => $new) { - if (!$root->hasAttribute($old)) { - continue; - } - $coverage->setAttribute($new, $root->getAttribute($old)); - $root->removeAttribute($old); - } + return []; } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TextUI\XmlConfiguration; - -use function assert; -use function in_array; -use DOMDocument; -use DOMElement; -use PHPUnit\Util\Xml\SnapshotNodeList; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class MoveWhitelistExcludesToCoverage implements \PHPUnit\TextUI\XmlConfiguration\Migration -{ /** - * @throws MigrationException + * @psalm-return list */ - public function migrate(DOMDocument $document) : void + public function requires(): array { - $whitelist = $document->getElementsByTagName('whitelist')->item(0); - if ($whitelist === null) { - return; - } - $excludeNodes = SnapshotNodeList::fromNodeList($whitelist->getElementsByTagName('exclude')); - if ($excludeNodes->count() === 0) { - return; - } - $coverage = $document->getElementsByTagName('coverage')->item(0); - if (!$coverage instanceof DOMElement) { - throw new \PHPUnit\TextUI\XmlConfiguration\MigrationException('Unexpected state - No coverage element'); - } - $targetExclude = $coverage->getElementsByTagName('exclude')->item(0); - if ($targetExclude === null) { - $targetExclude = $coverage->appendChild($document->createElement('exclude')); - } - foreach ($excludeNodes as $excludeNode) { - assert($excludeNode instanceof DOMElement); - foreach (SnapshotNodeList::fromNodeList($excludeNode->childNodes) as $child) { - if (!$child instanceof DOMElement || !in_array($child->nodeName, ['directory', 'file'], \true)) { - continue; - } - $targetExclude->appendChild($child); - } - if ($excludeNode->getElementsByTagName('*')->count() !== 0) { - throw new \PHPUnit\TextUI\XmlConfiguration\MigrationException('Dangling child elements in exclude found.'); - } - $whitelist->removeChild($excludeNode); - } + return []; } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TextUI\XmlConfiguration; - -use DOMDocument; -use DOMElement; -use PHPUnit\Util\Xml\SnapshotNodeList; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class MoveWhitelistIncludesToCoverage implements \PHPUnit\TextUI\XmlConfiguration\Migration -{ /** - * @throws MigrationException + * @internal This method is not covered by the backward compatibility promise for PHPUnit */ - public function migrate(DOMDocument $document) : void + public function valueObjectForEvents(): Phpt { - $whitelist = $document->getElementsByTagName('whitelist')->item(0); - if ($whitelist === null) { - return; - } - $coverage = $document->getElementsByTagName('coverage')->item(0); - if (!$coverage instanceof DOMElement) { - throw new \PHPUnit\TextUI\XmlConfiguration\MigrationException('Unexpected state - No coverage element'); + return new Phpt($this->filename); + } + /** + * Parse --INI-- section key value pairs and return as array. + */ + private function parseIniSection(array|string $content, array $ini = []): array + { + if (is_string($content)) { + $content = explode("\n", trim($content)); } - $include = $document->createElement('include'); - $coverage->appendChild($include); - foreach (SnapshotNodeList::fromNodeList($whitelist->childNodes) as $child) { - if (!$child instanceof DOMElement) { + foreach ($content as $setting) { + if (!str_contains($setting, '=')) { continue; } - if (!($child->nodeName === 'directory' || $child->nodeName === 'file')) { + $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; } - $include->appendChild($child); + $ini[$name] = $value; } + return $ini; } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TextUI\XmlConfiguration; - -use DOMDocument; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class RemoveCacheTokensAttribute implements \PHPUnit\TextUI\XmlConfiguration\Migration -{ - public function migrate(DOMDocument $document) : void + private function parseEnvSection(string $content): array { - $root = $document->documentElement; - if ($root->hasAttribute('cacheTokens')) { - $root->removeAttribute('cacheTokens'); + $env = []; + foreach (explode("\n", trim($content)) as $e) { + $e = explode('=', trim($e), 2); + if ($e[0] !== '' && isset($e[1])) { + $env[$e[0]] = $e[1]; + } } + return $env; } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TextUI\XmlConfiguration; - -use function sprintf; -use DOMDocument; -use DOMElement; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class RemoveEmptyFilter implements \PHPUnit\TextUI\XmlConfiguration\Migration -{ /** - * @throws MigrationException + * @throws Exception + * @throws ExpectationFailedException */ - public function migrate(DOMDocument $document) : void + private function assertPhptExpectation(array $sections, string $output): void { - $whitelist = $document->getElementsByTagName('whitelist')->item(0); - if ($whitelist instanceof DOMElement) { - $this->ensureEmpty($whitelist); - $whitelist->parentNode->removeChild($whitelist); - } - $filter = $document->getElementsByTagName('filter')->item(0); - if ($filter instanceof DOMElement) { - $this->ensureEmpty($filter); - $filter->parentNode->removeChild($filter); + $assertions = ['EXPECT' => 'assertEquals', 'EXPECTF' => 'assertStringMatchesFormat', 'EXPECTREGEX' => 'assertMatchesRegularExpression']; + $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; + Assert::$sectionAssertion($expected, $actual); + return; + } } + throw new \PHPUnit\Runner\InvalidPhptFileException(); } - /** - * @throws MigrationException - */ - private function ensureEmpty(DOMElement $element) : void + private function shouldTestBeSkipped(array $sections, array $settings): bool { - if ($element->attributes->length > 0) { - throw new \PHPUnit\TextUI\XmlConfiguration\MigrationException(sprintf('%s element has unexpected attributes', $element->nodeName)); + if (!isset($sections['SKIPIF'])) { + return \false; } - if ($element->getElementsByTagName('*')->length > 0) { - throw new \PHPUnit\TextUI\XmlConfiguration\MigrationException(sprintf('%s element has unexpected children', $element->nodeName)); + $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); + } + EventFacade::emitter()->testSkipped($this->valueObjectForEvents(), $message); + EventFacade::emitter()->testFinished($this->valueObjectForEvents(), 0); + return \true; } + return \false; } -} - + private function runClean(array $sections, bool $collectCoverage): void + { + $this->phpUtil->setStdin(''); + $this->phpUtil->setArgs(''); + if (isset($sections['CLEAN'])) { + $cleanCode = $this->render($sections['CLEAN']); + $this->phpUtil->runJob($cleanCode, $this->settings($collectCoverage)); + } + } + /** + * @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 \PHPUnit\Runner\InvalidPhptFileException(); + } + $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 \PHPUnit\Runner\InvalidPhptFileException(); + } + foreach ($unsupportedSections as $section) { + if (isset($sections[$section])) { + throw new \PHPUnit\Runner\UnsupportedPhptSectionException($section); + } + } + 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 \PHPUnit\Runner\PhptExternalFileCannotBeLoadedException($section, $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']; + } + /** + * @throws \SebastianBergmann\Template\InvalidArgumentException + */ + private function renderForCoverage(string &$job, bool $pathCoverage, ?string $codeCoverageCacheDirectory): void + { + $files = $this->getCoverageFiles(); + $template = new Template(__DIR__ . '/../Util/PHP/Template/PhptTestCase.tpl'); + $composerAutoload = '\'\''; + if (defined('PHPUNIT_COMPOSER_INSTALL')) { + $composerAutoload = var_export(PHPUNIT_COMPOSER_INSTALL, \true); + } + $phar = '\'\''; + if (defined('__PHPUNIT_PHAR__')) { + $phar = var_export(__PHPUNIT_PHAR__, \true); + } + if ($codeCoverageCacheDirectory === null) { + $codeCoverageCacheDirectory = 'null'; + } else { + $codeCoverageCacheDirectory = "'" . $codeCoverageCacheDirectory . "'"; + } + $bootstrap = ''; + if (ConfigurationRegistry::get()->hasBootstrap()) { + $bootstrap = ConfigurationRegistry::get()->bootstrap(); + } + $template->setVar(['bootstrap' => $bootstrap, 'composerAutoload' => $composerAutoload, 'phar' => $phar, 'job' => $files['job'], 'coverageFile' => $files['coverage'], 'driverMethod' => $pathCoverage ? 'forLineAndPathCoverage' : 'forLineCoverage', 'codeCoverageCacheDirectory' => $codeCoverageCacheDirectory]); + file_put_contents($files['job'], $job); + $job = $template->render(); + } + private function cleanupForCoverage(): RawCodeCoverageData + { + $coverage = RawCodeCoverageData::fromXdebugWithoutPathCoverage([]); + $files = $this->getCoverageFiles(); + $buffer = \false; + if (is_file($files['coverage'])) { + $buffer = @file_get_contents($files['coverage']); + } + if ($buffer !== \false) { + $coverage = @unserialize($buffer); + if ($coverage === \false) { + $coverage = RawCodeCoverageData::fromXdebugWithoutPathCoverage([]); + } + } + 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 (str_starts_with($line, '+')) { + $needle = $this->getCleanDiffLine($previousLine); + break; + } + if (str_starts_with($line, '-')) { + $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): array + { + $needle = trim($needle); + if (empty($needle)) { + return [['file' => realpath($this->filename), 'line' => 1]]; + } + $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 (str_contains($line, $needle)) { + return [['file' => realpath($this->filename), 'line' => $offset]]; + } + $offset++; + } + } + return [['file' => realpath($this->filename), 'line' => 1]]; + } + /** + * @psalm-return list + */ + private function settings(bool $collectCoverage): array + { + $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', 'open_basedir=', 'output_buffering=Off', 'output_handler=', 'report_memleaks=0', 'report_zend_debug=0']; + if (extension_loaded('pcov')) { + if ($collectCoverage) { + $settings[] = 'pcov.enabled=1'; + } else { + $settings[] = 'pcov.enabled=0'; + } + } + if (extension_loaded('xdebug')) { + if ($collectCoverage) { + $settings[] = 'xdebug.mode=coverage'; + } else { + $settings[] = 'xdebug.mode=off'; + } + } + return $settings; + } +} + * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\TextUI\XmlConfiguration; +namespace PHPUnit\Runner\ResultCache; +use const DIRECTORY_SEPARATOR; +use function array_keys; use function assert; -use DOMDocument; -use DOMElement; -use PHPUnit\Util\Xml\SnapshotNodeList; +use function dirname; +use function file_get_contents; +use function file_put_contents; +use function is_array; +use function is_dir; +use function is_file; +use function json_decode; +use function json_encode; +use PHPUnit\Framework\TestStatus\TestStatus; +use PHPUnit\Runner\DirectoryDoesNotExistException; +use PHPUnit\Runner\Exception; +use PHPUnit\Util\Filesystem; /** * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class RemoveLogTypes implements \PHPUnit\TextUI\XmlConfiguration\Migration +final class DefaultResultCache implements \PHPUnit\Runner\ResultCache\ResultCache { - public function migrate(DOMDocument $document) : void + /** + * @var int + */ + private const VERSION = 1; + /** + * @var string + */ + private const DEFAULT_RESULT_CACHE_FILENAME = '.phpunit.result.cache'; + private readonly string $cacheFilename; + /** + * @psalm-var array + */ + private array $defects = []; + /** + * @psalm-var array + */ + private array $times = []; + public function __construct(?string $filepath = null) { - $logging = $document->getElementsByTagName('logging')->item(0); - if (!$logging instanceof DOMElement) { + if ($filepath !== null && is_dir($filepath)) { + $filepath .= DIRECTORY_SEPARATOR . self::DEFAULT_RESULT_CACHE_FILENAME; + } + $this->cacheFilename = $filepath ?? $_ENV['PHPUNIT_RESULT_CACHE'] ?? self::DEFAULT_RESULT_CACHE_FILENAME; + } + public function setStatus(string $id, TestStatus $status): void + { + if ($status->isSuccess()) { return; } - foreach (SnapshotNodeList::fromNodeList($logging->getElementsByTagName('log')) as $logNode) { - assert($logNode instanceof DOMElement); - switch ($logNode->getAttribute('type')) { - case 'json': - case 'tap': - $logging->removeChild($logNode); - } + $this->defects[$id] = $status; + } + public function status(string $id): TestStatus + { + return $this->defects[$id] ?? TestStatus::unknown(); + } + public function setTime(string $id, float $time): void + { + $this->times[$id] = $time; + } + public function time(string $id): float + { + return $this->times[$id] ?? 0.0; + } + public function load(): void + { + if (!is_file($this->cacheFilename)) { + return; + } + $contents = file_get_contents($this->cacheFilename); + if ($contents === \false) { + return; + } + $data = json_decode($contents, \true); + if ($data === null) { + return; + } + if (!isset($data['version'])) { + return; + } + if ($data['version'] !== self::VERSION) { + return; + } + assert(isset($data['defects']) && is_array($data['defects'])); + assert(isset($data['times']) && is_array($data['times'])); + foreach (array_keys($data['defects']) as $test) { + $data['defects'][$test] = TestStatus::from($data['defects'][$test]); + } + $this->defects = $data['defects']; + $this->times = $data['times']; + } + /** + * @throws Exception + */ + public function persist(): void + { + if (!Filesystem::createDirectory(dirname($this->cacheFilename))) { + throw new DirectoryDoesNotExistException(dirname($this->cacheFilename)); } + $data = ['version' => self::VERSION, 'defects' => [], 'times' => $this->times]; + foreach ($this->defects as $test => $status) { + $data['defects'][$test] = $status->asInt(); + } + file_put_contents($this->cacheFilename, json_encode($data), \LOCK_EX); } } documentElement->setAttributeNS('http://www.w3.org/2001/XMLSchema-instance', 'xsi:noNamespaceSchemaLocation', 'https://schema.phpunit.de/9.3/phpunit.xsd'); } } detect($filename); - if (!$origin->detected()) { - throw new \PHPUnit\TextUI\XmlConfiguration\Exception(sprintf('"%s" is not a valid PHPUnit XML configuration file that can be migrated', $filename)); - } - $configurationDocument = (new XmlLoader())->loadFile($filename, \false, \true, \true); - foreach ((new \PHPUnit\TextUI\XmlConfiguration\MigrationBuilder())->build($origin->version()) as $migration) { - $migration->migrate($configurationDocument); - } - $configurationDocument->formatOutput = \true; - $configurationDocument->preserveWhiteSpace = \false; - return $configurationDocument->saveXML(); - } + public function setStatus(string $id, TestStatus $status): void; + public function status(string $id): TestStatus; + public function setTime(string $id, float $time): void; + public function time(string $id): float; + public function load(): void; + public function persist(): void; } cache = $cache; + $this->registerSubscribers($facade); + } + public function testSuiteStarted(): void + { + $this->testSuite++; + } + public function testSuiteFinished(): void + { + $this->testSuite--; + if ($this->testSuite === 0) { + $this->cache->persist(); + } + } + public function testPrepared(Prepared $event): void + { + $this->time = $event->telemetryInfo()->time(); + } + public function testMarkedIncomplete(MarkedIncomplete $event): void + { + $this->cache->setStatus($event->test()->id(), TestStatus::incomplete($event->throwable()->message())); + } + public function testConsideredRisky(ConsideredRisky $event): void + { + $this->cache->setStatus($event->test()->id(), TestStatus::risky($event->message())); + } + public function testErrored(Errored $event): void + { + $this->cache->setStatus($event->test()->id(), TestStatus::error($event->throwable()->message())); + } + public function testFailed(Failed $event): void + { + $this->cache->setStatus($event->test()->id(), TestStatus::failure($event->throwable()->message())); + } /** - * @var mixed + * @throws \PHPUnit\Event\InvalidArgumentException + * @throws InvalidArgumentException */ - private $value; - public function __construct(string $name, $value) + public function testSkipped(Skipped $event): void { - $this->name = $name; - $this->value = $value; + $this->cache->setStatus($event->test()->id(), TestStatus::skipped($event->message())); + $this->cache->setTime($event->test()->id(), $this->duration($event)); } - public function name() : string + /** + * @throws \PHPUnit\Event\InvalidArgumentException + * @throws InvalidArgumentException + */ + public function testFinished(Finished $event): void { - return $this->name; + $this->cache->setTime($event->test()->id(), $this->duration($event)); + $this->time = null; } - public function value() + /** + * @throws \PHPUnit\Event\InvalidArgumentException + * @throws InvalidArgumentException + */ + private function duration(Event $event): float { - return $this->value; + if ($this->time === null) { + return 0.0; + } + return round($event->telemetryInfo()->time()->duration($this->time)->asFloat(), 3); + } + /** + * @throws EventFacadeIsSealedException + * @throws UnknownSubscriberTypeException + */ + private function registerSubscribers(Facade $facade): void + { + $facade->registerSubscribers(new \PHPUnit\Runner\ResultCache\TestSuiteStartedSubscriber($this), new \PHPUnit\Runner\ResultCache\TestSuiteFinishedSubscriber($this), new \PHPUnit\Runner\ResultCache\TestPreparedSubscriber($this), new \PHPUnit\Runner\ResultCache\TestMarkedIncompleteSubscriber($this), new \PHPUnit\Runner\ResultCache\TestConsideredRiskySubscriber($this), new \PHPUnit\Runner\ResultCache\TestErroredSubscriber($this), new \PHPUnit\Runner\ResultCache\TestFailedSubscriber($this), new \PHPUnit\Runner\ResultCache\TestSkippedSubscriber($this), new \PHPUnit\Runner\ResultCache\TestFinishedSubscriber($this)); } } */ -final class ConstantCollection implements Countable, IteratorAggregate +abstract class Subscriber { - /** - * @var Constant[] - */ - private $constants; - /** - * @param Constant[] $constants - */ - public static function fromArray(array $constants) : self - { - return new self(...$constants); - } - private function __construct(\PHPUnit\TextUI\XmlConfiguration\Constant ...$constants) - { - $this->constants = $constants; - } - /** - * @return Constant[] - */ - public function asArray() : array - { - return $this->constants; - } - public function count() : int + private readonly \PHPUnit\Runner\ResultCache\ResultCacheHandler $handler; + public function __construct(\PHPUnit\Runner\ResultCache\ResultCacheHandler $handler) { - return count($this->constants); + $this->handler = $handler; } - public function getIterator() : \PHPUnit\TextUI\XmlConfiguration\ConstantCollectionIterator + protected function handler(): \PHPUnit\Runner\ResultCache\ResultCacheHandler { - return new \PHPUnit\TextUI\XmlConfiguration\ConstantCollectionIterator($this); + return $this->handler; } } */ -final class ConstantCollectionIterator implements Countable, Iterator +final class TestConsideredRiskySubscriber extends \PHPUnit\Runner\ResultCache\Subscriber implements ConsideredRiskySubscriber { - /** - * @var Constant[] - */ - private $constants; - /** - * @var int - */ - private $position; - public function __construct(\PHPUnit\TextUI\XmlConfiguration\ConstantCollection $constants) - { - $this->constants = $constants->asArray(); - } - public function count() : int - { - return iterator_count($this); - } - public function rewind() : void - { - $this->position = 0; - } - public function valid() : bool - { - return $this->position < count($this->constants); - } - public function key() : int - { - return $this->position; - } - public function current() : \PHPUnit\TextUI\XmlConfiguration\Constant - { - return $this->constants[$this->position]; - } - public function next() : void + public function notify(ConsideredRisky $event): void { - $this->position++; + $this->handler()->testConsideredRisky($event); } } name = $name; - $this->value = $value; - } - public function name() : string - { - return $this->name; - } - public function value() : string - { - return $this->value; + $this->handler()->testErrored($event); } } */ -final class IniSettingCollection implements Countable, IteratorAggregate +final class TestFailedSubscriber extends \PHPUnit\Runner\ResultCache\Subscriber implements FailedSubscriber { - /** - * @var IniSetting[] - */ - private $iniSettings; - /** - * @param IniSetting[] $iniSettings - */ - public static function fromArray(array $iniSettings) : self - { - return new self(...$iniSettings); - } - private function __construct(\PHPUnit\TextUI\XmlConfiguration\IniSetting ...$iniSettings) + public function notify(Failed $event): void { - $this->iniSettings = $iniSettings; - } - /** - * @return IniSetting[] - */ - public function asArray() : array - { - return $this->iniSettings; - } - public function count() : int - { - return count($this->iniSettings); - } - public function getIterator() : \PHPUnit\TextUI\XmlConfiguration\IniSettingCollectionIterator - { - return new \PHPUnit\TextUI\XmlConfiguration\IniSettingCollectionIterator($this); + $this->handler()->testFailed($event); } } */ -final class IniSettingCollectionIterator implements Countable, Iterator +final class TestFinishedSubscriber extends \PHPUnit\Runner\ResultCache\Subscriber implements FinishedSubscriber { /** - * @var IniSetting[] - */ - private $iniSettings; - /** - * @var int + * @throws \PHPUnit\Framework\InvalidArgumentException + * @throws InvalidArgumentException */ - private $position; - public function __construct(\PHPUnit\TextUI\XmlConfiguration\IniSettingCollection $iniSettings) - { - $this->iniSettings = $iniSettings->asArray(); - } - public function count() : int - { - return iterator_count($this); - } - public function rewind() : void - { - $this->position = 0; - } - public function valid() : bool - { - return $this->position < count($this->iniSettings); - } - public function key() : int - { - return $this->position; - } - public function current() : \PHPUnit\TextUI\XmlConfiguration\IniSetting - { - return $this->iniSettings[$this->position]; - } - public function next() : void + public function notify(Finished $event): void { - $this->position++; + $this->handler()->testFinished($event); } } handler()->testMarkedIncomplete($event); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Runner\ResultCache; + +use PHPUnit\Event\Test\Prepared; +use PHPUnit\Event\Test\PreparedSubscriber; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class TestPreparedSubscriber extends \PHPUnit\Runner\ResultCache\Subscriber implements PreparedSubscriber +{ + public function notify(Prepared $event): void + { + $this->handler()->testPrepared($event); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Runner\ResultCache; + +use PHPUnit\Event\InvalidArgumentException; +use PHPUnit\Event\Test\Skipped; +use PHPUnit\Event\Test\SkippedSubscriber; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class TestSkippedSubscriber extends \PHPUnit\Runner\ResultCache\Subscriber implements SkippedSubscriber +{ + /** + * @throws \PHPUnit\Framework\InvalidArgumentException + * @throws InvalidArgumentException + */ + public function notify(Skipped $event): void + { + $this->handler()->testSkipped($event); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Runner\ResultCache; + +use PHPUnit\Event\TestSuite\Finished; +use PHPUnit\Event\TestSuite\FinishedSubscriber; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class TestSuiteFinishedSubscriber extends \PHPUnit\Runner\ResultCache\Subscriber implements FinishedSubscriber +{ + public function notify(Finished $event): void + { + $this->handler()->testSuiteFinished(); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Runner\ResultCache; + +use PHPUnit\Event\TestSuite\Started; +use PHPUnit\Event\TestSuite\StartedSubscriber; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class TestSuiteStartedSubscriber extends \PHPUnit\Runner\ResultCache\Subscriber implements StartedSubscriber +{ + public function notify(Started $event): void + { + $this->handler()->testSuiteStarted(); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestRunner\TestResult; + +use function array_values; +use function assert; +use function implode; +use function str_contains; +use PHPUnit\Event\Code\TestMethod; +use PHPUnit\Event\EventFacadeIsSealedException; +use PHPUnit\Event\Facade; +use PHPUnit\Event\Test\BeforeFirstTestMethodErrored; +use PHPUnit\Event\Test\ConsideredRisky; +use PHPUnit\Event\Test\DeprecationTriggered; +use PHPUnit\Event\Test\Errored; +use PHPUnit\Event\Test\ErrorTriggered; +use PHPUnit\Event\Test\Failed; +use PHPUnit\Event\Test\Finished; +use PHPUnit\Event\Test\MarkedIncomplete; +use PHPUnit\Event\Test\NoticeTriggered; +use PHPUnit\Event\Test\PhpDeprecationTriggered; +use PHPUnit\Event\Test\PhpNoticeTriggered; +use PHPUnit\Event\Test\PhpunitDeprecationTriggered; +use PHPUnit\Event\Test\PhpunitErrorTriggered; +use PHPUnit\Event\Test\PhpunitWarningTriggered; +use PHPUnit\Event\Test\PhpWarningTriggered; +use PHPUnit\Event\Test\Skipped as TestSkipped; +use PHPUnit\Event\Test\WarningTriggered; +use PHPUnit\Event\TestRunner\DeprecationTriggered as TestRunnerDeprecationTriggered; +use PHPUnit\Event\TestRunner\ExecutionStarted; +use PHPUnit\Event\TestRunner\WarningTriggered as TestRunnerWarningTriggered; +use PHPUnit\Event\TestSuite\Finished as TestSuiteFinished; +use PHPUnit\Event\TestSuite\Skipped as TestSuiteSkipped; +use PHPUnit\Event\TestSuite\Started as TestSuiteStarted; +use PHPUnit\Event\TestSuite\TestSuiteForTestClass; +use PHPUnit\Event\TestSuite\TestSuiteForTestMethodWithDataProvider; +use PHPUnit\Event\UnknownSubscriberTypeException; +use PHPUnit\TestRunner\TestResult\Issues\Issue; +use PHPUnit\TextUI\Configuration\Source; +use PHPUnit\TextUI\Configuration\SourceFilter; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class Collector +{ + private readonly Source $source; + private int $numberOfTests = 0; + private int $numberOfTestsRun = 0; + private int $numberOfAssertions = 0; + private bool $prepared = \false; + private bool $currentTestSuiteForTestClassFailed = \false; + /** + * @psalm-var non-negative-int + */ + private int $numberOfIssuesIgnoredByBaseline = 0; + /** + * @psalm-var list + */ + private array $testErroredEvents = []; + /** + * @psalm-var list + */ + private array $testFailedEvents = []; + /** + * @psalm-var list + */ + private array $testMarkedIncompleteEvents = []; + /** + * @psalm-var list + */ + private array $testSuiteSkippedEvents = []; + /** + * @psalm-var list */ - private $includePaths; + private array $testSkippedEvents = []; /** - * @var IniSettingCollection + * @psalm-var array> */ - private $iniSettings; + private array $testConsideredRiskyEvents = []; /** - * @var ConstantCollection + * @psalm-var array> */ - private $constants; + private array $testTriggeredPhpunitDeprecationEvents = []; /** - * @var VariableCollection + * @psalm-var array> */ - private $globalVariables; + private array $testTriggeredPhpunitErrorEvents = []; /** - * @var VariableCollection + * @psalm-var array> */ - private $envVariables; + private array $testTriggeredPhpunitWarningEvents = []; /** - * @var VariableCollection + * @psalm-var list */ - private $postVariables; + private array $testRunnerTriggeredWarningEvents = []; /** - * @var VariableCollection + * @psalm-var list */ - private $getVariables; + private array $testRunnerTriggeredDeprecationEvents = []; /** - * @var VariableCollection + * @psalm-var array */ - private $cookieVariables; + private array $errors = []; /** - * @var VariableCollection + * @psalm-var array */ - private $serverVariables; + private array $deprecations = []; /** - * @var VariableCollection + * @psalm-var array */ - private $filesVariables; + private array $notices = []; /** - * @var VariableCollection + * @psalm-var array */ - private $requestVariables; - public function __construct(\PHPUnit\TextUI\XmlConfiguration\DirectoryCollection $includePaths, \PHPUnit\TextUI\XmlConfiguration\IniSettingCollection $iniSettings, \PHPUnit\TextUI\XmlConfiguration\ConstantCollection $constants, \PHPUnit\TextUI\XmlConfiguration\VariableCollection $globalVariables, \PHPUnit\TextUI\XmlConfiguration\VariableCollection $envVariables, \PHPUnit\TextUI\XmlConfiguration\VariableCollection $postVariables, \PHPUnit\TextUI\XmlConfiguration\VariableCollection $getVariables, \PHPUnit\TextUI\XmlConfiguration\VariableCollection $cookieVariables, \PHPUnit\TextUI\XmlConfiguration\VariableCollection $serverVariables, \PHPUnit\TextUI\XmlConfiguration\VariableCollection $filesVariables, \PHPUnit\TextUI\XmlConfiguration\VariableCollection $requestVariables) + private array $warnings = []; + /** + * @psalm-var array + */ + private array $phpDeprecations = []; + /** + * @psalm-var array + */ + private array $phpNotices = []; + /** + * @psalm-var array + */ + private array $phpWarnings = []; + /** + * @throws EventFacadeIsSealedException + * @throws UnknownSubscriberTypeException + */ + public function __construct(Facade $facade, Source $source) { - $this->includePaths = $includePaths; - $this->iniSettings = $iniSettings; - $this->constants = $constants; - $this->globalVariables = $globalVariables; - $this->envVariables = $envVariables; - $this->postVariables = $postVariables; - $this->getVariables = $getVariables; - $this->cookieVariables = $cookieVariables; - $this->serverVariables = $serverVariables; - $this->filesVariables = $filesVariables; - $this->requestVariables = $requestVariables; + $facade->registerSubscribers(new \PHPUnit\TestRunner\TestResult\ExecutionStartedSubscriber($this), new \PHPUnit\TestRunner\TestResult\TestSuiteSkippedSubscriber($this), new \PHPUnit\TestRunner\TestResult\TestSuiteStartedSubscriber($this), new \PHPUnit\TestRunner\TestResult\TestSuiteFinishedSubscriber($this), new \PHPUnit\TestRunner\TestResult\TestPreparedSubscriber($this), new \PHPUnit\TestRunner\TestResult\TestFinishedSubscriber($this), new \PHPUnit\TestRunner\TestResult\BeforeTestClassMethodErroredSubscriber($this), new \PHPUnit\TestRunner\TestResult\TestErroredSubscriber($this), new \PHPUnit\TestRunner\TestResult\TestFailedSubscriber($this), new \PHPUnit\TestRunner\TestResult\TestMarkedIncompleteSubscriber($this), new \PHPUnit\TestRunner\TestResult\TestSkippedSubscriber($this), new \PHPUnit\TestRunner\TestResult\TestConsideredRiskySubscriber($this), new \PHPUnit\TestRunner\TestResult\TestTriggeredDeprecationSubscriber($this), new \PHPUnit\TestRunner\TestResult\TestTriggeredErrorSubscriber($this), new \PHPUnit\TestRunner\TestResult\TestTriggeredNoticeSubscriber($this), new \PHPUnit\TestRunner\TestResult\TestTriggeredPhpDeprecationSubscriber($this), new \PHPUnit\TestRunner\TestResult\TestTriggeredPhpNoticeSubscriber($this), new \PHPUnit\TestRunner\TestResult\TestTriggeredPhpunitDeprecationSubscriber($this), new \PHPUnit\TestRunner\TestResult\TestTriggeredPhpunitErrorSubscriber($this), new \PHPUnit\TestRunner\TestResult\TestTriggeredPhpunitWarningSubscriber($this), new \PHPUnit\TestRunner\TestResult\TestTriggeredPhpWarningSubscriber($this), new \PHPUnit\TestRunner\TestResult\TestTriggeredWarningSubscriber($this), new \PHPUnit\TestRunner\TestResult\TestRunnerTriggeredDeprecationSubscriber($this), new \PHPUnit\TestRunner\TestResult\TestRunnerTriggeredWarningSubscriber($this)); + $this->source = $source; } - public function includePaths() : \PHPUnit\TextUI\XmlConfiguration\DirectoryCollection + public function result(): \PHPUnit\TestRunner\TestResult\TestResult { - return $this->includePaths; + return new \PHPUnit\TestRunner\TestResult\TestResult($this->numberOfTests, $this->numberOfTestsRun, $this->numberOfAssertions, $this->testErroredEvents, $this->testFailedEvents, $this->testConsideredRiskyEvents, $this->testSuiteSkippedEvents, $this->testSkippedEvents, $this->testMarkedIncompleteEvents, $this->testTriggeredPhpunitDeprecationEvents, $this->testTriggeredPhpunitErrorEvents, $this->testTriggeredPhpunitWarningEvents, $this->testRunnerTriggeredDeprecationEvents, $this->testRunnerTriggeredWarningEvents, array_values($this->errors), array_values($this->deprecations), array_values($this->notices), array_values($this->warnings), array_values($this->phpDeprecations), array_values($this->phpNotices), array_values($this->phpWarnings), $this->numberOfIssuesIgnoredByBaseline); } - public function iniSettings() : \PHPUnit\TextUI\XmlConfiguration\IniSettingCollection + public function executionStarted(ExecutionStarted $event): void { - return $this->iniSettings; + $this->numberOfTests = $event->testSuite()->count(); } - public function constants() : \PHPUnit\TextUI\XmlConfiguration\ConstantCollection + public function testSuiteSkipped(TestSuiteSkipped $event): void { - return $this->constants; + $testSuite = $event->testSuite(); + if (!$testSuite->isForTestClass()) { + return; + } + $this->testSuiteSkippedEvents[] = $event; } - public function globalVariables() : \PHPUnit\TextUI\XmlConfiguration\VariableCollection + public function testSuiteStarted(TestSuiteStarted $event): void { - return $this->globalVariables; + $testSuite = $event->testSuite(); + if (!$testSuite->isForTestClass()) { + return; + } + $this->currentTestSuiteForTestClassFailed = \false; } - public function envVariables() : \PHPUnit\TextUI\XmlConfiguration\VariableCollection + public function testSuiteFinished(TestSuiteFinished $event): void { - return $this->envVariables; + if ($this->currentTestSuiteForTestClassFailed) { + return; + } + $testSuite = $event->testSuite(); + if ($testSuite->isWithName()) { + return; + } + if ($testSuite->isForTestMethodWithDataProvider()) { + assert($testSuite instanceof TestSuiteForTestMethodWithDataProvider); + $test = $testSuite->tests()->asArray()[0]; + assert($test instanceof TestMethod); + \PHPUnit\TestRunner\TestResult\PassedTests::instance()->testMethodPassed($test, null); + return; + } + assert($testSuite instanceof TestSuiteForTestClass); + \PHPUnit\TestRunner\TestResult\PassedTests::instance()->testClassPassed($testSuite->className()); } - public function postVariables() : \PHPUnit\TextUI\XmlConfiguration\VariableCollection + public function testPrepared(): void { - return $this->postVariables; + $this->prepared = \true; } - public function getVariables() : \PHPUnit\TextUI\XmlConfiguration\VariableCollection + public function testFinished(Finished $event): void { - return $this->getVariables; + $this->numberOfAssertions += $event->numberOfAssertionsPerformed(); + $this->numberOfTestsRun++; + $this->prepared = \false; } - public function cookieVariables() : \PHPUnit\TextUI\XmlConfiguration\VariableCollection + public function beforeTestClassMethodErrored(BeforeFirstTestMethodErrored $event): void { - return $this->cookieVariables; + $this->testErroredEvents[] = $event; + $this->numberOfTestsRun++; } - public function serverVariables() : \PHPUnit\TextUI\XmlConfiguration\VariableCollection + public function testErrored(Errored $event): void { - return $this->serverVariables; + $this->testErroredEvents[] = $event; + $this->currentTestSuiteForTestClassFailed = \true; + /* + * @todo Eliminate this special case + */ + if (str_contains($event->asString(), 'Test was run in child process and ended unexpectedly')) { + return; + } + if (!$this->prepared) { + $this->numberOfTestsRun++; + } } - public function filesVariables() : \PHPUnit\TextUI\XmlConfiguration\VariableCollection + public function testFailed(Failed $event): void { - return $this->filesVariables; + $this->testFailedEvents[] = $event; + $this->currentTestSuiteForTestClassFailed = \true; } - public function requestVariables() : \PHPUnit\TextUI\XmlConfiguration\VariableCollection + public function testMarkedIncomplete(MarkedIncomplete $event): void { - return $this->requestVariables; + $this->testMarkedIncompleteEvents[] = $event; } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TextUI\XmlConfiguration; - -use const PATH_SEPARATOR; -use function constant; -use function define; -use function defined; -use function getenv; -use function implode; -use function ini_get; -use function ini_set; -use function putenv; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class PhpHandler -{ - public function handle(\PHPUnit\TextUI\XmlConfiguration\Php $configuration) : void + public function testSkipped(TestSkipped $event): void { - $this->handleIncludePaths($configuration->includePaths()); - $this->handleIniSettings($configuration->iniSettings()); - $this->handleConstants($configuration->constants()); - $this->handleGlobalVariables($configuration->globalVariables()); - $this->handleServerVariables($configuration->serverVariables()); - $this->handleEnvVariables($configuration->envVariables()); - $this->handleVariables('_POST', $configuration->postVariables()); - $this->handleVariables('_GET', $configuration->getVariables()); - $this->handleVariables('_COOKIE', $configuration->cookieVariables()); - $this->handleVariables('_FILES', $configuration->filesVariables()); - $this->handleVariables('_REQUEST', $configuration->requestVariables()); + $this->testSkippedEvents[] = $event; + if (!$this->prepared) { + $this->numberOfTestsRun++; + } } - private function handleIncludePaths(\PHPUnit\TextUI\XmlConfiguration\DirectoryCollection $includePaths) : void + public function testConsideredRisky(ConsideredRisky $event): void { - if (!$includePaths->isEmpty()) { - $includePathsAsStrings = []; - foreach ($includePaths as $includePath) { - $includePathsAsStrings[] = $includePath->path(); - } - ini_set('include_path', implode(PATH_SEPARATOR, $includePathsAsStrings) . PATH_SEPARATOR . ini_get('include_path')); + if (!isset($this->testConsideredRiskyEvents[$event->test()->id()])) { + $this->testConsideredRiskyEvents[$event->test()->id()] = []; } + $this->testConsideredRiskyEvents[$event->test()->id()][] = $event; } - private function handleIniSettings(\PHPUnit\TextUI\XmlConfiguration\IniSettingCollection $iniSettings) : void + public function testTriggeredDeprecation(DeprecationTriggered $event): void { - foreach ($iniSettings as $iniSetting) { - $value = $iniSetting->value(); - if (defined($value)) { - $value = (string) constant($value); - } - ini_set($iniSetting->name(), $value); + if ($event->ignoredByTest()) { + return; + } + if ($event->ignoredByBaseline()) { + $this->numberOfIssuesIgnoredByBaseline++; + return; + } + if (!$this->source->ignoreSuppressionOfDeprecations() && $event->wasSuppressed()) { + return; + } + if ($this->source->restrictDeprecations() && !(new SourceFilter())->includes($this->source, $event->file())) { + return; + } + $id = $this->issueId($event); + if (!isset($this->deprecations[$id])) { + $this->deprecations[$id] = Issue::from($event->file(), $event->line(), $event->message(), $event->test()); + return; } + $this->deprecations[$id]->triggeredBy($event->test()); } - private function handleConstants(\PHPUnit\TextUI\XmlConfiguration\ConstantCollection $constants) : void + public function testTriggeredPhpDeprecation(PhpDeprecationTriggered $event): void { - foreach ($constants as $constant) { - if (!defined($constant->name())) { - define($constant->name(), $constant->value()); - } + if ($event->ignoredByTest()) { + return; } + if ($event->ignoredByBaseline()) { + $this->numberOfIssuesIgnoredByBaseline++; + return; + } + if (!$this->source->ignoreSuppressionOfPhpDeprecations() && $event->wasSuppressed()) { + return; + } + if ($this->source->restrictDeprecations() && !(new SourceFilter())->includes($this->source, $event->file())) { + return; + } + $id = $this->issueId($event); + if (!isset($this->phpDeprecations[$id])) { + $this->phpDeprecations[$id] = Issue::from($event->file(), $event->line(), $event->message(), $event->test()); + return; + } + $this->phpDeprecations[$id]->triggeredBy($event->test()); } - private function handleGlobalVariables(\PHPUnit\TextUI\XmlConfiguration\VariableCollection $variables) : void + public function testTriggeredPhpunitDeprecation(PhpunitDeprecationTriggered $event): void { - foreach ($variables as $variable) { - $GLOBALS[$variable->name()] = $variable->value(); + if (!isset($this->testTriggeredPhpunitDeprecationEvents[$event->test()->id()])) { + $this->testTriggeredPhpunitDeprecationEvents[$event->test()->id()] = []; } + $this->testTriggeredPhpunitDeprecationEvents[$event->test()->id()][] = $event; } - private function handleServerVariables(\PHPUnit\TextUI\XmlConfiguration\VariableCollection $variables) : void + public function testTriggeredError(ErrorTriggered $event): void { - foreach ($variables as $variable) { - $_SERVER[$variable->name()] = $variable->value(); + if (!$this->source->ignoreSuppressionOfErrors() && $event->wasSuppressed()) { + return; + } + $id = $this->issueId($event); + if (!isset($this->errors[$id])) { + $this->errors[$id] = Issue::from($event->file(), $event->line(), $event->message(), $event->test()); + return; } + $this->errors[$id]->triggeredBy($event->test()); } - private function handleVariables(string $target, \PHPUnit\TextUI\XmlConfiguration\VariableCollection $variables) : void + public function testTriggeredNotice(NoticeTriggered $event): void { - foreach ($variables as $variable) { - $GLOBALS[$target][$variable->name()] = $variable->value(); + if ($event->ignoredByBaseline()) { + $this->numberOfIssuesIgnoredByBaseline++; + return; + } + if (!$this->source->ignoreSuppressionOfNotices() && $event->wasSuppressed()) { + return; + } + if ($this->source->restrictNotices() && !(new SourceFilter())->includes($this->source, $event->file())) { + return; + } + $id = $this->issueId($event); + if (!isset($this->notices[$id])) { + $this->notices[$id] = Issue::from($event->file(), $event->line(), $event->message(), $event->test()); + return; } + $this->notices[$id]->triggeredBy($event->test()); } - private function handleEnvVariables(\PHPUnit\TextUI\XmlConfiguration\VariableCollection $variables) : void + public function testTriggeredPhpNotice(PhpNoticeTriggered $event): void { - foreach ($variables as $variable) { - $name = $variable->name(); - $value = $variable->value(); - $force = $variable->force(); - if ($force || getenv($name) === \false) { - putenv("{$name}={$value}"); - } - $value = getenv($name); - if ($force || !isset($_ENV[$name])) { - $_ENV[$name] = $value; - } + if ($event->ignoredByBaseline()) { + $this->numberOfIssuesIgnoredByBaseline++; + return; + } + if (!$this->source->ignoreSuppressionOfPhpNotices() && $event->wasSuppressed()) { + return; + } + if ($this->source->restrictNotices() && !(new SourceFilter())->includes($this->source, $event->file())) { + return; + } + $id = $this->issueId($event); + if (!isset($this->phpNotices[$id])) { + $this->phpNotices[$id] = Issue::from($event->file(), $event->line(), $event->message(), $event->test()); + return; + } + $this->phpNotices[$id]->triggeredBy($event->test()); + } + public function testTriggeredWarning(WarningTriggered $event): void + { + if ($event->ignoredByBaseline()) { + $this->numberOfIssuesIgnoredByBaseline++; + return; + } + if (!$this->source->ignoreSuppressionOfWarnings() && $event->wasSuppressed()) { + return; + } + if ($this->source->restrictWarnings() && !(new SourceFilter())->includes($this->source, $event->file())) { + return; + } + $id = $this->issueId($event); + if (!isset($this->warnings[$id])) { + $this->warnings[$id] = Issue::from($event->file(), $event->line(), $event->message(), $event->test()); + return; + } + $this->warnings[$id]->triggeredBy($event->test()); + } + public function testTriggeredPhpWarning(PhpWarningTriggered $event): void + { + if ($event->ignoredByBaseline()) { + $this->numberOfIssuesIgnoredByBaseline++; + return; + } + if (!$this->source->ignoreSuppressionOfPhpWarnings() && $event->wasSuppressed()) { + return; + } + if ($this->source->restrictWarnings() && !(new SourceFilter())->includes($this->source, $event->file())) { + return; + } + $id = $this->issueId($event); + if (!isset($this->phpWarnings[$id])) { + $this->phpWarnings[$id] = Issue::from($event->file(), $event->line(), $event->message(), $event->test()); + return; + } + $this->phpWarnings[$id]->triggeredBy($event->test()); + } + public function testTriggeredPhpunitError(PhpunitErrorTriggered $event): void + { + if (!isset($this->testTriggeredPhpunitErrorEvents[$event->test()->id()])) { + $this->testTriggeredPhpunitErrorEvents[$event->test()->id()] = []; + } + $this->testTriggeredPhpunitErrorEvents[$event->test()->id()][] = $event; + } + public function testTriggeredPhpunitWarning(PhpunitWarningTriggered $event): void + { + if (!isset($this->testTriggeredPhpunitWarningEvents[$event->test()->id()])) { + $this->testTriggeredPhpunitWarningEvents[$event->test()->id()] = []; } + $this->testTriggeredPhpunitWarningEvents[$event->test()->id()][] = $event; + } + public function testRunnerTriggeredDeprecation(TestRunnerDeprecationTriggered $event): void + { + $this->testRunnerTriggeredDeprecationEvents[] = $event; + } + public function testRunnerTriggeredWarning(TestRunnerWarningTriggered $event): void + { + $this->testRunnerTriggeredWarningEvents[] = $event; + } + public function hasErroredTests(): bool + { + return !empty($this->testErroredEvents); + } + public function hasFailedTests(): bool + { + return !empty($this->testFailedEvents); + } + public function hasRiskyTests(): bool + { + return !empty($this->testConsideredRiskyEvents); + } + public function hasSkippedTests(): bool + { + return !empty($this->testSkippedEvents); + } + public function hasIncompleteTests(): bool + { + return !empty($this->testMarkedIncompleteEvents); + } + public function hasDeprecations(): bool + { + return !empty($this->deprecations) || !empty($this->phpDeprecations) || !empty($this->testTriggeredPhpunitDeprecationEvents) || !empty($this->testRunnerTriggeredDeprecationEvents); + } + public function hasNotices(): bool + { + return !empty($this->notices) || !empty($this->phpNotices); + } + public function hasWarnings(): bool + { + return !empty($this->warnings) || !empty($this->phpWarnings) || !empty($this->testTriggeredPhpunitWarningEvents) || !empty($this->testRunnerTriggeredWarningEvents); + } + /** + * @psalm-return non-empty-string + */ + private function issueId(DeprecationTriggered|ErrorTriggered|NoticeTriggered|PhpDeprecationTriggered|PhpNoticeTriggered|PhpWarningTriggered|WarningTriggered $event): string + { + return implode(':', [$event->file(), $event->line(), $event->message()]); } } name = $name; - $this->value = $value; - $this->force = $force; + self::collector(); } - public function name() : string + /** + * @throws EventFacadeIsSealedException + * @throws UnknownSubscriberTypeException + */ + public static function result(): \PHPUnit\TestRunner\TestResult\TestResult { - return $this->name; + return self::collector()->result(); } - public function value() + /** + * @throws EventFacadeIsSealedException + * @throws UnknownSubscriberTypeException + */ + public static function shouldStop(): bool { - return $this->value; + $configuration = ConfigurationRegistry::get(); + $collector = self::collector(); + if (($configuration->stopOnDefect() || $configuration->stopOnError()) && $collector->hasErroredTests()) { + return \true; + } + if (($configuration->stopOnDefect() || $configuration->stopOnFailure()) && $collector->hasFailedTests()) { + return \true; + } + if (($configuration->stopOnDefect() || $configuration->stopOnWarning()) && $collector->hasWarnings()) { + return \true; + } + if (($configuration->stopOnDefect() || $configuration->stopOnRisky()) && $collector->hasRiskyTests()) { + return \true; + } + if ($configuration->stopOnDeprecation() && $collector->hasDeprecations()) { + return \true; + } + if ($configuration->stopOnNotice() && $collector->hasNotices()) { + return \true; + } + if ($configuration->stopOnIncomplete() && $collector->hasIncompleteTests()) { + return \true; + } + if ($configuration->stopOnSkipped() && $collector->hasSkippedTests()) { + return \true; + } + return \false; } - public function force() : bool + /** + * @throws EventFacadeIsSealedException + * @throws UnknownSubscriberTypeException + */ + private static function collector(): \PHPUnit\TestRunner\TestResult\Collector { - return $this->force; + if (self::$collector === null) { + $configuration = ConfigurationRegistry::get(); + self::$collector = new \PHPUnit\TestRunner\TestResult\Collector(EventFacade::instance(), $configuration->source()); + } + return self::$collector; } } */ -final class VariableCollection implements Countable, IteratorAggregate +final class Issue { /** - * @var Variable[] + * @psalm-var non-empty-string + */ + private readonly string $file; + /** + * @psalm-var positive-int + */ + private readonly int $line; + /** + * @psalm-var non-empty-string + */ + private readonly string $description; + /** + * @psalm-var non-empty-array */ - private $variables; + private array $triggeringTests; /** - * @param Variable[] $variables + * @psalm-param non-empty-string $file + * @psalm-param positive-int $line + * @psalm-param non-empty-string $description */ - public static function fromArray(array $variables) : self + public static function from(string $file, int $line, string $description, Test $triggeringTest): self { - return new self(...$variables); + return new self($file, $line, $description, $triggeringTest); } - private function __construct(\PHPUnit\TextUI\XmlConfiguration\Variable ...$variables) + /** + * @psalm-param non-empty-string $file + * @psalm-param positive-int $line + * @psalm-param non-empty-string $description + */ + private function __construct(string $file, int $line, string $description, Test $triggeringTest) { - $this->variables = $variables; + $this->file = $file; + $this->line = $line; + $this->description = $description; + $this->triggeringTests = [$triggeringTest->id() => ['test' => $triggeringTest, 'count' => 1]]; + } + public function triggeredBy(Test $test): void + { + if (isset($this->triggeringTests[$test->id()])) { + $this->triggeringTests[$test->id()]['count']++; + return; + } + $this->triggeringTests[$test->id()] = ['test' => $test, 'count' => 1]; } /** - * @return Variable[] + * @psalm-return non-empty-string */ - public function asArray() : array + public function file(): string { - return $this->variables; + return $this->file; + } + /** + * @psalm-return positive-int + */ + public function line(): int + { + return $this->line; } - public function count() : int + /** + * @psalm-return non-empty-string + */ + public function description(): string { - return count($this->variables); + return $this->description; } - public function getIterator() : \PHPUnit\TextUI\XmlConfiguration\VariableCollectionIterator + /** + * @psalm-return non-empty-array + */ + public function triggeringTests(): array { - return new \PHPUnit\TextUI\XmlConfiguration\VariableCollectionIterator($this); + return $this->triggeringTests; } } */ -final class VariableCollectionIterator implements Countable, Iterator +final class PassedTests { + private static ?self $instance = null; /** - * @var Variable[] + * @psalm-var list */ - private $variables; + private array $passedTestClasses = []; /** - * @var int + * @psalm-var array */ - private $position; - public function __construct(\PHPUnit\TextUI\XmlConfiguration\VariableCollection $variables) + private array $passedTestMethods = []; + public static function instance(): self { - $this->variables = $variables->asArray(); + if (self::$instance !== null) { + return self::$instance; + } + self::$instance = new self(); + return self::$instance; } - public function count() : int + /** + * @psalm-param class-string $className + */ + public function testClassPassed(string $className): void { - return iterator_count($this); + $this->passedTestClasses[] = $className; } - public function rewind() : void + public function testMethodPassed(TestMethod $test, mixed $returnValue): void { - $this->position = 0; + $size = (new Groups())->size($test->className(), $test->methodName()); + $this->passedTestMethods[$test->className() . '::' . $test->methodName()] = ['returnValue' => $returnValue, 'size' => $size]; } - public function valid() : bool + public function import(self $other): void { - return $this->position < count($this->variables); + $this->passedTestClasses = array_merge($this->passedTestClasses, $other->passedTestClasses); + $this->passedTestMethods = array_merge($this->passedTestMethods, $other->passedTestMethods); } - public function key() : int + /** + * @psalm-param class-string $className + */ + public function hasTestClassPassed(string $className): bool { - return $this->position; + return in_array($className, $this->passedTestClasses, \true); } - public function current() : \PHPUnit\TextUI\XmlConfiguration\Variable + public function hasTestMethodPassed(string $method): bool { - return $this->variables[$this->position]; + return isset($this->passedTestMethods[$method]); } - public function next() : void + public function isGreaterThan(string $method, TestSize $other): bool { - $this->position++; + if ($other->isUnknown()) { + return \false; + } + assert($other instanceof Known); + $size = $this->passedTestMethods[$method]['size']; + if ($size->isUnknown()) { + return \false; + } + assert($size instanceof Known); + return $size->isGreaterThan($other); + } + public function returnValue(string $method): mixed + { + if (isset($this->passedTestMethods[$method])) { + return $this->passedTestMethods[$method]['returnValue']; + } + return null; } } className = $className; - $this->sourceFile = $sourceFile; - $this->arguments = $arguments; - } - /** - * @psalm-return class-string - */ - public function className() : string + public function notify(BeforeFirstTestMethodErrored $event): void { - return $this->className; - } - public function hasSourceFile() : bool - { - return $this->sourceFile !== ''; - } - public function sourceFile() : string - { - return $this->sourceFile; - } - public function hasArguments() : bool - { - return !empty($this->arguments); - } - public function arguments() : array - { - return $this->arguments; + $this->collector()->beforeTestClassMethodErrored($event); } } */ -final class ExtensionCollection implements IteratorAggregate +final class ExecutionStartedSubscriber extends \PHPUnit\TestRunner\TestResult\Subscriber implements TestRunnerExecutionStartedSubscriber { - /** - * @var Extension[] - */ - private $extensions; - /** - * @param Extension[] $extensions - */ - public static function fromArray(array $extensions) : self - { - return new self(...$extensions); - } - private function __construct(\PHPUnit\TextUI\XmlConfiguration\Extension ...$extensions) - { - $this->extensions = $extensions; - } - /** - * @return Extension[] - */ - public function asArray() : array - { - return $this->extensions; - } - public function getIterator() : \PHPUnit\TextUI\XmlConfiguration\ExtensionCollectionIterator + public function notify(ExecutionStarted $event): void { - return new \PHPUnit\TextUI\XmlConfiguration\ExtensionCollectionIterator($this); + $this->collector()->executionStarted($event); } } */ -final class ExtensionCollectionIterator implements Countable, Iterator +abstract class Subscriber { - /** - * @var Extension[] - */ - private $extensions; - /** - * @var int - */ - private $position; - public function __construct(\PHPUnit\TextUI\XmlConfiguration\ExtensionCollection $extensions) - { - $this->extensions = $extensions->asArray(); - } - public function count() : int - { - return iterator_count($this); - } - public function rewind() : void + private readonly \PHPUnit\TestRunner\TestResult\Collector $collector; + public function __construct(\PHPUnit\TestRunner\TestResult\Collector $collector) { - $this->position = 0; + $this->collector = $collector; } - public function valid() : bool + protected function collector(): \PHPUnit\TestRunner\TestResult\Collector { - return $this->position < count($this->extensions); - } - public function key() : int - { - return $this->position; - } - public function current() : \PHPUnit\TextUI\XmlConfiguration\Extension - { - return $this->extensions[$this->position]; - } - public function next() : void - { - $this->position++; + return $this->collector; } } cacheResult = $cacheResult; - $this->cacheResultFile = $cacheResultFile; - $this->columns = $columns; - $this->colors = $colors; - $this->stderr = $stderr; - $this->noInteraction = $noInteraction; - $this->verbose = $verbose; - $this->reverseDefectList = $reverseDefectList; - $this->convertDeprecationsToExceptions = $convertDeprecationsToExceptions; - $this->convertErrorsToExceptions = $convertErrorsToExceptions; - $this->convertNoticesToExceptions = $convertNoticesToExceptions; - $this->convertWarningsToExceptions = $convertWarningsToExceptions; - $this->forceCoversAnnotation = $forceCoversAnnotation; - $this->bootstrap = $bootstrap; - $this->processIsolation = $processIsolation; - $this->failOnEmptyTestSuite = $failOnEmptyTestSuite; - $this->failOnIncomplete = $failOnIncomplete; - $this->failOnRisky = $failOnRisky; - $this->failOnSkipped = $failOnSkipped; - $this->failOnWarning = $failOnWarning; - $this->stopOnDefect = $stopOnDefect; - $this->stopOnError = $stopOnError; - $this->stopOnFailure = $stopOnFailure; - $this->stopOnWarning = $stopOnWarning; - $this->stopOnIncomplete = $stopOnIncomplete; - $this->stopOnRisky = $stopOnRisky; - $this->stopOnSkipped = $stopOnSkipped; - $this->extensionsDirectory = $extensionsDirectory; - $this->testSuiteLoaderClass = $testSuiteLoaderClass; - $this->testSuiteLoaderFile = $testSuiteLoaderFile; - $this->printerClass = $printerClass; - $this->printerFile = $printerFile; - $this->beStrictAboutChangesToGlobalState = $beStrictAboutChangesToGlobalState; - $this->beStrictAboutOutputDuringTests = $beStrictAboutOutputDuringTests; - $this->beStrictAboutResourceUsageDuringSmallTests = $beStrictAboutResourceUsageDuringSmallTests; - $this->beStrictAboutTestsThatDoNotTestAnything = $beStrictAboutTestsThatDoNotTestAnything; - $this->beStrictAboutTodoAnnotatedTests = $beStrictAboutTodoAnnotatedTests; - $this->beStrictAboutCoversAnnotation = $beStrictAboutCoversAnnotation; - $this->enforceTimeLimit = $enforceTimeLimit; - $this->defaultTimeLimit = $defaultTimeLimit; - $this->timeoutForSmallTests = $timeoutForSmallTests; - $this->timeoutForMediumTests = $timeoutForMediumTests; - $this->timeoutForLargeTests = $timeoutForLargeTests; - $this->defaultTestSuite = $defaultTestSuite; - $this->executionOrder = $executionOrder; - $this->resolveDependencies = $resolveDependencies; - $this->defectsFirst = $defectsFirst; - $this->backupGlobals = $backupGlobals; - $this->backupStaticAttributes = $backupStaticAttributes; - $this->registerMockObjectsFromTestArgumentsRecursively = $registerMockObjectsFromTestArgumentsRecursively; - $this->conflictBetweenPrinterClassAndTestdox = $conflictBetweenPrinterClassAndTestdox; - } - public function cacheResult() : bool - { - return $this->cacheResult; - } - /** - * @psalm-assert-if-true !null $this->cacheResultFile - */ - public function hasCacheResultFile() : bool - { - return $this->cacheResultFile !== null; - } - /** - * @throws Exception - */ - public function cacheResultFile() : string - { - if (!$this->hasCacheResultFile()) { - throw new \PHPUnit\TextUI\XmlConfiguration\Exception('Cache result file is not configured'); - } - return (string) $this->cacheResultFile; - } - public function columns() - { - return $this->columns; - } - public function colors() : string - { - return $this->colors; - } - public function stderr() : bool - { - return $this->stderr; - } - public function noInteraction() : bool - { - return $this->noInteraction; - } - public function verbose() : bool - { - return $this->verbose; - } - public function reverseDefectList() : bool - { - return $this->reverseDefectList; - } - public function convertDeprecationsToExceptions() : bool - { - return $this->convertDeprecationsToExceptions; - } - public function convertErrorsToExceptions() : bool - { - return $this->convertErrorsToExceptions; - } - public function convertNoticesToExceptions() : bool - { - return $this->convertNoticesToExceptions; - } - public function convertWarningsToExceptions() : bool - { - return $this->convertWarningsToExceptions; - } - public function forceCoversAnnotation() : bool - { - return $this->forceCoversAnnotation; - } - /** - * @psalm-assert-if-true !null $this->bootstrap - */ - public function hasBootstrap() : bool - { - return $this->bootstrap !== null; - } - /** - * @throws Exception - */ - public function bootstrap() : string - { - if (!$this->hasBootstrap()) { - throw new \PHPUnit\TextUI\XmlConfiguration\Exception('Bootstrap script is not configured'); - } - return (string) $this->bootstrap; - } - public function processIsolation() : bool - { - return $this->processIsolation; - } - public function failOnEmptyTestSuite() : bool - { - return $this->failOnEmptyTestSuite; - } - public function failOnIncomplete() : bool - { - return $this->failOnIncomplete; - } - public function failOnRisky() : bool - { - return $this->failOnRisky; - } - public function failOnSkipped() : bool - { - return $this->failOnSkipped; - } - public function failOnWarning() : bool - { - return $this->failOnWarning; - } - public function stopOnDefect() : bool - { - return $this->stopOnDefect; - } - public function stopOnError() : bool - { - return $this->stopOnError; - } - public function stopOnFailure() : bool - { - return $this->stopOnFailure; - } - public function stopOnWarning() : bool - { - return $this->stopOnWarning; - } - public function stopOnIncomplete() : bool - { - return $this->stopOnIncomplete; - } - public function stopOnRisky() : bool - { - return $this->stopOnRisky; - } - public function stopOnSkipped() : bool - { - return $this->stopOnSkipped; - } - /** - * @psalm-assert-if-true !null $this->extensionsDirectory - */ - public function hasExtensionsDirectory() : bool - { - return $this->extensionsDirectory !== null; - } - /** - * @throws Exception - */ - public function extensionsDirectory() : string - { - if (!$this->hasExtensionsDirectory()) { - throw new \PHPUnit\TextUI\XmlConfiguration\Exception('Extensions directory is not configured'); - } - return (string) $this->extensionsDirectory; - } - /** - * @psalm-assert-if-true !null $this->testSuiteLoaderClass - * - * @deprecated see https://github.com/sebastianbergmann/phpunit/issues/4039 - */ - public function hasTestSuiteLoaderClass() : bool - { - return $this->testSuiteLoaderClass !== null; - } - /** - * @throws Exception - * - * @deprecated see https://github.com/sebastianbergmann/phpunit/issues/4039 - */ - public function testSuiteLoaderClass() : string - { - if (!$this->hasTestSuiteLoaderClass()) { - throw new \PHPUnit\TextUI\XmlConfiguration\Exception('TestSuiteLoader class is not configured'); - } - return (string) $this->testSuiteLoaderClass; - } - /** - * @psalm-assert-if-true !null $this->testSuiteLoaderFile - * - * @deprecated see https://github.com/sebastianbergmann/phpunit/issues/4039 - */ - public function hasTestSuiteLoaderFile() : bool - { - return $this->testSuiteLoaderFile !== null; - } - /** - * @throws Exception - * - * @deprecated see https://github.com/sebastianbergmann/phpunit/issues/4039 - */ - public function testSuiteLoaderFile() : string - { - if (!$this->hasTestSuiteLoaderFile()) { - throw new \PHPUnit\TextUI\XmlConfiguration\Exception('TestSuiteLoader sourcecode file is not configured'); - } - return (string) $this->testSuiteLoaderFile; - } - /** - * @psalm-assert-if-true !null $this->printerClass - */ - public function hasPrinterClass() : bool - { - return $this->printerClass !== null; - } - /** - * @throws Exception - */ - public function printerClass() : string - { - if (!$this->hasPrinterClass()) { - throw new \PHPUnit\TextUI\XmlConfiguration\Exception('ResultPrinter class is not configured'); - } - return (string) $this->printerClass; - } - /** - * @psalm-assert-if-true !null $this->printerFile - */ - public function hasPrinterFile() : bool - { - return $this->printerFile !== null; - } - /** - * @throws Exception - */ - public function printerFile() : string - { - if (!$this->hasPrinterFile()) { - throw new \PHPUnit\TextUI\XmlConfiguration\Exception('ResultPrinter sourcecode file is not configured'); - } - return (string) $this->printerFile; - } - public function beStrictAboutChangesToGlobalState() : bool - { - return $this->beStrictAboutChangesToGlobalState; - } - public function beStrictAboutOutputDuringTests() : bool - { - return $this->beStrictAboutOutputDuringTests; - } - public function beStrictAboutResourceUsageDuringSmallTests() : bool - { - return $this->beStrictAboutResourceUsageDuringSmallTests; - } - public function beStrictAboutTestsThatDoNotTestAnything() : bool - { - return $this->beStrictAboutTestsThatDoNotTestAnything; - } - public function beStrictAboutTodoAnnotatedTests() : bool - { - return $this->beStrictAboutTodoAnnotatedTests; - } - public function beStrictAboutCoversAnnotation() : bool - { - return $this->beStrictAboutCoversAnnotation; - } - public function enforceTimeLimit() : bool - { - return $this->enforceTimeLimit; - } - public function defaultTimeLimit() : int - { - return $this->defaultTimeLimit; - } - public function timeoutForSmallTests() : int - { - return $this->timeoutForSmallTests; - } - public function timeoutForMediumTests() : int - { - return $this->timeoutForMediumTests; - } - public function timeoutForLargeTests() : int - { - return $this->timeoutForLargeTests; - } - /** - * @psalm-assert-if-true !null $this->defaultTestSuite - */ - public function hasDefaultTestSuite() : bool - { - return $this->defaultTestSuite !== null; - } - /** - * @throws Exception - */ - public function defaultTestSuite() : string - { - if (!$this->hasDefaultTestSuite()) { - throw new \PHPUnit\TextUI\XmlConfiguration\Exception('Default test suite is not configured'); - } - return (string) $this->defaultTestSuite; - } - public function executionOrder() : int - { - return $this->executionOrder; - } - public function resolveDependencies() : bool - { - return $this->resolveDependencies; - } - public function defectsFirst() : bool - { - return $this->defectsFirst; - } - public function backupGlobals() : bool - { - return $this->backupGlobals; - } - public function backupStaticAttributes() : bool - { - return $this->backupStaticAttributes; - } - public function registerMockObjectsFromTestArgumentsRecursively() : bool - { - return $this->registerMockObjectsFromTestArgumentsRecursively; - } - public function conflictBetweenPrinterClassAndTestdox() : bool + public function notify(ConsideredRisky $event): void { - return $this->conflictBetweenPrinterClassAndTestdox; + $this->collector()->testConsideredRisky($event); } } path = $path; - $this->prefix = $prefix; - $this->suffix = $suffix; - $this->phpVersion = $phpVersion; - $this->phpVersionOperator = $phpVersionOperator; - } - public function path() : string - { - return $this->path; - } - public function prefix() : string - { - return $this->prefix; - } - public function suffix() : string - { - return $this->suffix; - } - public function phpVersion() : string - { - return $this->phpVersion; - } - public function phpVersionOperator() : VersionComparisonOperator + public function notify(Errored $event): void { - return $this->phpVersionOperator; + $this->collector()->testErrored($event); } } */ -final class TestDirectoryCollection implements Countable, IteratorAggregate +final class TestFailedSubscriber extends \PHPUnit\TestRunner\TestResult\Subscriber implements FailedSubscriber { - /** - * @var TestDirectory[] - */ - private $directories; - /** - * @param TestDirectory[] $directories - */ - public static function fromArray(array $directories) : self - { - return new self(...$directories); - } - private function __construct(\PHPUnit\TextUI\XmlConfiguration\TestDirectory ...$directories) - { - $this->directories = $directories; - } - /** - * @return TestDirectory[] - */ - public function asArray() : array - { - return $this->directories; - } - public function count() : int - { - return count($this->directories); - } - public function getIterator() : \PHPUnit\TextUI\XmlConfiguration\TestDirectoryCollectionIterator + public function notify(Failed $event): void { - return new \PHPUnit\TextUI\XmlConfiguration\TestDirectoryCollectionIterator($this); - } - public function isEmpty() : bool - { - return $this->count() === 0; + $this->collector()->testFailed($event); } } */ -final class TestDirectoryCollectionIterator implements Countable, Iterator +final class TestFinishedSubscriber extends \PHPUnit\TestRunner\TestResult\Subscriber implements FinishedSubscriber { - /** - * @var TestDirectory[] - */ - private $directories; - /** - * @var int - */ - private $position; - public function __construct(\PHPUnit\TextUI\XmlConfiguration\TestDirectoryCollection $directories) - { - $this->directories = $directories->asArray(); - } - public function count() : int + public function notify(Finished $event): void { - return iterator_count($this); - } - public function rewind() : void - { - $this->position = 0; - } - public function valid() : bool - { - return $this->position < count($this->directories); - } - public function key() : int - { - return $this->position; - } - public function current() : \PHPUnit\TextUI\XmlConfiguration\TestDirectory - { - return $this->directories[$this->position]; - } - public function next() : void - { - $this->position++; + $this->collector()->testFinished($event); } } path = $path; - $this->phpVersion = $phpVersion; - $this->phpVersionOperator = $phpVersionOperator; - } - public function path() : string - { - return $this->path; - } - public function phpVersion() : string - { - return $this->phpVersion; - } - public function phpVersionOperator() : VersionComparisonOperator + public function notify(MarkedIncomplete $event): void { - return $this->phpVersionOperator; + $this->collector()->testMarkedIncomplete($event); } } */ -final class TestFileCollection implements Countable, IteratorAggregate +final class TestPreparedSubscriber extends \PHPUnit\TestRunner\TestResult\Subscriber implements PreparedSubscriber { - /** - * @var TestFile[] - */ - private $files; - /** - * @param TestFile[] $files - */ - public static function fromArray(array $files) : self - { - return new self(...$files); - } - private function __construct(\PHPUnit\TextUI\XmlConfiguration\TestFile ...$files) - { - $this->files = $files; - } - /** - * @return TestFile[] - */ - public function asArray() : array - { - return $this->files; - } - public function count() : int - { - return count($this->files); - } - public function getIterator() : \PHPUnit\TextUI\XmlConfiguration\TestFileCollectionIterator - { - return new \PHPUnit\TextUI\XmlConfiguration\TestFileCollectionIterator($this); - } - public function isEmpty() : bool + public function notify(Prepared $event): void { - return $this->count() === 0; + $this->collector()->testPrepared(); } } */ -final class TestFileCollectionIterator implements Countable, Iterator +final class TestRunnerTriggeredDeprecationSubscriber extends \PHPUnit\TestRunner\TestResult\Subscriber implements DeprecationTriggeredSubscriber { - /** - * @var TestFile[] - */ - private $files; - /** - * @var int - */ - private $position; - public function __construct(\PHPUnit\TextUI\XmlConfiguration\TestFileCollection $files) - { - $this->files = $files->asArray(); - } - public function count() : int - { - return iterator_count($this); - } - public function rewind() : void - { - $this->position = 0; - } - public function valid() : bool - { - return $this->position < count($this->files); - } - public function key() : int - { - return $this->position; - } - public function current() : \PHPUnit\TextUI\XmlConfiguration\TestFile - { - return $this->files[$this->position]; - } - public function next() : void + public function notify(DeprecationTriggered $event): void { - $this->position++; + $this->collector()->testRunnerTriggeredDeprecation($event); } } name = $name; - $this->directories = $directories; - $this->files = $files; - $this->exclude = $exclude; - } - public function name() : string - { - return $this->name; - } - public function directories() : \PHPUnit\TextUI\XmlConfiguration\TestDirectoryCollection + public function notify(WarningTriggered $event): void { - return $this->directories; - } - public function files() : \PHPUnit\TextUI\XmlConfiguration\TestFileCollection - { - return $this->files; - } - public function exclude() : \PHPUnit\TextUI\XmlConfiguration\FileCollection - { - return $this->exclude; + $this->collector()->testRunnerTriggeredWarning($event); } } */ -final class TestSuiteCollection implements Countable, IteratorAggregate +final class TestSkippedSubscriber extends \PHPUnit\TestRunner\TestResult\Subscriber implements SkippedSubscriber { - /** - * @var TestSuite[] - */ - private $testSuites; - /** - * @param TestSuite[] $testSuites - */ - public static function fromArray(array $testSuites) : self - { - return new self(...$testSuites); - } - private function __construct(\PHPUnit\TextUI\XmlConfiguration\TestSuite ...$testSuites) - { - $this->testSuites = $testSuites; - } - /** - * @return TestSuite[] - */ - public function asArray() : array - { - return $this->testSuites; - } - public function count() : int - { - return count($this->testSuites); - } - public function getIterator() : \PHPUnit\TextUI\XmlConfiguration\TestSuiteCollectionIterator - { - return new \PHPUnit\TextUI\XmlConfiguration\TestSuiteCollectionIterator($this); - } - public function isEmpty() : bool + public function notify(Skipped $event): void { - return $this->count() === 0; + $this->collector()->testSkipped($event); } } */ -final class TestSuiteCollectionIterator implements Countable, Iterator +final class TestSuiteFinishedSubscriber extends \PHPUnit\TestRunner\TestResult\Subscriber implements FinishedSubscriber { - /** - * @var TestSuite[] - */ - private $testSuites; - /** - * @var int - */ - private $position; - public function __construct(\PHPUnit\TextUI\XmlConfiguration\TestSuiteCollection $testSuites) - { - $this->testSuites = $testSuites->asArray(); - } - public function count() : int - { - return iterator_count($this); - } - public function rewind() : void - { - $this->position = 0; - } - public function valid() : bool - { - return $this->position < count($this->testSuites); - } - public function key() : int - { - return $this->position; - } - public function current() : \PHPUnit\TextUI\XmlConfiguration\TestSuite - { - return $this->testSuites[$this->position]; - } - public function next() : void + public function notify(Finished $event): void { - $this->position++; + $this->collector()->testSuiteFinished($event); } } PHP(?:Unit)?)\\s+(?P[<>=!]{0,2})\\s*(?P[\\d\\.-]+(dev|(RC|alpha|beta)[\\d\\.])?)[ \\t]*\\r?$/m'; - private const REGEX_REQUIRES_VERSION_CONSTRAINT = '/@requires\\s+(?PPHP(?:Unit)?)\\s+(?P[\\d\\t \\-.|~^]+)[ \\t]*\\r?$/m'; - private const REGEX_REQUIRES_OS = '/@requires\\s+(?POS(?:FAMILY)?)\\s+(?P.+?)[ \\t]*\\r?$/m'; - private const REGEX_REQUIRES_SETTING = '/@requires\\s+(?Psetting)\\s+(?P([^ ]+?))\\s*(?P[\\w\\.-]+[\\w\\.]?)?[ \\t]*\\r?$/m'; - private const REGEX_REQUIRES = '/@requires\\s+(?Pfunction|extension)\\s+(?P([^\\s<>=!]+))\\s*(?P[<>=!]{0,2})\\s*(?P[\\d\\.-]+[\\d\\.]?)?[ \\t]*\\r?$/m'; - private const REGEX_TEST_WITH = '/@testWith\\s+/'; - /** @var string */ - private $docComment; - /** @var bool */ - private $isMethod; - /** @var array> pre-parsed annotations indexed by name and occurrence index */ - private $symbolAnnotations; - /** - * @var null|array - * - * @psalm-var null|(array{ - * __OFFSET: array&array{__FILE: string}, - * setting?: array, - * extension_versions?: array - * }&array< - * string, - * string|array{version: string, operator: string}|array{constraint: string}|array - * >) - */ - 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> $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&array{__FILE: string}, - * setting?: array, - * extension_versions?: array - * }&array< - * string, - * string|array{version: string, operator: string}|array{constraint: string}|array - * > - * - * @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)]; - // Trim docblock markers, split it into lines and rewind offset to start of docblock - $lines = preg_replace(['#^/\\*{2}#', '#\\*/$#'], '', 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 (\PHPUnitPHAR\PharIo\Version\Exception $e) { - 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])); - } - /** - * 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 InvalidDataSetException(sprintf('Data set %s is invalid.', is_int($key) ? '#' . $key : '"' . $key . '"')); - } - } - return $data; - } - /** - * @psalm-return array - */ - 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[A-Za-z_-]+)(?:[ \\t]+(?P.*?))?[ \\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); - } - public function isToBeExecutedAsPreCondition() : bool - { - return 1 === preg_match('/@preCondition\\b/', $this->docComment); - } - public function isToBeExecutedAsPostCondition() : bool - { - return 1 === preg_match('/@postCondition\\b/', $this->docComment); - } - 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); - // @codeCoverageIgnoreStart - } catch (ReflectionException $e) { - throw new Exception($e->getMessage(), $e->getCode(), $e); - // @codeCoverageIgnoreEnd - } - 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> */ - 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[A-Za-z_-]+)(?:[ \\t]+(?P.*?))?[ \\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 + public function notify(Skipped $event): void { - $annotations = []; - if ($reflector instanceof ReflectionClass) { - $annotations = array_merge($annotations, ...array_map(static function (ReflectionClass $trait) : array { - return self::parseDocBlock((string) $trait->getDocComment()); - }, array_values($reflector->getTraits()))); - } - return array_merge($annotations, self::parseDocBlock((string) $reflector->getDocComment())); + $this->collector()->testSuiteSkipped($event); } } indexed by class name */ - private $classDocBlocks = []; - /** @var array> 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) : \PHPUnit\Util\Annotation\DocBlock - { - if (array_key_exists($class, $this->classDocBlocks)) { - return $this->classDocBlocks[$class]; - } - try { - $reflection = new ReflectionClass($class); - // @codeCoverageIgnoreStart - } catch (ReflectionException $e) { - throw new Exception($e->getMessage(), $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd - return $this->classDocBlocks[$class] = \PHPUnit\Util\Annotation\DocBlock::ofClass($reflection); - } - /** - * @throws Exception - * - * @psalm-param class-string $classInHierarchy - */ - public function forMethod(string $classInHierarchy, string $method) : \PHPUnit\Util\Annotation\DocBlock + public function notify(Started $event): void { - if (isset($this->methodDocBlocks[$classInHierarchy][$method])) { - return $this->methodDocBlocks[$classInHierarchy][$method]; - } - try { - $reflection = new ReflectionMethod($classInHierarchy, $method); - // @codeCoverageIgnoreStart - } catch (ReflectionException $e) { - throw new Exception($e->getMessage(), $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd - return $this->methodDocBlocks[$classInHierarchy][$method] = \PHPUnit\Util\Annotation\DocBlock::ofMethod($reflection, $classInHierarchy); + $this->collector()->testSuiteStarted($event); } } getExcludedDirectories(); - } - /** - * @throws Exception - */ - public function isBlacklisted(string $file) : bool + public function notify(DeprecationTriggered $event): void { - return (new \PHPUnit\Util\ExcludeList())->isExcluded($file); + $this->collector()->testTriggeredDeprecation($event); } } collector()->testTriggeredError($event); } } - */ - private const WHITESPACE_MAP = [' ' => '·', "\t" => '⇥']; - /** - * @var array - */ - private const WHITESPACE_EOL_MAP = [' ' => '·', "\t" => '⇥', "\n" => '↵', "\r" => '⟵']; - /** - * @var array - */ - 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 "\x1b[2m{$buffer}\x1b[22m"; - } - public static function visualizeWhitespace(string $buffer, bool $visualizeEOL = \false) : string + public function notify(NoticeTriggered $event): void { - $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 = ["/\x1b\\[22m\x1b\\[2m/" => '', "/\x1b\\[([^m]*)m\x1b\\[([1-9][0-9;]*)m/" => "\x1b[\$1;\$2m", "/(\x1b\\[[^m]*m)+(\x1b\\[0m)/" => '$2']; - return preg_replace(array_keys($patterns), array_values($patterns), $buffer); + $this->collector()->testTriggeredNotice($event); } } 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 + public function notify(PhpDeprecationTriggered $event): void { - if (!$this->registered) { - return; - } - restore_error_handler(); + $this->collector()->testTriggeredPhpDeprecation($event); } } collector()->testTriggeredPhpNotice($event); + } } - */ - private const EXCLUDED_CLASS_NAMES = [ - // composer - ClassLoader::class => 1, - // doctrine/instantiator - Instantiator::class => 1, - // myclabs/deepcopy - DeepCopy::class => 1, - // nikic/php-parser - Parser::class => 1, - // phar-io/manifest - Manifest::class => 1, - // phar-io/version - PharIoVersion::class => 1, - // phpdocumentor/type-resolver - \PHPUnit\Util\Type::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 - Template::class => 1, - // phpunit/php-timer - Timer::class => 1, - // sebastian/cli-parser - CliParser::class => 1, - // sebastian/code-unit - CodeUnit::class => 1, - // sebastian/code-unit-reverse-lookup - Wizard::class => 1, - // sebastian/comparator - Comparator::class => 1, - // sebastian/complexity - Calculator::class => 1, - // sebastian/diff - Diff::class => 1, - // sebastian/environment - Runtime::class => 1, - // sebastian/exporter - Exporter::class => 1, - // sebastian/global-state - Snapshot::class => 1, - // sebastian/lines-of-code - Counter::class => 1, - // sebastian/object-enumerator - Enumerator::class => 1, - // sebastian/object-reflector - ObjectReflector::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, - ]; - /** - * @var string[] - */ - private static $directories = []; - /** - * @var bool - */ - private static $initialized = \false; - public static function addDirectory(string $directory) : void - { - if (!is_dir($directory)) { - throw new \PHPUnit\Util\Exception(sprintf('"%s" is not a directory', $directory)); - } - self::$directories[] = realpath($directory); - } - /** - * @throws Exception - * - * @return string[] - */ - public function getExcludedDirectories() : array - { - $this->initialize(); - return self::$directories; - } - /** - * @throws Exception - */ - public function isExcluded(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 + public function notify(PhpWarningTriggered $event): void { - if (self::$initialized) { - return; - } - foreach (self::EXCLUDED_CLASS_NAMES as $className => $parent) { - if (!class_exists($className)) { - continue; - } - $directory = (new ReflectionClass($className))->getFileName(); - 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'; - } - self::$initialized = \true; + $this->collector()->testTriggeredPhpWarning($event); } } collector()->testTriggeredPhpunitDeprecation($event); } } 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 + public function notify(PhpunitErrorTriggered $event): void { - return !(!is_dir($directory) && !@mkdir($directory, 0777, \true) && !is_dir($directory)); + $this->collector()->testTriggeredPhpunitError($event); } } 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]); - } - $prefix = defined('__PHPUNIT_PHAR_ROOT__') ? __PHPUNIT_PHAR_ROOT__ : \false; - $excludeList = new \PHPUnit\Util\ExcludeList(); - foreach ($eTrace as $frame) { - if (self::shouldPrintFrame($frame, $prefix, $excludeList)) { - $filteredStacktrace .= sprintf("%s:%s\n", $frame['file'], $frame['line'] ?? '?'); - } - } - return $filteredStacktrace; - } - private static function shouldPrintFrame(array $frame, $prefix, \PHPUnit\Util\ExcludeList $excludeList) : bool - { - if (!isset($frame['file'])) { - return \false; - } - $file = $frame['file']; - $fileIsNotPrefixed = $prefix === \false || strpos($file, $prefix) !== 0; - // @see https://github.com/sebastianbergmann/phpunit/issues/4033 - if (isset($GLOBALS['_SERVER']['SCRIPT_NAME'])) { - $script = realpath($GLOBALS['_SERVER']['SCRIPT_NAME']); - } else { - $script = ''; - } - return is_file($file) && self::fileIsExcluded($file, $excludeList) && $fileIsNotPrefixed && $file !== $script; - } - private static function fileIsExcluded(string $file, \PHPUnit\Util\ExcludeList $excludeList) : bool - { - return (empty($GLOBALS['__PHPUNIT_ISOLATION_EXCLUDE_LIST']) || !in_array($file, $GLOBALS['__PHPUNIT_ISOLATION_EXCLUDE_LIST'], \true)) && !$excludeList->isExcluded($file); - } - private static function frameExists(array $trace, string $file, int $line) : bool + public function notify(PhpunitWarningTriggered $event): void { - foreach ($trace as $frame) { - if (isset($frame['file'], $frame['line']) && $frame['file'] === $file && $frame['line'] === $line) { - return \true; - } - } - return \false; + $this->collector()->testTriggeredPhpunitWarning($event); } } > - */ - private const DEPRECATED_INI_SETTINGS = ['7.3' => ['iconv.input_encoding' => \true, 'iconv.output_encoding' => \true, 'iconv.internal_encoding' => \true, 'mbstring.func_overload' => \true, 'mbstring.http_input' => \true, 'mbstring.http_output' => \true, 'mbstring.internal_encoding' => \true, 'string.strip_tags' => \true], '7.4' => ['iconv.input_encoding' => \true, 'iconv.output_encoding' => \true, 'iconv.internal_encoding' => \true, 'mbstring.func_overload' => \true, 'mbstring.http_input' => \true, 'mbstring.http_output' => \true, 'mbstring.internal_encoding' => \true, 'pdo_odbc.db2_instance_name' => \true, 'string.strip_tags' => \true], '8.0' => ['iconv.input_encoding' => \true, 'iconv.output_encoding' => \true, 'iconv.internal_encoding' => \true, 'mbstring.http_input' => \true, 'mbstring.http_output' => \true, 'mbstring.internal_encoding' => \true], '8.1' => ['auto_detect_line_endings' => \true, 'filter.default' => \true, 'iconv.input_encoding' => \true, 'iconv.output_encoding' => \true, 'iconv.internal_encoding' => \true, 'mbstring.http_input' => \true, 'mbstring.http_output' => \true, 'mbstring.internal_encoding' => \true, 'oci8.old_oci_close_semantics' => \true], '8.2' => ['auto_detect_line_endings' => \true, 'filter.default' => \true, 'iconv.input_encoding' => \true, 'iconv.output_encoding' => \true, 'iconv.internal_encoding' => \true, 'mbstring.http_input' => \true, 'mbstring.http_output' => \true, 'mbstring.internal_encoding' => \true, 'oci8.old_oci_close_semantics' => \true], '8.3' => ['auto_detect_line_endings' => \true, 'filter.default' => \true, 'iconv.input_encoding' => \true, 'iconv.output_encoding' => \true, 'iconv.internal_encoding' => \true, 'mbstring.http_input' => \true, 'mbstring.http_output' => \true, 'mbstring.internal_encoding' => \true, 'oci8.old_oci_close_semantics' => \true]]; - /** - * @throws Exception - */ - public static function getIncludedFilesAsString() : string - { - return self::processIncludedFilesAsString(get_included_files()); - } - /** - * @param string[] $files - * - * @throws Exception - */ - public static function processIncludedFilesAsString(array $files) : string - { - $excludeList = new \PHPUnit\Util\ExcludeList(); - $prefix = \false; - $result = ''; - if (defined('__PHPUNIT_PHAR__')) { - $prefix = 'phar://' . __PHPUNIT_PHAR__ . '/'; - } - // Do not process bootstrap script - array_shift($files); - // If bootstrap script was a Composer bin proxy, skip the second entry as well - if (substr(strtr($files[0], '\\', '/'), -24) === '/phpunit/phpunit/phpunit') { - array_shift($files); - } - foreach (array_reverse($files) as $file) { - if (!empty($GLOBALS['__PHPUNIT_ISOLATION_EXCLUDE_LIST']) && in_array($file, $GLOBALS['__PHPUNIT_ISOLATION_EXCLUDE_LIST'], \true)) { - 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 (!$excludeList->isExcluded($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) { - if (self::isIniSettingDeprecated($key)) { - continue; - } - $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])); - } - } - } - $excludeList = self::SUPER_GLOBAL_ARRAYS; - $excludeList[] = 'GLOBALS'; - foreach (array_keys($GLOBALS) as $key) { - if (!$GLOBALS[$key] instanceof Closure && !in_array($key, $excludeList, \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; - } - private static function isIniSettingDeprecated(string $iniSetting) : bool + public function notify(WarningTriggered $event): void { - return isset(self::DEPRECATED_INI_SETTINGS[PHP_MAJOR_VERSION . '.' . PHP_MINOR_VERSION][$iniSetting]); + $this->collector()->testTriggeredWarning($event); } } - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Util; - -use const JSON_PRETTY_PRINT; -use const JSON_UNESCAPED_SLASHES; -use const JSON_UNESCAPED_UNICODE; -use function count; -use function is_array; -use function is_object; -use function json_decode; -use function json_encode; -use function json_last_error; -use function ksort; -use PHPUnit\Framework\Exception; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class Json +final class TestResult { + private readonly int $numberOfTests; + private readonly int $numberOfTestsRun; + private readonly int $numberOfAssertions; /** - * Prettify json string. - * - * @throws Exception + * @psalm-var list */ - public static function prettify(string $json) : string - { - $decodedJson = json_decode($json, \false); - if (json_last_error()) { - throw new Exception('Cannot prettify invalid json'); - } - return json_encode($decodedJson, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); - } + private readonly array $testErroredEvents; /** - * 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'. + * @psalm-var list */ - 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]; - } + private readonly array $testFailedEvents; /** - * 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. + * @psalm-var list */ - 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); - } - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Util\Log; - -use function class_exists; -use function get_class; -use function method_exists; -use function sprintf; -use function str_replace; -use function trim; -use DOMDocument; -use DOMElement; -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; -use ReflectionClass; -use ReflectionException; -use Throwable; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class JUnit extends Printer implements TestListener -{ + private readonly array $testMarkedIncompleteEvents; /** - * @var DOMDocument + * @psalm-var list */ - private $document; + private readonly array $testSuiteSkippedEvents; /** - * @var DOMElement + * @psalm-var list */ - private $root; + private readonly array $testSkippedEvents; /** - * @var bool + * @psalm-var array> */ - private $reportRiskyTests = \false; + private readonly array $testConsideredRiskyEvents; /** - * @var DOMElement[] + * @psalm-var array> */ - private $testSuites = []; + private readonly array $testTriggeredPhpunitDeprecationEvents; /** - * @var int[] + * @psalm-var array> */ - private $testSuiteTests = [0]; + private readonly array $testTriggeredPhpunitErrorEvents; /** - * @var int[] + * @psalm-var array> */ - private $testSuiteAssertions = [0]; + private readonly array $testTriggeredPhpunitWarningEvents; /** - * @var int[] + * @psalm-var list */ - private $testSuiteErrors = [0]; + private readonly array $testRunnerTriggeredDeprecationEvents; /** - * @var int[] + * @psalm-var list */ - private $testSuiteWarnings = [0]; + private readonly array $testRunnerTriggeredWarningEvents; /** - * @var int[] + * @psalm-var list */ - private $testSuiteFailures = [0]; + private readonly array $errors; /** - * @var int[] + * @psalm-var list */ - private $testSuiteSkipped = [0]; + private readonly array $deprecations; /** - * @var int[] + * @psalm-var list */ - private $testSuiteTimes = [0]; + private readonly array $notices; /** - * @var int + * @psalm-var list */ - private $testSuiteLevel = 0; + private readonly array $warnings; /** - * @var DOMElement + * @psalm-var list */ - private $currentTestCase; + private readonly array $phpDeprecations; /** - * @param null|mixed $out + * @psalm-var list */ - 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; - } + private readonly array $phpNotices; /** - * Flush buffer and close output. + * @psalm-var list */ - public function flush() : void - { - $this->write($this->getXML()); - parent::flush(); - } + private readonly array $phpWarnings; /** - * An error occurred. + * @psalm-var non-negative-int */ - public function addError(Test $test, Throwable $t, float $time) : void - { - $this->doAddFault($test, $t, 'error'); - $this->testSuiteErrors[$this->testSuiteLevel]++; - } + private readonly int $numberOfIssuesIgnoredByBaseline; /** - * A warning occurred. + * @psalm-param list $testErroredEvents + * @psalm-param list $testFailedEvents + * @psalm-param array> $testConsideredRiskyEvents + * @psalm-param list $testSuiteSkippedEvents + * @psalm-param list $testSkippedEvents + * @psalm-param list $testMarkedIncompleteEvents + * @psalm-param array> $testTriggeredPhpunitDeprecationEvents + * @psalm-param array> $testTriggeredPhpunitErrorEvents + * @psalm-param array> $testTriggeredPhpunitWarningEvents + * @psalm-param list $testRunnerTriggeredDeprecationEvents + * @psalm-param list $testRunnerTriggeredWarningEvents + * @psalm-param list $errors + * @psalm-param list $deprecations + * @psalm-param list $notices + * @psalm-param list $warnings + * @psalm-param list $phpDeprecations + * @psalm-param list $phpNotices + * @psalm-param list $phpWarnings + * @psalm-param non-negative-int $numberOfIssuesIgnoredByBaseline */ - public function addWarning(Test $test, Warning $e, float $time) : void + public function __construct(int $numberOfTests, int $numberOfTestsRun, int $numberOfAssertions, array $testErroredEvents, array $testFailedEvents, array $testConsideredRiskyEvents, array $testSuiteSkippedEvents, array $testSkippedEvents, array $testMarkedIncompleteEvents, array $testTriggeredPhpunitDeprecationEvents, array $testTriggeredPhpunitErrorEvents, array $testTriggeredPhpunitWarningEvents, array $testRunnerTriggeredDeprecationEvents, array $testRunnerTriggeredWarningEvents, array $errors, array $deprecations, array $notices, array $warnings, array $phpDeprecations, array $phpNotices, array $phpWarnings, int $numberOfIssuesIgnoredByBaseline) + { + $this->numberOfTests = $numberOfTests; + $this->numberOfTestsRun = $numberOfTestsRun; + $this->numberOfAssertions = $numberOfAssertions; + $this->testErroredEvents = $testErroredEvents; + $this->testFailedEvents = $testFailedEvents; + $this->testConsideredRiskyEvents = $testConsideredRiskyEvents; + $this->testSuiteSkippedEvents = $testSuiteSkippedEvents; + $this->testSkippedEvents = $testSkippedEvents; + $this->testMarkedIncompleteEvents = $testMarkedIncompleteEvents; + $this->testTriggeredPhpunitDeprecationEvents = $testTriggeredPhpunitDeprecationEvents; + $this->testTriggeredPhpunitErrorEvents = $testTriggeredPhpunitErrorEvents; + $this->testTriggeredPhpunitWarningEvents = $testTriggeredPhpunitWarningEvents; + $this->testRunnerTriggeredDeprecationEvents = $testRunnerTriggeredDeprecationEvents; + $this->testRunnerTriggeredWarningEvents = $testRunnerTriggeredWarningEvents; + $this->errors = $errors; + $this->deprecations = $deprecations; + $this->notices = $notices; + $this->warnings = $warnings; + $this->phpDeprecations = $phpDeprecations; + $this->phpNotices = $phpNotices; + $this->phpWarnings = $phpWarnings; + $this->numberOfIssuesIgnoredByBaseline = $numberOfIssuesIgnoredByBaseline; + } + public function numberOfTestsRun(): int + { + return $this->numberOfTestsRun; + } + public function numberOfAssertions(): int { - $this->doAddFault($test, $e, 'warning'); - $this->testSuiteWarnings[$this->testSuiteLevel]++; + return $this->numberOfAssertions; } /** - * A failure occurred. + * @psalm-return list */ - public function addFailure(Test $test, AssertionFailedError $e, float $time) : void + public function testErroredEvents(): array { - $this->doAddFault($test, $e, 'failure'); - $this->testSuiteFailures[$this->testSuiteLevel]++; + return $this->testErroredEvents; + } + public function numberOfTestErroredEvents(): int + { + return count($this->testErroredEvents); + } + public function hasTestErroredEvents(): bool + { + return $this->numberOfTestErroredEvents() > 0; } /** - * Incomplete test. + * @psalm-return list */ - public function addIncompleteTest(Test $test, Throwable $t, float $time) : void + public function testFailedEvents(): array + { + return $this->testFailedEvents; + } + public function numberOfTestFailedEvents(): int + { + return count($this->testFailedEvents); + } + public function hasTestFailedEvents(): bool { - $this->doAddSkipped(); + return $this->numberOfTestFailedEvents() > 0; } /** - * Risky test. + * @psalm-return array> */ - public function addRiskyTest(Test $test, Throwable $t, float $time) : void + public function testConsideredRiskyEvents(): array { - if (!$this->reportRiskyTests) { - return; - } - $this->doAddFault($test, $t, 'error'); - $this->testSuiteErrors[$this->testSuiteLevel]++; + return $this->testConsideredRiskyEvents; + } + public function numberOfTestsWithTestConsideredRiskyEvents(): int + { + return count($this->testConsideredRiskyEvents); + } + public function hasTestConsideredRiskyEvents(): bool + { + return $this->numberOfTestsWithTestConsideredRiskyEvents() > 0; } /** - * Skipped test. + * @psalm-return list */ - public function addSkippedTest(Test $test, Throwable $t, float $time) : void + public function testSuiteSkippedEvents(): array + { + return $this->testSuiteSkippedEvents; + } + public function numberOfTestSuiteSkippedEvents(): int + { + return count($this->testSuiteSkippedEvents); + } + public function hasTestSuiteSkippedEvents(): bool { - $this->doAddSkipped(); + return $this->numberOfTestSuiteSkippedEvents() > 0; } /** - * A testsuite started. + * @psalm-return list */ - public function startTestSuite(TestSuite $suite) : void + public function testSkippedEvents(): array { - $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; + return $this->testSkippedEvents; + } + public function numberOfTestSkippedEvents(): int + { + return count($this->testSkippedEvents); + } + public function hasTestSkippedEvents(): bool + { + return $this->numberOfTestSkippedEvents() > 0; } /** - * A testsuite ended. + * @psalm-return list */ - public function endTestSuite(TestSuite $suite) : void + public function testMarkedIncompleteEvents(): array { - $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--; + return $this->testMarkedIncompleteEvents; + } + public function numberOfTestMarkedIncompleteEvents(): int + { + return count($this->testMarkedIncompleteEvents); + } + public function hasTestMarkedIncompleteEvents(): bool + { + return $this->numberOfTestMarkedIncompleteEvents() > 0; } /** - * A test started. + * @psalm-return array> */ - public function startTest(Test $test) : void + public function testTriggeredPhpunitDeprecationEvents(): array { - $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); - // @codeCoverageIgnoreStart - } catch (ReflectionException $e) { - throw new Exception($e->getMessage(), $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd - $methodName = $test->getName(!$usesDataprovider); - if ($class->hasMethod($methodName)) { - try { - $method = $class->getMethod($methodName); - // @codeCoverageIgnoreStart - } catch (ReflectionException $e) { - throw new Exception($e->getMessage(), $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd - $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; + return $this->testTriggeredPhpunitDeprecationEvents; + } + public function numberOfTestsWithTestTriggeredPhpunitDeprecationEvents(): int + { + return count($this->testTriggeredPhpunitDeprecationEvents); + } + public function hasTestTriggeredPhpunitDeprecationEvents(): bool + { + return $this->numberOfTestsWithTestTriggeredPhpunitDeprecationEvents() > 0; } /** - * A test ended. + * @psalm-return array> */ - public function endTest(Test $test, float $time) : void + public function testTriggeredPhpunitErrorEvents(): array { - $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; + return $this->testTriggeredPhpunitErrorEvents; + } + public function numberOfTestsWithTestTriggeredPhpunitErrorEvents(): int + { + return count($this->testTriggeredPhpunitErrorEvents); + } + public function hasTestTriggeredPhpunitErrorEvents(): bool + { + return $this->numberOfTestsWithTestTriggeredPhpunitErrorEvents() > 0; } /** - * Returns the XML as a string. + * @psalm-return array> */ - public function getXML() : string + public function testTriggeredPhpunitWarningEvents(): array { - return $this->document->saveXML(); + return $this->testTriggeredPhpunitWarningEvents; } - private function doAddFault(Test $test, Throwable $t, string $type) : void + public function numberOfTestsWithTestTriggeredPhpunitWarningEvents(): int { - if ($this->currentTestCase === null) { - return; - } - if ($test instanceof SelfDescribing) { - $buffer = $test->toString() . "\n"; - } else { - $buffer = ''; - } - $buffer .= trim(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); + return count($this->testTriggeredPhpunitWarningEvents); } - private function doAddSkipped() : void + public function hasTestTriggeredPhpunitWarningEvents(): bool { - if ($this->currentTestCase === null) { - return; - } - $skipped = $this->document->createElement('skipped'); - $this->currentTestCase->appendChild($skipped); - $this->testSuiteSkipped[$this->testSuiteLevel]++; + return $this->numberOfTestsWithTestTriggeredPhpunitWarningEvents() > 0; } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Util\Log; - -use function class_exists; -use function count; -use function explode; -use function get_class; -use function getmypid; -use function ini_get; -use function is_bool; -use function is_scalar; -use function method_exists; -use function print_r; -use function round; -use function str_replace; -use function stripos; -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\DefaultResultPrinter; -use PHPUnit\Util\Exception; -use PHPUnit\Util\Filter; -use ReflectionClass; -use ReflectionException; -use PHPUnitPHAR\SebastianBergmann\Comparator\ComparisonFailure; -use Throwable; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class TeamCity extends DefaultResultPrinter -{ /** - * @var bool + * @psalm-return list */ - private $isSummaryTestCountPrinted = \false; + public function testRunnerTriggeredDeprecationEvents(): array + { + return $this->testRunnerTriggeredDeprecationEvents; + } + public function numberOfTestRunnerTriggeredDeprecationEvents(): int + { + return count($this->testRunnerTriggeredDeprecationEvents); + } + public function hasTestRunnerTriggeredDeprecationEvents(): bool + { + return $this->numberOfTestRunnerTriggeredDeprecationEvents() > 0; + } /** - * @var string + * @psalm-return list */ - private $startedTestName; + public function testRunnerTriggeredWarningEvents(): array + { + return $this->testRunnerTriggeredWarningEvents; + } + public function numberOfTestRunnerTriggeredWarningEvents(): int + { + return count($this->testRunnerTriggeredWarningEvents); + } + public function hasTestRunnerTriggeredWarningEvents(): bool + { + return $this->numberOfTestRunnerTriggeredWarningEvents() > 0; + } + public function wasSuccessful(): bool + { + return $this->wasSuccessfulIgnoringPhpunitWarnings() && !$this->hasTestTriggeredPhpunitErrorEvents() && !$this->hasTestRunnerTriggeredWarningEvents() && !$this->hasTestTriggeredPhpunitWarningEvents(); + } + public function wasSuccessfulIgnoringPhpunitWarnings(): bool + { + return !$this->hasTestErroredEvents() && !$this->hasTestFailedEvents(); + } + public function wasSuccessfulAndNoTestHasIssues(): bool + { + return $this->wasSuccessful() && !$this->hasTestsWithIssues(); + } + public function hasTestsWithIssues(): bool + { + return $this->hasRiskyTests() || $this->hasIncompleteTests() || $this->hasDeprecations() || !empty($this->errors) || $this->hasNotices() || $this->hasWarnings(); + } /** - * @var false|int + * @psalm-return list */ - private $flowId; - public function printResult(TestResult $result) : void + public function errors(): array { - $this->printHeader($result); - $this->printFooter($result); + return $this->errors; } /** - * An error occurred. + * @psalm-return list */ - public function addError(Test $test, Throwable $t, float $time) : void + public function deprecations(): array { - $this->printEvent('testFailed', ['name' => $test->getName(), 'message' => self::getMessage($t), 'details' => self::getDetails($t), 'duration' => self::toMilliseconds($time)]); + return $this->deprecations; } /** - * A warning occurred. + * @psalm-return list */ - public function addWarning(Test $test, Warning $e, float $time) : void + public function notices(): array { - $this->write(self::getMessage($e) . \PHP_EOL); + return $this->notices; } /** - * A failure occurred. + * @psalm-return list */ - public function addFailure(Test $test, AssertionFailedError $e, float $time) : void + public function warnings(): array { - $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); + return $this->warnings; } /** - * Incomplete test. + * @psalm-return list */ - public function addIncompleteTest(Test $test, Throwable $t, float $time) : void + public function phpDeprecations(): array { - $this->printIgnoredTest($test->getName(), $t, $time); + return $this->phpDeprecations; } /** - * Risky test. + * @psalm-return list */ - public function addRiskyTest(Test $test, Throwable $t, float $time) : void + public function phpNotices(): array { - $this->addError($test, $t, $time); + return $this->phpNotices; } /** - * Skipped test. + * @psalm-return list */ - public function addSkippedTest(Test $test, Throwable $t, float $time) : void + public function phpWarnings(): array { - $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); - } + return $this->phpWarnings; } - public function printIgnoredTest(string $testName, Throwable $t, float $time) : void + public function hasTests(): bool { - $this->printEvent('testIgnored', ['name' => $testName, 'message' => self::getMessage($t), 'details' => self::getDetails($t), 'duration' => self::toMilliseconds($time)]); + return $this->numberOfTests > 0; } - /** - * A testsuite started. - */ - public function startTestSuite(TestSuite $suite) : void + public function hasErrors(): bool { - 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); + return $this->numberOfErrors() > 0; } - /** - * A testsuite ended. - */ - public function endTestSuite(TestSuite $suite) : void + public function numberOfErrors(): int { - $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); + return $this->numberOfTestErroredEvents() + count($this->errors) + $this->numberOfTestsWithTestTriggeredPhpunitErrorEvents(); } - /** - * A test started. - */ - public function startTest(Test $test) : void + public function hasDeprecations(): bool { - $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); + return $this->numberOfDeprecations() > 0; } - /** - * A test ended. - */ - public function endTest(Test $test, float $time) : void + public function numberOfDeprecations(): int { - parent::endTest($test, $time); - $this->printEvent('testFinished', ['name' => $test->getName(), 'duration' => self::toMilliseconds($time)]); + return count($this->deprecations) + count($this->phpDeprecations) + count($this->testTriggeredPhpunitDeprecationEvents) + count($this->testRunnerTriggeredDeprecationEvents); } - protected function writeProgress(string $progress) : void + public function hasNotices(): bool { + return $this->numberOfNotices() > 0; } - private function printEvent(string $eventName, array $params = []) : void + public function numberOfNotices(): int { - $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"); + return count($this->notices) + count($this->phpNotices); } - private static function getMessage(Throwable $t) : string + public function hasWarnings(): bool { - $message = ''; - if ($t instanceof ExceptionWrapper) { - if ($t->getClassName() !== '') { - $message .= $t->getClassName(); - } - if ($message !== '' && $t->getMessage() !== '') { - $message .= ' : '; - } - } - return $message . $t->getMessage(); + return $this->numberOfWarnings() > 0; } - private static function getDetails(Throwable $t) : string + public function numberOfWarnings(): int { - $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); + return count($this->warnings) + count($this->phpWarnings) + count($this->testTriggeredPhpunitWarningEvents) + count($this->testRunnerTriggeredWarningEvents); } - private static function getPrimitiveValueAsString($value) : ?string + public function hasIncompleteTests(): bool { - if ($value === null) { - return 'null'; - } - if (is_bool($value)) { - return $value ? 'true' : 'false'; - } - if (is_scalar($value)) { - return print_r($value, \true); - } - return null; + return !empty($this->testMarkedIncompleteEvents); } - private static function escapeValue(string $text) : string + public function hasRiskyTests(): bool { - return str_replace(['|', "'", "\n", "\r", ']', '['], ['||', "|'", '|n', '|r', '|]', '|['], $text); + return !empty($this->testConsideredRiskyEvents); } - /** - * @param string $className - */ - private static function getFileName($className) : string + public function hasSkippedTests(): bool { - try { - return (new ReflectionClass($className))->getFileName(); - // @codeCoverageIgnoreStart - } catch (ReflectionException $e) { - throw new Exception($e->getMessage(), $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd + return !empty($this->testSkippedEvents); + } + public function hasIssuesIgnoredByBaseline(): bool + { + return $this->numberOfIssuesIgnoredByBaseline > 0; } /** - * @param float $time microseconds + * @psalm-return non-negative-int */ - private static function toMilliseconds(float $time) : int + public function numberOfIssuesIgnoredByBaseline(): int { - return (int) round($time * 1000); + return $this->numberOfIssuesIgnoredByBaseline; } } */ - protected $args = ''; + private static array $declaredClasses = []; /** - * @var array + * @psalm-var array> */ - protected $env = []; + private static array $fileToClassesMap = []; /** - * @var int + * @throws Exception */ - protected $timeout = 0; - public static function factory() : self + public function load(string $suiteClassFile): ReflectionClass { - if (DIRECTORY_SEPARATOR === '\\') { - return new \PHPUnit\Util\PHP\WindowsPhpProcess(); + $suiteClassFile = realpath($suiteClassFile); + $suiteClassName = $this->classNameFromFileName($suiteClassFile); + $loadedClasses = $this->loadSuiteClassFile($suiteClassFile); + foreach ($loadedClasses as $className) { + /** @noinspection PhpUnhandledExceptionInspection */ + $class = new ReflectionClass($className); + if ($class->isAnonymous()) { + continue; + } + if ($class->getFileName() !== $suiteClassFile) { + continue; + } + if (!$class->isSubclassOf(TestCase::class)) { + continue; + } + if (!str_ends_with(strtolower($class->getShortName()), strtolower($suiteClassName))) { + continue; + } + if (!$class->isAbstract()) { + return $class; + } + $e = new \PHPUnit\Runner\ClassIsAbstractException($class->getName(), $suiteClassFile); } - return new \PHPUnit\Util\PHP\DefaultPhpProcess(); + if (isset($e)) { + throw $e; + } + foreach ($loadedClasses as $className) { + if (str_ends_with(strtolower($className), strtolower($suiteClassName))) { + throw new \PHPUnit\Runner\ClassDoesNotExtendTestCaseException($className, $suiteClassFile); + } + } + throw new \PHPUnit\Runner\ClassCannotBeFoundException($suiteClassName, $suiteClassFile); } - public function __construct() + private function classNameFromFileName(string $suiteClassFile): string { - $this->runtime = new Runtime(); + $className = basename($suiteClassFile, '.php'); + $dotPos = strpos($className, '.'); + if ($dotPos !== \false) { + $className = substr($className, 0, $dotPos); + } + return $className; } /** - * Defines if should use STDERR redirection or not. - * - * Then $stderrRedirection is TRUE, STDERR is redirected to STDOUT. + * @psalm-return list */ - public function setUseStderrRedirection(bool $stderrRedirection) : void + private function loadSuiteClassFile(string $suiteClassFile): array { - $this->stderrRedirection = $stderrRedirection; + if (isset(self::$fileToClassesMap[$suiteClassFile])) { + return self::$fileToClassesMap[$suiteClassFile]; + } + if (empty(self::$declaredClasses)) { + self::$declaredClasses = get_declared_classes(); + } + require_once $suiteClassFile; + $loadedClasses = array_values(array_diff(get_declared_classes(), self::$declaredClasses)); + foreach ($loadedClasses as $loadedClass) { + /** @noinspection PhpUnhandledExceptionInspection */ + $class = new ReflectionClass($loadedClass); + if (!isset(self::$fileToClassesMap[$class->getFileName()])) { + self::$fileToClassesMap[$class->getFileName()] = []; + } + self::$fileToClassesMap[$class->getFileName()][] = $class->getName(); + } + self::$declaredClasses = get_declared_classes(); + if (empty($loadedClasses)) { + return self::$declaredClasses; + } + return $loadedClasses; } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Runner; + +use function array_diff; +use function array_merge; +use function array_reverse; +use function array_splice; +use function count; +use function in_array; +use function max; +use function shuffle; +use function usort; +use PHPUnit\Framework\DataProviderTestSuite; +use PHPUnit\Framework\Reorderable; +use PHPUnit\Framework\Test; +use PHPUnit\Framework\TestCase; +use PHPUnit\Framework\TestSuite; +use PHPUnit\Runner\ResultCache\NullResultCache; +use PHPUnit\Runner\ResultCache\ResultCache; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class TestSuiteSorter +{ /** - * Returns TRUE if uses STDERR redirection or FALSE if not. + * @var int */ - public function useStderrRedirection() : bool - { - return $this->stderrRedirection; - } + public const ORDER_DEFAULT = 0; /** - * Sets the input string to be sent via STDIN. + * @var int */ - public function setStdin(string $stdin) : void - { - $this->stdin = $stdin; - } + public const ORDER_RANDOMIZED = 1; /** - * Returns the input string to be sent via STDIN. + * @var int */ - public function getStdin() : string - { - return $this->stdin; - } + public const ORDER_REVERSED = 2; /** - * Sets the string of arguments to pass to the php job. + * @var int */ - public function setArgs(string $args) : void - { - $this->args = $args; - } + public const ORDER_DEFECTS_FIRST = 3; /** - * Returns the string of arguments to pass to the php job. + * @var int */ - public function getArgs() : string - { - return $this->args; - } + public const ORDER_DURATION = 4; /** - * Sets the array of environment variables to start the child process with. - * - * @param array $env + * @var int */ - public function setEnv(array $env) : void - { - $this->env = $env; - } + public const ORDER_SIZE = 5; + private const SIZE_SORT_WEIGHT = ['small' => 1, 'medium' => 2, 'large' => 3, 'unknown' => 4]; /** - * Returns the array of environment variables to start the child process with. + * @psalm-var array Associative array of (string => DEFECT_SORT_WEIGHT) elements */ - public function getEnv() : array - { - return $this->env; - } + private array $defectSortOrder = []; + private readonly ResultCache $cache; /** - * Sets the amount of seconds to wait before timing out. + * @psalm-var array A list of normalized names of tests before reordering */ - public function setTimeout(int $timeout) : void - { - $this->timeout = $timeout; - } + private array $originalExecutionOrder = []; /** - * Returns the amount of seconds to wait before timing out. + * @psalm-var array A list of normalized names of tests affected by reordering */ - public function getTimeout() : int + private array $executionOrder = []; + public function __construct(?ResultCache $cache = null) { - return $this->timeout; + $this->cache = $cache ?? new NullResultCache(); } /** - * Runs a single test in a separate PHP process. - * - * @throws InvalidArgumentException + * @throws Exception */ - public function runTestJob(string $job, Test $test, TestResult $result, string $processResultFile) : void + public function reorderTestsInSuite(Test $suite, int $order, bool $resolveDependencies, int $orderDefects, bool $isRootTestSuite = \true): void { - $result->startTest($test); - $processResult = ''; - $_result = $this->runJob($job); - if (file_exists($processResultFile)) { - $processResult = file_get_contents($processResultFile); - @unlink($processResultFile); + $allowedOrders = [self::ORDER_DEFAULT, self::ORDER_REVERSED, self::ORDER_RANDOMIZED, self::ORDER_DURATION, self::ORDER_SIZE]; + if (!in_array($order, $allowedOrders, \true)) { + throw new \PHPUnit\Runner\InvalidOrderException(); + } + $allowedOrderDefects = [self::ORDER_DEFAULT, self::ORDER_DEFECTS_FIRST]; + if (!in_array($orderDefects, $allowedOrderDefects, \true)) { + throw new \PHPUnit\Runner\InvalidOrderException(); + } + 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); } - $this->processChildResult($test, $result, $processResult, $_result['stderr']); } - /** - * Returns the command based into the configurations. - */ - public function getCommand(array $settings, ?string $file = null) : string + 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 { - $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')))); + if (empty($suite->tests())) { + return; } - $command .= $this->settingsToParameters($settings); - if (PHP_SAPI === 'phpdbg') { - $command .= ' -qrr'; - if (!$file) { - $command .= 's='; - } + 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) { + $suite->setTests($this->sortByDuration($suite->tests())); + } elseif ($order === self::ORDER_SIZE) { + $suite->setTests($this->sortBySize($suite->tests())); } - if ($file) { - $command .= ' ' . escapeshellarg($file); + if ($orderDefects === self::ORDER_DEFECTS_FIRST) { + $suite->setTests($this->sortDefectsFirst($suite->tests())); } - if ($this->args) { - if (!$file) { - $command .= ' --'; + if ($resolveDependencies && !$suite instanceof DataProviderTestSuite) { + $tests = $suite->tests(); + $suite->setTests($this->resolveDependencies($tests)); + } + } + private function addSuiteToDefectSortOrder(TestSuite $suite): void + { + $max = 0; + foreach ($suite->tests() as $test) { + if (!$test instanceof Reorderable) { + continue; + } + if (!isset($this->defectSortOrder[$test->sortId()])) { + $this->defectSortOrder[$test->sortId()] = $this->cache->status($test->sortId())->asInt(); + $max = max($max, $this->defectSortOrder[$test->sortId()]); } - $command .= ' ' . $this->args; } - if ($this->stderrRedirection) { - $command .= ' 2>&1'; + $this->defectSortOrder[$suite->sortId()] = $max; + } + 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, fn($left, $right) => $this->cmpDefectPriorityAndTime($left, $right)); + return $tests; + } + private function sortByDuration(array $tests): array + { + usort($tests, fn($left, $right) => $this->cmpDuration($left, $right)); + return $tests; + } + private function sortBySize(array $tests): array + { + usort($tests, fn($left, $right) => $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 + */ + private function cmpDefectPriorityAndTime(Test $a, Test $b): int + { + if (!($a instanceof Reorderable && $b instanceof Reorderable)) { + return 0; } - return $command; + $priorityA = $this->defectSortOrder[$a->sortId()] ?? 0; + $priorityB = $this->defectSortOrder[$b->sortId()] ?? 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; } /** - * Runs a single job (PHP code) using a separate PHP process. + * Compares test duration for sorting tests by duration ascending. */ - public abstract function runJob(string $job, array $settings = []) : array; - protected function settingsToParameters(array $settings) : string + private function cmpDuration(Test $a, Test $b): int { - $buffer = ''; - foreach ($settings as $setting) { - $buffer .= ' -d ' . escapeshellarg($setting); + if (!($a instanceof Reorderable && $b instanceof Reorderable)) { + return 0; } - return $buffer; + return $this->cache->time($a->sortId()) <=> $this->cache->time($b->sortId()); + } + /** + * 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->size()->asString() : 'unknown'; + $sizeB = ($b instanceof TestCase || $b instanceof DataProviderTestSuite) ? $b->size()->asString() : 'unknown'; + return self::SIZE_SORT_WEIGHT[$sizeA] <=> self::SIZE_SORT_WEIGHT[$sizeB]; } /** - * Processes the TestResult object from an isolated process. + * 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. * - * @throws InvalidArgumentException + * 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. + * + * @psalm-param array $tests + * + * @psalm-return array */ - private function processChildResult(Test $test, TestResult $result, string $stdout, string $stderr) : void + private function resolveDependencies(array $tests): array { - $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(); - if ($childResult === \false) { - $result->addFailure($test, new AssertionFailedError('Test was run in child process and ended unexpectedly'), $time); - } - } 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); + $newTestOrder = []; + $i = 0; + $provided = []; + do { + if ([] === array_diff($tests[$i]->requires(), $provided)) { + $provided = array_merge($provided, $tests[$i]->provides()); + $newTestOrder = array_merge($newTestOrder, array_splice($tests, $i, 1)); + $i = 0; + } else { + $i++; + } + } while (!empty($tests) && $i < count($tests)); + return array_merge($newTestOrder, $tests); + } + private function calculateTestExecutionOrder(Test $suite): array + { + $tests = []; + if ($suite instanceof TestSuite) { + foreach ($suite->tests() as $test) { + if (!$test instanceof TestSuite && $test instanceof Reorderable) { + $tests[] = $test->sortId(); + } else { + $tests = array_merge($tests, $this->calculateTestExecutionOrder($test)); } } } - $result->endTest($test, $time); - if (!empty($output)) { - print $output; - } + return $tests; } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Runner; + +use function array_slice; +use function dirname; +use function explode; +use function implode; +use function str_contains; +use PHPUnitPHAR\SebastianBergmann\Version as VersionId; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class Version +{ + private static string $pharVersion = '10.5.26'; + private static string $version = ''; /** - * Gets the thrown exception from a PHPUnit\Framework\TestFailure. - * - * @see https://github.com/sebastianbergmann/phpunit/issues/74 + * Returns the current version of PHPUnit. */ - private function getException(TestFailure $error) : Exception + public static function id(): string { - $exception = $error->thrownException(); - if ($exception instanceof __PHP_Incomplete_Class) { - $exceptionArray = []; - foreach ((array) $exception as $key => $value) { - $key = substr($key, strrpos($key, "\x00") + 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']); + if (self::$pharVersion !== '') { + return self::$pharVersion; + } + if (self::$version === '') { + self::$version = (new VersionId('10.5.26', dirname(__DIR__, 2)))->asString(); + } + return self::$version; + } + public static function series(): string + { + if (str_contains(self::id(), '-')) { + $version = explode('-', self::id(), 2)[0]; + } else { + $version = self::id(); } - return $exception; + return implode('.', array_slice(explode('.', $version), 0, 2)); + } + public static function majorVersionNumber(): int + { + return (int) explode('.', self::series())[0]; + } + public static function getVersionString(): string + { + return 'PHPUnit ' . self::id() . ' by Sebastian Bergmann and contributors.'; } } applicationStarted(); + $cliConfiguration = $this->buildCliConfiguration($argv); + $pathToXmlConfigurationFile = (new XmlConfigurationFileFinder())->find($cliConfiguration); + $this->executeCommandsThatOnlyRequireCliConfiguration($cliConfiguration, $pathToXmlConfigurationFile); + $xmlConfiguration = $this->loadXmlConfiguration($pathToXmlConfigurationFile); + $configuration = Registry::init($cliConfiguration, $xmlConfiguration); + (new PhpHandler())->handle($configuration->php()); + if ($configuration->hasBootstrap()) { + $this->loadBootstrapScript($configuration->bootstrap()); + } + $this->executeCommandsThatRequireCompleteConfiguration($configuration, $cliConfiguration); + $testSuite = $this->buildTestSuite($configuration); + $this->executeCommandsThatRequireCliConfigurationAndTestSuite($cliConfiguration, $testSuite); + $this->executeHelpCommandWhenThereIsNothingElseToDo($configuration, $testSuite); + $pharExtensions = null; + $extensionRequiresCodeCoverageCollection = \false; + $extensionReplacesOutput = \false; + $extensionReplacesProgressOutput = \false; + $extensionReplacesResultOutput = \false; + $extensionRequiresExportOfObjects = \false; + if (!$configuration->noExtensions()) { + if ($configuration->hasPharExtensionDirectory()) { + $pharExtensions = (new PharLoader())->loadPharExtensionsInDirectory($configuration->pharExtensionDirectory()); + } + $bootstrappedExtensions = $this->bootstrapExtensions($configuration); + $extensionRequiresCodeCoverageCollection = $bootstrappedExtensions['requiresCodeCoverageCollection']; + $extensionReplacesOutput = $bootstrappedExtensions['replacesOutput']; + $extensionReplacesProgressOutput = $bootstrappedExtensions['replacesProgressOutput']; + $extensionReplacesResultOutput = $bootstrappedExtensions['replacesResultOutput']; + $extensionRequiresExportOfObjects = $bootstrappedExtensions['requiresExportOfObjects']; + } + if ($extensionRequiresExportOfObjects) { + EventFacade::emitter()->exportObjects(); + } + CodeCoverage::instance()->init($configuration, CodeCoverageFilterRegistry::instance(), $extensionRequiresCodeCoverageCollection); + if (CodeCoverage::instance()->isActive()) { + CodeCoverage::instance()->ignoreLines((new CodeCoverageMetadataApi())->linesToBeIgnored($testSuite)); + } + $printer = OutputFacade::init($configuration, $extensionReplacesProgressOutput, $extensionReplacesResultOutput); + if (!$configuration->debug() && !$extensionReplacesOutput) { + $this->writeRuntimeInformation($printer, $configuration); + $this->writePharExtensionInformation($printer, $pharExtensions); + $this->writeRandomSeedInformation($printer, $configuration); + $printer->print(PHP_EOL); + } + if ($configuration->debug()) { + EventFacade::instance()->registerTracer(new EventLogger('php://stdout', \false)); + } + $this->registerLogfileWriters($configuration); + $testDoxResultCollector = $this->testDoxResultCollector($configuration); + TestResultFacade::init(); + $resultCache = $this->initializeTestResultCache($configuration); + if ($configuration->controlGarbageCollector()) { + new GarbageCollectionHandler(EventFacade::instance(), $configuration->numberOfTestsBeforeGarbageCollection()); + } + $baselineGenerator = $this->configureBaseline($configuration); + EventFacade::instance()->seal(); + $timer = new Timer(); + $timer->start(); + $runner = new \PHPUnit\TextUI\TestRunner(); + $runner->run($configuration, $resultCache, $testSuite); + $duration = $timer->stop(); + $testDoxResult = null; + if (isset($testDoxResultCollector)) { + $testDoxResult = $testDoxResultCollector->testMethodsGroupedByClass(); + } + if ($testDoxResult !== null && $configuration->hasLogfileTestdoxHtml()) { + try { + OutputFacade::printerFor($configuration->logfileTestdoxHtml())->print((new TestDoxHtmlRenderer())->render($testDoxResult)); + } catch (DirectoryDoesNotExistException|\PHPUnit\TextUI\InvalidSocketException $e) { + EventFacade::emitter()->testRunnerTriggeredWarning(sprintf('Cannot log test results in TestDox HTML format to "%s": %s', $configuration->logfileTestdoxHtml(), $e->getMessage())); + } + } + if ($testDoxResult !== null && $configuration->hasLogfileTestdoxText()) { + try { + OutputFacade::printerFor($configuration->logfileTestdoxText())->print((new TestDoxTextRenderer())->render($testDoxResult)); + } catch (DirectoryDoesNotExistException|\PHPUnit\TextUI\InvalidSocketException $e) { + EventFacade::emitter()->testRunnerTriggeredWarning(sprintf('Cannot log test results in TestDox plain text format to "%s": %s', $configuration->logfileTestdoxText(), $e->getMessage())); + } + } + $result = TestResultFacade::result(); + if (!$extensionReplacesResultOutput && !$configuration->debug()) { + OutputFacade::printResult($result, $testDoxResult, $duration); + } + CodeCoverage::instance()->generateReports($printer, $configuration); + if (isset($baselineGenerator)) { + (new Writer())->write($configuration->generateBaseline(), $baselineGenerator->baseline()); + $printer->print(sprintf(PHP_EOL . 'Baseline written to %s.' . PHP_EOL, realpath($configuration->generateBaseline()))); + } + $shellExitCode = (new \PHPUnit\TextUI\ShellExitCodeCalculator())->calculate($configuration->failOnDeprecation(), $configuration->failOnEmptyTestSuite(), $configuration->failOnIncomplete(), $configuration->failOnNotice(), $configuration->failOnRisky(), $configuration->failOnSkipped(), $configuration->failOnWarning(), $result); + EventFacade::emitter()->applicationFinished($shellExitCode); + return $shellExitCode; + // @codeCoverageIgnoreStart + } catch (Throwable $t) { + $this->exitWithCrashMessage($t); + } + // @codeCoverageIgnoreEnd + } + private function execute(\PHPUnit\TextUI\Command\Command $command): never + { + print Version::getVersionString() . PHP_EOL . PHP_EOL; + $result = $command->execute(); + print $result->output(); + exit($result->shellExitCode()); + } + private function loadBootstrapScript(string $filename): void + { + if (!is_readable($filename)) { + $this->exitWithErrorMessage(sprintf('Cannot open bootstrap script "%s"', $filename)); + } + try { + include_once $filename; + } catch (Throwable $t) { + $message = sprintf('Error in bootstrap script: %s:%s%s%s%s', $t::class, PHP_EOL, $t->getMessage(), PHP_EOL, $t->getTraceAsString()); + while ($t = $t->getPrevious()) { + $message .= sprintf('%s%sPrevious error: %s:%s%s%s%s', PHP_EOL, PHP_EOL, $t::class, PHP_EOL, $t->getMessage(), PHP_EOL, $t->getTraceAsString()); + } + $this->exitWithErrorMessage($message); + } + EventFacade::emitter()->testRunnerBootstrapFinished($filename); + } + private function buildCliConfiguration(array $argv): CliConfiguration + { + try { + $cliConfiguration = (new Builder())->fromParameters($argv); + } catch (ArgumentsException $e) { + $this->exitWithErrorMessage($e->getMessage()); + } + return $cliConfiguration; + } + private function loadXmlConfiguration(false|string $configurationFile): XmlConfiguration + { + if ($configurationFile === \false) { + return DefaultConfiguration::create(); + } + try { + return (new Loader())->load($configurationFile); + } catch (Throwable $e) { + $this->exitWithErrorMessage($e->getMessage()); + } + } + private function buildTestSuite(Configuration $configuration): TestSuite + { + try { + return (new TestSuiteBuilder())->build($configuration); + } catch (\PHPUnit\TextUI\Exception $e) { + $this->exitWithErrorMessage($e->getMessage()); + } + } /** - * @var string - */ - protected $tempFile; - /** - * Runs a single job (PHP code) using a separate PHP process. - * - * @throws Exception + * @psalm-return array{requiresCodeCoverageCollection: bool, replacesOutput: bool, replacesProgressOutput: bool, replacesResultOutput: bool, requiresExportOfObjects: bool} */ - public function runJob(string $job, array $settings = []) : array + private function bootstrapExtensions(Configuration $configuration): array + { + $facade = new ExtensionFacade(); + $extensionBootstrapper = new ExtensionBootstrapper($configuration, $facade); + foreach ($configuration->extensionBootstrappers() as $bootstrapper) { + $extensionBootstrapper->bootstrap($bootstrapper['className'], $bootstrapper['parameters']); + } + return ['requiresCodeCoverageCollection' => $facade->requiresCodeCoverageCollection(), 'replacesOutput' => $facade->replacesOutput(), 'replacesProgressOutput' => $facade->replacesProgressOutput(), 'replacesResultOutput' => $facade->replacesResultOutput(), 'requiresExportOfObjects' => $facade->requiresExportOfObjects()]; + } + private function executeCommandsThatOnlyRequireCliConfiguration(CliConfiguration $cliConfiguration, false|string $configurationFile): void { - 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'); + if ($cliConfiguration->generateConfiguration()) { + $this->execute(new GenerateConfigurationCommand()); + } + if ($cliConfiguration->migrateConfiguration()) { + if ($configurationFile === \false) { + $this->exitWithErrorMessage('No configuration file found to migrate'); } - $job = $this->stdin; + $this->execute(new MigrateConfigurationCommand(realpath($configurationFile))); + } + if ($cliConfiguration->hasAtLeastVersion()) { + $this->execute(new AtLeastVersionCommand($cliConfiguration->atLeastVersion())); + } + if ($cliConfiguration->version()) { + $this->execute(new ShowVersionCommand()); + } + if ($cliConfiguration->checkVersion()) { + $this->execute(new VersionCheckCommand()); + } + if ($cliConfiguration->help()) { + $this->execute(new ShowHelpCommand(Result::SUCCESS)); + } + } + private function executeCommandsThatRequireCliConfigurationAndTestSuite(CliConfiguration $cliConfiguration, TestSuite $testSuite): void + { + if ($cliConfiguration->listGroups()) { + $this->execute(new ListGroupsCommand($testSuite)); + } + if ($cliConfiguration->listTests()) { + $this->execute(new ListTestsAsTextCommand($testSuite)); + } + if ($cliConfiguration->hasListTestsXml()) { + $this->execute(new ListTestsAsXmlCommand($cliConfiguration->listTestsXml(), $testSuite)); + } + } + private function executeCommandsThatRequireCompleteConfiguration(Configuration $configuration, CliConfiguration $cliConfiguration): void + { + if ($cliConfiguration->listSuites()) { + $this->execute(new ListTestSuitesCommand($configuration->testSuite())); + } + if ($cliConfiguration->warmCoverageCache()) { + $this->execute(new WarmCodeCoverageCacheCommand($configuration, CodeCoverageFilterRegistry::instance())); + } + } + private function executeHelpCommandWhenThereIsNothingElseToDo(Configuration $configuration, TestSuite $testSuite): void + { + if ($testSuite->isEmpty() && !$configuration->hasCliArguments() && $configuration->testSuite()->isEmpty()) { + $this->execute(new ShowHelpCommand(Result::FAILURE)); + } + } + private function writeRuntimeInformation(Printer $printer, Configuration $configuration): void + { + $printer->print(Version::getVersionString() . PHP_EOL . PHP_EOL); + $runtime = 'PHP ' . \PHP_VERSION; + if (CodeCoverage::instance()->isActive()) { + $runtime .= ' with ' . CodeCoverage::instance()->driver()->nameAndVersion(); + } + $this->writeMessage($printer, 'Runtime', $runtime); + if ($configuration->hasConfigurationFile()) { + $this->writeMessage($printer, 'Configuration', $configuration->configurationFile()); } - return $this->runProcess($job, $settings); } /** - * Returns an array of file handles to be used in place of pipes. + * @psalm-param ?list $pharExtensions */ - protected function getHandles() : array + private function writePharExtensionInformation(Printer $printer, ?array $pharExtensions): void { - return []; + if ($pharExtensions === null) { + return; + } + foreach ($pharExtensions as $extension) { + $this->writeMessage($printer, 'Extension', $extension); + } + } + private function writeMessage(Printer $printer, string $type, string $message): void + { + $printer->print(sprintf("%-15s%s\n", $type . ':', $message)); + } + private function writeRandomSeedInformation(Printer $printer, Configuration $configuration): void + { + if ($configuration->executionOrder() === TestSuiteSorter::ORDER_RANDOMIZED) { + $this->writeMessage($printer, 'Random Seed', (string) $configuration->randomOrderSeed()); + } } /** - * Handles creating the child process and returning the STDOUT and STDERR. - * - * @throws Exception + * @throws EventFacadeIsSealedException + * @throws UnknownSubscriberTypeException */ - protected function runProcess(string $job, array $settings) : array + private function registerLogfileWriters(Configuration $configuration): void { - $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]); - } + if ($configuration->hasLogEventsText()) { + if (is_file($configuration->logEventsText())) { + unlink($configuration->logEventsText()); } + EventFacade::instance()->registerTracer(new EventLogger($configuration->logEventsText(), \false)); } - $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 === '' || $line === \false) { - fclose($pipes[$pipeOffset]); - unset($pipes[$pipeOffset]); - } elseif ($pipeOffset === 1) { - $stdout .= $line; - } else { - $stderr .= $line; - } - } - if (empty($pipes)) { - break; - } - } + if ($configuration->hasLogEventsVerboseText()) { + if (is_file($configuration->logEventsVerboseText())) { + unlink($configuration->logEventsVerboseText()); } - } else { - if (isset($pipes[1])) { - $stdout = stream_get_contents($pipes[1]); - fclose($pipes[1]); + EventFacade::instance()->registerTracer(new EventLogger($configuration->logEventsVerboseText(), \true)); + EventFacade::emitter()->exportObjects(); + } + if ($configuration->hasLogfileJunit()) { + try { + new JunitXmlLogger(OutputFacade::printerFor($configuration->logfileJunit()), EventFacade::instance()); + } catch (DirectoryDoesNotExistException|\PHPUnit\TextUI\InvalidSocketException $e) { + EventFacade::emitter()->testRunnerTriggeredWarning(sprintf('Cannot log test results in JUnit XML format to "%s": %s', $configuration->logfileJunit(), $e->getMessage())); } - if (isset($pipes[2])) { - $stderr = stream_get_contents($pipes[2]); - fclose($pipes[2]); + } + if ($configuration->hasLogfileTeamcity()) { + try { + new TeamCityLogger(DefaultPrinter::from($configuration->logfileTeamcity()), EventFacade::instance()); + } catch (DirectoryDoesNotExistException|\PHPUnit\TextUI\InvalidSocketException $e) { + EventFacade::emitter()->testRunnerTriggeredWarning(sprintf('Cannot log test results in TeamCity format to "%s": %s', $configuration->logfileTeamcity(), $e->getMessage())); } } - if (isset($handles[1])) { - rewind($handles[1]); - $stdout = stream_get_contents($handles[1]); - fclose($handles[1]); + } + /** + * @throws EventFacadeIsSealedException + * @throws UnknownSubscriberTypeException + */ + private function testDoxResultCollector(Configuration $configuration): ?TestDoxResultCollector + { + if ($configuration->hasLogfileTestdoxHtml() || $configuration->hasLogfileTestdoxText() || $configuration->outputIsTestDox()) { + return new TestDoxResultCollector(EventFacade::instance()); } - if (isset($handles[2])) { - rewind($handles[2]); - $stderr = stream_get_contents($handles[2]); - fclose($handles[2]); + return null; + } + /** + * @throws EventFacadeIsSealedException + * @throws UnknownSubscriberTypeException + */ + private function initializeTestResultCache(Configuration $configuration): ResultCache + { + if ($configuration->cacheResult()) { + $cache = new DefaultResultCache($configuration->testResultCacheFile()); + new ResultCacheHandler($cache, EventFacade::instance()); + return $cache; } - proc_close($process); - $this->cleanup(); - return ['stdout' => $stdout, 'stderr' => $stderr]; + return new NullResultCache(); } /** - * @param resource $pipe + * @throws EventFacadeIsSealedException + * @throws UnknownSubscriberTypeException */ - protected function process($pipe, string $job) : void + private function configureBaseline(Configuration $configuration): ?BaselineGenerator { - fwrite($pipe, $job); + if ($configuration->hasGenerateBaseline()) { + return new BaselineGenerator(EventFacade::instance(), $configuration->source()); + } + if ($configuration->source()->useBaseline()) { + /** @psalm-suppress MissingThrowsDocblock */ + $baselineFile = $configuration->source()->baseline(); + $baseline = null; + try { + $baseline = (new Reader())->read($baselineFile); + } catch (CannotLoadBaselineException $e) { + EventFacade::emitter()->testRunnerTriggeredWarning($e->getMessage()); + } + if ($baseline !== null) { + ErrorHandler::instance()->use($baseline); + } + } + return null; } - protected function cleanup() : void + /** + * @codeCoverageIgnore + */ + private function exitWithCrashMessage(Throwable $t): never { - if ($this->tempFile) { - unlink($this->tempFile); + $message = $t->getMessage(); + if (empty(trim($message))) { + $message = '(no message)'; + } + printf('%s%sAn error occurred inside PHPUnit.%s%sMessage: %s', PHP_EOL, PHP_EOL, PHP_EOL, PHP_EOL, $message); + $first = \true; + if ($t->getPrevious()) { + $t = $t->getPrevious(); } + do { + printf('%s%s: %s:%d%s%s%s%s', PHP_EOL, $first ? 'Location' : 'Caused by', $t->getFile(), $t->getLine(), PHP_EOL, PHP_EOL, $t->getTraceAsString(), PHP_EOL); + $first = \false; + } while ($t = $t->getPrevious()); + exit(Result::CRASH); } - protected function useTemporaryFile() : bool + private function exitWithErrorMessage(string $message): never { - return \false; + print Version::getVersionString() . PHP_EOL . PHP_EOL . $message . PHP_EOL; + exit(Result::EXCEPTION); } } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\Command; -if (isset($GLOBALS['__PHPUNIT_BOOTSTRAP'])) { - require_once $GLOBALS['__PHPUNIT_BOOTSTRAP']; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +interface Command +{ + public function execute(): \PHPUnit\TextUI\Command\Result; } +{driverMethod}($filter), - $filter - ); +declare (strict_types=1); +/* + * This file is part of PHPUnit. + * + * (c) Sebastian Bergmann + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\Command; - if ({codeCoverageCacheDirectory}) { - $coverage->cacheStaticAnalysis({codeCoverageCacheDirectory}); +use function version_compare; +use PHPUnit\Runner\Version; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class AtLeastVersionCommand implements \PHPUnit\TextUI\Command\Command +{ + private readonly string $version; + public function __construct(string $version) + { + $this->version = $version; } - - $coverage->start(__FILE__); -} - -register_shutdown_function( - function() use ($coverage) { - $output = null; - - if ($coverage) { - $output = $coverage->stop(); + public function execute(): \PHPUnit\TextUI\Command\Result + { + if (version_compare(Version::id(), $this->version, '>=')) { + return \PHPUnit\TextUI\Command\Result::from(); } - - file_put_contents('{coverageFile}', serialize($output)); + return \PHPUnit\TextUI\Command\Result::from('', \PHPUnit\TextUI\Command\Result::FAILURE); } -); - -ob_end_clean(); - -require '{job}'; - + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\Command; -if ($composerAutoload) { - require_once $composerAutoload; - define('PHPUNIT_COMPOSER_INSTALL', $composerAutoload); -} else if ($phar) { - require $phar; -} - -function __phpunit_run_isolated_test() +use function fgets; +use function file_put_contents; +use function getcwd; +use function trim; +use PHPUnit\Runner\Version; +use PHPUnit\TextUI\XmlConfiguration\Generator; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class GenerateConfigurationCommand implements \PHPUnit\TextUI\Command\Command { - if (!class_exists('{className}')) { - require_once '{filename}'; - } - - $result = new PHPUnit\Framework\TestResult; - - if ({collectCodeCoverageInformation}) { - $filter = unserialize('{codeCoverageFilter}'); - - $codeCoverage = new CodeCoverage( - (new Selector)->{driverMethod}($filter), - $filter - ); - - if ({cachesStaticAnalysis}) { - $codeCoverage->cacheStaticAnalysis(unserialize('{codeCoverageCacheDirectory}')); + public function execute(): \PHPUnit\TextUI\Command\Result + { + print 'Generating phpunit.xml in ' . getcwd() . \PHP_EOL . \PHP_EOL; + print 'Bootstrap script (relative to path shown above; default: vendor/autoload.php): '; + $bootstrapScript = $this->read(); + print 'Tests directory (relative to path shown above; default: tests): '; + $testsDirectory = $this->read(); + print 'Source directory (relative to path shown above; default: src): '; + $src = $this->read(); + print 'Cache directory (relative to path shown above; default: .phpunit.cache): '; + $cacheDirectory = $this->read(); + if ($bootstrapScript === '') { + $bootstrapScript = 'vendor/autoload.php'; } - - $result->setCodeCoverage($codeCoverage); - } - - $result->beStrictAboutTestsThatDoNotTestAnything({isStrictAboutTestsThatDoNotTestAnything}); - $result->beStrictAboutOutputDuringTests({isStrictAboutOutputDuringTests}); - $result->enforceTimeLimit({enforcesTimeLimit}); - $result->beStrictAboutTodoAnnotatedTests({isStrictAboutTodoAnnotatedTests}); - $result->beStrictAboutResourceUsageDuringSmallTests({isStrictAboutResourceUsageDuringSmallTests}); - - $test = new {className}('{name}', unserialize('{data}'), '{dataName}'); - $test->setDependencyInput(unserialize('{dependencyInput}')); - $test->setInIsolation(true); - - ob_end_clean(); - $test->run($result); - $output = ''; - if (!$test->hasExpectationOnOutput()) { - $output = $test->getActualOutput(); - } - - ini_set('xdebug.scream', '0'); - - @rewind(STDOUT); /* @ as not every STDOUT target stream is rewindable */ - if ($stdout = @stream_get_contents(STDOUT)) { - $output = $stdout . $output; - $streamMetaData = stream_get_meta_data(STDOUT); - if (!empty($streamMetaData['stream_type']) && 'STDIO' === $streamMetaData['stream_type']) { - @ftruncate(STDOUT, 0); - @rewind(STDOUT); + if ($testsDirectory === '') { + $testsDirectory = 'tests'; + } + if ($src === '') { + $src = 'src'; + } + if ($cacheDirectory === '') { + $cacheDirectory = '.phpunit.cache'; } + $generator = new Generator(); + file_put_contents('phpunit.xml', $generator->generateDefaultConfiguration(Version::series(), $bootstrapScript, $testsDirectory, $src, $cacheDirectory)); + /* @noinspection MissingDirectorySeparatorInspection */ + print \PHP_EOL . 'Generated phpunit.xml in ' . getcwd() . '.' . \PHP_EOL; + print 'Make sure to exclude the ' . $cacheDirectory . ' directory from version control.' . \PHP_EOL; + return \PHPUnit\TextUI\Command\Result::from(); + } + private function read(): string + { + return trim(fgets(\STDIN)); } - - file_put_contents( - '{processResultFile}', - serialize( - [ - 'testResult' => $test->getResult(), - 'numAssertions' => $test->getNumAssertions(), - 'result' => $result, - 'output' => $output - ] - ) - ); -} - -$configurationFilePath = '{configurationFilePath}'; - -if ('' !== $configurationFilePath) { - $configuration = (new Loader)->load($configurationFilePath); - - (new PhpHandler)->handle($configuration->php()); - - unset($configuration); -} - -function __phpunit_error_handler($errno, $errstr, $errfile, $errline) -{ - return true; -} - -set_error_handler('__phpunit_error_handler'); - -{constants} -{included_files} -{globals} - -restore_error_handler(); - -if (isset($GLOBALS['__PHPUNIT_BOOTSTRAP'])) { - require_once $GLOBALS['__PHPUNIT_BOOTSTRAP']; - unset($GLOBALS['__PHPUNIT_BOOTSTRAP']); } - -__phpunit_run_isolated_test(); + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\Command; -function __phpunit_run_isolated_test() +use function sort; +use function sprintf; +use function str_starts_with; +use PHPUnit\Framework\TestSuite; +use PHPUnit\TextUI\Configuration\Registry; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class ListGroupsCommand implements \PHPUnit\TextUI\Command\Command { - if (!class_exists('{className}')) { - require_once '{filename}'; + private readonly TestSuite $suite; + public function __construct(TestSuite $suite) + { + $this->suite = $suite; } - - $result = new PHPUnit\Framework\TestResult; - - if ({collectCodeCoverageInformation}) { - $filter = unserialize('{codeCoverageFilter}'); - - $codeCoverage = new CodeCoverage( - (new Selector)->{driverMethod}($filter), - $filter - ); - - if ({cachesStaticAnalysis}) { - $codeCoverage->cacheStaticAnalysis(unserialize('{codeCoverageCacheDirectory}')); + public function execute(): \PHPUnit\TextUI\Command\Result + { + $buffer = $this->warnAboutConflictingOptions(); + $buffer .= 'Available test group(s):' . \PHP_EOL; + $groups = $this->suite->groups(); + sort($groups); + foreach ($groups as $group) { + if (str_starts_with($group, '__phpunit_')) { + continue; + } + $buffer .= sprintf(' - %s' . \PHP_EOL, $group); } - - $result->setCodeCoverage($codeCoverage); - } - - $result->beStrictAboutTestsThatDoNotTestAnything({isStrictAboutTestsThatDoNotTestAnything}); - $result->beStrictAboutOutputDuringTests({isStrictAboutOutputDuringTests}); - $result->enforceTimeLimit({enforcesTimeLimit}); - $result->beStrictAboutTodoAnnotatedTests({isStrictAboutTodoAnnotatedTests}); - $result->beStrictAboutResourceUsageDuringSmallTests({isStrictAboutResourceUsageDuringSmallTests}); - - $test = new {className}('{methodName}', unserialize('{data}'), '{dataName}'); - \assert($test instanceof TestCase); - - $test->setDependencyInput(unserialize('{dependencyInput}')); - $test->setInIsolation(true); - - ob_end_clean(); - $test->run($result); - $output = ''; - if (!$test->hasExpectationOnOutput()) { - $output = $test->getActualOutput(); + return \PHPUnit\TextUI\Command\Result::from($buffer); } - - ini_set('xdebug.scream', '0'); - - @rewind(STDOUT); /* @ as not every STDOUT target stream is rewindable */ - if ($stdout = @stream_get_contents(STDOUT)) { - $output = $stdout . $output; - $streamMetaData = stream_get_meta_data(STDOUT); - if (!empty($streamMetaData['stream_type']) && 'STDIO' === $streamMetaData['stream_type']) { - @ftruncate(STDOUT, 0); - @rewind(STDOUT); + private function warnAboutConflictingOptions(): string + { + $buffer = ''; + $configuration = Registry::get(); + if ($configuration->hasFilter()) { + $buffer .= 'The --filter and --list-groups options cannot be combined, --filter is ignored' . \PHP_EOL; + } + if ($configuration->hasGroups()) { + $buffer .= 'The --group and --list-groups options cannot be combined, --group is ignored' . \PHP_EOL; } + if ($configuration->hasExcludeGroups()) { + $buffer .= 'The --exclude-group and --list-groups options cannot be combined, --exclude-group is ignored' . \PHP_EOL; + } + if ($configuration->includeTestSuite() !== '') { + $buffer .= 'The --testsuite and --list-groups options cannot be combined, --exclude-group is ignored' . \PHP_EOL; + } + if (!empty($buffer)) { + $buffer .= \PHP_EOL; + } + return $buffer; } - - file_put_contents( - '{processResultFile}', - serialize( - [ - 'testResult' => $test->getResult(), - 'numAssertions' => $test->getNumAssertions(), - 'result' => $result, - 'output' => $output - ] - ) - ); -} - -$configurationFilePath = '{configurationFilePath}'; - -if ('' !== $configurationFilePath) { - $configuration = (new Loader)->load($configurationFilePath); - - (new PhpHandler)->handle($configuration->php()); - - unset($configuration); -} - -function __phpunit_error_handler($errno, $errstr, $errfile, $errline) -{ - return true; -} - -set_error_handler('__phpunit_error_handler'); - -{constants} -{included_files} -{globals} - -restore_error_handler(); - -if (isset($GLOBALS['__PHPUNIT_BOOTSTRAP'])) { - require_once $GLOBALS['__PHPUNIT_BOOTSTRAP']; - unset($GLOBALS['__PHPUNIT_BOOTSTRAP']); } - -__phpunit_run_isolated_test(); suites = $suites; } - /** - * @throws Exception - */ - protected function getHandles() : array + public function execute(): \PHPUnit\TextUI\Command\Result { - if (\false === ($stdout_handle = tmpfile())) { - throw new Exception('A temporary file could not be created; verify that your TEMP environment variable is writable'); + $buffer = $this->warnAboutConflictingOptions(); + $buffer .= 'Available test suite(s):' . \PHP_EOL; + foreach ($this->suites as $suite) { + $buffer .= sprintf(' - %s' . \PHP_EOL, $suite->name()); } - return [1 => $stdout_handle]; + return \PHPUnit\TextUI\Command\Result::from($buffer); } - protected function useTemporaryFile() : bool + private function warnAboutConflictingOptions(): string { - return \true; + $buffer = ''; + $configuration = Registry::get(); + if ($configuration->hasFilter()) { + $buffer .= 'The --filter and --list-suites options cannot be combined, --filter is ignored' . \PHP_EOL; + } + if ($configuration->hasGroups()) { + $buffer .= 'The --group and --list-suites options cannot be combined, --group is ignored' . \PHP_EOL; + } + if ($configuration->hasExcludeGroups()) { + $buffer .= 'The --exclude-group and --list-suites options cannot be combined, --exclude-group is ignored' . \PHP_EOL; + } + if ($configuration->includeTestSuite() !== '') { + $buffer .= 'The --testsuite and --list-suites options cannot be combined, --exclude-group is ignored' . \PHP_EOL; + } + if (!empty($buffer)) { + $buffer .= \PHP_EOL; + } + return $buffer; } } stream = $out; - return; - } - if (!is_string($out)) { - return; - } - if (strpos($out, 'socket://') === 0) { - $tmp = explode(':', str_replace('socket://', '', $out)); - if (count($tmp) !== 2) { - throw new \PHPUnit\Util\Exception(sprintf('"%s" does not match "socket://hostname:port" format', $out)); - } - $this->stream = fsockopen($tmp[0], (int) $tmp[1]); - return; - } - if (strpos($out, 'php://') === \false && !\PHPUnit\Util\Filesystem::createDirectory(dirname($out))) { - throw new \PHPUnit\Util\Exception(sprintf('Directory "%s" was not created', dirname($out))); - } - $this->stream = fopen($out, 'wb'); - $this->isPhpStream = strncmp($out, 'php://', 6) !== 0; + $this->suite = $suite; } - public function write(string $buffer) : void + public function execute(): \PHPUnit\TextUI\Command\Result { - if ($this->stream) { - assert(is_resource($this->stream)); - fwrite($this->stream, $buffer); - } else { - if (PHP_SAPI !== 'cli' && PHP_SAPI !== 'phpdbg') { - $buffer = htmlspecialchars($buffer, ENT_COMPAT | ENT_SUBSTITUTE); + $buffer = $this->warnAboutConflictingOptions(); + $buffer .= 'Available test(s):' . \PHP_EOL; + foreach (new RecursiveIteratorIterator($this->suite) as $test) { + if ($test instanceof TestCase) { + $name = sprintf('%s::%s', $test::class, str_replace(' with data set ', '', $test->nameWithDataSet())); + } elseif ($test instanceof PhptTestCase) { + $name = $test->getName(); + } else { + continue; } - print $buffer; + $buffer .= sprintf(' - %s' . \PHP_EOL, $name); } + return \PHPUnit\TextUI\Command\Result::from($buffer); } - public function flush() : void + private function warnAboutConflictingOptions(): string { - if ($this->stream && $this->isPhpStream) { - assert(is_resource($this->stream)); - fclose($this->stream); + $buffer = ''; + $configuration = Registry::get(); + if ($configuration->hasFilter()) { + $buffer .= 'The --filter and --list-tests options cannot be combined, --filter is ignored' . \PHP_EOL; + } + if ($configuration->hasGroups()) { + $buffer .= 'The --group and --list-tests options cannot be combined, --group is ignored' . \PHP_EOL; + } + if ($configuration->hasExcludeGroups()) { + $buffer .= 'The --exclude-group and --list-tests options cannot be combined, --exclude-group is ignored' . \PHP_EOL; } + if (!empty($buffer)) { + $buffer .= \PHP_EOL; + } + return $buffer; } } - */ - public function publicMethodsInTestClass(ReflectionClass $class) : array - { - return $this->filterMethods($class, ReflectionMethod::IS_PUBLIC); - } - /** - * @psalm-return list - */ - public function methodsInTestClass(ReflectionClass $class) : array + private readonly string $filename; + private readonly TestSuite $suite; + public function __construct(string $filename, TestSuite $suite) { - return $this->filterMethods($class, null); + $this->filename = $filename; + $this->suite = $suite; } - /** - * @psalm-return list - */ - private function filterMethods(ReflectionClass $class, ?int $filter) : array + public function execute(): \PHPUnit\TextUI\Command\Result { - $methods = []; - // PHP <7.3.5 throw error when null is passed - // to ReflectionClass::getMethods() when strict_types is enabled. - $classMethods = $filter === null ? $class->getMethods() : $class->getMethods($filter); - foreach ($classMethods as $method) { - if ($method->getDeclaringClass()->getName() === TestCase::class) { + $buffer = $this->warnAboutConflictingOptions(); + $writer = new XMLWriter(); + $writer->openMemory(); + $writer->setIndent(\true); + $writer->startDocument(); + $writer->startElement('tests'); + $currentTestCase = null; + foreach (new RecursiveIteratorIterator($this->suite) as $test) { + if ($test instanceof TestCase) { + if ($test::class !== $currentTestCase) { + if ($currentTestCase !== null) { + $writer->endElement(); + } + $writer->startElement('testCaseClass'); + $writer->writeAttribute('name', $test::class); + $currentTestCase = $test::class; + } + $writer->startElement('testCaseMethod'); + $writer->writeAttribute('id', $test->valueObjectForEvents()->id()); + $writer->writeAttribute('name', $test->name()); + $writer->writeAttribute('groups', implode(',', $test->groups())); + /** + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/5481 + */ + if (!empty($test->dataSetAsString())) { + $writer->writeAttribute('dataSet', str_replace(' with data set ', '', $test->dataSetAsString())); + } + $writer->endElement(); continue; } - if ($method->getDeclaringClass()->getName() === Assert::class) { - continue; + if ($test instanceof PhptTestCase) { + if ($currentTestCase !== null) { + $writer->endElement(); + $currentTestCase = null; + } + $writer->startElement('phptFile'); + $writer->writeAttribute('path', $test->getName()); + $writer->endElement(); } - $methods[] = $method; } - return $methods; + if ($currentTestCase !== null) { + $writer->endElement(); + } + $writer->endElement(); + file_put_contents($this->filename, $writer->outputMemory()); + $buffer .= sprintf('Wrote list of tests that would have been run to %s' . \PHP_EOL, $this->filename); + return \PHPUnit\TextUI\Command\Result::from($buffer); + } + private function warnAboutConflictingOptions(): string + { + $buffer = ''; + $configuration = Registry::get(); + if ($configuration->hasFilter()) { + $buffer .= 'The --filter and --list-tests-xml options cannot be combined, --filter is ignored' . \PHP_EOL; + } + if ($configuration->hasGroups()) { + $buffer .= 'The --group and --list-tests-xml options cannot be combined, --group is ignored' . \PHP_EOL; + } + if ($configuration->hasExcludeGroups()) { + $buffer .= 'The --exclude-group and --list-tests-xml options cannot be combined, --exclude-group is ignored' . \PHP_EOL; + } + if (!empty($buffer)) { + $buffer .= \PHP_EOL; + } + return $buffer; } } filename = $filename; + } + public function execute(): \PHPUnit\TextUI\Command\Result + { + try { + $migrated = (new Migrator())->migrate($this->filename); + copy($this->filename, $this->filename . '.bak'); + file_put_contents($this->filename, $migrated); + return \PHPUnit\TextUI\Command\Result::from(sprintf('Created backup: %s.bak%sMigrated configuration: %s%s', $this->filename, \PHP_EOL, $this->filename, \PHP_EOL)); + } catch (Throwable $t) { + return \PHPUnit\TextUI\Command\Result::from(sprintf('Migration of %s failed:%s%s%s', $this->filename, \PHP_EOL, $t->getMessage(), \PHP_EOL), \PHPUnit\TextUI\Command\Result::FAILURE); + } } } getName()]; - } - if ($test instanceof SelfDescribing) { - return ['', $test->toString()]; - } - return ['', get_class($test)]; - } - public static function describeAsString(\PHPUnit\Framework\Test $test) : string + private readonly int $shellExitCode; + public function __construct(int $shellExitCode) { - if ($test instanceof SelfDescribing) { - return $test->toString(); - } - return get_class($test); + $this->shellExitCode = $shellExitCode; } - /** - * @throws CodeCoverageException - * - * @return array|bool - * - * @psalm-param class-string $className - */ - public static function getLinesToBeCovered(string $className, string $methodName) + public function execute(): \PHPUnit\TextUI\Command\Result { - $annotations = self::parseTestMethodAnnotations($className, $methodName); - if (!self::shouldCoversAnnotationBeUsed($annotations)) { - return \false; - } - return self::getLinesToBeCoveredOrUsed($className, $methodName, 'covers'); + return \PHPUnit\TextUI\Command\Result::from((new Help())->generate(), $this->shellExitCode); } - /** - * 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 +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\Command; + +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class ShowVersionCommand implements \PHPUnit\TextUI\Command\Command +{ + public function execute(): \PHPUnit\TextUI\Command\Result { - return self::getLinesToBeCoveredOrUsed($className, $methodName, 'uses'); + return \PHPUnit\TextUI\Command\Result::from(); } - public static function requiresCodeCoverageDataCollection(TestCase $test) : bool +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\Command; + +use function file_get_contents; +use function sprintf; +use function version_compare; +use PHPUnit\Runner\Version; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + * + * @codeCoverageIgnore + */ +final class VersionCheckCommand implements \PHPUnit\TextUI\Command\Command +{ + public function execute(): \PHPUnit\TextUI\Command\Result { - $annotations = self::parseTestMethodAnnotations(get_class($test), $test->getName(\false)); - // 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'])) { - // @see https://github.com/sebastianbergmann/phpunit/issues/4947#issuecomment-1084480950 - // return false; + $latestVersion = file_get_contents('https://phar.phpunit.de/latest-version-of/phpunit'); + $latestCompatibleVersion = file_get_contents('https://phar.phpunit.de/latest-version-of/phpunit-' . Version::majorVersionNumber()); + $notLatest = version_compare($latestVersion, Version::id(), '>'); + $notLatestCompatible = version_compare($latestCompatibleVersion, Version::id(), '>'); + if (!$notLatest && !$notLatestCompatible) { + return \PHPUnit\TextUI\Command\Result::from('You are using the latest version of PHPUnit.' . \PHP_EOL); } - // If there is at least one @covers annotation then - // code coverage data needs to be collected - if (isset($annotations['method']['covers'])) { - return \true; + $buffer = 'You are not using the latest version of PHPUnit.' . \PHP_EOL; + if ($notLatestCompatible) { + $buffer .= sprintf('The latest version compatible with PHPUnit %s is PHPUnit %s.' . \PHP_EOL, Version::id(), $latestCompatibleVersion); } - // 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'])) { - // @see https://github.com/sebastianbergmann/phpunit/issues/4947#issuecomment-1084480950 - // return false; + if ($notLatest) { + $buffer .= sprintf('The latest version is PHPUnit %s.' . \PHP_EOL, $latestVersion); } - // If there is no @coversNothing annotation then - // code coverage data may be collected - return \true; + return \PHPUnit\TextUI\Command\Result::from($buffer); } - /** - * @throws Exception - * - * @psalm-param class-string $className - */ - public static function getRequirements(string $className, string $methodName) : array +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\Command; + +use const PHP_EOL; +use function printf; +use PHPUnit\TextUI\Configuration\CodeCoverageFilterRegistry; +use PHPUnit\TextUI\Configuration\Configuration; +use PHPUnit\TextUI\Configuration\NoCoverageCacheDirectoryException; +use PHPUnitPHAR\SebastianBergmann\CodeCoverage\StaticAnalysis\CacheWarmer; +use PHPUnitPHAR\SebastianBergmann\Timer\NoActiveTimerException; +use PHPUnitPHAR\SebastianBergmann\Timer\Timer; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + * + * @codeCoverageIgnore + */ +final class WarmCodeCoverageCacheCommand implements \PHPUnit\TextUI\Command\Command +{ + private readonly Configuration $configuration; + private readonly CodeCoverageFilterRegistry $codeCoverageFilterRegistry; + public function __construct(Configuration $configuration, CodeCoverageFilterRegistry $codeCoverageFilterRegistry) { - return self::mergeArraysRecursively(Registry::getInstance()->forClassName($className)->requirements(), Registry::getInstance()->forMethod($className, $methodName)->requirements()); + $this->configuration = $configuration; + $this->codeCoverageFilterRegistry = $codeCoverageFilterRegistry; } /** - * Returns the missing requirements for a test. - * - * @throws Exception - * @throws Warning - * - * @psalm-param class-string $className + * @throws NoActiveTimerException + * @throws NoCoverageCacheDirectoryException */ - public static function getMissingRequirements(string $className, string $methodName) : array + public function execute(): \PHPUnit\TextUI\Command\Result { - $required = self::getRequirements($className, $methodName); - $missing = []; - $hint = null; - if (!empty($required['PHP'])) { - $operator = new \PHPUnit\Util\VersionComparisonOperator(empty($required['PHP']['operator']) ? '>=' : $required['PHP']['operator']); - if (!version_compare(PHP_VERSION, $required['PHP']['version'], $operator->asString())) { - $missing[] = sprintf('PHP %s %s is required.', $operator->asString(), $required['PHP']['version']); - $hint = 'PHP'; - } - } elseif (!empty($required['PHP_constraint'])) { - $version = new \PHPUnitPHAR\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 = 'PHP_constraint'; - } + if (!$this->configuration->hasCoverageCacheDirectory()) { + return \PHPUnit\TextUI\Command\Result::from('Cache for static analysis has not been configured' . PHP_EOL, \PHPUnit\TextUI\Command\Result::FAILURE); } - if (!empty($required['PHPUnit'])) { - $phpunitVersion = Version::id(); - $operator = new \PHPUnit\Util\VersionComparisonOperator(empty($required['PHPUnit']['operator']) ? '>=' : $required['PHPUnit']['operator']); - if (!version_compare($phpunitVersion, $required['PHPUnit']['version'], $operator->asString())) { - $missing[] = sprintf('PHPUnit %s %s is required.', $operator->asString(), $required['PHPUnit']['version']); - $hint = $hint ?? 'PHPUnit'; - } - } elseif (!empty($required['PHPUnit_constraint'])) { - $phpunitVersion = new \PHPUnitPHAR\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 = new \PHPUnit\Util\VersionComparisonOperator(empty($req['operator']) ? '>=' : $req['operator']); - if ($actualVersion === \false || !version_compare($actualVersion, $req['version'], $operator->asString())) { - $missing[] = sprintf('Extension %s %s %s is required.', $extension, $operator->asString(), $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 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 = null) : array - { - $registry = Registry::getInstance(); - if ($methodName !== null) { - try { - return ['method' => $registry->forMethod($className, $methodName)->symbolAnnotations(), 'class' => $registry->forClassName($className)->symbolAnnotations()]; - } catch (\PHPUnit\Util\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 - * - * @return ExecutionOrderDependency[] - */ - public static function getDependencies(string $className, string $methodName) : array - { - $annotations = self::parseTestMethodAnnotations($className, $methodName); - $dependsAnnotations = $annotations['class']['depends'] ?? []; - if (isset($annotations['method']['depends'])) { - $dependsAnnotations = array_merge($dependsAnnotations, $annotations['method']['depends']); - } - // Normalize dependency name to className::methodName - $dependencies = []; - foreach ($dependsAnnotations as $value) { - $dependencies[] = ExecutionOrderDependency::createFromDependsAnnotation($className, $value); - } - 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; - } - } - } - foreach (['method', 'class'] as $element) { - if (isset($annotations[$element]['covers'])) { - foreach ($annotations[$element]['covers'] as $coversTarget) { - $groups[] = ['__phpunit_covers_' . self::canonicalizeName($coversTarget)]; - } - } - if (isset($annotations[$element]['uses'])) { - foreach ($annotations[$element]['uses'] as $usesTarget) { - $groups[] = ['__phpunit_uses_' . self::canonicalizeName($usesTarget)]; - } - } - } - 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 \PHPUnit\Util\Reflection())->methodsInTestClass(new ReflectionClass($className)) as $method) { - $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->isToBeExecutedAsPreCondition()) { - array_unshift(self::$hookMethods[$className]['preCondition'], $method->getName()); - } - if ($docBlock->isToBeExecutedAsPostCondition()) { - self::$hookMethods[$className]['postCondition'][] = $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 (!$method->isPublic()) { - return \false; - } - 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]); - } - $codeUnits = CodeUnitCollection::fromArray([]); - $mapper = new Mapper(); - 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)); - } - try { - $codeUnits = $codeUnits->mergeWith($mapper->stringToCodeUnits($element)); - } catch (InvalidCodeUnitException $e) { - throw new InvalidCoversTargetException(sprintf('"@%s %s" is invalid', $mode, $element), $e->getCode(), $e); - } - } - return $mapper->codeUnitsToSourceLines($codeUnits); - } - private static function emptyHookMethodsArray() : array - { - return ['beforeClass' => ['setUpBeforeClass'], 'before' => ['setUp'], 'preCondition' => ['assertPreConditions'], 'postCondition' => ['assertPostConditions'], '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; - } - /** - * Trims any extensions from version string that follows after - * the .[.] 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/) - * - * @see 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; - } + $this->codeCoverageFilterRegistry->init($this->configuration, \true); + if (!$this->codeCoverageFilterRegistry->configured()) { + return \PHPUnit\TextUI\Command\Result::from('Filter for code coverage has not been configured' . PHP_EOL, \PHPUnit\TextUI\Command\Result::FAILURE); } - return $a; - } - private static function canonicalizeName(string $name) : string - { - return strtolower(trim($name, '\\')); + $timer = new Timer(); + $timer->start(); + print 'Warming cache for static analysis ... '; + (new CacheWarmer())->warmCache($this->configuration->coverageCacheDirectory(), !$this->configuration->disableCodeCoverageIgnore(), $this->configuration->ignoreDeprecatedCodeUnitsFromCodeCoverage(), $this->codeCoverageFilterRegistry->get()); + printf('[%s]%s', $timer->stop()->asString(), PHP_EOL); + return \PHPUnit\TextUI\Command\Result::from(); } } '│', '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 = [" \x1b[36m◐\x1b[0m running tests", " \x1b[36m◓\x1b[0m running tests", " \x1b[36m◑\x1b[0m running tests", " \x1b[36m◒\x1b[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 = []; - /** - * @var Timer - */ - private $timer; - /** - * @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, $verbose, $colors, $debug, $numberOfColumns, $reverse); - $this->timer = new Timer(); - $this->timer->start(); - } - public function printResult(TestResult $result) : void - { - $this->printHeader($result); - $this->printNonSuccessfulTestsSummary($result->count()); - $this->printFooter($result); - } - protected function printHeader(TestResult $result) : void - { - $this->write("\n" . (new ResourceUsageFormatter())->resourceUsage($this->timer->stop()) . "\n\n"); - } - protected function formatClassName(Test $test) : string - { - if ($test instanceof TestCase) { - return $this->prettifier->prettifyTestClass(get_class($test)); - } - return get_class($test); - } - /** - * @throws 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 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(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 = 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 + public const SUCCESS = 0; + public const FAILURE = 1; + public const EXCEPTION = 2; + public const CRASH = 255; + private readonly string $output; + private readonly int $shellExitCode; + public static function from(string $output = '', int $shellExitCode = self::SUCCESS): self { - if ($this->colors) { - $id = $this->spinState % count(self::SPINNER_ICONS); - $this->write(self::SPINNER_ICONS[$id]); - } + return new self($output, $shellExitCode); } - protected function undrawSpinner() : void + private function __construct(string $output, int $shellExitCode) { - if ($this->colors) { - $id = $this->spinState % count(self::SPINNER_ICONS); - $this->write("\x1b[1K\x1b[" . strlen(self::SPINNER_ICONS[$id]) . 'D'); - } + $this->output = $output; + $this->shellExitCode = $shellExitCode; } - private function formatRuntime(float $time, string $color = '') : string + public function output(): 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')); + return $this->output; } - private function printNonSuccessfulTestsSummary(int $numberOfExecutedTests) : void + public function shellExitCode(): int { - 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; - } + return $this->shellExitCode; } } - - - - Test Documentation - - - -EOT; - /** - * @var string - */ - private const CLASS_HEADER = <<<'EOT' - -

    %s

    -
      - -EOT; - /** - * @var string - */ - private const CLASS_FOOTER = <<<'EOT' -
    -EOT; - /** - * @var string - */ - private const PAGE_FOOTER = <<<'EOT' - - - -EOT; - public function printResult(TestResult $result) : void - { - } - /** - * 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, $this->currentTestClassPrettified)); - } - /** - * Handler for 'on test' event. - */ - protected function onTest(string $name, bool $success = \true) : void - { - $this->write(sprintf("
  • %s
  • \n", $success ? 'success' : 'defect', $name)); - } - /** - * Handler for 'end class' event. - */ - protected function endClass(string $name) : void - { - $this->write(self::CLASS_FOOTER); - } - /** - * Handler for 'end run' event. + * @throws ConfigurationCannotBeBuiltException */ - protected function endRun() : void + public function build(array $argv): \PHPUnit\TextUI\Configuration\Configuration { - $this->write(self::PAGE_FOOTER); + try { + $cliConfiguration = (new CliConfigurationBuilder())->fromParameters($argv); + $configurationFile = (new XmlConfigurationFileFinder())->find($cliConfiguration); + $xmlConfiguration = DefaultConfiguration::create(); + if ($configurationFile !== \false) { + $xmlConfiguration = (new Loader())->load($configurationFile); + } + return \PHPUnit\TextUI\Configuration\Registry::init($cliConfiguration, $xmlConfiguration); + } catch (CliConfigurationException|XmlConfigurationException $e) { + throw new \PHPUnit\TextUI\Configuration\ConfigurationCannotBeBuiltException($e->getMessage(), $e->getCode(), $e); + } } } */ - private $useColor; - public function __construct(bool $useColor = \false) - { - $this->useColor = $useColor; - } + private array $processed = []; /** - * Prettifies the name of a test class. - * - * @psalm-param class-string $className + * @throws Exception */ - public function prettifyTestClass(string $className) : string + public function fromParameters(array $parameters): \PHPUnit\TextUI\CliArguments\Configuration { try { - $annotations = Test::parseTestMethodAnnotations($className); - if (isset($annotations['class']['testdox'][0])) { - return $annotations['class']['testdox'][0]; - } - } catch (UtilException $e) { - // ignore, determine className by parsing the provided name - } - $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($className)) { - $className = 'UnnamedTests'; - } - if (!empty($parts)) { - $parts[] = $className; - $fullyQualifiedName = implode('\\', $parts); - } else { - $fullyQualifiedName = $className; - } - $result = preg_replace('/(?<=[[:lower:]])(?=[[:upper:]])/u', ' ', $className); - if ($fullyQualifiedName !== $className) { - return $result . ' (' . $fullyQualifiedName . ')'; - } - return $result; - } - /** - * @throws InvalidArgumentException - */ - public function prettifyTestCase(TestCase $test) : string - { - $annotations = Test::parseTestMethodAnnotations(get_class($test), $test->getName(\false)); - $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((string) $test->dataName())); - } - return $data; - } - /** - * Prettifies the name of a test method. - */ - public function prettifyTestMethod(string $name) : string - { - $buffer = ''; - if ($name === '') { - return $buffer; + $options = (new CliParser())->parse($parameters, self::SHORT_OPTIONS, self::LONG_OPTIONS); + } catch (CliParserException $e) { + throw new \PHPUnit\TextUI\CliArguments\Exception($e->getMessage(), $e->getCode(), $e); } - $string = (string) preg_replace('#\\d+$#', '', $name, -1, $count); - if (in_array($string, $this->strings, \true)) { - $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; + $atLeastVersion = null; + $backupGlobals = null; + $backupStaticProperties = null; + $beStrictAboutChangesToGlobalState = null; + $bootstrap = null; + $cacheDirectory = null; + $cacheResult = null; + $cacheResultFile = null; + $checkVersion = \false; + $colors = null; + $columns = null; + $configuration = null; + $coverageCacheDirectory = null; + $warmCoverageCache = \false; + $coverageFilter = null; + $coverageClover = null; + $coverageCobertura = null; + $coverageCrap4J = null; + $coverageHtml = null; + $coveragePhp = null; + $coverageText = null; + $coverageTextShowUncoveredFiles = null; + $coverageTextShowOnlySummary = null; + $coverageXml = null; + $pathCoverage = null; + $defaultTimeLimit = null; + $disableCodeCoverageIgnore = null; + $disallowTestOutput = null; + $displayIncomplete = null; + $displaySkipped = null; + $displayDeprecations = null; + $displayErrors = null; + $displayNotices = null; + $displayWarnings = null; + $enforceTimeLimit = null; + $excludeGroups = null; + $executionOrder = null; + $executionOrderDefects = null; + $failOnDeprecation = null; + $failOnEmptyTestSuite = null; + $failOnIncomplete = null; + $failOnNotice = null; + $failOnRisky = null; + $failOnSkipped = null; + $failOnWarning = null; + $stopOnDefect = null; + $stopOnDeprecation = null; + $stopOnError = null; + $stopOnFailure = null; + $stopOnIncomplete = null; + $stopOnNotice = null; + $stopOnRisky = null; + $stopOnSkipped = null; + $stopOnWarning = null; + $filter = null; + $generateBaseline = null; + $useBaseline = null; + $ignoreBaseline = \false; + $generateConfiguration = \false; + $migrateConfiguration = \false; + $groups = null; + $testsCovering = null; + $testsUsing = null; + $help = \false; + $includePath = null; + $iniSettings = []; + $junitLogfile = null; + $listGroups = \false; + $listSuites = \false; + $listTests = \false; + $listTestsXml = null; + $noCoverage = null; + $noExtensions = null; + $noOutput = null; + $noProgress = null; + $noResults = null; + $noLogging = null; + $processIsolation = null; + $randomOrderSeed = null; + $reportUselessTests = null; + $resolveDependencies = null; + $reverseList = null; + $stderr = null; + $strictCoverage = null; + $teamcityLogfile = null; + $testdoxHtmlFile = null; + $testdoxTextFile = null; + $testSuffixes = null; + $testSuite = null; + $excludeTestSuite = null; + $useDefaultConfiguration = \true; + $version = \false; + $logEventsText = null; + $logEventsVerboseText = null; + $printerTeamCity = null; + $printerTestDox = null; + $debug = \false; + foreach ($options[0] as $option) { + $optionAllowedMultipleTimes = \false; + switch ($option[0]) { + case '--colors': + $colors = $option[1] ?: \PHPUnit\TextUI\Configuration\Configuration::COLOR_AUTO; + break; + case '--bootstrap': + $bootstrap = $option[1]; + break; + case '--cache-directory': + $cacheDirectory = $option[1]; + break; + case '--cache-result': + $cacheResult = \true; + break; + case '--do-not-cache-result': + $cacheResult = \false; + break; + case '--cache-result-file': + $cacheResultFile = $option[1]; + break; + case '--columns': + if (is_numeric($option[1])) { + $columns = (int) $option[1]; + } elseif ($option[1] === 'max') { + $columns = 'max'; + } + break; + case 'c': + case '--configuration': + $configuration = $option[1]; + break; + case '--coverage-cache': + $coverageCacheDirectory = $option[1]; + break; + case '--warm-coverage-cache': + $warmCoverageCache = \true; + break; + case '--coverage-clover': + $coverageClover = $option[1]; + break; + case '--coverage-cobertura': + $coverageCobertura = $option[1]; + break; + case '--coverage-crap4j': + $coverageCrap4J = $option[1]; + break; + case '--coverage-html': + $coverageHtml = $option[1]; + break; + case '--coverage-php': + $coveragePhp = $option[1]; + break; + case '--coverage-text': + if ($option[1] === null) { + $option[1] = 'php://stdout'; + } + $coverageText = $option[1]; + break; + case '--only-summary-for-coverage-text': + $coverageTextShowOnlySummary = \true; + break; + case '--show-uncovered-for-coverage-text': + $coverageTextShowUncoveredFiles = \true; + break; + case '--coverage-xml': + $coverageXml = $option[1]; + break; + case '--path-coverage': + $pathCoverage = \true; + break; + case 'd': + $tmp = explode('=', $option[1]); + if (isset($tmp[0])) { + if (isset($tmp[1])) { + $iniSettings[$tmp[0]] = $tmp[1]; + } else { + $iniSettings[$tmp[0]] = '1'; + } + } + $optionAllowedMultipleTimes = \true; + break; + case 'h': + case '--help': + $help = \true; + break; + case '--filter': + $filter = $option[1]; + break; + case '--testsuite': + $testSuite = $option[1]; + break; + case '--exclude-testsuite': + $excludeTestSuite = $option[1]; + break; + case '--generate-baseline': + $generateBaseline = $option[1]; + if (basename($generateBaseline) === $generateBaseline) { + $generateBaseline = getcwd() . \DIRECTORY_SEPARATOR . $generateBaseline; + } + break; + case '--use-baseline': + $useBaseline = $option[1]; + if (basename($useBaseline) === $useBaseline && !is_file($useBaseline)) { + $useBaseline = getcwd() . \DIRECTORY_SEPARATOR . $useBaseline; + } + break; + case '--ignore-baseline': + $ignoreBaseline = \true; + break; + case '--generate-configuration': + $generateConfiguration = \true; + break; + case '--migrate-configuration': + $migrateConfiguration = \true; + break; + case '--group': + $groups = explode(',', $option[1]); + break; + case '--exclude-group': + $excludeGroups = explode(',', $option[1]); + break; + case '--covers': + $testsCovering = array_map('strtolower', explode(',', $option[1])); + break; + case '--uses': + $testsUsing = array_map('strtolower', explode(',', $option[1])); + break; + case '--test-suffix': + $testSuffixes = explode(',', $option[1]); + break; + case '--include-path': + $includePath = $option[1]; + break; + case '--list-groups': + $listGroups = \true; + break; + case '--list-suites': + $listSuites = \true; + break; + case '--list-tests': + $listTests = \true; + break; + case '--list-tests-xml': + $listTestsXml = $option[1]; + break; + case '--log-junit': + $junitLogfile = $option[1]; + break; + case '--log-teamcity': + $teamcityLogfile = $option[1]; + break; + case '--order-by': + foreach (explode(',', $option[1]) as $order) { + switch ($order) { + case 'default': + $executionOrder = TestSuiteSorter::ORDER_DEFAULT; + $executionOrderDefects = TestSuiteSorter::ORDER_DEFAULT; + $resolveDependencies = \true; + break; + case 'defects': + $executionOrderDefects = TestSuiteSorter::ORDER_DEFECTS_FIRST; + break; + case 'depends': + $resolveDependencies = \true; + break; + case 'duration': + $executionOrder = TestSuiteSorter::ORDER_DURATION; + break; + case 'no-depends': + $resolveDependencies = \false; + break; + case 'random': + $executionOrder = TestSuiteSorter::ORDER_RANDOMIZED; + break; + case 'reverse': + $executionOrder = TestSuiteSorter::ORDER_REVERSED; + break; + case 'size': + $executionOrder = TestSuiteSorter::ORDER_SIZE; + break; + default: + throw new \PHPUnit\TextUI\CliArguments\Exception(sprintf('unrecognized --order-by option: %s', $order)); + } + } + break; + case '--process-isolation': + $processIsolation = \true; + break; + case '--stderr': + $stderr = \true; + break; + case '--fail-on-deprecation': + $failOnDeprecation = \true; + break; + case '--fail-on-empty-test-suite': + $failOnEmptyTestSuite = \true; + break; + case '--fail-on-incomplete': + $failOnIncomplete = \true; + break; + case '--fail-on-notice': + $failOnNotice = \true; + break; + case '--fail-on-risky': + $failOnRisky = \true; + break; + case '--fail-on-skipped': + $failOnSkipped = \true; + break; + case '--fail-on-warning': + $failOnWarning = \true; + break; + case '--stop-on-defect': + $stopOnDefect = \true; + break; + case '--stop-on-deprecation': + $stopOnDeprecation = \true; + break; + case '--stop-on-error': + $stopOnError = \true; + break; + case '--stop-on-failure': + $stopOnFailure = \true; + break; + case '--stop-on-incomplete': + $stopOnIncomplete = \true; + break; + case '--stop-on-notice': + $stopOnNotice = \true; + break; + case '--stop-on-risky': + $stopOnRisky = \true; + break; + case '--stop-on-skipped': + $stopOnSkipped = \true; + break; + case '--stop-on-warning': + $stopOnWarning = \true; + break; + case '--teamcity': + $printerTeamCity = \true; + break; + case '--testdox': + $printerTestDox = \true; + break; + case '--testdox-html': + $testdoxHtmlFile = $option[1]; + break; + case '--testdox-text': + $testdoxTextFile = $option[1]; + break; + case '--no-configuration': + $useDefaultConfiguration = \false; + break; + case '--no-extensions': + $noExtensions = \true; + break; + case '--no-coverage': + $noCoverage = \true; + break; + case '--no-logging': + $noLogging = \true; + break; + case '--no-output': + $noOutput = \true; + break; + case '--no-progress': + $noProgress = \true; + break; + case '--no-results': + $noResults = \true; + break; + case '--globals-backup': + $backupGlobals = \true; + break; + case '--static-backup': + $backupStaticProperties = \true; + break; + case '--atleast-version': + $atLeastVersion = $option[1]; + break; + case '--version': + $version = \true; + break; + case '--dont-report-useless-tests': + $reportUselessTests = \false; + break; + case '--strict-coverage': + $strictCoverage = \true; + break; + case '--disable-coverage-ignore': + $disableCodeCoverageIgnore = \true; + break; + case '--strict-global-state': + $beStrictAboutChangesToGlobalState = \true; + break; + case '--disallow-test-output': + $disallowTestOutput = \true; + break; + case '--display-incomplete': + $displayIncomplete = \true; + break; + case '--display-skipped': + $displaySkipped = \true; + break; + case '--display-deprecations': + $displayDeprecations = \true; + break; + case '--display-errors': + $displayErrors = \true; + break; + case '--display-notices': + $displayNotices = \true; + break; + case '--display-warnings': + $displayWarnings = \true; + break; + case '--default-time-limit': + $defaultTimeLimit = (int) $option[1]; + break; + case '--enforce-time-limit': + $enforceTimeLimit = \true; + break; + case '--reverse-list': + $reverseList = \true; + break; + case '--check-version': + $checkVersion = \true; + break; + case '--coverage-filter': + if ($coverageFilter === null) { + $coverageFilter = []; + } + $coverageFilter[] = $option[1]; + break; + case '--random-order': + $executionOrder = TestSuiteSorter::ORDER_RANDOMIZED; + break; + case '--random-order-seed': + $randomOrderSeed = (int) $option[1]; + break; + case '--resolve-dependencies': + $resolveDependencies = \true; + break; + case '--ignore-dependencies': + $resolveDependencies = \false; + break; + case '--reverse-order': + $executionOrder = TestSuiteSorter::ORDER_REVERSED; + break; + case '--log-events-text': + $logEventsText = Filesystem::resolveStreamOrFile($option[1]); + if ($logEventsText === \false) { + throw new \PHPUnit\TextUI\CliArguments\Exception(sprintf('The path "%s" specified for the --log-events-text option could not be resolved', $option[1])); + } + break; + case '--log-events-verbose-text': + $logEventsVerboseText = Filesystem::resolveStreamOrFile($option[1]); + if ($logEventsVerboseText === \false) { + throw new \PHPUnit\TextUI\CliArguments\Exception(sprintf('The path "%s" specified for the --log-events-verbose-text option could not be resolved', $option[1])); + } + break; + case '--debug': + $debug = \true; + break; + } + if (!$optionAllowedMultipleTimes) { + $this->markProcessed($option[0]); + } } - $name[0] = strtoupper($name[0]); - if (strpos($name, '_') !== \false) { - return trim(str_replace('_', ' ', $name)); + if (empty($iniSettings)) { + $iniSettings = null; } - $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]; - } + if (empty($coverageFilter)) { + $coverageFilter = null; } - return $buffer; + return new \PHPUnit\TextUI\CliArguments\Configuration($options[1], $atLeastVersion, $backupGlobals, $backupStaticProperties, $beStrictAboutChangesToGlobalState, $bootstrap, $cacheDirectory, $cacheResult, $cacheResultFile, $checkVersion, $colors, $columns, $configuration, $coverageClover, $coverageCobertura, $coverageCrap4J, $coverageHtml, $coveragePhp, $coverageText, $coverageTextShowUncoveredFiles, $coverageTextShowOnlySummary, $coverageXml, $pathCoverage, $coverageCacheDirectory, $warmCoverageCache, $defaultTimeLimit, $disableCodeCoverageIgnore, $disallowTestOutput, $enforceTimeLimit, $excludeGroups, $executionOrder, $executionOrderDefects, $failOnDeprecation, $failOnEmptyTestSuite, $failOnIncomplete, $failOnNotice, $failOnRisky, $failOnSkipped, $failOnWarning, $stopOnDefect, $stopOnDeprecation, $stopOnError, $stopOnFailure, $stopOnIncomplete, $stopOnNotice, $stopOnRisky, $stopOnSkipped, $stopOnWarning, $filter, $generateBaseline, $useBaseline, $ignoreBaseline, $generateConfiguration, $migrateConfiguration, $groups, $testsCovering, $testsUsing, $help, $includePath, $iniSettings, $junitLogfile, $listGroups, $listSuites, $listTests, $listTestsXml, $noCoverage, $noExtensions, $noOutput, $noProgress, $noResults, $noLogging, $processIsolation, $randomOrderSeed, $reportUselessTests, $resolveDependencies, $reverseList, $stderr, $strictCoverage, $teamcityLogfile, $testdoxHtmlFile, $testdoxTextFile, $testSuffixes, $testSuite, $excludeTestSuite, $useDefaultConfiguration, $displayIncomplete, $displaySkipped, $displayDeprecations, $displayErrors, $displayNotices, $displayWarnings, $version, $coverageFilter, $logEventsText, $logEventsVerboseText, $printerTeamCity, $printerTestDox, $debug); } /** - * @throws InvalidArgumentException + * @psalm-param non-empty-string $option */ - private function mapTestMethodParameterNamesToProvidedDataValues(TestCase $test) : array + private function markProcessed(string $option): void { - try { - $reflector = new ReflectionMethod(get_class($test), $test->getName(\false)); - // @codeCoverageIgnoreStart - } catch (ReflectionException $e) { - throw new UtilException($e->getMessage(), $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd - $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(); - // @codeCoverageIgnoreStart - } catch (ReflectionException $e) { - throw new UtilException($e->getMessage(), $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd - } - $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 (!isset($this->processed[$option])) { + $this->processed[$option] = 1; + return; } - if ($this->useColor) { - $providedData = array_map(static function ($value) { - return Color::colorize('fg-cyan', Color::visualizeWhitespace((string) $value, \true)); - }, $providedData); + $this->processed[$option]++; + if ($this->processed[$option] === 2) { + EventFacade::emitter()->testRunnerTriggeredWarning(sprintf('Option %s cannot be used more than once', $option)); } - return $providedData; } } groups = $groups; - $this->excludeGroups = $excludeGroups; - $this->prettifier = new \PHPUnit\Util\TestDox\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 + * @psalm-var list + */ + private readonly array $arguments; + private readonly ?string $atLeastVersion; + private readonly ?bool $backupGlobals; + private readonly ?bool $backupStaticProperties; + private readonly ?bool $beStrictAboutChangesToGlobalState; + private readonly ?string $bootstrap; + private readonly ?string $cacheDirectory; + private readonly ?bool $cacheResult; + private readonly ?string $cacheResultFile; + private readonly bool $checkVersion; + private readonly ?string $colors; + private readonly null|int|string $columns; + private readonly ?string $configurationFile; + private readonly ?array $coverageFilter; + private readonly ?string $coverageClover; + private readonly ?string $coverageCobertura; + private readonly ?string $coverageCrap4J; + private readonly ?string $coverageHtml; + private readonly ?string $coveragePhp; + private readonly ?string $coverageText; + private readonly ?bool $coverageTextShowUncoveredFiles; + private readonly ?bool $coverageTextShowOnlySummary; + private readonly ?string $coverageXml; + private readonly ?bool $pathCoverage; + private readonly ?string $coverageCacheDirectory; + private readonly bool $warmCoverageCache; + private readonly ?int $defaultTimeLimit; + private readonly ?bool $disableCodeCoverageIgnore; + private readonly ?bool $disallowTestOutput; + private readonly ?bool $enforceTimeLimit; + private readonly ?array $excludeGroups; + private readonly ?int $executionOrder; + private readonly ?int $executionOrderDefects; + private readonly ?bool $failOnDeprecation; + private readonly ?bool $failOnEmptyTestSuite; + private readonly ?bool $failOnIncomplete; + private readonly ?bool $failOnNotice; + private readonly ?bool $failOnRisky; + private readonly ?bool $failOnSkipped; + private readonly ?bool $failOnWarning; + private readonly ?bool $stopOnDefect; + private readonly ?bool $stopOnDeprecation; + private readonly ?bool $stopOnError; + private readonly ?bool $stopOnFailure; + private readonly ?bool $stopOnIncomplete; + private readonly ?bool $stopOnNotice; + private readonly ?bool $stopOnRisky; + private readonly ?bool $stopOnSkipped; + private readonly ?bool $stopOnWarning; + private readonly ?string $filter; + private readonly ?string $generateBaseline; + private readonly ?string $useBaseline; + private readonly bool $ignoreBaseline; + private readonly bool $generateConfiguration; + private readonly bool $migrateConfiguration; + private readonly ?array $groups; + private readonly ?array $testsCovering; + private readonly ?array $testsUsing; + private readonly bool $help; + private readonly ?string $includePath; + private readonly ?array $iniSettings; + private readonly ?string $junitLogfile; + private readonly bool $listGroups; + private readonly bool $listSuites; + private readonly bool $listTests; + private readonly ?string $listTestsXml; + private readonly ?bool $noCoverage; + private readonly ?bool $noExtensions; + private readonly ?bool $noOutput; + private readonly ?bool $noProgress; + private readonly ?bool $noResults; + private readonly ?bool $noLogging; + private readonly ?bool $processIsolation; + private readonly ?int $randomOrderSeed; + private readonly ?bool $reportUselessTests; + private readonly ?bool $resolveDependencies; + private readonly ?bool $reverseList; + private readonly ?bool $stderr; + private readonly ?bool $strictCoverage; + private readonly ?string $teamcityLogfile; + private readonly ?bool $teamCityPrinter; + private readonly ?string $testdoxHtmlFile; + private readonly ?string $testdoxTextFile; + private readonly ?bool $testdoxPrinter; + /** + * @psalm-var ?non-empty-list + */ + private readonly ?array $testSuffixes; + private readonly ?string $testSuite; + private readonly ?string $excludeTestSuite; + private readonly bool $useDefaultConfiguration; + private readonly ?bool $displayDetailsOnIncompleteTests; + private readonly ?bool $displayDetailsOnSkippedTests; + private readonly ?bool $displayDetailsOnTestsThatTriggerDeprecations; + private readonly ?bool $displayDetailsOnTestsThatTriggerErrors; + private readonly ?bool $displayDetailsOnTestsThatTriggerNotices; + private readonly ?bool $displayDetailsOnTestsThatTriggerWarnings; + private readonly bool $version; + private readonly ?string $logEventsText; + private readonly ?string $logEventsVerboseText; + private readonly bool $debug; + /** + * @psalm-param list $arguments + * @psalm-param ?non-empty-list $testSuffixes + */ + public function __construct(array $arguments, ?string $atLeastVersion, ?bool $backupGlobals, ?bool $backupStaticProperties, ?bool $beStrictAboutChangesToGlobalState, ?string $bootstrap, ?string $cacheDirectory, ?bool $cacheResult, ?string $cacheResultFile, bool $checkVersion, ?string $colors, null|int|string $columns, ?string $configurationFile, ?string $coverageClover, ?string $coverageCobertura, ?string $coverageCrap4J, ?string $coverageHtml, ?string $coveragePhp, ?string $coverageText, ?bool $coverageTextShowUncoveredFiles, ?bool $coverageTextShowOnlySummary, ?string $coverageXml, ?bool $pathCoverage, ?string $coverageCacheDirectory, bool $warmCoverageCache, ?int $defaultTimeLimit, ?bool $disableCodeCoverageIgnore, ?bool $disallowTestOutput, ?bool $enforceTimeLimit, ?array $excludeGroups, ?int $executionOrder, ?int $executionOrderDefects, ?bool $failOnDeprecation, ?bool $failOnEmptyTestSuite, ?bool $failOnIncomplete, ?bool $failOnNotice, ?bool $failOnRisky, ?bool $failOnSkipped, ?bool $failOnWarning, ?bool $stopOnDefect, ?bool $stopOnDeprecation, ?bool $stopOnError, ?bool $stopOnFailure, ?bool $stopOnIncomplete, ?bool $stopOnNotice, ?bool $stopOnRisky, ?bool $stopOnSkipped, ?bool $stopOnWarning, ?string $filter, ?string $generateBaseline, ?string $useBaseline, bool $ignoreBaseline, bool $generateConfiguration, bool $migrateConfiguration, ?array $groups, ?array $testsCovering, ?array $testsUsing, bool $help, ?string $includePath, ?array $iniSettings, ?string $junitLogfile, bool $listGroups, bool $listSuites, bool $listTests, ?string $listTestsXml, ?bool $noCoverage, ?bool $noExtensions, ?bool $noOutput, ?bool $noProgress, ?bool $noResults, ?bool $noLogging, ?bool $processIsolation, ?int $randomOrderSeed, ?bool $reportUselessTests, ?bool $resolveDependencies, ?bool $reverseList, ?bool $stderr, ?bool $strictCoverage, ?string $teamcityLogfile, ?string $testdoxHtmlFile, ?string $testdoxTextFile, ?array $testSuffixes, ?string $testSuite, ?string $excludeTestSuite, bool $useDefaultConfiguration, ?bool $displayDetailsOnIncompleteTests, ?bool $displayDetailsOnSkippedTests, ?bool $displayDetailsOnTestsThatTriggerDeprecations, ?bool $displayDetailsOnTestsThatTriggerErrors, ?bool $displayDetailsOnTestsThatTriggerNotices, ?bool $displayDetailsOnTestsThatTriggerWarnings, bool $version, ?array $coverageFilter, ?string $logEventsText, ?string $logEventsVerboseText, ?bool $printerTeamCity, ?bool $printerTestDox, bool $debug) { - if (!$this->isOfInterest($test)) { - return; - } - $this->testStatus = BaseTestRunner::STATUS_FAILURE; - $this->failed++; + $this->arguments = $arguments; + $this->atLeastVersion = $atLeastVersion; + $this->backupGlobals = $backupGlobals; + $this->backupStaticProperties = $backupStaticProperties; + $this->beStrictAboutChangesToGlobalState = $beStrictAboutChangesToGlobalState; + $this->bootstrap = $bootstrap; + $this->cacheDirectory = $cacheDirectory; + $this->cacheResult = $cacheResult; + $this->cacheResultFile = $cacheResultFile; + $this->checkVersion = $checkVersion; + $this->colors = $colors; + $this->columns = $columns; + $this->configurationFile = $configurationFile; + $this->coverageFilter = $coverageFilter; + $this->coverageClover = $coverageClover; + $this->coverageCobertura = $coverageCobertura; + $this->coverageCrap4J = $coverageCrap4J; + $this->coverageHtml = $coverageHtml; + $this->coveragePhp = $coveragePhp; + $this->coverageText = $coverageText; + $this->coverageTextShowUncoveredFiles = $coverageTextShowUncoveredFiles; + $this->coverageTextShowOnlySummary = $coverageTextShowOnlySummary; + $this->coverageXml = $coverageXml; + $this->pathCoverage = $pathCoverage; + $this->coverageCacheDirectory = $coverageCacheDirectory; + $this->warmCoverageCache = $warmCoverageCache; + $this->defaultTimeLimit = $defaultTimeLimit; + $this->disableCodeCoverageIgnore = $disableCodeCoverageIgnore; + $this->disallowTestOutput = $disallowTestOutput; + $this->enforceTimeLimit = $enforceTimeLimit; + $this->excludeGroups = $excludeGroups; + $this->executionOrder = $executionOrder; + $this->executionOrderDefects = $executionOrderDefects; + $this->failOnDeprecation = $failOnDeprecation; + $this->failOnEmptyTestSuite = $failOnEmptyTestSuite; + $this->failOnIncomplete = $failOnIncomplete; + $this->failOnNotice = $failOnNotice; + $this->failOnRisky = $failOnRisky; + $this->failOnSkipped = $failOnSkipped; + $this->failOnWarning = $failOnWarning; + $this->stopOnDefect = $stopOnDefect; + $this->stopOnDeprecation = $stopOnDeprecation; + $this->stopOnError = $stopOnError; + $this->stopOnFailure = $stopOnFailure; + $this->stopOnIncomplete = $stopOnIncomplete; + $this->stopOnNotice = $stopOnNotice; + $this->stopOnRisky = $stopOnRisky; + $this->stopOnSkipped = $stopOnSkipped; + $this->stopOnWarning = $stopOnWarning; + $this->filter = $filter; + $this->generateBaseline = $generateBaseline; + $this->useBaseline = $useBaseline; + $this->ignoreBaseline = $ignoreBaseline; + $this->generateConfiguration = $generateConfiguration; + $this->migrateConfiguration = $migrateConfiguration; + $this->groups = $groups; + $this->testsCovering = $testsCovering; + $this->testsUsing = $testsUsing; + $this->help = $help; + $this->includePath = $includePath; + $this->iniSettings = $iniSettings; + $this->junitLogfile = $junitLogfile; + $this->listGroups = $listGroups; + $this->listSuites = $listSuites; + $this->listTests = $listTests; + $this->listTestsXml = $listTestsXml; + $this->noCoverage = $noCoverage; + $this->noExtensions = $noExtensions; + $this->noOutput = $noOutput; + $this->noProgress = $noProgress; + $this->noResults = $noResults; + $this->noLogging = $noLogging; + $this->processIsolation = $processIsolation; + $this->randomOrderSeed = $randomOrderSeed; + $this->reportUselessTests = $reportUselessTests; + $this->resolveDependencies = $resolveDependencies; + $this->reverseList = $reverseList; + $this->stderr = $stderr; + $this->strictCoverage = $strictCoverage; + $this->teamcityLogfile = $teamcityLogfile; + $this->testdoxHtmlFile = $testdoxHtmlFile; + $this->testdoxTextFile = $testdoxTextFile; + $this->testSuffixes = $testSuffixes; + $this->testSuite = $testSuite; + $this->excludeTestSuite = $excludeTestSuite; + $this->useDefaultConfiguration = $useDefaultConfiguration; + $this->displayDetailsOnIncompleteTests = $displayDetailsOnIncompleteTests; + $this->displayDetailsOnSkippedTests = $displayDetailsOnSkippedTests; + $this->displayDetailsOnTestsThatTriggerDeprecations = $displayDetailsOnTestsThatTriggerDeprecations; + $this->displayDetailsOnTestsThatTriggerErrors = $displayDetailsOnTestsThatTriggerErrors; + $this->displayDetailsOnTestsThatTriggerNotices = $displayDetailsOnTestsThatTriggerNotices; + $this->displayDetailsOnTestsThatTriggerWarnings = $displayDetailsOnTestsThatTriggerWarnings; + $this->version = $version; + $this->logEventsText = $logEventsText; + $this->logEventsVerboseText = $logEventsVerboseText; + $this->teamCityPrinter = $printerTeamCity; + $this->testdoxPrinter = $printerTestDox; + $this->debug = $debug; } /** - * Incomplete test. + * @psalm-return list */ - public function addIncompleteTest(Test $test, Throwable $t, float $time) : void + public function arguments(): array { - if (!$this->isOfInterest($test)) { - return; - } - $this->testStatus = BaseTestRunner::STATUS_INCOMPLETE; - $this->incomplete++; + return $this->arguments; } /** - * Risky test. + * @psalm-assert-if-true !null $this->atLeastVersion */ - public function addRiskyTest(Test $test, Throwable $t, float $time) : void + public function hasAtLeastVersion(): bool { - if (!$this->isOfInterest($test)) { - return; - } - $this->testStatus = BaseTestRunner::STATUS_RISKY; - $this->risky++; + return $this->atLeastVersion !== null; } /** - * Skipped test. + * @throws Exception */ - public function addSkippedTest(Test $test, Throwable $t, float $time) : void + public function atLeastVersion(): string { - if (!$this->isOfInterest($test)) { - return; + if (!$this->hasAtLeastVersion()) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); } - $this->testStatus = BaseTestRunner::STATUS_SKIPPED; - $this->skipped++; + return $this->atLeastVersion; } /** - * A testsuite started. + * @psalm-assert-if-true !null $this->backupGlobals */ - public function startTestSuite(TestSuite $suite) : void + public function hasBackupGlobals(): bool { + return $this->backupGlobals !== null; } /** - * A testsuite ended. + * @throws Exception */ - public function endTestSuite(TestSuite $suite) : void + public function backupGlobals(): bool { + if (!$this->hasBackupGlobals()) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->backupGlobals; } /** - * A test started. - * - * @throws InvalidArgumentException + * @psalm-assert-if-true !null $this->backupStaticProperties */ - public function startTest(Test $test) : void + public function hasBackupStaticProperties(): bool { - 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; + return $this->backupStaticProperties !== null; } /** - * A test ended. + * @throws Exception */ - public function endTest(Test $test, float $time) : void + public function backupStaticProperties(): bool { - if (!$this->isOfInterest($test)) { - return; + if (!$this->hasBackupStaticProperties()) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); } - $this->tests[] = [$this->currentTestMethodPrettified, $this->testStatus]; - $this->currentTestClassPrettified = null; - $this->currentTestMethodPrettified = null; + return $this->backupStaticProperties; + } + /** + * @psalm-assert-if-true !null $this->beStrictAboutChangesToGlobalState + */ + public function hasBeStrictAboutChangesToGlobalState(): bool + { + return $this->beStrictAboutChangesToGlobalState !== null; } - protected function doEndClass() : void + /** + * @throws Exception + */ + public function beStrictAboutChangesToGlobalState(): bool { - foreach ($this->tests as $test) { - $this->onTest($test[0], $test[1] === BaseTestRunner::STATUS_PASSED); + if (!$this->hasBeStrictAboutChangesToGlobalState()) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); } - $this->endClass($this->testClass); + return $this->beStrictAboutChangesToGlobalState; } /** - * Handler for 'start run' event. + * @psalm-assert-if-true !null $this->bootstrap */ - protected function startRun() : void + public function hasBootstrap(): bool { + return $this->bootstrap !== null; } /** - * Handler for 'start class' event. + * @throws Exception */ - protected function startClass(string $name) : void + public function bootstrap(): string { + if (!$this->hasBootstrap()) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->bootstrap; } /** - * Handler for 'on test' event. + * @psalm-assert-if-true !null $this->cacheDirectory */ - protected function onTest(string $name, bool $success = \true) : void + public function hasCacheDirectory(): bool { + return $this->cacheDirectory !== null; } /** - * Handler for 'end class' event. + * @throws Exception */ - protected function endClass(string $name) : void + public function cacheDirectory(): string { + if (!$this->hasCacheDirectory()) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->cacheDirectory; } /** - * Handler for 'end run' event. + * @psalm-assert-if-true !null $this->cacheResult */ - protected function endRun() : void + public function hasCacheResult(): bool { + return $this->cacheResult !== null; } - private function isOfInterest(Test $test) : bool + /** + * @throws Exception + */ + public function cacheResult(): bool { - if (!$test instanceof TestCase) { - return \false; - } - if ($test instanceof ErrorTestCase || $test instanceof WarningTestCase) { - return \false; - } - if (!empty($this->groups)) { - foreach ($test->getGroups() as $group) { - if (in_array($group, $this->groups, \true)) { - return \true; - } - } - return \false; - } - if (!empty($this->excludeGroups)) { - foreach ($test->getGroups() as $group) { - if (in_array($group, $this->excludeGroups, \true)) { - return \false; - } - } - return \true; + if (!$this->hasCacheResult()) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); } - return \true; + return $this->cacheResult; } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Util\TestDox; - -use const PHP_EOL; -use function array_map; -use function get_class; -use function implode; -use function method_exists; -use function preg_split; -use function trim; -use PHPUnit\Framework\AssertionFailedError; -use PHPUnit\Framework\Exception; -use PHPUnit\Framework\Reorderable; -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\Runner\BaseTestRunner; -use PHPUnit\Runner\PhptTestCase; -use PHPUnit\TextUI\DefaultResultPrinter; -use PHPUnit\Util\Filter; -use PHPUnitPHAR\SebastianBergmann\RecursionContext\InvalidArgumentException; -use Throwable; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -class TestDoxPrinter extends DefaultResultPrinter -{ /** - * @var NamePrettifier + * @psalm-assert-if-true !null $this->cacheResultFile + * + * @deprecated */ - protected $prettifier; + public function hasCacheResultFile(): bool + { + return $this->cacheResultFile !== null; + } /** - * @var int The number of test results received from the TestRunner + * @throws Exception + * + * @deprecated */ - protected $testIndex = 0; + public function cacheResultFile(): string + { + if (!$this->hasCacheResultFile()) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->cacheResultFile; + } + public function checkVersion(): bool + { + return $this->checkVersion; + } /** - * @var int The number of test results already sent to the output + * @psalm-assert-if-true !null $this->colors */ - protected $testFlushIndex = 0; + public function hasColors(): bool + { + return $this->colors !== null; + } /** - * @var array Buffer for test results + * @throws Exception */ - protected $testResults = []; + public function colors(): string + { + if (!$this->hasColors()) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->colors; + } /** - * @var array Lookup table for testname to testResults[index] + * @psalm-assert-if-true !null $this->columns */ - protected $testNameResultIndex = []; + public function hasColumns(): bool + { + return $this->columns !== null; + } /** - * @var bool + * @throws Exception */ - protected $enableOutputBuffer = \false; + public function columns(): int|string + { + if (!$this->hasColumns()) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->columns; + } /** - * @var array array + * @psalm-assert-if-true !null $this->configurationFile */ - protected $originalExecutionOrder = []; + public function hasConfigurationFile(): bool + { + return $this->configurationFile !== null; + } /** - * @var int + * @throws Exception */ - protected $spinState = 0; + public function configurationFile(): string + { + if (!$this->hasConfigurationFile()) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->configurationFile; + } /** - * @var bool + * @psalm-assert-if-true !null $this->coverageFilter */ - protected $showProgress = \true; + public function hasCoverageFilter(): bool + { + return $this->coverageFilter !== null; + } /** - * @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) + public function coverageFilter(): array { - parent::__construct($out, $verbose, $colors, $debug, $numberOfColumns, $reverse); - $this->prettifier = new \PHPUnit\Util\TestDox\NamePrettifier($this->colors); + if (!$this->hasCoverageFilter()) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->coverageFilter; } - public function setOriginalExecutionOrder(array $order) : void + /** + * @psalm-assert-if-true !null $this->coverageClover + */ + public function hasCoverageClover(): bool { - $this->originalExecutionOrder = $order; - $this->enableOutputBuffer = !empty($order); + return $this->coverageClover !== null; } - public function setShowProgressAnimation(bool $showProgress) : void + /** + * @throws Exception + */ + public function coverageClover(): string { - $this->showProgress = $showProgress; + if (!$this->hasCoverageClover()) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->coverageClover; } - public function printResult(TestResult $result) : void + /** + * @psalm-assert-if-true !null $this->coverageCobertura + */ + public function hasCoverageCobertura(): bool { + return $this->coverageCobertura !== null; } /** - * @throws InvalidArgumentException + * @throws Exception */ - public function endTest(Test $test, float $time) : void + public function coverageCobertura(): string { - 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++; + if (!$this->hasCoverageCobertura()) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); } - parent::endTest($test, $time); + return $this->coverageCobertura; } /** - * @throws InvalidArgumentException + * @psalm-assert-if-true !null $this->coverageCrap4J */ - public function addError(Test $test, Throwable $t, float $time) : void + public function hasCoverageCrap4J(): bool { - $this->registerTestResult($test, $t, BaseTestRunner::STATUS_ERROR, $time, \true); + return $this->coverageCrap4J !== null; } /** - * @throws InvalidArgumentException + * @throws Exception + */ + public function coverageCrap4J(): string + { + if (!$this->hasCoverageCrap4J()) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->coverageCrap4J; + } + /** + * @psalm-assert-if-true !null $this->coverageHtml */ - public function addWarning(Test $test, Warning $e, float $time) : void + public function hasCoverageHtml(): bool { - $this->registerTestResult($test, $e, BaseTestRunner::STATUS_WARNING, $time, \true); + return $this->coverageHtml !== null; } /** - * @throws InvalidArgumentException + * @throws Exception */ - public function addFailure(Test $test, AssertionFailedError $e, float $time) : void + public function coverageHtml(): string { - $this->registerTestResult($test, $e, BaseTestRunner::STATUS_FAILURE, $time, \true); + if (!$this->hasCoverageHtml()) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->coverageHtml; } /** - * @throws InvalidArgumentException + * @psalm-assert-if-true !null $this->coveragePhp */ - public function addIncompleteTest(Test $test, Throwable $t, float $time) : void + public function hasCoveragePhp(): bool { - $this->registerTestResult($test, $t, BaseTestRunner::STATUS_INCOMPLETE, $time, \false); + return $this->coveragePhp !== null; } /** - * @throws InvalidArgumentException + * @throws Exception */ - public function addRiskyTest(Test $test, Throwable $t, float $time) : void + public function coveragePhp(): string { - $this->registerTestResult($test, $t, BaseTestRunner::STATUS_RISKY, $time, \false); + if (!$this->hasCoveragePhp()) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->coveragePhp; } /** - * @throws InvalidArgumentException + * @psalm-assert-if-true !null $this->coverageText */ - public function addSkippedTest(Test $test, Throwable $t, float $time) : void + public function hasCoverageText(): bool { - $this->registerTestResult($test, $t, BaseTestRunner::STATUS_SKIPPED, $time, \false); + return $this->coverageText !== null; } - public function writeProgress(string $progress) : void + /** + * @throws Exception + */ + public function coverageText(): string { - $this->flushOutputBuffer(); + if (!$this->hasCoverageText()) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->coverageText; } - public function flush() : void + /** + * @psalm-assert-if-true !null $this->coverageTextShowUncoveredFiles + */ + public function hasCoverageTextShowUncoveredFiles(): bool { - $this->flushOutputBuffer(\true); + return $this->coverageTextShowUncoveredFiles !== null; } /** - * @throws InvalidArgumentException + * @throws Exception */ - protected function registerTestResult(Test $test, ?Throwable $t, int $status, float $time, bool $verbose) : void + public function coverageTextShowUncoveredFiles(): bool { - $testName = $test instanceof Reorderable ? $test->sortId() : $test->getName(); - $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); + if (!$this->hasCoverageTextShowUncoveredFiles()) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); } - $this->testResults[$this->testIndex] = $result; - $this->testNameResultIndex[$testName] = $this->testIndex; + return $this->coverageTextShowUncoveredFiles; } - protected function formatTestName(Test $test) : string + /** + * @psalm-assert-if-true !null $this->coverageTextShowOnlySummary + */ + public function hasCoverageTextShowOnlySummary(): bool { - return method_exists($test, 'getName') ? $test->getName() : ''; + return $this->coverageTextShowOnlySummary !== null; } - protected function formatClassName(Test $test) : string + /** + * @throws Exception + */ + public function coverageTextShowOnlySummary(): bool { - return get_class($test); + if (!$this->hasCoverageTextShowOnlySummary()) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->coverageTextShowOnlySummary; } - protected function testHasPassed() : bool + /** + * @psalm-assert-if-true !null $this->coverageXml + */ + public function hasCoverageXml(): bool { - if (!isset($this->testResults[$this->testIndex]['status'])) { - return \true; - } - if ($this->testResults[$this->testIndex]['status'] === BaseTestRunner::STATUS_PASSED) { - return \true; - } - return \false; + return $this->coverageXml !== null; } - protected function flushOutputBuffer(bool $forceFlush = \false) : void + /** + * @throws Exception + */ + public function coverageXml(): string { - if ($this->testFlushIndex === $this->testIndex) { - return; - } - if ($this->testFlushIndex > 0) { - if ($this->enableOutputBuffer && isset($this->originalExecutionOrder[$this->testFlushIndex - 1])) { - $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); + if (!$this->hasCoverageXml()) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); } + return $this->coverageXml; } - protected function showSpinner() : void + /** + * @psalm-assert-if-true !null $this->pathCoverage + */ + public function hasPathCoverage(): bool { - if (!$this->showProgress) { - return; - } - if ($this->spinState) { - $this->undrawSpinner(); - } - $this->spinState++; - $this->drawSpinner(); + return $this->pathCoverage !== null; } - protected function hideSpinner() : void + /** + * @throws Exception + */ + public function pathCoverage(): bool { - if (!$this->showProgress) { - return; - } - if ($this->spinState) { - $this->undrawSpinner(); + if (!$this->hasPathCoverage()) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); } - $this->spinState = 0; + return $this->pathCoverage; } - protected function drawSpinner() : void + /** + * @psalm-assert-if-true !null $this->coverageCacheDirectory + * + * @deprecated + */ + public function hasCoverageCacheDirectory(): bool { - // optional for CLI printers: show the user a 'buffering output' spinner + return $this->coverageCacheDirectory !== null; } - protected function undrawSpinner() : void + /** + * @throws Exception + * + * @deprecated + */ + public function coverageCacheDirectory(): string { - // remove the spinner from the current line + if (!$this->hasCoverageCacheDirectory()) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->coverageCacheDirectory; } - protected function writeTestResult(array $prevResult, array $result) : void + public function warmCoverageCache(): bool { + return $this->warmCoverageCache; } - protected function getEmptyTestResult() : array + /** + * @psalm-assert-if-true !null $this->defaultTimeLimit + */ + public function hasDefaultTimeLimit(): bool { - return ['className' => '', 'testName' => '', 'message' => '', 'failed' => '', 'verbose' => '']; + return $this->defaultTimeLimit !== null; } - protected function getTestResultByName(?string $testName) : array + /** + * @throws Exception + */ + public function defaultTimeLimit(): int { - if (isset($this->testNameResultIndex[$testName])) { - return $this->testResults[$this->testNameResultIndex[$testName]]; + if (!$this->hasDefaultTimeLimit()) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); } - return []; + return $this->defaultTimeLimit; + } + /** + * @psalm-assert-if-true !null $this->disableCodeCoverageIgnore + */ + public function hasDisableCodeCoverageIgnore(): bool + { + return $this->disableCodeCoverageIgnore !== null; } - protected function formatThrowable(Throwable $t, ?int $status = null) : string + /** + * @throws Exception + */ + public function disableCodeCoverageIgnore(): bool { - $message = trim(TestFailure::exceptionToString($t)); - if ($message) { - $message .= PHP_EOL . PHP_EOL . $this->formatStacktrace($t); - } else { - $message = $this->formatStacktrace($t); + if (!$this->hasDisableCodeCoverageIgnore()) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); } - return $message; + return $this->disableCodeCoverageIgnore; } - protected function formatStacktrace(Throwable $t) : string + /** + * @psalm-assert-if-true !null $this->disallowTestOutput + */ + public function hasDisallowTestOutput(): bool { - return Filter::getFilteredStacktrace($t); + return $this->disallowTestOutput !== null; } - protected function formatTestResultMessage(Throwable $t, array $result, string $prefix = '│') : string + /** + * @throws Exception + */ + public function disallowTestOutput(): bool { - $message = $this->formatThrowable($t, $result['status']); - if ($message === '') { - return ''; - } - if (!($this->verbose || $result['verbose'])) { - return ''; + if (!$this->hasDisallowTestOutput()) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); } - return $this->prefixLines($prefix, $message); + return $this->disallowTestOutput; } - protected function prefixLines(string $prefix, string $message) : string + /** + * @psalm-assert-if-true !null $this->enforceTimeLimit + */ + public function hasEnforceTimeLimit(): bool { - $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))); + return $this->enforceTimeLimit !== null; } -} - - * - * 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\TestResult; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class TextResultPrinter extends \PHPUnit\Util\TestDox\ResultPrinter -{ - public function printResult(TestResult $result) : void + /** + * @throws Exception + */ + public function enforceTimeLimit(): bool { + if (!$this->hasEnforceTimeLimit()) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->enforceTimeLimit; } /** - * Handler for 'start class' event. + * @psalm-assert-if-true !null $this->excludeGroups */ - protected function startClass(string $name) : void + public function hasExcludeGroups(): bool { - $this->write($this->currentTestClassPrettified . "\n"); + return $this->excludeGroups !== null; } /** - * Handler for 'on test' event. + * @throws Exception */ - protected function onTest(string $name, bool $success = \true) : void + public function excludeGroups(): array { - if ($success) { - $this->write(' [x] '); - } else { - $this->write(' [ ] '); + if (!$this->hasExcludeGroups()) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); } - $this->write($name . "\n"); + return $this->excludeGroups; } /** - * Handler for 'end class' event. + * @psalm-assert-if-true !null $this->executionOrder */ - protected function endClass(string $name) : void + public function hasExecutionOrder(): bool { - $this->write("\n"); + return $this->executionOrder !== null; } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Util\TestDox; - -use function array_filter; -use function get_class; -use function implode; -use function strpos; -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\Framework\WarningTestCase; -use PHPUnit\Util\Printer; -use PHPUnit\Util\Test as TestUtil; -use ReflectionClass; -use ReflectionException; -use PHPUnitPHAR\SebastianBergmann\RecursionContext\InvalidArgumentException; -use Throwable; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class XmlResultPrinter extends Printer implements TestListener -{ /** - * @var DOMDocument + * @throws Exception */ - private $document; + public function executionOrder(): int + { + if (!$this->hasExecutionOrder()) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->executionOrder; + } /** - * @var DOMElement + * @psalm-assert-if-true !null $this->executionOrderDefects */ - private $root; + public function hasExecutionOrderDefects(): bool + { + return $this->executionOrderDefects !== null; + } /** - * @var NamePrettifier + * @throws Exception */ - private $prettifier; + public function executionOrderDefects(): int + { + if (!$this->hasExecutionOrderDefects()) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->executionOrderDefects; + } /** - * @var null|Throwable + * @psalm-assert-if-true !null $this->failOnDeprecation */ - private $exception; + public function hasFailOnDeprecation(): bool + { + return $this->failOnDeprecation !== null; + } /** - * @param resource|string $out - * * @throws Exception */ - public function __construct($out = null) + public function failOnDeprecation(): bool { - $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 \PHPUnit\Util\TestDox\NamePrettifier(); - parent::__construct($out); + if (!$this->hasFailOnDeprecation()) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->failOnDeprecation; + } + /** + * @psalm-assert-if-true !null $this->failOnEmptyTestSuite + */ + public function hasFailOnEmptyTestSuite(): bool + { + return $this->failOnEmptyTestSuite !== null; } /** - * Flush buffer and close output. + * @throws Exception */ - public function flush() : void + public function failOnEmptyTestSuite(): bool { - $this->write($this->document->saveXML()); - parent::flush(); + if (!$this->hasFailOnEmptyTestSuite()) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->failOnEmptyTestSuite; } /** - * An error occurred. + * @psalm-assert-if-true !null $this->failOnIncomplete */ - public function addError(Test $test, Throwable $t, float $time) : void + public function hasFailOnIncomplete(): bool { - $this->exception = $t; + return $this->failOnIncomplete !== null; } /** - * A warning occurred. + * @throws Exception */ - public function addWarning(Test $test, Warning $e, float $time) : void + public function failOnIncomplete(): bool { + if (!$this->hasFailOnIncomplete()) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->failOnIncomplete; } /** - * A failure occurred. + * @psalm-assert-if-true !null $this->failOnNotice */ - public function addFailure(Test $test, AssertionFailedError $e, float $time) : void + public function hasFailOnNotice(): bool { - $this->exception = $e; + return $this->failOnNotice !== null; } /** - * Incomplete test. + * @throws Exception */ - public function addIncompleteTest(Test $test, Throwable $t, float $time) : void + public function failOnNotice(): bool { + if (!$this->hasFailOnNotice()) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->failOnNotice; } /** - * Risky test. + * @psalm-assert-if-true !null $this->failOnRisky */ - public function addRiskyTest(Test $test, Throwable $t, float $time) : void + public function hasFailOnRisky(): bool { + return $this->failOnRisky !== null; } /** - * Skipped test. + * @throws Exception */ - public function addSkippedTest(Test $test, Throwable $t, float $time) : void + public function failOnRisky(): bool { + if (!$this->hasFailOnRisky()) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->failOnRisky; } /** - * A test suite started. + * @psalm-assert-if-true !null $this->failOnSkipped */ - public function startTestSuite(TestSuite $suite) : void + public function hasFailOnSkipped(): bool { + return $this->failOnSkipped !== null; } /** - * A test suite ended. + * @throws Exception */ - public function endTestSuite(TestSuite $suite) : void + public function failOnSkipped(): bool { + if (!$this->hasFailOnSkipped()) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->failOnSkipped; } /** - * A test started. + * @psalm-assert-if-true !null $this->failOnWarning */ - public function startTest(Test $test) : void + public function hasFailOnWarning(): bool { - $this->exception = null; + return $this->failOnWarning !== null; } /** - * A test ended. - * - * @throws InvalidArgumentException + * @throws Exception */ - public function endTest(Test $test, float $time) : void + public function failOnWarning(): bool { - if (!$test instanceof TestCase || $test instanceof WarningTestCase) { - return; - } - $groups = array_filter($test->getGroups(), static function ($group) { - return !($group === 'small' || $group === 'medium' || $group === 'large' || strpos($group, '__phpunit_') === 0); - }); - $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 = TestUtil::parseTestMethodAnnotations(get_class($test), $test->getName(\false)); - 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 = TestUtil::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->hasFailOnWarning()) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); } - if ($this->exception !== null) { - if ($this->exception instanceof Exception) { - $steps = $this->exception->getSerializableTrace(); - } else { - $steps = $this->exception->getTrace(); - } - try { - $file = (new ReflectionClass($test))->getFileName(); - // @codeCoverageIgnoreStart - } catch (ReflectionException $e) { - throw new Exception($e->getMessage(), $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd - 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); + return $this->failOnWarning; } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Util; - -use const PHP_EOL; -use function get_class; -use function sprintf; -use function str_replace; -use PHPUnit\Framework\TestCase; -use PHPUnit\Framework\TestSuite; -use PHPUnit\Runner\PhptTestCase; -use RecursiveIteratorIterator; -use PHPUnitPHAR\SebastianBergmann\RecursionContext\InvalidArgumentException; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class TextTestListRenderer -{ /** - * @throws InvalidArgumentException + * @psalm-assert-if-true !null $this->stopOnDefect */ - public function render(TestSuite $suite) : string + public function hasStopOnDefect(): bool { - $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; + return $this->stopOnDefect !== null; } -} - - * - * 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 + /** + * @throws Exception + */ + public function stopOnDefect(): 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; + if (!$this->hasStopOnDefect()) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); } + return $this->stopOnDefect; } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Util; - -use function in_array; -use function sprintf; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - * - * @psalm-immutable - */ -final class VersionComparisonOperator -{ /** - * @psalm-var '<'|'lt'|'<='|'le'|'>'|'gt'|'>='|'ge'|'=='|'='|'eq'|'!='|'<>'|'ne' + * @psalm-assert-if-true !null $this->stopOnDeprecation */ - private $operator; - public function __construct(string $operator) + public function hasStopOnDeprecation(): bool { - $this->ensureOperatorIsValid($operator); - $this->operator = $operator; + return $this->stopOnDeprecation !== null; } /** - * @return '!='|'<'|'<='|'<>'|'='|'=='|'>'|'>='|'eq'|'ge'|'gt'|'le'|'lt'|'ne' + * @throws Exception */ - public function asString() : string + public function stopOnDeprecation(): bool { - return $this->operator; + if (!$this->hasStopOnDeprecation()) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->stopOnDeprecation; + } + /** + * @psalm-assert-if-true !null $this->stopOnError + */ + public function hasStopOnError(): bool + { + return $this->stopOnError !== null; } /** * @throws Exception - * - * @psalm-assert '<'|'lt'|'<='|'le'|'>'|'gt'|'>='|'ge'|'=='|'='|'eq'|'!='|'<>'|'ne' $operator */ - private function ensureOperatorIsValid(string $operator) : void + public function stopOnError(): bool { - if (!in_array($operator, ['<', 'lt', '<=', 'le', '>', 'gt', '>=', 'ge', '==', '=', 'eq', '!=', '<>', 'ne'], \true)) { - throw new \PHPUnit\Util\Exception(sprintf('"%s" is not a valid version_compare() operator', $operator)); + if (!$this->hasStopOnError()) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); } + return $this->stopOnError; } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Util; - -use const DIRECTORY_SEPARATOR; -use function addslashes; -use function array_map; -use function implode; -use function is_string; -use function realpath; -use function sprintf; -use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\CodeCoverage as FilterConfiguration; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - * - * @deprecated - */ -final class XdebugFilterScriptGenerator -{ - public function generate(FilterConfiguration $filter) : string + /** + * @psalm-assert-if-true !null $this->stopOnFailure + */ + public function hasStopOnFailure(): bool { - $files = array_map(static function ($item) { - return sprintf(" '%s'", $item); - }, $this->getItems($filter)); - $files = implode(",\n", $files); - return <<stopOnFailure !== null; } - private function getItems(FilterConfiguration $filter) : array + /** + * @throws Exception + */ + public function stopOnFailure(): bool { - $files = []; - foreach ($filter->directories() as $directory) { - $path = realpath($directory->path()); - if (is_string($path)) { - $files[] = sprintf(addslashes('%s' . DIRECTORY_SEPARATOR), $path); - } - } - foreach ($filter->files() as $file) { - $files[] = $file->path(); + if (!$this->hasStopOnFailure()) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); } - return $files; + return $this->stopOnFailure; } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Util; - -use const ENT_QUOTES; -use function assert; -use function class_exists; -use function htmlspecialchars; -use function mb_convert_encoding; -use function ord; -use function preg_replace; -use function settype; -use function strlen; -use DOMCharacterData; -use DOMDocument; -use DOMElement; -use DOMNode; -use DOMText; -use ReflectionClass; -use ReflectionException; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class Xml -{ /** - * @deprecated Only used by assertEqualXMLStructure() + * @psalm-assert-if-true !null $this->stopOnIncomplete */ - public static function import(DOMElement $element) : DOMElement + public function hasStopOnIncomplete(): bool { - return (new DOMDocument())->importNode($element, \true); + return $this->stopOnIncomplete !== null; } /** - * @deprecated Only used by assertEqualXMLStructure() + * @throws Exception */ - public static function removeCharacterDataNodes(DOMNode $node) : void + public function stopOnIncomplete(): bool { - if ($node->hasChildNodes()) { - for ($i = $node->childNodes->length - 1; $i >= 0; $i--) { - if (($child = $node->childNodes->item($i)) instanceof DOMCharacterData) { - $node->removeChild($child); - } - } + if (!$this->hasStopOnIncomplete()) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); } + return $this->stopOnIncomplete; } /** - * 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 + * @psalm-assert-if-true !null $this->stopOnNotice */ - public static function prepareString(string $string) : string + public function hasStopOnNotice(): bool { - return preg_replace('/[\\x00-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]/', '', htmlspecialchars(self::convertToUtf8($string), ENT_QUOTES)); + return $this->stopOnNotice !== null; } /** - * "Convert" a DOMElement object into a PHP variable. + * @throws Exception */ - public static function xmlToVariable(DOMElement $element) + public function stopOnNotice(): bool { - $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 { - assert(class_exists($className)); - $variable = (new ReflectionClass($className))->newInstanceArgs($constructorArgs); - // @codeCoverageIgnoreStart - } catch (ReflectionException $e) { - throw new \PHPUnit\Util\Exception($e->getMessage(), $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd - } 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; + if (!$this->hasStopOnNotice()) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); } - return $variable; + return $this->stopOnNotice; } - private static function convertToUtf8(string $string) : string + /** + * @psalm-assert-if-true !null $this->stopOnRisky + */ + public function hasStopOnRisky(): bool { - if (!self::isUtf8($string)) { - $string = mb_convert_encoding($string, 'UTF-8'); - } - return $string; + return $this->stopOnRisky !== null; } - private static function isUtf8(string $string) : bool + /** + * @throws Exception + */ + public function stopOnRisky(): 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; - } - } + if (!$this->hasStopOnRisky()) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); } - return \true; + return $this->stopOnRisky; } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Util\Xml; - -use RuntimeException; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class Exception extends RuntimeException implements \PHPUnit\Exception -{ -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Util\Xml; - -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - * - * @psalm-immutable - */ -final class FailedSchemaDetectionResult extends \PHPUnit\Util\Xml\SchemaDetectionResult -{ -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Util\Xml; - -use function chdir; -use function dirname; -use function error_reporting; -use function file_get_contents; -use function getcwd; -use function libxml_get_errors; -use function libxml_use_internal_errors; -use function sprintf; -use DOMDocument; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class Loader -{ /** - * @throws Exception + * @psalm-assert-if-true !null $this->stopOnSkipped */ - public function loadFile(string $filename, bool $isHtml = \false, bool $xinclude = \false, bool $strict = \false) : DOMDocument + public function hasStopOnSkipped(): bool { - $reporting = error_reporting(0); - $contents = file_get_contents($filename); - error_reporting($reporting); - if ($contents === \false) { - throw new \PHPUnit\Util\Xml\Exception(sprintf('Could not read "%s".', $filename)); - } - return $this->load($contents, $isHtml, $filename, $xinclude, $strict); + return $this->stopOnSkipped !== null; } /** * @throws Exception */ - public function load(string $actual, bool $isHtml = \false, string $filename = '', bool $xinclude = \false, bool $strict = \false) : DOMDocument + public function stopOnSkipped(): bool { - if ($actual === '') { - throw new \PHPUnit\Util\Xml\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 \PHPUnit\Util\Xml\Exception(sprintf('Could not load "%s".%s', $filename, $message !== '' ? "\n" . $message : '')); - } - if ($message === '') { - $message = 'Could not load XML for unknown reason'; - } - throw new \PHPUnit\Util\Xml\Exception($message); + if (!$this->hasStopOnSkipped()) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); } - return $document; + return $this->stopOnSkipped; } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Util\Xml; - -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - * - * @psalm-immutable - */ -abstract class SchemaDetectionResult -{ /** - * @psalm-assert-if-true SuccessfulSchemaDetectionResult $this + * @psalm-assert-if-true !null $this->stopOnWarning */ - public function detected() : bool + public function hasStopOnWarning(): bool { - return \false; + return $this->stopOnWarning !== null; } /** * @throws Exception */ - public function version() : string + public function stopOnWarning(): bool { - throw new \PHPUnit\Util\Xml\Exception('No supported schema was detected'); + if (!$this->hasStopOnWarning()) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->stopOnWarning; + } + /** + * @psalm-assert-if-true !null $this->filter + */ + public function hasFilter(): bool + { + return $this->filter !== null; } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Util\Xml; - -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class SchemaDetector -{ /** * @throws Exception */ - public function detect(string $filename) : \PHPUnit\Util\Xml\SchemaDetectionResult + public function filter(): string { - $document = (new \PHPUnit\Util\Xml\Loader())->loadFile($filename, \false, \true, \true); - $schemaFinder = new \PHPUnit\Util\Xml\SchemaFinder(); - foreach ($schemaFinder->available() as $candidate) { - $schema = (new \PHPUnit\Util\Xml\SchemaFinder())->find($candidate); - if (!(new \PHPUnit\Util\Xml\Validator())->validate($document, $schema)->hasValidationErrors()) { - return new \PHPUnit\Util\Xml\SuccessfulSchemaDetectionResult($candidate); - } + if (!$this->hasFilter()) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); } - return new \PHPUnit\Util\Xml\FailedSchemaDetectionResult(); + return $this->filter; } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Util\Xml; - -use function assert; -use function defined; -use function is_file; -use function rsort; -use function sprintf; -use DirectoryIterator; -use PHPUnit\Runner\Version; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class SchemaFinder -{ /** - * @psalm-return non-empty-list + * @psalm-assert-if-true !null $this->generateBaseline */ - public function available() : array + public function hasGenerateBaseline(): bool { - $result = [Version::series()]; - foreach (new DirectoryIterator($this->path() . 'schema') as $file) { - if ($file->isDot()) { - continue; - } - $version = $file->getBasename('.xsd'); - assert(!empty($version)); - $result[] = $version; - } - rsort($result); - return $result; + return $this->generateBaseline !== null; } /** * @throws Exception */ - public function find(string $version) : string + public function generateBaseline(): string { - if ($version === Version::series()) { - $filename = $this->path() . 'phpunit.xsd'; - } else { - $filename = $this->path() . 'schema/' . $version . '.xsd'; - } - if (!is_file($filename)) { - throw new \PHPUnit\Util\Xml\Exception(sprintf('Schema for PHPUnit %s is not available', $version)); + if (!$this->hasGenerateBaseline()) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); } - return $filename; + return $this->generateBaseline; } - private function path() : string + /** + * @psalm-assert-if-true !null $this->useBaseline + */ + public function hasUseBaseline(): bool { - if (defined('__PHPUNIT_PHAR_ROOT__')) { - return __PHPUNIT_PHAR_ROOT__ . '/'; - } - return __DIR__ . '/../../../'; + return $this->useBaseline !== null; } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Util\Xml; - -use function count; -use ArrayIterator; -use Countable; -use DOMNode; -use DOMNodeList; -use IteratorAggregate; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - * - * @template-implements IteratorAggregate - */ -final class SnapshotNodeList implements Countable, IteratorAggregate -{ /** - * @var DOMNode[] + * @throws Exception */ - private $nodes = []; - public static function fromNodeList(DOMNodeList $list) : self + public function useBaseline(): string { - $snapshot = new self(); - foreach ($list as $node) { - $snapshot->nodes[] = $node; + if (!$this->hasUseBaseline()) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); } - return $snapshot; + return $this->useBaseline; } - public function count() : int + public function ignoreBaseline(): bool { - return count($this->nodes); + return $this->ignoreBaseline; } - public function getIterator() : ArrayIterator + public function generateConfiguration(): bool { - return new ArrayIterator($this->nodes); + return $this->generateConfiguration; + } + public function migrateConfiguration(): bool + { + return $this->migrateConfiguration; } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Util\Xml; - -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - * - * @psalm-immutable - */ -final class SuccessfulSchemaDetectionResult extends \PHPUnit\Util\Xml\SchemaDetectionResult -{ /** - * @psalm-var non-empty-string + * @psalm-assert-if-true !null $this->groups */ - private $version; + public function hasGroups(): bool + { + return $this->groups !== null; + } /** - * @psalm-param non-empty-string $version + * @throws Exception */ - public function __construct(string $version) + public function groups(): array { - $this->version = $version; + if (!$this->hasGroups()) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->groups; } /** - * @psalm-assert-if-true SuccessfulSchemaDetectionResult $this + * @psalm-assert-if-true !null $this->testsCovering */ - public function detected() : bool + public function hasTestsCovering(): bool { - return \true; + return $this->testsCovering !== null; } /** - * @psalm-return non-empty-string + * @throws Exception */ - public function version() : string + public function testsCovering(): array { - return $this->version; + if (!$this->hasTestsCovering()) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->testsCovering; } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Util\Xml; - -use function sprintf; -use function trim; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - * - * @psalm-immutable - */ -final class ValidationResult -{ /** - * @psalm-var array> + * @psalm-assert-if-true !null $this->testsUsing */ - private $validationErrors = []; + public function hasTestsUsing(): bool + { + return $this->testsUsing !== null; + } /** - * @psalm-param array $errors + * @throws Exception */ - public static function fromArray(array $errors) : self + public function testsUsing(): array { - $validationErrors = []; - foreach ($errors as $error) { - if (!isset($validationErrors[$error->line])) { - $validationErrors[$error->line] = []; - } - $validationErrors[$error->line][] = trim($error->message); + if (!$this->hasTestsUsing()) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); } - return new self($validationErrors); + return $this->testsUsing; } - private function __construct(array $validationErrors) + public function help(): bool { - $this->validationErrors = $validationErrors; + return $this->help; } - public function hasValidationErrors() : bool + /** + * @psalm-assert-if-true !null $this->includePath + */ + public function hasIncludePath(): bool { - return !empty($this->validationErrors); + return $this->includePath !== null; } - public function asString() : string + /** + * @throws Exception + */ + public function includePath(): string { - $buffer = ''; - foreach ($this->validationErrors as $line => $validationErrorsOnLine) { - $buffer .= sprintf(\PHP_EOL . ' Line %d:' . \PHP_EOL, $line); - foreach ($validationErrorsOnLine as $validationError) { - $buffer .= sprintf(' - %s' . \PHP_EOL, $validationError); - } + if (!$this->hasIncludePath()) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); } - return $buffer; + return $this->includePath; } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Util\Xml; - -use function file_get_contents; -use function libxml_clear_errors; -use function libxml_get_errors; -use function libxml_use_internal_errors; -use DOMDocument; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class Validator -{ - public function validate(DOMDocument $document, string $xsdFilename) : \PHPUnit\Util\Xml\ValidationResult + /** + * @psalm-assert-if-true !null $this->iniSettings + */ + public function hasIniSettings(): bool { - $originalErrorHandling = libxml_use_internal_errors(\true); - $document->schemaValidateSource(file_get_contents($xsdFilename)); - $errors = libxml_get_errors(); - libxml_clear_errors(); - libxml_use_internal_errors($originalErrorHandling); - return \PHPUnit\Util\Xml\ValidationResult::fromArray($errors); + return $this->iniSettings !== null; } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Util; - -use function get_class; -use function implode; -use function str_replace; -use PHPUnit\Framework\TestCase; -use PHPUnit\Framework\TestSuite; -use PHPUnit\Runner\PhptTestCase; -use RecursiveIteratorIterator; -use PHPUnitPHAR\SebastianBergmann\RecursionContext\InvalidArgumentException; -use XMLWriter; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class XmlTestListRenderer -{ /** - * @throws InvalidArgumentException + * @throws Exception */ - public function render(TestSuite $suite) : string + public function iniSettings(): array { - $writer = new XMLWriter(); - $writer->openMemory(); - $writer->setIndent(\true); - $writer->startDocument('1.0', 'UTF-8'); - $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 (!$this->hasIniSettings()) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); } - if ($currentTestCase !== null) { - $writer->endElement(); + return $this->iniSettings; + } + /** + * @psalm-assert-if-true !null $this->junitLogfile + */ + public function hasJunitLogfile(): bool + { + return $this->junitLogfile !== null; + } + /** + * @throws Exception + */ + public function junitLogfile(): string + { + if (!$this->hasJunitLogfile()) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); } - $writer->endElement(); - $writer->endDocument(); - return $writer->outputMemory(); + return $this->junitLogfile; } -} - - - - - phpunit - phpunit - 9.6.19 - The PHP Unit Testing framework. - - - BSD-3-Clause - - - pkg:composer/phpunit/phpunit@9.6.19 - - - doctrine - deprecations - 1.1.3 - A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options to disable all deprecations or selectively for packages. - - - MIT - - - pkg:composer/doctrine/deprecations@1.1.3 - - - doctrine - instantiator - 1.5.0 - A small, lightweight utility to instantiate objects in PHP without invoking their constructors - - - MIT - - - pkg:composer/doctrine/instantiator@1.5.0 - - - myclabs - deep-copy - 1.11.1 - Create deep copies (clones) of your objects - - - MIT - - - pkg:composer/myclabs/deep-copy@1.11.1 - - - nikic - php-parser - v4.19.1 - A PHP parser written in PHP - - - BSD-3-Clause - - - pkg:composer/nikic/php-parser@v4.19.1 - - - phar-io - manifest - 2.0.4 - Component for reading phar.io manifest information from a PHP Archive (PHAR) - - - BSD-3-Clause - - - pkg:composer/phar-io/manifest@2.0.4 - - - phar-io - version - 3.2.1 - Library for handling version information and constraints - - - BSD-3-Clause - - - pkg:composer/phar-io/version@3.2.1 - - - phpdocumentor - reflection-common - 2.2.0 - Common reflection classes used by phpdocumentor to reflect the code structure - - - MIT - - - pkg:composer/phpdocumentor/reflection-common@2.2.0 - - - phpdocumentor - reflection-docblock - 5.3.0 - With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock. - - - MIT - - - pkg:composer/phpdocumentor/reflection-docblock@5.3.0 - - - phpdocumentor - type-resolver - 1.8.2 - A PSR-5 based resolver of Class names, Types and Structural Element Names - - - MIT - - - pkg:composer/phpdocumentor/type-resolver@1.8.2 - - - phpspec - prophecy - v1.19.0 - Highly opinionated mocking framework for PHP 5.3+ - - - MIT - - - pkg:composer/phpspec/prophecy@v1.19.0 - - - phpstan - phpdoc-parser - 1.28.0 - PHPDoc parser with support for nullable, intersection and generic types - - - MIT - - - pkg:composer/phpstan/phpdoc-parser@1.28.0 - - - phpunit - php-code-coverage - 9.2.31 - Library that provides collection, processing, and rendering functionality for PHP code coverage information. - - - BSD-3-Clause - - - pkg:composer/phpunit/php-code-coverage@9.2.31 - - - phpunit - php-file-iterator - 3.0.6 - FilterIterator implementation that filters files based on a list of suffixes. - - - BSD-3-Clause - - - pkg:composer/phpunit/php-file-iterator@3.0.6 - - - phpunit - php-invoker - 3.1.1 - Invoke callables with a timeout - - - BSD-3-Clause - - - pkg:composer/phpunit/php-invoker@3.1.1 - - - phpunit - php-text-template - 2.0.4 - Simple template engine. - - - BSD-3-Clause - - - pkg:composer/phpunit/php-text-template@2.0.4 - - - phpunit - php-timer - 5.0.3 - Utility class for timing - - - BSD-3-Clause - - - pkg:composer/phpunit/php-timer@5.0.3 - - - sebastian - cli-parser - 1.0.2 - Library for parsing CLI options - - - BSD-3-Clause - - - pkg:composer/sebastian/cli-parser@1.0.2 - - - sebastian - code-unit - 1.0.8 - Collection of value objects that represent the PHP code units - - - BSD-3-Clause - - - pkg:composer/sebastian/code-unit@1.0.8 - - - sebastian - code-unit-reverse-lookup - 2.0.3 - Looks up which function or method a line of code belongs to - - - BSD-3-Clause - - - pkg:composer/sebastian/code-unit-reverse-lookup@2.0.3 - - - sebastian - comparator - 4.0.8 - Provides the functionality to compare PHP values for equality - - - BSD-3-Clause - - - pkg:composer/sebastian/comparator@4.0.8 - - - sebastian - complexity - 2.0.3 - Library for calculating the complexity of PHP code units - - - BSD-3-Clause - - - pkg:composer/sebastian/complexity@2.0.3 - - - sebastian - diff - 4.0.6 - Diff implementation - - - BSD-3-Clause - - - pkg:composer/sebastian/diff@4.0.6 - - - sebastian - environment - 5.1.5 - Provides functionality to handle HHVM/PHP environments - - - BSD-3-Clause - - - pkg:composer/sebastian/environment@5.1.5 - - - sebastian - exporter - 4.0.6 - Provides the functionality to export PHP variables for visualization - - - BSD-3-Clause - - - pkg:composer/sebastian/exporter@4.0.6 - - - sebastian - global-state - 5.0.7 - Snapshotting of global state - - - BSD-3-Clause - - - pkg:composer/sebastian/global-state@5.0.7 - - - sebastian - lines-of-code - 1.0.4 - Library for counting the lines of code in PHP source code - - - BSD-3-Clause - - - pkg:composer/sebastian/lines-of-code@1.0.4 - - - sebastian - object-enumerator - 4.0.4 - Traverses array structures and object graphs to enumerate all referenced objects - - - BSD-3-Clause - - - pkg:composer/sebastian/object-enumerator@4.0.4 - - - sebastian - object-reflector - 2.0.4 - Allows reflection of object attributes, including inherited and non-public ones - - - BSD-3-Clause - - - pkg:composer/sebastian/object-reflector@2.0.4 - - - sebastian - recursion-context - 4.0.5 - Provides functionality to recursively process PHP variables - - - BSD-3-Clause - - - pkg:composer/sebastian/recursion-context@4.0.5 - - - sebastian - resource-operations - 3.0.4 - Provides a list of PHP built-in functions that operate on resources - - - BSD-3-Clause - - - pkg:composer/sebastian/resource-operations@3.0.4 - - - sebastian - type - 3.2.1 - Collection of value objects that represent the types of the PHP type system - - - BSD-3-Clause - - - pkg:composer/sebastian/type@3.2.1 - - - sebastian - version - 3.0.2 - Library that helps with managing the version number of Git-hosted PHP projects - - - BSD-3-Clause - - - pkg:composer/sebastian/version@3.0.2 - - - theseer - tokenizer - 1.2.3 - A small library for converting tokenized PHP source code into XML and potentially other formats - - - BSD-3-Clause - - - pkg:composer/theseer/tokenizer@1.2.3 - - - webmozart - assert - 1.11.0 - Assertions to validate method input/output with nice error messages. - - - MIT - - - pkg:composer/webmozart/assert@1.11.0 - - - - - - - - This Schema file defines the rules by which the XML configuration file of PHPUnit 8.5 may be structured. - - - - - - Root Element - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - The main type specifying the document structure - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - This Schema file defines the rules by which the XML configuration file of PHPUnit 9.0 may be structured. - - - - - - Root Element - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - The main type specifying the document structure - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - This Schema file defines the rules by which the XML configuration file of PHPUnit 9.0 may be structured. - - - - - - Root Element - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - The main type specifying the document structure - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - This Schema file defines the rules by which the XML configuration file of PHPUnit 9.2 may be structured. - - - - - - Root Element - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - The main type specifying the document structure - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - This Schema file defines the rules by which the XML configuration file of PHPUnit 9.3 may be structured. - - - - - - Root Element - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - The main type specifying the document structure - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - This Schema file defines the rules by which the XML configuration file of PHPUnit 9.4 may be structured. - - - - - - Root Element - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - The main type specifying the document structure - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + public function listGroups(): bool + { + return $this->listGroups; + } + public function listSuites(): bool + { + return $this->listSuites; + } + public function listTests(): bool + { + return $this->listTests; + } + /** + * @psalm-assert-if-true !null $this->listTestsXml + */ + public function hasListTestsXml(): bool + { + return $this->listTestsXml !== null; + } + /** + * @throws Exception + */ + public function listTestsXml(): string + { + if (!$this->hasListTestsXml()) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->listTestsXml; + } + /** + * @psalm-assert-if-true !null $this->noCoverage + */ + public function hasNoCoverage(): bool + { + return $this->noCoverage !== null; + } + /** + * @throws Exception + */ + public function noCoverage(): bool + { + if (!$this->hasNoCoverage()) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->noCoverage; + } + /** + * @psalm-assert-if-true !null $this->noExtensions + */ + public function hasNoExtensions(): bool + { + return $this->noExtensions !== null; + } + /** + * @throws Exception + */ + public function noExtensions(): bool + { + if (!$this->hasNoExtensions()) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->noExtensions; + } + /** + * @psalm-assert-if-true !null $this->noOutput + */ + public function hasNoOutput(): bool + { + return $this->noOutput !== null; + } + /** + * @throws Exception + */ + public function noOutput(): bool + { + if ($this->noOutput === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->noOutput; + } + /** + * @psalm-assert-if-true !null $this->noProgress + */ + public function hasNoProgress(): bool + { + return $this->noProgress !== null; + } + /** + * @throws Exception + */ + public function noProgress(): bool + { + if ($this->noProgress === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->noProgress; + } + /** + * @psalm-assert-if-true !null $this->noResults + */ + public function hasNoResults(): bool + { + return $this->noResults !== null; + } + /** + * @throws Exception + */ + public function noResults(): bool + { + if ($this->noResults === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->noResults; + } + /** + * @psalm-assert-if-true !null $this->noLogging + */ + public function hasNoLogging(): bool + { + return $this->noLogging !== null; + } + /** + * @throws Exception + */ + public function noLogging(): bool + { + if (!$this->hasNoLogging()) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->noLogging; + } + /** + * @psalm-assert-if-true !null $this->processIsolation + */ + public function hasProcessIsolation(): bool + { + return $this->processIsolation !== null; + } + /** + * @throws Exception + */ + public function processIsolation(): bool + { + if (!$this->hasProcessIsolation()) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->processIsolation; + } + /** + * @psalm-assert-if-true !null $this->randomOrderSeed + */ + public function hasRandomOrderSeed(): bool + { + return $this->randomOrderSeed !== null; + } + /** + * @throws Exception + */ + public function randomOrderSeed(): int + { + if (!$this->hasRandomOrderSeed()) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->randomOrderSeed; + } + /** + * @psalm-assert-if-true !null $this->reportUselessTests + */ + public function hasReportUselessTests(): bool + { + return $this->reportUselessTests !== null; + } + /** + * @throws Exception + */ + public function reportUselessTests(): bool + { + if (!$this->hasReportUselessTests()) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->reportUselessTests; + } + /** + * @psalm-assert-if-true !null $this->resolveDependencies + */ + public function hasResolveDependencies(): bool + { + return $this->resolveDependencies !== null; + } + /** + * @throws Exception + */ + public function resolveDependencies(): bool + { + if (!$this->hasResolveDependencies()) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->resolveDependencies; + } + /** + * @psalm-assert-if-true !null $this->reverseList + */ + public function hasReverseList(): bool + { + return $this->reverseList !== null; + } + /** + * @throws Exception + */ + public function reverseList(): bool + { + if (!$this->hasReverseList()) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->reverseList; + } + /** + * @psalm-assert-if-true !null $this->stderr + */ + public function hasStderr(): bool + { + return $this->stderr !== null; + } + /** + * @throws Exception + */ + public function stderr(): bool + { + if (!$this->hasStderr()) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->stderr; + } + /** + * @psalm-assert-if-true !null $this->strictCoverage + */ + public function hasStrictCoverage(): bool + { + return $this->strictCoverage !== null; + } + /** + * @throws Exception + */ + public function strictCoverage(): bool + { + if (!$this->hasStrictCoverage()) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->strictCoverage; + } + /** + * @psalm-assert-if-true !null $this->teamcityLogfile + */ + public function hasTeamcityLogfile(): bool + { + return $this->teamcityLogfile !== null; + } + /** + * @throws Exception + */ + public function teamcityLogfile(): string + { + if (!$this->hasTeamcityLogfile()) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->teamcityLogfile; + } + /** + * @psalm-assert-if-true !null $this->teamcityPrinter + */ + public function hasTeamCityPrinter(): bool + { + return $this->teamCityPrinter !== null; + } + /** + * @throws Exception + */ + public function teamCityPrinter(): bool + { + if (!$this->hasTeamCityPrinter()) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->teamCityPrinter; + } + /** + * @psalm-assert-if-true !null $this->testdoxHtmlFile + */ + public function hasTestdoxHtmlFile(): bool + { + return $this->testdoxHtmlFile !== null; + } + /** + * @throws Exception + */ + public function testdoxHtmlFile(): string + { + if (!$this->hasTestdoxHtmlFile()) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->testdoxHtmlFile; + } + /** + * @psalm-assert-if-true !null $this->testdoxTextFile + */ + public function hasTestdoxTextFile(): bool + { + return $this->testdoxTextFile !== null; + } + /** + * @throws Exception + */ + public function testdoxTextFile(): string + { + if (!$this->hasTestdoxTextFile()) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->testdoxTextFile; + } + /** + * @psalm-assert-if-true !null $this->testdoxPrinter + */ + public function hasTestDoxPrinter(): bool + { + return $this->testdoxPrinter !== null; + } + /** + * @throws Exception + */ + public function testdoxPrinter(): bool + { + if (!$this->hasTestdoxPrinter()) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->testdoxPrinter; + } + /** + * @psalm-assert-if-true !null $this->testSuffixes + */ + public function hasTestSuffixes(): bool + { + return $this->testSuffixes !== null; + } + /** + * @psalm-return non-empty-list + * + * @throws Exception + */ + public function testSuffixes(): array + { + if (!$this->hasTestSuffixes()) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->testSuffixes; + } + /** + * @psalm-assert-if-true !null $this->testSuite + */ + public function hasTestSuite(): bool + { + return $this->testSuite !== null; + } + /** + * @throws Exception + */ + public function testSuite(): string + { + if (!$this->hasTestSuite()) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->testSuite; + } + /** + * @psalm-assert-if-true !null $this->excludedTestSuite + */ + public function hasExcludedTestSuite(): bool + { + return $this->excludeTestSuite !== null; + } + /** + * @throws Exception + */ + public function excludedTestSuite(): string + { + if (!$this->hasExcludedTestSuite()) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->excludeTestSuite; + } + public function useDefaultConfiguration(): bool + { + return $this->useDefaultConfiguration; + } + /** + * @psalm-assert-if-true !null $this->displayDetailsOnIncompleteTests + */ + public function hasDisplayDetailsOnIncompleteTests(): bool + { + return $this->displayDetailsOnIncompleteTests !== null; + } + /** + * @throws Exception + */ + public function displayDetailsOnIncompleteTests(): bool + { + if (!$this->hasDisplayDetailsOnIncompleteTests()) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->displayDetailsOnIncompleteTests; + } + /** + * @psalm-assert-if-true !null $this->displayDetailsOnSkippedTests + */ + public function hasDisplayDetailsOnSkippedTests(): bool + { + return $this->displayDetailsOnSkippedTests !== null; + } + /** + * @throws Exception + */ + public function displayDetailsOnSkippedTests(): bool + { + if (!$this->hasDisplayDetailsOnSkippedTests()) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->displayDetailsOnSkippedTests; + } + /** + * @psalm-assert-if-true !null $this->displayDetailsOnTestsThatTriggerDeprecations + */ + public function hasDisplayDetailsOnTestsThatTriggerDeprecations(): bool + { + return $this->displayDetailsOnTestsThatTriggerDeprecations !== null; + } + /** + * @throws Exception + */ + public function displayDetailsOnTestsThatTriggerDeprecations(): bool + { + if (!$this->hasDisplayDetailsOnTestsThatTriggerDeprecations()) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->displayDetailsOnTestsThatTriggerDeprecations; + } + /** + * @psalm-assert-if-true !null $this->displayDetailsOnTestsThatTriggerErrors + */ + public function hasDisplayDetailsOnTestsThatTriggerErrors(): bool + { + return $this->displayDetailsOnTestsThatTriggerErrors !== null; + } + /** + * @throws Exception + */ + public function displayDetailsOnTestsThatTriggerErrors(): bool + { + if (!$this->hasDisplayDetailsOnTestsThatTriggerErrors()) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->displayDetailsOnTestsThatTriggerErrors; + } + /** + * @psalm-assert-if-true !null $this->displayDetailsOnTestsThatTriggerNotices + */ + public function hasDisplayDetailsOnTestsThatTriggerNotices(): bool + { + return $this->displayDetailsOnTestsThatTriggerNotices !== null; + } + /** + * @throws Exception + */ + public function displayDetailsOnTestsThatTriggerNotices(): bool + { + if (!$this->hasDisplayDetailsOnTestsThatTriggerNotices()) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->displayDetailsOnTestsThatTriggerNotices; + } + /** + * @psalm-assert-if-true !null $this->displayDetailsOnTestsThatTriggerWarnings + */ + public function hasDisplayDetailsOnTestsThatTriggerWarnings(): bool + { + return $this->displayDetailsOnTestsThatTriggerWarnings !== null; + } + /** + * @throws Exception + */ + public function displayDetailsOnTestsThatTriggerWarnings(): bool + { + if (!$this->hasDisplayDetailsOnTestsThatTriggerWarnings()) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->displayDetailsOnTestsThatTriggerWarnings; + } + public function version(): bool + { + return $this->version; + } + /** + * @psalm-assert-if-true !null $this->logEventsText + */ + public function hasLogEventsText(): bool + { + return $this->logEventsText !== null; + } + /** + * @throws Exception + */ + public function logEventsText(): string + { + if (!$this->hasLogEventsText()) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->logEventsText; + } + /** + * @psalm-assert-if-true !null $this->logEventsVerboseText + */ + public function hasLogEventsVerboseText(): bool + { + return $this->logEventsVerboseText !== null; + } + /** + * @throws Exception + */ + public function logEventsVerboseText(): string + { + if (!$this->hasLogEventsVerboseText()) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->logEventsVerboseText; + } + public function debug(): bool + { + return $this->debug; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\CliArguments; + +use RuntimeException; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class Exception extends RuntimeException implements \PHPUnit\Exception +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\CliArguments; + +use function getcwd; +use function is_dir; +use function is_file; +use function realpath; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class XmlConfigurationFileFinder +{ + public function find(\PHPUnit\TextUI\CliArguments\Configuration $configuration): false|string + { + $useDefaultConfiguration = $configuration->useDefaultConfiguration(); + if ($configuration->hasConfigurationFile()) { + if (is_dir($configuration->configurationFile())) { + $candidate = $this->configurationFileInDirectory($configuration->configurationFile()); + if ($candidate !== \false) { + return $candidate; + } + return \false; + } + return $configuration->configurationFile(); + } + if ($useDefaultConfiguration) { + $candidate = $this->configurationFileInDirectory(getcwd()); + if ($candidate !== \false) { + return $candidate; + } + } + return \false; + } + private function configurationFileInDirectory(string $directory): false|string + { + $candidates = [$directory . '/phpunit.xml', $directory . '/phpunit.dist.xml', $directory . '/phpunit.xml.dist']; + foreach ($candidates as $candidate) { + if (is_file($candidate)) { + return realpath($candidate); + } + } + return \false; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\Configuration; + +use function array_keys; +use function assert; +use PHPUnitPHAR\SebastianBergmann\CodeCoverage\Filter; +/** + * CLI options and XML configuration are static within a single PHPUnit 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 CodeCoverageFilterRegistry +{ + private static ?self $instance = null; + private ?Filter $filter = null; + private bool $configured = \false; + public static function instance(): self + { + if (self::$instance === null) { + self::$instance = new self(); + } + return self::$instance; + } + /** + * @codeCoverageIgnore + */ + public function get(): Filter + { + assert($this->filter !== null); + return $this->filter; + } + /** + * @codeCoverageIgnore + */ + public function init(\PHPUnit\TextUI\Configuration\Configuration $configuration, bool $force = \false): void + { + if (!$configuration->hasCoverageReport() && !$force) { + return; + } + if ($this->configured && !$force) { + return; + } + $this->filter = new Filter(); + if ($configuration->source()->notEmpty()) { + $this->filter->includeFiles(array_keys((new \PHPUnit\TextUI\Configuration\SourceMapper())->map($configuration->source()))); + $this->configured = \true; + } + } + /** + * @codeCoverageIgnore + */ + public function configured(): bool + { + return $this->configured; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\Configuration; + +/** + * @psalm-immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class Configuration +{ + public const COLOR_NEVER = 'never'; + public const COLOR_AUTO = 'auto'; + public const COLOR_ALWAYS = 'always'; + public const COLOR_DEFAULT = self::COLOR_NEVER; + /** + * @psalm-var list + */ + private readonly array $cliArguments; + private readonly ?string $configurationFile; + private readonly ?string $bootstrap; + private readonly bool $cacheResult; + private readonly ?string $cacheDirectory; + private readonly ?string $coverageCacheDirectory; + private readonly \PHPUnit\TextUI\Configuration\Source $source; + private readonly bool $pathCoverage; + private readonly ?string $coverageClover; + private readonly ?string $coverageCobertura; + private readonly ?string $coverageCrap4j; + private readonly int $coverageCrap4jThreshold; + private readonly ?string $coverageHtml; + private readonly int $coverageHtmlLowUpperBound; + private readonly int $coverageHtmlHighLowerBound; + private readonly string $coverageHtmlColorSuccessLow; + private readonly string $coverageHtmlColorSuccessMedium; + private readonly string $coverageHtmlColorSuccessHigh; + private readonly string $coverageHtmlColorWarning; + private readonly string $coverageHtmlColorDanger; + private readonly ?string $coverageHtmlCustomCssFile; + private readonly ?string $coveragePhp; + private readonly ?string $coverageText; + private readonly bool $coverageTextShowUncoveredFiles; + private readonly bool $coverageTextShowOnlySummary; + private readonly ?string $coverageXml; + private readonly string $testResultCacheFile; + private readonly bool $ignoreDeprecatedCodeUnitsFromCodeCoverage; + private readonly bool $disableCodeCoverageIgnore; + private readonly bool $failOnDeprecation; + private readonly bool $failOnEmptyTestSuite; + private readonly bool $failOnIncomplete; + private readonly bool $failOnNotice; + private readonly bool $failOnRisky; + private readonly bool $failOnSkipped; + private readonly bool $failOnWarning; + private readonly bool $stopOnDefect; + private readonly bool $stopOnDeprecation; + private readonly bool $stopOnError; + private readonly bool $stopOnFailure; + private readonly bool $stopOnIncomplete; + private readonly bool $stopOnNotice; + private readonly bool $stopOnRisky; + private readonly bool $stopOnSkipped; + private readonly bool $stopOnWarning; + private readonly bool $outputToStandardErrorStream; + private readonly int $columns; + private readonly bool $noExtensions; + /** + * @psalm-var ?non-empty-string + */ + private readonly ?string $pharExtensionDirectory; + /** + * @psalm-var list}> + */ + private readonly array $extensionBootstrappers; + private readonly bool $backupGlobals; + private readonly bool $backupStaticProperties; + private readonly bool $beStrictAboutChangesToGlobalState; + private readonly bool $colors; + private readonly bool $processIsolation; + private readonly bool $enforceTimeLimit; + private readonly int $defaultTimeLimit; + private readonly int $timeoutForSmallTests; + private readonly int $timeoutForMediumTests; + private readonly int $timeoutForLargeTests; + private readonly bool $reportUselessTests; + private readonly bool $strictCoverage; + private readonly bool $disallowTestOutput; + private readonly bool $displayDetailsOnIncompleteTests; + private readonly bool $displayDetailsOnSkippedTests; + private readonly bool $displayDetailsOnTestsThatTriggerDeprecations; + private readonly bool $displayDetailsOnTestsThatTriggerErrors; + private readonly bool $displayDetailsOnTestsThatTriggerNotices; + private readonly bool $displayDetailsOnTestsThatTriggerWarnings; + private readonly bool $reverseDefectList; + private readonly bool $requireCoverageMetadata; + private readonly bool $registerMockObjectsFromTestArgumentsRecursively; + private readonly bool $noProgress; + private readonly bool $noResults; + private readonly bool $noOutput; + private readonly int $executionOrder; + private readonly int $executionOrderDefects; + private readonly bool $resolveDependencies; + private readonly ?string $logfileTeamcity; + private readonly ?string $logfileJunit; + private readonly ?string $logfileTestdoxHtml; + private readonly ?string $logfileTestdoxText; + private readonly ?string $logEventsText; + private readonly ?string $logEventsVerboseText; + private readonly ?array $testsCovering; + private readonly ?array $testsUsing; + private readonly bool $teamCityOutput; + private readonly bool $testDoxOutput; + private readonly ?string $filter; + private readonly ?array $groups; + private readonly ?array $excludeGroups; + private readonly int $randomOrderSeed; + private readonly bool $includeUncoveredFiles; + private readonly \PHPUnit\TextUI\Configuration\TestSuiteCollection $testSuite; + private readonly string $includeTestSuite; + private readonly string $excludeTestSuite; + private readonly ?string $defaultTestSuite; + /** + * @psalm-var non-empty-list + */ + private readonly array $testSuffixes; + private readonly \PHPUnit\TextUI\Configuration\Php $php; + private readonly bool $controlGarbageCollector; + private readonly int $numberOfTestsBeforeGarbageCollection; + private readonly ?string $generateBaseline; + private readonly bool $debug; + /** + * @psalm-param list $cliArguments + * @psalm-param ?non-empty-string $pharExtensionDirectory + * @psalm-param non-empty-list $testSuffixes + * @psalm-param list}> $extensionBootstrappers + */ + public function __construct(array $cliArguments, ?string $configurationFile, ?string $bootstrap, bool $cacheResult, ?string $cacheDirectory, ?string $coverageCacheDirectory, \PHPUnit\TextUI\Configuration\Source $source, string $testResultCacheFile, ?string $coverageClover, ?string $coverageCobertura, ?string $coverageCrap4j, int $coverageCrap4jThreshold, ?string $coverageHtml, int $coverageHtmlLowUpperBound, int $coverageHtmlHighLowerBound, string $coverageHtmlColorSuccessLow, string $coverageHtmlColorSuccessMedium, string $coverageHtmlColorSuccessHigh, string $coverageHtmlColorWarning, string $coverageHtmlColorDanger, ?string $coverageHtmlCustomCssFile, ?string $coveragePhp, ?string $coverageText, bool $coverageTextShowUncoveredFiles, bool $coverageTextShowOnlySummary, ?string $coverageXml, bool $pathCoverage, bool $ignoreDeprecatedCodeUnitsFromCodeCoverage, bool $disableCodeCoverageIgnore, bool $failOnDeprecation, bool $failOnEmptyTestSuite, bool $failOnIncomplete, bool $failOnNotice, bool $failOnRisky, bool $failOnSkipped, bool $failOnWarning, bool $stopOnDefect, bool $stopOnDeprecation, bool $stopOnError, bool $stopOnFailure, bool $stopOnIncomplete, bool $stopOnNotice, bool $stopOnRisky, bool $stopOnSkipped, bool $stopOnWarning, bool $outputToStandardErrorStream, int|string $columns, bool $noExtensions, ?string $pharExtensionDirectory, array $extensionBootstrappers, bool $backupGlobals, bool $backupStaticProperties, bool $beStrictAboutChangesToGlobalState, bool $colors, bool $processIsolation, bool $enforceTimeLimit, int $defaultTimeLimit, int $timeoutForSmallTests, int $timeoutForMediumTests, int $timeoutForLargeTests, bool $reportUselessTests, bool $strictCoverage, bool $disallowTestOutput, bool $displayDetailsOnIncompleteTests, bool $displayDetailsOnSkippedTests, bool $displayDetailsOnTestsThatTriggerDeprecations, bool $displayDetailsOnTestsThatTriggerErrors, bool $displayDetailsOnTestsThatTriggerNotices, bool $displayDetailsOnTestsThatTriggerWarnings, bool $reverseDefectList, bool $requireCoverageMetadata, bool $registerMockObjectsFromTestArgumentsRecursively, bool $noProgress, bool $noResults, bool $noOutput, int $executionOrder, int $executionOrderDefects, bool $resolveDependencies, ?string $logfileTeamcity, ?string $logfileJunit, ?string $logfileTestdoxHtml, ?string $logfileTestdoxText, ?string $logEventsText, ?string $logEventsVerboseText, bool $teamCityOutput, bool $testDoxOutput, ?array $testsCovering, ?array $testsUsing, ?string $filter, ?array $groups, ?array $excludeGroups, int $randomOrderSeed, bool $includeUncoveredFiles, \PHPUnit\TextUI\Configuration\TestSuiteCollection $testSuite, string $includeTestSuite, string $excludeTestSuite, ?string $defaultTestSuite, array $testSuffixes, \PHPUnit\TextUI\Configuration\Php $php, bool $controlGarbageCollector, int $numberOfTestsBeforeGarbageCollection, ?string $generateBaseline, bool $debug) + { + $this->cliArguments = $cliArguments; + $this->configurationFile = $configurationFile; + $this->bootstrap = $bootstrap; + $this->cacheResult = $cacheResult; + $this->cacheDirectory = $cacheDirectory; + $this->coverageCacheDirectory = $coverageCacheDirectory; + $this->source = $source; + $this->testResultCacheFile = $testResultCacheFile; + $this->coverageClover = $coverageClover; + $this->coverageCobertura = $coverageCobertura; + $this->coverageCrap4j = $coverageCrap4j; + $this->coverageCrap4jThreshold = $coverageCrap4jThreshold; + $this->coverageHtml = $coverageHtml; + $this->coverageHtmlLowUpperBound = $coverageHtmlLowUpperBound; + $this->coverageHtmlHighLowerBound = $coverageHtmlHighLowerBound; + $this->coverageHtmlColorSuccessLow = $coverageHtmlColorSuccessLow; + $this->coverageHtmlColorSuccessMedium = $coverageHtmlColorSuccessMedium; + $this->coverageHtmlColorSuccessHigh = $coverageHtmlColorSuccessHigh; + $this->coverageHtmlColorWarning = $coverageHtmlColorWarning; + $this->coverageHtmlColorDanger = $coverageHtmlColorDanger; + $this->coverageHtmlCustomCssFile = $coverageHtmlCustomCssFile; + $this->coveragePhp = $coveragePhp; + $this->coverageText = $coverageText; + $this->coverageTextShowUncoveredFiles = $coverageTextShowUncoveredFiles; + $this->coverageTextShowOnlySummary = $coverageTextShowOnlySummary; + $this->coverageXml = $coverageXml; + $this->pathCoverage = $pathCoverage; + $this->ignoreDeprecatedCodeUnitsFromCodeCoverage = $ignoreDeprecatedCodeUnitsFromCodeCoverage; + $this->disableCodeCoverageIgnore = $disableCodeCoverageIgnore; + $this->failOnDeprecation = $failOnDeprecation; + $this->failOnEmptyTestSuite = $failOnEmptyTestSuite; + $this->failOnIncomplete = $failOnIncomplete; + $this->failOnNotice = $failOnNotice; + $this->failOnRisky = $failOnRisky; + $this->failOnSkipped = $failOnSkipped; + $this->failOnWarning = $failOnWarning; + $this->stopOnDefect = $stopOnDefect; + $this->stopOnDeprecation = $stopOnDeprecation; + $this->stopOnError = $stopOnError; + $this->stopOnFailure = $stopOnFailure; + $this->stopOnIncomplete = $stopOnIncomplete; + $this->stopOnNotice = $stopOnNotice; + $this->stopOnRisky = $stopOnRisky; + $this->stopOnSkipped = $stopOnSkipped; + $this->stopOnWarning = $stopOnWarning; + $this->outputToStandardErrorStream = $outputToStandardErrorStream; + $this->columns = $columns; + $this->noExtensions = $noExtensions; + $this->pharExtensionDirectory = $pharExtensionDirectory; + $this->extensionBootstrappers = $extensionBootstrappers; + $this->backupGlobals = $backupGlobals; + $this->backupStaticProperties = $backupStaticProperties; + $this->beStrictAboutChangesToGlobalState = $beStrictAboutChangesToGlobalState; + $this->colors = $colors; + $this->processIsolation = $processIsolation; + $this->enforceTimeLimit = $enforceTimeLimit; + $this->defaultTimeLimit = $defaultTimeLimit; + $this->timeoutForSmallTests = $timeoutForSmallTests; + $this->timeoutForMediumTests = $timeoutForMediumTests; + $this->timeoutForLargeTests = $timeoutForLargeTests; + $this->reportUselessTests = $reportUselessTests; + $this->strictCoverage = $strictCoverage; + $this->disallowTestOutput = $disallowTestOutput; + $this->displayDetailsOnIncompleteTests = $displayDetailsOnIncompleteTests; + $this->displayDetailsOnSkippedTests = $displayDetailsOnSkippedTests; + $this->displayDetailsOnTestsThatTriggerDeprecations = $displayDetailsOnTestsThatTriggerDeprecations; + $this->displayDetailsOnTestsThatTriggerErrors = $displayDetailsOnTestsThatTriggerErrors; + $this->displayDetailsOnTestsThatTriggerNotices = $displayDetailsOnTestsThatTriggerNotices; + $this->displayDetailsOnTestsThatTriggerWarnings = $displayDetailsOnTestsThatTriggerWarnings; + $this->reverseDefectList = $reverseDefectList; + $this->requireCoverageMetadata = $requireCoverageMetadata; + $this->registerMockObjectsFromTestArgumentsRecursively = $registerMockObjectsFromTestArgumentsRecursively; + $this->noProgress = $noProgress; + $this->noResults = $noResults; + $this->noOutput = $noOutput; + $this->executionOrder = $executionOrder; + $this->executionOrderDefects = $executionOrderDefects; + $this->resolveDependencies = $resolveDependencies; + $this->logfileTeamcity = $logfileTeamcity; + $this->logfileJunit = $logfileJunit; + $this->logfileTestdoxHtml = $logfileTestdoxHtml; + $this->logfileTestdoxText = $logfileTestdoxText; + $this->logEventsText = $logEventsText; + $this->logEventsVerboseText = $logEventsVerboseText; + $this->teamCityOutput = $teamCityOutput; + $this->testDoxOutput = $testDoxOutput; + $this->testsCovering = $testsCovering; + $this->testsUsing = $testsUsing; + $this->filter = $filter; + $this->groups = $groups; + $this->excludeGroups = $excludeGroups; + $this->randomOrderSeed = $randomOrderSeed; + $this->includeUncoveredFiles = $includeUncoveredFiles; + $this->testSuite = $testSuite; + $this->includeTestSuite = $includeTestSuite; + $this->excludeTestSuite = $excludeTestSuite; + $this->defaultTestSuite = $defaultTestSuite; + $this->testSuffixes = $testSuffixes; + $this->php = $php; + $this->controlGarbageCollector = $controlGarbageCollector; + $this->numberOfTestsBeforeGarbageCollection = $numberOfTestsBeforeGarbageCollection; + $this->generateBaseline = $generateBaseline; + $this->debug = $debug; + } + /** + * @psalm-assert-if-true !empty $this->cliArguments + */ + public function hasCliArguments(): bool + { + return !empty($this->cliArguments); + } + /** + * @psalm-return list + */ + public function cliArguments(): array + { + return $this->cliArguments; + } + /** + * @psalm-assert-if-true !empty $this->cliArguments + * + * @deprecated Use hasCliArguments() instead + */ + public function hasCliArgument(): bool + { + return !empty($this->cliArguments); + } + /** + * @throws NoCliArgumentException + * + * @return non-empty-string + * + * @deprecated Use cliArguments()[0] instead + */ + public function cliArgument(): string + { + if (!$this->hasCliArguments()) { + throw new \PHPUnit\TextUI\Configuration\NoCliArgumentException(); + } + return $this->cliArguments[0]; + } + /** + * @psalm-assert-if-true !null $this->configurationFile + */ + public function hasConfigurationFile(): bool + { + return $this->configurationFile !== null; + } + /** + * @throws NoConfigurationFileException + */ + public function configurationFile(): string + { + if (!$this->hasConfigurationFile()) { + throw new \PHPUnit\TextUI\Configuration\NoConfigurationFileException(); + } + return $this->configurationFile; + } + /** + * @psalm-assert-if-true !null $this->bootstrap + */ + public function hasBootstrap(): bool + { + return $this->bootstrap !== null; + } + /** + * @throws NoBootstrapException + */ + public function bootstrap(): string + { + if (!$this->hasBootstrap()) { + throw new \PHPUnit\TextUI\Configuration\NoBootstrapException(); + } + return $this->bootstrap; + } + public function cacheResult(): bool + { + return $this->cacheResult; + } + /** + * @psalm-assert-if-true !null $this->cacheDirectory + */ + public function hasCacheDirectory(): bool + { + return $this->cacheDirectory !== null; + } + /** + * @throws NoCacheDirectoryException + */ + public function cacheDirectory(): string + { + if (!$this->hasCacheDirectory()) { + throw new \PHPUnit\TextUI\Configuration\NoCacheDirectoryException(); + } + return $this->cacheDirectory; + } + /** + * @psalm-assert-if-true !null $this->coverageCacheDirectory + */ + public function hasCoverageCacheDirectory(): bool + { + return $this->coverageCacheDirectory !== null; + } + /** + * @throws NoCoverageCacheDirectoryException + */ + public function coverageCacheDirectory(): string + { + if (!$this->hasCoverageCacheDirectory()) { + throw new \PHPUnit\TextUI\Configuration\NoCoverageCacheDirectoryException(); + } + return $this->coverageCacheDirectory; + } + public function source(): \PHPUnit\TextUI\Configuration\Source + { + return $this->source; + } + /** + * @deprecated Use source()->restrictDeprecations() instead + */ + public function restrictDeprecations(): bool + { + return $this->source()->restrictDeprecations(); + } + /** + * @deprecated Use source()->restrictNotices() instead + */ + public function restrictNotices(): bool + { + return $this->source()->restrictNotices(); + } + /** + * @deprecated Use source()->restrictWarnings() instead + */ + public function restrictWarnings(): bool + { + return $this->source()->restrictWarnings(); + } + /** + * @deprecated Use source()->notEmpty() instead + */ + public function hasNonEmptyListOfFilesToBeIncludedInCodeCoverageReport(): bool + { + return $this->source->notEmpty(); + } + /** + * @deprecated Use source()->includeDirectories() instead + */ + public function coverageIncludeDirectories(): \PHPUnit\TextUI\Configuration\FilterDirectoryCollection + { + return $this->source()->includeDirectories(); + } + /** + * @deprecated Use source()->includeFiles() instead + */ + public function coverageIncludeFiles(): \PHPUnit\TextUI\Configuration\FileCollection + { + return $this->source()->includeFiles(); + } + /** + * @deprecated Use source()->excludeDirectories() instead + */ + public function coverageExcludeDirectories(): \PHPUnit\TextUI\Configuration\FilterDirectoryCollection + { + return $this->source()->excludeDirectories(); + } + /** + * @deprecated Use source()->excludeFiles() instead + */ + public function coverageExcludeFiles(): \PHPUnit\TextUI\Configuration\FileCollection + { + return $this->source()->excludeFiles(); + } + public function testResultCacheFile(): string + { + return $this->testResultCacheFile; + } + public function ignoreDeprecatedCodeUnitsFromCodeCoverage(): bool + { + return $this->ignoreDeprecatedCodeUnitsFromCodeCoverage; + } + public function disableCodeCoverageIgnore(): bool + { + return $this->disableCodeCoverageIgnore; + } + public function pathCoverage(): bool + { + return $this->pathCoverage; + } + public function hasCoverageReport(): bool + { + return $this->hasCoverageClover() || $this->hasCoverageCobertura() || $this->hasCoverageCrap4j() || $this->hasCoverageHtml() || $this->hasCoveragePhp() || $this->hasCoverageText() || $this->hasCoverageXml(); + } + /** + * @psalm-assert-if-true !null $this->coverageClover + */ + public function hasCoverageClover(): bool + { + return $this->coverageClover !== null; + } + /** + * @throws CodeCoverageReportNotConfiguredException + */ + public function coverageClover(): string + { + if (!$this->hasCoverageClover()) { + throw new \PHPUnit\TextUI\Configuration\CodeCoverageReportNotConfiguredException(); + } + return $this->coverageClover; + } + /** + * @psalm-assert-if-true !null $this->coverageCobertura + */ + public function hasCoverageCobertura(): bool + { + return $this->coverageCobertura !== null; + } + /** + * @throws CodeCoverageReportNotConfiguredException + */ + public function coverageCobertura(): string + { + if (!$this->hasCoverageCobertura()) { + throw new \PHPUnit\TextUI\Configuration\CodeCoverageReportNotConfiguredException(); + } + return $this->coverageCobertura; + } + /** + * @psalm-assert-if-true !null $this->coverageCrap4j + */ + public function hasCoverageCrap4j(): bool + { + return $this->coverageCrap4j !== null; + } + /** + * @throws CodeCoverageReportNotConfiguredException + */ + public function coverageCrap4j(): string + { + if (!$this->hasCoverageCrap4j()) { + throw new \PHPUnit\TextUI\Configuration\CodeCoverageReportNotConfiguredException(); + } + return $this->coverageCrap4j; + } + public function coverageCrap4jThreshold(): int + { + return $this->coverageCrap4jThreshold; + } + /** + * @psalm-assert-if-true !null $this->coverageHtml + */ + public function hasCoverageHtml(): bool + { + return $this->coverageHtml !== null; + } + /** + * @throws CodeCoverageReportNotConfiguredException + */ + public function coverageHtml(): string + { + if (!$this->hasCoverageHtml()) { + throw new \PHPUnit\TextUI\Configuration\CodeCoverageReportNotConfiguredException(); + } + return $this->coverageHtml; + } + public function coverageHtmlLowUpperBound(): int + { + return $this->coverageHtmlLowUpperBound; + } + public function coverageHtmlHighLowerBound(): int + { + return $this->coverageHtmlHighLowerBound; + } + public function coverageHtmlColorSuccessLow(): string + { + return $this->coverageHtmlColorSuccessLow; + } + public function coverageHtmlColorSuccessMedium(): string + { + return $this->coverageHtmlColorSuccessMedium; + } + public function coverageHtmlColorSuccessHigh(): string + { + return $this->coverageHtmlColorSuccessHigh; + } + public function coverageHtmlColorWarning(): string + { + return $this->coverageHtmlColorWarning; + } + public function coverageHtmlColorDanger(): string + { + return $this->coverageHtmlColorDanger; + } + /** + * @psalm-assert-if-true !null $this->coverageHtmlCustomCssFile + */ + public function hasCoverageHtmlCustomCssFile(): bool + { + return $this->coverageHtmlCustomCssFile !== null; + } + /** + * @throws NoCustomCssFileException + */ + public function coverageHtmlCustomCssFile(): string + { + if (!$this->hasCoverageHtmlCustomCssFile()) { + throw new \PHPUnit\TextUI\Configuration\NoCustomCssFileException(); + } + return $this->coverageHtmlCustomCssFile; + } + /** + * @psalm-assert-if-true !null $this->coveragePhp + */ + public function hasCoveragePhp(): bool + { + return $this->coveragePhp !== null; + } + /** + * @throws CodeCoverageReportNotConfiguredException + */ + public function coveragePhp(): string + { + if (!$this->hasCoveragePhp()) { + throw new \PHPUnit\TextUI\Configuration\CodeCoverageReportNotConfiguredException(); + } + return $this->coveragePhp; + } + /** + * @psalm-assert-if-true !null $this->coverageText + */ + public function hasCoverageText(): bool + { + return $this->coverageText !== null; + } + /** + * @throws CodeCoverageReportNotConfiguredException + */ + public function coverageText(): string + { + if (!$this->hasCoverageText()) { + throw new \PHPUnit\TextUI\Configuration\CodeCoverageReportNotConfiguredException(); + } + return $this->coverageText; + } + public function coverageTextShowUncoveredFiles(): bool + { + return $this->coverageTextShowUncoveredFiles; + } + public function coverageTextShowOnlySummary(): bool + { + return $this->coverageTextShowOnlySummary; + } + /** + * @psalm-assert-if-true !null $this->coverageXml + */ + public function hasCoverageXml(): bool + { + return $this->coverageXml !== null; + } + /** + * @throws CodeCoverageReportNotConfiguredException + */ + public function coverageXml(): string + { + if (!$this->hasCoverageXml()) { + throw new \PHPUnit\TextUI\Configuration\CodeCoverageReportNotConfiguredException(); + } + return $this->coverageXml; + } + public function failOnDeprecation(): bool + { + return $this->failOnDeprecation; + } + public function failOnEmptyTestSuite(): bool + { + return $this->failOnEmptyTestSuite; + } + public function failOnIncomplete(): bool + { + return $this->failOnIncomplete; + } + public function failOnNotice(): bool + { + return $this->failOnNotice; + } + public function failOnRisky(): bool + { + return $this->failOnRisky; + } + public function failOnSkipped(): bool + { + return $this->failOnSkipped; + } + public function failOnWarning(): bool + { + return $this->failOnWarning; + } + public function stopOnDefect(): bool + { + return $this->stopOnDefect; + } + public function stopOnDeprecation(): bool + { + return $this->stopOnDeprecation; + } + public function stopOnError(): bool + { + return $this->stopOnError; + } + public function stopOnFailure(): bool + { + return $this->stopOnFailure; + } + public function stopOnIncomplete(): bool + { + return $this->stopOnIncomplete; + } + public function stopOnNotice(): bool + { + return $this->stopOnNotice; + } + public function stopOnRisky(): bool + { + return $this->stopOnRisky; + } + public function stopOnSkipped(): bool + { + return $this->stopOnSkipped; + } + public function stopOnWarning(): bool + { + return $this->stopOnWarning; + } + public function outputToStandardErrorStream(): bool + { + return $this->outputToStandardErrorStream; + } + public function columns(): int + { + return $this->columns; + } + /** + * @deprecated Use noExtensions() instead + */ + public function loadPharExtensions(): bool + { + return $this->noExtensions; + } + public function noExtensions(): bool + { + return $this->noExtensions; + } + /** + * @psalm-assert-if-true !null $this->pharExtensionDirectory + */ + public function hasPharExtensionDirectory(): bool + { + return $this->pharExtensionDirectory !== null; + } + /** + * @psalm-return non-empty-string + * + * @throws NoPharExtensionDirectoryException + */ + public function pharExtensionDirectory(): string + { + if (!$this->hasPharExtensionDirectory()) { + throw new \PHPUnit\TextUI\Configuration\NoPharExtensionDirectoryException(); + } + return $this->pharExtensionDirectory; + } + /** + * @psalm-return list}> + */ + public function extensionBootstrappers(): array + { + return $this->extensionBootstrappers; + } + public function backupGlobals(): bool + { + return $this->backupGlobals; + } + public function backupStaticProperties(): bool + { + return $this->backupStaticProperties; + } + public function beStrictAboutChangesToGlobalState(): bool + { + return $this->beStrictAboutChangesToGlobalState; + } + public function colors(): bool + { + return $this->colors; + } + public function processIsolation(): bool + { + return $this->processIsolation; + } + public function enforceTimeLimit(): bool + { + return $this->enforceTimeLimit; + } + public function defaultTimeLimit(): int + { + return $this->defaultTimeLimit; + } + public function timeoutForSmallTests(): int + { + return $this->timeoutForSmallTests; + } + public function timeoutForMediumTests(): int + { + return $this->timeoutForMediumTests; + } + public function timeoutForLargeTests(): int + { + return $this->timeoutForLargeTests; + } + public function reportUselessTests(): bool + { + return $this->reportUselessTests; + } + public function strictCoverage(): bool + { + return $this->strictCoverage; + } + public function disallowTestOutput(): bool + { + return $this->disallowTestOutput; + } + public function displayDetailsOnIncompleteTests(): bool + { + return $this->displayDetailsOnIncompleteTests; + } + public function displayDetailsOnSkippedTests(): bool + { + return $this->displayDetailsOnSkippedTests; + } + public function displayDetailsOnTestsThatTriggerDeprecations(): bool + { + return $this->displayDetailsOnTestsThatTriggerDeprecations; + } + public function displayDetailsOnTestsThatTriggerErrors(): bool + { + return $this->displayDetailsOnTestsThatTriggerErrors; + } + public function displayDetailsOnTestsThatTriggerNotices(): bool + { + return $this->displayDetailsOnTestsThatTriggerNotices; + } + public function displayDetailsOnTestsThatTriggerWarnings(): bool + { + return $this->displayDetailsOnTestsThatTriggerWarnings; + } + public function reverseDefectList(): bool + { + return $this->reverseDefectList; + } + public function requireCoverageMetadata(): bool + { + return $this->requireCoverageMetadata; + } + /** + * @deprecated + */ + public function registerMockObjectsFromTestArgumentsRecursively(): bool + { + return $this->registerMockObjectsFromTestArgumentsRecursively; + } + public function noProgress(): bool + { + return $this->noProgress; + } + public function noResults(): bool + { + return $this->noResults; + } + public function noOutput(): bool + { + return $this->noOutput; + } + public function executionOrder(): int + { + return $this->executionOrder; + } + public function executionOrderDefects(): int + { + return $this->executionOrderDefects; + } + public function resolveDependencies(): bool + { + return $this->resolveDependencies; + } + /** + * @psalm-assert-if-true !null $this->logfileTeamcity + */ + public function hasLogfileTeamcity(): bool + { + return $this->logfileTeamcity !== null; + } + /** + * @throws LoggingNotConfiguredException + */ + public function logfileTeamcity(): string + { + if (!$this->hasLogfileTeamcity()) { + throw new \PHPUnit\TextUI\Configuration\LoggingNotConfiguredException(); + } + return $this->logfileTeamcity; + } + /** + * @psalm-assert-if-true !null $this->logfileJunit + */ + public function hasLogfileJunit(): bool + { + return $this->logfileJunit !== null; + } + /** + * @throws LoggingNotConfiguredException + */ + public function logfileJunit(): string + { + if (!$this->hasLogfileJunit()) { + throw new \PHPUnit\TextUI\Configuration\LoggingNotConfiguredException(); + } + return $this->logfileJunit; + } + /** + * @psalm-assert-if-true !null $this->logfileTestdoxHtml + */ + public function hasLogfileTestdoxHtml(): bool + { + return $this->logfileTestdoxHtml !== null; + } + /** + * @throws LoggingNotConfiguredException + */ + public function logfileTestdoxHtml(): string + { + if (!$this->hasLogfileTestdoxHtml()) { + throw new \PHPUnit\TextUI\Configuration\LoggingNotConfiguredException(); + } + return $this->logfileTestdoxHtml; + } + /** + * @psalm-assert-if-true !null $this->logfileTestdoxText + */ + public function hasLogfileTestdoxText(): bool + { + return $this->logfileTestdoxText !== null; + } + /** + * @throws LoggingNotConfiguredException + */ + public function logfileTestdoxText(): string + { + if (!$this->hasLogfileTestdoxText()) { + throw new \PHPUnit\TextUI\Configuration\LoggingNotConfiguredException(); + } + return $this->logfileTestdoxText; + } + /** + * @psalm-assert-if-true !null $this->logEventsText + */ + public function hasLogEventsText(): bool + { + return $this->logEventsText !== null; + } + /** + * @throws LoggingNotConfiguredException + */ + public function logEventsText(): string + { + if (!$this->hasLogEventsText()) { + throw new \PHPUnit\TextUI\Configuration\LoggingNotConfiguredException(); + } + return $this->logEventsText; + } + /** + * @psalm-assert-if-true !null $this->logEventsVerboseText + */ + public function hasLogEventsVerboseText(): bool + { + return $this->logEventsVerboseText !== null; + } + /** + * @throws LoggingNotConfiguredException + */ + public function logEventsVerboseText(): string + { + if (!$this->hasLogEventsVerboseText()) { + throw new \PHPUnit\TextUI\Configuration\LoggingNotConfiguredException(); + } + return $this->logEventsVerboseText; + } + public function outputIsTeamCity(): bool + { + return $this->teamCityOutput; + } + public function outputIsTestDox(): bool + { + return $this->testDoxOutput; + } + /** + * @psalm-assert-if-true !empty $this->testsCovering + */ + public function hasTestsCovering(): bool + { + return !empty($this->testsCovering); + } + /** + * @psalm-return list + * + * @throws FilterNotConfiguredException + */ + public function testsCovering(): array + { + if (!$this->hasTestsCovering()) { + throw new \PHPUnit\TextUI\Configuration\FilterNotConfiguredException(); + } + return $this->testsCovering; + } + /** + * @psalm-assert-if-true !empty $this->testsUsing + */ + public function hasTestsUsing(): bool + { + return !empty($this->testsUsing); + } + /** + * @psalm-return list + * + * @throws FilterNotConfiguredException + */ + public function testsUsing(): array + { + if (!$this->hasTestsUsing()) { + throw new \PHPUnit\TextUI\Configuration\FilterNotConfiguredException(); + } + return $this->testsUsing; + } + /** + * @psalm-assert-if-true !null $this->filter + */ + public function hasFilter(): bool + { + return $this->filter !== null; + } + /** + * @throws FilterNotConfiguredException + */ + public function filter(): string + { + if (!$this->hasFilter()) { + throw new \PHPUnit\TextUI\Configuration\FilterNotConfiguredException(); + } + return $this->filter; + } + /** + * @psalm-assert-if-true !empty $this->groups + */ + public function hasGroups(): bool + { + return !empty($this->groups); + } + /** + * @throws FilterNotConfiguredException + */ + public function groups(): array + { + if (!$this->hasGroups()) { + throw new \PHPUnit\TextUI\Configuration\FilterNotConfiguredException(); + } + return $this->groups; + } + /** + * @psalm-assert-if-true !empty $this->excludeGroups + */ + public function hasExcludeGroups(): bool + { + return !empty($this->excludeGroups); + } + /** + * @throws FilterNotConfiguredException + */ + public function excludeGroups(): array + { + if (!$this->hasExcludeGroups()) { + throw new \PHPUnit\TextUI\Configuration\FilterNotConfiguredException(); + } + return $this->excludeGroups; + } + public function randomOrderSeed(): int + { + return $this->randomOrderSeed; + } + public function includeUncoveredFiles(): bool + { + return $this->includeUncoveredFiles; + } + public function testSuite(): \PHPUnit\TextUI\Configuration\TestSuiteCollection + { + return $this->testSuite; + } + public function includeTestSuite(): string + { + return $this->includeTestSuite; + } + public function excludeTestSuite(): string + { + return $this->excludeTestSuite; + } + /** + * @psalm-assert-if-true !null $this->defaultTestSuite + */ + public function hasDefaultTestSuite(): bool + { + return $this->defaultTestSuite !== null; + } + /** + * @throws NoDefaultTestSuiteException + */ + public function defaultTestSuite(): string + { + if (!$this->hasDefaultTestSuite()) { + throw new \PHPUnit\TextUI\Configuration\NoDefaultTestSuiteException(); + } + return $this->defaultTestSuite; + } + /** + * @psalm-return non-empty-list + */ + public function testSuffixes(): array + { + return $this->testSuffixes; + } + public function php(): \PHPUnit\TextUI\Configuration\Php + { + return $this->php; + } + public function controlGarbageCollector(): bool + { + return $this->controlGarbageCollector; + } + public function numberOfTestsBeforeGarbageCollection(): int + { + return $this->numberOfTestsBeforeGarbageCollection; + } + /** + * @psalm-assert-if-true !null $this->generateBaseline + */ + public function hasGenerateBaseline(): bool + { + return $this->generateBaseline !== null; + } + /** + * @throws NoBaselineException + */ + public function generateBaseline(): string + { + if (!$this->hasGenerateBaseline()) { + throw new \PHPUnit\TextUI\Configuration\NoBaselineException(); + } + return $this->generateBaseline; + } + public function debug(): bool + { + return $this->debug; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration; + +use PHPUnit\TextUI\Configuration\Exception; +use RuntimeException; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class CannotFindSchemaException extends RuntimeException implements Exception +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\Configuration; + +use RuntimeException; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class CodeCoverageReportNotConfiguredException extends RuntimeException implements \PHPUnit\TextUI\Configuration\Exception +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\Configuration; + +use RuntimeException; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class ConfigurationCannotBeBuiltException extends RuntimeException implements \PHPUnit\TextUI\Configuration\Exception +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\Configuration; + +/** + * @internal This interface is not covered by the backward compatibility promise for PHPUnit + */ +interface Exception extends \PHPUnit\TextUI\Exception +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\Configuration; + +use RuntimeException; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class FilterNotConfiguredException extends RuntimeException implements \PHPUnit\TextUI\Configuration\Exception +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\Configuration; + +use RuntimeException; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class IncludePathNotConfiguredException extends RuntimeException implements \PHPUnit\TextUI\Configuration\Exception +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\Configuration; + +use RuntimeException; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class LoggingNotConfiguredException extends RuntimeException implements \PHPUnit\TextUI\Configuration\Exception +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\Configuration; + +use RuntimeException; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class NoBaselineException extends RuntimeException implements \PHPUnit\TextUI\Configuration\Exception +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\Configuration; + +use RuntimeException; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class NoBootstrapException extends RuntimeException implements \PHPUnit\TextUI\Configuration\Exception +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\Configuration; + +use RuntimeException; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class NoCacheDirectoryException extends RuntimeException implements \PHPUnit\TextUI\Configuration\Exception +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\Configuration; + +use RuntimeException; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class NoCliArgumentException extends RuntimeException implements \PHPUnit\TextUI\Configuration\Exception +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\Configuration; + +use RuntimeException; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class NoConfigurationFileException extends RuntimeException implements \PHPUnit\TextUI\Configuration\Exception +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\Configuration; + +use RuntimeException; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class NoCoverageCacheDirectoryException extends RuntimeException implements \PHPUnit\TextUI\Configuration\Exception +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\Configuration; + +use RuntimeException; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class NoCustomCssFileException extends RuntimeException implements \PHPUnit\TextUI\Configuration\Exception +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\Configuration; + +use RuntimeException; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class NoDefaultTestSuiteException extends RuntimeException implements \PHPUnit\TextUI\Configuration\Exception +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\Configuration; + +use RuntimeException; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class NoPharExtensionDirectoryException extends RuntimeException implements \PHPUnit\TextUI\Configuration\Exception +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\Configuration; + +use const DIRECTORY_SEPARATOR; +use function array_diff; +use function assert; +use function dirname; +use function explode; +use function is_int; +use function realpath; +use function time; +use PHPUnit\Event\Facade as EventFacade; +use PHPUnit\Runner\TestSuiteSorter; +use PHPUnit\TextUI\CliArguments\Configuration as CliConfiguration; +use PHPUnit\TextUI\CliArguments\Exception; +use PHPUnit\TextUI\XmlConfiguration\Configuration as XmlConfiguration; +use PHPUnit\TextUI\XmlConfiguration\LoadedFromFileConfiguration; +use PHPUnit\TextUI\XmlConfiguration\SchemaDetector; +use PHPUnit\Util\Filesystem; +use PHPUnitPHAR\SebastianBergmann\CodeCoverage\Report\Html\Colors; +use PHPUnitPHAR\SebastianBergmann\CodeCoverage\Report\Thresholds; +use PHPUnitPHAR\SebastianBergmann\Environment\Console; +use PHPUnitPHAR\SebastianBergmann\Invoker\Invoker; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class Merger +{ + /** + * @throws \PHPUnit\TextUI\XmlConfiguration\Exception + * @throws Exception + * @throws NoCustomCssFileException + */ + public function merge(CliConfiguration $cliConfiguration, XmlConfiguration $xmlConfiguration): \PHPUnit\TextUI\Configuration\Configuration + { + $configurationFile = null; + if ($xmlConfiguration->wasLoadedFromFile()) { + assert($xmlConfiguration instanceof LoadedFromFileConfiguration); + $configurationFile = $xmlConfiguration->filename(); + } + $bootstrap = null; + if ($cliConfiguration->hasBootstrap()) { + $bootstrap = $cliConfiguration->bootstrap(); + } elseif ($xmlConfiguration->phpunit()->hasBootstrap()) { + $bootstrap = $xmlConfiguration->phpunit()->bootstrap(); + } + if ($cliConfiguration->hasCacheResult()) { + $cacheResult = $cliConfiguration->cacheResult(); + } else { + $cacheResult = $xmlConfiguration->phpunit()->cacheResult(); + } + $cacheDirectory = null; + $coverageCacheDirectory = null; + if ($cliConfiguration->hasCacheDirectory() && Filesystem::createDirectory($cliConfiguration->cacheDirectory())) { + $cacheDirectory = realpath($cliConfiguration->cacheDirectory()); + } elseif ($xmlConfiguration->phpunit()->hasCacheDirectory() && Filesystem::createDirectory($xmlConfiguration->phpunit()->cacheDirectory())) { + $cacheDirectory = realpath($xmlConfiguration->phpunit()->cacheDirectory()); + } + if ($cacheDirectory !== null) { + $coverageCacheDirectory = $cacheDirectory . DIRECTORY_SEPARATOR . 'code-coverage'; + $testResultCacheFile = $cacheDirectory . DIRECTORY_SEPARATOR . 'test-results'; + } + if ($coverageCacheDirectory === null) { + if ($cliConfiguration->hasCoverageCacheDirectory() && Filesystem::createDirectory($cliConfiguration->coverageCacheDirectory())) { + $coverageCacheDirectory = realpath($cliConfiguration->coverageCacheDirectory()); + } elseif ($xmlConfiguration->codeCoverage()->hasCacheDirectory()) { + $coverageCacheDirectory = $xmlConfiguration->codeCoverage()->cacheDirectory()->path(); + } + } + if (!isset($testResultCacheFile)) { + if ($cliConfiguration->hasCacheResultFile()) { + $testResultCacheFile = $cliConfiguration->cacheResultFile(); + } elseif ($xmlConfiguration->phpunit()->hasCacheResultFile()) { + $testResultCacheFile = $xmlConfiguration->phpunit()->cacheResultFile(); + } elseif ($xmlConfiguration->wasLoadedFromFile()) { + $testResultCacheFile = dirname(realpath($xmlConfiguration->filename())) . DIRECTORY_SEPARATOR . '.phpunit.result.cache'; + } else { + $candidate = realpath($_SERVER['PHP_SELF']); + if ($candidate) { + $testResultCacheFile = dirname($candidate) . DIRECTORY_SEPARATOR . '.phpunit.result.cache'; + } else { + $testResultCacheFile = '.phpunit.result.cache'; + } + } + } + if ($cliConfiguration->hasDisableCodeCoverageIgnore()) { + $disableCodeCoverageIgnore = $cliConfiguration->disableCodeCoverageIgnore(); + } else { + $disableCodeCoverageIgnore = $xmlConfiguration->codeCoverage()->disableCodeCoverageIgnore(); + } + if ($cliConfiguration->hasFailOnDeprecation()) { + $failOnDeprecation = $cliConfiguration->failOnDeprecation(); + } else { + $failOnDeprecation = $xmlConfiguration->phpunit()->failOnDeprecation(); + } + if ($cliConfiguration->hasFailOnEmptyTestSuite()) { + $failOnEmptyTestSuite = $cliConfiguration->failOnEmptyTestSuite(); + } else { + $failOnEmptyTestSuite = $xmlConfiguration->phpunit()->failOnEmptyTestSuite(); + } + if ($cliConfiguration->hasFailOnIncomplete()) { + $failOnIncomplete = $cliConfiguration->failOnIncomplete(); + } else { + $failOnIncomplete = $xmlConfiguration->phpunit()->failOnIncomplete(); + } + if ($cliConfiguration->hasFailOnNotice()) { + $failOnNotice = $cliConfiguration->failOnNotice(); + } else { + $failOnNotice = $xmlConfiguration->phpunit()->failOnNotice(); + } + if ($cliConfiguration->hasFailOnRisky()) { + $failOnRisky = $cliConfiguration->failOnRisky(); + } else { + $failOnRisky = $xmlConfiguration->phpunit()->failOnRisky(); + } + if ($cliConfiguration->hasFailOnSkipped()) { + $failOnSkipped = $cliConfiguration->failOnSkipped(); + } else { + $failOnSkipped = $xmlConfiguration->phpunit()->failOnSkipped(); + } + if ($cliConfiguration->hasFailOnWarning()) { + $failOnWarning = $cliConfiguration->failOnWarning(); + } else { + $failOnWarning = $xmlConfiguration->phpunit()->failOnWarning(); + } + if ($cliConfiguration->hasStopOnDefect()) { + $stopOnDefect = $cliConfiguration->stopOnDefect(); + } else { + $stopOnDefect = $xmlConfiguration->phpunit()->stopOnDefect(); + } + if ($cliConfiguration->hasStopOnDeprecation()) { + $stopOnDeprecation = $cliConfiguration->stopOnDeprecation(); + } else { + $stopOnDeprecation = $xmlConfiguration->phpunit()->stopOnDeprecation(); + } + if ($cliConfiguration->hasStopOnError()) { + $stopOnError = $cliConfiguration->stopOnError(); + } else { + $stopOnError = $xmlConfiguration->phpunit()->stopOnError(); + } + if ($cliConfiguration->hasStopOnFailure()) { + $stopOnFailure = $cliConfiguration->stopOnFailure(); + } else { + $stopOnFailure = $xmlConfiguration->phpunit()->stopOnFailure(); + } + if ($cliConfiguration->hasStopOnIncomplete()) { + $stopOnIncomplete = $cliConfiguration->stopOnIncomplete(); + } else { + $stopOnIncomplete = $xmlConfiguration->phpunit()->stopOnIncomplete(); + } + if ($cliConfiguration->hasStopOnNotice()) { + $stopOnNotice = $cliConfiguration->stopOnNotice(); + } else { + $stopOnNotice = $xmlConfiguration->phpunit()->stopOnNotice(); + } + if ($cliConfiguration->hasStopOnRisky()) { + $stopOnRisky = $cliConfiguration->stopOnRisky(); + } else { + $stopOnRisky = $xmlConfiguration->phpunit()->stopOnRisky(); + } + if ($cliConfiguration->hasStopOnSkipped()) { + $stopOnSkipped = $cliConfiguration->stopOnSkipped(); + } else { + $stopOnSkipped = $xmlConfiguration->phpunit()->stopOnSkipped(); + } + if ($cliConfiguration->hasStopOnWarning()) { + $stopOnWarning = $cliConfiguration->stopOnWarning(); + } else { + $stopOnWarning = $xmlConfiguration->phpunit()->stopOnWarning(); + } + if ($cliConfiguration->hasStderr() && $cliConfiguration->stderr()) { + $outputToStandardErrorStream = \true; + } else { + $outputToStandardErrorStream = $xmlConfiguration->phpunit()->stderr(); + } + if ($cliConfiguration->hasColumns()) { + $columns = $cliConfiguration->columns(); + } else { + $columns = $xmlConfiguration->phpunit()->columns(); + } + if ($columns === 'max') { + $columns = (new Console())->getNumberOfColumns(); + } + if ($columns < 16) { + $columns = 16; + EventFacade::emitter()->testRunnerTriggeredWarning('Less than 16 columns requested, number of columns set to 16'); + } + assert(is_int($columns)); + $noExtensions = \false; + if ($cliConfiguration->hasNoExtensions() && $cliConfiguration->noExtensions()) { + $noExtensions = \true; + } + $pharExtensionDirectory = null; + if ($xmlConfiguration->phpunit()->hasExtensionsDirectory()) { + $pharExtensionDirectory = $xmlConfiguration->phpunit()->extensionsDirectory(); + } + $extensionBootstrappers = []; + foreach ($xmlConfiguration->extensions() as $extension) { + $extensionBootstrappers[] = ['className' => $extension->className(), 'parameters' => $extension->parameters()]; + } + if ($cliConfiguration->hasPathCoverage() && $cliConfiguration->pathCoverage()) { + $pathCoverage = $cliConfiguration->pathCoverage(); + } else { + $pathCoverage = $xmlConfiguration->codeCoverage()->pathCoverage(); + } + $defaultColors = Colors::default(); + $defaultThresholds = Thresholds::default(); + $coverageClover = null; + $coverageCobertura = null; + $coverageCrap4j = null; + $coverageCrap4jThreshold = 30; + $coverageHtml = null; + $coverageHtmlLowUpperBound = $defaultThresholds->lowUpperBound(); + $coverageHtmlHighLowerBound = $defaultThresholds->highLowerBound(); + $coverageHtmlColorSuccessLow = $defaultColors->successLow(); + $coverageHtmlColorSuccessMedium = $defaultColors->successMedium(); + $coverageHtmlColorSuccessHigh = $defaultColors->successHigh(); + $coverageHtmlColorWarning = $defaultColors->warning(); + $coverageHtmlColorDanger = $defaultColors->danger(); + $coverageHtmlCustomCssFile = null; + $coveragePhp = null; + $coverageText = null; + $coverageTextShowUncoveredFiles = \false; + $coverageTextShowOnlySummary = \false; + $coverageXml = null; + $coverageFromXmlConfiguration = \true; + if ($cliConfiguration->hasNoCoverage() && $cliConfiguration->noCoverage()) { + $coverageFromXmlConfiguration = \false; + } + if ($cliConfiguration->hasCoverageClover()) { + $coverageClover = $cliConfiguration->coverageClover(); + } elseif ($coverageFromXmlConfiguration && $xmlConfiguration->codeCoverage()->hasClover()) { + $coverageClover = $xmlConfiguration->codeCoverage()->clover()->target()->path(); + } + if ($cliConfiguration->hasCoverageCobertura()) { + $coverageCobertura = $cliConfiguration->coverageCobertura(); + } elseif ($coverageFromXmlConfiguration && $xmlConfiguration->codeCoverage()->hasCobertura()) { + $coverageCobertura = $xmlConfiguration->codeCoverage()->cobertura()->target()->path(); + } + if ($xmlConfiguration->codeCoverage()->hasCrap4j()) { + $coverageCrap4jThreshold = $xmlConfiguration->codeCoverage()->crap4j()->threshold(); + } + if ($cliConfiguration->hasCoverageCrap4J()) { + $coverageCrap4j = $cliConfiguration->coverageCrap4J(); + } elseif ($coverageFromXmlConfiguration && $xmlConfiguration->codeCoverage()->hasCrap4j()) { + $coverageCrap4j = $xmlConfiguration->codeCoverage()->crap4j()->target()->path(); + } + if ($xmlConfiguration->codeCoverage()->hasHtml()) { + $coverageHtmlHighLowerBound = $xmlConfiguration->codeCoverage()->html()->highLowerBound(); + $coverageHtmlLowUpperBound = $xmlConfiguration->codeCoverage()->html()->lowUpperBound(); + if ($coverageHtmlLowUpperBound > $coverageHtmlHighLowerBound) { + $coverageHtmlLowUpperBound = $defaultThresholds->lowUpperBound(); + $coverageHtmlHighLowerBound = $defaultThresholds->highLowerBound(); + } + $coverageHtmlColorSuccessLow = $xmlConfiguration->codeCoverage()->html()->colorSuccessLow(); + $coverageHtmlColorSuccessMedium = $xmlConfiguration->codeCoverage()->html()->colorSuccessMedium(); + $coverageHtmlColorSuccessHigh = $xmlConfiguration->codeCoverage()->html()->colorSuccessHigh(); + $coverageHtmlColorWarning = $xmlConfiguration->codeCoverage()->html()->colorWarning(); + $coverageHtmlColorDanger = $xmlConfiguration->codeCoverage()->html()->colorDanger(); + if ($xmlConfiguration->codeCoverage()->html()->hasCustomCssFile()) { + $coverageHtmlCustomCssFile = $xmlConfiguration->codeCoverage()->html()->customCssFile(); + } + } + if ($cliConfiguration->hasCoverageHtml()) { + $coverageHtml = $cliConfiguration->coverageHtml(); + } elseif ($coverageFromXmlConfiguration && $xmlConfiguration->codeCoverage()->hasHtml()) { + $coverageHtml = $xmlConfiguration->codeCoverage()->html()->target()->path(); + } + if ($cliConfiguration->hasCoveragePhp()) { + $coveragePhp = $cliConfiguration->coveragePhp(); + } elseif ($coverageFromXmlConfiguration && $xmlConfiguration->codeCoverage()->hasPhp()) { + $coveragePhp = $xmlConfiguration->codeCoverage()->php()->target()->path(); + } + if ($xmlConfiguration->codeCoverage()->hasText()) { + $coverageTextShowUncoveredFiles = $xmlConfiguration->codeCoverage()->text()->showUncoveredFiles(); + $coverageTextShowOnlySummary = $xmlConfiguration->codeCoverage()->text()->showOnlySummary(); + } + if ($cliConfiguration->hasCoverageTextShowUncoveredFiles()) { + $coverageTextShowUncoveredFiles = $cliConfiguration->coverageTextShowUncoveredFiles(); + } + if ($cliConfiguration->hasCoverageTextShowOnlySummary()) { + $coverageTextShowOnlySummary = $cliConfiguration->coverageTextShowOnlySummary(); + } + if ($cliConfiguration->hasCoverageText()) { + $coverageText = $cliConfiguration->coverageText(); + } elseif ($coverageFromXmlConfiguration && $xmlConfiguration->codeCoverage()->hasText()) { + $coverageText = $xmlConfiguration->codeCoverage()->text()->target()->path(); + } + if ($cliConfiguration->hasCoverageXml()) { + $coverageXml = $cliConfiguration->coverageXml(); + } elseif ($coverageFromXmlConfiguration && $xmlConfiguration->codeCoverage()->hasXml()) { + $coverageXml = $xmlConfiguration->codeCoverage()->xml()->target()->path(); + } + if ($cliConfiguration->hasBackupGlobals()) { + $backupGlobals = $cliConfiguration->backupGlobals(); + } else { + $backupGlobals = $xmlConfiguration->phpunit()->backupGlobals(); + } + if ($cliConfiguration->hasBackupStaticProperties()) { + $backupStaticProperties = $cliConfiguration->backupStaticProperties(); + } else { + $backupStaticProperties = $xmlConfiguration->phpunit()->backupStaticProperties(); + } + if ($cliConfiguration->hasBeStrictAboutChangesToGlobalState()) { + $beStrictAboutChangesToGlobalState = $cliConfiguration->beStrictAboutChangesToGlobalState(); + } else { + $beStrictAboutChangesToGlobalState = $xmlConfiguration->phpunit()->beStrictAboutChangesToGlobalState(); + } + if ($cliConfiguration->hasProcessIsolation()) { + $processIsolation = $cliConfiguration->processIsolation(); + } else { + $processIsolation = $xmlConfiguration->phpunit()->processIsolation(); + } + if ($cliConfiguration->hasEnforceTimeLimit()) { + $enforceTimeLimit = $cliConfiguration->enforceTimeLimit(); + } else { + $enforceTimeLimit = $xmlConfiguration->phpunit()->enforceTimeLimit(); + } + if ($enforceTimeLimit && !(new Invoker())->canInvokeWithTimeout()) { + EventFacade::emitter()->testRunnerTriggeredWarning('The pcntl extension is required for enforcing time limits'); + } + if ($cliConfiguration->hasDefaultTimeLimit()) { + $defaultTimeLimit = $cliConfiguration->defaultTimeLimit(); + } else { + $defaultTimeLimit = $xmlConfiguration->phpunit()->defaultTimeLimit(); + } + $timeoutForSmallTests = $xmlConfiguration->phpunit()->timeoutForSmallTests(); + $timeoutForMediumTests = $xmlConfiguration->phpunit()->timeoutForMediumTests(); + $timeoutForLargeTests = $xmlConfiguration->phpunit()->timeoutForLargeTests(); + if ($cliConfiguration->hasReportUselessTests()) { + $reportUselessTests = $cliConfiguration->reportUselessTests(); + } else { + $reportUselessTests = $xmlConfiguration->phpunit()->beStrictAboutTestsThatDoNotTestAnything(); + } + if ($cliConfiguration->hasStrictCoverage()) { + $strictCoverage = $cliConfiguration->strictCoverage(); + } else { + $strictCoverage = $xmlConfiguration->phpunit()->beStrictAboutCoverageMetadata(); + } + if ($cliConfiguration->hasDisallowTestOutput()) { + $disallowTestOutput = $cliConfiguration->disallowTestOutput(); + } else { + $disallowTestOutput = $xmlConfiguration->phpunit()->beStrictAboutOutputDuringTests(); + } + if ($cliConfiguration->hasDisplayDetailsOnIncompleteTests()) { + $displayDetailsOnIncompleteTests = $cliConfiguration->displayDetailsOnIncompleteTests(); + } else { + $displayDetailsOnIncompleteTests = $xmlConfiguration->phpunit()->displayDetailsOnIncompleteTests(); + } + if ($cliConfiguration->hasDisplayDetailsOnSkippedTests()) { + $displayDetailsOnSkippedTests = $cliConfiguration->displayDetailsOnSkippedTests(); + } else { + $displayDetailsOnSkippedTests = $xmlConfiguration->phpunit()->displayDetailsOnSkippedTests(); + } + if ($cliConfiguration->hasDisplayDetailsOnTestsThatTriggerDeprecations()) { + $displayDetailsOnTestsThatTriggerDeprecations = $cliConfiguration->displayDetailsOnTestsThatTriggerDeprecations(); + } else { + $displayDetailsOnTestsThatTriggerDeprecations = $xmlConfiguration->phpunit()->displayDetailsOnTestsThatTriggerDeprecations(); + } + if ($cliConfiguration->hasDisplayDetailsOnTestsThatTriggerErrors()) { + $displayDetailsOnTestsThatTriggerErrors = $cliConfiguration->displayDetailsOnTestsThatTriggerErrors(); + } else { + $displayDetailsOnTestsThatTriggerErrors = $xmlConfiguration->phpunit()->displayDetailsOnTestsThatTriggerErrors(); + } + if ($cliConfiguration->hasDisplayDetailsOnTestsThatTriggerNotices()) { + $displayDetailsOnTestsThatTriggerNotices = $cliConfiguration->displayDetailsOnTestsThatTriggerNotices(); + } else { + $displayDetailsOnTestsThatTriggerNotices = $xmlConfiguration->phpunit()->displayDetailsOnTestsThatTriggerNotices(); + } + if ($cliConfiguration->hasDisplayDetailsOnTestsThatTriggerWarnings()) { + $displayDetailsOnTestsThatTriggerWarnings = $cliConfiguration->displayDetailsOnTestsThatTriggerWarnings(); + } else { + $displayDetailsOnTestsThatTriggerWarnings = $xmlConfiguration->phpunit()->displayDetailsOnTestsThatTriggerWarnings(); + } + if ($cliConfiguration->hasReverseList()) { + $reverseDefectList = $cliConfiguration->reverseList(); + } else { + $reverseDefectList = $xmlConfiguration->phpunit()->reverseDefectList(); + } + $requireCoverageMetadata = $xmlConfiguration->phpunit()->requireCoverageMetadata(); + $registerMockObjectsFromTestArgumentsRecursively = $xmlConfiguration->phpunit()->registerMockObjectsFromTestArgumentsRecursively(); + if ($cliConfiguration->hasExecutionOrder()) { + $executionOrder = $cliConfiguration->executionOrder(); + } else { + $executionOrder = $xmlConfiguration->phpunit()->executionOrder(); + } + $executionOrderDefects = TestSuiteSorter::ORDER_DEFAULT; + if ($cliConfiguration->hasExecutionOrderDefects()) { + $executionOrderDefects = $cliConfiguration->executionOrderDefects(); + } elseif ($xmlConfiguration->phpunit()->defectsFirst()) { + $executionOrderDefects = TestSuiteSorter::ORDER_DEFECTS_FIRST; + } + if ($cliConfiguration->hasResolveDependencies()) { + $resolveDependencies = $cliConfiguration->resolveDependencies(); + } else { + $resolveDependencies = $xmlConfiguration->phpunit()->resolveDependencies(); + } + $colors = \false; + $colorsSupported = (new Console())->hasColorSupport(); + if ($cliConfiguration->hasColors()) { + if ($cliConfiguration->colors() === \PHPUnit\TextUI\Configuration\Configuration::COLOR_ALWAYS) { + $colors = \true; + } elseif ($colorsSupported && $cliConfiguration->colors() === \PHPUnit\TextUI\Configuration\Configuration::COLOR_AUTO) { + $colors = \true; + } + } elseif ($xmlConfiguration->phpunit()->colors() === \PHPUnit\TextUI\Configuration\Configuration::COLOR_ALWAYS) { + $colors = \true; + } elseif ($colorsSupported && $xmlConfiguration->phpunit()->colors() === \PHPUnit\TextUI\Configuration\Configuration::COLOR_AUTO) { + $colors = \true; + } + $logfileTeamcity = null; + $logfileJunit = null; + $logfileTestdoxHtml = null; + $logfileTestdoxText = null; + $loggingFromXmlConfiguration = \true; + if ($cliConfiguration->hasNoLogging() && $cliConfiguration->noLogging()) { + $loggingFromXmlConfiguration = \false; + } + if ($cliConfiguration->hasTeamcityLogfile()) { + $logfileTeamcity = $cliConfiguration->teamcityLogfile(); + } elseif ($loggingFromXmlConfiguration && $xmlConfiguration->logging()->hasTeamCity()) { + $logfileTeamcity = $xmlConfiguration->logging()->teamCity()->target()->path(); + } + if ($cliConfiguration->hasJunitLogfile()) { + $logfileJunit = $cliConfiguration->junitLogfile(); + } elseif ($loggingFromXmlConfiguration && $xmlConfiguration->logging()->hasJunit()) { + $logfileJunit = $xmlConfiguration->logging()->junit()->target()->path(); + } + if ($cliConfiguration->hasTestdoxHtmlFile()) { + $logfileTestdoxHtml = $cliConfiguration->testdoxHtmlFile(); + } elseif ($loggingFromXmlConfiguration && $xmlConfiguration->logging()->hasTestDoxHtml()) { + $logfileTestdoxHtml = $xmlConfiguration->logging()->testDoxHtml()->target()->path(); + } + if ($cliConfiguration->hasTestdoxTextFile()) { + $logfileTestdoxText = $cliConfiguration->testdoxTextFile(); + } elseif ($loggingFromXmlConfiguration && $xmlConfiguration->logging()->hasTestDoxText()) { + $logfileTestdoxText = $xmlConfiguration->logging()->testDoxText()->target()->path(); + } + $logEventsText = null; + if ($cliConfiguration->hasLogEventsText()) { + $logEventsText = $cliConfiguration->logEventsText(); + } + $logEventsVerboseText = null; + if ($cliConfiguration->hasLogEventsVerboseText()) { + $logEventsVerboseText = $cliConfiguration->logEventsVerboseText(); + } + $teamCityOutput = \false; + if ($cliConfiguration->hasTeamCityPrinter() && $cliConfiguration->teamCityPrinter()) { + $teamCityOutput = \true; + } + if ($cliConfiguration->hasTestDoxPrinter() && $cliConfiguration->testdoxPrinter()) { + $testDoxOutput = \true; + } else { + $testDoxOutput = $xmlConfiguration->phpunit()->testdoxPrinter(); + } + $noProgress = \false; + if ($cliConfiguration->hasNoProgress() && $cliConfiguration->noProgress()) { + $noProgress = \true; + } + $noResults = \false; + if ($cliConfiguration->hasNoResults() && $cliConfiguration->noResults()) { + $noResults = \true; + } + $noOutput = \false; + if ($cliConfiguration->hasNoOutput() && $cliConfiguration->noOutput()) { + $noOutput = \true; + } + $testsCovering = null; + if ($cliConfiguration->hasTestsCovering()) { + $testsCovering = $cliConfiguration->testsCovering(); + } + $testsUsing = null; + if ($cliConfiguration->hasTestsUsing()) { + $testsUsing = $cliConfiguration->testsUsing(); + } + $filter = null; + if ($cliConfiguration->hasFilter()) { + $filter = $cliConfiguration->filter(); + } + if ($cliConfiguration->hasGroups()) { + $groups = $cliConfiguration->groups(); + } else { + $groups = $xmlConfiguration->groups()->include()->asArrayOfStrings(); + } + if ($cliConfiguration->hasExcludeGroups()) { + $excludeGroups = $cliConfiguration->excludeGroups(); + } else { + $excludeGroups = $xmlConfiguration->groups()->exclude()->asArrayOfStrings(); + } + $excludeGroups = array_diff($excludeGroups, $groups); + if ($cliConfiguration->hasRandomOrderSeed()) { + $randomOrderSeed = $cliConfiguration->randomOrderSeed(); + } else { + $randomOrderSeed = time(); + } + if ($xmlConfiguration->wasLoadedFromFile() && $xmlConfiguration->hasValidationErrors()) { + if ((new SchemaDetector())->detect($xmlConfiguration->filename())->detected()) { + EventFacade::emitter()->testRunnerTriggeredDeprecation('Your XML configuration validates against a deprecated schema. Migrate your XML configuration using "--migrate-configuration"!'); + } else { + EventFacade::emitter()->testRunnerTriggeredWarning("Test results may not be as expected because the XML configuration file did not pass validation:\n" . $xmlConfiguration->validationErrors()); + } + } + $includeUncoveredFiles = $xmlConfiguration->codeCoverage()->includeUncoveredFiles(); + $includePaths = []; + if ($cliConfiguration->hasIncludePath()) { + foreach (explode(\PATH_SEPARATOR, $cliConfiguration->includePath()) as $includePath) { + $includePaths[] = new \PHPUnit\TextUI\Configuration\Directory($includePath); + } + } + foreach ($xmlConfiguration->php()->includePaths() as $includePath) { + $includePaths[] = $includePath; + } + $iniSettings = []; + if ($cliConfiguration->hasIniSettings()) { + foreach ($cliConfiguration->iniSettings() as $name => $value) { + $iniSettings[] = new \PHPUnit\TextUI\Configuration\IniSetting($name, $value); + } + } + foreach ($xmlConfiguration->php()->iniSettings() as $iniSetting) { + $iniSettings[] = $iniSetting; + } + $includeTestSuite = ''; + if ($cliConfiguration->hasTestSuite()) { + $includeTestSuite = $cliConfiguration->testSuite(); + } elseif ($xmlConfiguration->phpunit()->hasDefaultTestSuite()) { + $includeTestSuite = $xmlConfiguration->phpunit()->defaultTestSuite(); + } + $excludeTestSuite = ''; + if ($cliConfiguration->hasExcludedTestSuite()) { + $excludeTestSuite = $cliConfiguration->excludedTestSuite(); + } + $testSuffixes = ['Test.php', '.phpt']; + if ($cliConfiguration->hasTestSuffixes()) { + $testSuffixes = $cliConfiguration->testSuffixes(); + } + $sourceIncludeDirectories = []; + if ($cliConfiguration->hasCoverageFilter()) { + foreach ($cliConfiguration->coverageFilter() as $directory) { + $sourceIncludeDirectories[] = new \PHPUnit\TextUI\Configuration\FilterDirectory($directory, '', '.php'); + } + } + if ($xmlConfiguration->codeCoverage()->hasNonEmptyListOfFilesToBeIncludedInCodeCoverageReport()) { + foreach ($xmlConfiguration->codeCoverage()->directories() as $directory) { + $sourceIncludeDirectories[] = $directory; + } + $sourceIncludeFiles = $xmlConfiguration->codeCoverage()->files(); + $sourceExcludeDirectories = $xmlConfiguration->codeCoverage()->excludeDirectories(); + $sourceExcludeFiles = $xmlConfiguration->codeCoverage()->excludeFiles(); + } else { + foreach ($xmlConfiguration->source()->includeDirectories() as $directory) { + $sourceIncludeDirectories[] = $directory; + } + $sourceIncludeFiles = $xmlConfiguration->source()->includeFiles(); + $sourceExcludeDirectories = $xmlConfiguration->source()->excludeDirectories(); + $sourceExcludeFiles = $xmlConfiguration->source()->excludeFiles(); + } + $useBaseline = null; + $generateBaseline = null; + if (!$cliConfiguration->hasGenerateBaseline()) { + if ($cliConfiguration->hasUseBaseline()) { + $useBaseline = $cliConfiguration->useBaseline(); + } elseif ($xmlConfiguration->source()->hasBaseline()) { + $useBaseline = $xmlConfiguration->source()->baseline(); + } + } else { + $generateBaseline = $cliConfiguration->generateBaseline(); + } + assert($useBaseline !== ''); + assert($generateBaseline !== ''); + return new \PHPUnit\TextUI\Configuration\Configuration($cliConfiguration->arguments(), $configurationFile, $bootstrap, $cacheResult, $cacheDirectory, $coverageCacheDirectory, new \PHPUnit\TextUI\Configuration\Source($useBaseline, $cliConfiguration->ignoreBaseline(), \PHPUnit\TextUI\Configuration\FilterDirectoryCollection::fromArray($sourceIncludeDirectories), $sourceIncludeFiles, $sourceExcludeDirectories, $sourceExcludeFiles, $xmlConfiguration->source()->restrictDeprecations(), $xmlConfiguration->source()->restrictNotices(), $xmlConfiguration->source()->restrictWarnings(), $xmlConfiguration->source()->ignoreSuppressionOfDeprecations(), $xmlConfiguration->source()->ignoreSuppressionOfPhpDeprecations(), $xmlConfiguration->source()->ignoreSuppressionOfErrors(), $xmlConfiguration->source()->ignoreSuppressionOfNotices(), $xmlConfiguration->source()->ignoreSuppressionOfPhpNotices(), $xmlConfiguration->source()->ignoreSuppressionOfWarnings(), $xmlConfiguration->source()->ignoreSuppressionOfPhpWarnings()), $testResultCacheFile, $coverageClover, $coverageCobertura, $coverageCrap4j, $coverageCrap4jThreshold, $coverageHtml, $coverageHtmlLowUpperBound, $coverageHtmlHighLowerBound, $coverageHtmlColorSuccessLow, $coverageHtmlColorSuccessMedium, $coverageHtmlColorSuccessHigh, $coverageHtmlColorWarning, $coverageHtmlColorDanger, $coverageHtmlCustomCssFile, $coveragePhp, $coverageText, $coverageTextShowUncoveredFiles, $coverageTextShowOnlySummary, $coverageXml, $pathCoverage, $xmlConfiguration->codeCoverage()->ignoreDeprecatedCodeUnits(), $disableCodeCoverageIgnore, $failOnDeprecation, $failOnEmptyTestSuite, $failOnIncomplete, $failOnNotice, $failOnRisky, $failOnSkipped, $failOnWarning, $stopOnDefect, $stopOnDeprecation, $stopOnError, $stopOnFailure, $stopOnIncomplete, $stopOnNotice, $stopOnRisky, $stopOnSkipped, $stopOnWarning, $outputToStandardErrorStream, $columns, $noExtensions, $pharExtensionDirectory, $extensionBootstrappers, $backupGlobals, $backupStaticProperties, $beStrictAboutChangesToGlobalState, $colors, $processIsolation, $enforceTimeLimit, $defaultTimeLimit, $timeoutForSmallTests, $timeoutForMediumTests, $timeoutForLargeTests, $reportUselessTests, $strictCoverage, $disallowTestOutput, $displayDetailsOnIncompleteTests, $displayDetailsOnSkippedTests, $displayDetailsOnTestsThatTriggerDeprecations, $displayDetailsOnTestsThatTriggerErrors, $displayDetailsOnTestsThatTriggerNotices, $displayDetailsOnTestsThatTriggerWarnings, $reverseDefectList, $requireCoverageMetadata, $registerMockObjectsFromTestArgumentsRecursively, $noProgress, $noResults, $noOutput, $executionOrder, $executionOrderDefects, $resolveDependencies, $logfileTeamcity, $logfileJunit, $logfileTestdoxHtml, $logfileTestdoxText, $logEventsText, $logEventsVerboseText, $teamCityOutput, $testDoxOutput, $testsCovering, $testsUsing, $filter, $groups, $excludeGroups, $randomOrderSeed, $includeUncoveredFiles, $xmlConfiguration->testSuite(), $includeTestSuite, $excludeTestSuite, $xmlConfiguration->phpunit()->hasDefaultTestSuite() ? $xmlConfiguration->phpunit()->defaultTestSuite() : null, $testSuffixes, new \PHPUnit\TextUI\Configuration\Php(\PHPUnit\TextUI\Configuration\DirectoryCollection::fromArray($includePaths), \PHPUnit\TextUI\Configuration\IniSettingCollection::fromArray($iniSettings), $xmlConfiguration->php()->constants(), $xmlConfiguration->php()->globalVariables(), $xmlConfiguration->php()->envVariables(), $xmlConfiguration->php()->postVariables(), $xmlConfiguration->php()->getVariables(), $xmlConfiguration->php()->cookieVariables(), $xmlConfiguration->php()->serverVariables(), $xmlConfiguration->php()->filesVariables(), $xmlConfiguration->php()->requestVariables()), $xmlConfiguration->phpunit()->controlGarbageCollector(), $xmlConfiguration->phpunit()->numberOfTestsBeforeGarbageCollection(), $generateBaseline, $cliConfiguration->debug()); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\Configuration; + +use const PATH_SEPARATOR; +use function constant; +use function define; +use function defined; +use function getenv; +use function implode; +use function ini_get; +use function ini_set; +use function putenv; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class PhpHandler +{ + public function handle(\PHPUnit\TextUI\Configuration\Php $configuration): void + { + $this->handleIncludePaths($configuration->includePaths()); + $this->handleIniSettings($configuration->iniSettings()); + $this->handleConstants($configuration->constants()); + $this->handleGlobalVariables($configuration->globalVariables()); + $this->handleServerVariables($configuration->serverVariables()); + $this->handleEnvVariables($configuration->envVariables()); + $this->handleVariables('_POST', $configuration->postVariables()); + $this->handleVariables('_GET', $configuration->getVariables()); + $this->handleVariables('_COOKIE', $configuration->cookieVariables()); + $this->handleVariables('_FILES', $configuration->filesVariables()); + $this->handleVariables('_REQUEST', $configuration->requestVariables()); + } + private function handleIncludePaths(\PHPUnit\TextUI\Configuration\DirectoryCollection $includePaths): void + { + if (!$includePaths->isEmpty()) { + $includePathsAsStrings = []; + foreach ($includePaths as $includePath) { + $includePathsAsStrings[] = $includePath->path(); + } + ini_set('include_path', implode(PATH_SEPARATOR, $includePathsAsStrings) . PATH_SEPARATOR . ini_get('include_path')); + } + } + private function handleIniSettings(\PHPUnit\TextUI\Configuration\IniSettingCollection $iniSettings): void + { + foreach ($iniSettings as $iniSetting) { + $value = $iniSetting->value(); + if (defined($value)) { + $value = (string) constant($value); + } + ini_set($iniSetting->name(), $value); + } + } + private function handleConstants(\PHPUnit\TextUI\Configuration\ConstantCollection $constants): void + { + foreach ($constants as $constant) { + if (!defined($constant->name())) { + define($constant->name(), $constant->value()); + } + } + } + private function handleGlobalVariables(\PHPUnit\TextUI\Configuration\VariableCollection $variables): void + { + foreach ($variables as $variable) { + $GLOBALS[$variable->name()] = $variable->value(); + } + } + private function handleServerVariables(\PHPUnit\TextUI\Configuration\VariableCollection $variables): void + { + foreach ($variables as $variable) { + $_SERVER[$variable->name()] = $variable->value(); + } + } + private function handleVariables(string $target, \PHPUnit\TextUI\Configuration\VariableCollection $variables): void + { + foreach ($variables as $variable) { + $GLOBALS[$target][$variable->name()] = $variable->value(); + } + } + private function handleEnvVariables(\PHPUnit\TextUI\Configuration\VariableCollection $variables): void + { + foreach ($variables as $variable) { + $name = $variable->name(); + $value = $variable->value(); + $force = $variable->force(); + if ($force || getenv($name) === \false) { + putenv("{$name}={$value}"); + } + $value = getenv($name); + if ($force || !isset($_ENV[$name])) { + $_ENV[$name] = $value; + } + } + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\Configuration; + +use function assert; +use function file_get_contents; +use function file_put_contents; +use function serialize; +use function unserialize; +use PHPUnit\Event\Facade as EventFacade; +use PHPUnit\TextUI\CliArguments\Configuration as CliConfiguration; +use PHPUnit\TextUI\CliArguments\Exception; +use PHPUnit\TextUI\XmlConfiguration\Configuration as XmlConfiguration; +use PHPUnit\Util\VersionComparisonOperator; +/** + * CLI options and XML configuration are static within a single PHPUnit 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 +{ + private static ?\PHPUnit\TextUI\Configuration\Configuration $instance = null; + public static function saveTo(string $path): bool + { + $result = file_put_contents($path, serialize(self::get())); + if ($result) { + return \true; + } + // @codeCoverageIgnoreStart + return \false; + // @codeCoverageIgnoreEnd + } + /** + * This method is used by the "run test(s) in separate process" templates. + * + * @noinspection PhpUnused + * + * @codeCoverageIgnore + */ + public static function loadFrom(string $path): void + { + self::$instance = unserialize(file_get_contents($path), ['allowed_classes' => [\PHPUnit\TextUI\Configuration\Configuration::class, \PHPUnit\TextUI\Configuration\Php::class, \PHPUnit\TextUI\Configuration\ConstantCollection::class, \PHPUnit\TextUI\Configuration\Constant::class, \PHPUnit\TextUI\Configuration\IniSettingCollection::class, \PHPUnit\TextUI\Configuration\IniSetting::class, \PHPUnit\TextUI\Configuration\VariableCollection::class, \PHPUnit\TextUI\Configuration\Variable::class, \PHPUnit\TextUI\Configuration\DirectoryCollection::class, \PHPUnit\TextUI\Configuration\Directory::class, \PHPUnit\TextUI\Configuration\FileCollection::class, \PHPUnit\TextUI\Configuration\File::class, \PHPUnit\TextUI\Configuration\FilterDirectoryCollection::class, \PHPUnit\TextUI\Configuration\FilterDirectory::class, \PHPUnit\TextUI\Configuration\TestDirectoryCollection::class, \PHPUnit\TextUI\Configuration\TestDirectory::class, \PHPUnit\TextUI\Configuration\TestFileCollection::class, \PHPUnit\TextUI\Configuration\TestFile::class, \PHPUnit\TextUI\Configuration\TestSuiteCollection::class, \PHPUnit\TextUI\Configuration\TestSuite::class, VersionComparisonOperator::class, \PHPUnit\TextUI\Configuration\Source::class]]); + } + public static function get(): \PHPUnit\TextUI\Configuration\Configuration + { + assert(self::$instance instanceof \PHPUnit\TextUI\Configuration\Configuration); + return self::$instance; + } + /** + * @throws \PHPUnit\TextUI\XmlConfiguration\Exception + * @throws Exception + * @throws NoCustomCssFileException + */ + public static function init(CliConfiguration $cliConfiguration, XmlConfiguration $xmlConfiguration): \PHPUnit\TextUI\Configuration\Configuration + { + self::$instance = (new \PHPUnit\TextUI\Configuration\Merger())->merge($cliConfiguration, $xmlConfiguration); + EventFacade::emitter()->testRunnerConfigured(self::$instance); + return self::$instance; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\Configuration; + +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class SourceFilter +{ + public function includes(\PHPUnit\TextUI\Configuration\Source $source, string $path): bool + { + $files = (new \PHPUnit\TextUI\Configuration\SourceMapper())->map($source); + return isset($files[$path]); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\Configuration; + +use function realpath; +use PHPUnitPHAR\SebastianBergmann\FileIterator\Facade as FileIteratorFacade; +use SplObjectStorage; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class SourceMapper +{ + /** + * @psalm-var SplObjectStorage> + */ + private static ?SplObjectStorage $files = null; + /** + * @psalm-return array + */ + public function map(\PHPUnit\TextUI\Configuration\Source $source): array + { + if (self::$files === null) { + self::$files = new SplObjectStorage(); + } + if (isset(self::$files[$source])) { + return self::$files[$source]; + } + $files = []; + foreach ($source->includeDirectories() as $directory) { + foreach ((new FileIteratorFacade())->getFilesAsArray($directory->path(), $directory->suffix(), $directory->prefix()) as $file) { + $file = realpath($file); + if (!$file) { + continue; + } + $files[$file] = \true; + } + } + foreach ($source->includeFiles() as $file) { + $file = realpath($file->path()); + if (!$file) { + continue; + } + $files[$file] = \true; + } + foreach ($source->excludeDirectories() as $directory) { + foreach ((new FileIteratorFacade())->getFilesAsArray($directory->path(), $directory->suffix(), $directory->prefix()) as $file) { + $file = realpath($file); + if (!$file) { + continue; + } + if (!isset($files[$file])) { + continue; + } + unset($files[$file]); + } + } + foreach ($source->excludeFiles() as $file) { + $file = realpath($file->path()); + if (!$file) { + continue; + } + if (!isset($files[$file])) { + continue; + } + unset($files[$file]); + } + self::$files[$source] = $files; + return $files; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\Configuration; + +use function assert; +use function count; +use function is_dir; +use function is_file; +use function realpath; +use function str_ends_with; +use PHPUnit\Event\Facade as EventFacade; +use PHPUnit\Exception; +use PHPUnit\Framework\TestSuite; +use PHPUnit\Runner\TestSuiteLoader; +use PHPUnit\TextUI\RuntimeException; +use PHPUnit\TextUI\TestDirectoryNotFoundException; +use PHPUnit\TextUI\TestFileNotFoundException; +use PHPUnit\TextUI\XmlConfiguration\TestSuiteMapper; +use PHPUnitPHAR\SebastianBergmann\FileIterator\Facade as FileIteratorFacade; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class TestSuiteBuilder +{ + /** + * @throws \PHPUnit\Framework\Exception + * @throws RuntimeException + * @throws TestDirectoryNotFoundException + * @throws TestFileNotFoundException + */ + public function build(\PHPUnit\TextUI\Configuration\Configuration $configuration): TestSuite + { + if ($configuration->hasCliArguments()) { + $arguments = []; + foreach ($configuration->cliArguments() as $cliArgument) { + $argument = realpath($cliArgument); + if (!$argument) { + throw new TestFileNotFoundException($cliArgument); + } + $arguments[] = $argument; + } + if (count($arguments) === 1) { + $testSuite = $this->testSuiteFromPath($arguments[0], $configuration->testSuffixes()); + } else { + $testSuite = $this->testSuiteFromPathList($arguments, $configuration->testSuffixes()); + } + } + if (!isset($testSuite)) { + $xmlConfigurationFile = $configuration->hasConfigurationFile() ? $configuration->configurationFile() : 'Root Test Suite'; + assert(!empty($xmlConfigurationFile)); + $testSuite = (new TestSuiteMapper())->map($xmlConfigurationFile, $configuration->testSuite(), $configuration->includeTestSuite(), $configuration->excludeTestSuite()); + } + EventFacade::emitter()->testSuiteLoaded(\PHPUnit\Event\TestSuite\TestSuiteBuilder::from($testSuite)); + return $testSuite; + } + /** + * @psalm-param non-empty-string $path + * @psalm-param list $suffixes + * @psalm-param ?TestSuite $suite + * + * @throws \PHPUnit\Framework\Exception + */ + private function testSuiteFromPath(string $path, array $suffixes, ?TestSuite $suite = null): TestSuite + { + if (str_ends_with($path, '.phpt') && is_file($path)) { + $suite = $suite ?: TestSuite::empty($path); + $suite->addTestFile($path); + return $suite; + } + if (is_dir($path)) { + $files = (new FileIteratorFacade())->getFilesAsArray($path, $suffixes); + $suite = $suite ?: TestSuite::empty('CLI Arguments'); + $suite->addTestFiles($files); + return $suite; + } + try { + $testClass = (new TestSuiteLoader())->load($path); + } catch (Exception $e) { + print $e->getMessage() . \PHP_EOL; + exit(1); + } + if (!$suite) { + return TestSuite::fromClassReflector($testClass); + } + $suite->addTestSuite($testClass); + return $suite; + } + /** + * @psalm-param list $paths + * @psalm-param list $suffixes + * + * @throws \PHPUnit\Framework\Exception + */ + private function testSuiteFromPathList(array $paths, array $suffixes): TestSuite + { + $suite = TestSuite::empty('CLI Arguments'); + foreach ($paths as $path) { + $this->testSuiteFromPath($path, $suffixes, $suite); + } + return $suite; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\Configuration; + +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @psalm-immutable + */ +final class Constant +{ + private readonly string $name; + private readonly bool|string $value; + public function __construct(string $name, bool|string $value) + { + $this->name = $name; + $this->value = $value; + } + public function name(): string + { + return $this->name; + } + public function value(): bool|string + { + return $this->value; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\Configuration; + +use function count; +use Countable; +use IteratorAggregate; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @psalm-immutable + * + * @template-implements IteratorAggregate + */ +final class ConstantCollection implements Countable, IteratorAggregate +{ + /** + * @psalm-var list + */ + private readonly array $constants; + /** + * @psalm-param list $constants + */ + public static function fromArray(array $constants): self + { + return new self(...$constants); + } + private function __construct(\PHPUnit\TextUI\Configuration\Constant ...$constants) + { + $this->constants = $constants; + } + /** + * @psalm-return list + */ + public function asArray(): array + { + return $this->constants; + } + public function count(): int + { + return count($this->constants); + } + public function getIterator(): \PHPUnit\TextUI\Configuration\ConstantCollectionIterator + { + return new \PHPUnit\TextUI\Configuration\ConstantCollectionIterator($this); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\Configuration; + +use function count; +use function iterator_count; +use Countable; +use Iterator; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @template-implements Iterator + */ +final class ConstantCollectionIterator implements Countable, Iterator +{ + /** + * @psalm-var list + */ + private readonly array $constants; + private int $position = 0; + public function __construct(\PHPUnit\TextUI\Configuration\ConstantCollection $constants) + { + $this->constants = $constants->asArray(); + } + public function count(): int + { + return iterator_count($this); + } + public function rewind(): void + { + $this->position = 0; + } + public function valid(): bool + { + return $this->position < count($this->constants); + } + public function key(): int + { + return $this->position; + } + public function current(): \PHPUnit\TextUI\Configuration\Constant + { + return $this->constants[$this->position]; + } + public function next(): void + { + $this->position++; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\Configuration; + +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @psalm-immutable + */ +final class Directory +{ + private readonly string $path; + public function __construct(string $path) + { + $this->path = $path; + } + public function path(): string + { + return $this->path; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\Configuration; + +use function count; +use Countable; +use IteratorAggregate; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @psalm-immutable + * + * @template-implements IteratorAggregate + */ +final class DirectoryCollection implements Countable, IteratorAggregate +{ + /** + * @psalm-var list + */ + private readonly array $directories; + /** + * @psalm-param list $directories + */ + public static function fromArray(array $directories): self + { + return new self(...$directories); + } + private function __construct(\PHPUnit\TextUI\Configuration\Directory ...$directories) + { + $this->directories = $directories; + } + /** + * @psalm-return list + */ + public function asArray(): array + { + return $this->directories; + } + public function count(): int + { + return count($this->directories); + } + public function getIterator(): \PHPUnit\TextUI\Configuration\DirectoryCollectionIterator + { + return new \PHPUnit\TextUI\Configuration\DirectoryCollectionIterator($this); + } + public function isEmpty(): bool + { + return $this->count() === 0; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\Configuration; + +use function count; +use function iterator_count; +use Countable; +use Iterator; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @template-implements Iterator + */ +final class DirectoryCollectionIterator implements Countable, Iterator +{ + /** + * @psalm-var list + */ + private readonly array $directories; + private int $position = 0; + public function __construct(\PHPUnit\TextUI\Configuration\DirectoryCollection $directories) + { + $this->directories = $directories->asArray(); + } + public function count(): int + { + return iterator_count($this); + } + public function rewind(): void + { + $this->position = 0; + } + public function valid(): bool + { + return $this->position < count($this->directories); + } + public function key(): int + { + return $this->position; + } + public function current(): \PHPUnit\TextUI\Configuration\Directory + { + return $this->directories[$this->position]; + } + public function next(): void + { + $this->position++; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\Configuration; + +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @psalm-immutable + */ +final class ExtensionBootstrap +{ + /** + * @psalm-var class-string + */ + private readonly string $className; + /** + * @psalm-var array + */ + private readonly array $parameters; + /** + * @psalm-param class-string $className + * @psalm-param array $parameters + */ + public function __construct(string $className, array $parameters) + { + $this->className = $className; + $this->parameters = $parameters; + } + /** + * @psalm-return class-string + */ + public function className(): string + { + return $this->className; + } + /** + * @psalm-return array + */ + public function parameters(): array + { + return $this->parameters; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\Configuration; + +use IteratorAggregate; +/** + * @template-implements IteratorAggregate + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @psalm-immutable + */ +final class ExtensionBootstrapCollection implements IteratorAggregate +{ + /** + * @psalm-var list + */ + private readonly array $extensionBootstraps; + /** + * @psalm-param list $extensionBootstraps + */ + public static function fromArray(array $extensionBootstraps): self + { + return new self(...$extensionBootstraps); + } + private function __construct(\PHPUnit\TextUI\Configuration\ExtensionBootstrap ...$extensionBootstraps) + { + $this->extensionBootstraps = $extensionBootstraps; + } + /** + * @psalm-return list + */ + public function asArray(): array + { + return $this->extensionBootstraps; + } + public function getIterator(): \PHPUnit\TextUI\Configuration\ExtensionBootstrapCollectionIterator + { + return new \PHPUnit\TextUI\Configuration\ExtensionBootstrapCollectionIterator($this); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\Configuration; + +use function count; +use function iterator_count; +use Countable; +use Iterator; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @template-implements Iterator + */ +final class ExtensionBootstrapCollectionIterator implements Countable, Iterator +{ + /** + * @psalm-var list + */ + private readonly array $extensionBootstraps; + private int $position = 0; + public function __construct(\PHPUnit\TextUI\Configuration\ExtensionBootstrapCollection $extensionBootstraps) + { + $this->extensionBootstraps = $extensionBootstraps->asArray(); + } + public function count(): int + { + return iterator_count($this); + } + public function rewind(): void + { + $this->position = 0; + } + public function valid(): bool + { + return $this->position < count($this->extensionBootstraps); + } + public function key(): int + { + return $this->position; + } + public function current(): \PHPUnit\TextUI\Configuration\ExtensionBootstrap + { + return $this->extensionBootstraps[$this->position]; + } + public function next(): void + { + $this->position++; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\Configuration; + +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @psalm-immutable + */ +final class File +{ + /** + * @psalm-var non-empty-string + */ + private readonly string $path; + /** + * @psalm-param non-empty-string $path + */ + public function __construct(string $path) + { + $this->path = $path; + } + /** + * @psalm-return non-empty-string + */ + public function path(): string + { + return $this->path; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\Configuration; + +use function count; +use Countable; +use IteratorAggregate; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @psalm-immutable + * + * @template-implements IteratorAggregate + */ +final class FileCollection implements Countable, IteratorAggregate +{ + /** + * @psalm-var list + */ + private readonly array $files; + /** + * @psalm-param list $files + */ + public static function fromArray(array $files): self + { + return new self(...$files); + } + private function __construct(\PHPUnit\TextUI\Configuration\File ...$files) + { + $this->files = $files; + } + /** + * @psalm-return list + */ + public function asArray(): array + { + return $this->files; + } + public function count(): int + { + return count($this->files); + } + public function notEmpty(): bool + { + return !empty($this->files); + } + public function getIterator(): \PHPUnit\TextUI\Configuration\FileCollectionIterator + { + return new \PHPUnit\TextUI\Configuration\FileCollectionIterator($this); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\Configuration; + +use function count; +use function iterator_count; +use Countable; +use Iterator; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @template-implements Iterator + */ +final class FileCollectionIterator implements Countable, Iterator +{ + /** + * @psalm-var list + */ + private readonly array $files; + private int $position = 0; + public function __construct(\PHPUnit\TextUI\Configuration\FileCollection $files) + { + $this->files = $files->asArray(); + } + public function count(): int + { + return iterator_count($this); + } + public function rewind(): void + { + $this->position = 0; + } + public function valid(): bool + { + return $this->position < count($this->files); + } + public function key(): int + { + return $this->position; + } + public function current(): \PHPUnit\TextUI\Configuration\File + { + return $this->files[$this->position]; + } + public function next(): void + { + $this->position++; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\Configuration; + +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @psalm-immutable + */ +final class FilterDirectory +{ + /** + * @psalm-var non-empty-string + */ + private readonly string $path; + private readonly string $prefix; + private readonly string $suffix; + /** + * @psalm-param non-empty-string $path + */ + public function __construct(string $path, string $prefix, string $suffix) + { + $this->path = $path; + $this->prefix = $prefix; + $this->suffix = $suffix; + } + /** + * @psalm-return non-empty-string + */ + public function path(): string + { + return $this->path; + } + public function prefix(): string + { + return $this->prefix; + } + public function suffix(): string + { + return $this->suffix; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\Configuration; + +use function count; +use Countable; +use IteratorAggregate; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @psalm-immutable + * + * @template-implements IteratorAggregate + */ +final class FilterDirectoryCollection implements Countable, IteratorAggregate +{ + /** + * @psalm-var list + */ + private readonly array $directories; + /** + * @psalm-param list $directories + */ + public static function fromArray(array $directories): self + { + return new self(...$directories); + } + private function __construct(\PHPUnit\TextUI\Configuration\FilterDirectory ...$directories) + { + $this->directories = $directories; + } + /** + * @psalm-return list + */ + public function asArray(): array + { + return $this->directories; + } + public function count(): int + { + return count($this->directories); + } + public function notEmpty(): bool + { + return !empty($this->directories); + } + public function getIterator(): \PHPUnit\TextUI\Configuration\FilterDirectoryCollectionIterator + { + return new \PHPUnit\TextUI\Configuration\FilterDirectoryCollectionIterator($this); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\Configuration; + +use function count; +use function iterator_count; +use Countable; +use Iterator; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @template-implements Iterator + */ +final class FilterDirectoryCollectionIterator implements Countable, Iterator +{ + /** + * @psalm-var list + */ + private readonly array $directories; + private int $position = 0; + public function __construct(\PHPUnit\TextUI\Configuration\FilterDirectoryCollection $directories) + { + $this->directories = $directories->asArray(); + } + public function count(): int + { + return iterator_count($this); + } + public function rewind(): void + { + $this->position = 0; + } + public function valid(): bool + { + return $this->position < count($this->directories); + } + public function key(): int + { + return $this->position; + } + public function current(): \PHPUnit\TextUI\Configuration\FilterDirectory + { + return $this->directories[$this->position]; + } + public function next(): void + { + $this->position++; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\Configuration; + +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @psalm-immutable + */ +final class Group +{ + private readonly string $name; + public function __construct(string $name) + { + $this->name = $name; + } + public function name(): string + { + return $this->name; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\Configuration; + +use IteratorAggregate; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @psalm-immutable + * + * @template-implements IteratorAggregate + */ +final class GroupCollection implements IteratorAggregate +{ + /** + * @psalm-var list + */ + private readonly array $groups; + /** + * @psalm-param list $groups + */ + public static function fromArray(array $groups): self + { + return new self(...$groups); + } + private function __construct(\PHPUnit\TextUI\Configuration\Group ...$groups) + { + $this->groups = $groups; + } + /** + * @psalm-return list + */ + public function asArray(): array + { + return $this->groups; + } + /** + * @psalm-return list + */ + public function asArrayOfStrings(): array + { + $result = []; + foreach ($this->groups as $group) { + $result[] = $group->name(); + } + return $result; + } + public function isEmpty(): bool + { + return empty($this->groups); + } + public function getIterator(): \PHPUnit\TextUI\Configuration\GroupCollectionIterator + { + return new \PHPUnit\TextUI\Configuration\GroupCollectionIterator($this); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\Configuration; + +use function count; +use function iterator_count; +use Countable; +use Iterator; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @template-implements Iterator + */ +final class GroupCollectionIterator implements Countable, Iterator +{ + /** + * @psalm-var list + */ + private readonly array $groups; + private int $position = 0; + public function __construct(\PHPUnit\TextUI\Configuration\GroupCollection $groups) + { + $this->groups = $groups->asArray(); + } + public function count(): int + { + return iterator_count($this); + } + public function rewind(): void + { + $this->position = 0; + } + public function valid(): bool + { + return $this->position < count($this->groups); + } + public function key(): int + { + return $this->position; + } + public function current(): \PHPUnit\TextUI\Configuration\Group + { + return $this->groups[$this->position]; + } + public function next(): void + { + $this->position++; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\Configuration; + +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @psalm-immutable + */ +final class IniSetting +{ + private readonly string $name; + private readonly string $value; + public function __construct(string $name, string $value) + { + $this->name = $name; + $this->value = $value; + } + public function name(): string + { + return $this->name; + } + public function value(): string + { + return $this->value; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\Configuration; + +use function count; +use Countable; +use IteratorAggregate; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @psalm-immutable + * + * @template-implements IteratorAggregate + */ +final class IniSettingCollection implements Countable, IteratorAggregate +{ + /** + * @psalm-var list + */ + private readonly array $iniSettings; + /** + * @psalm-param list $iniSettings + */ + public static function fromArray(array $iniSettings): self + { + return new self(...$iniSettings); + } + private function __construct(\PHPUnit\TextUI\Configuration\IniSetting ...$iniSettings) + { + $this->iniSettings = $iniSettings; + } + /** + * @psalm-return list + */ + public function asArray(): array + { + return $this->iniSettings; + } + public function count(): int + { + return count($this->iniSettings); + } + public function getIterator(): \PHPUnit\TextUI\Configuration\IniSettingCollectionIterator + { + return new \PHPUnit\TextUI\Configuration\IniSettingCollectionIterator($this); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\Configuration; + +use function count; +use function iterator_count; +use Countable; +use Iterator; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @template-implements Iterator + */ +final class IniSettingCollectionIterator implements Countable, Iterator +{ + /** + * @psalm-var list + */ + private readonly array $iniSettings; + private int $position = 0; + public function __construct(\PHPUnit\TextUI\Configuration\IniSettingCollection $iniSettings) + { + $this->iniSettings = $iniSettings->asArray(); + } + public function count(): int + { + return iterator_count($this); + } + public function rewind(): void + { + $this->position = 0; + } + public function valid(): bool + { + return $this->position < count($this->iniSettings); + } + public function key(): int + { + return $this->position; + } + public function current(): \PHPUnit\TextUI\Configuration\IniSetting + { + return $this->iniSettings[$this->position]; + } + public function next(): void + { + $this->position++; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\Configuration; + +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @psalm-immutable + */ +final class Php +{ + private readonly \PHPUnit\TextUI\Configuration\DirectoryCollection $includePaths; + private readonly \PHPUnit\TextUI\Configuration\IniSettingCollection $iniSettings; + private readonly \PHPUnit\TextUI\Configuration\ConstantCollection $constants; + private readonly \PHPUnit\TextUI\Configuration\VariableCollection $globalVariables; + private readonly \PHPUnit\TextUI\Configuration\VariableCollection $envVariables; + private readonly \PHPUnit\TextUI\Configuration\VariableCollection $postVariables; + private readonly \PHPUnit\TextUI\Configuration\VariableCollection $getVariables; + private readonly \PHPUnit\TextUI\Configuration\VariableCollection $cookieVariables; + private readonly \PHPUnit\TextUI\Configuration\VariableCollection $serverVariables; + private readonly \PHPUnit\TextUI\Configuration\VariableCollection $filesVariables; + private readonly \PHPUnit\TextUI\Configuration\VariableCollection $requestVariables; + public function __construct(\PHPUnit\TextUI\Configuration\DirectoryCollection $includePaths, \PHPUnit\TextUI\Configuration\IniSettingCollection $iniSettings, \PHPUnit\TextUI\Configuration\ConstantCollection $constants, \PHPUnit\TextUI\Configuration\VariableCollection $globalVariables, \PHPUnit\TextUI\Configuration\VariableCollection $envVariables, \PHPUnit\TextUI\Configuration\VariableCollection $postVariables, \PHPUnit\TextUI\Configuration\VariableCollection $getVariables, \PHPUnit\TextUI\Configuration\VariableCollection $cookieVariables, \PHPUnit\TextUI\Configuration\VariableCollection $serverVariables, \PHPUnit\TextUI\Configuration\VariableCollection $filesVariables, \PHPUnit\TextUI\Configuration\VariableCollection $requestVariables) + { + $this->includePaths = $includePaths; + $this->iniSettings = $iniSettings; + $this->constants = $constants; + $this->globalVariables = $globalVariables; + $this->envVariables = $envVariables; + $this->postVariables = $postVariables; + $this->getVariables = $getVariables; + $this->cookieVariables = $cookieVariables; + $this->serverVariables = $serverVariables; + $this->filesVariables = $filesVariables; + $this->requestVariables = $requestVariables; + } + public function includePaths(): \PHPUnit\TextUI\Configuration\DirectoryCollection + { + return $this->includePaths; + } + public function iniSettings(): \PHPUnit\TextUI\Configuration\IniSettingCollection + { + return $this->iniSettings; + } + public function constants(): \PHPUnit\TextUI\Configuration\ConstantCollection + { + return $this->constants; + } + public function globalVariables(): \PHPUnit\TextUI\Configuration\VariableCollection + { + return $this->globalVariables; + } + public function envVariables(): \PHPUnit\TextUI\Configuration\VariableCollection + { + return $this->envVariables; + } + public function postVariables(): \PHPUnit\TextUI\Configuration\VariableCollection + { + return $this->postVariables; + } + public function getVariables(): \PHPUnit\TextUI\Configuration\VariableCollection + { + return $this->getVariables; + } + public function cookieVariables(): \PHPUnit\TextUI\Configuration\VariableCollection + { + return $this->cookieVariables; + } + public function serverVariables(): \PHPUnit\TextUI\Configuration\VariableCollection + { + return $this->serverVariables; + } + public function filesVariables(): \PHPUnit\TextUI\Configuration\VariableCollection + { + return $this->filesVariables; + } + public function requestVariables(): \PHPUnit\TextUI\Configuration\VariableCollection + { + return $this->requestVariables; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\Configuration; + +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @psalm-immutable + */ +final class Source +{ + /** + * @psalm-var non-empty-string + */ + private readonly ?string $baseline; + private readonly bool $ignoreBaseline; + private readonly \PHPUnit\TextUI\Configuration\FilterDirectoryCollection $includeDirectories; + private readonly \PHPUnit\TextUI\Configuration\FileCollection $includeFiles; + private readonly \PHPUnit\TextUI\Configuration\FilterDirectoryCollection $excludeDirectories; + private readonly \PHPUnit\TextUI\Configuration\FileCollection $excludeFiles; + private readonly bool $restrictDeprecations; + private readonly bool $restrictNotices; + private readonly bool $restrictWarnings; + private readonly bool $ignoreSuppressionOfDeprecations; + private readonly bool $ignoreSuppressionOfPhpDeprecations; + private readonly bool $ignoreSuppressionOfErrors; + private readonly bool $ignoreSuppressionOfNotices; + private readonly bool $ignoreSuppressionOfPhpNotices; + private readonly bool $ignoreSuppressionOfWarnings; + private readonly bool $ignoreSuppressionOfPhpWarnings; + /** + * @psalm-param non-empty-string $baseline + */ + public function __construct(?string $baseline, bool $ignoreBaseline, \PHPUnit\TextUI\Configuration\FilterDirectoryCollection $includeDirectories, \PHPUnit\TextUI\Configuration\FileCollection $includeFiles, \PHPUnit\TextUI\Configuration\FilterDirectoryCollection $excludeDirectories, \PHPUnit\TextUI\Configuration\FileCollection $excludeFiles, bool $restrictDeprecations, bool $restrictNotices, bool $restrictWarnings, bool $ignoreSuppressionOfDeprecations, bool $ignoreSuppressionOfPhpDeprecations, bool $ignoreSuppressionOfErrors, bool $ignoreSuppressionOfNotices, bool $ignoreSuppressionOfPhpNotices, bool $ignoreSuppressionOfWarnings, bool $ignoreSuppressionOfPhpWarnings) + { + $this->baseline = $baseline; + $this->ignoreBaseline = $ignoreBaseline; + $this->includeDirectories = $includeDirectories; + $this->includeFiles = $includeFiles; + $this->excludeDirectories = $excludeDirectories; + $this->excludeFiles = $excludeFiles; + $this->restrictDeprecations = $restrictDeprecations; + $this->restrictNotices = $restrictNotices; + $this->restrictWarnings = $restrictWarnings; + $this->ignoreSuppressionOfDeprecations = $ignoreSuppressionOfDeprecations; + $this->ignoreSuppressionOfPhpDeprecations = $ignoreSuppressionOfPhpDeprecations; + $this->ignoreSuppressionOfErrors = $ignoreSuppressionOfErrors; + $this->ignoreSuppressionOfNotices = $ignoreSuppressionOfNotices; + $this->ignoreSuppressionOfPhpNotices = $ignoreSuppressionOfPhpNotices; + $this->ignoreSuppressionOfWarnings = $ignoreSuppressionOfWarnings; + $this->ignoreSuppressionOfPhpWarnings = $ignoreSuppressionOfPhpWarnings; + } + /** + * @psalm-assert-if-true !null $this->baseline + */ + public function useBaseline(): bool + { + return $this->hasBaseline() && !$this->ignoreBaseline; + } + /** + * @psalm-assert-if-true !null $this->baseline + */ + public function hasBaseline(): bool + { + return $this->baseline !== null; + } + /** + * @psalm-return non-empty-string + * + * @throws NoBaselineException + */ + public function baseline(): string + { + if (!$this->hasBaseline()) { + throw new \PHPUnit\TextUI\Configuration\NoBaselineException(); + } + return $this->baseline; + } + public function includeDirectories(): \PHPUnit\TextUI\Configuration\FilterDirectoryCollection + { + return $this->includeDirectories; + } + public function includeFiles(): \PHPUnit\TextUI\Configuration\FileCollection + { + return $this->includeFiles; + } + public function excludeDirectories(): \PHPUnit\TextUI\Configuration\FilterDirectoryCollection + { + return $this->excludeDirectories; + } + public function excludeFiles(): \PHPUnit\TextUI\Configuration\FileCollection + { + return $this->excludeFiles; + } + public function notEmpty(): bool + { + return $this->includeDirectories->notEmpty() || $this->includeFiles->notEmpty(); + } + public function restrictDeprecations(): bool + { + return $this->restrictDeprecations; + } + public function restrictNotices(): bool + { + return $this->restrictNotices; + } + public function restrictWarnings(): bool + { + return $this->restrictWarnings; + } + public function ignoreSuppressionOfDeprecations(): bool + { + return $this->ignoreSuppressionOfDeprecations; + } + public function ignoreSuppressionOfPhpDeprecations(): bool + { + return $this->ignoreSuppressionOfPhpDeprecations; + } + public function ignoreSuppressionOfErrors(): bool + { + return $this->ignoreSuppressionOfErrors; + } + public function ignoreSuppressionOfNotices(): bool + { + return $this->ignoreSuppressionOfNotices; + } + public function ignoreSuppressionOfPhpNotices(): bool + { + return $this->ignoreSuppressionOfPhpNotices; + } + public function ignoreSuppressionOfWarnings(): bool + { + return $this->ignoreSuppressionOfWarnings; + } + public function ignoreSuppressionOfPhpWarnings(): bool + { + return $this->ignoreSuppressionOfPhpWarnings; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\Configuration; + +use PHPUnit\Util\VersionComparisonOperator; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @psalm-immutable + */ +final class TestDirectory +{ + /** + * @psalm-var non-empty-string + */ + private readonly string $path; + private readonly string $prefix; + private readonly string $suffix; + private readonly string $phpVersion; + private readonly VersionComparisonOperator $phpVersionOperator; + /** + * @psalm-param non-empty-string $path + */ + public function __construct(string $path, string $prefix, string $suffix, string $phpVersion, VersionComparisonOperator $phpVersionOperator) + { + $this->path = $path; + $this->prefix = $prefix; + $this->suffix = $suffix; + $this->phpVersion = $phpVersion; + $this->phpVersionOperator = $phpVersionOperator; + } + /** + * @psalm-return non-empty-string + */ + public function path(): string + { + return $this->path; + } + public function prefix(): string + { + return $this->prefix; + } + public function suffix(): string + { + return $this->suffix; + } + public function phpVersion(): string + { + return $this->phpVersion; + } + public function phpVersionOperator(): VersionComparisonOperator + { + return $this->phpVersionOperator; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\Configuration; + +use function count; +use Countable; +use IteratorAggregate; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @psalm-immutable + * + * @template-implements IteratorAggregate + */ +final class TestDirectoryCollection implements Countable, IteratorAggregate +{ + /** + * @psalm-var list + */ + private readonly array $directories; + /** + * @psalm-param list $directories + */ + public static function fromArray(array $directories): self + { + return new self(...$directories); + } + private function __construct(\PHPUnit\TextUI\Configuration\TestDirectory ...$directories) + { + $this->directories = $directories; + } + /** + * @psalm-return list + */ + public function asArray(): array + { + return $this->directories; + } + public function count(): int + { + return count($this->directories); + } + public function getIterator(): \PHPUnit\TextUI\Configuration\TestDirectoryCollectionIterator + { + return new \PHPUnit\TextUI\Configuration\TestDirectoryCollectionIterator($this); + } + public function isEmpty(): bool + { + return $this->count() === 0; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\Configuration; + +use function count; +use function iterator_count; +use Countable; +use Iterator; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @template-implements Iterator + */ +final class TestDirectoryCollectionIterator implements Countable, Iterator +{ + /** + * @psalm-var list + */ + private readonly array $directories; + private int $position = 0; + public function __construct(\PHPUnit\TextUI\Configuration\TestDirectoryCollection $directories) + { + $this->directories = $directories->asArray(); + } + public function count(): int + { + return iterator_count($this); + } + public function rewind(): void + { + $this->position = 0; + } + public function valid(): bool + { + return $this->position < count($this->directories); + } + public function key(): int + { + return $this->position; + } + public function current(): \PHPUnit\TextUI\Configuration\TestDirectory + { + return $this->directories[$this->position]; + } + public function next(): void + { + $this->position++; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\Configuration; + +use PHPUnit\Util\VersionComparisonOperator; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @psalm-immutable + */ +final class TestFile +{ + private readonly string $path; + private readonly string $phpVersion; + private readonly VersionComparisonOperator $phpVersionOperator; + public function __construct(string $path, string $phpVersion, VersionComparisonOperator $phpVersionOperator) + { + $this->path = $path; + $this->phpVersion = $phpVersion; + $this->phpVersionOperator = $phpVersionOperator; + } + public function path(): string + { + return $this->path; + } + public function phpVersion(): string + { + return $this->phpVersion; + } + public function phpVersionOperator(): VersionComparisonOperator + { + return $this->phpVersionOperator; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\Configuration; + +use function count; +use Countable; +use IteratorAggregate; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @psalm-immutable + * + * @template-implements IteratorAggregate + */ +final class TestFileCollection implements Countable, IteratorAggregate +{ + /** + * @psalm-var list + */ + private readonly array $files; + /** + * @psalm-param list $files + */ + public static function fromArray(array $files): self + { + return new self(...$files); + } + private function __construct(\PHPUnit\TextUI\Configuration\TestFile ...$files) + { + $this->files = $files; + } + /** + * @psalm-return list + */ + public function asArray(): array + { + return $this->files; + } + public function count(): int + { + return count($this->files); + } + public function getIterator(): \PHPUnit\TextUI\Configuration\TestFileCollectionIterator + { + return new \PHPUnit\TextUI\Configuration\TestFileCollectionIterator($this); + } + public function isEmpty(): bool + { + return $this->count() === 0; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\Configuration; + +use function count; +use function iterator_count; +use Countable; +use Iterator; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @template-implements Iterator + */ +final class TestFileCollectionIterator implements Countable, Iterator +{ + /** + * @psalm-var list + */ + private readonly array $files; + private int $position = 0; + public function __construct(\PHPUnit\TextUI\Configuration\TestFileCollection $files) + { + $this->files = $files->asArray(); + } + public function count(): int + { + return iterator_count($this); + } + public function rewind(): void + { + $this->position = 0; + } + public function valid(): bool + { + return $this->position < count($this->files); + } + public function key(): int + { + return $this->position; + } + public function current(): \PHPUnit\TextUI\Configuration\TestFile + { + return $this->files[$this->position]; + } + public function next(): void + { + $this->position++; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\Configuration; + +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @psalm-immutable + */ +final class TestSuite +{ + /** + * @psalm-var non-empty-string + */ + private readonly string $name; + private readonly \PHPUnit\TextUI\Configuration\TestDirectoryCollection $directories; + private readonly \PHPUnit\TextUI\Configuration\TestFileCollection $files; + private readonly \PHPUnit\TextUI\Configuration\FileCollection $exclude; + /** + * @psalm-param non-empty-string $name + */ + public function __construct(string $name, \PHPUnit\TextUI\Configuration\TestDirectoryCollection $directories, \PHPUnit\TextUI\Configuration\TestFileCollection $files, \PHPUnit\TextUI\Configuration\FileCollection $exclude) + { + $this->name = $name; + $this->directories = $directories; + $this->files = $files; + $this->exclude = $exclude; + } + /** + * @psalm-return non-empty-string + */ + public function name(): string + { + return $this->name; + } + public function directories(): \PHPUnit\TextUI\Configuration\TestDirectoryCollection + { + return $this->directories; + } + public function files(): \PHPUnit\TextUI\Configuration\TestFileCollection + { + return $this->files; + } + public function exclude(): \PHPUnit\TextUI\Configuration\FileCollection + { + return $this->exclude; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\Configuration; + +use function count; +use Countable; +use IteratorAggregate; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @psalm-immutable + * + * @template-implements IteratorAggregate + */ +final class TestSuiteCollection implements Countable, IteratorAggregate +{ + /** + * @psalm-var list + */ + private readonly array $testSuites; + /** + * @psalm-param list $testSuites + */ + public static function fromArray(array $testSuites): self + { + return new self(...$testSuites); + } + private function __construct(\PHPUnit\TextUI\Configuration\TestSuite ...$testSuites) + { + $this->testSuites = $testSuites; + } + /** + * @psalm-return list + */ + public function asArray(): array + { + return $this->testSuites; + } + public function count(): int + { + return count($this->testSuites); + } + public function getIterator(): \PHPUnit\TextUI\Configuration\TestSuiteCollectionIterator + { + return new \PHPUnit\TextUI\Configuration\TestSuiteCollectionIterator($this); + } + public function isEmpty(): bool + { + return $this->count() === 0; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\Configuration; + +use function count; +use function iterator_count; +use Countable; +use Iterator; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @template-implements Iterator + */ +final class TestSuiteCollectionIterator implements Countable, Iterator +{ + /** + * @psalm-var list + */ + private readonly array $testSuites; + private int $position = 0; + public function __construct(\PHPUnit\TextUI\Configuration\TestSuiteCollection $testSuites) + { + $this->testSuites = $testSuites->asArray(); + } + public function count(): int + { + return iterator_count($this); + } + public function rewind(): void + { + $this->position = 0; + } + public function valid(): bool + { + return $this->position < count($this->testSuites); + } + public function key(): int + { + return $this->position; + } + public function current(): \PHPUnit\TextUI\Configuration\TestSuite + { + return $this->testSuites[$this->position]; + } + public function next(): void + { + $this->position++; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\Configuration; + +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @psalm-immutable + */ +final class Variable +{ + private readonly string $name; + private readonly mixed $value; + private readonly bool $force; + public function __construct(string $name, mixed $value, bool $force) + { + $this->name = $name; + $this->value = $value; + $this->force = $force; + } + public function name(): string + { + return $this->name; + } + public function value(): mixed + { + return $this->value; + } + public function force(): bool + { + return $this->force; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\Configuration; + +use function count; +use Countable; +use IteratorAggregate; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @psalm-immutable + * + * @template-implements IteratorAggregate + */ +final class VariableCollection implements Countable, IteratorAggregate +{ + /** + * @psalm-var list + */ + private readonly array $variables; + /** + * @psalm-param list $variables + */ + public static function fromArray(array $variables): self + { + return new self(...$variables); + } + private function __construct(\PHPUnit\TextUI\Configuration\Variable ...$variables) + { + $this->variables = $variables; + } + /** + * @psalm-return list + */ + public function asArray(): array + { + return $this->variables; + } + public function count(): int + { + return count($this->variables); + } + public function getIterator(): \PHPUnit\TextUI\Configuration\VariableCollectionIterator + { + return new \PHPUnit\TextUI\Configuration\VariableCollectionIterator($this); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\Configuration; + +use function count; +use function iterator_count; +use Countable; +use Iterator; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @template-implements Iterator + */ +final class VariableCollectionIterator implements Countable, Iterator +{ + /** + * @psalm-var list + */ + private readonly array $variables; + private int $position = 0; + public function __construct(\PHPUnit\TextUI\Configuration\VariableCollection $variables) + { + $this->variables = $variables->asArray(); + } + public function count(): int + { + return iterator_count($this); + } + public function rewind(): void + { + $this->position = 0; + } + public function valid(): bool + { + return $this->position < count($this->variables); + } + public function key(): int + { + return $this->position; + } + public function current(): \PHPUnit\TextUI\Configuration\Variable + { + return $this->variables[$this->position]; + } + public function next(): void + { + $this->position++; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration\CodeCoverage; + +use function count; +use PHPUnit\TextUI\Configuration\Directory; +use PHPUnit\TextUI\Configuration\FileCollection; +use PHPUnit\TextUI\Configuration\FilterDirectoryCollection; +use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report\Clover; +use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report\Cobertura; +use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report\Crap4j; +use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report\Html; +use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report\Php; +use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report\Text; +use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report\Xml; +use PHPUnit\TextUI\XmlConfiguration\Exception; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + * + * @psalm-immutable + */ +final class CodeCoverage +{ + private readonly ?Directory $cacheDirectory; + private readonly FilterDirectoryCollection $directories; + private readonly FileCollection $files; + private readonly FilterDirectoryCollection $excludeDirectories; + private readonly FileCollection $excludeFiles; + private readonly bool $pathCoverage; + private readonly bool $includeUncoveredFiles; + private readonly bool $ignoreDeprecatedCodeUnits; + private readonly bool $disableCodeCoverageIgnore; + private readonly ?Clover $clover; + private readonly ?Cobertura $cobertura; + private readonly ?Crap4j $crap4j; + private readonly ?Html $html; + private readonly ?Php $php; + private readonly ?Text $text; + private readonly ?Xml $xml; + public function __construct(?Directory $cacheDirectory, FilterDirectoryCollection $directories, FileCollection $files, FilterDirectoryCollection $excludeDirectories, FileCollection $excludeFiles, bool $pathCoverage, bool $includeUncoveredFiles, bool $ignoreDeprecatedCodeUnits, bool $disableCodeCoverageIgnore, ?Clover $clover, ?Cobertura $cobertura, ?Crap4j $crap4j, ?Html $html, ?Php $php, ?Text $text, ?Xml $xml) + { + $this->cacheDirectory = $cacheDirectory; + $this->directories = $directories; + $this->files = $files; + $this->excludeDirectories = $excludeDirectories; + $this->excludeFiles = $excludeFiles; + $this->pathCoverage = $pathCoverage; + $this->includeUncoveredFiles = $includeUncoveredFiles; + $this->ignoreDeprecatedCodeUnits = $ignoreDeprecatedCodeUnits; + $this->disableCodeCoverageIgnore = $disableCodeCoverageIgnore; + $this->clover = $clover; + $this->cobertura = $cobertura; + $this->crap4j = $crap4j; + $this->html = $html; + $this->php = $php; + $this->text = $text; + $this->xml = $xml; + } + /** + * @psalm-assert-if-true !null $this->cacheDirectory + * + * @deprecated + */ + public function hasCacheDirectory(): bool + { + return $this->cacheDirectory !== null; + } + /** + * @throws Exception + * + * @deprecated + */ + public function cacheDirectory(): Directory + { + if (!$this->hasCacheDirectory()) { + throw new Exception('No cache directory has been configured'); + } + return $this->cacheDirectory; + } + public function hasNonEmptyListOfFilesToBeIncludedInCodeCoverageReport(): bool + { + return count($this->directories) > 0 || count($this->files) > 0; + } + public function directories(): FilterDirectoryCollection + { + return $this->directories; + } + public function files(): FileCollection + { + return $this->files; + } + public function excludeDirectories(): FilterDirectoryCollection + { + return $this->excludeDirectories; + } + public function excludeFiles(): FileCollection + { + return $this->excludeFiles; + } + public function pathCoverage(): bool + { + return $this->pathCoverage; + } + public function includeUncoveredFiles(): bool + { + return $this->includeUncoveredFiles; + } + public function ignoreDeprecatedCodeUnits(): bool + { + return $this->ignoreDeprecatedCodeUnits; + } + public function disableCodeCoverageIgnore(): bool + { + return $this->disableCodeCoverageIgnore; + } + /** + * @psalm-assert-if-true !null $this->clover + */ + public function hasClover(): bool + { + return $this->clover !== null; + } + /** + * @throws Exception + */ + public function clover(): Clover + { + if (!$this->hasClover()) { + throw new Exception('Code Coverage report "Clover XML" has not been configured'); + } + return $this->clover; + } + /** + * @psalm-assert-if-true !null $this->cobertura + */ + public function hasCobertura(): bool + { + return $this->cobertura !== null; + } + /** + * @throws Exception + */ + public function cobertura(): Cobertura + { + if (!$this->hasCobertura()) { + throw new Exception('Code Coverage report "Cobertura XML" has not been configured'); + } + return $this->cobertura; + } + /** + * @psalm-assert-if-true !null $this->crap4j + */ + public function hasCrap4j(): bool + { + return $this->crap4j !== null; + } + /** + * @throws Exception + */ + public function crap4j(): Crap4j + { + if (!$this->hasCrap4j()) { + throw new Exception('Code Coverage report "Crap4J" has not been configured'); + } + return $this->crap4j; + } + /** + * @psalm-assert-if-true !null $this->html + */ + public function hasHtml(): bool + { + return $this->html !== null; + } + /** + * @throws Exception + */ + public function html(): Html + { + if (!$this->hasHtml()) { + throw new Exception('Code Coverage report "HTML" has not been configured'); + } + return $this->html; + } + /** + * @psalm-assert-if-true !null $this->php + */ + public function hasPhp(): bool + { + return $this->php !== null; + } + /** + * @throws Exception + */ + public function php(): Php + { + if (!$this->hasPhp()) { + throw new Exception('Code Coverage report "PHP" has not been configured'); + } + return $this->php; + } + /** + * @psalm-assert-if-true !null $this->text + */ + public function hasText(): bool + { + return $this->text !== null; + } + /** + * @throws Exception + */ + public function text(): Text + { + if (!$this->hasText()) { + throw new Exception('Code Coverage report "Text" has not been configured'); + } + return $this->text; + } + /** + * @psalm-assert-if-true !null $this->xml + */ + public function hasXml(): bool + { + return $this->xml !== null; + } + /** + * @throws Exception + */ + public function xml(): Xml + { + if (!$this->hasXml()) { + throw new Exception('Code Coverage report "XML" has not been configured'); + } + return $this->xml; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report; + +use PHPUnit\TextUI\Configuration\File; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + * + * @psalm-immutable + */ +final class Clover +{ + private readonly File $target; + public function __construct(File $target) + { + $this->target = $target; + } + public function target(): File + { + return $this->target; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report; + +use PHPUnit\TextUI\Configuration\File; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + * + * @psalm-immutable + */ +final class Cobertura +{ + private readonly File $target; + public function __construct(File $target) + { + $this->target = $target; + } + public function target(): File + { + return $this->target; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report; + +use PHPUnit\TextUI\Configuration\File; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + * + * @psalm-immutable + */ +final class Crap4j +{ + private readonly File $target; + private readonly int $threshold; + public function __construct(File $target, int $threshold) + { + $this->target = $target; + $this->threshold = $threshold; + } + public function target(): File + { + return $this->target; + } + public function threshold(): int + { + return $this->threshold; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report; + +use PHPUnit\TextUI\Configuration\Directory; +use PHPUnit\TextUI\Configuration\NoCustomCssFileException; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + * + * @psalm-immutable + */ +final class Html +{ + private readonly Directory $target; + private readonly int $lowUpperBound; + private readonly int $highLowerBound; + private readonly string $colorSuccessLow; + private readonly string $colorSuccessMedium; + private readonly string $colorSuccessHigh; + private readonly string $colorWarning; + private readonly string $colorDanger; + private readonly ?string $customCssFile; + public function __construct(Directory $target, int $lowUpperBound, int $highLowerBound, string $colorSuccessLow, string $colorSuccessMedium, string $colorSuccessHigh, string $colorWarning, string $colorDanger, ?string $customCssFile) + { + $this->target = $target; + $this->lowUpperBound = $lowUpperBound; + $this->highLowerBound = $highLowerBound; + $this->colorSuccessLow = $colorSuccessLow; + $this->colorSuccessMedium = $colorSuccessMedium; + $this->colorSuccessHigh = $colorSuccessHigh; + $this->colorWarning = $colorWarning; + $this->colorDanger = $colorDanger; + $this->customCssFile = $customCssFile; + } + public function target(): Directory + { + return $this->target; + } + public function lowUpperBound(): int + { + return $this->lowUpperBound; + } + public function highLowerBound(): int + { + return $this->highLowerBound; + } + public function colorSuccessLow(): string + { + return $this->colorSuccessLow; + } + public function colorSuccessMedium(): string + { + return $this->colorSuccessMedium; + } + public function colorSuccessHigh(): string + { + return $this->colorSuccessHigh; + } + public function colorWarning(): string + { + return $this->colorWarning; + } + public function colorDanger(): string + { + return $this->colorDanger; + } + /** + * @psalm-assert-if-true !null $this->customCssFile + */ + public function hasCustomCssFile(): bool + { + return $this->customCssFile !== null; + } + /** + * @throws NoCustomCssFileException + */ + public function customCssFile(): string + { + if (!$this->hasCustomCssFile()) { + throw new NoCustomCssFileException(); + } + return $this->customCssFile; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report; + +use PHPUnit\TextUI\Configuration\File; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + * + * @psalm-immutable + */ +final class Php +{ + private readonly File $target; + public function __construct(File $target) + { + $this->target = $target; + } + public function target(): File + { + return $this->target; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report; + +use PHPUnit\TextUI\Configuration\File; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + * + * @psalm-immutable + */ +final class Text +{ + private readonly File $target; + private readonly bool $showUncoveredFiles; + private readonly bool $showOnlySummary; + public function __construct(File $target, bool $showUncoveredFiles, bool $showOnlySummary) + { + $this->target = $target; + $this->showUncoveredFiles = $showUncoveredFiles; + $this->showOnlySummary = $showOnlySummary; + } + public function target(): File + { + return $this->target; + } + public function showUncoveredFiles(): bool + { + return $this->showUncoveredFiles; + } + public function showOnlySummary(): bool + { + return $this->showOnlySummary; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report; + +use PHPUnit\TextUI\Configuration\Directory; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + * + * @psalm-immutable + */ +final class Xml +{ + private readonly Directory $target; + public function __construct(Directory $target) + { + $this->target = $target; + } + public function target(): Directory + { + return $this->target; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration; + +use PHPUnit\TextUI\Configuration\ExtensionBootstrapCollection; +use PHPUnit\TextUI\Configuration\Php; +use PHPUnit\TextUI\Configuration\Source; +use PHPUnit\TextUI\Configuration\TestSuiteCollection; +use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\CodeCoverage; +use PHPUnit\TextUI\XmlConfiguration\Logging\Logging; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + * + * @psalm-immutable + */ +abstract class Configuration +{ + private readonly ExtensionBootstrapCollection $extensions; + private readonly Source $source; + private readonly CodeCoverage $codeCoverage; + private readonly \PHPUnit\TextUI\XmlConfiguration\Groups $groups; + private readonly Logging $logging; + private readonly Php $php; + private readonly \PHPUnit\TextUI\XmlConfiguration\PHPUnit $phpunit; + private readonly TestSuiteCollection $testSuite; + public function __construct(ExtensionBootstrapCollection $extensions, Source $source, CodeCoverage $codeCoverage, \PHPUnit\TextUI\XmlConfiguration\Groups $groups, Logging $logging, Php $php, \PHPUnit\TextUI\XmlConfiguration\PHPUnit $phpunit, TestSuiteCollection $testSuite) + { + $this->extensions = $extensions; + $this->source = $source; + $this->codeCoverage = $codeCoverage; + $this->groups = $groups; + $this->logging = $logging; + $this->php = $php; + $this->phpunit = $phpunit; + $this->testSuite = $testSuite; + } + public function extensions(): ExtensionBootstrapCollection + { + return $this->extensions; + } + public function source(): Source + { + return $this->source; + } + public function codeCoverage(): CodeCoverage + { + return $this->codeCoverage; + } + public function groups(): \PHPUnit\TextUI\XmlConfiguration\Groups + { + return $this->groups; + } + public function logging(): Logging + { + return $this->logging; + } + public function php(): Php + { + return $this->php; + } + public function phpunit(): \PHPUnit\TextUI\XmlConfiguration\PHPUnit + { + return $this->phpunit; + } + public function testSuite(): TestSuiteCollection + { + return $this->testSuite; + } + /** + * @psalm-assert-if-true DefaultConfiguration $this + */ + public function isDefault(): bool + { + return \false; + } + /** + * @psalm-assert-if-true LoadedFromFileConfiguration $this + */ + public function wasLoadedFromFile(): bool + { + return \false; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration; + +use PHPUnit\Runner\TestSuiteSorter; +use PHPUnit\TextUI\Configuration\ConstantCollection; +use PHPUnit\TextUI\Configuration\DirectoryCollection; +use PHPUnit\TextUI\Configuration\ExtensionBootstrapCollection; +use PHPUnit\TextUI\Configuration\FileCollection; +use PHPUnit\TextUI\Configuration\FilterDirectoryCollection as CodeCoverageFilterDirectoryCollection; +use PHPUnit\TextUI\Configuration\GroupCollection; +use PHPUnit\TextUI\Configuration\IniSettingCollection; +use PHPUnit\TextUI\Configuration\Php; +use PHPUnit\TextUI\Configuration\Source; +use PHPUnit\TextUI\Configuration\TestSuiteCollection; +use PHPUnit\TextUI\Configuration\VariableCollection; +use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\CodeCoverage; +use PHPUnit\TextUI\XmlConfiguration\Logging\Logging; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + * + * @psalm-immutable + */ +final class DefaultConfiguration extends \PHPUnit\TextUI\XmlConfiguration\Configuration +{ + public static function create(): self + { + return new self(ExtensionBootstrapCollection::fromArray([]), new Source(null, \false, CodeCoverageFilterDirectoryCollection::fromArray([]), FileCollection::fromArray([]), CodeCoverageFilterDirectoryCollection::fromArray([]), FileCollection::fromArray([]), \false, \false, \false, \false, \false, \false, \false, \false, \false, \false), new CodeCoverage(null, CodeCoverageFilterDirectoryCollection::fromArray([]), FileCollection::fromArray([]), CodeCoverageFilterDirectoryCollection::fromArray([]), FileCollection::fromArray([]), \false, \true, \false, \false, null, null, null, null, null, null, null), new \PHPUnit\TextUI\XmlConfiguration\Groups(GroupCollection::fromArray([]), GroupCollection::fromArray([])), new Logging(null, null, null, null), new Php(DirectoryCollection::fromArray([]), IniSettingCollection::fromArray([]), ConstantCollection::fromArray([]), VariableCollection::fromArray([]), VariableCollection::fromArray([]), VariableCollection::fromArray([]), VariableCollection::fromArray([]), VariableCollection::fromArray([]), VariableCollection::fromArray([]), VariableCollection::fromArray([]), VariableCollection::fromArray([])), new \PHPUnit\TextUI\XmlConfiguration\PHPUnit(null, \true, null, 80, \PHPUnit\TextUI\Configuration\Configuration::COLOR_DEFAULT, \false, \false, \false, \false, \false, \false, \false, \false, \false, null, \false, \false, \false, \false, \false, \false, \false, \false, \false, \false, \false, \false, \false, \false, \false, \false, \false, null, \false, \false, \true, \false, \false, 1, 1, 10, 60, null, TestSuiteSorter::ORDER_DEFAULT, \true, \false, \false, \false, \false, \false, \false, 100), TestSuiteCollection::fromArray([])); + } + public function isDefault(): bool + { + return \true; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration; + +use RuntimeException; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class Exception extends RuntimeException implements \PHPUnit\Exception +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration; + +use function str_replace; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class Generator +{ + /** + * @var string + */ + private const TEMPLATE = <<<'EOT' - - - - This Schema file defines the rules by which the XML configuration file of PHPUnit 9.5 may be structured. - - - - - - Root Element - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - The main type specifying the document structure - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -sebastian/cli-parser + + + + {tests_directory} + + + + + + {src_directory} + + + + +EOT; + public function generateDefaultConfiguration(string $phpunitVersion, string $bootstrapScript, string $testsDirectory, string $srcDirectory, string $cacheDirectory): string + { + return str_replace(['{phpunit_version}', '{bootstrap_script}', '{tests_directory}', '{src_directory}', '{cache_directory}'], [$phpunitVersion, $bootstrapScript, $testsDirectory, $srcDirectory, $cacheDirectory], self::TEMPLATE); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration; + +use PHPUnit\TextUI\Configuration\GroupCollection; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + * + * @psalm-immutable + */ +final class Groups +{ + private readonly GroupCollection $include; + private readonly GroupCollection $exclude; + public function __construct(GroupCollection $include, GroupCollection $exclude) + { + $this->include = $include; + $this->exclude = $exclude; + } + public function hasInclude(): bool + { + return !$this->include->isEmpty(); + } + public function include(): GroupCollection + { + return $this->include; + } + public function hasExclude(): bool + { + return !$this->exclude->isEmpty(); + } + public function exclude(): GroupCollection + { + return $this->exclude; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration; + +use PHPUnit\TextUI\Configuration\ExtensionBootstrapCollection; +use PHPUnit\TextUI\Configuration\Php; +use PHPUnit\TextUI\Configuration\Source; +use PHPUnit\TextUI\Configuration\TestSuiteCollection; +use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\CodeCoverage; +use PHPUnit\TextUI\XmlConfiguration\Logging\Logging; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + * + * @psalm-immutable + */ +final class LoadedFromFileConfiguration extends \PHPUnit\TextUI\XmlConfiguration\Configuration +{ + private readonly string $filename; + private readonly \PHPUnit\TextUI\XmlConfiguration\ValidationResult $validationResult; + public function __construct(string $filename, \PHPUnit\TextUI\XmlConfiguration\ValidationResult $validationResult, ExtensionBootstrapCollection $extensions, Source $source, CodeCoverage $codeCoverage, \PHPUnit\TextUI\XmlConfiguration\Groups $groups, Logging $logging, Php $php, \PHPUnit\TextUI\XmlConfiguration\PHPUnit $phpunit, TestSuiteCollection $testSuite) + { + $this->filename = $filename; + $this->validationResult = $validationResult; + parent::__construct($extensions, $source, $codeCoverage, $groups, $logging, $php, $phpunit, $testSuite); + } + public function filename(): string + { + return $this->filename; + } + public function hasValidationErrors(): bool + { + return $this->validationResult->hasValidationErrors(); + } + public function validationErrors(): string + { + return $this->validationResult->asString(); + } + public function wasLoadedFromFile(): bool + { + return \true; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration; + +use const DIRECTORY_SEPARATOR; +use const PHP_VERSION; +use function assert; +use function defined; +use function dirname; +use function explode; +use function is_numeric; +use function preg_match; +use function realpath; +use function str_contains; +use function str_starts_with; +use function strlen; +use function strtolower; +use function substr; +use function trim; +use DOMDocument; +use DOMElement; +use DOMNode; +use DOMXPath; +use PHPUnit\Runner\TestSuiteSorter; +use PHPUnit\Runner\Version; +use PHPUnit\TextUI\Configuration\Configuration; +use PHPUnit\TextUI\Configuration\Constant; +use PHPUnit\TextUI\Configuration\ConstantCollection; +use PHPUnit\TextUI\Configuration\Directory; +use PHPUnit\TextUI\Configuration\DirectoryCollection; +use PHPUnit\TextUI\Configuration\ExtensionBootstrap; +use PHPUnit\TextUI\Configuration\ExtensionBootstrapCollection; +use PHPUnit\TextUI\Configuration\File; +use PHPUnit\TextUI\Configuration\FileCollection; +use PHPUnit\TextUI\Configuration\FilterDirectory; +use PHPUnit\TextUI\Configuration\FilterDirectoryCollection; +use PHPUnit\TextUI\Configuration\Group; +use PHPUnit\TextUI\Configuration\GroupCollection; +use PHPUnit\TextUI\Configuration\IniSetting; +use PHPUnit\TextUI\Configuration\IniSettingCollection; +use PHPUnit\TextUI\Configuration\Php; +use PHPUnit\TextUI\Configuration\Source; +use PHPUnit\TextUI\Configuration\TestDirectory; +use PHPUnit\TextUI\Configuration\TestDirectoryCollection; +use PHPUnit\TextUI\Configuration\TestFile; +use PHPUnit\TextUI\Configuration\TestFileCollection; +use PHPUnit\TextUI\Configuration\TestSuite as TestSuiteConfiguration; +use PHPUnit\TextUI\Configuration\TestSuiteCollection; +use PHPUnit\TextUI\Configuration\Variable; +use PHPUnit\TextUI\Configuration\VariableCollection; +use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\CodeCoverage; +use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report\Clover; +use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report\Cobertura; +use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report\Crap4j; +use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report\Html as CodeCoverageHtml; +use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report\Php as CodeCoveragePhp; +use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report\Text as CodeCoverageText; +use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report\Xml as CodeCoverageXml; +use PHPUnit\TextUI\XmlConfiguration\Logging\Junit; +use PHPUnit\TextUI\XmlConfiguration\Logging\Logging; +use PHPUnit\TextUI\XmlConfiguration\Logging\TeamCity; +use PHPUnit\TextUI\XmlConfiguration\Logging\TestDox\Html as TestDoxHtml; +use PHPUnit\TextUI\XmlConfiguration\Logging\TestDox\Text as TestDoxText; +use PHPUnit\Util\VersionComparisonOperator; +use PHPUnit\Util\Xml\Loader as XmlLoader; +use PHPUnit\Util\Xml\XmlException; +use PHPUnitPHAR\SebastianBergmann\CodeCoverage\Report\Html\Colors; +use PHPUnitPHAR\SebastianBergmann\CodeCoverage\Report\Thresholds; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class Loader +{ + /** + * @throws Exception + */ + public function load(string $filename): \PHPUnit\TextUI\XmlConfiguration\LoadedFromFileConfiguration + { + try { + $document = (new XmlLoader())->loadFile($filename); + } catch (XmlException $e) { + throw new \PHPUnit\TextUI\XmlConfiguration\Exception($e->getMessage(), $e->getCode(), $e); + } + $xpath = new DOMXPath($document); + try { + $xsdFilename = (new \PHPUnit\TextUI\XmlConfiguration\SchemaFinder())->find(Version::series()); + } catch (\PHPUnit\TextUI\XmlConfiguration\CannotFindSchemaException $e) { + throw new \PHPUnit\TextUI\XmlConfiguration\Exception($e->getMessage(), $e->getCode(), $e); + } + $configurationFileRealpath = realpath($filename); + return new \PHPUnit\TextUI\XmlConfiguration\LoadedFromFileConfiguration($configurationFileRealpath, (new \PHPUnit\TextUI\XmlConfiguration\Validator())->validate($document, $xsdFilename), $this->extensions($xpath), $this->source($configurationFileRealpath, $xpath), $this->codeCoverage($configurationFileRealpath, $xpath), $this->groups($xpath), $this->logging($configurationFileRealpath, $xpath), $this->php($configurationFileRealpath, $xpath), $this->phpunit($configurationFileRealpath, $document), $this->testSuite($configurationFileRealpath, $xpath)); + } + private function logging(string $filename, DOMXPath $xpath): Logging + { + $junit = null; + $element = $this->element($xpath, 'logging/junit'); + if ($element) { + $junit = new Junit(new File($this->toAbsolutePath($filename, (string) $this->getStringAttribute($element, 'outputFile')))); + } + $teamCity = null; + $element = $this->element($xpath, 'logging/teamcity'); + if ($element) { + $teamCity = new TeamCity(new File($this->toAbsolutePath($filename, (string) $this->getStringAttribute($element, 'outputFile')))); + } + $testDoxHtml = null; + $element = $this->element($xpath, 'logging/testdoxHtml'); + if ($element) { + $testDoxHtml = new TestDoxHtml(new File($this->toAbsolutePath($filename, (string) $this->getStringAttribute($element, 'outputFile')))); + } + $testDoxText = null; + $element = $this->element($xpath, 'logging/testdoxText'); + if ($element) { + $testDoxText = new TestDoxText(new File($this->toAbsolutePath($filename, (string) $this->getStringAttribute($element, 'outputFile')))); + } + return new Logging($junit, $teamCity, $testDoxHtml, $testDoxText); + } + private function extensions(DOMXPath $xpath): ExtensionBootstrapCollection + { + $extensionBootstrappers = []; + foreach ($xpath->query('extensions/bootstrap') as $bootstrap) { + assert($bootstrap instanceof DOMElement); + $parameters = []; + foreach ($xpath->query('parameter', $bootstrap) as $parameter) { + assert($parameter instanceof DOMElement); + $parameters[$parameter->getAttribute('name')] = $parameter->getAttribute('value'); + } + $extensionBootstrappers[] = new ExtensionBootstrap($bootstrap->getAttribute('class'), $parameters); + } + return ExtensionBootstrapCollection::fromArray($extensionBootstrappers); + } + /** + * @psalm-return non-empty-string + */ + private function toAbsolutePath(string $filename, string $path): string + { + $path = trim($path); + if (str_starts_with($path, '/')) { + 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') && !empty($path) && ($path[0] === '\\' || strlen($path) >= 3 && preg_match('#^[A-Z]:[/\\\\]#i', substr($path, 0, 3)))) { + return $path; + } + if (str_contains($path, '://')) { + return $path; + } + return dirname($filename) . DIRECTORY_SEPARATOR . $path; + } + private function source(string $filename, DOMXPath $xpath): Source + { + $baseline = null; + $restrictDeprecations = \false; + $restrictNotices = \false; + $restrictWarnings = \false; + $ignoreSuppressionOfDeprecations = \false; + $ignoreSuppressionOfPhpDeprecations = \false; + $ignoreSuppressionOfErrors = \false; + $ignoreSuppressionOfNotices = \false; + $ignoreSuppressionOfPhpNotices = \false; + $ignoreSuppressionOfWarnings = \false; + $ignoreSuppressionOfPhpWarnings = \false; + $element = $this->element($xpath, 'source'); + if ($element) { + $baseline = $this->getStringAttribute($element, 'baseline'); + if ($baseline !== null) { + $baseline = $this->toAbsolutePath($filename, $baseline); + } + $restrictDeprecations = $this->getBooleanAttribute($element, 'restrictDeprecations', \false); + $restrictNotices = $this->getBooleanAttribute($element, 'restrictNotices', \false); + $restrictWarnings = $this->getBooleanAttribute($element, 'restrictWarnings', \false); + $ignoreSuppressionOfDeprecations = $this->getBooleanAttribute($element, 'ignoreSuppressionOfDeprecations', \false); + $ignoreSuppressionOfPhpDeprecations = $this->getBooleanAttribute($element, 'ignoreSuppressionOfPhpDeprecations', \false); + $ignoreSuppressionOfErrors = $this->getBooleanAttribute($element, 'ignoreSuppressionOfErrors', \false); + $ignoreSuppressionOfNotices = $this->getBooleanAttribute($element, 'ignoreSuppressionOfNotices', \false); + $ignoreSuppressionOfPhpNotices = $this->getBooleanAttribute($element, 'ignoreSuppressionOfPhpNotices', \false); + $ignoreSuppressionOfWarnings = $this->getBooleanAttribute($element, 'ignoreSuppressionOfWarnings', \false); + $ignoreSuppressionOfPhpWarnings = $this->getBooleanAttribute($element, 'ignoreSuppressionOfPhpWarnings', \false); + } + return new Source($baseline, \false, $this->readFilterDirectories($filename, $xpath, 'source/include/directory'), $this->readFilterFiles($filename, $xpath, 'source/include/file'), $this->readFilterDirectories($filename, $xpath, 'source/exclude/directory'), $this->readFilterFiles($filename, $xpath, 'source/exclude/file'), $restrictDeprecations, $restrictNotices, $restrictWarnings, $ignoreSuppressionOfDeprecations, $ignoreSuppressionOfPhpDeprecations, $ignoreSuppressionOfErrors, $ignoreSuppressionOfNotices, $ignoreSuppressionOfPhpNotices, $ignoreSuppressionOfWarnings, $ignoreSuppressionOfPhpWarnings); + } + private function codeCoverage(string $filename, DOMXPath $xpath): CodeCoverage + { + $cacheDirectory = null; + $pathCoverage = \false; + $includeUncoveredFiles = \true; + $ignoreDeprecatedCodeUnits = \false; + $disableCodeCoverageIgnore = \false; + $element = $this->element($xpath, 'coverage'); + if ($element) { + $cacheDirectory = $this->getStringAttribute($element, 'cacheDirectory'); + if ($cacheDirectory !== null) { + $cacheDirectory = new Directory($this->toAbsolutePath($filename, $cacheDirectory)); + } + $pathCoverage = $this->getBooleanAttribute($element, 'pathCoverage', \false); + $includeUncoveredFiles = $this->getBooleanAttribute($element, 'includeUncoveredFiles', \true); + $ignoreDeprecatedCodeUnits = $this->getBooleanAttribute($element, 'ignoreDeprecatedCodeUnits', \false); + $disableCodeCoverageIgnore = $this->getBooleanAttribute($element, 'disableCodeCoverageIgnore', \false); + } + $clover = null; + $element = $this->element($xpath, 'coverage/report/clover'); + if ($element) { + $clover = new Clover(new File($this->toAbsolutePath($filename, (string) $this->getStringAttribute($element, 'outputFile')))); + } + $cobertura = null; + $element = $this->element($xpath, 'coverage/report/cobertura'); + if ($element) { + $cobertura = new Cobertura(new File($this->toAbsolutePath($filename, (string) $this->getStringAttribute($element, 'outputFile')))); + } + $crap4j = null; + $element = $this->element($xpath, 'coverage/report/crap4j'); + if ($element) { + $crap4j = new Crap4j(new File($this->toAbsolutePath($filename, (string) $this->getStringAttribute($element, 'outputFile'))), $this->getIntegerAttribute($element, 'threshold', 30)); + } + $html = null; + $element = $this->element($xpath, 'coverage/report/html'); + if ($element) { + $defaultColors = Colors::default(); + $defaultThresholds = Thresholds::default(); + $html = new CodeCoverageHtml(new Directory($this->toAbsolutePath($filename, (string) $this->getStringAttribute($element, 'outputDirectory'))), $this->getIntegerAttribute($element, 'lowUpperBound', $defaultThresholds->lowUpperBound()), $this->getIntegerAttribute($element, 'highLowerBound', $defaultThresholds->highLowerBound()), $this->getStringAttributeWithDefault($element, 'colorSuccessLow', $defaultColors->successLow()), $this->getStringAttributeWithDefault($element, 'colorSuccessMedium', $defaultColors->successMedium()), $this->getStringAttributeWithDefault($element, 'colorSuccessHigh', $defaultColors->successHigh()), $this->getStringAttributeWithDefault($element, 'colorWarning', $defaultColors->warning()), $this->getStringAttributeWithDefault($element, 'colorDanger', $defaultColors->danger()), $this->getStringAttribute($element, 'customCssFile')); + } + $php = null; + $element = $this->element($xpath, 'coverage/report/php'); + if ($element) { + $php = new CodeCoveragePhp(new File($this->toAbsolutePath($filename, (string) $this->getStringAttribute($element, 'outputFile')))); + } + $text = null; + $element = $this->element($xpath, 'coverage/report/text'); + if ($element) { + $text = new CodeCoverageText(new File($this->toAbsolutePath($filename, (string) $this->getStringAttribute($element, 'outputFile'))), $this->getBooleanAttribute($element, 'showUncoveredFiles', \false), $this->getBooleanAttribute($element, 'showOnlySummary', \false)); + } + $xml = null; + $element = $this->element($xpath, 'coverage/report/xml'); + if ($element) { + $xml = new CodeCoverageXml(new Directory($this->toAbsolutePath($filename, (string) $this->getStringAttribute($element, 'outputDirectory')))); + } + return new CodeCoverage($cacheDirectory, $this->readFilterDirectories($filename, $xpath, 'coverage/include/directory'), $this->readFilterFiles($filename, $xpath, 'coverage/include/file'), $this->readFilterDirectories($filename, $xpath, 'coverage/exclude/directory'), $this->readFilterFiles($filename, $xpath, 'coverage/exclude/file'), $pathCoverage, $includeUncoveredFiles, $ignoreDeprecatedCodeUnits, $disableCodeCoverageIgnore, $clover, $cobertura, $crap4j, $html, $php, $text, $xml); + } + private function getBoolean(string $value, bool $default): bool + { + if (strtolower($value) === 'false') { + return \false; + } + if (strtolower($value) === 'true') { + return \true; + } + return $default; + } + private function getValue(string $value): bool|string + { + if (strtolower($value) === 'false') { + return \false; + } + if (strtolower($value) === 'true') { + return \true; + } + return $value; + } + private function readFilterDirectories(string $filename, DOMXPath $xpath, string $query): FilterDirectoryCollection + { + $directories = []; + foreach ($xpath->query($query) as $directoryNode) { + assert($directoryNode instanceof DOMElement); + $directoryPath = $directoryNode->textContent; + if (!$directoryPath) { + continue; + } + $directories[] = new FilterDirectory($this->toAbsolutePath($filename, $directoryPath), $directoryNode->hasAttribute('prefix') ? $directoryNode->getAttribute('prefix') : '', $directoryNode->hasAttribute('suffix') ? $directoryNode->getAttribute('suffix') : '.php'); + } + return FilterDirectoryCollection::fromArray($directories); + } + private function readFilterFiles(string $filename, DOMXPath $xpath, string $query): FileCollection + { + $files = []; + foreach ($xpath->query($query) as $file) { + assert($file instanceof DOMNode); + $filePath = $file->textContent; + if ($filePath) { + $files[] = new File($this->toAbsolutePath($filename, $filePath)); + } + } + return FileCollection::fromArray($files); + } + private function groups(DOMXPath $xpath): \PHPUnit\TextUI\XmlConfiguration\Groups + { + $include = []; + $exclude = []; + foreach ($xpath->query('groups/include/group') as $group) { + assert($group instanceof DOMNode); + $include[] = new Group($group->textContent); + } + foreach ($xpath->query('groups/exclude/group') as $group) { + assert($group instanceof DOMNode); + $exclude[] = new Group($group->textContent); + } + return new \PHPUnit\TextUI\XmlConfiguration\Groups(GroupCollection::fromArray($include), GroupCollection::fromArray($exclude)); + } + private function getBooleanAttribute(DOMElement $element, string $attribute, bool $default): bool + { + if (!$element->hasAttribute($attribute)) { + return $default; + } + return $this->getBoolean($element->getAttribute($attribute), \false); + } + private function getIntegerAttribute(DOMElement $element, string $attribute, int $default): int + { + if (!$element->hasAttribute($attribute)) { + return $default; + } + return $this->getInteger($element->getAttribute($attribute), $default); + } + private function getStringAttribute(DOMElement $element, string $attribute): ?string + { + if (!$element->hasAttribute($attribute)) { + return null; + } + return $element->getAttribute($attribute); + } + private function getStringAttributeWithDefault(DOMElement $element, string $attribute, string $default): string + { + if (!$element->hasAttribute($attribute)) { + return $default; + } + return $element->getAttribute($attribute); + } + private function getInteger(string $value, int $default): int + { + if (is_numeric($value)) { + return (int) $value; + } + return $default; + } + private function php(string $filename, DOMXPath $xpath): Php + { + $includePaths = []; + foreach ($xpath->query('php/includePath') as $includePath) { + assert($includePath instanceof DOMNode); + $path = $includePath->textContent; + if ($path) { + $includePaths[] = new Directory($this->toAbsolutePath($filename, $path)); + } + } + $iniSettings = []; + foreach ($xpath->query('php/ini') as $ini) { + assert($ini instanceof DOMElement); + $iniSettings[] = new IniSetting($ini->getAttribute('name'), $ini->getAttribute('value')); + } + $constants = []; + foreach ($xpath->query('php/const') as $const) { + assert($const instanceof DOMElement); + $value = $const->getAttribute('value'); + $constants[] = new Constant($const->getAttribute('name'), $this->getValue($value)); + } + $variables = ['var' => [], 'env' => [], 'post' => [], 'get' => [], 'cookie' => [], 'server' => [], 'files' => [], 'request' => []]; + foreach (['var', 'env', 'post', 'get', 'cookie', 'server', 'files', 'request'] as $array) { + foreach ($xpath->query('php/' . $array) as $var) { + assert($var instanceof DOMElement); + $name = $var->getAttribute('name'); + $value = $var->getAttribute('value'); + $force = \false; + $verbatim = \false; + if ($var->hasAttribute('force')) { + $force = $this->getBoolean($var->getAttribute('force'), \false); + } + if ($var->hasAttribute('verbatim')) { + $verbatim = $this->getBoolean($var->getAttribute('verbatim'), \false); + } + if (!$verbatim) { + $value = $this->getValue($value); + } + $variables[$array][] = new Variable($name, $value, $force); + } + } + return new Php(DirectoryCollection::fromArray($includePaths), IniSettingCollection::fromArray($iniSettings), ConstantCollection::fromArray($constants), VariableCollection::fromArray($variables['var']), VariableCollection::fromArray($variables['env']), VariableCollection::fromArray($variables['post']), VariableCollection::fromArray($variables['get']), VariableCollection::fromArray($variables['cookie']), VariableCollection::fromArray($variables['server']), VariableCollection::fromArray($variables['files']), VariableCollection::fromArray($variables['request'])); + } + private function phpunit(string $filename, DOMDocument $document): \PHPUnit\TextUI\XmlConfiguration\PHPUnit + { + $executionOrder = TestSuiteSorter::ORDER_DEFAULT; + $defectsFirst = \false; + $resolveDependencies = $this->getBooleanAttribute($document->documentElement, 'resolveDependencies', \true); + if ($document->documentElement->hasAttribute('executionOrder')) { + foreach (explode(',', $document->documentElement->getAttribute('executionOrder')) as $order) { + switch ($order) { + case 'default': + $executionOrder = TestSuiteSorter::ORDER_DEFAULT; + $defectsFirst = \false; + $resolveDependencies = \true; + break; + case 'depends': + $resolveDependencies = \true; + break; + case 'no-depends': + $resolveDependencies = \false; + break; + case 'defects': + $defectsFirst = \true; + break; + case 'duration': + $executionOrder = TestSuiteSorter::ORDER_DURATION; + break; + case 'random': + $executionOrder = TestSuiteSorter::ORDER_RANDOMIZED; + break; + case 'reverse': + $executionOrder = TestSuiteSorter::ORDER_REVERSED; + break; + case 'size': + $executionOrder = TestSuiteSorter::ORDER_SIZE; + break; + } + } + } + $cacheDirectory = $this->getStringAttribute($document->documentElement, 'cacheDirectory'); + if ($cacheDirectory !== null) { + $cacheDirectory = $this->toAbsolutePath($filename, $cacheDirectory); + } + $cacheResultFile = $this->getStringAttribute($document->documentElement, 'cacheResultFile'); + if ($cacheResultFile !== null) { + $cacheResultFile = $this->toAbsolutePath($filename, $cacheResultFile); + } + $bootstrap = $this->getStringAttribute($document->documentElement, 'bootstrap'); + if ($bootstrap !== null) { + $bootstrap = $this->toAbsolutePath($filename, $bootstrap); + } + $extensionsDirectory = $this->getStringAttribute($document->documentElement, 'extensionsDirectory'); + if ($extensionsDirectory !== null) { + $extensionsDirectory = $this->toAbsolutePath($filename, $extensionsDirectory); + } + $backupStaticProperties = \false; + if ($document->documentElement->hasAttribute('backupStaticProperties')) { + $backupStaticProperties = $this->getBooleanAttribute($document->documentElement, 'backupStaticProperties', \false); + } elseif ($document->documentElement->hasAttribute('backupStaticAttributes')) { + $backupStaticProperties = $this->getBooleanAttribute($document->documentElement, 'backupStaticAttributes', \false); + } + $requireCoverageMetadata = \false; + if ($document->documentElement->hasAttribute('requireCoverageMetadata')) { + $requireCoverageMetadata = $this->getBooleanAttribute($document->documentElement, 'requireCoverageMetadata', \false); + } elseif ($document->documentElement->hasAttribute('forceCoversAnnotation')) { + $requireCoverageMetadata = $this->getBooleanAttribute($document->documentElement, 'forceCoversAnnotation', \false); + } + $beStrictAboutCoverageMetadata = \false; + if ($document->documentElement->hasAttribute('beStrictAboutCoverageMetadata')) { + $beStrictAboutCoverageMetadata = $this->getBooleanAttribute($document->documentElement, 'beStrictAboutCoverageMetadata', \false); + } elseif ($document->documentElement->hasAttribute('forceCoversAnnotation')) { + $beStrictAboutCoverageMetadata = $this->getBooleanAttribute($document->documentElement, 'beStrictAboutCoversAnnotation', \false); + } + return new \PHPUnit\TextUI\XmlConfiguration\PHPUnit($cacheDirectory, $this->getBooleanAttribute($document->documentElement, 'cacheResult', \true), $cacheResultFile, $this->getColumns($document), $this->getColors($document), $this->getBooleanAttribute($document->documentElement, 'stderr', \false), $this->getBooleanAttribute($document->documentElement, 'displayDetailsOnIncompleteTests', \false), $this->getBooleanAttribute($document->documentElement, 'displayDetailsOnSkippedTests', \false), $this->getBooleanAttribute($document->documentElement, 'displayDetailsOnTestsThatTriggerDeprecations', \false), $this->getBooleanAttribute($document->documentElement, 'displayDetailsOnTestsThatTriggerErrors', \false), $this->getBooleanAttribute($document->documentElement, 'displayDetailsOnTestsThatTriggerNotices', \false), $this->getBooleanAttribute($document->documentElement, 'displayDetailsOnTestsThatTriggerWarnings', \false), $this->getBooleanAttribute($document->documentElement, 'reverseDefectList', \false), $requireCoverageMetadata, $bootstrap, $this->getBooleanAttribute($document->documentElement, 'processIsolation', \false), $this->getBooleanAttribute($document->documentElement, 'failOnDeprecation', \false), $this->getBooleanAttribute($document->documentElement, 'failOnEmptyTestSuite', \false), $this->getBooleanAttribute($document->documentElement, 'failOnIncomplete', \false), $this->getBooleanAttribute($document->documentElement, 'failOnNotice', \false), $this->getBooleanAttribute($document->documentElement, 'failOnRisky', \false), $this->getBooleanAttribute($document->documentElement, 'failOnSkipped', \false), $this->getBooleanAttribute($document->documentElement, 'failOnWarning', \false), $this->getBooleanAttribute($document->documentElement, 'stopOnDefect', \false), $this->getBooleanAttribute($document->documentElement, 'stopOnDeprecation', \false), $this->getBooleanAttribute($document->documentElement, 'stopOnError', \false), $this->getBooleanAttribute($document->documentElement, 'stopOnFailure', \false), $this->getBooleanAttribute($document->documentElement, 'stopOnIncomplete', \false), $this->getBooleanAttribute($document->documentElement, 'stopOnNotice', \false), $this->getBooleanAttribute($document->documentElement, 'stopOnRisky', \false), $this->getBooleanAttribute($document->documentElement, 'stopOnSkipped', \false), $this->getBooleanAttribute($document->documentElement, 'stopOnWarning', \false), $extensionsDirectory, $this->getBooleanAttribute($document->documentElement, 'beStrictAboutChangesToGlobalState', \false), $this->getBooleanAttribute($document->documentElement, 'beStrictAboutOutputDuringTests', \false), $this->getBooleanAttribute($document->documentElement, 'beStrictAboutTestsThatDoNotTestAnything', \true), $beStrictAboutCoverageMetadata, $this->getBooleanAttribute($document->documentElement, 'enforceTimeLimit', \false), $this->getIntegerAttribute($document->documentElement, 'defaultTimeLimit', 1), $this->getIntegerAttribute($document->documentElement, 'timeoutForSmallTests', 1), $this->getIntegerAttribute($document->documentElement, 'timeoutForMediumTests', 10), $this->getIntegerAttribute($document->documentElement, 'timeoutForLargeTests', 60), $this->getStringAttribute($document->documentElement, 'defaultTestSuite'), $executionOrder, $resolveDependencies, $defectsFirst, $this->getBooleanAttribute($document->documentElement, 'backupGlobals', \false), $backupStaticProperties, $this->getBooleanAttribute($document->documentElement, 'registerMockObjectsFromTestArgumentsRecursively', \false), $this->getBooleanAttribute($document->documentElement, 'testdox', \false), $this->getBooleanAttribute($document->documentElement, 'controlGarbageCollector', \false), $this->getIntegerAttribute($document->documentElement, 'numberOfTestsBeforeGarbageCollection', 100)); + } + private function getColors(DOMDocument $document): string + { + $colors = Configuration::COLOR_DEFAULT; + if ($document->documentElement->hasAttribute('colors')) { + /* only allow boolean for compatibility with previous versions + 'always' only allowed from command line */ + if ($this->getBoolean($document->documentElement->getAttribute('colors'), \false)) { + $colors = Configuration::COLOR_AUTO; + } else { + $colors = Configuration::COLOR_NEVER; + } + } + return $colors; + } + private function getColumns(DOMDocument $document): int|string + { + $columns = 80; + if ($document->documentElement->hasAttribute('columns')) { + $columns = $document->documentElement->getAttribute('columns'); + if ($columns !== 'max') { + $columns = $this->getInteger($columns, 80); + } + } + return $columns; + } + private function testSuite(string $filename, DOMXPath $xpath): TestSuiteCollection + { + $testSuites = []; + foreach ($this->getTestSuiteElements($xpath) as $element) { + $exclude = []; + foreach ($element->getElementsByTagName('exclude') as $excludeNode) { + $excludeFile = $excludeNode->textContent; + if ($excludeFile) { + $exclude[] = new File($this->toAbsolutePath($filename, $excludeFile)); + } + } + $directories = []; + foreach ($element->getElementsByTagName('directory') as $directoryNode) { + assert($directoryNode instanceof DOMElement); + $directory = $directoryNode->textContent; + if (empty($directory)) { + continue; + } + $prefix = ''; + if ($directoryNode->hasAttribute('prefix')) { + $prefix = $directoryNode->getAttribute('prefix'); + } + $suffix = 'Test.php'; + if ($directoryNode->hasAttribute('suffix')) { + $suffix = $directoryNode->getAttribute('suffix'); + } + $phpVersion = PHP_VERSION; + if ($directoryNode->hasAttribute('phpVersion')) { + $phpVersion = $directoryNode->getAttribute('phpVersion'); + } + $phpVersionOperator = new VersionComparisonOperator('>='); + if ($directoryNode->hasAttribute('phpVersionOperator')) { + $phpVersionOperator = new VersionComparisonOperator($directoryNode->getAttribute('phpVersionOperator')); + } + $directories[] = new TestDirectory($this->toAbsolutePath($filename, $directory), $prefix, $suffix, $phpVersion, $phpVersionOperator); + } + $files = []; + foreach ($element->getElementsByTagName('file') as $fileNode) { + assert($fileNode instanceof DOMElement); + $file = $fileNode->textContent; + if (empty($file)) { + continue; + } + $phpVersion = PHP_VERSION; + if ($fileNode->hasAttribute('phpVersion')) { + $phpVersion = $fileNode->getAttribute('phpVersion'); + } + $phpVersionOperator = new VersionComparisonOperator('>='); + if ($fileNode->hasAttribute('phpVersionOperator')) { + $phpVersionOperator = new VersionComparisonOperator($fileNode->getAttribute('phpVersionOperator')); + } + $files[] = new TestFile($this->toAbsolutePath($filename, $file), $phpVersion, $phpVersionOperator); + } + $name = $element->getAttribute('name'); + assert(!empty($name)); + $testSuites[] = new TestSuiteConfiguration($name, TestDirectoryCollection::fromArray($directories), TestFileCollection::fromArray($files), FileCollection::fromArray($exclude)); + } + return TestSuiteCollection::fromArray($testSuites); + } + /** + * @psalm-return list + */ + private function getTestSuiteElements(DOMXPath $xpath): array + { + $elements = []; + $testSuiteNodes = $xpath->query('testsuites/testsuite'); + if ($testSuiteNodes->length === 0) { + $testSuiteNodes = $xpath->query('testsuite'); + } + if ($testSuiteNodes->length === 1) { + $element = $testSuiteNodes->item(0); + assert($element instanceof DOMElement); + $elements[] = $element; + } else { + foreach ($testSuiteNodes as $testSuiteNode) { + assert($testSuiteNode instanceof DOMElement); + $elements[] = $testSuiteNode; + } + } + return $elements; + } + private function element(DOMXPath $xpath, string $element): ?DOMElement + { + $nodes = $xpath->query($element); + if ($nodes->length === 1) { + $node = $nodes->item(0); + assert($node instanceof DOMElement); + return $node; + } + return null; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration\Logging; + +use PHPUnit\TextUI\Configuration\File; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + * + * @psalm-immutable + */ +final class Junit +{ + private readonly File $target; + public function __construct(File $target) + { + $this->target = $target; + } + public function target(): File + { + return $this->target; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration\Logging; + +use PHPUnit\TextUI\XmlConfiguration\Exception; +use PHPUnit\TextUI\XmlConfiguration\Logging\TestDox\Html as TestDoxHtml; +use PHPUnit\TextUI\XmlConfiguration\Logging\TestDox\Text as TestDoxText; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + * + * @psalm-immutable + */ +final class Logging +{ + private readonly ?\PHPUnit\TextUI\XmlConfiguration\Logging\Junit $junit; + private readonly ?\PHPUnit\TextUI\XmlConfiguration\Logging\TeamCity $teamCity; + private readonly ?TestDoxHtml $testDoxHtml; + private readonly ?TestDoxText $testDoxText; + public function __construct(?\PHPUnit\TextUI\XmlConfiguration\Logging\Junit $junit, ?\PHPUnit\TextUI\XmlConfiguration\Logging\TeamCity $teamCity, ?TestDoxHtml $testDoxHtml, ?TestDoxText $testDoxText) + { + $this->junit = $junit; + $this->teamCity = $teamCity; + $this->testDoxHtml = $testDoxHtml; + $this->testDoxText = $testDoxText; + } + public function hasJunit(): bool + { + return $this->junit !== null; + } + /** + * @throws Exception + */ + public function junit(): \PHPUnit\TextUI\XmlConfiguration\Logging\Junit + { + if ($this->junit === null) { + throw new Exception('Logger "JUnit XML" is not configured'); + } + return $this->junit; + } + public function hasTeamCity(): bool + { + return $this->teamCity !== null; + } + /** + * @throws Exception + */ + public function teamCity(): \PHPUnit\TextUI\XmlConfiguration\Logging\TeamCity + { + if ($this->teamCity === null) { + throw new Exception('Logger "Team City" is not configured'); + } + return $this->teamCity; + } + public function hasTestDoxHtml(): bool + { + return $this->testDoxHtml !== null; + } + /** + * @throws Exception + */ + public function testDoxHtml(): TestDoxHtml + { + if ($this->testDoxHtml === null) { + throw new Exception('Logger "TestDox HTML" is not configured'); + } + return $this->testDoxHtml; + } + public function hasTestDoxText(): bool + { + return $this->testDoxText !== null; + } + /** + * @throws Exception + */ + public function testDoxText(): TestDoxText + { + if ($this->testDoxText === null) { + throw new Exception('Logger "TestDox Text" is not configured'); + } + return $this->testDoxText; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration\Logging; + +use PHPUnit\TextUI\Configuration\File; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + * + * @psalm-immutable + */ +final class TeamCity +{ + private readonly File $target; + public function __construct(File $target) + { + $this->target = $target; + } + public function target(): File + { + return $this->target; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration\Logging\TestDox; + +use PHPUnit\TextUI\Configuration\File; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + * + * @psalm-immutable + */ +final class Html +{ + private readonly File $target; + public function __construct(File $target) + { + $this->target = $target; + } + public function target(): File + { + return $this->target; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration\Logging\TestDox; + +use PHPUnit\TextUI\Configuration\File; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + * + * @psalm-immutable + */ +final class Text +{ + private readonly File $target; + public function __construct(File $target) + { + $this->target = $target; + } + public function target(): File + { + return $this->target; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration; + +use function version_compare; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class MigrationBuilder +{ + private const AVAILABLE_MIGRATIONS = ['8.5' => [\PHPUnit\TextUI\XmlConfiguration\RemoveLogTypes::class], '9.2' => [\PHPUnit\TextUI\XmlConfiguration\RemoveCacheTokensAttribute::class, \PHPUnit\TextUI\XmlConfiguration\IntroduceCoverageElement::class, \PHPUnit\TextUI\XmlConfiguration\MoveAttributesFromRootToCoverage::class, \PHPUnit\TextUI\XmlConfiguration\MoveAttributesFromFilterWhitelistToCoverage::class, \PHPUnit\TextUI\XmlConfiguration\MoveWhitelistIncludesToCoverage::class, \PHPUnit\TextUI\XmlConfiguration\MoveWhitelistExcludesToCoverage::class, \PHPUnit\TextUI\XmlConfiguration\RemoveEmptyFilter::class, \PHPUnit\TextUI\XmlConfiguration\CoverageCloverToReport::class, \PHPUnit\TextUI\XmlConfiguration\CoverageCrap4jToReport::class, \PHPUnit\TextUI\XmlConfiguration\CoverageHtmlToReport::class, \PHPUnit\TextUI\XmlConfiguration\CoveragePhpToReport::class, \PHPUnit\TextUI\XmlConfiguration\CoverageTextToReport::class, \PHPUnit\TextUI\XmlConfiguration\CoverageXmlToReport::class, \PHPUnit\TextUI\XmlConfiguration\ConvertLogTypes::class], '9.5' => [\PHPUnit\TextUI\XmlConfiguration\RemoveListeners::class, \PHPUnit\TextUI\XmlConfiguration\RemoveTestSuiteLoaderAttributes::class, \PHPUnit\TextUI\XmlConfiguration\RemoveCacheResultFileAttribute::class, \PHPUnit\TextUI\XmlConfiguration\RemoveCoverageElementCacheDirectoryAttribute::class, \PHPUnit\TextUI\XmlConfiguration\RemoveCoverageElementProcessUncoveredFilesAttribute::class, \PHPUnit\TextUI\XmlConfiguration\IntroduceCacheDirectoryAttribute::class, \PHPUnit\TextUI\XmlConfiguration\RenameBackupStaticAttributesAttribute::class, \PHPUnit\TextUI\XmlConfiguration\RemoveBeStrictAboutResourceUsageDuringSmallTestsAttribute::class, \PHPUnit\TextUI\XmlConfiguration\RemoveBeStrictAboutTodoAnnotatedTestsAttribute::class, \PHPUnit\TextUI\XmlConfiguration\RemovePrinterAttributes::class, \PHPUnit\TextUI\XmlConfiguration\RemoveVerboseAttribute::class, \PHPUnit\TextUI\XmlConfiguration\RenameForceCoversAnnotationAttribute::class, \PHPUnit\TextUI\XmlConfiguration\RenameBeStrictAboutCoversAnnotationAttribute::class, \PHPUnit\TextUI\XmlConfiguration\RemoveConversionToExceptionsAttributes::class, \PHPUnit\TextUI\XmlConfiguration\RemoveNoInteractionAttribute::class, \PHPUnit\TextUI\XmlConfiguration\RemoveLoggingElements::class, \PHPUnit\TextUI\XmlConfiguration\RemoveTestDoxGroupsElement::class], '10.0' => [\PHPUnit\TextUI\XmlConfiguration\MoveCoverageDirectoriesToSource::class]]; + /** + * @throws MigrationBuilderException + */ + public function build(string $fromVersion): array + { + $stack = [new \PHPUnit\TextUI\XmlConfiguration\UpdateSchemaLocation()]; + foreach (self::AVAILABLE_MIGRATIONS as $version => $migrations) { + if (version_compare($version, $fromVersion, '<')) { + continue; + } + foreach ($migrations as $migration) { + $stack[] = new $migration(); + } + } + return $stack; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration; + +use PHPUnit\Exception; +use RuntimeException; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class MigrationBuilderException extends RuntimeException implements Exception +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration; + +use PHPUnit\Exception; +use RuntimeException; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class MigrationException extends RuntimeException implements Exception +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration; + +use DOMDocument; +use DOMElement; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class ConvertLogTypes implements \PHPUnit\TextUI\XmlConfiguration\Migration +{ + public function migrate(DOMDocument $document): void + { + $logging = $document->getElementsByTagName('logging')->item(0); + if (!$logging instanceof DOMElement) { + return; + } + $types = ['junit' => 'junit', 'teamcity' => 'teamcity', 'testdox-html' => 'testdoxHtml', 'testdox-text' => 'testdoxText', 'testdox-xml' => 'testdoxXml', 'plain' => 'text']; + $logNodes = []; + foreach ($logging->getElementsByTagName('log') as $logNode) { + if (!isset($types[$logNode->getAttribute('type')])) { + continue; + } + $logNodes[] = $logNode; + } + foreach ($logNodes as $oldNode) { + $newLogNode = $document->createElement($types[$oldNode->getAttribute('type')]); + $newLogNode->setAttribute('outputFile', $oldNode->getAttribute('target')); + $logging->replaceChild($newLogNode, $oldNode); + } + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration; + +use DOMElement; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class CoverageCloverToReport extends \PHPUnit\TextUI\XmlConfiguration\LogToReportMigration +{ + protected function forType(): string + { + return 'coverage-clover'; + } + protected function toReportFormat(DOMElement $logNode): DOMElement + { + $clover = $logNode->ownerDocument->createElement('clover'); + $clover->setAttribute('outputFile', $logNode->getAttribute('target')); + return $clover; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration; + +use DOMElement; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class CoverageCrap4jToReport extends \PHPUnit\TextUI\XmlConfiguration\LogToReportMigration +{ + protected function forType(): string + { + return 'coverage-crap4j'; + } + protected function toReportFormat(DOMElement $logNode): DOMElement + { + $crap4j = $logNode->ownerDocument->createElement('crap4j'); + $crap4j->setAttribute('outputFile', $logNode->getAttribute('target')); + $this->migrateAttributes($logNode, $crap4j, ['threshold']); + return $crap4j; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration; + +use DOMElement; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class CoverageHtmlToReport extends \PHPUnit\TextUI\XmlConfiguration\LogToReportMigration +{ + protected function forType(): string + { + return 'coverage-html'; + } + protected function toReportFormat(DOMElement $logNode): DOMElement + { + $html = $logNode->ownerDocument->createElement('html'); + $html->setAttribute('outputDirectory', $logNode->getAttribute('target')); + $this->migrateAttributes($logNode, $html, ['lowUpperBound', 'highLowerBound']); + return $html; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration; + +use DOMElement; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class CoveragePhpToReport extends \PHPUnit\TextUI\XmlConfiguration\LogToReportMigration +{ + protected function forType(): string + { + return 'coverage-php'; + } + protected function toReportFormat(DOMElement $logNode): DOMElement + { + $php = $logNode->ownerDocument->createElement('php'); + $php->setAttribute('outputFile', $logNode->getAttribute('target')); + return $php; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration; + +use DOMElement; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class CoverageTextToReport extends \PHPUnit\TextUI\XmlConfiguration\LogToReportMigration +{ + protected function forType(): string + { + return 'coverage-text'; + } + protected function toReportFormat(DOMElement $logNode): DOMElement + { + $text = $logNode->ownerDocument->createElement('text'); + $text->setAttribute('outputFile', $logNode->getAttribute('target')); + $this->migrateAttributes($logNode, $text, ['showUncoveredFiles', 'showOnlySummary']); + return $text; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration; + +use DOMElement; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class CoverageXmlToReport extends \PHPUnit\TextUI\XmlConfiguration\LogToReportMigration +{ + protected function forType(): string + { + return 'coverage-xml'; + } + protected function toReportFormat(DOMElement $logNode): DOMElement + { + $xml = $logNode->ownerDocument->createElement('xml'); + $xml->setAttribute('outputDirectory', $logNode->getAttribute('target')); + return $xml; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration; + +use function assert; +use DOMDocument; +use DOMElement; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class IntroduceCacheDirectoryAttribute implements \PHPUnit\TextUI\XmlConfiguration\Migration +{ + public function migrate(DOMDocument $document): void + { + $root = $document->documentElement; + assert($root instanceof DOMElement); + if ($root->hasAttribute('cacheDirectory')) { + return; + } + $root->setAttribute('cacheDirectory', '.phpunit.cache'); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration; + +use DOMDocument; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class IntroduceCoverageElement implements \PHPUnit\TextUI\XmlConfiguration\Migration +{ + public function migrate(DOMDocument $document): void + { + $coverage = $document->createElement('coverage'); + $document->documentElement->insertBefore($coverage, $document->documentElement->firstChild); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration; + +use function sprintf; +use DOMDocument; +use DOMElement; +use DOMXPath; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +abstract class LogToReportMigration implements \PHPUnit\TextUI\XmlConfiguration\Migration +{ + /** + * @throws MigrationException + */ + public function migrate(DOMDocument $document): void + { + $coverage = $document->getElementsByTagName('coverage')->item(0); + if (!$coverage instanceof DOMElement) { + throw new \PHPUnit\TextUI\XmlConfiguration\MigrationException('Unexpected state - No coverage element'); + } + $logNode = $this->findLogNode($document); + if ($logNode === null) { + return; + } + $reportChild = $this->toReportFormat($logNode); + $report = $coverage->getElementsByTagName('report')->item(0); + if ($report === null) { + $report = $coverage->appendChild($document->createElement('report')); + } + $report->appendChild($reportChild); + $logNode->parentNode->removeChild($logNode); + } + protected function migrateAttributes(DOMElement $src, DOMElement $dest, array $attributes): void + { + foreach ($attributes as $attr) { + if (!$src->hasAttribute($attr)) { + continue; + } + $dest->setAttribute($attr, $src->getAttribute($attr)); + $src->removeAttribute($attr); + } + } + abstract protected function forType(): string; + abstract protected function toReportFormat(DOMElement $logNode): DOMElement; + private function findLogNode(DOMDocument $document): ?DOMElement + { + $logNode = (new DOMXPath($document))->query(sprintf('//logging/log[@type="%s"]', $this->forType()))->item(0); + if (!$logNode instanceof DOMElement) { + return null; + } + return $logNode; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration; + +use DOMDocument; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +interface Migration +{ + public function migrate(DOMDocument $document): void; +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration; + +use DOMDocument; +use DOMElement; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class MoveAttributesFromFilterWhitelistToCoverage implements \PHPUnit\TextUI\XmlConfiguration\Migration +{ + /** + * @throws MigrationException + */ + public function migrate(DOMDocument $document): void + { + $whitelist = $document->getElementsByTagName('whitelist')->item(0); + if (!$whitelist) { + return; + } + $coverage = $document->getElementsByTagName('coverage')->item(0); + if (!$coverage instanceof DOMElement) { + throw new \PHPUnit\TextUI\XmlConfiguration\MigrationException('Unexpected state - No coverage element'); + } + $map = ['addUncoveredFilesFromWhitelist' => 'includeUncoveredFiles', 'processUncoveredFilesFromWhitelist' => 'processUncoveredFiles']; + foreach ($map as $old => $new) { + if (!$whitelist->hasAttribute($old)) { + continue; + } + $coverage->setAttribute($new, $whitelist->getAttribute($old)); + $whitelist->removeAttribute($old); + } + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration; + +use function assert; +use DOMDocument; +use DOMElement; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class MoveAttributesFromRootToCoverage implements \PHPUnit\TextUI\XmlConfiguration\Migration +{ + /** + * @throws MigrationException + */ + public function migrate(DOMDocument $document): void + { + $map = ['disableCodeCoverageIgnore' => 'disableCodeCoverageIgnore', 'ignoreDeprecatedCodeUnitsFromCodeCoverage' => 'ignoreDeprecatedCodeUnits']; + $root = $document->documentElement; + assert($root instanceof DOMElement); + $coverage = $document->getElementsByTagName('coverage')->item(0); + if (!$coverage instanceof DOMElement) { + throw new \PHPUnit\TextUI\XmlConfiguration\MigrationException('Unexpected state - No coverage element'); + } + foreach ($map as $old => $new) { + if (!$root->hasAttribute($old)) { + continue; + } + $coverage->setAttribute($new, $root->getAttribute($old)); + $root->removeAttribute($old); + } + } +} +. -All rights reserved. +declare (strict_types=1); +/* + * This file is part of PHPUnit. + * + * (c) Sebastian Bergmann + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration; -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the - distribution. - - * Neither the name of Sebastian Bergmann nor the names of his - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN -ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. +use function assert; +use DOMDocument; +use DOMElement; +use DOMXPath; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class MoveCoverageDirectoriesToSource implements \PHPUnit\TextUI\XmlConfiguration\Migration +{ + /** + * @throws MigrationException + */ + public function migrate(DOMDocument $document): void + { + $source = $document->getElementsByTagName('source')->item(0); + if ($source !== null) { + return; + } + $coverage = $document->getElementsByTagName('coverage')->item(0); + if ($coverage === null) { + return; + } + $root = $document->documentElement; + assert($root instanceof DOMElement); + $source = $document->createElement('source'); + $root->appendChild($source); + $xpath = new DOMXPath($document); + foreach (['include', 'exclude'] as $element) { + foreach (\PHPUnit\TextUI\XmlConfiguration\SnapshotNodeList::fromNodeList($xpath->query('//coverage/' . $element)) as $node) { + $source->appendChild($node); + } + } + if ($coverage->childElementCount !== 0) { + return; + } + assert($coverage->parentNode !== null); + $coverage->parentNode->removeChild($coverage); + } +} * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnitPHAR\SebastianBergmann\CliParser; +namespace PHPUnit\TextUI\XmlConfiguration; -use function array_map; -use function array_merge; -use function array_shift; -use function array_slice; use function assert; -use function count; -use function current; -use function explode; -use function is_array; -use function is_int; -use function is_string; -use function key; -use function next; -use function preg_replace; -use function reset; -use function sort; -use function strlen; -use function strpos; -use function strstr; -use function substr; -final class Parser +use function in_array; +use DOMDocument; +use DOMElement; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class MoveWhitelistExcludesToCoverage implements \PHPUnit\TextUI\XmlConfiguration\Migration { /** - * @psalm-param list $argv - * @psalm-param list $longOptions - * - * @throws AmbiguousOptionException - * @throws RequiredOptionArgumentMissingException - * @throws OptionDoesNotAllowArgumentException - * @throws UnknownOptionException + * @throws MigrationException */ - public function parse(array $argv, string $shortOptions, ?array $longOptions = null) : array + public function migrate(DOMDocument $document): void { - if (empty($argv)) { - return [[], []]; + $whitelist = $document->getElementsByTagName('whitelist')->item(0); + if ($whitelist === null) { + return; } - $options = []; - $nonOptions = []; - if ($longOptions) { - sort($longOptions); + $excludeNodes = \PHPUnit\TextUI\XmlConfiguration\SnapshotNodeList::fromNodeList($whitelist->getElementsByTagName('exclude')); + if ($excludeNodes->count() === 0) { + return; } - if (isset($argv[0][0]) && $argv[0][0] !== '-') { - array_shift($argv); + $coverage = $document->getElementsByTagName('coverage')->item(0); + if (!$coverage instanceof DOMElement) { + throw new \PHPUnit\TextUI\XmlConfiguration\MigrationException('Unexpected state - No coverage element'); } - reset($argv); - $argv = array_map('trim', $argv); - while (\false !== ($arg = current($argv))) { - $i = key($argv); - assert(is_int($i)); - next($argv); - if ($arg === '') { - continue; + $targetExclude = $coverage->getElementsByTagName('exclude')->item(0); + if ($targetExclude === null) { + $targetExclude = $coverage->appendChild($document->createElement('exclude')); + } + foreach ($excludeNodes as $excludeNode) { + assert($excludeNode instanceof DOMElement); + foreach (\PHPUnit\TextUI\XmlConfiguration\SnapshotNodeList::fromNodeList($excludeNode->childNodes) as $child) { + if (!$child instanceof DOMElement || !in_array($child->nodeName, ['directory', 'file'], \true)) { + continue; + } + $targetExclude->appendChild($child); } - if ($arg === '--') { - $nonOptions = array_merge($nonOptions, array_slice($argv, $i + 1)); - break; + if ($excludeNode->getElementsByTagName('*')->count() !== 0) { + throw new \PHPUnit\TextUI\XmlConfiguration\MigrationException('Dangling child elements in exclude found.'); } - if ($arg[0] !== '-' || strlen($arg) > 1 && $arg[1] === '-' && !$longOptions) { - $nonOptions[] = $arg; + $whitelist->removeChild($excludeNode); + } + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration; + +use DOMDocument; +use DOMElement; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class MoveWhitelistIncludesToCoverage implements \PHPUnit\TextUI\XmlConfiguration\Migration +{ + /** + * @throws MigrationException + */ + public function migrate(DOMDocument $document): void + { + $whitelist = $document->getElementsByTagName('whitelist')->item(0); + if ($whitelist === null) { + return; + } + $coverage = $document->getElementsByTagName('coverage')->item(0); + if (!$coverage instanceof DOMElement) { + throw new \PHPUnit\TextUI\XmlConfiguration\MigrationException('Unexpected state - No coverage element'); + } + $include = $document->createElement('include'); + $coverage->appendChild($include); + foreach (\PHPUnit\TextUI\XmlConfiguration\SnapshotNodeList::fromNodeList($whitelist->childNodes) as $child) { + if (!$child instanceof DOMElement) { continue; } - if (strlen($arg) > 1 && $arg[1] === '-' && is_array($longOptions)) { - $this->parseLongOption(substr($arg, 2), $longOptions, $options, $argv); - } else { - $this->parseShortOption(substr($arg, 1), $shortOptions, $options, $argv); + if (!($child->nodeName === 'directory' || $child->nodeName === 'file')) { + continue; } + $include->appendChild($child); + } + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration; + +use function assert; +use DOMDocument; +use DOMElement; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class RemoveBeStrictAboutResourceUsageDuringSmallTestsAttribute implements \PHPUnit\TextUI\XmlConfiguration\Migration +{ + public function migrate(DOMDocument $document): void + { + $root = $document->documentElement; + assert($root instanceof DOMElement); + if ($root->hasAttribute('beStrictAboutResourceUsageDuringSmallTests')) { + $root->removeAttribute('beStrictAboutResourceUsageDuringSmallTests'); + } + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration; + +use function assert; +use DOMDocument; +use DOMElement; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class RemoveBeStrictAboutTodoAnnotatedTestsAttribute implements \PHPUnit\TextUI\XmlConfiguration\Migration +{ + public function migrate(DOMDocument $document): void + { + $root = $document->documentElement; + assert($root instanceof DOMElement); + if ($root->hasAttribute('beStrictAboutTodoAnnotatedTests')) { + $root->removeAttribute('beStrictAboutTodoAnnotatedTests'); + } + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration; + +use function assert; +use DOMDocument; +use DOMElement; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class RemoveCacheResultFileAttribute implements \PHPUnit\TextUI\XmlConfiguration\Migration +{ + public function migrate(DOMDocument $document): void + { + $root = $document->documentElement; + assert($root instanceof DOMElement); + if ($root->hasAttribute('cacheResultFile')) { + $root->removeAttribute('cacheResultFile'); + } + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration; + +use function assert; +use DOMDocument; +use DOMElement; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class RemoveCacheTokensAttribute implements \PHPUnit\TextUI\XmlConfiguration\Migration +{ + public function migrate(DOMDocument $document): void + { + $root = $document->documentElement; + assert($root instanceof DOMElement); + if ($root->hasAttribute('cacheTokens')) { + $root->removeAttribute('cacheTokens'); + } + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration; + +use function assert; +use DOMDocument; +use DOMElement; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class RemoveConversionToExceptionsAttributes implements \PHPUnit\TextUI\XmlConfiguration\Migration +{ + public function migrate(DOMDocument $document): void + { + $root = $document->documentElement; + assert($root instanceof DOMElement); + if ($root->hasAttribute('convertDeprecationsToExceptions')) { + $root->removeAttribute('convertDeprecationsToExceptions'); + } + if ($root->hasAttribute('convertErrorsToExceptions')) { + $root->removeAttribute('convertErrorsToExceptions'); + } + if ($root->hasAttribute('convertNoticesToExceptions')) { + $root->removeAttribute('convertNoticesToExceptions'); + } + if ($root->hasAttribute('convertWarningsToExceptions')) { + $root->removeAttribute('convertWarningsToExceptions'); + } + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration; + +use DOMDocument; +use DOMElement; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class RemoveCoverageElementCacheDirectoryAttribute implements \PHPUnit\TextUI\XmlConfiguration\Migration +{ + public function migrate(DOMDocument $document): void + { + $node = $document->getElementsByTagName('coverage')->item(0); + if (!$node instanceof DOMElement || $node->parentNode === null) { + return; + } + if ($node->hasAttribute('cacheDirectory')) { + $node->removeAttribute('cacheDirectory'); + } + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration; + +use DOMDocument; +use DOMElement; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class RemoveCoverageElementProcessUncoveredFilesAttribute implements \PHPUnit\TextUI\XmlConfiguration\Migration +{ + public function migrate(DOMDocument $document): void + { + $node = $document->getElementsByTagName('coverage')->item(0); + if (!$node instanceof DOMElement || $node->parentNode === null) { + return; + } + if ($node->hasAttribute('processUncoveredFiles')) { + $node->removeAttribute('processUncoveredFiles'); } - return [$options, $nonOptions]; } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration; + +use function sprintf; +use DOMDocument; +use DOMElement; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class RemoveEmptyFilter implements \PHPUnit\TextUI\XmlConfiguration\Migration +{ /** - * @throws RequiredOptionArgumentMissingException + * @throws MigrationException */ - private function parseShortOption(string $arg, string $shortOptions, array &$opts, array &$args) : void + public function migrate(DOMDocument $document): void { - $argLength = strlen($arg); - for ($i = 0; $i < $argLength; $i++) { - $option = $arg[$i]; - $optionArgument = null; - if ($arg[$i] === ':' || ($spec = strstr($shortOptions, $option)) === \false) { - throw new UnknownOptionException('-' . $option); - } - assert(is_string($spec)); - if (strlen($spec) > 1 && $spec[1] === ':') { - if ($i + 1 < $argLength) { - $opts[] = [$option, substr($arg, $i + 1)]; - break; - } - if (!(strlen($spec) > 2 && $spec[2] === ':')) { - $optionArgument = current($args); - if (!$optionArgument) { - throw new RequiredOptionArgumentMissingException('-' . $option); - } - assert(is_string($optionArgument)); - next($args); - } - } - $opts[] = [$option, $optionArgument]; + $whitelist = $document->getElementsByTagName('whitelist')->item(0); + if ($whitelist instanceof DOMElement) { + $this->ensureEmpty($whitelist); + $whitelist->parentNode->removeChild($whitelist); + } + $filter = $document->getElementsByTagName('filter')->item(0); + if ($filter instanceof DOMElement) { + $this->ensureEmpty($filter); + $filter->parentNode->removeChild($filter); } } /** - * @psalm-param list $longOptions - * - * @throws AmbiguousOptionException - * @throws RequiredOptionArgumentMissingException - * @throws OptionDoesNotAllowArgumentException - * @throws UnknownOptionException + * @throws MigrationException */ - private function parseLongOption(string $arg, array $longOptions, array &$opts, array &$args) : void + private function ensureEmpty(DOMElement $element): void { - $count = count($longOptions); - $list = explode('=', $arg); - $option = $list[0]; - $optionArgument = null; - if (count($list) > 1) { - $optionArgument = $list[1]; + if ($element->attributes->length > 0) { + throw new \PHPUnit\TextUI\XmlConfiguration\MigrationException(sprintf('%s element has unexpected attributes', $element->nodeName)); } - $optionLength = strlen($option); - foreach ($longOptions as $i => $longOption) { - $opt_start = substr($longOption, 0, $optionLength); - if ($opt_start !== $option) { - continue; - } - $opt_rest = substr($longOption, $optionLength); - if ($opt_rest !== '' && $i + 1 < $count && $option[0] !== '=' && strpos($longOptions[$i + 1], $option) === 0) { - throw new AmbiguousOptionException('--' . $option); - } - if (substr($longOption, -1) === '=') { - /* @noinspection StrlenInEmptyStringCheckContextInspection */ - if (substr($longOption, -2) !== '==' && !strlen((string) $optionArgument)) { - if (\false === ($optionArgument = current($args))) { - throw new RequiredOptionArgumentMissingException('--' . $option); - } - next($args); - } - } elseif ($optionArgument) { - throw new OptionDoesNotAllowArgumentException('--' . $option); - } - $fullOption = '--' . preg_replace('/={1,2}$/', '', $longOption); - $opts[] = [$fullOption, $optionArgument]; + if ($element->getElementsByTagName('*')->length > 0) { + throw new \PHPUnit\TextUI\XmlConfiguration\MigrationException(sprintf('%s element has unexpected children', $element->nodeName)); + } + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration; + +use DOMDocument; +use DOMElement; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class RemoveListeners implements \PHPUnit\TextUI\XmlConfiguration\Migration +{ + public function migrate(DOMDocument $document): void + { + $node = $document->getElementsByTagName('listeners')->item(0); + if (!$node instanceof DOMElement || $node->parentNode === null) { return; } - throw new UnknownOptionException('--' . $option); + $node->parentNode->removeChild($node); } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnitPHAR\SebastianBergmann\CliParser; +namespace PHPUnit\TextUI\XmlConfiguration; -use function sprintf; -use RuntimeException; -final class AmbiguousOptionException extends RuntimeException implements Exception +use function assert; +use DOMDocument; +use DOMElement; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class RemoveLogTypes implements \PHPUnit\TextUI\XmlConfiguration\Migration { - public function __construct(string $option) + public function migrate(DOMDocument $document): void { - parent::__construct(sprintf('Option "%s" is ambiguous', $option)); + $logging = $document->getElementsByTagName('logging')->item(0); + if (!$logging instanceof DOMElement) { + return; + } + foreach (\PHPUnit\TextUI\XmlConfiguration\SnapshotNodeList::fromNodeList($logging->getElementsByTagName('log')) as $logNode) { + assert($logNode instanceof DOMElement); + switch ($logNode->getAttribute('type')) { + case 'json': + case 'tap': + $logging->removeChild($logNode); + } + } } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnitPHAR\SebastianBergmann\CliParser; +namespace PHPUnit\TextUI\XmlConfiguration; -use Throwable; -interface Exception extends Throwable +use DOMDocument; +use DOMElement; +use DOMXPath; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class RemoveLoggingElements implements \PHPUnit\TextUI\XmlConfiguration\Migration { + public function migrate(DOMDocument $document): void + { + $this->removeTestDoxElement($document); + $this->removeTextElement($document); + } + private function removeTestDoxElement(DOMDocument $document): void + { + $node = (new DOMXPath($document))->query('logging/testdoxXml')->item(0); + if (!$node instanceof DOMElement || $node->parentNode === null) { + return; + } + $node->parentNode->removeChild($node); + } + private function removeTextElement(DOMDocument $document): void + { + $node = (new DOMXPath($document))->query('logging/text')->item(0); + if (!$node instanceof DOMElement || $node->parentNode === null) { + return; + } + $node->parentNode->removeChild($node); + } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnitPHAR\SebastianBergmann\CliParser; +namespace PHPUnit\TextUI\XmlConfiguration; -use function sprintf; -use RuntimeException; -final class OptionDoesNotAllowArgumentException extends RuntimeException implements Exception +use function assert; +use DOMDocument; +use DOMElement; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class RemoveNoInteractionAttribute implements \PHPUnit\TextUI\XmlConfiguration\Migration { - public function __construct(string $option) + public function migrate(DOMDocument $document): void { - parent::__construct(sprintf('Option "%s" does not allow an argument', $option)); + $root = $document->documentElement; + assert($root instanceof DOMElement); + if ($root->hasAttribute('noInteraction')) { + $root->removeAttribute('noInteraction'); + } } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnitPHAR\SebastianBergmann\CliParser; +namespace PHPUnit\TextUI\XmlConfiguration; -use function sprintf; -use RuntimeException; -final class RequiredOptionArgumentMissingException extends RuntimeException implements Exception +use function assert; +use DOMDocument; +use DOMElement; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class RemovePrinterAttributes implements \PHPUnit\TextUI\XmlConfiguration\Migration { - public function __construct(string $option) + public function migrate(DOMDocument $document): void { - parent::__construct(sprintf('Required argument for option "%s" is missing', $option)); + $root = $document->documentElement; + assert($root instanceof DOMElement); + if ($root->hasAttribute('printerClass')) { + $root->removeAttribute('printerClass'); + } + if ($root->hasAttribute('printerFile')) { + $root->removeAttribute('printerFile'); + } } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnitPHAR\SebastianBergmann\CliParser; +namespace PHPUnit\TextUI\XmlConfiguration; -use function sprintf; -use RuntimeException; -final class UnknownOptionException extends RuntimeException implements Exception +use DOMDocument; +use DOMElement; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class RemoveTestDoxGroupsElement implements \PHPUnit\TextUI\XmlConfiguration\Migration { - public function __construct(string $option) + public function migrate(DOMDocument $document): void { - parent::__construct(sprintf('Unknown option "%s"', $option)); + $node = $document->getElementsByTagName('testdoxGroups')->item(0); + if (!$node instanceof DOMElement || $node->parentNode === null) { + return; + } + $node->parentNode->removeChild($node); } } -code-unit-reverse-lookup - -Copyright (c) 2016-2020, Sebastian Bergmann . -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the - distribution. - - * Neither the name of Sebastian Bergmann nor the names of his - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN -ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnitPHAR\SebastianBergmann\CodeUnitReverseLookup; +namespace PHPUnit\TextUI\XmlConfiguration; -use function array_merge; use function assert; -use function get_declared_classes; -use function get_declared_traits; -use function get_defined_functions; -use function is_array; -use function range; -use ReflectionClass; -use ReflectionFunction; -use ReflectionFunctionAbstract; -use ReflectionMethod; +use DOMDocument; +use DOMElement; /** - * @since Class available since Release 1.0.0 + * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -class Wizard +final class RemoveTestSuiteLoaderAttributes implements \PHPUnit\TextUI\XmlConfiguration\Migration { - /** - * @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]; - } - return $filename . ':' . $lineNumber; - } - private function updateLookupTable() : void - { - $this->processClassesAndTraits(); - $this->processFunctions(); - } - private function processClassesAndTraits() : void - { - $classes = get_declared_classes(); - $traits = get_declared_traits(); - assert(is_array($classes)); - assert(is_array($traits)); - foreach (array_merge($classes, $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() : void - { - foreach (get_defined_functions()['user'] as $function) { - if (isset($this->processedFunctions[$function])) { - continue; - } - $this->processFunctionOrMethod(new ReflectionFunction($function)); - $this->processedFunctions[$function] = \true; - } - } - private function processFunctionOrMethod(ReflectionFunctionAbstract $functionOrMethod) : void + public function migrate(DOMDocument $document): void { - 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()] = []; + $root = $document->documentElement; + assert($root instanceof DOMElement); + if ($root->hasAttribute('testSuiteLoaderClass')) { + $root->removeAttribute('testSuiteLoaderClass'); } - foreach (range($functionOrMethod->getStartLine(), $functionOrMethod->getEndLine()) as $line) { - $this->lookupTable[$functionOrMethod->getFileName()][$line] = $name; + if ($root->hasAttribute('testSuiteLoaderFile')) { + $root->removeAttribute('testSuiteLoaderFile'); } } } @@ -97602,2087 +89537,1669 @@ class Wizard declare (strict_types=1); /* - * This file is part of sebastian/code-unit. + * This file is part of PHPUnit. * * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnitPHAR\SebastianBergmann\CodeUnit; +namespace PHPUnit\TextUI\XmlConfiguration; +use function assert; +use DOMDocument; +use DOMElement; /** - * @psalm-immutable + * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class ClassMethodUnit extends CodeUnit +final class RemoveVerboseAttribute implements \PHPUnit\TextUI\XmlConfiguration\Migration { - /** - * @psalm-assert-if-true ClassMethodUnit $this - */ - public function isClassMethod() : bool + public function migrate(DOMDocument $document): void { - return \true; + $root = $document->documentElement; + assert($root instanceof DOMElement); + if ($root->hasAttribute('verbose')) { + $root->removeAttribute('verbose'); + } } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnitPHAR\SebastianBergmann\CodeUnit; +namespace PHPUnit\TextUI\XmlConfiguration; +use function assert; +use DOMDocument; +use DOMElement; /** - * @psalm-immutable + * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class ClassUnit extends CodeUnit +final class RenameBackupStaticAttributesAttribute implements \PHPUnit\TextUI\XmlConfiguration\Migration { - /** - * @psalm-assert-if-true ClassUnit $this - */ - public function isClass() : bool + public function migrate(DOMDocument $document): void { - return \true; + $root = $document->documentElement; + assert($root instanceof DOMElement); + if ($root->hasAttribute('backupStaticProperties')) { + return; + } + if (!$root->hasAttribute('backupStaticAttributes')) { + return; + } + $root->setAttribute('backupStaticProperties', $root->getAttribute('backupStaticAttributes')); + $root->removeAttribute('backupStaticAttributes'); } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnitPHAR\SebastianBergmann\CodeUnit; +namespace PHPUnit\TextUI\XmlConfiguration; -use function range; -use function sprintf; -use ReflectionClass; -use ReflectionFunction; -use ReflectionMethod; +use function assert; +use DOMDocument; +use DOMElement; /** - * @psalm-immutable + * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -abstract class CodeUnit +final class RenameBeStrictAboutCoversAnnotationAttribute implements \PHPUnit\TextUI\XmlConfiguration\Migration { - /** - * @var string - */ - private $name; - /** - * @var string - */ - private $sourceFileName; - /** - * @var array - * @psalm-var list - */ - private $sourceLines; - /** - * @psalm-param class-string $className - * - * @throws InvalidCodeUnitException - * @throws ReflectionException - */ - public static function forClass(string $className) : ClassUnit - { - self::ensureUserDefinedClass($className); - $reflector = self::reflectorForClass($className); - return new ClassUnit($className, $reflector->getFileName(), range($reflector->getStartLine(), $reflector->getEndLine())); - } - /** - * @psalm-param class-string $className - * - * @throws InvalidCodeUnitException - * @throws ReflectionException - */ - public static function forClassMethod(string $className, string $methodName) : ClassMethodUnit - { - self::ensureUserDefinedClass($className); - $reflector = self::reflectorForClassMethod($className, $methodName); - return new ClassMethodUnit($className . '::' . $methodName, $reflector->getFileName(), range($reflector->getStartLine(), $reflector->getEndLine())); - } - /** - * @psalm-param class-string $interfaceName - * - * @throws InvalidCodeUnitException - * @throws ReflectionException - */ - public static function forInterface(string $interfaceName) : InterfaceUnit - { - self::ensureUserDefinedInterface($interfaceName); - $reflector = self::reflectorForClass($interfaceName); - return new InterfaceUnit($interfaceName, $reflector->getFileName(), range($reflector->getStartLine(), $reflector->getEndLine())); - } - /** - * @psalm-param class-string $interfaceName - * - * @throws InvalidCodeUnitException - * @throws ReflectionException - */ - public static function forInterfaceMethod(string $interfaceName, string $methodName) : InterfaceMethodUnit - { - self::ensureUserDefinedInterface($interfaceName); - $reflector = self::reflectorForClassMethod($interfaceName, $methodName); - return new InterfaceMethodUnit($interfaceName . '::' . $methodName, $reflector->getFileName(), range($reflector->getStartLine(), $reflector->getEndLine())); - } - /** - * @psalm-param class-string $traitName - * - * @throws InvalidCodeUnitException - * @throws ReflectionException - */ - public static function forTrait(string $traitName) : TraitUnit - { - self::ensureUserDefinedTrait($traitName); - $reflector = self::reflectorForClass($traitName); - return new TraitUnit($traitName, $reflector->getFileName(), range($reflector->getStartLine(), $reflector->getEndLine())); - } - /** - * @psalm-param class-string $traitName - * - * @throws InvalidCodeUnitException - * @throws ReflectionException - */ - public static function forTraitMethod(string $traitName, string $methodName) : TraitMethodUnit - { - self::ensureUserDefinedTrait($traitName); - $reflector = self::reflectorForClassMethod($traitName, $methodName); - return new TraitMethodUnit($traitName . '::' . $methodName, $reflector->getFileName(), range($reflector->getStartLine(), $reflector->getEndLine())); - } - /** - * @psalm-param callable-string $functionName - * - * @throws InvalidCodeUnitException - * @throws ReflectionException - */ - public static function forFunction(string $functionName) : FunctionUnit - { - $reflector = self::reflectorForFunction($functionName); - if (!$reflector->isUserDefined()) { - throw new InvalidCodeUnitException(sprintf('"%s" is not a user-defined function', $functionName)); - } - return new FunctionUnit($functionName, $reflector->getFileName(), range($reflector->getStartLine(), $reflector->getEndLine())); - } - /** - * @psalm-param list $sourceLines - */ - private function __construct(string $name, string $sourceFileName, array $sourceLines) - { - $this->name = $name; - $this->sourceFileName = $sourceFileName; - $this->sourceLines = $sourceLines; - } - public function name() : string - { - return $this->name; - } - public function sourceFileName() : string - { - return $this->sourceFileName; - } - /** - * @psalm-return list - */ - public function sourceLines() : array - { - return $this->sourceLines; - } - public function isClass() : bool - { - return \false; - } - public function isClassMethod() : bool - { - return \false; - } - public function isInterface() : bool - { - return \false; - } - public function isInterfaceMethod() : bool - { - return \false; - } - public function isTrait() : bool - { - return \false; - } - public function isTraitMethod() : bool - { - return \false; - } - public function isFunction() : bool - { - return \false; - } - /** - * @psalm-param class-string $className - * - * @throws InvalidCodeUnitException - */ - private static function ensureUserDefinedClass(string $className) : void - { - try { - $reflector = new ReflectionClass($className); - if ($reflector->isInterface()) { - throw new InvalidCodeUnitException(sprintf('"%s" is an interface and not a class', $className)); - } - if ($reflector->isTrait()) { - throw new InvalidCodeUnitException(sprintf('"%s" is a trait and not a class', $className)); - } - if (!$reflector->isUserDefined()) { - throw new InvalidCodeUnitException(sprintf('"%s" is not a user-defined class', $className)); - } - // @codeCoverageIgnoreStart - } catch (\ReflectionException $e) { - throw new ReflectionException($e->getMessage(), (int) $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd - } - /** - * @psalm-param class-string $interfaceName - * - * @throws InvalidCodeUnitException - */ - private static function ensureUserDefinedInterface(string $interfaceName) : void - { - try { - $reflector = new ReflectionClass($interfaceName); - if (!$reflector->isInterface()) { - throw new InvalidCodeUnitException(sprintf('"%s" is not an interface', $interfaceName)); - } - if (!$reflector->isUserDefined()) { - throw new InvalidCodeUnitException(sprintf('"%s" is not a user-defined interface', $interfaceName)); - } - // @codeCoverageIgnoreStart - } catch (\ReflectionException $e) { - throw new ReflectionException($e->getMessage(), (int) $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd - } - /** - * @psalm-param class-string $traitName - * - * @throws InvalidCodeUnitException - */ - private static function ensureUserDefinedTrait(string $traitName) : void - { - try { - $reflector = new ReflectionClass($traitName); - if (!$reflector->isTrait()) { - throw new InvalidCodeUnitException(sprintf('"%s" is not a trait', $traitName)); - } - // @codeCoverageIgnoreStart - if (!$reflector->isUserDefined()) { - throw new InvalidCodeUnitException(sprintf('"%s" is not a user-defined trait', $traitName)); - } - } catch (\ReflectionException $e) { - throw new ReflectionException($e->getMessage(), (int) $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd - } - /** - * @psalm-param class-string $className - * - * @throws ReflectionException - */ - private static function reflectorForClass(string $className) : ReflectionClass + public function migrate(DOMDocument $document): void { - try { - return new ReflectionClass($className); - // @codeCoverageIgnoreStart - } catch (\ReflectionException $e) { - throw new ReflectionException($e->getMessage(), (int) $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd - } - /** - * @psalm-param class-string $className - * - * @throws ReflectionException - */ - private static function reflectorForClassMethod(string $className, string $methodName) : ReflectionMethod - { - try { - return new ReflectionMethod($className, $methodName); - // @codeCoverageIgnoreStart - } catch (\ReflectionException $e) { - throw new ReflectionException($e->getMessage(), (int) $e->getCode(), $e); + $root = $document->documentElement; + assert($root instanceof DOMElement); + if ($root->hasAttribute('beStrictAboutCoverageMetadata')) { + return; } - // @codeCoverageIgnoreEnd - } - /** - * @psalm-param callable-string $functionName - * - * @throws ReflectionException - */ - private static function reflectorForFunction(string $functionName) : ReflectionFunction - { - try { - return new ReflectionFunction($functionName); - // @codeCoverageIgnoreStart - } catch (\ReflectionException $e) { - throw new ReflectionException($e->getMessage(), (int) $e->getCode(), $e); + if (!$root->hasAttribute('beStrictAboutCoversAnnotation')) { + return; } - // @codeCoverageIgnoreEnd + $root->setAttribute('beStrictAboutCoverageMetadata', $root->getAttribute('beStrictAboutCoversAnnotation')); + $root->removeAttribute('beStrictAboutCoversAnnotation'); } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnitPHAR\SebastianBergmann\CodeUnit; +namespace PHPUnit\TextUI\XmlConfiguration; -use function array_merge; -use function count; -use Countable; -use IteratorAggregate; -final class CodeUnitCollection implements Countable, IteratorAggregate +use function assert; +use DOMDocument; +use DOMElement; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class RenameForceCoversAnnotationAttribute implements \PHPUnit\TextUI\XmlConfiguration\Migration { - /** - * @psalm-var list - */ - private $codeUnits = []; - /** - * @psalm-param list $items - */ - public static function fromArray(array $items) : self + public function migrate(DOMDocument $document): void { - $collection = new self(); - foreach ($items as $item) { - $collection->add($item); + $root = $document->documentElement; + assert($root instanceof DOMElement); + if ($root->hasAttribute('requireCoverageMetadata')) { + return; } - return $collection; - } - public static function fromList(CodeUnit ...$items) : self - { - return self::fromArray($items); - } - private function __construct() - { - } - /** - * @psalm-return list - */ - public function asArray() : array - { - return $this->codeUnits; - } - public function getIterator() : CodeUnitCollectionIterator - { - return new CodeUnitCollectionIterator($this); - } - public function count() : int - { - return count($this->codeUnits); - } - public function isEmpty() : bool - { - return empty($this->codeUnits); - } - public function mergeWith(self $other) : self - { - return self::fromArray(array_merge($this->asArray(), $other->asArray())); - } - private function add(CodeUnit $item) : void - { - $this->codeUnits[] = $item; + if (!$root->hasAttribute('forceCoversAnnotation')) { + return; + } + $root->setAttribute('requireCoverageMetadata', $root->getAttribute('forceCoversAnnotation')); + $root->removeAttribute('forceCoversAnnotation'); } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnitPHAR\SebastianBergmann\CodeUnit; +namespace PHPUnit\TextUI\XmlConfiguration; -use Iterator; -final class CodeUnitCollectionIterator implements Iterator +use function assert; +use DOMDocument; +use DOMElement; +use PHPUnit\Runner\Version; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class UpdateSchemaLocation implements \PHPUnit\TextUI\XmlConfiguration\Migration { - /** - * @psalm-var list - */ - private $codeUnits; - /** - * @var int - */ - private $position = 0; - public function __construct(CodeUnitCollection $collection) - { - $this->codeUnits = $collection->asArray(); - } - public function rewind() : void - { - $this->position = 0; - } - public function valid() : bool - { - return isset($this->codeUnits[$this->position]); - } - public function key() : int - { - return $this->position; - } - public function current() : CodeUnit - { - return $this->codeUnits[$this->position]; - } - public function next() : void + public function migrate(DOMDocument $document): void { - $this->position++; + $root = $document->documentElement; + assert($root instanceof DOMElement); + $root->setAttributeNS('http://www.w3.org/2001/XMLSchema-instance', 'xsi:noNamespaceSchemaLocation', 'https://schema.phpunit.de/' . Version::series() . '/phpunit.xsd'); } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnitPHAR\SebastianBergmann\CodeUnit; +namespace PHPUnit\TextUI\XmlConfiguration; +use PHPUnit\Runner\Version; +use PHPUnit\Util\Xml\Loader as XmlLoader; +use PHPUnit\Util\Xml\XmlException; /** - * @psalm-immutable + * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class FunctionUnit extends CodeUnit +final class Migrator { /** - * @psalm-assert-if-true FunctionUnit $this + * @throws Exception + * @throws MigrationBuilderException + * @throws MigrationException + * @throws XmlException */ - public function isFunction() : bool + public function migrate(string $filename): string { - return \true; + $origin = (new \PHPUnit\TextUI\XmlConfiguration\SchemaDetector())->detect($filename); + if (!$origin->detected()) { + throw new \PHPUnit\TextUI\XmlConfiguration\Exception('The file does not validate against any know schema'); + } + if ($origin->version() === Version::series()) { + throw new \PHPUnit\TextUI\XmlConfiguration\Exception('The file does not need to be migrated'); + } + $configurationDocument = (new XmlLoader())->loadFile($filename); + foreach ((new \PHPUnit\TextUI\XmlConfiguration\MigrationBuilder())->build($origin->version()) as $migration) { + $migration->migrate($configurationDocument); + } + $configurationDocument->formatOutput = \true; + $configurationDocument->preserveWhiteSpace = \false; + return $configurationDocument->saveXML(); } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnitPHAR\SebastianBergmann\CodeUnit; +namespace PHPUnit\TextUI\XmlConfiguration; +use function count; +use ArrayIterator; +use Countable; +use DOMNode; +use DOMNodeList; +use IteratorAggregate; /** - * @psalm-immutable + * @internal This class is not covered by the backward compatibility promise for PHPUnit + * + * @template-implements IteratorAggregate */ -final class InterfaceMethodUnit extends CodeUnit +final class SnapshotNodeList implements Countable, IteratorAggregate { /** - * @psalm-assert-if-true InterfaceMethod $this + * @psalm-var list */ - public function isInterfaceMethod() : bool + private array $nodes = []; + public static function fromNodeList(DOMNodeList $list): self { - return \true; + $snapshot = new self(); + foreach ($list as $node) { + $snapshot->nodes[] = $node; + } + return $snapshot; + } + public function count(): int + { + return count($this->nodes); + } + public function getIterator(): ArrayIterator + { + return new ArrayIterator($this->nodes); } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnitPHAR\SebastianBergmann\CodeUnit; +namespace PHPUnit\TextUI\XmlConfiguration; /** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + * * @psalm-immutable */ -final class InterfaceUnit extends CodeUnit +final class PHPUnit { - /** - * @psalm-assert-if-true InterfaceUnit $this - */ - public function isInterface() : bool + private readonly ?string $cacheDirectory; + private readonly bool $cacheResult; + private readonly ?string $cacheResultFile; + private readonly int|string $columns; + private readonly string $colors; + private readonly bool $stderr; + private readonly bool $displayDetailsOnIncompleteTests; + private readonly bool $displayDetailsOnSkippedTests; + private readonly bool $displayDetailsOnTestsThatTriggerDeprecations; + private readonly bool $displayDetailsOnTestsThatTriggerErrors; + private readonly bool $displayDetailsOnTestsThatTriggerNotices; + private readonly bool $displayDetailsOnTestsThatTriggerWarnings; + private readonly bool $reverseDefectList; + private readonly bool $requireCoverageMetadata; + private readonly ?string $bootstrap; + private readonly bool $processIsolation; + private readonly bool $failOnDeprecation; + private readonly bool $failOnEmptyTestSuite; + private readonly bool $failOnIncomplete; + private readonly bool $failOnNotice; + private readonly bool $failOnRisky; + private readonly bool $failOnSkipped; + private readonly bool $failOnWarning; + private readonly bool $stopOnDefect; + private readonly bool $stopOnDeprecation; + private readonly bool $stopOnError; + private readonly bool $stopOnFailure; + private readonly bool $stopOnIncomplete; + private readonly bool $stopOnNotice; + private readonly bool $stopOnRisky; + private readonly bool $stopOnSkipped; + private readonly bool $stopOnWarning; + /** + * @psalm-var ?non-empty-string + */ + private readonly ?string $extensionsDirectory; + private readonly bool $beStrictAboutChangesToGlobalState; + private readonly bool $beStrictAboutOutputDuringTests; + private readonly bool $beStrictAboutTestsThatDoNotTestAnything; + private readonly bool $beStrictAboutCoverageMetadata; + private readonly bool $enforceTimeLimit; + private readonly int $defaultTimeLimit; + private readonly int $timeoutForSmallTests; + private readonly int $timeoutForMediumTests; + private readonly int $timeoutForLargeTests; + private readonly ?string $defaultTestSuite; + private readonly int $executionOrder; + private readonly bool $resolveDependencies; + private readonly bool $defectsFirst; + private readonly bool $backupGlobals; + private readonly bool $backupStaticProperties; + private readonly bool $registerMockObjectsFromTestArgumentsRecursively; + private readonly bool $testdoxPrinter; + private readonly bool $controlGarbageCollector; + private readonly int $numberOfTestsBeforeGarbageCollection; + /** + * @psalm-param ?non-empty-string $extensionsDirectory + */ + public function __construct(?string $cacheDirectory, bool $cacheResult, ?string $cacheResultFile, int|string $columns, string $colors, bool $stderr, bool $displayDetailsOnIncompleteTests, bool $displayDetailsOnSkippedTests, bool $displayDetailsOnTestsThatTriggerDeprecations, bool $displayDetailsOnTestsThatTriggerErrors, bool $displayDetailsOnTestsThatTriggerNotices, bool $displayDetailsOnTestsThatTriggerWarnings, bool $reverseDefectList, bool $requireCoverageMetadata, ?string $bootstrap, bool $processIsolation, bool $failOnDeprecation, bool $failOnEmptyTestSuite, bool $failOnIncomplete, bool $failOnNotice, bool $failOnRisky, bool $failOnSkipped, bool $failOnWarning, bool $stopOnDefect, bool $stopOnDeprecation, bool $stopOnError, bool $stopOnFailure, bool $stopOnIncomplete, bool $stopOnNotice, bool $stopOnRisky, bool $stopOnSkipped, bool $stopOnWarning, ?string $extensionsDirectory, bool $beStrictAboutChangesToGlobalState, bool $beStrictAboutOutputDuringTests, bool $beStrictAboutTestsThatDoNotTestAnything, bool $beStrictAboutCoverageMetadata, bool $enforceTimeLimit, int $defaultTimeLimit, int $timeoutForSmallTests, int $timeoutForMediumTests, int $timeoutForLargeTests, ?string $defaultTestSuite, int $executionOrder, bool $resolveDependencies, bool $defectsFirst, bool $backupGlobals, bool $backupStaticProperties, bool $registerMockObjectsFromTestArgumentsRecursively, bool $testdoxPrinter, bool $controlGarbageCollector, int $numberOfTestsBeforeGarbageCollection) { - return \true; + $this->cacheDirectory = $cacheDirectory; + $this->cacheResult = $cacheResult; + $this->cacheResultFile = $cacheResultFile; + $this->columns = $columns; + $this->colors = $colors; + $this->stderr = $stderr; + $this->displayDetailsOnIncompleteTests = $displayDetailsOnIncompleteTests; + $this->displayDetailsOnSkippedTests = $displayDetailsOnSkippedTests; + $this->displayDetailsOnTestsThatTriggerDeprecations = $displayDetailsOnTestsThatTriggerDeprecations; + $this->displayDetailsOnTestsThatTriggerErrors = $displayDetailsOnTestsThatTriggerErrors; + $this->displayDetailsOnTestsThatTriggerNotices = $displayDetailsOnTestsThatTriggerNotices; + $this->displayDetailsOnTestsThatTriggerWarnings = $displayDetailsOnTestsThatTriggerWarnings; + $this->reverseDefectList = $reverseDefectList; + $this->requireCoverageMetadata = $requireCoverageMetadata; + $this->bootstrap = $bootstrap; + $this->processIsolation = $processIsolation; + $this->failOnDeprecation = $failOnDeprecation; + $this->failOnEmptyTestSuite = $failOnEmptyTestSuite; + $this->failOnIncomplete = $failOnIncomplete; + $this->failOnNotice = $failOnNotice; + $this->failOnRisky = $failOnRisky; + $this->failOnSkipped = $failOnSkipped; + $this->failOnWarning = $failOnWarning; + $this->stopOnDefect = $stopOnDefect; + $this->stopOnDeprecation = $stopOnDeprecation; + $this->stopOnError = $stopOnError; + $this->stopOnFailure = $stopOnFailure; + $this->stopOnIncomplete = $stopOnIncomplete; + $this->stopOnNotice = $stopOnNotice; + $this->stopOnRisky = $stopOnRisky; + $this->stopOnSkipped = $stopOnSkipped; + $this->stopOnWarning = $stopOnWarning; + $this->extensionsDirectory = $extensionsDirectory; + $this->beStrictAboutChangesToGlobalState = $beStrictAboutChangesToGlobalState; + $this->beStrictAboutOutputDuringTests = $beStrictAboutOutputDuringTests; + $this->beStrictAboutTestsThatDoNotTestAnything = $beStrictAboutTestsThatDoNotTestAnything; + $this->beStrictAboutCoverageMetadata = $beStrictAboutCoverageMetadata; + $this->enforceTimeLimit = $enforceTimeLimit; + $this->defaultTimeLimit = $defaultTimeLimit; + $this->timeoutForSmallTests = $timeoutForSmallTests; + $this->timeoutForMediumTests = $timeoutForMediumTests; + $this->timeoutForLargeTests = $timeoutForLargeTests; + $this->defaultTestSuite = $defaultTestSuite; + $this->executionOrder = $executionOrder; + $this->resolveDependencies = $resolveDependencies; + $this->defectsFirst = $defectsFirst; + $this->backupGlobals = $backupGlobals; + $this->backupStaticProperties = $backupStaticProperties; + $this->registerMockObjectsFromTestArgumentsRecursively = $registerMockObjectsFromTestArgumentsRecursively; + $this->testdoxPrinter = $testdoxPrinter; + $this->controlGarbageCollector = $controlGarbageCollector; + $this->numberOfTestsBeforeGarbageCollection = $numberOfTestsBeforeGarbageCollection; } -} -sebastian/code-unit - -Copyright (c) 2020, Sebastian Bergmann . -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the - distribution. - - * Neither the name of Sebastian Bergmann nor the names of his - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN -ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnitPHAR\SebastianBergmann\CodeUnit; - -use function array_keys; -use function array_merge; -use function array_unique; -use function array_values; -use function class_exists; -use function explode; -use function function_exists; -use function interface_exists; -use function ksort; -use function method_exists; -use function sort; -use function sprintf; -use function str_replace; -use function strpos; -use function trait_exists; -use ReflectionClass; -use ReflectionFunction; -use ReflectionMethod; -final class Mapper -{ /** - * @psalm-return array> + * @psalm-assert-if-true !null $this->cacheDirectory */ - public function codeUnitsToSourceLines(CodeUnitCollection $codeUnits) : array + public function hasCacheDirectory(): bool { - $result = []; - foreach ($codeUnits as $codeUnit) { - $sourceFileName = $codeUnit->sourceFileName(); - if (!isset($result[$sourceFileName])) { - $result[$sourceFileName] = []; - } - $result[$sourceFileName] = array_merge($result[$sourceFileName], $codeUnit->sourceLines()); - } - foreach (array_keys($result) as $sourceFileName) { - $result[$sourceFileName] = array_values(array_unique($result[$sourceFileName])); - sort($result[$sourceFileName]); - } - ksort($result); - return $result; + return $this->cacheDirectory !== null; } /** - * @throws InvalidCodeUnitException - * @throws ReflectionException + * @throws Exception */ - public function stringToCodeUnits(string $unit) : CodeUnitCollection + public function cacheDirectory(): string { - if (strpos($unit, '::') !== \false) { - [$firstPart, $secondPart] = explode('::', $unit); - if (empty($firstPart) && $this->isUserDefinedFunction($secondPart)) { - return CodeUnitCollection::fromList(CodeUnit::forFunction($secondPart)); - } - if ($this->isUserDefinedClass($firstPart)) { - if ($secondPart === '') { - return $this->publicMethodsOfClass($firstPart); - } - if ($secondPart === '') { - return $this->protectedAndPrivateMethodsOfClass($firstPart); - } - if ($secondPart === '') { - return $this->protectedMethodsOfClass($firstPart); - } - if ($secondPart === '') { - return $this->publicAndPrivateMethodsOfClass($firstPart); - } - if ($secondPart === '') { - return $this->privateMethodsOfClass($firstPart); - } - if ($secondPart === '') { - return $this->publicAndProtectedMethodsOfClass($firstPart); - } - if ($this->isUserDefinedMethod($firstPart, $secondPart)) { - return CodeUnitCollection::fromList(CodeUnit::forClassMethod($firstPart, $secondPart)); - } - } - if ($this->isUserDefinedInterface($firstPart)) { - return CodeUnitCollection::fromList(CodeUnit::forInterfaceMethod($firstPart, $secondPart)); - } - if ($this->isUserDefinedTrait($firstPart)) { - return CodeUnitCollection::fromList(CodeUnit::forTraitMethod($firstPart, $secondPart)); - } - } else { - if ($this->isUserDefinedClass($unit)) { - $units = [CodeUnit::forClass($unit)]; - foreach ($this->reflectorForClass($unit)->getTraits() as $trait) { - if (!$trait->isUserDefined()) { - // @codeCoverageIgnoreStart - continue; - // @codeCoverageIgnoreEnd - } - $units[] = CodeUnit::forTrait($trait->getName()); - } - return CodeUnitCollection::fromArray($units); - } - if ($this->isUserDefinedInterface($unit)) { - return CodeUnitCollection::fromList(CodeUnit::forInterface($unit)); - } - if ($this->isUserDefinedTrait($unit)) { - return CodeUnitCollection::fromList(CodeUnit::forTrait($unit)); - } - if ($this->isUserDefinedFunction($unit)) { - return CodeUnitCollection::fromList(CodeUnit::forFunction($unit)); - } - $unit = str_replace('', '', $unit); - if ($this->isUserDefinedClass($unit)) { - return $this->classAndParentClassesAndTraits($unit); - } + if (!$this->hasCacheDirectory()) { + throw new \PHPUnit\TextUI\XmlConfiguration\Exception('Cache directory is not configured'); } - throw new InvalidCodeUnitException(sprintf('"%s" is not a valid code unit', $unit)); + return $this->cacheDirectory; } - /** - * @psalm-param class-string $className - * - * @throws ReflectionException - */ - private function publicMethodsOfClass(string $className) : CodeUnitCollection + public function cacheResult(): bool { - return $this->methodsOfClass($className, ReflectionMethod::IS_PUBLIC); + return $this->cacheResult; } /** - * @psalm-param class-string $className + * @psalm-assert-if-true !null $this->cacheResultFile * - * @throws ReflectionException + * @deprecated */ - private function publicAndProtectedMethodsOfClass(string $className) : CodeUnitCollection + public function hasCacheResultFile(): bool { - return $this->methodsOfClass($className, ReflectionMethod::IS_PUBLIC | ReflectionMethod::IS_PROTECTED); + return $this->cacheResultFile !== null; } /** - * @psalm-param class-string $className + * @throws Exception * - * @throws ReflectionException + * @deprecated */ - private function publicAndPrivateMethodsOfClass(string $className) : CodeUnitCollection + public function cacheResultFile(): string { - return $this->methodsOfClass($className, ReflectionMethod::IS_PUBLIC | ReflectionMethod::IS_PRIVATE); + if (!$this->hasCacheResultFile()) { + throw new \PHPUnit\TextUI\XmlConfiguration\Exception('Cache result file is not configured'); + } + return $this->cacheResultFile; } - /** - * @psalm-param class-string $className - * - * @throws ReflectionException - */ - private function protectedMethodsOfClass(string $className) : CodeUnitCollection + public function columns(): int|string { - return $this->methodsOfClass($className, ReflectionMethod::IS_PROTECTED); + return $this->columns; } - /** - * @psalm-param class-string $className - * - * @throws ReflectionException - */ - private function protectedAndPrivateMethodsOfClass(string $className) : CodeUnitCollection + public function colors(): string + { + return $this->colors; + } + public function stderr(): bool + { + return $this->stderr; + } + public function displayDetailsOnIncompleteTests(): bool + { + return $this->displayDetailsOnIncompleteTests; + } + public function displayDetailsOnSkippedTests(): bool + { + return $this->displayDetailsOnSkippedTests; + } + public function displayDetailsOnTestsThatTriggerDeprecations(): bool + { + return $this->displayDetailsOnTestsThatTriggerDeprecations; + } + public function displayDetailsOnTestsThatTriggerErrors(): bool + { + return $this->displayDetailsOnTestsThatTriggerErrors; + } + public function displayDetailsOnTestsThatTriggerNotices(): bool + { + return $this->displayDetailsOnTestsThatTriggerNotices; + } + public function displayDetailsOnTestsThatTriggerWarnings(): bool + { + return $this->displayDetailsOnTestsThatTriggerWarnings; + } + public function reverseDefectList(): bool + { + return $this->reverseDefectList; + } + public function requireCoverageMetadata(): bool { - return $this->methodsOfClass($className, ReflectionMethod::IS_PROTECTED | ReflectionMethod::IS_PRIVATE); + return $this->requireCoverageMetadata; } /** - * @psalm-param class-string $className - * - * @throws ReflectionException + * @psalm-assert-if-true !null $this->bootstrap */ - private function privateMethodsOfClass(string $className) : CodeUnitCollection + public function hasBootstrap(): bool { - return $this->methodsOfClass($className, ReflectionMethod::IS_PRIVATE); + return $this->bootstrap !== null; } /** - * @psalm-param class-string $className - * - * @throws ReflectionException + * @throws Exception */ - private function methodsOfClass(string $className, int $filter) : CodeUnitCollection + public function bootstrap(): string { - $units = []; - foreach ($this->reflectorForClass($className)->getMethods($filter) as $method) { - if (!$method->isUserDefined()) { - continue; - } - $units[] = CodeUnit::forClassMethod($className, $method->getName()); + if (!$this->hasBootstrap()) { + throw new \PHPUnit\TextUI\XmlConfiguration\Exception('Bootstrap script is not configured'); } - return CodeUnitCollection::fromArray($units); + return $this->bootstrap; + } + public function processIsolation(): bool + { + return $this->processIsolation; + } + public function failOnDeprecation(): bool + { + return $this->failOnDeprecation; + } + public function failOnEmptyTestSuite(): bool + { + return $this->failOnEmptyTestSuite; + } + public function failOnIncomplete(): bool + { + return $this->failOnIncomplete; + } + public function failOnNotice(): bool + { + return $this->failOnNotice; + } + public function failOnRisky(): bool + { + return $this->failOnRisky; + } + public function failOnSkipped(): bool + { + return $this->failOnSkipped; + } + public function failOnWarning(): bool + { + return $this->failOnWarning; + } + public function stopOnDefect(): bool + { + return $this->stopOnDefect; + } + public function stopOnDeprecation(): bool + { + return $this->stopOnDeprecation; + } + public function stopOnError(): bool + { + return $this->stopOnError; + } + public function stopOnFailure(): bool + { + return $this->stopOnFailure; + } + public function stopOnIncomplete(): bool + { + return $this->stopOnIncomplete; + } + public function stopOnNotice(): bool + { + return $this->stopOnNotice; + } + public function stopOnRisky(): bool + { + return $this->stopOnRisky; + } + public function stopOnSkipped(): bool + { + return $this->stopOnSkipped; + } + public function stopOnWarning(): bool + { + return $this->stopOnWarning; } /** - * @psalm-param class-string $className - * - * @throws ReflectionException + * @psalm-assert-if-true !null $this->extensionsDirectory */ - private function classAndParentClassesAndTraits(string $className) : CodeUnitCollection + public function hasExtensionsDirectory(): bool { - $units = [CodeUnit::forClass($className)]; - $reflector = $this->reflectorForClass($className); - foreach ($this->reflectorForClass($className)->getTraits() as $trait) { - if (!$trait->isUserDefined()) { - // @codeCoverageIgnoreStart - continue; - // @codeCoverageIgnoreEnd - } - $units[] = CodeUnit::forTrait($trait->getName()); - } - while ($reflector = $reflector->getParentClass()) { - if (!$reflector->isUserDefined()) { - break; - } - $units[] = CodeUnit::forClass($reflector->getName()); - foreach ($reflector->getTraits() as $trait) { - if (!$trait->isUserDefined()) { - // @codeCoverageIgnoreStart - continue; - // @codeCoverageIgnoreEnd - } - $units[] = CodeUnit::forTrait($trait->getName()); - } - } - return CodeUnitCollection::fromArray($units); + return $this->extensionsDirectory !== null; } /** - * @psalm-param class-string $className + * @psalm-return non-empty-string * - * @throws ReflectionException + * @throws Exception */ - private function reflectorForClass(string $className) : ReflectionClass + public function extensionsDirectory(): string { - try { - return new ReflectionClass($className); - // @codeCoverageIgnoreStart - } catch (\ReflectionException $e) { - throw new ReflectionException($e->getMessage(), (int) $e->getCode(), $e); + if (!$this->hasExtensionsDirectory()) { + throw new \PHPUnit\TextUI\XmlConfiguration\Exception('Extensions directory is not configured'); } - // @codeCoverageIgnoreEnd + return $this->extensionsDirectory; } - /** - * @throws ReflectionException - */ - private function isUserDefinedFunction(string $functionName) : bool + public function beStrictAboutChangesToGlobalState(): bool { - if (!function_exists($functionName)) { - return \false; - } - try { - return (new ReflectionFunction($functionName))->isUserDefined(); - // @codeCoverageIgnoreStart - } catch (\ReflectionException $e) { - throw new ReflectionException($e->getMessage(), (int) $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd + return $this->beStrictAboutChangesToGlobalState; } - /** - * @throws ReflectionException - */ - private function isUserDefinedClass(string $className) : bool + public function beStrictAboutOutputDuringTests(): bool { - if (!class_exists($className)) { - return \false; - } - try { - return (new ReflectionClass($className))->isUserDefined(); - // @codeCoverageIgnoreStart - } catch (\ReflectionException $e) { - throw new ReflectionException($e->getMessage(), (int) $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd + return $this->beStrictAboutOutputDuringTests; + } + public function beStrictAboutTestsThatDoNotTestAnything(): bool + { + return $this->beStrictAboutTestsThatDoNotTestAnything; + } + public function beStrictAboutCoverageMetadata(): bool + { + return $this->beStrictAboutCoverageMetadata; + } + public function enforceTimeLimit(): bool + { + return $this->enforceTimeLimit; + } + public function defaultTimeLimit(): int + { + return $this->defaultTimeLimit; + } + public function timeoutForSmallTests(): int + { + return $this->timeoutForSmallTests; + } + public function timeoutForMediumTests(): int + { + return $this->timeoutForMediumTests; + } + public function timeoutForLargeTests(): int + { + return $this->timeoutForLargeTests; } /** - * @throws ReflectionException + * @psalm-assert-if-true !null $this->defaultTestSuite */ - private function isUserDefinedInterface(string $interfaceName) : bool + public function hasDefaultTestSuite(): bool { - if (!interface_exists($interfaceName)) { - return \false; - } - try { - return (new ReflectionClass($interfaceName))->isUserDefined(); - // @codeCoverageIgnoreStart - } catch (\ReflectionException $e) { - throw new ReflectionException($e->getMessage(), (int) $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd + return $this->defaultTestSuite !== null; } /** - * @throws ReflectionException + * @throws Exception */ - private function isUserDefinedTrait(string $traitName) : bool + public function defaultTestSuite(): string { - if (!trait_exists($traitName)) { - return \false; - } - try { - return (new ReflectionClass($traitName))->isUserDefined(); - // @codeCoverageIgnoreStart - } catch (\ReflectionException $e) { - throw new ReflectionException($e->getMessage(), (int) $e->getCode(), $e); + if (!$this->hasDefaultTestSuite()) { + throw new \PHPUnit\TextUI\XmlConfiguration\Exception('Default test suite is not configured'); } - // @codeCoverageIgnoreEnd + return $this->defaultTestSuite; + } + public function executionOrder(): int + { + return $this->executionOrder; + } + public function resolveDependencies(): bool + { + return $this->resolveDependencies; + } + public function defectsFirst(): bool + { + return $this->defectsFirst; + } + public function backupGlobals(): bool + { + return $this->backupGlobals; + } + public function backupStaticProperties(): bool + { + return $this->backupStaticProperties; } /** - * @throws ReflectionException + * @deprecated */ - private function isUserDefinedMethod(string $className, string $methodName) : bool + public function registerMockObjectsFromTestArgumentsRecursively(): bool { - if (!class_exists($className)) { - // @codeCoverageIgnoreStart - return \false; - // @codeCoverageIgnoreEnd - } - if (!method_exists($className, $methodName)) { - // @codeCoverageIgnoreStart - return \false; - // @codeCoverageIgnoreEnd - } - try { - return (new ReflectionMethod($className, $methodName))->isUserDefined(); - // @codeCoverageIgnoreStart - } catch (\ReflectionException $e) { - throw new ReflectionException($e->getMessage(), (int) $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd + return $this->registerMockObjectsFromTestArgumentsRecursively; + } + public function testdoxPrinter(): bool + { + return $this->testdoxPrinter; + } + public function controlGarbageCollector(): bool + { + return $this->controlGarbageCollector; + } + public function numberOfTestsBeforeGarbageCollection(): int + { + return $this->numberOfTestsBeforeGarbageCollection; } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnitPHAR\SebastianBergmann\CodeUnit; +namespace PHPUnit\TextUI\XmlConfiguration; /** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + * * @psalm-immutable */ -final class TraitMethodUnit extends CodeUnit +final class FailedSchemaDetectionResult extends \PHPUnit\TextUI\XmlConfiguration\SchemaDetectionResult { - /** - * @psalm-assert-if-true TraitMethodUnit $this - */ - public function isTraitMethod() : bool - { - return \true; - } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnitPHAR\SebastianBergmann\CodeUnit; +namespace PHPUnit\TextUI\XmlConfiguration; +use PHPUnit\Util\Xml\XmlException; /** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + * * @psalm-immutable */ -final class TraitUnit extends CodeUnit +abstract class SchemaDetectionResult { /** - * @psalm-assert-if-true TraitUnit $this + * @psalm-assert-if-true SuccessfulSchemaDetectionResult $this */ - public function isTrait() : bool + public function detected(): bool { - return \true; + return \false; + } + /** + * @throws XmlException + */ + public function version(): string + { + throw new XmlException('No supported schema was detected'); } } - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnitPHAR\SebastianBergmann\CodeUnit; - -use Throwable; -interface Exception extends Throwable -{ -} - * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnitPHAR\SebastianBergmann\CodeUnit; - -use RuntimeException; -final class InvalidCodeUnitException extends RuntimeException implements Exception -{ -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. +use PHPUnit\Util\Xml\Loader; +use PHPUnit\Util\Xml\XmlException; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -namespace PHPUnitPHAR\SebastianBergmann\CodeUnit; - -use RuntimeException; -final class NoTraitException extends RuntimeException implements Exception +final class SchemaDetector { + /** + * @throws XmlException + */ + public function detect(string $filename): \PHPUnit\TextUI\XmlConfiguration\SchemaDetectionResult + { + $document = (new Loader())->loadFile($filename); + $schemaFinder = new \PHPUnit\TextUI\XmlConfiguration\SchemaFinder(); + foreach ($schemaFinder->available() as $candidate) { + $schema = (new \PHPUnit\TextUI\XmlConfiguration\SchemaFinder())->find($candidate); + if (!(new \PHPUnit\TextUI\XmlConfiguration\Validator())->validate($document, $schema)->hasValidationErrors()) { + return new \PHPUnit\TextUI\XmlConfiguration\SuccessfulSchemaDetectionResult($candidate); + } + } + return new \PHPUnit\TextUI\XmlConfiguration\FailedSchemaDetectionResult(); + } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnitPHAR\SebastianBergmann\CodeUnit; +namespace PHPUnit\TextUI\XmlConfiguration; -use RuntimeException; -final class ReflectionException extends RuntimeException implements Exception +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + * + * @psalm-immutable + */ +final class SuccessfulSchemaDetectionResult extends \PHPUnit\TextUI\XmlConfiguration\SchemaDetectionResult { + /** + * @psalm-var non-empty-string + */ + private readonly string $version; + /** + * @psalm-param non-empty-string $version + */ + public function __construct(string $version) + { + $this->version = $version; + } + /** + * @psalm-assert-if-true SuccessfulSchemaDetectionResult $this + */ + public function detected(): bool + { + return \true; + } + /** + * @psalm-return non-empty-string + */ + public function version(): string + { + return $this->version; + } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnitPHAR\SebastianBergmann\Comparator; +namespace PHPUnit\TextUI\XmlConfiguration; -use function array_key_exists; -use function is_array; -use function sort; +use function assert; +use function defined; +use function is_file; +use function rsort; use function sprintf; -use function str_replace; -use function trim; +use DirectoryIterator; +use PHPUnit\Runner\Version; /** - * Compares arrays for equality. - * - * Arrays are equal if they contain the same key-value pairs. - * The order of the keys does not matter. - * The types of key-value pairs do not matter. + * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -class ArrayComparator extends Comparator +final class SchemaFinder { /** - * 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 + * @psalm-return non-empty-list */ - public function accepts($expected, $actual) + public function available(): array { - return is_array($expected) && is_array($actual); + $result = [Version::series()]; + foreach (new DirectoryIterator($this->path() . 'schema') as $file) { + if ($file->isDot()) { + continue; + } + $version = $file->getBasename('.xsd'); + assert(!empty($version)); + $result[] = $version; + } + rsort($result); + return $result; } /** - * Asserts that two arrays 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 + * @throws CannotFindSchemaException */ - public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = \false, $ignoreCase = \false, array &$processed = []) + public function find(string $version): string { - 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; + if ($version === Version::series()) { + $filename = $this->path() . 'phpunit.xsd'; + } else { + $filename = $this->path() . 'schema/' . $version . '.xsd'; } - $expectedAsString .= ')'; - $actualAsString .= ')'; - if (!$equal) { - throw new ComparisonFailure($expected, $actual, $expectedAsString, $actualAsString, \false, 'Failed asserting that two arrays are equal.'); + if (!is_file($filename)) { + throw new \PHPUnit\TextUI\XmlConfiguration\CannotFindSchemaException(sprintf('Schema for PHPUnit %s is not available', $version)); } + return $filename; } - protected function indent($lines) + private function path(): string { - return trim(str_replace("\n", "\n ", $lines)); + if (defined('__PHPUNIT_PHAR_ROOT__')) { + return __PHPUNIT_PHAR_ROOT__ . '/'; + } + return __DIR__ . '/../../../../'; } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnitPHAR\SebastianBergmann\Comparator; +namespace PHPUnit\TextUI\XmlConfiguration; -use PHPUnitPHAR\SebastianBergmann\Exporter\Exporter; +use const PHP_VERSION; +use function array_merge; +use function array_unique; +use function explode; +use function in_array; +use function is_dir; +use function is_file; +use function str_contains; +use function version_compare; +use PHPUnit\Framework\Exception as FrameworkException; +use PHPUnit\Framework\TestSuite as TestSuiteObject; +use PHPUnit\TextUI\Configuration\TestSuiteCollection; +use PHPUnit\TextUI\RuntimeException; +use PHPUnit\TextUI\TestDirectoryNotFoundException; +use PHPUnit\TextUI\TestFileNotFoundException; +use PHPUnitPHAR\SebastianBergmann\FileIterator\Facade; /** - * Abstract base class for comparators which compare values for equality. + * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -abstract class Comparator +final class TestSuiteMapper { /** - * @var Factory - */ - protected $factory; - /** - * @var Exporter + * @psalm-param non-empty-string $xmlConfigurationFile, + * + * @throws RuntimeException + * @throws TestDirectoryNotFoundException + * @throws TestFileNotFoundException */ - protected $exporter; - public function __construct() - { - $this->exporter = new Exporter(); - } - public function setFactory(Factory $factory) + public function map(string $xmlConfigurationFile, TestSuiteCollection $configuration, string $filter, string $excludedTestSuites): TestSuiteObject { - $this->factory = $factory; + try { + $filterAsArray = $filter ? explode(',', $filter) : []; + $excludedFilterAsArray = $excludedTestSuites ? explode(',', $excludedTestSuites) : []; + $result = TestSuiteObject::empty($xmlConfigurationFile); + foreach ($configuration as $testSuiteConfiguration) { + if (!empty($filterAsArray) && !in_array($testSuiteConfiguration->name(), $filterAsArray, \true)) { + continue; + } + if (!empty($excludedFilterAsArray) && in_array($testSuiteConfiguration->name(), $excludedFilterAsArray, \true)) { + continue; + } + $exclude = []; + foreach ($testSuiteConfiguration->exclude()->asArray() as $file) { + $exclude[] = $file->path(); + } + $files = []; + foreach ($testSuiteConfiguration->directories() as $directory) { + if (!str_contains($directory->path(), '*') && !is_dir($directory->path())) { + throw new TestDirectoryNotFoundException($directory->path()); + } + if (!version_compare(PHP_VERSION, $directory->phpVersion(), $directory->phpVersionOperator()->asString())) { + continue; + } + $files = array_merge($files, (new Facade())->getFilesAsArray($directory->path(), $directory->suffix(), $directory->prefix(), $exclude)); + } + foreach ($testSuiteConfiguration->files() as $file) { + if (!is_file($file->path())) { + throw new TestFileNotFoundException($file->path()); + } + if (!version_compare(PHP_VERSION, $file->phpVersion(), $file->phpVersionOperator()->asString())) { + continue; + } + $files[] = $file->path(); + } + if (!empty($files)) { + $testSuite = TestSuiteObject::empty($testSuiteConfiguration->name()); + $testSuite->addTestFiles(array_unique($files)); + $result->addTest($testSuite); + } + } + return $result; + } catch (FrameworkException $e) { + throw new RuntimeException($e->getMessage(), $e->getCode(), $e); + } } - /** - * 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 abstract 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 - */ - public abstract function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = \false, $ignoreCase = \false); } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnitPHAR\SebastianBergmann\Comparator; +namespace PHPUnit\TextUI\XmlConfiguration; -use RuntimeException; -use PHPUnitPHAR\SebastianBergmann\Diff\Differ; -use PHPUnitPHAR\SebastianBergmann\Diff\Output\UnifiedDiffOutputBuilder; +use function sprintf; +use function trim; +use LibXMLError; /** - * Thrown when an assertion for string equality failed. + * @internal This class is not covered by the backward compatibility promise for PHPUnit + * + * @psalm-immutable */ -class ComparisonFailure extends RuntimeException +final class ValidationResult { /** - * 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 + * @psalm-var array> */ - protected $message; + private readonly array $validationErrors; /** - * 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 + * @psalm-param array $errors */ - 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() + public static function fromArray(array $errors): self { - return $this->expected; + $validationErrors = []; + foreach ($errors as $error) { + if (!isset($validationErrors[$error->line])) { + $validationErrors[$error->line] = []; + } + $validationErrors[$error->line][] = trim($error->message); + } + return new self($validationErrors); } - /** - * @return string - */ - public function getActualAsString() + private function __construct(array $validationErrors) { - return $this->actualAsString; + $this->validationErrors = $validationErrors; } - /** - * @return string - */ - public function getExpectedAsString() + public function hasValidationErrors(): bool { - return $this->expectedAsString; + return !empty($this->validationErrors); } - /** - * @return string - */ - public function getDiff() + public function asString(): string { - if (!$this->actualAsString && !$this->expectedAsString) { - return ''; + $buffer = ''; + foreach ($this->validationErrors as $line => $validationErrorsOnLine) { + $buffer .= sprintf(\PHP_EOL . ' Line %d:' . \PHP_EOL, $line); + foreach ($validationErrorsOnLine as $validationError) { + $buffer .= sprintf(' - %s' . \PHP_EOL, $validationError); + } } - $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(); + return $buffer; } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnitPHAR\SebastianBergmann\Comparator; +namespace PHPUnit\TextUI\XmlConfiguration; -use function sprintf; -use function strtolower; +use function file_get_contents; +use function libxml_clear_errors; +use function libxml_get_errors; +use function libxml_use_internal_errors; use DOMDocument; -use DOMNode; -use ValueError; /** - * Compares DOMNode instances for equality. + * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -class DOMNodeComparator extends ObjectComparator +final class Validator { - /** - * 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 + public function validate(DOMDocument $document, string $xsdFilename): \PHPUnit\TextUI\XmlConfiguration\ValidationResult { - if ($canonicalize) { - $document = new DOMDocument(); - try { - @$document->loadXML($node->C14N()); - } catch (ValueError $e) { - } - $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; + $originalErrorHandling = libxml_use_internal_errors(\true); + $document->schemaValidateSource(file_get_contents($xsdFilename)); + $errors = libxml_get_errors(); + libxml_clear_errors(); + libxml_use_internal_errors($originalErrorHandling); + return \PHPUnit\TextUI\XmlConfiguration\ValidationResult::fromArray($errors); } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnitPHAR\SebastianBergmann\Comparator; +namespace PHPUnit\TextUI; -use function abs; -use function floor; use function sprintf; -use DateInterval; -use DateTime; -use DateTimeInterface; -use DateTimeZone; -use Exception; +use RuntimeException; /** - * Compares DateTimeInterface instances for equality. + * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -class DateTimeComparator extends ObjectComparator +final class CannotOpenSocketException extends RuntimeException implements \PHPUnit\TextUI\Exception { - /** - * 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 + public function __construct(string $hostname, int $port) { - $string = $datetime->format('Y-m-d\\TH:i:s.uO'); - return $string ?: 'Invalid DateTimeInterface object'; + parent::__construct(sprintf('Cannot open socket %s:%d', $hostname, $port)); } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnitPHAR\SebastianBergmann\Comparator; +namespace PHPUnit\TextUI; -use function is_float; -use function is_numeric; +use Throwable; /** - * Compares doubles for equality. - * - * @deprecated since v3.0.5 and v4.0.8 + * @internal This interface is not covered by the backward compatibility promise for PHPUnit */ -class DoubleComparator extends NumericComparator +interface Exception extends Throwable { - /** - * Smallest value available in PHP. - * - * @var float - */ - public const EPSILON = 1.0E-10; - /** - * Returns whether the comparator can compare two values. - * - * @param mixed $expected The first value to compare - * @param mixed $actual The second value to compare - * - * @return bool - */ - public function accepts($expected, $actual) - { - return (is_float($expected) || is_float($actual)) && is_numeric($expected) && is_numeric($actual); - } - /** - * Asserts that two values are equal. - * - * @param mixed $expected First value to compare - * @param mixed $actual Second value to compare - * @param float $delta Allowed numerical distance between two values to consider them equal - * @param bool $canonicalize Arrays are sorted before comparison when set to true - * @param bool $ignoreCase Case is ignored when set to true - * - * @throws ComparisonFailure - */ - public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = \false, $ignoreCase = \false) - { - if ($delta == 0) { - $delta = self::EPSILON; - } - parent::assertEquals($expected, $actual, $delta, $canonicalize, $ignoreCase); - } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnitPHAR\SebastianBergmann\Comparator; +namespace PHPUnit\TextUI; -use Exception; +use RuntimeException; /** - * Compares Exception instances for equality. + * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -class ExceptionComparator extends ObjectComparator +final class ExtensionsNotConfiguredException extends RuntimeException implements \PHPUnit\TextUI\Exception { - /** - * Returns whether the comparator can compare two values. - * - * @param mixed $expected The first value to compare - * @param mixed $actual The second value to compare - * - * @return bool - */ - public function accepts($expected, $actual) - { - return $expected instanceof Exception && $actual instanceof Exception; - } - /** - * Converts an object to an array containing all of its private, protected - * and public properties. - * - * @param object $object - * - * @return array - */ - protected function toArray($object) - { - $array = parent::toArray($object); - unset($array['file'], $array['line'], $array['trace'], $array['string'], $array['xdebug_message']); - return $array; - } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnitPHAR\SebastianBergmann\Comparator; +namespace PHPUnit\TextUI; -use function array_unshift; +use function sprintf; +use RuntimeException; /** - * Factory for comparators which compare values for equality. + * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -class Factory +final class InvalidSocketException extends RuntimeException implements \PHPUnit\TextUI\Exception { - /** - * @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(); - // @codeCoverageIgnore - } - 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; - } - } - throw new RuntimeException('No suitable Comparator implementation found'); - } - /** - * 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() : void - { - $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 NumericComparator()); - $this->registerDefaultComparator(new ScalarComparator()); - $this->registerDefaultComparator(new TypeComparator()); - } - private function registerDefaultComparator(Comparator $comparator) : void + public function __construct(string $socket) { - $this->defaultComparators[] = $comparator; - $comparator->setFactory($this); + parent::__construct(sprintf('"%s" does not match "socket://hostname:port" format', $socket)); } } -Comparator +. -All rights reserved. +declare (strict_types=1); +/* + * This file is part of PHPUnit. + * + * (c) Sebastian Bergmann + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI; -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the - distribution. - - * Neither the name of Sebastian Bergmann nor the names of his - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN -ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. +use RuntimeException; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class ReflectionException extends RuntimeException implements \PHPUnit\TextUI\Exception +{ +} * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnitPHAR\SebastianBergmann\Comparator; +namespace PHPUnit\TextUI; -use PHPUnit\Framework\MockObject\MockObject; /** - * Compares PHPUnit\Framework\MockObject\MockObject instances for equality. + * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -class MockObjectComparator extends ObjectComparator +final class RuntimeException extends \RuntimeException implements \PHPUnit\TextUI\Exception { - /** - * 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 MockObject && $actual instanceof MockObject; - } - /** - * Converts an object to an array containing all of its private, protected - * and public properties. - * - * @param object $object - * - * @return array - */ - protected function toArray($object) - { - $array = parent::toArray($object); - unset($array['__phpunit_invocationMocker']); - return $array; - } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnitPHAR\SebastianBergmann\Comparator; +namespace PHPUnit\TextUI; -use function abs; -use function is_float; -use function is_infinite; -use function is_nan; -use function is_numeric; -use function is_string; use function sprintf; +use RuntimeException; /** - * Compares numerical values for equality. + * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -class NumericComparator extends ScalarComparator +final class TestDirectoryNotFoundException extends RuntimeException implements \PHPUnit\TextUI\Exception { - /** - * 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 both of them are strings - return is_numeric($expected) && is_numeric($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 ($this->isInfinite($actual) && $this->isInfinite($expected)) { - return; - } - if (($this->isInfinite($actual) xor $this->isInfinite($expected)) || ($this->isNan($actual) || $this->isNan($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))); - } - } - private function isInfinite($value) : bool - { - return is_float($value) && is_infinite($value); - } - private function isNan($value) : bool + public function __construct(string $path) { - return is_float($value) && is_nan($value); + parent::__construct(sprintf('Test directory "%s" not found', $path)); } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnitPHAR\SebastianBergmann\Comparator; +namespace PHPUnit\TextUI; -use function get_class; -use function in_array; -use function is_object; use function sprintf; -use function substr_replace; +use RuntimeException; /** - * Compares objects for equality. + * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -class ObjectComparator extends ArrayComparator +final class TestFileNotFoundException extends RuntimeException implements \PHPUnit\TextUI\Exception { - /** - * 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) + public function __construct(string $path) { - return $this->exporter->toArray($object); + parent::__construct(sprintf('Test file "%s" not found', $path)); } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnitPHAR\SebastianBergmann\Comparator; +namespace PHPUnit\TextUI; -use function is_resource; +use const PHP_EOL; +use function count; +use function defined; +use function explode; +use function max; +use function preg_replace_callback; +use function str_pad; +use function str_repeat; +use function strlen; +use function wordwrap; +use PHPUnit\Util\Color; +use PHPUnitPHAR\SebastianBergmann\Environment\Console; /** - * Compares resources for equality. + * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -class ResourceComparator extends Comparator +final class Help { - /** - * 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) + private const LEFT_MARGIN = ' '; + private int $lengthOfLongestOptionName = 0; + private readonly int $columnsAvailableForDescription; + private ?bool $hasColor; + public function __construct(?int $width = null, ?bool $withColor = null) { - return is_resource($expected) && is_resource($actual); + if ($width === null) { + $width = (new Console())->getNumberOfColumns(); + } + if ($withColor === null) { + $this->hasColor = (new Console())->hasColorSupport(); + } else { + $this->hasColor = $withColor; + } + foreach ($this->elements() as $options) { + foreach ($options as $option) { + if (isset($option['arg'])) { + $this->lengthOfLongestOptionName = max($this->lengthOfLongestOptionName, strlen($option['arg'])); + } + } + } + $this->columnsAvailableForDescription = $width - $this->lengthOfLongestOptionName - 4; + } + public function generate(): string + { + if ($this->hasColor) { + return $this->writeWithColor(); + } + return $this->writeWithoutColor(); + } + private function writeWithoutColor(): string + { + $buffer = ''; + foreach ($this->elements() as $section => $options) { + $buffer .= "{$section}:" . PHP_EOL; + if ($section !== 'Usage') { + $buffer .= PHP_EOL; + } + foreach ($options as $option) { + if (isset($option['spacer'])) { + $buffer .= PHP_EOL; + } + if (isset($option['text'])) { + $buffer .= self::LEFT_MARGIN . $option['text'] . PHP_EOL; + } + if (isset($option['arg'])) { + $arg = str_pad($option['arg'], $this->lengthOfLongestOptionName); + $buffer .= self::LEFT_MARGIN . $arg . ' ' . $option['desc'] . PHP_EOL; + } + } + $buffer .= PHP_EOL; + } + return $buffer; + } + private function writeWithColor(): string + { + $buffer = ''; + foreach ($this->elements() as $section => $options) { + $buffer .= Color::colorize('fg-yellow', "{$section}:") . PHP_EOL; + if ($section !== 'Usage') { + $buffer .= PHP_EOL; + } + foreach ($options as $option) { + if (isset($option['spacer'])) { + $buffer .= PHP_EOL; + } + if (isset($option['text'])) { + $buffer .= self::LEFT_MARGIN . $option['text'] . PHP_EOL; + } + if (isset($option['arg'])) { + $arg = Color::colorize('fg-green', str_pad($option['arg'], $this->lengthOfLongestOptionName)); + $arg = preg_replace_callback('/(<[^>]+>)/', static fn($matches) => Color::colorize('fg-cyan', $matches[0]), $arg); + $desc = explode(PHP_EOL, wordwrap($option['desc'], $this->columnsAvailableForDescription, PHP_EOL)); + $buffer .= self::LEFT_MARGIN . $arg . ' ' . $desc[0] . PHP_EOL; + for ($i = 1; $i < count($desc); $i++) { + $buffer .= str_repeat(' ', $this->lengthOfLongestOptionName + 3) . $desc[$i] . PHP_EOL; + } + } + } + $buffer .= PHP_EOL; + } + return $buffer; } /** - * 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 + * @psalm-return array> */ - public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = \false, $ignoreCase = \false) + private function elements(): array { - if ($actual != $expected) { - throw new ComparisonFailure($expected, $actual, $this->exporter->export($expected), $this->exporter->export($actual)); + $elements = ['Usage' => [['text' => 'phpunit [options] ...']], 'Configuration' => [['arg' => '--bootstrap ', 'desc' => 'A PHP script that is included before the tests run'], ['arg' => '-c|--configuration ', 'desc' => 'Read configuration from XML file'], ['arg' => '--no-configuration', 'desc' => 'Ignore default configuration file (phpunit.xml)'], ['arg' => '--no-extensions', 'desc' => 'Do not load PHPUnit extensions'], ['arg' => '--include-path ', 'desc' => 'Prepend PHP\'s include_path with given path(s)'], ['arg' => '-d ', 'desc' => 'Sets a php.ini value'], ['arg' => '--cache-directory ', 'desc' => 'Specify cache directory'], ['arg' => '--generate-configuration', 'desc' => 'Generate configuration file with suggested settings'], ['arg' => '--migrate-configuration', 'desc' => 'Migrate configuration file to current format'], ['arg' => '--generate-baseline ', 'desc' => 'Generate baseline for issues'], ['arg' => '--use-baseline ', 'desc' => 'Use baseline to ignore issues'], ['arg' => '--ignore-baseline', 'desc' => 'Do not use baseline to ignore issues']], 'Selection' => [['arg' => '--list-suites', 'desc' => 'List available test suites'], ['arg' => '--testsuite ', 'desc' => 'Only run tests from the specified test suite(s)'], ['arg' => '--exclude-testsuite ', 'desc' => 'Exclude tests from the specified test suite(s)'], ['arg' => '--list-groups', 'desc' => 'List available test groups'], ['arg' => '--group ', 'desc' => 'Only run tests from the specified group(s)'], ['arg' => '--exclude-group ', 'desc' => 'Exclude tests from the specified group(s)'], ['arg' => '--covers ', 'desc' => 'Only run tests that intend to cover '], ['arg' => '--uses ', 'desc' => 'Only run tests that intend to use '], ['arg' => '--list-tests', 'desc' => 'List available tests'], ['arg' => '--list-tests-xml ', 'desc' => 'List available tests in XML format'], ['arg' => '--filter ', 'desc' => 'Filter which tests to run'], ['arg' => '--test-suffix ', 'desc' => 'Only search for test in files with specified suffix(es). Default: Test.php,.phpt']], 'Execution' => [['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 properties for each test'], ['spacer' => ''], ['arg' => '--strict-coverage', 'desc' => 'Be strict about code coverage metadata'], ['arg' => '--strict-global-state', 'desc' => 'Be strict about changes to global state'], ['arg' => '--disallow-test-output', 'desc' => 'Be strict about output during tests'], ['arg' => '--enforce-time-limit', 'desc' => 'Enforce time limit based on test size'], ['arg' => '--default-time-limit ', 'desc' => 'Timeout in seconds for tests that have no declared size'], ['arg' => '--dont-report-useless-tests', 'desc' => 'Do not report tests that do not test anything'], ['spacer' => ''], ['arg' => '--stop-on-defect', 'desc' => 'Stop after first error, failure, warning, or risky test'], ['arg' => '--stop-on-error', 'desc' => 'Stop after first error'], ['arg' => '--stop-on-failure', 'desc' => 'Stop after first failure'], ['arg' => '--stop-on-warning', 'desc' => 'Stop after first warning'], ['arg' => '--stop-on-risky', 'desc' => 'Stop after first risky test'], ['arg' => '--stop-on-deprecation', 'desc' => 'Stop after first test that triggered a deprecation'], ['arg' => '--stop-on-notice', 'desc' => 'Stop after first test that triggered a notice'], ['arg' => '--stop-on-skipped', 'desc' => 'Stop after first skipped test'], ['arg' => '--stop-on-incomplete', 'desc' => 'Stop after first incomplete test'], ['spacer' => ''], ['arg' => '--fail-on-empty-test-suite', 'desc' => 'Signal failure using shell exit code when no tests were run'], ['arg' => '--fail-on-warning', 'desc' => 'Signal failure using shell exit code when a warning was triggered'], ['arg' => '--fail-on-risky', 'desc' => 'Signal failure using shell exit code when a test was considered risky'], ['arg' => '--fail-on-deprecation', 'desc' => 'Signal failure using shell exit code when a deprecation was triggered'], ['arg' => '--fail-on-notice', 'desc' => 'Signal failure using shell exit code when a notice was triggered'], ['arg' => '--fail-on-skipped', 'desc' => 'Signal failure using shell exit code when a test was skipped'], ['arg' => '--fail-on-incomplete', 'desc' => 'Signal failure using shell exit code when a test was marked incomplete'], ['spacer' => ''], ['arg' => '--cache-result', 'desc' => 'Write test results to cache file'], ['arg' => '--do-not-cache-result', 'desc' => 'Do not write test results to cache file'], ['spacer' => ''], ['arg' => '--order-by ', 'desc' => 'Run tests in order: default|defects|depends|duration|no-depends|random|reverse|size'], ['arg' => '--random-order-seed ', 'desc' => 'Use the specified random seed when running tests in random order']], 'Reporting' => [['arg' => '--colors ', 'desc' => 'Use colors in output ("never", "auto" or "always")'], ['arg' => '--columns ', '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'], ['spacer' => ''], ['arg' => '--no-progress', 'desc' => 'Disable output of test execution progress'], ['arg' => '--no-results', 'desc' => 'Disable output of test results'], ['arg' => '--no-output', 'desc' => 'Disable all output'], ['spacer' => ''], ['arg' => '--display-incomplete', 'desc' => 'Display details for incomplete tests'], ['arg' => '--display-skipped', 'desc' => 'Display details for skipped tests'], ['arg' => '--display-deprecations', 'desc' => 'Display details for deprecations triggered by tests'], ['arg' => '--display-errors', 'desc' => 'Display details for errors triggered by tests'], ['arg' => '--display-notices', 'desc' => 'Display details for notices triggered by tests'], ['arg' => '--display-warnings', 'desc' => 'Display details for warnings triggered by tests'], ['arg' => '--reverse-list', 'desc' => 'Print defects in reverse order'], ['spacer' => ''], ['arg' => '--teamcity', 'desc' => 'Replace default progress and result output with TeamCity format'], ['arg' => '--testdox', 'desc' => 'Replace default result output with TestDox format'], ['spacer' => ''], ['arg' => '--debug', 'desc' => 'Replace default progress and result output with debugging information']], 'Logging' => [['arg' => '--log-junit ', 'desc' => 'Write test results in JUnit XML format to file'], ['arg' => '--log-teamcity ', 'desc' => 'Write test results in TeamCity format to file'], ['arg' => '--testdox-html ', 'desc' => 'Write test results in TestDox format (HTML) to file'], ['arg' => '--testdox-text ', 'desc' => 'Write test results in TestDox format (plain text) to file'], ['arg' => '--log-events-text ', 'desc' => 'Stream events as plain text to file'], ['arg' => '--log-events-verbose-text ', 'desc' => 'Stream events as plain text with extended information to file'], ['arg' => '--no-logging', 'desc' => 'Ignore logging configured in the XML configuration file']], 'Code Coverage' => [['arg' => '--coverage-clover ', 'desc' => 'Write code coverage report in Clover XML format to file'], ['arg' => '--coverage-cobertura ', 'desc' => 'Write code coverage report in Cobertura XML format to file'], ['arg' => '--coverage-crap4j ', 'desc' => 'Write code coverage report in Crap4J XML format to file'], ['arg' => '--coverage-html ', 'desc' => 'Write code coverage report in HTML format to directory'], ['arg' => '--coverage-php ', 'desc' => 'Write serialized code coverage data to file'], ['arg' => '--coverage-text=', 'desc' => 'Write code coverage report in text format to file [default: standard output]'], ['arg' => '--only-summary-for-coverage-text', 'desc' => 'Option for code coverage report in text format: only show summary'], ['arg' => '--show-uncovered-for-coverage-text', 'desc' => 'Option for code coverage report in text format: show uncovered files'], ['arg' => '--coverage-xml ', 'desc' => 'Write code coverage report in XML format to directory'], ['arg' => '--warm-coverage-cache', 'desc' => 'Warm static analysis cache'], ['arg' => '--coverage-filter ', 'desc' => 'Include in code coverage reporting'], ['arg' => '--path-coverage', 'desc' => 'Report path coverage in addition to line coverage'], ['arg' => '--disable-coverage-ignore', 'desc' => 'Disable metadata for ignoring code coverage'], ['arg' => '--no-coverage', 'desc' => 'Ignore code coverage reporting configured in the XML configuration file']]]; + if (defined('__PHPUNIT_PHAR__')) { + $elements['PHAR'] = [['arg' => '--manifest', 'desc' => 'Print Software Bill of Materials (SBOM) in plain-text format'], ['arg' => '--sbom', 'desc' => 'Print Software Bill of Materials (SBOM) in CycloneDX XML format'], ['arg' => '--composer-lock', 'desc' => 'Print composer.lock file used to build the PHAR']]; } + $elements['Miscellaneous'] = [['arg' => '-h|--help', 'desc' => 'Prints this usage information'], ['arg' => '--version', 'desc' => 'Prints the version and exits'], ['arg' => '--atleast-version ', 'desc' => 'Checks that version is greater than and exits'], ['arg' => '--check-version', 'desc' => 'Checks whether PHPUnit is the latest version and exits']]; + return $elements; } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnitPHAR\SebastianBergmann\Comparator; +namespace PHPUnit\TextUI\Output\Default\ProgressPrinter; -use function is_bool; -use function is_object; -use function is_scalar; -use function is_string; -use function method_exists; +use function floor; use function sprintf; -use function strtolower; +use function str_contains; +use function str_repeat; +use function strlen; +use PHPUnit\Event\EventFacadeIsSealedException; +use PHPUnit\Event\Facade; +use PHPUnit\Event\Test\DeprecationTriggered; +use PHPUnit\Event\Test\Errored; +use PHPUnit\Event\Test\ErrorTriggered; +use PHPUnit\Event\Test\NoticeTriggered; +use PHPUnit\Event\Test\PhpDeprecationTriggered; +use PHPUnit\Event\Test\PhpNoticeTriggered; +use PHPUnit\Event\Test\PhpWarningTriggered; +use PHPUnit\Event\Test\WarningTriggered; +use PHPUnit\Event\TestRunner\ExecutionStarted; +use PHPUnit\Event\UnknownSubscriberTypeException; +use PHPUnit\Framework\TestStatus\TestStatus; +use PHPUnit\TextUI\Configuration\Source; +use PHPUnit\TextUI\Configuration\SourceFilter; +use PHPUnit\TextUI\Output\Printer; +use PHPUnit\Util\Color; /** - * Compares scalar or NULL values for equality. + * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -class ScalarComparator extends Comparator +final class ProgressPrinter { + private readonly Printer $printer; + private readonly bool $colors; + private readonly int $numberOfColumns; + private readonly Source $source; + private int $column = 0; + private int $numberOfTests = 0; + private int $numberOfTestsWidth = 0; + private int $maxColumn = 0; + private int $numberOfTestsRun = 0; + private ?TestStatus $status = null; + private bool $prepared = \false; /** - * 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 + * @throws EventFacadeIsSealedException + * @throws UnknownSubscriberTypeException */ - public function accepts($expected, $actual) + public function __construct(Printer $printer, Facade $facade, bool $colors, int $numberOfColumns, Source $source) { - return (is_scalar($expected) xor null === $expected) && (is_scalar($actual) xor null === $actual) || is_string($expected) && is_object($actual) && method_exists($actual, '__toString') || is_object($expected) && method_exists($expected, '__toString') && is_string($actual); + $this->printer = $printer; + $this->colors = $colors; + $this->numberOfColumns = $numberOfColumns; + $this->source = $source; + $this->registerSubscribers($facade); } - /** - * 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) + public function testRunnerExecutionStarted(ExecutionStarted $event): void { - $expectedToCompare = $expected; - $actualToCompare = $actual; - // always compare as strings to avoid strange behaviour - // otherwise 0 == 'Foobar' - if (is_string($expected) && !is_bool($actual) || is_string($actual) && !is_bool($expected)) { - $expectedToCompare = (string) $expectedToCompare; - $actualToCompare = (string) $actualToCompare; - if ($ignoreCase) { - $expectedToCompare = strtolower($expectedToCompare); - $actualToCompare = strtolower($actualToCompare); - } + $this->numberOfTestsRun = 0; + $this->numberOfTests = $event->testSuite()->count(); + $this->numberOfTestsWidth = strlen((string) $this->numberOfTests); + $this->column = 0; + $this->maxColumn = $this->numberOfColumns - strlen(' / (XXX%)') - 2 * $this->numberOfTestsWidth; + } + public function beforeTestClassMethodErrored(): void + { + $this->printProgressForError(); + $this->updateTestStatus(TestStatus::error()); + } + public function testPrepared(): void + { + $this->prepared = \true; + } + public function testSkipped(): void + { + if (!$this->prepared) { + $this->printProgressForSkipped(); + } else { + $this->updateTestStatus(TestStatus::skipped()); } - 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.'); + } + public function testMarkedIncomplete(): void + { + $this->updateTestStatus(TestStatus::incomplete()); + } + public function testTriggeredNotice(NoticeTriggered $event): void + { + if ($event->ignoredByBaseline()) { + return; } - 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)) - ); + if ($this->source->restrictNotices() && !(new SourceFilter())->includes($this->source, $event->file())) { + return; + } + if (!$this->source->ignoreSuppressionOfNotices() && $event->wasSuppressed()) { + return; } + $this->updateTestStatus(TestStatus::notice()); } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnitPHAR\SebastianBergmann\Comparator; - -use SplObjectStorage; -/** - * 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) + public function testTriggeredPhpNotice(PhpNoticeTriggered $event): void { - return $expected instanceof SplObjectStorage && $actual instanceof SplObjectStorage; + if ($event->ignoredByBaseline()) { + return; + } + if ($this->source->restrictNotices() && !(new SourceFilter())->includes($this->source, $event->file())) { + return; + } + if (!$this->source->ignoreSuppressionOfPhpNotices() && $event->wasSuppressed()) { + return; + } + $this->updateTestStatus(TestStatus::notice()); + } + public function testTriggeredDeprecation(DeprecationTriggered $event): void + { + if ($event->ignoredByBaseline() || $event->ignoredByTest()) { + return; + } + if ($this->source->restrictDeprecations() && !(new SourceFilter())->includes($this->source, $event->file())) { + return; + } + if (!$this->source->ignoreSuppressionOfDeprecations() && $event->wasSuppressed()) { + return; + } + $this->updateTestStatus(TestStatus::deprecation()); + } + public function testTriggeredPhpDeprecation(PhpDeprecationTriggered $event): void + { + if ($event->ignoredByBaseline() || $event->ignoredByTest()) { + return; + } + if ($this->source->restrictDeprecations() && !(new SourceFilter())->includes($this->source, $event->file())) { + return; + } + if (!$this->source->ignoreSuppressionOfPhpDeprecations() && $event->wasSuppressed()) { + return; + } + $this->updateTestStatus(TestStatus::deprecation()); + } + public function testTriggeredPhpunitDeprecation(): void + { + $this->updateTestStatus(TestStatus::deprecation()); + } + public function testConsideredRisky(): void + { + $this->updateTestStatus(TestStatus::risky()); + } + public function testTriggeredWarning(WarningTriggered $event): void + { + if ($event->ignoredByBaseline()) { + return; + } + if ($this->source->restrictWarnings() && !(new SourceFilter())->includes($this->source, $event->file())) { + return; + } + if (!$this->source->ignoreSuppressionOfWarnings() && $event->wasSuppressed()) { + return; + } + $this->updateTestStatus(TestStatus::warning()); + } + public function testTriggeredPhpWarning(PhpWarningTriggered $event): void + { + if ($event->ignoredByBaseline()) { + return; + } + if ($this->source->restrictWarnings() && !(new SourceFilter())->includes($this->source, $event->file())) { + return; + } + if (!$this->source->ignoreSuppressionOfPhpWarnings() && $event->wasSuppressed()) { + return; + } + $this->updateTestStatus(TestStatus::warning()); + } + public function testTriggeredPhpunitWarning(): void + { + $this->updateTestStatus(TestStatus::warning()); + } + public function testTriggeredError(ErrorTriggered $event): void + { + if (!$this->source->ignoreSuppressionOfErrors() && $event->wasSuppressed()) { + return; + } + $this->updateTestStatus(TestStatus::error()); + } + public function testFailed(): void + { + $this->updateTestStatus(TestStatus::failure()); + } + public function testErrored(Errored $event): void + { + /* + * @todo Eliminate this special case + */ + if (str_contains($event->asString(), 'Test was run in child process and ended unexpectedly')) { + $this->updateTestStatus(TestStatus::error()); + return; + } + if (!$this->prepared) { + $this->printProgressForError(); + } else { + $this->updateTestStatus(TestStatus::error()); + } + } + public function testFinished(): void + { + if ($this->status === null) { + $this->printProgressForSuccess(); + } elseif ($this->status->isSkipped()) { + $this->printProgressForSkipped(); + } elseif ($this->status->isIncomplete()) { + $this->printProgressForIncomplete(); + } elseif ($this->status->isRisky()) { + $this->printProgressForRisky(); + } elseif ($this->status->isNotice()) { + $this->printProgressForNotice(); + } elseif ($this->status->isDeprecation()) { + $this->printProgressForDeprecation(); + } elseif ($this->status->isWarning()) { + $this->printProgressForWarning(); + } elseif ($this->status->isFailure()) { + $this->printProgressForFailure(); + } else { + $this->printProgressForError(); + } + $this->status = null; + $this->prepared = \false; } /** - * 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 + * @throws EventFacadeIsSealedException + * @throws UnknownSubscriberTypeException */ - public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = \false, $ignoreCase = \false) + private function registerSubscribers(Facade $facade): void { - 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.'); - } + $facade->registerSubscribers(new \PHPUnit\TextUI\Output\Default\ProgressPrinter\BeforeTestClassMethodErroredSubscriber($this), new \PHPUnit\TextUI\Output\Default\ProgressPrinter\TestConsideredRiskySubscriber($this), new \PHPUnit\TextUI\Output\Default\ProgressPrinter\TestErroredSubscriber($this), new \PHPUnit\TextUI\Output\Default\ProgressPrinter\TestFailedSubscriber($this), new \PHPUnit\TextUI\Output\Default\ProgressPrinter\TestFinishedSubscriber($this), new \PHPUnit\TextUI\Output\Default\ProgressPrinter\TestMarkedIncompleteSubscriber($this), new \PHPUnit\TextUI\Output\Default\ProgressPrinter\TestPreparedSubscriber($this), new \PHPUnit\TextUI\Output\Default\ProgressPrinter\TestRunnerExecutionStartedSubscriber($this), new \PHPUnit\TextUI\Output\Default\ProgressPrinter\TestSkippedSubscriber($this), new \PHPUnit\TextUI\Output\Default\ProgressPrinter\TestTriggeredDeprecationSubscriber($this), new \PHPUnit\TextUI\Output\Default\ProgressPrinter\TestTriggeredNoticeSubscriber($this), new \PHPUnit\TextUI\Output\Default\ProgressPrinter\TestTriggeredPhpDeprecationSubscriber($this), new \PHPUnit\TextUI\Output\Default\ProgressPrinter\TestTriggeredPhpNoticeSubscriber($this), new \PHPUnit\TextUI\Output\Default\ProgressPrinter\TestTriggeredPhpunitDeprecationSubscriber($this), new \PHPUnit\TextUI\Output\Default\ProgressPrinter\TestTriggeredPhpunitWarningSubscriber($this), new \PHPUnit\TextUI\Output\Default\ProgressPrinter\TestTriggeredPhpWarningSubscriber($this), new \PHPUnit\TextUI\Output\Default\ProgressPrinter\TestTriggeredWarningSubscriber($this)); + } + private function updateTestStatus(TestStatus $status): void + { + if ($this->status !== null && $this->status->isMoreImportantThan($status)) { + return; } - 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.'); + $this->status = $status; + } + private function printProgressForSuccess(): void + { + $this->printProgress('.'); + } + private function printProgressForSkipped(): void + { + $this->printProgressWithColor('fg-cyan, bold', 'S'); + } + private function printProgressForIncomplete(): void + { + $this->printProgressWithColor('fg-yellow, bold', 'I'); + } + private function printProgressForNotice(): void + { + $this->printProgressWithColor('fg-yellow, bold', 'N'); + } + private function printProgressForDeprecation(): void + { + $this->printProgressWithColor('fg-yellow, bold', 'D'); + } + private function printProgressForRisky(): void + { + $this->printProgressWithColor('fg-yellow, bold', 'R'); + } + private function printProgressForWarning(): void + { + $this->printProgressWithColor('fg-yellow, bold', 'W'); + } + private function printProgressForFailure(): void + { + $this->printProgressWithColor('bg-red, fg-white', 'F'); + } + private function printProgressForError(): void + { + $this->printProgressWithColor('fg-red, bold', 'E'); + } + private function printProgressWithColor(string $color, string $progress): void + { + if ($this->colors) { + $progress = Color::colorizeTextBox($color, $progress); + } + $this->printProgress($progress); + } + private function printProgress(string $progress): void + { + $this->printer->print($progress); + $this->column++; + $this->numberOfTestsRun++; + if ($this->column === $this->maxColumn || $this->numberOfTestsRun === $this->numberOfTests) { + if ($this->numberOfTestsRun === $this->numberOfTests) { + $this->printer->print(str_repeat(' ', $this->maxColumn - $this->column)); + } + $this->printer->print(sprintf(' %' . $this->numberOfTestsWidth . 'd / %' . $this->numberOfTestsWidth . 'd (%3s%%)', $this->numberOfTestsRun, $this->numberOfTests, floor($this->numberOfTestsRun / $this->numberOfTests * 100))); + if ($this->column === $this->maxColumn) { + $this->column = 0; + $this->printer->print("\n"); } } } @@ -99691,1732 +91208,1219 @@ class SplObjectStorageComparator extends Comparator declare (strict_types=1); /* - * This file is part of sebastian/comparator. + * This file is part of PHPUnit. * * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnitPHAR\SebastianBergmann\Comparator; +namespace PHPUnit\TextUI\Output\Default\ProgressPrinter; -use function gettype; -use function sprintf; +use PHPUnit\Event\Test\BeforeFirstTestMethodErrored; +use PHPUnit\Event\Test\BeforeFirstTestMethodErroredSubscriber; /** - * Compares values for type equality. + * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -class TypeComparator extends Comparator +final class BeforeTestClassMethodErroredSubscriber extends \PHPUnit\TextUI\Output\Default\ProgressPrinter\Subscriber implements BeforeFirstTestMethodErroredSubscriber { - /** - * 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) + public function notify(BeforeFirstTestMethodErrored $event): void { - 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)) - ); - } + $this->printer()->beforeTestClassMethodErrored(); } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnitPHAR\SebastianBergmann\Comparator; +namespace PHPUnit\TextUI\Output\Default\ProgressPrinter; -use Throwable; -interface Exception extends Throwable +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +abstract class Subscriber { + private readonly \PHPUnit\TextUI\Output\Default\ProgressPrinter\ProgressPrinter $printer; + public function __construct(\PHPUnit\TextUI\Output\Default\ProgressPrinter\ProgressPrinter $printer) + { + $this->printer = $printer; + } + protected function printer(): \PHPUnit\TextUI\Output\Default\ProgressPrinter\ProgressPrinter + { + return $this->printer; + } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnitPHAR\SebastianBergmann\Comparator; +namespace PHPUnit\TextUI\Output\Default\ProgressPrinter; -final class RuntimeException extends \RuntimeException implements Exception +use PHPUnit\Event\Test\ConsideredRisky; +use PHPUnit\Event\Test\ConsideredRiskySubscriber; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class TestConsideredRiskySubscriber extends \PHPUnit\TextUI\Output\Default\ProgressPrinter\Subscriber implements ConsideredRiskySubscriber { + public function notify(ConsideredRisky $event): void + { + $this->printer()->testConsideredRisky(); + } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnitPHAR\SebastianBergmann\Complexity; +namespace PHPUnit\TextUI\Output\Default\ProgressPrinter; -use PHPUnitPHAR\PhpParser\Error; -use PHPUnitPHAR\PhpParser\Node; -use PHPUnitPHAR\PhpParser\NodeTraverser; -use PHPUnitPHAR\PhpParser\NodeVisitor\NameResolver; -use PHPUnitPHAR\PhpParser\NodeVisitor\ParentConnectingVisitor; -use PHPUnitPHAR\PhpParser\ParserFactory; -final class Calculator +use PHPUnit\Event\Test\Errored; +use PHPUnit\Event\Test\ErroredSubscriber; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class TestErroredSubscriber extends \PHPUnit\TextUI\Output\Default\ProgressPrinter\Subscriber implements ErroredSubscriber { - /** - * @throws RuntimeException - */ - public function calculateForSourceFile(string $sourceFile) : ComplexityCollection - { - return $this->calculateForSourceString(\file_get_contents($sourceFile)); - } - /** - * @throws RuntimeException - */ - public function calculateForSourceString(string $source) : ComplexityCollection - { - try { - $nodes = (new ParserFactory())->createForHostVersion()->parse($source); - \assert($nodes !== null); - return $this->calculateForAbstractSyntaxTree($nodes); - // @codeCoverageIgnoreStart - } catch (Error $error) { - throw new RuntimeException($error->getMessage(), (int) $error->getCode(), $error); - } - // @codeCoverageIgnoreEnd - } - /** - * @param Node[] $nodes - * - * @throws RuntimeException - */ - public function calculateForAbstractSyntaxTree(array $nodes) : ComplexityCollection + public function notify(Errored $event): void { - $traverser = new NodeTraverser(); - $complexityCalculatingVisitor = new ComplexityCalculatingVisitor(\true); - $traverser->addVisitor(new NameResolver()); - $traverser->addVisitor(new ParentConnectingVisitor()); - $traverser->addVisitor($complexityCalculatingVisitor); - try { - /* @noinspection UnusedFunctionResultInspection */ - $traverser->traverse($nodes); - // @codeCoverageIgnoreStart - } catch (Error $error) { - throw new RuntimeException($error->getMessage(), (int) $error->getCode(), $error); - } - // @codeCoverageIgnoreEnd - return $complexityCalculatingVisitor->result(); + $this->printer()->testErrored($event); } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnitPHAR\SebastianBergmann\Complexity; - -/** - * @psalm-immutable - */ -final class Complexity -{ - /** - * @var string - */ - private $name; - /** - * @var int - */ - private $cyclomaticComplexity; - public function __construct(string $name, int $cyclomaticComplexity) - { - $this->name = $name; - $this->cyclomaticComplexity = $cyclomaticComplexity; - } - public function name() : string - { - return $this->name; - } - public function cyclomaticComplexity() : int +namespace PHPUnit\TextUI\Output\Default\ProgressPrinter; + +use PHPUnit\Event\Test\Failed; +use PHPUnit\Event\Test\FailedSubscriber; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class TestFailedSubscriber extends \PHPUnit\TextUI\Output\Default\ProgressPrinter\Subscriber implements FailedSubscriber +{ + public function notify(Failed $event): void { - return $this->cyclomaticComplexity; + $this->printer()->testFailed(); } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnitPHAR\SebastianBergmann\Complexity; +namespace PHPUnit\TextUI\Output\Default\ProgressPrinter; -use function count; -use Countable; -use IteratorAggregate; +use PHPUnit\Event\Test\Finished; +use PHPUnit\Event\Test\FinishedSubscriber; /** - * @psalm-immutable + * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class ComplexityCollection implements Countable, IteratorAggregate +final class TestFinishedSubscriber extends \PHPUnit\TextUI\Output\Default\ProgressPrinter\Subscriber implements FinishedSubscriber { - /** - * @psalm-var list - */ - private $items = []; - public static function fromList(Complexity ...$items) : self - { - return new self($items); - } - /** - * @psalm-param list $items - */ - private function __construct(array $items) - { - $this->items = $items; - } - /** - * @psalm-return list - */ - public function asArray() : array - { - return $this->items; - } - public function getIterator() : ComplexityCollectionIterator - { - return new ComplexityCollectionIterator($this); - } - public function count() : int - { - return count($this->items); - } - public function isEmpty() : bool + public function notify(Finished $event): void { - return empty($this->items); - } - public function cyclomaticComplexity() : int - { - $cyclomaticComplexity = 0; - foreach ($this as $item) { - $cyclomaticComplexity += $item->cyclomaticComplexity(); - } - return $cyclomaticComplexity; + $this->printer()->testFinished(); } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnitPHAR\SebastianBergmann\Complexity; +namespace PHPUnit\TextUI\Output\Default\ProgressPrinter; -use Iterator; -final class ComplexityCollectionIterator implements Iterator +use PHPUnit\Event\Test\MarkedIncomplete; +use PHPUnit\Event\Test\MarkedIncompleteSubscriber; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class TestMarkedIncompleteSubscriber extends \PHPUnit\TextUI\Output\Default\ProgressPrinter\Subscriber implements MarkedIncompleteSubscriber { - /** - * @psalm-var list - */ - private $items; - /** - * @var int - */ - private $position = 0; - public function __construct(ComplexityCollection $items) + public function notify(MarkedIncomplete $event): void { - $this->items = $items->asArray(); - } - public function rewind() : void - { - $this->position = 0; - } - public function valid() : bool - { - return isset($this->items[$this->position]); - } - public function key() : int - { - return $this->position; - } - public function current() : Complexity - { - return $this->items[$this->position]; - } - public function next() : void - { - $this->position++; + $this->printer()->testMarkedIncomplete(); } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnitPHAR\SebastianBergmann\Complexity; +namespace PHPUnit\TextUI\Output\Default\ProgressPrinter; -use Throwable; -interface Exception extends Throwable +use PHPUnit\Event\Test\Prepared; +use PHPUnit\Event\Test\PreparedSubscriber; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class TestPreparedSubscriber extends \PHPUnit\TextUI\Output\Default\ProgressPrinter\Subscriber implements PreparedSubscriber { + public function notify(Prepared $event): void + { + $this->printer()->testPrepared(); + } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnitPHAR\SebastianBergmann\Complexity; +namespace PHPUnit\TextUI\Output\Default\ProgressPrinter; -final class RuntimeException extends \RuntimeException implements Exception +use PHPUnit\Event\TestRunner\ExecutionStarted; +use PHPUnit\Event\TestRunner\ExecutionStartedSubscriber; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class TestRunnerExecutionStartedSubscriber extends \PHPUnit\TextUI\Output\Default\ProgressPrinter\Subscriber implements ExecutionStartedSubscriber { + public function notify(ExecutionStarted $event): void + { + $this->printer()->testRunnerExecutionStarted($event); + } } -sebastian/complexity - -Copyright (c) 2020, Sebastian Bergmann . -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the - distribution. - - * Neither the name of Sebastian Bergmann nor the names of his - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN -ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnitPHAR\SebastianBergmann\Complexity; +namespace PHPUnit\TextUI\Output\Default\ProgressPrinter; -use function assert; -use function is_array; -use PHPUnitPHAR\PhpParser\Node; -use PHPUnitPHAR\PhpParser\Node\Name; -use PHPUnitPHAR\PhpParser\Node\Stmt; -use PHPUnitPHAR\PhpParser\Node\Stmt\Class_; -use PHPUnitPHAR\PhpParser\Node\Stmt\ClassMethod; -use PHPUnitPHAR\PhpParser\Node\Stmt\Function_; -use PHPUnitPHAR\PhpParser\Node\Stmt\Trait_; -use PHPUnitPHAR\PhpParser\NodeTraverser; -use PHPUnitPHAR\PhpParser\NodeVisitorAbstract; -final class ComplexityCalculatingVisitor extends NodeVisitorAbstract +use PHPUnit\Event\Test\Skipped; +use PHPUnit\Event\Test\SkippedSubscriber; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class TestSkippedSubscriber extends \PHPUnit\TextUI\Output\Default\ProgressPrinter\Subscriber implements SkippedSubscriber { - /** - * @psalm-var list - */ - private $result = []; - /** - * @var bool - */ - private $shortCircuitTraversal; - public function __construct(bool $shortCircuitTraversal) - { - $this->shortCircuitTraversal = $shortCircuitTraversal; - } - public function enterNode(Node $node) : ?int - { - if (!$node instanceof ClassMethod && !$node instanceof Function_) { - return null; - } - if ($node instanceof ClassMethod) { - $name = $this->classMethodName($node); - } else { - $name = $this->functionName($node); - } - $statements = $node->getStmts(); - assert(is_array($statements)); - $this->result[] = new Complexity($name, $this->cyclomaticComplexity($statements)); - if ($this->shortCircuitTraversal) { - return NodeTraverser::DONT_TRAVERSE_CHILDREN; - } - return null; - } - public function result() : ComplexityCollection - { - return ComplexityCollection::fromList(...$this->result); - } - /** - * @param Stmt[] $statements - */ - private function cyclomaticComplexity(array $statements) : int - { - $traverser = new NodeTraverser(); - $cyclomaticComplexityCalculatingVisitor = new CyclomaticComplexityCalculatingVisitor(); - $traverser->addVisitor($cyclomaticComplexityCalculatingVisitor); - /* @noinspection UnusedFunctionResultInspection */ - $traverser->traverse($statements); - return $cyclomaticComplexityCalculatingVisitor->cyclomaticComplexity(); - } - private function classMethodName(ClassMethod $node) : string - { - $parent = $node->getAttribute('parent'); - assert($parent instanceof Class_ || $parent instanceof Trait_); - assert(isset($parent->namespacedName)); - assert($parent->namespacedName instanceof Name); - return $parent->namespacedName->toString() . '::' . $node->name->toString(); - } - private function functionName(Function_ $node) : string + public function notify(Skipped $event): void { - assert(isset($node->namespacedName)); - assert($node->namespacedName instanceof Name); - return $node->namespacedName->toString(); + $this->printer()->testSkipped(); } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnitPHAR\SebastianBergmann\Complexity; +namespace PHPUnit\TextUI\Output\Default\ProgressPrinter; -use function get_class; -use PHPUnitPHAR\PhpParser\Node; -use PHPUnitPHAR\PhpParser\Node\Expr\BinaryOp\BooleanAnd; -use PHPUnitPHAR\PhpParser\Node\Expr\BinaryOp\BooleanOr; -use PHPUnitPHAR\PhpParser\Node\Expr\BinaryOp\LogicalAnd; -use PHPUnitPHAR\PhpParser\Node\Expr\BinaryOp\LogicalOr; -use PHPUnitPHAR\PhpParser\Node\Expr\Ternary; -use PHPUnitPHAR\PhpParser\Node\Stmt\Case_; -use PHPUnitPHAR\PhpParser\Node\Stmt\Catch_; -use PHPUnitPHAR\PhpParser\Node\Stmt\ElseIf_; -use PHPUnitPHAR\PhpParser\Node\Stmt\For_; -use PHPUnitPHAR\PhpParser\Node\Stmt\Foreach_; -use PHPUnitPHAR\PhpParser\Node\Stmt\If_; -use PHPUnitPHAR\PhpParser\Node\Stmt\While_; -use PHPUnitPHAR\PhpParser\NodeVisitorAbstract; -final class CyclomaticComplexityCalculatingVisitor extends NodeVisitorAbstract +use PHPUnit\Event\Test\DeprecationTriggered; +use PHPUnit\Event\Test\DeprecationTriggeredSubscriber; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class TestTriggeredDeprecationSubscriber extends \PHPUnit\TextUI\Output\Default\ProgressPrinter\Subscriber implements DeprecationTriggeredSubscriber { - /** - * @var int - */ - private $cyclomaticComplexity = 1; - public function enterNode(Node $node) : void - { - /* @noinspection GetClassMissUseInspection */ - switch (get_class($node)) { - case BooleanAnd::class: - case BooleanOr::class: - case Case_::class: - case Catch_::class: - case ElseIf_::class: - case For_::class: - case Foreach_::class: - case If_::class: - case LogicalAnd::class: - case LogicalOr::class: - case Ternary::class: - case While_::class: - $this->cyclomaticComplexity++; - } - } - public function cyclomaticComplexity() : int + public function notify(DeprecationTriggered $event): void { - return $this->cyclomaticComplexity; + $this->printer()->testTriggeredDeprecation($event); } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnitPHAR\SebastianBergmann\Diff; +namespace PHPUnit\TextUI\Output\Default\ProgressPrinter; -final class Chunk +use PHPUnit\Event\Test\ErrorTriggered; +use PHPUnit\Event\Test\ErrorTriggeredSubscriber; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class TestTriggeredErrorSubscriber extends \PHPUnit\TextUI\Output\Default\ProgressPrinter\Subscriber implements ErrorTriggeredSubscriber { - /** - * @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 + public function notify(ErrorTriggered $event): void { - 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; + $this->printer()->testTriggeredError($event); } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnitPHAR\SebastianBergmann\Diff; +namespace PHPUnit\TextUI\Output\Default\ProgressPrinter; -final class Diff +use PHPUnit\Event\Test\NoticeTriggered; +use PHPUnit\Event\Test\NoticeTriggeredSubscriber; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class TestTriggeredNoticeSubscriber extends \PHPUnit\TextUI\Output\Default\ProgressPrinter\Subscriber implements NoticeTriggeredSubscriber { - /** - * @var string - */ - private $from; - /** - * @var string - */ - private $to; - /** - * @var Chunk[] - */ - private $chunks; - /** - * @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 + public function notify(NoticeTriggered $event): void { - $this->chunks = $chunks; + $this->printer()->testTriggeredNotice($event); } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnitPHAR\SebastianBergmann\Diff; +namespace PHPUnit\TextUI\Output\Default\ProgressPrinter; -use const PHP_INT_SIZE; -use const PREG_SPLIT_DELIM_CAPTURE; -use const PREG_SPLIT_NO_EMPTY; -use function array_shift; -use function array_unshift; -use function array_values; -use function count; -use function current; -use function end; -use function get_class; -use function gettype; -use function is_array; -use function is_object; -use function is_string; -use function key; -use function min; -use function preg_split; -use function prev; -use function reset; -use function sprintf; -use function substr; -use PHPUnitPHAR\SebastianBergmann\Diff\Output\DiffOutputBuilderInterface; -use PHPUnitPHAR\SebastianBergmann\Diff\Output\UnifiedDiffOutputBuilder; -final class Differ +use PHPUnit\Event\Test\PhpDeprecationTriggered; +use PHPUnit\Event\Test\PhpDeprecationTriggeredSubscriber; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class TestTriggeredPhpDeprecationSubscriber extends \PHPUnit\TextUI\Output\Default\ProgressPrinter\Subscriber implements PhpDeprecationTriggeredSubscriber { - public const OLD = 0; - public const ADDED = 1; - public const REMOVED = 2; - public const DIFF_LINE_END_WARNING = 3; - public const NO_LINE_END_EOF_WARNING = 4; - /** - * @var DiffOutputBuilderInterface - */ - private $outputBuilder; - /** - * @param DiffOutputBuilderInterface $outputBuilder - * - * @throws InvalidArgumentException - */ - public function __construct($outputBuilder = null) - { - if ($outputBuilder instanceof DiffOutputBuilderInterface) { - $this->outputBuilder = $outputBuilder; - } elseif (null === $outputBuilder) { - $this->outputBuilder = new UnifiedDiffOutputBuilder(); - } elseif (is_string($outputBuilder)) { - // PHPUnit 6.1.4, 6.2.0, 6.2.1, 6.2.2, and 6.2.3 support - // @see https://github.com/sebastianbergmann/phpunit/issues/2734#issuecomment-314514056 - // @deprecated - $this->outputBuilder = new UnifiedDiffOutputBuilder($outputBuilder); - } else { - throw new InvalidArgumentException(sprintf('Expected builder to be an instance of DiffOutputBuilderInterface, or a string, got %s.', is_object($outputBuilder) ? 'instance of "' . get_class($outputBuilder) . '"' : gettype($outputBuilder) . ' "' . $outputBuilder . '"')); - } - } - /** - * Returns the diff between two arrays or strings as string. - * - * @param array|string $from - * @param array|string $to - */ - 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 - */ - 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. - * - * @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. - */ - private function splitStringByLines(string $input) : array - { - return preg_split('/(.*\\R)/', $input, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY); - } - 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. - * - * @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. - */ - 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 + public function notify(PhpDeprecationTriggered $event): void { - $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]; + $this->printer()->testTriggeredPhpDeprecation($event); } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnitPHAR\SebastianBergmann\Diff; +namespace PHPUnit\TextUI\Output\Default\ProgressPrinter; -use function get_class; -use function gettype; -use function is_object; -use function sprintf; -use Exception; -final class ConfigurationException extends InvalidArgumentException +use PHPUnit\Event\Test\PhpNoticeTriggered; +use PHPUnit\Event\Test\PhpNoticeTriggeredSubscriber; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class TestTriggeredPhpNoticeSubscriber extends \PHPUnit\TextUI\Output\Default\ProgressPrinter\Subscriber implements PhpNoticeTriggeredSubscriber { - public function __construct(string $option, string $expected, $value, int $code = 0, ?Exception $previous = null) + public function notify(PhpNoticeTriggered $event): void { - parent::__construct(sprintf('Option "%s" must be %s, got "%s".', $option, $expected, is_object($value) ? get_class($value) : (null === $value ? '' : gettype($value) . '#' . $value)), $code, $previous); + $this->printer()->testTriggeredPhpNotice($event); } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnitPHAR\SebastianBergmann\Diff; +namespace PHPUnit\TextUI\Output\Default\ProgressPrinter; -use Throwable; -interface Exception extends Throwable +use PHPUnit\Event\Test\PhpWarningTriggered; +use PHPUnit\Event\Test\PhpWarningTriggeredSubscriber; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class TestTriggeredPhpWarningSubscriber extends \PHPUnit\TextUI\Output\Default\ProgressPrinter\Subscriber implements PhpWarningTriggeredSubscriber { + public function notify(PhpWarningTriggered $event): void + { + $this->printer()->testTriggeredPhpWarning($event); + } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnitPHAR\SebastianBergmann\Diff; +namespace PHPUnit\TextUI\Output\Default\ProgressPrinter; -class InvalidArgumentException extends \InvalidArgumentException implements Exception +use PHPUnit\Event\Test\PhpunitDeprecationTriggered; +use PHPUnit\Event\Test\PhpunitDeprecationTriggeredSubscriber; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class TestTriggeredPhpunitDeprecationSubscriber extends \PHPUnit\TextUI\Output\Default\ProgressPrinter\Subscriber implements PhpunitDeprecationTriggeredSubscriber { + public function notify(PhpunitDeprecationTriggered $event): void + { + $this->printer()->testTriggeredPhpunitDeprecation(); + } } -sebastian/diff - -Copyright (c) 2002-2020, Sebastian Bergmann . -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the - distribution. - - * Neither the name of Sebastian Bergmann nor the names of his - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN -ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnitPHAR\SebastianBergmann\Diff; +namespace PHPUnit\TextUI\Output\Default\ProgressPrinter; -final class Line +use PHPUnit\Event\Test\PhpunitWarningTriggered; +use PHPUnit\Event\Test\PhpunitWarningTriggeredSubscriber; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class TestTriggeredPhpunitWarningSubscriber extends \PHPUnit\TextUI\Output\Default\ProgressPrinter\Subscriber implements PhpunitWarningTriggeredSubscriber { - 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 + public function notify(PhpunitWarningTriggered $event): void { - return $this->content; - } - public function getType() : int - { - return $this->type; + $this->printer()->testTriggeredPhpunitWarning(); } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnitPHAR\SebastianBergmann\Diff; +namespace PHPUnit\TextUI\Output\Default\ProgressPrinter; -interface LongestCommonSubsequenceCalculator +use PHPUnit\Event\Test\WarningTriggered; +use PHPUnit\Event\Test\WarningTriggeredSubscriber; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class TestTriggeredWarningSubscriber extends \PHPUnit\TextUI\Output\Default\ProgressPrinter\Subscriber implements WarningTriggeredSubscriber { - /** - * Calculates the longest common subsequence of two arrays. - */ - public function calculate(array $from, array $to) : array; + public function notify(WarningTriggered $event): void + { + $this->printer()->testTriggeredWarning($event); + } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnitPHAR\SebastianBergmann\Diff; +namespace PHPUnit\TextUI\Output\Default; -use function array_fill; +use const PHP_EOL; +use function array_keys; use function array_merge; use function array_reverse; -use function array_slice; +use function array_unique; +use function assert; use function count; -use function in_array; -use function max; -final class MemoryEfficientLongestCommonSubsequenceCalculator implements LongestCommonSubsequenceCalculator -{ - /** - * {@inheritdoc} - */ - public function calculate(array $from, array $to) : array +use function explode; +use function ksort; +use function range; +use function sprintf; +use function str_starts_with; +use function strlen; +use function substr; +use function trim; +use PHPUnit\Event\Code\Test; +use PHPUnit\Event\Code\TestMethod; +use PHPUnit\Event\Test\BeforeFirstTestMethodErrored; +use PHPUnit\Event\Test\ConsideredRisky; +use PHPUnit\Event\Test\DeprecationTriggered; +use PHPUnit\Event\Test\ErrorTriggered; +use PHPUnit\Event\Test\NoticeTriggered; +use PHPUnit\Event\Test\PhpDeprecationTriggered; +use PHPUnit\Event\Test\PhpNoticeTriggered; +use PHPUnit\Event\Test\PhpunitDeprecationTriggered; +use PHPUnit\Event\Test\PhpunitErrorTriggered; +use PHPUnit\Event\Test\PhpunitWarningTriggered; +use PHPUnit\Event\Test\PhpWarningTriggered; +use PHPUnit\Event\Test\WarningTriggered; +use PHPUnit\TestRunner\TestResult\Issues\Issue; +use PHPUnit\TestRunner\TestResult\TestResult; +use PHPUnit\TextUI\Output\Printer; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class ResultPrinter +{ + private readonly Printer $printer; + private readonly bool $displayPhpunitErrors; + private readonly bool $displayPhpunitWarnings; + private readonly bool $displayTestsWithErrors; + private readonly bool $displayTestsWithFailedAssertions; + private readonly bool $displayRiskyTests; + private readonly bool $displayPhpunitDeprecations; + private readonly bool $displayDetailsOnIncompleteTests; + private readonly bool $displayDetailsOnSkippedTests; + private readonly bool $displayDetailsOnTestsThatTriggerDeprecations; + private readonly bool $displayDetailsOnTestsThatTriggerErrors; + private readonly bool $displayDetailsOnTestsThatTriggerNotices; + private readonly bool $displayDetailsOnTestsThatTriggerWarnings; + private readonly bool $displayDefectsInReverseOrder; + private bool $listPrinted = \false; + public function __construct(Printer $printer, bool $displayPhpunitErrors, bool $displayPhpunitWarnings, bool $displayPhpunitDeprecations, bool $displayTestsWithErrors, bool $displayTestsWithFailedAssertions, bool $displayRiskyTests, bool $displayDetailsOnIncompleteTests, bool $displayDetailsOnSkippedTests, bool $displayDetailsOnTestsThatTriggerDeprecations, bool $displayDetailsOnTestsThatTriggerErrors, bool $displayDetailsOnTestsThatTriggerNotices, bool $displayDetailsOnTestsThatTriggerWarnings, bool $displayDefectsInReverseOrder) { - $cFrom = count($from); - $cTo = count($to); - if ($cFrom === 0) { - return []; + $this->printer = $printer; + $this->displayPhpunitErrors = $displayPhpunitErrors; + $this->displayPhpunitWarnings = $displayPhpunitWarnings; + $this->displayPhpunitDeprecations = $displayPhpunitDeprecations; + $this->displayTestsWithErrors = $displayTestsWithErrors; + $this->displayTestsWithFailedAssertions = $displayTestsWithFailedAssertions; + $this->displayRiskyTests = $displayRiskyTests; + $this->displayDetailsOnIncompleteTests = $displayDetailsOnIncompleteTests; + $this->displayDetailsOnSkippedTests = $displayDetailsOnSkippedTests; + $this->displayDetailsOnTestsThatTriggerDeprecations = $displayDetailsOnTestsThatTriggerDeprecations; + $this->displayDetailsOnTestsThatTriggerErrors = $displayDetailsOnTestsThatTriggerErrors; + $this->displayDetailsOnTestsThatTriggerNotices = $displayDetailsOnTestsThatTriggerNotices; + $this->displayDetailsOnTestsThatTriggerWarnings = $displayDetailsOnTestsThatTriggerWarnings; + $this->displayDefectsInReverseOrder = $displayDefectsInReverseOrder; + } + public function print(TestResult $result): void + { + if ($this->displayPhpunitErrors) { + $this->printPhpunitErrors($result); } - if ($cFrom === 1) { - if (in_array($from[0], $to, \true)) { - return [$from[0]]; - } - return []; + if ($this->displayPhpunitWarnings) { + $this->printTestRunnerWarnings($result); } - $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; + if ($this->displayPhpunitDeprecations) { + $this->printTestRunnerDeprecations($result); + } + if ($this->displayTestsWithErrors) { + $this->printTestsWithErrors($result); + } + if ($this->displayTestsWithFailedAssertions) { + $this->printTestsWithFailedAssertions($result); + } + if ($this->displayPhpunitWarnings) { + $this->printDetailsOnTestsThatTriggeredPhpunitWarnings($result); + } + if ($this->displayPhpunitDeprecations) { + $this->printDetailsOnTestsThatTriggeredPhpunitDeprecations($result); + } + if ($this->displayRiskyTests) { + $this->printRiskyTests($result); + } + if ($this->displayDetailsOnIncompleteTests) { + $this->printIncompleteTests($result); + } + if ($this->displayDetailsOnSkippedTests) { + $this->printSkippedTestSuites($result); + $this->printSkippedTests($result); + } + if ($this->displayDetailsOnTestsThatTriggerErrors) { + $this->printIssueList('error', $result->errors()); + } + if ($this->displayDetailsOnTestsThatTriggerWarnings) { + $this->printIssueList('PHP warning', $result->phpWarnings()); + $this->printIssueList('warning', $result->warnings()); + } + if ($this->displayDetailsOnTestsThatTriggerNotices) { + $this->printIssueList('PHP notice', $result->phpNotices()); + $this->printIssueList('notice', $result->notices()); + } + if ($this->displayDetailsOnTestsThatTriggerDeprecations) { + $this->printIssueList('PHP deprecation', $result->phpDeprecations()); + $this->printIssueList('deprecation', $result->deprecations()); + } + } + public function flush(): void + { + $this->printer->flush(); + } + private function printPhpunitErrors(TestResult $result): void + { + if (!$result->hasTestTriggeredPhpunitErrorEvents()) { + return; + } + $elements = $this->mapTestsWithIssuesEventsToElements($result->testTriggeredPhpunitErrorEvents()); + $this->printListHeaderWithNumber($elements['numberOfTestsWithIssues'], 'PHPUnit error'); + $this->printList($elements['elements']); + } + private function printDetailsOnTestsThatTriggeredPhpunitDeprecations(TestResult $result): void + { + if (!$result->hasTestTriggeredPhpunitDeprecationEvents()) { + return; + } + $elements = $this->mapTestsWithIssuesEventsToElements($result->testTriggeredPhpunitDeprecationEvents()); + $this->printListHeaderWithNumberOfTestsAndNumberOfIssues($elements['numberOfTestsWithIssues'], $elements['numberOfIssues'], 'PHPUnit deprecation'); + $this->printList($elements['elements']); + } + private function printTestRunnerWarnings(TestResult $result): void + { + if (!$result->hasTestRunnerTriggeredWarningEvents()) { + return; + } + $elements = []; + foreach ($result->testRunnerTriggeredWarningEvents() as $event) { + $elements[] = ['title' => $event->message(), 'body' => '']; + } + $this->printListHeaderWithNumber(count($elements), 'PHPUnit test runner warning'); + $this->printList($elements); + } + private function printTestRunnerDeprecations(TestResult $result): void + { + if (!$result->hasTestRunnerTriggeredDeprecationEvents()) { + return; + } + $elements = []; + foreach ($result->testRunnerTriggeredDeprecationEvents() as $event) { + $elements[] = ['title' => $event->message(), 'body' => '']; + } + $this->printListHeaderWithNumber(count($elements), 'PHPUnit test runner deprecation'); + $this->printList($elements); + } + private function printDetailsOnTestsThatTriggeredPhpunitWarnings(TestResult $result): void + { + if (!$result->hasTestTriggeredPhpunitWarningEvents()) { + return; + } + $elements = $this->mapTestsWithIssuesEventsToElements($result->testTriggeredPhpunitWarningEvents()); + $this->printListHeaderWithNumberOfTestsAndNumberOfIssues($elements['numberOfTestsWithIssues'], $elements['numberOfIssues'], 'PHPUnit warning'); + $this->printList($elements['elements']); + } + private function printTestsWithErrors(TestResult $result): void + { + if (!$result->hasTestErroredEvents()) { + return; + } + $elements = []; + foreach ($result->testErroredEvents() as $event) { + if ($event instanceof BeforeFirstTestMethodErrored) { + $title = $event->testClassName(); + } else { + $title = $this->name($event->test()); } + $elements[] = ['title' => $title, 'body' => $event->throwable()->asString()]; } - $toStart = array_slice($to, 0, $jMax); - $toEnd = array_slice($to, $jMax); - return array_merge($this->calculate($fromStart, $toStart), $this->calculate($fromEnd, $toEnd)); + $this->printListHeaderWithNumber(count($elements), 'error'); + $this->printList($elements); } - private function length(array $from, array $to) : array + private function printTestsWithFailedAssertions(TestResult $result): void { - $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 { - // don't use max() to avoid function call overhead - if ($current[$j] > $prev[$j + 1]) { - $current[$j + 1] = $current[$j]; - } else { - $current[$j + 1] = $prev[$j + 1]; - } - } + if (!$result->hasTestFailedEvents()) { + return; + } + $elements = []; + foreach ($result->testFailedEvents() as $event) { + $body = $event->throwable()->asString(); + if (str_starts_with($body, 'AssertionError: ')) { + $body = substr($body, strlen('AssertionError: ')); } + $elements[] = ['title' => $this->name($event->test()), 'body' => $body]; } - return $current; + $this->printListHeaderWithNumber(count($elements), 'failure'); + $this->printList($elements); + } + private function printRiskyTests(TestResult $result): void + { + if (!$result->hasTestConsideredRiskyEvents()) { + return; + } + $elements = $this->mapTestsWithIssuesEventsToElements($result->testConsideredRiskyEvents()); + $this->printListHeaderWithNumber($elements['numberOfTestsWithIssues'], 'risky test'); + $this->printList($elements['elements']); + } + private function printIncompleteTests(TestResult $result): void + { + if (!$result->hasTestMarkedIncompleteEvents()) { + return; + } + $elements = []; + foreach ($result->testMarkedIncompleteEvents() as $event) { + $elements[] = ['title' => $this->name($event->test()), 'body' => $event->throwable()->asString()]; + } + $this->printListHeaderWithNumber(count($elements), 'incomplete test'); + $this->printList($elements); + } + private function printSkippedTestSuites(TestResult $result): void + { + if (!$result->hasTestSuiteSkippedEvents()) { + return; + } + $elements = []; + foreach ($result->testSuiteSkippedEvents() as $event) { + $elements[] = ['title' => $event->testSuite()->name(), 'body' => $event->message()]; + } + $this->printListHeaderWithNumber(count($elements), 'skipped test suite'); + $this->printList($elements); + } + private function printSkippedTests(TestResult $result): void + { + if (!$result->hasTestSkippedEvents()) { + return; + } + $elements = []; + foreach ($result->testSkippedEvents() as $event) { + $elements[] = ['title' => $this->name($event->test()), 'body' => $event->message()]; + } + $this->printListHeaderWithNumber(count($elements), 'skipped test'); + $this->printList($elements); } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnitPHAR\SebastianBergmann\Diff\Output; - -use function count; -abstract class AbstractChunkOutputBuilder implements DiffOutputBuilderInterface -{ /** - * Takes input of the diff array and returns the common parts. - * Iterates through diff line by line. + * @psalm-param non-empty-string $type + * @psalm-param list $issues */ - protected function getCommonChunks(array $diff, int $lineThreshold = 5) : array + private function printIssueList(string $type, array $issues): void { - $diffSize = count($diff); - $capturing = \false; - $chunkStart = 0; - $chunkSize = 0; - $commonChunks = []; - for ($i = 0; $i < $diffSize; ++$i) { - if ($diff[$i][1] === 0) { - if ($capturing === \false) { - $capturing = \true; - $chunkStart = $i; - $chunkSize = 0; - } else { - ++$chunkSize; + if (empty($issues)) { + return; + } + $numberOfUniqueIssues = count($issues); + $triggeringTests = []; + foreach ($issues as $issue) { + $triggeringTests = array_merge($triggeringTests, array_keys($issue->triggeringTests())); + } + $numberOfTests = count(array_unique($triggeringTests)); + unset($triggeringTests); + $this->printListHeader(sprintf('%d test%s triggered %d %s%s:' . PHP_EOL . PHP_EOL, $numberOfTests, ($numberOfTests !== 1) ? 's' : '', $numberOfUniqueIssues, $type, ($numberOfUniqueIssues !== 1) ? 's' : '')); + $i = 1; + foreach ($issues as $issue) { + $title = sprintf('%s:%d', $issue->file(), $issue->line()); + $body = trim($issue->description()) . PHP_EOL . PHP_EOL . 'Triggered by:'; + $triggeringTests = $issue->triggeringTests(); + ksort($triggeringTests); + foreach ($triggeringTests as $triggeringTest) { + $body .= PHP_EOL . PHP_EOL . '* ' . $triggeringTest['test']->id(); + if ($triggeringTest['count'] > 1) { + $body .= sprintf(' (%d times)', $triggeringTest['count']); } - } elseif ($capturing !== \false) { - if ($chunkSize >= $lineThreshold) { - $commonChunks[$chunkStart] = $chunkStart + $chunkSize; + if ($triggeringTest['test']->isTestMethod()) { + $body .= PHP_EOL . ' ' . $triggeringTest['test']->file() . ':' . $triggeringTest['test']->line(); } - $capturing = \false; } + $this->printIssueListElement($i++, $title, $body); + $this->printer->print(PHP_EOL); } - if ($capturing !== \false && $chunkSize >= $lineThreshold) { - $commonChunks[$chunkStart] = $chunkStart + $chunkSize; + } + private function printListHeaderWithNumberOfTestsAndNumberOfIssues(int $numberOfTestsWithIssues, int $numberOfIssues, string $type): void + { + $this->printListHeader(sprintf("%d test%s triggered %d %s%s:\n\n", $numberOfTestsWithIssues, ($numberOfTestsWithIssues !== 1) ? 's' : '', $numberOfIssues, $type, ($numberOfIssues !== 1) ? 's' : '')); + } + private function printListHeaderWithNumber(int $number, string $type): void + { + $this->printListHeader(sprintf("There %s %d %s%s:\n\n", ($number === 1) ? 'was' : 'were', $number, $type, ($number === 1) ? '' : 's')); + } + private function printListHeader(string $header): void + { + if ($this->listPrinted) { + $this->printer->print("--\n\n"); } - return $commonChunks; + $this->listPrinted = \true; + $this->printer->print($header); } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnitPHAR\SebastianBergmann\Diff\Output; - -use function fclose; -use function fopen; -use function fwrite; -use function stream_get_contents; -use function substr; -use PHPUnitPHAR\SebastianBergmann\Diff\Differ; -/** - * Builds a diff string representation in a loose unified diff format - * listing only changes lines. Does not include line numbers. - */ -final class DiffOnlyOutputBuilder implements DiffOutputBuilderInterface -{ /** - * @var string + * @psalm-param list $elements */ - private $header; - public function __construct(string $header = "--- Original\n+++ New\n") + private function printList(array $elements): void { - $this->header = $header; + $i = 1; + if ($this->displayDefectsInReverseOrder) { + $elements = array_reverse($elements); + } + foreach ($elements as $element) { + $this->printListElement($i++, $element['title'], $element['body']); + } + $this->printer->print("\n"); } - public function getDiff(array $diff) : string + private function printListElement(int $number, string $title, string $body): void { - $buffer = fopen('php://memory', 'r+b'); - if ('' !== $this->header) { - fwrite($buffer, $this->header); - if ("\n" !== substr($this->header, -1, 1)) { - fwrite($buffer, "\n"); + $body = trim($body); + $this->printer->print(sprintf("%s%d) %s\n%s%s", ($number > 1) ? "\n" : '', $number, $title, $body, (!empty($body)) ? "\n" : '')); + } + private function printIssueListElement(int $number, string $title, string $body): void + { + $body = trim($body); + $this->printer->print(sprintf("%d) %s\n%s%s", $number, $title, $body, (!empty($body)) ? "\n" : '')); + } + private function name(Test $test): string + { + if ($test->isTestMethod()) { + assert($test instanceof TestMethod); + if (!$test->testData()->hasDataFromDataProvider()) { + return $test->nameWithClass(); } + return $test->className() . '::' . $test->methodName() . $test->testData()->dataFromDataProvider()->dataAsStringForResultOutput(); } - foreach ($diff as $diffEntry) { - if ($diffEntry[1] === Differ::ADDED) { - fwrite($buffer, '+' . $diffEntry[0]); - } elseif ($diffEntry[1] === Differ::REMOVED) { - fwrite($buffer, '-' . $diffEntry[0]); - } elseif ($diffEntry[1] === Differ::DIFF_LINE_END_WARNING) { - fwrite($buffer, ' ' . $diffEntry[0]); - continue; - // Warnings should not be tested for line break, it will always be there - } else { - /* Not changed (old) 0 */ - continue; - // we didn't write the non changs line, so do not add a line break either + return $test->name(); + } + /** + * @psalm-param array> $events + * + * @psalm-return array{numberOfTestsWithIssues: int, numberOfIssues: int, elements: list} + */ + private function mapTestsWithIssuesEventsToElements(array $events): array + { + $elements = []; + $issues = 0; + foreach ($events as $reasons) { + $test = $reasons[0]->test(); + $testLocation = $this->testLocation($test); + $title = $this->name($test); + $body = ''; + $first = \true; + $single = count($reasons) === 1; + foreach ($reasons as $reason) { + if ($first) { + $first = \false; + } else { + $body .= PHP_EOL; + } + $body .= $this->reasonMessage($reason, $single); + $body .= $this->reasonLocation($reason, $single); + $issues++; } - $lc = substr($diffEntry[0], -1); - if ($lc !== "\n" && $lc !== "\r") { - fwrite($buffer, "\n"); - // \No newline at end of file + if (!empty($testLocation)) { + $body .= $testLocation; } + $elements[] = ['title' => $title, 'body' => $body]; } - $diff = stream_get_contents($buffer, -1, 0); - fclose($buffer); - return $diff; + return ['numberOfTestsWithIssues' => count($events), 'numberOfIssues' => $issues, 'elements' => $elements]; + } + private function testLocation(Test $test): string + { + if (!$test->isTestMethod()) { + return ''; + } + assert($test instanceof TestMethod); + return sprintf('%s%s:%d%s', PHP_EOL, $test->file(), $test->line(), PHP_EOL); + } + private function reasonMessage(ConsideredRisky|DeprecationTriggered|ErrorTriggered|NoticeTriggered|PhpDeprecationTriggered|PhpNoticeTriggered|PhpunitDeprecationTriggered|PhpunitErrorTriggered|PhpunitWarningTriggered|PhpWarningTriggered|WarningTriggered $reason, bool $single): string + { + $message = trim($reason->message()); + if ($single) { + return $message . PHP_EOL; + } + $lines = explode(PHP_EOL, $message); + $buffer = '* ' . $lines[0] . PHP_EOL; + if (count($lines) > 1) { + foreach (range(1, count($lines) - 1) as $line) { + $buffer .= ' ' . $lines[$line] . PHP_EOL; + } + } + return $buffer; + } + private function reasonLocation(ConsideredRisky|DeprecationTriggered|ErrorTriggered|NoticeTriggered|PhpDeprecationTriggered|PhpNoticeTriggered|PhpunitDeprecationTriggered|PhpunitErrorTriggered|PhpunitWarningTriggered|PhpWarningTriggered|WarningTriggered $reason, bool $single): string + { + if (!$reason instanceof DeprecationTriggered && !$reason instanceof PhpDeprecationTriggered && !$reason instanceof ErrorTriggered && !$reason instanceof NoticeTriggered && !$reason instanceof PhpNoticeTriggered && !$reason instanceof WarningTriggered && !$reason instanceof PhpWarningTriggered) { + return ''; + } + return sprintf('%s%s:%d%s', $single ? '' : ' ', $reason->file(), $reason->line(), PHP_EOL); } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnitPHAR\SebastianBergmann\Diff\Output; +namespace PHPUnit\TextUI\Output\Default; -/** - * Defines how an output builder should take a generated - * diff array and return a string representation of that diff. - */ -interface DiffOutputBuilderInterface +use PHPUnit\Event\EventFacadeIsSealedException; +use PHPUnit\Event\Facade; +use PHPUnit\Event\Test\PrintedUnexpectedOutput; +use PHPUnit\Event\Test\PrintedUnexpectedOutputSubscriber; +use PHPUnit\Event\UnknownSubscriberTypeException; +use PHPUnit\TextUI\Output\Printer; +final class UnexpectedOutputPrinter implements PrintedUnexpectedOutputSubscriber { - public function getDiff(array $diff) : string; + private readonly Printer $printer; + /** + * @throws EventFacadeIsSealedException + * @throws UnknownSubscriberTypeException + */ + public function __construct(Printer $printer, Facade $facade) + { + $this->printer = $printer; + $facade->registerSubscriber($this); + } + public function notify(PrintedUnexpectedOutput $event): void + { + $this->printer->print($event->output()); + } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnitPHAR\SebastianBergmann\Diff\Output; +namespace PHPUnit\TextUI\Output; -use function array_merge; -use function array_splice; -use function count; -use function fclose; -use function fopen; -use function fwrite; -use function is_bool; -use function is_int; -use function is_string; -use function max; -use function min; -use function sprintf; -use function stream_get_contents; -use function substr; -use PHPUnitPHAR\SebastianBergmann\Diff\ConfigurationException; -use PHPUnitPHAR\SebastianBergmann\Diff\Differ; +use function assert; +use PHPUnit\Event\EventFacadeIsSealedException; +use PHPUnit\Event\Facade as EventFacade; +use PHPUnit\Event\UnknownSubscriberTypeException; +use PHPUnit\Logging\TeamCity\TeamCityLogger; +use PHPUnit\Logging\TestDox\TestResultCollection; +use PHPUnit\Runner\DirectoryDoesNotExistException; +use PHPUnit\TestRunner\TestResult\TestResult; +use PHPUnit\TextUI\CannotOpenSocketException; +use PHPUnit\TextUI\Configuration\Configuration; +use PHPUnit\TextUI\InvalidSocketException; +use PHPUnit\TextUI\Output\Default\ProgressPrinter\ProgressPrinter as DefaultProgressPrinter; +use PHPUnit\TextUI\Output\Default\ResultPrinter as DefaultResultPrinter; +use PHPUnit\TextUI\Output\Default\UnexpectedOutputPrinter; +use PHPUnit\TextUI\Output\TestDox\ResultPrinter as TestDoxResultPrinter; +use PHPUnitPHAR\SebastianBergmann\Timer\Duration; +use PHPUnitPHAR\SebastianBergmann\Timer\ResourceUsageFormatter; /** - * Strict Unified diff output builder. - * - * Generates (strict) Unified diff's (unidiffs) with hunks. + * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class StrictUnifiedDiffOutputBuilder implements DiffOutputBuilderInterface +final class Facade { - private static $default = [ - 'collapseRanges' => \true, - // ranges of length one are rendered with the trailing `,1` - 'commonLineThreshold' => 6, - // number of same lines before ending a new hunk and creating a new one (if needed) - 'contextLines' => 3, - // like `diff: -u, -U NUM, --unified[=NUM]`, for patch/git apply compatibility best to keep at least @ 3 - 'fromFile' => null, - 'fromFileDate' => null, - 'toFile' => null, - 'toFileDate' => null, - ]; - /** - * @var bool - */ - private $changed; - /** - * @var bool - */ - private $collapseRanges; - /** - * @var int >= 0 - */ - private $commonLineThreshold; - /** - * @var string - */ - private $header; + private static ?\PHPUnit\TextUI\Output\Printer $printer = null; + private static ?DefaultResultPrinter $defaultResultPrinter = null; + private static ?TestDoxResultPrinter $testDoxResultPrinter = null; + private static ?\PHPUnit\TextUI\Output\SummaryPrinter $summaryPrinter = null; + private static bool $defaultProgressPrinter = \false; /** - * @var int >= 0 + * @throws EventFacadeIsSealedException + * @throws UnknownSubscriberTypeException */ - private $contextLines; - public function __construct(array $options = []) + public static function init(Configuration $configuration, bool $extensionReplacesProgressOutput, bool $extensionReplacesResultOutput): \PHPUnit\TextUI\Output\Printer { - $options = array_merge(self::$default, $options); - if (!is_bool($options['collapseRanges'])) { - throw new ConfigurationException('collapseRanges', 'a bool', $options['collapseRanges']); + self::createPrinter($configuration); + assert(self::$printer !== null); + if ($configuration->debug()) { + return self::$printer; } - if (!is_int($options['contextLines']) || $options['contextLines'] < 0) { - throw new ConfigurationException('contextLines', 'an int >= 0', $options['contextLines']); + self::createUnexpectedOutputPrinter(); + if (!$extensionReplacesProgressOutput) { + self::createProgressPrinter($configuration); } - if (!is_int($options['commonLineThreshold']) || $options['commonLineThreshold'] <= 0) { - throw new ConfigurationException('commonLineThreshold', 'an int > 0', $options['commonLineThreshold']); + if (!$extensionReplacesResultOutput) { + self::createResultPrinter($configuration); + self::createSummaryPrinter($configuration); } - $this->assertString($options, 'fromFile'); - $this->assertString($options, 'toFile'); - $this->assertStringOrNull($options, 'fromFileDate'); - $this->assertStringOrNull($options, 'toFileDate'); - $this->header = sprintf("--- %s%s\n+++ %s%s\n", $options['fromFile'], null === $options['fromFileDate'] ? '' : "\t" . $options['fromFileDate'], $options['toFile'], null === $options['toFileDate'] ? '' : "\t" . $options['toFileDate']); - $this->collapseRanges = $options['collapseRanges']; - $this->commonLineThreshold = $options['commonLineThreshold']; - $this->contextLines = $options['contextLines']; + if ($configuration->outputIsTeamCity()) { + new TeamCityLogger(\PHPUnit\TextUI\Output\DefaultPrinter::standardOutput(), EventFacade::instance()); + } + return self::$printer; } - public function getDiff(array $diff) : string + /** + * @psalm-param ?array $testDoxResult + */ + public static function printResult(TestResult $result, ?array $testDoxResult, Duration $duration): void { - if (0 === count($diff)) { - return ''; + assert(self::$printer !== null); + if ($result->numberOfTestsRun() > 0) { + if (self::$defaultProgressPrinter) { + self::$printer->print(\PHP_EOL . \PHP_EOL); + } + self::$printer->print((new ResourceUsageFormatter())->resourceUsage($duration) . \PHP_EOL . \PHP_EOL); } - $this->changed = \false; - $buffer = fopen('php://memory', 'r+b'); - fwrite($buffer, $this->header); - $this->writeDiffHunks($buffer, $diff); - if (!$this->changed) { - fclose($buffer); - return ''; + if (self::$testDoxResultPrinter !== null && $testDoxResult !== null) { + self::$testDoxResultPrinter->print($testDoxResult); + } + if (self::$defaultResultPrinter !== null) { + self::$defaultResultPrinter->print($result); + } + if (self::$summaryPrinter !== null) { + self::$summaryPrinter->print($result); } - $diff = stream_get_contents($buffer, -1, 0); - fclose($buffer); - // If the last char is not a linebreak: add it. - // This might happen when both the `from` and `to` do not have a trailing linebreak - $last = substr($diff, -1); - return "\n" !== $last && "\r" !== $last ? $diff . "\n" : $diff; } - private function writeDiffHunks($output, array $diff) : void + /** + * @throws CannotOpenSocketException + * @throws DirectoryDoesNotExistException + * @throws InvalidSocketException + */ + public static function printerFor(string $target): \PHPUnit\TextUI\Output\Printer { - // detect "No newline at end of file" and insert into `$diff` if needed - $upperLimit = count($diff); - if (0 === $diff[$upperLimit - 1][1]) { - $lc = substr($diff[$upperLimit - 1][0], -1); - if ("\n" !== $lc) { - array_splice($diff, $upperLimit, 0, [["\n\\ No newline at end of file\n", Differ::NO_LINE_END_EOF_WARNING]]); - } - } else { - // search back for the last `+` and `-` line, - // check if has trailing linebreak, else add under it warning under it - $toFind = [1 => \true, 2 => \true]; - for ($i = $upperLimit - 1; $i >= 0; --$i) { - if (isset($toFind[$diff[$i][1]])) { - unset($toFind[$diff[$i][1]]); - $lc = substr($diff[$i][0], -1); - if ("\n" !== $lc) { - array_splice($diff, $i + 1, 0, [["\n\\ No newline at end of file\n", Differ::NO_LINE_END_EOF_WARNING]]); - } - if (!count($toFind)) { - break; - } - } + if ($target === 'php://stdout') { + if (!self::$printer instanceof \PHPUnit\TextUI\Output\NullPrinter) { + return self::$printer; } + return \PHPUnit\TextUI\Output\DefaultPrinter::standardOutput(); } - // write hunks to output buffer - $cutOff = max($this->commonLineThreshold, $this->contextLines); - $hunkCapture = \false; - $sameCount = $toRange = $fromRange = 0; - $toStart = $fromStart = 1; - $i = 0; - /** @var int $i */ - foreach ($diff as $i => $entry) { - if (0 === $entry[1]) { - // same - if (\false === $hunkCapture) { - ++$fromStart; - ++$toStart; - continue; - } - ++$sameCount; - ++$toRange; - ++$fromRange; - if ($sameCount === $cutOff) { - $contextStartOffset = $hunkCapture - $this->contextLines < 0 ? $hunkCapture : $this->contextLines; - // note: $contextEndOffset = $this->contextLines; - // - // because we never go beyond the end of the diff. - // with the cutoff/contextlines here the follow is never true; - // - // if ($i - $cutOff + $this->contextLines + 1 > \count($diff)) { - // $contextEndOffset = count($diff) - 1; - // } - // - // ; that would be true for a trailing incomplete hunk case which is dealt with after this loop - $this->writeHunk($diff, $hunkCapture - $contextStartOffset, $i - $cutOff + $this->contextLines + 1, $fromStart - $contextStartOffset, $fromRange - $cutOff + $contextStartOffset + $this->contextLines, $toStart - $contextStartOffset, $toRange - $cutOff + $contextStartOffset + $this->contextLines, $output); - $fromStart += $fromRange; - $toStart += $toRange; - $hunkCapture = \false; - $sameCount = $toRange = $fromRange = 0; - } - continue; - } - $sameCount = 0; - if ($entry[1] === Differ::NO_LINE_END_EOF_WARNING) { - continue; - } - $this->changed = \true; - if (\false === $hunkCapture) { - $hunkCapture = $i; - } - if (Differ::ADDED === $entry[1]) { - // added - ++$toRange; - } - if (Differ::REMOVED === $entry[1]) { - // removed - ++$fromRange; + return \PHPUnit\TextUI\Output\DefaultPrinter::from($target); + } + private static function createPrinter(Configuration $configuration): void + { + $printerNeeded = \false; + if ($configuration->debug()) { + $printerNeeded = \true; + } + if ($configuration->outputIsTeamCity()) { + $printerNeeded = \true; + } + if ($configuration->outputIsTestDox()) { + $printerNeeded = \true; + } + if (!$configuration->noOutput() && !$configuration->noProgress()) { + $printerNeeded = \true; + } + if (!$configuration->noOutput() && !$configuration->noResults()) { + $printerNeeded = \true; + } + if ($printerNeeded) { + if ($configuration->outputToStandardErrorStream()) { + self::$printer = \PHPUnit\TextUI\Output\DefaultPrinter::standardError(); + return; } + self::$printer = \PHPUnit\TextUI\Output\DefaultPrinter::standardOutput(); + return; + } + self::$printer = new \PHPUnit\TextUI\Output\NullPrinter(); + } + private static function createProgressPrinter(Configuration $configuration): void + { + assert(self::$printer !== null); + if (!self::useDefaultProgressPrinter($configuration)) { + return; + } + new DefaultProgressPrinter(self::$printer, EventFacade::instance(), $configuration->colors(), $configuration->columns(), $configuration->source()); + self::$defaultProgressPrinter = \true; + } + private static function useDefaultProgressPrinter(Configuration $configuration): bool + { + if ($configuration->noOutput()) { + return \false; } - if (\false === $hunkCapture) { - return; + if ($configuration->noProgress()) { + return \false; } - // we end here when cutoff (commonLineThreshold) was not reached, but we where capturing a hunk, - // do not render hunk till end automatically because the number of context lines might be less than the commonLineThreshold - $contextStartOffset = $hunkCapture - $this->contextLines < 0 ? $hunkCapture : $this->contextLines; - // prevent trying to write out more common lines than there are in the diff _and_ - // do not write more than configured through the context lines - $contextEndOffset = min($sameCount, $this->contextLines); - $fromRange -= $sameCount; - $toRange -= $sameCount; - $this->writeHunk($diff, $hunkCapture - $contextStartOffset, $i - $sameCount + $contextEndOffset + 1, $fromStart - $contextStartOffset, $fromRange + $contextStartOffset + $contextEndOffset, $toStart - $contextStartOffset, $toRange + $contextStartOffset + $contextEndOffset, $output); + if ($configuration->outputIsTeamCity()) { + return \false; + } + return \true; } - private function writeHunk(array $diff, int $diffStartIndex, int $diffEndIndex, int $fromStart, int $fromRange, int $toStart, int $toRange, $output) : void + private static function createResultPrinter(Configuration $configuration): void { - fwrite($output, '@@ -' . $fromStart); - if (!$this->collapseRanges || 1 !== $fromRange) { - fwrite($output, ',' . $fromRange); + assert(self::$printer !== null); + if ($configuration->outputIsTestDox()) { + self::$defaultResultPrinter = new DefaultResultPrinter(self::$printer, \true, \true, \true, \false, \false, \true, \false, \false, $configuration->displayDetailsOnTestsThatTriggerDeprecations(), $configuration->displayDetailsOnTestsThatTriggerErrors(), $configuration->displayDetailsOnTestsThatTriggerNotices(), $configuration->displayDetailsOnTestsThatTriggerWarnings(), $configuration->reverseDefectList()); } - fwrite($output, ' +' . $toStart); - if (!$this->collapseRanges || 1 !== $toRange) { - fwrite($output, ',' . $toRange); + if ($configuration->outputIsTestDox()) { + self::$testDoxResultPrinter = new TestDoxResultPrinter(self::$printer, $configuration->colors()); } - fwrite($output, " @@\n"); - for ($i = $diffStartIndex; $i < $diffEndIndex; ++$i) { - if ($diff[$i][1] === Differ::ADDED) { - $this->changed = \true; - fwrite($output, '+' . $diff[$i][0]); - } elseif ($diff[$i][1] === Differ::REMOVED) { - $this->changed = \true; - fwrite($output, '-' . $diff[$i][0]); - } elseif ($diff[$i][1] === Differ::OLD) { - fwrite($output, ' ' . $diff[$i][0]); - } elseif ($diff[$i][1] === Differ::NO_LINE_END_EOF_WARNING) { - $this->changed = \true; - fwrite($output, $diff[$i][0]); - } - //} elseif ($diff[$i][1] === Differ::DIFF_LINE_END_WARNING) { // custom comment inserted by PHPUnit/diff package - // skip - //} else { - // unknown/invalid - //} + if ($configuration->noOutput() || $configuration->noResults()) { + return; + } + if (self::$defaultResultPrinter !== null) { + return; } + self::$defaultResultPrinter = new DefaultResultPrinter(self::$printer, \true, \true, \true, \true, \true, \true, $configuration->displayDetailsOnIncompleteTests(), $configuration->displayDetailsOnSkippedTests(), $configuration->displayDetailsOnTestsThatTriggerDeprecations(), $configuration->displayDetailsOnTestsThatTriggerErrors(), $configuration->displayDetailsOnTestsThatTriggerNotices(), $configuration->displayDetailsOnTestsThatTriggerWarnings(), $configuration->reverseDefectList()); } - private function assertString(array $options, string $option) : void + private static function createSummaryPrinter(Configuration $configuration): void { - if (!is_string($options[$option])) { - throw new ConfigurationException($option, 'a string', $options[$option]); + assert(self::$printer !== null); + if (($configuration->noOutput() || $configuration->noResults()) && !($configuration->outputIsTeamCity() || $configuration->outputIsTestDox())) { + return; } + self::$summaryPrinter = new \PHPUnit\TextUI\Output\SummaryPrinter(self::$printer, $configuration->colors()); } - private function assertStringOrNull(array $options, string $option) : void + /** + * @throws EventFacadeIsSealedException + * @throws UnknownSubscriberTypeException + */ + private static function createUnexpectedOutputPrinter(): void { - if (null !== $options[$option] && !is_string($options[$option])) { - throw new ConfigurationException($option, 'a string or ', $options[$option]); - } + assert(self::$printer !== null); + new UnexpectedOutputPrinter(self::$printer, EventFacade::instance()); } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnitPHAR\SebastianBergmann\Diff\Output; +namespace PHPUnit\TextUI\Output; -use function array_splice; +use function assert; use function count; +use function dirname; +use function explode; use function fclose; use function fopen; +use function fsockopen; use function fwrite; -use function max; -use function min; -use function stream_get_contents; -use function strlen; -use function substr; -use PHPUnitPHAR\SebastianBergmann\Diff\Differ; +use function str_replace; +use function str_starts_with; +use PHPUnit\Runner\DirectoryDoesNotExistException; +use PHPUnit\TextUI\CannotOpenSocketException; +use PHPUnit\TextUI\InvalidSocketException; +use PHPUnit\Util\Filesystem; /** - * Builds a diff string representation in unified diff format in chunks. + * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class UnifiedDiffOutputBuilder extends AbstractChunkOutputBuilder +final class DefaultPrinter implements \PHPUnit\TextUI\Output\Printer { /** - * @var bool - */ - private $collapseRanges = \true; - /** - * @var int >= 0 + * @psalm-var closed-resource|resource */ - private $commonLineThreshold = 6; + private $stream; + private readonly bool $isPhpStream; + private bool $isOpen; /** - * @var int >= 0 + * @throws CannotOpenSocketException + * @throws DirectoryDoesNotExistException + * @throws InvalidSocketException */ - private $contextLines = 3; + public static function from(string $out): self + { + return new self($out); + } /** - * @var string + * @throws CannotOpenSocketException + * @throws DirectoryDoesNotExistException + * @throws InvalidSocketException */ - private $header; + public static function standardOutput(): self + { + return new self('php://stdout'); + } /** - * @var bool + * @throws CannotOpenSocketException + * @throws DirectoryDoesNotExistException + * @throws InvalidSocketException */ - private $addLineNumbers; - public function __construct(string $header = "--- Original\n+++ New\n", bool $addLineNumbers = \false) + public static function standardError(): self { - $this->header = $header; - $this->addLineNumbers = $addLineNumbers; + return new self('php://stderr'); } - public function getDiff(array $diff) : string + /** + * @throws CannotOpenSocketException + * @throws DirectoryDoesNotExistException + * @throws InvalidSocketException + */ + private function __construct(string $out) { - $buffer = fopen('php://memory', 'r+b'); - if ('' !== $this->header) { - fwrite($buffer, $this->header); - if ("\n" !== substr($this->header, -1, 1)) { - fwrite($buffer, "\n"); + $this->isPhpStream = str_starts_with($out, 'php://'); + if (str_starts_with($out, 'socket://')) { + $tmp = explode(':', str_replace('socket://', '', $out)); + if (count($tmp) !== 2) { + throw new InvalidSocketException($out); } + $stream = @fsockopen($tmp[0], (int) $tmp[1]); + if ($stream === \false) { + throw new CannotOpenSocketException($tmp[0], (int) $tmp[1]); + } + $this->stream = $stream; + $this->isOpen = \true; + return; } - if (0 !== count($diff)) { - $this->writeDiffHunks($buffer, $diff); + if (!$this->isPhpStream && !Filesystem::createDirectory(dirname($out))) { + throw new DirectoryDoesNotExistException(dirname($out)); } - $diff = stream_get_contents($buffer, -1, 0); - fclose($buffer); - // If the diff is non-empty and last char is not a linebreak: add it. - // This might happen when both the `from` and `to` do not have a trailing linebreak - $last = substr($diff, -1); - return 0 !== strlen($diff) && "\n" !== $last && "\r" !== $last ? $diff . "\n" : $diff; + $this->stream = fopen($out, 'wb'); + $this->isOpen = \true; } - private function writeDiffHunks($output, array $diff) : void + public function print(string $buffer): void { - // detect "No newline at end of file" and insert into `$diff` if needed - $upperLimit = count($diff); - if (0 === $diff[$upperLimit - 1][1]) { - $lc = substr($diff[$upperLimit - 1][0], -1); - if ("\n" !== $lc) { - array_splice($diff, $upperLimit, 0, [["\n\\ No newline at end of file\n", Differ::NO_LINE_END_EOF_WARNING]]); - } - } else { - // search back for the last `+` and `-` line, - // check if has trailing linebreak, else add under it warning under it - $toFind = [1 => \true, 2 => \true]; - for ($i = $upperLimit - 1; $i >= 0; --$i) { - if (isset($toFind[$diff[$i][1]])) { - unset($toFind[$diff[$i][1]]); - $lc = substr($diff[$i][0], -1); - if ("\n" !== $lc) { - array_splice($diff, $i + 1, 0, [["\n\\ No newline at end of file\n", Differ::NO_LINE_END_EOF_WARNING]]); - } - if (!count($toFind)) { - break; - } - } - } - } - // write hunks to output buffer - $cutOff = max($this->commonLineThreshold, $this->contextLines); - $hunkCapture = \false; - $sameCount = $toRange = $fromRange = 0; - $toStart = $fromStart = 1; - $i = 0; - /** @var int $i */ - foreach ($diff as $i => $entry) { - if (0 === $entry[1]) { - // same - if (\false === $hunkCapture) { - ++$fromStart; - ++$toStart; - continue; - } - ++$sameCount; - ++$toRange; - ++$fromRange; - if ($sameCount === $cutOff) { - $contextStartOffset = $hunkCapture - $this->contextLines < 0 ? $hunkCapture : $this->contextLines; - // note: $contextEndOffset = $this->contextLines; - // - // because we never go beyond the end of the diff. - // with the cutoff/contextlines here the follow is never true; - // - // if ($i - $cutOff + $this->contextLines + 1 > \count($diff)) { - // $contextEndOffset = count($diff) - 1; - // } - // - // ; that would be true for a trailing incomplete hunk case which is dealt with after this loop - $this->writeHunk($diff, $hunkCapture - $contextStartOffset, $i - $cutOff + $this->contextLines + 1, $fromStart - $contextStartOffset, $fromRange - $cutOff + $contextStartOffset + $this->contextLines, $toStart - $contextStartOffset, $toRange - $cutOff + $contextStartOffset + $this->contextLines, $output); - $fromStart += $fromRange; - $toStart += $toRange; - $hunkCapture = \false; - $sameCount = $toRange = $fromRange = 0; - } - continue; - } - $sameCount = 0; - if ($entry[1] === Differ::NO_LINE_END_EOF_WARNING) { - continue; - } - if (\false === $hunkCapture) { - $hunkCapture = $i; - } - if (Differ::ADDED === $entry[1]) { - ++$toRange; - } - if (Differ::REMOVED === $entry[1]) { - ++$fromRange; - } - } - if (\false === $hunkCapture) { - return; - } - // we end here when cutoff (commonLineThreshold) was not reached, but we where capturing a hunk, - // do not render hunk till end automatically because the number of context lines might be less than the commonLineThreshold - $contextStartOffset = $hunkCapture - $this->contextLines < 0 ? $hunkCapture : $this->contextLines; - // prevent trying to write out more common lines than there are in the diff _and_ - // do not write more than configured through the context lines - $contextEndOffset = min($sameCount, $this->contextLines); - $fromRange -= $sameCount; - $toRange -= $sameCount; - $this->writeHunk($diff, $hunkCapture - $contextStartOffset, $i - $sameCount + $contextEndOffset + 1, $fromStart - $contextStartOffset, $fromRange + $contextStartOffset + $contextEndOffset, $toStart - $contextStartOffset, $toRange + $contextStartOffset + $contextEndOffset, $output); + assert($this->isOpen); + fwrite($this->stream, $buffer); } - private function writeHunk(array $diff, int $diffStartIndex, int $diffEndIndex, int $fromStart, int $fromRange, int $toStart, int $toRange, $output) : void + public function flush(): void { - if ($this->addLineNumbers) { - fwrite($output, '@@ -' . $fromStart); - if (!$this->collapseRanges || 1 !== $fromRange) { - fwrite($output, ',' . $fromRange); - } - fwrite($output, ' +' . $toStart); - if (!$this->collapseRanges || 1 !== $toRange) { - fwrite($output, ',' . $toRange); - } - fwrite($output, " @@\n"); - } else { - fwrite($output, "@@ @@\n"); - } - for ($i = $diffStartIndex; $i < $diffEndIndex; ++$i) { - if ($diff[$i][1] === Differ::ADDED) { - fwrite($output, '+' . $diff[$i][0]); - } elseif ($diff[$i][1] === Differ::REMOVED) { - fwrite($output, '-' . $diff[$i][0]); - } elseif ($diff[$i][1] === Differ::OLD) { - fwrite($output, ' ' . $diff[$i][0]); - } elseif ($diff[$i][1] === Differ::NO_LINE_END_EOF_WARNING) { - fwrite($output, "\n"); - // $diff[$i][0] - } else { - /* Not changed (old) Differ::OLD or Warning Differ::DIFF_LINE_END_WARNING */ - fwrite($output, ' ' . $diff[$i][0]); - } + if ($this->isOpen && $this->isPhpStream) { + fclose($this->stream); + $this->isOpen = \false; } } } @@ -101424,391 +92428,601 @@ final class UnifiedDiffOutputBuilder extends AbstractChunkOutputBuilder declare (strict_types=1); /* - * This file is part of sebastian/diff. + * This file is part of PHPUnit. * * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnitPHAR\SebastianBergmann\Diff; +namespace PHPUnit\TextUI\Output; -use function array_pop; -use function count; -use function max; -use function preg_match; -use function preg_split; /** - * Unified diff parser. + * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class Parser +final class NullPrinter implements \PHPUnit\TextUI\Output\Printer { - /** - * @return Diff[] - */ - public function parse(string $string) : array + public function print(string $buffer): void { - $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('#^---\\h+"?(?P[^\\v\\t"]+)#', $lines[$i], $fromMatch) && preg_match('#^\\+\\+\\+\\h+"?(?P[^\\v\\t"]+)#', $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 + public function flush(): void { - $chunks = []; - $chunk = null; - $diffLines = []; - foreach ($lines as $line) { - if (preg_match('/^@@\\s+-(?P\\d+)(?:,\\s*(?P\\d+))?\\s+\\+(?P\\d+)(?:,\\s*(?P\\d+))?\\s+@@/', $line, $match)) { - $chunk = new Chunk((int) $match['start'], isset($match['startrange']) ? max(1, (int) $match['startrange']) : 1, (int) $match['end'], isset($match['endrange']) ? max(1, (int) $match['endrange']) : 1); - $chunks[] = $chunk; - $diffLines = []; - continue; - } - if (preg_match('/^(?P[+ -])?(?P.*)/', $line, $match)) { - $type = Line::UNCHANGED; - if ($match['type'] === '+') { - $type = Line::ADDED; - } elseif ($match['type'] === '-') { - $type = Line::REMOVED; - } - $diffLines[] = new Line($type, $match['line']); - if (null !== $chunk) { - $chunk->setLines($diffLines); - } - } - } - $diff->setChunks($chunks); } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnitPHAR\SebastianBergmann\Diff; +namespace PHPUnit\TextUI\Output; -use function array_reverse; -use function count; -use function max; -use SplFixedArray; -final class TimeEfficientLongestCommonSubsequenceCalculator implements LongestCommonSubsequenceCalculator +/** + * @internal This interface is not covered by the backward compatibility promise for PHPUnit + */ +interface Printer { - /** - * {@inheritdoc} - */ - public function calculate(array $from, array $to) : array + public function print(string $buffer): void; + public function flush(): void; +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\Output; + +use function sprintf; +use PHPUnit\TestRunner\TestResult\TestResult; +use PHPUnit\Util\Color; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class SummaryPrinter +{ + private readonly \PHPUnit\TextUI\Output\Printer $printer; + private readonly bool $colors; + private bool $countPrinted = \false; + public function __construct(\PHPUnit\TextUI\Output\Printer $printer, bool $colors) { - $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; + $this->printer = $printer; + $this->colors = $colors; + } + public function print(TestResult $result): void + { + if ($result->numberOfTestsRun() === 0) { + $this->printWithColor('fg-black, bg-yellow', 'No tests executed!'); + return; } - for ($i = 1; $i <= $fromLength; ++$i) { - for ($j = 1; $j <= $toLength; ++$j) { - $o = $j * $width + $i; - // don't use max() to avoid function call overhead - $firstOrLast = $from[$i - 1] === $to[$j - 1] ? $matrix[$o - $width - 1] + 1 : 0; - if ($matrix[$o - 1] > $matrix[$o - $width]) { - if ($firstOrLast > $matrix[$o - 1]) { - $matrix[$o] = $firstOrLast; - } else { - $matrix[$o] = $matrix[$o - 1]; - } - } else { - if ($firstOrLast > $matrix[$o - $width]) { - $matrix[$o] = $firstOrLast; - } else { - $matrix[$o] = $matrix[$o - $width]; - } - } - } + if ($result->wasSuccessfulAndNoTestHasIssues() && !$result->hasTestSuiteSkippedEvents() && !$result->hasTestSkippedEvents()) { + $this->printWithColor('fg-black, bg-green', sprintf('OK (%d test%s, %d assertion%s)', $result->numberOfTestsRun(), ($result->numberOfTestsRun() === 1) ? '' : 's', $result->numberOfAssertions(), ($result->numberOfAssertions() === 1) ? '' : 's')); + $this->printNumberOfIssuesIgnoredByBaseline($result); + return; } - $i = $fromLength; - $j = $toLength; - while ($i > 0 && $j > 0) { - if ($from[$i - 1] === $to[$j - 1]) { - $common[] = $from[$i - 1]; - --$i; - --$j; + $color = 'fg-black, bg-yellow'; + if ($result->wasSuccessful()) { + if (!$result->hasTestsWithIssues()) { + $this->printWithColor($color, 'OK, but some tests were skipped!'); } else { - $o = $j * $width + $i; - if ($matrix[$o - $width] > $matrix[$o - 1]) { - --$j; - } else { - --$i; - } - } + $this->printWithColor($color, 'OK, but there were issues!'); + } + } else if ($result->hasTestErroredEvents() || $result->hasTestTriggeredPhpunitErrorEvents()) { + $color = 'fg-white, bg-red'; + $this->printWithColor($color, 'ERRORS!'); + } elseif ($result->hasTestFailedEvents()) { + $color = 'fg-white, bg-red'; + $this->printWithColor($color, 'FAILURES!'); + } elseif ($result->hasWarnings()) { + $this->printWithColor($color, 'WARNINGS!'); + } elseif ($result->hasDeprecations()) { + $this->printWithColor($color, 'DEPRECATIONS!'); + } elseif ($result->hasNotices()) { + $this->printWithColor($color, 'NOTICES!'); + } + $this->printCountString($result->numberOfTestsRun(), 'Tests', $color, \true); + $this->printCountString($result->numberOfAssertions(), 'Assertions', $color, \true); + $this->printCountString($result->numberOfErrors(), 'Errors', $color); + $this->printCountString($result->numberOfTestFailedEvents(), 'Failures', $color); + $this->printCountString($result->numberOfWarnings(), 'Warnings', $color); + $this->printCountString($result->numberOfDeprecations(), 'Deprecations', $color); + $this->printCountString($result->numberOfNotices(), 'Notices', $color); + $this->printCountString($result->numberOfTestSuiteSkippedEvents() + $result->numberOfTestSkippedEvents(), 'Skipped', $color); + $this->printCountString($result->numberOfTestMarkedIncompleteEvents(), 'Incomplete', $color); + $this->printCountString($result->numberOfTestsWithTestConsideredRiskyEvents(), 'Risky', $color); + $this->printWithColor($color, '.'); + $this->printNumberOfIssuesIgnoredByBaseline($result); + } + private function printCountString(int $count, string $name, string $color, bool $always = \false): void + { + if ($always || $count > 0) { + $this->printWithColor($color, sprintf('%s%s: %d', $this->countPrinted ? ', ' : '', $name, $count), \false); + $this->countPrinted = \true; + } + } + private function printWithColor(string $color, string $buffer, bool $lf = \true): void + { + if ($this->colors) { + $buffer = Color::colorizeTextBox($color, $buffer); + } + $this->printer->print($buffer); + if ($lf) { + $this->printer->print(\PHP_EOL); + } + } + private function printNumberOfIssuesIgnoredByBaseline(TestResult $result): void + { + if ($result->hasIssuesIgnoredByBaseline()) { + $this->printer->print(sprintf('%s%d issue%s %s ignored by baseline.%s', \PHP_EOL, $result->numberOfIssuesIgnoredByBaseline(), ($result->numberOfIssuesIgnoredByBaseline() > 1) ? 's' : '', ($result->numberOfIssuesIgnoredByBaseline() > 1) ? 'were' : 'was', \PHP_EOL)); } - return array_reverse($common); } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnitPHAR\SebastianBergmann\Environment; +namespace PHPUnit\TextUI\Output\TestDox; -use const DIRECTORY_SEPARATOR; -use const STDIN; -use const STDOUT; -use function defined; -use function fclose; -use function fstat; -use function function_exists; -use function getenv; -use function is_resource; -use function is_string; -use function posix_isatty; +use const PHP_EOL; +use function array_map; +use function assert; +use function explode; +use function implode; use function preg_match; -use function proc_close; -use function proc_open; -use function sapi_windows_vt100_support; -use function shell_exec; -use function stream_get_contents; -use function stream_isatty; +use function preg_split; +use function rtrim; +use function str_starts_with; use function trim; -final class Console +use PHPUnit\Event\Code\Throwable; +use PHPUnit\Framework\TestStatus\TestStatus; +use PHPUnit\Logging\TestDox\TestResult as TestDoxTestResult; +use PHPUnit\Logging\TestDox\TestResultCollection; +use PHPUnit\TextUI\Output\Printer; +use PHPUnit\Util\Color; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class ResultPrinter { + private readonly Printer $printer; + private readonly bool $colors; + public function __construct(Printer $printer, bool $colors) + { + $this->printer = $printer; + $this->colors = $colors; + } /** - * @var int - */ - public const STDIN = 0; - /** - * @var int + * @psalm-param array $tests */ - public const STDOUT = 1; + public function print(array $tests): void + { + foreach ($tests as $prettifiedClassName => $_tests) { + $this->printPrettifiedClassName($prettifiedClassName); + foreach ($_tests as $test) { + $this->printTestResult($test); + } + $this->printer->print(PHP_EOL); + } + } + public function flush(): void + { + $this->printer->flush(); + } /** - * @var int + * @psalm-param string $prettifiedClassName */ - public const STDERR = 2; + private function printPrettifiedClassName(string $prettifiedClassName): void + { + $buffer = $prettifiedClassName; + if ($this->colors) { + $buffer = Color::colorizeTextBox('underlined', $buffer); + } + $this->printer->print($buffer . PHP_EOL); + } + private function printTestResult(TestDoxTestResult $test): void + { + $this->printTestResultHeader($test); + $this->printTestResultBody($test); + } + private function printTestResultHeader(TestDoxTestResult $test): void + { + $buffer = ' ' . $this->symbolFor($test->status()) . ' '; + if ($this->colors) { + $this->printer->print(Color::colorizeTextBox($this->colorFor($test->status()), $buffer)); + } else { + $this->printer->print($buffer); + } + $this->printer->print($test->test()->testDox()->prettifiedMethodName($this->colors) . PHP_EOL); + } + private function printTestResultBody(TestDoxTestResult $test): void + { + if ($test->status()->isSuccess()) { + return; + } + if (!$test->hasThrowable()) { + return; + } + $this->printTestResultBodyStart($test); + $this->printThrowable($test); + $this->printTestResultBodyEnd($test); + } + private function printTestResultBodyStart(TestDoxTestResult $test): void + { + $this->printer->print($this->prefixLines($this->prefixFor('start', $test->status()), '')); + $this->printer->print(PHP_EOL); + } + private function printTestResultBodyEnd(TestDoxTestResult $test): void + { + $this->printer->print(PHP_EOL); + $this->printer->print($this->prefixLines($this->prefixFor('last', $test->status()), '')); + $this->printer->print(PHP_EOL); + } + private function printThrowable(TestDoxTestResult $test): void + { + $throwable = $test->throwable(); + assert($throwable instanceof Throwable); + $message = trim($throwable->description()); + $stackTrace = $this->formatStackTrace($throwable->stackTrace()); + $diff = ''; + if (!empty($message) && $this->colors) { + ['message' => $message, 'diff' => $diff] = $this->colorizeMessageAndDiff($message, $this->messageColorFor($test->status())); + } + if (!empty($message)) { + $this->printer->print($this->prefixLines($this->prefixFor('message', $test->status()), $message)); + $this->printer->print(PHP_EOL); + } + if (!empty($diff)) { + $this->printer->print($this->prefixLines($this->prefixFor('diff', $test->status()), $diff)); + $this->printer->print(PHP_EOL); + } + if (!empty($stackTrace)) { + if (!empty($message) || !empty($diff)) { + $prefix = $this->prefixFor('default', $test->status()); + } else { + $prefix = $this->prefixFor('trace', $test->status()); + } + $this->printer->print($this->prefixLines($prefix, PHP_EOL . $stackTrace)); + } + } /** - * Returns true if STDOUT supports colorization. - * - * This code has been copied and adapted from - * Symfony\Component\Console\Output\StreamOutput. + * @psalm-return array{message: string, diff: string} */ - public function hasColorSupport() : bool + private function colorizeMessageAndDiff(string $buffer, string $style): array { - if ('Hyper' === getenv('TERM_PROGRAM')) { - return \true; + $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 (str_starts_with($line, '-')) { + $line = Color::colorize('fg-red', Color::visualizeWhitespace($line, \true)); + } elseif (str_starts_with($line, '+')) { + $line = Color::colorize('fg-green', Color::visualizeWhitespace($line, \true)); + } elseif ($line === '@@ @@') { + $line = Color::colorize('fg-cyan', $line); + } + $diff[] = $line; + } } - 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 + $message = implode(PHP_EOL, $message); + $diff = implode(PHP_EOL, $diff); + if (!empty($message)) { + $message = Color::colorizeTextBox($style, $message); } - if (!defined('STDOUT')) { - // @codeCoverageIgnoreStart - return \false; - // @codeCoverageIgnoreEnd + return ['message' => $message, 'diff' => $diff]; + } + private function formatStackTrace(string $stackTrace): string + { + if (!$this->colors) { + return rtrim($stackTrace); } - return $this->isInteractive(STDOUT); + $lines = []; + $previousPath = ''; + foreach (explode(PHP_EOL, $stackTrace) as $line) { + if (preg_match('/^(.*):(\d+)$/', $line, $matches)) { + $lines[] = Color::colorizePath($matches[1], $previousPath) . Color::dim(':') . Color::colorize('fg-blue', $matches[2]) . "\n"; + $previousPath = $matches[1]; + continue; + } + $lines[] = $line; + $previousPath = ''; + } + return rtrim(implode('', $lines)); + } + private function prefixLines(string $prefix, string $message): string + { + return implode(PHP_EOL, array_map(static fn(string $line) => ' ' . $prefix . ($line ? ' ' . $line : ''), preg_split('/\r\n|\r|\n/', $message))); } /** - * Returns the number of columns of the terminal. - * - * @codeCoverageIgnore + * @psalm-param 'default'|'start'|'message'|'diff'|'trace'|'last' $type */ - public function getNumberOfColumns() : int + private function prefixFor(string $type, TestStatus $status): string + { + if (!$this->colors) { + return '│'; + } + return Color::colorize($this->colorFor($status), match ($type) { + 'default' => '│', + 'start' => '┐', + 'message' => '├', + 'diff' => '┊', + 'trace' => '╵', + 'last' => '┴', + }); + } + private function colorFor(TestStatus $status): string + { + if ($status->isSuccess()) { + return 'fg-green'; + } + if ($status->isError()) { + return 'fg-yellow'; + } + if ($status->isFailure()) { + return 'fg-red'; + } + if ($status->isSkipped()) { + return 'fg-cyan'; + } + if ($status->isIncomplete() || $status->isDeprecation() || $status->isNotice() || $status->isRisky() || $status->isWarning()) { + return 'fg-yellow'; + } + return 'fg-blue'; + } + private function messageColorFor(TestStatus $status): string + { + if ($status->isSuccess()) { + return ''; + } + if ($status->isError()) { + return 'bg-yellow,fg-black'; + } + if ($status->isFailure()) { + return 'bg-red,fg-white'; + } + if ($status->isSkipped()) { + return 'fg-cyan'; + } + if ($status->isIncomplete() || $status->isDeprecation() || $status->isNotice() || $status->isRisky() || $status->isWarning()) { + return 'fg-yellow'; + } + return 'fg-white,bg-blue'; + } + private function symbolFor(TestStatus $status): string + { + if ($status->isSuccess()) { + return '✔'; + } + if ($status->isError() || $status->isFailure()) { + return '✘'; + } + if ($status->isSkipped()) { + return '↩'; + } + if ($status->isDeprecation() || $status->isNotice() || $status->isRisky() || $status->isWarning()) { + return '⚠'; + } + if ($status->isIncomplete()) { + return '∅'; + } + return '?'; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI; + +use PHPUnit\TestRunner\TestResult\TestResult; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class ShellExitCodeCalculator +{ + private const SUCCESS_EXIT = 0; + private const FAILURE_EXIT = 1; + private const EXCEPTION_EXIT = 2; + public function calculate(bool $failOnDeprecation, bool $failOnEmptyTestSuite, bool $failOnIncomplete, bool $failOnNotice, bool $failOnRisky, bool $failOnSkipped, bool $failOnWarning, TestResult $result): int { - if (!$this->isInteractive(defined('STDIN') ? STDIN : self::STDIN)) { - return 80; + $returnCode = self::FAILURE_EXIT; + if ($result->wasSuccessful()) { + $returnCode = self::SUCCESS_EXIT; } - if ($this->isWindows()) { - return $this->getNumberOfColumnsWindows(); + if ($failOnEmptyTestSuite && !$result->hasTests()) { + $returnCode = self::FAILURE_EXIT; } - 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 - { - if (is_resource($fileDescriptor)) { - if (function_exists('stream_isatty') && @stream_isatty($fileDescriptor)) { - return \true; + if ($result->wasSuccessfulIgnoringPhpunitWarnings()) { + if ($failOnDeprecation && $result->hasDeprecations()) { + $returnCode = self::FAILURE_EXIT; } - if (function_exists('fstat')) { - $stat = @fstat(STDOUT); - return $stat && 020000 === ($stat['mode'] & 0170000); + if ($failOnIncomplete && $result->hasIncompleteTests()) { + $returnCode = self::FAILURE_EXIT; + } + if ($failOnNotice && $result->hasNotices()) { + $returnCode = self::FAILURE_EXIT; + } + if ($failOnRisky && $result->hasRiskyTests()) { + $returnCode = self::FAILURE_EXIT; + } + if ($failOnSkipped && $result->hasSkippedTests()) { + $returnCode = self::FAILURE_EXIT; + } + if ($failOnWarning && $result->hasWarnings()) { + $returnCode = self::FAILURE_EXIT; } - return \false; } - return function_exists('posix_isatty') && @posix_isatty($fileDescriptor); - } - private function isWindows() : bool - { - return DIRECTORY_SEPARATOR === '\\'; + if ($result->hasErrors()) { + $returnCode = self::EXCEPTION_EXIT; + } + return $returnCode; } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI; + +use function mt_srand; +use PHPUnit\Event; +use PHPUnit\Framework\TestSuite; +use PHPUnit\Runner\ResultCache\ResultCache; +use PHPUnit\Runner\TestSuiteSorter; +use PHPUnit\TextUI\Configuration\Configuration; +use Throwable; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class TestRunner +{ /** - * @codeCoverageIgnore + * @throws RuntimeException */ - private function getNumberOfColumnsInteractive() : int + public function run(Configuration $configuration, ResultCache $resultCache, TestSuite $suite): void { - 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]; - } + try { + Event\Facade::emitter()->testRunnerStarted(); + if ($configuration->executionOrder() === TestSuiteSorter::ORDER_RANDOMIZED) { + mt_srand($configuration->randomOrderSeed()); + } + if ($configuration->executionOrder() !== TestSuiteSorter::ORDER_DEFAULT || $configuration->executionOrderDefects() !== TestSuiteSorter::ORDER_DEFAULT || $configuration->resolveDependencies()) { + $resultCache->load(); + (new TestSuiteSorter($resultCache))->reorderTestsInSuite($suite, $configuration->executionOrder(), $configuration->resolveDependencies(), $configuration->executionOrderDefects()); + Event\Facade::emitter()->testSuiteSorted($configuration->executionOrder(), $configuration->executionOrderDefects(), $configuration->resolveDependencies()); + } + (new \PHPUnit\TextUI\TestSuiteFilterProcessor())->process($configuration, $suite); + Event\Facade::emitter()->testRunnerExecutionStarted(Event\TestSuite\TestSuiteBuilder::from($suite)); + $suite->run(); + Event\Facade::emitter()->testRunnerExecutionFinished(); + Event\Facade::emitter()->testRunnerFinished(); + } catch (Throwable $t) { + throw new \PHPUnit\TextUI\RuntimeException($t->getMessage(), (int) $t->getCode(), $t); } - return 80; } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI; + +use function array_map; +use PHPUnit\Event; +use PHPUnit\Framework\TestSuite; +use PHPUnit\Runner\Filter\Factory; +use PHPUnit\TextUI\Configuration\Configuration; +use PHPUnit\TextUI\Configuration\FilterNotConfiguredException; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class TestSuiteFilterProcessor +{ /** - * @codeCoverageIgnore + * @throws Event\RuntimeException + * @throws FilterNotConfiguredException */ - private function getNumberOfColumnsWindows() : int + public function process(Configuration $configuration, TestSuite $suite): void { - $ansicon = getenv('ANSICON'); - $columns = 80; - if (is_string($ansicon) && preg_match('/^(\\d+)x\\d+ \\(\\d+x(\\d+)\\)$/', trim($ansicon), $matches)) { - $columns = (int) $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 = (int) $matches[2]; - } - } + $factory = new Factory(); + if (!$configuration->hasFilter() && !$configuration->hasGroups() && !$configuration->hasExcludeGroups() && !$configuration->hasTestsCovering() && !$configuration->hasTestsUsing()) { + return; } - return $columns - 1; + if ($configuration->hasExcludeGroups()) { + $factory->addExcludeGroupFilter($configuration->excludeGroups()); + } + if ($configuration->hasGroups()) { + $factory->addIncludeGroupFilter($configuration->groups()); + } + if ($configuration->hasTestsCovering()) { + $factory->addIncludeGroupFilter(array_map(static fn(string $name): string => '__phpunit_covers_' . $name, $configuration->testsCovering())); + } + if ($configuration->hasTestsUsing()) { + $factory->addIncludeGroupFilter(array_map(static fn(string $name): string => '__phpunit_uses_' . $name, $configuration->testsUsing())); + } + if ($configuration->hasFilter()) { + $factory->addNameFilter($configuration->filter()); + } + $suite->injectFilter($factory); + Event\Facade::emitter()->testSuiteFiltered(Event\TestSuite\TestSuiteBuilder::from($suite)); } } -sebastian/environment - -Copyright (c) 2014-2022, Sebastian Bergmann . -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the - distribution. - - * Neither the name of Sebastian Bergmann nor the names of his - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN -ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnitPHAR\SebastianBergmann\Environment; +namespace PHPUnit\Util; -use const DIRECTORY_SEPARATOR; -use const PHP_OS; -use const PHP_OS_FAMILY; -use function defined; -final class OperatingSystem +use Throwable; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class Cloner { /** - * 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. + * @psalm-template OriginalType of object + * + * @psalm-param OriginalType $original + * + * @psalm-return OriginalType */ - public function getFamily() : string + public static function clone(object $original): object { - 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'; + try { + return clone $original; + } catch (Throwable) { + return $original; } } } @@ -101816,887 +93030,860 @@ final class OperatingSystem declare (strict_types=1); /* - * This file is part of sebastian/environment. + * This file is part of PHPUnit. * * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnitPHAR\SebastianBergmann\Environment; +namespace PHPUnit\Util; -use const PHP_BINARY; -use const PHP_BINDIR; -use const PHP_MAJOR_VERSION; -use const PHP_SAPI; -use const PHP_VERSION; +use const DIRECTORY_SEPARATOR; +use const PHP_EOL; use function array_map; -use function array_merge; -use function defined; -use function escapeshellarg; +use function count; use function explode; -use function extension_loaded; -use function getenv; -use function ini_get; -use function is_readable; -use function parse_ini_file; -use function php_ini_loaded_file; -use function php_ini_scanned_files; -use function phpversion; +use function implode; +use function max; +use function min; +use function preg_replace; +use function preg_replace_callback; +use function preg_split; use function sprintf; -use function strpos; +use function str_pad; +use function strtr; +use function trim; /** - * Utility class for HHVM/PHP environment handling. + * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class Runtime +final class Color { /** - * @var string + * @psalm-var array */ - private static $binary; + private const WHITESPACE_MAP = [' ' => '·', "\t" => '⇥']; /** - * Returns true when Xdebug or PCOV is available or - * the runtime used is PHPDBG. + * @psalm-var array */ - public function canCollectCodeCoverage() : bool - { - return $this->hasXdebug() || $this->hasPCOV() || $this->hasPHPDBGCodeCoverage(); - } + private const WHITESPACE_EOL_MAP = [' ' => '·', "\t" => '⇥', "\n" => '↵', "\r" => '⟵']; /** - * Returns true when Zend OPcache is loaded, enabled, - * and is configured to discard comments. + * @psalm-var array */ - public function discardsComments() : bool + private static array $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 (!$this->isOpcacheActive()) { - return \false; + if (trim($buffer) === '') { + return $buffer; } - if (ini_get('opcache.save_comments') !== '0') { - return \false; + $codes = array_map('\trim', explode(',', $color)); + $styles = []; + foreach ($codes as $code) { + if (isset(self::$ansiCodes[$code])) { + $styles[] = self::$ansiCodes[$code] ?? ''; + } } - return \true; + if (empty($styles)) { + return $buffer; + } + return self::optimizeColor(sprintf("\x1b[%sm", implode(';', $styles)) . $buffer . "\x1b[0m"); } - /** - * Returns true when Zend OPcache is loaded, enabled, - * and is configured to perform just-in-time compilation. - */ - public function performsJustInTimeCompilation() : bool + public static function colorizeTextBox(string $color, string $buffer): string { - if (PHP_MAJOR_VERSION < 8) { - return \false; - } - if (!$this->isOpcacheActive()) { - return \false; - } - if (strpos(ini_get('opcache.jit'), '0') === 0) { - return \false; + $lines = preg_split('/\r\n|\r|\n/', $buffer); + $padding = max(array_map('\strlen', $lines)); + $styledLines = []; + foreach ($lines as $line) { + $styledLines[] = self::colorize($color, str_pad($line, $padding)); } - return \true; + return implode(PHP_EOL, $styledLines); } - /** - * Returns the path to the binary of the current runtime. - * Appends ' --php' to the path when the runtime is HHVM. - */ - public function getBinary() : string + public static function colorizePath(string $path, ?string $previousPath = null, bool $colorizeFilename = \false): 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 ($previousPath === null) { + $previousPath = ''; } - 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; - } + $path = explode(DIRECTORY_SEPARATOR, $path); + $previousPath = explode(DIRECTORY_SEPARATOR, $previousPath); + for ($i = 0; $i < min(count($path), count($previousPath)); $i++) { + if ($path[$i] === $previousPath[$i]) { + $path[$i] = self::dim($path[$i]); } - // @codeCoverageIgnoreEnd } - if (self::$binary === null) { - // @codeCoverageIgnoreStart - self::$binary = 'php'; - // @codeCoverageIgnoreEnd + if ($colorizeFilename) { + $last = count($path) - 1; + $path[$last] = preg_replace_callback('/([\-_.]+|phpt$)/', static fn($matches) => self::dim($matches[0]), $path[$last]); } - return self::$binary; - } - public function getNameWithVersion() : string - { - return $this->getName() . ' ' . $this->getVersion(); + return self::optimizeColor(implode(self::dim(DIRECTORY_SEPARATOR), $path)); } - public function getNameWithVersionAndCodeCoverageDriver() : string + public static function dim(string $buffer): string { - if (!$this->canCollectCodeCoverage() || $this->hasPHPDBGCodeCoverage()) { - return $this->getNameWithVersion(); - } - if ($this->hasPCOV()) { - return sprintf('%s with PCOV %s', $this->getNameWithVersion(), phpversion('pcov')); - } - if ($this->hasXdebug()) { - return sprintf('%s with Xdebug %s', $this->getNameWithVersion(), phpversion('xdebug')); + if (trim($buffer) === '') { + return $buffer; } + return "\x1b[2m{$buffer}\x1b[22m"; } - public function getName() : string + public static function visualizeWhitespace(string $buffer, bool $visualizeEOL = \false): string { - if ($this->isHHVM()) { - // @codeCoverageIgnoreStart - return 'HHVM'; - // @codeCoverageIgnoreEnd - } - if ($this->isPHPDBG()) { - // @codeCoverageIgnoreStart - return 'PHPDBG'; - // @codeCoverageIgnoreEnd - } - return 'PHP'; + $replaceMap = $visualizeEOL ? self::WHITESPACE_EOL_MAP : self::WHITESPACE_MAP; + return preg_replace_callback('/\s+/', static fn($matches) => self::dim(strtr($matches[0], $replaceMap)), $buffer); } - public function getVendorUrl() : string + private static function optimizeColor(string $buffer): string { - if ($this->isHHVM()) { - // @codeCoverageIgnoreStart - return 'http://hhvm.com/'; - // @codeCoverageIgnoreEnd - } - return 'https://secure.php.net/'; + return preg_replace(["/\x1b\\[22m\x1b\\[2m/", "/\x1b\\[([^m]*)m\x1b\\[([1-9][0-9;]*)m/", "/(\x1b\\[[^m]*m)+(\x1b\\[0m)/"], ['', "\x1b[\$1;\$2m", '$2'], $buffer); } - public function getVersion() : string +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Util; + +use Throwable; +/** + * @internal This interface is not covered by the backward compatibility promise for PHPUnit + */ +interface Exception extends Throwable +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Util; + +use function sprintf; +use RuntimeException; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class InvalidDirectoryException extends RuntimeException implements \PHPUnit\Util\Exception +{ + public function __construct(string $directory) { - if ($this->isHHVM()) { - // @codeCoverageIgnoreStart - return HHVM_VERSION; - // @codeCoverageIgnoreEnd - } - return PHP_VERSION; + parent::__construct(sprintf('"%s" is not a directory', $directory)); } - /** - * Returns true when the runtime used is PHP and Xdebug is loaded. - */ - public function hasXdebug() : bool +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Util; + +use RuntimeException; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class InvalidJsonException extends RuntimeException implements \PHPUnit\Util\Exception +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Util; + +use function sprintf; +use RuntimeException; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class InvalidVersionOperatorException extends RuntimeException implements \PHPUnit\Util\Exception +{ + public function __construct(string $operator) { - return ($this->isPHP() || $this->isHHVM()) && extension_loaded('xdebug'); + parent::__construct(sprintf('"%s" is not a valid version_compare() operator', $operator)); } +} + + * + * 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\Util\Exception; +use RuntimeException; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class PhpProcessException extends RuntimeException implements Exception +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Util\Xml; + +use PHPUnit\Util\Exception; +use RuntimeException; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class XmlException extends RuntimeException implements Exception +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Util; + +use function class_exists; +use function defined; +use function dirname; +use function is_dir; +use function realpath; +use function str_starts_with; +use function sys_get_temp_dir; +use PHPUnitPHAR\Composer\Autoload\ClassLoader; +use PHPUnitPHAR\DeepCopy\DeepCopy; +use PHPUnitPHAR\PharIo\Manifest\Manifest; +use PHPUnitPHAR\PharIo\Version\Version as PharIoVersion; +use PHPUnitPHAR\PhpParser\Parser; +use PHPUnit\Framework\TestCase; +use ReflectionClass; +use PHPUnitPHAR\SebastianBergmann\CliParser\Parser as CliParser; +use PHPUnitPHAR\SebastianBergmann\CodeCoverage\CodeCoverage; +use PHPUnitPHAR\SebastianBergmann\CodeUnit\CodeUnit; +use PHPUnitPHAR\SebastianBergmann\CodeUnitReverseLookup\Wizard; +use PHPUnitPHAR\SebastianBergmann\Comparator\Comparator; +use PHPUnitPHAR\SebastianBergmann\Complexity\Calculator; +use PHPUnitPHAR\SebastianBergmann\Diff\Diff; +use PHPUnitPHAR\SebastianBergmann\Environment\Runtime; +use PHPUnitPHAR\SebastianBergmann\Exporter\Exporter; +use PHPUnitPHAR\SebastianBergmann\FileIterator\Facade as FileIteratorFacade; +use PHPUnitPHAR\SebastianBergmann\GlobalState\Snapshot; +use PHPUnitPHAR\SebastianBergmann\Invoker\Invoker; +use PHPUnitPHAR\SebastianBergmann\LinesOfCode\Counter; +use PHPUnitPHAR\SebastianBergmann\ObjectEnumerator\Enumerator; +use PHPUnitPHAR\SebastianBergmann\ObjectReflector\ObjectReflector; +use PHPUnitPHAR\SebastianBergmann\RecursionContext\Context; +use PHPUnitPHAR\SebastianBergmann\Template\Template; +use PHPUnitPHAR\SebastianBergmann\Timer\Timer; +use PHPUnitPHAR\SebastianBergmann\Type\TypeName; +use PHPUnitPHAR\SebastianBergmann\Version; +use PHPUnitPHAR\TheSeer\Tokenizer\Tokenizer; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class ExcludeList +{ /** - * Returns true when the runtime used is HHVM. + * @psalm-var array */ - public function isHHVM() : bool - { - return defined('HHVM_VERSION'); - } + private const EXCLUDED_CLASS_NAMES = [ + // composer + ClassLoader::class => 1, + // myclabs/deepcopy + DeepCopy::class => 1, + // nikic/php-parser + Parser::class => 1, + // phar-io/manifest + Manifest::class => 1, + // phar-io/version + PharIoVersion::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 + Template::class => 1, + // phpunit/php-timer + Timer::class => 1, + // sebastian/cli-parser + CliParser::class => 1, + // sebastian/code-unit + CodeUnit::class => 1, + // sebastian/code-unit-reverse-lookup + Wizard::class => 1, + // sebastian/comparator + Comparator::class => 1, + // sebastian/complexity + Calculator::class => 1, + // sebastian/diff + Diff::class => 1, + // sebastian/environment + Runtime::class => 1, + // sebastian/exporter + Exporter::class => 1, + // sebastian/global-state + Snapshot::class => 1, + // sebastian/lines-of-code + Counter::class => 1, + // sebastian/object-enumerator + Enumerator::class => 1, + // sebastian/object-reflector + ObjectReflector::class => 1, + // sebastian/recursion-context + Context::class => 1, + // sebastian/type + TypeName::class => 1, + // sebastian/version + Version::class => 1, + // theseer/tokenizer + Tokenizer::class => 1, + ]; /** - * Returns true when the runtime used is PHP without the PHPDBG SAPI. + * @psalm-var list */ - public function isPHP() : bool - { - return !$this->isHHVM() && !$this->isPHPDBG(); - } + private static array $directories = []; + private static bool $initialized = \false; + private readonly bool $enabled; /** - * Returns true when the runtime used is PHP with the PHPDBG SAPI. + * @psalm-param non-empty-string $directory + * + * @throws InvalidDirectoryException */ - public function isPHPDBG() : bool + public static function addDirectory(string $directory): void { - return PHP_SAPI === 'phpdbg' && !$this->isHHVM(); + if (!is_dir($directory)) { + throw new \PHPUnit\Util\InvalidDirectoryException($directory); + } + self::$directories[] = realpath($directory); } - /** - * 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 + public function __construct(?bool $enabled = null) { - return $this->isPHPDBG(); + if ($enabled === null) { + $enabled = !defined('PHPUNIT_TESTSUITE'); + } + $this->enabled = $enabled; } /** - * Returns true when the runtime used is PHP with PCOV loaded and enabled. + * @psalm-return list */ - public function hasPCOV() : bool + public function getExcludedDirectories(): array { - return $this->isPHP() && extension_loaded('pcov') && ini_get('pcov.enabled'); + self::initialize(); + return self::$directories; } - /** - * 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 + public function isExcluded(string $file): bool { - $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))); + if (!$this->enabled) { + return \false; } - foreach ($files as $ini) { - $config = parse_ini_file($ini, \true); - foreach ($values as $value) { - $set = ini_get($value); - if (empty($set)) { - continue; - } - if (!isset($config[$value]) || $set !== $config[$value]) { - $diff[$value] = sprintf('%s=%s', $value, $set); - } + self::initialize(); + foreach (self::$directories as $directory) { + if (str_starts_with($file, $directory)) { + return \true; } } - return $diff; + return \false; } - private function isOpcacheActive() : bool + private static function initialize(): void { - if (!extension_loaded('Zend OPcache')) { - return \false; + if (self::$initialized) { + return; } - if ((PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') && ini_get('opcache.enable_cli') === '1') { - return \true; + foreach (self::EXCLUDED_CLASS_NAMES as $className => $parent) { + if (!class_exists($className)) { + continue; + } + $directory = (new ReflectionClass($className))->getFileName(); + for ($i = 0; $i < $parent; $i++) { + $directory = dirname($directory); + } + self::$directories[] = $directory; } - if (PHP_SAPI !== 'cli' && PHP_SAPI !== 'phpdbg' && ini_get('opcache.enable') === '1') { - return \true; + /** + * Hide process isolation workaround on Windows: + * tempnam() prefix is limited to first 3 characters. + * + * @see https://php.net/manual/en/function.tempnam.php + */ + if (\PHP_OS_FAMILY === 'Windows') { + // @codeCoverageIgnoreStart + self::$directories[] = sys_get_temp_dir() . '\PHP'; + // @codeCoverageIgnoreEnd } - return \false; + self::$initialized = \true; } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnitPHAR\SebastianBergmann\Exporter; +namespace PHPUnit\Util; -use function bin2hex; -use function count; -use function function_exists; -use function get_class; -use function get_resource_type; -use function gettype; -use function implode; -use function ini_get; -use function ini_set; use function is_array; -use function is_float; -use function is_object; -use function is_resource; -use function is_string; -use function mb_strlen; -use function mb_substr; -use function preg_match; -use function spl_object_hash; -use function sprintf; -use function str_repeat; -use function str_replace; -use function strlen; -use function substr; -use function var_export; +use function is_scalar; use PHPUnitPHAR\SebastianBergmann\RecursionContext\Context; -use SplObjectStorage; /** - * A nifty utility for visualizing PHP variables. - * - * - * export(new Exception); - * + * @deprecated */ -class Exporter +final 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) + public static function export(mixed $value, bool $exportObjects = \false): string { - return $this->recursiveExport($value, $indentation); + if (self::isExportable($value) || $exportObjects) { + return (new \PHPUnitPHAR\SebastianBergmann\Exporter\Exporter())->export($value); + } + return '{enable export of objects to see this value}'; } - /** - * @param array $data - * @param Context $context - * - * @return string - */ - public function shortenedRecursiveExport(&$data, ?Context $context = null) + private static function isExportable(mixed &$value, ?Context $context = null): bool { - $result = []; - $exporter = new self(); + if (is_scalar($value) || $value === null) { + return \true; + } + if (!is_array($value)) { + return \false; + } 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); - } + if ($context->contains($value) !== \false) { + return \true; } - 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); - } + $array = $value; + $context->add($value); + foreach ($array as &$_value) { + if (!self::isExportable($_value, $context)) { + return \false; } - 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); + return \true; } - /** - * Converts an object to an array containing all of its private, protected - * and public properties. - * - * @return array - */ - public function toArray($value) +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Util; + +use function basename; +use function dirname; +use function is_dir; +use function mkdir; +use function realpath; +use function str_starts_with; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class Filesystem +{ + public static function createDirectory(string $directory): bool { - 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 ("\x00Error\x00trace" === $key || "\x00Exception\x00trace" === $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 === "\x00gcdata") { - 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; + return !(!is_dir($directory) && !@mkdir($directory, 0777, \true) && !is_dir($directory)); } /** - * 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 + * @psalm-param non-empty-string $path * - * @return string - * - * @see SebastianBergmann\Exporter\Exporter::export + * @return false|non-empty-string */ - protected function recursiveExport(&$value, $indentation, $processed = null) + public static function resolveStreamOrFile(string $path): false|string { - if ($value === null) { - return 'null'; - } - if ($value === \true) { - return 'true'; - } - if ($value === \false) { - return 'false'; - } - if (is_float($value)) { - $precisionBackup = ini_get('precision'); - ini_set('precision', '-1'); - try { - $valueStr = (string) $value; - if ((string) (int) $value === $valueStr) { - return $valueStr . '.0'; - } - return $valueStr; - } finally { - ini_set('precision', $precisionBackup); - } - } - if (gettype($value) === 'resource (closed)') { - return 'resource (closed)'; - } - if (is_resource($value)) { - return sprintf('resource(%d) of type (%s)', $value, get_resource_type($value)); - } - if (is_string($value)) { - // Match for most non printable chars somewhat taking multibyte chars into account - if (preg_match('/[^\\x09-\\x0d\\x1b\\x20-\\xff]/', $value)) { - return 'Binary String: 0x' . bin2hex($value); - } - return "'" . str_replace('', "\n", str_replace(["\r\n", "\n\r", "\r", "\n"], ['\\r\\n', '\\n\\r', '\\r', '\\n'], $value)) . "'"; - } - $whitespace = str_repeat(' ', 4 * $indentation); - if (!$processed) { - $processed = new Context(); - } - if (is_array($value)) { - if (($key = $processed->contains($value)) !== \false) { - return 'Array &' . $key; - } - $array = $value; - $key = $processed->add($value); - $values = ''; - if (count($array) > 0) { - foreach ($array as $k => $v) { - $values .= sprintf('%s %s => %s' . "\n", $whitespace, $this->recursiveExport($k, $indentation), $this->recursiveExport($value[$k], $indentation + 1, $processed)); - } - $values = "\n" . $values . $whitespace; - } - return sprintf('Array &%s (%s)', $key, $values); + if (str_starts_with($path, 'php://') || str_starts_with($path, 'socket://')) { + return $path; } - 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); + $directory = dirname($path); + if (is_dir($directory)) { + return realpath($directory) . \DIRECTORY_SEPARATOR . basename($path); } - return var_export($value, \true); + return \false; } } -Exporter - -Copyright (c) 2002-2021, Sebastian Bergmann . -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the - distribution. - - * Neither the name of Sebastian Bergmann nor the names of his - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN -ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnitPHAR\SebastianBergmann\GlobalState; +namespace PHPUnit\Util; -use const PHP_EOL; -use function is_array; -use function is_scalar; -use function serialize; +use function array_unshift; +use function defined; +use function in_array; +use function is_file; +use function realpath; use function sprintf; -use function var_export; +use function str_starts_with; +use PHPUnit\Framework\Exception; +use PHPUnit\Framework\PhptAssertionFailedError; +use Throwable; /** - * Exports parts of a Snapshot as PHP code. + * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class CodeExporter +final class Filter { - public function constants(Snapshot $snapshot) : string + /** + * @throws Exception + */ + public static function getFilteredStacktrace(Throwable $t, bool $unwrap = \true): string { - $result = ''; - foreach ($snapshot->constants() as $name => $value) { - $result .= sprintf('if (!defined(\'%s\')) define(\'%s\', %s);' . "\n", $name, $name, $this->exportVariable($value)); + $filteredStacktrace = ''; + if ($t instanceof PhptAssertionFailedError) { + $eTrace = $t->syntheticTrace(); + $eFile = $t->syntheticFile(); + $eLine = $t->syntheticLine(); + } elseif ($t instanceof Exception) { + $eTrace = $t->getSerializableTrace(); + $eFile = $t->getFile(); + $eLine = $t->getLine(); + } else { + if ($unwrap && $t->getPrevious()) { + $t = $t->getPrevious(); + } + $eTrace = $t->getTrace(); + $eFile = $t->getFile(); + $eLine = $t->getLine(); } - return $result; - } - public function globalVariables(Snapshot $snapshot) : string - { - $result = <<<'EOT' -call_user_func( - function () - { - foreach (array_keys($GLOBALS) as $key) { - unset($GLOBALS[$key]); + if (!self::frameExists($eTrace, $eFile, $eLine)) { + array_unshift($eTrace, ['file' => $eFile, 'line' => $eLine]); } - } -); - - -EOT; - foreach ($snapshot->globalVariables() as $name => $value) { - $result .= sprintf('$GLOBALS[%s] = %s;' . PHP_EOL, $this->exportVariable($name), $this->exportVariable($value)); + $prefix = defined('__PHPUNIT_PHAR_ROOT__') ? __PHPUNIT_PHAR_ROOT__ : \false; + $excludeList = new \PHPUnit\Util\ExcludeList(); + foreach ($eTrace as $frame) { + if (self::shouldPrintFrame($frame, $prefix, $excludeList)) { + $filteredStacktrace .= sprintf("%s:%s\n", $frame['file'], $frame['line'] ?? '?'); + } } - return $result; + return $filteredStacktrace; } - public function iniSettings(Snapshot $snapshot) : string + private static function shouldPrintFrame(array $frame, false|string $prefix, \PHPUnit\Util\ExcludeList $excludeList): bool { - $result = ''; - foreach ($snapshot->iniSettings() as $key => $value) { - $result .= sprintf('@ini_set(%s, %s);' . "\n", $this->exportVariable($key), $this->exportVariable($value)); + if (!isset($frame['file'])) { + return \false; } - return $result; + $file = $frame['file']; + $fileIsNotPrefixed = $prefix === \false || !str_starts_with($file, $prefix); + // @see https://github.com/sebastianbergmann/phpunit/issues/4033 + if (isset($GLOBALS['_SERVER']['SCRIPT_NAME'])) { + $script = realpath($GLOBALS['_SERVER']['SCRIPT_NAME']); + } else { + $script = ''; + } + return $fileIsNotPrefixed && $file !== $script && self::fileIsExcluded($file, $excludeList) && is_file($file); } - private function exportVariable($variable) : string + private static function fileIsExcluded(string $file, \PHPUnit\Util\ExcludeList $excludeList): bool { - if (is_scalar($variable) || null === $variable || is_array($variable) && $this->arrayOnlyContainsScalars($variable)) { - return var_export($variable, \true); - } - return 'unserialize(' . var_export(serialize($variable), \true) . ')'; + return (empty($GLOBALS['__PHPUNIT_ISOLATION_EXCLUDE_LIST']) || !in_array($file, $GLOBALS['__PHPUNIT_ISOLATION_EXCLUDE_LIST'], \true)) && !$excludeList->isExcluded($file); } - private function arrayOnlyContainsScalars(array $array) : bool + private static function frameExists(array $trace, string $file, int $line): 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; + foreach ($trace as $frame) { + if (isset($frame['file'], $frame['line']) && $frame['file'] === $file && $frame['line'] === $line) { + return \true; } } - return $result; + return \false; } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnitPHAR\SebastianBergmann\GlobalState; +namespace PHPUnit\Util; +use const PHP_MAJOR_VERSION; +use const PHP_MINOR_VERSION; +use function array_keys; +use function array_reverse; +use function array_shift; +use function defined; +use function get_defined_constants; +use function get_included_files; use function in_array; -use function strpos; -use ReflectionClass; -final class ExcludeList +use function ini_get_all; +use function is_array; +use function is_file; +use function is_scalar; +use function preg_match; +use function serialize; +use function sprintf; +use function str_ends_with; +use function str_starts_with; +use function strtr; +use function var_export; +use Closure; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class GlobalState { /** - * @var array - */ - private $globalVariables = []; - /** - * @var string[] - */ - private $classes = []; - /** - * @var string[] - */ - private $classNamePrefixes = []; - /** - * @var string[] + * @psalm-var list */ - private $parentClasses = []; + private const SUPER_GLOBAL_ARRAYS = ['_ENV', '_POST', '_GET', '_COOKIE', '_SERVER', '_FILES', '_REQUEST']; /** - * @var string[] + * @psalm-var array> */ - private $interfaces = []; + private const DEPRECATED_INI_SETTINGS = ['7.3' => ['iconv.input_encoding' => \true, 'iconv.output_encoding' => \true, 'iconv.internal_encoding' => \true, 'mbstring.func_overload' => \true, 'mbstring.http_input' => \true, 'mbstring.http_output' => \true, 'mbstring.internal_encoding' => \true, 'string.strip_tags' => \true], '7.4' => ['iconv.input_encoding' => \true, 'iconv.output_encoding' => \true, 'iconv.internal_encoding' => \true, 'mbstring.func_overload' => \true, 'mbstring.http_input' => \true, 'mbstring.http_output' => \true, 'mbstring.internal_encoding' => \true, 'pdo_odbc.db2_instance_name' => \true, 'string.strip_tags' => \true], '8.0' => ['iconv.input_encoding' => \true, 'iconv.output_encoding' => \true, 'iconv.internal_encoding' => \true, 'mbstring.http_input' => \true, 'mbstring.http_output' => \true, 'mbstring.internal_encoding' => \true], '8.1' => ['auto_detect_line_endings' => \true, 'filter.default' => \true, 'iconv.input_encoding' => \true, 'iconv.output_encoding' => \true, 'iconv.internal_encoding' => \true, 'mbstring.http_input' => \true, 'mbstring.http_output' => \true, 'mbstring.internal_encoding' => \true, 'oci8.old_oci_close_semantics' => \true], '8.2' => ['auto_detect_line_endings' => \true, 'filter.default' => \true, 'iconv.input_encoding' => \true, 'iconv.output_encoding' => \true, 'iconv.internal_encoding' => \true, 'mbstring.http_input' => \true, 'mbstring.http_output' => \true, 'mbstring.internal_encoding' => \true, 'oci8.old_oci_close_semantics' => \true], '8.3' => ['auto_detect_line_endings' => \true, 'filter.default' => \true, 'iconv.input_encoding' => \true, 'iconv.output_encoding' => \true, 'iconv.internal_encoding' => \true, 'mbstring.http_input' => \true, 'mbstring.http_output' => \true, 'mbstring.internal_encoding' => \true, 'oci8.old_oci_close_semantics' => \true]]; /** - * @var array + * @throws Exception */ - private $staticAttributes = []; - public function addGlobalVariable(string $variableName) : void + public static function getIncludedFilesAsString(): string { - $this->globalVariables[$variableName] = \true; - } - public function addClass(string $className) : void - { - $this->classes[] = $className; + return self::processIncludedFilesAsString(get_included_files()); } - public function addSubclassesOf(string $className) : void + /** + * @psalm-param list $files + * + * @throws Exception + */ + public static function processIncludedFilesAsString(array $files): string { - $this->parentClasses[] = $className; + $excludeList = new \PHPUnit\Util\ExcludeList(); + $prefix = \false; + $result = ''; + if (defined('__PHPUNIT_PHAR__')) { + $prefix = 'phar://' . __PHPUNIT_PHAR__ . '/'; + } + // Do not process bootstrap script + array_shift($files); + // If bootstrap script was a Composer bin proxy, skip the second entry as well + if (str_ends_with(strtr($files[0], '\\', '/'), '/phpunit/phpunit/phpunit')) { + array_shift($files); + } + foreach (array_reverse($files) as $file) { + if (!empty($GLOBALS['__PHPUNIT_ISOLATION_EXCLUDE_LIST']) && in_array($file, $GLOBALS['__PHPUNIT_ISOLATION_EXCLUDE_LIST'], \true)) { + continue; + } + if ($prefix !== \false && str_starts_with($file, $prefix)) { + continue; + } + // Skip virtual file system protocols + if (preg_match('/^(vfs|phpvfs[a-z0-9]+):/', $file)) { + continue; + } + if (!$excludeList->isExcluded($file) && is_file($file)) { + $result = 'require_once \'' . $file . "';\n" . $result; + } + } + return $result; } - public function addImplementorsOf(string $interfaceName) : void + public static function getIniSettingsAsString(): string { - $this->interfaces[] = $interfaceName; + $result = ''; + foreach (ini_get_all(null, \false) as $key => $value) { + if (self::isIniSettingDeprecated($key)) { + continue; + } + $result .= sprintf('@ini_set(%s, %s);' . "\n", self::exportVariable($key), self::exportVariable((string) $value)); + } + return $result; } - public function addClassNamePrefix(string $classNamePrefix) : void + public static function getConstantsAsString(): string { - $this->classNamePrefixes[] = $classNamePrefix; + $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 function addStaticAttribute(string $className, string $attributeName) : void + public static function getGlobalsAsString(): string { - if (!isset($this->staticAttributes[$className])) { - $this->staticAttributes[$className] = []; + $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])); + } + } + } + $excludeList = self::SUPER_GLOBAL_ARRAYS; + $excludeList[] = 'GLOBALS'; + foreach (array_keys($GLOBALS) as $key) { + if (!$GLOBALS[$key] instanceof Closure && !in_array($key, $excludeList, \true)) { + $result .= sprintf('$GLOBALS[\'%s\'] = %s;' . "\n", $key, self::exportVariable($GLOBALS[$key])); + } } - $this->staticAttributes[$className][$attributeName] = \true; + return $result; } - public function isGlobalVariableExcluded(string $variableName) : bool + private static function exportVariable(mixed $variable): string { - return isset($this->globalVariables[$variableName]); + if (is_scalar($variable) || $variable === null || is_array($variable) && self::arrayOnlyContainsScalars($variable)) { + return var_export($variable, \true); + } + return 'unserialize(' . var_export(serialize($variable), \true) . ')'; } - public function isStaticAttributeExcluded(string $className, string $attributeName) : bool + private static function arrayOnlyContainsScalars(array $array): bool { - if (in_array($className, $this->classes, \true)) { - 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; + $result = \true; + foreach ($array as $element) { + if (is_array($element)) { + $result = self::arrayOnlyContainsScalars($element); + } elseif (!is_scalar($element) && $element !== null) { + $result = \false; } - } - foreach ($this->interfaces as $type) { - if ($class->implementsInterface($type)) { - return \true; + if (!$result) { + break; } } - if (isset($this->staticAttributes[$className][$attributeName])) { - return \true; - } - return \false; + return $result; + } + private static function isIniSettingDeprecated(string $iniSetting): bool + { + return isset(self::DEPRECATED_INI_SETTINGS[PHP_MAJOR_VERSION . '.' . PHP_MINOR_VERSION][$iniSetting]); } } -sebastian/global-state - -Copyright (c) 2001-2022, Sebastian Bergmann . -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the - distribution. - - * Neither the name of Sebastian Bergmann nor the names of his - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN -ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnitPHAR\SebastianBergmann\GlobalState; +namespace PHPUnit\Util; -use function array_diff; -use function array_key_exists; -use function array_keys; -use function array_merge; -use function function_exists; -use function get_defined_functions; -use function in_array; +use const JSON_PRETTY_PRINT; +use const JSON_UNESCAPED_SLASHES; +use const JSON_UNESCAPED_UNICODE; +use function count; use function is_array; -use ReflectionClass; -use ReflectionProperty; +use function is_object; +use function json_decode; +use function json_encode; +use function json_last_error; +use function ksort; /** - * Restorer of snapshots of global state. + * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -class Restorer +final class Json { /** - * 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 + * @throws InvalidJsonException */ - public function restoreFunctions(Snapshot $snapshot) : void + public static function prettify(string $json): string { - if (!function_exists('PHPUnitPHAR\\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); + $decodedJson = json_decode($json, \false); + if (json_last_error()) { + throw new \PHPUnit\Util\InvalidJsonException(); } + return json_encode($decodedJson, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); } /** - * Restores all global and super-global variables from a snapshot. + * 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 function restoreGlobalVariables(Snapshot $snapshot) : void + public static function canonicalize(string $json): array { - $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, \true) && !$snapshot->excludeList()->isGlobalVariableExcluded($key)) { - if (array_key_exists($key, $globalVariables)) { - $GLOBALS[$key] = $globalVariables[$key]; - } else { - unset($GLOBALS[$key]); - } - } + $decodedJson = json_decode($json); + if (json_last_error()) { + return [\true, null]; } + self::recursiveSort($decodedJson); + $reencodedJson = json_encode($decodedJson); + return [\false, $reencodedJson]; } /** - * Restores all static attributes in user-defined classes from this snapshot. + * 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. */ - public function restoreStaticAttributes(Snapshot $snapshot) : void + private static function recursiveSort(mixed &$json): void { - $current = new Snapshot($snapshot->excludeList(), \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(null, $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->excludeList()->isStaticAttributeExcluded($className, $name)) { - continue; - } - if (!isset($defaults[$name])) { - continue; - } - $attribute->setAccessible(\true); - $attribute->setValue(null, $defaults[$name]); + 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; } } - } - /** - * 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]); - } - } + ksort($json); + foreach ($json as &$value) { + self::recursiveSort($value); } } } @@ -102704,1675 +93891,6388 @@ class Restorer declare (strict_types=1); /* - * This file is part of sebastian/global-state. + * This file is part of PHPUnit. * * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnitPHAR\SebastianBergmann\GlobalState; +namespace PHPUnit\Util\PHP; -use const PHP_VERSION_ID; +use const PHP_BINARY; +use const PHP_SAPI; use function array_keys; use function array_merge; -use function array_reverse; -use function func_get_args; -use function get_declared_classes; -use function get_declared_interfaces; -use function get_declared_traits; -use function get_defined_constants; -use function get_defined_functions; -use function get_included_files; -use function in_array; +use function assert; +use function explode; +use function file_exists; +use function file_get_contents; use function ini_get_all; -use function is_array; -use function is_object; -use function is_resource; -use function is_scalar; -use function serialize; +use function restore_error_handler; +use function set_error_handler; +use function trim; +use function unlink; use function unserialize; -use ReflectionClass; -use PHPUnitPHAR\SebastianBergmann\ObjectReflector\ObjectReflector; -use PHPUnitPHAR\SebastianBergmann\RecursionContext\Context; -use Throwable; +use ErrorException; +use PHPUnit\Event\Code\TestMethodBuilder; +use PHPUnit\Event\Code\ThrowableBuilder; +use PHPUnit\Event\Facade; +use PHPUnit\Event\NoPreviousThrowableException; +use PHPUnit\Event\TestData\MoreThanOneDataSetFromDataProviderException; +use PHPUnit\Framework\AssertionFailedError; +use PHPUnit\Framework\Exception; +use PHPUnit\Framework\Test; +use PHPUnit\Framework\TestCase; +use PHPUnit\Runner\CodeCoverage; +use PHPUnit\TestRunner\TestResult\PassedTests; +use PHPUnitPHAR\SebastianBergmann\Environment\Runtime; /** - * A snapshot of global state. + * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -class Snapshot +abstract class AbstractPhpProcess { + protected bool $stderrRedirection = \false; + protected string $stdin = ''; + protected string $arguments = ''; /** - * @var ExcludeList - */ - private $excludeList; - /** - * @var array - */ - private $globalVariables = []; - /** - * @var array + * @psalm-var array */ - private $superGlobalArrays = []; + protected array $env = []; + public static function factory(): self + { + return new \PHPUnit\Util\PHP\DefaultPhpProcess(); + } /** - * @var array + * Defines if should use STDERR redirection or not. + * + * Then $stderrRedirection is TRUE, STDERR is redirected to STDOUT. */ - private $superGlobalVariables = []; + public function setUseStderrRedirection(bool $stderrRedirection): void + { + $this->stderrRedirection = $stderrRedirection; + } /** - * @var array + * Returns TRUE if uses STDERR redirection or FALSE if not. */ - private $staticAttributes = []; + public function useStderrRedirection(): bool + { + return $this->stderrRedirection; + } /** - * @var array + * Sets the input string to be sent via STDIN. */ - private $iniSettings = []; + public function setStdin(string $stdin): void + { + $this->stdin = $stdin; + } /** - * @var array + * Returns the input string to be sent via STDIN. */ - private $includedFiles = []; + public function getStdin(): string + { + return $this->stdin; + } /** - * @var array + * Sets the string of arguments to pass to the php job. */ - private $constants = []; + public function setArgs(string $arguments): void + { + $this->arguments = $arguments; + } /** - * @var array + * Returns the string of arguments to pass to the php job. */ - private $functions = []; + public function getArgs(): string + { + return $this->arguments; + } /** - * @var array + * Sets the array of environment variables to start the child process with. + * + * @psalm-param array $env */ - private $interfaces = []; + public function setEnv(array $env): void + { + $this->env = $env; + } /** - * @var array + * Returns the array of environment variables to start the child process with. */ - private $classes = []; + public function getEnv(): array + { + return $this->env; + } /** - * @var array + * Runs a single test in a separate PHP process. + * + * @throws \PHPUnit\Runner\Exception + * @throws Exception + * @throws MoreThanOneDataSetFromDataProviderException + * @throws NoPreviousThrowableException */ - private $traits = []; + public function runTestJob(string $job, Test $test, string $processResultFile): void + { + $_result = $this->runJob($job); + $processResult = ''; + if (file_exists($processResultFile)) { + $processResult = file_get_contents($processResultFile); + @unlink($processResultFile); + } + $this->processChildResult($test, $processResult, $_result['stderr']); + } /** - * Creates a snapshot of the current global state. + * Returns the command based into the configurations. + * + * @return string[] */ - public function __construct(?ExcludeList $excludeList = 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) + public function getCommand(array $settings, ?string $file = null): array { - $this->excludeList = $excludeList ?: new ExcludeList(); - if ($includeConstants) { - $this->snapshotConstants(); + $runtime = new Runtime(); + $command = []; + $command[] = PHP_BINARY; + if ($runtime->hasPCOV()) { + $settings = array_merge($settings, $runtime->getCurrentSettings(array_keys(ini_get_all('pcov')))); + } elseif ($runtime->hasXdebug()) { + $settings = array_merge($settings, $runtime->getCurrentSettings(array_keys(ini_get_all('xdebug')))); } - if ($includeFunctions) { - $this->snapshotFunctions(); + $command = array_merge($command, $this->settingsToParameters($settings)); + if (PHP_SAPI === 'phpdbg') { + $command[] = '-qrr'; + if (!$file) { + $command[] = 's='; + } } - if ($includeClasses || $includeStaticAttributes) { - $this->snapshotClasses(); + if ($file) { + $command[] = '-f'; + $command[] = $file; } - if ($includeInterfaces) { - $this->snapshotInterfaces(); + if ($this->arguments) { + if (!$file) { + $command[] = '--'; + } + foreach (explode(' ', $this->arguments) as $arg) { + $command[] = trim($arg); + } } - if ($includeGlobalVariables) { - $this->setupSuperGlobalArrays(); - $this->snapshotGlobals(); + return $command; + } + /** + * Runs a single job (PHP code) using a separate PHP process. + */ + abstract public function runJob(string $job, array $settings = []): array; + /** + * @return list + */ + protected function settingsToParameters(array $settings): array + { + $buffer = []; + foreach ($settings as $setting) { + $buffer[] = '-d'; + $buffer[] = $setting; } - if ($includeStaticAttributes) { - $this->snapshotStaticAttributes(); + return $buffer; + } + /** + * @throws \PHPUnit\Runner\Exception + * @throws Exception + * @throws MoreThanOneDataSetFromDataProviderException + * @throws NoPreviousThrowableException + */ + private function processChildResult(Test $test, string $stdout, string $stderr): void + { + if (!empty($stderr)) { + $exception = new Exception(trim($stderr)); + assert($test instanceof TestCase); + Facade::emitter()->testErrored(TestMethodBuilder::fromTestCase($test), ThrowableBuilder::from($exception)); + return; } - if ($includeIniSettings) { - $this->iniSettings = ini_get_all(null, \false); + set_error_handler( + /** + * @throws ErrorException + */ + static function (int $errno, string $errstr, string $errfile, int $errline): never { + throw new ErrorException($errstr, $errno, $errno, $errfile, $errline); + } + ); + try { + $childResult = unserialize($stdout); + restore_error_handler(); + if ($childResult === \false) { + $exception = new AssertionFailedError('Test was run in child process and ended unexpectedly'); + assert($test instanceof TestCase); + Facade::emitter()->testErrored(TestMethodBuilder::fromTestCase($test), ThrowableBuilder::from($exception)); + Facade::emitter()->testFinished(TestMethodBuilder::fromTestCase($test), 0); + } + } catch (ErrorException $e) { + restore_error_handler(); + $childResult = \false; + $exception = new Exception(trim($stdout), 0, $e); + assert($test instanceof TestCase); + Facade::emitter()->testErrored(TestMethodBuilder::fromTestCase($test), ThrowableBuilder::from($exception)); } - if ($includeIncludedFiles) { - $this->includedFiles = get_included_files(); + if ($childResult !== \false) { + if (!empty($childResult['output'])) { + $output = $childResult['output']; + } + Facade::instance()->forward($childResult['events']); + PassedTests::instance()->import($childResult['passedTests']); + assert($test instanceof TestCase); + $test->setResult($childResult['testResult']); + $test->addToAssertionCount($childResult['numAssertions']); + if (CodeCoverage::instance()->isActive() && $childResult['codeCoverage'] instanceof \PHPUnitPHAR\SebastianBergmann\CodeCoverage\CodeCoverage) { + CodeCoverage::instance()->codeCoverage()->merge($childResult['codeCoverage']); + } } - if ($includeTraits) { - $this->traits = get_declared_traits(); + if (!empty($output)) { + print $output; } } - public function excludeList() : ExcludeList - { - return $this->excludeList; - } - public function globalVariables() : array - { - return $this->globalVariables; - } - public function superGlobalVariables() : array - { - return $this->superGlobalVariables; - } - public function superGlobalArrays() : array +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Util\PHP; + +use function array_merge; +use function fclose; +use function file_put_contents; +use function fwrite; +use function is_array; +use function is_resource; +use function proc_close; +use function proc_open; +use function stream_get_contents; +use function sys_get_temp_dir; +use function tempnam; +use function unlink; +use PHPUnit\Framework\Exception; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +class DefaultPhpProcess extends \PHPUnit\Util\PHP\AbstractPhpProcess +{ + private ?string $tempFile = null; + /** + * Runs a single job (PHP code) using a separate PHP process. + * + * @psalm-return array{stdout: string, stderr: string} + * + * @throws Exception + * @throws PhpProcessException + */ + public function runJob(string $job, array $settings = []): array { - return $this->superGlobalArrays; + if ($this->stdin) { + if (!($this->tempFile = tempnam(sys_get_temp_dir(), 'phpunit_')) || file_put_contents($this->tempFile, $job) === \false) { + throw new \PHPUnit\Util\PHP\PhpProcessException('Unable to write temporary file'); + } + $job = $this->stdin; + } + return $this->runProcess($job, $settings); } - public function staticAttributes() : array + /** + * Handles creating the child process and returning the STDOUT and STDERR. + * + * @psalm-return array{stdout: string, stderr: string} + * + * @throws Exception + * @throws PhpProcessException + */ + protected function runProcess(string $job, array $settings): array { - return $this->staticAttributes; + $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 => ['pipe', 'r'], 1 => ['pipe', 'w'], 2 => ['pipe', 'w']]; + if ($this->stderrRedirection) { + $pipeSpec[2] = ['redirect', 1]; + } + $process = proc_open($this->getCommand($settings, $this->tempFile), $pipeSpec, $pipes, null, $env); + if (!is_resource($process)) { + throw new \PHPUnit\Util\PHP\PhpProcessException('Unable to spawn worker process'); + } + if ($job) { + $this->process($pipes[0], $job); + } + fclose($pipes[0]); + $stderr = $stdout = ''; + 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]); + } + proc_close($process); + $this->cleanup(); + return ['stdout' => $stdout, 'stderr' => $stderr]; } - public function iniSettings() : array + /** + * @param resource $pipe + */ + protected function process($pipe, string $job): void { - return $this->iniSettings; + fwrite($pipe, $job); } - public function includedFiles() : array + protected function cleanup(): void { - return $this->includedFiles; + if ($this->tempFile) { + unlink($this->tempFile); + } } - public function constants() : array - { - return $this->constants; +} +initForIsolation( + PHPUnit\Event\Telemetry\HRTime::fromSecondsAndNanoseconds( + {offsetSeconds}, + {offsetNanoseconds} + ), + {exportObjects}, + ); + + require_once '{filename}'; + + if ({collectCodeCoverageInformation}) { + CodeCoverage::instance()->init(ConfigurationRegistry::get(), CodeCoverageFilterRegistry::instance(), true); + CodeCoverage::instance()->ignoreLines({linesToBeIgnored}); } - public function functions() : array - { - return $this->functions; + + $test = new {className}('{name}'); + + $test->setData('{dataName}', unserialize('{data}')); + $test->setDependencyInput(unserialize('{dependencyInput}')); + $test->setInIsolation(true); + + ob_end_clean(); + + $test->run(); + + $output = ''; + + if (!$test->expectsOutput()) { + $output = $test->output(); } - public function interfaces() : array - { - return $this->interfaces; + + ini_set('xdebug.scream', '0'); + + // Not every STDOUT target stream is rewindable + @rewind(STDOUT); + + if ($stdout = @stream_get_contents(STDOUT)) { + $output = $stdout . $output; + $streamMetaData = stream_get_meta_data(STDOUT); + + if (!empty($streamMetaData['stream_type']) && 'STDIO' === $streamMetaData['stream_type']) { + @ftruncate(STDOUT, 0); + @rewind(STDOUT); + } } - public function classes() : array - { - return $this->classes; + + file_put_contents( + '{processResultFile}', + serialize( + [ + 'testResult' => $test->result(), + 'codeCoverage' => {collectCodeCoverageInformation} ? CodeCoverage::instance()->codeCoverage() : null, + 'numAssertions' => $test->numberOfAssertionsPerformed(), + 'output' => $output, + 'events' => $dispatcher->flush(), + 'passedTests' => PassedTests::instance() + ] + ) + ); +} + +function __phpunit_error_handler($errno, $errstr, $errfile, $errline) +{ + return true; +} + +set_error_handler('__phpunit_error_handler'); + +{constants} +{included_files} +{globals} + +restore_error_handler(); + +ConfigurationRegistry::loadFrom('{serializedConfiguration}'); +(new PhpHandler)->handle(ConfigurationRegistry::get()->php()); + +if ('{bootstrap}' !== '') { + require_once '{bootstrap}'; +} + +__phpunit_run_isolated_test(); +initForIsolation( + PHPUnit\Event\Telemetry\HRTime::fromSecondsAndNanoseconds( + {offsetSeconds}, + {offsetNanoseconds} + ), + {exportObjects}, + ); + + require_once '{filename}'; + + if ({collectCodeCoverageInformation}) { + CodeCoverage::instance()->init(ConfigurationRegistry::get(), CodeCoverageFilterRegistry::instance(), true); + CodeCoverage::instance()->ignoreLines({linesToBeIgnored}); } - public function traits() : array - { - return $this->traits; + + $test = new {className}('{methodName}'); + + $test->setData('{dataName}', unserialize('{data}')); + $test->setDependencyInput(unserialize('{dependencyInput}')); + $test->setInIsolation(true); + + ob_end_clean(); + + $test->run(); + + $output = ''; + + if (!$test->expectsOutput()) { + $output = $test->output(); } - /** - * Creates a snapshot user-defined constants. - */ - private function snapshotConstants() : void - { - $constants = get_defined_constants(\true); - if (isset($constants['user'])) { - $this->constants = $constants['user']; + + ini_set('xdebug.scream', '0'); + + // Not every STDOUT target stream is rewindable + @rewind(STDOUT); + + if ($stdout = @stream_get_contents(STDOUT)) { + $output = $stdout . $output; + $streamMetaData = stream_get_meta_data(STDOUT); + + if (!empty($streamMetaData['stream_type']) && 'STDIO' === $streamMetaData['stream_type']) { + @ftruncate(STDOUT, 0); + @rewind(STDOUT); } } - /** - * Creates a snapshot user-defined functions. - */ - private function snapshotFunctions() : void - { - $functions = get_defined_functions(); - $this->functions = $functions['user']; + + file_put_contents( + '{processResultFile}', + serialize( + [ + 'testResult' => $test->result(), + 'codeCoverage' => {collectCodeCoverageInformation} ? CodeCoverage::instance()->codeCoverage() : null, + 'numAssertions' => $test->numberOfAssertionsPerformed(), + 'output' => $output, + 'events' => $dispatcher->flush(), + 'passedTests' => PassedTests::instance() + ] + ) + ); +} + +function __phpunit_error_handler($errno, $errstr, $errfile, $errline) +{ + return true; +} + +set_error_handler('__phpunit_error_handler'); + +{constants} +{included_files} +{globals} + +restore_error_handler(); + +ConfigurationRegistry::loadFrom('{serializedConfiguration}'); +(new PhpHandler)->handle(ConfigurationRegistry::get()->php()); + +if ('{bootstrap}' !== '') { + require_once '{bootstrap}'; +} + +__phpunit_run_isolated_test(); +{driverMethod}($filter), + $filter + ); + + if ({codeCoverageCacheDirectory}) { + $coverage->cacheStaticAnalysis({codeCoverageCacheDirectory}); } - /** - * 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; + + $coverage->start(__FILE__); +} + +register_shutdown_function( + function() use ($coverage) { + $output = null; + + if ($coverage) { + $output = $coverage->stop(); } - $this->classes = array_reverse($this->classes); + + file_put_contents('{coverageFile}', serialize($output)); } +); + +ob_end_clean(); + +require '{job}'; + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Util; + +use function array_keys; +use function array_merge; +use function array_reverse; +use PHPUnit\Framework\Assert; +use PHPUnit\Framework\TestCase; +use ReflectionClass; +use ReflectionException; +use ReflectionMethod; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class Reflection +{ /** - * Creates a snapshot user-defined interfaces. + * @psalm-param class-string $className + * @psalm-param non-empty-string $methodName + * + * @psalm-return array{file: non-empty-string, line: non-negative-int} */ - private function snapshotInterfaces() : void + public static function sourceLocationFor(string $className, string $methodName): array { - foreach (array_reverse(get_declared_interfaces()) as $interfaceName) { - $class = new ReflectionClass($interfaceName); - if (!$class->isUserDefined()) { - break; - } - $this->interfaces[] = $interfaceName; + try { + $reflector = new ReflectionMethod($className, $methodName); + $file = $reflector->getFileName(); + $line = $reflector->getStartLine(); + } catch (ReflectionException) { + $file = 'unknown'; + $line = 0; } - $this->interfaces = array_reverse($this->interfaces); + return ['file' => $file, 'line' => $line]; } /** - * Creates a snapshot of all global and super-global variables. + * @psalm-return list */ - private function snapshotGlobals() : void + public static function publicMethodsInTestClass(ReflectionClass $class): array { - $superGlobalArrays = $this->superGlobalArrays(); - foreach ($superGlobalArrays as $superGlobalArray) { - $this->snapshotSuperGlobalArray($superGlobalArray); - } - foreach (array_keys($GLOBALS) as $key) { - if ($key !== 'GLOBALS' && !in_array($key, $superGlobalArrays, \true) && $this->canBeSerialized($GLOBALS[$key]) && !$this->excludeList->isGlobalVariableExcluded($key)) { - /* @noinspection UnserializeExploitsInspection */ - $this->globalVariables[$key] = unserialize(serialize($GLOBALS[$key])); - } - } + return self::filterAndSortMethods($class, ReflectionMethod::IS_PUBLIC, \true); } /** - * Creates a snapshot a super-global variable array. + * @psalm-return list */ - private function snapshotSuperGlobalArray(string $superGlobalArray) : void + public static function methodsInTestClass(ReflectionClass $class): array { - $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)); - } - } + return self::filterAndSortMethods($class, null, \false); } /** - * Creates a snapshot of all static attributes in user-defined classes. + * @psalm-return list */ - private function snapshotStaticAttributes() : void + private static function filterAndSortMethods(ReflectionClass $class, ?int $filter, bool $sortHighestToLowest): array { - foreach ($this->classes as $className) { - $class = new ReflectionClass($className); - $snapshot = []; - foreach ($class->getProperties() as $attribute) { - if ($attribute->isStatic()) { - $name = $attribute->getName(); - if ($this->excludeList->isStaticAttributeExcluded($className, $name)) { - continue; - } - $attribute->setAccessible(\true); - if (PHP_VERSION_ID >= 70400 && !$attribute->isInitialized()) { - continue; - } - $value = $attribute->getValue(); - if ($this->canBeSerialized($value)) { - /* @noinspection UnserializeExploitsInspection */ - $snapshot[$name] = unserialize(serialize($value)); - } - } - } - if (!empty($snapshot)) { - $this->staticAttributes[$className] = $snapshot; + $methodsByClass = []; + foreach ($class->getMethods($filter) as $method) { + $declaringClassName = $method->getDeclaringClass()->getName(); + if ($declaringClassName === TestCase::class) { + continue; } - } - } - /** - * 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 ($declaringClassName === Assert::class) { + continue; } - if (is_object($value)) { - $class = new ReflectionClass($value); - if ($class->isAnonymous()) { - return \false; - } - try { - @serialize($value); - } catch (Throwable $t) { - return \false; - } + if (!isset($methodsByClass[$declaringClassName])) { + $methodsByClass[$declaringClassName] = []; } + $methodsByClass[$declaringClassName][] = $method; } - 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; + $classNames = array_keys($methodsByClass); + if ($sortHighestToLowest) { + $classNames = array_reverse($classNames); } - $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; - } - } + $methods = []; + foreach ($classNames as $className) { + $methods = array_merge($methods, $methodsByClass[$className]); } - return $result; + return $methods; } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnitPHAR\SebastianBergmann\GlobalState; +namespace PHPUnit\Util; -use Throwable; -interface Exception extends Throwable +use function str_starts_with; +use PHPUnit\Metadata\Parser\Registry; +use ReflectionMethod; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class Test { + public static function isTestMethod(ReflectionMethod $method): bool + { + if (!$method->isPublic()) { + return \false; + } + if (str_starts_with($method->getName(), 'test')) { + return \true; + } + $metadata = Registry::parser()->forMethod($method->getDeclaringClass()->getName(), $method->getName()); + return $metadata->isTest()->isNotEmpty(); + } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnitPHAR\SebastianBergmann\GlobalState; +namespace PHPUnit\Util; -final class RuntimeException extends \RuntimeException implements Exception +use function trim; +use PHPUnit\Framework\ExpectationFailedException; +use PHPUnit\Framework\PhptAssertionFailedError; +use PHPUnit\Framework\SelfDescribing; +use PHPUnit\Runner\ErrorException; +use Throwable; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class ThrowableToStringMapper { + public static function map(Throwable $t): string + { + if ($t instanceof ErrorException) { + return $t->getMessage(); + } + if ($t instanceof SelfDescribing) { + $buffer = $t->toString(); + if ($t instanceof ExpectationFailedException && $t->getComparisonFailure()) { + $buffer .= $t->getComparisonFailure()->getDiff(); + } + if ($t instanceof PhptAssertionFailedError) { + $buffer .= $t->diff(); + } + if (!empty($buffer)) { + $buffer = trim($buffer) . "\n"; + } + return $buffer; + } + return $t::class . ': ' . $t->getMessage() . "\n"; + } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnitPHAR\SebastianBergmann\LinesOfCode; +namespace PHPUnit\Util; -use function substr_count; -use PHPUnitPHAR\PhpParser\Error; -use PHPUnitPHAR\PhpParser\Node; -use PHPUnitPHAR\PhpParser\NodeTraverser; -use PHPUnitPHAR\PhpParser\ParserFactory; -final class Counter +use function in_array; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @psalm-immutable + */ +final class VersionComparisonOperator { /** - * @throws RuntimeException + * @psalm-var '<'|'lt'|'<='|'le'|'>'|'gt'|'>='|'ge'|'=='|'='|'eq'|'!='|'<>'|'ne' + */ + private readonly string $operator; + /** + * @psalm-param '<'|'lt'|'<='|'le'|'>'|'gt'|'>='|'ge'|'=='|'='|'eq'|'!='|'<>'|'ne' $operator + * + * @throws InvalidVersionOperatorException */ - public function countInSourceFile(string $sourceFile) : LinesOfCode + public function __construct(string $operator) { - return $this->countInSourceString(\file_get_contents($sourceFile)); + $this->ensureOperatorIsValid($operator); + $this->operator = $operator; } /** - * @throws RuntimeException + * @psalm-return '<'|'lt'|'<='|'le'|'>'|'gt'|'>='|'ge'|'=='|'='|'eq'|'!='|'<>'|'ne' */ - public function countInSourceString(string $source) : LinesOfCode + public function asString(): string { - $linesOfCode = substr_count($source, "\n"); - if ($linesOfCode === 0 && !empty($source)) { - $linesOfCode = 1; - } - try { - $nodes = (new ParserFactory())->createForHostVersion()->parse($source); - \assert($nodes !== null); - return $this->countInAbstractSyntaxTree($linesOfCode, $nodes); - // @codeCoverageIgnoreStart - } catch (Error $error) { - throw new RuntimeException($error->getMessage(), (int) $error->getCode(), $error); - } - // @codeCoverageIgnoreEnd + return $this->operator; } /** - * @param Node[] $nodes + * @psalm-param '<'|'lt'|'<='|'le'|'>'|'gt'|'>='|'ge'|'=='|'='|'eq'|'!='|'<>'|'ne' $operator * - * @throws RuntimeException + * @throws InvalidVersionOperatorException */ - public function countInAbstractSyntaxTree(int $linesOfCode, array $nodes) : LinesOfCode + private function ensureOperatorIsValid(string $operator): void { - $traverser = new NodeTraverser(); - $visitor = new LineCountingVisitor($linesOfCode); - $traverser->addVisitor($visitor); - try { - /* @noinspection UnusedFunctionResultInspection */ - $traverser->traverse($nodes); - // @codeCoverageIgnoreStart - } catch (Error $error) { - throw new RuntimeException($error->getMessage(), (int) $error->getCode(), $error); + if (!in_array($operator, ['<', 'lt', '<=', 'le', '>', 'gt', '>=', 'ge', '==', '=', 'eq', '!=', '<>', 'ne'], \true)) { + throw new \PHPUnit\Util\InvalidVersionOperatorException($operator); } - // @codeCoverageIgnoreEnd - return $visitor->result(); } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnitPHAR\SebastianBergmann\LinesOfCode; +namespace PHPUnit\Util\Xml; -use Throwable; -interface Exception extends Throwable +use function chdir; +use function dirname; +use function error_reporting; +use function file_get_contents; +use function getcwd; +use function libxml_get_errors; +use function libxml_use_internal_errors; +use function sprintf; +use DOMDocument; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class Loader { + /** + * @throws XmlException + */ + public function loadFile(string $filename): DOMDocument + { + $reporting = error_reporting(0); + $contents = file_get_contents($filename); + error_reporting($reporting); + if ($contents === \false) { + throw new \PHPUnit\Util\Xml\XmlException(sprintf('Could not read XML from file "%s"', $filename)); + } + return $this->load($contents, $filename); + } + /** + * @throws XmlException + */ + public function load(string $actual, ?string $filename = null): DOMDocument + { + if ($actual === '') { + if ($filename === null) { + throw new \PHPUnit\Util\Xml\XmlException('Could not parse XML from empty string'); + } + throw new \PHPUnit\Util\Xml\XmlException(sprintf('Could not parse XML from empty file "%s"', $filename)); + } + $document = new DOMDocument(); + $document->preserveWhiteSpace = \false; + $internal = libxml_use_internal_errors(\true); + $message = ''; + $reporting = error_reporting(0); + // Required for XInclude + if ($filename !== null) { + // Required for XInclude on Windows + if (\PHP_OS_FAMILY === 'Windows') { + $cwd = getcwd(); + @chdir(dirname($filename)); + } + $document->documentURI = $filename; + } + $loaded = $document->loadXML($actual); + if ($filename !== null) { + $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 || $message !== '') { + if ($filename !== null) { + throw new \PHPUnit\Util\Xml\XmlException(sprintf('Could not load "%s"%s', $filename, ($message !== '') ? ":\n" . $message : '')); + } + if ($message === '') { + $message = 'Could not load XML for unknown reason'; + } + throw new \PHPUnit\Util\Xml\XmlException($message); + } + return $document; + } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnitPHAR\SebastianBergmann\LinesOfCode; +namespace PHPUnit\Util; -use LogicException; -final class IllogicalValuesException extends LogicException implements Exception +use const ENT_QUOTES; +use function htmlspecialchars; +use function mb_convert_encoding; +use function ord; +use function preg_replace; +use function strlen; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class Xml { + /** + * 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)); + } + 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; + } } - + + + + phpunit + phpunit + 10.5.26 + The PHP Unit Testing framework. + + + BSD-3-Clause + + + pkg:composer/phpunit/phpunit@10.5.26 + + + myclabs + deep-copy + 1.12.0 + Create deep copies (clones) of your objects + + + MIT + + + pkg:composer/myclabs/deep-copy@1.12.0 + + + nikic + php-parser + v5.1.0 + A PHP parser written in PHP + + + BSD-3-Clause + + + pkg:composer/nikic/php-parser@v5.1.0 + + + phar-io + manifest + 2.0.4 + Component for reading phar.io manifest information from a PHP Archive (PHAR) + + + BSD-3-Clause + + + pkg:composer/phar-io/manifest@2.0.4 + + + phar-io + version + 3.2.1 + Library for handling version information and constraints + + + BSD-3-Clause + + + pkg:composer/phar-io/version@3.2.1 + + + phpunit + php-code-coverage + 10.1.15 + Library that provides collection, processing, and rendering functionality for PHP code coverage information. + + + BSD-3-Clause + + + pkg:composer/phpunit/php-code-coverage@10.1.15 + + + phpunit + php-file-iterator + 4.1.0 + FilterIterator implementation that filters files based on a list of suffixes. + + + BSD-3-Clause + + + pkg:composer/phpunit/php-file-iterator@4.1.0 + + + phpunit + php-invoker + 4.0.0 + Invoke callables with a timeout + + + BSD-3-Clause + + + pkg:composer/phpunit/php-invoker@4.0.0 + + + phpunit + php-text-template + 3.0.1 + Simple template engine. + + + BSD-3-Clause + + + pkg:composer/phpunit/php-text-template@3.0.1 + + + phpunit + php-timer + 6.0.0 + Utility class for timing + + + BSD-3-Clause + + + pkg:composer/phpunit/php-timer@6.0.0 + + + sebastian + cli-parser + 2.0.1 + Library for parsing CLI options + + + BSD-3-Clause + + + pkg:composer/sebastian/cli-parser@2.0.1 + + + sebastian + code-unit + 2.0.0 + Collection of value objects that represent the PHP code units + + + BSD-3-Clause + + + pkg:composer/sebastian/code-unit@2.0.0 + + + sebastian + code-unit-reverse-lookup + 3.0.0 + Looks up which function or method a line of code belongs to + + + BSD-3-Clause + + + pkg:composer/sebastian/code-unit-reverse-lookup@3.0.0 + + + sebastian + comparator + 5.0.1 + Provides the functionality to compare PHP values for equality + + + BSD-3-Clause + + + pkg:composer/sebastian/comparator@5.0.1 + + + sebastian + complexity + 3.2.0 + Library for calculating the complexity of PHP code units + + + BSD-3-Clause + + + pkg:composer/sebastian/complexity@3.2.0 + + + sebastian + diff + 5.1.1 + Diff implementation + + + BSD-3-Clause + + + pkg:composer/sebastian/diff@5.1.1 + + + sebastian + environment + 6.1.0 + Provides functionality to handle HHVM/PHP environments + + + BSD-3-Clause + + + pkg:composer/sebastian/environment@6.1.0 + + + sebastian + exporter + 5.1.2 + Provides the functionality to export PHP variables for visualization + + + BSD-3-Clause + + + pkg:composer/sebastian/exporter@5.1.2 + + + sebastian + global-state + 6.0.2 + Snapshotting of global state + + + BSD-3-Clause + + + pkg:composer/sebastian/global-state@6.0.2 + + + sebastian + lines-of-code + 2.0.2 + Library for counting the lines of code in PHP source code + + + BSD-3-Clause + + + pkg:composer/sebastian/lines-of-code@2.0.2 + + + sebastian + object-enumerator + 5.0.0 + Traverses array structures and object graphs to enumerate all referenced objects + + + BSD-3-Clause + + + pkg:composer/sebastian/object-enumerator@5.0.0 + + + sebastian + object-reflector + 3.0.0 + Allows reflection of object attributes, including inherited and non-public ones + + + BSD-3-Clause + + + pkg:composer/sebastian/object-reflector@3.0.0 + + + sebastian + recursion-context + 5.0.0 + Provides functionality to recursively process PHP variables + + + BSD-3-Clause + + + pkg:composer/sebastian/recursion-context@5.0.0 + + + sebastian + type + 4.0.0 + Collection of value objects that represent the types of the PHP type system + + + BSD-3-Clause + + + pkg:composer/sebastian/type@4.0.0 + + + sebastian + version + 4.0.1 + Library that helps with managing the version number of Git-hosted PHP projects + + + BSD-3-Clause + + + pkg:composer/sebastian/version@4.0.1 + + + theseer + tokenizer + 1.2.3 + A small library for converting tokenized PHP source code into XML and potentially other formats + + + BSD-3-Clause + + + pkg:composer/theseer/tokenizer@1.2.3 + + + + + + + + This Schema file defines the rules by which the XML configuration file of PHPUnit 10.0 may be structured. + + + + + + Root Element + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The main type specifying the document structure + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This Schema file defines the rules by which the XML configuration file of PHPUnit 10.1 may be structured. + + + + + + Root Element + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The main type specifying the document structure + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This Schema file defines the rules by which the XML configuration file of PHPUnit 10.2 may be structured. + + + + + + Root Element + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The main type specifying the document structure + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This Schema file defines the rules by which the XML configuration file of PHPUnit 10.3 may be structured. + + + + + + Root Element + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The main type specifying the document structure + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This Schema file defines the rules by which the XML configuration file of PHPUnit 10.4 may be structured. + + + + + + Root Element + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The main type specifying the document structure + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This Schema file defines the rules by which the XML configuration file of PHPUnit 8.5 may be structured. + + + + + + Root Element + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The main type specifying the document structure + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This Schema file defines the rules by which the XML configuration file of PHPUnit 9.0 may be structured. + + + + + + Root Element + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The main type specifying the document structure + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This Schema file defines the rules by which the XML configuration file of PHPUnit 9.0 may be structured. + + + + + + Root Element + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The main type specifying the document structure + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This Schema file defines the rules by which the XML configuration file of PHPUnit 9.2 may be structured. + + + + + + Root Element + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The main type specifying the document structure + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This Schema file defines the rules by which the XML configuration file of PHPUnit 9.3 may be structured. + + + + + + Root Element + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The main type specifying the document structure + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This Schema file defines the rules by which the XML configuration file of PHPUnit 9.4 may be structured. + + + + + + Root Element + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The main type specifying the document structure + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This Schema file defines the rules by which the XML configuration file of PHPUnit 9.5 may be structured. + + + + + + Root Element + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The main type specifying the document structure + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +BSD 3-Clause License -declare (strict_types=1); -/* - * This file is part of sebastian/lines-of-code. - * - * (c) Sebastian Bergmann - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnitPHAR\SebastianBergmann\LinesOfCode; +Copyright (c) 2020-2024, Sebastian Bergmann +All rights reserved. -use InvalidArgumentException; -final class NegativeValueException extends InvalidArgumentException implements Exception -{ -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnitPHAR\SebastianBergmann\LinesOfCode; +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. -final class RuntimeException extends \RuntimeException implements Exception -{ -} -sebastian/lines-of-code +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. -Copyright (c) 2020, Sebastian Bergmann . -All rights reserved. +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the - distribution. - - * Neither the name of Sebastian Bergmann nor the names of his - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN -ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnitPHAR\SebastianBergmann\LinesOfCode; +namespace PHPUnitPHAR\SebastianBergmann\CliParser; +use function array_map; use function array_merge; -use function array_unique; +use function array_shift; +use function array_slice; +use function assert; use function count; -use PHPUnitPHAR\PhpParser\Comment; -use PHPUnitPHAR\PhpParser\Node; -use PHPUnitPHAR\PhpParser\Node\Expr; -use PHPUnitPHAR\PhpParser\NodeVisitorAbstract; -final class LineCountingVisitor extends NodeVisitorAbstract +use function current; +use function explode; +use function is_array; +use function is_int; +use function is_string; +use function key; +use function next; +use function preg_replace; +use function reset; +use function sort; +use function str_ends_with; +use function str_starts_with; +use function strlen; +use function strstr; +use function substr; +final class Parser { /** - * @var int - */ - private $linesOfCode; - /** - * @var Comment[] - */ - private $comments = []; - /** - * @var int[] + * @psalm-param list $argv + * @psalm-param list $longOptions + * + * @psalm-return array{0: array, 1: array} + * + * @throws AmbiguousOptionException + * @throws OptionDoesNotAllowArgumentException + * @throws RequiredOptionArgumentMissingException + * @throws UnknownOptionException */ - private $linesWithStatements = []; - public function __construct(int $linesOfCode) - { - $this->linesOfCode = $linesOfCode; - } - public function enterNode(Node $node) : void + public function parse(array $argv, string $shortOptions, ?array $longOptions = null): array { - $this->comments = array_merge($this->comments, $node->getComments()); - if (!$node instanceof Expr) { - return; + if (empty($argv)) { + return [[], []]; } - $this->linesWithStatements[] = $node->getStartLine(); - } - public function result() : LinesOfCode - { - $commentLinesOfCode = 0; - foreach ($this->comments() as $comment) { - $commentLinesOfCode += $comment->getEndLine() - $comment->getStartLine() + 1; + $options = []; + $nonOptions = []; + if ($longOptions) { + sort($longOptions); } - return new LinesOfCode($this->linesOfCode, $commentLinesOfCode, $this->linesOfCode - $commentLinesOfCode, count(array_unique($this->linesWithStatements))); - } - /** - * @return Comment[] - */ - private function comments() : array - { - $comments = []; - foreach ($this->comments as $comment) { - $comments[$comment->getStartLine() . '_' . $comment->getStartTokenPos() . '_' . $comment->getEndLine() . '_' . $comment->getEndTokenPos()] = $comment; + if (isset($argv[0][0]) && $argv[0][0] !== '-') { + array_shift($argv); } - return $comments; + reset($argv); + $argv = array_map('trim', $argv); + while (\false !== $arg = current($argv)) { + $i = key($argv); + assert(is_int($i)); + next($argv); + if ($arg === '') { + continue; + } + if ($arg === '--') { + $nonOptions = array_merge($nonOptions, array_slice($argv, $i + 1)); + break; + } + if ($arg[0] !== '-' || strlen($arg) > 1 && $arg[1] === '-' && !$longOptions) { + $nonOptions[] = $arg; + continue; + } + if (strlen($arg) > 1 && $arg[1] === '-' && is_array($longOptions)) { + $this->parseLongOption(substr($arg, 2), $longOptions, $options, $argv); + continue; + } + $this->parseShortOption(substr($arg, 1), $shortOptions, $options, $argv); + } + return [$options, $nonOptions]; } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnitPHAR\SebastianBergmann\LinesOfCode; - -/** - * @psalm-immutable - */ -final class LinesOfCode -{ - /** - * @var int - */ - private $linesOfCode; - /** - * @var int - */ - private $commentLinesOfCode; - /** - * @var int - */ - private $nonCommentLinesOfCode; /** - * @var int - */ - private $logicalLinesOfCode; - /** - * @throws IllogicalValuesException - * @throws NegativeValueException + * @throws RequiredOptionArgumentMissingException */ - public function __construct(int $linesOfCode, int $commentLinesOfCode, int $nonCommentLinesOfCode, int $logicalLinesOfCode) + private function parseShortOption(string $argument, string $shortOptions, array &$options, array &$argv): void { - if ($linesOfCode < 0) { - throw new NegativeValueException('$linesOfCode must not be negative'); - } - if ($commentLinesOfCode < 0) { - throw new NegativeValueException('$commentLinesOfCode must not be negative'); - } - if ($nonCommentLinesOfCode < 0) { - throw new NegativeValueException('$nonCommentLinesOfCode must not be negative'); - } - if ($logicalLinesOfCode < 0) { - throw new NegativeValueException('$logicalLinesOfCode must not be negative'); - } - if ($linesOfCode - $commentLinesOfCode !== $nonCommentLinesOfCode) { - throw new IllogicalValuesException('$linesOfCode !== $commentLinesOfCode + $nonCommentLinesOfCode'); + $argumentLength = strlen($argument); + for ($i = 0; $i < $argumentLength; $i++) { + $option = $argument[$i]; + $optionArgument = null; + if ($argument[$i] === ':' || ($spec = strstr($shortOptions, $option)) === \false) { + throw new UnknownOptionException('-' . $option); + } + if (strlen($spec) > 1 && $spec[1] === ':') { + if ($i + 1 < $argumentLength) { + $options[] = [$option, substr($argument, $i + 1)]; + break; + } + if (!(strlen($spec) > 2 && $spec[2] === ':')) { + $optionArgument = current($argv); + if (!$optionArgument) { + throw new RequiredOptionArgumentMissingException('-' . $option); + } + assert(is_string($optionArgument)); + next($argv); + } + } + $options[] = [$option, $optionArgument]; } - $this->linesOfCode = $linesOfCode; - $this->commentLinesOfCode = $commentLinesOfCode; - $this->nonCommentLinesOfCode = $nonCommentLinesOfCode; - $this->logicalLinesOfCode = $logicalLinesOfCode; - } - public function linesOfCode() : int - { - return $this->linesOfCode; - } - public function commentLinesOfCode() : int - { - return $this->commentLinesOfCode; } - public function nonCommentLinesOfCode() : int - { - return $this->nonCommentLinesOfCode; - } - public function logicalLinesOfCode() : int - { - return $this->logicalLinesOfCode; - } - public function plus(self $other) : self - { - return new self($this->linesOfCode() + $other->linesOfCode(), $this->commentLinesOfCode() + $other->commentLinesOfCode(), $this->nonCommentLinesOfCode() + $other->nonCommentLinesOfCode(), $this->logicalLinesOfCode() + $other->logicalLinesOfCode()); - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnitPHAR\SebastianBergmann\ObjectEnumerator; - -use function array_merge; -use function func_get_args; -use function is_array; -use function is_object; -use PHPUnitPHAR\SebastianBergmann\ObjectReflector\ObjectReflector; -use PHPUnitPHAR\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 + * @psalm-param list $longOptions * - * @return object[] + * @throws AmbiguousOptionException + * @throws OptionDoesNotAllowArgumentException + * @throws RequiredOptionArgumentMissingException + * @throws UnknownOptionException */ - public function enumerate($variable) + private function parseLongOption(string $argument, array $longOptions, array &$options, array &$argv): void { - 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; + $count = count($longOptions); + $list = explode('=', $argument); + $option = $list[0]; + $optionArgument = null; + if (count($list) > 1) { + $optionArgument = $list[1]; } - $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)); + $optionLength = strlen($option); + foreach ($longOptions as $i => $longOption) { + $opt_start = substr($longOption, 0, $optionLength); + if ($opt_start !== $option) { + continue; } - } else { - $objects[] = $variable; - $reflector = new ObjectReflector(); - foreach ($reflector->getAttributes($variable) as $value) { - if (!is_array($value) && !is_object($value)) { - continue; + $opt_rest = substr($longOption, $optionLength); + if ($opt_rest !== '' && $i + 1 < $count && $option[0] !== '=' && str_starts_with($longOptions[$i + 1], $option)) { + throw new AmbiguousOptionException('--' . $option); + } + if (str_ends_with($longOption, '=')) { + if (!str_ends_with($longOption, '==') && !strlen((string) $optionArgument)) { + if (\false === $optionArgument = current($argv)) { + throw new RequiredOptionArgumentMissingException('--' . $option); + } + next($argv); } - $objects = array_merge($objects, $this->enumerate($value, $processed)); + } elseif ($optionArgument) { + throw new OptionDoesNotAllowArgumentException('--' . $option); } + $fullOption = '--' . preg_replace('/={1,2}$/', '', $longOption); + $options[] = [$fullOption, $optionArgument]; + return; } - return $objects; + throw new UnknownOptionException('--' . $option); } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnitPHAR\SebastianBergmann\ObjectEnumerator; +namespace PHPUnitPHAR\SebastianBergmann\CliParser; -use Throwable; -interface Exception extends Throwable +use function sprintf; +use RuntimeException; +final class AmbiguousOptionException extends RuntimeException implements Exception { + public function __construct(string $option) + { + parent::__construct(sprintf('Option "%s" is ambiguous', $option)); + } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnitPHAR\SebastianBergmann\ObjectEnumerator; +namespace PHPUnitPHAR\SebastianBergmann\CliParser; -class InvalidArgumentException extends \InvalidArgumentException implements Exception +use Throwable; +interface Exception extends Throwable { } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnitPHAR\SebastianBergmann\ObjectReflector; +namespace PHPUnitPHAR\SebastianBergmann\CliParser; -use Throwable; -interface Exception extends Throwable +use function sprintf; +use RuntimeException; +final class OptionDoesNotAllowArgumentException extends RuntimeException implements Exception { + public function __construct(string $option) + { + parent::__construct(sprintf('Option "%s" does not allow an argument', $option)); + } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnitPHAR\SebastianBergmann\ObjectReflector; +namespace PHPUnitPHAR\SebastianBergmann\CliParser; -class InvalidArgumentException extends \InvalidArgumentException implements Exception +use function sprintf; +use RuntimeException; +final class RequiredOptionArgumentMissingException extends RuntimeException implements Exception { + public function __construct(string $option) + { + parent::__construct(sprintf('Required argument for option "%s" is missing', $option)); + } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnitPHAR\SebastianBergmann\ObjectReflector; +namespace PHPUnitPHAR\SebastianBergmann\CliParser; -use function count; -use function explode; -use function get_class; -use function is_object; -class ObjectReflector +use function sprintf; +use RuntimeException; +final class UnknownOptionException extends RuntimeException implements Exception { - /** - * @param object $object - * - * @throws InvalidArgumentException - */ - public function getAttributes($object) : array + public function __construct(string $option) { - if (!is_object($object)) { - throw new InvalidArgumentException(); - } - $attributes = []; - $className = get_class($object); - foreach ((array) $object as $name => $value) { - $name = explode("\x00", (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; + parent::__construct(sprintf('Unknown option "%s"', $option)); } } +BSD 3-Clause License + +Copyright (c) 2016-2023, Sebastian Bergmann +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnitPHAR\SebastianBergmann\RecursionContext; +namespace PHPUnitPHAR\SebastianBergmann\CodeUnitReverseLookup; -use const PHP_INT_MAX; -use const PHP_INT_MIN; -use function array_key_exists; -use function array_pop; -use function array_slice; -use function count; +use function array_merge; +use function assert; +use function get_declared_classes; +use function get_declared_traits; +use function get_defined_functions; use function is_array; -use function is_object; -use function random_int; -use function spl_object_hash; -use SplObjectStorage; -/** - * A context containing previously processed arrays and objects - * when recursively processing a value. - */ -final class Context +use function range; +use ReflectionClass; +use ReflectionFunction; +use ReflectionFunctionAbstract; +use ReflectionMethod; +class Wizard { /** - * @var array[] + * @psalm-var array> */ - private $arrays; + private array $lookupTable = []; /** - * @var SplObjectStorage + * @psalm-var array */ - private $objects; + private array $processedClasses = []; /** - * Initialises the context. + * @psalm-var array */ - public function __construct() + private array $processedFunctions = []; + public function lookup(string $filename, int $lineNumber): string { - $this->arrays = []; - $this->objects = new SplObjectStorage(); + if (!isset($this->lookupTable[$filename][$lineNumber])) { + $this->updateLookupTable(); + } + if (isset($this->lookupTable[$filename][$lineNumber])) { + return $this->lookupTable[$filename][$lineNumber]; + } + return $filename . ':' . $lineNumber; } - /** - * @codeCoverageIgnore - */ - public function __destruct() + private function updateLookupTable(): void { - foreach ($this->arrays as &$array) { - if (is_array($array)) { - array_pop($array); - array_pop($array); - } - } + $this->processClassesAndTraits(); + $this->processFunctions(); } - /** - * Adds a value to the context. - * - * @param array|object $value the value to add - * - * @throws InvalidArgumentException Thrown if $value is not an array or object - * - * @return bool|int|string the ID of the stored value, either as a string or integer - * - * @psalm-template T - * @psalm-param T $value - * @param-out T $value - */ - public function add(&$value) + private function processClassesAndTraits(): void { - if (is_array($value)) { - return $this->addArray($value); - } - if (is_object($value)) { - return $this->addObject($value); + $classes = get_declared_classes(); + $traits = get_declared_traits(); + /* @noinspection PhpConditionAlreadyCheckedInspection */ + assert(is_array($traits)); + foreach (array_merge($classes, $traits) as $classOrTrait) { + if (isset($this->processedClasses[$classOrTrait])) { + continue; + } + foreach ((new ReflectionClass($classOrTrait))->getMethods() as $method) { + $this->processFunctionOrMethod($method); + } + $this->processedClasses[$classOrTrait] = \true; } - 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 - * - * @throws InvalidArgumentException Thrown if $value is not an array or object - * - * @return false|int|string the string or integer ID of the stored value if it has already been seen, or false if the value is not stored - * - * @psalm-template T - * @psalm-param T $value - * @param-out T $value - */ - public function contains(&$value) + private function processFunctions(): void { - if (is_array($value)) { - return $this->containsArray($value); - } - if (is_object($value)) { - return $this->containsObject($value); + foreach (get_defined_functions()['user'] as $function) { + if (isset($this->processedFunctions[$function])) { + continue; + } + $this->processFunctionOrMethod(new ReflectionFunction($function)); + $this->processedFunctions[$function] = \true; } - throw new InvalidArgumentException('Only arrays and objects are supported'); } - /** - * @return bool|int - */ - private function addArray(array &$array) + private function processFunctionOrMethod(ReflectionFunctionAbstract $functionOrMethod): void { - $key = $this->containsArray($array); - if ($key !== \false) { - return $key; + if ($functionOrMethod->isInternal()) { + return; } - $key = count($this->arrays); - $this->arrays[] =& $array; - if (!array_key_exists(PHP_INT_MAX, $array) && !array_key_exists(PHP_INT_MAX - 1, $array)) { - $array[] = $key; - $array[] = $this->objects; - } else { - /* cover the improbable case too */ - /* Note that array_slice (used in containsArray) will return the - * last two values added *not necessarily* the highest integer - * keys in the array, so the order of these writes to $array - * is important, but the actual keys used is not. */ - do { - $key = random_int(PHP_INT_MIN, PHP_INT_MAX); - } while (array_key_exists($key, $array)); - $array[$key] = $key; - do { - $key = random_int(PHP_INT_MIN, PHP_INT_MAX); - } while (array_key_exists($key, $array)); - $array[$key] = $this->objects; + $name = $functionOrMethod->getName(); + if ($functionOrMethod instanceof ReflectionMethod) { + $name = $functionOrMethod->getDeclaringClass()->getName() . '::' . $name; } - return $key; - } - /** - * @param object $object - */ - private function addObject($object) : string - { - if (!$this->objects->contains($object)) { - $this->objects->attach($object); + if (!isset($this->lookupTable[$functionOrMethod->getFileName()])) { + $this->lookupTable[$functionOrMethod->getFileName()] = []; } - return spl_object_hash($object); - } - /** - * @return false|int - */ - 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 false|string - */ - private function containsObject($value) - { - if ($this->objects->contains($value)) { - return spl_object_hash($value); + foreach (range($functionOrMethod->getStartLine(), $functionOrMethod->getEndLine()) as $line) { + $this->lookupTable[$functionOrMethod->getFileName()][$line] = $name; } - return \false; } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnitPHAR\SebastianBergmann\RecursionContext; +namespace PHPUnitPHAR\SebastianBergmann\CodeUnit; -use Throwable; -interface Exception extends Throwable +/** + * @psalm-immutable + */ +final class ClassMethodUnit extends CodeUnit { + /** + * @psalm-assert-if-true ClassMethodUnit $this + */ + public function isClassMethod(): bool + { + return \true; + } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnitPHAR\SebastianBergmann\RecursionContext; +namespace PHPUnitPHAR\SebastianBergmann\CodeUnit; -final class InvalidArgumentException extends \InvalidArgumentException implements Exception +/** + * @psalm-immutable + */ +final class ClassUnit extends CodeUnit { + /** + * @psalm-assert-if-true ClassUnit $this + */ + public function isClass(): bool + { + return \true; + } } -Recursion Context - -Copyright (c) 2002-2022, Sebastian Bergmann . -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the - distribution. - - * Neither the name of Sebastian Bergmann nor the names of his - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN -ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. -Resource Operations - -Copyright (c) 2015-2020, Sebastian Bergmann . -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the - distribution. - - * Neither the name of Sebastian Bergmann nor the names of his - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN -ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnitPHAR\SebastianBergmann\ResourceOperations; +namespace PHPUnitPHAR\SebastianBergmann\CodeUnit; -final class ResourceOperations +use function count; +use function file; +use function file_exists; +use function is_readable; +use function range; +use function sprintf; +use ReflectionClass; +use ReflectionFunction; +use ReflectionMethod; +/** + * @psalm-immutable + */ +abstract class CodeUnit { + private readonly string $name; + private readonly string $sourceFileName; + /** + * @psalm-var list + */ + private readonly array $sourceLines; + /** + * @psalm-param class-string $className + * + * @throws InvalidCodeUnitException + * @throws ReflectionException + */ + public static function forClass(string $className): ClassUnit + { + self::ensureUserDefinedClass($className); + $reflector = self::reflectorForClass($className); + return new ClassUnit($className, $reflector->getFileName(), range($reflector->getStartLine(), $reflector->getEndLine())); + } + /** + * @psalm-param class-string $className + * + * @throws InvalidCodeUnitException + * @throws ReflectionException + */ + public static function forClassMethod(string $className, string $methodName): ClassMethodUnit + { + self::ensureUserDefinedClass($className); + $reflector = self::reflectorForClassMethod($className, $methodName); + return new ClassMethodUnit($className . '::' . $methodName, $reflector->getFileName(), range($reflector->getStartLine(), $reflector->getEndLine())); + } + /** + * @throws InvalidCodeUnitException + */ + public static function forFileWithAbsolutePath(string $path): FileUnit + { + self::ensureFileExistsAndIsReadable($path); + return new FileUnit($path, $path, range(1, count(file($path)))); + } + /** + * @psalm-param class-string $interfaceName + * + * @throws InvalidCodeUnitException + * @throws ReflectionException + */ + public static function forInterface(string $interfaceName): InterfaceUnit + { + self::ensureUserDefinedInterface($interfaceName); + $reflector = self::reflectorForClass($interfaceName); + return new InterfaceUnit($interfaceName, $reflector->getFileName(), range($reflector->getStartLine(), $reflector->getEndLine())); + } + /** + * @psalm-param class-string $interfaceName + * + * @throws InvalidCodeUnitException + * @throws ReflectionException + */ + public static function forInterfaceMethod(string $interfaceName, string $methodName): InterfaceMethodUnit + { + self::ensureUserDefinedInterface($interfaceName); + $reflector = self::reflectorForClassMethod($interfaceName, $methodName); + return new InterfaceMethodUnit($interfaceName . '::' . $methodName, $reflector->getFileName(), range($reflector->getStartLine(), $reflector->getEndLine())); + } + /** + * @psalm-param class-string $traitName + * + * @throws InvalidCodeUnitException + * @throws ReflectionException + */ + public static function forTrait(string $traitName): TraitUnit + { + self::ensureUserDefinedTrait($traitName); + $reflector = self::reflectorForClass($traitName); + return new TraitUnit($traitName, $reflector->getFileName(), range($reflector->getStartLine(), $reflector->getEndLine())); + } + /** + * @psalm-param class-string $traitName + * + * @throws InvalidCodeUnitException + * @throws ReflectionException + */ + public static function forTraitMethod(string $traitName, string $methodName): TraitMethodUnit + { + self::ensureUserDefinedTrait($traitName); + $reflector = self::reflectorForClassMethod($traitName, $methodName); + return new TraitMethodUnit($traitName . '::' . $methodName, $reflector->getFileName(), range($reflector->getStartLine(), $reflector->getEndLine())); + } + /** + * @psalm-param callable-string $functionName + * + * @throws InvalidCodeUnitException + * @throws ReflectionException + */ + public static function forFunction(string $functionName): FunctionUnit + { + $reflector = self::reflectorForFunction($functionName); + if (!$reflector->isUserDefined()) { + throw new InvalidCodeUnitException(sprintf('"%s" is not a user-defined function', $functionName)); + } + return new FunctionUnit($functionName, $reflector->getFileName(), range($reflector->getStartLine(), $reflector->getEndLine())); + } + /** + * @psalm-param list $sourceLines + */ + private function __construct(string $name, string $sourceFileName, array $sourceLines) + { + $this->name = $name; + $this->sourceFileName = $sourceFileName; + $this->sourceLines = $sourceLines; + } + public function name(): string + { + return $this->name; + } + public function sourceFileName(): string + { + return $this->sourceFileName; + } + /** + * @psalm-return list + */ + public function sourceLines(): array + { + return $this->sourceLines; + } + public function isClass(): bool + { + return \false; + } + public function isClassMethod(): bool + { + return \false; + } + public function isInterface(): bool + { + return \false; + } + public function isInterfaceMethod(): bool + { + return \false; + } + public function isTrait(): bool + { + return \false; + } + public function isTraitMethod(): bool + { + return \false; + } + public function isFunction(): bool + { + return \false; + } + public function isFile(): bool + { + return \false; + } + /** + * @throws InvalidCodeUnitException + */ + private static function ensureFileExistsAndIsReadable(string $path): void + { + if (!(file_exists($path) && is_readable($path))) { + throw new InvalidCodeUnitException(sprintf('File "%s" does not exist or is not readable', $path)); + } + } + /** + * @psalm-param class-string $className + * + * @throws InvalidCodeUnitException + */ + private static function ensureUserDefinedClass(string $className): void + { + try { + $reflector = new ReflectionClass($className); + if ($reflector->isInterface()) { + throw new InvalidCodeUnitException(sprintf('"%s" is an interface and not a class', $className)); + } + if ($reflector->isTrait()) { + throw new InvalidCodeUnitException(sprintf('"%s" is a trait and not a class', $className)); + } + if (!$reflector->isUserDefined()) { + throw new InvalidCodeUnitException(sprintf('"%s" is not a user-defined class', $className)); + } + // @codeCoverageIgnoreStart + } catch (\ReflectionException $e) { + throw new ReflectionException($e->getMessage(), $e->getCode(), $e); + } + // @codeCoverageIgnoreEnd + } + /** + * @psalm-param class-string $interfaceName + * + * @throws InvalidCodeUnitException + */ + private static function ensureUserDefinedInterface(string $interfaceName): void + { + try { + $reflector = new ReflectionClass($interfaceName); + if (!$reflector->isInterface()) { + throw new InvalidCodeUnitException(sprintf('"%s" is not an interface', $interfaceName)); + } + if (!$reflector->isUserDefined()) { + throw new InvalidCodeUnitException(sprintf('"%s" is not a user-defined interface', $interfaceName)); + } + // @codeCoverageIgnoreStart + } catch (\ReflectionException $e) { + throw new ReflectionException($e->getMessage(), $e->getCode(), $e); + } + // @codeCoverageIgnoreEnd + } + /** + * @psalm-param class-string $traitName + * + * @throws InvalidCodeUnitException + */ + private static function ensureUserDefinedTrait(string $traitName): void + { + try { + $reflector = new ReflectionClass($traitName); + if (!$reflector->isTrait()) { + throw new InvalidCodeUnitException(sprintf('"%s" is not a trait', $traitName)); + } + // @codeCoverageIgnoreStart + if (!$reflector->isUserDefined()) { + throw new InvalidCodeUnitException(sprintf('"%s" is not a user-defined trait', $traitName)); + } + } catch (\ReflectionException $e) { + throw new ReflectionException($e->getMessage(), $e->getCode(), $e); + } + // @codeCoverageIgnoreEnd + } /** - * @return string[] + * @psalm-param class-string $className + * + * @throws ReflectionException + */ + private static function reflectorForClass(string $className): ReflectionClass + { + try { + return new ReflectionClass($className); + // @codeCoverageIgnoreStart + } catch (\ReflectionException $e) { + throw new ReflectionException($e->getMessage(), $e->getCode(), $e); + } + // @codeCoverageIgnoreEnd + } + /** + * @psalm-param class-string $className + * + * @throws ReflectionException + */ + private static function reflectorForClassMethod(string $className, string $methodName): ReflectionMethod + { + try { + return new ReflectionMethod($className, $methodName); + // @codeCoverageIgnoreStart + } catch (\ReflectionException $e) { + throw new ReflectionException($e->getMessage(), $e->getCode(), $e); + } + // @codeCoverageIgnoreEnd + } + /** + * @psalm-param callable-string $functionName + * + * @throws ReflectionException */ - public static function getFunctions() : array + private static function reflectorForFunction(string $functionName): ReflectionFunction { - 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', 'PHPUnitPHAR\\Vtiful\\Kernel\\Excel::setColumn', 'PHPUnitPHAR\\Vtiful\\Kernel\\Excel::setRow', 'PHPUnitPHAR\\Vtiful\\Kernel\\Format::align', 'PHPUnitPHAR\\Vtiful\\Kernel\\Format::bold', 'PHPUnitPHAR\\Vtiful\\Kernel\\Format::italic', 'PHPUnitPHAR\\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', 'PHPUnitPHAR\\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']; + try { + return new ReflectionFunction($functionName); + // @codeCoverageIgnoreStart + } catch (\ReflectionException $e) { + throw new ReflectionException($e->getMessage(), $e->getCode(), $e); + } + // @codeCoverageIgnoreEnd } } -sebastian/type - -Copyright (c) 2019-2022, Sebastian Bergmann . -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the - distribution. - - * Neither the name of Sebastian Bergmann nor the names of his - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN -ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnitPHAR\SebastianBergmann\Type; +namespace PHPUnitPHAR\SebastianBergmann\CodeUnit; -final class Parameter +use function array_merge; +use function count; +use Countable; +use IteratorAggregate; +/** + * @template-implements IteratorAggregate + * + * @psalm-immutable + */ +final class CodeUnitCollection implements Countable, IteratorAggregate { /** - * @psalm-var non-empty-string + * @psalm-var list */ - private $name; + private readonly array $codeUnits; + public static function fromList(CodeUnit ...$codeUnits): self + { + return new self($codeUnits); + } /** - * @var Type + * @psalm-param list $codeUnits */ - private $type; + private function __construct(array $codeUnits) + { + $this->codeUnits = $codeUnits; + } /** - * @psalm-param non-empty-string $name + * @psalm-return list */ - public function __construct(string $name, Type $type) + public function asArray(): array { - $this->name = $name; - $this->type = $type; + return $this->codeUnits; } - public function name() : string + public function getIterator(): CodeUnitCollectionIterator { - return $this->name; + return new CodeUnitCollectionIterator($this); } - public function type() : Type + public function count(): int { - return $this->type; + return count($this->codeUnits); + } + public function isEmpty(): bool + { + return empty($this->codeUnits); + } + public function mergeWith(self $other): self + { + return new self(array_merge($this->asArray(), $other->asArray())); } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnitPHAR\SebastianBergmann\Type; +namespace PHPUnitPHAR\SebastianBergmann\CodeUnit; -use function assert; -use ReflectionFunctionAbstract; -use ReflectionIntersectionType; -use ReflectionMethod; -use ReflectionNamedType; -use ReflectionType; -use ReflectionUnionType; -final class ReflectionMapper +use Iterator; +/** + * @template-implements Iterator + */ +final class CodeUnitCollectionIterator implements Iterator { /** - * @psalm-return list + * @psalm-var list */ - public function fromParameterTypes(ReflectionFunctionAbstract $functionOrMethod) : array - { - $parameters = []; - foreach ($functionOrMethod->getParameters() as $parameter) { - $name = $parameter->getName(); - assert($name !== ''); - if (!$parameter->hasType()) { - $parameters[] = new Parameter($name, new UnknownType()); - continue; - } - $type = $parameter->getType(); - if ($type instanceof ReflectionNamedType) { - $parameters[] = new Parameter($name, $this->mapNamedType($type, $functionOrMethod)); - continue; - } - if ($type instanceof ReflectionUnionType) { - $parameters[] = new Parameter($name, $this->mapUnionType($type, $functionOrMethod)); - continue; - } - if ($type instanceof ReflectionIntersectionType) { - $parameters[] = new Parameter($name, $this->mapIntersectionType($type, $functionOrMethod)); - } - } - return $parameters; - } - public function fromReturnType(ReflectionFunctionAbstract $functionOrMethod) : Type + private array $codeUnits; + private int $position = 0; + public function __construct(CodeUnitCollection $collection) { - if (!$this->hasReturnType($functionOrMethod)) { - return new UnknownType(); - } - $returnType = $this->returnType($functionOrMethod); - assert($returnType instanceof ReflectionNamedType || $returnType instanceof ReflectionUnionType || $returnType instanceof ReflectionIntersectionType); - if ($returnType instanceof ReflectionNamedType) { - return $this->mapNamedType($returnType, $functionOrMethod); - } - if ($returnType instanceof ReflectionUnionType) { - return $this->mapUnionType($returnType, $functionOrMethod); - } - if ($returnType instanceof ReflectionIntersectionType) { - return $this->mapIntersectionType($returnType, $functionOrMethod); - } + $this->codeUnits = $collection->asArray(); } - private function mapNamedType(ReflectionNamedType $type, ReflectionFunctionAbstract $functionOrMethod) : Type + public function rewind(): void { - if ($functionOrMethod instanceof ReflectionMethod && $type->getName() === 'self') { - return ObjectType::fromName($functionOrMethod->getDeclaringClass()->getName(), $type->allowsNull()); - } - if ($functionOrMethod instanceof ReflectionMethod && $type->getName() === 'static') { - return new StaticType(TypeName::fromReflection($functionOrMethod->getDeclaringClass()), $type->allowsNull()); - } - if ($type->getName() === 'mixed') { - return new MixedType(); - } - if ($functionOrMethod instanceof ReflectionMethod && $type->getName() === 'parent') { - return ObjectType::fromName($functionOrMethod->getDeclaringClass()->getParentClass()->getName(), $type->allowsNull()); - } - return Type::fromName($type->getName(), $type->allowsNull()); + $this->position = 0; } - private function mapUnionType(ReflectionUnionType $type, ReflectionFunctionAbstract $functionOrMethod) : Type + public function valid(): bool { - $types = []; - foreach ($type->getTypes() as $_type) { - assert($_type instanceof ReflectionNamedType || $_type instanceof ReflectionIntersectionType); - if ($_type instanceof ReflectionNamedType) { - $types[] = $this->mapNamedType($_type, $functionOrMethod); - continue; - } - $types[] = $this->mapIntersectionType($_type, $functionOrMethod); - } - return new UnionType(...$types); + return isset($this->codeUnits[$this->position]); } - private function mapIntersectionType(ReflectionIntersectionType $type, ReflectionFunctionAbstract $functionOrMethod) : Type + public function key(): int { - $types = []; - foreach ($type->getTypes() as $_type) { - assert($_type instanceof ReflectionNamedType); - $types[] = $this->mapNamedType($_type, $functionOrMethod); - } - return new IntersectionType(...$types); + return $this->position; } - private function hasReturnType(ReflectionFunctionAbstract $functionOrMethod) : bool + public function current(): CodeUnit { - if ($functionOrMethod->hasReturnType()) { - return \true; - } - if (!\method_exists($functionOrMethod, 'hasTentativeReturnType')) { - return \false; - } - return $functionOrMethod->hasTentativeReturnType(); + return $this->codeUnits[$this->position]; } - private function returnType(ReflectionFunctionAbstract $functionOrMethod) : ?ReflectionType + public function next(): void { - if ($functionOrMethod->hasReturnType()) { - return $functionOrMethod->getReturnType(); - } - if (!\method_exists($functionOrMethod, 'getTentativeReturnType')) { - return null; - } - return $functionOrMethod->getTentativeReturnType(); + $this->position++; } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnitPHAR\SebastianBergmann\Type; +namespace PHPUnitPHAR\SebastianBergmann\CodeUnit; -use function array_pop; -use function explode; -use function implode; -use function substr; -use ReflectionClass; -final class TypeName +/** + * @psalm-immutable + */ +final class FileUnit extends CodeUnit { /** - * @var ?string + * @psalm-assert-if-true FileUnit $this */ - 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 namespaceName() : ?string - { - return $this->namespaceName; - } - public function simpleName() : string - { - return $this->simpleName; - } - public function qualifiedName() : string + public function isFile(): bool { - return $this->namespaceName === null ? $this->simpleName : $this->namespaceName . '\\' . $this->simpleName; + return \true; } - public function isNamespaced() : bool +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnitPHAR\SebastianBergmann\CodeUnit; + +/** + * @psalm-immutable + */ +final class FunctionUnit extends CodeUnit +{ + /** + * @psalm-assert-if-true FunctionUnit $this + */ + public function isFunction(): bool { - return $this->namespaceName !== null; + return \true; } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnitPHAR\SebastianBergmann\Type; +namespace PHPUnitPHAR\SebastianBergmann\CodeUnit; -use Throwable; -interface Exception extends Throwable +/** + * @psalm-immutable + */ +final class InterfaceMethodUnit extends CodeUnit { + /** + * @psalm-assert-if-true InterfaceMethod $this + */ + public function isInterfaceMethod(): bool + { + return \true; + } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnitPHAR\SebastianBergmann\Type; +namespace PHPUnitPHAR\SebastianBergmann\CodeUnit; -final class RuntimeException extends \RuntimeException implements Exception +/** + * @psalm-immutable + */ +final class InterfaceUnit extends CodeUnit { + /** + * @psalm-assert-if-true InterfaceUnit $this + */ + public function isInterface(): bool + { + return \true; + } } +BSD 3-Clause License + +Copyright (c) 2020-2023, Sebastian Bergmann +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnitPHAR\SebastianBergmann\Type; +namespace PHPUnitPHAR\SebastianBergmann\CodeUnit; -use function assert; +use function array_keys; +use function array_merge; +use function array_unique; +use function array_values; use function class_exists; -use function count; use function explode; use function function_exists; -use function is_array; -use function is_object; -use function is_string; -use Closure; +use function interface_exists; +use function ksort; +use function method_exists; +use function sort; +use function sprintf; +use function str_contains; +use function trait_exists; use ReflectionClass; -use ReflectionException; -use ReflectionObject; -final class CallableType extends Type +use ReflectionFunction; +use ReflectionMethod; +final class Mapper { /** - * @var bool + * @psalm-return array> */ - private $allowsNull; - public function __construct(bool $nullable) + public function codeUnitsToSourceLines(CodeUnitCollection $codeUnits): array { - $this->allowsNull = $nullable; + $result = []; + foreach ($codeUnits as $codeUnit) { + $sourceFileName = $codeUnit->sourceFileName(); + if (!isset($result[$sourceFileName])) { + $result[$sourceFileName] = []; + } + $result[$sourceFileName] = array_merge($result[$sourceFileName], $codeUnit->sourceLines()); + } + foreach (array_keys($result) as $sourceFileName) { + $result[$sourceFileName] = array_values(array_unique($result[$sourceFileName])); + sort($result[$sourceFileName]); + } + ksort($result); + return $result; } /** - * @throws RuntimeException + * @throws InvalidCodeUnitException + * @throws ReflectionException */ - public function isAssignable(Type $other) : bool + public function stringToCodeUnits(string $unit): CodeUnitCollection { - 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 (str_contains($unit, '::')) { + [$firstPart, $secondPart] = explode('::', $unit); + if ($this->isUserDefinedFunction($secondPart)) { + return CodeUnitCollection::fromList(CodeUnit::forFunction($secondPart)); } - if ($this->hasInvokeMethod($other)) { - return \true; + if ($this->isUserDefinedMethod($firstPart, $secondPart)) { + return CodeUnitCollection::fromList(CodeUnit::forClassMethod($firstPart, $secondPart)); } - } - if ($other instanceof SimpleType) { - if ($this->isFunction($other)) { - return \true; + if ($this->isUserDefinedInterface($firstPart)) { + return CodeUnitCollection::fromList(CodeUnit::forInterfaceMethod($firstPart, $secondPart)); } - if ($this->isClassCallback($other)) { - return \true; + if ($this->isUserDefinedTrait($firstPart)) { + return CodeUnitCollection::fromList(CodeUnit::forTraitMethod($firstPart, $secondPart)); } - if ($this->isObjectCallback($other)) { - return \true; + } else { + if ($this->isUserDefinedClass($unit)) { + $units = [CodeUnit::forClass($unit)]; + foreach ($this->reflectorForClass($unit)->getTraits() as $trait) { + if (!$trait->isUserDefined()) { + // @codeCoverageIgnoreStart + continue; + // @codeCoverageIgnoreEnd + } + $units[] = CodeUnit::forTrait($trait->getName()); + } + return CodeUnitCollection::fromList(...$units); + } + if ($this->isUserDefinedInterface($unit)) { + return CodeUnitCollection::fromList(CodeUnit::forInterface($unit)); + } + if ($this->isUserDefinedTrait($unit)) { + return CodeUnitCollection::fromList(CodeUnit::forTrait($unit)); + } + if ($this->isUserDefinedFunction($unit)) { + return CodeUnitCollection::fromList(CodeUnit::forFunction($unit)); } } - return \false; - } - public function name() : string - { - return 'callable'; - } - public function allowsNull() : bool - { - return $this->allowsNull; + throw new InvalidCodeUnitException(sprintf('"%s" is not a valid code unit', $unit)); } /** - * @psalm-assert-if-true CallableType $this + * @psalm-param class-string $className + * + * @throws ReflectionException */ - public function isCallable() : bool - { - return \true; - } - private function isClosure(ObjectType $type) : bool + private function reflectorForClass(string $className): ReflectionClass { - return !$type->className()->isNamespaced() && $type->className()->simpleName() === Closure::class; + try { + return new ReflectionClass($className); + // @codeCoverageIgnoreStart + } catch (\ReflectionException $e) { + throw new ReflectionException($e->getMessage(), $e->getCode(), $e); + } + // @codeCoverageIgnoreEnd } /** - * @throws RuntimeException + * @throws ReflectionException */ - private function hasInvokeMethod(ObjectType $type) : bool + private function isUserDefinedFunction(string $functionName): bool { - $className = $type->className()->qualifiedName(); - assert(class_exists($className)); + if (!function_exists($functionName)) { + return \false; + } try { - $class = new ReflectionClass($className); + return (new ReflectionFunction($functionName))->isUserDefined(); // @codeCoverageIgnoreStart - } catch (ReflectionException $e) { - throw new RuntimeException($e->getMessage(), (int) $e->getCode(), $e); - // @codeCoverageIgnoreEnd - } - if ($class->hasMethod('__invoke')) { - return \true; + } catch (\ReflectionException $e) { + throw new ReflectionException($e->getMessage(), $e->getCode(), $e); } - return \false; + // @codeCoverageIgnoreEnd } - private function isFunction(SimpleType $type) : bool + /** + * @throws ReflectionException + */ + private function isUserDefinedClass(string $className): bool { - if (!is_string($type->value())) { + if (!class_exists($className)) { return \false; } - return function_exists($type->value()); + try { + return (new ReflectionClass($className))->isUserDefined(); + // @codeCoverageIgnoreStart + } catch (\ReflectionException $e) { + throw new ReflectionException($e->getMessage(), $e->getCode(), $e); + } + // @codeCoverageIgnoreEnd } - private function isObjectCallback(SimpleType $type) : bool + /** + * @throws ReflectionException + */ + private function isUserDefinedInterface(string $interfaceName): bool { - if (!is_array($type->value())) { + if (!interface_exists($interfaceName)) { return \false; } - if (count($type->value()) !== 2) { - return \false; + try { + return (new ReflectionClass($interfaceName))->isUserDefined(); + // @codeCoverageIgnoreStart + } catch (\ReflectionException $e) { + throw new ReflectionException($e->getMessage(), $e->getCode(), $e); } - if (!isset($type->value()[0], $type->value()[1])) { + // @codeCoverageIgnoreEnd + } + /** + * @throws ReflectionException + */ + private function isUserDefinedTrait(string $traitName): bool + { + if (!trait_exists($traitName)) { return \false; } - if (!is_object($type->value()[0]) || !is_string($type->value()[1])) { - return \false; + try { + return (new ReflectionClass($traitName))->isUserDefined(); + // @codeCoverageIgnoreStart + } catch (\ReflectionException $e) { + throw new ReflectionException($e->getMessage(), $e->getCode(), $e); } - [$object, $methodName] = $type->value(); - return (new ReflectionObject($object))->hasMethod($methodName); + // @codeCoverageIgnoreEnd } - private function isClassCallback(SimpleType $type) : bool + /** + * @throws ReflectionException + */ + private function isUserDefinedMethod(string $className, string $methodName): bool { - if (!is_string($type->value()) && !is_array($type->value())) { + if (!class_exists($className)) { + // @codeCoverageIgnoreStart return \false; + // @codeCoverageIgnoreEnd } - 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 (!isset($type->value()[0], $type->value()[1])) { - return \false; - } - if (!is_string($type->value()[0]) || !is_string($type->value()[1])) { - return \false; - } - [$className, $methodName] = $type->value(); + if (!method_exists($className, $methodName)) { + // @codeCoverageIgnoreStart + return \false; + // @codeCoverageIgnoreEnd } - assert(isset($className) && is_string($className) && class_exists($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(); - } + return (new ReflectionMethod($className, $methodName))->isUserDefined(); // @codeCoverageIgnoreStart - } catch (ReflectionException $e) { - throw new RuntimeException($e->getMessage(), (int) $e->getCode(), $e); - // @codeCoverageIgnoreEnd + } catch (\ReflectionException $e) { + throw new ReflectionException($e->getMessage(), $e->getCode(), $e); } - return \false; + // @codeCoverageIgnoreEnd } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnitPHAR\SebastianBergmann\Type; +namespace PHPUnitPHAR\SebastianBergmann\CodeUnit; -final class FalseType extends Type +/** + * @psalm-immutable + */ +final class TraitMethodUnit extends CodeUnit { - public function isAssignable(Type $other) : bool - { - if ($other instanceof self) { - return \true; - } - return $other instanceof SimpleType && $other->name() === 'bool' && $other->value() === \false; - } - public function name() : string - { - return 'false'; - } - public function allowsNull() : bool - { - return \false; - } /** - * @psalm-assert-if-true FalseType $this + * @psalm-assert-if-true TraitMethodUnit $this */ - public function isFalse() : bool + public function isTraitMethod(): bool { return \true; } @@ -104381,489 +100281,729 @@ final class FalseType extends Type declare (strict_types=1); /* - * This file is part of sebastian/type. + * This file is part of sebastian/code-unit. * * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnitPHAR\SebastianBergmann\Type; +namespace PHPUnitPHAR\SebastianBergmann\CodeUnit; -final class GenericObjectType extends Type +/** + * @psalm-immutable + */ +final class TraitUnit extends CodeUnit { /** - * @var bool + * @psalm-assert-if-true TraitUnit $this */ - private $allowsNull; - public function __construct(bool $nullable) + public function isTrait(): bool { - $this->allowsNull = $nullable; + return \true; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnitPHAR\SebastianBergmann\CodeUnit; + +use Throwable; +interface Exception extends Throwable +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnitPHAR\SebastianBergmann\CodeUnit; + +use RuntimeException; +final class InvalidCodeUnitException extends RuntimeException implements Exception +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnitPHAR\SebastianBergmann\CodeUnit; + +use RuntimeException; +final class NoTraitException extends RuntimeException implements Exception +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnitPHAR\SebastianBergmann\CodeUnit; + +use RuntimeException; +final class ReflectionException extends RuntimeException implements Exception +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnitPHAR\SebastianBergmann\Comparator; + +use function array_key_exists; +use function assert; +use function is_array; +use function sort; +use function sprintf; +use function str_replace; +use function trim; +use PHPUnitPHAR\SebastianBergmann\Exporter\Exporter; +/** + * Arrays are equal if they contain the same key-value pairs. + * The order of the keys does not matter. + * The types of key-value pairs do not matter. + */ +class ArrayComparator extends Comparator +{ + public function accepts(mixed $expected, mixed $actual): bool + { + return is_array($expected) && is_array($actual); } - public function isAssignable(Type $other) : bool + /** + * @throws ComparisonFailure + */ + public function assertEquals(mixed $expected, mixed $actual, float $delta = 0.0, bool $canonicalize = \false, bool $ignoreCase = \false, array &$processed = []): void { - if ($this->allowsNull && $other instanceof NullType) { - return \true; + assert(is_array($expected)); + assert(is_array($actual)); + if ($canonicalize) { + sort($expected); + sort($actual); } - if (!$other instanceof ObjectType) { - return \false; + $remaining = $actual; + $actualAsString = "Array (\n"; + $expectedAsString = "Array (\n"; + $equal = \true; + $exporter = new Exporter(); + foreach ($expected as $key => $value) { + unset($remaining[$key]); + if (!array_key_exists($key, $actual)) { + $expectedAsString .= sprintf(" %s => %s\n", $exporter->export($key), $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", $exporter->export($key), $exporter->shortenedExport($value)); + $actualAsString .= sprintf(" %s => %s\n", $exporter->export($key), $exporter->shortenedExport($actual[$key])); + } catch (ComparisonFailure $e) { + $expectedAsString .= sprintf(" %s => %s\n", $exporter->export($key), $e->getExpectedAsString() ? $this->indent($e->getExpectedAsString()) : $exporter->shortenedExport($e->getExpected())); + $actualAsString .= sprintf(" %s => %s\n", $exporter->export($key), $e->getActualAsString() ? $this->indent($e->getActualAsString()) : $exporter->shortenedExport($e->getActual())); + $equal = \false; + } + } + foreach ($remaining as $key => $value) { + $actualAsString .= sprintf(" %s => %s\n", $exporter->export($key), $exporter->shortenedExport($value)); + $equal = \false; + } + $expectedAsString .= ')'; + $actualAsString .= ')'; + if (!$equal) { + throw new ComparisonFailure($expected, $actual, $expectedAsString, $actualAsString, 'Failed asserting that two arrays are equal.'); } - return \true; } - public function name() : string + private function indent(string $lines): string { - return 'object'; + return trim(str_replace("\n", "\n ", $lines)); } - public function allowsNull() : bool +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnitPHAR\SebastianBergmann\Comparator; + +abstract class Comparator +{ + private Factory $factory; + public function setFactory(Factory $factory): void { - return $this->allowsNull; + $this->factory = $factory; } + abstract public function accepts(mixed $expected, mixed $actual): bool; /** - * @psalm-assert-if-true GenericObjectType $this + * @throws ComparisonFailure */ - public function isGenericObject() : bool + abstract public function assertEquals(mixed $expected, mixed $actual, float $delta = 0.0, bool $canonicalize = \false, bool $ignoreCase = \false): void; + protected function factory(): Factory { - return \true; + return $this->factory; } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnitPHAR\SebastianBergmann\Type; +namespace PHPUnitPHAR\SebastianBergmann\Comparator; -use function assert; -use function count; -use function implode; -use function in_array; -use function sort; -final class IntersectionType extends Type +use RuntimeException; +use PHPUnitPHAR\SebastianBergmann\Diff\Differ; +use PHPUnitPHAR\SebastianBergmann\Diff\Output\UnifiedDiffOutputBuilder; +final class ComparisonFailure extends RuntimeException { - /** - * @psalm-var non-empty-list - */ - private $types; - /** - * @throws RuntimeException - */ - public function __construct(Type ...$types) + private mixed $expected; + private mixed $actual; + private string $expectedAsString; + private string $actualAsString; + public function __construct(mixed $expected, mixed $actual, string $expectedAsString, string $actualAsString, string $message = '') { - $this->ensureMinimumOfTwoTypes(...$types); - $this->ensureOnlyValidTypes(...$types); - $this->ensureNoDuplicateTypes(...$types); - $this->types = $types; + parent::__construct($message); + $this->expected = $expected; + $this->actual = $actual; + $this->expectedAsString = $expectedAsString; + $this->actualAsString = $actualAsString; } - public function isAssignable(Type $other) : bool + public function getActual(): mixed { - return $other->isObject(); + return $this->actual; } - public function asString() : string + public function getExpected(): mixed { - return $this->name(); + return $this->expected; } - public function name() : string + public function getActualAsString(): string { - $types = []; - foreach ($this->types as $type) { - $types[] = $type->name(); - } - sort($types); - return implode('&', $types); + return $this->actualAsString; } - public function allowsNull() : bool + public function getExpectedAsString(): string { - return \false; + return $this->expectedAsString; } - /** - * @psalm-assert-if-true IntersectionType $this - */ - public function isIntersection() : bool + public function getDiff(): string { - return \true; + if (!$this->actualAsString && !$this->expectedAsString) { + return ''; + } + $differ = new Differ(new UnifiedDiffOutputBuilder("\n--- Expected\n+++ Actual\n")); + return $differ->diff($this->expectedAsString, $this->actualAsString); } - /** - * @psalm-return non-empty-list - */ - public function types() : array + public function toString(): string { - return $this->types; + return $this->getMessage() . $this->getDiff(); } - /** - * @throws RuntimeException - */ - private function ensureMinimumOfTwoTypes(Type ...$types) : void +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnitPHAR\SebastianBergmann\Comparator; + +use function assert; +use function mb_strtolower; +use function sprintf; +use DOMDocument; +use DOMNode; +use ValueError; +final class DOMNodeComparator extends ObjectComparator +{ + public function accepts(mixed $expected, mixed $actual): bool { - if (count($types) < 2) { - throw new RuntimeException('An intersection type must be composed of at least two types'); - } + return $expected instanceof DOMNode && $actual instanceof DOMNode; } /** - * @throws RuntimeException + * @throws ComparisonFailure */ - private function ensureOnlyValidTypes(Type ...$types) : void + public function assertEquals(mixed $expected, mixed $actual, float $delta = 0.0, bool $canonicalize = \false, bool $ignoreCase = \false, array &$processed = []): void { - foreach ($types as $type) { - if (!$type->isObject()) { - throw new RuntimeException('An intersection type can only be composed of interfaces and classes'); - } + assert($expected instanceof DOMNode); + assert($actual instanceof DOMNode); + $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, sprintf("Failed asserting that two DOM %s are equal.\n", $type)); } } /** - * @throws RuntimeException + * Returns the normalized, whitespace-cleaned, and indented textual + * representation of a DOMNode. */ - private function ensureNoDuplicateTypes(Type ...$types) : void + private function nodeToText(DOMNode $node, bool $canonicalize, bool $ignoreCase): string { - $names = []; - foreach ($types as $type) { - assert($type instanceof ObjectType); - $classQualifiedName = $type->className()->qualifiedName(); - if (in_array($classQualifiedName, $names, \true)) { - throw new RuntimeException('An intersection type must not contain duplicate types'); + if ($canonicalize) { + $document = new DOMDocument(); + try { + $c14n = $node->C14N(); + assert(!empty($c14n)); + @$document->loadXML($c14n); + } catch (ValueError) { } - $names[] = $classQualifiedName; + $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 ? mb_strtolower($text, 'UTF-8') : $text; } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnitPHAR\SebastianBergmann\Type; +namespace PHPUnitPHAR\SebastianBergmann\Comparator; +use function abs; use function assert; -use function class_exists; -use function is_iterable; -use ReflectionClass; -use ReflectionException; -final class IterableType extends Type +use function floor; +use function sprintf; +use DateInterval; +use DateTimeInterface; +use DateTimeZone; +final class DateTimeComparator extends ObjectComparator { - /** - * @var bool - */ - private $allowsNull; - public function __construct(bool $nullable) + public function accepts(mixed $expected, mixed $actual): bool { - $this->allowsNull = $nullable; + return $expected instanceof DateTimeInterface && $actual instanceof DateTimeInterface; } /** - * @throws RuntimeException + * @throws ComparisonFailure */ - public function isAssignable(Type $other) : bool + public function assertEquals(mixed $expected, mixed $actual, float $delta = 0.0, bool $canonicalize = \false, bool $ignoreCase = \false, array &$processed = []): void { - 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) { - $className = $other->className()->qualifiedName(); - assert(class_exists($className)); - try { - return (new ReflectionClass($className))->isIterable(); - // @codeCoverageIgnoreStart - } catch (ReflectionException $e) { - throw new RuntimeException($e->getMessage(), (int) $e->getCode(), $e); - // @codeCoverageIgnoreEnd - } + assert($expected instanceof DateTimeInterface); + assert($actual instanceof DateTimeInterface); + $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), 'Failed asserting that two DateTime objects are equal.'); } - return \false; } - public function name() : string + /** + * 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 { - return 'iterable'; + $string = $datetime->format('Y-m-d\TH:i:s.uO'); + return $string ?: 'Invalid DateTimeInterface object'; } - public function allowsNull() : bool +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnitPHAR\SebastianBergmann\Comparator; + +use function assert; +use Exception; +/** + * Compares Exception instances for equality. + */ +final class ExceptionComparator extends ObjectComparator +{ + public function accepts(mixed $expected, mixed $actual): bool { - return $this->allowsNull; + return $expected instanceof Exception && $actual instanceof Exception; } - /** - * @psalm-assert-if-true IterableType $this - */ - public function isIterable() : bool + protected function toArray(object $object): array { - return \true; + assert($object instanceof Exception); + $array = parent::toArray($object); + unset($array['file'], $array['line'], $array['trace'], $array['string'], $array['xdebug_message']); + return $array; } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnitPHAR\SebastianBergmann\Type; +namespace PHPUnitPHAR\SebastianBergmann\Comparator; -final class MixedType extends Type +use function array_unshift; +final class Factory { - public function isAssignable(Type $other) : bool + private static ?Factory $instance = null; + /** + * @psalm-var list + */ + private array $customComparators = []; + /** + * @psalm-var list + */ + private array $defaultComparators = []; + public static function getInstance(): self { - return !$other instanceof VoidType; + if (self::$instance === null) { + self::$instance = new self(); + // @codeCoverageIgnore + } + return self::$instance; } - public function asString() : string + public function __construct() { - return 'mixed'; + $this->registerDefaultComparators(); } - public function name() : string + public function getComparatorFor(mixed $expected, mixed $actual): Comparator { - return 'mixed'; + foreach ($this->customComparators as $comparator) { + if ($comparator->accepts($expected, $actual)) { + return $comparator; + } + } + foreach ($this->defaultComparators as $comparator) { + if ($comparator->accepts($expected, $actual)) { + return $comparator; + } + } + throw new RuntimeException('No suitable Comparator implementation found'); } - public function allowsNull() : bool + /** + * 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. + */ + public function register(Comparator $comparator): void { - return \true; + array_unshift($this->customComparators, $comparator); + $comparator->setFactory($this); } /** - * @psalm-assert-if-true MixedType $this + * Unregisters a comparator. + * + * This comparator will no longer be considered by getComparatorFor(). */ - public function isMixed() : bool + public function unregister(Comparator $comparator): void { - return \true; + foreach ($this->customComparators as $key => $_comparator) { + if ($comparator === $_comparator) { + unset($this->customComparators[$key]); + } + } + } + public function reset(): void + { + $this->customComparators = []; + } + private function registerDefaultComparators(): void + { + $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 NumericComparator()); + $this->registerDefaultComparator(new ScalarComparator()); + $this->registerDefaultComparator(new TypeComparator()); + } + private function registerDefaultComparator(Comparator $comparator): void + { + $this->defaultComparators[] = $comparator; + $comparator->setFactory($this); } } +BSD 3-Clause License + +Copyright (c) 2002-2023, Sebastian Bergmann +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnitPHAR\SebastianBergmann\Type; +namespace PHPUnitPHAR\SebastianBergmann\Comparator; -final class NeverType extends Type +use function array_keys; +use function assert; +use function str_starts_with; +use PHPUnit\Framework\MockObject\Stub; +/** + * Compares PHPUnit\Framework\MockObject\MockObject instances for equality. + */ +final class MockObjectComparator extends ObjectComparator { - public function isAssignable(Type $other) : bool - { - return $other instanceof self; - } - public function name() : string - { - return 'never'; - } - public function allowsNull() : bool + public function accepts(mixed $expected, mixed $actual): bool { - return \false; + return $expected instanceof Stub && $actual instanceof Stub; } - /** - * @psalm-assert-if-true NeverType $this - */ - public function isNever() : bool + protected function toArray(object $object): array { - return \true; + assert($object instanceof Stub); + $array = parent::toArray($object); + foreach (array_keys($array) as $key) { + if (!str_starts_with($key, '__phpunit_')) { + continue; + } + unset($array[$key]); + } + return $array; } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnitPHAR\SebastianBergmann\Type; +namespace PHPUnitPHAR\SebastianBergmann\Comparator; -final class NullType extends Type +use function abs; +use function is_float; +use function is_infinite; +use function is_nan; +use function is_numeric; +use function is_string; +use function sprintf; +use PHPUnitPHAR\SebastianBergmann\Exporter\Exporter; +final class NumericComparator extends ScalarComparator { - public function isAssignable(Type $other) : bool + public function accepts(mixed $expected, mixed $actual): bool { - return !$other instanceof VoidType; - } - public function name() : string - { - return 'null'; + // all numerical values, but not if both of them are strings + return is_numeric($expected) && is_numeric($actual) && !(is_string($expected) && is_string($actual)); } - public function asString() : string + /** + * @throws ComparisonFailure + */ + public function assertEquals(mixed $expected, mixed $actual, float $delta = 0.0, bool $canonicalize = \false, bool $ignoreCase = \false): void { - return 'null'; + if ($this->isInfinite($actual) && $this->isInfinite($expected)) { + return; + } + if (($this->isInfinite($actual) xor $this->isInfinite($expected)) || ($this->isNan($actual) || $this->isNan($expected)) || abs($actual - $expected) > $delta) { + $exporter = new Exporter(); + throw new ComparisonFailure($expected, $actual, '', '', sprintf('Failed asserting that %s matches expected %s.', $exporter->export($actual), $exporter->export($expected))); + } } - public function allowsNull() : bool + private function isInfinite(mixed $value): bool { - return \true; + return is_float($value) && is_infinite($value); } - /** - * @psalm-assert-if-true NullType $this - */ - public function isNull() : bool + private function isNan(mixed $value): bool { - return \true; + return is_float($value) && is_nan($value); } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnitPHAR\SebastianBergmann\Type; +namespace PHPUnitPHAR\SebastianBergmann\Comparator; -use function is_subclass_of; -use function strcasecmp; -final class ObjectType extends Type +use function assert; +use function in_array; +use function is_object; +use function sprintf; +use function substr_replace; +use PHPUnitPHAR\SebastianBergmann\Exporter\Exporter; +class ObjectComparator extends ArrayComparator { - /** - * @var TypeName - */ - private $className; - /** - * @var bool - */ - private $allowsNull; - public function __construct(TypeName $className, bool $allowsNull) + public function accepts(mixed $expected, mixed $actual): bool { - $this->className = $className; - $this->allowsNull = $allowsNull; + return is_object($expected) && is_object($actual); } - public function isAssignable(Type $other) : bool + /** + * @throws ComparisonFailure + */ + public function assertEquals(mixed $expected, mixed $actual, float $delta = 0.0, bool $canonicalize = \false, bool $ignoreCase = \false, array &$processed = []): void { - if ($this->allowsNull && $other instanceof NullType) { - return \true; + assert(is_object($expected)); + assert(is_object($actual)); + if ($actual::class !== $expected::class) { + $exporter = new Exporter(); + throw new ComparisonFailure($expected, $actual, $exporter->export($expected), $exporter->export($actual), sprintf('%s is not instance of expected class "%s".', $exporter->export($actual), $expected::class)); } - if ($other instanceof self) { - if (0 === strcasecmp($this->className->qualifiedName(), $other->className->qualifiedName())) { - return \true; - } - if (is_subclass_of($other->className->qualifiedName(), $this->className->qualifiedName(), \true)) { - return \true; + // 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(), $expected::class . ' Object', 0, 5), + substr_replace($e->getActualAsString(), $actual::class . ' Object', 0, 5), + 'Failed asserting that two objects are equal.' + ); } } - return \false; - } - public function name() : string - { - return $this->className->qualifiedName(); } - public function allowsNull() : bool + protected function toArray(object $object): array { - return $this->allowsNull; - } - public function className() : TypeName - { - return $this->className; - } - /** - * @psalm-assert-if-true ObjectType $this - */ - public function isObject() : bool - { - return \true; + return (new Exporter())->toArray($object); } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnitPHAR\SebastianBergmann\Type; +namespace PHPUnitPHAR\SebastianBergmann\Comparator; -use function strtolower; -final class SimpleType extends Type +use function assert; +use function is_resource; +use PHPUnitPHAR\SebastianBergmann\Exporter\Exporter; +final class ResourceComparator extends Comparator { - /** - * @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 ($this->name === 'bool' && $other->name() === 'true') { - return \true; - } - if ($this->name === 'bool' && $other->name() === 'false') { - return \true; - } - if ($other instanceof self) { - return $this->name === $other->name; - } - return \false; - } - public function name() : string - { - return $this->name; - } - public function allowsNull() : bool + public function accepts(mixed $expected, mixed $actual): bool { - return $this->allowsNull; - } - public function value() - { - return $this->value; + return is_resource($expected) && is_resource($actual); } /** - * @psalm-assert-if-true SimpleType $this + * @throws ComparisonFailure */ - public function isSimple() : bool - { - return \true; - } - private function normalize(string $name) : string + public function assertEquals(mixed $expected, mixed $actual, float $delta = 0.0, bool $canonicalize = \false, bool $ignoreCase = \false): void { - $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; + assert(is_resource($expected)); + assert(is_resource($actual)); + $exporter = new Exporter(); + if ($actual != $expected) { + throw new ComparisonFailure($expected, $actual, $exporter->export($expected), $exporter->export($actual)); } } } @@ -104871,7184 +101011,5692 @@ final class SimpleType extends Type declare (strict_types=1); /* - * This file is part of sebastian/type. + * This file is part of sebastian/comparator. * * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnitPHAR\SebastianBergmann\Type; +namespace PHPUnitPHAR\SebastianBergmann\Comparator; -final class StaticType extends Type +use function is_bool; +use function is_object; +use function is_scalar; +use function is_string; +use function mb_strtolower; +use function method_exists; +use function sprintf; +use PHPUnitPHAR\SebastianBergmann\Exporter\Exporter; +/** + * Compares scalar or NULL values for equality. + */ +class ScalarComparator extends Comparator { - /** - * @var TypeName - */ - private $className; - /** - * @var bool - */ - private $allowsNull; - public function __construct(TypeName $className, bool $allowsNull) + public function accepts(mixed $expected, mixed $actual): bool { - $this->className = $className; - $this->allowsNull = $allowsNull; + return (is_scalar($expected) xor null === $expected) && (is_scalar($actual) xor null === $actual) || is_string($expected) && is_object($actual) && method_exists($actual, '__toString') || is_object($expected) && method_exists($expected, '__toString') && is_string($actual); } - public function isAssignable(Type $other) : bool + /** + * @throws ComparisonFailure + */ + public function assertEquals(mixed $expected, mixed $actual, float $delta = 0.0, bool $canonicalize = \false, bool $ignoreCase = \false): void { - if ($this->allowsNull && $other instanceof NullType) { - return \true; - } - if (!$other instanceof ObjectType) { - return \false; + $expectedToCompare = $expected; + $actualToCompare = $actual; + $exporter = new Exporter(); + // always compare as strings to avoid strange behaviour + // otherwise 0 == 'Foobar' + if (is_string($expected) && !is_bool($actual) || is_string($actual) && !is_bool($expected)) { + $expectedToCompare = (string) $expectedToCompare; + $actualToCompare = (string) $actualToCompare; + if ($ignoreCase) { + $expectedToCompare = mb_strtolower($expectedToCompare, 'UTF-8'); + $actualToCompare = mb_strtolower($actualToCompare, 'UTF-8'); + } } - if (0 === \strcasecmp($this->className->qualifiedName(), $other->className()->qualifiedName())) { - return \true; + if ($expectedToCompare !== $actualToCompare && is_string($expected) && is_string($actual)) { + throw new ComparisonFailure($expected, $actual, $exporter->export($expected), $exporter->export($actual), 'Failed asserting that two strings are equal.'); } - if (\is_subclass_of($other->className()->qualifiedName(), $this->className->qualifiedName(), \true)) { - return \true; + if ($expectedToCompare != $actualToCompare) { + throw new ComparisonFailure( + $expected, + $actual, + // no diff is required + '', + '', + sprintf('Failed asserting that %s matches expected %s.', $exporter->export($actual), $exporter->export($expected)) + ); } - return \false; - } - public function name() : string - { - return 'static'; - } - public function allowsNull() : bool - { - return $this->allowsNull; - } - /** - * @psalm-assert-if-true StaticType $this - */ - public function isStatic() : bool - { - return \true; } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnitPHAR\SebastianBergmann\Type; +namespace PHPUnitPHAR\SebastianBergmann\Comparator; -final class TrueType extends Type +use function assert; +use PHPUnitPHAR\SebastianBergmann\Exporter\Exporter; +use SplObjectStorage; +final class SplObjectStorageComparator extends Comparator { - public function isAssignable(Type $other) : bool - { - if ($other instanceof self) { - return \true; - } - return $other instanceof SimpleType && $other->name() === 'bool' && $other->value() === \true; - } - public function name() : string - { - return 'true'; - } - public function allowsNull() : bool + public function accepts(mixed $expected, mixed $actual): bool { - return \false; + return $expected instanceof SplObjectStorage && $actual instanceof SplObjectStorage; } /** - * @psalm-assert-if-true TrueType $this + * @throws ComparisonFailure */ - public function isTrue() : bool + public function assertEquals(mixed $expected, mixed $actual, float $delta = 0.0, bool $canonicalize = \false, bool $ignoreCase = \false): void { - return \true; + assert($expected instanceof SplObjectStorage); + assert($actual instanceof SplObjectStorage); + $exporter = new Exporter(); + foreach ($actual as $object) { + if (!$expected->contains($object)) { + throw new ComparisonFailure($expected, $actual, $exporter->export($expected), $exporter->export($actual), 'Failed asserting that two objects are equal.'); + } + } + foreach ($expected as $object) { + if (!$actual->contains($object)) { + throw new ComparisonFailure($expected, $actual, $exporter->export($expected), $exporter->export($actual), 'Failed asserting that two objects are equal.'); + } + } } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnitPHAR\SebastianBergmann\Type; +namespace PHPUnitPHAR\SebastianBergmann\Comparator; -use const PHP_VERSION; -use function get_class; use function gettype; -use function strtolower; -use function version_compare; -abstract class Type +use function sprintf; +use PHPUnitPHAR\SebastianBergmann\Exporter\Exporter; +final class TypeComparator extends Comparator { - public static function fromValue($value, bool $allowsNull) : self - { - if ($allowsNull === \false) { - if ($value === \true) { - return new TrueType(); - } - if ($value === \false) { - return new FalseType(); - } - } - $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 - { - if (version_compare(PHP_VERSION, '8.1.0-dev', '>=') && strtolower($typeName) === 'never') { - return new NeverType(); - } - switch (strtolower($typeName)) { - case 'callable': - return new CallableType($allowsNull); - case 'true': - return new TrueType(); - case 'false': - return new FalseType(); - 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); - } - } - public function asString() : string - { - return ($this->allowsNull() ? '?' : '') . $this->name(); - } - /** - * @psalm-assert-if-true CallableType $this - */ - public function isCallable() : bool - { - return \false; - } - /** - * @psalm-assert-if-true TrueType $this - */ - public function isTrue() : bool - { - return \false; - } - /** - * @psalm-assert-if-true FalseType $this - */ - public function isFalse() : bool - { - return \false; - } - /** - * @psalm-assert-if-true GenericObjectType $this - */ - public function isGenericObject() : bool - { - return \false; - } - /** - * @psalm-assert-if-true IntersectionType $this - */ - public function isIntersection() : bool - { - return \false; - } - /** - * @psalm-assert-if-true IterableType $this - */ - public function isIterable() : bool - { - return \false; - } - /** - * @psalm-assert-if-true MixedType $this - */ - public function isMixed() : bool - { - return \false; - } - /** - * @psalm-assert-if-true NeverType $this - */ - public function isNever() : bool - { - return \false; - } - /** - * @psalm-assert-if-true NullType $this - */ - public function isNull() : bool - { - return \false; - } - /** - * @psalm-assert-if-true ObjectType $this - */ - public function isObject() : bool - { - return \false; - } - /** - * @psalm-assert-if-true SimpleType $this - */ - public function isSimple() : bool - { - return \false; - } - /** - * @psalm-assert-if-true StaticType $this - */ - public function isStatic() : bool - { - return \false; - } - /** - * @psalm-assert-if-true UnionType $this - */ - public function isUnion() : bool - { - return \false; - } - /** - * @psalm-assert-if-true UnknownType $this - */ - public function isUnknown() : bool + public function accepts(mixed $expected, mixed $actual): bool { - return \false; + return \true; } /** - * @psalm-assert-if-true VoidType $this + * @throws ComparisonFailure */ - public function isVoid() : bool + public function assertEquals(mixed $expected, mixed $actual, float $delta = 0.0, bool $canonicalize = \false, bool $ignoreCase = \false): void { - return \false; + if (gettype($expected) != gettype($actual)) { + throw new ComparisonFailure( + $expected, + $actual, + // we don't need a diff + '', + '', + sprintf('%s does not match expected type "%s".', (new Exporter())->shortenedExport($actual), gettype($expected)) + ); + } } - public abstract function isAssignable(self $other) : bool; - public abstract function name() : string; - public abstract function allowsNull() : bool; } * * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnitPHAR\SebastianBergmann\Type; - -use function count; -use function implode; -use function sort; -final class UnionType extends Type -{ - /** - * @psalm-var non-empty-list - */ - private $types; - /** - * @throws RuntimeException - */ - public function __construct(Type ...$types) - { - $this->ensureMinimumOfTwoTypes(...$types); - $this->ensureOnlyValidTypes(...$types); - $this->types = $types; - } - public function isAssignable(Type $other) : bool - { - foreach ($this->types as $type) { - if ($type->isAssignable($other)) { - return \true; - } - } - return \false; - } - public function asString() : string - { - return $this->name(); - } - public function name() : string - { - $types = []; - foreach ($this->types as $type) { - if ($type->isIntersection()) { - $types[] = '(' . $type->name() . ')'; - continue; - } - $types[] = $type->name(); - } - sort($types); - return implode('|', $types); - } - public function allowsNull() : bool - { - foreach ($this->types as $type) { - if ($type instanceof NullType) { - return \true; - } - } - return \false; - } - /** - * @psalm-assert-if-true UnionType $this - */ - public function isUnion() : bool - { - return \true; - } - public function containsIntersectionTypes() : bool - { - foreach ($this->types as $type) { - if ($type->isIntersection()) { - return \true; - } - } - return \false; - } - /** - * @psalm-return non-empty-list - */ - public function types() : array - { - return $this->types; - } - /** - * @throws RuntimeException - */ - private function ensureMinimumOfTwoTypes(Type ...$types) : void - { - if (count($types) < 2) { - throw new RuntimeException('A union type must be composed of at least two types'); - } - } - /** - * @throws RuntimeException - */ - private function ensureOnlyValidTypes(Type ...$types) : void - { - foreach ($types as $type) { - if ($type instanceof UnknownType) { - throw new RuntimeException('A union type must not be composed of an unknown type'); - } - if ($type instanceof VoidType) { - throw new RuntimeException('A union type must not be composed of a void type'); - } - } - } + * file that was distributed with this source code. + */ +namespace PHPUnitPHAR\SebastianBergmann\Comparator; + +use Throwable; +interface Exception extends Throwable +{ } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnitPHAR\SebastianBergmann\Type; +namespace PHPUnitPHAR\SebastianBergmann\Comparator; -final class UnknownType extends Type +final class RuntimeException extends \RuntimeException implements Exception { - public function isAssignable(Type $other) : bool - { - return \true; - } - public function name() : string - { - return 'unknown type'; - } - public function asString() : string - { - return ''; - } - public function allowsNull() : bool - { - return \true; - } - /** - * @psalm-assert-if-true UnknownType $this - */ - public function isUnknown() : bool - { - return \true; - } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnitPHAR\SebastianBergmann\Type; +namespace PHPUnitPHAR\SebastianBergmann\Complexity; -final class VoidType extends Type +use function assert; +use function file_get_contents; +use PHPUnitPHAR\PhpParser\Error; +use PHPUnitPHAR\PhpParser\Node; +use PHPUnitPHAR\PhpParser\NodeTraverser; +use PHPUnitPHAR\PhpParser\NodeVisitor\NameResolver; +use PHPUnitPHAR\PhpParser\NodeVisitor\ParentConnectingVisitor; +use PHPUnitPHAR\PhpParser\ParserFactory; +final class Calculator { - public function isAssignable(Type $other) : bool - { - return $other instanceof self; - } - public function name() : string + /** + * @throws RuntimeException + */ + public function calculateForSourceFile(string $sourceFile): ComplexityCollection { - return 'void'; + return $this->calculateForSourceString(file_get_contents($sourceFile)); } - public function allowsNull() : bool + /** + * @throws RuntimeException + */ + public function calculateForSourceString(string $source): ComplexityCollection { - return \false; + try { + $nodes = (new ParserFactory())->createForHostVersion()->parse($source); + assert($nodes !== null); + return $this->calculateForAbstractSyntaxTree($nodes); + // @codeCoverageIgnoreStart + } catch (Error $error) { + throw new RuntimeException($error->getMessage(), $error->getCode(), $error); + } + // @codeCoverageIgnoreEnd } /** - * @psalm-assert-if-true VoidType $this + * @param Node[] $nodes + * + * @throws RuntimeException */ - public function isVoid() : bool + public function calculateForAbstractSyntaxTree(array $nodes): ComplexityCollection { - return \true; + $traverser = new NodeTraverser(); + $complexityCalculatingVisitor = new ComplexityCalculatingVisitor(\true); + $traverser->addVisitor(new NameResolver()); + $traverser->addVisitor(new ParentConnectingVisitor()); + $traverser->addVisitor($complexityCalculatingVisitor); + try { + /* @noinspection UnusedFunctionResultInspection */ + $traverser->traverse($nodes); + // @codeCoverageIgnoreStart + } catch (Error $error) { + throw new RuntimeException($error->getMessage(), $error->getCode(), $error); + } + // @codeCoverageIgnoreEnd + return $complexityCalculatingVisitor->result(); } } -Version - -Copyright (c) 2013-2020, Sebastian Bergmann . -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the - distribution. - - * Neither the name of Sebastian Bergmann nor the names of his - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN -ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnitPHAR\SebastianBergmann; +namespace PHPUnitPHAR\SebastianBergmann\Complexity; -final class Version +use function str_contains; +/** + * @psalm-immutable + */ +final class Complexity { /** - * @var string + * @psalm-var non-empty-string */ - private $path; + private readonly string $name; /** - * @var string + * @psalm-var positive-int */ - private $release; + private int $cyclomaticComplexity; /** - * @var string + * @psalm-param non-empty-string $name + * @psalm-param positive-int $cyclomaticComplexity */ - private $version; - public function __construct(string $release, string $path) - { - $this->release = $release; - $this->path = $path; - } - public function getVersion() : string + public function __construct(string $name, int $cyclomaticComplexity) { - if ($this->version === null) { - if (\substr_count($this->release, '.') + 1 === 3) { - $this->version = $this->release; - } else { - $this->version = $this->release . '-dev'; - } - $git = $this->getGitInformation($this->path); - if ($git) { - if (\substr_count($this->release, '.') + 1 === 3) { - $this->version = $git; - } else { - $git = \explode('-', $git); - $this->version = $this->release . '-' . \end($git); - } - } - } - return $this->version; + $this->name = $name; + $this->cyclomaticComplexity = $cyclomaticComplexity; } /** - * @return bool|string + * @psalm-return non-empty-string */ - private function getGitInformation(string $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; - } -} - and contributors -All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -* Neither the name of Arne Blankerts nor the names of contributors - may be used to endorse or promote products derived from this software - without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT * NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS -BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, -OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. -ensureValidUri($value); - $this->value = $value; - } - public function asString() : string - { - return $this->value; - } - private function ensureValidUri($value) : void + public function name(): string { - if (\strpos($value, ':') === \false) { - throw new NamespaceUriException(\sprintf("Namespace URI '%s' must contain at least one colon", $value)); - } + return $this->name; } -} -line = $line; - $this->name = $name; - $this->value = $value; - } - public function getLine() : int + public function cyclomaticComplexity(): int { - return $this->line; + return $this->cyclomaticComplexity; } - public function getName() : string + public function isFunction(): bool { - return $this->name; + return !$this->isMethod(); } - public function getValue() : string + public function isMethod(): bool { - return $this->value; + return str_contains($this->name, '::'); } } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnitPHAR\SebastianBergmann\Complexity; -class TokenCollection implements \ArrayAccess, \Iterator, \Countable +use function array_filter; +use function array_merge; +use function array_reverse; +use function array_values; +use function count; +use function usort; +use Countable; +use IteratorAggregate; +/** + * @psalm-immutable + */ +final class ComplexityCollection implements Countable, IteratorAggregate { - /** @var Token[] */ - private $tokens = []; - /** @var int */ - private $pos; - public function addToken(Token $token) : void + /** + * @psalm-var list + */ + private readonly array $items; + public static function fromList(Complexity ...$items): self { - $this->tokens[] = $token; + return new self($items); } - public function current() : Token + /** + * @psalm-param list $items + */ + private function __construct(array $items) { - return \current($this->tokens); + $this->items = $items; } - public function key() : int + /** + * @psalm-return list + */ + public function asArray(): array { - return \key($this->tokens); + return $this->items; } - public function next() : void + public function getIterator(): ComplexityCollectionIterator { - \next($this->tokens); - $this->pos++; + return new ComplexityCollectionIterator($this); } - public function valid() : bool + /** + * @psalm-return non-negative-int + */ + public function count(): int { - return $this->count() > $this->pos; + return count($this->items); } - public function rewind() : void + public function isEmpty(): bool { - \reset($this->tokens); - $this->pos = 0; + return empty($this->items); } - public function count() : int + /** + * @psalm-return non-negative-int + */ + public function cyclomaticComplexity(): int { - return \count($this->tokens); + $cyclomaticComplexity = 0; + foreach ($this as $item) { + $cyclomaticComplexity += $item->cyclomaticComplexity(); + } + return $cyclomaticComplexity; } - public function offsetExists($offset) : bool + public function isFunction(): self { - return isset($this->tokens[$offset]); + return new self(array_values(array_filter($this->items, static fn(Complexity $complexity): bool => $complexity->isFunction()))); } - /** - * @throws TokenCollectionException - */ - public function offsetGet($offset) : Token + public function isMethod(): self { - if (!$this->offsetExists($offset)) { - throw new TokenCollectionException(\sprintf('No Token at offest %s', $offset)); - } - return $this->tokens[$offset]; + return new self(array_values(array_filter($this->items, static fn(Complexity $complexity): bool => $complexity->isMethod()))); } - /** - * @param Token $value - * - * @throws TokenCollectionException - */ - public function offsetSet($offset, $value) : void + public function mergeWith(self $other): self { - 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; + return new self(array_merge($this->asArray(), $other->asArray())); } - public function offsetUnset($offset) : void + public function sortByDescendingCyclomaticComplexity(): self { - unset($this->tokens[$offset]); + $items = $this->items; + usort($items, static function (Complexity $a, Complexity $b): int { + return $a->cyclomaticComplexity() <=> $b->cyclomaticComplexity(); + }); + return new self(array_reverse($items)); } } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnitPHAR\SebastianBergmann\Complexity; -class Tokenizer +use Iterator; +final class ComplexityCollectionIterator implements Iterator { /** - * Token Map for "non-tokens" - * - * @var array + * @psalm-var list */ - 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 + private readonly array $items; + private int $position = 0; + public function __construct(ComplexityCollection $items) { - $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]); - if (!$values) { - $result->addToken(new Token($line, \token_name($tok[0]), '{binary data}')); - continue; - } - foreach ($values as $v) { - $token = new Token($line, \token_name($tok[0]), $v); - $lastToken = $token; - $line++; - if ($v === '') { - continue; - } - $result->addToken($token); - } - } - return $this->fillBlanks($result, $lastToken->getLine()); + $this->items = $items->asArray(); } - private function fillBlanks(TokenCollection $tokens, int $maxLine) : TokenCollection + public function rewind(): void { - $prev = new Token(0, 'Placeholder', ''); - $final = new TokenCollection(); - foreach ($tokens as $token) { - $gap = $token->getLine() - $prev->getLine(); - while ($gap > 1) { - $linebreak = new Token($prev->getLine() + 1, 'T_WHITESPACE', ''); - $final->addToken($linebreak); - $prev = $linebreak; - $gap--; - } - $final->addToken($token); - $prev = $token; - } - $gap = $maxLine - $prev->getLine(); - while ($gap > 0) { - $linebreak = new Token($prev->getLine() + 1, 'T_WHITESPACE', ''); - $final->addToken($linebreak); - $prev = $linebreak; - $gap--; - } - return $final; + $this->position = 0; } -} -xmlns = $xmlns; + return isset($this->items[$this->position]); } - public function toDom(TokenCollection $tokens) : DOMDocument + public function key(): int { - $dom = new DOMDocument(); - $dom->preserveWhiteSpace = \false; - $dom->loadXML($this->toXML($tokens)); - return $dom; + return $this->position; } - public function toXML(TokenCollection $tokens) : string + public function current(): Complexity { - $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(); + return $this->items[$this->position]; } - private function addToken(Token $token) : void + public function next(): void { - 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(); - } + $this->position++; } } + * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnitPHAR\Webmozart\Assert; +namespace PHPUnitPHAR\SebastianBergmann\Complexity; -use ArrayAccess; -use BadMethodCallException; -use Closure; -use Countable; -use DateTime; -use DateTimeImmutable; -use Exception; -use ResourceBundle; -use SimpleXMLElement; use Throwable; -use Traversable; -/** - * Efficient assertions to validate the input/output of your methods. +interface Exception extends Throwable +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnitPHAR\SebastianBergmann\Complexity; + +final class RuntimeException extends \RuntimeException implements Exception +{ +} +BSD 3-Clause License + +Copyright (c) 2020-2023, Sebastian Bergmann +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * - * @author Bernhard Schussek + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. */ -class Assert +namespace PHPUnitPHAR\SebastianBergmann\Complexity; + +use function assert; +use function is_array; +use PHPUnitPHAR\PhpParser\Node; +use PHPUnitPHAR\PhpParser\Node\Expr\New_; +use PHPUnitPHAR\PhpParser\Node\Name; +use PHPUnitPHAR\PhpParser\Node\Stmt; +use PHPUnitPHAR\PhpParser\Node\Stmt\Class_; +use PHPUnitPHAR\PhpParser\Node\Stmt\ClassMethod; +use PHPUnitPHAR\PhpParser\Node\Stmt\Function_; +use PHPUnitPHAR\PhpParser\Node\Stmt\Interface_; +use PHPUnitPHAR\PhpParser\Node\Stmt\Trait_; +use PHPUnitPHAR\PhpParser\NodeTraverser; +use PHPUnitPHAR\PhpParser\NodeVisitorAbstract; +final class ComplexityCalculatingVisitor extends NodeVisitorAbstract { - use Mixin; /** - * @psalm-pure - * @psalm-assert string $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function string($value, $message = '') - { - if (!\is_string($value)) { - static::reportInvalidArgument(\sprintf($message ?: 'Expected a string. Got: %s', static::typeToString($value))); - } - } - /** - * @psalm-pure - * @psalm-assert non-empty-string $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException + * @psalm-var list */ - public static function stringNotEmpty($value, $message = '') + private array $result = []; + private bool $shortCircuitTraversal; + public function __construct(bool $shortCircuitTraversal) { - static::string($value, $message); - static::notEq($value, '', $message); + $this->shortCircuitTraversal = $shortCircuitTraversal; } - /** - * @psalm-pure - * @psalm-assert int $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function integer($value, $message = '') + public function enterNode(Node $node): ?int { - if (!\is_int($value)) { - static::reportInvalidArgument(\sprintf($message ?: 'Expected an integer. Got: %s', static::typeToString($value))); + if (!$node instanceof ClassMethod && !$node instanceof Function_) { + return null; } - } - /** - * @psalm-pure - * @psalm-assert numeric $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function integerish($value, $message = '') - { - if (!\is_numeric($value) || $value != (int) $value) { - static::reportInvalidArgument(\sprintf($message ?: 'Expected an integerish value. Got: %s', static::typeToString($value))); + if ($node instanceof ClassMethod) { + if ($node->getAttribute('parent') instanceof Interface_) { + return null; + } + if ($node->isAbstract()) { + return null; + } + $name = $this->classMethodName($node); + } else { + $name = $this->functionName($node); } - } - /** - * @psalm-pure - * @psalm-assert positive-int $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function positiveInteger($value, $message = '') - { - if (!(\is_int($value) && $value > 0)) { - static::reportInvalidArgument(\sprintf($message ?: 'Expected a positive integer. Got: %s', static::valueToString($value))); + $statements = $node->getStmts(); + assert(is_array($statements)); + $this->result[] = new Complexity($name, $this->cyclomaticComplexity($statements)); + if ($this->shortCircuitTraversal) { + return NodeTraverser::DONT_TRAVERSE_CHILDREN; } + return null; } - /** - * @psalm-pure - * @psalm-assert float $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function float($value, $message = '') + public function result(): ComplexityCollection { - if (!\is_float($value)) { - static::reportInvalidArgument(\sprintf($message ?: 'Expected a float. Got: %s', static::typeToString($value))); - } + return ComplexityCollection::fromList(...$this->result); } /** - * @psalm-pure - * @psalm-assert numeric $value - * - * @param mixed $value - * @param string $message + * @param Stmt[] $statements * - * @throws InvalidArgumentException + * @psalm-return positive-int */ - public static function numeric($value, $message = '') + private function cyclomaticComplexity(array $statements): int { - if (!\is_numeric($value)) { - static::reportInvalidArgument(\sprintf($message ?: 'Expected a numeric. Got: %s', static::typeToString($value))); - } + $traverser = new NodeTraverser(); + $cyclomaticComplexityCalculatingVisitor = new CyclomaticComplexityCalculatingVisitor(); + $traverser->addVisitor($cyclomaticComplexityCalculatingVisitor); + /* @noinspection UnusedFunctionResultInspection */ + $traverser->traverse($statements); + return $cyclomaticComplexityCalculatingVisitor->cyclomaticComplexity(); } /** - * @psalm-pure - * @psalm-assert positive-int|0 $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException + * @psalm-return non-empty-string */ - public static function natural($value, $message = '') + private function classMethodName(ClassMethod $node): string { - if (!\is_int($value) || $value < 0) { - static::reportInvalidArgument(\sprintf($message ?: 'Expected a non-negative integer. Got: %s', static::valueToString($value))); + $parent = $node->getAttribute('parent'); + assert($parent instanceof Class_ || $parent instanceof Trait_); + if ($parent->getAttribute('parent') instanceof New_) { + return 'anonymous class'; } + assert(isset($parent->namespacedName)); + assert($parent->namespacedName instanceof Name); + return $parent->namespacedName->toString() . '::' . $node->name->toString(); } /** - * @psalm-pure - * @psalm-assert bool $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException + * @psalm-return non-empty-string */ - public static function boolean($value, $message = '') + private function functionName(Function_ $node): string { - if (!\is_bool($value)) { - static::reportInvalidArgument(\sprintf($message ?: 'Expected a boolean. Got: %s', static::typeToString($value))); - } + assert(isset($node->namespacedName)); + assert($node->namespacedName instanceof Name); + $functionName = $node->namespacedName->toString(); + assert($functionName !== ''); + return $functionName; } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnitPHAR\SebastianBergmann\Complexity; + +use PHPUnitPHAR\PhpParser\Node; +use PHPUnitPHAR\PhpParser\Node\Expr\BinaryOp\BooleanAnd; +use PHPUnitPHAR\PhpParser\Node\Expr\BinaryOp\BooleanOr; +use PHPUnitPHAR\PhpParser\Node\Expr\BinaryOp\LogicalAnd; +use PHPUnitPHAR\PhpParser\Node\Expr\BinaryOp\LogicalOr; +use PHPUnitPHAR\PhpParser\Node\Expr\Ternary; +use PHPUnitPHAR\PhpParser\Node\Stmt\Case_; +use PHPUnitPHAR\PhpParser\Node\Stmt\Catch_; +use PHPUnitPHAR\PhpParser\Node\Stmt\ElseIf_; +use PHPUnitPHAR\PhpParser\Node\Stmt\For_; +use PHPUnitPHAR\PhpParser\Node\Stmt\Foreach_; +use PHPUnitPHAR\PhpParser\Node\Stmt\If_; +use PHPUnitPHAR\PhpParser\Node\Stmt\While_; +use PHPUnitPHAR\PhpParser\NodeVisitorAbstract; +final class CyclomaticComplexityCalculatingVisitor extends NodeVisitorAbstract +{ /** - * @psalm-pure - * @psalm-assert scalar $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException + * @psalm-var positive-int */ - public static function scalar($value, $message = '') + private int $cyclomaticComplexity = 1; + public function enterNode(Node $node): void { - if (!\is_scalar($value)) { - static::reportInvalidArgument(\sprintf($message ?: 'Expected a scalar. Got: %s', static::typeToString($value))); + switch ($node::class) { + case BooleanAnd::class: + case BooleanOr::class: + case Case_::class: + case Catch_::class: + case ElseIf_::class: + case For_::class: + case Foreach_::class: + case If_::class: + case LogicalAnd::class: + case LogicalOr::class: + case Ternary::class: + case While_::class: + $this->cyclomaticComplexity++; } } /** - * @psalm-pure - * @psalm-assert object $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException + * @psalm-return positive-int */ - public static function object($value, $message = '') + public function cyclomaticComplexity(): int { - if (!\is_object($value)) { - static::reportInvalidArgument(\sprintf($message ?: 'Expected an object. Got: %s', static::typeToString($value))); - } + return $this->cyclomaticComplexity; } - /** - * @psalm-pure - * @psalm-assert resource $value - * - * @param mixed $value - * @param string|null $type type of resource this should be. @see https://www.php.net/manual/en/function.get-resource-type.php - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function resource($value, $type = null, $message = '') +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnitPHAR\SebastianBergmann\Diff; + +use ArrayIterator; +use IteratorAggregate; +use Traversable; +/** + * @template-implements IteratorAggregate + */ +final class Chunk implements IteratorAggregate +{ + private int $start; + private int $startRange; + private int $end; + private int $endRange; + private array $lines; + public function __construct(int $start = 0, int $startRange = 1, int $end = 0, int $endRange = 1, array $lines = []) { - if (!\is_resource($value)) { - static::reportInvalidArgument(\sprintf($message ?: 'Expected a resource. Got: %s', static::typeToString($value))); - } - if ($type && $type !== \get_resource_type($value)) { - static::reportInvalidArgument(\sprintf($message ?: 'Expected a resource of type %2$s. Got: %s', static::typeToString($value), $type)); - } + $this->start = $start; + $this->startRange = $startRange; + $this->end = $end; + $this->endRange = $endRange; + $this->lines = $lines; } - /** - * @psalm-pure - * @psalm-assert callable $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function isCallable($value, $message = '') + public function start(): int { - if (!\is_callable($value)) { - static::reportInvalidArgument(\sprintf($message ?: 'Expected a callable. Got: %s', static::typeToString($value))); - } + return $this->start; } - /** - * @psalm-pure - * @psalm-assert array $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function isArray($value, $message = '') + public function startRange(): int { - if (!\is_array($value)) { - static::reportInvalidArgument(\sprintf($message ?: 'Expected an array. Got: %s', static::typeToString($value))); - } + return $this->startRange; } - /** - * @psalm-pure - * @psalm-assert iterable $value - * - * @deprecated use "isIterable" or "isInstanceOf" instead - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function isTraversable($value, $message = '') + public function end(): int { - @\trigger_error(\sprintf('The "%s" assertion is deprecated. You should stop using it, as it will soon be removed in 2.0 version. Use "isIterable" or "isInstanceOf" instead.', __METHOD__), \E_USER_DEPRECATED); - if (!\is_array($value) && !$value instanceof Traversable) { - static::reportInvalidArgument(\sprintf($message ?: 'Expected a traversable. Got: %s', static::typeToString($value))); - } + return $this->end; } - /** - * @psalm-pure - * @psalm-assert array|ArrayAccess $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function isArrayAccessible($value, $message = '') + public function endRange(): int { - if (!\is_array($value) && !$value instanceof ArrayAccess) { - static::reportInvalidArgument(\sprintf($message ?: 'Expected an array accessible. Got: %s', static::typeToString($value))); - } + return $this->endRange; } /** - * @psalm-pure - * @psalm-assert countable $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException + * @psalm-return list */ - public static function isCountable($value, $message = '') + public function lines(): array { - if (!\is_array($value) && !$value instanceof Countable && !$value instanceof ResourceBundle && !$value instanceof SimpleXMLElement) { - static::reportInvalidArgument(\sprintf($message ?: 'Expected a countable. Got: %s', static::typeToString($value))); - } + return $this->lines; } /** - * @psalm-pure - * @psalm-assert iterable $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException + * @psalm-param list $lines */ - public static function isIterable($value, $message = '') + public function setLines(array $lines): void { - if (!\is_array($value) && !$value instanceof Traversable) { - static::reportInvalidArgument(\sprintf($message ?: 'Expected an iterable. Got: %s', static::typeToString($value))); + foreach ($lines as $line) { + if (!$line instanceof Line) { + throw new InvalidArgumentException(); + } } + $this->lines = $lines; } /** - * @psalm-pure - * @psalm-template ExpectedType of object - * @psalm-param class-string $class - * @psalm-assert ExpectedType $value - * - * @param mixed $value - * @param string|object $class - * @param string $message - * - * @throws InvalidArgumentException + * @deprecated Use start() instead */ - public static function isInstanceOf($value, $class, $message = '') + public function getStart(): int { - if (!$value instanceof $class) { - static::reportInvalidArgument(\sprintf($message ?: 'Expected an instance of %2$s. Got: %s', static::typeToString($value), $class)); - } + return $this->start; } /** - * @psalm-pure - * @psalm-template ExpectedType of object - * @psalm-param class-string $class - * @psalm-assert !ExpectedType $value - * - * @param mixed $value - * @param string|object $class - * @param string $message - * - * @throws InvalidArgumentException + * @deprecated Use startRange() instead */ - public static function notInstanceOf($value, $class, $message = '') + public function getStartRange(): int { - if ($value instanceof $class) { - static::reportInvalidArgument(\sprintf($message ?: 'Expected an instance other than %2$s. Got: %s', static::typeToString($value), $class)); - } + return $this->startRange; } /** - * @psalm-pure - * @psalm-param array $classes - * - * @param mixed $value - * @param array $classes - * @param string $message - * - * @throws InvalidArgumentException + * @deprecated Use end() instead */ - public static function isInstanceOfAny($value, array $classes, $message = '') + public function getEnd(): int { - foreach ($classes as $class) { - if ($value instanceof $class) { - return; - } - } - static::reportInvalidArgument(\sprintf($message ?: 'Expected an instance of any of %2$s. Got: %s', static::typeToString($value), \implode(', ', \array_map(array(static::class, 'valueToString'), $classes)))); + return $this->end; } /** - * @psalm-pure - * @psalm-template ExpectedType of object - * @psalm-param class-string $class - * @psalm-assert ExpectedType|class-string $value - * - * @param object|string $value - * @param string $class - * @param string $message - * - * @throws InvalidArgumentException + * @deprecated Use endRange() instead */ - public static function isAOf($value, $class, $message = '') + public function getEndRange(): int { - static::string($class, 'Expected class as a string. Got: %s'); - if (!\is_a($value, $class, \is_string($value))) { - static::reportInvalidArgument(\sprintf($message ?: 'Expected an instance of this class or to this class among its parents "%2$s". Got: %s', static::valueToString($value), $class)); - } + return $this->endRange; } /** - * @psalm-pure - * @psalm-template UnexpectedType of object - * @psalm-param class-string $class - * @psalm-assert !UnexpectedType $value - * @psalm-assert !class-string $value + * @psalm-return list * - * @param object|string $value - * @param string $class - * @param string $message - * - * @throws InvalidArgumentException + * @deprecated Use lines() instead */ - public static function isNotA($value, $class, $message = '') + public function getLines(): array { - static::string($class, 'Expected class as a string. Got: %s'); - if (\is_a($value, $class, \is_string($value))) { - static::reportInvalidArgument(\sprintf($message ?: 'Expected an instance of this class or to this class among its parents other than "%2$s". Got: %s', static::valueToString($value), $class)); - } + return $this->lines; } - /** - * @psalm-pure - * @psalm-param array $classes - * - * @param object|string $value - * @param string[] $classes - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function isAnyOf($value, array $classes, $message = '') + public function getIterator(): Traversable { - foreach ($classes as $class) { - static::string($class, 'Expected class as a string. Got: %s'); - if (\is_a($value, $class, \is_string($value))) { - return; - } - } - static::reportInvalidArgument(\sprintf($message ?: 'Expected an instance of any of this classes or any of those classes among their parents "%2$s". Got: %s', static::valueToString($value), \implode(', ', $classes))); + return new ArrayIterator($this->lines); } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnitPHAR\SebastianBergmann\Diff; + +use ArrayIterator; +use IteratorAggregate; +use Traversable; +/** + * @template-implements IteratorAggregate + */ +final class Diff implements IteratorAggregate +{ /** - * @psalm-pure - * @psalm-assert empty $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException + * @psalm-var non-empty-string */ - public static function isEmpty($value, $message = '') - { - if (!empty($value)) { - static::reportInvalidArgument(\sprintf($message ?: 'Expected an empty value. Got: %s', static::valueToString($value))); - } - } + private string $from; /** - * @psalm-pure - * @psalm-assert !empty $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException + * @psalm-var non-empty-string */ - public static function notEmpty($value, $message = '') - { - if (empty($value)) { - static::reportInvalidArgument(\sprintf($message ?: 'Expected a non-empty value. Got: %s', static::valueToString($value))); - } - } + private string $to; /** - * @psalm-pure - * @psalm-assert null $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException + * @psalm-var list */ - public static function null($value, $message = '') - { - if (null !== $value) { - static::reportInvalidArgument(\sprintf($message ?: 'Expected null. Got: %s', static::valueToString($value))); - } - } + private array $chunks; /** - * @psalm-pure - * @psalm-assert !null $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException + * @psalm-param non-empty-string $from + * @psalm-param non-empty-string $to + * @psalm-param list $chunks */ - public static function notNull($value, $message = '') + public function __construct(string $from, string $to, array $chunks = []) { - if (null === $value) { - static::reportInvalidArgument($message ?: 'Expected a value other than null.'); - } + $this->from = $from; + $this->to = $to; + $this->chunks = $chunks; } /** - * @psalm-pure - * @psalm-assert true $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException + * @psalm-return non-empty-string */ - public static function true($value, $message = '') + public function from(): string { - if (\true !== $value) { - static::reportInvalidArgument(\sprintf($message ?: 'Expected a value to be true. Got: %s', static::valueToString($value))); - } + return $this->from; } /** - * @psalm-pure - * @psalm-assert false $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException + * @psalm-return non-empty-string */ - public static function false($value, $message = '') + public function to(): string { - if (\false !== $value) { - static::reportInvalidArgument(\sprintf($message ?: 'Expected a value to be false. Got: %s', static::valueToString($value))); - } + return $this->to; } /** - * @psalm-pure - * @psalm-assert !false $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException + * @psalm-return list */ - public static function notFalse($value, $message = '') + public function chunks(): array { - if (\false === $value) { - static::reportInvalidArgument($message ?: 'Expected a value other than false.'); - } + return $this->chunks; } /** - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException + * @psalm-param list $chunks */ - public static function ip($value, $message = '') + public function setChunks(array $chunks): void { - if (\false === \filter_var($value, \FILTER_VALIDATE_IP)) { - static::reportInvalidArgument(\sprintf($message ?: 'Expected a value to be an IP. Got: %s', static::valueToString($value))); - } + $this->chunks = $chunks; } /** - * @param mixed $value - * @param string $message + * @psalm-return non-empty-string * - * @throws InvalidArgumentException + * @deprecated */ - public static function ipv4($value, $message = '') + public function getFrom(): string { - if (\false === \filter_var($value, \FILTER_VALIDATE_IP, \FILTER_FLAG_IPV4)) { - static::reportInvalidArgument(\sprintf($message ?: 'Expected a value to be an IPv4. Got: %s', static::valueToString($value))); - } + return $this->from; } /** - * @param mixed $value - * @param string $message + * @psalm-return non-empty-string * - * @throws InvalidArgumentException + * @deprecated */ - public static function ipv6($value, $message = '') + public function getTo(): string { - if (\false === \filter_var($value, \FILTER_VALIDATE_IP, \FILTER_FLAG_IPV6)) { - static::reportInvalidArgument(\sprintf($message ?: 'Expected a value to be an IPv6. Got: %s', static::valueToString($value))); - } + return $this->to; } /** - * @param mixed $value - * @param string $message + * @psalm-return list * - * @throws InvalidArgumentException + * @deprecated */ - public static function email($value, $message = '') + public function getChunks(): array { - if (\false === \filter_var($value, \FILTER_VALIDATE_EMAIL)) { - static::reportInvalidArgument(\sprintf($message ?: 'Expected a value to be a valid e-mail address. Got: %s', static::valueToString($value))); - } + return $this->chunks; } - /** - * Does non strict comparisons on the items, so ['3', 3] will not pass the assertion. - * - * @param array $values - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function uniqueValues(array $values, $message = '') + public function getIterator(): Traversable { - $allValues = \count($values); - $uniqueValues = \count(\array_unique($values)); - if ($allValues !== $uniqueValues) { - $difference = $allValues - $uniqueValues; - static::reportInvalidArgument(\sprintf($message ?: 'Expected an array of unique values, but %s of them %s duplicated', $difference, 1 === $difference ? 'is' : 'are')); - } + return new ArrayIterator($this->chunks); } - /** - * @param mixed $value - * @param mixed $expect - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function eq($value, $expect, $message = '') +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnitPHAR\SebastianBergmann\Diff; + +use const PHP_INT_SIZE; +use const PREG_SPLIT_DELIM_CAPTURE; +use const PREG_SPLIT_NO_EMPTY; +use function array_shift; +use function array_unshift; +use function array_values; +use function count; +use function current; +use function end; +use function is_string; +use function key; +use function min; +use function preg_split; +use function prev; +use function reset; +use function str_ends_with; +use function substr; +use PHPUnitPHAR\SebastianBergmann\Diff\Output\DiffOutputBuilderInterface; +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; + private DiffOutputBuilderInterface $outputBuilder; + public function __construct(DiffOutputBuilderInterface $outputBuilder) { - if ($expect != $value) { - static::reportInvalidArgument(\sprintf($message ?: 'Expected a value equal to %2$s. Got: %s', static::valueToString($value), static::valueToString($expect))); - } + $this->outputBuilder = $outputBuilder; } - /** - * @param mixed $value - * @param mixed $expect - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function notEq($value, $expect, $message = '') + public function diff(array|string $from, array|string $to, ?LongestCommonSubsequenceCalculator $lcs = null): string { - if ($expect == $value) { - static::reportInvalidArgument(\sprintf($message ?: 'Expected a different value than %s.', static::valueToString($expect))); - } + $diff = $this->diffToArray($from, $to, $lcs); + return $this->outputBuilder->getDiff($diff); } - /** - * @psalm-pure - * - * @param mixed $value - * @param mixed $expect - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function same($value, $expect, $message = '') + public function diffToArray(array|string $from, array|string $to, ?LongestCommonSubsequenceCalculator $lcs = null): array { - if ($expect !== $value) { - static::reportInvalidArgument(\sprintf($message ?: 'Expected a value identical to %2$s. Got: %s', static::valueToString($value), static::valueToString($expect))); + if (is_string($from)) { + $from = $this->splitStringByLines($from); } - } - /** - * @psalm-pure - * - * @param mixed $value - * @param mixed $expect - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function notSame($value, $expect, $message = '') - { - if ($expect === $value) { - static::reportInvalidArgument(\sprintf($message ?: 'Expected a value not identical to %s.', static::valueToString($expect))); + if (is_string($to)) { + $to = $this->splitStringByLines($to); } - } - /** - * @psalm-pure - * - * @param mixed $value - * @param mixed $limit - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function greaterThan($value, $limit, $message = '') - { - if ($value <= $limit) { - static::reportInvalidArgument(\sprintf($message ?: 'Expected a value greater than %2$s. Got: %s', static::valueToString($value), static::valueToString($limit))); + [$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; } - /** - * @psalm-pure - * - * @param mixed $value - * @param mixed $limit - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function greaterThanEq($value, $limit, $message = '') + private function splitStringByLines(string $input): array { - if ($value < $limit) { - static::reportInvalidArgument(\sprintf($message ?: 'Expected a value greater than or equal to %2$s. Got: %s', static::valueToString($value), static::valueToString($limit))); - } + return preg_split('/(.*\R)/', $input, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY); } - /** - * @psalm-pure - * - * @param mixed $value - * @param mixed $limit - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function lessThan($value, $limit, $message = '') + private function selectLcsImplementation(array $from, array $to): LongestCommonSubsequenceCalculator { - if ($value >= $limit) { - static::reportInvalidArgument(\sprintf($message ?: 'Expected a value less than %2$s. Got: %s', static::valueToString($value), static::valueToString($limit))); + // 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(); } - /** - * @psalm-pure - * - * @param mixed $value - * @param mixed $limit - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function lessThanEq($value, $limit, $message = '') + private function calculateEstimatedFootprint(array $from, array $to): float|int { - if ($value > $limit) { - static::reportInvalidArgument(\sprintf($message ?: 'Expected a value less than or equal to %2$s. Got: %s', static::valueToString($value), static::valueToString($limit))); - } + $itemSize = (PHP_INT_SIZE === 4) ? 76 : 144; + return $itemSize * min(count($from), count($to)) ** 2; } - /** - * Inclusive range, so Assert::(3, 3, 5) passes. - * - * @psalm-pure - * - * @param mixed $value - * @param mixed $min - * @param mixed $max - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function range($value, $min, $max, $message = '') + private function detectUnmatchedLineEndings(array $diff): bool { - if ($value < $min || $value > $max) { - static::reportInvalidArgument(\sprintf($message ?: 'Expected a value between %2$s and %3$s. Got: %s', static::valueToString($value), static::valueToString($min), static::valueToString($max))); + $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; } - /** - * A more human-readable alias of Assert::inArray(). - * - * @psalm-pure - * - * @param mixed $value - * @param array $values - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function oneOf($value, array $values, $message = '') - { - static::inArray($value, $values, $message); - } - /** - * Does strict comparison, so Assert::inArray(3, ['3']) does not pass the assertion. - * - * @psalm-pure - * - * @param mixed $value - * @param array $values - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function inArray($value, array $values, $message = '') + private function getLinebreak($line): string { - if (!\in_array($value, $values, \true)) { - static::reportInvalidArgument(\sprintf($message ?: 'Expected one of: %2$s. Got: %s', static::valueToString($value), \implode(', ', \array_map(array(static::class, 'valueToString'), $values)))); + if (!is_string($line)) { + return ''; } - } - /** - * @psalm-pure - * - * @param string $value - * @param string $subString - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function contains($value, $subString, $message = '') - { - if (\false === \strpos($value, $subString)) { - static::reportInvalidArgument(\sprintf($message ?: 'Expected a value to contain %2$s. Got: %s', static::valueToString($value), static::valueToString($subString))); + $lc = substr($line, -1); + if ("\r" === $lc) { + return "\r"; } - } - /** - * @psalm-pure - * - * @param string $value - * @param string $subString - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function notContains($value, $subString, $message = '') - { - if (\false !== \strpos($value, $subString)) { - static::reportInvalidArgument(\sprintf($message ?: '%2$s was not expected to be contained in a value. Got: %s', static::valueToString($value), static::valueToString($subString))); + if ("\n" !== $lc) { + return ''; } - } - /** - * @psalm-pure - * - * @param string $value - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function notWhitespaceOnly($value, $message = '') - { - if (\preg_match('/^\\s*$/', $value)) { - static::reportInvalidArgument(\sprintf($message ?: 'Expected a non-whitespace string. Got: %s', static::valueToString($value))); + if (str_ends_with($line, "\r\n")) { + return "\r\n"; } + return "\n"; } - /** - * @psalm-pure - * - * @param string $value - * @param string $prefix - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function startsWith($value, $prefix, $message = '') + private static function getArrayDiffParted(array &$from, array &$to): array { - if (0 !== \strpos($value, $prefix)) { - static::reportInvalidArgument(\sprintf($message ?: 'Expected a value to start with %2$s. Got: %s', static::valueToString($value), static::valueToString($prefix))); + $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]; } - /** - * @psalm-pure - * - * @param string $value - * @param string $prefix - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function notStartsWith($value, $prefix, $message = '') +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnitPHAR\SebastianBergmann\Diff; + +use function gettype; +use function is_object; +use function sprintf; +use Exception; +final class ConfigurationException extends InvalidArgumentException +{ + public function __construct(string $option, string $expected, $value, int $code = 0, ?Exception $previous = null) { - if (0 === \strpos($value, $prefix)) { - static::reportInvalidArgument(\sprintf($message ?: 'Expected a value not to start with %2$s. Got: %s', static::valueToString($value), static::valueToString($prefix))); - } + parent::__construct(sprintf('Option "%s" must be %s, got "%s".', $option, $expected, is_object($value) ? $value::class : ((null === $value) ? '' : (gettype($value) . '#' . $value))), $code, $previous); } - /** - * @psalm-pure - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function startsWithLetter($value, $message = '') +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnitPHAR\SebastianBergmann\Diff; + +use Throwable; +interface Exception extends Throwable +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnitPHAR\SebastianBergmann\Diff; + +class InvalidArgumentException extends \InvalidArgumentException implements Exception +{ +} +BSD 3-Clause License + +Copyright (c) 2002-2024, Sebastian Bergmann +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnitPHAR\SebastianBergmann\Diff; + +final class Line +{ + public const ADDED = 1; + public const REMOVED = 2; + public const UNCHANGED = 3; + private int $type; + private string $content; + public function __construct(int $type = self::UNCHANGED, string $content = '') { - static::string($value); - $valid = isset($value[0]); - if ($valid) { - $locale = \setlocale(\LC_CTYPE, 0); - \setlocale(\LC_CTYPE, 'C'); - $valid = \ctype_alpha($value[0]); - \setlocale(\LC_CTYPE, $locale); - } - if (!$valid) { - static::reportInvalidArgument(\sprintf($message ?: 'Expected a value to start with a letter. Got: %s', static::valueToString($value))); - } + $this->type = $type; + $this->content = $content; } - /** - * @psalm-pure - * - * @param string $value - * @param string $suffix - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function endsWith($value, $suffix, $message = '') + public function content(): string { - if ($suffix !== \substr($value, -\strlen($suffix))) { - static::reportInvalidArgument(\sprintf($message ?: 'Expected a value to end with %2$s. Got: %s', static::valueToString($value), static::valueToString($suffix))); - } + return $this->content; } - /** - * @psalm-pure - * - * @param string $value - * @param string $suffix - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function notEndsWith($value, $suffix, $message = '') + public function type(): int { - if ($suffix === \substr($value, -\strlen($suffix))) { - static::reportInvalidArgument(\sprintf($message ?: 'Expected a value not to end with %2$s. Got: %s', static::valueToString($value), static::valueToString($suffix))); - } + return $this->type; } - /** - * @psalm-pure - * - * @param string $value - * @param string $pattern - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function regex($value, $pattern, $message = '') + public function isAdded(): bool { - if (!\preg_match($pattern, $value)) { - static::reportInvalidArgument(\sprintf($message ?: 'The value %s does not match the expected pattern.', static::valueToString($value))); - } + return $this->type === self::ADDED; } - /** - * @psalm-pure - * - * @param string $value - * @param string $pattern - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function notRegex($value, $pattern, $message = '') + public function isRemoved(): bool { - if (\preg_match($pattern, $value, $matches, \PREG_OFFSET_CAPTURE)) { - static::reportInvalidArgument(\sprintf($message ?: 'The value %s matches the pattern %s (at offset %d).', static::valueToString($value), static::valueToString($pattern), $matches[0][1])); - } + return $this->type === self::REMOVED; } - /** - * @psalm-pure - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function unicodeLetters($value, $message = '') + public function isUnchanged(): bool { - static::string($value); - if (!\preg_match('/^\\p{L}+$/u', $value)) { - static::reportInvalidArgument(\sprintf($message ?: 'Expected a value to contain only Unicode letters. Got: %s', static::valueToString($value))); - } + return $this->type === self::UNCHANGED; } /** - * @psalm-pure - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException + * @deprecated */ - public static function alpha($value, $message = '') + public function getContent(): string { - static::string($value); - $locale = \setlocale(\LC_CTYPE, 0); - \setlocale(\LC_CTYPE, 'C'); - $valid = !\ctype_alpha($value); - \setlocale(\LC_CTYPE, $locale); - if ($valid) { - static::reportInvalidArgument(\sprintf($message ?: 'Expected a value to contain only letters. Got: %s', static::valueToString($value))); - } + return $this->content; } /** - * @psalm-pure - * - * @param string $value - * @param string $message - * - * @throws InvalidArgumentException + * @deprecated */ - public static function digits($value, $message = '') + public function getType(): int { - $locale = \setlocale(\LC_CTYPE, 0); - \setlocale(\LC_CTYPE, 'C'); - $valid = !\ctype_digit($value); - \setlocale(\LC_CTYPE, $locale); - if ($valid) { - static::reportInvalidArgument(\sprintf($message ?: 'Expected a value to contain digits only. Got: %s', static::valueToString($value))); - } + return $this->type; } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnitPHAR\SebastianBergmann\Diff; + +interface LongestCommonSubsequenceCalculator +{ /** - * @psalm-pure - * - * @param string $value - * @param string $message - * - * @throws InvalidArgumentException + * Calculates the longest common subsequence of two arrays. */ - public static function alnum($value, $message = '') - { - $locale = \setlocale(\LC_CTYPE, 0); - \setlocale(\LC_CTYPE, 'C'); - $valid = !\ctype_alnum($value); - \setlocale(\LC_CTYPE, $locale); - if ($valid) { - static::reportInvalidArgument(\sprintf($message ?: 'Expected a value to contain letters and digits only. Got: %s', static::valueToString($value))); - } - } + public function calculate(array $from, array $to): array; +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnitPHAR\SebastianBergmann\Diff; + +use function array_fill; +use function array_merge; +use function array_reverse; +use function array_slice; +use function count; +use function in_array; +use function max; +final class MemoryEfficientLongestCommonSubsequenceCalculator implements LongestCommonSubsequenceCalculator +{ /** - * @psalm-pure - * @psalm-assert lowercase-string $value - * - * @param string $value - * @param string $message - * - * @throws InvalidArgumentException + * @inheritDoc */ - public static function lower($value, $message = '') + public function calculate(array $from, array $to): array { - $locale = \setlocale(\LC_CTYPE, 0); - \setlocale(\LC_CTYPE, 'C'); - $valid = !\ctype_lower($value); - \setlocale(\LC_CTYPE, $locale); - if ($valid) { - static::reportInvalidArgument(\sprintf($message ?: 'Expected a value to contain lowercase characters only. Got: %s', static::valueToString($value))); + $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)); } - /** - * @psalm-pure - * @psalm-assert !lowercase-string $value - * - * @param string $value - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function upper($value, $message = '') + private function length(array $from, array $to): array { - $locale = \setlocale(\LC_CTYPE, 0); - \setlocale(\LC_CTYPE, 'C'); - $valid = !\ctype_upper($value); - \setlocale(\LC_CTYPE, $locale); - if ($valid) { - static::reportInvalidArgument(\sprintf($message ?: 'Expected a value to contain uppercase characters only. Got: %s', static::valueToString($value))); + $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 if ($current[$j] > $prev[$j + 1]) { + $current[$j + 1] = $current[$j]; + } else { + $current[$j + 1] = $prev[$j + 1]; + } + } } + return $current; } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnitPHAR\SebastianBergmann\Diff\Output; + +use function count; +abstract class AbstractChunkOutputBuilder implements DiffOutputBuilderInterface +{ /** - * @psalm-pure - * - * @param string $value - * @param int $length - * @param string $message - * - * @throws InvalidArgumentException + * Takes input of the diff array and returns the common parts. + * Iterates through diff line by line. */ - public static function length($value, $length, $message = '') + protected function getCommonChunks(array $diff, int $lineThreshold = 5): array { - if ($length !== static::strlen($value)) { - static::reportInvalidArgument(\sprintf($message ?: 'Expected a value to contain %2$s characters. Got: %s', static::valueToString($value), $length)); + $diffSize = count($diff); + $capturing = \false; + $chunkStart = 0; + $chunkSize = 0; + $commonChunks = []; + for ($i = 0; $i < $diffSize; $i++) { + if ($diff[$i][1] === 0) { + if ($capturing === \false) { + $capturing = \true; + $chunkStart = $i; + $chunkSize = 0; + } else { + $chunkSize++; + } + } elseif ($capturing !== \false) { + if ($chunkSize >= $lineThreshold) { + $commonChunks[$chunkStart] = $chunkStart + $chunkSize; + } + $capturing = \false; + } + } + if ($capturing !== \false && $chunkSize >= $lineThreshold) { + $commonChunks[$chunkStart] = $chunkStart + $chunkSize; } + return $commonChunks; } - /** - * Inclusive min. - * - * @psalm-pure - * - * @param string $value - * @param int|float $min - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function minLength($value, $min, $message = '') +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnitPHAR\SebastianBergmann\Diff\Output; + +use function fclose; +use function fopen; +use function fwrite; +use function str_ends_with; +use function stream_get_contents; +use function substr; +use PHPUnitPHAR\SebastianBergmann\Diff\Differ; +/** + * Builds a diff string representation in a loose unified diff format + * listing only changes lines. Does not include line numbers. + */ +final class DiffOnlyOutputBuilder implements DiffOutputBuilderInterface +{ + private string $header; + public function __construct(string $header = "--- Original\n+++ New\n") { - if (static::strlen($value) < $min) { - static::reportInvalidArgument(\sprintf($message ?: 'Expected a value to contain at least %2$s characters. Got: %s', static::valueToString($value), $min)); - } + $this->header = $header; } - /** - * Inclusive max. - * - * @psalm-pure - * - * @param string $value - * @param int|float $max - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function maxLength($value, $max, $message = '') + public function getDiff(array $diff): string { - if (static::strlen($value) > $max) { - static::reportInvalidArgument(\sprintf($message ?: 'Expected a value to contain at most %2$s characters. Got: %s', static::valueToString($value), $max)); + $buffer = fopen('php://memory', 'r+b'); + if ('' !== $this->header) { + fwrite($buffer, $this->header); + if (!str_ends_with($this->header, "\n")) { + fwrite($buffer, "\n"); + } + } + foreach ($diff as $diffEntry) { + if ($diffEntry[1] === Differ::ADDED) { + fwrite($buffer, '+' . $diffEntry[0]); + } elseif ($diffEntry[1] === Differ::REMOVED) { + fwrite($buffer, '-' . $diffEntry[0]); + } elseif ($diffEntry[1] === Differ::DIFF_LINE_END_WARNING) { + fwrite($buffer, ' ' . $diffEntry[0]); + continue; + // Warnings should not be tested for line break, it will always be there + } else { + /* Not changed (old) 0 */ + continue; + // we didn't write the not-changed line, so do not add a line break either + } + $lc = substr($diffEntry[0], -1); + if ($lc !== "\n" && $lc !== "\r") { + fwrite($buffer, "\n"); + // \No newline at end of file + } } + $diff = stream_get_contents($buffer, -1, 0); + fclose($buffer); + return $diff; } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnitPHAR\SebastianBergmann\Diff\Output; + +/** + * Defines how an output builder should take a generated + * diff array and return a string representation of that diff. + */ +interface DiffOutputBuilderInterface +{ + public function getDiff(array $diff): string; +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnitPHAR\SebastianBergmann\Diff\Output; + +use function array_merge; +use function array_splice; +use function count; +use function fclose; +use function fopen; +use function fwrite; +use function is_bool; +use function is_int; +use function is_string; +use function max; +use function min; +use function sprintf; +use function stream_get_contents; +use function substr; +use PHPUnitPHAR\SebastianBergmann\Diff\ConfigurationException; +use PHPUnitPHAR\SebastianBergmann\Diff\Differ; +/** + * Strict Unified diff output builder. + * + * Generates (strict) Unified diff's (unidiffs) with hunks. + */ +final class StrictUnifiedDiffOutputBuilder implements DiffOutputBuilderInterface +{ + private static array $default = [ + 'collapseRanges' => \true, + // ranges of length one are rendered with the trailing `,1` + 'commonLineThreshold' => 6, + // number of same lines before ending a new hunk and creating a new one (if needed) + 'contextLines' => 3, + // like `diff: -u, -U NUM, --unified[=NUM]`, for patch/git apply compatibility best to keep at least @ 3 + 'fromFile' => null, + 'fromFileDate' => null, + 'toFile' => null, + 'toFileDate' => null, + ]; + private bool $changed; + private bool $collapseRanges; /** - * Inclusive , so Assert::lengthBetween('asd', 3, 5); passes the assertion. - * - * @psalm-pure - * - * @param string $value - * @param int|float $min - * @param int|float $max - * @param string $message - * - * @throws InvalidArgumentException + * @psalm-var positive-int */ - public static function lengthBetween($value, $min, $max, $message = '') - { - $length = static::strlen($value); - if ($length < $min || $length > $max) { - static::reportInvalidArgument(\sprintf($message ?: 'Expected a value to contain between %2$s and %3$s characters. Got: %s', static::valueToString($value), $min, $max)); - } - } + private int $commonLineThreshold; + private string $header; /** - * Will also pass if $value is a directory, use Assert::file() instead if you need to be sure it is a file. - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException + * @psalm-var positive-int */ - public static function fileExists($value, $message = '') + private int $contextLines; + public function __construct(array $options = []) { - static::string($value); - if (!\file_exists($value)) { - static::reportInvalidArgument(\sprintf($message ?: 'The file %s does not exist.', static::valueToString($value))); + $options = array_merge(self::$default, $options); + if (!is_bool($options['collapseRanges'])) { + throw new ConfigurationException('collapseRanges', 'a bool', $options['collapseRanges']); } - } - /** - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function file($value, $message = '') - { - static::fileExists($value, $message); - if (!\is_file($value)) { - static::reportInvalidArgument(\sprintf($message ?: 'The path %s is not a file.', static::valueToString($value))); + if (!is_int($options['contextLines']) || $options['contextLines'] < 0) { + throw new ConfigurationException('contextLines', 'an int >= 0', $options['contextLines']); } - } - /** - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function directory($value, $message = '') - { - static::fileExists($value, $message); - if (!\is_dir($value)) { - static::reportInvalidArgument(\sprintf($message ?: 'The path %s is no directory.', static::valueToString($value))); + if (!is_int($options['commonLineThreshold']) || $options['commonLineThreshold'] <= 0) { + throw new ConfigurationException('commonLineThreshold', 'an int > 0', $options['commonLineThreshold']); } + $this->assertString($options, 'fromFile'); + $this->assertString($options, 'toFile'); + $this->assertStringOrNull($options, 'fromFileDate'); + $this->assertStringOrNull($options, 'toFileDate'); + $this->header = sprintf("--- %s%s\n+++ %s%s\n", $options['fromFile'], (null === $options['fromFileDate']) ? '' : ("\t" . $options['fromFileDate']), $options['toFile'], (null === $options['toFileDate']) ? '' : ("\t" . $options['toFileDate'])); + $this->collapseRanges = $options['collapseRanges']; + $this->commonLineThreshold = $options['commonLineThreshold']; + $this->contextLines = $options['contextLines']; } - /** - * @param string $value - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function readable($value, $message = '') + public function getDiff(array $diff): string { - if (!\is_readable($value)) { - static::reportInvalidArgument(\sprintf($message ?: 'The path %s is not readable.', static::valueToString($value))); + if (0 === count($diff)) { + return ''; } - } - /** - * @param string $value - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function writable($value, $message = '') - { - if (!\is_writable($value)) { - static::reportInvalidArgument(\sprintf($message ?: 'The path %s is not writable.', static::valueToString($value))); + $this->changed = \false; + $buffer = fopen('php://memory', 'r+b'); + fwrite($buffer, $this->header); + $this->writeDiffHunks($buffer, $diff); + if (!$this->changed) { + fclose($buffer); + return ''; } + $diff = stream_get_contents($buffer, -1, 0); + fclose($buffer); + // If the last char is not a linebreak: add it. + // This might happen when both the `from` and `to` do not have a trailing linebreak + $last = substr($diff, -1); + return ("\n" !== $last && "\r" !== $last) ? $diff . "\n" : $diff; } - /** - * @psalm-assert class-string $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function classExists($value, $message = '') + private function writeDiffHunks($output, array $diff): void { - if (!\class_exists($value)) { - static::reportInvalidArgument(\sprintf($message ?: 'Expected an existing class name. Got: %s', static::valueToString($value))); + // detect "No newline at end of file" and insert into `$diff` if needed + $upperLimit = count($diff); + if (0 === $diff[$upperLimit - 1][1]) { + $lc = substr($diff[$upperLimit - 1][0], -1); + if ("\n" !== $lc) { + array_splice($diff, $upperLimit, 0, [["\n\\ No newline at end of file\n", Differ::NO_LINE_END_EOF_WARNING]]); + } + } else { + // search back for the last `+` and `-` line, + // check if it has a trailing linebreak, else add a warning under it + $toFind = [1 => \true, 2 => \true]; + for ($i = $upperLimit - 1; $i >= 0; $i--) { + if (isset($toFind[$diff[$i][1]])) { + unset($toFind[$diff[$i][1]]); + $lc = substr($diff[$i][0], -1); + if ("\n" !== $lc) { + array_splice($diff, $i + 1, 0, [["\n\\ No newline at end of file\n", Differ::NO_LINE_END_EOF_WARNING]]); + } + if (!count($toFind)) { + break; + } + } + } } - } - /** - * @psalm-pure - * @psalm-template ExpectedType of object - * @psalm-param class-string $class - * @psalm-assert class-string|ExpectedType $value - * - * @param mixed $value - * @param string|object $class - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function subclassOf($value, $class, $message = '') - { - if (!\is_subclass_of($value, $class)) { - static::reportInvalidArgument(\sprintf($message ?: 'Expected a sub-class of %2$s. Got: %s', static::valueToString($value), static::valueToString($class))); + // write hunks to output buffer + $cutOff = max($this->commonLineThreshold, $this->contextLines); + $hunkCapture = \false; + $sameCount = $toRange = $fromRange = 0; + $toStart = $fromStart = 1; + $i = 0; + /** @var int $i */ + foreach ($diff as $i => $entry) { + if (0 === $entry[1]) { + // same + if (\false === $hunkCapture) { + $fromStart++; + $toStart++; + continue; + } + $sameCount++; + $toRange++; + $fromRange++; + if ($sameCount === $cutOff) { + $contextStartOffset = ($hunkCapture - $this->contextLines < 0) ? $hunkCapture : $this->contextLines; + // note: $contextEndOffset = $this->contextLines; + // + // because we never go beyond the end of the diff. + // with the cutoff/contextlines here the follow is never true; + // + // if ($i - $cutOff + $this->contextLines + 1 > \count($diff)) { + // $contextEndOffset = count($diff) - 1; + // } + // + // ; that would be true for a trailing incomplete hunk case which is dealt with after this loop + $this->writeHunk($diff, $hunkCapture - $contextStartOffset, $i - $cutOff + $this->contextLines + 1, $fromStart - $contextStartOffset, $fromRange - $cutOff + $contextStartOffset + $this->contextLines, $toStart - $contextStartOffset, $toRange - $cutOff + $contextStartOffset + $this->contextLines, $output); + $fromStart += $fromRange; + $toStart += $toRange; + $hunkCapture = \false; + $sameCount = $toRange = $fromRange = 0; + } + continue; + } + $sameCount = 0; + if ($entry[1] === Differ::NO_LINE_END_EOF_WARNING) { + continue; + } + $this->changed = \true; + if (\false === $hunkCapture) { + $hunkCapture = $i; + } + if (Differ::ADDED === $entry[1]) { + // added + $toRange++; + } + if (Differ::REMOVED === $entry[1]) { + // removed + $fromRange++; + } + } + if (\false === $hunkCapture) { + return; } + // we end here when cutoff (commonLineThreshold) was not reached, but we were capturing a hunk, + // do not render hunk till end automatically because the number of context lines might be less than the commonLineThreshold + $contextStartOffset = ($hunkCapture - $this->contextLines < 0) ? $hunkCapture : $this->contextLines; + // prevent trying to write out more common lines than there are in the diff _and_ + // do not write more than configured through the context lines + $contextEndOffset = min($sameCount, $this->contextLines); + $fromRange -= $sameCount; + $toRange -= $sameCount; + $this->writeHunk($diff, $hunkCapture - $contextStartOffset, $i - $sameCount + $contextEndOffset + 1, $fromStart - $contextStartOffset, $fromRange + $contextStartOffset + $contextEndOffset, $toStart - $contextStartOffset, $toRange + $contextStartOffset + $contextEndOffset, $output); } - /** - * @psalm-assert class-string $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function interfaceExists($value, $message = '') + private function writeHunk(array $diff, int $diffStartIndex, int $diffEndIndex, int $fromStart, int $fromRange, int $toStart, int $toRange, $output): void { - if (!\interface_exists($value)) { - static::reportInvalidArgument(\sprintf($message ?: 'Expected an existing interface name. got %s', static::valueToString($value))); + fwrite($output, '@@ -' . $fromStart); + if (!$this->collapseRanges || 1 !== $fromRange) { + fwrite($output, ',' . $fromRange); + } + fwrite($output, ' +' . $toStart); + if (!$this->collapseRanges || 1 !== $toRange) { + fwrite($output, ',' . $toRange); + } + fwrite($output, " @@\n"); + for ($i = $diffStartIndex; $i < $diffEndIndex; $i++) { + if ($diff[$i][1] === Differ::ADDED) { + $this->changed = \true; + fwrite($output, '+' . $diff[$i][0]); + } elseif ($diff[$i][1] === Differ::REMOVED) { + $this->changed = \true; + fwrite($output, '-' . $diff[$i][0]); + } elseif ($diff[$i][1] === Differ::OLD) { + fwrite($output, ' ' . $diff[$i][0]); + } elseif ($diff[$i][1] === Differ::NO_LINE_END_EOF_WARNING) { + $this->changed = \true; + fwrite($output, $diff[$i][0]); + } + // } elseif ($diff[$i][1] === Differ::DIFF_LINE_END_WARNING) { // custom comment inserted by PHPUnit/diff package + // skip + // } else { + // unknown/invalid + // } } } - /** - * @psalm-pure - * @psalm-template ExpectedType of object - * @psalm-param class-string $interface - * @psalm-assert class-string $value - * - * @param mixed $value - * @param mixed $interface - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function implementsInterface($value, $interface, $message = '') + private function assertString(array $options, string $option): void { - if (!\in_array($interface, \class_implements($value))) { - static::reportInvalidArgument(\sprintf($message ?: 'Expected an implementation of %2$s. Got: %s', static::valueToString($value), static::valueToString($interface))); + if (!is_string($options[$option])) { + throw new ConfigurationException($option, 'a string', $options[$option]); } } - /** - * @psalm-pure - * @psalm-param class-string|object $classOrObject - * - * @param string|object $classOrObject - * @param mixed $property - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function propertyExists($classOrObject, $property, $message = '') + private function assertStringOrNull(array $options, string $option): void { - if (!\property_exists($classOrObject, $property)) { - static::reportInvalidArgument(\sprintf($message ?: 'Expected the property %s to exist.', static::valueToString($property))); + if (null !== $options[$option] && !is_string($options[$option])) { + throw new ConfigurationException($option, 'a string or ', $options[$option]); } } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnitPHAR\SebastianBergmann\Diff\Output; + +use function array_splice; +use function count; +use function fclose; +use function fopen; +use function fwrite; +use function max; +use function min; +use function str_ends_with; +use function stream_get_contents; +use function substr; +use PHPUnitPHAR\SebastianBergmann\Diff\Differ; +/** + * Builds a diff string representation in unified diff format in chunks. + */ +final class UnifiedDiffOutputBuilder extends AbstractChunkOutputBuilder +{ + private bool $collapseRanges = \true; + private int $commonLineThreshold = 6; /** - * @psalm-pure - * @psalm-param class-string|object $classOrObject - * - * @param string|object $classOrObject - * @param mixed $property - * @param string $message - * - * @throws InvalidArgumentException + * @psalm-var positive-int */ - public static function propertyNotExists($classOrObject, $property, $message = '') + private int $contextLines = 3; + private string $header; + private bool $addLineNumbers; + public function __construct(string $header = "--- Original\n+++ New\n", bool $addLineNumbers = \false) { - if (\property_exists($classOrObject, $property)) { - static::reportInvalidArgument(\sprintf($message ?: 'Expected the property %s to not exist.', static::valueToString($property))); - } + $this->header = $header; + $this->addLineNumbers = $addLineNumbers; } - /** - * @psalm-pure - * @psalm-param class-string|object $classOrObject - * - * @param string|object $classOrObject - * @param mixed $method - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function methodExists($classOrObject, $method, $message = '') + public function getDiff(array $diff): string { - if (!(\is_string($classOrObject) || \is_object($classOrObject)) || !\method_exists($classOrObject, $method)) { - static::reportInvalidArgument(\sprintf($message ?: 'Expected the method %s to exist.', static::valueToString($method))); + $buffer = fopen('php://memory', 'r+b'); + if ('' !== $this->header) { + fwrite($buffer, $this->header); + if (!str_ends_with($this->header, "\n")) { + fwrite($buffer, "\n"); + } + } + if (0 !== count($diff)) { + $this->writeDiffHunks($buffer, $diff); } + $diff = stream_get_contents($buffer, -1, 0); + fclose($buffer); + // If the diff is non-empty and last char is not a linebreak: add it. + // This might happen when both the `from` and `to` do not have a trailing linebreak + $last = substr($diff, -1); + return ('' !== $diff && "\n" !== $last && "\r" !== $last) ? $diff . "\n" : $diff; } - /** - * @psalm-pure - * @psalm-param class-string|object $classOrObject - * - * @param string|object $classOrObject - * @param mixed $method - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function methodNotExists($classOrObject, $method, $message = '') + private function writeDiffHunks($output, array $diff): void { - if ((\is_string($classOrObject) || \is_object($classOrObject)) && \method_exists($classOrObject, $method)) { - static::reportInvalidArgument(\sprintf($message ?: 'Expected the method %s to not exist.', static::valueToString($method))); + // detect "No newline at end of file" and insert into `$diff` if needed + $upperLimit = count($diff); + if (0 === $diff[$upperLimit - 1][1]) { + $lc = substr($diff[$upperLimit - 1][0], -1); + if ("\n" !== $lc) { + array_splice($diff, $upperLimit, 0, [["\n\\ No newline at end of file\n", Differ::NO_LINE_END_EOF_WARNING]]); + } + } else { + // search back for the last `+` and `-` line, + // check if it has trailing linebreak, else add a warning under it + $toFind = [1 => \true, 2 => \true]; + for ($i = $upperLimit - 1; $i >= 0; $i--) { + if (isset($toFind[$diff[$i][1]])) { + unset($toFind[$diff[$i][1]]); + $lc = substr($diff[$i][0], -1); + if ("\n" !== $lc) { + array_splice($diff, $i + 1, 0, [["\n\\ No newline at end of file\n", Differ::NO_LINE_END_EOF_WARNING]]); + } + if (!count($toFind)) { + break; + } + } + } + } + // write hunks to output buffer + $cutOff = max($this->commonLineThreshold, $this->contextLines); + $hunkCapture = \false; + $sameCount = $toRange = $fromRange = 0; + $toStart = $fromStart = 1; + $i = 0; + /** @var int $i */ + foreach ($diff as $i => $entry) { + if (0 === $entry[1]) { + // same + if (\false === $hunkCapture) { + $fromStart++; + $toStart++; + continue; + } + $sameCount++; + $toRange++; + $fromRange++; + if ($sameCount === $cutOff) { + $contextStartOffset = ($hunkCapture - $this->contextLines < 0) ? $hunkCapture : $this->contextLines; + // note: $contextEndOffset = $this->contextLines; + // + // because we never go beyond the end of the diff. + // with the cutoff/contextlines here the follow is never true; + // + // if ($i - $cutOff + $this->contextLines + 1 > \count($diff)) { + // $contextEndOffset = count($diff) - 1; + // } + // + // ; that would be true for a trailing incomplete hunk case which is dealt with after this loop + $this->writeHunk($diff, $hunkCapture - $contextStartOffset, $i - $cutOff + $this->contextLines + 1, $fromStart - $contextStartOffset, $fromRange - $cutOff + $contextStartOffset + $this->contextLines, $toStart - $contextStartOffset, $toRange - $cutOff + $contextStartOffset + $this->contextLines, $output); + $fromStart += $fromRange; + $toStart += $toRange; + $hunkCapture = \false; + $sameCount = $toRange = $fromRange = 0; + } + continue; + } + $sameCount = 0; + if ($entry[1] === Differ::NO_LINE_END_EOF_WARNING) { + continue; + } + if (\false === $hunkCapture) { + $hunkCapture = $i; + } + if (Differ::ADDED === $entry[1]) { + $toRange++; + } + if (Differ::REMOVED === $entry[1]) { + $fromRange++; + } + } + if (\false === $hunkCapture) { + return; } + // we end here when cutoff (commonLineThreshold) was not reached, but we were capturing a hunk, + // do not render hunk till end automatically because the number of context lines might be less than the commonLineThreshold + $contextStartOffset = ($hunkCapture - $this->contextLines < 0) ? $hunkCapture : $this->contextLines; + // prevent trying to write out more common lines than there are in the diff _and_ + // do not write more than configured through the context lines + $contextEndOffset = min($sameCount, $this->contextLines); + $fromRange -= $sameCount; + $toRange -= $sameCount; + $this->writeHunk($diff, $hunkCapture - $contextStartOffset, $i - $sameCount + $contextEndOffset + 1, $fromStart - $contextStartOffset, $fromRange + $contextStartOffset + $contextEndOffset, $toStart - $contextStartOffset, $toRange + $contextStartOffset + $contextEndOffset, $output); } - /** - * @psalm-pure - * - * @param array $array - * @param string|int $key - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function keyExists($array, $key, $message = '') + private function writeHunk(array $diff, int $diffStartIndex, int $diffEndIndex, int $fromStart, int $fromRange, int $toStart, int $toRange, $output): void { - if (!(isset($array[$key]) || \array_key_exists($key, $array))) { - static::reportInvalidArgument(\sprintf($message ?: 'Expected the key %s to exist.', static::valueToString($key))); + if ($this->addLineNumbers) { + fwrite($output, '@@ -' . $fromStart); + if (!$this->collapseRanges || 1 !== $fromRange) { + fwrite($output, ',' . $fromRange); + } + fwrite($output, ' +' . $toStart); + if (!$this->collapseRanges || 1 !== $toRange) { + fwrite($output, ',' . $toRange); + } + fwrite($output, " @@\n"); + } else { + fwrite($output, "@@ @@\n"); + } + for ($i = $diffStartIndex; $i < $diffEndIndex; $i++) { + if ($diff[$i][1] === Differ::ADDED) { + fwrite($output, '+' . $diff[$i][0]); + } elseif ($diff[$i][1] === Differ::REMOVED) { + fwrite($output, '-' . $diff[$i][0]); + } elseif ($diff[$i][1] === Differ::OLD) { + fwrite($output, ' ' . $diff[$i][0]); + } elseif ($diff[$i][1] === Differ::NO_LINE_END_EOF_WARNING) { + fwrite($output, "\n"); + // $diff[$i][0] + } else { + /* Not changed (old) Differ::OLD or Warning Differ::DIFF_LINE_END_WARNING */ + fwrite($output, ' ' . $diff[$i][0]); + } } } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnitPHAR\SebastianBergmann\Diff; + +use function array_pop; +use function assert; +use function count; +use function max; +use function preg_match; +use function preg_split; +/** + * Unified diff parser. + */ +final class Parser +{ /** - * @psalm-pure - * - * @param array $array - * @param string|int $key - * @param string $message - * - * @throws InvalidArgumentException + * @return Diff[] */ - public static function keyNotExists($array, $key, $message = '') + public function parse(string $string): array { - if (isset($array[$key]) || \array_key_exists($key, $array)) { - static::reportInvalidArgument(\sprintf($message ?: 'Expected the key %s to not exist.', static::valueToString($key))); + $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('#^---\h+"?(?P[^\v\t"]+)#', $lines[$i], $fromMatch) && preg_match('#^\+\+\+\h+"?(?P[^\v\t"]+)#', $lines[$i + 1], $toMatch)) { + if ($diff !== null) { + $this->parseFileDiff($diff, $collected); + $diffs[] = $diff; + $collected = []; + } + assert(!empty($fromMatch['file'])); + assert(!empty($toMatch['file'])); + $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; } - /** - * Checks if a value is a valid array key (int or string). - * - * @psalm-pure - * @psalm-assert array-key $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function validArrayKey($value, $message = '') + private function parseFileDiff(Diff $diff, array $lines): void { - if (!(\is_int($value) || \is_string($value))) { - static::reportInvalidArgument(\sprintf($message ?: 'Expected string or integer. Got: %s', static::typeToString($value))); + $chunks = []; + $chunk = null; + $diffLines = []; + foreach ($lines as $line) { + if (preg_match('/^@@\s+-(?P\d+)(?:,\s*(?P\d+))?\s+\+(?P\d+)(?:,\s*(?P\d+))?\s+@@/', $line, $match, \PREG_UNMATCHED_AS_NULL)) { + $chunk = new Chunk((int) $match['start'], isset($match['startrange']) ? max(0, (int) $match['startrange']) : 1, (int) $match['end'], isset($match['endrange']) ? max(0, (int) $match['endrange']) : 1); + $chunks[] = $chunk; + $diffLines = []; + continue; + } + if (preg_match('/^(?P[+ -])?(?P.*)/', $line, $match)) { + $type = Line::UNCHANGED; + if ($match['type'] === '+') { + $type = Line::ADDED; + } elseif ($match['type'] === '-') { + $type = Line::REMOVED; + } + $diffLines[] = new Line($type, $match['line']); + $chunk?->setLines($diffLines); + } } + $diff->setChunks($chunks); } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnitPHAR\SebastianBergmann\Diff; + +use function array_reverse; +use function count; +use function max; +use SplFixedArray; +final class TimeEfficientLongestCommonSubsequenceCalculator implements LongestCommonSubsequenceCalculator +{ /** - * Does not check if $array is countable, this can generate a warning on php versions after 7.2. - * - * @param Countable|array $array - * @param int $number - * @param string $message - * - * @throws InvalidArgumentException + * @inheritDoc */ - public static function count($array, $number, $message = '') + public function calculate(array $from, array $to): array { - static::eq(\count($array), $number, \sprintf($message ?: 'Expected an array to contain %d elements. Got: %d.', $number, \count($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; + // don't use max() to avoid function call overhead + $firstOrLast = ($from[$i - 1] === $to[$j - 1]) ? $matrix[$o - $width - 1] + 1 : 0; + if ($matrix[$o - 1] > $matrix[$o - $width]) { + if ($firstOrLast > $matrix[$o - 1]) { + $matrix[$o] = $firstOrLast; + } else { + $matrix[$o] = $matrix[$o - 1]; + } + } else if ($firstOrLast > $matrix[$o - $width]) { + $matrix[$o] = $firstOrLast; + } else { + $matrix[$o] = $matrix[$o - $width]; + } + } + } + $i = $fromLength; + $j = $toLength; + while ($i > 0 && $j > 0) { + if ($from[$i - 1] === $to[$j - 1]) { + $common[] = $from[$i - 1]; + $i--; + $j--; + } else { + $o = $j * $width + $i; + if ($matrix[$o - $width] > $matrix[$o - 1]) { + $j--; + } else { + $i--; + } + } + } + return array_reverse($common); } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnitPHAR\SebastianBergmann\Environment; + +use const DIRECTORY_SEPARATOR; +use const STDIN; +use const STDOUT; +use function defined; +use function fclose; +use function fstat; +use function function_exists; +use function getenv; +use function is_resource; +use function is_string; +use function posix_isatty; +use function preg_match; +use function proc_close; +use function proc_open; +use function sapi_windows_vt100_support; +use function shell_exec; +use function stream_get_contents; +use function stream_isatty; +use function trim; +final class Console +{ /** - * Does not check if $array is countable, this can generate a warning on php versions after 7.2. - * - * @param Countable|array $array - * @param int|float $min - * @param string $message - * - * @throws InvalidArgumentException + * @var int */ - public static function minCount($array, $min, $message = '') - { - if (\count($array) < $min) { - static::reportInvalidArgument(\sprintf($message ?: 'Expected an array to contain at least %2$d elements. Got: %d', \count($array), $min)); - } - } + public const STDIN = 0; /** - * Does not check if $array is countable, this can generate a warning on php versions after 7.2. - * - * @param Countable|array $array - * @param int|float $max - * @param string $message - * - * @throws InvalidArgumentException + * @var int */ - public static function maxCount($array, $max, $message = '') - { - if (\count($array) > $max) { - static::reportInvalidArgument(\sprintf($message ?: 'Expected an array to contain at most %2$d elements. Got: %d', \count($array), $max)); - } - } + public const STDOUT = 1; /** - * Does not check if $array is countable, this can generate a warning on php versions after 7.2. - * - * @param Countable|array $array - * @param int|float $min - * @param int|float $max - * @param string $message - * - * @throws InvalidArgumentException + * @var int */ - public static function countBetween($array, $min, $max, $message = '') - { - $count = \count($array); - if ($count < $min || $count > $max) { - static::reportInvalidArgument(\sprintf($message ?: 'Expected an array to contain between %2$d and %3$d elements. Got: %d', $count, $min, $max)); - } - } + public const STDERR = 2; /** - * @psalm-pure - * @psalm-assert list $array - * - * @param mixed $array - * @param string $message + * Returns true if STDOUT supports colorization. * - * @throws InvalidArgumentException + * This code has been copied and adapted from + * Symfony\Component\Console\Output\StreamOutput. */ - public static function isList($array, $message = '') + public function hasColorSupport(): bool { - if (!\is_array($array)) { - static::reportInvalidArgument($message ?: 'Expected list - non-associative array.'); - } - if ($array === \array_values($array)) { - return; + if ('Hyper' === getenv('TERM_PROGRAM')) { + return \true; } - $nextKey = -1; - foreach ($array as $k => $v) { - if ($k !== ++$nextKey) { - static::reportInvalidArgument($message ?: 'Expected list - non-associative array.'); - } + 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 } - } - /** - * @psalm-pure - * @psalm-assert non-empty-list $array - * - * @param mixed $array - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function isNonEmptyList($array, $message = '') - { - static::isList($array, $message); - static::notEmpty($array, $message); - } - /** - * @psalm-pure - * @psalm-template T - * @psalm-param mixed|array $array - * @psalm-assert array $array - * - * @param mixed $array - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function isMap($array, $message = '') - { - if (!\is_array($array) || \array_keys($array) !== \array_filter(\array_keys($array), '\\is_string')) { - static::reportInvalidArgument($message ?: 'Expected map - associative array with string keys.'); + if (!defined('STDOUT')) { + // @codeCoverageIgnoreStart + return \false; + // @codeCoverageIgnoreEnd } + return $this->isInteractive(STDOUT); } /** - * @psalm-pure - * @psalm-template T - * @psalm-param mixed|array $array - * @psalm-assert array $array - * @psalm-assert !empty $array - * - * @param mixed $array - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function isNonEmptyMap($array, $message = '') - { - static::isMap($array, $message); - static::notEmpty($array, $message); - } - /** - * @psalm-pure - * - * @param string $value - * @param string $message + * Returns the number of columns of the terminal. * - * @throws InvalidArgumentException + * @codeCoverageIgnore */ - public static function uuid($value, $message = '') + public function getNumberOfColumns(): int { - $value = \str_replace(array('urn:', 'uuid:', '{', '}'), '', $value); - // The nil UUID is special form of UUID that is specified to have all - // 128 bits set to zero. - if ('00000000-0000-0000-0000-000000000000' === $value) { - return; + if (!$this->isInteractive(defined('STDIN') ? STDIN : self::STDIN)) { + return 80; } - if (!\preg_match('/^[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12}$/', $value)) { - static::reportInvalidArgument(\sprintf($message ?: 'Value %s is not a valid UUID.', static::valueToString($value))); + if ($this->isWindows()) { + return $this->getNumberOfColumnsWindows(); } + return $this->getNumberOfColumnsInteractive(); } /** - * @psalm-param class-string $class + * Returns if the file descriptor is an interactive terminal or not. * - * @param Closure $expression - * @param string $class - * @param string $message + * Normally, we want to use a resource as a parameter, yet sadly it's not always available, + * eg when running code in interactive console (`php -a`), STDIN/STDOUT/STDERR constants are not defined. * - * @throws InvalidArgumentException + * @param int|resource $fileDescriptor */ - public static function throws(Closure $expression, $class = 'Exception', $message = '') + public function isInteractive($fileDescriptor = self::STDOUT): bool { - static::string($class); - $actual = 'none'; - try { - $expression(); - } catch (Exception $e) { - $actual = \get_class($e); - if ($e instanceof $class) { - return; + if (is_resource($fileDescriptor)) { + if (function_exists('stream_isatty') && @stream_isatty($fileDescriptor)) { + return \true; } - } catch (Throwable $e) { - $actual = \get_class($e); - if ($e instanceof $class) { - return; + if (function_exists('fstat')) { + $stat = @fstat(STDOUT); + return $stat && 020000 === ($stat['mode'] & 0170000); } + return \false; } - static::reportInvalidArgument($message ?: \sprintf('Expected to throw "%s", got "%s"', $class, $actual)); + return function_exists('posix_isatty') && @posix_isatty($fileDescriptor); } - /** - * @throws BadMethodCallException - */ - public static function __callStatic($name, $arguments) + private function isWindows(): bool { - if ('nullOr' === \substr($name, 0, 6)) { - if (null !== $arguments[0]) { - $method = \lcfirst(\substr($name, 6)); - \call_user_func_array(array(static::class, $method), $arguments); - } - return; - } - if ('all' === \substr($name, 0, 3)) { - static::isIterable($arguments[0]); - $method = \lcfirst(\substr($name, 3)); - $args = $arguments; - foreach ($arguments[0] as $entry) { - $args[0] = $entry; - \call_user_func_array(array(static::class, $method), $args); - } - return; - } - throw new BadMethodCallException('No such method: ' . $name); + return DIRECTORY_SEPARATOR === '\\'; } /** - * @param mixed $value - * - * @return string + * @codeCoverageIgnore */ - protected static function valueToString($value) + private function getNumberOfColumnsInteractive(): int { - if (null === $value) { - return 'null'; - } - if (\true === $value) { - return 'true'; - } - if (\false === $value) { - return 'false'; - } - if (\is_array($value)) { - return 'array'; - } - if (\is_object($value)) { - if (\method_exists($value, '__toString')) { - return \get_class($value) . ': ' . self::valueToString($value->__toString()); - } - if ($value instanceof DateTime || $value instanceof DateTimeImmutable) { - return \get_class($value) . ': ' . self::valueToString($value->format('c')); + if (function_exists('shell_exec') && preg_match('#\d+ (\d+)#', shell_exec('stty size') ?: '', $match) === 1) { + if ((int) $match[1] > 0) { + return (int) $match[1]; } - return \get_class($value); } - if (\is_resource($value)) { - return 'resource'; - } - if (\is_string($value)) { - return '"' . $value . '"'; + if (function_exists('shell_exec') && preg_match('#columns = (\d+);#', shell_exec('stty') ?: '', $match) === 1) { + if ((int) $match[1] > 0) { + return (int) $match[1]; + } } - return (string) $value; + return 80; } /** - * @param mixed $value - * - * @return string + * @codeCoverageIgnore */ - protected static function typeToString($value) - { - return \is_object($value) ? \get_class($value) : \gettype($value); - } - protected static function strlen($value) + private function getNumberOfColumnsWindows(): int { - if (!\function_exists('mb_detect_encoding')) { - return \strlen($value); - } - if (\false === ($encoding = \mb_detect_encoding($value))) { - return \strlen($value); + $ansicon = getenv('ANSICON'); + $columns = 80; + if (is_string($ansicon) && preg_match('/^(\d+)x\d+ \(\d+x(\d+)\)$/', trim($ansicon), $matches)) { + $columns = (int) $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 = (int) $matches[2]; + } + } } - return \mb_strlen($value, $encoding); - } - /** - * @param string $message - * - * @throws InvalidArgumentException - * - * @psalm-pure this method is not supposed to perform side-effects - * @psalm-return never - */ - protected static function reportInvalidArgument($message) - { - throw new InvalidArgumentException($message); - } - private function __construct() - { + return $columns - 1; } } - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnitPHAR\Webmozart\Assert; +Copyright (c) 2014-2024, Sebastian Bergmann +All rights reserved. -class InvalidArgumentException extends \InvalidArgumentException -{ -} -The MIT License (MIT) +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: -Copyright (c) 2014 Bernhard Schussek +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. */ -trait Mixin +namespace PHPUnitPHAR\SebastianBergmann\Environment; + +use const PHP_BINARY; +use const PHP_BINDIR; +use const PHP_MAJOR_VERSION; +use const PHP_SAPI; +use const PHP_VERSION; +use function array_map; +use function array_merge; +use function escapeshellarg; +use function explode; +use function extension_loaded; +use function ini_get; +use function is_readable; +use function parse_ini_file; +use function php_ini_loaded_file; +use function php_ini_scanned_files; +use function phpversion; +use function sprintf; +use function strrpos; +final class Runtime { + private static string $rawBinary; + private static bool $initialized = \false; /** - * @psalm-pure - * @psalm-assert string|null $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void + * Returns true when Xdebug or PCOV is available or + * the runtime used is PHPDBG. */ - public static function nullOrString($value, $message = '') + public function canCollectCodeCoverage(): bool { - null === $value || static::string($value, $message); + return $this->hasXdebug() || $this->hasPCOV() || $this->hasPHPDBGCodeCoverage(); } /** - * @psalm-pure - * @psalm-assert iterable $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void + * Returns true when Zend OPcache is loaded, enabled, + * and is configured to discard comments. */ - public static function allString($value, $message = '') + public function discardsComments(): bool { - static::isIterable($value); - foreach ($value as $entry) { - static::string($entry, $message); + if (!$this->isOpcacheActive()) { + return \false; } - } - /** - * @psalm-pure - * @psalm-assert iterable $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allNullOrString($value, $message = '') - { - static::isIterable($value); - foreach ($value as $entry) { - null === $entry || static::string($entry, $message); + if (ini_get('opcache.save_comments') !== '0') { + return \false; } + return \true; } /** - * @psalm-pure - * @psalm-assert non-empty-string|null $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function nullOrStringNotEmpty($value, $message = '') - { - null === $value || static::stringNotEmpty($value, $message); - } - /** - * @psalm-pure - * @psalm-assert iterable $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void + * Returns true when Zend OPcache is loaded, enabled, + * and is configured to perform just-in-time compilation. */ - public static function allStringNotEmpty($value, $message = '') + public function performsJustInTimeCompilation(): bool { - static::isIterable($value); - foreach ($value as $entry) { - static::stringNotEmpty($entry, $message); + if (PHP_MAJOR_VERSION < 8) { + return \false; } - } - /** - * @psalm-pure - * @psalm-assert iterable $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allNullOrStringNotEmpty($value, $message = '') - { - static::isIterable($value); - foreach ($value as $entry) { - null === $entry || static::stringNotEmpty($entry, $message); + if (!$this->isOpcacheActive()) { + return \false; } + if (ini_get('opcache.jit_buffer_size') === '0') { + return \false; + } + $jit = ini_get('opcache.jit'); + if ($jit === 'disable' || $jit === 'off') { + return \false; + } + if (strrpos($jit, '0') === 3) { + return \false; + } + return \true; } /** - * @psalm-pure - * @psalm-assert int|null $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function nullOrInteger($value, $message = '') - { - null === $value || static::integer($value, $message); - } - /** - * @psalm-pure - * @psalm-assert iterable $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void + * Returns the raw path to the binary of the current runtime. */ - public static function allInteger($value, $message = '') + public function getRawBinary(): string { - static::isIterable($value); - foreach ($value as $entry) { - static::integer($entry, $message); + if (self::$initialized) { + return self::$rawBinary; + } + if (PHP_BINARY !== '') { + self::$rawBinary = PHP_BINARY; + self::$initialized = \true; + return self::$rawBinary; + } + // @codeCoverageIgnoreStart + $possibleBinaryLocations = [PHP_BINDIR . '/php', PHP_BINDIR . '/php-cli.exe', PHP_BINDIR . '/php.exe']; + foreach ($possibleBinaryLocations as $binary) { + if (is_readable($binary)) { + self::$rawBinary = $binary; + self::$initialized = \true; + return self::$rawBinary; + } } + self::$rawBinary = 'php'; + self::$initialized = \true; + return self::$rawBinary; + // @codeCoverageIgnoreEnd } /** - * @psalm-pure - * @psalm-assert iterable $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void + * Returns the escaped path to the binary of the current runtime. */ - public static function allNullOrInteger($value, $message = '') + public function getBinary(): string { - static::isIterable($value); - foreach ($value as $entry) { - null === $entry || static::integer($entry, $message); - } + return escapeshellarg($this->getRawBinary()); } - /** - * @psalm-pure - * @psalm-assert numeric|null $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function nullOrIntegerish($value, $message = '') + public function getNameWithVersion(): string { - null === $value || static::integerish($value, $message); + return $this->getName() . ' ' . $this->getVersion(); } - /** - * @psalm-pure - * @psalm-assert iterable $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allIntegerish($value, $message = '') + public function getNameWithVersionAndCodeCoverageDriver(): string { - static::isIterable($value); - foreach ($value as $entry) { - static::integerish($entry, $message); + if ($this->hasPCOV()) { + return sprintf('%s with PCOV %s', $this->getNameWithVersion(), phpversion('pcov')); + } + if ($this->hasXdebug()) { + return sprintf('%s with Xdebug %s', $this->getNameWithVersion(), phpversion('xdebug')); } + return $this->getNameWithVersion(); } - /** - * @psalm-pure - * @psalm-assert iterable $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allNullOrIntegerish($value, $message = '') + public function getName(): string { - static::isIterable($value); - foreach ($value as $entry) { - null === $entry || static::integerish($entry, $message); + if ($this->isPHPDBG()) { + // @codeCoverageIgnoreStart + return 'PHPDBG'; + // @codeCoverageIgnoreEnd } + return 'PHP'; } - /** - * @psalm-pure - * @psalm-assert positive-int|null $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function nullOrPositiveInteger($value, $message = '') + public function getVendorUrl(): string { - null === $value || static::positiveInteger($value, $message); + return 'https://www.php.net/'; } - /** - * @psalm-pure - * @psalm-assert iterable $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allPositiveInteger($value, $message = '') + public function getVersion(): string { - static::isIterable($value); - foreach ($value as $entry) { - static::positiveInteger($entry, $message); - } + return PHP_VERSION; } /** - * @psalm-pure - * @psalm-assert iterable $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void + * Returns true when the runtime used is PHP and Xdebug is loaded. */ - public static function allNullOrPositiveInteger($value, $message = '') + public function hasXdebug(): bool { - static::isIterable($value); - foreach ($value as $entry) { - null === $entry || static::positiveInteger($entry, $message); - } + return $this->isPHP() && extension_loaded('xdebug'); } /** - * @psalm-pure - * @psalm-assert float|null $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void + * Returns true when the runtime used is PHP without the PHPDBG SAPI. */ - public static function nullOrFloat($value, $message = '') + public function isPHP(): bool { - null === $value || static::float($value, $message); + return !$this->isPHPDBG(); } /** - * @psalm-pure - * @psalm-assert iterable $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void + * Returns true when the runtime used is PHP with the PHPDBG SAPI. */ - public static function allFloat($value, $message = '') + public function isPHPDBG(): bool { - static::isIterable($value); - foreach ($value as $entry) { - static::float($entry, $message); - } + return PHP_SAPI === 'phpdbg'; } /** - * @psalm-pure - * @psalm-assert iterable $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void + * Returns true when the runtime used is PHP with the PHPDBG SAPI + * and the phpdbg_*_oplog() functions are available (PHP >= 7.0). */ - public static function allNullOrFloat($value, $message = '') + public function hasPHPDBGCodeCoverage(): bool { - static::isIterable($value); - foreach ($value as $entry) { - null === $entry || static::float($entry, $message); - } + return $this->isPHPDBG(); } /** - * @psalm-pure - * @psalm-assert numeric|null $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void + * Returns true when the runtime used is PHP with PCOV loaded and enabled. */ - public static function nullOrNumeric($value, $message = '') + public function hasPCOV(): bool { - null === $value || static::numeric($value, $message); + return $this->isPHP() && extension_loaded('pcov') && ini_get('pcov.enabled'); } /** - * @psalm-pure - * @psalm-assert iterable $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException + * 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 void + * @return string[] */ - public static function allNumeric($value, $message = '') + public function getCurrentSettings(array $values): array { - static::isIterable($value); - foreach ($value as $entry) { - static::numeric($entry, $message); + $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 (empty($set)) { + continue; + } + if (!isset($config[$value]) || $set !== $config[$value]) { + $diff[$value] = sprintf('%s=%s', $value, $set); + } + } } + return $diff; } - /** - * @psalm-pure - * @psalm-assert iterable $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allNullOrNumeric($value, $message = '') + private function isOpcacheActive(): bool { - static::isIterable($value); - foreach ($value as $entry) { - null === $entry || static::numeric($entry, $message); + if (!extension_loaded('Zend OPcache')) { + return \false; + } + if ((PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') && ini_get('opcache.enable_cli') === '1') { + return \true; + } + if (PHP_SAPI !== 'cli' && PHP_SAPI !== 'phpdbg' && ini_get('opcache.enable') === '1') { + return \true; } + return \false; } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnitPHAR\SebastianBergmann\Exporter; + +use function bin2hex; +use function count; +use function get_resource_type; +use function gettype; +use function implode; +use function ini_get; +use function ini_set; +use function is_array; +use function is_float; +use function is_object; +use function is_resource; +use function is_string; +use function mb_strlen; +use function mb_substr; +use function preg_match; +use function spl_object_id; +use function sprintf; +use function str_repeat; +use function str_replace; +use function var_export; +use BackedEnum; +use PHPUnitPHAR\SebastianBergmann\RecursionContext\Context; +use SplObjectStorage; +use UnitEnum; +final class Exporter +{ /** - * @psalm-pure - * @psalm-assert positive-int|0|null $value - * - * @param mixed $value - * @param string $message + * Exports a value as a string. * - * @throws InvalidArgumentException + * The output of this method is similar to the output of print_r(), but + * improved in various aspects: * - * @return void + * - 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 */ - public static function nullOrNatural($value, $message = '') + public function export(mixed $value, int $indentation = 0): string { - null === $value || static::natural($value, $message); + return $this->recursiveExport($value, $indentation); } - /** - * @psalm-pure - * @psalm-assert iterable $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allNatural($value, $message = '') + public function shortenedRecursiveExport(array &$data, ?Context $context = null): string { - static::isIterable($value); - foreach ($value as $entry) { - static::natural($entry, $message); + $result = []; + $exporter = new self(); + if (!$context) { + $context = new Context(); + } + $array = $data; + /* @noinspection UnusedFunctionResultInspection */ + $context->add($data); + foreach ($array as $key => $value) { + if (is_array($value)) { + if ($context->contains($data[$key]) !== \false) { + $result[] = '*RECURSION*'; + } else { + $result[] = sprintf('[%s]', $this->shortenedRecursiveExport($data[$key], $context)); + } + } else { + $result[] = $exporter->shortenedExport($value); + } } + return implode(', ', $result); } /** - * @psalm-pure - * @psalm-assert iterable $value - * - * @param mixed $value - * @param string $message + * Exports a value into a single-line string. * - * @throws InvalidArgumentException + * The output of this method is similar to the output of + * SebastianBergmann\Exporter\Exporter::export(). * - * @return void + * Newlines are replaced by the visible string '\n'. + * Contents of arrays and objects (if any) are replaced by '...'. */ - public static function allNullOrNatural($value, $message = '') + public function shortenedExport(mixed $value): string { - static::isIterable($value); - foreach ($value as $entry) { - null === $entry || static::natural($entry, $message); + if (is_string($value)) { + $string = str_replace("\n", '', $this->export($value)); + if (mb_strlen($string) > 40) { + return mb_substr($string, 0, 30) . '...' . mb_substr($string, -7); + } + return $string; } + if ($value instanceof BackedEnum) { + return sprintf('%s Enum (%s, %s)', $value::class, $value->name, $this->export($value->value)); + } + if ($value instanceof UnitEnum) { + return sprintf('%s Enum (%s)', $value::class, $value->name); + } + if (is_object($value)) { + return sprintf('%s Object (%s)', $value::class, (count($this->toArray($value)) > 0) ? '...' : ''); + } + if (is_array($value)) { + return sprintf('[%s]', (count($value) > 0) ? '...' : ''); + } + return $this->export($value); } /** - * @psalm-pure - * @psalm-assert bool|null $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void + * Converts an object to an array containing all of its private, protected + * and public properties. */ - public static function nullOrBoolean($value, $message = '') + public function toArray(mixed $value): array { - null === $value || static::boolean($value, $message); + 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 ("\x00Error\x00trace" === $key || "\x00Exception\x00trace" === $key) { + continue; + } + // properties are transformed to keys in the following way: + // private $propertyName => "\0ClassName\0propertyName" + // protected $propertyName => "\0*\0propertyName" + // public $propertyName => "propertyName" + if (preg_match('/^\0.+\0(.+)$/', (string) $key, $matches)) { + $key = $matches[1]; + } + // See https://github.com/php/php-src/commit/5721132 + if ($key === "\x00gcdata") { + continue; + } + $array[$key] = $val; + } + // Some internal classes like SplObjectStorage do not 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 $_value) { + $array['Object #' . spl_object_id($_value)] = ['obj' => $_value, 'inf' => $value->getInfo()]; + } + $value->rewind(); + } + return $array; } - /** - * @psalm-pure - * @psalm-assert iterable $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allBoolean($value, $message = '') + private function recursiveExport(mixed &$value, int $indentation, ?Context $processed = null): string { - static::isIterable($value); - foreach ($value as $entry) { - static::boolean($entry, $message); + if ($value === null) { + return 'null'; + } + if ($value === \true) { + return 'true'; + } + if ($value === \false) { + return 'false'; + } + if (is_float($value)) { + $precisionBackup = ini_get('precision'); + ini_set('precision', '-1'); + try { + $valueStr = (string) $value; + if ((string) (int) $value === $valueStr) { + return $valueStr . '.0'; + } + return $valueStr; + } finally { + ini_set('precision', $precisionBackup); + } + } + if (gettype($value) === 'resource (closed)') { + return 'resource (closed)'; + } + if (is_resource($value)) { + return sprintf('resource(%d) of type (%s)', $value, get_resource_type($value)); + } + if ($value instanceof BackedEnum) { + return sprintf('%s Enum #%d (%s, %s)', $value::class, spl_object_id($value), $value->name, $this->export($value->value, $indentation)); + } + if ($value instanceof UnitEnum) { + return sprintf('%s Enum #%d (%s)', $value::class, spl_object_id($value), $value->name); + } + if (is_string($value)) { + // Match for most non-printable chars somewhat taking multibyte chars into account + if (preg_match('/[^\x09-\x0d\x1b\x20-\xff]/', $value)) { + return 'Binary String: 0x' . bin2hex($value); + } + return "'" . str_replace('', "\n", str_replace(["\r\n", "\n\r", "\r", "\n"], ['\r\n', '\n\r', '\r', '\n'], $value)) . "'"; + } + $whitespace = str_repeat(' ', 4 * $indentation); + if (!$processed) { + $processed = new Context(); + } + if (is_array($value)) { + if (($key = $processed->contains($value)) !== \false) { + return 'Array &' . $key; + } + $array = $value; + $key = $processed->add($value); + $values = ''; + if (count($array) > 0) { + foreach ($array as $k => $v) { + $values .= $whitespace . ' ' . $this->recursiveExport($k, $indentation) . ' => ' . $this->recursiveExport($value[$k], $indentation + 1, $processed) . ",\n"; + } + $values = "\n" . $values . $whitespace; + } + return 'Array &' . (string) $key . ' [' . $values . ']'; + } + if (is_object($value)) { + $class = $value::class; + if ($processed->contains($value)) { + return $class . ' Object #' . spl_object_id($value); + } + $processed->add($value); + $values = ''; + $array = $this->toArray($value); + if (count($array) > 0) { + foreach ($array as $k => $v) { + $values .= $whitespace . ' ' . $this->recursiveExport($k, $indentation) . ' => ' . $this->recursiveExport($v, $indentation + 1, $processed) . ",\n"; + } + $values = "\n" . $values . $whitespace; + } + return $class . ' Object #' . spl_object_id($value) . ' (' . $values . ')'; } + return var_export($value, \true); } - /** - * @psalm-pure - * @psalm-assert iterable $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allNullOrBoolean($value, $message = '') +} +BSD 3-Clause License + +Copyright (c) 2002-2024, Sebastian Bergmann +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnitPHAR\SebastianBergmann\GlobalState; + +use const PHP_EOL; +use function is_array; +use function is_scalar; +use function serialize; +use function sprintf; +use function var_export; +final class CodeExporter +{ + public function constants(Snapshot $snapshot): string { - static::isIterable($value); - foreach ($value as $entry) { - null === $entry || static::boolean($entry, $message); + $result = ''; + foreach ($snapshot->constants() as $name => $value) { + $result .= sprintf('if (!defined(\'%s\')) define(\'%s\', %s);' . "\n", $name, $name, $this->exportVariable($value)); } + return $result; } - /** - * @psalm-pure - * @psalm-assert scalar|null $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function nullOrScalar($value, $message = '') + public function globalVariables(Snapshot $snapshot): string { - null === $value || static::scalar($value, $message); - } - /** - * @psalm-pure - * @psalm-assert iterable $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allScalar($value, $message = '') + $result = <<<'EOT' +call_user_func( + function () { - static::isIterable($value); - foreach ($value as $entry) { - static::scalar($entry, $message); + foreach (array_keys($GLOBALS) as $key) { + unset($GLOBALS[$key]); } } - /** - * @psalm-pure - * @psalm-assert iterable $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allNullOrScalar($value, $message = '') - { - static::isIterable($value); - foreach ($value as $entry) { - null === $entry || static::scalar($entry, $message); +); + + +EOT; + foreach ($snapshot->globalVariables() as $name => $value) { + $result .= sprintf('$GLOBALS[%s] = %s;' . PHP_EOL, $this->exportVariable($name), $this->exportVariable($value)); } + return $result; } - /** - * @psalm-pure - * @psalm-assert object|null $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function nullOrObject($value, $message = '') + public function iniSettings(Snapshot $snapshot): string { - null === $value || static::object($value, $message); + $result = ''; + foreach ($snapshot->iniSettings() as $key => $value) { + $result .= sprintf('@ini_set(%s, %s);' . "\n", $this->exportVariable($key), $this->exportVariable($value)); + } + return $result; } - /** - * @psalm-pure - * @psalm-assert iterable $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allObject($value, $message = '') + private function exportVariable(mixed $variable): string { - static::isIterable($value); - foreach ($value as $entry) { - static::object($entry, $message); + if (is_scalar($variable) || null === $variable || is_array($variable) && $this->arrayOnlyContainsScalars($variable)) { + return var_export($variable, \true); } + return 'unserialize(' . var_export(serialize($variable), \true) . ')'; } - /** - * @psalm-pure - * @psalm-assert iterable $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allNullOrObject($value, $message = '') + private function arrayOnlyContainsScalars(array $array): bool { - static::isIterable($value); - foreach ($value as $entry) { - null === $entry || static::object($entry, $message); + $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; } - /** - * @psalm-pure - * @psalm-assert resource|null $value - * - * @param mixed $value - * @param string|null $type type of resource this should be. @see https://www.php.net/manual/en/function.get-resource-type.php - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function nullOrResource($value, $type = null, $message = '') +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnitPHAR\SebastianBergmann\GlobalState; + +use function in_array; +use function str_starts_with; +use ReflectionClass; +final class ExcludeList +{ + private array $globalVariables = []; + private array $classes = []; + private array $classNamePrefixes = []; + private array $parentClasses = []; + private array $interfaces = []; + private array $staticProperties = []; + public function addGlobalVariable(string $variableName): void { - null === $value || static::resource($value, $type, $message); + $this->globalVariables[$variableName] = \true; } - /** - * @psalm-pure - * @psalm-assert iterable $value - * - * @param mixed $value - * @param string|null $type type of resource this should be. @see https://www.php.net/manual/en/function.get-resource-type.php - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allResource($value, $type = null, $message = '') + public function addClass(string $className): void { - static::isIterable($value); - foreach ($value as $entry) { - static::resource($entry, $type, $message); - } + $this->classes[] = $className; } - /** - * @psalm-pure - * @psalm-assert iterable $value - * - * @param mixed $value - * @param string|null $type type of resource this should be. @see https://www.php.net/manual/en/function.get-resource-type.php - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allNullOrResource($value, $type = null, $message = '') + public function addSubclassesOf(string $className): void { - static::isIterable($value); - foreach ($value as $entry) { - null === $entry || static::resource($entry, $type, $message); - } + $this->parentClasses[] = $className; } - /** - * @psalm-pure - * @psalm-assert callable|null $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function nullOrIsCallable($value, $message = '') + public function addImplementorsOf(string $interfaceName): void { - null === $value || static::isCallable($value, $message); + $this->interfaces[] = $interfaceName; } - /** - * @psalm-pure - * @psalm-assert iterable $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allIsCallable($value, $message = '') + public function addClassNamePrefix(string $classNamePrefix): void { - static::isIterable($value); - foreach ($value as $entry) { - static::isCallable($entry, $message); - } + $this->classNamePrefixes[] = $classNamePrefix; } - /** - * @psalm-pure - * @psalm-assert iterable $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allNullOrIsCallable($value, $message = '') + public function addStaticProperty(string $className, string $propertyName): void { - static::isIterable($value); - foreach ($value as $entry) { - null === $entry || static::isCallable($entry, $message); + if (!isset($this->staticProperties[$className])) { + $this->staticProperties[$className] = []; } + $this->staticProperties[$className][$propertyName] = \true; } - /** - * @psalm-pure - * @psalm-assert array|null $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function nullOrIsArray($value, $message = '') + public function isGlobalVariableExcluded(string $variableName): bool { - null === $value || static::isArray($value, $message); + return isset($this->globalVariables[$variableName]); } /** - * @psalm-pure - * @psalm-assert iterable $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void + * @psalm-param class-string $className */ - public static function allIsArray($value, $message = '') + public function isStaticPropertyExcluded(string $className, string $propertyName): bool { - static::isIterable($value); - foreach ($value as $entry) { - static::isArray($entry, $message); + if (in_array($className, $this->classes, \true)) { + return \true; + } + foreach ($this->classNamePrefixes as $prefix) { + if (str_starts_with($className, $prefix)) { + 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; + } } + return isset($this->staticProperties[$className][$propertyName]); } - /** - * @psalm-pure - * @psalm-assert iterable $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allNullOrIsArray($value, $message = '') +} +BSD 3-Clause License + +Copyright (c) 2001-2024, Sebastian Bergmann +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnitPHAR\SebastianBergmann\GlobalState; + +use function array_diff; +use function array_key_exists; +use function array_keys; +use function array_merge; +use function in_array; +use function is_array; +use ReflectionClass; +use ReflectionProperty; +final class Restorer +{ + public function restoreGlobalVariables(Snapshot $snapshot): void { - static::isIterable($value); - foreach ($value as $entry) { - null === $entry || static::isArray($entry, $message); + $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, \true) && !$snapshot->excludeList()->isGlobalVariableExcluded($key)) { + if (array_key_exists($key, $globalVariables)) { + $GLOBALS[$key] = $globalVariables[$key]; + } else { + unset($GLOBALS[$key]); + } + } } } - /** - * @psalm-pure - * @psalm-assert iterable|null $value - * - * @deprecated use "isIterable" or "isInstanceOf" instead - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function nullOrIsTraversable($value, $message = '') + public function restoreStaticProperties(Snapshot $snapshot): void { - null === $value || static::isTraversable($value, $message); + $current = new Snapshot($snapshot->excludeList(), \false, \false, \false, \false, \true, \false, \false, \false, \false); + $newClasses = array_diff($current->classes(), $snapshot->classes()); + unset($current); + foreach ($snapshot->staticProperties() as $className => $staticProperties) { + foreach ($staticProperties as $name => $value) { + $reflector = new ReflectionProperty($className, $name); + $reflector->setValue(null, $value); + } + } + foreach ($newClasses as $className) { + $class = new ReflectionClass($className); + $defaults = $class->getDefaultProperties(); + foreach ($class->getProperties() as $property) { + if (!$property->isStatic()) { + continue; + } + $name = $property->getName(); + if ($snapshot->excludeList()->isStaticPropertyExcluded($className, $name)) { + continue; + } + if (!isset($defaults[$name])) { + continue; + } + $property->setValue(null, $defaults[$name]); + } + } } - /** - * @psalm-pure - * @psalm-assert iterable $value - * - * @deprecated use "isIterable" or "isInstanceOf" instead - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allIsTraversable($value, $message = '') + private function restoreSuperGlobalArray(Snapshot $snapshot, string $superGlobalArray): void { - static::isIterable($value); - foreach ($value as $entry) { - static::isTraversable($entry, $message); + $superGlobalVariables = $snapshot->superGlobalVariables(); + if (isset($GLOBALS[$superGlobalArray], $superGlobalVariables[$superGlobalArray]) && is_array($GLOBALS[$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]); + } + } } } - /** - * @psalm-pure - * @psalm-assert iterable $value - * - * @deprecated use "isIterable" or "isInstanceOf" instead - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allNullOrIsTraversable($value, $message = '') +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnitPHAR\SebastianBergmann\GlobalState; + +use function array_keys; +use function array_merge; +use function array_reverse; +use function assert; +use function func_get_args; +use function get_declared_classes; +use function get_declared_interfaces; +use function get_declared_traits; +use function get_defined_constants; +use function get_defined_functions; +use function get_included_files; +use function in_array; +use function ini_get_all; +use function is_array; +use function is_object; +use function is_resource; +use function is_scalar; +use function serialize; +use function unserialize; +use ReflectionClass; +use PHPUnitPHAR\SebastianBergmann\ObjectReflector\ObjectReflector; +use PHPUnitPHAR\SebastianBergmann\RecursionContext\Context; +use Throwable; +/** + * A snapshot of global state. + */ +class Snapshot +{ + private ExcludeList $excludeList; + private array $globalVariables = []; + private array $superGlobalArrays = []; + private array $superGlobalVariables = []; + private array $staticProperties = []; + private array $iniSettings = []; + private array $includedFiles = []; + private array $constants = []; + private array $functions = []; + private array $interfaces = []; + private array $classes = []; + private array $traits = []; + public function __construct(?ExcludeList $excludeList = null, bool $includeGlobalVariables = \true, bool $includeStaticProperties = \true, bool $includeConstants = \true, bool $includeFunctions = \true, bool $includeClasses = \true, bool $includeInterfaces = \true, bool $includeTraits = \true, bool $includeIniSettings = \true, bool $includeIncludedFiles = \true) { - static::isIterable($value); - foreach ($value as $entry) { - null === $entry || static::isTraversable($entry, $message); + $this->excludeList = $excludeList ?: new ExcludeList(); + if ($includeConstants) { + $this->snapshotConstants(); } - } - /** - * @psalm-pure - * @psalm-assert array|ArrayAccess|null $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function nullOrIsArrayAccessible($value, $message = '') - { - null === $value || static::isArrayAccessible($value, $message); - } - /** - * @psalm-pure - * @psalm-assert iterable $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allIsArrayAccessible($value, $message = '') - { - static::isIterable($value); - foreach ($value as $entry) { - static::isArrayAccessible($entry, $message); + if ($includeFunctions) { + $this->snapshotFunctions(); } - } - /** - * @psalm-pure - * @psalm-assert iterable $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allNullOrIsArrayAccessible($value, $message = '') - { - static::isIterable($value); - foreach ($value as $entry) { - null === $entry || static::isArrayAccessible($entry, $message); + if ($includeClasses || $includeStaticProperties) { + $this->snapshotClasses(); } - } - /** - * @psalm-pure - * @psalm-assert countable|null $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function nullOrIsCountable($value, $message = '') - { - null === $value || static::isCountable($value, $message); - } - /** - * @psalm-pure - * @psalm-assert iterable $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allIsCountable($value, $message = '') - { - static::isIterable($value); - foreach ($value as $entry) { - static::isCountable($entry, $message); + if ($includeInterfaces) { + $this->snapshotInterfaces(); + } + if ($includeGlobalVariables) { + $this->setupSuperGlobalArrays(); + $this->snapshotGlobals(); + } + if ($includeStaticProperties) { + $this->snapshotStaticProperties(); + } + if ($includeIniSettings) { + $this->iniSettings = ini_get_all(null, \false); + } + if ($includeIncludedFiles) { + $this->includedFiles = get_included_files(); + } + if ($includeTraits) { + $this->traits = get_declared_traits(); } } - /** - * @psalm-pure - * @psalm-assert iterable $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allNullOrIsCountable($value, $message = '') + public function excludeList(): ExcludeList { - static::isIterable($value); - foreach ($value as $entry) { - null === $entry || static::isCountable($entry, $message); - } + return $this->excludeList; } - /** - * @psalm-pure - * @psalm-assert iterable|null $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function nullOrIsIterable($value, $message = '') + public function globalVariables(): array { - null === $value || static::isIterable($value, $message); + return $this->globalVariables; } - /** - * @psalm-pure - * @psalm-assert iterable $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allIsIterable($value, $message = '') + public function superGlobalVariables(): array { - static::isIterable($value); - foreach ($value as $entry) { - static::isIterable($entry, $message); - } + return $this->superGlobalVariables; } - /** - * @psalm-pure - * @psalm-assert iterable $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allNullOrIsIterable($value, $message = '') + public function superGlobalArrays(): array { - static::isIterable($value); - foreach ($value as $entry) { - null === $entry || static::isIterable($entry, $message); - } + return $this->superGlobalArrays; } - /** - * @psalm-pure - * @psalm-template ExpectedType of object - * @psalm-param class-string $class - * @psalm-assert ExpectedType|null $value - * - * @param mixed $value - * @param string|object $class - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function nullOrIsInstanceOf($value, $class, $message = '') + public function staticProperties(): array { - null === $value || static::isInstanceOf($value, $class, $message); + return $this->staticProperties; } - /** - * @psalm-pure - * @psalm-template ExpectedType of object - * @psalm-param class-string $class - * @psalm-assert iterable $value - * - * @param mixed $value - * @param string|object $class - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allIsInstanceOf($value, $class, $message = '') + public function iniSettings(): array { - static::isIterable($value); - foreach ($value as $entry) { - static::isInstanceOf($entry, $class, $message); - } + return $this->iniSettings; } - /** - * @psalm-pure - * @psalm-template ExpectedType of object - * @psalm-param class-string $class - * @psalm-assert iterable $value - * - * @param mixed $value - * @param string|object $class - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allNullOrIsInstanceOf($value, $class, $message = '') + public function includedFiles(): array { - static::isIterable($value); - foreach ($value as $entry) { - null === $entry || static::isInstanceOf($entry, $class, $message); - } + return $this->includedFiles; } - /** - * @psalm-pure - * @psalm-template ExpectedType of object - * @psalm-param class-string $class - * - * @param mixed $value - * @param string|object $class - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function nullOrNotInstanceOf($value, $class, $message = '') + public function constants(): array { - null === $value || static::notInstanceOf($value, $class, $message); + return $this->constants; } - /** - * @psalm-pure - * @psalm-template ExpectedType of object - * @psalm-param class-string $class - * - * @param mixed $value - * @param string|object $class - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allNotInstanceOf($value, $class, $message = '') + public function functions(): array { - static::isIterable($value); - foreach ($value as $entry) { - static::notInstanceOf($entry, $class, $message); - } + return $this->functions; } - /** - * @psalm-pure - * @psalm-template ExpectedType of object - * @psalm-param class-string $class - * @psalm-assert iterable $value - * - * @param mixed $value - * @param string|object $class - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allNullOrNotInstanceOf($value, $class, $message = '') + public function interfaces(): array { - static::isIterable($value); - foreach ($value as $entry) { - null === $entry || static::notInstanceOf($entry, $class, $message); - } + return $this->interfaces; } - /** - * @psalm-pure - * @psalm-param array $classes - * - * @param mixed $value - * @param array $classes - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function nullOrIsInstanceOfAny($value, $classes, $message = '') + public function classes(): array { - null === $value || static::isInstanceOfAny($value, $classes, $message); + return $this->classes; } - /** - * @psalm-pure - * @psalm-param array $classes - * - * @param mixed $value - * @param array $classes - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allIsInstanceOfAny($value, $classes, $message = '') + public function traits(): array { - static::isIterable($value); - foreach ($value as $entry) { - static::isInstanceOfAny($entry, $classes, $message); - } + return $this->traits; } - /** - * @psalm-pure - * @psalm-param array $classes - * - * @param mixed $value - * @param array $classes - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allNullOrIsInstanceOfAny($value, $classes, $message = '') + private function snapshotConstants(): void { - static::isIterable($value); - foreach ($value as $entry) { - null === $entry || static::isInstanceOfAny($entry, $classes, $message); + $constants = get_defined_constants(\true); + if (isset($constants['user'])) { + $this->constants = $constants['user']; } } - /** - * @psalm-pure - * @psalm-template ExpectedType of object - * @psalm-param class-string $class - * @psalm-assert ExpectedType|class-string|null $value - * - * @param object|string|null $value - * @param string $class - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function nullOrIsAOf($value, $class, $message = '') + private function snapshotFunctions(): void { - null === $value || static::isAOf($value, $class, $message); + $functions = get_defined_functions(); + $this->functions = $functions['user']; } - /** - * @psalm-pure - * @psalm-template ExpectedType of object - * @psalm-param class-string $class - * @psalm-assert iterable> $value - * - * @param iterable $value - * @param string $class - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allIsAOf($value, $class, $message = '') + private function snapshotClasses(): void { - static::isIterable($value); - foreach ($value as $entry) { - static::isAOf($entry, $class, $message); + 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); } - /** - * @psalm-pure - * @psalm-template ExpectedType of object - * @psalm-param class-string $class - * @psalm-assert iterable|null> $value - * - * @param iterable $value - * @param string $class - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allNullOrIsAOf($value, $class, $message = '') + private function snapshotInterfaces(): void { - static::isIterable($value); - foreach ($value as $entry) { - null === $entry || static::isAOf($entry, $class, $message); + 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); } - /** - * @psalm-pure - * @psalm-template UnexpectedType of object - * @psalm-param class-string $class - * - * @param object|string|null $value - * @param string $class - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function nullOrIsNotA($value, $class, $message = '') + private function snapshotGlobals(): void { - null === $value || static::isNotA($value, $class, $message); + $superGlobalArrays = $this->superGlobalArrays(); + foreach ($superGlobalArrays as $superGlobalArray) { + $this->snapshotSuperGlobalArray($superGlobalArray); + } + foreach (array_keys($GLOBALS) as $key) { + if ($key !== 'GLOBALS' && !in_array($key, $superGlobalArrays, \true) && $this->canBeSerialized($GLOBALS[$key]) && !$this->excludeList->isGlobalVariableExcluded($key)) { + /* @noinspection UnserializeExploitsInspection */ + $this->globalVariables[$key] = unserialize(serialize($GLOBALS[$key])); + } + } } - /** - * @psalm-pure - * @psalm-template UnexpectedType of object - * @psalm-param class-string $class - * - * @param iterable $value - * @param string $class - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allIsNotA($value, $class, $message = '') + private function snapshotSuperGlobalArray(string $superGlobalArray): void { - static::isIterable($value); - foreach ($value as $entry) { - static::isNotA($entry, $class, $message); + $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)); + } } } - /** - * @psalm-pure - * @psalm-template UnexpectedType of object - * @psalm-param class-string $class - * @psalm-assert iterable $value - * @psalm-assert iterable|null> $value - * - * @param iterable $value - * @param string $class - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allNullOrIsNotA($value, $class, $message = '') + private function snapshotStaticProperties(): void { - static::isIterable($value); - foreach ($value as $entry) { - null === $entry || static::isNotA($entry, $class, $message); + foreach ($this->classes as $className) { + $class = new ReflectionClass($className); + $snapshot = []; + foreach ($class->getProperties() as $property) { + if ($property->isStatic()) { + $name = $property->getName(); + if ($this->excludeList->isStaticPropertyExcluded($className, $name)) { + continue; + } + if (!$property->isInitialized()) { + continue; + } + $value = $property->getValue(); + if ($this->canBeSerialized($value)) { + /* @noinspection UnserializeExploitsInspection */ + $snapshot[$name] = unserialize(serialize($value)); + } + } + } + if (!empty($snapshot)) { + $this->staticProperties[$className] = $snapshot; + } } } - /** - * @psalm-pure - * @psalm-param array $classes - * - * @param object|string|null $value - * @param string[] $classes - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function nullOrIsAnyOf($value, $classes, $message = '') + private function setupSuperGlobalArrays(): void { - null === $value || static::isAnyOf($value, $classes, $message); + $this->superGlobalArrays = ['_ENV', '_POST', '_GET', '_COOKIE', '_SERVER', '_FILES', '_REQUEST']; } - /** - * @psalm-pure - * @psalm-param array $classes - * - * @param iterable $value - * @param string[] $classes - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allIsAnyOf($value, $classes, $message = '') + private function canBeSerialized(mixed $variable): bool { - static::isIterable($value); - foreach ($value as $entry) { - static::isAnyOf($entry, $classes, $message); + 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; } - /** - * @psalm-pure - * @psalm-param array $classes - * - * @param iterable $value - * @param string[] $classes - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allNullOrIsAnyOf($value, $classes, $message = '') + private function enumerateObjectsAndResources(mixed $variable): array { - static::isIterable($value); - foreach ($value as $entry) { - null === $entry || static::isAnyOf($entry, $classes, $message); + if (isset(func_get_args()[1])) { + $processed = func_get_args()[1]; + } else { + $processed = new Context(); + } + assert($processed instanceof Context); + $result = []; + if ($processed->contains($variable)) { + return $result; + } + $array = $variable; + /* @noinspection UnusedFunctionResultInspection */ + $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())->getProperties($variable) as $value) { + if (!is_array($value) && !is_object($value) && !is_resource($value)) { + continue; + } + if (!is_resource($value)) { + /** @noinspection SlowArrayOperationsInLoopInspection */ + $result = array_merge($result, $this->enumerateObjectsAndResources($value, $processed)); + } else { + $result[] = $value; + } + } } + return $result; } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnitPHAR\SebastianBergmann\GlobalState; + +use Throwable; +interface Exception extends Throwable +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnitPHAR\SebastianBergmann\GlobalState; + +final class RuntimeException extends \RuntimeException implements Exception +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnitPHAR\SebastianBergmann\LinesOfCode; + +use function assert; +use function file_get_contents; +use function substr_count; +use PHPUnitPHAR\PhpParser\Error; +use PHPUnitPHAR\PhpParser\Node; +use PHPUnitPHAR\PhpParser\NodeTraverser; +use PHPUnitPHAR\PhpParser\ParserFactory; +final class Counter +{ /** - * @psalm-pure - * @psalm-assert empty $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void + * @throws RuntimeException */ - public static function nullOrIsEmpty($value, $message = '') + public function countInSourceFile(string $sourceFile): LinesOfCode { - null === $value || static::isEmpty($value, $message); + return $this->countInSourceString(file_get_contents($sourceFile)); } /** - * @psalm-pure - * @psalm-assert iterable $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void + * @throws RuntimeException */ - public static function allIsEmpty($value, $message = '') + public function countInSourceString(string $source): LinesOfCode { - static::isIterable($value); - foreach ($value as $entry) { - static::isEmpty($entry, $message); + $linesOfCode = substr_count($source, "\n"); + if ($linesOfCode === 0 && !empty($source)) { + $linesOfCode = 1; + } + assert($linesOfCode >= 0); + try { + $nodes = (new ParserFactory())->createForHostVersion()->parse($source); + assert($nodes !== null); + return $this->countInAbstractSyntaxTree($linesOfCode, $nodes); + // @codeCoverageIgnoreStart + } catch (Error $error) { + throw new RuntimeException($error->getMessage(), $error->getCode(), $error); } + // @codeCoverageIgnoreEnd } /** - * @psalm-pure - * @psalm-assert iterable $value + * @psalm-param non-negative-int $linesOfCode * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException + * @param Node[] $nodes * - * @return void + * @throws RuntimeException */ - public static function allNullOrIsEmpty($value, $message = '') + public function countInAbstractSyntaxTree(int $linesOfCode, array $nodes): LinesOfCode { - static::isIterable($value); - foreach ($value as $entry) { - null === $entry || static::isEmpty($entry, $message); + $traverser = new NodeTraverser(); + $visitor = new LineCountingVisitor($linesOfCode); + $traverser->addVisitor($visitor); + try { + /* @noinspection UnusedFunctionResultInspection */ + $traverser->traverse($nodes); + // @codeCoverageIgnoreStart + } catch (Error $error) { + throw new RuntimeException($error->getMessage(), $error->getCode(), $error); } + // @codeCoverageIgnoreEnd + return $visitor->result(); } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnitPHAR\SebastianBergmann\LinesOfCode; + +use Throwable; +interface Exception extends Throwable +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnitPHAR\SebastianBergmann\LinesOfCode; + +use LogicException; +final class IllogicalValuesException extends LogicException implements Exception +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnitPHAR\SebastianBergmann\LinesOfCode; + +use InvalidArgumentException; +final class NegativeValueException extends InvalidArgumentException implements Exception +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnitPHAR\SebastianBergmann\LinesOfCode; + +final class RuntimeException extends \RuntimeException implements Exception +{ +} +BSD 3-Clause License + +Copyright (c) 2020-2023, Sebastian Bergmann +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnitPHAR\SebastianBergmann\LinesOfCode; + +use function array_merge; +use function array_unique; +use function assert; +use function count; +use PHPUnitPHAR\PhpParser\Comment; +use PHPUnitPHAR\PhpParser\Node; +use PHPUnitPHAR\PhpParser\Node\Expr; +use PHPUnitPHAR\PhpParser\NodeVisitorAbstract; +final class LineCountingVisitor extends NodeVisitorAbstract +{ /** - * @psalm-pure - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void + * @psalm-var non-negative-int */ - public static function nullOrNotEmpty($value, $message = '') - { - null === $value || static::notEmpty($value, $message); - } + private readonly int $linesOfCode; /** - * @psalm-pure - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void + * @var Comment[] */ - public static function allNotEmpty($value, $message = '') - { - static::isIterable($value); - foreach ($value as $entry) { - static::notEmpty($entry, $message); - } - } + private array $comments = []; /** - * @psalm-pure - * @psalm-assert iterable $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void + * @var int[] */ - public static function allNullOrNotEmpty($value, $message = '') - { - static::isIterable($value); - foreach ($value as $entry) { - null === $entry || static::notEmpty($entry, $message); - } - } + private array $linesWithStatements = []; /** - * @psalm-pure - * @psalm-assert iterable $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void + * @psalm-param non-negative-int $linesOfCode */ - public static function allNull($value, $message = '') + public function __construct(int $linesOfCode) { - static::isIterable($value); - foreach ($value as $entry) { - static::null($entry, $message); - } + $this->linesOfCode = $linesOfCode; } - /** - * @psalm-pure - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allNotNull($value, $message = '') + public function enterNode(Node $node): void { - static::isIterable($value); - foreach ($value as $entry) { - static::notNull($entry, $message); + $this->comments = array_merge($this->comments, $node->getComments()); + if (!$node instanceof Expr) { + return; } + $this->linesWithStatements[] = $node->getStartLine(); } - /** - * @psalm-pure - * @psalm-assert true|null $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function nullOrTrue($value, $message = '') - { - null === $value || static::true($value, $message); - } - /** - * @psalm-pure - * @psalm-assert iterable $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allTrue($value, $message = '') + public function result(): LinesOfCode { - static::isIterable($value); - foreach ($value as $entry) { - static::true($entry, $message); + $commentLinesOfCode = 0; + foreach ($this->comments() as $comment) { + $commentLinesOfCode += $comment->getEndLine() - $comment->getStartLine() + 1; } + $nonCommentLinesOfCode = $this->linesOfCode - $commentLinesOfCode; + $logicalLinesOfCode = count(array_unique($this->linesWithStatements)); + assert($commentLinesOfCode >= 0); + assert($nonCommentLinesOfCode >= 0); + assert($logicalLinesOfCode >= 0); + return new LinesOfCode($this->linesOfCode, $commentLinesOfCode, $nonCommentLinesOfCode, $logicalLinesOfCode); } /** - * @psalm-pure - * @psalm-assert iterable $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void + * @return Comment[] */ - public static function allNullOrTrue($value, $message = '') + private function comments(): array { - static::isIterable($value); - foreach ($value as $entry) { - null === $entry || static::true($entry, $message); + $comments = []; + foreach ($this->comments as $comment) { + $comments[$comment->getStartLine() . '_' . $comment->getStartTokenPos() . '_' . $comment->getEndLine() . '_' . $comment->getEndTokenPos()] = $comment; } + return $comments; } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnitPHAR\SebastianBergmann\LinesOfCode; + +/** + * @psalm-immutable + */ +final class LinesOfCode +{ /** - * @psalm-pure - * @psalm-assert false|null $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void + * @psalm-var non-negative-int */ - public static function nullOrFalse($value, $message = '') - { - null === $value || static::false($value, $message); - } + private readonly int $linesOfCode; /** - * @psalm-pure - * @psalm-assert iterable $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void + * @psalm-var non-negative-int */ - public static function allFalse($value, $message = '') - { - static::isIterable($value); - foreach ($value as $entry) { - static::false($entry, $message); - } - } + private readonly int $commentLinesOfCode; /** - * @psalm-pure - * @psalm-assert iterable $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void + * @psalm-var non-negative-int */ - public static function allNullOrFalse($value, $message = '') - { - static::isIterable($value); - foreach ($value as $entry) { - null === $entry || static::false($entry, $message); - } - } + private readonly int $nonCommentLinesOfCode; /** - * @psalm-pure - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void + * @psalm-var non-negative-int */ - public static function nullOrNotFalse($value, $message = '') - { - null === $value || static::notFalse($value, $message); - } + private readonly int $logicalLinesOfCode; /** - * @psalm-pure - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException + * @psalm-param non-negative-int $linesOfCode + * @psalm-param non-negative-int $commentLinesOfCode + * @psalm-param non-negative-int $nonCommentLinesOfCode + * @psalm-param non-negative-int $logicalLinesOfCode * - * @return void + * @throws IllogicalValuesException + * @throws NegativeValueException */ - public static function allNotFalse($value, $message = '') + public function __construct(int $linesOfCode, int $commentLinesOfCode, int $nonCommentLinesOfCode, int $logicalLinesOfCode) { - static::isIterable($value); - foreach ($value as $entry) { - static::notFalse($entry, $message); + /** @psalm-suppress DocblockTypeContradiction */ + if ($linesOfCode < 0) { + throw new NegativeValueException('$linesOfCode must not be negative'); } - } - /** - * @psalm-pure - * @psalm-assert iterable $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allNullOrNotFalse($value, $message = '') - { - static::isIterable($value); - foreach ($value as $entry) { - null === $entry || static::notFalse($entry, $message); + /** @psalm-suppress DocblockTypeContradiction */ + if ($commentLinesOfCode < 0) { + throw new NegativeValueException('$commentLinesOfCode must not be negative'); } - } - /** - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function nullOrIp($value, $message = '') - { - null === $value || static::ip($value, $message); - } - /** - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allIp($value, $message = '') - { - static::isIterable($value); - foreach ($value as $entry) { - static::ip($entry, $message); + /** @psalm-suppress DocblockTypeContradiction */ + if ($nonCommentLinesOfCode < 0) { + throw new NegativeValueException('$nonCommentLinesOfCode must not be negative'); } - } - /** - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allNullOrIp($value, $message = '') - { - static::isIterable($value); - foreach ($value as $entry) { - null === $entry || static::ip($entry, $message); + /** @psalm-suppress DocblockTypeContradiction */ + if ($logicalLinesOfCode < 0) { + throw new NegativeValueException('$logicalLinesOfCode must not be negative'); } - } - /** - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function nullOrIpv4($value, $message = '') - { - null === $value || static::ipv4($value, $message); - } - /** - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allIpv4($value, $message = '') - { - static::isIterable($value); - foreach ($value as $entry) { - static::ipv4($entry, $message); + if ($linesOfCode - $commentLinesOfCode !== $nonCommentLinesOfCode) { + throw new IllogicalValuesException('$linesOfCode !== $commentLinesOfCode + $nonCommentLinesOfCode'); } + $this->linesOfCode = $linesOfCode; + $this->commentLinesOfCode = $commentLinesOfCode; + $this->nonCommentLinesOfCode = $nonCommentLinesOfCode; + $this->logicalLinesOfCode = $logicalLinesOfCode; } /** - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void + * @psalm-return non-negative-int */ - public static function allNullOrIpv4($value, $message = '') + public function linesOfCode(): int { - static::isIterable($value); - foreach ($value as $entry) { - null === $entry || static::ipv4($entry, $message); - } + return $this->linesOfCode; } /** - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void + * @psalm-return non-negative-int */ - public static function nullOrIpv6($value, $message = '') + public function commentLinesOfCode(): int { - null === $value || static::ipv6($value, $message); + return $this->commentLinesOfCode; } /** - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void + * @psalm-return non-negative-int */ - public static function allIpv6($value, $message = '') + public function nonCommentLinesOfCode(): int { - static::isIterable($value); - foreach ($value as $entry) { - static::ipv6($entry, $message); - } + return $this->nonCommentLinesOfCode; } /** - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void + * @psalm-return non-negative-int */ - public static function allNullOrIpv6($value, $message = '') + public function logicalLinesOfCode(): int { - static::isIterable($value); - foreach ($value as $entry) { - null === $entry || static::ipv6($entry, $message); - } + return $this->logicalLinesOfCode; } - /** - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function nullOrEmail($value, $message = '') + public function plus(self $other): self { - null === $value || static::email($value, $message); + return new self($this->linesOfCode() + $other->linesOfCode(), $this->commentLinesOfCode() + $other->commentLinesOfCode(), $this->nonCommentLinesOfCode() + $other->nonCommentLinesOfCode(), $this->logicalLinesOfCode() + $other->logicalLinesOfCode()); } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnitPHAR\SebastianBergmann\ObjectEnumerator; + +use function array_merge; +use function is_array; +use function is_object; +use PHPUnitPHAR\SebastianBergmann\ObjectReflector\ObjectReflector; +use PHPUnitPHAR\SebastianBergmann\RecursionContext\Context; +final class Enumerator +{ /** - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void + * @psalm-return list */ - public static function allEmail($value, $message = '') + public function enumerate(array|object $variable, Context $processed = new Context()): array { - static::isIterable($value); - foreach ($value as $entry) { - static::email($entry, $message); + $objects = []; + if ($processed->contains($variable)) { + return $objects; } - } - /** - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allNullOrEmail($value, $message = '') - { - static::isIterable($value); - foreach ($value as $entry) { - null === $entry || static::email($entry, $message); + $array = $variable; + /* @noinspection UnusedFunctionResultInspection */ + $processed->add($variable); + if (is_array($variable)) { + foreach ($array as $element) { + if (!is_array($element) && !is_object($element)) { + continue; + } + /** @noinspection SlowArrayOperationsInLoopInspection */ + $objects = array_merge($objects, $this->enumerate($element, $processed)); + } + return $objects; } - } - /** - * @param array|null $values - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function nullOrUniqueValues($values, $message = '') - { - null === $values || static::uniqueValues($values, $message); - } - /** - * @param iterable $values - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allUniqueValues($values, $message = '') - { - static::isIterable($values); - foreach ($values as $entry) { - static::uniqueValues($entry, $message); + $objects[] = $variable; + foreach ((new ObjectReflector())->getProperties($variable) as $value) { + if (!is_array($value) && !is_object($value)) { + continue; + } + /** @noinspection SlowArrayOperationsInLoopInspection */ + $objects = array_merge($objects, $this->enumerate($value, $processed)); } + return $objects; } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnitPHAR\SebastianBergmann\ObjectReflector; + +use function count; +use function explode; +final class ObjectReflector +{ /** - * @param iterable $values - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void + * @psalm-return array */ - public static function allNullOrUniqueValues($values, $message = '') + public function getProperties(object $object): array { - static::isIterable($values); - foreach ($values as $entry) { - null === $entry || static::uniqueValues($entry, $message); + $properties = []; + $className = $object::class; + foreach ((array) $object as $name => $value) { + $name = explode("\x00", (string) $name); + if (count($name) === 1) { + $name = $name[0]; + } elseif ($name[1] !== $className) { + $name = $name[1] . '::' . $name[2]; + } else { + $name = $name[2]; + } + $properties[$name] = $value; } + return $properties; } - /** - * @param mixed $value - * @param mixed $expect - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function nullOrEq($value, $expect, $message = '') +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnitPHAR\SebastianBergmann\RecursionContext; + +use const PHP_INT_MAX; +use const PHP_INT_MIN; +use function array_key_exists; +use function array_pop; +use function array_slice; +use function count; +use function is_array; +use function random_int; +use function spl_object_hash; +use SplObjectStorage; +final class Context +{ + private array $arrays = []; + private SplObjectStorage $objects; + public function __construct() { - null === $value || static::eq($value, $expect, $message); + $this->objects = new SplObjectStorage(); } /** - * @param mixed $value - * @param mixed $expect - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void + * @codeCoverageIgnore */ - public static function allEq($value, $expect, $message = '') + public function __destruct() { - static::isIterable($value); - foreach ($value as $entry) { - static::eq($entry, $expect, $message); + foreach ($this->arrays as &$array) { + if (is_array($array)) { + array_pop($array); + array_pop($array); + } } } /** - * @param mixed $value - * @param mixed $expect - * @param string $message + * @psalm-template T * - * @throws InvalidArgumentException + * @psalm-param T $value * - * @return void + * @param-out T $value */ - public static function allNullOrEq($value, $expect, $message = '') + public function add(object|array &$value): int|string|false { - static::isIterable($value); - foreach ($value as $entry) { - null === $entry || static::eq($entry, $expect, $message); + if (is_array($value)) { + return $this->addArray($value); } + return $this->addObject($value); } /** - * @param mixed $value - * @param mixed $expect - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function nullOrNotEq($value, $expect, $message = '') - { - null === $value || static::notEq($value, $expect, $message); - } - /** - * @param mixed $value - * @param mixed $expect - * @param string $message + * @psalm-template T * - * @throws InvalidArgumentException + * @psalm-param T $value * - * @return void + * @param-out T $value */ - public static function allNotEq($value, $expect, $message = '') + public function contains(object|array &$value): int|string|false { - static::isIterable($value); - foreach ($value as $entry) { - static::notEq($entry, $expect, $message); + if (is_array($value)) { + return $this->containsArray($value); } + return $this->containsObject($value); } - /** - * @param mixed $value - * @param mixed $expect - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allNullOrNotEq($value, $expect, $message = '') + private function addArray(array &$array): int { - static::isIterable($value); - foreach ($value as $entry) { - null === $entry || static::notEq($entry, $expect, $message); + $key = $this->containsArray($array); + if ($key !== \false) { + return $key; } - } - /** - * @psalm-pure - * - * @param mixed $value - * @param mixed $expect - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function nullOrSame($value, $expect, $message = '') - { - null === $value || static::same($value, $expect, $message); - } - /** - * @psalm-pure - * - * @param mixed $value - * @param mixed $expect - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allSame($value, $expect, $message = '') - { - static::isIterable($value); - foreach ($value as $entry) { - static::same($entry, $expect, $message); + $key = count($this->arrays); + $this->arrays[] =& $array; + if (!array_key_exists(PHP_INT_MAX, $array) && !array_key_exists(PHP_INT_MAX - 1, $array)) { + $array[] = $key; + $array[] = $this->objects; + } else { + /* Cover the improbable case, too. + * + * Note that array_slice() (used in containsArray()) will return the + * last two values added, *not necessarily* the highest integer keys + * in the array. Therefore, the order of these writes to $array is + * important, but the actual keys used is not. */ + do { + /** @noinspection PhpUnhandledExceptionInspection */ + $key = random_int(PHP_INT_MIN, PHP_INT_MAX); + } while (array_key_exists($key, $array)); + $array[$key] = $key; + do { + /** @noinspection PhpUnhandledExceptionInspection */ + $key = random_int(PHP_INT_MIN, PHP_INT_MAX); + } while (array_key_exists($key, $array)); + $array[$key] = $this->objects; } + return $key; } - /** - * @psalm-pure - * - * @param mixed $value - * @param mixed $expect - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allNullOrSame($value, $expect, $message = '') + private function addObject(object $object): string { - static::isIterable($value); - foreach ($value as $entry) { - null === $entry || static::same($entry, $expect, $message); + if (!$this->objects->contains($object)) { + $this->objects->attach($object); } + return spl_object_hash($object); } - /** - * @psalm-pure - * - * @param mixed $value - * @param mixed $expect - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function nullOrNotSame($value, $expect, $message = '') - { - null === $value || static::notSame($value, $expect, $message); - } - /** - * @psalm-pure - * - * @param mixed $value - * @param mixed $expect - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allNotSame($value, $expect, $message = '') + private function containsArray(array $array): int|false { - static::isIterable($value); - foreach ($value as $entry) { - static::notSame($entry, $expect, $message); - } + $end = array_slice($array, -2); + return (isset($end[1]) && $end[1] === $this->objects) ? $end[0] : \false; } - /** - * @psalm-pure - * - * @param mixed $value - * @param mixed $expect - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allNullOrNotSame($value, $expect, $message = '') + private function containsObject(object $value): string|false { - static::isIterable($value); - foreach ($value as $entry) { - null === $entry || static::notSame($entry, $expect, $message); + if ($this->objects->contains($value)) { + return spl_object_hash($value); } + return \false; } +} +BSD 3-Clause License + +Copyright (c) 2002-2023, Sebastian Bergmann +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +BSD 3-Clause License + +Copyright (c) 2019-2023, Sebastian Bergmann +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnitPHAR\SebastianBergmann\Type; + +final class Parameter +{ /** - * @psalm-pure - * - * @param mixed $value - * @param mixed $limit - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void + * @psalm-var non-empty-string */ - public static function nullOrGreaterThan($value, $limit, $message = '') - { - null === $value || static::greaterThan($value, $limit, $message); - } + private string $name; + private Type $type; /** - * @psalm-pure - * - * @param mixed $value - * @param mixed $limit - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void + * @psalm-param non-empty-string $name */ - public static function allGreaterThan($value, $limit, $message = '') + public function __construct(string $name, Type $type) { - static::isIterable($value); - foreach ($value as $entry) { - static::greaterThan($entry, $limit, $message); - } + $this->name = $name; + $this->type = $type; } - /** - * @psalm-pure - * - * @param mixed $value - * @param mixed $limit - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allNullOrGreaterThan($value, $limit, $message = '') + public function name(): string { - static::isIterable($value); - foreach ($value as $entry) { - null === $entry || static::greaterThan($entry, $limit, $message); - } + return $this->name; } - /** - * @psalm-pure - * - * @param mixed $value - * @param mixed $limit - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function nullOrGreaterThanEq($value, $limit, $message = '') + public function type(): Type { - null === $value || static::greaterThanEq($value, $limit, $message); + return $this->type; } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnitPHAR\SebastianBergmann\Type; + +use function assert; +use ReflectionFunction; +use ReflectionIntersectionType; +use ReflectionMethod; +use ReflectionNamedType; +use ReflectionType; +use ReflectionUnionType; +final class ReflectionMapper +{ /** - * @psalm-pure - * - * @param mixed $value - * @param mixed $limit - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void + * @psalm-return list */ - public static function allGreaterThanEq($value, $limit, $message = '') + public function fromParameterTypes(ReflectionFunction|ReflectionMethod $functionOrMethod): array { - static::isIterable($value); - foreach ($value as $entry) { - static::greaterThanEq($entry, $limit, $message); + $parameters = []; + foreach ($functionOrMethod->getParameters() as $parameter) { + $name = $parameter->getName(); + assert($name !== ''); + if (!$parameter->hasType()) { + $parameters[] = new Parameter($name, new UnknownType()); + continue; + } + $type = $parameter->getType(); + if ($type instanceof ReflectionNamedType) { + $parameters[] = new Parameter($name, $this->mapNamedType($type, $functionOrMethod)); + continue; + } + if ($type instanceof ReflectionUnionType) { + $parameters[] = new Parameter($name, $this->mapUnionType($type, $functionOrMethod)); + continue; + } + if ($type instanceof ReflectionIntersectionType) { + $parameters[] = new Parameter($name, $this->mapIntersectionType($type, $functionOrMethod)); + } } + return $parameters; } - /** - * @psalm-pure - * - * @param mixed $value - * @param mixed $limit - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allNullOrGreaterThanEq($value, $limit, $message = '') + public function fromReturnType(ReflectionFunction|ReflectionMethod $functionOrMethod): Type { - static::isIterable($value); - foreach ($value as $entry) { - null === $entry || static::greaterThanEq($entry, $limit, $message); + if (!$this->hasReturnType($functionOrMethod)) { + return new UnknownType(); } - } - /** - * @psalm-pure - * - * @param mixed $value - * @param mixed $limit - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function nullOrLessThan($value, $limit, $message = '') - { - null === $value || static::lessThan($value, $limit, $message); - } - /** - * @psalm-pure - * - * @param mixed $value - * @param mixed $limit - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allLessThan($value, $limit, $message = '') - { - static::isIterable($value); - foreach ($value as $entry) { - static::lessThan($entry, $limit, $message); + $returnType = $this->returnType($functionOrMethod); + assert($returnType instanceof ReflectionNamedType || $returnType instanceof ReflectionUnionType || $returnType instanceof ReflectionIntersectionType); + if ($returnType instanceof ReflectionNamedType) { + return $this->mapNamedType($returnType, $functionOrMethod); } - } - /** - * @psalm-pure - * - * @param mixed $value - * @param mixed $limit - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allNullOrLessThan($value, $limit, $message = '') - { - static::isIterable($value); - foreach ($value as $entry) { - null === $entry || static::lessThan($entry, $limit, $message); + if ($returnType instanceof ReflectionUnionType) { + return $this->mapUnionType($returnType, $functionOrMethod); + } + if ($returnType instanceof ReflectionIntersectionType) { + return $this->mapIntersectionType($returnType, $functionOrMethod); } } - /** - * @psalm-pure - * - * @param mixed $value - * @param mixed $limit - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function nullOrLessThanEq($value, $limit, $message = '') + private function mapNamedType(ReflectionNamedType $type, ReflectionFunction|ReflectionMethod $functionOrMethod): Type { - null === $value || static::lessThanEq($value, $limit, $message); + if ($functionOrMethod instanceof ReflectionMethod && $type->getName() === 'self') { + return ObjectType::fromName($functionOrMethod->getDeclaringClass()->getName(), $type->allowsNull()); + } + if ($functionOrMethod instanceof ReflectionMethod && $type->getName() === 'static') { + return new StaticType(TypeName::fromReflection($functionOrMethod->getDeclaringClass()), $type->allowsNull()); + } + if ($type->getName() === 'mixed') { + return new MixedType(); + } + if ($functionOrMethod instanceof ReflectionMethod && $type->getName() === 'parent') { + return ObjectType::fromName($functionOrMethod->getDeclaringClass()->getParentClass()->getName(), $type->allowsNull()); + } + return Type::fromName($type->getName(), $type->allowsNull()); } - /** - * @psalm-pure - * - * @param mixed $value - * @param mixed $limit - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allLessThanEq($value, $limit, $message = '') + private function mapUnionType(ReflectionUnionType $type, ReflectionFunction|ReflectionMethod $functionOrMethod): Type { - static::isIterable($value); - foreach ($value as $entry) { - static::lessThanEq($entry, $limit, $message); + $types = []; + foreach ($type->getTypes() as $_type) { + assert($_type instanceof ReflectionNamedType || $_type instanceof ReflectionIntersectionType); + if ($_type instanceof ReflectionNamedType) { + $types[] = $this->mapNamedType($_type, $functionOrMethod); + continue; + } + $types[] = $this->mapIntersectionType($_type, $functionOrMethod); } + return new UnionType(...$types); } - /** - * @psalm-pure - * - * @param mixed $value - * @param mixed $limit - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allNullOrLessThanEq($value, $limit, $message = '') + private function mapIntersectionType(ReflectionIntersectionType $type, ReflectionFunction|ReflectionMethod $functionOrMethod): Type { - static::isIterable($value); - foreach ($value as $entry) { - null === $entry || static::lessThanEq($entry, $limit, $message); + $types = []; + foreach ($type->getTypes() as $_type) { + assert($_type instanceof ReflectionNamedType); + $types[] = $this->mapNamedType($_type, $functionOrMethod); } + return new IntersectionType(...$types); } - /** - * @psalm-pure - * - * @param mixed $value - * @param mixed $min - * @param mixed $max - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function nullOrRange($value, $min, $max, $message = '') + private function hasReturnType(ReflectionFunction|ReflectionMethod $functionOrMethod): bool { - null === $value || static::range($value, $min, $max, $message); + if ($functionOrMethod->hasReturnType()) { + return \true; + } + return $functionOrMethod->hasTentativeReturnType(); } - /** - * @psalm-pure - * - * @param mixed $value - * @param mixed $min - * @param mixed $max - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allRange($value, $min, $max, $message = '') + private function returnType(ReflectionFunction|ReflectionMethod $functionOrMethod): ?ReflectionType { - static::isIterable($value); - foreach ($value as $entry) { - static::range($entry, $min, $max, $message); + if ($functionOrMethod->hasReturnType()) { + return $functionOrMethod->getReturnType(); } + return $functionOrMethod->getTentativeReturnType(); } - /** - * @psalm-pure - * - * @param mixed $value - * @param mixed $min - * @param mixed $max - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allNullOrRange($value, $min, $max, $message = '') +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnitPHAR\SebastianBergmann\Type; + +use function array_pop; +use function explode; +use function implode; +use function substr; +use ReflectionClass; +final class TypeName +{ + private ?string $namespaceName; + private string $simpleName; + public static function fromQualifiedName(string $fullClassName): self { - static::isIterable($value); - foreach ($value as $entry) { - null === $entry || static::range($entry, $min, $max, $message); + if ($fullClassName[0] === '\\') { + $fullClassName = substr($fullClassName, 1); } + $classNameParts = explode('\\', $fullClassName); + $simpleName = array_pop($classNameParts); + $namespaceName = implode('\\', $classNameParts); + return new self($namespaceName, $simpleName); } - /** - * @psalm-pure - * - * @param mixed $value - * @param array $values - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function nullOrOneOf($value, $values, $message = '') + public static function fromReflection(ReflectionClass $type): self { - null === $value || static::oneOf($value, $values, $message); + return new self($type->getNamespaceName(), $type->getShortName()); } - /** - * @psalm-pure - * - * @param mixed $value - * @param array $values - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allOneOf($value, $values, $message = '') + public function __construct(?string $namespaceName, string $simpleName) { - static::isIterable($value); - foreach ($value as $entry) { - static::oneOf($entry, $values, $message); + if ($namespaceName === '') { + $namespaceName = null; } + $this->namespaceName = $namespaceName; + $this->simpleName = $simpleName; } - /** - * @psalm-pure - * - * @param mixed $value - * @param array $values - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allNullOrOneOf($value, $values, $message = '') + public function namespaceName(): ?string { - static::isIterable($value); - foreach ($value as $entry) { - null === $entry || static::oneOf($entry, $values, $message); - } + return $this->namespaceName; } - /** - * @psalm-pure - * - * @param mixed $value - * @param array $values - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function nullOrInArray($value, $values, $message = '') + public function simpleName(): string { - null === $value || static::inArray($value, $values, $message); + return $this->simpleName; } - /** - * @psalm-pure - * - * @param mixed $value - * @param array $values - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allInArray($value, $values, $message = '') + public function qualifiedName(): string { - static::isIterable($value); - foreach ($value as $entry) { - static::inArray($entry, $values, $message); - } + return ($this->namespaceName === null) ? $this->simpleName : ($this->namespaceName . '\\' . $this->simpleName); } - /** - * @psalm-pure - * - * @param mixed $value - * @param array $values - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allNullOrInArray($value, $values, $message = '') + public function isNamespaced(): bool { - static::isIterable($value); - foreach ($value as $entry) { - null === $entry || static::inArray($entry, $values, $message); - } + return $this->namespaceName !== null; } - /** - * @psalm-pure - * - * @param string|null $value - * @param string $subString - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function nullOrContains($value, $subString, $message = '') +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnitPHAR\SebastianBergmann\Type; + +use Throwable; +interface Exception extends Throwable +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnitPHAR\SebastianBergmann\Type; + +final class RuntimeException extends \RuntimeException implements Exception +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnitPHAR\SebastianBergmann\Type; + +use function assert; +use function class_exists; +use function count; +use function explode; +use function function_exists; +use function is_array; +use function is_object; +use function is_string; +use function str_contains; +use Closure; +use ReflectionClass; +use ReflectionObject; +final class CallableType extends Type +{ + private bool $allowsNull; + public function __construct(bool $nullable) { - null === $value || static::contains($value, $subString, $message); + $this->allowsNull = $nullable; } - /** - * @psalm-pure - * - * @param iterable $value - * @param string $subString - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allContains($value, $subString, $message = '') + public function isAssignable(Type $other): bool { - static::isIterable($value); - foreach ($value as $entry) { - static::contains($entry, $subString, $message); + if ($this->allowsNull && $other instanceof NullType) { + return \true; } - } - /** - * @psalm-pure - * - * @param iterable $value - * @param string $subString - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allNullOrContains($value, $subString, $message = '') - { - static::isIterable($value); - foreach ($value as $entry) { - null === $entry || static::contains($entry, $subString, $message); + 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; } - /** - * @psalm-pure - * - * @param string|null $value - * @param string $subString - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function nullOrNotContains($value, $subString, $message = '') + public function name(): string { - null === $value || static::notContains($value, $subString, $message); + return 'callable'; } - /** - * @psalm-pure - * - * @param iterable $value - * @param string $subString - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allNotContains($value, $subString, $message = '') + public function allowsNull(): bool { - static::isIterable($value); - foreach ($value as $entry) { - static::notContains($entry, $subString, $message); - } + return $this->allowsNull; } /** - * @psalm-pure - * - * @param iterable $value - * @param string $subString - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void + * @psalm-assert-if-true CallableType $this */ - public static function allNullOrNotContains($value, $subString, $message = '') + public function isCallable(): bool { - static::isIterable($value); - foreach ($value as $entry) { - null === $entry || static::notContains($entry, $subString, $message); - } + return \true; } - /** - * @psalm-pure - * - * @param string|null $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function nullOrNotWhitespaceOnly($value, $message = '') + private function isClosure(ObjectType $type): bool { - null === $value || static::notWhitespaceOnly($value, $message); + return $type->className()->qualifiedName() === Closure::class; } - /** - * @psalm-pure - * - * @param iterable $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allNotWhitespaceOnly($value, $message = '') + private function hasInvokeMethod(ObjectType $type): bool { - static::isIterable($value); - foreach ($value as $entry) { - static::notWhitespaceOnly($entry, $message); - } + $className = $type->className()->qualifiedName(); + assert(class_exists($className)); + return (new ReflectionClass($className))->hasMethod('__invoke'); } - /** - * @psalm-pure - * - * @param iterable $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allNullOrNotWhitespaceOnly($value, $message = '') + private function isFunction(SimpleType $type): bool { - static::isIterable($value); - foreach ($value as $entry) { - null === $entry || static::notWhitespaceOnly($entry, $message); + if (!is_string($type->value())) { + return \false; } + return function_exists($type->value()); } - /** - * @psalm-pure - * - * @param string|null $value - * @param string $prefix - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function nullOrStartsWith($value, $prefix, $message = '') - { - null === $value || static::startsWith($value, $prefix, $message); - } - /** - * @psalm-pure - * - * @param iterable $value - * @param string $prefix - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allStartsWith($value, $prefix, $message = '') + private function isObjectCallback(SimpleType $type): bool { - static::isIterable($value); - foreach ($value as $entry) { - static::startsWith($entry, $prefix, $message); + if (!is_array($type->value())) { + return \false; } + if (count($type->value()) !== 2) { + return \false; + } + if (!isset($type->value()[0], $type->value()[1])) { + return \false; + } + if (!is_object($type->value()[0]) || !is_string($type->value()[1])) { + return \false; + } + [$object, $methodName] = $type->value(); + return (new ReflectionObject($object))->hasMethod($methodName); } - /** - * @psalm-pure - * - * @param iterable $value - * @param string $prefix - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allNullOrStartsWith($value, $prefix, $message = '') + private function isClassCallback(SimpleType $type): bool { - static::isIterable($value); - foreach ($value as $entry) { - null === $entry || static::startsWith($entry, $prefix, $message); + if (!is_string($type->value()) && !is_array($type->value())) { + return \false; + } + if (is_string($type->value())) { + if (!str_contains($type->value(), '::')) { + return \false; + } + [$className, $methodName] = explode('::', $type->value()); + } + if (is_array($type->value())) { + if (count($type->value()) !== 2) { + return \false; + } + if (!isset($type->value()[0], $type->value()[1])) { + 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)); + if (!class_exists($className)) { + return \false; } + $class = new ReflectionClass($className); + if (!$class->hasMethod($methodName)) { + return \false; + } + $method = $class->getMethod($methodName); + return $method->isPublic() && $method->isStatic(); } - /** - * @psalm-pure - * - * @param string|null $value - * @param string $prefix - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function nullOrNotStartsWith($value, $prefix, $message = '') +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnitPHAR\SebastianBergmann\Type; + +final class FalseType extends Type +{ + public function isAssignable(Type $other): bool { - null === $value || static::notStartsWith($value, $prefix, $message); + if ($other instanceof self) { + return \true; + } + return $other instanceof SimpleType && $other->name() === 'bool' && $other->value() === \false; } - /** - * @psalm-pure - * - * @param iterable $value - * @param string $prefix - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allNotStartsWith($value, $prefix, $message = '') + public function name(): string { - static::isIterable($value); - foreach ($value as $entry) { - static::notStartsWith($entry, $prefix, $message); - } + return 'false'; } - /** - * @psalm-pure - * - * @param iterable $value - * @param string $prefix - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allNullOrNotStartsWith($value, $prefix, $message = '') + public function allowsNull(): bool { - static::isIterable($value); - foreach ($value as $entry) { - null === $entry || static::notStartsWith($entry, $prefix, $message); - } + return \false; } /** - * @psalm-pure - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void + * @psalm-assert-if-true FalseType $this */ - public static function nullOrStartsWithLetter($value, $message = '') + public function isFalse(): bool { - null === $value || static::startsWithLetter($value, $message); + return \true; } - /** - * @psalm-pure - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allStartsWithLetter($value, $message = '') +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnitPHAR\SebastianBergmann\Type; + +final class GenericObjectType extends Type +{ + private bool $allowsNull; + public function __construct(bool $nullable) { - static::isIterable($value); - foreach ($value as $entry) { - static::startsWithLetter($entry, $message); - } + $this->allowsNull = $nullable; } - /** - * @psalm-pure - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allNullOrStartsWithLetter($value, $message = '') + public function isAssignable(Type $other): bool { - static::isIterable($value); - foreach ($value as $entry) { - null === $entry || static::startsWithLetter($entry, $message); + if ($this->allowsNull && $other instanceof NullType) { + return \true; + } + if (!$other instanceof ObjectType) { + return \false; } + return \true; } - /** - * @psalm-pure - * - * @param string|null $value - * @param string $suffix - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function nullOrEndsWith($value, $suffix, $message = '') + public function name(): string { - null === $value || static::endsWith($value, $suffix, $message); + return 'object'; } - /** - * @psalm-pure - * - * @param iterable $value - * @param string $suffix - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allEndsWith($value, $suffix, $message = '') + public function allowsNull(): bool { - static::isIterable($value); - foreach ($value as $entry) { - static::endsWith($entry, $suffix, $message); - } + return $this->allowsNull; } /** - * @psalm-pure - * - * @param iterable $value - * @param string $suffix - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void + * @psalm-assert-if-true GenericObjectType $this */ - public static function allNullOrEndsWith($value, $suffix, $message = '') + public function isGenericObject(): bool { - static::isIterable($value); - foreach ($value as $entry) { - null === $entry || static::endsWith($entry, $suffix, $message); - } + return \true; } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnitPHAR\SebastianBergmann\Type; + +use function assert; +use function count; +use function implode; +use function in_array; +use function sort; +final class IntersectionType extends Type +{ /** - * @psalm-pure - * - * @param string|null $value - * @param string $suffix - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void + * @psalm-var non-empty-list */ - public static function nullOrNotEndsWith($value, $suffix, $message = '') - { - null === $value || static::notEndsWith($value, $suffix, $message); - } + private array $types; /** - * @psalm-pure - * - * @param iterable $value - * @param string $suffix - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void + * @throws RuntimeException */ - public static function allNotEndsWith($value, $suffix, $message = '') + public function __construct(Type ...$types) { - static::isIterable($value); - foreach ($value as $entry) { - static::notEndsWith($entry, $suffix, $message); - } + $this->ensureMinimumOfTwoTypes(...$types); + $this->ensureOnlyValidTypes(...$types); + $this->ensureNoDuplicateTypes(...$types); + $this->types = $types; } - /** - * @psalm-pure - * - * @param iterable $value - * @param string $suffix - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allNullOrNotEndsWith($value, $suffix, $message = '') + public function isAssignable(Type $other): bool { - static::isIterable($value); - foreach ($value as $entry) { - null === $entry || static::notEndsWith($entry, $suffix, $message); - } + return $other->isObject(); } - /** - * @psalm-pure - * - * @param string|null $value - * @param string $pattern - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function nullOrRegex($value, $pattern, $message = '') + public function asString(): string { - null === $value || static::regex($value, $pattern, $message); + return $this->name(); } - /** - * @psalm-pure - * - * @param iterable $value - * @param string $pattern - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allRegex($value, $pattern, $message = '') + public function name(): string { - static::isIterable($value); - foreach ($value as $entry) { - static::regex($entry, $pattern, $message); + $types = []; + foreach ($this->types as $type) { + $types[] = $type->name(); } + sort($types); + return implode('&', $types); } - /** - * @psalm-pure - * - * @param iterable $value - * @param string $pattern - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allNullOrRegex($value, $pattern, $message = '') + public function allowsNull(): bool { - static::isIterable($value); - foreach ($value as $entry) { - null === $entry || static::regex($entry, $pattern, $message); - } + return \false; } /** - * @psalm-pure - * - * @param string|null $value - * @param string $pattern - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void + * @psalm-assert-if-true IntersectionType $this */ - public static function nullOrNotRegex($value, $pattern, $message = '') + public function isIntersection(): bool { - null === $value || static::notRegex($value, $pattern, $message); + return \true; } /** - * @psalm-pure - * - * @param iterable $value - * @param string $pattern - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void + * @psalm-return non-empty-list */ - public static function allNotRegex($value, $pattern, $message = '') + public function types(): array { - static::isIterable($value); - foreach ($value as $entry) { - static::notRegex($entry, $pattern, $message); - } + return $this->types; } /** - * @psalm-pure - * - * @param iterable $value - * @param string $pattern - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void + * @throws RuntimeException */ - public static function allNullOrNotRegex($value, $pattern, $message = '') + private function ensureMinimumOfTwoTypes(Type ...$types): void { - static::isIterable($value); - foreach ($value as $entry) { - null === $entry || static::notRegex($entry, $pattern, $message); + if (count($types) < 2) { + throw new RuntimeException('An intersection type must be composed of at least two types'); } } /** - * @psalm-pure - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function nullOrUnicodeLetters($value, $message = '') - { - null === $value || static::unicodeLetters($value, $message); - } - /** - * @psalm-pure - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void + * @throws RuntimeException */ - public static function allUnicodeLetters($value, $message = '') + private function ensureOnlyValidTypes(Type ...$types): void { - static::isIterable($value); - foreach ($value as $entry) { - static::unicodeLetters($entry, $message); + foreach ($types as $type) { + if (!$type->isObject()) { + throw new RuntimeException('An intersection type can only be composed of interfaces and classes'); + } } } /** - * @psalm-pure - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void + * @throws RuntimeException */ - public static function allNullOrUnicodeLetters($value, $message = '') + private function ensureNoDuplicateTypes(Type ...$types): void { - static::isIterable($value); - foreach ($value as $entry) { - null === $entry || static::unicodeLetters($entry, $message); + $names = []; + foreach ($types as $type) { + assert($type instanceof ObjectType); + $classQualifiedName = $type->className()->qualifiedName(); + if (in_array($classQualifiedName, $names, \true)) { + throw new RuntimeException('An intersection type must not contain duplicate types'); + } + $names[] = $classQualifiedName; } } - /** - * @psalm-pure - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function nullOrAlpha($value, $message = '') +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnitPHAR\SebastianBergmann\Type; + +use function assert; +use function class_exists; +use function is_iterable; +use ReflectionClass; +final class IterableType extends Type +{ + private bool $allowsNull; + public function __construct(bool $nullable) { - null === $value || static::alpha($value, $message); + $this->allowsNull = $nullable; } /** - * @psalm-pure - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void + * @throws RuntimeException */ - public static function allAlpha($value, $message = '') + public function isAssignable(Type $other): bool { - static::isIterable($value); - foreach ($value as $entry) { - static::alpha($entry, $message); + 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) { + $className = $other->className()->qualifiedName(); + assert(class_exists($className)); + return (new ReflectionClass($className))->isIterable(); + } + return \false; } - /** - * @psalm-pure - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allNullOrAlpha($value, $message = '') + public function name(): string { - static::isIterable($value); - foreach ($value as $entry) { - null === $entry || static::alpha($entry, $message); - } + return 'iterable'; } - /** - * @psalm-pure - * - * @param string|null $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function nullOrDigits($value, $message = '') + public function allowsNull(): bool { - null === $value || static::digits($value, $message); + return $this->allowsNull; } /** - * @psalm-pure - * - * @param iterable $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void + * @psalm-assert-if-true IterableType $this */ - public static function allDigits($value, $message = '') + public function isIterable(): bool { - static::isIterable($value); - foreach ($value as $entry) { - static::digits($entry, $message); - } + return \true; } - /** - * @psalm-pure - * - * @param iterable $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allNullOrDigits($value, $message = '') +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnitPHAR\SebastianBergmann\Type; + +final class MixedType extends Type +{ + public function isAssignable(Type $other): bool { - static::isIterable($value); - foreach ($value as $entry) { - null === $entry || static::digits($entry, $message); - } + return !$other instanceof VoidType; } - /** - * @psalm-pure - * - * @param string|null $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function nullOrAlnum($value, $message = '') + public function asString(): string { - null === $value || static::alnum($value, $message); + return 'mixed'; } - /** - * @psalm-pure - * - * @param iterable $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allAlnum($value, $message = '') + public function name(): string { - static::isIterable($value); - foreach ($value as $entry) { - static::alnum($entry, $message); - } + return 'mixed'; } - /** - * @psalm-pure - * - * @param iterable $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allNullOrAlnum($value, $message = '') + public function allowsNull(): bool { - static::isIterable($value); - foreach ($value as $entry) { - null === $entry || static::alnum($entry, $message); - } + return \true; } /** - * @psalm-pure - * @psalm-assert lowercase-string|null $value - * - * @param string|null $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void + * @psalm-assert-if-true MixedType $this */ - public static function nullOrLower($value, $message = '') + public function isMixed(): bool { - null === $value || static::lower($value, $message); + return \true; } - /** - * @psalm-pure - * @psalm-assert iterable $value - * - * @param iterable $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allLower($value, $message = '') +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnitPHAR\SebastianBergmann\Type; + +final class NeverType extends Type +{ + public function isAssignable(Type $other): bool { - static::isIterable($value); - foreach ($value as $entry) { - static::lower($entry, $message); - } + return $other instanceof self; } - /** - * @psalm-pure - * @psalm-assert iterable $value - * - * @param iterable $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allNullOrLower($value, $message = '') + public function name(): string { - static::isIterable($value); - foreach ($value as $entry) { - null === $entry || static::lower($entry, $message); - } + return 'never'; } - /** - * @psalm-pure - * - * @param string|null $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function nullOrUpper($value, $message = '') + public function allowsNull(): bool { - null === $value || static::upper($value, $message); + return \false; } /** - * @psalm-pure - * - * @param iterable $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void + * @psalm-assert-if-true NeverType $this */ - public static function allUpper($value, $message = '') + public function isNever(): bool { - static::isIterable($value); - foreach ($value as $entry) { - static::upper($entry, $message); - } + return \true; } - /** - * @psalm-pure - * @psalm-assert iterable $value - * - * @param iterable $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allNullOrUpper($value, $message = '') +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnitPHAR\SebastianBergmann\Type; + +final class NullType extends Type +{ + public function isAssignable(Type $other): bool { - static::isIterable($value); - foreach ($value as $entry) { - null === $entry || static::upper($entry, $message); - } + return !$other instanceof VoidType; } - /** - * @psalm-pure - * - * @param string|null $value - * @param int $length - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function nullOrLength($value, $length, $message = '') + public function name(): string { - null === $value || static::length($value, $length, $message); + return 'null'; } - /** - * @psalm-pure - * - * @param iterable $value - * @param int $length - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allLength($value, $length, $message = '') + public function asString(): string { - static::isIterable($value); - foreach ($value as $entry) { - static::length($entry, $length, $message); - } + return 'null'; } - /** - * @psalm-pure - * - * @param iterable $value - * @param int $length - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allNullOrLength($value, $length, $message = '') + public function allowsNull(): bool { - static::isIterable($value); - foreach ($value as $entry) { - null === $entry || static::length($entry, $length, $message); - } + return \true; } /** - * @psalm-pure - * - * @param string|null $value - * @param int|float $min - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void + * @psalm-assert-if-true NullType $this */ - public static function nullOrMinLength($value, $min, $message = '') + public function isNull(): bool { - null === $value || static::minLength($value, $min, $message); + return \true; } - /** - * @psalm-pure - * - * @param iterable $value - * @param int|float $min - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allMinLength($value, $min, $message = '') +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnitPHAR\SebastianBergmann\Type; + +use function is_subclass_of; +use function strcasecmp; +final class ObjectType extends Type +{ + private TypeName $className; + private bool $allowsNull; + public function __construct(TypeName $className, bool $allowsNull) { - static::isIterable($value); - foreach ($value as $entry) { - static::minLength($entry, $min, $message); - } + $this->className = $className; + $this->allowsNull = $allowsNull; } - /** - * @psalm-pure - * - * @param iterable $value - * @param int|float $min - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allNullOrMinLength($value, $min, $message = '') + public function isAssignable(Type $other): bool { - static::isIterable($value); - foreach ($value as $entry) { - null === $entry || static::minLength($entry, $min, $message); + if ($this->allowsNull && $other instanceof NullType) { + return \true; + } + if ($other instanceof self) { + if (0 === strcasecmp($this->className->qualifiedName(), $other->className->qualifiedName())) { + return \true; + } + if (is_subclass_of($other->className->qualifiedName(), $this->className->qualifiedName(), \true)) { + return \true; + } } + return \false; } - /** - * @psalm-pure - * - * @param string|null $value - * @param int|float $max - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function nullOrMaxLength($value, $max, $message = '') + public function name(): string { - null === $value || static::maxLength($value, $max, $message); + return $this->className->qualifiedName(); } - /** - * @psalm-pure - * - * @param iterable $value - * @param int|float $max - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allMaxLength($value, $max, $message = '') + public function allowsNull(): bool { - static::isIterable($value); - foreach ($value as $entry) { - static::maxLength($entry, $max, $message); - } + return $this->allowsNull; } - /** - * @psalm-pure - * - * @param iterable $value - * @param int|float $max - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allNullOrMaxLength($value, $max, $message = '') + public function className(): TypeName { - static::isIterable($value); - foreach ($value as $entry) { - null === $entry || static::maxLength($entry, $max, $message); - } + return $this->className; } /** - * @psalm-pure - * - * @param string|null $value - * @param int|float $min - * @param int|float $max - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void + * @psalm-assert-if-true ObjectType $this */ - public static function nullOrLengthBetween($value, $min, $max, $message = '') + public function isObject(): bool + { + return \true; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnitPHAR\SebastianBergmann\Type; + +use function strtolower; +final class SimpleType extends Type +{ + private string $name; + private bool $allowsNull; + private mixed $value; + public function __construct(string $name, bool $nullable, mixed $value = null) { - null === $value || static::lengthBetween($value, $min, $max, $message); + $this->name = $this->normalize($name); + $this->allowsNull = $nullable; + $this->value = $value; } - /** - * @psalm-pure - * - * @param iterable $value - * @param int|float $min - * @param int|float $max - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allLengthBetween($value, $min, $max, $message = '') + public function isAssignable(Type $other): bool { - static::isIterable($value); - foreach ($value as $entry) { - static::lengthBetween($entry, $min, $max, $message); + if ($this->allowsNull && $other instanceof NullType) { + return \true; + } + if ($this->name === 'bool' && $other->name() === 'true') { + return \true; + } + if ($this->name === 'bool' && $other->name() === 'false') { + return \true; } + if ($other instanceof self) { + return $this->name === $other->name; + } + return \false; } - /** - * @psalm-pure - * - * @param iterable $value - * @param int|float $min - * @param int|float $max - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allNullOrLengthBetween($value, $min, $max, $message = '') + public function name(): string { - static::isIterable($value); - foreach ($value as $entry) { - null === $entry || static::lengthBetween($entry, $min, $max, $message); - } + return $this->name; } - /** - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function nullOrFileExists($value, $message = '') + public function allowsNull(): bool { - null === $value || static::fileExists($value, $message); + return $this->allowsNull; } - /** - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allFileExists($value, $message = '') + public function value(): mixed { - static::isIterable($value); - foreach ($value as $entry) { - static::fileExists($entry, $message); - } + return $this->value; } /** - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void + * @psalm-assert-if-true SimpleType $this */ - public static function allNullOrFileExists($value, $message = '') + public function isSimple(): bool { - static::isIterable($value); - foreach ($value as $entry) { - null === $entry || static::fileExists($entry, $message); - } + return \true; } - /** - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function nullOrFile($value, $message = '') + private function normalize(string $name): string { - null === $value || static::file($value, $message); + $name = strtolower($name); + return match ($name) { + 'boolean' => 'bool', + 'real', 'double' => 'float', + 'integer' => 'int', + '[]' => 'array', + default => $name, + }; } - /** - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allFile($value, $message = '') +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnitPHAR\SebastianBergmann\Type; + +use function is_subclass_of; +use function strcasecmp; +final class StaticType extends Type +{ + private TypeName $className; + private bool $allowsNull; + public function __construct(TypeName $className, bool $allowsNull) { - static::isIterable($value); - foreach ($value as $entry) { - static::file($entry, $message); - } + $this->className = $className; + $this->allowsNull = $allowsNull; } - /** - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allNullOrFile($value, $message = '') + public function isAssignable(Type $other): bool { - static::isIterable($value); - foreach ($value as $entry) { - null === $entry || static::file($entry, $message); + if ($this->allowsNull && $other instanceof NullType) { + return \true; + } + if (!$other instanceof ObjectType) { + return \false; + } + if (0 === strcasecmp($this->className->qualifiedName(), $other->className()->qualifiedName())) { + return \true; + } + if (is_subclass_of($other->className()->qualifiedName(), $this->className->qualifiedName(), \true)) { + return \true; } + return \false; } - /** - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function nullOrDirectory($value, $message = '') + public function name(): string { - null === $value || static::directory($value, $message); + return 'static'; } - /** - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allDirectory($value, $message = '') + public function allowsNull(): bool { - static::isIterable($value); - foreach ($value as $entry) { - static::directory($entry, $message); - } + return $this->allowsNull; } /** - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void + * @psalm-assert-if-true StaticType $this */ - public static function allNullOrDirectory($value, $message = '') + public function isStatic(): bool { - static::isIterable($value); - foreach ($value as $entry) { - null === $entry || static::directory($entry, $message); - } + return \true; } - /** - * @param string|null $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function nullOrReadable($value, $message = '') +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnitPHAR\SebastianBergmann\Type; + +final class TrueType extends Type +{ + public function isAssignable(Type $other): bool { - null === $value || static::readable($value, $message); + if ($other instanceof self) { + return \true; + } + return $other instanceof SimpleType && $other->name() === 'bool' && $other->value() === \true; } - /** - * @param iterable $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allReadable($value, $message = '') + public function name(): string { - static::isIterable($value); - foreach ($value as $entry) { - static::readable($entry, $message); - } + return 'true'; } - /** - * @param iterable $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allNullOrReadable($value, $message = '') + public function allowsNull(): bool { - static::isIterable($value); - foreach ($value as $entry) { - null === $entry || static::readable($entry, $message); - } + return \false; } /** - * @param string|null $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void + * @psalm-assert-if-true TrueType $this */ - public static function nullOrWritable($value, $message = '') + public function isTrue(): bool { - null === $value || static::writable($value, $message); + return \true; } - /** - * @param iterable $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allWritable($value, $message = '') +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnitPHAR\SebastianBergmann\Type; + +use function gettype; +use function strtolower; +abstract class Type +{ + public static function fromValue(mixed $value, bool $allowsNull): self { - static::isIterable($value); - foreach ($value as $entry) { - static::writable($entry, $message); + if ($allowsNull === \false) { + if ($value === \true) { + return new TrueType(); + } + if ($value === \false) { + return new FalseType(); + } + } + $typeName = gettype($value); + if ($typeName === 'object') { + return new ObjectType(TypeName::fromQualifiedName($value::class), $allowsNull); + } + $type = self::fromName($typeName, $allowsNull); + if ($type instanceof SimpleType) { + $type = new SimpleType($typeName, $allowsNull, $value); } + return $type; } - /** - * @param iterable $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allNullOrWritable($value, $message = '') + public static function fromName(string $typeName, bool $allowsNull): self + { + return match (strtolower($typeName)) { + 'callable' => new CallableType($allowsNull), + 'true' => new TrueType(), + 'false' => new FalseType(), + 'iterable' => new IterableType($allowsNull), + 'never' => new NeverType(), + 'null' => new NullType(), + 'object' => new GenericObjectType($allowsNull), + 'unknown type' => new UnknownType(), + 'void' => new VoidType(), + 'array', 'bool', 'boolean', 'double', 'float', 'int', 'integer', 'real', 'resource', 'resource (closed)', 'string' => new SimpleType($typeName, $allowsNull), + 'mixed' => new MixedType(), + default => new ObjectType(TypeName::fromQualifiedName($typeName), $allowsNull), + }; + } + public function asString(): string { - static::isIterable($value); - foreach ($value as $entry) { - null === $entry || static::writable($entry, $message); - } + return ($this->allowsNull() ? '?' : '') . $this->name(); } /** - * @psalm-assert class-string|null $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void + * @psalm-assert-if-true CallableType $this */ - public static function nullOrClassExists($value, $message = '') + public function isCallable(): bool { - null === $value || static::classExists($value, $message); + return \false; } /** - * @psalm-assert iterable $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void + * @psalm-assert-if-true TrueType $this */ - public static function allClassExists($value, $message = '') + public function isTrue(): bool { - static::isIterable($value); - foreach ($value as $entry) { - static::classExists($entry, $message); - } + return \false; } /** - * @psalm-assert iterable $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void + * @psalm-assert-if-true FalseType $this */ - public static function allNullOrClassExists($value, $message = '') + public function isFalse(): bool { - static::isIterable($value); - foreach ($value as $entry) { - null === $entry || static::classExists($entry, $message); - } + return \false; } /** - * @psalm-pure - * @psalm-template ExpectedType of object - * @psalm-param class-string $class - * @psalm-assert class-string|ExpectedType|null $value - * - * @param mixed $value - * @param string|object $class - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void + * @psalm-assert-if-true GenericObjectType $this */ - public static function nullOrSubclassOf($value, $class, $message = '') + public function isGenericObject(): bool { - null === $value || static::subclassOf($value, $class, $message); + return \false; } /** - * @psalm-pure - * @psalm-template ExpectedType of object - * @psalm-param class-string $class - * @psalm-assert iterable|ExpectedType> $value - * - * @param mixed $value - * @param string|object $class - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void + * @psalm-assert-if-true IntersectionType $this */ - public static function allSubclassOf($value, $class, $message = '') + public function isIntersection(): bool { - static::isIterable($value); - foreach ($value as $entry) { - static::subclassOf($entry, $class, $message); - } + return \false; } /** - * @psalm-pure - * @psalm-template ExpectedType of object - * @psalm-param class-string $class - * @psalm-assert iterable|ExpectedType|null> $value - * - * @param mixed $value - * @param string|object $class - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void + * @psalm-assert-if-true IterableType $this */ - public static function allNullOrSubclassOf($value, $class, $message = '') + public function isIterable(): bool { - static::isIterable($value); - foreach ($value as $entry) { - null === $entry || static::subclassOf($entry, $class, $message); - } + return \false; } /** - * @psalm-assert class-string|null $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void + * @psalm-assert-if-true MixedType $this */ - public static function nullOrInterfaceExists($value, $message = '') + public function isMixed(): bool { - null === $value || static::interfaceExists($value, $message); + return \false; } /** - * @psalm-assert iterable $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void + * @psalm-assert-if-true NeverType $this */ - public static function allInterfaceExists($value, $message = '') + public function isNever(): bool { - static::isIterable($value); - foreach ($value as $entry) { - static::interfaceExists($entry, $message); - } + return \false; } /** - * @psalm-assert iterable $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void + * @psalm-assert-if-true NullType $this */ - public static function allNullOrInterfaceExists($value, $message = '') + public function isNull(): bool { - static::isIterable($value); - foreach ($value as $entry) { - null === $entry || static::interfaceExists($entry, $message); - } + return \false; } /** - * @psalm-pure - * @psalm-template ExpectedType of object - * @psalm-param class-string $interface - * @psalm-assert class-string|null $value - * - * @param mixed $value - * @param mixed $interface - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void + * @psalm-assert-if-true ObjectType $this */ - public static function nullOrImplementsInterface($value, $interface, $message = '') + public function isObject(): bool { - null === $value || static::implementsInterface($value, $interface, $message); + return \false; } /** - * @psalm-pure - * @psalm-template ExpectedType of object - * @psalm-param class-string $interface - * @psalm-assert iterable> $value - * - * @param mixed $value - * @param mixed $interface - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void + * @psalm-assert-if-true SimpleType $this */ - public static function allImplementsInterface($value, $interface, $message = '') + public function isSimple(): bool { - static::isIterable($value); - foreach ($value as $entry) { - static::implementsInterface($entry, $interface, $message); - } + return \false; } /** - * @psalm-pure - * @psalm-template ExpectedType of object - * @psalm-param class-string $interface - * @psalm-assert iterable|null> $value - * - * @param mixed $value - * @param mixed $interface - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void + * @psalm-assert-if-true StaticType $this */ - public static function allNullOrImplementsInterface($value, $interface, $message = '') + public function isStatic(): bool { - static::isIterable($value); - foreach ($value as $entry) { - null === $entry || static::implementsInterface($entry, $interface, $message); - } + return \false; } /** - * @psalm-pure - * @psalm-param class-string|object|null $classOrObject - * - * @param string|object|null $classOrObject - * @param mixed $property - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void + * @psalm-assert-if-true UnionType $this */ - public static function nullOrPropertyExists($classOrObject, $property, $message = '') + public function isUnion(): bool { - null === $classOrObject || static::propertyExists($classOrObject, $property, $message); + return \false; } /** - * @psalm-pure - * @psalm-param iterable $classOrObject - * - * @param iterable $classOrObject - * @param mixed $property - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void + * @psalm-assert-if-true UnknownType $this */ - public static function allPropertyExists($classOrObject, $property, $message = '') + public function isUnknown(): bool { - static::isIterable($classOrObject); - foreach ($classOrObject as $entry) { - static::propertyExists($entry, $property, $message); - } + return \false; } /** - * @psalm-pure - * @psalm-param iterable $classOrObject - * - * @param iterable $classOrObject - * @param mixed $property - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void + * @psalm-assert-if-true VoidType $this */ - public static function allNullOrPropertyExists($classOrObject, $property, $message = '') + public function isVoid(): bool { - static::isIterable($classOrObject); - foreach ($classOrObject as $entry) { - null === $entry || static::propertyExists($entry, $property, $message); - } + return \false; } + abstract public function isAssignable(self $other): bool; + abstract public function name(): string; + abstract public function allowsNull(): bool; +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnitPHAR\SebastianBergmann\Type; + +use function count; +use function implode; +use function sort; +final class UnionType extends Type +{ /** - * @psalm-pure - * @psalm-param class-string|object|null $classOrObject - * - * @param string|object|null $classOrObject - * @param mixed $property - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void + * @psalm-var non-empty-list */ - public static function nullOrPropertyNotExists($classOrObject, $property, $message = '') - { - null === $classOrObject || static::propertyNotExists($classOrObject, $property, $message); - } + private array $types; /** - * @psalm-pure - * @psalm-param iterable $classOrObject - * - * @param iterable $classOrObject - * @param mixed $property - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void + * @throws RuntimeException */ - public static function allPropertyNotExists($classOrObject, $property, $message = '') + public function __construct(Type ...$types) { - static::isIterable($classOrObject); - foreach ($classOrObject as $entry) { - static::propertyNotExists($entry, $property, $message); - } + $this->ensureMinimumOfTwoTypes(...$types); + $this->ensureOnlyValidTypes(...$types); + $this->types = $types; } - /** - * @psalm-pure - * @psalm-param iterable $classOrObject - * - * @param iterable $classOrObject - * @param mixed $property - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allNullOrPropertyNotExists($classOrObject, $property, $message = '') + public function isAssignable(Type $other): bool { - static::isIterable($classOrObject); - foreach ($classOrObject as $entry) { - null === $entry || static::propertyNotExists($entry, $property, $message); + foreach ($this->types as $type) { + if ($type->isAssignable($other)) { + return \true; + } } + return \false; } - /** - * @psalm-pure - * @psalm-param class-string|object|null $classOrObject - * - * @param string|object|null $classOrObject - * @param mixed $method - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function nullOrMethodExists($classOrObject, $method, $message = '') + public function asString(): string { - null === $classOrObject || static::methodExists($classOrObject, $method, $message); + return $this->name(); } - /** - * @psalm-pure - * @psalm-param iterable $classOrObject - * - * @param iterable $classOrObject - * @param mixed $method - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allMethodExists($classOrObject, $method, $message = '') + public function name(): string { - static::isIterable($classOrObject); - foreach ($classOrObject as $entry) { - static::methodExists($entry, $method, $message); + $types = []; + foreach ($this->types as $type) { + if ($type->isIntersection()) { + $types[] = '(' . $type->name() . ')'; + continue; + } + $types[] = $type->name(); } + sort($types); + return implode('|', $types); } - /** - * @psalm-pure - * @psalm-param iterable $classOrObject - * - * @param iterable $classOrObject - * @param mixed $method - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allNullOrMethodExists($classOrObject, $method, $message = '') + public function allowsNull(): bool { - static::isIterable($classOrObject); - foreach ($classOrObject as $entry) { - null === $entry || static::methodExists($entry, $method, $message); + foreach ($this->types as $type) { + if ($type instanceof NullType) { + return \true; + } } + return \false; } /** - * @psalm-pure - * @psalm-param class-string|object|null $classOrObject - * - * @param string|object|null $classOrObject - * @param mixed $method - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void + * @psalm-assert-if-true UnionType $this */ - public static function nullOrMethodNotExists($classOrObject, $method, $message = '') + public function isUnion(): bool { - null === $classOrObject || static::methodNotExists($classOrObject, $method, $message); + return \true; } - /** - * @psalm-pure - * @psalm-param iterable $classOrObject - * - * @param iterable $classOrObject - * @param mixed $method - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allMethodNotExists($classOrObject, $method, $message = '') + public function containsIntersectionTypes(): bool { - static::isIterable($classOrObject); - foreach ($classOrObject as $entry) { - static::methodNotExists($entry, $method, $message); + foreach ($this->types as $type) { + if ($type->isIntersection()) { + return \true; + } } + return \false; } /** - * @psalm-pure - * @psalm-param iterable $classOrObject - * - * @param iterable $classOrObject - * @param mixed $method - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void + * @psalm-return non-empty-list */ - public static function allNullOrMethodNotExists($classOrObject, $method, $message = '') + public function types(): array { - static::isIterable($classOrObject); - foreach ($classOrObject as $entry) { - null === $entry || static::methodNotExists($entry, $method, $message); - } + return $this->types; } /** - * @psalm-pure - * - * @param array|null $array - * @param string|int $key - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void + * @throws RuntimeException */ - public static function nullOrKeyExists($array, $key, $message = '') + private function ensureMinimumOfTwoTypes(Type ...$types): void { - null === $array || static::keyExists($array, $key, $message); + if (count($types) < 2) { + throw new RuntimeException('A union type must be composed of at least two types'); + } } /** - * @psalm-pure - * - * @param iterable $array - * @param string|int $key - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void + * @throws RuntimeException */ - public static function allKeyExists($array, $key, $message = '') + private function ensureOnlyValidTypes(Type ...$types): void { - static::isIterable($array); - foreach ($array as $entry) { - static::keyExists($entry, $key, $message); + foreach ($types as $type) { + if ($type instanceof UnknownType) { + throw new RuntimeException('A union type must not be composed of an unknown type'); + } + if ($type instanceof VoidType) { + throw new RuntimeException('A union type must not be composed of a void type'); + } } } - /** - * @psalm-pure - * - * @param iterable $array - * @param string|int $key - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allNullOrKeyExists($array, $key, $message = '') +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnitPHAR\SebastianBergmann\Type; + +final class UnknownType extends Type +{ + public function isAssignable(Type $other): bool { - static::isIterable($array); - foreach ($array as $entry) { - null === $entry || static::keyExists($entry, $key, $message); - } + return \true; } - /** - * @psalm-pure - * - * @param array|null $array - * @param string|int $key - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function nullOrKeyNotExists($array, $key, $message = '') + public function name(): string { - null === $array || static::keyNotExists($array, $key, $message); + return 'unknown type'; } - /** - * @psalm-pure - * - * @param iterable $array - * @param string|int $key - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allKeyNotExists($array, $key, $message = '') + public function asString(): string { - static::isIterable($array); - foreach ($array as $entry) { - static::keyNotExists($entry, $key, $message); - } + return ''; } - /** - * @psalm-pure - * - * @param iterable $array - * @param string|int $key - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allNullOrKeyNotExists($array, $key, $message = '') + public function allowsNull(): bool { - static::isIterable($array); - foreach ($array as $entry) { - null === $entry || static::keyNotExists($entry, $key, $message); - } + return \true; } /** - * @psalm-pure - * @psalm-assert array-key|null $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void + * @psalm-assert-if-true UnknownType $this */ - public static function nullOrValidArrayKey($value, $message = '') + public function isUnknown(): bool { - null === $value || static::validArrayKey($value, $message); + return \true; } - /** - * @psalm-pure - * @psalm-assert iterable $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allValidArrayKey($value, $message = '') +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnitPHAR\SebastianBergmann\Type; + +final class VoidType extends Type +{ + public function isAssignable(Type $other): bool { - static::isIterable($value); - foreach ($value as $entry) { - static::validArrayKey($entry, $message); - } + return $other instanceof self; } - /** - * @psalm-pure - * @psalm-assert iterable $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allNullOrValidArrayKey($value, $message = '') + public function name(): string { - static::isIterable($value); - foreach ($value as $entry) { - null === $entry || static::validArrayKey($entry, $message); - } + return 'void'; } - /** - * @param Countable|array|null $array - * @param int $number - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function nullOrCount($array, $number, $message = '') + public function allowsNull(): bool { - null === $array || static::count($array, $number, $message); + return \false; } /** - * @param iterable $array - * @param int $number - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void + * @psalm-assert-if-true VoidType $this */ - public static function allCount($array, $number, $message = '') + public function isVoid(): bool { - static::isIterable($array); - foreach ($array as $entry) { - static::count($entry, $number, $message); - } + return \true; } - /** - * @param iterable $array - * @param int $number - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allNullOrCount($array, $number, $message = '') +} +BSD 3-Clause License + +Copyright (c) 2013-2023, Sebastian Bergmann +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnitPHAR\SebastianBergmann; + +use function end; +use function explode; +use function fclose; +use function is_dir; +use function is_resource; +use function proc_close; +use function proc_open; +use function stream_get_contents; +use function substr_count; +use function trim; +final class Version +{ + private readonly string $version; + public function __construct(string $release, string $path) { - static::isIterable($array); - foreach ($array as $entry) { - null === $entry || static::count($entry, $number, $message); - } + $this->version = $this->generate($release, $path); } - /** - * @param Countable|array|null $array - * @param int|float $min - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function nullOrMinCount($array, $min, $message = '') + public function asString(): string { - null === $array || static::minCount($array, $min, $message); + return $this->version; } - /** - * @param iterable $array - * @param int|float $min - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allMinCount($array, $min, $message = '') + private function generate(string $release, string $path): string { - static::isIterable($array); - foreach ($array as $entry) { - static::minCount($entry, $min, $message); + if (substr_count($release, '.') + 1 === 3) { + $version = $release; + } else { + $version = $release . '-dev'; + } + $git = $this->getGitInformation($path); + if (!$git) { + return $version; } + if (substr_count($release, '.') + 1 === 3) { + return $git; + } + $git = explode('-', $git); + return $release . '-' . end($git); } - /** - * @param iterable $array - * @param int|float $min - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allNullOrMinCount($array, $min, $message = '') + private function getGitInformation(string $path): bool|string { - static::isIterable($array); - foreach ($array as $entry) { - null === $entry || static::minCount($entry, $min, $message); + 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; } - /** - * @param Countable|array|null $array - * @param int|float $max - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function nullOrMaxCount($array, $max, $message = '') +} + and contributors +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of Arne Blankerts nor the names of contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT * NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS +BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, +OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +ensureValidUri($value); + $this->value = $value; } - /** - * @param iterable $array - * @param int|float $max - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allMaxCount($array, $max, $message = '') + public function asString(): string { - static::isIterable($array); - foreach ($array as $entry) { - static::maxCount($entry, $max, $message); - } + return $this->value; } - /** - * @param iterable $array - * @param int|float $max - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allNullOrMaxCount($array, $max, $message = '') + private function ensureValidUri($value): void { - static::isIterable($array); - foreach ($array as $entry) { - null === $entry || static::maxCount($entry, $max, $message); + if (\strpos($value, ':') === \false) { + throw new NamespaceUriException(\sprintf("Namespace URI '%s' must contain at least one colon", $value)); } } +} +line = $line; + $this->name = $name; + $this->value = $value; } - /** - * @param iterable $array - * @param int|float $min - * @param int|float $max - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allCountBetween($array, $min, $max, $message = '') + public function getLine(): int { - static::isIterable($array); - foreach ($array as $entry) { - static::countBetween($entry, $min, $max, $message); - } + return $this->line; } - /** - * @param iterable $array - * @param int|float $min - * @param int|float $max - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allNullOrCountBetween($array, $min, $max, $message = '') + public function getName(): string { - static::isIterable($array); - foreach ($array as $entry) { - null === $entry || static::countBetween($entry, $min, $max, $message); - } + return $this->name; } - /** - * @psalm-pure - * @psalm-assert list|null $array - * - * @param mixed $array - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function nullOrIsList($array, $message = '') + public function getValue(): string { - null === $array || static::isList($array, $message); + return $this->value; } - /** - * @psalm-pure - * @psalm-assert iterable $array - * - * @param mixed $array - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allIsList($array, $message = '') +} +tokens[] = $token; } - /** - * @psalm-pure - * @psalm-assert iterable $array - * - * @param mixed $array - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allNullOrIsList($array, $message = '') + public function current(): Token { - static::isIterable($array); - foreach ($array as $entry) { - null === $entry || static::isList($entry, $message); - } + return \current($this->tokens); } - /** - * @psalm-pure - * @psalm-assert non-empty-list|null $array - * - * @param mixed $array - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function nullOrIsNonEmptyList($array, $message = '') + public function key(): int { - null === $array || static::isNonEmptyList($array, $message); + return \key($this->tokens); } - /** - * @psalm-pure - * @psalm-assert iterable $array - * - * @param mixed $array - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allIsNonEmptyList($array, $message = '') + public function next(): void { - static::isIterable($array); - foreach ($array as $entry) { - static::isNonEmptyList($entry, $message); - } + \next($this->tokens); + $this->pos++; } - /** - * @psalm-pure - * @psalm-assert iterable $array - * - * @param mixed $array - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allNullOrIsNonEmptyList($array, $message = '') + public function valid(): bool { - static::isIterable($array); - foreach ($array as $entry) { - null === $entry || static::isNonEmptyList($entry, $message); - } + return $this->count() > $this->pos; } - /** - * @psalm-pure - * @psalm-template T - * @psalm-param mixed|array|null $array - * @psalm-assert array|null $array - * - * @param mixed $array - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function nullOrIsMap($array, $message = '') + public function rewind(): void { - null === $array || static::isMap($array, $message); + \reset($this->tokens); + $this->pos = 0; } - /** - * @psalm-pure - * @psalm-template T - * @psalm-param iterable> $array - * @psalm-assert iterable> $array - * - * @param mixed $array - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allIsMap($array, $message = '') + public function count(): int { - static::isIterable($array); - foreach ($array as $entry) { - static::isMap($entry, $message); - } + return \count($this->tokens); } - /** - * @psalm-pure - * @psalm-template T - * @psalm-param iterable|null> $array - * @psalm-assert iterable|null> $array - * - * @param mixed $array - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allNullOrIsMap($array, $message = '') + public function offsetExists($offset): bool { - static::isIterable($array); - foreach ($array as $entry) { - null === $entry || static::isMap($entry, $message); - } + return isset($this->tokens[$offset]); } /** - * @psalm-pure - * @psalm-template T - * @psalm-param mixed|array|null $array - * - * @param mixed $array - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void + * @throws TokenCollectionException */ - public static function nullOrIsNonEmptyMap($array, $message = '') + public function offsetGet($offset): Token { - null === $array || static::isNonEmptyMap($array, $message); + if (!$this->offsetExists($offset)) { + throw new TokenCollectionException(\sprintf('No Token at offest %s', $offset)); + } + return $this->tokens[$offset]; } /** - * @psalm-pure - * @psalm-template T - * @psalm-param iterable> $array - * - * @param mixed $array - * @param string $message - * - * @throws InvalidArgumentException + * @param Token $value * - * @return void + * @throws TokenCollectionException */ - public static function allIsNonEmptyMap($array, $message = '') + public function offsetSet($offset, $value): void { - static::isIterable($array); - foreach ($array as $entry) { - static::isNonEmptyMap($entry, $message); + 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; } - /** - * @psalm-pure - * @psalm-template T - * @psalm-param iterable|null> $array - * @psalm-assert iterable|null> $array - * @psalm-assert iterable $array - * - * @param mixed $array - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allNullOrIsNonEmptyMap($array, $message = '') + public function offsetUnset($offset): void { - static::isIterable($array); - foreach ($array as $entry) { - null === $entry || static::isNonEmptyMap($entry, $message); - } + unset($this->tokens[$offset]); } +} + '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 { - null === $value || static::uuid($value, $message); + $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]); + if (!$values) { + $result->addToken(new Token($line, \token_name($tok[0]), '{binary data}')); + continue; + } + foreach ($values as $v) { + $token = new Token($line, \token_name($tok[0]), $v); + $lastToken = $token; + $line++; + if ($v === '') { + continue; + } + $result->addToken($token); + } + } + return $this->fillBlanks($result, $lastToken->getLine()); } - /** - * @psalm-pure - * - * @param iterable $value - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allUuid($value, $message = '') + private function fillBlanks(TokenCollection $tokens, int $maxLine): TokenCollection { - static::isIterable($value); - foreach ($value as $entry) { - static::uuid($entry, $message); + $prev = new Token(0, 'Placeholder', ''); + $final = new TokenCollection(); + foreach ($tokens as $token) { + $gap = $token->getLine() - $prev->getLine(); + while ($gap > 1) { + $linebreak = new Token($prev->getLine() + 1, 'T_WHITESPACE', ''); + $final->addToken($linebreak); + $prev = $linebreak; + $gap--; + } + $final->addToken($token); + $prev = $token; + } + $gap = $maxLine - $prev->getLine(); + while ($gap > 0) { + $linebreak = new Token($prev->getLine() + 1, 'T_WHITESPACE', ''); + $final->addToken($linebreak); + $prev = $linebreak; + $gap--; } + return $final; } +} + $value - * @param string $message - * - * @throws InvalidArgumentException + * XMLSerializer constructor. * - * @return void + * @param NamespaceUri $xmlns */ - public static function allNullOrUuid($value, $message = '') + public function __construct(?NamespaceUri $xmlns = null) { - static::isIterable($value); - foreach ($value as $entry) { - null === $entry || static::uuid($entry, $message); + if ($xmlns === null) { + $xmlns = new NamespaceUri('https://github.com/theseer/tokenizer'); } + $this->xmlns = $xmlns; } - /** - * @psalm-param class-string $class - * - * @param Closure|null $expression - * @param string $class - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function nullOrThrows($expression, $class = 'Exception', $message = '') + public function toDom(TokenCollection $tokens): DOMDocument { - null === $expression || static::throws($expression, $class, $message); + $dom = new DOMDocument(); + $dom->preserveWhiteSpace = \false; + $dom->loadXML($this->toXML($tokens)); + return $dom; } - /** - * @psalm-param class-string $class - * - * @param iterable $expression - * @param string $class - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allThrows($expression, $class = 'Exception', $message = '') + public function toXML(TokenCollection $tokens): string { - static::isIterable($expression); - foreach ($expression as $entry) { - static::throws($entry, $class, $message); + $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(); } - /** - * @psalm-param class-string $class - * - * @param iterable $expression - * @param string $class - * @param string $message - * - * @throws InvalidArgumentException - * - * @return void - */ - public static function allNullOrThrows($expression, $class = 'Exception', $message = '') + private function addToken(Token $token): void { - static::isIterable($expression); - foreach ($expression as $entry) { - null === $entry || static::throws($entry, $class, $message); + 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(); } } } @@ -112056,12 +106704,17 @@ trait Mixin namespace PHPSTORM_META { override( - \PHPUnit\Framework\TestCase::createMock(0), + \PHPUnit\Framework\TestCase::createStub(0), map([""=>"$0"]) ); override( - \PHPUnit\Framework\TestCase::createStub(0), + \PHPUnit\Framework\TestCase::createConfiguredStub(0), + map([""=>"$0"]) + ); + + override( + \PHPUnit\Framework\TestCase::createMock(0), map([""=>"$0"]) ); @@ -112085,4 +106738,4 @@ namespace PHPSTORM_META { map([""=>"$0"]) ); } -䅢=ƿcL]yџ4 Zl8+AMM]ʆ'2GBMB \ No newline at end of file +\Cw㸨UtzGA'o.$=.KCe q4űGBMB \ No newline at end of file

    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�s-yZA$n;f#B_l_Y}fh%v5! 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 z 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�^?#fV--d*?}*HDmaOku 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 DoPtkG delta 858215 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

    WM-y&?0wlN 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%0Drk^)@cd-jzZL%e^KcxG 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