Be an Engineer.

社会人からWEBエンジニアになった人間の備忘録的勉強記録

iOS9から導入されたATSとは?そして回避する方法は?

(2016/06/18 追記) 以下の記事によると、2017年1月1日から全てのiOSアプリにATSに対応しないといけなくなるそうです。
その場合はこの記事にあるような回避策は有効じゃない可能性があります。ご承知ください。 japan.cnet.com

(以下本編)

※ この記事は2015年10月3日の情報を元に公開しています。

概要

Xcode6を使ってた時代はWebViewがちゃんと動いていたのですが、最近Xcode7にアップデートして動かしてみると、ページが表示されなくなって焦ったわけで。。
そこで調べてみるとATSという機能によって動かないことがわかったので、その知見を共有しておきます。

ATSとは

ATSとは「App Transport Security」の略で、ざっくり言うと「接続先URLがHTTPSでかつセキュアでないサーバー証明書を使ってないなら接続しねーぞこら。」という機能です。

OSX, iOS アプリケーションが NSURLConnection, CFURL, NSURLSession を利用してサーバに接続する際 現時点で最善に近いセキュアな接続を達成するために導入されたものとのこと。

開発者はATS導入を受けて、サーバー証明書Appleが要求する暗号化スイートに変更するか、ATS機能側の例外設定を適切にする必要があります。
ATSはiOS9.0以降、MacOSX10.11以降で使用可能となります(というか勝手に機能しだします。)

ATSのデフォルト動作

NSURLConnection, CFURL, NSURLSessionを利用してサーバーと接続する場合はATSが動作し、下記要求を満たさない場合は接続できなくなります。(CFNetwork SSLHandshake failed (-9824)なんてエラーが表示される)

These are the App Transport Security requirements:

  • The server must support at least Transport Layer Security (TLS) protocol version 1.2.
  • Connection ciphers are limited to those that provide forward secrecy (see the list of ciphers below.)
  • Certificates must be signed using a SHA256 or greater signature hash algorithm, with either a 2048-bit or greater RSA key or a 256-bit or greater Elliptic-Curve (ECC) key.

Invalid certificates result in a hard failure and no connection.

日本語でおk

  • サーバは TLS 1.2 をサポートしていなければならない
  • 利用できる暗号化スイート(Cipher Suite)は、Forward Secrecy を提供できる(下記リスト)ものに限られる
  • 利用されるサーバ証明書はSHA256以上のハッシュアルゴリズムによって署名されており、2048ビット以上のRSA 鍵、もしくは256ビット以上のECC鍵が使われている必要がある
    検証できない証明書はエラーとなり接続ができない

なお利用できる暗号化スイート(Cipher Suite)のリストは以下の通り。

  • TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
  • TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
  • TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384
  • TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA
  • TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256
  • TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA
  • TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
  • TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
  • TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384
  • TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256
  • TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA

Cipher suiteを調べる

エラーCipher suiteを確認するために接続先のWebサーバーで

$ openssl ciphers -v

と叩いても確認はできるらしいですが、今回はQUALYS SSL LabsのSSL Testを使って確認してみました(使い方はドメイン貼り付けてスタートさせるだけなので割愛)。

実行させてみると以下の様なリストがあるので、AppleのデフォルトのCipher suiteと比較します。

f:id:shirakiya:20151004014455p:plain

結果を見てみると、僕の場合はどうも鍵交換アルゴリズム(kx)がDHEが使われていることが問題のようです(ATSのデフォルト設定ではECDHEのみ許可されている)。

回避方法

もちろん証明書は現時点で最もセキュリティが高い暗号化スイートを利用しているのにこしたことが無いが、どうしてもATSを有効にするのは厳しい状況もあるはず(例えば自社で証明書を管理していないとかの政治的な事情とか)。
公式ドキュメントにも書かれている通り、ATS機能についての例外設定が可能です。

例外設定はinfo.plistに書きます(Xcode使ってるならポチポチで可能)。
設定構造はこんな感じです。

  • NSAppTransportSecurity (Dictionary)
    • NSAllowsArbitraryLoads (Boolean)
    • NSExceptionDomains (Dictionary)
      • <domain-name-for-exception-as-string> (Dictionary)
        • NSExceptionMinimumTLSVersion (String)
        • NSExceptionRequiresForwardSecrecy (Boolc)
        • NSExceptionAllowsInsecureHTTPLoads (Boolean)
        • NSRequiresCertificateTransparency (Boolean)
        • NSIncludesSubdomains (Boolean)
        • NSThirdPartyExceptionMinimumTLSVersion (String)
        • NSThirdPartyExceptionRequiresForwardSecrecy (Boolean)
        • NSThirdPartyExceptionAllowsInsecureHTTPLoads (Boolean)

NSAppTransportSecurity

トップレベルのDictionary。この配下に設定を加えていく

NSAllowsArbitraryLoads

こいつをYESすると、ATS機能が無効となる。つまりデフォルトはNO

NSExceptionDomains

特定のドメインに対する個別の設定を行うDictionary

<domain-name-for-exception-as-string>

特定のドメインを記載するDictionaly。keyに「apple.com」のようにドメインを設定する

NSExceptionMinimumTLSVersion

接続を許可するTLSのミニマムのバージョンを記載する。使用できる文字列は

  • TLSv1.0
  • TLSv1.1
  • TLSv1.2(デフォルトはこれ)

NSExceptionRequiresForwardSecrecy

許可する鍵交換アルゴリズムの設定。デフォルトはYES。
YESの場合は上記のCipher Suiteのリストに限られるが、Noにすると以下のリストも許可するようになる。

NSExceptionAllowsInsecureHTTPLoads

セキュアでないHTTPS接続を許可するかどうか。
NOにすると許可せず、YESにすると許可する。(デフォルトはNO)

  • セキュアでないHTTPS接続
    • no certificate
    • with an error for a self-signed
    • expired
    • hostname mismatch certificate.

NSIncludesSubdomains

指定したトップレベルドメインに対する全てのサブドメインにおいて、設定を適用されるかどうか。
NOにすると設定を適用せず、YESにすると設定を適用させる。(デフォルトはNO)

NSThirdPartyExceptionMinimumTLSVersion

アプリ開発者が変更不可能なサービスに対するTLSのミニマムのバージョンを設定する。
Appleがどうやって変更不可能かどうかを判断しているのかは不明でした...)

NSThirdPartyExceptionRequiresForwardSecrecy

アプリ開発者が変更不可能なサービスに対する、許可する鍵交換アルゴリズムを設定する。
(これもNSThirdPartyExceptionMinimumTLSVersion同様不明...)

NSThirdPartyExceptionAllowsInsecureHTTPLoads

アプリ開発者が変更不可能なサービスに対する、セキュアでないHTTPS接続を許可するかどうかを設定する。
(これもNSThirdPartyExceptionMinimumTLSVersion同様不明...)

今回の場合は鍵交換アルゴリズムが許可されていないものだったので、NSExceptionRequiresForwardSecrecyをNOにすることに。
Xcode上では感じで設定。

f:id:shirakiya:20151003234647p:plain

内部ではこんな感じ。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>NSAppTransportSecurity</key>
    <dict>
        <key>NSAllowsArbitraryLoads</key>
        <false/>
        <key>NSExceptionDomains</key>
        <dict>
            <key>example.com</key>
            <dict>
                <key>NSExceptionMinimumTLSVersion</key>
                <string>TLSv1.2</string>
                <key>NSExceptionRequiresForwardSecrecy</key>
                <false/>
                <key>NSExceptionAllowsInsecureHTTPLoads</key>
                <false/>
                <key>NSIncludesSubdomains</key>
                <false/>
            </dict>
        </dict>
    </dict>
    ...
</dict>
</plist>

そして再ビルドするとページが表示されました!!
ひとまず助かったという感じですが、理想はサーバー証明書Appleが要求する暗号化スイートを使ったものにすることなので油断はしてはいけないですねー。

参考資料

AWS SDK for PHP を使ってみる

環境要件

  • PHP5.3.3 以上のバージョンであること
  • cURLJSONXML・OpenSSL・zlib の拡張モジュールと入れてコンパイルしていること
    • $ php -mでどのモジュールが入っているか確認可能
  • cURLモジュールは 7.16.2 以上のバージョンであること

(参考)Requirements — AWS SDK for PHP 2.8.17 documentation

インストール方法

3種類の方法がある。

  1. Composer を利用してインストールする
  2. Pharをインストールする
  3. Zipファイルをダウンロードして使う

AWS SDK for PHPライブラリのバージョン管理ができて依存関係が簡単に解決できるComposerがAWS的にも推奨されているのでComposerを使った方法を紹介する。

(参考)Installation — AWS SDK for PHP 2.8.17 documentation

ComposerでAWS SDK for PHPを使う

公式ドキュメントと重複する部分もあるが、全体の流れを追うということで紹介。 ※ composerはcpanやBundlerなどと違ってバージョンが5.3.2以上のPHPが入っていれば使える。 (実際は細かい拡張モジュールが必要らしいが、公式ドキュメントにすら詳細が書かれていないので、エラーが表示されたら残念ぐらいの気持ちで良さそう)

1. composer.pharをダウンロードする

$ curl -sS https://getcomposer.org/installer | php

これでcomposer.pharがカレントディレクトリにダウンロードされる。

2. composer.jsonを作成する

composer.jsonは1のコマンドを実行させても作成されるわけではない。 新たに自分で作成し、インストールする AWS SDK for PHP の情報とそのバージョンを書く。

{
    "require": {
        "aws/aws-sdk-php": "2.*"
    }
}

3. インストール開始

$ php composer.phar install

これで AWS SDK for PHP のライブラリ本体がvender/以下にインストールされる。

4. 実際にSDKを利用するスクリプトでロードして利用する

<?php

require(dirname(__FILE__).'/../vendor/autoload.php');
// ↑公式とは書き方を変えている。requireはスクリプトを実行しているディレクトリが基準になるので、安易に相対パスを使うと痛い目に遭うことが多いため。

5. credential情報を取得するためにIAMユーザーを作成する

あんまりこの記事で伝えたいところでもないので、割愛。 参考となる公式記事はこちら

  • ここでアクセスキーとシークレットアクセスキーを取得しておく
  • 作成したユーザーの管理ポリシーは(状況のよりけりなので選定する必要があるが)以下のようなポリシーをアタッチしておく
    • AmazonRoute53DomainsFullAccess
    • AmazonRoute53FullAccess

6. credential情報を使う

SDKクライアントオブジェクトを生成するために、アクセスが許可されているユーザーなのか(credential情報)やどこリージョンにリクエストを送るのか等の情報を付与する必要がある。 その情報の与え方が複数あるようで。

1. ~/.aws/credentialsファイルに情報を記載するパターン

~/.aws/credentials

[project1]  // ①
aws_access_key_id = YOUR_AWS_ACCESS_KEY_ID
aws_secret_access_key = YOUR_AWS_SECRET_ACCESS_KEY

hogehoge.php

<?php

require(dirname(__FILE__).'/../vendor/autoload.php');

use Aws\Route53\Route53Client;

$client = Route53Client::factory(array(
    'profile' => 'project1',  // ①
));

2. 設定ファイルを作成して、コンストラクタに食わせるパターン

config.php

<?php

return array(
    'includes' => array('_aws'),
    'services' => array(
        'default_settings' => array(
            'params' => array(
                'credentials' => array(
                    'key'    => 'YOUR_AWS_ACCESS_KEY_ID',
                    'secret' => 'YOUR_AWS_SECRET_ACCESS_KEY',
                ),
                'region' => 'us-east-1'  // Route 53 の場合。他はこちら参照→http://docs.aws.amazon.com/ja_jp/general/latest/gr/rande.html
            )
        )
    )
);

hogehoge.php

<?php

require(dirname(__FILE__).'/../vendor/autoload.php');

use Aws\Route53\Route53Client;

$client = Route53Client::factory('path/to/config.php');

3. credentialオブジェクトを生成してコンストラクタに食わせるパターン

hogehoge.php

<?php

require(dirname(__FILE__).'/../vendor/autoload.php');

use use Aws\Route53\Route53Client;
use Aws\Common\Credentials\Credentials;

$credentials = new Credentials('YOUR_ACCESS_KEY', 'YOUR_SECRET_KEY');

$client = Route53Client::factory(array(
    'credentials' => $credentials
));

4. 生で書くパターン

hogehoge.php

<?php

require(dirname(__FILE__).'/../vendor/autoload.php');

use use Aws\Route53\Route53Client;

$client = Route53Client::factory(array(
    'credentials' => array(
        'key'    => 'YOUR_AWS_ACCESS_KEY_ID',
        'secret' => 'YOUR_AWS_SECRET_ACCESS_KEY',
    )
));

7. あとはAPIメソッドドキュメント眺めてAPI叩いて楽しむ

ドキュメントを読むと色々できて夢広がる感じの時が、一番楽しいポイントですよねー 色々できそうですが、ホストゾーン内のリソースレコードをCRUD操作するコードを書いたので参考までにベロっと貼っておきます。

(参考)Class Aws\Route53\Route53Client | AWS SDK for PHP

hogehoge.php

<?php

require(dirname(__FILE__).'/../vendor/autoload.php');

use Aws\Route53\Route53Client;

$client    = Route53Client::factory(array(
    'profile' => 'someproject',
));

/* レコード一覧取得 */
try {
    // http://docs.aws.amazon.com/aws-sdk-php/v2/api/class-Aws.Route53.Route53Client.html#_listResourceRecordSets
    $res = $client->listResourceRecordSets(array(
        'HostedZoneId' => 'Host Zone ID をコンソールから調べて記述',
    ));
    foreach ($res['ResourceRecordSets'] as $record) {
        echo $record['Name']."\n";
    }
} catch (Exception $e) {
    // 例外クラスは大量にあるので、エラー処理難しそう。。
    // http://docs.aws.amazon.com/aws-sdk-php/v2/api/namespace-Aws.Route53.Exception.html
    echo $e->getMessage();
}

/* レコード追加 */
try {
    // http://docs.aws.amazon.com/aws-sdk-php/v2/api/class-Aws.Route53.Route53Client.html#_changeResourceRecordSets
    $client->changeResourceRecordSets(array(
        'HostedZoneId' => 'Host Zone ID をコンソールから調べて記述',
        'ChangeBatch'  => array(
            'Comment' => 'from my PHP script',
            'Changes' => array(
                array(
                    'Action' => 'CREATE',  // string: CREATE | DELETE | UPSERT
                    'ResourceRecordSet' => array(
                        'Name' => 'www.hogehoge.com',
                        'Type' => 'A',
                        'TTL'  => 86400,
                        'ResourceRecords' => array(
                            array(
                                'Value' => 'some IP address',
                            ),
                        ),
                    ),
                ),
            ),
        ),
    ));
} catch (Exception $e) {
    echo $e->getMessage();
}

/* レコード変更 */
try {
    $client->changeResourceRecordSets(array(
        'HostedZoneId' => 'Host Zone ID をコンソールから調べて記述',
        'ChangeBatch'  => array(
            'Comment' => 'from my PHP script',
            'Changes' => array(
                array(
                    'Action' => 'UPSERT',  // string: CREATE | DELETE | UPSERT
                    'ResourceRecordSet' => array(
                        'Name' => 'www.shirakiya.com',
                        'Type' => 'A', // UPSERT の場合でも必要
                        'TTL'  => 7200,
                        'ResourceRecords' => array(
                            array(
                                'Value' => 'some IP address',  // UPSERT の場合でも必要
                            ),
                        ),
                    ),
                ),
            ),
        ),
    ));
} catch (Exception $e) {
    echo $e->getMessage();
}

/* レコード削除 */
try {
    $client->changeResourceRecordSets(array(
        'HostedZoneId' => 'Host Zone ID をコンソールから調べて記述',
        'ChangeBatch'  => array(
            'Comment' => 'from my PHP script',
            'Changes' => array(
                array(
                    'Action' => 'DELETE',  // string: CREATE | DELETE | UPSERT
                    'ResourceRecordSet' => array(
                        'Name' => 'www.shirakiya.com',
                        'Type' => 'A', // DELETE の場合でも必要
                        'TTL'  => 7200,
                        'ResourceRecords' => array(
                            array(
                                'Value' => 'some IP address',  // DELETE の場合でも必要
                            ),
                        ),
                    ),
                ),
            ),
        ),
    ));
} catch (Exception $e) {
    echo $e->getMessage();
}

これでいい感じにRoute 53のレコード管理ができると思います。 今回はRoute 53だけ紹介しましたが、もちろん他のAWSサービスでも使えますので!

(参考)Namespace Aws | AWS SDK for PHP

FuelPHPでCoffeeScriptのコンパイルにcoffeescript-phpを使う

はじめに

突然ですが、FuelPHPCoffeeScript使って実装したいと思ったことありませんか?
生のjavascriptよりも簡潔に書けて、javascript特有のバグも起きにくいということからCoffeeScriptで書きたいというケースは少なからずあると思います。
でもCoffeeScriptで実装する際にめんどくさいポイントが2つあると思っていて、

このポイントを解決できるのがcoffeescript-php(https://github.com/alxlit/coffeescript-php)です。
2012年に書かれたものなのですが、これは

という代物です。そこでcoffeescript-phpを使ってFuelPHPCoffeeScriptの自動コンパイル機能を簡単に実装してみます。

coffeescript-phpのインストール

FuelPHPcoffeescript-phpをインストールするにはComposerを使います。FuelPHPには初期からComposerがあるのでそれを利用します。

{
...
    "require": {
        ...
        "coffeescript/coffeescript": "1.3.1"
    },
...
}

と追加してからcomposer.pharがあるディレクトリで

$ php composer.phar update

とすればVENDERPATH配下にインストールされます。(FuelPHPVENDORPATH等についてはこちらを参照

使い方

coffeescript-phpがインストールができるともう使用することができます。
(Composer経由でインストールしたパッケージは、autoload.phpをrequireすれば使用可能になりますし、それがFuelPHPではCOREPATH/bootstrap.phpで記述されているのでほぼ全てのファイルで使用可能になります。)
ここいらがFuelPHPの楽ちんなところですね!

coffeescript-phpCoffeeScriptファイルからjavascriptファイルを生成するには以下のようにします。

<?php
...

// コンパイルを実行したいCoffeeScriptファイルへのフルパス
$coffeescript_path = "/path/to/coffeescript_file";

// ファイルの中身を取得
$coffeescript = File::read($coffeescript_path, true);
 
$javascript = CoffeeScript\Compiler::compile($coffeescript);
// $javascript にはコンパイル実行結果のjavascriptのコードが文字列で格納されている

あとは出力されたjavascriptのコードをお好きなファイル(例えばpublic/asset/js/配下とか)に書き出すとかできるようになります。
これを上手いこと使えば、CoffeeScriptの自動コンパイルの仕組みを作ることができます!

では!

余談

ちなみに私はCoffeeScriptを独自拡張したAsset::coffee()Coffeescriptのソースを読み込ませると、

  • 自動で更新があったのかどうかを判別し、
  • 更新があった場合はpublic/asset/js/配下にjavascriptファイルを設置して、
  • Asset::js()と同じようにHTMLのソースに貼っつける

という仕組みを作れました。(作った拡張Assetクラス↓↓)

ROOtKEY/asset.php at master · shirakiya/ROOtKEY · GitHub

このようにcoffeescript-phpは結構使い方によっちゃあ便利になると思います!

Vagrant+tmux環境下でvimのヤンクデータを共有する

はじめに

vagrant仮想環境で開発を進めているときにめっちゃイラッとすることが2つある。(※自分はMacとiTerm2を使っています。)

  1. 仮想環境下で新しいウィンドウを開くとssh接続が切れてローカルから再スタート
  2. Vimでヤンクしたコードが他のvimウィンドウでペーストできない

1に関してはtmuxを使うことによって回避できているが、2に関しては今までどうすることもできなかった。(調べても記事が無い)

なので、この2に関して力技でなんとかしてみた。

viminfoを使う

viminfoとはコマンド、検索履歴、レジスタなどの情報が格納されているファイルで、通常ホームディレクトリ以下に「.viminfo」の形で存在している。
※わかりやすい解説はこちら

このviminfoにレジスタのデータが書かれており、

# ヤンクした後に
:wv

# 別タブのvimで
:rv!

とすることによって、ヤンクしたデータを複数ウィンドウ間で共有することができる。
これを利用して、ヤンクすると自動的に:wvを実行し、ペーストすると自動的に先に:rv!を実行させることで、わざわざコマンドを叩く事なく、軽快なヤンク+ペーストをさせることができる。

以下を.vimrcに記述する。

"Linuxの場合はviminfoを用いてヤンクデータを共有
let OSTYPE = system('uname')
if OSTYPE == "Linux\n"
    noremap y y:wv<CR>
    noremap p :rv!<CR>p
endif

set viminfo='50,\"3000,:0,n~/.viminfo

基本的にMacのローカル環境は共有できている前提で、今回はOSがLinuxの時のみ有効にさせたが、ローカルでも同じことがしたい場合はif内を書いておけば良い。

またset以下はviminfoの設定で、何行までヤンクしたデータを保存するのかを決めている。
こちらの記事を参照したほうがわかりやすいので詳細については今回は割愛する。

FuelPHPの導入(完全版)

前回のFuelPHPの導入時にcssが権限周りでエラって表示されないという出来事があったが、その不具合を解消できたので、再度「完全版」ということで記事を書く。

PHPの環境構築は過去の記事を参照
Homebrewでnginx + php-fpmのphp実行環境構築

この環境構築の後にPHPのWAF(Web Application Framework)であるFuelPHPの導入をした時のメモ。
FuelPHPの紹介は他に譲るとして、今回はその導入に焦点を当てて書く。
対象はMacOSなのでご注意を。(Linuxについてはある程度共通しているはずだが、試していないので守備範囲外ということで。)

参考記事

FuelPHP 1.7 Documentation

oilコマンドのインストール

FuelPHPのパッケージコマンドであるoilコマンドをまずインストールする。
(oilコマンドの詳細はこちら

$ curl get.fuelphp.com/oil | sh

これでoilコマンドが使用可能な状態となる。

FuelPHPによる新規プロジェクトの作成

プロジェクトを置きたい任意のディレクトリで以下を実行。

$ oil create [プロジェクト名]

しばらく待つと、カレントディレクトリに指定したプロジェクト名のディレクトリが作成される。
その後、nginxに書き込み権限を与えるため、そしてComposerにより定義された依存ライブラリを取得するために以下のコマンドを実行。

$ cd /path/to/[プロジェクト名]
$ oil refine install
$ php composer.phar update

※ちなみにFuelPHPプロジェクトを作成するこの他の方法として、以下の2つがあるが割愛。
1. zipファイルをダウンロードしてくる
2. GitHubからクローンする

nginxの設定

ブラウザから見られるようにするためにnginxの設定を変更する。

※ただし今回は要求として「(FuelPHPとは関係が無い)phpスクリプトを実行するディレクトリ」と「FuelPHPの練習用ディレクトリ」の2つを分けたいと思っていたので、FuelPHP用のlocationを用意した。
※エラーログは現状共存する形になっているが、そこはお好きにどうぞ。

http {
    ...

    include mime.types;
    default_type application/octet-stream;

    server {
        listen 80;

        # 前回の記事
        root /path/to/your_document_root/; # ドキュメントルート
        server_name localhost;
        charset utf-8;
        index index.php index.html index.htm; # /で終わった時にアクセスするファイル

        # 前回の記事
        location ~ \.php$ {
            fastcgi_split_path_info ^(.+\.php)(/.+)$;
            fastcgi_pass 127.0.0.1:9000;
            fastcgi_index index.php;
            fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
            include fastcgi_params;
        }

        # FuelPHP用(「fuelphp_sample」というプロジェクト名の場合)
        location /fuelphp_sample/public/ {
            try_files $uri /fuelphp_sample/public/index.php?$uri&$args;
        }
   
        location /fuelphp_sample/public/\favicon.ico {
            empty_gif;
            access_log off;
            log_not_found off;
        }

        location ~ /fuelphp_sample/public/assets/(.+) {
            autoindex on;
            alias /path/to/your_document_root/fuelphp_sample/public/assets/$1;
        }

        location ~ /fuelphp_sample/public/\.php$ {
            fastcgi_pass 127.0.0.1:9000;
            fastcgi_index index.php;
            fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
            include fastcgi_params;
        }
    }
}

nginxを再起動した後にブラウザでhttp://localhost/fuelphp_sample/public/にアクセス。
前回はここでcssが403でエラーが出ていて、cssが当たっていない画面が表示された。

しかし、同僚に教えてもらったところこんな記事が。。 ほとラボ - Nginxでつくる、どシンプルな静的コンテンツサーバ

…ドキュメントルートまでのとあるディレクトリの"."に確かにOtherに実行権限が無かったあああ!!

そんなわけで実行権限を与える。

$ chmod o+x 対象ディレクトリ

すると治って下のページが表示された!(∩´∀`)∩ワーイ

f:id:shirakiya:20140721174527p:plain

ってことで同じように403でassets系のファイルにアクセスできない場合は、ディレクトリの実行権限に要注意で!

以上の流れをまとめると、

  • PHPの実行環境を作る
  • Nginxでプロキシサーバーを立てる(PHPサーバーでも可)
  • FuelPHPのアプリケーションを構築

FuelPHPの導入完了!

PHPのオブジェクト指向プログラミング(クラスの作り方)

最近独学でPHPの勉強をしています。
PHPの書籍は良書が少ないと言われているのを色々なところで目にしますが、そんな中で良書と呼ばれている「PHP 逆引きレシピ」を片手に(実際は片手で持てないぐらい分厚い本なのですが)、勉強を進めています。

そこで第5章のクラスとオブジェクトの勉強した内容をブログに綴ろうと思います。

クラスとは

  • オブジェクト:データとそのデータを操作するための手続きをひとまとめにしたもの。(オブジェクト = 変数 + 関数)
  • クラス:オブジェクトの定義(雛形)

オブジェクトが持つ変数をプロパティと呼び、関数をメソッドと呼ぶ。 PHPでは以下のように書く。

class クラス名
{
    // プロパティ
    private $プロパティ名;

    // メソッド
    public function メソッド名(引数)
    {
        // 処理を記述
    }
}

”public”等はアクセス修飾子と呼び、プロパティやメソッドへのアクセス権を表す。 アクセス修飾子を省略するとpublicを明示的に指定したのと同じ意味になる。(ただしコードを明瞭にするために省略せずに記述すべし)

アクセス修飾子まとめ

  • public:外部からでもアクセス可能
  • protected:定義するクラス内とそのクラスを継承したクラスからアクセス可能
  • private:定義するクラス内のみからアクセス可能

(クラスを作る例: fileinfo.php

<?php
class FileInfo
{
     private $retCode;

     //改行コードの数を調べるメソッド
     public function readFile($file)
     {
          $this->retCode['CRLF'] = 0;
          $this->retCode['LF']   = 0;

          $fp = fopen($file, 'r');
          // is_resourceは評価する変数がリソース型であるかどうかを返す(true or false)
          // リソースは特別な変数であり、外部リソースへのリファレンスを保持している
          if (! is_resource($fp)) {
               die('ファイルを開けませんでした');
          }

          while (($line = fgets($fp)) !== false) {
               if (preg_match('/\r\n\z/', $line)) {
                    $this->retCode['CRLF']++;
               } else {
                    $this->retCode['LF']++;
               }
          }
          fclose($fp);
     }

     // 改行コードの数を返すメソッド
     public function getRetCode()
     {
          if ($this->retCode['CRLF'] == 0 && $this->retCode['LF'] == 0) {
               return '';
          } elseif ($this->retCode['CRLF'] == 0) {
               return 'LF';
          } elseif ($this->retCode['LF'] == 0) {
               return 'CRLF';
          } else {
               return 'CRLF & LF';
          }
     }
}

クラスを使う

クラスを利用するには、new演算子でオブジェクトを作成(インスタンス化)する必要がある。
クラスから生成されたオブジェクトを「インスタンス」と呼び、クラスからインスタンスを生成することを「インスタンス化」(実体化)と呼ぶ。

(上記の例であれば、class FileInfoをインスタンス化することによって、readFileやgetRetCodeメソッドが使用可能となる)

1つのクラスからインスタンスはいくつでも生成でき、生成されたオブジェクトはそれぞれがプロパティを持つことになる。

(FileInfoクラスを使用する例: sample.php

<?php
require_once '/path/to/fileinfo.php';

$fileinfo = new FileInfo();
$fileinfo->readFile('./sample.php');

echo '改行コードは「'.h($fileinfo->getRetCode()).'」です。';
?>

今日はここまで。

HomebrewでCoffeeScriptの実行環境を作った

基本的にHomebrewでセットアップする。ご多分に漏れずこれもMac向けということで。

参考記事

Mac OS 環境に CoffeeScript をインストールする手順
node.jsとnpmのインストール

node.jsのインストール

node.jsはJavaScriptで出来たWebサーバ。公式HPはこちらから

$ brew install node

#node.jsがインストールされたかの確認
$ node -v

npmのインストール

npmとはnode package managerの略で、nodeを使ったツール(例えば今回のCoffeeScript等)をインストールするときに依存関係のあるデータとかを自動で管理してくれるパッケージマネージャ。

#nodeをインストールするときにnpmも一緒にインストールされている可能性あり。
$ brew install npm

#パスを通すために.bash__profileや.zshenvに以下を記述
export NODE_PATH="/usr/local/lib/node_modules:$PATH"

#npmがインストールされたかの確認
$ npm -v

#npmのヘルプを見る
$ npm help npm

CoffeeScriptのインストール

CoffeScriptはnpm経由でインストールする。

$ npm install -g coffee-script

#CoffeeScriptがインストールされたかの確認
$ coffee -v

※ちなみにnpm installは2つのモードがあり、globalモードとlocalモードが存在する。
* globalモード:/usr/local/lib/node_modules/以下にパッケージが置かれ、共通で使用できる。
* localモード:./node_modules/以下にパッケージが置かれ、カレントディレクトリ以下で使用できる。デフォルトはこちらのモードが適用される。

-gオプションは-globalと同一で、globalモードで実行するために必要なオプションとなっている。

以上でcoffeescriptが使える環境が作れた。めっちゃ簡単!