symfony学习笔记2—纯的PHP代码和symfony的区别

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS PostgreSQL,集群系列 2核4GB
简介: Symfony vs 纯PHP为啥symfony比普通的php文件访问要好?这一章我们写一个简单的php文件项目,然后组织它,你会发现为什么web应用会发展到现在这个样子。最后我们将学习symfony如何重用代码。

Symfony vs 纯PHP
为啥symfony比普通的php文件访问要好?
这一章我们写一个简单的php文件项目,然后组织它,你会发现为什么web应用会发展到现在这个样子。最后我们将学习symfony如何重用代码。

使用纯PHP创建一个简单博客程序
这里我们先使用纯php(flat php我擦 ,怎么翻译呢,就是php文件,但是谁不是php文件呢?)创建一个博客程序,先写一个文章列表,这段代码很直接,但是很脏。
<?php
// index.php
$link = mysql_connect('localhost', 'myuser', 'mypassword');
mysql_select_db('blog_db', $link);

$result = mysql_query('SELECT id, title FROM post', $link);
?>

<!DOCTYPE html>
<html>
<head>
<title>List of Posts</title>
</head>
<body>
<h1>List of Posts</h1>
<ul>
<?php while ($row = mysql_fetch_assoc($result)): ?>
<li>
<a href="/show.php?id=<?php echo $row['id'] ?>">
<?php echo $row['title'] ?>
</a>
</li>
<?php endwhile ?>
</ul>
</body>
</html>

<?php
mysql_close($link);
?>

这个很简单,也很好写,但是随着应用逻辑增多很难维护。这里有一些问题:
1.没有错误检查,如果数据库连接失败怎么办
2.没有组织,如果应用变大,逻辑增多,这个文件将会变得很大,不可维护,从那里验证输入,从那里处理请求,最终写成一团乱码
3.代码不可重用,所有代码都放在一个文件中,没法重用

还有一个问题没有提到,如何从数据库中取数据,symfony使用Doctrine(一种ORM)来获取数据很方便。

展现分离
下面做一些该进将逻辑和html展现分离,代码如下:
<?php
// index.php
$link = mysql_connect('localhost', 'myuser', 'mypassword');
mysql_select_db('blog_db', $link);

$result = mysql_query('SELECT id, title FROM post', $link);

$posts = array();
while ($row = mysql_fetch_assoc($result)) {
$posts[] = $row;
}

mysql_close($link);

// include the HTML presentation code
require 'templates/list.php';

现在把hmtl内容放在另外一个文件中templates/list.php类似模板

<!DOCTYPE html>
<html>
<head>
<title>List of Posts</title>
</head>
<body>
<h1>List of Posts</h1>
<ul>
<?php foreach ($posts as $post): ?>
<li>
<a href="/read?id=<?php echo $post['id'] ?>">
<?php echo $post['title'] ?>
</a>
</li>
<?php endforeach ?>
</ul>
</body>
</html>

一般index.php那个可以叫做控制器,控制这个词在很多场合都用到,不管任何语言和框架,它指处理用户输入和返回响应的地方。在这个例子中控制器从数据库中查数据,然后包含了一个展现数据的文件。这样分离之后如果想修改展示数据的方式例如list.json.php就很容易了

业务逻辑分离
目前这个应用只包含一个页面,但是如果增加第二个页面也使用相同的数据连接,相同的传递数据,所以我们将主要的获取数据的逻辑分离出来放在一个model.php中,如下:

<?php
// model.php
function open_database_connection()
{
$link = mysql_connect('localhost', 'myuser', 'mypassword');
mysql_select_db('blog_db', $link);

return $link;
}

function close_database_connection($link)
{
mysql_close($link);
}

function get_all_posts()
{
$link = open_database_connection();

$result = mysql_query('SELECT id, title FROM post', $link);
$posts = array();
while ($row = mysql_fetch_assoc($result)) {
$posts[] = $row;
}
close_database_connection($link);

return $posts;
}

我们把这个文件命名为model.php是应为在应用中通常将获取数据层叫做model,通常主要的业务逻辑放在model里面。

现在这个控制器index.php可易写成下面这样:

<?php
require_once 'model.php';

$posts = get_all_posts();

require 'templates/list.php';

现在这个控制器主要的功能就是从model中获取数据然后调用模板渲染数据,这是个很简单的模型-视图-控制器的例子

布局分离
到目前为止我们已经涉及到三个不同的文件,几乎复用了所有的代码,只有一个地方我们没有用到就是布局文件,下面创建一个布局文件layout.php

<!-- templates/layout.php -->
<!DOCTYPE html>
<html>
<head>
<title><?php echo $title ?></title>
</head>
<body>
<?php echo $content ?>
</body>
</html>

模板templete/list.php,现在可以继承布局了。
<?php $title = 'List of Posts' ?>

<?php ob_start() ?>
<h1>List of Posts</h1>
<ul>
<?php foreach ($posts as $post): ?>
<li>
<a href="/read?id=<?php echo $post['id'] ?>">
<?php echo $post['title'] ?>
</a>
</li>
<?php endforeach ?>
</ul>
<?php $content = ob_get_clean() ?>

<?php include 'layout.php' ?>

现在可以通用这个layout了,但是还需要一些丑陋的php函数例如ob_start(),ob_get_clean()方法,下面再将symfony的处理方式。


添加博客”show”页面
博客列表页面已经重新设计,代码可以复用了。现在添加一个展示博客的页面,向这个页面传递参数ID。首先在model.php中新建一个方法,如下:

// model.php
function get_post_by_id($id)
{
$link = open_database_connection();

$id = intval($id);
$query = 'SELECT date, title, body FROM post WHERE id = '.$id;
$result = mysql_query($query);
$row = mysql_fetch_assoc($result);

close_database_connection($link);

return $row;
}

然后新建一个文件show.php,控制器

<?php
require_once 'model.php';

$post = get_post_by_id($_GET['id']);

require 'templates/show.php';

最后新建一个模板templates/show.php,渲染文件

<?php $title = $post['title'] ?>

<?php ob_start() ?>
<h1><?php echo $post['title'] ?></h1>

<div class="date"><?php echo $post['date'] ?></div>
<div class="body">
<?php echo $post['body'] ?>
</div>
<?php $content = ob_get_clean() ?>

<?php include 'layout.php' ?>

新建这个页面的时候已经很容易,没有重复代码,还是还是有些问题可能导致问题,例如丢失参数id将会使页面报错。如果这个导致404错误到还好,最坏的情况是sql注入。

另外一个问题是每个控制器文件都必须应用model.php,当我们要访问另外一个表,就要在这个控制器中添加另外一个model,这个是比较麻烦的。

前端控制器解决方案
解决这个问题的方法是前端控制器,通过这个控制器文件所有的请求都可以被处理,唯一要做的是修改url,这样更加灵活。

Without a front controller
/index.php => Blog post list page (index.php executed)
/show.php => Blog post show page (show.php executed)

With index.php as the front controller
/index.php => Blog post list page (index.php executed)
/index.php/show => Blog post show page (index.php executed)


用apache中的重写功能可以省略“index.php”这样的话访问就更加简单了,例如/show

使用前端控制器只有单独的一个index.php文件可以处理所有的请求,例如访问show页面,/index.php/show,将会最终执行index.php,这是一个很强大的功能。我怎么没看出来。

创建前端控制器
现在我们再往前走一大步,用一个文件处理所有的请求,修改index.php文件如下:

<?php
// index.php

// load and initialize any global libraries
require_once 'model.php';
require_once 'controllers.php';

// route the request internally
$uri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
if ('/index.php' == $uri) {
list_action();
} elseif ('/index.php/show' == $uri && isset($_GET['id'])) {
show_action($_GET['id']);
} else {
header('Status: 404 Not Found');
echo '<html><body><h1>Page Not Found</h1></body></html>';
}

为了组织好代码控制器文件(index.php,show.php)现在变成了方法并且放在一个文件中,controllers.php如下:

function list_action()
{
$posts = get_all_posts();
require 'templates/list.php';
}

function show_action($id)
{
$post = get_post_by_id($id);
require 'templates/show.php';
}

作为一个前端控制器index.php中有新的规则,一是加载核心的类,可以调用到控制方法list_action()和show_action()。事实上这里的前端路由已经和symfony的机制很接近了。

现在,这个这个应用已经从一个简单的php文件重构成一个有组织的结构,最大程度的复用代码,但是还是看到一些代码不协调。为了完成这个blog我们可能需要写很多类似的代码,还要处理用户输入,验证,日志,安全等等。

初始symfony
牛逼哄哄的symfony出场了。(Symfony to the rescue)我想对手册作者说,你能不能不装逼,把要讲的东西讲清楚就好了!在使用之前我们要下载symfony,可以使用composer,这个工具可以下载正确版本的symfony和它所依赖的所有文件,提供一个自动下载器,我擦你妹 ,autoloader是个可以一个工具,用来使用类但是不显示的包含类文件,我擦啊 你他妈还玩花啊,不引用就用,构高级的。

有没有考虑windows用户的感受你!

 

在根目录下创建一个文件composer.json,如下:

{
"require": {
"symfony/symfony": "2.6.*"
},
"autoload": {
"files": ["model.php","controllers.php"]
}
}

然后下载Composer并安装,命令如下:

$ composer install

下载依赖文件的时候Composer创建了一个文件vendor/autoload.php,这个文件中有所有symfony frameword中所有需要的文件,就是composer.jeson中的。当初说好的symfony处理请求响应呢, 这里干吗呢?

symfony提供Request和Response两个类,他们处理请求和响应,这个不知道重复多少遍了!下面用symfony来写这个blog

<?php
// index.php
require_once 'vendor/autoload.php';

use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

$request = Request::createFromGlobals();

$uri = $request->getPathInfo();
if ('/' == $uri) {
$response = list_action();
} elseif ('/show' == $uri && $request->query->has('id')) {
$response = show_action($request->query->get('id'));
} else {
$html = '<html><body><h1>Page Not Found</h1></body></html>';
$response = new Response($html, Response::HTTP_NOT_FOUND);
}

// echo the headers and send the response
$response->send();

控制器现在返回一个response对象,可以添加一个render_template()方法,和symfony中的模板很像,如下:

// controllers.php
use Symfony\Component\HttpFoundation\Response;

function list_action()
{
$posts = get_all_posts();
$html = render_template('templates/list.php', array('posts' => $posts));

return new Response($html);
}

function show_action($id)
{
$post = get_post_by_id($id);
$html = render_template('templates/show.php', array('post' => $post));

return new Response($html);
}

// helper function to render templates
function render_template($path, array $args)
{
extract($args);
ob_start();
require $path;
$html = ob_get_clean();

return $html;
}

好吧, 通过使用symfony我们的程序更加灵活,又来,request提供访问http请求的可靠方法,getPathInfo()方法返回一个干净的url,例如:/show,/index.php/show,这样即使访问index.php/show,应用程序可以聪明的调用show_action()方法。

返回响应的时候response对象可以灵活的返回结果。

symfony简单示例
现在这个blog已经做好,但是里面还有很多代码,但是有没有办法使用更好的代码来实现这个blog,有没有办法来代替ob_start(),和ob_get_clean()呢?可以使用symfony来简化这些,如下:

// src/AppBundle/Controller/BlogController.php
namespace AppBundle\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\Controller;

class BlogController extends Controller
{
public function listAction()
{
$posts = $this->get('doctrine')
->getManager()
->createQuery('SELECT p FROM AcmeBlogBundle:Post p')
->execute();

return $this->render('Blog/list.html.php', array('posts' => $posts));
}

public function showAction($id)
{
$post = $this->get('doctrine')
->getManager()
->getRepository('AppBundle:Post')
->find($id);

if (!$post) {
// cause the 404 page not found to be displayed
throw $this->createNotFoundException();
}

return $this->render('Blog/show.html.php', array('post' => $post));
}
}

这两个控制器依然很重要哦,用Doctrin ORM来从数据库中来获取数据,模板组件渲染模板并返回一个Response对象,模板现在看上去比较简单,

<!-- app/Resources/views/Blog/list.html.php -->
<?php $view->extend('layout.html.php') ?>

<?php $view['slots']->set('title', 'List of Posts') ?>

<h1>List of Posts</h1>
<ul>
<?php foreach ($posts as $post): ?>
<li>
<a href="<?php echo $view['router']->generate(
'blog_show',
array('id' => $post->getId())
) ?>">
<?php echo $post->getTitle() ?>
</a>
</li>
<?php endforeach ?>
</ul>

layout布局是差不多的

<!-- app/Resources/views/layout.html.php -->
<!DOCTYPE html>
<html>
<head>
<title><?php echo $view['slots']->output(
'title',
'Default title'
) ?></title>
</head>
<body>
<?php echo $view['slots']->output('_content') ?>
</body>
</html>

妈蛋 $view从那里来的,没交代清除。

当symfony引擎启动的时候,妈蛋啊 , 说的这么高级 ,不就是访问网站的时候么。它需要通过请求信息和一个映射表知道执行那一个控制器。路由配置提供这个信息,如下:

# app/config/routing.yml
blog_list:
path: /blog
defaults: { _controller: AppBundle:Blog:list }

blog_show:
path: /blog/show/{id}
defaults: { _controller: AppBundle:Blog:show }

然后symfony处理一些简单的任务,前端控制器是很简单的,创建之后你就不需要再管它,说的轻巧,吃根灯草。还有如果使用symfony distribution根本不需要创建它,好吧,被你的装逼精神深深的折服了!

// web/app.php
require_once __DIR__.'/../app/bootstrap.php';
require_once __DIR__.'/../app/AppKernel.php';

use Symfony\Component\HttpFoundation\Request;

$kernel = new AppKernel('prod', false);
$kernel->handle(Request::createFromGlobals())->send();

前端控制器的唯一任务就是初始化symfony引擎Kernel,然后发送一个请求对象,symfony核心然后用这个路由映射找到执行那个控制器,最后控制器方法返回最终的响应对象。(编者一再强调很简单)

symfony的优点
开启装逼模式
什么是symfony framework,symfony framework是一个php类库,包含两个主要的任务
1.提供可选的第三方的类库组件(symfony components)
2.提供直观的配置和一个可以把很多php代码片段组合起来的胶水类库

symfony的终极目标是整合很多互不影响的组件来为开发者提供一致的使用体验。symfony本身也是一个束可以被配置和替换。symfony提供一套快速开发的工具而不需要在项目中添加额外的组件。普通用户可以快速开发,高手可以任意驰骋。

 

作者:Tyler Ning
出处:http://www.cnblogs.com/tylerdonet/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,如有问题,可以通过以下邮箱地址williamningdong@gmail.com  联系我,非常感谢。

相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
目录
相关文章
|
7天前
|
设计模式 数据库连接 PHP
PHP中的设计模式:提升代码的可维护性与扩展性在软件开发过程中,设计模式是开发者们经常用到的工具之一。它们提供了经过验证的解决方案,可以帮助我们解决常见的软件设计问题。本文将介绍PHP中常用的设计模式,以及如何利用这些模式来提高代码的可维护性和扩展性。我们将从基础的设计模式入手,逐步深入到更复杂的应用场景。通过实际案例分析,读者可以更好地理解如何在PHP开发中应用这些设计模式,从而写出更加高效、灵活和易于维护的代码。
本文探讨了PHP中常用的设计模式及其在实际项目中的应用。内容涵盖设计模式的基本概念、分类和具体使用场景,重点介绍了单例模式、工厂模式和观察者模式等常见模式。通过具体的代码示例,展示了如何在PHP项目中有效利用设计模式来提升代码的可维护性和扩展性。文章还讨论了设计模式的选择原则和注意事项,帮助开发者在不同情境下做出最佳决策。
|
9天前
|
存储 Java 数据库连接
php学习笔记-代码基本语法-day01
本文是关于PHP编程语言的基础语法学习笔记,内容包括PHP的介绍、注释风格、数据类型、命名规范、常量和变量的使用,以及变量和常量相关的常用函数。文中详细解释了PHP的基本语法元素和一些易混淆的概念,如传值赋值与传址赋值、可变变量,以及如何检查变量是否已定义或为空。
php学习笔记-代码基本语法-day01
|
9天前
|
JavaScript 前端开发 安全
php学习笔记-普通表单参数提交获取及页面的重定向和一个登录小demo-day05
本文介绍了PHP中普通表单参数的提交获取、页面重定向的方法,并通过一个登录示例演示了表单参数的封装和页面跳转处理。
|
9天前
|
存储 缓存 数据处理
php学习笔记-php会话控制,cookie,session的使用,cookie自动登录和session 图书上传信息添加和修改例子-day07
本文介绍了PHP会话控制及Web常用的预定义变量,包括`$_REQUEST`、`$_SERVER`、`$_COOKIE`和`$_SESSION`的用法和示例。涵盖了cookie的创建、使用、删除以及session的工作原理和使用,并通过图书上传的例子演示了session在实际应用中的使用。
php学习笔记-php会话控制,cookie,session的使用,cookie自动登录和session 图书上传信息添加和修改例子-day07
|
9天前
|
存储 API PHP
php学习笔记-php数组的创建和使用,数组常用函数-day03
关于PHP数组的创建、使用以及常用函数的详细学习笔记。
php学习笔记-php数组的创建和使用,数组常用函数-day03
|
9天前
|
JavaScript Java PHP
php学习笔记-php运算符,类型转换,打印输出语句相较于其他语言的特殊部分-day02
本文是第二天的PHP学习笔记,涵盖了PHP中运算符的使用、类型转换方式,以及打印输出语句的特点,展示了其相较于其他编程语言的一些特殊之处。
php学习笔记-php运算符,类型转换,打印输出语句相较于其他语言的特殊部分-day02
|
5天前
|
程序员 数据库连接 PHP
PHP中的异常处理:提升代码的健壮性
【9月更文挑战第30天】在编程的世界里,错误和异常是不可避免的。本文将深入探索PHP中异常处理的机制,揭示如何通过有效的异常管理来增强代码的健壮性和可靠性。我们将从基础概念出发,逐步深入到高级应用,最后通过实际代码示例来巩固理论知识。无论你是初学者还是有经验的开发者,这篇文章都将为你提供宝贵的指导和启示。
19 6
|
6天前
|
设计模式 算法 PHP
PHP中的设计模式:提升代码的灵活性与可维护性
在本文中,我们将深入探讨PHP编程语言中的一种重要概念——设计模式。设计模式是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。它代表了最佳的实践,被有经验的面向对象的软件开发人员所采用。本文将通过具体的实例,展示如何在PHP项目中应用设计模式,以提高代码的灵活性和可维护性。无论你是PHP初学者还是经验丰富的开发者,都能从中获得有价值的见解。
|
8天前
|
设计模式 算法 PHP
PHP中的设计模式:策略模式的深入探索与实践在软件开发的广袤天地中,PHP以其独特的魅力和强大的功能,成为无数开发者手中的得力工具。而在这条充满挑战与机遇的征途上,设计模式犹如一盏明灯,指引着我们穿越代码的迷雾,编写出更加高效、灵活且易于维护的程序。今天,就让我们聚焦于设计模式中的璀璨明珠——策略模式,深入探讨其在PHP中的实现方法及其实际应用价值。
策略模式,这一设计模式的核心在于它为软件设计带来了一种全新的视角和方法。它允许我们在运行时根据不同情况选择最适合的解决方案,从而极大地提高了程序的灵活性和可扩展性。在PHP这门广泛应用的编程语言中,策略模式同样大放异彩,为开发者们提供了丰富的创作空间。本文将从策略模式的基本概念入手,逐步深入到PHP中的实现细节,并通过一个具体的实例来展示其在实际项目中的应用效果。我们还将探讨策略模式的优势以及在实际应用中可能遇到的挑战和解决方案,为PHP开发者提供一份宝贵的参考。
|
7天前
|
设计模式 存储 数据库连接
探索PHP中的设计模式:提高代码的可维护性与扩展性
本文将深入探讨PHP中常用的设计模式,包括单例模式、工厂模式和观察者模式。通过具体的代码示例,展示如何在实际项目中应用这些设计模式,以提高代码的可维护性与扩展性。无论你是PHP初学者还是有一定经验的开发者,都可以通过本文的学习,提升你的编程技巧和项目架构能力。
下一篇
无影云桌面