> > 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.