Just like in JavaScript, many PHP developers find themselves in situations that warrant read-only properties. Unfortunately, the most common practice is to use a pseudo getter (again, like JavaScript) that’s more hack than it is design.
Again, I’m talking about things like [cci]$object->getProperty()[/cci], which works, but is poor design.
Like its JavaScript cousin, you’ve probably come across an object that looks something like this:
odometer += abs( $miles );
}
/**
* Get a protected odometer reading
*
* @return float
*/
public function getOdometer() {
return $this->odometer;
}
}
$subaru = new Car();
$subaru->drive( 500 );
echo $subaru->getOdometer(); // 500
Like the JavaScript equivalent, this works, but it’s an ugly, unituitive hack. Don’t use this.
The Alternative
If you have build a class in PHP 5, you’ve used what’s called a “magic method” – namely, [cci]__construct()[/cci]. A magic method is one that’s not directly exposed on an object instance, but which is invoked in certain specific situations instead. For example, invoking [cci]new Car()[/cci] would invoke [cci]Car::__construct()[/cci] if such a method exists.
As it so happens, PHP includes magic methods for both getters and setters of properties. Build these into your objects, and you can completely control the behavior of certain properties.
The code below is the same as before, but refactored to expose a read-only property on our object:
odometer += abs( $miles );
}
/**
* Magic getter for our object.
*
* @param string $field
*
* @throws Exception Throws an exception if the field is invalid.
*
* @return mixed
*/
public function __get( $field ) {
switch( $field ) {
case 'odometer':
return $this->odometer;
default:
throw new Exception( 'Invalid property: ' . $field );
}
}
}
$subaru = new Car();
$subaru->drive( 500 );
echo $subaru->odometer; // 500
$subaru->odometer = 0; // Fatal error: Cannot access protected property
echo $subaru->odometer; // 500
The [cci]@property-read[/cci] annotation in our class’ docblock will tell your IDE that the class has a read-only property. If you depend on things like inline autocomplete, this added documentation can be a lifesaver during development!
Other Uses
Magic getters and setters can also help adjust an object’s internal state when properties are read or set. As I noted in the JavaScript walkthrough yesterday, this is an extremely rare use case and obscures the functionality of your object and application. Don’t use it unless you know exactly what you’re doing and understand the potential consequences:
speed );
return implode( ',', $this->position );
case 'speed':
unset( $this->position);
return $this->speed;
default:
throw new Exception( 'Invalid property: ' . $field );
}
}
}
$electron = new Fermion();
echo $electron->position; // 50,35
echo $electron->speed; // Warning: Undefined property
$electron = new Fermion();
echo $electron->speed; // 75
echo $electron->position; // Warning: Undefined property