DockerでPhalconを動かしてみる


気になってたPHPフレームワークの一つ。
なかなか手をだせてなかったけど試してみたので備忘録程度に。
といってもチュートリアルに従って動かしただけ...


## Install

### プロジェクト作成

まずはComposerでプロジェクトを作成

```bash
$ composer create-project phalcon/compose --prefer=dist example-phalcon

Installing phalcon/compose (v2.3.0)
  - Installing phalcon/compose (v2.3.0): Downloading (100%)
Created project in example-phalcon
> php -r "file_exists('variables.env') || copy('variables.env.example', 'variables.env');"
Loading composer repositories with package information
Updating dependencies (including require-dev)
Nothing to install or update
Generating autoload files
```

### 設定ファイルの変更

今回はMongoDBでやりたいので`PostgreSQL settings``MySQL settings``phpMyAdmin settings`のセクションを削除します。
次に`Application`セクションの`WEB_ALIAS_DOMAIN`を適当なものに書き換えます。

```text:variable.env
# Application
WEB_DOCUMENT_ROOT=/project/public
WEB_ALIAS_DOMAIN=example-phalcon.local
APPLICATION_CACHE=/project/cache
APPLICATION_LOGS=/project/logs
# production | development | staging | testing
APPLICATION_ENV=development

# Debug
# Note: For debugging purposes you have to setup Xdebug by passing required parameters
PHP_DEBUGGER=xdebug

# Please provide your host (local machine IP) instead of 192.168.1.129
XDEBUG_REMOTE_HOST=192.168.1.129

# php.ini value for xdebug.remote_port
XDEBUG_REMOTE_PORT=9000

# php.ini value for xdebug.remote_autostart
# XDEBUG_REMOTE_AUTOSTART=0

# php.ini value for xdebug.remote_connect_back
XDEBUG_REMOTE_CONNECT_BACK=0
```

### hostsファイルの変更

hostsファイルに以下を追加して先ほど`WEB_ALIAS_DOMAIN`で設定した名前でアクセスできるようにします。

```text:/etc/hosts
127.0.0.1 example-phalcon.local
```

### Dockerのビルドと起動

使うサービスに合わせて`docker-compose.yml`を修正します。
今回はMongoDBなのでPostgreSQLやMySQLなどを削除します。

```yaml:docker-compose.yml
version: '2'

services:
  mongo:
    restart: always
    image: mongo:3.4
    expose:
      - "27017"
    ports:
      - "27017:27017"
    volumes:
      - mongo:/data/db

  memcached:
    restart: always
    image: memcached:1.4-alpine
    ports:
      - "11211:11211"

  elasticsearch:
    image: elasticsearch:5.2-alpine
    restart: always
    expose:
      - "9200"
      - "9300"
    ports:
      - "9200:9200"
      - "9300:9300"
    env_file:
      - variables.env
    volumes:
      - esdata:/usr/share/elasticsearch/data

  app:
    build: docker/app
    restart: always
    working_dir: /project
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./application:/project
      - ./cache:/project/cache
      - ./logs:/project/logs
      - ./conf/php/cli.ini:/etc/php/7.0/cli/conf.d/100-custom.ini
      - ./conf/php/fpm.ini:/etc/php/7.0/fpm/conf.d/100-custom.ini
    depends_on:
      - mongo
      - memcached
      - elasticsearch
    env_file:
      - variables.env

volumes:
  mongo:
    driver: local
  esdata:
    driver: local
```

修正ができたらビルドして起動します。

```bash
$ docker-compose build

Digest: sha256:c03bf98ed6825344db65b5886942342fdf0fc0e4a0217fca5ce90651ca424f3d
Status: Downloaded newer image for phalconphp/php-apache:ubuntu-16.04
 ---> fb133cee008d
Step 2/5 : MAINTAINER Serghei Iakovlev <serghei@phalconphp.com>
 ---> Running in 8093f5d7fea3
 ---> 910ca8064da9
Removing intermediate container 8093f5d7fea3
Step 3/5 : ENV PROVISION_CONTEXT "development"
 ---> Running in a3d7052b1ae6
 ---> c455d5525215
Removing intermediate container a3d7052b1ae6
Step 4/5 : COPY bin/*.sh /opt/docker/provision/entrypoint.d/
 ---> 3125a5399f1e
Removing intermediate container a810ea66b306
Step 5/5 : RUN chmod +x /opt/docker/provision/entrypoint.d/*.sh
 ---> Running in ef6cfecb6894
 ---> 2fc92b457bae
Removing intermediate container ef6cfecb6894
Successfully built 2fc92b457bae
Successfully tagged examplephalcon_app:latest

$ docker-compose up -d

Digest: sha256:90b78c44a58d6d927f96baabea3212d8c756017846715b630044aefcabcab2eb
Status: Downloaded newer image for mongo:3.4
Creating examplephalcon_mongo_1 ...
Creating examplephalcon_memcached_1 ...
Creating examplephalcon_elasticsearch_1 ...
Creating examplephalcon_memcached_1
Creating examplephalcon_elasticsearch_1
Creating examplephalcon_mongo_1 ... done
Creating examplephalcon_app_1 ...
Creating examplephalcon_app_1 ... done
```

### URLにアクセス

`WEB_ALIAS_DOMAIN`で設定したアドレスにアクセスし`phpinfo()`の内容が表示されれば完了

http://example-phalcon.local

`phalcon`セクションにバージョン等が表示されてることが確認できると思います。

## Phalcon

Phalconはフルスタックフレームワークなので機能は一通り揃っているようですが、案件に合わせてSimple、Single、Multiple、Microと使い分けて構成することができます。
https://github.com/phalcon/mvc

Phalcon Developer Toolsを使うことでジェネレーター的なものが使えるようになるみたいですが、今回はSimpleの構成を自作してみました。
https://github.com/phalcon/phalcon-devtools

Dockerで作った環境の`application`フォルダ内に構成をしていきます。
今回、modelまで一気にかこうかと思っていましたが、少し長くなりそうなので、controllerとviewまでをまとめます。

### 最終的なフォルダ構成

先に全体のフォルダ構成を示しておきます。
分割して説明をする上で全体の雰囲気がわかれば...

```
├── app
│   ├── controllers
│   │   └── IndexController.php
│   ├── models
│   └── views
│       ├── index
│       │   ├── hello.volt
│       │   └── index.volt
│       └── layout
│           └── default.volt
├── cache
├── logs
└── public
    ├── .htaccess
    └── index.php
```

### Autoloaders

controllerやmodelを読み込むために`Phalcon\Loader`を追加します。

```php:public/index.php
<?php

use Phalcon\Loader;

// Autoloaders
$loader = new Loader();
$loader->registerDirs([
	'../app/controlers/',
	'../app/models/',
])->register();
```

### Dependency Management

Phalconでは`dependency injection container`というものが提供されています。（DIコンテナ）ライブラリ同士の依存関係などを設定していくようです。

```php:public/index.php
<?php

use Phalcon\Di\FactoryDefault;

// Create a DI
$di = new FactoryDefault();
```

### Views（Volt）

次にViewの設定を行います。

```php:public/index.php
<?php

use Phalcon\Mvc\View;

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

これだけViewの設定も完了ですが、テンプレートエンジンVoltを使用するにはもう少し設定を加える必要があります。

```php:public/index.php
<?php

use Phalcon\Mvc\View;
use Phalcon\Mvc\View\Engine\Volt as PhVolt;

// Setup the view component
$di->set('view', function() {
    $view = new View();
    $view->setViewsDir('../app/views/');
    $view->registerEngines([
        '.volt' => function($view, $di) {
            if (!is_dir(APP_PATH.'/cache/volt')) {
                mkdir(APP_PATH.'/cache/volt');
            }
            $volt = new PhVolt($view, $di);
            $volt->setOptions([
                'compiledPath' => APP_PATH.'/cache/volt/',
                'compiledSeparator' => '_',
                'compiledExtension' => '.php'
            ]);
            return $volt;
        }
    ]);
    return $view;
});
```

まずは`Phalcon\Mvc\View\Engine\Volt`を読み込み、viewにVoltエンジンを追加します。
上では色々と書いてますがVoltを使うだけであれば以下のような書き方もできます。

```php
$view->registerEngine([
	'.volt' => 'Phalcon\Mvc\View\Engine\Volt',
]);
```

ただこれだとviewファイルと同階層にコンパイルファイルが生成されてしまい、なんだかよくわからなくなりますので`setOptions`を使って`cache`フォルダ内に生成されるように設定を追加しています。

### .htaccessでリライト設定

```txt:public/.htaccess
<IfModule mod_rewrite.c>
    RewriteEngine On
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteRule ^(.*)$ index.php?_url=/$1 [QSA,L]
</IfModule>
```

これはほぼお決まりだと思いますが、.htaccessの設定も忘れずに。

これでひとまずPhalconのBootstrap部分は完了です。

### Controllers

CakePHPやCodeIgniter、FuelPHPなどなど...
PHPフレームワークを使ったことがある方は何となくみるとわかるかなと思います。

```php:app/controllers/IndexControllers
<?php

use Phalcon\Mvc\Controller;

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

    public function helloAction($args)
    {
        $this->view->setVar('name', $args);
    }
}
```

### Views

Voltを使っているのでまずレイアウトファイルを作成しておきます。

```php:app/views/layout/default.volt
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>{{ title }} - Example Phalcon</title>
</head>
<body>
    <h1>Example Phalcon</h1>
    {% block contents %}{% endblock %}
</body>
</html>
```

上記の`contents`部分に各アクションの結果が表示されるようにしていきます。

```php:app/views/index/index.volt
{% extends 'layout/default.volt' %}

{% block contents %}
<h2>Hello, World!!</h2>
{% endblock %}
```

```php:app/views/index/hello.volt
{% extends 'layout/default.volt' %}

{% block contents %}
<h2>Hi!, {{ name }}</h2>
{% endblock %}
```

`extends`でレイアウトとなるファイルを指定し、`contents`ブロック内に表示したい内容を記述します。

あとはURLを開いて動くか確認してみます。
http://example-phalcon.local/
http://example-phalcon.local/index/hello/yaaah93

## ここまでの感想

日本語ドキュメントが少ないので、基本は英語ドキュメントやgithubにあるサンプルコードを参考にしながら進めてみました。
ここまではすんなり、特にハマるポイントもなく動かすことができました。ジェネレータを使えばもっと楽になるのかなと思いますが、初めてだし、一つずつ確認しながらできたのでひとまずはいいかなと。
次はモデル関係を確認しながら動かして見ます。