diff options
| author | Johannes Schlüter <johannes@php.net> | 2013-01-29 19:50:00 +0100 |
|---|---|---|
| committer | Johannes Schlüter <johannes@php.net> | 2013-01-29 19:50:00 +0100 |
| commit | 078b025a15a22c290f36e66b0c3336376623266e (patch) | |
| tree | fcf8dc6d71225f86848056b73d999053730b4e93 | |
| parent | d7d9ce0f4257d65527766b367aefcea25ef264f4 (diff) | |
| parent | 3bedc8ec277a246db6d3bfbe6313a861e46c4505 (diff) | |
| download | php-git-078b025a15a22c290f36e66b0c3336376623266e.tar.gz | |
Merge branch 'PHP-5.5' of git.php.net:/php-src into PHP-5.5
67 files changed, 2678 insertions, 336 deletions
@@ -1,9 +1,42 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| -?? ??? 201?, PHP 5.5.0 Alpha 3 +?? ??? 201?, PHP 5.5.0 Beta 1 +- Core: + . Fixed bug #60833 (self, parent, static behave inconsistently + case-sensitive). (Stas, mario at include-once dot org) + . Implemented FR #60524 (specify temp dir by php.ini). (ALeX Kazik). + +- cURL: + . Implemented FR #46439 - added CURLFile for safer file uploads. + (Stas) + +24 Jan 2013, PHP 5.5.0 Alpha 4 - Core: . Fixed bug #63980 (object members get trimmed by zero bytes). (Laruence) + . Implemented RFC for Class Name Resolution As Scalar Via "class" Keyword. + (Ralph Schindler, Nikita Popov, Lars) + +- DateTime + . Added DateTimeImmutable - a variant of DateTime that only returns the + modified state instead of changing itself. (Derick) + +- FPM: + . Fixed bug #63999 (php with fpm fails to build on Solaris 10 or 11). (Adam) + +- pgsql: + . Bug #46408: Locale number format settings can cause pg_query_params to + break with numerics. (asmecher, Lars) + +- dba: + . Bug #62489: dba_insert not working as expected. + (marc-bennewitz at arcor dot de, Lars) + +- Reflection: + . Fixed bug #64007 (There is an ability to create instance of Generator by hand). + (Laruence) + +10 Jan 2013, PHP 5.5.0 Alpha 3 - General improvements: . Fixed bug #63874 (Segfault if php_strip_whitespace has heredoc). (Pierrick) @@ -19,26 +52,11 @@ PHP NEWS - cURL: . Added new functions curl_escape, curl_multi_setopt, curl_multi_strerror - curl_pause, curl_reset, curl_share_close, curl_share_init, - curl_share_setopt curl_strerror and curl_unescape. (Pierrick) + curl_pause, curl_reset, curl_share_close, curl_share_init, + curl_share_setopt curl_strerror and curl_unescape. (Pierrick) . Addes new curl options CURLOPT_TELNETOPTIONS, CURLOPT_GSSAPI_DELEGATION, CURLOPT_ACCEPTTIMEOUT_MS, CURLOPT_SSL_OPTIONS, CURLOPT_TCP_KEEPALIVE, - CURLOPT_TCP_KEEPIDLE and CURLOPT_TCP_KEEPINTVL. (Pierrick) - -- DateTime - . Added DateTimeImmutable - a variant of DateTime that only returns the - modified state instead of changing itself. (Derick) - -- FPM: - . Fixed bug #63999 (php with fpm fails to build on Solaris 10 or 11). (Adam) - -- pgsql: - . Bug #46408: Locale number format settings can cause pg_query_params to - break with numerics. (asmecher, Lars) - -- dba: - . Bug #62489: dba_insert not working as expected. - (marc-bennewitz at arcor dot de, Lars) + CURLOPT_TCP_KEEPIDLE and CURLOPT_TCP_KEEPINTVL. (Pierrick) 18 Dec 2012, PHP 5.5.0 Alpha 2 diff --git a/README.PARAMETER_PARSING_API b/README.PARAMETER_PARSING_API index 927e48188c..edcee0f2ea 100644 --- a/README.PARAMETER_PARSING_API +++ b/README.PARAMETER_PARSING_API @@ -28,6 +28,17 @@ Both functions return SUCCESS or FAILURE depending on the result. The auto-conversions are performed as necessary. Arrays, objects, and resources cannot be auto-converted. +PHP 5.5 includes a new function: + +int zend_parse_parameter(int flags, int arg_num TSRMLS_DC, zval **arg, const char *spec, ...); + +This function behaves like zend_parse_parameters_ex() except that instead of +reading the arguments from the stack, it receives a single zval to convert +(passed with double indirection). The passed zval may be changed in place as +part of the conversion process. + +See also https://wiki.php.net/rfc/zpp_improv#expose_zend_parse_arg_as_zend_parse_parameter + Type specifiers --------------- @@ -65,9 +76,13 @@ Type specifiers will not be touched by the parsing function if they are not passed to it. / - use SEPARATE_ZVAL_IF_NOT_REF() on the parameter it follows - ! - the parameter it follows can be of specified type or NULL (applies - to all specifiers except for 'b', 'l', and 'd'). If NULL is passed, the - results pointer is set to NULL as well. + ! - the parameter it follows can be of specified type or NULL. If NULL is + passed and the output for such type is a pointer, then the output + pointer is set to a native NULL pointer. + For 'b', 'l' and 'd', an extra argument of type zend_bool* must be + passed after the corresponding bool*, long* or double* arguments, + respectively. A non-zero value will be written to the zend_bool iif a + PHP NULL is passed. Note on 64bit compatibility @@ -24,6 +24,8 @@ PHP 5.5 UPGRADE NOTES - Drop Windows XP and 2003 support. (Pierre) - All internal case insensitivity handling for class, function and constant names is done according to ASCII rules, current locale settings are ignored. +- self, parent & static keywords now are always case-insensitive (see bug + #60833). - php_logo_guid(), php_egg_logo_guid(), php_real_logo_guid() and zend_logo_guid() have been removed - Removal of Logo GUIDs @@ -75,6 +77,8 @@ PHP 5.5 UPGRADE NOTES for example. (https://wiki.php.net/rfc/empty_isset_exprs) - Added generators. (https://wiki.php.net/rfc/generators) +- ClassName::class syntax returning full class name for a class as + a string constant. (https://wiki.php.net/rfc/class_name_scalars) ======================================== 2. Changes in SAPI modules @@ -149,6 +153,10 @@ PHP 5.5 UPGRADE NOTES Furthermore both set_error_handler(NULL) and set_exception_handler(NULL) will now return the previously defined error/exception handler. Previously bool(true) was returned. +- setcookie(), setrawcookie() and ext/session now send Max-Age headers alongside + Expires headers. (see https://wiki.php.net/rfc/cookie_max-age) +- curl_setopt now accepts new option CURLOPT_SAFE_UPLOAD and CURLFile object for + safer file uploads (see https://wiki.php.net/rfc/curl-file-upload) ======================================== 5. New Functions @@ -161,6 +169,9 @@ PHP 5.5 UPGRADE NOTES - password_needs_rehash() - password_verify() +- cURL: + - curl_file_create + - Hash: - hash_pbkdf2() @@ -261,6 +272,9 @@ PHP 5.5 UPGRADE NOTES - IntlRuleBasedBreakIterator - IntlCodePointBreakIterator +- cURL: + - CURLFile + ======================================== 7. Removed Extensions ======================================== @@ -280,10 +294,16 @@ PHP 5.5 UPGRADE NOTES - mysqli: - Added MYSQLI_SERVER_PUBLIC_KEY constant to be used with mysqli_options() +- cURL: + - Added CURLOPT_SAFE_UPLOAD to be used with curl_setopt(). + ======================================== 10. Changes to INI File Handling ======================================== +- Core: + - Added sys_temp_dir INI directive, for specifying temp firectory. + - Intl: - Added intl.use_exceptions INI directive, which controls what happens when global errors are set together with intl.error_level. diff --git a/Zend/tests/bug60833.phpt b/Zend/tests/bug60833.phpt new file mode 100644 index 0000000000..deb6c0f691 --- /dev/null +++ b/Zend/tests/bug60833.phpt @@ -0,0 +1,40 @@ +--TEST-- +Bug #60833 (self, parent, static behave inconsistently case-sensitive) +--FILE-- +<?php +class A { + static $x = "A"; + function testit() { + $this->v1 = new sELF; + $this->v2 = new SELF; + } +} + +class B extends A { + static $x = "B"; + function testit() { + PARENT::testit(); + $this->v3 = new sELF; + $this->v4 = new PARENT; + $this->v4 = STATIC::$x; + } +} +$t = new B(); +$t->testit(); +var_dump($t); +?> +--EXPECTF-- +object(B)#%d (4) { + ["v1"]=> + object(A)#%d (0) { + } + ["v2"]=> + object(A)#%d (0) { + } + ["v3"]=> + object(B)#%d (0) { + } + ["v4"]=> + string(1) "B" +} + diff --git a/Zend/tests/bug63462.phpt b/Zend/tests/bug63462.phpt new file mode 100644 index 0000000000..e936a6fa69 --- /dev/null +++ b/Zend/tests/bug63462.phpt @@ -0,0 +1,74 @@ +--TEST-- +Test script to verify that magic methods should be called only once when accessing an unset property. +--CREDITS-- +Marco Pivetta <ocramius@gmail.com> +--FILE-- +<?php +class Test { + public $publicProperty; + protected $protectedProperty; + private $privateProperty; + + public function __construct() { + unset( + $this->publicProperty, + $this->protectedProperty, + $this->privateProperty + ); + } + + function __get($name) { + echo '__get ' . $name . "\n"; + return $this->$name; + } + + function __set($name, $value) { + echo '__set ' . $name . "\n"; + $this->$name = $value; + } + + function __isset($name) { + echo '__isset ' . $name . "\n"; + return isset($this->$name); + } +} + +$test = new Test(); + +$test->nonExisting; +$test->publicProperty; +$test->protectedProperty; +$test->privateProperty; +isset($test->nonExisting); +isset($test->publicProperty); +isset($test->protectedProperty); +isset($test->privateProperty); +$test->nonExisting = 'value'; +$test->publicProperty = 'value'; +$test->protectedProperty = 'value'; +$test->privateProperty = 'value'; + +?> + +--EXPECTF-- +__get nonExisting + +Notice: Undefined property: Test::$nonExisting in %s on line %d +__get publicProperty + +Notice: Undefined property: Test::$publicProperty in %s on line %d +__get protectedProperty + +Notice: Undefined property: Test::$protectedProperty in %s on line %d +__get privateProperty + +Notice: Undefined property: Test::$privateProperty in %s on line %d +__isset nonExisting +__isset publicProperty +__isset protectedProperty +__isset privateProperty +__set nonExisting +__set publicProperty +__set protectedProperty +__set privateProperty + diff --git a/Zend/tests/class_name_as_scalar.phpt b/Zend/tests/class_name_as_scalar.phpt new file mode 100644 index 0000000000..38e55b16d5 --- /dev/null +++ b/Zend/tests/class_name_as_scalar.phpt @@ -0,0 +1,77 @@ +--TEST-- +class name as scalar from ::class keyword +--FILE-- +<?php + +namespace Foo\Bar { + class One { + // compile time constants + const A = self::class; + const B = Two::class; + } + class Two extends One { + public static function run() { + var_dump(self::class); // self compile time lookup + var_dump(static::class); // runtime lookup + var_dump(parent::class); // runtime lookup + var_dump(Baz::class); // default compile time lookup + } + } + class Three extends Two { + // compile time static lookups + public static function checkCompileTime( + $one = self::class, + $two = Baz::class, + $three = One::A, + $four = self::B + ) { + var_dump($one, $two, $three, $four); + } + } + echo "In NS\n"; + var_dump(Moo::CLASS); // resolve in namespace +} + +namespace { + use Bee\Bop as Moo, + Foo\Bar\One; + echo "Top\n"; + var_dump(One::class); // resolve from use + var_dump(Boo::class); // resolve in global namespace + var_dump(Moo::CLASS); // resolve from use as + var_dump(\Moo::Class); // resolve fully qualified + $class = One::class; // assign class as scalar to var + $x = new $class; // create new class from original scalar assignment + var_dump($x); + Foo\Bar\Two::run(); // resolve runtime lookups + echo "Parent\n"; + Foo\Bar\Three::run(); // resolve runtime lookups with inheritance + echo "Compile Check\n"; + Foo\Bar\Three::checkCompileTime(); +} + +?> +--EXPECTF-- +In NS +string(11) "Foo\Bar\Moo" +Top +string(11) "Foo\Bar\One" +string(3) "Boo" +string(7) "Bee\Bop" +string(3) "Moo" +object(Foo\Bar\One)#1 (0) { +} +string(11) "Foo\Bar\Two" +string(11) "Foo\Bar\Two" +string(11) "Foo\Bar\One" +string(11) "Foo\Bar\Baz" +Parent +string(11) "Foo\Bar\Two" +string(13) "Foo\Bar\Three" +string(11) "Foo\Bar\One" +string(11) "Foo\Bar\Baz" +Compile Check +string(13) "Foo\Bar\Three" +string(11) "Foo\Bar\Baz" +string(11) "Foo\Bar\One" +string(11) "Foo\Bar\Two" diff --git a/Zend/tests/class_name_as_scalar_error_001.phpt b/Zend/tests/class_name_as_scalar_error_001.phpt new file mode 100644 index 0000000000..1c7aa7ea84 --- /dev/null +++ b/Zend/tests/class_name_as_scalar_error_001.phpt @@ -0,0 +1,13 @@ +--TEST-- +class name as scalar from ::class keyword error using static in class constant +--FILE-- +<?php + +namespace Foo\Bar { + class One { + const Baz = static::class; + } +} +?> +--EXPECTF-- +Fatal error: static::class cannot be used for compile-time class name resolution in %s on line %d diff --git a/Zend/tests/class_name_as_scalar_error_002.phpt b/Zend/tests/class_name_as_scalar_error_002.phpt new file mode 100644 index 0000000000..59b7a2edc9 --- /dev/null +++ b/Zend/tests/class_name_as_scalar_error_002.phpt @@ -0,0 +1,13 @@ +--TEST-- +class name as scalar from ::class keyword error using parent in class constant +--FILE-- +<?php + +namespace Foo\Bar { + class One { + const Baz = parent::class; + } +} +?> +--EXPECTF-- +Fatal error: parent::class cannot be used for compile-time class name resolution in %s on line %d diff --git a/Zend/tests/class_name_as_scalar_error_003.phpt b/Zend/tests/class_name_as_scalar_error_003.phpt new file mode 100644 index 0000000000..9299041693 --- /dev/null +++ b/Zend/tests/class_name_as_scalar_error_003.phpt @@ -0,0 +1,13 @@ +--TEST-- +class name as scalar from ::class keyword error using static in method signature +--FILE-- +<?php + +namespace Foo\Bar { + class One { + public function baz($x = static::class) {} + } +} +?> +--EXPECTF-- +Fatal error: static::class cannot be used for compile-time class name resolution in %s on line %d diff --git a/Zend/tests/class_name_as_scalar_error_004.phpt b/Zend/tests/class_name_as_scalar_error_004.phpt new file mode 100644 index 0000000000..c00037fca3 --- /dev/null +++ b/Zend/tests/class_name_as_scalar_error_004.phpt @@ -0,0 +1,13 @@ +--TEST-- +class name as scalar from ::class keyword error using parent in method signature +--FILE-- +<?php + +namespace Foo\Bar { + class One { + public function baz($x = parent::class) {} + } +} +?> +--EXPECTF-- +Fatal error: parent::class cannot be used for compile-time class name resolution in %s on line %d diff --git a/Zend/tests/class_name_as_scalar_error_005.phpt b/Zend/tests/class_name_as_scalar_error_005.phpt new file mode 100644 index 0000000000..39de69ffb3 --- /dev/null +++ b/Zend/tests/class_name_as_scalar_error_005.phpt @@ -0,0 +1,10 @@ +--TEST-- +class name as scalar from ::class keyword error using static non class context +--FILE-- +<?php + +$x = static::class; + +?> +--EXPECTF-- +Fatal error: Cannot access static::class when no class scope is active in %s on line %d
\ No newline at end of file diff --git a/Zend/tests/class_name_as_scalar_error_006.phpt b/Zend/tests/class_name_as_scalar_error_006.phpt new file mode 100644 index 0000000000..a4cc9a528b --- /dev/null +++ b/Zend/tests/class_name_as_scalar_error_006.phpt @@ -0,0 +1,10 @@ +--TEST-- +class name as scalar from ::class keyword error using parent in non class context +--FILE-- +<?php + +$x = parent::class; + +?> +--EXPECTF-- +Fatal error: Cannot access parent::class when no class scope is active in %s on line %d diff --git a/Zend/zend_API.c b/Zend/zend_API.c index eec4ab0bb1..95c90ea753 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -306,16 +306,14 @@ static const char *zend_parse_arg_impl(int arg_num, zval **arg, va_list *va, con { const char *spec_walk = *spec; char c = *spec_walk++; - int return_null = 0; + int check_null = 0; /* scan through modifiers */ while (1) { if (*spec_walk == '/') { SEPARATE_ZVAL_IF_NOT_REF(arg); } else if (*spec_walk == '!') { - if (Z_TYPE_PP(arg) == IS_NULL) { - return_null = 1; - } + check_null = 1; } else { break; } @@ -327,6 +325,12 @@ static const char *zend_parse_arg_impl(int arg_num, zval **arg, va_list *va, con case 'L': { long *p = va_arg(*va, long *); + + if (check_null) { + zend_bool *p = va_arg(*va, zend_bool *); + *p = (Z_TYPE_PP(arg) == IS_NULL); + } + switch (Z_TYPE_PP(arg)) { case IS_STRING: { @@ -380,6 +384,12 @@ static const char *zend_parse_arg_impl(int arg_num, zval **arg, va_list *va, con case 'd': { double *p = va_arg(*va, double *); + + if (check_null) { + zend_bool *p = va_arg(*va, zend_bool *); + *p = (Z_TYPE_PP(arg) == IS_NULL); + } + switch (Z_TYPE_PP(arg)) { case IS_STRING: { @@ -418,7 +428,7 @@ static const char *zend_parse_arg_impl(int arg_num, zval **arg, va_list *va, con int *pl = va_arg(*va, int *); switch (Z_TYPE_PP(arg)) { case IS_NULL: - if (return_null) { + if (check_null) { *p = NULL; *pl = 0; break; @@ -462,6 +472,12 @@ static const char *zend_parse_arg_impl(int arg_num, zval **arg, va_list *va, con case 'b': { zend_bool *p = va_arg(*va, zend_bool *); + + if (check_null) { + zend_bool *p = va_arg(*va, zend_bool *); + *p = (Z_TYPE_PP(arg) == IS_NULL); + } + switch (Z_TYPE_PP(arg)) { case IS_NULL: case IS_STRING: @@ -484,7 +500,7 @@ static const char *zend_parse_arg_impl(int arg_num, zval **arg, va_list *va, con case 'r': { zval **p = va_arg(*va, zval **); - if (return_null) { + if (check_null && Z_TYPE_PP(arg) == IS_NULL) { *p = NULL; break; } @@ -499,7 +515,7 @@ static const char *zend_parse_arg_impl(int arg_num, zval **arg, va_list *va, con case 'a': { zval **p = va_arg(*va, zval **); - if (return_null) { + if (check_null && Z_TYPE_PP(arg) == IS_NULL) { *p = NULL; break; } @@ -514,7 +530,7 @@ static const char *zend_parse_arg_impl(int arg_num, zval **arg, va_list *va, con case 'h': { HashTable **p = va_arg(*va, HashTable **); - if (return_null) { + if (check_null && Z_TYPE_PP(arg) == IS_NULL) { *p = NULL; break; } @@ -534,7 +550,7 @@ static const char *zend_parse_arg_impl(int arg_num, zval **arg, va_list *va, con case 'o': { zval **p = va_arg(*va, zval **); - if (return_null) { + if (check_null && Z_TYPE_PP(arg) == IS_NULL) { *p = NULL; break; } @@ -551,7 +567,7 @@ static const char *zend_parse_arg_impl(int arg_num, zval **arg, va_list *va, con zval **p = va_arg(*va, zval **); zend_class_entry *ce = va_arg(*va, zend_class_entry *); - if (return_null) { + if (check_null && Z_TYPE_PP(arg) == IS_NULL) { *p = NULL; break; } @@ -573,7 +589,7 @@ static const char *zend_parse_arg_impl(int arg_num, zval **arg, va_list *va, con zend_class_entry **lookup, **pce = va_arg(*va, zend_class_entry **); zend_class_entry *ce_base = *pce; - if (return_null) { + if (check_null && Z_TYPE_PP(arg) == IS_NULL) { *pce = NULL; break; } @@ -607,7 +623,7 @@ static const char *zend_parse_arg_impl(int arg_num, zval **arg, va_list *va, con zend_fcall_info_cache *fcc = va_arg(*va, zend_fcall_info_cache *); char *is_callable_error = NULL; - if (return_null) { + if (check_null && Z_TYPE_PP(arg) == IS_NULL) { fci->size = 0; fcc->initialized = 0; break; @@ -637,7 +653,7 @@ static const char *zend_parse_arg_impl(int arg_num, zval **arg, va_list *va, con case 'z': { zval **p = va_arg(*va, zval **); - if (return_null) { + if (check_null && Z_TYPE_PP(arg) == IS_NULL) { *p = NULL; } else { *p = *arg; @@ -648,7 +664,7 @@ static const char *zend_parse_arg_impl(int arg_num, zval **arg, va_list *va, con case 'Z': { zval ***p = va_arg(*va, zval ***); - if (return_null) { + if (check_null && Z_TYPE_PP(arg) == IS_NULL) { *p = NULL; } else { *p = arg; @@ -697,6 +713,19 @@ static int zend_parse_arg(int arg_num, zval **arg, va_list *va, const char **spe } /* }}} */ +ZEND_API int zend_parse_parameter(int flags, int arg_num TSRMLS_DC, zval **arg, const char *spec, ...) +{ + va_list va; + int ret; + int quiet = flags & ZEND_PARSE_PARAMS_QUIET; + + va_start(va, spec); + ret = zend_parse_arg(arg_num, arg, &va, &spec, quiet TSRMLS_CC); + va_end(va); + + return ret; +} + static int zend_parse_va_args(int num_args, const char *type_spec, va_list *va, int flags TSRMLS_DC) /* {{{ */ { const char *spec_walk; diff --git a/Zend/zend_API.h b/Zend/zend_API.h index 4a3985e0f4..8574f90eb5 100644 --- a/Zend/zend_API.h +++ b/Zend/zend_API.h @@ -258,6 +258,8 @@ ZEND_API char *zend_zval_type_name(const zval *arg); ZEND_API int zend_parse_method_parameters(int num_args TSRMLS_DC, zval *this_ptr, const char *type_spec, ...); ZEND_API int zend_parse_method_parameters_ex(int flags, int num_args TSRMLS_DC, zval *this_ptr, const char *type_spec, ...); +ZEND_API int zend_parse_parameter(int flags, int arg_num TSRMLS_DC, zval **arg, const char *spec, ...); + /* End of parameter parsing API -- andrei */ ZEND_API int zend_register_functions(zend_class_entry *scope, const zend_function_entry *functions, HashTable *function_table, int type TSRMLS_DC); diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 43b891695e..1a8d108900 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -5,7 +5,7 @@ | Copyright (c) 1998-2013 Zend Technologies Ltd. (http://www.zend.com) | +----------------------------------------------------------------------+ | This source file is subject to version 2.00 of the Zend license, | - | that is bundled with this package in the file LICENSE, and is | + | that is bundled with this package in the file LICENSE, and is | | available through the world-wide-web at the following url: | | http://www.zend.com/license/2_00.txt. | | If you did not receive a copy of the Zend license and are unable to | @@ -107,7 +107,7 @@ ZEND_API zend_executor_globals executor_globals; static void zend_duplicate_property_info(zend_property_info *property_info) /* {{{ */ { - if (!IS_INTERNED(property_info->name)) { + if (!IS_INTERNED(property_info->name)) { property_info->name = estrndup(property_info->name, property_info->name_length); } if (property_info->doc_comment) { @@ -118,7 +118,7 @@ static void zend_duplicate_property_info(zend_property_info *property_info) /* { static void zend_duplicate_property_info_internal(zend_property_info *property_info) /* {{{ */ { - if (!IS_INTERNED(property_info->name)) { + if (!IS_INTERNED(property_info->name)) { property_info->name = zend_strndup(property_info->name, property_info->name_length); } } @@ -383,7 +383,7 @@ int zend_add_func_name_literal(zend_op_array *op_array, const zval *zv TSRMLS_DC zval c; int lc_literal; - if (op_array->last_literal > 0 && + if (op_array->last_literal > 0 && &op_array->literals[op_array->last_literal - 1].constant == zv && op_array->literals[op_array->last_literal - 1].cache_slot == -1) { /* we already have function name as last literal (do nothing) */ @@ -391,7 +391,7 @@ int zend_add_func_name_literal(zend_op_array *op_array, const zval *zv TSRMLS_DC } else { ret = zend_add_literal(op_array, zv TSRMLS_CC); } - + lc_name = zend_str_tolower_dup(Z_STRVAL_P(zv), Z_STRLEN_P(zv)); ZVAL_STRINGL(&c, lc_name, Z_STRLEN_P(zv), 0); lc_literal = zend_add_literal(CG(active_op_array), &c TSRMLS_CC); @@ -410,7 +410,7 @@ int zend_add_ns_func_name_literal(zend_op_array *op_array, const zval *zv TSRMLS zval c; int lc_literal; - if (op_array->last_literal > 0 && + if (op_array->last_literal > 0 && &op_array->literals[op_array->last_literal - 1].constant == zv && op_array->literals[op_array->last_literal - 1].cache_slot == -1) { /* we already have function name as last literal (do nothing) */ @@ -443,7 +443,7 @@ int zend_add_class_name_literal(zend_op_array *op_array, const zval *zv TSRMLS_D zval c; int lc_literal; - if (op_array->last_literal > 0 && + if (op_array->last_literal > 0 && &op_array->literals[op_array->last_literal - 1].constant == zv && op_array->literals[op_array->last_literal - 1].cache_slot == -1) { /* we already have function name as last literal (do nothing) */ @@ -477,7 +477,7 @@ int zend_add_const_name_literal(zend_op_array *op_array, const zval *zv, int unq int name_len, ns_len; zval c; - if (op_array->last_literal > 0 && + if (op_array->last_literal > 0 && &op_array->literals[op_array->last_literal - 1].constant == zv && op_array->literals[op_array->last_literal - 1].cache_slot == -1) { /* we already have function name as last literal (do nothing) */ @@ -486,7 +486,7 @@ int zend_add_const_name_literal(zend_op_array *op_array, const zval *zv, int unq ret = zend_add_literal(op_array, zv TSRMLS_CC); } - /* skip leading '\\' */ + /* skip leading '\\' */ if (Z_STRVAL_P(zv)[0] == '\\') { name_len = Z_STRLEN_P(zv) - 1; name = Z_STRVAL_P(zv) + 1; @@ -816,7 +816,7 @@ void fetch_array_dim(znode *result, const znode *parent, const znode *dim TSRMLS opline.result.var = opline.op1.var; zend_llist_add_element(fetch_list_ptr, &opline); } - + init_op(&opline TSRMLS_CC); opline.opcode = ZEND_FETCH_DIM_W; /* the backpatching routine assumes W */ opline.result_type = IS_VAR; @@ -830,12 +830,12 @@ void fetch_array_dim(znode *result, const znode *parent, const znode *dim TSRMLS ZEND_HANDLE_NUMERIC_EX(Z_STRVAL(CONSTANT(opline.op2.constant)), Z_STRLEN(CONSTANT(opline.op2.constant))+1, index, numeric = 1); if (numeric) { zval_dtor(&CONSTANT(opline.op2.constant)); - ZVAL_LONG(&CONSTANT(opline.op2.constant), index); + ZVAL_LONG(&CONSTANT(opline.op2.constant), index); } else { CALCULATE_LITERAL_HASH(opline.op2.constant); } } - + GET_NODE(result, opline.result); zend_llist_add_element(fetch_list_ptr, &opline); @@ -940,7 +940,7 @@ void zend_do_assign(znode *result, znode *variable, znode *value TSRMLS_DC) /* { opline->result.var = get_temporary_variable(CG(active_op_array)); opline->op1_type = IS_CONST; LITERAL_STRINGL(opline->op1, - CG(active_op_array)->vars[value->u.op.var].name, + CG(active_op_array)->vars[value->u.op.var].name, CG(active_op_array)->vars[value->u.op.var].name_len, 1); CALCULATE_LITERAL_HASH(opline->op1.constant); SET_UNUSED(opline->op2); @@ -1569,7 +1569,7 @@ void zend_do_begin_function_declaration(znode *function_token, znode *function_n if (is_method) { int result; - + lcname = zend_new_interned_string(zend_str_tolower_dup(name, name_len), name_len + 1, 1 TSRMLS_CC); if (IS_INTERNED(lcname)) { @@ -1624,7 +1624,7 @@ void zend_do_begin_function_declaration(znode *function_token, znode *function_n } } else { char *class_lcname; - + class_lcname = do_alloca(CG(active_class_entry)->name_length + 1, use_heap); zend_str_tolower_copy(class_lcname, CG(active_class_entry)->name, CG(active_class_entry)->name_length); /* Improve after RC: cache the lowercase class name */ @@ -1675,7 +1675,7 @@ void zend_do_begin_function_declaration(znode *function_token, znode *function_n } else if ((name_len == sizeof(ZEND_TOSTRING_FUNC_NAME)-1) && (!memcmp(lcname, ZEND_TOSTRING_FUNC_NAME, sizeof(ZEND_TOSTRING_FUNC_NAME)-1))) { if (fn_flags & ((ZEND_ACC_PPP_MASK | ZEND_ACC_STATIC) ^ ZEND_ACC_PUBLIC)) { zend_error(E_WARNING, "The magic method __toString() must have public visibility and cannot be static"); - } + } CG(active_class_entry)->__tostring = (zend_function *) CG(active_op_array); } else if (!(fn_flags & ZEND_ACC_STATIC)) { CG(active_op_array)->fn_flags |= ZEND_ACC_ALLOW_STATIC; @@ -1806,14 +1806,14 @@ void zend_do_end_function_declaration(const znode *function_token TSRMLS_DC) /* if (CG(active_class_entry)) { zend_check_magic_method_implementation(CG(active_class_entry), (zend_function*)CG(active_op_array), E_COMPILE_ERROR TSRMLS_CC); } else { - /* we don't care if the function name is longer, in fact lowercasing only + /* we don't care if the function name is longer, in fact lowercasing only * the beginning of the name speeds up the check process */ name_len = strlen(CG(active_op_array)->function_name); zend_str_tolower_copy(lcname, CG(active_op_array)->function_name, MIN(name_len, sizeof(lcname)-1)); lcname[sizeof(lcname)-1] = '\0'; /* zend_str_tolower_copy won't necessarily set the zero byte */ if (name_len == sizeof(ZEND_AUTOLOAD_FUNC_NAME) - 1 && !memcmp(lcname, ZEND_AUTOLOAD_FUNC_NAME, sizeof(ZEND_AUTOLOAD_FUNC_NAME)) && CG(active_op_array)->num_args != 1) { zend_error(E_COMPILE_ERROR, "%s() must take exactly 1 argument", ZEND_AUTOLOAD_FUNC_NAME); - } + } } CG(active_op_array)->line_end = zend_get_compiled_lineno(TSRMLS_C); @@ -1939,7 +1939,7 @@ int zend_do_begin_function_call(znode *function_name, zend_bool check_namespace internal function with short name */ zend_do_begin_dynamic_function_call(function_name, 1 TSRMLS_CC); return 1; - } + } lcname = zend_str_tolower_dup(function_name->u.constant.value.str.val, function_name->u.constant.value.str.len); if ((zend_hash_find(CG(function_table), lcname, function_name->u.constant.value.str.len+1, (void **) &function)==FAILURE) || @@ -1948,10 +1948,10 @@ int zend_do_begin_function_call(znode *function_name, zend_bool check_namespace zend_do_begin_dynamic_function_call(function_name, 0 TSRMLS_CC); efree(lcname); return 1; /* Dynamic */ - } + } efree(function_name->u.constant.value.str.val); function_name->u.constant.value.str.val = lcname; - + zend_stack_push(&CG(function_call_stack), (void *) &function, sizeof(zend_function *)); if (CG(context).nested_calls + 1 > CG(active_op_array)->nested_calls) { CG(active_op_array)->nested_calls = CG(context).nested_calls + 1; @@ -1984,7 +1984,7 @@ void zend_do_begin_method_call(znode *left_bracket TSRMLS_DC) /* {{{ */ name = CONSTANT(last_op->op2.constant); if (Z_TYPE(name) != IS_STRING) { zend_error(E_COMPILE_ERROR, "Method name must be a string"); - } + } if (!IS_INTERNED(Z_STRVAL(name))) { Z_STRVAL(name) = estrndup(Z_STRVAL(name), Z_STRLEN(name)); } @@ -2119,6 +2119,53 @@ void zend_resolve_non_class_name(znode *element_name, zend_bool check_namespace } /* }}} */ +void zend_do_resolve_class_name(znode *result, znode *class_name, int is_static TSRMLS_DC) /* {{{ */ +{ + char *lcname; + int lctype; + znode constant_name; + + lcname = zend_str_tolower_dup(Z_STRVAL(class_name->u.constant), class_name->u.constant.value.str.len); + lctype = zend_get_class_fetch_type(lcname, strlen(lcname)); + switch (lctype) { + case ZEND_FETCH_CLASS_SELF: + if (!CG(active_class_entry)) { + zend_error(E_COMPILE_ERROR, "Cannot access self::class when no class scope is active"); + } + zval_dtor(&class_name->u.constant); + class_name->op_type = IS_CONST; + ZVAL_STRINGL(&class_name->u.constant, CG(active_class_entry)->name, CG(active_class_entry)->name_length, 1); + *result = *class_name; + break; + case ZEND_FETCH_CLASS_STATIC: + case ZEND_FETCH_CLASS_PARENT: + if (is_static) { + zend_error(E_COMPILE_ERROR, + "%s::class cannot be used for compile-time class name resolution", + lctype == ZEND_FETCH_CLASS_STATIC ? "static" : "parent" + ); + } + if (!CG(active_class_entry)) { + zend_error(E_COMPILE_ERROR, + "Cannot access %s::class when no class scope is active", + lctype == ZEND_FETCH_CLASS_STATIC ? "static" : "parent" + ); + } + constant_name.op_type = IS_CONST; + ZVAL_STRINGL(&constant_name.u.constant, "class", sizeof("class")-1, 1); + zend_do_fetch_constant(result, class_name, &constant_name, ZEND_RT, 1 TSRMLS_CC); + break; + case ZEND_FETCH_CLASS_DEFAULT: + zend_resolve_class_name(class_name, ZEND_FETCH_CLASS_GLOBAL, 1 TSRMLS_CC); + *result = *class_name; + break; + } + + efree(lcname); + +} +/* }}} */ + void zend_resolve_class_name(znode *class_name, ulong fetch_type, int check_ns_name TSRMLS_DC) /* {{{ */ { char *compound; @@ -2141,7 +2188,7 @@ void zend_resolve_class_name(znode *class_name, ulong fetch_type, int check_ns_n if (ZEND_FETCH_CLASS_DEFAULT != zend_get_class_fetch_type(Z_STRVAL(class_name->u.constant), Z_STRLEN(class_name->u.constant))) { zend_error(E_COMPILE_ERROR, "'\\%s' is an invalid class name", Z_STRVAL(class_name->u.constant)); } - } else { + } else { if (CG(current_import)) { len = compound - Z_STRVAL(class_name->u.constant); lcname = zend_str_tolower_dup(Z_STRVAL(class_name->u.constant), len); @@ -2506,8 +2553,8 @@ void zend_do_pass_param(znode *param, zend_uchar op, int offset TSRMLS_DC) /* {{ zend_error(E_COMPILE_ERROR, "Call-time pass-by-reference has been removed"); } return; - } - + } + if (function_ptr) { if (ARG_MAY_BE_SENT_BY_REF(function_ptr, (zend_uint) offset)) { if (param->op_type & (IS_VAR|IS_CV) && original_op != ZEND_SEND_VAL) { @@ -2678,7 +2725,7 @@ void zend_do_return(znode *expr, int do_end_vparse TSRMLS_DC) /* {{{ */ SET_UNUSED(opline->op1); SET_UNUSED(opline->op2); } - + opline = get_next_op(CG(active_op_array) TSRMLS_CC); opline->opcode = returns_reference ? ZEND_RETURN_BY_REF : ZEND_RETURN; @@ -2893,10 +2940,10 @@ void zend_do_end_finally(znode *try_token, znode* catch_token, znode *finally_to { if (catch_token->op_type == IS_UNUSED && finally_token->op_type == IS_UNUSED) { zend_error(E_COMPILE_ERROR, "Cannot use try without catch or finally"); - } + } if (finally_token->op_type != IS_UNUSED) { zend_op *opline; - + CG(active_op_array)->try_catch_array[try_token->u.op.opline_num].finally_op = finally_token->u.op.opline_num + 1; CG(active_op_array)->try_catch_array[try_token->u.op.opline_num].finally_end = get_next_op_number(CG(active_op_array)); CG(active_op_array)->has_finally_block = 1; @@ -2905,11 +2952,11 @@ void zend_do_end_finally(znode *try_token, znode* catch_token, znode *finally_to opline->opcode = ZEND_FAST_RET; SET_UNUSED(opline->op1); SET_UNUSED(opline->op2); - + CG(active_op_array)->opcodes[finally_token->u.op.opline_num].op1.opline_num = get_next_op_number(CG(active_op_array)); CG(context).in_finally--; - } + } } /* }}} */ @@ -3016,7 +3063,7 @@ static void do_inherit_parent_constructor(zend_class_entry *ce) /* {{{ */ lc_class_name = zend_str_tolower_dup(ce->name, ce->name_length); if (!zend_hash_exists(&ce->function_table, lc_class_name, ce->name_length+1)) { lc_parent_class_name = zend_str_tolower_dup(ce->parent->name, ce->parent->name_length); - if (!zend_hash_exists(&ce->function_table, lc_parent_class_name, ce->parent->name_length+1) && + if (!zend_hash_exists(&ce->function_table, lc_parent_class_name, ce->parent->name_length+1) && zend_hash_find(&ce->parent->function_table, lc_parent_class_name, ce->parent->name_length+1, (void **)&function)==SUCCESS) { if (function->common.fn_flags & ZEND_ACC_CTOR) { /* inherit parent's constructor */ @@ -3155,8 +3202,8 @@ static zend_bool zend_do_perform_implementation_check(const zend_function *fe, c return 0; } } - } - } + } + } if (fe->common.arg_info[i].type_hint != proto->common.arg_info[i].type_hint) { /* Incompatible type hint */ return 0; @@ -3185,7 +3232,7 @@ static zend_bool zend_do_perform_implementation_check(const zend_function *fe, c buf = erealloc(buf, length); \ } -static char * zend_get_function_declaration(zend_function *fptr TSRMLS_DC) /* {{{ */ +static char * zend_get_function_declaration(zend_function *fptr TSRMLS_DC) /* {{{ */ { char *offset, *buf; zend_uint length = 1024; @@ -3202,7 +3249,7 @@ static char * zend_get_function_declaration(zend_function *fptr TSRMLS_DC) /* {{ *(offset++) = ':'; *(offset++) = ':'; } - + { size_t name_len = strlen(fptr->common.function_name); REALLOC_BUF_IF_EXCEED(buf, offset, length, name_len); @@ -3243,7 +3290,7 @@ static char * zend_get_function_declaration(zend_function *fptr TSRMLS_DC) /* {{ offset += type_name_len; *(offset++) = ' '; } - + if (arg_info->pass_by_reference) { *(offset++) = '&'; } @@ -3345,7 +3392,7 @@ static char * zend_get_function_declaration(zend_function *fptr TSRMLS_DC) /* {{ *offset = '\0'; return buf; -} +} /* }}} */ static void do_inheritance_check_on_method(zend_function *child, zend_function *parent TSRMLS_DC) /* {{{ */ @@ -3357,7 +3404,7 @@ static void do_inheritance_check_on_method(zend_function *child, zend_function * && parent->common.fn_flags & ZEND_ACC_ABSTRACT && parent->common.scope != (child->common.prototype ? child->common.prototype->common.scope : child->common.scope) && child->common.fn_flags & (ZEND_ACC_ABSTRACT|ZEND_ACC_IMPLEMENTED_ABSTRACT)) { - zend_error(E_COMPILE_ERROR, "Can't inherit abstract function %s::%s() (previously declared abstract in %s)", + zend_error(E_COMPILE_ERROR, "Can't inherit abstract function %s::%s() (previously declared abstract in %s)", parent->common.scope->name, child->common.function_name, child->common.prototype ? child->common.prototype->common.scope->name : child->common.scope->name); @@ -3397,7 +3444,7 @@ static void do_inheritance_check_on_method(zend_function *child, zend_function * } if (parent_flags & ZEND_ACC_PRIVATE) { - child->common.prototype = NULL; + child->common.prototype = NULL; } else if (parent_flags & ZEND_ACC_ABSTRACT) { child->common.fn_flags |= ZEND_ACC_IMPLEMENTED_ABSTRACT; child->common.prototype = parent; @@ -3408,12 +3455,12 @@ static void do_inheritance_check_on_method(zend_function *child, zend_function * if (child->common.prototype && (child->common.prototype->common.fn_flags & ZEND_ACC_ABSTRACT)) { if (!zend_do_perform_implementation_check(child, child->common.prototype TSRMLS_CC)) { - zend_error(E_COMPILE_ERROR, "Declaration of %s::%s() must be compatible with %s", ZEND_FN_SCOPE_NAME(child), child->common.function_name, zend_get_function_declaration(child->common.prototype? child->common.prototype : parent TSRMLS_CC)); + zend_error(E_COMPILE_ERROR, "Declaration of %s::%s() must be compatible with %s", ZEND_FN_SCOPE_NAME(child), child->common.function_name, zend_get_function_declaration(child->common.prototype? child->common.prototype : parent TSRMLS_CC)); } } else if (EG(error_reporting) & E_STRICT || EG(user_error_handler)) { /* Check E_STRICT (or custom error handler) before the check so that we save some time */ if (!zend_do_perform_implementation_check(child, parent TSRMLS_CC)) { char *method_prototype = zend_get_function_declaration(child->common.prototype? child->common.prototype : parent TSRMLS_CC); - zend_error(E_STRICT, "Declaration of %s::%s() should be compatible with %s", ZEND_FN_SCOPE_NAME(child), child->common.function_name, method_prototype); + zend_error(E_STRICT, "Declaration of %s::%s() should be compatible with %s", ZEND_FN_SCOPE_NAME(child), child->common.function_name, method_prototype); efree(method_prototype); } } @@ -3432,9 +3479,9 @@ static zend_bool do_inherit_method_check(HashTable *child_function_table, zend_f } return 1; /* method doesn't exist in child, copy from parent */ } - + do_inheritance_check_on_method(child, parent TSRMLS_CC); - + return 0; } /* }}} */ @@ -3465,7 +3512,7 @@ static zend_bool do_inherit_property_access_check(HashTable *target_ht, zend_pro zend_error(E_COMPILE_ERROR, "Cannot redeclare %s%s::$%s as %s%s::$%s", (parent_info->flags & ZEND_ACC_STATIC) ? "static " : "non static ", parent_ce->name, hash_key->arKey, (child_info->flags & ZEND_ACC_STATIC) ? "static " : "non static ", ce->name, hash_key->arKey); - + } if(parent_info->flags & ZEND_ACC_CHANGED) { @@ -3731,10 +3778,10 @@ ZEND_API void zend_do_implement_interface(zend_class_entry *ce, zend_class_entry } } ce->interfaces[ce->num_interfaces++] = iface; - + zend_hash_merge_ex(&ce->constants_table, &iface->constants_table, (copy_ctor_func_t) zval_add_ref, sizeof(zval *), (merge_checker_func_t) do_inherit_constant_check, iface); zend_hash_merge_ex(&ce->function_table, &iface->function_table, (copy_ctor_func_t) do_inherit_method, sizeof(zend_function), (merge_checker_func_t) do_inherit_method_check, ce); - + do_implement_interface(ce, iface TSRMLS_CC); zend_do_inherit_interfaces(ce, iface TSRMLS_CC); } @@ -3774,10 +3821,10 @@ static zend_bool zend_traits_method_compatibility_check(zend_function *fn, zend_ { zend_uint fn_flags = fn->common.scope->ce_flags; zend_uint other_flags = other_fn->common.scope->ce_flags; - + return zend_do_perform_implementation_check(fn, other_fn TSRMLS_CC) && zend_do_perform_implementation_check(other_fn, fn TSRMLS_CC) - && ((fn_flags & (ZEND_ACC_FINAL|ZEND_ACC_STATIC)) == + && ((fn_flags & (ZEND_ACC_FINAL|ZEND_ACC_STATIC)) == (other_flags & (ZEND_ACC_FINAL|ZEND_ACC_STATIC))); /* equal final and static qualifier */ } /* }}} */ @@ -3790,7 +3837,7 @@ static void zend_add_magic_methods(zend_class_entry* ce, const char* mname, uint if (ce->constructor) { zend_error(E_COMPILE_ERROR, "%s has colliding constructor definitions coming from traits", ce->name); } - ce->constructor = fe; fe->common.fn_flags |= ZEND_ACC_CTOR; + ce->constructor = fe; fe->common.fn_flags |= ZEND_ACC_CTOR; } else if (!strncmp(mname, ZEND_DESTRUCTOR_FUNC_NAME, mname_len)) { ce->destructor = fe; fe->common.fn_flags |= ZEND_ACC_DTOR; } else if (!strncmp(mname, ZEND_GET_FUNC_NAME, mname_len)) { @@ -3927,7 +3974,7 @@ static int zend_traits_copy_functions(zend_function *fn TSRMLS_DC, int num_args, ce = va_arg(args, zend_class_entry*); overriden = va_arg(args, HashTable**); exclude_table = va_arg(args, HashTable*); - + fnname_len = strlen(fn->common.function_name); /* apply aliases which are qualified with a class name, there should not be any ambiguity */ @@ -3941,9 +3988,9 @@ static int zend_traits_copy_functions(zend_function *fn TSRMLS_DC, int num_args, && alias->trait_method->mname_len == fnname_len && (zend_binary_strcasecmp(alias->trait_method->method_name, alias->trait_method->mname_len, fn->common.function_name, fnname_len) == 0)) { fn_copy = *fn; - + /* if it is 0, no modifieres has been changed */ - if (alias->modifiers) { + if (alias->modifiers) { fn_copy.common.fn_flags = alias->modifiers | (fn->common.fn_flags ^ (fn->common.fn_flags & ZEND_ACC_PPP_MASK)); } @@ -4046,7 +4093,7 @@ static void zend_traits_init_trait_structures(zend_class_entry *ce TSRMLS_DC) /* cur_method_ref->ce->name, cur_method_ref->method_name); } - + /** With the other traits, we are more permissive. We do not give errors for those. This allows to be more defensive in such definitions. @@ -4060,20 +4107,20 @@ static void zend_traits_init_trait_structures(zend_class_entry *ce TSRMLS_DC) /* if (!(cur_precedence->exclude_from_classes[j] = zend_fetch_class(class_name, name_length, ZEND_FETCH_CLASS_TRAIT |ZEND_FETCH_CLASS_NO_AUTOLOAD TSRMLS_CC))) { zend_error(E_COMPILE_ERROR, "Could not find trait %s", class_name); - } + } zend_check_trait_usage(ce, cur_precedence->exclude_from_classes[j] TSRMLS_CC); /* make sure that the trait method is not from a class mentioned in exclude_from_classes, for consistency */ if (cur_precedence->trait_method->ce == cur_precedence->exclude_from_classes[i]) { zend_error(E_COMPILE_ERROR, - "Inconsistent insteadof definition. " + "Inconsistent insteadof definition. " "The method %s is to be used from %s, but %s is also on the exclude list", cur_method_ref->method_name, cur_precedence->trait_method->ce->name, cur_precedence->trait_method->ce->name); } - + efree(class_name); j++; } @@ -4113,7 +4160,7 @@ static void zend_traits_init_trait_structures(zend_class_entry *ce TSRMLS_DC) /* static void zend_traits_compile_exclude_table(HashTable* exclude_table, zend_trait_precedence **precedences, zend_class_entry *trait) /* {{{ */ { size_t i = 0, j; - + if (!precedences) { return; } @@ -4124,7 +4171,7 @@ static void zend_traits_compile_exclude_table(HashTable* exclude_table, zend_tra if (precedences[i]->exclude_from_classes[j] == trait) { zend_uint lcname_len = precedences[i]->trait_method->mname_len; char *lcname = zend_str_tolower_dup(precedences[i]->trait_method->method_name, lcname_len); - + if (zend_hash_add(exclude_table, lcname, lcname_len, NULL, 0, NULL) == FAILURE) { efree(lcname); zend_error(E_COMPILE_ERROR, "Failed to evaluate a trait precedence (%s). Method of trait %s was defined to be excluded multiple times", precedences[i]->trait_method->method_name, trait->name); @@ -4161,7 +4208,7 @@ static void zend_do_traits_method_binding(zend_class_entry *ce TSRMLS_DC) /* {{{ zend_hash_apply_with_arguments(&ce->traits[i]->function_table TSRMLS_CC, (apply_func_args_t)zend_traits_copy_functions, 3, ce, &overriden, NULL); } } - + zend_hash_apply_with_argument(&ce->function_table, (apply_func_arg_t)zend_fixup_trait_method, ce TSRMLS_CC); if (overriden) { @@ -4199,7 +4246,7 @@ static void zend_do_traits_property_binding(zend_class_entry *ce TSRMLS_DC) /* { const char* class_name_unused; zend_bool not_compatible; zval* prop_value; - char* doc_comment; + char* doc_comment; zend_uint flags; /* In the following steps the properties are inserted into the property table @@ -4228,10 +4275,10 @@ static void zend_do_traits_property_binding(zend_class_entry *ce TSRMLS_DC) /* { /* next: check for conflicts with current class */ if (zend_hash_quick_find(&ce->properties_info, prop_name, prop_name_length+1, prop_hash, (void **) &coliding_prop) == SUCCESS) { - if (coliding_prop->flags & ZEND_ACC_SHADOW) { + if (coliding_prop->flags & ZEND_ACC_SHADOW) { zend_hash_quick_del(&ce->properties_info, prop_name, prop_name_length+1, prop_hash); flags |= ZEND_ACC_CHANGED; - } else { + } else { if ((coliding_prop->flags & (ZEND_ACC_PPP_MASK | ZEND_ACC_STATIC)) == (flags & (ZEND_ACC_PPP_MASK | ZEND_ACC_STATIC))) { /* flags are identical, now the value needs to be checked */ @@ -4252,14 +4299,14 @@ static void zend_do_traits_property_binding(zend_class_entry *ce TSRMLS_DC) /* { } if (not_compatible) { - zend_error(E_COMPILE_ERROR, + zend_error(E_COMPILE_ERROR, "%s and %s define the same property ($%s) in the composition of %s. However, the definition differs and is considered incompatible. Class was composed", find_first_definition(ce, i, prop_name, prop_name_length, prop_hash, coliding_prop->ce)->name, property_info->ce->name, prop_name, ce->name); } else { - zend_error(E_STRICT, + zend_error(E_STRICT, "%s and %s define the same property ($%s) in the composition of %s. This might be incompatible, to improve maintainability consider using accessor methods in traits instead. Class was composed", find_first_definition(ce, i, prop_name, prop_name_length, prop_hash, coliding_prop->ce)->name, property_info->ce->name, @@ -4279,8 +4326,8 @@ static void zend_do_traits_property_binding(zend_class_entry *ce TSRMLS_DC) /* { Z_ADDREF_P(prop_value); doc_comment = property_info->doc_comment ? estrndup(property_info->doc_comment, property_info->doc_comment_len) : NULL; - zend_declare_property_ex(ce, prop_name, prop_name_length, - prop_value, flags, + zend_declare_property_ex(ce, prop_name, prop_name_length, + prop_value, flags, doc_comment, property_info->doc_comment_len TSRMLS_CC); } } @@ -4292,7 +4339,7 @@ static void zend_do_check_for_inconsistent_traits_aliasing(zend_class_entry *ce int i = 0; zend_trait_alias* cur_alias; char* lc_method_name; - + if (ce->trait_aliases) { while (ce->trait_aliases[i]) { cur_alias = ce->trait_aliases[i]; @@ -4313,7 +4360,7 @@ static void zend_do_check_for_inconsistent_traits_aliasing(zend_class_entry *ce we check against it and abort. 2) it is just a plain old inconsitency/typo/bug as in the case where alias is set. */ - + lc_method_name = zend_str_tolower_dup(cur_alias->trait_method->method_name, cur_alias->trait_method->mname_len); if (zend_hash_exists(&ce->function_table, @@ -4350,7 +4397,7 @@ ZEND_API void zend_do_bind_traits(zend_class_entry *ce TSRMLS_DC) /* {{{ */ /* first care about all methods to be flattened into the class */ zend_do_traits_method_binding(ce TSRMLS_CC); - + /* Aliases which have not been applied indicate typos/bugs. */ zend_do_check_for_inconsistent_traits_aliasing(ce TSRMLS_CC); @@ -4359,7 +4406,7 @@ ZEND_API void zend_do_bind_traits(zend_class_entry *ce TSRMLS_DC) /* {{{ */ /* verify that all abstract methods from traits have been implemented */ zend_verify_abstract_class(ce TSRMLS_CC); - + /* now everything should be fine and an added ZEND_ACC_IMPLICIT_ABSTRACT_CLASS should be removed */ if (ce->ce_flags & ZEND_ACC_IMPLICIT_ABSTRACT_CLASS) { ce->ce_flags -= ZEND_ACC_IMPLICIT_ABSTRACT_CLASS; @@ -4409,7 +4456,7 @@ void zend_prepare_reference(znode *result, znode *class_name, znode *method_name zend_trait_method_reference *method_ref = emalloc(sizeof(zend_trait_method_reference)); method_ref->ce = NULL; - /* REM: There should not be a need for copying, + /* REM: There should not be a need for copying, zend_do_begin_class_declaration is also just using that string */ if (class_name) { zend_resolve_class_name(class_name, ZEND_FETCH_CLASS_GLOBAL, 1 TSRMLS_CC); @@ -4983,7 +5030,7 @@ void zend_do_begin_class_declaration(const znode *class_token, znode *class_name build_runtime_defined_function_key(&key, lcname, new_class_entry->name_length TSRMLS_CC); opline->op1.constant = zend_add_literal(CG(active_op_array), &key TSRMLS_CC); Z_HASH_P(&CONSTANT(opline->op1.constant)) = zend_hash_func(Z_STRVAL(CONSTANT(opline->op1.constant)), Z_STRLEN(CONSTANT(opline->op1.constant))); - + opline->op2_type = IS_CONST; if (doing_inheritance) { @@ -5000,7 +5047,7 @@ void zend_do_begin_class_declaration(const znode *class_token, znode *class_name LITERAL_STRINGL(opline->op2, lcname, new_class_entry->name_length, 0); CALCULATE_LITERAL_HASH(opline->op2.constant); - + zend_hash_quick_update(CG(class_table), Z_STRVAL(key), Z_STRLEN(key), Z_HASH_P(&CONSTANT(opline->op1.constant)), &new_class_entry, sizeof(zend_class_entry *), NULL); CG(active_class_entry) = new_class_entry; @@ -5051,7 +5098,7 @@ void zend_do_end_class_declaration(const znode *class_token, const znode *parent } ce->info.user.line_end = zend_get_compiled_lineno(TSRMLS_C); - + /* Check for traits and proceed like with interfaces. * The only difference will be a combined handling of them in the end. * Thus, we need another opcode here. */ @@ -5076,7 +5123,7 @@ void zend_do_end_class_declaration(const znode *class_token, const znode *parent } } /* Inherit interfaces; reset number to zero, we need it for above check and - * will restore it during actual implementation. + * will restore it during actual implementation. * The ZEND_ACC_IMPLEMENT_INTERFACES flag disables double call to * zend_verify_abstract_class() */ if (ce->num_interfaces > 0) { @@ -5278,7 +5325,7 @@ void zend_do_declare_class_constant(znode *var_name, const znode *value TSRMLS_D ALLOC_ZVAL(property); *property = value->u.constant; - + cname = zend_new_interned_string(var_name->u.constant.value.str.val, var_name->u.constant.value.str.len+1, 0 TSRMLS_CC); if (IS_INTERNED(cname)) { @@ -5291,7 +5338,7 @@ void zend_do_declare_class_constant(znode *var_name, const znode *value TSRMLS_D zend_error(E_COMPILE_ERROR, "Cannot redefine class constant %s::%s", CG(active_class_entry)->name, var_name->u.constant.value.str.val); } FREE_PNODE(var_name); - + if (CG(doc_comment)) { efree(CG(doc_comment)); CG(doc_comment) = NULL; @@ -5380,17 +5427,17 @@ void zend_do_halt_compiler_register(TSRMLS_D) /* {{{ */ char *name, *cfilename; char haltoff[] = "__COMPILER_HALT_OFFSET__"; int len, clen; - + if (CG(has_bracketed_namespaces) && CG(in_namespace)) { zend_error(E_COMPILE_ERROR, "__HALT_COMPILER() can only be used from the outermost scope"); } - + cfilename = zend_get_compiled_filename(TSRMLS_C); clen = strlen(cfilename); zend_mangle_property_name(&name, &len, haltoff, sizeof(haltoff) - 1, cfilename, clen, 0); zend_register_long_constant(name, len+1, zend_get_scanned_file_offset(TSRMLS_C), CONST_CS, 0 TSRMLS_CC); pefree(name, 0); - + if (CG(in_namespace)) { zend_do_end_namespace(TSRMLS_C); } @@ -5467,7 +5514,7 @@ static zend_constant* zend_get_ct_const(const zval *const_name, int all_internal } } else if (zend_hash_find(EG(zend_constants), Z_STRVAL_P(const_name), Z_STRLEN_P(const_name)+1, (void **) &c) == FAILURE) { char *lookup_name = zend_str_tolower_dup(Z_STRVAL_P(const_name), Z_STRLEN_P(const_name)); - + if (zend_hash_find(EG(zend_constants), lookup_name, Z_STRLEN_P(const_name)+1, (void **) &c)==SUCCESS) { if ((c->flags & CONST_CT_SUBST) && !(c->flags & CONST_CS)) { efree(lookup_name); @@ -5520,7 +5567,7 @@ void zend_do_fetch_constant(znode *result, znode *constant_container, znode *con case ZEND_CT: /* this is a class constant */ type = zend_get_class_fetch_type(Z_STRVAL(constant_container->u.constant), Z_STRLEN(constant_container->u.constant)); - + if (ZEND_FETCH_CLASS_STATIC == type) { zend_error(E_ERROR, "\"static::\" is not allowed in compile-time constants"); } else if (ZEND_FETCH_CLASS_DEFAULT == type) { @@ -5584,7 +5631,7 @@ void zend_do_fetch_constant(znode *result, znode *constant_container, znode *con compound = memchr(Z_STRVAL(constant_name->u.constant), '\\', Z_STRLEN(constant_name->u.constant)); zend_resolve_non_class_name(constant_name, check_namespace TSRMLS_CC); - + if(zend_constant_ct_subst(result, &constant_name->u.constant, 1 TSRMLS_CC)) { break; } @@ -5600,7 +5647,7 @@ void zend_do_fetch_constant(znode *result, znode *constant_container, znode *con /* the name is unambiguous */ opline->extended_value = 0; opline->op2.constant = zend_add_const_name_literal(CG(active_op_array), &constant_name->u.constant, 0 TSRMLS_CC); - } else { + } else { opline->extended_value = IS_CONSTANT_UNQUALIFIED; if (CG(current_namespace)) { opline->extended_value |= IS_CONSTANT_IN_NAMESPACE; @@ -5674,7 +5721,7 @@ void zend_do_init_array(znode *result, const znode *expr, const znode *offset, z ZEND_HANDLE_NUMERIC_EX(Z_STRVAL(CONSTANT(opline->op2.constant)), Z_STRLEN(CONSTANT(opline->op2.constant))+1, index, numeric = 1); if (numeric) { zval_dtor(&CONSTANT(opline->op2.constant)); - ZVAL_LONG(&CONSTANT(opline->op2.constant), index); + ZVAL_LONG(&CONSTANT(opline->op2.constant), index); } else { CALCULATE_LITERAL_HASH(opline->op2.constant); } @@ -5706,7 +5753,7 @@ void zend_do_add_array_element(znode *result, const znode *expr, const znode *of ZEND_HANDLE_NUMERIC_EX(Z_STRVAL(CONSTANT(opline->op2.constant)), Z_STRLEN(CONSTANT(opline->op2.constant))+1, index, numeric = 1); if (numeric) { zval_dtor(&CONSTANT(opline->op2.constant)); - ZVAL_LONG(&CONSTANT(opline->op2.constant), index); + ZVAL_LONG(&CONSTANT(opline->op2.constant), index); } else { CALCULATE_LITERAL_HASH(opline->op2.constant); } @@ -5966,7 +6013,7 @@ void zend_do_fetch_lexical_variable(znode *varname, zend_bool is_ref TSRMLS_DC) Z_TYPE(value.u.constant) |= is_ref ? IS_LEXICAL_REF : IS_LEXICAL_VAR; Z_SET_REFCOUNT_P(&value.u.constant, 1); Z_UNSET_ISREF_P(&value.u.constant); - + zend_do_fetch_static_variable(varname, &value, is_ref ? ZEND_FETCH_STATIC : ZEND_FETCH_LEXICAL TSRMLS_CC); } /* }}} */ @@ -6482,7 +6529,7 @@ void zend_do_jmp_set(const znode *value, znode *jmp_token, znode *colon_token TS opline->result.var = get_temporary_variable(CG(active_op_array)); SET_NODE(opline->op1, value); SET_UNUSED(opline->op2); - + GET_NODE(colon_token, opline->result); jmp_token->u.op.opline_num = op_number; @@ -6511,11 +6558,11 @@ void zend_do_jmp_set_else(znode *result, const znode *false_value, const znode * opline->extended_value = 0; SET_NODE(opline->op1, false_value); SET_UNUSED(opline->op2); - + GET_NODE(result, opline->result); CG(active_op_array)->opcodes[jmp_token->u.op.opline_num].op2.opline_num = get_next_op_number(CG(active_op_array)); - + DEC_BPC(CG(active_op_array)); } /* }}} */ @@ -6728,7 +6775,7 @@ again: CG(increment_lineno) = 1; } if (CG(has_bracketed_namespaces) && !CG(in_namespace)) { - goto again; + goto again; } retval = ';'; /* implicit ; */ break; @@ -6818,13 +6865,13 @@ ZEND_API void zend_initialize_class_data(zend_class_entry *ce, zend_bool nullify int zend_get_class_fetch_type(const char *class_name, uint class_name_len) /* {{{ */ { if ((class_name_len == sizeof("self")-1) && - !memcmp(class_name, "self", sizeof("self")-1)) { - return ZEND_FETCH_CLASS_SELF; + !strncasecmp(class_name, "self", sizeof("self")-1)) { + return ZEND_FETCH_CLASS_SELF; } else if ((class_name_len == sizeof("parent")-1) && - !memcmp(class_name, "parent", sizeof("parent")-1)) { + !strncasecmp(class_name, "parent", sizeof("parent")-1)) { return ZEND_FETCH_CLASS_PARENT; } else if ((class_name_len == sizeof("static")-1) && - !memcmp(class_name, "static", sizeof("static")-1)) { + !strncasecmp(class_name, "static", sizeof("static")-1)) { return ZEND_FETCH_CLASS_STATIC; } else { return ZEND_FETCH_CLASS_DEFAULT; @@ -6937,7 +6984,7 @@ void zend_do_begin_namespace(const znode *name, zend_bool with_bracket TSRMLS_DC efree(CG(current_import)); CG(current_import) = NULL; } - + if (CG(doc_comment)) { efree(CG(doc_comment)); CG(doc_comment) = NULL; @@ -7109,7 +7156,7 @@ ZEND_API size_t zend_dirname(char *path, size_t len) len_adjust += 2; if (2 == len) { /* Return "c:" on Win32 for dirname("c:"). - * It would be more consistent to return "c:." + * It would be more consistent to return "c:." * but that would require making the string *longer*. */ return len; @@ -7117,7 +7164,7 @@ ZEND_API size_t zend_dirname(char *path, size_t len) } #elif defined(NETWARE) /* - * Find the first occurence of : from the left + * Find the first occurence of : from the left * move the path pointer to the position just after : * increment the len_adjust to the length of path till colon character(inclusive) * If there is no character beyond : simple return len diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index 0f58b55500..8042dd54ee 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -638,6 +638,8 @@ void zend_verify_namespace(TSRMLS_D); void zend_do_use(znode *name, znode *new_name, int is_global TSRMLS_DC); void zend_do_end_compilation(TSRMLS_D); +void zend_do_resolve_class_name(znode *result, znode *class_name, int is_static TSRMLS_DC); + void zend_do_label(znode *label TSRMLS_DC); void zend_do_goto(const znode *label TSRMLS_DC); void zend_resolve_goto_label(zend_op_array *op_array, zend_op *opline, int pass2 TSRMLS_DC); diff --git a/Zend/zend_language_parser.y b/Zend/zend_language_parser.y index 781d806fe7..ccbc9b174c 100644 --- a/Zend/zend_language_parser.y +++ b/Zend/zend_language_parser.y @@ -942,6 +942,7 @@ common_scalar: static_scalar: /* compile-time evaluated scalars */ common_scalar { $$ = $1; } + | static_class_name_scalar { $$ = $1; } | namespace_name { zend_do_fetch_constant(&$$, NULL, &$1, ZEND_CT, 1 TSRMLS_CC); } | T_NAMESPACE T_NS_SEPARATOR namespace_name { $$.op_type = IS_CONST; ZVAL_EMPTY_STRING(&$$.u.constant); zend_do_build_namespace_name(&$$, &$$, &$3 TSRMLS_CC); $3 = $$; zend_do_fetch_constant(&$$, NULL, &$3, ZEND_CT, 0 TSRMLS_CC); } | T_NS_SEPARATOR namespace_name { char *tmp = estrndup(Z_STRVAL($2.u.constant), Z_STRLEN($2.u.constant)+1); memcpy(&(tmp[1]), Z_STRVAL($2.u.constant), Z_STRLEN($2.u.constant)+1); tmp[0] = '\\'; efree(Z_STRVAL($2.u.constant)); Z_STRVAL($2.u.constant) = tmp; ++Z_STRLEN($2.u.constant); zend_do_fetch_constant(&$$, NULL, &$2, ZEND_CT, 0 TSRMLS_CC); } @@ -959,6 +960,7 @@ static_class_constant: scalar: T_STRING_VARNAME { $$ = $1; } + | class_name_scalar { $$ = $1; } | class_constant { $$ = $1; } | namespace_name { zend_do_fetch_constant(&$$, NULL, &$1, ZEND_RT, 1 TSRMLS_CC); } | T_NAMESPACE T_NS_SEPARATOR namespace_name { $$.op_type = IS_CONST; ZVAL_EMPTY_STRING(&$$.u.constant); zend_do_build_namespace_name(&$$, &$$, &$3 TSRMLS_CC); $3 = $$; zend_do_fetch_constant(&$$, NULL, &$3, ZEND_RT, 0 TSRMLS_CC); } @@ -1200,6 +1202,14 @@ class_constant: | variable_class_name T_PAAMAYIM_NEKUDOTAYIM T_STRING { zend_do_fetch_constant(&$$, &$1, &$3, ZEND_RT, 0 TSRMLS_CC); } ; +static_class_name_scalar: + class_name T_PAAMAYIM_NEKUDOTAYIM T_CLASS { zend_do_resolve_class_name(&$$, &$1, 1 TSRMLS_CC); } +; + +class_name_scalar: + class_name T_PAAMAYIM_NEKUDOTAYIM T_CLASS { zend_do_resolve_class_name(&$$, &$1, 0 TSRMLS_CC); } +; + %% /* Copy to YYRES the contents of YYSTR after stripping away unnecessary diff --git a/Zend/zend_object_handlers.c b/Zend/zend_object_handlers.c index 20d4e124c5..176d451e05 100644 --- a/Zend/zend_object_handlers.c +++ b/Zend/zend_object_handlers.c @@ -394,6 +394,16 @@ static int zend_get_property_guard(zend_object *zobj, zend_property_info *proper info.name = Z_STRVAL_P(member); info.name_length = Z_STRLEN_P(member); info.h = zend_get_hash_value(Z_STRVAL_P(member), Z_STRLEN_P(member) + 1); + } else if(property_info->name[0] == '\0'){ + const char *class_name = NULL, *prop_name = NULL; + zend_unmangle_property_name(property_info->name, property_info->name_length, &class_name, &prop_name); + if(class_name) { + /* use unmangled name for protected properties */ + info.name = prop_name; + info.name_length = strlen(prop_name); + info.h = zend_get_hash_value(info.name, info.name_length+1); + property_info = &info; + } } if (!zobj->guards) { ALLOC_HASHTABLE(zobj->guards); diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 3f9cc126ed..4dd544e578 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -974,27 +974,15 @@ ZEND_VM_HANDLER(40, ZEND_ECHO, CONST|TMP|VAR|CV, ANY) { USE_OPLINE zend_free_op free_op1; - zval z_copy; zval *z; SAVE_OPLINE(); z = GET_OP1_ZVAL_PTR(BP_VAR_R); - if (OP1_TYPE != IS_CONST && - UNEXPECTED(Z_TYPE_P(z) == IS_OBJECT) && - Z_OBJ_HT_P(z)->get_method != NULL) { - if (OP1_TYPE == IS_TMP_VAR) { - INIT_PZVAL(z); - } - if (zend_std_cast_object_tostring(z, &z_copy, IS_STRING TSRMLS_CC) == SUCCESS) { - zend_print_variable(&z_copy); - zval_dtor(&z_copy); - } else { - zend_print_variable(z); - } - } else { - zend_print_variable(z); + if (OP1_TYPE == IS_TMP_VAR && Z_TYPE_P(z) == IS_OBJECT) { + INIT_PZVAL(z); } + zend_print_variable(z); FREE_OP1(); CHECK_EXCEPTION(); @@ -3563,6 +3551,9 @@ ZEND_VM_HANDLER(99, ZEND_FETCH_CONSTANT, VAR|CONST|UNUSED, CONST) } ZVAL_COPY_VALUE(&EX_T(opline->result.var).tmp_var, *value); zval_copy_ctor(&EX_T(opline->result.var).tmp_var); + } else if (Z_STRLEN_P(opline->op2.zv) == sizeof("class")-1 && strcmp(Z_STRVAL_P(opline->op2.zv), "class") == 0) { + /* "class" is assigned as a case-sensitive keyword from zend_do_resolve_class_name */ + ZVAL_STRINGL(&EX_T(opline->result.var).tmp_var, ce->name, ce->name_length, 1); } else { zend_error_noreturn(E_ERROR, "Undefined class constant '%s'", Z_STRVAL_P(opline->op2.zv)); } diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 614249f993..25ac1ea89e 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -2091,27 +2091,15 @@ static int ZEND_FASTCALL ZEND_ECHO_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE - zval z_copy; zval *z; SAVE_OPLINE(); z = opline->op1.zv; - if (IS_CONST != IS_CONST && - UNEXPECTED(Z_TYPE_P(z) == IS_OBJECT) && - Z_OBJ_HT_P(z)->get_method != NULL) { - if (IS_CONST == IS_TMP_VAR) { - INIT_PZVAL(z); - } - if (zend_std_cast_object_tostring(z, &z_copy, IS_STRING TSRMLS_CC) == SUCCESS) { - zend_print_variable(&z_copy); - zval_dtor(&z_copy); - } else { - zend_print_variable(z); - } - } else { - zend_print_variable(z); + if (IS_CONST == IS_TMP_VAR && Z_TYPE_P(z) == IS_OBJECT) { + INIT_PZVAL(z); } + zend_print_variable(z); CHECK_EXCEPTION(); ZEND_VM_NEXT_OPCODE(); @@ -3731,6 +3719,9 @@ static int ZEND_FASTCALL ZEND_FETCH_CONSTANT_SPEC_CONST_CONST_HANDLER(ZEND_OPCO } ZVAL_COPY_VALUE(&EX_T(opline->result.var).tmp_var, *value); zval_copy_ctor(&EX_T(opline->result.var).tmp_var); + } else if (Z_STRLEN_P(opline->op2.zv) == sizeof("class")-1 && strcmp(Z_STRVAL_P(opline->op2.zv), "class") == 0) { + /* "class" is assigned as a case-sensitive keyword from zend_do_resolve_class_name */ + ZVAL_STRINGL(&EX_T(opline->result.var).tmp_var, ce->name, ce->name_length, 1); } else { zend_error_noreturn(E_ERROR, "Undefined class constant '%s'", Z_STRVAL_P(opline->op2.zv)); } @@ -7408,27 +7399,15 @@ static int ZEND_FASTCALL ZEND_ECHO_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE zend_free_op free_op1; - zval z_copy; zval *z; SAVE_OPLINE(); z = _get_zval_ptr_tmp(opline->op1.var, execute_data, &free_op1 TSRMLS_CC); - if (IS_TMP_VAR != IS_CONST && - UNEXPECTED(Z_TYPE_P(z) == IS_OBJECT) && - Z_OBJ_HT_P(z)->get_method != NULL) { - if (IS_TMP_VAR == IS_TMP_VAR) { - INIT_PZVAL(z); - } - if (zend_std_cast_object_tostring(z, &z_copy, IS_STRING TSRMLS_CC) == SUCCESS) { - zend_print_variable(&z_copy); - zval_dtor(&z_copy); - } else { - zend_print_variable(z); - } - } else { - zend_print_variable(z); + if (IS_TMP_VAR == IS_TMP_VAR && Z_TYPE_P(z) == IS_OBJECT) { + INIT_PZVAL(z); } + zend_print_variable(z); zval_dtor(free_op1.var); CHECK_EXCEPTION(); @@ -12619,27 +12598,15 @@ static int ZEND_FASTCALL ZEND_ECHO_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE zend_free_op free_op1; - zval z_copy; zval *z; SAVE_OPLINE(); z = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1 TSRMLS_CC); - if (IS_VAR != IS_CONST && - UNEXPECTED(Z_TYPE_P(z) == IS_OBJECT) && - Z_OBJ_HT_P(z)->get_method != NULL) { - if (IS_VAR == IS_TMP_VAR) { - INIT_PZVAL(z); - } - if (zend_std_cast_object_tostring(z, &z_copy, IS_STRING TSRMLS_CC) == SUCCESS) { - zend_print_variable(&z_copy); - zval_dtor(&z_copy); - } else { - zend_print_variable(z); - } - } else { - zend_print_variable(z); + if (IS_VAR == IS_TMP_VAR && Z_TYPE_P(z) == IS_OBJECT) { + INIT_PZVAL(z); } + zend_print_variable(z); if (free_op1.var) {zval_ptr_dtor(&free_op1.var);}; CHECK_EXCEPTION(); @@ -15595,6 +15562,9 @@ static int ZEND_FASTCALL ZEND_FETCH_CONSTANT_SPEC_VAR_CONST_HANDLER(ZEND_OPCODE } ZVAL_COPY_VALUE(&EX_T(opline->result.var).tmp_var, *value); zval_copy_ctor(&EX_T(opline->result.var).tmp_var); + } else if (Z_STRLEN_P(opline->op2.zv) == sizeof("class")-1 && strcmp(Z_STRVAL_P(opline->op2.zv), "class") == 0) { + /* "class" is assigned as a case-sensitive keyword from zend_do_resolve_class_name */ + ZVAL_STRINGL(&EX_T(opline->result.var).tmp_var, ce->name, ce->name_length, 1); } else { zend_error_noreturn(E_ERROR, "Undefined class constant '%s'", Z_STRVAL_P(opline->op2.zv)); } @@ -25170,6 +25140,9 @@ static int ZEND_FASTCALL ZEND_FETCH_CONSTANT_SPEC_UNUSED_CONST_HANDLER(ZEND_OPC } ZVAL_COPY_VALUE(&EX_T(opline->result.var).tmp_var, *value); zval_copy_ctor(&EX_T(opline->result.var).tmp_var); + } else if (Z_STRLEN_P(opline->op2.zv) == sizeof("class")-1 && strcmp(Z_STRVAL_P(opline->op2.zv), "class") == 0) { + /* "class" is assigned as a case-sensitive keyword from zend_do_resolve_class_name */ + ZVAL_STRINGL(&EX_T(opline->result.var).tmp_var, ce->name, ce->name_length, 1); } else { zend_error_noreturn(E_ERROR, "Undefined class constant '%s'", Z_STRVAL_P(opline->op2.zv)); } @@ -30205,27 +30178,15 @@ static int ZEND_FASTCALL ZEND_ECHO_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE - zval z_copy; zval *z; SAVE_OPLINE(); z = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var TSRMLS_CC); - if (IS_CV != IS_CONST && - UNEXPECTED(Z_TYPE_P(z) == IS_OBJECT) && - Z_OBJ_HT_P(z)->get_method != NULL) { - if (IS_CV == IS_TMP_VAR) { - INIT_PZVAL(z); - } - if (zend_std_cast_object_tostring(z, &z_copy, IS_STRING TSRMLS_CC) == SUCCESS) { - zend_print_variable(&z_copy); - zval_dtor(&z_copy); - } else { - zend_print_variable(z); - } - } else { - zend_print_variable(z); + if (IS_CV == IS_TMP_VAR && Z_TYPE_P(z) == IS_OBJECT) { + INIT_PZVAL(z); } + zend_print_variable(z); CHECK_EXCEPTION(); ZEND_VM_NEXT_OPCODE(); diff --git a/ext/curl/config.m4 b/ext/curl/config.m4 index 92559be7c4..e549330c50 100644 --- a/ext/curl/config.m4 +++ b/ext/curl/config.m4 @@ -149,6 +149,6 @@ int main(int argc, char *argv[]) AC_DEFINE(PHP_CURL_URL_WRAPPERS,1,[ ]) fi - PHP_NEW_EXTENSION(curl, interface.c multi.c share.c streams.c, $ext_shared) + PHP_NEW_EXTENSION(curl, interface.c multi.c share.c streams.c curl_file.c, $ext_shared) PHP_SUBST(CURL_SHARED_LIBADD) fi diff --git a/ext/curl/config.w32 b/ext/curl/config.w32 index a056845575..5acda7ec9e 100644 --- a/ext/curl/config.w32 +++ b/ext/curl/config.w32 @@ -13,7 +13,7 @@ if (PHP_CURL != "no") { && (((PHP_ZLIB=="no") && (CHECK_LIB("zlib_a.lib;zlib.lib", "curl", PHP_CURL))) || (PHP_ZLIB_SHARED && CHECK_LIB("zlib.lib", "curl", PHP_CURL)) || (PHP_ZLIB == "yes" && (!PHP_ZLIB_SHARED))) ) { - EXTENSION("curl", "interface.c multi.c share.c streams.c", true); + EXTENSION("curl", "interface.c multi.c share.c streams.c curl_file.c", true); AC_DEFINE('HAVE_CURL', 1, 'Have cURL library'); AC_DEFINE('HAVE_CURL_SSL', 1, 'Have SSL suppurt in cURL'); AC_DEFINE('HAVE_CURL_EASY_STRERROR', 1, 'Have curl_easy_strerror in cURL'); diff --git a/ext/curl/interface.c b/ext/curl/interface.c index 2e055811e3..07141485ab 100644 --- a/ext/curl/interface.c +++ b/ext/curl/interface.c @@ -317,7 +317,7 @@ ZEND_END_ARG_INFO() #if LIBCURL_VERSION_NUM >= 0x070c01 /* 7.12.1 */ ZEND_BEGIN_ARG_INFO(arginfo_curl_reset, 0) ZEND_ARG_INFO(0, ch) -ZEND_END_ARG_INFO() +ZEND_END_ARG_INFO() #endif #if LIBCURL_VERSION_NUM > 0x070f03 /* 7.15.4 */ @@ -403,6 +403,12 @@ ZEND_BEGIN_ARG_INFO(arginfo_curl_pause, 0) ZEND_ARG_INFO(0, bitmask) ZEND_END_ARG_INFO() #endif + +ZEND_BEGIN_ARG_INFO_EX(arginfo_curlfile_create, 0, 0, 1) + ZEND_ARG_INFO(0, filename) + ZEND_ARG_INFO(0, mimetype) + ZEND_ARG_INFO(0, postname) +ZEND_END_ARG_INFO() /* }}} */ /* {{{ curl_functions[] @@ -446,6 +452,7 @@ const zend_function_entry curl_functions[] = { PHP_FE(curl_share_init, arginfo_curl_share_init) PHP_FE(curl_share_close, arginfo_curl_share_close) PHP_FE(curl_share_setopt, arginfo_curl_share_setopt) + PHP_FE(curl_file_create, arginfo_curlfile_create) PHP_FE_END }; /* }}} */ @@ -526,7 +533,7 @@ PHP_MINFO_FUNCTION(curl) #endif #if LIBCURL_VERSION_NUM >= 0x071600 /* 7.22.0 */ {"NTLMWB", CURL_VERSION_NTLM_WB}, -#endif +#endif #if LIBCURL_VERSION_NUM >= 0x070a08 /* 7.10.8 */ {"SPNEGO", CURL_VERSION_SPNEGO}, #endif @@ -804,7 +811,7 @@ PHP_MINIT_FUNCTION(curl) REGISTER_CURL_CONSTANT(CURLINFO_STARTTRANSFER_TIME); REGISTER_CURL_CONSTANT(CURLINFO_TOTAL_TIME); - /* Other */ + /* Other */ REGISTER_CURL_CONSTANT(CURLMSG_DONE); REGISTER_CURL_CONSTANT(CURLVERSION_NOW); @@ -846,7 +853,7 @@ PHP_MINIT_FUNCTION(curl) REGISTER_CURL_CONSTANT(CURL_SSLVERSION_SSLv2); REGISTER_CURL_CONSTANT(CURL_SSLVERSION_SSLv3); REGISTER_CURL_CONSTANT(CURL_SSLVERSION_TLSv1); - + /* Curl TIMECOND constants (CURLOPT_TIMECONDITION) */ REGISTER_CURL_CONSTANT(CURL_TIMECOND_IFMODSINCE); REGISTER_CURL_CONSTANT(CURL_TIMECOND_IFUNMODSINCE); @@ -910,7 +917,7 @@ PHP_MINIT_FUNCTION(curl) #if LIBCURL_VERSION_NUM >= 0x070d00 /* Available since 7.13.0 */ REGISTER_CURL_CONSTANT(CURLOPT_FTP_ACCOUNT); -#endif +#endif #if LIBCURL_VERSION_NUM >= 0x070b02 /* Available since 7.11.2 */ REGISTER_CURL_CONSTANT(CURLOPT_TCP_NODELAY); @@ -918,7 +925,7 @@ PHP_MINIT_FUNCTION(curl) #if LIBCURL_VERSION_NUM >= 0x070c02 /* Available since 7.12.2 */ REGISTER_CURL_CONSTANT(CURLINFO_OS_ERRNO); -#endif +#endif #if LIBCURL_VERSION_NUM >= 0x070c03 /* Available since 7.12.3 */ REGISTER_CURL_CONSTANT(CURLINFO_NUM_CONNECTS); @@ -959,7 +966,7 @@ PHP_MINIT_FUNCTION(curl) REGISTER_CURL_CONSTANT(CURLOPT_FTP_ALTERNATIVE_TO_USER); REGISTER_CURL_CONSTANT(CURLOPT_MAX_RECV_SPEED_LARGE); REGISTER_CURL_CONSTANT(CURLOPT_MAX_SEND_SPEED_LARGE); -#endif +#endif #if LIBCURL_VERSION_NUM >= 0x071000 /* Available since 7.16.0 */ REGISTER_CURL_CONSTANT(CURLOPT_SSL_SESSIONID_CACHE); @@ -1003,7 +1010,7 @@ PHP_MINIT_FUNCTION(curl) REGISTER_CURL_CONSTANT(CURLUSESSL_CONTROL); REGISTER_CURL_CONSTANT(CURLUSESSL_NONE); REGISTER_CURL_CONSTANT(CURLUSESSL_TRY); -#endif +#endif #if LIBCURL_VERSION_NUM >= 0x071101 /* Available since 7.17.1 */ REGISTER_CURL_CONSTANT(CURLOPT_SSH_HOST_PUBLIC_KEY_MD5); @@ -1053,7 +1060,7 @@ PHP_MINIT_FUNCTION(curl) REGISTER_CURL_CONSTANT(CURLOPT_USERNAME); #endif -#if LIBCURL_VERSION_NUM >= 0x071303 /* Available since 7.19.3 */ +#if LIBCURL_VERSION_NUM >= 0x071303 /* Available since 7.19.3 */ REGISTER_CURL_CONSTANT(CURLAUTH_DIGEST_IE); #endif @@ -1135,7 +1142,7 @@ PHP_MINIT_FUNCTION(curl) REGISTER_CURL_CONSTANT(CURL_FNMATCHFUNC_FAIL); REGISTER_CURL_CONSTANT(CURL_FNMATCHFUNC_MATCH); REGISTER_CURL_CONSTANT(CURL_FNMATCHFUNC_NOMATCH); -#endif +#endif #if LIBCURL_VERSION_NUM >= 0x071502 /* Available since 7.21.2 */ REGISTER_CURL_CONSTANT(CURLPROTO_GOPHER); @@ -1187,6 +1194,7 @@ PHP_MINIT_FUNCTION(curl) #if CURLOPT_PASSWDFUNCTION != 0 REGISTER_CURL_CONSTANT(CURLOPT_PASSWDFUNCTION); #endif + REGISTER_CURL_CONSTANT(CURLOPT_SAFE_UPLOAD); #ifdef PHP_CURL_NEED_OPENSSL_TSL if (!CRYPTO_get_id_callback()) { @@ -1229,6 +1237,8 @@ PHP_MINIT_FUNCTION(curl) } #endif + curlfile_register_class(TSRMLS_C); + return SUCCESS; } /* }}} */ @@ -1275,7 +1285,7 @@ PHP_MSHUTDOWN_FUNCTION(curl) /* {{{ curl_write_nothing * Used as a work around. See _php_curl_close_ex */ -static size_t curl_write_nothing(char *data, size_t size, size_t nmemb, void *ctx) +static size_t curl_write_nothing(char *data, size_t size, size_t nmemb, void *ctx) { return size * nmemb; } @@ -1812,6 +1822,7 @@ static void alloc_curl_handle(php_curl **ch) zend_llist_init(&(*ch)->to_free->str, sizeof(char *), (llist_dtor_func_t) curl_free_string, 0); zend_llist_init(&(*ch)->to_free->slist, sizeof(struct curl_slist), (llist_dtor_func_t) curl_free_slist, 0); zend_llist_init(&(*ch)->to_free->post, sizeof(struct HttpPost), (llist_dtor_func_t) curl_free_post, 0); + (*ch)->safe_upload = 0; /* for now, for BC reason we allow unsafe API */ } /* }}} */ @@ -1833,7 +1844,7 @@ static void split_certinfo(char *string, zval *hash) split = strstr(s, "; "); if(split) *split = '\0'; - + key = s; tmp = memchr(key, '=', 64); if(tmp) { @@ -1853,13 +1864,13 @@ static void split_certinfo(char *string, zval *hash) static void create_certinfo(struct curl_certinfo *ci, zval *listcode TSRMLS_DC) { int i; - + if(ci) { zval *certhash = NULL; - + for(i=0; i<ci->num_of_certs; i++) { struct curl_slist *slist; - + MAKE_STD_ZVAL(certhash); array_init(certhash); for(slist = ci->certinfo[i]; slist; slist = slist->next) { @@ -1876,14 +1887,14 @@ static void create_certinfo(struct curl_certinfo *ci, zval *listcode TSRMLS_DC) MAKE_STD_ZVAL(hash); array_init(hash); - + split_certinfo(&slist->data[len+1], hash); add_assoc_zval(certhash, s, hash); } else { add_assoc_string(certhash, s, &slist->data[len+1], 1); } } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not extract hash key from certificate info"); + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not extract hash key from certificate info"); } } add_next_index_zval(listcode, certhash); @@ -2056,7 +2067,7 @@ PHP_FUNCTION(curl_copy_handle) if (ch->handlers->fnmatch->func_name) { zval_add_ref(&ch->handlers->fnmatch->func_name); dupch->handlers->fnmatch->func_name = ch->handlers->fnmatch->func_name; - } + } dupch->handlers->fnmatch->method = ch->handlers->fnmatch->method; curl_easy_setopt(dupch->cp, CURLOPT_FNMATCH_DATA, (void *) dupch); } @@ -2135,7 +2146,7 @@ static int _php_curl_setopt(php_curl *ch, long option, zval **zvalue, zval *retu #if LIBCURL_VERSION_NUM >= 0x070a06 /* Available since 7.10.6 */ case CURLOPT_HTTPAUTH: #endif -#if LIBCURL_VERSION_NUM >= 0x070a07 /* Available since 7.10.7 */ +#if LIBCURL_VERSION_NUM >= 0x070a07 /* Available since 7.10.7 */ case CURLOPT_FTP_CREATE_MISSING_DIRS: case CURLOPT_PROXYAUTH: #endif @@ -2185,11 +2196,11 @@ static int _php_curl_setopt(php_curl *ch, long option, zval **zvalue, zval *retu case CURLOPT_USE_SSL: #elif LIBCURL_VERSION_NUM >= 0x070b00 /* Available since 7.11.0 */ case CURLOPT_FTP_SSL: -#endif +#endif #if LIBCURL_VERSION_NUM >= 0x071100 /* Available since 7.17.0 */ case CURLOPT_APPEND: case CURLOPT_DIRLISTONLY: -#else +#else case CURLOPT_FTPAPPEND: case CURLOPT_FTPLISTONLY: #endif @@ -2247,6 +2258,10 @@ static int _php_curl_setopt(php_curl *ch, long option, zval **zvalue, zval *retu #endif error = curl_easy_setopt(ch->cp, option, Z_LVAL_PP(zvalue)); break; + case CURLOPT_SAFE_UPLOAD: + convert_to_long_ex(zvalue); + ch->safe_upload = (Z_LVAL_PP(zvalue) != 0); + break; /* String options */ case CURLOPT_CAINFO: @@ -2282,7 +2297,7 @@ static int _php_curl_setopt(php_curl *ch, long option, zval **zvalue, zval *retu #endif #if LIBCURL_VERSION_NUM >= 0x071004 /* Available since 7.16.4 */ case CURLOPT_KRBLEVEL: -#else +#else case CURLOPT_KRB4LEVEL: #endif #if LIBCURL_VERSION_NUM >= 0x071101 /* Available since 7.17.1 */ @@ -2307,7 +2322,7 @@ static int _php_curl_setopt(php_curl *ch, long option, zval **zvalue, zval *retu case CURLOPT_TLSAUTH_PASSWORD: case CURLOPT_TLSAUTH_USERNAME: #endif -#if LIBCURL_VERSION_NUM >= 0x071506 /* Available since 7.21.6 */ +#if LIBCURL_VERSION_NUM >= 0x071506 /* Available since 7.21.6 */ case CURLOPT_ACCEPT_ENCODING: case CURLOPT_TRANSFER_ENCODING: #else @@ -2315,7 +2330,7 @@ static int _php_curl_setopt(php_curl *ch, long option, zval **zvalue, zval *retu #endif #if LIBCURL_VERSION_NUM >= 0x071800 /* Available since 7.24.0 */ case CURLOPT_DNS_SERVERS: -#endif +#endif #if LIBCURL_VERSION_NUM >= 0x071900 /* Available since 7.25.0 */ case CURLOPT_MAIL_AUTH: #endif @@ -2339,8 +2354,8 @@ string_copy: #if LIBCURL_VERSION_NUM >= 0x071100 /* Strings passed to libcurl as ’char *’ arguments, are copied by the library... NOTE: before 7.17.0 strings were not copied. */ error = curl_easy_setopt(ch->cp, option, Z_STRVAL_PP(zvalue)); -#else - goto string_copy; +#else + goto string_copy; #endif } } @@ -2350,7 +2365,7 @@ string_copy: /* Curl file handle options */ case CURLOPT_FILE: case CURLOPT_INFILE: - case CURLOPT_STDERR: + case CURLOPT_STDERR: case CURLOPT_WRITEHEADER: { FILE *fp = NULL; int type; @@ -2560,9 +2575,6 @@ string_copy: ulong num_key; int numeric_key; - SEPARATE_ZVAL(current); - convert_to_string_ex(current); - zend_hash_get_current_key_ex(postfields, &string_key, &string_key_len, &num_key, 0, NULL); /* Pretend we have a string_key here */ @@ -2574,15 +2586,59 @@ string_copy: numeric_key = 0; } + if(Z_TYPE_PP(current) == IS_OBJECT && instanceof_function(Z_OBJCE_PP(current), curl_CURLFile_class TSRMLS_CC)) { + /* new-style file upload */ + zval *prop; + char *type = NULL, *filename = NULL; + + prop = zend_read_property(curl_CURLFile_class, *current, "name", sizeof("name")-1, 0 TSRMLS_CC); + if(Z_TYPE_P(prop) != IS_STRING) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid filename for key %s", string_key); + } else { + postval = Z_STRVAL_P(prop); + + if (php_check_open_basedir(postval TSRMLS_CC)) { + RETVAL_FALSE; + return 1; + } + + prop = zend_read_property(curl_CURLFile_class, *current, "mime", sizeof("mime")-1, 0 TSRMLS_CC); + if(Z_TYPE_P(prop) == IS_STRING && Z_STRLEN_P(prop) > 0) { + type = Z_STRVAL_P(prop); + } + prop = zend_read_property(curl_CURLFile_class, *current, "postname", sizeof("postname")-1, 0 TSRMLS_CC); + if(Z_TYPE_P(prop) == IS_STRING && Z_STRLEN_P(prop) > 0) { + filename = Z_STRVAL_P(prop); + } + error = curl_formadd(&first, &last, + CURLFORM_COPYNAME, string_key, + CURLFORM_NAMELENGTH, (long)string_key_len - 1, + CURLFORM_FILENAME, filename ? filename : postval, + CURLFORM_CONTENTTYPE, type ? type : "application/octet-stream", + CURLFORM_FILE, postval, + CURLFORM_END); + } + + if (numeric_key) { + efree(string_key); + } + continue; + } + + SEPARATE_ZVAL(current); + convert_to_string_ex(current); + postval = Z_STRVAL_PP(current); /* The arguments after _NAMELENGTH and _CONTENTSLENGTH * must be explicitly cast to long in curl_formadd * use since curl needs a long not an int. */ - if (*postval == '@') { + if (!ch->safe_upload && *postval == '@') { char *type, *filename; ++postval; + php_error_docref("curl.curlfile" TSRMLS_CC, E_DEPRECATED, "The usage of the @filename API for file uploading is deprecated. Please use the CURLFile class instead"); + if ((type = php_memnstr(postval, ";type=", sizeof(";type=") - 1, postval + Z_STRLEN_PP(current)))) { *type = '\0'; } @@ -2725,7 +2781,7 @@ string_copy: /* the following options deal with files, therefore the open_basedir check * is required. */ - case CURLOPT_COOKIEFILE: + case CURLOPT_COOKIEFILE: case CURLOPT_COOKIEJAR: case CURLOPT_RANDOM_FILE: case CURLOPT_SSLCERT: @@ -2829,7 +2885,7 @@ PHP_FUNCTION(curl_setopt) ZEND_FETCH_RESOURCE(ch, php_curl *, &zid, -1, le_curl_name, le_curl); - if (options <= 0) { + if (options <= 0 && options != CURLOPT_SAFE_UPLOAD) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid curl configuration option"); RETURN_FALSE; } @@ -3045,7 +3101,7 @@ PHP_FUNCTION(curl_getinfo) CAAS("redirect_url", s_code); } #endif -#if LIBCURL_VERSION_NUM >= 0x071300 /* Available since 7.19.0 */ +#if LIBCURL_VERSION_NUM >= 0x071300 /* Available since 7.19.0 */ if (curl_easy_getinfo(ch->cp, CURLINFO_PRIMARY_IP, &s_code) == CURLE_OK) { CAAS("primary_ip", s_code); } @@ -3085,7 +3141,7 @@ PHP_FUNCTION(curl_getinfo) struct curl_certinfo *ci = NULL; array_init(return_value); - + if (curl_easy_getinfo(ch->cp, CURLINFO_CERTINFO, &ci) == CURLE_OK) { create_certinfo(ci, return_value TSRMLS_CC); } else { @@ -3100,7 +3156,7 @@ PHP_FUNCTION(curl_getinfo) case CURLINFO_STRING: { char *s_code = NULL; - + if (curl_easy_getinfo(ch->cp, option, &s_code) == CURLE_OK && s_code) { RETURN_STRING(s_code, 1); } else { @@ -3111,7 +3167,7 @@ PHP_FUNCTION(curl_getinfo) case CURLINFO_LONG: { long code = 0; - + if (curl_easy_getinfo(ch->cp, option, &code) == CURLE_OK) { RETURN_LONG(code); } else { @@ -3221,16 +3277,16 @@ static void _php_curl_close_ex(php_curl *ch TSRMLS_DC) _php_curl_verify_handlers(ch, 0 TSRMLS_CC); - /* + /* * Libcurl is doing connection caching. When easy handle is cleaned up, - * if the handle was previously used by the curl_multi_api, the connection + * if the handle was previously used by the curl_multi_api, the connection * remains open un the curl multi handle is cleaned up. Some protocols are - * sending content like the FTP one, and libcurl try to use the + * sending content like the FTP one, and libcurl try to use the * WRITEFUNCTION or the HEADERFUNCTION. Since structures used in those * callback are freed, we need to use an other callback to which avoid * segfaults. * - * Libcurl commit d021f2e8a00 fix this issue and should be part of 7.28.2 + * Libcurl commit d021f2e8a00 fix this issue and should be part of 7.28.2 */ curl_easy_setopt(ch->cp, CURLOPT_HEADERFUNCTION, curl_write_nothing); curl_easy_setopt(ch->cp, CURLOPT_WRITEFUNCTION, curl_write_nothing); @@ -3349,7 +3405,7 @@ static void _php_curl_reset_handlers(php_curl *ch) } ch->handlers->write->fp = NULL; ch->handlers->write->method = PHP_CURL_STDOUT; - + if (ch->handlers->write_header->stream) { Z_DELREF_P(ch->handlers->write_header->stream); ch->handlers->write_header->stream = NULL; @@ -3443,7 +3499,7 @@ PHP_FUNCTION(curl_escape) /* {{{ proto void curl_unescape(resource ch, string str) URL decodes the given string */ -PHP_FUNCTION(curl_unescape) +PHP_FUNCTION(curl_unescape) { char *str = NULL, *out = NULL; int str_len = 0, out_len; @@ -3481,7 +3537,7 @@ PHP_FUNCTION(curl_pause) ZEND_FETCH_RESOURCE(ch, php_curl *, &zid, -1, le_curl_name, le_curl); - RETURN_LONG(curl_easy_pause(ch->cp, bitmask)); + RETURN_LONG(curl_easy_pause(ch->cp, bitmask)); } /* }}} */ #endif diff --git a/ext/curl/php_curl.h b/ext/curl/php_curl.h index a8c26c0528..c4222c0588 100644 --- a/ext/curl/php_curl.h +++ b/ext/curl/php_curl.h @@ -34,6 +34,14 @@ #define PHP_CURL_DEBUG 0 +#ifdef PHP_WIN32 +# define PHP_CURL_API __declspec(dllexport) +#elif defined(__GNUC__) && __GNUC__ >= 4 +# define PHP_CURL_API __attribute__ ((visibility("default"))) +#else +# define PHP_CURL_API +#endif + #include <curl/curl.h> #include <curl/multi.h> @@ -103,6 +111,8 @@ PHP_FUNCTION(curl_multi_setopt); #if LIBCURL_VERSION_NUM >= 0x071200 /* 7.18.0 */ PHP_FUNCTION(curl_pause); #endif +PHP_FUNCTION(curl_file_create); + void _php_curl_multi_close(zend_rsrc_list_entry * TSRMLS_DC); void _php_curl_share_close(zend_rsrc_list_entry * TSRMLS_DC); @@ -171,8 +181,11 @@ typedef struct { long id; zend_bool in_callback; zval *clone; + zend_bool safe_upload; } php_curl; +#define CURLOPT_SAFE_UPLOAD -1 + typedef struct { int still_running; CURLM *multi; @@ -211,7 +224,7 @@ typedef struct { fd_set readfds, writefds, excfds; int maxfd; - + char errstr[CURL_ERROR_SIZE + 1]; CURLMcode mcode; int pending; @@ -219,6 +232,8 @@ typedef struct { struct curl_slist *headers_slist; /* holds custom headers sent out in the request */ } php_curl_stream; +void curlfile_register_class(TSRMLS_D); +PHP_CURL_API extern zend_class_entry *curl_CURLFile_class; #else #define curl_module_ptr NULL diff --git a/ext/curl/tests/bug27023.phpt b/ext/curl/tests/bug27023.phpt index b738c956e9..62effec990 100644 --- a/ext/curl/tests/bug27023.phpt +++ b/ext/curl/tests/bug27023.phpt @@ -1,5 +1,7 @@ --TEST-- Bug #27023 (CURLOPT_POSTFIELDS does not parse content types for files) +--INI-- +error_reporting = E_ALL & ~E_DEPRECATED --SKIPIF-- <?php if (!extension_loaded("curl")) { diff --git a/ext/curl/tests/curl_file_serialize.phpt b/ext/curl/tests/curl_file_serialize.phpt new file mode 100644 index 0000000000..43b272ad64 --- /dev/null +++ b/ext/curl/tests/curl_file_serialize.phpt @@ -0,0 +1,21 @@ +--TEST-- +CURL file uploading +--SKIPIF-- +<?php +if (!extension_loaded("curl")) { + exit("skip curl extension not loaded"); +} +?> +--FILE-- +<?php +$data = 'a:2:{s:4:"file";O:8:"CURLFile":3:{s:4:"name";s:13:"testdata1.txt";s:4:"mime";s:0:"";s:8:"postname";s:0:"";}s:4:"data";s:3:"foo";}'; +var_dump(unserialize($data)); +?> +--EXPECTF-- +Fatal error: Uncaught exception 'Exception' with message 'Unserialization of CURLFile instances is not allowed' in %s +Stack trace: +#0 [internal function]: CURLFile->__wakeup() +#1 %s +#2 {main} + thrown in %s on line %d + diff --git a/ext/curl/tests/curl_file_upload.phpt b/ext/curl/tests/curl_file_upload.phpt new file mode 100644 index 0000000000..d3168e578a --- /dev/null +++ b/ext/curl/tests/curl_file_upload.phpt @@ -0,0 +1,85 @@ +--TEST-- +CURL file uploading +--SKIPIF-- +<?php +if (!extension_loaded("curl")) { + exit("skip curl extension not loaded"); +} +if (false === getenv('PHP_CURL_HTTP_REMOTE_SERVER')) { + exit("skip PHP_CURL_HTTP_REMOTE_SERVER env variable is not defined"); +} +?> +--FILE-- +<?php + +function testcurl($ch, $name, $mime = '', $postname = '') +{ + if(!empty($postname)) { + $file = new CurlFile($name, $mime, $postname); + } else if(!empty($mime)) { + $file = new CurlFile($name, $mime); + } else { + $file = new CurlFile($name); + } + curl_setopt($ch, CURLOPT_POSTFIELDS, array("file" => $file)); + var_dump(curl_exec($ch)); +} + +$host = getenv('PHP_CURL_HTTP_REMOTE_SERVER'); +$ch = curl_init(); +curl_setopt($ch, CURLOPT_URL, "{$host}/get.php?test=file"); +curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + +testcurl($ch, __DIR__ . '/curl_testdata1.txt'); +testcurl($ch, __DIR__ . '/curl_testdata1.txt', 'text/plain'); +testcurl($ch, __DIR__ . '/curl_testdata1.txt', '', 'foo.txt'); +testcurl($ch, __DIR__ . '/curl_testdata1.txt', 'text/plain', 'foo.txt'); + +$file = new CurlFile(__DIR__ . '/curl_testdata1.txt'); +$file->setMimeType('text/plain'); +var_dump($file->getMimeType()); +var_dump($file->getFilename()); +curl_setopt($ch, CURLOPT_POSTFIELDS, array("file" => $file)); +var_dump(curl_exec($ch)); + +$file = curl_file_create(__DIR__ . '/curl_testdata1.txt'); +$file->setPostFilename('foo.txt'); +var_dump($file->getPostFilename()); +curl_setopt($ch, CURLOPT_POSTFIELDS, array("file" => $file)); +var_dump(curl_exec($ch)); + +$params = array('file' => '@' . __DIR__ . '/curl_testdata1.txt'); +curl_setopt($ch, CURLOPT_POSTFIELDS, $params); +var_dump(curl_exec($ch)); + +curl_setopt($ch, CURLOPT_SAFE_UPLOAD, true); +$params = array('file' => '@' . __DIR__ . '/curl_testdata1.txt'); +curl_setopt($ch, CURLOPT_POSTFIELDS, $params); +var_dump(curl_exec($ch)); + +curl_setopt($ch, CURLOPT_URL, "{$host}/get.php?test=post"); +$params = array('file' => '@' . __DIR__ . '/curl_testdata1.txt'); +curl_setopt($ch, CURLOPT_POSTFIELDS, $params); +var_dump(curl_exec($ch)); + +curl_close($ch); +?> +--EXPECTF-- +string(%d) "curl_testdata1.txt|application/octet-stream" +string(%d) "curl_testdata1.txt|text/plain" +string(%d) "foo.txt|application/octet-stream" +string(%d) "foo.txt|text/plain" +string(%d) "text/plain" +string(%d) "%s/curl_testdata1.txt" +string(%d) "curl_testdata1.txt|text/plain" +string(%d) "foo.txt" +string(%d) "foo.txt|application/octet-stream" + +Deprecated: curl_setopt(): The usage of the @filename API for file uploading is deprecated. Please use the CURLFile class instead in %s on line %d +string(%d) "curl_testdata1.txt|application/octet-stream" +string(0) "" +string(%d) "array(1) { + ["file"]=> + string(%d) "@%s/curl_testdata1.txt" +} +" diff --git a/ext/curl/tests/curl_setopt_error.phpt b/ext/curl/tests/curl_setopt_error.phpt index ad73318f1c..01593aff22 100644 --- a/ext/curl/tests/curl_setopt_error.phpt +++ b/ext/curl/tests/curl_setopt_error.phpt @@ -14,14 +14,14 @@ curl_setopt(false); curl_setopt($ch); curl_setopt($ch, false); -curl_setopt($ch, -1); +curl_setopt($ch, -10); curl_setopt($ch, ''); curl_setopt($ch, 1, false); curl_setopt(false, false, false); curl_setopt($ch, '', false); curl_setopt($ch, 1, ''); -curl_setopt($ch, -1, 0); +curl_setopt($ch, -10, 0); ?> --EXPECTF-- *** curl_setopt() call with incorrect parameters diff --git a/ext/date/php_date.c b/ext/date/php_date.c index 54dc2f5b70..f4115dc7e1 100644 --- a/ext/date/php_date.c +++ b/ext/date/php_date.c @@ -2883,14 +2883,18 @@ PHP_FUNCTION(date_format) } /* }}} */ -static void php_date_modify(zval *object, char *modify, int modify_len, zval *return_value TSRMLS_DC) +static int php_date_modify(zval *object, char *modify, int modify_len TSRMLS_DC) { php_date_obj *dateobj; timelib_time *tmp_time; timelib_error_container *err = NULL; dateobj = (php_date_obj *) zend_object_store_get_object(object TSRMLS_CC); - DATE_CHECK_INITIALIZED(dateobj->time, DateTime); + + if (!(dateobj->time)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "The DateTime object has not been correctly initialized by its constructor"); + return 0; + } tmp_time = timelib_strtotime(modify, modify_len, &err, DATE_TIMEZONEDB, php_date_parse_tzfile_wrapper); @@ -2901,7 +2905,7 @@ static void php_date_modify(zval *object, char *modify, int modify_len, zval *re php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to parse time string (%s) at position %d (%c): %s", modify, err->error_messages[0].position, err->error_messages[0].character, err->error_messages[0].message); timelib_time_dtor(tmp_time); - RETURN_FALSE; + return 0; } memcpy(&dateobj->time->relative, &tmp_time->relative, sizeof(struct timelib_rel_time)); @@ -2937,6 +2941,8 @@ static void php_date_modify(zval *object, char *modify, int modify_len, zval *re timelib_update_ts(dateobj->time, NULL); timelib_update_from_sse(dateobj->time); dateobj->time->have_relative = 0; + + return 1; } /* {{{ proto DateTime date_modify(DateTime object, string modify) @@ -2952,9 +2958,11 @@ PHP_FUNCTION(date_modify) RETURN_FALSE; } - php_date_modify(object, modify, modify_len, return_value TSRMLS_CC); + if (php_date_modify(object, modify, modify_len TSRMLS_CC)) { + RETURN_ZVAL(object, 1, 0); + } - RETURN_ZVAL(object, 1, 0); + RETURN_FALSE; } /* }}} */ @@ -2971,9 +2979,11 @@ PHP_METHOD(DateTimeImmutable, modify) } new_object = date_clone_immutable(object TSRMLS_CC); - php_date_modify(new_object, modify, modify_len, return_value TSRMLS_CC); + if (php_date_modify(new_object, modify, modify_len TSRMLS_CC)) { + RETURN_ZVAL(new_object, 0, 1); + } - RETURN_ZVAL(new_object, 0, 1); + RETURN_FALSE; } /* }}} */ diff --git a/ext/gd/config.w32 b/ext/gd/config.w32 index 8c932a037c..22584ddd46 100644 --- a/ext/gd/config.w32 +++ b/ext/gd/config.w32 @@ -11,7 +11,8 @@ if (PHP_GD != "no") { CHECK_LIB("freetype_a.lib;freetype.lib", "gd", PHP_GD) && CHECK_LIB("libpng_a.lib;libpng.lib", "gd", PHP_GD) && CHECK_HEADER_ADD_INCLUDE("gd.h", "CFLAGS_GD", PHP_GD + ";ext\\gd\\libgd") && - CHECK_HEADER_ADD_INCLUDE("png.h", "CFLAGS_GD", PHP_GD + ";" + PHP_PHP_BUILD + "\\include\\libpng12") && + (CHECK_HEADER_ADD_INCLUDE("png.h", "CFLAGS_GD", PHP_GD + ";" + PHP_PHP_BUILD + "\\include\\libpng15") || + CHECK_HEADER_ADD_INCLUDE("png.h", "CFLAGS_GD", PHP_GD + ";" + PHP_PHP_BUILD + "\\include\\libpng12")) && (CHECK_LIB("libiconv_a.lib;libiconv.lib", "gd", PHP_GD) || CHECK_LIB("iconv_a.lib;iconv.lib", "gd", PHP_GD)) && CHECK_HEADER_ADD_INCLUDE("iconv.h", "CFLAGS_GD", PHP_GD) && (((PHP_ZLIB=="no") && (CHECK_LIB("zlib_a.lib;zlib.lib", "gd", PHP_GD) )) || diff --git a/ext/intl/ERROR.CONVENTIONS b/ext/intl/ERROR.CONVENTIONS new file mode 100644 index 0000000000..6f9079c56d --- /dev/null +++ b/ext/intl/ERROR.CONVENTIONS @@ -0,0 +1,115 @@ +The intl extension has particular conventions regarding error reporting. +These conventions are enumerated in this document. + +:: The last error is always stored globally. + +The global error code can be obtained in userland with intl_get_error_code(). +This is a U_* error code defined by ICU, but it does not have necessarily to be +returned obtained after a call to an ICU function. That is to say, the internal +PHP wrapper functions can set these error codes when appropriate. For instance, +in response to bad arguments (e.g. zend_parse_parameters() failure), the PHP +wrapper function should set the global error code to U_ILLEGAL_ARGUMENT_ERROR). + +The error code (an integer) can be converter to the corresponding enum name +string in userland with intl_error_name(). + +The associated message can be obtained with intl_get_error_message(). This is a +message set by the PHP wrapping code, not by ICU. The message should include the +name of the function that failed in order to make debugging easier (though if +you activate warnings with intl.error_level or exceptions with +intl.use_exceptions you get more fine-grained information about where the +error ocurred). + +The internal PHP code can set the global last error with: +void intl_error_set_code(intl_error* err, UErrorCode err_code TSRMLS_DC); +void intl_error_set_custom_msg(intl_error* err, char* msg, int copyMsg TSRMLS_DC); +void intl_error_set(intl_error* err, UErrorCode code, char* msg, int copyMsg TSRMLS_DC); + +and by passing NULL as the first parameter. The last function is a combination +of the first two. If the message is not a static buffer, copyMsg should be 1. +This makes the message string be copied and freed when no longer needed. There's +no way to pass ownership of the string without it being copied. + + +:: The last is ALSO stored in the object whose method call triggered the error, + unless the error is due to bad arguments, in which case only the global error + should be set + +Objects store an intl_error structed in their private data. For instance: +typedef struct { + zend_object zo; + intl_error err; + Calendar* ucal; +} Calendar_object; + +The global error and the object error can be SIMULTANEOUSLY set with these +functions: +void intl_errors_set_custom_msg(intl_error* err, char* msg, int copyMsg TSRMLS_DC); +void intl_errors_set_code(intl_error* err, UErrorCode err_code TSRMLS_DC); +void intl_errors_set(intl_error* err, UErrorCode code, char* msg, int copyMsg TSRMLS_DC); + +by passing a pointer to the object's intl_error structed as the first parameter. +Node the extra 's' in the functions' names ('errors', not 'error'). + +Static methods should only set the global error. + + +:: Intl classes that can be instantiated should provide ::getErrorCode() and + getErrorMessage() methods + +These methods are used to retrieve the error codes stored in the object's +private intl_error structured and mirror the global intl_get_error_code() and +intl_get_error_message(). + + +:: Intl methods and functions should return FALSE on error (even argument + parsing errors), not NULL. Constructors and factory methods are the + exception; these should return NULL, not FALSE. + +Not that constructors in Intl generally (always?) don't throws exceptions. +They instead destroy the object to that the result of new IntlClass() can +be NULL. This may be surprising. + + +:: Intl functions and methods should reset the global error before doing + anything else (even parse the arguments); instance methods should also reset + the object's private error + +Errors should be lost after a function call. This is different from the way +ICU operates, where functions return immediately if an error is set. + +Error resetting can be done with: +void intl_error_reset(NULL TSRMLS_DC); /* reset global error */ +void intl_errors_reset(intl_error* err TSRMLS_DC ); /* reset global and object error */ + +In practice, intl_errors_reset() is not used because most classes have also +plain functions mapped to the same internal functions as their instance methods. +Fetching of the object is done with zend_parse_method_parameters() instead of +directly using getThis(). Therefore, no reference to object is obtained until +the arguments are fully parsed. Without a reference to the object, there's no +way to reset the object's internal error code. Instead, resetting of the +object's internal error code is done upon fetching the object from its zval. + +Example: +U_CFUNC PHP_FUNCTION(breakiter_set_text) +{ + /* ... variable declations ... */ + BREAKITER_METHOD_INIT_VARS; /* macro also resets global error */ + object = getThis(); + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", + &text, &text_len) == FAILURE) { + intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR, + "breakiter_set_text: bad arguments", 0 TSRMLS_CC); + RETURN_FALSE; + } + + /* ... */ + + BREAKITER_METHOD_FETCH_OBJECT; /* macro also resets object's error */ + + /* ... */ +} + +Implementations of ::getErrorCode() and ::getErrorMessage() should not reset the +object's error code. diff --git a/ext/intl/breakiterator/breakiterator_class.cpp b/ext/intl/breakiterator/breakiterator_class.cpp index de4bfbb7b0..7bf271a344 100644 --- a/ext/intl/breakiterator/breakiterator_class.cpp +++ b/ext/intl/breakiterator/breakiterator_class.cpp @@ -296,7 +296,7 @@ static const zend_function_entry BreakIterator_class_functions[] = { PHP_ME_MAPPING(following, breakiter_following, ainfo_biter_offset, ZEND_ACC_PUBLIC) PHP_ME_MAPPING(preceding, breakiter_preceding, ainfo_biter_offset, ZEND_ACC_PUBLIC) PHP_ME_MAPPING(isBoundary, breakiter_is_boundary, ainfo_biter_offset, ZEND_ACC_PUBLIC) - PHP_ME_MAPPING(getLocale, breakiter_get_locale, ainfo_biter_void, ZEND_ACC_PUBLIC) + PHP_ME_MAPPING(getLocale, breakiter_get_locale, ainfo_biter_get_locale, ZEND_ACC_PUBLIC) PHP_ME_MAPPING(getPartsIterator, breakiter_get_parts_iterator, ainfo_biter_getPartsIterator, ZEND_ACC_PUBLIC) PHP_ME_MAPPING(getErrorCode, breakiter_get_error_code, ainfo_biter_void, ZEND_ACC_PUBLIC) diff --git a/ext/intl/calendar/calendar_methods.cpp b/ext/intl/calendar/calendar_methods.cpp index f2758fdcc8..2d33bd1952 100644 --- a/ext/intl/calendar/calendar_methods.cpp +++ b/ext/intl/calendar/calendar_methods.cpp @@ -782,7 +782,7 @@ U_CFUNC PHP_FUNCTION(intlcal_get_time_zone) TimeZone *tz = co->ucal->getTimeZone().clone(); if (tz == NULL) { - intl_error_set(NULL, U_MEMORY_ALLOCATION_ERROR, + intl_errors_set(CALENDAR_ERROR_P(co), U_MEMORY_ALLOCATION_ERROR, "intlcal_get_time_zone: could not clone TimeZone", 0 TSRMLS_CC); RETURN_FALSE; } diff --git a/ext/intl/config.m4 b/ext/intl/config.m4 index 7c95c130f2..4630a302ef 100644 --- a/ext/intl/config.m4 +++ b/ext/intl/config.m4 @@ -34,6 +34,7 @@ if test "$PHP_INTL" != "no"; then common/common_error.c \ common/common_enum.cpp \ common/common_date.cpp \ + converter/converter.c \ formatter/formatter.c \ formatter/formatter_main.c \ formatter/formatter_class.c \ @@ -86,6 +87,7 @@ if test "$PHP_INTL" != "no"; then idn/idn.c \ $icu_spoof_src, $ext_shared,,$ICU_INCS -Wno-write-strings) PHP_ADD_BUILD_DIR($ext_builddir/collator) + PHP_ADD_BUILD_DIR($ext_builddir/converter) PHP_ADD_BUILD_DIR($ext_builddir/common) PHP_ADD_BUILD_DIR($ext_builddir/formatter) PHP_ADD_BUILD_DIR($ext_builddir/normalizer) diff --git a/ext/intl/config.w32 b/ext/intl/config.w32 index a49918794c..bb1dca8124 100644 --- a/ext/intl/config.w32 +++ b/ext/intl/config.w32 @@ -26,6 +26,9 @@ if (PHP_INTL != "no") { common_enum.cpp \ common_date.cpp \ ", "intl"); + ADD_SOURCES(configure_module_dirname + "/converter", "\ + converter.c \ + ", "intl"); ADD_SOURCES(configure_module_dirname + "/formatter", "\ formatter.c \ formatter_attr.c \ diff --git a/ext/intl/converter/converter.c b/ext/intl/converter/converter.c new file mode 100644 index 0000000000..387760a9d1 --- /dev/null +++ b/ext/intl/converter/converter.c @@ -0,0 +1,1173 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | 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 | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Sara Golemon <pollita@php.net> | + +----------------------------------------------------------------------+ + */ + +#include "converter.h" +#include "zend_exceptions.h" + +#include <unicode/utypes.h> +#include <unicode/ucnv.h> +#include <unicode/ustring.h> + +#include "ext/intl/intl_error.h" + +typedef struct _php_converter_object { + zend_object obj; +#ifdef ZTS + void ***tsrm_ls; +#endif + UConverter *src, *dest; + zend_fcall_info to_cb, from_cb; + zend_fcall_info_cache to_cache, from_cache; + intl_error error; +} php_converter_object; + +static zend_class_entry *php_converter_ce; +static zend_object_handlers php_converter_object_handlers; + +#define CONV_GET(pzv) ((php_converter_object*)zend_objects_get_address((pzv) TSRMLS_CC)) +#define THROW_UFAILURE(obj, fname, error) php_converter_throw_failure(obj, error TSRMLS_CC, \ + fname "() returned error %ld: %s", (long)error, u_errorName(error)) + +/* {{{ php_converter_throw_failure */ +static inline void php_converter_throw_failure(php_converter_object *objval, UErrorCode error TSRMLS_DC, const char *format, ...) { + intl_error *err = objval ? &(objval->error) : NULL; + char message[1024]; + va_list vargs; + + va_start(vargs, format); + vsnprintf(message, sizeof(message), format, vargs); + va_end(vargs); + + intl_errors_set(err, error, message, 1 TSRMLS_CC); +} +/* }}} */ + +/* {{{ php_converter_default_callback */ +static void php_converter_default_callback(zval *return_value, zval *zobj, long reason, zval *error TSRMLS_DC) { + /* Basic functionality so children can call parent::toUCallback() */ + switch (reason) { + case UCNV_UNASSIGNED: + case UCNV_ILLEGAL: + case UCNV_IRREGULAR: + { + php_converter_object *objval = (php_converter_object*)CONV_GET(zobj); + char chars[127]; + int8_t chars_len = sizeof(chars); + UErrorCode error = U_ZERO_ERROR; + + /* Yes, this is fairly wasteful at first glance, + * but considering that the alternative is to store + * what's sent into setSubstChars() and the fact + * that this is an extremely unlikely codepath + * I'd rather take the CPU hit here, than waste time + * storing a value I'm unlikely to use. + */ + ucnv_getSubstChars(objval->src, chars, &chars_len, &error); + if (U_FAILURE(error)) { + THROW_UFAILURE(objval, "ucnv_getSubstChars", error); + chars[0] = 0x1A; + chars[1] = 0; + chars_len = 1; + } + RETVAL_STRINGL(chars, chars_len, 1); + } + } + zval_dtor(error); + ZVAL_LONG(error, U_ZERO_ERROR); +} +/* }}} */ + +/* {{{ proto void UConverter::toUCallback(long $reason, + string $source, string $codeUnits, + long &$error) */ +ZEND_BEGIN_ARG_INFO_EX(php_converter_toUCallback_arginfo, 0, ZEND_RETURN_VALUE, 4) + ZEND_ARG_INFO(0, reason) + ZEND_ARG_INFO(0, source) + ZEND_ARG_INFO(0, codeUnits) + ZEND_ARG_INFO(1, error) +ZEND_END_ARG_INFO(); +static PHP_METHOD(UConverter, toUCallback) { + long reason; + zval *source, *codeUnits, *error; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "lzzz", + &reason, &source, &codeUnits, &error) == FAILURE) { + return; + } + + php_converter_default_callback(return_value, getThis(), reason, error TSRMLS_CC); +} +/* }}} */ + +/* {{{ proto void UConverter::fromUCallback(long $reason, + Array $source, long $codePoint, + long &$error) */ +ZEND_BEGIN_ARG_INFO_EX(php_converter_fromUCallback_arginfo, 0, ZEND_RETURN_VALUE, 4) + ZEND_ARG_INFO(0, reason) + ZEND_ARG_INFO(0, source) + ZEND_ARG_INFO(0, codePoint) + ZEND_ARG_INFO(1, error) +ZEND_END_ARG_INFO(); +static PHP_METHOD(UConverter, fromUCallback) { + long reason; + zval *source, *codePoint, *error; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "lzzz", + &reason, &source, &codePoint, &error) == FAILURE) { + return; + } + + php_converter_default_callback(return_value, getThis(), reason, error TSRMLS_CC); +} +/* }}} */ + +/* {{{ php_converter_check_limits */ +static inline zend_bool php_converter_check_limits(php_converter_object *objval, long available, long needed TSRMLS_DC) { + if (available < needed) { + php_converter_throw_failure(objval, U_BUFFER_OVERFLOW_ERROR TSRMLS_CC, "Buffer overrun %ld bytes needed, %ld available", needed, available); + return 0; + } + return 1; +} +/* }}} */ + +#define TARGET_CHECK(cnvargs, needed) php_converter_check_limits(objval, cnvargs->targetLimit - cnvargs->target, needed TSRMLS_CC) + +/* {{{ php_converter_append_toUnicode_target */ +static void php_converter_append_toUnicode_target(zval *val, UConverterToUnicodeArgs *args, php_converter_object *objval TSRMLS_DC) { + switch (Z_TYPE_P(val)) { + case IS_NULL: + /* Code unit is being skipped */ + return; + case IS_LONG: + { + long lval = Z_LVAL_P(val); + if ((lval < 0) || (lval > 0x10FFFF)) { + php_converter_throw_failure(objval, U_ILLEGAL_ARGUMENT_ERROR TSRMLS_CC, "Invalid codepoint U+%04lx", lval); + return; + } + if (lval > 0xFFFF) { + /* Supplemental planes U+010000 - U+10FFFF */ + if (TARGET_CHECK(args, 2)) { + /* TODO: Find the ICU call which does this properly */ + *(args->target++) = (UChar)(((lval - 0x10000) >> 10) | 0xD800); + *(args->target++) = (UChar)(((lval - 0x10000) & 0x3FF) | 0xDC00); + } + return; + } + /* Non-suggogate BMP codepoint */ + if (TARGET_CHECK(args, 1)) { + *(args->target++) = (UChar)lval; + } + return; + } + case IS_STRING: + { + const char *strval = Z_STRVAL_P(val); + int i = 0, strlen = Z_STRLEN_P(val); + + while((i != strlen) && TARGET_CHECK(args, 1)) { + UChar c; + U8_NEXT(strval, i, strlen, c); + *(args->target++) = c; + } + return; + } + case IS_ARRAY: + { + HashTable *ht = Z_ARRVAL_P(val); + HashPosition pos; + zval **tmpzval; + + for(zend_hash_internal_pointer_reset_ex(ht, &pos); + zend_hash_get_current_data_ex(ht, (void**)&tmpzval, &pos) == SUCCESS; + zend_hash_move_forward_ex(ht, &pos)) { + php_converter_append_toUnicode_target(*tmpzval, args, objval TSRMLS_CC); + } + return; + } + default: + php_converter_throw_failure(objval, U_ILLEGAL_ARGUMENT_ERROR TSRMLS_CC, + "toUCallback() specified illegal type for substitution character"); + } +} +/* }}} */ + +/* {{{ php_converter_to_u_callback */ +static void php_converter_to_u_callback(const void *context, + UConverterToUnicodeArgs *args, + const char *codeUnits, int32_t length, + UConverterCallbackReason reason, + UErrorCode *pErrorCode) { + php_converter_object *objval = (php_converter_object*)context; + zval *zreason, *zsource, *zcodeunits, *zerror, *retval = NULL; + zval **zargs[4]; +#ifdef ZTS + TSRMLS_D = objval->tsrm_ls; +#endif + + MAKE_STD_ZVAL(zreason); + ZVAL_LONG(zreason, reason); + zargs[0] = &zreason; + + MAKE_STD_ZVAL(zsource); + ZVAL_STRINGL(zsource, args->source, args->sourceLimit - args->source, 1); + zargs[1] = &zsource; + + MAKE_STD_ZVAL(zcodeunits); + ZVAL_STRINGL(zcodeunits, codeUnits, length, 1); + zargs[2] = &zcodeunits; + + MAKE_STD_ZVAL(zerror); + ZVAL_LONG(zerror, *pErrorCode); + zargs[3] = &zerror; + + objval->to_cb.param_count = 4; + objval->to_cb.params = zargs; + objval->to_cb.retval_ptr_ptr = &retval; + objval->to_cb.no_separation = 0; + if (zend_call_function(&(objval->to_cb), &(objval->to_cache) TSRMLS_CC) == FAILURE) { + /* Unlikely */ + php_converter_throw_failure(objval, U_INTERNAL_PROGRAM_ERROR TSRMLS_CC, "Unexpected failure calling toUCallback()"); + } else if (retval) { + php_converter_append_toUnicode_target(retval, args, objval TSRMLS_CC); + zval_ptr_dtor(&retval); + } + + if (Z_TYPE_P(zerror) == IS_LONG) { + *pErrorCode = Z_LVAL_P(zerror); + } + + zval_ptr_dtor(&zreason); + zval_ptr_dtor(&zsource); + zval_ptr_dtor(&zcodeunits); + zval_ptr_dtor(&zerror); +} +/* }}} */ + +/* {{{ php_converter_append_fromUnicode_target */ +static void php_converter_append_fromUnicode_target(zval *val, UConverterFromUnicodeArgs *args, php_converter_object *objval TSRMLS_DC) { + switch (Z_TYPE_P(val)) { + case IS_NULL: + /* Ignore */ + return; + case IS_LONG: + if (TARGET_CHECK(args, 1)) { + *(args->target++) = Z_LVAL_P(val); + } + return; + case IS_STRING: + { + int vallen = Z_STRLEN_P(val); + if (TARGET_CHECK(args, vallen)) { + memcpy(args->target, Z_STRVAL_P(val), vallen); + args->target += vallen; + } + return; + } + case IS_ARRAY: + { + HashTable *ht = Z_ARRVAL_P(val); + HashPosition pos; + zval **tmpzval; + for(zend_hash_internal_pointer_reset_ex(ht, &pos); + zend_hash_get_current_data_ex(ht, (void**)&tmpzval, &pos) == SUCCESS; + zend_hash_move_forward_ex(ht, &pos)) { + php_converter_append_fromUnicode_target(*tmpzval, args, objval TSRMLS_CC); + } + return; + } + default: + php_converter_throw_failure(objval, U_ILLEGAL_ARGUMENT_ERROR TSRMLS_CC, "fromUCallback() specified illegal type for substitution character"); + } +} +/* }}} */ + +/* {{{ php_converter_from_u_callback */ +static void php_converter_from_u_callback(const void *context, + UConverterFromUnicodeArgs *args, + const UChar *codeUnits, int32_t length, UChar32 codePoint, + UConverterCallbackReason reason, + UErrorCode *pErrorCode) { + php_converter_object *objval = (php_converter_object*)context; + zval *zreason, *zsource, *zcodepoint, *zerror, *retval = NULL; + zval **zargs[4]; + int i; +#ifdef ZTS + TSRMLS_D = objval->tsrm_ls; +#endif + + MAKE_STD_ZVAL(zreason); + ZVAL_LONG(zreason, reason); + zargs[0] = &zreason; + + MAKE_STD_ZVAL(zsource); + array_init(zsource); + i = 0; + while (i < length) { + UChar32 c; + U16_NEXT(codeUnits, i, length, c); + add_next_index_long(zsource, c); + } + zargs[1] = &zsource; + + MAKE_STD_ZVAL(zcodepoint); + ZVAL_LONG(zcodepoint, codePoint); + zargs[2] = &zcodepoint; + + MAKE_STD_ZVAL(zerror); + ZVAL_LONG(zerror, *pErrorCode); + zargs[3] = &zerror; + + objval->from_cb.param_count = 4; + objval->from_cb.params = zargs; + objval->from_cb.retval_ptr_ptr = &retval; + objval->from_cb.no_separation = 0; + if (zend_call_function(&(objval->from_cb), &(objval->from_cache) TSRMLS_CC) == FAILURE) { + /* Unlikely */ + php_converter_throw_failure(objval, U_INTERNAL_PROGRAM_ERROR TSRMLS_CC, "Unexpected failure calling fromUCallback()"); + } else if (retval) { + php_converter_append_fromUnicode_target(retval, args, objval TSRMLS_CC); + zval_ptr_dtor(&retval); + } + + if (Z_TYPE_P(zerror) == IS_LONG) { + *pErrorCode = Z_LVAL_P(zerror); + } + + zval_ptr_dtor(&zreason); + zval_ptr_dtor(&zsource); + zval_ptr_dtor(&zcodepoint); + zval_ptr_dtor(&zerror); +} +/* }}} */ + +/* {{{ php_converter_set_callbacks */ +static inline zend_bool php_converter_set_callbacks(php_converter_object *objval, UConverter *cnv TSRMLS_DC) { + zend_bool ret = 1; + UErrorCode error = U_ZERO_ERROR; + + if (objval->obj.ce == php_converter_ce) { + /* Short-circuit having to go through method calls and data marshalling + * when we're using default behavior + */ + return 1; + } + + ucnv_setToUCallBack(cnv, (UConverterToUCallback)php_converter_to_u_callback, (const void*)objval, + NULL, NULL, &error); + if (U_FAILURE(error)) { + THROW_UFAILURE(objval, "ucnv_setToUCallBack", error); + ret = 0; + } + + error = U_ZERO_ERROR; + ucnv_setFromUCallBack(cnv, (UConverterFromUCallback)php_converter_from_u_callback, (const void*)objval, + NULL, NULL, &error); + if (U_FAILURE(error)) { + THROW_UFAILURE(objval, "ucnv_setFromUCallBack", error); + ret = 0; + } + return ret; +} +/* }}} */ + +/* {{{ php_converter_set_encoding */ +static zend_bool php_converter_set_encoding(php_converter_object *objval, + UConverter **pcnv, + const char *enc, int enc_len + TSRMLS_DC) { + UErrorCode error = U_ZERO_ERROR; + UConverter *cnv = ucnv_open(enc, &error); + + if (error == U_AMBIGUOUS_ALIAS_WARNING) { + UErrorCode getname_error = U_ZERO_ERROR; + const char *actual_encoding = ucnv_getName(cnv, &getname_error); + if (U_FAILURE(getname_error)) { + /* Should never happen */ + actual_encoding = "(unknown)"; + } + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Ambiguous encoding specified, using %s", actual_encoding); + } else if (U_FAILURE(error)) { + if (objval) { + THROW_UFAILURE(objval, "ucnv_open", error); + } else { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Error setting encoding: %d - %s", (int)error, u_errorName(error)); + } + return 0; + } + + if (objval && !php_converter_set_callbacks(objval, cnv TSRMLS_CC)) { + return 0; + } + + if (*pcnv) { + ucnv_close(*pcnv); + } + *pcnv = cnv; + return 1; +} +/* }}} */ + +/* {{{ php_converter_do_set_encoding */ +ZEND_BEGIN_ARG_INFO_EX(php_converter_set_encoding_arginfo, 0, ZEND_RETURN_VALUE, 1) + ZEND_ARG_INFO(0, encoding) +ZEND_END_ARG_INFO(); +static void php_converter_do_set_encoding(UConverter *cnv, INTERNAL_FUNCTION_PARAMETERS) { + php_converter_object *objval = CONV_GET(getThis()); + char *enc; + int enc_len; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &enc, &enc_len) == FAILURE) { + intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR, "Bad arguments, " + "expected one string argument", 0 TSRMLS_CC); + RETURN_FALSE; + } + intl_errors_reset(&objval->error TSRMLS_CC); + + RETURN_BOOL(php_converter_set_encoding(objval, &(objval->src), enc, enc_len TSRMLS_CC)); +} +/* }}} */ + +/* {{{ proto bool UConverter::setSourceEncoding(string encoding) */ +static PHP_METHOD(UConverter, setSourceEncoding) { + php_converter_object *objval = CONV_GET(getThis()); + php_converter_do_set_encoding(objval->src, INTERNAL_FUNCTION_PARAM_PASSTHRU); +} +/* }}} */ + +/* {{{ proto bool UConverter::setDestinationEncoding(string encoding) */ +static PHP_METHOD(UConverter, setDestinationEncoding) { + php_converter_object *objval = CONV_GET(getThis()); + php_converter_do_set_encoding(objval->dest, INTERNAL_FUNCTION_PARAM_PASSTHRU); +} +/* }}} */ + +/* {{{ php_converter_do_get_encoding */ +ZEND_BEGIN_ARG_INFO_EX(php_converter_get_encoding_arginfo, 0, ZEND_RETURN_VALUE, 0) +ZEND_END_ARG_INFO(); +static void php_converter_do_get_encoding(php_converter_object *objval, UConverter *cnv, INTERNAL_FUNCTION_PARAMETERS) { + const char *name; + + if (zend_parse_parameters_none() == FAILURE) { + intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR, "Expected no arguments", 0 TSRMLS_CC); + RETURN_FALSE; + } + + intl_errors_reset(&objval->error TSRMLS_CC); + + if (!cnv) { + RETURN_NULL(); + } + + name = ucnv_getName(cnv, &objval->error.code); + if (U_FAILURE(objval->error.code)) { + THROW_UFAILURE(objval, "ucnv_getName()", objval->error.code); + RETURN_FALSE; + } + + RETURN_STRING(name, 1); +} +/* }}} */ + +/* {{{ proto string UConverter::getSourceEncoding() */ +static PHP_METHOD(UConverter, getSourceEncoding) { + php_converter_object *objval = CONV_GET(getThis()); + php_converter_do_get_encoding(objval, objval->src, INTERNAL_FUNCTION_PARAM_PASSTHRU); +} +/* }}} */ + +/* {{{ proto string UConverter::getDestinationEncoding() */ +static PHP_METHOD(UConverter, getDestinationEncoding) { + php_converter_object *objval = CONV_GET(getThis()); + php_converter_do_get_encoding(objval, objval->dest, INTERNAL_FUNCTION_PARAM_PASSTHRU); +} +/* }}} */ + +/* {{{ php_converter_do_get_type */ +ZEND_BEGIN_ARG_INFO_EX(php_converter_get_type_arginfo, 0, ZEND_RETURN_VALUE, 0) +ZEND_END_ARG_INFO(); +static void php_converter_do_get_type(php_converter_object *objval, UConverter *cnv, INTERNAL_FUNCTION_PARAMETERS) { + UConverterType t; + + if (zend_parse_parameters_none() == FAILURE) { + intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR, "Expected no arguments", 0 TSRMLS_CC); + RETURN_FALSE; + } + intl_errors_reset(&objval->error TSRMLS_CC); + + if (!cnv) { + RETURN_NULL(); + } + + t = ucnv_getType(cnv); + if (U_FAILURE(objval->error.code)) { + THROW_UFAILURE(objval, "ucnv_getType", objval->error.code); + RETURN_FALSE; + } + + RETURN_LONG(t); +} +/* }}} */ + +/* {{{ proto long UConverter::getSourceType() */ +static PHP_METHOD(UConverter, getSourceType) { + php_converter_object *objval = CONV_GET(getThis()); + php_converter_do_get_type(objval, objval->src, INTERNAL_FUNCTION_PARAM_PASSTHRU); +} +/* }}} */ + +/* {{{ proto long UConverter::getDestinationType() */ +static PHP_METHOD(UConverter, getDestinationType) { + php_converter_object *objval = CONV_GET(getThis()); + php_converter_do_get_type(objval, objval->dest, INTERNAL_FUNCTION_PARAM_PASSTHRU); +} +/* }}} */ + +/* {{{ php_converter_resolve_callback */ +static void php_converter_resolve_callback(zval *zobj, + php_converter_object *objval, + const char *callback_name, + zend_fcall_info *finfo, + zend_fcall_info_cache *fcache TSRMLS_DC) { + char *errstr = NULL; + zval caller; + + array_init(&caller); + Z_ADDREF_P(zobj); + add_index_zval(&caller, 0, zobj); + add_index_string(&caller, 1, callback_name, 1); + if (zend_fcall_info_init(&caller, 0, finfo, fcache, NULL, &errstr TSRMLS_CC) == FAILURE) { + php_converter_throw_failure(objval, U_INTERNAL_PROGRAM_ERROR TSRMLS_CC, "Error setting converter callback: %s", errstr); + } + zval_dtor(&caller); + if (errstr) { + efree(errstr); + } +} +/* }}} */ + +/* {{{ proto void UConverter::__construct([string dest = 'utf-8',[string src = 'utf-8']]) */ +ZEND_BEGIN_ARG_INFO_EX(php_converter_arginfo, 0, ZEND_RETURN_VALUE, 0) + ZEND_ARG_INFO(0, destination_encoding) + ZEND_ARG_INFO(0, source_encoding) +ZEND_END_ARG_INFO(); + +static PHP_METHOD(UConverter, __construct) { + php_converter_object *objval = CONV_GET(getThis()); + char *src = "utf-8"; + int src_len = sizeof("utf-8") - 1; + char *dest = src; + int dest_len = src_len; + + intl_error_reset(NULL TSRMLS_CC); + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s!s!", + &dest, &dest_len, &src, &src_len) == FAILURE) { + intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR, + "UConverter::__construct(): bad arguments", 0 TSRMLS_CC); + return; + } + + php_converter_set_encoding(objval, &(objval->src), src, src_len TSRMLS_CC); + php_converter_set_encoding(objval, &(objval->dest), dest, dest_len TSRMLS_CC); + php_converter_resolve_callback(getThis(), objval, "toUCallback", &(objval->to_cb), &(objval->to_cache) TSRMLS_CC); + php_converter_resolve_callback(getThis(), objval, "fromUCallback", &(objval->from_cb), &(objval->from_cache) TSRMLS_CC); +} +/* }}} */ + +/* {{{ proto bool UConverter::setSubstChars(string $chars) */ +ZEND_BEGIN_ARG_INFO_EX(php_converter_setSubstChars_arginfo, 0, ZEND_RETURN_VALUE, 1) + ZEND_ARG_INFO(0, chars) +ZEND_END_ARG_INFO(); + +static PHP_METHOD(UConverter, setSubstChars) { + php_converter_object *objval = CONV_GET(getThis()); + char *chars; + int chars_len, ret = 1; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &chars, &chars_len) == FAILURE) { + intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR, + "UConverter::setSubstChars(): bad arguments", 0 TSRMLS_CC); + RETURN_FALSE; + } + intl_errors_reset(&objval->error TSRMLS_CC); + + if (objval->src) { + UErrorCode error = U_ZERO_ERROR; + ucnv_setSubstChars(objval->src, chars, chars_len, &error); + if (U_FAILURE(error)) { + THROW_UFAILURE(objval, "ucnv_setSubstChars", error); + ret = 0; + } + } else { + php_converter_throw_failure(objval, U_INVALID_STATE_ERROR TSRMLS_CC, "Source Converter has not been initialized yet"); + ret = 0; + } + + if (objval->dest) { + UErrorCode error = U_ZERO_ERROR; + ucnv_setSubstChars(objval->dest, chars, chars_len, &error); + if (U_FAILURE(error)) { + THROW_UFAILURE(objval, "ucnv_setSubstChars", error); + ret = 0; + } + } else { + php_converter_throw_failure(objval, U_INVALID_STATE_ERROR TSRMLS_CC, "Destination Converter has not been initialized yet"); + ret = 0; + } + + RETURN_BOOL(ret); +} +/* }}} */ + +/* {{{ proto string UConverter::getSubstChars() */ +ZEND_BEGIN_ARG_INFO_EX(php_converter_getSubstChars_arginfo, 0, ZEND_RETURN_VALUE, 0) +ZEND_END_ARG_INFO(); + +static PHP_METHOD(UConverter, getSubstChars) { + php_converter_object *objval = CONV_GET(getThis()); + char chars[127]; + int8_t chars_len = sizeof(chars); + UErrorCode error = U_ZERO_ERROR; + + if (zend_parse_parameters_none() == FAILURE) { + intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR, + "UConverter::getSubstChars(): expected no arguments", 0 TSRMLS_CC); + RETURN_FALSE; + } + intl_errors_reset(&objval->error TSRMLS_CC); + + if (!objval->src) { + RETURN_NULL(); + } + + /* src and dest get the same subst chars set, + * so it doesn't really matter which one we read from + */ + ucnv_getSubstChars(objval->src, chars, &chars_len, &error); + if (U_FAILURE(error)) { + THROW_UFAILURE(objval, "ucnv_getSubstChars", error); + RETURN_FALSE; + } + + RETURN_STRINGL(chars, chars_len, 1); +} +/* }}} */ + +/* {{{ php_converter_do_convert */ +static zend_bool php_converter_do_convert(UConverter *dest_cnv, char **pdest, int32_t *pdest_len, + UConverter *src_cnv, const char *src, int32_t src_len, + php_converter_object *objval + TSRMLS_DC) { + UErrorCode error = U_ZERO_ERROR; + int32_t dest_len, + temp_len; + char *dest; + UChar *temp; + + if (!src_cnv || !dest_cnv) { + php_converter_throw_failure(objval, U_INVALID_STATE_ERROR TSRMLS_CC, + "Internal converters not initialized"); + return 0; + } + + /* Get necessary buffer size first */ + temp_len = 1 + ucnv_toUChars(src_cnv, NULL, 0, src, src_len, &error); + if (U_FAILURE(error) && error != U_BUFFER_OVERFLOW_ERROR) { + THROW_UFAILURE(objval, "ucnv_toUChars", error); + return 0; + } + temp = safe_emalloc(sizeof(UChar), temp_len, sizeof(UChar)); + + /* Convert to intermediate UChar* array */ + error = U_ZERO_ERROR; + temp_len = ucnv_toUChars(src_cnv, temp, temp_len, src, src_len, &error); + if (U_FAILURE(error)) { + THROW_UFAILURE(objval, "ucnv_toUChars", error); + efree(temp); + return 0; + } + temp[temp_len] = 0; + + /* Get necessary output buffer size */ + dest_len = 1 + ucnv_fromUChars(dest_cnv, NULL, 0, temp, temp_len, &error); + if (U_FAILURE(error) && error != U_BUFFER_OVERFLOW_ERROR) { + THROW_UFAILURE(objval, "ucnv_fromUChars", error); + efree(temp); + return 0; + } + dest = safe_emalloc(sizeof(char), dest_len, sizeof(char)); + + /* Convert to final encoding */ + error = U_ZERO_ERROR; + dest_len = ucnv_fromUChars(dest_cnv, dest, dest_len, temp, temp_len, &error); + efree(temp); + if (U_FAILURE(error)) { + THROW_UFAILURE(objval, "ucnv_fromUChars", error); + efree(dest); + return 0; + } + + *pdest = dest; + if (pdest_len) { + *pdest_len = dest_len; + } + + return 1; +} +/* }}} */ + +/* {{{ proto string UConverter::reasonText(long reason) */ +#define UCNV_REASON_CASE(v) case (UCNV_ ## v) : RETURN_STRINGL( "REASON_" #v , sizeof( "REASON_" #v ) - 1, 1); +ZEND_BEGIN_ARG_INFO_EX(php_converter_reasontext_arginfo, 0, ZEND_RETURN_VALUE, 0) + ZEND_ARG_INFO(0, reason) +ZEND_END_ARG_INFO(); +static PHP_METHOD(UConverter, reasonText) { + long reason; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &reason) == FAILURE) { + intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR, + "UConverter::reasonText(): bad arguments", 0 TSRMLS_CC); + RETURN_FALSE; + } + intl_error_reset(NULL TSRMLS_CC); + + switch (reason) { + UCNV_REASON_CASE(UNASSIGNED) + UCNV_REASON_CASE(ILLEGAL) + UCNV_REASON_CASE(IRREGULAR) + UCNV_REASON_CASE(RESET) + UCNV_REASON_CASE(CLOSE) + UCNV_REASON_CASE(CLONE) + default: + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown UConverterCallbackReason: %ld", reason); + RETURN_FALSE; + } +} +/* }}} */ + +/* {{{ proto string UConverter::convert(string str[, bool reverse]) */ +ZEND_BEGIN_ARG_INFO_EX(php_converter_convert_arginfo, 0, ZEND_RETURN_VALUE, 1) + ZEND_ARG_INFO(0, str) + ZEND_ARG_INFO(0, reverse) +ZEND_END_ARG_INFO(); + +static PHP_METHOD(UConverter, convert) { + php_converter_object *objval = CONV_GET(getThis()); + char *str, *dest; + int str_len, dest_len; + zend_bool reverse = 0; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|b", + &str, &str_len, &reverse) == FAILURE) { + intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR, + "UConverter::convert(): bad arguments", 0 TSRMLS_CC); + RETURN_FALSE; + } + intl_errors_reset(&objval->error TSRMLS_CC); + + if (php_converter_do_convert(reverse ? objval->src : objval->dest, + &dest, &dest_len, + reverse ? objval->dest : objval->src, + str, str_len, + objval TSRMLS_CC)) { + RETURN_STRINGL(dest, dest_len, 0); + } else { + RETURN_FALSE; + } +} +/* }}} */ + +/* {{{ proto string UConverter::transcode(string $str, string $toEncoding, string $fromEncoding[, Array $options = array()]) */ +ZEND_BEGIN_ARG_INFO_EX(php_converter_transcode_arginfo, 0, ZEND_RETURN_VALUE, 3) + ZEND_ARG_INFO(0, str) + ZEND_ARG_INFO(0, toEncoding) + ZEND_ARG_INFO(0, fromEncoding) + ZEND_ARG_ARRAY_INFO(0, options, 1) +ZEND_END_ARG_INFO(); + +static PHP_METHOD(UConverter, transcode) { + char *str, *src, *dest; + int str_len, src_len, dest_len; + zval *options = NULL; + UConverter *src_cnv = NULL, *dest_cnv = NULL; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sss|a!", + &str, &str_len, &dest, &dest_len, &src, &src_len, &options) == FAILURE) { + intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR, + "UConverter::transcode(): bad arguments", 0 TSRMLS_CC); + RETURN_FALSE; + } + intl_error_reset(NULL TSRMLS_CC); + + if (php_converter_set_encoding(NULL, &src_cnv, src, src_len TSRMLS_CC) && + php_converter_set_encoding(NULL, &dest_cnv, dest, dest_len TSRMLS_CC)) { + char *out = NULL; + int out_len = 0; + UErrorCode error = U_ZERO_ERROR; + + if (options && zend_hash_num_elements(Z_ARRVAL_P(options))) { + zval **tmpzval; + + if (U_SUCCESS(error) && + zend_hash_find(Z_ARRVAL_P(options), "from_subst", sizeof("from_subst"), (void**)&tmpzval) == SUCCESS && + Z_TYPE_PP(tmpzval) == IS_STRING) { + error = U_ZERO_ERROR; + ucnv_setSubstChars(src_cnv, Z_STRVAL_PP(tmpzval), Z_STRLEN_PP(tmpzval) & 0x7F, &error); + } + if (U_SUCCESS(error) && + zend_hash_find(Z_ARRVAL_P(options), "to_subst", sizeof("to_subst"), (void**)&tmpzval) == SUCCESS && + Z_TYPE_PP(tmpzval) == IS_STRING) { + error = U_ZERO_ERROR; + ucnv_setSubstChars(dest_cnv, Z_STRVAL_PP(tmpzval), Z_STRLEN_PP(tmpzval) & 0x7F, &error); + } + } + + if (U_SUCCESS(error) && + php_converter_do_convert(dest_cnv, &out, &out_len, src_cnv, str, str_len, NULL TSRMLS_CC)) { + RETVAL_STRINGL(out, out_len, 0); + } + + if (U_FAILURE(error)) { + THROW_UFAILURE(NULL, "transcode", error); + RETVAL_FALSE; + } + } else { + RETVAL_FALSE; + } + + if (src_cnv) { + ucnv_close(src_cnv); + } + if (dest_cnv) { + ucnv_close(dest_cnv); + } +} +/* }}} */ + +/* {{{ proto int UConverter::getErrorCode() */ +ZEND_BEGIN_ARG_INFO_EX(php_converter_geterrorcode_arginfo, 0, ZEND_RETURN_VALUE, 0) +ZEND_END_ARG_INFO(); +static PHP_METHOD(UConverter, getErrorCode) { + php_converter_object *objval = CONV_GET(getThis()); + + if (zend_parse_parameters_none() == FAILURE) { + intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR, + "UConverter::getErrorCode(): expected no arguments", 0 TSRMLS_CC); + RETURN_FALSE; + } + + RETURN_LONG(intl_error_get_code(&(objval->error) TSRMLS_CC)); +} +/* }}} */ + +/* {{{ proto string UConverter::getErrorMessage() */ +ZEND_BEGIN_ARG_INFO_EX(php_converter_geterrormsg_arginfo, 0, ZEND_RETURN_VALUE, 0) +ZEND_END_ARG_INFO(); +static PHP_METHOD(UConverter, getErrorMessage) { + php_converter_object *objval = CONV_GET(getThis()); + char *message = intl_error_get_message(&(objval->error) TSRMLS_CC); + + if (zend_parse_parameters_none() == FAILURE) { + intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR, + "UConverter::getErrorMessage(): expected no arguments", 0 TSRMLS_CC); + RETURN_FALSE; + } + + if (message) { + RETURN_STRING(message, 1); + } else { + RETURN_NULL(); + } +} +/* }}} */ + +/* {{{ proto array UConverter::getAvailable() */ +ZEND_BEGIN_ARG_INFO_EX(php_converter_getavailable_arginfo, 0, ZEND_RETURN_VALUE, 0) +ZEND_END_ARG_INFO(); +static PHP_METHOD(UConverter, getAvailable) { + int32_t i, + count = ucnv_countAvailable(); + + if (zend_parse_parameters_none() == FAILURE) { + intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR, + "UConverter::getErrorMessage(): expected no arguments", 0 TSRMLS_CC); + RETURN_FALSE; + } + intl_error_reset(NULL TSRMLS_CC); + + array_init(return_value); + for(i = 0; i < count; i++) { + const char *name = ucnv_getAvailableName(i); + add_next_index_string(return_value, name, 1); + } +} +/* }}} */ + +/* {{{ proto array UConverter::getAliases(string name) */ +ZEND_BEGIN_ARG_INFO_EX(php_converter_getaliases_arginfo, 0, ZEND_RETURN_VALUE, 0) + ZEND_ARG_INFO(0, name) +ZEND_END_ARG_INFO(); +static PHP_METHOD(UConverter, getAliases) { + char *name; + int name_len; + UErrorCode error = U_ZERO_ERROR; + uint16_t i, count; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &name, &name_len) == FAILURE) { + intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR, + "UConverter::getAliases(): bad arguments", 0 TSRMLS_CC); + RETURN_FALSE; + } + intl_error_reset(NULL TSRMLS_CC); + + count = ucnv_countAliases(name, &error); + if (U_FAILURE(error)) { + THROW_UFAILURE(NULL, "ucnv_countAliases", error); + RETURN_FALSE; + } + + array_init(return_value); + for(i = 0; i < count; i++) { + const char *alias; + + error = U_ZERO_ERROR; + alias = ucnv_getAlias(name, i, &error); + if (U_FAILURE(error)) { + THROW_UFAILURE(NULL, "ucnv_getAlias", error); + zval_dtor(return_value); + RETURN_NULL(); + } + add_next_index_string(return_value, alias, 1); + } +} +/* }}} */ + +/* {{{ proto array UConverter::getStandards() */ +ZEND_BEGIN_ARG_INFO_EX(php_converter_getstandards_arginfo, 0, ZEND_RETURN_VALUE, 0) +ZEND_END_ARG_INFO(); +static PHP_METHOD(UConverter, getStandards) { + uint16_t i, count; + + if (zend_parse_parameters_none() == FAILURE) { + intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR, + "UConverter::getStandards(): expected no arguments", 0 TSRMLS_CC); + RETURN_FALSE; + } + intl_error_reset(NULL TSRMLS_CC); + + array_init(return_value); + count = ucnv_countStandards(); + for(i = 0; i < count; i++) { + UErrorCode error = U_ZERO_ERROR; + const char *name = ucnv_getStandard(i, &error); + if (U_FAILURE(error)) { + THROW_UFAILURE(NULL, "ucnv_getStandard", error); + zval_dtor(return_value); + RETURN_NULL(); + } + add_next_index_string(return_value, name, 1); + } +} +/* }}} */ + +static zend_function_entry php_converter_methods[] = { + PHP_ME(UConverter, __construct, php_converter_arginfo, ZEND_ACC_PUBLIC | ZEND_ACC_CTOR) + + /* Encoding selection */ + PHP_ME(UConverter, setSourceEncoding, php_converter_set_encoding_arginfo, ZEND_ACC_PUBLIC) + PHP_ME(UConverter, setDestinationEncoding, php_converter_set_encoding_arginfo, ZEND_ACC_PUBLIC) + PHP_ME(UConverter, getSourceEncoding, php_converter_get_encoding_arginfo, ZEND_ACC_PUBLIC) + PHP_ME(UConverter, getDestinationEncoding, php_converter_get_encoding_arginfo, ZEND_ACC_PUBLIC) + + /* Introspection for algorithmic converters */ + PHP_ME(UConverter, getSourceType, php_converter_get_type_arginfo, ZEND_ACC_PUBLIC) + PHP_ME(UConverter, getDestinationType, php_converter_get_type_arginfo, ZEND_ACC_PUBLIC) + + /* Basic codeunit error handling */ + PHP_ME(UConverter, getSubstChars, php_converter_getSubstChars_arginfo, ZEND_ACC_PUBLIC) + PHP_ME(UConverter, setSubstChars, php_converter_setSubstChars_arginfo, ZEND_ACC_PUBLIC) + + /* Default callback handlers */ + PHP_ME(UConverter, toUCallback, php_converter_toUCallback_arginfo, ZEND_ACC_PUBLIC) + PHP_ME(UConverter, fromUCallback, php_converter_fromUCallback_arginfo, ZEND_ACC_PUBLIC) + + /* Core conversion workhorses */ + PHP_ME(UConverter, convert, php_converter_convert_arginfo, ZEND_ACC_PUBLIC) + PHP_ME(UConverter, transcode, php_converter_transcode_arginfo, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + + /* Error inspection */ + PHP_ME(UConverter, getErrorCode, php_converter_geterrorcode_arginfo, ZEND_ACC_PUBLIC) + PHP_ME(UConverter, getErrorMessage, php_converter_geterrormsg_arginfo, ZEND_ACC_PUBLIC) + + /* Ennumeration and lookup */ + PHP_ME(UConverter, reasonText, php_converter_reasontext_arginfo, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(UConverter, getAvailable, php_converter_getavailable_arginfo, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(UConverter, getAliases, php_converter_getaliases_arginfo, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(UConverter, getStandards, php_converter_getstandards_arginfo, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + { NULL, NULL, NULL } +}; + +/* {{{ Converter create/clone/destroy */ +static void php_converter_free_object(php_converter_object *objval TSRMLS_DC) { + if (objval->src) { + ucnv_close(objval->src); + } + + if (objval->dest) { + ucnv_close(objval->dest); + } + + intl_error_reset(&(objval->error) TSRMLS_CC); + zend_object_std_dtor(&(objval->obj) TSRMLS_CC); + + efree(objval); +} + +static zend_object_value php_converter_object_ctor(zend_class_entry *ce, php_converter_object **pobjval TSRMLS_DC) { + php_converter_object *objval; + zend_object_value retval; + + objval = ecalloc(1, sizeof(php_converter_object)); + objval->obj.ce = ce; + +#ifdef ZTS + objval->tsrm_ls = TSRMLS_C; +#endif + intl_error_init(&(objval->error) TSRMLS_CC); + + retval.handle = zend_objects_store_put(objval, NULL, (zend_objects_free_object_storage_t)php_converter_free_object, NULL TSRMLS_CC); + retval.handlers = &php_converter_object_handlers; + *pobjval = objval; + + return retval; +} + +static zend_object_value php_converter_create_object(zend_class_entry *ce TSRMLS_DC) { + php_converter_object *objval = NULL; + zend_object_value retval = php_converter_object_ctor(ce, &objval TSRMLS_CC); + + object_properties_init(&(objval->obj), ce); + + return retval; +} + +static zend_object_value php_converter_clone_object(zval *object TSRMLS_DC) { + php_converter_object *objval, *oldobj = (php_converter_object*)zend_objects_get_address(object TSRMLS_CC); + zend_object_value retval = php_converter_object_ctor(Z_OBJCE_P(object), &objval TSRMLS_CC); + UErrorCode error = U_ZERO_ERROR; + + intl_errors_reset(&oldobj->error TSRMLS_CC); + + objval->src = ucnv_safeClone(oldobj->src, NULL, NULL, &error); + if (U_SUCCESS(error)) { + error = U_ZERO_ERROR; + objval->dest = ucnv_safeClone(oldobj->dest, NULL, NULL, &error); + } + if (U_FAILURE(error)) { + char *err_msg; + THROW_UFAILURE(oldobj, "ucnv_safeClone", error); + + err_msg = intl_error_get_message(&oldobj->error TSRMLS_CC); + zend_throw_exception(NULL, err_msg, 0 TSRMLS_CC); + efree(err_msg); + + return retval; + } + + /* Update contexts for converter error handlers */ + php_converter_set_callbacks(objval, objval->src TSRMLS_CC); + php_converter_set_callbacks(objval, objval->dest TSRMLS_CC); + + zend_objects_clone_members(&(objval->obj), retval, &(oldobj->obj), Z_OBJ_HANDLE_P(object) TSRMLS_CC); + + /* Newly cloned object deliberately does not inherit error state from original object */ + + return retval; +} +/* }}} */ + +#define CONV_REASON_CONST(v) zend_declare_class_constant_long(php_converter_ce, "REASON_" #v, sizeof("REASON_" #v) - 1, UCNV_ ## v TSRMLS_CC) +#define CONV_TYPE_CONST(v) zend_declare_class_constant_long(php_converter_ce, #v , sizeof(#v) - 1, UCNV_ ## v TSRMLS_CC) + +/* {{{ php_converter_minit */ +int php_converter_minit(INIT_FUNC_ARGS) { + zend_class_entry ce; + + INIT_CLASS_ENTRY(ce, "UConverter", php_converter_methods); + php_converter_ce = zend_register_internal_class(&ce TSRMLS_CC); + php_converter_ce->create_object = php_converter_create_object; + memcpy(&php_converter_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); + php_converter_object_handlers.clone_obj = php_converter_clone_object; + + /* enum UConverterCallbackReason */ + CONV_REASON_CONST(UNASSIGNED); + CONV_REASON_CONST(ILLEGAL); + CONV_REASON_CONST(IRREGULAR); + CONV_REASON_CONST(RESET); + CONV_REASON_CONST(CLOSE); + CONV_REASON_CONST(CLONE); + + /* enum UConverterType */ + CONV_TYPE_CONST(UNSUPPORTED_CONVERTER); + CONV_TYPE_CONST(SBCS); + CONV_TYPE_CONST(DBCS); + CONV_TYPE_CONST(MBCS); + CONV_TYPE_CONST(LATIN_1); + CONV_TYPE_CONST(UTF8); + CONV_TYPE_CONST(UTF16_BigEndian); + CONV_TYPE_CONST(UTF16_LittleEndian); + CONV_TYPE_CONST(UTF32_BigEndian); + CONV_TYPE_CONST(UTF32_LittleEndian); + CONV_TYPE_CONST(EBCDIC_STATEFUL); + CONV_TYPE_CONST(ISO_2022); + CONV_TYPE_CONST(LMBCS_1); + CONV_TYPE_CONST(LMBCS_2); + CONV_TYPE_CONST(LMBCS_3); + CONV_TYPE_CONST(LMBCS_4); + CONV_TYPE_CONST(LMBCS_5); + CONV_TYPE_CONST(LMBCS_6); + CONV_TYPE_CONST(LMBCS_8); + CONV_TYPE_CONST(LMBCS_11); + CONV_TYPE_CONST(LMBCS_16); + CONV_TYPE_CONST(LMBCS_17); + CONV_TYPE_CONST(LMBCS_18); + CONV_TYPE_CONST(LMBCS_19); + CONV_TYPE_CONST(LMBCS_LAST); + CONV_TYPE_CONST(HZ); + CONV_TYPE_CONST(SCSU); + CONV_TYPE_CONST(ISCII); + CONV_TYPE_CONST(US_ASCII); + CONV_TYPE_CONST(UTF7); + CONV_TYPE_CONST(BOCU1); + CONV_TYPE_CONST(UTF16); + CONV_TYPE_CONST(UTF32); + CONV_TYPE_CONST(CESU8); + CONV_TYPE_CONST(IMAP_MAILBOX); + + return SUCCESS; +} +/* }}} */ + +/* + * 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/intl/converter/converter.h b/ext/intl/converter/converter.h new file mode 100644 index 0000000000..bd316fcf98 --- /dev/null +++ b/ext/intl/converter/converter.h @@ -0,0 +1,28 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | 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 | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Sara Golemon <pollita@php.net> | + +----------------------------------------------------------------------+ +*/ + +#ifndef PHP_INTL_CONVERTER_H +#define PHP_INTL_CONVERTER_H + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "php.h" + +int php_converter_minit(INIT_FUNC_ARGS); + +#endif /* PHP_INTL_CONVERTER_H */ diff --git a/ext/intl/php_intl.c b/ext/intl/php_intl.c index d3d477c971..a2c4d77651 100644 --- a/ext/intl/php_intl.c +++ b/ext/intl/php_intl.c @@ -34,6 +34,8 @@ #include "collator/collator_create.h" #include "collator/collator_error.h" +#include "converter/converter.h" + #include "formatter/formatter.h" #include "formatter/formatter_class.h" #include "formatter/formatter_attr.h" @@ -986,6 +988,9 @@ PHP_MINIT_FUNCTION( intl ) /* Global error handling. */ intl_error_init( NULL TSRMLS_CC ); + /* 'Converter' class for codepage conversions */ + php_converter_minit(INIT_FUNC_ARGS_PASSTHRU); + return SUCCESS; } /* }}} */ diff --git a/ext/intl/tests/uconverter___construct_error.phpt b/ext/intl/tests/uconverter___construct_error.phpt new file mode 100644 index 0000000000..1b2480818b --- /dev/null +++ b/ext/intl/tests/uconverter___construct_error.phpt @@ -0,0 +1,14 @@ +--TEST-- +Basic UConverter::convert() usage +--INI-- +intl.error_level = E_WARNING +--SKIPIF-- +<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?> +--FILE-- +<?php +$c = new UConverter('utf-8', "\x80"); +var_dump($c); +--EXPECTF-- +Warning: UConverter::__construct(): ucnv_open() returned error 4: U_FILE_ACCESS_ERROR in %s on line %d +object(UConverter)#%d (0) { +} diff --git a/ext/intl/tests/uconverter_enum.phpt b/ext/intl/tests/uconverter_enum.phpt new file mode 100644 index 0000000000..67e02c9d75 --- /dev/null +++ b/ext/intl/tests/uconverter_enum.phpt @@ -0,0 +1,21 @@ +--TEST-- +UConverter Enumerations +--SKIPIF-- +<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?> +--FILE-- +<?php +$avail = UConverter::getAvailable(); +var_dump(count($avail) > 100); +var_dump(in_array('UTF-7', $avail)); +var_dump(in_array('CESU-8', $avail)); +var_dump(in_array('ISO-8859-1', $avail)); + +$latin1 = UConverter::getAliases('latin1'); +var_dump(in_array('ISO-8859-1', $latin1)); + +--EXPECT-- +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) diff --git a/ext/intl/tests/uconverter_func_basic.phpt b/ext/intl/tests/uconverter_func_basic.phpt new file mode 100644 index 0000000000..da8956beae --- /dev/null +++ b/ext/intl/tests/uconverter_func_basic.phpt @@ -0,0 +1,17 @@ +--TEST-- +Basic UConverter::transcode() usage +--SKIPIF-- +<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?> +--FILE-- +<?php +var_dump(UConverter::transcode("This is an ascii string", 'utf-8', 'latin1')); +// urlencode so that non-ascii shows up parsable in phpt file +var_dump(urlencode(UConverter::transcode("Espa\xF1ol", 'utf-8', 'latin1'))); +var_dump(urlencode(UConverter::transcode("Stra\xDFa", 'utf-8', 'latin1'))); + +var_dump(bin2hex(UConverter::transcode("\xE4", 'utf-8', 'koi8-r'))); +--EXPECT-- +string(23) "This is an ascii string" +string(12) "Espa%C3%B1ol" +string(11) "Stra%C3%9Fa" +string(4) "d094" diff --git a/ext/intl/tests/uconverter_func_subst.phpt b/ext/intl/tests/uconverter_func_subst.phpt new file mode 100644 index 0000000000..5cac5ce59c --- /dev/null +++ b/ext/intl/tests/uconverter_func_subst.phpt @@ -0,0 +1,31 @@ +--TEST-- +Basic UConverter::convert() w/ Subsitution +--SKIPIF-- +<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?> +--INI-- +intl.use_exceptions=false +--FILE-- +<?php +foreach(array('?','','??') as $subst) { + $opts = array('to_subst' => $subst); + $ret = UConverter::transcode("This is an ascii string", 'ascii', 'utf-8', $opts); + if ($ret === FALSE) { + echo "Error: ", intl_get_error_message(), "\n"; + } else { + var_dump($ret); + } + $ret = UConverter::transcode("Snowman: (\xE2\x98\x83)", 'ascii', 'utf-8', $opts); + if ($ret === FALSE) { + echo "Error: ", intl_get_error_message(), "\n"; + } else { + var_dump($ret); + } +} + +--EXPECTF-- +string(23) "This is an ascii string" +string(12) "Snowman: (?)" +Error: transcode() returned error 1: U_ILLEGAL_ARGUMENT_ERROR: U_ILLEGAL_ARGUMENT_ERROR +Error: transcode() returned error 1: U_ILLEGAL_ARGUMENT_ERROR: U_ILLEGAL_ARGUMENT_ERROR +Error: transcode() returned error 1: U_ILLEGAL_ARGUMENT_ERROR: U_ILLEGAL_ARGUMENT_ERROR +Error: transcode() returned error 1: U_ILLEGAL_ARGUMENT_ERROR: U_ILLEGAL_ARGUMENT_ERROR diff --git a/ext/intl/tests/uconverter_oop_algo.phpt b/ext/intl/tests/uconverter_oop_algo.phpt new file mode 100644 index 0000000000..349182ce32 --- /dev/null +++ b/ext/intl/tests/uconverter_oop_algo.phpt @@ -0,0 +1,18 @@ +--TEST-- +UConverter Algorithmic converters +--SKIPIF-- +<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?> +--FILE-- +<?php +$c = new UConverter('utf-8', 'latin1'); +var_dump(UConverter::LATIN_1 === $c->getSourceType()); +var_dump(UConverter::UTF8 === $c->getDestinationType()); + +$c = new UConverter('koi8-r', 'utf-32be'); +var_dump(UConverter::UTF32_BigEndian === $c->getSourceType()); +var_dump(UConverter::SBCS === $c->getDestinationType()); +--EXPECT-- +bool(true) +bool(true) +bool(true) +bool(true) diff --git a/ext/intl/tests/uconverter_oop_basic.phpt b/ext/intl/tests/uconverter_oop_basic.phpt new file mode 100644 index 0000000000..2b8909ff31 --- /dev/null +++ b/ext/intl/tests/uconverter_oop_basic.phpt @@ -0,0 +1,21 @@ +--TEST-- +Basic UConverter::convert() usage +--SKIPIF-- +<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?> +--FILE-- +<?php +$c = new UConverter('utf-8', 'latin1'); +var_dump($c->convert("This is an ascii string")); +// urlencode so that non-ascii shows up parsable in phpt file +var_dump(urlencode($c->convert("Espa\xF1ol"))); // U+00F1 LATIN SMALL LETTER N WITH TILDE +var_dump(urlencode($c->convert("Stra\xDFa"))); // U+00DF LATIN SMALL LETTER SHARP S +var_dump(urlencode($c->convert("Stra\xC3\x9Fa", true))); // Reverse prior op + +$k = new UConverter('utf-8', 'koi8-r'); +var_dump(bin2hex($k->convert("\xE4"))); // U+0414 CYRILLIC CAPITAL LETTER DE +--EXPECT-- +string(23) "This is an ascii string" +string(12) "Espa%C3%B1ol" +string(11) "Stra%C3%9Fa" +string(8) "Stra%DFa" +string(4) "d094" diff --git a/ext/intl/tests/uconverter_oop_callback.phpt b/ext/intl/tests/uconverter_oop_callback.phpt new file mode 100644 index 0000000000..47daf43305 --- /dev/null +++ b/ext/intl/tests/uconverter_oop_callback.phpt @@ -0,0 +1,52 @@ +--TEST-- +UConverter::convert() w/ Callback Reasons +--SKIPIF-- +<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?> +--FILE-- +<?php +class MyConverter extends UConverter { + /** + * Called during conversion from source encoding to internal UChar representation + */ + public function toUCallback($reason, $source, $codeUnits, &$error) { + echo "toUCallback(", UConverter::reasonText($reason), ", ...)\n"; + return parent::toUCallback($reason, $source, $codeUnits, $error); + } + + /** + * Called during conversion from internal UChar to destination encoding + */ + public function fromUCallback($reason, $source, $codePoint, &$error) { + echo "fromUCallback(", UConverter::reasonText($reason), ", ...)\n"; + return parent::fromUCallback($reason, $source, $codePoint, $error); + } + +} + +$c = new MyConverter('ascii', 'utf-8'); +foreach(array("regular", "irregul\xC1\xA1r", "\xC2\xA1unsupported!") as $word) { + $c->convert($word); +} +--EXPECT-- +toUCallback(REASON_RESET, ...) +toUCallback(REASON_RESET, ...) +fromUCallback(REASON_RESET, ...) +fromUCallback(REASON_RESET, ...) +toUCallback(REASON_RESET, ...) +toUCallback(REASON_ILLEGAL, ...) +toUCallback(REASON_RESET, ...) +toUCallback(REASON_ILLEGAL, ...) +fromUCallback(REASON_RESET, ...) +fromUCallback(REASON_UNASSIGNED, ...) +fromUCallback(REASON_RESET, ...) +fromUCallback(REASON_UNASSIGNED, ...) +toUCallback(REASON_RESET, ...) +toUCallback(REASON_RESET, ...) +fromUCallback(REASON_RESET, ...) +fromUCallback(REASON_UNASSIGNED, ...) +fromUCallback(REASON_RESET, ...) +fromUCallback(REASON_UNASSIGNED, ...) +toUCallback(REASON_CLOSE, ...) +fromUCallback(REASON_CLOSE, ...) +toUCallback(REASON_CLOSE, ...) +fromUCallback(REASON_CLOSE, ...) diff --git a/ext/intl/tests/uconverter_oop_callback_return.phpt b/ext/intl/tests/uconverter_oop_callback_return.phpt new file mode 100644 index 0000000000..cd7e7a5834 --- /dev/null +++ b/ext/intl/tests/uconverter_oop_callback_return.phpt @@ -0,0 +1,40 @@ +--TEST-- +UConverter::convert() w/ Callback Return Values +--SKIPIF-- +<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?> +--FILE-- +<?php +class MyConverter extends UConverter { + public function toUCallback($reason, $source, $codeUnits, &$error) { + $error = U_ZERO_ERROR; + switch ($codeUnits) { + case "\x80": return NULL; + case "\x81": return 'a'; + case "\x82": return ord('b'); + case "\x83": return array('c'); + } + } + + /** + * Called during conversion from internal UChar to destination encoding + */ + public function fromUCallback($reason, $source, $codePoint, &$error) { + $error = U_ZERO_ERROR; + switch ($codePoint) { + case 0x00F1: return "A"; + case 0x00F2: return ord("B"); + case 0x00F3: return array("C"); + case 0x00F4: return NULL; + } + } + +} + +$c = new MyConverter('ascii', 'utf-8'); +// This line will trigger toUCallback +var_dump($c->convert("\x80\x81\x82\x83")); +// This line will trigger fromUCallback +var_dump($c->convert("\xC3\xB1\xC3\xB2\xC3\xB3\xC3\xB4")); +--EXPECT-- +string(3) "abc" +string(3) "ABC" diff --git a/ext/intl/tests/uconverter_oop_subst.phpt b/ext/intl/tests/uconverter_oop_subst.phpt new file mode 100644 index 0000000000..d21d95f8d0 --- /dev/null +++ b/ext/intl/tests/uconverter_oop_subst.phpt @@ -0,0 +1,24 @@ +--TEST-- +Basic UConverter::convert() w/ Subsitution +--SKIPIF-- +<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?> +--INI-- +intl.use_exceptions=false +--FILE-- +<?php +$c = new UConverter('ascii', 'utf-8'); + +foreach(array('?','','<unknown>') as $subst) { + if (!$c->setSubstChars($subst)) { + echo "**Disallowed\n"; + continue; + } + var_dump($c->convert("This is an ascii string")); + var_dump($c->convert("Snowman: (\xE2\x98\x83)")); +} + +--EXPECT-- +string(23) "This is an ascii string" +string(12) "Snowman: (?)" +**Disallowed +**Disallowed diff --git a/ext/mysqlnd/mysqlnd_structs.h b/ext/mysqlnd/mysqlnd_structs.h index ecb1d89216..b88004a9f1 100644 --- a/ext/mysqlnd/mysqlnd_structs.h +++ b/ext/mysqlnd/mysqlnd_structs.h @@ -195,7 +195,7 @@ typedef struct st_mysqlnd_net_options unsigned int timeout_read; unsigned int timeout_write; - unsigned int net_read_buffer_size; + size_t net_read_buffer_size; /* SSL information */ char *ssl_key; diff --git a/ext/pdo/pdo_stmt.c b/ext/pdo/pdo_stmt.c index a469d09fc2..5dc445ff8d 100644 --- a/ext/pdo/pdo_stmt.c +++ b/ext/pdo/pdo_stmt.c @@ -2733,6 +2733,7 @@ static union _zend_function *row_get_ctor(zval *object TSRMLS_DC) ctor.function_name = "__construct"; ctor.scope = pdo_row_ce; ctor.handler = ZEND_FN(dbstmt_constructor); + ctor.fn_flags = ZEND_ACC_PUBLIC; return (union _zend_function*)&ctor; } diff --git a/ext/pdo/tests/pdo_036.phpt b/ext/pdo/tests/pdo_036.phpt index 94006c9e80..55c88762ba 100644 --- a/ext/pdo/tests/pdo_036.phpt +++ b/ext/pdo/tests/pdo_036.phpt @@ -5,19 +5,19 @@ Testing PDORow and PDOStatement instances with Reflection --FILE-- <?php -$instance = new reflectionclass('pdorow'); +$instance = new reflectionclass('pdostatement'); $x = $instance->newInstance(); var_dump($x); -$instance = new reflectionclass('pdostatement'); +$instance = new reflectionclass('pdorow'); $x = $instance->newInstance(); var_dump($x); ?> --EXPECTF-- -object(PDORow)#%d (0) { -} object(PDOStatement)#%d (1) { [%u|b%"queryString"]=> NULL } + +Fatal error: PDORow::__construct(): You should not create a PDOStatement manually in %spdo_036.php on line %d diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index ff3a6a5087..15befa2fc7 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -4192,32 +4192,40 @@ ZEND_METHOD(reflection_class, newInstance) { zval *retval_ptr = NULL; reflection_object *intern; - zend_class_entry *ce; + zend_class_entry *ce, *old_scope; + zend_function *constructor; METHOD_NOTSTATIC(reflection_class_ptr); GET_REFLECTION_OBJECT_PTR(ce); + object_init_ex(return_value, ce); + + old_scope = EG(scope); + EG(scope) = ce; + constructor = Z_OBJ_HT_P(return_value)->get_constructor(return_value TSRMLS_CC); + EG(scope) = old_scope; + /* Run the constructor if there is one */ - if (ce->constructor) { + if (constructor) { zval ***params = NULL; int num_args = 0; zend_fcall_info fci; zend_fcall_info_cache fcc; - if (!(ce->constructor->common.fn_flags & ZEND_ACC_PUBLIC)) { + if (!(constructor->common.fn_flags & ZEND_ACC_PUBLIC)) { zend_throw_exception_ex(reflection_exception_ptr, 0 TSRMLS_CC, "Access to non-public constructor of class %s", ce->name); - return; + zval_dtor(return_value); + RETURN_NULL(); } if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "*", ¶ms, &num_args) == FAILURE) { if (params) { efree(params); } + zval_dtor(return_value); RETURN_FALSE; } - object_init_ex(return_value, ce); - fci.size = sizeof(fci); fci.function_table = EG(function_table); fci.function_name = NULL; @@ -4229,7 +4237,7 @@ ZEND_METHOD(reflection_class, newInstance) fci.no_separation = 1; fcc.initialized = 1; - fcc.function_handler = ce->constructor; + fcc.function_handler = constructor; fcc.calling_scope = EG(scope); fcc.called_scope = Z_OBJCE_P(return_value); fcc.object_ptr = return_value; @@ -4242,6 +4250,7 @@ ZEND_METHOD(reflection_class, newInstance) zval_ptr_dtor(&retval_ptr); } php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invocation of %s's constructor failed", ce->name); + zval_dtor(return_value); RETURN_NULL(); } if (retval_ptr) { @@ -4250,9 +4259,7 @@ ZEND_METHOD(reflection_class, newInstance) if (params) { efree(params); } - } else if (!ZEND_NUM_ARGS()) { - object_init_ex(return_value, ce); - } else { + } else if (ZEND_NUM_ARGS()) { zend_throw_exception_ex(reflection_exception_ptr, 0 TSRMLS_CC, "Class %s does not have a constructor, so you cannot pass any constructor arguments", ce->name); } } @@ -4282,9 +4289,10 @@ ZEND_METHOD(reflection_class, newInstanceArgs) { zval *retval_ptr = NULL; reflection_object *intern; - zend_class_entry *ce; + zend_class_entry *ce, *old_scope; int argc = 0; HashTable *args; + zend_function *constructor; METHOD_NOTSTATIC(reflection_class_ptr); @@ -4293,19 +4301,28 @@ ZEND_METHOD(reflection_class, newInstanceArgs) if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|h", &args) == FAILURE) { return; } + if (ZEND_NUM_ARGS() > 0) { argc = args->nNumOfElements; } + object_init_ex(return_value, ce); + + old_scope = EG(scope); + EG(scope) = ce; + constructor = Z_OBJ_HT_P(return_value)->get_constructor(return_value TSRMLS_CC); + EG(scope) = old_scope; + /* Run the constructor if there is one */ - if (ce->constructor) { + if (constructor) { zval ***params = NULL; zend_fcall_info fci; zend_fcall_info_cache fcc; - if (!(ce->constructor->common.fn_flags & ZEND_ACC_PUBLIC)) { + if (!(constructor->common.fn_flags & ZEND_ACC_PUBLIC)) { zend_throw_exception_ex(reflection_exception_ptr, 0 TSRMLS_CC, "Access to non-public constructor of class %s", ce->name); - return; + zval_dtor(return_value); + RETURN_NULL(); } if (argc) { @@ -4314,8 +4331,6 @@ ZEND_METHOD(reflection_class, newInstanceArgs) params -= argc; } - object_init_ex(return_value, ce); - fci.size = sizeof(fci); fci.function_table = EG(function_table); fci.function_name = NULL; @@ -4327,7 +4342,7 @@ ZEND_METHOD(reflection_class, newInstanceArgs) fci.no_separation = 1; fcc.initialized = 1; - fcc.function_handler = ce->constructor; + fcc.function_handler = constructor; fcc.calling_scope = EG(scope); fcc.called_scope = Z_OBJCE_P(return_value); fcc.object_ptr = return_value; @@ -4340,6 +4355,7 @@ ZEND_METHOD(reflection_class, newInstanceArgs) zval_ptr_dtor(&retval_ptr); } php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invocation of %s's constructor failed", ce->name); + zval_dtor(return_value); RETURN_NULL(); } if (retval_ptr) { @@ -4348,9 +4364,7 @@ ZEND_METHOD(reflection_class, newInstanceArgs) if (params) { efree(params); } - } else if (!ZEND_NUM_ARGS() || !argc) { - object_init_ex(return_value, ce); - } else { + } else if (argc) { zend_throw_exception_ex(reflection_exception_ptr, 0 TSRMLS_CC, "Class %s does not have a constructor, so you cannot pass any constructor arguments", ce->name); } } diff --git a/ext/reflection/tests/bug64007.phpt b/ext/reflection/tests/bug64007.phpt new file mode 100644 index 0000000000..32ec6a5610 --- /dev/null +++ b/ext/reflection/tests/bug64007.phpt @@ -0,0 +1,19 @@ +--TEST-- +Bug #64007 (There is an ability to create instance of Generator by hand) +--FILE-- +<?php +$reflection = new ReflectionClass('Generator'); +try { + $generator = $reflection->newInstanceWithoutConstructor(); + var_dump($generator); +} catch (Exception $e) { + var_dump($e->getMessage()); +} + +$generator = $reflection->newInstance(); +var_dump($generator); +?> +--EXPECTF-- +string(97) "Class Generator is an internal class that cannot be instantiated without invoking its constructor" + +Catchable fatal error: The "Generator" class is reserved for internal use and cannot be manually instantiated in %sbug64007.php on line %d diff --git a/ext/spl/spl_directory.c b/ext/spl/spl_directory.c index 61d6324d52..f43a3709e1 100644 --- a/ext/spl/spl_directory.c +++ b/ext/spl/spl_directory.c @@ -1874,6 +1874,10 @@ static int spl_filesystem_object_cast(zval *readobj, zval *writeobj, int type TS spl_filesystem_object *intern = (spl_filesystem_object*)zend_object_store_get_object(readobj TSRMLS_CC); if (type == IS_STRING) { + if (Z_OBJCE_P(readobj)->__tostring) { + return std_object_handlers.cast_object(readobj, writeobj, type TSRMLS_CC); + } + switch (intern->type) { case SPL_FS_INFO: case SPL_FS_FILE: diff --git a/ext/spl/tests/bug64023.phpt b/ext/spl/tests/bug64023.phpt new file mode 100644 index 0000000000..2c177f9512 --- /dev/null +++ b/ext/spl/tests/bug64023.phpt @@ -0,0 +1,20 @@ +--TEST-- +Bug #64023: Overloading __toString() in SplFileInfo has no effect +--FILE-- +<?php +class A extends \SplFileInfo +{ + public function __toString() {return ' -expected- ';} +} + +$a = new A('/'); + +// Works +echo $a, $a->__toString(), $a->__toString() . '', "\n"; + +// Does not work - outputs parent::__toString() +echo $a . '', "\n"; + +--EXPECT-- + -expected- -expected- -expected- + -expected- diff --git a/ext/standard/html.c b/ext/standard/html.c index 79a6737ca5..414fa65c91 100644 --- a/ext/standard/html.c +++ b/ext/standard/html.c @@ -1628,8 +1628,8 @@ PHP_FUNCTION(get_html_translation_table) unsigned i, j, k, max_i, max_j, max_k; /* no mapping to unicode required */ - if (CHARSET_SINGLE_BYTE(charset)) { - max_i = 1; max_j = 1; max_k = 64; + if (CHARSET_SINGLE_BYTE(charset)) { /* ISO-8859-1 */ + max_i = 1; max_j = 4; max_k = 64; } else { max_i = 0x1E; max_j = 64; max_k = 64; } diff --git a/ext/standard/http_fopen_wrapper.c b/ext/standard/http_fopen_wrapper.c index 85a61167aa..870f904e9c 100644 --- a/ext/standard/http_fopen_wrapper.c +++ b/ext/standard/http_fopen_wrapper.c @@ -113,6 +113,7 @@ php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper, char *path, int redirected = ((flags & HTTP_WRAPPER_REDIRECTED) != 0); int follow_location = 1; php_stream_filter *transfer_encoding = NULL; + int response_code; tmp_line[0] = '\0'; @@ -657,7 +658,6 @@ finish: if (php_stream_get_line(stream, tmp_line, sizeof(tmp_line) - 1, &tmp_line_len) != NULL) { zval *http_response; - int response_code; if (tmp_line_len > 9) { response_code = atoi(tmp_line + 9); @@ -731,7 +731,9 @@ finish: http_header_line[http_header_line_length] = '\0'; if (!strncasecmp(http_header_line, "Location: ", 10)) { - if (context && php_stream_context_get_option(context, "http", "follow_location", &tmpzval) == SUCCESS) { + /* we only care about Location for 300, 301, 302, 303 and 307 */ + /* see http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.1 */ + if ((response_code >= 300 && response_code < 304 || 307 == response_code) && context && php_stream_context_get_option(context, "http", "follow_location", &tmpzval) == SUCCESS) { SEPARATE_ZVAL(tmpzval); convert_to_long_ex(tmpzval); follow_location = Z_LVAL_PP(tmpzval); diff --git a/ext/standard/tests/strings/get_html_translation_table_basic10.phpt b/ext/standard/tests/strings/get_html_translation_table_basic10.phpt new file mode 100644 index 0000000000..a5a356885d --- /dev/null +++ b/ext/standard/tests/strings/get_html_translation_table_basic10.phpt @@ -0,0 +1,121 @@ +--TEST-- +Test get_html_translation_table() function: htmlentities/HTML 4/ISO-8859-1 (bug #64011) +--FILE-- +<?php + +function so($a,$b) { return ord($a) - ord($b); } + +$table = HTML_ENTITIES; +$tt = get_html_translation_table($table, ENT_COMPAT, "ISO-8859-1"); +uksort( $tt, 'so' ); +var_dump( count($tt) ); +print_r( $tt ); +echo "Done\n"; + +?> +--EXPECT-- +int(100) +Array +( + ["] => " + [&] => & + [<] => < + [>] => > + [ ] => + [¡] => ¡ + [¢] => ¢ + [£] => £ + [¤] => ¤ + [¥] => ¥ + [¦] => ¦ + [§] => § + [¨] => ¨ + [©] => © + [ª] => ª + [«] => « + [¬] => ¬ + [] => ­ + [®] => ® + [¯] => ¯ + [°] => ° + [±] => ± + [²] => ² + [³] => ³ + [´] => ´ + [µ] => µ + [¶] => ¶ + [·] => · + [¸] => ¸ + [¹] => ¹ + [º] => º + [»] => » + [¼] => ¼ + [½] => ½ + [¾] => ¾ + [¿] => ¿ + [À] => À + [Á] => Á + [Â] =>  + [Ã] => à + [Ä] => Ä + [Å] => Å + [Æ] => Æ + [Ç] => Ç + [È] => È + [É] => É + [Ê] => Ê + [Ë] => Ë + [Ì] => Ì + [Í] => Í + [Î] => Î + [Ï] => Ï + [Ð] => Ð + [Ñ] => Ñ + [Ò] => Ò + [Ó] => Ó + [Ô] => Ô + [Õ] => Õ + [Ö] => Ö + [×] => × + [Ø] => Ø + [Ù] => Ù + [Ú] => Ú + [Û] => Û + [Ü] => Ü + [Ý] => Ý + [Þ] => Þ + [ß] => ß + [à] => à + [á] => á + [â] => â + [ã] => ã + [ä] => ä + [å] => å + [æ] => æ + [ç] => ç + [è] => è + [é] => é + [ê] => ê + [ë] => ë + [ì] => ì + [í] => í + [î] => î + [ï] => ï + [ð] => ð + [ñ] => ñ + [ò] => ò + [ó] => ó + [ô] => ô + [õ] => õ + [ö] => ö + [÷] => ÷ + [ø] => ø + [ù] => ù + [ú] => ú + [û] => û + [ü] => ü + [ý] => ý + [þ] => þ + [ÿ] => ÿ +) +Done diff --git a/ext/standard/var_unserializer.c b/ext/standard/var_unserializer.c index 857b2d4568..3730ff8e81 100644 --- a/ext/standard/var_unserializer.c +++ b/ext/standard/var_unserializer.c @@ -1,4 +1,4 @@ -/* Generated by re2c 0.13.5 */ +/* Generated by re2c 0.13.5 on Mon Jan 21 11:41:53 2013 */ #line 1 "ext/standard/var_unserializer.re" /* +----------------------------------------------------------------------+ @@ -427,7 +427,7 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER) -#line 425 "ext/standard/var_unserializer.c" +#line 431 "ext/standard/var_unserializer.c" { YYCTYPE yych; static const unsigned char yybm[] = { @@ -487,9 +487,9 @@ yy2: yych = *(YYMARKER = ++YYCURSOR); if (yych == ':') goto yy95; yy3: -#line 747 "ext/standard/var_unserializer.re" +#line 759 "ext/standard/var_unserializer.re" { return 0; } -#line 487 "ext/standard/var_unserializer.c" +#line 493 "ext/standard/var_unserializer.c" yy4: yych = *(YYMARKER = ++YYCURSOR); if (yych == ':') goto yy89; @@ -532,13 +532,13 @@ yy13: goto yy3; yy14: ++YYCURSOR; -#line 741 "ext/standard/var_unserializer.re" +#line 753 "ext/standard/var_unserializer.re" { /* this is the case where we have less data than planned */ php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Unexpected end of serialized data"); return 0; /* not sure if it should be 0 or 1 here? */ } -#line 536 "ext/standard/var_unserializer.c" +#line 542 "ext/standard/var_unserializer.c" yy16: yych = *++YYCURSOR; goto yy3; @@ -568,7 +568,7 @@ yy20: yych = *++YYCURSOR; if (yych != '"') goto yy18; ++YYCURSOR; -#line 624 "ext/standard/var_unserializer.re" +#line 630 "ext/standard/var_unserializer.re" { size_t len, len2, len3, maxlen; long elements; @@ -691,7 +691,7 @@ yy20: return object_common2(UNSERIALIZE_PASSTHRU, elements); } -#line 683 "ext/standard/var_unserializer.c" +#line 695 "ext/standard/var_unserializer.c" yy25: yych = *++YYCURSOR; if (yych <= ',') { @@ -716,7 +716,7 @@ yy27: yych = *++YYCURSOR; if (yych != '"') goto yy18; ++YYCURSOR; -#line 616 "ext/standard/var_unserializer.re" +#line 622 "ext/standard/var_unserializer.re" { INIT_PZVAL(*rval); @@ -724,7 +724,7 @@ yy27: return object_common2(UNSERIALIZE_PASSTHRU, object_common1(UNSERIALIZE_PASSTHRU, ZEND_STANDARD_CLASS_DEF_PTR)); } -#line 716 "ext/standard/var_unserializer.c" +#line 728 "ext/standard/var_unserializer.c" yy32: yych = *++YYCURSOR; if (yych == '+') goto yy33; @@ -745,7 +745,7 @@ yy34: yych = *++YYCURSOR; if (yych != '{') goto yy18; ++YYCURSOR; -#line 596 "ext/standard/var_unserializer.re" +#line 602 "ext/standard/var_unserializer.re" { long elements = parse_iv(start + 2); /* use iv() not uiv() in order to check data range */ @@ -765,7 +765,7 @@ yy34: return finish_nested_data(UNSERIALIZE_PASSTHRU); } -#line 757 "ext/standard/var_unserializer.c" +#line 769 "ext/standard/var_unserializer.c" yy39: yych = *++YYCURSOR; if (yych == '+') goto yy40; @@ -786,7 +786,7 @@ yy41: yych = *++YYCURSOR; if (yych != '"') goto yy18; ++YYCURSOR; -#line 567 "ext/standard/var_unserializer.re" +#line 573 "ext/standard/var_unserializer.re" { size_t len, maxlen; char *str; @@ -815,7 +815,7 @@ yy41: ZVAL_STRINGL(*rval, str, len, 0); return 1; } -#line 807 "ext/standard/var_unserializer.c" +#line 819 "ext/standard/var_unserializer.c" yy46: yych = *++YYCURSOR; if (yych == '+') goto yy47; @@ -836,7 +836,7 @@ yy48: yych = *++YYCURSOR; if (yych != '"') goto yy18; ++YYCURSOR; -#line 539 "ext/standard/var_unserializer.re" +#line 545 "ext/standard/var_unserializer.re" { size_t len, maxlen; char *str; @@ -864,7 +864,7 @@ yy48: ZVAL_STRINGL(*rval, str, len, 1); return 1; } -#line 856 "ext/standard/var_unserializer.c" +#line 868 "ext/standard/var_unserializer.c" yy53: yych = *++YYCURSOR; if (yych <= '/') { @@ -952,7 +952,7 @@ yy61: } yy63: ++YYCURSOR; -#line 529 "ext/standard/var_unserializer.re" +#line 535 "ext/standard/var_unserializer.re" { #if SIZEOF_LONG == 4 use_double: @@ -962,7 +962,7 @@ use_double: ZVAL_DOUBLE(*rval, zend_strtod((const char *)start + 2, NULL)); return 1; } -#line 954 "ext/standard/var_unserializer.c" +#line 966 "ext/standard/var_unserializer.c" yy65: yych = *++YYCURSOR; if (yych <= ',') { @@ -1021,7 +1021,7 @@ yy73: yych = *++YYCURSOR; if (yych != ';') goto yy18; ++YYCURSOR; -#line 514 "ext/standard/var_unserializer.re" +#line 520 "ext/standard/var_unserializer.re" { *p = YYCURSOR; INIT_PZVAL(*rval); @@ -1036,7 +1036,7 @@ yy73: return 1; } -#line 1028 "ext/standard/var_unserializer.c" +#line 1040 "ext/standard/var_unserializer.c" yy76: yych = *++YYCURSOR; if (yych == 'N') goto yy73; @@ -1063,7 +1063,7 @@ yy79: if (yych <= '9') goto yy79; if (yych != ';') goto yy18; ++YYCURSOR; -#line 487 "ext/standard/var_unserializer.re" +#line 493 "ext/standard/var_unserializer.re" { #if SIZEOF_LONG == 4 int digits = YYCURSOR - start - 3; @@ -1090,7 +1090,7 @@ yy79: ZVAL_LONG(*rval, parse_iv(start + 2)); return 1; } -#line 1082 "ext/standard/var_unserializer.c" +#line 1094 "ext/standard/var_unserializer.c" yy83: yych = *++YYCURSOR; if (yych <= '/') goto yy18; @@ -1098,24 +1098,24 @@ yy83: yych = *++YYCURSOR; if (yych != ';') goto yy18; ++YYCURSOR; -#line 480 "ext/standard/var_unserializer.re" +#line 486 "ext/standard/var_unserializer.re" { *p = YYCURSOR; INIT_PZVAL(*rval); ZVAL_BOOL(*rval, parse_iv(start + 2)); return 1; } -#line 1097 "ext/standard/var_unserializer.c" +#line 1109 "ext/standard/var_unserializer.c" yy87: ++YYCURSOR; -#line 473 "ext/standard/var_unserializer.re" +#line 479 "ext/standard/var_unserializer.re" { *p = YYCURSOR; INIT_PZVAL(*rval); ZVAL_NULL(*rval); return 1; } -#line 1107 "ext/standard/var_unserializer.c" +#line 1119 "ext/standard/var_unserializer.c" yy89: yych = *++YYCURSOR; if (yych <= ',') { @@ -1138,7 +1138,7 @@ yy91: if (yych <= '9') goto yy91; if (yych != ';') goto yy18; ++YYCURSOR; -#line 450 "ext/standard/var_unserializer.re" +#line 456 "ext/standard/var_unserializer.re" { long id; @@ -1161,7 +1161,7 @@ yy91: return 1; } -#line 1153 "ext/standard/var_unserializer.c" +#line 1165 "ext/standard/var_unserializer.c" yy95: yych = *++YYCURSOR; if (yych <= ',') { @@ -1184,7 +1184,7 @@ yy97: if (yych <= '9') goto yy97; if (yych != ';') goto yy18; ++YYCURSOR; -#line 429 "ext/standard/var_unserializer.re" +#line 435 "ext/standard/var_unserializer.re" { long id; @@ -1205,9 +1205,9 @@ yy97: return 1; } -#line 1197 "ext/standard/var_unserializer.c" +#line 1209 "ext/standard/var_unserializer.c" } -#line 749 "ext/standard/var_unserializer.re" +#line 761 "ext/standard/var_unserializer.re" return 0; diff --git a/ext/standard/var_unserializer.re b/ext/standard/var_unserializer.re index e54449a78c..204995783f 100644 --- a/ext/standard/var_unserializer.re +++ b/ext/standard/var_unserializer.re @@ -678,10 +678,13 @@ object ":" uiv ":" ["] { do { /* Try to find class directly */ + BG(serialize_lock) = 1; if (zend_lookup_class(class_name, len2, &pce TSRMLS_CC) == SUCCESS) { + BG(serialize_lock) = 0; ce = *pce; break; } + BG(serialize_lock) = 0; /* Check for unserialize callback */ if ((PG(unserialize_callback_func) == NULL) || (PG(unserialize_callback_func)[0] == '\0')) { @@ -696,7 +699,9 @@ object ":" uiv ":" ["] { args[0] = &arg_func_name; MAKE_STD_ZVAL(arg_func_name); ZVAL_STRING(arg_func_name, class_name, 1); + BG(serialize_lock) = 1; if (call_user_function_ex(CG(function_table), NULL, user_func, &retval_ptr, 1, args, 0, NULL TSRMLS_CC) != SUCCESS) { + BG(serialize_lock) = 0; php_error_docref(NULL TSRMLS_CC, E_WARNING, "defined (%s) but not found", user_func->value.str.val); incomplete_class = 1; ce = PHP_IC_ENTRY; @@ -704,6 +709,7 @@ object ":" uiv ":" ["] { zval_ptr_dtor(&arg_func_name); break; } + BG(serialize_lock) = 0; if (retval_ptr) { zval_ptr_dtor(&retval_ptr); } diff --git a/main/main.c b/main/main.c index f87a1427fe..325ef7ed53 100644 --- a/main/main.c +++ b/main/main.c @@ -521,6 +521,7 @@ PHP_INI_BEGIN() STD_PHP_INI_ENTRY("default_mimetype", SAPI_DEFAULT_MIMETYPE, PHP_INI_ALL, OnUpdateString, default_mimetype, sapi_globals_struct,sapi_globals) STD_PHP_INI_ENTRY("error_log", NULL, PHP_INI_ALL, OnUpdateErrorLog, error_log, php_core_globals, core_globals) STD_PHP_INI_ENTRY("extension_dir", PHP_EXTENSION_DIR, PHP_INI_SYSTEM, OnUpdateStringUnempty, extension_dir, php_core_globals, core_globals) + STD_PHP_INI_ENTRY("sys_temp_dir", NULL, PHP_INI_SYSTEM, OnUpdateStringUnempty, sys_temp_dir, php_core_globals, core_globals) STD_PHP_INI_ENTRY("include_path", PHP_INCLUDE_PATH, PHP_INI_ALL, OnUpdateStringUnempty, include_path, php_core_globals, core_globals) PHP_INI_ENTRY("max_execution_time", "30", PHP_INI_ALL, OnUpdateTimeout) STD_PHP_INI_ENTRY("open_basedir", NULL, PHP_INI_ALL, OnUpdateBaseDir, open_basedir, php_core_globals, core_globals) diff --git a/main/php_globals.h b/main/php_globals.h index 170431d079..256765d665 100644 --- a/main/php_globals.h +++ b/main/php_globals.h @@ -85,6 +85,7 @@ struct _php_core_globals { char *open_basedir; char *extension_dir; char *php_binary; + char *sys_temp_dir; char *upload_tmp_dir; long upload_max_filesize; diff --git a/main/php_open_temporary_file.c b/main/php_open_temporary_file.c index b43d6a7835..054d497be6 100644 --- a/main/php_open_temporary_file.c +++ b/main/php_open_temporary_file.c @@ -189,13 +189,28 @@ PHPAPI void php_shutdown_temporary_directory(void) /* * Determine where to place temporary files. */ -PHPAPI const char* php_get_temporary_directory(void) +PHPAPI const char* php_get_temporary_directory(TSRMLS_D) { /* Did we determine the temporary directory already? */ if (temporary_directory) { return temporary_directory; } + /* Is there a temporary directory "sys_temp_dir" in .ini defined? */ + { + char *sys_temp_dir = PG(sys_temp_dir); + if (sys_temp_dir) { + int len = strlen(sys_temp_dir); + if (len >= 2 && sys_temp_dir[len - 1] == DEFAULT_SLASH) { + temporary_directory = zend_strndup(sys_temp_dir, len - 1); + return temporary_directory; + } else if (len >= 1 && sys_temp_dir[len - 1] != DEFAULT_SLASH) { + temporary_directory = zend_strndup(sys_temp_dir, len); + return temporary_directory; + } + } + } + #ifdef PHP_WIN32 /* We can't count on the environment variables TEMP or TMP, * and so must make the Win32 API call to get the default @@ -263,7 +278,7 @@ PHPAPI int php_open_temporary_fd_ex(const char *dir, const char *pfx, char **ope if (!dir || *dir == '\0') { def_tmp: - temp_dir = php_get_temporary_directory(); + temp_dir = php_get_temporary_directory(TSRMLS_C); if (temp_dir && *temp_dir != '\0' && (!open_basedir_check || !php_check_open_basedir(temp_dir TSRMLS_CC))) { return php_do_open_temporary_file(temp_dir, pfx, opened_path_p TSRMLS_CC); @@ -294,12 +309,12 @@ PHPAPI FILE *php_open_temporary_file(const char *dir, const char *pfx, char **op if (fd == -1) { return NULL; } - + fp = fdopen(fd, "r+b"); if (fp == NULL) { close(fd); } - + return fp; } /* }}} */ diff --git a/main/streams/php_stream_plain_wrapper.h b/main/streams/php_stream_plain_wrapper.h index 4b3875577d..d88b30c479 100644 --- a/main/streams/php_stream_plain_wrapper.h +++ b/main/streams/php_stream_plain_wrapper.h @@ -31,7 +31,7 @@ PHPAPI php_stream *_php_stream_fopen(const char *filename, const char *mode, cha #define php_stream_fopen(filename, mode, opened) _php_stream_fopen((filename), (mode), (opened), 0 STREAMS_CC TSRMLS_CC) PHPAPI php_stream *_php_stream_fopen_with_path(char *filename, char *mode, char *path, char **opened_path, int options STREAMS_DC TSRMLS_DC); -#define php_stream_fopen_with_path(filename, mode, path, opened) _php_stream_fopen_with_path((filename), (mode), (path), (opened) STREAMS_CC TSRMLS_CC) +#define php_stream_fopen_with_path(filename, mode, path, opened) _php_stream_fopen_with_path((filename), (mode), (path), (opened), 0 STREAMS_CC TSRMLS_CC) PHPAPI php_stream *_php_stream_fopen_from_file(FILE *file, const char *mode STREAMS_DC TSRMLS_DC); #define php_stream_fopen_from_file(file, mode) _php_stream_fopen_from_file((file), (mode) STREAMS_CC TSRMLS_CC) diff --git a/php.ini-development b/php.ini-development index a97cd0f9da..93a4b7d8a5 100644 --- a/php.ini-development +++ b/php.ini-development @@ -729,6 +729,10 @@ user_dir = ; On windows: ; extension_dir = "ext" +; Directory where the temporary files should be placed. +; Defaults to the system default (see sys_get_temp_dir) +; sys_temp_dir = "/tmp" + ; Whether or not to enable the dl() function. The dl() function does NOT work ; properly in multithreaded servers, such as IIS or Zeus, and is automatically ; disabled on them. diff --git a/php.ini-production b/php.ini-production index dce8fc970c..7d84c9b879 100644 --- a/php.ini-production +++ b/php.ini-production @@ -729,6 +729,10 @@ user_dir = ; On windows: ; extension_dir = "ext" +; Directory where the temporary files should be placed. +; Defaults to the system default (see sys_get_temp_dir) +; sys_temp_dir = "/tmp" + ; Whether or not to enable the dl() function. The dl() function does NOT work ; properly in multithreaded servers, such as IIS or Zeus, and is automatically ; disabled on them. diff --git a/tests/basic/req60524.phpt b/tests/basic/req60524.phpt new file mode 100644 index 0000000000..6803e1fd88 --- /dev/null +++ b/tests/basic/req60524.phpt @@ -0,0 +1,8 @@ +--TEST-- +Req #60524 (Specify temporary directory) +--INI-- +sys_temp_dir=/path/to/temp/dir +--FILE-- +<?php echo sys_get_temp_dir(); ?> +--EXPECT-- +/path/to/temp/dir diff --git a/tests/classes/unset_properties.phpt b/tests/classes/unset_properties.phpt index 7f9b569887..264e720c9e 100644 --- a/tests/classes/unset_properties.phpt +++ b/tests/classes/unset_properties.phpt @@ -140,15 +140,15 @@ true new publicProperty value via public access protectedProperty set -__isset "protectedProperty"__isset "protectedProperty"false +__isset "protectedProperty"false __get "protectedProperty" __set "protectedProperty" to "new protectedProperty value via setter" __isset "protectedProperty"true new protectedProperty value via setter privateProperty set -__isset "privateProperty"__isset "privateProperty"false +__isset "privateProperty"false __get "privateProperty" __set "privateProperty" to "new privateProperty value via setter" __isset "privateProperty"true -new privateProperty value via setter
\ No newline at end of file +new privateProperty value via setter |
