diff --git a/README.md b/README.md index 25943a22197..fe59dfaed82 100644 --- a/README.md +++ b/README.md @@ -69,15 +69,20 @@ From `$client` object, you can access to all GitHub. // This file is generated by Composer require_once 'vendor/autoload.php'; +use Cache\Adapter\Redis\RedisCachePool; + +$client = new \Redis(); +$client->connect('127.0.0.1', 6379); +// Create a PSR6 cache pool +$pool = new RedisCachePool($client); + $client = new \Github\Client(); -$client->useCache(); - -// Or select directly which cache you want to use -$client->useCache( - // Built in one, or any cache implementing this interface: - // Github\HttpClient\Cache\CacheInterface - new \Github\HttpClient\Cache\FilesystemCache('/tmp/github-api-cache') -); +$client->addCache($pool); + +// Do some request + +// Stop using cache +$client->removeCache(); ``` Using cache, the client will get cached responses if resources haven't changed since last time, diff --git a/composer.json b/composer.json index 0519519435c..5b7eb44548a 100644 --- a/composer.json +++ b/composer.json @@ -17,11 +17,14 @@ } ], "require": { - "php": "^5.5|^7.0", + "php": "^5.5 || ^7.0", + "psr/http-message": "^1.0", + "psr/cache": "^1.0", "php-http/httplug": "^1.0", "php-http/discovery": "^1.0", "php-http/client-implementation": "^1.0", - "php-http/client-common": "^1.1" + "php-http/client-common": "^1.1", + "php-http/cache-plugin": "^1.0" }, "require-dev": { "phpunit/phpunit": "~4.0", @@ -29,9 +32,6 @@ "guzzlehttp/psr7": "^1.2", "sllh/php-cs-fixer-styleci-bridge": "~1.3" }, - "suggest": { - "knplabs/gaufrette": "Needed for optional Gaufrette cache" - }, "autoload": { "psr-4": { "Github\\": "lib/Github/" } }, diff --git a/lib/Github/Client.php b/lib/Github/Client.php index da4ffa14f98..945a801782b 100644 --- a/lib/Github/Client.php +++ b/lib/Github/Client.php @@ -5,9 +5,7 @@ use Github\Api\ApiInterface; use Github\Exception\InvalidArgumentException; use Github\Exception\BadMethodCallException; -use Github\HttpClient\Cache\CacheInterface; use Github\HttpClient\Plugin\Authentication; -use Github\HttpClient\Plugin\Cache; use Github\HttpClient\Plugin\GithubExceptionThrower; use Github\HttpClient\Plugin\History; use Github\HttpClient\Plugin\PathPrepend; @@ -19,6 +17,7 @@ use Http\Discovery\MessageFactoryDiscovery; use Http\Discovery\UriFactoryDiscovery; use Http\Message\MessageFactory; +use Psr\Cache\CacheItemPoolInterface; /** * Simple yet very cool PHP GitHub client. @@ -376,19 +375,21 @@ public function addHeaders(array $headers) } /** - * @param bool|CacheInterface $cache + * Add a cache plugin to cache responses locally. + * @param CacheItemPoolInterface $cache */ - public function useCache($cache = true) + public function addCache(CacheItemPoolInterface $cachePool) { - $this->removePlugin(Cache::class); - if ($cache !== false) { - if ($cache instanceof CacheInterface) { - $plugin = new Cache($cache); - } else { - $plugin = new Cache(); - } - $this->addPlugin($plugin); - } + $this->removeCache(); + $this->addPlugin(new Plugin\CachePlugin($cachePool)); + } + + /** + * Remove the cache plugin + */ + public function removeCache() + { + $this->removePlugin(Plugin\CachePlugin::class); } /** diff --git a/lib/Github/HttpClient/Cache/CacheInterface.php b/lib/Github/HttpClient/Cache/CacheInterface.php deleted file mode 100644 index 014e0d20626..00000000000 --- a/lib/Github/HttpClient/Cache/CacheInterface.php +++ /dev/null @@ -1,51 +0,0 @@ - - */ -interface CacheInterface -{ - /** - * @param string $id The id of the cached resource - * - * @return bool if present - */ - public function has($id); - - /** - * @param string $id The id of the cached resource - * - * @return null|int The modified since timestamp - */ - public function getModifiedSince($id); - - /** - * @param string $id The id of the cached resource - * - * @return null|string The ETag value - */ - public function getETag($id); - - /** - * @param string $id The id of the cached resource - * - * @throws \InvalidArgumentException If cache data don't exists - * - * @return ResponseInterface The cached response object - */ - public function get($id); - - /** - * @param string $id The id of the cached resource - * @param ResponseInterface $response The response to cache - * - * @throws \InvalidArgumentException If cache data cannot be saved - */ - public function set($id, ResponseInterface $response); -} diff --git a/lib/Github/HttpClient/Cache/FilesystemCache.php b/lib/Github/HttpClient/Cache/FilesystemCache.php deleted file mode 100644 index 82581b62a75..00000000000 --- a/lib/Github/HttpClient/Cache/FilesystemCache.php +++ /dev/null @@ -1,85 +0,0 @@ -path = $path; - } - - /** - * {@inheritdoc} - */ - public function get($id) - { - if (false !== $content = @file_get_contents($this->getPath($id))) { - return ResponseSerializer::unserialize($content); - } - - throw new \InvalidArgumentException(sprintf('File "%s" not found', $this->getPath($id))); - } - - /** - * {@inheritdoc} - */ - public function set($id, ResponseInterface $response) - { - if (!is_dir($this->path)) { - @mkdir($this->path, 0777, true); - } - - if (false === @file_put_contents($this->getPath($id), ResponseSerializer::serialize($response))) { - throw new \InvalidArgumentException(sprintf('Cannot put content in file "%s"', $this->getPath($id))); - } - if (false === @file_put_contents($this->getPath($id).'.etag', $response->getHeader('ETag'))) { - throw new \InvalidArgumentException(sprintf('Cannot put content in file "%s"', $this->getPath($id).'.etag')); - } - } - - /** - * {@inheritdoc} - */ - public function has($id) - { - return file_exists($this->getPath($id)); - } - - /** - * {@inheritdoc} - */ - public function getModifiedSince($id) - { - if ($this->has($id)) { - return filemtime($this->getPath($id)); - } - } - - public function getETag($id) - { - if (file_exists($this->getPath($id).'.etag')) { - return file_get_contents($this->getPath($id).'.etag'); - } - } - - /** - * @param $id string - * - * @return string - */ - protected function getPath($id) - { - return sprintf('%s%s%s', rtrim($this->path, DIRECTORY_SEPARATOR), DIRECTORY_SEPARATOR, md5($id)); - } -} diff --git a/lib/Github/HttpClient/Cache/GaufretteCache.php b/lib/Github/HttpClient/Cache/GaufretteCache.php deleted file mode 100644 index 812be7cbbb2..00000000000 --- a/lib/Github/HttpClient/Cache/GaufretteCache.php +++ /dev/null @@ -1,71 +0,0 @@ - - */ -class GaufretteCache implements CacheInterface -{ - /** - * @var Filesystem - */ - protected $filesystem; - - /** - * @param Filesystem $filesystem - */ - public function __construct(Filesystem $filesystem) - { - $this->filesystem = $filesystem; - } - - /** - * {@inheritdoc} - */ - public function get($id) - { - $content = $this->filesystem->read($id); - - return ResponseSerializer::unserialize($content); - } - - /** - * {@inheritdoc} - */ - public function set($id, ResponseInterface $response) - { - $this->filesystem->write($id, ResponseSerializer::serialize($response), true); - $this->filesystem->write($id.'.etag', $response->getHeader('ETag'), true); - } - - /** - * {@inheritdoc} - */ - public function has($id) - { - $this->filesystem->has($id); - } - - /** - * {@inheritdoc} - */ - public function getModifiedSince($id) - { - if ($this->filesystem->has($id)) { - return $this->filesystem->mtime($id); - } - } - - public function getETag($id) - { - if ($this->filesystem->has($id)) { - return $this->filesystem->read($id.'.etag'); - } - } -} diff --git a/lib/Github/HttpClient/Cache/ResponseSerializer.php b/lib/Github/HttpClient/Cache/ResponseSerializer.php deleted file mode 100644 index 43b8d95fa05..00000000000 --- a/lib/Github/HttpClient/Cache/ResponseSerializer.php +++ /dev/null @@ -1,61 +0,0 @@ - - */ -class ResponseSerializer -{ - /** - * @param ResponseInterface $response - * @param StreamFactory|null $streamFactory - * - * @return array - */ - public static function serialize(ResponseInterface $response, StreamFactory $streamFactory = null) - { - $streamFactory = $streamFactory ?: StreamFactoryDiscovery::find(); - - $bodyStream = $response->getBody(); - $body = $bodyStream->__toString(); - if ($bodyStream->isSeekable()) { - $bodyStream->rewind(); - } else { - /* - * If the body is not seekbable we can not rewind it. The stream could be a type of stream - * that you only can read once. That is why we have to replace the old stream with a new one. - */ - $response = $response->withBody($streamFactory->createStream($body)); - } - - return serialize(array('response' => serialize($response), 'body' => $body)); - } - - /** - * @param $data - * @param StreamFactory|null $streamFactory - * - * @return ResponseInterface|null - */ - public static function unserialize($serializedData, StreamFactory $streamFactory = null) - { - $data = unserialize($serializedData); - if (!isset($data['response']) || !isset($data['body'])) { - return null; - } - - $streamFactory = $streamFactory ?: StreamFactoryDiscovery::find(); - - $response = unserialize($data['response']); - $response = $response->withBody($streamFactory->createStream($data['body'])); - - return $response; - } -} diff --git a/lib/Github/HttpClient/Plugin/Cache.php b/lib/Github/HttpClient/Plugin/Cache.php deleted file mode 100644 index cc4397dd59e..00000000000 --- a/lib/Github/HttpClient/Plugin/Cache.php +++ /dev/null @@ -1,87 +0,0 @@ - - * @author Tobias Nyholm - */ -class Cache implements Plugin -{ - /** - * @var CacheInterface - */ - protected $cache; - - /** - * - * @param CacheInterface $cache - */ - public function __construct(CacheInterface $cache = null) - { - $this->cache = $cache; - } - - /** - * {@inheritdoc} - */ - public function handleRequest(RequestInterface $request, callable $next, callable $first) - { - $cacheKey = sha1($request->getUri()->__toString()); - - if ($modifiedAt = $this->getCache()->getModifiedSince($cacheKey)) { - $modifiedAt = new \DateTime('@'.$modifiedAt); - $modifiedAt->setTimezone(new \DateTimeZone('GMT')); - - $request = $request->withHeader( - 'If-Modified-Since', - sprintf('%s GMT', $modifiedAt->format('l, d-M-y H:i:s')) - ); - } - if ($etag = $this->getCache()->getETag($cacheKey)) { - $request = $request->withHeader( - 'If-None-Match', - $etag - ); - } - - return $next($request)->then(function (ResponseInterface $response) use ($request, $cacheKey) { - if (304 === $response->getStatusCode()) { - $cacheResponse = $this->getCache()->get($cacheKey); - $this->lastCachedResponse = $cacheResponse; - - return $cacheResponse; - } - - if (in_array($request->getMethod(), array('GET', 'HEAD'), true)) { - $this->getCache()->set($cacheKey, $response); - } - - return $response; - }); - } - - - /** - * @return CacheInterface - */ - public function getCache() - { - if (null === $this->cache) { - $this->cache = new FilesystemCache(sys_get_temp_dir().DIRECTORY_SEPARATOR.'php-github-api-cache'); - } - - return $this->cache; - } -} diff --git a/test/Github/Tests/HttpClient/Cache/FilesystemCacheTest.php b/test/Github/Tests/HttpClient/Cache/FilesystemCacheTest.php deleted file mode 100644 index 6383fecad5a..00000000000 --- a/test/Github/Tests/HttpClient/Cache/FilesystemCacheTest.php +++ /dev/null @@ -1,43 +0,0 @@ -set('test', new Response(200)); - - $this->assertNotNull($cache->get('test')); - } - - /** - * @test - */ - public function shouldGetATimestampForExistingFile() - { - $cache = new FilesystemCache('/tmp/github-api-test'); - - $cache->set('test', new Response(200)); - - $this->assertInternalType('int', $cache->getModifiedSince('test')); - } - - /** - * @test - */ - public function shouldNotGetATimestampForInexistingFile() - { - $cache = new FilesystemCache('/tmp/github-api-test'); - - $this->assertNull($cache->getModifiedSince('test2')); - } -} diff --git a/test/Github/Tests/HttpClient/Cache/GaufretteCacheTest.php b/test/Github/Tests/HttpClient/Cache/GaufretteCacheTest.php deleted file mode 100644 index 2e033f53b46..00000000000 --- a/test/Github/Tests/HttpClient/Cache/GaufretteCacheTest.php +++ /dev/null @@ -1,84 +0,0 @@ -markTestSkipped('Gaufrette not installed.'); - } - } - - /** - * @test - */ - public function shouldStoreAResponseForAGivenKey() - { - $response = new Response(200); - $filesystem = $this->getMockBuilder('Gaufrette\Filesystem')->disableOriginalConstructor()->getMock(); - $filesystem - ->expects($this->once()) - ->method('write') - ->with('test', serialize($response)) - ; - $filesystem - ->expects($this->once()) - ->method('read') - ->with('test') - ->will($this->returnValue('a:0:{}')) - ; - - $cache = new GaufretteCache($filesystem); - $cache->set('test', $response); - $this->assertNotNull($cache->get('test')); - } - - /** - * @test - */ - public function shouldGetATimestampForExistingFile() - { - $response = new Response(200); - $filesystem = $this->getMockBuilder('Gaufrette\Filesystem')->disableOriginalConstructor()->getMock(); - $filesystem - ->expects($this->once()) - ->method('has') - ->with('test') - ->will($this->returnValue(true)) - ; - $filesystem - ->expects($this->once()) - ->method('mtime') - ->with('test') - ->will($this->returnValue(100)) - ; - - $cache = new GaufretteCache($filesystem); - $cache->set('test', new Response(200)); - - $this->assertInternalType('int', $cache->getModifiedSince('test')); - } - - /** - * @test - */ - public function shouldNotGetATimestampForInexistingFile() - { - $filesystem = $this->getMockBuilder('Gaufrette\Filesystem')->disableOriginalConstructor()->getMock(); - $filesystem - ->expects($this->once()) - ->method('has') - ->with('test2') - ->will($this->returnValue(false)) - ; - - $cache = new GaufretteCache($filesystem); - - $this->assertNull($cache->getModifiedSince('test2')); - } -}