Skip to main content

PHP SDK

The official PHP SDK for Rexec provides a modern, PSR-compatible interface for Terminal as a Service.

Requirements

  • PHP 8.1 or later
  • Composer

Installation

composer require pipeopshq/rexec

Quick Start

<?php

require 'vendor/autoload.php';

use Rexec\RexecClient;

// Create client
$client = new RexecClient('https://your-instance.com', 'your-api-token');

// Create a container
$container = $client->containers()->create('ubuntu:24.04');
echo "Created: {$container->id}\n";

// Start it
$client->containers()->start($container->id);

// Execute a command
$result = $client->containers()->exec($container->id, "echo 'Hello from PHP!'");
echo $result->stdout;

// Clean up
$client->containers()->delete($container->id);

Client Initialization

Basic Client

use Rexec\RexecClient;

$client = new RexecClient(
    'https://your-instance.com',
    'your-api-token'
);
The RexecClient class is defined at /home/daytona/workspace/source/sdk/php/src/RexecClient.php:18.

With Custom Options

$client = new RexecClient(
    'https://your-instance.com',
    'your-api-token',
    [
        'timeout' => 60,
        'verify' => true,
    ]
);
See the constructor at /home/daytona/workspace/source/sdk/php/src/RexecClient.php:35.

Container Operations

The Container service provides methods for managing sandboxed environments.

List Containers

$containers = $client->containers()->list();

foreach ($containers as $c) {
    echo "{$c->name}: {$c->status}\n";
}

Create Container

// Simple creation
$container = $client->containers()->create('ubuntu:24.04');

// With options
$container = $client->containers()->create('python:3.12', [
    'name' => 'my-python-sandbox',
    'environment' => ['PYTHONPATH' => '/app'],
    'labels' => ['project' => 'demo'],
]);

echo "Created: {$container->id}\n";

Get Container

$container = $client->containers()->get($containerId);
echo "Container {$container->name} is {$container->status}\n";

Start Container

$client->containers()->start($containerId);

Stop Container

$client->containers()->stop($containerId);

Delete Container

$client->containers()->delete($containerId);

Execute Commands

// Execute a simple command
$result = $client->containers()->exec($containerId, 'python --version');

if ($result->isSuccess()) {
    echo "Output: {$result->stdout}\n";
} else {
    echo "Error: {$result->stderr}\n";
}

// Execute with array command
$result = $client->containers()->exec(
    $containerId,
    ['python', '-c', 'print("Hello")']
);

File Operations

Manage files and directories within containers.

List Files

$files = $client->files()->list($containerId, '/app');

foreach ($files as $file) {
    $type = $file->isDir ? 'DIR' : "{$file->size} bytes";
    echo "{$file->name} - {$type}\n";
}

Read File

$content = $client->files()->read($containerId, '/etc/hostname');
echo "Hostname: {$content}\n";

Write File

$client->files()->write(
    $containerId,
    '/app/script.py',
    "print('Hello!')"
);

Delete File

$client->files()->delete($containerId, '/tmp/scratch.txt');

Interactive Terminal

Connect to containers via WebSocket for real-time terminal access.

Basic Terminal Usage

use React\EventLoop\Loop;

$terminal = $client->terminal()->connect($containerId);

// Set up handlers
$terminal->onData(function ($data) {
    echo $data;
});

$terminal->onClose(function () {
    echo "Disconnected\n";
});

$terminal->onError(function ($e) {
    echo "Error: {$e->getMessage()}\n";
});

// Open connection
$terminal->open();

// Send commands
$terminal->write("ls -la\n");
$terminal->write("cd /app && python main.py\n");

// Resize terminal
$terminal->resize(120, 40);

// Run the event loop
Loop::run();

// Clean up
$terminal->close();

Terminal with Custom Size

$terminal = $client->terminal()->connect($containerId, [
    'cols' => 120,
    'rows' => 40,
]);

Advanced Examples

Run Script and Capture Output

function runScript(
    RexecClient $client,
    string $containerId,
    string $script
): string {
    $output = '';
    $terminal = $client->terminal()->connect($containerId);
    
    $terminal->onData(function ($data) use (&$output) {
        $output .= $data;
    });
    
    $terminal->open();
    $terminal->write("{$script}\nexit\n");
    
    Loop::run();
    
    return $output;
}

// Usage
$result = runScript(
    $client,
    $container->id,
    'apt update && apt install -y curl'
);
echo $result;

Batch Container Creation

use React\Promise\all;

function createBatch(
    RexecClient $client,
    int $count
): array {
    $promises = [];
    
    for ($i = 0; $i < $count; $i++) {
        $promises[] = $client->containers()->createAsync('ubuntu:24.04', [
            'name' => "worker-{$i}",
        ]);
    }
    
    return all($promises)->wait();
}

// Create 5 containers
$containers = createBatch($client, 5);
echo "Created " . count($containers) . " containers\n";

File Upload

function uploadFile(
    RexecClient $client,
    string $containerId,
    string $localPath,
    string $remotePath
): void {
    $content = file_get_contents($localPath);
    $client->files()->write($containerId, $remotePath, $content);
}

// Usage
uploadFile(
    $client,
    $container->id,
    './local-script.sh',
    '/home/script.sh'
);

Directory Sync

function syncDirectory(
    RexecClient $client,
    string $containerId,
    string $localDir,
    string $remoteDir
): void {
    $client->files()->mkdir($containerId, $remoteDir);
    
    $iterator = new RecursiveIteratorIterator(
        new RecursiveDirectoryIterator($localDir)
    );
    
    foreach ($iterator as $file) {
        if ($file->isFile()) {
            $relativePath = substr($file->getPathname(), strlen($localDir));
            $remotePath = $remoteDir . $relativePath;
            
            $content = file_get_contents($file->getPathname());
            $client->files()->write($containerId, $remotePath, $content);
            
            echo "Uploaded: {$remotePath}\n";
        }
    }
}

Error Handling

use Rexec\RexecException;

try {
    $container = $client->containers()->get('invalid-id');
} catch (RexecException $e) {
    if ($e->isApiError()) {
        echo "API error {$e->getStatusCode()}: {$e->getMessage()}\n";
    } else {
        echo "Network error: {$e->getMessage()}\n";
    }
}
The RexecException class provides methods to distinguish between API and network errors.

Type Declarations

The SDK uses PHP 8.1+ type declarations:
// All methods have full type hints
public function create(
    string $image,
    array $options = []
): Container;

// Return types are declared
public function list(): array;

// Nullable types where appropriate
public function get(string $id): ?Container;

WebSocket Requirements

For terminal functionality, you’ll need:
composer require react/event-loop
composer require ratchet/pawl

Testing

cd sdk/php
composer install
composer test

Source Code

View the full source code on GitHub:

License

MIT License - see LICENSE for details.

Build docs developers (and LLMs) love