Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

PECL operator で演算子オーバーロード

534 views

Published on

PHP カンファレンス福岡の前夜祭 (非公式) で発表したものです。operator 拡張モジュールで演算子オーバーロードを実現する方法、実現方式について説明しました。別のアプローチとして、zend_object_handlers を変更する方法にも簡単に触れています。

Published in: Technology
  • Be the first to comment

  • Be the first to like this

PECL operator で演算子オーバーロード

  1. 1. PECL operator で演算子オーバーロード 内山 雄司 (@y__uti) 2018-06-15 PHPカンファレンス福岡 非公式前夜祭
  2. 2. 自己紹介 内山 雄司 (@y__uti) ◦ http://y-uti.hatenablog.jp/ (phpusers-ja) ◦ 東京方面から来ました 仕事 ◦ 受託開発の会社 (株式会社ピコラボ) でプログラマをしています 興味 ◦ プログラミング言語処理系 ◦ 機械学習 2018-06-15 PHPカンファレンス福岡 非公式前夜祭 2
  3. 3. きっかけ 今年の 1 月頃に Stack Overflow に投稿された質問 2018-06-15 PHPカンファレンス福岡 非公式前夜祭 3 https://stackoverflow.com/questions/48270127/can-a-1-a-2-a-3-ever-evaluate-to-true
  4. 4. 解答例 toString をオーバーライドする方法 2018-06-15 PHPカンファレンス福岡 非公式前夜祭 4
  5. 5. 解答例 with を使って getter を定義する方法 2018-06-15 PHPカンファレンス福岡 非公式前夜祭 5
  6. 6. 解答例 「見えない文字」を変数名に含める方法 2018-06-15 PHPカンファレンス福岡 非公式前夜祭 6 ◦ 3 箇所の a はすべて異なる変数 ◦ HALFWIDTH HANGUL FILLER (U+FFA0) だそうです
  7. 7. 解答例 == 演算子をオーバーロードする方法 (Ruby, Python での解答) 2018-06-15 PHPカンファレンス福岡 非公式前夜祭 7 Ruby Python
  8. 8. 我らが PHP では? 2018-06-15 PHPカンファレンス福岡 非公式前夜祭 8 Can ($a==1 && $a==2 && $a==3) ever evaluate to true?
  9. 9. 解答例 (PHP の場合) 悩むまでもなかった。 2018-06-15 PHPカンファレンス福岡 非公式前夜祭 9 $a = true; if ($a == 1 && $a == 2 && $a == 3) { echo "Hello, world!¥n"; } http://php.net/manual/ja/language.operators.comparison.php
  10. 10. 我らが PHP では? (改) 2018-06-15 PHPカンファレンス福岡 非公式前夜祭 10 Can ($a==0 && $a==1 && $a==2) ever evaluate to true? 今度はむずかしいぞ!・・・というか無理 (だと思う)
  11. 11. PECL operator 演算子オーバーロードを可能にする拡張モジュール 2018-06-15 PHPカンファレンス福岡 非公式前夜祭 11 https://pecl.php.net/package/operator GitHub リポジトリで PHP 7 対応版も公開されています https://github.com/php/pecl-php-operator
  12. 12. 解答例 (operator 拡張モジュールを利用) == 演算子をオーバーロードする方法 (PHP での解答) 2018-06-15 PHPカンファレンス福岡 非公式前夜祭 12 class A { public function __is_equal($other) { return true; } } $a = new A(); if ($a == 0 && $a == 1 && $a == 2) { echo "Don't do this!¥n"; }
  13. 13. 定義可能な演算子 オーバーロードできる演算子の一覧 2018-06-15 PHPカンファレンス福岡 非公式前夜祭 13 https://github.com/php/pecl-php-operator
  14. 14. 実現方法 [1/2] バイトコード命令の処理を置き換え 2018-06-15 PHPカンファレンス福岡 非公式前夜祭 14 static int op_handler(zend_execute_data *execute_data) { ... } ◦ zend_set_user_opcode_handler 関数 ◦ ユーザが定義した関数をバイトコード命令ハンドラに設定する static PHP_MINIT_FUNCTION(operator) { ... zend_set_user_opcode_handler(ZEND_IS_EQUAL, op_handler); ... }
  15. 15. 実現方法 [1/2] バイトコード命令の処理を置き換え 2018-06-15 PHPカンファレンス福岡 非公式前夜祭 15 static int op_handler(zend_execute_data *execute_data) { ... } ◦ zend_set_user_opcode_handler 関数 ◦ ユーザが定義した関数をバイトコード命令ハンドラに設定する static PHP_MINIT_FUNCTION(operator) { ... zend_set_user_opcode_handler(ZEND_IS_EQUAL, op_handler); ... } この命令の処理を この関数に任せる
  16. 16. 実現方法 [2/2] メソッドが定義されていたら呼び出す 2018-06-15 PHPカンファレンス福岡 非公式前夜祭 16 static int op_handler(zend_execute_data *execute_data) { ... if ((Z_TYPE_P(op1) != IS_OBJECT) || !operator_get_method(method, op1, &fci, &fcc)) { return ZEND_USER_OPCODE_DISPATCH; } ... if (FAILURE == zend_call_function(&fci, &fcc)) { ... } ... return ZEND_USER_OPCODE_CONTINUE; }
  17. 17. 実現方法 [2/2] メソッドが定義されていたら呼び出す 2018-06-15 PHPカンファレンス福岡 非公式前夜祭 17 static int op_handler(zend_execute_data *execute_data) { ... if ((Z_TYPE_P(op1) != IS_OBJECT) || !operator_get_method(method, op1, &fci, &fcc)) { return ZEND_USER_OPCODE_DISPATCH; } ... if (FAILURE == zend_call_function(&fci, &fcc)) { ... } ... return ZEND_USER_OPCODE_CONTINUE; } オーバーロードされていなければ本来の処理を実行 メソッドを実行して次のバイトコード命令に進む
  18. 18. とはいえ 実用に耐えるものではなく・・・ 2018-06-15 PHPカンファレンス福岡 非公式前夜祭 18
  19. 19. さまざまな問題 [1/3] 実行速度の低下 2018-06-15 PHPカンファレンス福岡 非公式前夜祭 19 $a = 1; $b = 1; for ($i = 0; $i < 50000000; ++$i) { $a == $b; } Intel Core i5-3337U 1.80GHz 2GB Memory CentOS 7 (VM on Windows7) 各 5 回の実行の平均 0.979 4.816 0.0 1.0 2.0 3.0 4.0 5.0 6.0 PHP 7.2.6 PHP 7.2.6 + operator 実行時間[秒]
  20. 20. さまざまな問題 [2/3] ユーザ定義命令ハンドラの衝突 たとえば Xdebug と共存できない 2018-06-15 PHPカンファレンス福岡 非公式前夜祭 20 ... XDEBUG_SET_OPCODE_OVERRIDE_COMMON(ZEND_IS_EQUAL); ... https://github.com/xdebug/xdebug/blob/master/xdebug.c より抜粋
  21. 21. さまざまな問題 [3/3] 演算子に対応するバイトコード命令があるとは限らない たとえば $a > $b 2018-06-15 PHPカンファレンス福岡 非公式前夜祭 21 __is_greater() and __is_greater_or_equal() require a rebuild of the main PHP runtime using the included patch. Without this patch, $a > $b is automatically remapped to $b < $a by the engine. https://github.com/php/pecl-php-operator ◦ PHP ではコンパイラが $a > $b を $b < $a に変換する ◦ PECL operator では元々どちらだったかを記録することで対処
  22. 22. 別のアプローチ 2018-06-15 PHPカンファレンス福岡 非公式前夜祭 22
  23. 23. zend_object_handlers 拡張モジュールで自作したクラスの比較方法は自由に定義できる 2018-06-15 PHPカンファレンス福岡 非公式前夜祭 23 struct _zend_object_handlers { ... zend_object_compare_t compare_objects; ... }; ◦ オブジェクトの比較で compare_objects に設定された関数が呼ばれる http://php.net/manual/ja/language.oop5.object-comparison.php
  24. 24. DateTime の例 DateTime オブジェクトの比較 2018-06-15 PHPカンファレンス福岡 非公式前夜祭 24 <?php $utc = new DateTime('2017-01-01 00:00:00 UTC'); $jst = new DateTime('2017-01-01 09:00:00 JST'); echo ($utc == $jst ? 1 : 0), "¥n"; // 1 が出力される ◦ タイムゾーンが違っても同じ日時なら等しい ◦ PHP では "==" の挙動は変えられない ◦ 拡張モジュールで定義しているクラスだからできる
  25. 25. DateTime の例 (内部実装) date_object_compare_date 関数で比較するように設定している 2018-06-15 PHPカンファレンス福岡 非公式前夜祭 25 static void date_register_classes(void) { ... INIT_CLASS_ENTRY(ce_date, "DateTime", date_funcs_date); ... date_object_handlers_date.compare_objects = date_object_compare_date; ... } static int date_object_compare_date(zval *d1, zval *d2) { php_date_obj *o1 = Z_PHPDATE_P(d1); php_date_obj *o2 = Z_PHPDATE_P(d2); ... return timelib_time_compare(o1->time, o2->time); } https://github.com/php/php-src/blob/master/ext/date/php_date.c オブジェクトの比較を この関数に任せる
  26. 26. Comparable interface for PHP https://github.com/nikic/comparable このアプローチで Comparable インタフェースを定義 2018-06-15 PHPカンファレンス福岡 非公式前夜祭 26 <?php class MyClass implements Comparable { public static function compare($a, $b) { ... } } ◦ Comparable インタフェースを実装するクラスを定義する ◦ そのクラスのオブジェクトの比較では compare メソッドが使われる
  27. 27. これは実用になる? そういう感じではない 2018-06-15 PHPカンファレンス福岡 非公式前夜祭 27 Note: This is to the most part just code demonstrating the implementation of a magic interface for a tutorial. I do not currently plan on proposing including such an interface for PHP itself. https://github.com/nikic/comparable いくつかの問題 ◦ そもそも上記のとおり実用を意図していない ◦ PHP 7 に対応していない ◦ 比較演算しか変えられない ◦ この拡張を真似して実装しても任意の演算子の処理を定義できるようにはならない ◦ zend_object_handlers で管理されている操作だけ
  28. 28. まとめ 素直に equals メソッドを定義しましょう 2018-06-15 PHPカンファレンス福岡 非公式前夜祭 28

×