curlmaster

Wrapper for the cURL extension to handle GET, POST, HEAD requests. Features include (optionally compressed) response caching, caching override, ETAG support, cookie storage, SSL certificate verification.
Ordinary request GET:
$curlm=new curlmaster;
$curlm->CacheDir='/srv/cache';
$curlm->ca_file='/srv/cert-ca/cacert.pem';
// Method GET is default.
$response = $curlm->Request('https://www.google.com/');
var_export($response);
...produces this response, which is not being cached.
/*
array (
  'library' => 'peterkahl\\curlmaster\\curlmaster',
  'library-version' => '7.1.2.2',
  'origin' => 'new',
  'timestamp' => 1601043487,
  'exectime' => '0.092391',
  'status' => '200',
  'forced' => false,
  'cachingtime' => 0,
  'cachecompress' => false,
  'filename' => '/srv/cache/curlmaster_response_f648fed2369cff9219f1c65e54701c.json',
  'cookiefile' => '/srv/cache/curlmaster_cookie_www.google.com.cookie',
  'request' =>
  array (
    'method' => 'GET',
    'url' => 'https://www.google.com/',
    'user-agent' => 'Mozilla/5.0 (curlmaster/7.1.2.2; +https://github.com/peterkahl/curlmaster)',
    'headers' =>
    array (
    ),
    'etag-enable' => false,
    'ca-file' => '/srv/cert-ca/cacert.pem',
    'cipher' => '',
    'post-data' => '',
  ),
  'verbose' =>
  array (
    0 => '*   Trying 2a00:1450:4001:81f::2004:443...',
    1 => '* TCP_NODELAY set',
    2 => '* Connected to www.google.com (2a00:1450:4001:81f::2004) port 443 (#0)',
    3 => '* ALPN, offering h2',
    4 => '* ALPN, offering http/1.1',
    5 => '* successfully set certificate verify locations:',
    6 => '*   CAfile: /srv/cert-ca/cacert.pem',
    7 => '  CApath: /etc/ssl/certs',
    8 => '* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384',
    9 => '* ALPN, server accepted to use h2',
    10 => '* Server certificate:',
    11 => '*  subject: C=US; ST=California; L=Mountain View; O=Google LLC; CN=www.google.com',
    12 => '*  start date: Sep  3 06:42:46 2020 GMT',
    13 => '*  expire date: Nov 26 06:42:46 2020 GMT',
    14 => '*  subjectAltName: host "www.google.com" matched cert\'s "www.google.com"',
    15 => '*  issuer: C=US; O=Google Trust Services; CN=GTS CA 1O1',
    16 => '*  SSL certificate verify ok.',
    17 => '* Using HTTP2, server supports multi-use',
    18 => '* Connection state changed (HTTP/2 confirmed)',
    19 => '* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0',
    20 => '* Using Stream ID: 1 (easy handle 0x55c5b37ebf80)',
    21 => '> GET / HTTP/2',
    22 => 'Host: www.google.com',
    23 => 'user-agent: Mozilla/5.0 (curlmaster/7.1.2.2; +https://github.com/peterkahl/curlmaster)',
    24 => 'accept: /',
    25 => 'accept-encoding: deflate, gzip, br',
    26 => 'cookie: NID=204=mKHrdeEZ7MsIM_tAU0UbcXlfdzx6lsXvLES8lH54G27cTF9skpRfAOEWk9XqM6Ks-MQKZY-9aBQRMTCDSToCkP3K578C7cbdxIiTbq9ZRcqDp5nP5Uke2sZ5d8Lo9W_aw-soUOXQKM8qnG1F2by3MhACB29kOPGsDVmjJAt4oRw',
    27 => '',
    28 => '* old SSL session ID is stale, removing',
    29 => '* Connection state changed (MAX_CONCURRENT_STREAMS == 100)!',
    30 => '< HTTP/2 200 ',
    31 => '< date: Fri, 25 Sep 2020 14:18:07 GMT',
    32 => '< expires: -1',
    33 => '< cache-control: private, max-age=0',
    34 => '< content-type: text/html; charset=UTF-8',
    35 => '< content-encoding: gzip',
    36 => '< server: gws',
    37 => '< content-length: 15675',
    38 => '< x-xss-protection: 0',
    39 => '< x-frame-options: SAMEORIGIN',
    40 => '< alt-svc: h3-Q050=":443"; ma=2592000,h3-29=":443"; ma=2592000,h3-27=":443"; ma=2592000,h3-T051=":443"; ma=2592000,h3-T050=":443"; ma=2592000,h3-Q046=":443"; ma=2592000,h3-Q043=":443"; ma=2592000,quic=":443"; ma=2592000; v="46,43"',
    41 => '< ',
    42 => '* Closing connection 0',
    43 => '',
  ),
  'response' =>
  array (
    'timestamp' => 1601043487,
    'exectime' => '0.092378',
    'status' => '200',
    'cachingtime' => 0,
    'headers' =>
    array (
      'status' => 'HTTP/2 200',
      'date' => 'Fri, 25 Sep 2020 14:18:07 GMT',
      'expires' => '-1',
      'cache-control' => 'private, max-age=0',
      'content-type' => 'text/html; charset=UTF-8',
      'content-encoding' => 'gzip',
      'server' => 'gws',
      'content-length' => '15675',
      'x-xss-protection' => '0',
      'x-frame-options' => 'SAMEORIGIN',
      'alt-svc' => 'h3-Q050=":443"; ma=2592000,h3-29=":443"; ma=2592000,h3-27=":443"; ma=2592000,h3-T051=":443"; ma=2592000,h3-T050=":443"; ma=2592000,h3-Q046=":443"; ma=2592000,h3-Q043=":443"; ma=2592000,quic=":443"; ma=2592000; v="46,43"',
    ),
    'error-num' => 0,
    'error-verb' => 'CURLE_OK',
    'etag' => '',
    'last-modified' => 0,
    'body' => '<!doctype html>...[truncated]...</body></html>',
  ),
)
*/
Force caching of response:
$curlm=new curlmaster;
$curlm->CacheDir='/srv/cache';
$curlm->ca_file='/srv/cert-ca/cacert.pem';
$curlm->ForcedCacheMaxAge=3600;
$response = $curlm->Request('https://www.google.com/');
...produces this response, which is being cached as JSON.
{
    "library": "peterkahl\\curlmaster\\curlmaster",
    "library-version": "7.1.2.2",
    "origin": "new",
    "timestamp": 1601044020,
    "exectime": "0.120096",
    "status": "200",
    "forced": false,
    "cachingtime": 3600,
    "cachecompress": false,
    "filename": "\/srv\/cache\/curlmaster_response_f648fed2369cff9219f1c65e54701c.json",
    "cookiefile": "\/srv\/cache\/curlmaster_cookie_www.google.com.cookie",
    "request": {
        "method": "GET",
        "url": "https:\/\/www.google.com\/",
        "user-agent": "Mozilla\/5.0 (curlmaster\/7.1.2.2; +https:\/\/github.com\/peterkahl\/curlmaster)",
        "headers": [],
        "etag-enable": false,
        "ca-file": "\/srv\/cert-ca\/cacert.pem",
        "cipher": "",
        "post-data": ""
    },
    "verbose": [
        "*   Trying 2a00:1450:4001:81f::2004:443...",
        "* TCP_NODELAY set",
        "* Connected to www.google.com (2a00:1450:4001:81f::2004) port 443 (#0)",
        "* ALPN, offering h2",
        "* ALPN, offering http\/1.1",
        "* successfully set certificate verify locations:",
        "*   CAfile: \/srv\/cert-ca\/cacert.pem",
        "  CApath: \/etc\/ssl\/certs",
        "* SSL connection using TLSv1.3 \/ TLS_AES_256_GCM_SHA384",
        "* ALPN, server accepted to use h2",
        "* Server certificate:",
        "*  subject: C=US; ST=California; L=Mountain View; O=Google LLC; CN=www.google.com",
        "*  start date: Sep  3 06:42:46 2020 GMT",
        "*  expire date: Nov 26 06:42:46 2020 GMT",
        "*  subjectAltName: host \"www.google.com\" matched cert's \"www.google.com\"",
        "*  issuer: C=US; O=Google Trust Services; CN=GTS CA 1O1",
        "*  SSL certificate verify ok.",
        "* Using HTTP2, server supports multi-use",
        "* Connection state changed (HTTP\/2 confirmed)",
        "* Copying HTTP\/2 data in stream buffer to connection buffer after upgrade: len=0",
        "* Using Stream ID: 1 (easy handle 0x5635d76eaf80)",
        "> GET \/ HTTP\/2",
        "Host: www.google.com",
        "user-agent: Mozilla\/5.0 (curlmaster\/7.1.2.2; +https:\/\/github.com\/peterkahl\/curlmaster)",
        "accept: \/",
        "accept-encoding: deflate, gzip, br",
        "cookie: NID=204=mKHrdeEZ7MsIM_tAU0UbcXlfdzx6lsXvLES8lH54G27cTF9skpRfAOEWk9XqM6Ks-MQKZY-9aBQRMTCDSToCkP3K578C7cbdxIiTbq9ZRcqDp5nP5Uke2sZ5d8Lo9W_aw-soUOXQKM8qnG1F2by3MhACB29kOPGsDVmjJAt4oRw",
        "",
        "* old SSL session ID is stale, removing",
        "* Connection state changed (MAX_CONCURRENT_STREAMS == 100)!",
        "< HTTP\/2 200 ",
        "< date: Fri, 25 Sep 2020 14:27:00 GMT",
        "< expires: -1",
        "< cache-control: private, max-age=0",
        "< content-type: text\/html; charset=UTF-8",
        "< content-encoding: gzip",
        "< server: gws",
        "< content-length: 15675",
        "< x-xss-protection: 0",
        "< x-frame-options: SAMEORIGIN",
        "< alt-svc: h3-Q050=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000,h3-27=\":443\"; ma=2592000,h3-T051=\":443\"; ma=2592000,h3-T050=\":443\"; ma=2592000,h3-Q046=\":443\"; ma=2592000,h3-Q043=\":443\"; ma=2592000,quic=\":443\"; ma=2592000; v=\"46,43\"",
        "< ",
        "* Closing connection 0",
        ""
    ],
    "response": {
        "timestamp": 1601044020,
        "exectime": "0.120084",
        "status": "200",
        "cachingtime": 0,
        "headers": {
            "status": "HTTP\/2 200",
            "date": "Fri, 25 Sep 2020 14:27:00 GMT",
            "expires": "-1",
            "cache-control": "private, max-age=0",
            "content-type": "text\/html; charset=UTF-8",
            "content-encoding": "gzip",
            "server": "gws",
            "content-length": "15675",
            "x-xss-protection": "0",
            "x-frame-options": "SAMEORIGIN",
            "alt-svc": "h3-Q050=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000,h3-27=\":443\"; ma=2592000,h3-T051=\":443\"; ma=2592000,h3-T050=\":443\"; ma=2592000,h3-Q046=\":443\"; ma=2592000,h3-Q043=\":443\"; ma=2592000,quic=\":443\"; ma=2592000; v=\"46,43\""
        },
        "error-num": 0,
        "error-verb": "CURLE_OK",
        "etag": "",
        "last-modified": 0,
        "body": "<!doctype html>...[truncated]...<\/body><\/html>"
    }
}
Usage example, method POST:
use peterkahl\curlmaster\curlmaster;
$curlm = new curlmaster;
$curlm->CacheDir = '/srv/cache';
$curlm->ca_file = '/srv/cert-ca/cacert.pem';
$post_data = array(
  'user' => 'admin',
  'pwd'  => 'oracle',
);
$response = $curlm->Request('https://whatever.anything/login', 'POST', $post_data);
Usage example, method HEAD:
use peterkahl\curlmaster\curlmaster;
$curlm = new curlmaster;
$curlm->CacheDir = '/srv/cache';
$curlm->ca_file = '/srv/cert-ca/cacert.pem';
$curlm->headers = array('accept: text/html');
$response = $curlm->Request('https://github.com/', 'HEAD');
/*
The resulting response is an array. HEAD responses are not cached.
array (
  'library' => 'peterkahl\\curlmaster\\curlmaster',
  'library-version' => '7.1.1',
  'origin' => 'new',
  'timestamp' => 1600930605,
  'exectime' => '0.330269',
  'status' => '200',
  'forced' => false,
  'cachingtime' => -1,
  'filename' => '/srv/cache/curlmaster_response_ba5453820c9f8f87fec8b54e7540bf.json',
  'cookiefile' => '/srv/cache/curlmaster_cookie_github.com.cookie',
  'request' =>
  array (
    'method' => 'HEAD',
    'url' => 'https://github.com/',
    'user-agent' => 'Mozilla/5.0 (curlmaster/7.1.1; +https://github.com/peterkahl/curlmaster)',
    'headers' =>
    array (
      0 => 'accept: text/html',
    ),
    'etag-enable' => false,
    'ca-file' => '/srv/cert-ca/cacert.pem',
    'cipher' => '',
    'post-data' => '',
  ),
  'verbose' =>
  array (
    0 => '*   Trying 140.82.121.4:443...',
    1 => '* TCP_NODELAY set',
    2 => '* Connected to github.com (140.82.121.4) port 443 (#0)',
    3 => '* ALPN, offering h2',
    4 => '* ALPN, offering http/1.1',
    5 => '* successfully set certificate verify locations:',
    6 => '*   CAfile: /srv/cert-ca/cacert.pem',
    7 => '  CApath: /etc/ssl/certs',
    8 => '* SSL connection using TLSv1.3 / TLS_AES_128_GCM_SHA256',
    9 => '* ALPN, server accepted to use http/1.1',
    10 => '* Server certificate:',
    11 => '*  subject: C=US; ST=California; L=San Francisco; O=GitHub, Inc.; CN=github.com',
    12 => '*  start date: May  5 00:00:00 2020 GMT',
    13 => '*  expire date: May 10 12:00:00 2022 GMT',
    14 => '*  subjectAltName: host "github.com" matched cert\'s "github.com"',
    15 => '*  issuer: C=US; O=DigiCert Inc; OU=www.digicert.com; CN=DigiCert SHA2 High Assurance Server CA',
    16 => '*  SSL certificate verify ok.',
    17 => '> HEAD / HTTP/1.1',
    18 => 'Host: github.com',
    19 => 'User-Agent: Mozilla/5.0 (curlmaster/7.1.1; +https://github.com/peterkahl/curlmaster)',
    20 => 'Cookie: logged_in=no; _octo=GH1.1.586556027.1600930239',
    21 => 'accept: text/html',
    22 => '',
    23 => '* old SSL session ID is stale, removing',
    24 => '* Mark bundle as not supporting multiuse',
    25 => '< HTTP/1.1 200 OK',
    26 => '< date: Thu, 24 Sep 2020 06:56:45 GMT',
    27 => '< content-type: text/html; charset=utf-8',
    28 => '< server: GitHub.com',
    29 => '< status: 200 OK',
    30 => '< vary: X-PJAX, Accept-Encoding, Accept, X-Requested-With, Accept-Encoding',
    31 => '< etag: W/"4da62c2bc712cef0599ed6a7f550f4d2"',
    32 => '< cache-control: max-age=0, private, must-revalidate',
    33 => '< strict-transport-security: max-age=31536000; includeSubdomains; preload',
    34 => '< x-frame-options: deny',
    35 => '< x-content-type-options: nosniff',
    36 => '< x-xss-protection: 1; mode=block',
    37 => '< referrer-policy: origin-when-cross-origin, strict-origin-when-cross-origin',
    38 => '< expect-ct: max-age=2592000, report-uri="https://api.github.com/_private/browser/errors"',
    39 => '< content-security-policy: default-src \'none\'; base-uri \'self\'; block-all-mixed-content; connect-src \'self\' uploads.github.com www.githubstatus.com collector.githubapp.com api.github.com www.google-analytics.com github-cloud.s3.amazonaws.com github-production-repository-file-5c1aeb.s3.amazonaws.com github-production-upload-manifest-file-7fdce7.s3.amazonaws.com github-production-user-asset-6210df.s3.amazonaws.com cdn.optimizely.com logx.optimizely.com/v1/events wss://alive.github.com; font-src github.githubassets.com; form-action \'self\' github.com gist.github.com; frame-ancestors \'none\'; frame-src render.githubusercontent.com; img-src \'self\' data: github.githubassets.com identicons.github.com collector.githubapp.com github-cloud.s3.amazonaws.com *.githubusercontent.com customer-stories-feed.github.com spotlights-feed.github.com; manifest-src \'self\'; media-src github.githubassets.com; script-src github.githubassets.com; style-src \'unsafe-inline\' github.githubassets.com; worker-src github.com/socket-worker.js gist.github.com/socket-worker.js',
    40 => '* Added cookie _gh_sess="YT1whftzqTDiFdxWK6B8KsA6%2FF%2Fo4DhCEPXJ8%2FA%2FB97tPOmGTQ9aOEwwrlntWc8THlcYj91gzSznAgR%2BGMHR%2BIRfOW7ErgXFbJZpNrzwn5KjLpdPQHoz3bkLvZiKQ%2B7xiXBt4IcsO7wdJN4zrHcehaZgTTELIEVrIkLOoV3ZktjVMBhJf3SkBF3of%2B3zPbM2UjAfEDrVK%2FaKM3YXgwDbsKoAdWsrqgOO4Gv%2FprqpT2VqSgPSwCNnF3kzz4ivaTf%2B8IHIS7lAHlr5N82STNzLUg%3D%3D--lyzCNBt0J30oi58f--CFgqDV2Di71wa7gw%2BGmk3A%3D%3D" for domain github.com, path /, expire 0',
    41 => '< Set-Cookie: _gh_sess=YT1whftzqTDiFdxWK6B8KsA6%2FF%2Fo4DhCEPXJ8%2FA%2FB97tPOmGTQ9aOEwwrlntWc8THlcYj91gzSznAgR%2BGMHR%2BIRfOW7ErgXFbJZpNrzwn5KjLpdPQHoz3bkLvZiKQ%2B7xiXBt4IcsO7wdJN4zrHcehaZgTTELIEVrIkLOoV3ZktjVMBhJf3SkBF3of%2B3zPbM2UjAfEDrVK%2FaKM3YXgwDbsKoAdWsrqgOO4Gv%2FprqpT2VqSgPSwCNnF3kzz4ivaTf%2B8IHIS7lAHlr5N82STNzLUg%3D%3D--lyzCNBt0J30oi58f--CFgqDV2Di71wa7gw%2BGmk3A%3D%3D; Path=/; HttpOnly; Secure; SameSite=Lax',
    42 => '< Accept-Ranges: bytes',
    43 => '< X-GitHub-Request-Id: AF42:E4AE:14B52F0:1CD1320:5F6C432C',
    44 => '< ',
    45 => '* Closing connection 0',
    46 => '',
  ),
  'response' =>
  array (
    'timestamp' => 1600930605,
    'exectime' => '0.330255',
    'status' => '200',
    'cachingtime' => 0,
    'headers' =>
    array (
      'status' => 'HTTP/1.1 200 OK',
      'date' => 'Thu, 24 Sep 2020 06:56:45 GMT',
      'content-type' => 'text/html; charset=utf-8',
      'server' => 'GitHub.com',
      'status-1' => '200 OK',
      'vary' => 'X-PJAX, Accept-Encoding, Accept, X-Requested-With, Accept-Encoding',
      'etag' => 'W/"4da62c2bc712cef0599ed6a7f550f4d2"',
      'cache-control' => 'max-age=0, private, must-revalidate',
      'strict-transport-security' => 'max-age=31536000; includeSubdomains; preload',
      'x-frame-options' => 'deny',
      'x-content-type-options' => 'nosniff',
      'x-xss-protection' => '1; mode=block',
      'referrer-policy' => 'origin-when-cross-origin, strict-origin-when-cross-origin',
      'expect-ct' => 'max-age=2592000, report-uri="https://api.github.com/_private/browser/errors"',
      'content-security-policy' => 'default-src \'none\'; base-uri \'self\'; block-all-mixed-content; connect-src \'self\' uploads.github.com www.githubstatus.com collector.githubapp.com api.github.com www.google-analytics.com github-cloud.s3.amazonaws.com github-production-repository-file-5c1aeb.s3.amazonaws.com github-production-upload-manifest-file-7fdce7.s3.amazonaws.com github-production-user-asset-6210df.s3.amazonaws.com cdn.optimizely.com logx.optimizely.com/v1/events wss://alive.github.com; font-src github.githubassets.com; form-action \'self\' github.com gist.github.com; frame-ancestors \'none\'; frame-src render.githubusercontent.com; img-src \'self\' data: github.githubassets.com identicons.github.com collector.githubapp.com github-cloud.s3.amazonaws.com *.githubusercontent.com customer-stories-feed.github.com spotlights-feed.github.com; manifest-src \'self\'; media-src github.githubassets.com; script-src github.githubassets.com; style-src \'unsafe-inline\' github.githubassets.com; worker-src github.com/socket-worker.js gist.github.com/socket-worker.js',
      'set-cookie' => '_gh_sess=YT1whftzqTDiFdxWK6B8KsA6%2FF%2Fo4DhCEPXJ8%2FA%2FB97tPOmGTQ9aOEwwrlntWc8THlcYj91gzSznAgR%2BGMHR%2BIRfOW7ErgXFbJZpNrzwn5KjLpdPQHoz3bkLvZiKQ%2B7xiXBt4IcsO7wdJN4zrHcehaZgTTELIEVrIkLOoV3ZktjVMBhJf3SkBF3of%2B3zPbM2UjAfEDrVK%2FaKM3YXgwDbsKoAdWsrqgOO4Gv%2FprqpT2VqSgPSwCNnF3kzz4ivaTf%2B8IHIS7lAHlr5N82STNzLUg%3D%3D--lyzCNBt0J30oi58f--CFgqDV2Di71wa7gw%2BGmk3A%3D%3D; Path=/; HttpOnly; Secure; SameSite=Lax',
      'accept-ranges' => 'bytes',
      'x-github-request-id' => 'AF42:E4AE:14B52F0:1CD1320:5F6C432C',
    ),
    'error-num' => 0,
    'error-verb' => 'CURLE_OK',
    'etag' => 'W/"4da62c2bc712cef0599ed6a7f550f4d2"',
    'last-modified' => 0,
    'body' => '
',
  ),
)
*/
Cache Purging:
Best to set this up on a crontab job.
use peterkahl\curlmaster\curlmaster;
$curlm = new curlmaster;
$curlm->CacheDir = '/srv/cache';
$curlm->PurgeCache();