Up: Opcode Handlers   [Contents]


3.1 Handler Implementation

First, we define a general-purpose handler function template in C++. The handler argument contains the implementation of the opcode handler. It accepts three zval pointers (i.e. two operands and result), and returns a bool indicating whether the instruction is executed within this handler.

template <typename F>
int op_handler(
    zend_execute_data *execute_data,
    F                  handler
) {
    // ... initialization here
    if (!handler(op1, op2, result)) {
        return ZEND_USER_OPCODE_DISPATCH;
    }
    // ... clean up here
    return ZEND_USER_OPCODE_CONTINUE;
}

Then, we initialize the handler function. Fetch the pointer to the current line of opcode from execute_data, and pointers to each operand zval from opline.

const zend_op *opline = EX(opline);
zend_free_op free_op1, free_op2;
zval *op1 = zend_get_zval_ptr(opline,
        opline->op1_type, &opline->op1, execute_data, &free_op1, 0);
zval *op2 = zend_get_zval_ptr(opline,
        opline->op2_type, &opline->op2, execute_data, &free_op2, 0);
zval *result = opline->result_type ? EX_VAR(opline->result.var) : nullptr;

A operand may be a reference to another zval. We would want to first dereference it before use.

if (EXPECTED(op1)) {
    ZVAL_DEREF(op1);
}
if (op2) {
    ZVAL_DEREF(op2);
}

Now handler can be invoked. Before continuing to the next line of opcode, don’t forget to free the operands (if necessary) and increment EX(opline).

if (free_op2) {
    zval_ptr_dtor_nogc(free_op2);
}
if (free_op1) {
    zval_ptr_dtor_nogc(free_op1);
}
// No need to free `result` here.
EX(opline) = opline + 1;

Finally, register the handler functions.

int
add_handler(
    zend_execute_data *execute_data
) {
    return op_handler(execute_data, [] (auto zv1, auto zv2, auto rv) {
        if (/* Whether we should overload "+" operator */) {
            // ... do something
            return true;
        }
        return false;
    });
}
// Opcode handlers are usually registered on module init.
PHP_MINIT_FUNCTION(my_extension)
{
    zend_set_user_opcode_handler(ZEND_ADD, add_handler);
}