diff --git a/.gitattributes b/.gitattributes
index 8bdd87d..bb0a5ff 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -1,5 +1 @@
-/tests export-ignore
-/.gitattributes export-ignore
-/.gitignore export-ignore
-/phpunit.xml export-ignore
-/example.php export-ignore
+/.github export-ignore
diff --git a/.github/workflows/autotest.yml b/.github/workflows/autotest.yml
new file mode 100644
index 0000000..c74c21d
--- /dev/null
+++ b/.github/workflows/autotest.yml
@@ -0,0 +1,26 @@
+name: Autotest
+on: [push, pull_request]
+
+permissions:
+ contents: read
+
+jobs:
+ test:
+ runs-on: ubuntu-latest
+ container:
+ image: php:8.2-cli-alpine
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+
+ - name: Vendor
+ run: wget https://getcomposer.org/download/latest-stable/composer.phar && php composer.phar i
+
+ - name: Run PHPSTAN
+ run: php composer.phar phpstan
+
+ - name: Run CodeStyle check
+ run: php composer.phar cs-check
+
+ - name: PhpUnit
+ run: php composer.phar phpunit
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index 691d525..f4bf1fc 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,7 @@
/vendor/
/.idea/
/composer.lock
-/example.html
\ No newline at end of file
+/example.html
+/composer.phar
+/tests/file.cache
+/tests/.phpcs-cache
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c1430f8..40e3e44 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,16 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).
+## [3.0.0] - 2025-03-26
+### Added
+- Support PHP 8.2
+- Update all libs (endroid/qr-code, psr/cache, christian-riesen/base32, paragonie/random_compat)
+- Add psr/simple-cache:^3.0
+- Add CodeStyle fixer
+- PhpStan level up to 8
+- Add fast command with `Makeile`
+- Use Docker
+
## [2.2.0] - 2021-05-24
### Added
- Support for different types of writer for Endroid
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..bffb0a5
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,35 @@
+SHELL = /bin/bash
+### https://makefiletutorial.com/
+
+docker := docker run -it --rm -v $(PWD):/app -w /app php:8.2-cli-alpine
+composer := $(docker) php composer.phar
+
+bash:
+ $(docker) bash
+
+composer-download:
+ test -f composer.phar || wget https://getcomposer.org/download/latest-stable/composer.phar
+
+composer-i:
+ @make composer-download
+ $(composer) install --prefer-dist --no-scripts
+
+composer-u:
+ $(composer) update --prefer-dist --no-scripts $(name)
+
+cs-fix:
+ $(composer) cs-fix
+
+cs-check:
+ $(composer) cs-check
+
+phpstan:
+ $(composer) phpstan
+
+phpunit:
+ $(composer) phpunit
+
+test:
+ $(composer) cs-check
+ $(composer) phpstan
+ $(composer) phpunit
diff --git a/README.md b/README.md
index 0347d79..67b168c 100644
--- a/README.md
+++ b/README.md
@@ -30,7 +30,7 @@ This gives you a secret. You should:
2. attach the secret to their user account so you can query it
There are 2 ImageGenerator implementations included with this library:
-1. EndroidQrImageGenerator which requires you composer require `endroid/qr-code:~2.2|~3` which generates it without any external service dependencies.
+1. EndroidQrImageGenerator which requires you composer require `endroid/qr-code:^6.0` which generates it without any external service dependencies.
2. GoogleImageGenerator which uses the Google QR code API to generate the image.
I'd recommend using Endroid as it seems that Google has now [deprecated their QR code API](https://developers.google.com/chart/infographics/docs/qr_codes)
diff --git a/composer.json b/composer.json
index 8e3ed74..e18bca8 100644
--- a/composer.json
+++ b/composer.json
@@ -1,25 +1,32 @@
{
- "name": "dolondro/google-authenticator",
+ "name": "dochne/google-authenticator",
"description": "Code to authenticate against the Google Authenticator app",
"license": "MIT",
+ "version": "3.0.0",
"authors": [
{
"name": "Doug Nelson",
"email": "dougnelson@silktide.com"
+ },
+ {
+ "name": "Xakki",
+ "email": "git@xakki.ru"
}
],
"require": {
- "php": ">=5.4",
- "christian-riesen/base32": "^1.2",
- "psr/cache": "^1.0",
- "paragonie/random_compat": "^2.0|~9.99"
+ "php": ">=8.2",
+ "christian-riesen/base32": "^1.6",
+ "psr/cache": "^3.0",
+ "psr/simple-cache": "^3.0",
+ "paragonie/random_compat": "^9.99"
},
"require-dev": {
- "phpunit/phpunit": "~6",
- "endroid/qr-code": "~2.2|~3",
- "cache/filesystem-adapter": "^1.0",
- "phpstan/phpstan": "^0.11.3",
- "cache/array-adapter": "^1.0"
+ "phpunit/phpunit": "^11",
+ "endroid/qr-code": "^6.0",
+ "phpstan/phpstan": "^2.1",
+ "symfony/cache": "^7.2",
+ "squizlabs/php_codesniffer": "*",
+ "opsway/psr12-strict-coding-standard": "*"
},
"suggests": {
"endroid/qr-code": "Allows use of EndroidQrImageGenerator to generate the QR code images"
@@ -33,5 +40,16 @@
"psr-4": {
"Dolondro\\GoogleAuthenticator\\Tests\\": "tests"
}
+ },
+ "config": {
+ "allow-plugins": {
+ "dealerdirect/phpcodesniffer-composer-installer": true
+ }
+ },
+ "scripts": {
+ "phpstan": "XDEBUG_MODE=off vendor/bin/phpstan analyse",
+ "cs-check": "XDEBUG_MODE=off vendor/bin/phpcs",
+ "cs-fix": "XDEBUG_MODE=off vendor/bin/phpcbf",
+ "phpunit": "XDEBUG_MODE=off vendor/bin/phpunit -c phpunit.xml --no-coverage"
}
}
diff --git a/phpcs.xml b/phpcs.xml
new file mode 100644
index 0000000..eb305df
--- /dev/null
+++ b/phpcs.xml
@@ -0,0 +1,302 @@
+
+
+ Strict PSR12 code style standard
+
+
+
+
+
+
+ src/
+ tests/
+ vendor/*
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ tests/*
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ src/Db.php
+
+
+
+
+
+
+
+ src/Db.php
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ *.js
+ *.css
+ *.json
+
+
diff --git a/phpstan.neon b/phpstan.neon
new file mode 100644
index 0000000..f4099b4
--- /dev/null
+++ b/phpstan.neon
@@ -0,0 +1,5 @@
+parameters:
+ level: 8
+ paths:
+ - src
+ - tests
\ No newline at end of file
diff --git a/phpunit.xml b/phpunit.xml
index 4a51a80..473c2e7 100644
--- a/phpunit.xml
+++ b/phpunit.xml
@@ -1,17 +1,36 @@
-
-
-
+
+
-
- tests
+
+ ./tests
-
-
- ./src
-
-
- ./vendor
-
-
-
+
+
+
+
+
+
+ ./src
+
+
+
\ No newline at end of file
diff --git a/src/GoogleAuthenticator.php b/src/GoogleAuthenticator.php
index cce6a7a..df2115b 100644
--- a/src/GoogleAuthenticator.php
+++ b/src/GoogleAuthenticator.php
@@ -5,59 +5,47 @@
use Base32\Base32;
use Psr\Cache\CacheItemPoolInterface;
use Psr\SimpleCache\CacheInterface;
+use Psr\SimpleCache\InvalidArgumentException;
class GoogleAuthenticator
{
// According to the spec, this could be something other than 6. But again, apparently Google Authenticator ignores
// that part of the spec...
- protected $codeLength = 6;
+ protected int $codeLength = 6;
+
+ protected CacheItemPoolInterface|CacheInterface|null $cache = null;
/**
- * @var CacheItemPoolInterface|null
+ * @var array
*/
- protected $cache = null;
-
- protected $options = [
+ protected array $options = [
"window" => 1,
"time" => null,
];
- public function __construct($options = [])
+ /**
+ * @param array $options
+ */
+ public function __construct(array $options = [])
{
$this->options = array_merge($this->options, $options);
}
- /**
- * @param CacheItemPoolInterface|CacheInterface $cache
- *
- * @throws \Exception
- */
- public function setCache($cache)
+ public function setCache(CacheItemPoolInterface|CacheInterface|null $cache): void
{
- if ($cache instanceof CacheItemPoolInterface || $cache instanceof CacheInterface) {
- $this->cache = $cache;
-
- return;
- }
-
- throw new \Exception("Cache is not PSR-16 or PSR-6 compliant");
+ $this->cache = $cache;
}
/**
- * @param string $secret
- * @param string $code
- *
- * @return bool
- *
- * @throws \Exception
+ * @throws InvalidArgumentException
* @throws \Psr\Cache\InvalidArgumentException
*/
- public function authenticate($secret, $code)
+ public function authenticate(string $secret, string $code): bool
{
$correct = false;
- $time = isset($this->options["time"]) ? $this->options["time"] : time();
+ $time = $this->options["time"] ?? time();
- $window = $this->options["window"];
+ $window = (int) $this->options["window"];
for ($i = -$window; $i <= $window; $i++) {
$timeSlice = $this->getTimeSlice($time, $i);
@@ -91,7 +79,7 @@ public function authenticate($secret, $code)
// There definitely will be a better way of doing this, but this is a quick bugfix
//
// If someone has any better suggestions on how to achieve this, please send in a PR! :P
- $key = md5(crypt($secret."|".$code, md5($code)));
+ $key = md5(crypt($secret . "|" . $code, md5($code)));
// People mostly use PSR-16 these days as PSR-6 was a PITA
if ($this->cache instanceof CacheInterface) {
@@ -100,8 +88,6 @@ public function authenticate($secret, $code)
}
$this->cache->set($key, true, 30);
-
- return true;
}
if ($this->cache instanceof CacheItemPoolInterface) {
@@ -118,48 +104,26 @@ public function authenticate($secret, $code)
// We don't care about the value at all, it's just something that's needed to use the caching interface
$item->set(true);
$this->cache->save($item);
-
- return true;
}
-
- // I'd be pretty impressed if someone got here
return true;
}
- /**
- * @param int $time
- * @param int $offset
- *
- * @return float|int
- */
- protected function getTimeSlice($time, $offset = 0)
+ protected function getTimeSlice(int $time, int $offset = 0): float
{
return floor($time / 30) + $offset;
}
- /**
- * @param $string1
- * @param $string2
- *
- * @return bool
- */
- protected function isEqual($string1, $string2)
+ protected function isEqual(string $string1, string $string2): bool
{
- return substr_count($string1 ^ $string2, "\0") * 2 === strlen($string1.$string2);
+ return substr_count($string1 ^ $string2, "\0") * 2 === strlen($string1 . $string2);
}
- /**
- * @param string $secret
- * @param int|null $timeSlice
- *
- * @return string
- */
- public function calculateCode($secret, $timeSlice = null)
+ public function calculateCode(string $secret, float|int|null $timeSlice = null): string
{
// If we haven't been fed a timeSlice, then get one.
// It looks a bit unclean doing it like this, but it allows us to write testable code
- $time = isset($this->options["time"]) ? $this->options["time"] : time();
- $timeSlice = $timeSlice ? $timeSlice : $this->getTimeSlice($time);
+ $time = $this->options["time"] ?? time();
+ $timeSlice = $timeSlice ?: $this->getTimeSlice($time);
// Packs the timeslice as a "unsigned long" (always 32 bit, big endian byte order)
$timeSlice = pack("N", $timeSlice);
@@ -178,7 +142,8 @@ public function calculateCode($secret, $timeSlice = null)
$result = substr($hash, $offset, 4);
// Unpack it again
- $value = unpack('N', $result)[1];
+ $value = unpack('N', $result);
+ $value = $value ? $value[1] : false;
// Only 32 bits
$value = $value & 0x7FFFFFFF;
@@ -187,6 +152,6 @@ public function calculateCode($secret, $timeSlice = null)
$modulo = pow(10, $this->codeLength);
// Finally, pad out the string with 0s
- return str_pad($value % $modulo, $this->codeLength, '0', STR_PAD_LEFT);
+ return str_pad((string) ($value % $modulo), $this->codeLength, '0', STR_PAD_LEFT);
}
}
diff --git a/src/QrImageGenerator/EndroidQrImageGenerator.php b/src/QrImageGenerator/EndroidQrImageGenerator.php
index 5d0044c..b71e373 100644
--- a/src/QrImageGenerator/EndroidQrImageGenerator.php
+++ b/src/QrImageGenerator/EndroidQrImageGenerator.php
@@ -3,30 +3,46 @@
namespace Dolondro\GoogleAuthenticator\QrImageGenerator;
use Dolondro\GoogleAuthenticator\Secret;
-use Endroid\QrCode\QrCode;
+use Endroid\QrCode\Builder\Builder;
+use Endroid\QrCode\Encoding\Encoding;
+use Endroid\QrCode\ErrorCorrectionLevel;
+use Endroid\QrCode\Exception\ValidationException;
+use Endroid\QrCode\Label\Font\OpenSans;
+use Endroid\QrCode\Label\LabelAlignment;
+use Endroid\QrCode\RoundBlockSizeMode;
+use Endroid\QrCode\Writer\PngWriter;
+use Endroid\QrCode\Writer\WriterInterface;
class EndroidQrImageGenerator implements QrImageGeneratorInterface
{
- protected $size;
- protected $writer;
-
- public function __construct($size = 200, $writer = 'png')
- {
- if (!is_numeric($size)) {
- throw new \InvalidArgumentException("Size is required to be numeric");
- }
-
- $this->size = $size;
- $this->writer = $writer;
+ public function __construct(
+ protected int $size = 200,
+ protected WriterInterface $writer = new PngWriter(),
+ protected string $labelText = '',
+ ) {
}
- public function generateUri(Secret $secret)
+ /**
+ * @throws ValidationException
+ */
+ public function generateUri(Secret $secret): string
{
- $qrCode = new QrCode($secret->getUri());
- $qrCode->setSize($this->size);
-
- $qrCode->setWriterByName($this->writer);
-
- return $qrCode->writeDataUri();
+ $builder = new Builder(
+ writer: $this->writer,
+ writerOptions: [],
+ validateResult: false,
+ data: $secret->getUri(),
+ encoding: new Encoding('UTF-8'),
+ errorCorrectionLevel: ErrorCorrectionLevel::High,
+ size: $this->size,
+ margin: 10,
+ roundBlockSizeMode: RoundBlockSizeMode::Margin,
+ labelText: $this->labelText,
+ labelFont: new OpenSans(20),
+ labelAlignment: LabelAlignment::Center
+ );
+
+ $result = $builder->build();
+ return $result->getDataUri();
}
}
diff --git a/src/QrImageGenerator/GoogleQrImageGenerator.php b/src/QrImageGenerator/GoogleQrImageGenerator.php
index 81165f5..edf1d01 100644
--- a/src/QrImageGenerator/GoogleQrImageGenerator.php
+++ b/src/QrImageGenerator/GoogleQrImageGenerator.php
@@ -6,21 +6,12 @@
class GoogleQrImageGenerator implements QrImageGeneratorInterface
{
- protected $width;
- protected $height;
-
- public function __construct($width = 200, $height = 200)
+ public function __construct(protected int $width = 200, protected int $height = 200)
{
- if (!is_numeric($width) || !is_numeric($height)) {
- throw new \InvalidArgumentException("Both width and height are required to be numeric");
- }
-
- $this->width = $width;
- $this->height = $height;
}
- public function generateUri(Secret $secret)
+ public function generateUri(Secret $secret): string
{
- return "https://chart.googleapis.com/chart?chs={$this->width}x{$this->height}&chld=M|0&cht=qr&chl=".$secret->getUri();
+ return "https://chart.googleapis.com/chart?chs={$this->width}x{$this->height}&chld=M|0&cht=qr&chl=" . $secret->getUri();
}
}
diff --git a/src/QrImageGenerator/QrImageGeneratorInterface.php b/src/QrImageGenerator/QrImageGeneratorInterface.php
index b9db1f8..6ec9a80 100644
--- a/src/QrImageGenerator/QrImageGeneratorInterface.php
+++ b/src/QrImageGenerator/QrImageGeneratorInterface.php
@@ -6,5 +6,5 @@
interface QrImageGeneratorInterface
{
- public function generateUri(Secret $secret);
+ public function generateUri(Secret $secret): string;
}
diff --git a/src/Secret.php b/src/Secret.php
index dc8e733..3fdb183 100644
--- a/src/Secret.php
+++ b/src/Secret.php
@@ -4,65 +4,35 @@
class Secret
{
- protected $issuer;
- protected $accountName;
- protected $secretKey;
-
- /**
- * Secret constructor.
- *
- * @param string $issuer
- * @param string $accountName
- * @param string $secretKey
- */
- public function __construct($issuer, $accountName, $secretKey)
+ public function __construct(protected string $issuer, protected string $accountName, protected string $secretKey)
{
// As per spec sheet
- if (strpos($issuer.$accountName, ":") !== false) {
+ if (str_contains($issuer . $accountName, ":")) {
throw new \InvalidArgumentException("Neither the 'Issuer' parameter nor the 'AccountName' parameter may contain a colon");
}
-
- $this->issuer = $issuer;
- $this->accountName = $accountName;
- $this->secretKey = $secretKey;
}
- /**
- * @return string
- */
- public function getUri()
+ public function getUri(): string
{
- return "otpauth://totp/".rawurlencode($this->getLabel())."?secret=".$this->getSecretKey()."&issuer=".rawurlencode($this->getIssuer());
+ return "otpauth://totp/" . rawurlencode($this->getLabel()) . "?secret=" . $this->getSecretKey() . "&issuer=" . rawurlencode($this->getIssuer());
}
- /**
- * @return string
- */
- public function getLabel()
+ public function getLabel(): string
{
- return $this->issuer.":".$this->accountName;
+ return $this->issuer . ":" . $this->accountName;
}
- /**
- * @return mixed
- */
- public function getIssuer()
+ public function getIssuer(): string
{
return $this->issuer;
}
- /**
- * @return mixed
- */
- public function getAccountName()
+ public function getAccountName(): string
{
return $this->accountName;
}
- /**
- * @return mixed
- */
- public function getSecretKey()
+ public function getSecretKey(): string
{
return $this->secretKey;
}
diff --git a/src/SecretFactory.php b/src/SecretFactory.php
index 134e1eb..572f2e6 100644
--- a/src/SecretFactory.php
+++ b/src/SecretFactory.php
@@ -4,15 +4,15 @@
class SecretFactory
{
- protected $secretLength;
+ protected int $secretLength;
// For some reason the maniac who came up with base32 encoding decided they hated 0 and 1...
- protected $base32Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
+ protected string $base32Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
/**
* @param int $secretLength - this is the length of the encoded string, as such, it must be divisible by 8
*/
- public function __construct($secretLength = 16)
+ public function __construct(int $secretLength = 16)
{
if ($secretLength == 0 || $secretLength % 8 > 0) {
throw new \InvalidArgumentException("Secret length must be longer than 0 and divisible by 8");
@@ -23,13 +23,8 @@ public function __construct($secretLength = 16)
/**
* The spec technically allows you to only have an accountName not an issuer, but as it's strongly recommended,
* I don't feel particularly guilty about forcing it in the create.
- *
- * @param string $issuer
- * @param string $accountName
- *
- * @return Secret
*/
- public function create($issuer, $accountName)
+ public function create(string $issuer, string $accountName): Secret
{
return new Secret($issuer, $accountName, $this->generateSecretKey());
}
@@ -40,7 +35,7 @@ public function create($issuer, $accountName)
* Interestingly, the easiest way to get truly random key is just to iterate through the base 32 chars picking random
* characters
*/
- public function generateSecretKey()
+ public function generateSecretKey(): string
{
$key = "";
while (strlen($key) < $this->secretLength) {
diff --git a/tests/Authenticator/AuthenticatorTest.php b/tests/Authenticator/AuthenticatorTest.php
index 016bd97..553e2c0 100644
--- a/tests/Authenticator/AuthenticatorTest.php
+++ b/tests/Authenticator/AuthenticatorTest.php
@@ -2,14 +2,15 @@
namespace Dolondro\GoogleAuthenticator\Tests\Authenticator;
-use Cache\Adapter\PHPArray\ArrayCachePool;
use Dolondro\GoogleAuthenticator\GoogleAuthenticator;
use Dolondro\GoogleAuthenticator\Tests\Helper\ArrayPsr16Cache;
use PHPUnit\Framework\TestCase;
+use Symfony\Component\Cache\Adapter\FilesystemAdapter;
+use Symfony\Component\Cache\Adapter\PhpArrayAdapter;
class AuthenticatorTest extends TestCase
{
- public function testCalculateCode()
+ public function testCalculateCode(): void
{
$authenticator = new GoogleAuthenticator();
$this->assertEquals("818888", $authenticator->calculateCode("OX35UDZUWP23WBUA", 48535782));
@@ -27,10 +28,9 @@ public function testCalculateCode()
* [60, 2, true]
* [-31, 1, false]
* [-59, 2, true]
- *
* @throws \Psr\Cache\InvalidArgumentException
*/
- public function testAuthenticate($timeOffset, $window, $success)
+ public function testAuthenticate(int $timeOffset, int $window, bool $success): void
{
$secret = "G2XLNTQRVES7JF3V";
$code = "081446";
@@ -45,19 +45,22 @@ public function testAuthenticate($timeOffset, $window, $success)
self::assertSame($success, $authenticator->authenticate($secret, $code));
}
- public function testReplayAttackPsr6()
+ public function testReplayAttackPsr6(): void
{
$secret = "G2XLNTQRVES7JF3V";
$code = "081446";
$time = 1456073370;
$options = ["time" => $time];
$authenticator = new GoogleAuthenticator($options);
- $authenticator->setCache(new ArrayCachePool());
+ $authenticator->setCache(new PhpArrayAdapter(
+ __DIR__ . '/../file.cache',
+ new FilesystemAdapter(),
+ ));
$this->assertTrue($authenticator->authenticate($secret, $code));
$this->assertFalse($authenticator->authenticate($secret, $code));
}
- public function testReplayAttackPsr16()
+ public function testReplayAttackPsr16(): void
{
$secret = "G2XLNTQRVES7JF3V";
$code = "081446";
@@ -68,13 +71,4 @@ public function testReplayAttackPsr16()
$this->assertTrue($authenticator->authenticate($secret, $code));
$this->assertFalse($authenticator->authenticate($secret, $code));
}
-
- /**
- * @expectedException \Exception
- */
- public function testIncorrectCacheSupplied()
- {
- $authenticator = new GoogleAuthenticator();
- $authenticator->setCache(new \DateTime());
- }
}
diff --git a/tests/Helper/ArrayPsr16Cache.php b/tests/Helper/ArrayPsr16Cache.php
index 7e84989..aa8ffcd 100644
--- a/tests/Helper/ArrayPsr16Cache.php
+++ b/tests/Helper/ArrayPsr16Cache.php
@@ -5,50 +5,68 @@
use Psr\SimpleCache\CacheInterface;
/**
- * Class ArrayPsr16
* A very half arsed incorrect implementation as it's really just to prove a point.
*/
class ArrayPsr16Cache implements CacheInterface
{
- protected $data = [];
+ /** @var mixed[] */
+ protected array $data = [];
- public function get($key, $default = null)
+ public function get(string $key, mixed $default = null): mixed
{
- return isset($this->data[$key]) ? $this->data[$key] : $default;
+ return $this->data[$key] ?? $default;
}
- public function set($key, $value, $ttl = null)
+ public function set(string $key, mixed $value, null|int|\DateInterval $ttl = null): bool
{
$this->data[$key] = $value;
+ return true;
}
- public function has($key)
+ public function has(string $key): bool
{
return isset($this->data[$key]);
}
- public function delete($key)
+ public function delete(string $key): bool
{
- // TODO: Implement delete() method.
+ unset($this->data[$key]);
+ return true;
}
- public function clear()
+ public function clear(): bool
{
- // TODO: Implement clear() method.
+ $this->data = [];
+ return true;
}
- public function getMultiple($keys, $default = null)
+ public function getMultiple(iterable $keys, mixed $default = null): iterable
{
- // TODO: Implement getMultiple() method.
+ $result = [];
+ foreach ($keys as $key) {
+ if (isset($this->data[$key])) {
+ $result[$key] = $this->data[$key];
+ }
+ }
+ return $result;
}
- public function setMultiple($values, $ttl = null)
+ /**
+ * @param iterable $values
+ */
+ public function setMultiple(iterable $values, null|int|\DateInterval $ttl = null): bool
{
- // TODO: Implement setMultiple() method.
+ foreach ($values as $key => $value) {
+ $this->data[$key] = $value;
+ }
+ return true;
}
- public function deleteMultiple($keys)
+ public function deleteMultiple(iterable $keys): bool
{
- // TODO: Implement deleteMultiple() method.
+ foreach ($keys as $key) {
+ unset($this->data[$key]);
+ }
+ return true;
}
}
diff --git a/tests/Secret/SecretTest.php b/tests/Secret/SecretTest.php
index 50ad538..bbdd991 100644
--- a/tests/Secret/SecretTest.php
+++ b/tests/Secret/SecretTest.php
@@ -10,7 +10,7 @@ class SecretTest extends TestCase
/**
* Tests that realistically, aren't going to fail.
*/
- public function testBasicFunctionality()
+ public function testBasicFunctionality(): void
{
$secret = new Secret("ReynolmIndustries", "MrSmith", "SecretKey");
$this->assertEquals("ReynolmIndustries", $secret->getIssuer());
@@ -18,7 +18,7 @@ public function testBasicFunctionality()
$this->assertEquals("SecretKey", $secret->getSecretKey());
}
- public function testGetUri()
+ public function testGetUri(): void
{
$secret = new Secret("Example", "alice@google.com", "JBSWY3DPEHPK3PXP");
$this->assertEquals("otpauth://totp/Example%3Aalice%40google.com?secret=JBSWY3DPEHPK3PXP&issuer=Example", $secret->getUri());