PHP Classes and Objects
Implementing Access Control
Problem
You want to assign a visibility to methods and properties so they can only be accessed within classes that have a specific relationship to the object.Solution
Use the public, protected, and private keywords:class Person {
public $name; // accessible anywhere
protected $age; // accessible within the class and child classes
private $salary; // accessible only within this specific class
public function __construct() {
// ...
}
protected function set_age() {
// ...
}
private function set_salary() {
// ...
}
}
Discussion
PHP allows you to enforce where you can access methods and properties. There are three levels of visibility:• public
• protected
• private
Making a method or property public means anyone can call or edit it.1
You can also label a method or property as protected, which restricts access to only the current class and any child classes that extend that class.
The final visibility is private, which is the most restrictive. Properties and methods that are private can only be accessed within that specific class.
If you’re unfamiliar with this concept, access control can seem like an odd thing. However, when you use access control, you can actually create more robust code because it promotes data encapsulation, a key tenet of OO programming.
Inevitably, whenever you write code, there’s some part—the way you store the data, what parameters the functions take, how the database is organized—that doesn’t work as well as it should. It’s too slow, too awkward, or doesn’t allow you to add new features, so you clean it up.
Fixing code is a good thing, unless you accidently break other parts of your system in the process. When a program is designed with a high degree of encapsulation, the underlying data structures and database tables are not accessed directly. Instead, you define a set of functions and route all your requests through these functions.
For example, you have a database table that stores names and email addresses. A program with poor encapsulation directly accesses the table whenever it needs to fetch a person’s email address:
$name = 'Rasmus Lerdorf';
$sqlite = new PDO('sqlite:/usr/local/users.db');
$rows = $db->query("SELECT email FROM users WHERE name LIKE '$name'");
$row = $rows->fetch();
$email = $row['email'];
A better encapsulated program uses a function instead:
function getEmail($name) {
$sqlite = new PDO("sqlite:/usr/local/users.db");
$rows = $db->query("SELECT email FROM users WHERE name LIKE '$name'");
$row = $rows->fetch();
$email = $row['email'];
return $email;
}
$email = getEmail('Rasmus Lerdorf');
Using getEmail() has many benefits, including reducing the amount of code you need to write to fetch an email address. However, it also lets you safely alter your database schema because you only need to change the single query in getEmail() instead of searching through every line of every file, looking for places where you SELECT data from the users table. Or you can switch from one database to another with relative ease.
It’s hard to write a well-encapsulated program using functions, because the only way to signal to people “Don’t touch this!” is through comments and programming conventions.
Objects allow you to wall off implementation internals from outside access. This prevents people from relying on code that may change and forces them to use your functions to reach the data. Functions of this type are known as accessors, because they allow access to otherwise protected information. When redesigning code, if you update the accessors to work as before, none of the code will break.
Marking something as protected or private signals that it may change in the future, so people shouldn’t access it or they’ll violate encapsulation.
This is more than a social convention. PHP actually prevents people from calling a private method or reading a private property outside of the class. Therefore, from an external perspective, these methods and properties might as well not exist because there’s no way to access them.
In object-oriented programming, there is an implicit contract between the author and the users of the class. The users agree not to worry about the implementation details. The author agrees that as long as a person uses public methods they’ll always work, even if the author redesigns the class.
Both protected and private provide protection against usage outside of the class. Therefore, the decision to choose one visibility versus the other really comes down to a judgment call—do you expect someone will need to invoke that method in a child class?
If you (or your team) are the only people using that class, choosing private over protected allows you to be conservative and not overexpose access unnecessarily. It’s easy to open up the visibility later on, if needed. If you’re planning on distributing this code as a package, then biasing toward protected helps enable others to extend on your work without needing to modify your master library.
No comments:
Post a Comment