D
P
0

WordPress & PHP

PHP 8 Bug: 'floatval' as a sanitize_callback Causes a Fatal Error in WordPress

June 12, 2026·3 min read
PHP 8 Bug: 'floatval' as a sanitize_callback Causes a Fatal Error in WordPress

This is the bug that took down all post creation on a WordPress platform I built, for hours, and the cause was a single line that looks completely innocent:

register_post_meta( 'pc_property', '_harga', [
    'type'              => 'number',
    'single'            => true,
    'sanitize_callback' => 'floatval', // ← this is the trigger
    'show_in_rest'      => true,
] );

Looks reasonable, right? floatval to clean up numeric input. But in PHP 8, this line throws a fatal error every time a post is saved:

floatval() expects exactly 1 argument, 4 given
in wp-includes/class-wp-hook.php on line 341

What makes it more confusing: php -l (lint) passes without complaint, because lint doesn't execute the code. The bug only surfaces at runtime, in the middle of saving.

Why this happens

WordPress runs the sanitize_callback through the sanitize_{$object_type}_meta_{$meta_key} filter. And that filter doesn't just pass the value — it passes four arguments:

(value, meta_key, object_type, object_subtype)

For ordinary PHP functions (the ones you write yourself), this is fine: PHP silently discards any extra arguments you didn't declare. But PHP internal functions (written in C — like floatval, intval, trim; sanitize_text_field doesn't count, since that's a WP function) became strict in PHP 8: if a function is declared to accept 1 argument, passing it 4 is an immediate fatal.

So floatval receives (value, meta_key, object_type, object_subtype) = 4 arguments when it only wants 1 → ArgumentCountError. And because this happens in the filter chain while saving meta, every post save goes down with it.

The fix: wrap it in a closure

The solution is one line — wrap that internal function in a closure (a user function), which quietly discards the extra arguments:

register_post_meta( 'pc_property', '_harga', [
    'type'              => 'number',
    'single'            => true,
    'sanitize_callback' => fn( $value ) => (float) $value, // safe
    'show_in_rest'      => true,
] );

The closure fn($value) => (float) $value declares only one parameter. WordPress still passes four arguments, but because this is a user function, PHP discards the other three without complaint. Safe in PHP 8.

The general rule to remember

Never pass the name of a PHP internal function directly as a callback to a WordPress API that passes multiple arguments. The risky ones include:

  • floatval, intval, boolval, strval
  • trim, strtolower, strtoupper, htmlspecialchars (they have optional parameters but can still go fatal depending on the version)

Always wrap them in a closure when you only want to pass the value through:

'sanitize_callback' => fn( $v ) => (int) $v,        // not 'intval'
'sanitize_callback' => fn( $v ) => trim( (string) $v ), // not 'trim'

WordPress's own functions (sanitize_text_field, absint, sanitize_email, etc.) are safe because they're written to accept/ignore extra arguments — so those are fine to use directly.

The takeaway

php -l is green, the code "looks correct," but a single internal function name as a callback blows up an entire feature at runtime under PHP 8. This is a classic trap when migrating to PHP 8: C-land is strict, user-land is lenient. If a WordPress API passes more than one argument to your callback, wrap any internal function in a closure — and test by actually running the code, not just linting it.