标签归档:annotation

PHP C扩展框架Phalcon

Phalcon是一个C语言写的高性能PHP框架,相比PHP写的框架,它作为PHP的扩展在进程开启时便加载了,节省了每一次请求时的类库加载、MVC框架分派的开销。前面提到的使用Zephir来开发PHP扩展也是这个项目贡献的。
在CentOS 上安装Phalcon,因为之前安装过zephire了,所以直接下载cphalcon来编译安装就可以了,可以参照这里安装所需其他lib

git clone --depth=1 git://github.com/phalcon/cphalcon.git
cd cphalcon/build/
sudo ./install

sudo vim /etc/php.d/phalcon.ini

创建phalcon.ini增加以下内容

[phalcon]
extension=phalcon.so

这里单独把phalcon.ini单独配置,是因为Phalcon扩展加载要在PDO扩展之后,而我的PDO也是单独配置的。如果不是的话会提示这个错误:

$ php -m | grep phalcon
PHP Warning:  PHP Startup: Unable to load dynamic library '/usr/lib64/php/modules/phalcon.so' - /usr/lib64/php/modules/phalcon.so: undefined symbol: php_pdo_get_dbh_ce in Unknown on line 0

Zend Framwork类似,Phalcon也提供开发工具用于快速生成项目骨架

git clone https://github.com/phalcon/phalcon-devtools.git
cd phalcon-devtools
./phalcon.sh
sudo ln -s ~/phalcon-devtools/phalcon.php /usr/bin/phalcon
sudo chmod ugo+x /usr/bin/phalcon

phalcon commands

注意,这个开发工具也是需要依赖Phalcon扩展的,否则会提示

$ phalcon command
PHP Fatal error:  Class 'Phalcon\Script' not found in /home/vagrant/phalcon-devtools/phalcon.php on line 40
PHP Stack trace:
PHP   1. {main}() /home/vagrant/phalcon-devtools/phalcon.php:0

生成一个测试项目store

phalcon scaffold store

按照官方的Nginx说明配置

server {

    listen   8005;
    server_name store.localhost;

    index index.php index.html index.htm;
    set $root_path '/usr/share/nginx/html/tutorial/store/public';
    root $root_path;

    try_files $uri $uri/ @rewrite;

    location @rewrite {
        rewrite ^/(.*)$ /index.php?_url=/$1;
    }

    location ~ \.php {
        fastcgi_pass unix:/tmp/php5-fpm.sock;
        fastcgi_index /index.php;

        include fastcgi.conf;

        fastcgi_split_path_info       ^(.+\.php)(/.+)$;
        fastcgi_param PATH_INFO       $fastcgi_path_info;
        fastcgi_param PATH_TRANSLATED $document_root$fastcgi_path_info;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    }

    location ~* ^/(css|img|js|flv|swf|download)/(.+)$ {
        root $root_path;
    }

    location ~ /\.ht {
        deny all;
    }
}

访问一下192.168.33.14:8005,提示:Access denied。。。查看Nginx错误日志

[root@vagrant nginx]# tail -n 20 error.log
2015/07/28 03:41:42 [error] 7343#0: *1 FastCGI sent in stderr: "Access to the script '/usr/share/nginx/html/tutorial/store/public' has been denied (see security.limit_extensions)" while reading response header from upstream, client: 192.168.33.1, server: store.localhost, request: "GET / HTTP/1.1", upstream: "fastcgi://unix:/tmp/php5-fpm.sock:", host: "192.168.33.14:8005"

一开始还以为是PHP-FPM配置security.limit_extensions引起的,后来发现这个这个参数默认就是注释掉的,没限制。 最后在这里找到答案:原来是之前安装Nginx时配置了:cgi.fix_pathinfo=0,将它改回1(默认值)就可以了。当这个参数为1时,会把 PATH_TRANSLATED 转换为 SCRIPT_FILENAME。当然把这个参数去掉也是可以的

        ;fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;

重启Nginx

sudo service nginx restart

与其他PHP框架一样,Phalcon 支持MVC应用开发,提供了诸多组件如,数据库,缓存,队列等。
Phalcon\DI 作为全局的服务管理,用于服务注册,比如数据库,缓存,文件等。将这些资源统一起来管理,以便其他地方可以调用,各个需要的地方不再需要是new 一个对象,解除耦合关系。
Phalcon\Events\Manager作为全局的事件管理,用于事件注册,触发,以便自定义消息通知,如数据库查询事件,初始化事件。
Phalcon还有一个比较有特色的地方支持注释解析: Phalcon\Annotations 。官方教程举例如何使用注释做输出缓存。首先在Dispatcher服务中注册一个监听dispactch事件的缓存插件

<?php

$di['dispatcher'] = function () {

    $eventsManager = new \Phalcon\Events\Manager();

    // 添加插件到dispatch事件中
    $eventsManager->attach('dispatch', new CacheEnablerPlugin());

    $dispatcher = new \Phalcon\Mvc\Dispatcher();
    $dispatcher->setEventsManager($eventsManager);
    return $dispatcher;
};

然后实现监听的缓存插件

<?php

/**
 * 为视图启动缓存,如果被执行的action带有@Cache 注释单元。
 *
 */
class CacheEnablerPlugin extends \Phalcon\Mvc\User\Plugin
{

    /**
     * 这个事件在dispatcher中的每个路由被执行前执行
     *
     */
    public function beforeExecuteRoute($event, $dispatcher)
    {

        // 解析目前访问的控制的方法的注释
        $annotations = $this->annotations->getMethod(
            $dispatcher->getActiveController(),
            $dispatcher->getActiveMethod()
        );

        // 检查是否方法中带有注释名称‘Cache’的注释单元
        if ($annotations->has('Cache')) {

            // 这个方法带有‘Cache’注释单元
            $annotation = $annotations->get('Cache');

            // 获取注释单元的‘lifetime’参数
            $lifetime = $annotation->getNamedParameter('lifetime');

            $options = array('lifetime' => $lifetime);

            // 检查注释单元中是否有用户定义的‘key’参数
            if ($annotation->hasNamedParameter('key')) {
                $options['key'] = $annotation->getNamedParameter('key');
            }

            // 为当前dispatcher访问的方法开启cache
            $this->view->cache($options);
        }

    }

}

现在就可以使用注释来进行缓存了

<?php

class NewsController extends \Phalcon\Mvc\Controller
{

    public function indexAction()
    {

    }

    /**
     * This is a comment
     *
     * @Cache(lifetime=86400)
     */
    public function showAllAction()
    {
        $this->view->article = Articles::find();
    }

    /**
     * This is a comment
     *
     * @Cache(key="my-key", lifetime=86400)
     */
    public function showAction($slug)
    {
        $this->view->article = Articles::findFirstByTitle($slug);
    }

}

为Action增加了缓存,但对Action的代码改动为0(无侵入)并且非常简洁。数据模型里面也可以使用注释来对字段进行处理

<?php

use Phalcon\Mvc\Model;

class Robots extends Model
{
    /**
     * @Primary
     * @Identity
     * @Column(type="integer", nullable=false)
     */
    public $id;

    /**
     * @Column(type="string", length=70, nullable=false)
     */
    public $name;

    /**
     * @Column(type="string", length=32, nullable=false)
     */
    public $type;

    /**
     * @Column(type="integer", nullable=false)
     */
    public $year;
}

路由解析也可以使用RouterAnnotations反射来实现

?php

/**
 * @RoutePrefix("/api/products")
 */
class ProductsController
{

    /**
     * @Get("/")
     */
    public function indexAction()
    {

    }

    /**
     * @Get("/edit/{id:[0-9]+}", name="edit-robot")
     */
    public function editAction($id)
    {

    }

    /**
     * @Route("/save", methods={"POST", "PUT"}, name="save-robot")
     */
    public function saveAction()
    {

    }

    /**
     * @Route("/delete/{id:[0-9]+}", methods="DELETE",
     *      conversors={id="MyConversors::checkId"})
     */
    public function deleteAction($id)
    {

    }

    public function infoAction($id)
    {

    }

}

是不是跟Java annotation有点像?之前介绍的PHPUnit,Zend Framework,还有Symfony 2 Doctrine 2已经在PHP层面上支持,使用ReflectionClass便可实现。
Phalcon也支持创建命令行应用,可以将PHP应用打包成Phar,直接运行,也可以结合命令行颜色进行输出。

参考链接:
Nginx 安装说明(Nginx Installation Notes)
Can’t upgrade to Phalcon 1.3.0
Nginx/PHP-FPM “Access denied.” error
Access denied (403) for PHP files with Nginx + PHP-FPM
Phalcon 开发工具(Phalcon Developer Tools)
IDE autocomplete for PhalconPHP
Annotations in PHP: They Exist
PHP Annotations Are a Horrible Idea
PHP CLI Colors – PHP Class Command Line Colors (bash)