Skip to content
Snippets Groups Projects
Commit f3118af5 authored by Demian Katz's avatar Demian Katz
Browse files

Simplified money formatting code to be platform independent.

- Added test coverage.
parent ce2dd257
No related merge requests found
......@@ -26,7 +26,7 @@
* @link http://vufind.org/wiki/vufind2:developer_manual Wiki
*/
namespace VuFind\View\Helper\Root;
use Zend\View\Helper\AbstractHelper;
use NumberFormatter, Zend\View\Helper\AbstractHelper;
/**
* Safe money format view helper
......@@ -40,175 +40,56 @@ use Zend\View\Helper\AbstractHelper;
class SafeMoneyFormat extends AbstractHelper
{
/**
* Currency-rendering logic.
*
* @param string $number The number to format
* Default currency format (ISO 4217) to use.
*
* @return string
* @var string
*/
public function __invoke($number)
{
// money_format() does not exist on windows
if (function_exists('money_format')) {
return $this->view->escapeHtml(money_format('%.2n', $number));
} else {
return self::windowsSafeMoneyFormat($number);
}
}
protected $defaultCurrency;
/**
* Windows-compatible equivalent to built-in money_format function.
* Number formatter.
*
* @param string $number Number to format.
* @var NumberFormatter;
*/
protected $formatter;
/**
* Constructor
*
* @return string
* @param string $defaultCurrency Default currency format (ISO 4217) to use (null
* for default from locale)
*/
public static function windowsSafeMoneyFormat($number)
public function __construct($defaultCurrency = null)
{
// '' or NULL gets the locale values from environment variables
setlocale(LC_ALL, '');
$locale = localeconv();
// Windows doesn't support UTF-8 encoding in setlocale, so we'll have to
// convert the currency symbol manually:
$currency_symbol = self::safeMoneyFormatMakeUTF8($locale['currency_symbol']);
// How is the amount signed?
// Positive
if ($number > 0) {
$sign = $locale['positive_sign'];
$sign_posn = $locale['p_sign_posn'];
$sep_by_space = $locale['p_sep_by_space'];
$cs_precedes = $locale['p_cs_precedes'];
} else {
// Negative
$sign = $locale['negative_sign'];
$sign_posn = $locale['n_sign_posn'];
$sep_by_space = $locale['n_sep_by_space'];
$cs_precedes = $locale['n_cs_precedes'];
}
// Format the absolute value of the number
$m = number_format(
abs($number), $locale['frac_digits'], $locale['mon_decimal_point'],
$locale['mon_thousands_sep']
);
// Spaces between the number and symbol?
if ($sep_by_space) {
$space = ' ';
} else {
$space = '';
}
if ($cs_precedes) {
$m = $currency_symbol.$space.$m;
} else {
$m = $m.$space.$currency_symbol;
}
// HTML spaces
$m = str_replace(' ', ' ', $m);
// Initialize number formatter:
$locale = setlocale(LC_MONETARY, 0);
$this->formatter = new NumberFormatter($locale, NumberFormatter::CURRENCY);
// Add symbol
switch ($sign_posn) {
case 0:
$m = "($m)";
break;
case 1:
$m = $sign.$m;
break;
case 2:
$m = $m.$sign;
break;
case 3:
$m = $sign.$m;
break;
case 4:
$m = $m.$sign;
break;
default:
$m = "$m [error sign_posn = $sign_posn !]";
// Initialize default currency:
if (null === $defaultCurrency) {
$localeInfo = localeconv();
$defaultCurrency = isset($localeInfo['int_curr_symbol'])
? $localeInfo['int_curr_symbol'] : 'USD';
}
return $m;
$this->defaultCurrency = trim($defaultCurrency);
}
/**
* Adapted from code at http://us.php.net/manual/en/function.utf8-encode.php
* This is needed for Windows only as a support function for
* windowsSafeMoneyFormat; utf8_encode by itself doesn't do the job, but this
* is capable of properly turning currency symbols into valid UTF-8.
* Currency-rendering logic.
*
* @param string $instr String to convert to UTF-8
* @param float $number The number to format
* @param string $currency Currency format (ISO 4217) to use (null for default)
*
* @return string
*/
public static function safeMoneyFormatMakeUTF8($instr)
public function __invoke($number, $currency = null)
{
static $nibble_good_chars = false;
static $byte_map = array();
if (empty($byte_map)) {
for ($x=128;$x<256;++$x) {
$byte_map[chr($x)]=utf8_encode(chr($x));
}
$cp1252_map=array(
"\x80"=>"\xE2\x82\xAC", // EURO SIGN
"\x82" => "\xE2\x80\x9A", // SINGLE LOW-9 QUOTATION MARK
"\x83" => "\xC6\x92", // LATIN SMALL LETTER F WITH HOOK
"\x84" => "\xE2\x80\x9E", // DOUBLE LOW-9 QUOTATION MARK
"\x85" => "\xE2\x80\xA6", // HORIZONTAL ELLIPSIS
"\x86" => "\xE2\x80\xA0", // DAGGER
"\x87" => "\xE2\x80\xA1", // DOUBLE DAGGER
"\x88" => "\xCB\x86", // MODIFIER LETTER CIRCUMFLEX ACCENT
"\x89" => "\xE2\x80\xB0", // PER MILLE SIGN
"\x8A" => "\xC5\xA0", // LATIN CAPITAL LETTER S WITH CARON
"\x8B" => "\xE2\x80\xB9", // SINGLE LEFT-POINTING ANGLE QUOTE
"\x8C" => "\xC5\x92", // LATIN CAPITAL LIGATURE OE
"\x8E" => "\xC5\xBD", // LATIN CAPITAL LETTER Z WITH CARON
"\x91" => "\xE2\x80\x98", // LEFT SINGLE QUOTATION MARK
"\x92" => "\xE2\x80\x99", // RIGHT SINGLE QUOTATION MARK
"\x93" => "\xE2\x80\x9C", // LEFT DOUBLE QUOTATION MARK
"\x94" => "\xE2\x80\x9D", // RIGHT DOUBLE QUOTATION MARK
"\x95" => "\xE2\x80\xA2", // BULLET
"\x96" => "\xE2\x80\x93", // EN DASH
"\x97" => "\xE2\x80\x94", // EM DASH
"\x98" => "\xCB\x9C", // SMALL TILDE
"\x99" => "\xE2\x84\xA2", // TRADE MARK SIGN
"\x9A" => "\xC5\xA1", // LATIN SMALL LETTER S WITH CARON
"\x9B" => "\xE2\x80\xBA", // SINGLE RIGHT-POINTING ANGLE QUOTE
"\x9C" => "\xC5\x93", // LATIN SMALL LIGATURE OE
"\x9E" => "\xC5\xBE", // LATIN SMALL LETTER Z WITH CARON
"\x9F" => "\xC5\xB8" // LATIN CAPITAL LETTER Y WITH DIAERESIS
);
foreach ($cp1252_map as $k=>$v) {
$byte_map[$k]=$v;
}
}
if (!$nibble_good_chars) {
$ascii_char='[\x00-\x7F]';
$cont_byte='[\x80-\xBF]';
$utf8_2='[\xC0-\xDF]'.$cont_byte;
$utf8_3='[\xE0-\xEF]'.$cont_byte.'{2}';
$utf8_4='[\xF0-\xF7]'.$cont_byte.'{3}';
$utf8_5='[\xF8-\xFB]'.$cont_byte.'{4}';
$nibble_good_chars
= "@^($ascii_char+|$utf8_2|$utf8_3|$utf8_4|$utf8_5)(.*)$@s";
if (null === $currency) {
$currency = $this->defaultCurrency;
}
$outstr='';
$char='';
$rest='';
while ((strlen($instr))>0) {
if (1==preg_match($nibble_good_chars, $instr, $match)) {
$char=$match[1];
$rest=$match[2];
$outstr.=$char;
} elseif (1==preg_match('@^(.)(.*)$@s', $instr, $match)) {
$char=$match[1];
$rest=$match[2];
$outstr.=$byte_map[$char];
}
$instr=$rest;
}
return $outstr;
$escaper = $this->getView()->plugin('escapeHtml');
return $escaper(
$this->formatter->formatCurrency((float)$number, $currency)
);
}
}
<?php
/**
* SafeMoneyFormat view helper Test Class
*
* PHP version 5
*
* Copyright (C) Villanova University 2010.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* @category VuFind2
* @package Tests
* @author Demian Katz <demian.katz@villanova.edu>
* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License
* @link http://vufind.org/wiki/vufind2:unit_tests Wiki
*/
namespace VuFindTest\View\Helper\Root;
use VuFind\View\Helper\Root\SafeMoneyFormat;
/**
* SafeMoneyFormat view helper Test Class
*
* @category VuFind2
* @package Tests
* @author Demian Katz <demian.katz@villanova.edu>
* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License
* @link http://vufind.org/wiki/vufind2:unit_tests Wiki
*/
class SafeMoneyFormatTest extends \PHPUnit_Framework_TestCase
{
/**
* Locale (for restoration after testing)
*
* @var string
*/
protected $locale;
/**
* Standard setup method
*
* @return void
*/
public function setUp()
{
// store current default and set a value for consistency in testing
$this->locale = setlocale(LC_MONETARY, 0);
setlocale(LC_MONETARY, 'en_US.UTF8');
}
/**
* Standard teardown method
*
* @return void
*/
public function tearDown()
{
// restore current default
setlocale(LC_MONETARY, $this->locale);
}
/**
* Test the helper
*
* @return void
*/
public function testFormatting()
{
$escaper = new \Zend\View\Helper\EscapeHtml();
$view = $this->getMock('Zend\View\Renderer\PhpRenderer');
$view->expects($this->any())->method('plugin')
->with($this->equalTo('escapeHtml'))
->will($this->returnValue($escaper));
// test default settings
$smf = new SafeMoneyFormat();
$smf->setView($view);
$this->assertEquals('$3.00', $smf->__invoke(3));
$this->assertEquals('€3.00', $smf->__invoke(3, 'EUR'));
// test override default currency
$smf = new SafeMoneyFormat('EUR');
$smf->setView($view);
$this->assertEquals('€3.00', $smf->__invoke(3));
$this->assertEquals('$3.00', $smf->__invoke(3, 'USD'));
}
}
\ No newline at end of file
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment