2015-05-26 GMOリサーチ 寺田 渉
Facebook: 寺田渉 Twitter: @wa_terada
ボードゲーム翻訳
CakePHP Cookbook翻訳
TED 字幕 翻訳
開発 (PHP/JavaScript/Java/Perl)
0:00+30
PHPの仕様で
初心者がハマりやすいポイントや
知ってると便利な関数を紹介します。
0:30+15
文法編
==
これは NG!!!! が出力される。
なぜ?
<?php
$input = "1abc";
if ($input == 1) { echo "NG!!!!"; }
0:45+15
キャストされるから。
さけるには === を使う。
左右のどちらかが文字列でない場合、
== は危険。
<?php
$input = "1abc";
if ($input == 1) { echo "NG!!!!"; }
1:00+15
empty()
変数が
null , false , 0 , 0.0 , '' , [] , 未定義
なら true になる。
他にもあるが何?
empty(変数)
1:15+15
文字列の '0' が空だと
判定されることを忘れないこと。
$var = '0';
empty($var); //←これは true
1:30+15
文法っぽい関数
range(0, 10000, 5)
0,5,10,...という具合に
10000まで5つ刻みの数列がほしい!
1:45+15
$id = $array[0];
$name = $array[1];
これを下記のように書けます!
list($id, $name) = $array;
2:00+15
配列の +
この結果は違う?
配列の + と array_merge は何が違う?
<?php
$a = ['a' => 1, 'b' => 3, 5];
$b = ['a' => 2, 'c' => 4, 6];
var_export($a + $b);
var_export(array_merge($a, $b));
2:15+30
【$a + $b】
array (
'a' => 1, //先勝ち
'b' => 3,
'c' => 4,
0 => 5,
//先勝ちで6無し
)
→ key/index どちら
でも常に先勝ち
【array_merge($a, $b)】
array (
'a' => 2, //後勝ち
'b' => 3,
'c' => 4,
0 => 5,
1 => 6, //勝手に連番
)
→ key なら後勝ち
index なら連番
$a = ['a' => 1, 'b' => 3, 5];
$b = ['a' => 2, 'c' => 4, 6];
2:45+30
では、これはどうなるでしょう?
<?php
$a = [1,2];
$b = [3,4,5];
var_export($a + $b);
var_export(array_merge($a, $b));
3:15+15
【$a + $b】
array (
0 => 1,
1 => 2,
2 => 5,
)
【array_merge($a, $b)】
array(
0 => 1,
1 => 2,
2 => 3,
3 => 4,
4 => 5,
)
$a = [1,2];
$b = [3,4,5];
※おそらく混乱するので
配列の + はオススメしません。3:30+15
ついでに
array_merge_recursive
下記ではどうなる?
$a = ['a' => ['b' => 1]];
$b = ['a' => ['c' => 2]];
var_export(array_merge_recursive($a, $b));
//[
// 'a' => [
// 'b' => 1,
// 'c' => 2,
// ]
//]
$a = ['a' => 1];
$b = ['a' => 1];
var_export(array_merge_recursive($a, $b));
3:45+30
$a = ['a' => 1];
$b = ['a' => 1];
var_export(array_merge_recursive($a, $b));
答え: ['a' => [1, 1]]
array_merge_recursive は再帰的にマージするものだが、
マージする対象に配列以外があった場合は、
配列に変換 してマージする。
配列への変換を望まないなら CakePHP なら Hash::merge が使える。
4:15+30
関数編
文字列 分割
いろいろ
<?php
$str = "aaa<br/>bbb<br/>ccc";
$res = 【なんでしょう】;
array (
0 => 'aaa',
1 => 'bbb',
2 => 'ccc',
)
期待する結果:
4:45+10
<?php
$str = "aaa<br/>bbb<br/>ccc";
$res = explode("<br/>", $str);
var_export($res);
array (
0 => 'aaa',
1 => 'bbb',
2 => 'ccc',
)
結果:
4:55+05
<?php
$str = "aaa<br/>bbb<br>ccc";
$res = 【なんでしょう】;
期待する結果:
array (
0 => 'aaa',
1 => 'bbb',
2 => 'ccc',
)
5:00+10
array (
0 => 'aaa',
1 => 'bbb',
2 => 'ccc',
)
<?php
$str = "aaa<br/>bbb<br>ccc";
$res = preg_split('/<br¥/?>/', $str);
var_export($res);
結果:
5:10+05
<?php
$str = "aaa<br/>bbb<br>ccc";
$res = 【なんでしょう】;
期待する結果:
array (
0 => 'aaa',
1 => '<br/>',
2 => 'bbb',
3 => '<br>',
4 => 'ccc',
)
5:15+10
<?php
$str = "aaa<br/>bbb<br>ccc";
$res = preg_split('/(<br¥/?>)/', $str,
null, PREG_SPLIT_DELIM_CAPTURE);
var_export($res);
array (
0 => 'aaa',
1 => '<br/>',
2 => 'bbb',
3 => '<br>',
4 => 'ccc',
)
結果:
5:25+05
<?php
$str = "aaa<br/>bbb<br>ccc";
$res = str_split($str, 5);
var_export($res);
array (
0 => 'aaa<b',
1 => 'r/>bb',
2 => 'b<br>',
3 => 'ccc',
)
結果:
5:30+15
array_map
と
array_walk
指定した配列の要素に
コールバック関数を適用する
( http://jp1.php.net/manual/ja/function.array-map.php より)
配列の全ての要素に
ユーザー定義の関数を適用する
( http://jp1.php.net/manual/ja/function.array-walk.php より)
答えはドキュメントの中! ヒント:引数と戻り値
array array_map ( callable $callback , array $array1 [, array $... ] )
bool array_walk ( array &$array , callable $callback [, mixed $use
5:45+30
array_map
array_walk
array array_map ( callable $callback , array $array1 [, array $..
bool array_walk ( array &$array , callable $callback [, mixed $u
そう!
array_map は変更後の新しい配列を作るためのもの
array_walk は自身を書き換えるためのもの
6:15+30
foreach
で
置き換え
$array1 = [1,2];
foreach ($array1 as &$val) {
$val = '書換';
}
$array2 = [3,4];
foreach ($array2 as $val) {
//何か
}
var_export($array1); // ['書換', 4] なぜ!?
var_export($array2); // [3, 4]
6:45+30
$array1 = [1,2];
foreach ($array1 as &$val) {
$val = ‘書換’;
}
$array2 = [3,4];
foreach ($array2 as $val) {
//何か
}
$array1 = [1,2];
foreach ($array1 as &$val) {
$val = ‘書換’;
}
unset($val); //かならずこれが必要
$array2 = [3,4];
foreach ($array2 as $val) {
//何か
}
7:15+15
$array1 = [1,2];
array_walk($array1, function(&$val) {
$val = '書換';
});
//これなら危険は無い。これがオススメ。
$array2 = [3,4];
foreach ($array2 as $val) {
//何か
}
7:30+15
エンコードする
タイミング
これは何がNGか解りますか?
$fh = fopen($path, 'r');
while (($data = fgetcsv($fh, 1000, ",")) !== false) {
array_walk($data, function(&$val) {
$val = mb_convert_encoding($val,'UTF-8','SJIS');
});
// $data を使う処理
}
fclose($fh);
7:45+30
ならOK? まだダメですよね?
$fh = fopen($path, 'r');
while (($line = fgets($fh)) !== false) {
$line = mb_convert_encoding($line,'UTF-8','SJIS');
$data = str_getcsv($line, ",");
// $data を使う処理
}
fclose($fh);
エンコード前に fgetcsv を呼んではいけません。
区切り文字等の認識がエンコード前では正しくできないからです。
ならば
8:15+20
そう。
改行コードが正しく認識できません。
じゃ、どうする?
$fh = fopen($path, 'r');
while (($line = fgets($fh)) !== false) {
$line = mb_convert_encoding($line,'UTF-8','SJIS');
$data = str_getcsv($line, ",");
// $data を使う処理
}
fclose($fh);
8:35+10
そこでストリームフィルタです!
$fh = fopen($path, 'r');
stream_filter_append($fh, 'convert.iconv.utf-16le/utf-8',
STREAM_FILTER_READ);
while (($data = fgetcsv($fh, 1000, ",")) !== false) {
// $data を使う処理
}
fclose($fh);
stream_filter_append($fh, 'convert.iconv.cp932/utf-8',
Shift_JIS を読む場合はこう:
8:45+30
SPL編Standard PHP Library (SPL)
(標準で入っているライブラリ)
http://jp1.php.net/manual/ja/book.spl.php
バグ検出用の例外
LogicException
throwされたらバグだという場合の例外。
つまり、これをキャッチするプログラムは不要だし、
運用中には絶対throwされないようにすべき。
バグを発見しやすくするために仕込む例外はコレ。
if ($data == null) {
throw new LogicException(
"dataがnullの場合はこの関数使えません");
}
9:15+30
LogicException の代わりに
下記の関数作っておくのもオススメ。
https://github.com/waterada/phplib-bug_if
9:45+90
イテレータ
通常、foreach で回すには、
全要素をメモリに保持する必要があるし、
要素に対する制御はループの中に書く。
しかし
イテレータを使うと
要素ごとのメモリ読み込みができたり、
要素の制御をループ外に定義できる。
=メモリに優しく、汎用化しやすい
(身近なものでいうと bash コマンドのイメージ)
イテレータ
11:15+15
SplFileObject
ファイルの1行ずつをイテレータとして扱える。
<?php
$ite = new SplFileObject($path);
$ite->setFlags(SplFileObject::READ_CSV);
foreach ($ite as $line) {
echo $line[0];
}
$ite = null; //close
11:30+15
AppendIterator
イテレータ複数を1つのイテレータとして扱える。
サイズが大きくて array_merge では心配なとき良い。
$ite = new AppendIterator();
$ite->append(new ArrayIterator([1,2,3]));
$ite->append(new ArrayIterator([5,6,7]));
foreach ($ite as $a) {
echo $a;
}
//result: 123567
11:45+60
LimitIterator
イテレータのループ範囲を限定する。上位100件とか。
$ite = new LimitIterator(
new ArrayIterator([1,2,3,4,5,6,7] , 2, 4);
foreach ($ite as $a) {
echo $a;
}
//3456
12:45+15
CallbackFilterIterator
イテレータにフィルタを適用する。
$ite = new CallbackFilterIterator(
new ArrayIterator([1,2,3,4,5,6,7]),
function($current, $key, $iterator) {
return $current % 2 == 0;
}
);
foreach ($ite as $a) {
echo $a;
}
//246
13:00+15
Iterator (インターフェイス)
を implements して、
current(), next(), key(), valid(), rewind() を実装すれば
誰でもイテレータの自作が可能。
ちょっと作ってみましょう。
13:15+15
Iterator (例)
class OreOreArrayIterator implements Iterator {
private $array;
private $i;
public function __construct($array) {
$this->array = $array;
$this->i = 0;
}
public function rewind() { $this->i = 0; }
public function next() { $this->i++; }
public function valid() { return ($this->i < count($this->array)); }
public function key() { return $this->i; }
public function current() { return $this->array[$this->i]; }
}
foreach (new OreOreArrayIterator([1,2,3]) as $a) {
echo $a;
}
//123
13:30+60
PHP5.5 から ジェネレータ構文 が
導入されました。
yield って書くやつですね。
これを使うと
簡単に Iterator が作れます。
14:30+60
yield (例)
function generateOreOreArrayIterator($array) {
foreach ($array as $a) {
yield $a;
}
}
foreach (generateOreOreArrayIterator([1,2,3]) as $a) {
echo $a;
}
//123
簡単ですね!
ただし、 yield で作った Iterator は 巻き戻せません(使い捨て)
ので気をつけて。
14:30+60
公式ホームページは宝の山です!!
http://jp1.php.net/manual/ja/
PHPの公式ホームページは
かなり充実しています。
特に 文法 、 配列 、 文字列 、 SPL まわり
は一度ざっと読んでおくと良いでしょう。
判らなかったら公式ホームページ
を見てみましょう!
15:30+15
ぜひ Facebook 等で
寺田渉 まで
ご連絡ください!!
ご清聴ありがとうございました!

PHP基本的関数QUIZ