2

I understand the scope problems in PHP that arise from use of constants (as well as the global keyword with normal variables) in functions and classes. I understand Singletons and that I don't have (and will never have) any use for them in PHP. What I don't understand is if use of class constants creates the same or similar issues.

Here's a snippet of code, similar to what I'm writing but much simpler (for brevity's sake I'll treat everything as if it's in one file, no typechecking or initialization, etc.):


<?php

class Environment
{
    private $var1;
    private $var2;
    private $var3;

    public const CONST_VAR = "ThisIsConstant";

    public __construct($dependency1, $dependency2)
    {
        $this->var1 = $dependency;
        $this->var2 = new SomeOtherObject($dependency2);

        if ($this->var1 == self::CONST_VAR) // Situation 1
        {
            $var3 = true;
        }
    }

    public getStuff()
    {
        return $this->var1 . " and " . $this->var2->getOtherStuff();
    }
}

class SomeOtherObject
{
    private $stuff;

    public __construct($dependency)
    {
        if ($dependency == Environment::CONST_VAR) // Situation 2
        {
            $this->stuff = strtolower($dependency);
        }
        else
        {
            $this->stuff = $dependency;
        }
    }

    public getOtherStuff()
    {
        return $stuff;
    }
}

$env = new Environment("TestName", "OtherStuff");

echo $env->getStuff();
echo $env::CONST_VAR; // Situation 3
echo Environment::CONST_VAR // Situation 4

?>

Four situations are present:

  1. Accessing a class constant in the same class using self, which clearly presents no scope issue since I'm really just accessing a property.
  2. Accessing a class constant in another class using the class name, which seems to present a global scope issue since SomeOtherObject requires the Environment class to exist and have that constant. The Environment class is therefore not self-contained and harder to unit test.
  3. Accessing a class constant in the global space using a variable name, which requires that variable to be an Environment object but seems to not create any scope issue since the Environment class itself does not depend on anything outside of it, rather the other way around.
  4. Accessing a class constant in the global space using the class name, which also seems to not create any scope issue for the same reason as Situation 3.

From my understanding of these concepts it looks like Situation 2 is the only one that needs to be avoided...but are there global scope/unit testing/code smell issues I'm missing with Situations 1, 3, and 4?

Edit: This question was closed as opinion-based so I'll try to make this more direct. I'm not asking about whether or not using the global space in classes is okay, or whether or not singletons are an antipattern, or any of that. What I'm asking is if the scoping and self-containment issues that do arise from use of the global space within a class (this is not an opinion) work the same way in the opposite direction, i.e. using class constants in the global space. Does it make the code less testable? Does it introduce dependencies where there shouldn't be any? I'm not asking if the behavior is dogmatically right or wrong, I just want to be aware of measurable problems that can arise from doing this. Thanks to everyone for the comments so far!

Joey Miller
  • 131
  • 6
  • Do you ever use the PDO class constants in a class, do you think that introduces an issue? – Nigel Ren Feb 03 '23 at 08:45
  • I would treat PDO as a different situation entirely because it's available as a part of PHP and therefore accessible everywhere without having to do anything. – Joey Miller Feb 03 '23 at 08:48
  • 1
    What is the scope problems arise from constants and global keyword? – shingo Feb 03 '23 at 09:00
  • 1
    In #2, you can either embed the constant’s value literally and hope it never changes, or use the class as you’ve shown. It isn’t a right or wrong, it just depends. If you are using the class solely for the constant, and nothing else, and it is part of a larger library/package/module that you aren’t using, that is probably wasteful. Otherwise, it is part of the class’s interface, just like properties or methods, and not really scope-related. In that regard, it is really the class’s FQN that is in the “global scope”. – Chris Haas Feb 03 '23 at 09:10
  • 1
    The "normal php constants" are actually defined by the `define` function and are automatically available everywhere, there is no need to use the `global` keyword. Since they value cannot change there cannot be hidden mechanic controlling them which is the major issue with global variables. It seems like you have concerns about the dependency which doesn't have ideal answer, but the good thing about class constants is that the autoload is called which means they are "less dependent". – Kazz Feb 03 '23 at 09:13
  • 1
    I think defining your understanding of the problem might help, too. For instance, one major issue I'm aware of with regular constants is that you usually have to jump through extra hoops when testing because they cannot be changed. Similarly, one issue with global variables is that there's always a chance that someone (including yourself) might stomp the variable. Class constants don't suffer from the latter, and although the former is generally true, they can be [changed in subclasses](https://3v4l.org/F72SY) which might be helpful for testing. – Chris Haas Feb 03 '23 at 14:12
  • @shingo: See https://stackoverflow.com/questions/5166087/php-global-in-functions/5166527 and https://stackoverflow.com/questions/19788115/are-constants-as-evil-as-global-variables-and-singletons – Joey Miller Feb 04 '23 at 05:44
  • @Kazz: yes, sorry for not making it clear but I meant using `global` with normal variables. Edited my question to hopefully clarify this. – Joey Miller Feb 04 '23 at 05:45
  • @ChrisHaas: I see...I guess a lot of the problem for me with this specific project revolves around elimination of magic strings. I'd love to just use normal constants for all of that but since there's a lot of OOP it doesn't seem like a good idea. Class constants made sense because I don't actually need an occurence of Situation 2 anywhere, they would just be used within the Environment class and in the global space where it seems like they can be accessed without causing an issue, but I'm still not completely sure. – Joey Miller Feb 04 '23 at 05:53
  • @Kazz: and noted...maybe there's no answer to this after all! I've edited the question anyway to make it a bit more objective. – Joey Miller Feb 04 '23 at 05:55
  • 1
    The problems mentioned in these question should not be called scope problem, they are just undefined variable problems. For class constants most static analyzers can find these problems before you release the project. – shingo Feb 04 '23 at 07:55
  • 1
    The scope problem is for example when `Environment` is splitted in a file called `EnvA.php`, but meanwhile you have another file called `EnvB.php` which also has a class `Environment` inside. Now the meaning of `Environment::CONST_VAR` is based on which file (scope) the script is connecting. – shingo Feb 04 '23 at 08:00
  • 1
    When I mix classes with non class-based PHP, I try to have as much of my complex logic in the classes. Whenever I try to think of where I’d have a need to expose a class constant outside of a class, all examples turn into an Enum for me, or possibly an ENV variable. Looking at your `Environment` example being split across two files, I can’t imagine a legit real world example of doing that, and I mean no offense when I say that. I’d think constants would be the least of your worries when doing that. – Chris Haas Feb 04 '23 at 12:03
  • @shingo: Sorry for the lack of clarity - I meant this as a problem with the "global scope" or better yet the "global space", not a "scope problem" as in a problem with variable scope. – Joey Miller Feb 05 '23 at 08:14
  • @ChrisHaas: None taken! Would you mind elaborating? Do you mean that accessing the constant doesn't have a real-world use, or that the Environment class itself doesn't have a real-world use? And more about the additional things I might need to worry about? Appreciate the help. – Joey Miller Feb 05 '23 at 08:16
  • 1
    Having two files with the same (FQ) class name is something I can’t imagine a valid case for, except for a weird polyfil edge case. Thinking about an HTTP library where maybe the HTTP status codes or methods could be class constants, I’d think an enum would be better (or just the literals themselves). But even if a class constant was used, you are depending on the class, not the constant, and as long as you are using the class as intended, that’s okay. – Chris Haas Feb 05 '23 at 12:10
  • @ChrisHaas: Okay thanks. I don't think I have the same FQ class name used twice unless you mean defining it in one file and then using it in another. I think the best option for me at this point is to just have normal constants used in the global space and then inject those as dependencies to all the classes that would need them and just make sure as non-constant properties that they're not modified. – Joey Miller Feb 05 '23 at 21:27
  • "the scoping and self-containment issues that do arise" - which are these? – Nico Haase Feb 08 '23 at 09:27

0 Answers0