com php-src: zend-test extension to house code that is required for testing internal APIs, but that we would not want to expose for regular builds: Zend/tests/return_types/internal_functions001.phpt Zend/tests/return_types/internal_functions002.ph pt Zend/zend_builtin_functions.c appveyor/build_tas k.bat ext/sockets/tests/socket_export_stream-5.phpt ext/sockets/tests/socket_import_stream-5.phpt ext/ standard/tests/file/bug52820.phpt ext/standard/te s

[email protected] (Joe Watkins)
Newsgroups php.cvs
Message-ID <[email protected]>
Commit:    b3ca0c25003e5e9279159aa2e6460b25908332c4
Author:    Nikita Popov <[email protected]>         Sat, 11 Mar 2017 00:33:32 +0100
Committer: Joe Watkins <[email protected]>      Sun, 12 Mar 2017 18:58:50 +0000
Parents:   ea91c082328c6f93f20a078896c16c2051061f47
Branches:  master

Link:       http://git.php.net/?p=php-src.git;a=commitdiff;h=b3ca0c25003e5e9279159aa2e6460b25908332c4

Log:
zend-test extension to house code that is required for testing internal APIs, but that we would not want to expose for regular builds

Changed paths:
  M  Zend/tests/return_types/internal_functions001.phpt
  M  Zend/tests/return_types/internal_functions002.phpt
  M  Zend/zend_builtin_functions.c
  M  appveyor/build_task.bat
  M  ext/sockets/tests/socket_export_stream-5.phpt
  M  ext/sockets/tests/socket_import_stream-5.phpt
  M  ext/standard/tests/file/bug52820.phpt
  M  ext/standard/tests/file/stream_enclosed.phpt
  A  ext/standard/tests/url/parse_url_unterminated.phpt
  A  ext/zend_test/config.m4
  A  ext/zend_test/config.w32
  A  ext/zend_test/php_test.h
  A  ext/zend_test/test.c
  M  travis/compile.sh
diff_b3ca0c25003e5e9279159aa2e6460b25908332c4.txt (text/plain, 41.1 KB)
diff --git a/Zend/tests/return_types/internal_functions001.phpt b/Zend/tests/return_types/internal_functions001.phpt
index fffaf54..5dfd278 100644
--- a/Zend/tests/return_types/internal_functions001.phpt
+++ b/Zend/tests/return_types/internal_functions001.phpt
@@ -1,15 +1,14 @@
 --TEST--
 Return type for internal functions
-
 --SKIPIF--
 <?php
-if (!function_exists('zend_test_func')) {
-    print 'skip';
-}
-
+if (!function_exists('zend_test_array_return')) die('skip');
+// Internal function return types are only checked in debug builds
+if (!PHP_DEBUG) die('skip requires debug build');
+?>
 --FILE--
 <?php
-zend_test_func();
+zend_test_array_return();
 ?>
 --EXPECTF--
-Fatal error: Return value of zend_test_func() must be of the type array, null returned in %s on line %d
+Fatal error: Return value of zend_test_array_return() must be of the type array, null returned in %s on line %d
diff --git a/Zend/tests/return_types/internal_functions002.phpt b/Zend/tests/return_types/internal_functions002.phpt
index 7cdb2b8..0daf348 100644
--- a/Zend/tests/return_types/internal_functions002.phpt
+++ b/Zend/tests/return_types/internal_functions002.phpt
@@ -1,16 +1,12 @@
 --TEST--
 Return type for internal functions 2
-
 --SKIPIF--
 <?php
-if (!function_exists('zend_test_func2')) {
-    print 'skip';
-}
-
+if (!function_exists('zend_test_nullable_array_return')) die('skip');
 --FILE--
 <?php
-zend_test_func2();
-echo "==DONE==\n"
+zend_test_nullable_array_return();
 ?>
---EXPECTF--
+==DONE==
+--EXPECT--
 ==DONE==
diff --git a/Zend/zend_builtin_functions.c b/Zend/zend_builtin_functions.c
index 55d1209..7f6e46b 100644
--- a/Zend/zend_builtin_functions.c
+++ b/Zend/zend_builtin_functions.c
@@ -29,15 +29,6 @@
 #include "zend_closures.h"
 #include "zend_generators.h"
 
-#undef ZEND_TEST_EXCEPTIONS
-
-#if ZEND_DEBUG
-static zend_class_entry *zend_test_interface;
-static zend_class_entry *zend_test_class;
-static zend_class_entry *zend_test_trait;
-static zend_object_handlers zend_test_class_handlers;
-#endif
-
 static ZEND_FUNCTION(zend_version);
 static ZEND_FUNCTION(func_num_args);
 static ZEND_FUNCTION(func_get_arg);
@@ -61,13 +52,6 @@ static ZEND_FUNCTION(interface_exists);
 static ZEND_FUNCTION(trait_exists);
 static ZEND_FUNCTION(function_exists);
 static ZEND_FUNCTION(class_alias);
-#if ZEND_DEBUG
-static ZEND_FUNCTION(leak);
-static ZEND_FUNCTION(leak_variable);
-#ifdef ZEND_TEST_EXCEPTIONS
-static ZEND_FUNCTION(crash);
-#endif
-#endif
 static ZEND_FUNCTION(get_included_files);
 static ZEND_FUNCTION(is_subclass_of);
 static ZEND_FUNCTION(is_a);
@@ -93,13 +77,9 @@ static ZEND_FUNCTION(get_extension_funcs);
 static ZEND_FUNCTION(get_defined_constants);
 static ZEND_FUNCTION(debug_backtrace);
 static ZEND_FUNCTION(debug_print_backtrace);
-#if ZEND_DEBUG
-static ZEND_FUNCTION(zend_test_func);
-static ZEND_FUNCTION(zend_test_func2);
-#ifdef ZTS
+#if ZEND_DEBUG && defined(ZTS)
 static ZEND_FUNCTION(zend_thread_id);
 #endif
-#endif
 static ZEND_FUNCTION(gc_mem_caches);
 static ZEND_FUNCTION(gc_collect_cycles);
 static ZEND_FUNCTION(gc_enabled);
@@ -199,13 +179,6 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_alias, 0, 0, 2)
 	ZEND_ARG_INFO(0, autoload)
 ZEND_END_ARG_INFO()
 
-#if ZEND_DEBUG
-ZEND_BEGIN_ARG_INFO_EX(arginfo_leak_variable, 0, 0, 1)
-	ZEND_ARG_INFO(0, variable)
-	ZEND_ARG_INFO(0, leak_data)
-ZEND_END_ARG_INFO()
-#endif
-
 ZEND_BEGIN_ARG_INFO_EX(arginfo_trigger_error, 0, 0, 1)
 	ZEND_ARG_INFO(0, message)
 	ZEND_ARG_INFO(0, error_type)
@@ -255,70 +228,8 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_extension_loaded, 0, 0, 1)
 	ZEND_ARG_INFO(0, extension_name)
 ZEND_END_ARG_INFO()
 
-#if ZEND_DEBUG
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO(arginfo_zend_test_func, IS_ARRAY, 0)
-ZEND_END_ARG_INFO()
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO(arginfo_zend_test_func2, IS_ARRAY, 1)
-ZEND_END_ARG_INFO()
-#endif
-
-/* }}} */
-
-#if ZEND_DEBUG
-static zend_object *zend_test_class_new(zend_class_entry *class_type) /* {{{ */ {
-	zend_object *obj = zend_objects_new(class_type);
-	obj->handlers = &zend_test_class_handlers;
-	return obj;
-}
-/* }}} */
-
-static zend_function *zend_test_class_method_get(zend_object **object, zend_string *name, const zval *key) /* {{{ */ {
-	zend_internal_function *fptr = emalloc(sizeof(zend_internal_function));
-	fptr->type = ZEND_OVERLOADED_FUNCTION_TEMPORARY;
-	fptr->num_args = 1;
-	fptr->arg_info = NULL;
-	fptr->scope = (*object)->ce;
-	fptr->fn_flags = ZEND_ACC_CALL_VIA_HANDLER;
-	fptr->function_name = zend_string_copy(name);
-	fptr->handler = ZEND_FN(zend_test_func);
-	zend_set_function_arg_flags((zend_function*)fptr);
-
-	return (zend_function*)fptr;
-}
-/* }}} */
-
-static zend_function *zend_test_class_static_method_get(zend_class_entry *ce, zend_string *name) /* {{{ */ {
-	zend_internal_function *fptr = emalloc(sizeof(zend_internal_function));
-	fptr->type = ZEND_OVERLOADED_FUNCTION;
-	fptr->num_args = 1;
-	fptr->arg_info = NULL;
-	fptr->scope = ce;
-	fptr->fn_flags = ZEND_ACC_CALL_VIA_HANDLER|ZEND_ACC_STATIC;
-	fptr->function_name = name;
-	fptr->handler = ZEND_FN(zend_test_func);
-	zend_set_function_arg_flags((zend_function*)fptr);
-
-	return (zend_function*)fptr;
-}
-/* }}} */
-
-static int zend_test_class_call_method(zend_string *method, zend_object *object, INTERNAL_FUNCTION_PARAMETERS) /* {{{ */ {
-	RETVAL_STR(zend_string_copy(method));
-	return 0;
-}
-/* }}} */
-
-static ZEND_METHOD(_ZendTestTrait, testMethod) /* {{{ */ {
-	RETURN_TRUE;
-}
 /* }}} */
 
-static zend_function_entry zend_test_trait_methods[] = {
-    ZEND_ME(_ZendTestTrait, testMethod, arginfo_zend__void, ZEND_ACC_PUBLIC)
-    ZEND_FE_END
-};
-#endif
-
 static const zend_function_entry builtin_functions[] = { /* {{{ */
 	ZEND_FE(zend_version,		arginfo_zend__void)
 	ZEND_FE(func_num_args,		arginfo_zend__void)
@@ -343,13 +254,6 @@ static const zend_function_entry builtin_functions[] = { /* {{{ */
 	ZEND_FE(trait_exists,		arginfo_trait_exists)
 	ZEND_FE(function_exists,	arginfo_function_exists)
 	ZEND_FE(class_alias,		arginfo_class_alias)
-#if ZEND_DEBUG
-	ZEND_FE(leak,				NULL)
-	ZEND_FE(leak_variable,		arginfo_leak_variable)
-#ifdef ZEND_TEST_EXCEPTIONS
-	ZEND_FE(crash,				NULL)
-#endif
-#endif
 	ZEND_FE(get_included_files,	arginfo_zend__void)
 	ZEND_FALIAS(get_required_files,	get_included_files,		arginfo_zend__void)
 	ZEND_FE(is_subclass_of,		arginfo_is_subclass_of)
@@ -377,13 +281,9 @@ static const zend_function_entry builtin_functions[] = { /* {{{ */
 	ZEND_FE(get_defined_constants,		arginfo_get_defined_constants)
 	ZEND_FE(debug_backtrace, 		arginfo_debug_backtrace)
 	ZEND_FE(debug_print_backtrace, 		arginfo_debug_print_backtrace)
-#if ZEND_DEBUG
-	ZEND_FE(zend_test_func,		arginfo_zend_test_func)
-	ZEND_FE(zend_test_func2,	arginfo_zend_test_func2)
-#ifdef ZTS
+#if ZEND_DEBUG && defined(ZTS)
 	ZEND_FE(zend_thread_id,		NULL)
 #endif
-#endif
 	ZEND_FE(gc_mem_caches,      arginfo_zend__void)
 	ZEND_FE(gc_collect_cycles, 	arginfo_zend__void)
 	ZEND_FE(gc_enabled, 		arginfo_zend__void)
@@ -401,26 +301,6 @@ ZEND_MINIT_FUNCTION(core) { /* {{{ */
 
 	zend_register_default_classes();
 
-#if ZEND_DEBUG
-	INIT_CLASS_ENTRY(class_entry, "_ZendTestInterface", NULL);
-	zend_test_interface = zend_register_internal_interface(&class_entry);
-	zend_declare_class_constant_long(zend_test_interface, ZEND_STRL("DUMMY"), 0);
-	INIT_CLASS_ENTRY(class_entry, "_ZendTestClass", NULL);
-	zend_test_class = zend_register_internal_class_ex(&class_entry, NULL);
-	zend_class_implements(zend_test_class, 1, zend_test_interface);
-	zend_test_class->create_object = zend_test_class_new;
-	zend_test_class->get_static_method = zend_test_class_static_method_get;
-
-	memcpy(&zend_test_class_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
-	zend_test_class_handlers.get_method = zend_test_class_method_get;
-	zend_test_class_handlers.call_method = zend_test_class_call_method;
-
-	INIT_CLASS_ENTRY(class_entry, "_ZendTestTrait", zend_test_trait_methods);
-	zend_test_trait = zend_register_internal_class(&class_entry);
-	zend_test_trait->ce_flags |= ZEND_ACC_TRAIT;
-	zend_declare_property_null(zend_test_trait, "testProp", sizeof("testProp")-1, ZEND_ACC_PUBLIC);
-#endif
-
 	return SUCCESS;
 }
 /* }}} */
@@ -1653,63 +1533,6 @@ ZEND_FUNCTION(class_alias)
 }
 /* }}} */
 
-#if ZEND_DEBUG
-/* {{{ proto void leak([int num_bytes])
-   Cause an intentional memory leak, for testing/debugging purposes */
-ZEND_FUNCTION(leak)
-{
-	zend_long leakbytes = 3;
-
-	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|l", &leakbytes) == FAILURE) {
-		return;
-	}
-
-	emalloc(leakbytes);
-}
-/* }}} */
-
-/* {{{ proto void leak_variable(mixed variable [, bool leak_data])
-   Leak a variable that is a resource or an object */
-ZEND_FUNCTION(leak_variable)
-{
-	zval *zv;
-	zend_bool leak_data = 0;
-
-	if (zend_parse_parameters(ZEND_NUM_ARGS(), "z|b", &zv, &leak_data) == FAILURE) {
-		return;
-	}
-
-	if (!leak_data) {
-		Z_ADDREF_P(zv);
-	} else if (Z_TYPE_P(zv) == IS_RESOURCE) {
-		Z_ADDREF_P(zv);
-	} else if (Z_TYPE_P(zv) == IS_OBJECT) {
-		Z_ADDREF_P(zv);
-	} else {
-		zend_error(E_WARNING, "Leaking non-zval data is only applicable to resources and objects");
-	}
-}
-/* }}} */
-
-
-#ifdef ZEND_TEST_EXCEPTIONS
-/* {{{ proto void crash(void)
-   Crash the script */
-ZEND_FUNCTION(crash)
-{
-	char *nowhere = NULL;
-
-	if (zend_parse_parameters_none() == FAILURE) {
-		return;
-	}
-
-	memcpy(nowhere, "something", sizeof("something"));
-}
-/* }}} */
-#endif
-
-#endif /* ZEND_DEBUG */
-
 /* {{{ proto array get_included_files(void)
    Returns an array with the file names that were include_once()'d */
 ZEND_FUNCTION(get_included_files)
@@ -2095,28 +1918,12 @@ ZEND_FUNCTION(create_function)
 }
 /* }}} */
 
-#if ZEND_DEBUG
-ZEND_FUNCTION(zend_test_func)
-{
-	zval *arg1, *arg2;
-
-	zend_parse_parameters(ZEND_NUM_ARGS(), "|zz", &arg1, &arg2);
-}
-
-ZEND_FUNCTION(zend_test_func2)
-{
-	zval *arg1, *arg2;
-
-	zend_parse_parameters(ZEND_NUM_ARGS(), "|zz", &arg1, &arg2);
-}
-
-#ifdef ZTS
+#if ZEND_DEBUG && defined(ZTS)
 ZEND_FUNCTION(zend_thread_id)
 {
 	RETURN_LONG((zend_long)tsrm_thread_id());
 }
 #endif
-#endif
 
 /* {{{ proto string get_resource_type(resource res)
    Get the resource type name for a given resource */
diff --git a/appveyor/build_task.bat b/appveyor/build_task.bat
index ad47854..85a64e4 100644
--- a/appveyor/build_task.bat
+++ b/appveyor/build_task.bat
@@ -39,6 +39,7 @@ call configure.bat ^
 	--enable-object-out-dir=%PHP_BUILD_OBJ_DIR% ^
 	--with-php-build=%DEPS_DIR% ^
 	%ADD_CONF% ^
+	--enable-zend-test ^
 	--with-test-ini-ext-exclude=%EXT_EXCLUDE_FROM_TEST%
 if %errorlevel% neq 0 exit /b 3
 
diff --git a/ext/sockets/tests/socket_export_stream-5.phpt b/ext/sockets/tests/socket_export_stream-5.phpt
index 732b207..db2d018 100644
--- a/ext/sockets/tests/socket_export_stream-5.phpt
+++ b/ext/sockets/tests/socket_export_stream-5.phpt
@@ -5,7 +5,7 @@ socket_export_stream: effects of leaked handles
 if (!extension_loaded('sockets')) {
 	die('SKIP sockets extension not available.');
 }
-if (!function_exists('leak_variable'))
+if (!function_exists('zend_leak_variable'))
 	die('SKIP only for debug builds');
 --FILE--
 <?php
@@ -13,12 +13,12 @@ if (!function_exists('leak_variable'))
 $sock0 = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
 socket_bind($sock0, '0.0.0.0', 58380);
 $stream0 = socket_export_stream($sock0);
-leak_variable($stream0, true);
+zend_leak_variable($stream0);
 
 $sock1 = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
 socket_bind($sock1, '0.0.0.0', 58381);
 $stream1 = socket_export_stream($sock1);
-leak_variable($sock1, true);
+zend_leak_variable($sock1);
 
 echo "Done.\n";
 --EXPECT--
diff --git a/ext/sockets/tests/socket_import_stream-5.phpt b/ext/sockets/tests/socket_import_stream-5.phpt
index d48531e..0583f2e 100644
--- a/ext/sockets/tests/socket_import_stream-5.phpt
+++ b/ext/sockets/tests/socket_import_stream-5.phpt
@@ -5,18 +5,18 @@ socket_import_stream: effects of leaked handles
 if (!extension_loaded('sockets')) {
 	die('SKIP sockets extension not available.');
 }
-if (!function_exists('leak_variable'))
+if (!function_exists('zend_leak_variable'))
 	die('SKIP only for debug builds');
 --FILE--
 <?php
 
 $stream0 = stream_socket_server("udp://0.0.0.0:58380", $errno, $errstr, STREAM_SERVER_BIND);
 $sock0 = socket_import_stream($stream0);
-leak_variable($stream0, true);
+zend_leak_variable($stream0);
 
 $stream1 = stream_socket_server("udp://0.0.0.0:58381", $errno, $errstr, STREAM_SERVER_BIND);
 $sock1 = socket_import_stream($stream1);
-leak_variable($sock1, true);
+zend_leak_variable($sock1);
 
 echo "Done.\n";
 --EXPECT--
diff --git a/ext/standard/tests/file/bug52820.phpt b/ext/standard/tests/file/bug52820.phpt
index a00ebf5..e51c27e 100644
--- a/ext/standard/tests/file/bug52820.phpt
+++ b/ext/standard/tests/file/bug52820.phpt
@@ -2,7 +2,7 @@
 Bug #52820 (writes to fopencookie FILE* not committed when seeking the stream)
 --SKIPIF--
 <?php
-if (!function_exists('leak_variable'))
+if (!function_exists('zend_leak_variable'))
    die("skip only for debug builds");
 /* unfortunately no standard function does a cast to FILE*, so we need
  * curl to test this */
@@ -33,10 +33,10 @@ echo "\nmemory stream (close after):\n";
 fclose(do_stuff("php://memory"));
 
 echo "\ntemp stream (leak):\n";
-leak_variable(do_stuff("php://temp"), true);
+zend_leak_variable(do_stuff("php://temp"));
 
 echo "\nmemory stream (leak):\n";
-leak_variable(do_stuff("php://memory"), true);
+zend_leak_variable(do_stuff("php://memory"));
 
 echo "\nDone.\n";
 --EXPECTF--
diff --git a/ext/standard/tests/file/stream_enclosed.phpt b/ext/standard/tests/file/stream_enclosed.phpt
index f487ed0..ff764c8 100644
--- a/ext/standard/tests/file/stream_enclosed.phpt
+++ b/ext/standard/tests/file/stream_enclosed.phpt
@@ -2,7 +2,7 @@
 Unexposed/leaked stream encloses another stream
 --SKIPIF--
 <?php
-if (!function_exists('leak_variable')) die("skip only debug builds");
+if (!function_exists('zend_leak_variable')) die("skip only debug builds");
 --FILE--
 <?php
 $s = fopen('php://temp/maxmemory=1024','wb+');
@@ -15,6 +15,6 @@ while ($i++ < 5000) {
     fwrite($t, str_repeat('a',1024));
 }
 
-leak_variable($s, true);
-leak_variable($t, true);
+zend_leak_variable($s);
+zend_leak_variable($t);
 --EXPECT--
diff --git a/ext/standard/tests/url/parse_url_unterminated.phpt b/ext/standard/tests/url/parse_url_unterminated.phpt
new file mode 100644
index 0000000..bb0e096
--- /dev/null
+++ b/ext/standard/tests/url/parse_url_unterminated.phpt
@@ -0,0 +1,887 @@
+--TEST--
+Test parse_url() function: Parse unterminated string
+--SKIPIF--
+<?php
+if (!function_exists('zend_create_unterminated_string')) die('skip ext/test required');
+?>
+--FILE--
+<?php
+
+/*
+ * This is the same as the basic001 test, but with unterminated strings.
+ */
+
+include_once(dirname(__FILE__) . '/urls.inc');
+
+foreach ($urls as $url) {
+	echo "\n--> $url: ";
+	$str = zend_create_unterminated_string($url);
+	var_dump(parse_url($str));
+    zend_terminate_string($str);
+}
+
+echo "Done";
+?>
+--EXPECTF--
+
+--> 64.246.30.37: array(1) {
+  ["path"]=>
+  string(12) "64.246.30.37"
+}
+
+--> http://64.246.30.37: array(2) {
+  ["scheme"]=>
+  string(4) "http"
+  ["host"]=>
+  string(12) "64.246.30.37"
+}
+
+--> http://64.246.30.37/: array(3) {
+  ["scheme"]=>
+  string(4) "http"
+  ["host"]=>
+  string(12) "64.246.30.37"
+  ["path"]=>
+  string(1) "/"
+}
+
+--> 64.246.30.37/: array(1) {
+  ["path"]=>
+  string(13) "64.246.30.37/"
+}
+
+--> 64.246.30.37:80/: array(3) {
+  ["host"]=>
+  string(12) "64.246.30.37"
+  ["port"]=>
+  int(80)
+  ["path"]=>
+  string(1) "/"
+}
+
+--> php.net: array(1) {
+  ["path"]=>
+  string(7) "php.net"
+}
+
+--> php.net/: array(1) {
+  ["path"]=>
+  string(8) "php.net/"
+}
+
+--> http://php.net: array(2) {
+  ["scheme"]=>
+  string(4) "http"
+  ["host"]=>
+  string(7) "php.net"
+}
+
+--> http://php.net/: array(3) {
+  ["scheme"]=>
+  string(4) "http"
+  ["host"]=>
+  string(7) "php.net"
+  ["path"]=>
+  string(1) "/"
+}
+
+--> www.php.net: array(1) {
+  ["path"]=>
+  string(11) "www.php.net"
+}
+
+--> www.php.net/: array(1) {
+  ["path"]=>
+  string(12) "www.php.net/"
+}
+
+--> http://www.php.net: array(2) {
+  ["scheme"]=>
+  string(4) "http"
+  ["host"]=>
+  string(11) "www.php.net"
+}
+
+--> http://www.php.net/: array(3) {
+  ["scheme"]=>
+  string(4) "http"
+  ["host"]=>
+  string(11) "www.php.net"
+  ["path"]=>
+  string(1) "/"
+}
+
+--> www.php.net:80: array(2) {
+  ["host"]=>
+  string(11) "www.php.net"
+  ["port"]=>
+  int(80)
+}
+
+--> http://www.php.net:80: array(3) {
+  ["scheme"]=>
+  string(4) "http"
+  ["host"]=>
+  string(11) "www.php.net"
+  ["port"]=>
+  int(80)
+}
+
+--> http://www.php.net:80/: array(4) {
+  ["scheme"]=>
+  string(4) "http"
+  ["host"]=>
+  string(11) "www.php.net"
+  ["port"]=>
+  int(80)
+  ["path"]=>
+  string(1) "/"
+}
+
+--> http://www.php.net/index.php: array(3) {
+  ["scheme"]=>
+  string(4) "http"
+  ["host"]=>
+  string(11) "www.php.net"
+  ["path"]=>
+  string(10) "/index.php"
+}
+
+--> www.php.net/?: array(1) {
+  ["path"]=>
+  string(12) "www.php.net/"
+}
+
+--> www.php.net:80/?: array(3) {
+  ["host"]=>
+  string(11) "www.php.net"
+  ["port"]=>
+  int(80)
+  ["path"]=>
+  string(1) "/"
+}
+
+--> http://www.php.net/?: array(3) {
+  ["scheme"]=>
+  string(4) "http"
+  ["host"]=>
+  string(11) "www.php.net"
+  ["path"]=>
+  string(1) "/"
+}
+
+--> http://www.php.net:80/?: array(4) {
+  ["scheme"]=>
+  string(4) "http"
+  ["host"]=>
+  string(11) "www.php.net"
+  ["port"]=>
+  int(80)
+  ["path"]=>
+  string(1) "/"
+}
+
+--> http://www.php.net:80/index.php: array(4) {
+  ["scheme"]=>
+  string(4) "http"
+  ["host"]=>
+  string(11) "www.php.net"
+  ["port"]=>
+  int(80)
+  ["path"]=>
+  string(10) "/index.php"
+}
+
+--> http://www.php.net:80/foo/bar/index.php: array(4) {
+  ["scheme"]=>
+  string(4) "http"
+  ["host"]=>
+  string(11) "www.php.net"
+  ["port"]=>
+  int(80)
+  ["path"]=>
+  string(18) "/foo/bar/index.php"
+}
+
+--> http://www.php.net:80/this/is/a/very/deep/directory/structure/and/file.php: array(4) {
+  ["scheme"]=>
+  string(4) "http"
+  ["host"]=>
+  string(11) "www.php.net"
+  ["port"]=>
+  int(80)
+  ["path"]=>
+  string(53) "/this/is/a/very/deep/directory/structure/and/file.php"
+}
+
+--> http://www.php.net:80/this/is/a/very/deep/directory/structure/and/file.php?lots=1&of=2&parameters=3&too=4&here=5: array(5) {
+  ["scheme"]=>
+  string(4) "http"
+  ["host"]=>
+  string(11) "www.php.net"
+  ["port"]=>
+  int(80)
+  ["path"]=>
+  string(53) "/this/is/a/very/deep/directory/structure/and/file.php"
+  ["query"]=>
+  string(37) "lots=1&of=2&parameters=3&too=4&here=5"
+}
+
+--> http://www.php.net:80/this/is/a/very/deep/directory/structure/and/: array(4) {
+  ["scheme"]=>
+  string(4) "http"
+  ["host"]=>
+  string(11) "www.php.net"
+  ["port"]=>
+  int(80)
+  ["path"]=>
+  string(45) "/this/is/a/very/deep/directory/structure/and/"
+}
+
+--> http://www.php.net:80/this/is/a/very/deep/directory/structure/and/file.php: array(4) {
+  ["scheme"]=>
+  string(4) "http"
+  ["host"]=>
+  string(11) "www.php.net"
+  ["port"]=>
+  int(80)
+  ["path"]=>
+  string(53) "/this/is/a/very/deep/directory/structure/and/file.php"
+}
+
+--> http://www.php.net:80/this/../a/../deep/directory: array(4) {
+  ["scheme"]=>
+  string(4) "http"
+  ["host"]=>
+  string(11) "www.php.net"
+  ["port"]=>
+  int(80)
+  ["path"]=>
+  string(28) "/this/../a/../deep/directory"
+}
+
+--> http://www.php.net:80/this/../a/../deep/directory/: array(4) {
+  ["scheme"]=>
+  string(4) "http"
+  ["host"]=>
+  string(11) "www.php.net"
+  ["port"]=>
+  int(80)
+  ["path"]=>
+  string(29) "/this/../a/../deep/directory/"
+}
+
+--> http://www.php.net:80/this/is/a/very/deep/directory/../file.php: array(4) {
+  ["scheme"]=>
+  string(4) "http"
+  ["host"]=>
+  string(11) "www.php.net"
+  ["port"]=>
+  int(80)
+  ["path"]=>
+  string(42) "/this/is/a/very/deep/directory/../file.php"
+}
+
+--> http://www.php.net:80/index.php: array(4) {
+  ["scheme"]=>
+  string(4) "http"
+  ["host"]=>
+  string(11) "www.php.net"
+  ["port"]=>
+  int(80)
+  ["path"]=>
+  string(10) "/index.php"
+}
+
+--> http://www.php.net:80/index.php?: array(4) {
+  ["scheme"]=>
+  string(4) "http"
+  ["host"]=>
+  string(11) "www.php.net"
+  ["port"]=>
+  int(80)
+  ["path"]=>
+  string(10) "/index.php"
+}
+
+--> http://www.php.net:80/#foo: array(5) {
+  ["scheme"]=>
+  string(4) "http"
+  ["host"]=>
+  string(11) "www.php.net"
+  ["port"]=>
+  int(80)
+  ["path"]=>
+  string(1) "/"
+  ["fragment"]=>
+  string(3) "foo"
+}
+
+--> http://www.php.net:80/?#: array(4) {
+  ["scheme"]=>
+  string(4) "http"
+  ["host"]=>
+  string(11) "www.php.net"
+  ["port"]=>
+  int(80)
+  ["path"]=>
+  string(1) "/"
+}
+
+--> http://www.php.net:80/?test=1: array(5) {
+  ["scheme"]=>
+  string(4) "http"
+  ["host"]=>
+  string(11) "www.php.net"
+  ["port"]=>
+  int(80)
+  ["path"]=>
+  string(1) "/"
+  ["query"]=>
+  string(6) "test=1"
+}
+
+--> http://www.php.net/?test=1&: array(4) {
+  ["scheme"]=>
+  string(4) "http"
+  ["host"]=>
+  string(11) "www.php.net"
+  ["path"]=>
+  string(1) "/"
+  ["query"]=>
+  string(7) "test=1&"
+}
+
+--> http://www.php.net:80/?&: array(5) {
+  ["scheme"]=>
+  string(4) "http"
+  ["host"]=>
+  string(11) "www.php.net"
+  ["port"]=>
+  int(80)
+  ["path"]=>
+  string(1) "/"
+  ["query"]=>
+  string(1) "&"
+}
+
+--> http://www.php.net:80/index.php?test=1&: array(5) {
+  ["scheme"]=>
+  string(4) "http"
+  ["host"]=>
+  string(11) "www.php.net"
+  ["port"]=>
+  int(80)
+  ["path"]=>
+  string(10) "/index.php"
+  ["query"]=>
+  string(7) "test=1&"
+}
+
+--> http://www.php.net/index.php?&: array(4) {
+  ["scheme"]=>
+  string(4) "http"
+  ["host"]=>
+  string(11) "www.php.net"
+  ["path"]=>
+  string(10) "/index.php"
+  ["query"]=>
+  string(1) "&"
+}
+
+--> http://www.php.net:80/index.php?foo&: array(5) {
+  ["scheme"]=>
+  string(4) "http"
+  ["host"]=>
+  string(11) "www.php.net"
+  ["port"]=>
+  int(80)
+  ["path"]=>
+  string(10) "/index.php"
+  ["query"]=>
+  string(4) "foo&"
+}
+
+--> http://www.php.net/index.php?&foo: array(4) {
+  ["scheme"]=>
+  string(4) "http"
+  ["host"]=>
+  string(11) "www.php.net"
+  ["path"]=>
+  string(10) "/index.php"
+  ["query"]=>
+  string(4) "&foo"
+}
+
+--> http://www.php.net:80/index.php?test=1&test2=char&test3=mixesCI: array(5) {
+  ["scheme"]=>
+  string(4) "http"
+  ["host"]=>
+  string(11) "www.php.net"
+  ["port"]=>
+  int(80)
+  ["path"]=>
+  string(10) "/index.php"
+  ["query"]=>
+  string(31) "test=1&test2=char&test3=mixesCI"
+}
+
+--> www.php.net:80/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123: array(5) {
+  ["host"]=>
+  string(11) "www.php.net"
+  ["port"]=>
+  int(80)
+  ["path"]=>
+  string(10) "/index.php"
+  ["query"]=>
+  string(31) "test=1&test2=char&test3=mixesCI"
+  ["fragment"]=>
+  string(16) "some_page_ref123"
+}
+
+--> http://[email protected]:80/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123: array(7) {
+  ["scheme"]=>
+  string(4) "http"
+  ["host"]=>
+  string(11) "www.php.net"
+  ["port"]=>
+  int(80)
+  ["user"]=>
+  string(6) "secret"
+  ["path"]=>
+  string(10) "/index.php"
+  ["query"]=>
+  string(31) "test=1&test2=char&test3=mixesCI"
+  ["fragment"]=>
+  string(16) "some_page_ref123"
+}
+
+--> http://secret:@www.php.net/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123: array(7) {
+  ["scheme"]=>
+  string(4) "http"
+  ["host"]=>
+  string(11) "www.php.net"
+  ["user"]=>
+  string(6) "secret"
+  ["pass"]=>
+  string(0) ""
+  ["path"]=>
+  string(10) "/index.php"
+  ["query"]=>
+  string(31) "test=1&test2=char&test3=mixesCI"
+  ["fragment"]=>
+  string(16) "some_page_ref123"
+}
+
+--> http://:[email protected]:80/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123: array(8) {
+  ["scheme"]=>
+  string(4) "http"
+  ["host"]=>
+  string(11) "www.php.net"
+  ["port"]=>
+  int(80)
+  ["user"]=>
+  string(0) ""
+  ["pass"]=>
+  string(7) "hideout"
+  ["path"]=>
+  string(10) "/index.php"
+  ["query"]=>
+  string(31) "test=1&test2=char&test3=mixesCI"
+  ["fragment"]=>
+  string(16) "some_page_ref123"
+}
+
+--> http://secret:[email protected]/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123: array(7) {
+  ["scheme"]=>
+  string(4) "http"
+  ["host"]=>
+  string(11) "www.php.net"
+  ["user"]=>
+  string(6) "secret"
+  ["pass"]=>
+  string(7) "hideout"
+  ["path"]=>
+  string(10) "/index.php"
+  ["query"]=>
+  string(31) "test=1&test2=char&test3=mixesCI"
+  ["fragment"]=>
+  string(16) "some_page_ref123"
+}
+
+--> http://secret@[email protected]:80/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123: array(7) {
+  ["scheme"]=>
+  string(4) "http"
+  ["host"]=>
+  string(11) "www.php.net"
+  ["port"]=>
+  int(80)
+  ["user"]=>
+  string(14) "secret@hideout"
+  ["path"]=>
+  string(10) "/index.php"
+  ["query"]=>
+  string(31) "test=1&test2=char&test3=mixesCI"
+  ["fragment"]=>
+  string(16) "some_page_ref123"
+}
+
+--> http://secret:hid:[email protected]:80/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123: array(8) {
+  ["scheme"]=>
+  string(4) "http"
+  ["host"]=>
+  string(11) "www.php.net"
+  ["port"]=>
+  int(80)
+  ["user"]=>
+  string(6) "secret"
+  ["pass"]=>
+  string(7) "hid:out"
+  ["path"]=>
+  string(10) "/index.php"
+  ["query"]=>
+  string(31) "test=1&test2=char&test3=mixesCI"
+  ["fragment"]=>
+  string(16) "some_page_ref123"
+}
+
+--> nntp://news.php.net: array(2) {
+  ["scheme"]=>
+  string(4) "nntp"
+  ["host"]=>
+  string(12) "news.php.net"
+}
+
+--> ftp://ftp.gnu.org/gnu/glic/glibc.tar.gz: array(3) {
+  ["scheme"]=>
+  string(3) "ftp"
+  ["host"]=>
+  string(11) "ftp.gnu.org"
+  ["path"]=>
+  string(22) "/gnu/glic/glibc.tar.gz"
+}
+
+--> zlib:http://foo@bar: array(2) {
+  ["scheme"]=>
+  string(4) "zlib"
+  ["path"]=>
+  string(14) "http://foo@bar"
+}
+
+--> zlib:filename.txt: array(2) {
+  ["scheme"]=>
+  string(4) "zlib"
+  ["path"]=>
+  string(12) "filename.txt"
+}
+
+--> zlib:/path/to/my/file/file.txt: array(2) {
+  ["scheme"]=>
+  string(4) "zlib"
+  ["path"]=>
+  string(25) "/path/to/my/file/file.txt"
+}
+
+--> foo://foo@bar: array(3) {
+  ["scheme"]=>
+  string(3) "foo"
+  ["host"]=>
+  string(3) "bar"
+  ["user"]=>
+  string(3) "foo"
+}
+
+--> mailto:[email protected]: array(2) {
+  ["scheme"]=>
+  string(6) "mailto"
+  ["path"]=>
+  string(15) "[email protected]"
+}
+
+--> /foo.php?a=b&c=d: array(2) {
+  ["path"]=>
+  string(8) "/foo.php"
+  ["query"]=>
+  string(7) "a=b&c=d"
+}
+
+--> foo.php?a=b&c=d: array(2) {
+  ["path"]=>
+  string(7) "foo.php"
+  ["query"]=>
+  string(7) "a=b&c=d"
+}
+
+--> http://user:[email protected]:8080?bar=1&boom=0: array(6) {
+  ["scheme"]=>
+  string(4) "http"
+  ["host"]=>
+  string(15) "www.example.com"
+  ["port"]=>
+  int(8080)
+  ["user"]=>
+  string(4) "user"
+  ["pass"]=>
+  string(6) "passwd"
+  ["query"]=>
+  string(12) "bar=1&boom=0"
+}
+
+--> http://user_me-you:[email protected]:8080?bar=1&boom=0: array(6) {
+  ["scheme"]=>
+  string(4) "http"
+  ["host"]=>
+  string(15) "www.example.com"
+  ["port"]=>
+  int(8080)
+  ["user"]=>
+  string(11) "user_me-you"
+  ["pass"]=>
+  string(11) "my_pas-word"
+  ["query"]=>
+  string(12) "bar=1&boom=0"
+}
+
+--> file:///path/to/file: array(2) {
+  ["scheme"]=>
+  string(4) "file"
+  ["path"]=>
+  string(13) "/path/to/file"
+}
+
+--> file://path/to/file: array(3) {
+  ["scheme"]=>
+  string(4) "file"
+  ["host"]=>
+  string(4) "path"
+  ["path"]=>
+  string(8) "/to/file"
+}
+
+--> file:/path/to/file: array(2) {
+  ["scheme"]=>
+  string(4) "file"
+  ["path"]=>
+  string(13) "/path/to/file"
+}
+
+--> http://1.2.3.4:/abc.asp?a=1&b=2: array(4) {
+  ["scheme"]=>
+  string(4) "http"
+  ["host"]=>
+  string(7) "1.2.3.4"
+  ["path"]=>
+  string(8) "/abc.asp"
+  ["query"]=>
+  string(7) "a=1&b=2"
+}
+
+--> http://foo.com#bar: array(3) {
+  ["scheme"]=>
+  string(4) "http"
+  ["host"]=>
+  string(7) "foo.com"
+  ["fragment"]=>
+  string(3) "bar"
+}
+
+--> scheme:: array(1) {
+  ["scheme"]=>
+  string(6) "scheme"
+}
+
+--> foo+bar://baz@bang/bla: array(4) {
+  ["scheme"]=>
+  string(7) "foo+bar"
+  ["host"]=>
+  string(4) "bang"
+  ["user"]=>
+  string(3) "baz"
+  ["path"]=>
+  string(4) "/bla"
+}
+
+--> gg:9130731: array(2) {
+  ["scheme"]=>
+  string(2) "gg"
+  ["path"]=>
+  string(7) "9130731"
+}
+
+--> http://user:@pass@host/path?argument?value#etc: array(7) {
+  ["scheme"]=>
+  string(4) "http"
+  ["host"]=>
+  string(4) "host"
+  ["user"]=>
+  string(4) "user"
+  ["pass"]=>
+  string(5) "@pass"
+  ["path"]=>
+  string(5) "/path"
+  ["query"]=>
+  string(14) "argument?value"
+  ["fragment"]=>
+  string(3) "etc"
+}
+
+--> http://10.10.10.10/:80: array(3) {
+  ["scheme"]=>
+  string(4) "http"
+  ["host"]=>
+  string(11) "10.10.10.10"
+  ["path"]=>
+  string(4) "/:80"
+}
+
+--> http://x:?: array(2) {
+  ["scheme"]=>
+  string(4) "http"
+  ["host"]=>
+  string(1) "x"
+}
+
+--> x:blah.com: array(2) {
+  ["scheme"]=>
+  string(1) "x"
+  ["path"]=>
+  string(8) "blah.com"
+}
+
+--> x:/blah.com: array(2) {
+  ["scheme"]=>
+  string(1) "x"
+  ["path"]=>
+  string(9) "/blah.com"
+}
+
+--> x://::abc/?: bool(false)
+
+--> http://::?: array(2) {
+  ["scheme"]=>
+  string(4) "http"
+  ["host"]=>
+  string(1) ":"
+}
+
+--> http://::#: array(2) {
+  ["scheme"]=>
+  string(4) "http"
+  ["host"]=>
+  string(1) ":"
+}
+
+--> x://::6.5: array(3) {
+  ["scheme"]=>
+  string(1) "x"
+  ["host"]=>
+  string(1) ":"
+  ["port"]=>
+  int(6)
+}
+
+--> http://?:/: bool(false)
+
+--> http://@?:/: bool(false)
+
+--> file:///:: array(2) {
+  ["scheme"]=>
+  string(4) "file"
+  ["path"]=>
+  string(2) "/:"
+}
+
+--> file:///a:/: array(2) {
+  ["scheme"]=>
+  string(4) "file"
+  ["path"]=>
+  string(3) "a:/"
+}
+
+--> file:///ab:/: array(2) {
+  ["scheme"]=>
+  string(4) "file"
+  ["path"]=>
+  string(5) "/ab:/"
+}
+
+--> file:///a:/: array(2) {
+  ["scheme"]=>
+  string(4) "file"
+  ["path"]=>
+  string(3) "a:/"
+}
+
+--> file:///@:/: array(2) {
+  ["scheme"]=>
+  string(4) "file"
+  ["path"]=>
+  string(3) "@:/"
+}
+
+--> file:///:80/: array(2) {
+  ["scheme"]=>
+  string(4) "file"
+  ["path"]=>
+  string(5) "/:80/"
+}
+
+--> []: array(1) {
+  ["path"]=>
+  string(2) "[]"
+}
+
+--> http://[x:80]/: array(3) {
+  ["scheme"]=>
+  string(4) "http"
+  ["host"]=>
+  string(6) "[x:80]"
+  ["path"]=>
+  string(1) "/"
+}
+
+--> : array(1) {
+  ["path"]=>
+  string(0) ""
+}
+
+--> /: array(1) {
+  ["path"]=>
+  string(1) "/"
+}
+
+--> /rest/Users?filter={"id":"123"}: array(2) {
+  ["path"]=>
+  string(11) "/rest/Users"
+  ["query"]=>
+  string(19) "filter={"id":"123"}"
+}
+
+--> http:///blah.com: bool(false)
+
+--> http://:80: bool(false)
+
+--> http://user@:80: bool(false)
+
+--> http://user:pass@:80: bool(false)
+
+--> http://:: bool(false)
+
+--> http://@/: bool(false)
+
+--> http://@:/: bool(false)
+
+--> http://:/: bool(false)
+
+--> http://?: bool(false)
+
+--> http://#: bool(false)
+
+--> http://?:: bool(false)
+
+--> http://:?: bool(false)
+
+--> http://blah.com:123456: bool(false)
+
+--> http://blah.com:abcdef: bool(false)
+Done
diff --git a/ext/zend_test/config.m4 b/ext/zend_test/config.m4
new file mode 100644
index 0000000..becefcd
--- /dev/null
+++ b/ext/zend_test/config.m4
@@ -0,0 +1,6 @@
+PHP_ARG_ENABLE(zend-test, whether to enable zend-test extension,
+[  --enable-zend-test           Enable zend-test extension])
+
+if test "$PHP_ZEND_TEST" != "no"; then
+  PHP_NEW_EXTENSION(zend_test, test.c, $ext_shared,, -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1)
+fi
diff --git a/ext/zend_test/config.w32 b/ext/zend_test/config.w32
new file mode 100644
index 0000000..9b1574e
--- /dev/null
+++ b/ext/zend_test/config.w32
@@ -0,0 +1,8 @@
+// vim:ft=javascript
+
+ARG_ENABLE("zend-test", "enable zend-test extension", "no");
+
+if (PHP_ZEND_TEST != "no") {
+	EXTENSION("zend_test", "test.c", PHP_ZEND_TEST_SHARED, "/DZEND_ENABLE_STATIC_TSRMLS_CACHE=1");
+}
+
diff --git a/ext/zend_test/php_test.h b/ext/zend_test/php_test.h
new file mode 100644
index 0000000..fe8c5e4
--- /dev/null
+++ b/ext/zend_test/php_test.h
@@ -0,0 +1,47 @@
+/*
+  +----------------------------------------------------------------------+
+  | PHP Version 7                                                        |
+  +----------------------------------------------------------------------+
+  | Copyright (c) 1997-2017 The PHP Group                                |
+  +----------------------------------------------------------------------+
+  | This source file is subject to version 3.01 of the PHP license,      |
+  | that is bundled with this package in the file LICENSE, and is        |
+  | available through the world-wide-web at the following url:           |
+  | http://www.php.net/license/3_01.txt                                  |
+  | If you did not receive a copy of the PHP license and are unable to   |
+  | obtain it through the world-wide-web, please send a note to          |
+  | [email protected] so we can mail you a copy immediately.               |
+  +----------------------------------------------------------------------+
+  | Author:                                                              |
+  +----------------------------------------------------------------------+
+*/
+
+/* $Id$ */
+
+#ifndef PHP_TEST_H
+#define PHP_TEST_H
+
+extern zend_module_entry zend_test_module_entry;
+#define phpext_zend_test_ptr &zend_test_module_entry
+
+#define PHP_ZEND_TEST_VERSION "0.1.0"
+
+#ifdef ZTS
+#include "TSRM.h"
+#endif
+
+#if defined(ZTS) && defined(COMPILE_DL_ZEND_TEST)
+ZEND_TSRMLS_CACHE_EXTERN()
+#endif
+
+#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/ext/zend_test/test.c b/ext/zend_test/test.c
new file mode 100644
index 0000000..f203af8
--- /dev/null
+++ b/ext/zend_test/test.c
@@ -0,0 +1,260 @@
+/*
+  +----------------------------------------------------------------------+
+  | PHP Version 7                                                        |
+  +----------------------------------------------------------------------+
+  | Copyright (c) 1997-2017 The PHP Group                                |
+  +----------------------------------------------------------------------+
+  | This source file is subject to version 3.01 of the PHP license,      |
+  | that is bundled with this package in the file LICENSE, and is        |
+  | available through the world-wide-web at the following url:           |
+  | http://www.php.net/license/3_01.txt                                  |
+  | If you did not receive a copy of the PHP license and are unable to   |
+  | obtain it through the world-wide-web, please send a note to          |
+  | [email protected] so we can mail you a copy immediately.               |
+  +----------------------------------------------------------------------+
+  | Author:                                                              |
+  +----------------------------------------------------------------------+
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "php.h"
+#include "php_ini.h"
+#include "ext/standard/info.h"
+#include "php_test.h"
+
+static zend_class_entry *zend_test_interface;
+static zend_class_entry *zend_test_class;
+static zend_class_entry *zend_test_trait;
+static zend_object_handlers zend_test_class_handlers;
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO(arginfo_zend_test_array_return, IS_ARRAY, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO(arginfo_zend_test_nullable_array_return, IS_ARRAY, 1)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_zend_terminate_string, 0, 0, 1)
+	ZEND_ARG_INFO(1, str)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_zend_leak_variable, 0, 0, 1)
+	ZEND_ARG_INFO(0, variable)
+ZEND_END_ARG_INFO()
+
+ZEND_FUNCTION(zend_test_func)
+{
+	/* dummy */
+}
+
+ZEND_FUNCTION(zend_test_array_return)
+{
+	zval *arg1, *arg2;
+
+	zend_parse_parameters(ZEND_NUM_ARGS(), "|zz", &arg1, &arg2);
+}
+
+ZEND_FUNCTION(zend_test_nullable_array_return)
+{
+	zval *arg1, *arg2;
+
+	zend_parse_parameters(ZEND_NUM_ARGS(), "|zz", &arg1, &arg2);
+}
+
+/* Create a string without terminating null byte. Must be termined with
+ * zend_terminate_string() before destruction, otherwise a warning is issued
+ * in debug builds. */
+ZEND_FUNCTION(zend_create_unterminated_string)
+{
+	zend_string *str, *res;
+
+	if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &str) == FAILURE) {
+		return;
+	}
+
+	res = zend_string_alloc(ZSTR_LEN(str), 0);
+	memcpy(ZSTR_VAL(res), ZSTR_VAL(str), ZSTR_LEN(str));
+	/* No trailing null byte */
+
+	RETURN_STR(res);
+}
+
+/* Enforce terminate null byte on string. This avoids a warning in debug builds. */
+ZEND_FUNCTION(zend_terminate_string)
+{
+	zend_string *str;
+
+	if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &str) == FAILURE) {
+		return;
+	}
+
+	ZSTR_VAL(str)[ZSTR_LEN(str)] = '\0';
+}
+
+/* {{{ proto void zend_leak_bytes([int num_bytes])
+   Cause an intentional memory leak, for testing/debugging purposes */
+ZEND_FUNCTION(zend_leak_bytes)
+{
+	zend_long leakbytes = 3;
+
+	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|l", &leakbytes) == FAILURE) {
+		return;
+	}
+
+	emalloc(leakbytes);
+}
+/* }}} */
+
+/* {{{ proto void zend_leak_variable(mixed variable)
+   Leak a refcounted variable */
+ZEND_FUNCTION(zend_leak_variable)
+{
+	zval *zv;
+
+	if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &zv) == FAILURE) {
+		return;
+	}
+
+	if (!Z_REFCOUNTED_P(zv)) {
+		zend_error(E_WARNING, "Cannot leak variable that is not refcounted");
+		return;
+	}
+
+	Z_ADDREF_P(zv);
+}
+/* }}} */
+
+static zend_object *zend_test_class_new(zend_class_entry *class_type) /* {{{ */ {
+	zend_object *obj = zend_objects_new(class_type);
+	obj->handlers = &zend_test_class_handlers;
+	return obj;
+}
+/* }}} */
+
+static zend_function *zend_test_class_method_get(zend_object **object, zend_string *name, const zval *key) /* {{{ */ {
+	zend_internal_function *fptr = emalloc(sizeof(zend_internal_function));
+	fptr->type = ZEND_OVERLOADED_FUNCTION_TEMPORARY;
+	fptr->num_args = 1;
+	fptr->arg_info = NULL;
+	fptr->scope = (*object)->ce;
+	fptr->fn_flags = ZEND_ACC_CALL_VIA_HANDLER;
+	fptr->function_name = zend_string_copy(name);
+	fptr->handler = ZEND_FN(zend_test_func);
+	zend_set_function_arg_flags((zend_function*)fptr);
+
+	return (zend_function*)fptr;
+}
+/* }}} */
+
+static zend_function *zend_test_class_static_method_get(zend_class_entry *ce, zend_string *name) /* {{{ */ {
+	zend_internal_function *fptr = emalloc(sizeof(zend_internal_function));
+	fptr->type = ZEND_OVERLOADED_FUNCTION;
+	fptr->num_args = 1;
+	fptr->arg_info = NULL;
+	fptr->scope = ce;
+	fptr->fn_flags = ZEND_ACC_CALL_VIA_HANDLER|ZEND_ACC_STATIC;
+	fptr->function_name = name;
+	fptr->handler = ZEND_FN(zend_test_func);
+	zend_set_function_arg_flags((zend_function*)fptr);
+
+	return (zend_function*)fptr;
+}
+/* }}} */
+
+static int zend_test_class_call_method(zend_string *method, zend_object *object, INTERNAL_FUNCTION_PARAMETERS) /* {{{ */ {
+	RETVAL_STR(zend_string_copy(method));
+	return 0;
+}
+/* }}} */
+
+static ZEND_METHOD(_ZendTestTrait, testMethod) /* {{{ */ {
+	RETURN_TRUE;
+}
+/* }}} */
+
+static zend_function_entry zend_test_trait_methods[] = {
+    ZEND_ME(_ZendTestTrait, testMethod, NULL, ZEND_ACC_PUBLIC)
+    ZEND_FE_END
+};
+
+PHP_MINIT_FUNCTION(zend_test)
+{
+	zend_class_entry class_entry;
+
+	INIT_CLASS_ENTRY(class_entry, "_ZendTestInterface", NULL);
+	zend_test_interface = zend_register_internal_interface(&class_entry);
+	zend_declare_class_constant_long(zend_test_interface, ZEND_STRL("DUMMY"), 0);
+	INIT_CLASS_ENTRY(class_entry, "_ZendTestClass", NULL);
+	zend_test_class = zend_register_internal_class_ex(&class_entry, NULL);
+	zend_class_implements(zend_test_class, 1, zend_test_interface);
+	zend_test_class->create_object = zend_test_class_new;
+	zend_test_class->get_static_method = zend_test_class_static_method_get;
+
+	memcpy(&zend_test_class_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
+	zend_test_class_handlers.get_method = zend_test_class_method_get;
+	zend_test_class_handlers.call_method = zend_test_class_call_method;
+
+	INIT_CLASS_ENTRY(class_entry, "_ZendTestTrait", zend_test_trait_methods);
+	zend_test_trait = zend_register_internal_class(&class_entry);
+	zend_test_trait->ce_flags |= ZEND_ACC_TRAIT;
+	zend_declare_property_null(zend_test_trait, "testProp", sizeof("testProp")-1, ZEND_ACC_PUBLIC);
+	return SUCCESS;
+}
+
+PHP_MSHUTDOWN_FUNCTION(zend_test)
+{
+	return SUCCESS;
+}
+
+PHP_RINIT_FUNCTION(zend_test)
+{
+#if defined(COMPILE_DL_ZEND_TEST) && defined(ZTS)
+	ZEND_TSRMLS_CACHE_UPDATE();
+#endif
+	return SUCCESS;
+}
+
+PHP_RSHUTDOWN_FUNCTION(zend_test)
+{
+	return SUCCESS;
+}
+
+PHP_MINFO_FUNCTION(zend_test)
+{
+	php_info_print_table_start();
+	php_info_print_table_header(2, "zend-test extension", "enabled");
+	php_info_print_table_end();
+}
+
+const zend_function_entry zend_test_functions[] = {
+	ZEND_FE(zend_test_array_return, arginfo_zend_test_array_return)
+	ZEND_FE(zend_test_nullable_array_return, arginfo_zend_test_nullable_array_return)
+	ZEND_FE(zend_create_unterminated_string, NULL)
+	ZEND_FE(zend_terminate_string, arginfo_zend_terminate_string)
+	ZEND_FE(zend_leak_bytes, NULL)
+	ZEND_FE(zend_leak_variable, arginfo_zend_leak_variable)
+	ZEND_FE_END
+};
+
+zend_module_entry zend_test_module_entry = {
+	STANDARD_MODULE_HEADER,
+	"test",
+	zend_test_functions,
+	PHP_MINIT(zend_test),
+	PHP_MSHUTDOWN(zend_test),
+	PHP_RINIT(zend_test),
+	PHP_RSHUTDOWN(zend_test),
+	PHP_MINFO(zend_test),
+	PHP_ZEND_TEST_VERSION,
+	STANDARD_MODULE_PROPERTIES
+};
+
+#ifdef COMPILE_DL_ZEND_TEST
+#ifdef ZTS
+ZEND_TSRMLS_CACHE_DEFINE()
+#endif
+ZEND_GET_MODULE(test)
+#endif
+
diff --git a/travis/compile.sh b/travis/compile.sh
index 090ceda..bd127e7 100755
--- a/travis/compile.sh
+++ b/travis/compile.sh
@@ -58,6 +58,7 @@ $TS \
 --with-freetype-dir=/usr \
 --with-xpm-dir=/usr \
 --with-kerberos \
---enable-sysvmsg 
+--enable-sysvmsg \
+--enable-zend-test
 make -j2 --quiet
 make install
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.