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 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.
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.