Skip to content

Commit

Permalink
Add TimezoneAwareInterface
Browse files Browse the repository at this point in the history
  • Loading branch information
phansys committed May 30, 2020
1 parent 1498a47 commit 7959da6
Show file tree
Hide file tree
Showing 10 changed files with 165 additions and 33 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/qa.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,7 @@ jobs:
- uses: actions/checkout@master
- name: PHPStan
uses: "docker://oskarstark/phpstan-ga"
env:
REQUIRE_DEV: true
with:
args: analyse
33 changes: 31 additions & 2 deletions UPGRADE-2.x.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,40 @@
UPGRADE 2.x
===========

UPGRADE FROM 2.7 to 2.8
=======================

### Timezone detector

``Sonata\IntlBundle\Timezone\TimezoneAwareInterface`` was added in order to provide
timezone detection for any user class.

Timezone inference based on the ``Sonata\UserBundle\Model\User::getTimezone()`` method
is deprecated and will be dropped in 3.0 version.
You MUST implement ``Sonata\IntlBundle\Timezone\TimezoneAwareInterface`` explicitly
in your user class.

Before:
```php
class User
{
// ...
}
```

After:
```php
class User implements \Sonata\IntlBundle\Timezone\TimezoneAwareInterface
{
// ...
}
```

UPGRADE FROM 2.3 to 2.4
=======================

### Tests

All files under the ``Tests`` directory are now correctly handled as internal test classes.
You can't extend them anymore, because they are only loaded when running internal tests.
All files under the ``Tests`` directory are now correctly handled as internal test classes.
You can't extend them anymore, because they are only loaded when running internal tests.
More information can be found in the [composer docs](https://getcomposer.org/doc/04-schema.md#autoload-dev).
3 changes: 0 additions & 3 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,6 @@
"symfony/phpunit-bridge": "^5.0",
"symfony/security-core": "^4.4"
},
"suggest": {
"sonata-project/user-bundle": "For user timezone detection"
},
"config": {
"sort-packages": true
},
Expand Down
11 changes: 11 additions & 0 deletions docs/reference/configuration.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,17 @@ Timezone detectors
User timezone detector
^^^^^^^^^^^^^^^^^^^^^^

.. versionadded:: 2.7

If the model class for the authenticated user implements ``Sonata\IntlBundle\Timezone\TimezoneAwareInterface``,
it returns the timezone from its ``getTimezone()`` method.
For convenience, the ``Sonata\IntlBundle\Timezone\TimezoneAwareTrait`` is available,
which provides a basic implementation.

**DEPRECATED**
Relying on ``Sonata\UserBundle\Model\User`` is deprecated since 2.x in favor of
explicit implementation of ``Sonata\IntlBundle\Timezone\TimezoneAwareInterface``.

If the SonataUserBundle_ is enabled, it returns the timezone from the
``Sonata\UserBundle\Model\User::getTimezone()`` method.

Expand Down
21 changes: 7 additions & 14 deletions src/DependencyInjection/SonataIntlExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -60,15 +60,12 @@ protected function configureTimezone(ContainerBuilder $container, array $config)

$timezoneDetectors = $config['timezone']['detectors'];

$bundles = $container->getParameter('kernel.bundles');

if (0 === \count($timezoneDetectors)) { // no value define in the configuration, set one
// Support Sonata User Bundle.
if (isset($bundles['SonataUserBundle'])) {
$timezoneDetectors[] = 'sonata.intl.timezone_detector.user';
}

$timezoneDetectors[] = 'sonata.intl.timezone_detector.locale';
if (0 === \count($timezoneDetectors)) {
// define default values if there is no value defined in configuration.
$timezoneDetectors = [
'sonata.intl.timezone_detector.user',
'sonata.intl.timezone_detector.locale',
];
}

foreach ($timezoneDetectors as $id) {
Expand All @@ -87,10 +84,6 @@ protected function configureTimezone(ContainerBuilder $container, array $config)
->replaceArgument(0, $config['timezone']['default'])
;

if (!isset($bundles['SonataUserBundle'])) {
$container->removeDefinition('sonata.intl.timezone_detector.user');
}

$container->setParameter('sonata_intl.timezone.detectors', $timezoneDetectors);
}

Expand All @@ -105,7 +98,7 @@ protected function configureLocale(ContainerBuilder $container, array $config)
*
* @throws \RuntimeException If one of the locales is invalid
*/
private function validateTimezones(array $timezones)
private function validateTimezones(array $timezones): void
{
foreach ($timezones as $timezone) {
try {
Expand Down
22 changes: 22 additions & 0 deletions src/Timezone/TimezoneAwareInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

declare(strict_types=1);

/*
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Sonata\IntlBundle\Timezone;

/**
* @author Javier Spagnoletti <phansys@gmail.com>
*/
interface TimezoneAwareInterface
{
public function getTimezone(): ?string;
}
32 changes: 32 additions & 0 deletions src/Timezone/TimezoneAwareTrait.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php

declare(strict_types=1);

/*
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Sonata\IntlBundle\Timezone;

/**
* Basic Implementation of TimezoneAwareInterface.
*
* @author Javier Spagnoletti <phansys@gmail.com>
*/
trait TimezoneAwareTrait
{
/**
* @var string|null
*/
private $timezone;

final public function getTimezone(): ?string
{
return $this->timezone;
}
}
2 changes: 1 addition & 1 deletion src/Timezone/TimezoneDetectorInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ interface TimezoneDetectorInterface
/**
* Get the appropriate timezone.
*
* @return string
* @return string|null
*/
public function getTimezone();
}
18 changes: 16 additions & 2 deletions src/Timezone/UserBasedTimezoneDetector.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,15 +36,29 @@ public function __construct(TokenStorageInterface $securityContext)
public function getTimezone()
{
if (!$token = $this->securityContext->getToken()) {
return;
return null;
}

if (!$user = $token->getUser()) {
return;
return null;
}

if ($user instanceof TimezoneAwareInterface) {
return $user->getTimezone();
}

// NEXT_MAJOR: Remove this check and the related documentation at `docs/reference/configuration.rst`.
if ($user instanceof User) {
@trigger_error(sprintf(
'Timezone inference based on the %s class is deprecated since sonata-project/intl-bundle 2.x and will be dropped in 3.0 version.'
.' Implement %s explicitly in your user class instead.',
User::class,
TimezoneAwareInterface::class
), E_USER_DEPRECATED);

return $user->getTimezone();
}

return null;
}
}
54 changes: 43 additions & 11 deletions tests/Timezone/UserBasedTimezoneDetectorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
namespace Sonata\IntlBundle\Tests\Timezone;

use PHPUnit\Framework\TestCase;
use Sonata\IntlBundle\Timezone\TimezoneAwareInterface;
use Sonata\IntlBundle\Timezone\TimezoneAwareTrait;
use Sonata\IntlBundle\Timezone\UserBasedTimezoneDetector;
use Sonata\UserBundle\Model\User;
use Sonata\UserBundle\SonataUserBundle;
Expand All @@ -23,30 +25,60 @@
/**
* @author Emmanuel Vella <vella.emmanuel@gmail.com>
*/
class UserBasedTimezoneDetectorTest extends TestCase
final class UserBasedTimezoneDetectorTest extends TestCase
{
protected function setUp(): void
{
if (!class_exists(SonataUserBundle::class)) {
$this->markTestSkipped('SonataUserBundle must be installed to run this test.');
}
}

public static function timezoneProvider()
public static function timezoneProvider(): iterable
{
return [
['Europe/Paris'],
[null],
];
}

/**
* @dataProvider timezoneProvider
*/
public function testUserTimezoneDetection(?string $timezone): void
{
$user = new class($timezone) implements TimezoneAwareInterface {
use TimezoneAwareTrait;

public function __construct(?string $timezone)
{
$this->timezone = $timezone;
}
};

$token = $this->createMock(TokenInterface::class);
$token
->expects($this->once())
->method('getUser')
->willReturn($user)
;

$storage = $this->createMock(TokenStorageInterface::class);

$storage
->expects($this->once())
->method('getToken')
->willReturn($token)
;

$timezoneDetector = new UserBasedTimezoneDetector($storage);
$this->assertSame($timezone, $timezoneDetector->getTimezone());
}

/**
* @dataProvider timezoneProvider
*
* @group legacy
*/
public function testDetectsTimezoneForUser($timezone)
public function testDetectsTimezoneForUser(?string $timezone): void
{
if (!class_exists(SonataUserBundle::class)) {
$this->markTestSkipped('SonataUserBundle must be installed to run this test.');
}

$user = $this->createMock(User::class);
$user
->method('getTimezone')
Expand All @@ -70,7 +102,7 @@ public function testDetectsTimezoneForUser($timezone)
$this->assertSame($timezone, $timezoneDetector->getTimezone());
}

public function testTimezoneNotDetected()
public function testTimezoneNotDetected(): void
{
$storage = $this->createMock(TokenStorageInterface::class);

Expand Down

0 comments on commit 7959da6

Please sign in to comment.