$key ) ) { $cur = $cur->$key; } elseif ( is_array( $cur ) && 'length' === $key ) { return count( $cur ); } elseif ( is_string( $cur ) && 'length' === $key ) { return function_exists( 'mb_strlen' ) ? mb_strlen( $cur ) : strlen( $cur ); } else { return self::undef(); } } return $cur; } /** True when JS `obj.key !== undefined`. */ public static function defined( $base, $path ) { return ! self::is_undef( self::get( $base, $path ) ); } /** Number → string exactly as JS prints it (shortest round-trip representation). */ public static function num( $n ) { if ( is_int( $n ) ) { return (string) $n; } if ( is_nan( $n ) ) { return 'NaN'; } if ( is_infinite( $n ) ) { return $n > 0 ? 'Infinity' : '-Infinity'; } if ( floor( $n ) == $n && abs( $n ) < 1e21 ) { return number_format( $n, 0, '.', '' ); } $s = json_encode( $n, JSON_PRESERVE_ZERO_FRACTION ); if ( false !== strpos( $s, 'e' ) || false !== strpos( $s, 'E' ) ) { // JS style exponent: 1e-7, 1.5e+21 $s = preg_replace_callback( '/e([+-]?)0*(\d+)/i', function ( $m ) { return 'e' . ( '-' === $m[1] ? '-' : '+' ) . $m[2]; }, $s ); } return $s; } /** `${value}` in a template literal / String(value). */ public static function str( $v ) { if ( $v instanceof WPVFB_JS_Undefined ) { return 'undefined'; } if ( null === $v ) { return 'null'; } if ( true === $v ) { return 'true'; } if ( false === $v ) { return 'false'; } if ( is_int( $v ) || is_float( $v ) ) { return self::num( $v ); } if ( is_array( $v ) ) { if ( self::is_list( $v ) ) { return implode( ',', array_map( function ( $x ) { return self::is_nullish( $x ) ? '' : self::str( $x ); }, $v ) ); } return '[object Object]'; } return (string) $v; } public static function is_list( $v ) { if ( ! is_array( $v ) ) { return false; } return array() === $v || array_keys( $v ) === range( 0, count( $v ) - 1 ); } /** Number(v) / unary plus. */ public static function to_number( $v ) { if ( is_int( $v ) || is_float( $v ) ) { return $v; } if ( true === $v ) { return 1; } if ( false === $v || null === $v ) { return 0; } if ( $v instanceof WPVFB_JS_Undefined || is_array( $v ) ) { return NAN; } $s = trim( (string) $v ); if ( '' === $s ) { return 0; } if ( is_numeric( $s ) ) { return $s + 0; } return NAN; } /** parseInt(v, 10). */ public static function parse_int( $v ) { $s = ltrim( self::str( $v ) ); if ( preg_match( '/^[+-]?\d+/', $s, $m ) ) { return (int) $m[0]; } return NAN; } /** parseFloat(v). */ public static function parse_float( $v ) { $s = ltrim( self::str( $v ) ); if ( preg_match( '/^[+-]?(\d+\.?\d*|\.\d+)([eE][+-]?\d+)?/', $s, $m ) ) { return (float) $m[0]; // num() prints whole floats without a fraction, like JS } return NAN; } /** Strict equality for scalars (===). */ public static function eq( $a, $b ) { if ( ( is_int( $a ) || is_float( $a ) ) && ( is_int( $b ) || is_float( $b ) ) ) { return $a == $b; } if ( $a instanceof WPVFB_JS_Undefined || $b instanceof WPVFB_JS_Undefined ) { return $a instanceof WPVFB_JS_Undefined && $b instanceof WPVFB_JS_Undefined; } return $a === $b; } /** Array.isArray */ public static function is_array( $v ) { return is_array( $v ) && self::is_list( $v ); } /** typeof v === 'object' && v !== null (arrays count, as in JS). */ public static function is_object( $v ) { return is_array( $v ) || ( is_object( $v ) && ! ( $v instanceof WPVFB_JS_Undefined ) ); } /** Array/list helper: returns [] for anything that is not a JS array. */ public static function arr( $v ) { return self::is_array( $v ) ? $v : array(); } }