<?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()
{
}
}
?>