Location: PHPKode > scripts > Lightweight Club Calendar > lc-calendar-0.9.4/core/lcc_driver.class.php
<?php
/**
* Class file for generic Driver
*
* PHP Version 4
*
* @author <hide@address.com>
* @copyright Copyright (c) 2006, Benedikt Hallinger
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, 51 Franklin St, Fifth Floor,
* Boston, MA  02110-1301  USA
*/

/**
* Driver base class
*
* All core driver class files extend this class. It serves a common API used by
* driver loading and such as well as some common methods for validating fields
* and fetching/securing/processing data.
*/
class LCC_Driver
{
	/**
	* LCC_Core will store a reference of itself here on driver loading
	*
	* @var LCC_Core
	*/
	var $lcc_core = null;
	
	/**
	* Field Security (custom fields)
	*
	* This array holds the security parameters checked by LCC_Core
	* for fields that are to be read/set.
	* All fields that are requested from the selected Design must be defined
	* here, otherwise they will not be read from the Driver.
	* If fields are to be stored, the data entered must be matched by a
	* regular expression defined here.
	* Not all fields of an drivertype are required to be mandatory or writable.
	*
	* Pleade note, that date/time fields must support to a format usable by strtotime()!
	*
	* Actually not all fields are listed here. LCC uses some internal fields
	* that are automatically provided to the view but can never be written
	* since it is metadata generated from LCC_Core.
	*
	* The key is the name of the field as it is requested from the design
	* (e.g. $_POST['foobar'] is 'foobar' here). The key holds a associative array
	* containing the possible LCC-Settings which are:
	*  - regex         Regular expression that must match if storing data
	*  - mandatory     True or False; says if the field must be filled
	*                  to allow modification / addition of a field
	*  - writable      True or False
	*  - html          True or False, if True, then output will be passed through htmlentities()
	*
	* Each specific core driver class overwrites this with real values.
	*
	* @access private
	* @see setFieldSecurity(), checkField(), checkMandatory()
	* @var array
	*/
	var $_allowedFields = array();
	
	/**
	* Fields managed by LCC internally (metadata)
	*
	* This is to define the fields used by LCC internally so the core
	* can ensure no undefined data is read from the driver.
	* Additionally this is used to ensure this fields cannot be
	* defined as allowed fields
	*
	* Each specific core driver class overwrites this with real values.
	*
	* @access private
	* @var array
	*/
	var $_metaFields = array();

	/**
	* Methods the class must provide in order to be compatible with the core.
	*
	* Each specific core driver class overwrites this with real values.
	*
	* @var array
	* @access private
	*/
	var $_needed_methods = array();
	
	
	/**
	* Add or modify allowed driver fields
	*
	* All fields that are requested from the selected Design must be defined
	* here, otherwise they will not be read from the Driver.
	* If fields are to be stored, the data entered for the field
	* must be matched by a regular expression defined here.
	* Not all fields of an driver are required to be mandatory or writable.
	*
	* Here you can tune exisiting driver fields or add new ones to LCC.
	* It is possible to overwrite only a specific part of a existing field.
	*
	* The following field parameters exist:
	*   - regex:       (string) Regular expression that must match if storing a field
	*   - mandatory:   (bool) Is the field mandatory for adding/modifying a dataset to the backend (e.g. a new event)?
	*   - writable:    (bool) Is the field writable or read-only?
	*   - html:        (bool) Is HTML allowed? (if not, output will be passed through htmlentities())
	* Some Examples:
	* <code>
	* setFieldSecurity('myField', array('regex' => '/\d\d/', 'mandatory' => true));  // initially add a custom field (THIS WILL FAIL since not all parameters are given)
	* setFieldSecurity('myField', array('regex' => '/\d\d/', 'mandatory' => false, 'writable' => true,  'html' => false)); // initially add a custom field (This will succeed because the above error is fixed)
	* setFieldSecurity('myField', array('regex' => '/\d+/',  'mandatory' => true));  // modify mandatory flag and regex
	* </code>
	*
	* @param string $field      Name of the field
	* @param array  $param      Parameters of that field
	* @see $_allowedFields, checkfield()
	*/
	function setFieldSecurity($field, $params)
	{
		// Here we define the allowed parameters and a function that is called to ckeck
		$possible_parameters = array(
			'regex'     => 'is_string',
			'mandatory' => 'is_bool',
			'writable'  => 'is_bool',
			'html'      => 'is_bool'
		);
		
		if (!is_string($field) || strlen($field) == 0) die('<b>'.get_class($this).' setFieldSecurity() ERROR:</b> $field is not a string or empty');
		if (!is_array($params)) die('<b>'.get_class($this).' setFieldSecurity() ERROR:</b> Field parameters must be an array!');
		
		// Protect metadata fields:
		if (in_array($field, $this->_metaFields)) {
			die('<b>'.get_class($this).' setFieldSecurity() ERROR:</b> "'.$field.'" is reserved for internal use. Sorry!');
		}
		
		// check regular expression, if parameter given
		if ($params['regex'] !== null && preg_match($params['regex'], '') === false) die('<b>'.get_class($this).' setFieldSecurity() ERROR:</b> $regex seems not to be a valid regular expression!');
		
		// Check if we want to add a custom field (all parameters must be given then)
		if (!array_key_exists($field, $this->_allowedFields)) {
			// check if all params are here
			$params_not_set = $possible_parameters;
			foreach ($params as $key => $value) {
				if (array_key_exists($key, $params_not_set)) {
					unset($params_not_set[$key]);
				}
			}
			if (count($params_not_set) > 0) {
				die('<b>'.get_class($this).' setFieldSecurity() ERROR:</b> Not all field parameters for '.$field.' are given but all parameters are mandatory if adding a new field!');
			}
		}
		
		// modify or add data
		foreach ($params as $key => $value) {
			if (!array_key_exists($key, $possible_parameters)) {
				die('<b>'.get_class($this).' setFieldSecurity() ERROR:</b> Field parameter "'.$key.'" is not known!');
			} else {
				if (call_user_func ($possible_parameters[$key], $value)) {
					$this->_allowedFields[$field][$key] = $value;
				} else {
					die('<b>'.get_class($this).' setFieldSecurity() ERROR:</b> Field parameter "'.$key.'" has wrong type! ('.$possible_parameters[$key].'() failed)');
				}
			}
		}
	}
	
	/**
	* Add a user defined field to this driver
	*
	* This is a alias to {@link setFieldSecurity()}, so please see the documentation
	* of that method for more information on parameters.
	* However this is just an alias, you should use this method to add and the other only to
	* modify field parameters, so your code will be much more readable.
	*
	* @param string $field      Name of the field
	* @param array  $param      Parameters of that field (All required!)
	*/
	function addCustomField()
	{
		$args = func_get_args();
    	call_user_func_array( array(&$this, 'setFieldSecurity'), $args );
	}
	
	/**
	* Checks several rights of fields
	*
	* This function is used by the core to query the allowed fields.
	* The following checks are implemented:
	*  - 'mandatory'    See if this field is mandatory for the driver
	*  - 'writable'     See if this field is allowed to be modified
	*  - 'html'         Is HTML allowed?
	*  - 'validate'     Validate input (applies the regular expression)
	*
	* The following checks need additional parameters:
	*  - 'validate':    $parameter is the value to be checked against the defined regex
	*
	* @param string $field      Name of the field to check
	* @param string $check      Check to be performed
	* @param string $parameter  (optional) Parameter of the check
	* @see $_allowedFields, setFieldSecurity()
	*/
	function checkField($field, $check, $parameter = '')
	{
		if (array_key_exists($field, $this->_allowedFields) && is_string($field) && strlen($field) > 0) {
			switch ($check) {
				case 'mandatory':
				case 'writable':
				case 'html':
					$return = $this->_allowedFields[$field][$check];
				break;

				case 'validate':
					$return = $this->_validateField($field, $parameter); // call validation method
				break;
				default:
					die('<b>'.get_class($this).' checkField() ERROR:</b> The check "'.$check.'" is not implemented!');
			}
		} else {
			die('<b>'.get_class($this).' checkField() ERROR:</b> The requested field "'.$field.'" is not defined.');
		}

		return $return;
	}

	/**
	* Default method for validate action 'validate'
	*
	* Each driver base class amy override this method to provide a
	* custom way of field validation.
	*
	* Here, we look into the field definition of the driver and check the parameter,
	* which in this case is the fields value, using the regular expression defined
	* in the fields definition.
	*
	* This method gets called by {@link LCC_Driver::checkField()}
	*
	* @access private
	* @param string $field     The field we want to check
	* @param string $parameter The given parameter to match
	* @return boolean
	*/
	function _validateField($field, $parameter)
	{
		if ( strlen($parameter) > 0 ) {
			return @preg_match($this->_allowedFields[$field]['regex'], $parameter);
		}
		else
			// Default return is set to 'true' because of allowed empty fields.
			return true;
	}
	
	/**
	* This checks a given data array if all mandatory fields are set
	*
	* @param array $data    Associative array containing data ($fieldname => $value)
	* @return array         Array containing field names that are mandatory but not set
	* @see $_allowedFields, setFieldSecurity()
	*/
	function checkMandatory($data)
	{
		// Build list with mandatory fields
		$mandatorys = array();
		foreach ($this->_allowedFields as $field => $permission) {
			if ($permission['mandatory']) {
				array_push($mandatorys, $field);
			}
		}
		
		// Build list with filled fields
		$filled_fields = array();
		foreach ($data as $field => $value) {
			if (strlen($value) > 0) {
				$filled_fields[$field] = true;
			}
		}

		// Compare and return
		return array_diff($mandatorys, array_keys($filled_fields));
	}
	
	/**
	* Set the drivers config
	*
	* With this method you can overwrite parts of the
	* drivers default config.
	*
	* @param array $config      parts of drivers config (key => value pairs)
	*/
	function setConfig($config = array())
	{
		if (!isset($this->config) || !is_array($this->config)) {
			die('<b>'.get_class($this).' ERROR:</b> Your driver "'.get_class($this).'" is missing its default configuration!<br>Make sure the default configuration is stored
			    in the class variable "$config" and is an array!');
		} else {
			$subsyntaxcheck = array(); // used to check subsyntax only once
			
			foreach ($config as $key => $value) {
				if (array_key_exists($key, $this->config)) {
					// Config schema check
					if (is_array($this->config[$key]) && !is_array($value)) {
						die('<b>'.get_class($this).' ERROR:</b> Configuration key "'.$key.'" is expected to be an array!');
					}
					if (!is_array($this->config[$key]) && is_array($value)) {
						die('<b>'.get_class($this).' ERROR:</b> Configuration key "'.$key.'" is expected to be a string!');
					}
					
					// Deal with sub arrays, since some driver may use sub arrays
					// Supported are only two dimensional keys (e.g. $foo['bar']['baz'] = 'blabla') in total!
					// If the default config of the driver is an empty array, we assume dynamic keys
					if (is_array($this->config[$key]) && is_array($value)) {
						foreach ($value as $subkey => $subvalue) {
							if (!array_key_exists($key, $subsyntaxcheck)) {
								// Check this key only the first time, otherwise we run into trouble because new values are already added thus array is not empty
								$subsyntaxcheck[$key] = is_array($this->config[$key]) && empty($this->config[$key]);
							}
							if (array_key_exists($subkey, $this->config[$key]) || $subsyntaxcheck[$key]) {
								$this->config[$key][$subkey] = $subvalue;
							} else {
								echo "<pre>";var_dump($this->config[$key], $subvalue);
								die('<b>'.get_class($this).' ERROR:</b> Your driver "'.get_class($this).'" does not know the subconfiguration key "'.$subkey.'"!');
							}
						}
					} else {
						$this->config[$key] = $value;
					}
				} else {
					die('<b>'.get_class($this).' ERROR:</b> Your driver "'.get_class($this).'" does not know the configuration key "'.$key.'"!');
				}
			}
		}
	}
	
	/**
	* Get data from this driver and apply security settings for the fields
	*
	* This fetches all the drivers data from the backend and applies the settings of
	* the field defined in {@link $_allowedFields} to them.
	* Additionally, the meta fields defined in {@link $_metaFields} are allowed to pass.
	*
	* If the driver method getData() returns false, we assume the data doesn't exist
	* so we return false.
	*
	* The specific driver type implement methods that assign additional metadata,
	* so this method is not called directly from the core.
	*
	* @param int $id   ID of the data; is passed to $driver->getData()
	* @return array|false    Secured fields of the driver or false, if no data does exist
	*/
	function getSecuredData($id)
	{
		$secured_data = $this->getData($id);
		if ($secured_data === false || !is_array($secured_data)) {
			return false;  // return false because data doesn't exist
		} else {
			foreach ($secured_data as $field => $value) {
				if (in_array($field, $this->_metaFields)) {
					// let this field pass because it is internally used
					// [TODO] we should make some checks on the fields...
					// [TODO] maybe we should implement a getMetadata() method that handles internal fields
					$secured_data[$field] = htmlentities($value, ENT_QUOTES);  // secure html
				} elseif ($this->checkField($field, 'validate', $value)) {
					if ($this->checkField($field, 'html')) {
						$secured_data[$field] = $value;  // return as is
					} else {
						$secured_data[$field] = htmlentities($value, ENT_QUOTES);  // secure html
					}
				} else {
					// We drop a die here since such data should NEVER have entered the backend.
					// Input errors in fields in edit view are supposed to handle errors themself, if we
					// encounter a error here we can do nothing. Okay, maybe we can just
					// return a empty value for that field, but in this case the error
					// will never be recognized.
					die('<b>'.get_class($this).' getSecuredData() SECURITY ERROR:</b> The validation for format of "'.$field.'" failed!');
				}
			}
			if (count($secured_data) == 0) {
				$false = false;
				return $false;
			} else {
				return $secured_data;
			}
		}
	}
	
	/**
	* Get and secure data, run post processor on driver
	*
	* This returns the secured data of the driver and runs a hook on the specific driver,
	* so it can assign meta data or do some post processing
	*
	* @param int $id         ID of the event
	* @return array|false    Array of all data (fields + meta) or false if no event exists
	*/
	function getAllData($id)
	{
		// Get secured data
		$secured_data = $this->getSecuredData($id);
		if (!$secured_data) {
			$secured_data = false;
		} else {
			// Run post processor on driver
			$secured_data = $this->_getDataPostprocessor($secured_data, $id);
		}

		return $secured_data;
	}


	/**
	* Check if the extending class is compatible to the core
	*
	* Called by the core to check if this driver has everything it needs and dies if not
	*
	* @access private
	*/
	function _checkDriver()
	{
		$defined_methods = get_class_methods(get_class($this));
		// PHP4/PHP5 compliance: convert defined methods to lowercase
		foreach ($defined_methods as $key => $method) {
			$defined_methods[$key] = strtolower($method);
		}
		
		foreach ($this->_needed_methods as $method) {
			if ( !in_array(strtolower($method), $defined_methods) ) {
				die('<b>LCC driver loading error:</b> Your driver "' . get_class($this) . '" is missing the method "' . $method . '" !');
			}
		}

		// checks on class variables
		$defined_vars = get_class_vars(get_class($this));
		if ( !is_array($defined_vars['config']) ) {
			die('<b>LCC driver loading error:</b> Your driver "' . get_class($this) . '" is missing its default configuration array "$config" !');
		}
		
		// For upward compatibility: Call custom check hook on driver
		// $this->_checkDriverExtension();
	}

}
?>
Return current item: Lightweight Club Calendar