Disassembler and Debug Kit for PHP 7
Switch branches/tags
Nothing to show
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Failed to load latest commit information.



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:

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;

		protected $instructionCache;

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

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

		protected $instructionCache;

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

		public function isPending() : bool;
		public function onResolve();

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

		protected $instructionCache;

	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)