This RFC from Rob Landers proposed letting you declare a class inside another class, with public, protected or private visibility just like a method. That would let you keep a helper class, like a builder or a small data object, tied to the one class that uses it. It was declined.

How it would have worked

class Outer {
    public class Inner {
        public function __construct(public string $message) {}
    }

    private class PrivateInner {
        public function __construct(public string $message) {}
    }
}

$foo = new Outer\Inner('Hello, world!');
echo $foo->message;
// outputs: Hello, world!

$baz = new Outer\PrivateInner('Hello, world!');
// Uncaught TypeError: Cannot instantiate class Outer\PrivateInner from the global scope

You refer to a nested class as Outer\Inner, so the outer class acts a bit like a namespace.

The RFC laid out these rules:

  • A private nested class can only be used inside its outer class, a protected one also works in child classes, and a public one works anywhere.
  • Leaving out the visibility keyword makes it public.
  • Nested classes can be readonly, final or abstract, and the outer class's modifiers don't carry over to them.
  • A nested class can access the outer class's private members, but the outer class can't access the nested class's private members.
  • A nested class doesn't get a reference to the outer object automatically. You pass it in if you need it.
  • A public method can't declare a private nested class as its return type, which throws a TypeError. You can return it behind an interface instead.
  • You can nest classes inside classes, anonymous classes, enums and interfaces, but not traits.
  • ReflectionClass gets a new isNestedClass() method.

What it means for existing code

The syntax is new, so it doesn't conflict with code you have today. Some error messages would change, though, so tests that assert on exact messages could fail. Tools that parse PHP, like IDEs and static analyzers, would also need updates to understand the new syntax.

The vote

The RFC was declined 2 to 20 against the two-thirds majority it needed. Voting closed on May 18, 2025.