Commit 54098cf0 authored by Ondrej Sury's avatar Ondrej Sury

Imported Upstream version 5.5.0~alpha4

parent 8572aeb0
PHP NEWS
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
10 Jan 2012, PHP 5.5.0 Alpha 3
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)
......@@ -232,4 +258,7 @@ PHP NEWS
. Fixed bug #63248 (Load multiple magic files from a directory under Windows).
(Anatoliy)
- General improvements:
. Implemented FR #46487 (Dereferencing process-handles no longer waits on those processes). (Jille Timmermans)
<<< NOTE: Insert NEWS from last stable release here prior to actual release! >>>
......@@ -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
......
......@@ -105,10 +105,11 @@ PHP 5.5 UPGRADE NOTES
- pack()/unpack() had the following changes, which bring it more in line
with Perl's behavior:
- Implemented format character "Z": NUL-padded string
- "a" now does not remove trailing NUL characters on unpack() anymore
- "A" will now strip all trailing ASCII whitespace on unpack() (it used to
remove only trailing spaces).
- Implemented format character "Z": NULL padded string, with trailing NULL
bytes removed.
- Changed format character "a": this no longer removes trailing NULL bytes.
- Changed format character "A": all trailing ASCII whitespace is now removed
(defined as spaces, tabs, \r, \n and NULL).
- MessageFormatter::format() and related functions now accepted named arguments
and mixed numeric/named arguments in ICU 4.8+.
- MessageFormatter::format() and related functions now don't error out when
......
......@@ -5,6 +5,7 @@ UPGRADE NOTES - PHP X.Y
1. Internal API changes
a. Streams pooling API
b. Lowercasing and locales
c. zend_qsort_r
2. Build system changes
a. Unix build system changes
......@@ -53,6 +54,16 @@ such as strcasecmp, will be using locale rules.
Two new functions - zend_binary_strncasecmp_l and zend_binary_strcasecmp_l - added as
locale-based counterparts to zend_binary_strcasecmp and zend_binary_strncasecmp.
c. zend_qsort_r
Added the function zend_qsort_r():
typedef int (*compare_r_func_t)(const void *, const void * TSRMLS_DC, void *);
void zend_qsort_r(void *base, size_t nmemb, size_t siz, compare_r_func_t compare, void *arg TSRMLS_DC);
The extra argument it has (relatively to zend_qsort()) is passed to the
comparison function.
========================
2. Build system changes
========================
......
......@@ -15,7 +15,7 @@ $f = fopen("/tmp/blah", "r");
?>
===DONE===
--EXPECTF--
fopen(/tmp/blah): failed to open stream: No such file or directory (2) in %s:%d
fopen(/tmp/blah): failed to open stream: %s (2) in %s:%d
Warning: fopen(/tmp/blah): failed to open stream: No such file or directory in %s on line %d
Warning: fopen(/tmp/blah): failed to open stream: %s in %s on line %d
===DONE===
......@@ -10,7 +10,7 @@ set_error_handler(function($errno, $errstr, $errfile, $errline){
require 'notfound.php';
--EXPECTF--
error(require(notfound.php): failed to open stream: No such file or directory)
error(require(notfound.php): failed to open stream: %s)
Warning: Uncaught exception 'Exception' with message 'Foo' in %sbug60909_1.php:5
Stack trace:
#0 %sbug60909_1.php(8): {closure}(2, 'require(notfoun...', '%s', 8, Array)
......
--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
--TEST--
Bug #63982 (isset() inconsistently produces a fatal error on protected property)
--FILE--
<?php
class Test {
protected $protectedProperty;
}
$test = new Test();
var_dump(isset($test->protectedProperty));
var_dump(isset($test->protectedProperty->foo));
--EXPECTF--
bool(false)
bool(false)
--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"
--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
--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
--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
--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
--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
--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
......@@ -158,9 +158,10 @@ static void print_hash(zend_write_func_t write_func, HashTable *ht, int indent,
case HASH_KEY_IS_STRING:
if (is_object) {
const char *prop_name, *class_name;
int mangled = zend_unmangle_property_name(string_key, str_len - 1, &class_name, &prop_name);
int prop_len;
int mangled = zend_unmangle_property_name_ex(string_key, str_len - 1, &class_name, &prop_name, &prop_len);
ZEND_PUTS_EX(prop_name);
ZEND_WRITE_EX(prop_name, prop_len);
if (class_name && mangled == SUCCESS) {
if (class_name[0]=='*') {
ZEND_PUTS_EX(":protected");
......
......@@ -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;
......
......@@ -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);
......
......@@ -987,7 +987,7 @@ ZEND_FUNCTION(get_object_vars)
HashPosition pos;
char *key;
const char *prop_name, *class_name;
uint key_len;
uint key_len, prop_len;
ulong num_index;
zend_object *zobj;
......@@ -1014,10 +1014,10 @@ ZEND_FUNCTION(get_object_vars)
while (zend_hash_get_current_data_ex(properties, (void **) &value, &pos) == SUCCESS) {
if (zend_hash_get_current_key_ex(properties, &key, &key_len, &num_index, 0, &pos) == HASH_KEY_IS_STRING) {
if (zend_check_property_access(zobj, key, key_len-1 TSRMLS_CC) == SUCCESS) {
zend_unmangle_property_name(key, key_len-1, &class_name, &prop_name);
zend_unmangle_property_name_ex(key, key_len - 1, &class_name, &prop_name, &prop_len);
/* Not separating references */
Z_ADDREF_PP(value);
add_assoc_zval_ex(return_value, prop_name, strlen(prop_name)+1, *value);
add_assoc_zval_ex(return_value, prop_name, prop_len + 1, *value);
}
}
zend_hash_move_forward_ex(properties, &pos);
......
......@@ -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;
......@@ -4221,9 +4268,8 @@ static void zend_do_traits_property_binding(zend_class_entry *ce TSRMLS_DC) /* {
prop_name_length = property_info->name_length;
} else {
/* for private and protected we need to unmangle the names */
zend_unmangle_property_name(property_info->name, property_info->name_length,
&class_name_unused, &prop_name);
prop_name_length = strlen(prop_name);
zend_unmangle_property_name_ex(property_info->name, property_info->name_length,
&class_name_unused, &prop_name, &prop_name_length);
prop_hash = zend_get_hash_value(prop_name, prop_name_length + 1);
}
......@@ -5178,7 +5224,7 @@ static int zend_strnlen(const char* s, int maxlen) /* {{{ */
}
/* }}} */
ZEND_API int zend_unmangle_property_name(const char *mangled_property, int len, const char **class_name, const char **prop_name) /* {{{ */
ZEND_API int zend_unmangle_property_name_ex(const char *mangled_property, int len, const char **class_name, const char **prop_name, int *prop_len) /* {{{ */
{
int class_name_len;
......@@ -5186,22 +5232,34 @@ ZEND_API int zend_unmangle_property_name(const char *mangled_property, int len,
if (mangled_property[0]!=0) {
*prop_name = mangled_property;
if (prop_len) {
*prop_len = len;
}
return SUCCESS;
}
if (len < 3 || mangled_property[1]==0) {
zend_error(E_NOTICE, "Illegal member variable name");
*prop_name = mangled_property;
if (prop_len) {
*prop_len = len;
}
return FAILURE;
}
class_name_len = zend_strnlen(mangled_property+1, --len - 1) + 1;
class_name_len = zend_strnlen(mangled_property + 1, --len - 1) + 1;
if (class_name_len >= len || mangled_property[class_name_len]!=0) {
zend_error(E_NOTICE, "Corrupt member variable name");
*prop_name = mangled_property;
if (prop_len) {
*prop_len = len + 1;
}
return FAILURE;
}
*class_name = mangled_property+1;
*prop_name = (*class_name)+class_name_len;
*class_name = mangled_property + 1;
*prop_name = (*class_name) + class_name_len;
if (prop_len) {