Skip to content

Commit 32630a7

Browse files
committed
initial commit
0 parents  commit 32630a7

12 files changed

+363
-0
lines changed

Diff for: .github/workflows/continuous-integration.yml

+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
name: Continuous Integration
2+
3+
on:
4+
push:
5+
branches:
6+
- master
7+
pull_request:
8+
workflow_dispatch:
9+
10+
jobs:
11+
build:
12+
13+
runs-on: ubuntu-latest
14+
15+
strategy:
16+
matrix:
17+
operating-system:
18+
- ubuntu-latest
19+
php-version:
20+
- '8.0'
21+
- '8.1'
22+
- '8.2'
23+
steps:
24+
- name: Checkout
25+
uses: actions/checkout@v1
26+
27+
- name: Setup PHP ${{ matrix.php-version }}
28+
uses: shivammathur/setup-php@v2
29+
with:
30+
php-version: ${{ matrix.php-version }}
31+
coverage: none
32+
tools: none
33+
ini-values: assert.exception=1, zend.assertions=1
34+
35+
- name: Get composer cache directory
36+
id: composer-cache
37+
run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
38+
39+
- name: Cache dependencies
40+
uses: actions/cache@v3
41+
with:
42+
path: ${{ steps.composer-cache.outputs.dir }}
43+
key: ${{ runner.os }}-php${{ matrix.php-version }}-composer-${{ hashFiles('**/composer.json') }}
44+
restore-keys: ${{ runner.os }}-php${{ matrix.php-version }}-composer-
45+
46+
- name: Install dependencies
47+
run: composer install --no-interaction --prefer-dist
48+
49+
- name: QA
50+
run: PHP_CS_FIXER_IGNORE_ENV=1 composer qa

Diff for: .gitignore

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
/vendor/
2+
/phpunit.xml
3+
/composer.lock
4+
.php_cs.cache
5+
.phpunit.result.cache

Diff for: .php-cs-fixer.dist.php

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?php
2+
3+
$finder = \PhpCsFixer\Finder::create()
4+
->exclude('vendor')
5+
->in(__DIR__)
6+
;
7+
8+
return (new \PhpCsFixer\Config())
9+
->setRules([
10+
'@Symfony' => true,
11+
'concat_space' => ['spacing' => 'one'],
12+
'phpdoc_summary' => false,
13+
'yoda_style' => false,
14+
'global_namespace_import' => true,
15+
])
16+
->setFinder($finder)
17+
->setUsingCache(false)
18+
;

Diff for: README.md

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# Memoization
2+
3+
## Usage
4+
5+
```php
6+
7+
namespace App;
8+
9+
use Kalibora\Memoization\MemoizationTrait;
10+
11+
class HeavyProcessor
12+
{
13+
use MemoizationTrait;
14+
15+
public function process(): void
16+
{
17+
return $this->getMemoization()->memoize(__FUNCTION__, function () {
18+
// Very heavy processing takes place here.
19+
20+
return $value;
21+
});
22+
}
23+
}
24+
```

Diff for: composer.json

+49
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
{
2+
"name": "kalibora/memoization",
3+
"description": "A small helper for memoizing.",
4+
"keywords": ["memoization", "memoize"],
5+
"license": "MIT",
6+
"authors": [
7+
{
8+
9+
"name": "Toshiyuki Fujita",
10+
"email": "[email protected]"
11+
}
12+
],
13+
"autoload": {
14+
"psr-4": {"Kalibora\\Memoization\\": "src"}
15+
},
16+
"autoload-dev": {
17+
"psr-4": {"Kalibora\\Memoization\\": "tests"}
18+
},
19+
"scripts": {
20+
"qa": [
21+
"@test",
22+
"@analyse",
23+
"@cs"
24+
],
25+
"test" : [
26+
"phpunit"
27+
],
28+
"analyse" : [
29+
"phpstan analyse --no-progress --memory-limit=-1"
30+
],
31+
"analyze" : [
32+
"@analyse"
33+
],
34+
"cs": [
35+
"php-cs-fixer fix -v --dry-run --diff"
36+
],
37+
"cs-fix": [
38+
"php-cs-fixer fix -v"
39+
]
40+
},
41+
"require": {
42+
"php": "^8.0"
43+
},
44+
"require-dev": {
45+
"phpunit/phpunit": "^9.6",
46+
"phpstan/phpstan": "^1.0",
47+
"friendsofphp/php-cs-fixer": "^3.0"
48+
}
49+
}

Diff for: phpstan.neon.dist

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
parameters:
2+
level: max
3+
paths:
4+
- src
5+
- tests

Diff for: phpunit.xml.dist

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?xml version="1.0"?>
2+
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" bootstrap="./tests/bootstrap.php" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd">
3+
<coverage processUncoveredFiles="true">
4+
<include>
5+
<directory suffix=".php">src</directory>
6+
</include>
7+
</coverage>
8+
<testsuites>
9+
<testsuite name="main">
10+
<directory suffix="Test.php">./tests</directory>
11+
</testsuite>
12+
</testsuites>
13+
<php>
14+
<ini name="error_reporting" value="-1"/>
15+
</php>
16+
</phpunit>

Diff for: src/Memoization.php

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<?php
2+
3+
namespace Kalibora\Memoization;
4+
5+
class Memoization
6+
{
7+
/**
8+
* @var array<string, mixed>
9+
*/
10+
private array $data = [];
11+
12+
/**
13+
* @phpstan-template T
14+
*
15+
* @param callable(): T $fetch
16+
*
17+
* @return T
18+
*/
19+
public function memoize(string $key, callable $fetch): mixed
20+
{
21+
if (!array_key_exists($key, $this->data)) {
22+
$result = call_user_func($fetch);
23+
24+
$this->data[$key] = $result;
25+
}
26+
27+
return $this->data[$key];
28+
}
29+
30+
public function clear(string $key): void
31+
{
32+
unset($this->data[$key]);
33+
}
34+
35+
public function clearAll(): void
36+
{
37+
$this->data = [];
38+
}
39+
}

Diff for: src/MemoizationTrait.php

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?php
2+
3+
namespace Kalibora\Memoization;
4+
5+
trait MemoizationTrait
6+
{
7+
private ?Memoization $memoization = null;
8+
9+
private function getMemoization(): Memoization
10+
{
11+
if ($this->memoization === null) {
12+
$this->memoization = new Memoization();
13+
}
14+
15+
return $this->memoization;
16+
}
17+
}

Diff for: tests/MemoizationTest.php

+115
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
<?php
2+
3+
namespace Kalibora\Memoization;
4+
5+
use PHPUnit\Framework\TestCase;
6+
7+
/**
8+
* @phpstan-type DependsData = array{0: Memoization, 1: int, 2: callable}
9+
*/
10+
class MemoizationTest extends TestCase
11+
{
12+
/**
13+
* @phpstan-return DependsData
14+
*/
15+
public function testInnerProcessIsCalledOnlyOnceWhenMultipleCalls(): array
16+
{
17+
$memo = new Memoization();
18+
$count = 0;
19+
20+
$func = function () use ($memo, &$count) {
21+
return $memo->memoize('foo', function () use (&$count) {
22+
return ++$count;
23+
});
24+
};
25+
26+
$this->assertSame(1, $func());
27+
$this->assertSame(1, $func());
28+
$this->assertSame(1, $func());
29+
30+
return [$memo, $count, $func];
31+
}
32+
33+
/**
34+
* @depends testInnerProcessIsCalledOnlyOnceWhenMultipleCalls
35+
*
36+
* @phpstan-param DependsData $data
37+
*
38+
* @phpstan-return DependsData
39+
*/
40+
public function testInnerProcessIsCalledOnlyOnceAgainAfterCleared(array $data): array
41+
{
42+
list($memo, $count, $func) = $data;
43+
44+
$memo->clear('foo');
45+
$this->assertSame(2, $func());
46+
$this->assertSame(2, $func());
47+
$this->assertSame(2, $func());
48+
49+
$memo->clear('foo');
50+
$this->assertSame(3, $func());
51+
$this->assertSame(3, $func());
52+
$this->assertSame(3, $func());
53+
54+
return [$memo, $count, $func];
55+
}
56+
57+
public function testInnerProcessIsCalledOnlyOnceWhenMultipleCallsAndReturnValueIsNull(): void
58+
{
59+
$memo = new Memoization();
60+
$count = 0;
61+
62+
$func = function () use ($memo, &$count) {
63+
return $memo->memoize('foo', function () use (&$count) {
64+
++$count;
65+
66+
return null;
67+
});
68+
};
69+
70+
$this->assertSame(0, $count);
71+
72+
$this->assertNull($func());
73+
$this->assertSame(1, $count);
74+
$this->assertNull($func());
75+
$this->assertSame(1, $count);
76+
$this->assertNull($func());
77+
$this->assertSame(1, $count);
78+
79+
$memo->clear('foo');
80+
$this->assertNull($func());
81+
$this->assertSame(2, $count);
82+
$this->assertNull($func());
83+
$this->assertSame(2, $count);
84+
}
85+
86+
public function testClearAllWillDeleteAllCaches(): void
87+
{
88+
$memo = new Memoization();
89+
$count1 = 0;
90+
$count2 = 0;
91+
92+
$func1 = function () use ($memo, &$count1) {
93+
return $memo->memoize('foo', function () use (&$count1) {
94+
return ++$count1;
95+
});
96+
};
97+
98+
$func2 = function () use ($memo, &$count2) {
99+
return $memo->memoize('bar', function () use (&$count2) {
100+
return ++$count2;
101+
});
102+
};
103+
104+
$this->assertSame(1, $func1());
105+
$this->assertSame(1, $func1());
106+
$this->assertSame(1, $func2());
107+
$this->assertSame(1, $func2());
108+
109+
$memo->clearAll();
110+
$this->assertSame(2, $func1());
111+
$this->assertSame(2, $func1());
112+
$this->assertSame(2, $func2());
113+
$this->assertSame(2, $func2());
114+
}
115+
}

Diff for: tests/MemoizationTraitTest.php

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?php
2+
3+
namespace Kalibora\Memoization;
4+
5+
use PHPUnit\Framework\TestCase;
6+
7+
class MemoizationTraitTest extends TestCase
8+
{
9+
public function testSameInstance(): void
10+
{
11+
$memoizationAware = new class() {
12+
use MemoizationTrait;
13+
14+
public function getInstance(): Memoization
15+
{
16+
return $this->getMemoization();
17+
}
18+
};
19+
20+
$this->assertSame($memoizationAware->getInstance(), $memoizationAware->getInstance());
21+
}
22+
}

Diff for: tests/bootstrap.php

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<?php
2+
3+
require __DIR__ . '/../vendor/autoload.php';

0 commit comments

Comments
 (0)