Issues

ZF-3423: Validate_Float doesn't work with local than doesn't have a dot as decimal separator

Description

The php function strval use the decimal point character that is defined in the script's locale (category LC_NUMERIC), so depending on the local set in PHP.

For instance, strval(1.2) return "1,2" in "fr_FR.utf8" locale.

So basicaly the isValid() function of the Zend_Validate_Float class does this :


        $locale = localeconv();
        
        $valueFiltered = str_replace($locale['thousands_sep'], '', $valueString);
        $valueFiltered = str_replace($locale['decimal_point'], '.', $valueFiltered);
        if (strval(floatval($valueFiltered)) != $valueFiltered) {
          /* only true if the $locale['thousands_sep'] is already '' and $locale['decimal_point'] is already '.' */

Here, the code replace the locale decimal point by '.' and remove the locale thousand separator and then compare this string to the same string, convertred to float and displayed using that same locale decimal point and locale thousand separator ... well, the only chance for this to work is if the locale decimal point IS already a '.' and the locale thousand separator is ''.

I think this should be enough to test if a value is a float :


if (false===ereg('^[0-9]*\.[0-9]+$', $valueFiltered)){
  /* not a float */
}

Comments

Attention: You should also take in account that users may have set Zend_Locale to a different locale than the one from setlocale (setlocale is not threadsave).

So we could have system set to french and user setting arabic. Validate should be able also to verify such.

Easiest solution: Allow a locale to be set Use Zend_Locale to normalize the value.

This should be resolve this issue. It matches old value with converted from string to float and again to string old value.

The patch does not work when the user is using Zend_Form with an application locale. Input could be "1,2" even it system locale is set to "en" because the application locale is set to "fr".

The validation would still fail. Therefor this fix does not solve the problem, only part of it.

This is the same patch which I commented... there is no difference.

Hi,

I use this 'solution'. Maybe it isn't a correct one but it works for me in the application.

<?php

class Application_Validate_Float extends Zend_Validate_Float {
    
    const NOT_FLOAT = 'notFloat';

    /**
     * Message templates.
     * 
     * @var array
     */
    protected $_messageTemplates = array(
        self::NOT_FLOAT => "'%value%' does not appear to be a float"
    );

    /**
     * Locale
     *
     * @var Zend_Locale
     */    
    protected $_locale;
    
    /**
     * Constructor
     *
     * @param string|Zend_Locale|null $locale
     */
    public function __construct($locale = null) {
        
        $this->setLocale($locale);
        
    }
    
    
    /**
     * Returns the locale option
     *
     * @return string|Zend_Locale|null
     */
    public function getLocale()
    {
        return $this->_locale;
    }

    /**
     * Sets the locale option
     *
     * @param  string|Zend_Locale $locale
     * @return Zend_Validate_Date provides a fluent interface
     */
    public function setLocale($locale = null)
    {
        if ($locale === null) {
            $this->_locale = null;
            return $this;
        }

        require_once 'Zend/Locale.php';
        if (!Zend_Locale::isLocale($locale, true, false)) {
            if (!Zend_Locale::isLocale($locale, false, false)) {
                require_once 'Zend/Validate/Exception.php';
                throw new Zend_Validate_Exception("The locale '$locale' is no known locale");
            }

            $locale = new Zend_Locale($locale);
        }

        $this->_locale = (string) $locale;
        return $this;
    }
    
    
    /**
     * Defined by Zend_Validate_Interface
     *
     * Returns true if and only if $value is a floating-point value
     *
     * @param  string $value
     * @return boolean
     */
    public function isValid($value)
    {
        $valueString = (string) $value;

        $this->_setValue($valueString);

//        // ORIGINAL FROM ZEND_FLOAT (ZF 1.7.0)
//        $locale = localeconv();
//
//        $valueFiltered = str_replace($locale['thousands_sep'], '', $valueString);
//        $valueFiltered = str_replace($locale['decimal_point'], '.', $valueFiltered);
// 
//        if (strval(floatval($valueFiltered)) != $valueFiltered) {
//            $this->_error();
//            return false;
//        }

        // Locale aware.
        if(!Zend_Locale_Format::isFloat($value, array('locale' => $locale))) { 
            $this->_error(); 
            return false; 
        }
        
        return true;
    }
    
}
?>

Yes, this solution is the right direction. It can be used as workaround and shows what I said to be the solution.

There are only part details which will be changed by me.

Thanks Sven :-)

New feature added with r13372