1<?php declare(strict_types=1);
2
3/*
4 * This file is part of the Monolog package.
5 *
6 * (c) Jordi Boggiano <j.boggiano@seld.be>
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12namespace Monolog\Handler\Curl;
13
14use CurlHandle;
15
16/**
17 * This class is marked as internal and it is not under the BC promise of the package.
18 *
19 * @internal
20 */
21final class Util
22{
23    /** @var array<int> */
24    private static $retriableErrorCodes = [
25        CURLE_COULDNT_RESOLVE_HOST,
26        CURLE_COULDNT_CONNECT,
27        CURLE_HTTP_NOT_FOUND,
28        CURLE_READ_ERROR,
29        CURLE_OPERATION_TIMEOUTED,
30        CURLE_HTTP_POST_ERROR,
31        CURLE_SSL_CONNECT_ERROR,
32    ];
33
34    /**
35     * Executes a CURL request with optional retries and exception on failure
36     *
37     * @param  resource|CurlHandle $ch             curl handler
38     * @param  int                 $retries
39     * @param  bool                $closeAfterDone
40     * @return bool|string         @see curl_exec
41     */
42    public static function execute($ch, int $retries = 5, bool $closeAfterDone = true)
43    {
44        while ($retries--) {
45            $curlResponse = curl_exec($ch);
46            if ($curlResponse === false) {
47                $curlErrno = curl_errno($ch);
48
49                if (false === in_array($curlErrno, self::$retriableErrorCodes, true) || !$retries) {
50                    $curlError = curl_error($ch);
51
52                    if ($closeAfterDone) {
53                        curl_close($ch);
54                    }
55
56                    throw new \RuntimeException(sprintf('Curl error (code %d): %s', $curlErrno, $curlError));
57                }
58
59                continue;
60            }
61
62            if ($closeAfterDone) {
63                curl_close($ch);
64            }
65
66            return $curlResponse;
67        }
68
69        return false;
70    }
71}
72