0

I am trying to partially mimick Enum behaviour in PHP, and I'd like to take advantage of PHP's mixed type aswell.

Consider this example of my code (Using PHP PDO underlying):

class DBH {
    /** The current database handler **/
    private $dbh;

    const SQL_NOW = 1;

    public function insert($table, $keyvaluepairs) {
        $sql = "INSERT INTO `{$table}` (";
        $values_sql = ") VALUES(";
        $values = array();
        foreach ($keyvaluepairs as $key => $value) {
            if ($value == SELF::SQL_NOW) {
                $sql .= "NOW(), ";
            }
            else {
                $sql .= "`${key}`, ";
                $values_sql .= "?, ";
                $values[] = $value;
            }
        }
        $query = substr($sql, 0, -2).substr($values_sql, 0, -2).")";
        return $this->query($query, $values);
    }
}

All $value values are possible, as long as they are allowed by SQL natively, but PDO does not restrict itself to SQL, it can communicate with any RMDMS.

Currently my SQL_NOW clashes with the (int)1 value as far as I know.

How can I ensure that no value-clashing occurs, if possible, can I have an const/enum without value?

1 Answer 1

2

You cannot have a constant without a value, so any solution necessarily involves values that will not or can not be used "by mistake".

A practical solution would be to use GUIDs for the values of these constants, e.g.

const SQL_NOW = '{CD90A4D4-47EB-47A0-AFAD-B84944A06507}';

You also need to check against this value with the identical operator:

if ($value === SELF::SQL_NOW) { // three equals signs!

Since GUIDs that incorporate information from your network adapter's unique MAC address cannot be duplicated there is practically chance of accidental conflict.

That said, theoretically someone could attempt to insert this exact GUID in the database. The only way to guard against this is to use a placeholder value that cannot be used by your clients accidentally at all. This can be done with object instances, i.e.

$placeholder = new stdClass;
if ($value === $placeholder) {
    // This test will *never* be true unless someone does $value = $placeholder
}

Unfortunately object instances cannot be used as class constants, so this is not possible:

const SQL_NOW = new stdClass;

Static properties also cannot be initialized with the expression new stdClass, so this is not possible either:

static $SQL_NOW = new stdClass;

So this solution can only be implemented using static methods:

private static $SQL_NOW;

public static function SQL_NOW()
{
    return self::$SQL_NOW ?: self::$SQL_NOW = new stdClass;
}

You would then use the result of the function SQL_NOW() instead of the constant SQL_NOW in your code.

Sign up to request clarification or add additional context in comments.

2 Comments

I agree that the chance is very low that such a value would be used, but in this case {CD90A4D4-47EB-47A0-AFAD-B84944A06507} can easily be a valid value for a VARCHAR field.
@skiwi: Not really because GUIDs are Globally Unique IDentifiers -- they are usually derived from globally unique MAC addresses, and even if derived randomly the chance of a collision is astronomically low. I did expand the answer with a bulletproof alternative, but it's not as nice to use.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.