Why doesn't PHP permit private constants? [dup

2019-04-17 10:30发布

问题:

This question already has an answer here:

  • Are private constants possible in PHP? [duplicate] 4 answers
  • Why doesn't PHP permit private const? 2 answers

Why doesn't PHP permit private constants?

I am aware that there are workarounds, such as using a private static property instead.

However, from an OOP or software design perspective, what was the reasoning?

回答1:

Why doesn't PHP permit private constants?

In PHP constants are part of the interface and the interface is public, always (that's what an interface is for).

See as well PHP Interfaces.

I'm pretty sure this is the reason design-wise.

Regarding the comment under your question that someone wants to reduce the visibility of constants to change them later, I'd say this sounds more like a variable than a constant which does not change it's value. It's constant.



回答2:

tl;tr

What was the reasoning to not implement private constants?

This is a good question.

Did they really consider this?

I don't know.

  • When searching through the PHP internals mailing list, i found nothing about this topic. Unless a internal's member speaks up, we'll never know.

    With regard to the history of the language - a bit 1:1 C method wrapping, some bits from Perl (regexp), some bits Java (oop) - it's possible that this idea popped up, while looking for new language features.

    I would relate the concept of a "private constant" to VisualBasic or Java. I doubt that VB has a big or any influence on PHP. VB allows the definition of "private constants" in "Modules" - it's the default access scope. Visual Basic doesn't allow constants in Interfaces and Namespaces (but PHP does).

    PHP might include some OOP concepts from Java, but there is one big difference: constants are variables in Java. Their access modifiers / visibility levels are: "public, private, final and static". A private constant declaration looks like this: "private static final String MYCONST = "My Constant"; It's OOP - end of story. PHP constant access feels more hackish compared to that - BUT it's more simple and you still have the workaround at hand.

  • The first comment in the PHP manual for Class Constants is:

    It may seem obvious, but class constants are always publicly visible. They cannot be made private or protected. I do not see it state that in the docs anywhere.

    Why is this obvious? "Why is a class constant public by default and not private?" Maybe, it's a missing language feature, because not all class members can be hidden properly. And he is right, when you come from Java or VB to PHP this question pops up.

  • Let's take a look at the PHP spec. The current state of implementation in PHP is: class constants are always public and static. So, again and again, thumbs up for Facebook for writing such detailed document: the author considered different visibility or access-control levels.

Let's take a look at interface, class, constant and visibility:

  • How does the concept "const" differ from "private static"?

    The static variable can be changed, the constant cannot be changed. You cannot assign the runtime value of a function to a const (const A = sprintf('foo %s', 'bar');), but to a private static var.

  • An interface might have constants - they cannot be overridden.

  • A class might have a constant - which might be overridden by a inheriting class/interface.

  • There is also an OOP pattern called "constant interface pattern" - it describes the use of an interface solely to define constants, and having classes implement that interface in order to achieve convenient syntactic access to those constants.

    An interface is provided so you can describe a set of functions and then hide the final implementation of the functions in an implementing class. This allows you to change the implementation of the functions, without changing how you use it. Interfaces exist to expose an API.

    And by defining constants in an interface and implementing the interface by a class, the constants become part of the API. In fact, you are leaking implementations details into the API. That's why some consider this being an anti-pattern, among them Joshua Bloch (Java).

Now, let's try to combine some concepts and see if they fit.

Let's pretend we try to avoid the critique from above, then you need to introduce a syntax, which allows qualified access to the constant, but hides the constant in the API. You could come up with "Access control" via visibility levels: "public, private, protected, friend, foe". The goal is to prevent the users of a package or class from depending on unnecessary details of the implementation of that package or class. It is all about hiding implementation details, right?

What about "private constants" in "interfaces"?

That would actually solve the critique from above, right? But the combination of interface with "private", doesn't make sense. The concepts are contrary. That's why interface do not allow "private" access/visibility-levels. And a "private" constant in an "interface" would be mutually exclusive, too.

What about "private constants" in "classes"?

    class a { 
        /*private*/ const k = 'Class private constant k from a'; 
    }

    class b extends a
    {
        const k = 'Overridden private constant a::k with Class constant k from b';

        const k_a = parent::k; 

        // fatal error: self-referencing constant
        #const k_selfref = self::k . ' + ' . self::k_selfref; 

        // fatal error: "static::" is not allowed in compile-time constants
        #const k_staticref = static::k; 
    }

    // direct static access would no longer work, if private 
    // you need an instance of the parent class or an inheriting class instance
    echo a::k; 
    echo b::k;  
    echo b::k_a;

    $obj_b = new b;
    echo $obj_b::k;
    echo $obj_b::k_a;

Is there a benefit?

  • The private constant in the class wouldn't be exposed in the API. This is good OOP.

  • The access to the parent constant from outside would be a class and/or inheritance access.

    echo a::k, which now works - could respond with "Fatal error: Trying to access a private constant without a class instance or inheritance.".

  • (This might still work solely at compile-time, if there is no run-time value assignment to a const. But i'm not sure about this one.)

Are there caveats?

  • We would lose direct static access to constants.

  • Requiring that an instance of a class is created, just to access constants, is a waste of resources. Direct static access saves resources and is simple. If we introduce private, that's lost and the access would be bound to the instance.

  • A "private const" is implicitly a "private static const". The access operator is still "::".

    A possible follow-up change would be the switch to implicitly non-static constants. That's a BC break. If you switch the default behavior to non-static, the access operator changes from "::" to "->". This establishes a proper OOP object access to constants, which is comparable to Java's concept of "constants as variables with access level". This would work a bit like this: http://3v4l.org/UgEEm. The access operator changes to static, when the constant is declared as "public static const", right?

Is the benefit good enough to implement it?

I don't know. It's up for discussion. I like both: the const static access, because it's dead simple and the concept of "constants as variables" and proper access levels.

After that's implemented, in order to save resources and keep going fast, everyone starts to (re-)declare "public static const", to drop the instance requirement and violate OOP ;)

And btw: i found an HHVM overflow, while experimenting with code of this answer.