Pisz PHP jak JS

asynchroniczne programowanie w PHP

Maciej Iwanowski

O czym?

  • czym jest cooperative multitasking?
  • generatory i couroutines
  • jak tego w ogóle użyć?
  • jakie są alternatywy?
  • co robić, jak żyć?

Cooperative multitasking

  • współpraca między procesami (zadaniami)
  • dobrowolne wstrzymywanie przetwarzania
  • busy wating aka spinning

Cooperative multitasking

  • procesy i wątki się czasem nie sprawdzają
  • to jest właśnie web-scale
  • morze możliwości

Cooperative multitasking

Wygląda znajomo?


console.log('zaraz załaduję example.com');
$.ajax(
    'http://www.example.com',
    {success: function() {console.log('załadowałem example.com');}}
)
console.log('ładuję example.com');
                        

Jak przenieść asynchroniczność na stronę serwera (nie zmieniając przy tym języka)?

Cooperative multitasking

Używając Hacka używalibyśmy niemalże PHP


$results = run(array(
    "web"  => curl_exec_await("http://example.com"),
    "sql"  => mysql_async_query($conn, "SELECT * FROM user"),
    "disk" => file_async_contents("/var/log/access.log"),
    "mc"   => memcached_async_get("somekey"),
    "fib"  => fibonacci_gen(2000),
))->getWaitHandle()->join();
                        

Nie użylibyśmy wszakże wielu bibliotek

Generatory i coroutines

  • generatory zostały stworzone w innym celu
  • API ma wysoki próg wejścia
  • w PHP7 (yield from) jest trochę lepiej

Generatory


$iterateOverMe = function()
{
    yield 1;
    yield 2;
    yield 3;
};

foreach ($iterateOverMe() as $value) {
    var_dump($value);
}
                        

Coroutines

Ładne, czyż nie?


$socket = stream_socket_server("tcp://localhost:10666");
stream_set_blocking($socket, 0);
$socketObject = new Socket($socket);
while (true) {
    yield newTask(
        handleClient(yield $socketObject->accept())
    );
}
                        

Coroutines

  • yield $someValue
  • yield from someFunction()
  • Generator::send()
  • yield

Coroutines


$generatorWithTwoWayCommunication = function()
{
    $randomValue = mt_rand();
    yield $randomValue;
    $receivedFromOuterSpace = yield;
    yield $receivedFromOuterSpace * $receivedFromOuterSpace;
    return $receivedFromOuterSpace * $randomValue;
};
                        

Coroutines


$generator = $generatorWithTwoWayCommunication();
$valueFromFirstIteration = $generator->current();
$generator->next();
$valueFromSecondIteration = $generator->send(666);
$generator->next();
$valueReturnedFromGenerator = $generator->getReturn();
                        

Coroutines

$generatorWithTwoWayCommunication = function()
{
    $randomValue = mt_rand();
    yield $randomValue;
    $receivedFromOuterSpace = yield;
    $returnValue = yield from
        subGenerator($receivedFromOuterSpace, $randomValue);
    return $returnValue;
};

function subGenerator($valueFromOuterSpace, $randomValue)
{
    yield $valueFromOuterSpace * $valueFromOuterSpace;
    yield $valueFromOuterSpace * $randomValue;
    return 'Lis Witalis';
};

Coroutines


$generator = $generatorWithTwoWayCommunication();
$valueFromFirstIteration = $generator->current();
$generator->next();
$valueFromSecondIteration = $generator->send(666);
$generator->next();
$valueFromThirdIteration = $generator->current();
$generator->next();
$valueReturnedFromGenerator = $generator->getReturn();
                        
Staliśmy nad przepaścią, ale zrobiliśmy krok do przodu!
Władysław Gomułka
There is a thin line between awesome code and a total mess and I think coroutines sit exactly on that line
Nikita Popov

Natura PHP

stream_set_blocking($socket, 0)

PHP z zasady blokuje strumienie.

To oznacza, że jest mnóstwo kodu do napisania...

Natura PHP

Możemy korzystać z nieblokujących strumieni:

  • Guzzle 6
  • React
  • mysqlnd
  • pg_send_query()
  • niczym nieograniczona radosna twórczość własna

swoole

Asynchroniczne rozszerzenie PHP:

  • wygląda na to, że potrafi wiele
  • PHP 的异步
  • bardzo proste abstrakcje

Jakie są alternatywy?

NodeJS

Tu wszystko jest nieblokujące i nic nie jest blokujące
(no chyba, że naprawdę nam zależy...)

Jakie są alternatywy?

JVM

  • Futures w Javie
  • Aktorzy i react w Scali
  • Akka i pochodne

Jakie są alternatywy?

Hack

  • MCRouter
  • AsyncMysql
  • asio
  • async i await

Jakie są alternatywy?

.Net

  • operacje protokołu HTTP
  • operacje na plikach
  • przechwytywanie obrazu i dźwięku
  • tworzenie i odczytywanie plików graficznych
  • async i await

Co robić, jak żyć?

  • zastanowić się, czy warto podnosić złożoność kodu
  • przemyśleć wybór platformy
  • zastanowić się raz jeszcze, czy na pewno warto podnosić złożoność kodu
  • napisać mikrousługę w NodeJS, Scali/Javie, Hacku albo .Net

Źródła

?

wierzę w testy jednostkowe, testy funkcjonalne,
ciągłą integrację, buildów automatyzację,
generowanie binarek i ich szybkie wdrażanie,
maszyny wirtualne, procesy preforkowalne,
połączenia trwałe, destruktory wspaniałe,
zasobów uwalnianie, zmiennych unsetowanie,
uwielbiam przesłanianie, tęsknię za przeciążaniem

imitowane obiekty już oczekują na testy,
usługi me atomowe namespace'y mają gotowe,
finalne moje klauzule łatają już każdą dziurę,
iteratory czekają, generatory podają,
metody ponotowane już listenerom są znane,
zdarzenia się wyzwalają, kernelom opowiadają,
wszystko się trigeruje, pamięci ciągle brakuje,
incepcja postępująca i tak stepuję bez końca.