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.
- 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.
- 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.
Build and install the C extension first:
git clone https://github.com/betterde/voyager
cd voyager
phpize
./configure --enable-voyager
make
make installThen enable it in your php.ini:
extension=voyagerYou can also load it temporarily for a single command:
php -d extension=voyager.so your-script.phpVerify that PHP can see the extension:
php -m | grep voyagerInstall the wrapper with Composer:
composer require betterde/voyagerFor local development from this repository:
composer install<?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.<?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));<?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(); // 20The 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 '); // VOYAGERFor methods, pass optional flags as the fifth argument:
Debug::methodRedefine(
UserService::class,
'findUser',
'$id',
'return ["id" => $id, "name" => "generated user"];',
Debug::FLAG_PUBLIC,
);public static function functionRedefine(
string $functionName,
Closure|string $closureOrArgs,
?string $code = null,
?bool $returnByReference = null,
?string $docComment = null,
): boolRedefines 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.
public static function methodRedefine(
string $className,
string $methodName,
Closure|string $closureOrArgs,
?string $code = null,
int $flags = 0,
?string $docComment = null,
): boolRedefines 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_STATICThis 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
): boolvoyager_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
): boolVoyager 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.
.
├── composer.json
├── src/
│ └── Debug.php
└── stubs/
└── voyager_functions.php
