default eye-catch image.

vagrant(ubuntu/xenial64 vbox) ubuntu16にrbenv rubyをインストールするplaybook (ほぼ失敗作)

作ってみたがrbenv installにとても時間がかかるので現実的ではないと思う。 vagrantのように壊して作るのが前提ならrbenvでバージョンを切り替えられる必要はないから、 わざわざ毎回コンパイルすることはない気がする。 いずれapt-get installで直接インストールする方法を試してみる。 commonロール 実験の結果、ubuntu/xenial64 vboxに必要だったパッケージたちを入れるロール。 become: yes, become_method: sudo で実行。 --- - name: install packages apt: name=\"{{item}}\" state=present with_items: - gcc - make - libssl-dev - zlib1g-dev rbenv,rubyインストールロール ansible実行ユーザ(ubuntu)のホームディレクトリ以下にrbenvとrubyを入れるロール。 .profileに追加した内容を反映して次に進まないとエラーになる...->要改良 --- - name: retrieve the latest rbenv to ~/.rbenv git: repo=https://github.com/sstephenson/rbenv.git dest=/home/{{user}}/.rbenv accept_hostkey=yes force=yes - name: retrieve the latest ruby-build plugin to ~/.rbenv/plugins/ruby-build git: repo=git://github.com/sstephenson/ruby-build.git dest=/home/{{user}}/.rbenv/plugins/ruby-build accept_hostkey=yes force=yes - name: add path to ~/.rbenv/bin lineinfile: dest=/home/{{user}}/.profile line=\'PATH=\"$HOME/.rbenv/bin:$PATH\"\' state=present create=yes - name: add path to ~/.rbenv/plugins/ruby-build/bin lineinfile: dest=/home/{{user}}/.profile line=\'PATH=\"$HOME/.rbenv/plugins/ruby-build/bin:$PATH\"\' state=present create=yes - name: add initial evaluation of rbenv init lineinfile: dest=/home/{{user}}/.profile line=\'eval \"$(rbenv init -)\"\' state=present create=yes - name: add gem lineinfile: dest=/home/{{user}}/.gemrc line=\"gem{{COLON}} --no-ri --no-rdoc\" state=present create=yes - name: rbenv install 2.4.1 shell: bash -lc \"rbenv install 2.4.1\" - name: rbenv global 2.4.1 shell: bash -lc \"rbenv global 2.4.1\" - name: gem install bundler shell: bash -lc \"gem install bundler\"

default eye-catch image.

FileMakerServer16/17に非公式SSL/TLS証明書をインストールする

はじめに オンプレミスでFileMakerServerを配置するのではなく、AWS,Azure,さくらクラウドなどに配置することが増えているためか、FileMakerServerにSSL/TLS証明書をインストールしたいという要望が増えていそうです。 FileMaker社はFMS15,16用に利用可能なSSL/TLS証明書を公開していますが[1]、どれもアメリカ本国の業者であるだけでなく、いわゆる格安証明書がリストされておらず、たかだかイントラ用のアプリのために高額なお布施を積まないといけない状況にあります。 FileMaker社はFMS17用に利用可能なSSL/TLS証明書に関する記述を改めましたが[2]、いずれにせよ、適合するか否かは実験してみる必要があります。 適合しない証明書をインストールすると、「通信は暗号化されているか証明書を検証できない」というおかしな状況が出来上がります[3]。もともとClosedな傾向が強いFileMakerですが、この状況がどういう理由で発生しているのか確認する術がなく、発生してしまった場合に解決が非常に困難です。 日本の格安SSL/TLS証明書をFMS15,16,17にインストールすることができましたので、 本記事ではその事実を紹介します。 まず結論 以下の証明書をFMS15,16,17にインストールできました。 「鍵マーク」は緑色となります。 さくらインターネットのRapidSSL(DV証明書) 1,620円/年 中間CA証明書必須 クロスルート証明書必須 AWS EC2上 WindowsServer2012 R2 (JPN) または Azure上のWindowsServer2012 R2(JPN) IISでファイル認証 OS、プラットフォーム、Webサーバはオプションです。他でもOKなはずです。 格安証明書で有名なSSLストアはRapidSSLのファイル認証をやめてしまったので、 RapidSSLをファイル認証で取得できるのはここだけかもしれません。 SSLストアのFujiSSL、ComodoSSL(PositiveSSL)はいずれも入りませんでした。 中間CA証明書、クロスルート証明書がないと、証明書自体のインストールは出来ますが、鍵が「オレンジ色」になります。FMSは中間CA証明書、クロスルート証明書を見ています。 なお、FMSには中間証明書を指定するUIが一つしかありませんので、FileMaker社が指定する方法で中間CA証明書、クロスルート証明書を結合する必要があります。 以下の通り結合し1つのファイルとしておきます。 -----BEGIN CERTIFICATE----- Root certificate -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- Intermediate certificate 2 -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- Intermediate certificate 1 -----END CERTIFICATE-----

default eye-catch image.

非マルチサイトでユーザ情報を共有する

概要 ネットワーク機能経由のマルチサイトでなく、サブディレクトリ型マルチサイトでもなく、 単純にサブディレクトリに複数のWordPressインスタンスを並列するケースで、 複数のWordPressインスタンスから一つのユーザ情報を共有する方法について書く。 wp_users,wp_usermetaの共有 このあたりを参照すると書いてある。要は、インスタンスの数だけ作られるwp_users、wp_usermetaを使わず、一つを共有すれば良い。そのためにはwp-config.phpを修正すれば良い。ただしWordPressの互換性的な都合を理解しておく必要がある。 カスタム User テーブルとカスタム Usermeta テーブル CUSTOM_USER_TABLE および CUSTOM_USER_META_TABLE を使用すると、通常 WordPress が利用する user および usermeta テーブルを使用せず、代わりに指定されたテーブル名を使用してユーザー情報を格納します。 define( \'CUSTOM_USER_TABLE\', $table_prefix.\'my_users\' ); define( \'CUSTOM_USER_META_TABLE\', $table_prefix.\'my_usermeta\' ); WordPressは互換性確保のためwp_usersの拡張を推奨しておらず、wp_usermetaに拡張情報を持たせることになっている。拡張情報をwp_usersのカラムとして持たないことで、拡張が必要になったときに初めてwp_usermetaにレコードが作られる。つまり、wp_usermetaはインスタンス毎の拡張情報であるということ。 そのため、CUSTOM_USER_TABLE、CUSTOM_USER_META_TABLEを指定したとしても、共有しようとしたwp_users、wp_usermetaとは別にインスタンス毎にwp_users,wp_usermetaテーブルが作られる。 インスタンスのインストール時にID=1番のレコードが作られる。インスタンスの最初の管理者はこのレコードである。wp_usersのID=1はSuperAdmin的な特別なアカウントで、各インスタンスのwp_users,wp_usermetaが使われる。 共有しようとするwp_usersには、インストールしたインスタンス用のwp_usermetaが無いので「権限なし」となる。そこで、まず、各インスタンスに作られてしまったID=1でログインし、次に共有するwp_usersテーブルのアカウントにインスタンス毎の権限を付与することで解決する。 面倒だけど、確かにアカウントの互換性は保たれるな。

default eye-catch image.

FileMakerServer15の通信を無料でSSL化する方法

FileMakerServer15からデータベースの公開に関してセキュリティが考えられるようになった(本気になった感じ)。Windows VPSにFileMakerServerを配置することでお手軽クラウドが完成するので、割と重要なアップデートだと思う。 Rapid SSLは無理 CSRを作成し、証明書に署名してもらって、秘密鍵と一緒にサーバにインストールする流れはWebサーバと同じだが、RapidSSLなどドメイン認証式(かつファイル認証式)の証明書を作成する場合、FMSは80番で待っている訳ではないから「DocumentRootにファイルを配置してクロールしてもらう」という手続きを進められない。 じゃあ、簡易httpdを立てたらどうか。 RapidSSLのサイトには以下のような記述がある。 RapidSSL、PositiveSSLをお申込みの方へ ホームページが未完成、ホームページに運営者情報が掲載されていない場合、会員制など、ログインが必要なホームページの場合、審査を通過しないケースが増えております。 仮のWebサーバを立てて80番を開けてファイルを配置しても、そもそも怪しい署名リクエストはリジェクトされちゃうらしい!とりあえず、1日経っても来なかった。 一般的なフローではないのだから、仕方ないんじゃないかな。と納得する。そして無料SSLに触手を伸ばす。 Linuxであれば、Certbotを使って90日間の証明書を発行すれば良いが、2017年3月現在、WindowsServerで動作するCertbotが存在せず、Certbotは利用できなさそうである。Windows Serverにvagrant/virtualboxを立てて80番をそこに通すというやり方を思いついたが、それは相当に面倒くさい。 Zero SSL 世の中には便利なサービスがあるものだ。Certbot認証局側をやってくれるサービスが存在する。Zero SSLだ。手順はここのサイトが詳しかった。 サービスのWizardに従ってCSRとコモンネーム(ドメイン)を入力すると、サーバの80の指定URLにファイルを置いてくれ、と言われる。Wizardに従ってNextボタンを押下し続ければ、画面に署名済み証明書と中間証明書が表示される。 FMS15側で1)証明書、2)プライベートキー、3)中間証明書を入れてくれ、と言われるが、2)のプライベートキーはFMS15で生成したものをいれる。ZeroSSLで生成されるプライベートキーは利用しない。1),2),3)を安全な場所に保管しておくこと。 肝心のポート番号だが、80を閉じても繋がるようになった。しかし、443以外のwell knownでないFileMaker専用ポートは依然として使われるようだ。SSLをトンネルとして使うという訳ではなさそうだ。

default eye-catch image.

Laravel Mutator Accessor

たぶん Delphi/C# が起源だと思うんだが、データに紐づく規則や処理をモデルに寄せるパターン(Microsoft:The Repository Pattern)と同等の機能がPHP FrameworkであるLaravelのORM(Eloquent)にあり、流石に超便利だったのでメモっておく。 setter側をMutator、getter側をAccessorという。Mutator, Accessorを簡単にまとめると以下のような感じ。 Eloquentインスタンスに対して値をセットする際に必ずあるメソッド(Mutator)を経由する機能 Eloquentインスタンスから値をゲットする際に必ずあるメソッド(Accessor)を経由する機能 本家のドキュメントは以下のように続く。 Accessors and mutators allow you to format Eloquent attribute values when you retrieve or set them on model instances. For example, you may want to use the Laravel encrypter to encrypt a value while it is stored in the database, and then automatically decrypt the attribute when you access it on an Eloquent model. データに紐づくルールや処理をモデルに寄せることでコントローラの肥大化を防ぐ機能の一つだが、コントローラとモデルの間の取り決めを規約によって定めることで、コントローラからもモデルからも扱いやすくなる。 例えば、usersテーブルにfirst_nameという名前のフィールドがあったとする。 フィールドには小文字で入力し、使うときは必ず先頭1文字を大文字として使う、というルールがある場合Userモデル側を以下のような規約に従って書く。 <?php namespace App; use IlluminateDatabaseEloquentModel; class User extends Model { /* * Get the user\'s first name. * * @param string $value * @return string */ public function getFirstNameAttribute($value) { return ucfirst($value); } /* * Set the user\'s first name. * * @param string $value * @return void */ public function setFirstNameAttribute($value) { $this->attributes[\'first_name\'] = strtolower($value); } } コントローラからfirst_nameフィールドの値を取るコードは以下の通りだが、必ずAccessorを経由し値が返る。フィールドの値が何であれ先頭は必ず大文字になる。ブラウザ等に表示する際に加工する必要がない。 <?php $user = AppUser::find(1); $firstname = $user->first_name; ブラウザ等からデータが入って来たとき、それをusers.first_nameフィールドに格納する処理を書く際、必ずMutatorを経由する。 「小文字にする」という処理を一切意識しないで良い。 <?php $user = AppUser::find(1); $user->first_name = \'Sally\'; コントローラより上は涼しい顔をして重い処理の記述を省略できる。データに紐づくルールが重ければ重いほどモデルに寄せる効果が大きくなる。 例えば、データを暗号化して格納するケースにおいては、暗号化/復号処理をModelに隠蔽しControllerより上からは透過的に平文として扱うなど。 Eloquentは流石に雄弁だ。

default eye-catch image.

詐欺サイトを発見したので独自調査してみる

ある商品を探していたのだが、迂闊にも詐欺サイトのカートをポチってしまった。 届いた確認メールが突っ込みどころ満載だったのでそのサイトの模倣元に確認して事なきを得たが、住所と電話番号と氏名を抜かれてしまった。 あれ、自分はITで食ってるんじゃなかったか?詐欺サイトにハマるなんて...。 と疑心暗鬼になった。せっかくなのでブログのネタにしてみる。 実は技術的にこのサイトの秘密を暴いてみようと思ったのだが、 それ自体が不正アクセス禁止法に触れるため載せられないことが判明した。 詐欺サイト自体のセキュリティは緩かった。 サイトの特徴 アパレルのECサイトです。模倣元の子サービスという設定。 リテラシが比較的高くない女性向けだと思います。 市場価格から適度に格安。明らかにパチもんという設定ではない、 楽天との関係を装っています。 かなり精巧に模倣されているが、致命的な抜けがあります。例えば... ドメインが怪しい 特定商取引法の掲示がない 模倣サイトの子サービスの扱いなのに模倣サイトの電話番号がない スマホからだとポチるかも... 誤解を恐れずに言うならば詰めが甘い。もっとできるはず。 なお、情報の少ないスマホ経由だとかなりキワドい。 実質的にはスマホから流入して軽くポチッた人が被害にあっていそうだ。 確認メール おそらくチャイナだろう。日本語がメチャクチャ。詰めが甘いんだよな。 大多数の日本人はここで止まると思う。実質的には個人情報を抜く目的なのかもしれない。 ゆうちょ銀行の口座番号に振り込めと来た。 偽サイトを見つけたら 金銭的な被害がないので警察も動かないでしょう。 100円とか振り込んで相手方の様子を見て遊ぶのも面白いかもしれません。 七面倒臭い展開が待っていると思うのでやりませんがw 出来ることなんて以下ぐらい。 模倣元に偽サイトか否かの確認と事実の報告 振込先金融機関に口座番号が詐欺サイトに使われていることの報告 どういう仕組みで実行犯は金融機関の口座を取得し利用できているのだろうか。 恐らく善良な市民の口座がハックされ犯罪に使われているのだろうが、 そのあたりは知らない方が身のためだと思うので詮索は終わります。 もし実被害が出た場合、警察は指定口座から実行犯を割り出すことができるのだろうか。 公権力はどこまで力を持っているのか興味はあります。興味だけです。 実はもっと技術的に解析を試みたのだが、いくら相手が犯罪者とはいえ、 不正アクセス防止法を犯す行為をネットに晒すことは社会人として許されることはないから書かない。 セキュリティ的に無防備なサーバが存在し、どういう訳がそこで今回のような模倣サイトが運営されていた。 やり得な世界が広がっている事実に寒気がした。

default eye-catch image.

PythonでAES EncryptしてPHPでAES Decryptできなかった話

標題の通りなのですが。AESなんて枯れてるんだから鍵長と暗号モードを合わせれば出来るだろうと、軽く見積もってました。結果、無茶苦茶頑張ってみましたが出来ませんでした! 結論から先に書くと、以下の方法で逃げました。 PythonでEncrypt/Decrypt Classを書く Python側からは上記のClassをそのまま使う 上記のClassを叩くPythonスクリプトを書き、PHPからシェル経由でそのスクリプトを実行する。 もっと頑張れば道が見つかったかもしれませんが、本当にPython/PHPをクロスして暗号化/復号の対ができるのか心配になって、これに落ち着きました。 出来ない理由.. 正直出来ない理由もハッキリしません。 だいたいどちらでも以下ぐらいは設定できるものなのですが、以下を合わせたぐらいでは片方で暗号化したものを復号することができません。 鍵長=256bit Cypher Algorythm=Rijndael 暗号モード=ECB orCBC まともな暗号処理をするには暗号モードにECBは使えずInitialVector(IV)を与える必要があるのですが、乱数からIVを生成する箇所の共通性が怪しかったり、Python側では共通鍵の鍵長にAPIで制限がかかるものの、PHPでは任意に指定できたり、本当に実装すると突っ込みどころ満載になります。 Pythonのコードはだいたい以下のような感じになります(IVを共有するためにダイジェストにIVを含んでいるのは本質的でないので除いてみてください)。 PythonのCrypto.Cipherは Encryptした結果を自力でPaddingしてBase64エンコードしてますが、PHPのmdecryptはいきなりPrintableな文字列を要求します。普通に考えて、PHPのmdecryptがBase64デコードして同じようにPaddingを取り除くのを期待するのはナンセンスでしょ。 import base64 from Crypto import Random from Crypto.Cipher import AES class AESCipher(object): def __init__(self, key, block_size=32): self.bs = block_size if len(key) >= len(str(block_size)): self.key = key[:block_size] else: self.key = self._pad(key) def encrypt(self, raw): raw = self._pad(raw) iv = Random.new().read(AES.block_size) cipher = AES.new(self.key, AES.MODE_CBC, iv) return base64.b64encode(iv + cipher.encrypt(raw)) def decrypt(self, enc): enc = base64.b64decode(enc) iv = enc[:AES.block_size] cipher = AES.new(self.key, AES.MODE_CBC, iv) return self._unpad(cipher.decrypt(enc[AES.block_size:])) def _pad(self, s): return s + (self.bs - len(s) % self.bs) * chr(self.bs - len(s) % self.bs) def _unpad(self, s): return s[:-ord(s[len(s)-1:])] 気付いたこと 何か勘違いしているかもしれませんが、早々に別の道に進みました。

default eye-catch image.

実践 PSR-4 クラスをspl_autoloaderを使ってautoloadしてみる最も簡単なサンプル

PSR-4の一次情報を読んだことだし、現在の理解でPSR-4 Autoloadに対応したクラスを作成し実際にAutoloadしてみる。composerもPSR-4に対応しているが、まずはPHP-Figがサンプルとして公開しているspl_autoloaderを使った例を試すことにする。 仕様は以下の通り。 Fully qualified class name ikutyexamplePsr4HelloWorld Namespace Prefix ikutyexamplePsr4 Base Directory ./src/ikuty/example/Psr4 Resulting file path ./src/ikuty/example/Psr4/helloworld.php サンプルクラスの作成 ./src/ikuty/example/Psr4/helloworld.phpを以下の通り作成する。 $ pwd ./src/ikuty/example/Psr4 vi helloworld.php <?php namespace ikutyexamplePsr4; class HelloWorld { public function __construct() { echo \"Hello World!\"; } public function say() { echo \"Hello World!\"; } } 呼出側の作成(PSR-0的) PHP Figにあるクロージャ版spl_autoloaderを使った実装例を試してみる。 ikutyexamplePsr4HelloWorldをnew()すると、spl_autoload_registerで登録したクロージャが評価される。 なお、名前空間の深さとBaseDirectoryの深さ等しくしている意味でPSR-0的なサンプル。 [sample.php] <?php 2 spl_autoload_register(function ($class){ 3 4 //project-specific namespace prefix 5 $prefix = \'ikuty\\example\\Psr4\\\'; 6 7 //base directory for the namespace prefix 8 $base_dir = __DIR__ . \'/src/ikuty/example/Psr4/\'; 9 10 //does the class use the namespace prefix? 11 $len = strlen($prefix); 12 if (strncmp($prefix, $class, $len) !== 0){ 13 // no, move to the next registerd autoloader 14 return; 15 } 16 17 //get the relative class name 18 $relative_class = substr($class, $len); 19 20 //replace the namespace prefix with the base directory, replace namespace 21 //separators with directory separators in the relative class name, append 22 //with .php 23 $file = $base_dir . str_replace(\'\\\',\'/\', $relative_class).\'.php\'; 24 25 //if the file exists, require it 26 if (file_exists($file)) { 27 require $file; 28 } 29 30 }); 31 32 $example = new ikutyexamplePsr4HelloWorld(); 33 $example->say(); 名前空間プレフィックスとBaseDirectoryの紐づけは結局人力でやっている。「名前空間プレフィックス」と「クラス名」の間にある「サブ名前空間」を読み飛ばしている。名前空間プレフィックスに続く「サブ名前空間」分をクラス側が自由に使えるところが興味深い。 実行結果 実行してみる。実行結果から分かりにくくて恐縮だが、new()してもHello World!は出力されず、say()メソッドを実行したときにHello World!が2回表示された。late-binding的な感じ。 $ php sample.php Hello World!Hello World! 名前空間の深さと関係なくBaseDirectoryを設定する(PSR-4的) 例えば、ikutyexamplePsr4HelloWorldの名前空間プレフィックスikutyexamplePsr4をBaseDirectory ./ikuty-example-psr4 に紐づければ、名前空間がどれだけ深くてもデプロイは影響を受けない。 Fully qualified class name ikutyexamplePsr4HelloWorld Namespace Prefix ikutyexamplePsr4 Base Directory ./src/ikuty-example-Psr4 Resulting file path ./src/ikuty-example-Psr4/helloworld.php <?php 2 spl_autoload_register(function ($class){ 3 4 //project-specific namespace prefix 5 $prefix = \'ikuty\\example\\Psr4\\\'; 6 7 //base directory for the namespace prefix 8 //$base_dir = __DIR__ . \'/src/ikuty/example/Psr4/\'; 9 $base_dir = __DIR__ . \'/src-ikuty-example-psr4/\'; 10 11 12 //does the class use the namespace prefix? 13 $len = strlen($prefix); 14 if (strncmp($prefix, $class, $len) !== 0){ 15 // no, move to the next registerd autoloader 16 return; 17 } 18 19 //get the relative class name 20 $relative_class = substr($class, $len); 21 22 //replace the namespace prefix with the base directory, replace namespace 23 //separators with directory separators in the relative class name, append 24 //with .php 25 $file = $base_dir . str_replace(\'\\\',\'/\', $relative_class).\'.php\'; 26 27 //if the file exists, require it 28 if (file_exists($file)) { 29 require $file; 30 } 31 32 }); 33 34 $example = new ikutyexamplePsr4HelloWorld(); 35 $example->say(); 結論 PHP-Figがサンプルとして提供しているspl_autoloader(クロージャ版)をお試し実装してみた。 PSR-4は名前空間プレフィックスのベースディレクトリの関連を記述する仕様であり、ベースディレクトリの物理的な配置が名前空間プレフィックスに直接影響を受けない。 ベースディレクトリが深くならなくて良い

default eye-catch image.

Laravel5 Form Request Validationによるコントローラの軽量化

フォームのビジネスロジック(検証や保存)をコントローラに書くと、どうでも良いコードでコントローラが重くなってくる。フォームが複数ある場合、だいたい似たり寄ったりのコードが量産されていく。本エントリでは、Laravel5 の Form Request Validation によってコントローラからバリデーションロジックを完全分離する方法について記述する。 Form Request Validation - バリデーションの分離 流れは以下の通り。 AppHttpRequestsRequestクラスを派生し、ContactRequestクラスを作成する。 コントローラメソッドにて、AppHttpRequestsRequestクラスのインスタンスではなく ContractRequestクラスのインスタンスを受けるようにする。 コントローラメソッドに入ったときには既にContactRequestクラスによるバリデーションが完了していて、データが正しい前提で処理できる。 ContactRequestクラス、ContactRequestTraitクラスの例を以下に示す。 <?php namespace AppHttpRequests; class ContactRequest extends Request { use ConfirmRequestTrait; /** * Determine if the user is authorized to make this request. * * @return bool */ public function authorize() { return true; } /** * Get the validation rules that apply to the request. * * @return array */ public function rules() { return [ \'name\' => \'required\', \'email\' => \'required|email\', \'subject\' => \'required\', \'content\' => \'required\', ]; } /** * Set custom messages for validator errors. * * @return array */ public function messages() { return [ // ]; } /** * Set custom attributes for validator errors. * * @return array */ public function attributes() { return [ \'name\' => \'お名前\', \'email\' => \'メールアドレス\', \'subject\' => \'件名\', \'content\' => \'内容\', ]; } } <?php namespace AppHttpRequests; use IlluminateContractsValidationValidator; trait ConfirmRequestTrait { /** * Set custom messages for validator errors. * * @param IlluminateContractsValidationFactory $factory * * @return IlluminateContractsValidationValidator */ public function validator($factory) { // 値検証前の処理 if (method_exists($this, \'beforeValidate\')) { $this->beforeValidate(); } $validator = $factory->make( $this->all(), $rules, $this->messages(), $this->attributes() ); $validator->after(function ($validator) { $failed = $validator->failed(); // 値検証後の処理 if (method_exists($this, \'afterValidate\')) { $this->afterValidate($validator); } }); return $validator; } /** * Format the errors from the given Validator instance. * * @param IlluminateContractsValidationValidator $validator * * @return array */ protected function formatErrors(Validator $validator) { $errors = parent::formatErrors($validator); return $errors; } } Form Request Validation - コントローラ側 ポイントは、storeメソッドの引数に ContactRequestヒントが渡されていること。sotreメソッドに入る前にContactRequestによりバリデーションが実行され、バリデーションに成功した時に限り、sotre()メソッドに入る。バリデーションが失敗すると、その前のアドレスにリダイレクトされる。コントローラから一切のバリデーションロジックを排除できた。 <?php namespace App¥Http¥Controllers; use App¥Http¥Requests¥ContactRequest; use App¥Repositories¥ContactRepository; class ContactController extends Controller { /** /* @var ContactRepository */ protected $repository; /** /* @constructor /* param ContactRepository $repository */ public function __construct(ContactRepository $repository) { $this->repository = $repository; } /* * */ public function index() { ... } /** * @var ContactRequest $request * */ public function store(ContactRequest $request) { } }

default eye-catch image.

PSR-4 の理解

What\'s PSR-4 ? そもそもPSRって何の略だろうと調べたがそこから出てこなかった。 ↓なんじゃないか、という意見あり。 PHP Standard Recommendation PHP Specification Request カテゴリ毎に数字が割り当てられており他にも以下が存在する。 PSR-0 Autoloading PSR-1 Basic Coding Standard PSR-2 Coding Style Guide PSR-3 Logger Interface PSR-4 Improved Autoloading PSR-6 Chaching Interface PSR-7 HTTP Message Interface PHP-FIGという団体で取りまとめられている。http://www.php-fig.org/ PSR-4 それでは早速一次情報から漁ってみる。どのパスにどのクラスを配置するのか仕様を決めたいというのが根っこにある。決まりがあればクラスを使う側が楽だから。楽に使ってもらえるよう配布手段に規約を設ける。PSR-0(等?)と互換性がある。(PSR-0以外にもこういう決まりがあるのか?) This PSR describes a specification for autoloading classes from file paths. It is fully interoperable, and can be used in addition to any other autoloading specification, including PSR-0. This PSR also describes where to place files that will be autoloaded according to the specification. 仕様 言葉遊び ドキュメント内では\"class\"は class,interfaces,traits,それに似た構造を指すそうだ。あくまでも外部から使用する対象と配置に関する仕様だよ、ということだ。対象は何でも良い。 The term \"class\" refers to classes, interfaces, traits, and other similar structures. 完全修飾クラス名 完全修飾クラス名は次のような形式となるそうだ。 A fully qualified class name has the following form: \<NamespaceName>(<SubNamespaceNames>)*<ClassName> いくつか決まり事がある。 The fully qualified class name MUST have a top-level namespace name, also known as a \"vendor namespace\". 完全修飾クラス名にはベンダーを表すユニークな名前空間が入っていないといけない。 NamespaceNameが一意なのか、NamespaceNameとSubNamespaceNamesの合わせ技で一意なのか不明。 GitHubに上がってるのを見るとNamespaceNameだけで表現しているようだ。「トップレベル」って「一番最初=NamespaceName」のことか?? The fully qualified class name MAY have one or more sub-namespace names. 補助的に名前空間を複数追加しても良い。 階層構造を持った複雑な名前空間も定義できるということ。 The fully qualified class name MUST have a terminating class name. 完全修飾クラス名の最後はクラス名でなければならない。 Underscores have no special meaning in any portion of the fully qualified class name. アンスコは特別な意味を持たない。 アンスコを前後の識別子の接続子のように使うフレームワークが多いからね。 アンスコは単なる文字。 Alphabetic characters in the fully qualified class name MAY be any combination of lower case and upper case. 大文字小文字の任意の組み合わせで良い。 大文字小文字に意味を持たせるフレームワークが多いからね。 All class names MUST be referenced in a case-sensitive fashion. 大文字小文字を区別する。 ファイルをロードする側の決まりごと A contiguous series of one or more leading namespace and sub-namespace names, not including the leading namespace separator, in the fully qualified class name (a \"namespace prefix\") corresponds to at least one \"base directory\". NamespaceNameといくつかのSubNamespaceNameを「名前空間プレフィックス」と呼ぶ SubNamespaceNameが複数個あった場合その全てが「名前空間プレフィックス」になるとは読めない。 完全修飾クラス名とは「名前空間プレフィックス」+(「サブ名前空間」)+「クラス名」となる。 「名前空間プレフィックス」がディレクトリ名と対応している必要がある。と書いてあるがサンプルの「BaseDirectory」が名前空間プレフィックスと全然違う。意味不明。 The contiguous sub-namespace names after the \"namespace prefix\" correspond to a subdirectory within a \"base directory\", in which the namespace separators represent directory separators. The subdirectory name MUST match the case of the sub-namespace names. 名前空間プレフィックスに続くサブ名前空間が、BaseDirectoryに続くサブディレクトリに対応する。 サブ名前空間とサブディレクトリの大文字小文字は一致している必要がある。 The terminating class name corresponds to a file name ending in .php. The file name MUST match the case of the terminating class name. 終端のクラス名は、大文字小文字が一致するphpファイルと対応する。 例 PSR-4の例。やはり、どうも規則性が見えない。 FULLY QUALIFIED CLASS NAME NAMESPACE PREFIX BASE DIRECTORY RESULTING FILE PATH AcmeLogWriterFile_Writer AcmeLogWriter ./acme-log-writer/lib/ ./acme-log-writer/lib/File_Writer.php AuraWebResponseStatus AuraWeb /path/to/aura-web/src/ /path/to/aura-web/src/Response/Status.php SymfonyCoreRequest SymfonyCore ./vendor/Symfony/Core/ ./vendor/Symfony/Core/Request.php ZendAcl Zend /usr/includes/Zend/ /usr/includes/Zend/Acl.php 全然規則性が見えないぞ? 全然しっくりこないのでさらに調べてみた。COMPOSERがだいぶ前にPSR-4に対応したらしいのでAUTOLOADの補完ルールを色分けした。を参考にさせて頂きました。 FULLY QUALIFIED CLASS NAME NAMESPACE PREFIX BASE DIRECTORY RESULTING FILE PATH AcmeLogWriterFile_Writer AcmeLogWriter ./acme-log-writer/lib/ ./acme-log-writer/lib/File_Writer.php AuraWebResponseStatus AuraWeb /path/to/aura-web/src/ /path/to/aura-web/src/Response/Status.php SymfonyCoreRequest SymfonyCore ./vendor/Symfony/Core/ ./vendor/Symfony/Core/Request.php ZendAcl Zend /usr/includes/Zend/ /usr/includes/Zend/Acl.php 訳にも書いたが、全てのサブ名前空間が名前空間プレフィックスに含まれる訳ではないことに注意。名前空間プレフィックスに含まれなかったサブ名前空間が突然ファイルパスに出現したりする。 サブ名前空間とBaseDirectoryの規則性について、サブ名前空間をハイフンで区切った一つのディレクトリと対応させる場合もあるし、階層構造まで一致させる場合もある。名前空間プレフィックスとBase Directoryの関係性は割と柔軟。