# Short class syntax and inner classes

- Thread: https://externals.io/message/126331
- Original archive: https://news-web.php.net/php.internals/126331
- Started by Rob Landers on 2025-02-07T14:23:48+00:00
- Messages: 4
- Reply by email to internals@lists.php.net
- More for agents: search with https://externals.io/search.md?q={query} · RFCs at https://externals.io/rfc.md · JSON API at https://externals.io/api · documentation at https://externals.io/llms.txt

---

## #126331 — Rob Landers — 2025-02-07T14:23:48+00:00

URL: https://externals.io/message/126331#126331

Hey internals,

You may remember my RFC about Records (https://wiki.php.net/rfc/records). There was something intentionally left out, and due to life events, I am quite behind schedule on this one. BUT, what I left out (and the main reason for the short syntax) is the ability to have locally scoped records in a class definition.

Example using an anonymous record:

$x = new class {
  public record Point(int $x, int $y);

  // from inside the class, there is no special scope
  public function foo(): Point {
    return new Point(1, 1);
  }
}

$point = new $x::Point(1, 2);

Anyway, I think I've settled on an implementation that is acceptable, but I wanted your feedback before moving forward.

Instead of this being limited to Records -- an entirely new type of type and syntax -- would we rather have short class declarations (similar to the rules in the Records RFC) and locally scoped classes?

class Outer {
  // able to be instantiated from anywhere
  public class Inner(int $y);
  // a private/protected class can only be instantiated within methods/classes bound to Outer
  protected readonly class ProtectedInner(int $z);
}

Instantiation is done by static access: new Outer::Inner(1), which is currently a syntax error. Public nested classes may be used as type hints and return types as well as properties in other classes.

There are quite a few more things left to investigate before updating the RFC (or drafting a new one), but I am at a crossroads and I wanted to gather people's feedback first. If this is a big "NO" (or if someone else has started working on this and I'm just so happening to accidentally step on their toes, again): I don't want to spend several days/weeks digging into the details for classes. However, it would probably look something like the above.

Note, this doesn't preclude you from using a longer syntax, if that is your preference.

class Outer {
  public class Inner {
    public function __construct(public int $z) {}
  }
}

Anyway, I'd love to hear any preferences or thoughts -- strong or otherwise. I probably won't reply, but I will read everything and take it into consideration as I continue down this road.

Also, if there is already someone working on this ... please speak up so I don't get accused of stealing ideas again!

— Rob

---

## #126333 — tim@bastelstu.be — 2025-02-07T15:05:54+00:00

URL: https://externals.io/message/126331#126333 · In reply to: #126331

Hi

Am 2025-02-07 15:23, schrieb Rob Landers:
> Instantiation is done by static access: new Outer::Inner(1), which is 
> currently a syntax error.

How would I access static members on an inner class without having 
ambiguity? `Outer::Inner::Const` already is valid syntax.

> If this is a big "NO" (or if someone else has started working on this 
> and I'm just so happening to accidentally step on their toes, again): I 
> don't want to spend several days/weeks digging into the details for 
> classes. However, it would probably look something like the above.

I believe Ilija has some proof-of-concept regarding file-private 
classes, which would sidestep the above problem of disambiguating static 
member access.

Best regards
Tim Düsterhus

---

## #126336 — Rob Landers — 2025-02-07T15:42:29+00:00

URL: https://externals.io/message/126331#126336 · In reply to: #126333

> Hi
> 
> Am 2025-02-07 15:23, schrieb Rob Landers:
> > […]
> 
> How would I access static members on an inner class without having 
> ambiguity? `Outer::Inner::Const` already is valid syntax.

I can actually answer this one. The name of an inner class must not conflict with either a const or a method in the outer class because this is also currently valid syntax `new (Outer::Inner::Const)` where it resolves to a string of an existing class or `new (Outer::Inner::Method())` where it returns a string to an existing class.

— Rob

---

## #126337 — Ilija Tovilo — 2025-02-07T16:35:40+00:00

URL: https://externals.io/message/126331#126337 · In reply to: #126333

Hi there


>
> I believe Ilija has some proof-of-concept regarding file-private
> classes, which would sidestep the above problem of disambiguating static
> member access.

Indeed. I shared a draft internally and responses were mixed.

https://wiki.php.net/rfc/private-classes-and-functions
https://github.com/php/php-src/compare/master...iluuu1994:php-src:private-classes

The RFC proposes to allow declaring classes and functions as private,
which will limit their usage to the current file, or more accurately,
to the current namespace block (i.e. namespace {}, not namespace
across files)

The motivation is to allow declaring small helper classes (e.g. mocks
for tests) in the file where they are used. Of course, this is
currently already possible, but discouraged to preserve the ability to
autoload classes. However, autoloading is not necessary for private
members, so this concern goes away.

Furthermore, private mangles the class/function name so that it
doesn't conflict with other files declaring a class/function with the
same private name. For example, multiple tests can create a private
class DbMock without worrying about conflicts.

To summarize, the main benefits for private is:

* Single-use classes/functions live closer to their point of usage.
* It signals to users that the members are not intended to be used
outside the given file.
* It avoids naming conflicts for cases between private members.

The main pushback was that "private" does not sufficiently indicate
the semantics of this feature. This could simply be solved with a
different keyword. I guess for others, "namespace private" is a more
interesting concept.

One thing I disliked about the implementation is that private members
still pollute the global symbol tables. That said, it's not likely to
matter in practice.

Anyway, I didn't have any immediate plans to pursue this RFC, so feel
free to take over in any way you like.

Ilija
