/** * Currency format a number * * @throws Exception\RuntimeException * @param float|string|int $number * @return string */ public function format($number, ArrayObject $row = null) { $locale = $this->params['locale']; //$formatterId = md5($locale); $formatterId = $locale . (string) $this->params['pattern']; if (!array_key_exists($formatterId, $this->formatters)) { $this->loadFormatterId($formatterId); } if ($number !== null && !is_numeric($number)) { $this->throwNumberFormatterException($this->formatters[$formatterId], $number); } if ($this->unit_column !== null) { if (!isset($row[$this->unit_column])) { throw new Exception\RuntimeException(__METHOD__ . " Cannot determine unit code based on column '{$this->unit_column}'."); } $value = $this->formatters[$formatterId]->format($number) . ' ' . $row[$this->unit_column]; } elseif ($this->params['unit'] != '') { $value = $this->formatters[$formatterId]->format($number) . ' ' . $this->params['unit']; } else { throw new Exception\RuntimeException(__METHOD__ . " Unit code must be set prior to use the UnitFormatter"); } if (intl_is_failure($this->formatters[$formatterId]->getErrorCode())) { $this->throwNumberFormatterException($this->formatters[$formatterId], $number); } return $value; }
/** * * {@inheritdoc} * */ public function load($resource, $locale, $domain = 'messages') { if (!stream_is_local($resource)) { throw new InvalidResourceException(sprintf('This is not a local file "%s".', $resource)); } if (!is_dir($resource)) { throw new NotFoundResourceException(sprintf('File "%s" not found.', $resource)); } try { $rb = new \ResourceBundle($locale, $resource); } catch (\Exception $e) { // HHVM compatibility: constructor throws on invalid resource $rb = null; } if (!$rb) { throw new InvalidResourceException(sprintf('Cannot load resource "%s"', $resource)); } elseif (intl_is_failure($rb->getErrorCode())) { throw new InvalidResourceException($rb->getErrorMessage(), $rb->getErrorCode()); } $messages = $this->flatten($rb); $catalogue = new MessageCatalogue($locale); $catalogue->add($messages, $domain); if (class_exists('Symfony\\Component\\Config\\Resource\\DirectoryResource')) { $catalogue->addResource(new DirectoryResource($resource)); } return $catalogue; }
/** * Transforms a localized number into an integer or float * * @param string $value */ public function reverseTransform($value) { if (!is_string($value)) { throw new \InvalidArgumentException(sprintf('Expected argument of type string, %s given', gettype($value))); } $formatter = $this->getNumberFormatter(); $value = $formatter->parse($value); if (intl_is_failure($formatter->getErrorCode())) { throw new TransformationFailedException($formatter->getErrorMessage()); } return $value; }
/** * {@inheritdoc} */ public function load($resource, $locale, $domain = 'messages') { $rb = new \ResourceBundle($locale, $resource); if (!$rb) { throw new \RuntimeException("cannot load this resource : {$resource}"); } elseif (intl_is_failure($rb->getErrorCode())) { throw new \RuntimeException($rb->getErrorMessage(), $rb->getErrorCode()); } $messages = $this->flatten($rb); $catalogue = new MessageCatalogue($locale); $catalogue->add($messages, $domain); $catalogue->addResource(new FileResource($resource . '.dat')); return $catalogue; }
/** * Transforms a localized number into an integer or float * * @param string $value */ public function reverseTransform($value) { if (!is_string($value)) { throw new UnexpectedTypeException($value, 'string'); } if ('' === $value) { return null; } $formatter = $this->getNumberFormatter(); $value = $formatter->parse($value); if (intl_is_failure($formatter->getErrorCode())) { throw new TransformationFailedException($formatter->getErrorMessage()); } return $value; }
/** * {@inheritdoc} */ public function validate($value, Constraint $constraint) { if (null === $value || '' === $value) { return; } if (!is_scalar($value)) { throw new UnexpectedTypeException($value, 'string'); } $formatter = new \NumberFormatter(\Locale::getDefault(), \NumberFormatter::DECIMAL); $position = 0; $formatter->parse($value, \NumberFormatter::TYPE_DOUBLE, $position); if (intl_is_failure($formatter->getErrorCode()) || $position < strlen($value)) { /** @var Decimal $constraint */ $this->context->addViolation($constraint->message, ['{{ value }}' => $this->formatValue($value)]); } }
/** * Transforms between a percentage value into a normalized format (integer or float). * * @param number $value Percentage value. * @return number Normalized value. */ public function reverseTransform($value) { if (!is_string($value)) { throw new \InvalidArgumentException(sprintf('Expected argument of type string, %s given', gettype($value))); } $formatter = $this->getNumberFormatter(); // replace normal spaces so that the formatter can read them $value = $formatter->parse(str_replace(' ', ' ', $value)); if (intl_is_failure($formatter->getErrorCode())) { throw new TransformationFailedException($formatter->getErrorMessage()); } if (self::FRACTIONAL == $this->getOption('type')) { $value /= 100; } return $value; }
/** * {@inheritdoc} */ public function reverseTransform($value) { if (!is_string($value)) { throw new TransformationFailedException('Expected a string.'); } if ('' === $value) { return; } if ('NaN' === $value) { throw new TransformationFailedException('"NaN" is not a valid integer'); } $formatter = $this->getNumberFormatter(); $value = $formatter->parse($value, PHP_INT_SIZE == 8 ? $formatter::TYPE_INT64 : $formatter::TYPE_INT32); if (intl_is_failure($formatter->getErrorCode())) { throw new TransformationFailedException($formatter->getErrorMessage()); } return $value; }
/** * Transforms between a percentage value into a normalized format (integer or float). * * @param number $value Percentage value. * @return number Normalized value. */ public function reverseTransform($value) { if (!is_string($value)) { throw new UnexpectedTypeException($value, 'string'); } if ('' === $value) { return null; } $formatter = $this->getNumberFormatter(); // replace normal spaces so that the formatter can read them $value = $formatter->parse(str_replace(' ', ' ', $value)); if (intl_is_failure($formatter->getErrorCode())) { throw new TransformationFailedException($formatter->getErrorMessage()); } if (self::FRACTIONAL == $this->getOption('type')) { $value /= 100; } return $value; }
/** * @inheritdoc */ public function transform($value) { if (!is_string($value) && !is_numeric($value)) { throw new TransformationFailedException(sprintf('Expected a string to transform, got "%s" instead.', json_encode($value))); } if ('' === $value) { return null; } if ('NaN' === $value) { throw new TransformationFailedException('"NaN" is not a valid number'); } $position = 0; $formatter = $this->getNumberFormatter(); $groupSep = $formatter->getSymbol(\NumberFormatter::GROUPING_SEPARATOR_SYMBOL); $decSep = $formatter->getSymbol(\NumberFormatter::DECIMAL_SEPARATOR_SYMBOL); if ('.' !== $decSep && (!$this->grouping || '.' !== $groupSep)) { $value = str_replace('.', $decSep, $value); } if (',' !== $decSep && (!$this->grouping || ',' !== $groupSep)) { $value = str_replace(',', $decSep, $value); } $result = $formatter->parse($value, \NumberFormatter::TYPE_DOUBLE, $position); if (intl_is_failure($formatter->getErrorCode())) { throw new TransformationFailedException($formatter->getErrorMessage()); } if ($result >= PHP_INT_MAX || $result <= -PHP_INT_MAX) { throw new TransformationFailedException('I don\'t have a clear idea what infinity looks like'); } $encoding = mb_detect_encoding($value); $length = mb_strlen($value, $encoding); // After parsing, position holds the index of the character where the parsing stopped if ($position < $length) { // Check if there are unrecognized characters at the end of the // number (excluding whitespace characters) $remainder = trim(mb_substr($value, $position, $length, $encoding), " \t\n\r\v "); if ('' !== $remainder) { throw new TransformationFailedException(sprintf('The number contains unrecognized characters: "%s"', $remainder)); } } // Only the format() method in the NumberFormatter rounds, whereas parse() does not return $this->round($result); }
/** * {@inheritdoc} */ public function format($value, array $options = array()) { if (null === $value) { return $options['null_value']; } if (!is_numeric($value)) { throw FormatterException::invalidType($this, $value, 'numeric value'); } $formatter = new \NumberFormatter(\Locale::getDefault(), \NumberFormatter::DECIMAL); if (null !== $options['precision']) { $formatter->setAttribute(\NumberFormatter::FRACTION_DIGITS, $options['precision']); $formatter->setAttribute(\NumberFormatter::ROUNDING_MODE, $options['rounding_mode']); } $formatter->setAttribute(\NumberFormatter::GROUPING_USED, $options['grouping']); $value = $formatter->format($value); if (intl_is_failure($formatter->getErrorCode())) { throw FormatterException::intlError($this, $formatter->getErrorMessage()); } return $value; }
/** * {@inheritdoc} */ public function load($resource, $locale, $domain = 'messages') { if (!stream_is_local($resource . '.dat')) { throw new InvalidResourceException(sprintf('This is not a local file "%s".', $resource)); } if (!file_exists($resource . '.dat')) { throw new NotFoundResourceException(sprintf('File "%s" not found.', $resource)); } $rb = new \ResourceBundle($locale, $resource); if (!$rb) { throw new InvalidResourceException(sprintf('Cannot load resource "%s"', $resource)); } elseif (intl_is_failure($rb->getErrorCode())) { throw new InvalidResourceException($rb->getErrorMessage(), $rb->getErrorCode()); } $messages = $this->flatten($rb); $catalogue = new MessageCatalogue($locale); $catalogue->add($messages, $domain); $catalogue->addResource(new FileResource($resource . '.dat')); return $catalogue; }
/** * {@inheritdoc} */ public function format($value, array $options = array()) { if (null === $value) { return $options['null_value']; } if (!$value instanceof \DateTime) { throw FormatterException::invalidType($this, $value, 'DateTime instance'); } $dateTime = clone $value; if ('UTC' !== $options['time_zone']) { $dateTime->setTimezone(new \DateTimeZone('UTC')); } $formatter = new \IntlDateFormatter(\Locale::getDefault(), $options['date_format'], $options['time_format'], $options['time_zone'], $options['calendar'], $options['pattern']); $formatter->setLenient(false); $value = $formatter->format((int) $dateTime->format('U')); if (intl_is_failure(intl_get_error_code())) { throw FormatterException::intlError($this, intl_get_error_message()); } $value = preg_replace('~GMT\\+00:00$~', 'GMT', $value); return $value; }
/** * {@inheritdoc} */ public function validate($value, Constraint $constraint) { if (null === $value || '' === $value) { return; } if (!is_scalar($value)) { throw new UnexpectedTypeException($value, 'string'); } $formatter = new \NumberFormatter(\Locale::getDefault(), \NumberFormatter::DECIMAL); $formatter->setAttribute(\NumberFormatter::ROUNDING_MODE, \NumberFormatter::ROUND_DOWN); $formatter->setAttribute(\NumberFormatter::FRACTION_DIGITS, 0); $formatter->setAttribute(\NumberFormatter::MIN_FRACTION_DIGITS, 0); $formatter->setAttribute(\NumberFormatter::MAX_FRACTION_DIGITS, 0); $decimalSeparator = $formatter->getSymbol(\NumberFormatter::DECIMAL_SEPARATOR_SYMBOL); $position = 0; $formatter->parse($value, PHP_INT_SIZE == 8 ? $formatter::TYPE_INT64 : $formatter::TYPE_INT32, $position); if (intl_is_failure($formatter->getErrorCode()) || strpos($value, $decimalSeparator) !== false || $position < strlen($value)) { /** @var Integer $constraint */ $this->context->addViolation($constraint->message, ['{{ value }}' => $this->formatValue($value)]); } }
/** * Transforms a normalized format into a localized money string. * * @param MoneyValue $value Normalized number * * @throws TransformationFailedException If the given value is not numeric or * if the value can not be transformed * * @return string Localized money string */ public function transform($value) { if (null === $value) { return ''; } if (!$value instanceof MoneyValue) { throw new TransformationFailedException('Expected a MoneyValue object.'); } if (!is_numeric($value->value)) { throw new TransformationFailedException('Expected a numeric value.'); } $amountValue = $value->value; $amountValue /= $this->divisor; $formatter = $this->getNumberFormatter(); $value = $formatter->formatCurrency($amountValue, $value->currency); if (intl_is_failure($formatter->getErrorCode())) { throw new TransformationFailedException($formatter->getErrorMessage()); } // Convert fixed spaces to normal ones $value = str_replace(" ", ' ', $value); return $value; }
public function getDisplayedValue($value) { if ($value !== null && $value !== '') { $formatter = new \NumberFormatter($this->locale, $this->style); if ($this->precision !== null) { $formatter->setAttribute(\NumberFormatter::FRACTION_DIGITS, $this->precision); $formatter->setAttribute(\NumberFormatter::ROUNDING_MODE, $this->roundingMode); } if ($this->ruleSet !== null) { $formatter->setTextAttribute(\NumberFormatter::DEFAULT_RULESET, $this->ruleSet); } $formatter->setAttribute(\NumberFormatter::GROUPING_USED, $this->grouping); if ($this->style === \NumberFormatter::PERCENT && !$this->fractional) { $value /= 100; } if ($this->style === \NumberFormatter::CURRENCY) { if ($this->currencyCode === null) { $this->currencyCode = $formatter->getTextAttribute(\NumberFormatter::CURRENCY_CODE); } if (strlen($this->currencyCode) !== 3) { throw new TransformationFailedException('Your locale definition is not complete, you have to define a language and a country. (.e.g en_US, fr_FR)'); } $value = $formatter->formatCurrency($value, $this->currencyCode); } else { $value = $formatter->format($value); } if (intl_is_failure($formatter->getErrorCode())) { throw new TransformationFailedException($formatter->getErrorMessage()); } if (key_exists((string) $value, $this->values)) { $value = $this->values[$value]; } return $value; } return ''; }
/** * @dataProvider parseErrorProvider */ public function testParseErrorIntl($pattern, $value) { $errorCode = StubIntl::U_PARSE_ERROR; $errorMessage = 'Date parsing failed: U_PARSE_ERROR'; $this->skipIfIntlExtensionIsNotLoaded(); $formatter = $this->createIntlFormatter($pattern); $this->assertFalse($formatter->parse($value)); $this->assertSame($errorMessage, intl_get_error_message()); $this->assertSame($errorCode, intl_get_error_code()); $this->assertTrue(intl_is_failure(intl_get_error_code())); }
/** * Transforms a localized number into an integer or float. * * @param string $value The localized value * * @return int|float The numeric value * * @throws TransformationFailedException If the given value is not a string * or if the value can not be transformed. */ public function reverseTransform($value) { if (!is_string($value)) { throw new TransformationFailedException('Expected a string.'); } if ('' === $value) { return; } if ('NaN' === $value) { throw new TransformationFailedException('"NaN" is not a valid number'); } $position = 0; $formatter = $this->getNumberFormatter(); $groupSep = $formatter->getSymbol(\NumberFormatter::GROUPING_SEPARATOR_SYMBOL); $decSep = $formatter->getSymbol(\NumberFormatter::DECIMAL_SEPARATOR_SYMBOL); if ('.' !== $decSep && (!$this->grouping || '.' !== $groupSep)) { $value = str_replace('.', $decSep, $value); } if (',' !== $decSep && (!$this->grouping || ',' !== $groupSep)) { $value = str_replace(',', $decSep, $value); } if (false !== strpos($value, $decSep)) { $type = \NumberFormatter::TYPE_DOUBLE; } else { $type = PHP_INT_SIZE === 8 ? \NumberFormatter::TYPE_INT64 : \NumberFormatter::TYPE_INT32; } $result = $formatter->parse($value, $type, $position); if (intl_is_failure($formatter->getErrorCode())) { throw new TransformationFailedException($formatter->getErrorMessage()); } if ($result >= PHP_INT_MAX || $result <= -PHP_INT_MAX) { throw new TransformationFailedException('I don\'t have a clear idea what infinity looks like'); } if (false !== ($encoding = mb_detect_encoding($value, null, true))) { $length = mb_strlen($value, $encoding); $remainder = mb_substr($value, $position, $length, $encoding); } else { $length = strlen($value); $remainder = substr($value, $position, $length); } // After parsing, position holds the index of the character where the // parsing stopped if ($position < $length) { // Check if there are unrecognized characters at the end of the // number (excluding whitespace characters) $remainder = trim($remainder, " \t\n\r\v "); if ('' !== $remainder) { throw new TransformationFailedException(sprintf('The number contains unrecognized characters: "%s"', $remainder)); } } // NumberFormatter::parse() does not round return $this->round($result); }
/** * @dataProvider parseProvider */ public function testParseIntl($pattern, $value, $expected, $errorCode = 0, $errorMessage = 'U_ZERO_ERROR') { $this->skipIfIntlExtensionIsNotLoaded(); $formatter = $this->createIntlFormatter($pattern); $this->assertSame($expected, $formatter->parse($value)); $this->assertSame($errorMessage, intl_get_error_message()); $this->assertSame($errorCode, intl_get_error_code()); $this->assertSame($errorCode != 0, intl_is_failure(intl_get_error_code())); }
/** * Returns true if and only if $value is a valid integer * * @param string|int $value * @return bool * @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; }
protected function isIntlFailure($errorCode) { return intl_is_failure($errorCode); }
/** * Transforms a localized number into an integer or float * * @param string $value The localized value * * @return integer|float The numeric value * * @throws UnexpectedTypeException if the given value is not a string * @throws TransformationFailedException if the value can not be transformed */ public function reverseTransform($value) { if (!is_string($value)) { throw new UnexpectedTypeException($value, 'string'); } if ('' === $value) { return null; } if ('NaN' === $value) { throw new TransformationFailedException('"NaN" is not a valid number'); } $position = 0; $formatter = $this->getNumberFormatter(); $groupSep = $formatter->getSymbol(\NumberFormatter::GROUPING_SEPARATOR_SYMBOL); $decSep = $formatter->getSymbol(\NumberFormatter::DECIMAL_SEPARATOR_SYMBOL); if ('.' !== $decSep && (!$this->grouping || '.' !== $groupSep)) { $value = str_replace('.', $decSep, $value); } if (',' !== $decSep && (!$this->grouping || ',' !== $groupSep)) { $value = str_replace(',', $decSep, $value); } $result = $formatter->parse($value, \NumberFormatter::TYPE_DOUBLE, $position); if (intl_is_failure($formatter->getErrorCode())) { throw new TransformationFailedException($formatter->getErrorMessage()); } if ($result >= INF || $result <= -INF) { throw new TransformationFailedException('I don\'t have a clear idea what infinity looks like'); } // After parsing, position holds the index of the character where the // parsing stopped if ($position < strlen($value)) { // Check if there are unrecognized characters at the end of the // number $remainder = substr($value, $position); // Remove all whitespace characters if ('' !== preg_replace('/[\s\xc2\xa0]*/', '', $remainder)) { throw new TransformationFailedException( sprintf('The number contains unrecognized characters: "%s"', $remainder )); } } return $result; }
function renderPage() { $LINKSDB = new LinkDB($GLOBALS['config']['DATASTORE'], isLoggedIn(), $GLOBALS['config']['HIDE_PUBLIC_LINKS'], $GLOBALS['redirector'], $GLOBALS['config']['REDIRECTOR_URLENCODE']); $updater = new Updater(read_updates_file($GLOBALS['config']['UPDATES_FILE']), $GLOBALS, $LINKSDB, isLoggedIn()); try { $newUpdates = $updater->update(); if (!empty($newUpdates)) { write_updates_file($GLOBALS['config']['UPDATES_FILE'], $updater->getDoneUpdates()); } } catch (Exception $e) { die($e->getMessage()); } $PAGE = new PageBuilder(); $PAGE->assign('linkcount', count($LINKSDB)); $PAGE->assign('privateLinkcount', count_private($LINKSDB)); // Determine which page will be rendered. $query = isset($_SERVER['QUERY_STRING']) ? $_SERVER['QUERY_STRING'] : ''; $targetPage = Router::findPage($query, $_GET, isLoggedIn()); // Call plugin hooks for header, footer and includes, specifying which page will be rendered. // Then assign generated data to RainTPL. $common_hooks = array('includes', 'header', 'footer'); $pluginManager = PluginManager::getInstance(); foreach ($common_hooks as $name) { $plugin_data = array(); $pluginManager->executeHooks('render_' . $name, $plugin_data, array('target' => $targetPage, 'loggedin' => isLoggedIn())); $PAGE->assign('plugins_' . $name, $plugin_data); } // -------- Display login form. if ($targetPage == Router::$PAGE_LOGIN) { if ($GLOBALS['config']['OPEN_SHAARLI']) { header('Location: ?'); exit; } // No need to login for open Shaarli $token = ''; if (ban_canLogin()) { $token = getToken(); } // Do not waste token generation if not useful. $PAGE->assign('token', $token); if (isset($_GET['username'])) { $PAGE->assign('username', escape($_GET['username'])); } $PAGE->assign('returnurl', isset($_SERVER['HTTP_REFERER']) ? escape($_SERVER['HTTP_REFERER']) : ''); $PAGE->renderPage('loginform'); exit; } // -------- User wants to logout. if (isset($_SERVER['QUERY_STRING']) && startsWith($_SERVER['QUERY_STRING'], 'do=logout')) { invalidateCaches($GLOBALS['config']['PAGECACHE']); logout(); header('Location: ?'); exit; } // -------- Picture wall if ($targetPage == Router::$PAGE_PICWALL) { // Optionally filter the results: $links = $LINKSDB->filterSearch($_GET); $linksToDisplay = array(); // Get only links which have a thumbnail. foreach ($links as $link) { $permalink = '?' . escape(smallhash($link['linkdate'])); $thumb = lazyThumbnail($link['url'], $permalink); if ($thumb != '') { $link['thumbnail'] = $thumb; // Thumbnail HTML code. $linksToDisplay[] = $link; // Add to array. } } $data = array('linksToDisplay' => $linksToDisplay); $pluginManager->executeHooks('render_picwall', $data, array('loggedin' => isLoggedIn())); foreach ($data as $key => $value) { $PAGE->assign($key, $value); } $PAGE->renderPage('picwall'); exit; } // -------- Tag cloud if ($targetPage == Router::$PAGE_TAGCLOUD) { $tags = $LINKSDB->allTags(); // We sort tags alphabetically, then choose a font size according to count. // First, find max value. $maxcount = 0; foreach ($tags as $value) { $maxcount = max($maxcount, $value); } // Sort tags alphabetically: case insensitive, support locale if avalaible. uksort($tags, function ($a, $b) { // Collator is part of PHP intl. if (class_exists('Collator')) { $c = new Collator(setlocale(LC_COLLATE, 0)); if (!intl_is_failure(intl_get_error_code())) { return $c->compare($a, $b); } } return strcasecmp($a, $b); }); $tagList = array(); foreach ($tags as $key => $value) { // Tag font size scaling: // default 15 and 30 logarithm bases affect scaling, // 22 and 6 are arbitrary font sizes for max and min sizes. $size = log($value, 15) / log($maxcount, 30) * 2.2 + 0.8; $tagList[$key] = array('count' => $value, 'size' => number_format($size, 2, '.', '')); } $data = array('tags' => $tagList); $pluginManager->executeHooks('render_tagcloud', $data, array('loggedin' => isLoggedIn())); foreach ($data as $key => $value) { $PAGE->assign($key, $value); } $PAGE->renderPage('tagcloud'); exit; } // Daily page. if ($targetPage == Router::$PAGE_DAILY) { showDaily($PAGE, $LINKSDB); } // ATOM and RSS feed. if ($targetPage == Router::$PAGE_FEED_ATOM || $targetPage == Router::$PAGE_FEED_RSS) { $feedType = $targetPage == Router::$PAGE_FEED_RSS ? FeedBuilder::$FEED_RSS : FeedBuilder::$FEED_ATOM; header('Content-Type: application/' . $feedType . '+xml; charset=utf-8'); // Cache system $query = $_SERVER['QUERY_STRING']; $cache = new CachedPage($GLOBALS['config']['PAGECACHE'], page_url($_SERVER), startsWith($query, 'do=' . $targetPage) && !isLoggedIn()); $cached = $cache->cachedVersion(); if (!empty($cached)) { echo $cached; exit; } // Generate data. $feedGenerator = new FeedBuilder($LINKSDB, $feedType, $_SERVER, $_GET, isLoggedIn()); $feedGenerator->setLocale(strtolower(setlocale(LC_COLLATE, 0))); $feedGenerator->setHideDates($GLOBALS['config']['HIDE_TIMESTAMPS'] && !isLoggedIn()); $feedGenerator->setUsePermalinks(isset($_GET['permalinks']) || !$GLOBALS['config']['ENABLE_RSS_PERMALINKS']); if (!empty($GLOBALS['config']['PUBSUBHUB_URL'])) { $feedGenerator->setPubsubhubUrl($GLOBALS['config']['PUBSUBHUB_URL']); } $data = $feedGenerator->buildData(); // Process plugin hook. $pluginManager = PluginManager::getInstance(); $pluginManager->executeHooks('render_feed', $data, array('loggedin' => isLoggedIn(), 'target' => $targetPage)); // Render the template. $PAGE->assignAll($data); $PAGE->renderPage('feed.' . $feedType); $cache->cache(ob_get_contents()); ob_end_flush(); exit; } // Display openseach plugin (XML) if ($targetPage == Router::$PAGE_OPENSEARCH) { header('Content-Type: application/xml; charset=utf-8'); $PAGE->assign('serverurl', index_url($_SERVER)); $PAGE->renderPage('opensearch'); exit; } // -------- User clicks on a tag in a link: The tag is added to the list of searched tags (searchtags=...) if (isset($_GET['addtag'])) { // Get previous URL (http_referer) and add the tag to the searchtags parameters in query. if (empty($_SERVER['HTTP_REFERER'])) { header('Location: ?searchtags=' . urlencode($_GET['addtag'])); exit; } // In case browser does not send HTTP_REFERER parse_str(parse_url($_SERVER['HTTP_REFERER'], PHP_URL_QUERY), $params); // Prevent redirection loop if (isset($params['addtag'])) { unset($params['addtag']); } // Check if this tag is already in the search query and ignore it if it is. // Each tag is always separated by a space if (isset($params['searchtags'])) { $current_tags = explode(' ', $params['searchtags']); } else { $current_tags = array(); } $addtag = true; foreach ($current_tags as $value) { if ($value === $_GET['addtag']) { $addtag = false; break; } } // Append the tag if necessary if (empty($params['searchtags'])) { $params['searchtags'] = trim($_GET['addtag']); } else { if ($addtag) { $params['searchtags'] = trim($params['searchtags']) . ' ' . trim($_GET['addtag']); } } unset($params['page']); // We also remove page (keeping the same page has no sense, since the results are different) header('Location: ?' . http_build_query($params)); exit; } // -------- User clicks on a tag in result count: Remove the tag from the list of searched tags (searchtags=...) if (isset($_GET['removetag'])) { // Get previous URL (http_referer) and remove the tag from the searchtags parameters in query. if (empty($_SERVER['HTTP_REFERER'])) { header('Location: ?'); exit; } // In case browser does not send HTTP_REFERER parse_str(parse_url($_SERVER['HTTP_REFERER'], PHP_URL_QUERY), $params); // Prevent redirection loop if (isset($params['removetag'])) { unset($params['removetag']); } if (isset($params['searchtags'])) { $tags = explode(' ', $params['searchtags']); // Remove value from array $tags. $tags = array_diff($tags, array($_GET['removetag'])); $params['searchtags'] = implode(' ', $tags); if (empty($params['searchtags'])) { unset($params['searchtags']); } unset($params['page']); // We also remove page (keeping the same page has no sense, since the results are different) } header('Location: ?' . http_build_query($params)); exit; } // -------- User wants to change the number of links per page (linksperpage=...) if (isset($_GET['linksperpage'])) { if (is_numeric($_GET['linksperpage'])) { $_SESSION['LINKS_PER_PAGE'] = abs(intval($_GET['linksperpage'])); } header('Location: ' . generateLocation($_SERVER['HTTP_REFERER'], $_SERVER['HTTP_HOST'], array('linksperpage'))); exit; } // -------- User wants to see only private links (toggle) if (isset($_GET['privateonly'])) { if (empty($_SESSION['privateonly'])) { $_SESSION['privateonly'] = 1; // See only private links } else { unset($_SESSION['privateonly']); // See all links } header('Location: ' . generateLocation($_SERVER['HTTP_REFERER'], $_SERVER['HTTP_HOST'], array('privateonly'))); exit; } // -------- Handle other actions allowed for non-logged in users: if (!isLoggedIn()) { // User tries to post new link but is not logged in: // Show login screen, then redirect to ?post=... if (isset($_GET['post'])) { header('Location: ?do=login&post=' . urlencode($_GET['post']) . (!empty($_GET['title']) ? '&title=' . urlencode($_GET['title']) : '') . (!empty($_GET['description']) ? '&description=' . urlencode($_GET['description']) : '') . (!empty($_GET['source']) ? '&source=' . urlencode($_GET['source']) : '')); // Redirect to login page, then back to post link. exit; } showLinkList($PAGE, $LINKSDB); if (isset($_GET['edit_link'])) { header('Location: ?do=login&edit_link=' . escape($_GET['edit_link'])); exit; } exit; // Never remove this one! All operations below are reserved for logged in user. } // -------- All other functions are reserved for the registered user: // -------- Display the Tools menu if requested (import/export/bookmarklet...) if ($targetPage == Router::$PAGE_TOOLS) { $data = array('pageabsaddr' => index_url($_SERVER)); $pluginManager->executeHooks('render_tools', $data); foreach ($data as $key => $value) { $PAGE->assign($key, $value); } $PAGE->renderPage('tools'); exit; } // -------- User wants to change his/her password. if ($targetPage == Router::$PAGE_CHANGEPASSWORD) { if ($GLOBALS['config']['OPEN_SHAARLI']) { die('You are not supposed to change a password on an Open Shaarli.'); } if (!empty($_POST['setpassword']) && !empty($_POST['oldpassword'])) { if (!tokenOk($_POST['token'])) { die('Wrong token.'); } // Go away! // Make sure old password is correct. $oldhash = sha1($_POST['oldpassword'] . $GLOBALS['login'] . $GLOBALS['salt']); if ($oldhash != $GLOBALS['hash']) { echo '<script>alert("The old password is not correct.");document.location=\'?do=changepasswd\';</script>'; exit; } // Save new password $GLOBALS['salt'] = sha1(uniqid('', true) . '_' . mt_rand()); // Salt renders rainbow-tables attacks useless. $GLOBALS['hash'] = sha1($_POST['setpassword'] . $GLOBALS['login'] . $GLOBALS['salt']); try { writeConfig($GLOBALS, isLoggedIn()); } catch (Exception $e) { error_log('ERROR while writing config file after changing password.' . PHP_EOL . $e->getMessage()); // TODO: do not handle exceptions/errors in JS. echo '<script>alert("' . $e->getMessage() . '");document.location=\'?do=tools\';</script>'; exit; } echo '<script>alert("Your password has been changed.");document.location=\'?do=tools\';</script>'; exit; } else { $PAGE->assign('token', getToken()); $PAGE->renderPage('changepassword'); exit; } } // -------- User wants to change configuration if ($targetPage == Router::$PAGE_CONFIGURE) { if (!empty($_POST['title'])) { if (!tokenOk($_POST['token'])) { die('Wrong token.'); // Go away! } $tz = 'UTC'; if (!empty($_POST['continent']) && !empty($_POST['city']) && isTimeZoneValid($_POST['continent'], $_POST['city'])) { $tz = $_POST['continent'] . '/' . $_POST['city']; } $GLOBALS['timezone'] = $tz; $GLOBALS['title'] = $_POST['title']; $GLOBALS['titleLink'] = $_POST['titleLink']; $GLOBALS['redirector'] = $_POST['redirector']; $GLOBALS['disablesessionprotection'] = !empty($_POST['disablesessionprotection']); $GLOBALS['privateLinkByDefault'] = !empty($_POST['privateLinkByDefault']); $GLOBALS['config']['ENABLE_RSS_PERMALINKS'] = !empty($_POST['enableRssPermalinks']); $GLOBALS['config']['ENABLE_UPDATECHECK'] = !empty($_POST['updateCheck']); $GLOBALS['config']['HIDE_PUBLIC_LINKS'] = !empty($_POST['hidePublicLinks']); try { writeConfig($GLOBALS, isLoggedIn()); } catch (Exception $e) { error_log('ERROR while writing config file after configuration update.' . PHP_EOL . $e->getMessage()); // TODO: do not handle exceptions/errors in JS. echo '<script>alert("' . $e->getMessage() . '");document.location=\'?do=tools\';</script>'; exit; } echo '<script>alert("Configuration was saved.");document.location=\'?do=tools\';</script>'; exit; } else { $PAGE->assign('token', getToken()); $PAGE->assign('title', empty($GLOBALS['title']) ? '' : $GLOBALS['title']); $PAGE->assign('redirector', empty($GLOBALS['redirector']) ? '' : $GLOBALS['redirector']); list($timezone_form, $timezone_js) = generateTimeZoneForm($GLOBALS['timezone']); $PAGE->assign('timezone_form', $timezone_form); $PAGE->assign('timezone_js', $timezone_js); $PAGE->renderPage('configure'); exit; } } // -------- User wants to rename a tag or delete it if ($targetPage == Router::$PAGE_CHANGETAG) { if (empty($_POST['fromtag']) || empty($_POST['totag']) && isset($_POST['renametag'])) { $PAGE->assign('token', getToken()); $PAGE->assign('tags', $LINKSDB->allTags()); $PAGE->renderPage('changetag'); exit; } if (!tokenOk($_POST['token'])) { die('Wrong token.'); } // Delete a tag: if (isset($_POST['deletetag']) && !empty($_POST['fromtag'])) { $needle = trim($_POST['fromtag']); // True for case-sensitive tag search. $linksToAlter = $LINKSDB->filterSearch(array('searchtags' => $needle), true); foreach ($linksToAlter as $key => $value) { $tags = explode(' ', trim($value['tags'])); unset($tags[array_search($needle, $tags)]); // Remove tag. $value['tags'] = trim(implode(' ', $tags)); $LINKSDB[$key] = $value; } $LINKSDB->savedb($GLOBALS['config']['PAGECACHE']); echo '<script>alert("Tag was removed from ' . count($linksToAlter) . ' links.");document.location=\'?\';</script>'; exit; } // Rename a tag: if (isset($_POST['renametag']) && !empty($_POST['fromtag']) && !empty($_POST['totag'])) { $needle = trim($_POST['fromtag']); // True for case-sensitive tag search. $linksToAlter = $LINKSDB->filterSearch(array('searchtags' => $needle), true); foreach ($linksToAlter as $key => $value) { $tags = explode(' ', trim($value['tags'])); $tags[array_search($needle, $tags)] = trim($_POST['totag']); // Replace tags value. $value['tags'] = trim(implode(' ', $tags)); $LINKSDB[$key] = $value; } $LINKSDB->savedb($GLOBALS['config']['PAGECACHE']); // Save to disk. echo '<script>alert("Tag was renamed in ' . count($linksToAlter) . ' links.");document.location=\'?searchtags=' . urlencode($_POST['totag']) . '\';</script>'; exit; } } // -------- User wants to add a link without using the bookmarklet: Show form. if ($targetPage == Router::$PAGE_ADDLINK) { $PAGE->renderPage('addlink'); exit; } // -------- User clicked the "Save" button when editing a link: Save link to database. if (isset($_POST['save_edit'])) { // Go away! if (!tokenOk($_POST['token'])) { die('Wrong token.'); } // Remove multiple spaces. $tags = trim(preg_replace('/\\s\\s+/', ' ', $_POST['lf_tags'])); // Remove first '-' char in tags. $tags = preg_replace('/(^| )\\-/', '$1', $tags); // Remove duplicates. $tags = implode(' ', array_unique(explode(' ', $tags))); $linkdate = $_POST['lf_linkdate']; $url = trim($_POST['lf_url']); if (!startsWith($url, 'http:') && !startsWith($url, 'https:') && !startsWith($url, 'ftp:') && !startsWith($url, 'magnet:') && !startsWith($url, '?') && !startsWith($url, 'javascript:')) { $url = 'http://' . $url; } $link = array('title' => trim($_POST['lf_title']), 'url' => $url, 'description' => $_POST['lf_description'], 'private' => isset($_POST['lf_private']) ? 1 : 0, 'linkdate' => $linkdate, 'tags' => str_replace(',', ' ', $tags)); // If title is empty, use the URL as title. if ($link['title'] == '') { $link['title'] = $link['url']; } $pluginManager->executeHooks('save_link', $link); $LINKSDB[$linkdate] = $link; $LINKSDB->savedb($GLOBALS['config']['PAGECACHE']); pubsubhub(); // If we are called from the bookmarklet, we must close the popup: if (isset($_GET['source']) && ($_GET['source'] == 'bookmarklet' || $_GET['source'] == 'firefoxsocialapi')) { echo '<script>self.close();</script>'; exit; } $returnurl = !empty($_POST['returnurl']) ? $_POST['returnurl'] : '?'; $location = generateLocation($returnurl, $_SERVER['HTTP_HOST'], array('addlink', 'post', 'edit_link')); // Scroll to the link which has been edited. $location .= '#' . smallHash($_POST['lf_linkdate']); // After saving the link, redirect to the page the user was on. header('Location: ' . $location); exit; } // -------- User clicked the "Cancel" button when editing a link. if (isset($_POST['cancel_edit'])) { // If we are called from the bookmarklet, we must close the popup: if (isset($_GET['source']) && ($_GET['source'] == 'bookmarklet' || $_GET['source'] == 'firefoxsocialapi')) { echo '<script>self.close();</script>'; exit; } $returnurl = isset($_POST['returnurl']) ? $_POST['returnurl'] : '?'; $returnurl .= '#' . smallHash($_POST['lf_linkdate']); // Scroll to the link which has been edited. $returnurl = generateLocation($returnurl, $_SERVER['HTTP_HOST'], array('addlink', 'post', 'edit_link')); header('Location: ' . $returnurl); // After canceling, redirect to the page the user was on. exit; } // -------- User clicked the "Delete" button when editing a link: Delete link from database. if (isset($_POST['delete_link'])) { if (!tokenOk($_POST['token'])) { die('Wrong token.'); } // We do not need to ask for confirmation: // - confirmation is handled by JavaScript // - we are protected from XSRF by the token. $linkdate = $_POST['lf_linkdate']; $pluginManager->executeHooks('delete_link', $LINKSDB[$linkdate]); unset($LINKSDB[$linkdate]); $LINKSDB->savedb($GLOBALS['config']['PAGECACHE']); // save to disk // If we are called from the bookmarklet, we must close the popup: if (isset($_GET['source']) && ($_GET['source'] == 'bookmarklet' || $_GET['source'] == 'firefoxsocialapi')) { echo '<script>self.close();</script>'; exit; } // Pick where we're going to redirect // ============================================================= // Basically, we can't redirect to where we were previously if it was a permalink // or an edit_link, because it would 404. // Cases: // - / : nothing in $_GET, redirect to self // - /?page : redirect to self // - /?searchterm : redirect to self (there might be other links) // - /?searchtags : redirect to self // - /permalink : redirect to / (the link does not exist anymore) // - /?edit_link : redirect to / (the link does not exist anymore) // PHP treats the permalink as a $_GET variable, so we need to check if every condition for self // redirect is not satisfied, and only then redirect to / $location = "?"; // Self redirection if (count($_GET) == 0 || isset($_GET['page']) || isset($_GET['searchterm']) || isset($_GET['searchtags'])) { if (isset($_POST['returnurl'])) { $location = $_POST['returnurl']; // Handle redirects given by the form } else { $location = generateLocation($_SERVER['HTTP_REFERER'], $_SERVER['HTTP_HOST'], array('delete_link')); } } header('Location: ' . $location); // After deleting the link, redirect to appropriate location exit; } // -------- User clicked the "EDIT" button on a link: Display link edit form. if (isset($_GET['edit_link'])) { $link = $LINKSDB[$_GET['edit_link']]; // Read database if (!$link) { header('Location: ?'); exit; } // Link not found in database. $data = array('link' => $link, 'link_is_new' => false, 'token' => getToken(), 'http_referer' => isset($_SERVER['HTTP_REFERER']) ? escape($_SERVER['HTTP_REFERER']) : '', 'tags' => $LINKSDB->allTags()); $pluginManager->executeHooks('render_editlink', $data); foreach ($data as $key => $value) { $PAGE->assign($key, $value); } $PAGE->renderPage('editlink'); exit; } // -------- User want to post a new link: Display link edit form. if (isset($_GET['post'])) { $url = cleanup_url($_GET['post']); $link_is_new = false; // Check if URL is not already in database (in this case, we will edit the existing link) $link = $LINKSDB->getLinkFromUrl($url); if (!$link) { $link_is_new = true; $linkdate = strval(date('Ymd_His')); // Get title if it was provided in URL (by the bookmarklet). $title = empty($_GET['title']) ? '' : escape($_GET['title']); // Get description if it was provided in URL (by the bookmarklet). [Bronco added that] $description = empty($_GET['description']) ? '' : escape($_GET['description']); $tags = empty($_GET['tags']) ? '' : escape($_GET['tags']); $private = !empty($_GET['private']) && $_GET['private'] === "1" ? 1 : 0; // If this is an HTTP(S) link, we try go get the page to extract the title (otherwise we will to straight to the edit form.) if (empty($title) && strpos(get_url_scheme($url), 'http') !== false) { // Short timeout to keep the application responsive list($headers, $content) = get_http_response($url, 4); if (strpos($headers[0], '200 OK') !== false) { // Retrieve charset. $charset = get_charset($headers, $content); // Extract title. $title = html_extract_title($content); // Re-encode title in utf-8 if necessary. if (!empty($title) && strtolower($charset) != 'utf-8') { $title = mb_convert_encoding($title, 'utf-8', $charset); } } } if ($url == '') { $url = '?' . smallHash($linkdate); $title = 'Note: '; } $url = escape($url); $title = escape($title); $link = array('linkdate' => $linkdate, 'title' => $title, 'url' => $url, 'description' => $description, 'tags' => $tags, 'private' => $private); } $data = array('link' => $link, 'link_is_new' => $link_is_new, 'token' => getToken(), 'http_referer' => isset($_SERVER['HTTP_REFERER']) ? escape($_SERVER['HTTP_REFERER']) : '', 'source' => isset($_GET['source']) ? $_GET['source'] : '', 'tags' => $LINKSDB->allTags()); $pluginManager->executeHooks('render_editlink', $data); foreach ($data as $key => $value) { $PAGE->assign($key, $value); } $PAGE->renderPage('editlink'); exit; } if ($targetPage == Router::$PAGE_EXPORT) { // Export links as a Netscape Bookmarks file if (empty($_GET['selection'])) { $PAGE->renderPage('export'); exit; } // export as bookmarks_(all|private|public)_YYYYmmdd_HHMMSS.html $selection = $_GET['selection']; if (isset($_GET['prepend_note_url'])) { $prependNoteUrl = $_GET['prepend_note_url']; } else { $prependNoteUrl = false; } try { $PAGE->assign('links', NetscapeBookmarkUtils::filterAndFormat($LINKSDB, $selection, $prependNoteUrl, index_url($_SERVER))); } catch (Exception $exc) { header('Content-Type: text/plain; charset=utf-8'); echo $exc->getMessage(); exit; } $now = new DateTime(); header('Content-Type: text/html; charset=utf-8'); header('Content-disposition: attachment; filename=bookmarks_' . $selection . '_' . $now->format(LinkDB::LINK_DATE_FORMAT) . '.html'); $PAGE->assign('date', $now->format(DateTime::RFC822)); $PAGE->assign('eol', PHP_EOL); $PAGE->assign('selection', $selection); $PAGE->renderPage('export.bookmarks'); exit; } // -------- User is uploading a file for import if (isset($_SERVER['QUERY_STRING']) && startsWith($_SERVER['QUERY_STRING'], 'do=upload')) { // If file is too big, some form field may be missing. if (!isset($_POST['token']) || !isset($_FILES) || isset($_FILES['filetoupload']['size']) && $_FILES['filetoupload']['size'] == 0) { $returnurl = empty($_SERVER['HTTP_REFERER']) ? '?' : $_SERVER['HTTP_REFERER']; echo '<script>alert("The file you are trying to upload is probably bigger than what this webserver can accept (' . getMaxFileSize() . ' bytes). Please upload in smaller chunks.");document.location=\'' . escape($returnurl) . '\';</script>'; exit; } if (!tokenOk($_POST['token'])) { die('Wrong token.'); } importFile($LINKSDB); exit; } // -------- Show upload/import dialog: if ($targetPage == Router::$PAGE_IMPORT) { $PAGE->assign('token', getToken()); $PAGE->assign('maxfilesize', getMaxFileSize()); $PAGE->renderPage('import'); exit; } // Plugin administration page if ($targetPage == Router::$PAGE_PLUGINSADMIN) { $pluginMeta = $pluginManager->getPluginsMeta(); // Split plugins into 2 arrays: ordered enabled plugins and disabled. $enabledPlugins = array_filter($pluginMeta, function ($v) { return $v['order'] !== false; }); // Load parameters. $enabledPlugins = load_plugin_parameter_values($enabledPlugins, $GLOBALS['plugins']); uasort($enabledPlugins, function ($a, $b) { return $a['order'] - $b['order']; }); $disabledPlugins = array_filter($pluginMeta, function ($v) { return $v['order'] === false; }); $PAGE->assign('enabledPlugins', $enabledPlugins); $PAGE->assign('disabledPlugins', $disabledPlugins); $PAGE->renderPage('pluginsadmin'); exit; } // Plugin administration form action if ($targetPage == Router::$PAGE_SAVE_PLUGINSADMIN) { try { if (isset($_POST['parameters_form'])) { unset($_POST['parameters_form']); foreach ($_POST as $param => $value) { $GLOBALS['plugins'][$param] = escape($value); } } else { $GLOBALS['config']['ENABLED_PLUGINS'] = save_plugin_config($_POST); } writeConfig($GLOBALS, isLoggedIn()); } catch (Exception $e) { error_log('ERROR while saving plugin configuration:.' . PHP_EOL . $e->getMessage()); // TODO: do not handle exceptions/errors in JS. echo '<script>alert("' . $e->getMessage() . '");document.location=\'?do=' . Router::$PAGE_PLUGINSADMIN . '\';</script>'; exit; } header('Location: ?do=' . Router::$PAGE_PLUGINSADMIN); exit; } // -------- Otherwise, simply display search form and links: showLinkList($PAGE, $LINKSDB); exit; }
/** * Transforms a localized number into an integer or float * * @param string $value The localized value * * @return integer|float The numeric value * * @throws TransformationFailedException If the given value is not a string * or if the value can not be transformed. */ public function reverseTransform($value) { if (!is_string($value)) { throw new TransformationFailedException('Expected a string.'); } if ('' === $value) { return null; } if ('NaN' === $value) { throw new TransformationFailedException('"NaN" is not a valid number'); } $formatter = $this->getNumberFormatter(); $groupSep = $formatter->getSymbol(\NumberFormatter::GROUPING_SEPARATOR_SYMBOL); $decSep = $formatter->getSymbol(\NumberFormatter::DECIMAL_SEPARATOR_SYMBOL); if ('.' !== $decSep && (!$this->grouping || '.' !== $groupSep)) { $value = str_replace('.', $decSep, $value); } if (',' !== $decSep && (!$this->grouping || ',' !== $groupSep)) { $value = str_replace(',', $decSep, $value); } $value = $formatter->parse($value); if (intl_is_failure($formatter->getErrorCode())) { throw new TransformationFailedException($formatter->getErrorMessage()); } if ($value >= INF || $value <= -INF) { throw new TransformationFailedException('I don\'t have a clear idea what infinity looks like'); } return $value; }
/** * Returns true if and only if $value is a floating-point value * * @param string $value * @return bool * @throws ValidatorException\InvalidArgumentException */ public function isValid($value) { if (!is_string($value)) { $this->error(self::INVALID); return false; } $this->setValue($value); try { $formatter = $this->getIntlDateFormatter(); if (intl_is_failure($formatter->getErrorCode())) { throw new ValidatorException\InvalidArgumentException($formatter->getErrorMessage()); } } catch (IntlException $intlException) { throw new ValidatorException\InvalidArgumentException($intlException->getMessage(), 0, $intlException); } try { $timestamp = $formatter->parse($value); if (intl_is_failure($formatter->getErrorCode()) || $timestamp === false) { $this->error(self::INVALID_DATETIME); $this->invalidateFormatter = true; return false; } } catch (IntlException $intlException) { $this->error(self::INVALID_DATETIME); $this->invalidateFormatter = true; return false; } return true; }
/** * Returns a non lenient configured IntlDateFormatter * * @return IntlDateFormatter */ public function filter($value) { if (!is_string($value)) { return $value; } $dateType = $this->getDateType(); $timeType = $this->getTimeType(); $locale = $this->getLocale(); $timezone = $this->getTimezone(); $calendar = $this->getCalendar(); $pattern = $this->getPattern(); $formatterId = md5($dateType . "" . $timeType . "" . $locale . "" . $timezone . "" . $calendar . "" . $pattern); if (!isset($this->formatters[$formatterId])) { try { $this->formatters[$formatterId] = new IntlDateFormatter($locale, $dateType, $timeType, $timezone, IntlDateFormatter::GREGORIAN); $this->formatters[$formatterId]->setLenient(false); } catch (IntlException $intlException) { // throw new FilterException\InvalidArgumentException($intlException->getMessage(), 0, $intlException); return $value; } } $this->setTimezone($this->formatters[$formatterId]->getTimezone()->getID()); $this->setCalendar($this->formatters[$formatterId]->getCalendar()); $timestamp = $this->formatters[$formatterId]->parse($value); $this->formatters[$formatterId]->setPattern($this->getPattern()); $formatted = $this->formatters[$formatterId]->format($timestamp); if (intl_is_failure($this->formatters[$formatterId]->getErrorCode())) { // throw new FilterException\InvalidArgumentException($this->formatters[$formatterId]->getErrorMessage()); return $value; } return $formatted; }
function check($err_code) { var_export(intl_is_failure($err_code)); echo "\n"; }
/** * @dataProvider parseProvider */ public function testParseIntl($value, $expected, $message = '') { $this->skipIfIntlExtensionIsNotLoaded(); $this->skipIfICUVersionIsTooOld(); $formatter = $this->getIntlFormatterWithDecimalStyle(); $parsedValue = $formatter->parse($value, \NumberFormatter::TYPE_DOUBLE); $this->assertSame($expected, $parsedValue, $message); if ($expected === false) { $errorCode = StubIntl::U_PARSE_ERROR; $errorMessage = 'Number parsing failed: U_PARSE_ERROR'; } else { $errorCode = StubIntl::U_ZERO_ERROR; $errorMessage = 'U_ZERO_ERROR'; } $this->assertSame($errorMessage, intl_get_error_message()); $this->assertSame($errorCode, intl_get_error_code()); $this->assertSame($errorCode > 0, intl_is_failure(intl_get_error_code())); $this->assertSame($errorMessage, $formatter->getErrorMessage()); $this->assertSame($errorCode, $formatter->getErrorCode()); $this->assertSame($errorCode > 0, intl_is_failure($formatter->getErrorCode())); }
/** * Returns true if and only if $value is a floating-point value * * @param string $value * @return bool * @throws ValidatorException\InvalidArgumentException */ public function isValid($value) { if (!is_string($value)) { $this->error(self::INVALID); return false; } $this->setValue($value); $formatter = $this->getIntlDateFormatter(); if (intl_is_failure($formatter->getErrorCode())) { throw new ValidatorException\InvalidArgumentException("Invalid locale string given"); } $position = 0; $parsedDate = $formatter->parse($value, $position); if (intl_is_failure($formatter->getErrorCode())) { $this->error(self::INVALID_DATETIME); return false; } if ($position != strlen($value)) { $this->error(self::INVALID_DATETIME); return false; } return true; }
/** * Returns true if and only if $value is a floating-point value. Uses the formal definition of a float as described * in the PHP manual: {@link http://www.php.net/float} * * @param string $value * @return bool * @throws Exception\InvalidArgumentException */ public function isValid($value) { if (!is_scalar($value) || is_bool($value)) { $this->error(self::INVALID); return false; } $this->setValue($value); if (is_float($value) || is_int($value)) { return true; } // Need to check if this is scientific formatted string. If not, switch to decimal. $formatter = new NumberFormatter($this->getLocale(), NumberFormatter::SCIENTIFIC); if (intl_is_failure($formatter->getErrorCode())) { throw new Exception\InvalidArgumentException($formatter->getErrorMessage()); } if (StringUtils::hasPcreUnicodeSupport()) { $exponentialSymbols = '[Ee' . $formatter->getSymbol(NumberFormatter::EXPONENTIAL_SYMBOL) . ']+'; $search = '/' . $exponentialSymbols . '/u'; } else { $exponentialSymbols = '[Ee]'; $search = '/' . $exponentialSymbols . '/'; } if (!preg_match($search, $value)) { $formatter = new NumberFormatter($this->getLocale(), NumberFormatter::DECIMAL); } /** * @desc There are seperator "look-alikes" for decimal and group seperators that are more commonly used than the * official unicode chracter. We need to replace those with the real thing - or remove it. */ $groupSeparator = $formatter->getSymbol(NumberFormatter::GROUPING_SEPARATOR_SYMBOL); $decSeparator = $formatter->getSymbol(NumberFormatter::DECIMAL_SEPARATOR_SYMBOL); //NO-BREAK SPACE and ARABIC THOUSANDS SEPARATOR if ($groupSeparator == " ") { $value = str_replace(' ', $groupSeparator, $value); } elseif ($groupSeparator == "٬") { //NumberFormatter doesn't have grouping at all for Arabic-Indic $value = str_replace(array('\'', $groupSeparator), '', $value); } //ARABIC DECIMAL SEPARATOR if ($decSeparator == "٫") { $value = str_replace(',', $decSeparator, $value); } $groupSeparatorPosition = $this->wrapper->strpos($value, $groupSeparator); $decSeparatorPosition = $this->wrapper->strpos($value, $decSeparator); //We have seperators, and they are flipped. i.e. 2.000,000 for en-US if ($groupSeparatorPosition && $decSeparatorPosition && $groupSeparatorPosition > $decSeparatorPosition) { return false; } //If we have Unicode support, we can use the real graphemes, otherwise, just the ASCII characters $decimal = '[' . preg_quote($decSeparator, '/') . ']'; $prefix = '[+-]'; $exp = $exponentialSymbols; $numberRange = '0-9'; $useUnicode = ''; $suffix = ''; if (StringUtils::hasPcreUnicodeSupport()) { $prefix = '[' . preg_quote($formatter->getTextAttribute(NumberFormatter::POSITIVE_PREFIX) . $formatter->getTextAttribute(NumberFormatter::NEGATIVE_PREFIX) . $formatter->getSymbol(NumberFormatter::PLUS_SIGN_SYMBOL) . $formatter->getSymbol(NumberFormatter::MINUS_SIGN_SYMBOL), '/') . ']{0,3}'; $suffix = $formatter->getTextAttribute(NumberFormatter::NEGATIVE_SUFFIX) ? '[' . preg_quote($formatter->getTextAttribute(NumberFormatter::POSITIVE_SUFFIX) . $formatter->getTextAttribute(NumberFormatter::NEGATIVE_SUFFIX) . $formatter->getSymbol(NumberFormatter::PLUS_SIGN_SYMBOL) . $formatter->getSymbol(NumberFormatter::MINUS_SIGN_SYMBOL), '/') . ']{0,3}' : ''; $numberRange = '\\p{N}'; $useUnicode = 'u'; } /** * @desc Match against the formal definition of a float. The * exponential number check is modified for RTL non-Latin number * systems (Arabic-Indic numbering). I'm also switching out the period * for the decimal separator. The formal definition leaves out +- from * the integer and decimal notations so add that. This also checks * that a grouping sperator is not in the last GROUPING_SIZE graphemes * of the string - i.e. 10,6 is not valid for en-US. * @see http://www.php.net/float */ $lnum = '[' . $numberRange . ']+'; $dnum = '(([' . $numberRange . ']*' . $decimal . $lnum . ')|(' . $lnum . $decimal . '[' . $numberRange . ']*))'; $expDnum = '((' . $prefix . '((' . $lnum . '|' . $dnum . ')' . $exp . $prefix . $lnum . ')' . $suffix . ')|' . '(' . $suffix . '(' . $lnum . $prefix . $exp . '(' . $dnum . '|' . $lnum . '))' . $prefix . '))'; // LEFT-TO-RIGHT MARK (U+200E) is messing up everything for the handful // of locales that have it $lnumSearch = str_replace("", '', '/^' . $prefix . $lnum . $suffix . '$/' . $useUnicode); $dnumSearch = str_replace("", '', '/^' . $prefix . $dnum . $suffix . '$/' . $useUnicode); $expDnumSearch = str_replace("", '', '/^' . $expDnum . '$/' . $useUnicode); $value = str_replace("", '', $value); $unGroupedValue = str_replace($groupSeparator, '', $value); // No strrpos() in wrappers yet. ICU 4.x doesn't have grouping size for // everything. ICU 52 has 3 for ALL locales. $groupSize = $formatter->getAttribute(NumberFormatter::GROUPING_SIZE) ? $formatter->getAttribute(NumberFormatter::GROUPING_SIZE) : 3; $lastStringGroup = $this->wrapper->substr($value, -$groupSize); if ((preg_match($lnumSearch, $unGroupedValue) || preg_match($dnumSearch, $unGroupedValue) || preg_match($expDnumSearch, $unGroupedValue)) && false === $this->wrapper->strpos($lastStringGroup, $groupSeparator)) { return true; } return false; }