Reimplement layout XML parser using modern alternatives

This commit is contained in:
Kijin Sung 2025-06-01 02:35:15 +09:00
parent f556e5e3fa
commit 07e3298eb1
4 changed files with 182 additions and 233 deletions

View file

@ -110,9 +110,10 @@ abstract class BaseParser
* @param \SimpleXMLElement $extra_vars * @param \SimpleXMLElement $extra_vars
* @param string $lang * @param string $lang
* @param string $type * @param string $type
* @param array $options
* @return object * @return object
*/ */
protected static function _getExtraVars(\SimpleXMLElement $extra_vars, string $lang, string $type = ''): \stdClass protected static function _getExtraVars(\SimpleXMLElement $extra_vars, string $lang, string $type = '', array $options = []): \stdClass
{ {
$result = new \stdClass; $result = new \stdClass;
@ -193,12 +194,29 @@ abstract class BaseParser
$item->init_options[$value] = true; $item->init_options[$value] = true;
} }
} }
elseif ($type === 'layout')
{
$option_item = new \stdClass;
if (!empty($option['src']))
{
$thumbnail_path = $options['layout_path'] . $option['src'];
if (file_exists($thumbnail_path))
{
$option_item->thumbnail = $thumbnail_path;
$item->thumbnail_exist = true;
}
}
$title = self::_getChildrenByLang($option, 'title', $lang);
$value = trim($option['value'] ?? '') ?: trim($option->value ?? '');
$option_item->val = $title;
$item->options[$value] = $option_item;
}
else else
{ {
$option_item = new \stdClass; $option_item = new \stdClass;
$option_item->title = self::_getChildrenByLang($option, 'title', $lang); $option_item->title = self::_getChildrenByLang($option, 'title', $lang);
$option_item->value = trim($option['value'] ?? '') ?: trim($option->value ?? ''); $option_item->value = trim($option['value'] ?? '') ?: trim($option->value ?? '');
$item->options[$option_item->value] = $option_item; $item->options[trim($option_item->value ?? '')] = $option_item;
} }
} }
} }

View file

@ -0,0 +1,128 @@
<?php
namespace Rhymix\Framework\Parsers;
/**
* Layout (info.xml) parser class for XE compatibility.
*/
class LayoutInfoParser extends BaseParser
{
/**
* Load an XML file.
*
* @param string $filename
* @param string $layout_name
* @param string $layout_path
* @param string $lang
* @return ?object
*/
public static function loadXML(string $filename, string $layout_name, string $layout_path, string $lang = ''): ?object
{
// Load the XML file.
$xml = simplexml_load_string(file_get_contents($filename));
if ($xml === false)
{
return null;
}
// Get the current language.
$lang = $lang ?: (\Context::getLangType() ?: 'en');
// Initialize the layout definition.
$info = new \stdClass;
$info->layout = $layout_name;
$info->type = trim($xml['type'] ?? '');
$info->path = $layout_path;
// Get the XML schema version.
$version = strval($xml['version']) ?: '0.1';
// Parse version 0.2
if ($version === '0.2')
{
$info->title = self::_getChildrenByLang($xml, 'title', $lang) ?: $layout_name;
$info->description = self::_getChildrenByLang($xml, 'description', $lang);
$info->version = trim($xml->version);
$info->date = ($xml->date === 'RX_CORE') ? '' : date('Ymd', strtotime($xml->date . 'T12:00:00Z'));
$info->homepage = trim($xml->link);
$info->license = trim($xml->license);
$info->license_link = trim($xml->license['link'] ?? '');
$info->author = array();
foreach ($xml->author as $author)
{
$author_info = new \stdClass;
$author_info->name = self::_getChildrenByLang($author, 'name', $lang);
$author_info->email_address = trim($author['email_address'] ?? '');
$author_info->homepage = trim($author['link'] ?? '');
$info->author[] = $author_info;
}
}
// Parse version 0.1
else
{
$info->title = self::_getChildrenByLang($xml, 'title', $lang) ?: $layout_name;
$info->description = self::_getChildrenByLang($xml->author, 'description', $lang);
$info->version = trim($xml['version'] ?? '');
$info->date = date('Ymd', strtotime($xml->author['date'] . 'T12:00:00Z'));
$info->homepage = trim($xml->link);
$info->license = trim($xml->license);
$info->license_link = trim($xml->license['link'] ?? '');
$info->author = array();
$author_info = new \stdClass;
$author_info->name = self::_getChildrenByLang($xml->author, 'name', $lang);
$author_info->email_address = trim($xml->author['email_address']);
$author_info->homepage = trim($xml->author['link'] ?? '');
$info->author[] = $author_info;
}
// Get extra_vars.
$info->extra_var_count = 0;
if ($xml->extra_vars)
{
$info->extra_var = self::_getExtraVars($xml->extra_vars, $lang, 'layout', ['layout_path' => $layout_path]);
}
else
{
$info->extra_var = new \stdClass;
}
// Count extra vars.
$info->extra_var_count = count(get_object_vars($info->extra_var));
// Get menus.
$info->menu_count = 0;
if (isset($xml->menus->menu))
{
$info->menu = new \stdClass;
foreach ($xml->menus->menu as $menu)
{
$menu_item = new \stdClass;
$menu_item->name = trim($menu['name'] ?? '');
$menu_item->title = self::_getChildrenByLang($menu, 'title', $lang);
$menu_item->maxdepth = intval($menu['maxdepth'] ?? 0);
$menu_item->menu_srl = null;
$menu_item->xml_file = '';
$menu_item->php_file = '';
$info->menu->{$menu_item->name} = $menu_item;
$info->menu_count++;
}
}
else
{
$info->menu = null;
}
$info->menu_name_list = null;
// Prepare additional fields that will be filled in later.
$info->site_srl = 0;
$info->layout_srl = 0;
$info->layout_title = '';
$info->header_script = '';
// Return the complete result.
return $info;
}
}

View file

@ -92,12 +92,17 @@ class LayoutAdminController extends Layout
// Consider the rest of items as extra vars, except module, act, layout_srl, layout, and title .. Some gurida .. // Consider the rest of items as extra vars, except module, act, layout_srl, layout, and title .. Some gurida ..
$extra_vars = Context::getRequestVars(); $extra_vars = Context::getRequestVars();
unset($extra_vars->module); unset($extra_vars->module);
unset($extra_vars->module);
unset($extra_vars->act); unset($extra_vars->act);
unset($extra_vars->layout_srl); unset($extra_vars->layout_srl);
unset($extra_vars->layout); unset($extra_vars->layout);
unset($extra_vars->title); unset($extra_vars->title);
unset($extra_vars->apply_layout); unset($extra_vars->apply_layout);
unset($extra_vars->apply_mobile_view); unset($extra_vars->apply_mobile_view);
unset($extra_vars->success_return_url);
unset($extra_vars->error_return_url);
unset($extra_vars->xe_validator_id);
unset($extra_vars->ruleset);
$is_sitemap = $extra_vars->is_sitemap; $is_sitemap = $extra_vars->is_sitemap;
unset($extra_vars->is_sitemap); unset($extra_vars->is_sitemap);

View file

@ -494,7 +494,7 @@ class LayoutModel extends Layout
$layout = $info->layout; $layout = $info->layout;
$layout_srl = $info->layout_srl; $layout_srl = $info->layout_srl;
$site_srl = $info->site_srl; $site_srl = $info->site_srl;
$vars = unserialize($info->extra_vars); $vars = $info->extra_vars ? unserialize($info->extra_vars) : null;
if($info->module_srl) if($info->module_srl)
{ {
@ -550,10 +550,19 @@ class LayoutModel extends Layout
if(file_exists($cache_file) && filemtime($cache_file) > filemtime($xml_file)) if(file_exists($cache_file) && filemtime($cache_file) > filemtime($xml_file))
{ {
// Read cache file in a way that is compatible with the old format.
// The old format sets $layout_info directly, while the new format returns an object.
$layout_info = new stdClass; $layout_info = new stdClass;
include($cache_file); if (is_object($output = include($cache_file)))
{
$layout_info = $output;
}
if (empty($layout_info->layout))
{
return;
}
if($layout_info->extra_var && $vars) if ($layout_info->extra_var && $vars)
{ {
foreach($vars as $key => $value) foreach($vars as $key => $value)
{ {
@ -563,252 +572,41 @@ class LayoutModel extends Layout
} }
} }
} }
if(!$layout_info->title)
{
$layout_info->title = $layout;
}
return $layout_info; return $layout_info;
} }
// If no cache file exists, parse the xml and then return the variable. // If no cache file exists, parse the xml and then return the variable.
$tmp_xml_obj = Rhymix\Framework\Parsers\XEXMLParser::loadXMLFile($xml_file); $xml_info = Rhymix\Framework\Parsers\LayoutInfoParser::loadXML($xml_file, $layout, $layout_path);
if (!$tmp_xml_obj) if (!$xml_info)
{ {
return; return;
} }
$xml_obj = !empty($tmp_xml_obj->layout) ? $tmp_xml_obj->layout : $tmp_xml_obj->skin; // Fill in user configuration
if (!$xml_obj) foreach ($xml_info->extra_var ?: [] as $key => $value)
{ {
return; if (isset($vars->{$key}))
}
$buff = array();
$buff[] = '$layout_info = new stdClass;';
$buff[] = sprintf('$layout_info->site_srl = %s;', var_export($site_srl ?? 0, true));
if($xml_obj->version && $xml_obj->attrs->version == '0.2')
{
// Layout title, version and other information
$date_obj = new stdClass;
sscanf($xml_obj->date->body, '%d-%d-%d', $date_obj->y, $date_obj->m, $date_obj->d);
$date = sprintf('%04d%02d%02d', $date_obj->y, $date_obj->m, $date_obj->d);
$buff[] = sprintf('$layout_info->layout = %s;', var_export($layout, true));
$buff[] = sprintf('$layout_info->type = %s;', var_export($xml_obj->attrs->type ?? null, true));
$buff[] = sprintf('$layout_info->path = %s;', var_export($layout_path, true));
$buff[] = sprintf('$layout_info->title = %s;', var_export($xml_obj->title->body ?? null, true));
$buff[] = sprintf('$layout_info->description = %s;', var_export($xml_obj->description->body ?? null, true));
$buff[] = sprintf('$layout_info->version = %s;', var_export($xml_obj->version->body ?? null, true));
$buff[] = sprintf('$layout_info->date = %s;', var_export($date, true));
$buff[] = sprintf('$layout_info->homepage = %s;', var_export($xml_obj->link->body ?? null, true));
$buff[] = sprintf('$layout_info->layout_srl = $layout_srl;');
$buff[] = sprintf('$layout_info->layout_title = $layout_title;');
$buff[] = sprintf('$layout_info->license = %s;', var_export($xml_obj->license->body ?? null, true));
$buff[] = sprintf('$layout_info->license_link = %s;', var_export($xml_obj->license->attrs->link ?? null, true));
$buff[] = sprintf('$layout_info->layout_type = %s;', var_export($layout_type, true));
// Author information
if(!is_array($xml_obj->author)) $author_list[] = $xml_obj->author;
else $author_list = $xml_obj->author;
$buff[] = '$layout_info->author = array();';
for($i=0, $c=count($author_list); $i<$c; $i++)
{ {
$buff[] = sprintf('$layout_info->author[%d] = new stdClass;', $i); $value->value = $vars->{$key};
$buff[] = sprintf('$layout_info->author[%d]->name = %s;', $i, var_export($author_list[$i]->name->body, true));
$buff[] = sprintf('$layout_info->author[%d]->email_address = %s;', $i, var_export($author_list[$i]->attrs->email_address, true));
$buff[] = sprintf('$layout_info->author[%d]->homepage = %s;', $i, var_export($author_list[$i]->attrs->link, true));
}
// Extra vars (user defined variables to use in a template)
$extra_var_groups = $xml_obj->extra_vars->group;
if(!$extra_var_groups) $extra_var_groups = $xml_obj->extra_vars;
if(!is_array($extra_var_groups)) $extra_var_groups = array($extra_var_groups);
$buff[] = '$layout_info->extra_var = new stdClass;';
$extra_var_count = 0;
foreach($extra_var_groups as $group)
{
$extra_vars = $group->var;
if($extra_vars)
{
if(!is_array($extra_vars)) $extra_vars = array($extra_vars);
$count = count($extra_vars);
$extra_var_count += $count;
for($i=0;$i<$count;$i++)
{
unset($var, $options);
$var = $extra_vars[$i];
$name = $var->attrs->name;
$buff[] = sprintf('$layout_info->extra_var->%s = new stdClass;', $name);
$buff[] = sprintf('$layout_info->extra_var->%s->group = %s;', $name, var_export($group->title->body ?? null, true));
$buff[] = sprintf('$layout_info->extra_var->%s->title = %s;', $name, var_export($var->title->body ?? null, true));
$buff[] = sprintf('$layout_info->extra_var->%s->type = %s;', $name, var_export($var->attrs->type ?? null, true));
$buff[] = sprintf('$layout_info->extra_var->%s->value = $vars->%s ?? null;', $name, $name);
$buff[] = sprintf('$layout_info->extra_var->%s->description = %s;', $name, var_export($var->description->body ?? null, true));
$options = $var->options;
if(!$options) continue;
if(!is_array($options)) $options = array($options);
$buff[] = sprintf('$layout_info->extra_var->%s->options = array();', $var->attrs->name);
$options_count = count($options);
$thumbnail_exist = false;
for($j=0; $j < $options_count; $j++)
{
$buff[] = sprintf('$layout_info->extra_var->%s->options[%s] = new stdClass;', $var->attrs->name, var_export($options[$j]->attrs->value, true));
$thumbnail = $options[$j]->attrs->src;
if($thumbnail)
{
$thumbnail = $layout_path.$thumbnail;
if(file_exists($thumbnail))
{
$buff[] = sprintf('$layout_info->extra_var->%s->options[%s]->thumbnail = %s;', $var->attrs->name, var_export($options[$j]->attrs->value, true), var_export($thumbnail, true));
if(!$thumbnail_exist)
{
$buff[] = sprintf('$layout_info->extra_var->%s->thumbnail_exist = true;', $var->attrs->name);
$thumbnail_exist = true;
}
}
}
$buff[] = sprintf('$layout_info->extra_var->%s->options[%s]->val = %s;', $var->attrs->name, var_export($options[$j]->attrs->value, true), var_export($options[$j]->title->body, true));
}
}
}
}
$buff[] = sprintf('$layout_info->extra_var_count = %d;', $extra_var_count);
// Menu
if($xml_obj->menus->menu)
{
$menus = $xml_obj->menus->menu;
if(!is_array($menus)) $menus = array($menus);
$menu_count = count($menus);
$buff[] = sprintf('$layout_info->menu_count = %d;', $menu_count);
$buff[] = '$layout_info->menu = new stdClass;';
for($i=0;$i<$menu_count;$i++)
{
$name = $menus[$i]->attrs->name;
if($menus[$i]->attrs->default == "true") $buff[] = sprintf('$layout_info->default_menu = %s;', var_export($name, true));
$buff[] = sprintf('$layout_info->menu->%s = new stdClass;', $name);
$buff[] = sprintf('$layout_info->menu->%s->name = %s;', $name, var_export($menus[$i]->attrs->name, true));
$buff[] = sprintf('$layout_info->menu->%s->title = %s;', $name, var_export($menus[$i]->title->body, true));
$buff[] = sprintf('$layout_info->menu->%s->maxdepth = %s;', $name, var_export($menus[$i]->attrs->maxdepth, true));
$buff[] = sprintf('$layout_info->menu->%s->menu_srl = $vars->%s;', $name, $name);
$buff[] = sprintf('$layout_info->menu->%s->xml_file = "./files/cache/menu/".$vars->%s.".xml.php";',$name, $name);
$buff[] = sprintf('$layout_info->menu->%s->php_file = "./files/cache/menu/".$vars->%s.".php";',$name, $name);
}
} }
} }
else foreach ($xml_info->menu ?: [] as $key => $value)
{ {
// Layout title, version and other information if (isset($vars->{$key}) && $vars->{$key})
$date_obj = new stdClass;
sscanf($xml_obj->author->attrs->date, '%d. %d. %d', $date_obj->y, $date_obj->m, $date_obj->d);
$date = sprintf('%04d%02d%02d', $date_obj->y, $date_obj->m, $date_obj->d);
$buff[] = sprintf('$layout_info->layout = %s;', var_export($layout, true));
$buff[] = sprintf('$layout_info->path = %s;', var_export($layout_path, true));
$buff[] = sprintf('$layout_info->title = %s;', var_export($xml_obj->title->body ?? null, true));
$buff[] = sprintf('$layout_info->description = %s;', var_export($xml_obj->author->description->body ?? null, true));
$buff[] = sprintf('$layout_info->version = %s;', var_export($xml_obj->attrs->version ?? null, true));
$buff[] = sprintf('$layout_info->date = %s;', var_export($date, true));
$buff[] = sprintf('$layout_info->layout_srl = $layout_srl;');
$buff[] = sprintf('$layout_info->layout_title = $layout_title;');
// Author information
$buff[] = '$layout_info->author[0] = new stdClass();';
$buff[] = sprintf('$layout_info->author[0]->name = %s;', var_export($xml_obj->author->name->body ?? null, true));
$buff[] = sprintf('$layout_info->author[0]->email_address = %s;', var_export($xml_obj->author->attrs->email_address ?? null, true));
$buff[] = sprintf('$layout_info->author[0]->homepage = %s;', var_export($xml_obj->author->attrs->link ?? null, true));
// Extra vars (user defined variables to use in a template)
$extra_var_groups = $xml_obj->extra_vars->group;
if(!$extra_var_groups) $extra_var_groups = $xml_obj->extra_vars;
if(!is_array($extra_var_groups)) $extra_var_groups = array($extra_var_groups);
$extra_var_count = 0;
$buff[] = '$layout_info->extra_var = new stdClass;';
foreach($extra_var_groups as $group)
{ {
$extra_vars = $group->var; $value->menu_srl = $vars->{$key};
if($extra_vars) $value->xml_file = sprintf('./files/cache/menu/%s.xml.php', $vars->{$key});
{ $value->php_file = sprintf('./files/cache/menu/%s.php', $vars->{$key});
if(!is_array($extra_vars)) $extra_vars = array($extra_vars);
$count = count($extra_vars);
$extra_var_count += $count;
for($i=0;$i<$count;$i++)
{
unset($var, $options);
$var = $extra_vars[$i];
$name = $var->attrs->name;
$buff[] = sprintf('$layout_info->extra_var->%s = new stdClass();', $name);
$buff[] = sprintf('$layout_info->extra_var->%s->group = %s;', $name, var_export($group->title->body ?? null, true));
$buff[] = sprintf('$layout_info->extra_var->%s->title = %s;', $name, var_export($var->title->body ?? null, true));
$buff[] = sprintf('$layout_info->extra_var->%s->type = %s;', $name, var_export($var->attrs->type ?? null, true));
$buff[] = sprintf('$layout_info->extra_var->%s->value = $vars->%s ?? null;', $name, $name);
$buff[] = sprintf('$layout_info->extra_var->%s->description = %s ?? null;', $name, var_export($var->description->body ?? null, true));
$options = $var->options;
if(!$options) continue;
if(!is_array($options)) $options = array($options);
$options_count = count($options);
for($j=0;$j<$options_count;$j++)
{
$buff[] = sprintf('$layout_info->extra_var->%s->options[%s] = new stdClass;', $var->attrs->name, var_export($options[$j]->value->body, true));
$buff[] = sprintf('$layout_info->extra_var->%s->options[%s]->val = %s;', $var->attrs->name, var_export($options[$j]->value->body, true), var_export($options[$j]->title->body, true));
}
}
}
}
$buff[] = sprintf('$layout_info->extra_var_count = %d;', $extra_var_count);
// Menu
if($xml_obj->menus->menu)
{
$menus = $xml_obj->menus->menu;
if(!is_array($menus)) $menus = array($menus);
$menu_count = count($menus);
$buff[] = sprintf('$layout_info->menu_count = %d;', $menu_count);
$buff[] = '$layout_info->menu = new stdClass();';
for($i=0;$i<$menu_count;$i++)
{
$name = $menus[$i]->attrs->name;
if($menus[$i]->attrs->default == "true") $buff[] = sprintf('$layout_info->default_menu = %s;', var_export($name, true));
$buff[] = sprintf('$layout_info->menu->%s = new stdClass();', $name);
$buff[] = sprintf('$layout_info->menu->%s->name = %s;', $name, var_export($name, true));
$buff[] = sprintf('$layout_info->menu->%s->title = %s;', $name, var_export($menus[$i]->title->body ?? null, true));
$buff[] = sprintf('$layout_info->menu->%s->maxdepth = %s;', $name, var_export($menus[$i]->maxdepth->body ?? null, true));
$buff[] = sprintf('$layout_info->menu->%s->menu_srl = $vars->%s;', $name, $name);
$buff[] = sprintf('$layout_info->menu->%s->xml_file = "./files/cache/menu/".$vars->%s.".xml.php";',$name, $name);
$buff[] = sprintf('$layout_info->menu->%s->php_file = "./files/cache/menu/".$vars->%s.".php";',$name, $name);
}
} }
} }
// header_script
$layout_config = ModuleModel::getModulePartConfig('layout', $layout_srl); $layout_config = ModuleModel::getModulePartConfig('layout', $layout_srl);
$header_script = trim($layout_config->header_script ?? ''); $xml_info->header_script = trim($layout_config->header_script ?? '');
$xml_info->layout_srl = $layout_srl;
$xml_info->layout_title = $layout_title;
if(!empty($header_script)) Rhymix\Framework\Storage::writePHPData($cache_file, $xml_info, null, false);
{ return $xml_info;
$buff[] = sprintf(' $layout_info->header_script = %s; ', var_export($header_script, true));
}
FileHandler::writeFile($cache_file, '<?php if(!defined("__XE__")) exit(); ' . join(PHP_EOL, $buff));
if(FileHandler::exists($cache_file)) include($cache_file);
if(!$layout_info->title)
{
$layout_info->title = $layout;
}
return $layout_info;
} }
/** /**