diff --git a/.gitignore b/.gitignore
index cbfa2d6..53cec81 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
+/bin/
/vendor/
composer.lock
/.idea/
diff --git a/README.md b/README.md
index db5af5e..ca8958f 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,74 @@
-# php-async
+# joopschilder/php-async
Asynchronous PHP callable processing with return values via SysV shared memory.
Requires the `php-sysvshm` extension.
-Works with PHP from version 5.3 upwards due to `shm_attach(...)` returning a resource instead of an int.
+Works with PHP >= 5.3 due to `shm_attach(...)` returning a resource instead of an int.
+
+Note: This project is merely an experiment. It is, however, available on packagist.
+If you think your project lacks witchcraft combined with black magic, just add this package to your `composer.json`:
+```json
+{
+ "require": {
+ "joopschilder/php-async": "dev-master"
+ }
+}
+```
+
+
+## Examples
+
+### Promises
+You can actually return anything that is serializable in PHP: objects, arrays, strings, you name it.
+```php
+resolve();
+$pid = $promise->getValue();
+printf("Me (%d) and %d have worked very hard!\n", getmypid(), $pid);
+```
+The shutdown handler and destructors should take care of the rest.
+
+
+
+### Asynchronous curl requests
+... though you should probably look into curl multi handles for this: curl_multi_init() on PHP.net.
+
+```php
+isChild = true;
- $instance->attachShm();
+ Runtime::setChild();
+ $instance->_attachToShm();
try {
- $response = call_user_func_array($function, $parameters);
- if (is_resource($instance->shm))
- shm_put_var($instance->shm, $key, $response ?? Promise::RESPONSE_NONE);
+ $response = call_user_func($function, ...$parameters);
+ shm_put_var($instance->shm, $key, $response ?? Promise::RESPONSE_NONE);
exit(0);
} catch (\Throwable $throwable) {
- if (is_resource($instance->shm))
- shm_put_var($instance->shm, $key, Promise::RESPONSE_ERROR);
+ shm_put_var($instance->shm, $key, Promise::RESPONSE_ERROR);
exit(1);
}
@@ -89,7 +94,7 @@ class Asynchronous
/**
*
*/
- public static function cleanup()
+ public static function reap()
{
/*
* Iterate over all child process PIDs and check
@@ -106,7 +111,7 @@ class Asynchronous
/**
*
*/
- public static function awaitChildren()
+ public static function waitForChildren()
{
self::getInstance()->_awaitChildren();
}
@@ -142,9 +147,11 @@ class Asynchronous
* Use the filename as an identifier to create the
* System V IPC key.
*/
- $this->shmKey = ftok(__FILE__, 't');
+ if ($this->shmKey == null)
+ $this->shmKey = ftok(__FILE__, 't');
+
Promise::__setShmKey($this->shmKey);
- $this->attachShm();
+ $this->_attachToShm();
}
/**
@@ -179,12 +186,13 @@ class Asynchronous
return $this;
}
+
/**
* @return $this
*/
- private function attachShm()
+ private function _attachToShm()
{
- $this->shm = shm_attach($this->shmKey);
+ $this->shm = shm_attach($this->shmKey, self::BLOCK_SIZE_BYTES);
return $this;
}
@@ -224,7 +232,9 @@ class Asynchronous
* 9.999.999 is reached (Windows limit for
* shm keys).
*/
- self::$key = (++self::$key > 9999999) ? 0 : self::$key;
+ self::$key++;
+ if (self::$key > 99999999)
+ self::$key = 0;
return $promiseKey;
}
@@ -241,7 +251,7 @@ class Asynchronous
* The shutdown handler
*/
register_shutdown_function(function () use (&$instance) {
- if ($instance->isChild)
+ if (Runtime::isChild())
return;
$instance->_awaitChildren();
@@ -249,12 +259,16 @@ class Asynchronous
});
}
+ /**
+ *
+ */
public function __destruct()
{
- /*
- * To be sure - add destructor
- */
- self::removeShmBlock();
+ if (Runtime::isChild())
+ return;
+
+ $instance = self::getInstance();
+ $instance->_removeShmBlock();
}
}
\ No newline at end of file
diff --git a/src/Promise.php b/src/Promise.php
index 0f4937e..56125ca 100644
--- a/src/Promise.php
+++ b/src/Promise.php
@@ -27,7 +27,6 @@ class Promise
/** @var mixed|null */
private $value;
-
/**
* @param int $shmKey
*/
@@ -53,11 +52,14 @@ class Promise
}
/**
- * @return bool
+ * @return $this
*/
- public function shmValid()
+ private function attempt()
{
- return is_resource($this->shm);
+ if (shm_has_var($this->shm, $this->key))
+ $this->value = shm_get_var($this->shm, $this->key);
+
+ return $this;
}
/**
@@ -65,21 +67,18 @@ class Promise
*/
public function isResolved()
{
- if ($this->shmValid())
- return shm_has_var($this->shm, $this->key);
+ $this->attempt();
- return true;
+ return !is_null($this->value);
}
/**
* @return bool
*/
- public function isEmpty()
+ public function isVoid()
{
- $value = $this->getValue();
-
- return $value === self::RESPONSE_NONE || $value === null;
+ return $this->getValue() === self::RESPONSE_NONE;
}
@@ -97,12 +96,7 @@ class Promise
*/
public function getValue()
{
- if ($this->shmValid())
- return $this->isResolved() ? $this->resolve()->value : null;
-
- $this->value = self::RESPONSE_ERROR;
-
- return $this->value;
+ return $this->isResolved() ? $this->value : null;
}
@@ -113,14 +107,11 @@ class Promise
{
/*
* Actually block execution until a value is written to
- * the expected location of this Promise.
+ * the expected memory location of this Promise.
*/
while (!$this->isResolved())
usleep(1000);
- if (is_null($this->value) && $this->shmValid())
- $this->value = shm_get_var($this->shm, $this->key);
-
return $this;
}
@@ -137,11 +128,13 @@ class Promise
* garbage collector has noticed that there are no more
* references to this Promise instance.
*/
- if ($this->shmValid()) {
- if (shm_has_var($this->shm, $this->key))
- shm_remove_var($this->shm, $this->key);
+ if (Runtime::isChild())
+ return;
+
+ if (shm_has_var($this->shm, $this->key))
+ shm_remove_var($this->shm, $this->key);
+
+ shm_detach($this->shm);
- shm_detach($this->shm);
- }
}
}
\ No newline at end of file
diff --git a/src/Runtime.php b/src/Runtime.php
new file mode 100644
index 0000000..130a81d
--- /dev/null
+++ b/src/Runtime.php
@@ -0,0 +1,55 @@
+