What are abstract properties? There are abstract methods but the notion of an abstract property seems a bit ridiculous. Why so, you might ask! Well, the thing is, methods are declared & defined (not necessarily at same time). So you can declare a method without actually defining it (ie., no body, no set of commands which it will execute) and the same can be defined at a later stage. But there is no such thing as defining a property of a class; properties are always declared as containers of data which reserve a space in memory on class initialization.
As I was re-factoring data validation service in a project I’m working on (will open source it once first working draft is ready), I thought about making the $rules
property abstract. But then I realized the absurdity of my thought and the fact that PHP does not have such a concept, not even like what C# has. From what little I know of C#, it has a concept of abstract properties which are compiled into functions by the compiler. So if I were to do this:
public abstract class Foo {
//abstract property, only declaring a getter
public abstract string Bar {
get;
}
}
then Bar
would eventually be compiled as getBar()
(would’ve had setBar()
too if we had declared a setter). So while you think you’re using abstract properties, its just pseudo code for accessors and mutators (getters and setters if you prefer). To my knowledge, Java doesn’t have the concept of abstract properties. I think I read about Smalltalk having something similar but I’m not sure and we digress, so back to PHP if you will. 🙂
So why did I think of implementing abstract property in my validation service? The way my validation service is designed, it has a base abstract class which has only one method, validate()
, which accepts data and an array containing validation rules. Now each entity in the application has its own corresponding validation service which extends this base class and defines a set of validation rules unique to the entity it represents. To keep the naming consistent, its better to have $rules
property in all the children of the base class and this is where the notion of an abstract property seems plausible.
So how did I go about it? With a little bit of hocus pocus I should say:
abstract class Validator {
/**
* @var array An array containing names and types of abstract properties that must be implemented in child classes
*/
private $_abstract_properties = array(
'array' => array(
'rules',
),
);
final public function __construct() {
$this->_abstract_properties_existence_enforcer();
}
final protected function _abstract_properties_existence_enforcer() {
//check if the child has defined the abstract properties or not
$current_child = get_class( $this );
foreach ( $this->_abstract_properties as $type => $properties ) {
$count = count( $properties );
for ( $i = 0; $i < $count; $i++ ) {
if ( property_exists( $this, $properties[ $i ] ) && strtolower( gettype( $this->$properties[ $i ] ) ) == $type ) {
continue;
}
//property does not exist
$error = $current_child . ' class must define $' . $properties[ $i ] . ' property as ' . $type;
throw new \LogicException( $error );
}
}
unset( $error, $current_child );
}
public function validate( array $data ) {
//We know $this->rules exists because we have declared it as an abstract property
//above which all child classes need to define as an array
return Vendor\Library\Service\Validation::make( $data, $this->rules );
}
}
Any class that now extends Validator
would need to declare properties of same type as set in $_abstract_properties
. So for example, if we need an AuthValidator
then it would be like:
class AuthValidator extends Validator {
/**
* @var array An array containing validation rules for authentication
*/
public $rules = array(
'email' => array( 'required', 'email', 'exists:users' ),
'password' => array( 'required', 'alphanumeric_special', 'min:10' ),
);
}
And to use it all we need to do is:
class UserSessionController extends BaseController {
public function store() {
$validator = new Services\Validation\AuthValidator;
try {
$validator->validate( $_POST );
} catch ( \ValidationException $e ) {
//invalid data, handle response accordingly
}
}
}
I’ve set the constructor to be final
in Validator
as the children will have no need to have their own constructors but this is not mandatory for all implementations. If in your implementation child classes have their own constructors then the constructor here in base class shouldn’t be final
. In that case, one thing to remember will be to either put in parent::__construct()
call in child constructor(s) before anything else or call _abstract_properties_existence_enforcer()
. Its a bit of a hassle and in my opinion implementing a Singleton or Factory pattern (as per your need) is better in such a case which would avoid the oversight of failing to call _abstract_properties_existence_enforcer()
.
So there you have it, the notion of abstract properties in PHP realized. 😉
This seems like a scenario where PHP’s Java-like properties become apparent.
And correct me if I’m wrong: the difference between this method and a getter is a nuanced difference? e.g., this is a way where you can interact directly with the
$_abstract_properties
without using a defined getter ($foo = $this->_abstract_properties['array']['rules']
instead of$foo = Validator()->getProperty( 'array', 'rules' )
)?Yeah, PHP has had quite an influence from Java in last few years, not that its bad. Java has some cool stuff & like Igor Wiedler said in his talk at TakeOff 2013 – PHP is like a pirate, it takes the good stuff from everywhere! 😉
What I’ve shown here is not a getter, its an implementation of abstract properties. Like if you declare an abstract method then all child classes have to define that method, its not optional. Similarly what if you need all child classes to have certain properties defined? Since natively there’s no concept of abstract properties, this implementation would allow you to declare the property names with types and any child class of
Validator
here would need to define those properties with their respective types else PHP will throw aLogicException
. I’ve updated the post above with an example of how abstract properties can be of use (Validator::validate()
) and an example of child class with its usage. I hope that would clear the confusion. 🙂Actually, if you need to implement this (get a property of an object without explicitly calling a getter method) then you can always implement
__get()
in your class.