<?php

class JSObject implements ArrayAccess {
    private $data = array();

    function __construct($d) {
        $this->data = array_replace($this->data, $d);
    }

    public function __set($name, $value) {
        $this->data[$name] = $value;
    }
    public function __get($name) {
        global $undefined;
        if(array_key_exists($name, $this->data)) {
            return $this->data[$name];
        } else {
            return $undefined;
        }
    }
    public function __isset($name) {
        return array_key_exists($name, $this->data);
    }
    public function __unset($name) {
        if(array_key_exists($name, $this->data)) {
            unset($this->data[$name]);
        }
    }

    public function offsetExists($item) {
        return $this->__isset($item);
    }
    public function offsetGet($item) {
        return $this->__get($item);
    }
    public function offsetSet($item, $value) {
        $this->__set($item, $value);
    }
    public function offsetUnset($item) {
        $this->__unset($item);
    }
       
    function __toString() {
        return '[object Object]';
    }

    public function __debugInfo() {
        return $this->data;
    }
}        

class JSUndefined implements ArrayAccess {
    public function __set($name, $value) {
        global $undefined;
        return $undefined;
    }
    public function __get($name) {
        global $undefined;
        return $undefined;
    }
    public function __isset($name) {
        global $undefined;
        return $undefined;
    }
    public function __unset($name) {
        global $undefined;
        return $undefined;
    }

    public function offsetExists($item) {
        global $undefined;
        return $undefined;
    }
    public function offsetGet($item) {
        global $undefined;
        return $undefined;
    }
    public function offsetSet($item, $value) {
        global $undefined;
        return $undefined;
    }
    public function offsetUnset($item) {
        global $undefined;
        return $undefined;
    }

    function __toString() {
        return 'undefined';
    }
}

$undefined = new JSUndefined();


$x = new JSObject(['a' => 12]);
echo sprintf("%s | %s\n", $x, print_r($x, true));

echo sprintf("(%s, %s)\n", $x->a, $x['a']);

$x->a = 42;

echo sprintf("(%s, %s)\n", $x->a, $x['a']);

$x['b'] = 43;

echo sprintf("(%s, %s)\n", $x->b, $x['b']);

unset($x->b);

echo sprintf("%s\n", $x->b);

echo sprintf("%s\n", var_export($x->b == $undefined, true));

echo sprintf("%s\n", print_r($x, true));
