diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index 21d57da5..00000000 --- a/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "tools/phptools"] - path = tools/phptools - url = git://github.com/ralphschindler/PHPTools.git diff --git a/composer.json b/composer.json index ccf45d32..600e968a 100644 --- a/composer.json +++ b/composer.json @@ -1,6 +1,6 @@ { "name": "zendframework/zend-i18n", - "description": "Zend\\I18n component", + "description": " ", "license": "BSD-3-Clause", "keywords": [ "zf2", @@ -9,12 +9,13 @@ "homepage": "https://github.com/zendframework/zend-i18n", "autoload": { "psr-4": { - "Zend\\I18n\\": "src/" + "Zend\\I18n": "src/" } }, "require": { - "php": ">=5.3.23", - "zendframework/zend-stdlib": "self.version" + "php": ">=5.3.3", + "zendframework/zend-stdlib": "self.version", + "ext-intl": "*" }, "require-dev": { "zendframework/zend-cache": "self.version", diff --git a/src/Exception/ExceptionInterface.php b/src/Exception/ExceptionInterface.php index 1c91479a..a13a9c0a 100644 --- a/src/Exception/ExceptionInterface.php +++ b/src/Exception/ExceptionInterface.php @@ -1,22 +1,11 @@ options['locale'] = $locale; + return $this; + } + + /** + * Returns the locale option + * + * @return string + */ + public function getLocale() + { + if (!isset($this->options['locale'])) { + $this->options['locale'] = Locale::getDefault(); + } + return $this->options['locale']; + } +} diff --git a/src/Filter/Alnum.php b/src/Filter/Alnum.php new file mode 100644 index 00000000..69d55afe --- /dev/null +++ b/src/Filter/Alnum.php @@ -0,0 +1,97 @@ + null, + 'allow_white_space' => false, + ); + + /** + * Sets default option values for this instance + * + * @param array|Traversable|boolean|null $allowWhiteSpaceOrOptions + * @param string|null $locale + */ + public function __construct($allowWhiteSpaceOrOptions = null, $locale = null) + { + if ($allowWhiteSpaceOrOptions !== null) { + if (static::isOptions($allowWhiteSpaceOrOptions)){ + $this->setOptions($allowWhiteSpaceOrOptions); + } else { + $this->setAllowWhiteSpace($allowWhiteSpaceOrOptions); + $this->setLocale($locale); + } + } + } + + /** + * Sets the allowWhiteSpace option + * + * @param boolean $flag + * @return Alnum Provides a fluent interface + */ + public function setAllowWhiteSpace($flag = true) + { + $this->options['allow_white_space'] = (boolean) $flag; + return $this; + } + + /** + * Whether white space is allowed + * + * @return boolean + */ + public function getAllowWhiteSpace() + { + return $this->options['allow_white_space']; + } + + /** + * Defined by Zend\Filter\FilterInterface + * + * Returns $value as string with all non-alphanumeric characters removed + * + * @param mixed $value + * @return string + */ + public function filter($value) + { + $whiteSpace = $this->options['allow_white_space'] ? '\s' : ''; + $language = Locale::getPrimaryLanguage($this->getLocale()); + + if (!static::hasPcreUnicodeSupport()) { + // POSIX named classes are not supported, use alternative a-zA-Z0-9 match + $pattern = '/[^a-zA-Z0-9' . $whiteSpace . ']/'; + } elseif ($language == 'ja'|| $language == 'ko' || $language == 'zh') { + // Use english alphabet + $pattern = '/[^a-zA-Z0-9' . $whiteSpace . ']/u'; + } else { + // Use native language alphabet + $pattern = '/[^\p{L}\p{N}' . $whiteSpace . ']/u'; + } + + return preg_replace($pattern, '', (string) $value); + } +} diff --git a/src/Filter/Alpha.php b/src/Filter/Alpha.php new file mode 100644 index 00000000..8c619305 --- /dev/null +++ b/src/Filter/Alpha.php @@ -0,0 +1,47 @@ +options['allow_white_space'] ? '\s' : ''; + $language = Locale::getPrimaryLanguage($this->getLocale()); + + if (!static::hasPcreUnicodeSupport()) { + // POSIX named classes are not supported, use alternative [a-zA-Z] match + $pattern = '/[^a-zA-Z' . $whiteSpace . ']/'; + } elseif ($language == 'ja' || $language == 'ko' || $language == 'zh') { + // Use english alphabet + $pattern = '/[^a-zA-Z' . $whiteSpace . ']/u'; + } else { + // Use native language alphabet + $pattern = '/[^\p{L}' . $whiteSpace . ']/u'; + } + + return preg_replace($pattern, '', (string) $value); + } +} diff --git a/src/Filter/NumberFormat.php b/src/Filter/NumberFormat.php new file mode 100644 index 00000000..48b40ed5 --- /dev/null +++ b/src/Filter/NumberFormat.php @@ -0,0 +1,158 @@ + null, + 'style' => NumberFormatter::DEFAULT_STYLE, + 'type' => NumberFormatter::TYPE_DOUBLE + ); + + /** + * @var NumberFormatter + */ + protected $formatter = null; + + /** + * @param array|Traversable|string|null $localeOrOptions + * @param int $style + * @param int $type + */ + public function __construct( + $localeOrOptions = null, + $style = NumberFormatter::DEFAULT_STYLE, + $type = NumberFormatter::TYPE_DOUBLE) + { + if ($localeOrOptions !== null) { + if ($localeOrOptions instanceof Traversable) { + $localeOrOptions = iterator_to_array($localeOrOptions); + } + + if (!is_array($localeOrOptions)) { + $this->setLocale($localeOrOptions); + $this->setStyle($style); + $this->setType($type); + } else { + $this->setOptions($localeOrOptions); + } + } + } + + /** + * @param string|null $locale + * @return NumberFormat + */ + public function setLocale($locale = null) + { + $this->options['locale'] = $locale; + $this->formatter = null; + return $this; + } + + /** + * @param int $style + * @return NumberFormat + */ + public function setStyle($style) + { + $this->options['style'] = (int) $style; + $this->formatter = null; + return $this; + } + + /** + * @return int + */ + public function getStyle() + { + return $this->options['style']; + } + + /** + * @param int $type + * @return NumberFormat + */ + public function setType($type) + { + $this->options['type'] = (int) $type; + return $this; + } + + /** + * @return int + */ + public function getType() + { + return $this->options['type']; + } + + /** + * @param NumberFormatter $formatter + * @return NumberFormat + */ + public function setFormatter(NumberFormatter $formatter) + { + $this->formatter = $formatter; + return $this; + } + + /** + * @return NumberFormatter + * @throws Exception\RuntimeException + */ + public function getFormatter() + { + if ($this->formatter === null) { + $formatter = NumberFormatter::create($this->getLocale(), $this->getStyle()); + if (!$formatter) { + throw new Exception\RuntimeException( + 'Can not create NumberFormatter instance; ' . intl_get_error_message() + ); + } + + $this->formatter = $formatter; + } + + return $this->formatter; + } + + /** + * Defined by Zend\Filter\FilterInterface + * + * @see Zend\Filter\FilterInterface::filter() + * @param mixed $value + * @return mixed + */ + public function filter($value) + { + $formatter = $this->getFormatter(); + $type = $this->getType(); + + if (is_int($value) || is_float($value)) { + $result = @numfmt_format($formatter, $value, $type); + } else { + $value = str_replace(array("\xC2\xA0", ' '), '', $value); + $result = @numfmt_parse($formatter, $value, $type); + } + + if ($result === false) { + return $value; + } + + return str_replace("\xC2\xA0", ' ', $result); + } +} diff --git a/src/Translator/Loader/Gettext.php b/src/Translator/Loader/Gettext.php index a75e0769..c1c3cf9d 100644 --- a/src/Translator/Loader/Gettext.php +++ b/src/Translator/Loader/Gettext.php @@ -1,29 +1,18 @@ file = @fopen($filename, 'rb'); - if (!$this->file) { - throw new Exception\InvalidArgumentException( - sprintf('Could not open file %s for reading', $filename) - ); - } + $this->file = @fopen($filename, 'rb'); // Verify magic number $magic = fread($this->file, 4); - if ($magic === "\x95\x04\x12\xde") { - $this->littleEndian = true; - } elseif ($magic === "\xde\x12\x04\x95") { + if ($magic == "\x95\x04\x12\xde") { $this->littleEndian = false; + } elseif ($magic == "\xde\x12\x04\x95") { + $this->littleEndian = true; } else { fclose($this->file); - throw new Exception\InvalidArgumentException( - sprintf('%s is not a valid gettext file', $filename) - ); + throw new Exception\InvalidArgumentException(sprintf( + '%s is not a valid gettext file', + $filename + )); } // Verify major revision (only 0 and 1 supported) @@ -88,9 +79,10 @@ public function load($filename, $locale) if ($majorRevision !== 0 && $majorRevision !== 1) { fclose($this->file); - throw new Exception\InvalidArgumentException( - sprintf('%s has an unknown major revision', $filename) - ); + throw new Exception\InvalidArgumentException(sprintf( + '%s has an unknown major revision', + $filename + )); } // Gather main information @@ -100,10 +92,10 @@ public function load($filename, $locale) // Usually there follow size and offset of the hash table, but we have // no need for it, so we skip them. - fseek($originalStringTableOffset); + fseek($this->file, $originalStringTableOffset); $originalStringTable = $this->readIntegerList(2 * $numStrings); - fseek($translationStringTableOffset); + fseek($this->file, $translationStringTableOffset); $translationStringTable = $this->readIntegerList(2 * $numStrings); // Read in all translations @@ -117,16 +109,16 @@ public function load($filename, $locale) $originalString = array(''); if ($originalStringSize > 0) { - fseek($originalStringOffset); + fseek($this->file, $originalStringOffset); $originalString = explode("\0", fread($this->file, $originalStringSize)); } if ($translationStringSize > 0) { - fseek($translationStringOffset); + fseek($this->file, $translationStringOffset); $translationString = explode("\0", fread($this->file, $translationStringSize)); if (count($originalString) > 1 && count($translationString) > 1) { - $textDomain[$original[0]] = $translationString; + $textDomain[$originalString[0]] = $translationString; array_shift($originalString); @@ -134,22 +126,20 @@ public function load($filename, $locale) $textDomain[$string] = ''; } } else { - $textDomain[$original[0]] = $translationString[0]; + $textDomain[$originalString[0]] = $translationString[0]; } } } // Read header entries if (array_key_exists('', $textDomain)) { - $rawHeaders = explode("\n", $textDomain['']); + $rawHeaders = explode("\n", trim($textDomain[''])); foreach ($rawHeaders as $rawHeader) { - list($header, $content) = explode(':', $rawHeader, 1); + list($header, $content) = explode(':', $rawHeader, 2); if (trim(strtolower($header)) === 'plural-forms') { - $textDomain->pluralRule( - PluralRule::fromString($content) - ); + $textDomain->setPluralRule(PluralRule::fromString($content)); } } diff --git a/src/Translator/Loader/LoaderInterface.php b/src/Translator/Loader/LoaderInterface.php index bfde38b0..1436f8c1 100644 --- a/src/Translator/Loader/LoaderInterface.php +++ b/src/Translator/Loader/LoaderInterface.php @@ -1,36 +1,21 @@ pluralRule( + $textDomain->setPluralRule( PluralRule::fromString($textDomain['']['plural_forms']) ); } diff --git a/src/Translator/LoaderPluginManager.php b/src/Translator/LoaderPluginManager.php index e5ddba13..abbda11e 100644 --- a/src/Translator/LoaderPluginManager.php +++ b/src/Translator/LoaderPluginManager.php @@ -1,22 +1,11 @@ 'Zend\I18n\Translator\Loader\PhpArray', + 'phparray' => 'Zend\I18n\Translator\Loader\PhpArray', 'gettext' => 'Zend\I18n\Translator\Loader\Gettext', ); @@ -53,8 +40,8 @@ class LoaderPluginManager extends AbstractPluginManager * Validate the plugin * * Checks that the filter loaded is an instance of Loader\LoaderInterface. - * - * @param mixed $plugin + * + * @param mixed $plugin * @return void * @throws Exception\RuntimeException if invalid */ diff --git a/src/Translator/Plural/Parser.php b/src/Translator/Plural/Parser.php index 47072991..ebbd1c85 100644 --- a/src/Translator/Plural/Parser.php +++ b/src/Translator/Plural/Parser.php @@ -1,22 +1,11 @@ nullDenotationGetter; return $function($this); } @@ -172,6 +161,7 @@ public function getLeftDenotation($left) )); } + /** @var callable $function */ $function = $this->leftDenotationGetter; return $function($this, $left); } diff --git a/src/Translator/TextDomain.php b/src/Translator/TextDomain.php index 92568483..c2c34cf3 100644 --- a/src/Translator/TextDomain.php +++ b/src/Translator/TextDomain.php @@ -1,22 +1,11 @@ setLocale(array_shift($locales)); + if (count($locales) > 0) { + $translator->setFallbackLocale(array_shift($locales)); + } + } + + // patterns + if (isset($options['translation_patterns'])) { + if (!is_array($options['translation_patterns'])) { + throw new Exception\InvalidArgumentException( + '"translation_patterns" should be an array' + ); + } + + $requiredKeys = array('type', 'base_dir', 'pattern'); + foreach ($options['translation_patterns'] as $pattern) { + foreach ($requiredKeys as $key) { + if (!isset($pattern[$key])) { + throw new Exception\InvalidArgumentException( + "'{$key}' is missing for translation pattern options" + ); + } + } + + $translator->addTranslationPattern( + $pattern['type'], + $pattern['base_dir'], + $pattern['pattern'], + isset($pattern['text_domain']) ? $pattern['text_domain'] : 'default' + ); + } + } + + // files + if (isset($options['translation_files'])) { + if (!is_array($options['translation_files'])) { + throw new Exception\InvalidArgumentException( + '"translation_files" should be an array' + ); + } + + $requiredKeys = array('type', 'filename'); + foreach ($options['translation_files'] as $file) { + foreach ($requiredKeys as $key) { + if (!isset($file[$key])) { + throw new Exception\InvalidArgumentException( + "'{$key}' is missing for translation file options" + ); + } + } + + $translator->addTranslationFile( + $file['type'], + $file['filename'], + isset($file['text_domain']) ? $file['text_domain'] : 'default', + isset($file['locale']) ? $file['locale'] : null + ); + } + } + + // cache + if (isset($options['cache'])) { + if ($options['cache'] instanceof CacheStorage) { + $translator->setCache($options['cache']); + } else { + $translator->setCache(Cache\StorageFactory::factory($options['cache'])); + } + } + + return $translator; + } + /** * Set the default locale. * @@ -118,7 +205,7 @@ public function getLocale() */ public function setFallbackLocale($locale) { - $this->locale = $locale; + $this->fallbackLocale = $locale; return $this; } @@ -129,11 +216,7 @@ public function setFallbackLocale($locale) */ public function getFallbackLocale() { - if ($this->locale === null) { - $this->locale = Locale::getDefault(); - } - - return $this->locale; + return $this->fallbackLocale; } /** @@ -160,8 +243,8 @@ public function getCache() /** * Set the plugin manager for translation loaders - * - * @param LoaderPluginManager $pluginManager + * + * @param LoaderPluginManager $pluginManager * @return Translator */ public function setPluginManager(LoaderPluginManager $pluginManager) @@ -201,9 +284,9 @@ public function translate($message, $textDomain = 'default', $locale = null) if ($translation !== null && $translation !== '') { return $translation; - } - - if (null !== ($fallbackLocale = $this->getFallbackLocale()) + } + + if (null !== ($fallbackLocale = $this->getFallbackLocale()) && $locale !== $fallbackLocale ) { return $this->translate($message, $textDomain, $fallbackLocale); @@ -215,12 +298,13 @@ public function translate($message, $textDomain = 'default', $locale = null) /** * Translate a plural message. * - * @param type $singular - * @param type $plural - * @param type $number - * @param type $textDomain - * @param type $locale + * @param string $singular + * @param string $plural + * @param int $number + * @param string $textDomain + * @param string|null $locale * @return string + * @throws Exception\OutOfBoundsException */ public function translatePlural( $singular, @@ -230,26 +314,26 @@ public function translatePlural( $locale = null ) { $locale = $locale ?: $this->getLocale(); - $translation = $this->getTranslatedMessage($message, $locale, $textDomain); + $translation = $this->getTranslatedMessage($singular, $locale, $textDomain); if ($translation === null || $translation === '') { - if (null !== ($fallbackLocale = $this->getFallbackLocale()) + if (null !== ($fallbackLocale = $this->getFallbackLocale()) && $locale !== $fallbackLocale ) { return $this->translatePlural( - $singular, - $plural, - $number, - $textDomain, + $singular, + $plural, + $number, + $textDomain, $fallbackLocale ); } - return ($number != 1 ? $singular : $plural); + return ($number == 1 ? $singular : $plural); } $index = $this->messages[$textDomain][$locale] - ->pluralRule() + ->getPluralRule() ->evaluate($number); if (!isset($translation[$index])) { @@ -282,7 +366,7 @@ protected function getTranslatedMessage( $this->loadMessages($textDomain, $locale); } - if (!array_key_exists($message, $this->messages[$textDomain][$locale])) { + if (!isset($this->messages[$textDomain][$locale][$message])) { return null; } @@ -339,7 +423,7 @@ public function addTranslationPattern( $this->patterns[$textDomain][] = array( 'type' => $type, - 'baseDir' => rtrim($baseDir . '/'), + 'baseDir' => rtrim($baseDir, '/'), 'pattern' => $pattern, ); @@ -371,9 +455,8 @@ protected function loadMessages($textDomain, $locale) // Try to load from pattern if (isset($this->patterns[$textDomain])) { foreach ($this->patterns[$textDomain] as $pattern) { - $filename = $pattern['baseDir'] . '/' - . sprintf($pattern['pattern'], $locale); - + $filename = $pattern['baseDir'] + . '/' . sprintf($pattern['pattern'], $locale); if (is_file($filename)) { $this->messages[$textDomain][$locale] = $this->getPluginManager() ->get($pattern['type']) diff --git a/src/Translator/TranslatorAwareInterface.php b/src/Translator/TranslatorAwareInterface.php new file mode 100644 index 00000000..3427a9a5 --- /dev/null +++ b/src/Translator/TranslatorAwareInterface.php @@ -0,0 +1,76 @@ +get('Configuration'); + $translator = Translator::factory($config['translator']); + return $translator; + } +} diff --git a/src/Validator/Alnum.php b/src/Validator/Alnum.php new file mode 100644 index 00000000..deb8bcb7 --- /dev/null +++ b/src/Validator/Alnum.php @@ -0,0 +1,122 @@ + "Invalid type given. String, integer or float expected", + self::NOT_ALNUM => "The input contains characters which are non alphabetic and no digits", + self::STRING_EMPTY => "The input is an empty string", + ); + + /** + * Options for this validator + * + * @var array + */ + protected $options = array( + 'allowWhiteSpace' => false, // Whether to allow white space characters; off by default + ); + + /** + * Sets default option values for this instance + * + * @param array|\Traversable $options + */ + public function __construct($allowWhiteSpace = false) + { + $options = is_array($allowWhiteSpace) ? $allowWhiteSpace : null; + parent::__construct($options); + + if (is_scalar($allowWhiteSpace)) { + $this->options['allowWhiteSpace'] = (boolean) $allowWhiteSpace; + } + } + + /** + * Returns the allowWhiteSpace option + * + * @return boolean + */ + public function getAllowWhiteSpace() + { + return $this->options['allowWhiteSpace']; + } + + /** + * Sets the allowWhiteSpace option + * + * @param boolean $allowWhiteSpace + * @return AlnumFilter Provides a fluent interface + */ + public function setAllowWhiteSpace($allowWhiteSpace) + { + $this->options['allowWhiteSpace'] = (boolean) $allowWhiteSpace; + return $this; + } + + /** + * Returns true if and only if $value contains only alphabetic and digit characters + * + * @param string $value + * @return boolean + */ + public function isValid($value) + { + if (!is_string($value) && !is_int($value) && !is_float($value)) { + $this->error(self::INVALID); + return false; + } + + $this->setValue($value); + if ('' === $value) { + $this->error(self::STRING_EMPTY); + return false; + } + + if (null === self::$filter) { + self::$filter = new AlnumFilter(); + } + + self::$filter->setAllowWhiteSpace($this->options['allowWhiteSpace']); + + if ($value != self::$filter->filter($value)) { + $this->error(self::NOT_ALNUM); + return false; + } + + return true; + } +} diff --git a/src/Validator/Alpha.php b/src/Validator/Alpha.php new file mode 100644 index 00000000..4dfdd139 --- /dev/null +++ b/src/Validator/Alpha.php @@ -0,0 +1,88 @@ + "Invalid type given. String expected", + self::NOT_ALPHA => "The input contains non alphabetic characters", + self::STRING_EMPTY => "The input is an empty string" + ); + + /** + * Options for this validator + * + * @var array + */ + protected $options = array( + 'allowWhiteSpace' => false, // Whether to allow white space characters; off by default + ); + + /** + * Returns true if and only if $value contains only alphabetic characters + * + * @param string $value + * @return boolean + */ + public function isValid($value) + { + if (!is_string($value)) { + $this->error(self::INVALID); + return false; + } + + $this->setValue($value); + + if ('' === $value) { + $this->error(self::STRING_EMPTY); + return false; + } + + if (null === self::$filter) { + self::$filter = new AlphaFilter(); + } + + //self::$filter->setAllowWhiteSpace($this->allowWhiteSpace); + self::$filter->setAllowWhiteSpace($this->options['allowWhiteSpace']); + + if ($value !== self::$filter->filter($value)) { + $this->error(self::NOT_ALPHA); + return false; + } + + return true; + } + +} diff --git a/src/Validator/Float.php b/src/Validator/Float.php new file mode 100644 index 00000000..db16805f --- /dev/null +++ b/src/Validator/Float.php @@ -0,0 +1,133 @@ + "Invalid type given. String, integer or float expected", + self::NOT_FLOAT => "The input does not appear to be a float", + ); + + /** + * Optional locale + * + * @var string|null + */ + protected $locale; + + /** + * Constructor for the integer validator + * + * @param array|Traversable $options + */ + public function __construct($options = array()) + { + if ($options instanceof Traversable) { + $options = ArrayUtils::iteratorToArray($options); + } + + if (array_key_exists('locale', $options)) { + $this->setLocale($options['locale']); + } + + parent::__construct($options); + } + + /** + * Returns the set locale + * + * @return string + */ + public function getLocale() + { + if (null === $this->locale) { + $this->locale = Locale::getDefault(); + } + return $this->locale; + } + + /** + * Sets the locale to use + * + * @param string|null $locale + * @return Float + */ + public function setLocale($locale) + { + $this->locale = $locale; + return $this; + } + + + /** + * Returns true if and only if $value is a floating-point value + * + * @param string $value + * @return boolean + * @throws Exception\InvalidArgumentException + */ + public function isValid($value) + { + if (!is_string($value) && !is_int($value) && !is_float($value)) { + $this->error(self::INVALID); + return false; + } + + $this->setValue($value); + + if (is_float($value)) { + return true; + } + + $locale = $this->getLocale(); + $format = new NumberFormatter($locale, NumberFormatter::DECIMAL); + if (intl_is_failure($format->getErrorCode())) { + throw new Exception\InvalidArgumentException("Invalid locale string given"); + } + + $parsedFloat = $format->parse($value, NumberFormatter::TYPE_DOUBLE); + if (intl_is_failure($format->getErrorCode())) { + $this->error(self::NOT_FLOAT); + return false; + } + + $decimalSep = $format->getSymbol(NumberFormatter::DECIMAL_SEPARATOR_SYMBOL); + $groupingSep = $format->getSymbol(NumberFormatter::GROUPING_SEPARATOR_SYMBOL); + + $valueFiltered = str_replace($groupingSep, '', $value); + $valueFiltered = str_replace($decimalSep, '.', $valueFiltered); + + if (strval($parsedFloat) !== $valueFiltered) { + $this->error(self::NOT_FLOAT); + return false; + } + + return true; + } +} diff --git a/src/Validator/Int.php b/src/Validator/Int.php new file mode 100644 index 00000000..83a2f91d --- /dev/null +++ b/src/Validator/Int.php @@ -0,0 +1,130 @@ + "Invalid type given. String or integer expected", + self::NOT_INT => "The input does not appear to be an integer", + ); + + /** + * Optional locale + * + * @var string|null + */ + protected $locale; + + /** + * Constructor for the integer validator + * + * @param array|Traversable $options + */ + public function __construct($options = array()) + { + if ($options instanceof Traversable) { + $options = ArrayUtils::iteratorToArray($options); + } + + if (array_key_exists('locale', $options)) { + $this->setLocale($options['locale']); + } + + parent::__construct($options); + } + + /** + * Returns the set locale + */ + public function getLocale() + { + if (null === $this->locale) { + $this->locale = Locale::getDefault(); + } + return $this->locale; + } + + /** + * Sets the locale to use + * + * @param string $locale + * @return Int + */ + public function setLocale($locale) + { + $this->locale = $locale; + return $this; + } + + /** + * Returns true if and only if $value is a valid integer + * + * @param string|integer $value + * @return boolean + * @throws Exception\InvalidArgumentException + */ + public function isValid($value) + { + if (!is_string($value) && !is_int($value) && !is_float($value)) { + $this->error(self::INVALID); + return false; + } + + if (is_int($value)) { + return true; + } + + $this->setValue($value); + + $locale = $this->getLocale(); + $format = new NumberFormatter($locale, NumberFormatter::DECIMAL); + if (intl_is_failure($format->getErrorCode())) { + throw new Exception\InvalidArgumentException("Invalid locale string given"); + } + + $parsedInt = $format->parse($value, NumberFormatter::TYPE_INT64); + if (intl_is_failure($format->getErrorCode())) { + $this->error(self::NOT_INT); + return false; + } + + $decimalSep = $format->getSymbol(NumberFormatter::DECIMAL_SEPARATOR_SYMBOL); + $groupingSep = $format->getSymbol(NumberFormatter::GROUPING_SEPARATOR_SYMBOL); + + $valueFiltered = str_replace($groupingSep, '', $value); + $valueFiltered = str_replace($decimalSep, '.', $valueFiltered); + + if (strval($parsedInt) !== $valueFiltered) { + $this->error(self::NOT_INT); + return false; + } + + return true; + } +} diff --git a/src/Validator/PostCode.php b/src/Validator/PostCode.php new file mode 100644 index 00000000..5019db29 --- /dev/null +++ b/src/Validator/PostCode.php @@ -0,0 +1,389 @@ + "Invalid type given. String or integer expected", + self::NO_MATCH => "The input does not appear to be a postal code", + self::SERVICE => "The input does not appear to be a postal code", + self::SERVICEFAILURE => "An exception has been raised while validating the input.", + ); + + /** + * Optional Locale to use + * + * @var string|null + */ + protected $locale; + + /** + * Optional Manual postal code format + * + * @var string|null + */ + protected $format; + + /** + * Optional Service callback for additional validation + * + * @var mixed|null + */ + protected $service; + + /** + * Postal Code regexes by territory + * + * @var array + */ + protected static $postCodeRegex = array( + 'GB' => 'GIR[ ]?0AA|((AB|AL|B|BA|BB|BD|BH|BL|BN|BR|BS|BT|CA|CB|CF|CH|CM|CO|CR|CT|CV|CW|DA|DD|DE|DG|DH|DL|DN|DT|DY|E|EC|EH|EN|EX|FK|FY|G|GL|GY|GU|HA|HD|HG|HP|HR|HS|HU|HX|IG|IM|IP|IV|JE|KA|KT|KW|KY|L|LA|LD|LE|LL|LN|LS|LU|M|ME|MK|ML|N|NE|NG|NN|NP|NR|NW|OL|OX|PA|PE|PH|PL|PO|PR|RG|RH|RM|S|SA|SE|SG|SK|SL|SM|SN|SO|SP|SR|SS|ST|SW|SY|TA|TD|TF|TN|TQ|TR|TS|TW|UB|W|WA|WC|WD|WF|WN|WR|WS|WV|YO|ZE)(\d[\dA-Z]?[ ]?\d[ABD-HJLN-UW-Z]{2}))|BFPO[ ]?\d{1,4}', + 'JE' => 'JE\d[\dA-Z]?[ ]?\d[ABD-HJLN-UW-Z]{2}', + 'GG' => 'GY\d[\dA-Z]?[ ]?\d[ABD-HJLN-UW-Z]{2}', + 'IM' => 'IM\d[\dA-Z]?[ ]?\d[ABD-HJLN-UW-Z]{2}', + 'US' => '\d{5}([ \-]\d{4})?', + 'CA' => '[ABCEGHJKLMNPRSTVXY]\d[ABCEGHJ-NPRSTV-Z][ ]?\d[ABCEGHJ-NPRSTV-Z]\d', + 'DE' => '\d{5}', + 'JP' => '\d{3}-\d{4}', + 'FR' => '\d{2}[ ]?\d{3}', + 'AU' => '\d{4}', + 'IT' => '\d{5}', + 'CH' => '\d{4}', + 'AT' => '\d{4}', + 'ES' => '\d{5}', + 'NL' => '\d{4}[ ]?[A-Z]{2}', + 'BE' => '\d{4}', + 'DK' => '\d{4}', + 'SE' => '\d{3}[ ]?\d{2}', + 'NO' => '\d{4}', + 'BR' => '\d{5}[\-]?\d{3}', + 'PT' => '\d{4}([\-]\d{3})?', + 'FI' => '\d{5}', + 'AX' => '22\d{3}', + 'KR' => '\d{3}[\-]\d{3}', + 'CN' => '\d{6}', + 'TW' => '\d{3}(\d{2})?', + 'SG' => '\d{6}', + 'DZ' => '\d{5}', + 'AD' => 'AD\d{3}', + 'AR' => '([A-HJ-NP-Z])?\d{4}([A-Z]{3})?', + 'AM' => '(37)?\d{4}', + 'AZ' => '\d{4}', + 'BH' => '((1[0-2]|[2-9])\d{2})?', + 'BD' => '\d{4}', + 'BB' => '(BB\d{5})?', + 'BY' => '\d{6}', + 'BM' => '[A-Z]{2}[ ]?[A-Z0-9]{2}', + 'BA' => '\d{5}', + 'IO' => 'BBND 1ZZ', + 'BN' => '[A-Z]{2}[ ]?\d{4}', + 'BG' => '\d{4}', + 'KH' => '\d{5}', + 'CV' => '\d{4}', + 'CL' => '\d{7}', + 'CR' => '\d{4,5}|\d{3}-\d{4}', + 'HR' => '\d{5}', + 'CY' => '\d{4}', + 'CZ' => '\d{3}[ ]?\d{2}', + 'DO' => '\d{5}', + 'EC' => '([A-Z]\d{4}[A-Z]|(?:[A-Z]{2})?\d{6})?', + 'EG' => '\d{5}', + 'EE' => '\d{5}', + 'FO' => '\d{3}', + 'GE' => '\d{4}', + 'GR' => '\d{3}[ ]?\d{2}', + 'GL' => '39\d{2}', + 'GT' => '\d{5}', + 'HT' => '\d{4}', + 'HN' => '(?:\d{5})?', + 'HU' => '\d{4}', + 'IS' => '\d{3}', + 'IN' => '\d{6}', + 'ID' => '\d{5}', + 'IE' => '((D|DUBLIN)?([1-9]|6[wW]|1[0-8]|2[024]))?', + 'IL' => '\d{5}', + 'JO' => '\d{5}', + 'KZ' => '\d{6}', + 'KE' => '\d{5}', + 'KW' => '\d{5}', + 'LA' => '\d{5}', + 'LV' => '\d{4}', + 'LB' => '(\d{4}([ ]?\d{4})?)?', + 'LI' => '(948[5-9])|(949[0-7])', + 'LT' => '\d{5}', + 'LU' => '\d{4}', + 'MK' => '\d{4}', + 'MY' => '\d{5}', + 'MV' => '\d{5}', + 'MT' => '[A-Z]{3}[ ]?\d{2,4}', + 'MU' => '(\d{3}[A-Z]{2}\d{3})?', + 'MX' => '\d{5}', + 'MD' => '\d{4}', + 'MC' => '980\d{2}', + 'MA' => '\d{5}', + 'NP' => '\d{5}', + 'NZ' => '\d{4}', + 'NI' => '((\d{4}-)?\d{3}-\d{3}(-\d{1})?)?', + 'NG' => '(\d{6})?', + 'OM' => '(PC )?\d{3}', + 'PK' => '\d{5}', + 'PY' => '\d{4}', + 'PH' => '\d{4}', + 'PL' => '\d{2}-\d{3}', + 'PR' => '00[679]\d{2}([ \-]\d{4})?', + 'RO' => '\d{6}', + 'RU' => '\d{6}', + 'SM' => '4789\d', + 'SA' => '\d{5}', + 'SN' => '\d{5}', + 'SK' => '\d{3}[ ]?\d{2}', + 'SI' => '\d{4}', + 'ZA' => '\d{4}', + 'LK' => '\d{5}', + 'TJ' => '\d{6}', + 'TH' => '\d{5}', + 'TN' => '\d{4}', + 'TR' => '\d{5}', + 'TM' => '\d{6}', + 'UA' => '\d{5}', + 'UY' => '\d{5}', + 'UZ' => '\d{6}', + 'VA' => '00120', + 'VE' => '\d{4}', + 'ZM' => '\d{5}', + 'AS' => '96799', + 'CC' => '6799', + 'CK' => '\d{4}', + 'RS' => '\d{6}', + 'ME' => '8\d{4}', + 'CS' => '\d{5}', + 'YU' => '\d{5}', + 'CX' => '6798', + 'ET' => '\d{4}', + 'FK' => 'FIQQ 1ZZ', + 'NF' => '2899', + 'FM' => '(9694[1-4])([ \-]\d{4})?', + 'GF' => '9[78]3\d{2}', + 'GN' => '\d{3}', + 'GP' => '9[78][01]\d{2}', + 'GS' => 'SIQQ 1ZZ', + 'GU' => '969[123]\d([ \-]\d{4})?', + 'GW' => '\d{4}', + 'HM' => '\d{4}', + 'IQ' => '\d{5}', + 'KG' => '\d{6}', + 'LR' => '\d{4}', + 'LS' => '\d{3}', + 'MG' => '\d{3}', + 'MH' => '969[67]\d([ \-]\d{4})?', + 'MN' => '\d{6}', + 'MP' => '9695[012]([ \-]\d{4})?', + 'MQ' => '9[78]2\d{2}', + 'NC' => '988\d{2}', + 'NE' => '\d{4}', + 'VI' => '008(([0-4]\d)|(5[01]))([ \-]\d{4})?', + 'PF' => '987\d{2}', + 'PG' => '\d{3}', + 'PM' => '9[78]5\d{2}', + 'PN' => 'PCRN 1ZZ', + 'PW' => '96940', + 'RE' => '9[78]4\d{2}', + 'SH' => '(ASCN|STHL) 1ZZ', + 'SJ' => '\d{4}', + 'SO' => '\d{5}', + 'SZ' => '[HLMS]\d{3}', + 'TC' => 'TKCA 1ZZ', + 'WF' => '986\d{2}', + 'YT' => '976\d{2}', + ); + + /** + * Constructor for the PostCode validator + * + * Accepts a string locale and/or "format". + * + * @param array|Traversable $options + */ + public function __construct($options = array()) + { + if ($options instanceof Traversable) { + $options = ArrayUtils::iteratorToArray($options); + } + + if (array_key_exists('locale', $options)) { + $this->setLocale($options['locale']); + } else { + $this->setLocale(Locale::getDefault()); + } + if (array_key_exists('format', $options)) { + $this->setFormat($options['format']); + } + if (array_key_exists('service', $options)) { + $this->setService($options['service']); + } + + parent::__construct($options); + } + + /** + * Returns the set locale + * + * @return string|null The set locale + */ + public function getLocale() + { + return $this->locale; + } + + /** + * Sets the locale to use + * + * @param string|null $locale + * @return PostCode Provides fluid interface + */ + public function setLocale($locale) + { + $this->locale = $locale; + return $this; + } + + /** + * Returns the set postal code format + * + * @return string|null + */ + public function getFormat() + { + return $this->format; + } + + /** + * Sets a self defined postal format as regex + * + * @param string $format + * @return PostCode Provides fluid interface + */ + public function setFormat($format) + { + $this->format = $format; + return $this; + } + + /** + * Returns the actual set service + * + * @return mixed|null + */ + public function getService() + { + return $this->service; + } + + /** + * Sets a new callback for service validation + * + * @param mixed $service + * @return PostCode Provides fluid interface + */ + public function setService($service) + { + $this->service = $service; + return $this; + } + + /** + * Returns true if and only if $value is a valid postalcode + * + * @param string $value + * @return boolean + * @throws Exception\InvalidArgumentException + */ + public function isValid($value) + { + if (!is_string($value) && !is_int($value)) { + $this->error(self::INVALID); + return false; + } + + $this->setValue($value); + + $service = $this->getService(); + $locale = $this->getLocale(); + $format = $this->getFormat(); + if ((null === $format || '' === $format) && !empty($locale)) { + $region = Locale::getRegion($locale); + if ('' === $region) { + throw new Exception\InvalidArgumentException("Locale must contain a region"); + } + if (isset(self::$postCodeRegex[$region])) { + $format = self::$postCodeRegex[$region]; + } + } + if (null === $format || '' === $format) { + throw new Exception\InvalidArgumentException("A postcode-format string has to be given for validation"); + } + + if ($format[0] !== '/') { + $format = '/^' . $format; + } + if ($format[strlen($format) - 1] !== '/') { + $format .= '$/'; + } + + if (!empty($service)) { + if (!is_callable($service)) { + throw new Exception\InvalidArgumentException('Invalid callback given'); + } + + try { + $callback = new Callback($service); + $callback->setOptions(array( + 'format' => $format, + 'locale' => $locale, + )); + if (!$callback->isValid($value)) { + $this->error(self::SERVICE, $value); + return false; + } + } catch (\Exception $e) { + $this->error(self::SERVICEFAILURE, $value); + return false; + } + } + + if (!preg_match($format, $value)) { + $this->error(self::NO_MATCH); + return false; + } + + return true; + } +} diff --git a/src/View/Helper/AbstractTranslatorHelper.php b/src/View/Helper/AbstractTranslatorHelper.php new file mode 100644 index 00000000..aabf010b --- /dev/null +++ b/src/View/Helper/AbstractTranslatorHelper.php @@ -0,0 +1,132 @@ +translator = $translator; + if (null !== $textDomain) { + $this->setTranslatorTextDomain($textDomain); + } + return $this; + } + + /** + * Returns translator used in helper + * + * @return Translator|null + */ + public function getTranslator() + { + if (! $this->isTranslatorEnabled()) { + return null; + } + + return $this->translator; + } + + /** + * Checks if the helper has a translator + * + * @return bool + */ + public function hasTranslator() + { + return (bool) $this->getTranslator(); + } + + /** + * Sets whether translator is enabled and should be used + * + * @param bool $enabled [optional] whether translator should be used. + * Default is true. + * @return AbstractTranslatorHelper + */ + public function setTranslatorEnabled($enabled = true) + { + $this->translatorEnabled = (bool) $enabled; + return $this; + } + + /** + * Returns whether translator is enabled and should be used + * + * @return bool + */ + public function isTranslatorEnabled() + { + return $this->translatorEnabled; + } + + /** + * Set translation text domain + * + * @param string $textDomain + * @return AbstractTranslatorHelper + */ + public function setTranslatorTextDomain($textDomain = 'default') + { + $this->translatorTextDomain = $textDomain; + return $this; + } + + /** + * Return the translation text domain + * + * @return string + */ + public function getTranslatorTextDomain() + { + return $this->translatorTextDomain; + } +} diff --git a/src/View/Helper/CurrencyFormat.php b/src/View/Helper/CurrencyFormat.php new file mode 100644 index 00000000..75f7a094 --- /dev/null +++ b/src/View/Helper/CurrencyFormat.php @@ -0,0 +1,128 @@ +currencyCode = $currencyCode; + return $this; + } + + /** + * Get the 3-letter ISO 4217 currency code indicating the currency to use. + * + * @return string + */ + public function getCurrencyCode() + { + return $this->currencyCode; + } + + /** + * Set locale to use instead of the default. + * + * @param string $locale + * @return CurrencyFormat + */ + public function setLocale($locale) + { + $this->locale = (string) $locale; + return $this; + } + + /** + * Get the locale to use. + * + * @return string|null + */ + public function getLocale() + { + if ($this->locale === null) { + $this->locale = Locale::getDefault(); + } + + return $this->locale; + } + + /** + * Format a number. + * + * @param float $number + * @param string $currencyCode + * @param string $locale + * @return string + */ + public function __invoke( + $number, + $currencyCode = null, + $locale = null + ) { + if (null === $locale) { + $locale = $this->getLocale(); + } + if (null === $currencyCode) { + $currencyCode = $this->getCurrencyCode(); + } + + $formatterId = md5($locale); + + if (!isset($this->formatters[$formatterId])) { + $this->formatters[$formatterId] = new NumberFormatter( + $locale, + NumberFormatter::CURRENCY + ); + } + + return $this->formatters[$formatterId]->formatCurrency( + $number, $currencyCode + ); + } +} diff --git a/src/View/Helper/DateFormat.php b/src/View/Helper/DateFormat.php index ffad8089..bcb971dc 100644 --- a/src/View/Helper/DateFormat.php +++ b/src/View/Helper/DateFormat.php @@ -1,30 +1,20 @@ formatters[$name] = $formatter; + $this->timezone = (string) $timezone; + + foreach ($this->formatters as $formatter) { + $formatter->setTimeZoneId($this->timezone); + } return $this; } + /** + * Get the timezone to use. + * + * @return string|null + */ + public function getTimezone() + { + return $this->timezone; + } + + /** + * Set locale to use instead of the default. + * + * @param string $locale + * @return DateFormat + */ + public function setlocale($locale) + { + $this->locale = (string) $locale; + return $this; + } + + /** + * Get the locale to use. + * + * @return string|null + */ + public function getlocale() + { + if ($this->locale === null) { + $this->locale = Locale::getDefault(); + } + + return $this->locale; + } + /** * Format a date. * * @param DateTime|integer|array $date - * @param string $formatterName + * @param integer $dateType + * @param integer $timeType + * @param string $locale * @return string + * @throws Exception\RuntimeException */ - public function __invoke($date, $formatterName) - { - if (!isset($this->formatters[$formatterName])) { - throw new Exception\RuntimeException(sprintf( - 'No formatter with name %s found', - $formatterName - )); + public function __invoke( + $date, + $dateType = IntlDateFormatter::NONE, + $timeType = IntlDateFormatter::NONE, + $locale = null + ) { + if ($locale === null) { + $locale = $this->getlocale(); + } + + $timezone = $this->getTimezone(); + $formatterId = md5($dateType . "\0" . $timeType . "\0" . $locale); + + if (!isset($this->formatters[$formatterId])) { + $this->formatters[$formatterId] = new IntlDateFormatter( + $locale, + $dateType, + $timeType, + $timezone + ); } // DateTime support for IntlDateFormatter::format() was only added in 5.3.4 - if ($date instanceof DateTime - && version_compare(PHP_VERSION, '5.3.4', '<') - ) { + if ($date instanceof DateTime && version_compare(PHP_VERSION, '5.3.4', '<')) { $date = $date->getTimestamp(); } - return $this->formatters[$formatterName] - ->format($date); + return $this->formatters[$formatterId]->format($date); } } diff --git a/src/View/Helper/NumberFormat.php b/src/View/Helper/NumberFormat.php new file mode 100644 index 00000000..c1af3371 --- /dev/null +++ b/src/View/Helper/NumberFormat.php @@ -0,0 +1,166 @@ +formatStyle = (int) $formatStyle; + return $this; + } + + /** + * Get the format style to use. + * + * @return integer + */ + public function getFormatStyle() + { + if (null === $this->formatStyle) { + $this->formatStyle = NumberFormatter::DECIMAL; + } + return $this->formatStyle; + } + + /** + * Set format type to use instead of the default. + * + * @param integer $formatType + * @return NumberFormat + */ + public function setFormatType($formatType) + { + $this->formatType = (int) $formatType; + return $this; + } + + /** + * Get the format type to use. + * + * @return integer + */ + public function getFormatType() + { + if (null === $this->formatType) { + $this->formatType = NumberFormatter::TYPE_DEFAULT; + } + return $this->formatType; + } + + /** + * Set locale to use instead of the default. + * + * @param string $locale + * @return NumberFormat + */ + public function setLocale($locale) + { + $this->locale = (string) $locale; + return $this; + } + + /** + * Get the locale to use. + * + * @return string|null + */ + public function getLocale() + { + if ($this->locale === null) { + $this->locale = Locale::getDefault(); + } + + return $this->locale; + } + + /** + * Format a number. + * + * @param integer|float $number + * @param integer $formatStyle + * @param integer $formatType + * @param string $locale + * @return string + */ + public function __invoke( + $number, + $formatStyle = null, + $formatType = null, + $locale = null + ) { + if (null === $locale) { + $locale = $this->getLocale(); + } + if (null === $formatStyle) { + $formatStyle = $this->getFormatStyle(); + } + if (null === $formatType) { + $formatType = $this->getFormatType(); + } + + $formatterId = md5($formatStyle . "\0" . $locale); + + if (!isset($this->formatters[$formatterId])) { + $this->formatters[$formatterId] = new NumberFormatter( + $locale, + $formatStyle + ); + } + + return $this->formatters[$formatterId]->format($number, $formatType); + } +} diff --git a/src/View/Helper/Translate.php b/src/View/Helper/Translate.php index 15c75f9c..944f7d72 100644 --- a/src/View/Helper/Translate.php +++ b/src/View/Helper/Translate.php @@ -1,29 +1,16 @@ translator = $translator; - return $this; - } - /** * Translate a message. * @@ -62,13 +28,14 @@ public function setTranslator(Translator $translator) * @param string $textDomain * @param string $locale * @return string + * @throws Exception\RuntimeException */ public function __invoke($message, $textDomain = 'default', $locale = null) { - if ($this->translator === null) { + $translator = $this->getTranslator(); + if (null === $translator) { throw new Exception\RuntimeException('Translator has not been set'); } - - return $this->translator->translate($message, $textDomain, $locale); + return $translator->translate($message, $textDomain, $locale); } } diff --git a/src/View/Helper/TranslatePlural.php b/src/View/Helper/TranslatePlural.php index f7fca86b..187bd2e9 100644 --- a/src/View/Helper/TranslatePlural.php +++ b/src/View/Helper/TranslatePlural.php @@ -1,29 +1,16 @@ translator = $translator; - return $this; - } - /** * Translate a plural message. * @@ -64,18 +30,20 @@ public function setTranslator(Translator $translator) * @param string $textDomain * @param string $locale * @return string + * @throws Exception\RuntimeException */ public function __invoke( - $singular, - $plural, + $singular, + $plural, $number, - $textDomain = 'default', + $textDomain = 'default', $locale = null - ) { - if ($this->translator === null) { + ) + { + $translator = $this->getTranslator(); + if (null === $translator) { throw new Exception\RuntimeException('Translator has not been set'); } - - return $this->translator->translatePlural($singular, $plural, $number, $textDomain, $locale); + return $translator->translatePlural($singular, $plural, $number, $textDomain, $locale); } } diff --git a/src/View/HelperConfig.php b/src/View/HelperConfig.php new file mode 100644 index 00000000..3dfe2782 --- /dev/null +++ b/src/View/HelperConfig.php @@ -0,0 +1,49 @@ + 'Zend\I18n\View\Helper\CurrencyFormat', + 'dateformat' => 'Zend\I18n\View\Helper\DateFormat', + 'numberformat' => 'Zend\I18n\View\Helper\NumberFormat', + 'translate' => 'Zend\I18n\View\Helper\Translate', + 'translateplural' => 'Zend\I18n\View\Helper\TranslatePlural', + ); + + /** + * Configure the provided service manager instance with the configuration + * in this class. + * + * @param ServiceManager $serviceManager + * @return void + */ + public function configureServiceManager(ServiceManager $serviceManager) + { + foreach ($this->invokables as $name => $service) { + $serviceManager->setInvokableClass($name, $service); + } + } +} diff --git a/src/View/HelperConfiguration.php b/src/View/HelperConfiguration.php deleted file mode 100644 index 272ecc68..00000000 --- a/src/View/HelperConfiguration.php +++ /dev/null @@ -1,59 +0,0 @@ - 'Zend\I18n\View\Helper\Translate', - 'translateplural' => 'Zend\I18n\View\Helper\TranslatePlural', - ); - - /** - * Configure the provided service manager instance with the configuration - * in this class. - * - * @param ServiceManager $serviceManager - * @return void - */ - public function configureServiceManager(ServiceManager $serviceManager) - { - foreach ($this->invokables as $name => $service) { - $serviceManager->setInvokableClass($name, $service); - } - } -} diff --git a/test/Filter/AlnumTest.php b/test/Filter/AlnumTest.php new file mode 100644 index 00000000..02749050 --- /dev/null +++ b/test/Filter/AlnumTest.php @@ -0,0 +1,161 @@ +filter = new AlnumFilter(); + + $this->locale = Locale::getDefault(); + $language = Locale::getPrimaryLanguage($this->locale); + self::$meansEnglishAlphabet = in_array($language, array('ja')); + self::$unicodeEnabled = (@preg_match('/\pL/u', 'a')) ? true : false; + } + + /** + * Ensures that the filter follows expected behavior + * + * @return void + */ + public function testBasic() + { + if (!self::$unicodeEnabled) { + // POSIX named classes are not supported, use alternative a-zA-Z match + $valuesExpected = array( + 'abc123' => 'abc123', + 'abc 123' => 'abc123', + 'abcxyz' => 'abcxyz', + 'AZ@#4.3' => 'AZ43', + '' => '' + ); + } elseif (self::$meansEnglishAlphabet) { + // The Alphabet means english alphabet. + + /** + * The first element contains multibyte alphabets and digits. + * But , AlnumFilter is expected to return only singlebyte alphabets and digits. + * + * The second contains multibyte or singebyte space. + * The third contains various multibyte or singebyte characters. + */ + $valuesExpected = array( + 'aABb3456' => 'aB35', + 'z7 Y8 x9' => 'z8x', + ',s1.2r3#:q,' => 's12rq', + ); + } else { + //The Alphabet means each language's alphabet. + $valuesExpected = array( + 'abc123' => 'abc123', + 'abc 123' => 'abc123', + 'abcxyz' => 'abcxyz', + 'če2t3ně' => 'če2t3ně', + 'grz5e4gżółka' => 'grz5e4gżółka', + 'Be3l5gië' => 'Be3l5gië', + '' => '' + ); + } + + foreach ($valuesExpected as $input => $expected) { + $actual = $this->filter->filter($input); + $this->assertEquals($expected, $actual); + } + } + + /** + * Ensures that the allowWhiteSpace option works as expected + * + * @return void + */ + public function testAllowWhiteSpace() + { + $this->filter->setAllowWhiteSpace(true); + + if (!self::$unicodeEnabled) { + // POSIX named classes are not supported, use alternative a-zA-Z match + $valuesExpected = array( + 'abc123' => 'abc123', + 'abc 123' => 'abc 123', + 'abcxyz' => 'abcxyz', + 'AZ@#4.3' => 'AZ43', + '' => '', + "\n" => "\n", + " \t " => " \t " + ); + } elseif (self::$meansEnglishAlphabet) { + //The Alphabet means english alphabet. + $valuesExpected = array( + 'a B 45' => 'a B 5', + 'z3 x' => 'z3x' + ); + } else { + //The Alphabet means each language's alphabet. + $valuesExpected = array( + 'abc123' => 'abc123', + 'abc 123' => 'abc 123', + 'abcxyz' => 'abcxyz', + 'če2 t3ně' => 'če2 t3ně', + 'gr z5e4gżółka' => 'gr z5e4gżółka', + 'Be3l5 gië' => 'Be3l5 gië', + '' => '', + ); + } + + foreach ($valuesExpected as $input => $expected) { + $actual = $this->filter->filter($input); + $this->assertEquals($expected, $actual); + } + } +} diff --git a/test/Filter/AlphaTest.php b/test/Filter/AlphaTest.php new file mode 100644 index 00000000..248d6e13 --- /dev/null +++ b/test/Filter/AlphaTest.php @@ -0,0 +1,165 @@ +filter = new AlphaFilter(); + + $this->locale = Locale::getDefault(); + $language = Locale::getPrimaryLanguage($this->locale); + self::$meansEnglishAlphabet = in_array($language, array('ja')); + self::$unicodeEnabled = (@preg_match('/\pL/u', 'a')) ? true : false; + } + + /** + * Ensures that the filter follows expected behavior + * + * @return void + */ + public function testBasic() + { + if (!self::$unicodeEnabled) { + // POSIX named classes are not supported, use alternative a-zA-Z match + $valuesExpected = array( + 'abc123' => 'abc', + 'abc 123' => 'abc', + 'abcxyz' => 'abcxyz', + '' => '' + ); + } elseif (self::$meansEnglishAlphabet) { + //The Alphabet means english alphabet. + /** + * The first element contains multibyte alphabets. + * But , AlphaFilter is expected to return only singlebyte alphabets. + * The second contains multibyte or singlebyte space. + * The third contains multibyte or singlebyte digits. + * The forth contains various multibyte or singlebyte characters. + * The last contains only singlebyte alphabets. + */ + $valuesExpected = array( + 'aABbc' => 'aBc', + 'z Y x' => 'zx', + 'W1v3U4t' => 'vt', + ',sй.rλ:qν_p' => 'srqp', + 'onml' => 'onml' + ); + } else { + //The Alphabet means each language's alphabet. + $valuesExpected = array( + 'abc123' => 'abc', + 'abc 123' => 'abc', + 'abcxyz' => 'abcxyz', + 'četně' => 'četně', + 'لعربية' => 'لعربية', + 'grzegżółka' => 'grzegżółka', + 'België' => 'België', + '' => '' + ); + } + + foreach ($valuesExpected as $input => $expected) { + $actual = $this->filter->filter($input); + $this->assertEquals($expected, $actual); + } + } + + /** + * Ensures that the filter follows expected behavior + * + * @return void + */ + public function testAllowWhiteSpace() + { + $this->filter->setAllowWhiteSpace(true); + + if (!self::$unicodeEnabled) { + // POSIX named classes are not supported, use alternative a-zA-Z match + $valuesExpected = array( + 'abc123' => 'abc', + 'abc 123' => 'abc ', + 'abcxyz' => 'abcxyz', + '' => '', + "\n" => "\n", + " \t " => " \t " + ); + } if (self::$meansEnglishAlphabet) { + //The Alphabet means english alphabet. + $valuesExpected = array( + 'a B' => 'a B', + 'zY x' => 'zx' + ); + } else { + //The Alphabet means each language's alphabet. + $valuesExpected = array( + 'abc123' => 'abc', + 'abc 123' => 'abc ', + 'abcxyz' => 'abcxyz', + 'četně' => 'četně', + 'لعربية' => 'لعربية', + 'grzegżółka' => 'grzegżółka', + 'België' => 'België', + '' => '', + "\n" => "\n", + " \t " => " \t " + ); + } + + foreach ($valuesExpected as $input => $expected) { + $actual = $this->filter->filter($input); + $this->assertEquals($expected, $actual); + } + } +} diff --git a/test/Filter/NumberFormatTest.php b/test/Filter/NumberFormatTest.php new file mode 100644 index 00000000..0c014fa7 --- /dev/null +++ b/test/Filter/NumberFormatTest.php @@ -0,0 +1,120 @@ + 'en_US', + 'style' => NumberFormatter::DECIMAL + )); + + $this->assertEquals('en_US', $filter->getLocale()); + $this->assertEquals(NumberFormatter::DECIMAL, $filter->getStyle()); + } + + public function testConstructWithParameters() + { + $filter = new NumberFormatFilter('en_US', NumberFormatter::DECIMAL); + + $this->assertEquals('en_US', $filter->getLocale()); + $this->assertEquals(NumberFormatter::DECIMAL, $filter->getStyle()); + } + + + /** + * @param $locale + * @param $style + * @param $type + * @param $value + * @param $expected + * @dataProvider numberToFormattedProvider + */ + public function testNumberToFormatted($locale, $style, $type, $value, $expected) + { + $filter = new NumberFormatFilter($locale, $style, $type); + $this->assertEquals($expected, $filter->filter($value)); + } + + /** + * @param $locale + * @param $style + * @param $type + * @param $value + * @param $expected + * @dataProvider formattedToNumberProvider + */ + public function testFormattedToNumber($locale, $style, $type, $value, $expected) + { + $filter = new NumberFormatFilter($locale, $style, $type); + $this->assertEquals($expected, $filter->filter($value)); + } + + public static function numberToFormattedProvider() + { + return array( + array( + 'en_US', + NumberFormatter::DEFAULT_STYLE, + NumberFormatter::TYPE_DOUBLE, + 1234567.8912346, + '1,234,567.891' + ), + array( + 'de_DE', + NumberFormatter::DEFAULT_STYLE, + NumberFormatter::TYPE_DOUBLE, + 1234567.8912346, + '1.234.567,891' + ), + array( + 'ru_RU', + NumberFormatter::DEFAULT_STYLE, + NumberFormatter::TYPE_DOUBLE, + 1234567.8912346, + '1 234 567,891' + ), + ); + } + + public static function formattedToNumberProvider() + { + return array( + array( + 'en_US', + NumberFormatter::DEFAULT_STYLE, + NumberFormatter::TYPE_DOUBLE, + '1,234,567.891', + 1234567.891, + ), + array( + 'de_DE', + NumberFormatter::DEFAULT_STYLE, + NumberFormatter::TYPE_DOUBLE, + '1.234.567,891', + 1234567.891, + ), + array( + 'ru_RU', + NumberFormatter::DEFAULT_STYLE, + NumberFormatter::TYPE_DOUBLE, + '1 234 567,891', + 1234567.891, + ), + ); + } +} diff --git a/test/Translator/Loader/GettextTest.php b/test/Translator/Loader/GettextTest.php new file mode 100644 index 00000000..1384393a --- /dev/null +++ b/test/Translator/Loader/GettextTest.php @@ -0,0 +1,84 @@ +originalLocale = Locale::getDefault(); + Locale::setDefault('en_EN'); + + $this->testFilesDir = realpath(__DIR__ . '/../_files'); + } + + public function tearDown() + { + Locale::setDefault($this->originalLocale); + } + + public function testLoaderFailsToLoadMissingFile() + { + $loader = new GettextLoader(); + $this->setExpectedException('Zend\I18n\Exception\InvalidArgumentException', 'Could not open file'); + $loader->load('missing', 'en_EN'); + } + + public function testLoaderFailsToLoadBadFile() + { + $loader = new GettextLoader(); + $this->setExpectedException('Zend\I18n\Exception\InvalidArgumentException', + 'is not a valid gettext file'); + $loader->load($this->testFilesDir . '/failed.mo', 'en_EN'); + } + + public function testLoaderLoadsEmptyFile() + { + $loader = new GettextLoader(); + $domain = $loader->load($this->testFilesDir . '/translation_empty.mo', 'en_EN'); + $this->assertInstanceOf('Zend\I18n\Translator\TextDomain', $domain); + } + + public function testLoaderLoadsBigEndianFile() + { + $loader = new GettextLoader(); + $domain = $loader->load($this->testFilesDir . '/translation_bigendian.mo', 'en_EN'); + $this->assertInstanceOf('Zend\I18n\Translator\TextDomain', $domain); + } + + public function testLoaderReturnsValidTextDomain() + { + $loader = new GettextLoader(); + $textDomain = $loader->load($this->testFilesDir . '/translation_en.mo', 'en_EN'); + + $this->assertEquals('Message 1 (en)', $textDomain['Message 1']); + $this->assertEquals('Message 4 (en)', $textDomain['Message 4']); + } + + public function testLoaderLoadsPluralRules() + { + $loader = new GettextLoader(); + $textDomain = $loader->load($this->testFilesDir . '/translation_en.mo', 'en_EN'); + + $this->assertEquals(2, $textDomain->getPluralRule()->evaluate(0)); + $this->assertEquals(0, $textDomain->getPluralRule()->evaluate(1)); + $this->assertEquals(1, $textDomain->getPluralRule()->evaluate(2)); + $this->assertEquals(2, $textDomain->getPluralRule()->evaluate(10)); + } +} diff --git a/test/Translator/Loader/PhpArrayTest.php b/test/Translator/Loader/PhpArrayTest.php new file mode 100644 index 00000000..31338606 --- /dev/null +++ b/test/Translator/Loader/PhpArrayTest.php @@ -0,0 +1,77 @@ +originalLocale = Locale::getDefault(); + Locale::setDefault('en_EN'); + + $this->testFilesDir = realpath(__DIR__ . '/../_files'); + } + + public function tearDown() + { + Locale::setDefault($this->originalLocale); + } + + public function testLoaderFailsToLoadMissingFile() + { + $loader = new PhpArrayLoader(); + $this->setExpectedException('Zend\I18n\Exception\InvalidArgumentException', 'Could not open file'); + $loader->load('missing', 'en_EN'); + } + + public function testLoaderFailsToLoadNonArray() + { + $loader = new PhpArrayLoader(); + $this->setExpectedException('Zend\I18n\Exception\InvalidArgumentException', + 'Expected an array, but received'); + $loader->load($this->testFilesDir . '/failed.php', 'en_EN'); + } + + public function testLoaderLoadsEmptyArray() + { + $loader = new PhpArrayLoader(); + $textDomain = $loader->load($this->testFilesDir . '/translation_empty.php', 'en_EN'); + $this->assertInstanceOf('Zend\I18n\Translator\TextDomain', $textDomain); + } + + public function testLoaderReturnsValidTextDomain() + { + $loader = new PhpArrayLoader(); + $textDomain = $loader->load($this->testFilesDir . '/translation_en.php', 'en_EN'); + + $this->assertEquals('Message 1 (en)', $textDomain['Message 1']); + $this->assertEquals('Message 4 (en)', $textDomain['Message 4']); + } + + public function testLoaderLoadsPluralRules() + { + $loader = new PhpArrayLoader(); + $textDomain = $loader->load($this->testFilesDir . '/translation_en.php', 'en_EN'); + + $this->assertEquals(2, $textDomain->getPluralRule()->evaluate(0)); + $this->assertEquals(0, $textDomain->getPluralRule()->evaluate(1)); + $this->assertEquals(1, $textDomain->getPluralRule()->evaluate(2)); + $this->assertEquals(2, $textDomain->getPluralRule()->evaluate(10)); + } +} diff --git a/test/Translator/Plural/RuleTest.php b/test/Translator/Plural/RuleTest.php index a428a824..5b56be40 100644 --- a/test/Translator/Plural/RuleTest.php +++ b/test/Translator/Plural/RuleTest.php @@ -1,4 +1,12 @@ originalLocale = Locale::getDefault(); $this->translator = new Translator(); Locale::setDefault('en_EN'); + + $this->testFilesDir = __DIR__ . '/_files'; } public function tearDown() @@ -27,6 +48,49 @@ public function tearDown() Locale::setDefault($this->originalLocale); } + public function testFactoryCreatesTranslator() + { + $translator = Translator::factory(array( + 'locale' => 'de_DE', + 'patterns' => array( + array( + 'type' => 'phparray', + 'base_dir' => $this->testFilesDir . '/testarray', + 'pattern' => 'translation-%s.php' + ) + ), + 'files' => array( + array( + 'type' => 'phparray', + 'filename' => $this->testFilesDir . '/translation_en.php', + ) + ) + )); + + $this->assertInstanceOf('Zend\I18n\Translator\Translator', $translator); + $this->assertEquals('de_DE', $translator->getLocale()); + } + + public function testFactoryCreatesTranslatorWithCache() + { + $translator = Translator::factory(array( + 'locale' => 'de_DE', + 'patterns' => array( + array( + 'type' => 'phparray', + 'base_dir' => $this->testFilesDir . '/testarray', + 'pattern' => 'translation-%s.php' + ) + ), + 'cache' => array( + 'adapter' => 'memory' + ) + )); + + $this->assertInstanceOf('Zend\I18n\Translator\Translator', $translator); + $this->assertInstanceOf('Zend\Cache\Storage\StorageInterface', $translator->getCache()); + } + public function testDefaultLocale() { $this->assertEquals('en_EN', $this->translator->getLocale()); @@ -44,7 +108,26 @@ public function testTranslate() $loader->textDomain = new TextDomain(array('foo' => 'bar')); $this->translator->getPluginManager()->setService('test', $loader); $this->translator->addTranslationFile('test', null); - + $this->assertEquals('bar', $this->translator->translate('foo')); } + + public function testTranslatePlurals() + { + $this->translator->setLocale('en_EN'); + $this->translator->addTranslationFile( + 'phparray', + $this->testFilesDir . '/translation_en.php', + 'default', + 'en_EN' + ); + + $pl0 = $this->translator->translatePlural('Message 5', 'Message 5 Plural', 1); + $pl1 = $this->translator->translatePlural('Message 5', 'Message 5 Plural', 2); + $pl2 = $this->translator->translatePlural('Message 5', 'Message 5 Plural', 10); + + $this->assertEquals('Message 5 (en) Plural 0', $pl0); + $this->assertEquals('Message 5 (en) Plural 1', $pl1); + $this->assertEquals('Message 5 (en) Plural 2', $pl2); + } } diff --git a/test/Translator/_files/failed.mo b/test/Translator/_files/failed.mo new file mode 100644 index 00000000..e057750f Binary files /dev/null and b/test/Translator/_files/failed.mo differ diff --git a/test/Translator/_files/failed.php b/test/Translator/_files/failed.php new file mode 100644 index 00000000..da4a080a --- /dev/null +++ b/test/Translator/_files/failed.php @@ -0,0 +1,11 @@ + 'Nachricht 1', + 'Message 8' => 'Nachricht 8' +); diff --git a/test/Translator/_files/testarray/translation-en_US.php b/test/Translator/_files/testarray/translation-en_US.php new file mode 100644 index 00000000..70d9e406 --- /dev/null +++ b/test/Translator/_files/testarray/translation-en_US.php @@ -0,0 +1,16 @@ + 'Message 1 (en)', + 'Message 2' => 'Message 2 (en)', + 'Message 3' => 'Message 3 (en)', + 'Message 4' => 'Message 4 (en)', +); diff --git a/test/Translator/_files/testmo/translation-de_DE.mo b/test/Translator/_files/testmo/translation-de_DE.mo new file mode 100644 index 00000000..d3ff7c6b Binary files /dev/null and b/test/Translator/_files/testmo/translation-de_DE.mo differ diff --git a/test/Translator/_files/testmo/translation-en_US.mo b/test/Translator/_files/testmo/translation-en_US.mo new file mode 100644 index 00000000..9f451f8c Binary files /dev/null and b/test/Translator/_files/testmo/translation-en_US.mo differ diff --git a/test/Translator/_files/translation_bigendian.mo b/test/Translator/_files/translation_bigendian.mo new file mode 100644 index 00000000..53c830a5 Binary files /dev/null and b/test/Translator/_files/translation_bigendian.mo differ diff --git a/test/Translator/_files/translation_empty.mo b/test/Translator/_files/translation_empty.mo new file mode 100644 index 00000000..28f7bc39 Binary files /dev/null and b/test/Translator/_files/translation_empty.mo differ diff --git a/test/Translator/_files/translation_empty.php b/test/Translator/_files/translation_empty.php new file mode 100644 index 00000000..a79dd744 --- /dev/null +++ b/test/Translator/_files/translation_empty.php @@ -0,0 +1,11 @@ +\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=ISO-8859-1\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" +"Language-Team: \n" + diff --git a/test/Translator/_files/translation_en.mo b/test/Translator/_files/translation_en.mo new file mode 100644 index 00000000..9f451f8c Binary files /dev/null and b/test/Translator/_files/translation_en.mo differ diff --git a/test/Translator/_files/translation_en.php b/test/Translator/_files/translation_en.php new file mode 100644 index 00000000..4e83a06f --- /dev/null +++ b/test/Translator/_files/translation_en.php @@ -0,0 +1,26 @@ + array( + 'plural_forms' => 'nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);' + ), + 'Message 1' => 'Message 1 (en)', + 'Message 2' => 'Message 2 (en)', + 'Message 3' => 'Message 3 (en)', + 'Message 4' => 'Message 4 (en)', + 'Message 5' => array( + 0 => 'Message 5 (en) Plural 0', + 1 => 'Message 5 (en) Plural 1', + 2 => 'Message 5 (en) Plural 2' + ), + 'Cooking furniture' => 'Küchen Möbel (en)', + 'Küchen Möbel' => 'Cooking furniture (en)', +); diff --git a/test/Translator/_files/translation_en.po b/test/Translator/_files/translation_en.po new file mode 100644 index 00000000..0d57cb97 --- /dev/null +++ b/test/Translator/_files/translation_en.po @@ -0,0 +1,48 @@ +# translation of testmsg_en.po into English +# $Id: testmsg_en.po,v 1.0 2007/01/16 12:05:55 tokul Exp $ +# +msgid "" +msgstr "" +"Project-Id-Version: testmsg_en\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2007-01-17 13:22-0400\n" +"PO-Revision-Date: 2008-08-14 23:37+0100\n" +"Last-Translator: Thomas Weidner \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" +"Language-Team: \n" +"X-Poedit-SourceCharset: utf-8\n" + +#: test.php:0 +msgid "Message 1" +msgstr "Message 1 (en)" + +#: test.php:0 +msgid "Message 2" +msgstr "Message 2 (en)" + +#: test.php:0 +msgid "Message 3" +msgstr "Message 3 (en)" + +#: test.php:0 +msgid "Message 4" +msgstr "Message 4 (en)" + +#: test.php:0 +msgid "Message 5" +msgid_plural "Message 5 Plural" +msgstr[0] "Message 5 (en) Plural 0" +msgstr[1] "Message 5 (en) Plural 1" +msgstr[2] "Message 5 (en) Plural 2" + +#: test.php:0 +msgid "Cooking furniture" +msgstr "Küchen Möbel (en)" + +#: test.php:0 +msgid "Küchen Möbel" +msgstr "Cooking furniture (en)" + diff --git a/test/Validator/AlnumTest.php b/test/Validator/AlnumTest.php new file mode 100644 index 00000000..01080873 --- /dev/null +++ b/test/Validator/AlnumTest.php @@ -0,0 +1,150 @@ +validator = new AlnumValidator(); + } + + /** + * Ensures that the validator follows expected behavior for basic input values + * + * @return void + */ + public function testExpectedResultsWithBasicInputValues() + { + $valuesExpected = array( + 'abc123' => true, + 'abc 123' => false, + 'abcxyz' => true, + 'AZ@#4.3' => false, + 'aBc123' => true, + '' => false, + ' ' => false, + "\n" => false, + 'foobar1' => true + ); + foreach ($valuesExpected as $input => $result) { + $this->assertEquals($result, $this->validator->isValid($input)); + } + } + + /** + * Ensures that getMessages() returns expected initial value + * + * @return void + */ + public function testMessagesEmptyInitially() + { + $this->assertEquals(array(), $this->validator->getMessages()); + } + + /** + * Ensures that the allowWhiteSpace option works as expected + * + * @return void + */ + public function testOptionToAllowWhiteSpaceWithBasicInputValues() + { + $this->validator->setAllowWhiteSpace(true); + + $valuesExpected = array( + 'abc123' => true, + 'abc 123' => true, + 'abcxyz' => true, + 'AZ@#4.3' => false, + 'aBc123' => true, + '' => false, + ' ' => true, + "\n" => true, + " \t " => true, + 'foobar1' => true + ); + foreach ($valuesExpected as $input => $result) { + $this->assertEquals( + $result, + $this->validator->isValid($input), + "Expected '$input' to be considered " . ($result ? '' : 'in') . "valid" + ); + } + } + + /** + * @return void + */ + public function testEmptyStringValueResultsInProperValidationFailureMessages() + { + $this->assertFalse($this->validator->isValid('')); + + $messages = $this->validator->getMessages(); + $arrayExpected = array( + AlnumValidator::STRING_EMPTY => 'The input is an empty string' + ); + $this->assertThat($messages, $this->identicalTo($arrayExpected)); + } + + /** + * @return void + */ + public function testInvalidValueResultsInProperValidationFailureMessages() + { + $this->assertFalse($this->validator->isValid('#')); + $messages = $this->validator->getMessages(); + $arrayExpected = array( + AlnumValidator::NOT_ALNUM => 'The input contains characters which are non alphabetic and no digits' + ); + $this->assertThat($messages, $this->identicalTo($arrayExpected)); + } + + /** + * @ZF-4352 + */ + public function testNonStringValidation() + { + $this->assertFalse($this->validator->isValid(array(1 => 1))); + } + + /** + * @ZF-7475 + */ + public function testIntegerValidation() + { + $this->assertTrue($this->validator->isValid(1)); + } + + public function testEqualsMessageTemplates() + { + $validator = $this->validator; + $this->assertAttributeEquals($validator->getOption('messageTemplates'), + 'messageTemplates', $validator); + } +} diff --git a/test/Validator/AlphaTest.php b/test/Validator/AlphaTest.php new file mode 100644 index 00000000..b0826240 --- /dev/null +++ b/test/Validator/AlphaTest.php @@ -0,0 +1,111 @@ +validator = new AlphaValidator(); + } + + /** + * Ensures that the validator follows expected behavior + * + * @return void + */ + public function testBasic() + { + $valuesExpected = array( + 'abc123' => false, + 'abc 123' => false, + 'abcxyz' => true, + 'AZ@#4.3' => false, + 'aBc123' => false, + 'aBcDeF' => true, + '' => false, + ' ' => false, + "\n" => false + ); + foreach ($valuesExpected as $input => $result) { + $this->assertEquals($result, $this->validator->isValid($input)); + } + } + + /** + * Ensures that getMessages() returns expected default value + * + * @return void + */ + public function testGetMessages() + { + $this->assertEquals(array(), $this->validator->getMessages()); + } + + /** + * Ensures that the allowWhiteSpace option works as expected + * + * @return void + */ + public function testAllowWhiteSpace() + { + $this->validator->setAllowWhiteSpace(true); + + $valuesExpected = array( + 'abc123' => false, + 'abc 123' => false, + 'abcxyz' => true, + 'AZ@#4.3' => false, + 'aBc123' => false, + 'aBcDeF' => true, + '' => false, + ' ' => true, + "\n" => true, + " \t " => true, + "a\tb c" => true + ); + foreach ($valuesExpected as $input => $result) { + $this->assertEquals( + $result, + $this->validator->isValid($input), + "Expected '$input' to be considered " . ($result ? '' : 'in') . "valid" + ); + } + } + + /** + * @ZF-4352 + */ + public function testNonStringValidation() + { + $this->assertFalse($this->validator->isValid(array(1 => 1))); + } + + public function testEqualsMessageTemplates() + { + $validator = $this->validator; + $this->assertAttributeEquals($validator->getOption('messageTemplates'), + 'messageTemplates', $validator); + } +} diff --git a/test/Validator/FloatTest.php b/test/Validator/FloatTest.php new file mode 100644 index 00000000..fd183a19 --- /dev/null +++ b/test/Validator/FloatTest.php @@ -0,0 +1,202 @@ +locale = Locale::getDefault(); + $this->validator = new FloatValidator(); + } + + public function tearDown() + { + Locale::setDefault($this->locale); + } + + /** + * Ensures that the validator follows expected behavior + * + * @dataProvider basicProvider + * @return void + */ + public function testBasic($value, $expected) + { + $this->assertEquals($expected, $this->validator->isValid($value), + 'Failed expecting ' . $value . ' being ' . ($expected ? 'true' : 'false')); + } + + public function basicProvider() + { + return array( + array(1.00, true), + array(0.01, true), + array(-0.1, true), + array('10.1', true), + array(1, true), + array('10.1not a float', false), + ); + } + /** + * Ensures that getMessages() returns expected default value + * + * @return void + */ + public function testGetMessages() + { + $this->assertEquals(array(), $this->validator->getMessages()); + } + + /** + * Ensures that set/getLocale() works + */ + public function testSettingLocales() + { + $this->validator->setLocale('de'); + $this->assertEquals('de', $this->validator->getLocale()); + $this->assertEquals(true, $this->validator->isValid('10,5')); + } + + /** + * @ZF-4352 + */ + public function testNonStringValidation() + { + $this->assertFalse($this->validator->isValid(array(1 => 1))); + } + + /** + * @ZF-7489 + */ + public function testUsingApplicationLocale() + { + Locale::setDefault('de'); + $valid = new FloatValidator(); + $this->assertTrue($valid->isValid(123,456)); + $this->assertTrue($valid->isValid('123,456')); + } + + /** + * @ZF-7987 + */ + public function testLocaleDeFloatType() + { + $this->validator->setLocale('de'); + $this->assertEquals('de', $this->validator->getLocale()); + $this->assertEquals(true, $this->validator->isValid(10.5)); + } + + /** + * @ZF-7987 + */ + public function testPhpLocaleDeFloatType() + { + Locale::setDefault('de'); + $valid = new FloatValidator(); + $this->assertTrue($valid->isValid(10.5)); + } + + /** + * @ZF-7987 + */ + public function testPhpLocaleFrFloatType() + { + Locale::setDefault('fr'); + $valid = new FloatValidator(); + $this->assertTrue($valid->isValid(10.5)); + } + + public function deLocaleStringsProvider() + { + return array( + array('1,3', true), + array('1000,3', true), + array('1.000,3', true), + ); + } + + /** + * @ZF-8919 + * @dataProvider deLocaleStringsProvider + */ + public function testPhpLocaleDeStringType($float, $expected) + { + Locale::setDefault('de_AT'); + $valid = new FloatValidator(array('locale' => 'de_AT')); + $this->assertEquals($expected, $valid->isValid($float)); + } + + public function frLocaleStringsProvider() + { + return array( + array('1,3', true), + array('1000,3', true), + array('1 000,3', true), + array('1.3', false), + array('1000.3', false), + array('1,000.3', false), + ); + } + + /** + * @ZF-8919 + * @dataProvider frLocaleStringsProvider + */ + public function testPhpLocaleFrStringType($float, $expected) + { + $valid = new FloatValidator(array('locale' => 'fr_FR')); + $this->assertEquals($expected, $valid->isValid($float)); + } + + public function enLocaleStringsProvider() + { + return array( + array('1.3', true), + array('1000.3', true), + array('1,000.3', true), + ); + } + + /** + * @ZF-8919 + * @dataProvider enLocaleStringsProvider + */ + public function testPhpLocaleEnStringType($float, $expected) + { + $valid = new FloatValidator(array('locale' => 'en_US')); + $this->assertEquals($expected, $valid->isValid($float)); + } + + public function testEqualsMessageTemplates() + { + $validator = $this->validator; + $this->assertAttributeEquals($validator->getOption('messageTemplates'), + 'messageTemplates', $validator); + } +} diff --git a/test/Validator/IntTest.php b/test/Validator/IntTest.php new file mode 100644 index 00000000..25a17b4f --- /dev/null +++ b/test/Validator/IntTest.php @@ -0,0 +1,124 @@ +locale = Locale::getDefault(); + $this->validator = new IntValidator(); + } + + public function tearDown() + { + Locale::setDefault($this->locale); + } + + public function intDataProvider() + { + return array( + array(1.00, true), + array(0.00, true), + array(0.01, false), + array(-0.1, false), + array(-1, true), + array('10', true), + array(1, true), + array('not an int', false), + array(true, false), + array(false, false), + ); + } + + /** + * Ensures that the validator follows expected behavior + * + * @dataProvider intDataProvider() + * @return void + */ + public function testBasic($intVal, $expected) + { + $this->validator->setLocale('en'); + $this->assertEquals($expected, $this->validator->isValid($intVal)); + } + + /** + * Ensures that getMessages() returns expected default value + * + * @return void + */ + public function testGetMessages() + { + $this->assertEquals(array(), $this->validator->getMessages()); + } + + /** + * Ensures that set/getLocale() works + */ + public function testSettingLocales() + { + $this->validator->setLocale('de'); + $this->assertEquals('de', $this->validator->getLocale()); + $this->assertEquals(false, $this->validator->isValid('10 000')); + $this->assertEquals(true, $this->validator->isValid('10.000')); + } + + /** + * @ZF-4352 + */ + public function testNonStringValidation() + { + $this->assertFalse($this->validator->isValid(array(1 => 1))); + } + + /** + * @ZF-7489 + */ + public function testUsingApplicationLocale() + { + Locale::setDefault('de'); + $valid = new IntValidator(); + $this->assertTrue($valid->isValid('10.000')); + } + + /** + * @ZF-7703 + */ + public function testLocaleDetectsNoEnglishLocaleOnOtherSetLocale() + { + Locale::setDefault('de'); + $valid = new IntValidator(); + $this->assertTrue($valid->isValid(1200)); + $this->assertFalse($valid->isValid('1,200')); + } + + public function testEqualsMessageTemplates() + { + $validator = $this->validator; + $this->assertAttributeEquals($validator->getOption('messageTemplates'), + 'messageTemplates', $validator); + } +} diff --git a/test/Validator/PostCodeTest.php b/test/Validator/PostCodeTest.php new file mode 100644 index 00000000..09d747a4 --- /dev/null +++ b/test/Validator/PostCodeTest.php @@ -0,0 +1,179 @@ +validator = new PostCodeValidator(array('locale' => 'de_AT')); + } + + public function postCodesDataProvider() + { + return array( + array('2292', true), + array('1000', true), + array('0000', true), + array('12345', false), + array(1234, true), + array(9821, true), + array('21A4', false), + array('ABCD', false), + array(true, false), + array('AT-2292', false), + array(1.56, false), + ); + } + + /** + * Ensures that the validator follows expected behavior + * + * @dataProvider postCodesDataProvider + * @return void + */ + public function testBasic($postCode, $expected) + { + $this->assertEquals($expected, $this->validator->isValid($postCode)); + } + + /** + * Ensures that getMessages() returns expected default value + * + * @return void + */ + public function testGetMessages() + { + $this->assertEquals(array(), $this->validator->getMessages()); + } + + /** + * Ensures that a region is available + */ + public function testSettingLocalesWithoutRegion() + { + $this->setExpectedException('Zend\Validator\Exception\InvalidArgumentException', 'Locale must contain a region'); + $this->validator->setLocale('de')->isValid('1000'); + } + + /** + * Ensures that the region contains postal codes + */ + public function testSettingLocalesWithoutPostalCodes() + { + $this->setExpectedException('Zend\Validator\Exception\InvalidArgumentException', 'A postcode-format string has to be given for validation'); + $this->validator->setLocale('gez_ER')->isValid('1000'); + } + + /** + * Ensures locales can be retrieved + */ + public function testGettingLocale() + { + $this->assertEquals('de_AT', $this->validator->getLocale()); + } + + /** + * Ensures format can be set and retrieved + */ + public function testSetGetFormat() + { + $this->validator->setFormat('\d{1}'); + $this->assertEquals('\d{1}', $this->validator->getFormat()); + } + + public function testSetGetFormatThrowsExceptionOnNullFormat() + { + $this->setExpectedException('Zend\Validator\Exception\InvalidArgumentException', 'A postcode-format string has to be given'); + $this->validator->setLocale(null)->setFormat(null)->isValid('1000'); + } + + public function testSetGetFormatThrowsExceptionOnEmptyFormat() + { + $this->setExpectedException('Zend\Validator\Exception\InvalidArgumentException', 'A postcode-format string has to be given'); + $this->validator->setLocale(null)->setFormat('')->isValid('1000'); + } + + /** + * @group ZF-9212 + */ + public function testErrorMessageText() + { + $this->assertFalse($this->validator->isValid('hello')); + $message = $this->validator->getMessages(); + $this->assertContains('not appear to be a postal code', $message['postcodeNoMatch']); + } + + /** + * Test service class with invalid validation + * + * @group ZF2-44 + */ + public function testServiceClass() + { + $params = (object)array( + 'serviceTrue' => null, + 'serviceFalse' => null, + ); + + $serviceTrue = function($value) use ($params) { + $params->serviceTrue = $value; + return true; + }; + + $serviceFalse = function($value) use ($params) { + $params->serviceFalse = $value; + return false; + }; + + $this->assertEquals(null, $this->validator->getService()); + + + $this->validator->setService($serviceTrue); + $this->assertEquals($this->validator->getService(), $serviceTrue); + $this->assertTrue($this->validator->isValid('2292')); + $this->assertEquals($params->serviceTrue, '2292'); + + + $this->validator->setService($serviceFalse); + $this->assertEquals($this->validator->getService(), $serviceFalse); + $this->assertFalse($this->validator->isValid('hello')); + $this->assertEquals($params->serviceFalse, 'hello'); + + $message = $this->validator->getMessages(); + $this->assertContains('not appear to be a postal code', $message['postcodeService']); + } + + public function testEqualsMessageTemplates() + { + $validator = $this->validator; + $this->assertAttributeEquals($validator->getOption('messageTemplates'), + 'messageTemplates', $validator); + } +} diff --git a/test/View/Helper/CurrencyFormatTest.php b/test/View/Helper/CurrencyFormatTest.php new file mode 100644 index 00000000..164dc2fd --- /dev/null +++ b/test/View/Helper/CurrencyFormatTest.php @@ -0,0 +1,101 @@ +helper = new CurrencyHelper(); + } + + /** + * Tears down the fixture, for example, close a network connection. + * This method is called after a test is executed. + * + * @return void + */ + public function tearDown() + { + unset($this->helper); + } + + public function currencyTestsDataProvider() + { + return array( + // locale currency number expected + array('de_AT', 'EUR', 1234.56, '€ 1.234,56'), + array('de_AT', 'EUR', 0.123, '€ 0,12'), + array('de_DE', 'EUR', 1234567.891234567890000, '1.234.567,89 €'), + array('de_DE', 'RUR', 1234567.891234567890000, '1.234.567,89 RUR'), + array('ru_RU', 'EUR', 1234567.891234567890000, '1 234 567,89 €'), + array('ru_RU', 'RUR', 1234567.891234567890000, '1 234 567,89 р.'), + array('en_US', 'EUR', 1234567.891234567890000, '€1,234,567.89'), + array('en_US', 'RUR', 1234567.891234567890000, 'RUR1,234,567.89'), + array('en_US', 'USD', 1234567.891234567890000, '$1,234,567.89'), + ); + } + + /** + * @dataProvider currencyTestsDataProvider + */ + public function testBasic($locale, $currencyCode, $number, $expected) + { + $this->assertMbStringEquals($expected, $this->helper->__invoke( + $number, $currencyCode, $locale + )); + } + + /** + * @dataProvider currencyTestsDataProvider + */ + public function testSettersProvideDefaults($locale, $currencyCode, $number, $expected) + { + $this->helper + ->setLocale($locale) + ->setCurrencyCode($currencyCode); + + $this->assertMbStringEquals($expected, $this->helper->__invoke($number)); + } + + public function testDefaultLocale() + { + $this->assertEquals(Locale::getDefault(), $this->helper->getLocale()); + } + + public function assertMbStringEquals($expected, $test, $message = '') + { + $expected = str_replace(array("\xC2\xA0", ' '), '', $expected); + $test = str_replace(array("\xC2\xA0", ' '), '', $test); + $this->assertEquals($expected, $test, $message); + } +} diff --git a/test/View/Helper/DateFormatTest.php b/test/View/Helper/DateFormatTest.php new file mode 100644 index 00000000..38873d57 --- /dev/null +++ b/test/View/Helper/DateFormatTest.php @@ -0,0 +1,199 @@ +helper = new DateFormatHelper(); + } + + /** + * Tears down the fixture, for example, close a network connection. + * This method is called after a test is executed. + * + * @return void + */ + public function tearDown() + { + unset($this->helper); + } + + public function currencyTestsDataProvider() + { + $date = new DateTime('2012-07-02T22:44:03Z'); + return array( + // FULL format varies based on OS + // array( + // 'de_DE', + // 'Europe/Berlin', + // IntlDateFormatter::FULL, + // IntlDateFormatter::FULL, + // $date, + // 'Dienstag, 3. Juli 2012 00:44:03 Deutschland', + // ), + array( + 'de_DE', + 'Europe/Berlin', + IntlDateFormatter::LONG, + IntlDateFormatter::LONG, + $date, + '3. Juli 2012 00:44:03 MESZ', + ), + array( + 'de_DE', + 'Europe/Berlin', + IntlDateFormatter::MEDIUM, + IntlDateFormatter::MEDIUM, + $date, + '03.07.2012 00:44:03', + ), + array( + 'de_DE', + 'Europe/Berlin', + IntlDateFormatter::SHORT, + IntlDateFormatter::SHORT, + $date, + '03.07.12 00:44', + ), + // FULL format varies based on OS + // array( + // 'ru_RU', + // 'Europe/Moscow', + // IntlDateFormatter::FULL, + // IntlDateFormatter::FULL, + // $date, + // '3 июля 2012 г. 2:44:03 Россия (Москва)', + // ), + // LONG format varies based on OS for ru_RU locale + // array( + // 'ru_RU', + // 'Europe/Moscow', + // IntlDateFormatter::LONG, + // IntlDateFormatter::LONG, + // $date, + // '3 июля 2012 г. 2:44:03 GMT+04:00', + // ), + array( + 'ru_RU', + 'Europe/Moscow', + IntlDateFormatter::MEDIUM, + IntlDateFormatter::MEDIUM, + $date, + '03.07.2012 2:44:03', + ), + array( + 'ru_RU', + 'Europe/Moscow', + IntlDateFormatter::SHORT, + IntlDateFormatter::SHORT, + $date, + '03.07.12 2:44', + ), + // FULL format varies based on OS + // array( + // 'en_US', + // 'America/New_York', + // IntlDateFormatter::FULL, + // IntlDateFormatter::FULL, + // $date, + // 'Monday, July 2, 2012 6:44:03 PM ET', + // ), + array( + 'en_US', + 'America/New_York', + IntlDateFormatter::LONG, + IntlDateFormatter::LONG, + $date, + 'July 2, 2012 6:44:03 PM EDT', + ), + array( + 'en_US', + 'America/New_York', + IntlDateFormatter::MEDIUM, + IntlDateFormatter::MEDIUM, + $date, + 'Jul 2, 2012 6:44:03 PM', + ), + array( + 'en_US', + 'America/New_York', + IntlDateFormatter::SHORT, + IntlDateFormatter::SHORT, + $date, + '7/2/12 6:44 PM', + ), + ); + } + + /** + * @dataProvider currencyTestsDataProvider + */ + public function testBasic($locale, $timezone, $timeType, $dateType, $date, $expected) + { + $this->helper->setTimezone($timezone); + $this->assertMbStringEquals($expected, $this->helper->__invoke( + $date, $dateType, $timeType, $locale + )); + } + + /** + * @dataProvider currencyTestsDataProvider + */ + public function testSettersProvideDefaults($locale, $timezone, $timeType, $dateType, $date, $expected) + { + $this->helper + ->setTimezone($timezone) + ->setLocale($locale); + + $this->assertMbStringEquals($expected, $this->helper->__invoke( + $date, $dateType, $timeType + )); + } + + public function testDefaultLocale() + { + $this->assertEquals(Locale::getDefault(), $this->helper->getLocale()); + } + + public function assertMbStringEquals($expected, $test, $message = '') + { + $expected = str_replace(array("\xC2\xA0", ' '), '', $expected); + $test = str_replace(array("\xC2\xA0", ' '), '', $test); + $this->assertEquals($expected, $test, $message); + } +} diff --git a/test/View/Helper/NumberFormatTest.php b/test/View/Helper/NumberFormatTest.php new file mode 100644 index 00000000..d168ac78 --- /dev/null +++ b/test/View/Helper/NumberFormatTest.php @@ -0,0 +1,158 @@ +helper = new NumberFormatHelper(); + } + + /** + * Tears down the fixture, for example, close a network connection. + * This method is called after a test is executed. + * + * @return void + */ + public function tearDown() + { + unset($this->helper); + } + + public function currencyTestsDataProvider() + { + return array( + array( + 'de_DE', + NumberFormatter::DECIMAL, + NumberFormatter::TYPE_DOUBLE, + 1234567.891234567890000, + '1.234.567,891' + ), + array( + 'de_DE', + NumberFormatter::PERCENT, + NumberFormatter::TYPE_DOUBLE, + 1234567.891234567890000, + '123.456.789 %' + ), + array( + 'de_DE', + NumberFormatter::SCIENTIFIC, + NumberFormatter::TYPE_DOUBLE, + 1234567.891234567890000, + '1,23456789123457E6' + ), + array( + 'ru_RU', + NumberFormatter::DECIMAL, + NumberFormatter::TYPE_DOUBLE, + 1234567.891234567890000, + '1 234 567,891' + ), + array( + 'ru_RU', + NumberFormatter::PERCENT, + NumberFormatter::TYPE_DOUBLE, + 1234567.891234567890000, + '123 456 789 %' + ), + array( + 'ru_RU', + NumberFormatter::SCIENTIFIC, + NumberFormatter::TYPE_DOUBLE, + 1234567.891234567890000, + '1,23456789123457E6' + ), + array( + 'en_US', + NumberFormatter::DECIMAL, + NumberFormatter::TYPE_DOUBLE, + 1234567.891234567890000, + '1,234,567.891' + ), + array( + 'en_US', + NumberFormatter::PERCENT, + NumberFormatter::TYPE_DOUBLE, + 1234567.891234567890000, + '123,456,789%' + ), + array( + 'en_US', + NumberFormatter::SCIENTIFIC, + NumberFormatter::TYPE_DOUBLE, + 1234567.891234567890000, + '1.23456789123457E6' + ), + ); + } + + /** + * @dataProvider currencyTestsDataProvider + */ + public function testBasic($locale, $formatStyle, $formatType, $number, $expected) + { + $this->assertMbStringEquals($expected, $this->helper->__invoke( + $number, $formatStyle, $formatType, $locale + )); + } + + /** + * @dataProvider currencyTestsDataProvider + */ + public function testSettersProvideDefaults($locale, $formatStyle, $formatType, $number, $expected) + { + $this->helper + ->setLocale($locale) + ->setFormatStyle($formatStyle) + ->setFormatType($formatType); + + $this->assertMbStringEquals($expected, $this->helper->__invoke($number)); + } + + public function testDefaultLocale() + { + $this->assertEquals(Locale::getDefault(), $this->helper->getLocale()); + } + + public function assertMbStringEquals($expected, $test, $message = '') + { + $expected = str_replace(array("\xC2\xA0", ' '), '', $expected); + $test = str_replace(array("\xC2\xA0", ' '), '', $test); + $this->assertEquals($expected, $test, $message); + } +}