chore: add php-cs-fixer with PSR-12 config and CI check
- Add friendsofphp/php-cs-fixer to require-dev - Create .php-cs-fixer.dist.php configured for @PSR12 ruleset - Add php-cs-fixer dry-run step to CI pipeline - Auto-fix existing PSR-12 violations - Document code style tooling in readme.md
This commit is contained in:
1 parent
6b5a711fa9
commit
cb378e20bc
58 files changed
+2402
-481
No files matched your search
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Tests\Support;
|
||||
@@ -17,11 +18,13 @@ use Twig\Loader\FilesystemLoader;
|
||||
* Helpers for constructing the collaborators that the kernel listeners
|
||||
* depend on, without booting the full Symfony container.
|
||||
*/
|
||||
trait ListenerTestHelper {
|
||||
trait ListenerTestHelper
|
||||
{
|
||||
use TotpTestHelper;
|
||||
|
||||
/** Build a Twig Environment pointed at the project's real templates. */
|
||||
private function makeTwig(): Environment {
|
||||
private function makeTwig(): Environment
|
||||
{
|
||||
$loader = new FilesystemLoader(dirname(__DIR__, 2) . '/templates');
|
||||
$twig = new Environment($loader, ['strict_variables' => true]);
|
||||
// the templates reference a global `env` object; supply one with the
|
||||
@@ -49,32 +52,43 @@ trait ListenerTestHelper {
|
||||
* A RateLimiterFactoryInterface whose created limiter returns a RateLimit
|
||||
* with the given remaining tokens.
|
||||
*/
|
||||
private function makeRateLimiterFactory(int $remainingTokens): RateLimiterFactoryInterface {
|
||||
private function makeRateLimiterFactory(int $remainingTokens): RateLimiterFactoryInterface
|
||||
{
|
||||
$limiter = $this->makeLimiter($remainingTokens);
|
||||
return new class($limiter) implements RateLimiterFactoryInterface {
|
||||
public function __construct(private LimiterInterface $limiter) {}
|
||||
public function create(?string $key = null): LimiterInterface {
|
||||
return new class ($limiter) implements RateLimiterFactoryInterface {
|
||||
public function __construct(private LimiterInterface $limiter)
|
||||
{
|
||||
}
|
||||
public function create(?string $key = null): LimiterInterface
|
||||
{
|
||||
return $this->limiter;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private function makeLimiter(int $remainingTokens): LimiterInterface {
|
||||
private function makeLimiter(int $remainingTokens): LimiterInterface
|
||||
{
|
||||
$rateLimit = new RateLimit(
|
||||
$remainingTokens,
|
||||
new \DateTimeImmutable('+10 seconds'),
|
||||
$remainingTokens > 0,
|
||||
10,
|
||||
);
|
||||
return new class($rateLimit) implements LimiterInterface {
|
||||
public function __construct(private RateLimit $rateLimit) {}
|
||||
public function reserve(int $tokens = 1, ?float $maxTime = null): \Symfony\Component\RateLimiter\Reservation {
|
||||
return new class ($rateLimit) implements LimiterInterface {
|
||||
public function __construct(private RateLimit $rateLimit)
|
||||
{
|
||||
}
|
||||
public function reserve(int $tokens = 1, ?float $maxTime = null): \Symfony\Component\RateLimiter\Reservation
|
||||
{
|
||||
throw new \Symfony\Component\RateLimiter\Exception\ReserveNotSupportedException();
|
||||
}
|
||||
public function consume(int $tokens = 1): RateLimit {
|
||||
public function consume(int $tokens = 1): RateLimit
|
||||
{
|
||||
return $this->rateLimit;
|
||||
}
|
||||
public function reset(): void {}
|
||||
public function reset(): void
|
||||
{
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -82,14 +96,19 @@ trait ListenerTestHelper {
|
||||
* A factory whose limiter tracks how many consume(1) calls were made and
|
||||
* reports the limit as reached only after $threshold failures.
|
||||
*/
|
||||
private function makeCountingRateLimiterFactory(int $threshold): RateLimiterFactoryInterface {
|
||||
$limiter = new class($threshold) implements LimiterInterface {
|
||||
private function makeCountingRateLimiterFactory(int $threshold): RateLimiterFactoryInterface
|
||||
{
|
||||
$limiter = new class ($threshold) implements LimiterInterface {
|
||||
private int $consumed = 0;
|
||||
public function __construct(private int $threshold) {}
|
||||
public function reserve(int $tokens = 1, ?float $maxTime = null): \Symfony\Component\RateLimiter\Reservation {
|
||||
public function __construct(private int $threshold)
|
||||
{
|
||||
}
|
||||
public function reserve(int $tokens = 1, ?float $maxTime = null): \Symfony\Component\RateLimiter\Reservation
|
||||
{
|
||||
throw new \Symfony\Component\RateLimiter\Exception\ReserveNotSupportedException();
|
||||
}
|
||||
public function consume(int $tokens = 1): RateLimit {
|
||||
public function consume(int $tokens = 1): RateLimit
|
||||
{
|
||||
$this->consumed += $tokens;
|
||||
$remaining = max(0, $this->threshold - $this->consumed);
|
||||
return new RateLimit(
|
||||
@@ -99,11 +118,17 @@ trait ListenerTestHelper {
|
||||
$this->threshold,
|
||||
);
|
||||
}
|
||||
public function reset(): void { $this->consumed = 0; }
|
||||
public function reset(): void
|
||||
{
|
||||
$this->consumed = 0;
|
||||
}
|
||||
};
|
||||
return new class($limiter) implements RateLimiterFactoryInterface {
|
||||
public function __construct(private LimiterInterface $limiter) {}
|
||||
public function create(?string $key = null): LimiterInterface {
|
||||
return new class ($limiter) implements RateLimiterFactoryInterface {
|
||||
public function __construct(private LimiterInterface $limiter)
|
||||
{
|
||||
}
|
||||
public function create(?string $key = null): LimiterInterface
|
||||
{
|
||||
return $this->limiter;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Tests\Support;
|
||||
@@ -17,7 +18,8 @@ use Symfony\Component\Cache\Adapter\ArrayAdapter;
|
||||
* Provides a deterministic TOTP fixture plus a frozen clock and ready-made
|
||||
* ConfigBag / cache-pool helpers for tests that exercise TOTP-dependent code.
|
||||
*/
|
||||
trait TotpTestHelper {
|
||||
trait TotpTestHelper
|
||||
{
|
||||
/** well-known Base32 test secret (JBSWY3DPEHPK3PXP) */
|
||||
private const string TOTP_SECRET = 'JBSWY3DPEHPK3PXP';
|
||||
|
||||
@@ -25,30 +27,37 @@ trait TotpTestHelper {
|
||||
protected const string FROZEN_TIME = '2025-06-15 12:00:00';
|
||||
|
||||
/** Frozen clock that always returns the same instant. */
|
||||
private function frozenClock(): PsrClockInterface {
|
||||
private function frozenClock(): PsrClockInterface
|
||||
{
|
||||
$time = self::FROZEN_TIME;
|
||||
return new class($time) implements PsrClockInterface {
|
||||
public function __construct(private string $time) {}
|
||||
public function now(): DateTimeImmutable {
|
||||
return new class ($time) implements PsrClockInterface {
|
||||
public function __construct(private string $time)
|
||||
{
|
||||
}
|
||||
public function now(): DateTimeImmutable
|
||||
{
|
||||
return new DateTimeImmutable($this->time);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/** Provisioning URI built from the well-known secret + frozen clock. */
|
||||
private function totpUri(): string {
|
||||
private function totpUri(): string
|
||||
{
|
||||
$totp = TOTP::createFromSecret(self::TOTP_SECRET, $this->frozenClock());
|
||||
$totp->setLabel('Test-TOTP');
|
||||
return $totp->getProvisioningUri();
|
||||
}
|
||||
|
||||
/** The TOTP code that is valid at the frozen timestamp. */
|
||||
private function validTotpCode(): string {
|
||||
private function validTotpCode(): string
|
||||
{
|
||||
return TOTP::createFromSecret(self::TOTP_SECRET, $this->frozenClock())->now();
|
||||
}
|
||||
|
||||
/** A fresh in-memory cache pool suitable for wrapping in MonitorCacheKeys. */
|
||||
private function emptyPool(): CacheItemPoolInterface {
|
||||
private function emptyPool(): CacheItemPoolInterface
|
||||
{
|
||||
return new ArrayAdapter();
|
||||
}
|
||||
|
||||
@@ -67,9 +76,15 @@ trait TotpTestHelper {
|
||||
$clock = $this->frozenClock();
|
||||
$utilities = $this->createUtilities($clock);
|
||||
return new ConfigBag(
|
||||
$utilities, $clock,
|
||||
$cookieTtl, $this->totpUri(), $ipTtl, $teapot,
|
||||
$errorMessage, $teapotTitle, $tooManyTitle,
|
||||
$utilities,
|
||||
$clock,
|
||||
$cookieTtl,
|
||||
$this->totpUri(),
|
||||
$ipTtl,
|
||||
$teapot,
|
||||
$errorMessage,
|
||||
$teapotTitle,
|
||||
$tooManyTitle,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -77,7 +92,8 @@ trait TotpTestHelper {
|
||||
* Minimal Utilities stub that never triggers TOTP generation when
|
||||
* a non-empty totpUri is supplied to ConfigBag.
|
||||
*/
|
||||
private function createUtilities(?PsrClockInterface $clock = null): Utilities {
|
||||
private function createUtilities(?PsrClockInterface $clock = null): Utilities
|
||||
{
|
||||
$clock ??= $this->frozenClock();
|
||||
$cache = $this->createStub(CacheItemPoolInterface::class);
|
||||
$cache->method('hasItem')->willReturn(false);
|
||||
|
||||
Reference in new issue
Block a user