• PHP
【php8上級/準上級試験】模擬問題解説 問題12. list() iterable

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

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

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

解説記事一覧

模擬問題 12

PHP 7.0.x から PHP 7.1.x への移行 に関する説明の中で、誤っているものを1つ選びなさい。
なお、すべてのコードの先頭には下記のコードが書かれているものとする。

declare(strict_types=1);
error_reporting(-1);

配列の短縮構文 ([]) を使って、 代入用に配列の値を取り出せるようになった。
これは、list() の代替として使う事ができる(list() が無くなったわけではない)。そのため、以下のコード

$data = [
    [1, 2],
    [777, 999],
];

list($i, $j) = $data[0];
var_dump($i, $j);
[$i, $j] = $data[1];
var_dump($i, $j);

は正しく実行でき、結果は次のとおりとなる。

int(1)
int(2)
int(777)
int(999)

list()

list() は、配列から複数の値を取り出して、複数の変数に一度に代入するためのPHPの組み込み構文です。たとえば、以下のように使います。

$data = [1, 2];
list($a, $b) = $data;
// $a は 1、$b は 2 になります。

ポイント

  • PHP 7.1 から、短い構文 ([]) を使った配列の値の代入(分割代入)ができるようになりました。
  • これは list() の代替として使えますが、list() が廃止されたわけではなく、依然として利用可能です。
  • コード例も正しく動作し、期待通りの結果が得られます。

配列の分割代入とは

配列の分割代入とは、配列の各要素を一度に複数の変数に代入する方法です。
PHP 7.1 以降では、従来の list() を使う方法に代わり、よりシンプルな短縮構文 ([]) で記述できるようになりました。

たとえば、

[$a, $b] = [1, 2];

// $a には 1、$b には 2 が代入されます

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

新しい擬似型 (callable と同じような型) である iterable が導入された。パラメータおよび返り値の型指定で使うことができ、「配列か、あるいは Traversable インターフェイスを実装したオブジェクトを受け付ける」ようになる。そのため、以下のコード

function hoge(iterable $itr) {
    var_dump($itr);
}

hoge([1, 2]);
hoge(new ArrayObject());

は正しく実行でき、結果は次のとおりとなる。

array(2) {
  [0]=>
  int(1)
  [1]=>
  int(2)
}
object(ArrayObject)#1 (1) {
  ["storage":"ArrayObject":private]=>
  array(0) {
  }
}

一方で以下のコード

function hoge(iterable $itr) {
    var_dump($itr);
}

$obj = new stdClass();
$obj->s = 'string';
$obj->i = 999;
hoge($obj);

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

Fatal error: Uncaught TypeError: Argument 1 passed to hoge() must be iterable, object given, called in …

iterable 型ヒント

「iterable 型ヒント」とは、PHPで関数やメソッドの引数に指定する型の宣言のひとつです。
具体的には、iterable を使うことで、その引数に 配列 と Traversable インターフェイスを実装したオブジェクト のどちらかが渡されることを期待する、という意味になります。

ここでの「型ヒント」とは、

関数やメソッドのパラメータ、戻り値、プロパティなどに、どのような型の値が使用されるべきかを明示的に指定する仕組み
のことを指します。

数値形式ではない文字列を使って、数値を期待する演算 (+, -, *, /, **, %, <<, >>, |, &, ^ や、これらを用いた代入演算) を行おうとしたときに、 E_WARNING あるいは E_NOTICE レベルのエラーが発生するようになった。そのため、以下のコード

$i = '11' + '22';
var_dump($i);

は正しく実行でき、結果は次のとおりとなる。

int(33)

一方で以下のコード

$i = '11' + 'a';
var_dump($i);

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

Warning: A non-numeric value encountered in ...
int(11)

PHPは「動的型付け言語」

PHPは「動的型付け言語」と呼ばれ、変数の型を明示的に宣言する必要がありません。そのため、PHPは文脈に応じて自動的に値の型を変換(型変換、または「型ジャグリング」と呼ばれます)します。

PHPの動的型付け $age = “25” (文字列型) $age = $age + 5 (数値型に自動変換) 実行時に必要に応じて 変数の型が自動的に変換されます

問題文の場合'11''22' のように、文字列全体が数字で構成されている場合、PHPはこれを数値 1122 に変換します。

⬇️

結果、'11' + '22'11 + 22 と同じ意味になり、結果は 33 となります。

また’a’ のように、先頭が数字でない文字列の場合、PHPはこれを数値の 0 として扱います。

ただし、PHPのバージョン(PHP 8.1以降など)によっては、このような変換が行われる際に、E_WARNING(または E_NOTICE)レベルの警告が発生することがあります。

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

文字列操作関数 のうちオフセット指定のできるものすべてについて、負のオフセットを指定できるようになった。
[] や {} による 文字列への文字単位のアクセス についても同様となり、負のオフセットは、文字列の末尾からのオフセットと解釈される。
負の値を渡した場合、-0 が「文字列の末尾」となる。そのため、以下のコード

$s = 'abcdefg';
var_dump( $s[-1] );

は正しく実行でき、結果は次のとおりとなる。

string(1) "f"

負のオフセット(基準からの数えて何番目か)

PHP 7.1以降、文字列アクセス時に負のオフセット指定がサポートされました。ドキュメントによると「負のオフセットは文字列の末尾を起点とする」ため、

  • -1 は「末尾から1文字目」すなわち 最後の文字 を指します
  • -2 は「末尾から2文字目」なので、最後から二番目の文字を指します
  • -00 は数値的に同一視されるため、$s[-0]$s[0] と同じ扱いになります。つまり先頭の 'a' が返ります。
文字列: “abcdefg” (長さ 7) a 0 -7 b 1 -6 c 2 -5 d 3 -4 e 4 -3 f 5 -2 g 6 -1

よって問題文は

var_dump( $s[-1] );は “f” ではなく “g” となり内容は誤り❌です

問題番号正解の選択肢
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 を返す。