Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Merge branch 'master' of github.com:radig/locale

Conflicts:
	libs/localize.php
  • Loading branch information...
commit ba6bfab17189057bcf17890620bb0b040c71abb1 2 parents 3cea00d + caf13a8
@CauanCabral CauanCabral authored
View
55 libs/localize.php
@@ -56,9 +56,12 @@ public static function getInstance()
*/
static public function setLocale($locale)
{
- if(!setlocale(LC_ALL, $locale))
+ if(!setlocale(LC_ALL, array($locale . '.utf-8', $locale)))
throw new LocaleException("Locale {$locale} não disponível no seu sistema.");
+ if(!isset(Formats::$output[$locale]))
+ throw new LocaleException("Localização '{$locale}' não possuí formatação definida. Tente adicionar o formato antes de usa-lo.");
+
self::$currentLocale = $locale;
return self::getInstance();
@@ -145,22 +148,14 @@ static public function dateLiteral($value, $displayTime = false, $format = null)
*/
static public function currency($value)
{
- if(!is_string($value))
- {
- setlocale(LC_NUMERIC, 'en_US');
- $value = (string)$value;
- setlocale(LC_NUMERIC, self::$currentLocale);
- }
-
$currentFormat = localeconv();
- $value = str_replace(',', '', $value);
- if(empty($value) || !is_numeric($value))
- return $value;
+ $number = Utils::numberFormat($value, 2, true, $currentFormat['mon_decimal_point'], $currentFormat['mon_thousands_sep']);
- $currency = $currentFormat['currency_symbol'] . ' ' . self::number($value, 2, true);
+ if($number === false)
+ return $value;
- return $currency;
+ return $currentFormat['currency_symbol'] . ' ' . $number;
}
/**
@@ -179,40 +174,12 @@ static function number($value, $precision = null, $thousands = false)
if($precision === null)
$precision = 2;
- if(!is_string($value))
- {
- setlocale(LC_NUMERIC, 'en_US');
- $value = (string)$value;
- setlocale(LC_NUMERIC, self::$currentLocale);
- }
-
$currentFormat = localeconv();
- $value = (string)$value;
- $value = str_replace(',', '', $value);
-
- $parts = explode('.', $value);
+ $number = Utils::numberFormat($value, $precision, $thousands, $currentFormat['decimal_point'], $currentFormat['thousands_sep']);
- if(count($parts) == 2)
- {
- $int = (string)$parts[0];
- $dec = str_pad((string)$parts[1], $precision, '0', STR_PAD_RIGHT);
- }
- else
- {
- $int = (string)$parts[0];
- $dec = str_repeat('0', $precision);
- }
-
- $dec = substr($dec, 0, $precision);
-
- if($thousands)
- $int = number_format($int, 0, $currentFormat['decimal_point'], $currentFormat['thousands_sep']);
-
- $number = $int;
-
- if(!empty($dec))
- $number .= $currentFormat['decimal_point'] . $dec;
+ if($number === false)
+ return $value;
return $number;
}
View
14 libs/unlocalize.php
@@ -56,9 +56,12 @@ public static function getInstance()
*/
static public function setLocale($locale)
{
- if(!setlocale(LC_ALL, $locale))
+ if(!setlocale(LC_ALL, array($locale . '.utf-8', $locale)))
throw new LocaleException("Locale {$locale} não disponível no seu sistema.");
+ if(!isset(Formats::$input[$locale]))
+ throw new LocaleException("Localização '{$locale}' não possuí formatação definida. Tente adicionar o formato antes de usa-lo.");
+
self::$currentLocale = $locale;
return self::getInstance();
@@ -134,13 +137,10 @@ static public function decimal($value)
if(empty($value))
return $value;
- $oldLocale = setlocale(LC_NUMERIC, "0");
- setlocale(LC_NUMERIC, self::$currentLocale);
+ $currentFormat = localeconv();
$v = (string)$value;
- $currentFormat = localeconv();
-
$integer = $v;
$decimal = 0;
@@ -150,15 +150,13 @@ static public function decimal($value)
$decimal = substr($v, $decimalPoint + 1);
$integer = substr($v, 0, $decimalPoint);
- $integer = preg_replace('/[\.|,]/', '', $integer);
+ $integer = str_replace(array($currentFormat['thousands_sep'], $currentFormat['mon_thousands_sep']), '', $integer);
}
$value = $integer;
if($decimal > 0)
$value .= '.' . $decimal;
- setlocale(LC_NUMERIC, $oldLocale);
-
return $value;
}
View
105 libs/utils.php
@@ -71,4 +71,109 @@ static public function initDateTime($value)
return new DateTime();
}
}
+
+ /**
+ * Replacement function for number_format, with extras:
+ * - Use truncate over round
+ * - Optional use of thousands
+ *
+ * @param mixed $value
+ * @param int $precision
+ * @param bool $thousands
+ * @param string $decimalSep
+ * @param string $thousandSep
+ *
+ * @return mixed String numeric representation in success and False boolean on
+ * invalid numeric values.
+ */
+ static public function numberFormat($value, $precision = null, $thousands = false, $decimalSep = '.', $thousandSep = ',')
+ {
+ if($precision === null)
+ $precision = 2;
+
+ if(!is_string($value))
+ {
+ $oldLocale = setlocale(LC_NUMERIC, "");
+ setlocale(LC_NUMERIC, 'en_US');
+ $value = (string)$value;
+ setlocale(LC_NUMERIC, $oldLocale);
+ }
+
+ $value = str_replace(',', '', $value);
+
+ if(empty($value) || !is_numeric($value))
+ return false;
+
+ $parts = explode('.', $value);
+
+ if(count($parts) == 2)
+ {
+ $int = (string)$parts[0];
+ $dec = str_pad((string)$parts[1], $precision, '0', STR_PAD_RIGHT);
+ }
+ else
+ {
+ $int = (string)$parts[0];
+ $dec = str_repeat('0', $precision);
+ }
+
+ $dec = substr($dec, 0, $precision);
+
+ if($thousands)
+ {
+ $copy = '';
+ for($l = strlen($int) - 1, $c = 0; $l >= 0; $l--, $c++)
+ {
+ $copy = $int[$l] . $copy;
+
+ if($c === 2 && $l > 0)
+ {
+ $c = -1;
+ $copy = $thousandSep . $copy;
+ }
+ }
+ $int = $copy;
+ }
+
+ $number = $int;
+
+ if(!empty($dec))
+ $number .= $decimalSep . $dec;
+
+ return $number;
+ }
+
+ /**
+ * Extract Model and field name from a conditions
+ * consult string.
+ *
+ * @param string $raw Query correspondent field
+ * @return array Two position array with Model and field name
+ * respectively
+ */
+ static public function parseModelField($raw)
+ {
+ $validField = '/^([a-zA-Z][a-zA-Z0-9]*)(\\.[a-zA-Z][a-zA-Z0-9]*)?/';
+ $matched = array();
+
+ $field = $raw;
+ $modelName = '';
+
+ if(preg_match($validField, $raw, $matched) === 1)
+ {
+ $field = $matched[0];
+
+ // If condition have Model.field sintax
+ if(strpos($field, '.') !== false)
+ {
+ $ini = strpos($field, '.');
+
+ $modelName = substr($field, 0, $ini);
+ $field = substr($field, $ini + 1);
+ }
+
+ }
+
+ return array($modelName, $field);
+ }
}
View
248 models/behaviors/locale.php
@@ -1,7 +1,7 @@
<?php
App::import('CORE', 'ConnectionManager');
App::import('Lib', 'Locale.Unlocalize');
-
+App::import('Lib', 'Locale.Utils');
/**
* Behavior to automagic convert dates, numbers and currency from
* any localized format to DB format for security store.
@@ -30,7 +30,14 @@ class LocaleBehavior extends ModelBehavior
* Referência para o modelo que está utilizando o behavior
* @var Model
*/
- protected $model;
+ protected $_Model;
+
+ /**
+ * Lista de campos com o seu respectivo tipo para o modelo em uso
+ *
+ * @var array
+ */
+ private $_modelFields;
/**
* Lista de campos que devem ser ignorados por serem inseridos
@@ -56,6 +63,10 @@ class LocaleBehavior extends ModelBehavior
*/
private $systemLang;
+ private $_defaultSettings = array(
+ 'ignoreAutomagic' => true
+ );
+
/**
* Inicializa os dados do behavior
*
@@ -63,26 +74,42 @@ class LocaleBehavior extends ModelBehavior
*/
public function setup(&$model, $config = array())
{
- $this->settings = array(
- 'ignoreAutomagic' => true
- );
-
- $this->model =& $model;
- $this->settings = Set::merge($this->settings, $config);
+ $this->_Model =& $model;
$this->systemLang = substr(setlocale(LC_ALL, "0"), 0, 5);
- $db =& ConnectionManager::getDataSource($this->model->useDbConfig);
+ $this->__checkConfig($model, $config);
+ }
+
+ /**
+ * Check and set missing configurations
+ *
+ * @param Model $model
+ * @param array $config
+ * @return void
+ */
+ private function __checkConfig(Model $model, $config = null)
+ {
+ if($config !== null)
+ $this->settings[$model->alias] = Set::merge($this->_defaultSettings, $config);
+
+ if(!isset($this->_modelFields[$model->alias]))
+ $this->_modelFields[$model->alias] = $model->getColumnTypes();
+
+ if(isset($this->typesFormat[$model->useDbConfig]))
+ return;
+
+ $db =& ConnectionManager::getDataSource($model->useDbConfig);
+ $this->typesFormat[$model->useDbConfig] = array();
foreach($db->columns as $type => $info)
{
if(isset($info['format']))
- {
- $this->typesFormat[$type] = $info['format'];
- }
+ $this->typesFormat[$model->useDbConfig][$type] = $info['format'];
}
}
+
/**
* Invoca localização das informações no callback beforeValidate
*
@@ -90,9 +117,10 @@ public function setup(&$model, $config = array())
*/
public function beforeValidate(&$model)
{
- $this->model =& $model;
-
parent::beforeValidate($model);
+ $this->_Model =& $model;
+
+ $this->__checkConfig($model);
return $this->localizeData();
}
@@ -104,9 +132,10 @@ public function beforeValidate(&$model)
*/
public function beforeSave(&$model)
{
- $this->model =& $model;
-
parent::beforeSave($model);
+ $this->_Model =& $model;
+
+ $this->__checkConfig($model);
return $this->localizeData();
}
@@ -118,9 +147,10 @@ public function beforeSave(&$model)
*/
public function beforeFind(&$model, $query)
{
- $this->model =& $model;
-
parent::beforeFind($mode, $query);
+ $this->_Model =& $model;
+
+ $this->__checkConfig($model);
$this->localizeData($query['conditions']);
@@ -139,87 +169,100 @@ public function beforeFind(&$model, $query)
*/
public function localizeData(&$query = null)
{
- $status = true;
-
// verifica se há dados setados no modelo
- if(isset($this->model->data) && !empty($this->model->data))
+ if(isset($this->_Model->data) && !empty($this->_Model->data))
{
- // varre os dados setados
- foreach($this->model->data[$this->model->name] as $field => $value)
- {
- // caso o campo esteja vazio E não tenha um array como valor E o campo faz parte do schema
- if(!empty($value) && !is_array($value) && isset($this->model->_schema[$field]) && (!$this->settings['ignoreAutomagic'] || ($this->settings['ignoreAutomagic'] && !in_array($field, $this->cakeAutomagicFields))))
- {
- switch($this->model->_schema[$field]['type'])
- {
- case 'date':
- case 'datetime':
- case 'time':
- case 'timestamp':
- $status = ($status && $this->__dateConvert($this->model->data[$this->model->name][$field], $this->model->_schema[$field]['type']));
- break;
- case 'number':
- case 'decimal':
- case 'float':
- case 'double':
- $status = ($status && $this->__stringToFloat($this->model->data[$this->model->name][$field]));
- break;
- }
- }
- }
+ return $this->__modelLocalize();
}
// caso tenha sido invocado em um Find (haja query de busca)
if(!empty($query) && is_array($query))
{
- // varre os campos da condição
- foreach($query as $field => &$value)
+ return $this->__queryLocalize($query);
+ }
+
+ return true;
+ }
+
+ /**
+ * Localize data of Model->data array.
+ *
+ * @return bool $success
+ */
+ private function __modelLocalize()
+ {
+ $status = true;
+
+ foreach($this->_Model->data[$this->_Model->alias] as $field => $value)
+ {
+ if($this->__isUnLocalizableField($this->_Model, $field))
{
- if(strtolower($field) === 'or' || strtolower($field) === 'and' || is_numeric($field))
+ switch($this->_modelFields[$this->_Model->alias][$field])
{
- $status = $status && $this->localizeData($value);
- continue;
+ case 'date':
+ case 'datetime':
+ case 'timestamp':
+ $status = ($status && $this->__dateConvert($this->_Model->data[$this->_Model->alias][$field], $this->_modelFields[$this->_Model->alias][$field]));
+ break;
+ case 'number':
+ case 'decimal':
+ case 'float':
+ case 'double':
+ $status = ($status && $this->__decimal($this->_Model->data[$this->_Model->alias][$field]));
+ break;
}
+ }
+ }
- // caso sejam campos com a notação Model.field
- if(strpos($field, '.') !== false)
- {
- $ini = strpos($field, '.');
- $len = strpos($field, ' ');
+ return $status;
+ }
+
+ /**
+ * Localize data of Model->find() 'conditions' array
+ *
+ * @param array $query Model->find conditions
+ * @return bool $success
+ */
+ private function __queryLocalize(&$query)
+ {
+ $status = true;
- $modelName = substr($field, 0, $ini - 1);
+ // don't support directly written SQL
+ if(!is_array($query))
+ return true;
- if($len !== false)
- $field = substr($field, $ini + 1, $len - $ini - 1);
- else
- $field = substr($field, $ini + 1);
- }
+ foreach($query as $field => &$value)
+ {
+ if(strtolower($field) === 'or' || strtolower($field) === 'and' || is_numeric($field))
+ {
+ $status = $status && $this->__queryLocalize($value);
+ continue;
+ }
- // caso o campo esteja vazio E não tenha um array como valor E o campo faz parte do schema
- if(!empty($value) && isset($this->model->_schema[$field]) && (!$this->settings['ignoreAutomagic'] || ($this->settings['ignoreAutomagic'] && !in_array($field, $this->cakeAutomagicFields))))
+ list($modelName, $field) = Utils::parseModelField($field);
+
+ if($this->__isUnLocalizableField($this->_Model, $field, $value))
+ {
+ switch($this->_modelFields[$this->_Model->alias][$field])
{
- switch($this->model->_schema[$field]['type'])
- {
- case 'date':
- case 'datetime':
- case 'time':
- case 'timestamp':
- if(is_array($value))
- foreach($value as &$v)
- $status = ($status && $this->__dateConvert($v, $this->model->_schema[$field]['type']));
- else
- $status = ($status && $this->__dateConvert($value, $this->model->_schema[$field]['type']));
- break;
- case 'decimal':
- case 'float':
- case 'double':
- if(is_array($value))
- foreach($value as &$v)
- $status = ($status && $this->__stringToFloat($v));
- else
- $status = ($status && $this->__stringToFloat($value));
- break;
- }
+ case 'date':
+ case 'datetime':
+ case 'timestamp':
+ if(is_array($value))
+ foreach($value as &$v)
+ $status = ($status && $this->__dateConvert($v, $this->_modelFields[$this->_Model->alias][$field]));
+ else
+ $status = ($status && $this->__dateConvert($value, $this->_modelFields[$this->_Model->alias][$field]));
+ break;
+ case 'decimal':
+ case 'float':
+ case 'double':
+ if(is_array($value))
+ foreach($value as &$v)
+ $status = ($status && $this->__decimal($v));
+ else
+ $status = ($status && $this->__decimal($value));
+ break;
}
}
}
@@ -243,8 +286,12 @@ private function __dateConvert(&$value, $type = 'date')
try
{
$d = Unlocalize::setLocale($this->systemLang)->date($value);
+
+ if(empty($d))
+ return $value;
+
$dt = new DateTime($d);
- $value = $dt->format($this->typesFormat[$type]);
+ $value = $dt->format($this->typesFormat[$this->_Model->useDbConfig][$type]);
}
catch(Exception $e)
{
@@ -266,10 +313,37 @@ private function __dateConvert(&$value, $type = 'date')
* @param string $value
* @return bool success
*/
- private function __stringToFloat(&$value)
+ private function __decimal(&$value)
{
$value = Unlocalize::setLocale($this->systemLang)->decimal($value);
return true;
}
-}
+
+ /**
+ * Check if a field is 'un-localizable'
+ *
+ * @param Model $model
+ * @param string $field
+ * @return bool
+ */
+ private function __isUnLocalizableField($model, $field, $value = null)
+ {
+ if(!isset($this->settings[$model->alias]))
+ return false;
+
+ if($value === null && isset($model->data[$model->alias][$field]) && empty($model->data[$model->alias][$field]))
+ return false;
+
+ if($value !== null && empty($value))
+ return false;
+
+ if(!isset($this->_modelFields[$model->alias][$field]))
+ return false;
+
+ if($this->settings[$model->alias]['ignoreAutomagic'] && in_array($field, $this->cakeAutomagicFields))
+ return false;
+
+ return true;
+ }
+}
View
42 tests/cases/libs/utils.test.php
@@ -21,6 +21,9 @@ class UnlocalizeCase extends CakeTestCase
public function startCase()
{
parent::startCase();
+
+ // data input always in en_US
+ setlocale(LC_ALL, 'en_US');
}
/**
@@ -69,4 +72,43 @@ public function testInitDateTime()
$this->assertEqual(Utils::initDateTime('2012-04-16'), new DateTime('2012-04-16'));
$this->assertEqual(Utils::initDateTime('2012-04-16 09:37:45'), new DateTime('2012-04-16 09:37:45'));
}
+
+ public function testNumberFormat()
+ {
+ $this->assertEqual(Utils::numberFormat(1), '1.00');
+ $this->assertEqual(Utils::numberFormat('1.5'), '1.50');
+ $this->assertEqual(Utils::numberFormat(1.5), '1.50');
+ $this->assertEqual(Utils::numberFormat(1.534), '1.53');
+
+ $this->assertEqual(Utils::numberFormat(1.534, 3), '1.534');
+
+ $this->assertEqual(Utils::numberFormat('1,234.56'), '1234.56');
+ $this->assertEqual(Utils::numberFormat('1,234.56', null, true), '1,234.56');
+ }
+
+ public function testLocalizedNumberFormat()
+ {
+ setlocale(LC_NUMERIC, "pt_BR");
+ $val = 12.21;
+
+ $this->assertEqual((string)$val, '12,21');
+ $this->assertEqual(Utils::numberFormat($val), '12.21');
+ }
+
+ public function testParseModelField()
+ {
+ $this->assertEqual(Utils::parseModelField('ModelName.field'), array('ModelName', 'field'));
+ $this->assertEqual(Utils::parseModelField('Model0Name.field'), array('Model0Name', 'field'));
+ $this->assertEqual(Utils::parseModelField('ModelName.field <'), array('ModelName', 'field'));
+ $this->assertEqual(Utils::parseModelField('ModelName.field >='), array('ModelName', 'field'));
+ $this->assertEqual(Utils::parseModelField('ModelName.field %'), array('ModelName', 'field'));
+
+ $this->assertEqual(Utils::parseModelField('field'), array('', 'field'));
+ $this->assertEqual(Utils::parseModelField('field <'), array('', 'field'));
+ $this->assertEqual(Utils::parseModelField('field >='), array('', 'field'));
+ $this->assertEqual(Utils::parseModelField('field %'), array('', 'field'));
+
+ // Invalid formats
+ $this->assertEqual(Utils::parseModelField('Model_Name.field'), array('', 'Model'));
+ }
}
Please sign in to comment.
Something went wrong with that request. Please try again.