Skip to content

Commit

Permalink
Merge pull request #224 from mimmi20/feature-mezzio
Browse files Browse the repository at this point in the history
Allow use of `TranslatorInterface` as Translator
  • Loading branch information
gsteel committed Jun 12, 2024
2 parents 502be70 + 95a9f2b commit dc3f260
Show file tree
Hide file tree
Showing 9 changed files with 699 additions and 15 deletions.
10 changes: 5 additions & 5 deletions psalm-baseline.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1907,6 +1907,11 @@
<code><![CDATA[gettype($type)]]></code>
</RedundantConditionGivenDocblockType>
</file>
<file src="src/Translator/DummyTranslator.php">
<RedundantCastGivenDocblockType>
<code><![CDATA[(int) $number]]></code>
</RedundantCastGivenDocblockType>
</file>
<file src="src/Translator/TranslatorAwareInterface.php">
<PossiblyUnusedReturnValue>
<code><![CDATA[TranslatorAwareInterface]]></code>
Expand Down Expand Up @@ -2004,10 +2009,6 @@
<code><![CDATA[$configOrContainerInstance]]></code>
<code><![CDATA[$container->get('MvcTranslator')]]></code>
</MixedArgument>
<MixedMethodCall>
<code><![CDATA[get]]></code>
<code><![CDATA[has]]></code>
</MixedMethodCall>
<PossiblyUnusedMethod>
<code><![CDATA[validatePlugin]]></code>
</PossiblyUnusedMethod>
Expand All @@ -2018,7 +2019,6 @@
<code><![CDATA[$container === $this && method_exists($container, 'getServiceLocator')]]></code>
</RedundantCondition>
<RedundantConditionGivenDocblockType>
<code><![CDATA[$container]]></code>
<code><![CDATA[$container->getServiceLocator()]]></code>
</RedundantConditionGivenDocblockType>
</file>
Expand Down
4 changes: 3 additions & 1 deletion src/ConfigProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,14 @@ public function getDependencyConfig()
{
return [
'aliases' => [
'ValidatorManager' => ValidatorPluginManager::class,
Translator\TranslatorInterface::class => Translator\Translator::class,
'ValidatorManager' => ValidatorPluginManager::class,

// Legacy Zend Framework aliases
'Zend\Validator\ValidatorPluginManager' => ValidatorPluginManager::class,
],
'factories' => [
Translator\Translator::class => Translator\TranslatorFactory::class,
ValidatorPluginManager::class => ValidatorPluginManagerFactory::class,
],
];
Expand Down
25 changes: 25 additions & 0 deletions src/Translator/DummyTranslator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?php

declare(strict_types=1);

namespace Laminas\Validator\Translator;

use Laminas\I18n\Translator\TranslatorInterface as I18nTranslatorInterface;

/**
* @internal
*/
final class DummyTranslator implements I18nTranslatorInterface
{
/** @inheritDoc */
public function translate($message, $textDomain = 'default', $locale = null)
{
return $message;
}

/** @inheritDoc */
public function translatePlural($singular, $plural, $number, $textDomain = 'default', $locale = null)
{
return (int) $number === 1 ? $singular : $plural;
}
}
45 changes: 45 additions & 0 deletions src/Translator/Translator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<?php

declare(strict_types=1);

namespace Laminas\Validator\Translator;

use Laminas\I18n\Translator\TranslatorInterface as I18nTranslatorInterface;
use Laminas\Validator\Translator\TranslatorInterface as ValidatorTranslatorInterface;

final class Translator implements
I18nTranslatorInterface,
ValidatorTranslatorInterface
{
public function __construct(private readonly I18nTranslatorInterface $translator)
{
}

/**
* Translate a message using the given text domain and locale
*
* @param string $message
* @param string $textDomain
* @param string $locale
* @return string
*/
public function translate($message, $textDomain = 'default', $locale = null)
{
return $this->translator->translate($message, $textDomain, $locale);
}

/**
* Provide a pluralized translation of the given string using the given text domain and locale
*
* @param string $singular
* @param string $plural
* @param int $number
* @param string $textDomain
* @param string $locale
* @return string
*/
public function translatePlural($singular, $plural, $number, $textDomain = 'default', $locale = null)
{
return $this->translator->translatePlural($singular, $plural, $number, $textDomain, $locale);
}
}
122 changes: 122 additions & 0 deletions src/Translator/TranslatorFactory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
<?php

declare(strict_types=1);

namespace Laminas\Validator\Translator;

use Laminas\I18n\Translator\LoaderPluginManager;
use Laminas\I18n\Translator\Translator as I18nTranslator;
use Laminas\I18n\Translator\TranslatorInterface;
use Laminas\ServiceManager\ServiceManager;
use Psr\Container\ContainerInterface;
use Traversable;

use function array_key_exists;
use function assert;
use function extension_loaded;
use function is_array;

/**
* Overrides the translator factory from the i18n component in order to
* replace it with the bridge class from this namespace.
*/
final class TranslatorFactory
{
public function __invoke(ContainerInterface $container): Translator
{
// Assume that if a user has registered a service for the
// TranslatorInterface, it must be valid
if ($container->has(TranslatorInterface::class)) {
return new Translator($container->get(TranslatorInterface::class));
}

return $this->marshalTranslator($container);
}

/**
* Marshal an Translator.
*
* If configuration exists, will pass it to the I18nTranslator::factory,
* decorating the returned instance in an MvcTranslator.
*
* Otherwise:
*
* - returns an Translator decorating a DummyTranslator instance if
* ext/intl is not loaded.
* - returns an Translator decorating an empty I18nTranslator instance.
*/
private function marshalTranslator(ContainerInterface $container): Translator
{
// Load a translator from configuration, if possible
$translator = $this->marshalTranslatorFromConfig($container);

if ($translator instanceof Translator) {
return $translator;
}

// If ext/intl is not loaded, return a dummy translator
if (! extension_loaded('intl')) {
return new Translator(new DummyTranslator());
}

return new Translator(new I18nTranslator());
}

/**
* Attempt to marshal a translator from configuration.
*
* Returns:
* - an Translator seeded with a DummyTranslator if "translator"
* configuration is available, and evaluates to boolean false.
* - an Translator seed with an I18nTranslator if "translator"
* configuration is available, and is a non-empty array or a Traversable
* instance.
* - null in all other cases, including absence of a configuration service.
*/
private function marshalTranslatorFromConfig(ContainerInterface $container): ?Translator
{
if (! $container->has('config')) {
return null;
}

$config = $container->get('config');

if (! is_array($config) || ! array_key_exists('translator', $config)) {
return null;
}

// 'translator' => false
if ($config['translator'] === false) {
return new Translator(new DummyTranslator());
}

// Empty translator configuration
if (is_array($config['translator']) && empty($config['translator'])) {
return null;
}

// Unusable translator configuration
if (! is_array($config['translator']) && ! $config['translator'] instanceof Traversable) {
return null;
}

// Create translator from configuration
$i18nTranslator = I18nTranslator::factory($config['translator']);

// Inject plugins, if present
if ($container->has('TranslatorPluginManager')) {
$loaderManager = $container->get('TranslatorPluginManager');

assert($loaderManager instanceof LoaderPluginManager);

$i18nTranslator->setPluginManager($loaderManager);
}

// Inject into service manager instances
if ($container instanceof ServiceManager) {
$container->setService(TranslatorInterface::class, $i18nTranslator);
}

return new Translator($i18nTranslator);
}
}
21 changes: 17 additions & 4 deletions src/ValidatorPluginManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace Laminas\Validator;

use Laminas\I18n\Translator\TranslatorInterface;
use Laminas\I18n\Validator as I18nValidator;
use Laminas\ServiceManager\AbstractPluginManager;
use Laminas\ServiceManager\Exception\InvalidServiceException;
Expand Down Expand Up @@ -584,15 +585,27 @@ public function injectTranslator($first, $second)
$validator = $first;
}

if (! $validator instanceof Translator\TranslatorAwareInterface) {
return;
}

// V2 means we pull it from the parent container
if ($container === $this && method_exists($container, 'getServiceLocator') && $container->getServiceLocator()) {
$container = $container->getServiceLocator();
}

if ($validator instanceof Translator\TranslatorAwareInterface) {
if ($container && $container->has('MvcTranslator')) {
$validator->setTranslator($container->get('MvcTranslator'));
}
if (! $container instanceof ContainerInterface) {
return;
}

if ($container->has('MvcTranslator')) {
$validator->setTranslator($container->get('MvcTranslator'));

return;
}

if ($container->has(TranslatorInterface::class)) {
$validator->setTranslator($container->get(Translator\TranslatorInterface::class));
}
}

Expand Down
Loading

0 comments on commit dc3f260

Please sign in to comment.