mirror of
https://github.com/valitydev/thrift.git
synced 2024-11-07 02:45:22 +00:00
THRIFT-4126: implement required fields validation in php extension when validate compiler option is enabled
Client: php This closes #1215
This commit is contained in:
parent
7470995ce4
commit
1360270eb8
@ -760,10 +760,7 @@ void t_php_generator::generate_php_type_spec(ofstream& out, t_type* t) {
|
||||
* type information to generalize serialization routines.
|
||||
*/
|
||||
void t_php_generator::generate_php_struct_spec(ofstream& out, t_struct* tstruct) {
|
||||
indent(out) << "if (!isset(self::$_TSPEC)) {" << endl;
|
||||
indent_up();
|
||||
|
||||
indent(out) << "self::$_TSPEC = array(" << endl;
|
||||
indent(out) << "static $_TSPEC = array(" << endl;
|
||||
indent_up();
|
||||
|
||||
const vector<t_field*>& members = tstruct->get_members();
|
||||
@ -773,15 +770,14 @@ void t_php_generator::generate_php_struct_spec(ofstream& out, t_struct* tstruct)
|
||||
indent(out) << (*m_iter)->get_key() << " => array(" << endl;
|
||||
indent_up();
|
||||
out << indent() << "'var' => '" << (*m_iter)->get_name() << "'," << endl;
|
||||
out << indent() << "'isRequired' => " << ((*m_iter)->get_req() == t_field::T_REQUIRED ? "true" : "false") << "," << endl;
|
||||
generate_php_type_spec(out, t);
|
||||
indent(out) << ")," << endl;
|
||||
indent_down();
|
||||
}
|
||||
|
||||
indent_down();
|
||||
indent(out) << " );" << endl;
|
||||
indent_down();
|
||||
indent(out) << "}" << endl;
|
||||
indent(out) << " );" << endl << endl;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -813,7 +809,9 @@ void t_php_generator::generate_php_struct_definition(ofstream& out,
|
||||
out << " {" << endl;
|
||||
indent_up();
|
||||
|
||||
indent(out) << "static $_TSPEC;" << endl << endl;
|
||||
out << indent() << "static $isValidate = " << (validate_ ? "true" : "false") << ";" << endl << endl;
|
||||
|
||||
generate_php_struct_spec(out, tstruct);
|
||||
|
||||
for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
|
||||
string dval = "null";
|
||||
@ -832,8 +830,6 @@ void t_php_generator::generate_php_struct_definition(ofstream& out,
|
||||
out << indent() << "public function __construct(" << param << ") {" << endl;
|
||||
indent_up();
|
||||
|
||||
generate_php_struct_spec(out, tstruct);
|
||||
|
||||
if (members.size() > 0) {
|
||||
for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
|
||||
t_type* t = get_true_type((*m_iter)->get_type());
|
||||
|
@ -728,6 +728,42 @@ inline bool ttypes_are_compatible(int8_t t1, int8_t t2) {
|
||||
return ((t1 == t2) || (ttype_is_int(t1) && ttype_is_int(t2)));
|
||||
}
|
||||
|
||||
//is used to validate objects before serialization and after deserialization. For now, only required fields are validated.
|
||||
static
|
||||
void validate_thrift_object(zval* object) {
|
||||
|
||||
HashPosition key_ptr;
|
||||
zval** val_ptr;
|
||||
|
||||
TSRMLS_FETCH();
|
||||
zend_class_entry* object_class_entry = zend_get_class_entry(object TSRMLS_CC);
|
||||
HashTable* spec = Z_ARRVAL_P(zend_read_static_property(object_class_entry, "_TSPEC", 6, false TSRMLS_CC));
|
||||
|
||||
for (zend_hash_internal_pointer_reset_ex(spec, &key_ptr); zend_hash_get_current_data_ex(spec, (void**)&val_ptr, &key_ptr) == SUCCESS; zend_hash_move_forward_ex(spec, &key_ptr)) {
|
||||
ulong fieldno;
|
||||
if (zend_hash_get_current_key_ex(spec, NULL, NULL, &fieldno, 0, &key_ptr) != HASH_KEY_IS_LONG) {
|
||||
throw_tprotocolexception("Bad keytype in TSPEC (expected 'long')", INVALID_DATA);
|
||||
return;
|
||||
}
|
||||
HashTable* fieldspec = Z_ARRVAL_PP(val_ptr);
|
||||
|
||||
// field name
|
||||
zend_hash_find(fieldspec, "var", 4, (void**)&val_ptr);
|
||||
char* varname = Z_STRVAL_PP(val_ptr);
|
||||
|
||||
zend_hash_find(fieldspec, "isRequired", 11, (void**)&val_ptr);
|
||||
bool is_required = Z_BVAL_PP(val_ptr);
|
||||
|
||||
zval* prop = zend_read_property(object_class_entry, object, varname, strlen(varname), false TSRMLS_CC);
|
||||
|
||||
if (is_required && Z_TYPE_P(prop) == IS_NULL) {
|
||||
char errbuf[128];
|
||||
snprintf(errbuf, 128, "Required field %s.%s is unset!", object_class_entry->name, varname);
|
||||
throw_tprotocolexception(errbuf, INVALID_DATA);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void binary_deserialize_spec(zval* zthis, PHPInputTransport& transport, HashTable* spec) {
|
||||
// SET and LIST have 'elem' => array('type', [optional] 'class')
|
||||
// MAP has 'val' => array('type', [optiona] 'class')
|
||||
@ -737,7 +773,10 @@ void binary_deserialize_spec(zval* zthis, PHPInputTransport& transport, HashTabl
|
||||
zval** val_ptr = NULL;
|
||||
|
||||
int8_t ttype = transport.readI8();
|
||||
if (ttype == T_STOP) return;
|
||||
if (ttype == T_STOP) {
|
||||
validate_thrift_object(zthis);
|
||||
return;
|
||||
}
|
||||
int16_t fieldno = transport.readI16();
|
||||
if (zend_hash_index_find(spec, fieldno, (void**)&val_ptr) == SUCCESS) {
|
||||
HashTable* fieldspec = Z_ARRVAL_PP(val_ptr);
|
||||
@ -903,6 +942,9 @@ void binary_serialize(int8_t thrift_typeID, PHPOutputTransport& transport, zval*
|
||||
|
||||
|
||||
void binary_serialize_spec(zval* zthis, PHPOutputTransport& transport, HashTable* spec) {
|
||||
|
||||
validate_thrift_object(zthis);
|
||||
|
||||
HashPosition key_ptr;
|
||||
zval** val_ptr;
|
||||
|
||||
|
@ -849,6 +849,44 @@ bool ttypes_are_compatible(int8_t t1, int8_t t2) {
|
||||
return ((t1 == t2) || (ttype_is_int(t1) && ttype_is_int(t2)));
|
||||
}
|
||||
|
||||
//is used to validate objects before serialization and after deserialization. For now, only required fields are validated.
|
||||
static
|
||||
void validate_thrift_object(zval* object) {
|
||||
zend_class_entry* object_class_entry = Z_OBJCE_P(object);
|
||||
zval* is_validate = zend_read_static_property(object_class_entry, "isValidate", sizeof("isValidate")-1, false);
|
||||
zval* spec = zend_read_static_property(object_class_entry, "_TSPEC", sizeof("_TSPEC")-1, false);
|
||||
HashPosition key_ptr;
|
||||
zval* val_ptr;
|
||||
|
||||
if (Z_TYPE_INFO_P(is_validate) == IS_TRUE) {
|
||||
for (zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(spec), &key_ptr);
|
||||
(val_ptr = zend_hash_get_current_data_ex(Z_ARRVAL_P(spec), &key_ptr)) != nullptr;
|
||||
zend_hash_move_forward_ex(Z_ARRVAL_P(spec), &key_ptr)) {
|
||||
|
||||
zend_ulong fieldno;
|
||||
if (zend_hash_get_current_key_ex(Z_ARRVAL_P(spec), nullptr, &fieldno, &key_ptr) != HASH_KEY_IS_LONG) {
|
||||
throw_tprotocolexception("Bad keytype in TSPEC (expected 'long')", INVALID_DATA);
|
||||
return;
|
||||
}
|
||||
HashTable* fieldspec = Z_ARRVAL_P(val_ptr);
|
||||
|
||||
// field name
|
||||
zval* zvarname = zend_hash_str_find(fieldspec, "var", sizeof("var")-1);
|
||||
char* varname = Z_STRVAL_P(zvarname);
|
||||
|
||||
zval* is_required = zend_hash_str_find(fieldspec, "isRequired", sizeof("isRequired")-1);
|
||||
zval rv;
|
||||
zval* prop = zend_read_property(object_class_entry, object, varname, strlen(varname), false, &rv);
|
||||
|
||||
if (Z_TYPE_INFO_P(is_required) == IS_TRUE && Z_TYPE_P(prop) == IS_NULL) {
|
||||
char errbuf[128];
|
||||
snprintf(errbuf, 128, "Required field %s.%s is unset!", ZSTR_VAL(object_class_entry->name), varname);
|
||||
throw_tprotocolexception(errbuf, INVALID_DATA);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void binary_deserialize_spec(zval* zthis, PHPInputTransport& transport, HashTable* spec) {
|
||||
// SET and LIST have 'elem' => array('type', [optional] 'class')
|
||||
@ -857,6 +895,7 @@ void binary_deserialize_spec(zval* zthis, PHPInputTransport& transport, HashTabl
|
||||
while (true) {
|
||||
int8_t ttype = transport.readI8();
|
||||
if (ttype == T_STOP) {
|
||||
validate_thrift_object(zthis);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -892,6 +931,9 @@ void binary_deserialize_spec(zval* zthis, PHPInputTransport& transport, HashTabl
|
||||
|
||||
static
|
||||
void binary_serialize_spec(zval* zthis, PHPOutputTransport& transport, HashTable* spec) {
|
||||
|
||||
validate_thrift_object(zthis);
|
||||
|
||||
HashPosition key_ptr;
|
||||
zval* val_ptr;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user