理论计算机科学电子笔记157(2006)193-205www.elsevier.com/locate/entcs验证脚本化网页R G Stone博士1拉夫堡大学计算机科学系Leicestershire,LE11 3TU,England摘要根据DTD对XML文档进行验证是很容易理解的,并且存在完成此任务的工具但这里考虑的问题是XML文档生成器的验证。期望的结果是为特定的生成器建立它不能产生无效输出。许多(X)HTML网页是从包含用PHP等语言编写的嵌入式脚本的文档生成的。现有的工具可以验证从文档生成的XHTML然而,没有工具可以验证文档本身,保证所有可能生成的实例都是有效的。一个原型验证工具的脚本文件已经开发出来,它使用的符号开发,以捕捉一般输出的文件和系统增强的DTD。关键词:验证,XHTML,WML,PHP,DTD.1引言根 据 DTD 验 证 静 态 网 页 可 以 通 过 某 些 浏 览 器 ( 例 如 InternetExplorer[1]),基于Web的服务(例如W3C[2],WDG[3])和商业产品(例如CSE HTML Validator[4])实现。然而,存在数以百万计的动态Web文档,使用PHP[5]等语言编写脚本,每次浏览时都能够生成不同的XML页面,但没有方法可以验证源文档本身。1电子邮件:R.G. lboro.ac.uk1571-0661 © 2006 Elsevier B. V.在CC BY-NC-ND许可下开放访问。doi:10.1016/j.entcs.2005.12.055194R.G. Stone/Electronic Notes in Theoretical Computer Science 157(2006)193验证网页生成器的问题已经被各种研究人员通过构建不可能有无效输出的受控环境来解决这是通过JWIG中的[6]例如,或者通过设计和使用专用语言,如XDuce[7]和CDuce[8]。语言XDuce和CDuce都起源于ML。它们都将XML树数据结构作为内置的原始数据类型,并具有(非常)严格的编译时类型检查,可以从DTD或Schema派生。OCaml[9]的一个扩展的初步版本最近已经发布,其中包含CDuce类型。由Galaxy[11]实现的XQuery[10]具有强大的编译时类型检查,确保查询 不 会 引 发 任 何 类 型 错 误 。 Haskell 社 区 发 展 了 WASH[12] 和HaXML[13]。微软发布了一个名为Cω(C-omega)的C#版本,其中包含XML的数据类型扩展。这些系统可以为那些能够并愿意采用新策略的人巧妙地解决验证问题,可能需要转向新的编程语言。但是,他们没有提供一个立即解决遗留问题的用户继续使用旧的脚本语言,这是在本文中的广告。这些新系统是否特别适合作为本文中提出的系统的实现语言的单独问题将在后面的章节中讨论本文讨论了遗留问题的验证文件脚本语言没有内置的验证功能或检查。为了演示,使用的示例将是PHP生成WML,但使用的技术同样适用于其他脚本语言和其他XML兼容语言,特别是XHTML。2嵌入式脚本包含服务器端脚本的网页在传递到浏览器之前必须执行脚本。有几种服务器端脚本语言(PHP[5],ASP[15],Perl[16]等)。最简单的情况是,服务器端脚本语言通过echo或print命令生成输出。脚本元素通常嵌入在标记文本中,因此使用PHP生成最小WML页面的代码如下所示
<?PHPecho“card>";echo“/card>"?>R.G. Stone/Electronic Notes in Theoretical Computer Science 157(2006)193195<产品介绍在这个例子和后面的例子中,所需的<?公司简介 报头和<!DOCTYPE wml.> 为了简洁起见,省略了标题行。 还要注意的是,PHP代码是写在'括号'中的,<?PHP... ?>的常用缩写<?... ?>不兼容XML。3根据DTD进行本文的背景是使用脚本来交付根据文档类型定义(DTD)[17]有效的XML页面。DTD描述了可以使用的标记、它们的属性以及标记所包含的内容。例如,WML DTD[18]的简化摘录可以显示为<!ELEMENT wml(card+)><!ELEMENT卡( p*)><!ELEMENT p( #PCDATA)*>这个DTD符号可以读作如下。要使一个文档成为有效的WML文档,必须有一个wml元素,该元素必须包含至少一个(+)card元素。每个卡元素可以包含零个或多个(*)段落要素(p)。最后,每个段落元素可以包含一个任意数量的标记元素)。定义属性结构的DTD部分没有显示。上一节中脚本的输出将是 card>/card>/wml>因此,这是可以接受的,并被视为行使了没有(p)款要素的权利。4脚本化网页下面是一个PHP脚本的例子,它包含一个结构化语句(一个循环)<卡><?196R.G. Stone/Electronic Notes in Theoretical Computer Science 157(2006)193public void run($i){“p>";...“/p>";$i++;}?>卡><产品介绍我们可能会非正式地争论,无论$limit的值是多少,这个脚本的结果都是有效的WML,因为while循环在执行时总是生成成对的段落标记(,
),而标记接受任何数量的这样的对(包括没有)。一种更正式的方法是使用从正则表达式中借用的星号标记来< card>(.
)*/card>/wml>这可以理解为将输出描述为包含卡片标签的wml标签,而卡片标签又包含零个或多个段落标签。正是这个输出表达式根据WML DTD进行“检查”。wml元素只包含一个card元素(允许1个或多个),而card元素包含零个或多个paragraph元素(允许零个或多个)。 使用正则表达式表示法从脚本中捕获一般化输出的想法将在下面进一步发展。但是,该表示法被转换为XML样式,因此结果仍然可以通过用额外规则扩充原始表示法而获得的DTD进行验证。因此 (.& lt;/p>)*
将成为< card>< plist0>< p>.& lt;/p>其他发明的标签,如,最终将需要,他们将被称为Meta标签。5一般化输出和扩充DTD设想一种系统,其中脚本化的网页通过处理器以获得广义输出表达式,然后针对DTD验证广义输出表达式,该DTD是通过用涉及Meta标签的规则扩充原始DTD而获得的。脚本语言中的各种重复和选择控制结构将重新R.G. Stone/Electronic Notes in Theoretical Computer Science 157(2006)193197询问适当的Meta标记来描述它们对一般化输出表达式的贡献。这些总结见表1。与DTD中使用的正则表达式操作符的对应关系(见同一个表)将提供对如何扩充DTD以接受元标记的深入了解。继续上一节中的示例,如果脚本化的while循环生成了 p>.& lt;/p>/p list0>DTD将需要扩充,以接受它作为
. lt;/p> p>...lt;/p>.
.联系我们对于本例,将DTD中所有出现的p* 替换为(p*| p_list0)并添加定义<!ELEMENTp_list0(p)>概念RegExp程序控制示例代码元标签0,1,2,...*while循环while().1,2,3,...+重复循环做... while()选项?短条件if().选择|长条件if().别的...表1正则表达式运算符、程序控制结构和元标记之间的对应关系表然而,到目前为止只考虑了最简单的情况,即完全由简单的while循环创建的文字段落元素序列。在更一般的情况下,可以编写脚本来使用文字标记、循环和条件的任何混合来生成段落元素的序列。下面的例子更现实,因为它通过一个包含文字、循环和条件的序列创建了<卡><?echo“p>.</p>";而(...){echo“p>.</p>";}198R.G. Stone/Electronic Notes in Theoretical Computer Science 157(2006)193如果(.)echo“p>.</p>";?>卡><产品介绍在这种情况下,通用输出表达式如下所示:<卡>.联系我们.联系我们<联系我们.联系我们
卡><产品介绍为了表达这种普遍性,引入了实体p0,因此DTD中的p被(%p 0;)替换,定义为<!实体%P0(p| p_list0| p_list1| p选项|p_choices)>在这个定义下,(%p 0;)p0表示一个零个或多个元素的序列,每个元素都贡献零个或多个段落元素。这个规则必须对所有的tags(t)重复执行,这样,在DTD中出现t的地方,它将被替换为%t.star;。<!实体% t.star(%t0;)*><!实体Y%t0(t|t_list0|t_list1|t_option|t_choices)>请注意,在(|...)|...) *,其中*适用于包括t在内的各种替换,t也应该由实体%t0替换。6扩充的DTD到目前为止,所有对DTD的更改都是由于输出表达式,通过替换DTD中出现显然,现在还需要看看DTD的其他部分可 能 需 要 增 强 。 签 名 输 出 为 “one or more” 的 重 复 循 环 带 有 签 名“optional”输出的短条件语句(没有else部分)在DTD中。长R.G. Stone/Electronic Notes in Theoretical Computer Science 157(2006)193199条件语句(带有else部分)有一个签名 t_choice>.这...... lt;/t_choice>.或者这个lt;/t_choice>/t_choices>一个长条件语句会导致替换t的任何未修饰的实例(即在没有任何运算符'','+','?'的情况下出现t因为正在选择单个标签T在DTD中替换t+比替换tbe-更复杂,因为必须确保至少有一个带标签t的元素存在。在详细考虑替代之前,请比较以下四个实体定义:(i) 零次或多次出现元素t、t0(之前介绍过)<!实体 % t0(t| t_list0| t_list1| t选项|t_choices)>(ii) 元素t、t1的一次或多次出现<!实体% t1(t| t选择|t_list1)>(iii) 元素t、t01<!实体% t01(t| t选项|t_choices)>(iv) 恰好一个元素t,t11<!实体% t11(t| t_choices)>现在可以在定义下用实体t.plus替换t<!实体% t.plus((t_option| t_list0)*,%t1;,% t.star;)>这可以理解为将t.plus定义为零个或多个不能依赖于包含t标记的元素,后跟一个绝对包含至少一个t标记的元素,后跟零个或多个将贡献零个或多个t标记的替代T?在DTD中,实体t01的定义已经给出。t的替代是具有已经给出的定义的实体t11对DTD进行的替换总结在表2中。为了支持这些替换,需要添加如上定义的新实体t_star、t_plus、t0、t1、t01和t11,最后是描述每个标签t的派生标签t list0、t list1、t option、t choices和t choices的新元素规则。<!ELEMENTt_list0% t.star;><!ELEMENTt_list1% t.plus;><!ELEMENTt_option %t01;>200R.G. Stone/Electronic Notes in Theoretical Computer Science 157(2006)193DTD短语替换请登录t.star;(...)|...|... ). |...|... )t +% t.plus;(.|...|... )+(. |...|... )+t?%t 01;t%t 11;表2DTD中的替换表<!元件t选择(t_choice,t_choice)><!ELEMENTt_choice %t11;>请注意,当没有元标记时,扩充规则不会改变DTD的含义。例如,如果将t替换为t 0,并且将t 0定义为(t|t列表0|测试列表1 |t选项|t个选择),则在不存在元标签(t个列表0,t个列表1,t个选项,t个选择)的情况下,替换退化回t个选项。在原型中,扩充DTD的过程由一个序言程序处理,该程序读取原始DTD,生成额外的ELEMENT定义和ENTITY定义,并输出扩充的DTD。这在SWI-prolog[21]中通过使用预写的模块[22]来读取DTD变得更容易。7脚本处理器前面提到,脚本验证系统由两部分组成。 第一部分必须处理脚本,引入元标记并生成通用输出表达式。第二部分根据扩充的DTD验证输出表达式。在原型中,第一部分,脚本处理器,本身被分成两个阶段。脚本处理器首先使用一般的元标记(如list0、list1、option和choices)生成一个输出表达式。第二阶段检查第一阶段的输出,并插入正确的标签,将其更改为特定的元标签,如p list0,card option。在当前的实现中,脚本处理器的第一阶段是用C编写的,使用LEX[19]和YACC[20]来解析脚本,这个阶段R.G. Stone/Electronic Notes in Theoretical Computer Science 157(2006)193201生成包含通用元标记的输出表达式。 例如 .& lt;/p>
第二阶段是用prolog编写的,并生成特定的元标记,例如< card>< plist0>< p>.& lt;/p>8当前实现当前生成WML和XHTML的PHP脚本的实现在大量脚本上运行良好但是,如果它无法验证脚本,则脚本不一定能够发出无效输出。 弱点是插入元标记的第一阶段。问题在于假设脚本语言中的控制结构将生成能够由元标记描述的完整标记结构。这并不总是发生。举例说明这一点,echo“";echo“0”;while(.){返回“/p>";返回“p>";返回“1”;}“/p>";对于任何特定的执行,此脚本将导致如下序列
0
1
1
1.
这是有效的。但是,它将被赋予以下元标记 0 list0>/p> 1/list0>/p>这个表达式中的标签没有被正确嵌套,在过程的第二阶段(用特定的元标签替换一般的元标签)失败,因为输入阶段假设输入是格式良好的XML。已经开始在处理器中引入额外的中间级,它使用的规则如下:ab(cab)*c => abc(abc)*=>(abc)+因此,上述示例可以被操作以
0/p> list0> p> 1/p>/list0>202R.G. Stone/Electronic Notes in Theoretical Computer Science 157(2006)193这样做的问题是,起始表达式不是有效的XML,这正是因为标记没有正确嵌套,因此表达式不能作为XML文档读取和操作。这意味着操作必须通过将表达式仅视为开始标记、结束标记和非标记元素的线性混合来完成。这使得处理更困难,但并不棘手。当前的代码存在一个更严重的问题,它用特定的元标记替换了目前,如果处理器满足开放tag它检查所有的顶级标签直到结束
tag,希望它们都是相同的类型(tsay),这样一般的标签可以更改为。 但情况并非总是如此,如下例echo“";while(.){echo“ul>.& lt;/ul>";echo“br/>";}“/p>";该处理器具有 ul>.& lt;/ul> br/>/list0>并且找不到标记名t来将更改为。对此有潜在的解决方案一个是,通过引用DTD,可以更改标签的范围,从而: ul>.& lt;/ul>/list0> list0> br/>/list0>尽管这会改变表达式的含义,但如果DTD包含一条规则,<!ELEMENT p(.| ul|...| br|...)* >>该改变将不会改变表达式的有效性,因此对新表达式的有效性检查将获得期望的结果。在实践中,在许多类似的情况下,程序员可以通过在循环中添加一个封闭的或标记来另一个问题在于处理器第一级的简单性因为它本质上主要是语法的,所以它实际上并不执行脚本语言。这意味着,如果脚本通过打印文字以外的任何其他方法生成任何标记(例如,通过字符串连接构建它们或作为数据库查找的一部分获取它们),则这些标记将不会在一般化输出中表示,R.G. Stone/Electronic Notes in Theoretical Computer Science 157(2006)193203这些标签将不被验证。9替代实施战略本文讨论了脚本化文档,这些脚本化文档旨在根据某些DTD生成有效的XML作为输出。 在引言中,简要讨论了通过编程语言处理XML文档的新系统。可以考虑将这些作为本文中介绍的思想的 这些新系统致力于XML文档,以至于任何数据文档也将被要求至少是格式良好的XML文档。 PHP语言允许将脚本嵌入到<...?> 还是<?php.?> 但只有第二种是XML顺从 因此,至少需要一个预处理器来重写将源文档的封闭标记转换为XML格式。在此之后,PHP脚本文档的输入可以在一个步骤中完成(例如使用CDuce,通过将文件名作为参数提供给内置函数load xml,可以读入脚本文档script.php)。然而,PHP命令的文本将被保存为php标记的PCDATA内容,因此没有任何结构。仍然需要编写一个PHP解析器来检查脚本的细节。此外,由于PHP脚本的编写方式,结构化语句的开头部分(例如,而 (...)(可以很容易地在一个不同的标签中,以结束部分(}),如下面的例子因此而不仅仅是单独解析各个PHP标记的问题
卡>.联系我们<?php while(.)“什么?”
.联系我们<?php }?>/wml>新语言有强大而优雅的方法将输入XML树转换为输出XML树,这在以后的过程中非常有用。然而,强大的编译时类型检查会在进程需要 即 时 创 建 标 记 的 地 方 引 起 问 题 。 回 想 一 下 , 需 要 将 T>stuff/T>/list 0>转换为 T>stuff/T>/T_list0>对于任何标签T。这种标签的构造name out of pieces(T扩展为list0)正是类型检查系统设计用来防止的事情。可以设计变通方案,或者单独处理每个可能的标签(p到p列表0,h1到h1列表0等),或者将新标签T列表0表示为一对(T,列表0)。的204R.G. Stone/Electronic Notes in Theoretical Computer Science 157(2006)193第一种可能性将极大地扩展所需的代码量,而第二种可能性将需要通过打印例程进行最终转换,以将例如(p,list0)的对转换为字符串plist0。但它们仍然是解决方法,并且正在破坏语言设计的目的结论是,新的语言可以(并且确实)以一种非常优雅的类型安全方式处理XML树的普通转换然而,这种Meta级别的编程(从输入数据的组件构造元标记)目前似乎不能以类型安全的方式处理。另一个普遍的问题是,该技术是否适用于模式的验证,这代表了一个比DTD新的标准对DTD设计的一个明显的从广义上讲,模式是XML格式的DTD,具有更好的输入XML树叶子的功能。因此,原则上没有理由提出的技术不适用于模式验证。10总结验证脚本化网页而不是其输出的概念被认为是新颖的,并且可能非常有用,至少对于使用该技术的大量遗留站点来说是如此。已经发现了一种验证这种脚本的方法,该方法依赖于处理脚本以提供通用的输出表达式,然后根据增强的DTD验证该输出表达式。该方法已经为生成WML和XHTML的PHP脚本原型化。该方法可容易地应用于过程脚本语言和基于XML的输出的任何其他组合。虽然该方法可以验证大量的脚本,但它也有其局限性。产生通用输出表达式的处理器必须能够识别脚本在哪里生成标签。当前的原型类型要求这些是echo/print命令中的文本,而不是被字符串操作符或数据库查找所隐藏。当前的原型还需要脚本中的控制语句来生成格式良好的XML,尽管有计划扩展处理器以适应非格式良好的输出,在这种情况下,可以应用从正则表达式等效项派生的特殊规则。R.G. Stone/Electronic Notes in Theoretical Computer Science 157(2006)193205引用[1] 使用Internet Explorer作为验证程序:http://www.w3schools.com/dtd/dtdvalidation.asp。[2] W3C验证服务:http://validator.w3.org/。[3] WDG验证服务:http://www.htmlhelp.com/。[4] CSE验证器:http://www.htmlvalidator.com。[5] PHP主网站:http://www.php.net。[6] JWIG:“Extending Java for High-Level Web Service Construction”,A. S.克里斯滕森,A.Moller和M.I. Schwartzbach,ACM程序设计语言与系统汇刊第25卷第6期(可在网站上查阅:http://portal.acm.org/citation.cfm?电话:+86-0591 - 88999999看到也可参见:http://www.brics.dk/JWIG)。[7] CDuce:An XML-Centric General-Purpose Language,V. Benzaken,G. Castagna和A.Frisch,Proceedings of the ACM International Conference on Functional Programming,2003。(另见网络参考:http://www.cduce.org/)。[8] XDuce:A typed XML processing language,H. Hosoya和B. C. Pierce,ACM Transactionson Internet Technology,3(2):117-148,2003。(see也可参考网络:http://xduce.sourceforge.net/)。[9] OCamlweb参考:http://www.cduce.org/ocaml.html。[10] XQuery Web参考:http://www.w3.org/TR/xquery/。[11] 银河网络参考:http://www.galaxquery.org/。[12] WASH(Web Authoring System for Haskell)网址:http://haskell.org/hawiki/WaSh。[13] HaXML(使用Haskell解析、过滤、转换和生成XML文档的实用程序)。Web参考:http://www.cs.york.ac.uk/fp/HaXml/。[14] 科梅加网址:http://research.microsoft.com/Comega/。[15] ASP Web参考:http://msdn.microsoft.com/asp/。[16] PERL网络参考:www.example.com。[17] DTDweb参考:http://www.w3schools.com/dtd/default.asp。[18] WML DTD网络参考:http://www.wapforum.org/DTD/wml11.dtd。[19] LEX,Unix程序员手册(另请参阅网络参考:http://dinosaur.compilertools.net/网站。[20] YACC,Unix Programmers Manual(另请参阅Web参考:http://dinosaur.compilertools.net/网站。[21] SWI-Prolog网络参考:http://www.swi-prolog.org/。[22] SWI-Prolog SGML/XML解析器包Web参考:http://www.swi-prolog.org/packages/sgml2pl.html网站。