inputDir = $inputDir; $this->outputDir = $outputDir; $inputOutputMappings = $this->createInputOutputMappings(); $availableDataFiles = array(); $progress = new ProgressBar($consoleOutput, count($inputOutputMappings)); $progress->start(); foreach ($inputOutputMappings as $textFile => $outputFiles) { $mappings = $this->readMappingsFromFile($textFile); $language = $this->getLanguageFromTextFile($textFile); $this->removeEmptyEnglishMappings($mappings, $language); $this->makeDataFallbackToEnglish($textFile, $mappings); $mappingForFiles = $this->splitMap($mappings, $outputFiles); foreach ($mappingForFiles as $outputFile => $value) { $this->writeMappingFile($language, $outputFile, $value); $this->addConfigurationMapping($availableDataFiles, $language, $outputFile); } $progress->advance(); } $this->writeConfigMap($availableDataFiles); $progress->finish(); } private function createInputOutputMappings() { $topLevel = scandir($this->inputDir); $mappings = array(); foreach ($topLevel as $languageDirectory) { if (in_array($languageDirectory, $this->filesToIgnore)) { continue; } $fileLocation = $this->inputDir . DIRECTORY_SEPARATOR . $languageDirectory; if (is_dir($fileLocation)) { // Will contain files $countryCodeFiles = scandir($fileLocation); foreach ($countryCodeFiles as $countryCodeFileName) { if (in_array($countryCodeFileName, $this->filesToIgnore)) { continue; } $outputFiles = $this->createOutputFileNames( $countryCodeFileName, $this->getCountryCodeFromTextFileName($countryCodeFileName), $languageDirectory ); $mappings[$languageDirectory . DIRECTORY_SEPARATOR . $countryCodeFileName] = $outputFiles; } } } return $mappings; } /** * Method used by {@code #createInputOutputMappings()} to generate the list of output binary files * from the provided input text file. For the data files expected to be large (currently only * NANPA is supported), this method generates a list containing one output file for each area * code. Otherwise, a single file is added to the list. */ private function createOutputFileNames($file, $countryCode, $language) { $outputFiles = array(); if ($countryCode == self::NANPA_COUNTRY_CODE) { // Fetch the 4-digit prefixes stored in the file. $phonePrefixes = array(); $this->parseTextFile( $this->getFilePathFromLanguageAndCountryCode($language, $countryCode), function ($prefix, $location) use (&$phonePrefixes) { $shortPrefix = substr($prefix, 0, 4); if (!in_array($shortPrefix, $phonePrefixes)) { $phonePrefixes[] = $shortPrefix; } } ); foreach ($phonePrefixes as $prefix) { $outputFiles[] = $this->generateFilename($prefix, $language); } } elseif ($countryCode == 86) { /* * Reduce memory usage for China numbers * @see https://github.com/giggsey/libphonenumber-for-php/issues/44 */ // Fetch the 5-digit prefixes stored in the file. $phonePrefixes = array(); $this->parseTextFile( $this->getFilePathFromLanguageAndCountryCode($language, $countryCode), function ($prefix, $location) use (&$phonePrefixes) { $shortPrefix = substr($prefix, 0, 5); if (!in_array($shortPrefix, $phonePrefixes)) { $phonePrefixes[] = $shortPrefix; } } ); foreach ($phonePrefixes as $prefix) { $outputFiles[] = $this->generateFilename($prefix, $language); } } else { $outputFiles[] = $this->generateFilename($countryCode, $language); } return $outputFiles; } /** * Reads phone prefix data from the provides file path and invokes the given handler for each * mapping read. * * @param $filePath * @param $handler * @return array * @throws \InvalidArgumentException */ private function parseTextFile($filePath, \Closure $handler) { if (!file_exists($filePath) || !is_readable($filePath)) { throw new \InvalidArgumentException("File '{$filePath}' does not exist"); } $data = file($filePath); $countryData = array(); foreach ($data as $line) { // Remove \n $line = str_replace("\n", "", $line); $line = str_replace("\r", "", $line); $line = trim($line); if (strlen($line) == 0 || substr($line, 0, 1) == '#') { continue; } if (strpos($line, '|')) { // Valid line $parts = explode('|', $line); $prefix = $parts[0]; $location = $parts[1]; $handler($prefix, $location); } } return $countryData; } private function getFilePathFromLanguageAndCountryCode($language, $code) { return $this->getFilePath($language . DIRECTORY_SEPARATOR . $code . self::DATA_FILE_EXTENSION); } private function getFilePath($fileName) { $path = $this->inputDir . $fileName; return $path; } private function generateFilename($prefix, $language) { return $language . DIRECTORY_SEPARATOR . $prefix . self::DATA_FILE_EXTENSION; } private function getCountryCodeFromTextFileName($countryCodeFileName) { return str_replace(self::DATA_FILE_EXTENSION, '', $countryCodeFileName); } private function readMappingsFromFile($inputFile) { $areaCodeMap = array(); $this->parseTextFile( $this->inputDir . $inputFile, function ($prefix, $location) use (&$areaCodeMap) { $areaCodeMap[$prefix] = $location; } ); return $areaCodeMap; } private function getLanguageFromTextFile($textFile) { $parts = explode(DIRECTORY_SEPARATOR, $textFile); return $parts[0]; } private function removeEmptyEnglishMappings(&$mappings, $language) { if ($language != "en") { return; } foreach ($mappings as $k => $v) { if ($v == "") { unset($mappings[$k]); } } } /** * Compress the provided mappings according to the English data file if any. * @param string $textFile * @param array $mappings */ private function makeDataFallbackToEnglish($textFile, &$mappings) { $englishPath = $this->getEnglishDataPath($textFile); if ($textFile == $englishPath || !file_exists($this->getFilePath($englishPath))) { return; } $countryCode = substr($textFile, 3, 2); if (!array_key_exists($countryCode, $this->englishMaps)) { $englishMap = $this->readMappingsFromFile($englishPath); $this->englishMaps[$countryCode] = $englishMap; } $this->compressAccordingToEnglishData($this->englishMaps[$countryCode], $mappings); } private function getEnglishDataPath($textFile) { return "en" . DIRECTORY_SEPARATOR . substr($textFile, 3, 2) . self::DATA_FILE_EXTENSION; } private function compressAccordingToEnglishData($englishMap, &$nonEnglishMap) { foreach ($nonEnglishMap as $prefix => $value) { if (array_key_exists($prefix, $englishMap)) { $englishDescription = $englishMap[$prefix]; if ($englishDescription == $value) { if (!$this->hasOverlappingPrefix($prefix, $nonEnglishMap)) { unset($nonEnglishMap[$prefix]); } else { $nonEnglishMap[$prefix] = ""; } } } } } private function hasOverlappingPrefix($number, $mappings) { while (strlen($number) > 0) { $number = substr($number, 0, -1); if (array_key_exists($number, $mappings)) { return true; } } return false; } private function splitMap($mappings, $outputFiles) { $mappingForFiles = array(); foreach ($mappings as $prefix => $location) { $targetFile = null; foreach ($outputFiles as $k => $outputFile) { $outputFilePrefix = $this->getPhonePrefixLanguagePairFromFilename($outputFile)->prefix; if (self::startsWith($prefix, $outputFilePrefix)) { $targetFile = $outputFilePrefix; break; } } if (!array_key_exists($targetFile, $mappingForFiles)) { $mappingForFiles[$targetFile] = array(); } $mappingForFiles[$targetFile][$prefix] = $location; } return $mappingForFiles; } /** * Extracts the phone prefix and the language code contained in the provided file name. */ private function getPhonePrefixLanguagePairFromFilename($outputFile) { $parts = explode(DIRECTORY_SEPARATOR, $outputFile); $returnObj = new \stdClass(); $returnObj->language = $parts[0]; $returnObj->prefix = $this->getCountryCodeFromTextFileName($parts[1]); return $returnObj; } /** * * @link http://stackoverflow.com/a/834355/403165 * @param $haystack * @param $needle * @return bool */ private static function startsWith($haystack, $needle) { return !strncmp($haystack, $needle, strlen($needle)); } private function writeMappingFile($language, $outputFile, $data) { if (!file_exists($this->outputDir . $language)) { mkdir($this->outputDir . $language); } $phpSource = 'outputDir . $language . DIRECTORY_SEPARATOR . $outputFile . '.php'; file_put_contents($outputPath, $phpSource); } public function addConfigurationMapping(&$availableDataFiles, $language, $prefix) { if (!array_key_exists($language, $availableDataFiles)) { $availableDataFiles[$language] = array(); } $availableDataFiles[$language][] = $prefix; } private function writeConfigMap($availableDataFiles) { $phpSource = 'outputDir . 'Map.php'; file_put_contents($outputPath, $phpSource); } }