diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index e2471a7..4ae6e25 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -1,12 +1,12 @@ name: Tests -on: [ pull_request ] +on: ["push", "pull_request"] jobs: static-code-analysis: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v3 - name: Run PHPStan uses: docker://jakzal/phpqa with: @@ -15,11 +15,11 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - php: [ '7.2', '7.4', '8.0' ] - symfony: [ '4.4.*', '5.3.*' ] + php: [ '8.0', '8.1' ] + symfony: [ '5.4.*', '6.0.*' ] name: Test on Symfony ${{ matrix.symfony }} with PHP ${{ matrix.php }} steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - uses: shivammathur/setup-php@v2 with: php-version: ${{ matrix.php }} @@ -28,15 +28,34 @@ jobs: - run: composer require symfony/form:${{ matrix.symfony }} --no-update - run: composer install - run: make test + + test-highest: + runs-on: ubuntu-latest + strategy: + matrix: + php: [ '8.2' ] + symfony: [ '6.2.*' ] + name: Test on Symfony ${{ matrix.symfony }} with PHP ${{ matrix.php }} + steps: + - uses: actions/checkout@v3 + - uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + coverage: none # disable xdebug, pcov + - run: composer require symfony/framework-bundle:${{ matrix.symfony }} --no-update + - run: composer require symfony/form:${{ matrix.symfony }} --no-update + - run: composer install + - run: make test + test-lowest: runs-on: ubuntu-latest strategy: matrix: - php: [ '7.2', '7.3' ] - symfony: [ '3.4.*', '4.4.*' ] + php: [ '8.0' ] + symfony: [ '5.4.*'] name: Test lowest on Symfony ${{ matrix.symfony }} with PHP ${{ matrix.php }} steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - uses: shivammathur/setup-php@v2 with: php-version: ${{ matrix.php }} @@ -45,11 +64,15 @@ jobs: - run: composer require symfony/form:${{ matrix.symfony }} --no-update - run: composer install - run: make test-lowest + php-cs-fixer: + # https://github.com/jakzal/phpqa php-cs-fixer is not supported for php 8.2 (checked on 07-02-2023) + # https://github.com/jakzal/phpqa/issues/398 runs-on: ubuntu-latest steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v3 - name: Run PHP-CS-Fixer - uses: docker://jakzal/phpqa + #uses: docker://jakzal/phpqa + uses: docker://jakzal/phpqa:php8.1-alpine with: args: php-cs-fixer fix --dry-run diff --git a/.gitignore b/.gitignore index 082fd22..daef913 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ vendor/ composer.lock phpunit.xml -.php_cs.cache +.php-cs-fixer.cache .phpunit.result.cache diff --git a/.php_cs b/.php-cs-fixer.php similarity index 81% rename from .php_cs rename to .php-cs-fixer.php index 5a74404..574406f 100644 --- a/.php_cs +++ b/.php-cs-fixer.php @@ -1,11 +1,11 @@ setRules(array( +return (new PhpCsFixer\Config()) + ->setRules([ '@Symfony' => true, '@Symfony:risky' => true, 'no_superfluous_phpdoc_tags' => true, - )) + ]) ->setRiskyAllowed(true) ->setFinder( PhpCsFixer\Finder::create() diff --git a/Controller/SettingsController.php b/Controller/SettingsController.php index 3796535..7c317db 100644 --- a/Controller/SettingsController.php +++ b/Controller/SettingsController.php @@ -13,43 +13,13 @@ class SettingsController extends AbstractController { - /** - * @var string|null - */ - private $securityRole; - - /** - * @var bool - */ - private $securityManageOwnSettings; - - /** - * @var TranslatorInterface - */ - private $translator; - - /** - * @var SettingsManagerInterface - */ - private $settingsManager; - - /** - * @var string - */ - private $template; - public function __construct( - TranslatorInterface $translator, - SettingsManagerInterface $settingsManager, - string $template, - bool $securityManageOwnSettings, - ?string $securityRole + private TranslatorInterface $translator, + private SettingsManagerInterface $settingsManager, + private string $template, + private bool $securityManageOwnSettings, + private ?string $securityRole, ) { - $this->translator = $translator; - $this->settingsManager = $settingsManager; - $this->template = $template; - $this->securityManageOwnSettings = $securityManageOwnSettings; - $this->securityRole = $securityRole; } /** @@ -57,7 +27,7 @@ public function __construct( */ public function manageGlobalAction(Request $request): Response { - if (null !== $this->securityRole && !$this->get('security.authorization_checker')->isGranted($this->securityRole)) { + if (null !== $this->securityRole && !$this->isGranted($this->securityRole)) { throw new AccessDeniedException($this->translator->trans('not_allowed_to_edit_global_settings', [], 'settings')); } @@ -69,7 +39,9 @@ public function manageGlobalAction(Request $request): Response */ public function manageOwnAction(Request $request): Response { - if (null === $this->get('security.token_storage')->getToken()) { + $user = $this->getUser(); + + if (null === $user) { throw new AccessDeniedException($this->translator->trans('must_be_logged_in_to_edit_own_settings', [], 'settings')); } @@ -77,9 +49,8 @@ public function manageOwnAction(Request $request): Response throw new AccessDeniedException($this->translator->trans('not_allowed_to_edit_own_settings', [], 'settings')); } - $user = $this->get('security.token_storage')->getToken()->getUser(); if (!$user instanceof SettingsOwnerInterface) { - //For this to work the User entity must implement SettingsOwnerInterface + // For this to work the User entity must implement SettingsOwnerInterface throw new AccessDeniedException(); } diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php index f803293..f73c13e 100644 --- a/DependencyInjection/Configuration.php +++ b/DependencyInjection/Configuration.php @@ -13,15 +13,10 @@ class Configuration implements ConfigurationInterface /** * {@inheritdoc} */ - public function getConfigTreeBuilder() + public function getConfigTreeBuilder(): TreeBuilder { $treeBuilder = new TreeBuilder('dmishh_settings'); - // Keep compatibility with symfony/config < 4.2 - if (!method_exists($treeBuilder, 'getRootNode')) { - $rootNode = $treeBuilder->root('dmishh_settings'); - } else { - $rootNode = $treeBuilder->getRootNode(); - } + $rootNode = $treeBuilder->getRootNode(); $scopes = [ SettingsManagerInterface::SCOPE_ALL, @@ -32,7 +27,7 @@ public function getConfigTreeBuilder() $rootNode ->children() ->scalarNode('template') - ->defaultValue('DmishhSettingsBundle:Settings:manage.html.twig') + ->defaultValue('@DmishhSettings/Settings/manage.html.twig') ->end() ->scalarNode('cache_service')->defaultNull()->info('A service implementing Psr\Cache\CacheItemPoolInterface')->end() ->integerNode('cache_lifetime')->defaultValue(3600)->end() @@ -74,7 +69,7 @@ public function getConfigTreeBuilder() ->end() ->end() ->variableNode('constraints') - ->info('The constraints on this option. Example, use constraits found in Symfony\Component\Validator\Constraints') + ->info('The constraints on this option. Example, use constraints found in Symfony\Component\Validator\Constraints') ->defaultValue([]) ->validate() ->always(function ($v) { diff --git a/DependencyInjection/DmishhSettingsExtension.php b/DependencyInjection/DmishhSettingsExtension.php index 3d858c9..89e0423 100644 --- a/DependencyInjection/DmishhSettingsExtension.php +++ b/DependencyInjection/DmishhSettingsExtension.php @@ -22,8 +22,10 @@ class DmishhSettingsExtension extends Extension { /** * {@inheritdoc} + * + * @param array> $configs */ - public function load(array $configs, ContainerBuilder $container) + public function load(array $configs, ContainerBuilder $container): void { $configuration = new Configuration(); $config = $this->processConfiguration($configuration, $configs); diff --git a/Entity/Setting.php b/Entity/Setting.php index b0bd747..af42572 100644 --- a/Entity/Setting.php +++ b/Entity/Setting.php @@ -4,48 +4,31 @@ use Doctrine\ORM\Mapping as ORM; -/** - * @ORM\Table(name="dmishh_settings", indexes={@ORM\Index(name="name_owner_id_idx", columns={"name", "owner_id"})}) - * @ORM\Entity - */ +#[ORM\Table(name: 'dmishh_settings')] +#[ORM\Index(name: 'name_owner_id_idx', columns: ['name', 'owner_id'])] +#[ORM\Entity] class Setting { - /** - * @var int - * - * @ORM\Column(type="integer") - * @ORM\Id - * @ORM\GeneratedValue(strategy="AUTO") - */ - private $id; + #[ORM\Column(type: 'integer')] + #[ORM\Id] + #[ORM\GeneratedValue(strategy: 'AUTO')] + private ?int $id; - /** - * @var string|null - * - * @ORM\Column(type="string", length=255) - */ - private $name; + #[ORM\Column(type: 'string', length: 255)] + private ?string $name; - /** - * @var string|null - * - * @ORM\Column(type="text", nullable=true) - */ - private $value; + #[ORM\Column(type: 'text', nullable: true)] + private ?string $value; - /** - * @var string|null - * - * @ORM\Column(name="owner_id", type="string", length=255, nullable=true) - */ - private $ownerId; + #[ORM\Column(name: 'owner_id', type: 'string', length: 255, nullable: true)] + private ?string $ownerId; public function getId(): ?int { return $this->id; } - public function setName(?string $name) + public function setName(?string $name): void { $this->name = $name; } @@ -70,7 +53,7 @@ public function getOwnerId(): ?string return $this->ownerId; } - public function setOwnerId(?string $ownerId) + public function setOwnerId(?string $ownerId): void { $this->ownerId = $ownerId; } diff --git a/Exception/InvalidArgumentException.php b/Exception/InvalidArgumentException.php new file mode 100644 index 0000000..83dad5b --- /dev/null +++ b/Exception/InvalidArgumentException.php @@ -0,0 +1,7 @@ +settingsConfiguration = $settingsConfiguration; } /** * {@inheritdoc} + * + * @param array $options */ - public function buildForm(FormBuilderInterface $builder, array $options) + public function buildForm(FormBuilderInterface $builder, array $options): void { foreach ($this->settingsConfiguration as $name => $configuration) { // If setting's value exists in data and setting isn't disabled @@ -41,7 +40,7 @@ public function buildForm(FormBuilderInterface $builder, array $options) if (class_exists($class)) { $constraints[] = new $class($constraintOptions); } else { - throw new SettingsException(sprintf('Constraint class "%s" not found', $class)); + throw new UnknownConstraintException($class); } } @@ -68,7 +67,7 @@ function ($label) use ($fieldOptions) { } } - public function configureOptions(OptionsResolver $resolver) + public function configureOptions(OptionsResolver $resolver): void { $resolver->setDefaults( [ @@ -77,7 +76,7 @@ public function configureOptions(OptionsResolver $resolver) ); } - public function getBlockPrefix() + public function getBlockPrefix(): string { return 'settings_management'; } diff --git a/Manager/CachedSettingsManager.php b/Manager/CachedSettingsManager.php index 06d5258..bc9efc9 100644 --- a/Manager/CachedSettingsManager.php +++ b/Manager/CachedSettingsManager.php @@ -10,34 +10,18 @@ */ class CachedSettingsManager implements SettingsManagerInterface { - const PREFIX = 'dmishh_settings_%s_%s'; + public const PREFIX = 'dmishh_settings_%s_%s'; - /** - * @var CacheItemPoolInterface - */ - private $storage; - - /** - * @var SettingsManagerInterface - */ - private $settingsManager; - - /** - * @var int - */ - private $cacheLifeTime; - - public function __construct(SettingsManagerInterface $settingsManager, CacheItemPoolInterface $storage, $cacheLifeTime) + public function __construct(private SettingsManagerInterface $settingsManager, + private CacheItemPoolInterface $storage, + private int|\DateInterval|null $cacheLifeTime) { - $this->settingsManager = $settingsManager; - $this->storage = $storage; - $this->cacheLifeTime = $cacheLifeTime; } /** * {@inheritdoc} */ - public function get(string $name, ?SettingsOwnerInterface $owner = null, $default = null) + public function get(string $name, ?SettingsOwnerInterface $owner = null, mixed $default = null): mixed { if (null !== $cached = $this->fetchFromCache($name, $owner)) { return $cached; @@ -50,7 +34,10 @@ public function get(string $name, ?SettingsOwnerInterface $owner = null, $defaul } /** - * {@inheritdoc} + * Check cache and populate it if necessary. + * Returns all settings as associative name-value array. + * + * @return array */ public function all(?SettingsOwnerInterface $owner = null): array { @@ -67,7 +54,7 @@ public function all(?SettingsOwnerInterface $owner = null): array /** * {@inheritdoc} */ - public function set(string $name, $value, ?SettingsOwnerInterface $owner = null): void + public function set(string $name, mixed $value, ?SettingsOwnerInterface $owner = null): void { $this->invalidateCache($name, $owner); $this->invalidateCache(null, $owner); @@ -122,11 +109,9 @@ protected function fetchFromCache(?string $name, ?SettingsOwnerInterface $owner /** * Store in cache. * - * @param SettingsOwnerInterface $owner - * * @return bool TRUE if the entry was successfully stored in the cache, FALSE otherwise */ - protected function storeInCache(?string $name, $value, ?SettingsOwnerInterface $owner = null): bool + protected function storeInCache(?string $name, mixed $value, ?SettingsOwnerInterface $owner = null): bool { $item = $this->storage->getItem($this->getCacheKey($name, $owner)) ->set($value) @@ -135,9 +120,6 @@ protected function storeInCache(?string $name, $value, ?SettingsOwnerInterface $ return $this->storage->save($item); } - /** - * @param SettingsOwnerInterface $owner - */ protected function getCacheKey(?string $key, ?SettingsOwnerInterface $owner = null): string { return sprintf(self::PREFIX, $owner ? $owner->getSettingIdentifier() : '', $key); diff --git a/Manager/SettingsManager.php b/Manager/SettingsManager.php index 4e815ad..43d6a4e 100644 --- a/Manager/SettingsManager.php +++ b/Manager/SettingsManager.php @@ -10,7 +10,6 @@ use Dmishh\SettingsBundle\Serializer\SerializerInterface; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityRepository; -use Doctrine\Persistence\ObjectManager; /** * Settings Manager provides settings management and persistence using Doctrine's Object Manager. @@ -21,50 +20,32 @@ class SettingsManager implements SettingsManagerInterface { /** - * @var array + * @var ?array */ - private $globalSettings; + private ?array $globalSettings = null; /** - * @var array + * @var ?array> */ - private $ownerSettings; - - /** - * @var ObjectManager - */ - private $em; + private ?array $ownerSettings = null; /** * @var EntityRepository */ - private $repository; - - /** - * @var SerializerInterface - */ - private $serializer; - - /** - * @var array - */ - private $settingsConfiguration; + private EntityRepository $repository; public function __construct( - EntityManagerInterface $em, - SerializerInterface $serializer, - array $settingsConfiguration = [] + private EntityManagerInterface $em, + private SerializerInterface $serializer, + private array $settingsConfiguration = [] ) { - $this->em = $em; $this->repository = $em->getRepository(Setting::class); - $this->serializer = $serializer; - $this->settingsConfiguration = $settingsConfiguration; } /** * {@inheritdoc} */ - public function get(string $name, ?SettingsOwnerInterface $owner = null, $default = null) + public function get(string $name, ?SettingsOwnerInterface $owner = null, mixed $default = null): mixed { $this->validateSetting($name, $owner); $this->loadSettings($owner); @@ -77,8 +58,8 @@ public function get(string $name, ?SettingsOwnerInterface $owner = null, $defaul break; case SettingsManagerInterface::SCOPE_ALL: $value = $this->globalSettings[$name] ?? null; - // Do not break here. Try to fetch the users settings - // no break + // Do not break here. Try to fetch the users settings + // no break case SettingsManagerInterface::SCOPE_USER: if (null !== $owner) { $value = $this->ownerSettings[$owner->getSettingIdentifier()][$name] ?? $value; @@ -86,11 +67,13 @@ public function get(string $name, ?SettingsOwnerInterface $owner = null, $defaul break; } - return null === $value ? $default : $value; + return $value ?? $default; } /** - * {@inheritdoc} + * Returns all settings as associative name-value array. + * + * @return array */ public function all(?SettingsOwnerInterface $owner = null): array { @@ -115,7 +98,7 @@ public function all(?SettingsOwnerInterface $owner = null): array /** * {@inheritdoc} */ - public function set(string $name, $value, ?SettingsOwnerInterface $owner = null): void + public function set(string $name, mixed $value, ?SettingsOwnerInterface $owner = null): void { $this->setWithoutFlush($name, $value, $owner); $this->flush([$name], $owner); @@ -160,7 +143,7 @@ protected function findSettingByName(array $haystack, string $needle): ?Setting /** * Sets setting value to private array. Used for settings' batch saving. */ - private function setWithoutFlush(string $name, $value, ?SettingsOwnerInterface $owner = null): void + private function setWithoutFlush(string $name, mixed $value, ?SettingsOwnerInterface $owner = null): void { $this->validateSetting($name, $owner); $this->loadSettings($owner); @@ -175,29 +158,30 @@ private function setWithoutFlush(string $name, $value, ?SettingsOwnerInterface $ /** * Flushes settings defined by $names to database. * + * @param string[] $names + * * @throws UnknownSerializerException */ private function flush(array $names, ?SettingsOwnerInterface $owner = null): void { $settings = $this->repository->findBy([ 'name' => $names, - 'ownerId' => null === $owner ? null : $owner->getSettingIdentifier(), + 'ownerId' => $owner?->getSettingIdentifier(), ]); // Assert: $settings might be a smaller set than $names - // For each settings that you are trying to save + // For each setting that you are trying to save foreach ($names as $name) { try { $value = $this->get($name, $owner); - } catch (WrongScopeException $e) { + } catch (WrongScopeException) { continue; } - /** @var Setting $setting */ $setting = $this->findSettingByName($settings, $name); - if (!$setting) { + if (null === $setting) { // if the setting does not exist in DB, create it $setting = new Setting(); $setting->setName($name); @@ -216,17 +200,13 @@ private function flush(array $names, ?SettingsOwnerInterface $owner = null): voi /** * Checks that $name is valid setting and it's scope is also valid. * - * @param SettingsOwnerInterface $owner - * - * @return SettingsManager - * * @throws UnknownSettingException * @throws WrongScopeException */ private function validateSetting(string $name, ?SettingsOwnerInterface $owner = null): void { // Name validation - if (!\is_string($name) || !\array_key_exists($name, $this->settingsConfiguration)) { + if (!\array_key_exists($name, $this->settingsConfiguration)) { throw new UnknownSettingException($name); } @@ -256,7 +236,9 @@ private function loadSettings(SettingsOwnerInterface $owner = null): void } /** - * Retreives settings from repository. + * Retrieves settings from repository. + * + * @return array * * @throws UnknownSerializerException */ @@ -268,13 +250,13 @@ private function getSettingsFromRepository(?SettingsOwnerInterface $owner = null try { $this->validateSetting($name, $owner); $settings[$name] = null; - } catch (WrongScopeException $e) { + } catch (WrongScopeException) { continue; } } /** @var Setting $setting */ - foreach ($this->repository->findBy(['ownerId' => null === $owner ? null : $owner->getSettingIdentifier()]) as $setting) { + foreach ($this->repository->findBy(['ownerId' => $owner?->getSettingIdentifier()]) as $setting) { if (\array_key_exists($setting->getName(), $settings)) { $settings[$setting->getName()] = $this->serializer->unserialize($setting->getValue()); } diff --git a/Manager/SettingsManagerInterface.php b/Manager/SettingsManagerInterface.php index 6dfeba3..3877455 100644 --- a/Manager/SettingsManagerInterface.php +++ b/Manager/SettingsManagerInterface.php @@ -6,29 +6,33 @@ interface SettingsManagerInterface { - const SCOPE_ALL = 'all'; - const SCOPE_GLOBAL = 'global'; - const SCOPE_USER = 'user'; + public const SCOPE_ALL = 'all'; + public const SCOPE_GLOBAL = 'global'; + public const SCOPE_USER = 'user'; /** * Returns setting value by its name. * * @param mixed|null $default value to return if the setting is not set */ - public function get(string $name, ?SettingsOwnerInterface $owner = null, $default = null); + public function get(string $name, ?SettingsOwnerInterface $owner = null, mixed $default = null): mixed; /** * Returns all settings as associative name-value array. + * + * @return array */ public function all(?SettingsOwnerInterface $owner = null): array; /** * Sets setting value by its name. */ - public function set(string $name, $value, ?SettingsOwnerInterface $owner = null): void; + public function set(string $name, mixed $value, ?SettingsOwnerInterface $owner = null): void; /** * Sets settings' values from associative name-value array. + * + * @param array $settings */ public function setMany(array $settings, ?SettingsOwnerInterface $owner = null): void; diff --git a/README.md b/README.md index d641843..c29ccb4 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,9 @@ SettingsBundle Bundle for storing configuration with Symfony in database using Doctrine ORM. + +[![Build Status](https://github.com/rvanlaak/SettingsBundle/actions/workflows/main.yml/badge.svg)](https://github.com/rvanlaak/SettingsBundle/actions/workflows/main.yml) + 👀 This bundle was previously known as `dmissh/settings-bundle`, and the Packagist installation instruction will stay as is. ## Features @@ -39,22 +42,22 @@ Twig template: {{ get_setting('some_user_setting', app.user) }} {# => 'value' #} ``` -See the [general usage](/Resources/doc/general-usage.md) documentation for more examples. +See the [general usage](./Resources/doc/general-usage.md) documentation for more examples. ## Documentation -* [Installation](/Resources/doc/installation.md) -* [General usage](/Resources/doc/general-usage.md) -* [Scopes](/Resources/doc/scopes.md) -* [Advanced configuration](/Resources/doc/advanced-configuration.md) -* [I18n](/Resources/doc/i18n.md) -* [Customization](/Resources/doc/customization.md) -* [FAQ](/Resources/doc/faq.md) +* [Installation](./Resources/doc/installation.md) +* [General usage](./Resources/doc/general-usage.md) +* [Scopes](./Resources/doc/scopes.md) +* [Advanced configuration](./Resources/doc/advanced-configuration.md) +* [I18n](./Resources/doc/i18n.md) +* [Customization](./Resources/doc/customization.md) +* [FAQ](./Resources/doc/faq.md) ## Changelog, Roadmap and contribution -Please, do not hesitate to [report bugs](https://github.com/dmishh/SettingsBundle/issues) or send -[pull requests](https://github.com/dmishh/SettingsBundle/pulls). It will help to motivate me to support +Please, do not hesitate to [report bugs](https://github.com/rvanlaak/SettingsBundle/issues) or send +[pull requests](https://github.com/rvanlaak/SettingsBundle/pulls). It will help to motivate me to support library better than anything else :) See [CHANGELOG.md](CHANGELOG.md) for all major changes. @@ -65,4 +68,4 @@ Make sure to read the [UPGRADE.md](UPGRADE.md) to successfully migrate your appl ## License -The MIT License. For the full text of license, please, see [LICENSE](/LICENSE) +The MIT License. For the full text of license, please, see [LICENSE](LICENSE) diff --git a/Serializer/JsonSerializer.php b/Serializer/JsonSerializer.php index 7687b20..984a30a 100644 --- a/Serializer/JsonSerializer.php +++ b/Serializer/JsonSerializer.php @@ -2,14 +2,29 @@ namespace Dmishh\SettingsBundle\Serializer; +use Dmishh\SettingsBundle\Exception\InvalidArgumentException; + class JsonSerializer implements SerializerInterface { - public function serialize($data) + /** + * @throws \JsonException + */ + public function serialize(mixed $data): string { - return json_encode($data); + $serialized = json_encode($data, \JSON_THROW_ON_ERROR); + + /* @phpstan-ignore-next-line */ + if (false === $serialized) { + throw new InvalidArgumentException('Invalid argument: this argument cannot be serialized with this serializer'); + } + + return $serialized; } - public function unserialize($serialized) + /** + * @throws \JsonException + */ + public function unserialize(string $serialized): mixed { return json_decode($serialized, true); } diff --git a/Serializer/PhpSerializer.php b/Serializer/PhpSerializer.php index f0c44aa..34f4005 100644 --- a/Serializer/PhpSerializer.php +++ b/Serializer/PhpSerializer.php @@ -4,12 +4,12 @@ class PhpSerializer implements SerializerInterface { - public function serialize($data) + public function serialize(mixed $data): string { return serialize($data); } - public function unserialize($serialized) + public function unserialize(string $serialized): mixed { return unserialize($serialized); } diff --git a/Serializer/SerializerFactory.php b/Serializer/SerializerFactory.php index 1857e3f..f173bb5 100644 --- a/Serializer/SerializerFactory.php +++ b/Serializer/SerializerFactory.php @@ -10,24 +10,27 @@ class SerializerFactory /** * @param string $name short name of serializer (ex.: php) or full class name * - * @throws \Dmishh\SettingsBundle\Exception\UnknownSerializerException - * - * @return SerializerInterface + * @throws UnknownSerializerException */ - public static function create($name) + public static function create(string $name): SerializerInterface { $serializerClass = 'Dmishh\\SettingsBundle\\Serializer\\'.Container::camelize($name).'Serializer'; if (class_exists($serializerClass)) { - return new $serializerClass(); - } else { - $serializerClass = $name; - - if (class_exists($serializerClass)) { - $serializer = new $serializerClass(); - if ($serializer instanceof SerializerInterface) { - return $serializer; - } + $serializer = new $serializerClass(); + if ($serializer instanceof SerializerInterface) { + return $serializer; + } + + throw new UnknownSerializerException($serializerClass); + } + + $serializerClass = $name; + + if (class_exists($serializerClass)) { + $serializer = new $serializerClass(); + if ($serializer instanceof SerializerInterface) { + return $serializer; } } diff --git a/Serializer/SerializerInterface.php b/Serializer/SerializerInterface.php index 713a3b8..6ab514d 100644 --- a/Serializer/SerializerInterface.php +++ b/Serializer/SerializerInterface.php @@ -4,13 +4,7 @@ interface SerializerInterface { - /** - * @return string - */ - public function serialize($data); + public function serialize(mixed $data): string; - /** - * @param string $serialized - */ - public function unserialize($serialized); + public function unserialize(string $serialized): mixed; } diff --git a/Tests/AbstractTest.php b/Tests/AbstractTest.php index 7c96f5a..dbdd5ad 100644 --- a/Tests/AbstractTest.php +++ b/Tests/AbstractTest.php @@ -2,16 +2,13 @@ namespace Dmishh\SettingsBundle\Tests; -use Doctrine\Common\Annotations\AnnotationRegistry; use Doctrine\ORM\Configuration; +use Doctrine\ORM\EntityManager; use PHPUnit\Framework\TestCase; abstract class AbstractTest extends TestCase { - /** - * @var \Doctrine\ORM\EntityManager - */ - protected $em; + protected EntityManager $em; /** * {@inheritdoc} @@ -30,19 +27,14 @@ protected function tearDown(): void $this->em->close(); } - protected function createEntityManager() + protected function createEntityManager(): EntityManager { $config = new Configuration(); $config->setProxyDir(sys_get_temp_dir()); $config->setProxyNamespace('EntityProxy'); $config->setAutoGenerateProxyClasses(true); - AnnotationRegistry::registerFile( - __DIR__. - '/../vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/DoctrineAnnotations.php' - ); - $driver = new \Doctrine\ORM\Mapping\Driver\AnnotationDriver( - new \Doctrine\Common\Annotations\AnnotationReader(), + $driver = new \Doctrine\ORM\Mapping\Driver\AttributeDriver( [__DIR__.'/../Entity'] ); $config->setMetadataDriverImpl($driver); @@ -52,12 +44,10 @@ protected function createEntityManager() 'memory' => true, ]; - $em = \Doctrine\ORM\EntityManager::create($conn, $config); - - return $em; + return EntityManager::create($conn, $config); } - protected function generateSchema() + protected function generateSchema(): void { $metadatas = $this->em->getMetadataFactory()->getAllMetadata(); diff --git a/Tests/CachedSettingsManagerTest.php b/Tests/CachedSettingsManagerTest.php index 99e7b99..35fa183 100644 --- a/Tests/CachedSettingsManagerTest.php +++ b/Tests/CachedSettingsManagerTest.php @@ -2,6 +2,7 @@ namespace Dmishh\SettingsBundle\Tests; +use Dmishh\SettingsBundle\Entity\SettingsOwnerInterface; use Dmishh\SettingsBundle\Manager\CachedSettingsManager; use Dmishh\SettingsBundle\Manager\SettingsManager; use PHPUnit\Framework\TestCase; @@ -9,7 +10,7 @@ class CachedSettingsManagerTest extends TestCase { - public function testGet() + public function testGet(): void { $owner = $this->createOwner(); $name = 'name'; @@ -20,7 +21,7 @@ public function testGet() $settingsManager->shouldReceive('get')->once()->with($name, $owner, $defaultValue)->andReturn($value); $cachedSettingsManager = $this->getMockBuilder(CachedSettingsManager::class) - ->setMethods(['fetchFromCache', 'storeInCache']) + ->onlyMethods(['fetchFromCache', 'storeInCache']) ->setConstructorArgs([$settingsManager, $this->getMockBuilder(CacheItemPoolInterface::class)->getMock(), 4711]) ->getMock(); $cachedSettingsManager->expects($this->once()) @@ -35,7 +36,7 @@ public function testGet() $this->assertEquals($value, $cachedSettingsManager->get($name, $owner, $defaultValue)); } - public function testGetHit() + public function testGetHit(): void { $owner = $this->createOwner(); $name = 'name'; @@ -45,7 +46,7 @@ public function testGetHit() $settingsManager = \Mockery::mock(SettingsManager::class); $cachedSettingsManager = $this->getMockBuilder(CachedSettingsManager::class) - ->setMethods(['fetchFromCache', 'storeInCache']) + ->onlyMethods(['fetchFromCache', 'storeInCache']) ->setConstructorArgs([$settingsManager, $this->getMockBuilder(CacheItemPoolInterface::class)->getMock(), 4711]) ->getMock(); $cachedSettingsManager->expects($this->once()) @@ -56,7 +57,7 @@ public function testGetHit() $this->assertEquals($value, $cachedSettingsManager->get($name, $owner, $defaultValue)); } - public function testAll() + public function testAll(): void { $owner = $this->createOwner(); $value = ['foo' => 'bar']; @@ -65,13 +66,15 @@ public function testAll() $settingsManager->shouldReceive('all')->once()->with($owner)->andReturn($value); $cachedSettingsManager = $this->getMockBuilder(CachedSettingsManager::class) - ->setMethods(['fetchFromCache', 'storeInCache']) + ->onlyMethods(['fetchFromCache', 'storeInCache']) ->setConstructorArgs([$settingsManager, $this->getMockBuilder(CacheItemPoolInterface::class)->getMock(), 4711]) ->getMock(); + $cachedSettingsManager->expects($this->once()) ->method('fetchFromCache') ->with($this->equalTo(null), $this->equalTo($owner)) ->willReturn(null); + $cachedSettingsManager->expects($this->once()) ->method('storeInCache') ->with($this->equalTo(null), $this->equalTo($value), $this->equalTo($owner)) @@ -80,7 +83,7 @@ public function testAll() $this->assertEquals($value, $cachedSettingsManager->all($owner)); } - public function testAllHit() + public function testAllHit(): void { $owner = $this->createOwner(); $value = ['foo' => 'bar']; @@ -88,9 +91,10 @@ public function testAllHit() $settingsManager = \Mockery::mock(SettingsManager::class); $cachedSettingsManager = $this->getMockBuilder(CachedSettingsManager::class) - ->setMethods(['fetchFromCache', 'storeInCache']) + ->onlyMethods(['fetchFromCache', 'storeInCache']) ->setConstructorArgs([$settingsManager, $this->getMockBuilder(CacheItemPoolInterface::class)->getMock(), 4711]) ->getMock(); + $cachedSettingsManager->expects($this->once()) ->method('fetchFromCache') ->with($this->equalTo(null), $this->equalTo($owner)) @@ -99,7 +103,7 @@ public function testAllHit() $this->assertEquals($value, $cachedSettingsManager->all($owner)); } - public function testSet() + public function testSet(): void { $owner = $this->createOwner(); $name = 'name'; @@ -109,26 +113,24 @@ public function testSet() $settingsManager->shouldReceive('set')->once()->with($name, $value, $owner); $cachedSettingsManager = $this->getMockBuilder(CachedSettingsManager::class) - ->setMethods(['invalidateCache']) + ->onlyMethods(['invalidateCache']) ->setConstructorArgs([$settingsManager, $this->getMockBuilder(CacheItemPoolInterface::class)->getMock(), 4711]) ->getMock(); - // Clear the cache - $cachedSettingsManager->expects($this->at(0)) - ->method('invalidateCache') - ->with($this->equalTo($name), $this->equalTo($owner)) - ->willReturn(true); - - // Clear all cache for this owner - $cachedSettingsManager->expects($this->at(1)) + $cachedSettingsManager->expects($this->exactly(2)) ->method('invalidateCache') - ->with($this->equalTo(null), $this->equalTo($owner)) + ->withConsecutive( + // Clear the cache + [$this->equalTo($name), $this->equalTo($owner)], + // Clear all cache for this owner + [$this->equalTo(null), $this->equalTo($owner)], + ) ->willReturn(true); $cachedSettingsManager->set($name, $value, $owner); } - public function testSetMany() + public function testSetMany(): void { $owner = $this->createOwner(); $settings = ['name0' => 'value0', 'name1' => 'value1', 'name2' => 'value2']; @@ -137,9 +139,10 @@ public function testSetMany() $settingsManager->shouldReceive('setMany')->once()->with($settings, $owner); $cachedSettingsManager = $this->getMockBuilder(CachedSettingsManager::class) - ->setMethods(['invalidateCache']) + ->onlyMethods(['invalidateCache']) ->setConstructorArgs([$settingsManager, $this->getMockBuilder(CacheItemPoolInterface::class)->getMock(), 4711]) ->getMock(); + $cachedSettingsManager->expects($this->exactly(4)) ->method('invalidateCache') ->with($this->logicalOr('name0', 'name1', 'name2', null), $owner); @@ -147,7 +150,7 @@ public function testSetMany() $cachedSettingsManager->setMany($settings, $owner); } - public function testClear() + public function testClear(): void { $owner = $this->createOwner(); $name = 'name'; @@ -156,16 +159,16 @@ public function testClear() $settingsManager->shouldReceive('clear')->once()->with($name, $owner); $cachedSettingsManager = $this->getMockBuilder(CachedSettingsManager::class) - ->setMethods(['invalidateCache']) + ->onlyMethods(['invalidateCache']) ->setConstructorArgs([$settingsManager, $this->getMockBuilder(CacheItemPoolInterface::class)->getMock(), 4711]) ->getMock(); - $cachedSettingsManager->expects($this->at(0)) - ->method('invalidateCache') - ->with($this->equalTo($name), $this->equalTo($owner)) - ->willReturn(true); - $cachedSettingsManager->expects($this->at(1)) + + $cachedSettingsManager->expects($this->exactly(2)) ->method('invalidateCache') - ->with($this->equalTo(null), $this->equalTo($owner)) + ->withConsecutive( + [$this->equalTo($name), $this->equalTo($owner)], + [$this->equalTo(null), $this->equalTo($owner)], + ) ->willReturn(true); $cachedSettingsManager->clear($name, $owner); @@ -174,7 +177,7 @@ public function testClear() /** * Make sure we do always return a string, no matter input. */ - public function testGetCacheKey() + public function testGetCacheKey(): void { $name = 'name'; $owner = $this->createOwner(); @@ -192,15 +195,10 @@ public function testGetCacheKey() $this->assertStringContainsString('dmishh_settings', $getCacheKey->invoke($cachedSettingsManager, null, null)); } - /** - * @param string $ownerId - * - * @return \Dmishh\SettingsBundle\Entity\SettingsOwnerInterface - */ - protected function createOwner($ownerId = 'user1') + protected function createOwner(string $ownerId = 'user1'): SettingsOwnerInterface { return \Mockery::mock( - 'Dmishh\SettingsBundle\Entity\SettingsOwnerInterface', + SettingsOwnerInterface::class, ['getSettingIdentifier' => $ownerId] ); } diff --git a/Tests/CompilerPass/PublicServicePass.php b/Tests/CompilerPass/PublicServicePass.php new file mode 100644 index 0000000..dd550c5 --- /dev/null +++ b/Tests/CompilerPass/PublicServicePass.php @@ -0,0 +1,31 @@ + + */ +class PublicServicePass implements CompilerPassInterface +{ + public function __construct(private string $regex = '|.*|') + { + } + + public function process(ContainerBuilder $container): void + { + foreach ($container->getDefinitions() as $id => $definition) { + if (preg_match($this->regex, $id)) { + $definition->setPublic(true); + } + } + + foreach ($container->getAliases() as $id => $alias) { + if (preg_match($this->regex, $id)) { + $alias->setPublic(true); + } + } + } +} diff --git a/Tests/Functional/ServiceInstantiationTest.php b/Tests/Functional/ServiceInstantiationTest.php index 46523b2..66a471b 100644 --- a/Tests/Functional/ServiceInstantiationTest.php +++ b/Tests/Functional/ServiceInstantiationTest.php @@ -7,41 +7,53 @@ use Dmishh\SettingsBundle\Manager\SettingsManagerInterface; use Dmishh\SettingsBundle\Serializer\PhpSerializer; use Dmishh\SettingsBundle\Serializer\SerializerInterface; +use Dmishh\SettingsBundle\Tests\CompilerPass\PublicServicePass; use Doctrine\Bundle\DoctrineBundle\DoctrineBundle; -use Nyholm\BundleTest\BaseBundleTestCase; -use Nyholm\BundleTest\CompilerPass\PublicServicePass; +use Nyholm\BundleTest\TestKernel; +use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase; +use Symfony\Component\HttpKernel\KernelInterface; /** * @internal */ -final class ServiceInstantiationTest extends BaseBundleTestCase +final class ServiceInstantiationTest extends KernelTestCase { - protected function setUp(): void + protected static function getKernelClass(): string { - parent::setUp(); + return TestKernel::class; + } - // Make services public that have an idea that matches a regex - $this->addCompilerPass(new PublicServicePass('|Dmishh.*|')); + protected static function createKernel(array $options = []): KernelInterface + { + /** + * @var TestKernel $kernel + */ + $kernel = parent::createKernel($options); + $kernel->addTestCompilerPass(new PublicServicePass('|Dmishh.*|')); + $kernel->addTestConfig(\dirname(__DIR__).'/Resources/app/config/config.yml'); + $kernel->addTestBundle(DoctrineBundle::class); + $kernel->addTestBundle(DmishhSettingsBundle::class); + $kernel->handleOptions($options); + + return $kernel; } - public function testInitBundle() + public function testInitBundle(): void { - $kernel = $this->createKernel(); - $kernel->addConfigFile(\dirname(__DIR__).'/Resources/app/config/config.yml'); - $kernel->addBundle(DoctrineBundle::class); - $this->bootKernel(); - $container = $this->getContainer(); - - // Test if you services exists - self::assertTrue($container->has(SerializerInterface::class)); + $kernel = self::bootKernel(); + + // Get the container + $container = $kernel->getContainer(); + + self::assertTrue($container->has(SerializerInterface::class), 'Serializer interface not found'); $service = $container->get(SerializerInterface::class); - self::assertInstanceOf(PhpSerializer::class, $service); + self::assertInstanceOf(PhpSerializer::class, $service, 'PHP Serializer not found'); $service = $container->get(SettingsManagerInterface::class); self::assertInstanceOf(SettingsManager::class, $service); } - protected function getBundleClass() + protected function getBundleClass(): string { return DmishhSettingsBundle::class; } diff --git a/Tests/Serializer/CustomSerializer.php b/Tests/Serializer/CustomSerializer.php index b1b5f4d..880cccc 100644 --- a/Tests/Serializer/CustomSerializer.php +++ b/Tests/Serializer/CustomSerializer.php @@ -6,13 +6,13 @@ class CustomSerializer implements SerializerInterface { - public function serialize($data) + public function serialize(mixed $data): string { - return serialize(json_encode($data)); + return serialize(json_encode($data, \JSON_THROW_ON_ERROR)); } - public function unserialize($serialized) + public function unserialize(string $serialized): mixed { - return json_decode(unserialize($serialized), true); + return json_decode(unserialize($serialized), true, 512, \JSON_THROW_ON_ERROR); } } diff --git a/Tests/SerializerTest.php b/Tests/SerializerTest.php index 4bdce52..079d195 100644 --- a/Tests/SerializerTest.php +++ b/Tests/SerializerTest.php @@ -2,35 +2,40 @@ namespace Dmishh\SettingsBundle\Tests; +use Dmishh\SettingsBundle\Exception\UnknownSerializerException; use Dmishh\SettingsBundle\Serializer\SerializerFactory; +use Dmishh\SettingsBundle\Tests\Serializer\CustomSerializer; class SerializerTest extends AbstractTest { - public static $testData = ['abc' => '123', 123, 5.0]; + public static array $testData = ['abc' => '123', 123, 5.0]; - public function testPhpSerializer() + public function testPhpSerializer(): void { $serializer = SerializerFactory::create('php'); + $this->assertEquals(serialize(null), $serializer->serialize(null)); $this->assertEquals(serialize(self::$testData), $serializer->serialize(self::$testData)); $this->assertEquals(self::$testData, $serializer->unserialize($serializer->serialize(self::$testData))); } - public function testJsonSerializer() + public function testJsonSerializer(): void { $serializer = SerializerFactory::create('json'); - $this->assertEquals(json_encode(self::$testData), $serializer->serialize(self::$testData)); + $this->assertEquals(json_encode(null, \JSON_THROW_ON_ERROR), $serializer->serialize(null)); + $this->assertEquals(json_encode(self::$testData, \JSON_THROW_ON_ERROR), $serializer->serialize(self::$testData)); $this->assertEquals(self::$testData, $serializer->unserialize($serializer->serialize(self::$testData))); } - public function testCustomSerializer() + public function testCustomSerializer(): void { - $serializer = SerializerFactory::create('Dmishh\SettingsBundle\Tests\Serializer\CustomSerializer'); + $serializer = SerializerFactory::create(CustomSerializer::class); + $this->assertNull($serializer->unserialize($serializer->serialize(null))); $this->assertEquals(self::$testData, $serializer->unserialize($serializer->serialize(self::$testData))); } - public function testUnknownSerializer() + public function testUnknownSerializer(): void { - $this->expectException('\Dmishh\SettingsBundle\Exception\UnknownSerializerException'); - $serializer = SerializerFactory::create('unknown_serializer'); + $this->expectException(UnknownSerializerException::class); + SerializerFactory::create('unknown_serializer'); } } diff --git a/Tests/ServiceTest.php b/Tests/ServiceTest.php index 1bb2b89..3ae3f20 100644 --- a/Tests/ServiceTest.php +++ b/Tests/ServiceTest.php @@ -20,7 +20,7 @@ protected function getContainerExtensions(): array ]; } - public function testAlias() + public function testAlias(): void { $this->load(); $this->assertContainerBuilderHasAlias(SettingsManagerInterface::class, SettingsManager::class); @@ -29,7 +29,7 @@ public function testAlias() /** * If we provide a cache_service we should use the CachedSettingsManager as default. */ - public function testCacheServiceAlias() + public function testCacheServiceAlias(): void { $this->load(['cache_service' => 'cache']); $this->assertContainerBuilderHasAlias(SettingsManagerInterface::class, CachedSettingsManager::class); diff --git a/Tests/SettingsManagerTest.php b/Tests/SettingsManagerTest.php index 7310f36..fab338c 100644 --- a/Tests/SettingsManagerTest.php +++ b/Tests/SettingsManagerTest.php @@ -2,10 +2,15 @@ namespace Dmishh\SettingsBundle\Tests; +use Dmishh\SettingsBundle\Entity\Setting; +use Dmishh\SettingsBundle\Entity\SettingsOwnerInterface; +use Dmishh\SettingsBundle\Exception\WrongScopeException; use Dmishh\SettingsBundle\Manager\SettingsManager; use Dmishh\SettingsBundle\Manager\SettingsManagerInterface; +use Dmishh\SettingsBundle\Serializer\PhpSerializer; use Dmishh\SettingsBundle\Serializer\SerializerFactory; -use Mockery; +use Doctrine\ORM\EntityManager; +use Doctrine\ORM\EntityRepository; class SettingsManagerTest extends AbstractTest { @@ -31,7 +36,7 @@ public function testGlobalSettingsClear() $this->assertNull($settingsManager->get('some_setting')); } - public function testUserSettingsAccessor() + public function testUserSettingsAccessor(): void { $owner = $this->createOwner(); $settingsManager = $this->createSettingsManager(); @@ -39,7 +44,7 @@ public function testUserSettingsAccessor() $this->assertEquals('VALUE_USER', $settingsManager->get('some_setting', $owner)); } - public function testUserSettingsClear() + public function testUserSettingsClear(): void { $owner = $this->createOwner(); $settingsManager = $this->createSettingsManager(); @@ -48,7 +53,7 @@ public function testUserSettingsClear() $this->assertNull($settingsManager->get('some_setting', $owner)); } - public function testGlobalAndUserSettingsArentIntersect() + public function testGlobalAndUserSettingsArentIntersect(): void { $owner = $this->createOwner(); $settingsManager = $this->createSettingsManager(); @@ -64,7 +69,7 @@ public function testGlobalAndUserSettingsArentIntersect() $this->assertEquals('VALUE_USER_2', $settingsManager->get('some_setting', $owner)); } - public function testUsersSettingsArentIntersect() + public function testUsersSettingsArentIntersect(): void { $owner1 = $this->createOwner(1); $owner2 = $this->createOwner(2); @@ -81,7 +86,7 @@ public function testUsersSettingsArentIntersect() $this->assertEquals('VALUE_USER_2', $settingsManager->get('some_setting', $owner2)); } - public function testPersistence() + public function testPersistence(): void { $settingsManager = $this->createSettingsManager(); $settingsManager->set('some_setting', 'VALUE_GLOBAL'); @@ -96,9 +101,9 @@ public function testPersistence() $this->assertNull($settingsManager->get('some_setting')); } - public function testSetUserSettingInGlobalScopeRaisesException() + public function testSetUserSettingInGlobalScopeRaisesException(): void { - $this->expectException('\Dmishh\SettingsBundle\Exception\WrongScopeException'); + $this->expectException(WrongScopeException::class); $owner = $this->createOwner(); $settingsManager = $this->createSettingsManager(); @@ -108,18 +113,18 @@ public function testSetUserSettingInGlobalScopeRaisesException() $settingsManager->set('some_global_setting', 'VALUE_GLOBAL', $owner); } - public function testGetUserSettingInGlobalScopeRaisesException() + public function testGetUserSettingInGlobalScopeRaisesException(): void { - $this->expectException('\Dmishh\SettingsBundle\Exception\WrongScopeException'); + $this->expectException(WrongScopeException::class); $owner = $this->createOwner(); $settingsManager = $this->createSettingsManager(); $settingsManager->get('some_global_setting', $owner); } - public function testSetGlobalSettingInUserScopeRaisesException() + public function testSetGlobalSettingInUserScopeRaisesException(): void { - $this->expectException('\Dmishh\SettingsBundle\Exception\WrongScopeException'); + $this->expectException(WrongScopeException::class); $owner = $this->createOwner(); $settingsManager = $this->createSettingsManager(); @@ -129,15 +134,15 @@ public function testSetGlobalSettingInUserScopeRaisesException() $settingsManager->set('some_user_setting', 'VALUE_USER'); } - public function testGetGlobalSettingInUserScopeRaisesException() + public function testGetGlobalSettingInUserScopeRaisesException(): void { - $this->expectException('\Dmishh\SettingsBundle\Exception\WrongScopeException'); + $this->expectException(WrongScopeException::class); $settingsManager = $this->createSettingsManager(); $settingsManager->get('some_user_setting'); } - public function testGetAllGlobalSettings() + public function testGetAllGlobalSettings(): void { $settingsManager = $this->createSettingsManager(); $this->assertEquals( @@ -146,7 +151,7 @@ public function testGetAllGlobalSettings() ); } - public function testGetAllUserSettings() + public function testGetAllUserSettings(): void { $owner = $this->createOwner(); $settingsManager = $this->createSettingsManager(); @@ -156,7 +161,7 @@ public function testGetAllUserSettings() ); } - public function testScopeAll() + public function testScopeAll(): void { $owner = $this->createOwner(); $settingsManager = $this->createSettingsManager(); @@ -190,7 +195,7 @@ public function testScopeAll() ); } - public function testValidSerizalizationTypes() + public function testValidSerizalizationTypes(): void { $settingsManager = $this->createSettingsManager([], 'php'); $settingsManager->set('some_setting', 123); @@ -220,20 +225,20 @@ public function testGetSettingWithInvalidSerizalizationType() $settingsManager->get('some_setting'); } - public function testGetDefaultValue() + public function testGetDefaultValue(): void { $user = $this->createOwner(); $settingsManager = $this->createSettingsManager(); - //test default global value + // test default global value $this->assertNull($settingsManager->get('some_setting')); $this->assertEquals('foobar', $settingsManager->get('some_setting', null, 'foobar')); - //test default user value + // test default user value $this->assertNull($settingsManager->get('some_setting')); $this->assertEquals('foobar', $settingsManager->get('some_setting', $user, 'foobar')); - //test when there is an actual value + // test when there is an actual value $settingsManager->set('some_setting', 'value'); $this->assertEquals('value', $settingsManager->get('some_setting', null, 'foobar')); $this->assertEquals('value', $settingsManager->get('some_setting', $user, 'foobar')); @@ -242,20 +247,20 @@ public function testGetDefaultValue() /** * @see https://github.com/dmishh/SettingsBundle/issues/28 */ - public function testFlush() + public function testFlush(): void { $names = ['foo', 'bar', 'baz']; - $settings = 'foobar'; + $settings = ['foobar']; $owner = null; $value = 'settingValue'; $serializedValue = 'sValue'; - $flushMethod = new \ReflectionMethod('Dmishh\SettingsBundle\Manager\SettingsManager', 'flush'); + $flushMethod = new \ReflectionMethod(SettingsManager::class, 'flush'); $flushMethod->setAccessible(true); $serializer = $this - ->getMockBuilder('Dmishh\SettingsBundle\Serializer\PhpSerializer') - ->setMethods(['serialize']) + ->getMockBuilder(PhpSerializer::class) + ->onlyMethods(['serialize']) ->getMock(); $serializer @@ -265,9 +270,9 @@ public function testFlush() ->willReturn($serializedValue); $repo = $this - ->getMockBuilder('Doctrine\ORM\EntityRepository') + ->getMockBuilder(EntityRepository::class) ->disableOriginalConstructor() - ->setMethods(['findBy']) + ->onlyMethods(['findBy']) ->getMock(); $repo->expects($this->once())->method('findBy')->with( @@ -280,26 +285,26 @@ public function testFlush() )->willReturn($settings); $em = $this - ->getMockBuilder('Doctrine\Orm\EntityManager') + ->getMockBuilder(EntityManager::class) ->disableOriginalConstructor() - ->setMethods(['getRepository', 'flush']) + ->onlyMethods(['getRepository', 'flush']) ->getMock(); - $em->expects($this->once())->method('getRepository')->willReturn($repo); - $em->expects($this->once())->method('flush'); + $em->expects(self::once())->method('getRepository')->willReturn($repo); + $em->expects(self::once())->method('flush'); $setting = $this - ->getMockBuilder('Dmishh\SettingsBundle\Entity\Settings') + ->getMockBuilder(Setting::class) ->disableOriginalConstructor() - ->setMethods(['setValue']) + ->onlyMethods(['setValue']) ->getMock(); $setting->expects($this->exactly(\count($names)))->method('setValue')->with($this->equalTo($serializedValue)); $manager = $this - ->getMockBuilder('Dmishh\SettingsBundle\Manager\SettingsManager') + ->getMockBuilder(SettingsManager::class) ->setConstructorArgs([$em, $serializer, []]) - ->setMethods(['findSettingByName', 'get']) + ->onlyMethods(['findSettingByName', 'get']) ->getMock(); $manager @@ -324,7 +329,7 @@ public function testFlush() $flushMethod->invoke($manager, $names, $owner); } - public function testFindSettingByName() + public function testFindSettingByName(): void { $settingsManager = $this->createSettingsManager(); @@ -334,7 +339,7 @@ public function testFindSettingByName() $s4 = $this->createSetting('foo'); $settings = [$s1, $s2, $s3, $s4]; - $method = new \ReflectionMethod('Dmishh\SettingsBundle\Manager\SettingsManager', 'findSettingByName'); + $method = new \ReflectionMethod(SettingsManager::class, 'findSettingByName'); $method->setAccessible(true); $result = $method->invoke($settingsManager, $settings, 'bar'); @@ -349,8 +354,8 @@ public function testFindSettingByName() protected function createSetting($name) { - $s = $this->getMockBuilder('Dmishh\SettingsBundle\Entity\Setting') - ->setMethods(['getName']) + $s = $this->getMockBuilder(Setting::class) + ->onlyMethods(['getName']) ->getMock(); $s->expects($this->any()) @@ -360,20 +365,15 @@ protected function createSetting($name) return $s; } - /** - * @param string $ownerId - * - * @return \Dmishh\SettingsBundle\Entity\SettingsOwnerInterface - */ - protected function createOwner($ownerId = 'user1') + protected function createOwner(string $ownerId = 'user1'): SettingsOwnerInterface { - return Mockery::mock( - 'Dmishh\SettingsBundle\Entity\SettingsOwnerInterface', + return \Mockery::mock( + SettingsOwnerInterface::class, ['getSettingIdentifier' => $ownerId] ); } - protected function createSettingsManager(array $configuration = [], $serialization = 'php') + protected function createSettingsManager(array $configuration = [], $serialization = 'php'): SettingsManager { if (empty($configuration)) { $configuration = [ diff --git a/Twig/SettingsExtension.php b/Twig/SettingsExtension.php index f368899..aae41b4 100644 --- a/Twig/SettingsExtension.php +++ b/Twig/SettingsExtension.php @@ -13,14 +13,14 @@ */ class SettingsExtension extends AbstractExtension { - private $settingsManager; - - public function __construct(SettingsManagerInterface $settingsManager) + public function __construct(private SettingsManagerInterface $settingsManager) { - $this->settingsManager = $settingsManager; } - public function getFunctions() + /** + * @return TwigFunction[] + */ + public function getFunctions(): array { return [ new TwigFunction('get_setting', [$this->settingsManager, 'get']), diff --git a/composer.json b/composer.json index da74745..d9be0fc 100644 --- a/composer.json +++ b/composer.json @@ -8,7 +8,7 @@ "authors": [ { "name": "Dmitriy Scherbina", - "homepage": "http://dmishh.com" + "homepage": "https://dmishh.com/" }, { "name": "Tobias Nyholm", @@ -20,21 +20,21 @@ } ], "require": { - "php": "^7.2|^8.0", - "psr/cache": "^1.0", - "symfony/framework-bundle": "^3.4 || ^4.3 || ^5.0", - "symfony/form": "^3.4 || ^4.3 || ^5.0", - "doctrine/orm": "^2.6.3|^2.7" + "php": "^8.0", + "psr/cache": "^3.0", + "symfony/framework-bundle": "^5.4|^6.0", + "symfony/form": "^5.4 || ^6.0", + "doctrine/orm": "^2.14" }, "require-dev": { - "phpunit/phpunit": "^8.5", - "mockery/mockery": "^1.3", - "doctrine/doctrine-bundle": "^1.12 || ^2.0", + "phpunit/phpunit": "^9.5", + "mockery/mockery": "^1.5", + "doctrine/doctrine-bundle": "^2.8", "polishsymfonycommunity/symfony-mocker-container": "^1.0", - "matthiasnoback/symfony-dependency-injection-test": "^4.1", - "nyholm/symfony-bundle-test": "^1.6", - "symfony/translation": "^4.4 || ^5.0", - "symfony/security-core": "^4.4 || ^5.0", + "matthiasnoback/symfony-dependency-injection-test": "^4.3", + "nyholm/symfony-bundle-test": "^2.0.0", + "symfony/translation": "^5.4 || ^6.0", + "symfony/security-core": "^5.4 || ^6.0", "twig/twig": "^2.0 || ^3.0" }, "autoload": { @@ -42,7 +42,7 @@ }, "extra": { "branch-alias": { - "dev-master": "3.0.x-dev" + "dev-master": "4.0.x-dev" } }, "suggest": { diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 4f684a8..8ec02cc 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -1,192 +1,67 @@ parameters: ignoreErrors: - - message: "#^Call to an undefined method object\\:\\:isGranted\\(\\)\\.$#" + message: "#^Parameter \\#1 \\$settings of method Dmishh\\\\SettingsBundle\\\\Manager\\\\SettingsManagerInterface\\:\\:setMany\\(\\) expects array\\, mixed given\\.$#" count: 1 path: Controller/SettingsController.php - - message: "#^Call to an undefined method object\\:\\:getToken\\(\\)\\.$#" - count: 2 - path: Controller/SettingsController.php - - - - message: "#^Call to an undefined method Symfony\\\\Component\\\\Config\\\\Definition\\\\Builder\\\\TreeBuilder\\:\\:root\\(\\)\\.$#" + message: "#^Call to an undefined method Symfony\\\\Component\\\\Config\\\\Definition\\\\Builder\\\\NodeDefinition\\:\\:children\\(\\)\\.$#" count: 1 path: DependencyInjection/Configuration.php - - - message: "#^Method Dmishh\\\\SettingsBundle\\\\DependencyInjection\\\\DmishhSettingsExtension\\:\\:load\\(\\) has no return typehint specified\\.$#" - count: 1 - path: DependencyInjection/DmishhSettingsExtension.php - - message: "#^Method Dmishh\\\\SettingsBundle\\\\DependencyInjection\\\\DmishhSettingsExtension\\:\\:load\\(\\) has parameter \\$configs with no value type specified in iterable type array\\.$#" count: 1 path: DependencyInjection/DmishhSettingsExtension.php - - message: "#^Method Dmishh\\\\SettingsBundle\\\\Entity\\\\Setting\\:\\:setName\\(\\) has no return typehint specified\\.$#" + message: "#^Property Dmishh\\\\SettingsBundle\\\\Entity\\\\Setting\\:\\:\\$id is never written, only read\\.$#" count: 1 path: Entity/Setting.php - - message: "#^Method Dmishh\\\\SettingsBundle\\\\Entity\\\\Setting\\:\\:setOwnerId\\(\\) has no return typehint specified\\.$#" - count: 1 - path: Entity/Setting.php - - - - message: "#^Method Dmishh\\\\SettingsBundle\\\\Entity\\\\SettingsOwnerInterface\\:\\:getSettingIdentifier\\(\\) has no return typehint specified\\.$#" + message: "#^Method Dmishh\\\\SettingsBundle\\\\Entity\\\\SettingsOwnerInterface\\:\\:getSettingIdentifier\\(\\) has no return type specified\\.$#" count: 1 path: Entity/SettingsOwnerInterface.php - - - message: "#^Method Dmishh\\\\SettingsBundle\\\\Exception\\\\UnknownSerializerException\\:\\:__construct\\(\\) has parameter \\$serializerClass with no typehint specified\\.$#" - count: 1 - path: Exception/UnknownSerializerException.php - - - - message: "#^Method Dmishh\\\\SettingsBundle\\\\Exception\\\\UnknownSettingException\\:\\:__construct\\(\\) has parameter \\$settingName with no typehint specified\\.$#" - count: 1 - path: Exception/UnknownSettingException.php - - - - message: "#^Method Dmishh\\\\SettingsBundle\\\\Exception\\\\WrongScopeException\\:\\:__construct\\(\\) has parameter \\$scope with no typehint specified\\.$#" - count: 1 - path: Exception/WrongScopeException.php - - - - message: "#^Method Dmishh\\\\SettingsBundle\\\\Exception\\\\WrongScopeException\\:\\:__construct\\(\\) has parameter \\$settingName with no typehint specified\\.$#" - count: 1 - path: Exception/WrongScopeException.php - - - - message: "#^Property Dmishh\\\\SettingsBundle\\\\Form\\\\Type\\\\SettingsType\\:\\:\\$settingsConfiguration has no typehint specified\\.$#" - count: 1 - path: Form/Type/SettingsType.php - - message: "#^Method Dmishh\\\\SettingsBundle\\\\Form\\\\Type\\\\SettingsType\\:\\:__construct\\(\\) has parameter \\$settingsConfiguration with no value type specified in iterable type array\\.$#" count: 1 path: Form/Type/SettingsType.php - - message: "#^Method Dmishh\\\\SettingsBundle\\\\Form\\\\Type\\\\SettingsType\\:\\:buildForm\\(\\) has no return typehint specified\\.$#" - count: 1 - path: Form/Type/SettingsType.php - - - - message: "#^Method Dmishh\\\\SettingsBundle\\\\Form\\\\Type\\\\SettingsType\\:\\:buildForm\\(\\) has parameter \\$builder with no value type specified in iterable type Symfony\\\\Component\\\\Form\\\\FormBuilderInterface\\.$#" + message: "#^Parameter \\#2 \\$array of function array_key_exists expects array, mixed given\\.$#" count: 1 path: Form/Type/SettingsType.php - - message: "#^Method Dmishh\\\\SettingsBundle\\\\Form\\\\Type\\\\SettingsType\\:\\:buildForm\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" + message: "#^Parameter \\#2 \\$haystack of function in_array expects array, mixed given\\.$#" count: 1 path: Form/Type/SettingsType.php - - message: "#^Cannot instantiate interface Dmishh\\\\SettingsBundle\\\\Exception\\\\SettingsException\\.$#" + message: "#^Property Dmishh\\\\SettingsBundle\\\\Form\\\\Type\\\\SettingsType\\:\\:\\$settingsConfiguration type has no value type specified in iterable type array\\.$#" count: 1 path: Form/Type/SettingsType.php - - message: "#^Method Dmishh\\\\SettingsBundle\\\\Form\\\\Type\\\\SettingsType\\:\\:configureOptions\\(\\) has no return typehint specified\\.$#" - count: 1 - path: Form/Type/SettingsType.php - - - - message: "#^Method Dmishh\\\\SettingsBundle\\\\Manager\\\\CachedSettingsManager\\:\\:__construct\\(\\) has parameter \\$cacheLifeTime with no typehint specified\\.$#" - count: 1 - path: Manager/CachedSettingsManager.php - - - - message: "#^Method Dmishh\\\\SettingsBundle\\\\Manager\\\\CachedSettingsManager\\:\\:get\\(\\) has no return typehint specified\\.$#" - count: 1 - path: Manager/CachedSettingsManager.php - - - - message: "#^Method Dmishh\\\\SettingsBundle\\\\Manager\\\\CachedSettingsManager\\:\\:all\\(\\) return type has no value type specified in iterable type array\\.$#" - count: 1 - path: Manager/CachedSettingsManager.php - - - - message: "#^Method Dmishh\\\\SettingsBundle\\\\Manager\\\\CachedSettingsManager\\:\\:set\\(\\) has parameter \\$value with no typehint specified\\.$#" - count: 1 - path: Manager/CachedSettingsManager.php - - - - message: "#^Method Dmishh\\\\SettingsBundle\\\\Manager\\\\CachedSettingsManager\\:\\:setMany\\(\\) has parameter \\$settings with no value type specified in iterable type array\\.$#" - count: 1 - path: Manager/CachedSettingsManager.php - - - - message: "#^Method Dmishh\\\\SettingsBundle\\\\Manager\\\\CachedSettingsManager\\:\\:storeInCache\\(\\) has parameter \\$value with no typehint specified\\.$#" + message: "#^Method Dmishh\\\\SettingsBundle\\\\Manager\\\\CachedSettingsManager\\:\\:all\\(\\) should return array\\ but returns mixed\\.$#" count: 1 path: Manager/CachedSettingsManager.php - - - message: "#^Property Dmishh\\\\SettingsBundle\\\\Manager\\\\SettingsManager\\:\\:\\$globalSettings type has no value type specified in iterable type array\\.$#" - count: 1 - path: Manager/SettingsManager.php - - - - message: "#^Property Dmishh\\\\SettingsBundle\\\\Manager\\\\SettingsManager\\:\\:\\$ownerSettings type has no value type specified in iterable type array\\.$#" - count: 1 - path: Manager/SettingsManager.php - - - - message: "#^Property Dmishh\\\\SettingsBundle\\\\Manager\\\\SettingsManager\\:\\:\\$settingsConfiguration type has no value type specified in iterable type array\\.$#" - count: 1 - path: Manager/SettingsManager.php - - message: "#^Method Dmishh\\\\SettingsBundle\\\\Manager\\\\SettingsManager\\:\\:__construct\\(\\) has parameter \\$settingsConfiguration with no value type specified in iterable type array\\.$#" count: 1 path: Manager/SettingsManager.php - - message: "#^Method Dmishh\\\\SettingsBundle\\\\Manager\\\\SettingsManager\\:\\:get\\(\\) has no return typehint specified\\.$#" + message: "#^Method Dmishh\\\\SettingsBundle\\\\Manager\\\\SettingsManager\\:\\:all\\(\\) should return array\\ but returns array\\\\|null\\.$#" count: 1 path: Manager/SettingsManager.php - - message: "#^Method Dmishh\\\\SettingsBundle\\\\Manager\\\\SettingsManager\\:\\:all\\(\\) return type has no value type specified in iterable type array\\.$#" - count: 1 - path: Manager/SettingsManager.php - - - - message: "#^Method Dmishh\\\\SettingsBundle\\\\Manager\\\\SettingsManager\\:\\:set\\(\\) has parameter \\$value with no typehint specified\\.$#" - count: 1 - path: Manager/SettingsManager.php - - - - message: "#^Method Dmishh\\\\SettingsBundle\\\\Manager\\\\SettingsManager\\:\\:setMany\\(\\) has parameter \\$settings with no value type specified in iterable type array\\.$#" - count: 1 - path: Manager/SettingsManager.php - - - - message: "#^Method Dmishh\\\\SettingsBundle\\\\Manager\\\\SettingsManager\\:\\:setWithoutFlush\\(\\) has parameter \\$value with no typehint specified\\.$#" - count: 1 - path: Manager/SettingsManager.php - - - - message: "#^Method Dmishh\\\\SettingsBundle\\\\Manager\\\\SettingsManager\\:\\:flush\\(\\) has parameter \\$names with no value type specified in iterable type array\\.$#" - count: 1 - path: Manager/SettingsManager.php - - - - message: "#^Negated boolean expression is always false\\.$#" - count: 1 - path: Manager/SettingsManager.php - - - - message: "#^PHPDoc tag @return with type Dmishh\\\\SettingsBundle\\\\Manager\\\\SettingsManager is incompatible with native type void\\.$#" - count: 1 - path: Manager/SettingsManager.php - - - - message: "#^Method Dmishh\\\\SettingsBundle\\\\Manager\\\\SettingsManager\\:\\:getSettingsFromRepository\\(\\) return type has no value type specified in iterable type array\\.$#" + message: "#^Offset mixed does not exist on array\\\\>\\|null\\.$#" count: 1 path: Manager/SettingsManager.php @@ -201,57 +76,6 @@ parameters: path: Manager/SettingsManager.php - - message: "#^Method Dmishh\\\\SettingsBundle\\\\Manager\\\\SettingsManagerInterface\\:\\:get\\(\\) has no return typehint specified\\.$#" - count: 1 - path: Manager/SettingsManagerInterface.php - - - - message: "#^Method Dmishh\\\\SettingsBundle\\\\Manager\\\\SettingsManagerInterface\\:\\:all\\(\\) return type has no value type specified in iterable type array\\.$#" - count: 1 - path: Manager/SettingsManagerInterface.php - - - - message: "#^Method Dmishh\\\\SettingsBundle\\\\Manager\\\\SettingsManagerInterface\\:\\:set\\(\\) has parameter \\$value with no typehint specified\\.$#" - count: 1 - path: Manager/SettingsManagerInterface.php - - - - message: "#^Method Dmishh\\\\SettingsBundle\\\\Manager\\\\SettingsManagerInterface\\:\\:setMany\\(\\) has parameter \\$settings with no value type specified in iterable type array\\.$#" - count: 1 - path: Manager/SettingsManagerInterface.php - - - - message: "#^Method Dmishh\\\\SettingsBundle\\\\Serializer\\\\JsonSerializer\\:\\:serialize\\(\\) has parameter \\$data with no typehint specified\\.$#" - count: 1 - path: Serializer/JsonSerializer.php - - - - message: "#^Method Dmishh\\\\SettingsBundle\\\\Serializer\\\\JsonSerializer\\:\\:serialize\\(\\) should return string but returns string\\|false\\.$#" - count: 1 - path: Serializer/JsonSerializer.php - - - - message: "#^Method Dmishh\\\\SettingsBundle\\\\Serializer\\\\JsonSerializer\\:\\:unserialize\\(\\) has no return typehint specified\\.$#" - count: 1 - path: Serializer/JsonSerializer.php - - - - message: "#^Method Dmishh\\\\SettingsBundle\\\\Serializer\\\\PhpSerializer\\:\\:serialize\\(\\) has parameter \\$data with no typehint specified\\.$#" - count: 1 - path: Serializer/PhpSerializer.php - - - - message: "#^Method Dmishh\\\\SettingsBundle\\\\Serializer\\\\PhpSerializer\\:\\:unserialize\\(\\) has no return typehint specified\\.$#" - count: 1 - path: Serializer/PhpSerializer.php - - - - message: "#^Method Dmishh\\\\SettingsBundle\\\\Serializer\\\\SerializerInterface\\:\\:serialize\\(\\) has parameter \\$data with no typehint specified\\.$#" - count: 1 - path: Serializer/SerializerInterface.php - - - - message: "#^Method Dmishh\\\\SettingsBundle\\\\Serializer\\\\SerializerInterface\\:\\:unserialize\\(\\) has no return typehint specified\\.$#" - count: 1 - path: Serializer/SerializerInterface.php - + message: "#^Property Dmishh\\\\SettingsBundle\\\\Manager\\\\SettingsManager\\:\\:\\$ownerSettings \\(array\\\\>\\|null\\) does not accept array\\\\>\\.$#" + count: 2 + path: Manager/SettingsManager.php diff --git a/phpstan.neon.dist b/phpstan.neon.dist index 19eeee9..5937773 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -4,8 +4,11 @@ includes: parameters: level: max inferPrivatePropertyTypeFromConstructor: true + reportUnmatchedIgnoredErrors: false paths: - . - excludes_analyse: + excludePaths: + - tmp-phpqa - vendor - Tests + - .github diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 0d28194..c8fe8d8 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,33 +1,35 @@ - - + + - - - ./Tests - - + + + ./Controller + ./DependencyInjection + ./Entity + ./Exception + ./Form + ./Manager + ./Resources + ./Serializer + ./Twig + + - - - - + + + ./Tests + + + + + + + - - - ./ - - ./Resources - ./Tests - - -