Управление cURL ресурсами в PHP: правильное закрытие соединений

Раздел: PHP -> HTTP запросы с cURL

Управление ресурсами cURL в PHP: закрытие дескрипторов

Как освободить системные ресурсы после выполнения cURL-запроса в PHP?

Основной и наиболее эффективный способ корректного завершения работы с cURL в PHP - использование функции curl_close(). После того, как запрос выполнен и результат получен, вызов curl_close($ch) закрывает сессию и освобождает занятые ресурсы. Это предотвращает утечку памяти, особенно при большом количестве последовательных запросов или в долго работающих скриптах.

Пример:


$ch = curl_init('https://api.example.com/data');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch); // закрытие дескриптора
echo $response;
    

Php curl timeout (php curl таймаут)

Шаги выполнения:

  1. Инициализация с помощью curl_init().
  2. Установка опций (curl_setopt).
  3. Выполнение запроса (curl_exec).
  4. Закрытие дескриптора (curl_close).

Данный подход гарантирует, что все системные сокеты и буферы будут корректно освобождены. Если curl_close не вызван явно, дескриптор закроется только по завершении скрипта (автоматическая сборка мусора), что может быть не критично для коротких скриптов, но нежелательно в длительных циклах.

Типичные ошибки:

  • Попытка закрыть уже закрытый дескриптор вызывает предупреждение Warning: curl_close(): supplied resource is not a valid cURL handle. Решение - проверять состояние ресурса через is_resource() или использовать блок try...finally для гарантированного однократного закрытия.
  • Утечка ресурсов при раннем выходе из скрипта (например, через die или exit). Рекомендуется явно закрывать cURL перед вызовом таких функций или использовать деструктор, если дескриптор сохранён в объекте.

Как избежать предупреждений при повторном или ошибочном закрытии?

Для безопасного закрытия можно проверить, является ли переменная действительным ресурсом cURL:


if (is_resource($ch) && get_resource_type($ch) === 'curl') {
    curl_close($ch);
    unset($ch);
}
    

Php curl ssl (php curl ssl)

Этот подход удобен, когда дескриптор используется в разных местах кода или может быть закрыт раньше. Однако недостаток - избыточная проверка, замедляющая код.

Как гарантировать закрытие даже в случае исключения или ошибки?

Используйте конструкцию try...finally, которая выполняется при любом исходе:


$ch = curl_init('https://api.example.com');
try {
    curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_TIMEOUT => 10
    ]);
    $result = curl_exec($ch);
    if ($result === false) {
        throw new Exception('cURL error: ' . curl_error($ch));
    }
} finally {
    curl_close($ch);
}
echo $result;
    

Php curl ответ (ответ php curl)

Ошибка: Если curl_init завершилась неудачно (возвращает false), то в finally будет попытка закрыть false, что вызовет предупреждение. Решение - инициализировать дескриптор вне блока try и проверять на false перед close:


$ch = curl_init();
if ($ch === false) {
    die('Не удалось создать cURL handle');
}
try {
    // ...
} finally {
    if (is_resource($ch)) {
        curl_close($ch);
    }
}
        

Php curl authorization (php curl авторизация)

Как освободить cURL автоматически с помощью PHP 7?

Начиная с PHP 7, можно отказаться от явного закрытия, если дескриптор не сохраняется в глобальной области. При выходе переменной из области видимости или вызове unset() ресурс автоматически освобождается:


function fetchData($url) {
    $ch = curl_init($url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    return curl_exec($ch);
    // здесь $ch выходит из области видимости, ресурс освобождается
}
$data = fetchData('https://example.com');
    

Php curl close (php curl close)

Примечание: В PHP 5 автоматическое освобождение не гарантируется до конца скрипта, поэтому для совместимости рекомендуется всегда использовать curl_close явно.

Проблема: Если в функции возникают исключения, а дескриптор не закрыт, возможна утечка. Решение - использовать finally даже при автоматическом закрытии для явного контроля.

Как сбросить параметры дескриптора без закрытия?

Иногда требуется переиспользовать один и тот же дескриптор для нескольких запросов, но с разными настройками. Вместо curl_close и последующего curl_init можно вызвать curl_reset(), которая сбрасывает все опции до состояния по умолчанию, но оставляет дескриптор живым:


$ch = curl_init();
// первый запрос
curl_setopt($ch, CURLOPT_URL, 'https://site1.com');
$result1 = curl_exec($ch);

// сброс и второй запрос
curl_reset($ch);
curl_setopt($ch, CURLOPT_URL, 'https://site2.com');
curl_setopt($ch, CURLOPT_POST, true);
$result2 = curl_exec($ch);

curl_close($ch); // закрытие в конце
    

Цель: экономия ресурсов за счёт повторного использования выделенного соединения (особенно актуально для HTTPS с дорогим handshake).

Типичная ошибка: Не вызван curl_reset, из-за чего второй запрос выполняется с настройками первого (например, URL). Решение - явно сбрасывать опции или переустанавливать критически важные.

- Curl exec php (php curl exec)
- Curl setopt php (php curl setopt)
- Php curl file (php curl файл)

Расширенные примеры работы с curl_close в PHP

Ниже приведены подробные примеры, демонстрирующие различные сценарии использования закрытия cURL-дескриптора, включая неочевидные ситуации.

1. Закрытие нескольких дескрипторов в цикле (многопоточный cURL)

При использовании curl_multi закрытие отдельных дескрипторов выполняется после завершения всех запросов.

Пример

$urls = ['https://site1.com', 'https://site2.com', 'https://site3.com'];
$mh = curl_multi_init();
$handles = [];

foreach ($urls as $i => $url) {
    $ch = curl_init($url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_multi_add_handle($mh, $ch);
    $handles[$i] = $ch;
}

$active = null;
do {
    curl_multi_exec($mh, $active);
} while ($active > 0);

// Закрытие: сначала удалить из мульти-дескриптора, потом закрыть каждый
foreach ($handles as $ch) {
    curl_multi_remove_handle($mh, $ch);
    curl_close($ch);
}
curl_multi_close($mh);
echo "Все запросы выполнены и дескрипторы закрыты.";
Результат: вывод строки "Все запросы выполнены и дескрипторы закрыты." без ошибок.

2. Закрытие при использовании пользовательских обработчиков (shorthand)

Создание вспомогательной функции, которая гарантирует закрытие, даже если содержимое не выбрано.

Пример

function safeCurlExec($url, callable $callback = null) {
    $ch = curl_init($url);
    if ($ch === false) {
        throw new RuntimeException('Не удалось инициализировать cURL');
    }
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    try {
        $result = curl_exec($ch);
        if ($result === false) {
            throw new RuntimeException(curl_error($ch));
        }
        if ($callback !== null) {
            $callback($ch); // возможность дополнительно манипулировать дескриптором до закрытия
        }
        return $result;
    } finally {
        curl_close($ch);
    }
}

$data = safeCurlExec('https://api.example.com');
echo "Данные получены: " . strlen($data) . " байт";
Результат: "Данные получены: 1240 байт" (без утечек).

3. Закрытие дескриптора в деструкторе объекта (ООП-подход)

Пример

class CurlClient {
    private $handle;
    
    public function __construct($url) {
        $this->handle = curl_init($url);
        if ($this->handle === false) {
            throw new InvalidArgumentException('cURL init failed');
        }
        curl_setopt($this->handle, CURLOPT_RETURNTRANSFER, true);
    }
    
    public function fetch() {
        return curl_exec($this->handle);
    }
    
    public function __destruct() {
        if (is_resource($this->handle)) {
            curl_close($this->handle);
        }
    }
}

$client = new CurlClient('https://example.com');
echo $client->fetch();
// При выходе из скрипта деструктор автоматически закроет cURL
Результат: страница https://example.com, дескриптор корректно закрыт.

4. Закрытие при ошибках в потоке вывода (CURLOPT_FILE)

Если ответ сохраняется в файл, ресурс файла нужно закрывать отдельно, а cURL - в любом случае.

Пример

$filename = '/tmp/download.pdf';
$fp = fopen($filename, 'w');
$ch = curl_init('https://example.com/file.pdf');
curl_setopt_array($ch, [
    CURLOPT_FILE => $fp,
    CURLOPT_HEADER => false
]);

$success = curl_exec($ch);
if ($success === false) {
    // ошибка, возможно, удалить частичный файл
    unlink($filename);
}
curl_close($ch);
fclose($fp);
echo $success ? "Файл скачан: $filename" : "Ошибка: " . curl_error($ch);
Результат: при успехе файл создаётся, при ошибке файл удаляется, cURL закрыт.

5. Закрытие дескриптора при использовании CURLOPT_WRITEFUNCTION

Пример

$data = '';
$ch = curl_init('https://example.com/stream');
curl_setopt($ch, CURLOPT_WRITEFUNCTION, function($ch, $chunk) use (&$data) {
    $data .= $chunk;
    return strlen($chunk);
});

curl_exec($ch);
curl_close($ch);
echo "Получено " . strlen($data) . " символов";
Результат: "Получено 0 символов", если поток пуст, или другое число.

6. Закрытие после передачи данных по FTP (не HTTP)

Пример

$ch = curl_init();
curl_setopt_array($ch, [
    CURLOPT_URL => 'ftp://ftp.example.com/file.txt',
    CURLOPT_USERPWD => 'user:pass',
    CURLOPT_RETURNTRANSFER => true
]);
$file = curl_exec($ch);
if ($file === false) {
    echo "FTP ошибка: " . curl_error($ch);
} else {
    file_put_contents('local.txt', $file);
    echo "Файл сохранён.";
}
curl_close($ch);
Результат: файл загружен с FTP-сервера, cURL-соединение закрыто.

7. Закрытие при использовании cURL с NTLM-аутентификацией

Пример

$ch = curl_init('https://private.example.com');
curl_setopt_array($ch, [
    CURLOPT_HTTPAUTH => CURLAUTH_NTLM,
    CURLOPT_USERPWD => 'domain\user:password',
    CURLOPT_RETURNTRANSFER => true
]);
$response = curl_exec($ch);
if (curl_errno($ch) === CURLE_OK) {
    echo "Успешно.";
} else {
    echo "Ошибка: " . curl_error($ch);
}
curl_close($ch);
Результат: вывод "Успешно." или описание ошибки.

PHP cURL close - comments

En
Php curl close (php)