Archive for the 'PHP' Category

APP创业项目-PHP框架选择

本文继《APP创业项目-后端语言选择》之后讨论PHP开发框架选择问题。PHP从诞生至今出现了数以百计的大小框架,有人说PHP生来就是反框架的自由语言,框架的使用反而会降低PHP的性能。确实,引入复杂的架构带来了性能的降低,但我认为从团队合作的角度来说,基于统一的框架开发有助于合作与未来扩展。框架的优与劣关系到程序的性能,也关系到开发的效率,性能与开发效率很多时候在项目开发中是一对矛盾体,正如C与JAVA,一个以性能著称、一个以开发效率而流行。在矛盾中寻找平衡从来没有对于错。

PHP的众多框架中,也存在性能与效率的平衡问题,名声在外的Laravel被称为最优雅的框架,Yaf、Phalcon被誉为最快的框架,还有Yii、Codeigniter、Zend Framework等知名框架。并不是说优雅就不快或者快就不优雅,这只是一个相对的评价。那么我们该如何选择框架?

第一、选择的依据是什么?是性能还是效率?

虽然Laravel官网首页大大的字“Love beautiful code? We do too.”表明它对优雅的情怀,这并不影响Phalcon、Yii、Zend Framework的优雅,在开发效率上也并没有比Phalcon有明显地提升,但是不同的框架在性能上的差距还是比较明显的。参见性能比较,该网页从JSON、单查询、多查询等角度进行了框架性能比较,除了PHP原生之外,Phalcon-micro、Yaf、Phalcon都表现出数倍于Laravel的性能。

第二、我们需要框架做到多少,是一个全包保姆型的,还是一个极简爸爸型的?

全包保姆型框架像保姆一样帮你考虑得细致周到,照顾得你基本上不能独立生活。这类框架尽可能考虑到WEB开发的各个方面,框架对开发模式限制较多,开发者最好遵循其限制,较小的项目会显得高效轻松,随着开发的深入,会渐渐在遵循和突破之间进退两难。

极简爸爸型框架像爸爸一样给你提供大方向的导引,小细节充分给你自由。这类框架只实现了框架该实现Route、MVC、Request、Response、AutoLoader,而ORM、Cache、ACL等重要功能都由开发者自由实现。

Phalcon、Laravel在架构上都解耦得很好,他们既可以当做保姆型也可以当做爸爸型。Phalcon同时还提供了Phalcon-micro简化框架以进一步提升性能。

下面简要对Phalcon、Yaf、Laravel分析。 继续阅读 [ APP创业项目-PHP框架选择 ]

理解依赖注入 for Zend framework 2

依赖注入(Dependency Injection),也称为控制反转(Inversion of Control),一种设计模式,其目的是解除类之间的依赖关系。

假设我们需要举办一个Party,Party需要主持人、厨师、灯光、音响、食品、酒水等等。那么Party对他们存在依赖关系。用程序语言表示如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//Party.php
class Party{
    //主持人
    private $_host;
 
    function __construct(){
        include "./Host.php";
        $this->_host = new Host();
    }
 
    function startParty(){
        $this->_host->sayHello();
    }
}
 
//Host.php
class Host{
    private $_name;
    function sayHello(){
        echo "My name is " . $this->_name;
    }
}
 
//main
$party = new Party();
$party->startParty();

可见Party的运行依赖于Host,没有Host,Party不能单独运行,也不能单独发布为组件。为了解除Party对Host的依赖,我们可以这么做:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
//Party.php
class Party{
    //主持人
    private $_host;
 
    function __construct($host = ""){
        if($host){
            $this->_host = $host;
        }
    }
 
    function startParty(){
        $this->_host->sayHello();
    }
 
    function setHost($host){
        $this->_host = $host;
    }
}
 
//Host.php
class Host{
    private $_name;
    function sayHello(){
        echo "My name is " . $this->_name;
    }
}
 
//main
$host = new Host();
$party = new Party();
$party->setHost($host);//或者 $party = new Party($host)
$party->startParty();

此时Party类对Host类的依赖被移到外面,运行时Host类通过构造函数或者setter注入到Party中。Party本身可以被单独发布。如果Host没有sayHello方法,将其注入到Party中必然导致异常。为了约束Host必须含有sayHello方法,可以使用接口。
继续阅读 [ 理解依赖注入 for Zend framework 2 ]

Zend Framework 2 事件驱动编程的理解

利用程序解决一个问题时,我们通常会确定各个步骤以及步骤的顺序,这就是面向过程编程;而面向对象编程在解决问题的逻辑上仍然是过程化的。为了使步骤之间能够连贯执行,我们需要函数之间的来回调用,使得各函数之间形成依赖。事件驱动编程能够解耦这种依赖关系。

如果完成一件事分为A、B、C三个步骤(函数),A做完了调用B,B做完了调用C,事情结束了。事件驱动编程不需要A、B、C之间的调用,加入一个事件管理器(EventManager,简称EM),A通知EM我做完了(AFinished事件被EM抛出),此时A就不管了,你们谁接着执行跟我没关了。如果B要在A完成后执行,B就可以预先告诉EM,如果A做完了我做(即将B绑定到AFinished事件)。这样A与B之间的关系是由EM控制的,而不需要A、B之间互相调用。
继续阅读 [ Zend Framework 2 事件驱动编程的理解 ]

Zend Framework 2中如何使用Service Manager

Zend Framework 2 使用ServiceManager(简称SM)来实现控制反转(IoC)。有很多资料介绍了service managers的背景,我推荐大家看看this blog post from Evanthis post from Reese Wilson,但是仍然有很多开发者不能够很好地使用ServiceManager去解决他们的需求。这篇文章我将解释为什么ZF2框架需要使用多个服务管理器以及怎样使用它们。主要包含以下几个方面:

  1. 这些不同的服务管理器是什么?
  2. 不同的服务管理器用来干什么?
  3. 服务管理器与服务定位器是什么关系?
  4. 如何使用这些服务管理器定义服务?
  5. 如何在一个服务管理器中通过另一个服务管理器调用服务?

服务管理器使用在ZF2的许多地方,其中最重要的四个地方是:

  1. 应用全局服务管理(根服务管理器或者说是主要服务管理器)
  2. 控制器
  3. 控制器插件
  4. 视图助手

继续阅读 [ Zend Framework 2中如何使用Service Manager ]

基于ZF2的开源项目

Got-CMS
ZF2 + PostgreSQL
RUBEDO
ZF2 + MangoDB,大数据CMS
ejoom
PI Engine

Apache2 mod_deflate 开启网页Gzip压缩

利用mod_deflate 对网页文本文件进行Gzip压缩后再返回数据,能够有效提升数据传输量,提升访问速度,但是进行Gzip压缩需要消耗一定CPU资源。
默认Apache2已经加载mod_deflate.so模块
LoadModule deflate_module modules/mod_deflate.so
如果没有在httpd.conf中添加此句

随后,我们需要进行一些配置

<IfModule mod_deflate.c>
#打开Gzip
    SetOutputFilter DEFLATE
#对text/html text/css text/xhtml等文本文件进行压缩
    AddOutputFilterByType DEFLATE text/*
#对js 等script文件进行压缩
    AddOutputFilterByType DEFLATE application/ms* application/vnd* application/postscript application/javascript application/x-javascript
#对php数据进行压缩
    AddOutputFilterByType DEFLATE application/x-httpd-php application/x-httpd-fastphp
#对gif jpg png不进行压缩
    SetEnvIfNoCase Request_URI .(?:gif|jpe?g|png)$ no-gzip dont-vary
#对 zip exe 等文件不压缩
    SetEnvIfNoCase Request_URI .(?:exe|t?gz|zip|bz2|sit|rar)$ no-gzip dont-vary
#对mov MP3文件不压缩
    SetEnvIfNoCase Request_URI .(?:pdf|mov|avi|mp3|mp4|rm)$ no-gzip dont-vary
#处理浏览器兼容问题
    BrowserMatch ^Mozilla/4 gzip-only-text/html # Netscape 4.x
    BrowserMatch ^Mozilla/4.0[678] no-gzip # Netscape 4.06-4.08
    BrowserMatch bMSIE !no-gzip !gzip-only-text/html
</IfModule>

Apache服务器开启客户端缓存mod_expires设置

通过mod_expires控制客户端浏览器缓存文件的类型和时长,有利于提升服务器请求速度,降低服务器的请求次数。默认apache服务器已经加载了mod_expires模块,在httpd.conf中我们可以看到

LoadModule expires_module modules/mod_expires.so

如果没有,可以便秘mod_expires.c文件获得,并配置加载。

但是apache并没有默认配置该模块,我们需要做配置,通常的配置样例如下:

<IfModule mod_expires.c>
    ExpiresActive on
    ExpiresDefault A600
    ExpiresByType image/x-icon A2592000
    ExpiresByType application/x-javascript A604800
    ExpiresByType text/css A604800
    ExpiresByType image/gif A2592000
    ExpiresByType image/png A2592000
    ExpiresByType image/jpeg A2592000
    ExpiresByType text/plain A86400
    ExpiresByType application/x-shockwave-flash A2592000
    ExpiresByType video/x-flv A2592000
    ExpiresByType application/pdf A2592000
    ExpiresByType text/html A600
</IfModule>

其中的参数具体了解 Apache Module mod_expires

我们通常会写到的配置语句如下:
“access plus 1 month”
“access plus 4 weeks”
“now plus 30 days”
“modification plus 5 hours 3 minutes”
A2592000
M604800
access、now及A 三种写法的意义相同,指过期时间从访问时开始计算。
modification及M 的意义相同,指过期时间是以被访问文件的最后修改时间开始计算。

不用Https如何实现数据传输安全

Web请求的数据都是明文传输的,黑客通过截获数据得到用户的个人数据。为了数据安全一些安全性要求较高的网站采用Https协议,例如支付宝。但是,Https价格是比较昂贵的。我们是否有一些辅助方案来解决一些关键数据的安全问题。下面的架构提出了一个解决思路。

网络安全结构

利用AES对称加密算法加密传输的数据,服务器端利用密钥解密数据。

因为AES是对称加密算法,因此有密钥就可以解密密文。因此密钥不能始终使用同一个,应时刻变化,否则知道密钥轻松解密密文。如果密钥采用随机生成,服务器端就必须得到客户端的密钥,因此借助RSA非对称加密算法用公钥将AES随机密钥传输给服务器端,服务器利用私钥解密得到AES密钥明文,利用AES密钥明文解密数据。

为什么不直接用RSA算法加密数据,服务器用RSA解密?
因为RSA算法对于明文字节长度限制为117字节,解密密文长度128字节,对于web较大数据传输不能胜任。但是对于小数据,例如密码、用户ID等数据是可以的。

这样的思路需要我们具有两方面的工具:JS AES、RSA库和服务器端AES、RSA库。这两方面工具都有智者分享了。

配置在wamp上开发zend framework项目

在wamp php集成开发环境中开发zend framework(ZF)项目需要进行一些配置。主要包括:

PHP include_path配置
修改wamp php 设置中的 php.ini文件,搜索include_path,找到windows下的include_path,在值的引号中添加www/library的绝对路径,注意在路径前面加一个“;”。

站内消息设计方案及数据库设计

站内信数据库设计
站内消息,为了实现站内会员之间离线通信、针对某一部门(组)通知、全局公告功能。消息需要记录已读、未读和已删除状态。能够实现新消息提示。消息有等级之分。站内消息通常分为以下几种

  • 一对一、一对多
  • 一对组
  • 一对全局

最通用的设计方法就是将一对组和一对全局全部理解成一对一,思路清晰、逻辑简单。但是,这样设计的话对数据库的存储量是一个完全的占用,即浪费很多存储空间。设想一个100万用户的论坛,发一个全局通知,那么瞬间要想数据库插入100万条记录,太夸张了。
为了解决这个问题,我们可以先细化为以下几个问题:

  • 是否能够把这种全局消息只写入一条记录?
  • 一条消息怎么记录每个用户的阅读状态?
  • 怎么记录删除状态?

这里参考AlphaThink的设计方法,稍做修改。实现思路为,当发送一条消息给一个对象时,在Message表中插入一条记录,当用户阅读此消息时在MessageLog中插入一条记录,当用户删除此消息时在Message中插入一条记录(或者更新删除状态值)。此方法优势在于,对于全局消息只需插入一条消息记录,当被阅读时再插入Log记录,节省了大量用户不阅读消息的存储空间。

用两个表实现,
第一个表Message,用于存储消息id、发件人、消息内容、消息类型(private,group,global)、接受者id、发送时间。
第二个表MessageLog,用于存储消息的阅读情况,消息id、用户id、阅读状态、删除状态、阅读时间、删除时间。

Message字段:
id 消息id
sender_id 发件人id
message_title 消息标题
message_body 消息正文
type 消息类型(private表示1对1的消息,group表示1对组的消息,global表示全局消息)
message_priority(消息等级)
type_owner_id 消息接受者id(可以是用户id,也可以是组id,global为0)
send_time

MessageLog字段:
id Log id
message_id 消息id
user_id 用户id
read_status 阅读状态(read=1,unread=0)
delete_status 删除(delete=1,not_delete=0)
read_time 阅读时间
delete_time 删除时间
is_star 标记星号

这样设计会引来另外一个问题。当系统有大量一对一的消息时,需要在两个表(Message和MessageLog)中分别插入一条记录,而实际一对一的消息可以把阅读记录(read_status和delete_status)直接放入到Message表中,不需要MessageLog表。因此会带来MessageLog表的冗余。为了解决这个问题,可以把一对一的消息与一对全局的消息分开来设计。即再创建一个表MessageOne2One,专门记录一对一的消息。

另外还可能存在一个问题,一对全局的消息发送一年之后,一个新注册的用户进来了,他是否能够看到这个全局信息?如果看到的话是否有点不合理,因为一年前的消息可能已经没有价值了,如果还提示新消息的话浪费数据库检索资源。因此可以考虑给信息增加起止时间(from和end 时间戳)。

随着网站的运行,站内信越来越多,MessageLog将不断增加,因此应该增加站内信的定期清理功能,比如管理员手动删除站内信、每天删除一年前的站内信等等