My apologies for the overly verbose title of this blog entry. But it’s true. Embracing PHP 5 will make you a better programmer. At least, it has for me.
This minor epiphany revealed itself during yesterday’s Captcha Crisis over at IPS HeadQuarters. In a nut shell, some naughty spammer defeated our CAPTCHA and registered thousands of member accounts across hundreds of IP.Boards.
We had to react fast and release an update that not only fixes our CAPTCHA but also integrated reCAPTCHA which Brandon had already done for IP.Board 3.0.0.
Now, I’ve had my head stuck in IP.Board 3.0.0 since February. We made the decision to use PHP 5 so that we can take advantage of all the new “stuff” such as improved XML handling, public/private/protected methods and properties and magic methods to name a few. I started from scratch and wrote a brand new framework from the ground up using several PHP 5 tricks along the way. In short, it’s the classic MCV pattern using static utility classes and a singleton registery. I’ve blogged about it here although that’s a touch out-of-date but still fairly representative of the new framework.
So. With many customers asking us for a fix, the pressure was on. Josh, Brandon and I down-tooled and went to work on IP.Board 2.3.6. And what a minor shock that was! After nearly 8 months of using the new framework, it seemed backward to use $this->ipsclass for everything. Worse, I was limited to using PHP 4. Worse still was that all the captcha calls were de-centralized. Ouch.
Captcha code was copied through many files. This is a natural progression for stable code that gets developed over time. You start off with the captcha in one place. You then want to add it elsewhere, so you copy the code and paste it in place. Rinse and repeat. You do this because there is a real fear of changing too much code for minor releases. Even though you know it’s bad.
I decided to write a micro-framework to handle all captcha challenges; to centralize it into one public facing class which calls on little plug-in classes which provide the functionality. Performing this routine task is when it I discovered just how much better PHP 5 is.
Without the ability to set methods and properties to private, the code seemed harder to departmentalize. Without magic methods to offload method requests, I had to make do with pointless functions to do the same. Without the ability to chain functions together, I had to fudge something that does the same job. Any desire to write nice and tight code goes out of the window with PHP 4. It doesn’t try very hard to do things properly, so you don’t.
Once the mini-framework was complete, I set about updating IP.Board 3.0.0 to use the same methods. Thankfully, we had already decentralized CAPTCHA creation to a single class. The ability to drop in new plugins and have new CAPTCHA methods seemed useful, so I re-wrote the framework for PHP 5 and it was much more pleasurable experience!
To compare, here’s the mini-framework in PHP 4:
/** * Constructor bah to PHP 4 * * @param object IPSClass object * @param string Captcha plug-in to use */ function class_captcha( $ipsclass, $plugin ) { $this->ipsclass =& $ipsclass; $plugin = $this->ipsclass->txt_alphanumerical_clean( $plugin );if ( ! file_exists( KERNEL_PATH . 'class_captcha_plugin/' . $plugin . '.php' ) ) { $plugin = 'default'; }require_once( KERNEL_PATH . 'class_captcha_plugin/' . $plugin . '.php' ); $this->_plugInClass = new captchaPlugIn( $ipsclass ); }/** * Returns template bit * * @return string HTML Template bit */ function getTemplate() { return $this->_plugInClass->getTemplate(); }/** * Validate challenge * * @return boolean * @since 1.0 */ function validate() { return $this->_plugInClass->validate(); }/** * Fetch Plug In Class Handle * * If you need it... */ function fetchPlugInClassHandle() { return $this->_plugInClass; }
And here’s the same functionality, but in PHP 5:
/** * Constructor */ function __construct( ipsRegistry $registry ) { $this->registry = $registry; $this->DB = $this->registry->DB(); $this->settings = $this->registry->settings(); $this->request = $this->registry->request(); $this->lang = $this->registry->getClass('class_localization'); $this->member = $this->registry->member();$plugin = $this->settings['bot_antispam_type'];if ( ! file_exists( IPS_KERNEL_PATH . 'classCaptchaPlugin/' . $plugin . '.php' ) ) { $plugin = 'default'; }require_once( IPS_KERNEL_PATH . 'classCaptchaPlugin/' . $plugin . '.php' ); $this->_plugInClass = new captchaPlugIn( $registry ); }/** * Magic Call method * * @param string Method Name * @param mixed Method arguments * @return mixed */ public function __call( $method, $arguments ) { if ( method_exists( $this->_plugInClass, $method ) ) { return $this->_plugInClass->$method( $arguments ); } else { trigger_error( $method . " does not exist", E_USER_ERROR ); } }/** * Magic __get method * * @access public * @param mixed * @return mixed */ public function __get( $name ) { if ( property_exists( $this->_plugInClass, $name ) ) { return $this->_plugInClass->$name; } else { trigger_error( $name . " does not exist", E_USER_ERROR ); } }
Now, isn’t that much nicer!
Take advantage of all PHP 5 offers and your code will improve. Just looking at the difference between IP.Board 2.3′s and IP.Board 3.0.0 proves that PHP 5 has helped me become a much better programmer.

{ 4 comments… read them below or add one }
Glad to see you’re using the MVC model for 3, I’ve started using the model at the start of the summer and couldn’t love it more.
Although PHP5 does introduce lots of magic niceties, you’ve got to be careful not to over-rely on them. They can be slow, see: http://www.garfieldtech.com/blog/magic-benchmarks
Also, for the problem you faced there, you may find inheritance to be better suited. Basically your subclass would inherit from the parent class, and shared functions would be stored in the parent class, then you’d initiate the subclass through a static function stored in the parent class. This also allows you to set a standard set of functions in the parent class (see ‘Implements’) and makes it more obvious to developers what function calls are.
Using magic is handy, but it also hides a lot of things under its sleeves, and when you’re coding you want to make things as clear as possible.
It will be nice to get my hands on IPB 3 to start on IP.Tracker 2. I have a Java/C# background from before I started doing PHP programming. I have missed abstraction classes dearly as well the more OOP tools Java and C# have to offer (public, private, protected … things you have mentioned).
Not quite sure I understand the ‘Magic’ methods. I saw the examples on the PHP site, so I understand what they can do. May actually be one of my big hang-ups. I like giving my constructors the name of the class … not ‘__construct’. ‘__get’ and ‘__set’ seem pointless because I’d set the variables the class needs and provide get/set methods for those. A programmer could make a new object from a class and throw a bunch of random data into that class’s data array using the get/sets even if the methods of that class don’t do anything with that data (that’s the PHP example showed).
I’m going to have to read up on this more. I definitely like the looks of it and the potential, but I’m going to have some hang-ups with it just like I do with PHP4.
Althouh i am new in programing, i have background from C# before i started PHP too… i can say to you that yesterday when i read about the “magic” functions i was in a little shock… how can i put it?
when in C# i can do a :
public int var1
and themn i can “set” and “get” him, in PHP its like the opposite, becuse the magic function work only when the varible is undefined (that what i understand…) and again, the constuact was so anoying.. i used to give the constractor the name of the class so… i really dont get it why i need to use constract … but its like you said, PHP 5 is very good and i happy to use it.