65.9K
CodeProject 正在变化。 阅读更多。
Home

Joomla 远程 SQL 调用

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.20/5 (3投票s)

2015年8月28日

CPOL

5分钟阅读

viewsIcon

16062

downloadIcon

103

这是一个 Joomla 组件,它允许通过 HTTP 使用 XML 消息远程执行 SQL。

目录

本文的第二部分,包含 VB.NET 客户端,现已发布。 第二部分

引言

这是两篇文章中的第一篇,介绍了一种自动化 Joomla 任务或基于 Joomla 的应用程序的方法。

我经常处理托管的 Joomla 网站,并且很多时候我不得不将一个或多个公司应用程序与之集成。

一个真实的例子是公司 ERP 与 Virtuemart(一个基于 Joomla 的电子商务解决方案)的集成:每天有数百种产品需要在线发布,并且每天有数百种产品会多次更改价格。
此外,处理托管网站通常意味着无法直接访问数据库。

我创建了一个 Joomla 组件,该组件可以接收远程命令并将其发行到底层数据库,并支持事务。

架构

架构很简单;我创建了一个 Joomla 组件,该组件发布了一个处理 XML 格式入站消息的页面。它执行 XML 消息中包含的命令,并发送回一个 message 响应。

消息交换不应用任何标准;没有 SOAP 也没有其他 RPC 协议。我创建了一个专有的基于 HTTP 的协议。

安全

该页面是公开的。因此,如果没有安全检查,它可能会造成严重的安全漏洞。为此,我引入了一个基于密钥交换的简单安全机制。

组件安装后,它会生成一个唯一的安全令牌。此安全令牌必须存在于请求参数中才能被服务器接受。

看点

Joomla 组件同时实现了管理面板和站点部分。
管理面板显示必须使用的安全密钥,而站点页面仅处理 XML 请求。

我不解释如何创建 Joomla 组件,因为已经有很多关于此主题的教程(您可以从 此处 开始)。

有趣的点是使用安装后脚本生成唯一的安全密钥以及处理 XML 请求的站点页面。

安装后脚本

在 Joomla 安装包中,名为 “component.xml” 的 XML 文件包含了所有安装说明。指令 <scriptfile>script.php</scriptfile> 指示安装过程在安装包根文件夹中找到名为 component_nameInstallerScript 的类,该类位于 script.php 文件中。

此类的一些明确定义的 public 方法会在安装过程中被调用

  • function install($parent) 在安装过程中被调用。在这里,您可以指定在此阶段执行的附加步骤,
  • function uninstall($paent) 在卸载过程中被调用
  • function update($parent) 在更新过程中被调用
  • function preflight($type, $parent) 在安装和更新之前被调用
  • function postflight($type, $parent) 在安装和更新之后被调用

您可以 在此处 找到更多信息。

我使用 postflight 函数来编写一个包含安全令牌的 XML 文件

function postflight($type, $parent)
{
    $string = '<sec><token>'.md5(uniqid(rand(), true)).'</token></sec>';
    $xml = new SimpleXMLElement($string);
    $xml->asXML(JPATH_SITE.'/components/com_sqlxml/sec.xml');
}

站点部分

站点部分由一个只处理 XML 输出的控制器管理。请求必须指定 URL 参数 “format=xml”。
它注册了一个名为 “cmdep.execcmd” 的函数,该函数处理请求并准备要发送回的 XML 响应。

该函数的第一步是安全检查。如果令牌不匹配,它会回复错误。

安全令牌验证后,该函数读取请求的内容,并为指定的每个命令执行所需的操作。

让我们看看消息结构

<?xml version="1.0" encoding="utf-16"?>
<msg-req xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <commands>
    <command type="sql" action="select" 
    mandatory="false"><![CDATA[]]></command>
    <command type="sql" action="select" 
    mandatory="false"><![CDATA[]]></command>
  </commands>
</msg-req>

如您所见,msg-req 包含一个 commands 集合,然后是一个或多个 command
所有过程都包含在一个事务中;每个 command 都可以指定是否参与事务。

一个 command(现在只能是 SQL 语句)可以指定一些参数

  • “mandatory” 参数指示 command 是否必须成功终止才能完成事务。如果设置为 true 且命令失败,则事务将回滚。
  • action” 参数指示要执行的 SQL 语句的类型。
    支持的类型有 “INSERT”、“UPDATE”、“DELETE”、“SELECT”、“CALL”。
    参数值与命令的实际内容之间没有语义检查,此参数仅管理 SQL 命令的执行方式及其结果的返回方式。因此,“INSERT” 命令将返回最后插入的自动生成 ID,而 “UPDATE” 或 “DELETE” 命令将返回受影响的行数。
  • save” 参数用于指定一个变量名,在该变量名中临时保存返回值,以便在下一个命令中使用。

看下面的例子

TABLE1” 有 3 个字段:“ID” 是一个自动生成的数字字段,也是主键,后跟两个文本字段 “FIELD1” 和 “FIELD2”。

TABLE2” 也有 3 个字段:一个自动生成的 “ID”、“REF_ID”(引用 “TABLE1” 中的 “ID” 字段)和一个文本类型的 “FIELD1”。

如果您需要在这些表中插入相关记录,您需要创建 2 个 INSERT 命令(mandatory,以便只有当两个命令都成功执行时,事务才提交),第一个命令将返回值保存在名为 “SAVED_ID” 的变量中,第二个命令在下一个 SQL 语句中以 “$[SAVED_ID]” 的形式引用该变量。

<command type="sql" action="insert" mandatory="true" 
save="ID"><![CDATA[INSERT INTO TABLE1 (FIELD1, FIELD2) 
VALUES ('VALUE1', 'VALUE2')]]></command>
<command type="sql" action="insert" 
mandatory="true"><![CDATA[INSERT INTO TABLE2 
(REF_ID, FIELD1) VALUES ($[SAVED_ID], 'VALUE2')]]></command>

当第二个命令执行时,所有形式为 $[...] 的参数都将替换为相应的值。因此,$[SAVED_ID] 的值已被第一个语句中保存的值替换。

在 PHP 中,通过使用关联数组实现了此参数替换的管理。
返回值保存在一个全局数组中,使用 “saved” 参数的值作为键。

$res_values = array();
$cmd_attr = $cmd->attributes(); //xml message command attributes
$save = (string)$cmd_attr['save'];
 if ($save!=null) 
       $res_values[$save]=$query_result[0];

然后,通过查询数组来替换 SQL 语句中所有匹配 $[…] 的参数。

$tmp = (string)$cmd; //Use a temp string to store the sql command
//use a regular expression to find all $[…] parameters. 
//the first matching group is the complete parameter string "$[named_param]"
//the second matching group is the parater name only "named_param"
$ct = preg_match_all("/\\$\[((?:\[\S*\]|[^\[])*)\]/", $tmp, $arr);
//for each parameter found proceed with values substitution.
for($i=0; $i<$ct; $i++){
  $tmp = str_replace($arr[0][$i], $res_values[$arr[1][$i]], $tmp);
}

结论

当您无法直接访问托管数据库时,此组件非常有用。

您可以使用任何编程语言利用事务管理来自动化您的 Joomla 任务。

在下一篇文章中,我将展示一个 VB.NET 客户端,该客户端将探索该组件的一些功能。

更新

2015/09/09 - 发布了文章的第二部分。 第二部分

© . All rights reserved.