Skip to content

Commit f38061d

Browse files
committed
Merge branch 'PHP-8.5'
* PHP-8.5: [intl] Fix empty-needle grapheme_strpos offsets Fix GH-23444: ODBC_ATTR_ASSUME_UTF8 corrupts Unicode data outside Windows (#23445)
2 parents 4923c60 + f0db414 commit f38061d

5 files changed

Lines changed: 88 additions & 2 deletions

File tree

‎NEWS‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ PHP NEWS
1313
className/classList writes and attribute removals. (Ilia Alshanetsky)
1414

1515
- Intl:
16+
. Fixed grapheme_strpos() and grapheme_strrpos() with an empty needle
17+
returning UTF-16 offsets instead of grapheme offsets. (Ilia Alshanetsky)
1618
. Fixed a memory leak when dumping IntlCalendar instances. (Ilia Alshanetsky)
1719
. Fixed Collator::sortWithSortKeys() allocating fixed 2MiB buffers
1820
regardless of array size. (Ilia Alshanetsky)

‎ext/intl/grapheme/grapheme_util.cpp‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,11 @@ U_CFUNC int32_t grapheme_strpos_utf16(char *haystack, size_t haystack_len, char
139139
ret_pos = -1;
140140
goto finish;
141141
}
142-
ret_pos = last && offset >= 0 ? uhaystack_len : offset_pos;
142+
if (last && offset >= 0) {
143+
ret_pos = grapheme_count_graphemes(bi, uhaystack, uhaystack_len);
144+
} else {
145+
ret_pos = grapheme_count_graphemes(bi, uhaystack, offset_pos);
146+
}
143147
goto finish;
144148
}
145149

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
--TEST--
2+
grapheme_strpos() family with empty needle and offset on multi-code-unit graphemes
3+
--EXTENSIONS--
4+
intl
5+
--FILE--
6+
<?php
7+
8+
var_dump(grapheme_strpos("😀x", ""));
9+
var_dump(grapheme_strpos("😀x", "", 0));
10+
var_dump(grapheme_strpos("😀x", "", 1));
11+
var_dump(grapheme_stripos("😀x", "", 1));
12+
var_dump(grapheme_strpos("😀x", "", -1));
13+
var_dump(grapheme_strrpos("😀x", ""));
14+
var_dump(grapheme_strrpos("😀x", "", 1));
15+
var_dump(grapheme_strripos("😀x", "", 1));
16+
var_dump(grapheme_strrpos("😀x", "", -1));
17+
try {
18+
var_dump(grapheme_strpos("😀x", "", 5));
19+
} catch (Throwable $e) {
20+
echo $e::class, ': ', $e->getMessage(), "\n";
21+
}
22+
23+
?>
24+
--EXPECT--
25+
int(0)
26+
int(0)
27+
int(1)
28+
int(1)
29+
int(1)
30+
int(2)
31+
int(2)
32+
int(2)
33+
int(1)
34+
ValueError: grapheme_strpos(): Argument #3 ($offset) must be contained in argument #1 ($haystack)

‎ext/pdo_odbc/odbc_stmt.c‎

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ enum pdo_odbc_conv_result {
3535

3636
static int pdo_odbc_sqltype_is_unicode(pdo_odbc_stmt *S, SQLSMALLINT sqltype)
3737
{
38+
#ifdef PHP_WIN32
3839
if (!S->assume_utf8) return 0;
3940
switch (sqltype) {
4041
#ifdef SQL_WCHAR
@@ -52,6 +53,9 @@ static int pdo_odbc_sqltype_is_unicode(pdo_odbc_stmt *S, SQLSMALLINT sqltype)
5253
default:
5354
return 0;
5455
}
56+
#else
57+
return 0;
58+
#endif
5559
}
5660

5761
static int pdo_odbc_utf82ucs2(pdo_stmt_t *stmt, int is_unicode, const char *buf,
@@ -544,7 +548,15 @@ static int odbc_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_data *p
544548
break;
545549
}
546550
} else {
547-
P->len = SQL_LEN_DATA_AT_EXEC(Z_STRLEN_P(parameter));
551+
zend_ulong ulen;
552+
if (pdo_odbc_utf82ucs2(stmt, P->is_unicode,
553+
Z_STRVAL_P(parameter),
554+
Z_STRLEN_P(parameter),
555+
&ulen) == PDO_ODBC_CONV_OK) {
556+
P->len = SQL_LEN_DATA_AT_EXEC(ulen);
557+
} else {
558+
P->len = SQL_LEN_DATA_AT_EXEC(Z_STRLEN_P(parameter));
559+
}
548560
}
549561
}
550562
return 1;

‎ext/pdo_odbc/tests/gh23444.phpt‎

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
--TEST--
2+
GH-23444 (Unicode data is corrupted with ODBC_ATTR_ASSUME_UTF8)
3+
--EXTENSIONS--
4+
pdo_odbc
5+
--SKIPIF--
6+
<?php
7+
require 'ext/pdo/tests/pdo_test.inc';
8+
PDOTest::skip();
9+
?>
10+
--FILE--
11+
<?php
12+
require 'ext/pdo/tests/pdo_test.inc';
13+
$db = PDOTest::test_factory(__DIR__ . '/common.phpt');
14+
$db->exec("CREATE TABLE gh23444 (v NVARCHAR(100))");
15+
16+
// 13 bytes as UTF-8, so an unconverted parameter is an odd number of bytes
17+
$string = "\u{6e2c}\u{8a66}\u{4e2d}\u{1f418}";
18+
19+
$db->setAttribute(PDO::ODBC_ATTR_ASSUME_UTF8, true);
20+
$stmt = $db->prepare("INSERT INTO gh23444 VALUES(?)");
21+
$stmt->execute([$string]);
22+
23+
$stmt = $db->prepare("SELECT v FROM gh23444 WHERE v = ?");
24+
$stmt->execute([$string]);
25+
var_dump($stmt->fetchColumn() === $string);
26+
?>
27+
--CLEAN--
28+
<?php
29+
require 'ext/pdo/tests/pdo_test.inc';
30+
$db = PDOTest::test_factory(__DIR__ . '/common.phpt');
31+
$db->exec("DROP TABLE IF EXISTS gh23444");
32+
?>
33+
--EXPECT--
34+
bool(true)

0 commit comments

Comments
 (0)