XHP:扩展
XHP附带了所有标准HTML标签的类,但是由于这些是一流的对象,因此可以创建自己的XHP类来呈现不符合标准HTML规范的项目。
基本
所有XHP类名以冒号开始,:并且可以包括其他:,只要它们不相邻。请注意,这是Hack规则的一个例外,您不能:在类名中。
一个自定义的XHP类需要做两件事情:
- 延伸:x:element。
- 实现render()通过XHP对象返回的方法XHPRoot。
<?hh
class :introduction extends :x:element {
protected function render(): \XHPRoot {
return <strong>Hello!</strong>;
}
}
class :intro-plain-str extends :x:element {
protected function render(): \XHPRoot {
// Since this function returns an XHPRoot, if you want to return a primitive
// like a string, wrap it around <x:frag>
return <x:frag>Hello!</x:frag>;
}
}
function extending_examples_basic_run(): void {
echo <introduction />;
echo PHP_EOL . PHP_EOL;
echo <intro-plain-str />;
}
extending_examples_basic_run();
Output
<strong>Hello!</strong>
Hello!
属性
句法
您的自定义类可能具有与XML属性类似的属性,使用attribute关键字:
attribute <type> <name> [= default value|@required];
另外,可以组合多个声明:
attribute
int foo,
string bar @required;
类型
XHP属性支持以下类型:
- bool,int,float,string,array,mixed(有没有强制 ......一个int不强迫float,例如,你会得到一个XHPInvalidAttributeException,如果你试试这个)。
- Hack enum 名称
- XHP特定enums与该属性的内联形式enum {item, item...}。所有值必须是标量的,因此可以将其转换为字符串。这些枚举不是Hack enums。
- 类或接口名称
- 通用类型,带有类型参数
如果属性在实例化元素时属性不正确,则类型检查器会引发错误(例如<a href={true} />,因为XHP允许以其他方式设置属性(例如setAttribute()),并不是所有问题都可以被类型检查器捕获,并且XHPInvalidAttributeException将在运行时抛出,案例。
该->:运营商可以用来检索属性的值,而哈克将了解它的类型。
必需属性
您可以@required根据属性名称后面的声明指定属性。如果您尝试呈现XHP对象并且尚未设置必需属性,则将XHPAttributeRequiredException抛出一个。
<?hh
class :user-info extends :x:element {
attribute int userid @required;
attribute string name = "";
protected function render(): \XHPRoot {
return
<x:frag>User with id {$this->:userid} has name {$this->:name}</x:frag>;
}
}
function extending_examples_attributes_run(): void {
$uinfo = <user-info />;
// Can't render :user-info for an echo without setting the required userid
// attribute
try {
echo $uinfo;
} catch (\XHPAttributeRequiredException $ex) {
var_dump($ex->getMessage());
}
$uinfo->setAttribute('userid', 1000);
$uinfo->setAttribute('name', 'Joel');
echo $uinfo;
}
extending_examples_attributes_run();
Output
string(166) "Required attribute `userid` was not specified in element `user-info`.
/data/users/joelm/user-documentation/guides/hack/24-XHP/04-extending-examples/required-attributes.php:16"
User with id 1000 has name Joel
可空的属性
由于历史原因,可以推断出可以使用的类型,而不是明确说明。如果属性不是@required并且没有默认值,则属性为空。例如:
attribute
string iAmNotNullable @required,
string iAmNotNullableEither = "foo",
string butIAmNullable;
Inheritance
在自定义XHP类中拥有属性的最简单的方法是继承现有XHP对象的属性是使用XHPHelpers
trait。
Children
您应该声明自定义类允许的类型作为子集。你使用的children
声明:
children (:class1, :class2);
children empty; // no children allowed
如果您没有明确声明使用该children属性,那么您的类可以有任何小孩。如果您尝试将子集添加到不允许某个对象或不在其声明列表中的对象,那么XHPInvalidChildrenException将被抛出。
您可以在声明children时使用标准的正则表达式运算符*
(zero or more), +
(one or more) |
(this or that)
<?hh
class :my-br extends :x:element {
children empty; // no children allowed
protected function render(): \XHPRoot {
return
<x:frag>PHP_EOL</x:frag>;
}
}
class :my-ul extends :x:element {
children (:li)+; // one more more
protected function render(): \XHPRoot {
return
<ul>{$this->getChildren()}</ul>;
}
}
class :my-html extends :x:element {
children (:head, :body);
protected function render(): \XHPRoot {
return
<html>{$this->getChildren()}</html>;
}
}
function extending_examples_children_run(): void {
$my_br = <my-br />;
// Even though my-br does not take any children, you can still call the
// appendChild method with no consequences. The consequence will come when
// you try to render the object by something like an echo.
$my_br->appendChild(<ul />);
try {
echo $my_br;
} catch (\XHPInvalidChildrenException $ex) {
var_dump($ex->getMessage());
}
$my_ul = <my-ul />;
$my_ul->appendChild(<li />);
$my_ul->appendChild(<li />);
echo $my_ul;
echo PHP_EOL;
$my_html = <my-html />;
$my_html->appendChild(<head />);
$my_html->appendChild(<body />);
echo $my_html;
}
extending_examples_children_run();
Output
string(240) "Element `my-br` was rendered with invalid children.
/data/users/joelm/user-documentation/guides/hack/24-XHP/04-extending-examples/children.php:33
Verified 0 children before failing.
Children expected:
empty
Children received:
:ul[%flow]"
<ul><li></li><li></li></ul>
<html><head></head><body></body></html>
分类
XHP中的类别就像面向对象语言中的接口。您可以使用任何数量的类别标记您的自定义课程,然后可以从您的孩子引用。你使用category声明。每个类别都有一个前缀%。
category %name1, %name2,...., %$nameN;
类别取自HTML规范(例如%flow,,%phrase)。
<?hh
class :my-text extends :x:element {
category %phrase;
children (pcdata | %phrase); // prefixed colon ommitted purposely on pcdata
protected function render(): \XHPRoot {
return
<x:frag>{$this->getChildren('%phrase')}</x:frag>;
}
}
function extending_examples_categories_run(): void {
$my_text = <my-text />;
$my_text->appendChild(<em>"Hello!"</em>); // This is a %phrase
echo $my_text;
$my_text = <my-text />;
$my_text->appendChild("Bye!"); // This is pcdata, not a phrase
// Won't print out "Bye!" because render is only returing %phrase children
echo $my_text;
}
extending_examples_categories_run();
Output
<em>"Hello!"</em>
Async
XHP和 async共同共存。 async XHP类必须做另外两件事情:
- 使用XHPAsync特质
- 实施asyncRender(): Awaitable<XHPRoot>而不是render(): XHPRoot
<?hh
class :ui:get-status extends :x:element {
use XHPAsync;
protected async function asyncRender(): Awaitable<\XHPRoot> {
$ch = curl_init('https://developers.facebook.com/status/');
curl_setopt(
$ch,
CURLOPT_USERAGENT,
'hhvm/user-documentation example',
);
$status = await HH\Asio\curl_exec($ch);
return <x:frag>Status is: {$status}</x:frag>;
}
}
function extending_examples_async_run(): void {
$status = <ui:get-status />;
// This can be long, so just show a bit for illustrative purposes
$sub_status = substr($status, 0, 100);
var_dump($sub_status);
}
extending_examples_async_run();
Output
string(100) "Status is: <!DOCTYPE html>
<html lang="en" id="facebook" class="no"
XHP Helpers
该XHPHelpers特性实现了三种行为:
- 将属性从一个对象传递到从其render()方法返回的对象。
- 给每个对象一个唯一的id属性。
- 管理class属性。
属性转移
假设你有一个类要继承属性:div。你可以这样做:
<?hh
class :ui-my-box extends :x:element {
attribute :div; // inherit from attributes from <div>
protected function render(): \XHPRoot {
// returning this will ignore any attribute set on this custom object
// They are not transferred automically when returning the <div>
return
<div class="my-box">{$this->getChildren()}</div>;
}
}
function extending_examples_bad_attribute_transfer_run(): void {
$my_box = <ui-my-box title="My box" />;
// This will only bring <div class="my-box"></div> ... title= will be
// ignored.
echo $my_box->toString();
}
extending_examples_bad_attribute_transfer_run();
Output
<div class="my-box"></div>
上面的问题是,任何设置的属性:ui:my-custom都将丢失,因为<div>返回的render()属性将不会自动获取这些属性。
相反,你应该使用XHPHelpers。
<?hh
class :ui-my-good-box extends :x:element {
attribute :div; // inherit from attributes from <div>
// Make sure that attributes are transferred automatically when rendering.
use XHPHelpers;
protected function render(): \XHPRoot {
// returning this will transfer any attribute set on this custom object
return
<div class="my-good-box">{$this->getChildren()}</div>;
}
}
function extending_examples_good_attribute_transfer_run(): void {
$my_box = <ui-my-good-box title="My Good box" />;
echo $my_box->toString();
}
extending_examples_good_attribute_transfer_run();
Output
<div class="my-good-box" title="My Good box"></div>
现在,:ui:my-custom
渲染时,每个:div
属性将被转移,假定它在render()
函数中声明。此外,设置的:ui:my-custom
属性值将覆盖函数中:div
设置的相同属性render()
。
唯一ID
XHPHelpers有一种方法getID(),您可以调用它给您的渲染的自定义XHP对象一个唯一的ID,可以在您的代码或UI框架的其他部分(例如CSS)中引用。
<?hh
class :my-id extends :x:element {
attribute string id;
use XHPHelpers;
protected function render(): \XHPRoot {
return
<span id={$this->getID()}>This has a random id</span>;
}
}
function extending_examples_get_id_run(): void {
// This will print something like:
// <span id="8b95a23bc0">This has a random id</span>
echo <my-id />;
}
extending_examples_get_id_run();
Output
<span id="d5f52c9291">This has a random id</span>
类属性管理
XHPHelpers有两种方法可以将类名添加到classXHP对象的属性中。这一切都假定您的自定义类class直接声明或通过继承声明该属性。addClass()采取string并附string加到class属性; conditionClass()将a string和a bool追加string到class属性,如果bool是true。
<?hh
class :my-cls-adder extends :x:element {
attribute :div;
use XHPHelpers;
protected function render(): \XHPRoot {
$div = <div />;
$div->addClass('my-cls-adder');
$div->appendChild($this->getChildren());
return $div;
}
}
function extending_examples_add_class_run(): void {
echo <my-cls-adder />;
}
extending_examples_add_class_run();
Output
<div class="my-cls-adder"></div>