OOP in PHP (part II)

Time to discover the hidden powers of the object oriented programming in PHP. Today I’ll describe the usage of serialisation, the secrets of overloading and - probably the most useful thing today - design patterns.

Serialisation

As you remember from the first part of the article, objects are completely different type of variables. You could easily write a string, number or even an array to a file… but what about objects? You can’t just write an object to a file, as PHP will try to convert it to a string, which is technically impossible, and will result in throwing an error. Yeah, I guess you can already suppose that there’s a solution.

Actually, all you have to do to serialise an object (i.e. make it writable to a file) is use one function: serialize(). As you can guess, using unserialize() function will allow you to have your object back once it has been saved to a file. Remember to define the object’s class before unserialising it, otherwise it will become useless.

Methods __sleep() and __wakeup() make serialisation in PHP5 even easier. They are called, respectively, before an object is serialised or unserialised. They are very useful e.g. for closing and reopening database connections, or saving additional information about the serialisation process. Remember that the __sleep() magic function has to return an array of all members that should be serialised.

class AutoStalker {
   private $filename, $file;

   public function __construct($filename) {
      $this->filename = $filename;
      $this->open();
   }

   public function prepare_for_stalking() {
      $this->file = fopen($this->filename, 'a');
   }

   public function stalk($text) {
      fwrite($this->file, $text."\n");
   }

   public function see_whos_been_stalked() {
      return file_get_contents($this->filename);
   }

   public function __wakeup() {
      $this->prepare_for_stalking();
   }

   public function __sleep() {
      fclose($this->file);
      return array('filename');
   }
}

In the example above, I only serialise the file’s name, because we don’t need any additional information to open that file in the future.
Now, let’s see something more useful; how about using some PHP cookies?

// include the above class declaration

$now =  date("F jS, Y, H:i");

if(!$_COOKIE['stalker']) {
   setcookie('stalker', true);
   $stalker = new AutoStalker('log.txt');
   $stalker->stalk("Started stalking on ".$now);
   echo "Hello, my friend, I'm stalking you.";
}

$stalker->stalk("Viewed the stalking log on ".$now);
echo "Hello, my friend. Here's the stalk log:<br>";
echo nl2br($stalker->see_whos_been_stalked());

Yeah, that’s basically it. Of course, the code presented here is totally useless. Some real-life situation where you might want to use serialisation is, for instance, when you have to reload the same data a few times, you can just cache the information on where it is located by serialising it.

Overloading

Basically, overloading in PHP5 is either calling a method that hasn’t been defined or getting/setting a properly that you haven’t declared in the class. Seems easy? It may be quite complicated in the beginning, but as you probably won’t need to use it in most of your projects, it might eventually be useful. Sometime. In the future. Maybe.

class OMG {
   public $defined;

   public function defined() {
      $this->defined = "Successfully changed.";
   }
}
$omg = new OMG;

$omg->defined();
echo $omg->defined."<br>";

$omg->undefined();
echo $omg->undefined;

The above code won’t work properly. Why? Calling undefined methods is not allowed in PHP; it will bring a Fatal error: Call to undefined method OMG::undefined() error. How to get around it? Yeah, here’s when overloading becomes useful.

class OMG {
   public $defined;

   public function defined() {
      $this->defined = "Successfully changed.";
   }

   public function __call($name, $arguments) {
      $this->undefined = "You'd better change your pathetic code, jerk,
         the method <b>$name</b> doesn't exist in this universe.";
   }
}
$omg = new OMG;

$omg->defined();
echo $omg->defined."<br>";

$omg->undefined();
echo $omg->undefined;
}

Yes, the above code will cause a custom error message to be displayed. As you’ve probably noticed, the magic function __call($name, $arguments) is called whenever you invocate an undeclared method. It takes exactly two arguments; the first being the name of the called method, and the second an array of arguments it was supplied with.

When referring to a property that hasn’t been defined, PHP won’t report any fatal errors, but when you’re writing an interface for future implementation, it’d be nice of you to handle all overloading. You can overload members in a similar way; PHP will call the functions __get($name) and __set($name, $value) when you - respectively - want to use a nonexistent property of the given object, or set a member that doesn’t exist with a specified value.

class ZOMG {
   public $defined;

   public function __get($name) {
      $this->gundefined[] = $name;
   }

   public function __set($name, $value) {
      $this->undefined[] = array($name, $value);
   }

   public function __destruct() {
      echo "You've set ".count($this->undefined)." property(-ies) that
         hasn't (haven't) been defined before, and called ".count($this->gundefined)."
         nonexistent member(s).<br>Here's the list of members you've set:<br>";
      foreach ($this->undefined as $u) {
         echo "<b>".$u[0]."</b> value: <i>".$u[1]."</i><br>";
      }
      echo "And here's the list of members you've used:<br>";
      foreach ($this->gundefined as $g) {
         echo "<b>".$g."</b><br>";
      }
      echo "Have a nice day.";
   }
}

Design patterns

Design patterns are basically things that have been done so many times in the past, that someone actually thought of giving everyone else the ability to simply implement his solution to the problem (in other - Wikipedia’s - words, a design pattern is a general repeatable solution to a commonly occurring problem in software design). It’s logically, technically, and logistically impossible to describe all design patterns in a single article, so I’ll focus on two of them.

In most cases, it’s significantly easier and faster to use a design pattern in your scripts. When you meet a problem that may seem common to you, there’s a 99% chance you can use a pattern for it.

I’ll present the two design patterns I’ve found most useful; however, it doesn’t mean that they will be as useful for you - I suggest googling “php design patterns” if you are interested in them.

The singleton pattern is used to limit the number of instances of a given class to exactly one. For example, why would you need two file or database handlers? Signleton can help you reduce the loading time of a script by performing the time-consuming operations only once for the whole application.

class Fagballs {

   private static $instance;

   private function __construct() {}

   public static function get_instance() {
     if (self::$instance === null) {
       self::$instance = new self;
     }
     return self::$instance;
   }

   public function do_the_rest_of_the_stuff() {
     // the class logic
   }
}

Fagballs::get_instance()->do_the_rest_of_the_stuff();

Notice the private constructor in the above example; it prevents the class from being called by anything else that itself (get_instance()). Thus, trying to use $fag = new Fagballs; somewhere in the code would cause a fatal error to be reported.

Ever heard of loose coupling? It’s basically writing an application so that the different parts of it don’t rely on each other too much; the opposite would be tight coupling, when changing one class definition would cause a Series of Unfortunate Events happen in the code.

To prevent your code from becoming a Lemony Snicket’s greatest dream, you can use the factory pattern.

class Data {
   public $handler, $data;

   public function __construct($data) {
      $this->data = $data;
   }

   public function connect() {
      $this->handler = fopen('example.txt', 'a+');
   }

   public function save() {
      fwrite($this->handler, $this->data)
   }

class DataHandler {

   public static function load($data) {
      return new Data($data);
   }
}

$data = DataHandler::load('Hello world.');
$data->connect();
$data->save();

Obviously, you’d have to write more complicated applications to make use of the factory pattern. In the example above, by creating data handlers with DataHanlder class, you can easily change the data output method to, let’s say, database connection, without interrupting the rest of the code, as it doesn’t use any file-specific or database-specific queries.

Time to say goodbye

As - hopefully - you can see, object-oriented programming provides you with many useful problem solutions that you wouldn’t be able to use in the standard procedural programming. Serialisation, overloading and design patterns are, however, just a step closer to mastering OOP and there’s still a lot more you may be interested in learning.
Have fun getting to know the secrets PHP!


posted on Sunday, 16th December, 2007 at 12:12 (UTC +1000)
filed under PHP, Tutorial, Web
comments feed

3 Responses to “OOP in PHP (part II)”

[…] true power of object-oriented programming, there are many more things to learn. Be sure to read the second part of the article, explaining more advanced methods of coding good object-oriented applications. Have a good time […]

very nice article helped me very much…

best online casino ranked…

disturber tools?phyla,apocryphal:…

Leave a Reply