11

I'm a relative newcomer to PHP, and I'm figuring out the best way to implement some database access code. I'm trying to create some simple database access objects -- each table gets its own class, each instance of the class represents a row in the table, you know the drill.

I have code that seems to be working, but some of what I've seen online makes me worry that my approach may be wrong-headed somehow. And since "Can I do this?" and "SHOULD I do this?" are two different questions, I was hoping some PHP vets could chime in.

My current strategy is to create a base-level abstract Table class that contains all the common code and then have each class representing an individual table extend it.

My main concern is this code:


abstract class Table {
    protected abstract static function get_fields();
    protected abstract static function get_primary_key();
    protected abstract static function get_table_name();

The idea is that each implementing class will define the field names, the primary key(s), the table name, etc., and then the Table class will use those functions to fill in specific blanks, like so:


    static function insert($values) {
        $field_list = array();
        $value_list = array();
        foreach (static::get_fields() as $field_name) {
            if (isset($values[$field_name])) {
                $field_list[] = $field_name;
                $value_list[] = self::escape($values[$field_name]);
            }
        }
        $field_string = join(", ", $field_list);
        $value_string = "'" . join("', '", $value_list) . "'";

        $sql = "insert into " . static::get_table_name() . " ($field_string) values ($value_string)";

As you can see, the key is that I'm accessing those static abstract functions by prefacing them with static::. And as far as I can tell, it's working!

However, the accepted answer to this question indicates that abstract static functions still aren't allowed in 5.3.

So, I'm trying to figure out what to make of this. Is the answer wrong -- are abstract static functions now considered perfectly legal PHP code? Am I doing something inadvisable in my code? Is there another approach I ought to consider?

Community
  • 1
  • 1
BlairHippo
  • 9,502
  • 10
  • 54
  • 78
  • 1
    If you say "you know the deal", I know one thing for sure: The deal is to not use static functions for that. If you think it it's, it must be the null-checkers deal, because static functions do not work well with inheritance which you like to make use of. Additionally I suggest you to read the PoEEA, I mean, you know the deal kind of book, right? – hakre May 24 '12 at 16:39
  • 4
    @hakre: Where is this hostility and condescension coming from? "You know the drill" was a reference to a fairly standard approach to modeling database contents that I didn't think was worth belaboring. – BlairHippo May 24 '12 at 18:56
  • Please give reference that what you describe is a fairly standard approach. – hakre May 24 '12 at 19:33
  • @hakre: It's an approach I've encountered in production code many times -- EJBs, simple Java objects, Perl code, PHP code. It's a heck of a lot easier than rolling new SQL queries every time I want something out of the database. Why are you so bent out of shape about this? – BlairHippo May 24 '12 at 19:43
  • If it's that common, how is it commonly named? Please share the name. If you don't have a name for it, and when you refer to the common programming literature, which names come close to what you have experienced? Just asking, so it's more clear what you ask about. – hakre May 24 '12 at 19:46
  • @hakre: Are you trying to get me to admit I don't know the formal name for this approach? Fine. I don't. I'm not familiar with it from programming literature; I'm familiar through experience. Your point? – BlairHippo May 24 '12 at 19:59
  • So after not being able to name this, my next question is: What is the role of the static function here. Please say from your experience. Maybe that makes it more clear what you ask about. – hakre May 24 '12 at 20:06
  • @hakre: If you have a point to make, please make it. – BlairHippo May 24 '12 at 20:16
  • Okay, even by experience you can not describe what you're looking for. Formal names are only one thing, totally right, but if you even from practical experience can not formulate what exactly you're looking for, I'd say your question is problematic. – hakre May 24 '12 at 20:20
  • @hakre: I have described what I'm looking for. And I've found the answer I needed -- bringing an interface into the mix appears to give me exactly the behavior I'm looking for without any logical paradoxes cluttering the code. But thank you for your concern. – BlairHippo May 24 '12 at 20:27
  • From further reading I'd say that an interface is not what you're looking for (albeit helpful), but you want to create some template hence the template pattern came to mind. If you then understand that you don't need any static in the game things become even more easier to play. – hakre May 24 '12 at 20:53
  • 1
    There are a number of ORM frameworks available for PHP, including (but not limited to) [Doctrine](http://www.doctrine-project.org/projects/orm.html), [Zend Framework](http://framework.zend.com) and [Propel](http://www.propelorm.org). This is an extraordinarily complicated wheel; are you sure you want to re-invent it? –  May 24 '12 at 21:32
  • 5
    @Phoenix: Honestly? Lord, no. But my options are unpleasant; we don't have a proper sysadmin for the production server, and the PHP install that's there ... I'll be nice and call it "feature-light." Installing anything interesting is going to be way too white-knuckle for my liking. I'd rather make my own wheel, imperfect though it may be, than keep dragging around a sled. – BlairHippo May 24 '12 at 21:41

2 Answers2

17

Here's your example

abstract class Table implements iTable {

    public static function insert() {
        $class = get_called_class();
        $sql = 'INSERT INTO '.$class::get_class_name();
        echo $sql;
    }    

}

interface iTable {
    static function get_class_name();
}    


class ConcreteTable extends Table
{
    public function ConcreteTable() {}

    static function get_class_name() {
        return 'ConcreteTable';
    }

}

$t = new ConcreteTable();
$t::insert();

This example respects object paradigm, and you're sure it'll work even if PHP stop support late static bindings (which is a PHP specificity, I think)

Edit: What both answers show is that it's unknown that an abstract class introduces an interface as well for classes extending from it. Following the template pattern, this is possible in PHP even with static functions (albeit for a good reason that gives you strict standard warnings). The proof of concept:

abstract class Table
{
    abstract static function get_class_name();
    public static function insert() {
        printf('INSERT INTO %s', static::get_class_name());
    }    

}

class ConcreteTable extends Table
{
    public static function get_class_name() {
        return 'ConcreteTable';
    }
}

ConcreteTable::insert();

If you remove the static keywords here, you actually will get useful (and a standard way of doing things) code:

abstract class Table
{
    protected $table = NULL;
    public function insert() {
        printf('INSERT INTO %s', $this->table);
    }    

}

class ConcreteTable extends Table
{
    protected $table = 'ConcreteTable';
}

$table = new ConcreteTable();
...
$table->insert();
kennzors
  • 15
  • 8
zessx
  • 68,042
  • 28
  • 135
  • 158
  • Hmm, why is there the obviously superfluous `new` in there - all you need is a string, e.g. `$t = 'ConcretTable';` Why don't you just make this dependent on some function name read with `__FUNCTION__` constant? Or even more common: Add the table name as a protected property (or the SQL query)? – hakre May 24 '12 at 19:53
  • Inviting an interface to the party appears to give me the behavior I'm looking for without any logical paradoxes cluttering my code. You have my thanks. :-) – BlairHippo May 24 '12 at 20:28
  • @hakre : the `new` is just a Java habit – zessx May 24 '12 at 20:59
  • get_called_class() can replace the need to name the table that the class represents. So then you can have a getTableName() that returns get_called_class(), which can be overridden by individual classes to customize table name – Jon L. Jan 08 '13 at 22:57
6

An abstract function will never be static, in any kind of language.

A static function provide a functionality, even if there is no instance.

An abstract function doesn't provide any functionnality.

Logically, you can't use an abstract static function, which would --ALWAYS AND NEVER-- provide a functionnality.


B extends A

When you call a static function in B context, it'll be runned in A context (cause of static state).

But, in A context, this same function is abstract, an you're not allowed to call it.

zessx
  • 68,042
  • 28
  • 135
  • 158
  • So, that would be a "yes" to the question of whether I'm doing something inadvisable. Do you have any insight into why PHP seems to be letting me get away with it anyway, or suggestions for an alternate approach that's less paradoxical? – BlairHippo May 24 '12 at 15:05
  • You're using the object paradigm wrong. I agree with the abstract keyword, but those 3 functions will always work on instances, they shouldn't be static. – zessx May 24 '12 at 15:10
  • Why shouldn't they be static? The list of fields for the User object will remain consistent across all User objects; it's not dependent on an instance. Similarly, I'd think I should be be able to call "insert" or "select" on the class rather than an instantiated object. – BlairHippo May 24 '12 at 15:19
  • Your static functions, like insert, should only be runned in B classes. Why put a static insert function in A context, if you'll never call it (and never can) ? – zessx May 24 '12 at 15:30
  • Because the code for that insert function is fundamentally the same across all the B classes -- all that changes are the name of the table and the names of the fields. An inheritance model makes perfect sense to me here. Is there a more logically sound approach you feel I'm overlooking? – BlairHippo May 24 '12 at 15:37
  • Then, your insert function shouldn't be set as abstract. An abstract class can get non-abstract functions. If there're only abstract functions, it's called an interface, which is not what you're looking for. – zessx May 24 '12 at 15:42
  • The insert function isn't abstract. It's just that certain details (like the name of the table or the names of the fields) are defined by the subclass, not this top-level Table class. What I'm trying to do is create a fill-in-the-blank that subclasses are obligated to fill in. If abstract static functions are so inherently illogical that they're the wrong approach, fine; but what's the RIGHT approach? – BlairHippo May 24 '12 at 15:52
  • Ok, I see what you're looking for. A better practice would be use an interface in your abstract Table class. Then you'll be sure that get_class_name isn't null, and you'll be able to use get_called_class() instead of static:: Have a look in the next answer for an example. – zessx May 24 '12 at 17:07
  • What about using a static abstract function in a trait ? – Tofandel Jul 25 '18 at 22:39