/ ** * Example of implementation of the Generic class * * Opportunities: * 1. Access the class parameters through get * set * methods (accessors / mutators) * Example: $ object-> setName ('Dima'); $ object-> getName (); * When changing the standard behavior of the method (adding additional logic) * you can simply define it in a class without changing the class interface. * 2. Supports built-in data validation with the ability to use or advance * certain data types (numeric, string, email, date, etc.) * or regular expressions. * 3. Implements the Lazy initialization pattern to initialize objects. * Allows you to load object parameters from the database (for example) not immediately upon initialization, * but only on the first request. * * Visible flaws: * 1. It is not possible to use autocompet in IDE, since the methods are not explicitly defined * * Pros: * 1. Allows avoiding duplication of code when defining set and get methods of the same type * 2. Reduces class code and centralizes parameter control. * 3. If necessary, you can extend the functionality. For example, adding callback functions * or forced data filtering. * 4. Eliminates the routine duties of checking incoming data, thereby * providing some level of security. * It does not give a 100% guarantee at all - do not forget about it. * 5. Does not make adjustments to the class interface. You can easily combine regular classes and classes. * inherit GenericObject with no visible differences for class clients. If necessary * You can easily refuse to use simply by redefining all the heters and seters. * 6. Simplifies working with classes, providing a convenient and natural way. * object initialization: * $ obj1 = new Class (123); * $ obj2 = new Class (array ( * 'param1' => 'value1', * 'param2' => 'value2') *)); * /
Implementation
abstract class GenericObject { / ** * Array containing all class parameters * It is necessary to override in each class heir * var array * / protected $ _params = array (); ')
/ ** * Execute flag and initialization parameter * / private $ _initParam = null , $ _alreadyInitiated = false ;
/ ** * Object initialization * * Depending on the parameters passed, initialize the object: * 1. passed in string | integer: remember the parameter and use it for initialization * data when you first access them (Lazy Initialization). * 2. passed to the array: cyclically retrieving data from the array, check if there are any such parameters * in the class, validate and add them to the object. * * param mixed $ property * / public function __construct ($ property = null ) { if ($ property === null ) return ;
if (is_scalar ($ property)) { $ this -> _ initParam = $ property; } elseif (is_array ($ property)) { $ this -> _ alreadyInitiated = true ; $ this -> _ setParams ($ property); } }
/ ** * The function initializes the class object. * Should be redefined in each class heir * * param mixed $ initParam - the key to get information about the object * / protected function _init ($ initParam) { }
/ ** * Interception of a call of a nonexistent method * If the name of the called method starts with get or set - we find the corresponding * parameter and return or assign a new value to it * / public function __call ($ name, array $ args) { if (strlen ($ name)> 3) { $ paramName = strtolower ($ name {3}). substr ($ name, 4); switch (substr ($ name, 0, 3)) { case 'get' : return $ this -> _ getParam ($ paramName); case 'set' : if (empty ($ args)) { throw new GenericObject_Exception ( 'No value specified for assignment to parameter' ); } if ($ this -> _ setParam ($ paramName, $ args [0])) { return $ this ; } } } throw new GenericObject_Exception ( 'The missing class method was called ' . __CLASS__. '::' . $ name. '()' ); }
/ ** * Assigning a value to a parameter with pre-validation * / protected function _setParam ($ name, $ value) { if (! array_key_exists ($ name, $ this -> _ params)) { return false ; }
/ ** * Validation of values if the type or regular expression to be specified is specified. * We will not consider the class responsible for validation. * It is only important to know that it accepts the value, type and, if any, regular expression. * And returns the result of validation. * / if ($ value! == null && ($ type! == null || regex! == null )) { if (! Validate :: check ($ value, $ type, $ regex)) { if (isset ($ this -> _ params [$ name] [ 'message' ])) { throw new Validate_Exception ($ this -> _ params [$ name] [ 'message' ]); } throw new Validate_Exception ( 'Invalid parameter' . $ name. '(' . ($ type? $ type: $ regex). ')' ); } }
/ ** * Returns values * If the parameter value is not defined - the initialization method is called. * If the parameter value did not appear even after initialization, it returns null. * / protected function _getParam ($ name) { if (! $ this -> _ issetParam ($ name)) { $ this -> _ callInitMethod (); } return $ this -> _ issetParam ($ name)? $ this -> _ params [$ name] [ 'value' ]: null ; }
/ ** * Setting and validation of parameters from associative array * * param array $ params = array ('paramName' => 'value', ...) * / protected function _setParams (array $ params) { foreach ($ params as $ name => $ value) { $ this -> _ setParam ($ name, $ value); } }
/ ** * Check for the presence of the set value of the parameter * return bool * / protected function _issetParam ($ paramName) { return isset ($ this -> _ params [$ paramName] [ 'value' ]); }
/ ** * Call the initialization function * Initialization occurs only once and only when available. * initialization parameter (for example, identifier of the record in the database) * / private function _callInitMethod () { if ($ this -> _ alreadyInitiated === true || $ this -> _ initParam === null ) { return ; } $ this -> _ alreadyInitiated = true ; $ this -> _ init ($ this -> _ initParam); } }
protected function _init ($ customerId) { // data is obtained, for example, from the database $ params = array ( 'id' => '777' , 'name' => 'Jon' , 'email' => 'jon.black@gmail.com' , 'phone' => '123-45-67' , 'address' => 'Green street, 3/1' , ); $ this -> _ setParams ($ params); }
/ ** * We can explicitly override any method to add additional business logic. * At the same time, we do not need to change the class interface. * / public function setEmail ($ email) { if (! $ this -> _ checkIsUniqCustomerEmail ($ email)) { throw Validate_Exception ( 'Customer with the same email address already exists in the database!' ); } $ this -> _ setParam ( 'email' , $ email); }
/ ** * Implement the process of saving object data. * If id is specified - update, if not - insertable, and assign a new identifier to the object * / public function save () { if ($ this -> _ issetParam ( 'id' )) { $ this -> _ update (); } else { $ newId = $ this -> _ insert (); $ this -> _ setParam ( 'id' , $ newId); } } }
// At the time of initialization, the database is not accessed. // We can, for example, create in this way a large number of objects // and use only those that are necessary for us, without performing a large number of queries to the database. $ customer1 = new Customer (123); $ customer2 = new Customer (777); $ customer3 = new Customer (444);
print $ customer2-> getName (); // jon
// Supports fluent interface , i.e. each setter returns a reference to the containing // its object, thereby allowing the following style of writing to be maintained: $ customer3-> setName ( 'Key' ) -> setEmail ( 'key@gmail.com' ) -> setPhone ( '555-55-55' ) -> save ();
// If we need to create a new class object, we can do this: $ customerNew = new Customer (array ( 'name' => $ _POST [ 'customerName' ], 'email' => $ _POST [ 'customerEmail' ], 'phone' => $ _POST [ 'customerPhone' ], 'address' => $ _POST [ customerAddress' ], ))
// let's say we have a save method that saves an object in the database $ customerNew-> save ();
// after that we can find out the id of the object just saved print $ customerNew-> getId ();
Post scriptum
This code does not claim to be the ultimate truth, but is only one of many examples of the implementation of such functionality. The author does not propose to use it everywhere, because sometimes this is simply not necessary (for example, when using ORM libraries). Constructive (!) Criticism is welcome.