Skip to content

Commit 100a81c

Browse files
committed
Added support for PHP 7.4 typed properties (ref #2413)
1 parent 12b8772 commit 100a81c

File tree

6 files changed

+330
-32
lines changed

6 files changed

+330
-32
lines changed

package.xml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,14 @@ http://pear.php.net/dtd/package-2.0.xsd">
2626
</stability>
2727
<license uri="https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt">BSD 3-Clause License</license>
2828
<notes>
29+
- Added support for PHP 7.4 typed properties
30+
-- The nullable operator is now tokenized as T_NULLABLE inside property types, as it is elsewhere
31+
-- To get the type of a member var, use the File::getMemberProperties() method, which now contains a "type" array index
32+
--- This contains the type of the member var, or a blank string if not specified
33+
--- If the type is nullable, the return type will contain the leading ?
34+
--- If a type is specified, the position of the type token will be set in a "type_token" array index
35+
--- If the type is nullable, a "nullable_type" array index will also be set to TRUE
36+
--- If the type contains namespace information, it will be cleaned of whitespace and comments in the return value
2937
- The PSR1 standard now correctly bans alternate PHP tags
3038
-- Previously, it only banned short open tags and not the pre-7.0 alternate tags
3139
- Added support for only checking files that have been locally staged in a git repo

src/Files/File.php

Lines changed: 62 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1268,7 +1268,7 @@ public function getDeclarationName($stackPtr)
12681268
* 'type_hint' => string, // The type hint for the variable.
12691269
* 'type_hint_token' => integer, // The stack pointer to the type hint
12701270
* // or false if there is no type hint.
1271-
* 'nullable_type' => boolean, // Is the variable using a nullable type?
1271+
* 'nullable_type' => boolean, // TRUE if the var type is nullable.
12721272
* )
12731273
* </code>
12741274
*
@@ -1451,19 +1451,19 @@ public function getMethodParameters($stackPtr)
14511451
/**
14521452
* Returns the visibility and implementation properties of a method.
14531453
*
1454-
* The format of the array is:
1454+
* The format of the return value is:
14551455
* <code>
14561456
* array(
1457-
* 'scope' => 'public', // public protected or protected
1458-
* 'scope_specified' => true, // true is scope keyword was found.
1459-
* 'return_type' => '', // the return type of the method.
1457+
* 'scope' => 'public', // Public, private, or protected
1458+
* 'scope_specified' => true, // TRUE if the scope keyword was found.
1459+
* 'return_type' => '', // The return type of the method.
14601460
* 'return_type_token' => integer, // The stack pointer to the start of the return type
1461-
* // or false if there is no return type.
1462-
* 'nullable_return_type' => false, // true if the return type is nullable.
1463-
* 'is_abstract' => false, // true if the abstract keyword was found.
1464-
* 'is_final' => false, // true if the final keyword was found.
1465-
* 'is_static' => false, // true if the static keyword was found.
1466-
* 'has_body' => false, // true if the method has a body
1461+
* // or FALSE if there is no return type.
1462+
* 'nullable_return_type' => false, // TRUE if the return type is nullable.
1463+
* 'is_abstract' => false, // TRUE if the abstract keyword was found.
1464+
* 'is_final' => false, // TRUE if the final keyword was found.
1465+
* 'is_static' => false, // TRUE if the static keyword was found.
1466+
* 'has_body' => false, // TRUE if the method has a body
14671467
* );
14681468
* </code>
14691469
*
@@ -1603,16 +1603,19 @@ public function getMethodProperties($stackPtr)
16031603

16041604

16051605
/**
1606-
* Returns the visibility and implementation properties of the class member
1607-
* variable found at the specified position in the stack.
1606+
* Returns the visibility and implementation properties of a class member var.
16081607
*
1609-
* The format of the array is:
1608+
* The format of the return value is:
16101609
*
16111610
* <code>
16121611
* array(
1613-
* 'scope' => 'public', // public protected or protected.
1614-
* 'scope_specified' => false, // true if the scope was explicitly specified.
1615-
* 'is_static' => false, // true if the static keyword was found.
1612+
* 'scope' => string, // Public, private, or protected.
1613+
* 'scope_specified' => boolean, // TRUE if the scope was explicitly specified.
1614+
* 'is_static' => boolean, // TRUE if the static keyword was found.
1615+
* 'type' => string, // The type of the var (empty if no type specifed).
1616+
* 'type_token' => integer, // The stack pointer to the start of the type
1617+
* // or FALSE if there is no type.
1618+
* 'nullable_type' => boolean, // TRUE if the type is nullable.
16161619
* );
16171620
* </code>
16181621
*
@@ -1715,10 +1718,51 @@ public function getMemberProperties($stackPtr)
17151718
}
17161719
}//end for
17171720

1721+
$type = '';
1722+
$typeToken = false;
1723+
$nullableType = false;
1724+
1725+
if ($i < $stackPtr) {
1726+
// We've found a type.
1727+
$valid = [
1728+
T_STRING => T_STRING,
1729+
T_CALLABLE => T_CALLABLE,
1730+
T_SELF => T_SELF,
1731+
T_PARENT => T_PARENT,
1732+
T_NS_SEPARATOR => T_NS_SEPARATOR,
1733+
];
1734+
1735+
for ($i; $i < $stackPtr; $i++) {
1736+
if ($this->tokens[$i]['code'] === T_VARIABLE) {
1737+
// Hit another variable in a group definition.
1738+
break;
1739+
}
1740+
1741+
if ($this->tokens[$i]['code'] === T_NULLABLE) {
1742+
$nullableType = true;
1743+
}
1744+
1745+
if (isset($valid[$this->tokens[$i]['code']]) === true) {
1746+
if ($typeToken === false) {
1747+
$typeToken = $i;
1748+
}
1749+
1750+
$type .= $this->tokens[$i]['content'];
1751+
}
1752+
}
1753+
1754+
if ($type !== '' && $nullableType === true) {
1755+
$type = '?'.$type;
1756+
}
1757+
}//end if
1758+
17181759
return [
17191760
'scope' => $scope,
17201761
'scope_specified' => $scopeSpecified,
17211762
'is_static' => $isStatic,
1763+
'type' => $type,
1764+
'type_token' => $typeToken,
1765+
'nullable_type' => $nullableType,
17221766
];
17231767

17241768
}//end getMemberProperties()
@@ -1727,7 +1771,7 @@ public function getMemberProperties($stackPtr)
17271771
/**
17281772
* Returns the visibility and implementation properties of a class.
17291773
*
1730-
* The format of the array is:
1774+
* The format of the return value is:
17311775
* <code>
17321776
* array(
17331777
* 'is_abstract' => false, // true if the abstract keyword was found.

src/Standards/Squiz/Tests/PHP/DisallowInlineIfUnitTest.inc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,7 @@ if ($var1) {
88
$var1 ? $var2 = 0 : $var2 = 1;
99

1010
function foo(string $bar, array $baz, ?MyClass $object) : MyClass {}
11+
12+
class Example {
13+
public ?int $scalarType;
14+
}

src/Tokenizers/PHP.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -999,7 +999,9 @@ protected function tokenize($string)
999999
$prevNonEmpty = $tokenType;
10001000
}
10011001

1002-
if ($tokenType === T_FUNCTION) {
1002+
if ($tokenType === T_FUNCTION
1003+
|| isset(Util\Tokens::$methodPrefixes[$tokenType]) === true
1004+
) {
10031005
$newToken['code'] = T_NULLABLE;
10041006
$newToken['type'] = 'T_NULLABLE';
10051007
break;

tests/Core/File/GetMemberPropertiesTest.inc

Lines changed: 58 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,33 @@ class TestMemberProperties
55
/* testVar */
66
var $varA = true;
77

8+
/* testVarType */
9+
var int $varA = true;
10+
811
/* testPublic */
912
public $varB = true;
1013

14+
/* testPublicType */
15+
public string $varB = true;
16+
1117
/* testProtected */
1218
protected $varC = true;
1319

20+
/* testProtectedType */
21+
protected bool $varC = true;
22+
1423
/* testPrivate */
1524
private $varD = true;
1625

26+
/* testPrivateType */
27+
private array $varD = true;
28+
1729
/* testStatic */
1830
static $varE = true;
1931

32+
/* testStaticType */
33+
static string $varE = true;
34+
2035
/* testStaticVar */
2136
static var $varF = true;
2237

@@ -62,6 +77,17 @@ class TestMemberProperties
6277
*/
6378
private static $varJ = true;
6479

80+
public float
81+
/* testGroupType 1 */
82+
$x,
83+
/* testGroupType 2 */
84+
$y;
85+
86+
public static ?string
87+
/* testGroupNullableType 1 */
88+
$x = null,
89+
/* testGroupNullableType 2 */
90+
$y = null;
6591

6692
protected static
6793
/* testGroupProtectedStatic 1 */
@@ -84,9 +110,9 @@ class TestMemberProperties
84110
$varS = ONE / self::THREE,
85111
/* testGroupPrivate 6 */
86112
$varT = [
87-
'a' => 'a',
88-
'b' => 'b'
89-
],
113+
'a' => 'a',
114+
'b' => 'b'
115+
],
90116
/* testGroupPrivate 7 */
91117
$varU = __DIR__ . "/base";
92118

@@ -103,6 +129,25 @@ class TestMemberProperties
103129
/* testPropertyAfterMethod */
104130
private static $varV = true;
105131

132+
/* testMessyNullableType */
133+
public /* comment
134+
*/ ? //comment
135+
array $foo = [];
136+
137+
/* testNamespaceType */
138+
public \MyNamespace\MyClass $foo;
139+
140+
/* testNullableNamespaceType 1 */
141+
private ?ClassName $nullableClassType;
142+
143+
/* testNullableNamespaceType 2 */
144+
protected ?Folder\ClassName $nullableClassType2;
145+
146+
/* testMultilineNamespaceType */
147+
public \MyNamespace /** comment *\/ comment */
148+
\MyClass /* comment */
149+
\Foo $foo;
150+
106151
}
107152

108153
interface Base
@@ -118,19 +163,19 @@ $globalVariable = true;
118163
return;
119164

120165
$a = ( $foo == $bar ? new stdClass() :
121-
new class() {
122-
/* testNestedProperty 1 */
123-
public $var = true;
166+
new class() {
167+
/* testNestedProperty 1 */
168+
public $var = true;
124169

125-
/* testNestedMethodParam 1 */
126-
public function something($var = false) {}
127-
}
170+
/* testNestedMethodParam 1 */
171+
public function something($var = false) {}
172+
}
128173
);
129174

130175
function_call( 'param', new class {
131-
/* testNestedProperty 2 */
132-
public $year = 2017;
176+
/* testNestedProperty 2 */
177+
public $year = 2017;
133178

134-
/* testNestedMethodParam 2 */
135-
public function __construct( $open, $post_id ) {}
179+
/* testNestedMethodParam 2 */
180+
public function __construct( $open, $post_id ) {}
136181
}, 10, 2 );

0 commit comments

Comments
 (0)