salesforceeng / breakout
一个基于上下文的输出转义工具
Requires
- php: >=5.4.0
Requires (Dev)
- phpunit/phpunit: 4.1.4
This package is not auto-updated.
Last update: 2019-06-06 08:51:30 UTC
README
许可证: BSD-3条款
Breakout旨在提供上下文感知的转义。例如,为HTML转义的需求与JavaScript或URL值不同。该库还将处理UTF-8字符到它们的\uXXXX
版本(默认情况下会这样做,但可以通过在config
构造函数配置中传递false
值来禁用)。
默认情况下,它将尝试对通用HTML上下文进行转义,但您可以指定以下任何一个:
- html:用于页面内容的HTML输出处理
- htmlAttr:用于HTML标签属性的HTML编码
- js:转义字符串以包含在JavaScript字符串中(转换为
\xHH
实体) - url:对提供的数据进行URL编码(可以是字符串或数组)
- css:转义数据,删除键字符串并将某些值转换为
&#XX
格式
手动调用
您可以直接使用类(不是通过辅助器或文档渲染),通过创建对象并使用所选上下文的escape
方法。
<?php require_once 'vendor/autoload.php'; use SalesforceEng\Breakout\Breakout as Breakout; $b = new Breakout(); $str = 'this is a ☠ test with "something" here <script>alert("test");</script>'; echo 'html: '.$b->escape($str, 'html'); // result: this is a \u2620 test with "something" here <script>alert("test");<\/script> ?>
与Symfony(v1)一起使用
要在我们的Symfony安装中使用此库,需要做两个更改。
-
将文件
BreakoutHelper.php
移动到apps/frontend/lib/helper/BreakoutHelper.php
-
并更改
apps/frontend/configuration/settings.yml
配置文件,将其添加到标准辅助器列表中
standard_helpers: [Partial, Cache, Form, Javascript, UI, I18N, Breakout]
哦,记得刷新您的缓存...总是刷新您的缓存...
过滤类型
以下示例假设您正在使用上面提到的辅助库调用escape()
。
HTML
过滤默认为HTML上下文,因此不需要第二个属性
<?php echo escape('this is my <b>data</b>'); ?>
这将产生以下字符串:this is my <b>data</b>
。此替换执行了一个htmlspecialchars调用以过滤数据。这仅用于通用的HTML转义。
HTML属性
HTML属性需要一些不同的处理,因为它们是不同的上下文。下面是一个简单的示例,演示如何传递HTML属性字符串进行转义
<a href="" <?php echo escape('name="f\'test\'oo" onclick="error"', 'htmlattr'); ?>Sample Link</a>
产生的字符串是name="f"test"oo"
,引号被转换为它们的HTML实体。您还会注意到输出中缺少了onclick
属性。这是几个被列入黑名单的术语之一,因为JavaScript交互不应在HTML标签属性中发生。如果您确实需要覆盖它,您可以通过可选的第三个参数配置转义
<a href="" <?php echo escape('name="f\'test\'oo" onclick="error"', 'htmlattr', array('allow' => 'onclick')); ?>Sample Link</a>
JavaScript
JavaScript转义将字符转换为它们的实体并执行替换
<script> var foo = "<?php echo escape('te"this"sting', 'js'); ?>"; </script>
产生的输出是var foo = "te\x22this\x22sting";
,引号被正确转义。
CSS
CSS转义将所有非字母数字字符转换为它们的实体版本。所以
<div style="<?php echo escape("font-size:100px;color:red;font-weight:bold", 'css'); ?>">testing this</div>
经过转义后,结果为 <div style="font-size:100px;color:red;font-weight:bold">测试此内容</div>
。
URL
将数据转义,以便安全地用于URL字符串中(不是整个URL,只是用于查询字符串的数据)
<a href="/link.php?<?php echo escape(array('foo' => 'bar "this"'), 'url'); ?>">my link</a>
结果如下:
<a href="/link.php?foo=bar+%22this%22">my link</a>
辅助方法
作为 BreakoutHelper
的一部分,也存在辅助方法用于各种转义
<?php escapeHtml(...); escapeHtmlAttr(...); escapeCss(...); escapeJs(...); escapeUrl(...); ?>
这些方法都接受相同的两个选项:要转义的数据 $data
和额外的选项数组 $config
。
文档转义
Breakout 还具有转义“文档”(提供的字符串)并使用请求的转义替换占位符的能力。例如
<?php $data = array( 'html1' => '<b>testing</b>', 'js1' => 'te"this"sting', 'css1' => 'font-size:100px;color:red;font-weight:bold' ); $document = "this is a test of the document rendering.\n{{ js1|js }} and\n {{ css1|css }} finally\n {{ html1 }}"; $result = \SalesforceEng\Breakout\Breakout::render($document, $data); echo $result; ?>
这将输出结果
his is a test of the document rendering.
te\x22this\x22sting and
font-sizepx;color:red;font-weight:bold finally
<b>testing</b>
每个项目首先根据管道 |
字符后面的类型进行转义,然后在文档中进行替换。结果从 render
调用返回。**注意**:目前没有为 render
函数提供“辅助”方法。
注意:任何没有匹配数据的标签将**不会被渲染并直接删除**。不会发生数据替换。
文档转义 - 字符串、对象和函数
文档转义不仅支持如上例中所示的字符串,还支持通过属性和方法调用的基本对象交互。它们的工作方式类似于字符串替换,但有一些额外的语法。要引用对象的属性或方法,您使用点号和可选的括号来指定方法调用。以下是一些示例
<?php class Object1 { public $property1 = 'test'; public function myMethod() { return 'foobar'; } } $data = [ 'obj1' => new Object1() ]; $document = "this should replace both the property {{ obj1.property1 }} and the method {{ obj1.myMethod() }} values."; $result = \SalesforceEng\Breakout\Breakout::render($document, $data); /** * This example results in: * 'this should replace both the property test and the method foobar values.' */ ?>
您可以从输出行中看到,与对象相关的两个值都被替换了。第一个标记显示使用点号表示法定义要显示的(公共)属性,第二个标记使用方法调用,其中的 ()
表示它是一个方法。如果您不包括括号,它将假定它是一个属性。
此外,Breakout
还有一个小技巧。如果传递的数据是一个对象并且没有定义属性或方法,系统将尝试检测是否定义了 __toString
方法。例如
<?php class Object1 { public function __toString() { return 'my object'; } } $data = [ 'obj1' => new Object1() ]; $document = "this should replace based on toString: {{ obj1 }}"; $result = \SalesforceEng\Breakout\Breakout::render($document, $data); /** * This example results in: * 'this should replace based on toString: my object' */ ?>
分隔符替换
默认情况下,Breakout
使用 {{
和 }}
作为分隔符。如果您使用的是其他内容,您可以在 render
方法调用中将它们设置为可选参数
<?php // You can either set them as n array, one for each side $result = \SalesforceEng\Breakout\Breakout::render($document, $data, array('%%', '%%')); // Or if you just use a single delimiter for both side, you can just use a string: $result = \SalesforceEng\Breakout\Breakout::render($document, $data, '%%'); ?>
对于此示例,替换“双百分号”分隔符,而不是替换 {{ foo }}
,将替换 %% foo %%
。
输出原始数据
可能会有一些情况(例如,如果您使用的是与 Breakout 使用相同分隔符的前端模板库),您可能希望以无格式或无插值更改的方式输出原始数据。您可以使用 raw
块来完成此操作
<?php $document = <<<EOD username: {{ username }} {% raw %} {{ this should remain }} {% endraw %} EOD; $data = [ 'username' => 'ccornutt' ]; $result = \SalesforceEng\Breakout\Breakout::render($document, $data); ?>
生成的文档将保留 {{ this should remain }}
值并直接输出。
控制结构
除了基本的变量替换外,Breakout 还支持一些基本的控制结构。目前只支持两种:for
和 if
。
使用 For 循环
<?php $document = <<<EOD {% for item in items %} item: {{ item }} {% endfor %} EOD; $data = [ 'items' => ['foo', 'bar', 'baz'] ]; $result = \SalesforceEng\Breakout\Breakout::render($document, $data); ?>
这将导致列表中的每个项目都像这样输出:item: foo
。
使用 If 语句
if
的功能与它类似,本质上是一个 isset
<?php $document = <<<EOD {% if item %} {{ item }} {% endif %} EOD; $data = ['item' => 'foobar']; $result = \SalesforceEng\Breakout\Breakout::render($document, $data); ?>
在这种情况下,它会输出 "foobar",因为 item
的值存在。if
的处理还提供了简单的评估处理
<?php // With string matching $document = "{% if user.name == 'user1' %} show this {% endif %}"; // Or booleans $document = "{% if user.active == true %} show this {% endif %}"; // Or just checking if the value is set $document = "{% if user.active %} show this {% endif %}"; ?>
以及 "else" 功能
<?php $document = "{% if user.name == 'user1' %} show this {% else %} show that {% endif %}"; ?>
在上面的示例中,如果 user.name
等于 "user",则输出 "show this"。如果不等于,你会得到 "show that"。目前不支持 elseif
。
嵌套控制结构
您还可以像 for
和 if
一样嵌套这些控制结构
<?php $document = <<<EOD {% for item in items %} {% if item.name %}{{ item.name }}{% endfor %} {% endfor %} EOD; $data = [ 'items' => [ ['name' => 'item1'], ['value' => 'item2'] ] ]; $result = \SalesforceEng\Breakout\Breakout::render($document, $data); ?>
这将只输出 "item1",因为列表中的第二个项目没有 name
值(如果不存在数据,则仅删除标签)。