Library for PHP to execute functions asynchronously https://packagist.org/packages/joopschilder/php-async
Go to file
2019-01-22 17:44:32 +01:00
src Updated README and fixed responsibility mixup. Also, added stability by cleaning the memory block on startup. 2019-01-22 17:44:32 +01:00
.gitignore Actually stable version. 2019-01-17 00:49:53 +01:00
composer.json Updated README and fixed responsibility mixup. Also, added stability by cleaning the memory block on startup. 2019-01-22 17:44:32 +01:00
README.md Updated README and fixed responsibility mixup. Also, added stability by cleaning the memory block on startup. 2019-01-22 17:44:32 +01:00

joopschilder/php-async

Introduction

This package provides functions to run callables asynchronously in PHP. Return values are shared via System-V shared memory.
To use this package, you'll need PHP >= 7.0.0 with ext-sysvshm and ext-pcntl.
You should consider the state of this package to be experimental.

Note: This package should not be used in a CGI environment. The key that is used to access the block of shared memory is created based on the inode information of one of the source files. This means that, whenever multiple instances (processes) from the same project source are created, they will try to use the same block of memory and collisions will occur. I might swap the ftok() call for a random string generator somewhere down the road.

Note: It is possible (but discouraged) to change the amount of available shared memory. If you wish to do so, it's as simple as calling either Runtime::_setSharedMemorySizeMB(<amount of MB>); or Runtime::_setSharedMemorySizeB(<amount of bytes>);.
If you want to use 32MB for example, call Runtime::_setSharedMemorySizeMB(32);.
Be sure to make this call before using any of the asynchronous functionalities.

Installation

This package is available on Packagist and can be installed using Composer:

$ composer require joopschilder/async-php

It's also possible to manually add it to your composer.json:

{
    "require": {
        "joopschilder/php-async": "dev-master"
    }
}

Usage

Functions

The library exposes three functions in the global namespace that provide indirect access to the class Asynchronous:

  • async(callable $function, ...$parameters) to run something asynchronously, giving back a Promise;
  • async_wait_all() to wait for all currently running jobs to finish;
  • async_reap_zombies() to clean up any zombie processes during runtime if any exist;

Promises

Whenever you call async(...), a Promise instance is returned.
A Promise is considered to be resolved when the function it belongs to returned a value or finished execution. To block execution until a promise is resolved, simply call the resolve() method on the promise. It's possible to check whether the promise has been resolved in a non-blocking way by calling the isResolved() method.
You can actually return anything that is serializable in PHP: objects, arrays, strings, you name it.

<?php
require_once __DIR__ . '/vendor/autoload.php';

$promise = async(function() {
    sleep(random_int(1, 5));
    return getmypid();
});

// ... do some other work

$promise->resolve();
$pid = $promise->getValue();

The shutdown handler and destructors should take care of the cleanup.

Asynchronous curl requests

... though you should probably look into curl multi handles for this: curl_multi_init().

<?php
require_once __DIR__ . '/vendor/autoload.php';

// Create the body for the process...
$process = function(string $url) {
    $handle = curl_init($url);
    curl_setopt($handle, CURLOPT_FOLLOWLOCATION, 1);
    curl_setopt($handle, CURLOPT_RETURNTRANSFER, 1);
    $response = curl_exec($handle);
    curl_close($handle);
    file_put_contents(uniqid('download_'), $response);
};

// Define some urls we want to download...
$urls = [
    'example.com',
    'example2.com',
    'some.other.domain'
];

// And there we go.
foreach($urls as $url)
    async($process, $url);

That's all there is to it.

Tips

If you're on a UNIX system, you can make use of the tools ipcs and ipcrm to monitor and manage the shared memory blocks.
To track what's happening in real time, I like to use:

$ watch -n 1 "ipcs -m --human && ipcs -m -p && ipcs -m -t && ipcs -m -u"


To clean all 'unused' shared memory blocks (they might remain resident in RAM if your program terminated unexpectedly):

$ ipcrm -a

What's next?

  • Improving stability
  • Add an explaining diagram