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

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?

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.

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.

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.

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.

Related

Use constant as Function attributes

I've a question about function declaration. I would like have something like:
password_hash('er', PASSWORD_ARGON2I);
https://www.php.net/manual/en/function.password-hash.php
When I create a function, I would like declare some possibilities like:
PASSWORD_DEFAULT = 1
PASSWORD_BCRYPT = 2
PASSWORD_ARGON2I = 3
PASSWORD_ARGON2ID = 4
Then, when the user use the function he just set the value of the constant or the constant.
Actually I do:
$instanceOfMyClass->myFunction('er', MyClass::A_CONSTANT);
It done the job, but I'm forced to write the class name before use the constant name. Then if I do thaht I've access to all the constant of the class.
I think, I talk about something like constants at the function level.
Thanks a lot :-)
PS: Is there a way to do a DeclarationType like int|object|string for a function attribute ? Actually I don't type hint in this case, and for object, I need to specify a class or an Interface, sometimes, I accept many object to call the __toString() magic class, then I accept all objects.
I don't really understand your issue
constants like PASSWORD_DEFAULT, but just it (no function name with double :).
but my guess is, you are looking for define().
I suggest you to utilize OOP as much as possible and so like others said, use class constants. But if you want global constants for whatever reason, it goes like this:
define('DIRECTION_FORWARD', 0);
define('DIRECTION_LEFT', 1);
define('DIRECTION_RIGHT', 2);
function goInDirection($direction = DIRECTION_FORWARD)
{
// ....
}
Instead of just sequential numbers as values, you can use bitmasks, which work with bitwise operators and the power of 2.
https://www.php.net/manual/en/language.operators.bitwise.php
There are many ways of doing it.
the defined constants can not be modified, but they can be analyzed and verified if a condition is met; although this one is doing all that; since it would be double work for something immutable / immutable
<?php
class Main{
const CONSTANT = 'value constant';
function ouputConts() {
echo var_dump(self::CONSTANT);
}
}
in your case; that I can keep in a constant:
<?php
class Main{
const CONSTANT1 = 1; //integer
const CONSTANT2 = 0.1345; //float
const CONSTANT3 = 'text'; //string
const CONSTANT4 = array(1,2,3,4,5,10,20); //array
const CONSTANT5 = true; //bollean
function ouputConts() {
echo var_dump(self::CONSTANT1);
echo var_dump(self::CONSTANT2);
echo var_dump(self::CONSTANT3);
echo var_dump(self::CONSTANT4);
echo var_dump(self::CONSTANT5);
}
}
because in php an object or any type of object is considered mutable; you can not declare constants of type objects.
Documentation:
https://php.net/manual/en/language.constants.syntax.php
https://www.php.net/manual/en/language.oop5.constants.php
So your other question is whether you can pass a constant as a property of a function:
Yes, but you should have some considerations:
If you are in the same class it is not necessary you can use the value in any function of the same class, without needing to pass it is Global for the class / Scope:
class Main{
const CONSTANT = 'value constant';
function function1() {
$this->function2();
}
function function2() {
$this->function3();
}
function function3() {
$this->function4();
}
function function4() {
echo var_dump(self::CONSTANT);
}
}
If the function belongs to another class I recommend you first store it in a variable ...:
class Main1{
const CONSTANT = 'value constant';
function function1() {
$foo = CONSTANT;
Main2::FunctionTest($foo);
}
}
class Main2{
function FunctionTest($foo = '') {
echo var_dump($foo);
}
}
// I can not verify it.
I hope I have helped you in your question; if you need more help or deepen your query leave me a comment.
Update; i see this commnet:
#ChristophBurschka By doing that, when you use the function you don't
have help for retrieve specific constant (all class consant are not
available for this function). When you use native PHP function, you
just have possibilities to use available constant and the IDE have the
capatibilities to find them (and it list it to you automatically when
you start to fill the corresponding argument)? Thanks for your answer
about Type hint :-)
if the problem is around the scope of your const:
You can declare global constants in your applications (out of all classes and functions):
<?php
if (!defined('DB_PASS')) {
define('DB_PASS', 'yourpassword');
}
It will be available in any class and function of the whole application. and its use is not to be feared, since they are IMMUTABLE CONSTANTS.
Update recovery and implementation
With the last way I explain, you can then make a comparison and define which data you want to use; if the data of the constant or the data sent by the user; but all this must be tied to an analysis of the user's data in a specific function that you define.
Last Update
With the new define you can this:
<?php
//declaration:
if (!defined('PASS_SIS')) {
define('PASS_SIS', array(
'PASSWORD_DEFAULT' => 1,
'PASSWORD_BCRYPT' => 2,
'PASSWORD_ARGON2I' => 3,
'PASSWORD_ARGON2ID' => 4,
));
}
//call Function
$instanceOfMyClass->myFunction('er');
//function
function myFunction('er'){
echo var_dump(PASS_SIS);
//Access to data:
echo PASS_SIS['PASSWORD_DEFAULT'];
}

prevent PHP object from auto-casting

class Foo
{
public $var ;
function __construct($value)
{
$this->var = $value ;
}
}
$myFoo = new Foo('hello');
echo $myFoo->var . '<br>' ; // output : hello
// Question : how can I prevent another programer from accidentaly doing the following
$myFoo = 4 ;
echo $myFoo ; // output : 4
my question is in the comment // Question :...
I would like my coworkers being able to assign values to $myFoo use only $myFoo->var (or whatever public mutators are available in the class Foo)
thank you
EDIT :
with all respect to the users who claim it is not possible, SPL_Types PECL extention was able to achieve that (to a certain degree) see e.g http://php.net/manual/en/class.splint.php or http://blog.felixdv.com/2008/01/09/spl_types-in-php-and-strong-typing/
You cannot do this in any weakly typed language. If you have functions that take this variable as an argument, you can use type hinting in PHP, but otherwise you cannot prevent people from re-assigning their variables.
This is true to an extent even for strongly typed languages. If a programmer creates two instances of a class, there is no mechanism to prevent them from assigning a different instance to a variable of the same type.
The only way this can happen is if the programmer explicitly uses constants instead of variables (such as using things like final in Java, or val in Scala, etc), but, either way, you have no control over it in any language.
You cannot prevent changing the type within the class, but if you make it protected or private and then add a setVariable() method (where Variable is your variable name) you can control the input. Something like:
class myClass {
protected $integer = 0;
public function setInteger($new_value)
{
if (!is_int($new_value)) {
throw new RuntimeException('Cannot assign non-integer value to myClass::$integer');
}
$this->integer = $new_value;
}
public function getInteger()
{
return $this->integer;
}
}
// Now, outside of the class, the $integer property can only be changed using setInteger()
$class = new myClass;
$class->setInteger('dog'); // Uncaught Runtime exception ...
$class->setInteger(5);
echo $class->getInteger(); // 5
An alternative version of that function would accept string numbers and convert them to integers:
public function setInteger($new_value)
{
if (!is_numeric($new_value)) {
throw new RuntimeException('Cannot assign non-integer value to myClass::$integer');
}
$this->integer = (int) $new_value;
}
$class->setInteger('5'); // 5

Class works without declaring variables?

I'm learned php as functional and procedure language. Right now try to start learn objective-oriented and got an important question.
I have code:
class car {
function set_car($model) {
$this->model = $model;
}
function check_model()
{
if($this->model == "Mercedes") echo "Good car";
}
}
$mycar = new car;
$mycar->set_car("Mercedes");
echo $mycar->check_model();
Why it does work without declaration of $model?
var $model; in the begin?
Because in php works "auto-declaration" for any variables?
I'm stuck
Every object in PHP can get members w/o declaring them:
$mycar = new car;
$mycar->model = "Mercedes";
echo $mycar->check_model(); # Good car
That's PHP's default behaviour. Those are public. See manual.
Yes, if it doesn't exist, PHP declares it on the fly for you.
It is more elegant to define it anyway, and when working with extends it's recommended, because you can get weird situations if your extends are gonna use the same varnames and also don't define it private, protected or public.
More info:
http://www.php.net/manual/en/language.oop5.visibility.php
PHP class members can be created at any time. In this way it will be treated as public variable. To declare a private variable you need to declare it.
Yes. But this way variables will be public. And declaration class variable as "var" is deprecated - use public, protected or private.
No, it's because $model is an argument of the function set_car. Arguments are not exactly variables, but placeholders (references) to the variables or values that will be set when calling the function (or class method). E.g., $model takes the value "Mercedes" when calling set_car.
I think this behavior can lead to errors.
Lets consider this code with one misprint
declare(strict_types=1);
class A
{
public float $sum;
public function calcSum(float $a, float $b): float
{
$this->sum = $a;
$this->sums = $a + $b; //misprinted sums instead of sum
return $this->sum;
}
}
echo (new A())->calcSum(1, 1); //prints 1
Even I use PHP 7.4+ type hints and so one, neither compiler, nor IDE with code checkers can't find this typo.

What type of code that interprets correctly in procedural programming will cause an error when included in a class (outside of a function)?

I'm new to oop and was surprised to see that code that worked properly in procedural programming,
<?php
$number_of_floors = 5;
$stairs_per_floor= 10;
echo $total_stairs= $number_of_floors*$stairs_per_floor;
?>
Lead to an error when included inside of a class:
<?php
// Class
class Building {
// Object variables/properties
public $number_of_floors = 5; // These buildings have 5 floors
public $stairs_per_floor= 10;
public $total_stairs= $number_of_floors*$stairs_per_floor;
private $color;
// Class constructor
public function __construct($paint) {
$this->color = $paint;
}
public function describe() {
printf('This building has %d floors. It is %s in color.',
$this->number_of_floors,
$this->color
);
}
}
// Build a building and paint it red
$bldgA = new Building('red');
// Tell us how many floors these buildings have, and their painted color
$bldgA->describe();
?>
If you remove
public $total_stairs= $number_of_floors*$stairs_per_floor;
Everything works.
Are you not allowed to write arithmetic expressions inside of a class if they are outside of a function? What type of code that interprets correctly in procedural programming will cause an error when included in a class (outside of a function)?
You can not do the operation at the time of defining them. Instead you should add this to your constructor and do:
$this->total_stairs = $this->number_of_floors * $this->stairs_per_floor;
Furthermore I advise you to use the generally accepted coding standards of PHP which would mean, not to use underscores in variable names.
public $totalStairs;
public $numberOfFloors;
public $stairsPerFloor;
Even more important is the choice of meaningful and readable variables names. So $bldgA should be:
$buildingA
you can't assign value by mathematical calculation while defining variable. Calculate value in constructor.
<?php
// Class
class Building {
// Object variables/properties
public $number_of_floors = 5; // These buildings have 5 floors
public $stairs_per_floor= 10;
public $total_stairs=0;
private $color;
// Class constructor
public function __construct($paint) {
$this->color = $paint;
$this->total_stairs = $number_of_floors*$stairs_per_floor;
}
public function describe() {
printf('This building has %d floors. It is %s in color.',
$this->number_of_floors,
$this->color
);
}
}
// Build a building and paint it red
$bldgA = new Building('red');
// Tell us how many floors these buildings have, and their painted color
$bldgA->describe();
?>
To answer your question: within an object oriented design, all code belongs inside a method; either a "special" method like the constructor, within a regular method, or (in languages other than PHP) in getter/setter methods (http://php.net/manual/en/language.oop5.properties.php has a way of implementing those in PHP).
Outside of methods, you're allowed to declare properties or attributes - but you should think of that really as a declaration, not a way of executing logic. The fact you can assign literals during the declaration is purely a convenience.
Don't assign expressions as variable. Do it in the Constructor:
$this->total_stairs = $this->number_of_floors * $this->stairs_per_floor;
or do
public $total_stairs= $this->number_of_floors * $this->stairs_per_floor;
you have to use the instance variables, without $this-> they are interpreted as local variables.
$this->total_stairs = $this->number_of_floors*$this->stairs_per_floor;
also, move those to the constructor, as they are (look like) instance specific.
You can't execute expressions when define properties, even with constants and heredocs.
Calculate them in __construct method.

Assigning Gettext String to Class Member

I'm making a site that will be translated into x languages.
All strings must be localized.
There are occasions when I need to display a language name, country name or other information that has been retrieved from a database. The data being dealt with in this way will seldom be changed - as above I'm talking about language names, countries etc.
In this example I'm using the array that holds the languages into which the site's UI has been translated. To allow translation of the names (used for title text when the "change language" flag / link is hovered), I have an array like*:
Array("zh_CN" => _("Chinese - Simplified"), "en_GB" => _("English"));
I use them to get the relevant name string for a given language.
Currently I'm using a global array:
$global_langNames = Array("zh_CN" => _("Chinese - Simplified"), "en_GB" => _("English"));
Usage:
global $global_langNames;
echo $global_langNames[$code]; // $code = 'zh_CN'
Output (locale = en_GB):
Chinese Simplified
Output (locale = zh_CN):
简体中文
I would much rather declare this (and other) constant arrays as private members of the class, but it seems PHP isn't willing:
class constants_lang{
private static $langNames = Array("zh_CN" => _("Chinese - Simplified"), "en_GB" => _("English"));
static function getLangName($code){
return self::$langNames($code);
}
}
Results in:
Parse error: syntax error, unexpected '(', expecting ')' in /site/http/includes/classes/constants/lang.php on line 20
Should I hang my head low and go back to the global array, or is there another, better way for me to have a 'constant' array to be used in this manner?
*The array keys are from the database table storing language codes and whether we have a UI translation:
code ui translation
zh_CN 1
en_GB 1
zh_TW 0
....
Solution
class constants{
private $langNamesFromCode;
function __construct()
{
$this->langNamesFromCode = $this->initLangNamesFromCode();
}
/* INIT */
private static function initLangNamesFromCode()
{
return Array("zh_CN" => _("Chinese - Simplified"), "en_GB" => _("English"));
}
/* GETTERS */
public static function getLangNameFromCode($code)
{
if(self::isStatic()){
$langNamesFromCode = self::initLangNamesFromCode();
return $langNamesFromCode[$code];
}
else{
return $this->langNamesFromCode[$code];
}
}
/* UTILITY */
private static function isStatic()
{
return !(isset($this) && get_class($this) == __CLASS__);
}
}
Yes, you can only use (most) literals in variable initializations.
The work-around is something like:
class A {
private $var;
public function init() {
$this->var = func();
}
}
A::init();
You cant use functions in member declarations. If you need to do this move it to the constructor or a static function.

Categories