Файл: apwa/includes/mod_parser.php
Строк: 935
<?php
/**
*
* @package automod
* @version $Id$
* @copyright (c) 2008 phpBB Group
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
*
*/
/**
*/
if (!defined('IN_PHPBB'))
{
exit;
}
/**
* MOD Parser class
* Basic wrapper to run individual parser functions
* Also contains some parsing functions that are global (i.e. needed for all parsers)
* @package automod
*
* Each parser requires the following functions:
* ~ set_file($path_to_mod_file)
* ~ a means of setting the data to be acted upon
* ~ get_details()
* ~ returns an array of information about the MOD
* ~ get_actions()
* ~ returns an array of the MODs actions
* ~ get_modx_version
* ~ returns the MODX version of the MOD being looked at
*
*/
class parser
{
var $parser;
/**
* constructor, sets type of parser
*/
function parser($ext)
{
switch ($ext)
{
case 'xml':
// fix ticket 62689 only enter parser if it is xml
//http://www.phpbb.com/bugs/modteamtools/62689
$this->parser = new parser_xml();
break;
default:
}
}
function set_file($file)
{
$this->parser->set_file($file);
}
function get_details()
{
return $this->parser->get_details();
}
function get_actions()
{
return $this->parser->get_actions();
}
function get_modx_version()
{
if (!$this->parser->modx_version)
{
$this->get_details();
}
return $this->parser->modx_version;
}
/**
* Returns the needed sql query to reverse the actions taken by the given query
* @todo: Add more
*/
function reverse_query($orig_query)
{
if (preg_match('#ALTER TABLEs([a-z_]+)sADD(?:sCOLUMN)?s([a-z_]+)#i', $orig_query, $matches))
{
return "ALTER TABLE {$matches[1]} DROP COLUMN {$matches[2]};";
}
else if (preg_match('#CREATE TABLEs([a-z_]+)#i', $orig_query, $matches))
{
return "DROP TABLE {$matches[1]};";
}
return false;
}
/**
* Parse sql
*
* @param array $sql_query
*/
function parse_sql(&$sql_query)
{
global $dbms, $table_prefix;
if (!function_exists('get_available_dbms'))
{
global $phpbb_root_path, $phpEx;
include($phpbb_root_path . 'includes/functions_install.' . $phpEx);
}
static $available_dbms;
if (!isset($available_dbms))
{
$available_dbms = get_available_dbms($dbms);
}
$remove_remarks = $available_dbms[$dbms]['COMMENTS'];
$delimiter = $available_dbms[$dbms]['DELIM'];
if (sizeof($sql_query) == 1)
{
// do some splitting here
$sql_query = preg_replace('#phpbb_#i', $table_prefix, $sql_query);
$remove_remarks($sql_query[0]);
$sql_query = split_sql_file($sql_query[0], $delimiter);
}
else
{
$query_count = sizeof($sql_query);
for ($i = 0; $i < $query_count; $i++)
{
$sql_query[$i] = preg_replace('#phpbb_#i', $table_prefix, $sql_query[$i]);
$remove_remarks($sql_query[$i]);
}
}
//return $sql_query;
}
/**
* Returns the edits array, but now filled with edits to reverse the given array
* @todo: Add more
*/
function reverse_edits($actions)
{
$reverse_edits = array();
if (!empty($actions['EDITS']))
{
foreach ($actions['EDITS'] as $file => $edit_ary)
{
foreach ($edit_ary as $edit_id => $edit)
{
foreach ($edit as $find => $action_ary)
{
foreach ($action_ary as $type => $command)
{
// it is possible for a single edit in the install process
// to become more than one in the uninstall process
while (isset($reverse_edits['EDITS'][$file][$edit_id]))
{
$edit_id++;
}
switch (strtoupper($type))
{
// for before and after adds, we use the find as a tool for more precise finds
// this isn't perfect, but it seems better than having
// finds of only a couple characters, like "/*"
case 'AFTER ADD':
$total_find = rtrim($find, "n") . "n" . trim($command, "n");
$reverse_edits['EDITS'][$file][$edit_id][$total_find]['replace with'] = $find;
break;
case 'BEFORE ADD':
$total_find = rtrim($command, "n") . "n" . trim($find, "n");
// replace with the find
$reverse_edits['EDITS'][$file][$edit_id][$total_find]['replace with'] = $find;
break;
case 'REPLACE WITH':
case 'REPLACE, WITH':
case 'REPLACE-WITH':
case 'REPLACE':
// replace $command (new code) with $find (original code)
$reverse_edits['EDITS'][$file][$edit_id][$command]['replace with'] = $find;
break;
case 'IN-LINE-EDIT':
// build the reverse just like the normal action
foreach ($command as $action_id => $inline_edit)
{
foreach ($inline_edit as $inline_find => $inline_action_ary)
{
foreach ($inline_action_ary as $inline_action => $inline_command)
{
$inline_command = $inline_command[0];
switch (strtoupper($inline_action))
{
case 'IN-LINE-AFTER-ADD':
case 'IN-LINE-BEFORE-ADD':
// Replace with a blank string
$reverse_edits['EDITS'][$file][$edit_id][$find]['in-line-edit'][$action_id][$inline_command]['in-line-replace'][] = '';
break;
case 'IN-LINE-REPLACE':
// replace with the inline find
$reverse_edits['EDITS'][$file][$edit_id][$find]['in-line-edit'][$action_id][$inline_command][$inline_action][] = $inline_find;
break;
default:
// For the moment, we do nothing. What about increment?
break;
}
$action_id++;
}
}
}
break;
default:
// again, increment
break;
}
}
}
}
}
}
if (!empty($actions['NEW_FILES']))
{
foreach ($actions['NEW_FILES'] as $source => $target)
{
$reverse_edits['DELETE_FILES'][$source] = $target;
}
}
if (empty($actions['SQL']))
{
return $reverse_edits;
}
if (sizeof($actions['SQL']) == 1)
{
$actions['SQL'] = explode("n", $actions['SQL'][0]);
}
foreach ($actions['SQL'] as $query)
{
$reverse_edits['SQL'][] = parser::reverse_query($query);
}
return $reverse_edits;
}
}
/**
* XML parser
* @package automod
*/
class parser_xml
{
var $data;
var $file;
var $modx_version;
/**
* set data to read from
*/
function set_file($file)
{
// Shouldn't ever happen since the master class reads file names from
// the file system and lists them
if (!file_exists($file))
{
trigger_error('Cannot locate File: ' . $file);
}
$this->file = $file;
$this->data = trim(@file_get_contents($file));
$this->data = str_replace(array("rn", "r"), "n", $this->data);
$XML = new xml_array();
$this->data = $XML->parse($this->file, $this->data);
return;
}
/**
* return array of the basic MOD details
*/
function get_details()
{
global $user;
if (empty($this->data))
{
$this->set_file($this->file);
}
$header = array(
'MOD-VERSION' => array(0 => array('children' => array())),
'INSTALLATION' => array(0 => array('children' => array('TARGET-VERSION' => array(0 => array('data' => ''))))),
'AUTHOR-GROUP' => array(0 => array('children' => array('AUTHOR' => array()))),
'HISTORY' => array(0 => array('children' => array('ENTRY' => array()))),
);
$version = $phpbb_version = '';
$header = $this->data[0]['children']['HEADER'][0]['children'];
// get MOD version information
// This is also our first opportunity to differentiate MODX 1.0.x from
// MODX 1.2.0.
if (isset($header['MOD-VERSION'][0]['children']))
{
$this->modx_version = 1.0;
$version_info = $header['MOD-VERSION'][0]['children'];
$version = (isset($version_info['MAJOR'][0]['data'])) ? trim($version_info['MAJOR'][0]['data']) : 0;
$version .= '.' . ((isset($version_info['MINOR'][0]['data'])) ? trim($version_info['MINOR'][0]['data']) : 0);
$version .= '.' . ((isset($version_info['REVISION'][0]['data'])) ? trim($version_info['REVISION'][0]['data']) : 0);
$version .= (isset($version_info['RELEASE'][0]['data'])) ? trim($version_info['RELEASE'][0]['data']) : '';
}
else
{
$this->modx_version = 1.2;
$version = trim($header['MOD-VERSION'][0]['data']);
}
// get phpBB version recommendation
switch ($this->modx_version)
{
case 1.0:
if (isset($header['INSTALLATION'][0]['children']['TARGET-VERSION'][0]['children']))
{
$version_info = $header['INSTALLATION'][0]['children']['TARGET-VERSION'][0]['children'];
$phpbb_version = (isset($version_info['MAJOR'][0]['data'])) ? trim($version_info['MAJOR'][0]['data']) : 0;
$phpbb_version .= '.' . ((isset($version_info['MINOR'][0]['data'])) ? trim($version_info['MINOR'][0]['data']) : 0);
$phpbb_version .= '.' . ((isset($version_info['REVISION'][0]['data'])) ? trim($version_info['REVISION'][0]['data']) : 0);
$phpbb_version .= (isset($version_info['RELEASE'][0]['data'])) ? trim($version_info['RELEASE'][0]['data']) : '';
}
break;
case 1.2:
default:
$phpbb_version = (isset($header['INSTALLATION'][0]['children']['TARGET-VERSION'][0]['data'])) ? $header['INSTALLATION'][0]['children']['TARGET-VERSION'][0]['data'] : 0;
break;
}
$author_info = $header['AUTHOR-GROUP'][0]['children']['AUTHOR'];
$author_details = array();
for ($i = 0; $i < sizeof($author_info); $i++)
{
$author_details[] = array(
'AUTHOR_NAME' => isset($author_info[$i]['children']['USERNAME'][0]['data']) ? trim($author_info[$i]['children']['USERNAME'][0]['data']) : '',
'AUTHOR_EMAIL' => isset($author_info[$i]['children']['EMAIL'][0]['data']) ? trim($author_info[$i]['children']['EMAIL'][0]['data']) : '',
'AUTHOR_REALNAME' => isset($author_info[$i]['children']['REALNAME'][0]['data']) ? trim($author_info[$i]['children']['REALNAME'][0]['data']) : '',
'AUTHOR_WEBSITE' => isset($author_info[$i]['children']['HOMEPAGE'][0]['data']) ? trim($author_info[$i]['children']['HOMEPAGE'][0]['data']) : '',
);
}
// history
$history_info = (!empty($header['HISTORY'][0]['children']['ENTRY'])) ? $header['HISTORY'][0]['children']['ENTRY'] : array();
$history_size = sizeof($history_info);
$mod_history = array();
for ($i = 0; $i < $history_size; $i++)
{
$changes = array();
$entry = $history_info[$i]['children'];
$changelog = isset($entry['CHANGELOG']) ? $entry['CHANGELOG'] : array();
$changelog_size = sizeof($changelog);
$changelog_id = 0;
for ($j = 0; $j < $changelog_size; $j++)
{
// Ignore changelogs in foreign languages except in the case that there is no
// match for the current user's language
// TODO: Look at modifying localise_tags() for use here.
if (match_language($user->data['user_lang'], $changelog[$j]['attrs']['LANG']))
{
$changelog_id = $j;
}
}
$change_count = isset($changelog[$changelog_id]['children']['CHANGE']) ? sizeof($changelog[$changelog_id]['children']['CHANGE']) : 0;
for ($j = 0; $j < $change_count; $j++)
{
$changes[] = $changelog[$changelog_id]['children']['CHANGE'][$j]['data'];
}
switch ($this->modx_version)
{
case 1.0:
$changelog_version_ary = (isset($entry['REV-VERSION'][0]['children'])) ? $entry['REV-VERSION'][0]['children'] : array();
$changelog_version = (isset($changelog_version_ary['MAJOR'][0]['data'])) ? trim($changelog_version_ary['MAJOR'][0]['data']) : 0;
$changelog_version .= '.' . ((isset($changelog_version_ary['MINOR'][0]['data'])) ? trim($changelog_version_ary['MINOR'][0]['data']) : 0);
$changelog_version .= '.' . ((isset($changelog_version_ary['REVISION'][0]['data'])) ? trim($changelog_version_ary['REVISION'][0]['data']) : 0);
$changelog_version .= (isset($changelog_version_ary['RELEASE'][0]['data'])) ? trim($changelog_version_ary['RELEASE'][0]['data']) : '';
break;
case 1.2:
default:
$changelog_version = (isset($entry['REV-VERSION'][0]['data'])) ? $entry['REV-VERSION'][0]['data'] : '0.0.0';
break;
}
$mod_history[] = array(
'DATE' => $entry['DATE'][0]['data'],
'VERSION' => $changelog_version,
'CHANGES' => $changes,
);
}
$children = array();
// Parse links
if ($this->modx_version == 1.2)
{
$link_group = (isset($header['LINK-GROUP'][0]['children'])) ? $header['LINK-GROUP'][0]['children'] : array();
if (isset($link_group['LINK']))
{
for ($i = 0, $size = sizeof($link_group['LINK']); $i <= $size; $i++)
{
// do some stuff with attrs
// commented out due to a possible PHP bug. When using this,
// sizeof($link_group) changed each time ...
// $attrs = &$link_group[$i]['attrs'];
if (!isset($link_group['LINK'][$i]))
{
continue;
}
if ($link_group['LINK'][$i]['attrs']['TYPE'] == 'text')
{
continue;
}
$children[$link_group['LINK'][$i]['attrs']['TYPE']][] = array(
'href' => $link_group['LINK'][$i]['attrs']['HREF'],
'realname' => isset($link_group['LINK'][$i]['attrs']['REALNAME']) ? $link_group['LINK'][$i]['attrs']['REALNAME'] : core_basename($link_group['LINK'][$i]['attrs']['HREF']),
'title' => localise_tags($link_group, 'LINK', $i),
'lang' => $link_group['LINK'][$i]['attrs']['LANG'],
);
}
}
}
// try not to hardcode schema?
$details = array(
'MOD_PATH' => $this->file,
'MOD_NAME' => get_title($header),
// 'MOD_NAME' => localise_tags($header, 'TITLE'),
'MOD_DESCRIPTION' => nl2br(localise_tags($header, 'DESCRIPTION')),
'MOD_VERSION' => htmlspecialchars(trim($version)),
// 'MOD_DEPENDENCIES' => (isset($header['TITLE'][0]['data'])) ? htmlspecialchars(trim($header['TITLE'][0]['data'])) : '',
'AUTHOR_DETAILS' => $author_details,
'AUTHOR_NOTES' => nl2br(localise_tags($header, 'AUTHOR-NOTES')),
'MOD_HISTORY' => $mod_history,
'PHPBB_VERSION' => $phpbb_version,
'CHILDREN' => $children,
);
return $details;
}
/**
* returns complex array containing all mod actions
*/
function get_actions()
{
global $db, $user;
$actions = array();
$xml_actions = $this->data[0]['children']['ACTION-GROUP'][0]['children'];
// sql
$actions['SQL'] = array();
$sql_info = (!empty($xml_actions['SQL'])) ? $xml_actions['SQL'] : array();
$match_dbms = array();
switch ($db->sql_layer)
{
case 'firebird':
case 'oracle':
case 'postgres':
case 'sqlite':
case 'mssql':
case 'db2':
$match_dbms = array($db->sql_layer);
break;
case 'mssql_odbc':
$match_dbms = array('mssql');
break;
// and now for the MySQL fun
// This will generate an array of things we can probably use, but
// will not have any priority
case 'mysqli':
$match_dbms = array('mysql_41', 'mysqli', 'mysql');
break;
case 'mysql4':
case 'mysql':
if (version_compare($db->sql_server_info(true), '4.1.3', '>='))
{
$match_dbms = array('mysql_41', 'mysql4', 'mysql', 'mysqli');
}
else if (version_compare($db->sql_server_info(true), '4.0.0', '>='))
{
$match_dbms = array('mysql_40', 'mysql4', 'mysql', 'mysqli');
}
else
{
$match_dbms = array('mysql');
}
break;
// Should never happen
default:
break;
}
for ($i = 0; $i < sizeof($sql_info); $i++)
{
if ($this->modx_version == 1.0)
{
$actions['SQL'][] = (!empty($sql_info[$i]['data'])) ? trim($sql_info[$i]['data']) : '';
}
else if ($this->modx_version == 1.2)
{
// Make a slightly shorter name.
$xml_dbms = &$sql_info[$i]['attrs']['DBMS'];
if (!isset($sql_info[$i]['attrs']['DBMS']) || in_array($xml_dbms, $match_dbms))
{
$actions['SQL'][] = (!empty($sql_info[$i]['data'])) ? trim($sql_info[$i]['data']) : '';
}
else
{
// NOTE: skipped SQL is not currently useful
$sql_skipped = true;
}
}
}
// new files
$new_files_info = (!empty($xml_actions['COPY'])) ? $xml_actions['COPY'] : array();
for ($i = 0; $i < sizeof($new_files_info); $i++)
{
$new_files = $new_files_info[$i]['children']['FILE'];
for ($j = 0; $j < sizeof($new_files); $j++)
{
$from = str_replace('\', '/', $new_files[$j]['attrs']['FROM']);
$to = str_replace('\', '/', $new_files[$j]['attrs']['TO']);
$actions['NEW_FILES'][$from] = $to;
}
}
$delete_files_info = (!empty($xml_actions['DELETE'])) ? $xml_actions['DELETE'] : array();
for ($i = 0; $i < sizeof($delete_files_info); $i++)
{
$delete_files = $delete_files_info[$i]['children']['FILE'];
for ($j = 0; $j < sizeof($delete_files); $j++)
{
$name = str_replace('\', '/', $delete_files[$j]['attrs']['NAME']);
$actions['DELETE_FILES'][] = $name;
}
}
// open
$open_info = (!empty($xml_actions['OPEN'])) ? $xml_actions['OPEN'] : array();
for ($i = 0; $i < sizeof($open_info); $i++)
{
$current_file = str_replace('\', '/', trim($open_info[$i]['attrs']['SRC']));
$actions['EDITS'][$current_file] = array();
$edit_info = (!empty($open_info[$i]['children']['EDIT'])) ? $open_info[$i]['children']['EDIT'] : array();
// find, after add, before add, replace with
for ($j = 0; $j < sizeof($edit_info); $j++)
{
$action_info = (!empty($edit_info[$j]['children'])) ? $edit_info[$j]['children'] : array();
// store some array information to help decide what kind of operation we're doing
$action_count = $total_action_count = $remove_count = $find_count = 0;
if (isset($action_info['ACTION']))
{
$action_count += sizeof($action_info['ACTION']);
}
if (isset($action_info['INLINE-EDIT']))
{
$total_action_count += sizeof($action_info['INLINE-EDIT']);
}
if (isset($action_info['REMOVE']))
{
$remove_count = sizeof($action_info['REMOVE']); // should be an integer bounded between zero and one
}
if (isset($action_info['FIND']))
{
$find_count = sizeof($action_info['FIND']);
}
// the basic idea is to transform a "remove" tag into a replace-with action
if ($remove_count && !$find_count)
{
// but we still support it if $remove_count is > 1
for ($k = 0; $k < $remove_count; $k++)
{
// if there is no find tag associated, handle it directly
$actions['EDITS'][$current_file][$j][trim($action_info['REMOVE'][$k]['data'], "nr")]['replace with'] = '';
}
}
else if ($remove_count && $find_count)
{
// if there is a find and a remove, transform into a replace-with
// action, and let the logic below sort out the relationships.
for ($k = 0; $k < $remove_count; $k++)
{
$insert_index = (isset($action_info['ACTION'])) ? sizeof($action_info['ACTION']) : 0;
$action_info['ACTION'][$insert_index] = array(
'data' => '',
'attrs' => array('TYPE' => 'replace with'),
);
}
}
else if (!$find_count)
{
trigger_error(sprintf($user->lang['INVALID_MOD_NO_FIND'], htmlspecialchars($action_info['ACTION'][0]['data'])), E_USER_WARNING);
}
// first we try all the possibilities for a FIND/ACTION combo, then look at inline possibilities.
if (isset($action_info['ACTION']))
{
for ($k = 0; $k < $find_count; $k++)
{
// is this anything but the last iteration of the loop?
if ($k < ($find_count - 1))
{
// NULL has special meaning for an action ... no action to be taken; advance pointer
$actions['EDITS'][$current_file][$j][$action_info['FIND'][$k]['data']] = NULL;
}
else
{
// this is the last iteration, assign the action tags
for ($l = 0; $l < $action_count; $l++)
{
$type = str_replace('-', ' ', $action_info['ACTION'][$l]['attrs']['TYPE']);
$actions['EDITS'][$current_file][$j][trim($action_info['FIND'][$k]['data'], "nr")][$type] = (isset($action_info['ACTION'][$l]['data'])) ? preg_replace("#^(s)+n#", '', rtrim(trim($action_info['ACTION'][$l]['data'], "n"))) : '';
}
}
}
}
else
{
if (!$remove_count && !$total_action_count)
{
trigger_error(sprintf($user->lang['INVALID_MOD_NO_ACTION'], htmlspecialchars($action_info['FIND'][0]['data'])), E_USER_WARNING);
}
}
// add comment to the actions array
$actions['EDITS'][$current_file][$j]['comment'] = localise_tags($action_info, 'COMMENT');
// inline
if (isset($action_info['INLINE-EDIT']))
{
$inline_info = (!empty($action_info['INLINE-EDIT'])) ? $action_info['INLINE-EDIT'] : array();
if (isset($inline_info[0]['children']['INLINE-REMOVE']) && sizeof($inline_info[0]['children']['INLINE-REMOVE']))
{
// overwrite the existing array with the new one
$inline_info[0]['children'] = array(
'INLINE-FIND' => $inline_info[0]['children']['INLINE-REMOVE'],
'INLINE-ACTION' => array(
0 => array(
'attrs' => array('TYPE' => 'replace-with'),
'data' => '',
),
),
);
}
if ($find_count > $total_action_count)
{
// Yeah, $k is used more than once for different information
for ($k = 0; $k < $find_count; $k++)
{
// is this anything but the last iteration of the loop?
if ($k < ($find_count - 1))
{
// NULL has special meaning for an action ... no action to be taken; advance pointer
$actions['EDITS'][$current_file][$j][trim($action_info['FIND'][$k]['data'], "rn")] = NULL;
}
}
}
/*
* This loop attaches the in-line information to the _last
* find_ in the <edit> tag. This is the intended behavior
* Any additional finds ought to be in a different edit tag
*/
for ($k = 0; $k < sizeof($inline_info); $k++)
{
$inline_data = (!empty($inline_info[$k]['children'])) ? $inline_info[$k]['children'] : array();
$inline_find_count = (isset($inline_data['INLINE-FIND'])) ? sizeof($inline_data['INLINE-FIND']) : 0;
$inline_comment = localise_tags($inline_data, 'INLINE-COMMENT');
$actions['EDITS'][$current_file][$j][trim($action_info['FIND'][$find_count - 1]['data'], "rn")]['in-line-edit']['inline-comment'] = $inline_comment;
$inline_actions = (!empty($inline_data['INLINE-ACTION'])) ? $inline_data['INLINE-ACTION'] : array();
if (empty($inline_actions))
{
trigger_error(sprintf($user->lang['INVALID_MOD_NO_ACTION'], htmlspecialchars($inline_data['INLINE-FIND'][0]['data'])), E_USER_WARNING);
}
if (empty($inline_find_count))
{
trigger_error(sprintf($user->lang['INVALID_MOD_NO_FIND'], htmlspecialchars($inline_actions[0]['data'])), E_USER_WARNING);
}
for ($l = 0; $l < $inline_find_count; $l++)
{
$inline_find = $inline_data['INLINE-FIND'][$l]['data'];
// trying to reduce the levels of arrays without impairing features.
// need to keep the "full" edit intact.
//
// inline actions must be trimmed in case the MOD author
// inserts a new line by mistake
if ($l < ($inline_find_count - 1))
{
$actions['EDITS'][$current_file][$j][trim($action_info['FIND'][$find_count - 1]['data'], "rn")]['in-line-edit'][$k][$inline_find]['in-line-'][] = null;
}
else
{
for ($m = 0; $m < sizeof($inline_actions); $m++)
{
$type = str_replace(',', '-', str_replace(' ', '', $inline_actions[$m]['attrs']['TYPE']));
if (!empty($inline_actions[$m]['data']))
{
$actions['EDITS'][$current_file][$j][trim($action_info['FIND'][$find_count - 1]['data'], "rn")]['in-line-edit'][$k][$inline_find]['in-line-' . $type][] = trim($inline_actions[$m]['data'], "n");
}
else
{
$actions['EDITS'][$current_file][$j][trim($action_info['FIND'][$find_count - 1]['data'], "rn")]['in-line-edit'][$k][$inline_find]['in-line-' . $type][] = '';
}
}
}
}
}
}
}
}
if (!empty($xml_actions['PHP-INSTALLER']))
{
$actions['PHP_INSTALLER'] = $xml_actions['PHP-INSTALLER'][0]['data'];
}
if (!empty($xml_actions['DIY-INSTRUCTIONS']))
{
$actions['DIY_INSTRUCTIONS'] = localise_tags($xml_actions, 'DIY-INSTRUCTIONS');
}
return $actions;
}
}
/**
* XML processing
* @package automod
*/
class xml_array
{
var $output = array();
var $parser;
var $XML;
function parse($file, $XML)
{
$this->parser = xml_parser_create();
xml_set_object($this->parser, $this);
xml_set_element_handler($this->parser, "tag_open", "tag_closed");
xml_set_character_data_handler($this->parser, "tag_data");
$this->XML = xml_parse($this->parser, $XML);
if (!$this->XML)
{
die(sprintf("<strong>XML error</strong>: %s at line %d. View the file %s in a web browser for a more detailed error message.",
xml_error_string(xml_get_error_code($this->parser)), xml_get_current_line_number($this->parser), $file));
}
xml_parser_free($this->parser);
return $this->output;
}
function tag_open($parser, $name, $attrs)
{
$tag = array("name" => $name, "attrs" => $attrs);
array_push($this->output, $tag);
}
function tag_data($parser, $tag_data)
{
// Should be a string but' let's make sure.
$tag_data = (string) $tag_data;
if ($tag_data !== '')
{
if (isset($this->output[sizeof($this->output) - 1]['data']))
{
$this->output[sizeof($this->output) - 1]['data'] .= $tag_data;
}
else
{
$this->output[sizeof($this->output) - 1]['data'] = $tag_data;
}
}
}
function tag_closed($parser, $name)
{
$this->output[sizeof($this->output) - 2]['children'][$name][] = $this->output[sizeof($this->output) - 1];
array_pop($this->output);
}
}
?>