: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
* It handles json output for use on backend and frontend.
* @package AdvancedAds\Framework
* @author Advanced Ads <info@wpadvancedads.com>
namespace AdvancedAds\Framework;
use InvalidArgumentException;
defined( 'ABSPATH' ) || exit;
private $default_object_name = null;
* @param string $object_name Object name to be used.
public function __construct( $object_name ) {
$this->default_object_name = $object_name;
* @throws InvalidArgumentException When object name not defined.
public function hooks(): void {
if ( empty( $this->default_object_name ) ) {
throw new InvalidArgumentException( 'Please set default object name to be used when printing JSON.' );
$hook = is_admin() ? 'admin_footer' : 'wp_footer';
add_action( $hook, [ $this, 'output' ], 0 );
* @param mixed ...$args Arguments.
* 1. array|string Unique identifier or array<key, value>.
* 2. array|string The data itself can be either a scalar or an array.
* In Case of first param an array this can be object_name.
* 3. string Name for the JavaScript object.
* Passed directly, so it should be qualified JS variable.
public function add( ...$args ): JSON {
list( $key, $value, $object_name ) = $this->get_add_data( $args );
if ( is_array( $key ) ) {
foreach ( $key as $arr_key => $arr_value ) {
$this->add_to_storage( $arr_key, $arr_value, $object_name );
$this->add_to_storage( $key, $value, $object_name );
* @param string $key Unique identifier.
* @param mixed $value The data itself can be either a single or an array.
* @param string $object_name Name for the JavaScript object.
* Passed directly, so it should be qualified JS variable.
private function add_to_storage( $key, $value, $object_name ): void {
// If key doesn't exists.
if ( ! isset( $this->data[ $object_name ][ $key ] ) ) {
$this->data[ $object_name ][ $key ] = $value;
// If key already exists.
$old_value = $this->data[ $object_name ][ $key ];
$is_array = is_array( $old_value ) && is_array( $value );
$this->data[ $object_name ][ $key ] = $is_array ? array_merge( $old_value, $value ) : $value;
* Remove from JSON object.
* @param string $key Unique identifier.
* @param string $object_name Name for the JavaScript object.
* Passed directly, so it should be qualified JS variable.
public function remove( $key, $object_name = false ): JSON {
if ( empty( $object_name ) ) {
$object_name = $this->default_object_name;
if ( isset( $this->data[ $object_name ][ $key ] ) ) {
unset( $this->data[ $object_name ][ $key ] );
public function clear_all(): JSON {
$this->data[ $this->default_object_name ] = [];
public function output(): void {
$script = $this->encode();
echo "<script type='text/javascript'>\n"; // CDATA and type='text/javascript' is not needed for HTML 5.
echo "/* <![CDATA[ */\n";
echo "$script\n"; // phpcs:ignore
private function encode(): string {
foreach ( $this->data as $object_name => $object_data ) {
$script .= $this->single_object( $object_name, $object_data );
* @param string $object_name Object name to use as JS variable.
* @param array $object_data Object data to json encode.
private function single_object( $object_name, $object_data ): string {
if ( empty( $object_data ) ) {
foreach ( (array) $object_data as $key => $value ) {
if ( ! is_scalar( $value ) ) {
$object_data[ $key ] = html_entity_decode( (string) $value, ENT_QUOTES, 'UTF-8' );
return "var $object_name = " . wp_json_encode( $object_data ) . ';' . PHP_EOL;
* Normalize add arguments
* @param array $args Arguments array.
private function get_add_data( $args ): array {
$key = $args[0] ?? false;
$value = $args[1] ?? false;
$object_name = $args[2] ?? $this->default_object_name;
if ( is_array( $key ) && ! empty( $value ) ) {