Skip to content

Commit

Permalink
Merge pull request #111 from wandersonwhcr/feature/cache
Browse files Browse the repository at this point in the history
Cache
  • Loading branch information
wandersonwhcr authored Nov 10, 2021
2 parents 9c2f8c5 + 8dbfee0 commit d7a0e6d
Show file tree
Hide file tree
Showing 9 changed files with 320 additions and 10 deletions.
28 changes: 27 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@ these PHP releases:
* Romans `1.1.*`: PHP `^7.0` (Tiberius)
* Romans `1.2.*`: PHP `>=7.4` (Caligula)
* Romans `1.3.*`: PHP `>=7.4` (Claudius)
* Romans `1.4.*`: PHP `>=8.1` (Nero)
* Romans `1.4.*`: PHP `>=7.4` (Nero)
* Romans `1.5.*`: PHP `>=8.0` (Galba)

## Integrations

Expand Down Expand Up @@ -154,6 +155,31 @@ $filter = new IntToRoman();
$result = $filter->filter(0); // N
```

### Cache

This package uses [PSR-6 Caching Interface](https://www.php-fig.org/psr/psr-6)
to improve execution, mainly over loops (like `while` or `foreach`) using cache
libraries. Any PSR-6 implementation can be used and we suggest
[Symfony Cache](https://packagist.org/packages/symfony/cache) package.

```php
use Romans\Filter\IntToRoman;
use Romans\Filter\RomanToInt;
use Symfony\Component\Cache\Adapter\ArrayAdapter;

$cache = new ArrayAdapter();

$filter = new RomanToInt();
$filter->setCache($cache);
$result = $filter->filter('MCMXCIX'); // 1999
$result = $filter->filter('MCMXCIX'); // 1999 (from cache)

$filter = new IntToRoman();
$filter->setCache($cache);
$result = $filter->filter(1999); // MCMXCIX
$result = $filter->filter(1999); // MCMXCIX (from cache)
```

## Development

You can use Docker Compose to build an image and run a container to develop and
Expand Down
4 changes: 4 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,14 @@
"require": {
"php": ">=7.4"
},
"suggest": {
"symfony/cache": "Cache results to improve performance (or any PSR-6 implementation)"
},
"require-dev": {
"php-parallel-lint/php-parallel-lint": "1.3.*",
"phpmd/phpmd": "2.10.*",
"phpunit/phpunit": "9.5.*",
"psr/cache": "1.0.*",
"sebastian/phpcpd": "6.0.*",
"slevomat/coding-standard": "7.0.*",
"squizlabs/php_codesniffer": "3.6.*"
Expand Down
51 changes: 50 additions & 1 deletion composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

47 changes: 47 additions & 0 deletions src/Cache/CacheAwareTrait.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<?php

declare(strict_types=1);

namespace Romans\Cache;

use Psr\Cache\CacheItemPoolInterface as CacheInterface;

/**
* Cache Aware Trait
*/
trait CacheAwareTrait
{
private ?CacheInterface $cache = null;

/**
* Has Cache?
*
* @return bool Cache Exists
*/
public function hasCache(): bool
{
return $this->cache !== null;
}

/**
* Set Cache
*
* @param ?CacheInterface $cache Cache Object
* @param self Fluent Interface
*/
public function setCache(?CacheInterface $cache): self
{
$this->cache = $cache;
return $this;
}

/**
* Get Cache
*
* @return ?CacheInterface Cache Object
*/
public function getCache(): ?CacheInterface
{
return $this->cache;
}
}
33 changes: 26 additions & 7 deletions src/Filter/IntToRoman.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace Romans\Filter;

use Romans\Cache\CacheAwareTrait;
use Romans\Grammar\Grammar;
use Romans\Grammar\GrammarAwareTrait;

Expand All @@ -12,6 +13,7 @@
*/
class IntToRoman
{
use CacheAwareTrait;
use GrammarAwareTrait;

/**
Expand All @@ -24,6 +26,20 @@ public function __construct(?Grammar $grammar = null)
$this->setGrammar($grammar ?? new Grammar());
}

/**
* Helper to Cache a Result from Value
*
* @param int $value Integer
* @param string Roman Number Result
*/
private function cache(int $value, string $result): void
{
if ($this->hasCache()) {
$item = $this->getCache()->getItem($value)->set($result);
$this->getCache()->save($item);
}
}

/**
* Filter Integer to Roman Number
*
Expand All @@ -36,29 +52,32 @@ public function filter(int $value): string
throw new Exception(sprintf('Invalid integer: %d', $value), Exception::INVALID_INTEGER);
}

if ($this->hasCache() && $this->getCache()->hasItem($value)) {
return $this->getCache()->getItem($value)->get();
}

$tokens = $this->getGrammar()->getTokens();
$values = array_reverse($this->getGrammar()->getValuesWithModifiers(), true /* preserve keys */);
$result = '';

if ($value === 0) {
$dataset = $values[0];
$result = array_reduce($dataset, fn($result, $token) => $result . $tokens[$token], $result);

foreach ($dataset as $token) {
$result = $result . $tokens[$token];
}
$this->cache($value, $result);

return $result;
}

foreach ($values as $current => $dataset) {
while ($current > 0 && $value >= $current) {
$value = $value - $current;
foreach ($dataset as $token) {
$result = $result . $tokens[$token];
}
$value = $value - $current;
$result = array_reduce($dataset, fn($result, $token) => $result . $tokens[$token], $result);
}
}

$this->cache($value, $result);

return $result;
}
}
16 changes: 15 additions & 1 deletion src/Filter/RomanToInt.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace Romans\Filter;

use Romans\Cache\CacheAwareTrait;
use Romans\Grammar\Grammar;
use Romans\Lexer\Lexer;
use Romans\Parser\Parser;
Expand All @@ -13,6 +14,8 @@
*/
class RomanToInt
{
use CacheAwareTrait;

/**
* Lexer
*/
Expand Down Expand Up @@ -89,6 +92,17 @@ public function getParser(): Parser
*/
public function filter(string $value): int
{
return $this->getParser()->parse($this->getLexer()->tokenize($value));
if ($this->hasCache() && $this->getCache()->hasItem($value)) {
return $this->getCache()->getItem($value)->get();
}

$result = $this->getParser()->parse($this->getLexer()->tokenize($value));

if ($this->hasCache()) {
$item = $this->getCache()->getItem($value)->set($result);
$this->getCache()->save($item);
}

return $result;
}
}
35 changes: 35 additions & 0 deletions test/Cache/CacheAwareTraitTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php

declare(strict_types=1);

namespace RomansTest\Cache;

use PHPUnit\Framework\TestCase;
use Psr\Cache\CacheItemPoolInterface as CacheInterface;
use Romans\Cache\CacheAwareTrait;

/**
* Cache Aware Trait Test
*/
class CacheAwareTraitTest extends TestCase
{
/**
* Test Cache
*/
public function testCache(): void
{
$cache = $this->createMock(CacheInterface::class);
$element = $this->getMockForTrait(CacheAwareTrait::class);

$this->assertNull($element->getCache());
$this->assertFalse($element->hasCache());

$this->assertSame($element, $element->setCache($cache));
$this->assertSame($cache, $element->getCache());
$this->assertTrue($element->hasCache());

$this->assertSame($element, $element->setCache(null));
$this->assertNull($element->getCache());
$this->assertFalse($element->hasCache());
}
}
58 changes: 58 additions & 0 deletions test/Filter/IntToRomanTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
namespace RomansTest\Filter;

use PHPUnit\Framework\TestCase;
use Psr\Cache\CacheItemInterface as CacheItemInterface;
use Psr\Cache\CacheItemPoolInterface as CacheInterface;
use Romans\Filter\Exception as FilterException;
use Romans\Filter\IntToRoman;
use Romans\Grammar\Grammar;
Expand Down Expand Up @@ -80,4 +82,60 @@ public function testFilterWithNegative(): void

$this->filter->filter(-1);
}

/**
* Test Cache Found
*/
public function testCacheFound(): void
{
$item = $this->createMock(CacheItemInterface::class);
$cache = $this->createMock(CacheInterface::class);

$item->expects($this->once())
->method('get')
->willReturn('I');

$cache->method('hasItem')
->with($this->equalTo('1'))
->willReturn(true);

$cache->expects($this->once())
->method('getItem')
->willReturn($item);

$this->filter->setCache($cache);

$this->assertSame('I', $this->filter->filter(1));
}

/**
* Test Cache not Found
*/
public function testCacheNotFound(): void
{
$item = $this->createMock(CacheItemInterface::class);
$cache = $this->createMock(CacheInterface::class);

$item->expects($this->once())
->method('set')
->with($this->equalTo('I'))
->willReturnSelf();

$cache->method('hasItem')
->with($this->equalTo('1'))
->willReturn(false);

$cache->expects($this->once())
->method('getItem')
->willReturn($item);

$cache->expects($this->once())
->method('save')
->with($this->equalTo($item))
->willReturn(true);

$this->filter->setCache($cache);

$this->assertSame('I', $this->filter->filter(1));
}
}
Loading

0 comments on commit d7a0e6d

Please sign in to comment.