Upload
hisateru-tanaka
View
6.318
Download
3
Embed Size (px)
DESCRIPTION
2011/11/24の関西PHP勉強会で発表したPHP5.4ネタです。
Citation preview
PHP5.4おいしさつまみぐい
@tanakahisateru
ABOUT ME
•田中久輝 (たなかひさてる)
• @tanakahisateru
• https://github.com/tanakahisateru
• Firebug, FireCookie, jEdit
• ...and Pinoco
今回はオチなしです。
今日やること
• PHP5.4をすぐに使ってみる
• Array Short Syntax
• Built-in Server
• Trait
• Closure
•その他
注: 全機能はやりません。僕がやりたいところだけします。
PHP5.4の機能をざっと知りたいときは
Grahamさん( @predominant )の作ったスライドを見ると良いです :)
http://tipshare.info/view/4ec326d04b2122ce49000000
PHP5.4をすぐに使ってみる
祝 PHP 5.4 RC1 (2011/11/11)
• http://www.php.net/archive/2011.php#id2011-11-11-1
現状のPHP環境はそのままで、PHP 5.4 を試す
http://www.1x1.jp/blog/2011/06/
try_new_php_without_update_current_version.html
「展開して、configureして、makeします。」ってこの人が言ってます。
要るもの
• Windows
• http://windows.php.net/qa/ にバイナリがあるのでいけると思う。自力ビルドは茨の道すぎるのでやめたほうがいいよ。
• Mac
• XCode + MacPorts or Homebrew
• Linux
• とくに何も
% curl -o php-5.4.0RC1.tar.gz http://downloads.php.net/stas/php-5.4.0RC1.tar.gz% tar xzf php-5.4.0RC1.tar.gz% cd php-5.4.0RC1% ./configure \--prefix=/opt/local/php/5.4 \--bindir=/opt/local/bin \--with-config-file-path=/opt/local/php/5.4/etc \--with-config-file-scan-dir=/opt/local/php/5.4/var/db \--mandir=/opt/local/php/5.4/share/man \--infodir=/opt/local/php/5.4/share/info \--program-suffix=-5.4 \--with-apxs2=/opt/local/apache2/bin/apxs \(あとは https://gist.github.com/1344162 に書いてます)% make
なんか大量に進行状況出るのでお菓子でも食べて待ちましょう。
できあがりインストール(make install)しなくても
sapi/cli/phpを使ってだいぶ遊べます。
うまくいかない人はconfigureオプションを減らしていくといいです。無指定だと拡張機能がないだけで動くPHPにはなります。
% sapi/cli/php -vPHP 5.4.0RC1 (cli) (built: Nov 23 2011 23:08:40) Copyright (c) 1997-2011 The PHP GroupZend Engine v2.4.0, Copyright (c) 1998-2011 Zend Technologies
% sapi/cli/php -aInteractive shell
php >
バージョンを確認
さっそく遊んでみる
php > echo “Hello World\n”;Hello World
php > print_r(array_map(function($x){ return $x * 2; }, range(0,9)));Array( [0] => 0 [1] => 2 [2] => 4 [3] => 6 [4] => 8 [5] => 10 [6] => 12 [7] => 14 [8] => 16 [9] => 18)
php > echo 0xff == 0b11111111, "\n";1
新機能: 2進数リテラル
ARRAY SHORT SYNTAX
array(1, 2, 3)
[1, 2, 3]
array(‘a’=>1, ‘b’=>2)
[‘a’=>1, ‘b’=>2]
この人が最初に言いました
https://wiki.php.net/rfc/shortsyntaxforarrays
@rsky
var $belongsTo = array(! 'User');var $hasMany = array( 'Photo' => array( 'order' => 'number' ));
var $belongsTo = [! 'User']; var $hasMany = [ 'Photo' => [ 'order' => 'number' ]];
$this->render('list', array( 'posts' => Post::find(array( 'limit' => Config::get('postsPerPage', array('context'=>'blog', 'default'=>10)), ))));
$this->render('list', [ 'posts' => Post::find([ 'limit' => Config::get('postsPerPage', ['context'=>'blog', 'default'=>10]), ])]);
どっちが読みやすい?
「どっちも読みにくいわ」
「すんません」
$this->render('list', array( 'posts' => Post::find(array( 'limit' => Config::get('postsPerPage', array('context'=>'blog', 'default'=>10)), ))));
$this->render('list', [ 'posts' => Post::find([ 'limit' => Config::get('postsPerPage', ['context'=>'blog', 'default'=>10]), ])]);
気をとり直して
$this->render('list', array( 'posts' => Post::find(array( 'limit' => Config::get('postsPerPage', array('context'=>'blog', 'default'=>10)), ))));
$this->render('list', [ 'posts' => Post::find([ 'limit' => Config::get('postsPerPage', ['context'=>'blog', 'default'=>10]), ])]);
関数のネストではなくArrayを引数にしてるって見た目でわかる。
$this->render('list', array( 'posts' => Post::find(array( 'limit' => Config::get('postsPerPage', array('context'=>'blog', 'default'=>10)), ))));
$this->render('list', [ 'posts' => Post::find([ 'limit' => Config::get('postsPerPage', ['context'=>'blog', 'default'=>10]), ])]);
[ ... ] という囲みのまとまりの良さ
ARRAY SHORT SYNTAX
•右手だけの2キーストローク→楽チン
•関数の引数の括弧とは明らかに違うように見える。
• PHP以外の言語の人にもわかってもらえる。
•最後の引数にArrayで動作オプションを渡す関数がもっと使いやすくなる。
ARRAY SHORT SYNTAX
•「PHPのarrayはダサいから設定ファイルみんなYAMLにしようぜ」なんて言わないでね。
•とPHPの中の人が言ってるような気がします。
BUILT-IN SERVER
コマンドラインのPHPがローカルテスト用のWebサーバになる
% sapi/cli/php -S localhost:8080
デモ
どこがおいしいのか
• JavascriptやFlashには、file:// では期待どおり動かないAPIがある。→ フロントの人がPHP5.4でダミーサーバを書ける。
•手元のApacheにいちいちバーチャルホストを立てるのが面倒なぐらいのスポット仕事に。
•ダウンロードしたPHPのアプリケーションをすぐその場で動かせ、要らなければ捨てるだけ。インストールの残骸がシステムに残る心配がない。
いまどき .htaccess に mod_rewrite 書けないと
% sapi/cli/php -S localhost:8080 builtin-server.php
PHPのプログラムコードでそれ再現できるよ
ビルトインサーバ用スクリプトlist($path, $param) = array_merge( preg_split('/\?/', $_SERVER['REQUEST_URI'], 2), ['', '']);if($path != '/' && (file_exists('app/webroot' . $path))){ header(sprintf('Location: http://%s/app/webroot%s', $_SERVER['HTTP_HOST'], $_SERVER['REQUEST_URI'])); exit;}else if($path != '/' && (file_exists('./' . $path))){ return false;}else{ $_SERVER['PATH_INFO'] = $path; require 'app/webroot/index.php';}
% ~/php54/php-5.4.0RC1/sapi/cli/php -S localhost:8080 builtin-server.php
PHP 5.4.0RC1 Development Server started at Thu Nov 24 02:11:37 2011Listening on localhost:8080Document root is /Users/tanakahisateru/Sites/cakephp2Press Ctrl-C to quit.[Thu Nov 24 02:11:42 2011] ::1:63556 [200]: /app/webroot/css/cake.generic.css[Thu Nov 24 02:11:42 2011] ::1:63557 [200]: /app/webroot/img/cake.power.gif[Thu Nov 24 02:11:42 2011] ::1:63558 [200]: /app/webroot/img/cake.icon.png[Thu Nov 24 02:11:42 2011] ::1:63564 [200]: /app/webroot/favicon.ico
% ~/php54/php-5.4.0RC1/ sapi/cli/php -c ~/php54/php-5.4.0RC1/ -S localhost:8080 builtin-server.php
[Pdo_mysql]pdo_mysql.default_socket=/opt/local/var/run/mysql5/mysqld.sock
~/php54/php-5.4.0RC1/php.ini
厳密には MacPorts の MySQL との相性が問題で実行時に php.ini を指定しました。
でMySQLがやっと動いたんだけどね。
BUILT-IN SERVER
•ビルトインサーバ用のスクリプトを書ければ、URLリライトするフレームワークでもOK。
• Apacheの設定なしでもブラウザで動かして試せる。
•手持ちの開発環境がPHP5.3以下準拠でも、ビルトインサーバを意識したプロジェクトなら、Apacheのmod_phpを書き換えずにPHP5.4を試せる。
TRAIT
おまたせしました注目度NO.1の新機能
TRAIT
•親クラス以外からもメソッド実装を持ってこれる。
• Rubyでいうとmixinに相当する。
•ただしPHPのトレイトは、型階層(instanceofの結果)には影響しない。
•型の追加はインターフェースに任せる。実装の追加はトレイトが担う。
つまりどういうこと?
コード分割その1
INCLUDE / REQUIRE
•外部ファイルに関数やクラスを分けて書ける。
• HTMLを分けて書き、外部ファイルで出力処理。
•ただし使える場所は「クラスの外」と「関数/メソッドの中」のみ。
コード分割その2
継承• ある程度基本的な機能を持ったクラスをもとにして、足りない部分だけ実装する。
• 実用性を意識するとすぐに、オブジェクト指向の「~は~の一種」モデルが実際のありさまに準拠せず、システム主導になってしまう。
• class AppModel extends Modelclass GuestUser extends AppModelclass AdminUser extends AppModelって、これ本当は抽象的なUserに汎化されるべきだよね。
class AppModel extends Model {}
class GuestUser extends AppModel { public function getDisplayLabel() { ...; }}
class AdminUser extends AppModel { public function getDisplayLabel() { ...; } public function getAdminRioleType() { ...; }}
コピペ実装だめーっ!!
class AppModel extends Model { public function getDisplayLabel() { ...; }}
class GuestUser extends AppModel {}
class AdminUser extends AppModel { public function getAdminRioleType() { ...; }}
これでいいのかな
class AppModel extends Model { public function getDisplayLabel() { return $this->username . “さん”; }}
class GuestUser extends AppModel {}
class AdminUser extends AppModel { public function getAdminRoleType() { ...; }}
class Comment extends AppModel { // username フィールド持ってないよ。}
←だめじゃん
public function getDisplayLabel() { return $this->username . “さん”; }
class GuestUser extends AppModel { require ‘UserModel.inc’;}
class AdminUser extends AppModel { require ‘UserModel.inc’; public function getAdminRoleType() { ...; }}
class Comment extends AppModel {}
UserModel.inc
こういうことがしたいんだけどなぁ...
require書ける場所 =「クラスの外」orz
trait UserModel { public function getDisplayLabel() { return $this->username . “さん”; }}
class GuestUser extends AppModel { use UserModel;}
class AdminUser extends AppModel { use UserModel; public function getAdminRoleType() { ...; }}
class Comment extends AppModel {}
できるようになりました!
トレイトの継承や名前の変更なんかもできるんだけど今日は説明省略。
trait PersistentModel { public function save() { }}
abstract class User { public function getDisplayLabel() { return $this->username . “さん”; }}
class GuestUser extends User inplements Persistence { use PersistentModel;}
class AdminUser extends User inplements Persistence { use PersistentModel; public function getAdminRoleType() { ...; }}
本来はオブジェクト指向ってこうだよね
主=意味従=機能
PHP5.4世代になって、そんなO/Rマッパーが登場したらいいなあ
既存のフレームワークでもたぶん有効。少なくとも、似たモデル間、似たコントローラ間でコピペプログラミングせざるをえない状況を
だいぶ減らしてくれるはず。
CLOSURE
密かに超強化されてます
CALLABLEの整合性
これまで
php > $fun = 'intval';php > echo call_user_func($fun, "0001abc"), "\n";1php > echo is_callable($fun), "\n";1php > echo is_string($fun), "\n";1
php > echo $fun("0001abc"), "\n";1
call_user_funcしなくても関数を名前で呼べた
これからはさらに
php > $obj = new Exception('hoge');php > $msg = [$obj, 'getMessage'];php > echo call_user_func($msg), "\n";hogephp > echo is_callable($msg), "\n";1php > echo is_array($msg), "\n";1
php > echo $msg(), "\n";hoge
call_user_funcしなくてもメソッドをarrayで呼べる
stringもarrayも見た目はクロージャとそっくり。↓
is_callableでありさえすれば同じように扱える。クロージャがなければ関数名やArrayでも代替可能。
php > echo $closure(), "\n"; // クロージャの使い方
$THIS IN CLOSURE
PHP5.3ではこれエラー<?phpclass CounterFactory { function __construct($init) { $this->init = $init; } function createCounter() { $c = 0; return function() use(&$c) { return $this->init + ($c++); }; }}$fact = new CounterFactory(100);$c1 = $fact->createCounter();$c2 = $fact->createCounter();echo "c1:", $c1(), "\n"; echo "c1:", $c1(), "\n";echo "c2:", $c2(), "\n"; echo "c2:", $c2(), "\n";echo "c2:", $c2(), "\n"; echo "c1:", $c1(), "\n";
PHP5.3での回避策<?phpclass CounterFactory { function __construct($init) { $this->init = $init; } function createCounter() { $c = 0; $self = $this; return function() use($self, &$c) { return $self->init + ($c++); }; }}$fact = new CounterFactory(100);$c1 = $fact->createCounter();$c2 = $fact->createCounter();echo "c1:", $c1(), "\n"; echo "c1:", $c1(), "\n";echo "c2:", $c2(), "\n"; echo "c2:", $c2(), "\n";echo "c2:", $c2(), "\n"; echo "c1:", $c1(), "\n";
明示的なself → Pythonかと
だがPHP5.4ではこれOK!<?phpclass CounterFactory { function __construct($init) { $this->init = $init; } function createCounter() { $c = 0; return function() use(&$c) { return $this->init + ($c++); }; }}$fact = new CounterFactory(100);$c1 = $fact->createCounter();$c2 = $fact->createCounter();echo "c1:", $c1(), "\n"; echo "c1:", $c1(), "\n";echo "c2:", $c2(), "\n"; echo "c2:", $c2(), "\n";echo "c2:", $c2(), "\n"; echo "c1:", $c1(), "\n";
<?phpclass CounterFactory { function __construct($init) { $this->init = $init; } function createCounter() { $c = 0; return function() use(&$c) { return $this->init + ($c++); }; }}$fact = new CounterFactory(100);$c1 = $fact->createCounter();$c2 = $fact->createCounter();echo "c1:", $c1(), "\n"; echo "c1:", $c1(), "\n";echo "c2:", $c2(), "\n"; echo "c2:", $c2(), "\n";echo "c2:", $c2(), "\n"; echo "c1:", $c1(), "\n";
c1:100 <-- init=100 + c=0c1:101 <-- init=100 + c=1c2:100 <-- init=100 + c=0c2:101 <-- init=100 + c=1c2:102 <-- init=100 + c=2c1:102 <-- init=100 + c=2
もっとJavascriptみたいなことができる
クロージャの$THISすり替え<?phpclass CounterFactory { function __construct($init) { $this->init = $init; } function createCounter() { $c = 0; return function() use(&$c) { return $this->init + ($c++); }; }}$fact = new CounterFactory(100);$c1 = $fact->createCounter();echo "c1:", $c1(), "\n";echo "c1:", $c1(), "\n";$c1 = $c1->bindTo(new CounterFactory(1000));echo "c1:", $c1(), "\n";
<?phpclass CounterFactory { function __construct($init) { $this->init = $init; } function createCounter() { $c = 0; return function() use(&$c) { return $this->init + ($c++); }; }}$fact = new CounterFactory(100);$c1 = $fact->createCounter();echo "c1:", $c1(), "\n";echo "c1:", $c1(), "\n";$c1 = $c1->bindTo(new CounterFactory(1000));echo "c1:", $c1(), "\n";
c1:100 <-- init=100 + c=0c1:101 <-- init=100 + c=1c1:1002 <-- init=1000 + c=2
CLOSURE
• PHP5.4のクロージャはもうcreate_functionの代わりではない。
• Javascriptのようにthisが文脈によって変化しうる。
• クロージャの$thisは生成されたとき$thisだったものを引き継ぐ。 これはあくまで自然な挙動をデフォルトにしているにすぎない。
• それでも、束縛しているローカル変数は再定義できない。→なのでPHP5.3の$selfによる回避策はbindToに対応できない。PHP5.4
ではむしろ、クロージャの中で積極的に$thisを使っておくべき。
ぶっちゃけ<?phpclass Hoge { function __construct($init) { $this->init = $init; } function x($n) { $tmp = []; for($i=0; $i<$n; $i++) { $tmp[] = $this->init; } return $tmp; }}
print_r((new Hoge(100))->x(3));
<?phpclass Hoge { function __construct($init) { $this->init = $init; } function x($n) { return array_map( function() { return $this->init; }, range(0,$n-1) ); }}
print_r((new Hoge(100))->x(3));
どっちの方法でも、やりたいことの本質にまったく同じコードを書けるのが嬉しい。
その他
より言語っぽくなった
• newしたインスタンスのメソッドを呼べる
•関数が返したarrayの要素を取り出せる
当たり前なんじゃ?
echo (new Exception("hoge"))->getMessage(), "\n";
PHP5.3以下でエラーPHP5.4ではOK
echo range(0, 9)[5], “\n”;
PHP5.3以下でエラーPHP5.4ではOK
php > echo (array(1, 2, 3))[0], "\n";Parse error: syntax error, unexpected '[', expecting ',' or ';' in php shell code on line 1
php > echo (function($x){ return $x * 2; })(10), "\n";Parse error: syntax error, unexpected '(', expecting ',' or ';' in php shell code on line 1
PHP5.4.0RC1でもやっぱりエラー
期待どおりじゃない点はいつもご期待通りだぜ。
<?= が常にオンいえね、だいぶ古い話なんですけど
「<?= これ使っていいのは小学生までだよね」
的な、はてブコメント食らったことがありまして。 敵は討ったぞざまあみろ。
PHP5.4はゆるやかな変更
• PHP5.3の目玉新機能は、使うか使わないかで別世界になってしまう。5.2以前に戻る方法がない。
•名前空間
• Phar
• PHP5.4の新機能は、便利なところからちょっとづつ使えばいいものばかり。
以上、自分がやりたいことだけ
やりました。
皆さんも既存の環境は変えず、PHP5.4のコマンドラインとビルトインサーバで
「自分のやりたいことだけ」をつまみ食いしてください。