Created
March 19, 2013 12:11
-
-
Save oodavid/5195592 to your computer and use it in GitHub Desktop.
OOP - Method and Property bubbling in PHP
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* BUBBLER CLASS | |
* | |
* The Magic Methods __get, __set and __call are called by PHP when the object cannot | |
* find or access methods or properties within the object, the Bubbler class intercepts | |
* these and simply routes them to their "parent" object. | |
* | |
* +--------------------------------------+ | |
* | Object A /\ Plain Object | | |
* | +---------------| |---------------+ | | |
* | | Object B | | Bubbler | | | |
* | | +-------------| |-------------+ | | | |
* | | | Object C | | Bubbler | | | | |
* | | +------------------------------+ | | | |
* | +----------------------------------+ | | |
* | METHOD BUBBLING | | |
* | PROPERTY BUBBLING | | |
* +--------------------------------------+ | |
* | |
* This class was created for a query builder. The main class contained the database | |
* handler and some schemata relating to the database structure. The child classes | |
* were sql(), select(), upsert() and delete() - I wanted each of which to be fully chainable | |
* and also to be able to access the parent database handler and schemata and I didn't | |
* want any OTT engineering. In my query builder you can do these: | |
* | |
* query->sql('SELECT TABLES')->execute()->debug(); | |
* query->select('*')->from('users')->join('avatar')->where('id', 1)->limit(1)->execute()->debug(); | |
* query->delete()->from('users')->where('id', 10)->limit(1)->execute()->debug(); | |
* query->upsert('users')->data(array( 'id'=>NULL, 'email'=>'david@oodavid.com' ))->return(TRUE)->execute()->debug(); | |
* | |
* As you can see, each use execute() and debug(), lets explain what happens: | |
* execute() is defined as methods on the child objects sql, select, upsert and delete | |
* They each build a SQL variable and store it as a property on the parent query object. | |
* They also access the parents database handler and execute the query. | |
* debug() however is NOT defined on any of the child objects, thus the method bubbles up to the parent query object. | |
* Since the child objects have set the SQL property, this common method runs an analysis of the query showing timings, | |
* bottlenecks etc. etc. | |
* | |
* @author David King | |
* @copyright Copyright (c) 2013 + | |
*/ | |
class Bubbler{ | |
var $that; | |
public function __construct(&$that){ | |
// Store the reference to the "parent" object | |
$this->that = &$that; | |
} | |
public function __get($name){ | |
// Return the "parent" property if it exists | |
if(property_exists($this->that, $name)){ | |
return $this->that->$name; | |
} | |
} | |
public function __set($name, $value){ | |
// Set it on the parent property if it exists | |
if(property_exists($this->that, $name)){ | |
$this->that->$name = $value; | |
} else { | |
$this->$name = $value; | |
} | |
} | |
public function __call($name, $args){ | |
// Try and call it on the $that "parent"... | |
if(is_callable(array($this->that, $name))){ | |
return call_user_func_array(array($this->that, $name), $args); | |
} | |
} | |
} | |
/** | |
* EXAMPLE | |
* | |
* This example outputs the following: | |
* | |
* I am ObjectA and my name is: David King | |
* I am ObjectB and I am going to call sayMyName() - since I don't have that method it bubbles up to Object A, look: | |
* I am ObjectA and my name is: David King | |
* I am ObjectC and I will try to access a property I don't have, again it will bubble up to ObjectA... My Name is: David King | |
*/ | |
class ObjectA{ | |
var $name; | |
public function __construct($name){ | |
// Store the name | |
$this->name = $name; | |
} | |
public function sayMyName(){ | |
// Echo the name and return this for chaining | |
echo "I am ObjectA and my name is: {$this->name}\n"; | |
return $this; | |
} | |
public function B(){ | |
// Create and return an instance of ObjectB | |
$tmp = new ObjectB(&$this); | |
return $tmp; | |
} | |
public function C(){ | |
// Create and return an instance of ObjectC | |
$tmp = new ObjectC(&$this); | |
return $tmp; | |
} | |
} | |
class ObjectB extends Bubbler{ | |
public function saySomething(){ | |
// Say something and return this for chaining | |
echo "I am ObjectB and I am going to call sayMyName() - since I don't have that method it bubbles up to Object A, look:\n "; | |
$this->sayMyName(); | |
return $this; | |
} | |
} | |
class ObjectC extends Bubbler{ | |
public function saySomething(){ | |
// Say something and return this for chaining | |
echo "I am ObjectC and I will try to access a property I don't have, again it will bubble up to ObjectA... My Name is: {$this->name}\n"; | |
// return this for chaining | |
return $this; | |
} | |
} | |
$objectA = new ObjectA('David King'); | |
$objectA->sayMyName()->B()->saySomething()->C()->saySomething(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment