HerokuでPHPを使うときに気を付けるところ

HerokuでのPHPサポートが正式版になってたので、現時点でこんな感じで使っているよ、ということ。

# reference

- [Getting Started with PHP on Heroku | Heroku Dev Center](https://devcenter.heroku.com/articles/getting-started-with-php)
- [Customizing web server and runtime settings for PHP | Heroku Dev Center](https://devcenter.heroku.com/articles/custom-php-settings)
- [Heroku PHP Support | Heroku Dev Center](https://devcenter.heroku.com/articles/php-support)
- [PHP session handling on Heroku | Heroku Dev Center](https://devcenter.heroku.com/articles/php-sessions)

# start

- `composer.json`があればPHPアプリケーションとしてdetectされる
- しかし、`package.json`とか`Gemfile`とかあるとPHPのアプリケーションとして認識されない
 - 今どきはこういうファイル、大抵ある
- buildpackを明示的に指定することでPHPアプリケーションと認識させる
 - `heroku config:set BUILDPACK_URL=https://github.com/heroku/heroku-buildpack-php`
- 必要ならタイムゾーン設定
 - `heroku config:add TZ=Asia/Tokyo`

# runtime

- PHPとHHVMが選べる
- HHVMがサポートされてないフレームワークとかあるので注意
 - でも大概動く
- どちらを選ぶかは`composer.json`に記述する

## runtime settings

- document_rootの`index.php`があるところに`.user.ini`を置く
 - [PHP: .user.ini ファイル - Manual](http://docs.php.net/ja/configuration.file.per-user.php)

例

```ini
[PHP]
upload_max_filesize = "10M"

[Date]
date.timezone = "Asia/Tokyo"

[mbstring]
mbstring.language = Japanese
mbstring.internal_encoding = UTF-8
mbstring.encoding_translation = Off
mbstring.func_overload = 0
```

# server

- Apacheとnginxが選べる
 - Apacheはmod_proxy_fcgi経由のFastCGIで動く
 - nginxはphp-fpm経由で動く
- `Procfile`で指定する
 - nginx: `web: vendor/bin/heroku-php-nginx`
 - nginxでdocument_rootを指定: `web: vendor/bin/heroku-php-nginx web`
 - nginxで設定ファイルも指定: `web: vendor/bin/heroku-php-nginx -C nginx_app.conf web`
 - nginxの場合に`client_max_body_size`を指定できないぽいのでアップロードがある場合はApacheがよさそう
 - Apache（2.4）: `web: vendor/bin/heroku-php-apache2`
 - Apacheでdocument_rootを指定: `web: vendor/bin/heroku-php-apache2 web`
 - 設定ファイルを指定することもできるが`.htaccess`を使った方が楽 

## nginx_app.conf

`index.php`から起動する今どきなフレームワークの場合の設定。
ちなみにCSSとか画像をHerokuから配信するのであれば、ここで`Expires`ヘッダーを付加するヘッダーの追加が可能。

```nginx
location / {
    # try to serve file directory, fallback to rewrite
    try_files $uri @rewriteapp;
}

location @rewriteapp {
    # rewrite all to index.php
    rewrite ^(.*)$ /index.php/$1 last;
}

location ~ .*\.(jpg|gif|png|ico|css|js|ttf|eot|svg|woff) {
    expires 5d;
}

location ~ ^/index\.php(/|$) {
    fastcgi_pass heroku-fcgi;
    fastcgi_split_path_info ^(.+\.php)(/.*)$;
    include fastcgi_params;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    fastcgi_param HTTPS off;
}
```

## .htaccess（Apacheを使う場合）

`index.php`から起動する今どきなフレームワークの場合の設定。
もちろんその他の設定もできますが、省略。

```apache
FallbackResource /index.php
LimitRequestBody 10485760
```

# extension

- Heroku側で結構用意されてるのでそれを使う
 - https://devcenter.heroku.com/articles/php-support#extensions
- `composer.json`に使いたいextensionを記述すればOK
- どうしても自分で入れたい場合はbuildpackをいじるしかない
 - 面倒なのでやるべきではない
 - そこまでやるなら素直にAWS使うべき

# add-on

## New Relic

- add-onで有効にした後でdeployすると勝手にext-newrelicが入る
 - 有効にしただけだと当然入らないので注意。deployしよう
- ライセンスキーなどは自分のものに差し替えるのがよさそう
 - アプリ名: `heroku config:set NEW_RELIC_APP_NAME=<アプリ名>`
 - ライセンスキー: `heroku config:set NEW_RELIC_LICENSE_KEY=<自分のキー>`

## MongoLab

- 有効にすると`MONGOLAB_URI`という環境変数に接続先情報がセットされる
- Mongoクラスの接続文字列としてそのまま使える
- セッションストレージで利用する場合など、DB名が必要な場合は最後のスラッシュ以降の文字列を取り出して利用する。

```php
$mongolabUri = getenv('MONGOLAB_URI');
$mongolabDbName = substr($mongolabUri, strrpos($mongolabUri, '/') + 1);
```

## Postgres

- 有効にすると`DATABASE_URL`という環境変数に接続先情報がセットされる
 - 例: `postgres://<ユーザー名>:<パスワード>@<ホスト名>:<ポート番号>/<DB名>`
- `parse_url`関数を使って必要な値に分解するのがよい

```php
$dbUrl = parse_url(getenv('DATABASE_URL'));

$dbName = ltrim($dbUrl['path'], '/');
$dbHost = $dbUrl['host'];
$dbPort = $dbUrl['port'];
$dbUser = $dbUrl['user'];
$dbPass = $dbUrl['pass'];
```

## SendGrid

- 有効にすると`SENDGRID_USERNAME`と`SENDGRID_PASSWORD`という環境変数がセットされる
 - 必要であれば自分でSendGridのサイトから登録、自分のユーザー名とパスワードを同環境変数に設定して使う
 - ユーザー名: `heroku config:set SENDGRID_USERNAME=<自分のユーザー名>`
 - パスワード: `heroku config:set SENDGRID_PASSWORD=<自分のパスワード>`
 - 取得は`getenv('SENDGRID_USERNAME')`、`getenv('SENDGRID_PASSWORD')`で
- 公式のライブラリ、[Sendgrid-php](https://github.com/sendgrid/sendgrid-php)を使うのが便利

# logging

- ログ出力はローカルにするわけにいかない
- 標準出力（`php://stdout`）もしくは標準エラー出力（`php://stderr`）に出力するのがよい
- 必要に応じて[Papertrail](https://addons.heroku.com/papertrail)などのアドオンを組み合わせるのがオススメ

# limitation

- サーバー側に何か保存するのはダメ
 - ファイルアップロードを一度受けるぐらいならOK
- ファイルベースのセッション、使えない
 - memcachedなりMongoDBなりのadd-onをセッションストレージにすべき
 - https://devcenter.heroku.com/articles/php-sessions

# etc.

## switch environment

- 環境変数を利用してローカルの開発環境とHeroku上の環境で設定とか切り替えるのがよさそう
 - `heroku config:set PHP_ENV=heroku`

こんな感じで切り替え

```php
switch (getenv('PHP_ENV')) {
    case 'heroku':
        $app = require __DIR__ . '/bootstrap.heroku.php';
        break;
    default:
        $app = require __DIR__ . '/bootstrap.local.php';
        break;
}
```


## Basic Authentication

- Herokuの場合だけ読まれるファイルにPHPで書くのが早い

```php
if (isset($_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW'])
        && $_SERVER['PHP_AUTH_USER'] === '<ユーザー名>'
        && $_SERVER['PHP_AUTH_PW'] === '<パスワード>') {
} else {
    header('WWW-Authenticate: Basic realm="<表示されるメッセージ>"');
    header('HTTP/1.0 401 Unauthorized');
    exit;
}
```

## Hide versions of PHP and Apache

- [HerokuでPHPとApacheのバージョンを隠す方法](http://qiita.com/hiro_y/items/c583c4c4a8f4950abeef)に書きました