言語への理解を深めるために既存の関数を自作して再現しようと思います。
実装環境
- PHP 7.0.11
まずは試作
文字列を文字の配列と考えて、途切れたところまでを文字列の長さとして取得します。
<?php
function my_strlen( $str ) {
$i = 0;
while( isset( $str[$i] ) ) {
$i++;
}
return $i;
}
?>
動作の比較
実際に実行結果を比較してみます。
my_strlenの場合
<?php
class TestClass
{
public function __toString()
{
return __CLASS__;
}
}
my_strlen("test"); // 戻り値:4
my_strlen(123); // 戻り値:0
my_strlen(["a", "b", "c"]); // 戻り値:0
my_strlen(new TestClass()); // 戻り値:なし(Fatal errorが発生)
?>
既存のstrlenの場合
<?php
strlen("test"); // 戻り値:4
strlen(123); // 戻り値:3
strlen(["a", "b", "c"]); // 戻り値:NULL(Warningが発生)
strlen(new TestClass()); // 戻り値:9
?>
だいぶ結果が違いますね。
自作の関数は配列の場合、要素の個数を返してしまってますが、
既存の関数は文字列に変換ものはWarningエラーが発生させているようです。
修正版
そこで、文字数を確認する前に引数をキャストするように処理を追加してみました。
<?php
function my_strlen( $str ) {
$str = (string) $str;
$i = 0;
while( isset( $str[$i] ) ) {
$i++;
}
return $i;
}
my_strlen("test"); // 戻り値:4
my_strlen(123); // 戻り値:3
my_strlen(["a", "b", "c"]); // 戻り値:5(Noticeが発生)
my_strlen(new TestClass()); // 戻り値:9
?>
結果は近くなりましたが、キャストすると配列の場合は「Array」という文字列に変換されるため戻り値は「5」を返しています。
最終版
こちらがエラー処理までそろえた最終版です。
検証では紹介しませんでしたが、ファイルハンドラのようなリソースにも対応しています。
<?php
function my_strlen( $str ) {
$type = gettype( $str );
switch( $type ) {
case "array":
case "object":
case "resource":
if( "object" == $type && method_exists( $str, "__toString") ) {
break;
}
trigger_error("strlen() expects parameter 1 to be string, ".$type." given", E_USER_WARNING);
return null;
}
$str = (string) $str;
$i = 0;
while( isset( $str[$i] ) ) {
$i++;
}
return $i;
}
my_strlen("test"); // 戻り値:4
my_strlen(123); // 戻り値:3
my_strlen(["a", "b", "c"]); // 戻り値:NULL(Warningが発生)
my_strlen(new TestClass()); // 戻り値:9
$f = fopen(__FILE__, "r");
my_strlen($f); // 戻り値:NULL(Warningが発生)
fclose( $f );
?>