はじめに
PHPでPOSIX関数を使用する際、処理の失敗理由を知りたいと思ったことはありませんか?
POSIX関数は失敗時に単にfalseを返すだけで、詳細なエラー情報を提供しないことが多いです。しかし、失敗の詳細な理由はエラー番号として内部に保存されています。
その最後のエラー番号を取得するのが**posix_get_last_error関数**です。この関数はposix_errnoのエイリアス(別名)であり、より分かりやすい名前でエラー情報にアクセスできます。
この記事では、posix_get_last_errorの基本から実践的なエラーハンドリング方法まで、詳しく解説します。
posix_get_last_errorとは?
posix_get_last_errorは、最後に失敗したPOSIX関数のエラー番号を返す関数です。posix_errnoの別名(エイリアス)として機能します。
基本構文
posix_get_last_error(): int
パラメータ
この関数はパラメータを取りません。
戻り値
- 整数: 最後に失敗したPOSIX関数のエラー番号
- 0: エラーなし、または前回の操作が成功
posix_errnoとの違い
<?php
// 両者は完全に同じ機能を持つ
$result = posix_kill(99999, SIGTERM);
if ($result === false) {
$errno1 = posix_errno();
$errno2 = posix_get_last_error();
echo "posix_errno(): {$errno1}\n";
echo "posix_get_last_error(): {$errno2}\n";
echo "同じ値: " . ($errno1 === $errno2 ? 'はい' : 'いいえ') . "\n";
// 出力:
// posix_errno(): 3
// posix_get_last_error(): 3
// 同じ値: はい
}
// どちらを使用しても結果は同じ
// posix_get_last_error()の方が名前から意図が明確
?>
対応環境
- POSIX準拠システム(Linux、Unix、macOS)
- Windows では利用不可
- POSIX拡張モジュールが必要
対応バージョン
- PHP 4.2.0 以降で使用可能
基本的な使い方
最後のエラーを取得
<?php
// 存在しないファイルを開こうとする
$fd = posix_open('/nonexistent/file.txt', O_RDONLY);
if ($fd === false) {
$errno = posix_get_last_error();
echo "エラー番号: {$errno}\n";
// エラーメッセージを取得
$error_message = posix_strerror($errno);
echo "エラーメッセージ: {$error_message}\n";
}
// 出力例:
// エラー番号: 2
// エラーメッセージ: No such file or directory
?>
エラーの有無をチェック
<?php
function checkLastError() {
$errno = posix_get_last_error();
if ($errno === 0) {
echo "最後の操作は成功しました\n";
return true;
} else {
echo "最後の操作は失敗しました\n";
echo "エラー: " . posix_strerror($errno) . "\n";
return false;
}
}
// 成功する操作
posix_getpid();
checkLastError();
// 失敗する操作
posix_kill(99999, SIGTERM);
checkLastError();
?>
実践的な使用例
例1: エラーハンドリングクラス
<?php
class PosixErrorManager {
/**
* 最後のエラーを取得
*/
public static function getLastError() {
$errno = posix_get_last_error();
if ($errno === 0) {
return null;
}
return [
'errno' => $errno,
'message' => posix_strerror($errno),
'description' => self::getErrorDescription($errno)
];
}
/**
* エラーの日本語説明を取得
*/
private static function getErrorDescription($errno) {
return match($errno) {
EPERM => '操作が許可されていません。root権限が必要な可能性があります',
ENOENT => 'ファイルまたはディレクトリが見つかりません',
ESRCH => '指定されたプロセスが存在しません',
EINTR => 'システムコールが割り込まれました',
EIO => '入出力エラーが発生しました',
EACCES => 'アクセスが拒否されました。権限を確認してください',
EEXIST => 'ファイルまたはディレクトリが既に存在します',
ENOTDIR => 'ディレクトリではありません',
EISDIR => 'ディレクトリです。ファイルとして処理できません',
EINVAL => '無効な引数が指定されました',
EMFILE => '開いているファイルが多すぎます。リソース制限を確認してください',
ENOSPC => 'ディスク容量が不足しています',
EROFS => '読み取り専用ファイルシステムです',
default => posix_strerror($errno)
};
}
/**
* エラーがあるかチェック
*/
public static function hasError() {
return posix_get_last_error() !== 0;
}
/**
* エラーをクリア(次のPOSIX操作で上書きされる)
*/
public static function clearError() {
// エラーをクリアするには成功する操作を実行
posix_getpid();
}
/**
* エラー情報を表示
*/
public static function displayLastError($prefix = '') {
$error = self::getLastError();
if ($error === null) {
echo "{$prefix}エラーはありません\n";
return;
}
echo "{$prefix}エラーが発生しました:\n";
echo " エラー番号: {$error['errno']}\n";
echo " メッセージ: {$error['message']}\n";
echo " 詳細: {$error['description']}\n";
}
}
// 使用例
posix_kill(99999, SIGTERM);
if (PosixErrorManager::hasError()) {
PosixErrorManager::displayLastError('【POSIX操作】');
}
?>
例2: リトライ機能付きPOSIX操作
<?php
class PosixRetryHandler {
/**
* リトライ可能なエラーかどうか
*/
private static function isRetryableError($errno) {
return in_array($errno, [
EINTR, // システムコールの中断
EAGAIN, // リソースが一時的に利用不可
EBUSY // リソースがビジー
]);
}
/**
* リトライ付きで操作を実行
*/
public static function executeWithRetry(
callable $operation,
$max_retries = 3,
$retry_delay_ms = 100
) {
$attempt = 0;
while ($attempt <= $max_retries) {
$result = $operation();
if ($result !== false) {
return $result; // 成功
}
$errno = posix_get_last_error();
// リトライ不可能なエラー
if (!self::isRetryableError($errno)) {
throw new RuntimeException(
"操作失敗: " . posix_strerror($errno),
$errno
);
}
$attempt++;
if ($attempt <= $max_retries) {
echo "リトライ {$attempt}/{$max_retries} (errno: {$errno})...\n";
usleep($retry_delay_ms * 1000);
}
}
$errno = posix_get_last_error();
throw new RuntimeException(
"最大リトライ回数に到達: " . posix_strerror($errno),
$errno
);
}
}
// 使用例
try {
$result = PosixRetryHandler::executeWithRetry(
fn() => posix_kill($pid, SIGTERM),
max_retries: 5,
retry_delay_ms: 200
);
echo "シグナル送信成功\n";
} catch (RuntimeException $e) {
echo "エラー: " . $e->getMessage() . "\n";
}
?>
例3: エラーコンテキストの追跡
<?php
class PosixOperationTracker {
private static $operations = [];
/**
* 操作を実行してトラッキング
*/
public static function track($operation_name, callable $operation) {
$start_time = microtime(true);
$result = $operation();
$end_time = microtime(true);
$record = [
'name' => $operation_name,
'success' => $result !== false,
'duration' => $end_time - $start_time,
'timestamp' => date('Y-m-d H:i:s'),
];
if ($result === false) {
$errno = posix_get_last_error();
$record['errno'] = $errno;
$record['error'] = posix_strerror($errno);
}
self::$operations[] = $record;
return $result;
}
/**
* トラッキング履歴を取得
*/
public static function getHistory() {
return self::$operations;
}
/**
* 失敗した操作のみ取得
*/
public static function getFailures() {
return array_filter(
self::$operations,
fn($op) => !$op['success']
);
}
/**
* 統計情報を取得
*/
public static function getStatistics() {
$total = count(self::$operations);
$successes = count(array_filter(
self::$operations,
fn($op) => $op['success']
));
$failures = $total - $successes;
return [
'total' => $total,
'successes' => $successes,
'failures' => $failures,
'success_rate' => $total > 0 ? ($successes / $total) * 100 : 0
];
}
/**
* レポートを表示
*/
public static function displayReport() {
$stats = self::getStatistics();
echo "=== POSIX操作レポート ===\n\n";
echo "総操作数: {$stats['total']}\n";
echo "成功: {$stats['successes']}\n";
echo "失敗: {$stats['failures']}\n";
echo "成功率: " . number_format($stats['success_rate'], 2) . "%\n\n";
$failures = self::getFailures();
if (!empty($failures)) {
echo "失敗した操作:\n";
foreach ($failures as $i => $op) {
echo ($i + 1) . ". {$op['name']}\n";
echo " 時刻: {$op['timestamp']}\n";
echo " errno: {$op['errno']}\n";
echo " エラー: {$op['error']}\n\n";
}
}
}
/**
* 履歴をクリア
*/
public static function clear() {
self::$operations = [];
}
}
// 使用例
PosixOperationTracker::track(
'プロセス情報取得',
fn() => posix_getpid()
);
PosixOperationTracker::track(
'存在しないプロセスへのシグナル送信',
fn() => posix_kill(99999, SIGTERM)
);
PosixOperationTracker::track(
'ユーザー情報取得',
fn() => posix_getpwuid(posix_getuid())
);
PosixOperationTracker::displayReport();
?>
例4: 詳細なファイル操作エラーレポート
<?php
class FileOperationError {
/**
* ファイル操作を試行してエラーを詳細報告
*/
public static function tryFileOperation($filepath, $operation_type) {
echo "=== ファイル操作: {$operation_type} ===\n";
echo "対象: {$filepath}\n\n";
$result = match($operation_type) {
'read' => self::tryRead($filepath),
'write' => self::tryWrite($filepath),
'execute' => self::tryExecute($filepath),
'delete' => self::tryDelete($filepath),
default => ['success' => false, 'error' => '不明な操作']
};
if ($result['success']) {
echo "✓ 操作成功\n";
} else {
echo "✗ 操作失敗\n";
self::displayErrorDetails($result);
}
return $result;
}
private static function tryRead($filepath) {
if (!file_exists($filepath)) {
return [
'success' => false,
'error' => 'ファイルが存在しません',
'errno' => ENOENT
];
}
$result = posix_access($filepath, POSIX_R_OK);
if ($result === false) {
return [
'success' => false,
'error' => '読み取り権限がありません',
'errno' => posix_get_last_error(),
'suggestion' => 'chmod +r または所有者として実行してください'
];
}
return ['success' => true];
}
private static function tryWrite($filepath) {
if (!file_exists($filepath)) {
// 親ディレクトリの書き込み権限をチェック
$parent = dirname($filepath);
if (!posix_access($parent, POSIX_W_OK | POSIX_X_OK)) {
return [
'success' => false,
'error' => '親ディレクトリへの書き込み権限がありません',
'errno' => posix_get_last_error(),
'suggestion' => "chmod +w {$parent}"
];
}
return ['success' => true, 'note' => '新規ファイル作成可能'];
}
$result = posix_access($filepath, POSIX_W_OK);
if ($result === false) {
return [
'success' => false,
'error' => '書き込み権限がありません',
'errno' => posix_get_last_error(),
'suggestion' => 'chmod +w または所有者として実行してください'
];
}
return ['success' => true];
}
private static function tryExecute($filepath) {
if (!file_exists($filepath)) {
return [
'success' => false,
'error' => 'ファイルが存在しません',
'errno' => ENOENT
];
}
if (is_dir($filepath)) {
return [
'success' => false,
'error' => 'ディレクトリは実行できません',
'errno' => EISDIR
];
}
$result = posix_access($filepath, POSIX_X_OK);
if ($result === false) {
return [
'success' => false,
'error' => '実行権限がありません',
'errno' => posix_get_last_error(),
'suggestion' => 'chmod +x で実行権限を付与してください'
];
}
return ['success' => true];
}
private static function tryDelete($filepath) {
if (!file_exists($filepath)) {
return [
'success' => false,
'error' => 'ファイルが存在しません',
'errno' => ENOENT
];
}
$parent = dirname($filepath);
if (!posix_access($parent, POSIX_W_OK | POSIX_X_OK)) {
return [
'success' => false,
'error' => '親ディレクトリへの書き込み権限がありません',
'errno' => posix_get_last_error(),
'suggestion' => "chmod +w {$parent}"
];
}
return ['success' => true];
}
private static function displayErrorDetails($result) {
echo "\nエラー詳細:\n";
if (isset($result['errno'])) {
echo " エラー番号: {$result['errno']}\n";
echo " システムメッセージ: " . posix_strerror($result['errno']) . "\n";
}
echo " 説明: {$result['error']}\n";
if (isset($result['suggestion'])) {
echo "\n対処方法:\n";
echo " {$result['suggestion']}\n";
}
}
}
// 使用例
FileOperationError::tryFileOperation('/etc/shadow', 'read');
echo "\n";
FileOperationError::tryFileOperation('/tmp/test.txt', 'write');
?>
例5: システムコールのデバッグヘルパー
<?php
class PosixDebugger {
private static $debug_mode = false;
private static $log = [];
/**
* デバッグモードを有効化
*/
public static function enable() {
self::$debug_mode = true;
}
/**
* デバッグモードを無効化
*/
public static function disable() {
self::$debug_mode = false;
}
/**
* POSIX関数をデバッグ実行
*/
public static function call($function_name, ...$args) {
$start = microtime(true);
// 関数を実行
$result = call_user_func($function_name, ...$args);
$duration = microtime(true) - $start;
$errno = posix_get_last_error();
$log_entry = [
'function' => $function_name,
'args' => $args,
'result' => $result,
'success' => $result !== false,
'errno' => $errno,
'error' => $errno !== 0 ? posix_strerror($errno) : null,
'duration' => $duration,
'timestamp' => microtime(true)
];
self::$log[] = $log_entry;
if (self::$debug_mode) {
self::printCallInfo($log_entry);
}
return $result;
}
private static function printCallInfo($entry) {
$args_str = implode(', ', array_map(function($arg) {
if (is_string($arg)) return "'{$arg}'";
if (is_bool($arg)) return $arg ? 'true' : 'false';
if (is_null($arg)) return 'null';
return (string)$arg;
}, $entry['args']));
echo "[DEBUG] {$entry['function']}({$args_str})\n";
echo " 結果: " . var_export($entry['result'], true) . "\n";
echo " 成功: " . ($entry['success'] ? 'はい' : 'いいえ') . "\n";
if (!$entry['success']) {
echo " errno: {$entry['errno']}\n";
echo " エラー: {$entry['error']}\n";
}
echo " 実行時間: " . number_format($entry['duration'] * 1000, 3) . "ms\n";
echo "\n";
}
/**
* ログを取得
*/
public static function getLog() {
return self::$log;
}
/**
* ログをクリア
*/
public static function clearLog() {
self::$log = [];
}
/**
* サマリーを表示
*/
public static function displaySummary() {
$total = count(self::$log);
$successes = count(array_filter(self::$log, fn($e) => $e['success']));
$failures = $total - $successes;
echo "=== デバッグサマリー ===\n";
echo "総呼び出し数: {$total}\n";
echo "成功: {$successes}\n";
echo "失敗: {$failures}\n";
if ($failures > 0) {
echo "\n失敗した呼び出し:\n";
foreach (self::$log as $entry) {
if (!$entry['success']) {
echo " {$entry['function']}() - errno: {$entry['errno']}, {$entry['error']}\n";
}
}
}
}
}
// 使用例
PosixDebugger::enable();
PosixDebugger::call('posix_getpid');
PosixDebugger::call('posix_kill', 99999, SIGTERM);
PosixDebugger::call('posix_getpwuid', posix_getuid());
PosixDebugger::call('posix_access', '/etc/shadow', POSIX_R_OK);
echo "\n";
PosixDebugger::displaySummary();
?>
まとめ
posix_get_last_errorは、POSIX関数の最後のエラー番号を取得するための関数です。
主な特徴:
- ✅
posix_errnoのエイリアス(完全に同じ機能) - ✅ 最後に失敗したPOSIX関数のエラー番号を返す
- ✅
posix_strerrorと組み合わせて詳細なエラーメッセージを取得 - ✅ より分かりやすい関数名
使い分け:
posix_errno()– 短く書きたい場合posix_get_last_error()– コードの可読性を重視する場合
主なエラー番号:
EPERM(1) – 権限エラーENOENT(2) – ファイル/ディレクトリが存在しないESRCH(3) – プロセスが存在しないEINTR(4) – システムコールが中断EACCES(13) – アクセス拒否EEXIST(17) – 既に存在EINVAL(22) – 無効な引数EMFILE(24) – ファイル記述子の上限
ベストプラクティス:
- POSIX関数の戻り値を常にチェック
- 失敗時は必ずエラー番号を確認
- エラーハンドリングクラスで統一的に処理
- デバッグ用のロギング機能を実装
関連関数:
posix_errno()– 同じ機能の別名posix_strerror()– エラー番号からメッセージを取得
この関数を活用して、より堅牢で保守性の高いPOSIXプログラミングを実現しましょう!
参考リンク
この記事が役に立ったら、ぜひシェアしてください!
