• PHP
【php8上級/準上級試験】模擬問題解説 問題25 php:// stream_wrapper_register

「第1回 PHP 8 上級 模擬試験」を解説していきます

本記事ではひたすら下記記事の模擬試験の解説をしていきたいと思います!

第1回 PHP 8 上級 模擬試験
https://study.prime-strategy.co.jp/study/ph8ex1/

解説記事一覧

模擬問題 25

関数 に関する説明の中で、誤っているものを1つ選びなさい。
なお「\」はバックスラッシュに読み替えること。
また、すべてのコードには下記のコードが適切な箇所に書かれているものとする。

declare(strict_types=1);
error_reporting(-1);
下記はマニュアルから一部引用した内容である。

stream_wrapper_register ( string $protocol , string $class , int $flags = 0 ) : bool
成功した場合に true を、失敗した場合に false を返します。
stream_wrapper_register() は、 protocol というハンドラが既にある場合、 false を返します。

stream_filter_prepend ( resource $stream , string $filtername , int $read_write = ? , mixed $params = ? ) : resource

PHP では「ファイル、ネットワーク、データ圧縮などに関する、 共通した一連の関数群と利用法を持つ操作の一般化の手法」として、ストリームがある。
また「ストリームにおいてどのように特定の プロトコル/エンコーディングを扱うかを扱うかを指示する付加的なコード」をラッパーと呼称する。
PHP において、組み込みのラッパーは様々にあるが、例えば php:// はさまざまな入出力ストリームへのアクセスを提供している。
例えば php://input は読み込み専用のストリームで、リクエストの body 部から生のデータを読み込むことができる。
そのため、以下のコード

var_dump( file_get_contents('php://input') );

curl https://www.example.com/stream.php -d ‘{“exam_num”:100,”exam_string”:”value”}’
の形で呼び出すと、結果は次のとおりとなる。

string(38) “{“exam_num”:100,”exam_string”:”value”}”

ストリームは、データを連続する流れとして扱うための抽象概念です。

php://input が特に役立つのは「通常のフォーム送信以外」のデータを取得する場合です。具体的には:

  • JSONデータの取得: 現代のWeb APIやフロントエンドフレームワーク(React, Vue, Angularなど)は、JSONでデータをやり取りすることがほとんどです。これらは通常、Content-Type: application/json ヘッダーでPOSTリクエストを送信します。

通常のHTMLフォーム(application/x-www-form-urlencoded や multipart/form-data)でPOSTされたデータは $_POST グローバル変数に自動的に格納されますが、上記のような特殊なケースでは $_POST にデータが入らないため、php://input を使用して直接リクエストボディを読み取る必要があります。

PHPでのデータ取得: $_POST vs php://input 従来のHTMLフォーム $_POST で取得 <form method=”post”> <input name=”name” value=”John”> <input name=”email” value=”john@example.com”> 送信されるデータ形式: Content-Type: application/x-www-form-urlencoded name=John&email=john@example.com <?php // $_POST から直接取得できる $name = $_POST[‘name’]; $email = $_POST[‘email’]; // 処理… モダンなJSONリクエスト php://input で取得 fetch(‘/api.php’, { method: ‘POST’, headers: { ‘Content-Type’: ‘application/json’ }, 送信されるデータ形式: Content-Type: application/json {“name”:”John”,”email”:”john@example.com”} <?php // 生のJSONデータを取得 $json = file_get_contents(‘php://input’); $data = json_decode($json, true); // $data[‘name’], $data[‘email’] を使用 $_POSTではJSONデータを取得できないため、php://inputが必要

問題文の内容は正しい⭕です

php://memory および php://temp は読み書き可能なストリームで、一時データをファイルのように保存できるラッパーである。
そのため、以下のコード

$csv_string = "1,2,3\n4,5,6\n";

$fp = fopen('php://memory', 'r+');
fwrite($fp, $csv_string);
fseek($fp, 0, SEEK_SET);

while($row = fgetcsv($fp)) {
  var_dump($row);
}

を実行すると、結果は次のとおりとなる。

array(3) {
  [0]=>
  string(1) "1"
  [1]=>
  string(1) "2"
  [2]=>
  string(1) "3"
}
array(3) {
  [0]=>
  string(1) "4"
  [1]=>
  string(1) "5"
  [2]=>
  string(1) "6"
}

php://memory および php://temp は読み書き可能なストリーム

php://memory の基本 メモリ上に仮想的な「ファイル」を作成し、実際のファイルシステムを 使わずにデータを操作できる // メモリストリームをオープン $fp = fopen(‘php://memory’, ‘r+’); // データを書き込む fwrite($fp, “Hello, World!”); // ファイルポインタを先頭に戻す rewind($fp); // または fseek($fp, 0); メリット: 高速、一時ファイル不要、権限問題なし
php://memory と php://temp の違い 両方とも一時的なデータ操作に使えますが、大きなデータでは異なる動作をします php://memory • 常にメモリ上にデータを保持 • 小〜中規模のデータに最適 • 最も高速 • メモリ制限に注意 php://temp • 一定サイズを超えると ディスクに書き込む • 大規模データに適している • メモリ使用量を制限できる • デフォルト閾値は約2MB 大きなデータを扱う場合は php://temp を推奨

この説明に誤りはありません。内容は⭕です

stream_wrapper_register() 関数を使うと、新しいラッパーとその挙動を登録する事ができる。
そのため、以下のコード

$r = stream_wrapper_register('dummy', 'PhpStreamDummy');
var_dump($r);

を実行すると、結果は次のとおりとなる。

Warning: stream_wrapper_register(): class ‘PhpStreamDummy’ is undefined in …
bool(false)
一方で以下のコード

class PhpStream {
}

$r = stream_wrapper_register('php', 'PhpStream');
var_dump($r);

を実行すると、結果は次のとおりとなる。

bool(true)

stream_wrapper_register() は新しいストリームラッパーを登録する

  • PHP の ストリームラッパー をカスタマイズするために使う。
  • stream_wrapper_register(‘ラッパー名’, ‘クラス名’) の形式。
stream_wrapper_register の基本 カスタムストリームプロトコルを登録する関数 stream_wrapper_register(プロトコル名, クラス名) 使用例 // カスタムラッパー登録 class MyWrapper { // 必要なメソッド public function stream_open() { return true; } // 登録と使用 stream_wrapper_register( ‘mydata’, ‘MyWrapper’); $fp = fopen(‘mydata://file’, ‘r’); 注意: php, file, http などの予約名は使用できません

⚠️ 組み込みラッパー (php) を登録するとエラー

php は PHP の組み込みストリームラッパー であり、再登録できない。
実際にこのコードを実行すると Warning が発生して false を返す。

よって問題文は誤り❌です

stream_filter_prepend() 関数を使うと、フィルタをストリームに付加する事ができる。
そのため、exam.txt ファイルに「文字コード sjis で書かれた文字」を入れた上で、以下のコード

$file_name = './exam.txt';
$fp = fopen($file_name, 'r');
var_dump( fgets($fp) );
fclose($fp);

$fp = fopen($file_name, 'r');
$fp2 = stream_filter_prepend($fp, 'convert.iconv.SJIS-win/UTF-8');
var_dump( fgets($fp) );
fclose($fp);

を実行すると、結果は次のとおりとなる。

string(17) “sjis?????????

string(23) “sjisでこんにちは

よって選択肢の内容は正しい⭕です

問題番号正解の選択肢
1PHPの「最新以外の(古い)コード」は、公式サイトでの提供は全くしていない。そのため、古いバージョンのコードが必要な場合、別途「非公式の外部サイト」からソースコードを入手する必要がある。
2論理型 (boolean) は「真偽値」とも呼ばれ、値は true か false か null のいずれかになる。なお、true、false、null の文字は、大文字でも小文字でもよい。
3抽象クラスから継承する際、親クラスで abstract としてマークされた全てのメソッドは子クラスで定義する必要があり、可視性は同等(またはより緩い制約)で、必須引数の数は同じであれば型宣言が異なってもかまわない。
4PHP において、デストラクタは __destruct() メソッドで実装される。親クラスのデストラクタは暗黙的に呼ばれ、呼び出し順序は「子クラスのデストラクタ → 親クラスのデストラクタ」となる。
5__get() はアクセス不能(protected または private)または存在しないプロパティからデータを読み込む際に使用される。なお、__getStatic() は存在せず、オブジェクトや静的コンテキストで動作しない。
6PHP のリファレンス渡しを使用すると、関数内で引数を修正できる。(※※正解テキストでは「呼び出す側で変数に & を付ける必要がある」と記載されています。)
7名前空間は namespace キーワードで宣言する。通常はファイルの先頭に記述する必要があり、名前空間宣言前に書かれたクラスはその名前空間に含まれない。
8PHP 5 では Exception クラスが全例外の基底クラスだったが、PHP 7 以降は Exception クラスは Throwable インタフェースを基底とし、Error クラスが内部エラーの基底クラスとして導入された。
9ArrayAccess インターフェイスは、オブジェクトを配列としてアクセスするための機能を提供する。
10SplFileInfo クラスは、ファイルの情報取得や操作を行うためのクラスである。
11$_COOKIE は、HTTP クッキーから渡された値が連想配列として格納され、また設定も可能である。
12PHP 7.1.x では、文字列操作関数で負のオフセット指定が可能となり、[] や {} による文字単位のアクセスも文字列の末尾からのオフセットとして解釈される。
13可変変数は、スーパーグローバル変数にも使用できる。
14PHP 7.4.x では、波括弧を使った配列や文字列のオフセットアクセスの文法は非推奨となった。
15PHP 8 では、private メソッドの継承に関するルールが変更され、親クラスの同名メソッドの可視性に関係なく子クラスでオーバーライド可能となった。
16break は、現在実行中の for, foreach, while, do-while, switch 構造の実行を終了し、オプションでネストしたループの何段分を抜けるか指定できる。
17XSS 対策として、htmlentities() を適切に使用することで、文字列や配列の入力に対して安全な出力が可能となる。
18アップロードされたファイルの元のファイル名は、$_FILES[‘{formのnameの値}’][‘name’] に格納され、move_uploaded_file() の第二引数として利用できる。
19uniqid() と mt_rand() を組み合わせることで、暗号論的にランダムな(推測困難な)トークンを生成できる。
20PHP のセッションでは、セッション ID がクッキーに保存され、session_set_cookie_params() によりそのクッキーのパラメータを設定できる。
21PHP の変数の参照カウントは、xdebug_debug_zval() で確認でき、オブジェクトを clone した場合は内部的に参照が使われ、一時的に参照カウントが増加する。
22DirectoryIterator クラスは、ディレクトリ内のファイルやサブディレクトリの情報を取得するシンプルなインターフェイスを提供する。
23mail() 関数は、メールを送信するための関数で、第四引数で追加のヘッダー情報を指定できる。
24escapeshellarg() と escapeshellcmd() は、外部入力をシェルコマンドの引数として使用する際に必要なエスケープ処理を行い、どちらを使用しても意味合いは同じである。
25stream_wrapper_register() 関数を使用すると、新しいストリームラッパー(プロトコルハンドラ)を登録できるが、既に存在する場合は失敗する。
26strpos() 関数は、文字列内で指定した部分文字列の最初の出現位置を返し、見つからなければ false を返す。ただし、先頭位置の場合は 0 が返るため注意が必要。
27Phar のスタブには __HALT_COMPILER() が使用され、これ以降のコードはコンパイルされない。
28function_exists() 関数は、指定された関数が定義されているかどうかをチェックし、存在すれば true を返す。
29openssl_decrypt() 関数は、openssl_encrypt() で暗号化されたデータを正しく復号し、元のデータを取り戻す。
30strtotime() 関数は、英語形式の日付文字列を Unix タイムスタンプに変換し、無効な日付の場合は false を返す。