YAPC::Asia 2014 - 半端なPHPDisでPHPerに陰で笑われないためのPerl...
-
Upload
junichi-ishida -
Category
Software
-
view
11.905 -
download
6
description
Transcript of YAPC::Asia 2014 - 半端なPHPDisでPHPerに陰で笑われないためのPerl...
半端なPHPDisでPHPerに陰で笑われないためのPerl Monger向け最新PHP事情
YAPC::Asia 2014
@uzulla
Breaking news
(このタイミングでリリース…)
Meanwhile in YAPC::Europe 2014
I am ...@uzulla
Hachioji.pmPHPer
I ❤️PHP
皆さんに質問PHPを使っている人?
PHPに苦しめられている人?
DISりたくてここにいる人?
Do you know PHP ?
PHP is ... ?• Webに特化• LAMPというありふれた環境(Linux Apache Mysql PHP)
• 初心者でもイケるゆるふわなコード• 「Wordpress」「ECCUBE」「phpMyAdmin」等といった超有名アプリ
PHP is .... ?• レンタルサーバー(レン鯖)
• FTPでデプロイ• 関数のかたまり• レガシーコードがやばい• テンプレートエンジン(笑)
Q: PHPはテンプレートエンジン?A: はいPHP: Hypertext Preprocessor
...<div id="user_name"><?php echo $_GET['name']; ?></div>...
Q: DBつかえるってきいたけど?A: 勿論ですMysql,sqlite,Pgsql,Oracle,Sqlserver ...
traditional PHP code
<div id="user_name"><?php $db = mysql_connect('localhost', 'yay_id', 'yay_pass');
mysql_select_db('yay', $db);
$res = mysql_query("SELECT * FROM user_account WHERE user_id=".$_GET['id']);
$row = mysql_fetch_assoc($res);
echo $row['name'];?></div>
transitional PHP code
<div id="user_name"><?php if(isset($_GET['id'])&&is_string($_GET['id'])){ $id = (int)$_GET['id']; try { $pdo = new \PDO('mysql:host=localhost;dbname=yay;charset=utf8mb4', 'yay_id', 'yay_pass'); $pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION); $stmt = $pdo->prepare('SELECT * FROM user_account WHERE user_id=:user_id'); $stmt->bindValue('user_id', $id, \PDO::PARAM_INT); $sth = $stmt->execute(); $user = $sth->fetch(\PDO::FETCH_ASSOC); if(!empty($user)){ echo htmlspecialchars($user[0]['name'], ENT_QUOTES, "UTF-8"); }else{ echo "oh, i don't know that id."; } $pdo = null; } catch (\PDOException $e) { if(count(ob_get_status())===0){ http_response_code(500); } error_log("db fail ".$e->getMessage()); echo "oh, db fail sorry."; } catch (\Exception $e) { if(count(ob_get_status())===0){ http_response_code(500); } error_log("terrible error".$e->getMessage()); echo "uhoh, sorry."; } finally { // it's joke. }}else{ echo "oh, id is require.";}?></div>
…もう帰りたい?(NAOYAさんの発表が裏ですよ!)
MODERN PHP ?
MODERN PHP (probably)• オブジェクト指向プログラミングを駆使• イケてるライブラリを使う• 「Herokuにデプロイ」• 「継続的テストも必須だよね」• とかなんやかんや、やる感じ個人差有ります
Q: まって、テンプレートエンジンじゃないの?
Q: まって、テンプレートエンジンじゃないの?
A: PHPはクラス、インターフェイス、抽象クラスと基本からはじまり、単一継承ではあるものの
Traitによって柔軟さを確保し、例外機構、Finalizeなどもきちんと存在し、簡単なメタプロもできる程度のマジックメソッドを持ったオブジェクト指向プログラミング対応のテンプレートエ
ンジンです。
A『は?飛躍したぞ?』B「OOPって =& のこと?」C『PEARのことでしょ?』
論よりナントカ百聞は一見にしかずとりあえずウェブアプリケーションを作ってみます。残念ながらライブではありません。
はじめる前に
• Windows > PHPはいってない(XAMPPとかWebmatrixとかで入る)
• Linux > (Cent5以外なら)AptとかYumとか• Mac > 「それマヴェリック?もう5.4はいってるよ!!」
Macの人はすぐにできます
そんなに慌ててPHPを削除しないでください
1,ディレクトリ作ります
2,Composerをいれます。
ワンライナーで入る。ComposerはPerlで言う所のcpanmみたいなパッケージマネージャです。
3,おもむろにWAFをインストールします。
※今後php composer.pharをcomposerとタイプします
4,コードを書く
$ (vim|emacs) index.php
5,PHPでサーバーをたてる
• plackupみたいなの(開発のみに使う)
• PHP>=5.4からhttpdが組み込み• コンソールに色つきログが出て便利• (余談:HTML開発にもつかえる)
6,hello world!
Tadaa!!!できた!!!(Composerを手元で走らせて全部あげれば、世の中の90%(適当)の実行環境であるレンサバでも問題ありません)
繰り返しになりますがMacをお使いの皆さんは• 宗教上の理由• お医者さんに止められている• 家族の薦め、死んだ婆さんの遺言とかないかぎり今すぐ使えますよ!
言い訳はできません
PHPのOOPの話
…の前に、クラスのロード方法• 古代:全部コピペ(そもそも非クラス)
• 昔 :先頭に大量のrequire_once()
• 近代:自前でオートローダーを実装• 現代:PSR-0,4対応のローダー(主にComposerの機能)
(番外 PEARはinclude_path)
Composerのオートローダーの設定composer.json修正→composer update(等)
Composerのオートローダーの利用<?phprequire_once('vendor/autoload.php');
vendor/autoload.phpをrequire_onceするだけ
クラス名、ファイル名規約、PSR-0(または4)• 昔は規約なんてなかった、カオスだった
• Net_Services_Twitterとか…(長い)• 今はネームスペースを使う• ネームスペース例
\Uzulla\My\Library
• それに対応するファイルパス例/path/to/lib/Uzulla/My/Library.php
「PSR」?• PHPの規約、PHP-FIGが策定(PHP本体ではない)http://www.php-fig.org/
• 策定済→ 0,4:Auto load周り1,2:code style系、3:Logger Interface
• 策定中→ 5:PHPDoc、6:Cache Interface
• 世紀末フリーダム世界PHPに秩序を
さっさとOOPはい
こういう感じのファイル配置
(PSR-4だとYapcはショートカットできるけどわかりやすさの為)
コントローラーのベースクラスがこんなんで
Yapc/Controller/Base.php<?phpnamespace Yapc\Controller;use Slim\Slim;
class Base{ protected $slim;
public function __construct(){ $this->slim = Slim::getInstance(); }}
継承したクラスはこんなんです
Yapc/Controller/Index.php<?phpnamespace Yapc\Controller;
class Index extends Base{ public function run(){ $this->slim->render('template/index.twig'); }}
Perlだと?(雰囲気です)
package Index;use Mouse;extends 'Base';
has slim => ( is => 'rw', isa => 'Slim' );
sub run { my ($self) = @_; $self->slim->render('template/index.tt');}
no Mouse;1;
PHPのOOP…これは…
• JavaっぽいのOOP、Blessみたいなのはない• 「OOPの本」とかの例が(ある程度)書ける
つまり怖くない気付いてる人は気付いてると思いますが、PHPはHTMLのテンプレートエンジンですが、PHPのコード内にはHTMLは一切かかず、普通のプログラミング言語のフリをして使うのが今の書き方です。
Q: じゃあHTMLはどこに書くの?A: テンプレートエンジンである所のPHPで書かれた、別な安全なテンプレートエンジンを使うと良いでしょう!!
冗談ではありません。PHPはHTMLの中でいきなりDBにSELECTとかできて強力すぎるし、オートエスケープがないので、正しく安全に使うのは非常に面倒。なので、Twigとかつかうべきでしょう。
Type Hinting
function ( \My\Class $my_class_instance ){
• 引数の「型」っぽくクラス名書ける• クラス名、インターフェイス名、array等• 間違えると例外が上がって安心• 静的解析がはかどる
=>IDEが補完ガンガン(超重要)
でも…残念…
• integer、Stringなどのスカラ型がない• traitもダメ
※PHP.netでRFCにはなってるのでいつかは…(でも「…そうか…」という箇所もある)https://wiki.php.net/rfc/scalar_type_hinting_with_cast
独自例外クラス<?phpnamespace Yapc;class MyException extends \Exception{ public function scream(){ echo "うぎゃ~";
}}
独自例外クラスをキャッチとFinally
<?phptry { if($hoge){ throw new \Exception('汎用'); }
if($huge){ throw new \Yapc\MyException('オレオレ'); }
} catch (\Yapc\MyException $e) { //独自例外クラス $e->scream(); // <--独自例外に生やしたやつ} catch (\Exception $e) { //そのほか echo $e->getMessage();} finally { // PHP>=5.5から使える echo "終わりだ…";
}
SPL
• Standard PHP Library
• 色々な継承に使えるテンプレートがある• イテレータとかイテレータとか• 継承でForeachにわたせるようになるとか• 例外とか例外とか例外とか• 独自例外つくるより選ぶ方が楽
他にも…
• __set(),__get()、__toString()、__clone()等の少々のマジックメソッド
• final指定• trait
(多重継承の代用等)
OOPじゃないけど
• password_hash (PHP>=5.5)「PHPerでも」「将来にわたって使いやすい」「安全な」パスワードハッシュをつくれる
• Generator (PHP>=5.5)巨大ファイルや終わりの不明なデータ処理につかいやすい。
• [1,2] (配列リテラル)(PHP>=5.4)昔はarray(1,2)というダサイ記法だった
OOPじゃないけど2IDEによるリアルタイムLint
Typoや未宣言変数の指摘
クラス名の重複や
未使用変数の指摘
「型」の違う変数の指摘
めっちゃイライラが減る!!!「自分よりPHPStorm(等)が信用できる」
まとめ• PHPはOOPできるようにがんばってる• 例外とかもちゃんとある• 「Javaっぽい?」• IDEの静的解析が果てしなく便利
=> プロはIDEいらないかもですが=> 補完のために自然とクラスを作り出す
PHP5.6-RC4 now-
Released!! Yeah!!
• (割とマニアックな機能追加が多い)• 累乗演算子、GMPの演算子オーバーロード• (constによる)定数宣言で式利用可能に• 可変長引数記法や関数の名前空間、誰得?• 各種制限撤廃は嬉しい(php://input再利用可、ファイルアップロード2GBの天井消滅)
• 「余波で」PHP5.3が終了へ(これは重要)
ライブラリ
最近のライブラリ事情• PEARは消滅の流れ(PHPUnit、AWSなど)• Packagist+Composerが台頭• Packagist.orgのRSSが加速している
=> PHPのライブラリがドンドンふえている!(=>「ACME」ライブラリも多い…)
Packagist ?
• http://packagist.org
• ライブラリのリポジトリ(インデクスのみ)• npm,rubygemsと似てる• Composerから利用される• ホストはGithub 95%(適当体感)、残り
Bitbucketなど• 登録・投稿に一切の審査無し
増え続けるパッケージ(2014/08/25)
• 「36772 packages registered」• 「142437 versions available」
http://www.modulecounts.com/
Packagist監視業務は結構たのしい(つらい)
• よくわからんWAF
• よくわからんORM
• よくわからんルーター• よくわからんテンプレートエンジン• 「砂場では?」「スターを見よう…」• 監視業にはRSSが便利
• Twitter: @call_user_func
監視おじさんのスターリストから• id:uzullaのGithubのスターリストから抜粋• ライブラリやツール• 全部試した訳ではない• 趣味、偏りがあります
• https://github.com/Seldaek/monolog鉄板なLogger
• https://github.com/fabpot/Twig鉄板なテンプレートエンジン
• https://github.com/sebastianbergmann/phpunit鉄板のテストフレームワーク
• https://github.com/fabpot/Goutte要はLWP
• https://github.com/swiftmailer/swiftmailer鉄板のメール送信するやつ
• https://github.com/Halleck45/PhpMetricsコードを静的解析してスコアつけとかするやつ
• https://github.com/squizlabs/PHPCodeSniffer要はPerl::Critic、Perl::Lint
(他にもPHPMD)• https://github.com/fabpot/PHP-CS-Fixerコードフォーマット修正ツール(PHPStormがあるからつかってない…)
• https://github.com/bobthecow/psysh組み込みより高機能なREPL
• https://github.com/Bee-Lab/bowerphpJavascriptのBowerコンパチツール
• https://github.com/indeyets/pake要はmake的なやつ
• https://github.com/rocketeers/rocketeerデプロイツール
• https://github.com/nategood/commandoPHPでCLIプログラム書く時につかうGetOp的ライブラリ
• https://github.com/illuminate/databaseLaravelというWAFのORM部分切り出し
• https://github.com/j4mie/idiorm要はORM
• https://github.com/jpfuentes2/php-activerecord要はActiveRecord(他にもARはある)
• https://github.com/memememomo/php-SQL-Maker要はSQL::Maker
• https://github.com/clue/psocksdSOCKSトンネル、サーバーデーモンの実装のやつ
• https://github.com/bravo3/sshsshクライアント的なやつ
• https://github.com/fpoirotte/psshtssh「サーバー」
• https://github.com/clue/php-wake-on-lan-reactWOL(WakeOnLan)叩くやつ
• https://github.com/reactphp/react「Event-driven, non-blocking I/O with PHP.」
• https://github.com/ratchetphp/RatchetWebSocketサーバー(スタンドアロンでサーバーになります)
• https://github.com/marcj/php-pm「PHP ProcessManager for Request-Response Applications」
• https://github.com/videlalvaro/phacterl「Implementation of The Actor Model in PHP.」「アクターモデル」「マジで!?」
• https://github.com/ircmaxell/PHPPHP「A PHP VM implementation written in PHP.」「PHPでPHP実装した」「マジで?!」
• https://github.com/runekaagaard/snowscript要はPHPのCoffeScript、更新ほぼない
ライブラリまとめ• かなり面白いライブラリやツール増えた• 「これPHPでやるドメインか??」• 「ホントに動くのかこれ??」• Packagistは自分でも気軽に登録できる• 国産ライブラリもっと増えて欲しい
実行環境
「PHPの実行環境は沢山あるんじゃぞ」本家PHP、HHVM、HippyVM、PHP.js、Quercus、Phalanger、pipp、HappyJIT、PHP Compiler (PHC)、Rose、Roadsend PHP、Roadsend PHP: Raven (RPHP)Project_Zero/sMash、talaria Runtime、PHP4Mono、php-llvm、Zend Server
「本当の所は?」実用できるのは以下三つ• 本家PHP
• HHVM
• ZendServer
ZendServerは有料なのでよくわからないけど、プロファイラとか、5.3を今後もサポートとか、そういう強みがある模様
本家の特徴
• 本物である• pecl(c拡張)がつかえる• 安定している• mod_phpがある
HHVMの特徴
• 速い• 速い• なんかカッコイイ• httpd 動作可能• チョイチョイたりない互換性• Hack langつかえる• 速い
Q: どっちをつかうべきでしょうか?
A: 本家です
本家?HHVM?
• どっちか迷う人がHHVMはやめた方が良い• 速度以外のメリットはない(大体倍)• でも、実際のネックはDBとかでしょ?• 「本家でありえない物」も、あまりない(将来永続インスタンスとかでてきたらワンチャンあるで)
• Hack?使ってる所をまだみたことない
「ところでLAMPはオワコン?」「nginx+PHPが今ドキでは?」
Apacheの特徴• htaccessの楽さ
• mod_phpとmod_rewriteの組み合わせはやっぱり柔軟で強力(FastCGIはURIのマップでかゆいところに手が届かないか、コンフィグが複雑で面倒)
• 手間&手離れ
nginxの特徴• 特に遅いモバイル相手で裁ける量が違う• 複数アプリを相乗りしだすと色々面倒(複数のrepoがデプロイされてるとか…)
• 協力会社とお客様のご理解(「なんでhtaccessがないの!?」 「(秘伝のタレ)を置いて下さい」)
• 監視点や監視ログが増える
まとめ• 「PHPらしく」つかうとLAMPしかない事も• Apache+mod_phpの楽さ、柔軟さハンパない• nginx+php-fpmは速度が欲しければ良い• nginx+HHVM(fastCGI)は最悪php-fpmに戻せる
それPHP?
PHPの欠点遅い
本当なの?
• plackで 1000req/s の所 50~300req/sくらい※ 条件によります(雑だw)
• 超々重量級WAFで、一切調整しないと一桁とか
さて、ちょっとこれをみてくれないか
Perl (Plack)
#!/usr/bin/env perluse strict;use warnings;use utf8;use JSON::XS qw(encode_json);my $app = sub { my $env = shift; return [ 200, [ 'Content-Type' => 'application/json' ], [ encode_json({ message => 'Hello, World!' }) ], ];};return $app;
変哲のない生PlackなHelloWorldウェブアプリ
突然ですがベンチマーク• 環境• Vultr (VPS) の下から二つ目のプラン• 1コア (Vultr Virtual CPU 3392Mhz)
• Mem 1024MB、SSD
• CentOS6
Perl 5.20.0start_server --port=5000 --backlog 16384
-- plackup -E production -s Starlet --max-keepalive-reqs 1000 --max-reqs-per-child 50000 --min-reqs-per-child 40000 --max-
worker 1 -a ./helloworld.psgi
ab -n 10000 -c 10
result - perl
Server Software: Plack::Handler::StarletServer Hostname: 127.0.0.1Server Port: 5000
Document Path: /Document Length: 27 bytes
Concurrency Level: 10Time taken for tests: 1.606 secondsComplete requests: 10000Failed requests: 0Write errors: 0Total transferred: 1670000 bytesHTML transferred: 270000 bytesRequests per second: 6225.76 [#/sec] (mean)Time per request: 1.606 [ms] (mean)Time per request: 0.161 [ms] (mean, across all concurrent requests)Transfer rate: 1015.33 [Kbytes/sec] received
result - perl
Concurrency Level: 10Complete requests: 10000Requests per second: 6225.76 [#/sec] (mean)
「我がPerlは6000超え、PHPはどうだね?」『PHPは遅い…でも武器を手に入れたんだぜ!』「なんだと?」『PHPだってサーバーになれるんだ!!』「フッこざかしい、見せてみろ」
PHPの武器libev
php + reactPHP
<?phprequire_once 'vendor/autoload.php';
$app = function (\React\Http\Request $req,\React\Http\Response $res) { $res->writeHead(200, ['Content-Type' => 'application/json']); $res->end(json_encode(['message'=>'Hello World']));};
$loop = React\EventLoop\Factory::create();$socket = new React\Socket\Server($loop);$http = new React\Http\Server($socket);$http->on('request', $app);$socket->listen(5000);$loop->run();
PHP 5.6.0php boot.php
ab -n 10000 -c 10
result - php + reactPHP
Server Software:Server Hostname: 127.0.0.1Server Port: 5000
Document Path: /Document Length: 36 bytes
Concurrency Level: 10Time taken for tests: 3.397 secondsComplete requests: 10000Failed requests: 0Write errors: 0Total transferred: 1420000 bytesHTML transferred: 360000 bytesRequests per second: 2943.53 [#/sec] (mean)Time per request: 3.397 [ms] (mean)Time per request: 0.340 [ms] (mean, across all concurrent requests)Transfer rate: 408.18 [Kbytes/sec] received
result - php + reactPHP
Concurrency Level: 10Complete requests: 10000Requests per second: 2943.53 [#/sec] (mean)
「なんだこの無様な値は」『…こんなハズでは…PHPerに未来はないというのか!?』「ISUC○Nでハブられるような言語がパフォーマンスに口出しするなど10年速い、言語を変えて出直してこい」「いや!言語は変えない…俺はPHPerだ!!」
PHPの最終兵器HHVM
HipHop VM 3.1.0hhvm boot.php
ab -n 10000 -c 10
result - HHVM
Server Software:Server Hostname: 127.0.0.1Server Port: 5000
Document Path: /index.phpDocument Length: 36 bytes
Concurrency Level: 10Time taken for tests: 1.230 secondsComplete requests: 10000Failed requests: 0Write errors: 0Total transferred: 1420000 bytesHTML transferred: 360000 bytesRequests per second: 8132.90 [#/sec] (mean)Time per request: 1.230 [ms] (mean)Time per request: 0.123 [ms] (mean, across all concurrent requests)Transfer rate: 1127.81 [Kbytes/sec] received
result - HHVM
Concurrency Level: 10Complete requests: 10000Requests per second: 8132.90 [#/sec] (mean)
8132.90 [#/sec]
「さっきHHVMはお勧めしないとかいってたじゃねーか!」『手段を選んでいる場合ではなかった!』「貴様には矜持というものはないのか!!!」
『某有名パンダの人も言っていた …ような気がする…んだが、 得意な言語で勝負はきまらないんだ!』
※意味が違う
「…ところで、これはどこで実際につかわれてるんだ?」『これはベンチ番長で、reactPHP(のhttpd)を本番に使う度胸はない』「えっ」『本番は普通にHHVMをhttpdモード(等)でつかう』「えっ」
(※reactPHPのhttpdは機能が全くもって不足(POST周りとかChunckedとか))
HHVM httpd
hhvm -p 5000 -m s
ab -n 10000 -c 10
index.php<?phpheader('Content-Type: application/json');echo json_encode(["message" => 'Hello, World!']);
HHVM httpd result
Server Software:Server Hostname: 127.0.0.1Server Port: 5000
Document Path: /index.phpDocument Length: 27 bytes
Concurrency Level: 10Time taken for tests: 1.704 secondsComplete requests: 10000Failed requests: 0Write errors: 0Total transferred: 1240000 bytesHTML transferred: 270000 bytesRequests per second: 5868.96 [#/sec] (mean)Time per request: 1.704 [ms] (mean)Time per request: 0.170 [ms] (mean, across all concurrent requests)Transfer rate: 710.69 [Kbytes/sec] received
HHVM httpd result
Concurrency Level: 10Complete requests: 10000Requests per second: 5868.96 #/sec
「6225.76と5868.96で、負けとるやんけ」『普通にやったんじゃPerlには勝てなかったよ…』
• 『PHPは(それなりに)速いですが、リクエスト毎に全てをディスポーズするイミュータブル世界観による限界が…』
• 「それこそがPHPの特徴だろ?」• 『php-pm(reactPHPをワーカにしたプロセスマネージャ)に夢を感じる』
• こういう話好きな人友達になってください。
まとめ
• VPS&ローカルab => 計測誤差あるやろ• jsonのエンコードベンチ => マシなベンチを• 「実用的なものでなく、ベンチ番長で勝ってなんの意義があるのか?」『チャレンジしたい(という強い心)』
• 実は「kazeburoパラメタ」をオフにすると勝ってた(5000くらいになる)(start_server --port=5000 --backlog 16384 -- plackup -E production -s Starlet -a ./hw.psgi)http://www.slideshare.net/kazeburo/yapcasia-2014lttfb(Max-workerは1コアなので効果無かった)
• (上のLTの後で良かった、危ない)• kazeburoさんには勝てなかったよ…(違
それPHP?↓
それはもはやPHP(テンプレートエンジン)ではない
まとめのまとめ• PHPはWEB向けなら手軽です• 今なら道具もそろっている• 今風の書き方もできる• 皆さんなら、いいPHPコードが書けるでしょう• (ダークサイドにおちなければよい)• 「それなりな」速度出るぞ• ズル(?)すればさらに上も狙える
さて、こんなPHPをおぼえたいなら?(経験者向け)
php the right way
http://ja.phptherightway.com/
安心の日本語翻訳です。ここをよめばとりあえず最初迷わないでしょう。PerlMongerの人はこれだけ覚えて帰って下さい。(ちなみに、PHPの情報を集めるなら、できれば海外の物が良いでしょう…まあ、海外もスットコな情報多いですが)
基本的に以上ですが…時間はあまっている?
One more thing「s」または質問タイム
ーーーーーーーーーーーYAPC登壇者3人+パーフェクトRuby執筆者1人が書いた「Webアプリケーションエンジニア養成読本」好評発売中!
ゆるふわ暗黒PHP
「こんなの暗黒か?PHPerは息をするように避けてるぞ」と思いますけど、この間あるPerlMongerに話したらウケた内容多めです。
自動変換の闇
有名なPHPの自動変換ですね。true == "1" // truetrue == "0" // falsetrue == "-1" // truetrue == "" // false
皆様『PHPクソだな^^』
まあ自動変換つかわなきゃいいtrue === "1" // falsetrue === "0" // falsetrue === "-1" // falsetrue === "" // false
true === true // true"1" === "1" // true
※ しかし、例えばin_array()とか既存関数内のマッチは==が…(ウウウゥ~!!)
「そもそも1がtrueとかありえないでしょwワロスw」
『それ絶対JSON::xx::true周りの事知ってて言ってるで
しょ!!!』
閑話休題0+"1" => int(1)0+"a1" => int(0)0+"1a" => int(1)0+".1" => double(0.1)0+"1." => double(1);
PHPは文字列を数値にキャストするとき、先頭にある数字的な文字だけを拾う…まあギリギリ?
…だけとおもいきや。
これだ。0+"0e" => int(0)0+"0e0" => double(0) // <-- ???0+"2e2" => double(200) // <--!?0+"0XF" => int(15) // <--!?
数字の後にeやxがくると別の数値表現として解釈
「ウーーッ!!」(突如泡を吹いて倒れるPerlMonger)
はい
自動変換とは違うけどシンタックスが…な件
print "a" . 1; // => "a1"
…まあ普通?しかし…
print "a" . 1; // => "a1"!print "a".1; // Syntax Error
「突然のエラー」『冗談でしょう!?』
"a" .1; // NG(Syntax Error) "a". 1; // OK => "a1"
『ハァ!?』「ほら、.1は数値リテラルだから…」『なるほど!?いやダメでしょこれ!?』
現在のみなさんの脳内「PHP最悪ですね!^^」
「かかったな!」
Perlも.1が「先頭だと」0.1あつかい$ perl -e "print .1;"0.1
余談ですが...
#!perl@a = (1);print (1 .@a); # ok ==> 11print (1.@a); # NG
まあ、言語って難しいですね。(別にどうという話ではない)
はい
いわゆるsplitの闇
"1234" => ["1","2","3","4"]
としたい、よくありますね。PHPでこれはどうやるか?
最初にみつかるexplode()
文字列を文字列により分割する例: explode("", "1234");
=> エラーになる、空文字不許可
残念
それstr_split()
— 文字列を配列に変換する例: str_split("1234");
=> OKとおもいきやマルチバイト非対応
残念
(ちょっと詳しい人なら)
『えっ、mb_str_split()あるんでしょ?』「無い!!!」
mb_split() はどうか?
マルチバイト文字列を正規表現により分割するphp> var_dump(mb_split('', '1234'));array(1) { [0] => string(4) "1234"}
=> ダメ。しかもeregっぽいし…
残念
php.netを見ると…
アホか!!!!!
「使い物にならんものばっかりや…」
正解 preg_split()
正規表現で文字列を分割するpreg_split('//u', "あいう", -1, PREG_SPLIT_NO_EMPTY)
※//uはUnicode扱うやつです。
php > var_dump(preg_split('//u', "あいう", -1, PREG_SPLIT_NO_EMPTY));
array(3) { [0] => string(3) "あ"
[1] => string(3) "い"
[2] => string(3) "う"
}
php > var_dump(preg_split('//u', ""/*空文字*/, -1, PREG_SPLIT_NO_EMPTY));
array(0) {}
期待通りの結果です!
「…なんかながくね?うしろのいらなくね?」
外すと奇行に走り出す…php > var_dump( preg_split('//u', "あいう") );
array(5) { [0] => string(0) "" // < -- なにこれ [1] => string(3) "あ"
[2] => string(3) "い"
[3] => string(3) "う"
[4] => string(0) "" // < -- なにこれ}
php > var_dump( preg_split('//u', ""/*空文字*/ ) );
array(2) { [0] => string(0) "" // < -- なにこれ [1] => string(0) "" // < -- なにこれ}
なので -1, PREG_SPLIT_NO_EMPTYは必要です…
他の言語ならめっちゃ短くかけるのに、どうしてこうなった??
「オプションの長さなら、json_encodeも負けてないぞ!」<?phpjson_encode($data, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_AMP | JSON_HEX_QUOT | JSON_BIGINT_AS_STRING | JSON_PRETTY_PRINT );
はい
返値の謎
PHPにも返値はあります。本当です。※ CLI、Cronとかで実行するときとかちゃんと見ますよね?例die();exit(1);
安心してください、0と1が返ります。※ 二つはエイリアスです
実例$ cat exit.php<?phpexit(1);
$ php exit.php
$ echo $?1
正気ですね。
exit("1");
$ cat exit.php<?phpexit("1");
$ php exit.php1 < --- なにこいつ?
$ echo $?0
exit(true);(まあこんなのやらんけど)
$ cat exit.php<?phpexit(true);
$ php exit.php1 < --- ウワァァァッァァァァ!?!?!
$ echo $?0
( 単にboolのtrueをintでキャストすると1になるだけなんですけどね(多分) )
はい
PHP.netの闇
「http://php.netよくできてる」と、比較的多くの方から言われますね。
しかし「User Contributed Notes」は参考にしてはならない常識
ところで最近PHP.netがリニューアルされて少し対策されました。
「評価低いやつ、色が薄くなってる…」
はい
まとめ• 以上、氷山の一角でした。• 「危険PHP」を(訓練された)PHPerは書かない• 「安全PHP」書けばいい!• 疑ってかかれば大丈夫
• preg_splitとかは暗記すればいい
皆様『覚えたくないよ!』私「はい」
はい
異常でした
以上ですどうですか?ゆるふわ暗黒話で、最初の方を全部忘れている人がいたら悲しいですね。これを機会に一人でもPHPを使って見たいと思って頂ければ幸いです。トークの投票が好評なら来年もがんばります(?)
ご静聴有り難うございました
きたれ! Hachioji.pmPerl,PHP,Javasript,Java(スマホ)等
http://hachiojipm.org/ や @hachiojipm
質問ありますか?
YAPC登壇者3人+パーフェクトRuby執筆者1人が書いた「Webアプリケーションエンジニア養成読本」好評発売中!
おまけ(誰も質問とかしてくれなくて、はやく終わったらやる)
余談:namespaceが\が区切り文字ということは…
<?phpnamespace ¥\¥{ class ¥{ static public function ¥(){ echo "\\ is not ¥".PHP_EOL; } }}
namespace { \¥\¥\¥::¥();}
こういうコードが可能なので…
Meanwhile in windows
Windowsの一部のフォントはツライかもしれませんね!(こんなの書く人いないでしょうけど)
関数名変数名に寿司もビアもいけるぞ!
PHPたのしいですね!!
エラー処理の闇というか仕様
PHPの「エラー」
• (スタートアップエラー)• 実行時エラーよく画面にでてるNoticeとかFATALとかのやつ
• 例外
(※ E_USER等のエラーの話はしません)
実行時エラーの闇
• よくPHPサイトで出てくるエラーの事です• 画面に出す/出さない設定が可能
=> エラーログには出る• エラーレベルの設定も可能• NOTICEやDEPRECATED等は終了しない
=> (意図的に無視するPHPerの存在)• ERROR、PARSEなどは強制終了される
ゆるふわPHPerの例
• 「エラー消せました!^^シェアします!」
• 「@」やerror_reportingでエラーレベルを変更してエラーを握りつぶす
• display_errorsで「画面から隠す」=> エラーログが数ギガとかになる&見ない
そう言う方々は無視するにせよ、PHPにはPerlのuse strict、use warningsが無いのが不便…(軽いエラーでも終了してくれる)
無いならつくろ!!
set_error_handler( function ($errno, $errstr, $errfile, $errline){ echo("エラーだ~!ボク死ぬね^^!");
exit; });
エラーがきたら => 死ぬ
※適切に引数を表示したりerror_logしたりすると良いでしょう;
エラーハンドリングの仕様の話
• 前述の通り、自前のエラーハンドリング関数がは登録できる
• が、場所によって頻繁に差し替えるのはダルい=> ログや、詳細を抑制位にしかつかいづらい
• 例外みたいにつかいたいよーー=> 例外に変換しよう!!
例外に変換だ!!!
set_error_handler( function($errno, $errstr, $errfile, $errline){ throw new ErrorException( $errstr, $errno, 0, $errfile, $errline ); });
「ヤッタッ!これで例外に統一できる!!」
これで完璧…?
• 自前エラーハンドリング関数は作れた• 例外に変換とかもできた• 「これで制覇した??!!」• んなわきゃーない
残念仕様でてきたぞー^^
• FATAL(関数無いとか)やPARSEなどの強いエラーはトラップできない
• ログもエラーメッセージを出す事すらできない=> 「白い画面」がでてお客さんがおこ!!!=> そして闇っぽい解決策へ…
闇っぽい解決策
• register_shutdown_function()関数登録するとスクリプト終了時にコール
• FATALなエラーで死ぬ時も呼ばれる• そこで「最後のエラーコード」を見て、エラーっぽいならエラー処理をする
• 「なにをいってるんだこいつは??」
例
register_shutdown_function( function(){ // <-- スクリプト終了時に必ず呼ばれる $e = error_get_last(); // <-- 前のエラーをひろって if( $e['type'] == E_ERROR || // <-- 内容を見てよしなに…
$e['type'] == E_PARSE || // NOTICEとかは抜いてます $e['type'] == E_CORE_ERROR || $e['type'] == E_COMPILE_ERROR || $e['type'] == E_USER_ERROR ){ echo "致命的なエラーが発生しました。\n";
error_log("ERROR:". "/t:{$e['type']}/m:{$e['message']}". "/f:{$e['file']}/l:{$e['line']}"); } });
※正常終了時も無駄に呼ばれます
やってられるか!!!• といいつつやる、結局定型だし。• PHPのエラー処理を真面目に書くのはしんどい• そもそも、Fatalとか自分のコードが悪い• そもそも例外に統一してほしい…
• エラーハンドラでFATALも扱いたい…
はい
パターンマッチの闇ともいいづらい闇
「/(.*)-(.*)/ で "2014-8"をキャプチャしたい」
/(.)-(.)/ < よくある
preg_match()をつかえばよい
php > var_dump( preg_match("/(.*)-(.*)/u", "2014-8", $_));int(1)
php > var_dump( $_ );array(3) { [0] => string(6) "2014-8" [1] => string(4) "2014" [2] => string(1) "8"}
$_がPerlの$+{~}みたいなわけですねまあよさそう
でもダメかもしれない存在しない$_をいきなり渡してる。(phpにmy(宣言)は無い)preg_match( "/(?year.*)-(?month.*)/u", "2014-8", $_ /*<--こいつ*/
)
ちなみにE_NOTICEではない。でもちょっと前の静的解析ツールは怒る。
よくある折衷案php > $_ = []; // <--非常に無駄な空配列代入php > var_dump( preg_match("/(.*)-(.*)/u", "2014-8", $_));int(1)
php > var_dump( $_ );array(3) { [0] => string(6) "2014-8" [1] => string(4) "2014" [2] => string(1) "8"}
ダサい
はい