0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【PHP】文字列操作・配列関数・自作関数・変数スコープ

0
Posted at

環境

  • PHP 8.3
  • mbstring 拡張を使用(マルチバイト文字列関数のため)

1. 文字列の長さ(strlenmb_strlen)

一般形

strlen(文字列);
mb_strlen(文字列);

strlenバイト数mb_strlen文字数を返す。日本語(UTF-8)は1文字あたり3バイトなので、英数字と日本語で挙動が変わる点に注意。

$text = 'あいうえお';
echo strlen($text);    // バイト数
echo mb_strlen($text); // 文字数

$text2 = 'abc';
echo strlen($text2);
echo mb_strlen($text2);

出力結果

15
5
3
3
  • 日本語5文字 → strlenは15(3バイト×5文字)、mb_strlenは5
  • 半角英数字3文字 → どちらも3(1文字=1バイトのため差が出ない)

2. 文字列の置換(str_replace)

一般形

str_replace(検索文字列, 置換後文字列, 対象の文字列);
$str = '文字列を置換します';
echo str_replace('置換', 'ちかん', $str);

出力結果

文字列をちかんします

第1引数(検索文字列)を第2引数(置換文字列)に置き換える。


3. 指定文字列で分割(explode)

一般形

explode(区切り文字, 対象の文字列);
$str_2 = '指定文字列で,分割します';
var_dump(explode(',', $str_2));

出力結果

array(2) {
  [0]=> string(18) "指定文字列で"
  [1]=> string(15) "分割します"
}

カンマを区切り文字として配列に分割される。逆に配列を文字列に結合するには implode を使う(今回は未実装)。


4. 正規表現で文字列が含まれるか確認(preg_match)

一般形

preg_match(正規表現パターン, 対象の文字列);
$str_3 = '特定の文字列が含まれるか確認する';
echo preg_match('/文字列/', $str_3);

出力結果

1

マッチした場合は 1、しない場合は 0 を返す。郵便番号やメールアドレスの形式チェックにも応用できる。


5. 文字列の一部を取得(substrmb_substr)

一般形

substr(文字列, 開始位置, 取得する文字数);
mb_substr(文字列, 開始位置, 取得する文字数);

半角文字列には substr、マルチバイト文字列には mb_substr を使う。日本語に substr を使うと文字の途中でバイトが切れて文字化けするので注意。

echo substr('abcde', 1);       // 半角はsubstrでOK
echo mb_substr('カキクケコ', 2); // 日本語はmb_substrを使う

出力結果

bcde
クケコ

6. 配列に要素を追加(array_push)

一般形

array_push(配列, 追加する値1, 追加する値2, ...);
$array = ['りんご', 'みかん'];
array_push($array, 'ぶどう', 'なし');
var_dump($array);

出力結果

array(4) {
  [0]=> string(9) "りんご"
  [1]=> string(9) "みかん"
  [2]=> string(9) "ぶどう"
  [3]=> string(6) "なし"
}

array_push は複数の値をまとめて末尾に追加できる。


7. 自作関数(郵便番号のバリデーション)

一般形

function 関数名(引数1, 引数2, ...) {
    // 処理
    return 戻り値;
}

いきなり完成形のコードを書くのではなく、以下のステップで組み立てていく。

Step1. 何をしたいかを言葉にする(要件整理)

「郵便番号(例: 123-4567)を受け取って、正しい形式かどうかを判定したい」を、もう少し具体的な条件に落とし込む。

ハイフンを取り除いたときに、数字が7桁になっていればOKとする

いきなりコードを書き始めず、まず日本語で条件を書き出しておくと、後で処理の抜け漏れに気づきやすい。

Step2. 関数名を決める

何をする関数かが一目でわかる名前を付ける。PHPの組み込み関数はスネークケース(str_replaceなど)が多いが、自作関数はキャメルケースで書く現場も多い。プロジェクトの規約に合わせる。

function checkPostalCode() {}

Step3. 引数(入力)を決める

関数の中で「何を受け取って処理したいか」を考える。今回は判定したい郵便番号の文字列を外から渡してもらう必要があるので、引数を1つ用意する。

function checkPostalCode($postalCode) {}

⚠️ つまずいたポイント
最初に書いたコードは function checkPostalCode(){ ... } のように引数を用意せず、関数の中でいきなり $str という変数を使っていた。関数の外の変数は関数の中から直接見えない(スコープが違う)ため、呼び出し側から渡した値は必ず仮引数として受け取る必要がある。

Step4. 処理の流れを日本語で書き出す(疑似コード)

いきなりPHPで書かず、やることを箇条書きにする。

1. ハイフンを取り除く
2. 文字数を数える
3. 文字数が7なら true、それ以外は false を返す

Step5. 疑似コードをPHPのコードに変換する

Step4の箇条書きを、1行ずつPHPのコードに置き換えていく。

function checkPostalCode($postalCode)
{
    $replaced = str_replace('-', '', $postalCode); // 1. ハイフンを取り除く
    $length = strlen($replaced);                   // 2. 文字数を数える

    if ($length === 7) {                           // 3. 判定して返す
        return true;
    }
    return false;
}

Step6. 戻り値(出力)を決める

呼び出し側がこの関数をどう使いたいかを考える。if (checkPostalCode($postalCode)) のように条件分岐でそのまま使いたいので、戻り値は true / false の真偽値にする。

Step7. 実際に呼び出して動作確認する

最後に、想定通りの値が返ってくるか確認する。

$postalCode = '123-4567';
var_dump(checkPostalCode($postalCode));

出力結果

bool(true)

💡 講座の元コードでは処理の途中経過を確認するために var_dump($length); を関数の中に残していた。動作確認用のデバッグコードなので、完成後は消しておくと出力がスッキリする。


8. 変数のスコープ(グローバル変数・ローカル変数)

一般形

$グローバル変数名 = ;

function 関数名() {
    $ローカル変数名 = ; // 関数の外からは見えない
}

// 関数内でグローバル変数を使いたい場合
function 関数名2() {
    global $グローバル変数名;
    // ここで$グローバル変数名が使える
}
$globalVariable = 'グローバル変数です';

function checkScope()
{
    $localVariable = 'ローカル変数です';
    echo $localVariable;
}

echo $globalVariable; // 関数外なので問題なく表示できる
checkScope();          // 関数内で定義したローカル変数を関数内で表示

出力結果

グローバル変数です
ローカル変数です
  • 関数ので定義した変数(グローバル変数)は関数のからは直接参照できない
  • 関数ので定義した変数(ローカル変数)は関数のからは参照できない
  • グローバル変数を関数内で使いたい場合は、引数で渡すglobal キーワードを使う

⚠️ つまずいたポイント
元のコードは関数の外で echo $localVariable;(未定義の変数を参照)としていて、これは「スコープ外だから表示されない」という学習意図は正しいものの、実行するとPHPの警告(Warning: Undefined variable)が出る点に注意。


9. 外部ファイルの読み込み(require)

一般形

require(ファイルパス);

common.php

<?php
$commonVariable = '共通の変数です';

function commonTest()
{
    echo '外部ファイルの関数です';
}

main.php

require('common.php');

echo $commonVariable;
commonTest();

出力結果

共通の変数です
外部ファイルの関数です

require で読み込んだファイル内の変数・関数を、読み込んだ側でそのまま使うことができる。ファイルが見つからない場合は require は致命的エラー(Fatal Error)になる(似た関数の include は警告のみで処理を続行する違いがある)。


一般化(汎用化)してみる

講座で書いたコードは「この文字列専用」「この配列専用」の処理になっているものが多いので、どんな値が来ても使い回せる関数に書き直してみました。

1. 郵便番号チェックを汎用化

元のコードは「ハイフンを除いた文字数が7桁か」しか見ておらず、abc-defg のような数字以外の文字列でも桁数さえ合えば true になってしまう。正規表現でハイフンあり/なし両方の形式に対応しつつ、数字かどうかもチェックするようにした。

function isValidPostalCode(string $postalCode): bool
{
    return (bool) preg_match('/^\d{3}-?\d{4}$/', $postalCode);
}

出力結果

var_dump(isValidPostalCode('123-4567')); // bool(true)
var_dump(isValidPostalCode('1234567'));  // bool(true)  ハイフンなしでもOK
var_dump(isValidPostalCode('12-34567')); // bool(false) 桁の位置が違う
var_dump(isValidPostalCode('abc-defg')); // bool(false) 数字以外は弾ける
bool(true)
bool(true)
bool(false)
bool(false)

2. preg_match の「含まれるか確認」を汎用化

固定の「文字列」というキーワードではなく、任意のキーワードを渡せるようにした。

function containsKeyword(string $target, string $keyword): bool
{
    return (bool) preg_match('/' . preg_quote($keyword, '/') . '/u', $target);
}

出力結果

bool(true)   // '文字列' というキーワードで検索 → 含まれる
bool(false)  // '検索' というキーワードで検索 → 含まれない

3. substr / mb_substr を汎用化

半角・日本語どちらの文字列を渡しても文字化けしない、安全な切り出し関数にまとめた。

function safeSubstr(string $str, int $start, ?int $length = null): string
{
    return mb_substr($str, $start, $length);
}

出力結果

bcde     // safeSubstr('abcde', 1)
クケコ   // safeSubstr('カキクケコ', 2)

4. explode を汎用化

分割した後に前後の余分な空白まで取り除くようにした。

function splitAndTrim(string $delimiter, string $string): array
{
    return array_map('trim', explode($delimiter, $string));
}

出力結果

var_dump(splitAndTrim(',', '指定文字列で, 分割します'));
array(2) {
  [0]=> string(18) "指定文字列で"
  [1]=> string(15) "分割します"
}

5. array_push を汎用化

追加する値の個数を気にせず、可変長引数(...$items)でいくつでも渡せるようにした。

function addItems(array &$array, mixed ...$items): array
{
    array_push($array, ...$items);
    return $array;
}

出力結果

$fruits = ['りんご', 'みかん'];
var_dump(addItems($fruits, 'ぶどう', 'なし', 'もも'));
array(5) {
  [0]=> string(9) "りんご"
  [1]=> string(9) "みかん"
  [2]=> string(9) "ぶどう"
  [3]=> string(6) "なし"
  [4]=> string(6) "もも"
}

6. require を汎用化(安全な読み込み+スコープの注意点)

ファイルの存在確認をしてから読み込むことで require のFatal Errorを防ぐ関数。ただし、これを関数の中に書くと**「8. 変数のスコープ」で学んだ落とし穴**にそのままハマる。

// ❌ これだと動かない
function safeRequire(string $path): bool
{
    if (file_exists($path)) {
        require $path; // ここで作られる$commonVariableはこの関数のローカル変数になる
        return true;
    }
    return false;
}

var_dump(safeRequire('common.php'));
echo $commonVariable; // Warning: Undefined variable $commonVariable

require を関数の中に書くと、読み込まれたファイルの中で定義されている変数はその関数のローカルスコープに入ってしまい、関数の外からは見えなくなる。global を使えば解決できるが、require より前に global 宣言を書くのがポイント(後に書くと、読み込み時にできたローカル変数を空のグローバル変数で上書きしてしまい、やはり空になる)。

// ✅ globalをrequireより前に宣言する
function safeRequire(string $path): bool
{
    global $commonVariable; // requireの前に宣言しておく

    if (file_exists($path)) {
        require $path; // ここで代入される$commonVariableはグローバル変数に入る
        return true;
    }
    return false;
}

var_dump(safeRequire('common.php'));
echo $commonVariable;
commonTest();

出力結果

bool(true)
common.phpで定義された変数です
common.phpのcommonTest関数が呼ばれました

💡 汎用的な関数を書こうとしたことで、「スコープ」の理解がより深まりました。関数を跨いだ変数のやり取りは、素直に引数で渡す/戻り値で返す方が事故が少なく、global は基本的に最終手段と考えたほうが良さそうです。


まとめ

項目 ポイント
文字列の長さ 日本語は mb_strlen、半角は strlen
文字列操作 str_replace(置換)、explode(分割)、substr/mb_substr(部分取得)
正規表現 preg_match でパターンの一致判定(1 or 0)
配列 array_push で複数要素を一括追加
自作関数 引数はきちんと仮引数で受け取る。命名はキャメルケース/スネークケースの規約に合わせる
スコープ グローバル変数とローカル変数は別物。またぐには引数か global を使う
ファイル分割 require で外部ファイルの変数・関数を読み込める
汎用化 固定値専用の処理を、正規表現や可変長引数を使って「どんな入力でも使える関数」に書き直せる。globalrequireより前に宣言するのがポイント
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?