Index: branches/5.2.x/core/kernel/utility/formatters/formatter.php =================================================================== --- branches/5.2.x/core/kernel/utility/formatters/formatter.php (revision 16762) +++ branches/5.2.x/core/kernel/utility/formatters/formatter.php (revision 16763) @@ -1,305 +1,317 @@ _categoryHelper = $this->Application->recallObject('CategoryHelper'); } /** * Replace FCK links like "@@ID@@" to real page urls, when "using_fck" option is set. * * @param string $value * @param Array $options * @param string $format * @return string */ function _replaceFCKLinks(&$value, $options, $format = null) { if ((isset($format) && strpos($format, 'fck_ready') !== false) || (!array_key_exists('using_fck', $options) || !$options['using_fck'])) { // in textarea, where fck will be used OR not using fck return $value; } return $this->_categoryHelper->replacePageIds($value); } /** * Convert's value to match type from config * * @param mixed $value * @param Array $options * @return mixed * @access protected */ function TypeCast($value, $options) { if ( isset($options['type']) ) { $field_type = $options['type']; if ($field_type == 'numeric') { trigger_error('Invalid field type ' . $field_type . ' (in TypeCast method), please use float instead', E_USER_NOTICE); $field_type = 'float'; } elseif ( $field_type == 'string' ) { if ( isset($options['allow_html']) && $options['allow_html'] ) { $value = $this->Application->unescapeRequestVariable($value); } return $value; } $value = $this->formatNumber($value); $is_numeric_type = preg_match('#int|integer|double|float|real#', $field_type); if ( $value != '' && $is_numeric_type ) { $tc_value = $value; settype($tc_value, $field_type); // Type casing is considered a success only, when type casted value visually looks the same. return (string)$value === (string)$tc_value ? $tc_value : false; } } return $value; } /** * Formats number, according to regional settings * * @param string $number * @return float */ function formatNumber($number) { static $comma = null, $thousands = null; if ( !isset($comma) || !isset($thousands) ) { /** @var LanguagesItem $lang */ $lang = $this->Application->recallObject('lang.current'); $comma = $lang->GetDBField('DecimalPoint'); $thousands = $lang->GetDBField('ThousandSep'); } $number = str_replace($thousands, '', $number); $number = str_replace($comma, '.', $number); return $number; } /** * Applies type casting on each array element * @param Array $src * @param kDBItem|kDBList|kDBBase $object * @return Array * @access public */ public function TypeCastArray($src, &$object) { $dst = array (); foreach ($src as $id => $row) { $tmp_row = array (); foreach ($row as $fld => $value) { $field_options = $object->GetFieldOptions($fld); $tmp_row[$fld] = $this->TypeCast($value, $field_options); } $dst[$id] = $tmp_row; } return $dst; } /** * Formats value of a given field * * @param string $value * @param string $field_name * @param kDBItem|kDBList|kDBBase $object * @param string $format * @return string */ function Format($value, $field_name, &$object, $format = null) { if ( is_null($value) ) { return ''; } $options = $object->GetFieldOptions($field_name); if (!isset($format) && array_key_exists('format', $options)) { $format = $options['format']; } if ($value === false) { // used ? return $value; // for leaving badly formatted date on the form } $original_format = $format; if (isset($format)) { if (strpos($format, 'fck_ready') !== false) { $format = trim(str_replace('fck_ready', '', $format), ';'); } } if (isset($format) && $format) { if ( substr($format, -1) === 'f' ) { // High precision formats (e.g. '%01.20f') are not supported to keep code below as fast as possible. $value = round($value, substr($format, -2, 1)); } $value = sprintf($format, $value); - if ( isset($options['cut_zeros']) && $options['cut_zeros'] ) { + if ( isset($options['keep_cents']) && $options['keep_cents'] ) { + preg_match('/(\d+\.\d{' . $options['keep_cents'] . '})(\d+)?/', $value, $regs); + + // When "keep_cents" option matches format's precision, there will be an "Undefined index: 2" notice. + if ( rtrim($regs[2], '0') === '' ) { + $value = $regs[1]; + } + else { + // Remove trailing zeros in decimal part. + $value = preg_replace('/(\.\d+?)0+$/', '$1', $value); + } + } + elseif ( isset($options['cut_zeros']) && $options['cut_zeros'] ) { // Remove trailing zeros in decimal part (including "." if any left at the end). $value = preg_replace('/\.0+$/', '', $value); $value = preg_replace('/(\.\d+?)0+$/', '$1', $value); } } if (preg_match('#int|integer|double|float|real|numeric#', $options['type'])) { /** @var LanguagesItem $lang */ $lang = $this->Application->recallObject('lang.current'); return $lang->formatNumber($value); } elseif ($options['type'] == 'string') { $value = $this->_replaceFCKLinks($value, $options, $original_format); } return $value; } /** * Performs basic type validation on form field value * * @param mixed $value * @param string $field_name * @param kDBItem|kDBList|kDBBase $object * @return mixed * @access public */ public function Parse($value, $field_name, &$object) { $options = $object->GetFieldOptions($field_name); if ($value == '') { return $options['type'] == 'string' ? $value : null; } $tc_value = $this->TypeCast($value, $options); if ($tc_value === false) { return $value; // for leaving badly formatted date on the form } if(isset($options['type'])) { if (preg_match('#double|float|real|numeric#', $options['type'])) { $tc_value = str_replace(',', '.', $tc_value); } } if (isset($options['regexp'])) { if (!preg_match($options['regexp'], $value)) { $object->SetError($field_name, 'invalid_format'); } } return $tc_value; } function HumanFormat($format) { return $format; } /** * The method is supposed to alter config options or cofigure object in some way based on its usage of formatters * The methods is called for every field with formatter defined when configuring item. * Could be used for adding additional VirtualFields to an object required by some special Formatter * * @param string $field_name * @param array $field_options * @param kDBBase $object */ function PrepareOptions($field_name, &$field_options, &$object) { } /** * Used for split fields like timestamp -> date, time * Called from DBItem to update sub fields values after loading item * * @param string $field * @param string $value * @param Array $options * @param kDBItem|kDBList|kDBBase $object * @return void * @access public */ public function UpdateSubFields($field, $value, &$options, &$object) { } /** * Used for split fields like timestamp -> date, time * Called from DBItem Validate (before validation) to get back master field value from its sub_fields * * @param string $field * @param mixed $value * @param Array $options * @param kDBItem|kDBList|kDBBase $object */ function UpdateMasterFields($field, $value, &$options, &$object) { } /** * Return sample value, that can be entered in this field * * @param string $field * @param Array $options * @param kDBItem|kDBList|kDBBase $object * @return string * @access public */ public function GetSample($field, &$options, &$object) { return isset($options['sample_value']) ? $options['sample_value'] : ''; } } Index: branches/5.2.x/core/tests/kernel/utility/formatters/kFormatterTest.php =================================================================== --- branches/5.2.x/core/tests/kernel/utility/formatters/kFormatterTest.php (revision 0) +++ branches/5.2.x/core/tests/kernel/utility/formatters/kFormatterTest.php (revision 16763) @@ -0,0 +1,63 @@ +prophesize('kDBItem'); + $item_prophecy->GetFieldOptions('FieldName')->willReturn(array( + 'type' => 'float', + 'format' => '%01.6f', + ) + $field_options); + $item = $item_prophecy->reveal(); + + $this->assertSame($formatted_number, $formatter->Format($raw_number, 'FieldName', $item)); + } + + public function cutZerosDataProvider() + { + return array( + 'long' => array('12.345000', '12.345', array('cut_zeros' => 1)), + 'short' => array('12.300000', '12.3', array('cut_zeros' => 1)), + 'exact+leading_zero' => array('12.030000', '12.03', array('cut_zeros' => 1)), + 'very_long' => array('12.030050', '12.03005', array('cut_zeros' => 1)), + 'no_decimals' => array('12.000000', '12', array('cut_zeros' => 1)), + ); + } + + /** + * @dataProvider keepCentsDataProvider + */ + public function testKeepCents($raw_number, $formatted_number, array $field_options) + { + $formatter = new kFormatter(); + + $item_prophecy = $this->prophesize('kDBItem'); + $item_prophecy->GetFieldOptions('FieldName')->willReturn(array( + 'type' => 'float', + 'format' => '%01.6f', + ) + $field_options); + $item = $item_prophecy->reveal(); + + $this->assertSame($formatted_number, $formatter->Format($raw_number, 'FieldName', $item)); + } + + public function keepCentsDataProvider() + { + return array( + 'long' => array('12.345000', '12.345', array('keep_cents' => 2)), + 'short' => array('12.300000', '12.30', array('keep_cents' => 2)), + 'exact+leading_zero' => array('12.030000', '12.03', array('keep_cents' => 2)), + 'very_long' => array('12.030050', '12.03005', array('keep_cents' => 2)), + 'no_decimals' => array('12.000000', '12.00', array('keep_cents' => 2)), + ); + } + +} Property changes on: branches/5.2.x/core/tests/kernel/utility/formatters/kFormatterTest.php ___________________________________________________________________ Added: svn:keywords + Id Added: svn:eol-style + LF