diff --git a/Zend/zend_operators.c b/Zend/zend_operators.c index 29db080..659f994 100644 --- a/Zend/zend_operators.c +++ b/Zend/zend_operators.c @@ -31,6 +31,11 @@ #include "zend_strtod.h" #include "zend_exceptions.h" #include "zend_closures.h" +#include "zend_interfaces.h" + +#ifdef HAVE_SPL +# include "ext/spl/spl_iterators.h" +#endif #define LONG_SIGN_MASK (1L << (8*sizeof(long)-1)) @@ -1451,28 +1456,34 @@ ZEND_API int compare_function(zval *result, zval *op1, zval *op2 TSRMLS_DC) /* { return SUCCESS; case TYPE_PAIR(IS_OBJECT, IS_NULL): +#ifdef HAVE_SPL + zend_compare_objects(result, op1, op2); +#else ZVAL_LONG(result, 1); +#endif return SUCCESS; case TYPE_PAIR(IS_NULL, IS_OBJECT): - ZVAL_LONG(result, -1); +#ifdef HAVE_SPL + zend_compare_objects(result, op2, op1); + Z_LVAL_P(result) *= -1; +#else + ZVAL_LONG(result, 1); +#endif 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) { +#ifdef HAVE_SPL + if (instanceof_function(Z_OBJCE_P(op1), spl_ce_Comparable TSRMLS_CC)) { + zend_compare_objects(result, op1, op2); + return SUCCESS; + } else +#endif 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); @@ -1491,6 +1502,13 @@ ZEND_API int compare_function(zval *result, zval *op1, zval *op2 TSRMLS_DC) /* { } } if (Z_TYPE_P(op2) == IS_OBJECT) { +#ifdef HAVE_SPL + if (instanceof_function(Z_OBJCE_P(op2), spl_ce_Comparable TSRMLS_CC)) { + zend_compare_objects(result, op2, op1); + Z_LVAL_P(result) *= -1; + return SUCCESS; + } else +#endif 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); @@ -2083,6 +2101,24 @@ ZEND_API void zend_compare_objects(zval *result, zval *o1, zval *o2 TSRMLS_DC) / { Z_TYPE_P(result) = IS_LONG; +#ifdef HAVE_SPL + if (instanceof_function(Z_OBJCE_P(o1), spl_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; + } +#endif + if (Z_OBJ_HANDLE_P(o1) == Z_OBJ_HANDLE_P(o2)) { Z_LVAL_P(result) = 0; return; diff --git a/ext/spl/spl_iterators.c b/ext/spl/spl_iterators.c index 2c81965..407d4a7 100755 --- a/ext/spl/spl_iterators.c +++ b/ext/spl/spl_iterators.c @@ -59,6 +59,7 @@ PHPAPI zend_class_entry *spl_ce_AppendIterator; PHPAPI zend_class_entry *spl_ce_RegexIterator; PHPAPI zend_class_entry *spl_ce_RecursiveRegexIterator; PHPAPI zend_class_entry *spl_ce_Countable; +PHPAPI zend_class_entry *spl_ce_Comparable; PHPAPI zend_class_entry *spl_ce_RecursiveTreeIterator; ZEND_BEGIN_ARG_INFO(arginfo_recursive_it_void, 0) @@ -3229,6 +3230,15 @@ static const zend_function_entry spl_funcs_Countable[] = { {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 spl_funcs_Comparable[] = { + SPL_ABSTRACT_ME(Comparable, compareTo, arginfo_comparable_compareTo) + {NULL, NULL, NULL} +}; + /* {{{ PHP_MINIT_FUNCTION(spl_iterators) */ PHP_MINIT_FUNCTION(spl_iterators) @@ -3272,6 +3282,7 @@ PHP_MINIT_FUNCTION(spl_iterators) REGISTER_SPL_SUB_CLASS_EX(ParentIterator, RecursiveFilterIterator, spl_dual_it_new, spl_funcs_ParentIterator); REGISTER_SPL_INTERFACE(Countable); + REGISTER_SPL_INTERFACE(Comparable); REGISTER_SPL_INTERFACE(SeekableIterator); REGISTER_SPL_ITERATOR(SeekableIterator); diff --git a/ext/spl/spl_iterators.h b/ext/spl/spl_iterators.h index 27ae130..b1342eb 100755 --- a/ext/spl/spl_iterators.h +++ b/ext/spl/spl_iterators.h @@ -52,6 +52,7 @@ extern PHPAPI zend_class_entry *spl_ce_AppendIterator; extern PHPAPI zend_class_entry *spl_ce_RegexIterator; extern PHPAPI zend_class_entry *spl_ce_RecursiveRegexIterator; extern PHPAPI zend_class_entry *spl_ce_Countable; +extern PHPAPI zend_class_entry *spl_ce_Comparable; PHP_MINIT_FUNCTION(spl_iterators); diff --git a/ext/spl/tests/comparable.phpt b/ext/spl/tests/comparable.phpt new file mode 100644 index 0000000..7119798 --- /dev/null +++ b/ext/spl/tests/comparable.phpt @@ -0,0 +1,138 @@ +--TEST-- +SPL: Test the Comparable interface. +--SKIPIF-- + +--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