Search code examples
phpclassconstants

Best workaround to create a PHP class constant from an expression?


I'd like to be able to do something like this:

class Circle {

    const RADIUS_TO_CIRCUMFERENCE = M_PI * 2;  // Not allowed

    private $radius;

    public function __construct( $radius ) {
        $this->radius = $radius;
    }

    ...

    public function getCircumference() {
        return $this->radius * self::RADIUS_TO_CIRCUMFERENCE;
    }

}

But I can't create a class constant from an expression like that:

The value must be a constant expression, not (for example) a variable, a property, a result of a mathematical operation, or a function call.


So my question is: What's the best workaround for this limitation of PHP? I'm aware of the following workarounds, but are there any others which are better?

1. Create a property

class Circle {

    private static $RADIUS_TO_CIRCUMFERENCE;

    private $radius;

    public function __construct( $radius ) {
        $this->radius = $radius;
        $this->RADIUS_TO_CIRCUMFERENCE = M_PI * 2;
    }

    ...

    public function getCircumference() {
        return $this->radius * $this->RADIUS_TO_CIRCUMFERENCE;
    }

}

I don't like this, because the value of $RADIUS_TO_CIRCUMFERENCE can be changed, so it's not really a "constant".

2. Use define()

define( 'RAD_TO_CIRCUM', M_PI * 2 );

class Circle {

    const RADIUS_TO_CIRCUMFERENCE = RAD_TO_CIRCUM;

    ...

    public function getCircumference() {
        return $this->radius * self::RADIUS_TO_CIRCUMFERENCE;
    }

}

This is better, since the value is truly constant, but the drawback is that RAD_TO_CIRCUM has been globally defined.

A digression

I don't understand how this can work. (Edit: I've tested it, and it does work.) According to the Handbook of PHP Syntax:

The const modifier creates a compile-time constant and so the compiler will replace all usage of the constant with its value. In contrast, define creates a run-time constant which is not set until run-time. This is the reason why define constants may be assigned with expressional values, whereas const requires constant values which are known at compile-time.

The manual confirms that "constants defined using the const keyword ... are defined at compile-time".

In this bug report from 3 years ago, a member of the PHP team wrote:

For the class constant we need a constant value at compile time and can't evaluate expressions. define() is a regular function, evaluated at run time and can therefore contain any value of any form.

But in my example above, the value of RAD_TO_CIRCUM is not known at compile-time. So what is the compiler putting for the value of RADIUS_TO_CIRCUMFERENCE?

I'm guessing that the compiler creates some kind of placeholder for the value of RADIUS_TO_CIRCUMFERENCE, and at run-time, that placeholder gets replaced with the value of RAD_TO_CIRCUM. Might this placeholder be a kind of resource? If so, maybe this technique should be avoided? The manual says: "It is possible to define constants as a resource, but it should be avoided, as it can cause unexpected results."

3. Create a method

class Circle {

    ...

    private static function RADIUS_TO_CIRCUMFERENCE() {
        return M_PI * 2;
    }

    public function getCircumference() {
        return $this->radius * $this->RADIUS_TO_CIRCUMFERENCE();
    }

}

This is my favourite workaround that I'm aware of. The value is constant, and it doesn't affect the global space.

Is there another workaround which is even better?


Solution

  • As of PHP 5.6, you can use math expressions in PHP constants, so this would work:

    const RADIUS_TO_CIRCUMFERENCE = M_PI * 2;
    

    I encountered this thread because my environment wasn't configured properly (was set to PHP 5.4 by accident), so don't forget to check your PHP version.