diff --git a/.phive/phars.xml b/.phive/phars.xml index 3c109e8f..3107fd69 100644 --- a/.phive/phars.xml +++ b/.phive/phars.xml @@ -1,6 +1,6 @@ - + diff --git a/phpunit.xml b/phpunit.xml index 1dbb0c76..eecef62a 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/phpunit.xml.bak b/phpunit.xml.bak new file mode 100644 index 00000000..1dbb0c76 --- /dev/null +++ b/phpunit.xml.bak @@ -0,0 +1,31 @@ + + + + + src + + + src/bootstrap.php + + + + + + + + + tests/Xml + + + diff --git a/src/Xml/Reader/Configurator/xsd_schema.php b/src/Xml/Reader/Configurator/xsd_schema.php index 1107cf0c..383fa697 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 a0739e5f..2ff980a5 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 0765626f..7cdd8307 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 8f2224e2..158117cb 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 7e6d3b4b..be1b2e2a 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 7c912f74..bf3d017d 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 c94c0f02..07b35625 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 a1689be2..476646db 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 5ca38310..aa0994f5 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 4a2d9bd1..54842a54 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 ff373ad4..d6947329 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 98bd3238..73c9990f 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 cb490d2a..fb333371 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 bdf824ae..3d1505b4 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 3fb9aff2..38062417 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 6c0e635a..1bd639ba 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 a3d9edc5..fe774466 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 6753c8fa..f50e98a2 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 faf05234..e0fe29e8 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 02d16b0e..59dd361e 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 53c0d761..7d997691 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 a806a16d..643594a1 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 3d298f4b..5bd6c847 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 5c3218e4..8776c360 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 acf5aa84..1a20a662 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 eff410b7..75b7ec03 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 5f08800f..e4f62b9d 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 f7edd214..597e237f 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 b12a00af..5a0ac229 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 baf6e5ed..ea4b7f06 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 67bcce91..0490cf71 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 db185692..caf205f7 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 00807edb..7957b3b7 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 bd7bb9ce..4173e041 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 26398533..60085924 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 c5179829..77f1d5d4 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