betterde/voyager

A Composer package wrapper for the Voyager PHP extension, providing runtime function and method redefinition APIs for debugging, hot-fixing, instrumentation, and live system diagnostics.

Maintainers

Package info

github.com/betterde/voyager-php

pkg:composer/betterde/voyager

Statistics

Installs: 1

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

8.2.1 2026-05-01 18:53 UTC

This package is auto-updated.

Last update: 2026-05-02 05:46:34 UTC


README

Voyager PHP Wrapper is a Composer package for the voyager PHP extension. It adds auto-loadable PHP stubs and a small object-oriented facade around the native runtime function and method redefinition APIs.

The underlying extension lives in /Users/George/Develop/C/voyager and provides the native functions:

  • voyager_function_redefine()
  • voyager_method_redefine()

This package exposes those functions to Composer projects and provides Betterde\Voyager\Debug for a cleaner call style.

How It Works

How Voyager replaces code with Zend HashTables

Features

  • Redefine existing user-defined functions at runtime.
  • Redefine existing methods on user-defined classes at runtime.
  • Use closures directly for replacement implementations.
  • Use string argument/body mode for dynamic code generation.
  • Preserve a Composer-friendly developer experience with stubs and PSR-4 autoloading.
  • Provide readable flag constants for method visibility/static/reference behavior.

Requirements

  • PHP 8.1 or higher
  • Composer
  • The voyager PHP extension installed and enabled

The Composer package requires ext-voyager, so composer install will fail if the extension is not available to the PHP binary Composer is using.

Installing the Native Extension

Build and install the C extension first:

git clone https://github.com/betterde/voyager
cd voyager
phpize
./configure --enable-voyager
make
make install

Then enable it in your php.ini:

extension=voyager

You can also load it temporarily for a single command:

php -d extension=voyager.so your-script.php

Verify that PHP can see the extension:

php -m | grep voyager

Installing This Package

Install the wrapper with Composer:

composer require betterde/voyager

For local development from this repository:

composer install

Usage

Redefine a Function

<?php

use Betterde\Voyager\Debug;

require __DIR__ . '/vendor/autoload.php';

function greet(string $name): string
{
    return "Hello, {$name}!";
}

echo greet('World'); // Hello, World!

Debug::functionRedefine('greet', function (string $name): string {
    return "Hi, {$name}! This function was redefined.";
});

echo greet('World'); // Hi, World! This function was redefined.

Redefine a Method

<?php

use Betterde\Voyager\Debug;

require __DIR__ . '/vendor/autoload.php';

class UserService
{
    public function findUser(int $id): array
    {
        return ['id' => $id, 'name' => 'database user'];
    }
}

$service = new UserService();

Debug::methodRedefine(UserService::class, 'findUser', function (int $id): array {
    return ['id' => $id, 'name' => 'debug user'];
});

var_dump($service->findUser(1));

Use $this in a Redefined Method

<?php

use Betterde\Voyager\Debug;

class Counter
{
    public int $value = 0;

    public function increment(): int
    {
        return ++$this->value;
    }
}

$counter = new Counter();

Debug::methodRedefine(Counter::class, 'increment', function (): int {
    $this->value += 10;

    return $this->value;
});

echo $counter->increment(); // 10
echo $counter->increment(); // 20

String Body Mode

The native extension also supports defining the replacement with an argument list string and a code body string. The Debug facade keeps that mode available.

<?php

use Betterde\Voyager\Debug;

function normalize_name(string $name): string
{
    return trim($name);
}

Debug::functionRedefine(
    'normalize_name',
    '$name',
    'return strtoupper(trim($name));',
);

echo normalize_name(' voyager '); // VOYAGER

For methods, pass optional flags as the fifth argument:

Debug::methodRedefine(
    UserService::class,
    'findUser',
    '$id',
    'return ["id" => $id, "name" => "generated user"];',
    Debug::FLAG_PUBLIC,
);

API

Betterde\Voyager\Debug::functionRedefine()

public static function functionRedefine(
    string $functionName,
    Closure|string $closureOrArgs,
    ?string $code = null,
    ?bool $returnByReference = null,
    ?string $docComment = null,
): bool

Redefines an existing function.

  • Closure mode: pass a Closure as the second argument.
  • String mode: pass an argument list string as the second argument and a PHP code body string as the third argument.

Betterde\Voyager\Debug::methodRedefine()

public static function methodRedefine(
    string $className,
    string $methodName,
    Closure|string $closureOrArgs,
    ?string $code = null,
    int $flags = 0,
    ?string $docComment = null,
): bool

Redefines an existing class method.

Available facade flags:

Constant Value Meaning
Debug::FLAG_PUBLIC 0x0100 Public method
Debug::FLAG_PROTECTED 0x0200 Protected method
Debug::FLAG_PRIVATE 0x0400 Private method
Debug::FLAG_STATIC 0x0001 Static method
Debug::FLAG_RETURN_REFERENCE 0x0800 Return by reference

Flags may be combined with bitwise OR:

Debug::FLAG_PUBLIC | Debug::FLAG_STATIC

Native Functions

This package also autoloads stubs for the native functions, so IDEs and static analysis tools can understand them:

voyager_function_redefine(
    string $function_name,
    Closure|string $closure_or_args,
    ?string $code_or_doc_comment = null,
    ?bool $return_by_reference = null,
    ?string $doc_comment = null
): bool
voyager_method_redefine(
    string $class_name,
    string $method_name,
    Closure|string $closure_or_args,
    int|string|null $code_or_flags = null,
    int|string|null $flags_or_doc_comment = null,
    ?string $doc_comment = null
): bool

Important Notes

Voyager changes PHP runtime behavior at the Zend engine level. Treat it as a debugging, diagnostics, instrumentation, and emergency hot-fix tool.

  • Redefine only functions and methods you own and understand.
  • Avoid redefining code that is currently on the call stack.
  • Be careful with long-running workers, preloaded code, opcache, and production traffic.
  • Prefer narrow, reversible changes and add logging around live diagnostics.
  • Do not use runtime redefinition as a substitute for a normal deployment flow.

Project Structure

.
├── composer.json
├── src/
│   └── Debug.php
└── stubs/
    └── voyager_functions.php