LoginSignup
14
2

More than 3 years have passed since last update.

【PHP8】プリミティブ型にメソッドを生やそうスカラーオブジェクト

Posted at

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関数で行います。
この関数は型名(stringarray等)とクラス名を受け取ります。
メソッドは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)

現在サポートされている型名はnullboolintfloatstringarrayresourceです。
全てをサポートする意味があるかは別として、今のところは全てサポートしています。

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.iniextension=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で止まっているかなしみ。

14
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
14
2