Skip to content

Instantly share code, notes, and snippets.

@oodavid
Created March 19, 2013 12:11
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save oodavid/5195592 to your computer and use it in GitHub Desktop.
Save oodavid/5195592 to your computer and use it in GitHub Desktop.
OOP - Method and Property bubbling in PHP
<?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