Did You Know? Class Visibility in PHP

While it remains an imperative-style language, since version 5 PHP’s object model has gotten significantly more sophisticated. While in PHP 4 objects were little more than arrays with functions, the newer versions have most of the trimmings of modern OOP. Among those, probably considered a basic triviality at best, is member visibility. In fact, since PHP will assume that, if no visibility modifier is supplied, a method is public, it’s possible to write a class without ever thinking about its visibility.

Of course, if you’ve done anything much with OOP you can understand exactly why visibility is important, and why you’d want public-facing getters and setters to do your bidding (in most cases) instead of directly modifying members; heck, it’s about the closest we’re going to get to strong-typing in PHP. However, there is one very important “Gotcha!” in PHP’s visibility model: it is enforced at the class-level, not at the instance level.

This means, quite simply, that instances of the same class can call each other’s private/protected methods. I’m not here to decry this and tell you how to avoid it, because it’s here to stay so far as we can tell. I am here to embrace and, yes, evangelize it. Consider the case of the “User” object — that is, an object to represent the properties of a user. In most sufficiently complicated systems, a user will have to have permissions, which may even apply to different tiers of the application, or smaller object subdivisions, groups, etc – such a discussion is beyond the scope of this article – and I happen to already have just such an object in mind, as it is what is currently occupying most of my days at work.

Now, we have a user object, and somehow this object represents permissions. In my case, the user object will not always need to load its permissions to perform a task, and because of this the permissions (as well as anything else that doesn’t reside in the main table) are lazy-loaded the first time they are accessed via the getter. The block of code to do this, of course, is abstracted away, so any time I might need user permissions, I just call this:

$this->lazyLoadPermissions();

And this function, declared as private, takes care of checking to see if permissions are loaded yet, and if not, it loads them. When the user object is stored in the session at the end of the script’s execution, the permissions are not saved with the rest of the data (thank you __sleep()), and the cycle begins anew the next time I try to access this data.

So where does this neat visibility functionality come into play? One of the tasks I need to perform now and again is determining whether or not a particular user can claim “authority” over another user. Which is to say, just because I have user management privileges doesn’t mean I should be able to disable a user who is flagged as the owner of an object equal-to or higher-than my own permissions extend. So, to solve this problem, I wrote a simple function called hasAuthorityOver which accepts an instance of the user object as a parameter, and then tries to compare their permissions. Here are the first few lines:

	public function hasAuthorityOver(User_Object $subject) {
		$this->lazyLoadPermissions();
		$subject->lazyLoadPermissions();

As you can see, since the visibility is enforced at class level, and both of these objects are instances of User_Object, it is possible to call private methods on other objects, allowing you some small amount of flexibility in maintaining object integrity; of course, if you aren’t expecting this functionality, it can be jarring, but since it’s not going to change it’s worth embracing for the sake of making life easier.

Update! A new example enters the arena!

Another application of this technique might work as follows. Suppose you have an object Foo, which represents some arbitrary resource in a heirarchy, and Foo can have children which are also of type Foo. You might have something resembling this:

class Foo {
    private $children = array();
 
    public function setup(/* ... */, Foo $parent) {
        // ...
        $parent->addChild($this);
    }
 
    protected function $addChild(Foo $child) {
        // ...
    }
}

In this case, while the addChild method is hidden from public access, potential children may call their parent’s addChild method in order to add themselves in one easy step. This solves a problem of granting visibility between certain classes while still restricting it from the public scope, as you may elect to provide a “common ancestor” in order to allow objects to cross class boundaries. Food for thought!

Recent Entries

Comments are closed.