XHP:接口
有两个重要的接口中XHP,XHPRoot和XHPChild; 在函数中添加类型注释时,您将需要使用这些注释。
XHPRoot
该XHPRoot接口由所有XHP对象实现; 在实践中,这意味着:
- :x:element 子类 - 这些是重用(并组合)现有XHP类的类
- :x:primitive子类 - 这些定义基本元素,例如:x:frag和所有基本的HTML元素
- 所述的实施方式XHPUnsafeRenderable如下所述接口
XHPChild
XHP呈现树结构,该接口定义树的有效子节点; 这包括:
- 所有的实现 XHPRoot
- 字符串,整数,浮点数
- 上述任何一个数组
尽管字符串,整数,浮点数和数组都不是对象,但是类型instanceof检查器和HHVM都认为它们可以实现这个接口 - 无论是对于参数/返回类型还是用于检查。
高级接口
虽然XHP的安全默认功能通常是有益的,但偶尔需要绕过它们; 最常见的情况是:
- 迁移到XHP时需要嵌入另一个模板系统的输出。
- 需要从其他来源嵌入HTML,例如Markdown或BBCode渲染器。
XHP通常会阻碍:
- 转换所有变量,包括您的HTML代码。
- 执行子关系 - XHP对象不能被标记为允许HTML字符串子对象。
该XHPUnsafeRenderable和XHPAlwaysValidChild接口允许绕过这些安全机制。
XHPUnsafeRenderable
如果您需要渲染原始HTML字符串,请将其包装在实现此接口的类中,并提供一种toHTMLString(): string方法:
<?hh
/* YOU PROBABLY SHOULDN'T DO THIS
*
* Even with a scary (and accurate) name, it tends to be over-used.
* See below for an alternative.
*/
class ExamplePotentialXSSSecurityHole implements XHPUnsafeRenderable {
public function __construct(
private string $html,
) {
}
public function toHTMLString(): string {
return $this->html;
}
}
echo (
<div class="markdown">
{new ExamplePotentialXSSSecurityHole(
HHVM\UserDocumentation\XHP\Examples\md_render('Markdown goes here')
)}
</div>
)."\n";
我们不提供此接口的实现,因为通用实现往往被过度使用 - 相反,考虑进行更具体的实现:
<?hh
class ExampleMarkdownXHPWrapper implements XHPUnsafeRenderable {
private string $html;
public function __construct(
string $markdown_source,
) {
$this->html = HHVM\UserDocumentation\XHP\Examples\md_render(
$markdown_source
);
}
public function toHTMLString(): string {
return $this->html;
}
}
echo (
<div class="markdown">
{new ExampleMarkdownXHPWrapper('Markdown goes here')}
</div>
)."\n";
XHPAlwaysValidChild
可以通过实现此接口来绕过XHP的子级验证。实现此接口的大多数类也是实现XHPUnsafeRenderable,因为最常见的需求是当另一个渲染或模板系统生成子代时。
这也可以由XHP对象来实现,但这通常表示应该用类别替换子类规范。这个界面是故意打破XHP安全性的一部分,所以应尽量少用。
Example
<?hh
final class XHPUnsafeExample implements XHPUnsafeRenderable {
public function toHTMLString(): string {
return '<script>'.$_GET['I_LOVE_XSS'].'</script>';
}
}
$inputs = Map {
'<div />' => <div />,
'<x:frag />' => <x:frag />,
'"foo"' => 'foo',
'3' => 3,
'true' => true,
'null' => null,
'new stdClass()' => new stdClass(),
'[<li />, <li />, <li />]' => [<li />, <li />, <li />],
'XHPUnsafeExample' => new XHPUnsafeExample(),
};
$max_label_len = max($inputs->mapWithKey(($k, $_) ==> strlen($k)));
print str_repeat(' ', $max_label_len + 1)." | XHPRoot | XHPChild\n";
print str_repeat('-', $max_label_len + 1)."-|---------|----------\n";
foreach ($inputs as $label => $input) {
printf(
" %{$max_label_len}s | %-7s | %s\n",
$label,
$input instanceof XHPRoot ? 'yes' : 'no',
$input instanceof XHPChild ? 'yes' : 'no',
);
}
Output
| XHPRoot | XHPChild
--------------------------|---------|----------
<div /> | yes | yes
<x:frag /> | yes | yes
"foo" | no | yes
3 | no | yes
true | no | no
null | no | no
new stdClass() | no | no
[<li />, <li />, <li />] | no | yes
XHPUnsafeExample | no | yes