A barebones implementation of an atproto PDS in PHP and Slim Framework 4.
pds php atproto
0

Configure Feed

Select the types of activity you want to include in your feed.

refactor: abstract some repeated string functions to a separate file

Andrés Ignacio Torres (May 19, 2026, 7:50 PM -0700) 88a1ac0b 14497e7a

+95 -5
+2 -1
src/Domain/Account/Account.php
··· 4 4 5 5 namespace App\Domain\Account; 6 6 7 + use App\Domain\Common\StringNormalizer; 7 8 use DateTimeImmutable; 8 9 use JsonSerializable; 9 10 ··· 27 28 bool $invitesDisabled = false 28 29 ) { 29 30 $this->did = $did; 30 - $this->email = strtolower(trim($email)); 31 + $this->email = StringNormalizer::normalizeEmail($email); 31 32 $this->passwordScrypt = $passwordScrypt; 32 33 $this->emailConfirmedAt = $emailConfirmedAt; 33 34 $this->invitesDisabled = $invitesDisabled;
+2 -1
src/Domain/Actor/Actor.php
··· 4 4 5 5 namespace App\Domain\Actor; 6 6 7 + use App\Domain\Common\StringNormalizer; 7 8 use DateTimeImmutable; 8 9 use JsonSerializable; 9 10 ··· 30 31 ?DateTimeImmutable $deleteAfter = null 31 32 ) { 32 33 $this->did = $did; 33 - $this->handle = $handle === null ? null : strtolower(trim($handle)); 34 + $this->handle = StringNormalizer::normalizeHandle($handle); 34 35 $this->createdAt = $createdAt; 35 36 $this->takedownRef = $takedownRef; 36 37 $this->deactivatedAt = $deactivatedAt;
+29
src/Domain/Common/StringNormalizer.php
··· 1 + <?php 2 + 3 + declare(strict_types=1); 4 + 5 + namespace App\Domain\Common; 6 + 7 + /** 8 + * Small helpers for canonicalizing user-provided identifiers 9 + * before storage or comparison. 10 + */ 11 + final class StringNormalizer 12 + { 13 + /** 14 + * Normalize a handle by trimming surrounding whitespace and lowercasing. 15 + * Returns null when the input is null. 16 + */ 17 + public static function normalizeHandle(?string $handle): ?string 18 + { 19 + return $handle === null ? null : strtolower(trim($handle)); 20 + } 21 + 22 + /** 23 + * Normalize an email by trimming surrounding whitespace and lowercasing. 24 + */ 25 + public static function normalizeEmail(string $email): string 26 + { 27 + return strtolower(trim($email)); 28 + } 29 + }
+3 -2
src/Infrastructure/Persistence/Account/SqliteAccountRepository.php
··· 7 7 use App\Domain\Account\Account; 8 8 use App\Domain\Account\AccountNotFoundException; 9 9 use App\Domain\Account\AccountRepository; 10 + use App\Domain\Common\StringNormalizer; 10 11 use App\Infrastructure\Database\Database; 11 12 use App\Infrastructure\Database\Row; 12 13 use DateTimeImmutable; ··· 33 34 34 35 public function findAccountByHandle(string $handle): Account 35 36 { 36 - $handle = strtolower(trim($handle)); 37 + $handle = StringNormalizer::normalizeHandle($handle) ?? ''; 37 38 if ($handle === '') { 38 39 throw new AccountNotFoundException(); 39 40 } ··· 63 64 64 65 public function findAccountByEmail(string $email): Account 65 66 { 66 - $email = strtolower(trim($email)); 67 + $email = StringNormalizer::normalizeEmail($email); 67 68 if ($email === '') { 68 69 throw new AccountNotFoundException(); 69 70 }
+2 -1
src/Infrastructure/Persistence/Actor/SqliteActorRepository.php
··· 7 7 use App\Domain\Actor\Actor; 8 8 use App\Domain\Actor\ActorNotFoundException; 9 9 use App\Domain\Actor\ActorRepository; 10 + use App\Domain\Common\StringNormalizer; 10 11 use App\Infrastructure\Database\Database; 11 12 use App\Infrastructure\Database\Row; 12 13 use DateTimeImmutable; ··· 72 73 73 74 public function findActorByHandle(string $handle): Actor 74 75 { 75 - $handle = strtolower(trim($handle)); 76 + $handle = StringNormalizer::normalizeHandle($handle) ?? ''; 76 77 if ($handle === '') { 77 78 throw new ActorNotFoundException(); 78 79 }
+57
tests/Domain/Common/StringNormalizerTest.php
··· 1 + <?php 2 + 3 + declare(strict_types=1); 4 + 5 + namespace Tests\Domain\Common; 6 + 7 + use App\Domain\Common\StringNormalizer; 8 + use PHPUnit\Framework\Attributes\DataProvider; 9 + use Tests\TestCase; 10 + 11 + class StringNormalizerTest extends TestCase 12 + { 13 + /** 14 + * @return array<string, array{0: ?string, 1: ?string}> 15 + */ 16 + public static function normalizeHandleProvider(): array 17 + { 18 + return [ 19 + 'null stays null' => [null, null], 20 + 'already normalized' => ['alice.pds.test', 'alice.pds.test'], 21 + 'trim and lowercase' => [' Alice.PDS.Test ', 'alice.pds.test'], 22 + 'tabs and newlines are trimmed' => ["\tBob.PDS.Test\n", 'bob.pds.test'], 23 + 'whitespace only becomes empty' => [' ', ''], 24 + ]; 25 + } 26 + 27 + /** 28 + * @dataProvider normalizeHandleProvider 29 + */ 30 + #[DataProvider('normalizeHandleProvider')] 31 + public function testNormalizeHandle(?string $input, ?string $expected): void 32 + { 33 + $this->assertSame($expected, StringNormalizer::normalizeHandle($input)); 34 + } 35 + 36 + /** 37 + * @return array<string, array{0: string, 1: string}> 38 + */ 39 + public static function normalizeEmailProvider(): array 40 + { 41 + return [ 42 + 'already normalized' => ['alice@example.com', 'alice@example.com'], 43 + 'trim and lowercase' => [' Alice+Tag@Example.COM ', 'alice+tag@example.com'], 44 + 'tabs and newlines are trimmed' => ["\tbob@Example.COM\n", 'bob@example.com'], 45 + 'whitespace only becomes empty' => [' ', ''], 46 + ]; 47 + } 48 + 49 + /** 50 + * @dataProvider normalizeEmailProvider 51 + */ 52 + #[DataProvider('normalizeEmailProvider')] 53 + public function testNormalizeEmail(string $input, string $expected): void 54 + { 55 + $this->assertSame($expected, StringNormalizer::normalizeEmail($input)); 56 + } 57 + }