Using PHP Generic Objects To Organize Your Code

By  on  

I like using PHP classes to keep my code organized and easy to maintain. I know that they consume more memory than simply using an array or variable system, but if memory is an issue, I'd get more memory. Saving time by coding in a maintainable fashion is more important to me than upgrading a server. I've written a very generic, flexible PHP class that I use in my PHP code to get and set variables for use within the page.

The PHP Class

/* generic class */
class generic {

	var $vars;

	//constructor
	function generic() {  }

	// gets a value
	function get($var) {
		return $this->vars[$var];
	}

	// sets a key => value
	function set($key,$value) {
		$this->vars[$key] = $value;
	}

	// loads a key => value array into the class
	function load($array) {
		if(is_array($array)) {
			foreach($array as $key=>$value) {
				$this->vars[$key] = $value;
			}
		}
	}

	// empties a specified setting or all of them
	function unload($vars = '') {
		if($vars) {
			if(is_array($vars)) {
				foreach($vars as $var) {
					unset($this->vars[$var]);
				}
			}
			else {
				unset($this->vars[$vars]);
			}
		}
		else {
			$this->vars = array();
		}
	}

	/* return the object as an array */
	function get_all() {
		return $this->vars;
	}

}

Simple Usage

/* simple usage -- just gets and sets */
$person = new generic();

// set sample variables
$person->set('name', 'David');
$person->set('age', '24');
$person->set('occupation', 'Programmer');

// echo sample variables
echo '<strong>Name:</strong> ',$person->get('name'); // returns Name: David
echo '<strong>Age:</strong> ',$person->get('age'); // returns Age: 24
echo '<strong>Job:</strong> ',$person->get('occupation'); // returns Job: Programmer

// erase some variables -- first a single variable, then an array of variables
$person->unload('name');
$person->unload(array('age', 'occupation'));

Database Usage

/* database-related usage */
$query = 'SELECT name, age, occupation FROM users WHERE user_id = 1';
$result = mysql_query($query);
$row = mysql_fetch_assoc($result);

$user = new generic();

$user->load($row);
// echo sample variables
echo '<strong>Name:</strong> ',$user->get('name'); // returns Name: David
echo '<strong>Age:</strong> ',$user->get('age'); // returns Age: 24
echo '<strong>Job:</strong> ',$user->get('occupation'); // returns Job: Programmer

Save To The Session, Retrieve From The Session

$_SESSION['person'] = $person->get_all();

/* and on the next page, you'll retrieve it */

$person = new generic();
$person->load($_SESSION['person']);

Let me know your thoughts on this type of class. Do you have any suggestions for improvement?

Recent Features

Incredible Demos

Discussion

  1. Hi, David

    (please excuse my bad english speech)

    It’s a very good aproach.

    I like to use magic functions like __call() in one class called persistence and extend the others, because i use much get and set for organization and encapsulation porpouses.

    function __call($method, $args) {
            $atributo = strtolower(substr($method, 3, 1)).substr($method, 4);
    		switch (substr($method, 0, 3)) {
                case "get": return $this->{$atributo}; break;
                case "set": $this->{$atributo} = $args[0];
            }
    }
    

    this work for generic too. Ex:

    class person extends persistence {
    /* No code here */
    }
    $person = new person();
    $person->setName('David');
    $person->setAge('24');
    $person->setOccupation('Programmer');
    echo $person->getName(); /* etc */
    

    The persistence have too methods for save, delete, construct load and other things. So this is possible:

    $person = new person(1);
    $person->setAge(25) /* Next Year :-D */
    $person->save();
    $person->delete(); /* No more David :-( */
    

    OBS: Please, how to you make the code PHP look so good?

  2. I like what you’ve presented here Gevã. Makes sense and I think others will appreciate what you’ve posted!

    I use SyntaxHighlighter for the code highlighting — some quick, light-weight javascript and viola — great looking code!

  3. why don’t you use php’s array functions in load and unload? you can completely avoid your foreach loops using _faster_ builtin functions. or are those loops just for illustration purpose?

  4. Thank you for commenting Harald. Which built in functions would you suggest to optimize the code?

  5. Thiago

    using PDO you can generate the object with the fetch method

    http://br.php.net/manual/en/function.PDOStatement-setFetchMode.php

  6. kenman

    This very much reminds me of the registry pattern. It’s also similar to the PEAR::Config. You should look into those- they’re both mature and look to accomplish what I believe you’re trying to accomplish here.

  7. JP

    Assuming you want to use php5, why not take what Gevã suggests and take it a step further to make things even easier (IMHO)? Instead of using __call(), use __set() and __get() like this:

      function __get($key) { return $this->vars[ $key ]; }
      function __set($key,$value) { $this->vars[ $key ] = $value; }
    

    Then you can do something like this:

    $person = new generic();
    $person->Name = 'David';
    $person->Age = '24';
    echo $person->Name,' is ',$person->Age,'';
    

    Much cleaner and, more importantly, less typing :)

  8. What benefits do these generic objects have over associative arrays?

    Is this done simply for the OO feel? How is the maintainability improved?

    $state_of_mind[‘me’] = ‘confused’;

    ;)

  9. @Wally: I prefer this over associative arrays mostly because I feel the OO way of set and get is much more “English” friendly. I don’t have a big issue with associative arrays. This method also makes it easier for me to explain the code to others.

    @JP: Excellent work with your PHP5 example. Unfortunately, PHP5 isn’t always an option which is why I do things the way I do. Great post though — thank you!

  10. I use this code to append default values to my objects.

    function append_default(&$obj)
    {
     $default = func_get_arg(1);
     for($i = 2, $total = func_num_args(); $i < $total; $i++)
      $obj->{func_get_arg($i)} = $default;
    }
    

    You can call this code like

    append_default($some_object,"-","name","age");
    

    and the object will have name and age member variables set

  11. Under PHP5 this generic class is a perfect candidate for implementing the Iterator interfase:

    http://en.wikipedia.org/wiki/Iterator#PHP

  12. IMHO, your class is too “generic” :)

    Seems to be an excuse to use OOP.

    I use Objects whenever I feel it is necessary, but I remind myself that every tool has its purpose. I wouldn’t use your class everywhere like you seem to be doing. I would use an ORM: something like Propel or Doctrine or even DB_DataObject (PEAR) or PDO for handling data from an SQL. I would use a class like PEAR’s Config for site-wide variables. I try to avoid re-inventing the wheel and I use stuff that already exists as much as possible.

    Your code is very clean and almost perfectly consistent in its format. I like that :) Your braces in your ctor should follow the same format as your other methods.

    You should use an existing documentation format like phpDocumentor to automatically generate API docs.

    I agree with the others about using magic functions if you would be working in a PHP5 only environment.

    Thank you for sharing your thoughts with us. :)

  13. Marc A. Champlain

    I code only in PHP5 so I don’t know for earlier version, but you could simply do:

    $person = (object) null;  // or   $person = new stdClass();
    $person->name = "Marc";
    $person->age = "28";
    echo $person->age;
    
  14. Hey Dave! Nice class you have there! Did not think about this option before. But anyways, there is a big ‘error’ (if I can call it like that) which I like to point to. It’s not in the code, rather ‘visual’. After the heading “Database Usage” the whole content get’s bold, up to here! Dunno why, but it looks REALLY ugly! Hope you’ll fix that for others :-).

  15. @Sat: I’ll fix this. Stupid WordPress…

  16. azwan

    David, thanks for sharing this. I just started learning about OOP and this class really helps. Simple and seems practical.

    Anyway, some questions to ask (maybe other ppl can also help)

    Will you use this generic class as a class for other classes to inherit/extend?
    What about retrieving mulitple records (will it be array in array) in vars?

    I’m thinking of using the class for mainly data retrieving/manipulation from database.

    Thanks in advance.

  17. @azwan: I don’t do anything too terrible advanced with this class, although you could. That’s the beauty in its simplicity!

  18. Thomas Eng

    You could also do:

    function puts($s) {
        print $s . "\n";
    }
    
    class Object {
        function __construct() {
            $a = array_shift( func_get_args() );
            foreach($a as $key => $value) {
                $this->{$key} = $value;
            }
        }
    }
    $o = new Object(array( "name" => "John Doe", "age" => 31) );
    puts($o->name);
    puts($o->age);
    
  19. Tim

    Simplify by using

    mysql_fetch_object($row, 'generic')
    

    This will populate all the fields of the given object using returned rows. It returns the new object.

  20. Marcin

    tim, you’re right ;-)
    Or we can use casting:

     'b',
    	'c' => 'd',
    );
    
    //Make generic: ;-)
    $obj = (object)$array;
    
    
    var_dump($obj);
    
    //unload
    unset($obj->a);
    
    var_dump($obj);
    
    //set
    $obj->test = 'foo';
    var_dump($obj);
    
    //get
    echo $obj->c;
    
    
  21. I have learned a lot of useful things and enhance my my in programming from your great blog

    thank you very much

  22. Just reorganized my class however I’m wonder why have a get() function and not just use $user->var[‘name’]?

  23. Gino D.

    A better generic class:

    abstract class Generic {
    
    	public function __construct(array $data = []) {
    		foreach ($data as $name => $value) {
    			$this->{$name} = $value;
    			$label = str_replace(' ', null, ucwords(str_replace('_',' ', $name)));
    
    			if (!property_exists($this, $method = 'get' . $label) && !method_exists($this, $method))
    				$this->{$method} = function() use ($name) { return $this->{$name}; };
    
    			if (!property_exists($this, $method = 'set' . $label) && !method_exists($this, $method))
    				$this->{$method} = function($value) use ($name) { $this->{$name} = $value; };
    		}
    	}
    
    	final public function __call(string $method, array $arguments = []) {
    		array_unshift($arguments, $this);
    
    		if (isset($this->{$method}) && ($callback = $this->{$method}) instanceOf \Closure)
    			return $callback->call(... $arguments);
    
    		throw new Error(sprintf('Call to undefined method %s::%s()', get_called_class(), $method));
    	}
    }
    
    
    //
    
    class Person extends Generic {}
    
    $Person = new Person(['full_name' => null, 'job_occupation' => null, 'gender' => 'male']);
    $Person->setFullName('John Doe');
    $Person->setJobOccupation('PHP Dev');
    
    var_dump($Person->getGender() . ' ' . $Person->getJobOccupation() . ' ' . $Person->getFullName()); 
    
    //generate errors
    $Person->getfullname();
    $Person->getMethodNotExists();
    
    • Gino D.

      If you want have the getters/setters in not case sensitive just replace:

      1. $label = str_replace(' ', null, ucwords(str_replace('_',' ', $name))); ===> $label = strtolower(str_replace('_', null, $name)); // in __construct
      
      2. if (isset($this->{$method}) && ($callback = $this->{$method}) instanceOf \Closure) ===> if (isset($this->{($lcmethod = strtolower($method))}) && ($callback = $this->{$lcmethod}) instanceOf \Closure) // in __call
      
    • Gino D.

      To extend the class with anonymous methods:

      //property always in lowercase
      $Person->getinfo = function() { return $this->full_name . ', ' . $this->getJobOccupation(); };
      
      var_dump($Person->getInfo(), $Person->getINFO());
      

Wrap your code in <pre class="{language}"></pre> tags, link to a GitHub gist, JSFiddle fiddle, or CodePen pen to embed!