-
Notifications
You must be signed in to change notification settings - Fork 17
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #42 from Setono/fbc
FBC/FBP support
- Loading branch information
Showing
13 changed files
with
398 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Setono\SyliusFacebookPlugin\EventListener; | ||
|
||
use Setono\BotDetectionBundle\BotDetector\BotDetectorInterface; | ||
use Setono\SyliusFacebookPlugin\Context\PixelContextInterface; | ||
use Setono\SyliusFacebookPlugin\Generator\PixelEventsGeneratorInterface; | ||
use Setono\SyliusFacebookPlugin\Manager\FbcManagerInterface; | ||
use Setono\SyliusFacebookPlugin\Manager\FbpManagerInterface; | ||
use Symfony\Bundle\SecurityBundle\Security\FirewallMap; | ||
use Symfony\Component\HttpFoundation\RequestStack; | ||
use Symfony\Component\HttpKernel\Event\ResponseEvent; | ||
use Symfony\Component\HttpKernel\KernelEvents; | ||
|
||
final class SetCookiesSubscriber extends AbstractSubscriber | ||
{ | ||
private FbcManagerInterface $fbcManager; | ||
|
||
private FbpManagerInterface $fbpManager; | ||
|
||
public function __construct( | ||
RequestStack $requestStack, | ||
FirewallMap $firewallMap, | ||
PixelContextInterface $pixelContext, | ||
PixelEventsGeneratorInterface $pixelEventsGenerator, | ||
BotDetectorInterface $botDetector, | ||
FbcManagerInterface $fbcManager, | ||
FbpManagerInterface $fbpManager | ||
) { | ||
parent::__construct( | ||
$requestStack, | ||
$firewallMap, | ||
$pixelContext, | ||
$pixelEventsGenerator, | ||
$botDetector | ||
); | ||
|
||
$this->fbcManager = $fbcManager; | ||
$this->fbpManager = $fbpManager; | ||
} | ||
|
||
public static function getSubscribedEvents(): array | ||
{ | ||
return [ | ||
KernelEvents::RESPONSE => 'setCookies', | ||
]; | ||
} | ||
|
||
public function setCookies(ResponseEvent $event): void | ||
{ | ||
if (!$this->isRequestEligible()) { | ||
return; | ||
} | ||
|
||
$request = $this->requestStack->getCurrentRequest(); | ||
if (null === $request || $request->isXmlHttpRequest()) { | ||
return; | ||
} | ||
|
||
$response = $event->getResponse(); | ||
$fbcCookie = $this->fbcManager->getFbcCookie(); | ||
if (null !== $fbcCookie) { | ||
$response->headers->setCookie($fbcCookie); | ||
} | ||
|
||
$fbpCookie = $this->fbpManager->getFbpCookie(); | ||
if (null !== $fbpCookie) { | ||
$response->headers->setCookie($fbpCookie); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,135 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Setono\SyliusFacebookPlugin\Manager; | ||
|
||
use Symfony\Component\HttpFoundation\Cookie; | ||
use Symfony\Component\HttpFoundation\Request; | ||
use Symfony\Component\HttpFoundation\RequestStack; | ||
use Symfony\Component\HttpFoundation\Response; | ||
|
||
/** | ||
* We need this manager to be able to access fbc | ||
* at different stages of Request and generate it only once | ||
*/ | ||
final class FbcManager implements FbcManagerInterface | ||
{ | ||
private RequestStack $requestStack; | ||
|
||
private int $fbcTtl; | ||
|
||
private string $fbcCookieName; | ||
|
||
/** | ||
* Cached value of fbc generated from fbclid at current request | ||
*/ | ||
private ?string $generatedFbc = null; | ||
|
||
public function __construct( | ||
RequestStack $requestStack, | ||
int $fbcTtl, | ||
string $fbcCookieName = 'ssf_fbc' | ||
) { | ||
$this->requestStack = $requestStack; | ||
$this->fbcTtl = $fbcTtl; | ||
$this->fbcCookieName = $fbcCookieName; | ||
} | ||
|
||
public function getFbcCookie(): ?Cookie | ||
{ | ||
$fbc = $this->getFbcValue(); | ||
if (null === $fbc) { | ||
return null; | ||
} | ||
|
||
return Cookie::create( | ||
$this->fbcCookieName, | ||
$fbc, | ||
time() + $this->fbcTtl | ||
); | ||
} | ||
|
||
/** | ||
* We call it twice per request: | ||
* 1. When populate fbc to UserData (on request) | ||
* 2. When setting a fbc cookie (on response) | ||
*/ | ||
public function getFbcValue(): ?string | ||
{ | ||
// We already have fbc generated at previous call | ||
if (null !== $this->generatedFbc) { | ||
return $this->generatedFbc; | ||
} | ||
|
||
$request = $this->requestStack->getCurrentRequest(); | ||
if (null === $request) { | ||
return null; | ||
} | ||
|
||
/** @var string|null $fbc */ | ||
$fbc = $request->cookies->get($this->fbcCookieName); | ||
|
||
/** @var string|null $fbclid */ | ||
$fbclid = $request->query->get('fbclid'); | ||
|
||
// We have both fbc and fbclid | ||
if (is_string($fbclid) && is_string($fbc)) { | ||
// So should decide if we should regenerate it. | ||
// Extracting fbclid from fbc to compare | ||
$existingFbclid = $this->extractFbclid($fbc); | ||
|
||
// If fbclid is the same - we shouldn't regenerate fbc | ||
// and use old one from cookie with old timestamp | ||
if ($existingFbclid !== $fbclid) { | ||
return $this->generateFbc($fbclid); | ||
} | ||
} | ||
|
||
// We have fbc cookie and shouldn't try to | ||
// regenerate it from fbclid (as it is empty) | ||
if (is_string($fbc)) { | ||
return $fbc; | ||
} | ||
|
||
// Have no fbc cookie, but have fbclid | ||
// to generate fbc from it | ||
if (is_string($fbclid)) { | ||
return $this->generateFbc($fbclid); | ||
} | ||
|
||
// We have no fbc cookie and no fbclid, so can't generate | ||
return null; | ||
} | ||
|
||
private function generateFbc(string $fbclid): string | ||
{ | ||
$creationTime = ceil(microtime(true) * 1000); | ||
|
||
$fbc = sprintf( | ||
'fb.1.%s.%s', | ||
$creationTime, | ||
$fbclid | ||
); | ||
|
||
$this->generatedFbc = $fbc; | ||
|
||
return $fbc; | ||
} | ||
|
||
private function extractFbclid(string $fbc): ?string | ||
{ | ||
if (false === preg_match('/fb\.1\.(\d+)\.(.+)/', $fbc, $m)) { | ||
return null; | ||
} | ||
|
||
if (!isset($m[2])) { | ||
return null; | ||
} | ||
|
||
/** @var string $fbclid */ | ||
$fbclid = $m[2]; | ||
|
||
return $fbclid; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Setono\SyliusFacebookPlugin\Manager; | ||
|
||
use Symfony\Component\HttpFoundation\Cookie; | ||
|
||
interface FbcManagerInterface | ||
{ | ||
public function getFbcCookie(): ?Cookie; | ||
|
||
public function getFbcValue(): ?string; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Setono\SyliusFacebookPlugin\Manager; | ||
|
||
use Setono\ClientId\Provider\ClientIdProviderInterface; | ||
use Symfony\Component\HttpFoundation\Cookie; | ||
use Symfony\Component\HttpFoundation\Request; | ||
use Symfony\Component\HttpFoundation\RequestStack; | ||
use Symfony\Component\HttpFoundation\Response; | ||
|
||
/** | ||
* We need this manager to be able to access fbp | ||
* at different stages of Request and generate it only once | ||
*/ | ||
final class FbpManager implements FbpManagerInterface | ||
{ | ||
private RequestStack $requestStack; | ||
|
||
private ClientIdProviderInterface $clientIdProvider; | ||
|
||
private int $fbpTtl; | ||
|
||
private string $fbpCookieName; | ||
|
||
public function __construct( | ||
RequestStack $requestStack, | ||
ClientIdProviderInterface $clientIdProvider, | ||
int $fbpTtl, | ||
string $fbpCookieName = 'ssf_fbp' | ||
) { | ||
$this->requestStack = $requestStack; | ||
$this->clientIdProvider = $clientIdProvider; | ||
$this->fbpTtl = $fbpTtl; | ||
$this->fbpCookieName = $fbpCookieName; | ||
} | ||
|
||
public function getfbpCookie(): ?Cookie | ||
{ | ||
$fbp = $this->getfbpValue(); | ||
if (null === $fbp) { | ||
return null; | ||
} | ||
|
||
return Cookie::create( | ||
$this->fbpCookieName, | ||
$fbp, | ||
time() + $this->fbpTtl | ||
); | ||
} | ||
|
||
/** | ||
* We call it twice per request: | ||
* 1. When populate fbp to UserData (on request) | ||
* 2. When setting a fbp cookie (on response) | ||
*/ | ||
public function getFbpValue(): ?string | ||
{ | ||
$request = $this->requestStack->getCurrentRequest(); | ||
if (null === $request) { | ||
return null; | ||
} | ||
|
||
/** @var string|null $fbp */ | ||
$fbp = $request->cookies->get($this->fbpCookieName); | ||
|
||
// We have fbp cookie and shouldn't try to | ||
// regenerate it from fbplid (as it is empty) | ||
if (is_string($fbp)) { | ||
return $fbp; | ||
} | ||
|
||
$clientId = (string) $this->clientIdProvider->getClientId(); | ||
|
||
return $this->generateFbp($clientId); | ||
} | ||
|
||
private function generateFbp(string $clientId): string | ||
{ | ||
$creationTime = ceil(microtime(true) * 1000); | ||
|
||
return sprintf( | ||
'fb.1.%s.%s', | ||
$creationTime, | ||
$clientId | ||
); | ||
} | ||
} |
Oops, something went wrong.