TachyonVortex
TachyonVortex

Reputation: 8602

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?

Upvotes: 22

Views: 19123

Answers (5)

FrancescoMM
FrancescoMM

Reputation: 2960

I still have to use PHP 5.3 in one project (not something I can control) and have to cope with similar problems.

In your case you could define the constant externally (and it seems legit)

define('TWO_PI',PI*2);

class Circle
{
    const RADIUS_TO_CIRCUMFERENCE = TWO_PI;
...

This seems to work fine even in PHP 5.3. The cleanest approach is the method one but slightly slower.

Upvotes: 1

sh6210
sh6210

Reputation: 4540

In my case,

my installed php version is 7.1 but, in my IDE(PhpStorm) settings my php version 5.4 was selected. Once i've changed my php version the problem was gone.

PhpStorm settings->PHP and change the PHP language level and CLI interpreter php version.

Upvotes: 0

keisar
keisar

Reputation: 5326

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.

Upvotes: 17

Alexander Yancharuk
Alexander Yancharuk

Reputation: 14531

I would recommend this approach:

class Circle {

    ...

    private function RADIUS_TO_CIRCUMFERENCE() {
        static $RADIUS_TO_CIRCUMFERENCE;

        if ( null === $RADIUS_TO_CIRCUMFERENCE )
            $RADIUS_TO_CIRCUMFERENCE = M_PI * 2;

        return $RADIUS_TO_CIRCUMFERENCE;
    }

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

The goal is calculation only once for all class entities, like a real constant.

Upvotes: 2

Virus721
Virus721

Reputation: 8335

If you agree to use a standard variable instead of const keyword :

class Foo {

    public $CONST_A; // = calculation A // To let people quickly see the value.
    public $CONST_B; // = calculation B 

    public static function initClass() {
        self::$CONST_A = /* calculation A */;
        self::$CONST_B = /* calculation B */;
    }
}

Foo::initClass();

initClass() is done only once, when the class file is required.

Upvotes: -1

Related Questions