[PHP]stripcslashes関数を完全解説!Cスタイルのエスケープを元に戻す方法

PHP

こんにちは!今回は、PHPの標準関数であるstripcslashes()について詳しく解説していきます。addcslashes()でエスケープされた文字列を元に戻すための関数です!

stripcslashes関数とは?

stripcslashes()関数は、addcslashes()によってエスケープされた文字列を元の状態に戻す関数です。

C言語スタイルのバックスラッシュエスケープシーケンス(\n\t\rなど)を解釈して、対応する文字に変換します!

基本的な構文

stripcslashes(string $string): string
  • $string: エスケープを解除する文字列
  • 戻り値: エスケープが解除された文字列

エスケープシーケンスの対応表

エスケープシーケンス変換後説明
\n改行 (LF)ラインフィード
\r復帰 (CR)キャリッジリターン
\tタブ水平タブ
\v垂直タブ垂直タブ
\fフォームフィード改ページ
\\バックスラッシュバックスラッシュそのもの
\0NULL文字NULL (0x00)
\xHH16進数16進数表記の文字
\OOO8進数8進数表記の文字

基本的な使用例

シンプルな使用例

// エスケープされた文字列
$escaped = "Hello\\nWorld\\tTest";

// エスケープを解除
$unescaped = stripcslashes($escaped);
echo $unescaped . "\n";
// 出力:
// Hello
// World    Test

// バックスラッシュのエスケープ
$escaped = "C:\\\\Users\\\\Documents";
$unescaped = stripcslashes($escaped);
echo $unescaped . "\n";
// 出力: C:\Users\Documents

addcslashes()との組み合わせ

$original = "Hello\nWorld\tTest";

// エスケープ
$escaped = addcslashes($original, "\n\t");
echo "エスケープ後: " . $escaped . "\n";
// 出力: Hello\nWorld\tTest

// 元に戻す
$restored = stripcslashes($escaped);
echo "復元後: " . $restored . "\n";
// 出力: Hello
// World    Test

// 完全に一致するか確認
var_dump($original === $restored);  // bool(true)

16進数と8進数

// 16進数エスケープ
$hex = "\\x48\\x65\\x6C\\x6C\\x6F";  // "Hello"
echo stripcslashes($hex) . "\n";
// 出力: Hello

// 8進数エスケープ
$oct = "\\110\\145\\154\\154\\157";  // "Hello"
echo stripcslashes($oct) . "\n";
// 出力: Hello

// NULL文字
$withNull = "Hello\\0World";
$result = stripcslashes($withNull);
echo strlen($result) . "\n";  // 11(NULL文字を含む)

実践的な使用例

例1: データベースからのデータ復元

class DatabaseDataHandler {
    /**
     * データベースに保存する際にエスケープ
     */
    public static function escapeForStorage($data) {
        // 改行、タブ、バックスラッシュをエスケープ
        return addcslashes($data, "\n\r\t\\");
    }

    /**
     * データベースから取得したデータを復元
     */
    public static function unescapeFromStorage($data) {
        return stripcslashes($data);
    }

    /**
     * 複数フィールドの処理
     */
    public static function prepareForStorage($record) {
        $prepared = [];

        foreach ($record as $key => $value) {
            if (is_string($value)) {
                $prepared[$key] = self::escapeForStorage($value);
            } else {
                $prepared[$key] = $value;
            }
        }

        return $prepared;
    }

    /**
     * 複数フィールドの復元
     */
    public static function restoreFromStorage($record) {
        $restored = [];

        foreach ($record as $key => $value) {
            if (is_string($value)) {
                $restored[$key] = self::unescapeFromStorage($value);
            } else {
                $restored[$key] = $value;
            }
        }

        return $restored;
    }
}

// 使用例
$original = [
    'title' => 'Sample Title',
    'content' => "Line 1\nLine 2\tTabbed",
    'path' => 'C:\Users\Documents'
];

echo "=== 元のデータ ===\n";
print_r($original);

// 保存用に変換
$forStorage = DatabaseDataHandler::prepareForStorage($original);
echo "\n=== 保存用データ ===\n";
print_r($forStorage);
// content => "Line 1\\nLine 2\\tTabbed"
// path => "C:\\Users\\Documents"

// 復元
$restored = DatabaseDataHandler::restoreFromStorage($forStorage);
echo "\n=== 復元されたデータ ===\n";
print_r($restored);

// 一致確認
var_dump($original === $restored);  // bool(true)

例2: 設定ファイルの読み込み

class ConfigFileParser {
    /**
     * 設定ファイルを読み込む
     */
    public static function parseConfigFile($filename) {
        if (!file_exists($filename)) {
            throw new Exception("設定ファイルが見つかりません: {$filename}");
        }

        $lines = file($filename, FILE_IGNORE_NEW_LINES);
        $config = [];

        foreach ($lines as $line) {
            $line = trim($line);

            // コメント行と空行をスキップ
            if (empty($line) || $line[0] === '#') {
                continue;
            }

            // key=value 形式
            if (strpos($line, '=') !== false) {
                list($key, $value) = explode('=', $line, 2);
                $key = trim($key);
                $value = trim($value);

                // エスケープシーケンスを解釈
                $value = stripcslashes($value);

                $config[$key] = $value;
            }
        }

        return $config;
    }

    /**
     * 設定ファイルに書き込む
     */
    public static function writeConfigFile($filename, $config) {
        $lines = [];

        foreach ($config as $key => $value) {
            // エスケープ
            $escapedValue = addcslashes($value, "\n\r\t\\");
            $lines[] = "{$key}={$escapedValue}";
        }

        return file_put_contents($filename, implode("\n", $lines));
    }
}

// 使用例
// config.txt の内容:
// database_host=localhost
// database_path=C:\\Program Files\\MySQL
// message=Welcome!\nEnjoy your stay.

$config = ConfigFileParser::parseConfigFile('config.txt');
print_r($config);
/*
Array (
    [database_host] => localhost
    [database_path] => C:\Program Files\MySQL
    [message] => Welcome!
Enjoy your stay.
)
*/

// 新しい設定を追加
$config['log_file'] = "/var/log\tapplication.log";

// 書き込み
ConfigFileParser::writeConfigFile('config_new.txt', $config);

例3: ログファイルの処理

class LogProcessor {
    /**
     * ログエントリをエスケープ
     */
    public static function escapeLogEntry($message) {
        // 改行とタブをエスケープ(1行のログとして保存)
        return addcslashes($message, "\n\r\t");
    }

    /**
     * ログエントリを復元
     */
    public static function unescapeLogEntry($message) {
        return stripcslashes($message);
    }

    /**
     * ログファイルに書き込む
     */
    public static function writeLog($filename, $level, $message) {
        $timestamp = date('Y-m-d H:i:s');
        $escapedMessage = self::escapeLogEntry($message);

        $logEntry = "[{$timestamp}] [{$level}] {$escapedMessage}\n";

        return file_put_contents($filename, $logEntry, FILE_APPEND);
    }

    /**
     * ログファイルを読み込む
     */
    public static function readLog($filename) {
        if (!file_exists($filename)) {
            return [];
        }

        $lines = file($filename, FILE_IGNORE_NEW_LINES);
        $entries = [];

        foreach ($lines as $line) {
            if (preg_match('/^\[([\d\-: ]+)\] \[(\w+)\] (.+)$/', $line, $matches)) {
                $entries[] = [
                    'timestamp' => $matches[1],
                    'level' => $matches[2],
                    'message' => self::unescapeLogEntry($matches[3])
                ];
            }
        }

        return $entries;
    }

    /**
     * ログをフィルタリング
     */
    public static function filterLogByLevel($filename, $level) {
        $entries = self::readLog($filename);

        return array_filter($entries, function($entry) use ($level) {
            return strcasecmp($entry['level'], $level) === 0;
        });
    }
}

// 使用例
$logFile = 'application.log';

// 複数行のメッセージを書き込む
$message = "エラーが発生しました。\n詳細:\n\tファイルが見つかりません";
LogProcessor::writeLog($logFile, 'ERROR', $message);

LogProcessor::writeLog($logFile, 'INFO', 'アプリケーション起動');

// ログを読み込む
$logs = LogProcessor::readLog($logFile);
foreach ($logs as $log) {
    echo "[{$log['timestamp']}] [{$log['level']}]\n";
    echo $log['message'] . "\n\n";
}

// エラーのみ表示
$errors = LogProcessor::filterLogByLevel($logFile, 'ERROR');
print_r($errors);

例4: JSONデータの処理

class JsonDataHandler {
    /**
     * 特殊文字を含むデータをJSON用に準備
     */
    public static function prepareForJson($data) {
        if (is_array($data)) {
            return array_map([self::class, 'prepareForJson'], $data);
        }

        if (is_string($data)) {
            // バックスラッシュなどをエスケープ
            return addcslashes($data, "\0..\37");
        }

        return $data;
    }

    /**
     * JSONから復元
     */
    public static function restoreFromJson($data) {
        if (is_array($data)) {
            return array_map([self::class, 'restoreFromJson'], $data);
        }

        if (is_string($data)) {
            return stripcslashes($data);
        }

        return $data;
    }

    /**
     * 安全にJSONをデコード
     */
    public static function safeJsonDecode($json) {
        $data = json_decode($json, true);

        if (json_last_error() !== JSON_ERROR_NONE) {
            throw new Exception('JSON decode error: ' . json_last_error_msg());
        }

        return self::restoreFromJson($data);
    }
}

// 使用例
$data = [
    'name' => 'John Doe',
    'message' => "Hello\nWorld\tTest",
    'path' => 'C:\Users\Documents'
];

$prepared = JsonDataHandler::prepareForJson($data);
$json = json_encode($prepared);
echo "JSON: " . $json . "\n";

$decoded = JsonDataHandler::safeJsonDecode($json);
print_r($decoded);

例5: CSVファイルの処理

class CsvHandler {
    /**
     * CSVフィールドをエスケープ
     */
    public static function escapeField($field) {
        // 改行、タブ、カンマをエスケープ
        return addcslashes($field, "\n\r\t,");
    }

    /**
     * CSVフィールドを復元
     */
    public static function unescapeField($field) {
        return stripcslashes($field);
    }

    /**
     * CSV行を生成
     */
    public static function generateCsvLine($fields) {
        $escaped = array_map([self::class, 'escapeField'], $fields);
        return implode(',', $escaped);
    }

    /**
     * CSV行をパース
     */
    public static function parseCsvLine($line) {
        $fields = explode(',', $line);
        return array_map([self::class, 'unescapeField'], $fields);
    }

    /**
     * CSVファイルに書き込む
     */
    public static function writeCsv($filename, $data) {
        $lines = [];

        foreach ($data as $row) {
            $lines[] = self::generateCsvLine($row);
        }

        return file_put_contents($filename, implode("\n", $lines));
    }

    /**
     * CSVファイルを読み込む
     */
    public static function readCsv($filename) {
        if (!file_exists($filename)) {
            return [];
        }

        $lines = file($filename, FILE_IGNORE_NEW_LINES);
        $data = [];

        foreach ($lines as $line) {
            $data[] = self::parseCsvLine($line);
        }

        return $data;
    }
}

// 使用例
$data = [
    ['Name', 'Description', 'Path'],
    ['John Doe', "Line 1\nLine 2", 'C:\Users\John'],
    ['Jane Smith', 'Simple, text', 'D:\Documents'],
];

// CSVに書き込む
CsvHandler::writeCsv('data.csv', $data);

// CSVから読み込む
$loaded = CsvHandler::readCsv('data.csv');
print_r($loaded);

// 元のデータと一致するか確認
var_dump($data === $loaded);

例6: シリアライズデータの処理

class SerializedDataHandler {
    /**
     * データをシリアライズして保存
     */
    public static function serialize($data) {
        $serialized = serialize($data);

        // バイナリセーフなエスケープ
        return addcslashes($serialized, "\0..\37");
    }

    /**
     * シリアライズされたデータを復元
     */
    public static function unserialize($data) {
        $unescaped = stripcslashes($data);
        return unserialize($unescaped);
    }

    /**
     * ファイルに保存
     */
    public static function saveToFile($filename, $data) {
        $serialized = self::serialize($data);
        return file_put_contents($filename, $serialized);
    }

    /**
     * ファイルから読み込み
     */
    public static function loadFromFile($filename) {
        if (!file_exists($filename)) {
            return null;
        }

        $content = file_get_contents($filename);
        return self::unserialize($content);
    }
}

// 使用例
$data = [
    'user' => 'admin',
    'settings' => [
        'theme' => 'dark',
        'language' => 'ja',
        'notifications' => true
    ],
    'last_login' => time()
];

// ファイルに保存
SerializedDataHandler::saveToFile('session.dat', $data);

// ファイルから読み込み
$loaded = SerializedDataHandler::loadFromFile('session.dat');
print_r($loaded);

// 一致確認
var_dump($data == $loaded);

例7: コマンドライン引数の処理

class CommandLineHelper {
    /**
     * 引数をエスケープ
     */
    public static function escapeArgument($arg) {
        // スペース、タブ、改行をエスケープ
        return addcslashes($arg, " \t\n\r");
    }

    /**
     * 引数を復元
     */
    public static function unescapeArgument($arg) {
        return stripcslashes($arg);
    }

    /**
     * コマンドライン文字列を生成
     */
    public static function buildCommandLine($command, $args) {
        $escapedArgs = array_map([self::class, 'escapeArgument'], $args);
        return $command . ' ' . implode(' ', $escapedArgs);
    }

    /**
     * コマンドライン文字列をパース
     */
    public static function parseCommandLine($commandLine) {
        $parts = explode(' ', $commandLine);
        $command = array_shift($parts);

        $args = array_map([self::class, 'unescapeArgument'], $parts);

        return [
            'command' => $command,
            'args' => $args
        ];
    }
}

// 使用例
$command = 'myapp';
$args = [
    '--input=file with spaces.txt',
    "--message=Line 1\nLine 2",
    '--verbose'
];

// コマンドライン文字列を生成
$commandLine = CommandLineHelper::buildCommandLine($command, $args);
echo "コマンドライン:\n{$commandLine}\n";

// パース
$parsed = CommandLineHelper::parseCommandLine($commandLine);
print_r($parsed);

stripslashes()との違い

// stripslashes(): シンプルなバックスラッシュの除去
$str1 = "Hello\\nWorld";
echo stripslashes($str1) . "\n";
// 出力: Hello\nWorld(バックスラッシュのみ除去、\nは解釈されない)

// stripcslashes(): エスケープシーケンスを解釈
$str2 = "Hello\\nWorld";
echo stripcslashes($str2) . "\n";
// 出力:
// Hello
// World(\nが改行に変換される)

// もう一つの例
$path = "C:\\\\Users\\\\Documents";
echo "stripslashes: " . stripslashes($path) . "\n";
// C:\Users\Documents

echo "stripcslashes: " . stripcslashes($path) . "\n";
// C:\Users\Documents

// 16進数エスケープの違い
$hex = "\\x48\\x65\\x6C\\x6C\\x6F";
echo "stripslashes: " . stripslashes($hex) . "\n";
// \x48\x65\x6C\x6C\x6F(解釈されない)

echo "stripcslashes: " . stripcslashes($hex) . "\n";
// Hello(16進数が解釈される)

注意点と制限事項

バイナリデータの扱い

// NULL文字を含むデータ
$data = "Hello\\0World";
$result = stripcslashes($data);

echo "長さ: " . strlen($result) . "\n";  // 11
echo "表示: " . $result . "\n";          // HelloWorld(NULLは表示されない)

// バイナリセーフな処理が必要な場合は注意

不完全なエスケープシーケンス

// 不完全な16進数エスケープ
$incomplete = "\\x4";  // 1桁のみ
echo stripcslashes($incomplete) . "\n";
// 結果は環境依存

// 不完全な8進数エスケープ
$incomplete = "\\7";
echo stripcslashes($incomplete) . "\n";

パフォーマンス

// 大量のデータ処理
$largeData = str_repeat("Line\\n", 10000);

$start = microtime(true);
$result = stripcslashes($largeData);
$time = microtime(true) - $start;

echo "処理時間: {$time}秒\n";
echo "元のサイズ: " . strlen($largeData) . " bytes\n";
echo "結果のサイズ: " . strlen($result) . " bytes\n";

実用的なヘルパークラス

class EscapeHelper {
    /**
     * 安全にエスケープ
     */
    public static function safeCslashesEncode($data) {
        if (is_string($data)) {
            return addcslashes($data, "\0..\37");
        }

        if (is_array($data)) {
            return array_map([self::class, 'safeCslashesEncode'], $data);
        }

        return $data;
    }

    /**
     * 安全にアンエスケープ
     */
    public static function safeCslashesDecode($data) {
        if (is_string($data)) {
            return stripcslashes($data);
        }

        if (is_array($data)) {
            return array_map([self::class, 'safeCslashesDecode'], $data);
        }

        return $data;
    }

    /**
     * エスケープシーケンスを可視化
     */
    public static function visualizeEscapes($string) {
        $replacements = [
            "\n" => "\\n",
            "\r" => "\\r",
            "\t" => "\\t",
            "\0" => "\\0",
            "\\" => "\\\\"
        ];

        return strtr($string, $replacements);
    }
}

// 使用例
$data = [
    'text' => "Line 1\nLine 2",
    'path' => 'C:\Users\Documents',
    'nested' => [
        'field' => "Tab\tSeparated"
    ]
];

$encoded = EscapeHelper::safeCslashesEncode($data);
echo "エンコード後:\n";
print_r($encoded);

$decoded = EscapeHelper::safeCslashesDecode($encoded);
echo "\nデコード後:\n";
print_r($decoded);

// 可視化
echo "\n可視化:\n";
echo EscapeHelper::visualizeEscapes($data['text']) . "\n";

まとめ

stripcslashes()関数の特徴をまとめると:

できること:

  • Cスタイルのエスケープシーケンスを解釈
  • addcslashes()の逆操作
  • 16進数・8進数表記の変換
  • NULL文字の処理

主なエスケープシーケンス:

  • \n: 改行
  • \t: タブ
  • \r: 復帰
  • \\: バックスラッシュ
  • \xHH: 16進数
  • \OOO: 8進数

推奨される使用場面:

  • 設定ファイルの読み込み
  • ログファイルの処理
  • データベースからのデータ復元
  • シリアライズデータの処理
  • CSVファイルの処理

注意点:

  • バイナリデータの扱いに注意
  • 不完全なエスケープシーケンス
  • NULL文字を含むデータ

関連関数:

  • addcslashes(): Cスタイルでエスケープ
  • stripslashes(): バックスラッシュのみ除去
  • addslashes(): クォートをエスケープ

stripcslashes()addcslashes()とペアで使用することで、特殊文字を含むデータを安全に保存・復元できます。適切に使いこなすことで、堅牢なデータ処理システムを構築できます!

タイトルとURLをコピーしました