PHP Function Overloading

This is an archived blog post from 2011

For those of you un-familiar with this concept don't worry as it's quite simple to understand and really opens up the possibilities of what your class and by extension your application is capable of handling. In most object oriented languages function overloading is defined as such:

Function overloading or method overloading is a feature found in various programming languages such as Ada, C#, VB.NET, C++, D and Java that allows the creation of several methods with the same name which differ from each other in terms of the type of the input and the type of the output of the function. It is simply defined as the ability of a single function to perform different tasks. -Wikipedia

Most notably, function overloading gives you the ability to have more than one version of a function so that it can behave differently according to the context in which and how it is called. This gives your functions a dynamic or polymorphic aspect. An example would look something like this in any object oriented language:

public function MyFunction(string AString, int AnInt){}
public function MyFunction(array AnArray){}

The way the language handles this is by looking at how you call the function. If you call the function and only supply an array as an argument, it will know you want to use the second version of the function and run accordingly. However we accomplish this differently in PHP. In most object oriented languages, the common way to handle arguments is to place them in the function declaration. This in effect writes them in stone and if the arguments ever deviate from what is written, an error occurs. Thus, function overloading was necessary to allow dynamic functions.

In PHP we have a couple of convenient functions provided for us which in their own right provide us with the most common benefits of function overloading.

function MyFunction()
{
    $Number = func_num_args(); // Returns number of arguments passed to function
    $ArrayOfArguments = func_get_args(); // Returns a numeric array of the arguments passed to function
    $SingleArgument = func_get_arg(0); // Returns specific arguments passed to function denoted by it's index; 
    // 0 returns the first argument
}

These functions enable you to have one function which can handle any number of arguments of any type. It can also handle unexpected and expected arguments in whatever way you like. The question that this brings is why PHP has function overloading if it already has these convenient functions? Well, as you'll soon find out, PHP handles function overloading in a very unique way which can be extremely helpful in specific situations.

In particular I found myself needing one function to be available statically and also within an object context. Normally we write our applications using an object-class model, which means all of our functions are available to us through an object. The only downside to this however is if we want to call a function without having an object of that class to do it through. This is where static functions come in to play. In my situation I was building a File class to handle all of my file system operations including parsing files, creating files and folders, and deleting files and folders. Usually I would need to first create an object of the File class which housed the file in question and then call the delete function that it provided. But what if I wanted to delete a file without going through an object? This makes complete sense in the context of a file explorer of sorts. If you have a list of files and you want to delete one, it makes no sense to first open it up in an object and then delete it. If you have the required information, it's directory path, then you should be able to save the code/time and just delete it. In PHP the differences between static and non-static functions look like this:

class File
{
    function DeleteFile() // Simplified file deletion function
    {
        unlink($this->filePath); // Deletes file at path stored in object variable filePath
    }
    static function DeleteFile($path) // Static function which requires argument
    {
        unlink($path); // Deletes file given as argument
    }
}
// How the functions are called.
$object = new File(); // New Object
$object->DeleteFile(); // Call delete function

File::DeleteFile(path to file); // Static function being called

As you can see the largest difference between the two functions is how they are called. The first non-static function is business as usual. The static function however is very different from normal. To call a static function you first indicate which class in which it resides. You then include the scope resolution operator (::) and then the name of the function and any arguments. As a side note on static functions, they can only access static variables within a class. Since you don't call them through an object, the $this variable is un-defined. It looks like this:

class File
{
    static $SpecialNumber = 3; // Static variables must be defined and expressions are not allowed. So while you may initialize a static property to an integer or array (for instance), you may not initialize it to another variable, to a function return value, or to an object.

    static function DoThing()
    {
    return self::$SpecialNumber;
    }
}

The problem that you'll encounter however is that in PHP, each function must have a unique name. So my example of

function DeleteFile(){}
static function DeleteFile(){}

is invalid and would return errors. So how do we get around this? Well, PHP has some very special functions which it calls Magic Methods. Magic Methods in PHP are denoted by the double underscore which prepends the function name. Magic Methods are also not directly callable but are called by the class when necessary. There will be a future post listing all of the Magic Methods and their benefits, but for now we only need to look at two in particular.

__call() and __callStatic() are two powerful functions which handle undefined functions. Whenever you call or reference a function of a class that is undefined, PHP will pass the call or reference along to either __call() or __callStatic() depending on whether your call or reference is static or not. Because there is both a static and non-static version we can solve our problem quite simply. Both __call() and __callStatic() receive two arguments, a string of the name of the function requested and an array containing any arguments passed to said function.

So our solution would look something like this:

class File
{
    __call($name, $arguments)
    {
        switch($name)
        {
          case 'DeleteFile':
          unlink($this->filePath);
          break;
        }
    }
    __callStatic($name, $arguments)
    {
        switch($name)
        {
            case 'DeleteFile':
            $path = $arguments[0];
            unlink($path);
            break;
        }
    }
}

As you can see, if we have a lot of functions which need this functionality, things can get a tad messy, but this is how PHP handles this particular situation. Luckily this type of functionality isn't something that's required for most functions, but for those that it is, it's a life saver. The example of a file deletion function is an obvious one, but this functionality can be useful for all sorts of situations.

comments powered by Disqus