diff --git a/Zend/tests/comparable.phpt b/Zend/tests/comparable.phpt new file mode 100644 index 0000000..5fff8d9 --- /dev/null +++ b/Zend/tests/comparable.phpt @@ -0,0 +1,130 @@ +--TEST-- +Comparable interface +--FILE-- +v = $v; + } + + public function compareTo($o) { + echo "compareTo fired\n"; + + if ($o instanceof C) { + $value = $o->v; + } + elseif (is_null($o)) { + throw new Exception('Null values are unsupported'); + } + else { + $value = $o; + } + + if ($this->v > $value) { + return 1; + } + elseif ($this->v < $value) { + return -1; + } + return 0; + } +} + +$a = new C('a'); +$b = new C('b'); +$bDupl = new C('b'); + +echo "These should be true...\n"; +var_dump($b > $a, $a < $b, $a == $a, $a != $b, $b == $bDupl); + +echo "These should be false...\n"; +var_dump($b < $a, $a > $b, $a == $b, $b != $b, $a == $bDupl); + +echo "These should be true...\n"; +var_dump($a < 'b', $b > 'a', 'b' <= $b, $a == 'a', 'b' != $a); + +echo "These should be false...\n"; +var_dump('b' < $a, $b < 'a', $b > 'b', $a != 'a', $a == 'b'); + +echo "This should throw an exception\n"; +$a == null; +?> +--EXPECTF-- +Interface [ interface Comparable ] { + + - Constants [0] { + } + + - Static properties [0] { + } + + - Static methods [0] { + } + + - Properties [0] { + } + + - Methods [1] { + Method [ abstract public method compareTo ] { + + - Parameters [1] { + Parameter #0 [ $o ] + } + } + } +} + +These should be true... +compareTo fired +compareTo fired +compareTo fired +compareTo fired +compareTo fired +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +These should be false... +compareTo fired +compareTo fired +compareTo fired +compareTo fired +compareTo fired +bool(false) +bool(false) +bool(false) +bool(false) +bool(false) +These should be true... +compareTo fired +compareTo fired +compareTo fired +compareTo fired +compareTo fired +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +These should be false... +compareTo fired +compareTo fired +compareTo fired +compareTo fired +compareTo fired +bool(false) +bool(false) +bool(false) +bool(false) +bool(false) +This should throw an exception +compareTo fired + +Fatal error: Uncaught exception 'Exception' with message 'Null values are unsupported' in %s:%d +Stack trace: +#0 %s: C->compareTo(NULL) +#1 {main} + thrown in %s on line %d diff --git a/Zend/zend_interfaces.c b/Zend/zend_interfaces.c index 517b116..e9f365d 100755 --- a/Zend/zend_interfaces.c +++ b/Zend/zend_interfaces.c @@ -28,6 +28,7 @@ ZEND_API zend_class_entry *zend_ce_aggregate; ZEND_API zend_class_entry *zend_ce_iterator; ZEND_API zend_class_entry *zend_ce_arrayaccess; ZEND_API zend_class_entry *zend_ce_serializable; +ZEND_API zend_class_entry *zend_ce_comparable; /* {{{ zend_call_method Only returns the returned zval if retval_ptr != NULL */ @@ -514,6 +515,13 @@ static int zend_implement_serializable(zend_class_entry *interface, zend_class_e } /* }}}*/ +/* {{{ zend_implement_comparable */ +static int zend_implement_comparable(zend_class_entry *interface, zend_class_entry *class_type TSRMLS_DC) +{ + return SUCCESS; +} +/* }}} */ + /* {{{ function tables */ const zend_function_entry zend_funcs_aggregate[] = { ZEND_ABSTRACT_ME(iterator, getIterator, NULL) @@ -561,6 +569,15 @@ const zend_function_entry zend_funcs_serializable[] = { ZEND_FENTRY(unserialize, NULL, arginfo_serializable_serialize, ZEND_ACC_PUBLIC|ZEND_ACC_ABSTRACT|ZEND_ACC_CTOR) {NULL, NULL, NULL} }; + +ZEND_BEGIN_ARG_INFO_EX(arginfo_comparable_compareTo, 0, 0, 1) + ZEND_ARG_INFO(0, o) +ZEND_END_ARG_INFO(); + +static const zend_function_entry zend_funcs_comparable[] = { + ZEND_ABSTRACT_ME(Comparable, compareTo, arginfo_comparable_compareTo) + {NULL, NULL, NULL} +}; /* }}} */ #define REGISTER_ITERATOR_INTERFACE(class_name, class_name_str) \ @@ -588,6 +605,7 @@ ZEND_API void zend_register_interfaces(TSRMLS_D) REGISTER_ITERATOR_INTERFACE(arrayaccess, ArrayAccess); REGISTER_ITERATOR_INTERFACE(serializable, Serializable) + REGISTER_ITERATOR_INTERFACE(comparable, Comparable) } /* }}} */ diff --git a/Zend/zend_interfaces.h b/Zend/zend_interfaces.h index 3b2528b..c127155 100755 --- a/Zend/zend_interfaces.h +++ b/Zend/zend_interfaces.h @@ -31,6 +31,7 @@ extern ZEND_API zend_class_entry *zend_ce_aggregate; extern ZEND_API zend_class_entry *zend_ce_iterator; extern ZEND_API zend_class_entry *zend_ce_arrayaccess; extern ZEND_API zend_class_entry *zend_ce_serializable; +extern ZEND_API zend_class_entry *zend_ce_comparable; typedef struct _zend_user_iterator { zend_object_iterator it; diff --git a/Zend/zend_operators.c b/Zend/zend_operators.c index 29db080..06156b1 100644 --- a/Zend/zend_operators.c +++ b/Zend/zend_operators.c @@ -31,6 +31,7 @@ #include "zend_strtod.h" #include "zend_exceptions.h" #include "zend_closures.h" +#include "zend_interfaces.h" #define LONG_SIGN_MASK (1L << (8*sizeof(long)-1)) @@ -1451,29 +1452,24 @@ ZEND_API int compare_function(zval *result, zval *op1, zval *op2 TSRMLS_DC) /* { return SUCCESS; case TYPE_PAIR(IS_OBJECT, IS_NULL): - ZVAL_LONG(result, 1); + zend_compare_objects(result, op1, op2); return SUCCESS; case TYPE_PAIR(IS_NULL, IS_OBJECT): - ZVAL_LONG(result, -1); + zend_compare_objects(result, op2, op1); + Z_LVAL_P(result) *= -1; return SUCCESS; case TYPE_PAIR(IS_OBJECT, IS_OBJECT): - /* If both are objects sharing the same comparision handler then use is */ - if (Z_OBJ_HANDLER_P(op1,compare_objects) == Z_OBJ_HANDLER_P(op2,compare_objects)) { - if (Z_OBJ_HANDLE_P(op1) == Z_OBJ_HANDLE_P(op2)) { - /* object handles are identical, apprently this is the same object */ - ZVAL_LONG(result, 0); - return SUCCESS; - } - ZVAL_LONG(result, Z_OBJ_HT_P(op1)->compare_objects(op1, op2 TSRMLS_CC)); - return SUCCESS; - } - /* break missing intentionally */ + zend_compare_objects(result, op1, op2); + return SUCCESS; default: if (Z_TYPE_P(op1) == IS_OBJECT) { - if (Z_OBJ_HT_P(op1)->get) { + if (instanceof_function(Z_OBJCE_P(op1), zend_ce_comparable TSRMLS_CC)) { + zend_compare_objects(result, op1, op2); + return SUCCESS; + } else if (Z_OBJ_HT_P(op1)->get) { op_free = Z_OBJ_HT_P(op1)->get(op1 TSRMLS_CC); ret = compare_function(result, op_free, op2 TSRMLS_CC); zend_free_obj_get_result(op_free TSRMLS_CC); @@ -1491,7 +1487,11 @@ ZEND_API int compare_function(zval *result, zval *op1, zval *op2 TSRMLS_DC) /* { } } if (Z_TYPE_P(op2) == IS_OBJECT) { - if (Z_OBJ_HT_P(op2)->get) { + if (instanceof_function(Z_OBJCE_P(op2), zend_ce_comparable TSRMLS_CC)) { + zend_compare_objects(result, op2, op1); + Z_LVAL_P(result) *= -1; + return SUCCESS; + } else if (Z_OBJ_HT_P(op2)->get) { op_free = Z_OBJ_HT_P(op2)->get(op2 TSRMLS_CC); ret = compare_function(result, op1, op_free TSRMLS_CC); zend_free_obj_get_result(op_free TSRMLS_CC); @@ -2083,6 +2083,22 @@ ZEND_API void zend_compare_objects(zval *result, zval *o1, zval *o2 TSRMLS_DC) / { Z_TYPE_P(result) = IS_LONG; + if (instanceof_function(Z_OBJCE_P(o1), zend_ce_comparable TSRMLS_CC)) { + zval *retval; + + zend_call_method_with_1_params(&o1, NULL, NULL, "compareTo", &retval, o2); + + if (retval) { + convert_to_long_ex(&retval); + Z_LVAL_P(result) = Z_LVAL_P(retval); + zval_ptr_dtor(&retval); + } else { + Z_LVAL_P(result) = -1; + } + + return; + } + if (Z_OBJ_HANDLE_P(o1) == Z_OBJ_HANDLE_P(o2)) { Z_LVAL_P(result) = 0; return;