static int ZEND_FASTCALL zend_binary_assign_op_helper_SPEC_VAR_CV(int (*binary_op)(zval *result, zval *op1, zval *op2 TSRMLS_DC), ZEND_OPCODE_HANDLER_ARGS)
{
	zend_op *opline = EX(opline);
	zend_free_op free_op1, free_op_data2, free_op_data1;
	zval **var_ptr;
	zval *value;

	switch (opline->extended_value) {
		case ZEND_ASSIGN_OBJ:
			return zend_binary_assign_op_obj_helper_SPEC_VAR_CV(binary_op, ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
			break;
		case ZEND_ASSIGN_DIM: {
				zval **container = _get_zval_ptr_ptr_var(&opline->op1, EX(Ts), &free_op1 TSRMLS_CC);

				if (IS_VAR == IS_VAR && !container) {
					zend_error_noreturn(E_ERROR, "Cannot use string offset as an array");
				} else if (Z_TYPE_PP(container) == IS_OBJECT) {
					if (IS_VAR == IS_VAR && !(free_op1.var != NULL)) {
						Z_ADDREF_PP(container);  /* undo the effect of get_obj_zval_ptr_ptr() */
					}
					return zend_binary_assign_op_obj_helper_SPEC_VAR_CV(binary_op, ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
				} else {
					zend_op *op_data = opline+1;
					zval *dim = _get_zval_ptr_cv(&opline->op2, EX(Ts), BP_VAR_R TSRMLS_CC);

					zend_fetch_dimension_address(&EX_T(op_data->op2.u.var), container, dim, 0, BP_VAR_RW TSRMLS_CC);
					value = get_zval_ptr(&op_data->op1, EX(Ts), &free_op_data1, BP_VAR_R);
					var_ptr = _get_zval_ptr_ptr_var(&op_data->op2, EX(Ts), &free_op_data2 TSRMLS_CC);
					ZEND_VM_INC_OPCODE();
				}
			}
			break;
		default:
			value = _get_zval_ptr_cv(&opline->op2, EX(Ts), BP_VAR_R TSRMLS_CC);
			var_ptr = _get_zval_ptr_ptr_var(&opline->op1, EX(Ts), &free_op1 TSRMLS_CC);
			/* do nothing */
			break;
	}

	if (!var_ptr) {
		zend_error_noreturn(E_ERROR, "Cannot use assign-op operators with overloaded objects nor string offsets");
	}

	if (*var_ptr == EG(error_zval_ptr)) {
		if (!RETURN_VALUE_UNUSED(&opline->result)) {
			AI_SET_PTR(EX_T(opline->result.u.var).var, EG(uninitialized_zval_ptr));
			PZVAL_LOCK(EG(uninitialized_zval_ptr));
		}

		if (free_op1.var) {zval_ptr_dtor(&free_op1.var);};
		ZEND_VM_NEXT_OPCODE();
	}

	SEPARATE_ZVAL_IF_NOT_REF(var_ptr);

	if(Z_TYPE_PP(var_ptr) == IS_OBJECT && Z_OBJ_HANDLER_PP(var_ptr, get)
	   && Z_OBJ_HANDLER_PP(var_ptr, set)) {
		/* proxy object */
		zval *objval = Z_OBJ_HANDLER_PP(var_ptr, get)(*var_ptr TSRMLS_CC);
		Z_ADDREF_P(objval);
		binary_op(objval, objval, value TSRMLS_CC);
		Z_OBJ_HANDLER_PP(var_ptr, set)(var_ptr, objval TSRMLS_CC);
		zval_ptr_dtor(&objval);
	} else {
		binary_op(*var_ptr, *var_ptr, value TSRMLS_CC);
	}

	if (!RETURN_VALUE_UNUSED(&opline->result)) {
		AI_SET_PTR(EX_T(opline->result.u.var).var, *var_ptr);
		PZVAL_LOCK(*var_ptr);
	}

	if (opline->extended_value == ZEND_ASSIGN_DIM) {
		FREE_OP(free_op_data1);
		FREE_OP_VAR_PTR(free_op_data2);
	}
	if (free_op1.var) {zval_ptr_dtor(&free_op1.var);};
	ZEND_VM_NEXT_OPCODE();
}
static int ZEND_FASTCALL zend_binary_assign_op_helper_SPEC_VAR_CV(int (*binary_op)(zval *result, zval *op1, zval *op2 TSRMLS_DC), ZEND_OPCODE_HANDLER_ARGS)
{
	zend_op *opline = EX(opline);
	zend_free_op free_op1, free_op_data2, free_op_data1;
	zval **var_ptr;
	zval *value;

	switch (opline->extended_value) {
		case ZEND_ASSIGN_OBJ:
			return zend_binary_assign_op_obj_helper_SPEC_VAR_CV(binary_op, ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
			break;
		case ZEND_ASSIGN_DIM: {
				zval **container = _get_zval_ptr_ptr_var(&opline->op1, EX(Ts), &free_op1 TSRMLS_CC);

				if (IS_VAR == IS_VAR && !container) {
					zend_error_noreturn(E_ERROR, "Cannot use string offset as an array");
				} else if (Z_TYPE_PP(container) == IS_OBJECT) {
					if (IS_VAR == IS_VAR && !(free_op1.var != NULL)) {
						Z_ADDREF_PP(container);   
					}
					return zend_binary_assign_op_obj_helper_SPEC_VAR_CV(binary_op, ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
				} else {
					zend_op *op_data = opline+1;
					zval *dim = _get_zval_ptr_cv(&opline->op2, EX(Ts), BP_VAR_R TSRMLS_CC);

					zend_fetch_dimension_address(&EX_T(op_data->op2.u.var), container, dim, 0, BP_VAR_RW TSRMLS_CC);
					value = get_zval_ptr(&op_data->op1, EX(Ts), &free_op_data1, BP_VAR_R);
					var_ptr = _get_zval_ptr_ptr_var(&op_data->op2, EX(Ts), &free_op_data2 TSRMLS_CC);
					ZEND_VM_INC_OPCODE();
				}
			}
			break;
		default:
			value = _get_zval_ptr_cv(&opline->op2, EX(Ts), BP_VAR_R TSRMLS_CC);
			var_ptr = _get_zval_ptr_ptr_var(&opline->op1, EX(Ts), &free_op1 TSRMLS_CC);
			 
			break;
	}

	if (!var_ptr) {
		zend_error_noreturn(E_ERROR, "Cannot use assign-op operators with overloaded objects nor string offsets");
	}

	if (*var_ptr == EG(error_zval_ptr)) {
		if (!RETURN_VALUE_UNUSED(&opline->result)) {
			AI_SET_PTR(EX_T(opline->result.u.var).var, EG(uninitialized_zval_ptr));
			PZVAL_LOCK(EG(uninitialized_zval_ptr));
		}

		if (free_op1.var) {zval_ptr_dtor(&free_op1.var);};
		ZEND_VM_NEXT_OPCODE();
	}

	SEPARATE_ZVAL_IF_NOT_REF(var_ptr);

	if(Z_TYPE_PP(var_ptr) == IS_OBJECT && Z_OBJ_HANDLER_PP(var_ptr, get)
	   && Z_OBJ_HANDLER_PP(var_ptr, set)) {
		 
		zval *objval = Z_OBJ_HANDLER_PP(var_ptr, get)(*var_ptr TSRMLS_CC);
		Z_ADDREF_P(objval);
		binary_op(objval, objval, value TSRMLS_CC);
		Z_OBJ_HANDLER_PP(var_ptr, set)(var_ptr, objval TSRMLS_CC);
		zval_ptr_dtor(&objval);
	} else {
		binary_op(*var_ptr, *var_ptr, value TSRMLS_CC);
	}

	if (!RETURN_VALUE_UNUSED(&opline->result)) {
		AI_SET_PTR(EX_T(opline->result.u.var).var, *var_ptr);
		PZVAL_LOCK(*var_ptr);
	}

	if (opline->extended_value == ZEND_ASSIGN_DIM) {
		FREE_OP(free_op_data1);
		FREE_OP_VAR_PTR(free_op_data2);
	}
	if (free_op1.var) {zval_ptr_dtor(&free_op1.var);};
	ZEND_VM_NEXT_OPCODE();
}