monkeyscloud/monkeyslegion-process

Subprocess execution for MonkeysLegion v2 — async, pools, pipelines, signals, PHP 8.4 property hooks

Maintainers

Package info

github.com/MonkeysCloud/MonkeysLegion-Process

pkg:composer/monkeyscloud/monkeyslegion-process

Statistics

Installs: 0

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

1.0.0 2026-05-17 04:33 UTC

This package is auto-updated.

Last update: 2026-05-17 04:35:05 UTC


README

Enterprise-grade subprocess execution for the MonkeysLegion v2 framework.

Secure command building • sync/async execution • process pools • pipelines • real-time output streaming • signal handling • PHP 8.4 property hooks • PHPStan Level 9

Installation

composer require monkeyscloud/monkeyslegion-process

Requirements: PHP ≥ 8.4 · ext-pcntl

Quick Start

use MonkeysLegion\Process\Process;

// Run a command synchronously
$process = new Process(['ls', '-la']);
$result = $process->run();

echo $result->output();       // stdout
echo $result->exitCode();     // 0
var_dump($result->successful); // true (property hook)

Process Builder (Fluent API)

use MonkeysLegion\Process\ProcessBuilder;

$result = ProcessBuilder::create('npm', 'install')
    ->withCwd('/app')
    ->withEnv(['NODE_ENV' => 'production'])
    ->withTimeout(120)
    ->withIdleTimeout(30)
    ->run();

if ($result->failed) {
    echo $result->errorOutput();
}

Safe Command Builder

use MonkeysLegion\Process\CommandLine;

// Injection-safe — arguments are escaped automatically
$cmd = CommandLine::create('git')
    ->addArg('commit')
    ->addOption('-m', 'Initial commit')
    ->addIf($signed, '--gpg-sign');

$process = new Process($cmd->toArray());
$process->mustRun(); // Throws on failure

Shell Commands (Pipes & Redirects)

// Static factory for raw shell commands
$process = Process::fromShellCommandline('cat data.csv | grep error | wc -l');
$result = $process->run();

// Or via builder
$result = ProcessBuilder::shell('ls -la | grep .php')->run();

Output Control

// Clear buffers during long-running processes
$process->clearOutput();
$process->clearErrorOutput();

// Disable capture entirely (saves memory for high-output processes)
$process = new Process(['mysqldump', 'mydb']);
$process->disableOutput();
$process->run();

// Re-enable when needed
$process->enableOutput();

// Config getters
$process->getWorkingDirectory(); // ?string
$process->getEnv();              // ?array
$process->getTimeout();          // ?float
$process->getIdleTimeout();      // ?float

Async Execution

$process = new Process(['vendor/bin/phpunit']);
$process->start();

// Do other work...

$result = $process->wait();

Real-Time Output Callback

$process = new Process(['npm', 'run', 'build']);
$process->run(function (string $type, string $data): void {
    echo "[{$type}] {$data}";
});

Iterator Streaming

use MonkeysLegion\Process\Enum\OutputType;

$process = new Process(['tail', '-f', '/var/log/app.log']);
$process->setTimeout(null)->start();

foreach ($process as [$type, $data]) {
    if ($type === OutputType::Stderr) {
        break;
    }
    echo $data;
}

$process->stop();

Stdin Input

use MonkeysLegion\Process\InputStream;

// String input
$process = new Process(['wc', '-l']);
$process->setInput("line 1\nline 2\nline 3");
$result = $process->run();
echo $result->output(); // 3

// Streaming input
$input = new InputStream();
$input->write("chunk 1\n");
$input->write("chunk 2\n");
$input->close();

$process = new Process(['cat'], input: $input);
$process->run();

Process Pools (Concurrent)

use MonkeysLegion\Process\Pool\ProcessPool;

$results = ProcessPool::create(maxConcurrent: 3)
    ->add('assets', ['npm', 'run', 'build'])
    ->add('tests', ['vendor/bin/phpunit'])
    ->add('lint', ['vendor/bin/phpstan', 'analyse'])
    ->wait();

if ($results->successful) {
    echo "All passed!\n";
} else {
    foreach ($results->failures() as $name => $result) {
        echo "{$name} failed: {$result->errorOutput()}\n";
    }
}

// Per-result access
$results->get('tests')->output();

Completion Callback

ProcessPool::create()
    ->add('a', ['echo', 'hello'])
    ->add('b', ['echo', 'world'])
    ->onComplete(function (string $name, ProcessResult $result): void {
        echo "{$name} finished with code {$result->exitCode()}\n";
    })
    ->wait();

Pipelines (stdout → stdin chaining)

use MonkeysLegion\Process\Pipeline\ProcessPipeline;

$result = ProcessPipeline::create()
    ->pipe(['cat', 'access.log'])
    ->pipe(['grep', '-i', 'error'])
    ->pipe(['wc', '-l'])
    ->run();

echo $result->output();         // Final output from wc
echo $result->finalOutput;      // Same (property hook)
var_dump($result->successful);  // true if all stages passed

// Per-stage debugging
$stage0 = $result->stage(0);
echo $stage0->exitCode();

PHP Subprocess

use MonkeysLegion\Process\PhpProcess;

$process = new PhpProcess('<?php echo PHP_VERSION; ?>');
$result = $process->run();
echo $result->output(); // e.g. "8.4.1"

Signals

use MonkeysLegion\Process\Enum\Signal;

$process = new Process(['sleep', '60']);
$process->start();

// Send SIGTERM
$process->signal(Signal::SIGTERM);

// Or by number
$process->signal(15);

Timeouts

use MonkeysLegion\Process\Exception\ProcessTimedOutException;

$process = new Process(['sleep', '30']);
$process->setTimeout(5);       // Kill after 5s total
$process->setIdleTimeout(2);   // Kill after 2s with no output

try {
    $process->run();
} catch (ProcessTimedOutException $e) {
    echo $e->isIdleTimeout() ? 'Idle timeout' : 'General timeout';
}

Exception Handling

use MonkeysLegion\Process\Exception\{
    ProcessException,
    ProcessFailedException,
    ProcessTimedOutException,
    ProcessSignaledException,
};

try {
    $result = $process->mustRun();
} catch (ProcessFailedException $e) {
    echo $e->getProcessExitCode();
    echo $e->getProcessOutput();
    echo $e->getProcessErrorOutput();
} catch (ProcessTimedOutException $e) {
    echo $e->getTimeout();
} catch (ProcessSignaledException $e) {
    echo $e->getSignal()?->label();
}

// Or check result manually
$result = $process->run();
$result->throwIfFailed();

Testing (Fakes)

use MonkeysLegion\Process\Testing\{FakeProcess, FakeProcessFactory, PendingProcessFake};

// Quick fakes
$fake = FakeProcess::success('build complete');
$fake = FakeProcess::failure('error occurred', exitCode: 1);

// Factory with pattern matching
$factory = FakeProcessFactory::make()
    ->fake('npm *', (new PendingProcessFake())
        ->withOutput('installed 42 packages')
        ->withExitCode(0))
    ->fake('phpstan *', (new PendingProcessFake())
        ->withErrorOutput('Found 3 errors')
        ->withExitCode(1));

$result = $factory->create(['npm', 'install'])->run();
echo $result->output(); // "installed 42 packages"

// Assertions
$factory->assertRan('npm *');
$factory->assertNotRan('rm *');

Executable Finder

use MonkeysLegion\Process\ExecutableFinder;

$php  = ExecutableFinder::findPhp();         // /usr/bin/php
$node = ExecutableFinder::find('node');       // /usr/local/bin/node
$git  = ExecutableFinder::find('git', '/usr/bin/git'); // With fallback

Property Hooks (PHP 8.4)

The package uses PHP 8.4 property hooks for live state access:

$process->isRunning;    // bool (hook)
$process->isSuccessful; // bool (hook)
$process->statusLabel;  // string (hook)

$result->successful;    // bool (hook)
$result->failed;        // bool (hook)

$poolResults->successful; // bool (hook) — all passed
$poolResults->failed;     // bool (hook) — any failed
$poolResults->count;      // int (hook)

$pipelineResult->successful;  // bool (hook)
$pipelineResult->finalOutput; // string (hook)

DI Integration

use MonkeysLegion\Process\Provider\ProcessProvider;

// Register in your container
$factory = ProcessProvider::register($config['process'] ?? []);

// Use factory
$process = $factory->create(['git', 'status']);
$result = $process->run();

Configuration (config/process.mlc)

process {
    default_timeout  = ${PROCESS_TIMEOUT:-60}
    idle_timeout     = ${PROCESS_IDLE_TIMEOUT:-0}
    default_cwd      = ${PROCESS_CWD:-}

    pool {
        max_concurrent = ${PROCESS_POOL_MAX:-5}
    }

    env {
        inherit = true
    }
}

License

MIT © 2026 MonkeysCloud Team