Skip to content

Commit 3059848

Browse files
committed
Merge pull request #10 from scaytrase/feature/decorators
Decorators
2 parents 4fa3219 + 57ff5df commit 3059848

11 files changed

+531
-79
lines changed

composer.json

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,17 @@
1515
"paragonie/random_compat": "~1.1@stable"
1616
},
1717
"require-dev": {
18-
"phpunit/phpunit": "~4.5|~5.1"
18+
"phpunit/phpunit": "~4.5|~5.1",
19+
"psr/log": "~1.0",
20+
"psr/cache": "~1.0"
1921
},
2022
"autoload": {
2123
"psr-4": {
2224
"": "src/"
2325
}
26+
},
27+
"suggest": {
28+
"psr/log": "For LoggableRpcClient decorator",
29+
"psr/cache": "For CacheableRpcClient decorator"
2430
}
2531
}
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
<?php
2+
/**
3+
* Created by PhpStorm.
4+
* User: batanov.pavel
5+
* Date: 18.03.2016
6+
* Time: 15:27
7+
*/
8+
9+
namespace ScayTrase\Api\Rpc\Decorators;
10+
11+
use Psr\Cache\CacheItemInterface;
12+
use Psr\Cache\CacheItemPoolInterface;
13+
use ScayTrase\Api\Rpc\ResponseCollectionInterface;
14+
use ScayTrase\Api\Rpc\RpcRequestInterface;
15+
16+
final class CacheableResponseCollection implements \IteratorAggregate, ResponseCollectionInterface
17+
{
18+
/** @var CacheItemPoolInterface */
19+
private $cache;
20+
/** @var RequestKeyExtractor */
21+
private $extractor;
22+
/** @var array */
23+
private $items;
24+
/** @var ResponseCollectionInterface */
25+
private $proxiedCollection;
26+
/** @var int|null */
27+
private $ttl;
28+
29+
/**
30+
* CacheableResponseCollection constructor.
31+
*
32+
* @param CacheItemPoolInterface $cache
33+
* @param RequestKeyExtractor $extractor
34+
* @param array $items
35+
* @param ResponseCollectionInterface $proxiedCollection
36+
*/
37+
public function __construct(
38+
CacheItemPoolInterface $cache,
39+
RequestKeyExtractor $extractor,
40+
array $items,
41+
ResponseCollectionInterface $proxiedCollection,
42+
$ttl
43+
)
44+
{
45+
$this->cache = $cache;
46+
$this->extractor = $extractor;
47+
$this->items = $items;
48+
$this->proxiedCollection = $proxiedCollection;
49+
$this->ttl = $ttl ?: null;
50+
}
51+
52+
53+
/** {@inheritdoc} */
54+
public function getResponse(RpcRequestInterface $request)
55+
{
56+
$key = $this->extractor->getKey($request);
57+
58+
/** @var CacheItemInterface $item */
59+
$item = $this->items[$key]['item'];
60+
61+
if ($item->isHit()) {
62+
return $item->get();
63+
}
64+
65+
$item->expiresAfter($this->ttl);
66+
$item->set($this->proxiedCollection->getResponse($request));
67+
68+
$this->cache->save($item);
69+
70+
return $item->get();
71+
}
72+
73+
/** {@inheritdoc} */
74+
public function count()
75+
{
76+
return count($this->items) + $this->proxiedCollection->count();
77+
}
78+
79+
/** {@inheritdoc} */
80+
public function getIterator()
81+
{
82+
foreach ($this->items as $key => $data) {
83+
/** @var CacheItemInterface $item */
84+
$item = $data['item'];
85+
86+
if ($item->isHit()) {
87+
yield $item->get();
88+
}
89+
90+
yield $this->getResponse($data['request']);
91+
}
92+
}
93+
}
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
<?php
2+
/**
3+
* Created by PhpStorm.
4+
* User: batanov.pavel
5+
* Date: 18.03.2016
6+
* Time: 15:26
7+
*/
8+
9+
namespace ScayTrase\Api\Rpc\Decorators;
10+
11+
use Psr\Cache\CacheItemPoolInterface;
12+
use ScayTrase\Api\Rpc\RpcClientInterface;
13+
14+
final class CacheableRpcClient implements RpcClientInterface
15+
{
16+
const DEFAULT_KEY_PREFIX = 'rpc_client_cache';
17+
18+
/** @var CacheItemPoolInterface */
19+
private $cache;
20+
/** @var RpcClientInterface */
21+
private $decoratedClient;
22+
/** @var RequestKeyExtractor */
23+
private $extractor;
24+
/** @var int|null */
25+
private $ttl;
26+
27+
/**
28+
* CacheableRpcClient constructor.
29+
*
30+
* @param RpcClientInterface $decoratedClient
31+
* @param CacheItemPoolInterface $cache
32+
* @param int|null $ttl
33+
* @param string $keyPrefix
34+
*/
35+
public function __construct(
36+
RpcClientInterface $decoratedClient,
37+
CacheItemPoolInterface $cache,
38+
$ttl = null,
39+
$keyPrefix = self::DEFAULT_KEY_PREFIX)
40+
{
41+
$this->decoratedClient = $decoratedClient;
42+
$this->cache = $cache;
43+
$this->ttl = $ttl;
44+
45+
$this->extractor = new RequestKeyExtractor((string)$keyPrefix);
46+
}
47+
48+
/** {@inheritdoc} */
49+
public function invoke($calls)
50+
{
51+
if (!is_array($calls)) {
52+
$calls = [$calls];
53+
}
54+
55+
$items = [];
56+
$proxiedRequests = [];
57+
foreach ($calls as $call) {
58+
$key = $this->extractor->getKey($call);
59+
$item = $this->cache->getItem($key);
60+
$items[$key]['request'] = $call;
61+
$items[$key]['item'] = $item;
62+
if (!$item->isHit()) {
63+
$proxiedRequests[] = $call;
64+
}
65+
}
66+
67+
// Prevent batch calls when not necessary
68+
if (count($proxiedRequests) === 1 && !is_array($calls)) {
69+
$proxiedRequests = array_shift($proxiedRequests);
70+
}
71+
72+
return new CacheableResponseCollection(
73+
$this->cache,
74+
$this->extractor,
75+
$items,
76+
$this->decoratedClient->invoke($proxiedRequests),
77+
$this->ttl
78+
);
79+
}
80+
}

src/ScayTrase/Api/Rpc/LazyResponseCollection.php renamed to src/ScayTrase/Api/Rpc/Decorators/LazyResponseCollection.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,11 @@
66
* Time: 10:34
77
*/
88

9-
namespace ScayTrase\Api\Rpc;
9+
namespace ScayTrase\Api\Rpc\Decorators;
10+
11+
use ScayTrase\Api\Rpc\ResponseCollectionInterface;
12+
use ScayTrase\Api\Rpc\RpcClientInterface;
13+
use ScayTrase\Api\Rpc\RpcRequestInterface;
1014

1115
final class LazyResponseCollection implements \IteratorAggregate, ResponseCollectionInterface
1216
{

src/ScayTrase/Api/Rpc/LazyRpcClient.php renamed to src/ScayTrase/Api/Rpc/Decorators/LazyRpcClient.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@
66
* Time: 10:31
77
*/
88

9-
namespace ScayTrase\Api\Rpc;
9+
namespace ScayTrase\Api\Rpc\Decorators;
10+
11+
use ScayTrase\Api\Rpc\RpcClientInterface;
1012

1113
final class LazyRpcClient implements RpcClientInterface
1214
{
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
<?php
2+
/**
3+
* Created by PhpStorm.
4+
* User: batanov.pavel
5+
* Date: 18.03.2016
6+
* Time: 15:20
7+
*/
8+
9+
namespace ScayTrase\Api\Rpc\Decorators;
10+
11+
use Psr\Log\LoggerInterface;
12+
use ScayTrase\Api\Rpc\ResponseCollectionInterface;
13+
use ScayTrase\Api\Rpc\RpcRequestInterface;
14+
15+
final class LoggableResponseCollection implements \IteratorAggregate, ResponseCollectionInterface
16+
{
17+
/** @var LoggerInterface */
18+
private $logger;
19+
/** @var ResponseCollectionInterface */
20+
private $decoratedCollection;
21+
22+
/**
23+
* LoggableResponseCollection constructor.
24+
*
25+
* @param ResponseCollectionInterface $decoratedCollection
26+
* @param LoggerInterface $logger
27+
*/
28+
public function __construct(ResponseCollectionInterface $decoratedCollection, LoggerInterface $logger)
29+
{
30+
$this->decoratedCollection = $decoratedCollection;
31+
$this->logger = $logger;
32+
}
33+
34+
/** {@inheritdoc} */
35+
public function getResponse(RpcRequestInterface $request)
36+
{
37+
$response = $this->decoratedCollection->getResponse($request);
38+
39+
$this->logger->debug(
40+
sprintf(
41+
'%s Response for RPC method "%s" is %s',
42+
spl_object_hash($request),
43+
$request->getMethod(),
44+
$response->isSuccessful() ? 'Successful' : 'Failed'
45+
),
46+
json_decode(json_encode($response->getBody()), true)
47+
);
48+
if ($response->isSuccessful()) {
49+
$this->logger->debug(
50+
sprintf('%s Response for RPC method "%s"', spl_object_hash($request), $request->getMethod()),
51+
json_decode(json_encode($response->getBody()), true)
52+
);
53+
} else {
54+
$this->logger->debug(
55+
sprintf('%s Response for RPC method "%s"', spl_object_hash($request), $request->getMethod()),
56+
json_decode(json_encode($response->getError()), true)
57+
);
58+
}
59+
60+
return $response;
61+
}
62+
63+
/** {@inheritdoc} */
64+
public function count()
65+
{
66+
return $this->decoratedCollection->count();
67+
}
68+
69+
/** {@inheritdoc} */
70+
public function getIterator()
71+
{
72+
foreach ($this->decoratedCollection as $response) {
73+
//todo: log
74+
yield $response;
75+
}
76+
}
77+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
<?php
2+
/**
3+
* Created by PhpStorm.
4+
* User: batanov.pavel
5+
* Date: 18.03.2016
6+
* Time: 15:16
7+
*/
8+
9+
namespace ScayTrase\Api\Rpc\Decorators;
10+
11+
use Psr\Log\LoggerInterface;
12+
use Psr\Log\NullLogger;
13+
use ScayTrase\Api\Rpc\RpcClientInterface;
14+
15+
final class LoggableRpcClient implements RpcClientInterface
16+
{
17+
/** @var LoggerInterface */
18+
private $logger;
19+
/** @var RpcClientInterface */
20+
private $decoratedClient;
21+
22+
/**
23+
* LoggableRpcClient constructor.
24+
*
25+
* @param RpcClientInterface $decoratedClient
26+
* @param LoggerInterface $logger
27+
*/
28+
public function __construct(RpcClientInterface $decoratedClient, LoggerInterface $logger = null)
29+
{
30+
$this->decoratedClient = $decoratedClient;
31+
$this->logger = $logger;
32+
33+
if (null === $this->logger) {
34+
$this->logger = new NullLogger();
35+
}
36+
}
37+
38+
/** {@inheritdoc} */
39+
public function invoke($calls)
40+
{
41+
if (!is_array($calls)) {
42+
$calls = [$calls];
43+
}
44+
45+
foreach ($calls as $call) {
46+
$this->logger->debug(
47+
sprintf('%s Invoking RPC method "%s"', spl_object_hash($call), $call->getMethod()),
48+
json_decode(json_encode($call->getParameters()), true)
49+
);
50+
}
51+
52+
return new LoggableResponseCollection($this->decoratedClient->invoke($calls), $this->logger);
53+
}
54+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<?php
2+
/**
3+
* Created by PhpStorm.
4+
* User: batanov.pavel
5+
* Date: 18.03.2016
6+
* Time: 15:31
7+
*/
8+
9+
namespace ScayTrase\Api\Rpc\Decorators;
10+
11+
use ScayTrase\Api\Rpc\RpcRequestInterface;
12+
13+
/** @internal */
14+
class RequestKeyExtractor
15+
{
16+
/** @var string */
17+
private $keyPrefix;
18+
19+
/**
20+
* RequestKeyExtractor constructor.
21+
*
22+
* @param $keyPrefix
23+
*/
24+
public function __construct($keyPrefix) { $this->keyPrefix = $keyPrefix; }
25+
26+
27+
public function getKey(RpcRequestInterface $request)
28+
{
29+
$data = [
30+
'method' => (string)$request->getMethod(),
31+
'params' => json_decode(json_encode($request->getParameters()), true),
32+
];
33+
34+
$stringData = json_encode($data);
35+
36+
return $this->keyPrefix . sha1($stringData);
37+
}
38+
}

0 commit comments

Comments
 (0)