dapepe/xily

Xily是一个轻量级的PHP框架,用于开发基于XML的应用程序。

1.0.5 2018-01-31 16:20 UTC

This package is not auto-updated.

Last update: 2024-09-18 19:09:07 UTC


README

目的

Xily是一个轻量级的PHP框架,用于开发基于XML的应用程序。该框架包含五个核心类,使工作变得可能。一个主要方面是使用类似DOM的对象模型表示XML数据。基于此模型,您可以使用Xily为每个XML标签添加单个解析器(xilyBeans),从而使开发人员能够创建模块化、XML驱动的Web应用程序。

一点历史

Xily是由Peter Haider在ZeyOS(http://www.zeyos.com)开发的。开发始于2008年,当时团队需要一个框架来开发网站和门户。当时我们正在查看许多基于XML的模板系统,如Adobe ColdFusion、Open Laszlo或Adobe Flex。使用XML描述前端部分的方法真的吸引了我们,我们认为我们可能希望为PHP拥有类似的东西。而且,由于每个项目可能都有不同的要求,因此将XML引擎扩展为新行为和标签会很好。

所有这些的结果是Xily,一个框架,使我们能够(a。)在PHP中处理XML文档,并且(b。)通过简单地封装称为Xily Beans的复杂对象和程序逻辑来开发完全基于XML的应用程序和网站。

为什么选择Xily?

轻量级

Xily专注于其核心功能——即利用XML。核心库总共由5个文件组成!

专注

Xily只有一个目的:在PHP中创建XML驱动的应用程序。您可以使用xilyXML库简单地处理XML文档,而xilyBeans则允许您通过开发自己的XML命令集来创建XML驱动的应用程序和网站。

可扩展

您可以随着项目的发展扩展和增强Xily。Xily不会强迫您采用任何特定的方法——您甚至可以创建自己的XML命令集。

主要组件

Xily XML

使用XML类处理XML结构。它可以用来解析XML文件/字符串,或动态地处理这些信息,或操纵XML结构。

Xily Bean

Bean类允许您将应用程序逻辑附加到特定的XML标签上,这意味着您可以实际定义自己的自定义XML命令面板。

Xily Dictionary

Dictionary类提供XML和Array之间的接口,以便在Bean类中将数组视为XML树。

Xily Config

Config类是一个Singleton,提供对全局配置设置的访问。

Xily Base

Base类提供由DictXMLBean类共享的基本实用方法。

测试和示例

Xily XML

您将在/test目录中找到一些测试用例。让我们看看如何处理XML文件的一个快速示例。

<?xml version="1.0" encoding="UTF-8"?>
<recipes>
	<recipe level="easy" cookingtime="10">
		<title>Pancakes</title>
		<ingredients>
			<item>200g flour</item>
			<item>2 eggs</item>
			<item>3 tea spoons of sugar</item>
			<item>100ml milk</item>
			<item>100ml water</item>
		</ingredients>
	</recipe>
	<recipe level="medium" cookingtime="20">
		<title>Barbecue steak</title>
		<ingredients>
			<item>250g steak</item>
			<item>5 potatoes</item>
			<item>Oil</item>
			<item>Pepper</item>
			<item>Salt</item>
			<item>Garlic</item>
			<item>Chilli</item>
		</ingredients>
	</recipe>
	<recipe level="easy" cookingtime="5">
		<title>Scrambled eggs</title>
		<ingredients>
			<item>3 eggs</item>
			<item>Pepper</item>
			<item>Salt</item>
		</ingredients>
	</recipe>
</recipes>

首先,我们将从文件中加载XML结构。

$xmlRecipes = Xily\XML::create('data/recipes.xml', true);

Xily XML使用类似于E4X的语法来选择结构中的节点。getNodeByPath函数获取所有与路径描述匹配的节点。此示例列出所有烹饪时间低于20分钟的食谱。

$arrRecipes = $xmlRecipes->getNodesByPath('recipe(@cookingtime < 30)');
foreach ($arrRecipes as $xmlRecipe)
	echo $xmlRecipe->trace('title');

如果您只想选择单个节点,可以使用getNodeByPath函数。这将选择烹饪时间低于30分钟且易于准备的第一个节点。

$xmlSomeRecipe = $xmlRecipes->getNodeByPath('recipe(@cookingtime < 30, @level == "easy")');

跟踪返回所需节点的值或属性,而不是节点本身。以下示例获取“title”节点的值。

$xmlRecipes->trace('recipe(@id == "soup").title');

这相当于

$xmlRecipes->getNodeByPath('recipe(@id == "soup")')->child('title')->value();

您还可以使用子路径作为选择器。让我们获取所有包含“盐”作为成分的食谱

$arrRecipes = $xmlRecipes->getNodesByPath('recipe(ingredients.item == "Salt")');

更多示例,请查看示例脚本 test/test.xml.php

Xily Bean

在开发基于XML的应用程序时,您不仅想使用XML来定义应用程序的结构和数据,还想定义其行为和表示。Xily Beans允许您将应用程序分解为单个、可重用的单元,并通过在XML结构中代表它们来访问这些单元。这样,您就可以直接在XML中开发应用程序,就像在Adobe Flex或OpenLaszlo中一样,因为实际的编码功能嵌入在您的Xily Bean类中。这样,您可以专注于诸如设计、架构和可用性等因素,而不是编码过程本身。

创建自定义Beans

让我们创建一个简单的示例。假设我们想创建一个简单的HTML表单。对于此示例,我们将使用Bootstrap CSS 3.0.2

这就是该表单在常规HTML中的样子

<form class="form-horizontal" role="form">
  <div class="form-group">
    <label for="txtFirstname" class="col-sm-2 control-label">First name <span class="glyphicon glyphicon-asterisk"></span></label>
    <div class="col-sm-10">
      <input type="text" class="form-control" name="txtFirstname">
    </div>
  </div>
  <div class="form-group">
    <label for="txtLastname" class="col-sm-2 control-label">Last name <span class="glyphicon glyphicon-asterisk"></span></label>
    <div class="col-sm-10">
      <input type="text" class="form-control" name="txtLastname">
    </div>
  </div>
  <div class="form-group">
    <label for="txtEmail" class="col-sm-2 control-label">E-mail</label>
    <div class="col-sm-10">
      <input type="email" class="form-control" name="txtEmail">
    </div>
  </div>
  <div class="form-group">
    <label for="txtRegion" class="col-sm-2 control-label">Region</label>
    <div class="col-sm-10">
      <select class="form-control" id="txtRegion" name="region">
        <option value="">- Please Select -</option>
        <option value="North America">North America</option>
        <option value="Central America">Central America</option>
        <option value="South America">South America</option>
        <option value="European Union">European Union</option>
        <option value="Eastern Europe">Eastern Europe</option>
        <option value="Africa">Africa</option>
        <option value="Middle East">Middle East</option>
        <option value="Asia">Asia</option>
        <option value="Oceania">Oceania</option>
        <option value="The Caribbean">The Caribbean</option>
      </select>
    </div>
  </div>
</form>

如您所见,有很多重复的HTML可以被轻松避免。此外,如果您想稍后更新Bootstrap,类名和HTML结构可能会发生变化怎么办?出于这个原因,创建一个用于表单元素的类更为高效。

写这样的东西不是更好吗?

<form:frame width="2">
	<form:field name="Firstname" label="First name" required="true" />
	<form:field name="Lastname" label="Last name" required="true" />
	<form:field name="Email" label="E-mail" type="email" />
	<form:field name="region" label="Region?" placeholder="- Please Select -">
		<option>North America</option>
		<option>Central America</option>
		<option>South America</option>
		<option>European Union</option>
		<option>Eastern Europe</option>
		<option>Africa</option>
		<option>Middle East</option>
		<option>Asia</option>
		<option>Oceania</option>
		<option>The Caribbean</option>
	</form:field>
</form:frame>

更少杂乱,对吧?为了实现这一点,我们只需向我们的项目中添加两个新的Bean类

class FormFrame extends Bean {
	public function result($xmlData, $intLevel=0) {
		if ($this->hasAttribute('left')) {
			if ($xmlData instanceof XML)
				$xmlData->setAttribute('left', $this->attribute('left'));
			else
				$xmlData = new XML('data', null, array('left' => $this->attribute('left')));
		}

		return '<form class="form-horizontal" role="form"'
				.($this->hasAttribute('id') ? ' id="'.$this->attribute('id').'"' : '')
				.($this->hasAttribute('action') ? ' action="'.$this->attribute('action').'"' : '')
				.($this->hasAttribute('target') ? ' target="'.$this->attribute('target').'"' : '')
				.($this->hasAttribute('style') ? ' style="'.$this->attribute('style').'"' : '')
				.'>'
				.$this->dump($xmlData, $intLevel+1)
				.'</form>';
	}
}

class FormField extends Bean {
	public function result($xmlData, $intLevel=0) {
		$widthLeft = 2;
		if ($this->hasAttribute('left'))
			$widthLeft = (int) $this->attribute('left');
		elseif ($xmlData instanceof XML && $xmlData->hasAttribute('left'))
			$widthLeft = (int) $xmlData->attribute('left');

		$widthRight = 12 - $widthLeft; // Bootstrap's grid system is based on 12 columns

		$id   = $this->attribute('id');
		$name = $this->attribute('name');

		if (!$id || $id == '') {
			if (!$name || $name == '')
				return; // Either a name or an ID will have to be specified

			$id = 'txt'.ucfirst($name); // Automatically create an ID based on the name
		} elseif (!$name || $name == '') {
			$name = $id;
		}

		$elem = '<div class="form-group">'
				.'<label for="'.$id.'" class="col-sm-'.$widthLeft.' control-label">'
				.$this->attribute('label')
				.($this->isTrue('required') ? '<span class="glyphicon glyphicon-asterisk" />' : '')
				.'</label>'
				.'<div class="col-sm-'.$widthRight.'">';

		if ($this->hasChildren()) {
			$elem .= '<select class="form-control" id="'.$id.'" name="'.$name.'">'
					.($this->hasAttribute('placeholder') ? '<option value="">'.$this->attribute('placeholder').'</option>' : '');

			foreach ($this->children() as $xmlChild) {
				if (!$xmlChild->hasAttribute('value'))
					$xmlChild->setAttribute('value', trim($xmlChild->value()));
				if ($this->hasAttribute('selected') && $this->attribute('selected') == $xmlChild->attribute('value'))
					$xmlChild->setAttribute('selected', 'selected');

				$elem .= $xmlChild->toString();
			}

			$elem .= '</select>';
		} elseif ($this->attribute('type') == 'multi') {
			$elem .= '<textarea class="form-control" name="'.$id.'" name="'.$name.'" rows="'.($this->hasAttribute('rows') ? $this->attribute('rows') : 3).'"></textarea>';
		} else {
			$elem .= '<input type="'.($this->hasAttribute('type') ? $this->attribute('type') : 'text').'"'
					.($this->hasAttribute('placeholder') ? ' placeholder="'.$this->attribute('placeholder').'"' : '')
					.' class="form-control" name="'.$id.'" name="'.$name.'" />';
		}

		return $elem.'</div></div>';
	}
}

这两个类自动生成整个表单。现在您有了可重用组件类,您可以像HTML标签一样对待它们。

为了使您更容易与不同的Bean库一起工作,Xily在解析XML文件时使用懒加载来包含所需的文件。您只需通过将它们添加到Bean类的BEAN_DIRS数组中,就可以包含您的bean目录。

\Xily\Bean::$BEAN_DIRS[] = LIB_DIR.'xily/src/beans';
\Xily\Bean::$BEAN_DIRS[] = LIB_DIR.'custombeans';

继续我们之前的例子:对于我们的自定义Beans <form:frame/><form:field/>,我们在custombeans目录中创建一个名为form的目录,并创建两个名为frame.phpfield.php的文件,每个文件都包含相应的Bean类。

使用Xily数据引用(XDR)

除了定义自定义Beans之外,Xily数据引用(XDR)也是Xily的一个重要方面。XDRs允许您动态地引用和访问文档中的数据。

让我们有一个简单的例子:您想创建一个小型门户页面,其中还想要显示您的博客的最新帖子,并在列表中优雅地显示它们。

<html>
	<repeat source="#{.get(http://feeds.bbci.co.uk/news/technology/rss.xml)->xml->channel.item}">
		<div>
			<h3>#{title}</h3>
			<p>#{description}</p>
			<a href="#{link}">Read all</a>
		</div>
	</repeat>
</html>

XDRs提供了多种方法来动态访问数据。有7种不同的XDR类型,以提供广泛的访问可能性

您还可以访问PHP的预定义变量

Xily Config

可以使用Config类来加载和访问全局应用程序配置设置。

您可以从JSON或INI文件加载配置设置,例如

Xily\Config::load('config.ini');

但是,您也可以使用load方法直接加载关联数组

Xily\Config::load([
	'general' => [
		'option1' => 'value1',
		'option2' => 'value2'
	]
]);

您可以使用get方法访问配置选项。当加载多维配置文件时,您可以使用简单的点连接选择单个节点,例如

Xily\Config::get('general.option1'); // Return "value1"

同样,您可以使用set方法更改或设置单个配置值,例如

Xily\Config::set('general.option1', 'new value');

《Config》类还包括初始化返回值的方法。例如,如果您的配置文件包含对目录的引用,您可能希望确保值在目录名称的末尾有一个尾随的反斜杠

Xily\Config::set('mydir', '/var/www');
Xily\Config::getDir('mydir'); // Return "/var/www/"

此外,您还可以使用 get 方法将特定变量类型进行类型转换,以及初始化默认值。

; config.ini
[general]
dir = "/var/www"
debug = 0

[numeric]
taxrate = "1,20"
discount = "0.60"

get 方法的附加参数中指定期望的变量类型(字符串、整数、浮点数、数组、布尔值、对象)和默认值

Xily\Config::load('config.ini');
Xily\Config::get('general.debug', 'bool', true); // Returns FALSE
Xily\Config::get('numeric.taxrate', 'float', 1.19); // Returns the default value 1.19
Xily\Config::get('numeric.discount', 'float', 0); // Return 0.6

更多示例,请查看示例脚本 test/test.config.php

贡献

目前,Xily 被许多不同的项目使用,因此我一直在努力改进和测试该框架。我从 2008 年开始开发 Xily,所以到目前为止,整体结构和功能已经得到了很好的测试。然而,我很乐意找到更多项目参与者。我特别希望得到以下方面的帮助:

  • 调试和测试
  • 添加更多示例和教程
  • 维护 Xily 网站
  • 编写新的 Xily Beans 以扩展整体功能

如果您有兴趣——我期待着您的消息!只需在 GitHub 上给我发消息。

许可协议

版权(C)2008-2016 彼得-克里斯托夫·海德尔

本作品受 GNU 较小通用公共许可证(LGPL)的许可,应随此软件一同提供。您也可以从 https://gnu.ac.cn/licenses/lgpl.txt 获取 GNU 较小通用公共许可证的副本。