Page 1
Phanによる
PHPコード静的解析
ެ։ิగ൛
ピクシブ株式会社うさみけんた @tadsan
JAPAN phpcon 2016, 3rd November
公開日:
by USAMI Kenta@tadsan
に東京都大田区蒲田の大田区産業プラザ PiOで開催された『PHPカンファレンス2016』でレギュラーセッション(20分)として発表しました。
ピクシブ株式会社うさみけんた @tadsan
JAPAN phpcon 2016, 3rd November
リファクタリングやテストなどいろいろ!
おしごと
現実に動いている(ユーザーさんが利用している)サービスを破壊せずにコードの健全さを向上したい
そんなノウハウをギュッと詰め込んだ
……型のキャスト,変数とリファレンス
……ドキュメントの生成,問題箇所の発見
……型のキャスト,変数とリファレンス
……ドキュメントの生成,問題箇所の発見
PHPと型
手さぐりで型を調べる (←型検査)
$a = 13; $b = "2";echo $a + $b;
だけど数字だから足しざんできるね!」……みたいな茶番を毎回やってる
文字列(string) 配列(array) リソース(resource)オブジェクト(object) ヌル値(null)
// PHP 5 function trapezoid($h, $a, $b) {return $h * ($a + $b) / 2;}
// PHP 7 function trapezoid(float $h, float $a, float $b): float{return $h * ($a + $b) / 2;}
$a = 13; $b = [];// 環境依存の分岐 (本番環境じゃないと動かない)if (is_production()) {echo $a + $b; // 検出できないecho 1 + []; // ←これはPHP7で検出できるようになった}function f(): int {return []; // これも検出できない}
PHPDoc#とは何か
/** ここはDocCommentだよ */function hoge() {}$ref = new \ReflectionFunction('hoge');echo $ref->getDocComment();// => "/** ここはDocCommentだよ */"
/*** @param float $h* @param float $a* @param float $b* @return float*/function trapezoid($h, $a, $b) {return $h * ($a + $b) / 2;}
PhpStormの型表示
いままで顕在化しなかったバグが発生するおそれ
稼動中のコードの挙動を変更せず検査できる
タグ名
意味
例
@param引数を定義@param int $n1
@return返り値を定義@return int[]
@var変数/プロパティを定義@var int
@propertyマジックプロパティを定義@property int $id
final class Book {/** @var string */public $title;/** @var \MyApp\Author[] */public $authors;/** @var \MyApp\ISBN */public $isbn;}
/*** @property string $title* @property \MyApp\Author[] $authors* @property \MyApp\ISBN $isbn*/final class Book {use \Teto\Object\TypedProperty;protected static $property_types = ['title' => 'string','authors' => 'MyApp\Author[]','isbn' => '?MyApp\ISBN',];}
/** @return Book[]|\ArrayObject */function getBooks() {$data = hogehoge();return new \ArrayObject($data);}$books = getBooks();$books->▍ // ここで \ArrayObject の補完が効くforeach ($books as $book) {$book->▍ // ここで Book の補完が効く}
Phanの紹介
アメリカのEtsy社が開発する静的解析ツールhttps://github.com/etsy/phan
Phanの開発にも参加してる
最新のタグが付いてる.pharファイルをダウンロード
サービスがPHP7で稼動してなくても動かせる
#!/bin/sh/path/to/php7/bin/php ${PHAN_BIN:-/path/to/bin/phan} "$@"
https://github.com/etsy/phan/wiki/Getting-Started#creating-a-config-file
// ディレクトリホワイトリスト'directory_list' => ['src','vendor/symfony/console',],// ディレクトリブラックリスト"exclude_analysis_directory_list" => ['vendor/'],
./phan-wrapper -m csv -o phan.csv -x -j4 \--progress-bar --backward-compatibility-checks
pixiv-lib/Illust/Common.php:738 PhanTypeMismatchArgumentInternal Argument 1 (month) is string but \checkdate() takes int pixiv-lib/Illust/Common.php:738 PhanTypeMismatchArgumentInternal Argument 2 (day) is string but \checkdate() takes int path/to/file.php:333 PhanTypeMismatchArgumentInternal This is message.
// PHP 5 namespace MyApp;try {foo();} catch (RuntimeException $e) {// ↑ \MyApp\RuntimeException} catch (\Excaption $e) {// ↑ \Exception が正しい}
namespace MyApp;$e = get_error();if ($e instanceof RuntimeException) {// ↑ \MyApp\RuntimeException} elseif ($e instanceof \Excaption) {// ↑ \Exception が正しい}
$iter = null;// E_WARNING foreach ($iter as $i) {echo $i;}
/** @return void */function f() {return null;}function g():int {return [];}
/** @return int */function g(){return;}
/** @return void */function g(){ return; }$result = g();
// 代入しない配列 PhanNoopArray[1, 2, 3];// 代入も実行もしないクロージャ PhanNoopClosure function(){ return awesome(); };// returnしていないので無意味 PhanNoopProperty class C {public $p;function f() { $this->p; }}
function newAwesomeFunc(){}/** @deprecated */function oldFunc(){}oldFunc(); // PhpStormは取り消し線で表示
/** @suppress PhanUndeclaredConstant */class C {function f() { return AWESOME_CONSTANT; }/** @supress PhanUndeclaredProperty */function g() { return $this->p; }}
/** @template T1 */class Container {/** @var T1 */private $value;public function __construct($value){ $this->value =$value; }function getValue() { return $value; }}
but insert() is declared to return \成功
http://php.net/manual/ja/language.oop5.overloading.php
型を宣言することができる
リファクタリングのリスクを減らすことができる
@deprecated をつける習慣をつけると殲滅が捗る
