XHP介绍

2018-10-19 11:18 更新

XHP提供您的输出(通常为HTML)的本机XML样表示。这样可以对您的UI代码进行类型检查,并自动避免多个常见问题,如跨站点脚本(XSS)和双重转义。它还应用其他验证规则,例如<head>必须包含<title>。

使用传统插值,一个简单的页面可能如下所示:

<?hh 
$ user_name  =  'Fred' ; 
echo  “<tt> Hello <strong> $ user_name </ strong> </ tt>” ;

然而,使用XHP,它看起来像这样:

<?hh 
$ user_name  =  'Fred' ; 
echo  < tt > Hello  < strong > { $ user_name } </ strong > </ tt > ;

第一个例子使用字符串插值来输出HTML,而第二个例子没有引号,这意味着Hack完全可以理解语法 - 但这并不意味着你需要做的只是删除引号。所需的其他步骤包括:

  • 使用花括号包括变量 - 例如"<a>$foo</a>"变成<a>{$foo}</a>。
  • 由于XHP是象XML,所有的元素都必须关闭-例如,"<br>"成为<br />。
  • 确保您的HTML已正确嵌套。
  • 删除所有的HTML /属性转义 - 例如,您不需要htmlspecialchars()在XHP输出中包含一个变量之前调用; 如果你这样做,它将被双重转义。

关于Namespaces

XHP目前有几个命名空间的问题; 我们建议:

  • XHP类没有在命名空间中声明
  • 使用XHP类的代码没有命名空间。

我们计划在未来支持namespaces

XHP-Lib库

虽然XHP语法是Hack的一部分,但很大一部分实现是在一个名为XHP-Lib的普通库中,需要通过编写器来安装:

“require”:{ 
  “facebook / xhp-lib”:“〜2.2” 
}

这包括基类和接口以及标准HTML元素的定义。

为什么要使用XHP?

大多数用户的初始原因是因为它是“默认安全”:所有变量都以上下文相适应的方式自动转义(例如,有不同的转义属性值与文本节点的规则)。另外,XHP被类型检查器理解,确保不传递属性值。一个常见的例子是border="3",但是border是一个on / off属性,所以3的值没有意义。

对于XHP经验丰富的用户来说,最大的优点是可以轻松地添加自定义的“元素”,使用自己的行为,然后可以像纯HTML元素一样使用。例如,该网站定义<a:post>了与标准<a>标签具有相同接口的标签,但是使用POST请求而不是GET请求:

<?hh // strict

final class :a:post extends :x:element {
  attribute :a;

  use XHPHelpers;

  protected function render(): XHPRoot {
    $id = $this->getID();

    $anchor = <a>{$this->getChildren()}</a>;
    $form = (
      <form
        id={$id}
        method="post"
        action={$this->:href}
        target={$this->:target}
        class="postLink"
      >{$anchor}</form>
    );

    $this->transferAllAttributes($anchor);
    $anchor->setAttribute(
      'onclick',
      'document.getElementById("'.$id.'").submit(); return false;',
    );
    $anchor->setAttribute('href', '#');

    return $form;
  }
}

需要一点CSS,以便<form>不创建块元素:

form.postLink { 
  display:inline; 
}

在这一点上,新元素可以像任何内置元素一样使用:

<?hh
function intro_examples_a_a_post() {
  $get_link =
   <a href="http://www.example.com" rel="external nofollow" target="_blank"  rel="external nofollow" target="_blank"  rel="external nofollow" target="_blank" >I'm a normal link</a>;
  $post_link =
    <a:post href="http://www.example.com" rel="external nofollow" target="_blank"  rel="external nofollow" target="_blank"  rel="external nofollow" target="_blank" >I make a POST REQUEST</a:post>;

  echo $get_link;
  echo "\n";
  echo $post_link;
}

intro_examples_a_a_post();

Output

<a href="http://www.example.com" rel="external nofollow" target="_blank"  rel="external nofollow" target="_blank"  rel="external nofollow" target="_blank" >I'm a normal link</a>
<form id="f20194a67b" method="post" action="http://www.example.com" class="postLink"><a href="#" id="f20194a67b" onclick="document.getElementById("f20194a67b").submit(); return false;">I make a POST REQUEST</a></form>

运行时验证

由于XHP对象是一流的,而不仅仅是字符串,因此可以进行一整套验证,以确保您的UI没有细微的错误:

<?hh

function intro_examples_tag_matching_validation_using_string(): void {
  echo '<div class="section-header">';
  echo '<a href="#use">You should have used <span class="xhp">XHP</naps></a>';
  echo '</div>';
}

function intro_examples_tag_matching_validation_using_xhp(): void {
  // Typechecker error
  // Fatal syntax error at runtime
  echo
    <div class="section-header">
      <a href="#use">You should have used <span class="xhp">XHP</naps></a>
    </div>;
}

function intro_examples_tag_matching_validation_run(): void {
  intro_examples_tag_matching_validation_using_string();
  intro_examples_tag_matching_validation_using_xhp();
}

intro_examples_tag_matching_validation_run();

Output

Fatal error: XHP: mismatched tag: 'naps' not the same as 'span' in /data/users/joelm/user-documentation/guides/hack/24-XHP/01-introduction-examples/tag-matching-validation.php.type-errors on line 14

上述代码将不会进行类型检查或运行,因为XHP验证器将会看到<span>并且<naps>标签不匹配 - 但是以下代码将正确键入正确但无法运行,因为在标记匹配时,它们不能正确嵌套(根据HTML规范),并且嵌套验证仅在运行时发生:

?hh

function intro_examples_allowed_tag_validation_using_string(): void {
  echo '<ul><i>Item 1</i></ul>';
}

function intro_examples_allowed_tag_validation_using_xhp(): void {
  try {
    echo <ul><i>Item 1</i></ul>;
  } catch (\XHPInvalidChildrenException $ex) {
    // We will get here because an <i> cannot be nested directly below a <ul>
    var_dump($ex->getMessage());
  }
}

function intro_examples_allowed_tag_validation_run(): void {
  intro_examples_allowed_tag_validation_using_string();
  echo PHP_EOL . PHP_EOL;
  intro_examples_allowed_tag_validation_using_xhp();
}

intro_examples_allowed_tag_validation_run();

Output

<ul><i>Item 1</i></ul>

string(262) "Element `ul` was rendered with invalid children.

/data/users/joelm/user-documentation/guides/hack/24-XHP/01-introduction-examples/allowed-tag-validation.php:11

Verified 0 children before failing.

Children expected:
(:li)*

Children received:
:i[%flow,%phrase]"

安全

基于字符串的条目和验证是跨站点脚本(XSS)的首选。你可以通过使用特殊功能来解决这个问题htmlspecialchars(),但是你必须真正记住使用这些功能。输出之前,XHP自动将保留的HTML字符转义为HTML实体。

<?hh

function intro_examples_avoid_xss_using_string(string $could_be_bad): void {
  // Could call htmlspecialchars() here
  echo '<html><head/><body> ' . $could_be_bad . '</body></html>';
}

function intro_examples_avoid_xss_using_xhp(string $could_be_bad): void {
  // The string $could_be_bad will be escaped to HTML entities like:
  // <html><head></head><body>&lt;blink&gt;Ugh&lt;/blink&gt;</body></html>
  echo
    <html>
      <head/>
      <body>{$could_be_bad}</body>
    </html>;
}

function intro_examples_avoid_xss_run(string $could_be_bad): void {
  intro_examples_avoid_xss_using_string($could_be_bad);
  echo PHP_EOL . PHP_EOL;
  intro_examples_avoid_xss_using_xhp($could_be_bad);
}

intro_examples_avoid_xss_run('<blink>Ugh</blink>');

Output

<html><head/><body> <blink>Ugh</blink></body></html>

<html><head></head><body>&lt;blink&gt;Ugh&lt;/blink&gt;</body></html>
以上内容是否对您有帮助:
在线笔记
App下载
App下载

扫描二维码

下载编程狮App

公众号
微信公众号

编程狮公众号