Showing posts with label phalcon. Show all posts
Showing posts with label phalcon. Show all posts

March 22, 2014

オレオレPhalconで依存注入しつつテストしてみる

前回の記事で作ったAjaxFilter#filter()をテストしてみます。
http://madroom-project.blogspot.jp/2014/03/phalcon_1056.html

ディレクトリ構造とかは、適当に作っているオレオレPhalconを御覧ください。
http://madroom-project.blogspot.jp/2014/03/phalcon_21.html
https://github.com/mamor/phalcon-myapp

以下にPhalcon用の便利ツールっぽいものもあるのですが、今回は使わずにやってみます。
https://github.com/phalcon/incubator

まず app/bootstrap/helper.php を作成して、ヘルパ関数を作ってみました。di()は、Phalcon\DI\FactoryDefaultのインスタンスを返します。
<?php

use Phalcon\DI;

/**
 * @return Phalcon\DI\FactoryDefault
 */
function di()
{
    static $di = null;

    if (is_null($di)) {
        $di = (new DI)->getDefault();
    }

    return $di;
}
このファイルは app/bootstrap/bootstrap.php で読み込みます。
require_once __DIR__.'/helper.php';

次に composer.jsonに "phpunit/phpunit" と "mockery/mockery" と、この後作成する "app/tests/TestCase.php" を追加して composer update します。Mockeryは無くても良いですが、PHPUnitよりも少し簡単にモックが書けます。
 {
+    "require-dev": {
+        "phpunit/phpunit": "4.*",
+        "mockery/mockery": "0.*"
+    },
     "autoload": {
+        "classmap": [
+            "app/tests/TestCase.php"
+        ],
         "psr-0": {
             "Core": "app/classes",
             "App": "app/classes"

app/tests/TestCase.php を作成します。この後に作成するテストクラスの基底クラスになります。setMockToDI()は依存注入用メソッドです。
<?php

/**
 * Class TestCase
 */
abstract class TestCase extends \PHPUnit_Framework_TestCase {

    /**
     * @param  string $name
     * @param  mixed $mock
     * @param  bool $shared
     */
    protected function setMockToDI($name, $mock, $shared = null)
    {
        di()->set($name, function () use ($mock) {
            return $mock;
        }, $shared);
    }
}

プロジェクトルートに phpunit.xml を作成します。 http://docs.phalconphp.com/en/1.2.6/reference/unit-testing.html を基にしました。
<?xml version="1.0" encoding="UTF-8"?>
<phpunit bootstrap="./app/tests/bootstrap.php"
         backupGlobals="false"
         backupStaticAttributes="false"
         verbose="true"
         colors="true"
         convertErrorsToExceptions="true"
         convertNoticesToExceptions="true"
         convertWarningsToExceptions="true"
         processIsolation="false"
         stopOnFailure="false"
         syntaxCheck="true">
    <testsuite name="Phalcon - Testsuite">
        <directory>./app/tests/</directory>
    </testsuite>
</phpunit>

app/tests/bootstrap.php を作成します。composer の autoload.php と、前述のヘルパファイルを読み込んでいます。
<?php

include __DIR__ . '/../../vendor/autoload.php';
require_once __DIR__.'/../bootstrap/helper.php';

app/tests/ の下に、名前空間とかを加味して、テストクラスを作成します。今回は app/tests/Core/Filter/AjaxFilterTest.php としています。内容の細かいことは最後に書きます。
<?php namespace Core\Filter;

use Mockery as m;

/**
 * Class AjaxFilterTest
 */
class AjaxFilterTest extends \TestCase {

    /**
     * test for filter()
     */
    public function testFilter() {
        $m = m::mock();
        $m->shouldReceive('getServer')->once()->with('X_REQUESTED_WITH')->andReturn('xmlhttprequest');
        $this->setMockToDI('request', $m);

        $this->assertTrue((new AjaxFilter)->filter());
    }

    /**
     * test for filter()
     */
    public function testFilterFailed() {
        $m = m::mock();
        $m->shouldReceive('getServer')->once()->with('X_REQUESTED_WITH')->andReturn(null);
        $this->setMockToDI('request', $m);

        $this->assertFalse((new AjaxFilter)->filter());
    }
}

phpunitを実行してみます。
$ phpunit
PHPUnit 4.0.12 by Sebastian Bergmann.

Configuration read from /share/phalcon_project/phpunit.xml

..

Time: 302 ms, Memory: 6.00Mb

OK (2 tests, 2 assertions)

AjaxFilterTestクラスでは、Mockeryのモックオブジェクトを作成しています。このモックオブジェクトを、基底テストクラスのsetMockToDI()メソッドに渡して依存注入して、AjaxFilter#filter()におけるgetServer()をコントロールしている感じです。

もっと良い方法あれば教えて下さいm(_ _)m (Phalcon\DI\Injectableとか上手く使えないかな。。。)


March 21, 2014

Phalconでルーティングをフィルタする

例えば、このコントローラのこのアクションは、Ajaxでしかアクセスさせたくない。という場合に、ルーティングでチェックできます。
http://docs.phalconphp.com/en/1.2.6/reference/routing.html#match-callbacks

まず、フィルタクラスを作成します。尚、di()という関数が書いてありますが、この後に書くユニットテスト関係になります。(書きました。 http://madroom-project.blogspot.jp/2014/03/phalcon_22.html )
<?php namespace Core\Filter;

/**
 * Class AjaxFilter
 */
class AjaxFilter
{
    /**
     * @var \Phalcon\Http\Request
     */
    protected $request;

    /**
     * constructor
     */
    public function __construct()
    {
        $this->request = di()->get('request');
    }

    /**
     * @return bool
     */
    public function filter()
    {
        return $this->request->getServer('X_REQUESTED_WITH') === 'xmlhttprequest';
    }
}
ルーティングで、以下のようにします。AjaxFilter#filter()の戻り値(bool)で、このルーティングにマッチするかしないかが決まります。beforeMatch()はチェインもできるようです。
$router->add('/', 'Index::index')->beforeMatch([new Core\Filter\AjaxFilter, 'filter']);
要認証の基底コントローラ、要Ajaxの基底コントローラ。とかを作ってコンストラクタで制御する手段もありますが、その場合、要認証かつ要Ajaxとかなってくると、ややこしくなるので、こんな感じが良いのかなと思いました。

追記: Phalcon\Mvc\Controllerのコンストラクタはどういうわけか final になっていました。また、beforeMatch()をチェインした時、一方しか働かないような挙動になりました。(まだ細かくは見ていませんが。)

次はこのフィルタクラスをテストしてみます。

オレオレPhalconを作ってみる (とりあえずルーティング周り)

追記: レポジトリ作りました。 https://github.com/mamor/phalcon-myapp

--

Phalconはディレクトリ構造が凄く自由なので、適当に自分好みな感じに作っていこうと思います。

Phalcon DevTools (1.3.0)で、プロジェクトを作ります。
$ phalcon project phalcon_project
  • app/controllers/ControllerBase.php
  • app/controllers/IndexController.php
は削除します。app/controllers/ が空になるので、一応 .gitkeep を入れておきました。

以下の composer.json を作成して "composer install" します。以後、app/classes/に、App\Xxx\Yyyみたいな名前空間でクラスを作成します。コントローラもその中に作ります。
{
    "autoload": {
        "psr-0": {
            "App": "app/classes"
        }
    }
}

public/index.php に、以下を追記します。services.phpの読み込み後にしました。
/**
 * Bootstrap
 */
include __DIR__ . "/../app/bootstrap/bootstrap.php";

app/bootstrap/bootstrap.php を作成します。composer の autoload.php と、自作ルーティング設定ファイルの読み込みを行っています。
<?php

require_once __DIR__.'/../../vendor/autoload.php';
require_once __DIR__.'/routes.php';

app/bootstrap/routes.php を作成します。自動ルーティングをしないようにして、"/"に対するコントローラ::アクションと、not foundなコントローラ::アクションを登録しています。
<?php

/** Phalcon\DI\FactoryDefault $di */

$di->set('router', function () {
    $router = new Phalcon\Mvc\Router(false);
    $router->setDefaults(['namespace' => 'App\Controller', 'controller' => 'error', 'action' => 'route404']);

    $router->add('/', 'Index::index');

    return $router;
});

app/classes/App/Controller ディレクトリを作成して、以下の3つのコントローラを作成します。

AppController.php
<?php namespace App\Controller;

use Phalcon\Mvc\Controller;

class AppController extends Controller
{
}
ErrorController.php
<?php namespace App\Controller;

class ErrorController extends AppController
{
    public function route404Action()
    {
        $this->response->setStatusCode(404, 'Not Found');
    }
}
IndexController.php
<?php namespace App\Controller;

class IndexController extends AppController
{
    public function indexAction()
    {
    }
}

app/views/ ディレクトリに、各ビューファイル(.volt)を作成 or 編集します。

layouts/default.volt
<!DOCTYPE html>
<html>
    <head>
        <title>{% block title %}{% endblock %}</title>
    </head>
    <body>
        {% block content %}{% endblock %}
    </body>
</html>
index/index.volt
{% extends "layouts/default.volt" %}

{% block title %}Index{% endblock %}

{% block content %}
Index
{% endblock %}
error/route404.volt
{% extends "layouts/default.volt" %}

{% block title %}Error{% endblock %}

{% block content %}
Not Found
{% endblock %}

後ほど、GitHubにメモ用なレポつくろうと思います。

March 20, 2014

Phalconでオートコンプリートする

小ネタです。

https://github.com/phalcon/phalcon-devtools/tree/master/ide
で、使っているバージョンのディレクトリを、IDEで外部ライブラリに指定してあげればOKでした。尚、1.3.0は、今日現在"1.3.x"ブランチにあります。

Phalconと、PhalconのデベロッパーツールをUbuntu 13.10にインストールして、プロジェクトを作成してみた

少しハマったりしたので、手順をメモしておきます。

PHPのバージョンは、以下の通りです。
$ php -v
PHP 5.5.9-1+sury.org~saucy+1 (cli) (built: Feb 13 2014 15:58:58)
Copyright (c) 1997-2014 The PHP Group
Zend Engine v2.5.0, Copyright (c) 1998-2014 Zend Technologies
    with Xdebug v2.2.3, Copyright (c) 2002-2013, by Derick Rethans
    with Zend OPcache v7.0.3, Copyright (c) 1999-2014, by Zend Technologies
ドキュメント http://phalconphp.com/ja/download を見た限り、必要なパッケージは揃っていそうだったので、Phalcon本体だけインストールしようとしたら、エラーが出ました。
$ git clone git://github.com/phalcon/cphalcon.git
$ cd cphalcon/
$ git checkout 1.3.0
$ cd build/
$ sudo ./install

# ... 略 ...

/bin/bash /home/vagrant/cphalcon/build/64bits/libtool --mode=compile gcc  -I. -I/home/vagrant/cphalcon/build/64bits -DPHP_ATOM_INC -I/home/vagrant/cphalcon/build/64bits/include -I/home/vagrant/cphalcon/build/64bits/main -I/home/vagrant/cphalcon/build/64bits -I/usr/include/php5 -I/usr/include/php5/main -I/usr/include/php5/TSRM -I/usr/include/php5/Zend -I/usr/include/php5/ext -I/usr/include/php5/ext/date/lib  -DPHALCON_RELEASE -DHAVE_CONFIG_H  -march=native -mtune=native -O2 -finline-functions -fomit-frame-pointer -fvisibility=hidden   -c /home/vagrant/cphalcon/build/64bits/phalcon.c -o phalcon.lo
libtool: compile:  gcc -I. -I/home/vagrant/cphalcon/build/64bits -DPHP_ATOM_INC -I/home/vagrant/cphalcon/build/64bits/include -I/home/vagrant/cphalcon/build/64bits/main -I/home/vagrant/cphalcon/build/64bits -I/usr/include/php5 -I/usr/include/php5/main -I/usr/include/php5/TSRM -I/usr/include/php5/Zend -I/usr/include/php5/ext -I/usr/include/php5/ext/date/lib -DPHALCON_RELEASE -DHAVE_CONFIG_H -march=native -mtune=native -O2 -finline-functions -fomit-frame-pointer -fvisibility=hidden -c /home/vagrant/cphalcon/build/64bits/phalcon.c  -fPIC -DPIC -o .libs/phalcon.o
In file included from /usr/include/php5/ext/spl/spl_iterators.h:27:0,
                 from /home/vagrant/cphalcon/build/64bits/phalcon.c:202:
/usr/include/php5/ext/pcre/php_pcre.h:29:18: fatal error: pcre.h: No such file or directory
 #include "pcre.h"
                  ^
compilation terminated.
make: *** [phalcon.lo] Error 1
README.md https://github.com/phalcon/cphalcon を見ると "libpcre3-dev" が足りないようだったので、インストールしたら解決しました。
$ sudo apt-get install libpcre3-dev
$ sudo ./install

# ... 略 ...

Build complete.
Don't forget to run 'make test'.

Installing shared extensions:     /usr/lib/php5/20121212/

Thanks for compiling Phalcon!
Build succeed: Please restart your web server to complete the installation
インストールが出来たので php.ini に "extension = phalcon.so" を追記して、cliから確認してみると、またエラーが出ました。
$ php -r "print_r(get_loaded_extensions());" | grep phalcon
PHP Warning:  PHP Startup: Unable to load dynamic library '/usr/lib/php5/20121212/phalcon.so' - /usr/lib/php5/20121212/phalcon.so: undefined symbol: php_pdo_get_dbh_ce in Unknown on line 0
どうもエクステンションの読み込み順序のようで、php.iniではなく
  • /etc/php5/cli/conf.d/phalcon.ini
  • /etc/php5/apache2/conf.d/phalcon.ini
を作成して、それぞれに "extension = phalcon.so" を記述してやると、とりあえず動きました。
$ php -r "print_r(get_loaded_extensions());" | grep phalcon
    [55] => phalcon
次に、適当なディレクトリで composer.json を作ってデベロッパーツールをインストールしました。
{
    "require": {
        "phalcon/devtools": "dev-master"
    }
}
$ composer install
パスを通しておきます。
$ sudo ln -s /xxx/yyy/phalcon-devtools/vendor/bin/phalcon.php /usr/local/bin/phalcon
$ phalcon

Phalcon DevTools (1.3.0)

Available commands:
  commands (alias of: list, enumerate)
  controller (alias of: create-controller)
  model (alias of: create-model)
  all-models (alias of: create-all-models)
  project (alias of: create-project)
  scaffold
  migration
  webtools
さっそく、プロジェクトを作ってみます。
$ phalcon project phalcon_project

Phalcon DevTools (1.3.0)


  Success: Controller "index" was successfully created.


  Success: Project 'phalcon_project' was successfully created.
symlinkを貼るなどして、ブラウザからアクセスしてみます。
$ sudo ln -s /zzz/phalcon_project/public /var/www/phalcon
(凄く素っ気ない画面ですがw)とりあえず、これでOKかなー。