Skip to content

Commit

Permalink
Merge pull request #24 from guillaume-ro-fr/master
Browse files Browse the repository at this point in the history
Add PHP Attributes compatibility
  • Loading branch information
farmatholin authored Jan 30, 2024
2 parents 5ad2b42 + 15c3134 commit 72ece7a
Show file tree
Hide file tree
Showing 10 changed files with 237 additions and 15 deletions.
27 changes: 23 additions & 4 deletions Configuration/Page.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@

namespace Farmatholin\SegmentIoBundle\Configuration;

use Doctrine\Common\Annotations\Annotation;
use Doctrine\Common\Annotations\Annotation\Required;

/**
Expand All @@ -22,9 +21,9 @@
* @author Vladislav Marin <[email protected]>
*
* @Annotation
*
* @Target("METHOD")
*/
#[\Attribute(\Attribute::TARGET_METHOD)]
class Page implements AnalyticsInterface
{

Expand All @@ -33,20 +32,40 @@ class Page implements AnalyticsInterface
*
* @var string
*/
public $category;
public string $category;

/**
* @Required
*
* @var string
*/
public $name;
public string $name;

/**
* @var array
*/
public array $properties = [];

/**
* @param string $category
*/
public function __construct(
$category = null,
string $name = null,
array $properties = []
) {
if (is_array($category)) {
// Doctrine annotation
$this->category = $category['category'];
$this->name = $category['name'];
$this->properties = $category['properties'] ?? $this->properties;
} else {
// PHP Attributes
$this->category = $category;
$this->name = $name;
$this->properties = $properties;
}
}

/**
* @return string
Expand Down
27 changes: 26 additions & 1 deletion Configuration/Track.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,15 @@
* @Annotation
* @Target("METHOD")
*/
#[\Attribute(\Attribute::TARGET_METHOD)]
class Track implements AnalyticsInterface
{
/**
* @Required
*
* @var string
*/
public $event;
public string $event;

/**
* @var array
Expand All @@ -48,6 +49,30 @@ class Track implements AnalyticsInterface
*/
public bool $useTimestamp = false;

/**
* @param string $event
*/
public function __construct(
$event = null,
array $properties = [],
array $context = [],
bool $useTimestamp = false
) {
if (is_array($event)) {
// Doctrine annotations
$this->event = $event['event'];
$this->properties = $event['properties'] ?? $this->properties;
$this->context = $event['context'] ?? $this->context;
$this->useTimestamp = $event['useTimestamp'] ?? $this->useTimestamp;
} else {
// PHP Attributes
$this->event = $event;
$this->properties = $properties;
$this->context = $context;
$this->useTimestamp = $useTimestamp;
}
}

/**
* @return string
*/
Expand Down
23 changes: 22 additions & 1 deletion DependencyInjection/SegmentIoExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,11 @@

namespace Farmatholin\SegmentIoBundle\DependencyInjection;

use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
use Symfony\Component\DependencyInjection\Loader;

Expand All @@ -23,7 +26,7 @@
*
* @author Vladislav Marin <[email protected]>
*/
class SegmentIoExtension extends Extension
class SegmentIoExtension extends Extension implements CompilerPassInterface
{
/**
* @see https://segment.com/docs/connections/data-residency/
Expand All @@ -48,6 +51,12 @@ public function load(array $configs, ContainerBuilder $container)
$loader = new Loader\XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
$loader->load('services.xml');

$loader->load('annotations.xml');
if (PHP_VERSION_ID >= 80000) {
// PHP Attributes
$loader->load('attributes.xml');
}

if (isset(static::DATA_RESIDENCIES[$config['data_residency']]) && !isset($config['options']['host'])) {
$config['options']['host'] = static::DATA_RESIDENCIES[$config['data_residency']];
}
Expand All @@ -58,4 +67,16 @@ public function load(array $configs, ContainerBuilder $container)
$container->setParameter('farma.segment_io_env', $config['env']);
$container->setParameter('farma.segment_io_options', $config['options']);
}

public function process(ContainerBuilder $container)
{
// Doctrine annotations can be disabled by Symfony Framework or if doctrine/annotations is missing
try {
$container->findDefinition('annotation_reader');
} catch (ServiceNotFoundException $ignored) {
// Remove Doctrine annotation listener on Kernel request
$container->getDefinition('farma.segment_io.annotation_listener')
->clearTag('kernel.event_listener');
}
}
}
96 changes: 96 additions & 0 deletions EventListener/AttributeListener.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
<?php

/** @noinspection PhpElementIsNotAvailableInCurrentPhpVersionInspection */

namespace Farmatholin\SegmentIoBundle\EventListener;

use Farmatholin\SegmentIoBundle\Configuration\Page;
use Farmatholin\SegmentIoBundle\Configuration\Track;
use Farmatholin\SegmentIoBundle\Util\SegmentIoProvider;
use Symfony\Component\HttpKernel\Event\ControllerEvent;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;

class AttributeListener
{
protected SegmentIoProvider $segmentIoProvider;
protected TokenStorageInterface $tokenStorage;
protected string $guestId;

/**
* AttributeListener constructor.
*
* @param TokenStorageInterface $tokenStorage
* @param SegmentIoProvider $segmentIoProvider
* @param string $guestId
*/
public function __construct(TokenStorageInterface $tokenStorage, SegmentIoProvider $segmentIoProvider, string $guestId)
{
$this->segmentIoProvider = $segmentIoProvider;
$this->tokenStorage = $tokenStorage;
$this->guestId = $guestId;
}

public function onKernelController(ControllerEvent $event): void
{
$controller = $event->getController();

if (!is_array($controller)) {
return;
}

[$controllerObject, $methodName] = $controller;

$controllerReflectionObject = new \ReflectionObject($controllerObject);
$reflectionMethod = $controllerReflectionObject->getMethod($methodName);

$userId = $this->getUserId();
foreach ($reflectionMethod->getAttributes(Page::class, \ReflectionAttribute::IS_INSTANCEOF) as $attribute) {
/** @var Page $page */
$page = $attribute->newInstance();

$message = $page->getMessage();
$message['userId'] = $userId;

$this->segmentIoProvider->page($message);

$this->segmentIoProvider->flush();
}
foreach ($reflectionMethod->getAttributes(Track::class, \ReflectionAttribute::IS_INSTANCEOF) as $attribute) {
/** @var Track $track */
$track = $attribute->newInstance();

$message = $track->getMessage();
$message['userId'] = $userId;

$this->segmentIoProvider->track($message);

$this->segmentIoProvider->flush();
}
}

/**
* @return string|null
*
* @throws \ReflectionException
*/
private function getUserId(): ?string
{
if (null === $token = $this->tokenStorage->getToken()) {
return $this->guestId;
}

if (!is_object($user = $token->getUser())) {
return $this->guestId;
}

$reflect = new \ReflectionClass($user);
if ($reflect->hasMethod('getId')) {
$userId = $user->getId();
if (null !== $userId) {
return $userId;
}
}

return $this->guestId;
}
}
21 changes: 21 additions & 0 deletions Resources/config/annotations.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?xml version="1.0" ?>

<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">

<parameters>
<parameter key="farma.segment_io.event_listener.annotation_listener.class">Farmatholin\SegmentIoBundle\EventListener\AnnotationListener</parameter>
</parameters>

<services>
<service id="farma.segment_io.annotation_listener" class="%farma.segment_io.event_listener.annotation_listener.class%">
<argument type="service" id="annotation_reader" on-invalid="ignore" />
<argument type="service" id="security.token_storage" />
<argument type="service" id="segment_io.analytics"/>
<argument>%farma.segment_io_guest_id%</argument>
<tag name="kernel.event_listener" event="kernel.controller" method="onKernelController"/>
</service>
</services>

</container>
20 changes: 20 additions & 0 deletions Resources/config/attributes.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?xml version="1.0" ?>

<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">

<parameters>
<parameter key="farma.segment_io.event_listener.attribute_listener.class">Farmatholin\SegmentIoBundle\EventListener\AttributeListener</parameter>
</parameters>

<services>
<service id="farma.segment_io.attribute_listener" class="%farma.segment_io.event_listener.attribute_listener.class%">
<argument type="service" id="security.token_storage" />
<argument type="service" id="segment_io.analytics"/>
<argument>%farma.segment_io_guest_id%</argument>
<tag name="kernel.event_listener" event="kernel.controller" method="onKernelController"/>
</service>
</services>

</container>
8 changes: 0 additions & 8 deletions Resources/config/services.xml
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,6 @@
<argument type="service" id="farma.segment_io.error_handler" />
</service>

<service id="farma.segment_io.annotation_listener" class="%farma.segment_io.event_listener.annotation_listener.class%">
<argument type="service" id="annotation_reader" />
<argument type="service" id="security.token_storage" />
<argument type="service" id="segment_io.analytics"/>
<argument>%farma.segment_io_guest_id%</argument>
<tag name="kernel.event_listener" event="kernel.controller" method="onKernelController"/>
</service>

</services>


Expand Down
6 changes: 6 additions & 0 deletions Resources/doc/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@ Install
$ php composer.phar require "farmatholin/segment-io-bundle":"dev-master"
Install Doctrine Annotations package if needed

.. code-block:: bash
$ php composer.phar require "doctrine/annotations":"^2.0"
Enable the bundle in the kernel:

.. code-block:: php
Expand Down
20 changes: 20 additions & 0 deletions Resources/doc/usage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,23 @@ If user doesn't exit, id set to 'guest' or from configuration 'guest_id'
{
// your code
}
Usage with Attributes
----------------------

User for attributes is getting from TokenStorage.
If user doesn't exit, id set to 'guest' or from configuration 'guest_id'

.. code-block:: php
use Farmatholin\SegmentIoBundle\Configuration\Page;
use Farmatholin\SegmentIoBundle\Configuration\Track;
#[Route('/', name: 'homepage')]
#[Page('index', category: 'page', properties: ['foo' => 'bar'])]
#[Track('visit homepage', properties: ['bar' => 'foo'], context: ['aa' => 'bb'], useTimestamp: true)]
public function indexAction(Request $request)
{
// your code
}
4 changes: 3 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,14 @@
"symfony/config": ">= 2.7",
"symfony/dependency-injection": ">= 2.7",
"symfony/http-kernel": ">= 2.7",
"doctrine/annotations": "1.* || 2.*",
"symfony/security-core": ">= 2.7"
},
"autoload": {
"psr-4": {
"Farmatholin\\SegmentIoBundle\\": "."
}
},
"suggest": {
"doctrine/annotations": "This package is required to use annotations instead of PHP attributes"
}
}

0 comments on commit 72ece7a

Please sign in to comment.