PHP reimagined
If it were up to me, I'd change a thing or two about PHP. Of course, I don't have anything to say about PHP's development, and that's ok. I still find it an interesting thought experiment to discover what changes I'd like to make to the language I use on a day-by-day basis and I'd love to hear your thoughts as well. To be clear: it's a very subjective list and in no way a critique on the amazing work the core team is doing.
This is an updated version of an older post of mine, since PHP is growing and evolving year by year. Let's dive in!
# Final by default
A common misconception about OO programming is that it's all about inheritance. Inheritance and polymorphism have their place, but OO is way more than that.
Because these principles are more often than not abused by programmers who claim they write "OO" code, I think the language should help prevent us making these mistakes.
That's why I would make all classes final by default:
final class Foo { }
class Bar extends Foo { }
Going even one step further: classes are only allowed to extend from abstract classes or implement interfaces. This way we can prevent deep inheritance chains of concrete classes.
# Void by default
Void is a strange thing when you think about it: it a "type", indicating the lack of a type. Why not go with the obvious way: no return type, means nothing is returned.
class Foo { public function bar(): void { // … } }
class Foo { public function bar() { return false; } }
Now you might be thinking: what if a function wants to return two types, that's the next point.
# No mixed
type
The mixed
type basically means:
"you've got no idea what this function needs or will return, figure it out on your own".
Such a loose type system can be the source of many bugs. If you feel the need to use two different types in the same function, you should either make two implementations — this is where polymorphism has its place; or you should program to an interface.
Either way, there's always a better solution then relying on mixed
.
In my version of PHP, the language would ensure we always choose the better solution.
# All parameters must by typed
We already established that my version of PHP would make return types required. It's no surprise that the same goes for function parameters.
public function handle($bar) { }
public function handle(Bar $bar) { }
# Class properties must be typed
The same rules apply to class properties. Luckily for us, PHP 7.4 will introduce typed properties. I'd make them required though.
class Foo { public $bar; }
class Foo { public Bar $bar; }
# Visibility modifiers are required
Explicitness eliminates room for confusion. That's why all methods and class variables must have a visibility modifier.
class Foo { function bar() { // … } }
class Foo { public function bar() { // … } }
# Final on variables
I started this list by saying I'd drop the final
keyword, that is on classes and methods.
final
would be a valid keyword to mark class variables as "read only".
A final variable may be set on construct, and not be changed afterwards.
class Foo { public final Bar $bar; public __construct(Bar $bar) { $this->bar = $bar; } }
$foo = new Foo($bar); $foo->bar = new Bar();
# No more uninitialized state
Right now, Typed properties can be initialized after construction. This is valid PHP
class Foo { public string $bar; public function __construct() { // Don't initialize bar } } $foo = new Foo(); $foo->bar = 'abc';
PHP only throws an error when the property is accessed before it's initialised.
$foo = new Foo(); echo $foo->bar; // Error
I'd say to get rid rid of this behaviour. If a typed property isn't initialised after the object was constructed, you get an error.
# Named parameters
Named parameters were originally on this list, but fortunately are possible as of PHP 8!
# Better closures
I originally listed "multiline short closures" here, but I think it's a little bit more complex. What I'd like to see is a combination of function/fn
and =>/{
. I'd make all combinations possible:
function a() { return /* … */; } function b() => 1; fn c() { return /* … */; } fn d() => 1;
Here's the difference: when using the function
keyword, there's no automatic access to the outer scope, in other words you'll have to use use
to access variables outside the closure. Using fn
doesn't have this restriction.
If you're using the bracket notation for the closure's body {}
, you'll be allowed to write multiline functions, but there's no magic return statement. =>
on the other hand only allows a single expression, which is immediately returned.
# Scalar types are also objects
One of the few things I think that we're all in agreement about: the current PHP function names and definitions are inconsistent and kind of sucky.
Let's treat all scalar types as objects, allowing them to contain what otherwise would be standalone functions.
public function handle(): string { return "a, b, c"; } $this->handle()->explode(',');
# Improved variance
You may have noticed a trend in the above changes. Most of them relate to PHP's type system. If all them were added, we'd also need to make the current type system more flexible.
Luckily again, PHP 7.4 already introduces improvements regarding type variance.
class Bar extends Foo { /* … */ }
interface A { public function handle(Bar $bar): Foo; } class B implements A { public function handle(Foo $bar): Bar { // … } }
# Always strict type checking
Strict type checking is done by default, you should never declare(strict_types=1);
anymore.
# Generics
After several improvements to the type system, I'd add some more improved ways to actually use it.
First a feature that probably most of the PHP world is waiting for: generics.
class List<T> { public function current(): T { // … } }
# Enums
Next up: built-in enums. Based on the several userland implementations it's clear that the community would benefit from a built-in enum type.
enum Status { DRAFT, STATUS, PUBLISHED; }
class Bar { public Status $status; }
$bar->status = Status::DRAFT;
It's interesting to note that a new RFC popped up that might add enums in PHP 8.1. It's still being discussed though, so nothing concrete yet.
# Structs
To end this list: structs. One of my own packages I use all the time is the data transfer object package. It allows us to define strongly typed objects. In essence, they are a userland implementation of what structs are meant to solve.
struct Point { int $x; int $y; }
$point = Point {1, 2}
# What would you like to change?
Let me know what's on your PHP wishlist! If you want to be kept in the loop, feel free to subscribe to my newsletter.