/**
* Шаблонизатор XML
*/
class XmlTpl
{
private static $_defTagArray = array('#name' => '', '#value' => '', '#attrs' => array(), '#type' => '');
/**
* Массив данных экземпляра шаблона
* @var array
*/
private $_data = array();
/**
*
* @var XmlGenerator
*/
private $_XmlGenerator;
/**
*
* @param string $rootName имя корневого элемента
*/
public function __construct($rootName = false)
{
$this->_XmlGenerator = new XmlGenerator($rootName);
}
/**
* Создаем переменную для последующей записи в XML структуру
* @param string $name Имя переменной (если не передан второй параметр, то первый должен быть массивом с типом 'raw')
* @param mixed $value Строка или массив значений
* @example (пример переменной $value)
* array(
* array('tone_check2' => 0),
* array('filling' => ''),
* array(array
* (
* '#name' => 'photo',
* '#attrs' => array
* (
* 'id' => 1512,'src' => '','w' => '','h' => '','type' => 1
* ),
* '#value' => array
* (
* array('url' => 'no_url'),
* array(array(
* '#name' => 'big',
* '#attrs' => array('src' => 1512,'format' => 1,)
* //'#value' - пустое
* )),
* array(array
* (
* '#name' => 'author',
* '#attrs' => array('id' => 3,),
* '#value' => 'alp'
* )),
* )
* )),
* 'date_end' => false,
* )
* @example raw-массив
* ('', array('#type' => 'raw', '#value' => 'value'))
*/
public function assign($name, $value)
{
if (self::_isValidTagname($name))
{
$this->_data = array_merge_recursive($this->_data, self::_convertArray(array($name =>$value)));
}
}
/**
* Конвертирует входящий массив исходя из его типа
* в массив вида @link self::$_defTagArray
* @param array $array входящий массив
* @param int $arrayNumInSet[OPTIONAL] порядковый номер массива
* (если передаётся несколько массивов тегов на одном уровне)
* @return array массив, содержащий в качестве значения массив вида @link self::$_defTagArray
* (array($arrayNumInSet => self::$_defTagArray))
*/
private static function _convertArray($array, $arrayNumInSet = 0)
{
//если просто значение - возвращаем его
if (!is_array($array))
return $array;
//заполняем массив значениями по умолчанию
$tagArray = self::$_defTagArray;
$arrayType = self::_getArrayType($array);
switch ($arrayType)
{
case 'simple':
$tagArray['#name'] = self::_getSimpleTagName($array);
$tagArray['#value'] = self::_getSimpleTagValue($array);
break;
case 'complex':
$tagArray['#name'] = self::_getComplexTagName($array);
$tagArray['#type'] = self::_getComplexTagType($array);
$tagArray['#attrs'] = self::_getComplexTagAttrs($array);
$tagArray['#value'] = self::_getComplexTagValue($array);
break;
}
return array($arrayNumInSet => $tagArray);
}
/**
* Возвращает тип переданного массива (тега).
* Мы можем задать массив одного из трёх типов: простой тег, сложный тег, массив тегов
* @param array $array тег
* return string|bool ('numeric', 'simple', 'complex')|false если не массив
*/
private static function _getArrayType($array)
{
if (!is_array($array) || empty ($array))
return false;
$firstKey = array_keys($array);
$firstKey = $firstKey[0];
if (is_int($firstKey))
return 'numeric';
if (array_key_exists($firstKey, self::$_defTagArray))
return 'complex';
return 'simple';
}
/**
* Возвращает значение тега (в виде массива) для простого тега
* @uses self::_convertArray() для формирования значения
* @uses self::_processNumericArray() если передан массив тегов
* @param array $array тег
* @return array
*/
private static function _getSimpleTagValue($array)
{
$value = array_values($array);
$value = $value[0];
if (self::_getArrayType($value) == 'numeric')
return self::_processNumericArray($value);
return self::_convertArray($value);
}
/**
* Возвращает имя тега для простого тега
* @param array $array тег
* @return string
*/
private static function _getSimpleTagName($array)
{
$keys = array_keys($array);
$key = $keys[0];
if (self::_isValidTagname($key))
return $key;
return '';
}
/**
* Возвращает имя сложного тега
* @param array $array тег
* @return string
*/
private static function _getComplexTagName($array)
{
return (isset ($array['#name'])) ? $array['#name'] : '';
}
/**
* Возвращает тип сложного тега (подразумевается либо пустой, либо 'raw')
* @param array $array тег
* @return string
*/
private static function _getComplexTagType($array)
{
return (isset ($array['#type'])) ? $array['#type'] : '';
}
/**
* Возвращает атрибуты сложного тега
* @param array $array тег
* @return array
*/
private static function _getComplexTagAttrs($array)
{
return (isset ($array['#attrs'])) ? $array['#attrs'] : array();
}
/**
* Возвращает значение сложного тега (в виде массива)
* @uses self::_convertArray() для формирования значения
* @uses self::_processNumericArray() если передан массив тегов
* @param array $array тег
* @return array
*/
private static function _getComplexTagValue($array)
{
if (!isset ($array['#value']))
return '';
$value = $array['#value'];
if (self::_getArrayType($value) == 'numeric')
return self::_processNumericArray($value);
return self::_convertArray($value);
}
/**
* Обрабатывает массив массивов тегов
* @param array $arrays
* @return array обработанный массив массивов тегов
*/
private static function _processNumericArray($arrays)
{
$keys = array_keys($arrays);
$numKeys = array_filter($keys, 'is_int');
$convertedArrays = array();
foreach ($numKeys as $numKey)
{
$convertedArrays = array_merge_recursive($convertedArrays, self::_convertArray($arrays[$numKey], $numKey));
}
return $convertedArrays;
}
/**
* Проверяет, может ли быть переданное значение быть именем тега
* @param string $tagName
* @return bool
*/
private static function _isValidTagname($tagName)
{
if (/*!empty ($tagName) && */!is_int($tagName))
return true;
return false;
}
/**
* Генерация XML структуры.
*/
private function _generateXml()
{
$data = $this->_data;
foreach ($data as $key => $tagData)
{
if ($tagData['#type'] == 'raw')
$this->_XmlGenerator->writeRaw($tagData['#value']);
else if (!empty($tagData['#name']))
{
$this->_XmlGenerator->startElement($tagData['#name']);
$this->_setCurrentTagAttributes($tagData['#attrs']);
$this->_setCurrentTagValue($tagData['#value']);
$this->_XmlGenerator->endElement();
}
}
}
/**
* Заполняем значение текущего тега
* (может вызывать @link $this->getXML(), если передан массив)
* @param mixed $tagValue значение тега (массив/строка)
*/
protected function _setCurrentTagValue($tagValue)
{
if (is_array($tagValue))
$this->_generateXml($tagValue);
else
$this->_XmlGenerator->setValue($tagValue);
}
/**
* Заполняем атрибуты текущего тега
* @param array $tagAttributes массив атрибутов
*/
protected function _setCurrentTagAttributes($tagAttributes)
{
foreach ($tagAttributes as $key => $value)
{
$this->_XmlGenerator->writeAttribute($key, $value);
}
}
/**
* Генерирует и возвращает XML
* @return string
*/
public function getXml()
{
$this->_XmlGenerator->preGenerate();
$this->_generateXml();
return $this->_XmlGenerator->postGenerate();
}
public function getData()
{
return $this->_data;
}
}
/**
* Класс генерации XML данных
*/
class XmlGenerator
{
/**
* Объект
* @var XMLWriter
*/
protected $writer;
/**
* Имя корневого элемента
* @var string
*/
protected $rootName = 'root';
/**
* Номер версии XML
* @var float
*/
protected $version = '1.0';
/**
* Название кодировки
* @var string
*/
protected $encoding = 'UTF-8';
/**
* Конструктор
*/
function __construct($rootName = false)
{
if ($rootName)
$this->rootName = $rootName;
$this->writer = new XMLWriter();
}
/**
* перед генерацией XML документа
*/
public function preGenerate($write_headers = true)
{
$this->writer->openMemory();
if( $write_headers )
$this->header();
}
/**
* после генерации XML документа
* @return string
*/
public function postGenerate($write_headers = true)
{
if( $write_headers )
$this->footer();
return $this->writer->outputMemory();
}
/**
* Устанавливает версию и кодировку документа, а также корневой элемент
*/
protected function header()
{
$this->writer->startDocument($this->version, $this->encoding);
$this->writer->startElement($this->rootName);
}
/**
* Закрываем корневой элемент
*/
protected function footer()
{
$this->writer->endElement();
}
/**
* Пишем сырые XML-данные в структуру
* @param string $rawXml
*/
public function writeRaw($rawXml)
{
$this->writer->writeRaw($rawXml);
}
public function startElement($tagName)
{
$this->writer->startElement($tagName);
}
public function endElement()
{
$this->writer->endElement();
}
public function writeAttribute($key, $value)
{
$this->writer->writeAttribute($key, $value);
}
public function setValue($tagValue)
{
if (is_numeric($tagValue) || is_bool($tagValue))
$this->writer->text((float) $tagValue);
/**
* Дальше всё считаем строками.
*/
else if (!empty ($tagValue))
$this->writer->writeCData($tagValue);
}
}
//использование
$tpl = new XmlTpl();
$tpl->assign('', array('#type' => 'raw', '#value' => 'value'));
$jscripts = array
(
array('#name' => 'script', '#value' => '/js/script.js'),
array('#name' => 'script', '#value' => '/js/tooltip.js'),
array('#name' => 'script', '#value' => '/js/slib.js'),
);
$tpl->assign('ExtraHead', $jscripts);
$tpl->assign('MainMenu', '');
$tpl->assign('Footer', '');
echo $tpl->getXml();