当前位置: 首页 >> 程序设计 >> PHP沉思录
 

PHP沉思录

作者:      来源:zz     发表时间:2008-02-18     浏览次数:      字号:    

Smarty
在任何Web应用中,如何将程序代码和界面设计,或者说,将逻辑层和表现层分离开来,都会是一个问题。对于PHP这种类型的嵌入网页的脚本语言,这一问题尤其突出。在新手编写的代码中,把访问数据库的代码和操纵HTML元素的代码写在同一个页面里,是很常见的情况。为了避免这一问题,开发者倾向于将涉及业务逻辑的代码封装在某些单独的库文件中,再在负责显示界面的文件中将它们include进来。但是,这仍然无法避免在显示界面的文件中包含大量的PHP代码。究其所以然,是因为除了涉及业务逻辑的代码以外,即使仅仅在显示层,也往往涉及到复杂的显示逻辑。在一个典型的显示页面中,程序需要先包含所有必需的库文件,初始化上下文环境,创建相关业务逻辑对象(假如数据库访问代码已经被业务逻辑对象封装,可以节省数据库相关的代码),最后在HTML的空隙中把对象格式化为HTML元素进行显示。于是我们看到了无数这样的页面,在第一行HTML开始之前,就已经包含了数十行甚至更多的PHP代码,在HTML的内部,仍然充满了各种各样的PHP代码。因此,PHP代码和HTML代码搅和在一块的问题仍然无法解决,对HTML的修改仍然可能导致整个PHP程序崩溃。更加麻烦的是,这种不清晰的结构妨碍了PHP应用在规模上的进一步扩张。
为了解决这一问题,模板(template)技术应运而生。模板技术的基本原理是,通过一个解析器(parser),读取指定的模板文件(包含了某些特定标签的HTML文件),将这些标签替换为相关的PHP变量,再输出为标准的HTML。通过这种方式,不但分离了业务逻辑层和表现层,而且也尽可能地分离了显示逻辑和HTML代码。通过替换不同的模板文件,可以方便地生成各种格式的输出,例如HTML,XML,WML,后期稍加处理甚至可以生成PDF和Flash。早期较为著名的PHP模板引擎有PHPLib中的Template和FastTemplate。
但是,模板技术也有其先天的缺陷。
◆无法彻底分离逻辑。显示逻辑和HTML代码很难通过简单的标签替换,实现彻底的分离。例如,遍历并显示一个数组,在PHP中可以用简单的foreach语句实现,但是使用模板时,就需要进行对整个模板文件进行多次替换操作,造成效率的极大降低;或者根据不同的数据值显示不同的格式,如果模板文件完全不包含PHP代码,那么将很难做到这一点。
◆解析导致的性能损失。由于每次PHP页面被访问时,解析器都必须对模板文件进行替换操作,无疑会降低PHP应用的性能。尤其在多次的替换操作时更是如此。因此,不使用模板比使用模板往往更加快速,这也是许多PHP程序员摒弃模板技术的原因之一。
在经过数年的发展之后,“编译型”的模板技术渐渐占据了主流。所谓“编译型”,是指解析器读取模板文件以后,并不直接生成静态HTML,而是“编译”成一个新的PHP文件,并将它保存起来。以后访问该页面时,模板引擎会直接执行“编译”后的PHP文件。Smarty是这种模板引擎的代表。
针对以上的两个问题,Smarty作了如下处理:
◆独立语法。Smarty实现了一套自己的语法,这套语法不但支持变量替换和简单的判断,而且支持循环,修饰符(modifier),内置了很多功能强大的函数,而且还支持自定义函数。这套系统保证Smarty能够完全独立地处理显示输出,无须再和PHP有什么瓜葛。事实上,在Smarty模板中,是不能直接使用PHP代码的(通过显式定义可以使用),这也是一种强制分离逻辑层和表现层的方式。(理论上来说,Smarty的模板文件也可以应用于其它语言。)但是,这种解决方式也受到了指责,因为Smarty的语法过于强大,几乎变成了一门新的语言,指责者认为,这反而增加了复杂性。但是,根据作者的实际经验,Smarty的语法不但非常简单直观,而且只需要掌握一些最初级的语法,就足可以应付绝大多数的应用。即使是不懂编程的网页设计师,也很容易就能够掌握。
◆编译机制。Smarty的“编译”机制,节省了用于反复解析模板文件的时间,极大地提高了速度。由于“编译”后生成的是标准的PHP文件,因此从理论上来说,执行的速度不会低于没有模板的PHP应用的速度。在一些和解析型模板引擎进行的对比测试中,Smarty在第一次访问时落后,但是在以后的访问中速度远远超出竞争对手。而且,这种编译过程是智能的,在模板文件的内容被改变后,Smarty会自动重新编译,因此对于开发者来说,编译过程完全无需人工干预。另外,如果你愿意的话,生成的PHP文件还可以方便地应用于Zend Accelerator这样的工具,进行二次编译。
除此之外,Smarty还拥有其他一些优秀的特性:
◆缓存机制。由于实现了编译机制,在接收到对某个模板文件的访问请求时,Smarty会自动将它重定向到编译后的PHP文件。但是,这也意味着,Smarty也可以将它重定向到任何其他的文件——例如静态的HTML文件。在此基础之上,Smarty实现了自己的基于页面的缓存机制。Smarty能够将编译后的PHP文件产生的结果——静态HTML——保存起来,将重复发送的请求直接重定向给它,这意味着对于第一次之后的请求,不需要执行任何PHP代码(Smarty本身的代码当然除外)。对于不需要频繁更新的页面(我们知道这种网页往往在整个网站中占大多数),通过这种缓存机制获取的性能提升是惊人的。而且,由于它是在页面级实现的,因此完全无须涉及到复杂的对象级缓存问题,保持了逻辑上的简单性。
◆可配置性。Smarty在开发之初就将高度的可配置性作为自己的一个设计目标。它本身以100%的PHP编写,以源代码的方式发行,只需要将Smarty简单地拷贝到你的文件路径中,就可以使用了。Smarty的各项配置变量,都可以通过修改config文件或者手动编码进行定制。例如,Smarty默认的定界符是花括号({}),但是这往往和Javascript以及CSS中的花括号冲突。为了解决这一问题,可以简单地将默认定界符修改为其他的字符(例如ASP风格的“<%”和“%>”)。
◆可扩展性。Smart的实现基于面向对象的架构,并且提供了插件机制,非常便于用户修改和扩展其默认的认为。当然,你也可以直接修改它的源代码来达到目的。(对于基于脚本语言的开源应用来说,这是非常惬意的,因为你甚至不需要重新编译。)
让我们来看一个最简单的Smarty应用。这个应用包括两个文件:
TestSmarty.php→调用Smarty类库,初始化变量,并解析相应的模板文件
TestSmarty.tpl→模板文件,其实就是包含了Smarty标签的HTML,放在指定的模板目录下,默认是./templates
TestSmarty.php的内容如下:
 
<?php
include_once("./smarty/Smarty.class.php");
 
$Smarty = new Smarty();
$Smarty->assign("HelloStr", "Hello, world");
$Smarty->display("TestSmarty.tpl");
?>
 
这个文件的内容非常简单,任何有过PHP经验的程序员都应该能够理解:首先将Smarty类库所在的文件include进来,然后创建一个新的Smarty对象,并对HelloStr变量进行赋值,最后解析TestSmarty.tpl文件。
TestSmarty.php的内容如下:
 
This is a string from Smarty: {$HelloStr}
 
解析的结果为:
 
This is a string from Smarty: Hello, world
 
此时检查存放编译后的PHP文件的子目录(默认是./templates_c),可以找到一个名叫%%65^650^65099D8B%%TestSmarty.tpl.php的文件,内容如下:
 
<?php /* Smarty version 2.6.18, created on 2007-08-12 03:04:56
         compiled from TestSmarty.tpl */ ?>
This is a string from Smarty: <?php echo $this->_tpl_vars['HelloStr']; ?>
 
这就是Smarty引擎编译生成的结果。
为了启用缓存,可以在TestSmarty.php文件中加入这么一行(当然必须在display方法之前):
 
$Smarty->caching = 1;
 
重新访问该页面,然后检查存放缓存文件的子目录(默认是./cache),可以找到一个名叫%%65^650^65099D8B%%TestSmarty.tpl的文件,内容如下:
 
136
a:4:{s:8:"template";a:1:{s:14:"TestSmarty.tpl";b:1;}s:9:"timestamp";i:1186888266;s:7:"expires";i:1186891866;s:13:"cache_serials";a:0:{}}This is a string from Smarty: Hello, world
这就是生成的缓存文件,在静态的HTML文件之前,包含了已经序列化的PHP信息。虽然这些信息无法被直接阅读,但是多少还是能够猜测出来:模板的子目录,模板文件名,时间戳,生存期(过期时间),等等。如果读者有兴趣研究它们的详细定义,可以阅读Smarty的源代码。
注意,上述信息中包含了一项:生存期,即当前缓存在多长时间以后过期。Smarty默认的生存期是1小时,即3600秒。可以通过修改Smarty属性来设置生命期,代码如下:
 
$Smarty->cache_lifetime = 1800;
 
时间单位是秒,设置为1800表示当前缓存半小时后过期。
Smarty还支持为同一个模板创建多个缓存实例,这在实际应用中是非常常见的。举例来说,假设某个博客系统中,显示article的页面为Article.php,对应的模板文件为Article.tpl。但是,article页面的内容根据不同的article ID而不同,因此,必须为同一个页面创建不同的缓存实例。Smarty可以轻松做到这一点:
 
$Smarty->display("Article.tpl", $ArticleId);
 
只要将一个唯一标识符(在这个例子中是article的ID)作为第二个参数传给display方法,Smarty就会自动完成一切。
Smarty出现的时间虽然较老牌的PHPLib Template和FastTemplate为晚,但是发展非常迅速,而且已经成为PHP的官方子项目,拥有二级域名http://smarty.php.net/。正如它的官方站点上所说,与其说Smarty是一个模板引擎,不如说它是一个表现层的Framework。这句话极为重要。
作者个人认为,Smarty诞生和逐渐取得主流地位的意义,不仅仅是提供了一个优秀的模板引擎,而是表示PHP在解决更大规模的应用上迈出了坚实的一步。我们可以看到,PHP,或者说LAMP,正在以稳健而持续的步伐,向企业级应用迈进。■
 

Zend Framework
   从理论上来说,PHP是一种通用的动态语言,它可以替代Perl实现通用的脚本,甚至可以创建客户端GUI程序(通过GTK+)。但是,在实际应用中,PHP在绝大多数情况下都被用来开发Web应用。即使在Java和.NET这样有软件业界巨头支持的重量级竞争对手面前,出身草莽的PHP也毫不逊色,尤其是在应用最为广泛的轻量级web开发领域,PHP一直牢牢占据着领先的位置。在这一领域参与竞争的其他语言,例如Python和Perl,虽然各具特色,但是仍然无法撼动PHP的地位。PHP是当之无愧的“Web开发第一语言”,而且似乎没有什么能对它构成挑战。随着web应用在软件界的地位越来越重要,PHP也逐渐从脚本小子手中的玩具,转变成重要的工业语言,并获得了IBM这样的巨人的支持。
   但是,就在近两三年,一种新的Web开发解决方案的迅速崛起,震动了整个业界,让PHP开始感到王座不稳。这个解决方案,当然就是Ruby on Rails。
   本文无意对PHP和Ruby这两种语言进行全面的比较(虽然这的确是一个非常有意思的话题)。无论读者对Ruby和ROR持什么看法,有一点却是不争的事实:Ruby作为一种新的语言,虽然极具特色,但是并没有得到业界的普遍接受;只有在ROR诞生以后,以其惊人的生产力征服了无数开发者,Ruby这才一飞冲天。也就是说,Ruby至于在很大程度上是依靠ROR这个框架,才能起迅速崛起。
   对于感到严重威胁的PHP社区,如果要对Ruby进行有效的反击,这似乎是一条可行的道路:基于PHP语言,实现一个新的web开发框架,能够达到甚至超过ROR那样的生产力。这既是对Ruby的反击,其实也是PHP自身发展的必然结果。因为PHP在其发展过程中,早已经出现了数十个各种各样的框架,只是尚没有一个能具有ROR那样的生产力和影响力。
   但是,实现这样一个框架,也面临着不少的问题:
  
  &#61548; PHP语言本身能否完成这一任务?PHP原本只是用于快速解决web应用的简单脚本,虽然经过不断的发展,已经具有了许多高级的语言特征,但是是否能够实现象ROR那样精巧和强大的框架,能够达到ROR那样的生产力?
  &#61548; PHP社群能否接受这样一个框架?PHP的使用者,大多数是脚本小子出身,习惯于快速、敏捷、直观的开发方式,对于重量级的框架,未必能够普遍认同。
  
   无论这些问题的答案如何,重要的是,有人这么做了。Zend Framework就是这一思路的结果。
   如上所述,Zend Framework就是这样一个完全基于PHP语言的、向ROR学习的、针对web应用开发的框架。与其它同类的框架相比,Zend Framework有两个特点让它显得与众不同:
  
  &#61548; Zend Framework由Zend公司开发,因此它是一个“官方的”框架。众所周知,Zend公司是PHP编译器的维护者,也是Zend Optimizer、Zend Studio等一系列PHP相关产品的拥有者。由于这一关系,Zend Framework虽然没有被内置到PHP发行包中,但也算得上PHP的官方解决方案了。(顺便说句题外话,Zend公司最近发布了Zend Studio for Eclipse的beta版本,有兴趣的读者不妨尝试一下。)
  &#61548; Zend Framework跟ROR实在太象了。和ROR一样,Zend Framework使用了同样的Front Controller模式,同样的Model/Controller/View模型,同样的ORM思路,甚至连命名规则都十分相似。当然了,Zend Framework跟ROR的不同之处还是很多的,至于底层的实现细节,肯定区别更大。但是,不管是有意还是无意(好吧,我承认“无意”绝对不可能做成这样),Zend Framework的实现思路和ROR是非常相似的。
  
  这是一个典型的Zend Framework应用的目录:
  
  /root
  /application
  /controllers
  /models
  /views
  /filters
  /helpers
  /scripts
  /library
  /public
  /images
  /scripts
  /styles
  
   其中,library目录下存放的是Zend Framework所有的库文件,如果还有其他第三方的库文件,也可以存放在这里。Public目录是唯一一个可以通过web方式直接访问的目录,但它并不存放php文件,只存放CSS、JavaScript脚本、图片等静态文件。Application目录中存放着实现应用逻辑的所有PHP文件,它又拥有三个子目录:controllers、models、views。顾名思义,它们分别用于存放控制器/模型/视图的相应PHP文件。
   Zend Framework实现了Front Controller模式,这意味着,所有的http请求都会被转发到同一个入口,然后再路由到相应的控制器。这个入口就是root根目录下的index.php文件。Zend Framework通过URL Rewrite技术来实现这一点。在root根目录下,存在一个.htaccess文件,内容如下:
  
  RewriteEngine on
  RewriteRule .* index.php
  php_flag magic_quotes_gpc off
  php_flag register_globals off
  
   熟悉apache的URL Rewrite技术的读者,很容易理解它的意义:将所有的请求转发到index.php。当然,对于图片之类的静态文件的请求不需要被转发,这可以通过在public目录下放置另一个.htaccess来覆盖上级目录的Rewrite规则。
   (习惯在IIS下运行PHP的读者可能会问,Zend Framework是否可以在IIS下运行呢?由于IIS不支持基于. htaccess的URL Rewrite,因此Zend Framework无法简单地在IIS下运行。但是,由于IIS支持基于HttpModule的URL Rewrite,因此理论上经过某些修改,是有可能让Zend Framework在IIS下运行的。有兴趣的读者可以自行尝试。)
   Index.php的功能是将http请求转发到相关的控制器,另外,它还需要完成一些初始化时期的配置工作。它的内容如下:
  
  <?php
  error_reporting(E_ALL|E_STRICT);
  date_default_timezone_set('Europe/London');
  set_include_path('.' . PATH_SEPARATOR . './library'
  . PATH_SEPARATOR . './application/models/'
  . PATH_SEPARATOR . get_include_path());
  include "Zend/Loader.php";
  Zend_Loader::loadClass('Zend_Controller_Front');
  // setup controller
  $frontController = Zend_Controller_Front::getInstance();
  $frontController->throwExceptions(true);
  $frontController->setControllerDirectory('./application/controllers');
  // run!
  $frontController->dispatch();
  ?>
  
   开始的几行,分别用于配置报错等级、设置时区、设置包含文件路径,以及载入相关文件。Zend_Loader::loadClass是一个静态方法,用于装载指定的类。在载入Zend_Controller_Front以后,程序建立了这个类的实例,通知它抛出所有的异常,并且设置了控制器所在的目录。最后,调用dispatch()方法,将http请求转发到相应的控制器。
  在application/controllers目录下,存放着所有的控制器类。那么路由器是根据什么规则寻找相应的控制器呢?很简单,它按照一定的命名规范来进行匹配。下面是一个控制器文件的内容:
  
  root/application/controllers/IndexController.php
  
  <?php
  class IndexController extends Zend_Controller_Action {
  function indexAction() {
  echo "<p>in IndexController::indexAction()</p>";
  }
  function addAction() {
  echo "<p>in IndexController::addAction()</p>";
  }
  function editAction() {
  echo "<p>in IndexController::editAction()</p>";
  }
  function deleteAction() {
  echo "<p>in IndexController::deleteAction()</p>";
  }
  }
  
   所有的控制器类都派生自Zend_Controller_Action。控制器名字必须大写字母开头,其他字母必须都为小写,最后加上Controller结尾,并且存放在同名的php文件中。本例中的控制器是IndexController。(需要指出的是,“其他字母必须都为小写”是一条很诡异的规则,这意味着CurrentUserController这样的名字是不合法的,无法被正确地路由。如果你这样做了,会是个很难发现的bug。)
   控制器拥有一个或多个行为(action),体现在代码上,行为是控制器类拥有的public方法,以小写字母开头,以Action结尾。在本例中,indexAction、deleteAction、addAction和editAction是IndexController的四个行为。
   在浏览器中,可以通过“控制器/行为”的URL,来访问相应的行为。如上例,URL和行为的对应关系是:
  
  URL Action
  http://localhost/root/
   IndexController::indexAction()
  http://localhost/root/index
   IndexController::indexAction()
  http://localhost/root/index/add IndexController::addAction()
  http://localhost/root/index/edit IndexController::editAction()
  http://localhost/root/index/delete IndexController::deleteAction()
  
   可以看到,indexAction是一个控制器的缺省行为:如果没有指定行为,路由器将把请求转发到indexAction方法。同样,IndexController是缺省的控制器:如果没有指定控制器,路由器将把请求转发到IndexController控制器。
   在上例中,行为方法直接输出一行代码作为响应。在规范的应用中,与显示相关的代码应该存放在视图中。一个规范的控制器行为方法内容如下:
  
  function indexAction() {
  $view = new Zend_View();
  $view->setScriptPath('./application/views/scripts’);
  $view->title = "Hello world";
  echo $view->render();
  }
  
   首先,创建了一个视图类(Zend_View)的实例,然后设置其路径,并对它的title属性赋值,最后调用render()方法,渲染相应的模板文件,将结果返回给浏览器。
   同样的,视图根据特定的命名规范来匹配相应的模板文件。对于IndexController::indexAction()方法,视图会查找如下模板文件:root/application/views/scripts/index/index.phtml。后缀名为.phtml的模板文件事实上就是一个标准的PHP文件。(熟悉.rhtml后缀名的读者有何感觉?&#61514;)在本例中,index.phtml的内容如下:
  
  <html>
  <head>
  <title><?php echo $this->escape($this->title); ?></title>
  </head>
  <body>
  <h1><?php echo $this->escape($this->title); ?></h1>
  </body>
  </html>
  
   最后,我们终于又一次看到了亲切的“Hello world”。
  Zend Framework使用Zend_Db_Table来实现类似于ActiveRecord的ORM功能。和任何ORM一样,首先需要为它配置数据库的相关信息(服务器,用户名,密码,数据库名,等等)。虽然这并不复杂,但本文不拟讨论这些细节问题。(Zend Framework在它的Roadmap中计划支持YAML!)一旦配置完成后,可以在/application/models子目录下创建和数据库表相对应的模型类。
  举例来说,在数据库中存在表user,结构如下:
  
  CREATE TABLE user (
  id int(11) NOT NULL auto_increment,
  name varchar(100) NOT NULL,
  password varchar(100) NOT NULL,
  PRIMARY KEY (id)
  )
  
   相应地,可以在models目录下创建一个模型:
  
  root/application/models/User.php
  
  <?php
  class User extends Zend_Db_Table
  {
  protected $_name = 'user';
  }
  
   所有数据库相关的代码,都已经被封装在抽象类Zend_Db_Table中。有ROR经验的读者,可以和ROR中的模型对比一下:
  
  Class User < ActiveRecord::Base
  End
  
   Zend Framework的模型仅仅多出了一行,就是把数据库表名赋予$_name属性。
   下面来看看怎么使用这个User模型类。新的indexAction方法如下:
  
  function indexAction() {
  …
  $user = new User();
  $view->user = $user->fetchAll();
  echo $view->render();
  }
  
   User类的fetchAll()返回一个数组,包含该表中所有的内容。这个数组被存放在$view的user属性。接下来,看看新的index.phtml:
  
  …
  <table>
  <tr>
  <th>Id</th>
  <th>Name</th>
  </tr>
  <?php foreach($this->user as $user) : ?>
  <tr>
  <td><?php echo $this->escape($user->id);?></td>
  <td><?php echo $this->escape($user->name);?></td>
  </tr>
  <?php endforeach; ?>
  </table>
  …
  
   这段代码遍历了$this->user,并将它的属性依次打印出来。
   除了基本的MVC模型以外,Zend Framework还提供了一系列高级功能,下面是一个不全面的列表:
  
  &#61548; Zend_Acl(access control list):实现了非常灵活的权限控制机制,通过对特定的角色和资源进行规则定义,可以方便地实现各种复杂的权限控制规则,并且权限可以按照角色的树型结构实现继承。
  &#61548; Zend_Cache:提供了一种通用的数据缓存方式,可以将任何数据缓存到文件系统、数据库、内存,能够与Memcached和SQLite这样的第三方软件很好地集成。
  &#61548; Zend_Log:提供了一种通用的log解决方案,支持多个log后端,支持log信息格式化,支持信息过滤。
  &#61548; Zend_Pdf:完全用PHP语言实现的PDF文件操作引擎。
  &#61548; Zend_Feed:封装了对RSS和ATOM的操作。
  &#61548; Zend_Gdata:封装了对Google Data API的操作。
  &#61548; Zend_Json:JSON是AJAX中必不可少的数据格式。Zend_Json封装了数据在PHP和JSON格式之间的转换操作。
  &#61548; Zend_Rest:REST是越来越流行的Web Service协议,有逐渐凌驾传统的SOAP协议之势。Zend_Rest封装了对REST的操作,可以方便地实现REST的客户端或服务端应用。
  &#61548; Zend_Search_Lucene:封装了对著名的全文检索引擎Lucene的操作。
  
  Zend Framework在7月份发布1.0正式版,现在最新的版本为1.0.2。可以说,Zend Framework还是一个非常新的事物。但是在它漫长的测试期间,已经有不少人在尝试,甚至用于正式的项目,因为它的种种功能实在太让人馋涎欲滴了。
  回到本文开头的两个问题,Zend Framework的前途将会如何呢?虽然作者深知揣测未来的风险,但是还是很愿意尝试回答这两个问题:第一,Zend Framework的出现,已经证明了PHP 5有能力实现大型的、复杂的框架,而由于PHP的编译器掌握在Zend公司手中,能够保证PHP语言会持续发展,以满足进一步的需求。第二,PHP本来就已经在逐步进入企业开发的领域,出现一个重量级的Framework是必然的事情。也许PHP未来的开发主力,将由经过短期培训的、偏向快速解决问题的初级程序员,变为经验丰富的、熟悉各种复杂的IT平台、侧重应用的安全性和健壮性的资深工程师。毕竟,只要经过不懈的努力,脚本小子也可能有成为大师的一天,对吗?
  至于Zend Framework和ROR之间的竞争,现在没有人能够给出答案。也许到目前为止,Zend Framework还无法达到ROR 100%的生产力,但是它拥有更广大的用户基础,更成熟的语言,更短的学习曲线,类似C和Java的语法也更让人容易掌握。ROR中的DRY、“惯例重于配置”等原则,在Zend Framework中也得了很好的秉承和发扬。无论它是否能够有效地狙击ROR,毫无疑问的是,一旦Zend Framework被PHP社群广泛接受,PHP的生产力将会有极大的提高。

[1] [2]

责任编辑 webmaster

 
发表评论  打印本文  推荐本文  加入收藏  返回顶部  关闭窗口
 
 
 
 
评论更多>>
 
 
 
发表
 
姓名: QQ:
性别: MSN:
E-mail: 主页:
评分: 1 2 3 4 5
评论内容:
验证码:
  
  • 请遵守《互联网电子公告服务管理规定》及中华人民共和国其他各项有关法律法规。
  • 严禁发表危害国家安全、损害国家利益、破坏民族团结、破坏国家宗教政策、破坏社会稳定、侮辱、诽谤、教唆、淫秽等内容的评论 。
  • 用户需对自己在使用本站服务过程中的行为承担法律责任(直接或间接导致的)。
  • 本站管理员有权保留或删除评论内容。
  • 评论内容只代表网友个人观点,与本网站立场无关。
  •