Class Constant Visibility


In this video we cover a new feature available in PHP 7.1 - Class Constant Visibility.

In short, this brings the concept of public, protected, and private to const.

Direct from the PHP 7.1 docs:

<?php

class ConstDemo
{
    const PUBLIC_CONST_A = 1;
    public const PUBLIC_CONST_B = 2;
    protected const PROTECTED_CONST = 3;
    private const PRIVATE_CONST = 4;
}

Note here that both const, and public const are equivalent. In other words, const without a visibility modifier (public / protected / private) defaults to public.

Ok, so this seems fairly straightforward and useful.

But what if you don't yet quite understand the difference between public, protected, and private?

Let's take a quick look.

Typically the most common place to first encounter these visibility modifiers are in your first steps to working with classes:

public function someFunction() {}

// or

protected function anotherFunction() {}

// or

private function yetAnotherFunction() {}

When I first encountered these, I have to say I had no clue what the differences were, but I knew public always seemed to work. It also makes code that (on the surface) seems much easier to test. This is a large part in why beginners to testing so frequently change all class visibility to public... but that's for another discussion :)

Let's take an example:

class A
{
    public function ourFunction()
    {
        return true;
    }
}

Here we have a very basic class with one public function - ourFunction.

We can interact with this class:

$a = new A;
$a->ourFunction(); // true

We can shorten this:

$a = (new A)->ourFunction(); // true

Ok, so nothing mindblowing here. We have a class, it has a method, we can call it.

However, what happens if we change our class to make ourFunction a protected method?

class A
{
    protected function ourFunction()
    {
        return true;
    }
}

And then we try to call it:

$a->ourFunction();

It blows up:

PHP Fatal error:  Uncaught Error: Call to protected method A::ourFunction() from context '' in /path/to/your/file.php:8

A very similar error is thrown if using private instead of protected. Why?

Well, we cannot directly use anything other than the public API of a class. More on this shortly when we get to interface's.

Let's continue with a further example:

class A
{
    protected function aProtectedFunction()
    {
        return true;
    }
}

class B extends A
{
    public function someFunction()
    {
        return $this->aProtectedFunction();
    }
}

$b = (new B)->someFunction(); // true

In Class A we define a protected function. This function is only available internally to either itself (Class A), or anything that extends it. In our example, Class B extends Class A, so we gain access to anything public or protected in Class A.

We can expose the protected function of aProtectedFunction by wrapping it in a public function someFunction inside Class B.

Is this a great idea? It all depends on your project, of course. This is merely an example.

Let's continue the example:

class A
{
    private function aPrivateFunction()
    {
        return true;
    }
}

class B extends A
{
    public function someFunction()
    {
        return $this->aPrivateFunction();
    }
}

$b = (new B)->someFunction();

Now A contains a single private function.

Following the same pattern of trying to expose the private function by wrapping it in the public function won't work here. Private functions or properties (think: member variables) cannot be used by anything other than the class in which they are defined.

Ok, so knowing this, we could now take a look at an interface:

interface ourInterface
{
    public function someFunction();
}

An interface explicitly defines the methods that a class that implements this interface needs to implement. Uh huh.

Think of an interface as a contract. Any class that conforms to the contract must do what the contract says - in other words, if the interface says a public function someFunction must exist, then the class implementing this interface must have that function. It may have other functions, it may not, but it must have that function to comply with the interface.

An example better illustrates this:

interface ourInterface
{
    public function someFunction();
}

class A implements ourInterface
{
    public function someFunction()
    {
        return true;
    }

    protected function anotherFunction()
    {
        // not directly tied to the interface in any way
    }

    private function whatever()
    {
        // etc
    }
}

So why is all this interesting in the context of Class Constant Visibility?

Well, firstly, to explain why you might need the three types of constant.

A public const is going to be directly available to any other class that needs it. You don't need to a new up a class to access its constants:

class A
{
    const SOME_CONST = 'some constant';
    public const A_PUBLIC_CONST = 'a public const'
}

// could be used by anyone:

echo A::A_PUBLIC_CONST; // a public const

class B
{
    private function someFunction()
    {
        return A::SOME_CONST; // returns 'some constant'
    }
}

Remember const and public const are both public.

Again, contrived examples, likely you wouldn't want to tie class B to class A in such a crazy way. But you could. And if you can, people do, and that means it happens.

Knowing this, we can now use the Class Constant Visibility modifier in PHP 7.1 to stop this:

class A
{
    protected const A_PROTECTED_CONST = 'a protected const'
}

// can only be used by classes extending A

echo A::A_PUBLIC_CONST; // throws 'call to protected method'

// note this class does not extend A
class B
{
    public function someFunction()
    {
        return A::A_PROTECTED_CONST; // throws 'call to protected method'
    }
}

class C extends A
{
    private function someFunction()
    {
        return A::A_PROTECTED_CONST; // returns 'a protected const'
    }
}

By marking our constants as private or protected, we can protect (excuse the pun) them from outside access in the same way that we always could with private and protected functions.

This is a really nice, and useful, addition to PHP 7.1

And to finish up, tracking back to the concept of interface's, it's worth noting that you can define constants on an interface:

interface ourInterface
{
    const A_CONST = 'yes this is valid';
    public const B_CONST = 'and so is this';

    public function someFunction();
}

echo ourInterface::A_CONST; // 'yes this is valid'

But as an interface always defines the publicly available methods / constants, it must therefore stand that we cannot define protected const or private const on an interface:

interface ourInterface
{
    private const PRIV_CONST = 'is this valid?';
}

echo ourInterface::PRIV_CONST; // throws: 'PHP Fatal error: Access type for interface constant someInt::A_THING must be public'

And of course, you cannot define a protected const on an interface, and if you did, it would throw the same error.

Episodes

# Title Duration
1 Shorthand Destructuring 06:13
2 Nullable Types 06:37
3 Class Constant Visibility 03:15
4 Void Functions 01:53
5 Multi Catch Exception Handling 05:10