Hello.

To use AMPHP you don’t need to run its server. But let’s simplify the situation.

You have Fiber.
With Fiber you can build an EventLoop + Scheduler, create two
coroutines, and then run code inside them that, for example, renders a
template.

It will require a fair amount of code, but it’s possible.
At the same time, ob_start/ob_end will immediately break, because they
don’t support Fiber switching. This is a known bug.
And if you pass global state into closures, and that state changes
unpredictably, the application logic will essentially break.

**What’s different with TrueAsync**

1. You don’t need to write a lot of code. Yes, just spawn();
2. `ob_start` and `ob_end` will work correctly.
3. If closures share a variable that multiple coroutines write to
unpredictably, the application will still break.

In other words, no matter how asynchrony is implemented in PHP (and
other langs), it will always require correct handling of shared
memory.
And I want to clarify a bit. This is not about a global variable. It’s
about shared memory. These are not the same things. A local variable
can be passed into a closure by reference.
But if you don’t call WP() inside a coroutine, nothing prevents you
from using asynchrony.

In TrueAsync there is a Dockerfile for FPM, you can install it locally
and try running WordPress. I’m sure something will break, but not
everything at once :)

> My understanding of AMPHP is that things only become async if you explicitly 
> run AMPHP as your web server. It either has no effect
> if your application is running Apache + mod_php OR your application 100% 
> breaks with nothing getting executed at all. Is this not true?

You can use it without a server. The only difference is that the
coroutines won’t live longer than the HTTP request. The same applies
to TrueAsync when running under FPM.

> When I try to open my website locally it will either have everything broken 
> or it will just work regularly and nothing will be running async. There is no 
> other option.
It won’t break on its own.
It will only break if that “something” shares a variable or resource
with your code. If you write code according to SOLID principles, the
chance of breakage is very low.

If someone writes coroutine code and completely ignores memory
handling, it’s the same as writing an infinite loop. Can they do that?
Yes. Can you download that code? Yes. Can it break everything? It can.
What’s the conclusion?

> I do `composer update` one day and test my home page and it works. But deeply 
> nested somewhere there is a feature that will use Package A which
> then spawns a coroutine and leads to my global state code behaving weirdly. 
> It's not a fatal error. It's not very clear at all to me that something 
> changed,
> but now my array indexes are triggering DB updates out of order.

The exact same thing will happen in code without coroutines if, for
example, you have a class, you pass an array to it by reference,
and that class silently corrupts your values. You don’t need
asynchrony for that to happen.

```php
<?php

class Storage
{
    private array &$data; // holds a reference to the external array

    public function __construct(array &$data)
    {
        // store the reference
        $this->data = &$data;
    }

    public function loadUser()
    {
        // looks like a harmless read
        return $this->data['user'] ?? null;
    }

    public function updateInternally()
    {
        // silently mutates the external array
        $this->data['user'] = 'hacked';
        $this->data['counter'] = ($this->data['counter'] ?? 0) + 1;
    }
}

// ---------------------

$shared = [
    'user' => 'admin',
    'counter' => 0,
];

// create the class, passing the array by reference
$storage = new Storage($shared);

echo $storage->loadUser() . PHP_EOL; // admin

// somewhere else this method is called, modifying the shared array
$storage->updateInternally();

print_r($shared);
// the array is now corrupted unexpectedly

```

There is no asynchrony in the example above. It’s just normal code. It
breaks application logic because the class secretly holds the array
and modifies it without permission.

And a coroutine is just a regular object. If you pass a referenced
array into a coroutine and then say something “accidentally” broke, it
wasn’t accidental. Did the array get there by accident? No. Did some
developer write the code by accident? No. It’s a bit different from
synchronous code, but the meaning is exactly the same.
I have a mental habit: when I write asynchronous code, I always assume
that any object shared between coroutines can change at any moment.
Similarly, if you hand off memory ownership to another class or share
memory, you have to keep in mind that there is a chance some developer
might accidentally do something wrong.

The only difference between synchronous and asynchronous code is that
you don’t have a precise moment in time when the change happens. And
yes, that makes debugging harder. It makes finding bugs harder. But
it’s not something fundamentally new.

Yes, making the same mistakes with AMPHP is harder, because you need
to install a whole package and so on. But that’s not a guarantee :)

Reply via email to