恭喜!通过Symfony2的学习,你将用你自己的方式开发出更加高效、全面和流行的Web应用。Symfony2是基于这样一个基础构建的:它是一个开发工具,能够让你用你自己的方式更加快捷地开发出更为健壮的应用程序。Symfony2集成了许多技术的优点,包括工具和概念,你将学到大多数人多年来努力的方向。换句话说,你不只是在学习Symfony2,你还学习Web基础、最佳开发实践以及在Symfony2内部或单独使用的那些新的、令人惊喜的PHP库。所以,请做好准备!

如上所述,本章将从说明Web开发的通用基础概念开始:HTTP协议,无论你的技术背景或首选编程语言是什么,本章内容对于所有人来说都是必读的。

HTTP是简单的

HTTP(超文本传输协议)是一个允许两个机器相互通信的文本语言。就是这样,例如当检查XKCD最新漫画时,下列(类似)对话将会发生:

 

Symfony2Book01:Symfony2和HTTP基础_HTTP 

虽然实际使用的语言将更加正规,但它依然还是简单的。HTTP是个术语,是用来描述这种简单的、基于文本的语言。无论你从事何种的Web开发,你的服务器总是要理解基于文本的请求,并返回基于文本的响应。

Symfony2是基于现实构建的,无论你是否意识到这一点,HTTP都是你每天所需的。随着对Symfony2学习的深入,你将学会如何掌握它。

步骤1:客户端发出请求

Web上的每一个对话都是从请求开始的,这个请求是由客户端(诸如:网页浏览器、iPhone应用程序等)创建的一种特殊格式的文本消息,该文本消息能够被HTTP所理解。客户端将请求发送到服务端,然后等待服务端响应。

如下图所示,浏览器与XKCD服务端之间交互的第一部分(请求):

 

Symfony2Book01:Symfony2和HTTP基础_Symfony2_02 

以HTTP的方式来说,HTTP请求就象下面这个样子:

  1. GET / HTTP/1.1 
  2. Host: xkcd.com 
  3. Accept: text/html 
  4. User-Agent: Mozilla/5.0 (Macintosh)

这个简单的消息传送了客户端所需哪个资源被请求。HTTP请求的第一行是最重要的,它包含了两个方面:URI和HTTP方法。

URI(如 /、/contact等)都是客户端所需资源的唯一地址或位置。HTTP方法则是表示你想对资源做什么,HTTP方法是请求的动词,用以定义你对资源的操作:

GET 从服务器上检索资源
POST 在服务器上创建一个资源
UPDATE
 
更新服务器上的资源
DELETE
 
删除从服务器上该资源

因此,你可以发送一条看上去要删除某个指定博文的请求,如:

  1. DELETE /blog/15 HTTP/1.1

实际上在HTTP规范中定义了九种HTTP方法,但它们中的许多方法并没有得到广泛地使用和支持,实际上许多现代的浏览器并不支持PUT和DELETE方法(译者:随着RESTful的WebService出现,这种情况得到了极大的改善。)

除了第一行,HTTP请求所包含其他行的信息被称为HTTP请求头,头包含更多的信息:主机、客户端接受响应的格式、客户端所用代理的应用程序等。还有许多其它的HTTP请求头存在,你可以在维基百科的HTTP头字段列表中找到它们。

步骤2:服务端返回响应

一旦服务端得到请求,它就明确地知道客户端需要哪个资源(通过URI)以及希望对该资源进行什么操作(通过HTTP方法)。例如得到一个GET请求,服务端将准备资源,并在HTTP响应中将其返回给客户端。XKCD服务端返回的响应:

 

Symfony2Book01:Symfony2和HTTP基础_HTTP_03 

翻译成HTTP,被返回给客户端的响应如下所示:

  1. HTTP/1.1 200 OK 
  2. Date: Sat, 02 Apr 2011 21:05:05 GMT 
  3. Server: lighttpd/1.4.19 
  4. Content-Type: text/html 
  5.  
  6. <html> 
  7.   <!-- HTML for the xkcd comic --> 
  8. </html>

HTTP响应包含客户端所请求的资源以及其它相关信息,其中第一行最重要,它包含了HTTP状态码(这里是200)。状态码返回给客户端请求的总体结果,请求是否成功?是否存在错误?不同的状态码表示着成功、错误或者客户端需要做其它一些事(如重定向到另一页)。一个完整的列表可以在维基百科的HTTP状态代码列表中找到。

象请求一样,响应中其它HTTP附加信息也被称之为HTTP响应头。例如一个重要的HTTP响应头被称为Content-Type,同一响应的内容可以返回不同的格式,如HTML、XML和JSON等。Content-Type响应头将通知客户端返回的内容是什么格式的。

还存在许多其它的HTTP响应头,其中有些十分强大。例如,某些响应头可以创建强大的缓冲系统。

请求响应和Web开发

请求-响应对话是驱动Web上所有通信的基础,它是如此的强大和重要,它又是如此的简单。

一个最重要的事实就是:无论你使用何种语言、构建何种应用(Web、移动、JSON API)还是遵循何种开发理念,应用程序的最终目标就是理解每个请求,创建并返回相应的响应。

Symfony2就是适用这一现实的架构。

要了解更多HTTP的规范,可以阅读原始的HTTP 1.1 RFC或HTTP Bis(用于阐明原始规范),有一个很好的Firefox扩展Live HTTP Headers用来查看浏览时活动HTTP的请求头和响应头。

PHP中的请求和响应

那么如何使用PHP来与“请求”交互并创建“响应”呢?

  1. <?php 
  2. $uri = $_SERVER['REQUEST_URI']; 
  3. $foo = $_GET['foo']; 
  4.  
  5. header('Content-type: text/html'); 
  6. echo 'The URI requested is: '.$uri
  7. echo 'The value of the "foo" parameter is: '.$foo;

这段小程序看上去有些奇怪,但它其实只是从请求中提取信息,并用该信息创建响应。为了更好地解析HTTP请求信息,PHP使用全局变量:$_SERVER和$_GET,它们包含了请求的所有信息。同样,为了替换返回文本方式的HTTP响应,你也可以使用header()函数来创建响应头,并简单地将响应消息内容打印出来。PHP将自动创建出一个HTTP响应并将之返回给客户端:

  1. HTTP/1.1 200 OK 
  2. Date: Sat, 03 Apr 2011 02:14:33 GMT 
  3. Server: Apache/2.2.17 (Unix) 
  4. Content-Type: text/html 
  5.  
  6. The URI requested is: /testing?foo=symfony 
  7. The value of the "foo" parameter is: symfony

Symfony2中的请求和响应

Symfony2使用两个类可以非常容易地与请求和响应交互,从而取代原始的PHP方式。Request类是HTTP请求面向对象的简单表示。有了它,你获取请求信息将易如反掌。

  1. use Symfony\Component\HttpFoundation\Request; 
  2.  
  3. $request = Request::createFromGlobals(); 
  4.  
  5. // 请求的URI (如:/about) ,没有任何查询参数
  6. $request->getPathInfo(); 
  7.  
  8. // 分别检索GET和POST变量
  9. $request->query->get('foo'); 
  10. $request->request->get('bar'); 
  11.  
  12. // 检索被foo标识的UploadedFile实例
  13. $request->files->get('foo'); 
  14.  
  15. $request->getMethod();          // GET, POST, PUT, DELETE, HEAD 
  16. $request->getLanguages();       // 客户端接受语言的数组

Request类在后台所做的大量工作能使你省不少心。如isSecure()方法通过用PHP检查三个不同的值来确定用户是否使用了安全链接(通过https)。

Symfony2还提供了一个Response类,一个HTTP响应的简单PHP表示。这允许你使用面向对象的接口去构建返回客户端所需的响应。

  1. use Symfony\Component\HttpFoundation\Response; 
  2. $response = new Response(); 
  3.  
  4. $response->setContent('<html><body><h1>Hello world!</h1></body></html>'); 
  5. $response->setStatusCode(200); 
  6. $response->headers->set('Content-Type''text/html'); 
  7.  
  8. // prints the HTTP headers followed by the content 
  9. $response->send();

就算Symfony2再没提供其它工具,你也已经有了可以轻易访问请求信息的工具包和用来创建响应的面向对象接口。即使你学了更多Symfony2的功能,也请牢记,你应用程序的目标始终是解释请求,并根据你应用程序的逻辑创建相应的响应。

Request类和Response类都是Symfony2中名为HttpFoundation组件的一部分。该组件可以独立使用,它还提供处理会话和文件上传的类。

从请求到响应

同HTTP一样,RequestResponse对象也很简单。应用程序最复杂的部分是在两者之间写些什么。换句话说,真正的工作来自于编写解释请求并创建响应的代码。

你的应用程序可能做了诸如发送电子邮件、处理提交表单、向数据库写入数据、渲染HTML页面和确保内容安全性等诸多事情,但你如何来管理这一切,同时还要保持你代码的组织性和可维护性呢?

Symfony2就是用来解决上述问题,而无须让你因此分心。

前端控制器

传统方式中构建的应用程序,网站中的每一“页”都是它自身的物理文件。

  1. index.php 
  2. contact.php 
  3. blog.php

使用这种方式存在几个问题:不灵活的URL(你是否可以将blog.php文件改名为news.php,而无须破坏你所有的链接?)、每个文件都必须手工包含一些核心文件集以确保安全性(数据库连接和网站外观必须保持一致)。

更好的解决方案是使用前端控制器:单一的PHP文件,该文件用来处理进入应用程序的所有请求。例如:

/index.php 执行index.php
/index.php/contact 执行index.php
/index.php/blog 执行index.php

使用Apache的mod_rewrite模块(或其它Web服务器的相似模块),URL可以很方便地被清理成诸如:/、/contact、/blog 这样的链接。

现在,所有请求的处理都完全一样。前端控制器将被始终执行,不同的URL将被内部路由到你应用程序的不同部分,而无须根据不同的URL执行不同的PHP文件。这样就解决了前面传统方式所带来的问题。几乎所有现代应用程序都可以做到这一点,其中包括类似WordPress这样的应用程序。

保持组织性

然而,在你的前端控制器中,你如何知道哪些页面会被渲染,又如何保证它们会被合理渲染呢?无论使用哪种方式,你都将需要检查传入的URI,并根据传入的值执行不同的代码。这可以很快让代码变得丑陋。

  1. // index.php 
  2.  
  3. $request = Request::createFromGlobals(); 
  4. $path = $request->getPathInfo(); // the URL being requested 
  5.  
  6. if (in_array($patharray('''/')) { 
  7.     $response = new Response('Welcome to the homepage.'); 
  8. elseif ($path == '/contact') { 
  9.     $response = new Response('Contact us'); 
  10. else { 
  11.     $response = new Response('Page not found.', 404); 
  12. $response->send(); 

要解决这个问题很困难。但幸运的是,这正是Symfony2设计要做的。

Symfony2应用流

当你让Symfony2来处理每个请求时,生活就变得容易多了。Symfony2对每个请求都遵循同一简单模式:

Symfony2Book01:Symfony2和HTTP基础_Symfony2Book_04 

传入的请求被路由解释并传给控制器函数,以便返回Response类对象

你网站的每一页都被定义在路由配置文件中,在那里不同的URL被映射到不同的PHP函数。每个PHP函数(又名控制器)的工作就是得到请求的信息(也可以使用Symfony2中有许多其它的工具)去创建并返回一个Response对象。

就是这么简单,让我们回顾一下:

1、每个请求执行前端控制器文件;
2、根据你的路由配置和请求信息,路由系统决定应该执行哪个PHP函数;
3、正确的PHP函数被执行,在那里你的代码将创建并返回相应的Response对象。

实战Symfony2请求

无需深入了解太多细节,让我们看看实际情况。假设你需要在你Symfony2程序中添加/contact页,首先在你的路由配置文件中添加一个条目:

  1. contact: 
  2.     pattern:  /contact 
  3.     defaults: { _controller: AcmeDemoBundle:Main:contact }

在本例中使用了YAML来定义路由配置,路由配置也可以使用其他语言如XML或PHP来定义。

当有人访问/contact页时,该路由条目被匹配,同时执行指定的控制器。正如你将在路由一章中所学到的那样,AcmeDemoBundle:Main:contact字符串是个简单的语法,它指向MainController类中的PHP函数contactAction:

  1. class MainController 
  2.     public function contactAction() 
  3.     { 
  4.         return new Response('<h1>Contact us!</h1>'); 
  5.     } 
  6. }

在这个简单的例子里,控制器简单地创建了一个带有“<h1>Contact us!</h1>”HTML内容的Response对象,在控制器一章中,你将学到控制器是如何渲染模板,并将你的展示代码(如HTML语句)并入独立的模板文件中。这样就解除了对控制器只能进行硬处理(如数据库交互、处理提交表单和发送电子邮件等)的担忧。

Symfony2:构建你的应用程序,而非你的工具

你现在知道任何应用程序的目的都是解释每个传入请求并创建适当的响应。当应用程序增长时,保持代码的组织性和可维护性将变得越来越困难。总是一遍又一遍地重复同一烦人的任务:持久化数据到数据库、渲染和重用模板、处理提交表单、发送电子邮件、验证用户输入和安全性处理等等。

好消息是这些问题并非独一无二的,Symfony2提供了完整的框架工具让你构建你的应用程序而非工具。使用Symfony2,它并未把什么强加组你,你可以自由使用Symfony2框架的全部,或者是它自身的一部分。

独立工具:Symfony2组件

那么什么是Symfony2呢?首先Symfony2是20多个库的集合,这些库可以独立用于任何一个PHP项目中。这些库被称为Symfony2组件,不管你应用程序如何开发,它们都包含了几乎任何情况下都有用的东西。

  • HttpFoundation - 包含Request和Response类,以及其他处理会话和文件上传的类;
  • Routine - 强大而快捷的路由系统,让你将特定的URI(如:/contact)映射到如何处理这一请求的信息上(如执行 contactAction() 方法);
  • Form - 一个灵活的、全功能的创建表单和处理表单提交的框架;
  • Validation - 一个创建数据规则,并随后验证用户提交的数据是否符合所创规则的系统;
  • ClassLoader - 一个自动加载库,无需在使用PHP类时手工加载包含这些类的文件;
  • templating - 一个渲染模板、处理模板继承关系(即布局装饰模板)和执行其他通用模板任务的工具包;
  • Security - 一个功能强大的,能处理应用程序内所有安全类型的库;
  • Translation - 一个翻译你应用程序内字符串的框架。

每一个组件都是独立的,可用于任何PHP项目中,而不管你是否使用了Symfony2框架。它们的每一个既可以在需要时使用,也可以在必要时被替换。

完整的解决方案:Symfony2

那么什么是Symfony2框架呢?Symfony2是个PHP库,它实现两个功能:

  • 提供可选择的组件(如Symfony2组件)和第三方库(如用Swiftmailer发送电子邮件);
  • 提供合理的配置以及将这一切都粘合起来的“胶水”库

这个框架的目标是整合许多独立的工具,以期给开发人员一致的体验。甚至就连Symfony2框架本身也是一个Bundle(类似插件),在必要时也可以被完全配置和替换。

Symfony2为快速开发应用程序提供了强大的工具集,普通用户可以通过Symfony2的发行版(缺省提供了合理的项目架构)来快速进行开发,对于更高级的用户而言,The sky is the limit.