Logo

Maarten Balliauw {blog}

ASP.NET, ASP.NET MVC, Windows Azure, PHP, ...

About the author

Maarten Balliauw is currently employed as a Technical Evangelist at JetBrains. His interests are mainly web applications developed in ASP.NET (C#) or PHP and the Windows Azure cloud platform.
More about me More about me
Send mail E-mail me


ASP.NET MVC Quickly Pro NuGet Subscribe to my RSS feed Follow me on Twitter! View Maarten Balliauw's profile on LinkedIn
Maarten Balliauw - MVP - Most Valuable Professional
Maarten Balliauw - ASPInsider

Search

Archive

Disclaimer

The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.

© Copyright Maarten Balliauw 2013


Extension methods for PHP

PHP Extension Methods The concept of “extension” methods will be nothing new to this blog’s .NET-related audience. For the PHP-related audience, this is probably something new. Let’s start with the official definition for extension methods: Extension methods enable you to "add" methods to existing types without creating a new derived type, recompiling, or otherwise modifying the original type. Extension methods are a special kind of static method, but they are called as if they were instance methods on the extended type.

Let’s visualize this. Imagine having the following class:

class HelloWorld
{
    public function sayHello($to = 'World', $from = null)
    {
        $helloString = "Hello, " . $to . "!";
        if (!is_null($from)) {
            $helloString .= " From " . $from;
        }

        echo $helloString . "\r\n";
    }
}

Not too difficult. There’s a sayHello() method that allows you to optionally pass who should be greeted and who the greeting is from. Imagine you would like to have a sayHelloTo() method as well, a shortcut to sayHello($to). And some other helper methods that use sayHello() under the hood. Wouldn’t it be nice to have the HelloWorld class concentrating on the real logic and defining the helper/utility functions somewhere else? How about a class named HelloExtensions:

class HelloWorldExtensions
{
    public static function sayHelloTo(HelloWorld $subject, $name = '')
    {
        $subject->sayHello($name);
    }

    public static function sayHelloFrom(HelloWorld $subject, $name = '')
    {
        $subject->sayHello('World', $name);
    }
}

Ok, nice. But this means I should call HelloWorldExtensions::sayHelloTo(‘Maarten’); to use the utility functions, right? Well: no! Not if we implement the concept of extension methods. Let’s see what we can do to HelloWorld in order to make our life easier.

class HelloWorld
{
    public function sayHello($to = 'World', $from = null)
    {
        $helloString = "Hello, " . $to . "!";
        if (!is_null($from)) {
            $helloString .= " From " . $from;
        }

        echo $helloString . "\r\n";
    }

    public function __call($functionName, $arguments = array())
    {
        // Add an extra parameter
        array_unshift($arguments, $this);

        // Current reflected class
        $reflectedClass = new reflectionObject($this);

        // Find suitable class and function
        $availableClasses = get_declared_classes();
        foreach ($availableClasses as $class) {
            $classDefinition = new ReflectionClass($class);
            $availableMethods = $classDefinition->getMethods();
            foreach ($availableMethods as $method) {
                if ($method->isStatic() && $method->getName() == $functionName) {
                    $availableParameters = $method->getParameters();
                    if ($availableParameters[0]->getClass()->getName() == $reflectedClass->getName()) {
                        $method->invokeArgs(null, $arguments);
                    }
                }
            }
        }
    }
}

The magic method __call() is used to look for a class that defines a static method with the first parameter of our type, HelloWorld. This now allows us to write very clean calls to code, as well as simplify our HelloWorld class:

$helloWorld = new HelloWorld();
$helloWorld->sayHello();
$helloWorld->sayHelloTo('Maarten');
$helloWorld->sayHelloFrom('Maarten');

Sweet, no? Can someone get this into the PHP core? I would greatly appreciate it: my classes can focus on functionality and all utility functions that just pass around variables can be defined in a separate class.


Comments (14) -

Eric United States |

Tuesday, May 18, 2010 5:22 PM

Eric

Thank you for posting this sample - it seems conceptually similar to mixins, in that is is a good way to implement utility methods.  The main difference I can see is that methods must a) be declared statically, and b) type hint the caller as the first parameter.  This seems like a solution with a smaller code footprint than mixins.  

I am curious though about performance.  Both PHP magic methods and the Reflection API have a reputation for not being the best methods for a PHP production environment.  Have you tested calling these as extension methods vs. a typical inheritance structure?  Is the difference negligible?

thanks again,
- Eric

maartenba Belgium |

Tuesday, May 18, 2010 5:54 PM

maartenba

Check the next comment Smile

Ralph Schindler United States |

Tuesday, May 18, 2010 5:40 PM

Ralph Schindler

Just a few words of caution about the above post.

From a technical standpoint, this is not very performant for a number of reasons. First, __call() itself is not very fast- none of the magic methods are. It is always faster to call a method who's signature is defined by a class already. Second, Reflection is even less performant.  Reflection should be used in development time code and if it must, in very low trafficed production code.  Even then, if you are using Reflection, the conclusions of the reflection should be cached somewhere as to not incure the cost of reflection more than a single request in a production environment.  Third, after the __call slowdown, and the reflection slowdown, this code is iterating every known class for all its known methods.  In some cases, this loop might iterate over 1000's of items.. and if this technique is used heavily in an application, this means that any dynamic calls (stuff proxied through __call()) would incure a loop that iterates at least a couple of 1000 times.

From a best practices standpoint- this attempts to bypass some of the fundamental coding best practices that C#, Java and PHP strive for in their implementation.  By this I mean polymorphic code via class based inheritance and interfaces. By leveraging "extends" and "implements" you are adhereing to a "Design by contact" methodology, which is generally accepted as a "good thing".  Languages such as python and ruby don't follow this implementation in their object model and opt for things like "monkey patching" and "duck typing" which, why interesting techniques, generally mean your API's are documentation driven, as opposed to code driven.  For example, these extension methods would never pop up in code completion in an IDE.

I guess I could make a few more comments, but I think that hits all the high notes. Smile  In any case, it is an interesting bit of code!

Cheers,
Ralph


maartenba |

Tuesday, May 18, 2010 5:56 PM

maartenba

Hello Ralph,

This is indeed bad code, performance hits: a lot!

What I wanted to do is intentionally be vague on some of the "why" and "how". basically, in .NET, this technique is uded to extend classes that are final and can not be inherited from. It's also a widely used solution to making clean code wth 1 full blown method and then adding extension methods for those who need it.

I hope this post will give some more discussion Smile

Maarten

Artem United States |

Wednesday, May 19, 2010 4:58 PM

Artem

Why would you extend a class that's final? You're not supposed to.

maartenba Belgium |

Wednesday, May 19, 2010 5:59 PM

maartenba

Ever had the situation where you hard to do this:

You get: final class Foo { }
You need a helper method, likë: FooHelper::bar( $myInstanceOfFoo )

Using extension methods, you can just wire up this helper method at runtime.

Artem United States |

Wednesday, May 19, 2010 6:10 PM

Artem

No, I've never had such a situation. If a class is a final, it should stay final. That's the point of making it final in the first place.

maartenba Belgium |

Wednesday, May 19, 2010 6:19 PM

maartenba

While it is called extension method, this technique does not give access to the class internals. Which makes it essentially a shorthand for the helper classes I described above.

Steve Hollis United Kingdom |

Tuesday, May 18, 2010 8:00 PM

Steve Hollis

Interesting post. I blogged on a similar topic recently, albeit with a slightly different approach.

www.stevehollis.com/.../

It still uses reflection and the __call magic method, but employs a broker to add specific functionality to objects.

Ralph's points on performance are all clearly valid, but I wonder how significant they might be in a real life app compared to say, too many or poorly optimised DB queries, remote calls, excessive file system access, lack of caching etc. etc. Presumably we're only talking about milliseconds even for hundreds of magic method and reflection calls.

Regarding best practices, what about DRY? The approach that both Maarten and I have blogged about address an un-addressed problem in PHP... a means of so-called "horizontal" re-use. There are other approaches such as code generation that are "code completion friendly", but surely they offer a means of making code duplication easier rather than eliminating.

maartenba Belgium |

Tuesday, May 18, 2010 8:04 PM

maartenba

It is indeed essentially the same problem. Again, this post was not to give a best solution to the problem, just a starting point of something that would be a great addition to PHP as a language.

Hodicska Gergely |

Wednesday, May 19, 2010 4:07 AM

Hodicska Gergely

Hello Ralph,

I would argue a little as we are using reflection API in a very high trafficed environment (ustream.tv was already among alexa top 200), and it makes the controllers and the templates much cleaner. When I introduced this feature I thought that I would cache the result of the reflection API into APC (as it was always stated the reflection API is very slow), but I was surprised as getting the needed information from the API and from APC was the same.

Of course using the reflection API was noticeable on the performance side but even with it our framework outperforms all the popular (and of course general) PHP frameworks (over 1000 req/sec from a hello world page). Actually for us this cost worth the benefits we get on development side. And if we want we can avoid the slowdown by generating a static cache as part of the build/code deployment process.

Regards,
Felhő

Pádraic Brady Ireland |

Wednesday, May 19, 2010 4:40 PM

Pádraic Brady

The sample code coincides with something I do myself (as a sort of hackish way of getting some of Ruby's module benefits in certain scenarios - not many of those since I restrict it to times when I just have to be able to throw methods at a class without actually editing it too much). There are performance hits, but it really depends on where they're used, how often, and whether you can optimise a bit. One optimisation, that's obvious, is from relying on get_declared_classes(). Is it possible to be more specific about classes to reflect? For example, use a specific namespace or a class prefix - then filter the results just for those types of classes. That would cut the potential 1000s of class lookups down to a minimal count.

maartenba Belgium |

Wednesday, May 19, 2010 6:00 PM

maartenba

Probably possible, yes.

Chris Henry United States |

Wednesday, June 16, 2010 7:05 AM

Chris Henry

Interesting concept.  Why would someone choose to do this over simply extending the class and adding the methods that way?  It seems as though it would be difficult to leverage and extensions you write and lead to a mess of randomly extended objects without any helpful organization.

Pingbacks and trackbacks (8)+

Comments are closed