Skip to content

Commit f0f4f2f

Browse files
committed
Remove async functionality and related tests; update version to 3.1.0
1 parent 9c95e37 commit f0f4f2f

10 files changed

+205
-689
lines changed

README.md

+60-188
Original file line numberDiff line numberDiff line change
@@ -7,246 +7,118 @@
77
[![Check & fix styling](https://github.com/Thavarshan/matrix/actions/workflows/laravel-pint.yml/badge.svg)](https://github.com/Thavarshan/matrix/actions/workflows/laravel-pint.yml)
88
[![Total Downloads](https://img.shields.io/packagist/dt/jerome/matrix.svg)](https://packagist.org/packages/jerome/matrix)
99

10-
**Matrix** is a PHP library that brings asynchronous, non-blocking task execution to PHP. Inspired by the JavaScript `async`/`await` pattern, Matrix leverages `pcntl_fork()` and ReactPHP promises to run tasks in parallel child processes. The result is a clean, promise-based API enabling concurrency without blocking your main code.
10+
Matrix is a PHP library that provides asynchronous, non-blocking functionality inspired by JavaScript's `async`/`await` syntax. With Matrix, you can handle asynchronous tasks and manage concurrency in PHP using promises and a familiar, intuitive API.
1111

12-
Matrix’s `async()` helper returns a promise-like object (thanks to `AsyncPromise`) which provides familiar `then()` and `catch()` methods, making asynchronous tasks feel as natural as JavaScript promises.
12+
## Why Matrix?
1313

14-
---
15-
16-
## **Why Matrix?**
17-
18-
Matrix aims to simplify parallel execution of potentially long-running or CPU-intensive operations in PHP. Instead of waiting for each task to complete sequentially, you can spawn child processes, run tasks concurrently, and handle their results asynchronously.
14+
Matrix simplifies the execution of asynchronous tasks in PHP by combining promises with ReactPHP's event loop. It allows for non-blocking execution, error propagation, and easy integration with existing PHP projects.
1915

20-
- **JavaScript-like async API**: The `async()` function returns promise-like objects with `then()` and `catch()`.
21-
- **Built on `pcntl_fork()` and ReactPHP**: Achieves true parallelism by using multiple processes, integrated with React’s event loop for non-blocking IO.
22-
- **Error Propagation**: Exceptions thrown in child processes are serialized and rethrown as exceptions in the parent, simplifying error handling.
23-
- **Non-blocking Concurrency**: Your main code continues running while tasks proceed in parallel, improving efficiency for CPU-bound or blocking tasks.
16+
### Key Features
2417

25-
### **Key Features**
18+
- **JavaScript-like API**: Use `async()` and `await()` for straightforward asynchronous programming.
19+
- **Built with ReactPHP**: Ensures non-blocking execution using ReactPHP's event loop.
20+
- **Error Handling**: Catch and handle exceptions seamlessly with `.catch()` or `try-catch`.
21+
- **Automatic Loop Management**: The event loop runs automatically to handle promise resolution.
2622

27-
- **Familiar `then()` and `catch()` interface** for handling asynchronous results.
28-
- **Parallel execution via processes**: Offload heavy tasks to separate child processes.
29-
- **Automatic error forwarding**: Rethrow child exceptions in the parent for consistent error handling.
30-
- **Easy integration with existing code**: Just wrap your function calls with `async()` and chain handlers as needed.
31-
32-
---
33-
34-
## **Installation**
23+
## Installation
3524

36-
Install Matrix via Composer:
25+
Install via Composer:
3726

3827
```bash
3928
composer require jerome/matrix
4029
```
4130

42-
Ensure the following PHP extensions are enabled:
31+
Ensure the following extensions are enabled:
4332

44-
- `pcntl`
4533
- `sockets`
4634

47-
Matrix also relies on ReactPHP promises and event loop, installed automatically via Composer.
48-
49-
---
50-
51-
## ⚠️ **Important Notice: Avoid Using `pcntl_fork()` in Web Server Environments**
52-
53-
**Matrix PHP** leverages `pcntl_fork()` to achieve asynchronous, parallel task execution. However, it's crucial to understand the limitations and best practices when deploying Matrix PHP, especially concerning the use of `pcntl_fork()` within different server environments.
54-
55-
### 🚫 **Why Avoid `pcntl_fork()` in Web Servers?**
56-
57-
- **PHP Thread Safety:**
58-
PHP is **not inherently thread-safe**. Utilizing `pcntl_fork()` within a multi-threaded web server environment, such as Apache configured with the **Worker MPM**, can lead to unpredictable behavior, resource leaks, and potential server instability.
59-
60-
- **Apache Worker MPM Issues:**
61-
Running PHP as a module in an Apache installation configured with the **Worker MPM** is **not recommended**. The **Worker MPM** uses multiple threads to handle requests, which conflicts with PHP's thread safety limitations.
62-
63-
### **Recommended Setup**
64-
65-
- **Use Prefork MPM with Apache:**
66-
For a stable and compatible environment, it's advised to configure Apache with the **Prefork MPM** module when integrating with PHP. The **Prefork MPM** handles each request in a separate process rather than threads, aligning with PHP's operational model and avoiding thread safety issues.
67-
68-
**References:**
69-
- [PHP Manual: Installing PHP on Unix systems with Apache](http://php.net/install.unix.apache2.php)
70-
- [Steve Kallestad's Guide on Apache Worker MPM with PHP](http://www.stevekallestad.com/blog/apache_worker_mpm_with_php.html)
35+
Matrix relies on ReactPHP promises and event loop, which are installed automatically via Composer.
7136

72-
### 🔧 **Best Practices**
37+
## API Overview
7338

74-
- **Command-Line and Background Processes:**
75-
Matrix PHP is best suited for **command-line interfaces (CLI)**, background workers, or daemonized applications where process forking is safe and manageable.
39+
### `async(callable $callable): PromiseInterface`
7640

77-
- **Web Applications:**
78-
For web-based deployments, prefer using **event-driven asynchronous models** provided by libraries like **ReactPHP**, which do not rely on process forking and are more compatible with typical web server configurations.
41+
Wraps a callable into an asynchronous function that returns a promise.
7942

80-
### 🛡️ **Conclusion**
81-
82-
To ensure the stability, performance, and reliability of your PHP applications using **Matrix PHP**, adhere to the following guidelines:
83-
84-
1. **Avoid using `pcntl_fork()` in web server contexts**, especially with multi-threaded server modules like Apache's Worker MPM.
85-
2. **Configure Apache with Prefork MPM** when integrating with PHP to prevent thread safety issues.
86-
3. **Leverage event-driven asynchronous approaches** (e.g., ReactPHP) for web applications to achieve concurrency without the complexities of process forking.
87-
88-
By following these best practices, you can harness the full potential of **Matrix PHP** while maintaining a robust and stable server environment.
89-
90-
---
91-
92-
## **Asynchronous API Inspired by JavaScript**
93-
94-
Matrix provides a helper called `async()` that returns an `AsyncPromise`, mimicking JavaScript’s promise usage.
95-
96-
**Example:**
43+
Example:
9744

9845
```php
9946
use function async;
10047

101-
async(fn () => 'Task result')
102-
->then(fn($result) => print($result . PHP_EOL))
103-
->catch(fn($e) => print("Error: " . $e->getMessage()));
104-
```
48+
$func = async(fn () => 'Success');
10549

106-
**What happens here?**
50+
$func->then(fn ($value) => echo $value) // Outputs: Success
51+
->catch(fn ($e) => echo 'Error: ' . $e->getMessage());
52+
```
10753

108-
- `async()` forks a new child process using `AsyncProcessManager`.
109-
- The child runs your callable and serializes the result or error.
110-
- The parent listens on a socket via ReactPHP’s event loop.
111-
- When the child finishes, the parent promise is resolved or rejected.
112-
- The `AsyncPromise` provides a `.then()` for success and `.catch()` for errors, similar to JS promises.
54+
### `await(PromiseInterface $promise): mixed`
11355

114-
### **Error Handling**
56+
Awaits the resolution of a promise and returns its value.
11557

116-
If the child task throws an exception, `catch()` handles it gracefully:
58+
Example:
11759

11860
```php
119-
async(fn () => throw new RuntimeException('Something went wrong'))
120-
->then(fn($res) => print("Not called"))
121-
->catch(fn($e) => print("Caught error: " . $e->getMessage() . PHP_EOL));
61+
use function await;
62+
63+
try {
64+
$result = await(async(fn () => 'Success'));
65+
echo $result; // Outputs: Success
66+
} catch (\Throwable $e) {
67+
echo 'Error: ' . $e->getMessage();
68+
}
12269
```
12370

124-
---
125-
126-
## **Under the Hood**
127-
128-
- **Forked Processes**: Calling `async()` triggers `AsyncProcessManager::fork()`, which spawns a new child process. This child executes your given callable in isolation.
129-
- **IPC (Inter-Process Communication)**: Results or errors are serialized and sent back to the parent via a socket pair, using `stream_socket_pair()`.
130-
- **ReactPHP Integration**: The parent uses `React\EventLoop` to add a read stream listener. This ensures your code never blocks, waiting for the child process’s response asynchronously.
131-
- **Promises**: The `AsyncPromise` class wraps React’s `PromiseInterface` to provide a `.then()` and `.catch()` interface, making asynchronous code more intuitive.
132-
133-
---
134-
135-
## **Examples**
71+
## Examples
13672

137-
**Running Multiple Tasks in Parallel:**
73+
### Running Asynchronous Tasks
13874

13975
```php
140-
async(function () {
141-
usleep(500000); // Simulate half-second work
142-
return "Task A done";
143-
})->then(fn($res) => print("$res\n"));
76+
$promise = async(fn () => 'Task Completed');
14477

145-
async(function () {
146-
usleep(500000); // Another half-second task
147-
return "Task B done";
148-
})->then(fn($res) => print("$res\n"));
149-
150-
// Both tasks start around the same time, taking ~0.5s total if concurrent, rather than ~1s if sequential.
78+
$promise->then(fn ($value) => echo $value) // Outputs: Task Completed
79+
->catch(fn ($e) => echo 'Error: ' . $e->getMessage());
15180
```
15281

153-
**Transforming Results with Thenable Chain:**
82+
### Await Syntax
15483

15584
```php
156-
async(fn() => 21)
157-
->then(fn($val) => $val * 2) // 42
158-
->then(fn($val) => "The answer is $val") // "The answer is 42"
159-
->then(fn($str) => print($str . PHP_EOL))
160-
->catch(fn($e) => print("Error: " . $e->getMessage()));
85+
try {
86+
$result = await(async(fn () => 'Finished Task'));
87+
echo $result; // Outputs: Finished Task
88+
} catch (\Throwable $e) {
89+
echo 'Error: ' . $e->getMessage();
90+
}
16191
```
16292

163-
---
164-
165-
## **Testing and Integration**
166-
167-
You can write integration tests to confirm asynchronous behavior. For example, test that two half-second tasks complete in ~0.5s total, ensuring concurrency works as intended:
93+
### Handling Errors
16894

16995
```php
170-
it('runs tasks concurrently', function () {
171-
$start = microtime(true);
172-
173-
$p1 = async(fn() => (usleep(500000), 'Done A'));
174-
$p2 = async(fn() => (usleep(500000), 'Done B'));
96+
$promise = async(fn () => throw new \RuntimeException('Task Failed'));
17597

176-
$bothDone = React\Promise\all([
177-
$p1->then(fn($r) => $r),
178-
$p2->then(fn($r) => $r),
179-
]);
180-
181-
$results = awaitPromise($bothDone);
182-
$elapsed = microtime(true) - $start;
183-
184-
expect($results)->toEqual(['Done A', 'Done B']);
185-
expect($elapsed)->toBeLessThan(1.0); // Confirms concurrency
186-
});
98+
$promise->then(fn ($value) => echo $value)
99+
->catch(fn ($e) => echo 'Caught Error: ' . $e->getMessage()); // Outputs: Caught Error: Task Failed
187100
```
188101

189-
*(`awaitPromise()` is a helper test function that runs the event loop until the given promise resolves.)*
102+
## How It Works
190103

191-
---
192-
193-
## **Common Issues and Troubleshooting**
194-
195-
- **Sequential Execution Instead of Parallel**:
196-
Ensure `pcntl` and `sockets` extensions are enabled. Also, ensure you are not blocking the main thread. The event loop must run for asynchronous behavior.
197-
198-
- **No Interleaved Output**:
199-
Console output might be buffered. If you rely on interleaving stdout, consider using `fflush(STDOUT)` in the child, or rely on timing tests to confirm concurrency.
200-
201-
- **Exception Types**:
202-
If the child throws an unknown exception type not autoloaded in the parent, it defaults to `RuntimeException`. Basic details (message, file, line, trace) are preserved.
104+
1. **Event Loop Management**: The `async()` function ensures the event loop runs until the promise is resolved or rejected.
105+
2. **Promise Interface**: Promises provide `then` and `catch` for handling success and errors.
106+
3. **Synchronous Await**: The `await()` function allows synchronous-style code for promise resolution.
203107

204108
---
205-
206-
## **API Reference**
207109

208-
### Global `async()` function
209-
210-
```php
211-
function async(callable $callable): AsyncPromise
212-
```
110+
## Testing
213111

214-
- Accepts a callable to run asynchronously.
215-
- Returns `AsyncPromise`, providing `.then()` and `.catch()`.
112+
Run the tests to ensure everything is working as expected:
216113

217-
### `Matrix\AsyncProcessManager`
218-
219-
- `fork(callable $callable)`: Forks a child process to run the callable. Returns a `React\Promise\PromiseInterface`.
220-
221-
### `Matrix\AsyncPromise`
222-
223-
- `then(callable $onFulfilled): self`
224-
Attaches a success handler.
225-
- `catch(callable $onRejected): self`
226-
Attaches an error handler (alias to React’s `otherwise()`).
227-
228-
---
229-
230-
## **Contributing**
231-
232-
We welcome contributions. To contribute:
233-
234-
1. Fork the repository.
235-
2. Create a new feature branch (`git checkout -b feature/my-feature`).
236-
3. Commit your changes (`git commit -m 'Add new feature'`).
237-
4. Push to the branch (`git push origin feature/my-feature`).
238-
5. Open a pull request against `main`.
239-
240-
---
241-
242-
## **License**
114+
```bash
115+
composer test
116+
```
243117

244-
Matrix is licensed under the MIT License. See the [LICENSE](LICENSE.md) file for more information.
118+
## Contributing
245119

246-
---
247-
248-
## **Authors**
120+
Contributions are welcome! Fork the repository and create a pull request.
249121

250-
- **[Jerome Thayananthajothy]** - *Initial Work* - [Thavarshan](https://github.com/Thavarshan)
122+
## License
251123

252-
See the [contributors](https://github.com/Thavarshan/matrix/contributors) who participated in this project.
124+
Matrix is licensed under the MIT License.

composer.json

+4-6
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "jerome/matrix",
33
"description": "An unparalleled PHP asynchronous experience, offering genuine concurrency and fiber-based task management.",
4-
"version": "3.0.1",
4+
"version": "3.1.0",
55
"type": "library",
66
"license": "MIT",
77
"authors": [
@@ -35,11 +35,8 @@
3535
"symfony/var-dumper": "^7.2"
3636
},
3737
"autoload": {
38-
"psr-4": {
39-
"Matrix\\": "src/Matrix/"
40-
},
4138
"files": [
42-
"src/Matrix/async.php"
39+
"src/functions.php"
4340
]
4441
},
4542
"autoload-dev": {
@@ -50,7 +47,8 @@
5047
"scripts": {
5148
"analyse": "chmod +x bin/analysis.sh && ./bin/analysis.sh",
5249
"lint": "chmod +x bin/lint.sh && ./bin/lint.sh",
53-
"fix": "chmod +x bin/fix.sh && ./bin/fix.sh"
50+
"fix": "chmod +x bin/fix.sh && ./bin/fix.sh",
51+
"test": "vendor/bin/pest"
5452
},
5553
"config": {
5654
"sort-packages": true,

0 commit comments

Comments
 (0)