Given some unknown input, how do you tell which variables are being substituted into a (s)printf
statement?
printf("%s %s", "a", "b"); // both used
printf("%s", "a", "b"); // only the first one used
printf('%1$s %1$s', "a", "b"); // " "
printf('%s %1$s', "a", "b"); // " "
printf('%1$s %s %1$s', "a", "b"); // " "
printf('%2$s', "a", "b"); // only the second one used.
Checking the resultant string for the presence of the first or second variables won't help, because they could have the same value.
In my own situation, there is only ever 2 variables that could be substituted, and I need to know whether the second one is used or not.
Here is a bit hackish function to do it:
function printf_and_return_min_args_required(/* var args */) {
$old_track = ini_set('track_errors', '1');
$args = func_get_args();
$args2 = array();
$required = 100;
foreach ($args as $arg) {
$args2[] = $arg;
if (@call_user_func_array("printf", $args2)) {
$required = count($args2) - 1;
break;
}
}
ini_set('track_errors', $old_track);
return $required;
}
For sprintf()
you might have to return array($result, $required)
instead if you don't want the $result
printed by the function.
Parse the format string yourself and see how many args it expects.
EDIT: A psuedocode example (no error checking):
bool arg1used = false;
bool arg2used = false;
int unspecifiedscount = 0;
for (int i = 0; i < s.length; i++) {
if s[i] != '%' continue;
switch s[i+1] {
case '%':
i++;
break;
case 's':
if unspecifiedscount == 0 arg1used = true;
if unspecifiedscount == 1 arg2used = true;
break;
case '1':
if s[i+2] == '$' && s[i+3] == 's' {
arg1used = true;
i+=3;
true;
}
break;
case '2':
if s[i+2] == '$' && s[i+3] == 's' {
arg2used = true;
i+=3;
true;
}
break;
}
}
I've come up with a kludgey way which works in this situation, but would be interested to hear better solutions which are more generic.
if (($result = @sprintf($input, "a")) === false) {
// need two arguments
$result = sprintf($input, "a", "b");
} else {
// only needed one argument
}
Basically, it just tries it with one argument, and if that didn't work, then you know it needs two.