Class constants that work with inheritance

Dani 1 Tallied Votes 286 Views Share

For some reason, PHP class constants don't play nicely with inheritance.

For example, suppose you have the following code:

class Foo {
    const VAR = 'foo';

    public function __construct() {
        echo self::VAR;
    }
}

class Bar extends Foo {
    const VAR = 'bar';
}

// This will actually print out 'foo' even though we would expect it to print out 'bar'
$bar = new Bar();

You can see here that there is a class Foo that has a constant variable named VAR, and the constructor function for that class (that executes when a new object of that type is created), says to print out the value of that variable.

We then have a second class Bar that extends class Foo. With this type of inheritance, child class Bar is meant to inherit all properties and methods from its parent class Foo. The problem, unfortunately, is that this doesn't extend to constants. The solution to this problem is to use ReflectionObjects. The code below demonstrates the proper way to retrieve a class constant that obeys inheritance.

class Foo {
    const VAR = 'foo';
    
    public function __construct() {
        $object = new ReflectionObject($this);
        echo $object->getConstant('VAR');
    }
}

class Bar extends Foo {
    const VAR = 'bar';
}

// This will print out 'foo'
$foo = new Foo();

// This will print out 'bar'
$bar = new Bar();
pritaeas 2,194 ¯\_(ツ)_/¯ Moderator Featured Poster

Why not use static in your first example?

public function __construct() 
{
    echo static::VAR;
}

Based on this link, see the comment about late static binding.

commented: This works and is much more elegant than my ReflectiveObject solution +34
JamesCherrill 4,733 Most Valuable Poster Team Colleague Featured Poster

If PHP works like Java then all the initialisation of the parent is completed before the initialisations of the child, so the parent's constructor will be executed before the Childs initialisation, and so you will see "foo". If you print the variable after construction is complete then the Bar instance should show "bar".

In any case that looks like a shadowing situation, so Foo's VAR is hidden in Bar

Dani 4,084 The Queen of DaniWeb Administrator Featured Poster Premium Member

Why not use static in your first example?

Honestly, because I wasn't aware that it would produce something different than self::VAR!

If you print the variable after construction is complete then the Bar instance should show "bar".

Doing something like this, still results in foo being printed out twice:

class Foo {
    const VAR = 'foo';

    public function echo() {
        echo self::VAR;
    }
}

class Bar extends Foo {
    const VAR = 'bar';
}

// This will print out 'foo'
$foo = new Foo();
$foo->echo();

// This still prints out 'foo'
$bar = new Bar();
$bar->echo();
Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.