PHP
Heroku

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

More than 3 years have passed since last update.

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


reference


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

[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ヘッダーを付加するヘッダーの追加が可能。

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から起動する今どきなフレームワークの場合の設定。

もちろんその他の設定もできますが、省略。

FallbackResource /index.php

LimitRequestBody 10485760


extension


  • Heroku側で結構用意されてるのでそれを使う




  • 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名が必要な場合は最後のスラッシュ以降の文字列を取り出して利用する。

$mongolabUri = getenv('MONGOLAB_URI');

$mongolabDbName = substr($mongolabUri, strrpos($mongolabUri, '/') + 1);


Postgres


  • 有効にするとDATABASE_URLという環境変数に接続先情報がセットされる


    • 例: postgres://<ユーザー名>:<パスワード>@<ホスト名>:<ポート番号>/<DB名>




  • parse_url関数を使って必要な値に分解するのがよい

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

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


SendGrid


  • 有効にするとSENDGRID_USERNAMESENDGRID_PASSWORDという環境変数がセットされる


    • 必要であれば自分でSendGridのサイトから登録、自分のユーザー名とパスワードを同環境変数に設定して使う

    • ユーザー名: heroku config:set SENDGRID_USERNAME=<自分のユーザー名>

    • パスワード: heroku config:set SENDGRID_PASSWORD=<自分のパスワード>

    • 取得はgetenv('SENDGRID_USERNAME')getenv('SENDGRID_PASSWORD')



  • 公式のライブラリ、Sendgrid-phpを使うのが便利


logging


  • ログ出力はローカルにするわけにいかない

  • 標準出力(php://stdout)もしくは標準エラー出力(php://stderr)に出力するのがよい

  • 必要に応じてPapertrailなどのアドオンを組み合わせるのがオススメ


limitation


  • サーバー側に何か保存するのはダメ


    • ファイルアップロードを一度受けるぐらいならOK



  • ファイルベースのセッション、使えない




etc.


switch environment


  • 環境変数を利用してローカルの開発環境とHeroku上の環境で設定とか切り替えるのがよさそう


    • heroku config:set PHP_ENV=heroku



こんな感じで切り替え

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で書くのが早い

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