PHP框架选择






熊迪
2014.1.10

PHP的优点

  1. 开源免费
  2. 动态类型
  3. 无需编译
  4. 写完代码立即看到效果
  5. 丰富的第三方工具
  6. 遇到问题在网络上也容易找到答案
  7. 开发效率
  8. 。。。

PHP的缺点

  1. 代码风格不够严谨
  2. 组织大型项目不如Java
  3. 执行效率不高,并发上不去
  4. 我们都还不熟

为什么选择PHP

开发效率
易用性
上手难度
现成的框架

现有的PHP框架

ThinkPHP
OneThink
Phalcon
Yaf

ThinkPHP


简介

ThinkPHP 是一个免费开源的,快速、简单的面向对象的 轻量级PHP开发框架 ,创立于2006年初,遵循Apache2开源协议发布,是为了敏捷WEB应用开发和简化企业应用开发而诞生的。ThinkPHP从诞生以来一直秉承简洁实用的设计原则,在保持出色的性能和至简的代码的同时,也注重易用性。并且拥有众多的原创功能和特性,在社区团队的积极参与下,在易用性、扩展性和性能方面不断优化和改进,已经成长为国内最领先和最具影响力的WEB应用开发框架,众多的典型案例确保可以稳定用于商业以及门户级的开发。

特性

  • MVC支持-基于多层模型(M)、视图(V)、控制器(C)的设计模式
  • ORM支持-提供了全功能和高性能的ORM支持,支持大部分数据库
  • 模板引擎支持-内置了高性能的基于标签库和XML标签的编译型模板引擎
  • RESTFul支持-REST模式提供了RESTFul支持,为你打造全新的URL设计和访问体验
  • 云引擎支持-提供了对新浪SAE平台和百度BAE平台的强力支持,具备“横跨性”和“平滑性”,支持本地化开发和调试以及部署切换,让你轻松过渡,打造全新的开发体验。
  • CLI支持-支持基于命令行的应用开发
  • AMF支持-支持Flex开发和Flash通讯,打造互联网富应用
  • PHPRPC支持-提供基于PHPRpc的WEBService解决方案
  • MongoDb支持-提供NoSQL的支持
  • 缓存支持-提供了包括文件、数据库、Memcache、Xcache、Redis等多种类型的缓存支持

目录结构


入口文件

 index.php
<?php
//定义项目名称和路径
define('APP_NAME', 'ajax');
define('APP_PATH', './');
// 开启调试模式
define('APP_DEBUG',TRUE);
// 加载框架入口文件
require( "../ThinkPHP/ThinkPHP.php");

配置文件

config.php
<?php
return array(
    'URL_MODEL'                =>        2, // 如果你的环境不支持PATHINFO 请设置为3
    'DB_TYPE'                =>        'mysql',
    'DB_HOST'                =>        'localhost',
    'DB_NAME'                =>        'examples',
    'DB_USER'                =>        'root',
    'DB_PWD'                =>        '',
    'DB_PORT'                =>        '3306',
    'DB_PREFIX'                =>        'think_',
)

定义模型

 FromModel.class.php
<?php
class FormModel extends Model {
    // 自动验证设置
    protected $_validate = array(
        array('title', 'require', '标题必须!', 1),//1为必须验证
        array('title', '', '标题已经存在', 0, 'unique', self::MODEL_INSERT),
        array('content', 'require', '内容必须'),
    );
    // 自动填充设置
    protected $_auto = array(
        array('create_time', 'time', self::MODEL_INSERT, 'function'),
    );
}

定义Action

 IndexAction.class.php
<?php

class IndexAction extends Action {

    // 首页
    public function index() {
        $Form = M("Form");
        // 按照id排序显示前5条记录
        $list = $Form->order('id desc')->limit(3)->select();
        $this->list = $list;
        $this->display();
    }

    // 检查标题是否可用
    public function checkTitle($title='') {
        if (!empty($title)) {
            $Form = M("Form");
            if ($Form->getByTitle($title)) {
                $this->error('标题已经存在');
            } else {
                $this->success('标题可以使用!');
            }
        } else {
            $this->error('标题必须');
        }
    }

    // 处理表单数据
    public function insert() {
        $Form = D("Form");
        if ($vo = $Form->create()) {
            if (false !== $Form->add()) {
                $vo['create_time'] = date('Y-m-d H:i:s', $vo['create_time']);
                $vo['content'] = nl2br($vo['content']);
                $this->ajaxReturn($vo, '表单数据保存成功!', 1);
            } else {
                $this->error('数据写入错误!');
            }
        } else {
            $this->error($Form->getError());
        }
    }
}

视图

 index.html
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
        <title>ThinkPHP示例:Ajax表单提交</title>
    <load href="../Public/Js/Jquery/jquery.min.js" />
    <load href="../Public/Js/Jquery/jquery.form.js" />
        <style type="text/css">
        *{ padding: 0; margin: 0;font-size:16px; font-family: "微软雅黑"}
        div{ padding: 3px 20px;}
        body{ background: #fff; color: #333;}
        h2{font-size:36px}
        input,textarea {border:1px solid silver;padding:5px;width:350px}
        input{height:32px}
        input.button,input.submit{width:68px; margin:2px 5px;letter-spacing:4px;font-size:16px; font-weight:bold;border:1px solid silver; text-align:center; background-color:#F0F0FF;cursor:pointer}
        div.result{border:1px solid #d4d4d4; background:#FFC;color:#393939; padding:8px 20px;float:auto; width:85%;margin:2px}
        </style>
</head>
<body><script language="JavaScript">
    <!--
    $(function(){
        $('#form1').ajaxForm({
            beforeSubmit: checkForm, // pre-submit callback
            success: complete, // post-submit callback
            dataType: 'json'
        });
        function checkForm(){
            if( '' == $.trim($('#title').val())){
                $('#result').html('标题不能为空').show();
                return false;
            }
            //可以在此添加其它判断
        }
        function complete(data){
            if (data.status==1){
                $('#result').html(data.info).show();
                // 更新列表
                data = data.data;
                var html = '<div class="result" style=\'font-weight:normal;background:#A6FF4D\'><div style="border-bottom:1px dotted silver">标题:'+data.title+' [ '+data.create_time+' ]</div><div class="content">内容:'+data.content+'</div></div>';
                $('#list').prepend(html);
            }else{
                $('#result').html(data.info).show();
            }
        }

    });
    function checkTitle(){
        $.post('__URL__/checkTitle',{'title':$('#title').val()},function(data){
            $('#result').html(data.info).show();
        },'json');
    }
    //-->
    </script>
    <div class="main">
        <h2>ThinkPHP示例之:Ajax表单提交</h2>
        <form id="form1" method='post' action="__URL__/insert">
            <table cellpadding=2 cellspacing=2>
                <tr>
                    <td colspan="2"><div id="result" class="result" style="display:none;"></div></td>
                </tr>
                <tr>
                    <td >标题:</td>
                    <td ><input type="text" name="title" id="title"> <input type="button" value="检 查" class="button" onClick="checkTitle()"></td>
                </tr>
                <tr>
                    <td >内容:</td>
                    <td><textarea name="content" id="content" rows="5" cols="25"></textarea></td>
                </tr>
                <tr>
                    <td><input type="hidden" name="ajax" value="1"></td>
                    <td><input type="submit" class="button" value="提 交"> <input type="reset" class="button" value="清 空"></td>
                </tr>
                <tr>
                    <td colspan="2"> <div id="list" >
                            <volist name="list" id="vo">
                                <div class="result" style='font-weight:normal;<eq name="mod" value="1">background:#ECECFF</eq>'><div style="border-bottom:1px dotted silver">标题:{$vo.title} [ {$vo.create_time|date='Y-m-d H:i:s',###} ]</div>
                                    <div class="content">内容:{$vo.content|nl2br}</div>
                                </div>
                            </volist>
                        </div>
                    </td>
                </tr>
            </table>
        </form>
    </div>
</body>
</html>

总结

很精简的MVC模型
项目结构简单清晰
上手容易
国人开发
丰富的中文文档
模板不够强大

Phalcon

phalcon 是一个开源的,全堆栈的,用C语言写成的php5框架,专为高性能设计。你不需要学习和使用C语言的功能,因为这是一个PHP框架,只不过用C写成而已。同时Phalcon是松耦合的,您可以根据需要使用其他组件。

Phalcon不只是性能优越,我们的目标是让它强大而且易于使用!


Simple

 index.php
<?php

$app = new Phalcon\Mvc\Micro();

$app['view'] = function() {
    $view = new \Phalcon\Mvc\View();
    $view->setViewsDir('app/views/');
    return $view;
};

//Return a rendered view
$app->get('/products/show', function() use ($app) {

    // Render app/views/products/show.phtml passing some variables
    echo $app['view']->render('products/show', array(
        'id' => 100,
        'name' => 'Artichoke'
    ));

});

目录结构

single/
    app/
        controllers/
        models/
        views/
    public/
        css/
        img/
        js/

single mvc

 index.php
<?php

use Phalcon\Loader,
    Phalcon\DI\FactoryDefault,
    Phalcon\Mvc\Application,
    Phalcon\Mvc\View;

$loader = new Loader();

$loader->registerDirs(
    array(
        '../apps/controllers/',
        '../apps/models/'
    )
)->register();

$di = new FactoryDefault();

// Registering the view component
$di->set('view', function() {
    $view = new View();
    $view->setViewsDir('../apps/views/');
    return $view;
});

try {

    $application = new Application($di);

    echo $application->handle()->getContent();

} catch (\Exception $e) {
    echo $e->getMessage();
}

特性


模板引擎

{# app/views/products/show.volt #}

{% block last_products %}

{% for product in products %}
    * Name: {{ product.name|e }}
    {% if product.status == "Active" %}
       Price: {{ product.price + product.taxes/100 }}
    {% endif  %}
{% endfor  %}

{% endblock %}

总结

微型MVC设计
高性能
强大的模板引擎
功能非常强大
比thinkPHP缺少的就是中文支持,还有可能没thinkPHP强大的插件社区,也没有OneThink这样现成可用的CMS框架

YAF

Yaf有着和Zend Framework相似的API, 相似的理念, 而同时又保持着对Bingo的兼容, 以此来提高开发效率, 规范开发习惯. 本着对性能的追求, Yaf把框架中不易变的部分抽象出来,采用PHP扩展实现(c语言),以此来保证性能.在作者自己做的简单测试中, Yaf和原生的PHP在同样功能下, 性能损失小于10%, 而和Zend Framework的对比中, Yaf的性能是Zend Framework的50-60倍.

特性

  1. 用C语言开发的PHP框架, 相比原生的PHP, 几乎不会带来额外的性能开销.
  2. 所有的框架类, 不需要编译, 在PHP启动的时候加载, 并常驻内存.
  3. 更短的内存周转周期, 提高内存利用率, 降低内存占用率.
  4. 灵巧的自动加载. 支持全局和局部两种加载规则, 方便类库共享.
  5. 高性能的视图引擎.
  6. 高度灵活可扩展的框架, 支持自定义视图引擎, 支持插件, 支持自定义路由等等.
  7. 内建多种路由, 可以兼容目前常见的各种路由协议.
  8. 强大而又高度灵活的配置文件支持. 并支持缓存配置文件, 避免复杂的配置结构带来的性能损失.
  9. 在框架本身,对危险的操作习惯做了禁止.
  10. 更快的执行速度, 更少的内存占用.

目录结构

+ public
  |- index.php //入口文件
  |- .htaccess //重写规则    
  |+ css
  |+ img
  |+ js
+ conf
  |- application.ini //配置文件   
+ application
  |+ controllers
     |- Index.php //默认控制器
  |+ views    
     |+ index   //控制器
        |- index.phtml //默认视图
  |+ modules //其他模块
  |+ library //本地类库
  |+ models  //model目录
  |+ plugins //插件目录

控制器

<?php
class IndexController extends Yaf_Controller_Abstract {
   public function indexAction() {//默认Action
       $this->getView()->assign("content", "Hello World");
   }
}
?>

视图

<html>
 <head>
   <title>Hello World</title>
 </head>
 <body>
  <?php echo $content;?>
 </body>
</html>

路由配置

<?php
     /**
      * 指定3个变量名
      */
      $route = new Yaf_Route_Simple("m", "c", "a");
      $router->addRoute("name", $route);
     /**
      * 对于如下请求: "http://domain.com/index.php?c=index&a=test
      * 能得到如下路由结果
      */
      array(
        'module'     => '默认模块',
        'controller' => 'index',

'action' => 'test',)

总结

高性能
功能比较简单,复杂的路由还需要自己配置
可能是比较早开发的,所以还是遵循以前的一些理念
Phalcon基本完胜Yaf

最后

如果想快速建站,而且有基本的功能,显然OneThink是首选,丰富的应用插件,相信能为开发节省不少时间,就算不能拿来就用,也能二次开发
如果想长期使用,团队普及,选择Palcon比较好,总体风格类似Node.js的express,ruby的Sintra,scala的Scalatra,这种风格清晰明了,易学易懂,如果配合RestFull设计路由,即使是大项目,也能迅速理解原作者的设计用途

参考文

ThinkPHP官网,http://www.thinkphp.cn/
OneThink官网,http://www.onethink.cn/
ThinkPHP例子,https://github.com/liu21st/examples/tree/master/Exampleshttp://phalconphp.com/zh/
Phalcon官网,http://phalconphp.com/zh/
Phalcon中文文档,http://phalcon.5iunix.net/
Yaf文档,http://www.laruence.com/manual/index.html

HHVM

这是当前版本的HipHop(被称之为HHVM),被用以取代HPHPc以及HPHPi在产品的开发及发布,HHVM借由将 PHP 程式码编译为 二元码 ,使其可运行于虚拟机器的环境,也可以利用客制的JIT在执行时期编译为机器码。当前FacebookPHP程式皆运行在HHVM上。

性能测试

PHP-FPM
Requests per second: 23.17 [#/sec] (mean)
Time per request:    2157.579 [ms] (mean)
Time per request:    43.152 [ms] (mean, across all concurrent requests)
Transfer rate:       275.42 [Kbytes/sec] received

HHVM-FastCGI

Requests per second: 949.21 [#/sec] (mean)
Time per request:    52.676 [ms] (mean)
Time per request:    1.054 [ms] (mean, across all concurrent requests)
Transfer rate:       11276.46 [Kbytes/sec] received
40倍

Debug

root@imd:~# hhvm --help
Usage:

   hhvm [-m <mode>] [<options>] [<arg1>] [<arg2>] ...

Options:
  --help                                display this message
  --version                             display version number
  --compiler-id                         display the git hash for the compiler 
                                        id
  --repo-schema                         display the repo schema id used by this
                                        app
  -m [ --mode ] arg (=run)              run | debug (d) | server (s) | daemon |
                                        replay | translate (t)
  -c [ --config ] arg                   load specified config file
  -v [ --config-value ] arg             individual configuration string in a 
                                        format of name=value, where name can be
                                        any valid configuration for a config 
                                        file
  -p [ --port ] arg (=-1)               start an HTTP server at specified port
  --port-fd arg (=-1)                   use specified fd instead of creating a 
                                        socket
  --ssl-port-fd arg (=-1)               use specified fd for SSL instead of 
                                        creating a socket
  --admin-port arg (=-1)                start admin listener at specified port
  --debug-config arg                    load specified debugger config file
  -h [ --debug-host ] [=arg(=localhost)]
                                        connect to debugger server at specified
                                        address
  --debug-port arg (=-1)                connect to debugger server at specified
                                        port
  --debug-extension arg                 PHP file that extends y command
  --debug-cmd arg                       executes this debugger command and 
                                        returns its output in stdout
  --debug-sandbox arg (=default)        initial sandbox to attach to when 
                                        debugger is started
  -u [ --user ] arg                     run server under this user account
  -f [ --file ] arg                     executing specified file
  -l [ --lint ] arg                     lint specified file
  -w [ --show ] arg                     output specified file and do nothing 
                                        else
  --parse arg                           parse specified file and dump the AST
  --temp-file                           file specified is temporary and removed
                                        after execution
  --count arg (=1)                      how many times to repeat execution
  --no-safe-access-check arg (=0)       whether to ignore safe file access 
                                        check
  --arg arg                             arguments
  --extra-header arg                    extra-header to add to log lines
  --build-id arg                        unique identifier of compiled server 
                                        code
  --instance-id arg                     unique identifier of server instance
  --xhprof-flags arg (=0)               Set XHProf flags


DEbug

root@imd:~# hhvm -m debug
Welcome to HipHop Debugger!
Type "help" or "?" for a complete list of commands.

Note: no server specified, debugging local scripts only.
If you want to connect to a server, launch with "-h" or use:
  [m]achine [c]onnect <servername>

hphpd> 

Debug

 ───────────────────────────────────────────────────────── Session Commands ─────────────────────────────────────────────────────────

    [m]achine                                 connects to an HHVM server
    [t]hread                                  switches between different threads
    [s]et                                     various configuration options for hphpd
    [q]uit                                    quits debugger

    ─────────────────────────────────────────────────────── Program Flow Control ───────────────────────────────────────────────────────

    [b]reak                                   sets/clears/displays breakpoints
    [e]xception                               catches/clears exceptions
    [r]un                                     starts over a program
    <Ctrl-C>                                  breaks program execution
    [c]ontinue *                              continues program execution
    [s]tep     *                              steps into a function call or an expression
    [n]ext     *                              steps over a function call or a line
    [o]ut      *                              steps out a function call

    ───────────────────────────────────────────────────────── Display Commands ─────────────────────────────────────────────────────────

    [p]rint                                   prints a variable's value
    [w]here                                   displays stacktrace
    [u]p                                      goes up by frame(s)
    [d]own                                    goes down by frame(s)
    [f]rame                                   goes to a frame
    [v]ariable                                lists all local variables
    [g]lobal                                  lists all global variables
    [k]onstant                                lists all constants

    ──────────────────────────────────────────────────────── Evaluation Commands ────────────────────────────────────────────────────────

    @                                         evaluates one line of PHP code
    =                                         prints right-hand-side's value, assigns to $_
    ${name}=                                  assigns a value to left-hand-side
    [<?]php                                   starts input of a block of PHP code
    ?>                                        ends and evaluates a block a PHP code
    [a]bort                                   aborts input of a block of PHP code
    [z]end                                    evaluates the last snippet in Zend PHP

    ─────────────────────────────────────────────────── Documentation and Source Code ───────────────────────────────────────────────────

    [i]nfo                                    displays documentations and other information
    [l]ist     *                              displays source codes
    [h]elp    **                              displays this help
    ?                                         displays this help

    ──────────────────────────────────────────────────── Shell and Extended Commands ────────────────────────────────────────────────────

    ! {cmd}                                   executes a shell command
    & {cmd}                                   records and replays macros
    x {cmd}                                   extended commands
    y {cmd}                                   user extended commands


* These commands are replayable by just hitting return.
** Type "help help" to get more help.

DEbug

hphpd> ! ls
! ls
composer.json
composer.lock
composer.phar
node-v0.10.24
node-v0.10.24.tar.gz
vendor
www.pid

debug

hphpd> @echo "hello, imd"
@echo "hello, imd"
hello, imd
hphpd> @echo "hello, imd";
@echo "hello, imd";
hello, imd
hphpd> @echo 3 * 8
@echo 3 * 8
24
hphpd> @echo 3 * 8;
@echo 3 * 8;
24

Config

hhvm -m daemon -c server.hdf
PidFile = /var/run/hhvm/pid

Server {
  Port = 80
  SourceRoot = /var/www/
  DefaultDocument = index.php
}

Log {
  Level = Warning
  AlwaysLogUnhandledExceptions = true
  RuntimeErrorReportingLevel = 8191
  UseLogFile = true
  UseSyslog = false
  File = /var/log/hhvm/error.log
  Access {
    * {
      File = /var/log/hhvm/access.log
      Format = %h %l %u % t \"%r\" %>s %b
    }
  }
}

Repo {
  Central {
    Path = /var/log/hhvm/.hhvm.hhbc
  }
}

#include "/usr/share/hhvm/hdf/static.mime-types.hdf"
StaticFile {
  FilesMatch {
    * {
      pattern = .*\.(dll|exe)
      headers {
        * = Content-Disposition: attachment
      }
    }
  }
  Extensions : StaticMimeTypes
root@imd:/etc/hhvm# cat server.hdf 
PidFile = /var/run/hhvm/pid

Server {
  Port = 80
  SourceRoot = /var/www/
  DefaultDocument = index.php
}

Log {
  Level = Warning
  AlwaysLogUnhandledExceptions = true
  RuntimeErrorReportingLevel = 8191
  UseLogFile = true
  UseSyslog = false
  File = /var/log/hhvm/error.log
  Access {
    * {
      File = /var/log/hhvm/access.log
      Format = %h %l %u % t \"%r\" %>s %b
    }
  }
}

Repo {
  Central {
    Path = /var/log/hhvm/.hhvm.hhbc
  }
}

#include "/usr/share/hhvm/hdf/static.mime-types.hdf"
StaticFile {
  FilesMatch {
    * {
      pattern = .*\.(dll|exe)
      headers { 
        * = Content-Disposition: attachment 
      }
    }
  }
  Extensions : StaticMimeTypes
}

MySQL {
  TypedResults = false
}


总结

性能上HPHPC和HHVM比zend虚拟机+加速器要节约cpu在50%在300%间(官方提供),我实际应用中,一般节约CPU在100%-300%间左右,流量越大越明显。

我们以后的php开发部署,最好遵循一条龙服务。

谢谢

PHP框架选择

By ixiongdi

PHP框架选择

  • 2,795