SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

ますます便利になるPHPの新機能を探ろう!

より使いやすくなったPHP 8.5の新機能──関数と処理系の強化ポイント

ますます便利になるPHPの新機能を探ろう! 第8回

関数(2)

書記素対応文字列関数にロケールを追加

 PHP 8.5では、書記素対応文字列関数に、引数としてロケールを追加できるようになりました。既出のgrapheme_levenshtein関数を含む以下の関数に引数localeを指定可能です。

  • grapheme_strpos()
  • grapheme_stripos()
  • grapheme_strrpos()
  • grapheme_strripos()
  • grapheme_strstr()
  • grapheme_stristr()
  • grapheme_levenshtein()

 ロケールを指定する意味は、文字列がどのような言語に基づくものであるかを明示し、文字列マッチングの精度を上げることで、単一のコード体系で多国語をカバーするUnicodeでは必須と言えます。

 例えば以下のリスト(1)では、トルコ語の小文字「İ」(\u{0130})が正しく認識されないため、大文字小文字の区別をしないgrapheme_stripos関数の呼び出しでも、マッチする部分がないとしてfalseが返されます。

リスト grapheme_locale.php
var_dump(grapheme_stripos("i", "\u{0130}", 0));	// (1)false
var_dump(grapheme_stripos("i", "\u{0130}", 0, "tr_TR"));	// (2)0
var_dump(grapheme_stripos("i", "\u{0130}", 0, "en_US"));	// (3)false

 (2)ではロケールにトルコ語を表すtr_TRを指定することで、「İ」(\u{0130})が正しく認識されて0文字目で一致とされます。(3)は英語を表すen_USであるので、ここではまた不一致となります。

 ロケールは、Locale Data Markup Language(LDML)に基づいて指定します。tr_TRやen_USはこれに基づくものですが、さらに強度(Stlength)を付加することで、マッチングの精度をコントロールすることができます。強度の指定は、日本語の異体字間でのレーベンシュタイン距離の算出やマッチングに利用できます。

 例えば以下のリストは、異体字が存在する「葛」について、それぞれレーベンシュタイン距離の算出と検索を行ってみた例です。なお、「葛」に続いている\u{E0101}は異体字セレクタ(IVS)と言い、U+E0100~U+E01EFの範囲で「どの異体字を表すか」を指定するためのものです。

リスト grapheme_locale.php
$katsu = '葛';
$katsu_E0101 = "葛\u{E0101}";
var_dump(grapheme_levenshtein($katsu, $katsu_E0101));	// (1)0
var_dump(grapheme_levenshtein($katsu, $katsu_E0101, locale: "ja_JP-u-ks-identic"));	// (2)1
var_dump(grapheme_strpos($katsu, $katsu_E0101));	// (3)0
var_dump(grapheme_strpos($katsu, $katsu_E0101, locale: "ja_JP-u-ks-identic"));
	// (4)false

 (1)ではロケールの指定がないので異体字でも一致と見なされてしまいます。(2)ではロケールにja_JP-u-ks-identic(異体字を調べることのできるマッチング強度を表す:https://www.unicode.org/reports/tr35/tr35-collation.html#Combining_Rulesを参照)を指定したので、異なるものとして扱われています。(3)(4)も同様です。

 書記素対応文字列関数にロケールを指定できるようになったことで、レーベンシュタイン距離を求めたり、文字列の検索を行う場合の国際化対応が容易になります。

get_error_handler関数とget_exception_handler関数

 PHP 8.5では、エラーハンドラと例外ハンドラ(エラーと例外をカスタム処理する関数)がそれぞれget_error_handler関数とget_exception_handler関数で取得できるようになりました。

 もともと、エラハンドラを操作する関数としてset_error_handler関数とrestore_error_handler関数が存在しましたが、取得のための関数がありませんでした。新たな2つの関数によって、取得と設定の関数が揃ったことになります。以下のリストは、設定、取得、復帰を確かめています。

リスト get_handler.php
function myErrorHandler($errno, $errstr, $errfile, $errline)
{
    return false;
}

$old_handler = set_error_handler('myErrorHandler');	// セット前のハンドラ
var_dump($old_handler);	// NULL
$current_handler = get_error_handler();	// 現ハンドラ
var_dump($current_handler);	// string(14) "myErrorHandler"

restore_error_handler();	// ハンドラの復帰
var_dump(get_error_handler());	// NULL

 これはエラーハンドラの例ですが、例外ハンドラについても同様です。

curl_share_init_persistent関数

 PHP 8.5では、リクエスト間でcurlハンドルを共有できるようになるcurl_share_init_persistent関数が使えるようになりました。

 curlハンドルとは、HTTPによって外部リソースにアクセスするライブラリcURLで使うハンドルです。curl_init関数でハンドルを取得し、curl_setopt関数でオプションを設定、curl_exec関数でリクエストを実行するのが基本形です。

リスト curl_persistent.php
$url = "http://www.google.co.jp/";
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
$res = curl_exec($ch);
var_dump($res);
	// <HTML><HEAD><meta http-equiv="content-type" content="text/html;charset=utf-8">
	// …

 curl_share_init関数を使うと、単一のリクエスト内で共有できるハンドルを作成できます。共有ハンドルと複数のcurlハンドルをひも付けることで、curlハンドル間でクッキーなどを共有できるようになります。ただしあくまでも単一リクエスト内であるので、リクエスト終了時には接続は閉じられます。

 curl_share_init_persistent関数を使うと、複数のリクエストにまたがってcurlハンドルを共有できます。これは、1回のリクエストが終了しても接続は生き続け、次のリクエストでもそれが使われることを意味します。

リスト curl_persistent.php
$sh = curl_share_init_persistent([CURL_LOCK_DATA_DNS, CURL_LOCK_DATA_CONNECT]);
$ch1 = curl_init("http://www.google.co.jp/");
curl_setopt($ch1, CURLOPT_SHARE, $sh);
curl_setopt($ch1, CURLOPT_VERBOSE, true);
$res = curl_exec($ch1);
var_dump($res);
	// …略…
	// * Host www.google.co.jp:80 was resolved.
	// * IPv6: (none)
	// * IPv4: 172.217.161.35
	// *   Trying 172.217.161.35:80...
	// * Established connection to www.google.co.jp (172.217.161.35 port 80) from 192.168.108.52 port 50175 
	// * using HTTP/1.x
	// …略…
$ch2 = curl_init("http://www.google.co.jp/");
curl_setopt($ch2, CURLOPT_SHARE, $sh);
curl_setopt($ch2, CURLOPT_VERBOSE, true);
$res = curl_exec($ch2);
var_dump($res);
	// …略…
	// * Reusing existing http: connection with host www.google.co.jp
	// …略…

 同じオプションを持つ共有ハンドルは、curl_share_init_persistent関数によって1回だけ作成されます。2回目以降の呼び出しは、同じオプションであれば作成済みのものが使われます。上記の例では、1回目のcurl_exec関数の戻り値には接続先の情報が細かく含まれるのに対し、2回目の戻り値には既存の接続を再利用する旨の情報のみとなり、接続が共有されていることが分かります。

 このように接続に伴うコストは低減できますが、状況によっては接続が長時間開いたままになるので、外部リソース側では相応の負担が生じることに注意が必要です。

RFC3986/WHATWG標準準拠のURL API

 PHP 8.5では、RFC3986/WHATWG標準に基いたURI/URLを正しく扱えるようになりました。

 RFC3986とは、2005年に標準化されたURIの書式です。この構文に従うことで、アプリ内はもちろん外部とのやり取りにおいても、正しいURIを使うことが可能になります。

 後者はWHATWG URL Standardと呼ばれるURLの標準で、RFC3986のURL版のような位置付けです。JavaScriptを含むWebブラウザでの利用を想定しており、用語もURLに統一されています。URLを正規化する機能も含むので、Webに特化したシステムなら後者への準拠が考えられるでしょう。

 しかしながら、PHPではparse_url関数など機能によって古いRFC2396を使っていたりと対応がバラバラで、セキュリティ上のリスクにもなり得るものでした。そこでPHP 8.5では新たにUri\RFC3986モジュール(Uri\WhatWgモジュール)が利用可能になり、URIの生成や加工、取得といった操作を標準に基づいて行えるようになりました。

リスト rfc3986_api.php
$uri = new Uri\Rfc3986\Uri("https://naosan.jp");	// この2行は同じ意味
$uri = Uri\Rfc3986\Uri::parse("https://naosan.jp");
var_dump($uri);
	// Uri\Rfc3986\Uri Object
	// (
	//    [scheme] => https
	//     [username] => 
	//     [password] => 
	//     [host] => naosan.jp
	//     [port] => 
	//     [path] => 
	//     [query] => 
	//     [fragment] => 
	// )
$url = new Uri\WhatWg\Url("https://naosan.jp");	// この2行は同じ意味
$url = Uri\WhatWg\Url::parse("https://naosan.jp");
print_r($url); 
	// Uri\WhatWg\Url Object
	// …以降は同様…

 ここではUri\Rfc3986モジュールとUri\WhatWgモジュールを使っていますが、この範囲では両者は同じオブジェクトとなります。生成したURI(URL)からは、豊富なメソッドを使って加工や文字列の取得、判定を行うことができます。以下のリストは、その一部です。

リスト rfc3986_api.php
echo $url->withPath("/contact")->toAsciiString() . PHP_EOL;	// https://naosan.jp/contact
echo $url->withQuery("id=100")->toAsciiString() . PHP_EOL;	// https://naosan.jp/?id=100

CHIPSのためのsetcookie関数オプションpartitioned

 PHP 8.5では、setcookie関数(setrawcookie関数)にCHIPS(Cookies Having Independent Partitioned State)に対応するオプションpartitionedを追加できるようになりました。

 CHIPSとは、プライバシーサンドボックス技術の一つで、サードパーティーCookieをドメインごとに分ける仕組みです。サードパーティーCookieの問題点としては、複数のWebサイトを横断してのデータ収集や共有が可能になってしまうことですが、これをWebサイトごとに制限することで、ユーザーの意図しない情報共有を防ぐことができます。

 PHPには、クッキーをセットするsetcookie関数がありますが、これまでCHIPSには対応しておらず、samesiteオプションに"Partitioned;"を紛れ込ますなどの対応策がとられてきました。PHP 8.5では正式にpartitionedオプションが指定可能になり、このような回避策は不要になりました。

リスト setcookie_partitioned.php
setcookie('name', 'value', ['secure' => true, 'partitioned' => true]);

// PHP 8.4まで
setcookie('name', 'value', ['secure' => true, 'samesite' => 'strict; Partitioned;']);

次のページ
その他

この記事は参考になりましたか?

ますます便利になるPHPの新機能を探ろう!連載記事一覧

もっと読む

この記事の著者

WINGSプロジェクト 山内 直(WINGSプロジェクト ヤマウチ ナオ)

WINGSプロジェクトについて>有限会社 WINGSプロジェクトが運営する、テクニカル執筆コミュニティ(代表 山田祥寛)。主にWeb開発分野の書籍/記事執筆、翻訳、講演等を幅広く手がける。2018年11月時点での登録メンバは55名で、現在も執筆メンバを募集中。興味のある方は、どしどし応募頂きたい。著書記事多数。 RSS X: @WingsPro_info(公式)、@WingsPro_info/wings(メンバーリスト) Facebook <個人紹介>WINGSプロジェクト所属のテクニカルライター。出版社を経てフリーランスとして独立。ライター、エディター、デベロッパー、講師業に従事。屋号は「たまデジ。」。

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

山田 祥寛(ヤマダ ヨシヒロ)

静岡県榛原町生まれ。一橋大学経済学部卒業後、NECにてシステム企画業務に携わるが、2003年4月に念願かなってフリーライターに転身。Microsoft MVP for Visual Studio and Development Technologies。執筆コミュニティ「WINGSプロジェクト」代表。主な著書に「独習シリーズ(Java・C#・Python・PHP・Ruby・JSP&サーブレットなど)」「速習シリーズ(ASP.NET Core・Vue.js・React・TypeScript・ECMAScript、Laravelなど)」「改訂3版JavaScript本格入門」「これからはじめるReact実践入門」「はじめてのAndroidアプリ開発 Kotlin編 」他、著書多数

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

CodeZine(コードジン)
https://codezine.jp/article/detail/23115 2026/02/05 08:00

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング