yiisoft/yii2-httpclient

RFE: How about caching ability?

dicrtarasov opened this issue · 3 comments

Curl transport provide transparent content deflating and cookies persistence. Ok, but what about caching feature. For example I use this in my project:

<?php
/**
 * @author Igor A Tarasov <develop@dicr.org>
 */

declare(strict_types = 1);

use Yii;
use yii\base\InvalidConfigException;
use yii\caching\CacheInterface;
use yii\caching\TagDependency;
use yii\di\Instance;
use yii\httpclient\Client;
use yii\httpclient\Request;
use yii\httpclient\Response;
use function is_int;

/**
 * Client with response caching.
 *
 * For caching repeatable requests, headers (Cookies, User-Agent, ...) must be similars for next requests
 *
 * @noinspection PhpUnused
 */
class CachingClient extends Client
{
    /** @var \yii\caching\CacheInterface */
    public $cache = 'cache';

    /**
     * @var bool if true, then cache key calculated with cookies. If false, then browsing is inkognito.
     * Use this only when response depends on cookies.
     */
    public $cacheCookies = false;

    /** @var int cache time, s */
    public $cacheDuration;

    /**
     * @inheritDoc
     * @throws \yii\base\InvalidConfigException
     */
    public function init()
    {
        parent::init();

        if (! empty($this->cache)) {
            $this->cache = Instance::ensure($this->cache, CacheInterface::class);
        }

        if (empty($this->cacheDuration)) {
            $this->cacheDuration = null;
        } elseif (! is_int($this->cacheDuration) || $this->cacheDuration < 0) {
            throw new InvalidConfigException('cacheDuration');
        }
    }

    /**
     * Return cache key for request.
     *
     * @param \yii\httpclient\Request $request
     * @return string[]
     */
    protected function cacheKey(Request $request)
    {
        $keyRequest = $this->cacheCookies ? $request : (clone $request)->setCookies([]);

        return [__METHOD__, $keyRequest->toString()];
    }

    /**
     * @inheritDoc
     *
     * @throws \yii\base\InvalidConfigException
     * @throws \yii\httpclient\Exception
     */
    public function send($request)
    {
        /** @var string[]|null $cacheKey key for cache */
        $cacheKey = null;

        // load from cache
        if (! empty($this->cache)) {
            $cacheKey = $this->cacheKey($request);

            // load response
            $response = $this->cache->get($cacheKey);
            if ($response instanceof Response) {
                Yii::debug('Used cached response for request: ' . $request->fullUrl, __METHOD__);

                // restore client link
                $response->client = $request->client;

                return $response;
            }
        }

        // fetch from server
        $response = parent::send($request);

        // store in cache
        if ($response->isOk && ! empty($this->cache)) {
            $cacheResponse = $this->cacheCookies ? $response : (clone $response)->setCookies([]);

            // clean connection to client to not save it in cache
            $cacheResponse->client = null;

            // save response
            $this->cache->set($cacheKey, $cacheResponse, $this->cacheDuration, new TagDependency([
                'tags' => [__CLASS__]
            ]));
        }

        return $response;
    }
}```

Because of this issue #200
caching key from Request::toString with cookies works incorrect :(

I see that the code is practically applicable but unsure about how common it is. I'd wait for more requests of a similar feature.

@yiisoft/core-developers, @yiisoft/reviewers what do you think?

Code updated for improvements (clean client link in Response, to not store Client in cache and restore live client before returning response).