Recently I’ve decided to “upgrade” my PHP skills to version 5, and redesigned an old website of mine with PHP 5, MySQLi 5, MVC and other things I’ve picked up over the years in mind.
After finishing and attempting to deploy it on an external server I’ve came across a nasty surprise when it turned out that the static late binding feature in general and get_called_class() function in particular does not work on anything prior PHP 5.3. Something which I’ve somehow completely overlooked before… Unfortunately this turned out to be rather difficult to work around without changing much of my design.
During my research into this I’ve encountered a post somewhere in which the author claimed that any design that depends on get_called_class() is probably flawed anyway. Looking at the design goals that it made possible to achieve I do not really agree with that assessment. On the other hand the difficulties I had to go though to make it work on “older” PHP versions made me think about whether this is not indeed a sign that I was doing something wrong…
So below I’ll try to briefly describe what I’ve tried to achieve and I would be grateful if someone could point it out to me if there are any fundamental issues with my approach:
Basically I was trying to avoid repeating code as much as possible. As such I’ve created a Model base class which contained a lot of generic functionality for commonly used database operations. Classes representing the business objects belonging to the individual tables would extend the base class ideally only providing functionality that is unique to the given object.
For many basic operations such as loading, storing and searching the only unique information that is needed is the database table name and in case of the search methods the class name of the objects it needs to fabricate. Therefore the model base class provides a lot of such common functions as searchByFieldValue() etc. As these functions do not rely on instance data they are made static.
To illustrate, let’s take two classes called Article and Comment.
Both provide a “searchAll()” method (and many similar more specialized ones…) e.g. Article::searchAll() and Comment::searchAll()
Both of these will perform a SELECT * on the relevant table, and both will return an array of objects built from the query results. In case of the Article class an array of Article objects and in case of the Comment class an array of Comment objects. Since as mentioned above the only thing that changes is the table name and the class of the fabricated objects it should be reasonable if each of the two classes would need to only provide these two bits of information somehow and leave the rest of the (common) logic to the base class. Therefore searchAll() is provided as a static method in the Model class…
To define which table to query each extended class defines a class constant called DB_TABLE. (Though they could just as well provide a method, it would not change the nature of the problem…) The search etc. functions defined in the base class access this constant to learn the table name. This normally requires the use of get_called_class(). In addition get_called_class() is also used by the search functions to determine what objects to create from the database results…
Without the static late binding functionality the only alternatives are as far as I can see to either provide wrapper functions for each of the functions in the base class in each of the extended classes so that they can “pass down” this information, which seems pretty wasteful, or to only use instance methods and have the constructor pass down the information to the parent classes constructor. However as mentioned before, many of these functions do not require instance data and therefore I don’t see why they cannot be static…
Please let me know if I’m missing some obvious point somewhere, because so far I’m really amazed that it should be this difficult to have common (static) functions factored out into a base class while still retaining the ability to fully use them as if they belonged to the extended class…