2

Taste of JavaScript in Php

Well, It’s true that Php was not matured enough in it’s early days but now Php become very so much powerful and recently Php 5.5.x has been arrived with some cool new features which are really powerful additions to Php so thanks to Php who are working hard to make this language powerful and robust. Anyways…

If you don’t want to read the full article and just want to find out what I’m actually talking about then you may directly jump here.

If anyone ask me to mention only one feature of Php 5.x that I love more then the answer would be definitely Lambada, support for anonymous functions, also known as closures, which is one the most exciting, powerful and most useful feature since Php 5.3, though in Php 5.3 there were some limitations in anonymous functions, for example, in Php 5.3 keyword $this was not allowed in an anonymous function, which means, in Php 5.3 if you are using a Closure inside of a class, the Closure will not have access to $this, You must send a reference to $this, but, you can’t send $this directly, for example :

class MyClass
{
    public $foo = 'foo';

    public function bar() {
        // Get a reference to $this
        $self = &$this;
        $fn = function () use ($self) {
            echo $self->foo; // foo
        }
    }
}

In Php 5.4, the support for $this in Closures has been added. That’s a good news. Thanks to the developers of the language, once again. In Php 5.4, there are also two new functions has been added, which are really very helpful with Closures and these are bind and bindTo. These functions used with Closures to change or set the object binding, which means, someone can specify, which object a Closure should be bound to, for example :

// Declare an anonymous function
$fn = function(){
    return ++$this->foo; // increase the value
};
// Declare a class
class Bar{
    private $foo = 1; // initial value
}
// Make an instance and bind the
// closure to this ($bar) instance
$bar = new Bar;
$func = $fn->bindTo($bar, 'Bar');
echo $func(); // 2

Since, I’m familiar with these new features of Php, I was thinking that, if I could write Php code just like JavaScript, for example, in JavaScript we can write something like this

var obj = new Object();
obj.name = "Sheikh Heera";
obj.getName = function(){
    return this.name;
};
console.log(obj.getName()); // "Sheikh Heera"

This is really cool! I love this language. This language is different than Php and it is a classless language, instead it’s a prototype based OOP language.

Prototype-based programming is a style of object-oriented programming in which classes are not present, and behavior reuse (known as inheritance in class-based languages) is performed via a process of cloning existing objects that serve as prototypes. This model can also be known as classless, prototype-oriented or instance-based programming. Delegation is the language feature that supports prototype-based programming.

Read more about prototype based programming and example of JavaScript Prototype.

So, I was thinking that if I could write same code in Php and one day I just started to write some code and wrote something like this

$obj = new stdClass;
$obj->name = "Sheikh Heera";
$obj->getName = function(){
    return $this->name;
};
// Lets use it now
echo $obj->name; // Sheikh heera, well done
echo $obj->getName(); //Oops! undefined method stdClass::getName()

So, I didn’t success, Php raised a fatal error and it’s logical. The stdClass is Php's generic empty class, one of Php’s predefined classes kind of like Object in JavaScript but not actually used as universal base class for an object. Which is just like an empty container, only can hold dynamic properties and used as type of object

function objTest(stdClass $o)
{
    echo gettype($o); // object
    echo $o->prop; // Hello World
}
$obj = new stdClass;
$obj->prop = 'Hello World';
// Call the function
objTest($obj); // Output : object Hello World
//  
// Create an array
$arr = array('a', 'b', 'c');
// Convert the array to an object
$arrObj = (object) $arr;
var_dump($obj);
// Output :
/*
 * object(stdClass)[5]
 *   public 'prop' => string 'Hello World' (length=11)
 */
var_dump($arrObj);
// Output :
/*
 * object(stdClass)[7]
 *   string 'a' (length=1)
 *   string 'b' (length=1)
 *   string 'c' (length=1)
 */

Another Example :

class Foo{}
$foo = new Foo;
echo ($foo instanceof stdClass) ? 'Yes' : 'NO'; // No

So, $obj = new stdClass means that $obj is an object and an instance of stdClass and it can’t house dynamic methods like JavaScript but it can hold dynamic properties and it could be a scalar variable or even an array. For example, if you write

$obj1 = (object) 'Howdy!';
echo $obj1->scalar; // Howdy! scalar is the property, created automatically
// Or an array
$obj1 = (object)  array('one'=>1, 'two'=>2);
// Output :
 /*
  * object(stdClass)[8]
  *   public 'one' => int 1
  *   public 'two' => int 2
  */
echo $obj1->one // 1
echo $obj1->two // 2

So, what I want to do is that, to add dynamic methods in an object, that is already an object instance made by Php using the new keyword and I thought that, I can use stdClass but it’s not possible using this generic class, so I need to use something else to simulate the prototype based programming style in Php (not 100% original but make it look like that), so now ?

Finally it’s done…

Now I can write code like JavaScript in Php just like this :

$obj = new stdObject;
$obj->name = "Sheikh Heera";
$obj->getName = function(){
    return $this->name;
};
echo $obj->name; // Sheikh Heera, well done
echo $obj->getName(); // Sheikh Heera, it worked !

So, finally, I’ve figured out a way to create dynamic methods but without stdClass, instead using my own stdObject class that, I’ve written below :

class stdObject {
    /**
      * The prototype array to hold properties.
      *
      * @var array
      */
    private $prototype = array();
    /**
      * Create a new stdObject instance.
      *
      * @param  array|object (instance of stdObject)
      * @return void
      */
    public function __construct($param = null)
    {
        try{
            if(!is_null($param) && is_array($param)) {
                foreach ($param as $key => $value) {
                    if(is_callable($value)) {
                        $this->prototype[$key] = $value->bindTo($this);
                    }
                    elseif(is_scalar($value) || is_array($value)) {
                        $this->prototype[$key] = $value;
                    }
                }
            }
            elseif (is_object($param)) {
                $this->prototype = $param->prototype;
            }
        }
	catch(Exception $e) {
            echo $e->getMessage() . '
'; var_dump($e->getTraceAsString()); exit; } } /** * Check if a property/method is set in the prototype array. * * @param string $key * @return void */ public function __isset($key) { return isset($key); } /** * Set a property/method in the prototype array using $key => $value. * * @param string $key * @param mixed $value * @return void */ public function __set($key, $value) { try{ if(is_callable($value)) { $this->prototype[$key] = $value->bindTo($this); } elseif(is_scalar($value) || is_array($value)) { $this->prototype[$key] = $value; } else { throw new RunTimeException("Invalid data type {$value} given!"); } } catch(RunTimeException $e) { echo $e->getMessage() . '
'; var_dump($e->getTraceAsString()); exit; } } /** * Retrieve a property/method from the prototype array using $key. * * @param string $key * @return mixed */ public function __get($key) { try{ if(isset($this->prototype[$key])) { return $this->prototype[$key]; } else { throw new RunTimeException("Property '{$key}' doesn't exist !"); } } catch(RunTimeException $e) { echo $e->getMessage() . '
'; var_dump($e->getTraceAsString()); exit; } } /** * Call a method that is available in the prototype array. * * @param string $methodname * @param array $args * @return mixed */ public function __call($methodName, array $args) { try{ if (isset($this->prototype[$methodName])) { return call_user_func_array($this->prototype[$methodName], $args); } else{ throw new RunTimeException("Method {$methodName} doesn't exist !"); } } catch(RunTimeException $e) { echo $e->getMessage() . '
'; var_dump($e->getTraceAsString()); exit; } } }

So, now I can use it like JavaScript, just like this :

// prepare an array with properties and methods
$array = array(
    'name' => 'Sheikh Heera',
    'age' => 35,
    'sex' => 'Male',
    getInfo' => function() {
        if(isset($this->name)) $info = 'Name : ' . $this->name .'
'; if(isset($this->age)) $info .= 'Age : ' . $this->age . '
'; if(isset($this->sex)) $info .= 'Sex : ' . $this->sex . '
'; return $info; } ); // create a new stdobject with the array as argument $obj = new stdObject($array); // Add another property to the $obj $obj->address = 'My Sweet Home!'; // call the function echo $obj->getInfo(); // echo the address property echo 'Address : ' . $obj->address; // Output : /** Name : Sheikh Heera * Age : 35 * Sex : Male * Address : My Sweet Home! */ // Another object using old $obj object $objNew = new stdObject($obj); // Change the name $objNew->name = 'Robot'; // Create another new for $objNew function $objNew->getAge = function() { return $this->age; }; // print the new name echo 'New name : ' . $objNew->name; // Robot // print the new address $objNew->address = "The Earth"; // print the old age using new method echo 'Old Age : ' . $objNew->getAge(); // 35 // set new age $objNew->age = 30; // Print new age using new method echo 'New Age : ' . $objNew->getAge(); // 30 // Print new address echo 'New Address : ' . $objNew->address; // The Earth // Another empty object $foo = new stdObject(); $foo->name = "Foo"; // set name echo 'Name of Foo : ' . $foo->name; // Foo

Alternatively, you can use a trait, available since Php 5.4 to wrap all the functionality of the stdClass, something like this

trait MethodBuilder {
    // Put all the code from stdClass here
    private $prototype = array();
    public function __construct($param = null)
    {
        // ...
    }
    // ...
} 

Now, create classes and use the trait like :

// A simple stdObject class
class stdObject {
    use MethodBuilder;
}

// Another class using trait 
// and it's own public method
class Bar {
    use MethodBuilder;
    public function foo()
    {
        echo "I'm new foo !";
    }
}

So, now you can use them as

// Make an instance of stdObject
$anStdObj = new stdObject(array('greet'=>'Hello!'));
echo $anStdObj->greet; // Hello
// Make an instance of Bar
$newFoo = new Bar;
$newFoo->baz = "Hello World!";
$newFoo->showBaz = function(){
    return $this->baz;
};
// Call Bar's own foo method
// declared in the class
echo $newFoo->foo(); // I'm new foo !
// Call the dynamic method
echo $newFoo->showBaz(); // Hello World!

Well, this is not same like JavaScript and I’ve just shared my random thoughts, an idea that came in to my mind and nothing else. If someone wants to give me any feedback then feel free and if someone can modify it to improve then please go on and share your idea. Thanks!

→ An updated version is available here and this one also requires PHP 5.4+ but much better, IMO.

Latest Blog

0
Php

PHP – 8.0 Match Expression

In PHP 8.0 there is a new feature or I should say a new language construct (keyword) going to be introduced,  which has been implemented depending […]

0
Php

New Union Type in PHP – 8

A new RFC has been finished voting and accepted for PHP – 8. That is “Union Types”, which is an extension of PHP’s type system. […]