From c252ee13693c753169125d04feb2f7e57ed483f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joachim=20L=C3=B8vgaard?= Date: Mon, 21 Sep 2026 14:59:18 +0200 Subject: [PATCH] Make Fbp::$randomNumber private and add the missing native types The last two cleanups planned for 2.0: - Fbp::$randomNumber was the only public, mutable property on the otherwise immutable cookie value objects. It is private now; getRandomNumber() and withRandomNumber() already existed. - User::$fbc and User::$fbp were untyped, so anything could be assigned. They are Fbc|string|null and Fbp|string|null natively. - Fb::withCreationTime() takes int|\DateTimeInterface natively instead of checking an untyped parameter at runtime, so a wrong type is a TypeError instead of an InvalidArgumentException. - withSubdomainIndex(), withCreationTime() and withAppendix() declare static as their return type instead of documenting it. The private Parameters::normalize() helper is typed as well. See UPGRADE-2.0.md. --- UPGRADE-2.0.md | 13 +++++++++++++ src/Event/Parameters.php | 6 ++---- src/Event/User.php | 6 ++---- src/ValueObject/Fb.php | 18 +++--------------- src/ValueObject/Fbp.php | 2 +- tests/Event/UserTest.php | 21 +++++++++++++++++++++ tests/ValueObject/FbTest.php | 10 ---------- 7 files changed, 42 insertions(+), 34 deletions(-) diff --git a/UPGRADE-2.0.md b/UPGRADE-2.0.md index c365e36..c0e2d38 100644 --- a/UPGRADE-2.0.md +++ b/UPGRADE-2.0.md @@ -61,6 +61,19 @@ The context is now also passed on to nested objects. In 1.x, `$event->getPayload still serialized the user data in the server context. `FbqGenerator` was not affected, since it asks the user data and the custom data directly. +## Stricter types on `User`, `Fb` and `Fbp` + +- **`Fbp::$randomNumber` is private.** It was the only public, mutable property on the otherwise immutable cookie value + objects. Use `getRandomNumber()` and `withRandomNumber()`. +- **`User::$fbc` and `User::$fbp` are natively typed**, as `Fbc|string|null` and `Fbp|string|null`. They were untyped, so + anything could be assigned. Assigning something else now throws a `\TypeError`. +- **`Fb::withCreationTime()` takes `int|\DateTimeInterface` natively.** Passing anything else throws a `\TypeError`. It + used to throw an `InvalidArgumentException`. +- `withSubdomainIndex()`, `withCreationTime()` and `withAppendix()` declare `static` as their return type. They already + returned the concrete class; this only matters if you extend `Fb` yourself and override them. + +A `\TypeError` is PHP's own error for a programming mistake and does not implement `ExceptionInterface`. + ## `FbqGenerator::generateTrack()` no longer throws a `\JsonException` When the custom data cannot be encoded as JSON, it now logs an error and returns an empty string, which is what diff --git a/src/Event/Parameters.php b/src/Event/Parameters.php index d7796b8..f92f4da 100644 --- a/src/Event/Parameters.php +++ b/src/Event/Parameters.php @@ -56,11 +56,9 @@ protected static function getHashedFields(): array } /** - * @param mixed $data - * * @return array|string|float|int|bool|null */ - private static function normalize($data, PayloadContext $context, ?string $field = null) + private static function normalize(mixed $data, PayloadContext $context, ?string $field = null): array|string|float|int|bool|null { if (null === $data) { return null; @@ -121,7 +119,7 @@ private static function normalize($data, PayloadContext $context, ?string $field */ private static function filterEmptyValues(array $data): array { - return array_filter($data, static function ($value): bool { + return array_filter($data, static function (mixed $value): bool { return !(null === $value || '' === $value || [] === $value); }); } diff --git a/src/Event/User.php b/src/Event/User.php index ad8e87f..0811824 100644 --- a/src/Event/User.php +++ b/src/Event/User.php @@ -46,11 +46,9 @@ final class User extends Parameters public ?string $clientUserAgent = null; - /** @var string|Fbc|null */ - public $fbc; + public Fbc|string|null $fbc = null; - /** @var string|Fbp|null */ - public $fbp; + public Fbp|string|null $fbp = null; public ?string $subscriptionId = null; diff --git a/src/ValueObject/Fb.php b/src/ValueObject/Fb.php index 3554782..99db983 100644 --- a/src/ValueObject/Fb.php +++ b/src/ValueObject/Fb.php @@ -60,10 +60,7 @@ public function getSubdomainIndex(): int return $this->subdomainIndex; } - /** - * @return static - */ - public function withSubdomainIndex(int $subdomainIndex): self + public function withSubdomainIndex(int $subdomainIndex): static { Assert::greaterThanEq($subdomainIndex, 0); @@ -78,18 +75,12 @@ public function getCreationTime(): int return $this->creationTime; } - /** - * @param int|\DateTimeInterface $creationTime - * - * @return static - */ - public function withCreationTime($creationTime): self + public function withCreationTime(int|\DateTimeInterface $creationTime): static { if ($creationTime instanceof \DateTimeInterface) { $creationTime = (int) $creationTime->format('Uv'); } - Assert::integer($creationTime); Assert::greaterThanEq($creationTime, 1_075_590_000_000); // Facebooks founding date xD Assert::lessThanEq($creationTime, (time() + 1) * 1000); @@ -112,10 +103,7 @@ public function getAppendix(): ?string return $this->appendix; } - /** - * @return static - */ - public function withAppendix(?string $appendix): self + public function withAppendix(?string $appendix): static { if (null !== $appendix) { Assert::regex($appendix, self::REGEXP_APPENDIX); diff --git a/src/ValueObject/Fbp.php b/src/ValueObject/Fbp.php index 341d724..49528e9 100644 --- a/src/ValueObject/Fbp.php +++ b/src/ValueObject/Fbp.php @@ -11,7 +11,7 @@ */ final class Fbp extends Fb { - public int $randomNumber; + private int $randomNumber; public function __construct() { diff --git a/tests/Event/UserTest.php b/tests/Event/UserTest.php index cf59e2f..f1cc11f 100644 --- a/tests/Event/UserTest.php +++ b/tests/Event/UserTest.php @@ -5,6 +5,8 @@ namespace Setono\MetaConversionsApi\Event; use PHPUnit\Framework\TestCase; +use Setono\MetaConversionsApi\ValueObject\Fbc; +use Setono\MetaConversionsApi\ValueObject\Fbp; /** * @covers \Setono\MetaConversionsApi\Event\User @@ -23,4 +25,23 @@ public function it_normalizes(): void 'em' => ['55e79200c1635b37ad31a378c39feb12f120f116625093a19bc32fff15041149'], ], $user->getPayload()); } + + /** + * @test + */ + public function it_accepts_the_cookies_as_value_objects_and_as_strings(): void + { + $user = new User(); + self::assertNull($user->fbc); + self::assertNull($user->fbp); + self::assertSame([], $user->getPayload()); + + $user->fbc = Fbc::fromString('fb.1.1657051589577.ClickId'); + $user->fbp = Fbp::fromString('fb.1.1656874832584.1088522659'); + self::assertSame(['fbc' => 'fb.1.1657051589577.ClickId', 'fbp' => 'fb.1.1656874832584.1088522659'], $user->getPayload()); + + $user->fbc = 'fb.1.1657051589577.OtherClickId'; + $user->fbp = 'fb.1.1656874832584.1234567890'; + self::assertSame(['fbc' => 'fb.1.1657051589577.OtherClickId', 'fbp' => 'fb.1.1656874832584.1234567890'], $user->getPayload()); + } } diff --git a/tests/ValueObject/FbTest.php b/tests/ValueObject/FbTest.php index 1a10b80..efa7c7a 100644 --- a/tests/ValueObject/FbTest.php +++ b/tests/ValueObject/FbTest.php @@ -124,16 +124,6 @@ public static function creationTimesOutOfRange(): \Generator yield 'in the future' => [(time() + 60) * 1000]; } - /** - * @test - */ - public function it_rejects_a_creation_time_that_is_neither_an_integer_nor_a_datetime(): void - { - $this->expectException(InvalidArgumentException::class); - - (new Fbp())->withCreationTime('1656874832584'); // @phpstan-ignore argument.type - } - /** * @test */