diff --git a/ChangeLog.md b/ChangeLog.md index 753ebab0a..2aa82db81 100755 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -7,6 +7,9 @@ XP Framework Core ChangeLog ### RFCs +* Implemented xp-framework/rfc#336, part 2: Remove XP annotations syntax, + which was deprecated in XP 11, released October 2021. See PR #340. + (@thekid) * Implemented xp-framework/rfc#343, part 1: Drop support for all but the latest PHP 7 release. The minimum required PHP version is now **7.4**! (@thekid) diff --git a/src/main/php/lang/reflect/ClassParser.class.php b/src/main/php/lang/reflect/ClassParser.class.php index 2bdb2b27e..13e769502 100755 --- a/src/main/php/lang/reflect/ClassParser.class.php +++ b/src/main/php/lang/reflect/ClassParser.class.php @@ -300,15 +300,8 @@ public function parseAnnotations($bytes, $context, $imports= [], $line= -1) { for ($state= 0, $i= 1, $s= sizeof($tokens); $i < $s; $i++) { if (T_WHITESPACE === $tokens[$i][0]) { continue; - } else if (0 === $state) { // Initial state, expecting @attr or @$param: attr - if ('@' === $tokens[$i]) { - $annotation= $tokens[$i + 1][1]; - $param= null; - $value= null; - $i++; - $state= 1; - trigger_error('XP annotation syntax is deprecated in '.$place, E_USER_DEPRECATED); - } else if (']' === $tokens[$i]) { // Handle situations with trailing comma + } else if (0 === $state) { + if (']' === $tokens[$i]) { // Handle situations with trailing comma $annotations[0][$annotation]= $value; return $annotations; } else { @@ -344,7 +337,7 @@ public function parseAnnotations($bytes, $context, $imports= [], $line= -1) { } else { throw new IllegalStateException('Parse error: Expecting either "(", "," or "]", have '.(is_array($tokens[$i]) ? $tokens[$i][1] : $tokens[$i])); } - } else if (2 === $state) { // Inside braces of @attr(...) + } else if (2 === $state) { if (')' === $tokens[$i]) { $state= 1; } else if ($i + 2 < $s && (':' === $tokens[$i + 1] || ':' === $tokens[$i + 2])) { @@ -382,7 +375,7 @@ public function parseAnnotations($bytes, $context, $imports= [], $line= -1) { $state= 1; } else if (',' === $tokens[$i]) { $key= null; - } else if ('=' === $tokens[$i] || ':' === $tokens[$i]) { + } else if (':' === $tokens[$i]) { $state= 4; } else if (is_array($tokens[$i])) { $key= $tokens[$i][1]; diff --git a/src/test/php/lang/unittest/AnnotationParsingTest.class.php b/src/test/php/lang/unittest/AnnotationParsingTest.class.php deleted file mode 100755 index 1178e8856..000000000 --- a/src/test/php/lang/unittest/AnnotationParsingTest.class.php +++ /dev/null @@ -1,517 +0,0 @@ -parseAnnotations($input, nameof($this), array_merge($imports, [ - 'Namespaced' => 'lang.unittest.fixture.Namespaced' - ])); - } finally { - \xp::gc(); // Strip deprecation warning - } - } - - #[Test] - public function no_value() { - Assert::equals( - [0 => ['hello' => null], 1 => []], - $this->parse("#[@hello]") - ); - } - - #[Test] - public function sq_string_value() { - Assert::equals( - [0 => ['hello' => 'World'], 1 => []], - $this->parse("#[@hello('World')]") - ); - } - - #[Test] - public function sq_string_value_with_equals_sign() { - Assert::equals( - [0 => ['hello' => 'World=Welt'], 1 => []], - $this->parse("#[@hello('World=Welt')]") - ); - } - - #[Test] - public function sq_string_value_with_at_sign() { - Assert::equals( - [0 => ['hello' => '@World'], 1 => []], - $this->parse("#[@hello('@World')]") - ); - } - - #[Test] - public function sq_string_value_with_annotation() { - Assert::equals( - [0 => ['hello' => '@hello("World")'], 1 => []], - $this->parse("#[@hello('@hello(\"World\")')]") - ); - } - - #[Test] - public function sq_string_value_with_double_quotes() { - Assert::equals( - [0 => ['hello' => 'said "he"'], 1 => []], - $this->parse("#[@hello('said \"he\"')]") - ); - } - - #[Test] - public function sq_string_value_with_escaped_single_quotes() { - Assert::equals( - [0 => ['hello' => "said 'he'"], 1 => []], - $this->parse("#[@hello('said \'he\'')]") - ); - } - - #[Test] - public function dq_string_value() { - Assert::equals( - [0 => ['hello' => 'World'], 1 => []], - $this->parse('#[@hello("World")]') - ); - } - - #[Test] - public function dq_string_value_with_single_quote() { - Assert::equals( - [0 => ['hello' => 'Beck\'s'], 1 => []], - $this->parse('#[@hello("Beck\'s")]') - ); - } - - #[Test] - public function dq_string_value_with_escaped_double_quotes() { - Assert::equals( - [0 => ['hello' => 'said "he"'], 1 => []], - $this->parse('#[@hello("said \"he\"")]') - ); - } - - #[Test] - public function dq_string_value_with_escape_sequence() { - Assert::equals( - [0 => ['hello' => "World\n"], 1 => []], - $this->parse('#[@hello("World\n")]') - ); - } - - #[Test] - public function dq_string_value_with_at_sign() { - Assert::equals( - [0 => ['hello' => '@World'], 1 => []], - $this->parse('#[@hello("@World")]') - ); - } - - #[Test] - public function dq_string_value_with_annotation() { - Assert::equals( - [0 => ['hello' => '@hello(\'World\')'], 1 => []], - $this->parse('#[@hello("@hello(\'World\')")]') - ); - } - - #[Test] - public function int_value() { - Assert::equals( - [0 => ['answer' => 42], 1 => []], - $this->parse('#[@answer(42)]') - ); - } - - #[Test] - public function double_value() { - Assert::equals( - [0 => ['version' => 3.5], 1 => []], - $this->parse('#[@version(3.5)]') - ); - } - - #[Test] - public function multi_value_using_short_array() { - Assert::equals( - [0 => ['xmlmapping' => ['hw_server', 'server']], 1 => []], - $this->parse("#[@xmlmapping(['hw_server', 'server'])]") - ); - } - - #[Test] - public function short_array_value() { - Assert::equals( - [0 => ['versions' => [3.4, 3.5]], 1 => []], - $this->parse('#[@versions([3.4, 3.5])]') - ); - } - - #[Test] - public function short_array_value_with_nested_arrays() { - Assert::equals( - [0 => ['versions' => [[3], [4]]], 1 => []], - $this->parse('#[@versions([[3], [4]])]') - ); - } - - #[Test] - public function short_array_value_with_strings_containing_braces() { - Assert::equals( - [0 => ['versions' => ['(3..4]']], 1 => []], - $this->parse('#[@versions(["(3..4]"])]') - ); - } - - #[Test] - public function bool_true_value() { - Assert::equals( - [0 => ['supported' => true], 1 => []], - $this->parse('#[@supported(true)]') - ); - } - - #[Test] - public function bool_false_value() { - Assert::equals( - [0 => ['supported' => false], 1 => []], - $this->parse('#[@supported(false)]') - ); - } - - #[Test] - public function short_map_value() { - Assert::equals( - [0 => ['colors' => ['green' => '$10.50', 'red' => '$9.99']], 1 => []], - $this->parse("#[@colors(['green' => '$10.50', 'red' => '$9.99'])]") - ); - } - - #[Test] - public function multi_line_annotation() { - Assert::equals( - [0 => ['interceptors' => ['classes' => [ - 'lang.unittest.FirstInterceptor', - 'lang.unittest.SecondInterceptor', - ]]], 1 => []], - $this->parse(" - #[@interceptors(['classes' => [ - 'lang.unittest.FirstInterceptor', - 'lang.unittest.SecondInterceptor', - ]])] - ") - ); - } - - #[Test] - public function simple_XPath_annotation() { - Assert::equals( - [0 => ['fromXml' => ['xpath' => '/parent/child/@attribute']], 1 => []], - $this->parse("#[@fromXml(['xpath' => '/parent/child/@attribute'])]") - ); - } - - #[Test] - public function complex_XPath_annotation() { - Assert::equals( - [0 => ['fromXml' => ['xpath' => '/parent[@attr="value"]/child[@attr1="val1" and @attr2="val2"]']], 1 => []], - $this->parse("#[@fromXml(['xpath' => '/parent[@attr=\"value\"]/child[@attr1=\"val1\" and @attr2=\"val2\"]'])]") - ); - } - - #[Test] - public function string_with_equal_signs() { - Assert::equals( - [0 => ['permission' => 'rn=login, rt=config'], 1 => []], - $this->parse("#[@permission('rn=login, rt=config')]") - ); - } - - #[Test] - public function string_assigned_without_whitespace() { - Assert::equals( - [0 => ['arg' => ['name' => 'verbose', 'short' => 'v']], 1 => []], - $this->parse("#[@arg(['name' => 'verbose', 'short' => 'v'])]") - ); - } - - #[Test] - public function multiple_values_with_strings_and_equal_signs() { - Assert::equals( - [0 => ['permission' => ['names' => ['rn=login, rt=config1', 'rn=login, rt=config2']]], 1 => []], - $this->parse("#[@permission(['names' => ['rn=login, rt=config1', 'rn=login, rt=config2']])]") - ); - } - - #[Test] - public function unittest_annotation() { - Assert::equals( - [0 => ['test' => NULL, 'ignore' => NULL, 'limit' => ['time' => 0.1, 'memory' => 100]], 1 => []], - $this->parse("#[@test, @ignore, @limit(['time' => 0.1, 'memory' => 100])]") - ); - } - - #[Test] - public function overloaded_annotation() { - Assert::equals( - [0 => ['overloaded' => ['signatures' => [['string'], ['string', 'string']]]], 1 => []], - $this->parse('#[@overloaded(["signatures" => [["string"], ["string", "string"]]])]') - ); - } - - #[Test] - public function overloaded_annotation_spanning_multiple_lines() { - Assert::equals( - [0 => ['overloaded' => ['signatures' => [['string'], ['string', 'string']]]], 1 => []], - $this->parse( - "#[@overloaded(['signatures' => [\n". - " ['string'],\n". - " ['string', 'string']\n". - "]])]" - ) - ); - } - - #[Test] - public function webmethod_with_parameter_annotations() { - Assert::equals( - [ - 0 => ['webmethod' => ['verb' => 'GET', 'path' => '/greet/{name}']], - 1 => ['$name' => ['path' => null], '$greeting' => ['param' => null]] - ], - $this->parse('#[@webmethod(["verb" => "GET", "path" => "/greet/{name}"]), @$name: path, @$greeting: param]') - ); - } - - #[Test] - public function map_value_with_short_syntax() { - Assert::equals( - [0 => ['colors' => ['green' => '$10.50', 'red' => '$9.99']], 1 => []], - $this->parse("#[@colors(['green' => '$10.50', 'red' => '$9.99'])]") - ); - } - - #[Test] - public function short_array_syntax_as_value() { - Assert::equals( - [0 => ['permissions' => ['rn=login, rt=config', 'rn=admin, rt=config']], 1 => []], - $this->parse("#[@permissions(['rn=login, rt=config', 'rn=admin, rt=config'])]") - ); - } - - #[Test] - public function short_array_syntax_as_key() { - Assert::equals( - [0 => ['permissions' => ['names' => ['rn=login, rt=config', 'rn=admin, rt=config']]], 1 => []], - $this->parse("#[@permissions(['names' => ['rn=login, rt=config', 'rn=admin, rt=config']])]") - ); - } - - #[Test] - public function nested_short_array_syntax() { - Assert::equals( - [0 => ['values' => [[1, 1], [2, 2], [3, 3]]], 1 => []], - $this->parse("#[@values([[1, 1], [2, 2], [3, 3]])]") - ); - } - - #[Test] - public function nested_short_array_syntax_as_key() { - Assert::equals( - [0 => ['test' => ['values' => [[1, 1], [2, 2], [3, 3]]]], 1 => []], - $this->parse("#[@test(['values' => [[1, 1], [2, 2], [3, 3]]])]") - ); - } - - #[Test] - public function negative_and_positive_floats_inside_array() { - Assert::equals( - [0 => ['values' => [0.0, -1.5, +1.5]], 1 => []], - $this->parse("#[@values([0.0, -1.5, +1.5])]") - ); - } - - #[Test] - public function class_instance_value() { - Assert::equals( - [0 => ['value' => new Name('hello')], 1 => []], - $this->parse('#[@value(new Name("hello"))]') - ); - } - - #[Test] - public function imported_class_instance_value() { - Assert::equals( - [0 => ['value' => new Name('hello')], 1 => []], - $this->parse('#[@value(new Name("hello"))]', ['Name' => 'lang.unittest.Name']) - ); - } - - #[Test] - public function fully_qualified_class_instance_value() { - Assert::equals( - [0 => ['value' => new Name('hello')], 1 => []], - $this->parse('#[@value(new \lang\unittest\Name("hello"))]') - ); - } - - #[Test] - public function fully_qualified_not_loaded_class() { - $this->parse('#[@value(new \lang\unittest\NotLoaded())]'); - } - - - #[Test] - public function class_constant_via_self() { - Assert::equals( - [0 => ['value' => 'constant'], 1 => []], - $this->parse('#[@value(self::CONSTANT)]') - ); - } - - #[Test] - public function class_constant_via_parent() { - Assert::equals( - [0 => ['value' => 'constant'], 1 => []], - $this->parse('#[@value(parent::PARENTS_CONSTANT)]') - ); - } - - #[Test] - public function class_constant_via_classname() { - Assert::equals( - [0 => ['value' => 'constant'], 1 => []], - $this->parse('#[@value(AnnotationParsingTest::CONSTANT)]') - ); - } - - #[Test] - public function class_constant_via_ns_classname() { - Assert::equals( - [0 => ['value' => 'constant'], 1 => []], - $this->parse('#[@value(\lang\unittest\AnnotationParsingTest::CONSTANT)]') - ); - } - - #[Test] - public function class_constant_via_imported_classname() { - Assert::equals( - [0 => ['value' => 'namespaced'], 1 => []], - $this->parse('#[@value(Namespaced::CONSTANT)]') - ); - } - - #[Test] - public function class_constant_via_self_in_map() { - Assert::equals( - [0 => ['map' => ['key' => 'constant', 'value' => 'val']], 1 => []], - $this->parse('#[@map(["key" => self::CONSTANT, "value" => "val"])]') - ); - } - - #[Test] - public function class_constant_via_classname_in_map() { - Assert::equals( - [0 => ['map' => ['key' => 'constant', 'value' => 'val']], 1 => []], - $this->parse('#[@map(["key" => AnnotationParsingTest::CONSTANT, "value" => "val"])]') - ); - } - - #[Test] - public function class_constant_via_ns_classname_in_map() { - Assert::equals( - [0 => ['map' => ['key' => 'constant', 'value' => 'val']], 1 => []], - $this->parse('#[@map(["key" => \lang\unittest\AnnotationParsingTest::CONSTANT, "value" => "val"])]') - ); - } - - #[Test] - public function class_public_static_member() { - Assert::equals( - [0 => ['value' => 'exposed'], 1 => []], - $this->parse('#[@value(self::$exposed)]') - ); - } - - #[Test] - public function parent_public_static_member() { - Assert::equals( - [0 => ['value' => 'exposed'], 1 => []], - $this->parse('#[@value(parent::$parentsExposed)]') - ); - } - - #[Test] - public function class_protected_static_member() { - Assert::equals( - [0 => ['value' => 'hidden'], 1 => []], - $this->parse('#[@value(self::$hidden)]') - ); - } - - #[Test] - public function parent_protected_static_member() { - Assert::equals( - [0 => ['value' => 'hidden'], 1 => []], - $this->parse('#[@value(parent::$parentsHidden)]') - ); - } - - #[Test] - public function class_private_static_member() { - Assert::equals( - [0 => ['value' => 'internal'], 1 => []], - $this->parse('#[@value(self::$internal)]') - ); - } - - #[Test, Expect(class: ClassFormatException::class, message: '/Cannot access private static field .+AbstractAnnotationParsingTest::\$parentsInternal/')] - public function parent_private_static_member() { - $this->parse('#[@value(parent::$parentsInternal)]'); - } - - #[Test] - public function closure() { - $annotation= $this->parse('#[@value(function() { return true; })]'); - Assert::instance('Closure', $annotation[0]['value']); - } - - #[Test] - public function closures() { - $annotation= $this->parse('#[@values([ - function() { return true; }, - function() { return false; } - ])]'); - Assert::instance('Closure[]', $annotation[0]['values']); - } - - #[Test] - public function short_closure() { - $annotation= $this->parse('#[@value(fn() => true)]'); - Assert::instance('Closure', $annotation[0]['value']); - } - - #[Test, Values(['#[Value(fn() => new Name("Test"))]', '#[Value(function() { return new Name("Test"); })]',])] - public function imports_in_closures($closure) { - $annotation= $this->parse($closure, ['Name' => 'lang.unittest.Name']); - Assert::instance(Name::class, $annotation[0]['value']()); - } -} \ No newline at end of file diff --git a/src/test/php/lang/unittest/AttributeParsingTest.class.php b/src/test/php/lang/unittest/AttributeParsingTest.class.php index 2c112a2af..49e3b83a9 100755 --- a/src/test/php/lang/unittest/AttributeParsingTest.class.php +++ b/src/test/php/lang/unittest/AttributeParsingTest.class.php @@ -426,7 +426,7 @@ public function class_constant_via_parent() { public function class_constant_via_classname() { Assert::equals( [0 => ['value' => 'constant'], 1 => ['value' => 'lang.unittest.Value']], - $this->parse('#[Value(AnnotationParsingTest::CONSTANT)]') + $this->parse('#[Value(AttributeParsingTest::CONSTANT)]') ); } @@ -434,7 +434,7 @@ public function class_constant_via_classname() { public function class_constant_via_ns_classname() { Assert::equals( [0 => ['value' => 'constant'], 1 => ['value' => 'lang.unittest.Value']], - $this->parse('#[Value(\lang\unittest\AnnotationParsingTest::CONSTANT)]') + $this->parse('#[Value(\lang\unittest\AttributeParsingTest::CONSTANT)]') ); } @@ -458,7 +458,7 @@ public function class_constant_via_self_in_map() { public function class_constant_via_classname_in_map() { Assert::equals( [0 => ['map' => ['key' => 'constant', 'value' => 'val']], 1 => ['map' => 'lang.unittest.Map']], - $this->parse('#[Map(["key" => AnnotationParsingTest::CONSTANT, "value" => "val"])]') + $this->parse('#[Map(["key" => AttributeParsingTest::CONSTANT, "value" => "val"])]') ); } @@ -466,7 +466,7 @@ public function class_constant_via_classname_in_map() { public function class_constant_via_ns_classname_in_map() { Assert::equals( [0 => ['map' => ['key' => 'constant', 'value' => 'val']], 1 => ['map' => 'lang.unittest.Map']], - $this->parse('#[Map(["key" => \lang\unittest\AnnotationParsingTest::CONSTANT, "value" => "val"])]') + $this->parse('#[Map(["key" => \lang\unittest\AttributeParsingTest::CONSTANT, "value" => "val"])]') ); } diff --git a/src/test/php/lang/unittest/BrokenAnnotationTest.class.php b/src/test/php/lang/unittest/BrokenAnnotationTest.class.php deleted file mode 100755 index 88722f175..000000000 --- a/src/test/php/lang/unittest/BrokenAnnotationTest.class.php +++ /dev/null @@ -1,152 +0,0 @@ -parseAnnotations($input, nameof($this)); - } finally { - \xp::gc(); // Strip deprecation warning - } - } - - #[Test, Runtime(php: '<8.0'), Expect(class: ClassFormatException::class, message: '/Unterminated annotation/')] - public function no_ending_bracket() { - try { - XPClass::forName('lang.unittest.NoEndingBracket')->getAnnotations(); - } finally { - \xp::gc(); // Strip deprecation warning - } - } - - #[Test, Expect(class: ClassFormatException::class, message: '/Parse error/')] - public function unterminated_single_quoted_string_literal() { - $this->parse("#[@attribute('value)]"); - } - - #[Test, Expect(class: ClassFormatException::class, message: '/Parse error/')] - public function unterminated_double_quoted_string_literal() { - $this->parse('#[@attribute("value)]'); - } - - #[Test, Expect(class: ClassFormatException::class, message: '/Parse error: Unterminated string/')] - public function unterminated_dq_string() { - $this->parse('#[@ignore("Test)]'); - } - - #[Test, Expect(class: ClassFormatException::class, message: '/Parse error: Unterminated string/')] - public function unterminated_sq_string() { - $this->parse("#[@ignore('Test)]"); - } - - #[Test, Expect(class: ClassFormatException::class, message: '/Unterminated array/')] - public function unterminated_short_array() { - $this->parse('#[@ignore([1'); - } - - #[Test, Expect(class: ClassFormatException::class, message: '/Unterminated array/')] - public function unterminated_short_array_key() { - $this->parse('#[@ignore(["name" => [1'); - } - - #[Test, Expect(class: ClassFormatException::class, message: '/Malformed array/')] - public function malformed_short_array() { - $this->parse('#[@ignore([1 ,, 2])]'); - } - - #[Test, Expect(class: ClassFormatException::class, message: '/Malformed array/')] - public function malformed_short_array_no_commas() { - $this->parse('#[@ignore([1 2])]'); - } - - #[Test, Expect(class: ClassFormatException::class, message: '/Parse error: Expecting either "\(", "," or "\]"/')] - public function annotation_not_separated_by_commas() { - $this->parse("#[@test @throws('rdbms.SQLConnectException')]"); - } - - #[Test, Expect(class: ClassFormatException::class, message: '/Parse error: Expecting either "\(", "," or "\]"/')] - public function too_many_closing_braces() { - $this->parse("#[@throws('rdbms.SQLConnectException'))]"); - } - - #[Test, Expect(class: ClassFormatException::class, message: '/Undefined constant "editor"/')] - public function undefined_constant() { - $this->parse('#[@$editorId: param(editor)]'); - } - - #[Test, Expect(class: ClassFormatException::class, message: '/No such constant "EDITOR" in class/')] - public function undefined_class_constant() { - $this->parse('#[@$editorId: param(self::EDITOR)]'); - } - - #[Test, Expect(class: ClassFormatException::class, message: '/Class ".+" could not be found/')] - public function undefined_class_in_new() { - $this->parse('#[@$editorId: param(new NonExistantClass())]'); - } - - #[Test, Expect(class: ClassFormatException::class, message: '/Class ".+" could not be found/')] - public function undefined_class_in_constant() { - $this->parse('#[@$editorId: param(NonExistantClass::CONSTANT)]'); - } - - #[Test, Expect(class: ClassFormatException::class, message: '/No such field "EDITOR" in class/')] - public function undefined_class_member() { - $this->parse('#[@$editorId: param(self::$EDITOR)]'); - } - - #[Test, Expect(class: ClassFormatException::class, message: '/Cannot access protected static field .+AnnotationParsingTest::\$hidden/')] - public function class_protected_static_member() { - $this->parse('#[@value(AnnotationParsingTest::$hidden)]'); - } - - #[Test, Expect(class: ClassFormatException::class, message: '/Cannot access private static field .+AnnotationParsingTest::\$internal/')] - public function class_private_static_member() { - $this->parse('#[@value(AnnotationParsingTest::$internal)]'); - } - - #[Test, Expect(class: ClassFormatException::class, message: '/In `.+`: (Syntax error|Unmatched)/i')] - public function function_without_braces() { - $this->parse('#[@value(function)]'); - } - - #[Test, Expect(class: ClassFormatException::class, message: '/In `.+`: (Syntax error|Unmatched)/i')] - public function function_without_body() { - $this->parse('#[@value(function())]'); - } - - #[Test, Expect(class: ClassFormatException::class, message: '/In `.+`: (Syntax error|Unclosed)/i')] - public function function_without_closing_curly() { - $this->parse('#[@value(function() {)]'); - } - - #[Test, Expect(class: ClassFormatException::class, message: '/Parse error: Unexpected ","/')] - public function multi_value() { - $this->parse("#[@xmlmapping('hw_server', 'server')]"); - } - - #[Test, Expect(class: ClassFormatException::class, message: '/Parse error: Unexpected ","/')] - public function multi_value_without_whitespace() { - $this->parse("#[@xmlmapping('hw_server','server')]"); - } - - #[Test, Expect(class: ClassFormatException::class, message: '/Parse error: Unexpected ","/')] - public function multi_value_with_variable_types_backwards_compatibility() { - $this->parse("#[@xmlmapping('hw_server', TRUE)]"); - } - - #[Test, Expect(class: ClassFormatException::class, message: '/Parse error: Unexpected ","/')] - public function parsingContinuesAfterMultiValue() { - $this->parse("#[@xmlmapping('hw_server', 'server'), @restricted]"); - } -} \ No newline at end of file diff --git a/src/test/php/lang/unittest/MethodParametersTest.class.php b/src/test/php/lang/unittest/MethodParametersTest.class.php index c2675244b..6b506553c 100755 --- a/src/test/php/lang/unittest/MethodParametersTest.class.php +++ b/src/test/php/lang/unittest/MethodParametersTest.class.php @@ -258,7 +258,10 @@ public function accessing_a_parameter_via_non_existant_offset($offset) { /** @return lang.reflect.Parameter */ private function annotatedParameter() { try { - $p= $this->method("#[@\$param: test('value')]\npublic function fixture(\$param) { }")->getParameter(0); + $p= $this->method("public function fixture( + #[Test('value')] + \$param + ) { }")->getParameter(0); $p->getAnnotations(); return $p; } finally {