├── LICENSE ├── README.md ├── comparable.c ├── config.m4 ├── config.w32 └── php_comparable.h /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 by Nikita Popov. 2 | 3 | Some rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are 7 | met: 8 | 9 | * Redistributions of source code must retain the above copyright 10 | notice, this list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above 13 | copyright notice, this list of conditions and the following 14 | disclaimer in the documentation and/or other materials provided 15 | with the distribution. 16 | 17 | * The names of the contributors may not be used to endorse or 18 | promote products derived from this software without specific 19 | prior written permission. 20 | 21 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 24 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 25 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 26 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 27 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 28 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 29 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 30 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 31 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Comparable interface for PHP 2 | ============================ 3 | 4 | Note: This is to the most part just code demonstrating the implementation of a magic interface for 5 | a tutorial. I do not currently plan on proposing including such an interface for PHP itself. 6 | 7 | This extension implements a magic `Comparable` interface for PHP: 8 | 9 | interface Comparable { 10 | static function compare($obj1, $obj2); 11 | } 12 | 13 | When two objects (both implementing the interface) are comapred using `<`, `>` or `==` the 14 | `compare()` method will be invoked. One should not rely on which class the method is invoked 15 | on. (Due to technical reasons for `$l < $r` it will be called on the class of `$l`, but for 16 | `$l > $r` it will be called on the class of `$r`.) 17 | 18 | The `compare()` method can either return `null` to fall back to the default comparison behavior 19 | or one of the integer values `-1` (for "smaller"), `0` (for "equal") and `1` (for "greater"). If 20 | the returned value is not an integer, it will be cast to one. If it is not one of -1, 0 or 1 it 21 | will be normalized to them. 22 | 23 | An example (not sure how much sense it makes): 24 | 25 | ```php 26 | x = $x; $this->y = $y; $this->z = $z; 33 | } 34 | 35 | public static function compare($p1, $p2) { 36 | if ($p1->x == $p2->x && $p1->y == $p2->y && $p1->z == $p2->z) { 37 | return 0; 38 | } 39 | 40 | if ($p1->x < $p2->x && $p1->y < $p2->y && $p1->z < $p2->z) { 41 | return -1; 42 | } 43 | 44 | if ($p1->x > $p2->x && $p1->y > $p2->y && $p1->z > $p2->z) { 45 | return 1; 46 | } 47 | 48 | return 1; 49 | } 50 | } 51 | 52 | $p1 = new Point(1, 1, 1); 53 | $p2 = new Point(2, 2, 2); 54 | $p3 = new Point(1, 0, 2); 55 | 56 | var_dump($p1 < $p2, $p1 > $p2, $p1 == $p2); // true, false, false 57 | 58 | var_dump($p1 == $p1); // true 59 | 60 | var_dump($p1 < $p3, $p1 > $p3, $p1 == $p3); // false, false, false 61 | ``` 62 | 63 | Installation 64 | ------------ 65 | 66 | The extension is installed as usual: 67 | 68 | ./configure --enable-comparable 69 | make 70 | make install 71 | -------------------------------------------------------------------------------- /comparable.c: -------------------------------------------------------------------------------- 1 | /* 2 | +----------------------------------------------------------------------+ 3 | | PHP Version 5 | 4 | +----------------------------------------------------------------------+ 5 | | Copyright (c) 1997-2013 The PHP Group | 6 | +----------------------------------------------------------------------+ 7 | | This source file is subject to version 3.01 of the PHP license, | 8 | | that is bundled with this package in the file LICENSE, and is | 9 | | available through the world-wide-web at the following url: | 10 | | http://www.php.net/license/3_01.txt | 11 | | If you did not receive a copy of the PHP license and are unable to | 12 | | obtain it through the world-wide-web, please send a note to | 13 | | license@php.net so we can mail you a copy immediately. | 14 | +----------------------------------------------------------------------+ 15 | | Author: Nikita Popov | 16 | +----------------------------------------------------------------------+ 17 | */ 18 | 19 | /* $Id$ */ 20 | 21 | #ifdef HAVE_CONFIG_H 22 | #include "config.h" 23 | #endif 24 | 25 | #include "php.h" 26 | #include "php_comparable.h" 27 | #include "zend_interfaces.h" 28 | 29 | zend_module_entry comparable_module_entry = { 30 | STANDARD_MODULE_HEADER, 31 | "comparable", 32 | NULL, 33 | PHP_MINIT(comparable), 34 | NULL, 35 | NULL, 36 | NULL, 37 | NULL, 38 | "0.1", 39 | STANDARD_MODULE_PROPERTIES 40 | }; 41 | 42 | #ifdef COMPILE_DL_COMPARABLE 43 | ZEND_GET_MODULE(comparable) 44 | #endif 45 | 46 | zend_class_entry *comparable_ce; 47 | 48 | static zend_object_handlers comparable_handlers; 49 | 50 | static zend_object_value comparable_create_object_override(zend_class_entry *ce TSRMLS_DC) 51 | { 52 | zend_object *object; 53 | zend_object_value retval; 54 | 55 | retval = zend_objects_new(&object, ce TSRMLS_CC); 56 | object_properties_init(object, ce); 57 | 58 | retval.handlers = &comparable_handlers; 59 | 60 | return retval; 61 | } 62 | 63 | static int comparable_compare_objects(zval *obj1, zval *obj2 TSRMLS_DC) 64 | { 65 | zval *retval = NULL; 66 | int result; 67 | 68 | zend_call_method_with_2_params(NULL, Z_OBJCE_P(obj1), NULL, "compare", &retval, obj1, obj2); 69 | 70 | if (!retval || Z_TYPE_P(retval) == IS_NULL) { 71 | if (retval) { 72 | zval_ptr_dtor(&retval); 73 | } 74 | return zend_get_std_object_handlers()->compare_objects(obj1, obj2 TSRMLS_CC); 75 | } 76 | 77 | convert_to_long_ex(&retval); 78 | result = ZEND_NORMALIZE_BOOL(Z_LVAL_P(retval)); 79 | zval_ptr_dtor(&retval); 80 | 81 | return result; 82 | } 83 | 84 | static int implement_comparable(zend_class_entry *interface, zend_class_entry *ce TSRMLS_DC) 85 | { 86 | if (ce->create_object != NULL) { 87 | zend_error(E_ERROR, "Comparable interface can only be used on userland classes"); 88 | } 89 | 90 | ce->create_object = comparable_create_object_override; 91 | 92 | return SUCCESS; 93 | } 94 | 95 | ZEND_BEGIN_ARG_INFO_EX(arginfo_comparable, 0, 0, 2) 96 | ZEND_ARG_INFO(0, obj1) 97 | ZEND_ARG_INFO(0, obj2) 98 | ZEND_END_ARG_INFO() 99 | 100 | const zend_function_entry comparable_functions[] = { 101 | ZEND_FENTRY(compare, NULL, arginfo_comparable, ZEND_ACC_PUBLIC|ZEND_ACC_ABSTRACT|ZEND_ACC_STATIC) 102 | PHP_FE_END 103 | }; 104 | 105 | PHP_MINIT_FUNCTION(comparable) 106 | { 107 | zend_class_entry tmp_ce; 108 | INIT_CLASS_ENTRY(tmp_ce, "Comparable", comparable_functions); 109 | comparable_ce = zend_register_internal_interface(&tmp_ce TSRMLS_CC); 110 | 111 | comparable_ce->interface_gets_implemented = implement_comparable; 112 | 113 | memcpy(&comparable_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); 114 | comparable_handlers.compare_objects = comparable_compare_objects; 115 | 116 | return SUCCESS; 117 | } 118 | -------------------------------------------------------------------------------- /config.m4: -------------------------------------------------------------------------------- 1 | dnl $Id$ 2 | dnl config.m4 for extension comparable 3 | 4 | PHP_ARG_ENABLE(comparable, whether to enable comparable support, 5 | [ --enable-comparable Enable comparable support]) 6 | 7 | if test "$PHP_COMPARABLE" != "no"; then 8 | PHP_NEW_EXTENSION(comparable, comparable.c, $ext_shared) 9 | fi 10 | -------------------------------------------------------------------------------- /config.w32: -------------------------------------------------------------------------------- 1 | // $Id$ 2 | // vim:ft=javascript 3 | 4 | Otherwise, use ARG_ENABLE 5 | ARG_ENABLE("comparable", "enable comparable support", "no"); 6 | 7 | if (PHP_COMPARABLE != "no") { 8 | EXTENSION("comparable", "comparable.c"); 9 | } 10 | 11 | -------------------------------------------------------------------------------- /php_comparable.h: -------------------------------------------------------------------------------- 1 | /* 2 | +----------------------------------------------------------------------+ 3 | | PHP Version 5 | 4 | +----------------------------------------------------------------------+ 5 | | Copyright (c) 1997-2013 The PHP Group | 6 | +----------------------------------------------------------------------+ 7 | | This source file is subject to version 3.01 of the PHP license, | 8 | | that is bundled with this package in the file LICENSE, and is | 9 | | available through the world-wide-web at the following url: | 10 | | http://www.php.net/license/3_01.txt | 11 | | If you did not receive a copy of the PHP license and are unable to | 12 | | obtain it through the world-wide-web, please send a note to | 13 | | license@php.net so we can mail you a copy immediately. | 14 | +----------------------------------------------------------------------+ 15 | | Author: | 16 | +----------------------------------------------------------------------+ 17 | */ 18 | 19 | /* $Id$ */ 20 | 21 | #ifndef PHP_COMPARABLE_H 22 | #define PHP_COMPARABLE_H 23 | 24 | extern zend_module_entry comparable_module_entry; 25 | #define phpext_comparable_ptr &comparable_module_entry 26 | 27 | #ifdef PHP_WIN32 28 | # define PHP_COMPARABLE_API __declspec(dllexport) 29 | #elif defined(__GNUC__) && __GNUC__ >= 4 30 | # define PHP_COMPARABLE_API __attribute__ ((visibility("default"))) 31 | #else 32 | # define PHP_COMPARABLE_API 33 | #endif 34 | 35 | #ifdef ZTS 36 | #include "TSRM.h" 37 | #endif 38 | 39 | PHP_MINIT_FUNCTION(comparable); 40 | 41 | ZEND_BEGIN_MODULE_GLOBALS(comparable) 42 | HashTable orig_create_object_handlers; 43 | ZEND_END_MODULE_GLOBALS(comparable) 44 | 45 | #ifdef ZTS 46 | #define COMPARABLE_G(v) TSRMG(comparable_globals_id, zend_comparable_globals *, v) 47 | #else 48 | #define COMPARABLE_G(v) (comparable_globals.v) 49 | #endif 50 | 51 | #endif 52 | 53 | /* 54 | * Local variables: 55 | * tab-width: 4 56 | * c-basic-offset: 4 57 | * End: 58 | * vim600: noet sw=4 ts=4 fdm=marker 59 | * vim<600: noet sw=4 ts=4 60 | */ 61 | --------------------------------------------------------------------------------