go-php/receiver.c

180 lines
5.0 KiB
C

// Copyright 2017 Alexander Palaistras. All rights reserved.
// Use of this source code is governed by the MIT license that can be found in
// the LICENSE file.
#include <stdio.h>
#include <stdbool.h>
#include <main/php.h>
#include <zend_exceptions.h>
#include <ext/standard/php_string.h>
#include "value.h"
#include "receiver.h"
#include "_cgo_export.h"
// Fetch and return field for method receiver.
static engine_value *receiver_get(zval *object, zval *member) {
engine_receiver *this = _receiver_this(object);
return engineReceiverGet(this, Z_STRVAL_P(member));
}
// Set field for method receiver.
static void receiver_set(zval *object, zval *member, zval *value) {
engine_receiver *this = _receiver_this(object);
engineReceiverSet(this, Z_STRVAL_P(member), (void *) value);
}
// Check if field exists for method receiver.
static int receiver_exists(zval *object, zval *member, int check) {
engine_receiver *this = _receiver_this(object);
if (!engineReceiverExists(this, Z_STRVAL_P(member))) {
// Value does not exist.
return 0;
} else if (check == 2) {
// Value exists.
return 1;
}
int result = 0;
engine_value *val = engineReceiverGet(this, Z_STRVAL_P(member));
if (check == 1) {
// Value exists and is "truthy".
convert_to_boolean(val->internal);
result = _value_truth(val->internal);
} else if (check == 0) {
// Value exists and is not null.
result = (val->kind != KIND_NULL) ? 1 : 0;
} else {
// Check value is invalid.
result = 0;
}
_value_destroy(val);
return result;
}
// Call function with arguments passed and return value (if any).
static int receiver_method_call(char *name, INTERNAL_FUNCTION_PARAMETERS) {
zval args;
engine_receiver *this = _receiver_this(getThis());
array_init_size(&args, ZEND_NUM_ARGS());
if (zend_copy_parameters_array(ZEND_NUM_ARGS(), &args) == FAILURE) {
RETVAL_NULL();
} else {
engine_value *result = engineReceiverCall(this, name, (void *) &args);
if (result == NULL) {
RETVAL_NULL();
} else {
value_copy(return_value, result->internal);
_value_destroy(result);
}
}
zval_dtor(&args);
}
// Create new method receiver instance and attach to instantiated PHP object.
// Returns an exception if method receiver failed to initialize for any reason.
static void receiver_new(INTERNAL_FUNCTION_PARAMETERS) {
zval args;
engine_receiver *this = _receiver_this(getThis());
array_init_size(&args, ZEND_NUM_ARGS());
if (zend_copy_parameters_array(ZEND_NUM_ARGS(), &args) == FAILURE) {
zend_throw_exception(NULL, "Could not parse parameters for method receiver", 0);
} else {
// Create receiver instance. Throws an exception if creation fails.
int result = engineReceiverNew(this, (void *) &args);
if (result != 0) {
zend_throw_exception(NULL, "Failed to instantiate method receiver", 0);
}
}
zval_dtor(&args);
}
// Fetch and return function definition for method receiver. The method call
// happens in the method handler, as returned by this function.
static zend_internal_function *receiver_method_get(zend_object *object) {
zend_internal_function *func = emalloc(sizeof(zend_internal_function));
func->type = ZEND_OVERLOADED_FUNCTION;
func->handler = NULL;
func->arg_info = NULL;
func->num_args = 0;
func->scope = object->ce;
func->fn_flags = ZEND_ACC_CALL_VIA_HANDLER;
return func;
}
// Fetch and return constructor function definition for method receiver. The
// construct call happens in the constructor handler, as returned by this
// function.
static zend_internal_function *receiver_constructor_get(zend_object *object) {
static zend_internal_function func;
func.type = ZEND_INTERNAL_FUNCTION;
func.handler = receiver_new;
func.arg_info = NULL;
func.num_args = 0;
func.scope = object->ce;
func.fn_flags = 0;
func.function_name = object->ce->name;
return &func;
}
// Table of handler functions for method receivers.
static zend_object_handlers receiver_handlers = {
ZEND_OBJECTS_STORE_HANDLERS,
_receiver_get, // read_property
_receiver_set, // write_property
NULL, // read_dimension
NULL, // write_dimension
NULL, // get_property_ptr_ptr
NULL, // get
NULL, // set
_receiver_exists, // has_property
NULL, // unset_property
NULL, // has_dimension
NULL, // unset_dimension
NULL, // get_properties
_receiver_method_get, // get_method
_receiver_method_call, // call_method
_receiver_constructor_get // get_constructor
};
// Define class with unique name.
void receiver_define(char *name) {
zend_class_entry tmp;
INIT_CLASS_ENTRY_EX(tmp, name, strlen(name), NULL);
zend_class_entry *this = zend_register_internal_class(&tmp);
this->create_object = _receiver_init;
this->ce_flags |= ZEND_ACC_FINAL;
// Set standard handlers for receiver.
_receiver_handlers_set(&receiver_handlers);
}
void receiver_destroy(char *name) {
name = php_strtolower(name, strlen(name));
_receiver_destroy(name);
}
#include "_receiver.c"