diff --git a/.travis.yml b/.travis.yml index 25b34e4..8fe9415 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,8 +10,7 @@ matrix: install: - composer install - - yes | git clone git@github.com:php-test-helpers/php-test-helpers.git - - cd php-test-helpers + - cd vendors/php-test-helpers - phpize - ./configure --enable-test-helpers - make diff --git a/vendors/php-test-helpers/.gitignore b/vendors/php-test-helpers/.gitignore new file mode 100755 index 0000000..f963692 --- /dev/null +++ b/vendors/php-test-helpers/.gitignore @@ -0,0 +1,32 @@ +.deps +.libs +build +modules +*.lo +*.loT +*.la +Makefile +Makefile.fragments +Makefile.global +Makefile.objects +acinclude.m4 +aclocal.m4 +config.cache +config.guess +config.h +config.h.in +config.log +config.nice +config.status +config.sub +configure +configure.in +install-sh +libtool +ltmain.sh +missing +mkinstalldirs +run-tests.php +test.ini +tmp-php.ini +autom4te.cache diff --git a/vendors/php-test-helpers/.travis.yml b/vendors/php-test-helpers/.travis.yml new file mode 100755 index 0000000..01b1da3 --- /dev/null +++ b/vendors/php-test-helpers/.travis.yml @@ -0,0 +1,15 @@ +language: php + +php: + - 5.3 + - 5.4 + - 5.5 + +before_script: + - phpize + - ./configure --enable-test-helpers + - make + - sed -i -e 's/save_or_mail_results();//g' -e 's/\($failed_test_data\['\''test_name'\''\] . $failed_test_data\['\''info'\''\]\)/str_repeat('\''='\'', 80) . "\\n" . \1 . "\\n" . str_repeat('\''='\'', 80) . "\\n" . file_get_contents(realpath($failed_test_data['\''output'\'']), FILE_BINARY) . "\\n" . str_repeat('\''='\'', 80) . "\\n" . file_get_contents(realpath($failed_test_data['\''diff'\'']), FILE_BINARY)/g' run-tests.php + - sed -i -e 's/-\(@if test ! -z\)/\1/' -e 's/rm $(top_builddir)\/tmp-php.ini;//' -e 's/^\(.*TEST_PHP_EXECUTABLE=$(PHP_EXECUTABLE) \\\)/\t\t$(SED) -i '\''s\/^\\(.*xdebug.so\\)\/;\\1\/'\'' $\(top_builddir\)\/tmp-php.ini; \\\n\1/' Makefile + +script: REPORT_EXIT_STATUS=1 make test diff --git a/vendors/php-test-helpers/CREDITS b/vendors/php-test-helpers/CREDITS new file mode 100755 index 0000000..9174cea --- /dev/null +++ b/vendors/php-test-helpers/CREDITS @@ -0,0 +1,2 @@ +test_helpers +Sebastian Bergmann, Johannes Schlüter diff --git a/vendors/php-test-helpers/README.markdown b/vendors/php-test-helpers/README.markdown new file mode 100755 index 0000000..ef9bd9a --- /dev/null +++ b/vendors/php-test-helpers/README.markdown @@ -0,0 +1,197 @@ +# ext/test_helpers # + +`ext/test_helpers` is an extension for the PHP Interpreter to ease testing of PHP code. + +## Installation ## + +`ext/test_helpers` should be installed using the [PEAR Installer](http://pear.php.net/). This installer is the backbone of PEAR and PECL, which provides a distribution system for PHP packages and extensions, and is shipped with every release of PHP since version 4.3.0. + +The PEAR channel (`pear.phpunit.de`) that is used to distribute `ext/test_helpers` needs to be registered with the local PEAR environment: + + sb@ubuntu ~ % pear channel-discover pear.phpunit.de + Adding Channel "pear.phpunit.de" succeeded + Discovery of channel "pear.phpunit.de" succeeded + +This has to be done only once. Now the PEAR Installer can be used to install extensions and packages from the PHPUnit channel: + + sb@ubuntu ~ % pecl install phpunit/test_helpers + downloading test_helpers-1.0.0.tgz ... + Starting to download test_helpers-1.0.0.tgz (6,980 bytes) + .....done: 6,980 bytes + 4 source files, building + . + . + . + install ok: channel://pear.phpunit.de/test_helpers-1.0.0 + You should add "extension=test_helpers.so" to php.ini + +Further information about building stand-alone extensions for PHP can be found in the [Installation of PECL extensions](http://php.net/install.pecl) section of the PHP manual. + +## Usage ## + +### Intercepting the Exit Statement ### + +When a [unit test](http://en.wikipedia.org/wiki/Unit_test) exercises code that contains an `exit` / `die` statement, the execution of the whole test suite is aborted. This is not a good thing. + +With the `set_exit_overload()` function it is possible to overload the `exit` / `die` statement and make it a no-op, for instance: + + + +The code above will output + + We did not exit. + +The callback registered by `set_exit_overload()` might receive a parameter in case `exit` / `die` was called with a parameter: + + + +The code above will output + + Hello + No value given + +Another way of dealing with low-level functions and statements such as `die()` and `exit` is to wrap them in a proxy that by default (in production) delegates to the native implementation but for testing has a "testable" behaviour. + +### Intercepting Object Creation ### + +In a [unit test](http://en.wikipedia.org/wiki/Unit_test), [mock objects](http://en.wikipedia.org/wiki/Mock_Object) can simulate the behavior of complex, real (non-mock) objects and are therefore useful when a real object is difficult or impossible to incorporate into a unit test. + +A mock object can be used anywhere in the program where the program expects an object of the mocked class. However, this only works as long as the object can be passed into the context where the original object is used. + +Consider the following example: + + + +With the code above, it is impossible to run a unit test for the `SomeClass::doSomething()` method without also creating an object of `SomeOtherClass`. As the method creates the object of `SomeOtherClass` itself, we cannot inject a mock object in its stead. + +In a perfect world, code such as the above could be refactored using [Dependency Injection](http://en.wikipedia.org/wiki/Dependency_Injection): + + object = $object; + } + + public function doSomething() + { + // ... + } + } + ?> + +Unfortunately, this is not always possible (not because of technical reasons, though). + +This is where the `set_new_overload()` function comes into play. It can be used to register a [callback](http://www.php.net/manual/en/language.pseudo-types.php) that is automatically invoked when the `new` operator is executed: + + + + string(3) "Foo" + string(3) "Bar" + +The `new` operator callback can be unset when it is no longer required: + + + + string(3) "Bar" + string(3) "Foo" + +#### Class Posing #### + +The `set_new_overload()` function can be used to implement a programming language feature named *Class Posing*. [The implementation of Class Posing in Objective-C](http://en.wikipedia.org/wiki/Objective-C#Posing), for instance, permits a class to wholly replace another class within a program. The replacing class is said to "pose as" the target class. + +Class Posing has the following restrictions + +* A class may only pose as one of its direct or indirect superclasses +* The posing class must not define any new instance variables which are absent from the target class (though it may define or override methods). +* The target class may not have received any messages prior to the posing. + +These restrictions are not enforced by `ext/test_helpers` because the extension is only intended to ease the development of unit tests (for legacy software systems that cannot be refactored to use Dependency Injection). + +### Renaming Functions ### + +The `rename_function()` function can be used to rename function: + + + + string(14) "stubbed result" + +This allows the stubbing/mocking of functions. + +## Notes ## + +If this extension is used in combination with other extensions, such as Xdebug, which are also overloading the `ZEND_NEW` opcode you have to load it as `zend_extension` after loading the conflicting extension. This can be done in your `php.ini` like this: + + zend_extension=xdebug.so + zend_extension=test-helpers.so + +Please refer to `phpinfo()` to verify whether a conflict was detected and whether the work-around was enabled. + diff --git a/vendors/php-test-helpers/config.m4 b/vendors/php-test-helpers/config.m4 new file mode 100755 index 0000000..332b312 --- /dev/null +++ b/vendors/php-test-helpers/config.m4 @@ -0,0 +1,6 @@ +PHP_ARG_ENABLE(test_helpers, whether to enable test_helpers support, +[ --enable-test-helpers Enable test_helpers support]) + +if test "$PHP_TEST_HELPERS" != "no"; then + PHP_NEW_EXTENSION(test_helpers, test_helpers.c, $ext_shared) +fi diff --git a/vendors/php-test-helpers/config.w32 b/vendors/php-test-helpers/config.w32 new file mode 100755 index 0000000..b0bc358 --- /dev/null +++ b/vendors/php-test-helpers/config.w32 @@ -0,0 +1,6 @@ +ARG_ENABLE("test_helpers", "enable test_helpers support", "no"); + +if (PHP_TEST_HELPERS != "no") { + EXTENSION("test_helpers", "test_helpers.c"); +} + diff --git a/vendors/php-test-helpers/package.xml b/vendors/php-test-helpers/package.xml new file mode 100755 index 0000000..aa9e1f2 --- /dev/null +++ b/vendors/php-test-helpers/package.xml @@ -0,0 +1,75 @@ + + + test_helpers + pear.phpunit.de + An extension for the PHP Interpreter to ease testing of PHP code. + An extension for the PHP Interpreter to ease testing of PHP code. + + Sebastian Bergmann + sebastian + sebastian@php.net + yes + + + Johannes Schlueter + johannes + johannes@php.net + yes + + 2010-10-25 + + 1.1.0 + 1.1.0 + + + stable + stable + + BSD License + http://github.com/sebastianbergmann/php-test-helpers + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5.2.1 + + + 1.4.0b1 + + + + test_helpers + + diff --git a/vendors/php-test-helpers/php_test_helpers.h b/vendors/php-test-helpers/php_test_helpers.h new file mode 100755 index 0000000..8e14c1f --- /dev/null +++ b/vendors/php-test-helpers/php_test_helpers.h @@ -0,0 +1,59 @@ +/* + +----------------------------------------------------------------------+ + | ext/test_helper | + | An extension for the PHP Interpreter to ease testing of PHP code. | + +----------------------------------------------------------------------+ + | Copyright (c) 2009-2013 Sebastian Bergmann. All rights reserved. | + +----------------------------------------------------------------------+ + | Redistribution and use in source and binary forms, with or without | + | modification, are permitted provided that the following conditions | + | are met: | + | | + | * Redistributions of source code must retain the above copyright | + | notice, this list of conditions and the following disclaimer. | + | | + | * Redistributions in binary form must reproduce the above copyright | + | notice, this list of conditions and the following disclaimer in | + | the documentation and/or other materials provided with the | + | distribution. | + | | + | * Neither the name of Sebastian Bergmann nor the names of his | + | contributors may be used to endorse or promote products derived | + | from this software without specific prior written permission. | + | | + | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | + | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | + | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS | + | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE | + | COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, | + | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, | + | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | + | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | + | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | + | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN | + | ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | + | POSSIBILITY OF SUCH DAMAGE. | + +----------------------------------------------------------------------+ + | Author: Sebastian Bergmann | + | Johannes Schlüter | + +----------------------------------------------------------------------+ +*/ + +#ifndef PHP_TEST_HELPERS_H +#define PHP_TEST_HELPERS_H + +extern zend_module_entry test_helpers_module_entry; +#define phpext_test_helpers_ptr &test_helpers_module_entry + +#define TEST_HELPERS_VERSION "1.0.1-dev" + +#endif /* PHP_TEST_HELPERS_H */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ diff --git a/vendors/php-test-helpers/test_helpers.c b/vendors/php-test-helpers/test_helpers.c new file mode 100755 index 0000000..bb029a2 --- /dev/null +++ b/vendors/php-test-helpers/test_helpers.c @@ -0,0 +1,671 @@ +/* + +----------------------------------------------------------------------+ + | ext/test_helper | + | An extension for the PHP Interpreter to ease testing of PHP code. | + +----------------------------------------------------------------------+ + | Copyright (c) 2009-2013 Sebastian Bergmann. All rights reserved. | + +----------------------------------------------------------------------+ + | Redistribution and use in source and binary forms, with or without | + | modification, are permitted provided that the following conditions | + | are met: | + | | + | * Redistributions of source code must retain the above copyright | + | notice, this list of conditions and the following disclaimer. | + | | + | * Redistributions in binary form must reproduce the above copyright | + | notice, this list of conditions and the following disclaimer in | + | the documentation and/or other materials provided with the | + | distribution. | + | | + | * Neither the name of Sebastian Bergmann nor the names of his | + | contributors may be used to endorse or promote products derived | + | from this software without specific prior written permission. | + | | + | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | + | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | + | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS | + | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE | + | COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, | + | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, | + | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | + | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | + | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | + | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN | + | ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | + | POSSIBILITY OF SUCH DAMAGE. | + +----------------------------------------------------------------------+ + | Author: Johannes Schlüter | + | Scott MacVicar | + | Sebastian Bergmann | + +----------------------------------------------------------------------+ +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "php.h" +#include "php_ini.h" +#include "ext/standard/info.h" +#include "php_test_helpers.h" +#include "Zend/zend_exceptions.h" +#include "Zend/zend_extensions.h" + +#ifdef PHP_WIN32 +# define PHP_TEST_HELPERS_API __declspec(dllexport) +#elif defined(__GNUC__) && __GNUC__ >= 4 +# define PHP_TEST_HELPERS_API __attribute__ ((visibility("default"))) +#else +# define PHP_TEST_HELPERS_API +#endif + +/* {{{ PHP < 5.3.0 */ +#if PHP_VERSION_ID < 50300 +typedef opcode_handler_t user_opcode_handler_t; + +#define Z_ADDREF_P(z) ((z)->refcount++) + +#define zend_parse_parameters_none() zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "") + +static void zend_fcall_info_args_clear(zend_fcall_info *fci, int free_mem) /* {{{ */ +{ + if (fci->params) { + if (free_mem) { + efree(fci->params); + fci->params = NULL; + } + } + fci->param_count = 0; +} +/* }}} */ + +static int zend_fcall_info_argv(zend_fcall_info *fci TSRMLS_DC, int argc, va_list *argv) /* {{{ */ +{ + int i; + zval **arg; + + if (argc < 0) { + return FAILURE; + } + + zend_fcall_info_args_clear(fci, !argc); + + if (argc) { + fci->param_count = argc; + fci->params = (zval ***) erealloc(fci->params, fci->param_count * sizeof(zval **)); + + for (i = 0; i < argc; ++i) { + arg = va_arg(*argv, zval **); + fci->params[i] = arg; + } + } + + return SUCCESS; +} +/* }}} */ + +static int zend_fcall_info_argn(zend_fcall_info *fci TSRMLS_DC, int argc, ...) /* {{{ */ +{ + int ret; + va_list argv; + + va_start(argv, argc); + ret = zend_fcall_info_argv(fci TSRMLS_CC, argc, &argv); + va_end(argv); + + return ret; +} +/* }}} */ +#endif /* PHP_VERSION_ID < 50300 */ +/* }}} */ + +static user_opcode_handler_t old_new_handler = NULL; +static user_opcode_handler_t old_exit_handler = NULL; +static int test_helpers_module_initialized = 0; + +typedef struct { + zend_fcall_info fci; + zend_fcall_info_cache fcc; +} user_handler_t; + +ZEND_BEGIN_MODULE_GLOBALS(test_helpers) + user_handler_t new_handler; + user_handler_t exit_handler; +ZEND_END_MODULE_GLOBALS(test_helpers) + +ZEND_DECLARE_MODULE_GLOBALS(test_helpers) + +#ifdef ZTS +#define THG(v) TSRMG(test_helpers_globals_id, zend_test_helpers_globals *, v) +#else +#define THG(v) (test_helpers_globals.v) +#endif + +#undef EX +#define EX(element) execute_data->element + +#if PHP_VERSION_ID >= 50500 +# define EX_T(offset) (*EX_TMP_VAR(execute_data, offset)) +#else +# define EX_T(offset) (*(temp_variable *)((char*)execute_data->Ts + offset)) +#endif + +#if PHP_VERSION_ID >= 50399 +# define PTH_ZNODE znode_op +# define PTH_TYPE(t) t##_type +#else +# define PTH_ZNODE znode +# define PTH_TYPE(t) t.op_type +#endif + +zval *pth_get_zval_ptr(int node_type, PTH_ZNODE *node, zval **freeval, zend_execute_data *execute_data TSRMLS_DC) +{ + *freeval = NULL; + + switch (node_type) { + case IS_CONST: +#if PHP_VERSION_ID >= 50399 + return node->zv; +#else + return &node->u.constant; +#endif + break; + + case IS_VAR: +#if PHP_VERSION_ID >= 50399 + if (EX_T(node->var).var.ptr) { + return EX_T(node->var).var.ptr; +#else + if (EX_T(node->u.var).var.ptr) { + return EX_T(node->u.var).var.ptr; +#endif + } + break; + + case IS_TMP_VAR: +#if PHP_VERSION_ID >= 50399 + return (*freeval = &EX_T(node->var).tmp_var); +#else + return (*freeval = &EX_T(node->u.var).tmp_var); +#endif + break; + + case IS_CV: { + zval **tmp; +#if PHP_VERSION_ID >= 50399 + tmp = zend_get_compiled_variable_value(execute_data, node->constant); +#else + tmp = zend_get_compiled_variable_value(execute_data, node->u.constant.value.lval); +#endif + if (tmp) { + return *tmp; + } + break; + } + } + + return NULL; +} + +static void test_helpers_free_handler(zend_fcall_info *fci TSRMLS_DC) /* {{{ */ +{ + if (fci->function_name) { + zval_ptr_dtor(&fci->function_name); + fci->function_name = NULL; + } +#if PHP_VERSION_ID >= 50300 + if (fci->object_ptr) { + zval_ptr_dtor(&fci->object_ptr); + fci->object_ptr = NULL; + } +#endif +} +/* }}} */ + +static int pth_new_handler(ZEND_OPCODE_HANDLER_ARGS) /* {{{ */ +{ + zval *retval, *arg; + zend_op *opline = EX(opline); + zend_class_entry *old_ce, **new_ce; + + if (THG(new_handler).fci.function_name == NULL) { + if (old_new_handler) { + return old_new_handler(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); + } else { + return ZEND_USER_OPCODE_DISPATCH; + } + } + +#if ZEND_EXTENSION_API_NO >= 220100525 + old_ce = EX_T(opline->op1.var).class_entry; +#else + old_ce = EX_T(opline->op1.u.var).class_entry; +#endif + + MAKE_STD_ZVAL(arg); + ZVAL_STRINGL(arg, old_ce->name, old_ce->name_length, 1); + + zend_fcall_info_argn(&THG(new_handler).fci TSRMLS_CC, 1, &arg); + zend_fcall_info_call(&THG(new_handler).fci, &THG(new_handler).fcc, &retval, NULL TSRMLS_CC); + zend_fcall_info_args_clear(&THG(new_handler).fci, 1); + + convert_to_string_ex(&retval); + if (zend_lookup_class(Z_STRVAL_P(retval), Z_STRLEN_P(retval), &new_ce TSRMLS_CC) == FAILURE) { + if (!EG(exception)) { + zend_throw_exception_ex(zend_exception_get_default(TSRMLS_C), -1 TSRMLS_CC, "Class %s does not exist", Z_STRVAL_P(retval)); + } + zval_ptr_dtor(&arg); + zval_ptr_dtor(&retval); + + return ZEND_USER_OPCODE_CONTINUE; + } + + zval_ptr_dtor(&arg); + zval_ptr_dtor(&retval); + + +#if ZEND_EXTENSION_API_NO >= 220100525 + EX_T(opline->op1.var).class_entry = *new_ce; +#else + EX_T(opline->op1.u.var).class_entry = *new_ce; +#endif + + if (old_new_handler) { + return old_new_handler(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); + } else { + return ZEND_USER_OPCODE_DISPATCH; + } +} +/* }}} */ + +static int pth_exit_handler(ZEND_OPCODE_HANDLER_ARGS) /* {{{ */ +{ + zval *msg, *freeop; + zend_op *opline = EX(opline); + zval *retval = NULL; + + if (THG(exit_handler).fci.function_name == NULL) { + if (old_exit_handler) { + return old_exit_handler(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); + } else { + return ZEND_USER_OPCODE_DISPATCH; + } + } + + msg = pth_get_zval_ptr(opline->PTH_TYPE(op1), &opline->op1, &freeop, execute_data TSRMLS_CC); + + if (msg) { + zend_fcall_info_argn(&THG(exit_handler).fci TSRMLS_CC, 1, &msg); + } + zend_fcall_info_call(&THG(exit_handler).fci, &THG(exit_handler).fcc, &retval, NULL TSRMLS_CC); + zend_fcall_info_args_clear(&THG(exit_handler).fci, 1); + + if(UNEXPECTED(retval == NULL)) { + EX(opline)++; + return ZEND_USER_OPCODE_CONTINUE; + } + + convert_to_boolean(retval); + if (Z_LVAL_P(retval)) { + zval_ptr_dtor(&retval); + if (old_exit_handler) { + return old_exit_handler(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); + } else { + return ZEND_USER_OPCODE_DISPATCH; + } + } else { + zval_ptr_dtor(&retval); + EX(opline)++; + return ZEND_USER_OPCODE_CONTINUE; + } +} +/* }}} */ + +static void php_test_helpers_init_globals(zend_test_helpers_globals *globals) /* {{{ */ +{ + globals->new_handler.fci.function_name = NULL; + globals->exit_handler.fci.function_name = NULL; +#if PHP_VERSION_ID >= 50300 + globals->new_handler.fci.object_ptr = NULL; + globals->exit_handler.fci.object_ptr = NULL; +#endif +} +/* }}} */ + +/* {{{ PHP_MINIT_FUNCTION + */ +static PHP_MINIT_FUNCTION(test_helpers) +{ + if (test_helpers_module_initialized) { + /* This should never happen as it is handled by the module loader, but let's play safe */ + php_error_docref(NULL TSRMLS_CC, E_WARNING, "test_helpers had already been initialized! Either load it as regular PHP extension or zend_extension"); + return FAILURE; + } + + ZEND_INIT_MODULE_GLOBALS(test_helpers, php_test_helpers_init_globals, NULL); + old_new_handler = zend_get_user_opcode_handler(ZEND_NEW); + zend_set_user_opcode_handler(ZEND_NEW, pth_new_handler); + + old_exit_handler = zend_get_user_opcode_handler(ZEND_EXIT); + zend_set_user_opcode_handler(ZEND_EXIT, pth_exit_handler); + + test_helpers_module_initialized = 1; + + return SUCCESS; +} +/* }}} */ + +/* {{{ PHP_RSHUTDOWN_FUNCTION + */ +static PHP_RSHUTDOWN_FUNCTION(test_helpers) +{ + test_helpers_free_handler(&THG(new_handler).fci TSRMLS_CC); + test_helpers_free_handler(&THG(exit_handler).fci TSRMLS_CC); + return SUCCESS; +} +/* }}} */ + +/* {{{ PHP_MINFO_FUNCTION + */ +static PHP_MINFO_FUNCTION(test_helpers) +{ + char *conflict_text; + + if (pth_new_handler != zend_get_user_opcode_handler(ZEND_NEW)) { + conflict_text = "Yes. The work-around was NOT enabled. Please make sure test_helpers was loaded as zend_extension AFTER conflicting extensions like Xdebug!"; + } else if (old_new_handler != NULL) { + conflict_text = "Yes, work-around enabled"; + } else { + conflict_text = "No conflict detected"; + } + php_info_print_table_start(); + php_info_print_table_header(2, "test_helpers support", "enabled"); + php_info_print_table_row(2, "Conflicting extension found", conflict_text); + php_info_print_table_end(); +} +/* }}} */ + +static void overload_helper(user_opcode_handler_t op_handler, int opcode, user_handler_t *handler, INTERNAL_FUNCTION_PARAMETERS) /* {{{ */ +{ + zend_fcall_info fci; + zend_fcall_info_cache fcc; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "f", &fci, &fcc) == FAILURE) { + return; + } + + if (op_handler != zend_get_user_opcode_handler(opcode)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "A conflicting extension was detected. Make sure to load test_helpers as zend_extension after other extensions"); + } + + test_helpers_free_handler(&handler->fci TSRMLS_CC); + + handler->fci = fci; + handler->fcc = fcc; + Z_ADDREF_P(handler->fci.function_name); +#if PHP_VERSION_ID >= 50300 + if (handler->fci.object_ptr) { + Z_ADDREF_P(handler->fci.object_ptr); + } +#endif + + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto bool set_new_overload(callback cb) + Register a callback, called on instantiation of a new object */ +static PHP_FUNCTION(set_new_overload) +{ + overload_helper(pth_new_handler, ZEND_NEW, &THG(new_handler), INTERNAL_FUNCTION_PARAM_PASSTHRU); +} +/* }}} */ + +/* {{{ proto bool set_exit_overload(callback cb) + Register a callback, called on exit()/die() */ +static PHP_FUNCTION(set_exit_overload) +{ + overload_helper(pth_exit_handler, ZEND_EXIT, &THG(exit_handler), INTERNAL_FUNCTION_PARAM_PASSTHRU); +} +/* }}} */ + +static void unset_overload_helper(user_handler_t *handler, INTERNAL_FUNCTION_PARAMETERS) /* {{{ */ +{ + if (zend_parse_parameters_none() == FAILURE) { + return; + } + + test_helpers_free_handler(&handler->fci TSRMLS_CC); + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto bool unset_new_overload() + Remove the current new handler */ +static PHP_FUNCTION(unset_new_overload) +{ + unset_overload_helper(&THG(new_handler), INTERNAL_FUNCTION_PARAM_PASSTHRU); +} +/* }}} */ + +/* {{{ proto bool unset_exit_overload() + Remove the current exit handler */ +static PHP_FUNCTION(unset_exit_overload) +{ + unset_overload_helper(&THG(exit_handler), INTERNAL_FUNCTION_PARAM_PASSTHRU); +} +/* }}} */ + +static int pth_rename_function_impl(HashTable *table, char *orig, int orig_len, char *new, int new_len TSRMLS_DC) /* {{{ */ +{ + zend_function *func, *dummy_func; + + if (zend_hash_find(table, orig, orig_len + 1, (void **) &func) == FAILURE) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s(%s, %s) failed: %s does not exist!" , + get_active_function_name(TSRMLS_C), + orig, new, orig); + return FAILURE; + } + + /* TODO: Add infrastructure for resetting internal funcs */ + if (func->type != ZEND_USER_FUNCTION) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "\"%s\" is an internal function", orig); + return FAILURE; + } + + if (zend_hash_find(table, new, new_len + 1, (void **) &dummy_func) == SUCCESS) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s(%s, %s) failed: %s already exists!" , + get_active_function_name(TSRMLS_C), + orig, new, new); + return FAILURE; + } + + if (zend_hash_add(table, new, new_len + 1, func, sizeof(zend_function), NULL) == FAILURE) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s() failed to insert %s into EG(function_table)", get_active_function_name(TSRMLS_C), new); + return FAILURE; + } + + if (func->type == ZEND_USER_FUNCTION) { + function_add_ref(func); + } + + if (zend_hash_del(table, orig, orig_len + 1) == FAILURE) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s() failed to remove %s from function table", get_active_function_name(TSRMLS_C), orig); + + zend_hash_del(table, new, new_len + 1); + return FAILURE; + } + + return SUCCESS; +} +/* }}} */ + +static int pth_rename_function(HashTable *table, char *orig, int orig_len, char *new, int new_len TSRMLS_DC) /* {{{ */ +{ + char *lower_orig, *lower_new; + int success; + + lower_orig = zend_str_tolower_dup(orig, orig_len); + lower_new = zend_str_tolower_dup(new, new_len); + + success = pth_rename_function_impl(table, lower_orig, orig_len, lower_new, new_len TSRMLS_CC); + + efree(lower_orig); + efree(lower_new); + + return success; +} +/* }}} */ + +/* {{{ proto bool rename_method(string class name, string orig_method_name, string new_method_name) + Rename a method inside a class. The method whil remain partof the same class */ +static PHP_FUNCTION(rename_method) +{ + zend_class_entry *ce = NULL; + char *orig_fname, *new_fname; + int orig_fname_len, new_fname_len; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Css", &ce, &orig_fname, &orig_fname_len, &new_fname, &new_fname_len) == FAILURE) { + return; + } + + if (SUCCESS == pth_rename_function(&ce->function_table, orig_fname, orig_fname_len, new_fname, new_fname_len TSRMLS_CC)) { + RETURN_TRUE; + } else { + RETURN_FALSE; + } +} +/* }}} */ + +/* {{{ proto bool rename_function(string orig_func_name, string new_func_name) + Rename a function from its original to a new name. This is mainly useful in + unittest to stub out untested functions */ +static PHP_FUNCTION(rename_function) +{ + char *orig_fname, *new_fname; + int orig_fname_len, new_fname_len; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &orig_fname, &orig_fname_len, &new_fname, &new_fname_len) == FAILURE) { + return; + } + + if (SUCCESS == pth_rename_function(EG(function_table), orig_fname, orig_fname_len, new_fname, new_fname_len TSRMLS_CC)) { + RETURN_TRUE; + } else { + RETURN_FALSE; + } +} +/* }}} */ + +/* {{{ arginfo */ +/* {{{ unset_new_overload */ +ZEND_BEGIN_ARG_INFO(arginfo_unset_new_overload, 0) +ZEND_END_ARG_INFO() +/* }}} */ + +/* {{{ unset_exit_overload */ +ZEND_BEGIN_ARG_INFO(arginfo_unset_exit_overload, 0) +ZEND_END_ARG_INFO() +/* }}} */ + +/* {{{ set_new_overload */ +ZEND_BEGIN_ARG_INFO(arginfo_set_new_overload, 0) + ZEND_ARG_INFO(0, callback) +ZEND_END_ARG_INFO() +/* }}} */ + +/* {{{ rename_method */ +ZEND_BEGIN_ARG_INFO(arginfo_rename_method, 0) + ZEND_ARG_INFO(0, class_name) + ZEND_ARG_INFO(0, orig_method_name) + ZEND_ARG_INFO(0, new_method_name) +ZEND_END_ARG_INFO() +/* }}} */ + +/* {{{ rename_function */ +ZEND_BEGIN_ARG_INFO(arginfo_rename_function, 0) + ZEND_ARG_INFO(0, orig_func_name) + ZEND_ARG_INFO(0, new_func_name) +ZEND_END_ARG_INFO() +/* }}} */ + +/* {{{ set_exit_overload */ +ZEND_BEGIN_ARG_INFO(arginfo_set_exit_overload, 0) + ZEND_ARG_INFO(0, "callback") +ZEND_END_ARG_INFO() +/* }}} */ + +/* }}} */ + +/* {{{ test_helpers_functions[] + */ +static const zend_function_entry test_helpers_functions[] = { + PHP_FE(unset_new_overload, arginfo_unset_new_overload) + PHP_FE(set_new_overload, arginfo_set_new_overload) + PHP_FE(unset_exit_overload, arginfo_unset_exit_overload) + PHP_FE(set_exit_overload, arginfo_set_exit_overload) + PHP_FE(rename_method, arginfo_rename_method) + PHP_FE(rename_function, arginfo_rename_function) + {NULL, NULL, NULL} +}; +/* }}} */ + +/* {{{ test_helpers_module_entry + */ +zend_module_entry test_helpers_module_entry = { + STANDARD_MODULE_HEADER, + "test_helpers", + test_helpers_functions, + PHP_MINIT(test_helpers), + NULL, + NULL, + PHP_RSHUTDOWN(test_helpers), + PHP_MINFO(test_helpers), + TEST_HELPERS_VERSION, + STANDARD_MODULE_PROPERTIES +}; +/* }}} */ + +static int test_helpers_zend_startup(zend_extension *extension) /* {{{ */ +{ + return zend_startup_module(&test_helpers_module_entry); +} +/* }}} */ + +#ifndef ZEND_EXT_API +#define ZEND_EXT_API ZEND_DLEXPORT +#endif +ZEND_EXTENSION(); + +zend_extension zend_extension_entry = { + "test_helpers", + TEST_HELPERS_VERSION, + "Johannes Schlueter, Scott MacVicar, Sebastian Bergmann", + "http://github.com/johannes/php-test-helpers", + "Copyright (c) 2009-2013", + test_helpers_zend_startup, + NULL, /* shutdown_func_t */ + NULL, /* activate_func_t */ + NULL, /* deactivate_func_t */ + NULL, /* message_handler_func_t */ + NULL, /* op_array_handler_func_t */ + NULL, /* statement_handler_func_t */ + NULL, /* fcall_begin_handler_func_t */ + NULL, /* fcall_end_handler_func_t */ + NULL, /* op_array_ctor_func_t */ + NULL, /* op_array_dtor_func_t */ + STANDARD_ZEND_EXTENSION_PROPERTIES +}; + +#ifdef COMPILE_DL_TEST_HELPERS +ZEND_GET_MODULE(test_helpers) +#endif + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ diff --git a/vendors/php-test-helpers/tests/.gitignore b/vendors/php-test-helpers/tests/.gitignore new file mode 100755 index 0000000..2671150 --- /dev/null +++ b/vendors/php-test-helpers/tests/.gitignore @@ -0,0 +1,6 @@ +*.diff +*.exp +*.log +*.mem +*.out +*.php diff --git a/vendors/php-test-helpers/tests/load_as_zend_extension-php5.5.phpt b/vendors/php-test-helpers/tests/load_as_zend_extension-php5.5.phpt new file mode 100755 index 0000000..ebc9605 --- /dev/null +++ b/vendors/php-test-helpers/tests/load_as_zend_extension-php5.5.phpt @@ -0,0 +1,21 @@ +--TEST-- +Loading test_helpers as zend_extension +--INI-- +zend_extension=test_helpers.so +error_log= +display_errors=0 +--SKIPIF-- + +--FILE-- + +--EXPECT-- +PHP Warning: Module 'test_helpers' already loaded in Unknown on line 0 +done diff --git a/vendors/php-test-helpers/tests/load_as_zend_extension.phpt b/vendors/php-test-helpers/tests/load_as_zend_extension.phpt new file mode 100755 index 0000000..e9b6320 --- /dev/null +++ b/vendors/php-test-helpers/tests/load_as_zend_extension.phpt @@ -0,0 +1,21 @@ +--TEST-- +Loading test_helpers as zend_extension +--INI-- +zend_extension=modules/test_helpers.so +error_log= +display_errors=0 +--SKIPIF-- +=')) die("skip test is for PHP < 5.5."); +if (!extension_loaded('test_helpers')) die('skip test_helpers extension not loaded'); +if (!file_exists('modules/test_helpers.so')) die('skip test_helpers.so not found Static build? out-of-src-dir build?'); +?> +--FILE-- + +--EXPECT-- +PHP Warning: Module 'test_helpers' already loaded in Unknown on line 0 +done diff --git a/vendors/php-test-helpers/tests/rename_function.phpt b/vendors/php-test-helpers/tests/rename_function.phpt new file mode 100755 index 0000000..d639ee8 --- /dev/null +++ b/vendors/php-test-helpers/tests/rename_function.phpt @@ -0,0 +1,17 @@ +--TEST-- +rename_function() and user-defined functions +--SKIPIF-- + +--FILE-- + +--XFAIL-- +Not yet implemented +--FILE-- + + string(33) "Location: http://www.example.com/" +} diff --git a/vendors/php-test-helpers/tests/rename_method.phpt b/vendors/php-test-helpers/tests/rename_method.phpt new file mode 100755 index 0000000..1c82daf --- /dev/null +++ b/vendors/php-test-helpers/tests/rename_method.phpt @@ -0,0 +1,19 @@ +--TEST-- +rename_method() and user-defined functions +--SKIPIF-- + +--FILE-- + +--FILE-- + +--EXPECT-- +string(5) "DIE 1" +FALSE +NULL +FALSE +NULL +FALSE +string(5) "DIE 4" +TRUE +DIE 4 diff --git a/vendors/php-test-helpers/tests/set_exit_overload_exception.phpt b/vendors/php-test-helpers/tests/set_exit_overload_exception.phpt new file mode 100755 index 0000000..a1765e5 --- /dev/null +++ b/vendors/php-test-helpers/tests/set_exit_overload_exception.phpt @@ -0,0 +1,17 @@ +--TEST-- +set_exit_overload() with an uncaught exception +--SKIPIF-- + +--FILE-- +getMessage(); +} +?> +--EXPECT-- +Please don't segfault \ No newline at end of file diff --git a/vendors/php-test-helpers/tests/set_new_overload_backtrace.phpt b/vendors/php-test-helpers/tests/set_new_overload_backtrace.phpt new file mode 100755 index 0000000..c6cd4e1 --- /dev/null +++ b/vendors/php-test-helpers/tests/set_new_overload_backtrace.phpt @@ -0,0 +1,28 @@ +--TEST-- +debug_print_backtrace() should work inside the callback. +--SKIPIF-- + +--FILE-- + +--FILE-- + +--XFAIL-- +Currently not supported by the implementation but nice to have +--FILE-- + +--FILE-- + +--FILE-- + +--FILE-- + +--FILE-- + +--FILE-- + +--FILE-- + +--FILE-- +doSomethingElse(); + + // ... + } +} + +class Bar +{ + public function doSomethingElse() + { + var_dump(__METHOD__); + } +} + +class BarMock extends Bar +{ + public function doSomethingElse() + { + var_dump(__METHOD__); + } +} + +class FooTest +{ + public function setUp() + { + var_dump(__METHOD__); + + set_new_overload(array($this, 'newCallback')); + } + + public function tearDown() + { + var_dump(__METHOD__); + + unset_new_overload(); + } + + protected function newCallback($className) + { + var_dump(__METHOD__); + + switch ($className) { + case 'Bar': return 'BarMock'; + default: return $className; + } + } + + public function testDoSomething() + { + var_dump(__METHOD__); + + $foo = new Foo; + $foo->doSomething(); + } +} + +$test = new FooTest; +$test->setUp(); +$test->testDoSomething(); +$test->tearDown(); +--EXPECT-- +string(14) "FooTest::setUp" +string(24) "FooTest::testDoSomething" +string(20) "FooTest::newCallback" +string(16) "Foo::doSomething" +string(20) "FooTest::newCallback" +string(24) "BarMock::doSomethingElse" +string(17) "FooTest::tearDown" diff --git a/vendors/php-test-helpers/tests/set_new_overload_private_method.phpt b/vendors/php-test-helpers/tests/set_new_overload_private_method.phpt new file mode 100755 index 0000000..7c85353 --- /dev/null +++ b/vendors/php-test-helpers/tests/set_new_overload_private_method.phpt @@ -0,0 +1,38 @@ +--TEST-- +A private method can be registered from the right context with set_new_overload() +--SKIPIF-- + +--FILE-- +set_overload()); +var_dump(get_class(new Bar)); +--EXPECTF-- +Warning: set_new_overload() expects parameter 1 to be a valid callback, cannot access private method CB::callback() in %s on line %d +NULL +string(3) "Bar" +bool(true) +string(3) "Foo" diff --git a/vendors/php-test-helpers/tests/set_new_overload_static_method.phpt b/vendors/php-test-helpers/tests/set_new_overload_static_method.phpt new file mode 100755 index 0000000..dd8231b --- /dev/null +++ b/vendors/php-test-helpers/tests/set_new_overload_static_method.phpt @@ -0,0 +1,23 @@ +--TEST-- +A static method can be registered as a callback with set_new_overload() +--SKIPIF-- + +--FILE-- + +--FILE-- + +--FILE-- + +--EXPECT-- +DIE