PHPの第一人者NikitaのGitHubを覗いていたらなんか面白いものを見つけました。
わりと前から存在しているみたいで、最古のコミットは2013/01/25です。
積極的な機能追加こそほとんどないものの、メンテナンスは続いていて、PHPに合わせて着実にバージョンアップされています。
2020年11月にもPHP8対応のアプデがされています。
残念ながらPHPの機能として取り込むつもりはないということで、あくまでエクステンションの立場に留まるようです。
さて、ではどんな機能なのかというとこんなのです。
class StringHandler {
public static function length($self) {
return strlen($self);
}
public static function startsWith($self, $other) {
return strpos($self, $other) === 0;
}
}
register_primitive_type_handler('string', 'StringHandler');
$string = "abc";
var_dump($string->length()); // int(3)
var_dump($string->startsWith("a")); // bool(true)
文字列にメソッドが生えました。
Add support for method calls on primitive types in PHP
この拡張モジュールは、一部のプリミティブ型にメソッド呼び出しを可能にする機能を実装します。
これにより、$str->length()
のように新しいAPIを実装することが可能になります。
このリポジトリの主な目的は、新しいAPIを設計するための概念実証を行うことです。
プリミティブ型に対する操作をオブジェクト構文に切り替えることは、PHPに多数存在する一貫性のないコアAPIを再設計するための、またとない機会となります。
ユーザランドのコードとして新しいAPIを素早くプロトタイピングしてテストするための手段を提供します。
一旦APIの形が出来あがったら、それをPHPへ組み込むためにRFCとして提出されます。
注意:この機能はプロトタイピングのために存在しており、実際に私がPHPに求めているものではありません。
Registering type handlers
型ハンドラの登録はregister_primitive_type_handler
関数で行います。
この関数は型名(string
やarray
等)とクラス名を受け取ります。
メソッドはstaticに実装し、第一引数でプリミティブ型を受け取ります。
class StringHandler {
public static function length($self) {
return strlen($self);
}
public static function startsWith($self, $other) {
return strpos($self, $other) === 0;
}
}
register_primitive_type_handler('string', 'StringHandler');
$string = "abc";
var_dump($string->length()); // int(3)
var_dump($string->startsWith("a")); // bool(true)
現在サポートされている型名はnull
・bool
・int
・float
・string
・array
・resource
です。
全てをサポートする意味があるかは別として、今のところは全てサポートしています。
Implemented APIs
先述したとおり、このリポジトリの主目的は、プリミティブ型のための良いAPIを設計することです。
実際に作成したAPIはhandlers/
フォルダ内に存在します。(そして明らかに作業中です)
実際に使用したい場合はhandlers/bootstrap.php
をincludeすると使えます。
Installation
masterはPHP7.0から8.0までをサポートしています。
PHP5は0.2ブランチを使用してください。
Unix
以下のコマンドでコンパイル・インストールしてください。
phpize
./configure
make
sudo make install
Windows
prebuilt Windows DLLから使用しているPHPバージョンに合ったDLLをダウンロードし、PHPのエクステンションディレクトリに配置します。
そしてphp.ini
にextension=php_scalar_objects.dll
を追加してください。
Testing the extension
この拡張機能には、動作テストを行うためのrun-tests.php
が付属しています。
以下のコマンドで実行してください。
php run-tests.php -q -p php
Limitations
この拡張機能にはいくつかの制限があります。
技術的制約のため、ミュータブルなAPIを作成することはできません。
メソッド内で$self
を変更することはできません。
というかコピーが変更されるだけなので、元に影響することはありません。
stringハンドラ
どのようなことができるかのサンプルとしてstringハンドラが用意されていますので、その一部を紹介してみます。
length
文字列長を取得します。
strlenです。
echo "foobar"->length(); // 6
slice
文字列の一部を返します。
substrです。
echo "foobar"->slice(1, 4); // "ooba"
replaceSlice
文字列の一部を置換します。
substr_replaceです。
echo "abcde"->replaceSlice("aboo", 1, 4); // "faboor"
indexOf
文字の現れる位置を返します。
strposです。
echo "abc def abc def abc"->indexOf("abc", 1); // 8
lastIndexOf
文字が最後に現れる位置を返します。
strrposです。
echo "abc def abc def abc"->lastIndexOf("abc"); // 16
contains
文字が含まれるかどうかを判定します。
str_containsです。
echo "abc def abc def abc"->contains("abc"); // true
repeat
指定回数繰り返します。
str_repeatです。
echo "foobar"->repeat(3); // "foobarfoobarfoobar"
その他
サンプルは他にもtrim、toLower、toUpperなど色々用意されています。
ほとんどは1行で書けるような中身ばっかりですが、それを関数呼び出しではなく直接メソッド形式で書けるのは文法的に楽ですね。
また何か複雑な機能が欲しいほしいとなったときにも、このハンドラはPHPで書くことができるため、簡単に自由に追加することができます。
感想
PHPの機能として使えればたしかに便利そうではあります。
// 同じ。コード自体は特に意味ない
echo str_contains(substr(str_repeat('foo', 3), 0, 1), 'foo');
echo 'foo'->repeat(3)->substr(0, 1)->contains('foo');
どちらが見やすいかといえば意見が分かれるかもしれませんが、どちらが書きやすいかといえば間違いなく後者でしょう。
作者の言う、プロトタイピングという目的に実に合致した記述だと言えるでしょう。
とはいえ本人が望んでいないこともあり、PHP本体に取り込まれることはまずないと思われます。
半端に入ってしまうと却って絶望的なコードになってしまうので、PHP本体に入れるならこれを防ぐ方法も考えないといけないでしょうしね。
echo substr('foo'->repeat(3), 0, 1)->contains('foo');
試せなかった
Windows版ビルドが2017/04/14のPHP7.1で止まっているかなしみ。