salesforceeng/breakout

一个基于上下文的输出转义工具

1.0.1 2016-06-13 16:26 UTC

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 &quot;something&quot; here &lt;script&gt;alert(&quot;test&quot;);&lt;\/script&gt;
?>

与Symfony(v1)一起使用

要在我们的Symfony安装中使用此库,需要做两个更改。

  1. 将文件BreakoutHelper.php移动到apps/frontend/lib/helper/BreakoutHelper.php

  2. 并更改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 &lt;b&gt;data&lt;/b&gt;。此替换执行了一个htmlspecialchars调用以过滤数据。这仅用于通用的HTML转义。

HTML属性

HTML属性需要一些不同的处理,因为它们是不同的上下文。下面是一个简单的示例,演示如何传递HTML属性字符串进行转义

 <a href="" <?php echo escape('name="f\'test\'oo" onclick="error"', 'htmlattr'); ?>Sample Link</a>

产生的字符串是name="f&quot;test&quot;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&#45size&#58100px&#59color&#58red&#59font&#45weight&#58bold finally
 &lt;b&gt;testing&lt;/b&gt;

每个项目首先根据管道 | 字符后面的类型进行转义,然后在文档中进行替换。结果从 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 还支持一些基本的控制结构。目前只支持两种:forif

使用 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

嵌套控制结构

您还可以像 forif 一样嵌套这些控制结构

<?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 值(如果不存在数据,则仅删除标签)。