Skip to content

Commit

Permalink
PHP 8.3 Support: Dynamic class constant fetch (Part 3)
Browse files Browse the repository at this point in the history
- apache#6701
- https://wiki.php.net/rfc/dynamic_class_constant_fetch
- Fix the formatter
  - Add the option of the around spaces for the scope resolution operator (`::`)
  - Don't add spaces within braces (`{}`) e.g. {$example}
- Add unit tests
  • Loading branch information
junichi11 committed Nov 27, 2023
1 parent e002cff commit 45b6dda
Show file tree
Hide file tree
Showing 14 changed files with 520 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,10 @@ public boolean spaceAroundAssignOps() {
return preferences.getBoolean(SPACE_AROUND_ASSIGN_OPS, getDefaultAsBoolean(SPACE_AROUND_ASSIGN_OPS));
}

public boolean spaceAroundScopeResolutionOps() {
return preferences.getBoolean(SPACE_AROUND_SCOPE_RESOLUTION_OPS, getDefaultAsBoolean(SPACE_AROUND_SCOPE_RESOLUTION_OPS));
}

public boolean spaceAroundObjectOps() {
return preferences.getBoolean(SPACE_AROUND_OBJECT_OPS, getDefaultAsBoolean(SPACE_AROUND_OBJECT_OPS));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ public final class FmtOptions {
public static final String SPACE_AROUND_STRING_CONCAT_OPS = "spaceAroundStringConcatOps"; //NOI18N
public static final String SPACE_AROUND_ASSIGN_OPS = "spaceAroundAssignOps"; //NOI18N
public static final String SPACE_AROUND_KEY_VALUE_OPS = "spaceAroundKeyValueOps"; //NOI18N
public static final String SPACE_AROUND_SCOPE_RESOLUTION_OPS = "spaceAroundScopeResolutionOps"; //NOI18N
public static final String SPACE_AROUND_OBJECT_OPS = "spaceAroundObjectOps"; //NOI18N
public static final String SPACE_AROUND_NULLSAFE_OBJECT_OPS = "spaceAroundNullsafeObjectOps"; //NOI18N
public static final String SPACE_AROUND_DECLARE_EQUAL = "spaceAroundDeclareEqual"; //NOI18N
Expand Down Expand Up @@ -333,6 +334,7 @@ private static void createDefaults() {
{SPACE_AROUND_STRING_CONCAT_OPS, TRUE},
{SPACE_AROUND_KEY_VALUE_OPS, TRUE},
{SPACE_AROUND_ASSIGN_OPS, TRUE},
{SPACE_AROUND_SCOPE_RESOLUTION_OPS, FALSE},
{SPACE_AROUND_OBJECT_OPS, FALSE},
{SPACE_AROUND_NULLSAFE_OBJECT_OPS, FALSE},
{SPACE_AROUND_DECLARE_EQUAL, FALSE},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ public enum Kind {
WHITESPACE_AFTER_USE,
WHITESPACE_BEFORE_CLASS_LEFT_BRACE,
WHITESPACE_BEFORE_ANONYMOUS_CLASS_LEFT_BRACE,
WHITESPACE_AROUND_SCOPE_RESOLUTION_OP, // ::
WHITESPACE_AROUND_OBJECT_OP,
WHITESPACE_AROUND_NULLSAFE_OBJECT_OP,
WHITESPACE_AROUND_DECLARE_EQUAL,
Expand Down Expand Up @@ -125,6 +126,7 @@ public enum Kind {
WHITESPACE_WITHIN_ATTRIBUTE_BRACKETS,
WHITESPACE_WITHIN_ATTRIBUTE_DECL_PARENS,
WHITESPACE_WITHIN_TYPE_CAST_PARENS,
WHITESPACE_WITHIN_DYNAMIC_NAME_BRACES, // {$example}
WHITESPACE_BEFORE_COMMA,
WHITESPACE_AFTER_COMMA,
WHITESPACE_BEFORE_SEMI,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@
import org.netbeans.modules.php.editor.parser.astnodes.NamespaceDeclaration;
import org.netbeans.modules.php.editor.parser.astnodes.NullableType;
import org.netbeans.modules.php.editor.parser.astnodes.Program;
import org.netbeans.modules.php.editor.parser.astnodes.ReflectionVariable;
import org.netbeans.modules.php.editor.parser.astnodes.ReturnStatement;
import org.netbeans.modules.php.editor.parser.astnodes.SingleFieldDeclaration;
import org.netbeans.modules.php.editor.parser.astnodes.SingleUseStatementPart;
Expand Down Expand Up @@ -2505,6 +2506,26 @@ public void visit(IntersectionType node) {
processUnionOrIntersectionType(node.getTypes());
}

@Override
public void visit(ReflectionVariable node) {
// e.g. {$name}
while (moveNext() && ts.offset() < node.getName().getStartOffset()) {
addFormatToken(formatTokens);
if (ts.token().id() == PHPTokenId.PHP_CURLY_OPEN) {
formatTokens.add(new FormatToken(FormatToken.Kind.WHITESPACE_WITHIN_DYNAMIC_NAME_BRACES, ts.offset() + ts.token().length()));
}
}
ts.movePrevious();
scan(node.getName());
while (moveNext() && ts.offset() < node.getEndOffset()) {
if (ts.token().id() == PHPTokenId.PHP_CURLY_CLOSE) {
formatTokens.add(new FormatToken(FormatToken.Kind.WHITESPACE_WITHIN_DYNAMIC_NAME_BRACES, ts.offset()));
}
addFormatToken(formatTokens);
}
ts.movePrevious();
}

private void processUnionOrIntersectionType(List<Expression> types) {
assert !types.isEmpty();
final Expression lastType = types.get(types.size() - 1);
Expand Down Expand Up @@ -2604,6 +2625,11 @@ private void addFormatToken(List<FormatToken> tokens) {
case PHPDOC_COMMENT_END:
tokens.add(new FormatToken(FormatToken.Kind.DOC_COMMENT_END, ts.offset(), ts.token().text().toString()));
break;
case PHP_PAAMAYIM_NEKUDOTAYIM:
tokens.add(new FormatToken(FormatToken.Kind.WHITESPACE_AROUND_SCOPE_RESOLUTION_OP, ts.offset()));
tokens.add(new FormatToken(FormatToken.Kind.TEXT, ts.offset(), ts.token().text().toString()));
tokens.add(new FormatToken(FormatToken.Kind.WHITESPACE_AROUND_SCOPE_RESOLUTION_OP, ts.offset() + ts.token().length()));
break;
case PHP_OBJECT_OPERATOR:
tokens.add(new FormatToken(FormatToken.Kind.WHITESPACE_AROUND_OBJECT_OP, ts.offset()));
tokens.add(new FormatToken(FormatToken.Kind.TEXT, ts.offset(), ts.token().text().toString()));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ protected static class DocumentOptions {
public boolean spaceBeforeElse;
public boolean spaceBeforeCatch;
public boolean spaceBeforeFinally;
public boolean spaceAroundScopeResolutionOp;
public boolean spaceAroundObjectOp;
public boolean spaceAroundNullsafeObjectOp;
public boolean spaceAroundDeclareEqual;
Expand Down Expand Up @@ -274,6 +275,7 @@ public DocumentOptions(BaseDocument doc) {
spaceBeforeCatch = codeStyle.spaceBeforeCatch();
spaceBeforeFinally = codeStyle.spaceBeforeFinally();

spaceAroundScopeResolutionOp = codeStyle.spaceAroundScopeResolutionOps();
spaceAroundObjectOp = codeStyle.spaceAroundObjectOps();
spaceAroundNullsafeObjectOp = codeStyle.spaceAroundNullsafeObjectOps();
spaceAroundDeclareEqual = codeStyle.spaceAroundDeclareEqual();
Expand Down Expand Up @@ -1077,6 +1079,9 @@ public void run() {
}
}
break;
case WHITESPACE_AROUND_SCOPE_RESOLUTION_OP:
countSpaces = docOptions.spaceAroundScopeResolutionOp ? 1 : 0;
break;
case WHITESPACE_AROUND_OBJECT_OP:
countSpaces = docOptions.spaceAroundObjectOp ? 1 : 0;
break;
Expand Down Expand Up @@ -1550,6 +1555,10 @@ && countOfNewLines(formatTokens.get(index + 1).getOldText()) > 0) {
case WHITESPACE_WITHIN_TYPE_CAST_PARENS:
countSpaces = docOptions.spaceWithinTypeCastParens ? 1 : 0;
break;
case WHITESPACE_WITHIN_DYNAMIC_NAME_BRACES:
// change here if we add the option for it
countSpaces = 0;
break;
case WHITESPACE_AFTER_TYPE_CAST:
countSpaces = docOptions.spaceAfterTypeCast ? 1 : 0;
break;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,9 @@ LBL_spaceAroundTernaryOps=Ternary Operators
LBL_spaceAroundCoalescingOps=Coalescing Operators
LBL_spaceAroundAssignOps=Assignment Operators
LBL_spaceAroundKeyValueOps=Key => Value Operator
LBL_spaceAroundObjectOps=Object Operator
LBL_spaceAroundNullsafeObjectOps=Nullsafe Object Operator
LBL_spaceAroundScopeResolutionOps=Scope Resolution Operator (::)
LBL_spaceAroundObjectOps=Object Operator (->)
LBL_spaceAroundNullsafeObjectOps=Nullsafe Object Operator (?->)
LBL_spaceAroundStringConcatOps=String Concatenation Operator
LBL_spaceAroundDeclareEqual="=" in Declare Statement
LBL_spaceAroundUnionTypeSeparator="|" in Union Type
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,7 @@ private DefaultTreeModel createModel() {
new Item(SPACE_AROUND_STRING_CONCAT_OPS),
new Item(SPACE_AROUND_KEY_VALUE_OPS),
new Item(SPACE_AROUND_ASSIGN_OPS),
new Item(SPACE_AROUND_SCOPE_RESOLUTION_OPS),
new Item(SPACE_AROUND_OBJECT_OPS),
new Item(SPACE_AROUND_NULLSAFE_OBJECT_OPS),
new Item(SPACE_AROUND_DECLARE_EQUAL),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
<?php
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
class Test {
public const TEST = 'test';
public const TES = 'TES';
public const T = 'T';
public const TEST1 = self :: { 'TEST' };
public const TEST2 = self ::{ 'TES' . 'T'} ;
public const TEST3 = self :: {self::TES . self::T};

public static function method(): void {}
}

enum EnumTest: string {
case TEST = 'test';
case TES = 'TES';
case T = 'T';
case TEST1 = self::{'TEST'};
case TEST2 = self ::{'TES' . 'T'} ;
case TEST3 = self::{ self:: TES . self::T };
}

function test(string $param = "test"): void {
}

$variable = 'TEST';
$e = 'e';
$test = "Test";

Test :: test () ;

Test :: {"TEST"};
$test :: {"TEST"};
Test :: {$variable};
$test:: { $variable };
Test::{$variable . $e };
$test::{ $variable . $e};
Test:: { strtoupper("test")};
$test::{ strtoupper("test")};
Test::{'$variablee' };
$test :: {'$variablee'};
Test :: { strtolower("CLASS")};
$test :: { strtolower("CLASS")};
Test::{ 1000 };
$test :: {1000};
EnumTest ::{[]};
$test::{ [ ] };

Test :: { Test::method() }::{ test($variable) };
Test::{ test ('test1') } :: { test( 'test2' ) };

$test = new Test();
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
<?php

/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

class Test {

public const TEST = 'test';
public const TES = 'TES';
public const T = 'T';
public const TEST1 = self::{'TEST'};
public const TEST2 = self::{'TES' . 'T'};
public const TEST3 = self::{self::TES . self::T};

public static function method(): void {

}
}

enum EnumTest: string {

case TEST = 'test';
case TES = 'TES';
case T = 'T';
case TEST1 = self::{'TEST'};
case TEST2 = self::{'TES' . 'T'};
case TEST3 = self::{self::TES . self::T};
}

function test(string $param = "test"): void {

}

$variable = 'TEST';
$e = 'e';
$test = "Test";

Test::test();

Test::{"TEST"};
$test::{"TEST"};
Test::{$variable};
$test::{$variable};
Test::{$variable . $e};
$test::{$variable . $e};
Test::{strtoupper("test")};
$test::{strtoupper("test")};
Test::{'$variablee'};
$test::{'$variablee'};
Test::{strtolower("CLASS")};
$test::{strtolower("CLASS")};
Test::{1000};
$test::{1000};
EnumTest::{[]};
$test::{[]};

Test::{Test::method()}::{test($variable)};
Test::{test('test1')}::{test('test2')};

$test = new Test();
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
<?php

/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

class Test {

public const TEST = 'test';
public const TES = 'TES';
public const T = 'T';
public const TEST1 = self :: {'TEST'};
public const TEST2 = self :: {'TES' . 'T'};
public const TEST3 = self :: {self :: TES . self :: T};

public static function method(): void {

}
}

enum EnumTest: string {

case TEST = 'test';
case TES = 'TES';
case T = 'T';
case TEST1 = self :: {'TEST'};
case TEST2 = self :: {'TES' . 'T'};
case TEST3 = self :: {self :: TES . self :: T};
}

function test(string $param = "test"): void {

}

$variable = 'TEST';
$e = 'e';
$test = "Test";

Test :: test();

Test :: {"TEST"};
$test :: {"TEST"};
Test :: {$variable};
$test :: {$variable};
Test :: {$variable . $e};
$test :: {$variable . $e};
Test :: {strtoupper("test")};
$test :: {strtoupper("test")};
Test :: {'$variablee'};
$test :: {'$variablee'};
Test :: {strtolower("CLASS")};
$test :: {strtolower("CLASS")};
Test :: {1000};
$test :: {1000};
EnumTest :: {[]};
$test :: {[]};

Test :: {Test :: method()} :: {test($variable)};
Test :: {test('test1')} :: {test('test2')};

$test = new Test();
Loading

0 comments on commit 45b6dda

Please sign in to comment.