Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ PHP NEWS
possible. (NickSdot)

- Curl:
. Improved cURL option validation errors to include the option name.
(Sjoerd Langkemper)
. Raise a value error when the callback registered with CURLOPT_READFUNCTION
returns an unexpected long. (Sjoerd Langkemper)

Expand Down Expand Up @@ -50,6 +52,7 @@ PHP NEWS
. Added the "filter.max_filter_count" stream context option for php://filter
URLs. Using more than 16 filters without configuring this option is now
deprecated. (Sjoerd Langkemper)
. Improved performance of array_intersect(). (mehmetcansahin)
. Fixed bug GH-23006 (phpcredits() full-page HTML title says phpinfo()).
(Weilin Du)
. The following functions now raise a ValueError when the $filename argument
Expand Down
9 changes: 9 additions & 0 deletions UPGRADING
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,14 @@ PHP 8.6 UPGRADE NOTES
SplTempFileObject; the two previously returned different values.

- Standard:
. array_intersect() with at least two arrays now converts values to strings
while scanning its inputs instead of during sort comparisons. This can
change the number and order of conversion warnings and __toString() calls,
which conversion exception is reached, and the result for stateful
__toString() implementations. Argument types are validated before checking
for empty arrays or converting values, so an invalid later argument can
suppress conversion side effects from earlier arrays. Values are not
converted if any input array is empty.
. Form feed (\f) is now added in the default trimmed characters of trim(),
rtrim() and ltrim().
RFC: https://wiki.php.net/rfc/trim_form_feed
Expand Down Expand Up @@ -715,6 +723,7 @@ PHP 8.6 UPGRADE NOTES

- Standard:
. Improved performance of array_fill_keys().
. Improved performance of array_intersect().
. Improved performance of array_map() with multiple arrays passed.
. Improved performance of array_sum() and array_product() for
integer-only arrays.
Expand Down
7 changes: 4 additions & 3 deletions Zend/tests/bug74093.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,10 @@ max_execution_time=1
hard_timeout=1
--FILE--
<?php
$a1 = range(1, 3000000);
$a2 = range(100000, 3999999);
array_intersect($a1, $a2);
$values = range(1, 6000000);
/* array_intersect() now uses a linear-time hash implementation. Use a large
* internal string sort to retain the hard-timeout workload. */
sort($values, SORT_STRING);
?>
--EXPECTF--
Fatal error: Maximum execution time of 1+1 seconds exceeded %s
10 changes: 10 additions & 0 deletions Zend/tests/named_params/internal_variadics.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,14 @@ try {
echo $e->getMessage(), "\n";
}

var_dump(array_intersect(array: [1, 2]) === [1, 2]);

try {
array_intersect([1, 2], arrays: [2]);
} catch (ArgumentCountError $e) {
echo $e->getMessage(), "\n";
}

try {
$array = [1, 2];
array_push($array, ...['values' => 3]);
Expand All @@ -25,4 +33,6 @@ try {
--EXPECT--
Internal function array_merge() does not accept named variadic arguments
Internal function array_diff_key() does not accept named variadic arguments
bool(true)
Internal function array_intersect() does not accept named variadic arguments
Internal function array_push() does not accept named variadic arguments
75 changes: 35 additions & 40 deletions ext/curl/interface.c
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,39 @@ ZEND_DECLARE_MODULE_GLOBALS(curl)
# define php_curl_ret(__ret) RETVAL_FALSE; return;
#endif

// php_curl_option_get_name(CURLOPT_HTTPHEADER) -> "HTTPHEADER"
static const char * php_curl_option_get_name(zend_long option) {

#if LIBCURL_VERSION_NUM >= 0x074900
const struct curl_easyoption * opt = curl_easy_option_by_id(option);
if (EXPECTED(opt != NULL)) {
return opt->name;
}
#endif

const char prefix[] = "CURLOPT_";
const size_t prefix_len = sizeof(prefix) - 1;
zend_string *key;
zend_constant *constant;

ZEND_HASH_FOREACH_STR_KEY_PTR(EG(zend_constants), key, constant) {
if (!key
|| Z_TYPE(constant->value) != IS_LONG
|| strncmp(ZSTR_VAL(key), prefix, prefix_len) != 0) {
continue;
}

if (Z_LVAL(constant->value) == option) {
return ZSTR_VAL(key) + prefix_len;
}
} ZEND_HASH_FOREACH_END();
return "UNKNOWN_OPTION";
}

static zend_result php_curl_option_str(php_curl *ch, zend_long option, const char *str, const size_t len)
{
if (zend_char_has_nul_byte(str, len)) {
zend_value_error("%s(): cURL option must not contain any null bytes", get_active_function_name());
zend_value_error("%s(): cURL option CURLOPT_%s must not contain any null bytes", get_active_function_name(), php_curl_option_get_name(option));
return FAILURE;
}

Expand Down Expand Up @@ -2017,7 +2046,7 @@ static zend_result _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue
ch->handlers.write->method = PHP_CURL_FILE;
ZVAL_COPY(&ch->handlers.write->stream, zvalue);
} else {
zend_value_error("%s(): The provided file handle must be writable", get_active_function_name());
zend_value_error("%s(): The file handle provided for CURLOPT_FILE must be writable", get_active_function_name());
return FAILURE;
}
break;
Expand All @@ -2035,7 +2064,7 @@ static zend_result _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue
ch->handlers.write_header->method = PHP_CURL_FILE;
ZVAL_COPY(&ch->handlers.write_header->stream, zvalue);
} else {
zend_value_error("%s(): The provided file handle must be writable", get_active_function_name());
zend_value_error("%s(): The file handle provided for CURLOPT_WRITEHEADER must be writable", get_active_function_name());
return FAILURE;
}
break;
Expand Down Expand Up @@ -2064,7 +2093,7 @@ static zend_result _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue
zval_ptr_dtor(&ch->handlers.std_err);
ZVAL_COPY(&ch->handlers.std_err, zvalue);
} else {
zend_value_error("%s(): The provided file handle must be writable", get_active_function_name());
zend_value_error("%s(): The file handle provided for CURLOPT_STDERR must be writable", get_active_function_name());
return FAILURE;
}
ZEND_FALLTHROUGH;
Expand All @@ -2091,43 +2120,9 @@ static zend_result _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue
HashTable *ph;
zend_string *val, *tmp_val;
struct curl_slist *slist = NULL;
const char *name = NULL;

switch (option) {
case CURLOPT_HTTPHEADER:
name = "CURLOPT_HTTPHEADER";
break;
case CURLOPT_QUOTE:
name = "CURLOPT_QUOTE";
break;
case CURLOPT_HTTP200ALIASES:
name = "CURLOPT_HTTP200ALIASES";
break;
case CURLOPT_POSTQUOTE:
name = "CURLOPT_POSTQUOTE";
break;
case CURLOPT_PREQUOTE:
name = "CURLOPT_PREQUOTE";
break;
case CURLOPT_TELNETOPTIONS:
name = "CURLOPT_TELNETOPTIONS";
break;
case CURLOPT_MAIL_RCPT:
name = "CURLOPT_MAIL_RCPT";
break;
case CURLOPT_RESOLVE:
name = "CURLOPT_RESOLVE";
break;
case CURLOPT_PROXYHEADER:
name = "CURLOPT_PROXYHEADER";
break;
case CURLOPT_CONNECT_TO:
name = "CURLOPT_CONNECT_TO";
break;
}

if (Z_TYPE_P(zvalue) != IS_ARRAY) {
zend_type_error("%s(): The %s option must have an array value", get_active_function_name(), name);
zend_type_error("%s(): The CURLOPT_%s option must have an array value", get_active_function_name(), php_curl_option_get_name(option));
return FAILURE;
}

Expand All @@ -2139,7 +2134,7 @@ static zend_result _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue
if (zend_str_has_nul_byte(val)) {
curl_slist_free_all(slist);
zend_tmp_string_release(tmp_val);
zend_value_error("%s(): cURL option %s must not contain any null bytes", get_active_function_name(), name);
zend_value_error("%s(): cURL option CURLOPT_%s must not contain any null bytes", get_active_function_name(), php_curl_option_get_name(option));
return FAILURE;
}

Expand Down
19 changes: 14 additions & 5 deletions ext/curl/tests/bug48207.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -36,17 +36,26 @@ $tempfile = tempnam(sys_get_temp_dir(), 'CURL_FILE_HANDLE');
$fp = fopen($tempfile, "r"); // Opening 'fubar' with the incorrect readonly flag

$ch = curl_init($url);
try {
curl_setopt($ch, CURLOPT_FILE, $fp);
} catch (ValueError $exception) {
echo $exception->getMessage() . "\n";

foreach ([
CURLOPT_FILE,
CURLOPT_WRITEHEADER,
CURLOPT_STDERR,
] as $option) {
try {
curl_setopt($ch, $option, $fp);
} catch (ValueError $exception) {
echo $exception->getMessage(), "\n";
}
}

curl_exec($ch);
is_file($tempfile) and @unlink($tempfile);
isset($tempname) and is_file($tempname) and @unlink($tempname);
?>
--EXPECT--
curl_setopt(): The provided file handle must be writable
curl_setopt(): The file handle provided for CURLOPT_FILE must be writable
curl_setopt(): The file handle provided for CURLOPT_WRITEHEADER must be writable
curl_setopt(): The file handle provided for CURLOPT_STDERR must be writable
Hello World!
Hello World!
2 changes: 1 addition & 1 deletion ext/curl/tests/bug68089.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,5 @@ try {
?>
Done
--EXPECT--
curl_setopt(): cURL option must not contain any null bytes
curl_setopt(): cURL option CURLOPT_URL must not contain any null bytes
Done
21 changes: 15 additions & 6 deletions ext/ldap/tests/skipifbindfailure.inc
Original file line number Diff line number Diff line change
@@ -1,14 +1,23 @@
<?php
require_once 'connect.inc';
require_once dirname(__DIR__, 3) . '/tests/probe_cache.inc';

if ($skip_on_bind_failure) {
$configuration = [$uri, $user, $passwd, $protocol_version];

try {
ProbeCache::getFailure('ldap.bind', $configuration, static function () use ($uri, $user, $passwd, $protocol_version): void {
$link = ldap_connect($uri);
ldap_set_option($link, LDAP_OPT_PROTOCOL_VERSION, $protocol_version);
if (!@ldap_bind($link, $user, $passwd)) {
throw new ProbeFailureException(sprintf("Can't bind to LDAP Server - [%d] %s", ldap_errno($link), ldap_error($link)));
}

$link = ldap_connect($uri);
ldap_set_option($link, LDAP_OPT_PROTOCOL_VERSION, $protocol_version);
if (!@ldap_bind($link, $user, $passwd))
die(sprintf("skip Can't bind to LDAP Server - [%d] %s", ldap_errno($link), ldap_error($link)));

ldap_unbind($link);
ldap_unbind($link);
});
} catch (ProbeFailureException $e) {
die("skip {$e->getMessage()}");
}
}

if (isset($require_vendor)) {
Expand Down
20 changes: 16 additions & 4 deletions ext/mysqli/tests/skipifconnectfailure.inc
Original file line number Diff line number Diff line change
@@ -1,7 +1,19 @@
<?php
require_once 'connect.inc';
$link = @my_mysqli_connect($host, $user, $passwd, $db, $port, $socket);
if (!is_object($link))
die(sprintf("skip Can't connect to MySQL Server - [%d] %s", mysqli_connect_errno(), mysqli_connect_error()));
mysqli_close($link);
require_once dirname(__DIR__, 3) . '/tests/probe_cache.inc';

$configuration = [$host, $port, $user, $passwd, $db, $socket, get_environment_connection_flags()];

try {
ProbeCache::getFailure('mysqli', $configuration, static function () use ($host, $user, $passwd, $db, $port, $socket): void {
$link = @my_mysqli_connect($host, $user, $passwd, $db, $port, $socket);
if (!is_object($link)) {
throw new ProbeFailureException(sprintf("Can't connect to MySQL Server - [%d] %s", mysqli_connect_errno(), mysqli_connect_error()));
}

mysqli_close($link);
});
} catch (ProbeFailureException $e) {
die("skip {$e->getMessage()}");
}
?>
36 changes: 27 additions & 9 deletions ext/mysqli/tests/test_setup/test_helpers.inc
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
<?php

require_once dirname(__DIR__, 4) . '/tests/probe_cache.inc';

function get_default_host(): string {
static $host = null;
if ($host === null) {
Expand Down Expand Up @@ -110,11 +112,31 @@ function default_mysqli_connect(): \mysqli{
function mysqli_check_skip_test(): void {
mysqli_connect_or_skip();
}
function mysqli_connect_or_skip() {

function mysqli_connect_or_skip(): mysqli {
$configuration = [
get_default_host(),
get_default_port(),
get_default_user(),
get_default_password(),
get_default_database(),
get_default_socket(),
get_environment_connection_flags(),
];

try {
return default_mysqli_connect();
} catch (\mysqli_sql_exception) {
die(sprintf("skip Can't connect to MySQL Server - [%d] %s", mysqli_connect_errno(), mysqli_connect_error()));
return ProbeCache::getFailure('mysqli', $configuration, static function (): mysqli {
try {
return default_mysqli_connect();
} catch (mysqli_sql_exception $e) {
throw new ProbeFailureException(
sprintf("Can't connect to MySQL Server - [%d] %s", mysqli_connect_errno(), mysqli_connect_error()),
$e,
);
}
});
} catch (ProbeFailureException $e) {
die("skip {$e->getMessage()}");
}
}
function have_innodb(mysqli $link): bool {
Expand All @@ -123,11 +145,7 @@ function have_innodb(mysqli $link): bool {
return $supported === 'YES' || $supported === 'DEFAULT';
}
function mysqli_check_innodb_support_skip_test(): void {
try {
$link = default_mysqli_connect();
} catch (\mysqli_sql_exception) {
die(sprintf("skip Can't connect to MySQL Server - [%d] %s", mysqli_connect_errno(), mysqli_connect_error()));
}
$link = mysqli_connect_or_skip();
if (! have_innodb($link)) {
die(sprintf("skip Needs InnoDB support"));
}
Expand Down
15 changes: 12 additions & 3 deletions ext/odbc/tests/skipif.inc
Original file line number Diff line number Diff line change
@@ -1,8 +1,17 @@
<?php

include 'config.inc';
require_once dirname(__DIR__, 3) . '/tests/probe_cache.inc';

$conn = @odbc_connect($dsn, $user, $pass);
if (!$conn) {
die('skip could not connect');
try {
$conn = ProbeCache::getFailure('odbc', [$dsn, $user, $pass], static function () use ($dsn, $user, $pass): Odbc\Connection {
$conn = @odbc_connect($dsn, $user, $pass);
if (!$conn) {
throw new ProbeFailureException('could not connect');
}

return $conn;
});
} catch (ProbeFailureException $e) {
die("skip {$e->getMessage()}");
}
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ require_once getenv('REDIR_TEST_DIR') . 'pdo_test.inc';
$db = PDOTest::factory();
PDOTest::dropTableIfExists($db, "pdo_attr_statement_class_basic");
?>
--EXPECT--
--EXPECTF--
array(1) {
[0]=>
string(12) "PDOStatement"
Expand All @@ -89,15 +89,15 @@ StatementWithPublicDestructor::__destruct
Class derived from PDOStatement, with private constructor:
bool(true)
StatementWithPrivateConstructor::__construct
object(StatementWithPrivateConstructor)#2 (1) {
object(StatementWithPrivateConstructor)#%d (1) {
["queryString"]=>
string(68) "SELECT id, label FROM pdo_attr_statement_class_basic ORDER BY id ASC"
}
string(6) "param1"
Class derived from a child of PDOStatement:
bool(true)
StatementWithPrivateConstructor::__construct
object(StatementDerivedFromChild)#2 (1) {
object(StatementDerivedFromChild)#%d (1) {
["queryString"]=>
string(68) "SELECT id, label FROM pdo_attr_statement_class_basic ORDER BY id ASC"
}
Expand Down
Loading