. printf() and friends how support the %h and %H format specifiers. These
are the same as %g and %G, but always use "." as the decimal separator,
rather than determining it through the LC_NUMERIC locale.
+ . printf() and friends now support using "*" as width or precision, in which
+ case the width/precision is passed as an argument to printf. This also
+ allows using precision -1 with %g, %G, %h and %H. For example, the following
+ code can be used to reproduce PHP's default floating point formatting:
+
+ printf("%.*H", (int) ini_get("precision"), $float);
+ printf("%.*H", (int) ini_get("serialize_precision"), $float);
- Zip:
. Extension updated to version 1.19.0
}
/* }}} */
+#define ARG_NUM_NEXT -1
+#define ARG_NUM_INVALID -2
+
+int php_sprintf_get_argnum(char **format, size_t *format_len) {
+ char *temppos = *format;
+ while (isdigit((int) *temppos)) temppos++;
+ if (*temppos != '$') {
+ return ARG_NUM_NEXT;
+ }
+
+ int argnum = php_sprintf_getnumber(format, format_len);
+ if (argnum <= 0) {
+ zend_value_error("Argument number must be greater than zero");
+ return ARG_NUM_INVALID;
+ }
+
+ (*format)++; /* skip the '$' */
+ (*format_len)--;
+ return argnum - 1;
+}
+
/* php_formatted_print() {{{
* New sprintf implementation for PHP.
*
*format, format - Z_STRVAL_P(z_format)));
if (isalpha((int)*format)) {
width = precision = 0;
- argnum = currarg++;
+ argnum = ARG_NUM_NEXT;
} else {
/* first look for argnum */
- temppos = format;
- while (isdigit((int)*temppos)) temppos++;
- if (*temppos == '$') {
- argnum = php_sprintf_getnumber(&format, &format_len);
-
- if (argnum <= 0) {
- zend_value_error("Argument number must be greater than zero");
- goto fail;
- }
- argnum--;
- format++; /* skip the '$' */
- format_len--;
- } else {
- argnum = currarg++;
+ argnum = php_sprintf_get_argnum(&format, &format_len);
+ if (argnum == ARG_NUM_INVALID) {
+ goto fail;
}
/* after argnum comes modifiers */
/* after modifiers comes width */
- if (isdigit((int)*format)) {
+ if (*format == '*') {
+ format++;
+ format_len--;
+
+ int width_argnum = php_sprintf_get_argnum(&format, &format_len);
+ if (width_argnum == ARG_NUM_INVALID) {
+ goto fail;
+ }
+ if (width_argnum == ARG_NUM_NEXT) {
+ width_argnum = currarg++;
+ }
+ if (width_argnum >= argc) {
+ max_missing_argnum = MAX(max_missing_argnum, width_argnum);
+ continue;
+ }
+ tmp = &args[width_argnum];
+ ZVAL_DEREF(tmp);
+ if (Z_TYPE_P(tmp) != IS_LONG) {
+ zend_value_error("Width must be an integer");
+ goto fail;
+ }
+ if (Z_LVAL_P(tmp) < 0 || Z_LVAL_P(tmp) > INT_MAX) {
+ zend_value_error("Width must be greater than zero and less than %d", INT_MAX);
+ goto fail;
+ }
+ width = Z_LVAL_P(tmp);
+ adjusting |= ADJ_WIDTH;
+ } else if (isdigit((int)*format)) {
PRINTF_DEBUG(("sprintf: getting width\n"));
if ((width = php_sprintf_getnumber(&format, &format_len)) < 0) {
zend_value_error("Width must be greater than zero and less than %d", INT_MAX);
format++;
format_len--;
PRINTF_DEBUG(("sprintf: getting precision\n"));
- if (isdigit((int)*format)) {
+ if (*format == '*') {
+ format++;
+ format_len--;
+
+ int prec_argnum = php_sprintf_get_argnum(&format, &format_len);
+ if (prec_argnum == ARG_NUM_INVALID) {
+ goto fail;
+ }
+ if (prec_argnum == ARG_NUM_NEXT) {
+ prec_argnum = currarg++;
+ }
+ if (prec_argnum >= argc) {
+ max_missing_argnum = MAX(max_missing_argnum, prec_argnum);
+ continue;
+ }
+ tmp = &args[prec_argnum];
+ ZVAL_DEREF(tmp);
+ if (Z_TYPE_P(tmp) != IS_LONG) {
+ zend_value_error("Precision must be an integer");
+ goto fail;
+ }
+ if (Z_LVAL_P(tmp) < -1 || Z_LVAL_P(tmp) > INT_MAX) {
+ zend_value_error("Precision must be between -1 and %d", INT_MAX);
+ goto fail;
+ }
+ precision = Z_LVAL_P(tmp);
+ adjusting |= ADJ_PRECISION;
+ expprec = 1;
+ } else if (isdigit((int)*format)) {
if ((precision = php_sprintf_getnumber(&format, &format_len)) < 0) {
zend_value_error("Precision must be greater than zero and less than %d", INT_MAX);
goto fail;
}
PRINTF_DEBUG(("sprintf: format character='%c'\n", *format));
+ if (argnum == ARG_NUM_NEXT) {
+ argnum = currarg++;
+ }
if (argnum >= argc) {
max_missing_argnum = MAX(max_missing_argnum, argnum);
continue;
}
+ if (expprec && precision == -1
+ && *format != 'g' && *format != 'G' && *format != 'h' && *format != 'H') {
+ zend_value_error("Precision -1 is only supported for %%g, %%G, %%h and %%H");
+ goto fail;
+ }
+
/* now we expect to find a type specifier */
tmp = &args[argnum];
switch (*format) {
--- /dev/null
+--TEST--
+Star width and precision in sprintf()
+--FILE--
+<?php
+
+
+$f = 1.23456789012345678;
+$fx = 1.23456789012345678e100;
+var_dump($f, $fx);
+
+printf("%.*f\n", 10, $f);
+printf("%.*G\n", 10, $f);
+printf("%.*g\n", -1, $fx);
+printf("%.*G\n", -1, $fx);
+printf("%.*h\n", -1, $fx);
+printf("%.*H\n", -1, $fx);
+printf("%.*s\n", 3, "foobar");
+echo "\n";
+
+printf("%*f\n", 10, $f);
+printf("%*G\n", 10, $f);
+printf("%*s\n", 10, "foobar");
+echo "\n";
+
+printf("%*.*f\n", 10, 3, $f);
+printf("%*.*G\n", 10, 3, $f);
+printf("%*.*s\n", 10, 3, "foobar");
+echo "\n";
+
+printf("%1$.*2\$f\n", $f, 10);
+printf("%.*2\$f\n", $f, 10);
+printf("%2$.*f\n", 10, $f);
+printf("%1$*2\$f\n", $f, 10);
+printf("%*2\$f\n", $f, 10);
+printf("%2$*f\n", 10, $f);
+printf("%1$*2$.*3\$f\n", $f, 10, 3);
+printf("%*2$.*3\$f\n", $f, 10, 3);
+printf("%3$*.*f\n", 10, 3, $f);
+echo "\n";
+
+try {
+ printf("%.*G\n", "foo", 1.5);
+} catch (ValueError $e) {
+ echo $e->getMessage(), "\n";
+}
+
+try {
+ printf("%.*G\n", -100, 1.5);
+} catch (ValueError $e) {
+ echo $e->getMessage(), "\n";
+}
+
+try {
+ printf("%.*s\n", -1, "Foo");
+} catch (ValueError $e) {
+ echo $e->getMessage(), "\n";
+}
+
+try {
+ printf("%*G\n", -1, $f);
+} catch (ValueError $e) {
+ echo $e->getMessage(), "\n";
+}
+
+?>
+--EXPECT--
+float(1.2345678901234567)
+float(1.2345678901234569E+100)
+1.2345678901
+1.23456789
+1.2345678901234569e+100
+1.2345678901234569E+100
+1.2345678901234569e+100
+1.2345678901234569E+100
+foo
+
+ 1.234568
+ 1.23457
+ foobar
+
+ 1.235
+ 1.23
+ foo
+
+1.2345678901
+1.2345678901
+1.2345678901
+ 1.234568
+ 1.234568
+ 1.234568
+ 1.235
+ 1.235
+ 1.235
+
+Precision must be an integer
+Precision must be between -1 and 2147483647
+Precision -1 is only supported for %g, %G, %h and %H
+Width must be greater than zero and less than 2147483647