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,strvaltrim,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.
