Skip to content

Type inconsistency between the filter_var function and it's callback on the $value parameter. #18760

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
tonurics opened this issue Jun 4, 2025 · 5 comments

Comments

@tonurics
Copy link

tonurics commented Jun 4, 2025

Description

The filter_var function has the following description:
filter_var(mixed $value, int $filter = FILTER_DEFAULT, array|int $options = 0): mixed

The "callback signature" [description] of the User Defined Filter of the FILTER_CALLBACK filter is:
callback(string $value): mixed

Problem:

This inconsistency prevents FILTER_CALLBACK from being used to sanitize arrays, such as those set in the predefined variables after an HTTP POST request.

e.g.: The following code generates a type error. [EDIT 2025-06-09:] The code sample in this post does not demonstrate the reported problem, see updated sample here: #18760 (comment)

<?php
unset($json);
$json=filter_var(
	value:$_POST['array'],
	filter:FILTER_CALLBACK,
	options:[
		'options'=>function(
			mixed $value,
		):mixed{
			return json_encode($value);
		},
	],
);

Solution:

The type of the $value parameter in the "callback signature" [description] should be changed to match its filter_var calling function. i.e.:
callback(mixed $value): mixed

PHP Version

PHP 8.4.7 (cli) (built: May  9 2025 07:02:39) (NTS)
Copyright (c) The PHP Group
Zend Engine v4.4.7, Copyright (c) Zend Technologies
    with Zend OPcache v8.4.7, Copyright (c), by Zend Technologies

Operating System

Debian 6.1.140-1

@nielsdos
Copy link
Member

nielsdos commented Jun 8, 2025

As far as I understand, this doesn't actually affect the execution, it's just that this is wrong in the documentation. Correct?

@tonurics
Copy link
Author

tonurics commented Jun 9, 2025

No, the current "callback signature" is accurately described in the documentation. [The sample code generates a type error in execution.]

The solution would require updates to execution and documentation.

@nielsdos
Copy link
Member

nielsdos commented Jun 9, 2025

Thanks for getting back.
I think I'm not understanding the issue correctly.
I adapted your sample code to use a fixed array and it runs without an error:

<?php
$array = [
    'test' => 1234,
    'test2' => 'test',
    'array' => ['A','B','C'],
];

unset($json);
$json=filter_var(
	value:$array,
	filter:FILTER_CALLBACK,
	options:[
		'options'=>function(
			mixed $value,
		):mixed{
			return json_encode($value);
		},
	],
);
print_r($json);

Results in:

Array
(
    [test] => "1234"
    [test2] => "test"
    [array] => Array
        (
            [0] => "\u00e9"
            [1] => "B"
            [2] => "C"
        )

)

@tonurics
Copy link
Author

tonurics commented Jun 9, 2025

The use of json_encode in my sample code, unfortunately produced a misleading false negative that does not actually demonstrate the reported problem. When I wrote the sample code: I wasn't thinking json_encode's own $value parameter accepts mixed types, which would accommodate the recast variable.

I apologize for any confusion. And thank you for your debugging assistance and patience.

Instead please refer to this updated sample code:

<?php
unset($array);
$array=[
	'test'=>1234,
	'test2'=>'test',
];
echo gettype($array)."\n";

unset($json);
$json=filter_var(
	value:$array,
	filter:FILTER_CALLBACK,
	options:[
		'options'=>function(
			mixed $value,
		):mixed{
			echo gettype($value)."\n";
			return json_encode(array_values($value));
		},
	],
);
print_r($json);

Results in:

array
string

The following fatal error is then emitted:

array_values(): Argument #1 ($array) must be of type array, string given

@tonurics
Copy link
Author

tonurics commented Jun 9, 2025

Upon further investigation, I see that I overlooked a warning in the parameters documentation for the filter_var function:

Scalar values are converted to string internally before they are filtered.

Also the documentation on the use of arrays with FILTER_CALLBACK is lacking in clarity on how the function behaves. After analysis, I found that the user-defined function is applied recursively to all elements in the array, similar to array_walk_recursive, but with some notable differences:

  1. The value of each non-array member in the $value parameter is recast as a string.
  2. The user-defined function is applied to the value, and the returned result is assigned to a corresponding key in the filter_var return array matching the original key specified by the $value parameter.

I'll also note that using the FILTER_FORCE_ARRAY flag with the filter_var_array appears to follow the same behavior.

Sample code:

<?php
unset($array);
$array=filter_var(
	value:[
		'int'=>1234,
		'float'=>0.123,
		'string'=>'test',
		'array'=>[
			'A',
			'B',
			'C',
		],
	],
	filter:FILTER_CALLBACK,
	options:[
		'options'=>function(
			mixed $value,
		):mixed{
			return [
				gettype($value),
				$value,
			];
		},
	],
);
print_r($array);

Results in:

Array
(
    [int] => Array
        (
            [0] => string
            [1] => 1234
        )

    [float] => Array
        (
            [0] => string
            [1] => 0.123
        )

    [string] => Array
        (
            [0] => string
            [1] => test
        )

    [array] => Array
        (
            [0] => Array
                (
                    [0] => string
                    [1] => A
                )

            [1] => Array
                (
                    [0] => string
                    [1] => B
                )

            [2] => Array
                (
                    [0] => string
                    [1] => C
                )

        )

)

This behavior was not aligned with my expectations. But with this new knowledge, I would like to propose the documentation be improved to better describe the above. [That would technically resolve the issue.]

Separately, I would suggest improving the filter_var function's capabilities to enable direct processing of arrays when used with user-defined functions, as described in my opening post. Perhaps the FILTER_FORCE_ARRAY flag could be used with filter_var in this case? [It appears to have no effect when combined with FILTER_CALLBACK on the filter_var function.]

Side thoughts: I wonder if recasting the $value parameter as a string during filtering is historical cruft from PHP 5. Given the actual returned result from the function is mixed type, and there are flags like FILTER_REQUIRE_SCALAR and FILTER_REQUIRE_ARRAY for the validation filters.

I apologize this turned out to not be a "hard" bug, and more of a misunderstanding/documentation issue. I thought I researched the topic well enough to make an informed bug report, but the subject proved deeper upon revisiting it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants