<?php
/**
* Page DocBlock definition
* @package org.zadara.marius.pax
*/
/**
* Attributes class definition.
* This class is responsible with all the work done on tag's attributes
*
* @author Marius Zadara <marius@zadara.org>
* @category Classes
* @copyright (C) 2008-2009 Marius Zadara
* @license Free for non-comercial use
* @package org.zadara.marius.pax
* @final
* @see File
* @see IAttributes
* @version 6.0
* @since 5.0
*/
final class Attributes extends File implements IAttributes
{
/**
* The atributes file definition content.
*
* @access private
* @var string
*/
private $attributesFileContent;
/**
* Array with all the attributes for a tag.
*
* @access private
* @var array
*/
private $tagAttributes;
/**
* Array with the namespaces.
*
* @access private
* @var array
*/
private $registeredNamespaces;
/**
* Class constructor.
*
* @access public
* @see PAXObject::__construct()
*/
public function __construct()
{
// init the parent
parent::__construct();
// init the class members
$this->tagAttributes = null;
$this->attributesFileContent = null;
$this->registeredNamespaces = null;
}
/**
* Method to get the tag attributes from the definition file.
* Throws exception if error encountered.
*
* @param string <b>$rootTag</b> The attributes definition root tag
* @param string <b>$tag</b> The searched tag
* @param string <b>$paxNsSeparator</b> The namespaces separator used in tag's name
* @param string <b>$namespaceURL</b> The name used in defining tags from other namespaces
* @return Array if the tag has attributes or false if no attribute found
*/
public function getTagAttributes($rootTag, $tag, $paxNsSeparator, $namespaceURL)
{
// if already seached for the current tag's attributes
// return them
if (isset($this->tagAttributes[$tag]))
return $this->tagAttributes[$tag];
// try to load the content for the first time
if (!is_object($this->attributesFileContent))
{
try
{
// get the content as xml from the file
$this->attributesFileContent = @simplexml_load_file($this->path);
// if failed, throw exception
if ($this->attributesFileContent === false)
throw new PAXException(sprintf(Messages::$MSG_037, $this->path), 37);
}
catch (PAXException $pe)
{
// if exception caught, send it further
throw $pe;
}
catch (Exception $e)
{
// if exception caught, send it further
throw new PAXException($e->getMessage());
}
}
// get the separator position inside the tag's name
$paxNsSepPos = strpos($tag, $paxNsSeparator);
if ($paxNsSepPos !== false)
{
// get the namespace
$namespace = substr($tag, 0, $paxNsSepPos);
// init the namespaces array the first time
if (!is_array($this->registeredNamespaces))
$this->registeredNamespaces = array();
// register the namespace
if (!in_array($namespace, $this->registeredNamespaces))
{
if ($this->attributesFileContent->registerXPathNamespace($namespace, sprintf("%s%s", $namespaceURL, $namespace)))
array_push($this->registeredNamespaces, $namespace);
else
throw new PAXException(sprintf(Messages::$MSG_041), 41);
}
}
// search the tag's attributes using XPath expressions
$result = @$this->attributesFileContent->xPath(sprintf("/%s/%s", $rootTag, $tag));
if ($result == false)
return false;
if (sizeof($result) == 0)
return false;
$result = $result[0];
$tagAttributes = array();
// get each attribute for the tag
foreach ($result as $attribute)
{
$attributeName = $attribute->getName();
// skip duplicate attributes
if (isset($tagAttributes[$attributeName]))
continue;
// get also each attribute for the current tag's attribute
$defs = $attribute->attributes();
$tagAttributes[$attributeName] = array();
foreach ($defs as $def => $val)
{
// cast to string
$def = (string) $def;
$val = (string) $val;
// skip duplicate attribute definition
if (!isset($tagAttributes[$attributeName][$def]))
$tagAttributes[$attributeName][$def] = $val;
}
if (sizeof($tagAttributes[$attributeName]) == 0)
$tagAttributes[$attributeName] = true;
}
// init the array with all the tags attributes
// only the first time
if (!is_array($this->tagAttributes))
$this->tagAttributes = array();
// save the attributes definitions
$this->tagAttributes[$tag] = $tagAttributes;
// return the attributes definitions
return $tagAttributes;
}
/**
* Method to filter the tag's attributes according to its definition.
* Throws exception if error encountered.
*
* @access public
* @static
* @param config <b>$config</b> The PAX config object
* @param config <b>$elements</b> The PAX elements object
* @param config <b>$directories</b> The PAX directories object
* @param config <b>$filenames</b> The PAX filenames object
* @param array <b>$nodes</b> The nodes array
* @param array <b>$attributesDefinitions</b> The attributes definitions array
* @return void
*/
public static function filterTagAttributes(&$config, $elements, $directories, $filenames, &$nodes, &$attributesDefinitions)
{
// catch any exception
try
{
// get the filtering status from the config
$filterAttributes = $config->get("filterTagAttributes", true);
// get the filtering attribute name
$filterAttributesName = $config->get("filterAttributesAttributeName", "filterAttributes");
// if the attribute has been found in the root,
// update the filtering status using the 'true texts' dictionary
if (isset($nodes[0]['attributes'][$filterAttributesName]))
$filterAttributes = Model::isInList($nodes[0]['attributes'][$filterAttributesName], $elements->get("trueTexts"));
// continue with the filtering?
if ($filterAttributes)
{
// allow no attributes definition?
$allowNoAttributes = $config->get("allowNoAttributes", false);
// if not ...
if (!$allowNoAttributes)
{
// validate the attributes definition file
// using the configuration
$attributes = new Attributes();
$attributesPath = $directories->get("definitions", "definitions");
$attributesPath .= $filenames->get("attributes", "attributes.xml");
$attributes->setPath($attributesPath);
if (!$attributes->exists())
throw new PAXException(sprintf(Messages::$MSG_034, $tagsPath), 34);
if (!$attributes->isOrdinary())
throw new PAXException(sprintf(Messages::$MSG_035, $tagsPath), 35);
if (!$attributes->isReadable())
throw new PAXException(sprintf(Messages::$MSG_036, $tagsPath), 36);
// get extra informations
// usefull in attribute parsing
$rootTagAttributes = $config->get("attributesRoot", "attributes");
$mandatoryText = $elements->get("mandatoryText", "mandatory");
$defaultValueText = $elements->get("defaultValueText", "defaultValue");
$namespaceURL = $elements->get("namespaceURL", "http://pax.org/");
// parse the nodes list
foreach ($nodes as $index => $nodeData)
{
// if invalid node, skip
if ($nodeData === false)
continue;
// if the node type is close, skip
// (the closed nodes don't have attributes)
if ($nodeData['type'] == "close")
continue;
if (!isset($nodes[$index]['attributes']))
$nodes[$index]['attributes'] = array();
$paxNsSeparator = $elements->get("paxNsSeparator", ":");
// get the tag attributes according to the definition
$tagAttributes = $attributes->getTagAttributes($rootTagAttributes, $nodeData['tag'], $paxNsSeparator, $namespaceURL);
// if no attributes were defined,
// update the values and continue
if ($tagAttributes === false)
{
$nodes[$index]['attributes'] = false;
continue;
}
$attributesCount = sizeof($tagAttributes);
// if no attributes were defined,
// update the values and continue
if ($attributesCount == 0)
{
$nodes[$index]['attributes'] = false;
continue;
}
if (isset($nodeData['attributes']) && is_array($nodeData['attributes']))
{
// validate each attribute from the file according to the definition
foreach ($nodeData['attributes'] as $attributeName => $attributeValue)
{
if (stripos($attributeName, $paxNsSeparator) !== false)
{
unset($nodes[$index]['attributes'][$attributeName]);
continue;
}
// if not found in the definition, unset it
if (!isset($tagAttributes[$attributeName]))
unset($nodes[$index]['attributes'][$attributeName]);
}
}
// add the mandatory attributes with their's default values
foreach ($tagAttributes as $attributeName => $attributeData)
{
if (isset($nodes[$index]['attributes'][$attributeName]))
continue;
if (isset($attributeData[$mandatoryText]))
{
$isMandatory = Model::isInList($attributeData[$mandatoryText], $elements->get('trueTexts'));
if (!$isMandatory)
continue;
if (!isset($attributeData[$defaultValueText]))
continue;
if (!isset($nodes[$index]['attributes']))
$nodes[$index]['attributes'] = array();
$nodes[$index]['attributes'][$attributeName] = $attributeData[$defaultValueText];
}
}
// if at the end the attributes list is empty, set it to false
if ((isset($nodes[$index]['attributes'])) && (sizeof($nodes[$index]['attributes']) == 0))
$nodes[$index]['attributes'] = false;
if ($nodes[$index]['attributes'] === false)
continue;
if ($attributesDefinitions == null)
$attributesDefinitions = array();
$attributesDefinitions[$index] = array();
// clear the 'mandatory' and 'defaultValue' attributes from the definition
// and save the new attributes list for the current index
foreach ($nodes[$index]['attributes'] as $attributeName => $attributeValue)
{
$attributesDefinitions[$index][$attributeName] = $tagAttributes[$attributeName];
if (isset($attributesDefinitions[$index][$attributeName][$mandatoryText]))
unset($attributesDefinitions[$index][$attributeName][$mandatoryText]);
if (isset($attributesDefinitions[$index][$attributeName][$defaultValueText]))
unset($attributesDefinitions[$index][$attributeName][$defaultValueText]);
if (sizeof($attributesDefinitions[$index][$attributeName]) == 0)
unset($attributesDefinitions[$index][$attributeName]);
}
if (sizeof($attributesDefinitions[$index]) == 0)
unset($attributesDefinitions[$index]);
}
}
}
}
catch (PAXException $pe)
{
// in case of exception, send it further
throw $pe;
}
catch (Exception $e)
{
// in case of exception, send it further
throw $e;
}
}
/**
* Method to filter tag attribute's value.
* Throws exception if error encountered.
*
* @param config <b>$config</b> The PAX config
* @param config <b>$elements</b> The PAX elements
* @param config <b>$directories</b> The PAX directories
* @param config <b>$filenames</b> The PAX filenames
* @param array <b>$nodes</b> The list of nodes
* @param array <b>$attributesDefinitions</b> Attributes definitions
* @return void
*/
public static function filterTagAttributesValue(&$config, $elements, $directories, $filenames, &$nodes, &$attributesDefinitions)
{
// catch any exception
try
{
// get the filtering status from the config
$filterAttributesValue = $config->get("filterTagAttributesValues", true);
// get the filtering attribute name
$filterAttributesValueTagName = $config->get("filterAttributesValueAttributeName", "filterAttributesValue");
// update the filtering value if the attribute has been found
if (isset($nodes[0]['attributes'][$filterAttributesValueTagName]))
$filterAttributesValue = Model::isInList($nodes[0]['attributes'][$filterAttributesValueTagName], $elements->get("trueTexts"));
// continue with the filtering?
if ($filterAttributesValue)
{
// there are some attributes definitions?
if (is_array($attributesDefinitions) && (sizeof($attributesDefinitions) > 0))
{
// get the attribute value validation name
$attributeValueValidationMethodName = $elements->get("attrbuteValueValidationMethodName", "validateAttributeValue_[ATTRIBUTE]");
$paxNsSeparator = $elements->get("paxNsSeparator", ":");
// parse the nodes list
foreach ($nodes as $index => $nodeData)
{
// skip the invalid nodes
if ($nodeData === false)
continue;
// skip the closed nodes
if ($nodeData['type'] == 'close')
continue;
if (!isset($nodeData['attributes']))
continue;
// skip the nodes without any attributes
if ($nodeData['attributes'] === false)
continue;
// skip the nodes without any attributes definitions
if (!isset($attributesDefinitions[$index]))
continue;
$paxNsSepPos = strpos($nodeData['tag'], $paxNsSeparator);
// get the node name either from the a namespace or local
if ($paxNsSepPos !== false)
$currentNodeName = substr($nodeData['tag'], $paxNsSepPos + 1);
else
$currentNodeName = $nodeData['tag'];
// beautify the node name
$currentNodeName = ucfirst($currentNodeName);
// at this point, the class should already exists
// (loaded at the moment of filtering tags)
if (!class_exists($currentNodeName))
throw new PAXException(sprintf(Messages::$MSG_038, $currentNodeName), 38);
// validate each attribute
foreach ($nodeData['attributes'] as $attributeName => $attributeValue)
{
// skip the attributes without any definitions
if (!isset($attributesDefinitions[$index][$attributeName]))
continue;
// update the validation method name
$validationMethodName = str_ireplace("[ATTRIBUTE]", strtoupper($attributeName), $attributeValueValidationMethodName);
// seach the method inside the current class
// if not found, skip filtering for the current attribute
if (!method_exists($currentNodeName, $validationMethodName))
continue;
// create the evaluation string
$evalString = sprintf("\$newAttributeValue = %s::%s('%s', \$attributesDefinitions[\$index][\$attributeName]);", $currentNodeName, $validationMethodName, $attributeValue);
// try to evaluate the string
// if failed, thow exception
if (@eval($evalString) === false)
throw new PAXException(sprintf(Messages::$MSG_039, $currentNodeName, $attributeName, $nodeData['value']), 39);
if (is_null($newAttributeValue))
{
// if the result of validation method is null,
// unset the attribute
unset($nodes[$index]['attributes'][$attributeName]);
}
else
{
// update the attribute value with the new one
$nodes[$index]['attributes'][$attributeName] = $newAttributeValue;
}
}
// if at the end, the node has no attributes,
// update the record
if (sizeof($nodes[$index]['attributes']) == 0)
$nodes[$index]['attributes'] = false;
}
}
}
}
catch (PAXException $pe)
{
// in case of exception, throw it further
throw $pe;
}
catch (Exception $e)
{
// in case of exception, throw it further
throw $e;
}
}
/**
* Method to compile the instructions
*
* @access public
* @static
* @param config <b>$instructions</b> The PAX config
* @param array </b>$nodes</b> The nodes list
* @return void
*/
public static function compileInstructions(&$instructions, &$nodes)
{
// validate the instructions
if (is_null($instructions))
return;
// create the pattern
$pattern = new Pattern();
$instructionExtractor = new InstructionExtractor();
$instructionEvaluator = new InstructionEvaluator();
$currentInstructions = array();
$startDelimiter = $instructions->get("startDelimiter", "!#");
$endDelimiter = $instructions->get("endDelimiter", "#!");
// parse each node
foreach ($nodes as $nodeIndex => $nodeData)
{
if ($nodeData === false)
continue;
if (!isset($nodeData['attributes']))
continue;
if ($nodeData['attributes'] === false)
continue;
foreach ($nodeData['attributes'] as $attributeName => $attributeValue)
{
try
{
// try to extract instructions from the value of the current attribute
$currentInstructions = $instructionExtractor->extract($attributeValue, $startDelimiter, $endDelimiter);
}
catch (PAXException $pe)
{
// if exception, throw it further
throw $pe;
}
catch (Exception $e)
{
// if exception, throw it further
throw $e;
}
// validate the instructions
if ($currentInstructions === false)
continue;
// compile each instruction
// and replace it by its value
foreach ($currentInstructions as $instruction)
{
$replacePattern = $pattern->makeInstructionReplacePattern($instruction, $startDelimiter, $endDelimiter);
$nodes[$nodeIndex]['attributes'][$attributeName] = InstructionReplacer::replace($replacePattern, $instructionEvaluator->evaluate($instruction), $nodes[$nodeIndex]['attributes'][$attributeName]);
}
}
}
}
/**
* Class destructor.
*
* @access public
*/
function __destruct()
{
}
}
?>
|