Skip to content

Commit 8f78985

Browse files
authored
Do not restrict @internal usages of anonymous class members (#6235)
1 parent 8e60b1a commit 8f78985

7 files changed

Lines changed: 258 additions & 0 deletions

‎src/Rules/InternalTag/RestrictedInternalUsageHelper.php‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@ final class RestrictedInternalUsageHelper
1616

1717
public function shouldClassBeReported(Scope $scope, ClassReflection $classReflection): bool
1818
{
19+
if ($classReflection->isAnonymous()) {
20+
return false;
21+
}
22+
1923
return $this->shouldBeReported($scope, $classReflection->getName());
2024
}
2125

‎tests/PHPStan/Rules/InternalTag/RestrictedInternalClassConstantUsageExtensionTest.php‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,4 +87,14 @@ public function testNoNamespace(): void
8787
]);
8888
}
8989

90+
public function testBug13042(): void
91+
{
92+
$this->analyse([__DIR__ . '/data/bug-13042.php'], [
93+
[
94+
'Access to internal constant Bug13042\Foo::INTERNAL from outside its root namespace Bug13042.',
95+
143,
96+
],
97+
]);
98+
}
99+
90100
}

‎tests/PHPStan/Rules/InternalTag/RestrictedInternalMethodUsageExtensionTest.php‎

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,4 +66,22 @@ public function testNoNamespace(): void
6666
]);
6767
}
6868

69+
public function testBug13042(): void
70+
{
71+
$this->analyse([__DIR__ . '/data/bug-13042.php'], [
72+
[
73+
'Call to internal method Bug13042\Foo::doInternal() from outside its root namespace Bug13042.',
74+
144,
75+
],
76+
[
77+
'Call to internal method Bug13042\Foo::doInternal() from outside its root namespace Bug13042.',
78+
163,
79+
],
80+
[
81+
'Call to internal method Bug13042\Foo::doInternal() from outside its root namespace Bug13042.',
82+
182,
83+
],
84+
]);
85+
}
86+
6987
}

‎tests/PHPStan/Rules/InternalTag/RestrictedInternalPropertyUsageExtensionTest.php‎

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,4 +56,22 @@ public function testRule(): void
5656
]);
5757
}
5858

59+
public function testBug13042(): void
60+
{
61+
$this->analyse([__DIR__ . '/data/bug-13042.php'], [
62+
[
63+
'Access to internal property Bug13042\Foo::$internal from outside its root namespace Bug13042.',
64+
141,
65+
],
66+
[
67+
'Access to internal property Bug13042\Foo::$internal from outside its root namespace Bug13042.',
68+
160,
69+
],
70+
[
71+
'Access to internal property Bug13042\Foo::$internal from outside its root namespace Bug13042.',
72+
179,
73+
],
74+
]);
75+
}
76+
5977
}

‎tests/PHPStan/Rules/InternalTag/RestrictedInternalStaticMethodUsageExtensionTest.php‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,4 +81,14 @@ public function testBug13210(): void
8181
$this->analyse([__DIR__ . '/data/bug-13210.php'], []);
8282
}
8383

84+
public function testBug13042(): void
85+
{
86+
$this->analyse([__DIR__ . '/data/bug-13042.php'], [
87+
[
88+
'Call to internal static method Bug13042\Foo::doInternalStatic() from outside its root namespace Bug13042.',
89+
145,
90+
],
91+
]);
92+
}
93+
8494
}

‎tests/PHPStan/Rules/InternalTag/RestrictedInternalStaticPropertyUsageExtensionTest.php‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,4 +66,14 @@ public function testStaticPropertyAccessOnInternalSubclass(): void
6666
]);
6767
}
6868

69+
public function testBug13042(): void
70+
{
71+
$this->analyse([__DIR__ . '/data/bug-13042.php'], [
72+
[
73+
'Access to internal static property Bug13042\Foo::$internalStatic from outside its root namespace Bug13042.',
74+
142,
75+
],
76+
]);
77+
}
78+
6979
}
Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
<?php
2+
3+
namespace Bug13042 {
4+
5+
trait SomeTrait
6+
{
7+
8+
/** @internal don't use it directly */
9+
private ?string $text = null;
10+
11+
/** @internal */
12+
private static ?string $staticText = null;
13+
14+
/** @internal */
15+
private function internalMethod(): void
16+
{
17+
}
18+
19+
/** @internal */
20+
private static function internalStaticMethod(): void
21+
{
22+
}
23+
24+
public function setText(string $text): void
25+
{
26+
$this->text = $text;
27+
self::$staticText = $text;
28+
$this->internalMethod();
29+
self::internalStaticMethod();
30+
}
31+
32+
}
33+
34+
class HelloWorld
35+
{
36+
37+
public function sayHello(): object
38+
{
39+
return new class {
40+
41+
use SomeTrait;
42+
43+
/** @internal */
44+
public $ownProperty;
45+
46+
/** @internal */
47+
public static $ownStaticProperty;
48+
49+
/** @internal */
50+
public const OWN_CONSTANT = 'x';
51+
52+
/** @internal */
53+
public function ownMethod(): void
54+
{
55+
}
56+
57+
/** @internal */
58+
public static function ownStaticMethod(): void
59+
{
60+
}
61+
62+
public function doFoo(): void
63+
{
64+
$this->ownProperty;
65+
self::$ownStaticProperty;
66+
self::OWN_CONSTANT;
67+
$this->ownMethod();
68+
self::ownStaticMethod();
69+
}
70+
71+
};
72+
}
73+
74+
public function fromTheOutside(): void
75+
{
76+
$foo = new class {
77+
78+
/** @internal */
79+
public $internal;
80+
81+
/** @internal */
82+
public static $internalStatic;
83+
84+
/** @internal */
85+
const INTERNAL = 'x';
86+
87+
/** @internal */
88+
public function doInternal(): void
89+
{
90+
}
91+
92+
/** @internal */
93+
public static function doInternalStatic(): void
94+
{
95+
}
96+
97+
};
98+
99+
$foo->internal;
100+
$foo::$internalStatic;
101+
$foo::INTERNAL;
102+
$foo->doInternal();
103+
$foo::doInternalStatic();
104+
}
105+
106+
}
107+
108+
class Foo
109+
{
110+
111+
/** @internal */
112+
public $internal;
113+
114+
/** @internal */
115+
public static $internalStatic;
116+
117+
/** @internal */
118+
const INTERNAL = 'x';
119+
120+
/** @internal */
121+
public function doInternal(): void
122+
{
123+
}
124+
125+
/** @internal */
126+
public static function doInternalStatic(): void
127+
{
128+
}
129+
130+
}
131+
132+
}
133+
134+
namespace Bug13042Other {
135+
136+
function (): object {
137+
return new class {
138+
139+
public function doFoo(\Bug13042\Foo $foo): void
140+
{
141+
$foo->internal;
142+
$foo::$internalStatic;
143+
$foo::INTERNAL;
144+
$foo->doInternal();
145+
$foo::doInternalStatic();
146+
}
147+
148+
};
149+
};
150+
151+
}
152+
153+
namespace Bug13042Inheritance {
154+
155+
function (): object {
156+
return new class extends \Bug13042\Foo {
157+
158+
public function doFoo(): void
159+
{
160+
$this->internal;
161+
self::$internalStatic;
162+
self::INTERNAL;
163+
$this->doInternal();
164+
self::doInternalStatic();
165+
}
166+
167+
};
168+
};
169+
170+
// A named subclass gets the very same exemptions: the static access rules re-ask
171+
// the extension with the referenced class instead of the declaring one, so only
172+
// the instance accesses below are reported. The anonymous subclass above must not
173+
// be reported any more strictly than this one.
174+
class NamedSubclass extends \Bug13042\Foo
175+
{
176+
177+
public function doFoo(): void
178+
{
179+
$this->internal;
180+
self::$internalStatic;
181+
self::INTERNAL;
182+
$this->doInternal();
183+
self::doInternalStatic();
184+
}
185+
186+
}
187+
188+
}

0 commit comments

Comments
 (0)