Adding support for binary format 2 (no more crc28 strids); Removing obsolete stuff; Adding typehints
Publish PHP Package / docker (push) Successful in 6s Details

This commit is contained in:
Pavel Shevaev 2024-03-06 16:21:38 +03:00
parent d6fc62f3d7
commit a3b62e10d9
1 changed files with 280 additions and 143 deletions

View File

@ -37,13 +37,13 @@ function config_set_base_dirs(array $dirs)
$CONFIG_BASE_DIRS = array_map(function($d) { return normalize_path($d); }, $dirs); $CONFIG_BASE_DIRS = array_map(function($d) { return normalize_path($d); }, $dirs);
} }
function config_base_dirs() function config_base_dirs() : array
{ {
global $CONFIG_BASE_DIRS; global $CONFIG_BASE_DIRS;
return $CONFIG_BASE_DIRS; return $CONFIG_BASE_DIRS;
} }
function config_map_base_dir($file, $normalized = false, $strict = true, $dirs = null) function config_map_base_dir(string $file, bool $normalized = false, bool $strict = true, ?array $dirs = null) : ?string
{ {
if(!is_array($dirs)) if(!is_array($dirs))
$dirs = config_base_dirs(); $dirs = config_base_dirs();
@ -57,7 +57,7 @@ function config_map_base_dir($file, $normalized = false, $strict = true, $dirs =
return null; return null;
} }
function config_real_path($rel_path, $strict = true, $dirs = null) function config_real_path(string $rel_path, bool $strict = true, ?array $dirs = null) : ?string
{ {
if(!is_array($dirs)) if(!is_array($dirs))
$dirs = config_base_dirs(); $dirs = config_base_dirs();
@ -69,25 +69,25 @@ function config_real_path($rel_path, $strict = true, $dirs = null)
return null; return null;
} }
function config_build_dir() function config_build_dir() : string
{ {
global $GAME_ROOT; global $GAME_ROOT;
return "$GAME_ROOT/build/tmp/"; return "$GAME_ROOT/build/tmp/";
} }
function config_set_worker_init_fn($fn) function config_set_worker_init_fn(callable $fn)
{ {
global $CONFIG_INIT_WORKER_FUNC; global $CONFIG_INIT_WORKER_FUNC;
$CONFIG_INIT_WORKER_FUNC = $fn; $CONFIG_INIT_WORKER_FUNC = $fn;
} }
function config_scan_files() function config_scan_files() : array
{ {
$files = scan_files_rec(config_base_dirs(), array('conf.js')); $files = scan_files_rec(config_base_dirs(), array('conf.js'));
return config_filter_files($files); return config_filter_files($files);
} }
function config_filter_files(array $files) function config_filter_files(array $files) : array
{ {
global $CONFIG_FILTER_FN; global $CONFIG_FILTER_FN;
if($CONFIG_FILTER_FN != null) if($CONFIG_FILTER_FN != null)
@ -101,13 +101,54 @@ function config_set_files_filter_fn(callable $fn)
$CONFIG_FILTER_FN = $fn; $CONFIG_FILTER_FN = $fn;
} }
function config_pack_bundle(array $cache_entries, $use_lz4 = false, $use_config_refs = false) function config_pack_bundle(
array $cache_entries,
bool $use_lz4 = false,
bool $use_config_refs = false,
int $binary_format = 1,
?int $version = null
) : string
{ {
global $GAME_ROOT; global $GAME_ROOT;
$binary_format = 1; if(is_null($version))
$version = game_version_code(); $version = game_version_code();
$packed_data = null;
if($binary_format == 1)
{
$packed_data = _config_pack_bundle_fmt1(
$cache_entries,
$use_lz4,
$use_config_refs,
$version
);
}
else if($binary_format == 2)
{
$packed_data = _config_pack_bundle_fmt2(
$cache_entries,
$use_lz4,
$use_config_refs,
$version
);
}
else
throw new Exception("Unknown binary format: $binary_format");
echo "CONF.BUNDLE: entries " . sizeof($cache_entries) . "; total " . kb($packed_data) .
"; format $binary_format; lz4 $use_lz4; refs $use_config_refs; CRC " . crc32($packed_data) . "\n";
return $packed_data;
}
function _config_pack_bundle_fmt1(
array $cache_entries,
bool $use_lz4 = false,
bool $use_config_refs = false,
int $version) : string
{
$MAP = array(); $MAP = array();
$STRIDMAP = array(); $STRIDMAP = array();
@ -149,21 +190,109 @@ function config_pack_bundle(array $cache_entries, $use_lz4 = false, $use_config_
$payloads_bundle .= $item[1]; $payloads_bundle .= $item[1];
$packed_data = $packed_data =
pack("C", $binary_format) . pack("C", 1) .
pack("V", $version) . pack("V", $version) .
pack("V", strlen($header_msgpack)) . pack("V", strlen($header_msgpack)) .
$header_msgpack . $header_msgpack .
$payloads_bundle; $payloads_bundle;
echo "CONF.BUNDLE: entries " . sizeof($cache_entries) . "; total " . kb($packed_data) . return $packed_data;
"; lz4 $use_lz4; refs $use_config_refs; CRC " . crc32($packed_data) . "\n"; }
function _config_pack_bundle_fmt2(
array $cache_entries,
bool $use_lz4 = false,
bool $use_config_refs = false,
int $version) : string
{
$MAP = array();
$STRIDMAP = array();
$STRIDLIST = array();
$payloads = array();
$strids = array();
$payloads_offset = 0;
foreach($cache_entries as $entry)
{
list($format, $payload) = _config_get_payload($entry, $use_lz4, $use_config_refs);
$payload_size = strlen($payload);
$payloads[] = array($payloads_offset, $payload, $format, $payload_size);
$payloads_offset += $payload_size;
$strids_indices = array();
$strid_parts = explode('/', ltrim($entry->strid, '@'));
foreach($strid_parts as $strid_part)
{
if(!isset($STRIDMAP[$strid_part]))
{
$strid_index = count($STRIDLIST);
$STRIDLIST[] = $strid_part;
$STRIDMAP[$strid_part] = $strid_index;
$strids_indices[] = $strid_index;
}
else
$strids_indices[] = $STRIDMAP[$strid_part];
}
$strids[] = $strids_indices;
}
$header = array();
foreach($cache_entries as $idx => $entry)
{
if(isset($MAP[$entry->id]))
throw new Exception("Duplicating config id for '{$entry->strid}' conflicts with '{$MAP[$entry->id]}'");
$MAP[$entry->id] = $entry->strid;
$header[] = array(
$payloads[$idx][2], //format
$entry->id,
$strids[$idx], //strid as a lookup indices
$entry->class_id,
$payloads[$idx][0], //offset
$payloads[$idx][3] //size
);
}
$strids_msgpack = config_msgpack_pack($STRIDLIST);
$header_msgpack = config_msgpack_pack($header);
$payloads_bundle = '';
foreach($payloads as $item)
$payloads_bundle .= $item[1];
$packed_data =
pack("C", 2) .
pack("V", $version) .
pack("V", strlen($strids_msgpack)) .
pack("V", strlen($header_msgpack)) .
$strids_msgpack .
$header_msgpack .
$payloads_bundle;
return $packed_data; return $packed_data;
} }
function config_unpack_bundle($packed_data) function config_unpack_bundle(string $packed_data) : array
{
$packed_info = substr($packed_data, 0, 1);
$info = unpack('Cformat', $packed_info);
if($info['format'] === 1)
{
return _config_unpack_bundle_fmt1($packed_data);
}
else if($info['format'] === 2)
{
return _config_unpack_bundle_fmt2($packed_data);
}
else
throw new Exception("Unknown format: {$info['format']}");
}
function _config_unpack_bundle_fmt1(string $packed_data) : array
{ {
$packed_info = substr($packed_data, 0, 1+4+4); $packed_info = substr($packed_data, 0, 1+4+4);
$info = unpack('Cformat/Vversion/Vheader_len', $packed_info); $info = unpack('Cformat/Vversion/Vheader_len', $packed_info);
if($info['format'] !== 1) if($info['format'] !== 1)
@ -187,7 +316,37 @@ function config_unpack_bundle($packed_data)
return $entries; return $entries;
} }
function _config_get_payload(ConfigCacheEntry $ce, $use_lz4, $use_config_refs) function _config_unpack_bundle_fmt2(string $packed_data) : array
{
$packed_info = substr($packed_data, 0, 1+4+4+4);
$info = unpack('Cformat/Vversion/Vstrids_len/Vheader_len', $packed_info);
if($info['format'] !== 2)
throw new Exception("Unknown format: {$info['format']}");
$strids_msgpack = substr($packed_data, 1+4+4+4, $info['strids_len']);
$strids = config_msgpack_unpack($strids_msgpack);
$header_msgpack = substr($packed_data, 1+4+4+4+$info['strids_len'], $info['header_len']);
$header = config_msgpack_unpack($header_msgpack);
$payloads_bundle = substr($packed_data, 1+4+4+4+$info['strids_len']+$info['header_len']);
$entries = array();
foreach($header as $item)
{
list($format, $id, $strid_crc, $class_id, $offset, $size) = $item;
$payload = substr($payloads_bundle, $offset, $size);
$entries[$id] = array($class_id, _config_unpack_payload($format, $payload));
}
return $entries;
}
function _config_get_payload(ConfigCacheEntry $ce, bool $use_lz4, bool $use_config_refs) : array
{ {
$format = ConfigCacheEntry::FMT_BINARY; $format = ConfigCacheEntry::FMT_BINARY;
$payload = null; $payload = null;
@ -210,7 +369,7 @@ function _config_get_payload(ConfigCacheEntry $ce, $use_lz4, $use_config_refs)
return array($format, $payload); return array($format, $payload);
} }
function _config_unpack_payload($format, $payload) function _config_unpack_payload(int $format, string $payload) : array
{ {
$msg_packed = null; $msg_packed = null;
if($format === ConfigCacheEntry::FMT_LZ4) if($format === ConfigCacheEntry::FMT_LZ4)
@ -224,7 +383,13 @@ function _config_unpack_payload($format, $payload)
return config_msgpack_unpack($msg_packed); return config_msgpack_unpack($msg_packed);
} }
function config_make_standalone_ext_bundle(array $configs, $file_path, $use_lz4 = true) function config_make_standalone_ext_bundle(
array $configs,
string $file_path,
bool $use_lz4 = true,
int $binary_format = 1,
?int $version = null
)
{ {
$cache_entries = array(); $cache_entries = array();
foreach($configs as $conf) foreach($configs as $conf)
@ -243,41 +408,41 @@ function config_make_standalone_ext_bundle(array $configs, $file_path, $use_lz4
$cache_entries[] = $entry; $cache_entries[] = $entry;
} }
$packed_data = config_pack_bundle($cache_entries, $use_lz4); $packed_data = config_pack_bundle($cache_entries, $use_lz4, false, $binary_format, $version);
ensure_write($file_path, $packed_data); ensure_write($file_path, $packed_data);
} }
function config_bench_load($file) function config_bench_load(string $file)
{ {
$base_dir = config_map_base_dir($file); $base_dir = config_map_base_dir($file);
list($proto_id, $_) = config_ensure_header($base_dir, $file); list($conf_id, $_) = config_ensure_header($base_dir, $file);
if(!$proto_id) if(!$conf_id)
throw new Exception("Bad proto_id: {$proto_id}"); throw new Exception("Bad conf id: {$conf_id}");
$t = microtime(true); $t = microtime(true);
$parse_res = config_parse(config_base_dirs(), $file); $parse_res = config_parse(config_base_dirs(), $file);
if($parse_res->error !== 0) if($parse_res->error !== 0)
throw new Exception("Error({$parse_res->error}) while loading JSON from {$file}:\n" . $parse_res->error_descr); throw new Exception("Error({$parse_res->error}) while loading JSON from {$file}:\n" . $parse_res->error_descr);
echo "PARSE: " . (microtime(true) - $t) . "\n"; echo "PARSE: " . (microtime(true) - $t) . "\n";
$t = microtime(true); $t = microtime(true);
$config = config_load_ex($base_dir, $file, $parse_res->parsed_arr, $proto_id); $config = config_load_ex($base_dir, $file, $parse_res->parsed_arr, $conf_id);
echo "LOAD: " . (microtime(true) - $t) . "\n"; echo "LOAD: " . (microtime(true) - $t) . "\n";
} }
function config_get_tmp_build_path($file) function config_get_tmp_build_path(string $file) : string
{ {
$name = str_replace(":", "-", str_replace("\\", "-", str_replace("/", "-", normalize_path($file)))); $name = str_replace(":", "-", str_replace("\\", "-", str_replace("/", "-", normalize_path($file))));
$name = ltrim($name, "-"); $name = ltrim($name, "-");
return normalize_path(config_build_dir() . "/$name"); return normalize_path(config_build_dir() . "/$name");
} }
function config_get_includes_map_path() function config_get_includes_map_path() : string
{ {
return config_build_dir() . "/includes.map"; return config_build_dir() . "/includes.map";
} }
function config_load_includes_map($file = null) function config_load_includes_map(?string $file = null) : array
{ {
$file = $file ? $file : config_get_includes_map_path(); $file = $file ? $file : config_get_includes_map_path();
@ -293,7 +458,7 @@ function config_load_includes_map($file = null)
return $includes_map; return $includes_map;
} }
function config_save_includes_map(array $includes_map, $file = null) function config_save_includes_map(array $includes_map, ?string $file = null)
{ {
$file = $file ? $file : config_get_includes_map_path(); $file = $file ? $file : config_get_includes_map_path();
@ -302,19 +467,19 @@ function config_save_includes_map(array $includes_map, $file = null)
class ConfigFetchResult class ConfigFetchResult
{ {
public $all = array(); public array $all = array();
public $by_id = array(); public array $by_id = array();
public $by_path = array(); public array $by_path = array();
public $by_alias = array(); public array $by_alias = array();
} }
function config_fetch_ex( function config_fetch_ex(
array $files, array $files,
$force_stale = false, bool $force_stale = false,
$verbose = false, bool $verbose = false,
$includes_map_file = null, ?string $includes_map_file = null,
$max_workers = null ?int $max_workers = null
) ) : ConfigFetchResult
{ {
if(!$files) if(!$files)
return new ConfigFetchResult(); return new ConfigFetchResult();
@ -363,7 +528,7 @@ function config_fetch_ex(
return $result; return $result;
} }
function _config_fetch_cache_ex(array $results_by_job) function _config_fetch_cache_ex(array $results_by_job) : array
{ {
$result = new ConfigFetchResult(); $result = new ConfigFetchResult();
$total_stales = 0; $total_stales = 0;
@ -409,7 +574,7 @@ function _config_fetch_cache_ex(array $results_by_job)
return array($result, $total_stales); return array($result, $total_stales);
} }
function _config_worker_run_procs(array $jobs, $includes_map_file, $force, $verbose) function _config_worker_run_procs(array $jobs, string $includes_map_file, bool $force, bool $verbose) : array
{ {
$worker_args = array(); $worker_args = array();
foreach($jobs as $idx => $job) foreach($jobs as $idx => $job)
@ -418,7 +583,7 @@ function _config_worker_run_procs(array $jobs, $includes_map_file, $force, $verb
return run_background_gamectl_workers('config_worker', $worker_args); return run_background_gamectl_workers('config_worker', $worker_args);
} }
function _config_worker_func(array $job, array $includes_map, $force, $verbose) function _config_worker_func(array $job, array $includes_map, bool $force, bool $verbose) : array
{ {
global $CONFIG_INIT_WORKER_FUNC; global $CONFIG_INIT_WORKER_FUNC;
if(is_callable($CONFIG_INIT_WORKER_FUNC)) if(is_callable($CONFIG_INIT_WORKER_FUNC))
@ -454,19 +619,19 @@ function _config_worker_func(array $job, array $includes_map, $force, $verbose)
return $results; return $results;
} }
function _config_invalidate_cache($file, $cache_file) : ConfigCacheEntry function _config_invalidate_cache(string $file, string $cache_file) : ConfigCacheEntry
{ {
$cache_payload_file = config_get_cache_payload_path($file); $cache_payload_file = config_get_cache_payload_path($file);
//TODO: pass it from above? //TODO: pass it from above?
$base_dir = config_map_base_dir($file); $base_dir = config_map_base_dir($file);
list($proto_id, $_) = config_ensure_header($base_dir, $file); list($conf_id, $_) = config_ensure_header($base_dir, $file);
if(!$proto_id) if(!$conf_id)
throw new Exception("Bad proto_id: {$proto_id}"); throw new Exception("Bad conf id: {$conf_id}");
$GLOBALS['CONFIG_CURRENT_FILE'] = $file; $GLOBALS['CONFIG_CURRENT_FILE'] = $file;
$GLOBALS['CONFIG_CURRENT_PROTO_ID'] = $proto_id; $GLOBALS['CONFIG_CURRENT_PROTO_ID'] = $conf_id;
$GLOBALS['CONFIG_EXTRAS'] = ConfigCacheEntryExtras::create(); $GLOBALS['CONFIG_EXTRAS'] = ConfigCacheEntryExtras::create();
$pres = config_parse(config_base_dirs(), $file); $pres = config_parse(config_base_dirs(), $file);
@ -475,7 +640,7 @@ function _config_invalidate_cache($file, $cache_file) : ConfigCacheEntry
$includes = config_get_module_includes($pres->jsm_module); $includes = config_get_module_includes($pres->jsm_module);
$config = config_load_ex($base_dir, $file, $pres->parsed_arr, $proto_id); $config = config_load_ex($base_dir, $file, $pres->parsed_arr, $conf_id);
$payload_data = config_msgpack_pack($config->export()); $payload_data = config_msgpack_pack($config->export());
$cache_entry = new ConfigCacheEntry(); $cache_entry = new ConfigCacheEntry();
@ -497,61 +662,61 @@ function _config_invalidate_cache($file, $cache_file) : ConfigCacheEntry
return $cache_entry; return $cache_entry;
} }
function config_find_by_alias(ConfigFetchResult $cache_entries, $strid) : ConfigCacheEntry function config_find_by_alias(ConfigFetchResult $cache_entries, string $strid) : ConfigCacheEntry
{ {
if(array_key_exists($strid, $cache_entries->by_alias)) if(array_key_exists($strid, $cache_entries->by_alias))
return $cache_entries->by_alias[$strid]; return $cache_entries->by_alias[$strid];
throw new Exception("Failed to find config by alias '$strid'!"); throw new Exception("Failed to find config by alias '$strid'!");
} }
function config_find_by_id(ConfigFetchResult $cache_entries, $id) : ConfigCacheEntry function config_find_by_id(ConfigFetchResult $cache_entries, int $id) : ConfigCacheEntry
{ {
if(array_key_exists($id, $cache_entries->by_id)) if(array_key_exists($id, $cache_entries->by_id))
return $cache_entries->by_id[$id]; return $cache_entries->by_id[$id];
throw new Exception("Failed to find config by id '$id'!"); throw new Exception("Failed to find config by id '$id'!");
} }
function config_find_by_path(ConfigFetchResult $cache_entries, $path) : ConfigCacheEntry function config_find_by_path(ConfigFetchResult $cache_entries, string $path) : ConfigCacheEntry
{ {
if(array_key_exists($path, $cache_entries->by_path)) if(array_key_exists($path, $cache_entries->by_path))
return $cache_entries->by_path[$path]; return $cache_entries->by_path[$path];
throw new Exception("Failed to find config by path '$path'!"); throw new Exception("Failed to find config by path '$path'!");
} }
function config_fetch_by_path(string $path, $force_stale = false) : ConfigCacheEntry function config_fetch_by_path(string $path, bool $force_stale = false) : ConfigCacheEntry
{ {
$ces = config_fetch_ex(array($path), $force_stale); $ces = config_fetch_ex(array($path), $force_stale);
if(!$ces) if(empty($ces->all))
throw new Exception("Config not found at path '$path'"); throw new Exception("Config not found at path '$path'");
return $ces->all[0]; return $ces->all[0];
} }
function config_fetch_all($force_stale = false) function config_fetch_all(bool $force_stale = false) : ConfigFetchResult
{ {
return config_fetch_ex(config_scan_files(), $force_stale); return config_fetch_ex(config_scan_files(), $force_stale);
} }
class ConfigCacheEntryExtras class ConfigCacheEntryExtras
{ {
private static $klass = ConfigCacheEntryExtras::class; private static string $klass = ConfigCacheEntryExtras::class;
static function init($project_specific_klass) static function init(string $project_specific_klass)
{ {
self::$klass = $project_specific_klass; self::$klass = $project_specific_klass;
} }
static function create() static function create() : object
{ {
return new self::$klass(); return new self::$klass();
} }
function export() function export() : array
{ {
$as_array = get_object_vars($this); $as_array = get_object_vars($this);
return $as_array; return $as_array;
} }
function import($as_array) function import(array $as_array)
{ {
foreach($as_array as $field_name => $field_value) foreach($as_array as $field_name => $field_value)
$this->$field_name = $field_value; $this->$field_name = $field_value;
@ -568,28 +733,28 @@ class ConfigCacheEntry
const FMT_LZ4 = 1; const FMT_LZ4 = 1;
const FMT_FILE_REF = 2; const FMT_FILE_REF = 2;
public $class; public string $class;
public $class_id; public int $class_id;
public $id; public int $id;
public $strid; public string $strid;
public $cache_file; public string $cache_file;
//NOTE: actual payload is stored in a separate file for faster incremental retrievals //NOTE: actual payload is stored in a separate file for faster incremental retrievals
public $payload_file; public string $payload_file;
public $file; public string $file;
public $includes = array(); public array $includes = array();
public $refs = array(); public array $refs = array();
public $extras; public object $extras;
public $_config; public $_config;
public $_payload; public $_payload;
static function serialize($ce) static function serialize(ConfigCacheEntry $ce) : string
{ {
$d = $ce->export(); $d = $ce->export();
return serialize($d); return serialize($d);
} }
static function unserialize($str) static function unserialize(string $str) : ?ConfigCacheEntry
{ {
$d = @unserialize($str); $d = @unserialize($str);
if(!is_array($d)) if(!is_array($d))
@ -604,7 +769,7 @@ class ConfigCacheEntry
$this->extras = ConfigCacheEntryExtras::create(); $this->extras = ConfigCacheEntryExtras::create();
} }
function __get($name) function __get(string $name)
{ {
if($name === "config") if($name === "config")
{ {
@ -627,7 +792,7 @@ class ConfigCacheEntry
throw new Exception("No such property '$name'"); throw new Exception("No such property '$name'");
} }
function __set($name, $v) function __set(string $name, $v)
{ {
if($name === "config") if($name === "config")
$this->_config = $v; $this->_config = $v;
@ -637,7 +802,7 @@ class ConfigCacheEntry
throw new Exception("No such property '$name'"); throw new Exception("No such property '$name'");
} }
function export() function export() : array
{ {
$d = array(); $d = array();
$d[] = $this->class; $d[] = $this->class;
@ -674,27 +839,17 @@ class ConfigCacheEntry
} }
} }
function config_get_cache_path($file) function config_get_cache_path(string $file) : string
{ {
return config_get_tmp_build_path($file . '.cacheb'); return config_get_tmp_build_path($file . '.cacheb');
} }
function config_get_cache_payload_path($file) function config_get_cache_payload_path(string $file) : string
{ {
return config_get_tmp_build_path($file . '.pdata'); return config_get_tmp_build_path($file . '.pdata');
} }
function config_get_id($conf_dir, $file_path) function config_extract_refs(string $src, bool $as_map = true) : array
{
return config_file2id($conf_dir, $file_path);
}
function config_get_strid($conf_dir, $file_path)
{
return config_file2strid($conf_dir, $file_path);
}
function config_extract_refs($src, $as_map = true)
{ {
$refs = array(); $refs = array();
if(preg_match_all('~"(@[^"]+)"~', $src, $matches)) if(preg_match_all('~"(@[^"]+)"~', $src, $matches))
@ -709,7 +864,7 @@ function config_extract_refs($src, $as_map = true)
return $as_map ? $refs : array_keys($refs); return $as_map ? $refs : array_keys($refs);
} }
function config_get_header($file, &$proto_id, &$alias) function config_get_header(string $file, ?int &$conf_id, ?string &$strid) : bool
{ {
$h = fopen($file, "r"); $h = fopen($file, "r");
$line = fgets($h, 256); $line = fgets($h, 256);
@ -717,33 +872,33 @@ function config_get_header($file, &$proto_id, &$alias)
if(preg_match('~\{\s*/\*\s*proto_id\s*=\s*(\d+)\s*;\s*alias\s*=\s*([^\s]+)~', $line, $matches)) if(preg_match('~\{\s*/\*\s*proto_id\s*=\s*(\d+)\s*;\s*alias\s*=\s*([^\s]+)~', $line, $matches))
{ {
$proto_id = (int)$matches[1]; $conf_id = (int)$matches[1];
$alias = $matches[2]; $strid = $matches[2];
return true; return true;
} }
else else
return false; return false;
} }
function config_set_header($contents, $proto_id, $alias, &$is_success) function config_set_header(string $contents, int $conf_id, string $strid, bool &$is_success) : string
{ {
$contents = preg_replace('~\s*\{~', "{ /* proto_id = {$proto_id} ; alias = {$alias} */", $contents, 1/*limit*/, $count); $contents = preg_replace('~\s*\{~', "{ /* proto_id = {$conf_id} ; alias = {$strid} */", $contents, 1/*limit*/, $count);
$is_success = $count == 1; $is_success = $count == 1;
return $contents; return $contents;
} }
function config_extract_header($contents) function config_extract_header(string $contents) : string
{ {
return preg_replace('~(\s*\{)\s*/\*\s*proto_id\s*=\s*\d+\s*;\s*alias\s*=\s*[^\s]+\s*\*/~', '$1', $contents); return preg_replace('~(\s*\{)\s*/\*\s*proto_id\s*=\s*\d+\s*;\s*alias\s*=\s*[^\s]+\s*\*/~', '$1', $contents);
} }
function config_ensure_header($conf_dir, $file, $force = false) function config_ensure_header(string $conf_dir, string $file, bool $force = false) : array
{ {
if(config_get_header($file, $curr_proto_id, $curr_alias)) if(config_get_header($file, $curr_proto_id, $curr_alias))
{ {
$alias = config_get_strid($conf_dir, $file); $alias = config_file2strid($conf_dir, $file);
if($force) if($force)
$curr_proto_id = config_get_id($conf_dir, $file); $curr_proto_id = config_file2id($conf_dir, $file);
//NOTE: keeping current proto id intact if not forced //NOTE: keeping current proto id intact if not forced
if($force || $curr_alias !== $alias) if($force || $curr_alias !== $alias)
@ -760,34 +915,34 @@ function config_ensure_header($conf_dir, $file, $force = false)
} }
else else
{ {
$proto_id = config_get_id($conf_dir, $file); $conf_id = config_file2id($conf_dir, $file);
$alias = config_get_strid($conf_dir, $file); $alias = config_file2strid($conf_dir, $file);
$lines = file($file); $lines = file($file);
$lines[0] = config_set_header($lines[0], $proto_id, $alias, $is_success); $lines[0] = config_set_header($lines[0], $conf_id, $alias, $is_success);
if(!$is_success) if(!$is_success)
throw new Exception("Could not set header for '$file' in line: {$lines[0]}"); throw new Exception("Could not set header for '$file' in line: {$lines[0]}");
ensure_write($file, join("", $lines)); ensure_write($file, join("", $lines));
return array($proto_id, $alias); return array($conf_id, $alias);
} }
} }
function config_load($conf_dir, $conf_path) function config_load(string $conf_dir, string $conf_path) : object
{ {
list($proto_id, $_) = config_ensure_header($conf_dir, $conf_path); list($conf_id, $_) = config_ensure_header($conf_dir, $conf_path);
if(!$proto_id) if(!$conf_id)
throw new Exception("Bad proto_id: {$proto_id}"); throw new Exception("Bad conf id: {$conf_id}");
$pres = config_parse($conf_dir, $conf_path); $pres = config_parse(array($conf_dir), $conf_path);
if($pres->error !== 0) if($pres->error !== 0)
throw new Exception("Error({$pres->error}) while loading JSON from {$conf_path}:\n" . $pres->error_descr); throw new Exception("Error({$pres->error}) while loading JSON from {$conf_path}:\n" . $pres->error_descr);
return config_load_ex($conf_dir, $conf_path, $pres->parsed_arr, $proto_id); return config_load_ex($conf_dir, $conf_path, $pres->parsed_arr, $conf_id);
} }
function conf2json($conf, $json_flags = 0) function conf2json($conf, int $json_flags = 0)
{ {
$arr = $conf->export(true); $arr = $conf->export(true);
$arr['class'] = get_class($conf); $arr['class'] = get_class($conf);
@ -800,14 +955,14 @@ function conf2json($conf, $json_flags = 0)
class ConfigParseResult class ConfigParseResult
{ {
public $error = 0; public int $error = 0;
public $error_descr; public string $error_descr;
public $normalized_jzon = ''; public string $normalized_jzon = '';
public $parsed_arr; public array $parsed_arr;
public $jsm_module; public $jsm_module;
} }
function config_parse(array $base_dirs, $file) : ConfigParseResult function config_parse(array $base_dirs, string $file) : ConfigParseResult
{ {
$res = new ConfigParseResult(); $res = new ConfigParseResult();
@ -841,7 +996,7 @@ function config_parse(array $base_dirs, $file) : ConfigParseResult
return $res; return $res;
} }
function config_check_and_decode_jzon($json) function config_check_and_decode_jzon(string $json) : array
{ {
try try
{ {
@ -854,7 +1009,7 @@ function config_check_and_decode_jzon($json)
} }
} }
function config_load_ex($conf_dir, $file, array $arr, $id = null) function config_load_ex(string $conf_dir, string $file, array $arr, ?int $id = null) : object
{ {
if(!isset($arr['class']) || !isset($arr['class'][0])) if(!isset($arr['class']) || !isset($arr['class'][0]))
throw new Exception("Class is not set in file '$file'."); throw new Exception("Class is not set in file '$file'.");
@ -887,18 +1042,18 @@ function config_load_ex($conf_dir, $file, array $arr, $id = null)
return $cnf; return $cnf;
} }
function config_is_file($filename) function config_is_file(string $filename) : bool
{ {
return (strrpos($filename, '.conf.js') === (strlen($filename) - 8)); return (strrpos($filename, '.conf.js') === (strlen($filename) - 8));
} }
function config_file2id($conf_dir, $filename) function config_file2id(string $conf_dir, string $filename) : int
{ {
$nfilename = config_make_path($conf_dir, $filename); $nfilename = config_make_path($conf_dir, $filename);
return config_crc28($nfilename); return config_crc28($nfilename);
} }
function config_file2strid($conf_dir, $filename) function config_file2strid(string $conf_dir, string $filename) : string
{ {
$strid = config_make_path($conf_dir, $filename); $strid = config_make_path($conf_dir, $filename);
$fname_idx = strrpos($strid, "/"); $fname_idx = strrpos($strid, "/");
@ -907,7 +1062,7 @@ function config_file2strid($conf_dir, $filename)
return "@".$strid; return "@".$strid;
} }
function config_make_path($conf_dir, $path) function config_make_path(string $conf_dir, string $path) : string
{ {
return ltrim(str_replace(normalize_path($conf_dir, true/*nix*/), return ltrim(str_replace(normalize_path($conf_dir, true/*nix*/),
'', '',
@ -915,19 +1070,12 @@ function config_make_path($conf_dir, $path)
'/'); '/');
} }
function config_str2id($str) function config_crc28(string $what) : int
{
if(strpos($str, '@') === 0)
$str = substr($str, 1) . '.conf.js';
return config_crc28($str);
}
function config_crc28($what)
{ {
return crc32($what) & 0xFFFFFFF; return crc32($what) & 0xFFFFFFF;
} }
function config_get_module_includes(\JSM_Module $cm) function config_get_module_includes(\JSM_Module $cm) : array
{ {
$includes = array(); $includes = array();
foreach($cm->getIncludes() as $include => $_) foreach($cm->getIncludes() as $include => $_)
@ -940,7 +1088,7 @@ function config_get_module_includes(\JSM_Module $cm)
return $includes; return $includes;
} }
function config_includes_map_find_text_origin(array $map, $file, $text) function config_includes_map_find_text_origin(array $map, string $file, string $text) : array
{ {
if(!isset($map[$file])) if(!isset($map[$file]))
return array($file); return array($file);
@ -959,24 +1107,13 @@ function config_includes_map_find_text_origin(array $map, $file, $text)
return $res; return $res;
} }
function config_str_replace_in_files($subj, $repl, array $files) function config_str_ends_with(string $haystack, string $needle) : bool
{
foreach($files as $file)
{
$contents = ensure_read($file);
$new_contents = str_replace($subj, $repl, $contents);
if($new_contents !== $contents)
ensure_write($file, $new_contents);
}
}
function config_str_ends_with($haystack, $needle)
{ {
// search forward starting from end minus needle length characters // search forward starting from end minus needle length characters
return $needle === "" || (($temp = strlen($haystack) - strlen($needle)) >= 0 && strpos($haystack, $needle, $temp) !== false); return $needle === "" || (($temp = strlen($haystack) - strlen($needle)) >= 0 && strpos($haystack, $needle, $temp) !== false);
} }
function config_walk_fields($proto, $callback) function config_walk_fields($proto, callable $callback)
{ {
if(!method_exists($proto, 'CLASS_FIELDS_PROPS')) if(!method_exists($proto, 'CLASS_FIELDS_PROPS'))
return; return;
@ -1001,7 +1138,7 @@ function config_walk_fields($proto, $callback)
if(function_exists('msgpack_pack')) if(function_exists('msgpack_pack'))
{ {
function config_msgpack_pack($data) function config_msgpack_pack(array $data) : string
{ {
$prev = ini_set('msgpack.use_str8_serialization', '0'); $prev = ini_set('msgpack.use_str8_serialization', '0');
$res = msgpack_pack($data); $res = msgpack_pack($data);
@ -1010,14 +1147,14 @@ if(function_exists('msgpack_pack'))
return $res; return $res;
} }
function config_msgpack_unpack($data) function config_msgpack_unpack(string $data) : array
{ {
return msgpack_unpack($data); return msgpack_unpack($data);
} }
} }
else else
{ {
function config_msgpack_pack($data) function config_msgpack_pack(array $data) : string
{ {
include_once(__DIR__ . '/msgpack/msgpack_custom.inc.php'); include_once(__DIR__ . '/msgpack/msgpack_custom.inc.php');
@ -1025,7 +1162,7 @@ else
return $packer->pack($data); return $packer->pack($data);
} }
function config_msgpack_unpack($data) function config_msgpack_unpack(string $data) : array
{ {
return \MessagePack\MessagePack::unpack($data); return \MessagePack\MessagePack::unpack($data);
} }