Skip to content

Commit 07811b6

Browse files
committedSep 26, 2023
Fix GH-11997: ctype_alnum 5 times slower in PHP 8.1 or greater
Currently, a common function is used where a function pointer gets passed to check the character class type. If we instead use a macro, the macro version of these character class type checkers can be used. While this gives only a minor speed-up for glibc-based systems, on Alpine this gives a multi-facor speed-up This is essentially a manual revert of dc80ea7. Full discussion in GH-11997. Closes GH-12300.
1 parent 14fc3d1 commit 07811b6

File tree

2 files changed

+43
-32
lines changed

2 files changed

+43
-32
lines changed
 

‎NEWS

+4
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ PHP NEWS
99
ext/dom). (nielsdos)
1010
. Fixed bug GH-12273 (__builtin_cpu_init check). (Freaky)
1111

12+
- CType:
13+
. Fixed bug GH-11997 (ctype_alnum 5 times slower in PHP 8.1 or greater).
14+
(nielsdos)
15+
1216
- Filter:
1317
. Fix explicit FILTER_REQUIRE_SCALAR with FILTER_CALLBACK (ilutov)
1418

‎ext/ctype/ctype.c

+39-32
Original file line numberDiff line numberDiff line change
@@ -61,27 +61,9 @@ static PHP_MINFO_FUNCTION(ctype)
6161
}
6262
/* }}} */
6363

64-
static void ctype_impl(
65-
INTERNAL_FUNCTION_PARAMETERS, int (*iswhat)(int), bool allow_digits, bool allow_minus) {
66-
zval *c;
67-
68-
ZEND_PARSE_PARAMETERS_START(1, 1);
69-
Z_PARAM_ZVAL(c)
70-
ZEND_PARSE_PARAMETERS_END();
71-
72-
if (Z_TYPE_P(c) == IS_STRING) {
73-
char *p = Z_STRVAL_P(c), *e = Z_STRVAL_P(c) + Z_STRLEN_P(c);
74-
if (e == p) {
75-
RETURN_FALSE;
76-
}
77-
while (p < e) {
78-
if (!iswhat((int)*(unsigned char *)(p++))) {
79-
RETURN_FALSE;
80-
}
81-
}
82-
RETURN_TRUE;
83-
}
84-
64+
/* Slow fallback for deprecated cases defined in a no-inline function to not bloat code. */
65+
static zend_never_inline void ctype_fallback(zval *c, zval *return_value, int (*iswhat)(int), bool allow_digits, bool allow_minus)
66+
{
8567
php_error_docref(NULL, E_DEPRECATED,
8668
"Argument of type %s will be interpreted as string in the future", zend_zval_type_name(c));
8769
if (Z_TYPE_P(c) == IS_LONG) {
@@ -99,80 +81,105 @@ static void ctype_impl(
9981
}
10082
}
10183

84+
/* Define as a macro such that iswhat can use the macro version instead of the function version.
85+
* This heavily reduces the overhead. (GH-11997) */
86+
#define ctype_impl(iswhat, allow_digits, allow_minus) do { \
87+
zval *c; \
88+
\
89+
ZEND_PARSE_PARAMETERS_START(1, 1); \
90+
Z_PARAM_ZVAL(c) \
91+
ZEND_PARSE_PARAMETERS_END(); \
92+
\
93+
if (Z_TYPE_P(c) == IS_STRING) { \
94+
char *p = Z_STRVAL_P(c), *e = Z_STRVAL_P(c) + Z_STRLEN_P(c); \
95+
if (e == p) { \
96+
RETURN_FALSE; \
97+
} \
98+
while (p < e) { \
99+
if (!iswhat((int)*(unsigned char *)(p++))) { \
100+
RETURN_FALSE; \
101+
} \
102+
} \
103+
RETURN_TRUE; \
104+
} \
105+
\
106+
ctype_fallback(c, return_value, iswhat, allow_digits, allow_minus); \
107+
} while (0);
108+
102109
/* {{{ Checks for alphanumeric character(s) */
103110
PHP_FUNCTION(ctype_alnum)
104111
{
105-
ctype_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, isalnum, 1, 0);
112+
ctype_impl(isalnum, 1, 0);
106113
}
107114
/* }}} */
108115

109116
/* {{{ Checks for alphabetic character(s) */
110117
PHP_FUNCTION(ctype_alpha)
111118
{
112-
ctype_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, isalpha, 0, 0);
119+
ctype_impl(isalpha, 0, 0);
113120
}
114121
/* }}} */
115122

116123
/* {{{ Checks for control character(s) */
117124
PHP_FUNCTION(ctype_cntrl)
118125
{
119-
ctype_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, iscntrl, 0, 0);
126+
ctype_impl(iscntrl, 0, 0);
120127
}
121128
/* }}} */
122129

123130
/* {{{ Checks for numeric character(s) */
124131
PHP_FUNCTION(ctype_digit)
125132
{
126-
ctype_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, isdigit, 1, 0);
133+
ctype_impl(isdigit, 1, 0);
127134
}
128135
/* }}} */
129136

130137
/* {{{ Checks for lowercase character(s) */
131138
PHP_FUNCTION(ctype_lower)
132139
{
133-
ctype_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, islower, 0, 0);
140+
ctype_impl(islower, 0, 0);
134141
}
135142
/* }}} */
136143

137144
/* {{{ Checks for any printable character(s) except space */
138145
PHP_FUNCTION(ctype_graph)
139146
{
140-
ctype_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, isgraph, 1, 1);
147+
ctype_impl(isgraph, 1, 1);
141148
}
142149
/* }}} */
143150

144151
/* {{{ Checks for printable character(s) */
145152
PHP_FUNCTION(ctype_print)
146153
{
147-
ctype_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, isprint, 1, 1);
154+
ctype_impl(isprint, 1, 1);
148155
}
149156
/* }}} */
150157

151158
/* {{{ Checks for any printable character which is not whitespace or an alphanumeric character */
152159
PHP_FUNCTION(ctype_punct)
153160
{
154-
ctype_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, ispunct, 0, 0);
161+
ctype_impl(ispunct, 0, 0);
155162
}
156163
/* }}} */
157164

158165
/* {{{ Checks for whitespace character(s)*/
159166
PHP_FUNCTION(ctype_space)
160167
{
161-
ctype_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, isspace, 0, 0);
168+
ctype_impl(isspace, 0, 0);
162169
}
163170
/* }}} */
164171

165172
/* {{{ Checks for uppercase character(s) */
166173
PHP_FUNCTION(ctype_upper)
167174
{
168-
ctype_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, isupper, 0, 0);
175+
ctype_impl(isupper, 0, 0);
169176
}
170177
/* }}} */
171178

172179
/* {{{ Checks for character(s) representing a hexadecimal digit */
173180
PHP_FUNCTION(ctype_xdigit)
174181
{
175-
ctype_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, isxdigit, 1, 0);
182+
ctype_impl(isxdigit, 1, 0);
176183
}
177184
/* }}} */
178185

0 commit comments

Comments
 (0)
Please sign in to comment.