Skip to content


Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?

Latest commit


Git stats


Failed to load latest commit information.
Latest commit message
Commit time


A disassembler and debug kit for PHP7

Build Status Coverage Status

Inspector provides an API for disassembling and debugging PHP7 code, in userland.


The following API is provided:

Note that, because of restrictions around an extension extending Reflection classes, Inspector classes that extend Reflection do so virtually with a __call interface.

namespace Inspector
	class InspectorClass extends \ReflectionClass {
		public function isPending() : bool;
		public function onResolve();

		public function getMethod(string name) : InspectorMethod;
		public function getMethods(int filter = 0) : array;

		public static function purge(array filters = []);

	class InspectorMethod extends \ReflectionMethod implements InspectorInstructionInterface {
		public function getDeclaringClass() : InspectorClass;

	class InspectorFunction extends \ReflectionFunction implements InspectorInstructionInterface {
		public function isPending() : bool;
		public function onResolve();
		public function onTrace(InspectorFrame $frame);

		public static function purge(array filters = []);

	abstract class InspectorFile extends InspectorFunction {
		public function __construct(string file);

		public function isPending() : bool;
		public function onResolve();
		public function onTrace(InspectorFrame $frame);

		public static function purge(array filters = []);

	interface InspectorInstructionInterface {
		public function getInstruction(int num = 0) : InspectorInstruction;
		public function getInstructionCount() : int;
		public function getEntryInstruction() : ?InspectorInstruction;
		public function findFirstInstruction(int opcode, int offset = 0) : ?InspectorInstruction;
		public function findLastInstruction(int opcode, int offset = -1) : ?InspectorInstruction;
		public function flushInstructionCache();

	final class InspectorInstruction {
		public function getOffset() : int;
		public function getOpcode() : int;
		public function getOpcodeName() : ?string;
		public function getOperand(int which) : InspectorOperand;
		public function getExtendedValue() : mixed;
		public function getLine() : int;
		public function getFunction() : InspectorFunction;
		public function getNext() : ?InspectorInstruction;
		public function getPrevious() : ?InspectorInstruction;
		public function getRelative(int offset) : ?InspectorInstruction;
		public function getBreakPoint() : InspectorBreakPoint;

		public function getFlags([int which]) : ?int;

		/* opcode constants are registered with prefixed ZEND_ (verbatim) */
		/* vm constants are registered with prefix ZEND_VM_ (verbatim) */

	final class InspectorOperand {
		const OP1;
		const OP2;
		const RESULT;

		public function isUnused() : bool;
		public function isExtendedTypeUnused() : bool;
		public function isCompiledVariable() : bool;
		public function isTemporaryVariable() : bool;
		public function isVariable() : bool;
		public function isConstant() : bool;
		public function isJumpTarget() : bool;
		public function getWhich() : int;
		public function getValue(?InspectorFrame $frame) : mixed;
		public function getName() : string;
		public function getNumber() : int;
		public function getInstruction() : InspectorInstruction;

	abstract class InspectorBreakPoint extends InspectorBreakPointAbstract {
		public function __construct(InspectorInstruction opline);

		public function enable() : bool;
		public function disable() : bool;
		public function isEnabled() : bool;

		public function getInstruction() : InspectorInstruction;

		abstract public function hit(InspectorFrame frame);

		public static function onException(\Closure(InspectorFrame frame, \Throwable ex) handler);

	final class InspectorFrame {
		public function getFunction() : InspectorInstructionInterface;
		public function getInstruction() : ?InspectorInstruction;
		public function setInstruction(InspectorInstruction instruction);
		public function getSymbols() : ?array;
		public function getPrevious() : ?InspectorFrame;
		public function getCall() : ?InspectorFrame;
		public function getStack() : array;
		public function getParameters() : array;
		public function getVariable(int num) : mixed;
		public function getThis() : ?object;

		public static function getCurrent() : InspectorFrame;


Other people are better at this than I am ...

Making stuff look nice is not my forte, all the same, here is some simple code using Inspector:

use Inspector\InspectorFunction;
use Inspector\InspectorInstruction;
use Inspector\InspectorOperand;

function printConstant($mixed) {
	if (strlen($mixed) > 8) {
			"%s...\t", substr(str_replace(
		), 0, 8));
	} else printf("%s\t", $mixed ?: "null");

function printOperand(InspectorOperand $op) {
	if ($op->isConstant()) {
	} else if ($op->isCompiledVariable()) {
		printf("\$%s\t", $op->getName());
	} else if ($op->isTemporaryVariable()) {
		printf("T%d\t", $op->getNumber());
	} else if($op->isVariable()) {
		printf("V%d\t", $op->getNumber());
	} else printf("-\t");

function printFunction(InspectorFunction $inspector) {
	$opline = $inspector->getInstruction();

	do {
		printf("%s\t", $opline->getOpcodeName());
	} while ($opline = $opline->getNext());

printFunction(new InspectorFunction(function($a, $b){
	return $a + $b;

Which will yield:

OPCODE          OP1     OP2     RESULT
ZEND_RECV       -       -       $a
ZEND_RECV       -       -       $b
ZEND_ADD        $a      $b      T1
ZEND_RETURN     T1      -       -
ZEND_RETURN     null    -       -


  • tests (started)


Disassembler and Debug Kit for PHP 7






No releases published


No packages published

Contributors 4