Приглашаем посетить
Толстой (tolstoy-lit.ru)

Section 8.7.  Access Control Modifiers

Previous
Table of Contents
Next

8.7. Access Control Modifiers

There are a number of keywords you can place before a class, a method definition, or a property to alter the way PHP treats them. Here's the full list, along with what each of them does:

  • Public: This property or method can be used from anywhere in the script

  • Private: This property or method can be used only by the class or object it is part of; it cannot be accessed elsewhere

  • Protected: This property or method can be used only by code in the class it is part of, or by descendants of that class

  • Final: This property, method, or class cannot be overridden in subclasses

  • Abstract: This method or class cannot be used directlyyou have to subclass this

The problem with public properties is that they allow methods to be called and properties to be set from anywhere within your script, which is generally not a smart thing. One of the benefits of properly programmed OOP code is encapsulation, which can be thought of as similar to data hiding. That is, if your object exposes all its properties to the world, programmers using those objects need to understand how your classes work. In an encapsulated word, other programmers would only need to know the specification for your class, such as "call function X, and you'll get Y" back. They wouldn'tand shouldn'thave to know how it all works internally.

To give an example of this, we had a DogTag object $DogTag inside each dog object, as well as a $Name property, but they contained repeated information. If someone had changed the $Name property, the $DogTag information would have remained the same. The programmer can't really be blamed for changing $Name: it was publicly accessible, after all. The solution is to make all the variables private to the object using either private or protected, and to provide accessor methods like setName( ) to stop unknowing programmers from changing variables directly. These accessors are written by us, so we can have them do all the necessary work, such as changing the name on the dog tag when a dog's name changes.

Generally speaking, most of the variables in a class should be marked as either protected or private . Sometimes you will need to use public , but those times are few and far between.

8.7.1. Public

Public properties and methods are accessible from anywhere in your script, which makes this modifier the easiest to use. In PHP 4, all object properties were declared with var and were essentially public, but using this terminology is deprecated and may generate compiler warnings. Take a look at the following code:

    class Dog {
            public $Name;

            public function bark( ) {
                    print "Woof!\n";
            }
    }

    class Poodle extends Dog {
            public function bark( ) {
                    print "Yip!\n";
            }
    }

    $poppy = new Poodle;
    $poppy->Name = "Poppy";
    print $poppy->Name;

That code works in precisely the same way as before; the public keyword has not made any difference. This is because, by default, all class methods are public; before PHP 5, there was no way to make them anything else.

While the public keyword is not needed, I recommend you use it anywayit is a good way to remind people who read your code that a given method is indeed public. It is also possible that class methods without an access modifier may be deprecated in the distant future.

You always need to specify an access modifier for properties. Previous versions of PHP used the var keyword to declare properties, again because it had no concept of access modifiers. You should avoid this, and be more specific with public or one of the other keywords.

8.7.2. Private

Private properties are accessible only inside the methods of the class that defined them. If a new class inherits from it, the properties will not be available in the methods of that new class; they remain accessible only in the functions from the original class. For example:

    class Dog {
            private $Name;
            private $DogTag;

            public function setName($NewName) {
                    // etc
            }
    }

Both $Name and $DogTag are private, which means no one can access them unless they are doing so in a method that is part of the class, such as setName( ). This remains public because we want this to be accessible by anyone.

Now if our nosey programmer comes along and tries to set $Name directly, using code like $poppy->Name, he will not get what he was expecting: PHP will give him the error message: "Cannot access private property Dog::$Name". However, if that private property were inherited from another class, PHP will try to accommodate his request by having a private property and a public property. Yes, this is confusing; however, the following code should clear things up:

    class Dog {
            private $Name;
    }

    class Poodle extends Dog { }

    $poppy = new Poodle;
    $poppy->Name = "Poppy";
    print_r($poppy);

Running that script will output the following:

    poodle Object
    (
    [Name:private] =>
    [Name] => Poppy
    )

Notice that there are two Name propertiesone that is private and cannot be touched, and another that PHP creates for local use as requested. Clearly this is confusing, and you should try to avoid this situation, if possible.

Keep in mind that private methods and properties can only be accessed by the exact class that owns them; child classes cannot access private parent methods and properties. If you want to do this, you need the protected keyword instead.

8.7.3. Protected

Properties and methods marked as protected are accessible only through the object that owns them, whether or not they are declared in that object's class or have descended from a parent class. Consider the following code:

    class Dog {
            public $Name;
            private function getName( ) {
                    return $this->Name;
            }
    }

    class Poodle extends Dog {
            public function bark( ) {
                    print "'Woof', says " . $this->getName( );
            }
    }

    $poppy = new Poodle;
    $poppy->Name = "Poppy";
    $poppy->bark( );

In that code, the class Poodle extends from class Dog, class Dog has a public property $Name and a private method getName( ), and class Poodle has a public method called bark( ). So, we create a Poodle, give it a $Name value of "Poppy" (the $Name property comes from the Dog class), then ask it to bark( ). The bark( ) method is public, which means we can call it as shown above, so this is all well and good.

However, the bark( ) method calls the getName( ) method, which is part of the Dog class and was marked privatethis will stop the script from working, because private properties and methods cannot be accessed from inherited classes. That is, we cannot access private Dog methods and properties from inside the Poodle class.

Now try changing getName( ) to protected, and all should become clearthe property is still not available to the world as a whole, but handles inheritance as you would expect, meaning that we can access getName( ) from inside Poodle.

8.7.4. Final

The final keyword is used to declare that a method or class cannot be overridden by a subclass. For example:

    class Dog {
            private $Name;
            private $DogTag;
            final public function bark( ) {
                    print "Woof!\n";
            }
            // etc

The Dog bark( ) method is now declared final, which means it cannot be overridden in a child class. If we have bark( ) redefined in the Poodle class, PHP outputs a fatal error message: "Cannot override final method dog::bark( )". Using the final keyword is optional, but it makes your life easier by acting as a safeguard against people overriding a method you believe should be permanent.

For stronger protection, the final keyword can also be used to declare a class uninheritablethat is, that programmers cannot extend another class from it. For example:

    final class Dog {
            private $Name;
            public function getName( ) {
                    return $this->Name;
            }
    }

    class Poodle extends Dog {
            public function bark( ) {
                    print "'Woof', says " . $this->getName( );
            }
    }

Attempting to run that script will result in a fatal error, with the message: "Class Poodle may not inherit from final class (Dog)".

8.7.5. Abstract

The abstract keyword is used to say that a method or class cannot be created in your program as it stands. This does not stop people inheriting from that abstract class to create a new, non-abstract (concrete) class.

Consider this code:

    $poppy = new Dog;

The code is perfectly legalwe have a class Dog, and we're creating one instance of that and assigning it to $poppy. However, given that we have actual breeds of dog to choose from, what this code actually means is "create a dog with no particular breed." Even mongrels have breed classifications, which means that a dog without a breed is impossible and should not be allowed. We can use the abstract keyword to enforce this in code:

    abstract class Dog {
            private $Name;
    // etc

    $poppy = new Dog;

The Dog class is now abstract, and $poppy is now being created as an abstract dog object. PHP now halts execution with a fatal error message: "Cannot instantiate abstract class Dog".

As mentioned already, you can also use the abstract keyword with methods, but if a class has at least one abstract method, the class itself must be declared abstract. Also, you will get errors if you try to provide any code inside an abstract method, which makes this illegal:

    abstract class Dog {
            abstract function bark( ) {
                    print "Woof!";
            }
    }

It even makes this illegal:

    abstract class Dog {
            abstract function bark( ) { }
    }

Instead, a proper abstract method should look like this:

    abstract class Dog {
            abstract function bark( );
    }

Section 8.7.  Access Control Modifiers

If it helps you understand things better, you can think of abstract classes as being similar to interfaces, which are discussed later in this chapter.


8.7.6. Iterating Through Object Properties

We can treat an object as an array with the foreach loop, and it will iterate over each of the properties inside that object that are accessible. That is, private and protected properties will not be accessible in the general scope. Take a look at this script:

    class Person {
            public $FirstName = "Bill";
            public $MiddleName = "Terence";
            public $LastName = "Murphy";
            private $Password = "Poppy";
            public $Age = 29;
            public $HomeTown = "Edinburgh";
            public $FavouriteColor = "Purple";
    }

    $bill = new Person( );

    foreach($bill as $var => $value) {
            echo "$var is $value\n";
    }

That will output this:

    FirstName is Bill
    MiddleName is Terence
    LastName is Murphy
    Age is 29
    HomeTown is Edinburgh
    FavouriteColor is Purple

Note that the $Password property is nowhere in sight, because it is marked Private and we're trying to access it from the global scope. If we re-fiddle the script a little so that the foreach loop is called inside a method, we should be able to see the property:

    class Person {
            public $FirstName = "Bill";
            public $MiddleName = "Terence";
            public $LastName = "Murphy";
            private $Password = "Poppy";
            public $Age = 29;
            public $HomeTown = "Edinburgh";
            public $FavouriteColor = "Purple";

            public function outputVars( ) {
                    foreach($this as $var => $value) {
                            echo "$var is $value\n";
                    }
            }
    }

    $bill = new Person( );
    $bill->outputVars( );

Now the output is this:

    FirstName is Bill
    MiddleName is Terence
    LastName is Murphy
    Password is Poppy
    Age is 29
    HomeTown is Edinburgh
    FavouriteColor is Purple

Now that it's the object itself looping through its properties, we can see private properties just fine. Looping through objects this way is a great way to handwrite serialization methodsjust remember to put the code inside a method; otherwise, private and protected data will get ignored.


Previous
Table of Contents
Next