[PHP-DEV] [RFC] Four Pragmatic Directions for PHP: Simplicity, Arrays, Performance, Concurrency

Hi internals,

I’m a developer who uses both Java and PHP regularly. I’m writing
because I care about PHP’s future.

PHP’s strength has always been simplicity and pragmatism. But since
PHP 7 and especially PHP 8, we’ve been adding “enterprise features”
(typed properties, attributes, property hooks) that make PHP more like
Java – without achieving Java’s type safety or ecosystem.

I believe PHP should refocus on what made it great. Here are four
pragmatic directions:


  1. Stop Chasing Java

Every “enterprise feature” pushes developers like me toward Java. Not
because Java is better, but because if I need enterprise complexity
anyway, I’d rather use a language designed for it.

PHP cannot beat Java at its own game. But it can beat Java where it
matters: rapid prototyping, simple deployment, web-native development,
low learning curve.

Please keep PHP simple. Keep it pragmatic.


  1. Add Practical Array Helpers

PHP is a web language – 90% of what we do is manipulate arrays. Yet
basic operations are still verbose:

// Current
$names = array_values(array_filter($users, fn($u) => $u->active));
$first = count($names) > 0 ? $names[0] : null;

// Proposed
$names = array_pluck($users, 'name');
$names = array_where($users, fn($u) => $u->active);
$first = array_first($users);
$grouped = array_group_by($users, 'role');
$compact = array_compact($data);

These helpers would make everyday code cleaner without adding
complexity.

1. Improve Performance

Performance has always been a strength. Keep optimizing:

- JIT for real-world web workloads

- Reduce memory allocation in array operations

- Make OpCache smarter

1. Provide Built-in Concurrency (Without Complexity)

Concurrency is the biggest missing piece. But instead of complex
async/await or coroutines, consider a simple worker management API:

php

$server = new WorkerServer();
$server->onWorkerStart(fn() => loadApp());
$server->onRequest(fn($req) => handle($req));
$server->start(4);


This would:

- Run standalone: `php server.php` – no nginx, no PHP-FPM

- Keep process isolation (each worker is separate)

- Allow resource initialization once per worker

- Give developers control without new concepts

Why not just use FrankenPHP? FrankenPHP is excellent, but requires an
additional binary and new deployment workflow. Many developers just
want a simple, built-in way to run their app without configuring two
services.

---

Why This Matters

PHP's competitors are winning on concurrency (Node.js, Go) and syntax
(Python). PHP's advantages – simplicity, web-native deployment, low
learning curve – are being eroded.

I don't want to leave PHP. I want PHP to stay great. I want to keep
choosing PHP for years to come.

Thank you for your time and for maintaining PHP. I'm happy to provide
more examples or participate in discussions.

Best regards,
[qinhao]

On 24.03.2026 11:08, 沉默领域、 wrote:

// Current
$names = array_values(array_filter($users, fn($u) => $u->active));
$first = count($names) > 0 ? $names[0] : null;

Not the best example, as you can also do:

$first = array_find($users, fn ($user) => $user->active)?->name;

There are some existing functions like array_column() and array_first() that seems you didn't know existed. So, there's been some progress in this territory too.

--
Aleksander Machniak
Kolab Groupware Developer [https://kolab.org]
Roundcube Webmail Developer [https://roundcube.net]
----------------------------------------------------
PGP: 19359DC1 # Blog: https://kolabian.wordpress.com

I agree with this sentiment.

My main gripe with PHP 8 is that undefined array key accesses trigger a warning. There is no solution to this that doesn’t require destroying the readability of a legacy codebase or using some hacky global error handler.

I think the direction should always have been this: undefined array key accesses should simply return null by default.

To ensure data integrity in critical paths, a new language construct could be used: strict_access($arr[‘key’]). This construct strict_access (similar to isset which is not a “real” function) would tell the compiler to emit a specialized opcode that triggers a warning if the key is missing. Maybe a shorthand like $arr['key']! could be used. This would give developers the best of both worlds: clean, ‘script-style’ code by default, and explicit, high-performance validation only where it’s actually requested.

  • Dan

On Tue, Mar 24, 2026 at 6:33 AM Aleksander Machniak <alec@alec.pl> wrote:

On 24.03.2026 11:08, 沉默领域、 wrote:

// Current
$names = array_values(array_filter($users, fn($u) => $u->active));
$first = count($names) > 0 ? $names[0] : null;

Not the best example, as you can also do:

$first = array_find($users, fn ($user) => $user->active)?->name;

There are some existing functions like array_column() and array_first()
that seems you didn’t know existed. So, there’s been some progress in
this territory too.


Aleksander Machniak
Kolab Groupware Developer [https://kolab.org]
Roundcube Webmail Developer [https://roundcube.net]

PGP: 19359DC1 # Blog: https://kolabian.wordpress.com

On 2026-03-24 23:08, 沉默领域、 wrote:

// Proposed
$names = array_pluck($users, 'name');
$names = array_where($users, fn($u) => $u->active);
$first = array_first($users);
$grouped = array_group_by($users, 'role');
$compact = array_compact($data);

Of those, array_compact() is obscure in its purpose, and of the others one exists under the suggested name and three of the others exist under different names.

array_group_by() is the only one that isn't already implemented. I think it means:

$roles = array_unique(array_column($users, 'role'));
$grouped = array_combine($roles, array_map(fn($r) => array_filter($users, fn($u) => $u['role']==$r), $roles));

though there are more efficient (and readable) ways of doing that.

--

Optimising the implementation would be more difficult if the language were allowed to be sloppier. Conversely, tightening up the language allows improved optimisation.

On Tue, Mar 24, 2026, at 5:08 AM, 沉默领域、 wrote:

Hi internals,

I'm a developer who uses both Java and PHP regularly. I'm writing
because I care about PHP's future.

PHP's strength has always been simplicity and pragmatism. But since
PHP 7 and especially PHP 8, we've been adding "enterprise features"
(typed properties, attributes, property hooks) that make PHP more like
Java – without achieving Java's type safety or ecosystem.

I believe PHP should refocus on what made it great. Here are four
pragmatic directions:

---

1. Stop Chasing Java

Every "enterprise feature" pushes developers like me toward Java. Not
because Java is better, but because if I need enterprise complexity
anyway, I'd rather use a language designed for it.

PHP cannot beat Java at its own game. But it can beat Java where it
matters: rapid prototyping, simple deployment, web-native development,
low learning curve.

Please keep PHP simple. Keep it pragmatic.

This accusation has been thrown at PHP every few months since PHP 5.0 came out (which did indeed ape Java 2 for much of its OOP syntax and behavior). It's really a farcical misunderstanding of language design, "enterprise features," and "pragmatism."

Many of the features added in the 8.0+ era are simple syntactic sugar to reduce typing. Is that "enterprise?" Typed properties greatly reduce the amount of error handling needed, and the amount of tests needed. Is that "enterprise?" Property hooks vastly reduce the need for boilerplate code, making it faster and easier to bang out code that is forward-compatible. Is that "enterprise?"

The programming language community at large keeps learning, and has learned a ton since the 90s. One thing it has learned, but many developers have not, is that "code that finds errors for you at compile time" is NOT an enterprise feature. It's an everybody feature, which is why virtually every untyped language has had some form of types added to it, in various ways. (PHP directly, Python via unenforced annotations, Javascript via TypeScript, etc.)

2. Add Practical Array Helpers

PHP is a web language – 90% of what we do is manipulate arrays. Yet
basic operations are still verbose:

```php
// Current
$names = array_values(array_filter($users, fn($u) => $u->active));
$first = count($names) > 0 ? $names[0] : null;

// Proposed
$names = array_pluck($users, 'name');
$names = array_where($users, fn($u) => $u->active);
$first = array_first($users);
$grouped = array_group_by($users, 'role');
$compact = array_compact($data);

These helpers would make everyday code cleaner without adding
complexity.

You may have noticed that a number of array and string helper functions have been added in recent years. If there are more that you feel should be, RFCs are welcome. Just adding an array function is probably one of the simplest technical RFCs to write.

1. Improve Performance

Performance has always been a strength. Keep optimizing:

• JIT for real-world web workloads

• Reduce memory allocation in array operations

• Make OpCache smarter

All of these are happening, to the extent possible.

1. Provide Built-in Concurrency (Without Complexity)

Concurrency is the biggest missing piece. But instead of complex
async/await or coroutines, consider a simple worker management API:

php
$server = new WorkerServer();
$server->onWorkerStart(fn() => loadApp());
$server->onRequest(fn($req) => handle($req));
$server->start(4);
This would:

• Run standalone: `php server.php` – no nginx, no PHP-FPM

• Keep process isolation (each worker is separate)

• Allow resource initialization once per worker

• Give developers control without new concepts

Why not just use FrankenPHP? FrankenPHP is excellent, but requires an
additional binary and new deployment workflow. Many developers just
want a simple, built-in way to run their app without configuring two
services.

You may not have noticed, if you're new to the list, but there has been an extensive and ongoing discussion of improved async support, with a lot of focus on how to make it easy to use right and hard to use wrong. Because that is actually a really, really hard problem space. "Simple worker management" is not actually simple, when you dig into it. There's a lot of things that can go wrong, and designing it (both the code and the APi) such that it's hard to have things go wrong is itself really hard. That's why it's going on over a year now.

Rest assured, there's an awful lot of people that really want good async/workers/etc. But it's nowhere near as easy as you think, or it would be done already. (See also: generics.)

--Larry Garfield

I used to have a really long and interesting blog post on PHP arrays about this:

On Tue, Mar 24, 2026, at 12:04, Dan Liebner wrote:

I agree with this sentiment.

My main gripe with PHP 8 is that undefined array key accesses trigger a warning. There is no solution to this that doesn’t require destroying the readability of a legacy codebase or using some hacky global error handler.

I think the direction should always have been this: undefined array key accesses should simply return null by default.

And then my disk caught on fire and it was never archived. TL;DR: treating PHP arrays as an infinite field of nulls has some very interesting mathematical properties. No other language on earth gave you this.

Practical usecases were ways to effectively “cheat” on some np-hard and np-complete problems, like subset-sum, knapsack, Hamiltonian Path, etc. This warning (and eventual removal of this property) makes these “cheats” no longer possible in PHP and constrained just like every other language on the planet.

— Rob

On 26 March 2026 07:51:31 GMT, Rob Landers <rob@bottled.codes> wrote:

And then my disk caught on fire and it was never archived. TL;DR: treating PHP arrays as an infinite field of nulls has some very interesting mathematical properties. No other language on earth gave you this.

I agree that some useful use cases have been "thrown out with the bathwater" by trying to make array access less prone to mistakes.

I think the pragmatic response to that is to propose new features that cater *even better* to those use cases. For instance:

- an "optional key access" operator like $foo[?'bar'] that makes the intent explicit but concise
- a PHP version of Python's "defaultdict", which lets you *choose* the default value for undefined items, rather than always null
- copy-on-write structs with predefined keys

At the same time, PHP has always been a long way behind other languages when it comes to lazy lists and iterators; I would love to see:

- a "sparse list" type, where you can define the size and initial value without allocating memory for all elements
- lazy ranges
- built-in functions or operators which can operate lazily on iterators (i.e. without enumerating them)

These aren't trivial to design and implement, but it would be great to transfer some of the energy from "I don't like this thing someone else wanted" into "I want this thing as well".

Regards,

Rowan Tommins
[IMSoP]

On 2026-03-27 03:58, Rowan Tommins [IMSoP] wrote:

On 26 March 2026 07:51:31 GMT, Rob Landers <rob@bottled.codes> wrote:

- an "optional key access" operator like $foo[?'bar'] that makes the intent explicit but concise

So, syntactic sugar for "($foo['bar'] ?? null)".

On 2026-03-27 03:58, Rowan Tommins [IMSoP] wrote:

On 26 March 2026 07:51:31 GMT, Rob Landers rob@bottled.codes wrote:

  • an “optional key access” operator like $foo[?‘bar’] that makes the intent explicit but concise

So, syntactic sugar for “($foo[‘bar’] ?? null)”.

That, to me, would be a massive improvement in terms of code readability.

On Thu, Mar 26, 2026 at 11:40 PM Morgan <Weedpacket@varteg.nz> wrote:

On 2026-03-27 03:58, Rowan Tommins [IMSoP] wrote:

On 26 March 2026 07:51:31 GMT, Rob Landers rob@bottled.codes wrote:

  • an “optional key access” operator like $foo[?‘bar’] that makes the intent explicit but concise

So, syntactic sugar for “($foo[‘bar’] ?? null)”.

In that case, maybe reuse something similar to the null-safe operator ?

Something like $foo['bar']?

With the ability to have some freedom in placing the ? character.

This way, you have both syntactic sugar:

return $foo[‘one’]?;
// is roughly equivalent to:
return $foo[‘one’] ?? null;

return $foo[‘one’]?[‘two’];
// is roughly equivalent to:
return $foo[‘one’][‘two’] ?? null;

And here it gets interesting:

return $foo[‘one’][‘two’]?[‘three’];
// is roughly equivalent to:
if (!isset($foo[‘bar’]) {
trigger_error(‘Undefined key “one” in …’, …);
}
return $foo[‘one’][‘two’][‘three’] ?? null;

This brings a “new” way to deal with nested array keys (like explained in the first code samples).

I don’t know about the parser yet, but technically, in language semantics, it is a unary-right, contrary to the “?” operator which is binary left&right and must be used alongside the “:” operator.

Would need an implementation and some tests to ensure the reliability.

···

Le 27/03/2026 à 06:26, Dan Liebner a écrit :

On 2026-03-27 03:58, Rowan Tommins [IMSoP] wrote:

On 26 March 2026 07:51:31 GMT, Rob Landers rob@bottled.codes wrote:

  • an “optional key access” operator like $foo[?‘bar’] that makes the intent explicit but concise

So, syntactic sugar for “($foo[‘bar’] ?? null)”.

That, to me, would be a massive improvement in terms of code readability.

On Thu, Mar 26, 2026 at 11:40 PM Morgan <Weedpacket@varteg.nz> wrote:

On 2026-03-27 03:58, Rowan Tommins [IMSoP] wrote:

On 26 March 2026 07:51:31 GMT, Rob Landers rob@bottled.codes wrote:

  • an “optional key access” operator like $foo[?‘bar’] that makes the intent explicit but concise

So, syntactic sugar for “($foo[‘bar’] ?? null)”.

On 27/03/2026 15:19, Alex Rock wrote:

In that case, maybe reuse something similar to the null-safe operator ?

Something like `$foo['bar']?`

I think even if you could make it work in the parser, it would be hard to make that read well with the ternary operator:

$foo['a'] ? ['b'] ? ['c'] : ['d'];

return $foo\[&#39;one&#39;\]?\[&#39;two&#39;\];

Looking at that, it's not obvious to me whether it's 'one' or 'two' which is optional.

return $foo['one'] ?['two'];
return $foo['one']? ['two'];

And here it gets interesting:

return $foo\[&#39;one&#39;\]\[&#39;two&#39;\]?\[&#39;three&#39;\];
// is roughly equivalent to:
if \(\!isset\($foo\[&#39;one&#39;\]\) \{
trigger\_error\(&#39;Undefined key &quot;one&quot; in \.\.\.&#39;, \.\.\.\);
\}
return $foo\[&#39;one&#39;\]\[&#39;two&#39;\]\[&#39;three&#39;\] ?? null;

You can have the same ability with a ? inside the brackets, and without the ambiguity:

// all three keys required to exist (as of PHP 9):
return $foo['one']['two']['three'];

// 'one' and 'two' required, 'three' optional:
return $foo['one']['two'][?'three'];

// both 'two' and 'three' optional:
return $foo['one'][?'two'][?'three'];

// optional all the way:
return $foo[?'one'][?'two'][?'three'];

This would maybe have similar short-cut semantics to ?->

$foo = [];
return $foo[?'one']['two'];
// OK: the ? neutralises access to the missing section of the array, short-cutting to NULL
// Similar to $foo?->bar->baz when $foo is NULL

$foo = ['one' => []];
return $foo[?'one']['two'];
// Error: the ? had no effect, so we're trying to access non-existent key 'two' in the empty array $foo['one']
// Similar to $foo?->bar->baz when $foo->bar exists

But an RFC could pin down those details.

--
Rowan Tommins
[IMSoP]

Yeah, adding ? after expressions gets weird because of ternary operators.

I do think the long-form $foo[?'one'][?'two'][?'three'] is acceptable in terms of developer experience considering it’s not a super common scenario. I’m not sure if that would be annoying to implement on the C side of things.

  • Dan

On Sat, Mar 28, 2026 at 2:33 PM Rowan Tommins [IMSoP] <imsop.php@rwec.co.uk> wrote:

On 27/03/2026 15:19, Alex Rock wrote:

In that case, maybe reuse something similar to the null-safe operator ?

Something like $foo['bar']?

I think even if you could make it work in the parser, it would be hard
to make that read well with the ternary operator:

$foo[‘a’] ? [‘b’] ? [‘c’] : [‘d’];

return $foo[‘one’]?[‘two’];

Looking at that, it’s not obvious to me whether it’s ‘one’ or ‘two’
which is optional.

return $foo[‘one’] ?[‘two’];
return $foo[‘one’]? [‘two’];

And here it gets interesting:

return $foo[‘one’][‘two’]?[‘three’];
// is roughly equivalent to:
if (!isset($foo[‘one’]) {
trigger_error(‘Undefined key “one” in …’, …);
}
return $foo[‘one’][‘two’][‘three’] ?? null;

You can have the same ability with a ? inside the brackets, and without
the ambiguity:

// all three keys required to exist (as of PHP 9):
return $foo[‘one’][‘two’][‘three’];

// ‘one’ and ‘two’ required, ‘three’ optional:
return $foo[‘one’][‘two’][?‘three’];

// both ‘two’ and ‘three’ optional:
return $foo[‘one’][?‘two’][?‘three’];

// optional all the way:
return $foo[?‘one’][?‘two’][?‘three’];

This would maybe have similar short-cut semantics to ?->

$foo = ;
return $foo[?‘one’][‘two’];
// OK: the ? neutralises access to the missing section of the array,
short-cutting to NULL
// Similar to $foo?->bar->baz when $foo is NULL

$foo = [‘one’ => ];
return $foo[?‘one’][‘two’];
// Error: the ? had no effect, so we’re trying to access non-existent
key ‘two’ in the empty array $foo[‘one’]
// Similar to $foo?->bar->baz when $foo->bar exists

But an RFC could pin down those details.


Rowan Tommins
[IMSoP]