Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion config.m4
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,5 @@ if test "$PHP_ATTRIBUTES_VALIDATION" != "no"; then
fi

PHP_ADD_LIBRARY(stdc++, 1, ATTRIBUTES_VALIDATION_SHARED_LIBADD)
PHP_NEW_EXTENSION(attributes_validation, attributes_validation.c src/av_validate_function.c src/av_call_function.c src/av_base_model.c src/av_exception.c src/av_model_configs.c src/helpers/av_wrappers.c src/helpers/av_options.c src/helpers/av_string.c src/helpers/av_error_messages.c src/fields/av_field.c src/fields/av_alias.c src/validators/av_typehint_validator.c, $ext_shared)
PHP_NEW_EXTENSION(attributes_validation, attributes_validation.c src/av_validate_function.c src/av_call_function.c src/av_base_model.c src/av_exception.c src/av_model_configs.c src/helpers/av_wrappers.c src/helpers/av_options.c src/helpers/av_string.c src/helpers/av_error_messages.c src/helpers/av_value_to_string.c src/fields/av_field.c src/fields/av_alias.c src/validators/av_typehint_validator.c, $ext_shared)
fi
7 changes: 6 additions & 1 deletion project.yml
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,12 @@
int8: INT8
bool: UINT8
#:treat_as_array: {} # hint to cmock that these types are pointers to something
#:treat_as_void: [] # hint to cmock that these types are actually aliases of void
# Treat opaque Zend types as void so CMock compares pointers by value instead
# of trying to sizeof an incomplete struct definition.
:treat_as_void:
- zend_class_entry
- zend_object
- zend_resource
:memcmp_if_unknown: true # allow cmock to use the memory comparison assertions for unknown types
:when_ptr: :compare_data # hint to cmock how to handle pointers in general, the options being :compare_ptr, :compare_data, or :smart

Expand Down
9 changes: 0 additions & 9 deletions src/helpers/av_error_messages.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,6 @@ static const char* av_error_type_messages[] = {
[AV_ERROR_TYPE] = "The {field} must be {expected}.",
};

static zend_string* av_value_to_string(zval *value)
{
// TODO: Implement zval to zend_string implementation
if (value == NULL) {
return zend_string_init("null", 4, 0);
}
return zval_get_string(value);
}

static zend_string* av_replace_placeholders(const char *template, size_t length, av_field *field, av_property_info *prop_info)
{
struct { const char *search; size_t len; size_t counts; zend_string *replace; } table[] = {
Expand Down
3 changes: 3 additions & 0 deletions src/helpers/av_error_messages.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ static zend_string* build_union_type_string(zend_type property_type);
static zend_string* generate_error_message(av_field *field, zend_type property_type);
static bool av_vowel_sound(char c);

// Value conversion
zend_string* av_value_to_string(zval *value);

// Updates errors
static zend_always_inline void add_field_error_to_array(zval *errors_array, const char *error_message, size_t length);
static zend_always_inline void add_field_error(zval *errors, zend_string *field_name, const char *error_message, size_t length);
Expand Down
65 changes: 65 additions & 0 deletions src/helpers/av_value_to_string.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
#include "av_value_to_string.h"
#include "av_wrappers.h"
#include "Zend/zend_API.h"
#include "Zend/zend_operators.h"
#include <string.h>

/*
* Converts any PHP zval into a zend_string suitable for inclusion in an
* error message template (the {value} placeholder).
*
* The conversion is fully type-aware so error messages stay readable:
* null -> "null"
* bool -> "true" / "false"
* int -> numeric string
* double -> numeric string
* string -> single-quoted value
* array -> "array"
* object -> __toString() result when Stringable, else the class name
* resource -> resource type name, or "resource"
*
* All Zend internals are reached through the mockable av_wrappers so the
* function can be unit tested in isolation.
*/
zend_string* av_value_to_string(zval *value)
{
if (value == NULL) {
return av_string_init("null", sizeof("null") - 1, 0);
}

ZVAL_DEREF(value);

switch (Z_TYPE_P(value)) {
case IS_NULL:
return av_string_init("null", sizeof("null") - 1, 0);
case IS_TRUE:
return av_string_init("true", sizeof("true") - 1, 0);
case IS_FALSE:
return av_string_init("false", sizeof("false") - 1, 0);
case IS_LONG:
return av_long_to_str(Z_LVAL_P(value));
case IS_DOUBLE:
return av_double_to_str(Z_DVAL_P(value));
case IS_STRING:
return av_string_concat3("'", 1, Z_STRVAL_P(value), Z_STRLEN_P(value), "'", 1);
case IS_ARRAY:
return av_string_init("array", sizeof("array") - 1, 0);
case IS_OBJECT:
if (av_is_stringable(Z_OBJCE_P(value))) {
zval result;
if (av_call_tostring(Z_OBJ_P(value), &result) == SUCCESS) {
zend_string *str = av_string_concat3("'", 1, Z_STRVAL(result), Z_STRLEN(result), "'", 1);
av_zval_ptr_dtor(&result);
return str;
}
}
return av_string_copy(Z_OBJCE_P(value)->name);
case IS_RESOURCE: {
const char *type_name = av_rsrc_list_get_rsrc_type(Z_RES_P(value));
const char *fallback = "resource";
return av_string_init(type_name ? type_name : fallback, strlen(type_name ? type_name : fallback), 0);
}
default:
return av_string_init("(unknown)", sizeof("(unknown)") - 1, 0);
}
}
13 changes: 13 additions & 0 deletions src/helpers/av_value_to_string.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#ifndef AV_HELPERS_VALUE_TO_STRING_H
#define AV_HELPERS_VALUE_TO_STRING_H

#include <Zend/zend_types.h>

/*
* Converts any PHP zval into a zend_string for use in error message
* templates (the {value} placeholder). See av_value_to_string.c for the
* per-type conversion rules.
*/
zend_string* av_value_to_string(zval *value);

#endif /* AV_HELPERS_VALUE_TO_STRING_H */
51 changes: 51 additions & 0 deletions src/helpers/av_wrappers.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
#include "av_wrappers.h"
#include "Zend/zend_API.h"
#include "Zend/zend_interfaces.h"
#include "Zend/zend_list.h"
#include "Zend/zend_operators.h"
#include "Zend/zend_variables.h"

/*
* Wrapper implementations for Zend internals.
Expand All @@ -22,3 +26,50 @@ void* av_emalloc(size_t size) {
void av_efree(void *ptr) {
efree(ptr);
}

zend_string* av_string_concat3(const char *str1, size_t str1_len,
const char *str2, size_t str2_len,
const char *str3, size_t str3_len) {
return zend_string_concat3(str1, str1_len, str2, str2_len, str3, str3_len);
}

zend_string* av_string_copy(zend_string *s) {
return zend_string_copy(s);
}

zend_string* av_long_to_str(zend_long num) {
return zend_long_to_str(num);
}

zend_string* av_double_to_str(double num) {
return zend_double_to_str(num);
}

bool av_instanceof_function(const zend_class_entry *instance_ce, const zend_class_entry *ce) {
return instanceof_function(instance_ce, ce);
}

bool av_is_stringable(const zend_class_entry *instance_ce) {
return instanceof_function(instance_ce, zend_ce_stringable);
}

const char* av_rsrc_list_get_rsrc_type(zend_resource *res) {
return zend_rsrc_list_get_rsrc_type(res);
}

zend_result av_call_tostring(zend_object *object, zval *retval) {
ZVAL_UNDEF(retval);
zend_call_method_with_0_params(object, NULL, NULL, "__tostring", retval);
if (Z_TYPE_P(retval) != IS_STRING) {
if (Z_TYPE_P(retval) != IS_UNDEF) {
zval_ptr_dtor(retval);
ZVAL_UNDEF(retval);
}
return FAILURE;
}
return SUCCESS;
}

void av_zval_ptr_dtor(zval *zval_ptr) {
zval_ptr_dtor(zval_ptr);
}
15 changes: 14 additions & 1 deletion src/helpers/av_wrappers.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,17 @@ void av_string_release(zend_string *s);
void* av_emalloc(size_t size);
void av_efree(void *ptr);

#endif /* AV_HELPERS_AV_WRAPPERS_H */
/* Additional wrappers used by av_value_to_string to allow mocking in unit tests. */
zend_string* av_string_concat3(const char *str1, size_t str1_len,
const char *str2, size_t str2_len,
const char *str3, size_t str3_len);
zend_string* av_string_copy(zend_string *s);
zend_string* av_long_to_str(zend_long num);
zend_string* av_double_to_str(double num);
bool av_instanceof_function(const zend_class_entry *instance_ce, const zend_class_entry *ce);
bool av_is_stringable(const zend_class_entry *instance_ce);
const char* av_rsrc_list_get_rsrc_type(zend_resource *res);
zend_result av_call_tostring(zend_object *object, zval *retval);
void av_zval_ptr_dtor(zval *zval_ptr);

#endif /* AV_HELPERS_AV_WRAPPERS_H */
Loading
Loading