OOP in the style of JavaScript with PHP

Chapter 1: Literals

 

JavaScript

PHP

Output:

{ a: 1, b: 2 }

Output:

Array
(
    [a] => 1
    [b] => 2
)

var o1 = {
    a: 1,
    b: 2
};
console.log(o1);
$o1 = [
    'a' => 1,
    'b' => 2
];
print_r($o1);

stdClass Object
(
    [a] => 1
    [b] => 2
)

$o1 = (object)[
    'a' => 1,
    'b' => 2
];
print_r($o1);

Casting to object

JavaScript

PHP

Output:

{ a: 1, b: 2 }

Output:

stdClass Object
(
    [a] => 1
    [b] => 2
)

var o1 = new Object();
o1.a = 5;
o1.b = 6;
console.log(o1);
$o1 = new stdClass();
$o1->a = 5;
$o1->b = 6;
print_r($o1);

JavaScript

PHP

Output:

Hello, World!

Output:

PHP Fatal error:  Uncaught Error: Call to undefined method stdClass::a() in /home/milko/work/private/jsphp-presentation/example.php:6
Stack trace:
#0 {main}
  thrown in /home/milko/work/private/jsphp-presentation/example.php on line 6

 

var o1 = {};
o1.func = function (someArg) {
    console.log(someArg);
};
o1.func('Hello, World!');
$o1 = new stdClass();
$o1->func = function ($someArg) {
    print_r($someArg);
};
$o1->func('Hello, World!');

Note:

($o1->func)('Hello, World!');

will work!

The JSObject class

class JSObject {

    public function __call($name, $arguments) {
        if ($this->$name instanceof Closure) {
            return call_user_func_array($this->$name, $arguments);
        }
        throw new NotAFunctionException("{$this->$name} is not a function!");
    }

}
class NotAFunctionException extends Exception {};

JavaScript

PHP

Output:

Hello, World!

Output:

Hello, World!

var o1 = {};
o1.func = function (someArg) {
    console.log(someArg);
};
o1.func('Hello, World!');
$o1 = new JSObject();
$o1->func = function ($someArg) {
    print_r($someArg);
};
$o1->func('Hello, World!');

JavaScript

PHP

Output:

Hello, World!The value of a is 5

Output:

PHP Fatal error:  Uncaught Error: Using $this when not in object context in /home/milko/work/private/jsphp-presentation/example.php:19
Stack trace:
#0 /home/milko/work/private/jsphp-presentation/example.php(9): {closure}('Hello, World!')
#1 /home/milko/work/private/jsphp-presentation/example.php(21): JSObject->__call('func', Array)
#2 {main}
  thrown in /home/milko/work/private/jsphp-presentation/example.php on line 19

 

var o1 = {};
o1.a = 5;
o1.func = function (someArg) {
    console.log(
        someArg
        + "The value of a is "
        + this.a
    );
};
o1.func('Hello, World!');
$o1 = new JSObject();
$o1->a = 5;
$o1->func = function ($someArg) {
    print_r(
        $someArg
        . "The value of a is " 
        . $this->a
    );
};
$o1->func('Hello, World!');

Refering to context here

The JSObject class

class JSObject {

    public function __call($name, $arguments) {
        if ($this->$name instanceof Closure) {
            return call_user_func_array($this->$name->bindTo($this), $arguments);
        }
        throw new NotAFunctionException("{$this->$name} is not a function!");
    }
}
class NotAFunctionException extends Exception {};

JavaScript

PHP

Output:

Hello, World!The value of a is 5

Output:

Hello, World!The value of a is 5

var o1 = {};
o1.a = 5;
o1.func = function (someArg) {
    console.log(
        someArg
        + "The value of a is "
        + this.a
    );
};
o1.func('Hello, World!');
$o1 = new JSObject();
$o1->a = 5;
$o1->func = function ($someArg) {
    print_r(
        $someArg
        . "The value of a is " 
        . $this->a
    );
};
$o1->func('Hello, World!');

Chapter 2: Constructors

JavaScript

PHP

Output:

Running constructor
Hello, World!The value of a is 5
[Function: CF1]
[Function]

Output:

A variaty of errors

function CF1(a) {

    console.log('Running constructor');

    this.a = a;

    this.func = function (someArg) {
        console.log(
            someArg
            + "The value of a is "
            + this.a
        );
    };
}

var CF2 = function () {
   console.log('Running constructor 2');
};

var o1 = new CF1(5);
o1.func('Hello, World!');
console.log(o1.constructor);
CF2();
console.log((new CF2()).constructor);
function CF1($a) {

    print_r('Runninng constructor');

    $this->a = $a;

    $this->func = function ($someArg) {
        print_r(
            $someArg 
            . "The value of a is "
            . $this->a
        );
    };
}

$o1 = new CF1(5);
$o1->func('Hello, World!');
print_r($o1->constructor);
print_r((new function() {})->constructor);

The

JSConstructor class

class JSConstructor extends JSObject {

    private $__callable__;

    private $__name__;

    public function __construct(Closure $constructor, string $name = null) {
        $this->__callable__ = $constructor;
        $this->__name__ = $name;
    }

    public function __invoke(...$arguments) {
        return call_user_func_array($this->__callable__, $arguments);
    }

    public function __toString() {
        return sprintf('[Function%s]', is_null($this->__name__) ? '' : ': ' . $this->__name__);
    }

    public function newInstance(...$arguments) {
        $instance = new JSObject();
        $instance->constructor = $this;
        call_user_func_array($this->__callable__->bindTo($instance), $arguments);
        return $instance;
    }
}

JavaScript

PHP

Output:

Running constructor
Hello, World!The value of a is 5
[Function: CF1]
[Function]

Output:

function CF1(a) {

    console.log('Running constructor');

    this.a = a;

    this.func = function (someArg) {
        console.log(
            someArg
            + "The value of a is "
            + this.a
        );
    };
}

var CF2 = function () {
   console.log('Running constructor 2');
};

var o1 = new CF1(5);
o1.func('Hello, World!');
console.log(o1.constructor);
CF2();
console.log((new CF2()).constructor);
$CF1 = new JSConstructor(function ($a) {

    print_r("Runninng constructor\n");

    $this->a = $a;

    $this->func = function ($someArg) {
        print_r(
            $someArg
            . "The value of a is "
            . $this->a . "\n"
        );
    };
}, 'CF1');

$CF2 = new JSConstructor(function () {
    print_r("Running constructor 2\n");
});

$o1 = $CF1->newInstance(5);
$o1->func('Hello, World!');
echo $o1->constructor . "\n";
$CF2();
echo $CF2->newInstance()->constructor;

Running constructor
Hello, World!The value of a is 5
[Function: CF1]
[Function]

Chapter 3: Inheritance

JavaScript

PHP

Output:

5
This is proto
Context is c2

 

Output:

var proto = {
    a: 5,
    context: 'c1',
    fn: function () {
        console.log('This is proto');
        console.log('Context is ' + this.context);
    }
};

var obj = {
    context: 'c2'
};
obj.__proto__ = proto;

console.log(obj.a);
obj.fn();
$proto = new JSObject();
$proto->a = 5;
$proto->context = 'c1';
$proto->fn = function () {
    echo "This is proto\n";
    echo "Context is {$this->context}\n";
};


$obj = new JSObject();
$obj->context = 'c2';
$obj->__proto__ = $proto;

echo $obj->a . "\n";
$obj->fn();

3 'undefined property' notices and a fatal NotAFunctionException

The JSObject class

class JSObject {

    public $__proto__;

    public function __call($name, $arguments) {
        if ($this->$name instanceof Closure) {
            return call_user_func_array($this->$name->bindTo($this), $arguments);
        }
        throw new NotAFunctionException("{$this->$name} is not a function!");
    }

    public function __get($name) {
        if ($this->__proto__ instanceof JSObject) {
            return $this->__proto__->$name;
        }
    }
}
class NotAFunctionException extends Exception {};

JavaScript

PHP

Output:

5
This is proto
Context is c2

 

Output:

var proto = {
    a: 5,
    context: 'c1',
    fn: function () {
        console.log('This is proto');
        console.log('Context is ' + this.context);
    }
};

var obj = {
    context: 'c2'
};
obj.__proto__ = proto;

console.log(obj.a);
obj.fn();
$proto = new JSObject();
$proto->a = 5;
$proto->context = 'c1';
$proto->fn = function () {
    echo "This is proto\n";
    echo "Context is {$this->context}\n";
};


$obj = new JSObject();
$obj->context = 'c2';
$obj->__proto__ = $proto;

echo $obj->a . "\n";
$obj->fn();

5
This is proto
Context is c2

 

JavaScript

PHP

Output:

Calling from the other side!
true

 

Output:

function C1() {}
C1.prototype.fn = function () {
   console.log('Calling from the other side!');
};

var obj = new C1();
obj.fn();
console.log(obj.__proto__ === C1.prototype);
$C1 = new JSConstructor(function () {});
$C1->prototype->fn = function () {
    echo "Calling from the other side!\n";
};

$obj = $C1->newInstance();
$obj->fn();
print_r($obj.__proto__ === $C1.prototype);

Again, lot's of errors!

The

JSConstructor class

class JSConstructor extends JSObject {

    private $__callable__;

    private $__name__;

    public $prototype;

    public function __construct(Closure $constructor, string $name = null) {
        $this->__callable__ = $constructor;
        $this->__name__ = $name;
        $this->prototype = new JSObject();
    }

    public function __invoke(...$arguments) {
        return call_user_func_array($this->__callable__, $arguments);
    }

    public function __toString() {
        return sprintf('[Function%s]', is_null($this->__name__) ? '' : ': ' . $this->__name__);
    }

    public function newInstance(...$arguments) {
        $instance = new JSObject();
        $instance->constructor = $this;
        $instance->__proto__ = $this->prototype;
        call_user_func_array($this->__callable__->bindTo($instance), $arguments);
        return $instance;
    }
}

JavaScript

PHP

Output:

Calling from the other side!
true

 

Output:

function C1() {}
C1.prototype.fn = function () {
   console.log('Calling from the other side!');
};

var obj = new C1();
obj.fn();
console.log(obj.__proto__ === C1.prototype);
$C1 = new JSConstructor(function () {});
$C1->prototype->fn = function () {
    echo "Calling from the other side!\n";
};

$obj = $C1->newInstance();
$obj->fn();
print_r($obj.__proto__ === $C1.prototype);

Calling from the other side!
true

 

Thank you!

OOP in the style of JavaScript with PHP

By Milko Kosturkov

OOP in the style of JavaScript with PHP

  • 616