Location: PHPKode > scripts > PAX > pax/core/classes/PAX.class.php
<?php


/**
 * Page DocBlock definition
 * @package org.zadara.marius.pax
 */

/**
 * PAX class definition.
 * This is the main class for the program.
 * 
 * @author Marius Zadara <hide@address.com>
 * @category Classes
 * @copyright (C) 2008-2009 Marius Zadara
 * @license Free for non-comercial use
 * @package org.zadara.marius.pax
 * @final 
 * @see PAXObject
 * @see IPAX
 * @version 6.0
 * @since 6.0
 */
final class PAX extends PAXObject implements IPAX
{
	/**
	 * Prerequisites object.
	 *
	 * @access private
	 * @var config
	 */
	private $prerequisites;

	/**
	 * Validations object.
	 *
	 * @access private
	 * @var config
	 */	
	private $validations;

	/**
	 * Directories object.
	 *
	 * @access private
	 * @var config
	 */	
	private $directories;

	/**
	 * Filenames object.
	 *
	 * @access private
	 * @var config
	 */		
	private $filenames;
	
	/**
	 * Elements object.
	 *
	 * @access private
	 * @var config
	 */		
	private $elements;
	
	/**
	 * Configuration object.
	 *
	 * @access private
	 * @var config
	 */		
	private $config;
	
	/**
	 * Nodes array
	 *
	 * @access private
	 * @var array
	 */
	private $nodes;
	
	/**
	 * Namespaces array.
	 *
	 * @access private
	 * @var config
	 */		
	private $namespaces;
	
	/**
	 * Tags array.
	 *
	 * @access private
	 * @var config
	 */		
	private $tags;
	
	/**
	 * Attributes definitions array
	 *
	 * @access private
	 * @var array
	 */	
	private $attributesDefinitions;

	
	
	/**
	 * Instructions definitions.
	 *
	 * @access private
	 * @var config
	 */
	private $instructions;
	
	
	
	/**
	 * Variables definitions
	 *
	 * @access private
	 * @var config
	 */
	private $variables;
	
	
	/**
	 * Custom container for holding user own data.
	 * Must be an object 
	 * 
	 * @access private
	 * @var Object
	 */
	private $userObject;
	
	
	
	
	/**
	 * Class constructor.
	 *
	 * @access public
	 */
	public function __construct()
	{
		// init the parent first
		parent::__construct();
		
		// init the members
		$this->prerequisites = null;
		$this->validations = null;
		$this->directories = null;
		$this->filenames = null;
		$this->elements = null;
		$this->config = null;
		$this->nodes = null;
		$this->namespaces = null;
		$this->tags = null;
		$this->attributesDefinitions = null; 	
		$this->instructions = null;
		$this->variables = null;
		$this->userObject = null;			
	}

	
	/**
	 * Prerequisites setter.
	 *
	 * @access public
	 * @param config <b>$prerequisites</b> The prerequisites config object
	 * @return void
	 */
	public function setPrerequisites(&$prerequisites)
	{
		// validate the parameter
		if ($prerequisites instanceof Config)
			$this->prerequisites = $prerequisites;
	}

	/**
	 * Validations setter.
	 *
	 * @access public
	 * @param config <b>$validations</b> The validations config object
	 * @return void
	 */
	public function setValidations(&$validations)
	{
		// validate the parameter
		if ($validations instanceof Config)
			$this->validations = $validations;	
	}

	/**
	 * Directories setter.
	 *
	 * @access public
	 * @param config <b>$directories</b> The directories config object
	 * @return void
	 */	
	public function setDirectories(&$directories)	
	{
		// validate the parameter
		if ($directories instanceof Config)
			$this->directories = $directories;
	}
	
	/**
	 * Filenames setter.
	 *
	 * @access public
	 * @param config <b>$filenames</b> The filenames config object
	 * @return void
	 */	
	public function setFilenames(&$filenames)
	{
		if ($filenames instanceof Config)
			$this->filenames = $filenames;		
	}
	
	/**
	 * Elements setter.
	 *
	 * @access public
	 * @param config <b>$elements</b> The elements config object
	 * @return void
	 */	
	public function setElements(&$elements)
	{
		// validate the parameter
		if ($elements instanceof Config)
			$this->elements = $elements;
	}
	
	/**
	 * Configuration setter.
	 *
	 * @access public
	 * @param config <b>$config</b> The configuration object
	 * @return void
	 */
	public function setConfig(&$config)
	{
		// validate the parameter
		if ($config instanceof Config)
			$this->config = $config;
	}


	/**
	 * Instructions setter.
	 *
	 * @access public
	 * @param config <b>$instructions</b> The instructions object
	 * @return void
	 */
	public function setInstructions(&$instructions)
	{
		// validate the paramater
		if ($instructions instanceof Config)
			$this->instructions = $instructions;
	}
	
	
	/**
	 * Variables setter.
	 *
	 * @access public
	 * @param config $instructions The variabiles object
	 * @return void
	 */	
	public function setVariables(&$variables)
	{
		// validate the parameter
		if ($variables instanceof Config)
			$this->variables = $variables;
	}
	
	
	/**
	 * User object setter.
	 *
	 * @access public
	 * @param object <b>$userObject</b> The user object
	 * @return void
	 */
	public function setUserObject(&$userObject)
	{
		if (is_object($userObject))
			$this->userObject = $userObject;
	}
	
	
	
	/**
	 * Method to parse a file using its path.
	 * 
	 * @access public
	 * @param string <b>$path</b> The file path
	 * @return void
	 */
	public function parseFile($path)
	{
		// catch any exceptions
		try
		{
			// create a new file object and set the path
			$file = new File();
			$file->setPath($path);
			
			// validate the file - must exists
			if (!$file->exists())
				throw new PAXException(Messages::$MSG_004, 4);
				
			// validate the file - must be ordinary file
			if (!$file->isOrdinary())
				throw new PAXException(Messages::$MSG_001, 1);
				
			// validate the file - must be readable				
			if (!$file->isReadable())
				throw new PAXException(Messages::$MSG_005, 5);
				
			// get the file content, removing any extra spaces
			$content = trim($file->getContent());
				
			if ($content == "")
				throw new PAXException(Messages::$MSG_007, 7);
			
			// parse the file content
			$this->parseString($content);
					
			unset($file, $content);						
		}
		catch (PAXException $pe)
		{
			// if any exception, throw it further
			throw $pe;
		}
		catch (Exception $e)
		{
			// if any exception, throw it further			
			throw $e;
		}
	}

	/**
	 * Method to parse a string
	 *
	 * @access public
	 * @param string <b>$string</b> The string to be parsed
	 * @return void
	 */
	public function parseString($string)
	{
		// validate the string
		if (!is_string($string))
			throw new PAXException(Messages::$MSG_008, 8);
			
		// remove the extra-spaces
		$string = trim($string);
		
		if ($string == "")
			throw new PAXException(Messages::$MSG_007, 7);

		// catch any exceptions
		try 
		{
			// validate the environment
			$this->isValidEnvironment();
			
			// validate the string as XML
			$content = @simplexml_load_string($string);
			
			// if the string is not a valid XML,
			// throw a new exception
			if ($content === false)
				throw new PAXException(Messages::$MSG_011, 11);

			unset($content);	
				
			// parse the string
			$this->parse($string);
		}
		catch (PAXException $pe)
		{
			// if any exception, throw it further
			throw $pe;
		}
		catch (Exception $e)
		{
			// if any exception, throw it further
			throw $e;
		}
	}
	
	
	/**
	 * Method to check the environment.
	 * Throws exception in case of error
	 *
	 * @access private
	 * @return boolean
	 */
	private function isValidEnvironment()
	{		
		$environment = new Environment();
		
		// validate the simpleXML extension
		if ($this->validations->get('checkSimpleXML', true) === true)
		{
			$simpleXMLExtension = $this->prerequisites->get('simpleXMLModuleName', 'SimpleXML'); 
			
			if (!$environment->isExtensionLoaded($simpleXMLExtension))
			{
				if (!$environment->loadExtension($simpleXMLExtension))
					throw new PAXException(sprintf(Messages::$MSG_009, $simpleXMLExtension), 9);								
			}
		}

		// validate the xmlParser extension
		if ($this->validations->get('checkXMLParser', true) === true)
		{
			$XMLParserExtension = $this->prerequisites->get('xmlParserModuleName', 'xml'); 
			
			if (!$environment->isExtensionLoaded($XMLParserExtension))
			{
				if (!$environment->loadExtension($XMLParserExtension))
					throw new PAXException(sprintf(Messages::$MSG_012, $XMLParserExtension), 12);								
			}
		}
					
		// validate the current PHP version
		if ($this->validations->get('checkPHPVersion', true) === true)
		{
			$minPHPVersionRequired = $this->prerequisites->get('minPHPVersion', '5.2.0');

			if (!$environment->isMinimalPHP($minPHPVersionRequired))
				throw new PAXException(sprintf(Messages::$MSG_010, PHP_VERSION, $minPHPVersionRequired), 10);				
		}
				
		return true;		
	}

	
	/**
	 * Method to check the current model.
	 * Throws exception if error encountered.
	 * 
	 * @access private
	 * @return void
	 */
	private function checkModel()
	{
		// try to extract the model name from the current nodes
		$modelName = Model::extractModelName($this->nodes);
		
		// if error, throw a new exception
		if ($modelName === false)
			throw new PAXException(Messages::$MSG_016, 16);
		
		// check to see if the current model is disabled
		if ($this->isDisabledModel($modelName))
			throw new PAXException(sprintf(Messages::$MSG_017, $modelName), 17);
						
		try 
		{
			// update the directories using the current model name
			Model::updateDirectories($this->directories, $modelName);						
			
			// check the directories
			$this->checkDirectories();	
		}
		catch (PAXException $pe)
		{
			throw $pe;
		}
		catch (Exception $e)
		{
			throw $e;		
		}
	}
	
	
	/**
	 * Method to check the namespaces.
	 * 
	 * @access private
	 * @return void
	 */
	private function checkNamespaces()
	{
		try 
		{
			// get the used namespaces
			$usedNamespaces = Model::extractNamespaces($this->nodes, $this->elements);
			
			if (!is_null($usedNamespaces))
			{
				$definedNamespaces = Model::getDefinedNamespaces($this->nodes[0]['attributes'], $this->elements);
				$this->namespaces = Model::checkNamespaces($definedNamespaces, $usedNamespaces);
				
				// validate the namespaces
				Model::validateNamespaces($this->namespaces, $this->directories->get("namespaces", "namespaces"));
			}
			
			unset($usedNamespace, $definedNamespaces);
		}
		catch (PAXException $pe)
		{
			throw $pe;
		}
		catch (Exception $e)
		{
			throw $e;	
		}
	}
		
	
	/**
	 * Method to set the parser options.
	 *
	 * @access private
	 * @param config <b>$parser</b> The parser object
	 */
	private function setParserOptions(&$parser)
	{
		// get the xml parser options
		$xmlParserOptions = $this->config->get("XMLParserOptions");	
			
		// at least one option specified?
		if (!is_null($xmlParserOptions) && is_array($xmlParserOptions) && (sizeof($xmlParserOptions) > 0))
		{
			// parse the xml options list
			foreach ($xmlParserOptions as $option => $value)
			{
				// retain only valid options
				if
				( 
					($option != XML_OPTION_CASE_FOLDING) &&
					($option != XML_OPTION_SKIP_TAGSTART) &&
					($option != XML_OPTION_SKIP_WHITE)
				)
				continue;
				
				// retain only valid values
				if (($value != 0) && ($value != 1))
					continue;	
				
				// try to set the current option
				// if failed, throw exception
				if (@xml_parser_set_option($parser, $option, $value) === false)
					throw new PAXException(sprintf(Messages::$MSG_015, $option), 15);										
			}			
		}
		
		unset($xmlParserOptions, $option, $value);					
	}
	

	
	
	/**
	 * Main parse method.
	 * This method will interpret the xml content.
	 * 
	 * @access private
	 * @param string $content
	 * @return void
	 */
	private function parse($content)
	{
		// try to create the xml parser object
		$parser = @xml_parser_create();
		
		// validate the parser object
		if (!is_resource($parser))
			throw new PAXException(Messages::$MSG_013, 13);
		
		// set the parser options
		$this->setParserOptions($parser);
			
		// try to translate the content into arrays
		// if failed, throw new exception
		if (@xml_parse_into_struct($parser, $content, $this->nodes) == 0)
			throw new PAXException(Messages::$MSG_014, 14);
		
		// clear the parser, not needed anymore
		@xml_parser_free($parser);

		// try to do various tasks
		try 
		{			
			$this->checkModel();		
						
			$this->checkNamespaces();
						
			Model::filterTags($this->config, $this->elements, $this->directories, $this->filenames, $this->nodes, $this->namespaces, ":");			
												
			Model::filterTagAttributes($this->config, $this->elements, $this->directories, $this->filenames, $this->nodes, $this->attributesDefinitions);
			
			Model::compileInstructions($this->config, $this->elements, $this->variables, $this->instructions, $this->nodes);
			
			Model::filterTagAttributesValue($this->config, $this->elements, $this->directories, $this->filenames, $this->nodes, $this->attributesDefinitions);	
			
			Model::filterTagContent($this->config, $this->elements, $this->nodes);

			$this->run();	
			
			
			
		}
		catch (PAXException $pe)
		{
			// if any exception, throw if further
			throw $pe;
		}
		catch (Exception $e)
		{
			// if any exception, throw it further
			throw $e;
		}		
	}	
	
	
	
	/**
	 * Method to check if a model is disabled.
	 *
	 * @access private
	 * @param string <b>$model</b> The name of the model
	 * @return boolean
	 */
	private function isDisabledModel($model)
	{
		// default result
		$isDisabled = false;

		// validate the model?
		if ($this->validations->get("checkDisabledModels", true) === true)
		{
			// get the disabled models
			$disabledModels = $this->config->get("disabledModels", null); 
			
			if (!is_null($disabledModels))						
			{
				// check into an array
				if (is_array($disabledModels) && (sizeof($disabledModels) > 0) && in_array($model, $disabledModels))
					$isDisabled = true;
					
				// check as a string
				if (is_string($disabledModels) && (trim($disabledModels) == $model))
					$isDisabled = true;
			}
			
			unset($disabledModels);
		}

		return $isDisabled;		
	}

	
	/**
	 * Method to check the directories.
	 *
	 * @access private
	 * @return void
	 */
	private function checkDirectories()
	{		
		// set the directories to be checked
		$keys = array('models', 'definitions', 'implementations', 'libraries', 'extra', 'namespaces');
		$resource = new Dir();
				
		// parse the directories list
		foreach ($keys as $key)
		{
			$path = trim($this->directories->get($key, $key));
			
			if ($path == "")
				throw new PAXException(sprintf(Messages::$MSG_018, $path), 18);

			$resource->setPath($path);
			
			// check the path - must exists
			if (!$resource->exists())
				throw new PAXException(sprintf(Messages::$MSG_019, $path), 19);
				
			// check the path - must be readable				
			if (!$resource->isReadable())
				throw new PAXException(sprintf(Messages::$MSG_020, $path), 20);	
		}		
	}

	
	
	/**
	 * Main method of the parser.
	 * This will parse the nodes list and interpret them.
	 * Throws exception in case of error
	 *
	 * @access private
	 * @return void
	 */
	private function run()
	{
		$file = new File();
		
		
		// (try) to include the libraries for this model
		$loaderPath = $this->directories->get("libraries").$this->filenames->get("modelLibrariesLoader", "_addLibraries.php"); 
		$file->setPath($loaderPath);
		
		// the file must exists
		if (!$file->exists())
			throw new PAXException(sprintf(Messages::$MSG_043), 43);
		
		// be readable
		if (!$file->isReadable())
			throw new PAXException(sprintf(Messages::$MSG_044), 44);
			
		// and ordinary
		if (!$file->isOrdinary())
			throw new PAXException(sprintf(Messages::$MSG_045), 45);
			
		// load the libraries
		require_once $loaderPath;
		
		

		// (try) to include the extra classes/functions for this model
		$loaderPath = $this->directories->get("extra").$this->filenames->get("modelExtraLoader", "_addExtra.php"); 
		$file->setPath($loaderPath);
		
		// the file must exists
		if (!$file->exists())
			throw new PAXException(sprintf(Messages::$MSG_046), 46);
		
		// be readable
		if (!$file->isReadable())
			throw new PAXException(sprintf(Messages::$MSG_047), 47);
			
		// and ordinary
		if (!$file->isOrdinary())
			throw new PAXException(sprintf(Messages::$MSG_048), 48);
			
		// load the extra
		require_once $loaderPath;
		
		// clear the file object - not needed anymore
		unset($file);
		
		
		// create a hash-map with the instances of the nodes
		// this will help pax not to instanciate the same class more than once
		// improving performance
		$nodes = new Config();		
		
		
		// catch any exceptions might occur
		try 
		{
		    // parse the nodes list
			foreach ($this->nodes as $nodeIndex => $nodeData)
			{
			    // skip invalid nodes
				if ($nodeData === false)
					continue;

				// create the instance in the first time
				if (!$nodes->keyExists($nodeData['tag']))
				{				    
					$tempObject = new $nodeData['tag'];
					
					// set the refernce to the user object
					$tempObject->setUserObject($this->userObject);

					
					// save the instance
					$nodes->set($nodeData['tag'], $tempObject);
					unset($tempObject);
				}
	
				// use the instance previously created
				$currentNode = &$nodes->get($nodeData['tag']);
				
				// update the attributes				
				if (isset($nodeData['attributes']) && is_array($nodeData['attributes']))
				{
				    // set the new attributes
					$currentNode->setAttributes($nodeData['attributes']);
				}
				else
				{
				    // if no attributes, clear the old ones
				    $currentNode->resetAttributes();
				}
				    
				
				// update the content
				if (isset($nodeData['value']))
				{
				    // set the new content of the node
				    $currentNode->setContent($nodeData['value']);
				}
				else
				{
				    // if no content, clear the old one
				    $currentNode->resetContent();
				}
				    
				    
								
				// call the custom method according to the node type
				switch ($nodeData['type']) 
				{
					case "open":
						$currentNode->onOpen();
					break;
					
					case "close":
						$currentNode->onClose();
					break;
					
					case "complete":
						$currentNode->onComplete();
					break;
					
					default:
						;
					break;
				}
			}	
		}
		catch (Exception $e)
		{
		    // if any exception, throw it futher
			throw $e;
		}

		// clear the list with the node's instances
		unset($nodes);
	}
	
	
	/**
	 * Class destructor.
	 * 
	 * @access private
	 */
	function __destruct()
	{
	}
}


?>
Return current item: PAX