PHP的Yii框架中Model模型的学习教程

模型是 MVC 模式中的一部分, 是代表业务数据、规则和逻辑的对象。

模型是 CModel 或其子类的实例。模型用于保持数据以及与其相关的业务逻辑。

模型是单独的数据对象。它可以是数据表中的一行,或者一个用户输入的表单。 数据对象的每个字段对应模型中的一个属性。每个属性有一个标签(label), 并且可以通过一系列规则进行验证。

Yii 实现了两种类型的模型:表单模型和 Active Record。二者均继承于相同的基类 CModel。

表单模型是 CFormModel 的实例。表单模型用于保持从用户的输入获取的数据。 这些数据经常被获取,使用,然后丢弃。例如,在一个登录页面中, 我们可以使用表单模型用于表示由最终用户提供的用户名和密码信息。

Active Record (AR) 是一种用于通过面向对象的风格抽象化数据库访问的设计模式。 每个 AR 对象是一个 CActiveRecord 或其子类的实例。代表数据表中的一行。 行中的字段对应 AR 对象中的属性。

可通过继承 yii\base\Model 或它的子类定义模型类,基类yii\base\Model支持许多实用的特性:

  • 属性: 代表可像普通类属性或数组一样被访问的业务数据;
  • 属性标签: 指定属性显示出来的标签;
  • 块赋值: 支持一步给许多属性赋值;
  • 验证规则: 确保输入数据符合所申明的验证规则;
  • 数据导出: 允许模型数据导出为自定义格式的数组。

属性

模型通过 属性 来代表业务数据,每个属性像是模型的公有可访问属性, yii\base\Model::attributes() 指定模型所拥有的属性。

可像访问一个对象属性一样访问模型的属性:

$model = new \app\models\ContactForm;

// "name" 是ContactForm模型的属性
$model->name = 'example';
echo $model->name;

也可像访问数组单元项一样访问属性,这要感谢yii\base\Model支持 ArrayAccess 数组访问 和 ArrayIterator 数组迭代器:

$model = new \app\models\ContactForm;

// 像访问数组单元项一样访问属性
$model['name'] = 'example';
echo $model['name'];

// 迭代器遍历模型
foreach ($model as $name => $value) {
  echo "$name: $value\n";
}

定义属性

默认情况下你的模型类直接从yii\base\Model继承,所有 non-static public非静态公有 成员变量都是属性。 例如,下述ContactForm模型类有四个属性name, email, subject and body, ContactForm 模型用来代表从HTML表单获取的输入数据。

namespace app\models;

use yii\base\Model;

class ContactForm extends Model
{
  public $name;
  public $email;
  public $subject;
  public $body;
}

另一种方式是可覆盖 yii\base\Model::attributes() 来定义属性,该方法返回模型的属性名。 例如 yii\db\ActiveRecord 返回对应数据表列名作为它的属性名, 注意可能需要覆盖魔术方法如__get(), __set()使属性像普通对象属性被访问。

属性标签

当属性显示或获取输入时,经常要显示属性相关标签,例如假定一个属性名为firstName, 在某些地方如表单输入或错误信息处,你可能想显示对终端用户来说更友好的 First Name 标签。

可以调用 yii\base\Model::getAttributeLabel() 获取属性的标签,例如:

$model = new \app\models\ContactForm;

// 显示为 "Name"
echo $model->getAttributeLabel('name');

默认情况下,属性标签通过yii\base\Model::generateAttributeLabel()方法自动从属性名生成. 它会自动将驼峰式大小写变量名转换为多个首字母大写的单词,例如 username 转换为 Username, firstName 转换为 First Name。

如果你不想用自动生成的标签,可以覆盖 yii\base\Model::attributeLabels() 方法明确指定属性标签,例如:

namespace app\models;

use yii\base\Model;

class ContactForm extends Model
{
  public $name;
  public $email;
  public $subject;
  public $body;

  public function attributeLabels()
  {
    return [
      'name' => 'Your name',
      'email' => 'Your email address',
      'subject' => 'Subject',
      'body' => 'Content',
    ];
  }
}

应用支持多语言的情况下,可翻译属性标签, 可在 yii\base\Model::attributeLabels() 方法中定义,如下所示:

public function attributeLabels()
{
  return [
    'name' => \Yii::t('app', 'Your name'),
    'email' => \Yii::t('app', 'Your email address'),
    'subject' => \Yii::t('app', 'Subject'),
    'body' => \Yii::t('app', 'Content'),
  ];
}

甚至可以根据条件定义标签,例如通过使用模型的 scenario场景, 可对相同的属性返回不同的标签。

补充:属性标签是 视图一部分,但是在模型中申明标签通常非常方便,并可行程非常简洁可重用代码。
场景

模型可能在多个 场景 下使用,例如 User 模块可能会在收集用户登录输入,也可能会在用户注册时使用。 在不同的场景下,模型可能会使用不同的业务规则和逻辑,例如 email 属性在注册时强制要求有,但在登陆时不需要。

模型使用 yii\base\Model::scenario 属性保持使用场景的跟踪, 默认情况下,模型支持一个名为 default 的场景,如下展示两种设置场景的方法:

// 场景作为属性来设置
$model = new User;
$model->scenario = 'login';

// 场景通过构造初始化配置来设置
$model = new User(['scenario' => 'login']);

默认情况下,模型支持的场景由模型中申明的 验证规则 来决定, 但你可以通过覆盖yii\base\Model::scenarios()方法来自定义行为,如下所示:

namespace app\models;

use yii\db\ActiveRecord;

class User extends ActiveRecord
{
  public function scenarios()
  {
    return [
      'login' => ['username', 'password'],
      'register' => ['username', 'email', 'password'],
    ];
  }
}

补充:在上述和下述的例子中,模型类都是继承yii\db\ActiveRecord, 因为多场景的使用通常发生在Active Record 类中.
scenarios() 方法返回一个数组,数组的键为场景名,值为对应的 active attributes活动属性。 活动属性可被 块赋值 并遵循验证规则在上述例子中,username 和 password 在login场景中启用,在 register 场景中, 除了 username and password 外 email也被启用。

scenarios() 方法默认实现会返回所有yii\base\Model::rules()方法申明的验证规则中的场景, 当覆盖scenarios()时,如果你想在默认场景外使用新场景,可以编写类似如下代码:

namespace app\models;

use yii\db\ActiveRecord;

class User extends ActiveRecord
{
  public function scenarios()
  {
    $scenarios = parent::scenarios();
    $scenarios['login'] = ['username', 'password'];
    $scenarios['register'] = ['username', 'email', 'password'];
    return $scenarios;
  }
}

场景特性主要在验证 和 属性块赋值 中使用。 你也可以用于其他目的,例如可基于不同的场景定义不同的 属性标签。

验证规则

当模型接收到终端用户输入的数据,数据应当满足某种规则(称为 验证规则, 也称为 业务规则)。 例如假定ContactForm模型,你可能想确保所有属性不为空且 email 属性包含一个有效的邮箱地址, 如果某个属性的值不满足对应的业务规则,相应的错误信息应显示,以帮助用户修正错误。

可调用 yii\base\Model::validate() 来验证接收到的数据, 该方法使用yii\base\Model::rules()申明的验证规则来验证每个相关属性, 如果没有找到错误,会返回 true,否则它会将错误保存在 yii\base\Model::errors 属性中并返回false,例如:

$model = new \app\models\ContactForm;

// 用户输入数据赋值到模型属性
$model->attributes = \Yii::$app->request->post('ContactForm');

if ($model->validate()) {
  // 所有输入数据都有效 all inputs are valid
} else {
  // 验证失败:$errors 是一个包含错误信息的数组
  $errors = $model->errors;
}

通过覆盖 yii\base\Model::rules() 方法指定模型属性应该满足的规则来申明模型相关验证规则。 下述例子显示ContactForm模型申明的验证规则:

public function rules()
{
  return [
    // name, email, subject 和 body 属性必须有值
    [['name', 'email', 'subject', 'body'], 'required'],

    // email 属性必须是一个有效的电子邮箱地址
    ['email', 'email'],
  ];
}

一条规则可用来验证一个或多个属性,一个属性可对应一条或多条规则。 更多关于如何申明验证规则的详情请参考 验证输入 一节.

有时你想一条规则只在某个 场景 下应用,为此你可以指定规则的 on 属性,如下所示:

public function rules()
{
  return [
    // 在"register" 场景下 username, email 和 password 必须有值
    [['username', 'email', 'password'], 'required', 'on' => 'register'],

    // 在 "login" 场景下 username 和 password 必须有值
    [['username', 'password'], 'required', 'on' => 'login'],
  ];
}

如果没有指定 on 属性,规则会在所有场景下应用, 在当前yii\base\Model::scenario 下应用的规则称之为 active rule活动规则。

一个属性只会属于scenarios()中定义的活动属性且在rules()申明对应一条或多条活动规则的情况下被验证。

块赋值

块赋值只用一行代码将用户所有输入填充到一个模型,非常方便, 它直接将输入数据对应填充到 yii\base\Model::attributes 属性。 以下两段代码效果是相同的,都是将终端用户输入的表单数据赋值到 ContactForm 模型的属性, 明显地前一段块赋值的代码比后一段代码简洁且不易出错。

$model = new \app\models\ContactForm;
$model->attributes = \Yii::$app->request->post('ContactForm');
$model = new \app\models\ContactForm;
$data = \Yii::$app->request->post('ContactForm', []);
$model->name = isset($data['name']) ? $data['name'] : null;
$model->email = isset($data['email']) ? $data['email'] : null;
$model->subject = isset($data['subject']) ? $data['subject'] : null;
$model->body = isset($data['body']) ? $data['body'] : null;

安全属性

块赋值只应用在模型当前yii\base\Model::scenario场景yii\base\Model::scenarios()方法 列出的称之为 安全属性 的属性上,例如,如果User模型申明以下场景, 当当前场景为login时候,只有username and password 可被块赋值,其他属性不会被赋值。

public function scenarios()
{
  return [
    'login' => ['username', 'password'],
    'register' => ['username', 'email', 'password'],
  ];
}

补充: 块赋值只应用在安全属性上,因为你想控制哪些属性会被终端用户输入数据所修改, 例如,如果 User 模型有一个permission属性对应用户的权限, 你可能只想让这个属性在后台界面被管理员修改。
由于默认yii\base\Model::scenarios()的实现会返回yii\base\Model::rules()所有属性和数据, 如果不覆盖这个方法,表示所有只要出现在活动验证规则中的属性都是安全的。

为此,提供一个特别的别名为 safe 的验证器来申明哪些属性是安全的不需要被验证, 如下示例的规则申明 title 和 description都为安全属性。

public function rules()
{
  return [
    [['title', 'description'], 'safe'],
  ];
}

非安全属性

如上所述,yii\base\Model::scenarios() 方法提供两个用处:定义哪些属性应被验证,定义哪些属性安全。 在某些情况下,你可能想验证一个属性但不想让他是安全的,可在scenarios()方法中属性名加一个惊叹号 !。 例如像如下的secret属性。

public function scenarios()
{
  return [
    'login' => ['username', 'password', '!secret'],
  ];
}

当模型在 login 场景下,三个属性都会被验证,但只有 username和 password 属性会被块赋值, 要对secret属性赋值,必须像如下例子明确对它赋值。

$model->secret = $secret;

数据导出

模型通常要导出成不同格式,例如,你可能想将模型的一个集合转成JSON或Excel格式, 导出过程可分解为两个步骤,第一步,模型转换成数组;第二步,数组转换成所需要的格式。 你只需要关注第一步,因为第二步可被通用的数据转换器如yii\web\JsonResponseFormatter来完成。

将模型转换为数组最简单的方式是使用 yii\base\Model::attributes 属性,例如:

$post = \app\models\Post::findOne(100);
$array = $post->attributes;

yii\base\Model::attributes 属性会返回 所有 yii\base\Model::attributes() 申明的属性的值。

更灵活和强大的将模型转换为数组的方式是使用 yii\base\Model::toArray() 方法, 它的行为默认和 yii\base\Model::attributes 相同, 但是它允许你选择哪些称之为字段的数据项放入到结果数组中并同时被格式化。 实际上,它是导出模型到 RESTful 网页服务开发的默认方法,详情请参阅响应格式.

字段

字段是模型通过调用yii\base\Model::toArray()生成的数组的单元名。

默认情况下,字段名对应属性名,但是你可以通过覆盖 yii\base\Model::fields() 和/或 yii\base\Model::extraFields() 方法来改变这种行为, 两个方法都返回一个字段定义列表,fields() 方法定义的字段是默认字段,表示toArray()方法默认会返回这些字段。extraFields()方法定义额外可用字段,通过toArray()方法指定$expand参数来返回这些额外可用字段。 例如如下代码会返回fields()方法定义的所有字段和extraFields()方法定义的prettyName and fullAddress字段。

$array = $model->toArray([], ['prettyName', 'fullAddress']);
可通过覆盖 fields() 来增加、删除、重命名和重定义字段,fields() 方法返回值应为数组, 数组的键为字段名,数组的值为对应的可为属性名或匿名函数返回的字段定义对应的值。 特使情况下,如果字段名和属性定义名相同,可以省略数组键,例如:

// 明确列出每个字段,特别用于你想确保数据表或模型属性改变不会导致你的字段改变(保证后端的API兼容).
public function fields()
{
  return [
    // 字段名和属性名相同
    'id',

    // 字段名为 "email",对应属性名为 "email_address"
    'email' => 'email_address',

    // 字段名为 "name", 值通过PHP代码返回
    'name' => function () {
      return $this->first_name . ' ' . $this->last_name;
    },
  ];
}

// 过滤掉一些字段,特别用于你想继承父类实现并不想用一些敏感字段
public function fields()
{
  $fields = parent::fields();

  // 去掉一些包含敏感信息的字段
  unset($fields['auth_key'], $fields['password_hash'], $fields['password_reset_token']);

  return $fields;
}

警告:由于模型的所有属性会被包含在导出数组,最好检查数据确保没包含敏感数据, 如果有敏感数据,应覆盖 fields() 方法过滤掉,在上述列子中,我们选择过滤掉 auth_key, password_hash and password_reset_token。
最佳实践

模型是代表业务数据、规则和逻辑的中心地方,通常在很多地方重用, 在一个设计良好的应用中,模型通常比控制器代码多。

归纳起来,模型:

  • 可包含属性来展示业务数据;
  • 可包含验证规则确保数据有效和完整;
  • 可包含方法实现业务逻辑;
  • 不应直接访问请求,session和其他环境数据,这些数据应该由控制器传入到模型;
  • 应避免嵌入HTML或其他展示代码,这些代码最好在 视图中处理;
  • 单个模型中避免太多的场景.

在开发大型复杂系统时应经常考虑最后一条建议, 在这些系统中,模型会很大并在很多地方使用,因此会包含需要规则集和业务逻辑, 最后维护这些模型代码成为一个噩梦,因为一个简单修改会影响好多地方, 为确保模型好维护,最好使用以下策略:

定义可被多个 应用主体 或 模块 共享的模型基类集合。 这些模型类应包含通用的最小规则集合和逻辑。
在每个使用模型的 应用主体 或 模块中, 通过继承对应的模型基类来定义具体的模型类,具体模型类包含应用主体或模块指定的规则和逻辑。
例如,在高级应用模板,你可以定义一个模型基类common\models\Post, 然后在前台应用中,定义并使用一个继承common\models\Post的具体模型类frontend\models\Post, 在后台应用中可以类似地定义backend\models\Post。 通过这种策略,你清楚frontend\models\Post只对应前台应用,如果你修改它,就无需担忧修改会影响后台应用。

(0)

相关推荐

  • PHP 基于Yii框架中使用smarty模板的方法详解

    第一种方法按照YII系统的办法生成视图觉得有点麻烦,觉得用smarty更省事.尝试着把smarty模板加进来了. 复制代码 代码如下: date_default_timezone_set("PRC");class PlaceController extends CController {protected $_smarty;function __construct(){parent::__construct('place');//需要一个参数来调用父类的构造函数,该参数为控制器ID$p

  • Yii PHP Framework实用入门教程(详细介绍)

    说明:因为最近工作工作关系,需要开发一个在Linux下运行的Web Application,需要对现在比较流行的一些PHP框架做一个了解和评估,下面的这篇文章是笔者最近学习一个比较新的PHP Framework的一点经历和操作步骤,因为官方的手册写得比较晦涩(特别是中文的),曾经尝试遍读它那个手册再动手,读了一大半发现仍无法理解,于是干脆先下手为强了,因而也就有了下面的文章. 介绍Yii 是一个基于组件.纯OOP的.用于开发大型 Web 应用的高性能 PHP 框架.它将 Web 编程中的可重用性

  • PHP YII框架开发小技巧之模型(models)中rules自定义验证规则

    YII的models中的rules部分是一些表单的验证规则,对于表单验证十分有用,在相应的视图(views)里面添加了表单,在表单被提交之前程序都会自动先来这里面的规则里验证,只有通过对其有效的限制规则后才能被提交,可以很有效地保证表单安全和信息的有效性.还是给大家具体说明一下: 以下是视图(views)部分的简单代码: <?php $form=$this->beginWidget('CActiveForm', array( 'id'=>'tag-form', 'enableAjaxVa

  • yiic命令时提示“php.exe”不是内部或外部命令的解决方法

    本文实例讲述了yiic命令时提示"php.exe"不是内部或外部命令的解决方法,分享给大家供大家参考.具体方法如下: 在CMD中运行 yiic webapp work 如果报"php.exe"不是内部命令,是这样的:原因是Yii自带的yiic.bat找不到php.exe. 解决方法: 因为没有加入环境变量,所以无法直接执行php.exe. 右击"我的电脑->属性->高级->环境变量->系统变量->PATH->编辑&quo

  • 在Yii框架中使用PHP模板引擎Twig的例子

    Twig是一款快速.安全.灵活的PHP模板引擎,它内置了许多filter和tags,并且支持模板继承,能让你用最简洁的代码来描述你的模板.他的语法和Python下的模板引擎Jinjia以及Django的模板语法都非常像. 比如我们在PHP中需要输出变量并且将其进行转义时,语法比较累赘: 复制代码 代码如下: <?php echo $var ?><?php echo htmlspecialchars(\$var, ENT_QUOTES, 'UTF-8') ?> 但是在Twig中可以这

  • 详细解读PHP的Yii框架中登陆功能的实现

    Yii的登陆机制 Yii 生成应用时已经提供了最基础的用户登陆机制.我们用 Yii 生成一个新的应用,进入 protected/components 目录,我们可以看到 UserIdentity.php 文件,里面的 UserIdentity 类里面只有一个 public 函数如下: public function authenticate() { $users=array( // username => password 'demo'=>'demo', 'admin'=>'admin'

  • PHP yii实现model添加默认值的方法(两种方法)

    yii实现model添加默认值的方法(2种方法) 这篇文章主要介绍了yii实现model添加默认值的方法,结合实例分析了在rules()方法及在beforeSave()方法中设定两种实现技巧,对大家也许有帮助, 本文实例讲述了yii实现model添加默认值的方法.分享给大家供大家参考,具体如下: yii model 继承自CActiveRecord 有些字段可能不会出现在表单中,而需要在程序中加入.如订单编号,时间戳,操作的user_id等等. 以下二种方法: 1.在rules()方法中设定:

  • Yii中使用PHPExcel导出Excel的方法

    本文实例讲述了Yii中使用PHPExcel导出Excel的方法.分享给大家供大家参考.具体分析如下: 最近在研究PHP的Yii框架,很喜欢,碰到导出Excel的问题,研究了一下,就有了下面的方法. 1.首先在cofig/main.php中添加对PHPExcel的引用,我的方法是这样,代码如下: 复制代码 代码如下: // autoloading model and component classes     'import'=>array(         /*'application.modu

  • PHP的Yii框架中Model模型的学习教程

    模型是 MVC 模式中的一部分, 是代表业务数据.规则和逻辑的对象. 模型是 CModel 或其子类的实例.模型用于保持数据以及与其相关的业务逻辑. 模型是单独的数据对象.它可以是数据表中的一行,或者一个用户输入的表单. 数据对象的每个字段对应模型中的一个属性.每个属性有一个标签(label), 并且可以通过一系列规则进行验证. Yii 实现了两种类型的模型:表单模型和 Active Record.二者均继承于相同的基类 CModel. 表单模型是 CFormModel 的实例.表单模型用于保持

  • Java的Hibernate框架中的组合映射学习教程

    一.组合映射 组合是关联关系的一种特殊情况,是关联关系耦合度最高的一种关系,组合的主对象和子对象拥有相同的生命周期,主对像消亡的话子对象也会消亡.这里使用雇主和用户作为示例,用户和雇主都拥有联系方式属性,如果这里站在对象角度思考的话,常常会把对象模型绘制成为组合的方式,抽象出来一个共同的联系方式类,然后两种人分别包含相应的联系方式对象即可,向应的对象模型时它的对象示例如下图所示: 组合对象模型在生成相应的关系模型后会把对应的子类包含到主表中,所以对应的表结构会将相应的属性生成到对应的表中,相应的

  • Java的Hibernate框架中的继承映射学习教程

    一.继承映射 继承是面向对象很重要的特性,它实现了代码的服用,在关系模型中同样也有继承关系,这种继承关系其实可以看做是一种枚举关系,一种类型中可以枚举出很多子类型,这些子类型和父对象形成了继承关系,能够对其进行枚举的大部分都可以看做是一种继承映射,所以这种枚举关系可以看做是继承映射,例如动物就是一种抽象类,它是其它动物猪.猫等的父类,它们之间就是一种继承关系,如下图: 这种继承映射在转化为关系模型后会生成一张表,那么这张表是如何区分这两种类型的呢?用的是关系字段,需要在表中添加类型字段,使用关键

  • Yii中Model(模型)的创建及使用方法

    本文实例分析了Yii中Model(模型)的创建及使用方法.分享给大家供大家参考,具体如下: YII 实现了两种模型,表单模型(CFormModel类)和Active Record模型(CAtiveRecord类),它们都继承自CModel类. CFormModel代表的数据模型是从HTML表单收集的输入,封装了所有逻辑(如表单的验证和其它业务逻辑,应用到表单的域上).它能将数据存储在内 存中,或者在一个Active Record的帮助下,存入数据库里. 数据库连接操作 在config/main.

  • YII框架中搜索分页jQuery写法详解

    控制层 use frontend\models\StudUser; use yii\data\Pagination; use yii\db\Query; /** * 查询 * */ public function actionSearch() { //接值 $where=Yii::$app->request->get(); //实例化query $query=new Query(); $query->from('stud_user'); //判断 if(isset($where['sex

  • Flask和Django框架中自定义模型类的表名、父类相关问题分析

    本文实例分析了Flask和Django框架中自定义模型类的表名.父类相关问题.分享给大家供大家参考,具体如下: 一. Flask和Django中定义表名(执行迁移后生成)的方式和flask是不同的: 1. Django中表名的定义方式,以代码为例说明 class User(AbstractUser): # 继承自认证系统模型类 """用户模型类""" mobile = modles.CharField(max_length=11, mix_len

  • JavaScript中全选、全不选、反选、无刷新删除、批量删除、即点即改入库(在yii框架中操作)的代码分享

    效果展示: 代码实现: 控制器 <?php namespace app\controllers; use Yii; use yii\filters\AccessControl; use yii\web\Controller; use yii\filters\VerbFilter; use app\models\LoginForm; use app\models\ContactForm; //use yii\db\ActiveRecord; use yii\data\Pagination; use

  • YII框架中使用memcache的方法详解

    本文实例讲述了YII框架中使用memcache的方法.分享给大家供大家参考,具体如下: yii中可以很方便的使用memcache 一.配置 在main.php的components中加入cache配置 array( 'components'=>array( 'cache'=>array( 'class'=>'CMemCache', 'servers'=>array( array( 'host'=>'server1', 'port'=>11211, 'weight'=&g

  • Yii框架中memcache用法实例

    本文实例讲述了Yii框架中memcache用法.分享给大家供大家参考.具体分析如下: 在现在的公司用的是YII的框架,接触到的东西也比较多,可以学到的东西也比较多,在以前的公司没有接触过memcache,只是听过,但是从来没有真正用过.现在终于有机会使用一下了,就以我做的项目为例吧! 我做的项目是一个手机排行榜,但是排行榜每隔15分钟刷新一次啊,排行榜有一个前三名,可能前15分钟这三个人是前三名,也许下一个15分钟又是别人前三名了,产品要求,这些人中只要是前三名的都要发奖品,思考了好久,最终决定

  • Yii框架中jquery表单验证插件用法示例

    本文实例讲述了Yii框架中jquery表单验证插件用法.分享给大家供大家参考,具体如下: 运行效果图如下: 视图层: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtm

随机推荐