Skip to content

Commit f3aa678

Browse files
committed
add short classes
1 parent d13508f commit f3aa678

2 files changed

Lines changed: 279 additions & 4 deletions

File tree

drafts/short-class.md

Lines changed: 274 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,274 @@
1+
# PHP RFC: Short and Inner Classes
2+
3+
* Version: 0.1
4+
* Date: 2025-02-08
5+
* Author: Rob Landers, rob@bottled.codes
6+
* Status: Draft (or Under Discussion or Accepted or Declined)
7+
* First Published at: <http://wiki.php.net/rfc/short-class>
8+
9+
## Introduction
10+
11+
This RFC proposes a new short syntax for class definitions in PHP and the ability to embed these classes within other
12+
classes.
13+
14+
## Proposal
15+
16+
Data transfer objects (DTOs) are a common pattern in PHP applications and are usually simple data structures that hold
17+
data and have no behavior.
18+
With this RFC,
19+
we propose a simple and concise syntax for defining these classes, looking almost like named anonymous classes, as well as
20+
the ability to embed them within other classes.
21+
22+
### Short Class Syntax
23+
24+
The proposed syntax for a short class definition is as follows: a keyword `class`,
25+
followed by the class name, then a list of public properties enclosed in parentheses.
26+
Optionally, a list of traits, interfaces, and a parent class may be defined.
27+
28+
```php
29+
class Point(int $x, int $y);
30+
```
31+
32+
This is equivalent to the following full class definition:
33+
34+
```php
35+
class Point {
36+
public function __construct(public int $x, public int $y) {}
37+
}
38+
```
39+
40+
Any properties defined within the parenthesis are defined as a public property of the class.
41+
42+
#### Default Values
43+
44+
Default values may be provided for properties:
45+
46+
```php
47+
class Point(int $x = 0, int $y = 0);
48+
```
49+
50+
#### Inheritance and Behavior
51+
52+
With class short syntax, no behavior may be defined, yet it can still utilize traits, interfaces, and other classes.
53+
54+
```php
55+
class Point(int $x, int $y) extends BasePoint implements JsonSerializable use PointTrait, Evolvable;
56+
```
57+
58+
Note that the original constructor from any parent class is overridden and not called by the short syntax.
59+
60+
#### Empty Classes
61+
62+
Short classes may also be empty:
63+
64+
```php
65+
class Point() extends BasePoint use PointTrait;
66+
```
67+
68+
#### Attributes
69+
70+
Attributes may also be used with short classes:
71+
72+
```php
73+
class Password(#[SensitiveParameter] string $password);
74+
```
75+
76+
### Inner Classes
77+
78+
Inner classes are classes that are defined within another class.
79+
80+
```php
81+
class Foo {
82+
class Bar(public string $message);
83+
84+
private class Baz {
85+
public function __construct(public string $message) {}
86+
}
87+
}
88+
89+
$foo = new Foo::Bar('Hello, world!');
90+
echo $foo->message;
91+
// outputs: Hello, world!
92+
$baz = new Foo::Baz('Hello, world!');
93+
// Fatal error: Uncaught Error: Cannot access private class Foo::Baz
94+
```
95+
96+
Inner classes have scope similar to properties, which applies to parameters and returns types as well.
97+
98+
#### Modifiers
99+
100+
Properties support modifiers such as `public`, `protected`, and `private` as well as `static`, `final` and `readonly`.
101+
When using these as modifiers on an inner class, there are some intuitive rules:
102+
103+
- `public`, `private`, and `protected` apply to the visibility of the class.
104+
- `static`, `final`, and `readonly` apply to the class itself.
105+
106+
Thus, an inner class with the modifier `private readonly` is only accessible within the class
107+
and any instances are readonly.
108+
109+
#### Visibility
110+
111+
A `private` or `protected` inner class is only accessible within the class it is defined in
112+
(or its subclasses in the case of protected classes).
113+
This also applies to methods and return types.
114+
115+
```php
116+
class Foo {
117+
private class Bar(public string $message);
118+
119+
// Fatal error: Uncaught Error: Cannot return private class Foo::Bar
120+
public function getMessage(): Bar {
121+
return new Bar('Hello, world!');
122+
}
123+
}
124+
```
125+
126+
#### Accessing Inner Classes
127+
128+
From outside the class, public inner classes may be accessed using the `::` operator:
129+
130+
```php
131+
new Foo::Bar('Hello, world!');
132+
```
133+
134+
This may also be used from inside the class or in subclasses at the developer’s discretion. Alternatively, inner classes
135+
may be accessed using `self::` or `static::` from inside the class, or just using the name itself:
136+
137+
```php
138+
139+
private function getBar(): Foo::Bar {
140+
$a = new Bar('Hello, world!');
141+
$b = new self::Bar('Hello, world!');
142+
$c = new static::Bar('Hello, world!');
143+
$d = new Foo::Bar('Hello, world!');
144+
}
145+
146+
```
147+
148+
Note that inner classes effectively "shadow" outer classes of the same name:
149+
150+
```php
151+
readonly class Vect(int $x, int $y);
152+
153+
class Foo {
154+
class Vect(int $x, int $y, int $z);
155+
156+
// Vect is Foo::Vect not \Vect
157+
public function __construct(public Vect $vect) {}
158+
}
159+
```
160+
161+
#### Names
162+
163+
Inner classes may not have any name that conflicts with a constant or static method of the same name.
164+
165+
```php
166+
class Foo {
167+
const Bar = 'bar';
168+
class Bar(public string $message);
169+
170+
// Fatal error: Uncaught Error: Cannot redeclare Foo::Bar
171+
}
172+
173+
class Foo {
174+
static function Bar() {}
175+
class Bar(public string $message);
176+
177+
// Fatal error: Uncaught Error: Cannot redeclare Foo::Bar
178+
}
179+
```
180+
181+
These rules are to prevent developer confusion because these instantiations all look similar,
182+
but without this rule, they would all work:
183+
184+
```php
185+
new (Foo::Bar); // create a new class from the name stored in Foo::Bar
186+
new (Foo::Bar()); // create a new instance from the name returned by Foo::Bar()
187+
new Foo::Bar(); // create a new instance of the class Foo::Bar
188+
```
189+
190+
## Backward Incompatible Changes
191+
192+
What breaks, and what is the justification for it?
193+
194+
## Proposed PHP Version(s)
195+
196+
List the proposed PHP versions that the feature will be included in. Use
197+
relative versions such as "next PHP 8.x" or "next PHP 8.x.y".
198+
199+
## RFC Impact
200+
201+
### To SAPIs
202+
203+
Describe the impact to CLI, Development web server, embedded PHP etc.
204+
205+
### To Existing Extensions
206+
207+
Will existing extensions be affected?
208+
209+
### To Opcache
210+
211+
It is necessary to develop RFC's with opcache in mind, since opcache is
212+
a core extension distributed with PHP.
213+
214+
Please explain how you have verified your RFC's compatibility with
215+
opcache.
216+
217+
### New Constants
218+
219+
Describe any new constants so they can be accurately and comprehensively
220+
explained in the PHP documentation.
221+
222+
### php.ini Defaults
223+
224+
If there are any php.ini settings then list: \* hardcoded default values
225+
\* php.ini-development values \* php.ini-production values
226+
227+
## Open Issues
228+
229+
Make sure there are no open issues when the vote starts!
230+
231+
## Unaffected PHP Functionality
232+
233+
List existing areas/features of PHP that will not be changed by the RFC.
234+
235+
This helps avoid any ambiguity, shows that you have thought deeply about
236+
the RFC's impact, and helps reduces mail list noise.
237+
238+
## Future Scope
239+
240+
This section details areas where the feature might be improved in
241+
future, but that are not currently proposed in this RFC.
242+
243+
## Proposed Voting Choices
244+
245+
Include these so readers know where you are heading and can discuss the
246+
proposed voting options.
247+
248+
## Patches and Tests
249+
250+
Links to any external patches and tests go here.
251+
252+
If there is no patch, make it clear who will create a patch, or whether
253+
a volunteer to help with implementation is needed.
254+
255+
Make it clear if the patch is intended to be the final patch, or is just
256+
a prototype.
257+
258+
For changes affecting the core language, you should also provide a patch
259+
for the language specification.
260+
261+
## Implementation
262+
263+
After the project is implemented, this section should contain - the
264+
version(s) it was merged into - a link to the git commit(s) - a link to
265+
the PHP manual entry for the feature - a link to the language
266+
specification section (if any)
267+
268+
## References
269+
270+
Links to external references, discussions or RFCs
271+
272+
## Rejected Features
273+
274+
Keep this updated with features that were discussed on the mail lists.

drafts/template.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
# PHP RFC: Your Title Here
22

3-
\* Version: 0.9 \* Date: 2013-02-24 (use today's date here) \* Author:
4-
Your Name, your_email_address@example.com \* Status: Draft (or Under
5-
Discussion or Accepted or Declined) \* First Published at:
6-
<http://wiki.php.net/rfc/your_rfc_name>
3+
* Version: 0.9
4+
* Date: 2013-02-24 (use today's date here)
5+
* Author: Your Name, your_email_address@example.com
6+
* Status: Draft (or Under Discussion or Accepted or Declined)
7+
* First Published at: <http://wiki.php.net/rfc/your_rfc_name>
78

89
This is a suggested template for PHP Request for Comments (RFCs). Change
910
this template to suit your RFC. Not all RFCs need to be tightly

0 commit comments

Comments
 (0)