From 9006ed0b6834220aaf8bd77e6e26e898b977b742 Mon Sep 17 00:00:00 2001 From: Ben Roberts Date: Wed, 10 Oct 2018 20:54:57 -0600 Subject: [PATCH 1/3] Update composer.json --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 4becb48..0aa2996 100644 --- a/composer.json +++ b/composer.json @@ -17,7 +17,7 @@ ], "require": { "php": ">=5.5.9", - "symfony/framework-bundle": "~2.8|~3.0" + "symfony/framework-bundle": "~2.8|~3.0|~4.0" }, "autoload": { "psr-4": { "Lexik\\Bundle\\CurrencyBundle\\": "" } From 3058642573642d88be5673ef178cb996d8cd797e Mon Sep 17 00:00:00 2001 From: Ben Roberts Date: Thu, 11 Oct 2018 07:24:54 -0600 Subject: [PATCH 2/3] Update adapters.xml --- Resources/config/adapters.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Resources/config/adapters.xml b/Resources/config/adapters.xml index 44446a4..5e58ac5 100644 --- a/Resources/config/adapters.xml +++ b/Resources/config/adapters.xml @@ -17,7 +17,7 @@ - + From 5ffe05e6429ece0a735e336543fe7675194fa4df Mon Sep 17 00:00:00 2001 From: Ben Roberts Date: Sun, 26 Sep 2021 07:33:41 +0200 Subject: [PATCH 3/3] Refactor for PHP 8.x and Symfony 5.x Replaced PHPDoc comments with typed parameters and return types. Removed superfluous comments. Upgraded to PHPUnit 9.5 Added required PHP extensions to composer.json. Fixed all static analysis issues to PHPStan level 9. Formatted code to PSR-12 using Easy Coding Standard. Generally brought the package up to modern PHP standards. --- Adapter/AbstractCurrencyAdapter.php | 91 +++++++------------ Adapter/AdapterCollector.php | 22 ++--- Adapter/AdapterFactory.php | 87 +++++++----------- Adapter/DoctrineCurrencyAdapter.php | 60 ++++-------- Adapter/EcbCurrencyAdapter.php | 46 +++++----- Adapter/OerCurrencyAdapter.php | 81 ++++++----------- Adapter/YahooCurrencyAdapter.php | 67 +++++--------- Command/ImportCurrencyCommand.php | 55 +++++++---- Currency/Converter.php | 59 ++++-------- Currency/ConverterInterface.php | 15 +-- Currency/Formatter.php | 39 +++----- Currency/FormatterInterface.php | 18 +--- DependencyInjection/Compiler/AdapterPass.php | 10 +- DependencyInjection/Configuration.php | 14 +-- .../LexikCurrencyExtension.php | 10 +- Entity/Currency.php | 55 ++--------- EventListener/LocaleListener.php | 33 +++---- Exception/CurrencyNotFoundException.php | 6 +- LexikCurrencyBundle.php | 2 +- Tests/Fixtures/CurrencyData.php | 21 ++--- Tests/Unit/Adapter/AdapterFactoryTest.php | 39 ++++---- Tests/Unit/BaseUnitTestCase.php | 79 +++++++--------- Tests/Unit/Converter/ConverterTest.php | 32 +++---- .../Twig/Extension/CurrencyExtensionTest.php | 30 +++--- Tests/bootstrap.php | 6 +- Twig/Extension/CurrencyExtension.php | 90 ++++++------------ composer.json | 21 ++++- ecs.php | 24 +++++ phpstan.neon | 19 ++++ phpunit.xml.dist | 42 ++++----- rector.php | 38 ++++++++ 31 files changed, 514 insertions(+), 697 deletions(-) create mode 100644 ecs.php create mode 100644 phpstan.neon create mode 100644 rector.php diff --git a/Adapter/AbstractCurrencyAdapter.php b/Adapter/AbstractCurrencyAdapter.php index b0a3f4f..2ce6025 100644 --- a/Adapter/AbstractCurrencyAdapter.php +++ b/Adapter/AbstractCurrencyAdapter.php @@ -2,114 +2,91 @@ namespace Lexik\Bundle\CurrencyBundle\Adapter; +use ArrayIterator; +use Lexik\Bundle\CurrencyBundle\Entity\Currency; + /** * * @author Cédric Girard * @author Yoann Aparici + * @extends ArrayIterator */ -abstract class AbstractCurrencyAdapter extends \ArrayIterator +abstract class AbstractCurrencyAdapter extends ArrayIterator { - /** - * @var string - */ - protected $defaultCurrency; + protected string $defaultCurrency; /** - * @var array + * @var array */ - protected $managedCurrencies = array(); + protected array $managedCurrencies = []; /** - * @var string + * @var class-string */ - protected $currencyClass; + protected string $currencyClass; - /** - * Set default currency - * - * @param string $defaultCurrency - */ - public function setDefaultCurrency($defaultCurrency) + public function setDefaultCurrency(string $defaultCurrency): void { $this->defaultCurrency = $defaultCurrency; } - /** - * Get default currency - * - * @return string - */ - public function getDefaultCurrency() + public function getDefaultCurrency(): string { return $this->defaultCurrency; } /** - * Get managedCurrencies - * - * @param array $currencies + * @param array $currencies */ - public function setManagedCurrencies($currencies) + public function setManagedCurrencies(array $currencies): void { $this->managedCurrencies = $currencies; } /** - * Get managedCurrencies - * - * @return array + * @return array */ - public function getManagedCurrencies() + public function getManagedCurrencies(): array { return $this->managedCurrencies; } /** - * Get managedCurrencies - * - * @return array + * @param class-string $currencyClass */ - public function setCurrencyClass($currencyClass) + public function setCurrencyClass(string $currencyClass): void { - return $this->currencyClass = $currencyClass; + $this->currencyClass = $currencyClass; } /** - * Set object - * - * @param mixed $index - * @param Currency $newval + * @param string $key + * @param Currency $value */ - public function offsetSet($index, $newval) + public function offsetSet($key, $value): void { - if (!$newval instanceof $this->currencyClass) { - throw new \InvalidArgumentException(sprintf('$newval must be an instance of Currency, instance of "%s" given', get_class($newval))); + if (!$value instanceof $this->currencyClass) { + throw new \InvalidArgumentException(sprintf('$newval must be an instance of Currency, instance of "%s" given', $value::class)); } - parent::offsetSet($index, $newval); + parent::offsetSet($key, $value); } /** - * Append a value - * * @param Currency $value */ - public function append($value) + public function append($value): void { if (!$value instanceof $this->currencyClass) { - throw new \InvalidArgumentException(sprintf('$newval must be an instance of Currency, instance of "%s" given', get_class($value))); + throw new \InvalidArgumentException(sprintf('$newval must be an instance of Currency, instance of "%s" given', $value::class)); } parent::append($value); } - /** - * Convert all - * - * @param mixed $rate - */ - protected function convertAll($rate) + protected function convertAll(float $rate): void { + /** @var Currency $currency */ foreach ($this as $currency) { $currency->convert($rate); } @@ -119,13 +96,11 @@ protected function convertAll($rate) * This method is used by the constructor * to attach all currencies. */ - abstract public function attachAll(); + abstract public function attachAll(): void; /** - * Get identier value for the adapter must be unique + * Get identifier value for the adapter must be unique * for all the project - * - * @return string */ - abstract protected function getIdentifier(); -} \ No newline at end of file + abstract public function getIdentifier(): string; +} diff --git a/Adapter/AdapterCollector.php b/Adapter/AdapterCollector.php index ca83cfa..6d9f43c 100644 --- a/Adapter/AdapterCollector.php +++ b/Adapter/AdapterCollector.php @@ -9,26 +9,17 @@ */ final class AdapterCollector { - private $elements = array(); - /** - * Add an adapter - * - * @param mixed $key - * @param AbstractCurrencyAdapter $adapter + * @var array */ - public function add(AbstractCurrencyAdapter $adapter) + private array $elements = []; + + public function add(AbstractCurrencyAdapter $adapter): void { $this->elements[$adapter->getIdentifier()] = $adapter; } - /** - * Get adapter - * - * @param mixed $key - * @return AbstractCurrencyAdapter - */ - public function get($key) + public function get(string $key): AbstractCurrencyAdapter { if (!isset($this->elements[$key])) { throw new InvalidArgumentException('Adapter does not exist'); @@ -36,5 +27,4 @@ public function get($key) return $this->elements[$key]; } - -} \ No newline at end of file +} diff --git a/Adapter/AdapterFactory.php b/Adapter/AdapterFactory.php index 025ff9f..ec5fb63 100644 --- a/Adapter/AdapterFactory.php +++ b/Adapter/AdapterFactory.php @@ -2,55 +2,44 @@ namespace Lexik\Bundle\CurrencyBundle\Adapter; -use Doctrine\ORM\EntityManager; use Doctrine\Bundle\DoctrineBundle\Registry; +use Doctrine\ORM\EntityManager; /** - * This class is used to create DoctrineCurrencyAdapter - * * @author Yoann Aparici * @author Cédric Girard */ class AdapterFactory { /** - * @var EntityManager - */ - protected $doctrine; - - /** - * @var array + * @var array{default: string, managed: array} */ - private $currencies; + private array $currencies; /** - * @var string + * @param Registry $doctrine + * @param string $defaultCurrency + * @param array $availableCurrencies + * @param class-string $currencyClass */ - private $currencyClass; - - /** - * __construct - * - * @param EntityManager $em - */ - public function __construct(Registry $doctrine, $defaultCurrency, $availableCurrencies, $currencyClass) - { - $this->doctrine = $doctrine; - - $this->currencies = array(); - $this->currencies['default'] = $defaultCurrency; - $this->currencies['managed'] = $availableCurrencies; - $this->currencyClass = $currencyClass; + public function __construct( + protected Registry $doctrine, + string $defaultCurrency, + array $availableCurrencies, + private string $currencyClass + ) { + $this->currencies = [ + 'default' => $defaultCurrency, + 'managed' => $availableCurrencies + ]; } /** - * Create an adaper from the given class. - * - * @param string $adapterClass - * @return Lexik\Bundle\CurrencyBundle\Adapter\AbstractCurrencyAdapter + * @param class-string $adapterClass */ - public function create($adapterClass) + public function create(string $adapterClass): AbstractCurrencyAdapter { + /** @var AbstractCurrencyAdapter $adapter */ $adapter = new $adapterClass(); $adapter->setDefaultCurrency($this->currencies['default']); $adapter->setManagedCurrencies($this->currencies['managed']); @@ -60,17 +49,17 @@ public function create($adapterClass) } /** - * Create a DoctrineCurrencyAdapter. - * - * @return Lexik\Bundle\CurrencyBundle\Adapter\DoctrineCurrencyAdapter + * @param ?class-string $adapterClass */ - public function createDoctrineAdapter($adapterClass = null, $entityManagerName = null) + public function createDoctrineAdapter(string $adapterClass = null, string $entityManagerName = null): AbstractCurrencyAdapter { if (null == $adapterClass) { - $adapterClass = 'Lexik\Bundle\CurrencyBundle\Adapter\DoctrineCurrencyAdapter'; + $adapterClass = DoctrineCurrencyAdapter::class; } + /** @var DoctrineCurrencyAdapter $adapter */ $adapter = $this->create($adapterClass); + /** @var EntityManager $em */ $em = $this->doctrine->getManager($entityManagerName); $adapter->setManager($em); @@ -78,44 +67,38 @@ public function createDoctrineAdapter($adapterClass = null, $entityManagerName = } /** - * Create an EcbCurrencyAdapter. - * - * @return Lexik\Bundle\CurrencyBundle\Adapter\EcbCurrencyAdapter + * @param ?class-string $adapterClass */ - public function createEcbAdapter($adapterClass = null) + public function createEcbAdapter(string $adapterClass = null): AbstractCurrencyAdapter { if (null == $adapterClass) { - $adapterClass = 'Lexik\Bundle\CurrencyBundle\Adapter\EcbCurrencyAdapter'; + $adapterClass = EcbCurrencyAdapter::class; } return $this->create($adapterClass); } /** - * Create an OerCurrencyAdapter. - * - * @return Lexik\Bundle\CurrencyBundle\Adapter\OerCurrencyAdapter + * @param ?class-string $adapterClass */ - public function createOerAdapter($adapterClass = null) + public function createOerAdapter(string $adapterClass = null): AbstractCurrencyAdapter { if (null == $adapterClass) { - $adapterClass = 'Lexik\Bundle\CurrencyBundle\Adapter\OerCurrencyAdapter'; + $adapterClass = OerCurrencyAdapter::class; } return $this->create($adapterClass); } /** - * Create an YahooCurrencyAdapter. - * - * @return Lexik\Bundle\CurrencyBundle\Adapter\YahooCurrencyAdapter + * @param ?class-string $adapterClass */ - public function createYahooAdapter($adapterClass = null) + public function createYahooAdapter(string $adapterClass = null): AbstractCurrencyAdapter { if (null == $adapterClass) { - $adapterClass = 'Lexik\Bundle\CurrencyBundle\Adapter\YahooCurrencyAdapter'; + $adapterClass = YahooCurrencyAdapter::class; } return $this->create($adapterClass); } -} \ No newline at end of file +} diff --git a/Adapter/DoctrineCurrencyAdapter.php b/Adapter/DoctrineCurrencyAdapter.php index fcaf84a..2c8e13a 100644 --- a/Adapter/DoctrineCurrencyAdapter.php +++ b/Adapter/DoctrineCurrencyAdapter.php @@ -3,6 +3,9 @@ namespace Lexik\Bundle\CurrencyBundle\Adapter; use Doctrine\ORM\EntityManager; +use Doctrine\ORM\EntityManagerInterface; +use Exception; +use Lexik\Bundle\CurrencyBundle\Entity\Currency; /** * @author Yoann Aparici @@ -10,46 +13,21 @@ */ class DoctrineCurrencyAdapter extends AbstractCurrencyAdapter { - /** - * @var EntityManager - */ - private $manager; + private EntityManager $manager; - /** - * @var bool - */ - private $initialized = false; + private bool $initialized = false; - /** - * {@inheritdoc} - */ - public function attachAll() + public function attachAll(): void { // nothing here } - /** - * Return identifier - * - * @return string - */ - public function getIdentifier() + public function getIdentifier(): string { return 'doctrine'; } - /** - * @param EntityManager $manager - */ - public function setManager(EntityManager $manager) - { - $this->manager = $manager; - } - - /** - * {@inheritdoc} - */ - public function offsetExists($index) + public function offsetExists($index): bool { if (!$this->isInitialized()) { $this->initialize(); @@ -58,30 +36,29 @@ public function offsetExists($index) return parent::offsetExists($index); } - /** - * {@inheritdoc} - */ - public function offsetGet($index) + public function setManager(EntityManager $manager): void + { + $this->manager = $manager; + } + + public function offsetGet(mixed $key): mixed { if (!$this->isInitialized()) { $this->initialize(); } - return parent::offsetGet($index); + return parent::offsetGet($key); } - /** - * @return bool - */ - private function isInitialized() + private function isInitialized(): bool { return $this->initialized; } /** - * @throws \Exception + * @throws Exception */ - private function initialize() + private function initialize(): void { if (!isset($this->manager)) { throw new \RuntimeException('No ObjectManager set on DoctrineCurrencyAdapter.'); @@ -91,6 +68,7 @@ private function initialize() ->getRepository($this->currencyClass) ->findAll(); + /** @var Currency $currency */ foreach ($currencies as $currency) { $this[$currency->getCode()] = $currency; } diff --git a/Adapter/EcbCurrencyAdapter.php b/Adapter/EcbCurrencyAdapter.php index a43fa4a..f519a30 100644 --- a/Adapter/EcbCurrencyAdapter.php +++ b/Adapter/EcbCurrencyAdapter.php @@ -2,36 +2,28 @@ namespace Lexik\Bundle\CurrencyBundle\Adapter; +use Lexik\Bundle\CurrencyBundle\Entity\Currency; +use SimpleXMLElement; + /** * @author Cédric Girard * @author Yoann Aparici */ class EcbCurrencyAdapter extends AbstractCurrencyAdapter { - /** - * @var string - */ - private $ecbUrl; - - /** - * Set the ECB url. - * - * @param string $url - */ - public function setEcbUrl($url) + private string $ecbUrl; + + public function setEcbUrl(string $url): void { $this->ecbUrl = $url; } - /** - * Init object storage - */ - public function attachAll() + public function attachAll(): void { $defaultRate = 1; - // Add euro - $euro = new $this->currencyClass; + /** @var Currency $euro */ + $euro = new $this->currencyClass(); $euro->setCode('EUR'); $euro->setRate(1); @@ -40,16 +32,23 @@ public function attachAll() // Get other currencies $xml = @simplexml_load_file($this->ecbUrl); - if ($xml instanceof \SimpleXMLElement) { + if ($xml instanceof SimpleXMLElement) { + /** @var array $data */ $data = $xml->xpath('//gesmes:Envelope/*[3]/*'); + /** @var SimpleXMLElement $child */ foreach ($data[0]->children() as $child) { - $code = (string) $child->attributes()->currency; + + /** @var SimpleXMLElement $row */ + $row = $child->attributes(); + + $code = (string) $row->currency; if (in_array($code, $this->managedCurrencies)) { - $currency = new $this->currencyClass; + /** @var Currency $currency */ + $currency = new $this->currencyClass(); $currency->setCode($code); - $currency->setRate((string) $child->attributes()->rate); + $currency->setRate((float) $row->rate); $this[$currency->getCode()] = $currency; } @@ -63,10 +62,7 @@ public function attachAll() } } - /** - * {@inheritdoc} - */ - public function getIdentifier() + public function getIdentifier(): string { return 'ecb'; } diff --git a/Adapter/OerCurrencyAdapter.php b/Adapter/OerCurrencyAdapter.php index 0754a86..b48444b 100644 --- a/Adapter/OerCurrencyAdapter.php +++ b/Adapter/OerCurrencyAdapter.php @@ -2,6 +2,7 @@ namespace Lexik\Bundle\CurrencyBundle\Adapter; +use Lexik\Bundle\CurrencyBundle\Entity\Currency; use Lexik\Bundle\CurrencyBundle\Exception\CurrencyNotFoundException; /** @@ -12,83 +13,59 @@ */ class OerCurrencyAdapter extends AbstractCurrencyAdapter { - /** - * @var string - */ - private $url; + private string $url; - /** - * @var string - */ - private $appId; + private ?string $appId = null; - /** - * Set the OER url. - * - * @param string $url - */ - public function setOerUrl($url) + public function setOerUrl(string $url): void { $this->url = $url; } - /** - * Sets the app-id - * - * @param string $appId - */ - public function setOerAppId($appId) - { - $this->appId = $appId; - } - + public function setOerAppId(?string $appId): void + { + $this->appId = $appId; + } - /** - * Init object storage - */ - public function attachAll() + public function attachAll(): void { // Get other currencies $data = @file_get_contents($this->getUrl()); - $data = @json_decode($data, true); + $data = @json_decode((string) $data, true, 512, JSON_THROW_ON_ERROR); - if($data && is_array($data) && isset($data['rates'])) - { - $data = $data['rates']; - foreach($this->managedCurrencies as $code) - { - if(isset($data[$code])) - { - $currency = new $this->currencyClass; + if ($data && is_array($data) && isset($data['rates'])) { + $data = $data['rates']; + foreach ($this->managedCurrencies as $code) { + if (isset($data[$code])) { + /** @var Currency $currency */ + $currency = new $this->currencyClass(); $currency->setCode($code); $currency->setRate($data[$code]); $this[$code] = $currency; - } - } + } + } if (isset($this[$this->defaultCurrency])) { $defaultRate = $this[$this->defaultCurrency]->getRate(); + } else { + throw new CurrencyNotFoundException("Your default currency is not supported by Oer provider"); } - else - throw new CurrencyNotFoundException("Your default currency is not supported by Oer provider"); $this->convertAll($defaultRate); } } - public function getUrl() - { - if(!$this->appId) - throw new \InvalidArgumentException('OER_APP_ID must be set in order to use OerCurrencyAdapter'); - return sprintf("%s?app_id=%s", $this->url, $this->appId); - } + public function getUrl(): string + { + if ($this->appId === '' || $this->appId === '0') { + throw new \InvalidArgumentException('OER_APP_ID must be set in order to use OerCurrencyAdapter'); + } + return sprintf("%s?app_id=%s", $this->url, $this->appId); + } - /** - * {@inheritdoc} - */ - public function getIdentifier() + public function getIdentifier(): string { return 'oer'; } -} \ No newline at end of file +} diff --git a/Adapter/YahooCurrencyAdapter.php b/Adapter/YahooCurrencyAdapter.php index 5354574..8efcb14 100644 --- a/Adapter/YahooCurrencyAdapter.php +++ b/Adapter/YahooCurrencyAdapter.php @@ -2,6 +2,7 @@ namespace Lexik\Bundle\CurrencyBundle\Adapter; +use Lexik\Bundle\CurrencyBundle\Entity\Currency; use Lexik\Bundle\CurrencyBundle\Exception\CurrencyNotFoundException; /** @@ -11,31 +12,19 @@ */ class YahooCurrencyAdapter extends AbstractCurrencyAdapter { - /** - * @var string - */ - private $yahooUrl; + private string $yahooUrl; /** - * @var array + * @var array */ - private $currencyCodes = array(); + private array $currencyCodes = []; - - /** - * Set the Yahoo! url. - * - * @param string $url - */ - public function setYahooUrl($url) + public function setYahooUrl(string $url): void { $this->yahooUrl = $url; } - /** - * Init object storage - */ - public function attachAll() + public function attachAll(): void { foreach ($this->managedCurrencies as $managedCurrency) { $this->addCurrency($managedCurrency); @@ -44,7 +33,8 @@ public function attachAll() $defaultRate = 1; // Add default currency (euro in this example) - $euro = new $this->currencyClass; + /** @var Currency $euro */ + $euro = new $this->currencyClass(); $euro->setCode('EUR'); $euro->setRate($defaultRate); @@ -52,20 +42,14 @@ public function attachAll() // Build YQL query $strCodes = ''; - foreach ($this->currencyCodes as $index=>$currencyCode) { - $strCodes .= "'EUR".$currencyCode."'"; + foreach ($this->currencyCodes as $index => $currencyCode) { + $strCodes .= "'EUR" . $currencyCode . "'"; if ($index != count($this->currencyCodes) - 1) { $strCodes .= ", "; } } - $yqlQuery = "select id,Rate from yahoo.finance.xchange where pair in (".$strCodes.")"; - - $args = array( - 'q' => $yqlQuery, - 'format' => "json", - 'env' => "store://datatables.org/alltableswithkeys", - ); + $yqlQuery = "select id,Rate from yahoo.finance.xchange where pair in (" . $strCodes . ")"; $yqlQueryURL = $this->yahooUrl . "?q=" . urlencode($yqlQuery) @@ -74,19 +58,19 @@ public function attachAll() $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $yqlQueryURL); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); $json = curl_exec($ch); // Convert JSON response to PHP object - $data = json_decode($json); - $results = $data->query->results->rate; + $data = json_decode((string) $json, null, 512, JSON_THROW_ON_ERROR); + $results = $data->query->results->rate; // @phpstan-ignore-line // Check if query was okay and result is given if (is_null($results)) { - new \RuntimeException('YQL query failed!'); + throw new \RuntimeException('YQL query failed!'); } - $currencies = array(); + $currencies = []; foreach ($results as $row) { $code = substr($row->id, 3); @@ -97,7 +81,8 @@ public function attachAll() foreach ($currencies as $code => $rate) { if (in_array($code, $this->managedCurrencies)) { // you can check if the currency is in the managed currencies - $currency = new $this->currencyClass; + /** @var Currency $currency */ + $currency = new $this->currencyClass(); $currency->setCode($code); $currency->setRate($rate); @@ -114,21 +99,13 @@ public function attachAll() $this->convertAll($defaultRate); } - /** - * {@inheritdoc} - */ - public function getIdentifier() + public function getIdentifier(): string { return 'yahoo'; } - /** - * Add currency to the query - * - * @param $code - */ - private function addCurrency($code) { + private function addCurrency(string $code): void + { $this->currencyCodes[] = $code; } - -} \ No newline at end of file +} diff --git a/Command/ImportCurrencyCommand.php b/Command/ImportCurrencyCommand.php index 0d44380..0e7db3e 100644 --- a/Command/ImportCurrencyCommand.php +++ b/Command/ImportCurrencyCommand.php @@ -2,7 +2,12 @@ namespace Lexik\Bundle\CurrencyBundle\Command; -use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand; +use Doctrine\Persistence\ManagerRegistry; +use Lexik\Bundle\CurrencyBundle\Adapter\AbstractCurrencyAdapter; +use Lexik\Bundle\CurrencyBundle\Adapter\AdapterCollector; +use Lexik\Bundle\CurrencyBundle\Adapter\DoctrineCurrencyAdapter; +use Lexik\Bundle\CurrencyBundle\Entity\Currency; +use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; @@ -12,12 +17,23 @@ * @author Cédric Girard * @author Yoann Aparici */ -class ImportCurrencyCommand extends ContainerAwareCommand +class ImportCurrencyCommand extends Command { + /** + * @param class-string $currencyClass + */ + public function __construct( + private ManagerRegistry $managerRegistry, + private AdapterCollector $adapterCollector, + private string $currencyClass + ) { + parent::__construct(); + } + /** * {@inheritdoc} */ - protected function configure() + protected function configure(): void { $this ->setName('lexik:currency:import') @@ -30,37 +46,44 @@ protected function configure() /** * {@inheritdoc} */ - protected function execute(InputInterface $input, OutputInterface $output) + protected function execute(InputInterface $input, OutputInterface $output): int { - $adapter = $this->getContainer() - ->get('lexik_currency.adapter_collector') - ->get($input->getArgument('adapter')); + /** @var string $adapterName */ + $adapterName = $input->getArgument('adapter'); + $adapter = $this->adapterCollector->get($adapterName); $adapter->attachAll(); - // Persist currencies - $entityManagerName = $input->getOption('em'); - $em = $this->getContainer()->get('doctrine')->getManager($entityManagerName); + /** @var string $managerName */ + $managerName = $input->getOption('em'); + $entityManagerName = $managerName; + $em = $this->managerRegistry->getManager($entityManagerName); - $repository = $em->getRepository($this->getContainer()->getParameter('lexik_currency.currency_class')); + $repository = $em->getRepository($this->currencyClass); foreach ($adapter as $value) { - // Check if already exist - $currency = $repository->findOneBy(array( + $currency = $repository->findOneBy([ 'code' => $value->getCode(), - )); + ]); + /** @var Currency|null $currency */ if (!$currency) { $currency = $value; $em->persist($currency); - $output->writeln(sprintf('Add: %s = %s', $currency->getCode(), $currency->getRate())); + $output->writeln( + sprintf('Add: %s = %s', $currency->getCode(), $currency->getRate()) + ); } else { $currency->setRate($value->getRate()); - $output->writeln(sprintf('Update: %s = %s', $currency->getCode(), $currency->getRate())); + $output->writeln( + sprintf('Update: %s = %s', $currency->getCode(), $currency->getRate()) + ); } } $em->flush(); + + return Command::SUCCESS; } } diff --git a/Currency/Converter.php b/Currency/Converter.php index 75594f5..7bc3102 100644 --- a/Currency/Converter.php +++ b/Currency/Converter.php @@ -6,53 +6,36 @@ use Lexik\Bundle\CurrencyBundle\Exception\CurrencyNotFoundException; /** - * Currency converter. - * * @author Cédric Girard * @author Yoann Aparici */ class Converter implements ConverterInterface { - /** - * @var AbstractCurrencyAdapter - */ - protected $adapter; + private int $roundMode; /** - * @var integer - */ - protected $precision; - - /** - * @var string - */ - protected $roundMode; - - /** - * Construct. - * - * @param AbstractCurrencyAdapter $adapter - * @param integer $precision - * @param string $roundMode * @throws \InvalidArgumentException */ - public function __construct(AbstractCurrencyAdapter $adapter, $precision = 2, $roundMode = 'up') + public function __construct(protected AbstractCurrencyAdapter $adapter, protected int $precision = 2, string $roundMode = 'up') { - $allowedModes = array('up', 'down', 'even', 'odd'); + $allowedModes = ['up', 'down', 'even', 'odd']; if (!in_array($roundMode, $allowedModes)) { - throw new \InvalidArgumentException(sprintf('Invalid round mode "%s", please use one of the following values: %s', $roundMode, implode(', ', $allowedModes))); + throw new \InvalidArgumentException( + sprintf( + 'Invalid round mode "%s", please use one of the following values: %s', + $roundMode, + implode(', ', $allowedModes) + ) + ); } - $this->adapter = $adapter; - $this->precision = $precision; - $this->roundMode = constant(sprintf('PHP_ROUND_HALF_%s', strtoupper($roundMode))); + /** @var int $roundingConstant */ + $roundingConstant = constant(sprintf('PHP_ROUND_HALF_%s', strtoupper($roundMode))); + $this->roundMode = $roundingConstant; } - /** - * {@inheritDoc} - */ - public function convert($value, $targetCurrency, $round = true, $valueCurrency = null) + public function convert(float $value, string $targetCurrency, bool $round = true, string $valueCurrency = null): float { if (!isset($this->adapter[$targetCurrency])) { throw new CurrencyNotFoundException($targetCurrency); @@ -66,26 +49,22 @@ public function convert($value, $targetCurrency, $round = true, $valueCurrency = throw new CurrencyNotFoundException($valueCurrency); } - if ($targetCurrency != $valueCurrency) { - if ($this->getDefaultCurrency() == $valueCurrency) { + if ($targetCurrency !== $valueCurrency) { + if ($this->getDefaultCurrency() === $valueCurrency) { $value *= $this->adapter[$targetCurrency]->getRate(); - } else { $value /= $this->adapter[$valueCurrency]->getRate(); // value in the default currency - if ($this->getDefaultCurrency() != $targetCurrency) { + if ($this->getDefaultCurrency() !== $targetCurrency) { $value *= $this->adapter[$targetCurrency]->getRate(); } } } - return $round ? round($value, $this->precision, $this->roundMode) : $value; + return $round ? round($value, $this->precision, (int) $this->roundMode) : $value; } - /** - * {@inheritDoc} - */ - public function getDefaultCurrency() + public function getDefaultCurrency(): string { return $this->adapter->getDefaultCurrency(); } diff --git a/Currency/ConverterInterface.php b/Currency/ConverterInterface.php index 6c2682b..15a9524 100644 --- a/Currency/ConverterInterface.php +++ b/Currency/ConverterInterface.php @@ -9,19 +9,8 @@ interface ConverterInterface { /** * Convert from default currency to another. - * - * @param float $value - * @param string $targetCurrency - * @param boolean $round - * @param string $valueCurrency - * @return float */ - public function convert($value, $targetCurrency, $round = true, $valueCurrency = null); + public function convert(float $value, string $targetCurrency, bool $round = true, string $valueCurrency = null): float; - /** - * Get default currency. - * - * @return string - */ - public function getDefaultCurrency(); + public function getDefaultCurrency(): string; } diff --git a/Currency/Formatter.php b/Currency/Formatter.php index ef5a33a..94fd566 100644 --- a/Currency/Formatter.php +++ b/Currency/Formatter.php @@ -3,62 +3,45 @@ namespace Lexik\Bundle\CurrencyBundle\Currency; /** - * Currency formatter. - * * @author Cédric Girard */ class Formatter implements FormatterInterface { /** - * @var string + * @var array */ - protected $locale; + protected array $cleanCharacters; - /** - * @var array - */ - protected $cleanCharacters; - - /** - * @param sting $locale - */ - public function __construct($locale) + public function __construct(protected string $locale) { - $this->locale = $locale; - $this->cleanCharacters = array('EU', 'UK', 'US'); + $this->cleanCharacters = ['EU', 'UK', 'US']; } - /** - * {@inheritDoc} - */ - public function format($value, $valueCurrency = null, $decimal = true, $symbol = true) + public function format(float $value, string $valueCurrency = '', bool $decimal = true, bool $symbol = true): ?string { $formatter = new \NumberFormatter($this->locale, $symbol ? \NumberFormatter::CURRENCY : \NumberFormatter::PATTERN_DECIMAL); $value = $formatter->formatCurrency($value, $valueCurrency); if (!$decimal) { - $value = preg_replace('/[.,]00((?=\D)|$)/', '', $value); + $value = (string) preg_replace('/[.,]00((?=\D)|$)/', '', $value); } - if (count($this->cleanCharacters) > 0) { + if ($this->cleanCharacters !== []) { $value = str_replace($this->cleanCharacters, '', $value); } return $value; } - /** - * {@inheritDoc} - */ - public function setLocale($locale) + public function setLocale(string $locale): void { - $this->locale = (string) $locale; + $this->locale = $locale; } /** - * @param array $cleanCharacters + * @param array $cleanCharacters */ - public function setCleanCharacters(array $cleanCharacters) + public function setCleanCharacters(array $cleanCharacters): void { $this->cleanCharacters = $cleanCharacters; } diff --git a/Currency/FormatterInterface.php b/Currency/FormatterInterface.php index e675612..5dc31f0 100644 --- a/Currency/FormatterInterface.php +++ b/Currency/FormatterInterface.php @@ -7,21 +7,7 @@ */ interface FormatterInterface { - /** - * Format a given value. - * - * @param string $value - * @param null $valueCurrency - * @param boolean $decimal - * @param boolean $symbol - * @return string - */ - public function format($value, $valueCurrency = null, $decimal = true, $symbol = true); + public function format(float $value, string $valueCurrency = '', bool $decimal = true, bool $symbol = true): ?string; - /** - * Set the locale to use to format the value. - * - * @param string $locale - */ - public function setLocale($locale); + public function setLocale(string $locale): void; } diff --git a/DependencyInjection/Compiler/AdapterPass.php b/DependencyInjection/Compiler/AdapterPass.php index f1868eb..b4923cb 100644 --- a/DependencyInjection/Compiler/AdapterPass.php +++ b/DependencyInjection/Compiler/AdapterPass.php @@ -2,8 +2,8 @@ namespace Lexik\Bundle\CurrencyBundle\DependencyInjection\Compiler; -use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Reference; /** @@ -11,13 +11,13 @@ */ class AdapterPass implements CompilerPassInterface { - public function process(ContainerBuilder $container) + public function process(ContainerBuilder $container): void { // Attach all adapter to the adapter collector $definition = $container->getDefinition('lexik_currency.adapter_collector'); - foreach ($container->findTaggedServiceIds('lexik_currency.adapter') as $id => $attributes) { - $definition->addMethodCall('add', array(new Reference($id))); + foreach (array_keys($container->findTaggedServiceIds('lexik_currency.adapter')) as $id) { + $definition->addMethodCall('add', [new Reference($id)]); } // set default adapter alias @@ -29,4 +29,4 @@ public function process(ContainerBuilder $container) } } } -} \ No newline at end of file +} diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php index 15fb221..dacd22f 100644 --- a/DependencyInjection/Configuration.php +++ b/DependencyInjection/Configuration.php @@ -5,18 +5,12 @@ use Symfony\Component\Config\Definition\Builder\TreeBuilder; use Symfony\Component\Config\Definition\ConfigurationInterface; -/** - * This is the class that validates and merges configuration from your app/config files - */ class Configuration implements ConfigurationInterface { - /** - * {@inheritDoc} - */ - public function getConfigTreeBuilder() + public function getConfigTreeBuilder(): TreeBuilder { - $treeBuilder = new TreeBuilder(); - $rootNode = $treeBuilder->root('lexik_currency'); + $treeBuilder = new TreeBuilder('lexik_currency'); + $rootNode = $treeBuilder->getRootNode(); $rootNode ->addDefaultsIfNotSet() @@ -30,7 +24,7 @@ public function getConfigTreeBuilder() ->isRequired() ->end() ->arrayNode('managed') - ->defaultValue(array('EUR')) + ->defaultValue(['EUR']) ->isRequired() ->prototype('scalar') ->end() diff --git a/DependencyInjection/LexikCurrencyExtension.php b/DependencyInjection/LexikCurrencyExtension.php index 3bca23d..ddeb035 100644 --- a/DependencyInjection/LexikCurrencyExtension.php +++ b/DependencyInjection/LexikCurrencyExtension.php @@ -2,10 +2,10 @@ namespace Lexik\Bundle\CurrencyBundle\DependencyInjection; -use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\Config\FileLocator; -use Symfony\Component\HttpKernel\DependencyInjection\Extension; +use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Loader; +use Symfony\Component\HttpKernel\DependencyInjection\Extension; /** * This is the class that loads and manages your bundle configuration @@ -13,14 +13,14 @@ class LexikCurrencyExtension extends Extension { /** - * {@inheritDoc} + * @param string[] $configs */ - public function load(array $configs, ContainerBuilder $container) + public function load(array $configs, ContainerBuilder $container): void { $configuration = new Configuration(); $config = $this->processConfiguration($configuration, $configs); - $loader = new Loader\XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); + $loader = new Loader\XmlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config')); $loader->load('services.xml'); $loader->load('adapters.xml'); diff --git a/Entity/Currency.php b/Entity/Currency.php index 7a0434c..34bb97a 100644 --- a/Entity/Currency.php +++ b/Entity/Currency.php @@ -9,85 +9,48 @@ */ class Currency { - /** - * @var int - */ - protected $id; + protected int $id; /** * @Assert\Length(min=3) * @Assert\Length(max=3) * @Assert\NotBlank() * @Assert\Type(type="string") - * - * @var string */ - protected $code; + protected string $code; /** * @Assert\NotBlank() * @Assert\Type(type="numeric") - * - * @var string */ - protected $rate; + protected float $rate; - /** - * Get ID - * - * @return string - */ - public function getId() + public function getId(): int { return $this->id; } - /** - * Get code - * - * @return string - */ - public function getCode() + public function getCode(): string { return $this->code; } - /** - * Set code - * - * @param string $code - */ - public function setCode($code) + public function setCode(string $code): void { $this->code = $code; } - /** - * Get rate - * - * @return string - */ - public function getRate() + public function getRate(): float { return $this->rate; } - /** - * Set rate - * - * @param string $rate - */ - public function setRate($rate) + public function setRate(float $rate): void { $this->rate = $rate; } - /** - * Convert currency rate - * - * @param float $rate - */ - public function convert($rate) + public function convert(float $rate): void { $this->rate /= $rate; } diff --git a/EventListener/LocaleListener.php b/EventListener/LocaleListener.php index 8e73e8b..4483ea2 100644 --- a/EventListener/LocaleListener.php +++ b/EventListener/LocaleListener.php @@ -4,7 +4,7 @@ use Lexik\Bundle\CurrencyBundle\Currency\FormatterInterface; use Symfony\Component\EventDispatcher\EventSubscriberInterface; -use Symfony\Component\HttpKernel\Event\GetResponseEvent; +use Symfony\Component\HttpKernel\Event\RequestEvent; use Symfony\Component\HttpKernel\KernelEvents; /** @@ -12,35 +12,24 @@ */ class LocaleListener implements EventSubscriberInterface { - /** - * @var \Lexik\Bundle\CurrencyBundle\Currency\FormatterInterface - */ - private $formatter; - - /** - * @param FormatterInterface $formatter - */ - public function __construct(FormatterInterface $formatter) + public function __construct(private FormatterInterface $formatter) { - $this->formatter = $formatter; } /** - * {@inheritDoc} + * @return array> */ - public static function getSubscribedEvents() + public static function getSubscribedEvents(): array { - return array( - KernelEvents::REQUEST => array( - array('setCurrencyFormatterLocale', 17) // must be registered before the default Locale listener - ), - ); + return [ + KernelEvents::REQUEST => [ + // must be registered before the default Locale listener + ['setCurrencyFormatterLocale', 17] + ], + ]; } - /** - * @param GetResponseEvent $event - */ - public function setCurrencyFormatterLocale(GetResponseEvent $event) + public function setCurrencyFormatterLocale(RequestEvent $event): void { $request = $event->getRequest(); diff --git a/Exception/CurrencyNotFoundException.php b/Exception/CurrencyNotFoundException.php index 446f945..f2075d9 100644 --- a/Exception/CurrencyNotFoundException.php +++ b/Exception/CurrencyNotFoundException.php @@ -8,8 +8,8 @@ */ class CurrencyNotFoundException extends \InvalidArgumentException { - public function __construct($currency) + public function __construct(string $currency) { - parent::__construct(sprintf('Can\'t find currency: "%s"', $currency)); + parent::__construct(sprintf('Cannot find currency: "%s"', $currency)); } -} \ No newline at end of file +} diff --git a/LexikCurrencyBundle.php b/LexikCurrencyBundle.php index 5e47722..97694d7 100644 --- a/LexikCurrencyBundle.php +++ b/LexikCurrencyBundle.php @@ -3,8 +3,8 @@ namespace Lexik\Bundle\CurrencyBundle; use Lexik\Bundle\CurrencyBundle\DependencyInjection\Compiler\AdapterPass; -use Symfony\Component\HttpKernel\Bundle\Bundle; use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\HttpKernel\Bundle\Bundle; class LexikCurrencyBundle extends Bundle { diff --git a/Tests/Fixtures/CurrencyData.php b/Tests/Fixtures/CurrencyData.php index 43c7982..97a3745 100644 --- a/Tests/Fixtures/CurrencyData.php +++ b/Tests/Fixtures/CurrencyData.php @@ -2,10 +2,10 @@ namespace Lexik\Bundle\CurrencyBundle\Tests\Fixtures; -use Lexik\Bundle\CurrencyBundle\Entity\Currency; - use Doctrine\Common\DataFixtures\FixtureInterface; -use Doctrine\Common\Persistence\ObjectManager; + +use Doctrine\Persistence\ObjectManager; +use Lexik\Bundle\CurrencyBundle\Entity\Currency; /** * Tests fixtures class. @@ -14,15 +14,12 @@ */ class CurrencyData implements FixtureInterface { - /** - * @see Doctrine\Common\DataFixtures.FixtureInterface::load() - */ - public function load(ObjectManager $manager) + public function load(ObjectManager $manager): void { - $values = array( - array('code' => 'EUR', 'rate' => 1), - array('code' => 'USD', 'rate' => 1.3), - ); + $values = [ + ['code' => 'EUR', 'rate' => 1.0], + ['code' => 'USD', 'rate' => 1.3], + ]; foreach ($values as $data) { $currency = new Currency(); @@ -34,4 +31,4 @@ public function load(ObjectManager $manager) $manager->flush(); } -} \ No newline at end of file +} diff --git a/Tests/Unit/Adapter/AdapterFactoryTest.php b/Tests/Unit/Adapter/AdapterFactoryTest.php index 72e0449..8f75a15 100644 --- a/Tests/Unit/Adapter/AdapterFactoryTest.php +++ b/Tests/Unit/Adapter/AdapterFactoryTest.php @@ -2,58 +2,59 @@ namespace Lexik\Bundle\CurrencyBundle\Tests\Unit\Adapter; +use Doctrine\Bundle\DoctrineBundle\Registry; use Lexik\Bundle\CurrencyBundle\Adapter\AdapterFactory; +use Lexik\Bundle\CurrencyBundle\Adapter\DoctrineCurrencyAdapter; +use Lexik\Bundle\CurrencyBundle\Adapter\EcbCurrencyAdapter; +use Lexik\Bundle\CurrencyBundle\Adapter\YahooCurrencyAdapter; +use Lexik\Bundle\CurrencyBundle\Entity\Currency; use Lexik\Bundle\CurrencyBundle\Tests\Unit\BaseUnitTestCase; class AdapterFactoryTest extends BaseUnitTestCase { - const CURRENCY_ENTITY = 'Lexik\Bundle\CurrencyBundle\Entity\Currency'; - - protected $doctrine; - - public function setUp() + public function setUp(): void { $this->doctrine = $this->getMockDoctrine(); $em = $this->getEntityManager(); $this->createSchema($em); } - public function testCreateEcbAdapter() + public function testCreateEcbAdapter(): void { - $factory = new AdapterFactory($this->doctrine, 'EUR', array('EUR', 'USD'), self::CURRENCY_ENTITY); + $factory = new AdapterFactory($this->doctrine, 'EUR', ['EUR', 'USD'], Currency::class); $adapter = $factory->createEcbAdapter(); - $this->assertInstanceOf('Lexik\Bundle\CurrencyBundle\Adapter\EcbCurrencyAdapter', $adapter); + $this->assertInstanceOf(EcbCurrencyAdapter::class, $adapter); $this->assertEquals('EUR', $adapter->getDefaultCurrency()); - $this->assertEquals(array('EUR', 'USD'), $adapter->getManagedCurrencies()); + $this->assertEquals(['EUR', 'USD'], $adapter->getManagedCurrencies()); $this->assertEquals(0, count($adapter)); } - public function testCreateYahooAdapter() + public function testCreateYahooAdapter(): void { - $factory = new AdapterFactory($this->doctrine, 'EUR', array('EUR', 'USD'), self::CURRENCY_ENTITY); + $factory = new AdapterFactory($this->doctrine, 'EUR', ['EUR', 'USD'], Currency::class); $adapter = $factory->createYahooAdapter(); - $this->assertInstanceOf('Lexik\Bundle\CurrencyBundle\Adapter\YahooCurrencyAdapter', $adapter); + $this->assertInstanceOf(YahooCurrencyAdapter::class, $adapter); $this->assertEquals('EUR', $adapter->getDefaultCurrency()); - $this->assertEquals(array('EUR', 'USD'), $adapter->getManagedCurrencies()); + $this->assertEquals(['EUR', 'USD'], $adapter->getManagedCurrencies()); $this->assertEquals(0, count($adapter)); } - public function testCreateDoctrineAdapter() + public function testCreateDoctrineAdapter(): void { $em = $this->getEntityManager(); $this->loadFixtures($em); - $factory = new AdapterFactory($this->doctrine, 'USD', array('EUR'), self::CURRENCY_ENTITY); + $factory = new AdapterFactory($this->doctrine, 'USD', ['EUR'], Currency::class); $adapter = $factory->createDoctrineAdapter(); - $this->assertInstanceOf('Lexik\Bundle\CurrencyBundle\Adapter\DoctrineCurrencyAdapter', $adapter); + $this->assertInstanceOf(DoctrineCurrencyAdapter::class, $adapter); $this->assertEquals('USD', $adapter->getDefaultCurrency()); - $this->assertEquals(array('EUR'), $adapter->getManagedCurrencies()); + $this->assertEquals(['EUR'], $adapter->getManagedCurrencies()); $this->assertEquals(0, count($adapter)); - $adapter['USD']; // force initialization + $adapter['USD']; // force initialization // @phpstan-ignore-line $this->assertEquals(2, count($adapter)); } -} \ No newline at end of file +} diff --git a/Tests/Unit/BaseUnitTestCase.php b/Tests/Unit/BaseUnitTestCase.php index b1b4fa8..b190740 100644 --- a/Tests/Unit/BaseUnitTestCase.php +++ b/Tests/Unit/BaseUnitTestCase.php @@ -2,67 +2,53 @@ namespace Lexik\Bundle\CurrencyBundle\Tests\Unit; -use Lexik\Bundle\CurrencyBundle\Tests\Fixtures\CurrencyData; - -use Doctrine\ORM\EntityManager; +use Doctrine\Bundle\DoctrineBundle\Registry; use Doctrine\Common\DataFixtures\Executor\ORMExecutor; use Doctrine\Common\DataFixtures\Purger\ORMPurger; -use Doctrine\Common\Annotations\AnnotationReader; +use Doctrine\ORM\Configuration; +use Doctrine\ORM\EntityManager; +use Doctrine\ORM\EntityManagerInterface; +use Doctrine\ORM\Mapping\DefaultQuoteStrategy; +use Doctrine\ORM\Mapping\Driver\SimplifiedXmlDriver; +use Doctrine\ORM\Repository\DefaultRepositoryFactory; +use Doctrine\ORM\Tools\SchemaTool; +use Lexik\Bundle\CurrencyBundle\Tests\Fixtures\CurrencyData; +use PHPUnit\Framework\TestCase; /** * Base unit test class providing functions to create a mock entity manger, load schema and fixtures. * * @author Cédric Girard */ -abstract class BaseUnitTestCase extends \PHPUnit_Framework_TestCase +abstract class BaseUnitTestCase extends TestCase { - /** - * Create the database schema. - * - * @param EntityManager $em - */ - protected function createSchema(EntityManager $em) + public Registry $doctrine; + + protected function createSchema(EntityManagerInterface $em): void { - $schemaTool = new \Doctrine\ORM\Tools\SchemaTool($em); + $schemaTool = new SchemaTool($em); $schemaTool->createSchema($em->getMetadataFactory()->getAllMetadata()); } - /** - * Load test fixtures. - * - * @param EntityManager $om - */ - protected function loadFixtures(EntityManager $em) + protected function loadFixtures(EntityManagerInterface $em): void { $purger = new ORMPurger(); $executor = new ORMExecutor($em, $purger); - $executor->execute(array(new CurrencyData()), false); + $executor->execute([new CurrencyData()], false); } /** * EntityManager mock object together with annotation mapping driver and * pdo_sqlite database in memory - * - * @return EntityManager */ - protected function getMockSqliteEntityManager() + protected function getMockSqliteEntityManager(): EntityManagerInterface { - $cache = new \Doctrine\Common\Cache\ArrayCache(); + $xmlDriver = new SimplifiedXmlDriver([ + __DIR__ . '/../../Resources/config/doctrine' => 'Lexik\Bundle\CurrencyBundle\Entity', + ]); - // xml driver - $xmlDriver = new \Doctrine\ORM\Mapping\Driver\SimplifiedXmlDriver(array( - __DIR__.'/../../Resources/config/doctrine' => 'Lexik\Bundle\CurrencyBundle\Entity', - )); - - // configuration mock - $config = $this->getMock('Doctrine\ORM\Configuration'); - $config->expects($this->any()) - ->method('getMetadataCacheImpl') - ->will($this->returnValue($cache)); - $config->expects($this->any()) - ->method('getQueryCacheImpl') - ->will($this->returnValue($cache)); + $config = $this->createMock(Configuration::class); $config->expects($this->once()) ->method('getProxyDir') ->will($this->returnValue(sys_get_temp_dir())); @@ -83,26 +69,24 @@ protected function getMockSqliteEntityManager() ->will($this->returnValue('Doctrine\ORM\EntityRepository')); $config->expects($this->any()) ->method('getRepositoryFactory') - ->will($this->returnValue(new \Doctrine\ORM\Repository\DefaultRepositoryFactory())); + ->will($this->returnValue(new DefaultRepositoryFactory())); $config->expects($this->any()) ->method('getQuoteStrategy') - ->will($this->returnValue(new \Doctrine\ORM\Mapping\DefaultQuoteStrategy())); + ->will($this->returnValue(new DefaultQuoteStrategy())); - $conn = array( + $conn = [ 'driver' => 'pdo_sqlite', 'memory' => true, - ); - - $em = \Doctrine\ORM\EntityManager::create($conn, $config); + ]; - return $em; + return EntityManager::create($conn, $config); } - protected function getMockDoctrine() + protected function getMockDoctrine(): Registry { $em = $this->getMockSqliteEntityManager(); - $doctrine = $this->getMockBuilder('Doctrine\Bundle\DoctrineBundle\Registry') + $doctrine = $this->getMockBuilder(Registry::class) ->disableOriginalConstructor() ->getMock(); @@ -113,10 +97,11 @@ protected function getMockDoctrine() return $doctrine; } - protected function getEntityManager() + protected function getEntityManager(): EntityManagerInterface { $em = $this->doctrine->getManager(); + assert($em instanceof EntityManagerInterface); return $em; } -} \ No newline at end of file +} diff --git a/Tests/Unit/Converter/ConverterTest.php b/Tests/Unit/Converter/ConverterTest.php index 9330f4f..ce5fb27 100644 --- a/Tests/Unit/Converter/ConverterTest.php +++ b/Tests/Unit/Converter/ConverterTest.php @@ -2,19 +2,18 @@ namespace Lexik\Bundle\CurrencyBundle\Tests\Unit\Converter; +use Lexik\Bundle\CurrencyBundle\Adapter\AbstractCurrencyAdapter; use Lexik\Bundle\CurrencyBundle\Adapter\AdapterFactory; use Lexik\Bundle\CurrencyBundle\Currency\Converter; +use Lexik\Bundle\CurrencyBundle\Entity\Currency; +use Lexik\Bundle\CurrencyBundle\Exception\CurrencyNotFoundException; use Lexik\Bundle\CurrencyBundle\Tests\Unit\BaseUnitTestCase; class ConverterTest extends BaseUnitTestCase { - const CURRENCY_ENTITY = 'Lexik\Bundle\CurrencyBundle\Entity\Currency'; + private AbstractCurrencyAdapter $adapter; - protected $doctrine; - - private $adapter; - - public function setUp() + public function setUp(): void { $this->doctrine = $this->getMockDoctrine(); $em = $this->getEntityManager(); @@ -22,11 +21,11 @@ public function setUp() $this->createSchema($em); $this->loadFixtures($em); - $factory = new AdapterFactory($this->doctrine, 'EUR', array('EUR', 'USD'), self::CURRENCY_ENTITY); + $factory = new AdapterFactory($this->doctrine, 'EUR', ['EUR', 'USD'], Currency::class); $this->adapter = $factory->createDoctrineAdapter(); } - public function testConvert() + public function testConvert(): void { $converter = new Converter($this->adapter); @@ -39,7 +38,7 @@ public function testConvert() $this->assertEquals(8.666, $converter->convert(8.666, 'EUR')); } - public function testConvertNotRounded() + public function testConvertNotRounded(): void { $converter = new Converter($this->adapter); @@ -47,7 +46,7 @@ public function testConvertNotRounded() $this->assertEquals(8.666, $converter->convert(8.666, 'EUR', false)); } - public function testConvertFromNoDefaultCurrency() + public function testConvertFromNoDefaultCurrency(): void { $converter = new Converter($this->adapter); @@ -55,7 +54,7 @@ public function testConvertFromNoDefaultCurrency() $this->assertEquals(6.67, $converter->convert(8.666, 'EUR', true, 'USD')); } - public function testConvertFromNoDefaultCurrencyNotRounded() + public function testConvertFromNoDefaultCurrencyNotRounded(): void { $converter = new Converter($this->adapter); @@ -63,14 +62,13 @@ public function testConvertFromNoDefaultCurrencyNotRounded() $this->assertEquals(6.6661538461538, $converter->convert(8.666, 'EUR', false, 'USD')); } - /** - * @expectedException Lexik\Bundle\CurrencyBundle\Exception\CurrencyNotFoundException - * @expectedExceptionMessage Can't find currency: "UUU" - */ - public function testConvertUndefinedTarget() + public function testConvertUndefinedTarget(): void { $converter = new Converter($this->adapter); + $this->expectException(CurrencyNotFoundException::class); + $this->expectExceptionMessage('Cannot find currency: "UUU"'); + $converter->convert(8.666, 'UUU'); } -} \ No newline at end of file +} diff --git a/Tests/Unit/Twig/Extension/CurrencyExtensionTest.php b/Tests/Unit/Twig/Extension/CurrencyExtensionTest.php index 900cd44..0c7970d 100644 --- a/Tests/Unit/Twig/Extension/CurrencyExtensionTest.php +++ b/Tests/Unit/Twig/Extension/CurrencyExtensionTest.php @@ -2,22 +2,22 @@ namespace Lexik\Bundle\CurrencyBundle\Tests\Unit\Twig\Extension; -use Lexik\Bundle\CurrencyBundle\Currency\Converter; +use Doctrine\Bundle\DoctrineBundle\Registry; use Lexik\Bundle\CurrencyBundle\Adapter\AdapterFactory; +use Lexik\Bundle\CurrencyBundle\Currency\Converter; use Lexik\Bundle\CurrencyBundle\Currency\Formatter; -use Lexik\Bundle\CurrencyBundle\Twig\Extension\CurrencyExtension; +use Lexik\Bundle\CurrencyBundle\Entity\Currency; use Lexik\Bundle\CurrencyBundle\Tests\Unit\BaseUnitTestCase; +use Lexik\Bundle\CurrencyBundle\Twig\Extension\CurrencyExtension; use Symfony\Component\DependencyInjection\Container; class CurrencyExtensionTest extends BaseUnitTestCase { - const CURRENCY_ENTITY = 'Lexik\Bundle\CurrencyBundle\Entity\Currency'; - - protected $doctrine; + public Registry $doctrine; - private $container; + private Container $container; - public function setUp() + public function setUp(): void { $this->doctrine = $this->getMockDoctrine(); $em = $this->getEntityManager(); @@ -25,7 +25,7 @@ public function setUp() $this->createSchema($em); $this->loadFixtures($em); - $factory = new AdapterFactory($this->doctrine, 'EUR', array('EUR', 'USD'), self::CURRENCY_ENTITY); + $factory = new AdapterFactory($this->doctrine, 'EUR', ['EUR', 'USD'], Currency::class); $converter = new Converter($factory->createDoctrineAdapter()); @@ -36,7 +36,7 @@ public function setUp() $this->container->set('lexik_currency.formatter', $formatter); } - public function testConvert() + public function testConvert(): void { $extension = new CurrencyExtension($this->container); @@ -44,7 +44,7 @@ public function testConvert() $this->assertEquals(8.67, $extension->convert(8.666, 'EUR')); } - public function testFormat() + public function testFormat(): void { $extension = new CurrencyExtension($this->container); @@ -52,20 +52,20 @@ public function testFormat() $this->assertEquals('8,67 €', $extension->format(8.666, 'EUR')); $this->assertEquals('8,67 $', $extension->format(8.666, 'USD')); $this->assertEquals('8 $', $extension->format(8.0, 'USD', false)); - $this->assertEquals('8,666', $extension->format(8.666, 'USD', false, false)); - $this->assertEquals('8', $extension->format(8.0, 'USD', true, false)); + $this->assertEquals('8,67', $extension->format(8.666, 'USD', false, false)); + $this->assertEquals('8', $extension->format(8.0, 'USD', false, false)); $this->assertEquals('8 $', $extension->format(8.0, 'USD', false, true)); } - public function testConvertAndFormat() + public function testConvertAndFormat(): void { $extension = new CurrencyExtension($this->container); $this->assertEquals('11,27 $', $extension->convertAndFormat(8.666, 'USD')); $this->assertEquals('11,27 $', $extension->convertAndFormat(8.666, 'USD', false)); - $this->assertEquals('11,2658', $extension->convertAndFormat(8.666, 'USD', false, false)); + $this->assertEquals('11,27', $extension->convertAndFormat(8.666, 'USD', false, false)); $this->assertEquals('8,67', $extension->convertAndFormat(8.666, 'USD', true, false, 'USD')); - $this->assertEquals('8', $extension->convertAndFormat(8.0, 'USD', true, false, 'USD')); + $this->assertEquals('8,00', $extension->convertAndFormat(8.0, 'USD', true, false, 'USD')); $this->assertEquals('8,00 $', $extension->convertAndFormat(8.0, 'USD', true, true, 'USD')); $this->assertEquals('8 $', $extension->convertAndFormat(8.0, 'USD', false, true, 'USD')); } diff --git a/Tests/bootstrap.php b/Tests/bootstrap.php index d9f7acc..326c216 100644 --- a/Tests/bootstrap.php +++ b/Tests/bootstrap.php @@ -1,7 +1,7 @@ * @author Yoann Aparici */ -class CurrencyExtension extends \Twig_Extension +class CurrencyExtension extends AbstractExtension { - /** - * @var ContainerInterface - */ - protected $container; + protected ContainerInterface $container; - /** - * Construct. - * - * @param ContainerInterface $container We need the entire container to lazy load the Converter - */ public function __construct(ContainerInterface $container) { $this->container = $container; } - /** - * {@inheritdoc} - */ - public function getFilters() + public function getFilters(): array { - return array( - new \Twig_SimpleFilter('currency_convert', array($this, 'convert')), - new \Twig_SimpleFilter('currency_format', array($this, 'format')), - new \Twig_SimpleFilter('currency_convert_format', array($this, 'convertAndFormat')), - ); + return [ + new TwigFilter('currency_convert', [$this, 'convert']), + new TwigFilter('currency_format', [$this, 'format']), + new TwigFilter('currency_convert_format', [$this, 'convertAndFormat']), + ]; } - /** - * @return \Lexik\Bundle\CurrencyBundle\Currency\ConverterInterface - */ - public function getConverter() + public function getConverter(): ConverterInterface { - return $this->container->get('lexik_currency.converter'); + $converter = $this->container->get('lexik_currency.converter'); + assert($converter instanceof ConverterInterface); + + return $converter; } - /** - * @return \Lexik\Bundle\CurrencyBundle\Currency\FormatterInterface - */ - public function getFormatter() + public function getFormatter(): FormatterInterface { - return $this->container->get('lexik_currency.formatter'); + $formatter = $this->container->get('lexik_currency.formatter'); + assert($formatter instanceof FormatterInterface); + + return $formatter; } - /** - * Convert the given value. - * - * @param float $value - * @param string $targetCurrency target currency code - * @param boolean $round roud converted value - * @param string $valueCurrency $value currency code - * @return float - */ - public function convert($value, $targetCurrency, $round = true, $valueCurrency = null) + public function convert(float $value, string $targetCurrency, bool $round = true, string $valueCurrency = null): float { return $this->getConverter()->convert($value, $targetCurrency, $round, $valueCurrency); } - /** - * Format the given value. - * - * @param mixed $value - * @param string $valueCurrency $value currency code - * @param boolean $decimal show decimal part - * @param boolean $symbol show currency symbol - * @return string - */ - public function format($value, $valueCurrency = null, $decimal = true, $symbol = true) + public function format(float $value, string $valueCurrency = null, bool $decimal = true, bool $symbol = true): ?string { if (null === $valueCurrency) { $valueCurrency = $this->getConverter()->getDefaultCurrency(); @@ -87,27 +62,14 @@ public function format($value, $valueCurrency = null, $decimal = true, $symbol = return $this->getFormatter()->format($value, $valueCurrency, $decimal, $symbol); } - /** - * Convert and format the given value. - * - * @param mixed $value - * @param string $targetCurrency target currency code - * @param boolean $decimal show decimal part - * @param boolean $symbol show currency symbol - * @param string $valueCurrency the $value currency code - * @return string - */ - public function convertAndFormat($value, $targetCurrency, $decimal = true, $symbol = true, $valueCurrency = null) + public function convertAndFormat(float $value, string $targetCurrency, bool $decimal = true, bool $symbol = true, string $valueCurrency = null): ?string { $value = $this->convert($value, $targetCurrency, $decimal, $valueCurrency); return $this->format($value, $targetCurrency, $decimal, $symbol); } - /** - * {@inheritdoc} - */ - public function getName() + public function getName(): string { return 'lexik_currency.currency_extension'; } diff --git a/composer.json b/composer.json index 0aa2996..07e6903 100644 --- a/composer.json +++ b/composer.json @@ -1,8 +1,8 @@ { "name": "lexik/currency-bundle", "type": "symfony-bundle", - "description": "This Symfony2 bundle provide a service and a Twig extension to convert and display currencies.", - "keywords": ["Symfony2", "bundle", "currency"], + "description": "This Symfony bundle provide a service and a Twig extension to convert and display currencies.", + "keywords": ["Symfony", "bundle", "currency"], "homepage": "https://github.com/lexik/LexikCurrencyBundle", "license": "MIT", "authors": [ @@ -16,8 +16,21 @@ } ], "require": { - "php": ">=5.5.9", - "symfony/framework-bundle": "~2.8|~3.0|~4.0" + "php": ">8.0", + "ext-curl": "*", + "ext-intl": "*", + "ext-simplexml": "*", + "symfony/framework-bundle": "^5.0 || ^6.0" + }, + "require-dev": { + "rector/rector": "^0.12.16", + "symplify/easy-coding-standard": "^10.0", + "doctrine/orm": "^2.11", + "doctrine/doctrine-bundle": "^2.5", + "phpunit/phpunit": "^9.5", + "doctrine/doctrine-fixtures-bundle": "^3.4", + "twig/twig": "^3.3", + "symfony/validator": "^6.0" }, "autoload": { "psr-4": { "Lexik\\Bundle\\CurrencyBundle\\": "" } diff --git a/ecs.php b/ecs.php new file mode 100644 index 0000000..e0d13a1 --- /dev/null +++ b/ecs.php @@ -0,0 +1,24 @@ +parameters(); + $parameters->set(Option::PATHS, [ + __DIR__ + ]); + $parameters->set(Option::PARALLEL, true); + + $containerConfigurator->import(SetList::PSR_12); + + $services = $containerConfigurator->services(); + $services->set(ArraySyntaxFixer::class) + ->call('configure', [[ + 'syntax' => 'short', + ]]); +}; diff --git a/phpstan.neon b/phpstan.neon new file mode 100644 index 0000000..aeb27a6 --- /dev/null +++ b/phpstan.neon @@ -0,0 +1,19 @@ + +parameters: + + level: 9 + + paths: + - Adapter + - Command + - Currency + - DependencyInjection + - Entity + - EventListener + - Exception + - Resources + - Tests + - Twig + + ignoreErrors: + - '#NodeDefinition::addDefaultsIfNotSet\(\)#' diff --git a/phpunit.xml.dist b/phpunit.xml.dist index f3cb7d3..318747e 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,34 +1,32 @@ - - + + + ./Adapter + ./Command + ./Currency + ./DependencyInjection + ./Entity + ./EventListener + ./Exception + ./Twig + + + ./Resources + ./Tests + ./vendor + + ./Tests/ - - - - ./ - - ./Resources - ./Tests - ./vendor - - - - - + diff --git a/rector.php b/rector.php new file mode 100644 index 0000000..3e9e996 --- /dev/null +++ b/rector.php @@ -0,0 +1,38 @@ +parameters(); + + $parameters->set(Option::PATHS, [ + __DIR__ . '/Adapter', + __DIR__ . '/Command', + __DIR__ . '/Currency', + __DIR__ . '/DependencyInjection', + __DIR__ . '/Entity', + __DIR__ . '/EventListener', + __DIR__ . '/Exception', + __DIR__ . '/Resources', + __DIR__ . '/Tests', + __DIR__ . '/Twig', + ]); + $parameters->set(Option::PARALLEL, true); + + $containerConfigurator->import(SetList::TYPE_DECLARATION); + $containerConfigurator->import(SetList::CODE_QUALITY); + $containerConfigurator->import(SetList::PHP_80); + $containerConfigurator->import(SetList::PHP_74); + $containerConfigurator->import(SetList::PHP_73); + $containerConfigurator->import(SetList::PHP_72); + $containerConfigurator->import(SetList::PHP_71); + $containerConfigurator->import(SetList::PHP_70); + + //$services = $containerConfigurator->services(); + //$services->set(TypedPropertyRector::class); +};