PHP Classes

File: bin/build.php

Recommend this page to a friend!
  Classes of Michele Locati  >  Punic  >  bin/build.php  >  Download  
File: bin/build.php
Role: Application script
Content type: text/plain
Description: Configuration script
Class: Punic
Localize numbers, dates, units using Unicode CLDR
Author: By
Last change: Fix URL of full JSON data for newer CLDR versions
Effectively remove ZZ country from territoryInfo
Add minority_official support for languages
Ckeck languages
Remove "Unknown territory" from territoryInfo, let's be sure we have all the data of territoryInfo
PSR-2
Add territoryInfo.json to Punic data files
Date: 6 years ago
Size: 48,055 bytes
 

Contents

Class file image Download
<?php
function handleError($errno, $errstr, $errfile, $errline)
{
    if ($errno == E_NOTICE || $errno == E_WARNING) {
        throw new Exception("$errstr in $errfile @ line $errline", $errno);
    }
}

set_error_handler('handleError');

try {
    echo "Initializing... ";
    define('CLDR_VERSION', 26);
    define('ROOT_DIR', dirname(__DIR__));
    define('SOURCE_DIR', ROOT_DIR . DIRECTORY_SEPARATOR . 'temp' . DIRECTORY_SEPARATOR . 'source-data');
    define('DESTINATION_DIR', ROOT_DIR . DIRECTORY_SEPARATOR . 'code' . DIRECTORY_SEPARATOR . 'data');
    define('TESTS_DIR', ROOT_DIR . DIRECTORY_SEPARATOR . 'tests' . DIRECTORY_SEPARATOR . 'dataFiles');

    if (isset($argv)) {
        foreach ($argv as $i => $arg) {
            if ($i > 0) {
                if ((strcasecmp($arg, 'debug') === 0) || (strcasecmp($arg, '--debug') === 0)) {
                    defined('DEBUG') or define('DEBUG', true);
                }
                if ((strcasecmp($arg, 'full') === 0) || (strcasecmp($arg, '--full') === 0)) {
                    defined('FULL_JSON') or define('FULL_JSON', true);
                }
                if ((strcasecmp($arg, 'post-clean') === 0) || (strcasecmp($arg, '--post-clean') === 0)) {
                    defined('POST_CLEAN') or define('POST_CLEAN', true);
                }
            }
        }
    }
    defined('DEBUG') or define('DEBUG', false);
    defined('FULL_JSON') or define('FULL_JSON', false);
    define('LOCAL_ZIP_FILE', SOURCE_DIR . DIRECTORY_SEPARATOR . (FULL_JSON ? 'cldr_full' : 'cldr') . '-' . CLDR_VERSION . '.zip');
    define('SOURCE_DIR_DATA', SOURCE_DIR . DIRECTORY_SEPARATOR . (FULL_JSON ? 'cldr_full' : 'cldr') . '-' . CLDR_VERSION);
    defined('POST_CLEAN') or define('POST_CLEAN', false);

    if (!is_dir(SOURCE_DIR)) {
        if (mkdir(SOURCE_DIR, 0777, true) === false) {
            echo "Failed to create " . SOURCE_DIR . "\n";
            die(1);
        }
    }
    echo "done.\n";

    if (is_dir(DESTINATION_DIR)) {
        echo "Cleanup old data folder... ";
        deleteFromFilesystem(DESTINATION_DIR);
        echo "done.\n";
    }
    echo "Creating data folder... ";
    if (mkdir(DESTINATION_DIR, 0777, false) === false) {
        echo "Failed to create " . DESTINATION_DIR . "\n";
        die(1);
    }
    echo "done.\n";
    if (!is_dir(TESTS_DIR)) {
        echo "Creating tests folder... ";
        if (mkdir(TESTS_DIR, 0777, false) === false) {
            echo "Failed to create " . TESTS_DIR . "\n";
            die(1);
        }
        echo "done.\n";
    }

    if (!is_dir(SOURCE_DIR_DATA)) {
        if (!is_file(LOCAL_ZIP_FILE)) {
            downloadCLDR();
        }
        ExtractCLDR();
    }
    copyData();
    if (POST_CLEAN) {
        echo "Cleanup temporary data folder... ";
        deleteFromFilesystem(SOURCE_DIR_DATA);
        echo "done.\n";
    }
    die(0);
} catch (Exception $x) {
    echo $x->getMessage(), "\n";
    die(1);
}

function downloadCLDR()
{
    if(version_compare(CLDR_VERSION, 26) >= 0) {
        $remoteURL = 'http://unicode.org/Public/cldr/' . CLDR_VERSION . '/' . (FULL_JSON ? 'json-full.zip' : 'json.zip');
    }
    else {
        $remoteURL = 'http://unicode.org/Public/cldr/' . CLDR_VERSION . '/' . (FULL_JSON ? 'json_full.zip' : 'json.zip');
    }
    $zipFrom = null;
    $zipTo = null;
    echo "Downloading $remoteURL... ";
    try {
        $zipFrom = fopen($remoteURL, 'rb');
        if ($zipFrom === false) {
            throw new Exception("Failed to read $remoteURL");
        }
        $zipTo = fopen(LOCAL_ZIP_FILE, 'wb');
        if ($zipTo === false) {
            throw new Exception("Failed to create " . LOCAL_ZIP_FILE);
        }
        while (!feof($zipFrom)) {
            $buffer = fread($zipFrom, 4096);
            if ($buffer === false) {
                throw new Exception("Failed to fetch data from $remoteURL");
            }
            if (fwrite($zipTo, $buffer) === false) {
                throw new Exception("Failed to write data to " . LOCAL_ZIP_FILE);
            }
        }
        fclose($zipTo);
        $zipTo = null;
        fclose($zipFrom);
        $zipFrom = null;
        echo "done.\n";
    } catch (Exception $x) {
        if ($zipTo) {
            fclose($zipTo);
            $zipTo = null;
        }
        if ($zipFrom) {
            fclose($zipFrom);
            $zipFrom = null;
        }
        if (is_file(LOCAL_ZIP_FILE)) {
            unlink(LOCAL_ZIP_FILE);
        }
        throw $x;
    }
}

function extractCLDR()
{
    $zip = null;
    echo "Extracting " . LOCAL_ZIP_FILE . "... ";
    try {
        $zip = new ZipArchive();
        $rc = $zip->open(LOCAL_ZIP_FILE);
        if ($rc !== true) {
            throw new Exception("Opening " . LOCAL_ZIP_FILE . " failed with return code $rc");
        }
        $zip->extractTo(SOURCE_DIR_DATA);
        $zip->close();
        $zip = null;
        echo "done.\n";
    } catch (Exception $x) {
        if ($zip) {
            @$zip->close();
            $zip = null;
        }
        if (is_dir(SOURCE_DIR_DATA)) {
            try {
                deleteFromFilesystem(SOURCE_DIR_DATA);
            } catch (Exception $foo) {
            }
        }
        throw $x;
    }
}

function copyData()
{
    $copy = array(
        'ca-gregorian.json' => array('kind' => 'main', 'save-as' => 'calendar.json', 'roots' => array('dates', 'calendars', 'gregorian')),
        'timeZoneNames.json' => array('kind' => 'main', 'roots' => array('dates', 'timeZoneNames')),
        'listPatterns.json' => array('kind' => 'main', 'roots' => array('listPatterns')),
        'units.json' => array('kind' => 'main', 'roots' => array('units')),
        'dateFields.json' => array('kind' => 'main', 'roots' => array('dates', 'fields')),
        'languages.json' => array('kind' => 'main', 'roots' => array('localeDisplayNames', 'languages')),
        'territories.json' => array('kind' => 'main', 'roots' => array('localeDisplayNames', 'territories')),
        'localeDisplayNames.json' => array('kind' => 'main', 'roots' => array('localeDisplayNames')),
        'numbers.json' => array('kind' => 'main', 'roots' => array('numbers')),
        /*
        'characters.json' => array('kind' => 'main', 'roots' => array('characters')),
        'contextTransforms.json' => array('kind' => 'main', 'roots' => array('contextTransforms')),
        'currencies.json' => array('kind' => 'main', 'roots' => array('numbers', 'currencies')),
        'delimiters.json' => array('kind' => 'main', 'roots' => array('delimiters')),
        'layout.json' => array('kind' => 'main', 'roots' => array('layout', 'orientation')),
        'measurementSystemNames.json' => array('kind' => 'main', 'roots' => array('localeDisplayNames', 'measurementSystemNames')),
        'scripts.json' => array('kind' => 'main', 'roots' => array('localeDisplayNames', 'scripts')),
        'transformNames.json' => array('kind' => 'main', 'roots' => array('localeDisplayNames', 'transformNames')),
        'variants.json' => array('kind' => 'main', 'roots' => array('localeDisplayNames', 'variants')),
        */
        'territoryInfo.json' => array('kind' => 'supplemental', 'roots' => array('supplemental', 'territoryInfo')),
        'weekData.json' => array('kind' => 'supplemental', 'roots' => array('supplemental', 'weekData')),
        'parentLocales.json' => array('kind' => 'supplemental', 'roots' => array('supplemental', 'parentLocales', 'parentLocale')),
        'likelySubtags.json' => array('kind' => 'supplemental', 'roots' => array('supplemental', 'likelySubtags')),
        'territoryContainment.json' => array('kind' => 'supplemental', 'roots' => array('supplemental', 'territoryContainment')),
        'metaZones.json' => array('kind' => 'supplemental', 'roots' => array('supplemental', 'metaZones')),
        'plurals.json' => array('kind' => 'supplemental', 'roots' => array('supplemental', 'plurals-type-cardinal')),
    );
    $src = SOURCE_DIR_DATA . DIRECTORY_SEPARATOR . 'main';
    $locales = scandir($src);
    if ($locales === false) {
        throw new Exception("Failed to retrieve the file list of $src");
    }
    $locales = array_diff($locales, array('.', '..'));
    foreach ($locales as $locale) {
        if (is_dir($src . DIRECTORY_SEPARATOR . $locale)) {
            echo "Parsing locale $locale... ";
            $destFolder = DESTINATION_DIR . DIRECTORY_SEPARATOR . $locale;
            if (is_dir($destFolder)) {
                deleteFromFilesystem($destFolder);
            }
            if (mkdir($destFolder) === false) {
                throw new Exception("Failed to create $destFolder\n");
            }
            foreach ($copy as $copyFrom => $info) {
                if ($info['kind'] === 'main') {
                    $copyTo = array_key_exists('save-as', $info) ? $info['save-as'] : $copyFrom;
                    if ($copyTo === false) {
                        $copyTo = $copyFrom;
                    }
                    $dstFile = $destFolder . DIRECTORY_SEPARATOR . $copyTo;
                    $useLocale = $locale;
                    $srcFile = $src . DIRECTORY_SEPARATOR . $useLocale . DIRECTORY_SEPARATOR . $copyFrom;
                    if (!is_file($srcFile)) {
                        $useLocale = 'en';
                        $srcFile = $src . DIRECTORY_SEPARATOR . $useLocale . DIRECTORY_SEPARATOR . $copyFrom;
                        if (!is_file($srcFile)) {
                            throw new Exception("File not found: $srcFile");
                        }
                    }
                    $info['roots'] = array_merge(array('main', $useLocale), $info['roots']);
                    $info['unsetByPath'] = array_merge(
                        isset($info['unsetByPath']) ? $info['unsetByPath'] : array(),
                        array(
                            "/main/$useLocale" => array('identity'),
                        )
                    );
                    copyDataFile($srcFile, $info, $dstFile);
                }
            }
            echo "done.\n";
        }
    }
    echo "Parsing supplemental files... ";
    $src = SOURCE_DIR_DATA . DIRECTORY_SEPARATOR . 'supplemental';
    foreach ($copy as $copyFrom => $info) {
        if ($info['kind'] === 'supplemental') {
            $copyTo = array_key_exists('save-as', $info) ? $info['save-as'] : $copyFrom;
            $dstFile = DESTINATION_DIR . DIRECTORY_SEPARATOR . $copyTo;
            $srcFile = $src . DIRECTORY_SEPARATOR . $copyFrom;
            if (!is_file($srcFile)) {
                throw new Exception("File not found: $srcFile");
            }
            $info['unsetByPath'] = array_merge(
                isset($info['unsetByPath']) ? $info['unsetByPath'] : array(),
                array(
                    '/supplemental' => array('version', 'generation'),
                )
            );
            copyDataFile($srcFile, $info, $dstFile);
        }
    }
    echo "done.\n";
}

function copyDataFile($srcFile, $info, $dstFile)
{
    $json = file_get_contents($srcFile);
    if ($json === false) {
        throw new Exception("Failed to read from $srcFile");
    }
    $data = json_decode($json, true);
    if (is_null($data)) {
        throw new Exception("Failed to decode data in $srcFile");
    }
    $path = '';
    foreach ($info['roots'] as $root) {
        if (!is_array($data)) {
            throw new Exception("Decoded data should be an array in $srcFile (path: $path)");
        }
        if (isset($info['unsetByPath'][$path])) {
            foreach ($info['unsetByPath'][$path] as $node) {
                if (array_key_exists($node, $data)) {
                    unset($data[$node]);
                }
            }
        }
        checkOneKey($data, $root);
        $data = $data[$root];
        $path .= "/$root";
    }
    if (!is_array($data)) {
        throw new Exception("Decoded data should be an array in $srcFile (path: $path)");
    }
    $jsonFlags = 0;
    if (version_compare(PHP_VERSION, '5.4.0') >= 0) {
        $jsonFlags |= JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE;
        if (DEBUG) {
            $jsonFlags |= JSON_PRETTY_PRINT;
        }
    }
    switch (basename($dstFile)) {
        case 'calendar.json':
            unset($data['dateTimeFormats']['availableFormats']);
            unset($data['dateTimeFormats']['appendItems']);
            unset($data['dateTimeFormats']['intervalFormats']);
            foreach (array_keys($data['dateTimeFormats']) as $key) {
                $data['dateTimeFormats'][$key] = toPhpSprintf($data['dateTimeFormats'][$key]);
            }
            foreach (array('eraNames' => 'wide', 'eraAbbr' => 'abbreviated', 'eraNarrow' => 'narrow') as $keyFrom => $keyTo) {
                if (array_key_exists($keyFrom, $data['eras'])) {
                    $data['eras'][$keyTo] = $data['eras'][$keyFrom];
                    unset($data['eras'][$keyFrom]);
                }
            }
            break;
        case 'territoryInfo.json': // http://www.unicode.org/reports/tr35/tr35-info.html#Supplemental_Territory_Information
            unset($data['ZZ']);
            foreach ($data as $k => $v) {
                $D = array();
                foreach ($v as $k2 => $v2) {
                    switch ($k2) {
                        case '_gdp': // Gross domestic product
                            if (!is_int($v2)) {
                                $v3 = @intval($v2);
                                if (strval($v3) !== $v2) {
                                    $v3 = @floatval($v2);
                                }
                                if (strval($v3) !== $v2) {
                                    throw new Exception("Unable to parse $v2 as an integer ($k2)");
                                }
                                $v2 = $v3;
                            }
                            $D['gdp'] = $v2;
                            break;
                        case '_literacyPercent':
                            if (!(is_int($v2) || is_float($v2))) {
                                $v3 = @floatval($v2);
                                if (strval($v3) !== $v2) {
                                    $v3 = @floatval($v2);
                                }
                                if (strval($v3) !== $v2) {
                                    throw new Exception("Unable to parse $v2 as an integer ($k2)");
                                }
                                $v2 = $v3;
                            }
                            $D['literacy'] = $v2;
                            break;
                        case '_population':
                            if (!is_int($v2)) {
                                $v3 = @intval($v2);
                                if (strval($v3) !== $v2) {
                                    $v3 = @floatval($v2);
                                }
                                if (strval($v3) !== $v2) {
                                    throw new Exception("Unable to parse $v2 as an integer ($k2)");
                                }
                                $v2 = $v3;
                            }
                            $D['population'] = $v2;
                            break;
                        case 'languagePopulation':
                            if (!is_array($v2)) {
                                throw new Exception("Invalid node: $k2 is not an array");
                            }
                            $D['languages'] = array();
                            foreach ($v2 as $k3 => $v3) {
                                if (!is_array($v3)) {
                                    throw new Exception("Invalid node: $k2/$k3 is not an array");
                                }
                                $D['languages'][$k3] = array();
                                foreach ($v3 as $k4 => $v4) {
                                    switch ($k4) {
                                        case '_officialStatus':
                                            switch ($v4) {
                                                case 'official':
                                                    $v5 = 'o';
                                                    break;
                                                case 'official_regional':
                                                    $v5 = 'r';
                                                    break;
                                                case 'de_facto_official':
                                                    $v5 = 'f';
                                                    break;
                                                case 'official_minority':
                                                	 $v5 = 'm';
                                                	 break;
                                                default:
                                                    throw new Exception("Unknown language status: $v4");
                                            }
                                            $D['languages'][$k3]['status'] = $v5;
                                            break;
                                        case '_populationPercent':
                                            if (!(is_int($v4) || is_float($v4))) {
                                                $v5 = @floatval($v4);
                                                if (strval($v5) !== $v4) {
                                                    $v5 = @floatval($v4);
                                                }
                                                if (strval($v5) !== $v4) {
                                                    throw new Exception("Unable to parse $v4 as an integer ($k2)");
                                                }
                                                $v4 = $v5;
                                            }
                                            $D['languages'][$k3]['population'] = $v4;
                                            break;
                                        case '_writingPercent':
                                            if (!(is_int($v4) || is_float($v4))) {
                                                $v5 = @floatval($v4);
                                                if (strval($v5) !== $v4) {
                                                    $v5 = @floatval($v4);
                                                }
                                                if (strval($v5) !== $v4) {
                                                    throw new Exception("Unable to parse $v4 as an integer ($k2)");
                                                }
                                                $v4 = $v5;
                                            }
                                            $D['languages'][$k3]['writing'] = $v4;
                                            break;
                                        default:
                                            throw new Exception("Unknown node: $k2/$k3/$k4");
                                    }
                                }
                                if (!array_key_exists('population', $D['languages'][$k3])) {
                                    throw new Exception("Missing _populationPercent node in for $k/$k2/$k3");
                                }
                            }
                            if (empty($D['languages'])) {
                                throw new Exception("No languages for $k");
                            }
                            break;
                        default:
                            throw new Exception("Unknown node: $k2");
                            die($k2);
                    }
                }
                if (!array_key_exists('gdp', $D)) {
                    throw new Exception("Missing _gdp node in for $k");
                }
                if (!array_key_exists('literacy', $D)) {
                    throw new Exception("Missing _literacyPercent node in for $k");
                }
                if (!array_key_exists('population', $D)) {
                    throw new Exception("Missing _population node in for $k");
                }
                if (!array_key_exists('languages', $D)) {
                    throw new Exception("Missing languagePopulation node in for $k");
                }
                $data[$k] = $D;
            }
            break;
        case 'weekData.json':
            foreach (array_keys($data['minDays']) as $key) {
                $value = $data['minDays'][$key];
                if (!preg_match('/^[0-9]+$/', $value)) {
                    throw new Exception("Bad number: $value");
                }
                $data['minDays'][$key] = intval($value);
            }
            $dict = array('sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat');
            foreach (array_keys($data['firstDay']) as $key) {
                $val = array_search($data['firstDay'][$key], $dict, true);
                if ($val === false) {
                    throw new Exception("Unknown weekday name: {$data['firstDay'][$key]}");
                }
                $data['firstDay'][$key] = $val;
            }
            unset($data['firstDay-alt-variant']);
            unset($data['weekendStart']);
            unset($data['weekendEnd']);
            break;
        case 'territoryContainment.json':
            foreach (array_keys($data) as $key) {
                if (array_key_exists('_grouping', $data[$key])) {
                    unset($data[$key]['_grouping']);
                }
                if (array_key_exists('_contains', $data[$key])) {
                    $data[$key]['contains'] = $data[$key]['_contains'];
                    unset($data[$key]['_contains']);
                }
                if (strpos($key, '-status-') !== false) {
                    unset($data[$key]);
                }
            }
            break;
        case 'metaZones.json':
            checkOneKey($data['metazoneInfo'], 'timezone');
            $data['metazoneInfo'] = $data['metazoneInfo']['timezone'];
            foreach ($data['metazoneInfo'] as $id0 => $info0) {
                $values1 = null;
                foreach ($info0 as $id1 => $info1) {
                    if (is_int($id1)) {
                        $info1 = fixMetazoneInfo($info1);
                    } else {
                        foreach ($info1 as $id2 => $info2) {
                            if (is_int($id2)) {
                                $info2 = fixMetazoneInfo($info2);
                            } else {
                                foreach ($info2 as $id3 => $info3) {
                                    if (is_int($id3)) {
                                        $info3 = fixMetazoneInfo($info3);
                                    } else {
                                        throw new Exception('Invalid metazoneInfo node');
                                    }
                                    $info2[$id3] = $info3;
                                }
                            }
                            $info1[$id2] = $info2;
                        }
                    }
                    $info0[$id1] = $info1;
                }
                $data['metazoneInfo'][$id0] = $info0;
            }
            $metazones = array();
            if ((!array_key_exists('metazones', $data)) && is_array($data['metazones']) && (count($data['metazones']) > 0)) {
                throw new Exception('metazones node not found/invalid');
            }
            foreach ($data['metazones'] as $mz) {
                checkOneKey($mz, 'mapZone');
                $mz = $mz['mapZone'];
                foreach (array_keys($mz) as $i) {
                    switch ($i) {
                        case '_other':
                        case '_territory':
                        case '_type':
                            $mz[substr($i, 1)] = $mz[$i];
                            unset($mz[$i]);
                            break;
                        default:
                            throw new Exception('Invalid mapZone node key: ' . $i);
                    }
                }
                $metazones[] = $mz;
            }
            $data['metazones'] = $metazones;
            break;
        case 'timeZoneNames.json':
            foreach (array('gmtFormat', 'gmtZeroFormat', 'regionFormat', 'regionFormat-type-standard', 'regionFormat-type-daylight', 'fallbackFormat') as $k) {
                if (array_key_exists($k, $data)) {
                    $data[$k] = toPhpSprintf($data[$k]);
                }
            }
            break;
        case 'listPatterns.json':
            $keys = array_keys($data);
            foreach ($keys as $key) {
                if (!preg_match('/^listPattern-type-(.+)$/', $key, $m)) {
                    throw new Exception("Invalid node '$key' in " . $dstFile);
                }
                foreach (array_keys($data[$key]) as $k) {
                    $data[$key][$k] = toPhpSprintf($data[$key][$k]);
                }
                $data[$m[1]] = $data[$key];
                unset($data[$key]);
            }
            break;
        case 'plurals.json':
            $testData = array();
            foreach ($data as $l => $lData) {
                $testData[$l] = array();
                $keys = array_keys($lData);
                foreach ($keys as $key) {
                    if (!preg_match('/^pluralRule-count-(.+)$/', $key, $m)) {
                        throw new Exception("Invalid node '$key' in " . $dstFile);
                    }
                    $rule = $m[1];
                    $testData[$l][$rule] = array();
                    $vOriginal = $lData[$key];
                    $examples = explode('@', $vOriginal);
                    $v = trim(array_shift($examples));
                    foreach ($examples as $example) {
                        list($exampleNumberType, $exampleValues) = explode(' ', $example, 2);
                        switch ($exampleNumberType) {
                            case 'integer':
                            case 'decimal':
                                $exampleValues = preg_replace('/, …$/', '', $exampleValues);
                                $exampleValuesParsed = array();
                                foreach (explode(', ', trim($exampleValues)) as $ev) {
                                    if (preg_match('/^[+\-]?\d+$/', $ev)) {
                                        $exampleValuesParsed[] = $ev;
                                        $exampleValuesParsed[] = intval($ev);
                                    } elseif (preg_match('/^[+\-]?\d+\.\d+$/', $ev)) {
                                        $exampleValuesParsed[] = $ev;
                                    } elseif (preg_match('/^([+\-]?\d+)~([+\-]?\d+)$/', $ev, $m)) {
                                        $exampleValuesParsed[] = $m[1];
                                        $exampleValuesParsed[] = intval($m[1]);
                                        $exampleValuesParsed[] = $m[2];
                                        $exampleValuesParsed[] = intval($m[2]);
                                    } elseif (preg_match('/^([+\-]?\d+(\.\d+)?)~([+\-]?\d+(\.\d+)?)$/', $ev, $m)) {
                                        $exampleValuesParsed[] = $m[1];
                                        $exampleValuesParsed[] = $m[3];
                                    } elseif ($ev !== '…') {
                                        throw new Exception("Invalid node '$key' in $dstFile: $vOriginal");
                                    }
                                }
                                $testData[$l][$rule] = $exampleValuesParsed;
                                break;
                            default:
                                throw new Exception("Invalid node '$key' in $dstFile: $vOriginal");
                        }
                    }
                    if ($rule === 'other') {
                        if (strlen($v) > 0) {
                            throw new Exception("Invalid node '$key' in $dstFile: $vOriginal");
                        }
                    } else {
                        $v = str_replace(' = ', ' == ', $v);
                        $map = array('==' => 'true', '!=' => 'false');
                        foreach (array('^', ' and ', ' or ') as $pre) {
                            while (preg_match(
                                '/' . $pre . '(([nivfwft]( % \\d+)?) (==|!=) ((\\d+)(((\\.\\.)|,)+(\\d+))+))/',
                                $v,
                                $m
                            )) {
                                $found = $m[1];
                                $leftyPart = $m[2]; // eg 'n % 10'
                                $operator = $m[4]; // eg '=='
                                $ranges = explode(',', $m[5]);
                                foreach (array_keys($ranges) as $j) {
                                    if (preg_match('/^(\\d+)\\.\\.(\\d+)$/', $ranges[$j], $m)) {
                                        $ranges[$j] = "array({$m[1]}, {$m[2]})";
                                    }
                                }
                                $v = str_replace($found, "static::inRange($leftyPart, {$map[$operator]}, " . implode(', ', $ranges) . ")", $v);
                            }
                        }
                        if (strpos($v, '..') !== false) {
                            throw new Exception("Invalid node '$key' in $dstFile: $vOriginal");
                        }
                        foreach (array(
                            'n' => '%1$s', // absolute value of the source number (integer and decimals).
                            'i' => '%2$s', // integer digits of n
                            'v' => '%3$s', // number of visible fraction digits in n, with trailing zeros.
                            'w' => '%4$s', // number of visible fraction digits in n, without trailing zeros.
                            'f' => '%5$s', // visible fractional digits in n, with trailing zeros.
                            't' => '%6$s', // visible fractional digits in n, without trailing zeros.
                        ) as $from => $to) {
                            $v = preg_replace('/^' . $from .' /', "$to ", $v);
                            $v = preg_replace("/^$from /", "$to ", $v);
                            $v = str_replace(" $from ", " $to ", $v);
                            $v = str_replace("($from, ", "($to, ", $v);
                            $v = str_replace("($from ", "($to ", $v);
                            $v = str_replace(" $from,", " $to,", $v);
                        }
                        $v = str_replace(' % ', ' %% ', $v);
                        $lData[$rule] = $v;
                    }
                    unset($lData[$key]);
                }
                $data[$l] = $lData;
            }
            $testJson = json_encode($testData, $jsonFlags);
            if ($testJson === false) {
                throw new Exception("Failed to serialize test data for $srcFile");
            }
            $testDataFile = TESTS_DIR . DIRECTORY_SEPARATOR . basename($dstFile);
            if (is_file($testDataFile)) {
                deleteFromFilesystem($testDataFile);
            }
            if (file_put_contents($testDataFile, $testJson) === false) {
                throw new Exception("Failed write to $testDataFile");
            }
            break;
        case 'units.json':
            $compounds = array();
            foreach (array_keys($data) as $width) {
                switch ($width) {
                    case 'long':
                    case 'short':
                    case 'narrow':
                    case 'long':
                        foreach (array_keys($data[$width]) as $unitKey) {
                            switch ($unitKey) {
                                case 'per':
                                    if (implode('|', array_keys(($data[$width][$unitKey]))) !== 'compoundUnitPattern') {
                                        throw new Exception("Invalid node (1) '$width/$unitKey' in " . $dstFile);
                                    }
                                    $data[$width]['_compoundPattern'] = toPhpSprintf($data[$width][$unitKey]['compoundUnitPattern']);
                                    unset($data[$width][$unitKey]);
                                    break;
                                default:
                                    if (!preg_match('/^(\\w+)?-(.+)$/', $unitKey, $m)) {
                                        throw new Exception("Invalid node (2) '$width/$unitKey' in " . $dstFile);
                                    }
                                    $unitKind = $m[1];
                                    $unitName = $m[2];
                                    if (!array_key_exists($unitKind, $data[$width])) {
                                        $data[$width][$unitKind] = array();
                                    }
                                    if (!array_key_exists($unitName, $data[$width][$unitKind])) {
                                        $data[$width][$unitKind][$unitName] = array();
                                    }
                                    foreach (array_keys($data[$width][$unitKey]) as $pluralRuleSrc) {
                                        switch ($pluralRuleSrc) {
                                            case 'displayName':
                                                $data[$width][$unitKind][$unitName]['_name'] = $data[$width][$unitKey][$pluralRuleSrc];
                                                break;
                                            case 'perUnitPattern':
                                                $data[$width][$unitKind][$unitName]['_per'] = toPhpSprintf($data[$width][$unitKey][$pluralRuleSrc]);
                                                break;
                                            default:
                                                if (!preg_match('/^unitPattern-count-(.+)$/', $pluralRuleSrc, $m)) {
                                                    throw new Exception("Invalid node (4) '$width/$unitKey/$pluralRuleSrc' in " . $dstFile);
                                                }
                                                $pluralRule = $m[1];
                                                $data[$width][$unitKind][$unitName][$pluralRule] = toPhpSprintf($data[$width][$unitKey][$pluralRuleSrc]);
                                                break;
                                        }
                                    }
                                    unset($data[$width][$unitKey]);
                                    break;
                            }
                        }
                        break;
                    default:
                        if (preg_match('/^durationUnit-type-(.+)/', $width, $m)) {
                            if (implode('|', array_keys(($data[$width]))) !== 'durationUnitPattern') {
                                throw new Exception("Invalid node (5) '$width' in " . $dstFile);
                            }
                            $t = $m[1];
                            if (!array_key_exists('_durationPattern', $data)) {
                                $data['_durationPattern'] = array();
                            }
                            $data['_durationPattern'][$t] = $data[$width]['durationUnitPattern'];
                            unset($data[$width]);
                        } else {
                            throw new Exception("Invalid node (6) '$width' in " . $dstFile);
                        }
                        break;
                }
            }
            break;
        case 'localeDisplayNames.json':
            if (!array_key_exists('localeDisplayPattern', $data)) {
                throw new Exception("Missing node 'localeDisplayPattern' in " . $dstFile);
            }
            foreach (array_keys($data['localeDisplayPattern']) as $k) {
                $data['localeDisplayPattern'][$k] = toPhpSprintf($data['localeDisplayPattern'][$k]);
            }
            if (!array_key_exists('codePatterns', $data)) {
                throw new Exception("Missing node 'codePatterns' in " . $dstFile);
            }
            foreach (array_keys($data['codePatterns']) as $k) {
                $data['codePatterns'][$k] = toPhpSprintf($data['codePatterns'][$k]);
            }
            break;
        case 'numbers.json':
            $final = array();
            $numberSystems = array();
            foreach ($data as $key => $value) {
                if (preg_match('/^([a-z]+)-numberSystem-([a-z]+)$/i', $key, $m)) {
                    $keyChunk = $m[1];
                    $ns = $m[2];
                    if (!array_key_exists($ns, $numberSystems)) {
                        $numberSystems[$ns] = array();
                    }
                    if (is_array($value)) {
                        $unitPattern = null;
                        foreach ($value as $k2 => $v2) {
                            if (preg_match('/^unitPattern-(.+)$/i', $k2, $m)) {
                                if (is_null($unitPattern)) {
                                    $unitPattern = array();
                                }
                                $unitPattern[$m[1]] = toPhpSprintf($v2);
                                unset($value[$k2]);
                            }
                        }
                        if (!is_null($unitPattern)) {
                            $value['unitPattern'] = $unitPattern;
                        }
                    }
                    $numberSystems[$ns][$keyChunk] = $value;
                } else {
                    switch ($key) {
                        case 'defaultNumberingSystem':
                        case 'otherNumberingSystems':
                            break;
                        case 'minimumGroupingDigits':
                            if (is_string($value) && preg_match('/^\\d+$/', $value)) {
                                $value = @intval($value);
                            }
                            if (!is_int($value)) {
                                throw new Exception("Invalid node '$key' in " . $dstFile);
                            }
                            $final[$key] = $value;
                            break;
                        default:
                            throw new Exception("Invalid node '$key' in " . $dstFile);
                    }
                }
            }
            if (!array_key_exists('latn', $numberSystems)) {
                throw new Exception("Missing 'latn' in " . $dstFile);
            }
            foreach ($numberSystems['latn'] as $key => $value) {
                if (array_key_exists($key, $final)) {
                    throw new Exception("Duplicated node '$key' in " . $dstFile);
                }
                // $final[$key] = $value; REMOVED ADVANCED LOCALIZATION
                if ($key === 'symbols') { // REMOVED ADVANCED LOCALIZATION
                    $final[$key] = $value;
                }
            }
            $data = $final;
            $symbols = array_key_exists('symbols', $data) ? $data['symbols'] : null;
            if (empty($symbols) || (!is_array($symbols))) {
                throw new Exception("Missing symbols in " . $dstFile);
            }
            foreach (array_keys($data) as $key) {
                if (is_array($data[$key]) && preg_match('/\\w+Formats$/', $key) && array_key_exists('standard', $data[$key])) {
                    $format = $data[$key]['standard'];
                    $data[$key]['standard'] = array('format' => $format);
                    foreach (numberFormatToRegularExpressions($symbols, $format) as $rxKey => $rx) {
                        $data[$key]['standard']["rx$rxKey"] = $rx;
                    }
                }
            }
            break;
    }
    $json = json_encode($data, $jsonFlags);
    if ($json === false) {
        throw new Exception("Failed to serialize data of $srcFile");
    }
    if (is_file($dstFile)) {
        deleteFromFilesystem($dstFile);
    }
    if (file_put_contents($dstFile, $json) === false) {
        throw new Exception("Failed write to $dstFile");
    }
}

function deleteFromFilesystem($path)
{
    if (is_file($path)) {
        if (unlink($path) === false) {
            throw new Exception("Failed to delete file $path");
        }
    } else {
        $contents = scandir($path);
        if ($contents === false) {
            throw new Exception("Failed to retrieve the file list of $path");
        }
        foreach (array_diff($contents, array('.', '..')) as $item) {
            deleteFromFilesystem($path . DIRECTORY_SEPARATOR . $item);
        }
        if (rmdir($path) === false) {
            throw new Exception("Failed to delete directory $path");
        }
    }
}

function toPhpSprintf($fmt)
{
    $result = $fmt;
    if (is_string($fmt)) {
        $result = str_replace('%', '%%', $result);
        $result = preg_replace_callback(
            '/\\{(\\d+)\\}/',
            function ($matches) {
                return '%' . (1 + intval($matches[1])) . '$s';
            },
            $fmt
        );
    }

    return $result;
}

function fixMetazoneInfo($a)
{
    checkOneKey($a, 'usesMetazone');
    $a = $a['usesMetazone'];
    foreach (array_keys($a) as $key) {
        switch ($key) {
            case '_mzone':
            case '_from':
            case '_to':
                $a[substr($key, 1)] = $a[$key];
                unset($a[$key]);
                break;
            default:
                throw new Exception('Invalid metazoneInfo node');
        }
    }

    return $a;
}

function checkOneKey($node, $key)
{
    if (!is_array($node)) {
        throw new Exception("$node is not an array");
    }
    if (count($node) !== 1) {
        throw new Exception("Expected just one node '$key', found these keys: " . implode(', ', array_keys($node)));
    }
    if (!array_key_exists($key, $node)) {
        throw new Exception("Expected just one node '$key', found this key: " . implode(', ', array_keys($node)));
    }
}

function numberFormatToRegularExpressions($symbols, $isoPattern)
{
    $p = explode(';', $isoPattern);
    $patterns = array(
        '+' => $p[0],
        '-' => (count($p) == 1) ? "-{$p[0]}" : $p[1],
    );
    $result = array();
    foreach ($patterns as $patternKey => $pattern) {
        $rxPost = $rxPre = '';
        if (preg_match('/(-)?([^0#E,\\.\\-+]*)(.+?)([^0#E,\\.\\-+]*)(-)?$/', $pattern, $m)) {
            for ($i = 1; $i < 6; $i++) {
                if (!isset($m[$i])) {
                    $m[$i] = '';
                }
            }
            if (strlen($m[2]) > 0) {
                $rxPre = preg_quote($m[2]);
            }
            $pattern = $m[1] . $m[3] . $m[5];
            if (strlen($m[4]) > 0) {
                $rxPost = preg_quote($m[4]);
            }
        }
        $rx = '';
        if (strpos($pattern, '.') !== false) {
            list($intPattern, $decimalPattern) = explode('.', $pattern, 2);
        } else {
            $intPattern = $pattern;
            $decimalPattern = '';
        }
        if (strpos($intPattern, 'E') !== false) {
            switch ($intPattern) {
                case '#E0':
                case '#E00':
                    $rx .= '(' . preg_quote($symbols['plusSign']). ')?[0-9]+((' . preg_quote($symbols['decimal']) . ')[0-9]+)*[eE]((' . preg_quote($symbols['minusSign']) . ')|(' . preg_quote($symbols['plusSign']) . '))?[0-9]+';
                    break;
                case '-#E0':
                case '-#E00':
                    $rx .= '(' . preg_quote($symbols['minusSign']). ')?[0-9]+((' . preg_quote($symbols['decimal']) . ')[0-9]+)*[eE]((' . preg_quote($symbols['minusSign']) . ')|(' . preg_quote($symbols['plusSign']) . '))?[0-9]+';
                    break;
                default:
                    throw new \Exception("Invalid chunk ('$intPattern') in pattern '$pattern'");
            }
        } elseif (strpos($intPattern, ',') !== false) {
            $chunks = explode(',', $intPattern);
            $maxChunkIndex = count($chunks) - 1;
            $prevChunk = null;
            for ($chunkIndex = 0; $chunkIndex <= $maxChunkIndex; $chunkIndex++) {
                $chunk = $chunks[$chunkIndex];
                $nextChunk = ($chunkIndex == $maxChunkIndex) ? null : $chunks[$chunkIndex + 1];
                switch ($chunk) {
                    case '#':
                    case '-#':
                        if ($chunk === '-#') {
                            $rx .= '(' . preg_quote($symbols['minusSign']) . ')?';
                        } else {
                            $rx .= '(' . preg_quote($symbols['plusSign']) . ')?';
                        }
                        if ($nextChunk === '##0') {
                            $rx .= '[0-9]{1,3}';
                        } elseif ($nextChunk === '##') {
                            $rx .= '[0-9]{1,2}';
                        } else {
                            throw new \Exception("Invalid chunk #$chunkIndex ('$chunk') in pattern '$pattern'");
                        }
                        break;
                    case '##':
                        if ($nextChunk === '##0') {
                            $rx .= '((' . preg_quote($symbols['group']) . ')?[0-9]{2})*';
                        } else {
                            throw new \Exception("Invalid chunk #$chunkIndex ('$chunk') in pattern '$pattern'");
                        }
                        break;
                    case '##0':
                        if ($prevChunk === '##') {
                            $rx .= '[0-9]';
                        } elseif (($prevChunk === '#') || ($prevChunk === '-#')) {
                            $rx .= '((' . preg_quote($symbols['group']) . ')?[0-9]{3})*';
                        } else {
                            throw new \Exception("Invalid chunk #$chunkIndex ('$chunk') in pattern '$pattern'");
                        }
                        break;
                    case '#0':
                        if ($chunkIndex === 0) {
                            $rx .= '[0-9]*';
                        } else {
                            throw new \Exception("Invalid chunk #$chunkIndex ('$chunk') in pattern '$pattern'");
                        }
                        break;
                }
                $prevChunk = $chunk;
            }
        } else {
            throw new \Exception("Invalid chunk ('$intPattern') in pattern '$pattern'");
        }

        if (strlen($decimalPattern) > 0) {
            switch ($decimalPattern) {
                case '###':
                    $rx .= '((' . preg_quote($symbols['decimal']) . ')[0-9]+)?';
                    break;
                case '###-':
                    $rx .= '((' . preg_quote($symbols['decimal']) . ')[0-9]+)?(' . preg_quote($symbols['minusSign']) . ')';
                    break;
                default:
                    if (preg_match('/^(0+)(-?)$/', $decimalPattern, $m)) {
                        $n = strlen($m[1]);
                        $rx .= '(' . preg_quote($symbols['decimal']) . ')[0-9]{' . strlen($m[1]) . '}';
                        if (substr($decimalPattern, -1) === '-') {
                            $rx .= '(' . preg_quote($symbols['minusSign']) . ')';
                        }
                    } else {
                        throw new \Exception("Invalid chunk ('$decimalPattern') in pattern '$pattern'");
                    }
            }
        }

        $result[$patternKey] = '/^' . $rxPre . $rx . $rxPost  . '$/u';
    }

    return $result;
}
For more information send a message to info at phpclasses dot org.