>
>  As far as I know, all current SAPIs follow one of two patterns:
>

It seems that my example, although taken from real life, is more of an
anti-pattern. Let's take a real example that is not an anti-pattern.

There is a B2B CRM built on services. Services are classes instantiated in
memory only once via DI, and all that. We process requests. Requests are
executed within a logical *Scope*. The scope depends on the *TOKEN* and
reflects the following entities:

   - *User Profile*
   - *Company Settings*

We have two implementation options:

   1. Pass the user profile and company settings to every method.
   2. Use some static variable to make the semantics shorter.

When the application runs in separate processes, there are no issues. What
do we do?

   1. Pass the UserProfile object into DI.
   2. Pass the CompanySettings object into DI.

Everyone is happy. If it’s a *long-running* process, everyone is twice as
happy because the already loaded services and resolved DI are reused
multiple times for handling requests. Memory copying is reduced, and for
fast requests, we get a nice performance boost. This is especially pleasant
for users with a good internet connection.

However, this model will not work if each request is processed in a
separate coroutine. There are two possible solutions:

   1. Pass the *"environment objects"* explicitly through function calls (*I’d
   like to see developers doing this and staying in a good mood*).
   2. Use something else.

There is also a *hybrid solution*, where the service method is called
through a *central manager*, the environment objects are stored in function
parameters but are injected not by the calling code but by the manager,
which extracts them from the current context. However, this approach
assumes that the service method *cannot* be called directly, so it is not
suitable as a *general solution*.

This approach will remain relevant *until PHP becomes a fully multitasking
language with parallel execution* (*if that ever happens*). However, there
is a strong argument against this.
In *Go*, you cannot implement an architecture with global contexts without
extra effort. But such solutions have a killer feature: *simplicity*. PHP
allows you to implement such architectures, which is one of its strengths.
By supporting this approach, we *enhance PHP’s ability* to move in this
direction.

This is the perspective in which I try to look at *RFC functions *as an
additional tool that should fit into *existing practices*.

> ```php function get_query_string_param(string $name): ?string {```

The solution you provided is similar to a real one, but I do not recommend
using it for the following reasons:

   1. Manual memory management. The coroutine completes, but the data
   remains. Who will clean it up?
   2. The need to write additional code to track memory usage. (call
   defer() or try/finally each time)
   3. The programmer might simply forget
   4. Async\Context is shared state, but not a global variable.

Async\Context is designed to *guarantee memory management*, which is a key
aspect for *long-running* applications. It *automatically releases object
references* when a coroutine completes. The programmer does not need to
write *extra code* with defer() for this.
Second point: Why Async\Context, Channel, and Future should not be just
external library objects.

There is a dilemma between a *"zoo of solutions"* and *"diversity"*.
Languages like *Rust, C, and C++* solve problems where diversity is more
important, even at the cost of dealing with a fragmented ecosystem.

Example: You need a *hash map function* optimized for *relocatable memory
blocks*, but such a thing doesn't exist out-of-the-box. The *more
diversity, the better*, as you can choose a solution tailored to your
unique case.

However, *PHP is not designed for such tasks*. If fundamental primitives in
PHP exist in *multiple competing libraries*, it becomes a nightmare.

   - It’s one thing if *standardized primitives* exist, and libraries
   provide alternatives on top of a shared contract.
   - It’s another if *every developer* has to reinvent the wheel from
   scratch.

If such primitives are not included in this or future RFCs, and instead are
left to chance, *it's better not to accept the RFC at all*.

This is the reason why the current RFC is large. It helps to see the bigger
picture. Once the general principles are agreed upon, we can split it into
parts (Incremental Design).

Such a plan.

Ed.

Reply via email to