Skip to content

Instantly share code, notes, and snippets.

@xlplugins
Created October 26, 2026 10:38
Show Gist options
  • Select an option

  • Save xlplugins/ea88e315722eea62dff85614d5c5cce8 to your computer and use it in GitHub Desktop.

Select an option

Save xlplugins/ea88e315722eea62dff85614d5c5cce8 to your computer and use it in GitHub Desktop.
FunnelKit phone trace (temporary). Logs
<?php
/**
* FunnelKit phone trace (temporary). Logs, for express wallet orders only, the billing/shipping phone
* at each step from the wallet to WooCommerce's validation, with every digit masked as "9".
* Log: WooCommerce > Status > Logs > source "fk-phone-trace".
*/
add_action( 'woocommerce_before_checkout_process', function () {
if ( empty( $_POST['payment_request_type'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
return; // Not an express wallet order.
}
$mask = function ( $v ) {
$v = preg_replace( '/[0-9]/', '9', (string) $v );
return '"' . $v . '" hex ' . bin2hex( $v );
};
$log = function ( $msg ) {
wc_get_logger()->info( $msg, array( 'source' => 'fk-phone-trace' ) );
};
$who = function ( $hook ) {
global $wp_filter;
$out = array();
if ( empty( $wp_filter[ $hook ] ) ) {
return 'none';
}
foreach ( $wp_filter[ $hook ]->callbacks as $prio => $cbs ) {
foreach ( $cbs as $cb ) {
try {
$f = $cb['function'];
$ref = is_array( $f ) ? new ReflectionMethod( $f[0], $f[1] ) : new ReflectionFunction( $f );
$out[] = $prio . ' ' . str_replace( ABSPATH, '', $ref->getFileName() ) . ':' . $ref->getStartLine();
} catch ( Throwable $e ) {
$out[] = $prio . ' ?';
}
}
}
return implode( ' | ', $out );
};
// 1. Exactly what the wallet sent.
$log( sprintf( '1 wallet: type=%s billing=%s shipping=%s ua=%s',
sanitize_text_field( wp_unslash( $_POST['payment_request_type'] ) ), // phpcs:ignore
$mask( wp_unslash( $_POST['billing_phone'] ?? '' ) ), // phpcs:ignore
$mask( wp_unslash( $_POST['shipping_phone'] ?? '' ) ), // phpcs:ignore
sanitize_text_field( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ?? '' ) )
) );
// 2. Who can change or reject the phone on this site.
foreach ( array( 'woocommerce_process_checkout_field_billing_phone', 'woocommerce_process_checkout_tel_field', 'woocommerce_checkout_posted_data', 'woocommerce_validate_phone' ) as $hook ) {
$log( '2 hooked on ' . $hook . ': ' . $who( $hook ) );
}
// 3. The phone right before your own filter (priority 5) runs.
add_filter( 'woocommerce_checkout_posted_data', function ( $data ) use ( $log, $mask ) {
$log( '3 before your filter: billing=' . $mask( $data['billing_phone'] ?? '' ) );
return $data;
}, 4 );
// 4. The phone after every filter, i.e. what WooCommerce validates.
add_filter( 'woocommerce_checkout_posted_data', function ( $data ) use ( $log, $mask ) {
$log( '4 after all filters: billing=' . $mask( $data['billing_phone'] ?? '' ) );
return $data;
}, PHP_INT_MAX );
// 5. WooCommerce's verdict: format check alone vs. final result after other plugins.
add_filter( 'woocommerce_validate_phone', function ( $valid, $phone ) use ( $log, $mask ) {
$log( sprintf( '5 validate: phone=%s format_ok=%s final=%s', $mask( $phone ), WC_Validation::is_phone_format( $phone ) ? 'yes' : 'no', $valid ? 'valid' : 'INVALID' ) );
return $valid;
}, PHP_INT_MAX, 2 );
}, 1 );
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment