1

The following code snippet will produce an error on PHP 8.2:

<?php

const foo = new stdClass();

foo->bar = 'baz';

echo foo->bar;

?>

I would expect that an error would not occur, since I am assigning to the prop rather than trying to reassign the constant.

If I create a new class, extending stdClass, and add the following method:

class extendsStdClass extends stdClass {
    public function set(string $name, mixed $value) {
      $this->$name = $value;
    }
}

then I can assign to props using the following syntax:

<?php

const foo = new extendsStdClass();

foo->set('bar', 'baz');

echo foo->bar;

?>

but, the linter will not recognize props being set in this way, nor provide any type hinting:

Undefined property: extendsStdClass::$bar

Is there some reason we are not able to write to props on a class instance that is defined as a constant?

schoodic
  • 13
  • 3
  • `$x = foo; $x->bar = 'baz';` also assigns to the object's field. BTW: What exactly is `the linter` you're referring to? It could be a quality of implementation issue and/or a version issue (PHP8,2 being new). – Ulrich Eckhardt Feb 05 '23 at 19:03
  • _"the linter will not recognize props being set in this way, nor provide any type hinting"_ Depending on the linter, you can probably accomplish the intended result by defining `@method` entries in your extended class docblock. – Alex Howansky Feb 05 '23 at 19:10
  • I'm also not sure why you'd expect no error to occur here. You define a constant object that has no attributes and then you try to change that object by dynamically adding a new attribute. Constants, by definition, are immutable. – Alex Howansky Feb 05 '23 at 19:18
  • @UlrichEckhardt I'm using VSCode connected to WSL with the "PHP" extension by DEVSENSE. – schoodic Feb 05 '23 at 19:34
  • @AlexHowansky assigning also fails if you instantiate a class with known properties and attempt to modify that property. I just wouldn't expect that the object referenced by the const would be immutable, but maybe that's because I come from Javascript land. – schoodic Feb 05 '23 at 19:36
  • In what manner are you expecting `const foo = new stdClass();` to behave that's any different from how `$foo = new stdClass();` behaves? You want to be prevented from assigning a new value to `foo` but you don't want to be prevented from arbitrarily changing the value of any of its attributes? I'm confused as to what you're hoping to accomplish here. – Alex Howansky Feb 05 '23 at 19:46
  • @AlexHowansky I'd like to be able to define this const in a namespace, and access/modify it via `Namespace\foo` from the outside. Assigning the constant to a variable as Ulrich noted above does actually allow it to be modified. – schoodic Feb 05 '23 at 19:51

1 Answers1

0

I'd like to be able to define this const in a namespace, and access/modify it via

If you want to modify it, then it's not a const.

If you actually want a const, then you can just use an array to get key/value pairs:

namespace MyNamespace {
    const FOO = [
        'one' => 1,
        'two' => 2,
    ];
}

Then reference it anywhere via:

print_r(\MyNamespace\FOO);

Yields:

Array
(
    [one] => 1
    [two] => 2
)

And you can pull out a single value with array notation:

echo \MyNamespace\FOO['one'];

If you want something that you can reference directly by namespace, and still modify, you could create a class with static storage. Sort of like a half-assed singleton:

namespace MyNamespace;

class Foo
{
    private static array $data = [];
    public static function get($name)
    {
        return self::$data[$name] ?? null;
    }
    public static function set($name, $value)
    {
        self::$data[$name] = $value;
    }
}

Then you can set values from anywhere:

\MyNamespace\Foo::set('one', 1);

And get values from anywhere:

echo \MyNamespace\Foo::get('one');
Alex Howansky
  • 50,515
  • 8
  • 78
  • 98