
PHP8 上級/準上級試験の認定模擬問題として、プライム・ストラテジー様のPRIME STUDYが認定されているようです。ただ、この模擬問題には解説がありませんので解説をまとめていこうと思います。シリーズ第28回目です。

PHP初級レベルから脱却して中級/上級レベルにいきたい人にピッタリの内容ですね。

今回は文字列関数に関する問題です。str_replace() や strpos()、preg_match() など、よく使われる文字列処理関数についての理解が問われます。挙動や戻り値の違いを正しく把握しておくことでバグを未然に防ぎ、より意図通りのコードが書けるようになります。しっかり押さえていきましょう!
PRIME STUDY認定模擬問題のリンクはこちらです → https://study.prime-strategy.co.jp/
問題文
関数 に関する説明の中で、誤っているものを1つ選びなさい。
また、すべてのコードには下記のコードが適切な箇所に書かれているものとする。
declare(strict_types=1);
error_reporting(-1);
下記はマニュアルから一部引用した内容である。
call_user_func ( callable $callback , mixed …$args ) : mixed
call_user_func_array ( callable $callback , array $args ) : mixed
forward_static_call ( callable $callback , mixed …$args ) : mixed
callback パラメータで指定したユーザー定義の関数あるいはメソッドを、 それに続く引数を指定してコールします。この関数はメソッドのコンテキストでコールしなければなりません。 クラスの外部で使用することはできません。 この関数は 遅延静的束縛 を使います。
forward_static_call_array ( callable $callback , array $args ) : mixed
callback パラメータで指定したユーザー定義の関数あるいはメソッドをコールします。 この関数はメソッドのコンテキストでコールしなければなりません。 クラスの外部で使用することはできません。 この関数は 遅延静的束縛 を使います。 転送先のメソッドへのすべての引数は値渡しで、 call_user_func_array() と同様に配列で指定します。
function_exists ( string $function_name ) : bool
組み込みの内部関数およびユーザー定義関数の中から、 function_name で指定した名前の関数を探します。
register_shutdown_function ( callable $callback , mixed …$args ) : void
スクリプト処理が完了したとき、あるいは exit() がコールされたときに実行するコールバック関数を登録します。
register_shutdown_function() は複数回コールする ことが可能で、登録された順に関数がコールされます。 登録した関数内で exit() をコールした場合、 処理はそこで終了してその他のシャットダウン関数はコールされません。
(1)
call_user_func() 関数 と call_user_func_array() 関数は、最初の引数で指定したコールバック関数をコールする。call_user_func() 関数が「渡す引数を可変長引数で指定」するのに対して、call_user_func_array() 関数は「渡す引数を配列で指定」する。
第一引数に[オブジェクト, メソッド名]または[クラス名, メソッド名]の配列を渡すと、オブジェクトメソッドや静的メソッドを呼ぶ事も出来る。
また、引数を参照渡しで受け取る関数を call_user_func() から呼ぶと、エラー(Warning)が発生する。
そのため、以下のコード
function func($a, $b) {
echo FUNCTION, PHP_EOL;
var_dump($a, $b);
echo PHP_EOL;
}
function func2(&$v) {
$v[] = 999;
}
class Hoge {
public function func1($a) {
echo METHOD, PHP_EOL;
var_dump($a);
echo PHP_EOL;
}
public static function func2($a) {
echo __METHOD__, PHP_EOL;
var_dump(get_called_class(), $a);
echo PHP_EOL;
}
}
call_user_func('func', 1, 2);
call_user_func_array('func', ['var', 'val']);
$obj = new Hoge();
call_user_func([$obj, 'func1'], 999);
call_user_func([Hoge::class, 'func2'], 'val');
$v = [];
call_user_func('func2', $v);
var_dump($v);
を実行すると、結果は次のとおりとなる。
func
int(1)
int(2)
func
string(3) "var"
string(3) "val"
Hoge::func1
int(999)
Hoge::func2
string(4) "Hoge"
string(3) "val"
Warning: func2(): Argument #1 ($v) must be passed by reference, value given in …
array(0) {
}
(2)
forward_static_call() 関数 と forward_static_call_array() 関数は、静的メソッドをコールする。
そのため、以下のコード
class Hoge {
public static function func() {
echo METHOD, PHP_EOL;
var_dump(get_called_class());
echo PHP_EOL;
}
}
class Foo extends Hoge {
public static function callMethod() {
forward_static_call(['Hoge', 'func']);
echo PHP_EOL;
call_user_func(['Hoge', 'func']);
}
}
Foo::callMethod();
forward_static_call(['Hoge', 'func']);
を実行すると、結果は次のとおりとなる。
Hoge::func
string(3) "Foo"
Hoge::func
string(4) "Hoge"
Fatal error: Uncaught Error: Cannot call forward_static_call() when no class scope is active in …
(3)
function_exists() 関数 は指定した関数が定義されている場合に true を返す。
そのため、以下のコード
class Hoge {
public function func() {
}
}
function foo() {
}
$fn = function($v) {
var_dump( $v, function_exists($v) );
echo PHP_EOL;
};
$fn( [Hoge:class, 'func'] );
$fn( 'foo' );
$fn( 'function_exists' );
$fn( 'echo' );
$fn( 'dummy' );
を実行すると、結果は次のとおりとなる。
array(2) {
[0]=>
string(4) "Hoge"
[1]=>
string(4) "func"
}
bool(true)
string(3) "foo"
bool(true)
string(15) "function_exists"
bool(true)
string(4) "echo"
bool(true)
string(5) "dummy"
bool(false)
(4)
register_shutdown_function() 関数はシャットダウン時に実行する関数を登録する。
そのため、以下のコード
register_shutdown_function(function() {
echo "register_shutdown_function 1", PHP_EOL;
});
register_shutdown_function(function($v) {
echo "register_shutdown_function 2 {$v}", PHP_EOL;
}, 'val');
register_shutdown_function(function() {
echo "register_shutdown_function 3", PHP_EOL;
exit;
});
register_shutdown_function(function() {
echo "register_shutdown_function 4", PHP_EOL;
});
echo "start", PHP_EOL;
exit;
を実行すると、結果は次のとおりとなる。
start
register_shutdown_function 1
register_shutdown_function 2 val
register_shutdown_function 3
解説
選択肢1の解説
正しいです。
call_user_func() は 引数を 普通に並べて渡します。
call_user_func_array() は 引数を 配列でまとめて渡します。
また、[$obj, ‘method’] のように書けば、インスタンスメソッドが呼べる、参照渡しの引数(&$v)を普通に渡すと Warning が出る、コードにあるとおり$v に値は入らない、とすべて正しく説明されています。
選択肢2の解説
正しいです。
forward_static_call() は「遅延静的束縛」を使う関数で、オーバーライド先のクラスから静的メソッドを呼ぶ時に便利です。クラスの中からしか使えません(クラススコープ外から使うとエラー)
選択肢の説明どおり、forward_static_call() 内では get_called_class() に Foo が返ります(呼び出し元クラス)最後の forward_static_call() はスコープ外からなので Fatal error が出ます。
選択肢3の解説
誤りです。
$fn( [Hoge::class, 'func'] );
この $v の中身は 配列(クラス名とメソッド名)です。ですが、function_exists() は「関数名(文字列) が定義されているか?」を調べる関数です。
[Hoge::class, ‘func’] を渡すと、これは 関数ではない(=配列) なので、function_exists() は常に false を返すべきです。
つまり、このコードで function_exists([Hoge::class, ‘func’]) を呼ぶと、本来は false が返るはずなのに、選択肢の中では bool(true) と書かれていて、結果が間違っていることになります。
選択肢4の解説
正しいです。
register_shutdown_function() は終了時に呼ばれる関数を登録できる、複数登録ができる、登録された順に実行される、途中で exit が呼ばれるとそこで止まる(以降の登録は実行されない)、と正しいことが書かれています。
正解選択肢
以上より選択肢3が正解となります。
他の問題へ
模擬問題(1)インストール
模擬問題(2)変数の型
模擬問題(3)クラス
模擬問題(4)メソッドその1
模擬問題(5)メソッドその2
模擬問題(6)リファレンス
模擬問題(7)名前空間
模擬問題(8)エラー
模擬問題(9)定義済みのインターフェイスとクラスおよび SPL インターフェイス
模擬問題(10)SPL
模擬問題(11)定義済の変数
模擬問題(12)PHP7.0.xからPHP7.1.xへの移行
模擬問題(13)可変変数
模擬問題(14)PHP7.3.xからPHP7.4.xへの移行
模擬問題(15)PHP7.4.xからPHP8.0.xへの移行
模擬問題(16)制御構造
模擬問題(17)XSS(クロスサイトスクリプティング)
模擬問題(18)ファイルアップロード
模擬問題(19)推測困難なトークン
模擬問題(20)セッション
模擬問題(21)PHP のメモリ消費
模擬問題(22)関数その1
模擬問題(23)関数その2
模擬問題(24)関数その3
模擬問題(25)関数その4
模擬問題(26)関数その5
模擬問題(27)関数その6
模擬問題(28)関数その7
模擬問題(29)関数その8
模擬問題(30)関数その9
コメント