PHP Object Attributes
-
In this article we’re going to draw inspiration from Ruby’s
attr_accessormethod in an attempt to create dynamic getter and setter methods for PHP classes. Let’s start by taking a look at a similar way in which we can use the two languages to create a simple Book class.Accessing Attributes
We can directly assign and access public data members in PHP, but Ruby objects have no public data members. We can simulate a similar behavior in Ruby using the
attr_accessormethod.PHP
class Book { public $author; } $book = new Book; $book->author = 'jim'; print $book->author; // => jim
Ruby
class Book attr_accessor :author end book = Book.new book.author = 'jim' print book.author # => jim
While it appears that Ruby is using a public attribute, this is simply because parentheses are optional in Ruby. We can call the same methods like this:
Ruby
book.author=('jim') print book.author()
Taking a look at the class definition, one might infer that Ruby’s
attr_accessorclass method is creating a public attribute. It is actually doing something quite different. When we invoke this method, we dynamically create theauthor()andauthor=()instance methods which access and assign the@authorinstance variable in our object. This gives us some flexibility to change the implementation of these methods later on if we choose.The Problem with Public
Imagine we want our class to capitalize the author name when we assign it. In Ruby, we could implement this pretty easily without breaking the existing API. We can explicitly define the accessor method ourselves.
Ruby
class Book attr_accessor :author def author=(author) @author = author.capitalize end end book = Book.new book.author = 'jim' print book.author # => Jim
If we were to try and do the same thing in PHP with public data members, we immediately see a problem. We can’t change the implementation of author assignment. We are assigning the value directly, and have no encapsulation within the object.
Getter and Setters
There is a point where you basically give up on public attributes for all but the most basic data structures. They’re just too inflexible, with the preferred alternative being protected or private members with getter and setter methods:
PHP
class Book { protected $_author; public function setAuthor($author) { $this->_author = ucfirst($author); } public function getAuthor() { return $this->_author; } } $book = new Book; $book->setAuthor('jim'); print $book->getAuthor(); // => Jim
This class definition accomplishes the same thing as the above Ruby class definition. By using methods to access the author, we can easily abstract any details within our class without any need to change the class API in the future.
Better Access using Magic
The PHP example is a bit more verbose than the Ruby, and would become even more so for each attribute that we add. Using Ruby as an example, we can actually come up with a reasonably elegant solution to the repetitive getter/setter methods using PHP’s
__getand__setmagic methods. Let’s say that we also want a title and page count for our class. The Ruby definition would look like this:Ruby
class Book attr_accessor :author, :title, :pages def author=(author) @author = author.capitalize end end book = Book.new book.author = 'jim' book.title = 'Wandering in the Desert' print "#{book.author} : #{book.title}" # => Jim : Wandering in the Desert
Wouldn’t it be nice if if we could do something like this in PHP?
PHP
class Book extends AttrObject { protected $_author; protected $_title; protected $_pages; public function __construct() { $this->attrAccessor('author', 'title', 'pages'); } public function setAuthor($author) { $this->_author = ucfirst($author); } } $book = new Book; $book->author = 'jim'; $book->title = 'Wandering in the Desert' print "$book->author : $book->title"; // => Jim : Wandering in the Desert
This syntax simplifies our class by eliminating redundant getter and setter methods. The custom
AttrObjectclass defines three methods. TheattrAccessor()method creates an attribute on the instance that can be both read and written. Two other variations exist:attrReader()creates attributes that can only be read, andattrWriter()creates attributes that can only be written.Overriding Attributes
While it may appear that we’re calling public attributes, these attributes remain protected.
AttrObjectworks by using the missing property handlers__get()and__set(). When one of our attributes is accessed, it first scans for a method likegetAuthor()orsetAuthor()on the object. If not found, it looks for a protected property like$_author. If that is also not found, it searches for a method called_get()or_set(). Finally, an error is found if nothing is available to satisfy the conditions.You can download the class source code here:
attr-code.tgz for Unix
attr-code.zip for WindowsThe advantages of the example are clear when the implementation of these attributes need to change. The attributes promote encapsulation by allowing the implementation to change while the interface remains the same.


3 comments
comment by Michel de Lange 4 Jan 08
Code note, demo/demo.php: you might want to change require_once dirname(__FILE__).’/lib/AttrObject.php’ to require_once dirname(__FILE__).’/../lib/AttrObject.php’.
comment by Mike 4 Jan 08
That’s been fixed, thanks.
comment by Maledictus 19 Feb 08
You should use attr_reader instead of attr_accessor if you want to write your own setter.
Post a comment