[PHP]is_resource関数完全ガイド – リソース型判定の基本から応用テクニックまで

PHP

PHPでファイル操作やデータベース接続、画像処理などを行う際、「この変数はリソース型なの?」という判定が必要になる場面があります。そんな時に重要な役割を果たすのがis_resource関数です。この記事では、is_resource関数の基本的な使い方から実践的な活用方法まで、初心者にも分かりやすく解説します。

is_resource関数とは?

is_resource関数は、指定された変数がリソース型であるかどうかを判定するPHPの組み込み関数です。リソース型とは、外部リソース(ファイル、データベース接続、画像データなど)への参照を保持する特殊なPHPの変数型です。

基本的な構文

bool is_resource(mixed $value)

パラメータ:

  • $value:チェックしたい値

戻り値:

  • true:値がリソース型の場合
  • false:値がリソース型以外の場合

リソース型の基本理解

リソース型とは?

リソース型は、PHPの基本的なデータ型(整数、文字列、配列など)とは異なり、外部システムとの接続や特別なデータ構造を表現するために使用されます。

<?php
// ファイルリソース
$file = fopen('example.txt', 'r');
var_dump(is_resource($file));  // bool(true)
var_dump(gettype($file));      // string(8) "resource"

// データベース接続リソース(MySQLi)
$mysqli = new mysqli('localhost', 'user', 'pass', 'database');
$result = $mysqli->query('SELECT * FROM users');
var_dump(is_resource($result));  // bool(false) - MySQLiはオブジェクト

// cURLリソース
$curl = curl_init();
var_dump(is_resource($curl));  // bool(true)

// 画像リソース
$image = imagecreate(100, 100);
var_dump(is_resource($image));  // bool(true)
?>

基本的な使用例

シンプルなリソースチェック

<?php
function checkResourceType($value) {
    if (is_resource($value)) {
        echo "これはリソース型です: " . get_resource_type($value) . "\n";
    } else {
        echo "これはリソース型ではありません: " . gettype($value) . "\n";
    }
}

// 各種リソースのテスト
$file = fopen('test.txt', 'w');
$curl = curl_init();
$image = imagecreate(50, 50);
$string = "Hello World";
$array = [1, 2, 3];

checkResourceType($file);   // これはリソース型です: stream
checkResourceType($curl);   // これはリソース型です: curl
checkResourceType($image);  // これはリソース型です: gd
checkResourceType($string); // これはリソース型ではありません: string
checkResourceType($array);  // これはリソース型ではありません: array

// リソースのクリーンアップ
fclose($file);
curl_close($curl);
imagedestroy($image);
?>

get_resource_type()との組み合わせ

<?php
function analyzeResource($resource) {
    if (is_resource($resource)) {
        $type = get_resource_type($resource);
        echo "リソースタイプ: {$type}\n";
        
        switch ($type) {
            case 'stream':
                echo "ファイルまたはストリームリソースです\n";
                break;
            case 'curl':
                echo "cURLリソースです\n";
                break;
            case 'gd':
                echo "画像リソースです\n";
                break;
            case 'mysql link':
                echo "MySQLデータベース接続です\n";
                break;
            default:
                echo "その他のリソースです\n";
        }
    } else {
        echo "リソースではありません\n";
    }
}

// テスト
$resources = [
    fopen('php://memory', 'r+'),
    curl_init(),
    imagecreate(10, 10),
    "not a resource"
];

foreach ($resources as $index => $resource) {
    echo "リソース {$index}: ";
    analyzeResource($resource);
    echo "---\n";
}
?>

実践的な活用シーン

1. ファイル操作での安全性チェック

<?php
class FileManager {
    private $fileHandle;
    
    public function openFile($filename, $mode = 'r') {
        $this->fileHandle = fopen($filename, $mode);
        
        if (!is_resource($this->fileHandle)) {
            throw new RuntimeException("ファイルを開けませんでした: {$filename}");
        }
        
        return $this;
    }
    
    public function readLine() {
        if (!is_resource($this->fileHandle)) {
            throw new RuntimeException("有効なファイルハンドルがありません");
        }
        
        return fgets($this->fileHandle);
    }
    
    public function writeData($data) {
        if (!is_resource($this->fileHandle)) {
            throw new RuntimeException("有効なファイルハンドルがありません");
        }
        
        return fwrite($this->fileHandle, $data);
    }
    
    public function closeFile() {
        if (is_resource($this->fileHandle)) {
            fclose($this->fileHandle);
            $this->fileHandle = null;
        }
    }
    
    public function __destruct() {
        $this->closeFile();
    }
}

// 使用例
try {
    $fileManager = new FileManager();
    $fileManager->openFile('example.txt', 'w+');
    $fileManager->writeData("Hello, World!\n");
    $fileManager->writeData("This is a test file.\n");
    $fileManager->closeFile();
    
    echo "ファイル操作が完了しました\n";
} catch (RuntimeException $e) {
    echo "エラー: " . $e->getMessage() . "\n";
}
?>

2. cURLリソースの管理

<?php
class HttpClient {
    private $curlHandle;
    
    public function __construct() {
        $this->curlHandle = curl_init();
        
        if (!is_resource($this->curlHandle)) {
            throw new RuntimeException("cURLリソースの初期化に失敗しました");
        }
        
        // デフォルト設定
        curl_setopt_array($this->curlHandle, [
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_TIMEOUT => 30,
            CURLOPT_FOLLOWLOCATION => true,
            CURLOPT_SSL_VERIFYPEER => false,
        ]);
    }
    
    public function get($url) {
        if (!is_resource($this->curlHandle)) {
            throw new RuntimeException("無効なcURLリソースです");
        }
        
        curl_setopt($this->curlHandle, CURLOPT_URL, $url);
        curl_setopt($this->curlHandle, CURLOPT_HTTPGET, true);
        
        $response = curl_exec($this->curlHandle);
        
        if ($response === false) {
            throw new RuntimeException("cURLエラー: " . curl_error($this->curlHandle));
        }
        
        return $response;
    }
    
    public function post($url, $data) {
        if (!is_resource($this->curlHandle)) {
            throw new RuntimeException("無効なcURLリソースです");
        }
        
        curl_setopt_array($this->curlHandle, [
            CURLOPT_URL => $url,
            CURLOPT_POST => true,
            CURLOPT_POSTFIELDS => $data,
        ]);
        
        $response = curl_exec($this->curlHandle);
        
        if ($response === false) {
            throw new RuntimeException("cURLエラー: " . curl_error($this->curlHandle));
        }
        
        return $response;
    }
    
    public function getInfo() {
        if (!is_resource($this->curlHandle)) {
            return [];
        }
        
        return curl_getinfo($this->curlHandle);
    }
    
    public function __destruct() {
        if (is_resource($this->curlHandle)) {
            curl_close($this->curlHandle);
        }
    }
}

// 使用例
try {
    $client = new HttpClient();
    $response = $client->get('https://api.example.com/data');
    $info = $client->getInfo();
    
    echo "HTTPステータス: " . $info['http_code'] . "\n";
    echo "レスポンス長: " . strlen($response) . " bytes\n";
} catch (RuntimeException $e) {
    echo "エラー: " . $e->getMessage() . "\n";
}
?>

3. 画像処理での利用

<?php
class ImageProcessor {
    private $imageResource;
    
    public function createImage($width, $height) {
        $this->imageResource = imagecreatetruecolor($width, $height);
        
        if (!is_resource($this->imageResource)) {
            throw new RuntimeException("画像リソースの作成に失敗しました");
        }
        
        return $this;
    }
    
    public function loadImage($filename) {
        $info = getimagesize($filename);
        
        if ($info === false) {
            throw new InvalidArgumentException("無効な画像ファイルです: {$filename}");
        }
        
        switch ($info[2]) {
            case IMAGETYPE_JPEG:
                $this->imageResource = imagecreatefromjpeg($filename);
                break;
            case IMAGETYPE_PNG:
                $this->imageResource = imagecreatefrompng($filename);
                break;
            case IMAGETYPE_GIF:
                $this->imageResource = imagecreatefromgif($filename);
                break;
            default:
                throw new InvalidArgumentException("サポートされていない画像形式です");
        }
        
        if (!is_resource($this->imageResource)) {
            throw new RuntimeException("画像の読み込みに失敗しました: {$filename}");
        }
        
        return $this;
    }
    
    public function resize($newWidth, $newHeight) {
        if (!is_resource($this->imageResource)) {
            throw new RuntimeException("画像リソースがありません");
        }
        
        $originalWidth = imagesx($this->imageResource);
        $originalHeight = imagesy($this->imageResource);
        
        $newImage = imagecreatetruecolor($newWidth, $newHeight);
        
        if (!is_resource($newImage)) {
            throw new RuntimeException("リサイズ用画像の作成に失敗しました");
        }
        
        imagecopyresampled(
            $newImage,
            $this->imageResource,
            0, 0, 0, 0,
            $newWidth, $newHeight,
            $originalWidth, $originalHeight
        );
        
        imagedestroy($this->imageResource);
        $this->imageResource = $newImage;
        
        return $this;
    }
    
    public function saveAs($filename, $quality = 90) {
        if (!is_resource($this->imageResource)) {
            throw new RuntimeException("画像リソースがありません");
        }
        
        $extension = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
        
        switch ($extension) {
            case 'jpg':
            case 'jpeg':
                imagejpeg($this->imageResource, $filename, $quality);
                break;
            case 'png':
                imagepng($this->imageResource, $filename);
                break;
            case 'gif':
                imagegif($this->imageResource, $filename);
                break;
            default:
                throw new InvalidArgumentException("サポートされていない保存形式です: {$extension}");
        }
        
        return $this;
    }
    
    public function __destruct() {
        if (is_resource($this->imageResource)) {
            imagedestroy($this->imageResource);
        }
    }
}

// 使用例
try {
    $processor = new ImageProcessor();
    $processor->createImage(200, 200)
              ->saveAs('created_image.png');
    
    echo "画像処理が完了しました\n";
} catch (Exception $e) {
    echo "エラー: " . $e->getMessage() . "\n";
}
?>

リソース管理のベストプラクティス

1. リソースリークの防止

<?php
class ResourceManager {
    private $resources = [];
    
    public function addResource($resource, $name = null) {
        if (!is_resource($resource)) {
            throw new InvalidArgumentException("有効なリソースを指定してください");
        }
        
        $key = $name ?: uniqid('resource_');
        $this->resources[$key] = [
            'resource' => $resource,
            'type' => get_resource_type($resource),
            'created' => time()
        ];
        
        return $key;
    }
    
    public function getResource($key) {
        if (!isset($this->resources[$key])) {
            throw new InvalidArgumentException("リソースが見つかりません: {$key}");
        }
        
        $resource = $this->resources[$key]['resource'];
        
        if (!is_resource($resource)) {
            unset($this->resources[$key]);
            throw new RuntimeException("リソースが無効になっています: {$key}");
        }
        
        return $resource;
    }
    
    public function closeResource($key) {
        if (!isset($this->resources[$key])) {
            return false;
        }
        
        $resourceData = $this->resources[$key];
        $resource = $resourceData['resource'];
        
        if (is_resource($resource)) {
            $type = $resourceData['type'];
            
            switch ($type) {
                case 'stream':
                    fclose($resource);
                    break;
                case 'curl':
                    curl_close($resource);
                    break;
                case 'gd':
                    imagedestroy($resource);
                    break;
            }
        }
        
        unset($this->resources[$key]);
        return true;
    }
    
    public function closeAll() {
        foreach (array_keys($this->resources) as $key) {
            $this->closeResource($key);
        }
    }
    
    public function listResources() {
        $list = [];
        foreach ($this->resources as $key => $data) {
            $list[$key] = [
                'type' => $data['type'],
                'valid' => is_resource($data['resource']),
                'age' => time() - $data['created']
            ];
        }
        return $list;
    }
    
    public function __destruct() {
        $this->closeAll();
    }
}

// 使用例
$manager = new ResourceManager();

try {
    // 複数のリソースを管理
    $fileKey = $manager->addResource(fopen('test.txt', 'w'), 'testfile');
    $curlKey = $manager->addResource(curl_init(), 'httpclient');
    $imageKey = $manager->addResource(imagecreate(100, 100), 'thumbnail');
    
    // リソースの使用
    $file = $manager->getResource($fileKey);
    fwrite($file, "Hello World!");
    
    // リソースの状態確認
    print_r($manager->listResources());
    
    // 個別にクローズ
    $manager->closeResource($fileKey);
    
    echo "リソース管理が完了しました\n";
} catch (Exception $e) {
    echo "エラー: " . $e->getMessage() . "\n";
}
?>

2. リソースの自動クリーンアップ

<?php
class AutoCleanupResource {
    private $resource;
    private $cleanupCallback;
    
    public function __construct($resource, callable $cleanupCallback = null) {
        if (!is_resource($resource)) {
            throw new InvalidArgumentException("有効なリソースを指定してください");
        }
        
        $this->resource = $resource;
        $this->cleanupCallback = $cleanupCallback;
    }
    
    public function getResource() {
        if (!is_resource($this->resource)) {
            throw new RuntimeException("リソースが無効です");
        }
        
        return $this->resource;
    }
    
    public function isValid() {
        return is_resource($this->resource);
    }
    
    public function getType() {
        return is_resource($this->resource) ? get_resource_type($this->resource) : null;
    }
    
    public function cleanup() {
        if (is_resource($this->resource)) {
            if ($this->cleanupCallback) {
                call_user_func($this->cleanupCallback, $this->resource);
            } else {
                $this->defaultCleanup();
            }
            $this->resource = null;
        }
    }
    
    private function defaultCleanup() {
        $type = get_resource_type($this->resource);
        
        switch ($type) {
            case 'stream':
                fclose($this->resource);
                break;
            case 'curl':
                curl_close($this->resource);
                break;
            case 'gd':
                imagedestroy($this->resource);
                break;
        }
    }
    
    public function __destruct() {
        $this->cleanup();
    }
}

// 使用例
function customCleanup($resource) {
    echo "カスタムクリーンアップを実行中...\n";
    fclose($resource);
}

$fileResource = new AutoCleanupResource(
    fopen('test.txt', 'w'),
    'customCleanup'
);

$curlResource = new AutoCleanupResource(curl_init());

echo "ファイルリソースタイプ: " . $fileResource->getType() . "\n";
echo "cURLリソースタイプ: " . $curlResource->getType() . "\n";

// スコープから外れると自動的にクリーンアップされる
?>

古いリソースと新しいオブジェクトの違い

MySQLiの例

<?php
// 古いmysql拡張(PHP 7.0で削除済み)
// $connection = mysql_connect('localhost', 'user', 'pass');
// var_dump(is_resource($connection)); // bool(true)

// 新しいMySQLi(オブジェクト指向)
$mysqli = new mysqli('localhost', 'user', 'pass', 'database');
var_dump(is_resource($mysqli)); // bool(false) - オブジェクトです

// PDO(オブジェクト指向)
$pdo = new PDO('mysql:host=localhost;dbname=test', 'user', 'pass');
var_dump(is_resource($pdo)); // bool(false) - オブジェクトです

// 判定の仕方
function checkDatabaseConnection($connection) {
    if (is_resource($connection)) {
        echo "古いmysql拡張のリソースです\n";
    } elseif ($connection instanceof mysqli) {
        echo "MySQLiオブジェクトです\n";
    } elseif ($connection instanceof PDO) {
        echo "PDOオブジェクトです\n";
    } else {
        echo "不明な接続タイプです\n";
    }
}

checkDatabaseConnection($mysqli);
checkDatabaseConnection($pdo);
?>

デバッグとトラブルシューティング

リソースの詳細分析

<?php
function debugResource($value, $name = 'Unknown') {
    echo "=== リソース分析: {$name} ===\n";
    echo "値: " . var_export($value, true) . "\n";
    echo "型: " . gettype($value) . "\n";
    echo "is_resource: " . (is_resource($value) ? 'true' : 'false') . "\n";
    
    if (is_resource($value)) {
        echo "リソースタイプ: " . get_resource_type($value) . "\n";
        
        // リソース固有の情報
        $type = get_resource_type($value);
        switch ($type) {
            case 'stream':
                $meta = stream_get_meta_data($value);
                echo "ストリーム情報:\n";
                echo "  - URI: " . $meta['uri'] . "\n";
                echo "  - モード: " . $meta['mode'] . "\n";
                echo "  - 読み取り可能: " . ($meta['seekable'] ? 'Yes' : 'No') . "\n";
                break;
                
            case 'curl':
                $info = curl_getinfo($value);
                echo "cURL情報:\n";
                echo "  - URL: " . ($info['url'] ?: 'Not set') . "\n";
                echo "  - HTTPコード: " . $info['http_code'] . "\n";
                break;
                
            case 'gd':
                echo "GD画像情報:\n";
                echo "  - 幅: " . imagesx($value) . "px\n";
                echo "  - 高さ: " . imagesy($value) . "px\n";
                break;
        }
    }
    echo "\n";
}

// テスト用リソース
$resources = [
    'file' => fopen('php://memory', 'r+'),
    'curl' => curl_init(),
    'image' => imagecreate(50, 50),
    'string' => 'not a resource',
    'null' => null
];

foreach ($resources as $name => $resource) {
    debugResource($resource, $name);
}

// クリーンアップ
fclose($resources['file']);
curl_close($resources['curl']);
imagedestroy($resources['image']);
?>

メモリリークの検出

<?php
class ResourceLeakDetector {
    private $initialMemory;
    private $resources = [];
    
    public function __construct() {
        $this->initialMemory = memory_get_usage();
    }
    
    public function trackResource($resource, $name) {
        if (!is_resource($resource)) {
            throw new InvalidArgumentException("リソースではありません");
        }
        
        $this->resources[$name] = [
            'resource' => $resource,
            'type' => get_resource_type($resource),
            'memory_at_creation' => memory_get_usage()
        ];
    }
    
    public function checkLeaks() {
        $currentMemory = memory_get_usage();
        $memoryIncrease = $currentMemory - $this->initialMemory;
        
        echo "=== メモリリーク検出 ===\n";
        echo "初期メモリ: " . $this->formatBytes($this->initialMemory) . "\n";
        echo "現在のメモリ: " . $this->formatBytes($currentMemory) . "\n";
        echo "増加量: " . $this->formatBytes($memoryIncrease) . "\n";
        
        $activeResources = 0;
        foreach ($this->resources as $name => $data) {
            if (is_resource($data['resource'])) {
                $activeResources++;
                echo "アクティブリソース: {$name} ({$data['type']})\n";
            }
        }
        
        echo "アクティブリソース数: {$activeResources}\n";
        
        if ($memoryIncrease > 1024 * 1024) { // 1MB以上
            echo "⚠️  メモリリークの可能性があります\n";
        }
        
        echo "\n";
    }
    
    private function formatBytes($bytes) {
        $units = ['B', 'KB', 'MB', 'GB'];
        $bytes = max($bytes, 0);
        $pow = floor(($bytes ? log($bytes) : 0) / log(1024));
        $pow = min($pow, count($units) - 1);
        
        $bytes /= pow(1024, $pow);
        
        return round($bytes, 2) . ' ' . $units[$pow];
    }
}

// 使用例
$detector = new ResourceLeakDetector();

// リソースを作成・追跡
for ($i = 0; $i < 100; $i++) {
    $file = fopen('php://memory', 'r+');
    $detector->trackResource($file, "file_{$i}");
    
    // 一部のリソースを意図的に閉じない(リーク)
    if ($i % 10 !== 0) {
        fclose($file);
    }
}

$detector->checkLeaks();
?>

まとめ

is_resource関数は、PHPでリソース型の判定を行うための重要な関数です。ファイル操作、ネットワーク通信、画像処理など、外部リソースを扱う際には必須の機能となります。

重要なポイント:

  • リソース型は外部リソースへの参照を保持する特殊な型
  • 適切なリソース管理でメモリリークを防ぐ
  • 新しいPHPではオブジェクト指向のAPIが推奨される
  • リソースの自動クリーンアップ機能を実装することが重要

リソース管理はアプリケーションの安定性とパフォーマンスに直結します。この記事で紹介した例を参考に、適切なリソース管理を実装してください。


この記事がPHP開発の参考になれば幸いです。他にも疑問点があれば、コメントやお問い合わせフォームからお気軽にご質問ください。

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