Confessions of a Converted PHP Developer: Animal Abuse
Sometimes the functionality of a library or set of classes that you’re working with is 99% perfect for the job, but the last 1% requires modification of some of the core assumptions made in the code. Altering code can cause maintenance frustrations down the track and extending code can cause maintenance frustration right away, but Ruby provides us with a flexible way to modify code with less damage.
In converting to Ruby I realized how strict PHP is in its class hierarchy. This surprised me as I’d always viewed PHP as a very loose language, mainly due to its loose typing and lack of formal code structure guidelines. The latter being inherently, in my opinion, due to the way that a lot of developers learn the language. I find most PHP developers learn starting by using inline PHP as a low learning curve entry point into dynamic web languages and then move on to fuller, more complex, applications.
With PHP, your class hierarchy is very much a linear progression, with the occasional injection from the side. What I mean by that is you will start with a class, possibly declared abstract, and work your way up to an end class with extension after extension, and along the way you might implement some interfaces (that’s the injection from the side bit). This is a very straight forward structure, without any way of dynamically altering this process.
Ruby provides the same extension inheritance structure, but it also allows you various not so rigid ways to alter classes — as we’ve seen in the past with the mixin structure. But Ruby goes even further and allows you to redeclare classes. If you did this in PHP, you’d get a rather strict telling off.
<?PHP
class foo
{
public function hello_world()
{
echo "Hi";
}
}
#somewhere else in my codebase
class foo
{
public function hello_world()
{
echo "Hello world!";
}
}To start with I’ve created a class called foo, with a hello_world method. But then elsewhere in my code, I want to change my foo class and its hello_world method to output a slightly different message. Unfortunately, the above code will output PHP Fatal error: Cannot redeclare class foo. Not good. To get this behaviour, we would need to extend our class and, in doing so, introduce a lot more complexity.
<?PHP
class bar extends foo
{
public function hello_world()
{
echo "Hello world!";
}
}Voila, we have now got a class called bar which has the correct hello_world method. This is perfect yea? No — it’s not.
The problem with this style of coding is that to acheive the outcome we want, we’ve had to create a completely new class to use. If we already have a codebase that is using the foo class, we’re up a creek without a paddle and would have to find / replace throughout the entire codebase. While this might be trivial for a small codebase, it could turn into quite an exercise for a larger codebase. Let’s have a look at a common scenario with a request / response situation — ignore the fact that this code calls functions which clearly don’t exist and wouldn’t work if they did!
<?PHP
class API_Request
{
public function get($resource)
{
$content = magically_get_resource_content($resource);
$status_code = magically_get_resource_status_code($resource);
return $this->_build_response($content, $status_code);
}
protected function _build_response($content, $code)
{
return new API_Response($content, $code);
}
}
class API_Response
{
protected $_response;
protected $_status_code;
public function __construct($content, $status_code)
{
$this->_response = json_decode($content);
$this->_status_code = $status_code;
}
public function is_successful()
{
return $this->_status_code == 200;
}
}Here we can call get on API_Request, with a URL resource string, and get back an instance of API_Response with an is_successful method. This method will tell us if the status code of the http request was 200 or not. Now, say we wanted to change the functionality of the API_Response class to match the status code definitions of HTTP/1.1, and tell us that any status code in the 200’s is a success. To do this we’d have to either edit the underlying API_Response class, or extend it, and extend the API_Request class to call the newly extended API_Response class. All of this is a hell of a lot of work for such a simple change, especially when there’s the possibility of having to update code that uses these classes throughout your project.
Here I’m going to do this, replacing API_Response with API_Better_Response, and API_Request with API_Better_Request.
<?PHP
class API_Better_Response
{
public function is_successful()
{
return $this->_status_code >= 200 && $this->_status_code <= 299;
}
}
class API_Better_Request
{
protected function _build_response($content, $code)
{
return new API_Better_Response($content, $code);
}
}Now we have overwritten the _build_response method to correctly return the API_Better_Response class, but we’d also have to go through our application code and find all instances of API_Request and replace it with API_Better_Request.
Ruby to the rescue: Monkey Patching
Ruby has the ability to reopen a class anywhere, and override or add methods. It’s an extremely easy process — you simply declare the class again. Let’s have a look at the equivalent Request and Response classes in Ruby — again ignoring the magical mystery code which doesn’t exist.
module API class Request def get(resource) content = magically_get_resource_content(resource) status_code = magically_get_resource_status_code(resource) API::Response.new(content, status_code) end end class Response def initialize(content, status_code) @response, @status_code = ActiveSupport::JSON.decode(content), status_code end def is_successful @status_code == 200 end end end
Here we’ve got the same code, with the same incorrect is_successful method. Instead of doing any annoying class extension and alteration like we had to in PHP, we can simply reopen the class and change the method to do what we really want it to do.
module API class Response def is_successful (200...300) === @status_code end end end
Now is_successful will return true if any 2xx http status code is returned. We’re asking if the range 200-300 (not including 300) loosely equals the status code of the request. Note that we haven’t touched any of the other methods and they will still be in their original form — we just overwrote the is_successful method.
This type of dynamic class alteration is often called Monkey Patching or, according to Wikipedia, Duck Punching. Apparently the naming is in the form of [animal] [past participle verb]. You can read up on the origins of the name at Wikipedia.
So we’ve seen that Ruby allows for easy and flexible dynamic alteration of classes at runtime, which is extremely useful. You can also make multiple monkey patches, but only calls to the code after it is patched will be affected.
I must note that Monkey Patching is often a quick fix solution that can create headaches for future developers (or for yourself, if your memory is like mine) for several reasons:
- Class logic that is expected to be in the original file, is no longer where it’s anticipated to be — and is difficult to track down.
- Code that you patch may no longer work if the underlying library or code is updated and the methods you patch are removed, or their behaviour changed.
I guess what I’m trying to say here is: Use at your own risk! You may find that with a little thought and refactoring you can achieve the same results in a cleaner way, and save future you, or future other developers from a few headaches along the way.
Do you use monkey patching in your code? Avoid it at all costs? Feel free to comment below with your reasons.
Like most of these sorts of programming tricks (which I'd put in the same class as "goto"), there are times when they're incredibly useful and powerful. However, it's rare that something like this is really the "cleanest" option. For example, if you're building a unit test framework, the ability to mess around with functions at runtime is actually a good thing done for the right reasons.
But, if you're just trying to special-case some functionality along certain runtime paths or impose a modification on some external library, I'd argue that it's frequently the wrong solution. It might "just work" in the immediate-term, and for the reasons you briefly pointed out it is incredibly risky going forward. In the API example you give, I think subclassing or simply modifying the underlying library would absolutely be more appropriate, despite the find/replace effort.
The biggest concern I have with it is that it violates "coding by contract." If I monkey patch a new method on to a class, how can any component that consumes that class know about it? Code ends up littered with calls like `if method_exists($someObject, 'shinyNewMethod')`.
I'd much rather see something like multiple inheritance, or runtime injection of Traits into a class that still works with method argument type-hinting and the instanceof operator. That way, I can at least know that an object meets some criteria without having to care how or where the implementation came from.
I'm sure monkey patching has its uses. However, it seems that the use case you're advocating here stems from inadequate planning. Granted, we all have jobs that might not allow us the time to go back and add dependency injection into an entire layer of the application, but in a perfect world, I would not recommend monkey patching. Seems like it would be hard to track down bugs and a general pain to deal with if it's not documented extremely well. There's a reason it's called monkey patching and not brain surgery patching.
As with all my examples, they're purely examples. It's hard to create an example with enough complexity to actually warrant the code we're looking at without making it unnecessarily verbose. I'm definitely not advocating using monkey patching for such a simple exercise as this.
The main point was that sometimes we just don't have control of the code libraries we're using — and it might not be just as simple as 'change libraries' — so saying that the developer should have implemented it better only gets us so far. Down in the trenches where you've got to get your code deployed by deadline it doesn't help.
I'm sure this trick can be very useful when you've got a deadline looming and a stack of things left to implement, but that's also it's main flaw. It strikes me as something that'll be joining register_globals and inline code as one those handy timesavers we'll be getting shouted at by webdev bloggers for using in a couple of years.
Overriding methods also can be very confusing for other people looking at your code, we usually expect methods to always work in a certain way, so it can be a nightmare to debug (even for you in 2-3 years).
I'm not saying it's a bad thing, actually i love it in ruby, just that it should be used carefully (and well commented)
This is yet another reason to avoid Ruby!
The protection in PHP (and most other oo languages) which you are saying is bad is there for a very good reason! So why are you hailing a serious bug in Ruby as an advantage!
I think this may have it's uses but I would be steering well clear. It seems like a way to make debugging harder and I would rather the headache upfront.
This is exactly what extension is for. It would be better to extend and then refactor existing code rather than add a level of complexity that will cause headaches later and could result in duplicate code.