/**
  * Returns best-matching Locale object based on the Accept-Language header
  * provided as parameter. System default locale will be returned if no
  * successful matches were done.
  *
  * @param string $acceptLanguageHeader The Accept-Language HTTP header
  * @return \TYPO3\Flow\I18n\Locale Best-matching existing Locale instance
  * @api
  */
 public function detectLocaleFromHttpHeader($acceptLanguageHeader)
 {
     $acceptableLanguages = \TYPO3\Flow\I18n\Utility::parseAcceptLanguageHeader($acceptLanguageHeader);
     if ($acceptableLanguages === false) {
         return $this->localizationService->getConfiguration()->getDefaultLocale();
     }
     foreach ($acceptableLanguages as $languageIdentifier) {
         if ($languageIdentifier === '*') {
             return $this->localizationService->getConfiguration()->getDefaultLocale();
         }
         try {
             $locale = new \TYPO3\Flow\I18n\Locale($languageIdentifier);
         } catch (\TYPO3\Flow\I18n\Exception\InvalidLocaleIdentifierException $exception) {
             continue;
         }
         $bestMatchingLocale = $this->localeCollection->findBestMatchingLocale($locale);
         if ($bestMatchingLocale !== null) {
             return $bestMatchingLocale;
         }
     }
     return $this->localizationService->getConfiguration()->getDefaultLocale();
 }
 /**
  * Parses date and / or time in strict mode.
  *
  * @param string $datetimeToParse Date/time to be parsed
  * @param array $parsedFormat Format parsed by DatesReader
  * @param array $localizedLiterals Array of date / time literals from CLDR
  * @return array Array of parsed date and / or time elements, FALSE on failure
  * @throws \TYPO3\Flow\I18n\Exception\InvalidArgumentException When unexpected symbol found in format
  * @see \TYPO3\Flow\I18n\Cldr\Reader\DatesReader
  */
 protected function doParsingInStrictMode($datetimeToParse, array $parsedFormat, array $localizedLiterals)
 {
     $datetimeElements = array('year' => null, 'month' => null, 'day' => null, 'hour' => null, 'minute' => null, 'second' => null, 'timezone' => null);
     $using12HourClock = false;
     $timeIsPm = false;
     try {
         foreach ($parsedFormat as $subformat) {
             if (is_array($subformat)) {
                 // This is literal string, should match exactly
                 if (\TYPO3\Flow\I18n\Utility::stringBeginsWith($datetimeToParse, $subformat[0])) {
                     $datetimeToParse = substr_replace($datetimeToParse, '', 0, strlen($subformat[0]));
                     continue;
                 } else {
                     return false;
                 }
             }
             $lengthOfSubformat = strlen($subformat);
             $numberOfCharactersToRemove = 0;
             switch ($subformat[0]) {
                 case 'h':
                 case 'K':
                     $hour = $this->extractAndCheckNumber($datetimeToParse, $lengthOfSubformat === 2, 1, 12);
                     $numberOfCharactersToRemove = $lengthOfSubformat === 1 && $hour < 10 ? 1 : 2;
                     if ($subformat[0] === 'h' && $hour === 12) {
                         $hour = 0;
                     }
                     $datetimeElements['hour'] = $hour;
                     $using12HourClock = true;
                     break;
                 case 'k':
                 case 'H':
                     $hour = $this->extractAndCheckNumber($datetimeToParse, $lengthOfSubformat === 2, 1, 24);
                     $numberOfCharactersToRemove = $lengthOfSubformat === 1 && $hour < 10 ? 1 : 2;
                     if ($subformat[0] === 'k' && $hour === 24) {
                         $hour = 0;
                     }
                     $datetimeElements['hour'] = $hour;
                     break;
                 case 'a':
                     $dayPeriods = $localizedLiterals['dayPeriods']['format']['wide'];
                     if (\TYPO3\Flow\I18n\Utility::stringBeginsWith($datetimeToParse, $dayPeriods['am'])) {
                         $numberOfCharactersToRemove = strlen($dayPeriods['am']);
                     } elseif (\TYPO3\Flow\I18n\Utility::stringBeginsWith($datetimeToParse, $dayPeriods['pm'])) {
                         $timeIsPm = true;
                         $numberOfCharactersToRemove = strlen($dayPeriods['pm']);
                     } else {
                         return false;
                     }
                     break;
                 case 'm':
                     $minute = $this->extractAndCheckNumber($datetimeToParse, $lengthOfSubformat === 2, 0, 59);
                     $numberOfCharactersToRemove = $lengthOfSubformat === 1 && $minute < 10 ? 1 : 2;
                     $datetimeElements['minute'] = $minute;
                     break;
                 case 's':
                     $second = $this->extractAndCheckNumber($datetimeToParse, $lengthOfSubformat === 2, 0, 59);
                     $numberOfCharactersToRemove = $lengthOfSubformat === 1 && $second < 10 ? 1 : 2;
                     $datetimeElements['second'] = $second;
                     break;
                 case 'd':
                     $dayOfTheMonth = $this->extractAndCheckNumber($datetimeToParse, $lengthOfSubformat === 2, 1, 31);
                     $numberOfCharactersToRemove = $lengthOfSubformat === 1 && $dayOfTheMonth < 10 ? 1 : 2;
                     $datetimeElements['day'] = $dayOfTheMonth;
                     break;
                 case 'M':
                 case 'L':
                     $typeOfLiteral = $subformat[0] === 'L' ? 'stand-alone' : 'format';
                     if ($lengthOfSubformat <= 2) {
                         $month = $this->extractAndCheckNumber($datetimeToParse, $lengthOfSubformat === 2, 1, 12);
                         $numberOfCharactersToRemove = $lengthOfSubformat === 1 && $month < 10 ? 1 : 2;
                     } elseif ($lengthOfSubformat <= 4) {
                         $lengthOfLiteral = $lengthOfSubformat === 3 ? 'abbreviated' : 'wide';
                         $month = 0;
                         foreach ($localizedLiterals['months'][$typeOfLiteral][$lengthOfLiteral] as $monthId => $monthName) {
                             if (\TYPO3\Flow\I18n\Utility::stringBeginsWith($datetimeToParse, $monthName)) {
                                 $month = $monthId;
                                 break;
                             }
                         }
                     } else {
                         throw new \TYPO3\Flow\I18n\Exception\InvalidArgumentException('Cannot parse formats with narrow month pattern as it is not unique.', 1279965245);
                     }
                     if ($month === 0) {
                         return false;
                     }
                     $datetimeElements['month'] = $month;
                     break;
                 case 'y':
                     if ($lengthOfSubformat === 2) {
                         /** @todo How should the XX date be returned? Like 19XX? **/
                         $year = substr($datetimeToParse, 0, 2);
                         $numberOfCharactersToRemove = 2;
                     } else {
                         $year = substr($datetimeToParse, 0, $lengthOfSubformat);
                         $datetimeToParseLength = strlen($datetimeToParse);
                         for ($i = $lengthOfSubformat; $i < $datetimeToParseLength; ++$i) {
                             if (is_numeric($datetimeToParse[$i])) {
                                 $year .= $datetimeToParse[$i];
                             } else {
                                 break;
                             }
                         }
                         $numberOfCharactersToRemove = $i;
                     }
                     if (!is_numeric($year)) {
                         return false;
                     }
                     $year = (int) $year;
                     $datetimeElements['year'] = $year;
                     break;
                 case 'v':
                 case 'z':
                     if ($lengthOfSubformat <= 3) {
                         $pattern = self::PATTERN_MATCH_STRICT_TIMEZONE_ABBREVIATION;
                     } else {
                         $pattern = self::PATTERN_MATCH_STRICT_TIMEZONE_TZ;
                     }
                     if (preg_match($pattern, $datetimeToParse, $matches) !== 1) {
                         return false;
                     }
                     $datetimeElements['timezone'] = $matches[0];
                     break;
                 case 'D':
                 case 'F':
                 case 'w':
                 case 'W':
                 case 'Q':
                 case 'q':
                 case 'G':
                 case 'S':
                 case 'E':
                 case 'Y':
                 case 'u':
                 case 'l':
                 case 'g':
                 case 'e':
                 case 'c':
                 case 'A':
                 case 'Z':
                 case 'V':
                     // Silently ignore unsupported formats or formats that there is no need to parse
                     break;
                 default:
                     throw new \TYPO3\Flow\I18n\Exception\InvalidArgumentException('Unexpected format symbol, "' . $subformat[0] . '" detected for date / time parsing.', 1279965528);
             }
             if ($using12HourClock && $timeIsPm) {
                 $datetimeElements['hour'] += 12;
                 $timeIsPm = false;
             }
             if ($numberOfCharactersToRemove > 0) {
                 $datetimeToParse = substr_replace($datetimeToParse, '', 0, $numberOfCharactersToRemove);
             }
         }
     } catch (\TYPO3\Flow\I18n\Parser\Exception\InvalidParseStringException $exception) {
         // Method extractAndCheckNumber() throws exception when constraints in $datetimeToParse are not fulfilled
         return false;
     }
     return $datetimeElements;
 }
 /**
  * @test
  * @dataProvider sampleHaystackStringsAndNeedleStrings
  */
 public function stringIsFoundAtEndingOfAnotherString($haystack, $needle, $comparison)
 {
     $expectedResult = $comparison === 'ending' || $comparison === 'both' ? TRUE : FALSE;
     $result = \TYPO3\Flow\I18n\Utility::stringEndsWith($haystack, $needle);
     $this->assertEquals($expectedResult, $result);
 }
 /**
  * Parses number in lenient mode.
  *
  * Lenient parsing ignores everything that can be ignored, and tries to
  * extract number from the string, even if it's not well formed.
  *
  * Implementation is simple but should work more often than strict parsing.
  *
  * Algorithm:
  * 1. Find first digit
  * 2. Find last digit
  * 3. Find decimal separator between first and last digit (if any)
  * 4. Remove non-digits from integer part
  * 5. Remove non-digits from decimal part (optional)
  * 6. Try to match negative prefix before first digit
  * 7. Try to match negative suffix after last digit
  *
  * @param string $numberToParse Number to be parsed
  * @param array $parsedFormat Parsed format (from NumbersReader)
  * @param array $localizedSymbols An array with symbols to use
  * @return mixed Parsed float number or FALSE on failure
  */
 protected function doParsingInLenientMode($numberToParse, array $parsedFormat, array $localizedSymbols)
 {
     $numberIsNegative = false;
     $positionOfFirstDigit = null;
     $positionOfLastDigit = null;
     $charactersOfNumberString = str_split($numberToParse);
     foreach ($charactersOfNumberString as $position => $character) {
         if (ord($character) >= 48 && ord($character) <= 57) {
             $positionOfFirstDigit = $position;
             break;
         }
     }
     if ($positionOfFirstDigit === null) {
         return false;
     }
     krsort($charactersOfNumberString);
     foreach ($charactersOfNumberString as $position => $character) {
         if (ord($character) >= 48 && ord($character) <= 57) {
             $positionOfLastDigit = $position;
             break;
         }
     }
     $positionOfDecimalSeparator = strrpos($numberToParse, $localizedSymbols['decimal'], $positionOfFirstDigit);
     if ($positionOfDecimalSeparator === false) {
         $integerPart = substr($numberToParse, $positionOfFirstDigit, $positionOfLastDigit - $positionOfFirstDigit + 1);
         $decimalPart = false;
     } else {
         $integerPart = substr($numberToParse, $positionOfFirstDigit, $positionOfDecimalSeparator - $positionOfFirstDigit);
         $decimalPart = substr($numberToParse, $positionOfDecimalSeparator + 1, $positionOfLastDigit - $positionOfDecimalSeparator);
     }
     $parsedNumber = (int) preg_replace(self::PATTERN_MATCH_NOT_DIGITS, '', $integerPart);
     if ($decimalPart !== false) {
         $decimalPart = (int) preg_replace(self::PATTERN_MATCH_NOT_DIGITS, '', $decimalPart);
         $parsedNumber = (double) ($parsedNumber . '.' . $decimalPart);
     }
     $partBeforeNumber = substr($numberToParse, 0, $positionOfFirstDigit);
     $partAfterNumber = substr($numberToParse, -(strlen($numberToParse) - $positionOfLastDigit - 1));
     if (!empty($parsedFormat['negativePrefix']) && !empty($parsedFormat['negativeSuffix'])) {
         if (\TYPO3\Flow\I18n\Utility::stringEndsWith($partBeforeNumber, $parsedFormat['negativePrefix']) && \TYPO3\Flow\I18n\Utility::stringBeginsWith($partAfterNumber, $parsedFormat['negativeSuffix'])) {
             $numberIsNegative = true;
         }
     } elseif (!empty($parsedFormat['negativePrefix']) && \TYPO3\Flow\I18n\Utility::stringEndsWith($partBeforeNumber, $parsedFormat['negativePrefix'])) {
         $numberIsNegative = true;
     } elseif (!empty($parsedFormat['negativeSuffix']) && \TYPO3\Flow\I18n\Utility::stringBeginsWith($partAfterNumber, $parsedFormat['negativeSuffix'])) {
         $numberIsNegative = true;
     }
     $parsedNumber /= $parsedFormat['multiplier'];
     if ($numberIsNegative) {
         $parsedNumber = 0 - $parsedNumber;
     }
     return $parsedNumber;
 }
示例#5
0
 /**
  * Finds all Locale objects representing locales available in the
  * Flow installation. This is done by scanning all Private and Public
  * resource files of all active packages, in order to find localized files.
  *
  * Localized files have a locale identifier added before their extension
  * (or at the end of filename, if no extension exists). For example, a
  * localized file for foobar.png, can be foobar.en.png, fobar.en_GB.png, etc.
  *
  * Just one localized resource file causes the corresponding locale to be
  * regarded as available (installed, supported).
  *
  * Note: result of this method invocation is cached
  *
  * @return void
  */
 protected function generateAvailableLocalesCollectionByScanningFilesystem()
 {
     foreach ($this->packageManager->getActivePackages() as $activePackage) {
         $packageResourcesPath = $this->localeBasePath . $activePackage->getPackageKey() . '/';
         if (!is_dir($packageResourcesPath)) {
             continue;
         }
         $directoryIterator = new \RecursiveDirectoryIterator($packageResourcesPath, \RecursiveDirectoryIterator::UNIX_PATHS);
         $recursiveIteratorIterator = new \RecursiveIteratorIterator($directoryIterator, \RecursiveIteratorIterator::SELF_FIRST);
         foreach ($recursiveIteratorIterator as $fileOrDirectory) {
             if ($fileOrDirectory->isFile()) {
                 $localeIdentifier = Utility::extractLocaleTagFromFilename($fileOrDirectory->getFilename());
                 if ($localeIdentifier !== FALSE) {
                     $this->localeCollection->addLocale(new Locale($localeIdentifier));
                 }
             }
         }
     }
 }
 /**
  * Finds all Locale objects representing locales available in the
  * Flow installation. This is done by scanning all Private and Public
  * resource files of all active packages, in order to find localized files.
  *
  * Localized files have a locale identifier added before their extension
  * (or at the end of filename, if no extension exists). For example, a
  * localized file for foobar.png, can be foobar.en.png, fobar.en_GB.png, etc.
  *
  * Just one localized resource file causes the corresponding locale to be
  * regarded as available (installed, supported).
  *
  * Note: result of this method invocation is cached
  *
  * @return void
  */
 protected function generateAvailableLocalesCollectionByScanningFilesystem()
 {
     $whitelistPaths = array_keys(array_filter((array) $this->settings['scan']['includePaths']));
     if ($whitelistPaths === []) {
         return;
     }
     $blacklistPattern = $this->getScanBlacklistPattern();
     /** @var PackageInterface $activePackage */
     foreach ($this->packageManager->getActivePackages() as $activePackage) {
         $packageResourcesPath = Files::getNormalizedPath($activePackage->getResourcesPath());
         if (!is_dir($packageResourcesPath)) {
             continue;
         }
         $directories = [];
         foreach ($whitelistPaths as $path) {
             $scanPath = Files::concatenatePaths(array($packageResourcesPath, $path));
             if (is_dir($scanPath)) {
                 array_push($directories, Files::getNormalizedPath($scanPath));
             }
         }
         while ($directories !== []) {
             $currentDirectory = array_pop($directories);
             $relativeDirectory = '/' . str_replace($packageResourcesPath, '', $currentDirectory);
             if ($blacklistPattern !== '' && preg_match($blacklistPattern, $relativeDirectory) === 1) {
                 continue;
             }
             if ($handle = opendir($currentDirectory)) {
                 while (false !== ($filename = readdir($handle))) {
                     if ($filename[0] === '.') {
                         continue;
                     }
                     $pathAndFilename = Files::concatenatePaths(array($currentDirectory, $filename));
                     if (is_dir($pathAndFilename)) {
                         array_push($directories, Files::getNormalizedPath($pathAndFilename));
                     } else {
                         $localeIdentifier = Utility::extractLocaleTagFromFilename($filename);
                         if ($localeIdentifier !== false) {
                             $this->localeCollection->addLocale(new Locale($localeIdentifier));
                         }
                     }
                 }
                 closedir($handle);
             }
         }
     }
 }
 /**
  * Finds all Locale objects representing locales available in the
  * Flow installation. This is done by scanning all Private and Public
  * resource files of all active packages, in order to find localized files.
  *
  * Localized files have a locale identifier added before their extension
  * (or at the end of filename, if no extension exists). For example, a
  * localized file for foobar.png, can be foobar.en.png, fobar.en_GB.png, etc.
  *
  * Just one localized resource file causes the corresponding locale to be
  * regarded as available (installed, supported).
  *
  * Note: result of this method invocation is cached
  *
  * @return void
  */
 protected function generateAvailableLocalesCollectionByScanningFilesystem()
 {
     /** @var PackageInterface $activePackage */
     foreach ($this->packageManager->getActivePackages() as $activePackage) {
         $packageResourcesPath = $activePackage->getResourcesPath();
         if (!is_dir($packageResourcesPath)) {
             continue;
         }
         $directories = array(Files::getNormalizedPath($packageResourcesPath));
         while ($directories !== array()) {
             $currentDirectory = array_pop($directories);
             if ($handle = opendir($currentDirectory)) {
                 while (false !== ($filename = readdir($handle))) {
                     if ($filename[0] === '.') {
                         continue;
                     }
                     $pathAndFilename = Files::concatenatePaths(array($currentDirectory, $filename));
                     if (is_dir($pathAndFilename)) {
                         array_push($directories, Files::getNormalizedPath($pathAndFilename));
                     } else {
                         $localeIdentifier = Utility::extractLocaleTagFromFilename($filename);
                         if ($localeIdentifier !== false) {
                             $this->localeCollection->addLocale(new Locale($localeIdentifier));
                         }
                     }
                 }
                 closedir($handle);
             }
         }
     }
 }