[PHP] ereg() から preg_match() への変更でチョットした留意点


PHP7ではereg()が廃止となりpreg_match()を利用することになりました。
古いコードの書き換えをされている方もいると思います。

ereg()をpreg_match()に変更し
マッチングパターンをPOSIXからPCREに書き換えると各所で紹介されていますね。

ちょっと見落としがちかなと思うのは戻り値の違いです。
自分は望む結果が得られずに?となりましたので記事に残しておきます。

ereg()もpreg_match()もint型の戻り値を返しますが

ereg()
 マッチした場合にはマッチした文字列の長さを返し、
 マッチしなかった場合 またはエラーとなった場合は FALSE を返します。
preg_match()
 マッチした場合に 1 を返します。 マッチしなかった場合は 0、エラーが発生した場合は FALSE を返します。

という違いがあります。
マッチング結果を使用して何か処理を行う場合、結果に違いが生じるかもしれません。

<?php
$string = 'ab123cdefg@abcd678efg@a456bcdeg';        // 評価文字列
$pattern = '([a-z]+@)';                             // @とその前のアルファベット(不定長)を検出
while($Ret = ereg($pattern, $string, $matches)){    // $Ret マッチした文字列の長さ
    $pos = strpos($string, $matches[0]);            // $pos マッチした文字列が最初に現れた場所
    $string = substr($string, $pos + $Ret);         // $pos + $Ret を切り捨て評価文字列を再作成
    $result[] = $string;
}
var_dump($result);
?>

-- 結果 ------------------------------------------
array (size=2)
  0 => string 'abcd678efg@a456bcdeg' (length=20)
  1 => string 'a456bcdeg' (length=9)

このようなコードのereg()をpreg_match()に置き換えるだけだと

<?php
$string = 'ab123cdefg@abcd678efg@a456bcdeg';
$pattern = '/([a-z]+@)/';                                // PCREパターンに書き換え
while($Ret = preg_match($pattern, $string, $matches)){   // preg_match に変更、$Retに真偽値 1 が入る
    $pos = strpos($string, $matches[0]);
    $string = substr($string, $pos + $Ret);          // $Retが真偽値の為 $pos + 1 しか切り捨てない
    $result[] = $string;
}
var_dump($result);
?>

-- 結果 ------------------------------------------------
array (size=8)
  0 => string 'defg@abcd678efg@a456bcdeg' (length=25)
  1 => string 'efg@abcd678efg@a456bcdeg' (length=24)
  2 => string 'fg@abcd678efg@a456bcdeg' (length=23)
  3 => string 'g@abcd678efg@a456bcdeg' (length=22)
  4 => string '@abcd678efg@a456bcdeg' (length=21)
  5 => string 'fg@a456bcdeg' (length=12)
  6 => string 'g@a456bcdeg' (length=11)
  7 => string '@a456bcdeg' (length=10)

という具合に結果が変わってしまいます。
マッチした文字列長を後で取得することにします。

<?php
$string = 'ab123cdefg@abcd678efg@a456bcdeg';
$pattern = '/([a-z]+@)/';
while(preg_match($pattern, $string, $matches)){   // 戻り値は格納しない
    $pos = strpos($string, $matches[0]);
    $pos += strlen($matches[0]);                  // strlenでマッチした文字列長を取得
    $string = substr($string, $pos);
    $result[] = $string;
}
var_dump($result);
?>

-- 結果 -------------------------------------------
array (size=2)
  0 => string 'abcd678efg@a456bcdeg' (length=20)
  1 => string 'a456bcdeg' (length=9)

これで変更前と同じ結果が得られました。