XHP介紹

2018-10-19 11:18 更新

XHP提供您的輸出(通常為HTML)的本機XML樣表示。這樣可以對您的UI代碼進(jìn)行類型檢查,并自動避免多個常見問題,如跨站點腳本(XSS)和雙重轉(zhuǎn)義。它還應(yīng)用其他驗證規(guī)則,例如<head>必須包含<title>。

使用傳統(tǒng)插值,一個簡單的頁面可能如下所示:

<?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,所有的元素都必須關(guān)閉-例如,"<br>"成為<br />。
  • 確保您的HTML已正確嵌套。
  • 刪除所有的HTML /屬性轉(zhuǎn)義 - 例如,您不需要htmlspecialchars()在XHP輸出中包含一個變量之前調(diào)用; 如果你這樣做,它將被雙重轉(zhuǎn)義。

關(guān)于Namespaces

XHP目前有幾個命名空間的問題; 我們建議:

  • XHP類沒有在命名空間中聲明
  • 使用XHP類的代碼沒有命名空間。

我們計劃在未來支持namespaces。

XHP-Lib庫

雖然XHP語法是Hack的一部分,但很大一部分實現(xiàn)是在一個名為XHP-Lib的普通庫中,需要通過編寫器來安裝:

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

這包括基類和接口以及標(biāo)準(zhǔn)HTML元素的定義。

為什么要使用XHP?

大多數(shù)用戶的初始原因是因為它是“默認(rèn)安全”:所有變量都以上下文相適應(yīng)的方式自動轉(zhuǎn)義(例如,有不同的轉(zhuǎn)義屬性值與文本節(jié)點的規(guī)則)。另外,XHP被類型檢查器理解,確保不傳遞屬性值。一個常見的例子是border="3",但是border是一個on / off屬性,所以3的值沒有意義。

對于XHP經(jīng)驗豐富的用戶來說,最大的優(yōu)點是可以輕松地添加自定義的“元素”,使用自己的行為,然后可以像純HTML元素一樣使用。例如,該網(wǎng)站定義<a:post>了與標(biāo)準(zhǔn)<a>標(biāo)簽具有相同接口的標(biāo)簽,但是使用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>不創(chuàng)建塊元素:

form.postLink { 
  display:inline; 
}

在這一點上,新元素可以像任何內(nèi)置元素一樣使用:

<?hh
function intro_examples_a_a_post() {
  $get_link =
   <a  rel="external nofollow" target="_blank" >I'm a normal link</a>;
  $post_link =
    <a:post  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  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對象是一流的,而不僅僅是字符串,因此可以進(jìn)行一整套驗證,以確保您的UI沒有細(xì)微的錯誤:

<?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

上述代碼將不會進(jìn)行類型檢查或運行,因為XHP驗證器將會看到<span>并且<naps>標(biāo)簽不匹配 - 但是以下代碼將正確鍵入正確但無法運行,因為在標(biāo)記匹配時,它們不能正確嵌套(根據(jù)HTML規(guī)范),并且嵌套驗證僅在運行時發(fā)生:

?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字符轉(zhuǎn)義為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>
以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號