http://qiita.com/Nabetani/items/c206fbc645c255cb7de6
http://nabetani.sakura.ne.jp/hena/ord11arithseq/
前回の続き。
今回は複数の等差数列から、最も長い列になるものを取得します。
最初は/(1.{0,})\1{3,}1/とかそんなかんじでガガッと一発取得してやろう、とか考えていたのですが、後方参照でサブパターンをマッチさせる方法がわからず挫折。
結局一重ではありますがループになってしまいました。
残念。
得意な人ならきっとできると思うので教えてください。
かかった時間は2時間くらい。
そのうち1.5時間は正規表現を作っては消していたところ。
つうかbase_convert()便利だな。
「オフラインリアルタイムどう書く」の一覧
http://nabetani.sakura.ne.jp/hena/ord11arithseq/
前回の続き。
今回は複数の等差数列から、最も長い列になるものを取得します。
= strlen($input)){ break; }
}
return $count;
}
}
// テスト
$test = [
[ '12345abcz', '5' ],
/* 省略 */
];
$sequence = new SEQUENCE();
foreach($test as $key=>$data){
$answer = $sequence->get($data[0]);
if($answer !== (int)$data[1]){
print('えらー');
}
}
順番にくるくるやっていけばいいだけなのですが、何故か正規表現に思い至ってしまったのでやってみた。最初は/(1.{0,})\1{3,}1/とかそんなかんじでガガッと一発取得してやろう、とか考えていたのですが、後方参照でサブパターンをマッチさせる方法がわからず挫折。
結局一重ではありますがループになってしまいました。
残念。
得意な人ならきっとできると思うので教えてください。
かかった時間は2時間くらい。
そのうち1.5時間は正規表現を作っては消していたところ。
つうかbase_convert()便利だな。
「オフラインリアルタイムどう書く」の一覧
PR
http://qiita.com/Nabetani/items/93cde1a6b7561426a3ac
http://nabetani.sakura.ne.jp/hena/ord11bitamida/
前回はいきなり第12回を解いてたので、だんだん後ろに戻ってみたいと思います。
今回は第11回。
お題はビットあみだくじ。
そのかわりといってはなんですが、5段以上、10行以上のあみだにも簡単に対応できます。
「オフラインリアルタイムどう書く」の一覧
http://nabetani.sakura.ne.jp/hena/ord11bitamida/
前回はいきなり第12回を解いてたので、だんだん後ろに戻ってみたいと思います。
今回は第11回。
お題はビットあみだくじ。
extractHex($hexData);
foreach($bitArray as $key=>$bits){
// 1段進む
$nowAmida = $this->getNext($nowAmida, $bits);
}
// 終了
return implode('', $nowAmida);
}
/**
* 1段進む
* 1があれば、次の0を探してそこと入れ替えればよい
* @param array 現在の配列
* @param String '01010101'みたいな
* @param array 一歩進んだ後の配列
*/
private function getNext($nowAmida, $bits){
$bits .= '0'; // 最後の0
$left = -1; // 左端の1の場所
for($loop=0; $loop<strlen($bits); $loop++){="" 1だった="" if($bits[$loop]="=='1'){" 左端が無ければ左端としてセット="" if($left="" <="" 0){="" $left="$loop;" }="" continue;="" 0だった="" 左端がセットされていれば、その位置と入れ替える=""> -1){
list($nowAmida[$loop], $nowAmida[$left]) = [ $nowAmida[$left], $nowAmida[$loop] ];
$left = -1;
}
}
return $nowAmida;
}
/**
* 16進数を2進数に展開
* @param String 「d6-7b-e1-9e」みたいな文字列
* @return array ['00000000', '11111111' ]
*/
private function extractHex($hexData){
$bin = explode('-', $hexData);
array_walk($bin, function(&$n) {
$n = str_pad(base_convert($n, 16, 2), 8, '0', STR_PAD_LEFT);
});
return $bin;
}
}
// 以下はテスト
$test = [
['d6-7b-e1-9e', '740631825' ], ['83-4c-20-10', '123805476' ], ['fb-f7-7e-df', '274056813' ], ['55-33-0f-ff', '123456780' ],
['00-00-00-00', '012345678' ], ['00-00-00-55', '021436587' ], ['40-10-04-01', '021436587' ], ['00-00-aa-00', '103254768' ],
['80-20-08-02', '103254768' ], ['ff-7e-3c-18', '876543210' ], ['aa-55-aa-55', '351708264' ], ['55-aa-aa-55', '012345678' ],
['db-24-db-e7', '812543670' ], ['00-01-00-40', '021345687' ], ['00-00-80-00', '102345678' ], ['01-40-00-00', '021345687' ],
['00-00-00-02', '012345768' ], ['00-00-02-00', '012345768' ], ['00-14-00-00', '012436578' ], ['00-00-01-40', '021345687' ],
['00-80-01-00', '102345687' ], ['c8-00-00-81', '120354687' ], ['05-48-08-14', '021435687' ], ['24-05-00-f0', '413205687' ],
['40-08-14-01', '021536487' ], ['18-c8-80-80', '210534678' ], ['1c-88-52-00', '120564738' ], ['ec-dc-67-62', '213468705' ],
['0a-b6-60-e9', '035162784' ], ['52-d6-c6-c2', '120345678' ], ['47-e7-b0-36', '231047658' ], ['0f-85-91-aa', '108263754' ],
['76-b6-ed-f3', '601435782' ], ['f5-5e-f7-3d', '025847163' ], ['dd-e7-fb-f9', '610247538' ], ['8f-f4-af-fd', '583246017' ],
['bf-fb-cb-f7', '105382674' ], ['e5-fd-ff-ff', '512046378' ], ['ef-df-ef-fe', '713205648' ], ['bf-7f-fd-d7', '826437105' ],
['36-ff-df-de', '814527603' ], ['6f-dd-ff-ff', '230685147' ]
];
$amida = new AMIDA();
foreach($test as $key=>$data){
$answer = $amida->get($data[0]);
if($answer !== $data[1]){
print('えらー');
}
}
ビット演算が苦手なので、地味に左から見ていくだけという単純かつ地道なアルゴリズムになっています。そのかわりといってはなんですが、5段以上、10行以上のあみだにも簡単に対応できます。
「オフラインリアルタイムどう書く」の一覧
http://qiita.com/Nabetani/items/1de39df381dfeee305ab
http://nabetani.sakura.ne.jp/hena/ord12aloroturtle/
道なりの亀、という問題を見つけました。
我らがPHPで解いてる人が誰もいなかったのでやってみました。
かかった時間はたぶん1時間くらいです。
きちんと計っとけばよかった。
「オフラインリアルタイムどう書く」の一覧
http://nabetani.sakura.ne.jp/hena/ord12aloroturtle/
道なりの亀、という問題を見つけました。
我らがPHPで解いてる人が誰もいなかったのでやってみました。
かかった時間はたぶん1時間くらいです。
きちんと計っとけばよかった。
<?php
class KAME{
// マップ
private $mapArray = [
'ABCDEFGHIJK', 'LMNOPQRSTUV', 'WXYZabcdefg'
,'hij?????765', 'klm?????432', 'nop?????10z'
,'qrs?????yxw', 'tuv?????vut'];
// 一歩進む先
private $directionTo = [[1, 0], [0, 1], [-1, 0], [0, -1]];
// 現在地
private $nowX = 0;
private $nowY = 0;
// 向いてる方向
private $direction = 0; // →0↓1←2↑3
/**
* 辿ったルートを取得
* @param String 辿る道順
* @param String 辿ったルート
*/
public function get($route){
$this->reset();
$ret = 'A';
foreach(str_split($route) as $now){
if($now === 'R'){
$this->turnRight();
continue;
}elseif($now === 'L'){
$this->turnLeft();
continue;
}else{
$ret .= $this->getRoute(hexdec($now));
if(substr($ret, -1) === '?'){return $ret;}
continue;
}
}
return $ret;
}
/**
* 辿ったルートを取得
* @param int 何歩進むか
* @param String 辿ったルートを表す文字列
*/
private function getRoute($steps){
$ret = '';
// 歩数分繰り返し
for($i=0; $i<$steps; $i++){
// 一歩進む
$this->nowX += $this->directionTo[$this->direction][0];
$this->nowY += $this->directionTo[$this->direction][1];
// 進んだ先の値を取得
if(!isset($this->mapArray[$this->nowY][$this->nowX])){
return $ret . '?';
}
$kame = $this->mapArray[$this->nowY][$this->nowX];
// 進んだ先が場外であれば終了
if($kame==='?'){
return $ret . '?';
}
// 前回進んだ先が'tuv'であれば現在地と方向を変換
$this->routeChange($kame);
// ルートを積む
$ret .= $kame;
}
return $ret;
}
/**
* 現在地が'tuv'の何れかである場合、現在地と方向を変更する必要がある
* @param 現在地
*/
private function routeChange($kame){
// tuv以外なら何もしない
if(strpos('tuv', $kame) === false){
return;
}
// それ以外なら位置を変更、方向は反転
$this->nowX = (10 - $this->nowX);
$this->turnRight();
$this->turnRight();
}
/**
* 右を向く
*/
private function turnRight(){
if(++$this->direction >= 4){$this->direction = 0;}
}
/**
* 左を向く
*/
private function turnLeft(){
if(--$this->direction <= -1){$this->direction = 3;}
}
/**
* リセット
*/
private function reset(){
$this->nowX = 0;
$this->nowY = 0;
$this->direction = 0;
}
}
// 以下はテスト
$test = [];
$test[] = [ "2RcL3LL22", "ABCNYjmpsvy147edcbcdef" ];
$test[] = [ "L3R4L5RR5R3L5", "A?" ];
$test[] = [ "2ReLLe", "ABCNYjmpsvy147eTITe741yvspmjYNC" ];
$test[] = [ "1ReRRe", "ABMXilorux036fUJUf630xuroliXMB" ];
$test[] = [ "ReRRe", "ALWhknqtwz25gVKVg52zwtqnkhWLA" ];
$test[] = [ "f", "ABCDEFGHIJK?" ];
$test[] = [ "Rf", "ALWhknqtwz25gVK?" ];
$test[] = [ "1Rf", "ABMXilorux036fUJ?" ];
$test[] = [ "2Rf", "ABCNYjmpsvy147eTI?" ];
$test[] = [ "aR1RaL1LaR1R2L1L2", "ABCDEFGHIJKVUTSRQPONMLWXYZabcdefg567432" ];
$test[] = [ "2R1R2L1L2R1R2L1L2R1R2L1L2R1R2L1L2", "ABCNMLWXYjihklmponqrsvutwxy" ];
$test[] = [ "2R4R2L4L2R4R2L4L2R4R2L4L2", "ABCNYjmlknqtwxy147efgVK?" ];
$test[] = [ "R1L2R4R2L4L2R4R2L4L2R4R2L4L2", "ALMNYjmponqtwz0147eTUVK?" ];
$test[] = [ "R2L2R4R2L4L2R4R2L4L2R4R2L4L2", "ALWXYjmpsrqtwz2347eTIJK?" ];
$test[] = [ "R3L2R4R2L4L2R4R2L4L2R4R2L4L2", "ALWhijmpsvutwz2567eTI?" ];
$test[] = [ "R5L2L5L1LaR1L4L5", "ALWhknopmjYNCBMXilorux0325gVKJIHGF" ];
$test[] = [ "1R2L4L2R4R2L4L2R4", "ABMXYZabQFGHIJUfg?" ];
$test[] = [ "2R2L4L2R4R2L4L2R4", "ABCNYZabcRGHIJKVg?" ];
$test[] = [ "3R2L4L2R4R2L4L2R4", "ABCDOZabcdSHIJK?" ];
$test[] = [ "4R2L4L2R4R2L4L2R4", "ABCDEPabcdeTIJK?" ];
$test[] = [ "5R2L4L2R4R2L4L2R4", "ABCDEFQbcdefUJK?" ];
$test[] = [ "LLL1RRR1LLL1RRR2R1", "ALMXYZ?" ];
$test[] = [ "R3RRR3", "ALWhij?" ];
$test[] = [ "1LLL4RRR1LR1RL1", "ABMXilm?" ];
$test[] = [ "R2L1R2L1R3R4", "ALWXilmpsvut?" ];
$test[] = [ "7R4f47LLLc6R9L", "ABCDEFGHSd?" ];
$test[] = [ "5RR868L8448LL4R6", "ABCDEFEDCBA?" ];
$test[] = [ "42Rd1RLLa7L5", "ABCDEFGRc?" ];
$test[] = [ "RRLL6RLR1L5d12LaLRRL529L", "ABCDEFGRSTUV?" ];
$test[] = [ "RLR7L6LL1LRRRcRL52R", "ALWhknqtuv?" ];
$test[] = [ "1RLR8RLR1R437L99636R", "ABMXiloruxwtqnkhWLA?" ];
$test[] = [ "LLL2L3La9Le5LRR", "ALWXYZOD?" ];
$test[] = [ "R1LcRR491", "ALMNOPQRSTUV?" ];
$test[] = [ "R8L1R1R512L8RLLReRf", "ALWhknqtwx0z?" ];
$test[] = [ "1RcL8f1L29a5", "ABMXilorux036fedcbaZYXW?" ];
$test[] = [ "R822LeL46LL39LL", "ALWhknqtwz25gfedcbaZYXW?" ];
$test[] = [ "9R3L5LRRLb5R3L7cLLLR4L", "ABCDEFGHIJUf65?" ];
$test[] = [ "7LLRRR2R3R69Lf76eR2L", "ABCDEFGHSdcbaPE?" ];
$test[] = [ "8RRRLL3Le", "ABCDEFGHITe765?" ];
$test[] = [ "8R5RLL6LbL4LL5bL", "ABCDEFGHITe7410z?" ];
$test[] = [ "6LR2R1LR5LRLRL484L63", "ABCDEFGHITe741yxw?" ];
$kame = new KAME();
foreach($test as $key=>$data){
$route = $kame->get($data[0]);
if($route !== $data[1]){
print('えらー');
}
}
「オフラインリアルタイムどう書く」の一覧
http://blog.generace.co.jp/2013/06/21/894
なんか前提の時点でおかしい。
> 1.ループ処理を比較
> 2.インクリメントの前置と後置を比較
速度ではなく目的によって使い分けるべき。
あとIteratorは?
> 3.if else と 三項演算子を比較
> 4.if else と switch を比較
if($a){/* */}elseif($b){/* */}という比較は三項演算子やswitchでは(普通に使う限り)できない。
> 5.== と === の比較
理由がない限り===を使うべき。
> 6.empty と count の比較
count(false)→1
PDOStatement::fetch()は結果セットが無いとFALSEを返すので誤診断する。
まあfetchAll()使えばいいっちゃいいんですが。
というかむしろここでこそ5番目の==を使ってif($list)にした方がいいと思われる。
> 7.isset と array_key_exists の比較
isset($list[0], $list[1])という使い方はarray_key_existsではできない。
> まとめ
ここまでの中身とまとめ全然関係ねえ!
そういや前回のコードもフルボッコにされてるのにそのままですね。
Web系なのにこういうフットワークの鈍いところはちょっと。
なんか前提の時点でおかしい。
> 1.ループ処理を比較
> 2.インクリメントの前置と後置を比較
速度ではなく目的によって使い分けるべき。
あとIteratorは?
> 3.if else と 三項演算子を比較
> 4.if else と switch を比較
if($a){/* */}elseif($b){/* */}という比較は三項演算子やswitchでは(普通に使う限り)できない。
> 5.== と === の比較
理由がない限り===を使うべき。
> 6.empty と count の比較
count(false)→1
PDOStatement::fetch()は結果セットが無いとFALSEを返すので誤診断する。
まあfetchAll()使えばいいっちゃいいんですが。
というかむしろここでこそ5番目の==を使ってif($list)にした方がいいと思われる。
> 7.isset と array_key_exists の比較
isset($list[0], $list[1])という使い方はarray_key_existsではできない。
> まとめ
ここまでの中身とまとめ全然関係ねえ!
そういや前回のコードもフルボッコにされてるのにそのままですね。
Web系なのにこういうフットワークの鈍いところはちょっと。
前回の続き。
interfaceのinstanceofで引数判定、なんてよくやることですが、instanceof演算子はtraitには反応しません。
http://tanakahisateru.hatenablog.jp/entry/20110704/1309781929
> そりゃまあ当然で、PHP5.4のトレイトは、使用者側でアドホックにメソッド名の置き換えすらできてしまうので、「必ず特定のメソッドに応答することを保証する」インターフェースとしては使えないです。
実は元のメソッド名でも反応します。
asは単にエイリアスを作るだけなので、元のメソッド自体は残っているのです。
参考リンク先にも普通に
> Test class will now have "hello" and "helloA" methods
って書かれています。
従ってトレイトを「必ず特定のメソッドに応答することを保証する」インターフェースとして使用することは可能……と思いきや実はできません。
B::hoge as private;ってするとCall to private methodになっちゃうんですよね。
なんてこった。
ということで先方のサイトはこう読み直すのがよいでしょう。
> そりゃまあ当然で、PHP5.4のトレイトは、使用者側でアドホックに可視性の変更すらできてしまうので、「必ず特定のメソッドに応答することを保証する」インターフェースとしては使えないです。
まあ、せっかくなのでtraitをインターフェースとして使ってみます。
特にトレイト名を文字列で与えているあたりが微妙だ。
トレイト名はクラス名のようには扱うことができず、use節以外で使用するとUse of undefined constantのNoticeになってしまうようです。
こんな面倒なことをせず素直にmethod_exists($d, 'hoge')でいいや、と思ったのは秘密。
PHPにはこういう横紙破りな関数が揃ってるのが良くも悪くもナイスですね。
interfaceのinstanceofで引数判定、なんてよくやることですが、instanceof演算子はtraitには反応しません。
<?php
// 基底クラス
class A{}
// trait
trait B{
public function hoge(){
print('B::hoge/'.__CLASS__.'/'.__METHOD__);
}
}
trait C{
public function hoge(){
print('C::hoge/'.__CLASS__.'/'.__METHOD__);
}
}
// Aを継承したクラス
class D extends A{
// トレイトB,Cを使用
use B, C{
// B::hogeを優先
B::hoge insteadof C;
// B::hogeをfoo()に改名
B::hoge as foo;
// C::hogeをbar()に改名
C::hoge as bar;
}
}
$d = new D();
$d->foo(); // B:hoge/D/B::hoge
$d->bar(); // C:hoge/D/C::hoge
$d->hoge(); // B:hoge/D/B::hoge 元のメソッド名でも反応する
$d instanceof A; // true
$d instanceof C; // false
http://tanakahisateru.hatenablog.jp/entry/20110704/1309781929
> そりゃまあ当然で、PHP5.4のトレイトは、使用者側でアドホックにメソッド名の置き換えすらできてしまうので、「必ず特定のメソッドに応答することを保証する」インターフェースとしては使えないです。
実は元のメソッド名でも反応します。
asは単にエイリアスを作るだけなので、元のメソッド自体は残っているのです。
参考リンク先にも普通に
> Test class will now have "hello" and "helloA" methods
って書かれています。
従ってトレイトを「必ず特定のメソッドに応答することを保証する」インターフェースとして使用することは可能……と思いきや実はできません。
B::hoge as private;ってするとCall to private methodになっちゃうんですよね。
なんてこった。
ということで先方のサイトはこう読み直すのがよいでしょう。
> そりゃまあ当然で、PHP5.4のトレイトは、使用者側でアドホックに可視性の変更すらできてしまうので、「必ず特定のメソッドに応答することを保証する」インターフェースとしては使えないです。
まあ、せっかくなのでtraitをインターフェースとして使ってみます。
<?php
/**
* @param Object インスタンス
* @param String トレイト名
* @return boolean instanceof
*/
function trait_instanceof($trait, $className){
$class = new ReflectionClass($trait);
$traits = $class->getTraitNames();
return in_array($className, $traits, true);
}
$is = trait_instanceof($d, 'B');
微妙だ。特にトレイト名を文字列で与えているあたりが微妙だ。
トレイト名はクラス名のようには扱うことができず、use節以外で使用するとUse of undefined constantのNoticeになってしまうようです。
こんな面倒なことをせず素直にmethod_exists($d, 'hoge')でいいや、と思ったのは秘密。
PHPにはこういう横紙破りな関数が揃ってるのが良くも悪くもナイスですね。
PHP5.4でtraitという機能が追加されています。
クラスAをextendsしてるけど、ここに追加でクラスBとクラスCもextendsしたい、そんな要求を概ね叶えてくれる機能です。
クラスとはひとつしかextendsできないため、extends A, B, Cができませんでした。
トレイトを使うことによってこれらの問題点を解消し、ロジックを書いたうえで複数extendsのようなことを行うことができるようになります。
トレイト内のprivateな値も参照できていることから、普通のextendsより強い結びつきということがわかります。
実際はもっと単純に、useしたtraitは全部合わせてひとつのクラスと看做すと考えればよいでしょう。
要するにもうPartial classってことでいいよね。
クラスAをextendsしてるけど、ここに追加でクラスBとクラスCもextendsしたい、そんな要求を概ね叶えてくれる機能です。
<?php
// 基底クラス
class A{
public function hoge(){
print('クラスAのメソッドhoge');
}
}
// trait
trait B{
public function hoge(){
print('トレイトBのメソッドhoge');
}
public function fuga(){
print('トレイトBのメソッドfuga');
}
public function foo(){
print('トレイトBのメソッドfoo');
}
}
trait C{
public function fuga(){
print('トレイトCのメソッドfuga');
}
private $int = 1; // プロパティ
}
// Aを継承したクラス
class D extends A{
// トレイトB,Cを使用
use B, C{
// 被っているメソッドfugaはBを使用
B::fuga insteadof C;
}
// @Override
public function foo(){
print('クラスDのメソッドfoo');
}
// トレイトのprivateを参照可能
public function getInt(){
print($this->int);
}
}
// 優先順位は自分自身 > trait > 基底クラス
$d = new D();
$d->hoge(); // トレイトBのメソッドhoge
$d->fuga(); // トレイトBのメソッドfuga
$d->foo(); // クラスDのメソッドfoo
$d->getInt(); // 1
interfaceではメソッドの定義しか行えずロジックは書けませんでした。クラスとはひとつしかextendsできないため、extends A, B, Cができませんでした。
トレイトを使うことによってこれらの問題点を解消し、ロジックを書いたうえで複数extendsのようなことを行うことができるようになります。
トレイト内のprivateな値も参照できていることから、普通のextendsより強い結びつきということがわかります。
実際はもっと単純に、useしたtraitは全部合わせてひとつのクラスと看做すと考えればよいでしょう。
要するにもうPartial classってことでいいよね。
Aura.Httpは、HTTPリクエストを行ったりするライブラリです。
実体はCURLのラッパですが、CURLが無かったときは自力でリクエストしてくれるみたいです。
あとheader()等ブラウザに返すレスポンスも出力できるようです。
ということで早速使ってみようとしたのですが、とりあえずコンストラクタが異様にめんどいです。
こんなのサンプルがないと絶対わからんわ。
なんでAura\Http\Message\Factoryを2回も使っているのでしょうか?
これについては素直にinstance.phpを使った方がよいでしょう。
ていうか何故instance.phpの自動インクルードはないのでしょうか。
インクルードしてしまえば使用方法自体は非常に簡単です。
使ってみましょう。
どういうことだよ。
Array to string conversionというNoticeが発生します。
発生箇所はAura\Http\Adapter\Curl.phpです。
一回目のsend()の時点のデータが、その後newRequest()で新しいリクエストを取得したのにも関わらず残ってるのですよね。
これを回避するには、再度ややこしいコンストラクタを使って完全に新たなHTTPオブジェクトを作らないといけないっぽいです。
せっかくのnewRequest()意味なし。
うーむ、Auraは素晴らしいフレームワークだ、って話自体が微妙なことになってきたような気がしないでもないような。
AuraPHPの記事
実体はCURLのラッパですが、CURLが無かったときは自力でリクエストしてくれるみたいです。
あとheader()等ブラウザに返すレスポンスも出力できるようです。
ということで早速使ってみようとしたのですが、とりあえずコンストラクタが異様にめんどいです。
こんなのサンプルがないと絶対わからんわ。
なんでAura\Http\Message\Factoryを2回も使っているのでしょうか?
これについては素直にinstance.phpを使った方がよいでしょう。
ていうか何故instance.phpの自動インクルードはないのでしょうか。
インクルードしてしまえば使用方法自体は非常に簡単です。
使ってみましょう。
<?php
require_once('path/to/channel/vendor/autoload.php');
// Aura.Http
$http = require_once('path/to/channel/vendor/aura/http/scripts/instance.php');
// リクエスト
$request = $http->newRequest();
$request->setMethod(Aura\Http\Message\Request::METHOD_GET);
$request->setUrl('http://example.com/hoge.php');
// BASIC認証
$request->setAuth(Aura\Http\Message\Request::AUTH_BASIC)
->setUsername('user')->setPassword('pass');
// 送信実行
$stack = $http->send($request);
// 返り値はSplStackだが必ず[0]
// 本文
$body = $stack[0]->getContent();
// ステータス
$statusCode = $stack[0]->getStatusCode(); // 200
$statusText = $stack[0]->getStatusText(); // OK
// レスポンスヘッダのリスト
$headers = $stack[0]->getHeaders();
// Content-Lengthヘッダ
$contentlength = $stack[0]->headers->{'Content-Length'}; // Aura\Http\Header
// Cookie
$stack[0]->getCookies(); // Aura\Http\Cookie\Collection
// PHPSESSID
$stack[0]->getCookies()->PHPSESSID; // Aura\Http\Cookie
// 2回目のリクエスト
$request2 = $http->newRequest();
$request2->setUrl('http://example.com/fuga.php');
// セッション引き継ぎ
$request2->setCookies($stack[0]->getCookies());
$stack2 = $http->send($request2); // ここでエラー
……エラーになりました。どういうことだよ。
Array to string conversionというNoticeが発生します。
発生箇所はAura\Http\Adapter\Curl.phpです。
一回目のsend()の時点のデータが、その後newRequest()で新しいリクエストを取得したのにも関わらず残ってるのですよね。
これを回避するには、再度ややこしいコンストラクタを使って完全に新たなHTTPオブジェクトを作らないといけないっぽいです。
せっかくのnewRequest()意味なし。
うーむ、Auraは素晴らしいフレームワークだ、って話自体が微妙なことになってきたような気がしないでもないような。
AuraPHPの記事
Aura.Filterは、名前のとおりフィルタリングを行います。
とりあえず最大の問題点を挙げておくと、使用するフィルタを手動で登録しないといけません。
デフォルトで用意されているフィルタについてもです。
Zend_Filterのようなオートロード機構もありません。
デフォルトのコンストラクタはこんなことになっています。
Composerのオートロードは諦め、instance.phpをインクルードするのがよいでしょう。
名前はフィルタですが、実際はバリデーションとフィルタを同時に行います。
Aura\Filter\RuleCollectionにバリデーションとフィルタのルールを与え、最後に値をvalues()で渡すという形でフィルタリングを行います。
values()は参照渡しになっているので、与えたデータが直接変更されます。
返り値はバリデーションとフィルタが成功したらtrue、ひとつでも問題があればfalseとなります。
失敗時の原因は別途getMessages()で取得します。
ルールの与えかたは3種類あります。
addSoftRule()は、該当のルールでバリデーションが失敗しても、失敗を記録するだけで次のルールに進みます。
基本的にこれを使うことが多いでしょう。
addHardRule()は、失敗したら同じ値に対してその後のルール適用は行いません。
例の場合、'email'判定に失敗したら、'alpha'への変換を実行しない、となります。
それ以外の値、上記では'a'や'c'については影響を及ぼしません。
addStopRule()は、失敗した時点で全てのフィルタリングを即座に中止します。
これらは第二引数に与える値によって挙動が変化します。
まずバリデーションとして、RuleCollection\ISであれば===、IS_NOTは!==、IS_BLANK_ORは===もしくはNULLです。
次にフィルタとして、FIXであれば正規表現による置換、FIX_BLANK_ORであれば置換、ただし''などはNULLにする、となります。
基本的にはRuleCollection\IS等でバリデーションだけを行い、電話番号欄に数値以外があったらRuleCollection\FIXで数値を削除といったふうに追加でフィルタリングを行うという運用になるでしょう。
ルールはコンストラクタを見るとわかるとおり、最初から30種類ほど用意されているので、大抵はそれらを使うだけで事足りるはずです。
なお、Aura.Filterはあくまで入力値に対するフィルタリングを行うだけなので、値をHTMLやSQLに渡す際のエスケープはもちろん別途行わなければなりません。
入力値フィルタでhtmlspecialchars()やmysqli_real_escape_string()を行うのは大間違いなので行わないようにしましょう。
もちろんその手のフィルタは最初から存在しません。
AuraPHPの記事
とりあえず最大の問題点を挙げておくと、使用するフィルタを手動で登録しないといけません。
デフォルトで用意されているフィルタについてもです。
Zend_Filterのようなオートロード機構もありません。
デフォルトのコンストラクタはこんなことになっています。
$ruleCollection = new \Aura\Filter\RuleCollection(
new \Aura\Filter\RuleLocator([
'alnum' => function() { return new Rule\Alnum; },
'alpha' => function() { return new Rule\Alpha; },
'between' => function() { return new Rule\Between; },
'blank' => function() { return new Rule\Blank; },
'bool' => function() { return new Rule\Bool; },
'creditCard' => function() { return new Rule\CreditCard; },
'dateTime' => function() { return new Rule\DateTime; },
'email' => function() { return new Rule\Email; },
'equalToField' => function() { return new Rule\EqualToField; },
'equalToValue' => function() { return new Rule\EqualToValue; },
'float' => function() { return new Rule\Float; },
'inKeys' => function() { return new Rule\InKeys; },
'int' => function() { return new Rule\Int; },
'inValues' => function() { return new Rule\InValues; },
'ipv4' => function() { return new Rule\Ipv4; },
'max' => function() { return new Rule\Max; },
'min' => function() { return new Rule\Min; },
'regex' => function() { return new Rule\Regex; },
'strictEqualToField' => function() { return new Rule\StrictEqualToField; },
'strictEqualToValue' => function() { return new Rule\StrictEqualToValue; },
'string' => function() { return new Rule\String; },
'strlenBetween' => function() { return new Rule\StrlenBetween; },
'strlenMax' => function() { return new Rule\StrlenMax; },
'strlenMin' => function() { return new Rule\StrlenMin; },
'strlen' => function() { return new Rule\Strlen; },
'trim' => function() { return new Rule\Trim; },
'upload' => function() { return new Rule\Upload; },
'url' => function() { return new Rule\Url; },
'word' => function() { return new Rule\Word; },
])
);
なんとも残念な感じです。Composerのオートロードは諦め、instance.phpをインクルードするのがよいでしょう。
名前はフィルタですが、実際はバリデーションとフィルタを同時に行います。
<?php
// Aura.Filter
$filter = require_once('path/to/channel/vendor/aura/filter/scripts/instance.php');
// 失敗したら全フィルタリングを即打ち切り
$filter->addStopRule('a', $filter::IS, 'alnum');
$filter->addStopRule('a', $filter::FIX, 'int');
// 失敗したら該当の値は打ち切り、他は続ける
$filter->addHardRule('b', $filter::IS, 'email');
$filter->addHardRule('b', $filter::FIX, 'alpha');
// 失敗しても続ける
$filter->addSoftRule('c', $filter::IS, 'bool');
$filter->addSoftRule('c', $filter::FIX, 'blank');
// フィルタ実行
$data = array( 'a'=>'a1', 'b'=>'hoge@example.jp', 'c'=>'c3' );
$success = $filter->values($data); // false
// $dataは直接書き換えられる
// array( 'a'=>1, 'b'=>'hogeexamplejp', 'c'=>NULL )
// 失敗時に原因を取得
$filter->getMessages();
Aura\Filter\RuleCollectionにバリデーションとフィルタのルールを与え、最後に値をvalues()で渡すという形でフィルタリングを行います。
values()は参照渡しになっているので、与えたデータが直接変更されます。
返り値はバリデーションとフィルタが成功したらtrue、ひとつでも問題があればfalseとなります。
失敗時の原因は別途getMessages()で取得します。
ルールの与えかたは3種類あります。
addSoftRule()は、該当のルールでバリデーションが失敗しても、失敗を記録するだけで次のルールに進みます。
基本的にこれを使うことが多いでしょう。
addHardRule()は、失敗したら同じ値に対してその後のルール適用は行いません。
例の場合、'email'判定に失敗したら、'alpha'への変換を実行しない、となります。
それ以外の値、上記では'a'や'c'については影響を及ぼしません。
addStopRule()は、失敗した時点で全てのフィルタリングを即座に中止します。
これらは第二引数に与える値によって挙動が変化します。
まずバリデーションとして、RuleCollection\ISであれば===、IS_NOTは!==、IS_BLANK_ORは===もしくはNULLです。
次にフィルタとして、FIXであれば正規表現による置換、FIX_BLANK_ORであれば置換、ただし''などはNULLにする、となります。
基本的にはRuleCollection\IS等でバリデーションだけを行い、電話番号欄に数値以外があったらRuleCollection\FIXで数値を削除といったふうに追加でフィルタリングを行うという運用になるでしょう。
ルールはコンストラクタを見るとわかるとおり、最初から30種類ほど用意されているので、大抵はそれらを使うだけで事足りるはずです。
なお、Aura.Filterはあくまで入力値に対するフィルタリングを行うだけなので、値をHTMLやSQLに渡す際のエスケープはもちろん別途行わなければなりません。
入力値フィルタでhtmlspecialchars()やmysqli_real_escape_string()を行うのは大間違いなので行わないようにしましょう。
もちろんその手のフィルタは最初から存在しません。
AuraPHPの記事
Aura.Autoloadは、名前のとおりオートローディングを扱います。
自作ライブラリなどをオートロードするのに使えます。
クラス名さえ規則的に決めていれば、その後一切のrequire_once()が不要になります。
オートロードを有効にするには、基本的にファイル名=クラス名とすることが前提となります。
単にregister()するだけでも、include_pathからファイルを探してくれます。
add()メソッドで、クラス名の接頭辞、ネームスペースで抽出し、該当の条件であれば特定のディレクトリから探すという設定が可能になります。
ネームスペースが切ってあった場合、自動的にディレクトリを掘って探します。
これはAuraの命名規則と同じ構造となります。
setClass()は完全に特定のクラスを呼び出すとき専用で、クラス名とは別のファイル名を指定可能です。
まあ、こっちはrequire_once()と変わらない気もしますが。
このように、クラス名の取り決めさえしておけば一切インクルードを書かなくても済むようになる、非常に便利なAura.Autoloadでした。
しかしAuraそのものはオートローディングをComposerに任せていてAura.Autoloadを使っていないというこの現実。
AuraPHPの記事
自作ライブラリなどをオートロードするのに使えます。
クラス名さえ規則的に決めていれば、その後一切のrequire_once()が不要になります。
<?php
require_once('path/to/channel/vendor/autoload.php');
// Aura.Autoloader
$loader = new \Aura\Autoload\Loader();
// spl_autoload_register
$loader->register();
// デフォルトではinclude_pathが対象
$foo = new Foo(); // ./Foo.php
// クラス名がBarで始まる場合、↓をベースディレクトリにする
$loader->add('Bar', '/dir1');
$bar = new Barbara(); // /dir1/Bar.php
// ネームスペースHogeで始まる場合、↓をベースディレクトリにする
$loader->add('Hoge\\', '/dir2');
$fuga = new Hoge\Fuga(); // /dir2/Hoge/Fuga.php
// 個別に指定
$loader->setClass('Piyo', '/dir3/baz.php');
$piyo = new Piyo(); // /dir3/baz.php
// 指定クラスのフルパスを取得
$exist = $loader->find('Hoge\Foo\Bar'); // 存在しなければfalse
// ロード済のファイルを取得
$loaded = $loader->getLoaded();
// spl_autoload_unregister
$loader->unregister();
オートロードを有効にするには、基本的にファイル名=クラス名とすることが前提となります。
単にregister()するだけでも、include_pathからファイルを探してくれます。
add()メソッドで、クラス名の接頭辞、ネームスペースで抽出し、該当の条件であれば特定のディレクトリから探すという設定が可能になります。
ネームスペースが切ってあった場合、自動的にディレクトリを掘って探します。
これはAuraの命名規則と同じ構造となります。
setClass()は完全に特定のクラスを呼び出すとき専用で、クラス名とは別のファイル名を指定可能です。
まあ、こっちはrequire_once()と変わらない気もしますが。
このように、クラス名の取り決めさえしておけば一切インクルードを書かなくても済むようになる、非常に便利なAura.Autoloadでした。
しかしAuraそのものはオートローディングをComposerに任せていてAura.Autoloadを使っていないというこの現実。
AuraPHPの記事
前回前々回とURLをパースしてルーティングを行いましたが、逆にルートからURLを作成することも当然可能です。
あとは返り値にリンクするだけで、翌日翌月次の記事といったリンクが作成可能です。
url_for()が簡単にできました。
AuraPHPの記事
<?php
require_once('path/to/channel/vendor/autoload.php');
// Router
$router = new Aura\Router\Map(
new Aura\Router\DefinitionFactory()
,new Aura\Router\RouteFactory()
);
// ルート
$router->add('date1', '/{:year:(\d+)}/{:month:(\d+)}/{:day:(\d+)}', array(
'values' => array( 'controller' => 'date', 'action' => 'read' )
));
$router->add('date2', '/{:year}{:month}{:day}', array(
'params' => array( 'year' => '(\d\d\d\d)', 'month' => '(\d\d)', 'day' => '(\d\d)' )
));
// ルートからURLを生成
$router->generate('date1', array(
'year'=>'2013', 'month'=>'01', 'day'=>'02'
)); // '/2013/01/02'
$router->generate('date2', array(
'year'=>'2013', 'month'=>'01', 'day'=>'02'
)); // '/20130102'
generate()メソッドに必要な情報を与えると、URLの具体的な値として帰ってきます。あとは返り値にリンクするだけで、翌日翌月次の記事といったリンクが作成可能です。
url_for()が簡単にできました。
AuraPHPの記事