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.
Fund package maintenance!
Requires
- php: >=8.1
- ext-voyager: >=8.2.1
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
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
Closureas 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
